/* SPDX-License-Identifier: GPL-2.0 or MIT */ #ifndef _ASM_X86_VMWARE_H #define _ASM_X86_VMWARE_H #include #include #include /* * VMware hypercall ABI. * * - Low bandwidth (LB) hypercalls (I/O port based, vmcall and vmmcall) * have up to 6 input and 6 output arguments passed and returned using * registers: %eax (arg0), %ebx (arg1), %ecx (arg2), %edx (arg3), * %esi (arg4), %edi (arg5). * The following input arguments must be initialized by the caller: * arg0 - VMWARE_HYPERVISOR_MAGIC * arg2 - Hypercall command * arg3 bits [15:0] - Port number, LB and direction flags * * - Low bandwidth TDX hypercalls (x86_64 only) are similar to LB * hypercalls. They also have up to 6 input and 6 output on registers * arguments, with different argument to register mapping: * %r12 (arg0), %rbx (arg1), %r13 (arg2), %rdx (arg3), * %rsi (arg4), %rdi (arg5). * * - High bandwidth (HB) hypercalls are I/O port based only. They have * up to 7 input and 7 output arguments passed and returned using * registers: %eax (arg0), %ebx (arg1), %ecx (arg2), %edx (arg3), * %esi (arg4), %edi (arg5), %ebp (arg6). * The following input arguments must be initialized by the caller: * arg0 - VMWARE_HYPERVISOR_MAGIC * arg1 - Hypercall command * arg3 bits [15:0] - Port number, HB and direction flags * * For compatibility purposes, x86_64 systems use only lower 32 bits * for input and output arguments. * * The hypercall definitions differ in the low word of the %edx (arg3) * in the following way: the old I/O port based interface uses the port * number to distinguish between high- and low bandwidth versions, and * uses IN/OUT instructions to define transfer direction. * * The new vmcall interface instead uses a set of flags to select * bandwidth mode and transfer direction. The flags should be loaded * into arg3 by any user and are automatically replaced by the port * number if the I/O port method is used. */ #define VMWARE_HYPERVISOR_HB BIT(0) #define VMWARE_HYPERVISOR_OUT BIT(1) #define VMWARE_HYPERVISOR_PORT 0x5658 #define VMWARE_HYPERVISOR_PORT_HB (VMWARE_HYPERVISOR_PORT | \ VMWARE_HYPERVISOR_HB) #define VMWARE_HYPERVISOR_MAGIC 0x564d5868U #define VMWARE_CMD_GETVERSION 10 #define VMWARE_CMD_GETHZ 45 #define VMWARE_CMD_GETVCPU_INFO 68 #define VMWARE_CMD_STEALCLOCK 91 /* * Hypercall command mask: * bits [6:0] command, range [0, 127] * bits [19:16] sub-command, range [0, 15] */ #define VMWARE_CMD_MASK 0xf007fU #define CPUID_VMWARE_FEATURES_ECX_VMMCALL BIT(0) #define CPUID_VMWARE_FEATURES_ECX_VMCALL BIT(1) extern unsigned long vmware_hypercall_slow(unsigned long cmd, unsigned long in1, unsigned long in3, unsigned long in4, unsigned long in5, u32 *out1, u32 *out2, u32 *out3, u32 *out4, u32 *out5); #define VMWARE_TDX_VENDOR_LEAF 0x1af7e4909ULL #define VMWARE_TDX_HCALL_FUNC 1 extern unsigned long vmware_tdx_hypercall(unsigned long cmd, unsigned long in1, unsigned long in3, unsigned long in4, unsigned long in5, u32 *out1, u32 *out2, u32 *out3, u32 *out4, u32 *out5); /* * The low bandwidth call. The low word of %edx is presumed to have OUT bit * set. The high word of %edx may contain input data from the caller. */ #define VMWARE_HYPERCALL \ ALTERNATIVE_2("movw %[port], %%dx\n\t" \ "inl (%%dx), %%eax", \ "vmcall", X86_FEATURE_VMCALL, \ "vmmcall", X86_FEATURE_VMW_VMMCALL) static inline unsigned long vmware_hypercall1(unsigned long cmd, unsigned long in1) { unsigned long out0; if (cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) return vmware_tdx_hypercall(cmd, in1, 0, 0, 0, NULL, NULL, NULL, NULL, NULL); if (unlikely(!alternatives_patched) && !__is_defined(MODULE)) return vmware_hypercall_slow(cmd, in1, 0, 0, 0, NULL, NULL, NULL, NULL, NULL); asm_inline volatile (VMWARE_HYPERCALL : "=a" (out0) : [port] "i" (VMWARE_HYPERVISOR_PORT), "a" (VMWARE_HYPERVISOR_MAGIC), "b" (in1), "c" (cmd), "d" (0) : "cc", "memory"); return out0; } static inline unsigned long vmware_hypercall3(unsigned long cmd, unsigned long in1, u32 *out1, u32 *out2) { unsigned long out0; if (cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) return vmware_tdx_hypercall(cmd, in1, 0, 0, 0, out1, out2, NULL, NULL, NULL); if (unlikely(!alternatives_patched) && !__is_defined(MODULE)) return vmware_hypercall_slow(cmd, in1, 0, 0, 0, out1, out2, NULL, NULL, NULL); asm_inline volatile (VMWARE_HYPERCALL : "=a" (out0), "=b" (*out1), "=c" (*out2) : [port] "i" (VMWARE_HYPERVISOR_PORT), "a" (VMWARE_HYPERVISOR_MAGIC), "b" (in1), "c" (cmd), "d" (0) : "cc", "memory"); return out0; } static inline unsigned long vmware_hypercall4(unsigned long cmd, unsigned long in1, u32 *out1, u32 *out2, u32 *out3) { unsigned long out0; if (cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) return vmware_tdx_hypercall(cmd, in1, 0, 0, 0, out1, out2, out3, NULL, NULL); if (unlikely(!alternatives_patched) && !__is_defined(MODULE)) return vmware_hypercall_slow(cmd, in1, 0, 0, 0, out1, out2, out3, NULL, NULL); asm_inline volatile (VMWARE_HYPERCALL : "=a" (out0), "=b" (*out1), "=c" (*out2), "=d" (*out3) : [port] "i" (VMWARE_HYPERVISOR_PORT), "a" (VMWARE_HYPERVISOR_MAGIC), "b" (in1), "c" (cmd), "d" (0) : "cc", "memory"); return out0; } static inline unsigned long vmware_hypercall5(unsigned long cmd, unsigned long in1, unsigned long in3, unsigned long in4, unsigned long in5, u32 *out2) { unsigned long out0; if (cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) return vmware_tdx_hypercall(cmd, in1, in3, in4, in5, NULL, out2, NULL, NULL, NULL); if (unlikely(!alternatives_patched) && !__is_defined(MODULE)) return vmware_hypercall_slow(cmd, in1, in3, in4, in5, NULL, out2, NULL, NULL, NULL); asm_inline volatile (VMWARE_HYPERCALL : "=a" (out0), "=c" (*out2) : [port] "i" (VMWARE_HYPERVISOR_PORT), "a" (VMWARE_HYPERVISOR_MAGIC), "b" (in1), "c" (cmd), "d" (in3), "S" (in4), "D" (in5) : "cc", "memory"); return out0; } static inline unsigned long vmware_hypercall6(unsigned long cmd, unsigned long in1, unsigned long in3, u32 *out2, u32 *out3, u32 *out4, u32 *out5) { unsigned long out0; if (cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) return vmware_tdx_hypercall(cmd, in1, in3, 0, 0, NULL, out2, out3, out4, out5); if (unlikely(!alternatives_patched) && !__is_defined(MODULE)) return vmware_hypercall_slow(cmd, in1, in3, 0, 0, NULL, out2, out3, out4, out5); asm_inline volatile (VMWARE_HYPERCALL : "=a" (out0), "=c" (*out2), "=d" (*out3), "=S" (*out4), "=D" (*out5) : [port] "i" (VMWARE_HYPERVISOR_PORT), "a" (VMWARE_HYPERVISOR_MAGIC), "b" (in1), "c" (cmd), "d" (in3) : "cc", "memory"); return out0; } static inline unsigned long vmware_hypercall7(unsigned long cmd, unsigned long in1, unsigned long in3, unsigned long in4, unsigned long in5, u32 *out1, u32 *out2, u32 *out3) { unsigned long out0; if (cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) return vmware_tdx_hypercall(cmd, in1, in3, in4, in5, out1, out2, out3, NULL, NULL); if (unlikely(!alternatives_patched) && !__is_defined(MODULE)) return vmware_hypercall_slow(cmd, in1, in3, in4, in5, out1, out2, out3, NULL, NULL); asm_inline volatile (VMWARE_HYPERCALL : "=a" (out0), "=b" (*out1), "=c" (*out2), "=d" (*out3) : [port] "i" (VMWARE_HYPERVISOR_PORT), "a" (VMWARE_HYPERVISOR_MAGIC), "b" (in1), "c" (cmd), "d" (in3), "S" (in4), "D" (in5) : "cc", "memory"); return out0; } #ifdef CONFIG_X86_64 #define VMW_BP_CONSTRAINT "r" #else #define VMW_BP_CONSTRAINT "m" #endif /* * High bandwidth calls are not supported on encrypted memory guests. * The caller should check cc_platform_has(CC_ATTR_MEM_ENCRYPT) and use * low bandwidth hypercall if memory encryption is set. * This assumption simplifies HB hypercall implementation to just I/O port * based approach without alternative patching. */ static inline unsigned long vmware_hypercall_hb_out(unsigned long cmd, unsigned long in2, unsigned long in3, unsigned long in4, unsigned long in5, unsigned long in6, u32 *out1) { unsigned long out0; asm_inline volatile ( UNWIND_HINT_SAVE "push %%" _ASM_BP "\n\t" UNWIND_HINT_UNDEFINED "mov %[in6], %%" _ASM_BP "\n\t" "rep outsb\n\t" "pop %%" _ASM_BP "\n\t" UNWIND_HINT_RESTORE : "=a" (out0), "=b" (*out1) : "a" (VMWARE_HYPERVISOR_MAGIC), "b" (cmd), "c" (in2), "d" (in3 | VMWARE_HYPERVISOR_PORT_HB), "S" (in4), "D" (in5), [in6] VMW_BP_CONSTRAINT (in6) : "cc", "memory"); return out0; } static inline unsigned long vmware_hypercall_hb_in(unsigned long cmd, unsigned long in2, unsigned long in3, unsigned long in4, unsigned long in5, unsigned long in6, u32 *out1) { unsigned long out0; asm_inline volatile ( UNWIND_HINT_SAVE "push %%" _ASM_BP "\n\t" UNWIND_HINT_UNDEFINED "mov %[in6], %%" _ASM_BP "\n\t" "rep insb\n\t" "pop %%" _ASM_BP "\n\t" UNWIND_HINT_RESTORE : "=a" (out0), "=b" (*out1) : "a" (VMWARE_HYPERVISOR_MAGIC), "b" (cmd), "c" (in2), "d" (in3 | VMWARE_HYPERVISOR_PORT_HB), "S" (in4), "D" (in5), [in6] VMW_BP_CONSTRAINT (in6) : "cc", "memory"); return out0; } #undef VMW_BP_CONSTRAINT #undef VMWARE_HYPERCALL #endif