LINUX中斷描述符初始化
@CopyLeft by ICANTH , I Can do ANy THing that I CAN THink ! ~
Author : WenHui , WuHan University , 2012-6-4
硬件產(chǎn)生中斷之后,需要通過門描述符來尋找中斷的處理程序入口。門描述符和段描述符一樣,8個(gè)字節(jié)。門描述符大體分為:段偏移、段選擇子以及DPL。段選擇子用于在GDT中尋找到門段基址,DPL用于控制當(dāng)前進(jìn)程中斷或異常訪問權(quán)限。當(dāng)發(fā)生中斷時(shí),將門描述符所指向的段基地放入%cs,將段偏移放入%eip,轉(zhuǎn)入相應(yīng)服務(wù)。
門描述符結(jié)構(gòu)如下:
任務(wù)門描述符 :用于在發(fā)生中斷時(shí)調(diào)度相應(yīng)進(jìn)程
中斷門描述符 :描述中斷處理程序所在段選擇子和段內(nèi)偏移值。據(jù)此修改EIP及關(guān)閉中斷Eflags的IT標(biāo)識(shí)位
陷阱門描述符 :與中斷門描述符一樣,但不關(guān)中斷
中斷描述符表IDT( Interrupt Descriptor Table )用于存放256個(gè)門描述符,對(duì)應(yīng)256個(gè)中斷向量。其中IA32規(guī)定前0~31號(hào)固定向量用于異常。寄存器 idtr 為門描述符表IDT的物理地址。根據(jù)向量尋找基對(duì)應(yīng)門描述符,并將中斷或異常程序加載過程下圖所示:
LINUX初始化中斷描述符表分兩步:初步初始化,以及最終初始化。初步初始化在head.S文件: http://os1a.cs.columbia.edu/lxr/source/arch/x86/kernel/head_32.S
中斷描述符表初步初始化
1)聲明256個(gè)門描述符的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個(gè)門描述符。對(duì)于每個(gè)門描述符,段選擇子都指向內(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)中實(shí)現(xiàn),都由系統(tǒng)初始化入口函數(shù)start_kernel()所調(diào)用。對(duì)于特定異常,其處理異常函數(shù)預(yù)先已經(jīng)設(shè)定好;但對(duì)于特定異步中斷,由于需要捕獲設(shè)備I/O中斷的程序數(shù)目不定,所以得采用特定數(shù)據(jù)結(jié)構(gòu)來對(duì)irq及其action進(jìn)行描述。
trap_init: http://os1a.cs.columbia.edu/lxr/source/arch/x86/kernel/traps.c#830
do_IRQ:
1 )異常部分最終初始化
由于IA32規(guī)定異常所對(duì)應(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號(hào)中斷門描述符。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()組裝成一個(gè)門描述符格式,并調(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
(); /* 初始化該平臺(tái)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號(hào)處理者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)生錯(cuò)誤碼并壓入棧中。另外一些異常,系統(tǒng)將不產(chǎn)生異常,例如overflow,為了統(tǒng)一中斷、產(chǎn)生錯(cuò)誤碼的異常和不產(chǎn)生錯(cuò)誤碼的異常的棧內(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處理“棧異?!碧幚頃r(shí),call *edi = call do_stack_segment,do_stack_segment函數(shù)由DO_ERROR宏生成。error_code給do_stack_segment函數(shù)傳入regs參數(shù)時(shí),由ABI規(guī)范規(guī)定,eax為第一個(gè)參數(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
;???? /* 對(duì)應(yīng)%gs,對(duì)于異常而言是處理器代碼 */
033
???????? long
orig_eax
; /* 對(duì)應(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。對(duì)于CPU0的vecotr_irq而言,若其IRQ是PIC中斷,則其vector早已由PIC固定,所以設(shè)置其IRQ0…IRQ15的vector為0…15。若是I/O APIC,由于其vector分配是可由OS動(dòng)態(tài)設(shè)置,所以vector 0…15可能會(huì)被重新分配;
2) 本質(zhì)上調(diào)用native_init_IRQ()函數(shù)對(duì)irq_desc、以及中斷部分門描述符進(jìn)行初始化,并針對(duì)CONFIG_4KSTACKS配置、協(xié)處理器模擬浮點(diǎn)運(yùn)算等進(jìn)行配置;
a、 調(diào)用init_ISA_irqs()初始化8259A可斷控制器,并對(duì)相應(yīng)中斷請(qǐng)求線IRQ進(jìn)行初始化、使其對(duì)應(yīng)中斷控制器irq_desc的操作函數(shù)為8259A操作接口函數(shù);
b、 調(diào)用apic_intr_init()函數(shù)針對(duì)采取I/O APIC中斷處理器的情況,對(duì)APIC中斷處理器進(jìn)行初始化工作;
c、 將調(diào)用set_intr_gate為系統(tǒng)中每根中斷請(qǐng)求線IRQ地應(yīng)的中斷向量號(hào)設(shè)置了相應(yīng)的中斷門描述門, 其中斷處理函數(shù)定義在interrupt數(shù)組中 ;
d、 在PC平臺(tái)下set_irq(2, &rq2)對(duì)從8259A中斷控制器的第三根IRQ請(qǐng)求做特殊處理;
e、 irq_ctx_init函數(shù)用于在配置CONFIG_4KSTACK的情況下配置當(dāng)前CPU的中斷棧相關(guān)項(xiàng)。LINUX內(nèi)核在未配置CONFIG_4KSTACK時(shí),共享所中斷進(jìn)程的內(nèi)核棧,內(nèi)核棧為兩頁,即8K。在2.6版本時(shí),增加了CONFIG_4KSTACK選項(xiàng),將棧大小從兩頁減小至一頁,為了應(yīng)對(duì)棧的減少,故中斷處理程序擁有自己的中斷處理程序線,為原先共享?xiàng)5囊话耄?K,每個(gè)CPU擁有一個(gè)中斷棧。
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ù)對(duì)irq_desc、以及中斷部分門描述符進(jìn)行初始化。x86_init是
x86_init_ops
類型,集成針對(duì)x86特定平臺(tái)的各種初始化工作,包含初始化PCI總線、中斷、異常等,其中irqs就是針對(duì)中斷進(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}。并且對(duì)于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對(duì)應(yīng)的中斷向量號(hào)設(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從片的級(jí)聯(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è)置針對(duì)浮點(diǎn)計(jì)算異常的處理函數(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時(shí),為系統(tǒng)中每一CPU設(shè)置中斷、異常處理函數(shù)所需的內(nèi)核態(tài)棧;在沒有選中該選項(xiàng)的情況下,irq_ctx_init是個(gè)空語句 */
266
????????
irq_ctx_init
(
smp_processor_id
());
267
#endif
268
}
101
void
__init
init_ISA_irqs
(void)
102
{
103
???????? int
i
;
/*
若存在LOCAL_APIC,則對(duì)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級(jí)聯(lián)而成,每片可有8個(gè)IRQ,故兩片最多初始化16個(gè)IRQ,但由于IRQ 2用于級(jí)聯(lián),故實(shí)際僅有15個(gè)有效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ù)組符號(hào)由匯編代碼定義,其源碼如下:
/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號(hào)內(nèi)部中斷 */
832
.rept (NR_VECTORS-FIRST_EXTERNAL_VECTOR+6)/7
833
???????? .balign 32 /* 32字節(jié)對(duì)齊 */
834
?? .rept 7
835
???? .if vector < NR_VECTORS
836
?????? .if vector <> FIRST_EXTERNAL_VECTOR
/* 按照CFA規(guī)則修改前一個(gè)offset,以達(dá)4字節(jié)對(duì)齊。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é)對(duì)齊 */
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)個(gè)跳轉(zhuǎn)到common_interrupt的匯編代碼片段,起始地址是irq_entries_start;在數(shù)據(jù)段產(chǎn)生一個(gè)中斷數(shù)組的符號(hào)interrupt,用于記錄產(chǎn)生代碼段中每個(gè)中斷向量處理的匯編代碼片段地址,在C語言中將interrupt符號(hào)作為中斷數(shù)組變量導(dǎo)入:
132
extern void (*
__initconst
interrupt
[
NR_VECTORS
-
FIRST_EXTERNAL_VECTOR
])(void);
ENTRY(interrupt)編譯之后,所生成的代碼段和數(shù)據(jù)段內(nèi)存布局如下:
ENTRIY(interrupt)匯編代碼段主要由兩個(gè)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é)對(duì)齊,內(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é)對(duì)齊,相比于老版本每次都為push和long jmp而言,所以利用short jmp節(jié)省了內(nèi)存開銷。參見: http://didat.sprg.uniroma2.it/la/docs/la12-10.pdf
每個(gè)中斷門描述符在將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寄存器的值在每次字符串指令操作后自動(dòng)+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號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元

