Skip to content

Commit 31f2927

Browse files
jgross1gregkh
authored andcommitted
x86/xen: add central hypercall functions
commit b4845bb 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 <[email protected]> Signed-off-by: Juergen Gross <[email protected]> Co-developed-by: Peter Zijlstra <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 82c211e commit 31f2927

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
@@ -5,6 +5,7 @@
55
#endif
66
#include <linux/console.h>
77
#include <linux/cpu.h>
8+
#include <linux/instrumentation.h>
89
#include <linux/kexec.h>
910
#include <linux/memblock.h>
1011
#include <linux/slab.h>
@@ -28,6 +29,9 @@
2829

2930
EXPORT_SYMBOL_GPL(hypercall_page);
3031

32+
DEFINE_STATIC_CALL(xen_hypercall, xen_hypercall_hvm);
33+
EXPORT_STATIC_CALL_TRAMP(xen_hypercall);
34+
3135
/*
3236
* Pointer to the xen_vcpu_info structure or
3337
* &HYPERVISOR_shared_info->vcpu_info[cpu]. See xen_hvm_init_shared_info
@@ -73,6 +77,67 @@ EXPORT_SYMBOL(xen_start_flags);
7377
*/
7478
struct shared_info *HYPERVISOR_shared_info = &xen_dummy_shared_info;
7579

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

301+
/* Set correct hypercall function. */
302+
if (xen_domain)
303+
xen_hypercall_setfunc();
304+
301305
if (xen_pvh_domain() && nopv) {
302306
/* Guest booting via the Xen-PVH boot entry goes here */
303307
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
@@ -1329,6 +1329,9 @@ asmlinkage __visible void __init xen_start_kernel(struct start_info *si)
13291329

13301330
xen_domain_type = XEN_PV_DOMAIN;
13311331
xen_start_flags = xen_start_info->flags;
1332+
/* Interrupts are guaranteed to be off initially. */
1333+
early_boot_irqs_disabled = true;
1334+
static_call_update_early(xen_hypercall, xen_hypercall_pv);
13321335

13331336
xen_setup_features();
13341337

@@ -1419,7 +1422,6 @@ asmlinkage __visible void __init xen_start_kernel(struct start_info *si)
14191422
WARN_ON(xen_cpuhp_setup(xen_cpu_up_prepare_pv, xen_cpu_dead_pv));
14201423

14211424
local_irq_disable();
1422-
early_boot_irqs_disabled = true;
14231425

14241426
xen_raw_console_write("mapping kernel into physical memory\n");
14251427
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
@@ -181,4 +181,13 @@ static inline void xen_hvm_post_suspend(int suspend_cancelled) {}
181181

182182
void xen_add_extra_mem(unsigned long start_pfn, unsigned long n_pfns);
183183

184+
#ifdef CONFIG_XEN_PV
185+
void xen_hypercall_pv(void);
186+
#endif
187+
void xen_hypercall_hvm(void);
188+
void xen_hypercall_amd(void);
189+
void xen_hypercall_intel(void);
190+
void xen_hypercall_setfunc(void);
191+
void *__xen_hypercall_setfunc(void);
192+
184193
#endif /* XEN_OPS_H */

0 commit comments

Comments
 (0)