diff options
author | Neel Natu <neel@FreeBSD.org> | 2014-01-17 04:21:39 +0000 |
---|---|---|
committer | Neel Natu <neel@FreeBSD.org> | 2014-01-17 04:21:39 +0000 |
commit | 160471d264f71257665d258ca4d6b49783fbede3 (patch) | |
tree | 8a4c6fba8af0f7c8269e5e402537fe850315a466 /sys | |
parent | 6f17dec9a117e30d61ac694f2c333150073d0bd8 (diff) | |
download | src-160471d264f71257665d258ca4d6b49783fbede3.tar.gz src-160471d264f71257665d258ca4d6b49783fbede3.zip |
If a VM-exit happens during an NMI injection then clear the "NMI Blocking" bit
in the Guest Interruptibility-state VMCS field.
If we fail to do this then a subsequent VM-entry will fail because it is an
error to inject an NMI into the guest while "NMI Blocking" is turned on. This
is described in "Checks on Guest Non-Register State" in the Intel SDM.
Submitted by: David Reed (david.reed@tidalscale.com)
Notes
Notes:
svn path=/head/; revision=260802
Diffstat (limited to 'sys')
-rw-r--r-- | sys/amd64/vmm/intel/vmcs.h | 8 | ||||
-rw-r--r-- | sys/amd64/vmm/intel/vmx.c | 30 |
2 files changed, 26 insertions, 12 deletions
diff --git a/sys/amd64/vmm/intel/vmcs.h b/sys/amd64/vmm/intel/vmcs.h index fde93e522674..cd2f84b8978b 100644 --- a/sys/amd64/vmm/intel/vmcs.h +++ b/sys/amd64/vmm/intel/vmcs.h @@ -333,10 +333,10 @@ vmcs_write(uint32_t encoding, uint64_t val) /* * VMCS interrupt information fields */ -#define VMCS_INTR_INFO_VALID (1U << 31) -#define VMCS_INTR_INFO_TYPE(info) (((info) >> 8) & 0x7) -#define VMCS_INTR_INFO_HW_INTR (0 << 8) -#define VMCS_INTR_INFO_NMI (2 << 8) +#define VMCS_INTR_VALID (1U << 31) +#define VMCS_INTR_T_MASK 0x700 /* Interruption-info type */ +#define VMCS_INTR_T_HWINTR (0 << 8) +#define VMCS_INTR_T_NMI (2 << 8) /* * VMCS IDT-Vectoring information fields diff --git a/sys/amd64/vmm/intel/vmx.c b/sys/amd64/vmm/intel/vmx.c index a4e0a851e982..0c3717596489 100644 --- a/sys/amd64/vmm/intel/vmx.c +++ b/sys/amd64/vmm/intel/vmx.c @@ -1065,7 +1065,7 @@ vmx_inject_nmi(struct vmx *vmx, int vcpu) * Inject the virtual NMI. The vector must be the NMI IDT entry * or the VMCS entry check will fail. */ - info = VMCS_INTR_INFO_NMI | VMCS_INTR_INFO_VALID; + info = VMCS_INTR_T_NMI | VMCS_INTR_VALID; info |= IDT_NMI; vmcs_write(VMCS_ENTRY_INTR_INFO, info); @@ -1103,7 +1103,7 @@ vmx_inject_interrupts(struct vmx *vmx, int vcpu, struct vlapic *vlapic) * because of a pending AST. */ info = vmcs_read(VMCS_ENTRY_INTR_INFO); - if (info & VMCS_INTR_INFO_VALID) + if (info & VMCS_INTR_VALID) return; /* @@ -1134,7 +1134,7 @@ vmx_inject_interrupts(struct vmx *vmx, int vcpu, struct vlapic *vlapic) goto cantinject; /* Inject the interrupt */ - info = VMCS_INTR_INFO_HW_INTR | VMCS_INTR_INFO_VALID; + info = VMCS_INTR_T_HWINTR | VMCS_INTR_VALID; info |= vector; vmcs_write(VMCS_ENTRY_INTR_INFO, info); @@ -1444,10 +1444,12 @@ vmx_exit_process(struct vmx *vmx, int vcpu, struct vm_exit *vmexit) int error, handled; struct vmxctx *vmxctx; struct vlapic *vlapic; - uint32_t eax, ecx, edx, idtvec_info, idtvec_err, intr_info, reason; + uint32_t eax, ecx, edx, gi, idtvec_info, idtvec_err, intr_info, reason; uint64_t qual, gpa; bool retu; + CTASSERT((PINBASED_CTLS_ONE_SETTING & PINBASED_VIRTUAL_NMI) != 0); + handled = 0; vmxctx = &vmx->ctx[vcpu]; @@ -1480,6 +1482,18 @@ vmx_exit_process(struct vmx *vmx, int vcpu, struct vm_exit *vmexit) vmcs_write(VMCS_ENTRY_EXCEPTION_ERROR, idtvec_err); } + /* + * If 'virtual NMIs' are being used and the VM-exit + * happened while injecting an NMI during the previous + * VM-entry, then clear "blocking by NMI" in the Guest + * Interruptibility-state. + */ + if ((idtvec_info & VMCS_INTR_T_MASK) == + VMCS_INTR_T_NMI) { + gi = vmcs_read(VMCS_GUEST_INTERRUPTIBILITY); + gi &= ~VMCS_INTERRUPTIBILITY_NMI_BLOCKING; + vmcs_write(VMCS_GUEST_INTERRUPTIBILITY, gi); + } vmcs_write(VMCS_ENTRY_INST_LENGTH, vmexit->inst_length); } default: @@ -1556,8 +1570,8 @@ vmx_exit_process(struct vmx *vmx, int vcpu, struct vm_exit *vmexit) * this virtual interrupt during the subsequent VM enter. */ intr_info = vmcs_read(VMCS_EXIT_INTR_INFO); - KASSERT((intr_info & VMCS_INTR_INFO_VALID) != 0 && - VMCS_INTR_INFO_TYPE(intr_info) == 0, + KASSERT((intr_info & VMCS_INTR_VALID) != 0 && + (intr_info & VMCS_INTR_T_MASK) == VMCS_INTR_T_HWINTR, ("VM exit interruption info invalid: %#x", intr_info)); vmx_trigger_hostintr(intr_info & 0xff); @@ -2039,11 +2053,11 @@ vmx_inject(void *arg, int vcpu, int type, int vector, uint32_t code, if (error) return (error); - if (info & VMCS_INTR_INFO_VALID) + if (info & VMCS_INTR_VALID) return (EAGAIN); info = vector | (type_map[type] << 8) | (code_valid ? 1 << 11 : 0); - info |= VMCS_INTR_INFO_VALID; + info |= VMCS_INTR_VALID; error = vmcs_setreg(vmcs, 0, VMCS_IDENT(VMCS_ENTRY_INTR_INFO), info); if (error != 0) return (error); |