据说linux kernel只用到了arm的usr和svc两种模式;用户态位于usr,内核态位于svc。如果发生中断呢?难道不是处于irq模式?在中断异常的用户入口__irq_usr处加了打印,发现此时的mode是3,不是应该处于irq mode吗?怎么是svc呢?怎么切过去的?这还要从中断异常的入口说起。还记得中断向量表吗?
/*
* Interrupt dispatcher
*/
vector_stub irq, IRQ_MODE, 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这是一个宏,是个很关键的宏。
.macro vector_stub, name, mode, correction=0
.align 5
vector_\name:
.if \correction@修正返回地址,为什么要修正?进入异常模式时lr中保存的是当前PC,异常返回后,需要执行的是当前状态的下一条指令,根据arm spec需要-4或者-8
sub lr, lr, #\correction
.endif
@
@ Save r0, lr_<exception> (parent PC) and spsr_<exception>
@ (parent CPSR)
@
stmia sp, {r0, lr} @ save r0, lr,到sp栈顶指针指定的内存
mrs lr, spsr @spsr读到lr,这是异常之前的状态,spsr_irq
str lr, [sp, #8] @ save spsr到sp+8的内存中
@
@ Prepare for SVC32 mode. IRQs remain disabled.
@
mrs r0, cpsr @cpsr读到r0,这是异常之后的
eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)@改mode了,异或特点:异或0不变,异或1取反;之前是0的bit都会置1,是1的bit不变。
#define PSR_ISETSTATE PSR_T_BIT //thumb
#define PSR_ISETSTATE 0 //arm
msr spsr_cxsf, r0 @r0保存到spsr_cxsf
格式:
MSR{<条件码>CPSR_f|SPSR_f,<#ommed_8r>
MSR{<条件码>CPSR_<field>|SPSR_<field>,Rm
其中:<field>字段可以是以下之一或多种:(位从右到左)
C:控制域屏蔽字段(PSR中的第0位到第7位);
X:扩展域屏蔽字段(PSR中的第8位到第15位);
S:状态域屏蔽字段(PSR中的第16位到第32位);
F:标志域屏蔽字段(PSR中的第24位到第31位)。
注意了这里只是改了spsr_irq的mode,还没有开始切换呢。
@
@ the branch table must immediately follow this code
@
and lr, lr, #0x0f @lr保存的是spsr,低4bit,是mode
THUMB( adr r0, 1f )
THUMB( ldr lr, [r0, lr, lsl #2] )
mov r0, sp @将irq的栈顶地址给r0,就可以通过r0读irq的栈了
ARM( ldr lr, [pc, lr, lsl #2] )@pc = lr<<2;lr = pc+lr,有左移还是因为long对齐,__irq_usr 位于0偏移处,__irq_svc位于3*4偏移处。
movs pc, lr @ branch to handler in SVC mode跳转的同时,做模式切换,s表示同时将spsr赋给cpsr,这里就切换过来了;切换到svc mode,spsr_svc应该保存的是切换之前的cpsr,应该是irq mode为2啊;可为什么读回来是0呢?
在异常发生后,ARM内核会自动做以下工作,确实是由CPU自动处理的:
1 保存执行状态:将CPSR复制到发生的异常模式下SPSR中;
2 模式切换:将CPSR模式位强制设置为与异常类型相对应的值,同时处理器进入到ARM执行模式,禁止所有IRQ中断,当进入FIQ快速中断模式时禁止FIQ中断;
3 保存返回地址:将下一条指令的地址(被打断程序)保存在LR(异常模式下LR_excep)中。
4 跳入异常向量表:强制设置PC的值为相应异常向量地址,跳转到异常处理程序中。
如果直接模式切换了,cpsr就不会copy了?所以spsr_svc中保存的还是上一次异常user to irq时的user mode?
ENDPROC(vector_\name)
.align 2
@ handler addresses follow this label
1:
.endm
为什么要切换svc呢?应该是中断嵌套的要求,中断嵌套可能导致链接寄存器r14_irq被破坏,就找不到返回点了。
1 arm体系下pt_regs结构struct pt_regs {long uregs[18];};uregs[0] - uregs[17]分别对应,r0 - r15,cpsr,ORIG_r0
#define ARM_cpsr uregs[16]
#define ARM_pc uregs[15]
#define ARM_lr uregs[14]
#define ARM_sp uregs[13]
#define ARM_ip uregs[12]
#define ARM_fp uregs[11]
#define ARM_r10 uregs[10]
#define ARM_r9 uregs[9]
#define ARM_r8 uregs[8]
#define ARM_r7 uregs[7]
#define ARM_r6 uregs[6]
#define ARM_r5 uregs[5]
#define ARM_r4 uregs[4]
#define ARM_r3 uregs[3]
#define ARM_r2 uregs[2]
#define ARM_r1 uregs[1]
#define ARM_r0 uregs[0]
#define ARM_ORIG_r0 uregs[17]
2 irq中断时堆栈的变化
--------
spsr
--------
lr ,中断返回地址,修正后的
--------
r0
-------- <-进入irq_svc或irq_usr之前,sp的值,也是r0的值
pt_regs
--------- <-进入svc_entry或usr_entry后,sp的值