亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

LINUX中斷描述符初始化

系統 2016 0
LINUX中斷描述符初始化

@CopyLeft by ICANTH I Can do ANy THing that I CAN THink ~

Author WenHui WuHan University 2012-6-4

硬件產生中斷之后,需要通過門描述符來尋找中斷的處理程序入口。門描述符和段描述符一樣,8個字節。門描述符大體分為:段偏移、段選擇子以及DPL。段選擇子用于在GDT中尋找到門段基址,DPL用于控制當前進程中斷或異常訪問權限。當發生中斷時,將門描述符所指向的段基地放入%cs,將段偏移放入%eip,轉入相應服務。

門描述符結構如下:

clip_image002

任務門描述符 :用于在發生中斷時調度相應進程

中斷門描述符 :描述中斷處理程序所在段選擇子和段內偏移值。據此修改EIP及關閉中斷Eflags的IT標識位

陷阱門描述符 :與中斷門描述符一樣,但不關中斷

中斷描述符表IDT( Interrupt Descriptor Table )用于存放256個門描述符,對應256個中斷向量。其中IA32規定前0~31號固定向量用于異常。寄存器 idtr 為門描述符表IDT的物理地址。根據向量尋找基對應門描述符,并將中斷或異常程序加載過程下圖所示:

clip_image004

LINUX初始化中斷描述符表分兩步:初步初始化,以及最終初始化。初步初始化在head.S文件: http://os1a.cs.columbia.edu/lxr/source/arch/x86/kernel/head_32.S

中斷描述符表初步初始化

1)聲明256個門描述符的IDT表空間。

    
    
    
      701
    
     idt_descr:
  
    
    
    
      702
    
    ???????? .word IDT_ENTRIES*8-1?????????? # idt contains 256 entries
  
    
    
    
      703
    
    ???????? .long idt_table
  

2)設置指向IDT表地址的寄存器 ldtr

    
    
    
      425
    
    ???????? lgdt early_gdt_descr
  
    
    
    
      
        426
      
    
    
      ???????? lidt idt_descr
    
  
    
    
    
      427
    
    ???????? ljmp $(__KERNEL_CS),$1f
  
    
    
    
      428
    
     1:????? movl $(__KERNEL_DS),%eax??????? # reload all the segment registers
  

2)初始化256個門描述符。對于每個門描述符,段選擇子都指向內核段,段偏移都指向igore_int函數,該函數只打印一句話:“哥們,別急,俺還沒被真正初始化勒!~”。

    
    
    
      490
    
     /*
  
    
    
    
      491
    
    ? *? setup_idt
  
    
    
    
      492
    
    ? *
  
    
    
    
      493
    
    ? *? sets up a idt with 256 entries pointing to
  
    
    
    
      494
    
    ? *? ignore_int, interrupt gates. It doesn't actually load
  
    
    
    
      495
    
    ? *? idt - that can be done only after paging has been enabled
  
    
    
    
      496
    
    ? *? and the kernel moved to PAGE_OFFSET. Interrupts
  
    
    
    
      497
    
    ? *? are enabled elsewhere, when we can be relatively
  
    
    
    
      498
    
    ? *? sure everything is ok.
  
    
    
    
      499
    
    ? *
  
    
    
    
      500
    
    ? *? Warning: %esi is live across this function.
  
    
    
    
      501
    
    ? */
  
    
    
    
      502
    
     setup_idt:
  
    
    
    
      503
    
    ???????? lea ignore_int,%edx
  
    
    
    
      504
    
    ???????? movl $(__KERNEL_CS << 16),%eax
  
    
    
    
      505
    
    ???????? movw %dx,%ax??????????? /* selector = 0x0010 = cs */
  
    
    
    
      506
    
    ???????? movw $0x8E00,%dx??????? /* interrupt gate - dpl=0, present */
  
    
    
    
      507
    
  
    
    
    
      508
    
    ???????? lea idt_table,%edi
  
    
    
    
      509
    
    ???????? mov $256,%ecx
  
    
    
    
      510
    
     rp_sidt:
  
    
    
    
      511
    
    ???????? movl %eax,(%edi)
  
    
    
    
      512
    
    ???????? movl %edx,4(%edi)
  
    
    
    
      513
    
    ???????? addl $8,%edi
  
    
    
    
      514
    
    ???????? dec %ecx
  
    
    
    
      515
    
    ???????? jne rp_sidt
  
中斷描述符表最終初始化

中斷描述符表最終初始化分為兩部分:異常和中斷,分別在函數trap_init(void)和init_IRQ(void)中實現,都由系統初始化入口函數start_kernel()所調用。對于特定異常,其處理異常函數預先已經設定好;但對于特定異步中斷,由于需要捕獲設備I/O中斷的程序數目不定,所以得采用特定數據結構來對irq及其action進行描述。

trap_init: http://os1a.cs.columbia.edu/lxr/source/arch/x86/kernel/traps.c#830

do_IRQ:

1 )異常部分最終初始化

由于IA32規定異常所對應的固定向量,所以直接調用set_trap_gate()、set_intr_gate()、set_system_gate()、set_system_intr_gate()、set_task_gate()、set_intr_gate_ist()設置異常處理函數、初始相應異常。而這些函數,都只是_set_gate宏的封裝而已。異常處理函數由宏定義DO_ERROR生成。其調用關系如下:

clip_image006

在trap_init(void)函數中,調用在set_intr_gate_ist()設置“stack exception”的12號中斷門描述符。set_intr_gate_ist是設置中斷描述符函數_set_gate的一層封裝,給_set_gate傳入異常處理函數stack_segment作為門描述符的偏移地址,傳入 __KERNEL_CS 作為段選擇子,傳入GATE_INTERRUPT表示中斷門描述符類型,傳入DPL = 0表示只能由內核態訪問、而不能由用戶調用。

