Skip to content

Commit 0e20f1f

Browse files
author
Peter Zijlstra
committed
x86/hyperv: Clean up hv_do_hypercall()
What used to be a simple few instructions has turned into a giant mess (for x86_64). Not only does it use static_branch wrong, it mixes it with dynamic branches for no apparent reason. Notably it uses static_branch through an out-of-line function call, which completely defeats the purpose, since instead of a simple JMP/NOP site, you get a CALL+RET+TEST+Jcc sequence in return, which is absolutely idiotic. Add to that a dynamic test of hyperv_paravisor_present, something which is set once and never changed. Replace all this idiocy with a single direct function call to the right hypercall variant. Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Reviewed-by: Michael Kelley <mhklinux@outlook.com> Tested-by: Michael Kelley <mhklinux@outlook.com> Acked-by: Sean Christopherson <seanjc@google.com> Link: https://lkml.kernel.org/r/20250714103440.897136093@infradead.org
1 parent a1d34a4 commit 0e20f1f

4 files changed

Lines changed: 89 additions & 102 deletions

File tree

arch/x86/hyperv/hv_init.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,27 @@
3737
#include <linux/export.h>
3838

3939
void *hv_hypercall_pg;
40+
41+
#ifdef CONFIG_X86_64
42+
u64 hv_std_hypercall(u64 control, u64 param1, u64 param2)
43+
{
44+
u64 hv_status;
45+
46+
if (!hv_hypercall_pg)
47+
return U64_MAX;
48+
49+
register u64 __r8 asm("r8") = param2;
50+
asm volatile (CALL_NOSPEC
51+
: "=a" (hv_status), ASM_CALL_CONSTRAINT,
52+
"+c" (control), "+d" (param1), "+r" (__r8)
53+
: THUNK_TARGET(hv_hypercall_pg)
54+
: "cc", "memory", "r9", "r10", "r11");
55+
56+
return hv_status;
57+
}
58+
#else
4059
EXPORT_SYMBOL_GPL(hv_hypercall_pg);
60+
#endif
4161

4262
union hv_ghcb * __percpu *hv_ghcb_pg;
4363

arch/x86/hyperv/ivm.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,9 +385,23 @@ int hv_snp_boot_ap(u32 apic_id, unsigned long start_ip, unsigned int cpu)
385385
return ret;
386386
}
387387

388+
u64 hv_snp_hypercall(u64 control, u64 param1, u64 param2)
389+
{
390+
u64 hv_status;
391+
392+
register u64 __r8 asm("r8") = param2;
393+
asm volatile("vmmcall"
394+
: "=a" (hv_status), ASM_CALL_CONSTRAINT,
395+
"+c" (control), "+d" (param1), "+r" (__r8)
396+
: : "cc", "memory", "r9", "r10", "r11");
397+
398+
return hv_status;
399+
}
400+
388401
#else
389402
static inline void hv_ghcb_msr_write(u64 msr, u64 value) {}
390403
static inline void hv_ghcb_msr_read(u64 msr, u64 *value) {}
404+
u64 hv_snp_hypercall(u64 control, u64 param1, u64 param2) { return U64_MAX; }
391405
#endif /* CONFIG_AMD_MEM_ENCRYPT */
392406

393407
#ifdef CONFIG_INTEL_TDX_GUEST
@@ -437,6 +451,7 @@ u64 hv_tdx_hypercall(u64 control, u64 param1, u64 param2)
437451
#else
438452
static inline void hv_tdx_msr_write(u64 msr, u64 value) {}
439453
static inline void hv_tdx_msr_read(u64 msr, u64 *value) {}
454+
u64 hv_tdx_hypercall(u64 control, u64 param1, u64 param2) { return U64_MAX; }
440455
#endif /* CONFIG_INTEL_TDX_GUEST */
441456

442457
#if defined(CONFIG_AMD_MEM_ENCRYPT) || defined(CONFIG_INTEL_TDX_GUEST)

arch/x86/include/asm/mshyperv.h

