aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorNeel Natu <neel@FreeBSD.org>2014-01-17 04:21:39 +0000
committerNeel Natu <neel@FreeBSD.org>2014-01-17 04:21:39 +0000
commit160471d264f71257665d258ca4d6b49783fbede3 (patch)
tree8a4c6fba8af0f7c8269e5e402537fe850315a466 /sys
parent6f17dec9a117e30d61ac694f2c333150073d0bd8 (diff)
downloadsrc-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.h8
-rw-r--r--sys/amd64/vmm/intel/vmx.c30
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);