鸿蒙内核源码分析(中断切换篇) | 自下而上逐行分析保存和恢复中断现场全过程 | 百篇博客分析鸿蒙源码 | v42.01

    百万汉字注解 + 百篇博客分析 => 挖透鸿蒙内核源码
    https://weharmony.gitee.io
上级 95734ecf
......@@ -259,46 +259,49 @@ OsKernelTaskLoad: @内核任务的加载
LDMFD SP!, {LR, PC}^ @返回地址赋给pc指针,直接跳出.
OsIrqHandler: @硬中断处理,此时已切换到硬中断栈
SUB LR, LR, #4
SUB LR, LR, #4 @记录译码指令地址,以防切换过程丢失指令.
/* push r0-r3 to irq stack */
STMFD SP, {R0-R3} @r0-r3寄存器入 irq
SUB R0, SP, #(4 * 4)@r0 = sp - 16
SUB R0, SP, #(4 * 4)@r0 = sp - 16,目的是记录{R0-R3}4个寄存器保存的开始位置,届时从R3开始出栈
MRS R1, SPSR @获取程序状态控制寄存器
MOV R2, LR @r2=lr
/* disable irq, switch to svc mode */@超级用户模式(SVC 模式),主要用于 SWI(软件中断) OS(操作系统)
CPSID i, #0x13 @切换到SVC模式,此处一切换,后续指令将SVC的栈
CPSID i, #0x13 @切换到SVC模式,此处一切换,后续指令将SVC栈运行
@CPSID i为关中断指令,对应的是CPSIE
@TaskIrqContext 开始保存中断现场 ......
/* push spsr and pc in svc stack */
STMFD SP!, {R1, R2} @实际是将 SPSR,LR入栈,入栈顺序为 R1,R2,SP自增
STMFD SP, {LR} @LR再入栈,SP不自增
STMFD SP!, {R1, R2} @实际是将 SPSR,PC入栈对应TaskIrqContext.PC,TaskIrqContext.CPSR,
STMFD SP, {LR} @LR再入栈,SP不自增,如果是用户模式,LR值将被 282行:STMFD SP, {R13, R14}^覆盖
@如果非用户模式,将被 286行:SUB SP, SP, #(2 * 4) 跳过.
AND R3, R1, #CPSR_MASK_MODE @获取CPU的运行模式
CMP R3, #CPSR_USER_MODE @中断是否发生在用户模式
BNE OsIrqFromKernel @中断不发生在用户模式下则跳转到OsIrqFromKernel
BNE OsIrqFromKernel @非用户模式不用将USP,ULR保存在TaskIrqContext
/* push user sp, lr in svc stack */
STMFD SP, {R13, R14}^ @spLRsvc
STMFD SP, {R13, R14}^ @将用户模式的spLRsvc
OsIrqFromKernel: @从内核发起中断
/* from svc not need save sp and lr */@svc模式下发生的中断不需要保存splr寄存器值
SUB SP, SP, #(2 * 4) @申请8个栈空间
SUB SP, SP, #(2 * 4) @目的是为了留白给 TaskIrqContext.USP,TaskIrqContext.ULR
@TaskIrqContext.ULR已经在 276行保存了,276行用的是SP而不是SP!,所以此处要跳2个空间
/* pop r0-r3 from irq stack*/
LDMFD R0, {R0-R3} @R0位置依次出栈
LDMFD R0, {R0-R3} @R0位置依次出栈
/* push caller saved regs as trashed regs in svc stack */
STMFD SP!, {R0-R3, R12} @R0~R3入栈,R12入栈
STMFD SP!, {R0-R3, R12} @寄存器入栈,对应 TaskIrqContext.R0~R3,R12
/* 8 bytes stack align */
SUB SP, SP, #4 @栈对齐
SUB SP, SP, #4 @栈对齐 对应TaskIrqContext.resved
/*
* save fpu regs in case in case those been
* altered in interrupt handlers.
*/
PUSH_FPU_REGS R0 @保存fpu regs,以防中断处理程序中的fpu regs被修改。
@TaskIrqContext 结束保存中断现场 ......
@开始执行真正的中断处理函数了.
#ifdef LOSCFG_IRQ_USE_STANDALONE_STACK @是否使用了独立的IRQ栈
PUSH {R4} @R4先入栈保存,接下来要切换栈,需保存现场
MOV R4, SP @R4=SP
......@@ -319,8 +322,8 @@ OsIrqFromKernel: @从内核发起中断
CMP R0, #0 @是否需要调度,R0为参数保存值
BLNE OsSchedPreempt @不相等,R00,一般是 1
MOV R0,SP @ OsSaveSignalContextIrq 参数1来源
MOV R1,R7 @ OsSaveSignalContextIrq 参数2来源
MOV R0,SP @参数
MOV R1,R7 @参数
BL OsSaveSignalContextIrq @跳转至C代码
/* restore fpu regs */
......@@ -329,29 +332,31 @@ OsIrqFromKernel: @从内核发起中断
ADD SP, SP, #4 @sp = sp + 4
OsIrqContextRestore: @恢复硬中断环境
LDR R0, [SP, #(4 * 7)] @读取 SP+28 位置数据给R0
MSR SPSR_cxsf, R0 @恢复spsr
LDR R0, [SP, #(4 * 7)] @R0 = sp + 7,目的是跳到恢复中断现场TaskIrqContext.CPSR位置,刚好是TaskIrqContext倒数第7的位置.
MSR SPSR_cxsf, R0 @恢复spsr 即:spsr = TaskIrqContext.CPSR
AND R0, R0, #CPSR_MASK_MODE @掩码找出当前工作模式
CMP R0, #CPSR_USER_MODE @是否为用户模式?
LDMFD SP!, {R0-R3, R12} @SP位置依次出栈
BNE OsIrqContextRestoreToKernel @CPU工作在非用户模式
@TaskIrqContext 开始恢复中断现场 ......
LDMFD SP!, {R0-R3, R12} @SP位置依次出栈 对应 TaskIrqContext.R0~R3,R12
@此时已经恢复了5个寄存器,接来下是TaskIrqContext.USP,TaskIrqContext.ULR
BNE OsIrqContextRestoreToKernel @看非用户模式,怎么恢复中断现场.
/* load user sp and lr, and jump cpsr */
LDMFD SP, {R13, R14}^
ADD SP, SP, #(3 * 4)
LDMFD SP, {R13, R14}^ @出栈,恢复用户模式splr 即:TaskIrqContext.USP,TaskIrqContext.ULR
ADD SP, SP, #(3 * 4) @3个位置,跳过 CPSR ,因为上一句不是 SP!,所以跳3个位置,刚好到了保存TaskIrqContext.PC的位置
/* return to user mode */
LDMFD SP!, {PC}^ @回到用户模式
LDMFD SP!, {PC}^ @回到用户模式,整个中断过程完成
@TaskIrqContext 结束恢复中断现场(用户模式下) ......
OsIrqContextRestoreToKernel:
OsIrqContextRestoreToKernel:@从内核恢复中断
/* svc mode not load sp */
ADD SP, SP, #4
LDMFD SP!, {LR}
ADD SP, SP, #4 @其实是跳过TaskIrqContext.USP,因为在内核模式下并没有保存这个值,翻看 287
LDMFD SP!, {LR} @弹出LR
/* jump cpsr and return to svc mode */
ADD SP, SP, #4
LDMFD SP!, {PC}^
ADD SP, SP, #4 @跳过cpsr
LDMFD SP!, {PC}^ @回到svc模式,整个中断过程完成
@TaskIrqContext 结束恢复中断现场(内核模式下) ......
FUNCTION(ArchSpinLock) @非要拿到锁
mov r1, #1 @r1=1
......
......@@ -348,7 +348,7 @@ cpu_start: /* CPU 启动 */
bl mmu_setup /* 安装MMU */
bl secondary_cpu_start /* 启动次级CPU */
b . //注意 b . 就会跳转到当前地址,相当死循环
b . //注意 b . 就会跳转到当前地址,相当死循环
secondary_cpu_init: /* 次级CPU初始化 */
#ifdef LOSCFG_TEE_ENABLE
......@@ -495,7 +495,7 @@ __abt_stack:
__abt_stack_top:
__irq_stack:
.space OS_EXC_IRQ_STACK_SIZE * CORE_NUM
.space OS_EXC_IRQ_STACK_SIZE * CORE_NUM //64
__irq_stack_top:
__fiq_stack:
......
......@@ -334,7 +334,7 @@ typedef struct {
SchedStat schedStat; /**< Schedule statistics */ //调度统计
#endif
#endif
UINTPTR userArea; //使用区域,由运行时划定,根据运行态不同而不同
UINTPTR userArea; //用户区域,由运行时划定,根据运行态不同而不同
UINTPTR userMapBase; //用户模式下的栈底位置
UINT32 userMapSize; /**< user thread stack size ,real size : userMapSize + USER_STACK_MIN_SIZE */
UINT32 processID; /**< Which belong process *///所属进程ID
......
......@@ -632,7 +632,7 @@ void OsSaveSignalContextIrq(unsigned int *sp, unsigned int r7)
sig_cb *sigcb = NULL;
unsigned long cpsr;
UINT32 intSave;
TaskIrqContext *context = (TaskIrqContext *)(sp);//汇编设置好SP位置
TaskIrqContext *context = (TaskIrqContext *)(sp);//汇编设置SP,从SP位置取出TaskIrqContext
OS_RETURN_IF_VOID(sp == NULL);
cpsr = context->CPSR;
......
此差异已折叠。
git add -A
git commit -m '注解汇编代码如何保存任务上下文
git commit -m '鸿蒙内核源码分析(中断切换篇) | 自下而上逐行分析保存和恢复中断现场全过程 | 百篇博客分析鸿蒙源码 | v42.01
百万汉字注解 + 百篇博客分析 => 挖透鸿蒙内核源码
https://weharmony.gitee.io
'
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册