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 : /*
8 : * vcpuid.c
9 : *
10 : * Verify correctness of CPUID implementation.
11 : *
12 : * This uses shell status code to indicate its result; non-zero return
13 : * code indicates the CPUID instruction is not implemented or not
14 : * implemented correctly.
15 : *
16 : * The asm_ tests use inline assembler, which is not supported on
17 : * 64-bit Windows. We use #if to prevent these routines from being
18 : * provided on 64-bit Windows.
19 : *
20 : * TODO(bradchen): test on a machine with SSE4.
21 : */
22 : #include "native_client/src/include/portability.h"
23 : #include <stdio.h>
24 : #include <string.h>
25 : #include <assert.h>
26 : #include <stdlib.h>
27 : #include <setjmp.h>
28 : #include <signal.h>
29 : #include "native_client/src/trusted/cpu_features/arch/x86/cpu_x86.h"
30 : #include "native_client/src/trusted/platform_qualify/nacl_cpuwhitelist.h"
31 :
32 : /* MAGIC_CONST is a 32 bit constant, somewhat arbitrarily choosen. */
33 : /* The value should be a valid (i.e. not denormal single-precision */
34 : /* float; otherwise unexpected FP exceptions are possible. */
35 : const int kMagicConst = 0xc01df00d;
36 : const int kMagicConst_ROUNDSS = 0xc0000000;
37 : const int kMagicConst_POPCNT = 13;
38 : const int kMagicConst_CRC32 = 0xb906c3ea;
39 :
40 : #if !(NACL_WINDOWS && (NACL_BUILD_SUBARCH == 64))
41 : static int asm_HasMMX(void) {
42 1 : volatile int before, after;
43 1 : before = kMagicConst;
44 : #if defined(__GNUC__)
45 1 : __asm__ volatile("mov %1, %%eax \n\t"
46 : "xor %%ecx, %%ecx \n\t"
47 : "movd %%eax, %%mm0 \n\t" /* copy eax into mm0 (MMX) */
48 : "movd %%mm0, %%ecx \n\t" /* copy mm0 into ecx (MMX) */
49 : "mov %%ecx, %0 \n\t"
50 : : "=g" (after)
51 : : "m" (before)
52 : : "eax", "ecx", "mm0" );
53 : #elif NACL_WINDOWS
54 : __asm {
55 : mov eax, before
56 : xor ecx, ecx
57 : movd mm0, eax
58 : movd ecx, mm0
59 : mov after, ecx
60 : }
61 : #else
62 : # error Unsupported platform
63 : #endif
64 1 : return (after == kMagicConst);
65 : }
66 :
67 : /* TODO(brad): Test this routine on a machine with 3DNow */
68 : static int asm_Has3DNow(void) {
69 0 : volatile int before, after;
70 0 : before = kMagicConst;
71 : #if defined(__GNUC__)
72 0 : __asm__ volatile("mov %1, %%eax \n\t"
73 : "movd %%eax, %%mm0 \n\t" /* copy eax into mm0 (MMX) */
74 : "pfadd %%mm0, %%mm0 \n\t" /* 3DNow! */
75 : "movd %%mm0, %%ecx \n\t" /* copy mm0 into ecx (MMX) */
76 : "mov %%ecx, %0 \n\t"
77 : "emms \n\t"
78 : : "=g" (after)
79 : : "m" (before)
80 : : "%eax", "%ecx");
81 : #elif NACL_WINDOWS
82 : __asm {
83 : mov eax, before
84 : movd mm0, eax
85 : pfadd mm0, mm0
86 : movd ecx, mm0
87 : mov after, ecx
88 : emms
89 : }
90 : #else
91 : # error Unsupported platform
92 : #endif
93 0 : return (after == kMagicConst + 0x800000);
94 : }
95 :
96 :
97 : static int asm_HasSSE(void) {
98 1 : volatile int before, after;
99 1 : before = kMagicConst;
100 1 : after = 0;
101 : #if defined(__GNUC__)
102 1 : __asm__ volatile("movss %1, %%xmm0 \n\t"
103 : /* copy before into xmm0 (SSE2) */
104 : "movss %%xmm0, %0 \n\t" /* copy xmm0 into after (SSE) */
105 : : "=g" (after)
106 : : "m" (before)
107 : : "xmm0" );
108 : #elif NACL_WINDOWS
109 : __asm {
110 : movss xmm0, before
111 : movss after, xmm0
112 : }
113 : #else
114 : # error Unsupported platform
115 : #endif
116 1 : return (after == kMagicConst);
117 : }
118 :
119 : static int asm_HasSSE2(void) {
120 1 : volatile int before, after;
121 1 : before = kMagicConst;
122 : #if defined(__GNUC__)
123 1 : __asm__ volatile("mov %1, %%eax \n\t"
124 : "xor %%ecx, %%ecx \n\t"
125 : "movd %%eax, %%xmm0 \n\t" /* copy eax into xmm0 (SSE2) */
126 : "movd %%xmm0, %%ecx \n\t" /* copy xmm0 into ecx (SSE2) */
127 : "mov %%ecx, %0 \n\t"
128 : : "=g" (after)
129 : : "m" (before)
130 : : "eax", "ecx", "xmm0");
131 : #elif NACL_WINDOWS
132 : __asm {
133 : mov eax, before
134 : xor ecx, ecx
135 : movd xmm0, eax
136 : movd ecx, xmm0
137 : mov after, ecx
138 : }
139 : #else
140 : # error Unsupported platform
141 : #endif
142 1 : return (after == kMagicConst);
143 : }
144 :
145 : static int asm_HasSSE3(void) {
146 1 : volatile int before, after;
147 1 : after = 0;
148 1 : before = kMagicConst;
149 : #if defined(__GNUC__)
150 1 : __asm__ volatile("mov %1, %%eax \n\t"
151 : "movd %%eax, %%xmm0 \n\t"
152 : "movddup %%xmm0, %%xmm1 \n\t"
153 : "movd %%xmm1, %%ecx \n\t"
154 : "mov %%ecx, %0 \n\t"
155 : : "=g" (after)
156 : : "m" (before)
157 : : "eax", "ecx", "xmm0", "xmm1" );
158 : #elif NACL_WINDOWS
159 : __asm {
160 : mov eax, before
161 : movd xmm0, eax
162 : movddup xmm1, xmm0
163 : movd ecx, xmm1
164 : mov after, ecx
165 : }
166 : #else
167 : # error Unsupported platform
168 : #endif
169 1 : return (after == kMagicConst);
170 : }
171 :
172 : static int asm_HasSSSE3(void) {
173 1 : volatile int after;
174 : #if defined(__GNUC__)
175 1 : __asm__ volatile("mov $0x0000ffff, %%eax \n\t"
176 : "xor %%ecx, %%ecx \n\t"
177 : "movd %%eax, %%mm0 \n\t" /* copy eax into mm0 (MMX) */
178 : /* pabsw will change 0x0000ffff to 0x00000001 */
179 : "pabsw %%mm0, %%mm0 \n\t"
180 : "movd %%mm0, %%ecx \n\t" /* copy mm0 into ecx (MMX) */
181 : "mov %%ecx, %0 \n\t"
182 : "emms \n\t"
183 : : "=g" (after)
184 : :
185 : : "eax", "ecx", "mm0");
186 : #elif NACL_WINDOWS
187 : __asm {
188 : mov eax, 0x0000ffff
189 : xor ecx, ecx
190 : movd mm0, eax
191 : pabsw mm0, mm0
192 : movd ecx, mm0
193 : mov after, ecx
194 : emms
195 : }
196 : #else
197 : # error Unsupported platform
198 : #endif
199 1 : return (after == 1);
200 : }
201 :
202 : static int asm_HasSSE41(void) {
203 1 : volatile int before, after;
204 1 : before = kMagicConst;
205 : #if defined(__GNUC__)
206 1 : __asm__ volatile("mov %1, %%eax \n\t"
207 : "movd %%eax, %%xmm0 \n\t"
208 : "roundss $0, %%xmm0, %%xmm0 \n\t"
209 : "movd %%xmm0, %%ecx \n\t"
210 : "mov %%ecx, %0 \n\t"
211 : : "=g" (after)
212 : : "g" (before)
213 : : "eax", "ecx", "xmm0" );
214 : #elif NACL_WINDOWS
215 : __asm {
216 : mov eax, before
217 : movd xmm0, eax
218 : /*
219 : * NOTE: Use _emit for older MSFT compilers that don't know of SSE4
220 : * 66 0f 3a 0a c0 00 roundss $0, xmm0, xmm0
221 : */
222 : _emit 0x66
223 : _emit 0x0f
224 : _emit 0x3a
225 : _emit 0x0a
226 : _emit 0xc0
227 : _emit 0x00
228 : movd ecx, xmm0
229 : mov after, ecx
230 : }
231 : #else
232 : # error Unsupported platform
233 : #endif
234 1 : return (after == kMagicConst_ROUNDSS);
235 : }
236 :
237 : static int asm_HasSSE42(void) {
238 1 : volatile int after;
239 : #if defined(__GNUC__)
240 1 : __asm__ volatile("mov $0x0000ffff, %%eax \n\t"
241 : "xor %%ecx, %%ecx \n\t"
242 : "crc32 %%eax, %%ecx \n\t"
243 : "mov %%ecx, %0 \n\t"
244 : : "=g" (after)
245 : :
246 : : "eax", "ecx" );
247 : #elif NACL_WINDOWS
248 : __asm {
249 : mov eax, 0x0000ffff
250 : xor ecx, ecx
251 : /*
252 : * NOTE: Use _emit for older MSFT compilers that don't know of SSE4
253 : * f2 0f 38 f1 c8 crc32 ecx, eax
254 : */
255 : _emit 0xf2
256 : _emit 0x0f
257 : _emit 0x38
258 : _emit 0xf1
259 : _emit 0xc8
260 : mov after, ecx
261 : }
262 : #else
263 : # error Unsupported platform
264 : #endif
265 1 : return (after == kMagicConst_CRC32);
266 : }
267 :
268 : static int asm_HasPOPCNT(void) {
269 1 : volatile int before, after;
270 1 : before = kMagicConst;
271 : #if defined(__GNUC__)
272 1 : __asm__ volatile("mov %1, %%eax \n\t"
273 : "xor %%ecx, %%ecx \n\t"
274 : "popcnt %%eax, %%ecx \n\t"
275 : "mov %%ecx, %0 \n\t"
276 : : "=g" (after)
277 : : "m" (before)
278 : : "eax", "ecx");
279 : #elif NACL_WINDOWS
280 : __asm {
281 : mov eax, before
282 : xor ecx, ecx
283 : /*
284 : * NOTE: Use _emit for older MSFT compilers that don't know of SSE4
285 : * f3 0f b8 c8 popcnt ecx, eax
286 : */
287 : _emit 0xf3
288 : _emit 0x0f
289 : _emit 0xb8
290 : _emit 0xc8
291 : mov after, ecx
292 : }
293 : #else
294 : # error Unsupported platform
295 : #endif
296 1 : return (after == kMagicConst_POPCNT);
297 : }
298 :
299 : static int asm_HasCMOV(void) {
300 1 : volatile int before, after;
301 1 : before = kMagicConst;
302 : #if defined(__GNUC__)
303 1 : __asm__ volatile("mov %1, %%eax \n\t"
304 : "xor %%ecx, %%ecx \n\t"
305 : "add $0, %%eax \n\t" /* to set condition code */
306 : "cmovnz %%eax, %%ecx \n\t"
307 : "mov %%ecx, %0 \n\t"
308 : : "=g" (after)
309 : : "m" (before)
310 : : "eax", "ecx");
311 : #elif NACL_WINDOWS
312 : __asm {
313 : mov eax, before
314 : xor ecx, ecx
315 : add eax, 0
316 : cmovnz ecx, eax
317 : mov after, ecx
318 : }
319 : #else
320 : # error Unsupported platform
321 : #endif
322 1 : return (after == kMagicConst);
323 : }
324 :
325 : static int asm_HasTSC(void) {
326 1 : uint32_t _eax, _edx;
327 1 : _eax = 0;
328 1 : _edx = 0;
329 :
330 : #if defined(__GNUC__)
331 1 : __asm__ volatile("rdtsc"
332 : : "=a" (_eax), "=d" (_edx)
333 : );
334 : #elif NACL_WINDOWS
335 : __asm {
336 : rdtsc
337 : mov _eax, eax
338 : mov _edx, ecx
339 : }
340 : #else
341 : # error Unsupported platform
342 : #endif
343 1 : return ((_eax | _edx) != 0);
344 : }
345 :
346 : static int asm_HasX87(void) {
347 : #if defined(__GNUC__)
348 1 : __asm__ volatile("fld1 \n\t"
349 : "fstp %st(0) \n\t");
350 1 : return 1;
351 : #elif NACL_WINDOWS
352 : __asm {
353 : fld1
354 : fstp st(0)
355 : }
356 : return 1;
357 : #else
358 : # error Unsupported platform
359 : #endif
360 : }
361 :
362 : #if 0
363 : /* I'm having some trouble with my cmpxchg8b instruction */
364 : static int asm_HasCX8(void) {
365 : uint32_t _eax, _ebx, _ecx, _edx;
366 : uint64_t foo64 = 0;
367 : _eax = 0;
368 : _edx = 0;
369 : _ebx = 0;
370 : _ecx = kMagicConst;
371 :
372 : #if defined(__GNUC__)
373 : __asm__ volatile("cmpxchg8b %0 \n\t"
374 : : "=g" (foo64), "=b" (_ebx), "=c" (_ecx)
375 : : "a" (_eax), "d" (_edx) );
376 : #elif NACL_WINDOWS
377 : __asm {
378 : }
379 : #else
380 : # error Unsupported platform
381 : #endif
382 : printf("ebx == %x ecx == %x\n", _ebx, _ecx);
383 : return (foo64 == (uint64_t)kMagicConst);
384 : }
385 : #endif /* 0 */
386 : #endif /* 64-bit Windows */
387 :
388 : #if (NACL_LINUX || NACL_OSX)
389 : /* Linux/MacOS signal handling code, for trapping illegal instruction faults */
390 : static int sawbadinstruction = 0;
391 : static struct sigaction crash_detect;
392 : static int signum;
393 :
394 : sigjmp_buf crash_load;
395 :
396 0 : void handler_load(int signum) {
397 0 : siglongjmp(crash_load, signum);
398 0 : }
399 :
400 11 : void all_sigs(struct sigaction *new_action,
401 11 : struct sigaction *prev_action) {
402 11 : int sig;
403 11 : struct sigaction ign;
404 :
405 704 : for (sig = SIGHUP; sig < NSIG; ++sig) {
406 341 : switch (sig) {
407 : case SIGWINCH:
408 : case SIGCHLD:
409 : case SIGTSTP:
410 33 : break;
411 : default:
412 308 : (void) sigaction(sig, new_action, prev_action);
413 308 : break;
414 : }
415 341 : }
416 11 : ign.sa_handler = SIG_DFL;
417 11 : sigemptyset(&ign.sa_mask);
418 11 : ign.sa_flags = SA_ONSTACK;
419 11 : (void) sigaction(SIGWINCH, &ign, 0);
420 11 : (void) sigaction(SIGCHLD, &ign, 0);
421 11 : (void) sigaction(SIGTSTP, &ign, 0);
422 11 : }
423 :
424 : static void SignalInit(void) {
425 11 : sawbadinstruction = 0;
426 11 : crash_detect.sa_handler = handler_load;
427 11 : sigemptyset(&crash_detect.sa_mask);
428 11 : crash_detect.sa_flags = SA_RESETHAND;
429 11 : all_sigs(&crash_detect, 0);
430 11 : }
431 :
432 : static void SetSawBadInst(void) {
433 0 : sawbadinstruction = 1;
434 0 : }
435 :
436 : static int SawBadInst(void) {
437 22 : return sawbadinstruction != 0;
438 : }
439 :
440 : /*
441 : * DoTest tests for a particular CPU feature using thetest().
442 : * It returns 0 if the feature is present, 1 if it is not.
443 : */
444 11 : static int DoTest(int (*thetest)(void), const char *s) {
445 11 : SignalInit();
446 11 : if (0 != (signum = sigsetjmp(crash_load, 1))) {
447 0 : SetSawBadInst();
448 0 : if (SIGILL == signum) {
449 0 : fprintf(stderr, "%s: illegal instruction\n", s);
450 0 : } else {
451 0 : fprintf(stderr, "%s: signal %d received\n", s, signum);
452 : }
453 0 : }
454 11 : if (! SawBadInst()) {
455 11 : int hasfeature = thetest();
456 22 : if (hasfeature && (! SawBadInst())) {
457 11 : printf("[Has %s]\n", s);
458 11 : return 0;
459 : }
460 0 : }
461 0 : printf("no %s\n", s);
462 0 : return 1;
463 11 : }
464 : #elif NACL_WINDOWS
465 : /* Windows signal handling code, for trapping illegal instruction faults */
466 : /*
467 : * DoTest tests for a particular CPU feature using thetest().
468 : * It returns 0 if the feature is present, 1 if it is not.
469 : */
470 : static int DoTest(int (*thetest)(void), const char *s) {
471 : int hasfeature = 0;
472 :
473 : __try {
474 : hasfeature = thetest();
475 : } __except (GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION ?
476 : EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
477 : printf("Saw exception\n");
478 : hasfeature = 0;
479 : }
480 : if (hasfeature) {
481 : printf("[Has %s]\n", s);
482 : return 0;
483 : } else {
484 : printf("no %s\n", s);
485 : return 1;
486 : }
487 : }
488 : #else
489 : # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
490 : #endif
491 :
492 12 : static int DoCPUFeatureTest(NaClCPUFeaturesX86 *features,
493 12 : NaClCPUFeatureX86ID id,
494 12 : int (*thetest)(void)) {
495 12 : if (NaClGetCPUFeatureX86(features, id))
496 11 : return DoTest(thetest, NaClGetCPUFeatureX86Name(id));
497 : else
498 1 : return 0;
499 12 : }
500 :
501 0 : static void PrintFail(const char *why) {
502 0 : fprintf(stderr, "ERROR: %s.\n", why);
503 0 : fprintf(stderr, "Google Native Client cannot continue.\n");
504 0 : }
505 :
506 : #define TEST_NEGATIVE_CASE 0
507 : int CPUIDImplIsValid(void) {
508 2 : int rcode = 0;
509 1 : NaClCPUFeaturesX86 cpuf;
510 1 : NaClGetCurrentCPUFeaturesX86((NaClCPUFeatures *) &cpuf);
511 :
512 1 : if (!NaClGetCPUFeatureX86(&cpuf, NaClCPUFeatureX86_CPUIDSupported)) {
513 0 : PrintFail("CPUID not implemented");
514 0 : return 0;
515 : }
516 1 : if (!NaClGetCPUFeatureX86(&cpuf, NaClCPUFeatureX86_CPUSupported)) {
517 0 : PrintFail("CPU not supported");
518 0 : return 0;
519 : }
520 :
521 : #if (NACL_WINDOWS && (NACL_BUILD_SUBARCH == 64))
522 : /* Unfortunately the asm_ tests will not work on 64-bit Windows */
523 : return 1;
524 : #else
525 1 : rcode |= DoCPUFeatureTest(&cpuf, NaClCPUFeatureX86_x87, asm_HasX87);
526 1 : rcode |= DoCPUFeatureTest(&cpuf, NaClCPUFeatureX86_MMX, asm_HasMMX);
527 1 : rcode |= DoCPUFeatureTest(&cpuf, NaClCPUFeatureX86_SSE, asm_HasSSE);
528 1 : rcode |= DoCPUFeatureTest(&cpuf, NaClCPUFeatureX86_SSE2, asm_HasSSE2);
529 1 : rcode |= DoCPUFeatureTest(&cpuf, NaClCPUFeatureX86_3DNOW, asm_Has3DNow);
530 1 : rcode |= DoCPUFeatureTest(&cpuf, NaClCPUFeatureX86_SSE3, asm_HasSSE3);
531 1 : rcode |= DoCPUFeatureTest(&cpuf, NaClCPUFeatureX86_SSSE3, asm_HasSSSE3);
532 1 : rcode |= DoCPUFeatureTest(&cpuf, NaClCPUFeatureX86_SSE41, asm_HasSSE41);
533 1 : rcode |= DoCPUFeatureTest(&cpuf, NaClCPUFeatureX86_SSE42, asm_HasSSE42);
534 1 : rcode |= DoCPUFeatureTest(&cpuf, NaClCPUFeatureX86_POPCNT, asm_HasPOPCNT);
535 1 : rcode |= DoCPUFeatureTest(&cpuf, NaClCPUFeatureX86_CMOV, asm_HasCMOV);
536 1 : rcode |= DoCPUFeatureTest(&cpuf, NaClCPUFeatureX86_TSC, asm_HasTSC);
537 :
538 : #if TEST_NEGATIVE_CASE
539 : printf("TESTING: simulating invalid CPUID implementation\n");
540 : rcode |= DoTest(asm_HasSSSE3, "SSSE3");
541 : rcode |= DoTest(asm_HasSSE41, "SSE41");
542 : rcode |= DoTest(asm_HasSSE42, "SSE42");
543 : rcode |= DoTest(asm_HasPOPCNT, "POPCNT");
544 : rcode |= DoTest(asm_HasCMOV, "CMOV");
545 : #endif
546 : #endif /* 64-bit Windows */
547 : /*
548 : * TODO(brad): implement the rest of these tests
549 : * rcode |= DoCPUFeatureTest(&cpuf, NaClCPUFeatureX86_CX8, asm_HasCX8);
550 : * rcode |= DoCPUFeatureTest(&cpuf, NaClCPUFeatureX86_CX16, asm_HasCX16);
551 : * DoTest(asm_HasSSE4a, "SSE4a");
552 : * DoTest(asm_HasEMMX, "EMMX");
553 : * DoTest(asm_HasE3DNow, "E3DNow");
554 : * what about LZCNT?
555 : */
556 1 : if (rcode != 0) {
557 0 : PrintFail("CPUID not implemented correctly.");
558 0 : return 0;
559 : }
560 1 : printf("[CPUID implementation looks okay]\n");
561 1 : return 1;
562 1 : }
|