diff options
author | jkim <jkim@FreeBSD.org> | 2011-04-29 18:20:12 +0000 |
---|---|---|
committer | jkim <jkim@FreeBSD.org> | 2011-04-29 18:20:12 +0000 |
commit | 3b56923bc2454db20d56c7417fb33cf7941e3ac0 (patch) | |
tree | 5b47beac857c1c4c4e523ffc68eb762dbbe8fcc6 /sys/x86 | |
parent | 12fa2ffdffc903cbd285fc6a7cef301385de0676 (diff) | |
download | FreeBSD-src-3b56923bc2454db20d56c7417fb33cf7941e3ac0.zip FreeBSD-src-3b56923bc2454db20d56c7417fb33cf7941e3ac0.tar.gz |
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.
Diffstat (limited to 'sys/x86')
-rw-r--r-- | sys/x86/x86/tsc.c | 118 |
1 files changed, 101 insertions, 17 deletions
diff --git a/sys/x86/x86/tsc.c b/sys/x86/x86/tsc.c index e91522c..0b7510c 100644 --- a/sys/x86/x86/tsc.c +++ b/sys/x86/x86/tsc.c @@ -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, ®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; + } 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(); |