_set_gate函數中,其調用pack_gate()組裝成一個門描述符格式,并調用write_idt_entry寫入IDT表中相應描述符。stack_segment函數壓入do_stack_segment函數地址并跳轉到error_code匯編代碼進行處理。具體代碼(/arch/x86/include/asm/desc.h)如下:

    
    
    
      830
    
     void 
    
      __init
    
    
      trap_init
    
    (void)
  
    
    
    
      831
    
     {
  
    
    
    
      
        857
      
    
    
      ???????? 
      
        set_intr_gate_ist
      
      (12, &
      
        stack_segment
      
      , 
      
        STACKFAULT_STACK
      
      );
    
  
    
    
    
      898
    
    ???????? 
    
      x86_init
    
    .
    
      irqs
    
    .
    
      trap_init
    
    (); /* 初始化該平臺x86的特定設備 */
  
    
    
    
      899
    
     }
  
    ?
  
    
    
    
      383
    
     static inline void 
    
      set_intr_gate_ist
    
    (int 
    
      n
    
    , void *
    
      addr
    
    , unsigned 
    
      ist
    
    )
  
    
    
    
      384
    
     {
  
    
    
    
      385
    
    ???????? 
    
      BUG_ON
    
    ((unsigned)
    
      n
    
     > 0xFF);
  
    
    
    
      386
    
    ???????? 
    
      _set_gate
    
    (
    
      n
    
    , 
    
      GATE_INTERRUPT
    
    , 
    
      addr
    
    , 0, 
    
      ist
    
    , 
    
      __KERNEL_CS
    
    );
  
    
    
    
      387
    
     }
  
    ?
  
    
    
    
      312
    
     static inline void 
    
      _set_gate
    
    (int 
    
      gate
    
    , unsigned 
    
      type
    
    , void *
    
      addr
    
    ,
  
    
    
    
      313
    
    ????????????????????????????? unsigned 
    
      dpl
    
    , unsigned 
    
      ist
    
    , unsigned 
    
      seg
    
    )
  
    
    
    
      314
    
     {
  
    
    
    
      315
    
    ???????? 
    
      gate_desc
    
    
      s
    
    ;
  
    
    
    
      316
    
    ???????? 
    
      pack_gate
    
    (&
    
      s
    
    , 
    
      type
    
    , (unsigned long)
    
      addr
    
    , 
    
      dpl
    
    , 
    
      ist
    
    , 
    
      seg
    
    );
  
    
    
    
      317
    
    ???????? /*
  
    
    
    
      318
    
    ????????? * does not need to be atomic because it is only done once at
  
    
    
    
      319
    
    ????????? * setup time
  
    
    
    
      320
    
    ????????? */
  
    
    
    
      321
    
    ???????? 
    
      write_idt_entry
    
    (
    
      idt_table
    
    , 
    
      gate
    
    , &
    
      s
    
    );
  
    
    
    
      322
    
     }
  
    ?
  
    
    
    
      064
    
     static inline void 
    
      pack_gate
    
    (
    
      gate_desc
    
     *
    
      gate
    
    , unsigned char 
    
      type
    
    ,
  
    
    
    
      065
    
    ????????????????????????????? unsigned long 
    
      base
    
    , unsigned 
    
      dpl
    
    , unsigned 
    
      flags
    
    ,
  
    
    
    
      066
    
    ????????????????????????????? unsigned short 
    
      seg
    
    )
  
    
    
    
      067
    
     {
  
    
    
    
      068
    
    ???????? 
    
      gate
    
    ->
    
      a
    
     = (
    
      seg
    
     << 16) | (
    
      base
    
     & 0xffff);
  
    
    
    
      069
    
    ???????? 
    
      gate
    
    ->
    
      b
    
     = (
    
      base
    
     & 0xffff0000) |
  
    
    
    
      070
    
    ?????????????????? (((0x80 | 
    
      type
    
     | (
    
      dpl
    
     << 5)) & 0xff) << 8);
  
    
    
    
      071
    
     }
  
    ?
  
    
    
    
      115
    
     static inline void 
    
      native_write_idt_entry
    
    (
    
      gate_desc
    
     *
    
      idt
    
    , int 
    
      entry
    
    ,
  
    
    
    
      116
    
    ?????????????????????????????????????????? const 
    
      gate_desc
    
     *
    
      gate
    
    )
  
    
    
    
      117
    
     {
  
    
    
    
      118
    
    ???????? 
    
      memcpy
    
    (&
    
      idt
    
    [
    
      entry
    
    ], 
    
      gate
    
    , sizeof(*
    
      gate
    
    ));
  
    
    
    
      119
    
     }
  
    ?
  
    然異常12號處理者stack_segment,何許人也?在/arch/x86/kernel/entry_32.S中定義如下:
  
    
    
    
      1014
    
     ENTRY(stack_segment)
  
    
    
    
      1015
    
    ???????? RING0_EC_FRAME
  
    
    
    
      
        1016
      
    
    
      ???????? pushl $do_stack_segment
    
  
    
    
    
      1017
    
    ???????? CFI_ADJUST_CFA_OFFSET 4
  
    
    
    
      
        1018
      
    
    
      ???????? jmp error_code
    
  
    
    
    
      1019
    
    ???????? CFI_ENDPROC
  
    
    
    
      1020
    
     END(stack_segment)
  

