中断描述符 (IDT) ================================================================================ 三个常见的中断和异常来源: * 异常 - sync; * 软中断 - sync; * 外部中断 - async。 异常的类型: * 故障 - 在指令导致异常`之前`会被准确地报告。`%rip`保存的指针指向故障的指令; * 陷阱 - 在指令导致异常`之后`会被准确地报告。`%rip`保存的指针同样指向故障的指令; * 终止 - 是不明确的异常。 因为它们不能被明确,中止通常不允许程序可靠地再次启动。 只有当RFLAGS.IF = 1时,`可屏蔽`中断触发才中断处理程序。 除非RFLAGS.IF位清零,否则它们将持续处于等待处理状态。 `不可屏蔽`中断(NMI)不受rFLAGS.IF位的影响。 无论怎样一个NMI的发生都会进一步屏蔽之后的其他NMI,直到执行IRET(中断返回)指令。 具体的异常和中断来源被分配了固定的向量标识号(也称“中断向量”或简称“向量”)。中断处理程序使用中断向量来定位异常或中断,从而分配相应的系统软件服务处理程序。有至多256个特殊的中断向量可用。前32个是保留的,用于预定义的异常和中断条件。请参考[arch / x86 / include / asm / traps.h](http://lxr.free-electrons.com/source/arch/x86/include/asm/traps.h#L121)头文件中对他们的定义: ``` /* 中断/异常 */ enum { X86_TRAP_DE = 0, /* 0, 除零错误 */ X86_TRAP_DB, /* 1, 调试 */ X86_TRAP_NMI, /* 2, 不可屏蔽中断 */ X86_TRAP_BP, /* 3, 断点 */ X86_TRAP_OF, /* 4, 溢出 */ X86_TRAP_BR, /* 5, 超出范围 */ X86_TRAP_UD, /* 6, 操作码无效 */ X86_TRAP_NM, /* 7, 设备不可用 */ X86_TRAP_DF, /* 8, 双精度浮点错误 */ X86_TRAP_OLD_MF, /* 9, 协处理器段溢出 */ X86_TRAP_TS, /* 10, 无效的 TSS */ X86_TRAP_NP, /* 11, 段不存在 */ X86_TRAP_SS, /* 12, 堆栈段故障 */ X86_TRAP_GP, /* 13, 一般保护故障 */ X86_TRAP_PF, /* 14, 页错误 */ X86_TRAP_SPURIOUS, /* 15, 伪中断 */ X86_TRAP_MF, /* 16, x87 浮点异常 */ X86_TRAP_AC, /* 17, 对齐检查 */ X86_TRAP_MC, /* 18, 机器检测 */ X86_TRAP_XF, /* 19, SIMD (单指令多数据结构浮点)异常 */ X86_TRAP_IRET = 32, /* 32, IRET (中断返回)异常 */ }; ``` 错误代码(Error code) -------------------------------------------------------------------------------- 处理器异常处理程序使用错误代码报告某些异常的错误和状态信息。在控制权交给异常处理程序期间,异常处理装置将错误代码推送到堆栈中。错误代码有两种格式: * 多数异常错误报告格式; * 页错误格式。 选择子错误代码的格式如下: ``` 31 16 15 3 2 1 0 +-------------------------------------------------------------------------------+ | | | T | I | E | | Reserved | Selector Index | - | D | X | | | | I | T | T | +-------------------------------------------------------------------------------+ ``` 说明如下: * `EXT` - 如果该位设置为1,则异常源在处理器外部。 如果设置为0,则异常源位于处理器的内部; * `IDT` - 如果该位设置为1,则错误代码选择子索引字段引用位于“中断描述符表”中的门描述符。 如果设置为0,则选择子索引字段引用“全局描述符表”或本地描述符表“LDT”中的描述符,由“TI”位所指示; * `TI` - 如果该位设置为1,则错误代码选择子索引字段引用“LDT”中的描述符。 如果清除为0,则选择子索引字段引用“GDT”中的描述符; * `Selector Index` - 选择子索引字段指定索引为“GDT‘,“LDT”或“IDT”,它是由“IDT”和“TI”位指定的。 页错误代码格式如下: ``` 31 4 3 2 1 0 +-------------------------------------------------------------------------------+ | | | R | U | R | - | | Reserved | I/D | S | - | - | P | | | | V | S | W | - | +-------------------------------------------------------------------------------+ ``` 说明如下: * `I/D` - 如果该位设置为1,表示造成页错误的访问是取指; * `RSV` - 如果该位设置为1,则页错误是处理器从保留给分页表的区域中读取1的结果; * `U/S` - 如果该位被设置为0,则是管理员模式(`CPL = 0,1或2`)进行访问导致了页错误。 如果该位设置为1,则是用户模式(CPL = 3)进行访问导致了页错误; * `R/W` - 如果该位被设置为0,导致页错误的是内存读取。 如果该位设置为1,则导致页错误的是内存写入; * `P` - 如果该位被设置为0,则页错误是由不存在的页面引起的。 如果该位设置为1,页错误是由于违反页保护引起的。 中断控制传输(Interrupt Control Transfers) -------------------------------------------------------------------------------- IDT可以包含三种门描述符中的任何一种: * `Task Gate(任务门)` - 包含用于异常与或中断处理程序任务的TSS的段选择子; * `Interrupt Gate(中断门)` - 包含处理器用于将程序从执行转移到中断处理程序的段选择子和偏移量; * `Trap Gate(陷阱门)` - 包含处理器用于将程序从执行转移到异常处理程序的段选择子和偏移量。 门的一般格式是: ``` 127 96 +-------------------------------------------------------------------------------+ | | | Reserved | | | +-------------------------------------------------------------------------------- 95 64 +-------------------------------------------------------------------------------+ | | | Offset 63..32 | | | +-------------------------------------------------------------------------------+ 63 48 47 46 44 42 39 34 32 +-------------------------------------------------------------------------------+ | | | D | | | | | | | | Offset 31..16 | P | P | 0 |Type |0 0 0 | 0 | 0 | IST | | | | L | | | | | | | -------------------------------------------------------------------------------+ 31 16 15 0 +-------------------------------------------------------------------------------+ | | | | Segment Selector | Offset 15..0 | | | | +-------------------------------------------------------------------------------+ ``` 说明如下: * `Selector` - 目标代码段的段选择子; * `Offset` - 处理程序入口点的偏移量; * `DPL` - 描述符权限级别; * `P` - 当前段标志; * `IST` - 中断堆栈表; * `TYPE` - 本地描述符表(LDT)段描述符,任务状态段(TSS)描述符,调用门描述符,中断门描述符,陷阱门描述符或任务门描述符之一。 `IDT` 描述符在Linux内核中由以下结构表示(仅适用于`x86_64`): ```C struct gate_struct64 { u16 offset_low; u16 segment; unsigned ist : 3, zero0 : 5, type : 5, dpl : 2, p : 1; u16 offset_middle; u32 offset_high; u32 zero1; } __attribute__((packed)); ``` 它定义在 [arch/x86/include/asm/desc_defs.h](http://lxr.free-electrons.com/source/arch/x86/include/asm/desc_defs.h#L51) 头文件中。 任务门描述符不包含`IST`字段,并且其格式与中断/陷阱门不同: ```C struct ldttss_desc64 { u16 limit0; u16 base0; unsigned base1 : 8, type : 5, dpl : 2, p : 1; unsigned limit1 : 4, zero0 : 3, g : 1, base2 : 8; u32 base3; u32 zero1; } __attribute__((packed)); ``` 任务切换期间的异常(Exceptions During a Task Switch) -------------------------------------------------------------------------------- 任务切换在加载段选择子期间可能会发生异常。页错误也可能会在访问TSS时出现。在这些情况下,由硬件任务切换机构完成从TSS加载新的任务状态,然后触发适当的异常处理。 **在长模式下,由于硬件任务切换机构被禁用,因而在任务切换期间不会发生异常。** 不可屏蔽中断(Nonmaskable interrupt) -------------------------------------------------------------------------------- **未完待续** API -------------------------------------------------------------------------------- **未完待续** 中断堆栈表(Interrupt Stack Table) -------------------------------------------------------------------------------- **未完待续**