鸿蒙内核源码分析(异常接管篇) | 中文注解HarmonyOS源码

系统 OpenHarmony
文章由鸿蒙社区产出,想要了解更多内容请前往:51CTO和华为官方战略合作共建的鸿蒙技术社区https://harmonyos.51cto.com/

[[386679]]

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

为何要有异常接管?

拿小孩成长打比方,大人总希望孩子能健康成长,但在成长过程中总会遇到各种各样的问题,树欲静而风不止,成长路上有危险,有时是自己的问题有时是外在环境问题.就像抖音最近的流行口水歌一样,社会很单纯,复杂的是人啊,每次听到都想站起来扭几下.哎! 老衲到底做错什么了?

比如:老被小朋友欺负怎么弄? 发现乱花钱怎么搞? 青春期发育怎么应对? 失恋要跳楼又怎么办? 意思超过他的认知范围,就是靠它自己解决不了了,就需要有更高权限,更高智慧的人介入进来,帮着解决,干擦屁股的事.

那么应用程序就是那个小孩,内核就是监护人,有更高的权限,更高的智慧.而且监护人还不止一个,而是六个,每个监护人对应解决一种情况,情况发生了就由它来接管这件事的处理,小朋友你就别管了,先把你关家里,处理好了外面安全了再把应用程序放出来玩去.

这六个人处理问题都自带工具,有标准的解决方案,有自己独立的办公场所,办公场所就是栈空间(独立的),标准解决方案就是私有代码段,放在固定的位置.而自带的工具就是 SPSR_***,SP_***,LR_***寄存器组.详见 系列篇之工作模式篇 ,这里再简单回顾下有哪些工作模式,包括小孩自己(用户模式)一共是七种模式.

七种工作模式

图来源于 ARM720T.pdf第43页,在ARM体系中,CPU工作在以下七种模式中:

● 用户模式(usr):属于正常的用户模式,不能直接切换到其他模式,ARM处理器正常的程序执行状态。

● 快速中断模式(fiq):支持高速数据传输及通道处理,FIQ异常响应时进入此模式

● 外部中断模式(irq):用于通用中断处理,IRQ异常响应时进入此模式

● 管理模式(svc):操作系统保护模式,系统复位和软件中断响应时进入此模式(由系统调用执行软中断SWI命令触发)

● 数据访问终止模式(abt):当数据或指令预取终止时进入该模式,可用于处理存储器故障、实现虚拟存储器和存储器保护。

● 系统模式(sys):运行具有特权的操作系统任务,与用户模式类似,但具有可以直接切换到其他模式等特权

● 未定义指令中止模式(und):处理未定义的指令陷阱,当未定义的指令执行时进入该模式,可用于支持硬件协处理器的软件仿真。

除用户模式外,其余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寄存器内容,用户就可以据此追溯函数间的调用关系,定位异常原因。

六种异常模式实现代码

  1. /* Define exception type ID */      //ARM处理器一共有7种工作模式,除了用户和系统模式其余都叫异常工作模式 
  2. #define OS_EXCEPT_RESET          0x00   //重置功能,例如:开机就进入CPSR_SVC_MODE模式 
  3. #define OS_EXCEPT_UNDEF_INSTR    0x01   //未定义的异常,就是others 
  4. #define OS_EXCEPT_SWI            0x02   //软中断 
  5. #define OS_EXCEPT_PREFETCH_ABORT 0x03   //预取异常(取指异常), 指令三步骤: 取指,译码,执行,  
  6. #define OS_EXCEPT_DATA_ABORT     0x04   //数据异常 
  7. #define OS_EXCEPT_FIQ            0x05   //快中断异常 
  8. #define OS_EXCEPT_ADDR_ABORT     0x06   //地址异常 
  9. #define OS_EXCEPT_IRQ            0x07   //普通中断异常 