有的異常,例如stack_segment,系統由硬件產生錯誤碼并壓入棧中。另外一些異常,系統將不產生異常,例如overflow,為了統一中斷、產生錯誤碼的異常和不產生錯誤碼的異常的棧內存布局,故“人為地”pushl $0。

    
    
    
      958
    
     ENTRY(overflow)
  
    
    
    
      959
    
    ???????? RING0_INT_FRAME
  
    
    
    
      
        960
      
    
    
      ???????? pushl $0
    
  
    
    
    
      961
    
    ???????? CFI_ADJUST_CFA_OFFSET 4
  
    
    
    
      
        962
      
    
    
      ???????? pushl $do_overflow
    
  
    
    
    
      963
    
    ???????? CFI_ADJUST_CFA_OFFSET 4
  
    
    
    
      
        964
      
    
    
      ???????? jmp error_code
    
  
    
    
    
      965
    
    ???????? CFI_ENDPROC
  
    
    
    
      966
    
     END(overflow)
  

在error_code中,首先保存中斷寄存器上下文等,其次調用do_stack_segment進行處理,最后調用ret_from_exception進行善后工作,代碼如下:

    
    
    
      1291
    
     error_code:
  
    
    
    
      1292
    
    ???????? /* the function address is in %gs's slot on the stack */
  
    
    
    
      1293
    
    ???????? pushl %fs
  
    
    
    
      1294
    
    ???????? CFI_ADJUST_CFA_OFFSET 4
  
    
    
    
      1295
    
    ???????? /*CFI_REL_OFFSET fs, 0*/
  
    
    
    
      1296
    
    ???????? pushl %es
  
    
    
    
      1297
    
    ???????? CFI_ADJUST_CFA_OFFSET 4
  
    
    
    
      1298
    
    ???????? /*CFI_REL_OFFSET es, 0*/
  
    
    
    
      1299
    
    ???????? pushl %ds
  
    
    
    
      1300
    
    ???????? CFI_ADJUST_CFA_OFFSET 4
  
    
    
    
      1301
    
    ???????? /*CFI_REL_OFFSET ds, 0*/
  
    
    
    
      1302
    
    ???????? pushl %eax
  
    
    
    
      1303
    
    ???????? CFI_ADJUST_CFA_OFFSET 4
  
    
    
    
      1304
    
    ???????? CFI_REL_OFFSET eax, 0
  
    
    
    
      1305
    
    ???????? pushl %ebp
  
    
    
    
      1306
    
    ???????? CFI_ADJUST_CFA_OFFSET 4
  
    
    
    
      1307
    
    ???????? CFI_REL_OFFSET ebp, 0
  
    
    
    
      1308
    
    ???????? pushl %edi
  
    
    
    
      1309
    
    ???????? CFI_ADJUST_CFA_OFFSET 4
  
    
    
    
      1310
    
    ???????? CFI_REL_OFFSET edi, 0
  
    
    
    
      1311
    
    ???????? pushl %esi
  
    
    
    
      1312
    
    ???????? CFI_ADJUST_CFA_OFFSET 4
  
    
    
    
      1313
    
    ???????? CFI_REL_OFFSET esi, 0
  
    
    
    
      1314
    
    ???????? pushl %edx
  
    
    
    
      1315
    
    ???????? CFI_ADJUST_CFA_OFFSET 4
  
    
    
    
      1316
    
    ???????? CFI_REL_OFFSET edx, 0
  
    
    
    
      1317
    
    ???????? pushl %ecx
  
    
    
    
      1318
    
    ???????? CFI_ADJUST_CFA_OFFSET 4
  
    
    
    
      1319
    
    ???????? CFI_REL_OFFSET ecx, 0
  
    
    
    
      1320
    
    ???????? pushl %ebx
  
    
    
    
      1321
    
    ???????? CFI_ADJUST_CFA_OFFSET 4
  
    
    
    
      1322
    
    ???????? CFI_REL_OFFSET ebx, 0
  
    
    
    
      1323
    
    ???????? cld
  
    
    
    
      1324
    
    ???????? movl $(__KERNEL_PERCPU), %ecx
  
    
    
    
      1325
    
    ???????? movl %ecx, %fs
  
    
    
    
      1326
    
    ???????? UNWIND_ESPFIX_STACK?? /* 用于處理系統棧不是32位的情況 */
  
    
    
    
      1327
    
    ???????? GS_TO_REG %ecx??????? /* 把%gs存入%ecx中 */
  
    
    
    
      1328
    
    ???????? movl PT_GS(%esp), %edi????????? # get the function address
  
    
    
    
      1329
    
    ???????? movl PT_ORIG_EAX(%esp), %edx??? # get the error code
  
    
    
    
      
        1330
      
    
    
      ???????? movl $-1, PT_ORIG_EAX(%esp)???? # no syscall to restart
    
  
    
    
    
      1331
    
    ???????? REG_TO_PTGS %ecx????? /* 把%gs的值存入棧的%gs中,即處理代碼指針位置 */
  
    
    
    
      1332
    
    ???????? SET_KERNEL_GS %ecx
  
    
    
    
      1333
    
    ???????? movl $(__USER_DS), %ecx
  
    
    
    
      1334
    
    ???????? movl %ecx, %ds
  
    
    
    
      1335
    
    ???????? movl %ecx, %es
  
    
    
    
      1336
    
    ???????? TRACE_IRQS_OFF
  
    
    
    
      
        1337
      
    
    
      ???????? movl %esp,%eax????????????????? # pt_regs pointer
    
  
    
    
    
      1338
    
    ???????? call *%edi
  
    
    
    
      1339
    
    ???????? jmp ret_from_exception
  
    
    
    
      1340
    
    ???????? CFI_ENDPROC
  
    
    
    
      1341
    
     END(page_fault)
  

