Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7ac63f6b authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'x86-vmware-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull x86 vmware updates from Ingo Molnar:
 "This updates the VMWARE guest driver with support for VMCALL/VMMCALL
  based hypercalls"

* 'x86-vmware-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  input/vmmouse: Update the backdoor call with support for new instructions
  drm/vmwgfx: Update the backdoor call with support for new instructions
  x86/vmware: Add a header file for hypercall definitions
  x86/vmware: Update platform detection code for VMCALL/VMMCALL hypercalls
parents e2bddc20 f7b15c74
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -17193,6 +17193,7 @@ M: "VMware, Inc." <pv-drivers@vmware.com>
L:	virtualization@lists.linux-foundation.org
S:	Supported
F:	arch/x86/kernel/cpu/vmware.c
F:	arch/x86/include/asm/vmware.h

VMWARE PVRDMA DRIVER
M:	Adit Ranadive <aditr@vmware.com>
+2 −0
Original line number Diff line number Diff line
@@ -231,6 +231,8 @@
#define X86_FEATURE_VMMCALL		( 8*32+15) /* Prefer VMMCALL to VMCALL */
#define X86_FEATURE_XENPV		( 8*32+16) /* "" Xen paravirtual guest */
#define X86_FEATURE_EPT_AD		( 8*32+17) /* Intel Extended Page Table access-dirty bit */
#define X86_FEATURE_VMCALL		( 8*32+18) /* "" Hypervisor supports the VMCALL instruction */
#define X86_FEATURE_VMW_VMMCALL		( 8*32+19) /* "" VMware prefers VMMCALL hypercall instruction */

/* Intel-defined CPU features, CPUID level 0x00000007:0 (EBX), word 9 */
#define X86_FEATURE_FSGSBASE		( 9*32+ 0) /* RDFSBASE, WRFSBASE, RDGSBASE, WRGSBASE instructions*/
+53 −0
Original line number Diff line number Diff line
/* 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>

/*
 * The hypercall definitions differ in the low word of the %edx argument
 * in the following way: the old port base interface uses the port
 * number to distinguish between high- and low bandwidth versions.
 *
 * The new vmcall interface instead uses a set of flags to select
 * bandwidth mode and transfer direction. The flags should be loaded
 * into %dx by any user and are automatically replaced by the port
 * number if the VMWARE_HYPERVISOR_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_OUT  BIT(1)

/* The low bandwidth call. The low word of edx is presumed clear. */
#define VMWARE_HYPERCALL						\
	ALTERNATIVE_2("movw $" VMWARE_HYPERVISOR_PORT ", %%dx; inl (%%dx)", \
		      "vmcall", X86_FEATURE_VMCALL,			\
		      "vmmcall", X86_FEATURE_VMW_VMMCALL)

/*
 * The high bandwidth out call. The low word of edx is presumed to have the
 * HB and OUT bits set.
 */
#define VMWARE_HYPERCALL_HB_OUT						\
	ALTERNATIVE_2("movw $" VMWARE_HYPERVISOR_PORT_HB ", %%dx; rep outsb", \
		      "vmcall", X86_FEATURE_VMCALL,			\
		      "vmmcall", X86_FEATURE_VMW_VMMCALL)

/*
 * The high bandwidth in call. The low word of edx is presumed to have the
 * HB bit set.
 */
#define VMWARE_HYPERCALL_HB_IN						\
	ALTERNATIVE_2("movw $" VMWARE_HYPERVISOR_PORT_HB ", %%dx; rep insb", \
		      "vmcall", X86_FEATURE_VMCALL,			\
		      "vmmcall", X86_FEATURE_VMW_VMMCALL)
#endif
+76 −18
Original line number Diff line number Diff line
@@ -30,34 +30,69 @@
#include <asm/hypervisor.h>
#include <asm/timer.h>
#include <asm/apic.h>
#include <asm/vmware.h>

#undef pr_fmt
#define pr_fmt(fmt)	"vmware: " fmt

#define CPUID_VMWARE_INFO_LEAF               0x40000000
#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 VMWARE_HYPERVISOR_PORT	0x5658

