0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

鸿蒙内核源码分析之为何要有异常接管?

鸿蒙系统HarmonyOS 来源:my.oschina 作者:鸿蒙内核源码分析 2021-04-25 14:57 次阅读

为何要有异常接管?

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

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

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

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

七种工作模式

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

pIYBAGCFEduATXz2AATRBfaA7Mc170.png

用户模式(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寄存器,每种异常模式下的切换方式如图:

o4YBAGCFEe6AGT9yAAIvdU9Qu3Q163.png

另一个是要恢复每种模式的状态,即CPSR(1个)和SPSR(共5个)的关系,对M[4:0]的修改,如图:

pIYBAGCFEfuAdQwQAAGEK0_cCEc202.png

以下是M[4:0]在每种模式下具体操作方式:

栈帧

每个函数都有自己的栈空间,称为栈帧。调用函数时,会创建子函数的栈帧,同时将函数入参、局部变量、寄存器入栈。栈帧从高地址向低地址生长,也就是说栈底是高地址,栈顶是底地址. 详见系列篇之用栈方式篇

以ARM32 CPU架构为例,每个栈帧中都会保存PC、LR、SP和FP寄存器的历史值。 堆栈分析原理如下图所示,实际堆栈信息根据不同CPU架构有所差异,此处仅做示意。 图中不同颜色的寄存器表示不同的函数。可以看到函数调用过程中,寄存器的保存。通过FP寄存器,栈回溯到异常函数的父函数,继续按照规律对栈进行解析,推出函数调用关系,方便用户定位问题。

pIYBAGCFEiSAcZPyAACu9SZfN9Y985.png

解读

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个寄存器

pIYBAGCFEpiAY5PdAAM5VlArjfA004.png

结尾

以上为异常接管对应的代码处理,具体每种异常发生的场景和代码细节处理

编辑:hfy

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • ARM
    ARM
    +关注

    关注

    134

    文章

    9088

    浏览量

    367405
  • 寄存器
    +关注

    关注

    31

    文章

    5336

    浏览量

    120244
  • cpu
    cpu
    +关注

    关注

    68

    文章

    10855

    浏览量

    211604
  • 鸿蒙系统
    +关注

    关注

    183

    文章

    2634

    浏览量

    66308
收藏 人收藏

    评论

    相关推荐

    鸿蒙内核源码分析:用通俗易懂的语言告诉你鸿蒙内核发生了什么?

    查看。内存在内核的比重极大内存模块占了鸿蒙内核约15%代码量, 近50个文件,非常复杂。鸿蒙源码分析
    发表于 11-19 10:14

    鸿蒙内核源码分析源码注释篇):给HarmonyOS源码逐行加上中文注释

    都懂的概念去诠释或者映射一个他们从没听过的概念.说别人能听得懂的话这很重要!!! 一个没学过计算机知识的卖菜大妈就不可能知道内核的基本运作了吗? NO!,笔者在系列篇中试图用 鸿蒙源码分析
    发表于 11-19 10:32

    鸿蒙内核源码分析:给HarmonyOS源码逐行加上中文注释

    加上中文注解的版本,目前几大核心模块加注已基本完成,正持续加注完善中…为何想给鸿蒙源码加上中文注释源于注者大学时阅读linux 2.6 内核痛苦经历,一直有个心愿,想让更多计算机尤其是
    发表于 11-19 15:06

    鸿蒙源码分析系列(总目录) | 给HarmonyOS源码逐行加上中文注释

    同步更新。鸿蒙源码分析系列篇|- 鸿蒙内核源码分析
    发表于 11-20 11:24

    鸿蒙内核源码分析(必读篇):用故事说内核

    本文基于开源鸿蒙内核分析,官方源码【kernel_liteos_a】官方文档【docs】参考文档【Huawei LiteOS】本文作者:鸿蒙
    发表于 11-23 10:15

    鸿蒙内核源码分析(双循环链表篇) :内核最重要结构体

    鸿蒙源码分析系列文章图解鸿蒙内核, 从 HarmonyOS 架构层视角整理成文, 并首创用生活场景讲故事的方式试图去解构
    发表于 11-24 13:39

    鸿蒙内核源码分析(必读篇)

    本文基于开源鸿蒙内核分析,官方源码【kernel_liteos_a】官方文档【docs】参考文档【Huawei LiteOS】本文作者:鸿蒙
    发表于 11-25 09:28

    HarmonyOS内核源码分析(上)电子书-上线了

    `为方便大家开发鸿蒙系统,小编为大家编辑整理了一本HarmonyOS内核源码分析系列电子书,需要参考学习的朋友快来下吧!本电子书主要介绍如何给鸿蒙
    发表于 11-25 17:13

    LCD电容触摸屏,触摸屏幕时,总是会有异常坐标,这是为何

    LCD电容触摸屏,触摸屏幕时,总是会有异常坐标,这是为何
    发表于 07-02 10:58

    鸿蒙内核源码分析(百篇博客分析.挖透鸿蒙内核)

    入研究实在是暴殄天物,于心不忍。坚信鸿蒙大势所趋,未来可期,其必定成功,也必然成功,誓做其坚定的追随者和传播者。为何要精读内核源码?每位码农的学职生涯,都应精读一遍
    发表于 07-04 17:16

    为何要精读鸿蒙内核源码?

    一个没学过计算机知识的卖菜大妈就不可能知道内核的基本运作了吗? 不一定!在系列篇中试图用 鸿蒙内核源码分析(总目录)之故事篇 去引导这一层级
    的头像 发表于 04-26 15:00 1894次阅读
    <b class='flag-5'>为何</b>要精读<b class='flag-5'>鸿蒙</b><b class='flag-5'>内核</b><b class='flag-5'>源码</b>?

    鸿蒙内核源码分析鸿蒙内核的每段汇编代码解析

    本篇说清楚CPU的工作模式 读本篇之前建议先读鸿蒙内核源码分析(总目录)其他篇. 正如一个互联网项目的后台管理系统有权限管理一样,CPU工作是否也有权限(模式)? 一个成熟的软硬件架构
    的头像 发表于 03-02 09:56 4345次阅读
    <b class='flag-5'>鸿蒙</b><b class='flag-5'>内核</b><b class='flag-5'>源码</b><b class='flag-5'>分析</b>:<b class='flag-5'>鸿蒙</b><b class='flag-5'>内核</b>的每段汇编代码解析

    鸿蒙内核源码分析: 虚拟内存和物理内存是怎么管理的

    有了上篇鸿蒙内核源码分析(内存概念篇)的基础,本篇讲内存管理部分,本章源码超级多,很烧脑,但笔者关键处都加了注释。废话不多说,开始吧。内存一
    发表于 11-23 11:45 19次下载
    <b class='flag-5'>鸿蒙</b><b class='flag-5'>内核</b><b class='flag-5'>源码</b><b class='flag-5'>分析</b>: 虚拟内存和物理内存是怎么管理的

    鸿蒙内核源码分析内核最重要结构体

    为何鸿蒙内核源码分析系列开篇就说 LOS_DL_LIST ? 因为它在鸿蒙 LOS
    发表于 11-24 17:54 35次下载
    <b class='flag-5'>鸿蒙</b><b class='flag-5'>内核</b><b class='flag-5'>源码</b><b class='flag-5'>分析</b> :<b class='flag-5'>内核</b>最重要结构体

    华为鸿蒙系统内核源码分析上册

    鸿蒙內核源码注释中文版【 Gitee仓】给 Harmoηy○S源码逐行加上中文注解,详细阐述设计细节,助你快速精读 Harmonyos内核源码
    发表于 04-09 14:40 17次下载