在error_code中執行在1338行call *edi之前,棧的內存布局如下圖。

clip_image008

在error_code處理“棧異常”處理時,call *edi = call do_stack_segment,do_stack_segment函數由DO_ERROR宏生成。error_code給do_stack_segment函數傳入regs參數時,由ABI規范規定,eax為第一個參數 struct pt_regs regs。pt_regs與棧內存布局相同,其結構體如下:

    
    
    
      021
    
     struct 
    
      pt_regs
    
     {
  
    
    
    
      022
    
    ???????? long 
    
      ebx
    
    ;
  
    
    
    
      023
    
    ???????? long 
    
      ecx
    
    ;
  
    
    
    
      024
    
    ???????? long 
    
      edx
    
    ;
  
    
    
    
      025
    
    ???????? long 
    
      esi
    
    ;
  
    
    
    
      026
    
    ???????? long 
    
      edi
    
    ;
  
    
    
    
      027
    
    ???????? long 
    
      ebp
    
    ;
  
    
    
    
      028
    
    ???????? long 
    
      eax
    
    ;
  
    
      029
    
    ???????? int? 
    
      xds
    
    ;
  
    
      030
    
    ???????? int? 
    
      xes
    
    ;
  
    
      031
    
    ???????? int? 
    
      xfs
    
    ;
  
    
      032
    
    ???????? int? 
    
      xgs
    
    ;???? /* 對應%gs,對于異常而言是處理器代碼 */
  
    
      033
    
    ???????? long 
    
      orig_eax
    
    ; /* 對應于內存布局中的error code或vector */
  
    
    
    
      034
    
    ???????? long 
    
      eip
    
    ;
  
    
    
    
      035
    
    ???????? int? 
    
      xcs
    
    ;
  
    
    
    
      036
    
    ???????? long 
    
      eflags
    
    ;
  
    
    
    
      037
    
    ???????? long 
    
      esp
    
    ;
  
    
    
    
      038
    
    ???????? int? 
    
      xss
    
    ;
  
    
    
    
      039
    
     };
  

stack_segment函數是“ 棧異常 ”的異常處理函數,由 DO_ERROR宏 產生:

    
    
    
      215
    
     #ifdef CONFIG_X86_32
  
    
    
    
      216
    
    
      DO_ERROR
    
    (12, 
    
      SIGBUS
    
    , "stack segment", 
    
      stack_segment
    
    )
  
    
    
    
      217
    
     #endif
  
    
    
    
      183
    
     #define 
    
      DO_ERROR
    
    (
    
      trapnr
    
    , 
    
      signr
    
    , 
    
      str
    
    , 
    
      name
    
    )????????????????????????????? \
  
    
    
    
      184
    
    
      dotraplinkage
    
     void 
    
      do_
    
    ##name(struct 
    
      pt_regs
    
     *
    
      regs
    
    , long 
    
      error_code
    
    )???? \
  
    
    
    
      185
    
     {?????????????????????????????????????????????????????????????????????? \
  
    
    
    
      186
    
    ???????? if (
    
      notify_die
    
    (
    
      DIE_TRAP
    
    , 
    
      str
    
    , 
    
      regs
    
    , 
    
      error_code
    
    , 
    
      trapnr
    
    , 
    
      signr
    
    )? \
  
    
    
    
      187
    
    ???????????????????????????????????????????????????????? == 
    
      NOTIFY_STOP
    
    ) \
  
    
    
    
      188
    
    ???????????????? return;???????????????????????????????????????????????? \
  
    
    
    
      189
    
    ???????? 
    
      conditional_sti
    
    (
    
      regs
    
    );????????????????????????????????????????? \
  
    
    
    
      
        190
      
    
    
      ???????? 
      
        do_trap
      
      (
      
        trapnr
      
      , 
      
        signr
      
      , 
      
        str
      
      , 
      
        regs
      
      , 
      
        error_code
      
      , 
      
        NULL
      
      );??????????? \
    
  
    
    
    
      191
    
     }
  

可見,do_stack_segment最后調用do_trap進行異常處理。

2 )中斷描述符表中斷部分最終初始化

由start_kernel()調用init_IRQ(void)進行中斷最終初始化。其操作流程如下:

clip_image010

1) 將CPU0 IRQ0…IRQ15的vectors設置為0…15。對于CPU0的vecotr_irq而言,若其IRQ是PIC中斷,則其vector早已由PIC固定,所以設置其IRQ0…IRQ15的vector為0…15。若是I/O APIC,由于其vector分配是可由OS動態設置,所以vector 0…15可能會被重新分配;

2) 本質上調用native_init_IRQ()函數對irq_desc、以及中斷部分門描述符進行初始化,并針對CONFIG_4KSTACKS配置、協處理器模擬浮點運算等進行配置;

a、 調用init_ISA_irqs()初始化8259A可斷控制器,并對相應中斷請求線IRQ進行初始化、使其對應中斷控制器irq_desc的操作函數為8259A操作接口函數;

b、 調用apic_intr_init()函數針對采取I/O APIC中斷處理器的情況,對APIC中斷處理器進行初始化工作;

c、 將調用set_intr_gate為系統中每根中斷請求線IRQ地應的中斷向量號設置了相應的中斷門描述門, 其中斷處理函數定義在interrupt數組中

d、 在PC平臺下set_irq(2, &rq2)對從8259A中斷控制器的第三根IRQ請求做特殊處理;

