aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCathy Avery <cavery@redhat.com>2020-04-09 09:32:47 -0400
committerPaolo Bonzini <pbonzini@redhat.com>2020-04-09 09:49:54 -0400
commit9da1f4d89d16a6049f7f2ac941c1101f2a34a5b9 (patch)
treeef068099670491bb35b405a8a4ab80eaea6bdbcb
parentd4db486be71938848165c699d07dfa216072ba25 (diff)
downloadkvm-unit-tests-9da1f4d89d16a6049f7f2ac941c1101f2a34a5b9.tar.gz
svm: Add test cases around NMI injection with HLT
This test checks for NMI delivery to L2 and intercepted NMI (VMEXIT_NMI) delivery to L1 during an active HLT. Signed-off-by: Cathy Avery <cavery@redhat.com> Message-Id: <20200409133247.16653-3-cavery@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-rw-r--r--x86/svm_tests.c103
1 files changed, 103 insertions, 0 deletions
diff --git a/x86/svm_tests.c b/x86/svm_tests.c
index b6c0106..2ec7275 100644
--- a/x86/svm_tests.c
+++ b/x86/svm_tests.c
@@ -9,6 +9,7 @@
#include "alloc_page.h"
#include "isr.h"
#include "apic.h"
+#include "delay.h"
#define SVM_EXIT_MAX_DR_INTERCEPT 0x3f
@@ -1419,6 +1420,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 }
/*
@@ -1528,6 +1628,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 }
};