327 lines
9.4 KiB
C
327 lines
9.4 KiB
C
/* SPDX-License-Identifier: GPL-2.0 or MIT */
|
|
#ifndef _ASM_X86_VMWARE_H
|
|
#define _ASM_X86_VMWARE_H
|
|
|
|
#include <asm/cpufeatures.h>
|
|
#include <asm/alternative.h>
|
|
#include <linux/stringify.h>
|
|
|
|
/*
|
|
* 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
|