浅析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 */
|