天天看點

linux中斷子系統(三) - 中斷調用過程

cpu中斷入口

我們知道,arm的異常和複位向量表有兩種選擇,一種是低端向量,向量位址位于0x00000000,另一種是高端向量,向量位址位于0xffff0000,Linux選擇使用高端向量模式,也就是說,當異常發生時,CPU會把PC指針自動跳轉到始于0xffff0000開始的某一個位址上。

ARM異常向量表

位址 異常類别
0xffff0000 複位
0xffff0004 未定義指令
0xffff0008 軟中斷swi
0xffff000c prefetch abort
0xffff0010 data abort
0xffff0014 保留
0xffff0018 IRQ
0xffff001c FIQ

中斷向量表在arch/arm/kernel/entry_armv.S中定義,系統啟動階段setup_arch函數會調用arch/arm/kernel/traps.h中的early_trap_init,

這個函數會把__vectors_start(這個位址是不是連結在核心鏡像,編譯完了指派的)開始的代碼拷貝到0xffff0000處,

把__stubs_start開始的代碼拷貝到0xffff+0x200處,這樣中斷來時,就可以跳轉到相應的中斷向量入口。

memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);

memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);

真正的向量跳轉表,位于__vectors_start和__vectors_end之間

.equ    stubs_offset, __vectors_start + 0x200 - __stubs_start
    .globl  __vectors_start
__vectors_start:
 ARM(   swi SYS_ERROR0  )
 THUMB( svc #0      )
 THUMB( nop         )
    W(b)    vector_und + stubs_offset
    W(ldr)  pc, .LCvswi + stubs_offset
    W(b)    vector_pabt + stubs_offset
    W(b)    vector_dabt + stubs_offset
    W(b)    vector_addrexcptn + stubs_offset
    W(b)    vector_irq + stubs_offset
    W(b)    vector_fiq + stubs_offset

    .globl  __vectors_end
__vectors_end:      

跳轉部分

.globl  __stubs_start
__stubs_start:
    /*
     * Interrupt dispatcher
     */
    //這一句宏展開後實際上就是定義了vector_irq,根據進入cpu中斷前的模式,分别跳轉到__irq_usr或__irq_svc
    vector_stub irq, IRQ_MODE, 4
    .long   __irq_usr           @  0  (USR_26 / USR_32)
    .long   __irq_invalid           @  1  (FIQ_26 / FIQ_32)
    .long   __irq_invalid           @  2  (IRQ_26 / IRQ_32)
    .long   __irq_svc           @  3  (SVC_26 / SVC_32)
    .long   __irq_invalid           @  4
    ...

    /*
     * Data abort dispatcher
     * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
     */
    //這一句宏展開後實際上就是定義了vector_dabt,根據進入cpu中斷前的模式,分别跳轉到__dabt_usr或__dabt_svc
    vector_stub dabt, ABT_MODE, 8
    ...

    /*
     * Prefetch abort dispatcher
     * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
     */
    vector_stub pabt, ABT_MODE, 4
    ...

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

vector_fiq:
    disable_fiq
    subs    pc, lr, #4

vector_addrexcptn:
    b   vector_addrexcptn

.LCvswi:
    .word   vector_swi

    .globl  __stubs_end
__stubs_end:      

__irq_usr和__irq_svc的差別是進入和退出中斷時是否進行使用者棧和核心棧之間的切換等,他們最終都會進入到irq_handler這個宏

.macro  irq_handler
    get_irqnr_preamble r5, lr
1:  get_irqnr_and_base r0, r6, r5, lr
    movne   r1, sp
    @
    @ routine called with r0 = irq number, r1 = struct pt_regs *
    @
    adrne   lr, BSYM(1b)
    bne asm_do_IRQ

    .endm      

asm_do_IRQ

asm_do_IRQ()  arch/arm/kernel/irq.c
    generic_handle_irq()
        generic_handle_irq_desc
            desc->handle_irq 系統中斷處理函數
                desc->chip->ack(irq) 清除中斷
                handle_IRQ_event(irq, action) 周遊并調用這個desc下所有的使用者中斷處理函數action
                    action->handler(irq, action->dev_id); 使用者中斷處理函數      

參考文章

  1. ​​Linux中斷(interrupt)子系統之二:arch相關的硬體封裝層 ​​

繼續閱讀