[kvm-unit-tests PATCH] x86: nSVM: Add support for VNMI test

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add a VNMI test case to test Virtual NMI in a nested environment,
The test covers the Virtual NMI (VNMI) event delivery.

Signed-off-by: Santosh Shukla <santosh.shukla@xxxxxxx>
---
 lib/x86/processor.h |  1 +
 x86/svm.c           |  5 +++
 x86/svm.h           |  8 +++++
 x86/svm_tests.c     | 79 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 93 insertions(+)

diff --git a/lib/x86/processor.h b/lib/x86/processor.h
index 9a0dad677870..cd86b5d60545 100644
--- a/lib/x86/processor.h
+++ b/lib/x86/processor.h
@@ -193,6 +193,7 @@ static inline bool is_intel(void)
 #define X86_FEATURE_PAUSEFILTER     (CPUID(0x8000000A, 0, EDX, 10))
 #define X86_FEATURE_PFTHRESHOLD     (CPUID(0x8000000A, 0, EDX, 12))
 #define	X86_FEATURE_VGIF		(CPUID(0x8000000A, 0, EDX, 16))
+#define	X86_FEATURE_V_NMI		(CPUID(0x8000000A, 0, EDX, 25))
 
 
 static inline bool this_cpu_has(u64 feature)
diff --git a/x86/svm.c b/x86/svm.c
index 93794fdfd7a2..2073c52fdb49 100644
--- a/x86/svm.c
+++ b/x86/svm.c
@@ -110,6 +110,11 @@ bool npt_supported(void)
 	return this_cpu_has(X86_FEATURE_NPT);
 }
 
+bool vnmi_supported(void)
+{
+	return this_cpu_has(X86_FEATURE_V_NMI);
+}
+
 int get_test_stage(struct svm_test *test)
 {
 	barrier();
diff --git a/x86/svm.h b/x86/svm.h
index e93822b66bab..98c237ba4aa3 100644
--- a/x86/svm.h
+++ b/x86/svm.h
@@ -131,6 +131,13 @@ struct __attribute__ ((__packed__)) vmcb_control_area {
 #define V_INTR_MASKING_SHIFT 24
 #define V_INTR_MASKING_MASK (1 << V_INTR_MASKING_SHIFT)
 
+#define V_NMI_PENDING_SHIFT 11
+#define V_NMI_PENDING (1 << V_NMI_PENDING_SHIFT)
+#define V_NMI_MASK_SHIFT 12
+#define V_NMI_MASK (1 << V_NMI_MASK_SHIFT)
+#define V_NMI_ENABLE_SHIFT 26
+#define V_NMI_ENABLE (1 << V_NMI_ENABLE_SHIFT)
+
 #define SVM_INTERRUPT_SHADOW_MASK 1
 
 #define SVM_IOIO_STR_SHIFT 2
@@ -418,6 +425,7 @@ void default_prepare(struct svm_test *test);
 void default_prepare_gif_clear(struct svm_test *test);
 bool default_finished(struct svm_test *test);
 bool npt_supported(void);
+bool vnmi_supported(void);
 int get_test_stage(struct svm_test *test);
 void set_test_stage(struct svm_test *test, int s);
 void inc_test_stage(struct svm_test *test);
diff --git a/x86/svm_tests.c b/x86/svm_tests.c
index 1bd4d3b20505..e331d5cc97ed 100644
--- a/x86/svm_tests.c
+++ b/x86/svm_tests.c
@@ -1624,6 +1624,82 @@ static bool nmi_hlt_check(struct svm_test *test)
     return get_test_stage(test) == 3;
 }
 
+static volatile bool vnmi_fired;
+static void vnmi_handler(isr_regs_t *regs)
+{
+    vnmi_fired = true;
+}
+
+static void vnmi_prepare(struct svm_test *test)
+{
+    default_prepare(test);
+    vnmi_fired = false;
+    vmcb->control.int_ctl = V_NMI_ENABLE;
+    vmcb->control.int_vector = NMI_VECTOR;
+    handle_irq(NMI_VECTOR, vnmi_handler);
+    set_test_stage(test, 0);
+}
+
+static void vnmi_test(struct svm_test *test)
+{
+    if (vnmi_fired) {
+        report(!vnmi_fired, "vNMI dispatched even before injection");
+        set_test_stage(test, -1);
+    }
+
+    vmmcall();
+
+    if (!vnmi_fired) {
+        report(vnmi_fired, "pending vNMI not dispatched");
+        set_test_stage(test, -1);
+    }
+    report(vnmi_fired, "vNMI delivered to guest");
+
+    vmmcall();
+}
+
+static bool vnmi_finished(struct svm_test *test)
+{
+    switch (get_test_stage(test)) {
+    case 0:
+        if (vmcb->control.exit_code != SVM_EXIT_VMMCALL) {
+            report_fail("VMEXIT not due to error. Exit reason 0x%x",
+                        vmcb->control.exit_code);
+            return true;
+        }
+        report(!vnmi_fired, "vNMI with vector 2 not injected");
+        vmcb->control.int_ctl |= V_NMI_PENDING;
+        vmcb->save.rip += 3;
+        break;
+
+    case 1:
+        if (vmcb->control.exit_code != SVM_EXIT_VMMCALL) {
+            report_fail("VMEXIT not due to vmmcall. Exit reason 0x%x",
+                        vmcb->control.exit_code);
+            return true;
+        }
+        if (vmcb->control.int_ctl & V_NMI_MASK) {
+            report_fail("V_NMI_MASK not cleared on VMEXIT");
+            return true;
+        }
+        report_pass("VNMI serviced");
+        vmcb->save.rip += 3;
+        break;
+
+    default:
+        return true;
+    }
+
+    inc_test_stage(test);
+
+    return get_test_stage(test) == 2;
+}
+
+static bool vnmi_check(struct svm_test *test)
+{
+    return get_test_stage(test) == 2;
+}
+
 static volatile int count_exc = 0;
 
 static void my_isr(struct ex_regs *r)
@@ -3729,6 +3805,9 @@ struct svm_test svm_tests[] = {
     { "nmi_hlt", smp_supported, nmi_prepare,
       default_prepare_gif_clear, nmi_hlt_test,
       nmi_hlt_finished, nmi_hlt_check },
+    { "vnmi", vnmi_supported, vnmi_prepare,
+      default_prepare_gif_clear, vnmi_test,
+      vnmi_finished, vnmi_check },
     { "virq_inject", default_supported, virq_inject_prepare,
       default_prepare_gif_clear, virq_inject_test,
       virq_inject_finished, virq_inject_check },
-- 
2.25.1




[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux