mirror of
https://github.com/opnsense/src.git
synced 2026-04-15 14:29:58 -04:00
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
This commit is contained in:
parent
8839e0e9f3
commit
01e1933dcc
7 changed files with 187 additions and 134 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
47
sys/x86/include/vmware.h
Normal file
47
sys/x86/include/vmware.h
Normal file
|
|
@ -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_ */
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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, ®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 = 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:
|
||||
|
|
|
|||
Loading…
Reference in a new issue