On Wed, 2023-07-26 at 13:07 -0700, Sean Christopherson wrote: > On Tue, Jul 25, 2023, David Woodhouse wrote: > > On Fri, 2023-05-26 at 13:32 -0700, Sean Christopherson wrote: > > > : Aha! And QEMU appears to have Xen emulation support. That means KVM-Unit-Tests > > > : is an option. Specifically, extend the "access" test to use this hypercall instead > > > : of INVLPG. That'll verify that the flush is actually being performed as expteced. > > > > That works. Metin has a better version that actually sets up the > > hypercall page properly and uses it, but that one bails out when Xen > > support isn't present, and doesn't show the failure mode quite so > > clearly. This is the simple version: > > IIUC, y'all have already written both tests, so why not post both? I certainly > won't object to more tests if they provide different coverage. Yeah, it just needed cleaning up. This is what we have; Metin will submit it for real after a little more polishing. It modifies the existing access test so that *if* it's run in a Xen environment, and *if* the HVMOP_flush_tlbs call returns success instead of -ENOSYS, it'll use that instead of invlpg. In itself, that doesn't give us an automatic regression tests, because you still need to run it manually — as before, qemu-system-x86_64 -device isa-debug-exit,iobase=0xf4,iosize=0x4 -vnc none -serial stdio -device pci-testdev --accel kvm,xen-version=0x4000a,kernel-irqchip=split -kernel ~/access_test.flat If we really want to, we can look at making it run that way when qemu and the host kernel support Xen guests...? diff --git a/x86/access.c b/x86/access.c index 83c8221..8c6e44a 100644 --- a/x86/access.c +++ b/x86/access.c @@ -4,6 +4,7 @@ #include "asm/page.h" #include "x86/vm.h" #include "access.h" +#include "alloc_page.h" #define true 1 #define false 0 @@ -253,12 +254,90 @@ static void clear_user_mask(pt_element_t *ptep, int level, unsigned long virt) *ptep &= ~PT_USER_MASK; } +uint8_t *hypercall_page; + +#define __HYPERVISOR_hvm_op 34 +#define HVMOP_flush_tlbs 5 + +static inline int do_hvm_op_flush_tlbs(void) +{ + long res = 0, _a1 = (long)(HVMOP_flush_tlbs), _a2 = (long)(NULL); + + asm volatile ("call *%[offset]" +#if defined(__x86_64__) + : "=a" (res), "+D" (_a1), "+S" (_a2) +#else + : "=a" (res), "+b" (_a1), "+c" (_a2) +#endif + : [offset] "r" (hypercall_page + (__HYPERVISOR_hvm_op * 32)) + : "memory"); + + if (res) + printf("hvm_op/HVMOP_flush_tlbs failed: %ld.", res); + + return (int)res; +} + +#define XEN_CPUID_FIRST_LEAF 0x40000000 +#define XEN_CPUID_SIGNATURE_EBX 0x566e6558 /* "XenV" */ +#define XEN_CPUID_SIGNATURE_ECX 0x65584d4d /* "MMXe" */ +#define XEN_CPUID_SIGNATURE_EDX 0x4d4d566e /* "nVMM" */ + +static void init_hypercalls(void) +{ + struct cpuid c; + u32 base; + bool found = false; + + for (base = XEN_CPUID_FIRST_LEAF; base < XEN_CPUID_FIRST_LEAF + 0x10000; + base += 0x100) { + c = cpuid(base); + if ((c.b == XEN_CPUID_SIGNATURE_EBX) && + (c.c == XEN_CPUID_SIGNATURE_ECX) && + (c.d == XEN_CPUID_SIGNATURE_EDX) && + ((c.a - base) >= 2)) { + found = true; + break; + } + } + if (!found) { + printf("Using native invlpg instruction\n"); + return; + } + + hypercall_page = alloc_pages_flags(0, AREA_ANY | FLAG_DONTZERO); + if (!hypercall_page) + report_abort("failed to allocate hypercall page"); + + memset(hypercall_page, 0xc3, PAGE_SIZE); + + c = cpuid(base + 2); + wrmsr(c.b, (u64)hypercall_page); + barrier(); + + if (hypercall_page[0] == 0xc3) + report_abort("Hypercall page not initialised correctly\n"); + + /* + * Fall back to invlpg instruction if HVMOP_flush_tlbs hypercall is + * unsuported. + */ + if (do_hvm_op_flush_tlbs()) { + printf("Using native invlpg instruction\n"); + free_page(hypercall_page); + hypercall_page = NULL; + return; + } + + printf("Using Xen HVMOP_flush_tlbs hypercall\n"); +} + static void set_user_mask(pt_element_t *ptep, int level, unsigned long virt) { *ptep |= PT_USER_MASK; /* Flush to avoid spurious #PF */ - invlpg((void*)virt); + hypercall_page ? do_hvm_op_flush_tlbs() : invlpg((void*)virt); } static unsigned set_cr4_smep(ac_test_t *at, int smep) @@ -577,7 +656,7 @@ fault: static void ac_set_expected_status(ac_test_t *at) { - invlpg(at->virt); + hypercall_page ? do_hvm_op_flush_tlbs() : invlpg((void*)at->virt); if (at->ptep) at->expected_pte = *at->ptep; @@ -1162,6 +1241,10 @@ int ac_test_run(int pt_levels) printf("run\n"); tests = successes = 0; + setup_vm(); + + init_hypercalls(); + shadow_cr0 = read_cr0(); shadow_cr4 = read_cr4(); shadow_cr3 = read_cr3(); @@ -1234,6 +1317,9 @@ int ac_test_run(int pt_levels) successes += ac_test_cases[i](&pt_env); } + if (hypercall_page) + free_page(hypercall_page); + printf("\n%d tests, %d failures\n", tests, tests - successes); return successes == tests; -- 2.34.1
Attachment:
smime.p7s
Description: S/MIME cryptographic signature