The APM Vol #2 - 15.20 lists illegal combinations related to event injection. Add testing to verify that these illegal combinations cause an invalid VM exit. Also add testing to verify that legal combinations for event injection work as intended. This includes testing all valid injection types and injecting all exceptions when the exception type is specified. Signed-off-by: Kevin Cheng --- lib/x86/processor.h | 5 ++ x86/svm_tests.c | 195 ++++++++++++++++++++++++++++---------------- x86/unittests.cfg | 9 +- 3 files changed, 137 insertions(+), 72 deletions(-) diff --git a/lib/x86/processor.h b/lib/x86/processor.h index b073ee168ce4b..0ae812df59b1c 100644 --- a/lib/x86/processor.h +++ b/lib/x86/processor.h @@ -528,6 +528,11 @@ static inline bool this_cpu_has_mwait(void) return this_cpu_has(X86_FEATURE_MWAIT); } +static inline bool this_cpu_has_shstk(void) +{ + return this_cpu_has(X86_FEATURE_SHSTK); +} + struct far_pointer32 { u32 offset; u16 selector; diff --git a/x86/svm_tests.c b/x86/svm_tests.c index a40468693b396..5d27286129337 100644 --- a/x86/svm_tests.c +++ b/x86/svm_tests.c @@ -1674,74 +1674,6 @@ 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) -{ - count_exc++; -} - -static void exc_inject_prepare(struct svm_test *test) -{ - default_prepare(test); - handle_exception(DE_VECTOR, my_isr); - handle_exception(NMI_VECTOR, my_isr); -} - - -static void exc_inject_test(struct svm_test *test) -{ - asm volatile ("vmmcall\n\tvmmcall\n\t"); -} - -static bool exc_inject_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 vmmcall. Exit reason 0x%x", - vmcb->control.exit_code); - return true; - } - vmcb->save.rip += 3; - vmcb->control.event_inj = NMI_VECTOR | SVM_EVTINJ_TYPE_EXEPT | SVM_EVTINJ_VALID; - break; - - case 1: - if (vmcb->control.exit_code != SVM_EXIT_ERR) { - report_fail("VMEXIT not due to error. Exit reason 0x%x", - vmcb->control.exit_code); - return true; - } - report(count_exc == 0, "exception with vector 2 not injected"); - vmcb->control.event_inj = DE_VECTOR | SVM_EVTINJ_TYPE_EXEPT | SVM_EVTINJ_VALID; - break; - - case 2: - 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; - } - vmcb->save.rip += 3; - report(count_exc == 1, "divide overflow exception injected"); - report(!(vmcb->control.event_inj & SVM_EVTINJ_VALID), "eventinj.VALID cleared"); - break; - - default: - return true; - } - - inc_test_stage(test); - - return get_test_stage(test) == 3; -} - -static bool exc_inject_check(struct svm_test *test) -{ - return count_exc == 1 && get_test_stage(test) == 3; -} - static volatile bool virq_fired; static volatile unsigned long virq_rip; @@ -2548,6 +2480,129 @@ static void test_dr(void) vmcb->save.dr7 = dr_saved; } +/* Returns true if exception can be injected via the SVM_EVTINJ_TYPE_EXEPT type */ +static bool is_injectable_exception(int vec) +{ + /* + * Vectors that do not correspond to an exception are excluded. NMI is + * not an exception so it is excluded. BR and OF are excluded because + * BOUND and INTO are not legal in 64-bit mode. + * + * The VE vector is excluded because it is Intel only. + * + * The HV and VC vectors are excluded because they are only relevant + * within secure guest VMs. + */ + u8 exception_vectors[32] = { + [DE_VECTOR] = 1, [DB_VECTOR] = 1, + [BP_VECTOR] = 1, [UD_VECTOR] = 1, + [NM_VECTOR] = 1, [DF_VECTOR] = 1, + [TS_VECTOR] = 1, [NP_VECTOR] = 1, + [SS_VECTOR] = 1, [GP_VECTOR] = 1, + [PF_VECTOR] = 1, [MF_VECTOR] = 1, + [AC_VECTOR] = 1, [MC_VECTOR] = 1, + [XF_VECTOR] = 1, [CP_VECTOR] = this_cpu_has_shstk(), + [SX_VECTOR] = 1, + }; + + return exception_vectors[vec]; +} + +static bool is_valid_injection_type_mask(int type_mask) +{ + return type_mask == SVM_EVTINJ_TYPE_INTR || + type_mask == SVM_EVTINJ_TYPE_NMI || + type_mask == SVM_EVTINJ_TYPE_EXEPT || + type_mask == SVM_EVTINJ_TYPE_SOFT; +} + +static volatile bool event_injection_handled; +static void event_injection_irq_handler(isr_regs_t *regs) +{ + event_injection_handled = true; + vmmcall(); +} + +static void event_injection_exception_handler(struct ex_regs *r) +{ + event_injection_handled = true; + vmmcall(); +} + +static void svm_event_injection(void) +{ + u32 event_inj_saved = vmcb->control.event_inj, vector = 0x22, event_inj; + int type, type_mask; + bool reserved; + + handle_exception(DE_VECTOR, event_injection_exception_handler); + handle_irq(vector, event_injection_irq_handler); + + /* Setting reserved values of TYPE is illegal */ + for (type = 0; type < 8; type++) { + type_mask = type << SVM_EVTINJ_TYPE_SHIFT; + reserved = !is_valid_injection_type_mask(type_mask); + event_injection_handled = false; + event_inj = SVM_EVTINJ_VALID; + + switch (type_mask) { + case SVM_EVTINJ_TYPE_EXEPT: + event_inj |= DE_VECTOR; + break; + default: + event_inj |= vector; + } + + vmcb->control.event_inj = event_inj | + (type << SVM_EVTINJ_TYPE_SHIFT); + if (reserved) { + report(svm_vmrun() == SVM_EXIT_ERR, + "Test EVENTINJ error code with type %d", type); + report(!event_injection_handled, + "Reserved type %d ignores EVENTINJ vector field", type); + } else { + report(svm_vmrun() == SVM_EXIT_VMMCALL, + "Test EVENTINJ delivers with type %d", type); + } + + if (type_mask == SVM_EVTINJ_TYPE_NMI) + report(!event_injection_handled, + "Injected NMI ignores EVENTINJ vector field"); + else if (!reserved) + report(event_injection_handled, + "Test EVENTINJ IRQ handler invoked with type %d", type); + + vmcb->control.event_inj = event_inj_saved; + } + + /* + * It is illegal to specify event injection type 3 (Exception) with a + * vector that does not correspond to an exception. + */ + event_inj = SVM_EVTINJ_VALID | SVM_EVTINJ_TYPE_EXEPT; + for (vector = 0; vector < 256; vector++) { + vmcb->control.event_inj = event_inj | vector; + event_injection_handled = false; + + if (vector >= 32 || !is_injectable_exception(vector)) { + report(svm_vmrun() == SVM_EXIT_ERR, + "Test EVENTINJ exception type error code with vector %d", + vector); + } else { + handle_exception(vector, event_injection_exception_handler); + report(svm_vmrun() == SVM_EXIT_VMMCALL, + "Test EVENTINJ exception type delivers with vector %d", + vector); + report(event_injection_handled, + "Test EVENTINJ exception handler invoked with vector %d", + vector); + } + + vmcb->control.event_inj = event_inj_saved; + } +} + + asm( "insn_sidt: sidt idt_descr;ret\n\t" "insn_sgdt: sgdt gdt_descr;ret\n\t" @@ -4074,9 +4129,6 @@ struct svm_test svm_tests[] = { { "latency_svm_insn", default_supported, lat_svm_insn_prepare, default_prepare_gif_clear, null_test, lat_svm_insn_finished, lat_svm_insn_check }, - { "exc_inject", default_supported, exc_inject_prepare, - default_prepare_gif_clear, exc_inject_test, - exc_inject_finished, exc_inject_check }, { "pending_event", default_supported, pending_event_prepare, default_prepare_gif_clear, pending_event_test, pending_event_finished, pending_event_check }, @@ -4164,6 +4216,7 @@ struct svm_test svm_tests[] = { TEST(pause_filter_test), TEST(svm_shutdown_intercept_test), TEST(svm_insn_intercept_test), + TEST(svm_event_injection), { NULL, NULL, NULL, NULL, NULL, NULL, NULL } }; diff --git a/x86/unittests.cfg b/x86/unittests.cfg index 65dcf8b6cba89..118e7cdd0286d 100644 --- a/x86/unittests.cfg +++ b/x86/unittests.cfg @@ -253,7 +253,14 @@ arch = x86_64 [svm] file = svm.flat smp = 2 -test_args = "-pause_filter_test -svm_intr_intercept_mix_smi -svm_insn_intercept_test -svm_pf_exception_test -svm_pf_exception_forced_emulation_test -svm_pf_inv_asid_test -svm_pf_inv_tlb_ctl_test" +test_args = "-pause_filter_test -svm_intr_intercept_mix_smi -svm_insn_intercept_test -svm_pf_exception_test -svm_pf_exception_forced_emulation_test -svm_pf_inv_asid_test -svm_pf_inv_tlb_ctl_test -svm_event_injection" +qemu_params = -cpu max,+svm -m 4g +arch = x86_64 +groups = svm + +[svm_event_injection] +file = svm.flat +test_args = svm_event_injection qemu_params = -cpu max,+svm -m 4g arch = x86_64 groups = svm -- 2.52.0.457.g6b5491de43-goog