aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel
diff options
context:
space:
mode:
authorAlok Kataria <akataria@vmware.com>2008-10-27 13:41:46 -0400
committerH. Peter Anvin <hpa@zytor.com>2008-11-01 21:57:08 -0400
commit88b094fb8d4fe43b7025ea8d487059e8813e02cd (patch)
treefa2fc5386aeb1d62d3901c9a39c8fbb2b6be43d2 /arch/x86/kernel
parent49ab56ac6e1b907b7dadb72a4012460359feaf0e (diff)
x86: Hypervisor detection and get tsc_freq from hypervisor
Impact: Changes timebase calibration on Vmware. v3->v2 : Abstract the hypervisor detection and feature (tsc_freq) request behind a hypervisor.c file v2->v1 : Add a x86_hyper_vendor field to the cpuinfo_x86 structure. This avoids multiple calls to the hypervisor detection function. This patch adds function to detect if we are running under VMware. The current way to check if we are on VMware is following, # check if "hypervisor present bit" is set, if so read the 0x40000000 cpuid leaf and check for "VMwareVMware" signature. # if the above fails, check the DMI vendors name for "VMware" string if we find one we query the VMware hypervisor port to check if we are under VMware. The DMI + "VMware hypervisor port check" is needed for older VMware products, which don't implement the hypervisor signature cpuid leaf. Also note that since we are checking for the DMI signature the hypervisor port should never be accessed on native hardware. This patch also adds a hypervisor_get_tsc_freq function, instead of calibrating the frequency which can be error prone in virtualized environment, we ask the hypervisor for it. We get the frequency from the hypervisor by accessing the hypervisor port if we are running on VMware. Other hypervisors too can add code to the generic routine to get frequency on their platform. Signed-off-by: Alok N Kataria <akataria@vmware.com> Signed-off-by: Dan Hecht <dhecht@vmware.com> Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Diffstat (limited to 'arch/x86/kernel')
-rw-r--r--arch/x86/kernel/cpu/Makefile1
-rw-r--r--arch/x86/kernel/cpu/common.c2
-rw-r--r--arch/x86/kernel/cpu/hypervisor.c48
-rw-r--r--arch/x86/kernel/cpu/vmware.c88
-rw-r--r--arch/x86/kernel/setup.c7
-rw-r--r--arch/x86/kernel/tsc.c9
6 files changed, 154 insertions, 1 deletions
diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile
index 82ec6075c057..a5c04e88777e 100644
--- a/arch/x86/kernel/cpu/Makefile
+++ b/arch/x86/kernel/cpu/Makefile
@@ -4,6 +4,7 @@
4 4
5obj-y := intel_cacheinfo.o addon_cpuid_features.o 5obj-y := intel_cacheinfo.o addon_cpuid_features.o
6obj-y += proc.o capflags.o powerflags.o common.o 6obj-y += proc.o capflags.o powerflags.o common.o
7obj-y += vmware.o hypervisor.o
7 8
8obj-$(CONFIG_X86_32) += bugs.o cmpxchg.o 9obj-$(CONFIG_X86_32) += bugs.o cmpxchg.o
9obj-$(CONFIG_X86_64) += bugs_64.o 10obj-$(CONFIG_X86_64) += bugs_64.o
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index b9c9ea0217a9..b88595c36254 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -36,6 +36,7 @@
36#include <asm/proto.h> 36#include <asm/proto.h>
37#include <asm/sections.h> 37#include <asm/sections.h>
38#include <asm/setup.h> 38#include <asm/setup.h>
39#include <asm/hypervisor.h>
39 40
40#include "cpu.h" 41#include "cpu.h"
41 42
@@ -703,6 +704,7 @@ static void __cpuinit identify_cpu(struct cpuinfo_x86 *c)
703 detect_ht(c); 704 detect_ht(c);
704#endif 705#endif
705 706
707 init_hypervisor(c);
706 /* 708 /*
707 * On SMP, boot_cpu_data holds the common feature set between 709 * On SMP, boot_cpu_data holds the common feature set between
708 * all CPUs; so make sure that we indicate which features are 710 * all CPUs; so make sure that we indicate which features are
diff --git a/arch/x86/kernel/cpu/hypervisor.c b/arch/x86/kernel/cpu/hypervisor.c
new file mode 100644
index 000000000000..7bd55064ffe9
--- /dev/null
+++ b/arch/x86/kernel/cpu/hypervisor.c
@@ -0,0 +1,48 @@
1/*
2 * Common hypervisor code
3 *
4 * Copyright (C) 2008, VMware, Inc.
5 * Author : Alok N Kataria <akataria@vmware.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
15 * NON INFRINGEMENT. See the GNU General Public License for more
16 * details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 */
23
24#include <asm/processor.h>
25#include <asm/vmware.h>
26
27static inline void __cpuinit
28detect_hypervisor_vendor(struct cpuinfo_x86 *c)
29{
30 if (vmware_platform()) {
31 c->x86_hyper_vendor = X86_HYPER_VENDOR_VMWARE;
32 } else {
33 c->x86_hyper_vendor = X86_HYPER_VENDOR_NONE;
34 }
35}
36
37unsigned long get_hypervisor_tsc_freq(void)
38{
39 if (boot_cpu_data.x86_hyper_vendor == X86_HYPER_VENDOR_VMWARE)
40 return vmware_get_tsc_khz();
41 return 0;
42}
43
44void __cpuinit init_hypervisor(struct cpuinfo_x86 *c)
45{
46 detect_hypervisor_vendor(c);
47}
48
diff --git a/arch/x86/kernel/cpu/vmware.c b/arch/x86/kernel/cpu/vmware.c
new file mode 100644
index 000000000000..d5d1b75a4b77
--- /dev/null
+++ b/arch/x86/kernel/cpu/vmware.c
@@ -0,0 +1,88 @@
1/*
2 * VMware Detection code.
3 *
4 * Copyright (C) 2008, VMware, Inc.
5 * Author : Alok N Kataria <akataria@vmware.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
15 * NON INFRINGEMENT. See the GNU General Public License for more
16 * details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 */
23
24#include <linux/dmi.h>
25#include <asm/div64.h>
26
27#define CPUID_VMWARE_INFO_LEAF 0x40000000
28#define VMWARE_HYPERVISOR_MAGIC 0x564D5868
29#define VMWARE_HYPERVISOR_PORT 0x5658
30
31#define VMWARE_PORT_CMD_GETVERSION 10
32#define VMWARE_PORT_CMD_GETHZ 45
33
34#define VMWARE_PORT(cmd, eax, ebx, ecx, edx) \
35 __asm__("inl (%%dx)" : \
36 "=a"(eax), "=c"(ecx), "=d"(edx), "=b"(ebx) : \
37 "0"(VMWARE_HYPERVISOR_MAGIC), \
38 "1"(VMWARE_PORT_CMD_##cmd), \
39 "2"(VMWARE_HYPERVISOR_PORT), "3"(0) : \
40 "memory");
41
42static inline int __vmware_platform(void)
43{
44 uint32_t eax, ebx, ecx, edx;
45 VMWARE_PORT(GETVERSION, eax, ebx, ecx, edx);
46 return eax != (uint32_t)-1 && ebx == VMWARE_HYPERVISOR_MAGIC;
47}
48
49static unsigned long __vmware_get_tsc_khz(void)
50{
51 uint64_t tsc_hz;
52 uint32_t eax, ebx, ecx, edx;
53
54 VMWARE_PORT(GETHZ, eax, ebx, ecx, edx);
55
56 if (eax == (uint32_t)-1)
57 return 0;
58 tsc_hz = eax | (((uint64_t)ebx) << 32);
59 do_div(tsc_hz, 1000);
60 BUG_ON(tsc_hz >> 32);
61 return tsc_hz;
62}
63
64int vmware_platform(void)
65{
66 if (cpu_has_hypervisor) {
67 unsigned int eax, ebx, ecx, edx;
68 char hyper_vendor_id[13];
69
70 cpuid(CPUID_VMWARE_INFO_LEAF, &eax, &ebx, &ecx, &edx);
71 memcpy(hyper_vendor_id + 0, &ebx, 4);
72 memcpy(hyper_vendor_id + 4, &ecx, 4);
73 memcpy(hyper_vendor_id + 8, &edx, 4);
74 hyper_vendor_id[12] = '\0';
75 if (!strcmp(hyper_vendor_id, "VMwareVMware"))
76 return 1;
77 } else if (dmi_available && dmi_name_in_vendors("VMware") &&
78 __vmware_platform())
79 return 1;
80
81 return 0;
82}
83
84unsigned long vmware_get_tsc_khz(void)
85{
86 BUG_ON(!vmware_platform());
87 return __vmware_get_tsc_khz();
88}
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index 0fa6790c1dd3..f44dadfb32cf 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -98,6 +98,7 @@
98 98
99#include <mach_apic.h> 99#include <mach_apic.h>
100#include <asm/paravirt.h> 100#include <asm/paravirt.h>
101#include <asm/hypervisor.h>
101 102
102#include <asm/percpu.h> 103#include <asm/percpu.h>
103#include <asm/topology.h> 104#include <asm/topology.h>
@@ -909,6 +910,12 @@ void __init setup_arch(char **cmdline_p)
909 910
910 dmi_check_system(bad_bios_dmi_table); 911 dmi_check_system(bad_bios_dmi_table);
911 912
913 /*
914 * VMware detection requires dmi to be available, so this
915 * needs to be done after dmi_scan_machine, for the BP.
916 */
917 init_hypervisor(&boot_cpu_data);
918
912#ifdef CONFIG_X86_32 919#ifdef CONFIG_X86_32
913 probe_roms(); 920 probe_roms();
914#endif 921#endif
diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
index 62348e4fd8d1..6dbf0bcb44a8 100644
--- a/arch/x86/kernel/tsc.c
+++ b/arch/x86/kernel/tsc.c
@@ -15,6 +15,7 @@
15#include <asm/vgtod.h> 15#include <asm/vgtod.h>
16#include <asm/time.h> 16#include <asm/time.h>
17#include <asm/delay.h> 17#include <asm/delay.h>
18#include <asm/hypervisor.h>
18 19
19unsigned int cpu_khz; /* TSC clocks / usec, not used here */ 20unsigned int cpu_khz; /* TSC clocks / usec, not used here */
20EXPORT_SYMBOL(cpu_khz); 21EXPORT_SYMBOL(cpu_khz);
@@ -352,9 +353,15 @@ unsigned long native_calibrate_tsc(void)
352{ 353{
353 u64 tsc1, tsc2, delta, ref1, ref2; 354 u64 tsc1, tsc2, delta, ref1, ref2;
354 unsigned long tsc_pit_min = ULONG_MAX, tsc_ref_min = ULONG_MAX; 355 unsigned long tsc_pit_min = ULONG_MAX, tsc_ref_min = ULONG_MAX;
355 unsigned long flags, latch, ms, fast_calibrate; 356 unsigned long flags, latch, ms, fast_calibrate, tsc_khz;
356 int hpet = is_hpet_enabled(), i, loopmin; 357 int hpet = is_hpet_enabled(), i, loopmin;
357 358
359 tsc_khz = get_hypervisor_tsc_freq();
360 if (tsc_khz) {
361 printk(KERN_INFO "TSC: Frequency read from the hypervisor\n");
362 return tsc_khz;
363 }
364
358 local_irq_save(flags); 365 local_irq_save(flags);
359 fast_calibrate = quick_pit_calibrate(); 366 fast_calibrate = quick_pit_calibrate();
360 local_irq_restore(flags); 367 local_irq_restore(flags);