相关SDM章节: 27.2.3- Information About NMI Unblocking Due to IRET
最近收到同事发来的一个问题,即:
硬件逻辑
通过查阅 SDM,可以获得以下信息:
IRET
指令的执行可能会导致 vmexit,vmexit 的原因可能为:faults,EPT violation,page-modification log-full events,或者 SPP-related events.如果在
IRET
执行时,NMI处于被block状态
,即 Guest Interruptibililty State 的 bit3-Blocking by NMI 为1。那么IRET的执行会导致NMI被解除block,也就是将 Blocking by NMI从1设置为0. 即使 IRET 本身导致了fault 或 vmexit,unblock 的硬件行为
不会发生变化。1中所述的所有 vmexit,都会在 VM-exit Interrupt-information field / VM-exit qualification 的 bit12 提供 NMI unblocking due to IRET 信息,指示本次vmexit 是由 IRET 导致,并且 IRET unblock 了NMI,也就是 IRET 指令的执行导致了 Guest Interruptibility field 的 bit3 Blocking by NMI 从1 变为了0.
但是,NMI unblocking due to IRET 还需要其它条件辅助才能成立。
- "NMI-exiting" control 为0,或 "virtual NMIs" control 为1
- vmexit
没有写
IDT-vectoring information field 的 valid bit 为 1 - vmexit 不是由 #DF(double fault) 异常导致的
在这三个条件同时成立的情况下,IRET 导致的 vmexit 会 Unblock NMI,也就是将 NMI unblocking due to IRET 写1.
- 如果 IRET 指令的一部分为memory access,"virtual NMIs" control 为0,NMI 在IRET指令执行之前就已经被block 了,那么 NMI unblocking due to IRET 会被写1.
- 如果 IRET 指令的一部分为memory access,"virtual NMIs" control 为1,且 virtual-NMI 在IRET指令执行之前就已经被 block了,那么 NMI unblocking due to IRET 也会被写1.
IRET也可能会导致 APIC-access vmexit,EPT misconfigrations,但这类 vmexit 不会携带 NMI unblocking due to IRET 信息。
具体的逻辑如下图所示,红色部分会最终将 NMI unblocking due to IRET 信息存储到 VM-exit Interrupt-information field 中。绿色部分最终会将 NMI unblocking due to IRET 存储到 VM-exit qualification field 中。
软件逻辑
- 每次 vmexit 时
软件逻辑很简单,在每次 vmexit 时,均会查看上图中菱形框中的条件是否满足,如果满足,就直接对Guest Interruptibility state 的 bit3,也就是 Blocking by NMI 写1,以在下次 vmentry 之前屏蔽掉 NMI。这里隐含着一个软件逻辑:由于 IRET 导致的 vmexit 会修改 Blocking by NMI,但这是不合理的,因此需要在由 IRET 导致的 vmexit 产生后,重新修改Blocking by NMI,使其能够正常block NMI。
另一方面,如果菱形框中的条件不满足,就正常读取 Blocking by NMI 的值确定是否需要对 NMI 进行屏蔽。
- 处理 EPT violation vmexit 或 PML_FULL vmexit 时
逻辑与每次 vmexit
时相同。但这里读取的NMI unblocking due to IRET 信息来自vmexit qualification,而不是 每次 vmexit
时的 vmexit interrupt information。
总结
Guest 执行 IRET 会导致本来处于"Blocking NMI"的Guest环境变为"not Blocking NMI",这是硬件逻辑的不合理之处,软件应该在检测到由 IRET 导致的 unblock NMI 动作之后,在root mode 对 NMI 重新 block。