LINUX中斷描述符初始化
@CopyLeft by ICANTH , I Can do ANy THing that I CAN THink ! ~
Author : WenHui , WuHan University , 2012-6-4
硬件產(chǎn)生中斷之后,需要通過門描述符來尋找中斷的處理程序入口。門描述符和段描述符一樣,8個字節(jié)。門描述符大體分為:段偏移、段選擇子以及DPL。段選擇子用于在GDT中尋找到門段基址,DPL用于控制當(dāng)前進(jìn)程中斷或異常訪問權(quán)限。當(dāng)發(fā)生中斷時,將門描述符所指向的段基地放入%cs,將段偏移放入%eip,轉(zhuǎn)入相應(yīng)服務(wù)。
門描述符結(jié)構(gòu)如下:
任務(wù)門描述符 :用于在發(fā)生中斷時調(diào)度相應(yīng)進(jìn)程
中斷門描述符 :描述中斷處理程序所在段選擇子和段內(nèi)偏移值。據(jù)此修改EIP及關(guān)閉中斷Eflags的IT標(biāo)識位
陷阱門描述符 :與中斷門描述符一樣,但不關(guān)中斷
中斷描述符表IDT( Interrupt Descriptor Table )用于存放256個門描述符,對應(yīng)256個中斷向量。其中IA32規(guī)定前0~31號固定向量用于異常。寄存器 idtr 為門描述符表IDT的物理地址。根據(jù)向量尋找基對應(yīng)門描述符,并將中斷或異常程序加載過程下圖所示:
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)設(shè)置指向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個門描述符。對于每個門描述符,段選擇子都指向內(nèi)核段,段偏移都指向igore_int函數(shù),該函數(shù)只打印一句話:“哥們,別急,俺還沒被真正初始化勒!~”。
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
中斷描述符表最終初始化
中斷描述符表最終初始化分為兩部分:異常和中斷,分別在函數(shù)trap_init(void)和init_IRQ(void)中實現(xiàn),都由系統(tǒng)初始化入口函數(shù)start_kernel()所調(diào)用。對于特定異常,其處理異常函數(shù)預(yù)先已經(jīng)設(shè)定好;但對于特定異步中斷,由于需要捕獲設(shè)備I/O中斷的程序數(shù)目不定,所以得采用特定數(shù)據(jù)結(jié)構(gòu)來對irq及其action進(jìn)行描述。
trap_init: http://os1a.cs.columbia.edu/lxr/source/arch/x86/kernel/traps.c#830
do_IRQ:
1 )異常部分最終初始化
由于IA32規(guī)定異常所對應(yīng)的固定向量,所以直接調(diào)用set_trap_gate()、set_intr_gate()、set_system_gate()、set_system_intr_gate()、set_task_gate()、set_intr_gate_ist()設(shè)置異常處理函數(shù)、初始相應(yīng)異常。而這些函數(shù),都只是_set_gate宏的封裝而已。異常處理函數(shù)由宏定義DO_ERROR生成。其調(diào)用關(guān)系如下:
在trap_init(void)函數(shù)中,調(diào)用在set_intr_gate_ist()設(shè)置“stack exception”的12號中斷門描述符。set_intr_gate_ist是設(shè)置中斷描述符函數(shù)_set_gate的一層封裝,給_set_gate傳入異常處理函數(shù)stack_segment作為門描述符的偏移地址,傳入 __KERNEL_CS 作為段選擇子,傳入GATE_INTERRUPT表示中斷門描述符類型,傳入DPL = 0表示只能由內(nèi)核態(tài)訪問、而不能由用戶調(diào)用。
_set_gate函數(shù)中,其調(diào)用pack_gate()組裝成一個門描述符格式,并調(diào)用write_idt_entry寫入IDT表中相應(yīng)描述符。stack_segment函數(shù)壓入do_stack_segment函數(shù)地址并跳轉(zhuǎn)到error_code匯編代碼進(jìn)行處理。具體代碼(/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的特定設(shè)備 */
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,系統(tǒng)由硬件產(chǎn)生錯誤碼并壓入棧中。另外一些異常,系統(tǒng)將不產(chǎn)生異常,例如overflow,為了統(tǒng)一中斷、產(chǎn)生錯誤碼的異常和不產(chǎn)生錯誤碼的異常的棧內(nèi)存布局,故“人為地”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中,首先保存中斷寄存器上下文等,其次調(diào)用do_stack_segment進(jìn)行處理,最后調(diào)用ret_from_exception進(jìn)行善后工作,代碼如下:
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?? /* 用于處理系統(tǒng)棧不是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中執(zhí)行在1338行call *edi之前,棧的內(nèi)存布局如下圖。
在error_code處理“棧異常”處理時,call *edi = call do_stack_segment,do_stack_segment函數(shù)由DO_ERROR宏生成。error_code給do_stack_segment函數(shù)傳入regs參數(shù)時,由ABI規(guī)范規(guī)定,eax為第一個參數(shù) struct pt_regs regs。pt_regs與棧內(nèi)存布局相同,其結(jié)構(gòu)體如下:
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 ;???? /* 對應(yīng)%gs,對于異常而言是處理器代碼 */
033 ???????? long orig_eax ; /* 對應(yīng)于內(nèi)存布局中的error code或vector */
034 ???????? long eip ;
035 ???????? int? xcs ;
036 ???????? long eflags ;
037 ???????? long esp ;
038 ???????? int? xss ;
039 };
stack_segment函數(shù)是“ 棧異常 ”的異常處理函數(shù),由 DO_ERROR宏 產(chǎn)生:
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最后調(diào)用do_trap進(jìn)行異常處理。
2 )中斷描述符表中斷部分最終初始化
由start_kernel()調(diào)用init_IRQ(void)進(jìn)行中斷最終初始化。其操作流程如下:
1) 將CPU0 IRQ0…IRQ15的vectors設(shè)置為0…15。對于CPU0的vecotr_irq而言,若其IRQ是PIC中斷,則其vector早已由PIC固定,所以設(shè)置其IRQ0…IRQ15的vector為0…15。若是I/O APIC,由于其vector分配是可由OS動態(tài)設(shè)置,所以vector 0…15可能會被重新分配;
2) 本質(zhì)上調(diào)用native_init_IRQ()函數(shù)對irq_desc、以及中斷部分門描述符進(jìn)行初始化,并針對CONFIG_4KSTACKS配置、協(xié)處理器模擬浮點(diǎn)運(yùn)算等進(jìn)行配置;
a、 調(diào)用init_ISA_irqs()初始化8259A可斷控制器,并對相應(yīng)中斷請求線IRQ進(jìn)行初始化、使其對應(yīng)中斷控制器irq_desc的操作函數(shù)為8259A操作接口函數(shù);
b、 調(diào)用apic_intr_init()函數(shù)針對采取I/O APIC中斷處理器的情況,對APIC中斷處理器進(jìn)行初始化工作;
c、 將調(diào)用set_intr_gate為系統(tǒng)中每根中斷請求線IRQ地應(yīng)的中斷向量號設(shè)置了相應(yīng)的中斷門描述門, 其中斷處理函數(shù)定義在interrupt數(shù)組中 ;
d、 在PC平臺下set_irq(2, &rq2)對從8259A中斷控制器的第三根IRQ請求做特殊處理;
e、 irq_ctx_init函數(shù)用于在配置CONFIG_4KSTACK的情況下配置當(dāng)前CPU的中斷棧相關(guān)項。LINUX內(nèi)核在未配置CONFIG_4KSTACK時,共享所中斷進(jìn)程的內(nèi)核棧,內(nèi)核棧為兩頁,即8K。在2.6版本時,增加了CONFIG_4KSTACK選項,將棧大小從兩頁減小至一頁,為了應(yīng)對棧的減少,故中斷處理程序擁有自己的中斷處理程序線,為原先共享棧的一半,即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()本質(zhì)上調(diào)用native_init_IRQ()初始化。用x86_init.irqs.intr_init()函數(shù)對irq_desc、以及中斷部分門描述符進(jìn)行初始化。x86_init是 x86_init_ops 類型,集成針對x86特定平臺的各種初始化工作,包含初始化PCI總線、中斷、異常等,其中irqs就是針對中斷進(jìn)行初始化操作。*/
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: */
/* 調(diào)用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 ????????? */
/* 調(diào)用set_intr_gate()為每根IRQ對應(yīng)的中斷向量號設(shè)置了相應(yīng)的中斷門描述符 */
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 ???????? }
/* 系統(tǒng)若非I/O APIC,則IRQ3用于8259A從片的級聯(lián),故進(jìn)行特殊處理 */
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 ????????? */
/* 在處理器集成協(xié)處理器,而該協(xié)處理器沒有浮點(diǎn)處理單元的情況下設(shè)置針對浮點(diǎn)計算異常的處理函數(shù)fpu_irq,該函數(shù)用軟件模擬的方法來進(jìn)行浮點(diǎn)數(shù)運(yùn)算 */
263 ???????? if ( boot_cpu_data . hard_math && ! cpu_has_fpu )
264 ???????????????? setup_irq ( FPU_IRQ , & fpu_irq );
/* 在內(nèi)核選中CONFIG_4KSTACKS時,為系統(tǒng)中每一CPU設(shè)置中斷、異常處理函數(shù)所需的內(nèi)核態(tài)棧;在沒有選中該選項的情況下,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模塊進(jìn)行設(shè)置 */
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 ????????? */
/* 系統(tǒng)中斷老式處理方式是由兩片8259A級聯(lián)而成,每片可有8個IRQ,故兩片最多初始化16個IRQ,但由于IRQ 2用于級聯(lián),故實際僅有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 數(shù)組
interrupt數(shù)組符號由匯編代碼定義,其源碼如下:
/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號內(nèi)部中斷 */
832 .rept (NR_VECTORS-FIRST_EXTERNAL_VECTOR+6)/7
833 ???????? .balign 32 /* 32字節(jié)對齊 */
834 ?? .rept 7
835 ???? .if vector < NR_VECTORS
836 ?????? .if vector <> FIRST_EXTERNAL_VECTOR
/* 按照CFA規(guī)則修改前一個offset,以達(dá)4字節(jié)對齊。CFA標(biāo)準(zhǔn)(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規(guī)則4字節(jié)對齊 */
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,將在代碼段產(chǎn)生(NR_VECTORS - FIRST_EXTERNAL_VECTOR)個跳轉(zhuǎn)到common_interrupt的匯編代碼片段,起始地址是irq_entries_start;在數(shù)據(jù)段產(chǎn)生一個中斷數(shù)組的符號interrupt,用于記錄產(chǎn)生代碼段中每個中斷向量處理的匯編代碼片段地址,在C語言中將interrupt符號作為中斷數(shù)組變量導(dǎo)入:
132 extern void (* __initconst interrupt [ NR_VECTORS - FIRST_EXTERNAL_VECTOR ])(void);
ENTRY(interrupt)編譯之后,所生成的代碼段和數(shù)據(jù)段內(nèi)存布局如下:
ENTRIY(interrupt)匯編代碼段主要由兩個rept構(gòu)成,外層rept循環(huán)(NR_VECTORS-FIRST_EXTERNAL_VECTOR+6)/7次,而每次內(nèi)層rept循環(huán)7次,內(nèi)層循環(huán)所產(chǎn)生的代碼以32字節(jié)對齊,內(nèi)層rept循環(huán)產(chǎn)生的代碼如上圖irq_entries_start中以粗黑方框表示。
以兩層rept循環(huán)生成jmp common_interrupt的目的在于:
在內(nèi)循環(huán)內(nèi),前6次循環(huán)產(chǎn)生的代碼指令為:push和short jmp,而第7次產(chǎn)生的代碼指令為:push和long jmp。push占2字節(jié),short jmp占2字節(jié),long jmp占5字節(jié),故采取此種方式內(nèi)層rept循環(huán)7次產(chǎn)生的代碼大小為:6 * (2 + 2) + 2 + 5 = 31 字節(jié)。而外層循環(huán)以32字節(jié)對齊,相比于老版本每次都為push和long jmp而言,所以利用short jmp節(jié)省了內(nèi)存開銷。參見: http://didat.sprg.uniroma2.it/la/docs/la12-10.pdf
每個中斷門描述符在將vector壓入后都跳轉(zhuǎn)到common_interrupt進(jìn)行處理。common_interrupt在保存中斷現(xiàn)場之后,跳轉(zhuǎn)到do_IRQ進(jìn)行中斷函數(shù)處理,最后調(diào)用iret_from_intr進(jìn)行中斷返回、恢復(fù)中斷上下文。
863 common_interrupt:
864 ???????? addl $-0x80,(%esp)????? /* Adjust vector into the [-256,-1] range */
865 ???????? SAVE_ALL /* 宏定義,負(fù)責(zé)完成宏定義中斷現(xiàn)場的保護(hù)工作 */
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? /* 清除系統(tǒng)標(biāo)志寄存器EFLAGS中的方向標(biāo)志位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在調(diào)用 do_IRQ 之前中斷棧內(nèi)存布局如下:
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

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