e、 irq_ctx_init函數用于在配置CONFIG_4KSTACK的情況下配置當前CPU的中斷棧相關項。LINUX內核在未配置CONFIG_4KSTACK時,共享所中斷進程的內核棧,內核棧為兩頁,即8K。在2.6版本時,增加了CONFIG_4KSTACK選項,將棧大小從兩頁減小至一頁,為了應對棧的減少,故中斷處理程序擁有自己的中斷處理程序線,為原先共享棧的一半,即4K,每個CPU擁有一個中斷棧。

    
    
    
      125
    
     void 
    
      __init
    
    
      init_IRQ
    
    (void)
  
    
    
    
      126
    
     {
  
    
    
    
      127
    
    ???????? int 
    
      i
    
    ;
  
    
    
    
      128
    
  
    
    
    
      129
    
    ???????? /*
  
    
    
    
      130
    
    ????????? * On cpu 0, Assign IRQ0_VECTOR..IRQ15_VECTOR's to IRQ 0..15.
  
    
    
    
      131
    
    ????????? * If these IRQ's are handled by legacy interrupt-controllers like PIC,
  
    
    
    
      132
    
    ????????? * then this configuration will likely be static after the boot. If
  
    
    
    
      133
    
    ????????? * these IRQ's are handled by more mordern controllers like IO-APIC,
  
    
    
    
      134
    
    ????????? * then this vector space can be freed and re-used dynamically as the
  
    
    
    
      135
    
    ????????? * irq's migrate etc.
  
    
    
    
      136
    
    ????????? */
  
    
    
    
      137
    
    ???????? for (
    
      i
    
     = 0; 
    
      i
    
     < 
    
      legacy_pic
    
    ->
    
      nr_legacy_irqs
    
    ; 
    
      i
    
    ++)
  
    
    
    
      138
    
    ???????????????? 
    
      per_cpu
    
    (vector_irq, 0)[
    
      IRQ0_VECTOR
    
     + 
    
      i
    
    ] = 
    
      i
    
    ;
  
    
      ?
    
  
    /* intr_init()本質上調用native_init_IRQ()初始化。用x86_init.irqs.intr_init()函數對irq_desc、以及中斷部分門描述符進行初始化。x86_init是
    
      x86_init_ops
    
    類型,集成針對x86特定平臺的各種初始化工作,包含初始化PCI總線、中斷、異常等,其中irqs就是針對中斷進行初始化操作。*/
  
    
      140
    
    ???????? 
    
      x86_init
    
    .
    
      irqs
    
    .
    
      intr_init
    
    ();
  
    
    
    
      141
    
     }
  

/arch/x86/include/asm/x86_init.h

    
      115
    
     /**
  
    
      116
    
    ? * struct x86_init_ops - functions for platform specific setup
  
    
      117
    
    ? *
  
    
      118
    
    ? */
  
    
      119
    
     struct 
    
      x86_init_ops
    
     {
  
    
    
    
      122
    
    ???????? struct 
    
      x86_init_irqs
    
    ??????????? 
    
      irqs
    
    ;
  
    
    
    
      124
    
    ???????? struct 
    
      x86_init_paging
    
    ????????? 
    
      paging
    
    ;
  
    
      125
    
    ???????? struct 
    
      x86_init_timers
    
    ????????? 
    
      timers
    
    ;
  
    
      127
    
    ???????? struct 
    
      x86_init_pci
    
    ???????????? 
    
      pci
    
    ;
  
    
      128
    
     };
  
    ?
  
    
    
    
      047
    
     /**
  
    
    
    
      048
    
    ? * struct x86_init_irqs - platform specific interrupt setup
  
    
    
    
      049
    
    ? * @pre_vector_init:??????????? init code to run before interrupt vectors
  
    
    
    
      050
    
    ? *????????????????????????????? are set up.
  
    
      051
    
    ? * @intr_init:????????????????? interrupt init code
  
    
      052
    
    ? * @trap_init:????????????????? platform specific trap setup
  
    
      053
    
    ? */
  
    
      054
    
     struct 
    
      x86_init_irqs
    
     {
  
    
      055
    
    ???????? void (*
    
      pre_vector_init
    
    )(void);
  
    
    
    
      056
    
    ???????? void (*
    
      intr_init
    
    )(void);
  
    
    
    
      057
    
    ???????? void (*
    
      trap_init
    
    )(void);
  
    
    
    
      058
    
     };
  

