public inbox for kvm@vger.kernel.org
 help / color / mirror / Atom feed
From: Sean Christopherson <seanjc@google.com>
To: Metin Kaya <metikaya@amazon.co.uk>
Cc: pbonzini@redhat.com, kvm@vger.kernel.org, dwmw@amazon.co.uk
Subject: Re: [kvm-unit-tests PATCH] x86/access: Use HVMOP_flush_tlbs hypercall instead of invlpg() for Xen
Date: Thu, 3 Aug 2023 17:16:06 -0700	[thread overview]
Message-ID: <ZMxDRg6gWsVLeYFL@google.com> (raw)
In-Reply-To: <20230728071857.29991-1-metikaya@amazon.co.uk>

*sigh*

First off, thanks for the test, much appreciated.

However, the purpose of adding test code is to be able to actually run the damn
test.  Yeah, I got there in the end by hacking the QEMU command line generated
by KUT, but holy moly I should not have had to do that.  E.g. swapping out
"-machine accel=kvm" for "-accel kvm" just to be able to enable xen-version=0x4000a
was not a detail I wanted to find out on my own.

And the real kicker: after upgrading QEMU and figuring out the right command line,
the test fails.  Specifically, it hangs in the focused check_smep_andnot_wp() test.

My host is a HSW.  I haven't tried another host.  AFAICT, the test unexpectedly
ends up at RIP 0x0 and loops indefinitely on a page fault (the test has a nop
page fault handler so it doesn't die).

E.g. with EPT disabling so that KVM intercepts #PF:

stable-4247    [007] .....   456.192867: kvm_page_fault: vcpu 0 rip 0x0 address 0x0000000000000000 error_code 0x10
stable-4247    [007] .....   456.192868: kvm_inj_exception: #PF (0x11)
stable-4247    [007] d....   456.192868: kvm_entry: vcpu 0, rip 0x0


On Fri, Jul 28, 2023, Metin Kaya wrote:
> @@ -250,12 +251,90 @@ static void set_cr0_wp(int wp)
>  	}
>  }
>  
> +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");

Can this be easily extracted to a generic-ish xen_hypercall() helper?  I.e. make
the inline assembly reusable.

> +
> +	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
> +	 * unsupported.
> +	 */
> +	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");
> +}

All of the Xen stuff belongs in "library" code, e.g. add xen.{c,h} and prefix
the public functions with xen_.  And drop the HVMOP_flush_tlbs probing from
xen_init_hypercalls(), i.e. make it a generic helper.

And I think it also makes sense to have a separate testcase for the Xen hypercall,
e.g. so that we test both cases when Xen is present, eliminate flakiness from Xen
setup failing, make it easy to run or skip the Xen test, make it easy to see which
flavor is running (checking the log is annoying), etc.

That way you can have x86/access_test.c's main() pivot on a command line param
and skip the test if do_hvm_op_flush_tlbs() fails (though I would call it something
like xen_flush_tlbs()).

>  static void clear_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);

And rather than hypercall_page, end up with "bool use_xen_pv_tlb_flush" or so.

      reply	other threads:[~2023-08-04  0:16 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-07-28  7:18 [kvm-unit-tests PATCH] x86/access: Use HVMOP_flush_tlbs hypercall instead of invlpg() for Xen Metin Kaya
2023-08-04  0:16 ` Sean Christopherson [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ZMxDRg6gWsVLeYFL@google.com \
    --to=seanjc@google.com \
    --cc=dwmw@amazon.co.uk \
    --cc=kvm@vger.kernel.org \
    --cc=metikaya@amazon.co.uk \
    --cc=pbonzini@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox