CPU的异常处理
一、异常入口
当一个异常发生时, CPU 内核能感知异常发生,而且会生成一个目标异常等级(target
exception level)。 CPU 会自动做如下一些事情。
- 把 PSTATE 寄存器的值保存到对应目标异常等级的 SPSR_ELx 中。 把返回地址保存在对应目标异常等级的 ELR_ELx 中。
- 把 PSTATE 寄存器里的 D、 A、 I、 F 标志位都设置为 1,相当于把调试异常、 SError、IRQ 以及 FIQ 都关闭。
- 对于同步异常,要分析异常的原因,并把具体原因写入 ESR_ELx。
- 切换 SP 寄存器为目标异常等级的 SP_Elx 或者 SP_EL0 寄存器。
- 从异常发生现场的异常等级切换到对应目标异常等级,然后跳转到异常向量表里。
上述是 ARMv8 处理器检测到异常发生后自动做的事情。操作系统需要做的事情是从中断向量表开始,根据异常发生的类型,跳转到合适的异常向量表。异常向量表的每个项都会保存一条跳转指令,然后跳转到恰当的异常处理函数并处理异常。
二、 异常返回
当操作系统的异常处理完成后,执行一条 ERET 指令即可从异常返回。这条指令会自动完
成如下工作。
- 从 ELR_ELx 中恢复 PC 指针。
- 从 SPSR_ELx 中恢复 PSTATE 寄存器的状态。中断处理过程是关闭中断的情况下进行的,那中断处理完成后什么时候把中断打开呢?
当中断发生时, CPU 会把 PSTATE 寄存器的值保存到对应目标异常等级的 SPSR_ELx 中,并且把 PSTATE 寄存器里的 D、 A、 I、 F 标志位都设置为 1, 这相当于把本地 CPU 的中断关闭。
当中断处理完成后,操作系统调用 ERET 指令返回中断现场,并且会把 SPSR_ELx 恢复到PSTATE 寄存器中,这相当于把中断打开。
异常触发与返回的流程如图 所示
三、异常返回地址
以下两个寄存器存放了不同的返回地址。
X30 寄存器(又称为 LR), 存放的是子函数的返回地址, 一般是用于完成函数调用的,可以使用 RET 指令来返回父函数。
ELR_ELx,存放的异常返回的地址,即发生异常那一瞬间的地址, 它可能是在用户空
间中的地址,也可能是在内核空间中的地址,不管它在哪个空间,执行 ERET 指令就
可以返回异常现场。
既然 ELR_Elx 保存了异常返回地址, 那么这个返回地址是指向发生异常时的指令还是下一
条指令呢?我们需要区分不同的情况。
对于异步异常(中断),返回地址指向第一条还没执行或由于中断没有成功执行的指令。
对于不是系统调用的同步异常,比如数据异常、访问了没有映射的地址等,返回的是触发
同步异常的那条指令。 例如, 通过 LDR 指令访问一个地址, 这个地址没有建立地址映射。 CPU
访问这个地址时触发了一个数据异常,陷入内核态。在内核态里,操作系统把这个地址映射建
立起来,然后再返回异常现场。此时, CPU 会继续执行这条 LDR 指令。刚才因为地址没有映
射而触发异常,异常处理中修复了这个映射关系,所以 LDR 可以访问这个地址。
系统调用返回的是系统调用指令(例如 SVC 指令)的下一条指令
参考:
ARM的异常处理_arm异常中断处理函数-CSDN博客
【物联网】ARM核异常处理_arm系统 00002023ffebodb8-CSDN博客