ARM Linux 中断向量表建立流程

Linux Version : 2.6.29

1. start_kernel-->setup_arch-->early_trap_init

   1:  
    memcpy((void
 *)vectors, __vectors_start, __vectors_end - __vectors_start);
   2:  
    memcpy((void
 *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
   3:  
    memcpy((void
 *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);

对于第一行:

         __vectors_start 和 __vectors_end 定义在 arch/arm/kernel/entry-armv.S  , 它们之间保存了中断向量表。      

   1:  
    .globl    __vectors_start
   2:  
__vectors_start:
   3:  
    swi    SYS_ERROR0   
   4:  
    b    vector_und + stubs_offset
   5:  
    ldr    pc, .LCvswi + stubs_offset
   6:  
    b    vector_pabt + stubs_offset
   7:  
    b    vector_dabt + stubs_offset
   8:  
    b    vector_addrexcptn + stubs_offset
   9:  
    b    vector_irq + stubs_offset
  10:  
    b    vector_fiq + stubs_offset
  11:  
 
  12:  
    .globl    __vectors_end
  13:  
__vectors_end:

         vectors 的地址为CONFIG_VECTORS_BASE , 在.config中定义为0xffff0000

         所以 第1行就是把中断向量表拷贝到0xffff0000

  对于第二行: 

             vector_stub是一个带参数的宏,第一个是name,第二个是arm excepiton mode,第三个是为了得到返回地址,lr需要减去的偏移

   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:  
    @
  10:  
    @ Save r0, lr_ (parent PC) and spsr_
  11:  
    @ (parent CPSR)
  12:  
    @
  13:  
    stmia    sp, {r0, lr}        @ save r0, lr
  14:  
    mrs    lr, spsr
  15:  
    str    lr, [sp, #8]        @ save spsr
  16:  
 
  17:  
    @
  18:  
    @ Prepare for
 SVC32 mode.  IRQs remain disabled.
  19:  
    @ 
  20:  
    mrs    r0, cpsr
  21:  
    eor    r0, r0, #(/mode ^ SVC_MODE) @把cpsr内容与(mode^SVC_mode)异或,即r0里为SVC_MODE      
  22:  
    msr    spsr_cxsf, r0  @把r0的值写入整个spsr寄存器(cxsf表示要往哪个字节写入)
  23:  
 
  24:  
    @
  25:  
    @ the branch table must immediately follow this
 code
  26:  
    @
  27:  
    and    lr, lr, #0x0f  @lr为spsr_的值,此语句取到进入异常前的mode
  28:  
    mov    r0, sp         @ 
  29:  
    ldr    lr, [pc, lr, lsl #2] @lr=pc+mode*4,其中pc为紧接着30的指令,即vector_stub后的第一条指令
  30:  
    movs    pc, lr            @ movs会把spsr的值赋给cpsr,所以branch to handler in
 SVC mode
  31:  
ENDPROC(vector_/name)
  32:  
    .endm

            再来看下vector 跳转表

   1:  
    .long
    __irq_usr            @  0  (USR_26 / USR_32)
   2:  
    .long
    __irq_invalid            @  1  (FIQ_26 / FIQ_32)
   3:  
    .long
    __irq_invalid            @  2  (IRQ_26 / IRQ_32)
   4:  
    .long
    __irq_svc            @  3  (SVC_26 / SVC_32)
   5:  
    .long
    __irq_invalid            @  4
   6:  
    .long
    __irq_invalid            @  5
   7:  
    .long
    __irq_invalid            @  6
   8:  
    .long
    __irq_invalid            @  7
   9:  
    .long
    __irq_invalid            @  8
  10:  
    .long
    __irq_invalid            @  9
  11:  
    .long
    __irq_invalid            @  a
  12:  
    .long
    __irq_invalid            @  b
  13:  
    .long
    __irq_invalid            @  c
  14:  
    .long
    __irq_invalid            @  d
  15:  
    .long
    __irq_invalid            @  e
  16:  
    .long
    __irq_invalid            @  f

             这里只有usr 和svc 有入口,而其他都是invalid ,是因为linux只会从usr(application) 和svc(kernel)两种mode跳转到exception来

          __stubs_start 和 __stubs_end 之间的代码简化后为:

   1:  
__stubs_start:
   2:  
   vector_irq:    @vector_stub    irq, IRQ_MODE, 4
   3:  
   vector_dabt:   @vector_stub    dabt, ABT_MODE, 8
   4:  
   vector_pabt:   @vector_stub    pabt, ABT_MODE, 4
   5:  
   vector_und:    @vector_stub    und, UND_MODE
   6:  
   vector_fiq:
   7:  
   vector_addrexcptn:
   8:  
   .LCvswi:
   9:  
__stubs_end:

          由此可以知道 __stubs_start 和 __stubs_end 之间定义了各种异常的入口

         我们再来看为什么异常入口是“b vector_und + stubs_offset”, 同时为什么stubs_offset 的定义如下

.equ    stubs_offset, __vectors_start + 0x200 - __stubs_start

          arm 的跳转指令b 是跳转到相对于PC的一个偏移地址( offset ),汇编器在编译时会对label 减去PC 得到offset,同时vector 拷贝后是如下排列的

 

__vectors_start

 

 

 

B vector_

 

 

 

__vectors_end

 

 

+0x200

__stubs_start

 

 

 

vector_

 

 

 

__stubs_end

            因此,"b vector_"  的label –PC =  offset, 而offset 为 b 指令与vector的offset,即

                         vector_-__stubs_start + ( 0x200 – ( PC_old – __vectors_start ) )

                       = vector_ + __vectors_start + 0x200 – __stubs_start – PC_old

                所以异常入口为“b vector_und + stubs_offset”, 同时stubs_offset= __vectors_start + 0x200 – __stubs_start

            我们可以通过objdump反汇编来验证:

 

00000060 :
    .globl    __stubs_start
__stubs_start:
/*
* Interrupt dispatcher
*/
    vector_stub    irq, IRQ_MODE, 4
  60 :    e24ee004     sub    lr, lr, #4    ; 0x4
  64:    e88d4001     stm    sp, {r0, lr}

 

1d4:    e1a00000     .word    0xe1a00000
1d8:    e1a00000     .word    0xe1a00000
1dc:    e1a00000     .word    0xe1a00000

000001e0 :

/*
* Undef instr entry dispatcher
* Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
*/

 

__vectors_start:
    swi    SYS_ERROR0
284:    ef9f0000     svc    0x009f0000
    b    vector_und + stubs_offset
 288:    ea0000dd     b    604
    ldr    pc, .LCvswi + stubs_offset
28c:    e59ff410     ldr    pc, [pc, #1040]    ; 6a4
    b    vector_pabt + stubs_offset
290:    ea0000bb     b    584
    b    vector_dabt + stubs_offset
294:    ea00009a     b    504
    b    vector_addrexcptn + stubs_offset
298:    ea0000fa     b    688
    b    vector_irq + stubs_offset
29c:    ea000078     b    484
    b    vector_fiq + stubs_offset
2a0:    ea0000f7     b    684

                              0x1e0 – 0x60 + 0x200 – ( 0x288 + 8 ) – 0x284 = 0xdd*4

           


你可能感兴趣的:(ARM Linux 中断向量表建立流程)