Linux異常處理結構、中斷處理結構

LINUX 的異常向量在哪裏:0xffff0000(可通過配置某個寄存器來配置)

1、ARM異常向量表

void __init trap_init(void)   //將0x00000000異常向量 複製到 0xffff0000 處

      arch/arm/kernel/entry-armv.S

.globl    __vectors_start
__vectors_start:
    swi    SYS_ERROR0                            /* 復位時,執行這條指令 */
    b    vector_und + stubs_offset               /* 未定義異常 */
    ldr    pc, .LCvswi + stubs_offset            /* swi異常 */
    b    vector_pabt + stubs_offset              /* 指令預取異常 */
    b    vector_dabt + stubs_offset              /* 數據訪問終止 */
    b    vector_addrexcptn + stubs_offset        /* 沒有用 */
    b    vector_irq + stubs_offset               /* irq異常 */
    b    vector_fiq + stubs_offset               /* fiq異常 */

    .globl    __vectors_end
__vectors_end:

 異常向量表,無非還是一些跳轉指令。當發生irq中斷,執行指令“b vector_irq + stubs_offset”,也就是跳轉到vector_irq代碼段繼續執行。

      在linux內核初始化階段,start_kernel函數(init/main.c)會調用trap_init、init_IRQ兩個函數來初始化異常向量相關處理函數。簡要說明就是,將異常向量表拷貝到地址0xffff0000處(ARM體系協處理器寄存器c1能設置異常向量的基地址爲0xffff0000),再把異常向量表中異常處理的進一步函數代碼段拷貝到0xffff0200位置(vector_und、vector_irq等)。

2、異常處理進一步函數----vector_irq

      arch/arm/kernel/entry-armv.S

.globl    __stubs_start
__stubs_start:
    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
    .long    __irq_invalid            @  5
    .long    __irq_invalid            @  6
    .long    __irq_invalid            @  7
    .long    __irq_invalid            @  8
    .long    __irq_invalid            @  9
    .long    __irq_invalid            @  a
    .long    __irq_invalid            @  b
    .long    __irq_invalid            @  c
    .long    __irq_invalid            @  d
    .long    __irq_invalid            @  e
    .long    __irq_invalid            @  f

  從第4行到第19行,記錄了(代碼鏈接階段填入的地址數據)在各個模式下遇到irq中斷時,發生異常的處理分支。比如第4行__irq_usr表示用戶模式下發生irq中斷時,由__irq_usr對應的代碼段來處理這種情況。

 

vector_stub是一個宏,將宏展開內容如下:

vector_irq:

    //保存cpsr/lr

    sub    lr, lr, #4
    stmia    sp, {r0, lr}    @ save r0, lr
    
    mrs    lr, spsr
    str    lr, [sp, #8]        @ save spsr
    //切換工作模式
    mrs    r0, cpsr
    eor    r0, r0, #(IRQ_MODE ^ SVC_MODE)
    msr    spsr_cxsf, r0

    //下一級跳轉   

   and    lr, lr, #0x0f
    mov    r0, sp
    ldr    lr, [pc, lr, lsl #2]
    movs    pc, lr            @ branch to handler in SVC mode
    .endm

這個宏的目的就是,根據進入irq中斷前處理器所處的模式,將緊接着其下邊的16個地址池中對應位置的處理向量,取出來賦給PC,完成進一步跳轉。這裏我們選擇讓程序跳轉到__irq_usr代碼段繼續執行。

3、異常處理進一步函數----__irq_usr

      arch/arm/kernel/entry-armv.S

__irq_usr:
    usr_entry             @將usr模式下的寄存器、中斷返回地址保存到堆棧中    //保存現場

    get_thread_info tsk  @獲取當前進程的進程描述符中的成員變量thread_info的地址,並將該地址保存到寄存器tsk等於r9

    irq_handler          @中斷處理    //宏

    mov    why, #0       
    b    ret_to_user     @中斷處理完成,返回中斷產生的位置

 

 

4、irq_handler

      irq_handler是一個宏,將其內容展開如下:

      arch/arm/kernel/entry-armv.S

.macro    irq_handler
  get_irqnr_preamble r5, lr
1:    get_irqnr_and_base r0, r6, r5, lr
    movne    r1, sp
    adrne    lr, 1b
    bne    asm_do_IRQ
   .endm

由此可見,進入asm_do_IRQ函數開始具體的中斷處理。需要指出的是,asm_do_IRQ是中斷的C語言總入口函數。

5. asm_do_IRQ函數原型爲

smlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)

進入asm_do_IRQ這個函數

(1)struct irq_desc *desc = irq_desc + irq;

    從名字上看可知    irq_desc[NR_IRQS]這是一個“中斷描述”數組。以中斷號“NR_IRQS”爲下標。
   struct irq_desc *desc = irq_desc + irq;
   在這個數組下標處取到一個“數組項”。

(2) 進入處理函數 desc_handle_irq(irq, desc);
            
       static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc)
       {
                        desc->handle_irq(irq, desc);
       }
            

 搜索handle_irq,發現handle_irq被__set_irq_handler調用過
 搜索__set_irq_handler,發現__set_irq_handler被set_irq_handler調用過
 搜索set_irq_handler,發現__set_irq_handler被s3c24xx_init_irq(void)調用過

 

1~2作用: 
                在“void __init s3c24xx_init_irq(void)”中實現初始化

 

中斷處理 C 函數入口 “asm_do_IRQ”會調用到事先初始化好的“handle_irq”函數。
handle_irq == handle_edge_irq
 handle_edge_irq:
  -->desc->chip->ack(irq). 清中斷
 -->handle_IRQ_event(irq, action). 
                      --> action->handler()處理中斷

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章