微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > ARM Linux 中断向量表建立流程

ARM Linux 中断向量表建立流程

时间:11-09 来源:互联网 点击:

三.ARM Linux中断处理过程分析(2)
续前文,让我们先分析进入IRQ之前的处理器模式为SVC时的情况,程序会跳转到__irq_svc继续运行,其相应代码如下:
20__irq_svc: sub sp, sp, #S_FRAME_SIZE
21 stmia sp, {r0 - r12} @ save r0 - r12
22 ldr r7, .LCirq
23 add r5, sp, #S_FRAME_SIZE
24 ldmia r7, {r7 - r9}
25 add r4, sp, #S_SP
26 mov r6, lr
27 stmia r4, {r5, r6, r7, r8, r9} @ save sp_SVC, lr_SVC, pc, cpsr, old_ro
28 1: get_irqnr_and_base r0, r6, r5, lr
29 movne r1, sp
30 @
31 @ routine called with r0 = irq number, r1 = struct pt_regs *
32 @
33 adrsvc ne, lr, 1b
34 bne asm_do_IRQ
35 ldr r0, [sp, #S_PSR] @ irqs are already disabled
36 msr spsr, r0
37 ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
行20~27:保存进入中断之前的寄存器,把它们放在堆栈中。其中#S_FRAME_SIZE和#S_SP的定义在arch/arm/kernel/entry-header.S中:
#ifdef CONFIG_CPU_32
#define S_FRAME_SIZE 72
#define S_OLD_R0 68
#define S_PSR 64
#else
#define S_FRAME_SIZE 68
#define S_OLD_R0 64
#define S_PSR 60
#endif
#define S_PC 60
#define S_LR 56
#define S_SP 52
#define S_IP 48
#define S_FP 44
#define S_R10 40
#define S_R9 36
#define S_R8 32
#define S_R7 28
#define S_R6 24
#define S_R5 20
#define S_R4 16
#define S_R3 12
#define S_R2 8
#define S_R1 4
#define S_R0 0
#define S_OFF 8
.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,它是一个宏定义,作用是获取中断号(irq number),它将被保存在r0中。另外,get_irqnr_and_base还会改变cpsr寄存器中的Z位,如果确实找到了发生的中断号,则Z位被清除,否则Z位被置位。get_irqnr_and_base这个宏定义的实现是依赖具体的硬件的,对于pxa270 cpu,其实现如下:
.macro get_irqnr_and_base, irqnr, irqstat, base, tmp
mov base, #io_p2v(0x40000000) @ IIR Ctl = 0x40d00000
add base, base, #0x00d00000
ldr irqstat, [base, #0] @ ICIP
ldr irqnr, [base, #4] @ ICMR
ands irqstat, irqstat, irqnr
beq 1001f /* 没找到中断,跳转*/
rsb irqnr, irqstat, #0
and irqstat, irqstat, irqnr
clz irqnr, irqstat
rsb irqnr, irqnr, #(31 - PXA_IRQ_SKIP)
#ifdef CONFIG_CPU_BULVERDE
b 1002f
#endif
1001:
1002:
.endm
.macro irq_prio_table
.endm
bics irqstat, irqstat, irqnr 对照intmsk将intpnd中禁止的中断清0。因为intpnd在某一时刻只可以有一位为1,所以有一位被bics清0了,就会影响标志位从而beq跳转,return r0=0;从1001:开始所作的事情是循环查intpnd哪一位置为了1。有点疑惑的是tst 指令:
tst 类似于 CMP,不产生放置到目的寄存器中的结果。而是在给出的两个操作数上进行操作并把结果反映到状态标志上。使用 tst 来检查是否设置了特定的位。操作数 1 是要测试的数据字而操作数 2 是一个位掩码。经过测试后,如果匹配则设置 Zero 标志,否则清除它。
那么这里的tst irqstat, #1,当zero置1了表示有中断位,为什么下面是bne 1002f而不是beq?请教请教。。。。。。。)
asm_do_IRQ是用C语言编码的函数,它在arch/arm/kernel/irq.c中被定义,其原型为:
asmlinkage void asm_do_IRQ(int irq, struct pt_regs *regs);
这里牵扯到一个问题就是,在汇编中如何调用C语言的函数,参数是如何传递的?为了让ARM的汇编代码可与C代码一起连接,在编写ARM汇编时,应遵循一套标准,这就是ATPCS(The ARM-Thumb Procedure Call Standard)。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:恢复寄存器,返回到发生中断之前的代码中继续执行。
这就是整个ARM linux中断处理的过程。以后有时间,再继续展开asm_do_IRQ继续分析。对于进入中断前处理器模式是USR的中断处理过程(__irq_usr),这里就不再做分析,这与__irq_svc基本相同
asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
{
struct irqdesc * desc;
struct irqaction * action;
int cpu;
irq = fixup_irq(irq);// 查找子中断号,如无子中断return 原irq
/*
* Some hardware gives randomly wrong

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top