Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+89 -102
+20
arch/x86/hyperv/hv_init.c
··· 37 37 #include <linux/export.h> 38 38 39 39 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 40 59 EXPORT_SYMBOL_GPL(hv_hypercall_pg); 60 + #endif 41 61 42 62 union hv_ghcb * __percpu *hv_ghcb_pg; 43 63
+15
arch/x86/hyperv/ivm.c
··· 385 385 return ret; 386 386 } 387 387 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 + 388 401 #else 389 402 static inline void hv_ghcb_msr_write(u64 msr, u64 value) {} 390 403 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; } 391 405 #endif /* CONFIG_AMD_MEM_ENCRYPT */ 392 406 393 407 #ifdef CONFIG_INTEL_TDX_GUEST ··· 451 437 #else 452 438 static inline void hv_tdx_msr_write(u64 msr, u64 value) {} 453 439 static inline void hv_tdx_msr_read(u64 msr, u64 *value) {} 440 + u64 hv_tdx_hypercall(u64 control, u64 param1, u64 param2) { return U64_MAX; } 454 441 #endif /* CONFIG_INTEL_TDX_GUEST */ 455 442 456 443 #if defined(CONFIG_AMD_MEM_ENCRYPT) || defined(CONFIG_INTEL_TDX_GUEST)
+41 -96
arch/x86/include/asm/mshyperv.h
··· 6 6 #include <linux/nmi.h> 7 7 #include <linux/msi.h> 8 8 #include <linux/io.h> 9 + #include <linux/static_call.h> 9 10 #include <asm/nospec-branch.h> 10 11 #include <asm/paravirt.h> 11 12 #include <asm/msr.h> ··· 40 39 return 0; 41 40 } 42 41 43 - #if IS_ENABLED(CONFIG_HYPERV) 44 - extern bool hyperv_paravisor_present; 42 + extern u64 hv_tdx_hypercall(u64 control, u64 param1, u64 param2); 43 + extern u64 hv_snp_hypercall(u64 control, u64 param1, u64 param2); 44 + extern u64 hv_std_hypercall(u64 control, u64 param1, u64 param2); 45 45 46 + #if IS_ENABLED(CONFIG_HYPERV) 46 47 extern void *hv_hypercall_pg; 47 48 48 49 extern union hv_ghcb * __percpu *hv_ghcb_pg; 49 50 50 51 bool hv_isolation_type_snp(void); 51 52 bool hv_isolation_type_tdx(void); 52 - u64 hv_tdx_hypercall(u64 control, u64 param1, u64 param2); 53 + 54 + #ifdef CONFIG_X86_64 55 + DECLARE_STATIC_CALL(hv_hypercall, hv_std_hypercall); 56 + #endif 53 57 54 58 /* 55 59 * DEFAULT INIT GPAT and SEGMENT LIMIT value in struct VMSA ··· 71 65 { 72 66 u64 input_address = input ? virt_to_phys(input) : 0; 73 67 u64 output_address = output ? virt_to_phys(output) : 0; 74 - u64 hv_status; 75 68 76 69 #ifdef CONFIG_X86_64 77 - if (hv_isolation_type_tdx() && !hyperv_paravisor_present) 78 - return hv_tdx_hypercall(control, input_address, output_address); 79 - 80 - if (hv_isolation_type_snp() && !hyperv_paravisor_present) { 81 - __asm__ __volatile__("mov %[output_address], %%r8\n" 82 - "vmmcall" 83 - : "=a" (hv_status), ASM_CALL_CONSTRAINT, 84 - "+c" (control), "+d" (input_address) 85 - : [output_address] "r" (output_address) 86 - : "cc", "memory", "r8", "r9", "r10", "r11"); 87 - return hv_status; 88 - } 89 - 90 - if (!hv_hypercall_pg) 91 - return U64_MAX; 92 - 93 - __asm__ __volatile__("mov %[output_address], %%r8\n" 94 - CALL_NOSPEC 95 - : "=a" (hv_status), ASM_CALL_CONSTRAINT, 96 - "+c" (control), "+d" (input_address) 97 - : [output_address] "r" (output_address), 98 - THUNK_TARGET(hv_hypercall_pg) 99 - : "cc", "memory", "r8", "r9", "r10", "r11"); 70 + return static_call_mod(hv_hypercall)(control, input_address, output_address); 100 71 #else 101 72 u32 input_address_hi = upper_32_bits(input_address); 102 73 u32 input_address_lo = lower_32_bits(input_address); 103 74 u32 output_address_hi = upper_32_bits(output_address); 104 75 u32 output_address_lo = lower_32_bits(output_address); 76 + u64 hv_status; 105 77 106 78 if (!hv_hypercall_pg) 107 79 return U64_MAX; ··· 92 108 "D"(output_address_hi), "S"(output_address_lo), 93 109 THUNK_TARGET(hv_hypercall_pg) 94 110 : "cc", "memory"); 95 - #endif /* !x86_64 */ 96 111 return hv_status; 112 + #endif /* !x86_64 */ 97 113 } 98 114 99 115 /* Fast hypercall with 8 bytes of input and no output */ 100 116 static inline u64 _hv_do_fast_hypercall8(u64 control, u64 input1) 101 117 { 118 + #ifdef CONFIG_X86_64 119 + return static_call_mod(hv_hypercall)(control, input1, 0); 120 + #else 121 + u32 input1_hi = upper_32_bits(input1); 122 + u32 input1_lo = lower_32_bits(input1); 102 123 u64 hv_status; 103 124 104 - #ifdef CONFIG_X86_64 105 - if (hv_isolation_type_tdx() && !hyperv_paravisor_present) 106 - return hv_tdx_hypercall(control, input1, 0); 107 - 108 - if (hv_isolation_type_snp() && !hyperv_paravisor_present) { 109 - __asm__ __volatile__( 110 - "vmmcall" 111 - : "=a" (hv_status), ASM_CALL_CONSTRAINT, 112 - "+c" (control), "+d" (input1) 113 - :: "cc", "r8", "r9", "r10", "r11"); 114 - } else { 115 - __asm__ __volatile__(CALL_NOSPEC 116 - : "=a" (hv_status), ASM_CALL_CONSTRAINT, 117 - "+c" (control), "+d" (input1) 118 - : THUNK_TARGET(hv_hypercall_pg) 119 - : "cc", "r8", "r9", "r10", "r11"); 120 - } 121 - #else 122 - { 123 - u32 input1_hi = upper_32_bits(input1); 124 - u32 input1_lo = lower_32_bits(input1); 125 - 126 - __asm__ __volatile__ (CALL_NOSPEC 127 - : "=A"(hv_status), 128 - "+c"(input1_lo), 129 - ASM_CALL_CONSTRAINT 130 - : "A" (control), 131 - "b" (input1_hi), 132 - THUNK_TARGET(hv_hypercall_pg) 133 - : "cc", "edi", "esi"); 134 - } 135 - #endif 125 + __asm__ __volatile__ (CALL_NOSPEC 126 + : "=A"(hv_status), 127 + "+c"(input1_lo), 128 + ASM_CALL_CONSTRAINT 129 + : "A" (control), 130 + "b" (input1_hi), 131 + THUNK_TARGET(hv_hypercall_pg) 132 + : "cc", "edi", "esi"); 136 133 return hv_status; 134 + #endif 137 135 } 138 136 139 137 static inline u64 hv_do_fast_hypercall8(u16 code, u64 input1) ··· 128 162 /* Fast hypercall with 16 bytes of input */ 129 163 static inline u64 _hv_do_fast_hypercall16(u64 control, u64 input1, u64 input2) 130 164 { 165 + #ifdef CONFIG_X86_64 166 + return static_call_mod(hv_hypercall)(control, input1, input2); 167 + #else 168 + u32 input1_hi = upper_32_bits(input1); 169 + u32 input1_lo = lower_32_bits(input1); 170 + u32 input2_hi = upper_32_bits(input2); 171 + u32 input2_lo = lower_32_bits(input2); 131 172 u64 hv_status; 132 173 133 - #ifdef CONFIG_X86_64 134 - if (hv_isolation_type_tdx() && !hyperv_paravisor_present) 135 - return hv_tdx_hypercall(control, input1, input2); 136 - 137 - if (hv_isolation_type_snp() && !hyperv_paravisor_present) { 138 - __asm__ __volatile__("mov %[input2], %%r8\n" 139 - "vmmcall" 140 - : "=a" (hv_status), ASM_CALL_CONSTRAINT, 141 - "+c" (control), "+d" (input1) 142 - : [input2] "r" (input2) 143 - : "cc", "r8", "r9", "r10", "r11"); 144 - } else { 145 - __asm__ __volatile__("mov %[input2], %%r8\n" 146 - CALL_NOSPEC 147 - : "=a" (hv_status), ASM_CALL_CONSTRAINT, 148 - "+c" (control), "+d" (input1) 149 - : [input2] "r" (input2), 150 - THUNK_TARGET(hv_hypercall_pg) 151 - : "cc", "r8", "r9", "r10", "r11"); 152 - } 153 - #else 154 - { 155 - u32 input1_hi = upper_32_bits(input1); 156 - u32 input1_lo = lower_32_bits(input1); 157 - u32 input2_hi = upper_32_bits(input2); 158 - u32 input2_lo = lower_32_bits(input2); 159 - 160 - __asm__ __volatile__ (CALL_NOSPEC 161 - : "=A"(hv_status), 162 - "+c"(input1_lo), ASM_CALL_CONSTRAINT 163 - : "A" (control), "b" (input1_hi), 164 - "D"(input2_hi), "S"(input2_lo), 165 - THUNK_TARGET(hv_hypercall_pg) 166 - : "cc"); 167 - } 168 - #endif 174 + __asm__ __volatile__ (CALL_NOSPEC 175 + : "=A"(hv_status), 176 + "+c"(input1_lo), ASM_CALL_CONSTRAINT 177 + : "A" (control), "b" (input1_hi), 178 + "D"(input2_hi), "S"(input2_lo), 179 + THUNK_TARGET(hv_hypercall_pg) 180 + : "cc"); 169 181 return hv_status; 182 + #endif 170 183 } 171 184 172 185 static inline u64 hv_do_fast_hypercall16(u16 code, u64 input1, u64 input2)
+13 -6
arch/x86/kernel/cpu/mshyperv.c
··· 38 38 bool hv_nested; 39 39 struct ms_hyperv_info ms_hyperv; 40 40 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 - 45 41 #if IS_ENABLED(CONFIG_HYPERV) 46 42 static inline unsigned int hv_get_nested_msr(unsigned int reg) 47 43 { ··· 284 288 old_restore_sched_clock_state = x86_platform.restore_sched_clock_state; 285 289 x86_platform.restore_sched_clock_state = hv_restore_sched_clock_state; 286 290 } 291 + 292 + #ifdef CONFIG_X86_64 293 + DEFINE_STATIC_CALL(hv_hypercall, hv_std_hypercall); 294 + EXPORT_STATIC_CALL_TRAMP_GPL(hv_hypercall); 295 + #define hypercall_update(hc) static_call_update(hv_hypercall, hc) 296 + #endif 287 297 #endif /* CONFIG_HYPERV */ 298 + 299 + #ifndef hypercall_update 300 + #define hypercall_update(hc) (void)hc 301 + #endif 288 302 289 303 static uint32_t __init ms_hyperv_platform(void) 290 304 { ··· 490 484 ms_hyperv.shared_gpa_boundary = 491 485 BIT_ULL(ms_hyperv.shared_gpa_boundary_bits); 492 486 493 - hyperv_paravisor_present = !!ms_hyperv.paravisor_present; 494 - 495 487 pr_info("Hyper-V: Isolation Config: Group A 0x%x, Group B 0x%x\n", 496 488 ms_hyperv.isolation_config_a, ms_hyperv.isolation_config_b); 497 489 498 490 499 491 if (hv_get_isolation_type() == HV_ISOLATION_TYPE_SNP) { 500 492 static_branch_enable(&isolation_type_snp); 493 + if (!ms_hyperv.paravisor_present) 494 + hypercall_update(hv_snp_hypercall); 501 495 } else if (hv_get_isolation_type() == HV_ISOLATION_TYPE_TDX) { 502 496 static_branch_enable(&isolation_type_tdx); 503 497 ··· 505 499 ms_hyperv.hints &= ~HV_X64_APIC_ACCESS_RECOMMENDED; 506 500 507 501 if (!ms_hyperv.paravisor_present) { 502 + hypercall_update(hv_tdx_hypercall); 508 503 /* 509 504 * Mark the Hyper-V TSC page feature as disabled 510 505 * in a TDX VM without paravisor so that the