Index: sys/x86/x86/tsc.c =================================================================== --- sys/x86/x86/tsc.c (revision 221188) +++ sys/x86/x86/tsc.c (working copy) @@ -32,6 +32,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -90,6 +91,77 @@ 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) + printf("Hypervisor detected (\"%s\")\n", hv_sig); + if (strncmp(hv_sig, "VMwareVMware", sizeof(hv_sig) - 1) != 0) + return (0); + } else { + p = getenv("smbios.system.serial"); + if (p == NULL) + return (0); + if (strncmp(p, "VMware-", 7) != 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; +#endif + return (1); +} + static void tsc_freq_intel(void) { @@ -102,7 +174,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 +227,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 +271,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();