1 : /*
2 : * Copyright (c) 2012 The Native Client Authors. All rights reserved.
3 : * Use of this source code is governed by a BSD-style license that can be
4 : * found in the LICENSE file.
5 : */
6 :
7 : #include "native_client/src/trusted/validator/x86/nacl_cpuid.h"
8 :
9 : /*
10 : * nacl_cpuid.c
11 : * Retrieve and decode CPU model specific feature mask.
12 : */
13 : #if NACL_WINDOWS
14 : #include <intrin.h> /* __cpuid intrinsic */
15 : #endif /* NACL_WINDOWS */
16 :
17 : #include <stdio.h>
18 : #include <string.h>
19 : #include <assert.h>
20 : #include <stdlib.h>
21 :
22 : #include "native_client/src/include/portability_io.h"
23 :
24 : /*
25 : * TODO(bradchen): consolidate to use one debug print mechanism.
26 : */
27 :
28 : #define CPUID_EDX_x87 0x00000001 /* x87 FPU support */
29 : #define CPUID_EDX_VME 0x00000002 /* Virtual 8086 Mode Enhancement */
30 : #define CPUID_EDX_DEB 0x00000004 /* Debugging Extensions */
31 : #define CPUID_EDX_PSE 0x00000008 /* Page Size Extensions */
32 : #define CPUID_EDX_TSC 0x00000010 /* Time Stamp Counter */
33 : #define CPUID_EDX_MSR 0x00000020 /* RDMSR and WRMSR */
34 : #define CPUID_EDX_PAE 0x00000040 /* Physical Address Extensions */
35 : #define CPUID_EDX_MCE 0x00000080 /* Machine Check Exception */
36 : #define CPUID_EDX_CX8 0x00000100 /* CMPXCHG8B Instruction */
37 : #define CPUID_EDX_APIC 0x00000200 /* APIC on chip */
38 : /* 0x00000400 reserved */
39 : #define CPUID_EDX_SEP 0x00000800 /* SYSENTER and SYSEXIT */
40 : #define CPUID_EDX_MTRR 0x00001000 /* Memory Type Range Registers */
41 : #define CPUID_EDX_PGE 0x00002000 /* PTE Global Bit */
42 : #define CPUID_EDX_MCA 0x00004000 /* Machine Check Architecture */
43 : #define CPUID_EDX_CMOV 0x00008000 /* CMOV instruction */
44 : #define CPUID_EDX_PAT 0x00010000 /* Page Attribute Table */
45 : #define CPUID_EDX_PSE36 0x00020000 /* Page Size Extension bis */
46 : #define CPUID_EDX_PSN 0x00040000 /* Processor Serial Number */
47 : #define CPUID_EDX_CLFLUSH 0x00080000 /* CLFLUSH instruction */
48 : /* 0x00100000 reserved */
49 : #define CPUID_EDX_DS 0x00200000 /* Debug Store */
50 : #define CPUID_EDX_ACPI 0x00400000 /* Thermal Monitor and Clock Ctrl */
51 : #define CPUID_EDX_MMX 0x00800000 /* MMX extensions */
52 : #define CPUID_EDX_FXSR 0x01000000 /* FXSAVE/FXRSTOR instructions */
53 : #define CPUID_EDX_SSE 0x02000000 /* SSE extensions */
54 : #define CPUID_EDX_SSE2 0x04000000 /* SSE2 extensions */
55 : #define CPUID_EDX_SS 0x08000000 /* self snoop */
56 : #define CPUID_EDX_HTT 0x10000000 /* hyper-threading */
57 : #define CPUID_EDX_TM 0x20000000 /* Thermal monitor */
58 : /* 0x40000000 reserved */
59 : #define CPUID_EDX_PBE 0x80000000 /* Pending Break Enable */
60 :
61 : #define CPUID_ECX_SSE3 0x00000001 /* SSE3 extensions */
62 : /* 0x00000002 reserved */
63 : /* 0x00000004 reserved */
64 : #define CPUID_ECX_MON 0x00000008 /* MONITOR/MWAIT instructions */
65 : #define CPUID_ECX_DSCPL 0x00000010 /* CPL Qualified Debug Store */
66 : #define CPUID_ECX_VMX 0x00000020 /* Virtual Machine Extensions */
67 : #define CPUID_ECX_SMX 0x00000040 /* Safer Mode Extensions */
68 : #define CPUID_ECX_EST 0x00000080 /* Enahcned SpeedStep */
69 : #define CPUID_ECX_TM2 0x00000100 /* Thermal Monitor 2 */
70 : #define CPUID_ECX_SSSE3 0x00000200 /* SS_S_E3 extensions */
71 : #define CPUID_ECX_CXID 0x00000400 /* L1 context ID */
72 : /* 0x00000800 reserved */
73 : /* 0x00001000 reserved */
74 : #define CPUID_ECX_CX16 0x00002000 /* CMPXCHG16B instruction */
75 : #define CPUID_ECX_XTPR 0x00004000 /* xTPR update control */
76 : #define CPUID_ECX_PDCM 0x00008000 /* Perf/Debug Capability MSR */
77 : /* 0x00010000 reserved */
78 : /* 0x00020000 reserved */
79 : /* 0x00040000 reserved */
80 : #define CPUID_ECX_SSE41 0x00080000 /* SSE4.1 extensions */
81 : #define CPUID_ECX_SSE42 0x00100000 /* SSE4.2 extensions */
82 : /* 0x00200000 reserved */
83 : #define CPUID_ECX_MOVBE 0x00400000 /* MOVBE instruction */
84 : #define CPUID_ECX_POPCNT 0x00800000 /* POPCNT instruction */
85 : /* 0x00100000 reserved */
86 : #define CPUID_ECX_AES 0x02000000 /* AES instructions */
87 : #define CPUID_ECX_XSAVE 0x04000000 /* XSAVE/XRSTOR/XSETBV/XGETBV */
88 : #define CPUID_ECX_OSXSAVE 0x08000000 /* XSAVE et al enabled by OS */
89 : #define CPUID_ECX_AVX 0x10000000 /* AVX instructions */
90 : /* 0x20000000 reserved */
91 : /* 0x40000000 reserved */
92 : /* 0x80000000 reserved */
93 :
94 : /* AMD-specific masks */
95 : #define CPUID_EDX_EMMX 0x00400000
96 : #define CPUID_EDX_LM 0x20000000 /* longmode */
97 : #define CPUID_EDX_E3DN 0x40000000
98 : #define CPUID_EDX_3DN 0x80000000
99 : #define CPUID_ECX_SVM 0x00000004
100 : #define CPUID_ECX_ABM 0x00000020 /* lzcnt instruction */
101 : #define CPUID_ECX_SSE4A 0x00000040
102 : #define CPUID_ECX_PRE 0x00000100
103 : #define CPUID_ECX_SSE5 0x00000800
104 :
105 : typedef enum {
106 : CFReg_EAX_I=0, /* eax == 1 */
107 : CFReg_EBX_I, /* eax == 1 */
108 : CFReg_ECX_I, /* eax == 1 */
109 : CFReg_EDX_I, /* eax == 1 */
110 : CFReg_EAX_A, /* eax == 0x80000001 */
111 : CFReg_EBX_A, /* eax == 0x80000001 */
112 : CFReg_ECX_A, /* eax == 0x80000001 */
113 : CFReg_EDX_A /* eax == 0x80000001 */
114 : } CPUFeatureReg;
115 :
116 : typedef struct cpufeature {
117 : CPUFeatureReg reg;
118 : uint32_t mask;
119 : const char *name;
120 : } CPUFeature;
121 :
122 : static const CPUFeature CPUFeatureDescriptions[(int)NaClCPUFeature_Max] = {
123 : {CFReg_EDX_I, CPUID_EDX_x87, "x87"},
124 : {CFReg_EDX_I, CPUID_EDX_MMX, "MMX"},
125 : {CFReg_EDX_I, CPUID_EDX_SSE, "SSE"},
126 : {CFReg_EDX_I, CPUID_EDX_SSE2, "SSE2"},
127 : {CFReg_ECX_I, CPUID_ECX_SSE3, "SSE3"},
128 : {CFReg_ECX_I, CPUID_ECX_SSSE3, "SSSE3"},
129 : {CFReg_ECX_I, CPUID_ECX_SSE41, "SSE41"},
130 : {CFReg_ECX_I, CPUID_ECX_SSE42, "SSE42"},
131 : {CFReg_ECX_I, CPUID_ECX_MOVBE, "MOVBE"},
132 : {CFReg_ECX_I, CPUID_ECX_POPCNT, "POPCNT"},
133 : {CFReg_EDX_I, CPUID_EDX_CX8, "CMPXCHG8B"},
134 : {CFReg_ECX_I, CPUID_ECX_CX16, "CMPXCHG16B"},
135 : {CFReg_EDX_I, CPUID_EDX_CMOV, "CMOV"},
136 : {CFReg_ECX_I, CPUID_ECX_MON, "MONITOR/MWAIT"},
137 : {CFReg_EDX_I, CPUID_EDX_FXSR, "FXSAVE/FXRSTOR"},
138 : {CFReg_EDX_I, CPUID_EDX_CLFLUSH, "CLFLUSH"},
139 : {CFReg_EDX_I, CPUID_EDX_MSR, "RDMSR/WRMSR"},
140 : {CFReg_EDX_I, CPUID_EDX_TSC, "RDTSC"},
141 : {CFReg_EDX_I, CPUID_EDX_VME, "VME"},
142 : {CFReg_EDX_I, CPUID_EDX_PSN, "PSN"},
143 : {CFReg_ECX_I, CPUID_ECX_VMX, "VMX"},
144 : {CFReg_ECX_I, CPUID_ECX_OSXSAVE, "OSXSAVE"},
145 : {CFReg_ECX_I, CPUID_ECX_AVX, "AVX"},
146 : {CFReg_EDX_A, CPUID_EDX_3DN, "3DNow"},
147 : {CFReg_EDX_A, CPUID_EDX_EMMX, "EMMX"},
148 : {CFReg_EDX_A, CPUID_EDX_E3DN, "E3DNow"},
149 : {CFReg_ECX_A, CPUID_ECX_ABM, "LZCNT"},
150 : {CFReg_ECX_A, CPUID_ECX_SSE4A, "SSE4A"},
151 : {CFReg_EDX_A, CPUID_EDX_LM, "LongMode"},
152 : {CFReg_ECX_A, CPUID_ECX_SVM, "SVM"},
153 : };
154 :
155 : #define /* static const int */ kVendorIDLength 13
156 : static const char Intel_CPUID0[kVendorIDLength] = "GenuineIntel";
157 : static const char AMD_CPUID0[kVendorIDLength] = "AuthenticAMD";
158 : #ifdef NOTYET
159 : static const char UMC_CPUID0[kVendorIDLength] = "UMC UMC UMC ";
160 : static const char Cyrix_CPUID0[kVendorIDLength] = "CyrixInstead";
161 : static const char NexGen_CPUID0[kVendorIDLength] = "NexGenDriven";
162 : static const char Cantaur_CPUID0[kVendorIDLength] = "CentaurHauls";
163 : static const char Rise_CPUID0[kVendorIDLength] = "RiseRiseRise";
164 : static const char SiS_CPUID0[kVendorIDLength] = "SiS SiS SiS ";
165 : static const char TM_CPUID0[kVendorIDLength] = "GenuineTMx86";
166 : static const char NSC_CPUID0[kVendorIDLength] = "Geode by NSC";
167 : #endif
168 :
169 36 : static int asm_HasCPUID() {
170 : volatile int before, after, result;
171 : #if NACL_BUILD_SUBARCH == 64
172 : /* Note: If we are running in x86-64, then cpuid must be defined,
173 : * since CPUID dates from DX2 486, and x86-64 was added after this.
174 : */
175 : return 1;
176 : /* TODO(bradchen): split into separate Windows, etc., files */
177 : #elif defined(__GNUC__)
178 36 : __asm__ volatile("pushfl \n\t" /* save EFLAGS to eax */
179 : "pop %%eax \n\t"
180 : "movl %%eax, %0 \n\t" /* remember EFLAGS in %0 */
181 : "xor $0x00200000, %%eax\n\t" /* toggle bit 21 */
182 : "push %%eax \n\t" /* write eax to EFLAGS */
183 : "popfl \n\t"
184 : "pushfl \n\t" /* save EFLAGS to %1 */
185 : "pop %1 \n\t"
186 : /*
187 : * We use "r" constraints here, forcing registers,
188 : * because a memory reference using the stack
189 : * pointer wouldn't be safe since we're moving the
190 : * stack pointer around in between the
191 : * instructions. We need to inform the compiler
192 : * that we're clobbering %eax as a scratch register.
193 : */
194 : : "=r" (before), "=r" (after) : : "eax");
195 : #elif NACL_WINDOWS
196 : __asm {
197 : pushfd
198 : pop eax
199 : mov before, eax
200 : xor eax, 0x00200000
201 : push eax
202 : popfd
203 : pushfd
204 : pop after
205 : }
206 : #else
207 : # error Unsupported platform
208 : #endif
209 36 : result = (before ^ after) & 0x0200000;
210 36 : return result;
211 : }
212 :
213 108 : static void asm_CPUID(uint32_t op, volatile uint32_t reg[4]) {
214 : #if defined(__GNUC__)
215 : #if NACL_BUILD_SUBARCH == 64
216 : __asm__ volatile("push %%rbx \n\t" /* save %ebx */
217 : #else
218 108 : __asm__ volatile("pushl %%ebx \n\t"
219 : #endif
220 : "cpuid \n\t"
221 : "movl %%ebx, %1 \n\t"
222 : /* save what cpuid just put in %ebx */
223 : #if NACL_BUILD_SUBARCH == 64
224 : "pop %%rbx \n\t"
225 : #else
226 : "popl %%ebx \n\t" /* restore the old %ebx */
227 : #endif
228 : : "=a"(reg[0]), "=S"(reg[1]), "=c"(reg[2]), "=d"(reg[3])
229 : : "a"(op)
230 : : "cc");
231 : #elif NACL_WINDOWS
232 : __cpuid((uint32_t*)reg, op);
233 : #else
234 : # error Unsupported platform
235 : #endif
236 108 : }
237 :
238 36 : static void CacheCPUVersionID(NaClCPUData* data) {
239 36 : uint32_t reg[4] = {0, 0, 0, 0 };
240 36 : asm_CPUID(0, reg);
241 36 : data->_vidwords[0] = reg[1];
242 36 : data->_vidwords[1] = reg[3];
243 36 : data->_vidwords[2] = reg[2];
244 36 : data->_vidwords[3] = 0;
245 36 : }
246 :
247 : /* Defines the (cached) cpu version id */
248 : #define CPUVersionID(data) ((char*) (data)->_vidwords)
249 :
250 :
251 : /* Cache feature vector as array of uint32_t [ecx, edx] */
252 36 : static void CacheCPUFeatureVector(NaClCPUData* data) {
253 : int i;
254 324 : for (i = 0; i < kMaxCPUFeatureReg; ++i) {
255 288 : data->_featurev[i] = 0;
256 : }
257 36 : asm_CPUID(1, data->_featurev);
258 : /* this is for AMD CPUs */
259 36 : asm_CPUID(0x80000001, &data->_featurev[CFReg_EAX_A]);
260 : #if 0
261 : /* print feature vector */
262 : printf("CPUID: %08x %08x %08x %08x\n",
263 : data->_featurev[0],
264 : data->_featurev[1],
265 : data->_featurev[2],
266 : data->_featurev[3]);
267 : printf("CPUID: %08x %08x %08x %08x\n",
268 : data->_featurev[4],
269 : data->_featurev[5],
270 : data->_featurev[6],
271 : data->_featurev[7]);
272 : #endif
273 36 : }
274 :
275 : /* CacheGetCPUIDString creates an ASCII string that identfies this CPU's */
276 : /* vendor ID, family, model, and stepping, as per the CPUID instruction */
277 36 : static void CacheGetCPUIDString(NaClCPUData* data) {
278 36 : char *cpuversionid = CPUVersionID(data);
279 36 : uint32_t *fv = data->_featurev;
280 36 : char* wlid = data->_wlid;
281 : /* Subtract 1 in this assert to avoid counting two null characters. */
282 : assert(9 + kVendorIDLength - 1 == kCPUIDStringLength);
283 36 : memcpy(wlid, cpuversionid, kVendorIDLength-1);
284 36 : SNPRINTF(&(wlid[kVendorIDLength-1]), 9, "%08x", (int)fv[CFReg_EAX_I]);
285 36 : }
286 :
287 13 : char *GetCPUIDString(NaClCPUData* data) {
288 13 : return data->_wlid;
289 : }
290 :
291 : /* Returns true if the given feature is defined by the CPUID. */
292 726 : static int CheckCPUFeature(NaClCPUData* data, NaClCPUFeatureID fid) {
293 726 : const CPUFeature *f = &CPUFeatureDescriptions[fid];
294 726 : uint32_t *fv = data->_featurev;
295 : #if 0
296 : printf("%s: %x (%08x & %08x)\n", f->name, (fv[f->reg] & f->mask),
297 : fv[f->reg], f->mask);
298 : #endif
299 726 : if (fv[f->reg] & f->mask) {
300 414 : return 1;
301 : } else {
302 312 : return 0;
303 : }
304 : }
305 :
306 : uint64_t NaClXGETBV(uint32_t);
307 :
308 : /* Cache XCR vector */
309 36 : static void CacheCPUXCRVector(NaClCPUData* data) {
310 36 : if (CheckCPUFeature(data, NaClCPUFeature_OSXSAVE)) {
311 : int i;
312 0 : for (i = 0; i < kMaxCPUXCRReg; ++i) {
313 0 : data->_xcrv[i] = NaClXGETBV(i);
314 : }
315 : } else {
316 : int i;
317 72 : for (i = 0; i < kMaxCPUXCRReg; ++i) {
318 36 : data->_xcrv[i] = 0;
319 : }
320 : }
321 36 : }
322 :
323 : /* Check that we have a supported 386 architecture. NOTE:
324 : * As as side effect, the given cpu features is cleared before
325 : * setting the appropriate fields.
326 : */
327 : static void CheckNaClArchFeatures(NaClCPUData* data,
328 23 : nacl_arch_features* features) {
329 23 : const size_t kCPUID0Length = 12;
330 : char *cpuversionid;
331 23 : memset(features, 0, sizeof(*features));
332 23 : if (data->_has_CPUID) features->f_cpuid_supported = 1;
333 23 : cpuversionid = CPUVersionID(data);
334 23 : if (strncmp(cpuversionid, Intel_CPUID0, kCPUID0Length) == 0) {
335 23 : features->f_cpu_supported = 1;
336 0 : } else if (strncmp(cpuversionid, AMD_CPUID0, kCPUID0Length) == 0) {
337 0 : features->f_cpu_supported = 1;
338 : }
339 23 : }
340 :
341 10 : int NaClArchSupported(NaClCPUFeaturesX86 *features) {
342 10 : return (features->arch_features.f_cpuid_supported &&
343 : features->arch_features.f_cpu_supported);
344 : }
345 :
346 158 : void NaClClearCPUFeatures(NaClCPUFeaturesX86 *features) {
347 158 : memset(features, 0, sizeof(*features));
348 158 : }
349 :
350 66 : void NaClSetAllCPUFeatures(NaClCPUFeaturesX86 *features) {
351 : /* Be a little more pedantic than using memset because we don't know exactly
352 : * how the structure is laid out. If we use memset, fields may be initialized
353 : * to 0xff instead of 1 ... this isn't the end of the world but it can
354 : * create a skew if the structure is hashed, etc.
355 : */
356 : int id;
357 : /* Ensure any padding is zeroed. */
358 66 : NaClClearCPUFeatures(features);
359 66 : features->arch_features.f_cpuid_supported = 1;
360 66 : features->arch_features.f_cpu_supported = 1;
361 2046 : for (id = 0; id < NaClCPUFeature_Max; ++id) {
362 1980 : NaClSetCPUFeature(features, id, 1);
363 : }
364 66 : }
365 :
366 : void NaClCopyCPUFeatures(NaClCPUFeaturesX86* target,
367 175 : const NaClCPUFeaturesX86* source) {
368 175 : memcpy(target, source, sizeof(NaClCPUFeaturesX86));
369 175 : }
370 :
371 : void NaClSetCPUFeature(NaClCPUFeaturesX86 *features, NaClCPUFeatureID id,
372 2695 : int state) {
373 2695 : features->data[id] = (char) state;
374 2695 : }
375 :
376 18 : const char* NaClGetCPUFeatureName(NaClCPUFeatureID id) {
377 18 : return CPUFeatureDescriptions[id].name;
378 : }
379 :
380 : /* WARNING: This routine and subroutines it uses are not threadsafe.
381 : * However, if races occur, they are short lived, and at worst, will
382 : * result in defining fewer features than are actually supported by
383 : * the hardware. Hence, if a race occurs, the validator may reject
384 : * some features that should not be rejected.
385 : */
386 23 : static void GetCPUFeatures(NaClCPUData* data, NaClCPUFeaturesX86 *cpuf) {
387 : int id;
388 23 : NaClClearCPUFeatures(cpuf);
389 23 : CheckNaClArchFeatures(data, &cpuf->arch_features);
390 23 : if (!cpuf->arch_features.f_cpuid_supported) return;
391 :
392 713 : for (id = 0; id < NaClCPUFeature_Max; ++id) {
393 690 : NaClSetCPUFeature(cpuf, id, CheckCPUFeature(data, id));
394 : }
395 :
396 : /*
397 : * If the operating system doesn't maintain the AVX state,
398 : * pretend we don't have the instructions available at all.
399 : */
400 23 : if (!(NaClGetCPUFeature(cpuf, NaClCPUFeature_OSXSAVE)
401 : && (data->_xcrv[0] & 6) == 6)) {
402 23 : NaClSetCPUFeature(cpuf, NaClCPUFeature_AVX, 0);
403 : }
404 : }
405 :
406 36 : void NaClCPUDataGet(NaClCPUData* data) {
407 36 : data->_has_CPUID = asm_HasCPUID();
408 36 : CacheCPUVersionID(data);
409 36 : CacheCPUFeatureVector(data);
410 36 : CacheCPUXCRVector(data);
411 36 : CacheGetCPUIDString(data);
412 36 : }
413 :
414 23 : void NaClGetCurrentCPUFeatures(NaClCPUFeaturesX86 *cpu_features) {
415 : NaClCPUData cpu_data;
416 23 : NaClCPUDataGet(&cpu_data);
417 23 : GetCPUFeatures(&cpu_data, cpu_features);
418 23 : }
|