Skip to contentSkip to conversation

svm: Add test cases around NMI injection

PATCH Applied Cathy Avery 2 participants 2 messages Apr 9, 2020 – Apr 10, 2020 KVM

lore
Share & tools
lore
#0 lore raw

PATCH v2 1/2 svm: Add test cases around NMI injection

This test checks for NMI delivery to L2 and
intercepted NMI (VMEXIT_NMI) delivery to L1.

---
v2: Remove redundant NMI_VECTOR

---
 x86/svm_tests.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 82 insertions(+)
x86/svm_tests.c +82 -0
index 16b9dfd..b6c0106 100644
@@ -1340,6 +1340,85 @@ static bool interrupt_check(struct svm_test *test)
     return get_test_stage(test) == 5;
 }
 
+static volatile bool nmi_fired;
+
+static void nmi_handler(isr_regs_t *regs)
+{
+    nmi_fired = true;
+    apic_write(APIC_EOI, 0);
+}
+
+static void nmi_prepare(struct svm_test *test)
+{
+    default_prepare(test);
+    nmi_fired = false;
+    handle_irq(NMI_VECTOR, nmi_handler);
+    set_test_stage(test, 0);
+}
+
+static void nmi_test(struct svm_test *test)
+{
+    apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
+
+    report(nmi_fired, "direct NMI while running guest");
+
+    if (!nmi_fired)
+        set_test_stage(test, -1);
+
+    vmmcall();
+
+    nmi_fired = false;
+
+    apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
+
+    if (!nmi_fired) {
+        report(nmi_fired, "intercepted pending NMI not dispatched");
+        set_test_stage(test, -1);
+    }
+
+}
+
+static bool nmi_finished(struct svm_test *test)
+{
+    switch (get_test_stage(test)) {
+    case 0:
+        if (vmcb->control.exit_code != SVM_EXIT_VMMCALL) {
+            report(false, "VMEXIT not due to vmmcall. Exit reason 0x%x",
+                   vmcb->control.exit_code);
+            return true;
+        }
+        vmcb->save.rip += 3;
+
+        vmcb->control.intercept |= (1ULL << INTERCEPT_NMI);
+        break;
+
+    case 1:
+        if (vmcb->control.exit_code != SVM_EXIT_NMI) {
+            report(false, "VMEXIT not due to NMI intercept. Exit reason 0x%x",
+                   vmcb->control.exit_code);
+            return true;
+        }
+
+        report(true, "NMI intercept while running guest");
+        break;
+
+    case 2:
+        break;
+
+    default:
+        return true;
+    }
+
+    inc_test_stage(test);
+
+    return get_test_stage(test) == 3;
+}
+
+static bool nmi_check(struct svm_test *test)
+{
+    return get_test_stage(test) == 3;
+}
+
 #define TEST(name) { #name, .v2 = name }
 
 /*
@@ -1446,6 +1525,9 @@ struct svm_test svm_tests[] = {
     { "interrupt", default_supported, interrupt_prepare,
       default_prepare_gif_clear, interrupt_test,
       interrupt_finished, interrupt_check },
+    { "nmi", default_supported, nmi_prepare,
+      default_prepare_gif_clear, nmi_test,
+      nmi_finished, nmi_check },
     TEST(svm_guest_state_test),
     { NULL, NULL, NULL, NULL, NULL, NULL, NULL }
 };
Signature · 1 line
2.20.1
maintainer Applied
#1 lore raw
Quoted 102 lines · Cathy Avery

This test checks for NMI delivery to L2 and
intercepted NMI (VMEXIT_NMI) delivery to L1.

---
v2: Remove redundant NMI_VECTOR

---
 x86/svm_tests.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 82 insertions(+)
x86/svm_tests.c +79 -0
index 16b9dfd..b6c0106 100644
@@ -1340,6 +1340,85 @@ static bool interrupt_check(struct svm_test *test)
     return get_test_stage(test) == 5;
 }
 
+static volatile bool nmi_fired;
+
+static void nmi_handler(isr_regs_t *regs)
+{
+    nmi_fired = true;
+    apic_write(APIC_EOI, 0);
+}
+
+static void nmi_prepare(struct svm_test *test)
+{
+    default_prepare(test);
+    nmi_fired = false;
+    handle_irq(NMI_VECTOR, nmi_handler);
+    set_test_stage(test, 0);
+}
+
+static void nmi_test(struct svm_test *test)
+{
+    apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
+
+    report(nmi_fired, "direct NMI while running guest");
+
+    if (!nmi_fired)
+        set_test_stage(test, -1);
+
+    vmmcall();
+
+    nmi_fired = false;
+
+    apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
+
+    if (!nmi_fired) {
+        report(nmi_fired, "intercepted pending NMI not dispatched");
+        set_test_stage(test, -1);
+    }
+
+}
+
+static bool nmi_finished(struct svm_test *test)
+{
+    switch (get_test_stage(test)) {
+    case 0:
+        if (vmcb->control.exit_code != SVM_EXIT_VMMCALL) {
+            report(false, "VMEXIT not due to vmmcall. Exit reason 0x%x",
+                   vmcb->control.exit_code);
+            return true;
+        }
+        vmcb->save.rip += 3;
+
+        vmcb->control.intercept |= (1ULL << INTERCEPT_NMI);
+        break;
+
+    case 1:
+        if (vmcb->control.exit_code != SVM_EXIT_NMI) {
+            report(false, "VMEXIT not due to NMI intercept. Exit reason 0x%x",
+                   vmcb->control.exit_code);
+            return true;
+        }
+
+        report(true, "NMI intercept while running guest");
+        break;
+
+    case 2:
+        break;
+
+    default:
+        return true;
+    }
+
+    inc_test_stage(test);
+
+    return get_test_stage(test) == 3;
+}
+
+static bool nmi_check(struct svm_test *test)
+{
+    return get_test_stage(test) == 3;
+}
+
 #define TEST(name) { #name, .v2 = name }
 
 /*

x86/svm_tests.c struct svm_test svm_tests[] = { · line 1533

Quoted 9 lines
x86/svm_tests.c +3 -0
@@ -1446,6 +1525,9 @@ struct svm_test svm_tests[] = {
     { "interrupt", default_supported, interrupt_prepare,
       default_prepare_gif_clear, interrupt_test,
       interrupt_finished, interrupt_check },
+    { "nmi", default_supported, nmi_prepare,
+      default_prepare_gif_clear, nmi_test,
+      nmi_finished, nmi_check },
     TEST(svm_guest_state_test),
     { NULL, NULL, NULL, NULL, NULL, NULL, NULL }
 };

Queued (both), thanks.

Paolo