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/cpu_features/arch/x86/cpu_x86.h"
8 :
9 : /*
10 : * cpu_x86.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 : #include "native_client/src/shared/platform/nacl_log.h"
24 :
25 : /*
26 : * TODO(bradchen): consolidate to use one debug print mechanism.
27 : */
28 :
29 : #define CPUID_EDX_x87 0x00000001 /* x87 FPU support */
30 : #define CPUID_EDX_VME 0x00000002 /* Virtual 8086 Mode Enhancement */
31 : #define CPUID_EDX_DEB 0x00000004 /* Debugging Extensions */
32 : #define CPUID_EDX_PSE 0x00000008 /* Page Size Extensions */
33 : #define CPUID_EDX_TSC 0x00000010 /* Time Stamp Counter */
34 : #define CPUID_EDX_MSR 0x00000020 /* RDMSR and WRMSR */
35 : #define CPUID_EDX_PAE 0x00000040 /* Physical Address Extensions */
36 : #define CPUID_EDX_MCE 0x00000080 /* Machine Check Exception */
37 : #define CPUID_EDX_CX8 0x00000100 /* CMPXCHG8B Instruction */
38 : #define CPUID_EDX_APIC 0x00000200 /* APIC on chip */
39 : /* 0x00000400 reserved */
40 : #define CPUID_EDX_SEP 0x00000800 /* SYSENTER and SYSEXIT */
41 : #define CPUID_EDX_MTRR 0x00001000 /* Memory Type Range Registers */
42 : #define CPUID_EDX_PGE 0x00002000 /* PTE Global Bit */
43 : #define CPUID_EDX_MCA 0x00004000 /* Machine Check Architecture */
44 : #define CPUID_EDX_CMOV 0x00008000 /* CMOV instruction */
45 : #define CPUID_EDX_PAT 0x00010000 /* Page Attribute Table */
46 : #define CPUID_EDX_PSE36 0x00020000 /* Page Size Extension bis */
47 : #define CPUID_EDX_PSN 0x00040000 /* Processor Serial Number */
48 : #define CPUID_EDX_CLFLUSH 0x00080000 /* CLFLUSH instruction */
49 : /* 0x00100000 reserved */
50 : #define CPUID_EDX_DS 0x00200000 /* Debug Store */
51 : #define CPUID_EDX_ACPI 0x00400000 /* Thermal Monitor and Clock Ctrl */
52 : #define CPUID_EDX_MMX 0x00800000 /* MMX extensions */
53 : #define CPUID_EDX_FXSR 0x01000000 /* FXSAVE/FXRSTOR instructions */
54 : #define CPUID_EDX_SSE 0x02000000 /* SSE extensions */
55 : #define CPUID_EDX_SSE2 0x04000000 /* SSE2 extensions */
56 : #define CPUID_EDX_SS 0x08000000 /* Self snoop */
57 : #define CPUID_EDX_HTT 0x10000000 /* Hyper-threading */
58 : #define CPUID_EDX_TM 0x20000000 /* Thermal monitor */
59 : /* 0x40000000 reserved */
60 : #define CPUID_EDX_PBE 0x80000000 /* Pending Break Enable */
61 :
62 : #define CPUID_ECX_SSE3 0x00000001 /* SSE3 extensions */
63 : #define CPUID_ECX_CLMUL 0x00000002 /* PCLMULQDQ instruction */
64 : #define CPUID_ECX_DTES64 0x00000004 /* 64-bit DS Area */
65 : #define CPUID_ECX_MON 0x00000008 /* MONITOR/MWAIT instructions */
66 : #define CPUID_ECX_DSCPL 0x00000010 /* CPL Qualified Debug Store */
67 : #define CPUID_ECX_VMX 0x00000020 /* Virtual Machine Extensions */
68 : #define CPUID_ECX_SMX 0x00000040 /* Safer Mode Extensions */
69 : #define CPUID_ECX_EST 0x00000080 /* Enahcned SpeedStep */
70 : #define CPUID_ECX_TM2 0x00000100 /* Thermal Monitor 2 */
71 : #define CPUID_ECX_SSSE3 0x00000200 /* SS_S_E3 extensions */
72 : #define CPUID_ECX_CXID 0x00000400 /* L1 context ID */
73 : /* 0x00000800 reserved */
74 : #define CPUID_ECX_FMA 0x00001000 /* FMA instructions */
75 : #define CPUID_ECX_CX16 0x00002000 /* CMPXCHG16B instruction */
76 : #define CPUID_ECX_XTPR 0x00004000 /* xTPR update control */
77 : #define CPUID_ECX_PDCM 0x00008000 /* Perf/Debug Capability MSR */
78 : /* 0x00010000 reserved */
79 : #define CPUID_ECX_PCID 0x00020000 /* Process-context identifiers */
80 : #define CPUID_ECX_DCA 0x00040000 /* Direct Cache Access */
81 : #define CPUID_ECX_SSE41 0x00080000 /* SSE4.1 extensions */
82 : #define CPUID_ECX_SSE42 0x00100000 /* SSE4.2 extensions */
83 : #define CPUID_ECX_x2APIC 0x00800000 /* x2APIC feature */
84 : #define CPUID_ECX_MOVBE 0x00400000 /* MOVBE instruction */
85 : #define CPUID_ECX_POPCNT 0x00800000 /* POPCNT instruction */
86 : #define CPUID_ECX_TSCDDLN 0xx1000000 /* TSC-Deadline */
87 : #define CPUID_ECX_AES 0x02000000 /* AES instructions */
88 : #define CPUID_ECX_XSAVE 0x04000000 /* XSAVE/XRSTOR/XSETBV/XGETBV */
89 : #define CPUID_ECX_OSXSAVE 0x08000000 /* XSAVE et al enabled by OS */
90 : #define CPUID_ECX_AVX 0x10000000 /* AVX instructions */
91 : #define CPUID_ECX_F16C 0x20000000 /* 16bit floating-point instructions */
92 : #define CPUID_ECX_RDRAND 0x40000000 /* RDRAND instruction */
93 : /* 0x80000000 reserved */
94 :
95 : /* Function 07h main leaf (ecx = 0) */
96 : #define CPUID_EBX_FSGSBASE 0x00000001 /* RD/WR FS/GS BASE instructions */
97 : /* 0x00000002 reserved */
98 : /* 0x00000004 reserved */
99 : #define CPUID_EBX_BMI1 0x00000008 /* BMI1 instructions */
100 : #define CPUID_EBX_HLE 0x00000010 /* HLE instructions */
101 : #define CPUID_EBX_AVX2 0x00000020 /* AVX2 instructions */
102 : #define CPUID_EBX_SMEP 0x00000040 /* Supervisor Mode Exec-Protection */
103 : /* 0x00000080 reserved */
104 : #define CPUID_EBX_BMI2 0x00000100 /* BMI2 instructions */
105 : #define CPUID_EBX_ERMS 0x00000200 /* Enhanced REP MOVSB/STOSB */
106 : #define CPUID_EBX_INVPCID 0x00000400 /* Invalidate Processor Context ID */
107 : #define CPUID_EBX_RTM 0x00000800 /* Restricted Transactional Memory */
108 : /* 0xFFFFF000 reserved */
109 :
110 : /* AMD-specific masks - most are the same as in eax == 1 subfunction */
111 : /* 0x00000001 duplicates CPUID_EDX_x87 */
112 : /* 0x00000002 duplicates CPUID_EDX_VME */
113 : /* 0x00000004 duplicates CPUID_EDX_DEB */
114 : /* 0x00000008 duplicates CPUID_EDX_PSE */
115 : /* 0x00000010 duplicates CPUID_EDX_TSC */
116 : /* 0x00000020 duplicates CPUID_EDX_MSR */
117 : /* 0x00000040 duplicates CPUID_EDX_PAE */
118 : /* 0x00000080 duplicates CPUID_EDX_MCE */
119 : /* 0x00000100 duplicates CPUID_EDX_CX8 */
120 : /* 0000000200 duplicates CPUID_EDX_APIC */
121 : /* 0x00000400 reserved */
122 : #define CPUID_EDX_SYSCALL 0x00000800 /* SYSCALL/SYSRET instructions */
123 : /* 0x00001000 duplicates CPUID_EDX_MTRR */
124 : /* 0x00002000 duplicates CPUID_EDX_PGE */
125 : /* 0x00004000 duplicates CPUID_EDX_MCA */
126 : /* 0x00008000 duplicates CPUID_EDX_CMOV */
127 : /* 0x00010000 duplicates CPUID_EDX_PAT */
128 : /* 0x00020000 duplicates CPUID_EDX_PSE36 */
129 : /* 0x00040000 reserved */
130 : /* 0x00080000 reserved */
131 : #define CPUID_EDX_NX 0x00100000 /* Execute Disable Bit available */
132 : /* 0x00200000 reserved */
133 : #define CPUID_EDX_EMMX 0x00400000 /* Extensions to MMX instructions */
134 : /* 0x00800000 duplicates CPUID_EDX_MMX */
135 : /* 0x01000000 duplicates CPUID_EDX_FXSR */
136 : #define CPUID_EDX_FFFXSR 0x02000000 /* FXSAVE/FXRSTOR optimizations */
137 : #define CPUID_EDX_1GBPAGES 0x04000000 /* 1-GB large page support */
138 : #define CPUID_EDX_TSCP 0x08000000 /* RDTSCP instruction */
139 : /* 0x10000000 reserved */
140 : #define CPUID_EDX_LM 0x20000000 /* Longmode (AKA x86-64 mode) */
141 : #define CPUID_EDX_E3DN 0x40000000 /* Extensions to 3DNow! instructions */
142 : #define CPUID_EDX_3DN 0x80000000 /* 3DNow! instructions */
143 :
144 : #define CPUID_ECX_LAHF 0x00000001 /* LAHF/SAHF in x86-64 mode */
145 : #define CPUID_ECX_CMPLEGACY 0x00000002 /* Core Multi-Processing legacy mode */
146 : #define CPUID_ECX_SVM 0x00000004 /* Secure Virtual Machine */
147 : #define CPUID_ECX_EXTAPIC 0x00000008 /* Extended APIC space */
148 : #define CPUID_ECX_ALTMOVCR8 0x00000010 /* LOCK MOV CR0 means MOV CR8 */
149 : #define CPUID_ECX_ABM 0x00000020 /* LZCNT instruction */
150 : #define CPUID_ECX_SSE4A 0x00000040 /* SSRE4A instructions */
151 : #define CPUID_ECX_MISALGNSSE 0x00000080 /* Misalign SSE mode */
152 : #define CPUID_ECX_PRE 0x00000100 /* 3DNow! prefetch */
153 : #define CPUID_ECX_OSVW 0x00000200 /* OS visible workaround */
154 : #define CPUID_ECX_IBS 0x00000400 /* Instruction Based Sampling */
155 : #define CPUID_ECX_XOP 0x00000800 /* XOP instructions */
156 : #define CPUID_ECX_SKINIT 0x00001000 /* SKINIT/STGI are always supported */
157 : #define CPUID_ECX_WDT 0x00002000 /* Watchdog timer support */
158 : /* 0x00004000 reserved */
159 : #define CPUID_ECX_LWP 0x00008000 /* Lightweight profiling support */
160 : #define CPUID_ECX_FMA4 0x00010000 /* FMA4 instructions */
161 : /* 0x00020000 reserved */
162 : /* 0x00040000 reserved */
163 : #define CPUID_ECX_NODEID 0x00080000 /* MSRC001_100C[NodeId, NodesPerCPU] */
164 : /* 0x00100000 reserved */
165 : #define CPUID_ECX_TBM 0x00200000 /* Trailing bit manipulations */
166 : #define CPUID_ECX_TOPOLOGY 0x00400000 /* Topology extensions support */
167 : /* 0xFF800000 reserved */
168 : #define CPUID_NONE 0x00000000 /* bogus value */
169 :
170 : typedef enum {
171 : CFReg_EAX_I=0, /* eax == 1 */
172 : CFReg_EBX_I, /* eax == 1 */
173 : CFReg_ECX_I, /* eax == 1 */
174 : CFReg_EDX_I, /* eax == 1 */
175 : CFReg_EAX_7, /* eax == 7; ecx == 0 */
176 : CFReg_EBX_7, /* eax == 7; ecx == 0 */
177 : CFReg_ECX_7, /* eax == 7; ecx == 0 */
178 : CFReg_EDX_7, /* eax == 7; ecx == 0 */
179 : CFReg_EAX_A, /* eax == 0x80000001 */
180 : CFReg_EBX_A, /* eax == 0x80000001 */
181 : CFReg_ECX_A, /* eax == 0x80000001 */
182 : CFReg_EDX_A, /* eax == 0x80000001 */
183 : CFReg_NONE /* bogus value */
184 : } CPUFeatureReg;
185 :
186 : enum {
187 : kFeatureFixedOff,
188 : kFeatureFixedOn
189 : };
190 :
191 : typedef struct cpufeature {
192 : uint8_t reg;
193 : uint8_t fixedFeature;
194 : uint32_t mask;
195 : const char *name;
196 : } CPUFeature;
197 :
198 : static const CPUFeature CPUFeatureDescriptions[(int)NaClCPUFeatureX86_Max] = {
199 : #define NACL_X86_CPU_FEATURE(id, reg, idx, fix, ven, str) \
200 : { \
201 : NACL_CONCAT(CFReg_, reg), \
202 : NACL_CONCAT(kFeature, fix), \
203 : NACL_CONCAT(CPUID_, idx), \
204 : str, \
205 : },
206 : #include "native_client/src/trusted/cpu_features/arch/x86/cpu_x86_features.h"
207 : #undef NACL_X86_CPU_FEATURE
208 : };
209 :
210 : #define /* static const int */ kVendorIDLength 13
211 : static const char Intel_CPUID0[kVendorIDLength] = "GenuineIntel";
212 : static const char AMD_CPUID0[kVendorIDLength] = "AuthenticAMD";
213 : #ifdef NOTYET
214 : static const char UMC_CPUID0[kVendorIDLength] = "UMC UMC UMC ";
215 : static const char Cyrix_CPUID0[kVendorIDLength] = "CyrixInstead";
216 : static const char NexGen_CPUID0[kVendorIDLength] = "NexGenDriven";
217 : static const char Cantaur_CPUID0[kVendorIDLength] = "CentaurHauls";
218 : static const char Rise_CPUID0[kVendorIDLength] = "RiseRiseRise";
219 : static const char SiS_CPUID0[kVendorIDLength] = "SiS SiS SiS ";
220 : static const char TM_CPUID0[kVendorIDLength] = "GenuineTMx86";
221 : static const char NSC_CPUID0[kVendorIDLength] = "Geode by NSC";
222 : #endif
223 :
224 : static int asm_HasCPUID(void) {
225 565 : volatile int before, after, result;
226 : #if NACL_BUILD_SUBARCH == 64
227 : /* Note: If we are running in x86-64, then cpuid must be defined,
228 : * since CPUID dates from DX2 486, and x86-64 was added after this.
229 : */
230 565 : return 1;
231 : /* TODO(bradchen): split into separate Windows, etc., files */
232 : #elif defined(__GNUC__)
233 : __asm__ volatile("pushfl \n\t" /* save EFLAGS to eax */
234 : "pop %%eax \n\t"
235 : "movl %%eax, %0 \n\t" /* remember EFLAGS in %0 */
236 : "xor $0x00200000, %%eax\n\t" /* toggle bit 21 */
237 : "push %%eax \n\t" /* write eax to EFLAGS */
238 : "popfl \n\t"
239 : "pushfl \n\t" /* save EFLAGS to %1 */
240 : "pop %1 \n\t"
241 : /*
242 : * We use "r" constraints here, forcing registers,
243 : * because a memory reference using the stack
244 : * pointer wouldn't be safe since we're moving the
245 : * stack pointer around in between the
246 : * instructions. We need to inform the compiler
247 : * that we're clobbering %eax as a scratch register.
248 : */
249 : : "=r" (before), "=r" (after) : : "eax");
250 : #elif NACL_WINDOWS
251 : __asm {
252 : pushfd
253 : pop eax
254 : mov before, eax
255 : xor eax, 0x00200000
256 : push eax
257 : popfd
258 : pushfd
259 : pop after
260 : }
261 : #else
262 : # error Unsupported platform
263 : #endif
264 : result = (before ^ after) & 0x0200000;
265 : return result;
266 : }
267 :
268 1695 : static void asm_CPUID(uint32_t op, volatile uint32_t reg[4]) {
269 : #if defined(__GNUC__)
270 : #if NACL_BUILD_SUBARCH == 64
271 1695 : __asm__ volatile("push %%rbx \n\t" /* save %ebx */
272 : #else
273 : __asm__ volatile("pushl %%ebx \n\t"
274 : #endif
275 : "cpuid \n\t"
276 : "movl %%ebx, %1 \n\t"
277 : /* save what cpuid just put in %ebx */
278 : #if NACL_BUILD_SUBARCH == 64
279 : "pop %%rbx \n\t"
280 : #else
281 : "popl %%ebx \n\t" /* restore the old %ebx */
282 : #endif
283 : : "=a"(reg[0]), "=S"(reg[1]), "=c"(reg[2]), "=d"(reg[3])
284 : : "a"(op)
285 : : "cc");
286 : #elif NACL_WINDOWS
287 : __cpuid((uint32_t*)reg, op);
288 : #else
289 : # error Unsupported platform
290 : #endif
291 1695 : }
292 :
293 : /*
294 : * Historically CPUID only used eax to select "CPUID function". Function 07h
295 : * broke this tradition: it's now required to specify "leaf" in ECX register.
296 : *
297 : * We can specify leaf in all cases (older "CPUID functions" will just ignore
298 : * it), but there is a catch: MSVC 2005 or below don't include __cpuidex
299 : * intrinsic required to call CPUID with leaf support!
300 : *
301 : * Thus we have two functions: asm_CPUID (for "CPUID functions" without leaves)
302 : * and asm_CPUIDx (for "CPUID functions" with leaves). If code is compiled using
303 : * MSVC 2005 or MSVC 2008 then features detected using function 07h will not be
304 : * available.
305 : *
306 : * Note: MSVC 2008 is particularly problematic: MSVC 2008 does not support
307 : * __cpuidex while MSVC 2008 SP1 does. Unfortunatelly there are no easy way
308 : * to distinguish MSVC 2008 SP1 from MSVC 2008 using ifdef's thus we disable
309 : * __cpuidex for MSVC 2008 unconditionally.
310 : */
311 565 : static void asm_CPUIDx(uint32_t op, volatile uint32_t reg[4], uint32_t ecx) {
312 : #if defined(__GNUC__)
313 : #if NACL_BUILD_SUBARCH == 64
314 565 : __asm__ volatile("push %%rbx \n\t" /* save %ebx */
315 : #else
316 : __asm__ volatile("pushl %%ebx \n\t"
317 : #endif
318 : "cpuid \n\t"
319 : "movl %%ebx, %1 \n\t"
320 : /* save what cpuid just put in %ebx */
321 : #if NACL_BUILD_SUBARCH == 64
322 : "pop %%rbx \n\t"
323 : #else
324 : "popl %%ebx \n\t" /* restore the old %ebx */
325 : #endif
326 : : "=a"(reg[0]), "=S"(reg[1]), "=c"(reg[2]), "=d"(reg[3])
327 : : "a"(op), "c"(ecx)
328 : : "cc");
329 : #elif NACL_WINDOWS
330 : #ifdef _MSC_VER
331 : #if _MSC_VER < 1600
332 : reg[0] = 0;
333 : reg[1] = 0;
334 : reg[2] = 0;
335 : reg[3] = 0;
336 : #else
337 : __cpuidex((uint32_t*)reg, op, ecx);
338 : #endif
339 : #else /* NACL_WINDOWS, but _MSC_VER is not defined */
340 : /* This is Windows but not MSVC: who knows if __cpuidex is available? */
341 : # error Unsupported compiler
342 : #endif
343 : #else
344 : # error Unsupported platform
345 : #endif
346 565 : }
347 :
348 565 : static void CacheCPUVersionID(NaClCPUData* data) {
349 565 : uint32_t reg[4] = {0, 0, 0, 0 };
350 565 : asm_CPUID(0, reg);
351 565 : data->_vidwords[0] = reg[1];
352 565 : data->_vidwords[1] = reg[3];
353 565 : data->_vidwords[2] = reg[2];
354 565 : data->_vidwords[3] = 0;
355 565 : }
356 :
357 : /* Defines the (cached) cpu version id */
358 : #define CPUVersionID(data) ((char*) (data)->_vidwords)
359 :
360 :
361 : /* Cache feature vector as array of uint32_t [ecx, edx] */
362 565 : static void CacheCPUFeatureVector(NaClCPUData* data) {
363 565 : int i;
364 14690 : for (i = 0; i < kMaxCPUFeatureReg; ++i) {
365 6780 : data->_featurev[i] = 0;
366 6780 : }
367 565 : asm_CPUID(1, data->_featurev);
368 : /* This is for for model-specific instructions from AMD and Intel after
369 : AMD's Bulldozer (introduced BMI1 set) and Intel's Haswell (introduced
370 : AVX2, BMI2, HLE, RTM, and RD/WR FS/GS BASE instructions). */
371 565 : asm_CPUIDx(7, &data->_featurev[CFReg_EAX_7], 0);
372 : /* this is for AMD CPUs */
373 565 : asm_CPUID(0x80000001, &data->_featurev[CFReg_EAX_A]);
374 : #if 0
375 : /* print feature vector */
376 : printf("CPUID: %08x %08x %08x %08x\n",
377 : data->_featurev[0],
378 : data->_featurev[1],
379 : data->_featurev[2],
380 : data->_featurev[3]);
381 : printf("CPUID: %08x %08x %08x %08x\n",
382 : data->_featurev[4],
383 : data->_featurev[5],
384 : data->_featurev[6],
385 : data->_featurev[7]);
386 : #endif
387 565 : }
388 :
389 : /* CacheGetCPUIDString creates an ASCII string that identfies this CPU's */
390 : /* vendor ID, family, model, and stepping, as per the CPUID instruction */
391 565 : static void CacheGetCPUIDString(NaClCPUData* data) {
392 565 : char *cpuversionid = CPUVersionID(data);
393 565 : uint32_t *fv = data->_featurev;
394 565 : char* wlid = data->_wlid;
395 : /* Subtract 1 in this assert to avoid counting two null characters. */
396 : assert(9 + kVendorIDLength - 1 == kCPUIDStringLength);
397 1695 : memcpy(wlid, cpuversionid, kVendorIDLength-1);
398 565 : SNPRINTF(&(wlid[kVendorIDLength-1]), 9, "%08x", (int)fv[CFReg_EAX_I]);
399 565 : }
400 :
401 269 : char *GetCPUIDString(NaClCPUData* data) {
402 269 : return data->_wlid;
403 : }
404 :
405 : /* Returns true if the given feature is defined by the CPUID. */
406 11813 : static int CheckCPUFeature(NaClCPUData* data, NaClCPUFeatureX86ID fid) {
407 11813 : const CPUFeature *f = &CPUFeatureDescriptions[fid];
408 11813 : uint32_t *fv = data->_featurev;
409 23330 : if ((fid == NaClCPUFeatureX86_CPUIDSupported) ||
410 : (fid == NaClCPUFeatureX86_CPUSupported)) {
411 : /* CPUIDSupported and CPUSupported aren't actually in CPUID,
412 : CPUFeatureDescriptions therefore doesn't contain actual reg/mask for
413 : them. */
414 592 : return 1;
415 : }
416 11221 : if (fv[f->reg] & f->mask) {
417 5624 : return 1;
418 : } else {
419 5597 : return 0;
420 : }
421 11813 : }
422 :
423 : uint64_t NaClXGETBV(uint32_t);
424 :
425 : /* Cache XCR vector */
426 565 : static void CacheCPUXCRVector(NaClCPUData* data) {
427 565 : if (CheckCPUFeature(data, NaClCPUFeatureX86_OSXSAVE)) {
428 0 : int i;
429 0 : for (i = 0; i < kMaxCPUXCRReg; ++i) {
430 0 : data->_xcrv[i] = NaClXGETBV(i);
431 0 : }
432 0 : } else {
433 565 : int i;
434 2260 : for (i = 0; i < kMaxCPUXCRReg; ++i) {
435 565 : data->_xcrv[i] = 0;
436 565 : }
437 : }
438 565 : }
439 :
440 : /* Check that we have a supported 386 architecture. NOTE:
441 : * As as side effect, the given cpu features is cleared before
442 : * setting the appropriate fields.
443 : */
444 296 : static void CheckNaClArchFeatures(NaClCPUData *data,
445 296 : NaClCPUFeaturesX86 *features) {
446 296 : const size_t kCPUID0Length = 12;
447 296 : char *cpuversionid;
448 296 : if (data->_has_CPUID) {
449 296 : NaClSetCPUFeatureX86(features, NaClCPUFeatureX86_CPUIDSupported, 1);
450 296 : }
451 296 : cpuversionid = CPUVersionID(data);
452 296 : if (strncmp(cpuversionid, Intel_CPUID0, kCPUID0Length) == 0) {
453 296 : NaClSetCPUFeatureX86(features, NaClCPUFeatureX86_CPUSupported, 1);
454 296 : } else if (strncmp(cpuversionid, AMD_CPUID0, kCPUID0Length) == 0) {
455 0 : NaClSetCPUFeatureX86(features, NaClCPUFeatureX86_CPUSupported, 1);
456 0 : }
457 296 : }
458 :
459 574 : void NaClSetAllCPUFeaturesX86(NaClCPUFeatures *f) {
460 : /* TODO(jfb) Use a safe cast in this interface. */
461 574 : NaClCPUFeaturesX86 *features = (NaClCPUFeaturesX86 *) f;
462 : /* Be a little more pedantic than using memset because we don't know exactly
463 : * how the structure is laid out. If we use memset, fields may be initialized
464 : * to 0xff instead of 1 ... this isn't the end of the world but it can
465 : * create a skew if the structure is hashed, etc.
466 : */
467 574 : int id;
468 : /* Ensure any padding is zeroed. */
469 574 : NaClClearCPUFeaturesX86(features);
470 44772 : for (id = 0; id < NaClCPUFeatureX86_Max; ++id) {
471 21812 : NaClSetCPUFeatureX86(features, id, 1);
472 21812 : }
473 574 : }
474 :
475 : /* WARNING: This routine and subroutines it uses are not threadsafe.
476 : * However, if races occur, they are short lived, and at worst, will
477 : * result in defining fewer features than are actually supported by
478 : * the hardware. Hence, if a race occurs, the validator may reject
479 : * some features that should not be rejected.
480 : */
481 296 : static void GetCPUFeatures(NaClCPUData* data, NaClCPUFeaturesX86 *cpuf) {
482 296 : int id;
483 296 : NaClClearCPUFeaturesX86(cpuf);
484 296 : CheckNaClArchFeatures(data, cpuf);
485 296 : if (!NaClGetCPUFeatureX86(cpuf, NaClCPUFeatureX86_CPUIDSupported)) {
486 0 : return;
487 : }
488 :
489 23088 : for (id = 0; id < NaClCPUFeatureX86_Max; ++id) {
490 11248 : NaClSetCPUFeatureX86(cpuf, id, CheckCPUFeature(data, id));
491 11248 : }
492 :
493 : /*
494 : * If the operating system doesn't maintain the YMM state,
495 : * pretend we don't have the instructions available at all.
496 : */
497 296 : if (!(NaClGetCPUFeatureX86(cpuf, NaClCPUFeatureX86_OSXSAVE)
498 : && (data->_xcrv[0] & 6) == 6)) {
499 296 : NaClSetCPUFeatureX86(cpuf, NaClCPUFeatureX86_AVX, 0);
500 296 : NaClSetCPUFeatureX86(cpuf, NaClCPUFeatureX86_F16C, 0);
501 296 : NaClSetCPUFeatureX86(cpuf, NaClCPUFeatureX86_FMA, 0);
502 296 : NaClSetCPUFeatureX86(cpuf, NaClCPUFeatureX86_FMA4, 0);
503 296 : }
504 296 : }
505 :
506 565 : void NaClCPUDataGet(NaClCPUData* data) {
507 565 : data->_has_CPUID = asm_HasCPUID();
508 565 : CacheCPUVersionID(data);
509 565 : CacheCPUFeatureVector(data);
510 565 : CacheCPUXCRVector(data);
511 565 : CacheGetCPUIDString(data);
512 565 : }
513 :
514 296 : void NaClGetCurrentCPUFeaturesX86(NaClCPUFeatures *f) {
515 : /* TODO(jfb) Use a safe cast in this interface. */
516 296 : NaClCPUFeaturesX86 *features = (NaClCPUFeaturesX86 *) f;
517 296 : NaClCPUData cpu_data;
518 296 : NaClCPUDataGet(&cpu_data);
519 296 : GetCPUFeatures(&cpu_data, features);
520 296 : }
521 :
522 : /* The CPUFeaturesDescriptions struct defines the CPU feature model for
523 : * fixed-feature CPU mode. We currently require the same set of features
524 : * for both 32- and 64-bit x86 CPUs, intended to be supported by
525 : * most/all post-Pentium III CPUs. This set may be something we need to
526 : * revisit in the future.
527 : */
528 2 : int NaClFixCPUFeaturesX86(NaClCPUFeatures *f) {
529 : /* TODO(jfb) Use a safe cast in this interface. */
530 2 : NaClCPUFeaturesX86 *features = (NaClCPUFeaturesX86 *) f;
531 2 : NaClCPUFeatureX86ID fid;
532 2 : int rvalue = 1;
533 :
534 156 : for (fid = 0; fid < NaClCPUFeatureX86_Max; fid++) {
535 76 : if (CPUFeatureDescriptions[fid].fixedFeature) {
536 22 : if (!NaClGetCPUFeatureX86(features, fid)) {
537 : /* This CPU is missing a required feature. */
538 0 : NaClLog(LOG_ERROR,
539 : "This CPU is missing a feature required by fixed-mode: %s\n",
540 0 : NaClGetCPUFeatureX86Name(fid));
541 0 : rvalue = 0; /* set return value to indicate failure */
542 0 : }
543 22 : } else {
544 : /* Feature is not in the fixed model.
545 : * Ensure features does not have it either.
546 : */
547 54 : NaClSetCPUFeatureX86(features, fid, 0);
548 : }
549 76 : }
550 2 : return rvalue;
551 : }
552 :
553 33 : const char* NaClGetCPUFeatureX86Name(NaClCPUFeatureX86ID id) {
554 33 : return CPUFeatureDescriptions[id].name;
555 : }
556 :
557 34892 : void NaClSetCPUFeatureX86(NaClCPUFeaturesX86 *f, NaClCPUFeatureX86ID id,
558 34892 : int state) {
559 34892 : f->data[id] = (char) state;
560 34892 : }
561 :
562 2009 : void NaClClearCPUFeaturesX86(NaClCPUFeaturesX86 *features) {
563 6027 : memset(features, 0, sizeof(*features));
564 2009 : }
565 :
566 569 : void NaClCopyCPUFeaturesX86(NaClCPUFeaturesX86 *target,
567 569 : const NaClCPUFeaturesX86 *source) {
568 1707 : memcpy(target, source, sizeof(*target));
569 569 : }
570 :
571 1415 : int NaClArchSupportedX86(const NaClCPUFeaturesX86 *f) {
572 1415 : return (NaClGetCPUFeatureX86(f, NaClCPUFeatureX86_CPUIDSupported) &&
573 1415 : NaClGetCPUFeatureX86(f, NaClCPUFeatureX86_CPUSupported));
574 1415 : }
|