Skip to content

Commit 7e44e70

Browse files
jgross1gregkh
authored andcommitted
x86/xen: add central hypercall functions
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 <[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 fa71985 commit 7e44e70

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/slab.h>
1011
#include <linux/panic_notifier.h>
@@ -27,6 +28,9 @@
2728

2829
EXPORT_SYMBOL_GPL(hypercall_page);
2930

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

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

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

12491249
xen_domain_type = XEN_PV_DOMAIN;
12501250
xen_start_flags = xen_start_info->flags;
1251+
/* Interrupts are guaranteed to be off initially. */
1252+
early_boot_irqs_disabled = true;
1253+
static_call_update_early(xen_hypercall, xen_hypercall_pv);
12511254

12521255
xen_setup_features();
12531256

@@ -1340,7 +1343,6 @@ asmlinkage __visible void __init xen_start_kernel(struct start_info *si)
13401343
WARN_ON(xen_cpuhp_setup(xen_cpu_up_prepare_pv, xen_cpu_dead_pv));
13411344

13421345
local_irq_disable();
1343-
early_boot_irqs_disabled = true;
13441346

13451347
xen_raw_console_write("mapping kernel into physical memory\n");
13461348
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>
@@ -80,6 +82,87 @@ SYM_CODE_END(asm_cpu_bringup_and_idle)
8082
#endif
8183
#endif
8284

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

165+
#ifdef CONFIG_XEN_PV
166+
void xen_hypercall_pv(void);
167+
#endif
168+
void xen_hypercall_hvm(void);
169+
void xen_hypercall_amd(void);
170+
void xen_hypercall_intel(void);
171+
void xen_hypercall_setfunc(void);
172+
void *__xen_hypercall_setfunc(void);
173+
165174
#endif /* XEN_OPS_H */

0 commit comments

Comments
 (0)