#define VMWARE_PORT_CMD_GETVERSION	10
#define VMWARE_PORT_CMD_GETHZ		45
#define VMWARE_PORT_CMD_GETVCPU_INFO	68
#define VMWARE_PORT_CMD_LEGACY_X2APIC	3
#define VMWARE_PORT_CMD_VCPU_RESERVED	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_PORT(cmd, eax, ebx, ecx, edx)				\
	__asm__("inl (%%dx)" :						\
		"=a"(eax), "=c"(ecx), "=d"(edx), "=b"(ebx) :		\
			"0"(VMWARE_HYPERVISOR_MAGIC),			\
			"1"(VMWARE_PORT_CMD_##cmd),			\
			"2"(VMWARE_HYPERVISOR_PORT), "3"(UINT_MAX) :	\
			"memory");
		"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)

static unsigned long vmware_tsc_khz __ro_after_init;
static u8 vmware_hypercall_mode     __ro_after_init;

static inline int __vmware_platform(void)
{
	uint32_t eax, ebx, ecx, edx;
	VMWARE_PORT(GETVERSION, eax, ebx, ecx, edx);
	VMWARE_CMD(GETVERSION, eax, ebx, ecx, edx);
	return eax != (uint32_t)-1 && ebx == VMWARE_HYPERVISOR_MAGIC;
}

@@ -129,6 +164,10 @@ static void __init vmware_set_capabilities(void)
{
	setup_force_cpu_cap(X86_FEATURE_CONSTANT_TSC);
	setup_force_cpu_cap(X86_FEATURE_TSC_RELIABLE);
	if (vmware_hypercall_mode == CPUID_VMWARE_FEATURES_ECX_VMCALL)
		setup_force_cpu_cap(X86_FEATURE_VMCALL);
	else if (vmware_hypercall_mode == CPUID_VMWARE_FEATURES_ECX_VMMCALL)
		setup_force_cpu_cap(X86_FEATURE_VMW_VMMCALL);
}

static void __init vmware_platform_setup(void)
@@ -136,7 +175,7 @@ static void __init vmware_platform_setup(void)
	uint32_t eax, ebx, ecx, edx;
	uint64_t lpj, tsc_khz;

	VMWARE_PORT(GETHZ, eax, ebx, ecx, edx);
	VMWARE_CMD(GETHZ, eax, ebx, ecx, edx);

	if (ebx != UINT_MAX) {
		lpj = tsc_khz = eax | (((uint64_t)ebx) << 32);
@@ -174,10 +213,21 @@ static void __init vmware_platform_setup(void)
	vmware_set_capabilities();
}

static u8 vmware_select_hypercall(void)
{
	int eax, ebx, ecx, edx;

	cpuid(CPUID_VMWARE_FEATURES_LEAF, &eax, &ebx, &ecx, &edx);
	return (ecx & (CPUID_VMWARE_FEATURES_ECX_VMMCALL |
		       CPUID_VMWARE_FEATURES_ECX_VMCALL));
}

/*
 * While checking the dmi string information, just checking the product
 * serial key should be enough, as this will always have a VMware
 * specific string when running under VMware hypervisor.
 * If !boot_cpu_has(X86_FEATURE_HYPERVISOR), vmware_hypercall_mode
 * intentionally defaults to 0.
 */
static uint32_t __init vmware_platform(void)
{
@@ -187,8 +237,16 @@ static uint32_t __init vmware_platform(void)

		cpuid(CPUID_VMWARE_INFO_LEAF, &eax, &hyper_vendor_id[0],
		      &hyper_vendor_id[1], &hyper_vendor_id[2]);
		if (!memcmp(hyper_vendor_id, "VMwareVMware", 12))
		if (!memcmp(hyper_vendor_id, "VMwareVMware", 12)) {
			if (eax >= CPUID_VMWARE_FEATURES_LEAF)
				vmware_hypercall_mode =
					vmware_select_hypercall();

			pr_info("hypercall mode: 0x%02x\n",
				(unsigned int) vmware_hypercall_mode);

			return CPUID_VMWARE_INFO_LEAF;
		}
	} else if (dmi_available && dmi_name_in_serial("VMware") &&
		   __vmware_platform())
		return 1;
@@ -200,9 +258,9 @@ static uint32_t __init vmware_platform(void)
static bool __init vmware_legacy_x2apic_available(void)
{
	uint32_t eax, ebx, ecx, edx;
	VMWARE_PORT(GETVCPU_INFO, eax, ebx, ecx, edx);
	return (eax & (1 << VMWARE_PORT_CMD_VCPU_RESERVED)) == 0 &&
	       (eax & (1 << VMWARE_PORT_CMD_LEGACY_X2APIC)) != 0;
	VMWARE_CMD(GETVCPU_INFO, eax, ebx, ecx, edx);
	return (eax & (1 << VMWARE_CMD_VCPU_RESERVED)) == 0 &&
	       (eax & (1 << VMWARE_CMD_LEGACY_X2APIC)) != 0;
}

const __initconst struct hypervisor_x86 x86_hyper_vmware = {
+10 −11
Original line number Diff line number Diff line
@@ -46,8 +46,6 @@
#define RETRIES                 3

#define VMW_HYPERVISOR_MAGIC    0x564D5868
#define VMW_HYPERVISOR_PORT     0x5658
#define VMW_HYPERVISOR_HB_PORT  0x5659

#define VMW_PORT_CMD_MSG        30
#define VMW_PORT_CMD_HB_MSG     0
@@ -93,7 +91,7 @@ static int vmw_open_channel(struct rpc_channel *channel, unsigned int protocol)

	VMW_PORT(VMW_PORT_CMD_OPEN_CHANNEL,
		(protocol | GUESTMSG_FLAG_COOKIE), si, di,
		VMW_HYPERVISOR_PORT,
		0,
		VMW_HYPERVISOR_MAGIC,
		eax, ebx, ecx, edx, si, di);

@@ -126,7 +124,7 @@ static int vmw_close_channel(struct rpc_channel *channel)

	VMW_PORT(VMW_PORT_CMD_CLOSE_CHANNEL,
		0, si, di,
		(VMW_HYPERVISOR_PORT | (channel->channel_id << 16)),
		channel->channel_id << 16,
		VMW_HYPERVISOR_MAGIC,
		eax, ebx, ecx, edx, si, di);

@@ -160,7 +158,8 @@ static unsigned long vmw_port_hb_out(struct rpc_channel *channel,
		VMW_PORT_HB_OUT(
			(MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG,
			msg_len, si, di,
			VMW_HYPERVISOR_HB_PORT | (channel->channel_id << 16),
			VMWARE_HYPERVISOR_HB | (channel->channel_id << 16) |
			VMWARE_HYPERVISOR_OUT,
			VMW_HYPERVISOR_MAGIC, bp,
			eax, ebx, ecx, edx, si, di);

@@ -181,7 +180,7 @@ static unsigned long vmw_port_hb_out(struct rpc_channel *channel,

		VMW_PORT(VMW_PORT_CMD_MSG | (MSG_TYPE_SENDPAYLOAD << 16),
			 word, si, di,
			 VMW_HYPERVISOR_PORT | (channel->channel_id << 16),
			 channel->channel_id << 16,
			 VMW_HYPERVISOR_MAGIC,
			 eax, ebx, ecx, edx, si, di);
	}
@@ -213,7 +212,7 @@ static unsigned long vmw_port_hb_in(struct rpc_channel *channel, char *reply,
		VMW_PORT_HB_IN(
			(MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG,
			reply_len, si, di,
			VMW_HYPERVISOR_HB_PORT | (channel->channel_id << 16),
			VMWARE_HYPERVISOR_HB | (channel->channel_id << 16),
			VMW_HYPERVISOR_MAGIC, bp,
			eax, ebx, ecx, edx, si, di);

@@ -230,7 +229,7 @@ static unsigned long vmw_port_hb_in(struct rpc_channel *channel, char *reply,

		VMW_PORT(VMW_PORT_CMD_MSG | (MSG_TYPE_RECVPAYLOAD << 16),
			 MESSAGE_STATUS_SUCCESS, si, di,
			 VMW_HYPERVISOR_PORT | (channel->channel_id << 16),
			 channel->channel_id << 16,
			 VMW_HYPERVISOR_MAGIC,
			 eax, ebx, ecx, edx, si, di);

@@ -269,7 +268,7 @@ static int vmw_send_msg(struct rpc_channel *channel, const char *msg)

		VMW_PORT(VMW_PORT_CMD_SENDSIZE,
			msg_len, si, di,
			VMW_HYPERVISOR_PORT | (channel->channel_id << 16),
			channel->channel_id << 16,
			VMW_HYPERVISOR_MAGIC,
			eax, ebx, ecx, edx, si, di);

@@ -327,7 +326,7 @@ static int vmw_recv_msg(struct rpc_channel *channel, void **msg,

		VMW_PORT(VMW_PORT_CMD_RECVSIZE,
			0, si, di,
			(VMW_HYPERVISOR_PORT | (channel->channel_id << 16)),
			channel->channel_id << 16,
			VMW_HYPERVISOR_MAGIC,
			eax, ebx, ecx, edx, si, di);

@@ -371,7 +370,7 @@ static int vmw_recv_msg(struct rpc_channel *channel, void **msg,

		VMW_PORT(VMW_PORT_CMD_RECVSTATUS,
			MESSAGE_STATUS_SUCCESS, si, di,
			(VMW_HYPERVISOR_PORT | (channel->channel_id << 16)),
			channel->channel_id << 16,
			VMW_HYPERVISOR_MAGIC,
			eax, ebx, ecx, edx, si, di);

Loading