linux运行模式

据说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的值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值