ARM处理器异常处理的返回地址分析
作者:于连庆, 华清远见嵌入式学院 讲师。
ARM 处理器一共有7 种类型的异常,按优先级从高到低排列如下:
Reset
Data Abort
FIQ
IRQ
Prefetch Abort
SWI
Undefined instruction
当任何一个异常发生并得到响应时,ARM内核自动完成以下动作:
1、拷贝CPSR到SPSR_<mode>
2、设置适当的CPSR位:
改变处理器状态进入ARM状态;
改变处理器模式进入相应的异常模式;
设置中断禁止位禁止相应中断。
3、更新LR_<mode>
4、设置PC到相应的异常向量
当一个异常处理返回时,一共有3 件事情需要处理:通用寄存器的恢复、状态寄存器的恢复以及PC 指针的恢复。
通用寄存器的恢复采用一般的堆栈操作指令,而PC和CPSR的恢复可以通过一条指令来实现,下面是3 个例子:
MOVS pc, lr 或 SUBS pc, lr, #4 或LDMFD sp!, {pc}^
这几条指令都是普通的数据处理指令,特殊之处就是把PC寄存器作为了目标寄存器,并且带了特殊的后缀“S”或“^”,在特权模式下,“S”或“^”的作用就是使指令在执行时,同时完成从 SPSR 到CPSR 的拷贝,达到恢复状态寄存器的目的。
异常返回时另一个非常重要的问题是返回地址的确定,在前面曾提到进入异常时处理器会有一个保存LR 的动作,但是该保存值并不一定是正确的返回地址,下面以一个简单的指令执行流水状态图来对此加以说明。
我们知道在ARM 架构里,PC值指向当前执行指令的地址加8处,也就是说, 当执行指令A(地址0x8000)时,PC 等于指令C 的地址(0x8008)。假如指令A 是“BL”指令,则当执行该指令时,会把PC(=0x8008)保存到LR 寄存器里面,但是接下去处理器会马上对LR 进行一个自动的调整动作:LR=LR-0x4。这样,最终保存在 LR 里面的是 B 指令的地址,所以当从 BL 返回时,LR 里面正好是正确的返回地址。同样的调整机制在所有LR自动保存操作中都存在,比如进入中断响应时,处理器所做的LR 保存中,也进行了一次自动调整,并且调整动作都是LR=LR-0x4。
下面,我们对不同类型的异常的返回地址依次进行说明:
假设在指令A 处(地址0x8000)发生了异常,进入异常响应后,LR 上经过调整保存的地址值应该是B 的地址0x8004。
1、 如果发生的是软件中断,即A 是“SWI”指令
异常是由指令本身引起的,从 SWI 中断返回后下一条执行指令就是B,正好是LR 寄存器保存的地址, 所以只要直接把LR 恢复给PC。
MOVS pc, lr
2、 发生的是Undefined instruction异常
异常是由指令本身引起的,从异常返回后下一条执行指令就是B,正好是LR 寄存器保存的地址, 所以只要直接把LR 恢复给PC。
MOVS pc, lr
3、 发生的是IRQ或FIQ中断
因为指令不可能被中断打断,所以A指令执行完以后才能响应中断,此时PC已更新,指向指令D的地址(地址0x800C),LR 上经过调整保存的地址值是C 的地址0x8008。中断返回后应该执行B指令,所以返回操作是:
SUBS pc, lr, #4
4、 发生的是Prefetch Abort异常
该异常并不是处理器试图从一个非法地址取指令时触发,取出的指令只是被标记为非法,按正常处理流程放在流水线上,在执行阶段触发Prefetch Abort异常,此时LR 上经过调整保存的地址值是B 的地址0x8004。异常返回应该返回到A指令,尝试重新取指令,所以返回操作是:
SUBS pc, lr, #4
5、 发生的是“Data Abort”
CPU访问存储器时触发该异常,此时PC指向指令D的地址(地址0x800C),LR 上经过调整保存的地址值是C 的地址0x8008。异常返回后,应回到指令A,尝试重新操作存储器,所以返回操作是:
SUBS pc, lr, #8