Date: Sat, 8 Feb 2014 05:04:34 +0000 (UTC) From: Neel Natu <neel@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r261617 - head/sys/amd64/vmm/intel Message-ID: <201402080504.s1854YeY000712@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: neel Date: Sat Feb 8 05:04:34 2014 New Revision: 261617 URL: http://svnweb.freebsd.org/changeset/base/261617 Log: 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. Modified: head/sys/amd64/vmm/intel/vmx.c Modified: head/sys/amd64/vmm/intel/vmx.c ============================================================================== --- head/sys/amd64/vmm/intel/vmx.c Sat Feb 8 04:29:36 2014 (r261616) +++ head/sys/amd64/vmm/intel/vmx.c Sat Feb 8 05:04:34 2014 (r261617) @@ -1719,19 +1719,10 @@ vmx_exit_process(struct vmx *vmx, int vc 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 *vmxct 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 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 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);
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201402080504.s1854YeY000712>