Skip to content

Commit b4845bb

Browse files
committed
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: Andrew Cooper <[email protected]> Signed-off-by: Juergen Gross <[email protected]> Co-developed-by: Peter Zijlstra <[email protected]>
1 parent a2796df commit b4845bb

File tree

7 files changed

+190
-1
lines changed

7 files changed

+190
-1
lines changed

arch/x86/include/asm/xen/hypercall.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ struct xen_dm_op_buf;
8888

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

91+
void xen_hypercall_func(void);
92+
DECLARE_STATIC_CALL(xen_hypercall, xen_hypercall_func);
93+
9194
#define __HYPERCALL "call hypercall_page+%c[offset]"
9295
#define __HYPERCALL_ENTRY(x) \
9396
[offset] "i" (__HYPERVISOR_##x * sizeof(hypercall_page[0]))

arch/x86/xen/enlighten.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include <linux/console.h>
44
#include <linux/cpu.h>
5+
#include <linux/instrumentation.h>
56
#include <linux/kexec.h>
67
#include <linux/memblock.h>
78
#include <linux/slab.h>
@@ -23,6 +24,9 @@
2324

2425
EXPORT_SYMBOL_GPL(hypercall_page);
2526

27+
DEFINE_STATIC_CALL(xen_hypercall, xen_hypercall_hvm);
28+
EXPORT_STATIC_CALL_TRAMP(xen_hypercall);
29+
2630
/*
2731
* Pointer to the xen_vcpu_info structure or
2832
* &HYPERVISOR_shared_info->vcpu_info[cpu]. See xen_hvm_init_shared_info
@@ -68,6 +72,67 @@ EXPORT_SYMBOL(xen_start_flags);
6872
*/
6973
struct shared_info *HYPERVISOR_shared_info = &xen_dummy_shared_info;
7074

75+
static __ref void xen_get_vendor(void)
76+
{
77+
init_cpu_devs();
78+
cpu_detect(&boot_cpu_data);
79+
get_cpu_vendor(&boot_cpu_data);
80+
}
81+
82+
void xen_hypercall_setfunc(void)
83+
{
84+
if (static_call_query(xen_hypercall) != xen_hypercall_hvm)
85+
return;
86+
87+
if ((boot_cpu_data.x86_vendor == X86_VENDOR_AMD ||
88+
boot_cpu_data.x86_vendor == X86_VENDOR_HYGON))
89+
static_call_update(xen_hypercall, xen_hypercall_amd);
90+
else
91+
static_call_update(xen_hypercall, xen_hypercall_intel);
92+
}
93+
94+
/*
95+
* Evaluate processor vendor in order to select the correct hypercall
96+
* function for HVM/PVH guests.
97+
* Might be called very early in boot before vendor has been set by
98+
* early_cpu_init().
99+
*/
100+
noinstr void *__xen_hypercall_setfunc(void)
101+
{
102+
void (*func)(void);
103+
104+
/*
105+
* Xen is supported only on CPUs with CPUID, so testing for
106+
* X86_FEATURE_CPUID is a test for early_cpu_init() having been
107+
* run.
108+
*
109+
* Note that __xen_hypercall_setfunc() is noinstr only due to a nasty
110+
* dependency chain: it is being called via the xen_hypercall static
111+
* call when running as a PVH or HVM guest. Hypercalls need to be
112+
* noinstr due to PV guests using hypercalls in noinstr code. So we
113+
* can safely tag the function body as "instrumentation ok", since
114+
* the PV guest requirement is not of interest here (xen_get_vendor()
115+
* calls noinstr functions, and static_call_update_early() might do
116+
* so, too).
117+
*/
118+
instrumentation_begin();
119+
120+
if (!boot_cpu_has(X86_FEATURE_CPUID))
121+
xen_get_vendor();
122+
123+
if ((boot_cpu_data.x86_vendor == X86_VENDOR_AMD ||
124+
boot_cpu_data.x86_vendor == X86_VENDOR_HYGON))
125+
func = xen_hypercall_amd;
126+
else
127+
func = xen_hypercall_intel;
128+
129+
static_call_update_early(xen_hypercall, func);
130+
131+
instrumentation_end();
132+
133+
return func;
134+
}
135+
71136
static int xen_cpu_up_online(unsigned int cpu)
72137
{
73138
xen_init_lock_cpu(cpu);

arch/x86/xen/enlighten_hvm.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,10 @@ static uint32_t __init xen_platform_hvm(void)
300300
if (xen_pv_domain())
301301
return 0;
302302

303+
/* Set correct hypercall function. */
304+
if (xen_domain)
305+
xen_hypercall_setfunc();
306+
303307
if (xen_pvh_domain() && nopv) {
304308
/* Guest booting via the Xen-PVH boot entry goes here */
305309
pr_info("\"nopv\" parameter is ignored in PVH guest\n");

arch/x86/xen/enlighten_pv.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1341,6 +1341,9 @@ asmlinkage __visible void __init xen_start_kernel(struct start_info *si)
13411341

13421342
xen_domain_type = XEN_PV_DOMAIN;
13431343
xen_start_flags = xen_start_info->flags;
1344+
/* Interrupts are guaranteed to be off initially. */
1345+
early_boot_irqs_disabled = true;
1346+
static_call_update_early(xen_hypercall, xen_hypercall_pv);
13441347

13451348
xen_setup_features();
13461349

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

14331436
local_irq_disable();
1434-
early_boot_irqs_disabled = true;
14351437

14361438
xen_raw_console_write("mapping kernel into physical memory\n");
14371439
xen_setup_kernel_pagetable((pgd_t *)xen_start_info->pt_base,

arch/x86/xen/xen-asm.S

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,32 @@
2020

2121
#include <linux/init.h>
2222
#include <linux/linkage.h>
23+
#include <linux/objtool.h>
2324
#include <../entry/calling.h>
2425

2526
.pushsection .noinstr.text, "ax"
27+
/*
28+
* PV hypercall interface to the hypervisor.
29+
*
30+
* Called via inline asm(), so better preserve %rcx and %r11.
31+
*
32+
* Input:
33+
* %eax: hypercall number
34+
* %rdi, %rsi, %rdx, %r10, %r8: args 1..5 for the hypercall
35+
* Output: %rax
36+
*/
37+
SYM_FUNC_START(xen_hypercall_pv)
38+
ANNOTATE_NOENDBR
39+
push %rcx
40+
push %r11
41+
UNWIND_HINT_SAVE
42+
syscall
43+
UNWIND_HINT_RESTORE
44+
pop %r11
45+
pop %rcx
46+
RET
47+
SYM_FUNC_END(xen_hypercall_pv)
48+
2649
/*
2750
* Disabling events is simply a matter of making the event mask
2851
* non-zero.

arch/x86/xen/xen-head.S

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66

77
#include <linux/elfnote.h>
88
#include <linux/init.h>
9+
#include <linux/instrumentation.h>
910

1011
#include <asm/boot.h>
1112
#include <asm/asm.h>
13+
#include <asm/frame.h>
1214
#include <asm/msr.h>
1315
#include <asm/page_types.h>
1416
#include <asm/percpu.h>
@@ -87,6 +89,87 @@ SYM_CODE_END(xen_cpu_bringup_again)
8789
#endif
8890
#endif
8991

92+
.pushsection .noinstr.text, "ax"
93+
/*
94+
* Xen hypercall interface to the hypervisor.
95+
*
96+
* Input:
97+
* %eax: hypercall number
98+
* 32-bit:
99+
* %ebx, %ecx, %edx, %esi, %edi: args 1..5 for the hypercall
100+
* 64-bit:
101+
* %rdi, %rsi, %rdx, %r10, %r8: args 1..5 for the hypercall
102+
* Output: %[er]ax
103+
*/
104+
SYM_FUNC_START(xen_hypercall_hvm)
105+
ENDBR
106+
FRAME_BEGIN
107+
/* Save all relevant registers (caller save and arguments). */
108+
#ifdef CONFIG_X86_32
109+
push %eax
110+
push %ebx
111+
push %ecx
112+
push %edx
113+
push %esi
114+
push %edi
115+
#else
116+
push %rax
117+
push %rcx
118+
push %rdx
119+
push %rdi
120+
push %rsi
121+
push %r11
122+
push %r10
123+
push %r9
124+
push %r8
125+
#ifdef CONFIG_FRAME_POINTER
126+
pushq $0 /* Dummy push for stack alignment. */
127+
#endif
128+
#endif
129+
/* Set the vendor specific function. */
130+
call __xen_hypercall_setfunc
131+
/* Set ZF = 1 if AMD, Restore saved registers. */
132+
#ifdef CONFIG_X86_32
133+
lea xen_hypercall_amd, %ebx
134+
cmp %eax, %ebx
135+
pop %edi
136+
pop %esi
137+
pop %edx
138+
pop %ecx
139+
pop %ebx
140+
pop %eax
141+
#else
142+
lea xen_hypercall_amd(%rip), %rbx
143+
cmp %rax, %rbx
144+
#ifdef CONFIG_FRAME_POINTER
145+
pop %rax /* Dummy pop. */
146+
#endif
147+
pop %r8
148+
pop %r9
149+
pop %r10
150+
pop %r11
151+
pop %rsi
152+
pop %rdi
153+
pop %rdx
154+
pop %rcx
155+
pop %rax
156+
#endif
157+
/* Use correct hypercall function. */
158+
jz xen_hypercall_amd
159+
jmp xen_hypercall_intel
160+
SYM_FUNC_END(xen_hypercall_hvm)
161+
162+
SYM_FUNC_START(xen_hypercall_amd)
163+
vmmcall
164+
RET
165+
SYM_FUNC_END(xen_hypercall_amd)
166+
167+
SYM_FUNC_START(xen_hypercall_intel)
168+
vmcall
169+
RET
170+
SYM_FUNC_END(xen_hypercall_intel)
171+
.popsection
172+
90173
ELFNOTE(Xen, XEN_ELFNOTE_GUEST_OS, .asciz "linux")
91174
ELFNOTE(Xen, XEN_ELFNOTE_GUEST_VERSION, .asciz "2.6")
92175
ELFNOTE(Xen, XEN_ELFNOTE_XEN_VERSION, .asciz "xen-3.0")

arch/x86/xen/xen-ops.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,4 +326,13 @@ static inline void xen_smp_intr_free_pv(unsigned int cpu) {}
326326
static inline void xen_smp_count_cpus(void) { }
327327
#endif /* CONFIG_SMP */
328328

329+
#ifdef CONFIG_XEN_PV
330+
void xen_hypercall_pv(void);
331+
#endif
332+
void xen_hypercall_hvm(void);
333+
void xen_hypercall_amd(void);
334+
void xen_hypercall_intel(void);
335+
void xen_hypercall_setfunc(void);
336+
void *__xen_hypercall_setfunc(void);
337+
329338
#endif /* XEN_OPS_H */

0 commit comments

Comments
 (0)