Detect VMware guest and set the TSC frequency as reported by the hypervisor.

VMware products virtualize TSC and it run at fixed frequency in so-called
"apparent time".  Although virtualized i8254 also runs in apparent time, TSC
calibration always gives slightly off frequency because of the complicated
timer emulation and lost-tick correction mechanism.
This commit is contained in:
Jung-uk Kim 2011-04-29 18:20:12 +00:00
parent e57539af23
commit 5da5812ba7
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=221214

View file

@ -32,6 +32,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/cpu.h>
#include <sys/limits.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
@ -90,6 +91,87 @@ 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 (%%dx)"
: "=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
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 = 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;
} else {
vmware_hvcall(VMW_HVCMD_GETHZ, regs);
if (regs[1] != UINT_MAX)
tsc_freq = regs[0] | ((uint64_t)regs[1] << 32);
}
tsc_is_invariant = 1;
#ifdef SMP
smp_tsc = 1; /* XXX */
#endif
return (1);
}
static void
tsc_freq_intel(void)
{
@ -102,7 +184,6 @@ tsc_freq_intel(void)
/*
* Intel Processor Identification and the CPUID Instruction
* Application Note 485.
*
* http://www.intel.com/assets/pdf/appnote/241618.pdf
*/
if (cpu_exthigh >= 0x80000004) {
@ -156,6 +237,25 @@ probe_tsc_freq(void)
u_int regs[4];
uint64_t tsc1, tsc2;
if (cpu_high >= 6) {
do_cpuid(6, regs);
if ((regs[2] & CPUID_PERF_STAT) != 0) {
/*
* XXX Some emulators expose host CPUID without actual
* support for these MSRs. We must test whether they
* really work.
*/
wrmsr(MSR_MPERF, 0);
wrmsr(MSR_APERF, 0);
DELAY(10);
if (rdmsr(MSR_MPERF) > 0 && rdmsr(MSR_APERF) > 0)
tsc_perf_stat = 1;
}
}
if (tsc_freq_vmware())
return;
switch (cpu_vendor_id) {
case CPU_VENDOR_AMD:
if ((amd_pminfo & AMDPM_TSC_INVARIANT) != 0 ||
@ -181,22 +281,6 @@ probe_tsc_freq(void)
break;
}
if (cpu_high >= 6) {
do_cpuid(6, regs);
if ((regs[2] & CPUID_PERF_STAT) != 0) {
/*
* XXX Some emulators expose host CPUID without actual
* support for these MSRs. We must test whether they
* really work.
*/
wrmsr(MSR_MPERF, 0);
wrmsr(MSR_APERF, 0);
DELAY(10);
if (rdmsr(MSR_MPERF) > 0 && rdmsr(MSR_APERF) > 0)
tsc_perf_stat = 1;
}
}
if (tsc_skip_calibration) {
if (cpu_vendor_id == CPU_VENDOR_INTEL)
tsc_freq_intel();