Lines changed: 41 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <linux/nmi.h>
77
#include <linux/msi.h>
88
#include <linux/io.h>
9+
#include <linux/static_call.h>
910
#include <asm/nospec-branch.h>
1011
#include <asm/paravirt.h>
1112
#include <asm/msr.h>
@@ -39,16 +40,21 @@ static inline unsigned char hv_get_nmi_reason(void)
3940
return 0;
4041
}
4142

42-
#if IS_ENABLED(CONFIG_HYPERV)
43-
extern bool hyperv_paravisor_present;
43+
extern u64 hv_tdx_hypercall(u64 control, u64 param1, u64 param2);
44+
extern u64 hv_snp_hypercall(u64 control, u64 param1, u64 param2);
45+
extern u64 hv_std_hypercall(u64 control, u64 param1, u64 param2);
4446

47+
#if IS_ENABLED(CONFIG_HYPERV)
4548
extern void *hv_hypercall_pg;
4649

4750
extern union hv_ghcb * __percpu *hv_ghcb_pg;
4851

4952
bool hv_isolation_type_snp(void);
5053
bool hv_isolation_type_tdx(void);
51-
u64 hv_tdx_hypercall(u64 control, u64 param1, u64 param2);
54+
55+
#ifdef CONFIG_X86_64
56+
DECLARE_STATIC_CALL(hv_hypercall, hv_std_hypercall);
57+
#endif
5258

5359
/*
5460
* DEFAULT INIT GPAT and SEGMENT LIMIT value in struct VMSA
@@ -65,37 +71,15 @@ static inline u64 hv_do_hypercall(u64 control, void *input, void *output)
6571
{
6672
u64 input_address = input ? virt_to_phys(input) : 0;
6773
u64 output_address = output ? virt_to_phys(output) : 0;
68-
u64 hv_status;
6974

7075
#ifdef CONFIG_X86_64
71-
if (hv_isolation_type_tdx() && !hyperv_paravisor_present)
72-
return hv_tdx_hypercall(control, input_address, output_address);
73-
74-
if (hv_isolation_type_snp() && !hyperv_paravisor_present) {
75-
__asm__ __volatile__("mov %[output_address], %%r8\n"
76-
"vmmcall"
77-
: "=a" (hv_status), ASM_CALL_CONSTRAINT,
78-
"+c" (control), "+d" (input_address)
79-
: [output_address] "r" (output_address)
80-
: "cc", "memory", "r8", "r9", "r10", "r11");
81-
return hv_status;
82-
}
83-
84-
if (!hv_hypercall_pg)
85-
return U64_MAX;
86-
87-
__asm__ __volatile__("mov %[output_address], %%r8\n"
88-
CALL_NOSPEC
89-
: "=a" (hv_status), ASM_CALL_CONSTRAINT,
90-
"+c" (control), "+d" (input_address)
91-
: [output_address] "r" (output_address),
92-
THUNK_TARGET(hv_hypercall_pg)
93-
: "cc", "memory", "r8", "r9", "r10", "r11");
76+
return static_call_mod(hv_hypercall)(control, input_address, output_address);
9477
#else
9578
u32 input_address_hi = upper_32_bits(input_address);
9679
u32 input_address_lo = lower_32_bits(input_address);
9780
u32 output_address_hi = upper_32_bits(output_address);
9881
u32 output_address_lo = lower_32_bits(output_address);
82+
u64 hv_status;
9983

10084
if (!hv_hypercall_pg)
10185
return U64_MAX;
@@ -108,48 +92,30 @@ static inline u64 hv_do_hypercall(u64 control, void *input, void *output)
10892
"D"(output_address_hi), "S"(output_address_lo),
10993
THUNK_TARGET(hv_hypercall_pg)
11094
: "cc", "memory");
111-
#endif /* !x86_64 */
11295
return hv_status;
96+
#endif /* !x86_64 */
11397
}
11498

