svn commit: r273800 - in head/sys: amd64/include i386/include kern sys x86/include x86/x86

John Baldwin jhb at FreeBSD.org
Tue Oct 28 19:17:46 UTC 2014


Author: jhb
Date: Tue Oct 28 19:17:44 2014
New Revision: 273800
URL: https://svnweb.freebsd.org/changeset/base/273800

Log:
  Rework virtual machine hypervisor detection.
  - Move the existing code to x86/x86/identcpu.c since it is x86-specific.
  - If the CPUID2_HV flag is set, assume a hypervisor is present and query
    the 0x40000000 leaf to determine the hypervisor vendor ID.  Export the
    vendor ID and the highest supported hypervisor CPUID leaf via
    hv_vendor[] and hv_high variables, respectively.  The hv_vendor[]
    array is also exported via the hw.hv_vendor sysctl.
  - Merge the VMWare detection code from tsc.c into the new probe in
    identcpu.c.  Add a VM_GUEST_VMWARE to identify vmware and use that in
    the TSC code to identify VMWare.
  
  Differential Revision:	https://reviews.freebsd.org/D1010
  Reviewed by:	delphij, jkim, neel

Added:
  head/sys/x86/include/vmware.h   (contents, props changed)
Modified:
  head/sys/amd64/include/md_var.h
  head/sys/i386/include/md_var.h
  head/sys/kern/subr_param.c
  head/sys/sys/systm.h
  head/sys/x86/x86/identcpu.c
  head/sys/x86/x86/tsc.c

Modified: head/sys/amd64/include/md_var.h
==============================================================================
--- head/sys/amd64/include/md_var.h	Tue Oct 28 18:33:59 2014	(r273799)
+++ head/sys/amd64/include/md_var.h	Tue Oct 28 19:17:44 2014	(r273800)
@@ -62,6 +62,8 @@ extern	u_int	cpu_mon_mwait_flags;
 extern	u_int	cpu_mon_min_size;
 extern	u_int	cpu_mon_max_size;
 extern	char	ctx_switch_xsave[];
+extern	u_int	hv_high;
+extern	char	hv_vendor[];
 extern	char	kstack[];
 extern	char	sigcode[];
 extern	int	szsigcode;

Modified: head/sys/i386/include/md_var.h
==============================================================================
--- head/sys/i386/include/md_var.h	Tue Oct 28 18:33:59 2014	(r273799)
+++ head/sys/i386/include/md_var.h	Tue Oct 28 19:17:44 2014	(r273800)
@@ -64,6 +64,8 @@ extern	u_int	cyrix_did;
 #if defined(I586_CPU) && !defined(NO_F00F_HACK)
 extern	int	has_f00f_bug;
 #endif
+extern	u_int	hv_high;
+extern	char	hv_vendor[];
 extern	char	kstack[];
 extern	char	sigcode[];
 extern	int	szsigcode;

Modified: head/sys/kern/subr_param.c
==============================================================================
--- head/sys/kern/subr_param.c	Tue Oct 28 18:33:59 2014	(r273799)
+++ head/sys/kern/subr_param.c	Tue Oct 28 19:17:44 2014	(r273800)
@@ -99,7 +99,11 @@ pid_t	pid_max = PID_MAX;
 long	maxswzone;			/* max swmeta KVA storage */
 long	maxbcache;			/* max buffer cache KVA storage */
 long	maxpipekva;			/* Limit on pipe KVA */
-int 	vm_guest;			/* Running as virtual machine guest? */
+#ifdef XEN
+int	vm_guest = VM_GUEST_XEN;
+#else
+int	vm_guest = VM_GUEST_NO;		/* Running as virtual machine guest? */
+#endif
 u_long	maxtsiz;			/* max text size */
 u_long	dfldsiz;			/* initial data size limit */
 u_long	maxdsiz;			/* max data size */
