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.
35 * Verify correctness of CPUID implementation.
37 * This uses shell status code to indicate its result; non-zero return
38 * code indicates the CPUID instruction is not implemented or not
39 * implemented correctly.
41 * TODO: test on a machine with SSE4.
43 #include "native_client/include/portability.h"
50 #include "native_client/ncv/nacl_cpuid.h"
51 #include "native_client/platform_qual_test/nacl_cpuwhitelist.h"
53 /* MAGIC_CONST is a 32 bit constant, somewhat arbitrarily choosen. */
54 /* The value should be a valid (i.e. not denormal single-precision */
55 /* float; otherwise unexpected FP exceptions are possible. */
56 const int kMagicConst
= 0xc01df00d;
57 const int kMagicConst_ROUNDSS
= 0xc0000000;
58 const int kMagicConst_POPCNT
= 13;
59 const int kMagicConst_CRC32
= 0xb906c3ea;
61 static int asm_HasCPUID() {
62 volatile int before
, after
, result
;
64 #if (NACL_LINUX || NACL_OSX)
65 asm volatile("pushfl \n\t" /* save EFLAGS to eax */
67 "movl %%eax, %0 \n\t" /* remember EFLAGS in %0 */
68 "xor $0x00200000, %%eax\n\t" /* toggle bit 21 */
69 "push %%eax \n\t" /* write eax to EFLAGS */
71 "pushfl \n\t" /* save EFLAGS to %1 */
73 : "=g" (before
), "=g" (after
)
78 # error Building with GCC on Windows is not supported.
91 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
93 result
= (before
^ after
) & 0x0200000;
97 static int asm_HasMMX() {
98 volatile int before
, after
;
100 #if (NACL_LINUX || NACL_OSX)
101 asm volatile("mov %1, %%eax \n\t"
102 "xor %%ecx, %%ecx \n\t"
103 "movd %%eax, %%mm0 \n\t" /* copy eax into mm0 (MMX) */
104 "movd %%mm0, %%ecx \n\t" /* copy mm0 into ecx (MMX) */
108 : "eax", "ecx", "mm0" );
118 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
120 return (after
== kMagicConst
);
123 /* TODO: Test this routine on a machine with 3DNow */
124 static int asm_Has3DNow() {
125 volatile int before
, after
;
126 before
= kMagicConst
;
127 #if (NACL_LINUX || NACL_OSX)
128 asm volatile("mov %1, %%eax \n\t"
129 "movd %%eax, %%mm0 \n\t" /* copy eax into mm0 (MMX) */
130 "pfadd %%mm0, %%mm0 \n\t" /* 3DNow! */
131 "movd %%mm0, %%ecx \n\t" /* copy mm0 into ecx (MMX) */
147 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
149 return (after
== kMagicConst
+ 0x800000);
153 static int asm_HasSSE() {
154 volatile int before
, after
;
155 before
= kMagicConst
;
157 #if (NACL_LINUX || NACL_OSX)
158 asm volatile("movss %1, %%xmm0 \n\t" /* copy before into xmm0 (SSE2) */
159 "movss %%xmm0, %0 \n\t" /* copy xmm0 into after (SSE) */
169 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
171 return (after
== kMagicConst
);
174 static int asm_HasSSE2() {
175 volatile int before
, after
;
176 before
= kMagicConst
;
177 #if (NACL_LINUX || NACL_OSX)
178 asm volatile("mov %1, %%eax \n\t"
179 "xor %%ecx, %%ecx \n\t"
180 "movd %%eax, %%xmm0 \n\t" /* copy eax into xmm0 (SSE2) */
181 "movd %%xmm0, %%ecx \n\t" /* copy xmm0 into ecx (SSE2) */
185 : "eax", "ecx", "xmm0");
195 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
197 return (after
== kMagicConst
);
200 static int asm_HasSSE3() {
201 volatile int before
, after
;
203 before
= kMagicConst
;
204 #if (NACL_LINUX || NACL_OSX)
205 asm volatile("mov %1, %%eax \n\t"
206 "movd %%eax, %%xmm0 \n\t"
207 "movddup %%xmm0, %%xmm1 \n\t"
208 "movd %%xmm1, %%ecx \n\t"
212 : "eax", "ecx", "xmm0", "xmm1" );
222 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
224 return (after
== kMagicConst
);
227 static int asm_HasSSSE3() {
229 #if (NACL_LINUX || NACL_OSX)
230 asm volatile("mov $0x0000ffff, %%eax \n\t"
231 "xor %%ecx, %%ecx \n\t"
232 "movd %%eax, %%mm0 \n\t" /* copy eax into mm0 (MMX) */
233 /* pabsw will change 0x0000ffff to 0x00000001 */
234 "pabsw %%mm0, %%mm0 \n\t"
235 "movd %%mm0, %%ecx \n\t" /* copy mm0 into ecx (MMX) */
240 : "eax", "ecx", "mm0");
252 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
257 static int asm_HasSSE41() {
258 volatile int before
, after
;
259 before
= kMagicConst
;
260 #if (NACL_LINUX || NACL_OSX)
261 asm volatile("mov %1, %%eax \n\t"
262 "movd %%eax, %%xmm0 \n\t"
263 "roundss $0, %%xmm0, %%xmm0 \n\t"
264 "movd %%xmm0, %%ecx \n\t"
268 : "eax", "ecx", "xmm0" );
273 // NOTE: Use _emit for older MSFT compilers that don't know of SSE4
274 // 66 0f 3a 0a c0 00 roundss $0, xmm0, xmm0
285 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
287 return (after
== kMagicConst_ROUNDSS
);
290 static int asm_HasSSE42() {
292 #if (NACL_LINUX || NACL_OSX)
293 asm volatile("mov $0x0000ffff, %%eax \n\t"
294 "xor %%ecx, %%ecx \n\t"
295 "crc32 %%eax, %%ecx \n\t"
304 // NOTE: Use _emit for older MSFT compilers that don't know of SSE4
305 // f2 0f 38 f1 c8 crc32 ecx, eax
314 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
316 return (after
== kMagicConst_CRC32
);
319 static int asm_HasPOPCNT() {
320 volatile int before
, after
;
321 before
= kMagicConst
;
322 #if (NACL_LINUX || NACL_OSX)
323 asm volatile("mov %1, %%eax \n\t"
324 "xor %%ecx, %%ecx \n\t"
325 "popcnt %%eax, %%ecx \n\t"
334 // NOTE: Use _emit for older MSFT compilers that don't know of SSE4
335 // f3 0f b8 c8 popcnt ecx, eax
343 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
345 return (after
== kMagicConst_POPCNT
);
348 static int asm_HasCMOV() {
349 volatile int before
, after
;
350 before
= kMagicConst
;
351 #if (NACL_LINUX || NACL_OSX)
352 asm volatile("mov %1, %%eax \n\t"
353 "xor %%ecx, %%ecx \n\t"
354 "add $0, %%eax \n\t" /* to set condition code */
355 "cmovnz %%eax, %%ecx \n\t"
369 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
371 return (after
== kMagicConst
);
374 static int asm_HasTSC() {
379 #if (NACL_LINUX || NACL_OSX)
381 : "=a" (_eax
), "=d" (_edx
)
390 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
392 return ((_eax
| _edx
) != 0);
395 static int asm_HasX87() {
396 #if (NACL_LINUX || NACL_OSX)
397 asm volatile("fld1 \n\t"
407 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
412 /* I'm having some trouble with my cmpxchg8b instruction */
413 static int asm_HasCX8() {
414 uint32_t _eax
, _ebx
, _ecx
, _edx
;
421 #if (NACL_LINUX || NACL_OSX)
422 asm volatile("cmpxchg8b %0 \n\t"
423 : "=g" (foo64
), "=b" (_ebx
), "=c" (_ecx
)
424 : "a" (_eax
), "d" (_edx
) );
429 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
431 printf("ebx == %x ecx == %x\n", _ebx
, _ecx
);
432 return (foo64
== (uint64_t)kMagicConst
);
436 #if (NACL_LINUX || NACL_OSX)
437 /* Linux/MacOS signal handling code, for trapping illegal instruction faults */
438 static int sawbadinstruction
= 0;
439 static struct sigaction crash_detect
;
442 sigjmp_buf crash_load
;
444 void handler_load(int signum
) {
445 siglongjmp(crash_load
, signum
);
448 void all_sigs(struct sigaction
*new_action
,
449 struct sigaction
*prev_action
) {
451 struct sigaction ign
;
453 for (sig
= SIGHUP
; sig
< NSIG
; ++sig
) {
460 (void) sigaction(sig
, new_action
, prev_action
);
464 ign
.sa_handler
= SIG_DFL
;
465 sigemptyset(&ign
.sa_mask
);
466 ign
.sa_flags
= SA_ONSTACK
;
467 (void) sigaction(SIGWINCH
, &ign
, 0);
468 (void) sigaction(SIGCHLD
, &ign
, 0);
469 (void) sigaction(SIGTSTP
, &ign
, 0);
472 static void SignalInit() {
473 sawbadinstruction
= 0;
474 crash_detect
.sa_handler
= handler_load
;
475 sigemptyset(&crash_detect
.sa_mask
);
476 crash_detect
.sa_flags
= SA_RESETHAND
;
477 all_sigs(&crash_detect
, 0);
480 static void SetSawBadInst() {
481 sawbadinstruction
= 1;
484 static int SawBadInst() {
485 return sawbadinstruction
!= 0;
489 * DoTest tests for a particular CPU feature using thetest().
490 * It returns 0 if the feature is present, 1 if it is not.
492 static int DoTest(int (*thetest
)(), char *s
) {
494 if (0 != (signum
= sigsetjmp(crash_load
, 1))) {
496 if (SIGILL
== signum
) {
497 fprintf(stderr
, "%s: illegal instruction\n", s
);
499 fprintf(stderr
, "%s: signal %d received\n", s
, signum
);
502 if (! SawBadInst()) {
503 int hasfeature
= thetest();
504 if (hasfeature
&& (! SawBadInst())) {
505 printf("[Has %s]\n", s
);
509 printf("no %s\n", s
);
513 /* Windows signal handling code, for trapping illegal instruction faults */
515 * DoTest tests for a particular CPU feature using thetest().
516 * It returns 0 if the feature is present, 1 if it is not.
518 static int DoTest(int (*thetest
)(), char *s
) {
522 hasfeature
= thetest();
523 } __except (GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION
?
524 EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
) {
525 printf("Saw exception\n");
529 printf("[Has %s]\n", s
);
532 printf("no %s\n", s
);
537 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
540 static void PrintFail(const char *why
) {
541 fprintf(stderr
, "ERROR: %s.\n", why
);
542 fprintf(stderr
, "Google Native Client cannot continue.\n");
545 #define TEST_NEGATIVE_CASE 0
546 int CPUIDImplIsValid() {
549 GetCPUFeatures(&cpuf
);
552 PrintFail("CPUID not implemented");
555 rcode
= DoTest(asm_HasCPUID
, "CPUID"); /* This test is redundant. */
556 /* CPUID feature is required/mandatory */
558 PrintFail("CPUID not implemented");
562 if (cpuf
.f_x87
) rcode
|= DoTest(asm_HasX87
, "x87");
563 if (cpuf
.f_MMX
) rcode
|= DoTest(asm_HasMMX
, "MMX");
564 if (cpuf
.f_SSE
) rcode
|= DoTest(asm_HasSSE
, "SSE");
565 if (cpuf
.f_SSE2
) rcode
|= DoTest(asm_HasSSE2
, "SSE2");
566 if (cpuf
.f_3DNOW
) rcode
|= DoTest(asm_Has3DNow
, "3DNow");
567 if (cpuf
.f_SSE3
) rcode
|= DoTest(asm_HasSSE3
, "SSE3");
568 if (cpuf
.f_SSSE3
) rcode
|= DoTest(asm_HasSSSE3
, "SSSE3");
569 if (cpuf
.f_SSE41
) rcode
|= DoTest(asm_HasSSE41
, "SSE41");
570 if (cpuf
.f_SSE42
) rcode
|= DoTest(asm_HasSSE42
, "SSE42");
571 if (cpuf
.f_POPCNT
) rcode
|= DoTest(asm_HasPOPCNT
, "POPCNT");
572 if (cpuf
.f_CMOV
) rcode
|= DoTest(asm_HasCMOV
, "CMOV");
573 if (cpuf
.f_TSC
) rcode
|= DoTest(asm_HasTSC
, "TSC");
575 #if TEST_NEGATIVE_CASE
576 printf("TESTING: simulating invalid CPUID implementation\n");
577 rcode
|= DoTest(asm_HasSSSE3
, "SSSE3");
578 rcode
|= DoTest(asm_HasSSE41
, "SSE41");
579 rcode
|= DoTest(asm_HasSSE42
, "SSE42");
580 rcode
|= DoTest(asm_HasPOPCNT
, "POPCNT");
581 rcode
|= DoTest(asm_HasCMOV
, "CMOV");
584 // TODO: implement the rest of these tests
585 // if (cpuf.f_CX8) rcode |= DoTest(asm_HasCX8, "CMPXCHG8B");
586 // if (cpuf.f_CX16) rcode |= DoTest(asm_HasCX16, "CMPXCHG16B");
587 // DoTest(asm_HasSSE4a, "SSE4a");
588 // DoTest(asm_HasEMMX, "EMMX");
589 // DoTest(asm_HasE3DNow, "E3DNow");
592 PrintFail("CPUID not implemented correctly.");
595 printf("[CPUID implementation looks okay]\n");