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,轉入相應服務。
門描述符結構如下:
任務門描述符 :用于在發生中斷時調度相應進程
中斷門描述符 :描述中斷處理程序所在段選擇子和段內偏移值。據此修改EIP及關閉中斷Eflags的IT標識位
陷阱門描述符 :與中斷門描述符一樣,但不關中斷
中斷描述符表IDT( Interrupt Descriptor Table )用于存放256個門描述符,對應256個中斷向量。其中IA32規定前0~31號固定向量用于異常。寄存器 idtr 為門描述符表IDT的物理地址。根據向量尋找基對應門描述符,并將中斷或異常程序加載過程下圖所示:
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生成。其調用關系如下:
在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之前,棧的內存布局如下圖。
在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)進行中斷最終初始化。其操作流程如下:
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)編譯之后,所生成的代碼段和數據段內存布局如下:
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 之前中斷棧內存布局如下:
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

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