From: Santosh Shukla <santosh.shukla@xxxxxxx> Add a VNMI test case to test Virtual NMI in a nested environment, The test covers the Virtual NMI (VNMI) delivery. Signed-off-by: Santosh Shukla <santosh.shukla@xxxxxxx> [sean: reuse pieces of NMI test framework, fix formatting issues] Signed-off-by: Sean Christopherson <seanjc@xxxxxxxxxx> --- lib/x86/processor.h | 1 + x86/svm.c | 5 +++ x86/svm.h | 8 +++++ x86/svm_tests.c | 78 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+) diff --git a/lib/x86/processor.h b/lib/x86/processor.h index 3d58ef72..3802c1e2 100644 --- a/lib/x86/processor.h +++ b/lib/x86/processor.h @@ -267,6 +267,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)) #define X86_FEATURE_AMD_PMU_V2 (CPUID(0x80000022, 0, EAX, 0)) static inline bool this_cpu_has(u64 feature) diff --git a/x86/svm.c b/x86/svm.c index ba435b4a..022a0fde 100644 --- a/x86/svm.c +++ b/x86/svm.c @@ -99,6 +99,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 766ff7e3..4631c2ff 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_MASK (1 << V_NMI_PENDING_SHIFT) +#define V_NMI_BLOCKING_SHIFT 12 +#define V_NMI_BLOCKING_MASK (1 << V_NMI_BLOCKING_SHIFT) +#define V_NMI_ENABLE_SHIFT 26 +#define V_NMI_ENABLE_MASK (1 << V_NMI_ENABLE_SHIFT) + #define SVM_INTERRUPT_SHADOW_MASK 1 #define SVM_IOIO_STR_SHIFT 2 @@ -419,6 +426,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 e87db3fa..3d2ca0f6 100644 --- a/x86/svm_tests.c +++ b/x86/svm_tests.c @@ -1419,6 +1419,81 @@ static bool nmi_hlt_check(struct svm_test *test) return get_test_stage(test) == 3; } +static void vnmi_prepare(struct svm_test *test) +{ + nmi_prepare(test); + + /* + * Disable NMI interception to start. Enabling vNMI without + * intercepting "real" NMIs should result in an ERR VM-Exit. + */ + vmcb->control.intercept &= ~(1ULL << INTERCEPT_NMI); + vmcb->control.int_ctl = V_NMI_ENABLE_MASK; + vmcb->control.int_vector = NMI_VECTOR; +} + +static void vnmi_test(struct svm_test *test) +{ + report_svm_guest(!nmi_fired, test, "No vNMI before injection"); + vmmcall(); + + report_svm_guest(nmi_fired, test, "vNMI delivered after injection"); + vmmcall(); +} + +static bool vnmi_finished(struct svm_test *test) +{ + switch (get_test_stage(test)) { + case 0: + if (vmcb->control.exit_code != SVM_EXIT_ERR) { + report_fail("Wanted ERR VM-Exit, got 0x%x", + vmcb->control.exit_code); + return true; + } + report(!nmi_fired, "vNMI enabled but NMI_INTERCEPT unset!"); + vmcb->control.intercept |= (1ULL << INTERCEPT_NMI); + vmcb->save.rip += 3; + break; + + case 1: + if (vmcb->control.exit_code != SVM_EXIT_VMMCALL) { + report_fail("Wanted VMMCALL VM-Exit, got 0x%x", + vmcb->control.exit_code); + return true; + } + report(!nmi_fired, "vNMI with vector 2 not injected"); + vmcb->control.int_ctl |= V_NMI_PENDING_MASK; + vmcb->save.rip += 3; + break; + + case 2: + if (vmcb->control.exit_code != SVM_EXIT_VMMCALL) { + report_fail("Wanted VMMCALL VM-Exit, got 0x%x", + vmcb->control.exit_code); + return true; + } + if (vmcb->control.int_ctl & V_NMI_BLOCKING_MASK) { + report_fail("V_NMI_BLOCKING_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) == 3; +} + +static bool vnmi_check(struct svm_test *test) +{ + return get_test_stage(test) == 3; +} + static volatile int count_exc = 0; static void my_isr(struct ex_regs *r) @@ -3298,6 +3373,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.40.0.348.gf938b09366-goog