11599
/* Fast hypercall with 8 bytes of input and no output */
116100
static inline u64 _hv_do_fast_hypercall8(u64 control, u64 input1)
117101
{
118-
u64 hv_status;
119-
120102
#ifdef CONFIG_X86_64
121-
if (hv_isolation_type_tdx() && !hyperv_paravisor_present)
122-
return hv_tdx_hypercall(control, input1, 0);
123-
124-
if (hv_isolation_type_snp() && !hyperv_paravisor_present) {
125-
__asm__ __volatile__(
126-
"vmmcall"
127-
: "=a" (hv_status), ASM_CALL_CONSTRAINT,
128-
"+c" (control), "+d" (input1)
129-
:: "cc", "r8", "r9", "r10", "r11");
130-
} else {
131-
__asm__ __volatile__(CALL_NOSPEC
132-
: "=a" (hv_status), ASM_CALL_CONSTRAINT,
133-
"+c" (control), "+d" (input1)
134-
: THUNK_TARGET(hv_hypercall_pg)
135-
: "cc", "r8", "r9", "r10", "r11");
136-
}
103+
return static_call_mod(hv_hypercall)(control, input1, 0);
137104
#else
138-
{
139-
u32 input1_hi = upper_32_bits(input1);
140-
u32 input1_lo = lower_32_bits(input1);
141-
142-
__asm__ __volatile__ (CALL_NOSPEC
143-
: "=A"(hv_status),
144-
"+c"(input1_lo),
145-
ASM_CALL_CONSTRAINT
146-
: "A" (control),
147-
"b" (input1_hi),
148-
THUNK_TARGET(hv_hypercall_pg)
149-
: "cc", "edi", "esi");
150-
}
151-
#endif
105+
u32 input1_hi = upper_32_bits(input1);
106+
u32 input1_lo = lower_32_bits(input1);
107+
u64 hv_status;
108+
109+
__asm__ __volatile__ (CALL_NOSPEC
110+
: "=A"(hv_status),
111+
"+c"(input1_lo),
112+
ASM_CALL_CONSTRAINT
113+
: "A" (control),
114+
"b" (input1_hi),
115+
THUNK_TARGET(hv_hypercall_pg)
116+
: "cc", "edi", "esi");
152117
return hv_status;
118+
#endif
153119
}
154120

155121
static inline u64 hv_do_fast_hypercall8(u16 code, u64 input1)
@@ -162,45 +128,24 @@ static inline u64 hv_do_fast_hypercall8(u16 code, u64 input1)
162128
/* Fast hypercall with 16 bytes of input */
163129
static inline u64 _hv_do_fast_hypercall16(u64 control, u64 input1, u64 input2)
164130
{
165-
u64 hv_status;
166-
167131
#ifdef CONFIG_X86_64
168-
if (hv_isolation_type_tdx() && !hyperv_paravisor_present)
169-
return hv_tdx_hypercall(control, input1, input2);
170-
171-
if (hv_isolation_type_snp() && !hyperv_paravisor_present) {
172-
__asm__ __volatile__("mov %[input2], %%r8\n"
173-
"vmmcall"
174-
: "=a" (hv_status), ASM_CALL_CONSTRAINT,
175-
"+c" (control), "+d" (input1)
176-
: [input2] "r" (input2)
177-
: "cc", "r8", "r9", "r10", "r11");
178-
} else {
179-
__asm__ __volatile__("mov %[input2], %%r8\n"
180-
CALL_NOSPEC
181-
: "=a" (hv_status), ASM_CALL_CONSTRAINT,
182-
"+c" (control), "+d" (input1)
183-
: [input2] "r" (input2),
184-
THUNK_TARGET(hv_hypercall_pg)
185-
: "cc", "r8", "r9", "r10", "r11");
186-
}
132+
return static_call_mod(hv_hypercall)(control, input1, input2);
187133
#else
188-
{
189-
u32 input1_hi = upper_32_bits(input1);
190-
u32 input1_lo = lower_32_bits(input1);
191-
u32 input2_hi = upper_32_bits(input2);
192-
u32 input2_lo = lower_32_bits(input2);
193-
194-
__asm__ __volatile__ (CALL_NOSPEC
195-
: "=A"(hv_status),
196-
"+c"(input1_lo), ASM_CALL_CONSTRAINT
197-
: "A" (control), "b" (input1_hi),
198-
"D"(input2_hi), "S"(input2_lo),
199-
THUNK_TARGET(hv_hypercall_pg)
200-
: "cc");
201-
}
202-
#endif
134+
u32 input1_hi = upper_32_bits(input1);
135+
u32 input1_lo = lower_32_bits(input1);
136+
u32 input2_hi = upper_32_bits(input2);
137+
u32 input2_lo = lower_32_bits(input2);
138+
u64 hv_status;
139+
140+
__asm__ __volatile__ (CALL_NOSPEC
141+
: "=A"(hv_status),
142+
"+c"(input1_lo), ASM_CALL_CONSTRAINT
143+
: "A" (control), "b" (input1_hi),
144+
"D"(input2_hi), "S"(input2_lo),
145+
THUNK_TARGET(hv_hypercall_pg)
146+
: "cc");
203147
return hv_status;
148+
#endif
204149
}
205150

