一些基础arm linux中断相关的介绍不在这里重复,可以网上查阅一下网上的资料,这里就直接开门间山,跟着代码走一下中断发生后的处理。
1. exception vector table
1 .globl __vectors_start 2 __vectors_start: 3 ARM( swi SYS_ERROR0 ) 4 THUMB( svc #0 ) 5 THUMB( nop ) 6 W(b) vector_und + stubs_offset 7 W(ldr) pc, .LCvswi + stubs_offset 8 W(b) vector_pabt + stubs_offset 9 W(b) vector_dabt + stubs_offset 10 W(b) vector_addrexcptn + stubs_offset 11 W(b) vector_irq + stubs_offset 12 W(b) vector_fiq + stubs_offset 13 14 .globl __vectors_end 15 __vectors_end:
上面的代码(arch/arm/kernel/entry_armv.S)是arm linux的exception vector table。需要注意的是__vectors_start和__stubs_start处的代码在内核初始化过程中从原来的内核代码段拷贝到了0xffff0000和0xffff0200。(具体请参考start_kernel -> setup_arch -> paging_init -> devicemaps_init ->early_trap_init函数)
当发生一个外部中断时处理器跳转到这个table中的” W(b) vector_irq + stubs_offset”处。这里简单介绍一下vector_irq和stubs_offset。
1.1 vector_irq
1 __stubs_start: 2 /* 3 * Interrupt dispatcher 4 */ 5 vector_stub irq, IRQ_MODE, 4 6 7 .long __irq_usr @ 0 (USR_26 / USR_32) 8 .long __irq_invalid @ 1 (FIQ_26 / FIQ_32) 9 .long __irq_invalid @ 2 (IRQ_26 / IRQ_32) 10 .long __irq_svc @ 3 (SVC_26 / SVC_32) 11 .long __irq_invalid @ 4 12 … (此处省略N个指令)
在这块代码段中vector_stub是个宏,宏的内容如下。
1 .macro vector_stub, name, mode, correction=0 2 .align 5 3 4 vector_\name: 5 .if \correction 6 sub lr, lr, #\correction 7 .endif 8 9 …(此处省略N行指令) 10 1: 11 .endm
这样vector_stub irq, IRQ_MODE, 4展开以后第一行的vector_\name:就变成了vector_irq。不少人会觉得向量表中的” W(b) vector_irq + stubs_offset”这条指令有些疑惑,觉得” W(b) vector_irq”就会跳到上面所述的代码中,为什么后面还需要加上stubs_offset呢?下面就一起讨论一下stubs_offset。
1.2 stubs_offset
上面提到过vectors_start和stubs_start都会在early_trap_init函数中进行搬迁。vectors_start搬迁到0xffff0000,stubs_start搬迁到0xffff0200处。“b vector_irq”实际上是编译的时候根据原代码段的pc(跳转到vector_irq的指令地址)和vector_irq的地址偏移进行的跳转,但是很显然经过代码搬迁后这个相对偏移肯定发生变化。那怎么正确跳转到搬迁后的vector_irq呢?请看下面的计算。
搬迁之前的vectors_start和stubs_start的偏移是stubs_start - vectors_start,搬迁后变成了0x200。这样0x200 - (stubs_start - vectors_start)正好是搬迁以后两个地方的偏移发生变化的量。所以”b vector_irq”改成”b vector_irq + 0x200 - (stubs_start - vectors_start)”就可以正确访问到搬迁后的vector_irq。而stubs_offset正好是.equ stubs_offset, __vectors_start + 0x200 - __stubs_start。这就是stubs_offset的来由。
2. vector_irq
下面细看一下vector_stub的内容
1 .macro vector_stub, name, mode, correction=0 2 .align 5 3 @ 发生中断后处理器响应中断的伪代码如下 4 @ 1. lr_<exception> = return link 5 @ 2. spsr_<exception> = cpsr 6 @ 3. cpsr[4:0] = exception mode number 7 @ 4. cpsr[5] = 0 @ if run in ARM 8 @ 5. if (fiq or reset) then cpsr[6] = 1 @ disable F 9 @ 6. cpsr[7] = 1 @ disable I 10 @ 7. pc = exception vector address 11 vector_\name: 12 .if \correction 13 sub lr, lr, #\correction @ 调整lr,这里lr = lr - 4 14 .endif 15 16 stmia sp, {r0, lr} @ save r0, lr 17 mrs lr, spsr 18 str lr, [sp, #8] @ save spsr 此时sp指针未做移动 19 20 @ 准备转换到SVC模式,IRQ仍然保持禁止 21 mrs r0, cpsr 22 eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE) 23 msr spsr_cxsf, r0 24 25 @ 随后要跳转的branch table必须紧挨着下面这部分代码 26 and lr, lr, #0x0f @ 先取一下当前ARM的模式 27 THUMB( adr r0, 1f ) 28 THUMB( ldr lr, [r0, lr, lsl #2] ) 29 mov r0, sp @ 把sp放到r0中(注意r0-r7是各个模式公用) 30 ARM( ldr lr, [pc, lr, lsl #2] ) @ 跳转的地址lr请见下一节的说明。 31 movs pc, lr @ 跳转的同时把spsr恢复到cpsr中,切换到svc 32 ENDPROC(vector_\name)
ldr lr, [pc, lr, lsl #2]到底是哪个代码段呢? 这个需要看一下下面的代码。
1 /* 2 * Interrupt dispatcher 3 */ 4 vector_stub irq, IRQ_MODE, 4 5 6 .long __irq_usr @ 0 (USR_26 / USR_32) 7 .long __irq_invalid @ 1 (FIQ_26 / FIQ_32) 8 .long __irq_invalid @ 2 (IRQ_26 / IRQ_32) 9 .long __irq_svc @ 3 (SVC_26 / SVC_32) 10 .long __irq_invalid @ 4 11 .long __irq_invalid @ 5 12 .long __irq_invalid @ 6 13 .long __irq_invalid @ 7 14 .long __irq_invalid @ 8 15 .long __irq_invalid @ 9 16 .long __irq_invalid @ a 17 .long __irq_invalid @ b 18 .long __irq_invalid @ c 19 .long __irq_invalid @ d 20 .long __irq_invalid @ e 21 .long __irq_invalid @ f
vector_stub结束后紧挨着放着多个.long。细心的朋友已经发现了arm中模式的数值和上面的模式存放的序号是一致的。比如svc模式的数值是0b10011,与上0xf后就是3。
这样”ldr lr, [pc, lr, lsl #2]”得到了lr就是” .long __irq_svc ”。自然的接下来就调到了__irq_svc处。(如果是在usr模式下发生的中断,就跳到__irq_usr)
3. __irq_svc
1 __irq_svc: 2 svc_entry 3 irq_handler 4 5 #ifdef CONFIG_PREEMPT 6 get_thread_info tsk 7 ldr r8, [tsk, #TI_PREEMPT] @ get preempt count 8 ldr r0, [tsk, #TI_FLAGS] @ get flags 9 teq r8, #0 @ if preempt count != 0 10 movne r0, #0 @ force flags to 0 11 tst r0, #_TIF_NEED_RESCHED 12 blne svc_preempt 13 #endif 14 15 #ifdef CONFIG_TRACE_IRQFLAGS 16 @ The parent context IRQs must have been enabled to get here in 17 @ the first place, so there's no point checking the PSR I bit. 18 bl trace_hardirqs_on 19 #endif 20 svc_exit r5 @ return from exception 21 UNWIND(.fnend ) 22 ENDPROC(__irq_svc)
浙公网安备 33010602011771号