Skip to content

Commit

Permalink
x86/xen: add central hypercall functions
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
jgross1 authored and gregkh committed Dec 19, 2024
1 parent c7b4cfa commit a501b4d
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 1 deletion.
3 changes: 3 additions & 0 deletions arch/x86/include/asm/xen/hypercall.h
Original file line number Diff line number Diff line change
Expand Up @@ -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]))
Expand Down
65 changes: 65 additions & 0 deletions arch/x86/xen/enlighten.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions arch/x86/xen/enlighten_hvm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
4 changes: 3 additions & 1 deletion arch/x86/xen/enlighten_pv.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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,
Expand Down
22 changes: 22 additions & 0 deletions arch/x86/xen/xen-asm.S
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,30 @@

#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,
Expand Down
82 changes: 82 additions & 0 deletions arch/x86/xen/xen-head.S
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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)
Expand Down
9 changes: 9 additions & 0 deletions arch/x86/xen/xen-ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

0 comments on commit a501b4d

Please sign in to comment.