[PATCH 7/9] KVM: Adds support for real NMI injection on VMX processors

Gregory Haskins
2007-05-09T15:19:32Z

drivers/kvm/vmx.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++----
drivers/kvm/vmx.h | 3 +++
2 files changed, 61 insertions(+), 5 deletions(-)

diff --git a/drivers/kvm/vmx.c b/drivers/kvm/vmx.c
index 2b4a9ac..d99bbbb 100644
--- a/drivers/kvm/vmx.c
+++ b/drivers/kvm/vmx.c
@@ -1148,7 +1148,14 @@ static int vmx_vcpu_setup(struct kvm_vcpu *vcpu)
PIN_BASED_VM_EXEC_CONTROL,
PIN_BASED_EXT_INTR_MASK /* 20.6.1 */
| PIN_BASED_NMI_EXITING /* 20.6.1 */
+ | PIN_BASED_VIRTUAL_NMI /* 20.6.1 */
);
+
+ if (!(vmcs_read32(PIN_BASED_VM_EXEC_CONTROL) & PIN_BASED_VIRTUAL_NMI))
+ printk(KERN_WARNING "KVM: Warning - Host processor does " \
+ "not support virtual-NMI injection. Using IRQ " \
+ "method\n");
+
vmcs_write32_fixedbits(MSR_IA32_VMX_PROCBASED_CTLS,
CPU_BASED_VM_EXEC_CONTROL,
CPU_BASED_HLT_EXITING /* 20.6.2 */
@@ -1297,6 +1304,43 @@ static void inject_rmode_irq(struct kvm_vcpu *vcpu, int irq)
vmcs_writel(GUEST_RSP, (vmcs_readl(GUEST_RSP) & ~0xffff) | (sp - 6));
}

+static int do_nmi_requests(struct kvm_vcpu *vcpu)
+{
+ int nmi_window = 0;
+
+ BUG_ON(!(test_bit(kvm_irqpin_nmi, &vcpu->irq.pending)));
+
+ nmi_window =
+ (((vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & 0xb) == 0)
+ && (vmcs_read32(VM_ENTRY_INTR_INFO_FIELD)
+ & INTR_INFO_VALID_MASK));
+
+ if (nmi_window) {
+ if (vcpu->rmode.active)
+ inject_rmode_irq(vcpu, 2);
+ else
+ vmcs_write32(VM_ENTRY_INTR_INFO_FIELD,
+ 2 |
+ INTR_TYPE_NMI |
+ INTR_INFO_VALID_MASK);
+
+ __clear_bit(kvm_irqpin_nmi, &vcpu->irq.pending);
+ } else {
+ /*
+ * NMIs blocked. Wait for unblock.
+ */
+ u32 cbvec = vmcs_read32(CPU_BASED_VM_EXEC_CONTROL);
+ cbvec |= CPU_BASED_NMI_EXITING;
+ vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, cbvec);
+ }
+
+ /*
+ * nmi_window correctly reflects whether we handled this interrupt
+ * or not, so just return it as the "handled" indicator
+ */
+ return nmi_window;
+}
+
static int do_intr_requests(struct kvm_vcpu *vcpu,
struct kvm_run *kvm_run,
kvm_irqpin_t pin)
@@ -1329,9 +1373,11 @@ static int do_intr_requests(struct kvm_vcpu *vcpu,
break;
case kvm_irqpin_nmi:
/*
- * FIXME: Someday we will handle this using the
- * specific VMX NMI features. For now, just inject
- * the NMI as a standard interrupt on vector 2
+ * We should only get here if the processor does
+ * not support virtual NMIs. Inject the NMI as a
+ * standard interrupt on vector 2. The implication is
+ * that NMIs are going to be subject to RFLAGS.IF
+ * masking, unfortunately.
*/
ack.flags |= KVM_IRQACKDATA_VECTOR_VALID;
ack.vector = 2;
@@ -1374,7 +1420,8 @@ static int do_intr_requests(struct kvm_vcpu *vcpu,
static void clear_pending_controls(struct kvm_vcpu *vcpu)
{
u32 cbvec = vmcs_read32(CPU_BASED_VM_EXEC_CONTROL);
- cbvec &= ~CPU_BASED_VIRTUAL_INTR_PENDING;
+ cbvec &= ~(CPU_BASED_VIRTUAL_INTR_PENDING
+ | CPU_BASED_NMI_EXITING);
vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, cbvec);
}

@@ -1391,7 +1438,6 @@ static void do_interrupt_requests(struct kvm_vcpu *vcpu,
switch (pin) {
case kvm_irqpin_localint:
case kvm_irqpin_extint:
- case kvm_irqpin_nmi:
do_intr_requests(vcpu, kvm_run, pin);
break;
case kvm_irqpin_smi:
@@ -1399,6 +1445,13 @@ static void do_interrupt_requests(struct kvm_vcpu *vcpu,
printk(KERN_WARNING "KVM: dropping unhandled SMI\n");
__clear_bit(pin, &vcpu->irq.pending);
break;
+ case kvm_irqpin_nmi:
+ if (vmcs_read32(PIN_BASED_VM_EXEC_CONTROL)
+ & PIN_BASED_VIRTUAL_NMI)
+ do_nmi_requests(vcpu);
+ else
+ do_intr_requests(vcpu, kvm_run, pin);
+ break;
case kvm_irqpin_invalid:
/* drop */
break;
diff --git a/drivers/kvm/vmx.h b/drivers/kvm/vmx.h
index d0dc93d..d3fe017 100644
--- a/drivers/kvm/vmx.h
+++ b/drivers/kvm/vmx.h
@@ -35,6 +35,7 @@
#define CPU_BASED_CR8_LOAD_EXITING 0x00080000
#define CPU_BASED_CR8_STORE_EXITING 0x00100000
#define CPU_BASED_TPR_SHADOW 0x00200000
+#define CPU_BASED_NMI_EXITING 0x00400000
#define CPU_BASED_MOV_DR_EXITING 0x00800000
#define CPU_BASED_UNCOND_IO_EXITING 0x01000000
#define CPU_BASED_ACTIVATE_IO_BITMAP 0x02000000
@@ -44,6 +45,7 @@

#define PIN_BASED_EXT_INTR_MASK 0x1
#define PIN_BASED_NMI_EXITING 0x8
+#define PIN_BASED_VIRTUAL_NMI 0x20

#define VM_EXIT_ACK_INTR_ON_EXIT 0x00008000
#define VM_EXIT_HOST_ADD_SPACE_SIZE 0x00000200
@@ -221,6 +223,7 @@ enum vmcs_field {
#define VECTORING_INFO_VALID_MASK INTR_INFO_VALID_MASK

#define INTR_TYPE_EXT_INTR (0 << 8) /* external interrupt */
+#define INTR_TYPE_NMI (2 << 8) /* non-maskable interrupt */
#define INTR_TYPE_EXCEPTION (3 << 8) /* processor exception */

/*