一些基础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)

 

posted on 2013-08-28 22:29  choi87  阅读(634)  评论(0)    收藏  举报