- 论坛徽章:
- 0
|
大家好,我有一个关于中断返回地址的问题:
对于ARM处理器,当发生中断的时候,硬件会进行一系列的动作,具体如下:
1、修改CPSR(Current Program Status Register)寄存器中的M[4:0]。M[4:0]表示了ARM处理器当前处于的模式( processor modes)。对于中断,应该对应IRQ mode
2、保存发生中断那一点的CPSR值(step 1之前的状态,用SPSR_irq保存)和PC值(保存在lr_irq寄存器中)。对于thumb state,lr_irq = PC,对于ARM state,lr_irq = PC - 4
3、mask IRQ exception。也就是设定CPSR.I = 1
4、设定PC值为IRQ exception vector。
HW在保存PC值的时候为何要减去4?我的理解是这样的(不一定对)。由于ARM采用流水线结构,当CPU正在执行某一条指令的时候,其实取指的动作早就执行了,这时候PC值=正在执行的指令地址 + 8。一旦发生了中断,当前正在执行的指令当然要执行完毕,但是已经完成取指、译码的指令则终止执行,但无论如何,PC值其实是比正在执行的指令超前的。减去4只是让返回地址设定为发生中断那条指令的下一条指令。
硬件动作之后,轮到软件上场了(代码位于linux/arch/arm/kernel/entry-armv.S,我看的是3.14内核):
异常向量表如下:
__vectors_start:
W(b) vector_rst
W(b) vector_und
W(ldr) pc, __vectors_start + 0x1000
W(b) vector_pabt
W(b) vector_dabt
W(b) vector_addrexcptn
W(b) vector_irq
W(b) vector_fiq
对于中断,代码会跳转到vector_irq,具体的代码如下:
/*
* Interrupt dispatcher
*/
vector_stub irq, IRQ_MODE, 4----这里的参数是4,为何要减去4?????
.long __irq_usr @ 0 (USR_26 / USR_32)
.long __irq_invalid @ 1 (FIQ_26 / FIQ_32)
.long __irq_invalid @ 2 (IRQ_26 / IRQ_32)
.long __irq_svc @ 3 (SVC_26 / SVC_32)
.long __irq_invalid @ 4
.long __irq_invalid @ 5
.long __irq_invalid @ 6
.long __irq_invalid @ 7
.long __irq_invalid @ 8
.long __irq_invalid @ 9
.long __irq_invalid @ a
.long __irq_invalid @ b
.long __irq_invalid @ c
.long __irq_invalid @ d
.long __irq_invalid @ e
.long __irq_invalid @ f
vector_stub是一个宏定义,展开后就是vector_irq符号。vector_stub的代码如下:
.macro vector_stub, name, mode, correction=0
.align 5
vector_\name:
.if \correction
sub lr, lr, #\correction----------(A)
.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, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
msr spsr_cxsf, r0
@
@ the branch table must immediately follow this code
@
and lr, lr, #0x0f
THUMB( adr r0, 1f )
THUMB( ldr lr, [r0, lr, lsl #2] )
mov r0, sp
ARM( ldr lr, [pc, lr, lsl #2] )
movs pc, lr @ branch to handler in SVC mode
ENDPROC(vector_\name)
问题来了,上面程序中的(A)又一次调整了保存的PC值,又一次减去了4,如果这里减去4,那么这时候lr中保存的就是发生中断点的PC值了,但是,对于中断,我们期望返回的不是发生中断那一点的指令,我猜想调整到中断向量之前,发生中断那一点的指令已经被执行完毕,因此返回现场的时候应该回到下一条指令(也就是PC+4)就ok了,我到底哪个地方搞错了呢? |
|