/arch/x86/kernel/x86_init.c

    
    
    
      029
    
     /*
  
    
    
    
      030
    
    ? * The platform setup functions are preset with the default functions
  
    
    
    
      031
    
    ? * for standard PC hardware.
  
    
    
    
      032
    
    ? */
  
    
    
    
      033
    
     struct 
    
      x86_init_ops
    
    
      x86_init
    
    
      __initdata
    
     = {
  
    
    
    
      051
    
    ???????? .
    
      irqs
    
     = {
  
    
    
    
      052
    
    ???????????????? .
    
      pre_vector_init
    
    ??????? = 
    
      init_ISA_irqs
    
    ,
  
    
    
    
      053
    
    ???????????????? .
    
      intr_init
    
    ????????????? = 
    
      native_init_IRQ
    
    ,
  
    
    
    
      054
    
    ???????????????? .
    
      trap_init
    
    ????????????? = 
    
      x86_init_noop
    
    ,
  
    
    
    
      055
    
    ???????? },
  
    
    
    
      082
    
     };
  
    
    
    
      235
    
     void 
    
      __init
    
    
      native_init_IRQ
    
    (void)
  
    
    
    
      236
    
     {
  
    
    
    
      237
    
    ???????? int 
    
      i
    
    ;
  
    
    
    
      238
    
  
    
    
    
      239
    
    ???????? /* Execute any quirks before the call gates are initialised: */
  
    /* 調用init_ISA_irqs()初始化irq_desc[0…NR_IRQS] = {.status = IRQ_DISABLED, 
  
    .action = NULLL, .depth = 1}。并且對于irq0 … 15,
  
    將其handler = &i8259A_irq_type*/
  
    
    
    
      240
    
    ???????? 
    
      x86_init
    
    .
    
      irqs
    
    .
    
      pre_vector_init
    
    ();
  
    
    
    
      241
    
  
    
    
    
      242
    
    ???????? 
    
      apic_intr_init
    
    ();
  
    
    
    
      243
    
  
    
    
    
      244
    
    ???????? /*
  
    
    
    
      245
    
    ????????? * Cover the whole vector space, no vector can escape
  
    
    
    
      246
    
    ????????? * us. (some of these will be overridden and become
  
    
    
    
      247
    
    ????????? * 'special' SMP interrupts)
  
    
    
    
      248
    
    ????????? */
  
    
      /* 
    
    調用set_intr_gate()為每根IRQ對應的中斷向量號設置了相應的中斷門描述符 */
  
    
      249
    
    ???????? for (
    
      i
    
     = 
    
      FIRST_EXTERNAL_VECTOR
    
    ; 
    
      i
    
     < 
    
      NR_VECTORS
    
    ; 
    
      i
    
    ++) {
  
    
    
    
      250
    
    ???????????????? /* IA32_SYSCALL_VECTOR could be used in trap_init already. */
  
    
    
    
      251
    
    ???????????????? if (!
    
      test_bit
    
    (
    
      i
    
    , 
    
      used_vectors
    
    ))
  
    
    
    
      252
    
    ???????????????????????? 
    
      set_intr_gate
    
    (
    
      i
    
    , 
    
      interrupt
    
    [
    
      i
    
    -
    
      FIRST_EXTERNAL_VECTOR
    
    ]);
  
    
    
    
      253
    
    ???????? }
  
    /* 系統若非I/O APIC,則IRQ3用于8259A從片的級聯,故進行特殊處理 */
  
    
    
    
      255
    
    ???????? if (!
    
      acpi_ioapic
    
    )
  
    
    
    
      256
    
    ???????????????? 
    
      setup_irq
    
    (2, &
    
      irq2
    
    );
  
    
    
    
      257
    
  
    
    
    
      258
    
     #ifdef CONFIG_X86_32
  
    
    
    
      259
    
    ???????? /*
  
    
    
    
      260
    
    ????????? * External FPU? Set up irq13 if so, for
  
    
    
    
      261
    
    ????????? * original braindamaged IBM FERR coupling.
  
    
    
    
      262
    
    ????????? */
  
    
      /* 
    
    在處理器集成協處理器,而該協處理器沒有浮點處理單元的情況下設置針對浮點計算異常的處理函數fpu_irq,該函數用軟件模擬的方法來進行浮點數運算 */
  
    
      263
    
    ???????? if (
    
      boot_cpu_data
    
    .
    
      hard_math
    
     && !
    
      cpu_has_fpu
    
    )
  
    
    
    
      264
    
    ???????????????? 
    
      setup_irq
    
    (
    
      FPU_IRQ
    
    , &
    
      fpu_irq
    
    );
  
    /* 在內核選中CONFIG_4KSTACKS時,為系統中每一CPU設置中斷、異常處理函數所需的內核態棧;在沒有選中該選項的情況下,irq_ctx_init是個空語句 */
  
    
    
    
      266
    
    ???????? 
    
      irq_ctx_init
    
    (
    
      smp_processor_id
    
    ());
  
    
    
    
      267
    
     #endif
  
    
    
    
      268
    
     }
  
    
    
    
      101
    
     void 
    
      __init
    
    
      init_ISA_irqs
    
    (void)
  
    
    
    
      102
    
     {
  
    
    
    
      103
    
    ???????? int 
    
      i
    
    ;
  
    
      /* 
    
    若存在LOCAL_APIC,則對Local APIC模塊進行設置 */
  
    
      105
    
     #if 
    
      defined
    
    (CONFIG_X86_64) || 
    
      defined
    
    (CONFIG_X86_LOCAL_APIC)
  
    
    
    
      106
    
    ???????? 
    
      init_bsp_APIC
    
    ();
  
    
    
    
      107
    
     #endif
  
    
      /* 
    
    初始化中斷控制器8259A */
  
    
      108
    
    ???????? 
    
      legacy_pic
    
    ->
    
      init
    
    (0);
  
    
    
    
      109
    
  
    
    
    
      110
    
    ???????? /*
  
    
    
    
      111
    
    ????????? * 16 old-style INTA-cycle interrupts:
  
    
    
    
      112
    
    ????????? */
  
    
      /* 
    
    系統中斷老式處理方式是由兩片8259A級聯而成,每片可有8個IRQ,故兩片最多初始化16個IRQ,但由于IRQ 2用于級聯,故實際僅有15個有效IRQ。 */
  
    
      113
    
    ???????? for (
    
      i
    
     = 0; 
    
      i
    
     < 
    
      legacy_pic
    
    ->
    
      nr_legacy_irqs
    
    ; 
    
      i
    
    ++) {
  
    
    
    
      114
    
    ???????????????? struct 
    
      irq_desc
    
     *
    
      desc
    
     = 
    
      irq_to_desc
    
    (
    
      i
    
    );
  
    
      115
    
  
    
      116
    
    ???????????????? 
    
      desc
    
    ->
    
      status
    
     = 
    
      IRQ_DISABLED
    
    ;
  
    
      117
    
    ???????????????? 
    
      desc
    
    ->
    
      action
    
     = 
    
      NULL
    
    ;
  
    
      118
    
    ???????????????? 
    
      desc
    
    ->
    
      depth
    
     = 1;
  
    
      119
    
  
    
    
    
      120
    
    ???????????????? 
    
      set_irq_chip_and_handler_name
    
    (
    
      i
    
    , &
    
      i8259A_chip
    
    ,
  
    
    
    
      121
    
    ?????????????????????????????????????????????? 
    
      handle_level_irq
    
    , "XT");
  
    
      122
    
    ???????? }
  
    
    
    
      123
    
     }
  

