ARM异常处理过程
ARM程序在正常执行中,遇到一些特殊情况,需要放下正在执行的工作,去解决异常,然后再返回原来的地方继续工作,这样的一套机制称为ARM异常处理机制。
首先,程序正在正常执行,遇到异常后,不能直接去解决异常,因为此刻程序的一些变量,栈等需要保存,因为等到异常返回后,你要在之前被打断的地方继续要原来的目的进行。在程序去处理异常时,需要保存现场,记录当前的状态。
那ARM怎样去记录当前的状态呢?具体有以下几个方面:
1.将CPSR保存到相应异常模式下的SPSR_<mode>,同时当ARM处于Thumb状态时,要设置T位为ARM状态,禁止中断标志位;
2.将PC保存到相应异常模式下的LR_<mode>中;
3.将状态寄存器CPSR设置为对应异常模式,CPSR最后四个bit位是异常模式选择;
4.将PC寄存器指向对应处理异常处理程序的入口,即PC指向异常向量表对应异常类型的地址。
(2)、未定义的指令(Undef)
异常向量表就是一条条跳转语句,因为只占用四个字节,所以异常处理程序无法存储在异常向量表中,需要跳转到其他地方去执行异常处理程序。
因此,在异常处理之前,首先要完成以上这些操作,不过这些都是硬件自动完成的,所以我们不必操心,但是,在异常处理程序结束后,返回之前的状态这个过程则需要我们人为设置的。
异常结束返回时:
从SPSR_<mode>恢复CPSR,即恢复异常之前的CPU的状态
将LR_<mode>恢复PC,PC指向程序被打断的地方继续进行,但是不同的异常返回,PC的计算有所不同,下文会讲解到。
这些操作都是在ARM状态下执行的。
好了,异常处理机制暂时完成。
下面来讲清楚一些细节问题,上文说到的异常返回时,PC的计算有所不同,那我们来讲一下具体有哪些不同:
首先我们要知道,讲一下完成指令的过程,ARM状态下,在异常产生时内核将lr_<mode>=pc-4,意思就是说,这个步骤是要记录异常返回之后要执行的地址。
但是,lr_<mode>的值是要根据异常的类型进行调整的,如下:
MOV r0,#1 pc-8 正在执行
MOV r1,#2 pc-4 译码 这里保存到lr_<mode>
ADD r0,r0,r1 pc 取指
一条指令(ARM状态下,一条指令四个字节)分三个阶段:取指->译码->执行,取指地址(pc)=正在执行的地址+8
(1)从软件中断SWI和未定义异常返回:
MOV r0,#1 pc-8
MOV r1,#2 pc-4
ADD r0,r0,r1 pc
这两种异常是执行SWI指令或未定义指令发生了异常,即是在执行阶段发生了异常,所以lr保存的是MOV r1,#2这条指令的地址(pc现在指向异常向量表的0x8(异常处理程序的入口)的地址),
所以异常返回后,执行的指令是从MOV r1,#2这里开始,所以lr_<mode>的值直接给pc即可,即:
MOVS pc,lr_<mode>
(2)从FIQ、IRQ和预取指令异常返回:
这两种异常必须要等到当前指令执行完才能去执行异常处理程序,所以执行完当前指令后,预取指令pc和译码指令都已经更新了(+4),如下图:执行MOV r0,#8这条指令时,中断信号来了,
但是要等到这条指令执行完才能去执行异常处理程序,此时异常处理返回时要从MOV r1,#2这条指令开始执行。
MOV r0,#1 pc-8 <-异常信号到来
MOV r1,#2 pc-4
ADD r0,r0,r1 pc
但是执行完MOV r0,#1后,pc已经更新,指向了下一条指令,如下:
MOV r0,#1 pc-12
MOV r1,#2 pc-8
ADD r0,r0,r1 pc-4
MOV r1,r0 pc
若异常返回时,此时pc-4为ADD r0,r0,r1指令,但是程序应该从MOV r1,#2这条指令开始,所以不能直接将lr_<mode>给pc,而是将lr_<mode> - 4 给pc,即:
SUBS pc,lr,#4
(3)预取指令异常
该异常在取指令时发生异常,但是要在执行阶段才响应异常,lr_<mode>为MOV r1,#2 的地址
MOV r0,#1 pc-8 <-异常处理
MOV r1,#2 pc-4
ADD r0,r0,r1 pc <-异常信号到来
此异常可能是取指令时内存没有访问权限或者为空,所以在异常处理程序中将内存内容修改好,在返回时再次执行MOV r0,#1,所以返回时pc应指向MOV r0,#1,即
SUBS pc,lr,#4
(4)从数据访问异常返回:
LDR r0,r3 pc-8 <-异常信号到来
MOV r1,#2 pc-4
ADD r0,r0,r1 pc
此异常是在是在执行LDR r0,r3时访问数据错误导致的异常,返回时要重新执行LDR r0,r3 ,
LDR r0,r3 pc-12
MOV r1,#2 pc-8
ADD r0,r0,r1 pc -4
MOV r1,r0 pc
此时pc已经更新,若要重新执行LDR r0,r3,将pcl指向lr_<mode> - 8 ,即:
SUBS pc, lr, #8
所以总结一下,异常处理程序返回时的地址应根据异常的类型决定
(1)判断异常程序是在指令执行阶段进行还是指令执行完后进行,即pc有没有更新;
(2)异常程序处理完后是否要重新执行指令。