@@ -136,7 +140,7 @@ SYSCTL_ULONG(_kern, OID_AUTO, sgrowsiz, 
     "Amount to grow stack on a stack fault");
 SYSCTL_PROC(_kern, OID_AUTO, vm_guest, CTLFLAG_RD | CTLTYPE_STRING,
     NULL, 0, sysctl_kern_vm_guest, "A",
-    "Virtual machine guest detected? (none|generic|xen)");
+    "Virtual machine guest detected?");
 
 /*
  * These have to be allocated somewhere; allocating
@@ -154,73 +158,18 @@ static const char *const vm_guest_sysctl
 	"generic",
 	"xen",
 	"hv",
+	"vmware",
 	NULL
 };
 CTASSERT(nitems(vm_guest_sysctl_names) - 1 == VM_LAST);
 
-#ifndef XEN
-static const char *const vm_bnames[] = {
-	"QEMU",				/* QEMU */
-	"Plex86",			/* Plex86 */
-	"Bochs",			/* Bochs */
-	"Xen",				/* Xen */
-	"BHYVE",			/* bhyve */
-	"Seabios",			/* KVM */
-	NULL
-};
-
-static const char *const vm_pnames[] = {
-	"VMware Virtual Platform",	/* VMWare VM */
-	"Virtual Machine",		/* Microsoft VirtualPC */
-	"VirtualBox",			/* Sun xVM VirtualBox */
-	"Parallels Virtual Platform",	/* Parallels VM */
-	"KVM",				/* KVM */
-	NULL
-};
-
-
-/*
- * Detect known Virtual Machine hosts by inspecting the emulated BIOS.
- */
-static enum VM_GUEST
-detect_virtual(void)
-{
-	char *sysenv;
-	int i;
-
-	sysenv = kern_getenv("smbios.bios.vendor");
-	if (sysenv != NULL) {
-		for (i = 0; vm_bnames[i] != NULL; i++)
-			if (strcmp(sysenv, vm_bnames[i]) == 0) {
-				freeenv(sysenv);
-				return (VM_GUEST_VM);
-			}
-		freeenv(sysenv);
-	}
-	sysenv = kern_getenv("smbios.system.product");
-	if (sysenv != NULL) {
-		for (i = 0; vm_pnames[i] != NULL; i++)
-			if (strcmp(sysenv, vm_pnames[i]) == 0) {
-				freeenv(sysenv);
-				return (VM_GUEST_VM);
-			}
-		freeenv(sysenv);
-	}
-	return (VM_GUEST_NO);
-}
-#endif
-
 /*
  * Boot time overrides that are not scaled against main memory
  */
 void
 init_param1(void)
 {
-#ifndef XEN
-	vm_guest = detect_virtual();
-#else
-	vm_guest = VM_GUEST_XEN;
-#endif
+
 	hz = -1;
 	TUNABLE_INT_FETCH("kern.hz", &hz);
 	if (hz == -1)

Modified: head/sys/sys/systm.h
==============================================================================
--- head/sys/sys/systm.h	Tue Oct 28 18:33:59 2014	(r273799)
+++ head/sys/sys/systm.h	Tue Oct 28 19:17:44 2014	(r273800)
@@ -73,7 +73,7 @@ extern int vm_guest;		/* Running as virt
  * Keep in sync with vm_guest_sysctl_names[].
  */
 enum VM_GUEST { VM_GUEST_NO = 0, VM_GUEST_VM, VM_GUEST_XEN, VM_GUEST_HV,
-		VM_LAST };
+		VM_GUEST_VMWARE, VM_LAST };
 
 #if defined(WITNESS) || defined(INVARIANTS)
 void	kassert_panic(const char *fmt, ...)  __printflike(1, 2);

