summaryrefslogtreecommitdiffstats
path: root/sys/x86
diff options
context:
space:
mode:
authorjkim <jkim@FreeBSD.org>2011-04-29 18:20:12 +0000
committerjkim <jkim@FreeBSD.org>2011-04-29 18:20:12 +0000
commit3b56923bc2454db20d56c7417fb33cf7941e3ac0 (patch)
tree5b47beac857c1c4c4e523ffc68eb762dbbe8fcc6 /sys/x86
parent12fa2ffdffc903cbd285fc6a7cef301385de0676 (diff)
downloadFreeBSD-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.c118
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, &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();
OpenPOWER on IntegriCloud