地址异常处理(Address abort)

  1. @ Description: Address abort exception handler 
  2. _osExceptAddrAbortHdl: @地址异常处理 
  3.     SUB     LR, LR, #8                                       @ LR offset to return from this exception: -8. 
  4.     STMFD   SP, {R0-R7}                                      @ Push working registers, but don`t change SP. 
  5.  
  6.     MOV     R0, #OS_EXCEPT_ADDR_ABORT                        @ Set exception ID to OS_EXCEPT_ADDR_ABORT. 
  7.  
  8.     B       _osExceptDispatch                                @跳到异常分发统一处理 

快中断处理(fiq)

  1. @ Description: Fast interrupt request exception handler 
  2. _osExceptFiqHdl: @快中断异常处理 
  3.     SUB     LR, LR, #4                                       @ LR offset to return from this exception: -4. 
  4.     STMFD   SP, {R0-R7}                                      @ Push working registers. 
  5.  
  6.     MOV     R0, #OS_EXCEPT_FIQ                               @ Set exception ID to OS_EXCEPT_FIQ. 
  7.  
  8.     B       _osExceptDispatch                                @ Branch to global exception handler. 

解读

● 快中断处理时需禁用普通中断

取指异常(Prefectch abort)

  1. @ Description: Prefectch abort exception handler 
  2. _osExceptPrefetchAbortHdl: 
  3. #ifdef LOSCFG_GDB 
  4. #if __LINUX_ARM_ARCH__ >= 7 
  5.     GDB_HANDLE OsPrefetchAbortExcHandleEntry 
  6. #endif 
  7. #else 
  8.     SUB     LR, LR, #4                                       @ LR offset to return from this exception: -4. 
  9.     STMFD   SP, {R0-R7}                                      @ Push working registers, but don`t change SP. 
  10.     MOV     R5, LR 
  11.     MRS     R1, SPSR 
  12.  
  13.     MOV     R0, #OS_EXCEPT_PREFETCH_ABORT                    @ Set exception ID to OS_EXCEPT_PREFETCH_ABORT. 
  14.  
  15.     AND     R4, R1, #CPSR_MASK_MODE                          @ Interrupted mode 
  16.     CMP     R4, #CPSR_USER_MODE                              @ User mode 
  17.     BEQ     _osExcPageFault                                   @ Branch if user mode 
  18.  
  19. _osKernelExceptPrefetchAbortHdl: 
  20.     MOV     LR, R5 
  21.     B       _osExceptDispatch                                @ Branch to global exception handler. 
  22. #endif 

