changeset 14043:0291cd939b43

3506 Use "hypervisor" CPUID bit to detect hypervisor environment Reviewed by: Hans Rosenfeld <hans.rosenfeld@nexenta.com> Reviewed by: Albert Lee <trisk@nexenta.com> Approved by: Robert Mustacchi <rm@joyent.com>
author Yuri Pankov <yuri.pankov@nexenta.com>
date Mon, 10 Jun 2013 09:51:40 -0700
parents e5bd6d1f1685
children 8fa61bfa2342
files usr/src/uts/i86pc/os/cpuid.c usr/src/uts/i86pc/os/cpuid_subr.c usr/src/uts/i86pc/os/microcode.c usr/src/uts/i86pc/os/mlsetup.c usr/src/uts/i86pc/os/startup.c usr/src/uts/i86pc/os/timestamp.c usr/src/uts/intel/ia32/ml/i86_subr.s usr/src/uts/intel/sys/x86_archext.h
diffstat 8 files changed, 156 insertions(+), 83 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/i86pc/os/cpuid.c	Sun Jun 09 14:38:37 2013 +0400
+++ b/usr/src/uts/i86pc/os/cpuid.c	Mon Jun 10 09:51:40 2013 -0700
@@ -21,6 +21,7 @@
 /*
  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2011 by Delphix. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
  */
 /*
  * Copyright (c) 2010, Intel Corporation.
@@ -220,7 +221,7 @@
 boolean_t xsave_force_disable = B_FALSE;
 
 /*
- * This is set to platform type Solaris is running on.
+ * This is set to platform type we are running on.
  */
 static int platform_type = -1;
 
@@ -597,20 +598,20 @@
 }
 
 #if !defined(__xpv)
