Set up a test framework that verifies an exception occurring in L2 is forwarded to the right place (L1 or L2). It adds an exception test array and exception callbacks to that array. Tests two conditions for each exception. 1) Exception generated in L2, is handled by L2 when L2 exception handler is registered. 2) Exception generated in L2, is handled by L1 when intercept exception bit map is set in L1. Add testing for below exceptions: (#GP, #UD, #DE, #DB, #AC) 1. #GP is generated in c by non-canonical access in L2. 2. #UD is generated by calling "ud2" instruction in L2. 3. #DE is generated using instrumented code which generates divide by zero condition. 4. #DB is generated by setting TF bit before entering to L2. 5. #AC is genrated by writing 8 bytes to 4 byte aligned address in L2 user mode when AM bit is set in CR0 register and AC bit is set in RFLAGS. Signed-off-by: Manali Shukla <manali.shukla@xxxxxxx> --- x86/svm_tests.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/x86/svm_tests.c b/x86/svm_tests.c index e2ec954..7544034 100644 --- a/x86/svm_tests.c +++ b/x86/svm_tests.c @@ -10,6 +10,7 @@ #include "isr.h" #include "apic.h" #include "delay.h" +#include "x86/usermode.h" #define SVM_EXIT_MAX_DR_INTERCEPT 0x3f @@ -3289,6 +3290,118 @@ static void svm_intr_intercept_mix_smi(void) svm_intr_intercept_mix_run_guest(NULL, SVM_EXIT_SMI); } +static void svm_l2_gp_test(struct svm_test *test) +{ + *(volatile u64 *)NONCANONICAL = 0; +} + +static void svm_l2_ud_test(struct svm_test *test) +{ + asm volatile ("ud2"); +} + +static void svm_l2_de_test(struct svm_test *test) +{ + asm volatile ( + "xor %%eax, %%eax\n\t" + "xor %%ebx, %%ebx\n\t" + "xor %%edx, %%edx\n\t" + "idiv %%ebx\n\t" + ::: "eax", "ebx", "edx"); +} + +static void svm_l2_db_test(struct svm_test *test) +{ + write_rflags(read_rflags() | X86_EFLAGS_TF); +} + +static uint64_t usermode_callback(void) +{ + /* + * Trigger an #AC by writing 8 bytes to a 4-byte aligned address. + * Disclaimer: It is assumed that the stack pointer is aligned + * on a 16-byte boundary as x86_64 stacks should be. + */ + asm volatile("movq $0, -0x4(%rsp)"); + + return 0; +} + +static void svm_l2_ac_test(struct svm_test *test) +{ + bool hit_ac = false; + + write_cr0(read_cr0() | X86_CR0_AM); + write_rflags(read_rflags() | X86_EFLAGS_AC); + + run_in_user(usermode_callback, AC_VECTOR, 0, 0, 0, 0, &hit_ac); + report(hit_ac, "Usermode #AC handled in L2"); + vmmcall(); +} + +struct svm_exception_test { + u8 vector; + void (*guest_code)(struct svm_test*); +}; + +struct svm_exception_test svm_exception_tests[] = { + { GP_VECTOR, svm_l2_gp_test }, + { UD_VECTOR, svm_l2_ud_test }, + { DE_VECTOR, svm_l2_de_test }, + { DB_VECTOR, svm_l2_db_test }, + { AC_VECTOR, svm_l2_ac_test }, +}; + +static u8 svm_exception_test_vector; + +static void svm_exception_handler(struct ex_regs *regs) +{ + report(regs->vector == svm_exception_test_vector, + "Handling %s in L2's exception handler", + exception_mnemonic(svm_exception_test_vector)); + vmmcall(); +} + +static void handle_exception_in_l2(u8 vector) +{ + handler old_handler = handle_exception(vector, svm_exception_handler); + svm_exception_test_vector = vector; + + report(svm_vmrun() == SVM_EXIT_VMMCALL, + "%s handled by L2", exception_mnemonic(vector)); + + handle_exception(vector, old_handler); +} + +static void handle_exception_in_l1(u32 vector) +{ + u32 old_ie = vmcb->control.intercept_exceptions; + + vmcb->control.intercept_exceptions |= (1ULL << vector); + + report(svm_vmrun() == (SVM_EXIT_EXCP_BASE + vector), + "%s handled by L1", exception_mnemonic(vector)); + + vmcb->control.intercept_exceptions = old_ie; +} + +static void svm_exception_test(void) +{ + struct svm_exception_test *t; + int i; + + for (i = 0; i < ARRAY_SIZE(svm_exception_tests); i++) { + t = &svm_exception_tests[i]; + test_set_guest(t->guest_code); + + handle_exception_in_l2(t->vector); + vmcb_ident(vmcb); + + handle_exception_in_l1(t->vector); + vmcb_ident(vmcb); + } +} + struct svm_test svm_tests[] = { { "null", default_supported, default_prepare, default_prepare_gif_clear, null_test, @@ -3389,6 +3502,7 @@ struct svm_test svm_tests[] = { TEST(svm_nm_test), TEST(svm_int3_test), TEST(svm_into_test), + TEST(svm_exception_test), TEST(svm_lbrv_test0), TEST(svm_lbrv_test1), TEST(svm_lbrv_test2), -- 2.34.1