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 <j...@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:
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to