Commit f998678b authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'x86_vmware_for_v6.11_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull x86 vmware updates from Borislav Petkov:

 - Add a unified VMware hypercall API layer which should be used by all
   callers instead of them doing homegrown solutions. This will provide
   for adding API support for confidential computing solutions like TDX

* tag 'x86_vmware_for_v6.11_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86/vmware: Add TDX hypercall support
  x86/vmware: Remove legacy VMWARE_HYPERCALL* macros
  x86/vmware: Correct macro names
  x86/vmware: Use VMware hypercall API
  drm/vmwgfx: Use VMware hypercall API
  input/vmmouse: Use VMware hypercall API
  ptp/vmware: Use VMware hypercall API
  x86/vmware: Introduce VMware hypercall API
parents 222dfb83 57b7b6ac
...@@ -7,51 +7,321 @@ ...@@ -7,51 +7,321 @@
#include <linux/stringify.h> #include <linux/stringify.h>
/* /*
* The hypercall definitions differ in the low word of the %edx argument * VMware hypercall ABI.
* in the following way: the old port base interface uses the port *
* number to distinguish between high- and low bandwidth versions. * - 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 * The new vmcall interface instead uses a set of flags to select
* bandwidth mode and transfer direction. The flags should be loaded * bandwidth mode and transfer direction. The flags should be loaded
* into %dx by any user and are automatically replaced by the port * into arg3 by any user and are automatically replaced by the port
* number if the VMWARE_HYPERVISOR_PORT method is used. * number if the I/O port method is used.
*
* In short, new driver code should strictly use the new definition of
* %dx content.
*/ */
/* Old port-based version */
#define VMWARE_HYPERVISOR_PORT 0x5658
#define VMWARE_HYPERVISOR_PORT_HB 0x5659
/* Current vmcall / vmmcall version */
#define VMWARE_HYPERVISOR_HB BIT(0) #define VMWARE_HYPERVISOR_HB BIT(0)
#define VMWARE_HYPERVISOR_OUT BIT(1) #define VMWARE_HYPERVISOR_OUT BIT(1)
/* The low bandwidth call. The low word of edx is presumed clear. */ #define VMWARE_HYPERVISOR_PORT 0x5658
#define VMWARE_HYPERCALL \ #define VMWARE_HYPERVISOR_PORT_HB (VMWARE_HYPERVISOR_PORT | \
ALTERNATIVE_2("movw $" __stringify(VMWARE_HYPERVISOR_PORT) ", %%dx; " \ VMWARE_HYPERVISOR_HB)
"inl (%%dx), %%eax", \
"vmcall", X86_FEATURE_VMCALL, \ #define VMWARE_HYPERVISOR_MAGIC 0x564d5868U
"vmmcall", X86_FEATURE_VMW_VMMCALL)
#define VMWARE_CMD_GETVERSION 10
#define VMWARE_CMD_GETHZ 45
#define VMWARE_CMD_GETVCPU_INFO 68
#define VMWARE_CMD_STEALCLOCK 91
/* /*
* The high bandwidth out call. The low word of edx is presumed to have the * Hypercall command mask:
* HB and OUT bits set. * bits [6:0] command, range [0, 127]
* bits [19:16] sub-command, range [0, 15]
*/ */
#define VMWARE_HYPERCALL_HB_OUT \ #define VMWARE_CMD_MASK 0xf007fU
ALTERNATIVE_2("movw $" __stringify(VMWARE_HYPERVISOR_PORT_HB) ", %%dx; " \
"rep outsb", \ #define CPUID_VMWARE_FEATURES_ECX_VMMCALL BIT(0)
"vmcall", X86_FEATURE_VMCALL, \ #define CPUID_VMWARE_FEATURES_ECX_VMCALL BIT(1)
"vmmcall", X86_FEATURE_VMW_VMMCALL)
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 high bandwidth in call. The low word of edx is presumed to have the * The low bandwidth call. The low word of %edx is presumed to have OUT bit
* HB bit set. * set. The high word of %edx may contain input data from the caller.
*/ */
#define VMWARE_HYPERCALL_HB_IN \ #define VMWARE_HYPERCALL \
ALTERNATIVE_2("movw $" __stringify(VMWARE_HYPERVISOR_PORT_HB) ", %%dx; " \ ALTERNATIVE_2("movw %[port], %%dx\n\t" \
"rep insb", \ "inl (%%dx), %%eax", \
"vmcall", X86_FEATURE_VMCALL, \ "vmcall", X86_FEATURE_VMCALL, \
"vmmcall", X86_FEATURE_VMW_VMMCALL) "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 #endif
...@@ -41,80 +41,97 @@ ...@@ -41,80 +41,97 @@
#define CPUID_VMWARE_INFO_LEAF 0x40000000 #define CPUID_VMWARE_INFO_LEAF 0x40000000
#define CPUID_VMWARE_FEATURES_LEAF 0x40000010 #define CPUID_VMWARE_FEATURES_LEAF 0x40000010
#define CPUID_VMWARE_FEATURES_ECX_VMMCALL BIT(0)
#define CPUID_VMWARE_FEATURES_ECX_VMCALL BIT(1)
#define VMWARE_HYPERVISOR_MAGIC 0x564D5868 #define GETVCPU_INFO_LEGACY_X2APIC BIT(3)
#define GETVCPU_INFO_VCPU_RESERVED BIT(31)
#define VMWARE_CMD_GETVERSION 10
#define VMWARE_CMD_GETHZ 45
#define VMWARE_CMD_GETVCPU_INFO 68
#define VMWARE_CMD_LEGACY_X2APIC 3
#define VMWARE_CMD_VCPU_RESERVED 31
#define VMWARE_CMD_STEALCLOCK 91
#define STEALCLOCK_NOT_AVAILABLE (-1) #define STEALCLOCK_NOT_AVAILABLE (-1)
#define STEALCLOCK_DISABLED 0 #define STEALCLOCK_DISABLED 0
#define STEALCLOCK_ENABLED 1 #define STEALCLOCK_ENABLED 1
#define VMWARE_PORT(cmd, eax, ebx, ecx, edx) \
__asm__("inl (%%dx), %%eax" : \
"=a"(eax), "=c"(ecx), "=d"(edx), "=b"(ebx) : \
"a"(VMWARE_HYPERVISOR_MAGIC), \
"c"(VMWARE_CMD_##cmd), \
"d"(VMWARE_HYPERVISOR_PORT), "b"(UINT_MAX) : \
"memory")
#define VMWARE_VMCALL(cmd, eax, ebx, ecx, edx) \
__asm__("vmcall" : \
"=a"(eax), "=c"(ecx), "=d"(edx), "=b"(ebx) : \
"a"(VMWARE_HYPERVISOR_MAGIC), \
"c"(VMWARE_CMD_##cmd), \
"d"(0), "b"(UINT_MAX) : \
"memory")
#define VMWARE_VMMCALL(cmd, eax, ebx, ecx, edx) \
__asm__("vmmcall" : \
"=a"(eax), "=c"(ecx), "=d"(edx), "=b"(ebx) : \
"a"(VMWARE_HYPERVISOR_MAGIC), \
"c"(VMWARE_CMD_##cmd), \
"d"(0), "b"(UINT_MAX) : \
"memory")
#define VMWARE_CMD(cmd, eax, ebx, ecx, edx) do { \
switch (vmware_hypercall_mode) { \
case CPUID_VMWARE_FEATURES_ECX_VMCALL: \
VMWARE_VMCALL(cmd, eax, ebx, ecx, edx); \
break; \
case CPUID_VMWARE_FEATURES_ECX_VMMCALL: \
VMWARE_VMMCALL(cmd, eax, ebx, ecx, edx); \
break; \
default: \
VMWARE_PORT(cmd, eax, ebx, ecx, edx); \
break; \
} \
} while (0)
struct vmware_steal_time { struct vmware_steal_time {
union { union {
uint64_t clock; /* stolen time counter in units of vtsc */ u64 clock; /* stolen time counter in units of vtsc */
struct { struct {
/* only for little-endian */ /* only for little-endian */
uint32_t clock_low; u32 clock_low;
uint32_t clock_high; u32 clock_high;
}; };
}; };
uint64_t reserved[7]; u64 reserved[7];
}; };
static unsigned long vmware_tsc_khz __ro_after_init; static unsigned long vmware_tsc_khz __ro_after_init;
static u8 vmware_hypercall_mode __ro_after_init; static u8 vmware_hypercall_mode __ro_after_init;
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)
{
unsigned long out0, rbx, rcx, rdx, rsi, rdi;
switch (vmware_hypercall_mode) {
case CPUID_VMWARE_FEATURES_ECX_VMCALL:
asm_inline volatile ("vmcall"
: "=a" (out0), "=b" (rbx), "=c" (rcx),
"=d" (rdx), "=S" (rsi), "=D" (rdi)
: "a" (VMWARE_HYPERVISOR_MAGIC),
"b" (in1),
"c" (cmd),
"d" (in3),
"S" (in4),
"D" (in5)
: "cc", "memory");
break;
case CPUID_VMWARE_FEATURES_ECX_VMMCALL:
asm_inline volatile ("vmmcall"
: "=a" (out0), "=b" (rbx), "=c" (rcx),
"=d" (rdx), "=S" (rsi), "=D" (rdi)
: "a" (VMWARE_HYPERVISOR_MAGIC),
"b" (in1),
"c" (cmd),
"d" (in3),
"S" (in4),
"D" (in5)
: "cc", "memory");
break;
default:
asm_inline volatile ("movw %[port], %%dx; inl (%%dx), %%eax"
: "=a" (out0), "=b" (rbx), "=c" (rcx),
"=d" (rdx), "=S" (rsi), "=D" (rdi)
: [port] "i" (VMWARE_HYPERVISOR_PORT),
"a" (VMWARE_HYPERVISOR_MAGIC),
"b" (in1),
"c" (cmd),
"d" (in3),
"S" (in4),
"D" (in5)
: "cc", "memory");
break;
}
if (out1)
*out1 = rbx;
if (out2)
*out2 = rcx;
if (out3)
*out3 = rdx;
if (out4)
*out4 = rsi;
if (out5)
*out5 = rdi;
return out0;
}
static inline int __vmware_platform(void) static inline int __vmware_platform(void)
{ {
uint32_t eax, ebx, ecx, edx; u32 eax, ebx, ecx;
VMWARE_CMD(GETVERSION, eax, ebx, ecx, edx);
return eax != (uint32_t)-1 && ebx == VMWARE_HYPERVISOR_MAGIC; eax = vmware_hypercall3(VMWARE_CMD_GETVERSION, 0, &ebx, &ecx);
return eax != UINT_MAX && ebx == VMWARE_HYPERVISOR_MAGIC;
} }
static unsigned long vmware_get_tsc_khz(void) static unsigned long vmware_get_tsc_khz(void)
...@@ -166,21 +183,12 @@ static void __init vmware_cyc2ns_setup(void) ...@@ -166,21 +183,12 @@ static void __init vmware_cyc2ns_setup(void)
pr_info("using clock offset of %llu ns\n", d->cyc2ns_offset); pr_info("using clock offset of %llu ns\n", d->cyc2ns_offset);
} }
static int vmware_cmd_stealclock(uint32_t arg1, uint32_t arg2) static int vmware_cmd_stealclock(u32 addr_hi, u32 addr_lo)
{ {
uint32_t result, info; u32 info;
asm volatile (VMWARE_HYPERCALL : return vmware_hypercall5(VMWARE_CMD_STEALCLOCK, 0, 0, addr_hi, addr_lo,
"=a"(result), &info);
"=c"(info) :
"a"(VMWARE_HYPERVISOR_MAGIC),
"b"(0),
"c"(VMWARE_CMD_STEALCLOCK),
"d"(0),
"S"(arg1),
"D"(arg2) :
"memory");
return result;
} }
static bool stealclock_enable(phys_addr_t pa) static bool stealclock_enable(phys_addr_t pa)
...@@ -215,15 +223,15 @@ static bool vmware_is_stealclock_available(void) ...@@ -215,15 +223,15 @@ static bool vmware_is_stealclock_available(void)
* Return: * Return:
* The steal clock reading in ns. * The steal clock reading in ns.
*/ */
static uint64_t vmware_steal_clock(int cpu) static u64 vmware_steal_clock(int cpu)
{ {
struct vmware_steal_time *steal = &per_cpu(vmw_steal_time, cpu); struct vmware_steal_time *steal = &per_cpu(vmw_steal_time, cpu);
uint64_t clock; u64 clock;
if (IS_ENABLED(CONFIG_64BIT)) if (IS_ENABLED(CONFIG_64BIT))
clock = READ_ONCE(steal->clock); clock = READ_ONCE(steal->clock);
else { else {
uint32_t initial_high, low, high; u32 initial_high, low, high;
do { do {
initial_high = READ_ONCE(steal->clock_high); initial_high = READ_ONCE(steal->clock_high);
...@@ -235,7 +243,7 @@ static uint64_t vmware_steal_clock(int cpu) ...@@ -235,7 +243,7 @@ static uint64_t vmware_steal_clock(int cpu)
high = READ_ONCE(steal->clock_high); high = READ_ONCE(steal->clock_high);
} while (initial_high != high); } while (initial_high != high);
clock = ((uint64_t)high << 32) | low; clock = ((u64)high << 32) | low;
} }
return mul_u64_u32_shr(clock, vmware_cyc2ns.cyc2ns_mul, return mul_u64_u32_shr(clock, vmware_cyc2ns.cyc2ns_mul,
...@@ -389,13 +397,13 @@ static void __init vmware_set_capabilities(void) ...@@ -389,13 +397,13 @@ static void __init vmware_set_capabilities(void)
static void __init vmware_platform_setup(void) static void __init vmware_platform_setup(void)
{ {
uint32_t eax, ebx, ecx, edx; u32 eax, ebx, ecx;
uint64_t lpj, tsc_khz; u64 lpj, tsc_khz;
VMWARE_CMD(GETHZ, eax, ebx, ecx, edx); eax = vmware_hypercall3(VMWARE_CMD_GETHZ, UINT_MAX, &ebx, &ecx);
if (ebx != UINT_MAX) { if (ebx != UINT_MAX) {
lpj = tsc_khz = eax | (((uint64_t)ebx) << 32); lpj = tsc_khz = eax | (((u64)ebx) << 32);
do_div(tsc_khz, 1000); do_div(tsc_khz, 1000);
WARN_ON(tsc_khz >> 32); WARN_ON(tsc_khz >> 32);
pr_info("TSC freq read from hypervisor : %lu.%03lu MHz\n", pr_info("TSC freq read from hypervisor : %lu.%03lu MHz\n",
...@@ -446,7 +454,7 @@ static u8 __init vmware_select_hypercall(void) ...@@ -446,7 +454,7 @@ static u8 __init vmware_select_hypercall(void)
* If !boot_cpu_has(X86_FEATURE_HYPERVISOR), vmware_hypercall_mode * If !boot_cpu_has(X86_FEATURE_HYPERVISOR), vmware_hypercall_mode
* intentionally defaults to 0. * intentionally defaults to 0.
*/ */
static uint32_t __init vmware_platform(void) static u32 __init vmware_platform(void)
{ {
if (boot_cpu_has(X86_FEATURE_HYPERVISOR)) { if (boot_cpu_has(X86_FEATURE_HYPERVISOR)) {
unsigned int eax; unsigned int eax;
...@@ -474,12 +482,65 @@ static uint32_t __init vmware_platform(void) ...@@ -474,12 +482,65 @@ static uint32_t __init vmware_platform(void)
/* Checks if hypervisor supports x2apic without VT-D interrupt remapping. */ /* Checks if hypervisor supports x2apic without VT-D interrupt remapping. */
static bool __init vmware_legacy_x2apic_available(void) static bool __init vmware_legacy_x2apic_available(void)
{ {
uint32_t eax, ebx, ecx, edx; u32 eax;
VMWARE_CMD(GETVCPU_INFO, eax, ebx, ecx, edx);
return !(eax & BIT(VMWARE_CMD_VCPU_RESERVED)) && eax = vmware_hypercall1(VMWARE_CMD_GETVCPU_INFO, 0);
(eax & BIT(VMWARE_CMD_LEGACY_X2APIC)); return !(eax & GETVCPU_INFO_VCPU_RESERVED) &&
(eax & GETVCPU_INFO_LEGACY_X2APIC);
} }
#ifdef CONFIG_INTEL_TDX_GUEST
/*
* TDCALL[TDG.VP.VMCALL] uses %rax (arg0) and %rcx (arg2). Therefore,
* we remap those registers to %r12 and %r13, respectively.
*/
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)
{
struct tdx_module_args args = {};
if (!hypervisor_is_type(X86_HYPER_VMWARE)) {
pr_warn_once("Incorrect usage\n");
return ULONG_MAX;
}
if (cmd & ~VMWARE_CMD_MASK) {
pr_warn_once("Out of range command %lx\n", cmd);
return ULONG_MAX;
}
args.rbx = in1;
args.rdx = in3;
args.rsi = in4;
args.rdi = in5;
args.r10 = VMWARE_TDX_VENDOR_LEAF;
args.r11 = VMWARE_TDX_HCALL_FUNC;
args.r12 = VMWARE_HYPERVISOR_MAGIC;
args.r13 = cmd;
/* CPL */
args.r15 = 0;
__tdx_hypercall(&args);
if (out1)
*out1 = args.rbx;
if (out2)
*out2 = args.r13;
if (out3)
*out3 = args.rdx;
if (out4)
*out4 = args.rsi;
if (out5)
*out5 = args.rdi;
return args.r12;
}
EXPORT_SYMBOL_GPL(vmware_tdx_hypercall);
#endif
#ifdef CONFIG_AMD_MEM_ENCRYPT #ifdef CONFIG_AMD_MEM_ENCRYPT
static void vmware_sev_es_hcall_prepare(struct ghcb *ghcb, static void vmware_sev_es_hcall_prepare(struct ghcb *ghcb,
struct pt_regs *regs) struct pt_regs *regs)
......
...@@ -48,8 +48,6 @@ ...@@ -48,8 +48,6 @@
#define RETRIES 3 #define RETRIES 3
#define VMW_HYPERVISOR_MAGIC 0x564D5868
#define VMW_PORT_CMD_MSG 30 #define VMW_PORT_CMD_MSG 30
#define VMW_PORT_CMD_HB_MSG 0 #define VMW_PORT_CMD_HB_MSG 0
#define VMW_PORT_CMD_OPEN_CHANNEL (MSG_TYPE_OPEN << 16 | VMW_PORT_CMD_MSG) #define VMW_PORT_CMD_OPEN_CHANNEL (MSG_TYPE_OPEN << 16 | VMW_PORT_CMD_MSG)
...@@ -104,20 +102,18 @@ static const char* const mksstat_kern_name_desc[MKSSTAT_KERN_COUNT][2] = ...@@ -104,20 +102,18 @@ static const char* const mksstat_kern_name_desc[MKSSTAT_KERN_COUNT][2] =
*/ */
static int vmw_open_channel(struct rpc_channel *channel, unsigned int protocol) static int vmw_open_channel(struct rpc_channel *channel, unsigned int protocol)
{ {
unsigned long eax, ebx, ecx, edx, si = 0, di = 0; u32 ecx, edx, esi, edi;
VMW_PORT(VMW_PORT_CMD_OPEN_CHANNEL, vmware_hypercall6(VMW_PORT_CMD_OPEN_CHANNEL,
(protocol | GUESTMSG_FLAG_COOKIE), si, di, (protocol | GUESTMSG_FLAG_COOKIE), 0,
0, &ecx, &edx, &esi, &edi);
VMW_HYPERVISOR_MAGIC,
eax, ebx, ecx, edx, si, di);
if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0)
return -EINVAL; return -EINVAL;
channel->channel_id = HIGH_WORD(edx); channel->channel_id = HIGH_WORD(edx);
channel->cookie_high = si; channel->cookie_high = esi;
channel->cookie_low = di; channel->cookie_low = edi;
return 0; return 0;
} }
...@@ -133,17 +129,13 @@ static int vmw_open_channel(struct rpc_channel *channel, unsigned int protocol) ...@@ -133,17 +129,13 @@ static int vmw_open_channel(struct rpc_channel *channel, unsigned int protocol)
*/ */
static int vmw_close_channel(struct rpc_channel *channel) static int vmw_close_channel(struct rpc_channel *channel)
{ {
unsigned long eax, ebx, ecx, edx, si, di; u32 ecx;
/* Set up additional parameters */
si = channel->cookie_high;
di = channel->cookie_low;
VMW_PORT(VMW_PORT_CMD_CLOSE_CHANNEL, vmware_hypercall5(VMW_PORT_CMD_CLOSE_CHANNEL,
0, si, di, 0, channel->channel_id << 16,
channel->channel_id << 16, channel->cookie_high,
VMW_HYPERVISOR_MAGIC, channel->cookie_low,
eax, ebx, ecx, edx, si, di); &ecx);
if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0)
return -EINVAL; return -EINVAL;
...@@ -163,24 +155,18 @@ static int vmw_close_channel(struct rpc_channel *channel) ...@@ -163,24 +155,18 @@ static int vmw_close_channel(struct rpc_channel *channel)
static unsigned long vmw_port_hb_out(struct rpc_channel *channel, static unsigned long vmw_port_hb_out(struct rpc_channel *channel,
const char *msg, bool hb) const char *msg, bool hb)
{ {
unsigned long si, di, eax, ebx, ecx, edx; u32 ebx, ecx;
unsigned long msg_len = strlen(msg); unsigned long msg_len = strlen(msg);
/* HB port can't access encrypted memory. */ /* HB port can't access encrypted memory. */
if (hb && !cc_platform_has(CC_ATTR_MEM_ENCRYPT)) { if (hb && !cc_platform_has(CC_ATTR_MEM_ENCRYPT)) {
unsigned long bp = channel->cookie_high; vmware_hypercall_hb_out(
u32 channel_id = (channel->channel_id << 16);
si = (uintptr_t) msg;
di = channel->cookie_low;
VMW_PORT_HB_OUT(
(MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG, (MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG,
msg_len, si, di, msg_len,
VMWARE_HYPERVISOR_HB | channel_id | channel->channel_id << 16,
VMWARE_HYPERVISOR_OUT, (uintptr_t) msg, channel->cookie_low,
VMW_HYPERVISOR_MAGIC, bp, channel->cookie_high,
eax, ebx, ecx, edx, si, di); &ebx);
return ebx; return ebx;
} }
...@@ -194,14 +180,13 @@ static unsigned long vmw_port_hb_out(struct rpc_channel *channel, ...@@ -194,14 +180,13 @@ static unsigned long vmw_port_hb_out(struct rpc_channel *channel,
memcpy(&word, msg, bytes); memcpy(&word, msg, bytes);
msg_len -= bytes; msg_len -= bytes;
msg += bytes; msg += bytes;
si = channel->cookie_high;
di = channel->cookie_low;
VMW_PORT(VMW_PORT_CMD_MSG | (MSG_TYPE_SENDPAYLOAD << 16), vmware_hypercall5(VMW_PORT_CMD_MSG |
word, si, di, (MSG_TYPE_SENDPAYLOAD << 16),
channel->channel_id << 16, word, channel->channel_id << 16,
VMW_HYPERVISOR_MAGIC, channel->cookie_high,
eax, ebx, ecx, edx, si, di); channel->cookie_low,
&ecx);
} }
return ecx; return ecx;
...@@ -220,22 +205,17 @@ static unsigned long vmw_port_hb_out(struct rpc_channel *channel, ...@@ -220,22 +205,17 @@ static unsigned long vmw_port_hb_out(struct rpc_channel *channel,
static unsigned long vmw_port_hb_in(struct rpc_channel *channel, char *reply, static unsigned long vmw_port_hb_in(struct rpc_channel *channel, char *reply,
unsigned long reply_len, bool hb) unsigned long reply_len, bool hb)
{ {
unsigned long si, di, eax, ebx, ecx, edx; u32 ebx, ecx, edx;
/* HB port can't access encrypted memory */ /* HB port can't access encrypted memory */
if (hb && !cc_platform_has(CC_ATTR_MEM_ENCRYPT)) { if (hb && !cc_platform_has(CC_ATTR_MEM_ENCRYPT)) {
unsigned long bp = channel->cookie_low; vmware_hypercall_hb_in(
u32 channel_id = (channel->channel_id << 16);
si = channel->cookie_high;
di = (uintptr_t) reply;
VMW_PORT_HB_IN(
(MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG, (MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG,
reply_len, si, di, reply_len,
VMWARE_HYPERVISOR_HB | channel_id, channel->channel_id << 16,
VMW_HYPERVISOR_MAGIC, bp, channel->cookie_high,
eax, ebx, ecx, edx, si, di); (uintptr_t) reply, channel->cookie_low,
&ebx);
return ebx; return ebx;
} }
...@@ -245,14 +225,13 @@ static unsigned long vmw_port_hb_in(struct rpc_channel *channel, char *reply, ...@@ -245,14 +225,13 @@ static unsigned long vmw_port_hb_in(struct rpc_channel *channel, char *reply,
while (reply_len) { while (reply_len) {
unsigned int bytes = min_t(unsigned long, reply_len, 4); unsigned int bytes = min_t(unsigned long, reply_len, 4);
si = channel->cookie_high; vmware_hypercall7(VMW_PORT_CMD_MSG |
di = channel->cookie_low; (MSG_TYPE_RECVPAYLOAD << 16),
MESSAGE_STATUS_SUCCESS,
VMW_PORT(VMW_PORT_CMD_MSG | (MSG_TYPE_RECVPAYLOAD << 16),
MESSAGE_STATUS_SUCCESS, si, di,
channel->channel_id << 16, channel->channel_id << 16,
VMW_HYPERVISOR_MAGIC, channel->cookie_high,
eax, ebx, ecx, edx, si, di); channel->cookie_low,
&ebx, &ecx, &edx);
if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0)
break; break;
...@@ -276,22 +255,18 @@ static unsigned long vmw_port_hb_in(struct rpc_channel *channel, char *reply, ...@@ -276,22 +255,18 @@ static unsigned long vmw_port_hb_in(struct rpc_channel *channel, char *reply,
*/ */
static int vmw_send_msg(struct rpc_channel *channel, const char *msg) static int vmw_send_msg(struct rpc_channel *channel, const char *msg)
{ {
unsigned long eax, ebx, ecx, edx, si, di; u32 ebx, ecx;
size_t msg_len = strlen(msg); size_t msg_len = strlen(msg);
int retries = 0; int retries = 0;
while (retries < RETRIES) { while (retries < RETRIES) {
retries++; retries++;
/* Set up additional parameters */ vmware_hypercall5(VMW_PORT_CMD_SENDSIZE,
si = channel->cookie_high; msg_len, channel->channel_id << 16,
di = channel->cookie_low; channel->cookie_high,
channel->cookie_low,
VMW_PORT(VMW_PORT_CMD_SENDSIZE, &ecx);
msg_len, si, di,
channel->channel_id << 16,
VMW_HYPERVISOR_MAGIC,
eax, ebx, ecx, edx, si, di);
if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) { if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) {
/* Expected success. Give up. */ /* Expected success. Give up. */
...@@ -329,7 +304,7 @@ STACK_FRAME_NON_STANDARD(vmw_send_msg); ...@@ -329,7 +304,7 @@ STACK_FRAME_NON_STANDARD(vmw_send_msg);
static int vmw_recv_msg(struct rpc_channel *channel, void **msg, static int vmw_recv_msg(struct rpc_channel *channel, void **msg,
size_t *msg_len) size_t *msg_len)
{ {
unsigned long eax, ebx, ecx, edx, si, di; u32 ebx, ecx, edx;
char *reply; char *reply;
size_t reply_len; size_t reply_len;
int retries = 0; int retries = 0;
...@@ -341,15 +316,11 @@ static int vmw_recv_msg(struct rpc_channel *channel, void **msg, ...@@ -341,15 +316,11 @@ static int vmw_recv_msg(struct rpc_channel *channel, void **msg,
while (retries < RETRIES) { while (retries < RETRIES) {
retries++; retries++;
/* Set up additional parameters */ vmware_hypercall7(VMW_PORT_CMD_RECVSIZE,
si = channel->cookie_high; 0, channel->channel_id << 16,
di = channel->cookie_low; channel->cookie_high,
channel->cookie_low,
VMW_PORT(VMW_PORT_CMD_RECVSIZE, &ebx, &ecx, &edx);
0, si, di,
channel->channel_id << 16,
VMW_HYPERVISOR_MAGIC,
eax, ebx, ecx, edx, si, di);
if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) { if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) {
DRM_ERROR("Failed to get reply size for host message.\n"); DRM_ERROR("Failed to get reply size for host message.\n");
...@@ -384,16 +355,12 @@ static int vmw_recv_msg(struct rpc_channel *channel, void **msg, ...@@ -384,16 +355,12 @@ static int vmw_recv_msg(struct rpc_channel *channel, void **msg,
reply[reply_len] = '\0'; reply[reply_len] = '\0';
vmware_hypercall5(VMW_PORT_CMD_RECVSTATUS,
/* Ack buffer */ MESSAGE_STATUS_SUCCESS,
si = channel->cookie_high;
di = channel->cookie_low;
VMW_PORT(VMW_PORT_CMD_RECVSTATUS,
MESSAGE_STATUS_SUCCESS, si, di,
channel->channel_id << 16, channel->channel_id << 16,
VMW_HYPERVISOR_MAGIC, channel->cookie_high,
eax, ebx, ecx, edx, si, di); channel->cookie_low,
&ecx);
if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) { if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) {
kfree(reply); kfree(reply);
...@@ -652,13 +619,7 @@ static inline void reset_ppn_array(PPN64 *arr, size_t size) ...@@ -652,13 +619,7 @@ static inline void reset_ppn_array(PPN64 *arr, size_t size)
*/ */
static inline void hypervisor_ppn_reset_all(void) static inline void hypervisor_ppn_reset_all(void)
{ {
unsigned long eax, ebx, ecx, edx, si = 0, di = 0; vmware_hypercall1(VMW_PORT_CMD_MKSGS_RESET, 0);
VMW_PORT(VMW_PORT_CMD_MKSGS_RESET,
0, si, di,
0,
VMW_HYPERVISOR_MAGIC,
eax, ebx, ecx, edx, si, di);
} }
/** /**
...@@ -669,13 +630,7 @@ static inline void hypervisor_ppn_reset_all(void) ...@@ -669,13 +630,7 @@ static inline void hypervisor_ppn_reset_all(void)
*/ */
static inline void hypervisor_ppn_add(PPN64 pfn) static inline void hypervisor_ppn_add(PPN64 pfn)
{ {
unsigned long eax, ebx, ecx, edx, si = 0, di = 0; vmware_hypercall1(VMW_PORT_CMD_MKSGS_ADD_PPN, (unsigned long)pfn);
VMW_PORT(VMW_PORT_CMD_MKSGS_ADD_PPN,
(unsigned long)pfn, si, di,
0,
VMW_HYPERVISOR_MAGIC,
eax, ebx, ecx, edx, si, di);
} }
/** /**
...@@ -686,13 +641,7 @@ static inline void hypervisor_ppn_add(PPN64 pfn) ...@@ -686,13 +641,7 @@ static inline void hypervisor_ppn_add(PPN64 pfn)
*/ */
static inline void hypervisor_ppn_remove(PPN64 pfn) static inline void hypervisor_ppn_remove(PPN64 pfn)
{ {
unsigned long eax, ebx, ecx, edx, si = 0, di = 0; vmware_hypercall1(VMW_PORT_CMD_MKSGS_REMOVE_PPN, (unsigned long)pfn);
VMW_PORT(VMW_PORT_CMD_MKSGS_REMOVE_PPN,
(unsigned long)pfn, si, di,
0,
VMW_HYPERVISOR_MAGIC,
eax, ebx, ecx, edx, si, di);
} }
#if IS_ENABLED(CONFIG_DRM_VMWGFX_MKSSTATS) #if IS_ENABLED(CONFIG_DRM_VMWGFX_MKSSTATS)
......
...@@ -34,6 +34,8 @@ ...@@ -34,6 +34,8 @@
#define VMWARE_HYPERVISOR_HB BIT(0) #define VMWARE_HYPERVISOR_HB BIT(0)
#define VMWARE_HYPERVISOR_OUT BIT(1) #define VMWARE_HYPERVISOR_OUT BIT(1)
#define VMWARE_HYPERVISOR_MAGIC 0x564D5868
#define X86_IO_MAGIC 0x86 #define X86_IO_MAGIC 0x86
#define X86_IO_W7_SIZE_SHIFT 0 #define X86_IO_W7_SIZE_SHIFT 0
...@@ -45,86 +47,158 @@ ...@@ -45,86 +47,158 @@
#define X86_IO_W7_IMM_SHIFT 5 #define X86_IO_W7_IMM_SHIFT 5
#define X86_IO_W7_IMM_MASK (0xff << X86_IO_W7_IMM_SHIFT) #define X86_IO_W7_IMM_MASK (0xff << X86_IO_W7_IMM_SHIFT)
static inline void vmw_port(unsigned long cmd, unsigned long in_ebx, static inline
unsigned long in_si, unsigned long in_di, unsigned long vmware_hypercall1(unsigned long cmd, unsigned long in1)
unsigned long flags, unsigned long magic,
unsigned long *eax, unsigned long *ebx,
unsigned long *ecx, unsigned long *edx,
unsigned long *si, unsigned long *di)
{ {
register u64 x0 asm("x0") = magic; register u64 x0 asm("x0") = VMWARE_HYPERVISOR_MAGIC;
register u64 x1 asm("x1") = in_ebx; register u64 x1 asm("x1") = in1;
register u64 x2 asm("x2") = cmd; register u64 x2 asm("x2") = cmd;
register u64 x3 asm("x3") = flags | VMWARE_HYPERVISOR_PORT; register u64 x3 asm("x3") = VMWARE_HYPERVISOR_PORT;
register u64 x4 asm("x4") = in_si; register u64 x7 asm("x7") = ((u64)X86_IO_MAGIC << 32) |
register u64 x5 asm("x5") = in_di; X86_IO_W7_WITH |
X86_IO_W7_DIR |
(2 << X86_IO_W7_SIZE_SHIFT);
asm_inline volatile (
"mrs xzr, mdccsr_el0; "
: "+r" (x0)
: "r" (x1), "r" (x2), "r" (x3), "r" (x7)
: "memory");
return x0;
}
static inline
unsigned long vmware_hypercall5(unsigned long cmd, unsigned long in1,
unsigned long in3, unsigned long in4,
unsigned long in5, u32 *out2)
{
register u64 x0 asm("x0") = VMWARE_HYPERVISOR_MAGIC;
register u64 x1 asm("x1") = in1;
register u64 x2 asm("x2") = cmd;
register u64 x3 asm("x3") = in3 | VMWARE_HYPERVISOR_PORT;
register u64 x4 asm("x4") = in4;
register u64 x5 asm("x5") = in5;
register u64 x7 asm("x7") = ((u64)X86_IO_MAGIC << 32) | register u64 x7 asm("x7") = ((u64)X86_IO_MAGIC << 32) |
X86_IO_W7_WITH | X86_IO_W7_WITH |
X86_IO_W7_DIR | X86_IO_W7_DIR |
(2 << X86_IO_W7_SIZE_SHIFT); (2 << X86_IO_W7_SIZE_SHIFT);
asm volatile("mrs xzr, mdccsr_el0 \n\t" asm_inline volatile (
: "+r"(x0), "+r"(x1), "+r"(x2), "mrs xzr, mdccsr_el0; "
"+r"(x3), "+r"(x4), "+r"(x5) : "+r" (x0), "+r" (x2)
: "r"(x7) : "r" (x1), "r" (x3), "r" (x4), "r" (x5), "r" (x7)
:); : "memory");
*eax = x0;
*ebx = x1; *out2 = x2;
*ecx = x2; return x0;
*edx = x3;
*si = x4;
*di = x5;
} }
static inline void vmw_port_hb(unsigned long cmd, unsigned long in_ecx, static inline
unsigned long in_si, unsigned long in_di, unsigned long vmware_hypercall6(unsigned long cmd, unsigned long in1,
unsigned long flags, unsigned long magic, unsigned long in3, u32 *out2,
unsigned long bp, u32 w7dir, u32 *out3, u32 *out4, u32 *out5)
unsigned long *eax, unsigned long *ebx,
unsigned long *ecx, unsigned long *edx,
unsigned long *si, unsigned long *di)
{ {
register u64 x0 asm("x0") = magic; register u64 x0 asm("x0") = VMWARE_HYPERVISOR_MAGIC;
register u64 x1 asm("x1") = in1;
register u64 x2 asm("x2") = cmd;
register u64 x3 asm("x3") = in3 | VMWARE_HYPERVISOR_PORT;
register u64 x4 asm("x4");
register u64 x5 asm("x5");
register u64 x7 asm("x7") = ((u64)X86_IO_MAGIC << 32) |
X86_IO_W7_WITH |
X86_IO_W7_DIR |
(2 << X86_IO_W7_SIZE_SHIFT);
asm_inline volatile (
"mrs xzr, mdccsr_el0; "
: "+r" (x0), "+r" (x2), "+r" (x3), "=r" (x4), "=r" (x5)
: "r" (x1), "r" (x7)
: "memory");
*out2 = x2;
*out3 = x3;
*out4 = x4;
*out5 = x5;
return x0;
}
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)
{
register u64 x0 asm("x0") = VMWARE_HYPERVISOR_MAGIC;
register u64 x1 asm("x1") = in1;
register u64 x2 asm("x2") = cmd;
register u64 x3 asm("x3") = in3 | VMWARE_HYPERVISOR_PORT;
register u64 x4 asm("x4") = in4;
register u64 x5 asm("x5") = in5;
register u64 x7 asm("x7") = ((u64)X86_IO_MAGIC << 32) |
X86_IO_W7_WITH |
X86_IO_W7_DIR |
(2 << X86_IO_W7_SIZE_SHIFT);
asm_inline volatile (
"mrs xzr, mdccsr_el0; "
: "+r" (x0), "+r" (x1), "+r" (x2), "+r" (x3)
: "r" (x4), "r" (x5), "r" (x7)
: "memory");
*out1 = x1;
*out2 = x2;
*out3 = x3;
return x0;
}
static inline
unsigned long vmware_hypercall_hb(unsigned long cmd, unsigned long in2,
unsigned long in3, unsigned long in4,
unsigned long in5, unsigned long in6,
u32 *out1, int dir)
{
register u64 x0 asm("x0") = VMWARE_HYPERVISOR_MAGIC;
register u64 x1 asm("x1") = cmd; register u64 x1 asm("x1") = cmd;
register u64 x2 asm("x2") = in_ecx; register u64 x2 asm("x2") = in2;
register u64 x3 asm("x3") = flags | VMWARE_HYPERVISOR_PORT_HB; register u64 x3 asm("x3") = in3 | VMWARE_HYPERVISOR_PORT_HB;
register u64 x4 asm("x4") = in_si; register u64 x4 asm("x4") = in4;
register u64 x5 asm("x5") = in_di; register u64 x5 asm("x5") = in5;
register u64 x6 asm("x6") = bp; register u64 x6 asm("x6") = in6;
register u64 x7 asm("x7") = ((u64)X86_IO_MAGIC << 32) | register u64 x7 asm("x7") = ((u64)X86_IO_MAGIC << 32) |
X86_IO_W7_STR | X86_IO_W7_STR |
X86_IO_W7_WITH | X86_IO_W7_WITH |
w7dir; dir;
asm volatile("mrs xzr, mdccsr_el0 \n\t"
: "+r"(x0), "+r"(x1), "+r"(x2),
"+r"(x3), "+r"(x4), "+r"(x5)
: "r"(x6), "r"(x7)
:);
*eax = x0;
*ebx = x1;
*ecx = x2;
*edx = x3;
*si = x4;
*di = x5;
}
#define VMW_PORT(cmd, in_ebx, in_si, in_di, flags, magic, eax, ebx, ecx, edx, \ asm_inline volatile (
si, di) \ "mrs xzr, mdccsr_el0; "
vmw_port(cmd, in_ebx, in_si, in_di, flags, magic, &eax, &ebx, &ecx, \ : "+r" (x0), "+r" (x1)
&edx, &si, &di) : "r" (x2), "r" (x3), "r" (x4), "r" (x5),
"r" (x6), "r" (x7)
: "memory");
#define VMW_PORT_HB_OUT(cmd, in_ecx, in_si, in_di, flags, magic, bp, eax, ebx, \ *out1 = x1;
ecx, edx, si, di) \ return x0;
vmw_port_hb(cmd, in_ecx, in_si, in_di, flags, magic, bp, \ }
0, &eax, &ebx, &ecx, &edx, &si, &di)
#define VMW_PORT_HB_IN(cmd, in_ecx, in_si, in_di, flags, magic, bp, eax, ebx, \ static inline
ecx, edx, si, di) \ unsigned long vmware_hypercall_hb_out(unsigned long cmd, unsigned long in2,
vmw_port_hb(cmd, in_ecx, in_si, in_di, flags, magic, bp, \ unsigned long in3, unsigned long in4,
X86_IO_W7_DIR, &eax, &ebx, &ecx, &edx, &si, &di) unsigned long in5, unsigned long in6,
u32 *out1)
{
return vmware_hypercall_hb(cmd, in2, in3, in4, in5, in6, out1, 0);
}
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)
{
return vmware_hypercall_hb(cmd, in2, in3, in4, in5, in6, out1,
X86_IO_W7_DIR);
}
#endif #endif
#endif /* _VMWGFX_MSG_ARM64_H */ #endif /* _VMWGFX_MSG_ARM64_H */
...@@ -37,191 +37,6 @@ ...@@ -37,191 +37,6 @@
#include <asm/vmware.h> #include <asm/vmware.h>
/**
* Hypervisor-specific bi-directional communication channel. Should never
* execute on bare metal hardware. The caller must make sure to check for
* supported hypervisor before using these macros.
*
* The last two parameters are both input and output and must be initialized.
*
* @cmd: [IN] Message Cmd
* @in_ebx: [IN] Message Len, through EBX
* @in_si: [IN] Input argument through SI, set to 0 if not used
* @in_di: [IN] Input argument through DI, set ot 0 if not used
* @flags: [IN] hypercall flags + [channel id]
* @magic: [IN] hypervisor magic value
* @eax: [OUT] value of EAX register
* @ebx: [OUT] e.g. status from an HB message status command
* @ecx: [OUT] e.g. status from a non-HB message status command
* @edx: [OUT] e.g. channel id
* @si: [OUT]
* @di: [OUT]
*/
#define VMW_PORT(cmd, in_ebx, in_si, in_di, \
flags, magic, \
eax, ebx, ecx, edx, si, di) \
({ \
asm volatile (VMWARE_HYPERCALL : \
"=a"(eax), \
"=b"(ebx), \
"=c"(ecx), \
"=d"(edx), \
"=S"(si), \
"=D"(di) : \
"a"(magic), \
"b"(in_ebx), \
"c"(cmd), \
"d"(flags), \
"S"(in_si), \
"D"(in_di) : \
"memory"); \
})
/**
* Hypervisor-specific bi-directional communication channel. Should never
* execute on bare metal hardware. The caller must make sure to check for
* supported hypervisor before using these macros.
*
* The last 3 parameters are both input and output and must be initialized.
*
* @cmd: [IN] Message Cmd
* @in_ecx: [IN] Message Len, through ECX
* @in_si: [IN] Input argument through SI, set to 0 if not used
* @in_di: [IN] Input argument through DI, set to 0 if not used
* @flags: [IN] hypercall flags + [channel id]
* @magic: [IN] hypervisor magic value
* @bp: [IN]
* @eax: [OUT] value of EAX register
* @ebx: [OUT] e.g. status from an HB message status command
* @ecx: [OUT] e.g. status from a non-HB message status command
* @edx: [OUT] e.g. channel id
* @si: [OUT]
* @di: [OUT]
*/
#ifdef __x86_64__
#define VMW_PORT_HB_OUT(cmd, in_ecx, in_si, in_di, \
flags, magic, bp, \
eax, ebx, ecx, edx, si, di) \
({ \
asm volatile ( \
UNWIND_HINT_SAVE \
"push %%rbp;" \
UNWIND_HINT_UNDEFINED \
"mov %12, %%rbp;" \
VMWARE_HYPERCALL_HB_OUT \
"pop %%rbp;" \
UNWIND_HINT_RESTORE : \
"=a"(eax), \
"=b"(ebx), \
"=c"(ecx), \
"=d"(edx), \
"=S"(si), \
"=D"(di) : \
"a"(magic), \
"b"(cmd), \
"c"(in_ecx), \
"d"(flags), \
"S"(in_si), \
"D"(in_di), \
"r"(bp) : \
"memory", "cc"); \
})
#define VMW_PORT_HB_IN(cmd, in_ecx, in_si, in_di, \
flags, magic, bp, \
eax, ebx, ecx, edx, si, di) \
({ \
asm volatile ( \
UNWIND_HINT_SAVE \
"push %%rbp;" \
UNWIND_HINT_UNDEFINED \
"mov %12, %%rbp;" \
VMWARE_HYPERCALL_HB_IN \
"pop %%rbp;" \
UNWIND_HINT_RESTORE : \
"=a"(eax), \
"=b"(ebx), \
"=c"(ecx), \
"=d"(edx), \
"=S"(si), \
"=D"(di) : \
"a"(magic), \
"b"(cmd), \
"c"(in_ecx), \
"d"(flags), \
"S"(in_si), \
"D"(in_di), \
"r"(bp) : \
"memory", "cc"); \
})
#elif defined(__i386__)
/*
* In the 32-bit version of this macro, we store bp in a memory location
* because we've ran out of registers.
* Now we can't reference that memory location while we've modified
* %esp or %ebp, so we first push it on the stack, just before we push
* %ebp, and then when we need it we read it from the stack where we
* just pushed it.
*/
#define VMW_PORT_HB_OUT(cmd, in_ecx, in_si, in_di, \
flags, magic, bp, \
eax, ebx, ecx, edx, si, di) \
({ \
asm volatile ("push %12;" \
"push %%ebp;" \
"mov 0x04(%%esp), %%ebp;" \
VMWARE_HYPERCALL_HB_OUT \
"pop %%ebp;" \
"add $0x04, %%esp;" : \
"=a"(eax), \
"=b"(ebx), \
"=c"(ecx), \
"=d"(edx), \
"=S"(si), \
"=D"(di) : \
"a"(magic), \
"b"(cmd), \
"c"(in_ecx), \
"d"(flags), \
"S"(in_si), \
"D"(in_di), \
"m"(bp) : \
"memory", "cc"); \
})
#define VMW_PORT_HB_IN(cmd, in_ecx, in_si, in_di, \
flags, magic, bp, \
eax, ebx, ecx, edx, si, di) \
({ \
asm volatile ("push %12;" \
"push %%ebp;" \
"mov 0x04(%%esp), %%ebp;" \
VMWARE_HYPERCALL_HB_IN \
"pop %%ebp;" \
"add $0x04, %%esp;" : \
"=a"(eax), \
"=b"(ebx), \
"=c"(ecx), \
"=d"(edx), \
"=S"(si), \
"=D"(di) : \
"a"(magic), \
"b"(cmd), \
"c"(in_ecx), \
"d"(flags), \
"S"(in_si), \
"D"(in_di), \
"m"(bp) : \
"memory", "cc"); \
})
#endif /* defined(__i386__) */
#endif /* defined(__i386__) || defined(__x86_64__) */ #endif /* defined(__i386__) || defined(__x86_64__) */
#endif /* _VMWGFX_MSG_X86_H */ #endif /* _VMWGFX_MSG_X86_H */
...@@ -21,19 +21,16 @@ ...@@ -21,19 +21,16 @@
#include "psmouse.h" #include "psmouse.h"
#include "vmmouse.h" #include "vmmouse.h"
#define VMMOUSE_PROTO_MAGIC 0x564D5868U
/* /*
* Main commands supported by the vmmouse hypervisor port. * Main commands supported by the vmmouse hypervisor port.
*/ */
#define VMMOUSE_PROTO_CMD_GETVERSION 10 #define VMWARE_CMD_ABSPOINTER_DATA 39
#define VMMOUSE_PROTO_CMD_ABSPOINTER_DATA 39 #define VMWARE_CMD_ABSPOINTER_STATUS 40
#define VMMOUSE_PROTO_CMD_ABSPOINTER_STATUS 40 #define VMWARE_CMD_ABSPOINTER_COMMAND 41
#define VMMOUSE_PROTO_CMD_ABSPOINTER_COMMAND 41 #define VMWARE_CMD_ABSPOINTER_RESTRICT 86
#define VMMOUSE_PROTO_CMD_ABSPOINTER_RESTRICT 86
/* /*
* Subcommands for VMMOUSE_PROTO_CMD_ABSPOINTER_COMMAND * Subcommands for VMWARE_CMD_ABSPOINTER_COMMAND
*/ */
#define VMMOUSE_CMD_ENABLE 0x45414552U #define VMMOUSE_CMD_ENABLE 0x45414552U
#define VMMOUSE_CMD_DISABLE 0x000000f5U #define VMMOUSE_CMD_DISABLE 0x000000f5U
...@@ -76,28 +73,6 @@ struct vmmouse_data { ...@@ -76,28 +73,6 @@ struct vmmouse_data {
char dev_name[128]; char dev_name[128];
}; };
/*
* Hypervisor-specific bi-directional communication channel
* implementing the vmmouse protocol. Should never execute on
* bare metal hardware.
*/
#define VMMOUSE_CMD(cmd, in1, out1, out2, out3, out4) \
({ \
unsigned long __dummy1, __dummy2; \
__asm__ __volatile__ (VMWARE_HYPERCALL : \
"=a"(out1), \
"=b"(out2), \
"=c"(out3), \
"=d"(out4), \
"=S"(__dummy1), \
"=D"(__dummy2) : \
"a"(VMMOUSE_PROTO_MAGIC), \
"b"(in1), \
"c"(VMMOUSE_PROTO_CMD_##cmd), \
"d"(0) : \
"memory"); \
})
/** /**
* vmmouse_report_button - report button state on the correct input device * vmmouse_report_button - report button state on the correct input device
* *
...@@ -145,14 +120,12 @@ static psmouse_ret_t vmmouse_report_events(struct psmouse *psmouse) ...@@ -145,14 +120,12 @@ static psmouse_ret_t vmmouse_report_events(struct psmouse *psmouse)
struct input_dev *abs_dev = priv->abs_dev; struct input_dev *abs_dev = priv->abs_dev;
struct input_dev *pref_dev; struct input_dev *pref_dev;
u32 status, x, y, z; u32 status, x, y, z;
u32 dummy1, dummy2, dummy3;
unsigned int queue_length; unsigned int queue_length;
unsigned int count = 255; unsigned int count = 255;
while (count--) { while (count--) {
/* See if we have motion data. */ /* See if we have motion data. */
VMMOUSE_CMD(ABSPOINTER_STATUS, 0, status = vmware_hypercall1(VMWARE_CMD_ABSPOINTER_STATUS, 0);
status, dummy1, dummy2, dummy3);
if ((status & VMMOUSE_ERROR) == VMMOUSE_ERROR) { if ((status & VMMOUSE_ERROR) == VMMOUSE_ERROR) {
psmouse_err(psmouse, "failed to fetch status data\n"); psmouse_err(psmouse, "failed to fetch status data\n");
/* /*
...@@ -172,7 +145,8 @@ static psmouse_ret_t vmmouse_report_events(struct psmouse *psmouse) ...@@ -172,7 +145,8 @@ static psmouse_ret_t vmmouse_report_events(struct psmouse *psmouse)
} }
/* Now get it */ /* Now get it */
VMMOUSE_CMD(ABSPOINTER_DATA, 4, status, x, y, z); status = vmware_hypercall4(VMWARE_CMD_ABSPOINTER_DATA, 4,
&x, &y, &z);
/* /*
* And report what we've got. Prefer to report button * And report what we've got. Prefer to report button
...@@ -247,14 +221,10 @@ static psmouse_ret_t vmmouse_process_byte(struct psmouse *psmouse) ...@@ -247,14 +221,10 @@ static psmouse_ret_t vmmouse_process_byte(struct psmouse *psmouse)
static void vmmouse_disable(struct psmouse *psmouse) static void vmmouse_disable(struct psmouse *psmouse)
{ {
u32 status; u32 status;
u32 dummy1, dummy2, dummy3, dummy4;
VMMOUSE_CMD(ABSPOINTER_COMMAND, VMMOUSE_CMD_DISABLE,
dummy1, dummy2, dummy3, dummy4);
VMMOUSE_CMD(ABSPOINTER_STATUS, 0, vmware_hypercall1(VMWARE_CMD_ABSPOINTER_COMMAND, VMMOUSE_CMD_DISABLE);
status, dummy1, dummy2, dummy3);
status = vmware_hypercall1(VMWARE_CMD_ABSPOINTER_STATUS, 0);
if ((status & VMMOUSE_ERROR) != VMMOUSE_ERROR) if ((status & VMMOUSE_ERROR) != VMMOUSE_ERROR)
psmouse_warn(psmouse, "failed to disable vmmouse device\n"); psmouse_warn(psmouse, "failed to disable vmmouse device\n");
} }
...@@ -271,26 +241,24 @@ static void vmmouse_disable(struct psmouse *psmouse) ...@@ -271,26 +241,24 @@ static void vmmouse_disable(struct psmouse *psmouse)
static int vmmouse_enable(struct psmouse *psmouse) static int vmmouse_enable(struct psmouse *psmouse)
{ {
u32 status, version; u32 status, version;
u32 dummy1, dummy2, dummy3, dummy4;
/* /*
* Try enabling the device. If successful, we should be able to * Try enabling the device. If successful, we should be able to
* read valid version ID back from it. * read valid version ID back from it.
*/ */
VMMOUSE_CMD(ABSPOINTER_COMMAND, VMMOUSE_CMD_ENABLE, vmware_hypercall1(VMWARE_CMD_ABSPOINTER_COMMAND, VMMOUSE_CMD_ENABLE);
dummy1, dummy2, dummy3, dummy4);
/* /*
* See if version ID can be retrieved. * See if version ID can be retrieved.
*/ */
VMMOUSE_CMD(ABSPOINTER_STATUS, 0, status, dummy1, dummy2, dummy3); status = vmware_hypercall1(VMWARE_CMD_ABSPOINTER_STATUS, 0);
if ((status & 0x0000ffff) == 0) { if ((status & 0x0000ffff) == 0) {
psmouse_dbg(psmouse, "empty flags - assuming no device\n"); psmouse_dbg(psmouse, "empty flags - assuming no device\n");
return -ENXIO; return -ENXIO;
} }
VMMOUSE_CMD(ABSPOINTER_DATA, 1 /* single item */, version = vmware_hypercall1(VMWARE_CMD_ABSPOINTER_DATA,
version, dummy1, dummy2, dummy3); 1 /* single item */);
if (version != VMMOUSE_VERSION_ID) { if (version != VMMOUSE_VERSION_ID) {
psmouse_dbg(psmouse, "Unexpected version value: %u vs %u\n", psmouse_dbg(psmouse, "Unexpected version value: %u vs %u\n",
(unsigned) version, VMMOUSE_VERSION_ID); (unsigned) version, VMMOUSE_VERSION_ID);
...@@ -301,11 +269,11 @@ static int vmmouse_enable(struct psmouse *psmouse) ...@@ -301,11 +269,11 @@ static int vmmouse_enable(struct psmouse *psmouse)
/* /*
* Restrict ioport access, if possible. * Restrict ioport access, if possible.
*/ */
VMMOUSE_CMD(ABSPOINTER_RESTRICT, VMMOUSE_RESTRICT_CPL0, vmware_hypercall1(VMWARE_CMD_ABSPOINTER_RESTRICT,
dummy1, dummy2, dummy3, dummy4); VMMOUSE_RESTRICT_CPL0);
VMMOUSE_CMD(ABSPOINTER_COMMAND, VMMOUSE_CMD_REQUEST_ABSOLUTE, vmware_hypercall1(VMWARE_CMD_ABSPOINTER_COMMAND,
dummy1, dummy2, dummy3, dummy4); VMMOUSE_CMD_REQUEST_ABSOLUTE);
return 0; return 0;
} }
...@@ -342,7 +310,7 @@ static bool vmmouse_check_hypervisor(void) ...@@ -342,7 +310,7 @@ static bool vmmouse_check_hypervisor(void)
*/ */
int vmmouse_detect(struct psmouse *psmouse, bool set_properties) int vmmouse_detect(struct psmouse *psmouse, bool set_properties)
{ {
u32 response, version, dummy1, dummy2; u32 response, version, type;
if (!vmmouse_check_hypervisor()) { if (!vmmouse_check_hypervisor()) {
psmouse_dbg(psmouse, psmouse_dbg(psmouse,
...@@ -351,9 +319,9 @@ int vmmouse_detect(struct psmouse *psmouse, bool set_properties) ...@@ -351,9 +319,9 @@ int vmmouse_detect(struct psmouse *psmouse, bool set_properties)
} }
/* Check if the device is present */ /* Check if the device is present */
response = ~VMMOUSE_PROTO_MAGIC; response = ~VMWARE_HYPERVISOR_MAGIC;
VMMOUSE_CMD(GETVERSION, 0, version, response, dummy1, dummy2); version = vmware_hypercall3(VMWARE_CMD_GETVERSION, 0, &response, &type);
if (response != VMMOUSE_PROTO_MAGIC || version == 0xffffffffU) if (response != VMWARE_HYPERVISOR_MAGIC || version == 0xffffffffU)
return -ENXIO; return -ENXIO;
if (set_properties) { if (set_properties) {
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
#include <asm/hypervisor.h> #include <asm/hypervisor.h>
#include <asm/vmware.h> #include <asm/vmware.h>
#define VMWARE_MAGIC 0x564D5868
#define VMWARE_CMD_PCLK(nr) ((nr << 16) | 97) #define VMWARE_CMD_PCLK(nr) ((nr << 16) | 97)
#define VMWARE_CMD_PCLK_GETTIME VMWARE_CMD_PCLK(0) #define VMWARE_CMD_PCLK_GETTIME VMWARE_CMD_PCLK(0)
...@@ -24,15 +23,10 @@ static struct ptp_clock *ptp_vmw_clock; ...@@ -24,15 +23,10 @@ static struct ptp_clock *ptp_vmw_clock;
static int ptp_vmw_pclk_read(u64 *ns) static int ptp_vmw_pclk_read(u64 *ns)
{ {
u32 ret, nsec_hi, nsec_lo, unused1, unused2, unused3; u32 ret, nsec_hi, nsec_lo;
asm volatile (VMWARE_HYPERCALL :
"=a"(ret), "=b"(nsec_hi), "=c"(nsec_lo), "=d"(unused1),
"=S"(unused2), "=D"(unused3) :
"a"(VMWARE_MAGIC), "b"(0),
"c"(VMWARE_CMD_PCLK_GETTIME), "d"(0) :
"memory");
ret = vmware_hypercall3(VMWARE_CMD_PCLK_GETTIME, 0,
&nsec_hi, &nsec_lo);
if (ret == 0) if (ret == 0)
*ns = ((u64)nsec_hi << 32) | nsec_lo; *ns = ((u64)nsec_hi << 32) | nsec_lo;
return ret; return ret;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment