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