Added: head/sys/x86/include/vmware.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/x86/include/vmware.h	Tue Oct 28 19:17:44 2014	(r273800)
@@ -0,0 +1,47 @@
+/*-
+ * Copyright (c) 2011-2014 Jung-uk Kim <jkim at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _X86_VMWARE_H_
+#define	_X86_VMWARE_H_
+
+#define	VMW_HVMAGIC		0x564d5868
+#define	VMW_HVPORT		0x5658
+#define	VMW_HVCMD_GETVERSION	10
+#define	VMW_HVCMD_GETHZ		45
+
+static __inline void
+vmware_hvcall(u_int cmd, u_int *p)
+{
+
+	__asm __volatile("inl %w3, %0"
+	: "=a" (p[0]), "=b" (p[1]), "=c" (p[2]), "=d" (p[3])
+	: "0" (VMW_HVMAGIC), "1" (UINT_MAX), "2" (cmd), "3" (VMW_HVPORT)
+	: "memory");
+}
+
+#endif /* !_X86_VMWARE_H_ */

Modified: head/sys/x86/x86/identcpu.c
==============================================================================
--- head/sys/x86/x86/identcpu.c	Tue Oct 28 18:33:59 2014	(r273799)
+++ head/sys/x86/x86/identcpu.c	Tue Oct 28 19:17:44 2014	(r273800)
@@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/bus.h>
 #include <sys/cpu.h>
 #include <sys/eventhandler.h>
+#include <sys/limits.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
 #include <sys/sysctl.h>
@@ -63,6 +64,7 @@ __FBSDID("$FreeBSD$");
 
 #include <amd64/vmm/intel/vmx_controls.h>
 #include <x86/isa/icu.h>
+#include <x86/vmware.h>
 
 #ifdef __i386__
 #define	IDENTBLUE_CYRIX486	0
@@ -76,6 +78,7 @@ static u_int find_cpu_vendor_id(void);
 static void print_AMD_info(void);
 static void print_INTEL_info(void);
 static void print_INTEL_TLB(u_int data);
+static void print_hypervisor_info(void);
 static void print_svm_info(void);
 static void print_via_padlock_info(void);
 static void print_vmx_info(void);
@@ -120,6 +123,11 @@ static int hw_clockrate;
 SYSCTL_INT(_hw, OID_AUTO, clockrate, CTLFLAG_RD,
     &hw_clockrate, 0, "CPU instruction clock rate");
 
+u_int hv_high;
+char hv_vendor[16];
+SYSCTL_STRING(_hw, OID_AUTO, hv_vendor, CTLFLAG_RD, hv_vendor, 0,
+    "Hypervisor vendor");
+
 static eventhandler_tag tsc_post_tag;
 
 static char cpu_brand[48];
@@ -949,7 +957,6 @@ printcpuinfo(void)
 				if (tsc_perf_stat)
 					printf(", performance statistics");
 			}
-
 		}
 #ifdef __i386__
 	} else if (cpu_vendor_id == CPU_VENDOR_CYRIX) {
@@ -967,17 +974,18 @@ printcpuinfo(void)
 	if (*cpu_vendor || cpu_id)
 		printf("\n");
 
-	if (!bootverbose)
-		return;
-
-	if (cpu_vendor_id == CPU_VENDOR_AMD)
-		print_AMD_info();
-	else if (cpu_vendor_id == CPU_VENDOR_INTEL)
-		print_INTEL_info();
+	if (bootverbose) {
+		if (cpu_vendor_id == CPU_VENDOR_AMD)
+			print_AMD_info();
+		else if (cpu_vendor_id == CPU_VENDOR_INTEL)
+			print_INTEL_info();
 #ifdef __i386__
-	else if (cpu_vendor_id == CPU_VENDOR_TRANSMETA)
-		print_transmeta_info();
+		else if (cpu_vendor_id == CPU_VENDOR_TRANSMETA)
+			print_transmeta_info();
 #endif
+	}
+
+	print_hypervisor_info();
 }
 
 void
@@ -1182,6 +1190,99 @@ hook_tsc_freq(void *arg __unused)
 
 SYSINIT(hook_tsc_freq, SI_SUB_CONFIGURE, SI_ORDER_ANY, hook_tsc_freq, NULL);
 
