arm异常处理函数的设计
1 异常发生时处理器的动作
当任何一个异常发生并得到响应时,ARM 内核自动完成以下动作:
拷贝 CPSR 到 SPSR_
设置适当的 CPSR 位:
改变处理器状态进入 ARM 状态
改变处理器模式进入相应的异常模式
设置中断禁止位禁止相应中断
更新 LR_
设置 PC 到相应的异常向量
注意当响应异常后,不管异常发生在ARM 还是Thumb 状态下,处理器都将
自动进入ARM 状态。另一个需要注意的地方是中断使能被自动关闭,也就是说
缺省情况下中断是不可重入的。
除这些自动完成的动作之外,如果在汇编级进行手动编程,还需要注意保存
必要的通用寄存器。
2 进入异常处理循环后软件的任务
进入异常处理程序以后,用户可以完全按照自己的意愿来进行程序设计,包
括调用Thumb 状态的函数,等等。但是对于绝大多数的系统来说,有一个步骤
必须处理,就是要把中断控制器中对应的中断状态标识清掉,表明该中断请求已
经得到响应。否则等退出中断函数以后,又马上会被再一次触发,从而进入周而
复始的死循环。
3 异常的返回
当一个异常处理返回时,一共有3 件事情需要处理:
通用寄存器的恢复
状态寄存器的恢复
PC 指针的恢复
通用寄存器的恢复采用一般的堆栈操作指令,而PC 和CPSR 的恢复可以通
过一条指令来实现,下面是3 个例子:
MOVS pc, lr 或 SUBS pc, lr, #4 或LDMFD sp!, {pc}^
这几条指令都是普通的数据处理指令,特殊之处就是把PC 寄存器作为了目
标寄存器,并且带了特殊的后缀“S”或“^”,在特权模式下,“S”或“^”的作
用就是使指令在执行时,同时完成从SPSR 到CPSR 的拷贝,达到恢复状态寄存器的目的。
我们知道在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。
由此我们来对不同异常类型的返回地址进行依次比较:
假设在指令B 处(地址0x8004)发生了中断响应,进入中断响应后LR 上经
过调整保存的地址值应该是C 的地址0x8008。
(a) 如果发生的是软件中断,即B 是“SWI”指令
从SWI 中断返回后下一条执行指令就是C,正好是LR 寄存器保存的地址,
所以只要直接把LR 恢复给PC。
(b) 如果发生的是“IRQ”或“FIQ”等指令
因为外部中断请求中断了B 指令的执行,当中断返回后,需要重新回到B
指令的执行,也就是返回地址应该是B(0x8004),需要把LR 减4。
(c) 如果发生的是“Data Abort”
在B上进入数据异常的响应,但导致数据异常的原因却应该是上一条指令A。
当中断处理程序修复数据异常以后,要回到A 上重新执行导致数据异常的指令,
因此返回地址应该是LR 减8。
如果原来的指令执行状态是Thumb,异常返回地址的分析与此类似,对LR
的调整正好与ARM 状态完全一致。
4 ARM 编译器对异常处理函数编写的扩展
考虑到异常处理函数在现场保护和返回地址的处理上与普通函数的不同之
处,不能直接把普通函数体连接到异常向量表上,需要在上面加一层封装,下面
是一个例子:
IRQ_Handler ;中断响应,从向量表直接跳来
STMFD SP!, {R0-R12, LR} ;保护现场,一般只需保护{r0-r3,lr}即可
BL IrqHandler ;进入普通处理函数,C 或汇编均可
LDMFD SP!, {R0-R12, LR} ;恢复现场
SUBS PC, LR, #4 ;中断返回,注意返回地址
为了方便使用高级语言直接编写异常处理函数,ARM 编译器对此作了特定
的扩展,可以使用函数声明关键字__irq,这样编译出来的函数就满足异常响应
对现场保护和恢复的需要,并且自动加入对LR 进行减4 的处理,符合IRQ 和
FIQ 中断处理的要求。
__irq void IRQ_Handler (void)
{…}
评论