Skip to content

Commit 34bf25e

Browse files
YustasSwampbp3tk0v
authored andcommitted
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 bac7b4e ("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/[email protected] ] Signed-off-by: Alexey Makhalov <[email protected]> Signed-off-by: Borislav Petkov (AMD) <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent f266106 commit 34bf25e

File tree

2 files changed

+327
-22
lines changed

2 files changed

+327
-22
lines changed

arch/x86/include/asm/vmware.h

Lines changed: 265 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,277 @@
77
#include <linux/stringify.h>
88

99
/*
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.
1337
*
1438
* The new vmcall interface instead uses a set of flags to select
1539
* 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.
42+
*/
43+
44+
#define VMWARE_HYPERVISOR_HB BIT(0)
45+
#define VMWARE_HYPERVISOR_OUT BIT(1)
46+
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.
2170
*/
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+
}
22251

23-
/* Old port-based version */
24-
#define VMWARE_HYPERVISOR_PORT 0x5658
25-
#define VMWARE_HYPERVISOR_PORT_HB 0x5659
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;
26259

27-
/* Current vmcall / vmmcall version */
28-
#define VMWARE_HYPERVISOR_HB BIT(0)
29-
#define VMWARE_HYPERVISOR_OUT BIT(1)
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
30281

31282
/* The low bandwidth call. The low word of edx is presumed clear. */
32283
#define VMWARE_HYPERCALL \

arch/x86/kernel/cpu/vmware.c

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,9 @@
4141

4242
#define CPUID_VMWARE_INFO_LEAF 0x40000000
4343
#define CPUID_VMWARE_FEATURES_LEAF 0x40000010
44-
#define CPUID_VMWARE_FEATURES_ECX_VMMCALL BIT(0)
45-
#define CPUID_VMWARE_FEATURES_ECX_VMCALL BIT(1)
4644

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
5245
#define VMWARE_CMD_LEGACY_X2APIC 3
5346
#define VMWARE_CMD_VCPU_RESERVED 31
54-
#define VMWARE_CMD_STEALCLOCK 91
5547

5648
#define STEALCLOCK_NOT_AVAILABLE (-1)
5749
#define STEALCLOCK_DISABLED 0
@@ -110,6 +102,68 @@ struct vmware_steal_time {
110102
static unsigned long vmware_tsc_khz __ro_after_init;
111103
static u8 vmware_hypercall_mode __ro_after_init;
112104

105+
unsigned long vmware_hypercall_slow(unsigned long cmd,
106+
unsigned long in1, unsigned long in3,
107+
unsigned long in4, unsigned long in5,
108+
u32 *out1, u32 *out2, u32 *out3,
109+
u32 *out4, u32 *out5)
110+
{
111+
unsigned long out0, rbx, rcx, rdx, rsi, rdi;
112+
113+
switch (vmware_hypercall_mode) {
114+
case CPUID_VMWARE_FEATURES_ECX_VMCALL:
115+
asm_inline volatile ("vmcall"
116+
: "=a" (out0), "=b" (rbx), "=c" (rcx),
117+
"=d" (rdx), "=S" (rsi), "=D" (rdi)
118+
: "a" (VMWARE_HYPERVISOR_MAGIC),
119+
"b" (in1),
120+
"c" (cmd),
121+
"d" (in3),
122+
"S" (in4),
123+
"D" (in5)
124+
: "cc", "memory");
125+
break;
126+
case CPUID_VMWARE_FEATURES_ECX_VMMCALL:
127+
asm_inline volatile ("vmmcall"
128+
: "=a" (out0), "=b" (rbx), "=c" (rcx),
129+
"=d" (rdx), "=S" (rsi), "=D" (rdi)
130+
: "a" (VMWARE_HYPERVISOR_MAGIC),
131+
"b" (in1),
132+
"c" (cmd),
133+
"d" (in3),
134+
"S" (in4),
135+
"D" (in5)
136+
: "cc", "memory");
137+
break;
138+
default:
139+
asm_inline volatile ("movw %[port], %%dx; inl (%%dx), %%eax"
140+
: "=a" (out0), "=b" (rbx), "=c" (rcx),
141+
"=d" (rdx), "=S" (rsi), "=D" (rdi)
142+
: [port] "i" (VMWARE_HYPERVISOR_PORT),
143+
"a" (VMWARE_HYPERVISOR_MAGIC),
144+
"b" (in1),
145+
"c" (cmd),
146+
"d" (in3),
147+
"S" (in4),
148+
"D" (in5)
149+
: "cc", "memory");
150+
break;
151+
}
152+
153+
if (out1)
154+
*out1 = rbx;
155+
if (out2)
156+
*out2 = rcx;
157+
if (out3)
158+
*out3 = rdx;
159+
if (out4)
160+
*out4 = rsi;
161+
if (out5)
162+
*out5 = rdi;
163+
164+
return out0;
165+
}
166+
113167
static inline int __vmware_platform(void)
114168
{
115169
uint32_t eax, ebx, ecx, edx;

0 commit comments

Comments
 (0)