Debugging: Add code to print backtrace for guest on SIGSEGV
[nativeclient.git] / platform_qual_test / vcpuid.c
blob66f6cf6bd35a5edefca32d06ca49abd6f159640c
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"
159 /* copy before into xmm0 (SSE2) */
160 "movss %%xmm0, %0 \n\t" /* copy xmm0 into after (SSE) */
161 : "=g" (after)
162 : "m" (before)
163 : "xmm0" );
164 #elif NACL_WINDOWS
165 __asm {
166 movss xmm0, before
167 movss after, xmm0
169 #else
170 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
171 #endif
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) */
183 "mov %%ecx, %0 \n\t"
184 : "=g" (after)
185 : "m" (before)
186 : "eax", "ecx", "xmm0");
187 #elif NACL_WINDOWS
188 __asm {
189 mov eax, before
190 xor ecx, ecx
191 movd xmm0, eax
192 movd ecx, xmm0
193 mov after, ecx
195 #else
196 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
197 #endif
198 return (after == kMagicConst);
201 static int asm_HasSSE3() {
202 volatile int before, after;
203 after = 0;
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"
210 "mov %%ecx, %0 \n\t"
211 : "=g" (after)
212 : "m" (before)
213 : "eax", "ecx", "xmm0", "xmm1" );
214 #elif NACL_WINDOWS
215 __asm {
216 mov eax, before
217 movd xmm0, eax
218 movddup xmm1, xmm0
219 movd ecx, xmm1
220 mov after, ecx
222 #else
223 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
224 #endif
225 return (after == kMagicConst);
228 static int asm_HasSSSE3() {
229 volatile int after;
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) */
237 "mov %%ecx, %0 \n\t"
238 "emms \n\t"
239 : "=g" (after)
241 : "eax", "ecx", "mm0");
242 #elif NACL_WINDOWS
243 __asm {
244 mov eax, 0x0000ffff
245 xor ecx, ecx
246 movd mm0, eax
247 pabsw mm0, mm0
248 movd ecx, mm0
249 mov after, ecx
250 emms
252 #else
253 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
254 #endif
255 return (after == 1);
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"
266 "mov %%ecx, %0 \n\t"
267 : "=g" (after)
268 : "g" (before)
269 : "eax", "ecx", "xmm0" );
270 #elif NACL_WINDOWS
271 __asm {
272 mov eax, before
273 movd xmm0, eax
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
278 _emit 0x66
279 _emit 0x0f
280 _emit 0x3a
281 _emit 0x0a
282 _emit 0xc0
283 _emit 0x00
284 movd ecx, xmm0
285 mov after, ecx
287 #else
288 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
289 #endif
290 return (after == kMagicConst_ROUNDSS);
293 static int asm_HasSSE42() {
294 volatile int after;
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"
299 "mov %%ecx, %0 \n\t"
300 : "=g" (after)
302 : "eax", "ecx" );
303 #elif NACL_WINDOWS
304 __asm {
305 mov eax, 0x0000ffff
306 xor ecx, ecx
308 * NOTE: Use _emit for older MSFT compilers that don't know of SSE4
309 * f2 0f 38 f1 c8 crc32 ecx, eax
311 _emit 0xf2
312 _emit 0x0f
313 _emit 0x38
314 _emit 0xf1
315 _emit 0xc8
316 mov after, ecx
318 #else
319 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
320 #endif
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"
331 "mov %%ecx, %0 \n\t"
332 : "=g" (after)
333 : "m" (before)
334 : "eax", "ecx");
335 #elif NACL_WINDOWS
336 __asm {
337 mov eax, before
338 xor ecx, ecx
340 * NOTE: Use _emit for older MSFT compilers that don't know of SSE4
341 * f3 0f b8 c8 popcnt ecx, eax
343 _emit 0xf3
344 _emit 0x0f
345 _emit 0xb8
346 _emit 0xc8
347 mov after, ecx
349 #else
350 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
351 #endif
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"
363 "mov %%ecx, %0 \n\t"
364 : "=g" (after)
365 : "m" (before)
366 : "eax", "ecx");
367 #elif NACL_WINDOWS
368 __asm {
369 mov eax, before
370 xor ecx, ecx
371 add eax, 0
372 cmovnz ecx, eax
373 mov after, ecx
375 #else
376 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
377 #endif
378 return (after == kMagicConst);
381 static int asm_HasTSC() {
382 uint32_t _eax, _edx;
383 _eax = 0;
384 _edx = 0;
386 #if (NACL_LINUX || NACL_OSX)
387 __asm__ volatile("rdtsc"
388 : "=a" (_eax), "=d" (_edx)
390 #elif NACL_WINDOWS
391 __asm {
392 rdtsc
393 mov _eax, eax
394 mov _edx, ecx
396 #else
397 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
398 #endif
399 return ((_eax | _edx) != 0);
402 static int asm_HasX87() {
403 #if (NACL_LINUX || NACL_OSX)
404 __asm__ volatile("fld1 \n\t"
405 "fstp %st(0) \n\t");
406 return 1;
407 #elif NACL_WINDOWS
408 __asm {
409 fld1
410 fstp st(0)
412 return 1;
413 #else
414 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
415 #endif
418 #if 0
419 /* I'm having some trouble with my cmpxchg8b instruction */
420 static int asm_HasCX8() {
421 uint32_t _eax, _ebx, _ecx, _edx;
422 uint64_t foo64 = 0;
423 _eax = 0;
424 _edx = 0;
425 _ebx = 0;
426 _ecx = kMagicConst;
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) );
432 #elif NACL_WINDOWS
433 __asm {
435 #else
436 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
437 #endif
438 printf("ebx == %x ecx == %x\n", _ebx, _ecx);
439 return (foo64 == (uint64_t)kMagicConst);
441 #endif
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;
447 static int signum;
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) {
457 int sig;
458 struct sigaction ign;
460 for (sig = SIGHUP; sig < NSIG; ++sig) {
461 switch (sig) {
462 case SIGWINCH:
463 case SIGCHLD:
464 case SIGTSTP:
465 break;
466 default:
467 (void) sigaction(sig, new_action, prev_action);
468 break;
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) {
500 SignalInit();
501 if (0 != (signum = sigsetjmp(crash_load, 1))) {
502 SetSawBadInst();
503 if (SIGILL == signum) {
504 fprintf(stderr, "%s: illegal instruction\n", s);
505 } else {
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);
513 return 0;
516 printf("no %s\n", s);
517 return 1;
519 #elif NACL_WINDOWS
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) {
526 int hasfeature = 0;
528 __try {
529 hasfeature = thetest();
530 } __except (GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION ?
531 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
532 printf("Saw exception\n");
533 hasfeature = 0;
535 if (hasfeature) {
536 printf("[Has %s]\n", s);
537 return 0;
538 } else {
539 printf("no %s\n", s);
540 return 1;
543 #else
544 # error Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS
545 #endif
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() {
554 int rcode;
555 CPUFeatures cpuf;
556 GetCPUFeatures(&cpuf);
558 if (!cpuf.f_386) {
559 PrintFail("CPUID not implemented");
560 return 0;
562 rcode = DoTest(asm_HasCPUID, "CPUID"); /* This test is redundant. */
563 /* CPUID feature is required/mandatory */
564 if (rcode) {
565 PrintFail("CPUID not implemented");
566 return 0;
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");
589 #endif
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");
598 * what about LZCNT?
600 if (rcode != 0) {
601 PrintFail("CPUID not implemented correctly.");
602 return 0;
604 printf("[CPUID implementation looks okay]\n");
605 return 1;