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"
159 /* copy before into xmm0 (SSE2) */
160 "movss %%xmm0, %0 \n\t" /* copy xmm0 into after (SSE) */
170 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
172 return (after
== kMagicConst
);
175 static int asm_HasSSE2() {
176 volatile int before
, after
;
177 before
= kMagicConst
;
178 #if (NACL_LINUX || NACL_OSX)
179 __asm__
volatile("mov %1, %%eax \n\t"
180 "xor %%ecx, %%ecx \n\t"
181 "movd %%eax, %%xmm0 \n\t" /* copy eax into xmm0 (SSE2) */
182 "movd %%xmm0, %%ecx \n\t" /* copy xmm0 into ecx (SSE2) */
186 : "eax", "ecx", "xmm0");
196 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
198 return (after
== kMagicConst
);
201 static int asm_HasSSE3() {
202 volatile int before
, after
;
204 before
= kMagicConst
;
205 #if (NACL_LINUX || NACL_OSX)
206 __asm__
volatile("mov %1, %%eax \n\t"
207 "movd %%eax, %%xmm0 \n\t"
208 "movddup %%xmm0, %%xmm1 \n\t"
209 "movd %%xmm1, %%ecx \n\t"
213 : "eax", "ecx", "xmm0", "xmm1" );
223 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
225 return (after
== kMagicConst
);
228 static int asm_HasSSSE3() {
230 #if (NACL_LINUX || NACL_OSX)
231 __asm__
volatile("mov $0x0000ffff, %%eax \n\t"
232 "xor %%ecx, %%ecx \n\t"
233 "movd %%eax, %%mm0 \n\t" /* copy eax into mm0 (MMX) */
234 /* pabsw will change 0x0000ffff to 0x00000001 */
235 "pabsw %%mm0, %%mm0 \n\t"
236 "movd %%mm0, %%ecx \n\t" /* copy mm0 into ecx (MMX) */
241 : "eax", "ecx", "mm0");
253 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
258 static int asm_HasSSE41() {
259 volatile int before
, after
;
260 before
= kMagicConst
;
261 #if (NACL_LINUX || NACL_OSX)
262 __asm__
volatile("mov %1, %%eax \n\t"
263 "movd %%eax, %%xmm0 \n\t"
264 "roundss $0, %%xmm0, %%xmm0 \n\t"
265 "movd %%xmm0, %%ecx \n\t"
269 : "eax", "ecx", "xmm0" );
275 * NOTE: Use _emit for older MSFT compilers that don't know of SSE4
276 * 66 0f 3a 0a c0 00 roundss $0, xmm0, xmm0
288 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
290 return (after
== kMagicConst_ROUNDSS
);
293 static int asm_HasSSE42() {
295 #if (NACL_LINUX || NACL_OSX)
296 __asm__
volatile("mov $0x0000ffff, %%eax \n\t"
297 "xor %%ecx, %%ecx \n\t"
298 "crc32 %%eax, %%ecx \n\t"
308 * NOTE: Use _emit for older MSFT compilers that don't know of SSE4
309 * f2 0f 38 f1 c8 crc32 ecx, eax
319 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
321 return (after
== kMagicConst_CRC32
);
324 static int asm_HasPOPCNT() {
325 volatile int before
, after
;
326 before
= kMagicConst
;
327 #if (NACL_LINUX || NACL_OSX)
328 __asm__
volatile("mov %1, %%eax \n\t"
329 "xor %%ecx, %%ecx \n\t"
330 "popcnt %%eax, %%ecx \n\t"
340 * NOTE: Use _emit for older MSFT compilers that don't know of SSE4
341 * f3 0f b8 c8 popcnt ecx, eax
350 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
352 return (after
== kMagicConst_POPCNT
);
355 static int asm_HasCMOV() {
356 volatile int before
, after
;
357 before
= kMagicConst
;
358 #if (NACL_LINUX || NACL_OSX)
359 __asm__
volatile("mov %1, %%eax \n\t"
360 "xor %%ecx, %%ecx \n\t"
361 "add $0, %%eax \n\t" /* to set condition code */
362 "cmovnz %%eax, %%ecx \n\t"
376 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
378 return (after
== kMagicConst
);
381 static int asm_HasTSC() {
386 #if (NACL_LINUX || NACL_OSX)
387 __asm__
volatile("rdtsc"
388 : "=a" (_eax
), "=d" (_edx
)
397 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
399 return ((_eax
| _edx
) != 0);
402 static int asm_HasX87() {
403 #if (NACL_LINUX || NACL_OSX)
404 __asm__
volatile("fld1 \n\t"
414 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
419 /* I'm having some trouble with my cmpxchg8b instruction */
420 static int asm_HasCX8() {
421 uint32_t _eax
, _ebx
, _ecx
, _edx
;
428 #if (NACL_LINUX || NACL_OSX)
429 __asm__
volatile("cmpxchg8b %0 \n\t"
430 : "=g" (foo64
), "=b" (_ebx
), "=c" (_ecx
)
431 : "a" (_eax
), "d" (_edx
) );
436 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
438 printf("ebx == %x ecx == %x\n", _ebx
, _ecx
);
439 return (foo64
== (uint64_t)kMagicConst
);
443 #if (NACL_LINUX || NACL_OSX)
444 /* Linux/MacOS signal handling code, for trapping illegal instruction faults */
445 static int sawbadinstruction
= 0;
446 static struct sigaction crash_detect
;
449 sigjmp_buf crash_load
;
451 void handler_load(int signum
) {
452 siglongjmp(crash_load
, signum
);
455 void all_sigs(struct sigaction
*new_action
,
456 struct sigaction
*prev_action
) {
458 struct sigaction ign
;
460 for (sig
= SIGHUP
; sig
< NSIG
; ++sig
) {
467 (void) sigaction(sig
, new_action
, prev_action
);
471 ign
.sa_handler
= SIG_DFL
;
472 sigemptyset(&ign
.sa_mask
);
473 ign
.sa_flags
= SA_ONSTACK
;
474 (void) sigaction(SIGWINCH
, &ign
, 0);
475 (void) sigaction(SIGCHLD
, &ign
, 0);
476 (void) sigaction(SIGTSTP
, &ign
, 0);
479 static void SignalInit() {
480 sawbadinstruction
= 0;
481 crash_detect
.sa_handler
= handler_load
;
482 sigemptyset(&crash_detect
.sa_mask
);
483 crash_detect
.sa_flags
= SA_RESETHAND
;
484 all_sigs(&crash_detect
, 0);
487 static void SetSawBadInst() {
488 sawbadinstruction
= 1;
491 static int SawBadInst() {
492 return sawbadinstruction
!= 0;
496 * DoTest tests for a particular CPU feature using thetest().
497 * It returns 0 if the feature is present, 1 if it is not.
499 static int DoTest(int (*thetest
)(), char *s
) {
501 if (0 != (signum
= sigsetjmp(crash_load
, 1))) {
503 if (SIGILL
== signum
) {
504 fprintf(stderr
, "%s: illegal instruction\n", s
);
506 fprintf(stderr
, "%s: signal %d received\n", s
, signum
);
509 if (! SawBadInst()) {
510 int hasfeature
= thetest();
511 if (hasfeature
&& (! SawBadInst())) {
512 printf("[Has %s]\n", s
);
516 printf("no %s\n", s
);
520 /* Windows signal handling code, for trapping illegal instruction faults */
522 * DoTest tests for a particular CPU feature using thetest().
523 * It returns 0 if the feature is present, 1 if it is not.
525 static int DoTest(int (*thetest
)(), char *s
) {
529 hasfeature
= thetest();
530 } __except (GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION
?
531 EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
) {
532 printf("Saw exception\n");
536 printf("[Has %s]\n", s
);
539 printf("no %s\n", s
);
544 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
547 static void PrintFail(const char *why
) {
548 fprintf(stderr
, "ERROR: %s.\n", why
);
549 fprintf(stderr
, "Google Native Client cannot continue.\n");
552 #define TEST_NEGATIVE_CASE 0
553 int CPUIDImplIsValid() {
556 GetCPUFeatures(&cpuf
);
559 PrintFail("CPUID not implemented");
562 rcode
= DoTest(asm_HasCPUID
, "CPUID"); /* This test is redundant. */
563 /* CPUID feature is required/mandatory */
565 PrintFail("CPUID not implemented");
569 if (cpuf
.f_x87
) rcode
|= DoTest(asm_HasX87
, "x87");
570 if (cpuf
.f_MMX
) rcode
|= DoTest(asm_HasMMX
, "MMX");
571 if (cpuf
.f_SSE
) rcode
|= DoTest(asm_HasSSE
, "SSE");
572 if (cpuf
.f_SSE2
) rcode
|= DoTest(asm_HasSSE2
, "SSE2");
573 if (cpuf
.f_3DNOW
) rcode
|= DoTest(asm_Has3DNow
, "3DNow");
574 if (cpuf
.f_SSE3
) rcode
|= DoTest(asm_HasSSE3
, "SSE3");
575 if (cpuf
.f_SSSE3
) rcode
|= DoTest(asm_HasSSSE3
, "SSSE3");
576 if (cpuf
.f_SSE41
) rcode
|= DoTest(asm_HasSSE41
, "SSE41");
577 if (cpuf
.f_SSE42
) rcode
|= DoTest(asm_HasSSE42
, "SSE42");
578 if (cpuf
.f_POPCNT
) rcode
|= DoTest(asm_HasPOPCNT
, "POPCNT");
579 if (cpuf
.f_CMOV
) rcode
|= DoTest(asm_HasCMOV
, "CMOV");
580 if (cpuf
.f_TSC
) rcode
|= DoTest(asm_HasTSC
, "TSC");
582 #if TEST_NEGATIVE_CASE
583 printf("TESTING: simulating invalid CPUID implementation\n");
584 rcode
|= DoTest(asm_HasSSSE3
, "SSSE3");
585 rcode
|= DoTest(asm_HasSSE41
, "SSE41");
586 rcode
|= DoTest(asm_HasSSE42
, "SSE42");
587 rcode
|= DoTest(asm_HasPOPCNT
, "POPCNT");
588 rcode
|= DoTest(asm_HasCMOV
, "CMOV");
592 * TODO: implement the rest of these tests
593 * if (cpuf.f_CX8) rcode |= DoTest(asm_HasCX8, "CMPXCHG8B");
594 * if (cpuf.f_CX16) rcode |= DoTest(asm_HasCX16, "CMPXCHG16B");
595 * DoTest(asm_HasSSE4a, "SSE4a");
596 * DoTest(asm_HasEMMX, "EMMX");
597 * DoTest(asm_HasE3DNow, "E3DNow");
601 PrintFail("CPUID not implemented correctly.");
604 printf("[CPUID implementation looks okay]\n");