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

x86/vmware: Introduce VMware hypercall API

Introduce a vmware_hypercall family of functions. It is a common implementation
to be used by the VMware guest code and virtual device drivers in architecture
independent manner.

The API consists of vmware_hypercallX and vmware_hypercall_hb_{out,in}
set of functions analogous to KVM's hypercall API. Architecture-specific
implementation is hidden inside.

It will simplify future enhancements in VMware hypercalls such as SEV-ES and
TDX related changes without needs to modify a caller in device drivers code.

Current implementation extends an idea from

bac7b4e84323 ("x86/vmware: Update platform detection code for VMCALL/VMMCALL hypercalls")

to have a slow, but safe path vmware_hypercall_slow() earlier during the boot
when alternatives are not yet applied. The code inherits VMWARE_CMD logic from
the commit mentioned above.

Move common macros from vmware.c to vmware.h.

[ bp: Fold in a fix:
https://lore.kernel.org/r/20240625083348.2299-1-alexey.makhalov@broadcom.com ]

Signed-off-by: Alexey Makhalov <alexey.makhalov@broadcom.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Link: https://lore.kernel.org/r/20240613191650.9913-2-alexey.makhalov@broadcom.com

authored by

Alexey Makhalov and committed by
Borislav Petkov (AMD)
34bf25e8 f2661062

+327 -22
+265 -14
arch/x86/include/asm/vmware.h
··· 7 7 #include <linux/stringify.h> 8 8 9 9 /* 10 - * The hypercall definitions differ in the low word of the %edx argument 11 - * in the following way: the old port base interface uses the port 12 - * number to distinguish between high- and low bandwidth versions. 10 + * VMware hypercall ABI. 11 + * 12 + * - Low bandwidth (LB) hypercalls (I/O port based, vmcall and vmmcall) 13 + * have up to 6 input and 6 output arguments passed and returned using 14 + * registers: %eax (arg0), %ebx (arg1), %ecx (arg2), %edx (arg3), 15 + * %esi (arg4), %edi (arg5). 16 + * The following input arguments must be initialized by the caller: 17 + * arg0 - VMWARE_HYPERVISOR_MAGIC 18 + * arg2 - Hypercall command 19 + * arg3 bits [15:0] - Port number, LB and direction flags 20 + * 21 + * - High bandwidth (HB) hypercalls are I/O port based only. They have 22 + * up to 7 input and 7 output arguments passed and returned using 23 + * registers: %eax (arg0), %ebx (arg1), %ecx (arg2), %edx (arg3), 24 + * %esi (arg4), %edi (arg5), %ebp (arg6). 25 + * The following input arguments must be initialized by the caller: 26 + * arg0 - VMWARE_HYPERVISOR_MAGIC 27 + * arg1 - Hypercall command 28 + * arg3 bits [15:0] - Port number, HB and direction flags 29 + * 30 + * For compatibility purposes, x86_64 systems use only lower 32 bits 31 + * for input and output arguments. 32 + * 33 + * The hypercall definitions differ in the low word of the %edx (arg3) 34 + * in the following way: the old I/O port based interface uses the port 35 + * number to distinguish between high- and low bandwidth versions, and 36 + * uses IN/OUT instructions to define transfer direction. 13 37 * 14 38 * The new vmcall interface instead uses a set of flags to select 15 39 * bandwidth mode and transfer direction. The flags should be loaded 16 - * into %dx by any user and are automatically replaced by the port 17 - * number if the VMWARE_HYPERVISOR_PORT method is used. 18 - * 19 - * In short, new driver code should strictly use the new definition of 20 - * %dx content. 40 + * into arg3 by any user and are automatically replaced by the port 41 + * number if the I/O port method is used. 21 42 */ 22 43 23 - /* Old port-based version */ 24 - #define VMWARE_HYPERVISOR_PORT 0x5658 25 - #define VMWARE_HYPERVISOR_PORT_HB 0x5659 44 + #define VMWARE_HYPERVISOR_HB BIT(0) 45 + #define VMWARE_HYPERVISOR_OUT BIT(1) 26 46 27 - /* Current vmcall / vmmcall version */ 28 - #define VMWARE_HYPERVISOR_HB BIT(0) 29 - #define VMWARE_HYPERVISOR_OUT BIT(1) 47 + #define VMWARE_HYPERVISOR_PORT 0x5658 48 + #define VMWARE_HYPERVISOR_PORT_HB (VMWARE_HYPERVISOR_PORT | \ 49 + VMWARE_HYPERVISOR_HB) 50 + 51 + #define VMWARE_HYPERVISOR_MAGIC 0x564d5868U 52 + 53 + #define VMWARE_CMD_GETVERSION 10 54 + #define VMWARE_CMD_GETHZ 45 55 + #define VMWARE_CMD_GETVCPU_INFO 68 56 + #define VMWARE_CMD_STEALCLOCK 91 57 + 58 + #define CPUID_VMWARE_FEATURES_ECX_VMMCALL BIT(0) 59 + #define CPUID_VMWARE_FEATURES_ECX_VMCALL BIT(1) 60 + 61 + extern unsigned long vmware_hypercall_slow(unsigned long cmd, 62 + unsigned long in1, unsigned long in3, 63 + unsigned long in4, unsigned long in5, 64 + u32 *out1, u32 *out2, u32 *out3, 65 + u32 *out4, u32 *out5); 66 + 67 + /* 68 + * The low bandwidth call. The low word of %edx is presumed to have OUT bit 69 + * set. The high word of %edx may contain input data from the caller. 70 + */ 71 + #define VMWARE_HYPERCALL \ 72 + ALTERNATIVE_2("movw %[port], %%dx\n\t" \ 73 + "inl (%%dx), %%eax", \ 74 + "vmcall", X86_FEATURE_VMCALL, \ 75 + "vmmcall", X86_FEATURE_VMW_VMMCALL) 76 + 77 + static inline 78 + unsigned long vmware_hypercall1(unsigned long cmd, unsigned long in1) 79 + { 80 + unsigned long out0; 81 + 82 + if (unlikely(!alternatives_patched) && !__is_defined(MODULE)) 83 + return vmware_hypercall_slow(cmd, in1, 0, 0, 0, 84 + NULL, NULL, NULL, NULL, NULL); 85 + 86 + asm_inline volatile (VMWARE_HYPERCALL 87 + : "=a" (out0) 88 + : [port] "i" (VMWARE_HYPERVISOR_PORT), 89 + "a" (VMWARE_HYPERVISOR_MAGIC), 90 + "b" (in1), 91 + "c" (cmd), 92 + "d" (0) 93 + : "cc", "memory"); 94 + return out0; 95 + } 96 + 97 + static inline 98 + unsigned long vmware_hypercall3(unsigned long cmd, unsigned long in1, 99 + u32 *out1, u32 *out2) 100 + { 101 + unsigned long out0; 102 + 103 + if (unlikely(!alternatives_patched) && !__is_defined(MODULE)) 104 + return vmware_hypercall_slow(cmd, in1, 0, 0, 0, 105 + out1, out2, NULL, NULL, NULL); 106 + 107 + asm_inline volatile (VMWARE_HYPERCALL 108 + : "=a" (out0), "=b" (*out1), "=c" (*out2) 109 + : [port] "i" (VMWARE_HYPERVISOR_PORT), 110 + "a" (VMWARE_HYPERVISOR_MAGIC), 111 + "b" (in1), 112 + "c" (cmd), 113 + "d" (0) 114 + : "cc", "memory"); 115 + return out0; 116 + } 117 + 118 + static inline 119 + unsigned long vmware_hypercall4(unsigned long cmd, unsigned long in1, 120 + u32 *out1, u32 *out2, u32 *out3) 121 + { 122 + unsigned long out0; 123 + 124 + if (unlikely(!alternatives_patched) && !__is_defined(MODULE)) 125 + return vmware_hypercall_slow(cmd, in1, 0, 0, 0, 126 + out1, out2, out3, NULL, NULL); 127 + 128 + asm_inline volatile (VMWARE_HYPERCALL 129 + : "=a" (out0), "=b" (*out1), "=c" (*out2), "=d" (*out3) 130 + : [port] "i" (VMWARE_HYPERVISOR_PORT), 131 + "a" (VMWARE_HYPERVISOR_MAGIC), 132 + "b" (in1), 133 + "c" (cmd), 134 + "d" (0) 135 + : "cc", "memory"); 136 + return out0; 137 + } 138 + 139 + static inline 140 + unsigned long vmware_hypercall5(unsigned long cmd, unsigned long in1, 141 + unsigned long in3, unsigned long in4, 142 + unsigned long in5, u32 *out2) 143 + { 144 + unsigned long out0; 145 + 146 + if (unlikely(!alternatives_patched) && !__is_defined(MODULE)) 147 + return vmware_hypercall_slow(cmd, in1, in3, in4, in5, 148 + NULL, out2, NULL, NULL, NULL); 149 + 150 + asm_inline volatile (VMWARE_HYPERCALL 151 + : "=a" (out0), "=c" (*out2) 152 + : [port] "i" (VMWARE_HYPERVISOR_PORT), 153 + "a" (VMWARE_HYPERVISOR_MAGIC), 154 + "b" (in1), 155 + "c" (cmd), 156 + "d" (in3), 157 + "S" (in4), 158 + "D" (in5) 159 + : "cc", "memory"); 160 + return out0; 161 + } 162 + 163 + static inline 164 + unsigned long vmware_hypercall6(unsigned long cmd, unsigned long in1, 165 + unsigned long in3, u32 *out2, 166 + u32 *out3, u32 *out4, u32 *out5) 167 + { 168 + unsigned long out0; 169 + 170 + if (unlikely(!alternatives_patched) && !__is_defined(MODULE)) 171 + return vmware_hypercall_slow(cmd, in1, in3, 0, 0, 172 + NULL, out2, out3, out4, out5); 173 + 174 + asm_inline volatile (VMWARE_HYPERCALL 175 + : "=a" (out0), "=c" (*out2), "=d" (*out3), "=S" (*out4), 176 + "=D" (*out5) 177 + : [port] "i" (VMWARE_HYPERVISOR_PORT), 178 + "a" (VMWARE_HYPERVISOR_MAGIC), 179 + "b" (in1), 180 + "c" (cmd), 181 + "d" (in3) 182 + : "cc", "memory"); 183 + return out0; 184 + } 185 + 186 + static inline 187 + unsigned long vmware_hypercall7(unsigned long cmd, unsigned long in1, 188 + unsigned long in3, unsigned long in4, 189 + unsigned long in5, u32 *out1, 190 + u32 *out2, u32 *out3) 191 + { 192 + unsigned long out0; 193 + 194 + if (unlikely(!alternatives_patched) && !__is_defined(MODULE)) 195 + return vmware_hypercall_slow(cmd, in1, in3, in4, in5, 196 + out1, out2, out3, NULL, NULL); 197 + 198 + asm_inline volatile (VMWARE_HYPERCALL 199 + : "=a" (out0), "=b" (*out1), "=c" (*out2), "=d" (*out3) 200 + : [port] "i" (VMWARE_HYPERVISOR_PORT), 201 + "a" (VMWARE_HYPERVISOR_MAGIC), 202 + "b" (in1), 203 + "c" (cmd), 204 + "d" (in3), 205 + "S" (in4), 206 + "D" (in5) 207 + : "cc", "memory"); 208 + return out0; 209 + } 210 + 211 + #ifdef CONFIG_X86_64 212 + #define VMW_BP_CONSTRAINT "r" 213 + #else 214 + #define VMW_BP_CONSTRAINT "m" 215 + #endif 216 + 217 + /* 218 + * High bandwidth calls are not supported on encrypted memory guests. 219 + * The caller should check cc_platform_has(CC_ATTR_MEM_ENCRYPT) and use 220 + * low bandwidth hypercall if memory encryption is set. 221 + * This assumption simplifies HB hypercall implementation to just I/O port 222 + * based approach without alternative patching. 223 + */ 224 + static inline 225 + unsigned long vmware_hypercall_hb_out(unsigned long cmd, unsigned long in2, 226 + unsigned long in3, unsigned long in4, 227 + unsigned long in5, unsigned long in6, 228 + u32 *out1) 229 + { 230 + unsigned long out0; 231 + 232 + asm_inline volatile ( 233 + UNWIND_HINT_SAVE 234 + "push %%" _ASM_BP "\n\t" 235 + UNWIND_HINT_UNDEFINED 236 + "mov %[in6], %%" _ASM_BP "\n\t" 237 + "rep outsb\n\t" 238 + "pop %%" _ASM_BP "\n\t" 239 + UNWIND_HINT_RESTORE 240 + : "=a" (out0), "=b" (*out1) 241 + : "a" (VMWARE_HYPERVISOR_MAGIC), 242 + "b" (cmd), 243 + "c" (in2), 244 + "d" (in3 | VMWARE_HYPERVISOR_PORT_HB), 245 + "S" (in4), 246 + "D" (in5), 247 + [in6] VMW_BP_CONSTRAINT (in6) 248 + : "cc", "memory"); 249 + return out0; 250 + } 251 + 252 + static inline 253 + unsigned long vmware_hypercall_hb_in(unsigned long cmd, unsigned long in2, 254 + unsigned long in3, unsigned long in4, 255 + unsigned long in5, unsigned long in6, 256 + u32 *out1) 257 + { 258 + unsigned long out0; 259 + 260 + asm_inline volatile ( 261 + UNWIND_HINT_SAVE 262 + "push %%" _ASM_BP "\n\t" 263 + UNWIND_HINT_UNDEFINED 264 + "mov %[in6], %%" _ASM_BP "\n\t" 265 + "rep insb\n\t" 266 + "pop %%" _ASM_BP "\n\t" 267 + UNWIND_HINT_RESTORE 268 + : "=a" (out0), "=b" (*out1) 269 + : "a" (VMWARE_HYPERVISOR_MAGIC), 270 + "b" (cmd), 271 + "c" (in2), 272 + "d" (in3 | VMWARE_HYPERVISOR_PORT_HB), 273 + "S" (in4), 274 + "D" (in5), 275 + [in6] VMW_BP_CONSTRAINT (in6) 276 + : "cc", "memory"); 277 + return out0; 278 + } 279 + #undef VMW_BP_CONSTRAINT 280 + #undef VMWARE_HYPERCALL 30 281 31 282 /* The low bandwidth call. The low word of edx is presumed clear. */ 32 283 #define VMWARE_HYPERCALL \
+62 -8
arch/x86/kernel/cpu/vmware.c
··· 41 41 42 42 #define CPUID_VMWARE_INFO_LEAF 0x40000000 43 43 #define CPUID_VMWARE_FEATURES_LEAF 0x40000010 44 - #define CPUID_VMWARE_FEATURES_ECX_VMMCALL BIT(0) 45 - #define CPUID_VMWARE_FEATURES_ECX_VMCALL BIT(1) 46 44 47 - #define VMWARE_HYPERVISOR_MAGIC 0x564D5868 48 - 49 - #define VMWARE_CMD_GETVERSION 10 50 - #define VMWARE_CMD_GETHZ 45 51 - #define VMWARE_CMD_GETVCPU_INFO 68 52 45 #define VMWARE_CMD_LEGACY_X2APIC 3 53 46 #define VMWARE_CMD_VCPU_RESERVED 31 54 - #define VMWARE_CMD_STEALCLOCK 91 55 47 56 48 #define STEALCLOCK_NOT_AVAILABLE (-1) 57 49 #define STEALCLOCK_DISABLED 0 ··· 101 109 102 110 static unsigned long vmware_tsc_khz __ro_after_init; 103 111 static u8 vmware_hypercall_mode __ro_after_init; 112 + 113 + unsigned long vmware_hypercall_slow(unsigned long cmd, 114 + unsigned long in1, unsigned long in3, 115 + unsigned long in4, unsigned long in5, 116 + u32 *out1, u32 *out2, u32 *out3, 117 + u32 *out4, u32 *out5) 118 + { 119 + unsigned long out0, rbx, rcx, rdx, rsi, rdi; 120 + 121 + switch (vmware_hypercall_mode) { 122 + case CPUID_VMWARE_FEATURES_ECX_VMCALL: 123 + asm_inline volatile ("vmcall" 124 + : "=a" (out0), "=b" (rbx), "=c" (rcx), 125 + "=d" (rdx), "=S" (rsi), "=D" (rdi) 126 + : "a" (VMWARE_HYPERVISOR_MAGIC), 127 + "b" (in1), 128 + "c" (cmd), 129 + "d" (in3), 130 + "S" (in4), 131 + "D" (in5) 132 + : "cc", "memory"); 133 + break; 134 + case CPUID_VMWARE_FEATURES_ECX_VMMCALL: 135 + asm_inline volatile ("vmmcall" 136 + : "=a" (out0), "=b" (rbx), "=c" (rcx), 137 + "=d" (rdx), "=S" (rsi), "=D" (rdi) 138 + : "a" (VMWARE_HYPERVISOR_MAGIC), 139 + "b" (in1), 140 + "c" (cmd), 141 + "d" (in3), 142 + "S" (in4), 143 + "D" (in5) 144 + : "cc", "memory"); 145 + break; 146 + default: 147 + asm_inline volatile ("movw %[port], %%dx; inl (%%dx), %%eax" 148 + : "=a" (out0), "=b" (rbx), "=c" (rcx), 149 + "=d" (rdx), "=S" (rsi), "=D" (rdi) 150 + : [port] "i" (VMWARE_HYPERVISOR_PORT), 151 + "a" (VMWARE_HYPERVISOR_MAGIC), 152 + "b" (in1), 153 + "c" (cmd), 154 + "d" (in3), 155 + "S" (in4), 156 + "D" (in5) 157 + : "cc", "memory"); 158 + break; 159 + } 160 + 161 + if (out1) 162 + *out1 = rbx; 163 + if (out2) 164 + *out2 = rcx; 165 + if (out3) 166 + *out3 = rdx; 167 + if (out4) 168 + *out4 = rsi; 169 + if (out5) 170 + *out5 = rdi; 171 + 172 + return out0; 173 + } 104 174 105 175 static inline int __vmware_platform(void) 106 176 {