ARM Linux中断处理过程分析.docx
- 文档编号:10252741
- 上传时间:2023-02-09
- 格式:DOCX
- 页数:9
- 大小:21.26KB
ARM Linux中断处理过程分析.docx
《ARM Linux中断处理过程分析.docx》由会员分享,可在线阅读,更多相关《ARM Linux中断处理过程分析.docx(9页珍藏版)》请在冰豆网上搜索。
ARMLinux中断处理过程分析
ARMLinux中断处理过程分析
(1)[原创2007-06-0921:
43:
26]发表者:
jimmy_lee
Author:
jimmy.li
Date:
2007-06-09
在我的上一篇文章(ARMlinux的中断向量表初始化分析)中已经分析了ARMLinux中断向量表是如何建立的,在这篇文章中,我将分析一下Linux内核的ARM体系下,中断处理是如何响应的一个过程。
在ARM体系架构下,定义了7种异常,每一种异常都有自己的入口地址,即异常向量表,当异常发生时,处理器会自动跳转到相应的入口处执行。
对于ARMv4及其以上的版本,异常向量表的起始位置由协处理器15(cp15)的控制寄存器(c1)里的V位(bit13)有关,当V=0时,异常向量表的起始位置在0x00000000,而当V=1时,异常向量表就起始于0xffff0000位置。
在上一篇文章中,我们已经分析知道异常向量表放置于0xffff0000起始位置,而IRQ中断处理入口地址为:
0xffff0018,所以当发生一IRQ中断异常时,处理器会自动跳转到0xffff0018这个虚拟地址上。
0xffff0018这个虚拟地址上是一条跳转指令:
b__real_stubs_start+(vector_IRQ-__stubs_start)
所以对于IRQ的处理就是从vector_IRQ标号处开始的。
在linux2.4.19内核中相应代码如下:
1vector_IRQ:
@
2@savemodespecificregisters
3@
4ldrr13,.LCsirq
5sublr,lr,#4
6strlr,[r13]@savelr_IRQ
7mrslr,spsr
8strlr,[r13,#4]@savespsr_IRQ
9@
10@nowbranchtothereleventMODEhandlingroutine
11@
12mrsr13,cpsr
13bicr13,r13,#MODE_MASK
14orrr13,r13,#I_BIT|MODE_SVC
15msrspsr_c,r13@switchtoSVC_32mode
16
17andlr,lr,#15
18ldrlr,[pc,lr,lsl#2]
19movspc,lr@Changesmodeandbranches
20
21.LCtab_irq:
.word__irq_usr@0(USR_26/USR_32)
22.word__irq_invalid@1(FIQ_26/FIQ_32)
23.word__irq_invalid@2(IRQ_26/IRQ_32)
24.word__irq_svc@3(SVC_26/SVC_32)
25.word__irq_invalid@4
26.word__irq_invalid@5
27.word__irq_invalid@6
28.word__irq_invalid@7
29.word__irq_invalid@8
30.word__irq_invalid@9
31.word__irq_invalid@a
32.word__irq_invalid@b
33.word__irq_invalid@c
34.word__irq_invalid@d
35.word__irq_invalid@e
36.word__irq_invalid@f
首先,行4~8是保存进入IRQ模式之前的pc指针(在lr_IRQ)和CPSR(在SPSR_IRQ)到.LCsirq所指向的地址中。
.LCsirq相关代码也是位于entry-armv.S中:
.LCsirq:
.word__temp_irq
…
__temp_irq:
.word0@savedlr_irq
.word0@savedspsr_irq
.word-1@old_r0
在这里补充一下ARM对于异常的处理过程,可以用下面的一段伪码来表示:
r14_<异常模式>=returnlink
SPSR_<异常模式>=CPSR
CPSR[4:
0]=异常模式编码
CPSR[5]=0;运行于ARM状态
If<异常模式>==ResetorFIQthen{
;当复位或响应FIQ异常时,禁止新的fiq和irq异常
CPSR[6]=1;
CPSR[7]=1;
}elseif<异常模式>==IRQthen{
;当响应IRQ异常时,禁止新的IRQ异常
CPSR[7]=1;
}
PC=异常向量地址
所以在运行到行4~8之前时,lr为进入IRQ之前的pc指针,spsr为进入IRQ之前的cpsr指针。
接着,行12~15更新spsr寄存器为SVR模式,并关闭IRQ,为从IRQ模式切换到SVR模式做准备。
行17,根据进入IRQ模式之前的psr(因为在行7,lr已经被置以spsr_irq),获取之前的处理器模式(psr&0b1111)。
行18,根据获取的进入IRQ之前的处理器模式,查找相应的跳转入口(__irq_usr对应于之前是USR模式,__irq_svc对于之前是SVC模式,对于其它模式均跳转到__irq_invalid,在linux系统中处理器进入IRQ之前只有usr和svc两种模式,其它模式均不允许开启IRQ)。
此行实际上是:
lr=pc+lr<<2,pc指向当前指令地址值加8个字节的地址,即pc指向当前指令的下两条指令的地址,所以pc在此时指向的是.LCtab_irq地址。
行19,跳转到相应入口,并且ARM寄存器r13和r14则切换到了SVC模式下的寄存器。
ARMLinux中断处理过程分析
(2)[原创2007-06-1020:
51:
14]发表者:
jimmy_lee
Author:
jimmy.li
Date:
2007-06-10
--------------------------
续前文,让我们先分析进入IRQ之前的处理器模式为SVC时的情况,程序会跳转到__irq_svc继续运行,其相应代码如下:
20__irq_svc:
subsp,sp,#S_FRAME_SIZE
21stmiasp,{r0-r12}@saver0-r12
22ldrr7,.LCirq
23addr5,sp,#S_FRAME_SIZE
24ldmiar7,{r7-r9}
25addr4,sp,#S_SP
26movr6,lr
27stmiar4,{r5,r6,r7,r8,r9}@savesp_SVC,lr_SVC,pc,cpsr,old_ro
281:
get_irqnr_and_baser0,r6,r5,lr
29movner1,sp
30@
31@routinecalledwithr0=irqnumber,r1=structpt_regs*
32@
33adrsvcne,lr,1b
34bneasm_do_IRQ
35ldrr0,[sp,#S_PSR]@irqsarealreadydisabled
36msrspsr,r0
37ldmiasp,{r0-pc}^@loadr0-pc,cpsr
行20~27:
保存进入中断之前的寄存器,把它们放在堆栈中。
其中#S_FRAME_SIZE和#S_SP的定义在arch/arm/kernel/entry-header.S中:
#ifdefCONFIG_CPU_32
#defineS_FRAME_SIZE72
#defineS_OLD_R068
#defineS_PSR64
#else
#defineS_FRAME_SIZE68
#defineS_OLD_R064
#defineS_PSR60
#endif
#defineS_PC60
#defineS_LR56
#defineS_SP52
#defineS_IP48
#defineS_FP44
#defineS_R1040
#defineS_R936
#defineS_R832
#defineS_R728
#defineS_R624
#defineS_R520
#defineS_R416
#defineS_R312
#defineS_R28
#defineS_R14
#defineS_R00
#defineS_OFF8
.LCirq在entry-armv.S中是这样定义的:
.LCirq:
.word__temp_irq
这与行4处的.LCsirq定义是一样的,可见整个过程利用__temp_irq作为中转,把进入中断之前的CPSR和PC(中断处理结束后要返回的地址)放入堆栈,以便中断返回时直接恢复。
行20~27执行的结果是:
r5->
old_r0
cpsr
pc
lr_svc
r4->
sp_svc
r12
r11
…
r1
sp->
r0
行28的get_irqnr_and_base,它是一个宏定义,作用是获取中断号(irqnumber),它将被保存在r0中。
另外,get_irqnr_and_base还会改变cpsr寄存器中的Z位,如果确实找到了发生的中断号,则Z位被清除,否则Z位被置位。
get_irqnr_and_base这个宏定义的实现是依赖具体的硬件的,对于pxa270cpu,其实现如下:
.macroget_irqnr_and_base,irqnr,irqstat,base,tmp
mov\base,#io_p2v(0x40000000)@IIRCtl=0x40d00000
add\base,\base,#0x00d00000
ldr\irqstat,[\base,#0]@ICIP
ldr\irqnr,[\base,#4]@ICMR
ands\irqstat,\irqstat,\irqnr
beq1001f/*没找到中断,跳转*/
rsb\irqnr,\irqstat,#0
and\irqstat,\irqstat,\irqnr
clz\irqnr,\irqstat
rsb\irqnr,\irqnr,#(31-PXA_IRQ_SKIP)
#ifdefCONFIG_CPU_BULVERDE
b1002f
#endif
1001:
#ifdefCONFIG_CPU_BULVERDE
add\base,\base,#0x9c
ldr\irqstat,[\base,#0]@ICIP2
ldr\irqnr,[\base,#4]@ICMR2
ands\irqstat,\irqstat,\irqnr
beq1002f
rsb\irqnr,\irqstat,#0
and\irqstat,\irqstat,\irqnr
clz\irqnr,\irqstat
rsb\irqnr,\irqnr,#31
add\irqnr,\irqnr,#(32-PXA_IRQ_SKIP)
1002:
#endif
.endm
关于这段get_irqnr_and_base的实现代码比较简单,这里不作解释。
行29~34:
如果在行28找到中断号,Z=0,则这些指令的条件都满足,则执行把sp指针赋予r1;标号1(行28)的相对地址赋予lr,以便处理完该中断后,再查询是否还有未处理的中断号;跳转到asm_do_IRQ处理相应中断。
asm_do_IRQ是用C语言编码的函数,它在arch/arm/kernel/irq.c中被定义,其原型为:
asmlinkagevoidasm_do_IRQ(intirq,structpt_regs*regs);
这里牵扯到一个问题就是,在汇编中如何调用C语言的函数,参数是如何传递的?
为了让ARM的汇编代码可与C代码一起连接,在编写ARM汇编时,应遵循一套标准,这就是ATPCS(TheARM-ThumbProcedureCallStandard)。
ATPCS定义{r0~r3}为参数传递和结果返回寄存器;若参数超过4个字型(32bit),则使用堆栈进行传递;头4个参数依次存于r0...r3,大于4个的后续字型参数通过栈传送。
关于栈的使用,是使用满递减的堆栈标准,也就是栈是从高地址向低地址方向增长的(递减堆栈),栈指针寄存器指向的数据是最后压入堆栈内的有效数据(满堆栈)。
所以在跳转到asm_do_IRQ函数之前,r0就必须设置为中断号(行28get_irqnr_and_base把中断号放置于r0),r1就必须是指向pt_regs这样结构(定义于include/asm-arm/proc-armv/ptrace.h)的指针,而行29把sp指针赋予r1,就完成了这样的一个调用准备。
行35~37:
恢复寄存器,返回到发生中断之前的代码中继续执行。
这就是整个ARMlinux中断处理的过程。
以后有时间,再继续展开asm_do_IRQ继续分析。
对于进入中断前处理器模式是USR的中断处理过程(__irq_usr),这里就不再做分析,这与__irq_svc基本相同,或许有时间以后我会再写出来的
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- ARM Linux中断处理过程分析 Linux 中断 处理 过程 分析