第6章 中断和异常处理 -- part2(Intel手册翻译)
前一篇翻译了第三卷第6章的前14个小结,这篇翻译第15小结。
6.15 异常和中断参考
以下部分描述了生成异常和中断的条件。它们按照向量号的顺序排列。这些部分包含的信息如下:
• 异常类别 - 指示异常类别是故障、陷阱还是中止类型。有些异常可以是故障或陷阱类型,具体取决于何时检测到错误条件。(此部分不适用于中断。)
• 描述 - 给出异常或中断类型的一般描述。它还描述了处理器如何处理异常或中断。
• 异常错误代码 - 指示是否为异常保存了错误代码。如果保存了错误代码,则描述错误代码的内容。(此部分不适用于中断。)
• 保存的指令指针 - 描述保存的(或返回的)指令指针指向哪个指令。它还指示指针是否可以用于重新启动故障指令。
• 程序状态更改 - 描述异常或中断对当前正在运行的程序或任务状态的影响,以及在不丢失连续性的情况下重新启动程序或任务的可能性。
中断0——除法错误异常(#DE)的翻译:
异常类别:故障。
描述:表示DIV或IDIV指令的除数操作数为0,或者结果无法用目标操作数指定的位数表示。
异常错误代码:无。
保存的指令指针:保存的CS和EIP寄存器内容指向生成异常的指令。
程序状态变化:除法错误不会伴随程序状态变化,因为异常发生在故障指令执行之前。
中断1 - 调试异常 (#DB)
-
异常类别:
陷阱或故障。异常处理程序可以通过检查DR6和其他调试寄存器的内容来区分陷阱或故障。 -
描述:
表示已检测到一个或多个调试异常条件。异常是故障还是陷阱取决于条件(请参见表6-3)。有关调试异常的详细信息,请参见第17章“调试、分支剖析、TSC和Intel®资源导向技术(Intel® RDT)功能”。
注:
- 硬件供应商可能使用INT1指令进行硬件调试。因此,英特尔建议软件供应商改用INT3指令进行软件断点。
-
异常错误代码:
无。异常处理程序可以检查调试寄存器以确定引起异常的条件。 -
保存的指令指针:
故障 - 保存的CS和EIP寄存器内容指向生成异常的指令。
陷阱 - 保存的CS和EIP寄存器内容指向生成异常的指令后面的指令。 -
程序状态更改:
故障 - 调试异常发生在执行故障指令之前,因此不会伴随程序状态更改。程序可以在从调试异常处理程序返回后恢复正常执行。
陷阱 - 调试异常伴随着程序状态更改,因为正在执行的指令或任务切换允许在生成异常之前完成。但是,程序的新状态不会被破坏,程序的执行可以继续可靠地进行。
以下内容详细说明了在执行加载SS寄存器的MOV或POP指令后的指令边界上处理调试异常的方法:
• 如果EFLAGS.TF为1,则不会生成单步陷阱。
• 如果指令遇到数据断点,则在MOV或POP之后的指令完成后传递结果调试异常。即使下一条指令是INT n、INT3或INTO,也会发生这种情况。
• 在MOV或POP之后的指令上的任何指令断点都被抑制(就像EFLAGS.RF为1一样)。
在RTM区域内的任何调试异常都会导致事务中止,并默认将控制流重定向到回退指令地址。如果启用了RTM事务区域的高级调试功能,则由于调试异常而导致的任何事务中止会导致执行回滚到XBEGIN指令之前,然后传递#DB。请参见《Intel® 64和IA-32体系结构软件开发人员手册》第1卷第16.3.7节“启用RTM调试器支持”。
中断2 - NMI中断
-
异常类别:
不适用。 -
描述:
非屏蔽中断(NMI)由处理器的NMI引脚或通过I/O APIC设置为本地APIC的NMI请求外部生成。此中断会导致调用NMI中断处理程序。 -
异常错误代码:
不适用。 -
保存的指令指针:
处理器总是在指令边界上进行NMI中断。CS和EIP寄存器的保存内容指向在发生中断时要执行的下一条指令。有关处理器何时进行NMI中断的更多信息,请参见“异常分类”一节。 -
程序状态更改:
当接收到NMI中断时,正在执行的指令将被完成,然后才会生成NMI。因此,在中断处理程序返回时,程序或任务可以重新启动,而不会丢失连续性,前提是中断处理程序在处理中断之前保存了处理器的状态,并在返回之前恢复了处理器的状态。
中断3——断点异常(#BP)
-
异常类
陷阱。 -
描述
表示执行了断点指令(INT3,操作码CC),导致生成断点陷阱。通常,调试器通过将指令的第一个操作码字节替换为INT3指令的操作码来设置断点。(INT3指令只有一个字节长,这使得在RAM中的代码段中用断点操作码替换操作码变得容易。)操作系统或调试工具可以使用映射到与代码段相同物理地址空间的数据段,在希望调用调试器的地方放置INT3指令。
对于P6系列、奔腾、Intel486和Intel386处理器,使用调试寄存器设置断点更为方便。(有关断点异常的信息,请参见第17.3.2节“断点异常(#BP)——中断向量3”。)如果需要更多断点超出调试寄存器的限制,可以使用INT3指令。
RTM区域内的任何断点异常都会导致事务中止,并默认将控制流重定向到回退指令地址。如果启用了RTM事务区域的高级调试,则由于断点异常而导致的任何事务中止会导致执行回滚到XBEGIN指令之前,然后传递调试异常(#DB)——而不是断点异常。请参见《Intel® 64和IA-32体系结构软件开发人员手册》第1卷第16.3.7节“启用RTM调试器支持”。
还可以通过执行带有操作数3的INT n指令来生成断点异常。此指令(INT 3)的操作与INT3指令略有不同(请参见《Intel® 64和IA-32体系结构软件开发人员手册》第2A卷第3章“INT n/INTO/INT3/INT1——调用中断过程”)。 -
异常错误代码
无。 -
保存的指令指针
保存的CS和EIP寄存器内容指向INT3指令后面的指令。 -
程序状态更改
尽管EIP指向断点指令后面的指令,但程序的状态基本上没有改变,因为INT3指令不会影响任何寄存器或内存位置。因此,调试器可以通过将导致断点的INT3指令替换为原始操作码并减少保存的EIP寄存器内容来恢复暂停的程序。从调试器返回后,程序执行将恢复到替换的指令。
中断4 - 溢出异常 (#OF)
-
异常类别:
陷阱(Trap)。 -
描述:
当执行 INTO 指令时,如果 EFLAGS 寄存器中的 OF 标志被设置,就会发生溢出陷阱。一些算术指令(如 ADD 和 SUB)执行有符号和无符号算术。这些指令会在 EFLAGS 寄存器中设置 OF 和 CF 标志,以分别表示有符号溢出和无符号溢出。在对有符号操作数进行算术运算时,可以直接测试 OF 标志,也可以使用 INTO 指令。使用 INTO 指令的好处是,如果检测到溢出异常,可以自动调用异常处理程序来处理溢出条件。 -
异常错误代码:
无。 -
保存的指令指针:
CS 和 EIP 寄存器的保存内容指向 INTO 指令后面的指令。 -
程序状态变化:
尽管 EIP 指向 INTO 指令后面的指令,但程序的状态基本上没有改变,因为 INTO 指令不会影响任何寄存器或内存位置。因此,在从溢出异常处理程序返回后,程序可以恢复正常执行。
中断5——BOUND范围超出异常(#BR):
-
异常类
故障。 -
描述:
当执行BOUND指令时,如果发生了BOUND范围超出故障,就会发出此异常。BOUND指令检查一个有符号数组索引是否在位于内存中的数组的上限和下限之间。如果数组索引不在数组的范围内,就会生成BOUND范围超出故障。
- 异常错误代码:
无。
- 保存的指令指针:
保存的CS和EIP寄存器的内容指向生成异常的BOUND指令。
- 程序状态更改:
边界检查故障不会伴随着程序状态的更改,因为BOUND指令的操作数不会被修改。从BOUND范围超出异常处理程序返回会导致BOUND指令重新启动。
中断6 - 无效操作码异常 (#UD)
-
异常类别
故障。 -
描述
表示处理器执行了以下操作之一:
• 尝试执行无效或保留的操作码。
• 尝试执行带有其伴随操作码无效的操作数类型的指令;例如,LES指令的源操作数不是内存位置。
• 尝试在不支持MMX技术或SSE / SSE2 / SSE3扩展的Intel 64或IA-32处理器上执行MMX或SSE / SSE2 / SSE3指令。 CPUID特征标志MMX(位23),SSE(位25),SSE2(位26),SSE3(ECX,位0),SSSE3(ECX,位9)指示对这些扩展的支持。
• 当控制寄存器CR0中的EM标志设置为1时,尝试在EM标志设置为1时执行MMX指令或SSE / SSE2 / SSE3 / SSSE3 SIMD指令(除MOVNTI,PAUSE,PREFETCHh,SFENCE,LFENCE,MFENCE,CLFLUSH,MONITOR和MWAIT指令)。
• 当控制寄存器CR4中的OSFXSR位清除(0)时,尝试执行SSE / SE2 / SSE3 / SSSE3指令。请注意,这不包括以下SSE / SSE2 / SSE3指令:MASKMOVQ,MOVNTQ,MOVNTI,PREFETCHh,SFENCE,LFENCE,MFENCE和CLFLUSH;或PAVGB,PAVGW,PEXTRW,PINSRW,PMAXSW,PMAXUB,PMINSW,PMINUB,PMOVMSKB,PMULHUW,PSADBW,PSHUFW,PADDQ,PSUBQ,PALIGNR,PABSB,PABSD,PABSW,PHADDD,PHADDSW,PHADDW,PHSUBD,PHSUBSW,PHSUBW,PMADDUBSM,PMULHRSW,PSHUFB,PSIGNB,PSIGND和PSIGNW的64位版本。
• 当OSXMMEXCPT位在控制寄存器CR4中清除(0)时,在Intel 64或IA-32处理器上执行SSE / SSE2 / SSE3 / SSSE3指令会导致SIMD浮点异常。
• 执行UD0,UD1或UD2指令。请注意,即使是执行UD0,UD1或UD2指令导致无效操作码异常,保存的指令指针仍将指向UD0,UD1或UD2指令。
• 检测到在不能锁定的指令之前或可以锁定但目标操作数不是内存位置的指令之前的LOCK前缀。
• 尝试在实地址或虚拟8086模式下执行LLDT,SLDT,LTR,STR,LSL,LAR,VERR,VERW或ARPL指令。
• 尝试在非SMM模式下执行RSM指令。
在实现乱序执行微架构的Intel 64和IA-32处理器中,直到尝试撤销执行无效指令的结果时才生成此异常;也就是说,解码和推测性地尝试执行无效操作码不会生成此异常。同样,在Pentium处理器和早期的IA-32处理器中,不会因预取和初步解码无效指令而生成此异常。(有关中断和异常的一般规则,请参见第6.5节“异常分类”。)
D6和F1操作码是由Intel 64和IA-32体系结构保留的未定义操作码。即使是未定义的,这些操作码也不会生成无效操作码异常。
- 异常错误代码
无。
- 保存的指令指针
CS和EIP寄存器的保存内容指向生成异常的指令。
- 程序状态更改
无效操作码故障不会伴随程序状态更改,因为不会执行无效指令。
中断7——设备不可用异常 (#NM)
-
异常类别:
故障。 -
描述:
表示以下情况之一:
设备不可用异常由以下三种情况之一产生:
• 处理器在控制寄存器CR0中设置了EM标志(1),并执行了x87 FPU浮点指令。有关WAIT/FWAIT指令的特殊情况,请参见下面的段落。
• 处理器在寄存器CR0的MP和TS标志设置的情况下执行了WAIT/FWAIT指令,而不管EM标志的设置如何。
• 处理器在控制寄存器CR0中设置了TS标志并清除了EM标志的情况下,执行了x87 FPU、MMX或SSE/SSE2/SSE3指令(除MOVNTI、PAUSE、PREFETCHh、SFENCE、LFENCE、MFENCE和CLFLUSH之外)。
当处理器没有内部x87 FPU浮点单元时,将设置EM标志。然后,每次遇到x87 FPU浮点指令时,都会生成一个设备不可用异常,允许异常处理程序调用浮点指令仿真例程。
TS标志表示自上次执行x87浮点、MMX或SSE/SSE2/SSE3指令以来发生了上下文切换(任务切换);但是x87 FPU、XMM和MXCSR寄存器的上下文未保存。当设置了TS标志并清除了EM标志时,每次遇到x87浮点、MMX或SSE/SSE2/SSE3指令(除了上面列出的指令)时,处理器都会生成一个设备不可用异常。异常处理程序可以在执行指令之前保存x87 FPU、XMM和MXCSR寄存器的上下文。有关TS标志的更多信息,请参见第2.5节“控制寄存器”。
控制寄存器CR0中的MP标志与TS标志一起使用,以确定WAIT或FWAIT指令是否应生成设备不可用异常。它将TS标志的功能扩展到WAIT和FWAIT指令,使异常处理程序有机会在执行WAIT或FWAIT指令之前保存x87 FPU的上下文。MP标志主要用于Intel 286和Intel386 DX处理器。对于在Pentium 4、Intel Xeon、P6系列、Pentium或Intel486 DX处理器或Intel 487 SX协处理器上运行的程序,应始终设置MP标志;对于在Intel486 SX处理器上运行的程序,应清除MP标志。 -
异常错误代码:
无。 -
保存的指令指针:
保存的CS和EIP寄存器内容指向生成异常的浮点指令或WAIT/FWAIT指令。 -
程序状态更改:
设备不可用故障不会伴随程序状态更改,因为未执行生成异常的指令。如果设置了EM标志,则异常处理程序可以读取EIP指向的浮点指令,并调用适当的仿真例程。如果设置了MP和TS标志或仅设置了TS标志,则异常处理程序可以保存x87 FPU的上下文,清除TS标志,并继续执行中断的浮点或WAIT/FWAIT指令。
中断8——双重故障异常 (#DF)
-
异常类别:
中止。 -
描述:
表示处理器在调用先前异常的异常处理程序时检测到第二个异常。通常,当处理器在尝试调用异常处理程序时检测到另一个异常时,两个异常可以串行处理。但是,如果处理器无法串行处理它们,则会发出双重故障异常。为确定何时需要将两个故障标记为双重故障,处理器将异常分为三类:良性(benign)异常、贡献(contributory )异常和页面故障(请参见表6-4)。
表6-5显示了导致生成双重故障的各种异常类别组合。双重故障异常属于异常的中止类别。程序或任务无法重新启动或恢复。双重故障处理程序可用于收集有关机器状态的诊断信息,并在可能时优雅地关闭应用程序和/或系统或重新启动系统。
在预取指令时可能遇到段错误或页面错误;但是,此行为超出了表6-5的范围。在处理器尝试传输控制到适当的故障处理程序时,任何进一步生成的故障仍可能导致双重故障序列。
如果在尝试调用双重故障处理程序时发生另一个贡献或页面故障异常,则处理器进入关闭模式。此模式类似于执行HLT指令后的状态。在此模式下,处理器停止执行指令,直到接收到NMI中断、SMI中断、硬件复位或INIT#。处理器生成一个特殊的总线周期,以指示它已进入关闭模式。软件设计人员可能需要了解硬件在进入关闭模式时的响应。例如,硬件可能在前面板上打开指示灯,生成NMI中断以记录诊断信息,调用重置初始化、生成INIT初始化或生成SMI。如果在关闭期间存在任何事件,则将在处理从关闭唤醒事件后处理它们(例如,A20M#中断)。
如果处理器在执行NMI中断处理程序时发生关闭,则只有硬件复位才能重新启动处理器。同样,如果在SMM中执行时发生关闭,则必须使用硬件复位来重新启动处理器。
-
异常错误代码:
零。处理器始终将错误代码0推送到双重故障处理程序的堆栈上。 -
保存的指令指针:
保存的CS和EIP寄存器内容未定义。 -
程序状态更改:
双重故障异常后的程序状态未定义。程序或任务无法恢复或重新启动。双重故障异常处理程序的唯一可用操作是收集所有可能的上下文信息以用于诊断,然后关闭应用程序和/或关闭或重置处理器。如果在任何异常处理机器状态的任何部分损坏时发生双重故障,则无法调用处理程序,必须重置处理器。
中断9——协处理器段溢出异常(Interrupt 9—Coprocessor Segment Overrun Exception)。
-
描述:
该异常表示在Intel386 CPU与Intel 387数学协处理器传输中间部分的操作数时,检测到了页面或段违规。P6系列、奔腾和Intel486处理器不会生成此异常;相反,此条件会通过通用保护异常(#GP)中断13来检测。 -
异常错误代码:
无。 -
保存的指令指针:
CS和EIP寄存器的保存内容指向生成异常的指令。 -
程序状态更改:
协处理器段溢出异常后的程序状态是未定义的。程序或任务无法恢复或重新启动。异常处理程序唯一可用的操作是保存指令指针并使用FNINIT指令重新初始化x87 FPU。
中断10 - 无效TSS异常(#TS)
-
异常类
故障。 -
描述
指示与TSS相关的错误。这样的错误可能在任务切换期间或在使用TSS信息的指令执行期间被检测到。表6-6显示了导致生成无效TSS异常的条件。
此异常可以在原始任务的上下文中生成,也可以在新任务的上下文中生成(参见第7.3节“任务切换”)。直到处理器完全验证了新TSS的存在,异常才在原始任务的上下文中生成。一旦验证了新TSS的存在,任务切换就被认为是完成的。在此之后检测到的任何无效TSS条件都在新任务的上下文中处理。(当任务寄存器被加载为新TSS的段选择器时,任务切换被认为是完成的,如果切换是由过程调用或中断引起的,则新TSS的先前任务链接字段引用旧TSS时,任务切换被认为是完成的。)
无效TSS处理程序必须是使用任务门调用的任务。不建议在故障TSS上下文中处理此异常,因为处理器状态可能不一致。
-
异常错误代码
包含导致违规的段描述符的段选择器索引的错误代码被推送到异常处理程序的堆栈上。如果设置了EXT标志,则表示异常是由当前运行的程序外部事件引起的(例如,如果使用任务门的外部中断处理程序尝试切换到无效的TSS)。 -
保存的指令指针
如果在执行任务切换之前检测到异常条件,则CS和EIP寄存器的保存内容指向调用任务切换的指令。如果在执行任务切换之后检测到异常条件,则CS和EIP寄存器的保存内容指向新任务的第一条指令。 -
程序状态更改
无效TSS处理程序从故障中恢复的能力取决于导致故障的错误条件。有关任务切换过程和可能采取的恢复操作的更多信息,请参见第7.3节“任务切换”。
如果在任务切换期间发生无效的TSS异常,它可能发生在提交到新任务点之前或之后。如果它发生在提交点之前,则不会发生程序状态更改。如果它发生在提交点之后(当新段选择器的段描述符信息已经加载到段寄存器中),处理器将在生成异常之前从新TSS加载所有状态信息。在任务切换期间,处理器首先使用TSS中的段选择器加载所有段寄存器,然后检查它们的内容是否有效。如果发现无效的TSS异常,则加载剩余的段寄存器,但不检查其有效性,因此可能无法用于引用内存。无效的TSS处理程序不应该依赖于能够使用在CS、SS、DS、ES、FS和GS寄存器中找到的段选择器而不引起另一个异常。异常处理程序应在尝试恢复新任务之前加载所有段寄存器;否则,在使诊断更加困难的条件下,可能会在以后导致通用保护异常(#GP)。英特尔建议处理这种情况的方法是使用一个任务来处理无效的TSS异常。从无效的TSS异常处理程序任务切换回中断的任务将导致处理器在从TSS加载寄存器时检查它们。
中断11 - 段不存在 (#NP)
-
异常类
故障。 -
描述
表示段或门描述符的现有标志位被清除。处理器可以在以下任何操作期间生成此异常:
• 尝试加载CS、DS、ES、FS或GS寄存器时。[在加载SS寄存器时检测到不存在的段会导致堆栈故障异常(#SS)的生成。]这种情况可能发生在执行任务切换时。
• 使用LLDT指令加载LDTR时尝试加载。在任务切换操作期间加载LDTR时检测到不存在的LDT会导致无效TSS异常(#TS)的生成。
• 执行LTR指令时,TSS被标记为不存在。
• 尝试使用标记为段不存在但仍然有效的门描述符或TSS。
操作系统通常使用段不存在异常来实现段级虚拟内存。如果异常处理程序加载了段并返回,则中断的程序或任务将恢复执行。
然而,门描述符中的不存在指示并不表示段不存在(因为门不对应于段)。操作系统可以使用门描述符的现有标志来触发对操作系统特殊意义的异常。
随后引用不存在段的贡献性异常或页面故障将导致生成双重故障(#DF)而不是#NP。
- 异常错误代码
包含导致违规的段选择器索引的错误代码被推送到异常处理程序的堆栈上。如果设置了EXT标志,则表示异常是由以下情况之一引起的:
• 导致中断的外部事件(NMI或INTR),随后引用不存在的段
• 随后引用不存在的段的良性异常
如果错误代码引用IDT条目,则设置IDT标志。当正在服务的中断的IDT条目引用不存在的门时,就会发生这种情况。这种事件可以由INT指令或硬件中断生成。
- 保存的指令指针
CS和EIP寄存器的保存内容通常指向生成异常的指令。如果在为新TSS中的段选择器加载段描述符时发生异常,则CS和EIP寄存器指向新任务中的第一条指令。如果在访问门描述符时发生异常,则CS和EIP寄存器指向调用访问的指令(例如引用调用门的CALL指令)。
- 程序状态更改
如果段不存在异常发生在加载寄存器(CS、DS、SS、ES、FS、GS或LDTR)的结果中,则伴随异常的是程序状态更改,因为寄存器未加载。通过简单地将缺失的段加载到内存中并设置段描述符中的现有标志,可以从此异常中恢复。
如果段不存在异常发生在访问门描述符时,则不会伴随异常的程序状态更改。通过仅设置门描述符中的现有标志,可以从此异常中恢复。
如果在任务切换期间发生段不存在异常,则它可能发生在提交到新任务点之前或之后(请参见第7.3节“任务切换”)。如果它发生在提交点之前,则不会发生程序状态更改。如果它发生在提交点之后,则处理器将在生成异常之前从新TSS中加载所有状态信息(而不执行任何其他限制、现有或类型检查)。段不存在异常处理程序不应该依赖于能够使用在CS、SS、DS、ES、FS和GS寄存器中找到的段选择器而不引起另一个异常。 (有关如何处理此情况的其他信息,请参见本章中的“中断10 - 无效TSS异常(#TS)”中的程序状态更改描述。)
中断12——堆栈故障异常 (#SS)
-
异常类别:
故障。 -
描述:
指示检测到以下堆栈相关条件之一:
• 在引用SS寄存器的操作中检测到限制违规。可能导致限制违规的操作包括面向堆栈的指令,如POP、PUSH、CALL、RET、IRET、ENTER和LEAVE,以及其他隐式或显式使用SS寄存器的内存引用(例如,MOV AX,[BP+6]或MOV AX,SS:[EAX+6])。当没有足够的堆栈空间用于分配局部变量时,ENTER指令会生成此异常。
• 在加载SS寄存器时检测到不存在的堆栈段。在执行任务切换、不同特权级别的CALL指令、返回到不同特权级别、LSS指令或MOV或POP指令到SS寄存器时,可能会发生此违规。
• 在64位模式下,在使用包含非规范内存地址的堆栈指针寄存器引用内存的操作期间检测到规范违规。
在这种故障情况下,可以通过扩展堆栈段的限制(在限制违规的情况下)或将缺失的堆栈段加载到内存中(在不存在违规的情况下)来恢复。
在软件故意引起的规范违规的情况下,可以通过将正确的规范值加载到RSP中来恢复。否则,RSP中地址的规范违规可能反映了软件中的某些寄存器损坏。 -
异常错误代码:
如果异常是由不存在的堆栈段或在特权级别调用期间新堆栈溢出引起的,则错误代码包含导致异常的段选择器。在这里,异常处理程序可以测试由段选择器指向的段描述符中的存在标志以确定异常的原因。对于正常的限制违规(在已使用的堆栈段上),错误代码设置为0。 -
保存的指令指针:
CS和EIP寄存器的保存内容通常指向生成异常的指令。但是,在任务切换期间尝试加载不存在的堆栈段导致异常时,CS和EIP寄存器指向新任务的第一条指令。 -
程序状态更改:
堆栈故障异常通常不会伴随程序状态更改,因为生成故障的指令不会执行。在此处,异常处理程序纠正堆栈故障条件后,可以重新启动指令。
如果在任务切换期间发生堆栈故障,则在提交到新任务点之后发生(请参见第7.3节“任务切换”)。在此处,处理器在生成异常之前从新TSS加载所有状态信息(不执行任何其他限制、存在或类型检查)。因此,堆栈故障处理程序不应依赖于能够使用CS、SS、DS、ES、FS和GS寄存器中找到的段选择器而不引发另一个异常。在尝试恢复新任务之前,异常处理程序应检查所有段寄存器;否则,可能会在更难诊断的情况下稍后导致一般保护故障。 (有关如何处理此情况的其他信息,请参见本章中的“中断10——无效TSS异常(#TS)”中的程序状态更改描述。)
中断13 - 通用保护异常 (#GP)
-
异常类别:
故障。 -
描述:
表示处理器检测到一类称为“通用保护违规”的保护违规。
导致生成此异常的条件包括所有不会导致其他异常生成的保护违规(例如,无效的TSS、段不存在、堆栈故障或页面故障异常)。以下条件会导致生成通用保护异常:
• 访问CS、DS、ES、FS或GS段时超出段限制。
• 在引用描述符表时超出段限制(除了在任务切换或堆栈切换期间)。
• 转移到不可执行的段上执行。
• 写入代码段或只读数据段。
• 从只执行代码段中读取。
• 将SS寄存器加载为只读段的段选择器(除非在任务切换期间选择器来自TSS,在这种情况下会发生无效的TSS异常)。
• 将SS、DS、ES、FS或GS寄存器加载为系统段的段选择器。
• 将DS、ES、FS或GS寄存器加载为只执行代码段的段选择器。
• 将SS寄存器加载为可执行段或空段选择器的段选择器。
• 将CS寄存器加载为数据段或空段选择器的段选择器。
• 在DS、ES、FS或GS寄存器包含空段选择器时访问内存。
• 在调用或跳转到TSS期间切换到繁忙任务。
• 在非IRET任务切换上使用指向当前LDT中TSS描述符的段选择器的段选择器。TSS描述符只能驻留在GDT中。此条件在IRET任务切换期间会导致#TS异常。
• 违反第5章“保护”中描述的任何特权规则。
• 超出15字节的指令长度限制(只有在指令之前放置冗余前缀时才会发生)。
• 将CR0寄存器加载为设置PG标志(启用分页)和清除PE标志(禁用保护)。
• 将CR0寄存器加载为设置NW标志和清除CD标志。
• 引用IDT中不是中断、陷阱或任务门的条目(在中断或异常之后)。
• 在虚拟8086模式下从中断或陷阱门访问中断或异常处理程序时,处理程序的代码段DPL大于0。
• 当CPL不等于0时尝试执行特权指令(请参见第5.9节“特权指令”以获取特权指令列表)。
• 当CR4.UMIP = 1且CPL不等于0时,尝试执行SGDT、SIDT、SLDT、SMSW或STR。
• 在MSR中写入保留位。
• 访问包含空段选择器的门。
• 在CPL大于所引用的中断、陷阱或任务门的DPL时执行INT n指令。
• 调用、中断或陷阱门中的段选择器不指向代码段。
• LLDT指令中的段选择器操作数是本地类型(TI标志已设置)或不指向LDT类型的段描述符。
• LTR指令中的段选择器操作数是本地的或指向不可用的TSS。
• 调用、跳转或返回的目标代码段选择器为空。
• 如果控制寄存器CR4中的PAE和/或PSE标志已设置,并且处理器检测到分页目录指针表条目中的任何保留位设置为1。这些位在写入导致重新加载页目录指针表条目的控制寄存器CR0、CR3或CR4时进行检查。
• 尝试将非零值写入MXCSR寄存器的保留位。
• 在执行需要16字节对齐的指令时,尝试访问未对齐的128位内存位置,该指令也适用于堆栈段。
程序或任务可以在任何通用保护异常后重新启动。如果异常发生在尝试调用中断处理程序时,则中断可能会丢失,但中断程序可以重新启动。
-
异常错误代码
处理器将错误代码推送到异常处理程序的堆栈上。如果在加载段描述符时检测到故障条件,则错误代码包含描述符的段选择器或IDT向量号;否则,错误代码为0。错误代码中选择器的来源可能是以下任何一种:
• 指令的操作数。
• 来自指令操作数的门的选择器。
• 参与任务切换的TSS中的选择器。
• IDT向量号。 -
保存的指令指针
保存的CS和EIP寄存器内容指向生成异常的指令。 -
程序状态更改
通常情况下,通用保护异常不会伴随程序状态更改,因为无效的指令或操作不会被执行。异常处理程序可以设计为纠正所有导致通用保护异常的条件,并在不丢失程序连续性的情况下重新启动程序或任务。
如果通用保护异常发生在任务切换期间,则可能发生在提交新任务点之前或之后(请参见第7.3节“任务切换”)。如果它发生在提交点之前,则不会发生程序状态更改。如果它发生在提交点之后,则处理器将在生成异常之前从新TSS加载所有状态信息(而不执行任何其他限制、存在或类型检查)。因此,通用保护异常处理程序不应依赖于能够使用在CS、SS、DS、ES、FS和GS寄存器中找到的段选择器而不会引发另一个异常。 (有关如何处理此情况的其他信息,请参见本章中“中断10 - 无效TSS异常(#TS)”的程序状态更改描述。)
64位模式下的通用保护异常
以下条件会在64位模式下导致通用保护异常:
• 如果内存地址处于非规范形式。
• 如果段描述符内存地址处于非规范形式。
• 如果调用或jmp的目标偏移量的目标偏移量处于非规范形式。
• 如果代码段或64位调用门重叠非规范空间。
• 如果64位门中选择器指向的代码段描述符没有设置L位且清除了D位。
• 如果IRET中EFLAGS.NT位已设置。
• 如果返回兼容模式时IRET的堆栈段选择器为空。
• 如果返回CPL3和64位模式时IRET的堆栈段选择器为空。
• 如果返回非CPL3和64位模式时IRET的空堆栈段选择器RPL不等于CPL。
• 如果IRET的建议新代码段描述符同时设置了D位和L位。
• 如果目标操作数的代码段描述符指向的段描述符是代码段,并且同时设置了D位和L位。
• 如果64位调用门的段描述符位于非规范空间中。
• 如果64位调用门的DPL小于CPL或小于64位调用门的RPL。
• 如果64位调用门的上64位的类型字段不为0。
• 如果在兼容模式下尝试将空选择器加载到SS寄存器中。
• 如果在CPL3和64位模式下尝试将空选择器加载到SS寄存器中。
• 如果在RPL不等于CPL的非CPL3和64位模式下尝试将空选择器加载到SS寄存器中。
• 如果IRET的建议新代码段描述符同时设置了D位和L位。
• 如果目标操作数中段选择器所指向的段描述符是代码段,并且同时设置了D位和L位。
中断14——页面故障异常(#PF)
-
异常类别:
故障。 -
描述:
当启用分页(CR0寄存器中的PG标志被设置)时,处理器在使用页翻译机制将线性地址转换为物理地址时,检测到以下情况之一:
• 用于地址转换的页目录或页表条目中的P(存在)标志被清除,表明页表或包含操作数的页面不存在于物理内存中。
• 程序没有足够的特权来访问指定的页面(即,运行在用户模式下的程序尝试访问超级用户模式页面)。如果CR4中设置了SMAP标志,则代码在超级用户模式下运行,尝试访问用户模式地址的数据也可能触发页面故障。如果CR4中设置了PKE标志或PKS标志,则保护密钥权限寄存器可能会导致对具有某些保护密钥的线性地址的数据访问引发页面故障。
• 运行在用户模式下的代码尝试写入只读页面。如果CR0中设置了WP标志,则代码在超级用户模式下尝试写入只读页面也会触发页面故障。
• 从线性地址获取指令时,将其转换为具有执行禁用位设置的内存页面中的物理地址(有关执行禁用位的信息,请参见第4章“分页”)。如果CR4中设置了SMEP标志,则代码在超级用户模式下尝试从用户模式地址获取指令也会触发页面故障。
• 分页结构条目中的一个或多个保留位被设置为1。请参见下面的RSVD错误代码标志的描述。
• 对非影子堆栈页面进行了影子堆栈访问。请参见《Intel® 64和IA-32体系结构软件开发人员手册》第1卷第18.2节“影子堆栈”和第4.6节“访问权限”。
• 一个飞地访问违反了指定的访问控制要求。请参见第37章“飞地访问控制和数据结构”的第37.3节“访问控制要求”和第37.20节“飞地页面缓存映射(EPCM)”。在这种情况下,异常称为SGX引起的页面故障。处理器使用错误代码(下面)来区分SGX引起的页面故障和普通页面故障。
异常处理程序可以从页面不存在的情况中恢复,并在不丢失程序连续性的情况下重新启动程序或任务。它也可以在特权违规后重新启动程序或任务,但导致特权违规的问题可能无法纠正。
另请参见:第4.7节“页面故障异常”。
- 异常错误代码:
是(特殊格式)。处理器向页面故障处理程序提供两个信息以帮助诊断异常并从中恢复:
• 栈上的错误代码。页面故障的错误代码格式与其他异常的错误代码格式不同(请参见图6-11)。处理器按以下方式设置错误代码中的位:
— P标志(位0)。
如果线性地址没有翻译,因为用于翻译该地址的分页结构条目中的P标志为0,则此标志为0。
— W/R标志(位1)。
如果导致页面故障异常的访问是写入,则此标志为1;否则为0。此标志描述导致页面故障异常的访问,而不是由分页指定的访问权限。
— U/S标志(位2)。
如果用户模式访问导致页面故障异常,则此标志为1;如果超级用户模式访问导致页面故障异常,则此标志为0。此标志描述导致页面故障异常的访问,而不是由分页指定的访问权限。
— RSVD标志(位3)。
如果线性地址没有翻译,因为用于翻译该地址的分页结构条目中设置了保留位,则此标志为1。
— I/D标志(位4)。
如果导致页面故障异常的访问是指令获取,则此标志为1。此标志描述导致页面故障异常的访问,而不是由分页指定的访问权限。
— PK标志(位5)。
如果导致页面故障异常的访问是对具有保护密钥的线性地址的数据访问,并且保护密钥权限寄存器禁止访问,则此标志为1。
— SS标志(位6)。
如果导致页面故障异常的访问是影子堆栈访问(包括飞地模式下的影子堆栈访问),则此标志为1;否则为0。此标志描述导致页面故障异常的访问,而不是由分页指定的访问权限。
— SGX标志(位15)。
如果异常与分页无关,并且由于违反了SGX特定的访问控制要求而导致,则此标志为1。因为只有在没有普通页面故障的情况下才会发生这种违规,所以只有在P标志(位0)为1且RSVD标志(位3)和PK标志(位5)都为0时才设置此标志。
有关页面故障异常及其产生的错误代码的更多信息,请参见第4.6节“访问权限”和第4.7节“页面故障异常”。
• CR2寄存器的内容。处理器使用32位线性地址将CR2寄存器加载为生成异常的地址。页面故障处理程序可以使用此地址来定位相应的页目录和页表条目。在页面故障处理程序执行期间,可能会发生另一个页面故障;处理程序应在第二个页面故障发生之前保存CR2寄存器的内容。如果页面故障是由页面级保护违规引起的,则在发生故障时,页目录条目中的访问标志被设置。关于IA-32处理器在相应的页表条目中的访问标志的行为是特定于模型的,而不是体系结构定义的。
- 保存的指令指针:
保存的CS和EIP寄存器的内容通常指向生成异常的指令。如果页面故障异常发生在任务切换期间,则CS和EIP寄存器可能指向新任务的第一条指令(如下面的“程序状态更改”部分所述)。
- 程序状态改变
通常情况下,页面故障异常不会伴随着程序状态的改变,因为导致异常的指令并没有被执行。在页面故障异常处理程序纠正了违规行为(例如,将缺失的页面加载到内存中)之后,程序或任务的执行可以恢复。
当任务切换期间发生页面故障异常时,程序状态可能会发生以下变化。在任务切换期间,页面故障异常可能会发生在以下任何操作期间:
• 在将原始任务的状态写入该任务的TSS时。
• 在读取GDT以定位新任务的TSS描述符时。
• 在读取新任务的TSS时。
• 在从新任务读取与段选择器相关联的段描述符时。
• 在读取新任务的LDT以验证存储在新TSS中的段寄存器时。
在最后两种情况下,异常发生在新任务的上下文中。指令指针指向新任务的第一条指令,而不是导致任务切换的指令(或在中断的情况下执行的最后一条指令)。如果操作系统的设计允许在任务切换期间发生页面故障,则页面故障处理程序应通过任务门调用。
如果在任务切换期间发生页面故障,则处理器将在生成异常之前从新TSS中加载所有状态信息(而不执行任何其他限制、存在或类型检查)。因此,页面故障处理程序不应依赖于能够使用在CS、SS、DS、ES、FS和GS寄存器中找到的段选择器而不引起另一个异常。 (有关如何处理此情况的更多信息,请参见本章中“中断10 - 无效TSS异常(#TS)”的“程序状态更改”描述。)
- 其他异常处理信息
应特别注意确保在显式堆栈切换期间发生的异常不会导致处理器使用无效的堆栈指针(SS:ESP)。为16位IA-32处理器编写的软件通常使用一对指令来切换到新堆栈,例如:
MOV SS,AX
MOV SP,StackTop
在32位IA-32处理器之一上执行此代码时,可能会在将段选择器加载到SS寄存器中但在加载ESP寄存器之前发生页面故障、通用保护故障(#GP)或对齐检查故障(#AC)。此时,堆栈指针的两个部分(SS和ESP)是不一致的。新的堆栈段正在与旧的堆栈指针一起使用。
如果异常处理程序切换到一个定义良好的堆栈(即处理程序是任务或更高特权级的过程),处理器不会使用不一致的堆栈指针。但是,如果异常处理程序在相同的特权级别和来自同一任务,则处理器将尝试使用不一致的堆栈指针。
在处理页面故障、通用保护或对齐检查异常的系统中(使用陷阱或中断门处理),与异常处理程序相同特权级别的软件应使用LSS指令而不是一对MOV指令来初始化新堆栈,如本注释中早先所述。
当异常处理程序在特权级0(正常情况)下运行时,问题仅限于在特权级0下运行的过程或任务,通常是操作系统的内核。
当检测到页面错误时,处理器会更新CR2。如果在传递早期页面错误时发生第二个页面错误,则第二个错误的故障线性地址将覆盖CR2的内容(替换先前的地址)。即使页面错误导致双重错误或在传递双重错误期间发生,也会更新CR2。
中断 16—x87 FPU 浮点错误 (#MF)
-
异常类
故障。 -
描述
表示 x87 FPU 检测到浮点错误。寄存器 CR0 中的 NE 标志必须设置为生成中断 16 (浮点错误异常)。 (有关 NE 标志的详细说明,请参见“控制寄存器”一节。)
注意: SIMD 浮点异常 (#XM) 通过中断 19 发出。
在执行 x87 FPU 指令时,x87 FPU 检测并报告六种浮点错误条件:
• 无效操作 (#I)
— 栈溢出或下溢 (#IS)
— 无效算术操作 (#IA)
• 除以零 (#Z)
• 非规格化操作数 (#D)
• 数字溢出 (#O)
• 数字下溢 (#U)
• 不精确结果 (精度) (#P)
每个错误条件都代表一个 x87 FPU 异常类型,对于每个异常类型,x87 FPU 在 x87 FPU 状态寄存器中提供一个标志和在 x87 FPU 控制寄存器中提供一个掩码位。如果 x87 FPU 检测到浮点错误并且异常类型的掩码位被设置,x87 FPU 通过生成预定义的 (默认) 响应并继续程序执行来自动处理异常。默认响应已经设计为为大多数浮点应用程序提供合理的结果。
如果异常的掩码被清除并且寄存器 CR0 中的 NE 标志被设置,x87 FPU 将执行以下操作:
- 在 FPU 状态寄存器中设置必要的标志。
- 等待程序指令流中遇到下一个“等待”x87 FPU 指令或 WAIT/FWAIT 指令。
- 生成一个内部错误信号,导致处理器生成浮点异常 (#MF)。
在执行等待的 x87 FPU 指令或 WAIT/FWAIT 指令之前,x87 FPU 检查待处理的 x87 FPU 浮点异常 (如上述步骤 2 所述)。对于“非等待”x87 FPU 指令,包括 FNINIT、FNCLEX、FNSTSW、FNSTSW AX、FNSTCW、FNSTENV 和 FNSAVE 指令,忽略待处理的 x87 FPU 异常。在执行状态管理指令 FXSAVE 和 FXRSTOR 时,也忽略待处理的 x87 FPU 异常。
所有 x87 FPU 浮点错误条件都可以恢复。x87 FPU 浮点错误异常处理程序可以从 x87 FPU 状态字中的标志设置确定导致异常的错误条件。有关处理 x87 FPU 浮点异常的更多信息,请参见《Intel® 64 和 IA-32 架构软件开发人员手册》第 1 卷第 8 章中的“软件异常处理”。
- 异常错误代码
无。x87 FPU 提供自己的错误信息。
- 保存的指令指针
保存的 CS 和 EIP 寄存器内容指向即将执行的浮点或 WAIT/FWAIT 指令,在生成浮点错误异常时。这不是检测到错误条件的故障指令。故障指令的地址包含在 x87 FPU 指令指针寄存器中。有关 FPU 保存用于处理浮点错误异常的信息的更多信息,请参见《Intel® 64 和 IA-32 架构软件开发人员手册》第 1 卷第 8 章中的“x87 FPU 指令和数据 (操作数) 指针”一节。
- 程序状态更改
由于处理异常被延迟到故障指令后的下一个等待 x87 FPU 浮点或 WAIT/FWAIT 指令,因此程序状态更改通常伴随着 x87 FPU 浮点异常。然而,x87 FPU 保存了有关错误条件的足够信息,以允许从错误中恢复并重新执行故障指令 (如果需要)。
在非 x87 FPU 浮点指令依赖于 x87 FPU 浮点指令的结果的情况下,可以在依赖指令前插入 WAIT 或 FWAIT 指令,以强制处理待处理的 x87 FPU 浮点异常,然后再执行依赖指令。有关 x87 浮点错误异常同步的更多信息,请参见《Intel® 64 和 IA-32 架构软件开发人员手册》第 1 卷第 8 章中的“x87 FPU 异常同步”一节。
中断17 - 对齐检查异常(#AC):
-
异常类别:
故障。 -
描述:
当启用对齐检查时,指示处理器检测到未对齐的内存操作数。对齐检查仅在数据(或堆栈)访问中执行(不在代码获取或系统段访问中执行)。对齐检查违规的一个例子是在奇数字节地址存储的字,或在不是4的整数倍地址存储的双字。表6-7列出了处理器识别的各种数据类型的对齐要求。
请注意,对齐检查异常(#AC)仅针对必须在字、双字和四字边界上对齐的数据类型生成。对于未对齐在16字节边界上的128位数据类型,将生成一般保护异常(#GP)。
要启用对齐检查,必须满足以下条件:
• CR0寄存器中的AM标志已设置。
• EFLAGS寄存器中的AC标志已设置。
• CPL为3(包括虚拟8086模式)。
对齐检查异常(#AC)仅在特权级3(用户模式)下操作时生成。默认为特权级0的内存引用,例如段描述符加载,即使由特权级3引起的内存引用也不会生成对齐检查异常。
在特权级3时将GDTR、IDTR、LDTR或任务寄存器的内容存储在内存中可能会生成对齐检查异常。虽然应用程序通常不会存储这些寄存器,但可以通过将存储的信息对齐到偶数字地址来避免故障。
FXSAVE/XSAVE和FXRSTOR/XRSTOR指令保存和恢复一个512字节的数据结构,其中第一个字节必须对齐到16字节边界。如果在执行这些指令时启用了对齐检查异常(#AC)(且CPL为3),则未对齐的内存操作数可能会导致对齐检查异常或一般保护异常(#GP),具体取决于处理器实现(请参阅Intel® 64和IA-32体系结构软件开发人员手册第2A卷第3章中的“FXSAVE-保存x87 FPU、MMX、SSE和SSE2状态”和“FXRSTOR-恢复x87 FPU、MMX、SSE和SSE2状态”,以及第2C卷第5章中的“XSAVE-保存处理器扩展状态”和“XRSTOR-恢复处理器扩展状态”)。
MOVDQU、MOVUPS和MOVUPD指令执行128位未对齐的加载或存储。LDDQU指令加载128位未对齐的数据。当操作数未对齐在16字节边界上时,它们不会生成一般保护异常(#GP)。如果启用了对齐检查,则当数据地址未对齐在8字节边界上时,根据处理器实现,可能会生成对齐检查异常(#AC)。
FSAVE和FRSTOR指令可能会生成未对齐引用,从而导致对齐检查故障。这些指令很少被应用程序使用。
-
异常错误代码:
是的。错误代码为空;所有位都清除,除了可能的第0位-EXT;请参阅第6.13节。如果在传递除软件中断以外的事件时识别到#AC,则设置EXT(请参阅Intel® 64和IA-32体系结构软件开发人员手册第2A卷第3章中的“INT n/INTO/INT3/INT1-调用中断过程”)。 -
保存的指令指针:
保存的CS和EIP寄存器内容指向生成异常的指令。 -
程序状态更改:
对齐检查故障不会伴随程序状态更改,因为指令未执行。
中断18 - 机器检查异常 (#MC)
-
异常类别:
中止。 -
描述:
表示处理器检测到内部机器错误或总线错误,或者外部代理检测到总线错误。机器检查异常是特定于型号的,适用于 Pentium 和后代处理器。机器检查异常的实现在不同的处理器系列之间是不同的,这些实现可能与未来的 Intel 64 或 IA-32 处理器不兼容。(使用 CPUID 指令确定是否存在此功能。)
外部代理检测到的总线错误通过专用引脚向处理器发出信号:Pentium 4、Intel Xeon 和 P6 家族处理器上的 BINIT# 和 MCERR# 引脚以及 Pentium 处理器上的 BUSCHK# 引脚。当这些引脚之一被启用时,断言该引脚会导致错误信息被加载到机器检查寄存器中,并生成机器检查异常。
机器检查异常和机器检查体系结构在第15章“机器检查体系结构”中详细讨论。此外,有关处理器特定硬件信息,请参阅各个处理器的数据手册。
-
异常错误代码:
无。机器检查 MSRs 提供错误信息。 -
保存的指令指针:
对于 Pentium 4 和 Intel Xeon 处理器,扩展机器检查状态寄存器的保存内容直接与导致生成机器检查异常的错误相关联(参见第15.3.1.2节“IA32_MCG_STATUS MSR”和第15.3.2.6节“IA32_MCG 扩展机器检查状态 MSRs”)。
对于 P6 家族处理器,如果 MCG_STATUS MSR 中的 EIPV 标志被设置,则 CS 和 EIP 寄存器的保存内容直接与导致生成机器检查异常的错误相关联;如果该标志被清除,则保存的指令指针可能与错误不相关(参见第15.3.1.2节“IA32_MCG_STATUS MSR”)。
对于 Pentium 处理器,CS 和 EIP 寄存器的内容可能与错误不相关。
程序状态更改:
通过在控制寄存器 CR4 中设置 MCE 标志来启用机器检查机制。
对于 Pentium 4、Intel Xeon、P6 家族和 Pentium 处理器,机器检查异常始终伴随着程序状态更改,并生成中止类异常。对于中止异常,可以从机器检查 MSRs 中收集有关异常的信息,但通常无法重新启动程序。
如果未启用机器检查机制(控制寄存器 CR4 中的 MCE 标志被清除),则机器检查异常会导致处理器进入关机状态。
中断19 - SIMD浮点异常(#XM)
-
异常类
故障。 -
描述
指示处理器检测到SSE / SSE2 / SSE3 SIMD浮点异常。必须设置MXCSR寄存器中的适当状态标志并取消屏蔽特定异常,才能生成此中断。执行SSE / SSE2 / SSE3 SIMD浮点指令时可能发生六类数字异常条件:
•无效操作(#I)
•除以零(#Z)
•非规格化操作数(#D)
•数字溢出(#O)
•数字下溢(#U)
•不精确的结果(精度)(#P)
无效操作,除以零和非规格化操作数异常是预计算异常;也就是说,在任何算术操作发生之前就会检测到它们。数字下溢,数字溢出和不精确的结果异常是后计算异常。
有关SIMD浮点异常类的详细信息,请参见Intel® 64和IA-32体系结构软件开发人员手册第1卷第11章中的“SIMD浮点异常”。
当发生SIMD浮点异常时,处理器会执行以下操作之一:
•通过产生最合理的结果并允许程序继续执行来自动处理异常。这是对掩码异常的响应。
•生成SIMD浮点异常,从而调用软件异常处理程序。这是对未屏蔽异常的响应。
每个六个SIMD浮点异常条件在MXCSR寄存器中都有相应的标志位和掩码位。如果异常被屏蔽(MXCSR寄存器中的相应掩码位被设置),处理器将采取适当的自动默认操作并继续计算。如果异常未被屏蔽(相应的掩码位被清除)并且操作系统支持SIMD浮点异常(控制寄存器CR4中的OSXMMEXCPT标志被设置),则通过SIMD浮点异常调用软件异常处理程序。如果异常未被屏蔽且OSXMMEXCPT位被清除(表示操作系统不支持未屏蔽的SIMD浮点异常),则会发出无效操作码异常(#UD),而不是SIMD浮点异常。
请注意,由于SIMD浮点异常是精确的并立即发生,因此不会出现x87 FPU指令,WAIT / FWAIT指令或另一个SSE / SSE2 / SSE3指令捕获未决未屏蔽的SIMD浮点异常的情况。
在SIMD浮点异常被屏蔽时发生了SIMD浮点异常的情况下(导致相应的异常标志被设置),并且在随后取消屏蔽SIMD浮点异常时,当异常被取消屏蔽时不会生成异常。
当SSE / SSE2 / SSE3 SIMD浮点指令对打包操作数(由两个或四个子操作数组成)进行操作时,可能会检测到多个SIMD浮点异常条件。如果对于一个或多个子操作数集,检测到不超过一个异常条件,则为每个检测到的异常条件设置异常标志。例如,对于一个子操作数检测到的无效异常不会阻止报告另一个子操作数的除以零异常。但是,当为一个子操作数生成两个或多个异常条件时,仅报告一个异常条件,根据表6-8所示的优先级。
注:
1.虽然QNaN不是异常,但处理QNaN操作数的方式优先于低优先级异常。例如,QNaN除以零会导致QNaN,而不是除以零异常。
2.如果被屏蔽,则指令执行将继续,并且可能会发生较低优先级的异常。
- 异常错误代码
无。
- 保存的指令指针
保存的CS和EIP寄存器内容指向执行SIMD浮点异常时执行的SSE / SSE2 / SSE3指令。这是检测到错误条件的故障指令。
- 程序状态更改
程序状态更改不伴随SIMD浮点异常,因为异常的处理是立即的,除非特定异常被屏蔽。可用的状态信息通常足以允许从错误中恢复并重新执行故障指令(如果需要)。
中断20 - 虚拟化异常 (#VE)
-
异常类
故障。 -
描述
表示处理器在VMX非根操作中检测到EPT违规。并非所有EPT违规都会导致虚拟化异常。有关详细信息,请参见第25.5.7.2节。
异常处理程序可以从EPT违规中恢复并重新启动程序或任务,而不会丢失程序连续性。但是,在某些情况下,导致EPT违规的问题可能是无法纠正的。 -
异常错误代码
无。 -
保存的指令指针
CS和EIP寄存器的保存内容通常指向生成异常的指令。 -
程序状态更改
虚拟化异常通常不会伴随程序状态更改,因为生成异常的指令不会被执行。在虚拟化异常处理程序纠正违规(例如通过执行EPTP切换VM函数)之后,可以恢复程序或任务的执行。
其他异常处理信息
处理器在虚拟化异常信息区域中保存有关虚拟化异常的信息。有关详细信息,请参见第25.5.7.2节。
中断21 - 控制保护异常 (#CP)
-
异常类
故障。 -
描述
表示控制流转移尝试违反了控制流执行技术的约束。 -
异常错误代码
是(特殊格式)。处理器通过堆栈上的错误代码向控制保护异常处理程序提供以下信息。
• 位14:0 - CPEC
— 1 - NEAR-RET:表示#CP是由近RET指令引起的。
— 2 - FAR-RET / IRET:表示#CP是由FAR RET或IRET指令引起的。
— 3 - ENDBRANCH:表示#CP是由间接调用或跳转指令目标处缺少ENDBRANCH引起的。
— 4 - RSTORSSP:表示#CP是由RSTORSSP指令中的影子栈恢复令牌检查失败引起的。
— 5- SETSSBSY:表示#CP是由SETSSBSY指令中的监管者影子栈令牌检查失败引起的。
• 如果错误代码的第15位(ENCL)设置为1,则表示#CP发生在飞地执行期间。
-
保存的指令指针
CS和EIP寄存器的保存内容通常指向生成异常的指令。 -
程序状态更改
控制保护异常通常不会伴随程序状态更改,因为生成异常的指令不会被执行。
当在任务切换期间生成控制保护异常时,程序状态可能会发生以下更改。
在任务切换期间,控制保护异常可能会在以下任何操作期间发生:
• 如果任务切换由IRET发起,则旧任务影子栈上存储的CS和LIP与新任务的CS和LIP不匹配(其中LIP是返回地址的线性地址)。
• 如果任务切换由IRET发起,并且新任务的SSP从旧任务的影子栈中加载(如果新任务的CPL < 3),或者从IA32_PL3_SSP加载SSP(如果新任务的CPL = 3)不对齐为4字节或超过4GB的值。
在这些情况下,异常发生在新任务的上下文中。指令指针指向新任务的第一条指令,而不是导致任务切换的指令(或在中断的情况下执行的最后一条指令)。如果操作系统的设计允许在任务切换期间发生控制保护故障,则应通过任务门调用控制保护故障处理程序。
中断 32 到 255 - 用户定义中断的翻译
-
异常类别
不适用。 -
描述
指示处理器执行了以下操作之一:
• 执行一个 INT n 指令,其中指令操作数是从 32 到 255 的向量号之一。
• 响应 INTR 引脚或本地 APIC 发出的中断请求,当与请求相关联的中断向量号为 32 到 255 时。 -
异常错误代码
不适用。 -
保存的指令指针
CS 和 EIP 寄存器的保存内容指向 INT n 指令后的指令或 INTR 信号发生的指令后面的指令。 -
程序状态更改
由 INT n 指令或 INTR 信号生成的中断不会伴随程序状态更改。INT n 指令在指令流中生成中断。当处理器接收到 INTR 信号时,在响应中断之前,它会提交所有先前指令的状态更改;因此,在从中断处理程序返回时,程序执行可以恢复。
本文来自博客园,作者:dolinux,未经同意,禁止转载