arm异发生时,硬件会自动保存pc到异常模式下的lr,以及保存cpsr到spsr,把cpsr设置为相应异常模式,把pc设置为相应异常处理程序的入口。这个问题就来了,如果发生嵌套中断,那么会覆盖之前中断的lr,无法返回。
为了避免这个问题,在执行ISR之前,会进行模式切换,切换到SVC货系统模式。在相应的模式下构造一个帧,就像一般的函数调用栈帧,保存现场,保存lr,sp等等。接着调用ISR,如果发生中断就继续构造栈帧。这样返回的时候就会像函数调用那样一层一层返回。。
分析arm-linux里面的中断,也是用的这个方法。
vector_irq:
.if 4
sub lr, lr, #4
.endif
@
@ Save r0, lr_<exception> (parent PC) and spsr_<exception>
@ (parent CPSR)
@
stmia sp, {r0, lr} @ save r0, lr
mrs lr, spsr
str lr, [sp, #8] @ save spsr
@
@ Prepare for SVC32 mode. IRQs remain disabled.
@
mrs r0, cpsr
eor r0, r0, #(IRQ_MODE ^ SVC_MODE | PSR_ISETSTATE)
msr spsr_cxsf, r0
@
@ the branch table must immediately follow this code
@
// lr中当前存储了进入异常处理程序之前的状态寄存器的值,宏定义的前面部
// 分有从spsr取值到lr的代码,对后几位做与,即是获取在中断前处理器所
// 处的状态,这个值在后面会被用作跳转表的索引。
and lr, lr, #0x0f
// 用做他用,sp值当第一个参数传给后面函数
mov r0, sp
// pc是当前执行指令地址加8,即跳转表的基地址,lr是索引,很好的技巧,
// 取pc获取当前指令地址什么时候都没错
ARM( ldr lr, [pc, lr, lsl #2] )
movs pc, lr @ branch to handler in SVC mode
ENDPROC(vector_irq)
后面会跳入到__irq_svc entry_armv.S里面看。
开始就会调用svc_entry,里面会构造栈帧。最后调用svc_exit恢复。