summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2014-10-28 19:17:44 +0000
committerjhb <jhb@FreeBSD.org>2014-10-28 19:17:44 +0000
commitd47eb7d2d46dabce3135bac7e98b771b40aff97d (patch)
tree396338e21acadb31ec69556dca490e2010a46862 /sys
parent7dd1b73c3843393e02fb47a35ad7b9bf4dac7c2f (diff)
downloadFreeBSD-src-d47eb7d2d46dabce3135bac7e98b771b40aff97d.zip
FreeBSD-src-d47eb7d2d46dabce3135bac7e98b771b40aff97d.tar.gz
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
Diffstat (limited to 'sys')
-rw-r--r--sys/amd64/include/md_var.h2
-rw-r--r--sys/i386/include/md_var.h2
-rw-r--r--sys/kern/subr_param.c67
-rw-r--r--sys/sys/systm.h2
-rw-r--r--sys/x86/include/vmware.h47
-rw-r--r--sys/x86/x86/identcpu.c132
-rw-r--r--sys/x86/x86/tsc.c69
7 files changed, 187 insertions, 134 deletions
diff --git a/sys/amd64/include/md_var.h b/sys/amd64/include/md_var.h
index e5e78f6..96da886 100644
--- a/sys/amd64/include/md_var.h
+++ b/sys/amd64/include/md_var.h
@@ -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;
diff --git a/sys/i386/include/md_var.h b/sys/i386/include/md_var.h
index 2425fab..c406cc1 100644
--- a/sys/i386/include/md_var.h
+++ b/sys/i386/include/md_var.h
@@ -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;
diff --git a/sys/kern/subr_param.c b/sys/kern/subr_param.c
index d16f2dc..95f3250 100644
--- a/sys/kern/subr_param.c
+++ b/sys/kern/subr_param.c
@@ -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, CTLFLAG_RWTUN | CTLFLAG_NOFETCH, &sgrows
"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_names[] = {
"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)
diff --git a/sys/sys/systm.h b/sys/sys/systm.h
index 6e5ee61..6f2e86d 100644
--- a/sys/sys/systm.h
+++ b/sys/sys/systm.h
@@ -73,7 +73,7 @@ extern int vm_guest; /* Running as virtual machine guest? */
* 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);
diff --git a/sys/x86/include/vmware.h b/sys/x86/include/vmware.h
new file mode 100644
index 0000000..c72f48d
--- /dev/null
+++ b/sys/x86/include/vmware.h
@@ -0,0 +1,47 @@
+/*-
+ * Copyright (c) 2011-2014 Jung-uk Kim <jkim@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_ */
diff --git a/sys/x86/x86/identcpu.c b/sys/x86/x86/identcpu.c
index 87a5664..bae430a 100644
--- a/sys/x86/x86/identcpu.c
+++ b/sys/x86/x86/identcpu.c
@@ -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);
+}
diff --git a/sys/x86/x86/tsc.c b/sys/x86/x86/tsc.c
index b78e3c9..4ca574e 100644
--- a/sys/x86/x86/tsc.c
+++ b/sys/x86/x86/tsc.c
@@ -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_timecounter = {
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:
OpenPOWER on IntegriCloud