]> 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:08:58 +0000 (18:08 +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 e5e0fe10c692406165e52b7e24d5361885b06384..7de14258687f9d9a6e9097538f40f79fa9e0b1a0 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 3c61bb98c10e283ddccbcc33ce6fd7d860a95280..6006c42876ea3949d18a0320596dbaa5a1468923 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
@@ -72,6 +76,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);
index c66807dd027039864975c9d1c4e797dc76667c92..3ed6db6e805c20bf1c43da977416e92e7b5bd517 100644 (file)
@@ -299,6 +299,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 9280e15de3af55b3e08f743850a484fa61c1a12b..ee8f452cc58bb98744eb75dd69baa250979743e2 100644 (file)
@@ -1248,6 +1248,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();
 
@@ -1340,7 +1343,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,
index fd09c7301b3f9dbb72db24993eeecac17f6e7abd..aa2676a7e2a4770b73ffd598d196e2a0a3fbfe53 100644 (file)
 
 #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.
index ffaa62167f6e8ddf1da9fcc439503e5fa1215700..3635065d7560b460f36a16535b504b1cfaa50c00 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>
@@ -80,6 +82,87 @@ SYM_CODE_END(asm_cpu_bringup_and_idle)
 #endif
 #endif
 
+       .pushsection .noinstr.text, "ax"
+/*
+ * 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)
+       ENDBR
+       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
+
        ELFNOTE(Xen, XEN_ELFNOTE_GUEST_OS,       .asciz "linux")
        ELFNOTE(Xen, XEN_ELFNOTE_GUEST_VERSION,  .asciz "2.6")
        ELFNOTE(Xen, XEN_ELFNOTE_XEN_VERSION,    .asciz "xen-3.0")
index b2b2f4315b78da3cb39d88d0582e5a990fc46e4b..3a939418a2f2faa2141b9e0ca0535c04c04eff0c 100644 (file)
@@ -162,4 +162,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 */