Fix a bug in the handling of VM-exits caused by non-maskable interrupts (NMI).
If a VM-exit is caused by an NMI then "blocking by NMI" is in effect on the CPU when the VM-exit is completed. No more NMIs will be recognized until the execution of an "iret". Prior to this change the NMI handler was dispatched via a software interrupt with interrupts enabled. This meant that an interrupt could be recognized by the processor before the NMI handler completed its execution. The "iret" issued by the interrupt handler would then cause the "blocking by NMI" to be cleared prematurely. This is now fixed by handling the NMI with interrupts disabled in addition to "blocking by NMI" already established by the VM-exit.
This commit is contained in:
parent
6efc5b1db6
commit
62fbd7c27a
@ -1719,19 +1719,10 @@ vmx_exit_process(struct vmx *vmx, int vcpu, struct vm_exit *vmexit)
|
||||
vmx_restore_nmi_blocking(vmx, vcpu);
|
||||
|
||||
/*
|
||||
* If the NMI-exiting VM execution control is set to '1'
|
||||
* then an NMI in non-root operation causes a VM-exit.
|
||||
* NMI blocking is in effect for this logical processor so
|
||||
* it is sufficient to simply vector to the NMI handler via
|
||||
* a software interrupt.
|
||||
* The NMI has already been handled in vmx_exit_handle_nmi().
|
||||
*/
|
||||
if ((intr_info & VMCS_INTR_T_MASK) == VMCS_INTR_T_NMI) {
|
||||
KASSERT((intr_info & 0xff) == IDT_NMI, ("VM exit due "
|
||||
"to NMI has invalid vector: %#x", intr_info));
|
||||
VCPU_CTR0(vmx->vm, vcpu, "Vectoring to NMI handler");
|
||||
__asm __volatile("int $2");
|
||||
if ((intr_info & VMCS_INTR_T_MASK) == VMCS_INTR_T_NMI)
|
||||
return (1);
|
||||
}
|
||||
break;
|
||||
case EXIT_REASON_EPT_FAULT:
|
||||
vmm_stat_incr(vmx->vm, vcpu, VMEXIT_EPT_FAULT, 1);
|
||||
@ -1874,6 +1865,36 @@ vmx_exit_inst_error(struct vmxctx *vmxctx, int rc, struct vm_exit *vmexit)
|
||||
return (UNHANDLED);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the NMI-exiting VM execution control is set to '1' then an NMI in
|
||||
* non-root operation causes a VM-exit. NMI blocking is in effect so it is
|
||||
* sufficient to simply vector to the NMI handler via a software interrupt.
|
||||
* However, this must be done before maskable interrupts are enabled
|
||||
* otherwise the "iret" issued by an interrupt handler will incorrectly
|
||||
* clear NMI blocking.
|
||||
*/
|
||||
static __inline void
|
||||
vmx_exit_handle_nmi(struct vmx *vmx, int vcpuid, struct vm_exit *vmexit)
|
||||
{
|
||||
uint32_t intr_info;
|
||||
|
||||
KASSERT((read_rflags() & PSL_I) == 0, ("interrupts enabled"));
|
||||
|
||||
if (vmexit->u.vmx.exit_reason != EXIT_REASON_EXCEPTION)
|
||||
return;
|
||||
|
||||
intr_info = vmcs_read(VMCS_EXIT_INTR_INFO);
|
||||
KASSERT((intr_info & VMCS_INTR_VALID) != 0,
|
||||
("VM exit interruption info invalid: %#x", intr_info));
|
||||
|
||||
if ((intr_info & VMCS_INTR_T_MASK) == VMCS_INTR_T_NMI) {
|
||||
KASSERT((intr_info & 0xff) == IDT_NMI, ("VM exit due "
|
||||
"to NMI has invalid vector: %#x", intr_info));
|
||||
VCPU_CTR0(vmx->vm, vcpuid, "Vectoring to NMI handler");
|
||||
__asm __volatile("int $2");
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
vmx_run(void *arg, int vcpu, register_t startrip, pmap_t pmap,
|
||||
void *rendezvous_cookie)
|
||||
@ -1949,8 +1970,6 @@ vmx_run(void *arg, int vcpu, register_t startrip, pmap_t pmap,
|
||||
vmx_run_trace(vmx, vcpu);
|
||||
rc = vmx_enter_guest(vmxctx, vmx, launched);
|
||||
|
||||
enable_intr();
|
||||
|
||||
/* Collect some information for VM exit processing */
|
||||
vmexit->rip = rip = vmcs_guest_rip();
|
||||
vmexit->inst_length = vmexit_instruction_length();
|
||||
@ -1958,12 +1977,14 @@ vmx_run(void *arg, int vcpu, register_t startrip, pmap_t pmap,
|
||||
vmexit->u.vmx.exit_qualification = vmcs_exit_qualification();
|
||||
|
||||
if (rc == VMX_GUEST_VMEXIT) {
|
||||
launched = 1;
|
||||
vmx_exit_handle_nmi(vmx, vcpu, vmexit);
|
||||
enable_intr();
|
||||
handled = vmx_exit_process(vmx, vcpu, vmexit);
|
||||
} else {
|
||||
enable_intr();
|
||||
handled = vmx_exit_inst_error(vmxctx, rc, vmexit);
|
||||
}
|
||||
|
||||
launched = 1;
|
||||
vmx_exit_trace(vmx, vcpu, rip, exit_reason, handled);
|
||||
} while (handled);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user