异常分发,缺页中断 汇编部分代码注解

    百万汉字注解 + 百篇博客分析 => 挖透鸿蒙内核源码
    https://my.oschina.net/weharmony
上级 ad4e941c
此差异已折叠。
......@@ -117,7 +117,7 @@ STATIC UINTPTR g_minAddr;
STATIC UINTPTR g_maxAddr;
STATIC UINT32 g_currHandleExcCpuID = INVALID_CPUID;
VOID OsExcHook(UINT32 excType, ExcContext *excBufAddr, UINT32 far, UINT32 fsr);
UINT32 g_curNestCount[LOSCFG_KERNEL_CORE_NUM] = { 0 };//
UINT32 g_curNestCount[LOSCFG_KERNEL_CORE_NUM] = { 0 };//记录当前嵌套异常的数量
BOOL g_excFromUserMode[LOSCFG_KERNEL_CORE_NUM];//记录CPU core 异常来自用户态还是内核态 TRUE为用户态,默认为内核态
STATIC EXC_PROC_FUNC g_excHook = (EXC_PROC_FUNC)OsExcHook;//全局异常处理钩子
#if (LOSCFG_KERNEL_SMP == YES)
......@@ -239,7 +239,11 @@ STATIC INT32 OsDecodeDataFSR(UINT32 regDFSR)
ret = OsDecodeFS(bitsFS);
return ret;
}
//共享页缺失异常
/*
* 共享页缺失异常
* 异常状态寄存器(Fault Status Register -FAR)
* 异常地址寄存器(Fault Address Register -FSR)
*/
UINT32 OsArmSharedPageFault(UINT32 excType, ExcContext *frame, UINT32 far, UINT32 fsr)
{
PRINT_INFO("page fault entry!!!\n");
......@@ -1040,7 +1044,12 @@ LITE_OS_SEC_TEXT VOID STATIC OsExcPriorDisposal(ExcContext *excBufAddr)
* Description : EXC handler entry
* Input : excType --- exc type
* excBufAddr --- address of EXC buf
*///异常处理的执行入口,由汇编语言层调用 见于 los_hw_exc.s 文件
*/
/*异常处理的执行入口,由汇编语言层调用 见于 los_hw_exc.s 文件
* 参数excBufAddr为异常发生时保存下来寄存器的值.
* 异常状态寄存器(Fault Status Register -FAR)
* 异常地址寄存器(Fault Address Register -FSR)
*/
LITE_OS_SEC_TEXT_INIT VOID OsExcHandleEntry(UINT32 excType, ExcContext *excBufAddr, UINT32 far, UINT32 fsr)
{
/* Task scheduling is not allowed during exception handling *///异常处理期间不允许任务调度
......
......@@ -170,11 +170,18 @@ _osExceptUndefInstrHdl:@出现未定义的指令处理
B _osExceptDispatch @ Branch to global exception handler.
#endif
/*
STMIB(地址先增而后完成操作)、STMFA(满递增堆栈);
STMIA(完成操作而后地址递增)、STMEA(空递增堆栈);
STMDB(地址先减而后完成操作)、STMFD(满递减堆栈);
STMDA(完成操作而后地址递减)、STMED(空递减堆栈)。
*/
@ Description: Software interrupt exception handler
_osExceptSwiHdl: @软中断异常处理
@保存任务上下文(TaskContext) 开始... 一定要对照TaskContext来理解
SUB SP, SP, #(4 * 16) @先申请16个栈空间用于处理本次软中断
STMIA SP, {R0-R12} @保存R0-R12寄存器值
STMIA SP, {R0-R12} @TaskContext.R[GEN_REGS_NUM] STMIA从左到右执行,先放R0 .. R12
MRS R3, SPSR @读取本模式下的SPSR
MOV R4, LR @保存回跳寄存器LR
......@@ -185,18 +192,18 @@ _osExceptSwiHdl: @软中断异常处理
@ we enter from user mode, we need get the values of USER mode r13(sp) and r14(lr).
@ stmia with ^ will return the user mode registers (provided that r15 is not in the register list).
MOV R0, SP @获取SP,R0将作为OsArmA32SyscallHandle的参数
STMFD SP!, {R3} @ Save the CPSR 入栈保存CPSR
STMFD SP!, {R3} @ Save the CPSR 入栈保存CPSR => TaskContext.regPSR
ADD R3, SP, #(4 * 17) @ Offset to pc/cpsr storage 跳到PC/CPSR存储位置
STMFD R3!, {R4} @ Save the CPSR and r15(pc) 保存LR寄存器
STMFD R3, {R13, R14}^ @ Save user mode r13(sp) and r14(lr) 保存用户模式下的SPLR寄存器
SUB SP, SP, #4
STMFD R3!, {R4} @ Save the CPSR and r15(pc) 保存LR寄存器 => TaskContext.PC
STMFD R3, {R13, R14}^ @ Save user mode r13(sp) and r14(lr) 从右向左 保存 => TaskContext.LRSP
SUB SP, SP, #4 @ => TaskContext.resved
PUSH_FPU_REGS R1 @保存中断模式(用户模式模式)
@保存任务上下文(TaskContext) 结束
MOV FP, #0 @ Init frame pointer
CPSIE I @开中断,表明在系统调用期间可响应中断
BLX OsArmA32SyscallHandle /*交给C语言处理系统调用*/
CPSID I @执行后续指令前必须先关中断
@恢复任务上下文(TaskContext) 开始
POP_FPU_REGS R1 @弹出FP值给R1
ADD SP, SP,#4 @ 定位到保存旧SPSR值的位置
LDMFD SP!, {R3} @ Fetch the return SPSR 弹出旧SPSR
......@@ -209,23 +216,24 @@ _osExceptSwiHdl: @软中断异常处理
LDMFD SP, {R13, R14}^ @ Restore user mode R13/R14 恢复用户模式的R13/R14寄存器
ADD SP, SP, #(2 * 4) @定位到保存旧PC值的位置
LDMFD SP!, {PC}^ @ Return to user 切回用户模式运行
@恢复任务上下文(TaskContext) 结束
OsKernelSVCHandler:
ADD R0, SP, #(4 * 16) @R0=sp+64
MOV R5, R0
STMFD R0!, {R4} @ Store PC
STMFD R0!, {R4}
STMFD R0!, {R5}
OsKernelSVCHandler:@主要目的是保存ExcContext中除(R0~R12)的其他寄存器
ADD R0, SP, #(4 * 16) @跳转到保存PC,LR,SP的位置,此时R0位置刚好是SP的位置
MOV R5, R0 @R5记录SP位置,因为R0要暂时充当SP寄存器来使用
STMFD R0!, {R4} @ Store PC => ExcContext.PC
STMFD R0!, {R4} @ 相当于保存了=> ExcContext.LR
STMFD R0!, {R5} @ 相当于保存了=> ExcContext.SP
STMFD SP!, {R3} @ Push task`s CPSR (i.e. exception SPSR).
SUB SP, SP, #(4 * 2) @ user sp and lr
STMFD SP!, {R3} @ Push task`s CPSR (i.e. exception SPSR). =>ExcContext.regPSR
SUB SP, SP, #(4 * 2) @ user sp and lr => =>ExcContext.USP,ULR
MOV R0, #OS_EXCEPT_SWI @ Set exception ID to OS_EXCEPT_SWI.
@ 设置异常ID为软中断
B _osExceptionSwi @ Branch to global exception handler.
@ 跳到软中断处理
@ 跳到全局异常处理
@ Description: Prefectch abort exception handler
_osExceptPrefetchAbortHdl:
_osExceptPrefetchAbortHdl: @预取异常处理
#ifdef LOSCFG_GDB
#if __LINUX_ARM_ARCH__ >= 7
GDB_HANDLE OsPrefetchAbortExcHandleEntry
......@@ -240,7 +248,7 @@ _osExceptPrefetchAbortHdl:
AND R4, R1, #CPSR_MASK_MODE @ Interrupted mode
CMP R4, #CPSR_USER_MODE @ User mode
BEQ _osExcPageFault @ Branch if user mode
BEQ _osExcPageFault @ Branch if user mode
_osKernelExceptPrefetchAbortHdl:
MOV LR, R5
......@@ -282,10 +290,11 @@ _osExceptFiqHdl: @快中断异常处理
@ 设置参数异常类型,将作为参数传给_osExceptDispatch
B _osExceptDispatch @ Branch to global exception handler.
_osExcPageFault: @缺页异常处理函数
_osExcPageFault: @缺页中断处理函数
SUB R3, SP, #(8 * 4) @ Save the start address of working registers.
MSR CPSR_c, #(CPSR_INT_DISABLE | CPSR_SVC_MODE) @ Switch to SVC mode, and disable all interrupts
MOV R2, SP
@按 ExcContext 格式开始保存现场 因为 OsArmSharedPageFault 第二个参数就是 ExcContext
STMFD SP!, {R5} @ Push original PC
STMFD SP!, {LR} @ Push original svc LR
STMFD SP!, {R2} @ Push original svc SP
......@@ -295,31 +304,31 @@ _osExcPageFault: @缺页异常处理函数
STMFD SP!, {R1}
SUB SP, SP, #8
STMIA SP, {R13, R14}^ @ Save user mode r13(sp) and r14(lr)
MOV R4, SP
@按ExcContext格式完成保存现场
MOV R4, SP @R4指向SP ExcContext开始位置
BIC SP, SP, #7
PUSH_FPU_REGS R1
CMP R0, #OS_EXCEPT_DATA_ABORT
CMP R0, #OS_EXCEPT_DATA_ABORT @从正确的地址中取数据发生异常
BNE 1f
MRC P15, 0, R2, C6, C0, 0
MRC P15, 0, R3, C5, C0, 0
MRC P15, 0, R2, C6, C0, 0 @参数3 UINT32 far 异常地址寄存器(Fault Address Register -FSR)
MRC P15, 0, R3, C5, C0, 0 @参数4 UINT32 fsr 异常状态寄存器(Fault Status Register -FSR)
B 2f
1: MRC P15, 0, R2, C6, C0, 2
MRC P15, 0, R3, C5, C0, 1
2: MOV R1, R4
2: MOV R1, R4 @参数R1 ExcContext开始位置
MOV R5, R0
MOV R8, R2
MOV R9, R3
CPSIE I
BLX OsArmSharedPageFault
CPSID I
CPSIE I @禁止中断
BLX OsArmSharedPageFault @缺页中断处理参数(UINT32 excType, ExcContext *frame, UINT32 far, UINT32 fsr)
CPSID I @恢复中断
POP_FPU_REGS R1
MOV SP, R4
CMP R0, #0
BEQ _OsExcReturn
BEQ _OsExcReturn @异常返回
MOV R0, R5 @ exc type
B _osExceptionSwi @跳到软中断执行,系统调用就是通过软中断实现
......@@ -335,17 +344,17 @@ _osExceptDispatch: @处理异常分发
MSR CPSR_c, #(CPSR_INT_DISABLE | CPSR_SVC_MODE) @ Switch to SVC mode, and disable all interrupts
@ 切换到SVC模式,屏蔽掉所有中断
MOV R5, SP @ R5 = SP ,保存SP位置
EXC_SP_SET __exc_stack_top, OS_EXC_STACK_SIZE, R6, R7 @ 切换到当前CPUSVC模式栈处理
STMFD SP!, {R1} @ Push Exception PC 保存上一个工作模式的PC
STMFD SP!, {LR} @ Push SVC LR 保存上一个工作模式的LR
STMFD SP!, {R5} @ Push SVC SP 保存上一个工作模式的SP
STMFD SP!, {R8-R12} @ Push original R12-R8,保存上一个工作模式的R8-R12
EXC_SP_SET __exc_stack_top, OS_EXC_STACK_SIZE, R6, R7 @ 切换到当前CPUSVC模式栈处理,R6,R7用于记录CPUid和偏移量
@开始 保存异常上下文(ExcContext), 顺序是 USP,ULR,SPSR,R0~R15
STMFD SP!, {R1} @ Push Exception PC 保存上一个工作模式的PC => ExcContext.PC
STMFD SP!, {LR} @ Push SVC LR 保存上一个工作模式的LR => ExcContext.LR
STMFD SP!, {R5} @ Push SVC SP 保存上一个工作模式的SP => ExcContext.SP
STMFD SP!, {R8-R12} @ Push original R12-R8,保存上一个工作模式的R8-R12 => ExcContext.R8 - R12
LDMFD R3!, {R4-R11} @ Move original R7-R0 from exception stack to original stack.
@ 将保存在上一个工作模式的R0~R7取出
STMFD SP!, {R4-R11} @ 将上一个工作模式的R0~R7保存到新的栈中
STMFD SP!, {R2} @ Push task`s CPSR (i.e. exception SPSR).
@ 任务的CPSR入栈
STMFD SP!, {R4-R11} @ 将上一个工作模式的R0~R7保存到新的栈中 => ExcContext.R0 - R7
STMFD SP!, {R2} @ Push task`s CPSR (i.e. exception SPSR). => ExcContext.regCPSR
@除了ExcContext.USP,ULR两个值,保存异常上下文(ExcContext)其他值完成.
CMP R0, #OS_EXCEPT_DATA_ABORT @是数据异常吗?
BNE 1f @不是跳到 锚点1
MRC P15, 0, R8, C6, C0, 0 @R8=C6(内存失效的地址) 0(访问数据失效)
......@@ -358,58 +367,58 @@ _osExceptDispatch: @处理异常分发
B 3f @直接跳到 锚点3: 处执行
2: MOV R8, #0 @R8=0
MOV R9, #0 @R9=0
@可看出异常过后,R8,R9的值发生变化,获取协处理对应的数据.
3: AND R2, R2, #CPSR_MASK_MODE @获取当前工作模式
CMP R2, #CPSR_USER_MODE @ User mode 是否为用户模式
BNE 4f @不是用户模式,跳到 锚点4: 处运行
STMFD SP, {R13, R14}^ @ save user mode sp and lr 保存用户模式的SPLR
4:
SUB SP, SP, #(4 * 2) @sp=sp-(4*2) 指向真正的栈顶
_osExceptionSwi: @软中断的处理,系统调用就是由软中断实现的
4: @保存 => ExcContext.USP,ULR
SUB SP, SP, #(4 * 2) @非用户模式下不需要保存 USP,ULR,所以跳过2个空间
@填充好ExcContext.USP,ULR两个值,R8,R9 获得异常的信息,将接着往下执行
_osExceptionSwi: @异常软处理
MOV R1, SP @ The second argument to the exception
@ 异常的第二个参数,第一个参数是R0
MRC P15, 0, R4, C0, C0, 5 @ R4获取当前cpu id
AND R4, R4, #MPIDR_CPUID_MASK @ Get Current cpu id
LSL R2, R4, #2 @(Logic Shift Left)逻辑左移指令 R2 = R4<<2 @note_why 没看明白这句话的含义
LDR R3, =g_curNestCount @ if(g_curNestCount > 0) dump to _osExceptionGetSP
LDR R3, =g_curNestCount @ if(g_curNestCount > 0) dump to _osExceptionGetSP 嵌套异常
@将g_curNestCount的地址存入R3
ADD R3, R3, R2 @ R3 = R3 + R2
LDR R4, [R3] @R4 = *R3
ADD R3, R3, R2 @ R3 = R3 + R2,
LDR R4, [R3] @ R4 = *R3
CMP R4, #0 @R4 0对比
BNE _osExceptionGetSP @不相等则跳转
BNE _osExceptionGetSP @(g_curNestCount > 0)说明有嵌套异常,必须先处理异常
@判断异常发生在任务堆栈或系统堆栈中
LDR R3, =g_intCount @ Judge the exception is occur in task stack or system stack
ADD R3, R3, R2
LDR R4, [R3]
LDR R4, [R3] @获取g_intCount[ArchCurrCpuid()]的值
@软中断的优先级要低于硬中断,这里判断是否有硬中断发生,有则需先处理硬中断
CMP R4, #0 @ if (g_intCount[ArchCurrCpuid()] > 0) 当前有中断要处理
BNE _osExceptionGetSP @ can not switch svc stack 无法切换到svc堆栈
@切换到统一异常堆栈(SVC栈)
BNE _osExceptionGetSP @ can not switch svc stack 有中断要处理,必须先处理异常
@切换到SVC栈中处理未定义的异常
EXC_SP_SET __svc_stack_top, OS_EXC_SVC_STACK_SIZE, R6, R7 @ Switch to unified exception stack.
ADD R4, R4, #1
STR R4, [R3]
_osExceptionGetSP:
MOV R2, R8 @ far CP15c6获取
MOV R3, R9 @ fsr CP15c5获取
LDR R5, =OsExcHandleEntry @ OsExcHandleEntry(UINT32 excType, ExcContext * excBufAddr)
_osExceptionGetSP:@处理异常 R1=sp
MOV R2, R8 @ far CP15c6获取 异常地址寄存器(Fault Address Register -FSR)
MOV R3, R9 @ fsr CP15c5获取 异常状态寄存器(Fault Status Register -FSR)
LDR R5, =OsExcHandleEntry @ OsExcHandleEntry(UINT32 excType, ExcContext * excBufAddr,UINT32 far, UINT32 fsr)
BX R5 @LDR为加载指令把OsExcHandleEntry的地址放入R5BX为带分支的跳转指令,去执行OsExcHandleEntry
_OsExcReturn:
_OsExcReturn:@异常返回
LDR R0, [SP, #(2 * 4)]
AND R0, R0, #CPSR_MASK_MODE
CMP R0, #CPSR_USER_MODE @ User mode
BNE _OsExcReturnToKernel @非用户模式跳转执行
LDMFD SP, {R13, R14}^ @ load user mode sp and lr
_OsExcReturnToKernel:
ADD SP, SP, #(2 * 4)
LDMFD SP!, {R1}
MSR SPSR_cxsf, R1 @ Set the return mode SPSR
LDMFD SP!, {R0-R12}
ADD SP, SP, #4
LDMFD SP!, {LR, PC}^
LDMFD SP, {R13, R14}^ @ load user mode sp and lr
@恢复splr的值 < = ExcContext.USP,ULR 回到用户栈继续运行
_OsExcReturnToKernel: @内核模式的异常返回
ADD SP, SP, #(2 * 4) @先跳开ExcContext.USPULR
LDMFD SP!, {R1} @取出R1 = ExcContext.SPSR
MSR SPSR_cxsf, R1 @ Set the return mode SPSR < = ExcContext.regCPSR
LDMFD SP!, {R0-R12} @依次恢复 R0~R12 < = ExcContext.R0 - R12
ADD SP, SP, #4 @跳过ExcContext.SP
LDMFD SP!, {LR, PC}^ @恢复LRPC的值 < = ExcContext.LR ,PC
.end
git add -A
git commit -m '鸿蒙内核源码分析(中断管理篇) | 硬中断的实现类似观察者模式 | 百篇博客分析鸿蒙源码 | v44.01
git commit -m '异常分发,缺页中断 汇编部分代码注解
百万汉字注解 + 百篇博客分析 => 挖透鸿蒙内核源码
https://my.oschina.net/weharmony
'
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册