ncval_test: Make test failures easier to understand
[nativeclient.git] / ncv / nacl_cpuid.c
blob89303b42bb596d075f250471b2793195db70998b
1 /*
2 * Copyright 2008, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * nacl_cpuid.c
34 * Retrieve and decode CPU model specific feature mask.
36 #include "native_client/include/portability.h"
37 #include <stdio.h>
38 #include <string.h>
39 #include <assert.h>
40 #include <stdlib.h>
41 #include "native_client/ncv/nacl_cpuid.h"
42 #include "native_client/platform_qual_test/nacl_cpuwhitelist.h"
45 * TODO: consolidate to use one debug print mechanism.
48 typedef enum {
49 CPUFeature_386=0,
50 CPUFeature_x87,
51 CPUFeature_MMX,
52 CPUFeature_SSE,
53 CPUFeature_SSE2,
54 CPUFeature_SSE3,
55 CPUFeature_SSSE3,
56 CPUFeature_SSE41,
57 CPUFeature_SSE42,
58 CPUFeature_MOVBE,
59 CPUFeature_POPCNT,
60 CPUFeature_CX8,
61 CPUFeature_CX16,
62 CPUFeature_CMOV,
63 CPUFeature_MON,
64 CPUFeature_FXSR,
65 CPUFeature_CLFSH,
66 /* These instructions are illegal but included for completeness */
67 CPUFeature_MSR,
68 CPUFeature_TSC,
69 CPUFeature_VME,
70 CPUFeature_PSN,
71 CPUFeature_VMX,
72 /* AMD-specific features */
73 CPUFeature_3DNOW,
74 CPUFeature_EMMX,
75 CPUFeature_E3DNOW,
76 CPUFeature_LZCNT,
77 CPUFeature_SSE4A,
78 CPUFeature_LM,
79 CPUFeature_SVM,
80 CPUFeature_Last,
81 } CPUFeatureID;
83 #define CPUID_EDX_x87 0x00000001 /* x87 FPU support */
84 #define CPUID_EDX_VME 0x00000002 /* Virtual 8086 Mode Enhancement */
85 #define CPUID_EDX_DEB 0x00000004 /* Debugging Extensions */
86 #define CPUID_EDX_PSE 0x00000008 /* Page Size Extensions */
87 #define CPUID_EDX_TSC 0x00000010 /* Time Stamp Counter */
88 #define CPUID_EDX_MSR 0x00000020 /* RDMSR and WRMSR */
89 #define CPUID_EDX_PAE 0x00000040 /* Physical Address Extensions */
90 #define CPUID_EDX_MCE 0x00000080 /* Machine Check Exception */
91 #define CPUID_EDX_CX8 0x00000100 /* CMPXCHG8B Instruction */
92 #define CPUID_EDX_APIC 0x00000200 /* APIC on chip */
93 /* 0x00000400 reserved */
94 #define CPUID_EDX_SEP 0x00000800 /* SYSENTER and SYSEXIT */
95 #define CPUID_EDX_MTRR 0x00001000 /* Memory Type Range Registers */
96 #define CPUID_EDX_PGE 0x00002000 /* PTE Global Bit */
97 #define CPUID_EDX_MCA 0x00004000 /* Machine Check Architecture */
98 #define CPUID_EDX_CMOV 0x00008000 /* CMOV instruction */
99 #define CPUID_EDX_PAT 0x00010000 /* Page Attribute Table */
100 #define CPUID_EDX_PSE36 0x00020000 /* Page Size Extension bis */
101 #define CPUID_EDX_PSN 0x00040000 /* Processor Serial Number */
102 #define CPUID_EDX_CLFSH 0x00080000 /* CFLUSH instruction */
103 /* 0x00100000 reserved */
104 #define CPUID_EDX_DS 0x00200000 /* Debug Store */
105 #define CPUID_EDX_ACPI 0x00400000 /* Thermal Monitor and Clock Ctrl */
106 #define CPUID_EDX_MMX 0x00800000 /* MMX extensions */
107 #define CPUID_EDX_FXSR 0x01000000 /* FXSAVE/FXRSTOR instructions */
108 #define CPUID_EDX_SSE 0x02000000 /* SSE extensions */
109 #define CPUID_EDX_SSE2 0x04000000 /* SSE2 extensions */
110 #define CPUID_EDX_SS 0x08000000 /* self snoop */
111 #define CPUID_EDX_HTT 0x10000000 /* hyper-threading */
112 #define CPUID_EDX_TM 0x20000000 /* Thermal monitor */
113 /* 0x40000000 reserved */
114 #define CPUID_EDX_PBE 0x80000000 /* Pending Break Enable */
116 #define CPUID_ECX_SSE3 0x00000001 /* SSE3 extensions */
117 /* 0x00000002 reserved */
118 /* 0x00000004 reserved */
119 #define CPUID_ECX_MON 0x00000008 /* MONITOR/MWAIT instructions */
120 #define CPUID_ECX_DSCPL 0x00000010 /* CPL Qualified Debug Store */
121 #define CPUID_ECX_VMX 0x00000020 /* Virtual Machine Extensions */
122 #define CPUID_ECX_SMX 0x00000040 /* Safer Mode Extensions */
123 #define CPUID_ECX_EST 0x00000080 /* Enahcned SpeedStep */
124 #define CPUID_ECX_TM2 0x00000100 /* Thermal Monitor 2 */
125 #define CPUID_ECX_SSSE3 0x00000200 /* SS_S_E3 extensions */
126 #define CPUID_ECX_CXID 0x00000400 /* L1 context ID */
127 /* 0x00000800 reserved */
128 /* 0x00001000 reserved */
129 #define CPUID_ECX_CX16 0x00002000 /* CMPXCHG16B instruction */
130 #define CPUID_ECX_XTPR 0x00004000 /* xTPR update control */
131 #define CPUID_ECX_PDCM 0x00008000 /* Perf/Debug Capability MSR */
132 /* 0x00010000 reserved */
133 /* 0x00020000 reserved */
134 /* 0x00040000 reserved */
135 #define CPUID_ECX_SSE41 0x00080000 /* SSE4.1 extensions */
136 #define CPUID_ECX_SSE42 0x00100000 /* SSE4.2 extensions */
137 /* 0x00200000 reserved */
138 #define CPUID_ECX_MOVBE 0x00400000 /* MOVBE instruction */
139 #define CPUID_ECX_POPCNT 0x00800000 /* POPCNT instruction */
140 /* 0x01000000 reserved */
141 /* 0x02000000 reserved */
142 /* 0x04000000 reserved */
143 /* 0x08000000 reserved */
144 /* 0x10000000 reserved */
145 /* 0x20000000 reserved */
146 /* 0x40000000 reserved */
147 /* 0x80000000 reserved */
149 /* AMD-specific masks */
150 #define CPUID_EDX_EMMX 0x00400000
151 #define CPUID_EDX_LM 0x20000000 /* longmode */
152 #define CPUID_EDX_E3DN 0x40000000
153 #define CPUID_EDX_3DN 0x80000000
154 #define CPUID_ECX_SVM 0x00000004
155 #define CPUID_ECX_ABM 0x00000020 /* lzcnt instruction */
156 #define CPUID_ECX_SSE4A 0x00000040
157 #define CPUID_ECX_PRE 0x00000100
158 #define CPUID_ECX_SSE5 0x00000800
160 typedef enum {
161 CFReg_EAX_I=0, /* eax == 1 */
162 CFReg_EBX_I, /* eax == 1 */
163 CFReg_ECX_I, /* eax == 1 */
164 CFReg_EDX_I, /* eax == 1 */
165 CFReg_EAX_A, /* eax == 0x80000001 */
166 CFReg_EBX_A, /* eax == 0x80000001 */
167 CFReg_ECX_A, /* eax == 0x80000001 */
168 CFReg_EDX_A, /* eax == 0x80000001 */
169 } CPUFeatureReg;
170 #define kMaxCPUFeatureReg 8
172 typedef struct cpufeature {
173 CPUFeatureReg reg;
174 uint32_t mask;
175 char *name;
176 } CPUFeature;
178 static const CPUFeature CPUFeatureDescriptions[(int)CPUFeature_Last] = {
179 /* Note: CPUID dates from DX2 486 */
180 {CFReg_EDX_I, CPUID_EDX_VME, "80386 ISA"},
181 {CFReg_EDX_I, CPUID_EDX_x87, "x87 FPU"},
182 {CFReg_EDX_I, CPUID_EDX_MMX, "MMX"},
183 {CFReg_EDX_I, CPUID_EDX_SSE, "SSE"},
184 {CFReg_EDX_I, CPUID_EDX_SSE2, "SSE2"},
185 {CFReg_ECX_I, CPUID_ECX_SSE3, "SSE3"},
186 {CFReg_ECX_I, CPUID_ECX_SSSE3, "SSSE3"},
187 {CFReg_ECX_I, CPUID_ECX_SSE41, "SSE41"},
188 {CFReg_ECX_I, CPUID_ECX_SSE42, "SSE42"},
189 {CFReg_ECX_I, CPUID_ECX_MOVBE, "MOVBE"},
190 {CFReg_ECX_I, CPUID_ECX_POPCNT, "POPCNT"},
191 {CFReg_EDX_I, CPUID_EDX_CX8, "CMPXCHG8B"},
192 {CFReg_ECX_I, CPUID_ECX_CX16, "CMPXCHG16B"},
193 {CFReg_EDX_I, CPUID_EDX_CMOV, "CMOV"},
194 {CFReg_ECX_I, CPUID_ECX_MON, "MONITOR/MWAIT"},
195 {CFReg_EDX_I, CPUID_EDX_FXSR, "FXSAVE/FXRSTOR"},
196 {CFReg_EDX_I, CPUID_EDX_CLFSH, "CLFSH"},
197 {CFReg_EDX_I, CPUID_EDX_MSR, "RDMSR/WRMSR"},
198 {CFReg_EDX_I, CPUID_EDX_TSC, "RDTSC"},
199 {CFReg_EDX_I, CPUID_EDX_VME, "VME"},
200 {CFReg_EDX_I, CPUID_EDX_PSN, "PSN"},
201 {CFReg_ECX_I, CPUID_ECX_VMX, "VMX"},
202 {CFReg_EDX_A, CPUID_EDX_3DN, "3DNow"},
203 {CFReg_EDX_A, CPUID_EDX_EMMX, "EMMX"},
204 {CFReg_EDX_A, CPUID_EDX_E3DN, "E3DNow"},
205 {CFReg_ECX_A, CPUID_ECX_ABM, "LZCNT"},
206 {CFReg_ECX_A, CPUID_ECX_SSE4A, "SSE4A"},
207 {CFReg_EDX_A, CPUID_EDX_LM, "LongMode"},
208 {CFReg_ECX_A, CPUID_ECX_SVM, "SVM"},
211 #define /* static const int */ kVendorIDLength 13
212 static const char Intel_CPUID0[kVendorIDLength] = "GenuineIntel";
213 static const char AMD_CPUID0[kVendorIDLength] = "AuthenticAMD";
214 #ifdef NOTYET
215 static const char UMC_CPUID0[kVendorIDLength] = "UMC UMC UMC ";
216 static const char Cyrix_CPUID0[kVendorIDLength] = "CyrixInstead";
217 static const char NexGen_CPUID0[kVendorIDLength] = "NexGenDriven";
218 static const char Cantaur_CPUID0[kVendorIDLength] = "CentaurHauls";
219 static const char Rise_CPUID0[kVendorIDLength] = "RiseRiseRise";
220 static const char SiS_CPUID0[kVendorIDLength] = "SiS SiS SiS ";
221 static const char TM_CPUID0[kVendorIDLength] = "GenuineTMx86";
222 static const char NSC_CPUID0[kVendorIDLength] = "Geode by NSC";
223 #endif
225 static int asm_HasCPUID() {
226 volatile int before, after, result;
228 #if (NACL_LINUX || NACL_OSX)
229 asm volatile("pushfl \n\t" /* save EFLAGS to eax */
230 "pop %%eax \n\t"
231 "movl %%eax, %0 \n\t" /* remember EFLAGS in %0 */
232 "xor $0x00200000, %%eax\n\t" /* toggle bit 21 */
233 "push %%eax \n\t" /* write eax to EFLAGS */
234 "popfl \n\t"
235 "pushfl \n\t" /* save EFLAGS to %1 */
236 "pop %1 \n\t"
237 : "=g" (before), "=g" (after));
238 #elif NACL_WINDOWS
239 __asm {
240 pushfd
241 pop eax
242 mov before, eax
243 xor eax, 0x00200000
244 push eax
245 popfd
246 pushfd
247 pop after
249 #else
250 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
251 #endif
252 result = (before ^ after) & 0x0200000;
253 return result;
256 static void asm_CPUID(uint32_t op, volatile uint32_t reg[4])
258 #if (NACL_LINUX || NACL_OSX)
259 asm volatile("pushl %%ebx \n\t" /* save %ebx */
260 "cpuid \n\t"
261 "movl %%ebx, %1 \n\t" /* save what cpuid just put in %ebx */
262 "popl %%ebx \n\t" /* restore the old %ebx */
263 : "=a"(reg[0]), "=S"(reg[1]), "=c"(reg[2]), "=d"(reg[3])
264 : "a"(op)
265 : "cc");
266 #endif
267 #if NACL_WINDOWS
268 /* There is no need to save %ebx and %esi, it's done automatically
269 * by the compiler.
271 __asm {
272 mov eax, op
273 cpuid
274 mov esi, [reg]
275 mov [esi], eax
276 mov [esi+1*4], ebx
277 mov [esi+2*4], ecx
278 mov [esi+3*4], edx
280 #endif
283 static char *CPUVersionID() {
284 static int ready = 0;
285 static uint32_t vidwords[4];
287 if (!ready) {
288 uint32_t reg[4];
289 asm_CPUID(0, reg);
290 vidwords[0] = reg[1];
291 vidwords[1] = reg[3];
292 vidwords[2] = reg[2];
293 vidwords[3] = 0;
294 ready = 1;
296 return (char *)vidwords;
299 /* returns feature vector as array of uint32_t [ecx, edx] */
300 static uint32_t *CPUFeatureVector() {
301 static int ready = 0;
302 static uint32_t featurev[kMaxCPUFeatureReg] = {0, 0, 0, 0, 0, 0, 0, 0};
304 if (!ready) {
305 asm_CPUID(1, featurev);
306 /* this is for AMD CPUs */
307 asm_CPUID(0x80000001, &featurev[CFReg_EAX_A]);
308 ready = 1;
309 #if 0
310 /* print feature vector */
311 printf("CPUID: %08x %08x %08x %08x\n",
312 featurev[0], featurev[1], featurev[2], featurev[3]);
313 printf("CPUID: %08x %08x %08x %08x\n",
314 featurev[4], featurev[5], featurev[6], featurev[7]);
315 #endif
317 return featurev;
320 /* GetCPUIDString creates an ASCII string that identfies this CPU's */
321 /* vendor ID, family, model, and stepping, as per the CPUID instruction */
322 /* WARNING: This routine is not threadsafe. */
323 char *GetCPUIDString() {
324 char *cpuversionid = CPUVersionID();
325 uint32_t *fv = CPUFeatureVector();
326 static char wlid[kCPUWhitelistIDLength] = "xxx";
328 /* Subtract 1 in this assert to avoid counting two null characters. */
329 assert(9 + kVendorIDLength - 1 == kCPUWhitelistIDLength);
330 memcpy(wlid, cpuversionid, kVendorIDLength-1);
331 SNPRINTF(&(wlid[kVendorIDLength-1]), 9, "%08x", (int)fv[CFReg_EAX_I]);
332 return wlid;
335 static bool CheckCPUFeature(CPUFeatureID fid) {
336 const CPUFeature *f = &CPUFeatureDescriptions[fid];
337 static uint32_t *fv = NULL;
339 if (fv == NULL) fv = CPUFeatureVector();
340 #if 0
341 printf("%s: %x (%08x & %08x)\n", f->name, (fv[f->reg] & f->mask),
342 fv[f->reg], f->mask);
343 #endif
344 if (fv[f->reg] & f->mask) {
345 return 1;
346 } else {
347 return 0;
351 static bool Check386CPU() {
352 const size_t kCPUID0Length = 12;
353 char *cpuversionid;
354 int hascpuid = asm_HasCPUID();
356 if (!hascpuid) return 0;
357 cpuversionid = CPUVersionID();
358 if (strncmp(cpuversionid, Intel_CPUID0, kCPUID0Length) == 0) {
359 return CheckCPUFeature(CPUFeature_386);
360 } else if (strncmp(cpuversionid, AMD_CPUID0, kCPUID0Length) == 0) {
361 return CheckCPUFeature(CPUFeature_386);
362 } else {
363 return 0;
367 /* WARNING: This routine and subroutines it uses are not threadsafe. */
368 /* Caller is responsible for calling this exactly once. */
369 void GetCPUFeatures(CPUFeatures *cpuf) {
370 cpuf->f_386 = Check386CPU();
371 if (cpuf->f_386 == 0) return;
373 cpuf->f_x87 = CheckCPUFeature(CPUFeature_x87);
374 cpuf->f_MMX = CheckCPUFeature(CPUFeature_MMX);
375 cpuf->f_SSE = CheckCPUFeature(CPUFeature_SSE);
376 cpuf->f_SSE2 = CheckCPUFeature(CPUFeature_SSE2);
377 cpuf->f_SSE3 = CheckCPUFeature(CPUFeature_SSE3);
378 cpuf->f_SSSE3 = CheckCPUFeature(CPUFeature_SSSE3);
379 cpuf->f_SSE41 = CheckCPUFeature(CPUFeature_SSE41);
380 cpuf->f_SSE42 = CheckCPUFeature(CPUFeature_SSE42);
381 cpuf->f_MOVBE = CheckCPUFeature(CPUFeature_MOVBE);
382 cpuf->f_POPCNT = CheckCPUFeature(CPUFeature_POPCNT);
383 cpuf->f_CX8 = CheckCPUFeature(CPUFeature_CX8);
384 cpuf->f_CX16 = CheckCPUFeature(CPUFeature_CX16);
385 cpuf->f_CMOV = CheckCPUFeature(CPUFeature_CMOV);
386 cpuf->f_MON = CheckCPUFeature(CPUFeature_MON);
387 cpuf->f_FXSR = CheckCPUFeature(CPUFeature_FXSR);
388 cpuf->f_CFLUSH = CheckCPUFeature(CPUFeature_CLFSH);
389 /* These instructions are illegal but included for completeness */
390 cpuf->f_MSR = CheckCPUFeature(CPUFeature_MSR);
391 cpuf->f_TSC = CheckCPUFeature(CPUFeature_TSC);
392 cpuf->f_VME = CheckCPUFeature(CPUFeature_VME);
393 cpuf->f_PSN = CheckCPUFeature(CPUFeature_PSN);
394 cpuf->f_VMX = CheckCPUFeature(CPUFeature_VMX);
395 /* AMD-specific features */
396 cpuf->f_3DNOW = CheckCPUFeature(CPUFeature_3DNOW);
397 cpuf->f_EMMX = CheckCPUFeature(CPUFeature_EMMX);
398 cpuf->f_E3DNOW = CheckCPUFeature(CPUFeature_E3DNOW);
399 cpuf->f_LZCNT = CheckCPUFeature(CPUFeature_LZCNT);
400 cpuf->f_SSE4A = CheckCPUFeature(CPUFeature_SSE4A);
401 cpuf->f_LM = CheckCPUFeature(CPUFeature_LM);
402 cpuf->f_SVM = CheckCPUFeature(CPUFeature_SVM);