Skip to contentSkip to conversation

svm: NMI injection and HLT tests

PATCH Superseded Cathy Avery 2 participants 5 messages Apr 9, 2020 KVM

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore
#0 lore raw

cover svm: NMI injection and HLT tests

Patch 1 is NMI injection and intercept. Patch 2 is the same test with guest in HLT.

Cathy Avery (2):
svm: Add test cases around NMI injection
svm: Add test cases around NMI injection with HLT

 x86/svm_tests.c | 187 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 187 insertions(+)
Signature · 1 line
2.20.1
#1 lore raw

PATCH 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.

---
 x86/svm_tests.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 84 insertions(+)
x86/svm_tests.c +84 -0
index 16b9dfd..d1dbdef 100644
@@ -1340,6 +1340,87 @@ static bool interrupt_check(struct svm_test *test)
     return get_test_stage(test) == 5;
 }
 
+static volatile bool nmi_fired;
+
+#define NMI_VECTOR    2
+
+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 +1527,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
#2 lore raw

Re: [PATCH kvm-unit-tests 1/2] svm: Add test cases around NMI injection

x86/svm_tests.c static bool interrupt_check(struct svm_test *test) · line 1346

Quoted 4 lines · Cathy Avery
x86/svm_tests.c +4 -0
+static volatile bool nmi_fired;
+
+#define NMI_VECTOR    2
+

This one is already defined, so it is not necessary to include the #define here.

Paolo

#3 lore raw

PATCH 2/2 svm: Add test cases around NMI injection with HLT

In reply to Cathy Avery

This test checks for NMI delivery to L2 and
intercepted NMI (VMEXIT_NMI) delivery to L1
during an active HLT.

---
 x86/svm_tests.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 103 insertions(+)
x86/svm_tests.c +103 -0
index d1dbdef..7d7c546 100644
@@ -9,6 +9,7 @@
 #include "alloc_page.h"
 #include "isr.h"
 #include "apic.h"
+#include "delay.h"
 
 #define SVM_EXIT_MAX_DR_INTERCEPT 0x3f
 
@@ -1421,6 +1422,105 @@ static bool nmi_check(struct svm_test *test)
     return get_test_stage(test) == 3;
 }
 
+#define NMI_DELAY 100000000ULL
+
+static void nmi_message_thread(void *_test)
+{
+    struct svm_test *test = _test;
+
+    while (get_test_stage(test) != 1)
+        pause();
+
+    delay(NMI_DELAY);
+
+    apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, id_map[0]);
+
+    while (get_test_stage(test) != 2)
+        pause();
+
+    delay(NMI_DELAY);
+
+    apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, id_map[0]);
+}
+
+static void nmi_hlt_test(struct svm_test *test)
+{
+    long long start;
+
+    on_cpu_async(1, nmi_message_thread, test);
+
+    start = rdtsc();
+
+    set_test_stage(test, 1);
+
+    asm volatile ("hlt");
+
+    report((rdtsc() - start > NMI_DELAY) && nmi_fired,
+          "direct NMI + hlt");
+
+    if (!nmi_fired)
+        set_test_stage(test, -1);
+
+    nmi_fired = false;
+
+    vmmcall();
+
+    start = rdtsc();
+
+    set_test_stage(test, 2);
+
+    asm volatile ("hlt");
+
+    report((rdtsc() - start > NMI_DELAY) && nmi_fired,
+           "intercepted NMI + hlt");
+
+    if (!nmi_fired) {
+        report(nmi_fired, "intercepted pending NMI not dispatched");
+        set_test_stage(test, -1);
+    }
+
+    set_test_stage(test, 3);
+}
+
+static bool nmi_hlt_finished(struct svm_test *test)
+{
+    switch (get_test_stage(test)) {
+    case 1:
+        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 2:
+        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 3:
+        break;
+
+    default:
+        return true;
+    }
+
+    return get_test_stage(test) == 3;
+}
+
+static bool nmi_hlt_check(struct svm_test *test)
+{
+    return get_test_stage(test) == 3;
+}
+
 #define TEST(name) { #name, .v2 = name }
 
 /*
@@ -1530,6 +1630,9 @@ struct svm_test svm_tests[] = {
     { "nmi", default_supported, nmi_prepare,
       default_prepare_gif_clear, nmi_test,
       nmi_finished, nmi_check },
+    { "nmi_hlt", smp_supported, nmi_prepare,
+      default_prepare_gif_clear, nmi_hlt_test,
+      nmi_hlt_finished, nmi_hlt_check },
     TEST(svm_guest_state_test),
     { NULL, NULL, NULL, NULL, NULL, NULL, NULL }
 };
Signature · 1 line
2.20.1
maintainer
#4 lore raw
In reply to Cathy Avery
Quoted 10 lines · Cathy Avery

Patch 1 is NMI injection and intercept. Patch 2 is the same test with guest in HLT.

Cathy Avery (2):
svm: Add test cases around NMI injection
svm: Add test cases around NMI injection with HLT

 x86/svm_tests.c | 187 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 187 insertions(+)

Looks good, apart from one nit in patch 1. Using the LAPIC timer could have been an alternative (which would also let the HLT test run on uniprocessor guests), but there is certainly no issue with using an IPI and making things a little more varied. :)

Paolo