]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
x86/xen: add central hypercall functions
authorJuergen Gross <jgross@suse.com>
Thu, 17 Oct 2024 09:00:52 +0000 (11:00 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 19 Dec 2024 17:06:13 +0000 (18:06 +0100)
commit b4845bb6383821a9516ce30af3a27dc873e37fd4 upstream.

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: Andrew Cooper <andrew.cooper3@citrix.com>
Signed-off-by: Juergen Gross <jgross@suse.com>
Co-developed-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/x86/include/asm/xen/hypercall.h
arch/x86/xen/enlighten.c
arch/x86/xen/enlighten_hvm.c
arch/x86/xen/enlighten_pv.c
arch/x86/xen/xen-asm.S
arch/x86/xen/xen-head.S
arch/x86/xen/xen-ops.h

index 454b20815f3574ac448b7fe284da01b43ef5b27a..82ef730aab256b8b3d6bbd466e2281eae35b223b 100644 (file)
@@ -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]))
index 0f68c6da7382bca2cf4b037cbbe15a96ff9718ed..de78a0762fda0464647c21a8a4f76515ad8a6122 100644 (file)
@@ -4,6 +4,7 @@
 #include <linux/memblock.h>
 #endif
 #include <linux/cpu.h>
+#include <linux/instrumentation.h>
 #include <linux/kexec.h>
 #include <linux/slab.h>
 
@@ -22,6 +23,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
@@ -94,6 +98,67 @@ struct shared_info *HYPERVISOR_shared_info = &xen_dummy_shared_info;
  */
 int xen_have_vcpu_info_placement = 1;
 
+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_KEY(xen_hypercall).func != 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);
index ec50b7423a4c8609118f3f6f1ee67aa9dd238b31..f591fc5f7022ba46f2075e53b1734d0d2f38a3c3 100644 (file)
@@ -284,6 +284,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");
index b1efc4b4f42ad59ec57e19a0f4a7207b764067c7..c2cd3074e19d5b2dc20e6354e387d1455e868b7f 100644 (file)
@@ -1220,6 +1220,9 @@ asmlinkage __visible void __init xen_start_kernel(void)
 
        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();
 
@@ -1324,7 +1327,6 @@ asmlinkage __visible void __init xen_start_kernel(void)
        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,
index 94bed27f67291f7a0e3506a43b67c269fd4607f6..2055206b0f4154b6ca09aee05e9e35bca2280ddb 100644 (file)
 
 #include <linux/init.h>
 #include <linux/linkage.h>
+#include <linux/objtool.h>
 #include <../entry/calling.h>
 
+/*
+ * 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)
+       push %rcx
+       push %r11
+       UNWIND_HINT_SAVE
+       syscall
+       UNWIND_HINT_RESTORE
+       pop %r11
+       pop %rcx
+       RET
+SYM_FUNC_END(xen_hypercall_pv)
+
 /*
  * Enable events.  This clears the event mask and tests the pending
  * event status with one and operation.  If there are pending events,
index 2a3ef5fcba34b92c32ff85c946f8f1ec6c5f69ee..61f904daee85f675f6eafc1bf68e890fce634fd5 100644 (file)
@@ -6,9 +6,11 @@
 
 #include <linux/elfnote.h>
 #include <linux/init.h>
+#include <linux/instrumentation.h>
 
 #include <asm/boot.h>
 #include <asm/asm.h>
+#include <asm/frame.h>
 #include <asm/msr.h>
 #include <asm/page_types.h>
 #include <asm/percpu.h>
@@ -64,6 +66,86 @@ SYM_CODE_END(asm_cpu_bringup_and_idle)
 #endif
 #endif
 
+       .pushsection .text
+/*
+ * Xen hypercall interface to the hypervisor.
+ *
+ * Input:
+ *     %eax: hypercall number
+ *   32-bit:
+ *     %ebx, %ecx, %edx, %esi, %edi: args 1..5 for the hypercall
+ *   64-bit:
+ *     %rdi, %rsi, %rdx, %r10, %r8: args 1..5 for the hypercall
+ * Output: %[er]ax
+ */
+SYM_FUNC_START(xen_hypercall_hvm)
+       FRAME_BEGIN
+       /* Save all relevant registers (caller save and arguments). */
+#ifdef CONFIG_X86_32
+       push %eax
+       push %ebx
+       push %ecx
+       push %edx
+       push %esi
+       push %edi
+#else
+       push %rax
+       push %rcx
+       push %rdx
+       push %rdi
+       push %rsi
+       push %r11
+       push %r10
+       push %r9
+       push %r8
+#ifdef CONFIG_FRAME_POINTER
+       pushq $0        /* Dummy push for stack alignment. */
+#endif
+#endif
+       /* Set the vendor specific function. */
+       call __xen_hypercall_setfunc
+       /* Set ZF = 1 if AMD, Restore saved registers. */
+#ifdef CONFIG_X86_32
+       lea xen_hypercall_amd, %ebx
+       cmp %eax, %ebx
+       pop %edi
+       pop %esi
+       pop %edx
+       pop %ecx
+       pop %ebx
+       pop %eax
+#else
+       lea xen_hypercall_amd(%rip), %rbx
+       cmp %rax, %rbx
+#ifdef CONFIG_FRAME_POINTER
+       pop %rax        /* Dummy pop. */
+#endif
+       pop %r8
+       pop %r9
+       pop %r10
+       pop %r11
+       pop %rsi
+       pop %rdi
+       pop %rdx
+       pop %rcx
+       pop %rax
+#endif
+       /* Use correct hypercall function. */
+       jz xen_hypercall_amd
+       jmp xen_hypercall_intel
+SYM_FUNC_END(xen_hypercall_hvm)
+
+SYM_FUNC_START(xen_hypercall_amd)
+       vmmcall
+       RET
+SYM_FUNC_END(xen_hypercall_amd)
+
+SYM_FUNC_START(xen_hypercall_intel)
+       vmcall
+       RET
+SYM_FUNC_END(xen_hypercall_intel)
+       .popsection
+
 .pushsection .text
        .balign PAGE_SIZE
 SYM_CODE_START(hypercall_page)
index 98242430d07e7c97b762b37d77abb284d067c3c8..2fc9077290db7d3e88a1bff94ee3f6398d5fd35c 100644 (file)
@@ -161,4 +161,13 @@ void xen_hvm_post_suspend(int suspend_cancelled);
 static inline void xen_hvm_post_suspend(int suspend_cancelled) {}
 #endif
 
+#ifdef CONFIG_XEN_PV
+void xen_hypercall_pv(void);
+#endif
+void xen_hypercall_hvm(void);
+void xen_hypercall_amd(void);
+void xen_hypercall_intel(void);
+void xen_hypercall_setfunc(void);
+void *__xen_hypercall_setfunc(void);
+
 #endif /* XEN_OPS_H */