数据访问异常(Data abort)

  1. @ Description: Data abort exception handler 
  2. _osExceptDataAbortHdl: @数据异常处理,缺页就属于数据异常 
  3. #ifdef LOSCFG_GDB 
  4. #if __LINUX_ARM_ARCH__ >= 7 
  5.     GDB_HANDLE OsDataAbortExcHandleEntry 
  6. #endif 
  7. #else 
  8.     SUB     LR, LR, #8                                       @ LR offset to return from this exception: -8. 
  9.     STMFD   SP, {R0-R7}                                      @ Push working registers, but don`t change SP. 
  10.     MOV     R5, LR 
  11.     MRS     R1, SPSR 
  12.  
  13.     MOV     R0, #OS_EXCEPT_DATA_ABORT                        @ Set exception ID to OS_EXCEPT_DATA_ABORT. 
  14.  
  15.     B     _osExcPageFault   @跳到缺页异常处理 
  16. #endif 

软中断处理(swi)

  1. @ Description: Software interrupt exception handler 
  2. _osExceptSwiHdl: @软中断异常处理 
  3.     SUB     SP, SP, #(4 * 16)   @先申请16个栈空间用于处理本次软中断 
  4.     STMIA   SP, {R0-R12}        @保存R0-R12寄存器值 
  5.     MRS     R3, SPSR            @读取本模式下的SPSR值 
  6.     MOV     R4, LR              @保存回跳寄存器LR 
  7.  
  8.     AND     R1, R3, #CPSR_MASK_MODE                          @ Interrupted mode 获取中断模式 
  9.     CMP     R1, #CPSR_USER_MODE                              @ User mode    是否为用户模式 
  10.     BNE     OsKernelSVCHandler                               @ Branch if not user mode 非用户模式下跳转 
  11.     @ 当为用户模式时,获取SP和LR寄出去值 
  12.     @ we enter from user mode, we need get the values of  USER mode r13(sp) and r14(lr). 
  13.     @ stmia with ^ will return the user mode registers (provided that r15 is not in the register list). 
  14.     MOV     R0, SP                                           @获取SP值,R0将作为OsArmA32SyscallHandle的参数 
  15.     STMFD   SP!, {R3}                                        @ Save the CPSR 入栈保存CPSR值 
  16.     ADD     R3, SP, #(4 * 17)                                @ Offset to pc/cpsr storage 跳到PC/CPSR存储位置 
  17.     STMFD   R3!, {R4}                                        @ Save the CPSR and r15(pc) 保存LR寄存器 
  18.     STMFD   R3, {R13, R14}^                                  @ Save user mode r13(sp) and r14(lr) 保存用户模式下的SP和LR寄存器 
  19.     SUB     SP, SP, #4 
  20.     PUSH_FPU_REGS R1    @保存中断模式(用户模式模式)                                          
  21.  
  22.     MOV     FP, #0                                           @ Init frame pointer 
  23.     CPSIE   I   @开中断,表明在系统调用期间可响应中断 
  24.     BLX     OsArmA32SyscallHandle   /*交给C语言处理系统调用*/ 
  25.     CPSID   I   @执行后续指令前必须先关中断 
  26.  
  27.     POP_FPU_REGS R1                                          @弹出FP值给R1 
  28.     ADD     SP, SP,#4                                        @ 定位到保存旧SPSR值的位置 
  29.     LDMFD   SP!, {R3}                                        @ Fetch the return SPSR 弹出旧SPSR值 
  30.     MSR     SPSR_cxsf, R3                                    @ Set the return mode SPSR 恢复该模式下的SPSR值 
  31.  
  32.     @ we are leaving to user mode, we need to restore the values of USER mode r13(sp) and r14(lr). 
  33.     @ ldmia with ^ will return the user mode registers (provided that r15 is not in the register list) 
  34.  
  35.     LDMFD   SP!, {R0-R12}                                    @恢复R0-R12寄存器 
  36.     LDMFD   SP, {R13, R14}^                                  @ Restore user mode R13/R14 恢复用户模式的R13/R14寄存器 
  37.     ADD     SP, SP, #(2 * 4)                                 @定位到保存旧PC值的位置 
  38.     LDMFD   SP!, {PC}^                                       @ Return to user 切回用户模式运行 

普通中断处理(irq)

  1. OsIrqHandler:   @硬中断处理,此时已切换到硬中断栈 
  2.     SUB     LR, LR, #4 
  3.     /* push r0-r3 to irq stack */ 
  4.     STMFD   SP, {R0-R3}     @r0-r3寄存器入 irq 栈 
  5.     SUB     R0, SP, #(4 * 4)@r0 = sp - 16 
  6.     MRS     R1, SPSR        @获取程序状态控制寄存器 
  7.     MOV     R2, LR          @r2=lr 
  8.  
  9.     /* disable irq, switch to svc mode */@超级用户模式(SVC 模式),主要用于 SWI(软件中断)和 OS(操作系统)。 
  10.     CPSID   i, #0x13                @切换到SVC模式,此处一切换,后续指令将入SVC的栈 
  11.                                     @CPSID i为关中断指令,对应的是CPSIE 
  12.     /* push spsr and pc in svc stack */ 
  13.     STMFD   SP!, {R1, R2} @实际是将 SPSR,和LR入栈,入栈顺序为 R1,R2,SP自增 
  14.     STMFD   SP, {LR}      @LR再入栈,SP不自增 
  15.  
  16.     AND     R3, R1, #CPSR_MASK_MODE @获取CPU的运行模式 
  17.     CMP     R3, #CPSR_USER_MODE     @中断是否发生在用户模式 
  18.     BNE     OsIrqFromKernel         @中断不发生在用户模式下则跳转到OsIrqFromKernel 
  19.  
  20.     /* push user sp, lr in svc stack */ 
  21.     STMFD   SP, {R13, R14}^         @sp和LR入svc栈 

解读

● 普通中断处理时可以响应快中断

未定义异常处理(undef)

  1. @ Description: Undefined instruction exception handler 
  2. _osExceptUndefInstrHdl:@出现未定义的指令处理 
  3. #ifdef LOSCFG_GDB 
  4.     GDB_HANDLE OsUndefIncExcHandleEntry 
  5. #else 
  6.                                                               @ LR offset to return from this exception:  0. 
  7.     STMFD   SP, {R0-R7}                                       @ Push working registers, but don`t change SP. 
  8.  
  9.     MOV     R0, #OS_EXCEPT_UNDEF_INSTR                        @ Set exception ID to OS_EXCEPT_UNDEF_INSTR. 
  10.  
  11.     B       _osExceptDispatch                                 @ Branch to global exception handler. 
  12.  
  13. #endif 

