Commit b4845bb6 authored by Juergen Gross's avatar Juergen Gross
Browse files

x86/xen: add central hypercall functions



Add generic hypercall functions usable for all normal (i.e. not iret)
hypercalls. Depending on the guest type and the processor vendor
different functions need to be used due to the to be used instruction
for entering the hypervisor:

- PV guests need to use syscall
- HVM/PVH guests on Intel need to use vmcall
- HVM/PVH guests on AMD and Hygon need to use vmmcall

As PVH guests need to issue hypercalls very early during boot, there
is a 4th hypercall function needed for HVM/PVH which can be used on
Intel and AMD processors. It will check the vendor type and then set
the Intel or AMD specific function to use via static_call().

This is part of XSA-466 / CVE-2024-53241.

Reported-by: default avatarAndrew Cooper <andrew.cooper3@citrix.com>
Signed-off-by: default avatarJuergen Gross <jgross@suse.com>
Co-developed-by: default avatarPeter Zijlstra <peterz@infradead.org>
parent a2796dff
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -88,6 +88,9 @@ struct xen_dm_op_buf;

extern struct { char _entry[32]; } hypercall_page[];

void xen_hypercall_func(void);
DECLARE_STATIC_CALL(xen_hypercall, xen_hypercall_func);

#define __HYPERCALL		"call hypercall_page+%c[offset]"
#define __HYPERCALL_ENTRY(x)						\
	[offset] "i" (__HYPERVISOR_##x * sizeof(hypercall_page[0]))
+65 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@

#include <linux/console.h>
#include <linux/cpu.h>
#include <linux/instrumentation.h>
#include <linux/kexec.h>
#include <linux/memblock.h>
#include <linux/slab.h>
@@ -23,6 +24,9 @@

EXPORT_SYMBOL_GPL(hypercall_page);

DEFINE_STATIC_CALL(xen_hypercall, xen_hypercall_hvm);
EXPORT_STATIC_CALL_TRAMP(xen_hypercall);

/*
 * Pointer to the xen_vcpu_info structure or
 * &HYPERVISOR_shared_info->vcpu_info[cpu]. See xen_hvm_init_shared_info
@@ -68,6 +72,67 @@ EXPORT_SYMBOL(xen_start_flags);
 */
struct shared_info *HYPERVISOR_shared_info = &xen_dummy_shared_info;

static __ref void xen_get_vendor(void)
{
	init_cpu_devs();
	cpu_detect(&boot_cpu_data);
	get_cpu_vendor(&boot_cpu_data);
}

void xen_hypercall_setfunc(void)
{
	if (static_call_query(xen_hypercall) != xen_hypercall_hvm)
		return;

	if ((boot_cpu_data.x86_vendor == X86_VENDOR_AMD ||
	     boot_cpu_data.x86_vendor == X86_VENDOR_HYGON))
		static_call_update(xen_hypercall, xen_hypercall_amd);
	else
		static_call_update(xen_hypercall, xen_hypercall_intel);
}

/*
 * Evaluate processor vendor in order to select the correct hypercall
 * function for HVM/PVH guests.
 * Might be called very early in boot before vendor has been set by
 * early_cpu_init().
 */
noinstr void *__xen_hypercall_setfunc(void)
{
	void (*func)(void);

	/*
	 * Xen is supported only on CPUs with CPUID, so testing for
	 * X86_FEATURE_CPUID is a test for early_cpu_init() having been
	 * run.
	 *
	 * Note that __xen_hypercall_setfunc() is noinstr only due to a nasty
	 * dependency chain: it is being called via the xen_hypercall static
	 * call when running as a PVH or HVM guest. Hypercalls need to be
	 * noinstr due to PV guests using hypercalls in noinstr code. So we
	 * can safely tag the function body as "instrumentation ok", since
	 * the PV guest requirement is not of interest here (xen_get_vendor()
	 * calls noinstr functions, and static_call_update_early() might do
	 * so, too).
	 */
	instrumentation_begin();

	if (!boot_cpu_has(X86_FEATURE_CPUID))
		xen_get_vendor();

	if ((boot_cpu_data.x86_vendor == X86_VENDOR_AMD ||
	     boot_cpu_data.x86_vendor == X86_VENDOR_HYGON))
		func = xen_hypercall_amd;
	else
		func = xen_hypercall_intel;

	static_call_update_early(xen_hypercall, func);

	instrumentation_end();

	return func;
}

static int xen_cpu_up_online(unsigned int cpu)
{
	xen_init_lock_cpu(cpu);
+4 −0
Original line number Diff line number Diff line
@@ -300,6 +300,10 @@ static uint32_t __init xen_platform_hvm(void)
	if (xen_pv_domain())
		return 0;

	/* Set correct hypercall function. */
	if (xen_domain)
		xen_hypercall_setfunc();

	if (xen_pvh_domain() && nopv) {
		/* Guest booting via the Xen-PVH boot entry goes here */
		pr_info("\"nopv\" parameter is ignored in PVH guest\n");
+3 −1
Original line number Diff line number Diff line
@@ -1341,6 +1341,9 @@ asmlinkage __visible void __init xen_start_kernel(struct start_info *si)

	xen_domain_type = XEN_PV_DOMAIN;
	xen_start_flags = xen_start_info->flags;
	/* Interrupts are guaranteed to be off initially. */
	early_boot_irqs_disabled = true;
	static_call_update_early(xen_hypercall, xen_hypercall_pv);

	xen_setup_features();

@@ -1431,7 +1434,6 @@ asmlinkage __visible void __init xen_start_kernel(struct start_info *si)
	WARN_ON(xen_cpuhp_setup(xen_cpu_up_prepare_pv, xen_cpu_dead_pv));

	local_irq_disable();
	early_boot_irqs_disabled = true;

	xen_raw_console_write("mapping kernel into physical memory\n");
	xen_setup_kernel_pagetable((pgd_t *)xen_start_info->pt_base,
+23 −0
Original line number Diff line number Diff line
@@ -20,9 +20,32 @@

#include <linux/init.h>
#include <linux/linkage.h>
#include <linux/objtool.h>
#include <../entry/calling.h>

.pushsection .noinstr.text, "ax"
/*
 * PV hypercall interface to the hypervisor.
 *
 * Called via inline asm(), so better preserve %rcx and %r11.
 *
 * Input:
 *	%eax: hypercall number
 *	%rdi, %rsi, %rdx, %r10, %r8: args 1..5 for the hypercall
 * Output: %rax
 */
SYM_FUNC_START(xen_hypercall_pv)
	ANNOTATE_NOENDBR
	push %rcx
	push %r11
	UNWIND_HINT_SAVE
	syscall
	UNWIND_HINT_RESTORE
	pop %r11
	pop %rcx
	RET
SYM_FUNC_END(xen_hypercall_pv)

/*
 * Disabling events is simply a matter of making the event mask
 * non-zero.
Loading