On Wed, Aug 20, 2025 at 12:29 PM Kevin Cheng <chengkev@xxxxxxxxxx> wrote: > > 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 <chengkev@xxxxxxxxxx> > --- > 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 > Just checking in as it's been a couple weeks :) If there is anyone else who would be better suited to take a look at these please let me know and I can cc them as well!