1 /* SPDX-License-Identifier: GPL-2.0 or MIT */
2 #ifndef _ASM_X86_VMWARE_H
3 #define _ASM_X86_VMWARE_H
4
5 #include <asm/cpufeatures.h>
6 #include <asm/alternative.h>
7 #include <linux/stringify.h>
8
9 /*
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 * - Low bandwidth TDX hypercalls (x86_64 only) are similar to LB
22 * hypercalls. They also have up to 6 input and 6 output on registers
23 * arguments, with different argument to register mapping:
24 * %r12 (arg0), %rbx (arg1), %r13 (arg2), %rdx (arg3),
25 * %rsi (arg4), %rdi (arg5).
26 *
27 * - High bandwidth (HB) hypercalls are I/O port based only. They have
28 * up to 7 input and 7 output arguments passed and returned using
29 * registers: %eax (arg0), %ebx (arg1), %ecx (arg2), %edx (arg3),
30 * %esi (arg4), %edi (arg5), %ebp (arg6).
31 * The following input arguments must be initialized by the caller:
32 * arg0 - VMWARE_HYPERVISOR_MAGIC
33 * arg1 - Hypercall command
34 * arg3 bits [15:0] - Port number, HB and direction flags
35 *
36 * For compatibility purposes, x86_64 systems use only lower 32 bits
37 * for input and output arguments.
38 *
39 * The hypercall definitions differ in the low word of the %edx (arg3)
40 * in the following way: the old I/O port based interface uses the port
41 * number to distinguish between high- and low bandwidth versions, and
42 * uses IN/OUT instructions to define transfer direction.
43 *
44 * The new vmcall interface instead uses a set of flags to select
45 * bandwidth mode and transfer direction. The flags should be loaded
46 * into arg3 by any user and are automatically replaced by the port
47 * number if the I/O port method is used.
48 */
49
50 #define VMWARE_HYPERVISOR_HB BIT(0)
51 #define VMWARE_HYPERVISOR_OUT BIT(1)
52
53 #define VMWARE_HYPERVISOR_PORT 0x5658
54 #define VMWARE_HYPERVISOR_PORT_HB (VMWARE_HYPERVISOR_PORT | \
55 VMWARE_HYPERVISOR_HB)
56
57 #define VMWARE_HYPERVISOR_MAGIC 0x564d5868U
58
59 #define VMWARE_CMD_GETVERSION 10
60 #define VMWARE_CMD_GETHZ 45
61 #define VMWARE_CMD_GETVCPU_INFO 68
62 #define VMWARE_CMD_STEALCLOCK 91
63 /*
64 * Hypercall command mask:
65 * bits [6:0] command, range [0, 127]
66 * bits [19:16] sub-command, range [0, 15]
67 */
68 #define VMWARE_CMD_MASK 0xf007fU
69
70 #define CPUID_VMWARE_FEATURES_ECX_VMMCALL BIT(0)
71 #define CPUID_VMWARE_FEATURES_ECX_VMCALL BIT(1)
72
73 extern unsigned long vmware_hypercall_slow(unsigned long cmd,
74 unsigned long in1, unsigned long in3,
75 unsigned long in4, unsigned long in5,
76 u32 *out1, u32 *out2, u32 *out3,
77 u32 *out4, u32 *out5);
78
79 #define VMWARE_TDX_VENDOR_LEAF 0x1af7e4909ULL
80 #define VMWARE_TDX_HCALL_FUNC 1
81
82 extern unsigned long vmware_tdx_hypercall(unsigned long cmd,
83 unsigned long in1, unsigned long in3,
84 unsigned long in4, unsigned long in5,
85 u32 *out1, u32 *out2, u32 *out3,
86 u32 *out4, u32 *out5);
87
88 /*
89 * The low bandwidth call. The low word of %edx is presumed to have OUT bit
90 * set. The high word of %edx may contain input data from the caller.
91 */
92 #define VMWARE_HYPERCALL \
93 ALTERNATIVE_2("movw %[port], %%dx\n\t" \
94 "inl (%%dx), %%eax", \
95 "vmcall", X86_FEATURE_VMCALL, \
96 "vmmcall", X86_FEATURE_VMW_VMMCALL)
97
98 static inline
vmware_hypercall1(unsigned long cmd,unsigned long in1)99 unsigned long vmware_hypercall1(unsigned long cmd, unsigned long in1)
100 {
101 unsigned long out0;
102
103 if (cpu_feature_enabled(X86_FEATURE_TDX_GUEST))
104 return vmware_tdx_hypercall(cmd, in1, 0, 0, 0,
105 NULL, NULL, NULL, NULL, NULL);
106
107 if (unlikely(!alternatives_patched) && !__is_defined(MODULE))
108 return vmware_hypercall_slow(cmd, in1, 0, 0, 0,
109 NULL, NULL, NULL, NULL, NULL);
110
111 asm_inline volatile (VMWARE_HYPERCALL
112 : "=a" (out0)
113 : [port] "i" (VMWARE_HYPERVISOR_PORT),
114 "a" (VMWARE_HYPERVISOR_MAGIC),
115 "b" (in1),
116 "c" (cmd),
117 "d" (0)
118 : "cc", "memory");
119 return out0;
120 }
121
122 static inline
vmware_hypercall3(unsigned long cmd,unsigned long in1,u32 * out1,u32 * out2)123 unsigned long vmware_hypercall3(unsigned long cmd, unsigned long in1,
124 u32 *out1, u32 *out2)
125 {
126 unsigned long out0;
127
128 if (cpu_feature_enabled(X86_FEATURE_TDX_GUEST))
129 return vmware_tdx_hypercall(cmd, in1, 0, 0, 0,
130 out1, out2, NULL, NULL, NULL);
131
132 if (unlikely(!alternatives_patched) && !__is_defined(MODULE))
133 return vmware_hypercall_slow(cmd, in1, 0, 0, 0,
134 out1, out2, NULL, NULL, NULL);
135
136 asm_inline volatile (VMWARE_HYPERCALL
137 : "=a" (out0), "=b" (*out1), "=c" (*out2)
138 : [port] "i" (VMWARE_HYPERVISOR_PORT),
139 "a" (VMWARE_HYPERVISOR_MAGIC),
140 "b" (in1),
141 "c" (cmd),
142 "d" (0)
143 : "cc", "memory");
144 return out0;
145 }
146
147 static inline
vmware_hypercall4(unsigned long cmd,unsigned long in1,u32 * out1,u32 * out2,u32 * out3)148 unsigned long vmware_hypercall4(unsigned long cmd, unsigned long in1,
149 u32 *out1, u32 *out2, u32 *out3)
150 {
151 unsigned long out0;
152
153 if (cpu_feature_enabled(X86_FEATURE_TDX_GUEST))
154 return vmware_tdx_hypercall(cmd, in1, 0, 0, 0,
155 out1, out2, out3, NULL, NULL);
156
157 if (unlikely(!alternatives_patched) && !__is_defined(MODULE))
158 return vmware_hypercall_slow(cmd, in1, 0, 0, 0,
159 out1, out2, out3, NULL, NULL);
160
161 asm_inline volatile (VMWARE_HYPERCALL
162 : "=a" (out0), "=b" (*out1), "=c" (*out2), "=d" (*out3)
163 : [port] "i" (VMWARE_HYPERVISOR_PORT),
164 "a" (VMWARE_HYPERVISOR_MAGIC),
165 "b" (in1),
166 "c" (cmd),
167 "d" (0)
168 : "cc", "memory");
169 return out0;
170 }
171
172 static inline
vmware_hypercall5(unsigned long cmd,unsigned long in1,unsigned long in3,unsigned long in4,unsigned long in5,u32 * out2)173 unsigned long vmware_hypercall5(unsigned long cmd, unsigned long in1,
174 unsigned long in3, unsigned long in4,
175 unsigned long in5, u32 *out2)
176 {
177 unsigned long out0;
178
179 if (cpu_feature_enabled(X86_FEATURE_TDX_GUEST))
180 return vmware_tdx_hypercall(cmd, in1, in3, in4, in5,
181 NULL, out2, NULL, NULL, NULL);
182
183 if (unlikely(!alternatives_patched) && !__is_defined(MODULE))
184 return vmware_hypercall_slow(cmd, in1, in3, in4, in5,
185 NULL, out2, NULL, NULL, NULL);
186
187 asm_inline volatile (VMWARE_HYPERCALL
188 : "=a" (out0), "=c" (*out2)
189 : [port] "i" (VMWARE_HYPERVISOR_PORT),
190 "a" (VMWARE_HYPERVISOR_MAGIC),
191 "b" (in1),
192 "c" (cmd),
193 "d" (in3),
194 "S" (in4),
195 "D" (in5)
196 : "cc", "memory");
197 return out0;
198 }
199
200 static inline
vmware_hypercall6(unsigned long cmd,unsigned long in1,unsigned long in3,u32 * out2,u32 * out3,u32 * out4,u32 * out5)201 unsigned long vmware_hypercall6(unsigned long cmd, unsigned long in1,
202 unsigned long in3, u32 *out2,
203 u32 *out3, u32 *out4, u32 *out5)
204 {
205 unsigned long out0;
206
207 if (cpu_feature_enabled(X86_FEATURE_TDX_GUEST))
208 return vmware_tdx_hypercall(cmd, in1, in3, 0, 0,
209 NULL, out2, out3, out4, out5);
210
211 if (unlikely(!alternatives_patched) && !__is_defined(MODULE))
212 return vmware_hypercall_slow(cmd, in1, in3, 0, 0,
213 NULL, out2, out3, out4, out5);
214
215 asm_inline volatile (VMWARE_HYPERCALL
216 : "=a" (out0), "=c" (*out2), "=d" (*out3), "=S" (*out4),
217 "=D" (*out5)
218 : [port] "i" (VMWARE_HYPERVISOR_PORT),
219 "a" (VMWARE_HYPERVISOR_MAGIC),
220 "b" (in1),
221 "c" (cmd),
222 "d" (in3)
223 : "cc", "memory");
224 return out0;
225 }
226
227 static inline
vmware_hypercall7(unsigned long cmd,unsigned long in1,unsigned long in3,unsigned long in4,unsigned long in5,u32 * out1,u32 * out2,u32 * out3)228 unsigned long vmware_hypercall7(unsigned long cmd, unsigned long in1,
229 unsigned long in3, unsigned long in4,
230 unsigned long in5, u32 *out1,
231 u32 *out2, u32 *out3)
232 {
233 unsigned long out0;
234
235 if (cpu_feature_enabled(X86_FEATURE_TDX_GUEST))
236 return vmware_tdx_hypercall(cmd, in1, in3, in4, in5,
237 out1, out2, out3, NULL, NULL);
238
239 if (unlikely(!alternatives_patched) && !__is_defined(MODULE))
240 return vmware_hypercall_slow(cmd, in1, in3, in4, in5,
241 out1, out2, out3, NULL, NULL);
242
243 asm_inline volatile (VMWARE_HYPERCALL
244 : "=a" (out0), "=b" (*out1), "=c" (*out2), "=d" (*out3)
245 : [port] "i" (VMWARE_HYPERVISOR_PORT),
246 "a" (VMWARE_HYPERVISOR_MAGIC),
247 "b" (in1),
248 "c" (cmd),
249 "d" (in3),
250 "S" (in4),
251 "D" (in5)
252 : "cc", "memory");
253 return out0;
254 }
255
256 #ifdef CONFIG_X86_64
257 #define VMW_BP_CONSTRAINT "r"
258 #else
259 #define VMW_BP_CONSTRAINT "m"
260 #endif
261
262 /*
263 * High bandwidth calls are not supported on encrypted memory guests.
264 * The caller should check cc_platform_has(CC_ATTR_MEM_ENCRYPT) and use
265 * low bandwidth hypercall if memory encryption is set.
266 * This assumption simplifies HB hypercall implementation to just I/O port
267 * based approach without alternative patching.
268 */
269 static inline
vmware_hypercall_hb_out(unsigned long cmd,unsigned long in2,unsigned long in3,unsigned long in4,unsigned long in5,unsigned long in6,u32 * out1)270 unsigned long vmware_hypercall_hb_out(unsigned long cmd, unsigned long in2,
271 unsigned long in3, unsigned long in4,
272 unsigned long in5, unsigned long in6,
273 u32 *out1)
274 {
275 unsigned long out0;
276
277 asm_inline volatile (
278 UNWIND_HINT_SAVE
279 "push %%" _ASM_BP "\n\t"
280 UNWIND_HINT_UNDEFINED
281 "mov %[in6], %%" _ASM_BP "\n\t"
282 "rep outsb\n\t"
283 "pop %%" _ASM_BP "\n\t"
284 UNWIND_HINT_RESTORE
285 : "=a" (out0), "=b" (*out1)
286 : "a" (VMWARE_HYPERVISOR_MAGIC),
287 "b" (cmd),
288 "c" (in2),
289 "d" (in3 | VMWARE_HYPERVISOR_PORT_HB),
290 "S" (in4),
291 "D" (in5),
292 [in6] VMW_BP_CONSTRAINT (in6)
293 : "cc", "memory");
294 return out0;
295 }
296
297 static inline
vmware_hypercall_hb_in(unsigned long cmd,unsigned long in2,unsigned long in3,unsigned long in4,unsigned long in5,unsigned long in6,u32 * out1)298 unsigned long vmware_hypercall_hb_in(unsigned long cmd, unsigned long in2,
299 unsigned long in3, unsigned long in4,
300 unsigned long in5, unsigned long in6,
301 u32 *out1)
302 {
303 unsigned long out0;
304
305 asm_inline volatile (
306 UNWIND_HINT_SAVE
307 "push %%" _ASM_BP "\n\t"
308 UNWIND_HINT_UNDEFINED
309 "mov %[in6], %%" _ASM_BP "\n\t"
310 "rep insb\n\t"
311 "pop %%" _ASM_BP "\n\t"
312 UNWIND_HINT_RESTORE
313 : "=a" (out0), "=b" (*out1)
314 : "a" (VMWARE_HYPERVISOR_MAGIC),
315 "b" (cmd),
316 "c" (in2),
317 "d" (in3 | VMWARE_HYPERVISOR_PORT_HB),
318 "S" (in4),
319 "D" (in5),
320 [in6] VMW_BP_CONSTRAINT (in6)
321 : "cc", "memory");
322 return out0;
323 }
324 #undef VMW_BP_CONSTRAINT
325 #undef VMWARE_HYPERCALL
326
327 #endif
328