From 9809511c44729704d6f683faf47bd9a7f506b8dc Mon Sep 17 00:00:00 2001 From: jhb Date: Tue, 10 Feb 2015 16:34:42 +0000 Subject: MFC 273800: 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. --- sys/amd64/include/md_var.h | 2 + sys/i386/include/md_var.h | 2 + sys/kern/subr_param.c | 67 +++-------------------- sys/sys/systm.h | 2 +- sys/x86/include/vmware.h | 47 ++++++++++++++++ sys/x86/x86/identcpu.c | 132 +++++++++++++++++++++++++++++++++++++++++---- sys/x86/x86/tsc.c | 69 ++---------------------- 7 files changed, 187 insertions(+), 134 deletions(-) create mode 100644 sys/x86/include/vmware.h (limited to 'sys') diff --git a/sys/amd64/include/md_var.h b/sys/amd64/include/md_var.h index a7bef6a..44b4465 100644 --- a/sys/amd64/include/md_var.h +++ b/sys/amd64/include/md_var.h @@ -63,6 +63,8 @@ extern u_int cpu_mon_min_size; extern u_int cpu_mon_max_size; extern u_int cpu_maxphyaddr; 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 ec9d2ce..b6b3326 100644 --- a/sys/i386/include/md_var.h +++ b/sys/i386/include/md_var.h @@ -66,6 +66,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 afd3eac..25927f7 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_RW | CTLFLAG_TUN, &sgrowsiz, 0, "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 = 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 = 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 0542535..006532d 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 + * 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 8bdc14e..972490a 100644 --- a/sys/x86/x86/identcpu.c +++ b/sys/x86/x86/identcpu.c @@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -63,6 +64,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #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 = 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 = 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 = 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(); /* @@ -2049,3 +2153,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 b80e0e8..e96e405 100644 --- a/sys/x86/x86/tsc.c +++ b/sys/x86/x86/tsc.c @@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include "cpufreq_if.h" @@ -108,72 +109,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, ®s[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 = 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; @@ -183,7 +123,6 @@ tsc_freq_vmware(void) tsc_freq = regs[0] | ((uint64_t)regs[1] << 32); } tsc_is_invariant = 1; - return (1); } static void @@ -267,8 +206,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: -- cgit v1.1