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

Commit bac7b4e8 authored by Thomas Hellstrom's avatar Thomas Hellstrom Committed by Borislav Petkov
Browse files

x86/vmware: Update platform detection code for VMCALL/VMMCALL hypercalls



Vmware has historically used an INL instruction for this, but recent
hardware versions support using VMCALL/VMMCALL instead, so use this
method if supported at platform detection time. Explicitly code separate
macro versions since the alternatives self-patching has not been
performed at platform detection time.

Also put tighter constraints on the assembly input parameters.

Co-developed-by: default avatarDoug Covelli <dcovelli@vmware.com>
Signed-off-by: default avatarDoug Covelli <dcovelli@vmware.com>
Signed-off-by: default avatarThomas Hellstrom <thellstrom@vmware.com>
Signed-off-by: default avatarBorislav Petkov <bp@suse.de>
Reviewed-by: default avatarDoug Covelli <dcovelli@vmware.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: linux-graphics-maintainer@vmware.com
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: virtualization@lists.linux-foundation.org
Cc: <pv-drivers@vmware.com>
Cc: x86-ml <x86@kernel.org>
Link: https://lkml.kernel.org/r/20190828080353.12658-2-thomas_os@shipmail.org
parent a55aa89a
Loading
Loading
Loading
Loading
+71 −17
Original line number Diff line number Diff line
@@ -35,29 +35,64 @@
#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;
}

@@ -136,7 +171,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 +209,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 +233,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 +254,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 = {