206151
static inline u64 hv_do_fast_hypercall16(u16 code, u64 input1, u64 input2)

arch/x86/kernel/cpu/mshyperv.c

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,6 @@
3838
bool hv_nested;
3939
struct ms_hyperv_info ms_hyperv;
4040

41-
/* Used in modules via hv_do_hypercall(): see arch/x86/include/asm/mshyperv.h */
42-
bool hyperv_paravisor_present __ro_after_init;
43-
EXPORT_SYMBOL_GPL(hyperv_paravisor_present);
44-
4541
#if IS_ENABLED(CONFIG_HYPERV)
4642
static inline unsigned int hv_get_nested_msr(unsigned int reg)
4743
{
@@ -288,8 +284,18 @@ static void __init x86_setup_ops_for_tsc_pg_clock(void)
288284
old_restore_sched_clock_state = x86_platform.restore_sched_clock_state;
289285
x86_platform.restore_sched_clock_state = hv_restore_sched_clock_state;
290286
}
287+
288+
#ifdef CONFIG_X86_64
289+
DEFINE_STATIC_CALL(hv_hypercall, hv_std_hypercall);
290+
EXPORT_STATIC_CALL_TRAMP_GPL(hv_hypercall);
291+
#define hypercall_update(hc) static_call_update(hv_hypercall, hc)
292+
#endif
291293
#endif /* CONFIG_HYPERV */
292294

295+
#ifndef hypercall_update
296+
#define hypercall_update(hc) (void)hc
297+
#endif
298+
293299
static uint32_t __init ms_hyperv_platform(void)
294300
{
295301
u32 eax;
@@ -484,21 +490,22 @@ static void __init ms_hyperv_init_platform(void)
484490
ms_hyperv.shared_gpa_boundary =
485491
BIT_ULL(ms_hyperv.shared_gpa_boundary_bits);
486492

487-
hyperv_paravisor_present = !!ms_hyperv.paravisor_present;
488-
489493
pr_info("Hyper-V: Isolation Config: Group A 0x%x, Group B 0x%x\n",
490494
ms_hyperv.isolation_config_a, ms_hyperv.isolation_config_b);
491495

492496

493497
if (hv_get_isolation_type() == HV_ISOLATION_TYPE_SNP) {
494498
static_branch_enable(&isolation_type_snp);
499+
if (!ms_hyperv.paravisor_present)
500+
hypercall_update(hv_snp_hypercall);
495501
} else if (hv_get_isolation_type() == HV_ISOLATION_TYPE_TDX) {
496502
static_branch_enable(&isolation_type_tdx);
497503

498504
/* A TDX VM must use x2APIC and doesn't use lazy EOI. */
499505
ms_hyperv.hints &= ~HV_X64_APIC_ACCESS_RECOMMENDED;
500506

501507
if (!ms_hyperv.paravisor_present) {
508+
hypercall_update(hv_tdx_hypercall);
502509
/*
503510
* Mark the Hyper-V TSC page feature as disabled
504511
* in a TDX VM without paravisor so that the

0 commit comments

Comments
 (0)