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 --- x86/svm_tests.c | 192 ++++++++++++++++++++++++++++++------------------ 1 file changed, 121 insertions(+), 71 deletions(-) diff --git a/x86/svm_tests.c b/x86/svm_tests.c index a40468693b396..a069add43d078 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,126 @@ 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. + */ + static 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] = 1, [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 test_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" @@ -2893,6 +2945,7 @@ static void svm_guest_state_test(void) test_dr(); test_msrpm_iopm_bitmap_addrs(); test_canonicalization(); + test_event_injection(); } extern void guest_rflags_test_guest(struct svm_test *test); @@ -4074,9 +4127,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 }, -- 2.52.0.322.g1dd061c0dc-goog