]>
Commit | Line | Data |
---|---|---|
00e5a55c BS |
1 | From: Alok Kataria <akataria@vmware.com> |
2 | Subject: x86: Hypervisor detection and get tsc_freq from hypervisor | |
3 | Patch-mainline: | |
4 | References: bnc#441338 | |
5 | ||
6 | Impact: Changes timebase calibration on Vmware. | |
7 | ||
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. | |
12 | ||
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 | |
19 | under VMware. | |
20 | ||
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. | |
25 | ||
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 | |
31 | their platform. | |
32 | ||
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> | |
37 | ||
38 | --- | |
39 | ||
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 | |
55 | ||
56 | ||
57 | --- a/arch/x86/kernel/cpu/Makefile | |
58 | +++ b/arch/x86/kernel/cpu/Makefile | |
59 | @@ -4,6 +4,7 @@ | |
60 | ||
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 | |
64 | ||
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 | |
69 | @@ -14,6 +14,7 @@ | |
70 | #include <asm/mce.h> | |
71 | #include <asm/pat.h> | |
72 | #include <asm/asm.h> | |
73 | +#include <asm/hypervisor.h> | |
74 | #ifdef CONFIG_X86_LOCAL_APIC | |
75 | #include <asm/mpspec.h> | |
76 | #include <asm/apic.h> | |
77 | @@ -505,6 +506,7 @@ static void __cpuinit identify_cpu(struc | |
78 | c->x86, c->x86_model); | |
79 | } | |
80 | ||
81 | + init_hypervisor(c); | |
82 | /* | |
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 | |
87 | @@ -34,6 +34,7 @@ | |
88 | #include <asm/sections.h> | |
89 | #include <asm/setup.h> | |
90 | #include <asm/genapic.h> | |
91 | +#include <asm/hypervisor.h> | |
92 | ||
93 | #include "cpu.h" | |
94 | ||
95 | @@ -387,6 +388,7 @@ static void __cpuinit identify_cpu(struc | |
96 | ||
97 | detect_ht(c); | |
98 | ||
99 | + init_hypervisor(c); | |
100 | /* | |
101 | * On SMP, boot_cpu_data holds the common feature set between | |
102 | * all CPUs; so make sure that we indicate which features are | |
103 | --- /dev/null | |
104 | +++ b/arch/x86/kernel/cpu/hypervisor.c | |
105 | @@ -0,0 +1,48 @@ | |
106 | +/* | |
107 | + * Common hypervisor code | |
108 | + * | |
109 | + * Copyright (C) 2008, VMware, Inc. | |
110 | + * Author : Alok N Kataria <akataria@vmware.com> | |
111 | + * | |
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. | |
116 | + * | |
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 | |
121 | + * details. | |
122 | + * | |
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. | |
126 | + * | |
127 | + */ | |
128 | + | |
129 | +#include <asm/processor.h> | |
130 | +#include <asm/vmware.h> | |
131 | + | |
132 | +static inline void __cpuinit | |
133 | +detect_hypervisor_vendor(struct cpuinfo_x86 *c) | |
134 | +{ | |
135 | + if (vmware_platform()) { | |
136 | + c->x86_hyper_vendor = X86_HYPER_VENDOR_VMWARE; | |
137 | + } else { | |
138 | + c->x86_hyper_vendor = X86_HYPER_VENDOR_NONE; | |
139 | + } | |
140 | +} | |
141 | + | |
142 | +unsigned long get_hypervisor_tsc_freq(void) | |
143 | +{ | |
144 | + if (boot_cpu_data.x86_hyper_vendor == X86_HYPER_VENDOR_VMWARE) | |
145 | + return vmware_get_tsc_khz(); | |
146 | + return 0; | |
147 | +} | |
148 | + | |
149 | +void __cpuinit init_hypervisor(struct cpuinfo_x86 *c) | |
150 | +{ | |
151 | + detect_hypervisor_vendor(c); | |
152 | +} | |
153 | + | |
154 | --- /dev/null | |
155 | +++ b/arch/x86/kernel/cpu/vmware.c | |
156 | @@ -0,0 +1,88 @@ | |
157 | +/* | |
158 | + * VMware Detection code. | |
159 | + * | |
160 | + * Copyright (C) 2008, VMware, Inc. | |
161 | + * Author : Alok N Kataria <akataria@vmware.com> | |
162 | + * | |
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. | |
167 | + * | |
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 | |
172 | + * details. | |
173 | + * | |
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. | |
177 | + * | |
178 | + */ | |
179 | + | |
180 | +#include <linux/dmi.h> | |
181 | +#include <asm/div64.h> | |
182 | + | |
183 | +#define CPUID_VMWARE_INFO_LEAF 0x40000000 | |
184 | +#define VMWARE_HYPERVISOR_MAGIC 0x564D5868 | |
185 | +#define VMWARE_HYPERVISOR_PORT 0x5658 | |
186 | + | |
187 | +#define VMWARE_PORT_CMD_GETVERSION 10 | |
188 | +#define VMWARE_PORT_CMD_GETHZ 45 | |
189 | + | |
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) : \ | |
196 | + "memory"); | |
197 | + | |
198 | +static inline int __vmware_platform(void) | |
199 | +{ | |
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; | |
203 | +} | |
204 | + | |
205 | +static unsigned long __vmware_get_tsc_khz(void) | |
206 | +{ | |
207 | + uint64_t tsc_hz; | |
208 | + uint32_t eax, ebx, ecx, edx; | |
209 | + | |
210 | + VMWARE_PORT(GETHZ, eax, ebx, ecx, edx); | |
211 | + | |
212 | + if (eax == (uint32_t)-1) | |
213 | + return 0; | |
214 | + tsc_hz = eax | (((uint64_t)ebx) << 32); | |
215 | + do_div(tsc_hz, 1000); | |
216 | + BUG_ON(tsc_hz >> 32); | |
217 | + return tsc_hz; | |
218 | +} | |
219 | + | |
220 | +int vmware_platform(void) | |
221 | +{ | |
222 | + if (cpu_has_hypervisor) { | |
223 | + unsigned int eax, ebx, ecx, edx; | |
224 | + char hyper_vendor_id[13]; | |
225 | + | |
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")) | |
232 | + return 1; | |
233 | + } else if (dmi_available && dmi_name_in_vendors("VMware") && | |
234 | + __vmware_platform()) | |
235 | + return 1; | |
236 | + | |
237 | + return 0; | |
238 | +} | |
239 | + | |
240 | +unsigned long vmware_get_tsc_khz(void) | |
241 | +{ | |
242 | + BUG_ON(!vmware_platform()); | |
243 | + return __vmware_get_tsc_khz(); | |
244 | +} | |
245 | --- a/arch/x86/kernel/setup.c | |
246 | +++ b/arch/x86/kernel/setup.c | |
247 | @@ -98,6 +98,7 @@ | |
248 | ||
249 | #include <mach_apic.h> | |
250 | #include <asm/paravirt.h> | |
251 | +#include <asm/hypervisor.h> | |
252 | ||
253 | #include <asm/percpu.h> | |
254 | #include <asm/topology.h> | |
255 | @@ -905,6 +906,12 @@ void __init setup_arch(char **cmdline_p) | |
256 | e820_reserve_resources(); | |
257 | e820_mark_nosave_regions(max_low_pfn); | |
258 | ||
259 | + /* | |
260 | + * VMware detection requires dmi to be available, so this | |
261 | + * needs to be done after dmi_scan_machine, for the BP. | |
262 | + */ | |
263 | + init_hypervisor(&boot_cpu_data); | |
264 | + | |
265 | #ifdef CONFIG_X86_32 | |
266 | request_resource(&iomem_resource, &video_ram_resource); | |
267 | #endif | |
268 | --- a/arch/x86/kernel/tsc.c | |
269 | +++ b/arch/x86/kernel/tsc.c | |
270 | @@ -15,6 +15,7 @@ | |
271 | #include <asm/vgtod.h> | |
272 | #include <asm/time.h> | |
273 | #include <asm/delay.h> | |
274 | +#include <asm/hypervisor.h> | |
275 | ||
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) | |
279 | { | |
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; | |
285 | ||
286 | + tsc_khz = get_hypervisor_tsc_freq(); | |
287 | + if (tsc_khz) { | |
288 | + printk(KERN_INFO "TSC: Frequency read from the hypervisor\n"); | |
289 | + return tsc_khz; | |
290 | + } | |
291 | + | |
292 | /* | |
293 | * Run 5 calibration loops to get the lowest frequency value | |
294 | * (the best estimate). We use two different calibration modes | |
295 | --- /dev/null | |
296 | +++ b/include/asm-x86/hypervisor.h | |
297 | @@ -0,0 +1,26 @@ | |
298 | +/* | |
299 | + * Copyright (C) 2008, VMware, Inc. | |
300 | + * | |
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. | |
305 | + * | |
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 | |
310 | + * details. | |
311 | + * | |
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. | |
315 | + * | |
316 | + */ | |
317 | +#ifndef ASM_X86__HYPERVISOR_H | |
318 | +#define ASM_X86__HYPERVISOR_H | |
319 | + | |
320 | +extern unsigned long get_hypervisor_tsc_freq(void); | |
321 | +extern void init_hypervisor(struct cpuinfo_x86 *c); | |
322 | + | |
323 | +#endif | |
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: */ | |
328 | u16 cpu_index; | |
329 | #endif | |
330 | + unsigned int x86_hyper_vendor; | |
331 | } __attribute__((__aligned__(SMP_CACHE_BYTES))); | |
332 | ||
333 | #define X86_VENDOR_INTEL 0 | |
334 | @@ -122,6 +123,9 @@ struct cpuinfo_x86 { | |
335 | ||
336 | #define X86_VENDOR_UNKNOWN 0xff | |
337 | ||
338 | +#define X86_HYPER_VENDOR_NONE 0 | |
339 | +#define X86_HYPER_VENDOR_VMWARE 1 | |
340 | + | |
341 | /* | |
342 | * capabilities of CPUs | |
343 | */ | |
344 | --- /dev/null | |
345 | +++ b/include/asm-x86/vmware.h | |
346 | @@ -0,0 +1,26 @@ | |
347 | +/* | |
348 | + * Copyright (C) 2008, VMware, Inc. | |
349 | + * | |
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. | |
354 | + * | |
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 | |
359 | + * details. | |
360 | + * | |
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. | |
364 | + * | |
365 | + */ | |
366 | +#ifndef ASM_X86__VMWARE_H | |
367 | +#define ASM_X86__VMWARE_H | |
368 | + | |
369 | +extern unsigned long vmware_get_tsc_khz(void); | |
370 | +extern int vmware_platform(void); | |
371 | + | |
372 | +#endif |