1 From: Alok Kataria <akataria@vmware.com>
2 Subject: x86: Hypervisor detection and get tsc_freq from hypervisor
6 Impact: Changes timebase calibration on Vmware.
8 v3->v2 : Abstract the hypervisor detection and feature (tsc_freq) request
9 behind a hypervisor.c file
10 v2->v1 : Add a x86_hyper_vendor field to the cpuinfo_x86 structure.
11 This avoids multiple calls to the hypervisor detection function.
13 This patch adds function to detect if we are running under VMware.
14 The current way to check if we are on VMware is following,
15 # check if "hypervisor present bit" is set, if so read the 0x40000000
16 cpuid leaf and check for "VMwareVMware" signature.
17 # if the above fails, check the DMI vendors name for "VMware" string
18 if we find one we query the VMware hypervisor port to check if we are
21 The DMI + "VMware hypervisor port check" is needed for older VMware products,
22 which don't implement the hypervisor signature cpuid leaf.
23 Also note that since we are checking for the DMI signature the hypervisor
24 port should never be accessed on native hardware.
26 This patch also adds a hypervisor_get_tsc_freq function, instead of
27 calibrating the frequency which can be error prone in virtualized
28 environment, we ask the hypervisor for it. We get the frequency from
29 the hypervisor by accessing the hypervisor port if we are running on VMware.
30 Other hypervisors too can add code to the generic routine to get frequency on
33 Signed-off-by: Alok N Kataria <akataria@vmware.com>
34 Signed-off-by: Dan Hecht <dhecht@vmware.com>
35 Signed-off-by: H. Peter Anvin <hpa@zytor.com>
36 Signed-off-by: Takashi Iwai <tiwai@suse.de>
40 arch/x86/kernel/cpu/Makefile | 1
41 arch/x86/kernel/cpu/common.c | 2
42 arch/x86/kernel/cpu/common_64.c | 2
43 arch/x86/kernel/cpu/hypervisor.c | 48 +++++++++++++++++++++
44 arch/x86/kernel/cpu/vmware.c | 88 +++++++++++++++++++++++++++++++++++++++
45 arch/x86/kernel/setup.c | 7 +++
46 arch/x86/kernel/tsc.c | 9 +++
47 include/asm-x86/hypervisor.h | 26 +++++++++++
48 include/asm-x86/processor.h | 4 +
49 include/asm-x86/vmware.h | 26 +++++++++++
50 10 files changed, 212 insertions(+), 1 deletion(-)
51 create mode 100644 arch/x86/kernel/cpu/hypervisor.c
52 create mode 100644 arch/x86/kernel/cpu/vmware.c
53 create mode 100644 include/asm-x86/hypervisor.h
54 create mode 100644 include/asm-x86/vmware.h
57 --- a/arch/x86/kernel/cpu/Makefile
58 +++ b/arch/x86/kernel/cpu/Makefile
61 obj-y := intel_cacheinfo.o addon_cpuid_features.o
62 obj-y += proc.o feature_names.o
63 +obj-y += vmware.o hypervisor.o
65 obj-$(CONFIG_X86_32) += common.o bugs.o
66 obj-$(CONFIG_X86_64) += common_64.o bugs_64.o
67 --- a/arch/x86/kernel/cpu/common.c
68 +++ b/arch/x86/kernel/cpu/common.c
73 +#include <asm/hypervisor.h>
74 #ifdef CONFIG_X86_LOCAL_APIC
75 #include <asm/mpspec.h>
77 @@ -505,6 +506,7 @@ static void __cpuinit identify_cpu(struc
78 c->x86, c->x86_model);
83 * On SMP, boot_cpu_data holds the common feature set between
84 * all CPUs; so make sure that we indicate which features are
85 --- a/arch/x86/kernel/cpu/common_64.c
86 +++ b/arch/x86/kernel/cpu/common_64.c
88 #include <asm/sections.h>
89 #include <asm/setup.h>
90 #include <asm/genapic.h>
91 +#include <asm/hypervisor.h>
95 @@ -387,6 +388,7 @@ static void __cpuinit identify_cpu(struc
101 * On SMP, boot_cpu_data holds the common feature set between
102 * all CPUs; so make sure that we indicate which features are
104 +++ b/arch/x86/kernel/cpu/hypervisor.c
107 + * Common hypervisor code
109 + * Copyright (C) 2008, VMware, Inc.
110 + * Author : Alok N Kataria <akataria@vmware.com>
112 + * This program is free software; you can redistribute it and/or modify
113 + * it under the terms of the GNU General Public License as published by
114 + * the Free Software Foundation; either version 2 of the License, or
115 + * (at your option) any later version.
117 + * This program is distributed in the hope that it will be useful, but
118 + * WITHOUT ANY WARRANTY; without even the implied warranty of
119 + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
120 + * NON INFRINGEMENT. See the GNU General Public License for more
123 + * You should have received a copy of the GNU General Public License
124 + * along with this program; if not, write to the Free Software
125 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
129 +#include <asm/processor.h>
130 +#include <asm/vmware.h>
132 +static inline void __cpuinit
133 +detect_hypervisor_vendor(struct cpuinfo_x86 *c)
135 + if (vmware_platform()) {
136 + c->x86_hyper_vendor = X86_HYPER_VENDOR_VMWARE;
138 + c->x86_hyper_vendor = X86_HYPER_VENDOR_NONE;
142 +unsigned long get_hypervisor_tsc_freq(void)
144 + if (boot_cpu_data.x86_hyper_vendor == X86_HYPER_VENDOR_VMWARE)
145 + return vmware_get_tsc_khz();
149 +void __cpuinit init_hypervisor(struct cpuinfo_x86 *c)
151 + detect_hypervisor_vendor(c);
155 +++ b/arch/x86/kernel/cpu/vmware.c
158 + * VMware Detection code.
160 + * Copyright (C) 2008, VMware, Inc.
161 + * Author : Alok N Kataria <akataria@vmware.com>
163 + * This program is free software; you can redistribute it and/or modify
164 + * it under the terms of the GNU General Public License as published by
165 + * the Free Software Foundation; either version 2 of the License, or
166 + * (at your option) any later version.
168 + * This program is distributed in the hope that it will be useful, but
169 + * WITHOUT ANY WARRANTY; without even the implied warranty of
170 + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
171 + * NON INFRINGEMENT. See the GNU General Public License for more
174 + * You should have received a copy of the GNU General Public License
175 + * along with this program; if not, write to the Free Software
176 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
180 +#include <linux/dmi.h>
181 +#include <asm/div64.h>
183 +#define CPUID_VMWARE_INFO_LEAF 0x40000000
184 +#define VMWARE_HYPERVISOR_MAGIC 0x564D5868
185 +#define VMWARE_HYPERVISOR_PORT 0x5658
187 +#define VMWARE_PORT_CMD_GETVERSION 10
188 +#define VMWARE_PORT_CMD_GETHZ 45
190 +#define VMWARE_PORT(cmd, eax, ebx, ecx, edx) \
191 + __asm__("inl (%%dx)" : \
192 + "=a"(eax), "=c"(ecx), "=d"(edx), "=b"(ebx) : \
193 + "0"(VMWARE_HYPERVISOR_MAGIC), \
194 + "1"(VMWARE_PORT_CMD_##cmd), \
195 + "2"(VMWARE_HYPERVISOR_PORT), "3"(0) : \
198 +static inline int __vmware_platform(void)
200 + uint32_t eax, ebx, ecx, edx;
201 + VMWARE_PORT(GETVERSION, eax, ebx, ecx, edx);
202 + return eax != (uint32_t)-1 && ebx == VMWARE_HYPERVISOR_MAGIC;
205 +static unsigned long __vmware_get_tsc_khz(void)
208 + uint32_t eax, ebx, ecx, edx;
210 + VMWARE_PORT(GETHZ, eax, ebx, ecx, edx);
212 + if (eax == (uint32_t)-1)
214 + tsc_hz = eax | (((uint64_t)ebx) << 32);
215 + do_div(tsc_hz, 1000);
216 + BUG_ON(tsc_hz >> 32);
220 +int vmware_platform(void)
222 + if (cpu_has_hypervisor) {
223 + unsigned int eax, ebx, ecx, edx;
224 + char hyper_vendor_id[13];
226 + cpuid(CPUID_VMWARE_INFO_LEAF, &eax, &ebx, &ecx, &edx);
227 + memcpy(hyper_vendor_id + 0, &ebx, 4);
228 + memcpy(hyper_vendor_id + 4, &ecx, 4);
229 + memcpy(hyper_vendor_id + 8, &edx, 4);
230 + hyper_vendor_id[12] = '\0';
231 + if (!strcmp(hyper_vendor_id, "VMwareVMware"))
233 + } else if (dmi_available && dmi_name_in_vendors("VMware") &&
234 + __vmware_platform())
240 +unsigned long vmware_get_tsc_khz(void)
242 + BUG_ON(!vmware_platform());
243 + return __vmware_get_tsc_khz();
245 --- a/arch/x86/kernel/setup.c
246 +++ b/arch/x86/kernel/setup.c
249 #include <mach_apic.h>
250 #include <asm/paravirt.h>
251 +#include <asm/hypervisor.h>
253 #include <asm/percpu.h>
254 #include <asm/topology.h>
255 @@ -896,6 +897,12 @@ void __init setup_arch(char **cmdline_p)
256 e820_reserve_resources();
257 e820_mark_nosave_regions(max_low_pfn);
260 + * VMware detection requires dmi to be available, so this
261 + * needs to be done after dmi_scan_machine, for the BP.
263 + init_hypervisor(&boot_cpu_data);
266 request_resource(&iomem_resource, &video_ram_resource);
268 --- a/arch/x86/kernel/tsc.c
269 +++ b/arch/x86/kernel/tsc.c
271 #include <asm/vgtod.h>
272 #include <asm/time.h>
273 #include <asm/delay.h>
274 +#include <asm/hypervisor.h>
276 unsigned int cpu_khz; /* TSC clocks / usec, not used here */
277 EXPORT_SYMBOL(cpu_khz);
278 @@ -189,9 +190,15 @@ unsigned long native_calibrate_tsc(void)
280 u64 tsc1, tsc2, delta, pm1, pm2, hpet1, hpet2;
281 unsigned long tsc_pit_min = ULONG_MAX, tsc_ref_min = ULONG_MAX;
282 - unsigned long flags;
283 + unsigned long flags, tsc_khz;
284 int hpet = is_hpet_enabled(), i;
286 + tsc_khz = get_hypervisor_tsc_freq();
288 + printk(KERN_INFO "TSC: Frequency read from the hypervisor\n");
293 * Run 5 calibration loops to get the lowest frequency value
294 * (the best estimate). We use two different calibration modes
296 +++ b/include/asm-x86/hypervisor.h
299 + * Copyright (C) 2008, VMware, Inc.
301 + * This program is free software; you can redistribute it and/or modify
302 + * it under the terms of the GNU General Public License as published by
303 + * the Free Software Foundation; either version 2 of the License, or
304 + * (at your option) any later version.
306 + * This program is distributed in the hope that it will be useful, but
307 + * WITHOUT ANY WARRANTY; without even the implied warranty of
308 + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
309 + * NON INFRINGEMENT. See the GNU General Public License for more
312 + * You should have received a copy of the GNU General Public License
313 + * along with this program; if not, write to the Free Software
314 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
317 +#ifndef ASM_X86__HYPERVISOR_H
318 +#define ASM_X86__HYPERVISOR_H
320 +extern unsigned long get_hypervisor_tsc_freq(void);
321 +extern void init_hypervisor(struct cpuinfo_x86 *c);
324 --- a/include/asm-x86/processor.h
325 +++ b/include/asm-x86/processor.h
326 @@ -109,6 +109,7 @@ struct cpuinfo_x86 {
327 /* Index into per_cpu list: */
330 + unsigned int x86_hyper_vendor;
331 } __attribute__((__aligned__(SMP_CACHE_BYTES)));
333 #define X86_VENDOR_INTEL 0
334 @@ -122,6 +123,9 @@ struct cpuinfo_x86 {
336 #define X86_VENDOR_UNKNOWN 0xff
338 +#define X86_HYPER_VENDOR_NONE 0
339 +#define X86_HYPER_VENDOR_VMWARE 1
342 * capabilities of CPUs
345 +++ b/include/asm-x86/vmware.h
348 + * Copyright (C) 2008, VMware, Inc.
350 + * This program is free software; you can redistribute it and/or modify
351 + * it under the terms of the GNU General Public License as published by
352 + * the Free Software Foundation; either version 2 of the License, or
353 + * (at your option) any later version.
355 + * This program is distributed in the hope that it will be useful, but
356 + * WITHOUT ANY WARRANTY; without even the implied warranty of
357 + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
358 + * NON INFRINGEMENT. See the GNU General Public License for more
361 + * You should have received a copy of the GNU General Public License
362 + * along with this program; if not, write to the Free Software
363 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
366 +#ifndef ASM_X86__VMWARE_H
367 +#define ASM_X86__VMWARE_H
369 +extern unsigned long vmware_get_tsc_khz(void);
370 +extern int vmware_platform(void);