interrupt 數組

interrupt數組符號由匯編代碼定義,其源碼如下:

/arch/x86/kernel/entry_32.S

    
    
    
      819
    
     /*
  
    
    
    
      820
    
    ? * Build the entry stubs and pointer table with some assembler magic.
  
    
    
    
      821
    
    ? * We pack 7 stubs into a single 32-byte chunk, which will fit in a
  
    
    
    
      822
    
    ? * single cache line on all modern x86 implementations.
  
    
    
    
      823
    
    ? */
  
    
    
    
      824
    
     .section .init.rodata,"a"
  
    
    
    
      825
    
     ENTRY(interrupt)
  
    
    
    
      826
    
     .text
  
    
    
    
      827
    
    ???????? .p2align 5
  
    
    
    
      828
    
    ???????? .p2align CONFIG_X86_L1_CACHE_SHIFT
  
    
    
    
      829
    
     ENTRY(irq_entries_start)
  
    
      830
    
    ???????? RING0_INT_FRAME
  
    
      831
    
     vector=FIRST_EXTERNAL_VECTOR /* =0x20,0~31號內部中斷 */
  
    
    
    
      
        832
      
    
    
       .rept (NR_VECTORS-FIRST_EXTERNAL_VECTOR+6)/7
    
  
    
    
    
      833
    
    ???????? .balign 32 /* 32字節對齊 */
  
    
    
    
      
        834
      
    
    
      ?? .rept 7
    
  
    
    
    
      835
    
    ???? .if vector < NR_VECTORS
  
    
    
    
      836
    
    ?????? .if vector <> FIRST_EXTERNAL_VECTOR
  
    /* 按照CFA規則修改前一個offset,以達4字節對齊。CFA標準(Call Frame Information), help a debugger create a reliable backtrace through functions. */
  
    
    
    
      837
    
    ???????? CFI_ADJUST_CFA_OFFSET -4 
  
    
    
    
      838
    
    ?????? .endif
  
    
    
    
      839
    
     1:????? pushl $(~vector+0x80)?? /* Note: always in signed byte range ,[-256 ~ -1] */
    
    
  
    
      840
    
    ???????? CFI_ADJUST_CFA_OFFSET 4???????? /* 按CFA規則4字節對齊 */
  
    
    
    
      841
    
    ?????? .if ((vector-FIRST_EXTERNAL_VECTOR)%7) <> 6
  
    
    
    
      842
    
    ???????? jmp 2f
  
    
    
    
      843
    
    ?????? .endif
  
    
    
    
      844
    
    ?????? .previous
  
    
    
    
      845
    
    ???????? .long 1b
  
    
    
    
      846
    
    ?????? .text
  
    
    
    
      847
    
     vector=vector+1
  
    
    
    
      848
    
    ???? .endif
  
    
    
    
      
        849
      
    
    
      ?? .endr /* end of rep 7 */
    
  
    
    
    
      850
    
     2:????? jmp common_interrupt
  
    
    
    
      
        851
      
    
    
       .endr /* end of rep (NR_VECTORS – FIRST_...) */
    
  
    
    
    
      852
    
     END(irq_entries_start)
  
    
    
    
      853
    
  
    
    
    
      854
    
     .previous
  
    
    
    
      855
    
     END(interrupt)
  

ENTRY(interrupt)通過偽指令.rept、.endr,將在代碼段產生(NR_VECTORS - FIRST_EXTERNAL_VECTOR)個跳轉到common_interrupt的匯編代碼片段,起始地址是irq_entries_start;在數據段產生一個中斷數組的符號interrupt,用于記錄產生代碼段中每個中斷向量處理的匯編代碼片段地址,在C語言中將interrupt符號作為中斷數組變量導入:

    
      132
    
     extern void (*
    
      __initconst
    
    
      interrupt
    
    [
    
      NR_VECTORS
    
    -
    
      FIRST_EXTERNAL_VECTOR
    
    ])(void);
  

ENTRY(interrupt)編譯之后,所生成的代碼段和數據段內存布局如下:

clip_image012

ENTRIY(interrupt)匯編代碼段主要由兩個rept構成,外層rept循環(NR_VECTORS-FIRST_EXTERNAL_VECTOR+6)/7次,而每次內層rept循環7次,內層循環所產生的代碼以32字節對齊,內層rept循環產生的代碼如上圖irq_entries_start中以粗黑方框表示。

以兩層rept循環生成jmp common_interrupt的目的在于:

在內循環內,前6次循環產生的代碼指令為:push和short jmp,而第7次產生的代碼指令為:push和long jmp。push占2字節,short jmp占2字節,long jmp占5字節,故采取此種方式內層rept循環7次產生的代碼大小為:6 * (2 + 2) + 2 + 5 = 31 字節。而外層循環以32字節對齊,相比于老版本每次都為push和long jmp而言,所以利用short jmp節省了內存開銷。參見: http://didat.sprg.uniroma2.it/la/docs/la12-10.pdf

