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