+#ifndef XEN
+static const char *const vm_bnames[] = {
+	"QEMU",				/* QEMU */
+	"Plex86",			/* Plex86 */
+	"Bochs",			/* Bochs */
+	"Xen",				/* Xen */
+	"BHYVE",			/* bhyve */
+	"Seabios",			/* KVM */
+	NULL
+};
+
+static const char *const vm_pnames[] = {
+	"VMware Virtual Platform",	/* VMWare VM */
+	"Virtual Machine",		/* Microsoft VirtualPC */
+	"VirtualBox",			/* Sun xVM VirtualBox */
+	"Parallels Virtual Platform",	/* Parallels VM */
+	"KVM",				/* KVM */
+	NULL
+};
+
+static void
+identify_hypervisor(void)
+{
+	u_int regs[4];
+	char *p;
+	int i;
+
+	/*
+	 * [RFC] CPUID usage for interaction between Hypervisors and Linux.
+	 * http://lkml.org/lkml/2008/10/1/246
+	 *
+	 * KB1009458: Mechanisms to determine if software is running in
+	 * a VMware virtual machine
+	 * http://kb.vmware.com/kb/1009458
+	 */
+	if (cpu_feature2 & CPUID2_HV) {
+		vm_guest = VM_GUEST_VM;
+		do_cpuid(0x40000000, regs);
+		if (regs[0] >= 0x40000000) {
+			hv_high = regs[0];
+			((u_int *)&hv_vendor)[0] = regs[1];
+			((u_int *)&hv_vendor)[1] = regs[2];
+			((u_int *)&hv_vendor)[2] = regs[3];
+			hv_vendor[12] = '\0';
+			if (strcmp(hv_vendor, "VMwareVMware") == 0)
+				vm_guest = VM_GUEST_VMWARE;
+		}
+		return;
+	}
+
+	/*
+	 * Examine SMBIOS strings for older hypervisors.
+	 */
+	p = kern_getenv("smbios.system.serial");
+	if (p != NULL) {
+		if (strncmp(p, "VMware-", 7) == 0 || strncmp(p, "VMW", 3) == 0) {
+			vmware_hvcall(VMW_HVCMD_GETVERSION, regs);
+			if (regs[1] == VMW_HVMAGIC) {
+				vm_guest = VM_GUEST_VMWARE;			
+				freeenv(p);
+				return;
+			}
+		}
+		freeenv(p);
+	}
+
+	/*
+	 * XXX: Some of these entries may not be needed since they were
+	 * added to FreeBSD before the checks above.
+	 */
+	p = kern_getenv("smbios.bios.vendor");
+	if (p != NULL) {
+		for (i = 0; vm_bnames[i] != NULL; i++)
+			if (strcmp(p, vm_bnames[i]) == 0) {
+				vm_guest = VM_GUEST_VM;
+				freeenv(p);
+				return;
+			}
+		freeenv(p);
+	}
+	p = kern_getenv("smbios.system.product");
+	if (p != NULL) {
+		for (i = 0; vm_pnames[i] != NULL; i++)
+			if (strcmp(p, vm_pnames[i]) == 0) {
+				vm_guest = VM_GUEST_VM;
+				freeenv(p);
+				return;
+			}
+		freeenv(p);
+	}
+}
+#endif
+
 /*
  * Final stage of CPU identification.
  */
@@ -1213,6 +1314,9 @@ identify_cpu(void)
 	cpu_feature2 = regs[2];
 #endif
 