每個中斷門描述符在將vector壓入后都跳轉到common_interrupt進行處理。common_interrupt在保存中斷現場之后,跳轉到do_IRQ進行中斷函數處理,最后調用iret_from_intr進行中斷返回、恢復中斷上下文。

    
    
    
      863
    
     common_interrupt:
  
    
    
    
      864
    
    ???????? addl $-0x80,(%esp)????? /* Adjust vector into the [-256,-1] range */
  
    
    
    
      865
    
    ???????? SAVE_ALL /* 宏定義,負責完成宏定義中斷現場的保護工作 */
  
    
    
    
      866
    
    ???????? TRACE_IRQS_OFF
  
    
    
    
      867
    
    ???????? movl %esp,%eax
  
    
    
    
      
        868
      
    
    
      ???????? call do_IRQ
    
  
    
    
    
      
        869
      
    
    
      ???????? jmp ret_from_intr
    
  
    
    
    
      870
    
     ENDPROC(common_interrupt)
  
    
    
    
      195
    
     .macro SAVE_ALL
  
    
    
    
      196
    
    ???????? cld? /* 清除系統標志寄存器EFLAGS中的方向標志位DF,使%si、%di寄存器的值在每次字符串指令操作后自動+1,使自字符串從低地址到高地址方向處理 */
  
    
    
    
      197
    
    ???????? PUSH_GS
  
    
    
    
      198
    
    ???????? pushl %fs
  
    
    
    
      199
    
    ???????? CFI_ADJUST_CFA_OFFSET 4
  
    
    
    
      200
    
    ???????? /*CFI_REL_OFFSET fs, 0;*/
  
    
    
    
      201
    
    ???????? pushl %es
  
    
    
    
      202
    
    ???????? CFI_ADJUST_CFA_OFFSET 4
  
    
    
    
      203
    
    ???????? /*CFI_REL_OFFSET es, 0;*/
  
    
    
    
      204
    
    ???????? pushl %ds
  
    
    
    
      205
    
    ???????? CFI_ADJUST_CFA_OFFSET 4
  
    
    
    
      206
    
    ???????? /*CFI_REL_OFFSET ds, 0;*/
  
    
    
    
      207
    
    ???????? pushl %eax
  
    
    
    
      208
    
    ???????? CFI_ADJUST_CFA_OFFSET 4
  
    
    
    
      209
    
    ???????? CFI_REL_OFFSET eax, 0
  
    
    
    
      210
    
    ???????? pushl %ebp
  
    
    
    
      211
    
    ???????? CFI_ADJUST_CFA_OFFSET 4
  
    
    
    
      212
    
    ???????? CFI_REL_OFFSET ebp, 0
  
    
    
    
      213
    
    ???????? pushl %edi
  
    
    
    
      214
    
    ???????? CFI_ADJUST_CFA_OFFSET 4
  
    
      215
    
    ???????? CFI_REL_OFFSET edi, 0
  
    
      216
    
    ???????? pushl %esi
  
    
      217
    
    ???????? CFI_ADJUST_CFA_OFFSET 4
  
    
    
    
      218
    
    ???????? CFI_REL_OFFSET esi, 0
  
    
    
    
      219
    
    ???????? pushl %edx
  
    
    
    
      220
    
    ???????? CFI_ADJUST_CFA_OFFSET 4
  
    
    
    
      221
    
    ???????? CFI_REL_OFFSET edx, 0
  
    
    
    
      222
    
    ???????? pushl %ecx
  
    
    
    
      223
    
    ???????? CFI_ADJUST_CFA_OFFSET 4
  
    
    
    
      224
    
    ???????? CFI_REL_OFFSET ecx, 0
  
    
    
    
      225
    
    ???????? pushl %ebx
  
    
    
    
      226
    
    ???????? CFI_ADJUST_CFA_OFFSET 4
  
    
    
    
      227
    
    ???????? CFI_REL_OFFSET ebx, 0
  
    
    
    
      228
    
    ???????? movl $(__USER_DS), %edx
  
    
    
    
      229
    
    ???????? movl %edx, %ds
  
    
    
    
      230
    
    ???????? movl %edx, %es
  
    
    
    
      231
    
    ???????? movl $(__KERNEL_PERCPU), %edx
  
    
    
    
      232
    
    ???????? movl %edx, %fs
  
    
    
    
      233
    
    ???????? SET_KERNEL_GS %edx
  
    
    
    
      234
    
     .endm
  

common_interrupt在調用 do_IRQ 之前中斷棧內存布局如下:

clip_image014

LINUX中斷描述符初始化


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 国产欧美日韩精品a在线观看高清 | 香港之夜免费观看 | 亚洲狠狠婷婷综合久久久图片 | 女人十八毛片免费观 | 中文字幕在线观看一区 | 日日夜夜免费精品 | 成人短视频在线观看 | 欧美激情精品久久久久 | 久久日韩精品激情 | 免费看曰批女人爽的视频网址 | 日韩免费看 | 免费视频一级片 | 国产午夜精品一区二区三区嫩草 | 99久久国内精品成人免费 | 爆操白虎 | 日韩国产成人精品视频人 | 亚洲福利一区二区 | 精品国产免费第一区二区三区日韩 | 欧美在线精品一区二区三区 | 亚洲精品日韩一区二区 | 国产欧美日韩中文字幕 | 欧美深度肠交 | 在线精品视频成人网 | 久青草视频免费视频播放线路1 | 伊人丁香狠狠色综合久久 | 欧洲一级 | 亚洲欧美日韩在线一区 | 老司机午夜精品视频你懂的 | 国产一区二区免费播放 | 精品久久久久久久久久久久久久久 | 久久综合九九 | 青青热久免费精品视频精品 | 四虎影视网址 | 亚洲精品123区在线观看 | 午夜一级在线 | 亚洲91在线 | 久免费视频| 性做久久久久久免费观看 | 久久久一本 | 在线日韩一区 | 国内精品久久久久影院蜜芽 |