[翻译]Interrupt Descriptor Table–IDT
中断描述附表
INT_NUM | Short Description PM |
---|---|
0x00 | Division by zero |
0x01 | Debugger |
0x02 | NMI |
0x03 | Breakpoint |
0x04 | Overflow |
0x05 | Bounds |
0x06 | Invalid Opcode |
0x07 | Coprocessor not available |
0x08 | Double fault |
0x09 | Coprocessor Segment Overrun (386 or earlier only) |
0x0A | Invalid Task State Segment |
0x0B | Segment not present |
0x0C | Stack Fault |
0x0D | General Protection |
0x0E | Page Fault |
0x0F | reserved |
0x10 | Math Fault |
0x11 | Alignment Check |
0x12 | Machine Check |
0x13 | SIMD Floating-Point Exception |
钩子
Name | Bit | Description |
0 | 0..2 | These bits are ignored by the CPU and should be 0. |
Limit | 0..15 | Defines the length of the IDT in bytes (minimum value is 100h, a value of 1000h means 200 interrupts). |
Base | 16..47 | This 32 bits are the physical address where the IDT starts (INT 0) |
中断描述附表中多个表项,每个表项的实际大小为 8-byte,结构如下:
struct IDTDescr{ uint16 offset_1; // offset bits 0..15 uint16 selector; // a code segment selector in GDT or LDT uint8 zero; // unused, set to 0 uint8 type_attr; // type and attributes, see below uint16 offset_2; // offset bits 16..31 };
偏移量是一个 32 位的值,被分成了两个部分。选择子是一个 16 位的值,必须是全局描述附表中的一个合法选择子。
type_attr 信息如下:
7 0 +---+---+---+---+---+---+---+---+ | P | Priv | S | GateType | +---+---+---+---+---+---+---+---+
位信息如下:
Name | Bit | Full Name | Description | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Offset | 48..63 | Offset 16..31 | Higher part of the offset. | ||||||||||||||||||||
P | 47 | Present | can be set to 0 for unused interrupts or for Paging. | ||||||||||||||||||||
DPL | 45,46 | Descriptor Privilege Level | Gate call protection. Specifies which privilege Level the calling Descriptor minimum should have. So hardware and CPU interrupts can be protected from beeing called out of userspace. | ||||||||||||||||||||
S | 44 | Storage Segment | = 0 for interrupt gates. | ||||||||||||||||||||
Typ | 40..43 | Gate Type 0..3 | Possible IDT gate types :
|
||||||||||||||||||||
0 | 32..39 | Unused 0..7 | Have to be 0. | ||||||||||||||||||||
Selector | 16..31 | Selector 0..15 | Selector of the interrupt function (to make sense - the kernel's selector). The selector's descriptor's DPL field has to be 0. | ||||||||||||||||||||
Offset | 0..15 | Offset 0..15 | Lower part of the interrupt function's offset address (also known as pointer). |
I386 中断门
中断门用来指定中断服务程序。在保护模式下,当你写汇编指令 INT 50 时,CPU 在中断描述附表中查找第 50 个表项(位于 50 * 8 byte)。然后,中断门的段选择子与偏移量被加载。通过段选择子与偏移量可以调用中断服务程序。当遇到中断服务程序中的 IRET 指令时,CPU 返回原来的地址继续执行。当运行在 32 位模式下,而选择子是 16 位,在执行完中断服务程序后,CPU 会继续在 16 位保护模式下运行。如果想在执行完中断服务后仍然运行在 32 位模式,需要执行 032 IRET 指令,否则 CPU 无法知道是否返回 32 位模式(在栈中读取 32 位地址作为返回地址,而不是 16 位)。
type_attr | Type |
---|---|
0b1110=0xE | 32-bit interrupt gate |
0b0110=0x6 | 16-bit interrupt gate |
Here are some pre-cooked type_attr values people are likely to use (assuming DPL=0):
- 32-bit Interrupt gate: 0x8E ( P=1, DPL=00b, S=0, type=1110b => type_attr=1000_1110b=0x8E)
I386 陷阱门
当属于陷阱门或者中断门的中断或者异常产生时,CPU 会在栈(EFLAGS,CS, IP)中存放放回信息,因此中断处理程序可以通过 IRET 指令返回。然后会执行对应门选择子的 segment:offset。对于某些异常,错误码也会压入栈中,需要在执行 IRET 前,将其 POP 出来。陷阱与异常相似,他们的描述子相同,只是类型不同。不同之处是:对于中断门,中断会禁用其他描述符表项直到 IRET 返回时。
type_attr | Type |
---|---|
0b1111=0xf | 32-bit trap gate |
0b0110=0x7 | 16-bit trap gate |
Here are some pre-cooked type_attr values people are likely to use (assuming DPL=0):
- 32-bit Trap gate: 0x8F ( P=1, DPL=00b, S=0, type=1111b => type_attr=1000_1111b=0x8F)
Thus, Trap and Interrupt gate descriptors hold the following data (other than type_attr):
- 16-bit selector of a code segment in GDT or LDT
- 32-bit offset into that segment - address of the handler, where execution will be transferred
I386 任务门
在任务门情况下,描述符的偏移量部分没有使用,应该设置为 0。
当类型是任务门的中断或者异常发生时,任务会发生切换。
"A task gate in the IDT references a TSS descriptor in the GDT. A switch to the handler task is handled in the same manner as an ordinary task switch. (..) The link back to the interrupted task is stored in the previous task link field of the handler task's TSS. If an exception caused an error code to be generated, this error code is copied to the stack of the new task."
—Intel manual (vol.3 p.5-19) |
"*NOTE* Because IA-32 tasks are not re-entrant, an interrupt-handler task must disable interrupts between the time it completes handling the interrupt and the time it executes the IRET instruction. This action prevents another interrupt from occurring while the interrupt task's TSS is still marked busy, which would cause a general-protection (#GP) exception."
—Intel manual |
Choosing type_attr values: (See
type_attr | Type |
---|---|
0b0101=0x5 | task gate |
For DPL=0, type_attr=0x85=0b0101
因此, TSS 选择子是在任务门下唯一需要的描述符。
Advantages over using trap/interrupt gates:
- The entire context of the interrupted task is saved automatically (no need to worry about registers)
- The handler can be isolated from other tasks in a separate address space in LDT.
- "A new tss permits the handler to use a new privilege level 0 stack when handling the exception or interrupt. If an exception or interrupt occurs when the current privilege level 0 stack is corrupted, accessing the handler through a task gate can prevent a system crash by providing the handler with a new privilege level 0 stack" --Intel manual
Disadvantage:
- Saving the entire task context into TSS is slower than using a trap/interrupt gate (where the handler can save only what it needs).
- Is it that much faster if the handler does PUSHAD or pushes registers one by one?
- Does it make a difference, considering a non-dummy, non-trivial handler?
设置与存储
中断描述附表使用 LIDT 汇编指令来设置。操作数是中断描述附表的地址:
Byte: +---------------+---------------+ 0 | Size | +---------------+---------------+ +---------------+---------------+---------------+---------------+ 2 | Offset | +---------------+---------------+---------------+---------------+
偏移量是中断描述附表自己的虚拟地址。大小字段是描述附表的大小减一。这个结构可以通过 SIDT 指令,被再次存放到内存中。
IDT in IA-32e Mode (64-bit IDT)
When in long or compatibility mode (once the EFER.LME flag has been set) the IDT's structure changes slightly. The IDTR structure's (used by LIDT and SITD) base field changes to 64-bits to allow the IDT to reside anywhere in memory, and each entry in the IDT grows by 64-bits. The first dword is the high bits of the address, while the second is zero.
Offset | Size | Description |
0 | 2 | Limit - Maximum addressable byte in table |
2 | 8 | Offset - Linear (paged) base address of IDT |
Offset | Size | Description |
0 | 2 | Offset low bits (0..15) |
2 | 2 | Selector (Code segment selector) |
4 | 1 | Zero |
5 | 1 | Type and Attributes (same as before) |
6 | 2 | Offset middle bits (16..31) |
8 | 4 | Offset high bits (32..63) |
12 | 4 | Zero |