+#ifndef XEN
+	identify_hypervisor();
+#endif
 	cpu_vendor_id = find_cpu_vendor_id();
 
 	/*
@@ -2046,3 +2150,11 @@ print_vmx_info(void)
 		);
 	}
 }
+
+static void
+print_hypervisor_info(void)
+{
+
+	if (*hv_vendor)
+		printf("Hypervisor: Origin = \"%s\"\n", hv_vendor);
+}

Modified: head/sys/x86/x86/tsc.c
==============================================================================
--- head/sys/x86/x86/tsc.c	Tue Oct 28 18:33:59 2014	(r273799)
+++ head/sys/x86/x86/tsc.c	Tue Oct 28 19:17:44 2014	(r273800)
@@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$");
 #include <machine/cputypes.h>
 #include <machine/md_var.h>
 #include <machine/specialreg.h>
+#include <x86/vmware.h>
 
 #include "cpufreq_if.h"
 
@@ -102,72 +103,11 @@ static struct timecounter tsc_timecounte
 	800,			/* quality (adjusted in code) */
 };
 
-#define	VMW_HVMAGIC		0x564d5868
-#define	VMW_HVPORT		0x5658
-#define	VMW_HVCMD_GETVERSION	10
-#define	VMW_HVCMD_GETHZ		45
-
-static __inline void
-vmware_hvcall(u_int cmd, u_int *p)
-{
-
-	__asm __volatile("inl %w3, %0"
-	: "=a" (p[0]), "=b" (p[1]), "=c" (p[2]), "=d" (p[3])
-	: "0" (VMW_HVMAGIC), "1" (UINT_MAX), "2" (cmd), "3" (VMW_HVPORT)
-	: "memory");
-}
-
-static int
+static void
 tsc_freq_vmware(void)
 {
-	char hv_sig[13];
 	u_int regs[4];
-	char *p;
-	u_int hv_high;
-	int i;
 
-	/*
-	 * [RFC] CPUID usage for interaction between Hypervisors and Linux.
-	 * http://lkml.org/lkml/2008/10/1/246
-	 *
-	 * KB1009458: Mechanisms to determine if software is running in
-	 * a VMware virtual machine
-	 * http://kb.vmware.com/kb/1009458
-	 */
-	hv_high = 0;
-	if ((cpu_feature2 & CPUID2_HV) != 0) {
-		do_cpuid(0x40000000, regs);
-		hv_high = regs[0];
-		for (i = 1, p = hv_sig; i < 4; i++, p += sizeof(regs) / 4)
-			memcpy(p, &regs[i], sizeof(regs[i]));
-		*p = '\0';
-		if (bootverbose) {
-			/*
-			 * HV vendor	ID string
-			 * ------------+--------------
-			 * KVM		"KVMKVMKVM"
-			 * Microsoft	"Microsoft Hv"
-			 * VMware	"VMwareVMware"
-			 * Xen		"XenVMMXenVMM"
-			 */
-			printf("Hypervisor: Origin = \"%s\"\n", hv_sig);
-		}
-		if (strncmp(hv_sig, "VMwareVMware", 12) != 0)
-			return (0);
-	} else {
-		p = kern_getenv("smbios.system.serial");
-		if (p == NULL)
-			return (0);
-		if (strncmp(p, "VMware-", 7) != 0 &&
-		    strncmp(p, "VMW", 3) != 0) {
-			freeenv(p);
-			return (0);
-		}
-		freeenv(p);
-		vmware_hvcall(VMW_HVCMD_GETVERSION, regs);
-		if (regs[1] != VMW_HVMAGIC)
-			return (0);
-	}
 	if (hv_high >= 0x40000010) {
 		do_cpuid(0x40000010, regs);
 		tsc_freq = regs[0] * 1000;
@@ -177,7 +117,6 @@ tsc_freq_vmware(void)
 			tsc_freq = regs[0] | ((uint64_t)regs[1] << 32);
 	}
 	tsc_is_invariant = 1;
-	return (1);
 }
 
 static void
@@ -261,8 +200,10 @@ probe_tsc_freq(void)
 		}
 	}
 
-	if (tsc_freq_vmware())
+	if (vm_guest == VM_GUEST_VMWARE) {
+		tsc_freq_vmware();
 		return;
+	}
 
 	switch (cpu_vendor_id) {
 	case CPU_VENDOR_AMD:


More information about the svn-src-head mailing list