-
 /*
  * Determine the type of the underlying platform. This is used to customize
  * initialization of various subsystems (e.g. TSC). determine_platform() must
  * only ever be called once to prevent two processors from seeing different
- * values of platform_type, it must be called before cpuid_pass1(), the
- * earliest consumer to execute.
+ * values of platform_type. Must be called before cpuid_pass1(), the earliest
+ * consumer to execute (uses _cpuid_chiprev --> synth_amd_info --> get_hwenv).
  */
 void
 determine_platform(void)
 {
 	struct cpuid_regs cp;
-	char *xen_str;
-	uint32_t xen_signature[4], base;
+	uint32_t base;
+	uint32_t regs[4];
+	char *hvstr = (char *)regs;
 
 	ASSERT(platform_type == -1);
 
@@ -620,31 +621,75 @@
 		return;
 
 	/*
-	 * In a fully virtualized domain, Xen's pseudo-cpuid function
-	 * returns a string representing the Xen signature in %ebx, %ecx,
-	 * and %edx. %eax contains the maximum supported cpuid function.
-	 * We need at least a (base + 2) leaf value to do what we want
-	 * to do. Try different base values, since the hypervisor might
-	 * use a different one depending on whether hyper-v emulation
-	 * is switched on by default or not.
+	 * If Hypervisor CPUID bit is set, try to determine hypervisor
+	 * vendor signature, and set platform type accordingly.
+	 *
+	 * References:
+	 * http://lkml.org/lkml/2008/10/1/246
+	 * http://kb.vmware.com/kb/1009458
+	 */
+	cp.cp_eax = 0x1;
+	(void) __cpuid_insn(&cp);
+	if ((cp.cp_ecx & CPUID_INTC_ECX_HV) != 0) {
+		cp.cp_eax = 0x40000000;
+		(void) __cpuid_insn(&cp);
+		regs[0] = cp.cp_ebx;
+		regs[1] = cp.cp_ecx;
+		regs[2] = cp.cp_edx;
+		regs[3] = 0;
+		if (strcmp(hvstr, HVSIG_XEN_HVM) == 0) {
+			platform_type = HW_XEN_HVM;
+			return;
+		}
+		if (strcmp(hvstr, HVSIG_VMWARE) == 0) {
+			platform_type = HW_VMWARE;
+			return;
+		}
+		if (strcmp(hvstr, HVSIG_KVM) == 0) {
+			platform_type = HW_KVM;
+			return;
+		}
+		if (strcmp(hvstr, HVSIG_MICROSOFT) == 0)
+			platform_type = HW_MICROSOFT;
+	} else {
+		/*
+		 * Check older VMware hardware versions. VMware hypervisor is
+		 * detected by performing an IN operation to VMware hypervisor
+		 * port and checking that value returned in %ebx is VMware
+		 * hypervisor magic value.
+		 *
+		 * References: http://kb.vmware.com/kb/1009458
+		 */
+		vmware_port(VMWARE_HVCMD_GETVERSION, regs);
+		if (regs[1] == VMWARE_HVMAGIC) {
+			platform_type = HW_VMWARE;
+			return;
+		}
+	}
+
+	/*
+	 * Check Xen hypervisor. In a fully virtualized domain,
+	 * Xen's pseudo-cpuid function returns a string representing the
+	 * Xen signature in %ebx, %ecx, and %edx. %eax contains the maximum
+	 * supported cpuid function. We need at least a (base + 2) leaf value
+	 * to do what we want to do. Try different base values, since the
+	 * hypervisor might use a different one depending on whether Hyper-V
+	 * emulation is switched on by default or not.
 	 */
 	for (base = 0x40000000; base < 0x40010000; base += 0x100) {
 		cp.cp_eax = base;
 		(void) __cpuid_insn(&cp);
-		xen_signature[0] = cp.cp_ebx;
-		xen_signature[1] = cp.cp_ecx;
-		xen_signature[2] = cp.cp_edx;
-		xen_signature[3] = 0;
-		xen_str = (char *)xen_signature;
-		if (strcmp("XenVMMXenVMM", xen_str) == 0 &&
+		regs[0] = cp.cp_ebx;
+		regs[1] = cp.cp_ecx;
+		regs[2] = cp.cp_edx;
+		regs[3] = 0;
+		if (strcmp(hvstr, HVSIG_XEN_HVM) == 0 &&
 		    cp.cp_eax >= (base + 2)) {
-			platform_type = HW_XEN_HVM;
+			platform_type &= ~HW_NATIVE;
+			platform_type |= HW_XEN_HVM;
 			return;
 		}
 	}
-
-	if (vmware_platform()) /* running under vmware hypervisor? */
-		platform_type = HW_VMWARE;
 }
 
 int
@@ -2366,7 +2411,7 @@
  * the other cpus.
  *
  * Fixup the brand string, and collect any information from cpuid
- * that requires dynamicically allocated storage to represent.
+ * that requires dynamically allocated storage to represent.
  */
 /*ARGSUSED*/
 void
--- a/usr/src/uts/i86pc/os/cpuid_subr.c	Sun Jun 09 14:38:37 2013 +0400
+++ b/usr/src/uts/i86pc/os/cpuid_subr.c	Mon Jun 10 09:51:40 2013 -0700
@@ -22,6 +22,8 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ *
+ * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
  */
 
 /*
@@ -391,7 +393,7 @@
 #endif
 		platform = get_hwenv();
 
-		if ((platform == HW_XEN_HVM) || (platform == HW_VMWARE)) {
+		if ((platform & HW_VIRTUAL) != 0) {
 			*skt_p = X86_SOCKET_UNKNOWN;
 		} else if (family == 0xf) {
 			*skt_p = amd_skts[rmp->rm_sktidx][model & 0x3];
--- a/usr/src/uts/i86pc/os/microcode.c	Sun Jun 09 14:38:37 2013 +0400
+++ b/usr/src/uts/i86pc/os/microcode.c	Mon Jun 10 09:51:40 2013 -0700
@@ -22,6 +22,8 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ *
+ * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
  */
 
 #include <sys/asm_linkage.h>
@@ -202,17 +204,21 @@
  * We also assume that we don't support a mix of Intel and
  * AMD processors in the same box.
  *
- * An i86xpv guest domain can't update the microcode.
+ * An i86xpv guest domain or VM can't update the microcode.
  */
+
+#define	XPVDOMU_OR_HVM	\
+	((hwenv == HW_XEN_PV && !is_controldom()) || (hwenv & HW_VIRTUAL) != 0)
+
 /*ARGSUSED*/
 static int
 ucode_capable_amd(cpu_t *cp)
 {
 	int hwenv = get_hwenv();
 
-	if (hwenv == HW_XEN_HVM || (hwenv == HW_XEN_PV && !is_controldom())) {
+	if (XPVDOMU_OR_HVM)
 		return (0);
-	}
+
 	return (cpuid_getfamily(cp) >= 0x10);
 }
 
@@ -221,9 +227,9 @@
 {
 	int hwenv = get_hwenv();
 
-	if (hwenv == HW_XEN_HVM || (hwenv == HW_XEN_PV && !is_controldom())) {
+	if (XPVDOMU_OR_HVM)
 		return (0);
-	}
+
 	return (cpuid_getfamily(cp) >= 6);
 }
 
--- a/usr/src/uts/i86pc/os/mlsetup.c	Sun Jun 09 14:38:37 2013 +0400
+++ b/usr/src/uts/i86pc/os/mlsetup.c	Mon Jun 10 09:51:40 2013 -0700
@@ -195,8 +195,7 @@
 	cpuid_pass1(cpu[0], x86_featureset);
 
 #if !defined(__xpv)
-
-	if (get_hwenv() == HW_XEN_HVM)
+	if ((get_hwenv() & HW_XEN_HVM) != 0)
 		xen_hvm_init();
 
 	/*
@@ -218,7 +217,7 @@
 	 * The Xen hypervisor does not correctly report whether rdtscp is
 	 * supported or not, so we must assume that it is not.
 	 */
-	if (get_hwenv() != HW_XEN_HVM &&
+	if ((get_hwenv() & HW_XEN_HVM) == 0 &&
 	    is_x86_feature(x86_featureset, X86FSET_TSCP))
 		patch_tsc_read(X86_HAVE_TSCP);
 	else if (cpuid_getvendor(CPU) == X86_VENDOR_AMD &&
--- a/usr/src/uts/i86pc/os/startup.c	Sun Jun 09 14:38:37 2013 +0400
+++ b/usr/src/uts/i86pc/os/startup.c	Mon Jun 10 09:51:40 2013 -0700
@@ -21,6 +21,7 @@
 /*
  * Copyright (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2012 DEY Storage Systems, Inc.  All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
  */
 /*
  * Copyright (c) 2010, Intel Corporation.
@@ -1534,7 +1535,7 @@
 	 */
 	microfind();
 
-	if (get_hwenv() == HW_XEN_HVM)
+	if ((get_hwenv() & HW_XEN_HVM) != 0)
 		update_default_path();
 #endif
 
@@ -1663,7 +1664,7 @@
 	 * Initialize a handle for the boot cpu - others will initialize
 	 * as they startup.  Do not do this if we know we are in an HVM domU.
 	 */
-	if ((get_hwenv() != HW_XEN_HVM) &&
+	if ((get_hwenv() & HW_XEN_HVM) == 0 &&
 	    (hdl = cmi_init(CMI_HDL_NATIVE, cmi_ntv_hwchipid(CPU),
 	    cmi_ntv_hwcoreid(CPU), cmi_ntv_hwstrandid(CPU))) != NULL &&
 	    is_x86_feature(x86_featureset, X86FSET_MCA)) {
@@ -2298,7 +2299,7 @@
 		 * Startup the memory scrubber.
 		 * XXPV	This should be running somewhere ..
 		 */
-		if (get_hwenv() != HW_XEN_HVM)
+		if ((get_hwenv() & HW_VIRTUAL) == 0)
 			memscrub_init();
 #endif
 	}
--- a/usr/src/uts/i86pc/os/timestamp.c	Sun Jun 09 14:38:37 2013 +0400
+++ b/usr/src/uts/i86pc/os/timestamp.c	Mon Jun 10 09:51:40 2013 -0700
@@ -22,6 +22,8 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ *
+ * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
  */
 
 #include <sys/types.h>
@@ -432,8 +434,7 @@
 	int hwtype;
 
 	hwtype = get_hwenv();
-	if (!tsc_master_slave_sync_needed || hwtype == HW_XEN_HVM ||
-	    hwtype == HW_VMWARE)
+	if (!tsc_master_slave_sync_needed || (hwtype & HW_VIRTUAL) != 0)
 		return;
 
 	flags = clear_int_flag();
@@ -512,8 +513,7 @@
 	int hwtype;
 
 	hwtype = get_hwenv();
-	if (!tsc_master_slave_sync_needed || hwtype == HW_XEN_HVM ||
-	    hwtype == HW_VMWARE)
+	if (!tsc_master_slave_sync_needed || (hwtype & HW_VIRTUAL) != 0)
 		return;
 
 	flags = clear_int_flag();
--- a/usr/src/uts/intel/ia32/ml/i86_subr.s	Sun Jun 09 14:38:37 2013 +0400
+++ b/usr/src/uts/intel/ia32/ml/i86_subr.s	Mon Jun 10 09:51:40 2013 -0700
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
  */
 
 /*
@@ -4339,62 +4340,58 @@
 #endif /* __lint */
 
 /*
- * This is how VMware lets the guests figure that they are running
- * on top of VMWare platform :
- * Write 0xA in the ECX register and put the I/O port address value of
- * 0x564D5868 in the EAX register. Then read a word from port 0x5658.
- * If VMWare is installed than this code will be executed correctly and
- * the EBX register will contain the same I/O port address value of 0x564D5868.
- * If VMWare is not installed then OS will return an exception on port access. 
+ * VMware implements an I/O port that programs can query to detect if software
+ * is running in a VMware hypervisor. This hypervisor port behaves differently
+ * depending on magic values in certain registers and modifies some registers
+ * as a side effect.
+ *
+ * References: http://kb.vmware.com/kb/1009458 
  */
+
 #if defined(__lint)
 
-int
-vmware_platform(void) { return (1); }
+/* ARGSUSED */
+void
+vmware_port(int cmd, uint32_t *regs) { return; }
 
 #else
 
 #if defined(__amd64)
 
-	ENTRY(vmware_platform)
+	ENTRY(vmware_port)
 	pushq	%rbx
-	xorl	%ebx, %ebx
-	movl	$0x564d5868, %eax
-	movl	$0xa, %ecx
-	movl	$0x5658, %edx
+	movl	$VMWARE_HVMAGIC, %eax
+	movl	$0xffffffff, %ebx
+	movl	%edi, %ecx
+	movl	$VMWARE_HVPORT, %edx
 	inl	(%dx)
-	movl	$0x564d5868, %ecx
-	xorl	%eax, %eax
-	cmpl	%ecx, %ebx
-	jne	1f
-	incl	%eax
-1:
+	movl	%eax, (%rsi)
+	movl	%ebx, 4(%rsi)
+	movl	%ecx, 8(%rsi)
+	movl	%edx, 12(%rsi)
 	popq	%rbx
 	ret
-	SET_SIZE(vmware_platform)
+	SET_SIZE(vmware_port)
 
 #elif defined(__i386)
 
-	ENTRY(vmware_platform)
+	ENTRY(vmware_port)
 	pushl	%ebx
-	pushl	%ecx
-	pushl	%edx
-	xorl	%ebx, %ebx
-	movl	$0x564d5868, %eax
-	movl	$0xa, %ecx
-	movl	$0x5658, %edx
+	pushl	%esi
+	movl	$VMWARE_HVMAGIC, %eax
+	movl	$0xffffffff, %ebx
+	movl	12(%esp), %ecx
+	movl	$VMWARE_HVPORT, %edx
 	inl	(%dx)
-	movl	$0x564d5868, %ecx
-	xorl	%eax, %eax
-	cmpl	%ecx, %ebx
-	jne	1f
-	incl	%eax
-1:
-	popl	%edx
-	popl	%ecx
+	movl	16(%esp), %esi
+	movl	%eax, (%esi)
+	movl	%ebx, 4(%esi)
+	movl	%ecx, 8(%esi)
+	movl	%edx, 12(%esi)
+	popl	%esi
 	popl	%ebx
 	ret
-	SET_SIZE(vmware_platform)
+	SET_SIZE(vmware_port)
 
 #endif /* __i386 */
 #endif /* __lint */
--- a/usr/src/uts/intel/sys/x86_archext.h	Sun Jun 09 14:38:37 2013 +0400
+++ b/usr/src/uts/intel/sys/x86_archext.h	Mon Jun 10 09:51:40 2013 -0700
@@ -21,6 +21,7 @@
 /*
  * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2011 by Delphix. All rights reserved.
+ * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
  */
 /*
  * Copyright (c) 2010, Intel Corporation.
@@ -123,6 +124,7 @@
 #define	CPUID_INTC_ECX_AVX	0x10000000	/* AVX supported */
 #define	CPUID_INTC_ECX_F16C	0x20000000	/* F16C supported */
 #define	CPUID_INTC_ECX_RDRAND	0x40000000	/* RDRAND supported */
+#define	CPUID_INTC_ECX_HV	0x80000000	/* Hypervisor */
 
 #define	FMT_CPUID_INTC_ECX					\
 	"\20"							\
@@ -759,7 +761,7 @@
 extern int cpuid_arat_supported(void);
 extern int cpuid_iepb_supported(struct cpu *);
 extern int cpuid_deadline_tsc_supported(void);
-extern int vmware_platform(void);
+extern void vmware_port(int, uint32_t *);
 #endif
 
 struct cpu_ucode_info;
@@ -820,16 +822,37 @@
 extern void xsave_setup_msr(struct cpu *);
 
 /*
+ * Hypervisor signatures
+ */
+#define	HVSIG_XEN_HVM	"XenVMMXenVMM"
+#define	HVSIG_VMWARE	"VMwareVMware"
+#define	HVSIG_KVM	"KVMKVMKVM"
+#define	HVSIG_MICROSOFT	"Microsoft Hv"
+
+/*
  * Defined hardware environments
  */
-#define	HW_NATIVE	0x00	/* Running on bare metal */
-#define	HW_XEN_PV	0x01	/* Running on Xen Hypervisor paravirutualized */
-#define	HW_XEN_HVM	0x02	/* Running on Xen hypervisor HVM */
-#define	HW_VMWARE	0x03	/* Running on VMware hypervisor */
+#define	HW_NATIVE	(1 << 0)	/* Running on bare metal */
+#define	HW_XEN_PV	(1 << 1)	/* Running on Xen PVM */
+
+#define	HW_XEN_HVM	(1 << 2)	/* Running on Xen HVM */
+#define	HW_VMWARE	(1 << 3)	/* Running on VMware hypervisor */
+#define	HW_KVM		(1 << 4)	/* Running on KVM hypervisor */
+#define	HW_MICROSOFT	(1 << 5)	/* Running on Microsoft hypervisor */
+
+#define	HW_VIRTUAL	(HW_XEN_HVM | HW_VMWARE | HW_KVM | HW_MICROSOFT)
 
 #endif	/* _KERNEL */
 
-#endif
+#endif	/* !_ASM */
+
+/*
+ * VMware hypervisor related defines
+ */
+#define	VMWARE_HVMAGIC		0x564d5868
+#define	VMWARE_HVPORT		0x5658
+#define	VMWARE_HVCMD_GETVERSION	0x0a
+#define	VMWARE_HVCMD_GETTSCFREQ	0x2d
 
 #ifdef	__cplusplus
 }