imcplugin: Set NACL_FD environment variable
[nativeclient.git] / platform_qual_test / vcpuid.c
blob1c78af39a477cd48d22e275bde8abd44bf205cf6
1 /*
2 * Copyright 2008, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
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
14 * distribution.
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.
33 * vcpuid.c
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"
44 #include <stdio.h>
45 #include <string.h>
46 #include <assert.h>
47 #include <stdlib.h>
48 #include <setjmp.h>
49 #include <signal.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 */
66 "pop %%eax \n\t"
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 */
70 "popfl \n\t"
71 "pushfl \n\t" /* save EFLAGS to %1 */
72 "pop %1 \n\t"
73 : "=g" (before), "=g" (after)
75 : "%eax" );
76 #elif NACL_WINDOWS
77 #if defined(__GNUC__)
78 # error Building with GCC on Windows is not supported.
79 #endif
80 __asm {
81 pushfd
82 pop eax
83 mov before, eax
84 xor eax, 0x00200000
85 push eax
86 popfd
87 pushfd
88 pop after
90 #else
91 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
92 #endif
93 result = (before ^ after) & 0x0200000;
94 return result;
97 static int asm_HasMMX() {
98 volatile int before, after;
99 before = kMagicConst;
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) */
105 "mov %%ecx, %0 \n\t"
106 : "=g" (after)
107 : "m" (before)
108 : "eax", "ecx", "mm0" );
109 #elif NACL_WINDOWS
110 __asm {
111 mov eax, before
112 xor ecx, ecx
113 movd mm0, eax
114 movd ecx, mm0
115 mov after, ecx
117 #else
118 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
119 #endif
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) */
132 "mov %%ecx, %0 \n\t"
133 "emms \n\t"
134 : "=g" (after)
135 : "m" (before)
136 : "%eax", "%ecx");
137 #elif NACL_WINDOWS
138 __asm {
139 mov eax, before
140 movd mm0, eax
141 pfadd mm0, mm0
142 movd ecx, mm0
143 mov after, ecx
144 emms
146 #else
147 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
148 #endif
149 return (after == kMagicConst + 0x800000);
153 static int asm_HasSSE() {
154 volatile int before, after;
155 before = kMagicConst;
156 after = 0;
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) */
160 : "=g" (after)
161 : "m" (before)
162 : "xmm0" );
163 #elif NACL_WINDOWS
164 __asm {
165 movss xmm0, before
166 movss after, xmm0
168 #else
169 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
170 #endif
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) */
182 "mov %%ecx, %0 \n\t"
183 : "=g" (after)
184 : "m" (before)
185 : "eax", "ecx", "xmm0");
186 #elif NACL_WINDOWS
187 __asm {
188 mov eax, before
189 xor ecx, ecx
190 movd xmm0, eax
191 movd ecx, xmm0
192 mov after, ecx
194 #else
195 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
196 #endif
197 return (after == kMagicConst);
200 static int asm_HasSSE3() {
201 volatile int before, after;
202 after = 0;
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"
209 "mov %%ecx, %0 \n\t"
210 : "=g" (after)
211 : "m" (before)
212 : "eax", "ecx", "xmm0", "xmm1" );
213 #elif NACL_WINDOWS
214 __asm {
215 mov eax, before
216 movd xmm0, eax
217 movddup xmm1, xmm0
218 movd ecx, xmm1
219 mov after, ecx
221 #else
222 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
223 #endif
224 return (after == kMagicConst);
227 static int asm_HasSSSE3() {
228 volatile int after;
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) */
236 "mov %%ecx, %0 \n\t"
237 "emms \n\t"
238 : "=g" (after)
240 : "eax", "ecx", "mm0");
241 #elif NACL_WINDOWS
242 __asm {
243 mov eax, 0x0000ffff
244 xor ecx, ecx
245 movd mm0, eax
246 pabsw mm0, mm0
247 movd ecx, mm0
248 mov after, ecx
249 emms
251 #else
252 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
253 #endif
254 return (after == 1);
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"
265 "mov %%ecx, %0 \n\t"
266 : "=g" (after)
267 : "g" (before)
268 : "eax", "ecx", "xmm0" );
269 #elif NACL_WINDOWS
270 __asm {
271 mov eax, before
272 movd xmm0, eax
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
275 _emit 0x66
276 _emit 0x0f
277 _emit 0x3a
278 _emit 0x0a
279 _emit 0xc0
280 _emit 0x00
281 movd ecx, xmm0
282 mov after, ecx
284 #else
285 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
286 #endif
287 return (after == kMagicConst_ROUNDSS);
290 static int asm_HasSSE42() {
291 volatile int after;
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"
296 "mov %%ecx, %0 \n\t"
297 : "=g" (after)
299 : "eax", "ecx" );
300 #elif NACL_WINDOWS
301 __asm {
302 mov eax, 0x0000ffff
303 xor ecx, ecx
304 // NOTE: Use _emit for older MSFT compilers that don't know of SSE4
305 // f2 0f 38 f1 c8 crc32 ecx, eax
306 _emit 0xf2
307 _emit 0x0f
308 _emit 0x38
309 _emit 0xf1
310 _emit 0xc8
311 mov after, ecx
313 #else
314 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
315 #endif
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"
326 "mov %%ecx, %0 \n\t"
327 : "=g" (after)
328 : "m" (before)
329 : "eax", "ecx");
330 #elif NACL_WINDOWS
331 __asm {
332 mov eax, before
333 xor ecx, ecx
334 // NOTE: Use _emit for older MSFT compilers that don't know of SSE4
335 // f3 0f b8 c8 popcnt ecx, eax
336 _emit 0xf3
337 _emit 0x0f
338 _emit 0xb8
339 _emit 0xc8
340 mov after, ecx
342 #else
343 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
344 #endif
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"
356 "mov %%ecx, %0 \n\t"
357 : "=g" (after)
358 : "m" (before)
359 : "eax", "ecx");
360 #elif NACL_WINDOWS
361 __asm {
362 mov eax, before
363 xor ecx, ecx
364 add eax, 0
365 cmovnz ecx, eax
366 mov after, ecx
368 #else
369 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
370 #endif
371 return (after == kMagicConst);
374 static int asm_HasTSC() {
375 uint32_t _eax, _edx;
376 _eax = 0;
377 _edx = 0;
379 #if (NACL_LINUX || NACL_OSX)
380 asm volatile("rdtsc"
381 : "=a" (_eax), "=d" (_edx)
383 #elif NACL_WINDOWS
384 __asm {
385 rdtsc
386 mov _eax, eax
387 mov _edx, ecx
389 #else
390 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
391 #endif
392 return ((_eax | _edx) != 0);
395 static int asm_HasX87() {
396 #if (NACL_LINUX || NACL_OSX)
397 asm volatile("fld1 \n\t"
398 "fstp %st(0) \n\t");
399 return 1;
400 #elif NACL_WINDOWS
401 __asm {
402 fld1
403 fstp st(0)
405 return 1;
406 #else
407 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
408 #endif
411 #if 0
412 /* I'm having some trouble with my cmpxchg8b instruction */
413 static int asm_HasCX8() {
414 uint32_t _eax, _ebx, _ecx, _edx;
415 uint64_t foo64 = 0;
416 _eax = 0;
417 _edx = 0;
418 _ebx = 0;
419 _ecx = kMagicConst;
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) );
425 #elif NACL_WINDOWS
426 __asm {
428 #else
429 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
430 #endif
431 printf("ebx == %x ecx == %x\n", _ebx, _ecx);
432 return (foo64 == (uint64_t)kMagicConst);
434 #endif
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;
440 static int signum;
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) {
450 int sig;
451 struct sigaction ign;
453 for (sig = SIGHUP; sig < NSIG; ++sig) {
454 switch (sig) {
455 case SIGWINCH:
456 case SIGCHLD:
457 case SIGTSTP:
458 break;
459 default:
460 (void) sigaction(sig, new_action, prev_action);
461 break;
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) {
493 SignalInit();
494 if (0 != (signum = sigsetjmp(crash_load, 1))) {
495 SetSawBadInst();
496 if (SIGILL == signum) {
497 fprintf(stderr, "%s: illegal instruction\n", s);
498 } else {
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);
506 return 0;
509 printf("no %s\n", s);
510 return 1;
512 #elif NACL_WINDOWS
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) {
519 int hasfeature = 0;
521 __try {
522 hasfeature = thetest();
523 } __except (GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION ?
524 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
525 printf("Saw exception\n");
526 hasfeature = 0;
528 if (hasfeature) {
529 printf("[Has %s]\n", s);
530 return 0;
531 } else {
532 printf("no %s\n", s);
533 return 1;
536 #else
537 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
538 #endif
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() {
547 int rcode;
548 CPUFeatures cpuf;
549 GetCPUFeatures(&cpuf);
551 if (!cpuf.f_386) {
552 PrintFail("CPUID not implemented");
553 return 0;
555 rcode = DoTest(asm_HasCPUID, "CPUID"); /* This test is redundant. */
556 /* CPUID feature is required/mandatory */
557 if (rcode) {
558 PrintFail("CPUID not implemented");
559 return 0;
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");
582 #endif
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");
590 // what about LZCNT?
591 if (rcode != 0) {
592 PrintFail("CPUID not implemented correctly.");
593 return 0;
595 printf("[CPUID implementation looks okay]\n");
596 return 1;