2 * Copyright 2008, Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
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
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.
34 * Retrieve and decode CPU model specific feature mask.
36 #include "native_client/include/portability.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.
66 /* These instructions are illegal but included for completeness */
72 /* AMD-specific features */
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
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 */
170 #define kMaxCPUFeatureReg 8
172 typedef struct 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";
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";
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 */
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 */
235 "pushfl \n\t" /* save EFLAGS to %1 */
237 : "=g" (before
), "=g" (after
));
250 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
252 result
= (before
^ after
) & 0x0200000;
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 */
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])
268 /* There is no need to save %ebx and %esi, it's done automatically
283 static char *CPUVersionID() {
284 static int ready
= 0;
285 static uint32_t vidwords
[4];
290 vidwords
[0] = reg
[1];
291 vidwords
[1] = reg
[3];
292 vidwords
[2] = reg
[2];
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};
305 asm_CPUID(1, featurev
);
306 /* this is for AMD CPUs */
307 asm_CPUID(0x80000001, &featurev
[CFReg_EAX_A
]);
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]);
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
]);
335 static bool CheckCPUFeature(CPUFeatureID fid
) {
336 const CPUFeature
*f
= &CPUFeatureDescriptions
[fid
];
337 static uint32_t *fv
= NULL
;
339 if (fv
== NULL
) fv
= CPUFeatureVector();
341 printf("%s: %x (%08x & %08x)\n", f
->name
, (fv
[f
->reg
] & f
->mask
),
342 fv
[f
->reg
], f
->mask
);
344 if (fv
[f
->reg
] & f
->mask
) {
351 static bool Check386CPU() {
352 const size_t kCPUID0Length
= 12;
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
);
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
);