The nVMX tests already have coverage for instruction intercepts. Add a similar test for nSVM to improve test parity between nSVM and nVMX. Signed-off-by: Kevin Cheng --- x86/svm_tests.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/x86/svm_tests.c b/x86/svm_tests.c index 80d5aeb1..50201683 100644 --- a/x86/svm_tests.c +++ b/x86/svm_tests.c @@ -12,6 +12,7 @@ #include "util.h" #include "x86/usermode.h" #include "vmalloc.h" +#include "pmu.h" #define SVM_EXIT_MAX_DR_INTERCEPT 0x3f @@ -2487,6 +2488,124 @@ static void test_dr(void) vmcb->save.dr7 = dr_saved; } +asm( + "insn_sidt: sidt idt_descr;ret\n\t" + "insn_sgdt: sgdt gdt_descr;ret\n\t" + "insn_sldt: sldt %ax;ret\n\t" + "insn_str: str %ax;ret\n\t" + "insn_lidt: lidt idt_descr;ret\n\t" + "insn_lgdt: lgdt gdt_descr;ret\n\t" + "insn_lldt: xor %eax, %eax; lldt %ax;ret\n\t" + "insn_rdpmc: xor %ecx, %ecx; rdpmc;ret\n\t" + "insn_cpuid: mov $10, %eax; cpuid;ret\n\t" + "insn_invd: invd;ret\n\t" + "insn_pause: pause;ret\n\t" + "insn_hlt: hlt;ret\n\t" + "insn_invlpg: invlpg 0x12345678;ret\n\t" + "insn_monitor: xor %eax, %eax; xor %ecx, %ecx; xor %edx, %edx; monitor;ret\n\t" + "insn_mwait: xor %eax, %eax; xor %ecx, %ecx; mwait;ret\n\t" +); + +extern void insn_sidt(struct svm_test *test); +extern void insn_sgdt(struct svm_test *test); +extern void insn_sldt(struct svm_test *test); +extern void insn_str(struct svm_test *test); +extern void insn_lidt(struct svm_test *test); +extern void insn_lgdt(struct svm_test *test); +extern void insn_lldt(struct svm_test *test); +extern void insn_rdpmc(struct svm_test *test); +extern void insn_cpuid(struct svm_test *test); +extern void insn_invd(struct svm_test *test); +extern void insn_pause(struct svm_test *test); +extern void insn_hlt(struct svm_test *test); +extern void insn_invlpg(struct svm_test *test); +extern void insn_monitor(struct svm_test *test); +extern void insn_mwait(struct svm_test *test); + +u32 cur_insn; + +typedef bool (*supported_fn)(void); + +static bool this_cpu_has_mwait(void) +{ + return this_cpu_has(X86_FEATURE_MWAIT); +} + +struct insn_table { + const char *name; + u32 flag; + void (*insn_func)(struct svm_test *test); + u32 reason; + u64 exit_info_1; + u64 exit_info_2; + bool always_traps; + const supported_fn supported_fn; +}; + +static struct insn_table insn_table[] = { + {"STORE IDTR", INTERCEPT_STORE_IDTR, insn_sidt, SVM_EXIT_IDTR_READ, 0, 0}, + {"STORE GDTR", INTERCEPT_STORE_GDTR, insn_sgdt, SVM_EXIT_GDTR_READ, 0, 0}, + {"STORE LDTR", INTERCEPT_STORE_LDTR, insn_sldt, SVM_EXIT_LDTR_READ, 0, 0}, + {"STORE TR", INTERCEPT_STORE_TR, insn_str, SVM_EXIT_TR_READ, 0, 0}, + {"LOAD IDTR", INTERCEPT_LOAD_IDTR, insn_lidt, SVM_EXIT_IDTR_WRITE, 0, 0}, + {"LOAD GDTR", INTERCEPT_LOAD_GDTR, insn_lgdt, SVM_EXIT_GDTR_WRITE, 0, 0}, + {"LOAD LDTR", INTERCEPT_LOAD_LDTR, insn_lldt, SVM_EXIT_LDTR_WRITE, 0, 0}, + {"RDPMC", INTERCEPT_RDPMC, insn_rdpmc, SVM_EXIT_RDPMC, 0, 0, false, this_cpu_has_pmu}, + {"CPUID", INTERCEPT_CPUID, insn_cpuid, SVM_EXIT_CPUID, 0, 0, true}, + {"INVD", INTERCEPT_INVD, insn_invd, SVM_EXIT_INVD, 0, 0, true}, + {"PAUSE", INTERCEPT_PAUSE, insn_pause, SVM_EXIT_PAUSE, 0, 0}, + {"HLT", INTERCEPT_HLT, insn_hlt, SVM_EXIT_HLT, 0, 0}, + {"INVLPG", INTERCEPT_INVLPG, insn_invlpg, SVM_EXIT_INVLPG, 0, 0}, + {"MONITOR", INTERCEPT_MONITOR, insn_monitor, SVM_EXIT_MONITOR, 0, 0, false, this_cpu_has_mwait}, + {"MWAIT", INTERCEPT_MWAIT, insn_mwait, SVM_EXIT_MWAIT, 0, 0, false, this_cpu_has_mwait}, + {NULL}, +}; + +static void insn_intercept_test(void) +{ + u32 exit_code; + u64 exit_info_1; + u64 exit_info_2; + + for (cur_insn = 0; insn_table[cur_insn].name != NULL; ++cur_insn) { + struct insn_table insn = insn_table[cur_insn]; + + if (insn.supported_fn && !insn.supported_fn()) { + printf("\tFeature required for %s is not supported.\n", + insn_table[cur_insn].name); + continue; + } + + test_set_guest(insn.insn_func); + + if (insn.insn_func != insn_hlt && !insn.always_traps) + report(svm_vmrun() == SVM_EXIT_VMMCALL, "execute %s", insn.name); + + vmcb->control.intercept |= 1 << insn.flag; + + svm_vmrun(); + + exit_code = vmcb->control.exit_code; + exit_info_1 = vmcb->control.exit_info_1; + exit_info_2 = vmcb->control.exit_info_2; + + report(exit_code == insn.reason, + "Expected exit code: 0x%x, received exit code: 0x%x", + exit_code, insn.reason); + + if (!exit_info_1) + report(exit_info_1 == insn.exit_info_1, + "Expected exit_info_1: 0x%lx, received exit_info_1: 0x%lx", + exit_info_1, insn.exit_info_1); + if (!exit_info_2) + report(exit_info_2 == insn.exit_info_2, + "Expected exit_info_2: 0x%lx, received exit_info_2: 0x%lx", + exit_info_2, insn.exit_info_2); + + vmcb->control.intercept &= ~(1 << insn.flag); + } +} + /* TODO: verify if high 32-bits are sign- or zero-extended on bare metal */ #define TEST_BITMAP_ADDR(save_intercept, type, addr, exit_code, \ msg) { \ @@ -3564,6 +3683,7 @@ struct svm_test svm_tests[] = { TEST(svm_tsc_scale_test), TEST(pause_filter_test), TEST(svm_shutdown_intercept_test), + TEST(insn_intercept_test), { NULL, NULL, NULL, NULL, NULL, NULL, NULL } }; -- 2.51.0.261.g7ce5a0a67e-goog