分类: LINUX
2008-02-29 16:33:02
浅析armlinux 2.4.19中断irq分发例程的派发流程之中间层
文章来源:http://gliethttp.cublog.cn
接续前一篇《浅析armlinux 2.4.19中断irq分发例程的派发流程之根基》
看看irq寄存器环境保存和高层asm_do_IRQ()中断处理函数的调用跳转[gliethttp_20071225].
//----------------------------------------------------------------------
//1.先看看__irq_svc中断irq处理例程的实现代码'
//在arch\arm\kernel\entry-header.S中有如下3个常量定义
/*
#ifdef CONFIG_CPU_32
#define S_FRAME_SIZE 72
#define S_OLD_R0 68
#define S_PSR 64
#endif
其实这就是pt_regs结构体的偏移值,该结构体定义在include\asm-arm\proc-armv\ptrace.h中
struct pt_regs {
long uregs[18];
};
#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]
*/
.align 5
//因为在vector_IRQ中irq模式下的lr_irq和lr_spsr执行msr spsr_c, r13之后,使用movs pc, lr进行跳
//转到__irq_svc,所以带s的操作指令movs,将使spsr_c的状态自动恢复到cpsr中,也就是使cpu工作在
//禁止irq中断的svc模式下[当然irq的I位在发生irq的同时就有硬件自动禁用了gliethttp_20071225]
//所以linux2.4内核下irq处理程序使用的sp就是暂时借用了svc内核空间程序的堆栈,如下代码也工作在svc模式
__irq_svc:sub sp, sp, #S_FRAME_SIZE
//因为我的at91rm9200在linux2.4.19中工作在"小端"模式,低址存低位,
//所以下面的r1=sp-S_FRAME_SIZE的地址就是&uregs[0]的地址[gliethttp_20071225]
//从低址向高址依次存放r0,r1,...,r12,也就是将数据存放到结构体的uregs[0]~uregs[12]中
stmia sp, {r0 - r12}
//参看《浅析armlinux 2.4.19中断irq分发例程的派发流程之根基》
//.LCirq+0 存放了 lr-4的地址值,也就是irq中断处理函数处理完成之后,返回到的pc地址值
//.LCirq+4 存放了 spsr,也就是irq中断发生跳转到irq处理函数vector_IRQ之前cpu所处的模式值
// 主要是用来对I和F位以及几个N、Z、C和V标志位的恢复
ldr r7, .LCirq
add r5, sp, #S_FRAME_SIZE//r5存放了由该irq中断处理程序借用的svc的实际堆栈指针
ldmia r7, {r7 - r9}//r7=lr-4;r8=spsr;r9=随意值,实际为__temp_abt数值
add r4, sp, #S_SP//r4=&ARM_sp=&uregs[13]地址
//r6=当前svc模式下的lr,不允许嵌套中断发生;
//因为这是svc内核代码正在执行的时候发生的irq中断处理,所以当然要保存svc模式下正在运算的lr地址值,
//这样从irq返回时也才能够继续执行svc模式下被irq中断了的运行的内核代码[gliethttp_20071225]
mov r6, lr
//gliethttp_20071225
//按照存pt_regs结构体的储空间规则存放r0-r12,sp_svc,lr_svc,pc,cpsr,ARM_ORIG_r0;总之pt_regs存放了
//发生irq中断之前cpu所运行的整个环境,比如:正在内核空间进行pte_alloc()的操作的时候,irq发生到此,
//那么pt_regs存放着pte_alloc()运行到被irq中断的那个时刻,所有的寄存器值[gliethttp_20071225].
stmia r4, {r5, r6, r7, r8, r9}
/*
//at91rm9200中断号读取代码
.macro get_irqnr_and_base, irqnr, irqstat, base, tmp
ldr \base, =(AT91C_VA_BASE_SYS)
ldr \irqnr, [\base, #AIC_IVR]
ldr \irqstat, [\base, #AIC_ISR]
teq \irqstat, #0
streq \tmp, [\base, #AIC_EOICR]
.endm
*/
1: get_irqnr_and_base r0, r6, r5, lr
movne r1, sp
/*
//gliethttp_20071225[entry-header.S]
//Like adr, but force SVC mode (if required)
.macro adrsvc, cond, reg, label
adr\cond \reg, \label
.endm
adrsvc ne, lr, 1b等价于subne lr, pc, #32
*/
adrsvc ne, lr, 1b
//gliethttp_20071225
//arch\arm\kernel\irq.c->asmlinkage void asm_do_IRQ(int irq, struct pt_regs *regs)
//对asm_do_IRQ的理解将在另一篇中《浅析armlinux 2.4.19中断irq分发例程的派发流程之高端层》浅析
bne asm_do_IRQ//在I位禁用irq中断期间执行asm_do_IRQ高端处理函数
ldr r0, [sp, #S_PSR]//将发生这个irq中断之前的cpu状态寄存器spsr拿出来
msr spsr, r0//拷贝到svc模式的spsr中
//弹出pt_regs结构体中的所有寄存器数值,恢复到被irq中断的内核代码执行处继续执行[gliethttp_20071225]
//^号操作中,如果有pc寄存器,那么和s操作一样,将恢复spsr中的内容到cpsr,同时自动切换cpu模式到spsr
ldmia sp, {r0 - pc}^
//----------------------------------------------------------------------
//2.再看看__irq_usr中断irq处理例程的实现代码
//从用户空间发生的irq中断,
//因为在vector_IRQ中irq模式下的lr_irq和lr_spsr执行msr spsr_c, r13之后,使用movs pc, lr进行跳
//转到__irq_svc,所以带s的操作指令movs,将使spsr_c的状态自动恢复到cpsr中,也就是使cpu工作在
//禁止irq中断的svc模式下[当然irq的I位在发生irq的同时就有硬件自动禁用了gliethttp_20071225]
//所以linux2.4内核下irq处理程序使用的sp就是暂时借用了svc内核空间程序的堆栈,如下代码也工作在svc模式
.align 5
__irq_usr:sub sp, sp, #S_FRAME_SIZE//理解同__irq_svc
stmia sp, {r0 - r12}//注意usr用户模式下的r0-r12和svc模式下的r0-r12是共用的
//参看《浅析armlinux 2.4.19中断irq分发例程的派发流程之根基》
//.LCirq+0 存放了 lr-4的地址值,也就是irq中断处理函数处理完成之后,返回到的pc地址值
//.LCirq+4 存放了 spsr,也就是irq中断发生跳转到irq处理函数vector_IRQ之前cpu所处的模式值
// 主要是用来对I和F位以及几个N、Z、C和V标志位的恢复
ldr r4, .LCirq
add r8, sp, #S_PC
//r5=用户模式下lr-4的地址值,也就是irq中断处理函数处理完成之后,返回到的pc地址值
//r6=用户模式下的cpsr值,返回时会被使用到
//r7=暂时无用
ldmia r4, {r5 - r7}
stmia r8, {r5 - r7}//uregs[15]=r5;uregs[16]=r6;uregs[17]=r7
stmdb r8, {sp, lr}^//^表示存储user用户模式下的sp,lr寄存器到r8,r8-4处
//通过上面的几个简单操作,被irq中断了的用户空间程序所使用的所有cpu寄存器都被暂时
//保存到了svc内核程序空间的sp堆栈上[gliethttp_20071225]
/*arch\arm\kernel\entry-header.S
.macro alignment_trap, rbase, rtemp, sym
#ifdef CONFIG_ALIGNMENT_TRAP
#define OFF_CR_ALIGNMENT(x) cr_alignment - x
ldr \rtemp, [\rbase, #OFF_CR_ALIGNMENT(\sym)]
mcr p15, 0, \rtemp, c1, c0
#endif
.endm
alignment_trap r4, r7, __temp_irq
zero_fp
对应的反汇编为:
c001737c: e5947024 ldr r7, [r4, #36]
c0017380: ee017f10 mcr 15, 0, r7, cr1, cr0, {0}
c0017384: e3a0b000 mov fp, #0 ; 0x0
*/
//__temp_irq为.LCirq+0的值,也就是lr-4,也就是irq中断处理完成之后,用户空间的pc值
//所以它必须是合法的,必须经过检查
alignment_trap r4, r7, __temp_irq
zero_fp
1: get_irqnr_and_base r0, r6, r5, lr
movne r1, sp
adrsvc ne, lr, 1b
bne asm_do_IRQ//参数r0=irq,r1=regs
mov why, #0
/*
arch\arm\kernel\entry-header.S
.macro get_current_task, rd
mov \rd, sp, lsr #13
mov \rd, \rd, lsl #13
.endm
*/
//因为当前在svc模式下,和当前的usr空间是一一对应的
//在schedule的时候,没有用户模式下的task都会对应一个内核模式堆栈
//所以这个sp就是svc内核模式下的用户task堆栈
//该内核模式堆栈的低8k空间存放了current的进程task_struct内核数据结构体
get_current_task tsk
b ret_to_user
/*
arch\arm\kernel\entry-common.S
ENTRY(ret_to_user)
ret_slow_syscall:
ldr r1, [tsk, #TSK_NEED_RESCHED]
ldr r2, [tsk, #TSK_SIGPENDING]
teq r1, #0 @ need_resched => schedule()
bne reschedule
1: teq r2, #0 @ sigpending => do_signal()
bne __do_signal
restore:
//从当前sp恢复到usr用户空间,继续执行刚才被irq中断了的用户空间程序[gliethttp_20071225]
restore_user_regs
__do_signal:
enable_irq r1
mov r0, #0 @ NULL 'oldset'
mov r1, sp @ 'regs'
mov r2, why @ 'syscall'
bl SYMBOL_NAME(do_signal) @ note the bl above sets lr
disable_irq r1 @ ensure IRQs are disabled
b restore
*/