Hi,
I am recently experimenting with how VMware handles NMI interrupts in nested virtualization, and I find that the behavior of VMware is incorrect in a corner case.
The version of VMware and my host OS are:
To reproduce this bug:
The source code of the VMDK file can be found at: https://github.com/lxylxy123456/uberxmhf/blob/9eb50d71910b2586c11f92462f37096a7066502b/xmhf/src/xmhf...
Actual output (on VMware):
...
Detecting environment
End detecting environment
Experiment: 17
Enter host, exp=17, state=0
hlt_wait() begin, source = EXIT_NMI_H (5)
Inject NMI
Interrupt recorded: EXIT_NMI_H (5)
At instruction: 0x00000000
VM-exit reason: 0x00000012
hlt_wait() end
hlt_wait() begin, source = EXIT_TIMER_H (6)
Inject interrupt
Interrupt recorded: EXIT_TIMER_H (6)
hlt_wait() end
hlt_wait() begin, source = EXIT_TIMER_H (6)
Inject NMI
Inject interrupt
Interrupt recorded: EXIT_TIMER_H (6)
hlt_wait() end
Leave host
Interrupt recorded: EXIT_NMI_H (5)
At instruction: 0x00000000
VM-exit reason: 0x0000000a
CPU(0x02): key press: 65, guest=1
source: EXIT_VMEXIT (7)
exit_source: EXIT_NMI_H (5)
rip: 0x082013df
exit_rip: 0x08208488
TEST_ASSERT '0 && (exit_source == source)' failed, line 372, file lhv-guest.c
Expected output (reproducible on real Intel hardware):
...
Detecting environment
End detecting environment
Experiment: 17
Enter host, exp=17, state=0
hlt_wait() begin, source = EXIT_NMI_H (5)
Inject NMI
Interrupt recorded: EXIT_NMI_H (5)
At instruction: 0x00000000
VM-exit reason: 0x00000012
hlt_wait() end
hlt_wait() begin, source = EXIT_TIMER_H (6)
Inject interrupt
Interrupt recorded: EXIT_TIMER_H (6)
hlt_wait() end
hlt_wait() begin, source = EXIT_TIMER_H (6)
Inject NMI
Inject interrupt
Interrupt recorded: EXIT_TIMER_H (6)
hlt_wait() end
Leave host
Interrupt recorded: EXIT_VMEXIT (7)
CPU(0x01): key press: 250, guest=1
Enter host, exp=17, state=1
iret_wait() begin, source = EXIT_MEASURE (1)
iret_wait() end
Leave host
Experiment: 1
... (endless)
Explanation:
The VMDK file (d.vmdk) contains a micro-hypervisor called LHV. Assume VMware runs in L0, LHV runs in L1, the guest of LHV runs in L2.
The code in LHV performs an experiment (called "Experiment 17" in serial output) on CPU 0 to test the behavior of NMI blocking. The experiment steps are:
The expected behavior is:
However, on VMware, the behavior appears to be:
It appears that VMware's implementation is incorrect. NMI is blocked in L2, but Intel's SDM says that NMI is always unblocked in L2. Quote from Intel SDM:
The following items describe the use of bit 3 (blocking by NMI) in the interruptibility-state field if the “virtual NMIs” VM-execution control is 1:
Could you please fix this implementation problem in VMware? Thank you.