Interrupt, Trap, and System Gates | |||||||||
Linux uses a slightly different breakdown and terminology from Intel | |||||||||
when classifying the interrupt descriptors included in the Interrupt Descriptor Table: | |||||||||
Interrupt gate | |||||||||
An Intel interrupt gate that cannot be accessed by a User Mode process | |||||||||
(the gate's DPL field is equal to 0). All Linux interrupt handlers are activated by means of | |||||||||
interrupt gates , and all are restricted to Kernel Mode. | |||||||||
System gate | |||||||||
An Intel trap gate that can be accessed by a User Mode process (the gate's DPL field is equal to 3). | |||||||||
The three Linux exception handlers associated with the vectors 4, 5, and 128 are activated | |||||||||
by means of system gates , so the three assembly language instructions into , bound , | |||||||||
and int $0x80 can be issued in User Mode. | |||||||||
System interrupt gate | |||||||||
An Intel interrupt gate that can be accessed by a User Mode process (the gate's DPL field is equal to 3). | |||||||||
The exception handler associated with the vector 3 is activated by means of a system interrupt gate, | |||||||||
so the assembly language instruction int3 can be issued in User Mode. | |||||||||
Trap gate | |||||||||
An Intel trap gate that cannot be accessed by a User Mode process (the gate's DPL field is equal to 0). | |||||||||
Most Linux exception handlers are activated by means of trap gates | |||||||||
Task gate | |||||||||
An Intel task gate that cannot be accessed by a User Mode process (the gate's DPL field is equal to 0). | |||||||||
The Linux handler for the "Double fault " exception is activated by means of a task gate. | |||||||||
The following architecture-dependent functions are used to insert gates in the IDT | |||||||||
set_intr_gate(n,addr) | //Inserts an interrupt gate in the n th IDT entry | ||||||||
set_system_gate(n,addr) | |||||||||
set_system_intr_gate(n,addr) | |||||||||
set_trap_gate(n,addr) | |||||||||
set_task_gate(n,gdt) | |||||||||
//i386/kernel/traps.c | |||||||||
static void __init set_system_gate(unsigned int n, void *addr) | |||||||||
{ | |||||||||
_set_gate(idt_table+n,15,3,addr,__KERNEL_CS); | |||||||||
} | |||||||||
#define _set_gate(gate_addr,type,dpl,addr,seg) / | |||||||||
do { / | |||||||||
int __d0, __d1; / | |||||||||
__asm__ __volatile__ ("movw %%dx,%%ax/n/t" / | |||||||||
movw %4,%%dx/n/t / | |||||||||
movl %%eax,%0/n/t / | |||||||||
movl %%edx,%1 / | |||||||||
:"=m" (*((long *) (gate_addr))), / | |||||||||
"=m" (*(1+(long *) (gate_addr))), "=&a" (__d0), "=&d" (__d1) / | |||||||||
:"i" ((short) (0x8000+(dpl<<13)+(type<<8))), / | |||||||||
"3" ((char *) (addr)),"2" ((seg) << 16)); / | |||||||||
} while (0) | |||||||||
The IDT is initialized and used by the BIOS routines while the computer still operates in Real Mode. | |||||||||
Once Linux takes over, however, the IDT is moved to another area of RAM | |||||||||
and initialized a second time, because Linux does not use any BIOS routine | |||||||||
//i386/kernel/head.s | |||||||||
/* | |||||||||
* setup_idt | |||||||||
* | |||||||||
* sets up a idt with 256 entries pointing to | |||||||||
* ignore_int, interrupt gates. It doesn't actually load | |||||||||
* idt - that can be done only after paging has been enabled | |||||||||
* and the kernel moved to PAGE_OFFSET. Interrupts | |||||||||
* are enabled elsewhere, when we can be relatively | |||||||||
* sure everything is ok. | |||||||||
*/ | |||||||||
setup_idt: | |||||||||
lea ignore_int,%edx | |||||||||
movl $(__KERNEL_CS << 16),%eax | |||||||||
movw %dx,%ax | /* selector = 0x0010 = cs */ | ||||||||
movw $0x8E00,%dx | /* interrupt gate - dpl=0, present */ | ||||||||
lea idt_table,%edi | |||||||||
mov $256,%ecx | |||||||||
rp_sidt: | |||||||||
movl %eax,(%edi) | |||||||||
movl %edx,4(%edi) | |||||||||
addl $8,%edi | |||||||||
dec %ecx | |||||||||
jne rp_sidt | |||||||||
ret | |||||||||
/* This is the default interrupt "handler" :-) */ | |||||||||
int_msg: | |||||||||
.asciz "Unknown interrupt/n" | |||||||||
ALIGN | |||||||||
ignore_int: | |||||||||
cld | |||||||||
pushl %eax | |||||||||
pushl %ecx | |||||||||
pushl %edx | |||||||||
pushl %es | |||||||||
pushl %ds | |||||||||
movl $(__KERNEL_DS),%eax | |||||||||
movl %eax,%ds | |||||||||
movl %eax,%es | |||||||||
pushl $int_msg | |||||||||
call printk | |||||||||
popl %eax | |||||||||
popl %ds | |||||||||
popl %es | |||||||||
popl %edx | |||||||||
popl %ecx | |||||||||
popl %eax | |||||||||
iret | |||||||||
The ignore_int( ) handler should never be executed. The occurrence of "Unknown interrupt" | |||||||||
messages on the console or in the log files denotes either a hardware problem | |||||||||
(an I/O device is issuing unforeseen interrupts) or a kernel problem | |||||||||
(an interrupt or exception is not being handled properly). | |||||||||
Understanding the linux kernel-ch4-Initializing the Interrupt Descriptor Table
最新推荐文章于 2022-03-10 17:35:33 发布