CPU有哪几种工作模式?每种模式都有独立堆栈,如何用汇编初始化这些栈?

    搜索 @note_pic 可查看绘制的全部字符图
    搜索 @note_why 是尚未看明白的地方,有看明白的,请Pull Request完善
    搜索 @note_thinking 是一些的思考和建议
    搜索 @note_#if0 是由第三方项目提供不在内核源码中定义的极为重要结构体,为方便理解而添加的。
    搜索 @note_good 是给源码点赞的地方
上级 11f61d6a
......@@ -42,28 +42,28 @@
.global OsIrqHandler /*硬中断处理*/
.global ArchSpinUnlock /*自旋锁解锁*/
.global OsSchedToUserSpinUnlock /*尚未实现*/
/* @note_why 为何要重新定义OS_TASK_STATUS_RUNNING? */
.equ OS_TASK_STATUS_RUNNING, 0x0010U /* .equ用于把常量值设置为可以在文本段中使用的符号 #define OS_TASK_STATUS_RUNNING 0x0010U */
.equ OS_PERCPU_STRUCT_SIZE, 0x28U
.equ OS_PERCPU_TASK_LOCK_OFFSET, 0x14U
.fpu vfpv4 /* .fpu @note_why 尚未知这句话的含义 */
/* 此宏用于对齐和不对齐8字节边界上的堆栈以符合ABI */
/* macros to align and unalign the stack on 8 byte boundary for ABI compliance */
.macro STACK_ALIGN, reg /* 汇编带参数的宏定义*/
MOV \reg, sp
.macro STACK_ALIGN, reg /* 栈对齐*/
MOV \reg, sp
TST SP, #4
SUBEQ SP, #4
PUSH { \reg }
.endm
.macro STACK_RESTORE, reg /*汇编带参数的宏定义*/
POP { \reg } @
MOV sp, \reg
.macro STACK_RESTORE, reg /*栈恢复*/
POP { \reg }
MOV sp, \reg
.endm
/* macros to save and restore fpu regs */
.macro PUSH_FPU_REGS reg1 /* 汇编宏定义,类似于 #define PUSH_FPU_REGS(reg1) ... */
#if !defined(LOSCFG_ARCH_FPU_DISABLE)
/* FPU(floating-point processor unit)浮点运算单元*/
/* macros to save and restore fpu regs */
.macro PUSH_FPU_REGS reg1 /* 保存fpu寄存器 */
#if !defined(LOSCFG_ARCH_FPU_DISABLE) @FPU使能
VMRS \reg1, FPEXC
PUSH {\reg1}
VMRS \reg1, FPSCR
......@@ -75,7 +75,7 @@
#endif
.endm
.macro POP_FPU_REGS reg1
.macro POP_FPU_REGS reg1 /* 恢复fpu寄存器 */
#if !defined(LOSCFG_ARCH_FPU_DISABLE)
VPOP {D0-D15}
#if defined(LOSCFG_ARCH_FPU_VFP_D32)
......@@ -94,12 +94,12 @@ OsStartToRun:
MSR CPSR_c, #(CPSR_INT_DISABLE | CPSR_SVC_MODE) @禁止中断并切到管理模式
LDRH R1, [R0, #4] @将存储器地址为R0+4 的低16位数据读入寄存器R1,并将R1 的高16 位清零
ORR R1, #OS_TASK_STATUS_RUNNING
STRH R1, [R0, #4]
ORR R1, #OS_TASK_STATUS_RUNNING @或指令 R1=R1|OS_TASK_STATUS_RUNNING
STRH R1, [R0, #4] @将寄存器R1中的低16位写入以R0+4地址的存储器中
/* R0 is new task, save it on tpidrprw */
MCR p15, 0, R0, c13, c0, 4
ISB
MCR p15, 0, R0, c13, c0, 4 @ C5=C4=R0
ISB @指令同步屏障,清除流水线并且确保在新指令执行时,之前的指令都已经执行完毕。
VPUSH {S0} /* fpu */
VPOP {S0}
......@@ -149,15 +149,15 @@ OsTaskContextLoad:
MOV R4, R0 @R4=R0 说明R4也记录了CPSR内容,这个内容将用于 OsKernelTaskLoad中保存到SPSR
AND R0, R0, #CPSR_MASK_MODE @R0 =R0&CPSR_MASK_MODE ,目的是清除高16
CMP R0, #CPSR_USER_MODE @比较R0是否为用户模式
BNE OsKernelTaskLoad @不相等则跳转到OsKernelTaskLoad执行
BNE OsKernelTaskLoad @不相等则跳转到OsKernelTaskLoad执行,return回去了
#ifdef LOSCFG_KERNEL_SMP
#ifdef LOSCFG_KERNEL_SMP_LOCKDEP
/* 8 bytes stack align */
SUB SP, SP, #4
LDR R0, =g_taskSpin
BL OsLockDepCheckOut
ADD SP, SP, #4
SUB SP, SP, #4 @sp = sp -4
LDR R0, =g_taskSpin @R0 = g_taskSpin
BL OsLockDepCheckOut @带链接的跳转指令。指令将下一条指令的地址拷贝到R14(LR)链接寄存器中,然后跳转到指定地址运行程序
ADD SP, SP, #4 @sp=sp+4
#endif
/* R0 = &g_taskSpin.rawLock */
LDR R0, =g_taskSpin
......@@ -181,12 +181,12 @@ OsTaskContextLoad:
ADD SP, SP, #(2 * 4)
LDMFD SP!, {PC}^
OsKernelTaskLoad: @内核任务的加载
MSR SPSR_cxsf, R4 @R4保存到程序状态保存寄存器32
OsKernelTaskLoad: @内核任务的加载
MSR SPSR_cxsf, R4 @R4保存到程序状态保存寄存器32
/* restore r0-r12, lr */
LDMFD SP!, {R0-R12} @出栈,依次保存到 R0-R12,其实就是恢复现场
ADD SP, SP, #4 @sp=SP+4
LDMFD SP!, {LR, PC}^ @返回地址赋给pc指针
LDMFD SP!, {R0-R12} @出栈,依次保存到 R0-R12,其实就是恢复现场
ADD SP, SP, #4 @sp=SP+4
LDMFD SP!, {LR, PC}^ @返回地址赋给pc指针
OsIrqHandler:
SUB LR, LR, #4
......
......@@ -285,14 +285,14 @@ STATIC VOID OsExcType(UINT32 excType, ExcContext *excBufAddr, UINT32 far, UINT32
}
STATIC const CHAR *g_excTypeString[] = {//异常类型的字符说明,在鸿蒙内核中什么才算是异常? 看这里
"reset", //重置命令
"undefined instruction", //未定义的指令
"software interrupt", //软中断,比如定时器
"prefetch abort", //取指异常
"data abort", //数据异常
"fiq", //快中断异常
"address abort", //地址异常
"irq" //中断异常
"reset", //复位异常源 - SVC模式(Supervisor保护模式)
"undefined instruction", //未定义指令异常源- und模式
"software interrupt", //软中断异常源 - SVC模式
"prefetch abort", //取指异常源 - abort模式
"data abort", //数据异常源 - abort模式
"fiq", //快中断异常源 - FIQ模式
"address abort", //地址异常源 - abort模式
"irq" //中断异常源 - IRQ模式
};
//打印系统信息
STATIC VOID OsExcSysInfo(UINT32 excType, const ExcContext *excBufAddr)
......@@ -705,7 +705,7 @@ VOID BackTrace(UINT32 regFP)//fp:R11寄存器
BackTraceSub(regFP);
}
//异常处理模块的初始化
//异常接管模块的初始化
VOID OsExcInit(VOID)
{
OsExcStackInfoReg(g_excStack, sizeof(g_excStack) / sizeof(g_excStack[0]));//异常模式下注册内核栈信息
......
......@@ -36,6 +36,10 @@
#include "los_mmu_descriptor_v6.h"
#undef ASSEMBLY
/******************************************************************************
CPU下异常向量表
******************************************************************************/
.global __exc_stack_top
.global __irq_stack_top
......@@ -68,16 +72,16 @@
.fpu vfpv4
.arm
/* 设置异常模式栈的SP 参数1为栈底, 参数2为栈大小 r11 存放的是 cpu id */
/* param0 is stack bottom, param1 is stack size, r11 hold cpu id */
.macro EXC_SP_SET param0, param1
ldr r1, =\param0
mov r0, \param1
bl sp_set
ldr r1, =\param0 @r1 = 栈底
mov r0, \param1 @r0 = 栈大小
bl sp_set @跳到设置SP
.endm
/* param0 is stack top, param1 is stack size, param2 is magic num */
.macro STACK_MAGIC_SET param0, param1, param2
.macro STACK_MAGIC_SET param0, param1, param2 @设置栈魔法数字
ldr r0, =\param0
mov r1, \param1
ldr r2, =\param2
......@@ -332,12 +336,12 @@ cpu_start: /* 启动CPU */
/*
* set sp for current cpu
* r1 is stack bottom, r0 is stack size, r11 hold cpu id
*/
*/@设置当前CPUSP, r1为栈底, r0为栈大小 r11 cpu id
sp_set:
mul r3, r0, r11
sub r2, r1, r3
mov sp, r2
bx lr /* set sp */
mul r3, r0, r11 @r3=r0*r11 先计算偏移量
sub r2, r1, r3 @r2=r1-r3 如此得到r2cpuSP位置,从这里可以看出 栈底地址是要高于栈顶的
mov sp, r2 @sp = r2 设置栈指针位置,SP默认指向了栈底
bx lr /* set sp */ @跳回去继续执行
/*
* r4: page table base address
......@@ -468,6 +472,6 @@ __svc_stack:
.space OS_EXC_SVC_STACK_SIZE * CORE_NUM
__svc_stack_top:
__exc_stack:
__exc_stack:
.space OS_EXC_STACK_SIZE * CORE_NUM
__exc_stack_top:
/*
* Copyright (c) 2013-2019, Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (c) 2020, Huawei Device Co., Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "los_stackinfo_pri.h"
#include "los_printf_pri.h"
#include "los_config.h"
#ifdef LOSCFG_SHELL
#include "shcmd.h"
#include "shell.h"
#endif
const StackInfo *g_stackInfo = NULL;
UINT32 g_stackNum;
//获取栈的吃水线
UINT32 OsStackWaterLineGet(const UINTPTR *stackBottom, const UINTPTR *stackTop, UINT32 *peakUsed)
{
UINT32 size;
const UINTPTR *tmp = NULL;
if (*stackTop == OS_STACK_MAGIC_WORD) {//栈顶值是否等于 magic 0xCCCCCCCC
tmp = stackTop + 1;
while ((tmp < stackBottom) && (*tmp == OS_STACK_INIT)) {//记录从栈顶到栈低有多少个连续的 0xCACACACA
tmp++;
}
size = (UINT32)((UINTPTR)stackBottom - (UINTPTR)tmp);//剩余多少非0xCACACACA的栈空间
*peakUsed = (size == 0) ? size : (size + sizeof(CHAR *));//得出高峰用值,还剩多少可用
return LOS_OK;
} else {
*peakUsed = OS_INVALID_WATERLINE;//栈溢出了
return LOS_NOK;
}
}
//异常情况下的栈检查,主要就是检查栈顶值有没有被改写
VOID OsExcStackCheck(VOID)
{
UINT32 index;
UINT32 cpuid;
UINTPTR *stackTop = NULL;
if (g_stackInfo == NULL) {
return;
}
for (index = 0; index < g_stackNum; index++) {
for (cpuid = 0; cpuid < LOSCFG_KERNEL_CORE_NUM; cpuid++) {
stackTop = (UINTPTR *)((UINTPTR)g_stackInfo[index].stackTop + cpuid * g_stackInfo[index].stackSize);
if (*stackTop != OS_STACK_MAGIC_WORD) {// 只要栈顶内容不是 0xCCCCCCCCC 就是溢出了.
PRINT_ERR("cpu:%u %s overflow , magic word changed to 0x%x\n",
LOSCFG_KERNEL_CORE_NUM - 1 - cpuid, g_stackInfo[index].stackName, *stackTop);
}
}
}
}
//打印栈的信息 把每个CPU的栈信息打印出来
VOID OsExcStackInfo(VOID)
{
UINT32 index;
UINT32 cpuid;
UINT32 size;
UINTPTR *stackTop = NULL;
UINTPTR *stack = NULL;
if (g_stackInfo == NULL) {
return;
}
PrintExcInfo("\n stack name cpu id stack addr total size used size\n"
" ---------- ------ --------- -------- --------\n");
for (index = 0; index < g_stackNum; index++) {
for (cpuid = 0; cpuid < LOSCFG_KERNEL_CORE_NUM; cpuid++) {//可以看出 各个CPU的栈是紧挨的的
stackTop = (UINTPTR *)((UINTPTR)g_stackInfo[index].stackTop + cpuid * g_stackInfo[index].stackSize);
stack = (UINTPTR *)((UINTPTR)stackTop + g_stackInfo[index].stackSize);
(VOID)OsStackWaterLineGet(stack, stackTop, &size);//获取吃水线, 鸿蒙用WaterLine 这个词用的很妙
PrintExcInfo("%11s %-5d %-10p 0x%-8x 0x%-4x\n", g_stackInfo[index].stackName,
LOSCFG_KERNEL_CORE_NUM - 1 - cpuid, stackTop, g_stackInfo[index].stackSize, size);
}
}
OsExcStackCheck();//发生异常时栈检查
}
/*************************************************************************************** @note_pic
OsExcStackInfo 各个CPU栈布局图,其他栈也是一样,CPU各核硬件栈都是紧挨着
__undef_stack(SMP)
+-------------------+ <--- cpu1 top
| |
| CPU core1 |
| |
+--------------------<--- cpu2 top
| |
| cpu core 2 |
| |
+--------------------<--- cpu3 top
| |
| cpu core 3 |
| |
+--------------------<--- cpu4 top
| |
| cpu core 4 |
| |
+-------------------+
******************************************************************************************/
//注册栈信息
VOID OsExcStackInfoReg(const StackInfo *stackInfo, UINT32 stackNum)
{
g_stackInfo = stackInfo; //g_excStack
g_stackNum = stackNum;
}
//task栈的初始化,设置固定的值. 0xcccccccc 和 0xcacacaca
VOID OsStackInit(VOID *stacktop, UINT32 stacksize)
{
/* initialize the task stack, write magic num to stack top */
(VOID)memset_s(stacktop, stacksize, (INT32)OS_STACK_INIT, stacksize);//清一色填 0xCACACACA
*((UINTPTR *)stacktop) = OS_STACK_MAGIC_WORD;//0xCCCCCCCCC 中文就是"烫烫烫烫" 这几个字懂点计算机的人都不会陌生了.
}
#ifdef LOSCFG_SHELL_CMD_DEBUG
SHELLCMD_ENTRY(stack_shellcmd, CMD_TYPE_EX, "stack", 1, (CmdCallBackFunc)OsExcStackInfo);//采用shell命令静态注册方式
#endif
/*
* Copyright (c) 2013-2019, Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (c) 2020, Huawei Device Co., Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "los_stackinfo_pri.h"
#include "los_printf_pri.h"
#include "los_config.h"
#ifdef LOSCFG_SHELL
#include "shcmd.h"
#include "shell.h"
#endif
const StackInfo *g_stackInfo = NULL; //CPU所有工作模式的栈信息
UINT32 g_stackNum; //CPU所有工作模式的栈数量
//获取栈的吃水线
UINT32 OsStackWaterLineGet(const UINTPTR *stackBottom, const UINTPTR *stackTop, UINT32 *peakUsed)
{
UINT32 size;
const UINTPTR *tmp = NULL;
if (*stackTop == OS_STACK_MAGIC_WORD) {//栈顶值是否等于 magic 0xCCCCCCCC
tmp = stackTop + 1;
while ((tmp < stackBottom) && (*tmp == OS_STACK_INIT)) {//记录从栈顶到栈低有多少个连续的 0xCACACACA
tmp++;
}
size = (UINT32)((UINTPTR)stackBottom - (UINTPTR)tmp);//剩余多少非0xCACACACA的栈空间
*peakUsed = (size == 0) ? size : (size + sizeof(CHAR *));//得出高峰用值,还剩多少可用
return LOS_OK;
} else {
*peakUsed = OS_INVALID_WATERLINE;//栈溢出了
return LOS_NOK;
}
}
//异常情况下的栈检查,主要就是检查栈顶值有没有被改写
VOID OsExcStackCheck(VOID)
{
UINT32 index;
UINT32 cpuid;
UINTPTR *stackTop = NULL;
if (g_stackInfo == NULL) {
return;
}
for (index = 0; index < g_stackNum; index++) {
for (cpuid = 0; cpuid < LOSCFG_KERNEL_CORE_NUM; cpuid++) {
stackTop = (UINTPTR *)((UINTPTR)g_stackInfo[index].stackTop + cpuid * g_stackInfo[index].stackSize);
if (*stackTop != OS_STACK_MAGIC_WORD) {// 只要栈顶内容不是 0xCCCCCCCCC 就是溢出了.
PRINT_ERR("cpu:%u %s overflow , magic word changed to 0x%x\n",
LOSCFG_KERNEL_CORE_NUM - 1 - cpuid, g_stackInfo[index].stackName, *stackTop);
}
}
}
}
//打印栈的信息 把每个CPU的栈信息打印出来
VOID OsExcStackInfo(VOID)
{
UINT32 index;
UINT32 cpuid;
UINT32 size;
UINTPTR *stackTop = NULL;
UINTPTR *stack = NULL;
if (g_stackInfo == NULL) {
return;
}
PrintExcInfo("\n stack name cpu id stack addr total size used size\n"
" ---------- ------ --------- -------- --------\n");
for (index = 0; index < g_stackNum; index++) {
for (cpuid = 0; cpuid < LOSCFG_KERNEL_CORE_NUM; cpuid++) {//可以看出 各个CPU的栈是紧挨的的
stackTop = (UINTPTR *)((UINTPTR)g_stackInfo[index].stackTop + cpuid * g_stackInfo[index].stackSize);
stack = (UINTPTR *)((UINTPTR)stackTop + g_stackInfo[index].stackSize);
(VOID)OsStackWaterLineGet(stack, stackTop, &size);//获取吃水线, 鸿蒙用WaterLine 这个词用的很妙
PrintExcInfo("%11s %-5d %-10p 0x%-8x 0x%-4x\n", g_stackInfo[index].stackName,
LOSCFG_KERNEL_CORE_NUM - 1 - cpuid, stackTop, g_stackInfo[index].stackSize, size);
}
}
OsExcStackCheck();//发生异常时栈检查
}
/*************************************************************************************** @note_pic
OsExcStackInfo 各个CPU栈布局图,其他栈也是一样,CPU各核硬件栈都是紧挨着
__undef_stack(SMP)
+-------------------+ <--- cpu1 top
| |
| CPU core1 |
| |
+--------------------<--- cpu2 top
| |
| cpu core 2 |
| |
+--------------------<--- cpu3 top
| |
| cpu core 3 |
| |
+--------------------<--- cpu4 top
| |
| cpu core 4 |
| |
+-------------------+
******************************************************************************************/
//注册栈信息
VOID OsExcStackInfoReg(const StackInfo *stackInfo, UINT32 stackNum)
{
g_stackInfo = stackInfo; //全局变量指向g_excStack
g_stackNum = stackNum;
}
//task栈的初始化,设置固定的值. 0xcccccccc 和 0xcacacaca
VOID OsStackInit(VOID *stacktop, UINT32 stacksize)
{
/* initialize the task stack, write magic num to stack top */
(VOID)memset_s(stacktop, stacksize, (INT32)OS_STACK_INIT, stacksize);//清一色填 0xCACACACA
*((UINTPTR *)stacktop) = OS_STACK_MAGIC_WORD;//0xCCCCCCCCC 中文就是"烫烫烫烫" 这几个字懂点计算机的人都不会陌生了.
}
#ifdef LOSCFG_SHELL_CMD_DEBUG
SHELLCMD_ENTRY(stack_shellcmd, CMD_TYPE_EX, "stack", 1, (CmdCallBackFunc)OsExcStackInfo);//采用shell命令静态注册方式
#endif
git add -A
git commit -m '注解汇编代码,原来它们竟如此可爱,爱了爱了.
git commit -m 'CPU有哪几种工作模式?每种模式都有独立堆栈,如何用汇编初始化这些栈?
搜索 @note_pic 可查看绘制的全部字符图
搜索 @note_why 是尚未看明白的地方,有看明白的,请Pull Request完善
搜索 @note_thinking 是一些的思考和建议
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册