异常分发统一处理

  1. _osExceptDispatch: @异常模式统一分发处理 
  2.     MRS     R2, SPSR                                         @ Save CPSR before exception. 
  3.     MOV     R1, LR                                           @ Save PC before exception. 
  4.     SUB     R3, SP, #(8 * 4)                                 @ Save the start address of working registers. 
  5.  
  6.     MSR     CPSR_c, #(CPSR_INT_DISABLE | CPSR_SVC_MODE)      @ Switch to SVC mode, and disable all interrupts 
  7.     MOV     R5, SP 
  8.     EXC_SP_SET __exc_stack_top, OS_EXC_STACK_SIZE, R6, R7 
  9.  
  10.     STMFD   SP!, {R1}                                        @ Push Exception PC 
  11.     STMFD   SP!, {LR}                                        @ Push SVC LR 
  12.     STMFD   SP!, {R5}                                        @ Push SVC SP 
  13.     STMFD   SP!, {R8-R12}                                    @ Push original R12-R8, 
  14.     LDMFD   R3!, {R4-R11}                                    @ Move original R7-R0 from exception stack to original stack. 
  15.     STMFD   SP!, {R4-R11} 
  16.     STMFD   SP!, {R2}                                        @ Push task`s CPSR (i.e. exception SPSR). 
  17.  
  18.     CMP     R0, #OS_EXCEPT_DATA_ABORT       @是数据异常吗? 
  19.     BNE     1f                              @不是跳到 锚点1处 
  20.     MRC     P15, 0, R8, C6, C0, 0           @R8=C6(内存失效的地址) 0(访问数据失效) 
  21.     MRC     P15, 0, R9, C5, C0, 0           @R9=C5(内存失效的状态) 0(无效整个指令cache) 
  22.     B       3f                              @跳到锚点3处执行 
  23. 1:  CMP     R0, #OS_EXCEPT_PREFETCH_ABORT   @是预取异常吗? 
  24.     BNE     2f                              @不是跳到 锚点2处 
  25.     MRC     P15, 0, R8, C6, C0, 2           @R8=C6(内存失效的地址) 2(访问指令失效) 
  26.     MRC     P15, 0, R9, C5, C0, 1           @R9=C5(内存失效的状态) 1(虚拟地址) 
  27.     B       3f                              @跳到锚点3处执行 
  28. 2:  MOV     R8, #0 
  29.     MOV     R9, #0 
  30.  
  31. 3:  AND     R2, R2, #CPSR_MASK_MODE  
  32.     CMP     R2, #CPSR_USER_MODE                              @ User mode 
  33.     BNE     4f @不是用户模式 
  34.     STMFD   SP, {R13, R14}^                                  @ save user mode sp and lr 
  35. 4: 
  36.     SUB     SP, SP, #(4 * 2) @sp=sp-(4*2) 

非常重要的ARM37个寄存器

详见 系列篇之寄存器篇

结尾

以上为异常接管对应的代码处理,具体每种异常发生的场景和代码细节处理,因内容太多,太复杂,系列篇后续将分篇一一分析.敬请关注!

参与贡献

● 访问注解仓库地址

● Fork 本仓库 >> 新建 Feat_xxx 分支 >> 提交代码注解 >> 新建 Pull Request

● 新建 Issue

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

 

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2021-04-08 09:32:17

鸿蒙HarmonyOS应用

2021-03-24 17:18:41

鸿蒙HarmonyOS应用开发

2021-01-22 09:47:22

鸿蒙HarmonyOS应用开发

2021-03-15 15:18:16

鸿蒙HarmonyOS应用

2021-10-20 16:08:57

鸿蒙HarmonyOS应用

2021-04-09 16:39:41

鸿蒙HarmonyOS应用

2021-03-11 16:07:40

鸿蒙HarmonyOS应用开发

2022-03-11 20:23:14

鸿蒙源码分析进程管理

2021-04-12 18:14:56

鸿蒙HarmonyOS应用开发

2021-03-30 15:30:44

鸿蒙HarmonyOS应用开发

2021-04-13 09:20:15

鸿蒙HarmonyOS应用开发

2022-03-31 16:26:49

鸿蒙源码分析进程管理

2022-04-13 11:02:12

鸿蒙事件模块事件Event

2022-03-03 18:28:28

Harmony进程任务管理模块

2021-04-01 09:38:02

鸿蒙HarmonyOS应用

2022-01-10 15:31:44

鸿蒙HarmonyOS应用

2022-01-12 10:50:23

鸿蒙HarmonyOS应用

2021-04-01 17:36:30

鸿蒙HarmonyOS应用开发

2021-04-07 15:11:26

鸿蒙HarmonyOS应用

2021-06-04 09:57:49

鸿蒙HarmonyOS应用
点赞
收藏

51CTO技术栈公众号