为何要有异常接管?
拿小孩成长打比方,大人总希望孩子能健康成长,但在成长过程中总会遇到各种各样的问题,树欲静而风不止,成长路上有危险,有时是自己的问题有时是外在环境问题.就像抖音最近的流行口水歌一样,社会很单纯,复杂的是人啊,每次听到都想站起来扭几下.哎! 老衲到底做错什么了?
比如:老被其他小朋友欺负怎么弄? 发现乱花钱怎么搞? 青春期发育怎么应对? 失恋要跳楼又怎么办? 意思是超过他的认知范围,靠它自己解决不了了,就需要有更高权限,更高智慧的人介入进来,帮着解决,干擦屁股的事.
那么应用程序就是那个小孩,内核就是监护人,有更高的权限,更高的智慧.而且监护人还不止一个,而是六个,每个监护人对应解决一种情况,情况发生了就由它来接管这件事的处理,小朋友你就别管了哈,先把你关家里,处理好了外面安全了再把应用程序放出来玩去.
这六个人处理问题都自带工具,有标准的解决方案,有自己独立的办公场所,办公场所就是栈空间(独立的),标准解决方案就是私有代码段,放在固定的位置.而自带的工具就是SPSR_***,SP_***,LR_***寄存器组.详见系列篇之工作模式篇,这里再简单回顾下有哪些工作模式,包括小孩自己(用户模式)一共是七种模式.
七种工作模式
图来源于ARM720T.pdf第43页,在ARM体系中,CPU工作在以下七种模式中:
用户模式(usr):该模式是用户程序的工作模式,它运行在操作系统的用户态,它没有权限去操作其它硬件资源,只能执行处理自己的数据,也不能切换到其它模式下,要想访问硬件资源或切换到其它模式只能通过软中断或产生异常。
快速中断模式(fiq):快速中断模式是相对一般中断模式而言的,用来处理高优先级中断的模式,处理对时间要求比较紧急的中断请求,主要用于高速数据传输及通道处理中。
普通中断模式(irq):一般中断模式也叫普通中断模式,用于处理一般的中断请求,通常在硬件产生中断信号之后自动进入该模式,该模式可以自由访问系统硬件资源。
管理模式(svc):操作系统保护模式,CPU上电复位和当应用程序执行 SVC 指令调用系统服务时也会进入此模式,操作系统内核的普通代码通常工作在这个模式下。
终止模式(abt):当数据或指令预取终止时进入该模式,中止模式用于支持虚拟内存或存储器保护,当用户程序访问非法地址,没有权限读取的内存地址时,会进入该模式,
系统模式(sys):供操作系统使用的高特权用户模式,与用户模式类似,但具有可以直接切换到其他模式等特权,用户模式与系统模式两者使用相同的寄存器,都没有SPSR(Saved Program Statement Register,已保存程序状态寄存器),但系统模式比用户模式有更高的权限,可以访问所有系统资源。
未定义模式(und):未定义模式用于支持硬件协处理器的软件仿真,CPU在指令的译码阶段不能识别该指令操作时,会进入未定义模式。
除用户模式外,其余6种工作模式都属于特权模式
特权模式中除了系统模式以外的其余5种模式称为异常模式
大多数程序运行于用户模式
进入特权模式是为了处理中断、异常、或者访问被保护的系统资源
硬件权限级别:系统模式 > 异常模式 > 用户模式
快中断(fiq)与慢中断(irq)区别:快中断处理时禁止中断
每种模式都有自己独立的入口和独立的运行栈空间.系列篇之CPU篇已介绍过只要提供了入口函数和运行空间,CPU就可以干活了.入口函数解决了指令来源问题,运行空间解决了指令的运行场地问题. 而且在多核情况下,每个CPU核的每种特权模式都有自己独立的栈空间.注意是特权模式下的栈空间,用户模式的栈空间是由用户(应用)程序提供的.
官方概念
异常接管是操作系统对运行期间发生的异常情况(芯片硬件异常)进行处理的一系列动作,例如打印异常发生时当前函数的调用栈信息、CPU现场信息、任务的堆栈情况等。 异常接管作为一种调测手段,可以在系统发生异常时给用户提供有用的异常信息,譬如异常类型、发生异常时的系统状态等,方便用户定位分析问题。
鸿蒙的异常接管,在系统发生异常时的处理动作为:显示异常发生时正在运行的任务信息(包括任务名、任务号、堆栈大小等),以及CPU现场等信息。
进入和退出异常方式
异常接管切换需要处理好两件事:
一个是代码要切到哪个位置,也就是要重置PC寄存器,每种异常模式下的切换方式如图:
另一个是要恢复每种模式的状态,即CPSR(1个)和SPSR(共5个)的关系,对M[4:0]的修改,如图:
以下是M[4:0]在每种模式下具体操作方式:
栈帧
每个函数都有自己的栈空间,称为栈帧。调用函数时,会创建子函数的栈帧,同时将函数入参、局部变量、寄存器入栈。栈帧从高地址向低地址生长,也就是说栈底是高地址,栈顶是底地址. 详见系列篇之用栈方式篇
以ARM32 CPU架构为例,每个栈帧中都会保存PC、LR、SP和FP寄存器的历史值。 堆栈分析原理如下图所示,实际堆栈信息根据不同CPU架构有所差异,此处仅做示意。 图中不同颜色的寄存器表示不同的函数。可以看到函数调用过程中,寄存器的保存。通过FP寄存器,栈回溯到异常函数的父函数,继续按照规律对栈进行解析,推出函数调用关系,方便用户定位问题。
解读
LR寄存器(Link Register),链接寄存器,指向函数的返回地址。
R11:可以用作通用寄存器,在开启特定编译选项时可以用作帧指针寄存器FP,用来实现栈回溯功能。 GNU编译器(gcc)默认将R11作为存储变量的通用寄存器,因而默认情况下无法使用FP的栈回溯功能。为支持调用栈解析功能,需要在编译参数中添加-fno-omit-frame-pointer选项,提示编译器将R11作为FP使用。
FP寄存器(Frame Point),帧指针寄存器,指向当前函数的父函数的栈帧起始地址。利用该寄存器可以得到父函数的栈帧,从栈帧中获取父函数的FP,就可以得到祖父函数的栈帧,以此类推,可以追溯程序调用栈,得到函数间的调用关系。 当系统发生异常时,系统打印异常函数的栈帧中保存的寄存器内容,以及父函数、祖父函数的栈帧中的LR、FP寄存器内容,用户就可以据此追溯函数间的调用关系,定位异常原因。
六种异常模式实现代码
/* Define exception type ID */ //ARM处理器一共有7种工作模式,除了用户和系统模式其余都叫异常工作模式 #define OS_EXCEPT_RESET 0x00 //重置功能,例如:开机就进入CPSR_SVC_MODE模式 #define OS_EXCEPT_UNDEF_INSTR 0x01 //未定义的异常,就是others #define OS_EXCEPT_SWI 0x02 //软中断 #define OS_EXCEPT_PREFETCH_ABORT 0x03 //预取异常(取指异常), 指令三步骤: 取指,译码,执行, #define OS_EXCEPT_DATA_ABORT 0x04 //数据异常 #define OS_EXCEPT_FIQ 0x05 //快中断异常 #define OS_EXCEPT_ADDR_ABORT 0x06 //地址异常 #define OS_EXCEPT_IRQ 0x07 //普通中断异常
地址异常处理(Address abort)
@ Description: Address abort exception handler _osExceptAddrAbortHdl: @地址异常处理 SUB LR, LR, #8 @ LR offset to return from this exception: -8. STMFD SP, {R0-R7} @ Push working registers, but don`t change SP. MOV R0, #OS_EXCEPT_ADDR_ABORT @ Set exception ID to OS_EXCEPT_ADDR_ABORT. B _osExceptDispatch @跳到异常分发统一处理
快中断处理(fiq)
@ Description: Fast interrupt request exception handler _osExceptFiqHdl: @快中断异常处理 SUB LR, LR, #4 @ LR offset to return from this exception: -4. STMFD SP, {R0-R7} @ Push working registers. MOV R0, #OS_EXCEPT_FIQ @ Set exception ID to OS_EXCEPT_FIQ. B _osExceptDispatch @ Branch to global exception handler.
解读
快中断处理时需禁用普通中断
取指异常(Prefectch abort)
@ Description: Prefectch abort exception handler _osExceptPrefetchAbortHdl: #ifdef LOSCFG_GDB #if __LINUX_ARM_ARCH__ >= 7 GDB_HANDLE OsPrefetchAbortExcHandleEntry #endif #else SUB LR, LR, #4 @ LR offset to return from this exception: -4. STMFD SP, {R0-R7} @ Push working registers, but don`t change SP. MOV R5, LR MRS R1, SPSR MOV R0, #OS_EXCEPT_PREFETCH_ABORT @ Set exception ID to OS_EXCEPT_PREFETCH_ABORT. AND R4, R1, #CPSR_MASK_MODE @ Interrupted mode CMP R4, #CPSR_USER_MODE @ User mode BEQ _osExcPageFault @ Branch if user mode _osKernelExceptPrefetchAbortHdl: MOV LR, R5 B _osExceptDispatch @ Branch to global exception handler. #endif
数据访问异常(Data abort)
@ Description: Data abort exception handler _osExceptDataAbortHdl: @数据异常处理,缺页就属于数据异常 #ifdef LOSCFG_GDB #if __LINUX_ARM_ARCH__ >= 7 GDB_HANDLE OsDataAbortExcHandleEntry #endif #else SUB LR, LR, #8 @ LR offset to return from this exception: -8. STMFD SP, {R0-R7} @ Push working registers, but don`t change SP. MOV R5, LR MRS R1, SPSR MOV R0, #OS_EXCEPT_DATA_ABORT @ Set exception ID to OS_EXCEPT_DATA_ABORT. B _osExcPageFault @跳到缺页异常处理 #endif
软中断处理(swi)
@ Description: Software interrupt exception handler _osExceptSwiHdl: @软中断异常处理 SUB SP, SP, #(4 * 16) @先申请16个栈空间用于处理本次软中断 STMIA SP, {R0-R12} @保存R0-R12寄存器值 MRS R3, SPSR @读取本模式下的SPSR值 MOV R4, LR @保存回跳寄存器LR AND R1, R3, #CPSR_MASK_MODE @ Interrupted mode 获取中断模式 CMP R1, #CPSR_USER_MODE @ User mode 是否为用户模式 BNE OsKernelSVCHandler @ Branch if not user mode 非用户模式下跳转 @ 当为用户模式时,获取SP和LR寄出去值 @ 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值 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) 保存用户模式下的SP和LR寄存器 SUB SP, SP, #4 PUSH_FPU_REGS R1 @保存中断模式(用户模式模式) MOV FP, #0 @ Init frame pointer CPSIE I @开中断,表明在系统调用期间可响应中断 BLX OsArmA32SyscallHandle /*交给C语言处理系统调用*/ CPSID I @执行后续指令前必须先关中断 POP_FPU_REGS R1 @弹出FP值给R1 ADD SP, SP,#4 @ 定位到保存旧SPSR值的位置 LDMFD SP!, {R3} @ Fetch the return SPSR 弹出旧SPSR值 MSR SPSR_cxsf, R3 @ Set the return mode SPSR 恢复该模式下的SPSR值 @ we are leaving to user mode, we need to restore the values of USER mode r13(sp) and r14(lr). @ ldmia with ^ will return the user mode registers (provided that r15 is not in the register list) LDMFD SP!, {R0-R12} @恢复R0-R12寄存器 LDMFD SP, {R13, R14}^ @ Restore user mode R13/R14 恢复用户模式的R13/R14寄存器 ADD SP, SP, #(2 * 4) @定位到保存旧PC值的位置 LDMFD SP!, {PC}^ @ Return to user 切回用户模式运行
普通中断处理(irq)
OsIrqHandler: @硬中断处理,此时已切换到硬中断栈 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 MRS R1, SPSR @获取程序状态控制寄存器 MOV R2, LR @r2=lr /* disable irq, switch to svc mode */@超级用户模式(SVC 模式),主要用于 SWI(软件中断)和 OS(操作系统)。 CPSID i, #0x13 @切换到SVC模式,此处一切换,后续指令将入SVC的栈 @CPSID i为关中断指令,对应的是CPSIE /* push spsr and pc in svc stack */ STMFD SP!, {R1, R2} @实际是将 SPSR,和LR入栈,入栈顺序为 R1,R2,SP自增 STMFD SP, {LR} @LR再入栈,SP不自增 AND R3, R1, #CPSR_MASK_MODE @获取CPU的运行模式 CMP R3, #CPSR_USER_MODE @中断是否发生在用户模式 BNE OsIrqFromKernel @中断不发生在用户模式下则跳转到OsIrqFromKernel /* push user sp, lr in svc stack */ STMFD SP, {R13, R14}^ @sp和LR入svc栈
解读
普通中断处理时可以响应快中断
未定义异常处理(undef)
@ Description: Undefined instruction exception handler _osExceptUndefInstrHdl:@出现未定义的指令处理 #ifdef LOSCFG_GDB GDB_HANDLE OsUndefIncExcHandleEntry #else @ LR offset to return from this exception: 0. STMFD SP, {R0-R7} @ Push working registers, but don`t change SP. MOV R0, #OS_EXCEPT_UNDEF_INSTR @ Set exception ID to OS_EXCEPT_UNDEF_INSTR. B _osExceptDispatch @ Branch to global exception handler. #endif
异常分发统一处理
_osExceptDispatch: @异常模式统一分发处理 MRS R2, SPSR @ Save CPSR before exception. MOV R1, LR @ Save PC before exception. 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 R5, SP EXC_SP_SET __exc_stack_top, OS_EXC_STACK_SIZE, R6, R7 STMFD SP!, {R1} @ Push Exception PC STMFD SP!, {LR} @ Push SVC LR STMFD SP!, {R5} @ Push SVC SP STMFD SP!, {R8-R12} @ Push original R12-R8, LDMFD R3!, {R4-R11} @ Move original R7-R0 from exception stack to original stack. STMFD SP!, {R4-R11} STMFD SP!, {R2} @ Push task`s CPSR (i.e. exception SPSR). CMP R0, #OS_EXCEPT_DATA_ABORT @是数据异常吗? BNE 1f @不是跳到 锚点1处 MRC P15, 0, R8, C6, C0, 0 @R8=C6(内存失效的地址) 0(访问数据失效) MRC P15, 0, R9, C5, C0, 0 @R9=C5(内存失效的状态) 0(无效整个指令cache) B 3f @跳到锚点3处执行 1: CMP R0, #OS_EXCEPT_PREFETCH_ABORT @是预取异常吗? BNE 2f @不是跳到 锚点2处 MRC P15, 0, R8, C6, C0, 2 @R8=C6(内存失效的地址) 2(访问指令失效) MRC P15, 0, R9, C5, C0, 1 @R9=C5(内存失效的状态) 1(虚拟地址) B 3f @跳到锚点3处执行 2: MOV R8, #0 MOV R9, #0 3: AND R2, R2, #CPSR_MASK_MODE CMP R2, #CPSR_USER_MODE @ User mode BNE 4f @不是用户模式 STMFD SP, {R13, R14}^ @ save user mode sp and lr 4: SUB SP, SP, #(4 * 2) @sp=sp-(4*2)
非常重要的ARM37个寄存器
结尾
以上为异常接管对应的代码处理,具体每种异常发生的场景和代码细节处理
编辑:hfy
-
ARM
+关注
关注
134文章
9088浏览量
367405 -
寄存器
+关注
关注
31文章
5336浏览量
120244 -
cpu
+关注
关注
68文章
10855浏览量
211604 -
鸿蒙系统
+关注
关注
183文章
2634浏览量
66308
发布评论请先 登录
相关推荐
评论