一、 概述
这里主要介绍异常的返回行为(不包括复位异常)。(这里主要参考 armv7m)。
二、异常返回
在异常结束后,通常就会发生异常返回的行为,也就是:
ExceptionReturn(bits(28) EXC_RETURN)
2.1 返回
2.1.1 异常返回的识别
异常返回的时候,需要按照特定的过程才能被识别为异常退出,如下:
返回需要再处于 Handler 模式且使用下列指令之一将 0xFXXXXXXX(EXC_RETURN 值) 加载到 PC:
(1)POP/LDM 指令加载 PC
(2)LDR 指令加载 PC
(3)BX 指令,可以使用任何寄存器
使用这种方式,PC 会拦截这个值。
其中,EXC_RETURN 值定义如下(其数据与压栈时候的 LR 相关):
bit[31:28]:0xF,标识这个值是 EXC_RETURN
bit[27:5]:只能置1,否则不可预测
bit[4]:如果没实现 FP 扩展,置1,否则不可预测;如果实现了 FP 扩展,定义此异常的堆栈帧是否为FP状态信息分配了空间。如果分配了堆栈空间,位[4]为0(这是因为异常进入的时候会将CONTROL.FPCA 值写入这个位,反过来,返回的时候会将位写回寄存器)
Bit[3:0] 定义如下:
(该图片来着 ARM:《DDI0403E_d_armv7m_arm.pdf》)
(该图片来着 ARM:《DDI0403E_d_armv7m_arm.pdf》)
注意:EXC_RETURN 只能在 Handler 模式下才能加载到PC,在 Thread模式下加载到 PC 只会认为是地址,而不是特殊值,加载该值会导致MemManage异常,或INVSTATE UsageFault异常,或将异常升级为HardFault
2.1.2 异常返回
异常返回的时候有一个完整性检查:
(1)返回的异常号在返回开始时保存在IPSR中,在SCB中被列为活动。
(2)通常,除了返回异常之外,如果至少有一个异常是活动的,则返回必须为Handler模式。这将检查异常返回数是否不匹配。软件可以使用CCR.NONBASETHRDENA来禁用此检查
(3)返回到Thread模式时,恢复到IPSR Exception number字段的值必须为0。
(4)返回到Handler模式时,恢复到IPSR Exception number字段的值不能为0。
(5)EXC_RETURN[3:0]不能是保留值,也就是只能是规定的值之一
任何失败的检查都会导致INVPC UsageFault,并在LR中显示EXC_RETURN值。
如果检查是无无错的,那么就会按照 EXC_RETURN 值进行操作,从而知道返回的模式,SP 是 MSP 还是 PSP,是否有 FP 扩展。然后会对返回的异常进行失活,出栈
2.2 出栈
出栈与入栈相对应。
(1)检查是否有 FP 扩展且 EXC_RETURN(4) 是否等于 1(EXC_RETURN 值与 入栈的 LR 对应),从而知道栈里面是 104 字节还是 32 字节需要出栈,是否 8 字节对齐
(2)按照入栈时候的数据进行出栈
2.2.1 栈对齐
当CCR-STKALIGN设置为1时异常返回时,处理器使用从堆栈中弹出的PSR值的第[9]位的值来确定是否必须调整堆栈指针对齐。这与在异常进入时执行的任何强制堆栈对齐相反。
在异常返回时,处理器通过将帧大小添加到SP并使用(4*xPSR[9])来调整SP,其中xPSR[9]是来自堆叠的xPSR值的位值。这将恢复原来的SP对齐。如果异常退出序列以4字节对齐的堆栈指针开始,则此调整没有影响
如果异常退出导致派生异常,则处理器进入派生异常时使用在启动异常退出序列之前使用的堆栈对齐方式。
三、参考文档
ARM:《DDI0403E_d_armv7m_arm.pdf》