]> 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:07:23 +0000 (18:07 +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 95d970359e1746e230eadd5c8e0ae776ad3720ba..d75c392651e730075ef5770e0655de92247ee836 100644 (file)
@@ -5,6 +5,7 @@
 #endif
 #include <linux/console.h>
 #include <linux/cpu.h>
+#include <linux/instrumentation.h>
 #include <linux/kexec.h>
 #include <linux/slab.h>
 #include <linux/panic_notifier.h>
@@ -27,6 +28,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
@@ -99,6 +103,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_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);
index e68ea5f4ad1ce0c3bfadafec4e01dc44fc2664e5..c041810392f601f70da9f5a5a4af1c1e9cfaee52 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 998db0257e2ad3dc23a18edf12f6aeb32ee0b820..ce6df600f8fb3cab7b02864c8f3792ba60d73663 100644 (file)
@@ -1207,6 +1207,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();
 
@@ -1304,7 +1307,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 427519d78bbf9d4b2cabd3dcc0ec7162389a6fda..045760ddac6abe94afadf4056b61bbdb30f7d572 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 71f31032c635f57f6e9788fbc8e561e8707e53d4..e0b877c8a7569e279925d1654dd5d0a6899bf4ef 100644 (file)
@@ -164,4 +164,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 */