source: rtems/cpukit/score/cpu/i386/sse_test.c

Last change on this file was d0c1ce6, checked in by Sebastian Huber <sebastian.huber@…>, on 04/21/20 at 08:17:00

i386: Replace fpcr clobber with memory clobber

Update #3943.

  • Property mode set to 100644
File size: 25.6 KB
Line 
1/**
2 *  @file
3 *
4 *  @brief Test FPU/SSE Context Save and Restore
5 */
6
7/*
8 * Authorship
9 * ----------
10 * This software was created by
11 *     Till Straumann <strauman@slac.stanford.edu>, 2009,
12 *     Stanford Linear Accelerator Center, Stanford University.
13 *
14 * Acknowledgement of sponsorship
15 * ------------------------------
16 * This software was produced by
17 *     the Stanford Linear Accelerator Center, Stanford University,
18 *     under Contract DE-AC03-76SFO0515 with the Department of Energy.
19 *
20 * Government disclaimer of liability
21 * ----------------------------------
22 * Neither the United States nor the United States Department of Energy,
23 * nor any of their employees, makes any warranty, express or implied, or
24 * assumes any legal liability or responsibility for the accuracy,
25 * completeness, or usefulness of any data, apparatus, product, or process
26 * disclosed, or represents that its use would not infringe privately owned
27 * rights.
28 *
29 * Stanford disclaimer of liability
30 * --------------------------------
31 * Stanford University makes no representations or warranties, express or
32 * implied, nor assumes any liability for the use of this software.
33 *
34 * Stanford disclaimer of copyright
35 * --------------------------------
36 * Stanford University, owner of the copyright, hereby disclaims its
37 * copyright and all other rights in this software.  Hence, anyone may
38 * freely use it for any purpose without restriction.
39 *
40 * Maintenance of notices
41 * ----------------------
42 * In the interest of clarity regarding the origin and status of this
43 * SLAC software, this and all the preceding Stanford University notices
44 * are to remain affixed to any copy or derivative of this software made
45 * or distributed by the recipient and are to be affixed to any copy of
46 * software made or distributed by the recipient that contains a copy or
47 * derivative of this software.
48 *
49 * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
50 */
51
52
53/* Code for testing FPU/SSE context save/restore across exceptions
54 * (including interrupts).
55 *
56 * There are two tasks and an IRQ/EXC handler involved. One task (LP)
57 * is of lower priority than the other (HP) task.
58 *
59 * 1) LP task sets up a context area in memory (known contents; every
60 *    register is loaded with different values)
61 *
62 * 2) LP task
63 *       2a saves original FP/SSE context
64 *       2b loads context from 1) into FPU/SSE
65 *       2c raises an exception or interrupt
66 *
67 *   *  (2d save FPU/SSE context after irq/exception returns to
68 *          separate area for verification
69 *       2e reload original FP/SSE context.)
70 *
71 *   * All these five steps are coded in assembly to prevent
72 *     gcc from manipulating the FP/SSE state. The last two steps,
73 *     however, are effectively executed during 6 when control is
74 *     returned to the LP task.
75 *
76 * 3) IRQ/EXC handler OS wrapper saves context, initializes FPU and
77 *    MXCSR.
78 *
79 * 4) user (our) irq/exc handler clears exception condition, clobbers
80 *    FPU and XMM regs and finally releases a semaphore on which HP
81 *    task is waiting.
82 *
83 * 5) context switch to HP task. HP task clobbers FPU and XMM regs.
84 *    Then it tries to re-acquire the synchronization semaphore and
85 *    blocks.
86 *
87 * 6) task switch back to (interrupted) LP task. Original context is
88 *    restored and verified against the context that was setup in 1).
89 *
90 *
91 * Three methods for interrupting the LP task are tested
92 *
93 *  a) FP exception (by setting an exception status in the context from 1)
94 *  b) SSE exception (by computing the square root of a vector of negative
95 *     numbers.
96 *  c) IRQ (software IRQ via 'INT xx' instruction)
97 *
98 */
99
100#ifdef HAVE_CONFIG_H
101#include "config.h"
102#endif
103
104#ifdef __rtems__
105#include <rtems.h>
106#include <rtems/score/cpu.h>
107#include <rtems/irq.h>
108#include <rtems/error.h>
109#endif
110
111#include <inttypes.h>
112#include <stdio.h>
113#include <stdlib.h>
114#include <string.h>
115#include <math.h>
116
117/* This is currently hardcoded (int xx opcode requires immediate operand) */
118#define SSE_TEST_IRQ  10
119
120typedef uint8_t   __v8  __attribute__((vector_size(16)));
121typedef uint32_t __v32  __attribute__((vector_size(16)));
122typedef float     __vf  __attribute__((vector_size(16)));
123
124#ifndef __rtems__
125/* Clone of what is defined in rtems/score/cpu.h (for testing under linux) */
126typedef struct Context_Control_sse {
127  uint16_t  fcw;
128  uint16_t  fsw;
129  uint8_t   ftw;
130  uint8_t   res_1;
131  uint16_t  fop;
132  uint32_t  fpu_ip;
133  uint16_t  cs;
134  uint16_t  res_2;
135  uint32_t  fpu_dp;
136  uint16_t  ds;
137  uint16_t  res_3;
138  uint32_t  mxcsr;
139  uint32_t  mxcsr_mask;
140  struct {
141    uint8_t fpreg[10];
142    uint8_t res_4[ 6];
143  } fp_mmregs[8];
144  uint8_t   xmmregs[8][16];
145  uint8_t   res_5[224];
146} Context_Control_sse
147__attribute__((aligned(16)))
148;
149#endif
150
151#define MXCSR_FZ          (1<<15)   /* Flush to zero */
152#define MXCSR_RC(x) (((x)&3)<<13)   /* Rounding ctrl */
153#define MXCSR_PM          (1<<12)   /* Precision msk */
154#define MXCSR_UM          (1<<11)   /* Underflow msk */
155#define MXCSR_OM          (1<<10)   /* Overflow  msk */
156#define MXCSR_ZM          (1<< 9)   /* Divbyzero msk */
157#define MXCSR_DM          (1<< 8)   /* Denormal  msk */
158#define MXCSR_IM          (1<< 7)   /* Invalidop msk */
159#define MXCSR_DAZ         (1<< 6)   /* Denorml are 0 */
160#define MXCSR_PE          (1<< 5)   /* Precision flg */
161#define MXCSR_UE          (1<< 4)   /* Underflow flg */
162#define MXCSR_OE          (1<< 3)   /* Overflow  flg */
163#define MXCSR_ZE          (1<< 2)   /* Divbyzero flg */
164#define MXCSR_DE          (1<< 1)   /* Denormal  flg */
165#define MXCSR_IE          (1<< 0)   /* Invalidop flg */
166
167#define MXCSR_ALLM (MXCSR_PM | MXCSR_UM | MXCSR_OM | MXCSR_ZM | MXCSR_DM | MXCSR_IM)
168#define MXCSR_ALLE (MXCSR_PE | MXCSR_UE | MXCSR_OE | MXCSR_ZE | MXCSR_DE | MXCSR_IE)
169
170#define FPSR_B            (1<<15)   /* FPU busy      */
171#define FPSR_C3           (1<<14)   /* Cond code C3  */
172#define FPSR_TOP(x) (((x)&7)<<11)   /* TOP           */
173#define FPSR_C2           (1<<10)   /* Cond code C2  */
174#define FPSR_C1           (1<< 9)   /* Cond code C1  */
175#define FPSR_C0           (1<< 8)   /* Cond code C0  */
176#define FPSR_ES           (1<< 7)   /* Error summary */
177#define FPSR_SF           (1<< 6)   /* Stack fault   */
178#define FPSR_PE           (1<< 5)   /* Precision flg */
179#define FPSR_UE           (1<< 4)   /* Underflow flg */
180#define FPSR_OE           (1<< 3)   /* Overflow  flg */
181#define FPSR_ZE           (1<< 2)   /* Divbyzero flg */
182#define FPSR_DE           (1<< 1)   /* Denormal  flg */
183#define FPSR_IE           (1<< 0)   /* Invalidop flg */
184
185#define FPCW_X            (1<<12)   /* Infinity ctrl */
186#define FPCW_RC(x)  (((x)&3)<<10)   /* Rounding ctrl */
187#define FPCW_PC(x)  (((x)&3)<< 8)   /* Precision ctl */
188#define FPCW_PM           (1<< 5)   /* Precision msk */
189#define FPCW_UM           (1<< 4)   /* Underflow msk */
190#define FPCW_OM           (1<< 3)   /* Overflow  msk */
191#define FPCW_ZM           (1<< 2)   /* Divbyzero msk */
192#define FPCW_DM           (1<< 1)   /* Denormal  msk */
193#define FPCW_IM           (1<< 0)   /* Invalidop msk */
194
195#define FPCW_ALLM (FPCW_PM | FPCW_UM | FPCW_OM | FPCW_ZM | FPCW_DM | FPCW_IM)
196#define FPSR_ALLE (FPSR_ES | FPSR_SF | FPSR_PE | FPSR_UE | FPSR_OE | FPSR_ZE | FPSR_DE | FPSR_IE)
197
198/* Store 'double' into 80-bit register image */
199void
200fp_st1(uint8_t (*p_dst)[10], double v)
201{
202        asm volatile("fstpt %0":"=m"(*p_dst):"t"(v):"st");
203}
204
205/* Store 'double' into 80-bit register image #i in context */
206void
207fp_st(Context_Control_sse *p_ctxt, int i, double v)
208{
209        fp_st1(&p_ctxt->fp_mmregs[i].fpreg,v);
210}
211
212/* Load 'double' from 80-bit register image */
213double
214fp_ld1(uint8_t (*p_src)[10])
215{
216double v;
217
218        asm volatile("fldt %1":"=t"(v):"m"((*p_src)[0]),"m"(*p_src));
219        return v;
220}
221
222/* Load 'double' from 80-bit register image #i in context */
223double
224fp_ld(Context_Control_sse *p_ctxt, int i)
225{
226        return fp_ld1(&p_ctxt->fp_mmregs[i].fpreg);
227}
228
229#define FPUCLOBBER \
230        "st","st(1)","st(2)","st(3)",   \
231        "st(4)","st(5)","st(6)","st(7)",\
232        "fpsr","memory"
233
234/* There seems to be no way to say that mxcsr was clobbered */
235
236#define SSECLOBBER \
237        "xmm0","xmm1","xmm2","xmm3",    \
238        "xmm4","xmm5","xmm6","xmm7"
239
240static void
241sse_clobber(uint32_t x)
242{
243__v32 v = { x, x, x, x };
244        asm volatile (
245                "       movdqa %0,     %%xmm0      \n"
246                "       movdqa %%xmm0, %%xmm1      \n"
247                "       movdqa %%xmm0, %%xmm2      \n"
248                "       movdqa %%xmm0, %%xmm3      \n"
249                "       movdqa %%xmm0, %%xmm4      \n"
250                "       movdqa %%xmm0, %%xmm5      \n"
251                "       movdqa %%xmm0, %%xmm6      \n"
252                "       movdqa %%xmm0, %%xmm7      \n"
253                :
254                :"m"(v)
255                :SSECLOBBER
256        );
257}
258
259void
260all_clobber(uint32_t v1, uint32_t v2);
261
262__asm__ (
263"all_clobber:               \n"
264"   finit                   \n"
265"       movq  0(%esp), %xmm0    \n"
266"   punpcklqdq %xmm0, %xmm0 \n"
267"       movdqa %xmm0, %xmm1     \n"
268"       movdqa %xmm0, %xmm2     \n"
269"       movdqa %xmm0, %xmm3     \n"
270"       movdqa %xmm0, %xmm4     \n"
271"       movdqa %xmm0, %xmm5     \n"
272"       movdqa %xmm0, %xmm6     \n"
273"       movdqa %xmm0, %xmm7     \n"
274"       ret                     \n"
275);
276
277/* Clear FPU and save FPU/SSE registers to context area */
278
279void
280init_ctxt(Context_Control_sse *p_ctxt);
281
282__asm__ (
283"init_ctxt:            \n"
284"       finit              \n"
285"   mov    4(%esp), %eax\n"
286"   fxsave (%eax)      \n"
287"   fwait              \n"
288"   ret                \n"
289);
290
291/* Save FPU/SSE registers to context area */
292
293static void
294stor_ctxt(Context_Control_sse *p_ctxt)
295{
296        memset(p_ctxt, 0, sizeof(*p_ctxt));
297        asm volatile(
298/*              "       finit                \n" */
299                "       fxsave %0            \n"
300                "   fwait                \n"
301                : "=m"(*p_ctxt)
302                :
303                : FPUCLOBBER
304        );
305}
306
307#define H08 "0x%02"PRIx8
308#define H16 "0x%04"PRIx16
309#define H32 "0x%08"PRIx32
310
311#define F16 "mismatch ("H16" != "H16")\n"
312
313#define FLDCMP(fld, fmt) \
314        if ( a->fld != b->fld ) { \
315                rval = 1;             \
316                if ( !quiet )         \
317           fprintf(stderr,#fld" mismatch ("fmt" != "fmt")\n",a->fld, b->fld); \
318        }
319
320#define FLTCMP(i)                                   \
321        do {                                            \
322                if (   ( (a->ftw ^ b->ftw) & (1<<i))        \
323                        || ( (a->ftw & b->ftw  & (1<<i)) &&     \
324                 memcmp(a->fp_mmregs[i].fpreg,      \
325                    b->fp_mmregs[i].fpreg,          \
326                    sizeof(a->fp_mmregs[i].fpreg))  \
327               )                                    \
328           ) {                                      \
329            rval = 1;                               \
330            if ( !quiet ) {                         \
331              double fa = fp_ld(a, i);              \
332              double fb = fp_ld(b, i);              \
333                          if ( ((a->ftw ^ b->ftw) & (1<<i)) )   \
334                fprintf(stderr,"fpreg[%u] TAG mismatch (%u != %u)\n",i,(a->ftw & (1<<i)) ? 1 : 0,(b->ftw & (1<<i)) ? 1 : 0); \
335                          else                                  \
336                fprintf(stderr,"fpreg[%u] mismatch (%g != %g)\n",i,fa,fb); \
337                }                                       \
338            }                                           \
339    } while (0)
340
341#define XMMCMP(i)                                   \
342        do {                                            \
343                if ( memcmp(&a->xmmregs[i],                 \
344                    &b->xmmregs[i],                 \
345                    sizeof(a->xmmregs[i]))          \
346           ) {                                      \
347            rval = 1;                               \
348            if ( !quiet ) {                         \
349              int _jj;                              \
350              fprintf(stderr,"xmmreg[%u] mismatch:\n", i); \
351              fprintf(stderr,"    ");               \
352              for (_jj=0; _jj<16; _jj++)            \
353                fprintf(stderr,"%02x ",a->xmmregs[i][_jj]); \
354              fprintf(stderr,"\n !=\n");            \
355              fprintf(stderr,"    ");               \
356              for (_jj=0; _jj<16; _jj++)            \
357                fprintf(stderr,"%02x ",b->xmmregs[i][_jj]); \
358              fprintf(stderr,"\n");                 \
359                }                                       \
360            }                                           \
361    } while (0)
362
363
364/* Compare two FPU/SSE context areas and flag differences;
365 * RETURNS: zero if the contexts match and nonzero otherwise
366 */
367static int
368cmp_ctxt(Context_Control_sse *a, Context_Control_sse *b, int quiet)
369{
370int rval = 0;
371int i;
372        FLDCMP(fcw,H16);
373        FLDCMP(fsw,H16);
374        FLDCMP(ftw,H08);
375        FLDCMP(fop,H16);
376        FLDCMP(fpu_ip,H32);
377        FLDCMP(cs,H16);
378        FLDCMP(fpu_dp,H32);
379        FLDCMP(ds,H16);
380        FLDCMP(mxcsr,H32);
381        FLDCMP(mxcsr_mask,H32);
382        for ( i=0; i<8; i++ ) {
383                FLTCMP(i);
384        }
385        for ( i=0; i<8; i++ ) {
386                XMMCMP(i);
387        }
388        return rval;
389}
390
391/* Possible arguments to exc_raise() */
392
393#define FP_EXC   0
394#define IRQ_EXC  1
395#define SSE_EXC -1
396
397/* Check stack alignment by raising the interrupt from a
398 * non-16-byte aligned section of code. The exception/IRQ
399 * handler must align the stack and SSE context area
400 * properly or it will crash.
401 */
402#define __INTRAISE(x) " int  $32+"#x" \n"
403#define INTRAISE(x)   __INTRAISE(x)
404
405__asm__ (
406"do_raise:               \n"
407"   fwait                \n"
408"       test    %eax, %eax   \n"
409"   je      2f           \n"
410"   jl      1f           \n"
411INTRAISE(SSE_TEST_IRQ)
412"   jmp     2f           \n"
413"1: sqrtps  %xmm0, %xmm0 \n"
414"2:                      \n"
415"   ret                  \n"
416);
417
418#define SSE_TEST_HP_FAILED       1
419#define SSE_TEST_FSPR_FAILED     2
420#define SSE_TEST_CTXTCMP_FAILED  4
421
422static const char *fail_msgs[] = {
423        "Seems that HP task was not executing",
424        "FPSR 'Invalid-operation' flag should be clear",
425        "Restored context does NOT match the saved one",
426};
427
428static void prstat(int st, const char *where)
429{
430int i,msk;
431        for ( i=0, msk=1; i<sizeof(fail_msgs)/sizeof(fail_msgs[0]); i++, msk<<=1 ) {
432                if ( (st & msk) ) {
433                        fprintf(stderr,"sse_test ERROR: %s (testing: %s)\n", fail_msgs[i], where);
434                }
435        }
436}
437
438int                 sse_test_debug   = 0;
439
440static int
441exc_raise(int kind)
442{
443Context_Control_sse nctxt;
444Context_Control_sse octxt;
445Context_Control_sse orig_ctxt;
446int                 i,j,rval;
447double              s2;
448uint16_t            fsw;
449__vf                f4  = { -1., -2., -3., -4. };
450__vf                tmp;
451__v32               sgn = { (1<<31), (1<<31), (1<<31), (1<<31) };
452
453        stor_ctxt(&octxt);
454
455        octxt.fsw   &= ~FPSR_ALLE;
456        octxt.mxcsr &= ~MXCSR_ALLE;
457
458        for ( i=0; i<8; i++ ) {
459                fp_st(&octxt, i, (double)i+0.1);
460                for (j=0; j<16; j++) {
461                        octxt.xmmregs[i][j]=(i<<4)+j;
462                }
463        }
464
465
466        if ( SSE_EXC == kind ) {
467                memcpy(octxt.xmmregs[0], &f4, sizeof(f4));
468                octxt.mxcsr &= ~MXCSR_IM;
469        }
470
471        /* set tags to 'valid'            */
472        octxt.ftw = 0xff;
473
474        /* enable 'invalid arg' exception */
475        octxt.fcw &= ~ ( FPCW_IM );
476       
477        if ( FP_EXC == kind ) {
478                octxt.fsw |=   ( FPSR_IE | FPSR_ES );
479        }
480
481        if ( sse_test_debug )
482                printk("RAISE (fsw was 0x%04x)\n", orig_ctxt.fsw);
483        asm volatile(
484                "       fxsave  %2           \n"
485#ifdef __rtems__
486                "   movl    %4, sse_test_check\n"
487#endif
488                "       fxrstor %3           \n"
489                "   call    do_raise     \n"
490#ifdef __rtems__
491                "   movl    sse_test_check, %1\n"
492#else
493                "   movl    $0, %1       \n"
494#endif
495#ifdef TEST_MISMATCH
496                "       pxor %%xmm0, %%xmm0  \n"
497#endif
498                "       fxsave  %0           \n"
499                "       fxrstor %2           \n"
500        : "=m"(nctxt),"=&r"(rval),"=m"(orig_ctxt)
501        : "m"(octxt), "i"(SSE_TEST_HP_FAILED),"a"(kind)
502        : "xmm0"
503        );
504
505        if ( ( FPSR_IE & nctxt.fsw ) ) {
506                rval |= SSE_TEST_FSPR_FAILED;
507        }
508        if ( FP_EXC == kind )
509                nctxt.fsw |= (FPSR_IE | FPSR_ES);
510        else if ( SSE_EXC == kind ) {
511                tmp = __builtin_ia32_sqrtps( (__vf)(~sgn & (__v32)f4) );
512                /* sqrt raises PE; just clear it */
513                nctxt.mxcsr &= ~MXCSR_PE;
514                memcpy( octxt.xmmregs[0], &tmp, sizeof(tmp) );
515        }
516
517        if ( cmp_ctxt(&nctxt, &octxt, 0) ) {
518                rval |= SSE_TEST_CTXTCMP_FAILED;
519        }
520
521        s2 = sqrt(2.0);
522
523        asm volatile("fstsw %0":"=m"(fsw));
524
525        if ( sse_test_debug )
526                printf("sqrt(2): %f (FSTW: 0x%02"PRIx16")\n", sqrt(2.0), fsw);
527
528        return rval;
529}
530
531#ifdef __rtems__
532static void
533sse_test_ehdl(CPU_Exception_frame *p_f);
534
535rtems_id            sse_test_sync    = 0;
536cpuExcHandlerType   sse_test_ohdl    = 0;
537
538CPU_Exception_frame *sse_test_frame  = 0;
539volatile int        sse_test_check   = SSE_TEST_HP_FAILED;
540unsigned            sse_tests        = 0;
541
542rtems_task
543sse_test_hp_task(rtems_task_argument arg)
544{
545rtems_id sync = (rtems_id)arg;
546
547uint16_t                        fp_cw;
548uint32_t                        mxcsr;
549rtems_status_code   sc;
550const char *        msgs[] = {"FPU_EXC", "SSE_EXC", "IRQ_EXC"};
551int                 i;
552
553        /* verify that FPU control word is default value */
554        asm volatile("fstcw %0":"=m"(fp_cw));
555        if ( fp_cw != _CPU_Null_fp_context.fpucw ) {
556                fprintf(
557                        stderr,
558                        "ERROR: FPU CW initialization mismatch: got 0x%04"PRIx16"; expected 0x%04"PRIx16"\n",
559                        fp_cw,
560                        _CPU_Null_fp_context.fpucw
561                );
562        }
563
564        /* check MXCSR default value                     */
565        asm volatile("stmxcsr %0":"=m"(mxcsr));
566        if ( mxcsr != _CPU_Null_fp_context.mxcsr ) {
567                fprintf(
568                        stderr,
569                        "ERROR: MXCSR initialization mismatch: got 0x%08"PRIx32"; expected 0x%08"PRIx32"\n",
570                        mxcsr,
571                        _CPU_Null_fp_context.mxcsr
572                );
573        }
574
575
576        for (i=0; i<sizeof(msgs)/sizeof(msgs[0]); i++ ) {
577                if ( ( sse_tests & (1<<i) ) ) {
578                        if ( sse_test_debug )
579                                printk("HP task will now block for %s\n",msgs[i]);
580
581                        /* Blocking here lets the low-priority task continue */
582                        sc = rtems_semaphore_obtain(sync, RTEMS_WAIT, 500);
583
584                        all_clobber(0xaffeaffe, 0xcafecafe);
585
586                        if ( RTEMS_SUCCESSFUL != sc ) {
587                                rtems_error(sc,"ERROR: sse_test hp task wasn't notified of exception\n");
588                                goto bail;
589                        }
590
591                        /* set flag indicating that we executed until here */
592                        sse_test_check = 0;
593                }
594        }
595
596bail:
597        rtems_task_suspend(RTEMS_SELF);
598}
599
600/* Flags to skip individual tests */
601#define SSE_TEST_FPU_EXC  (1<<0)
602#define SSE_TEST_SSE_EXC  (1<<1)
603#define SSE_TEST_IRQ_EXC  (1<<2)
604
605#define SSE_TEST_ALL      7
606
607/* If this flag is given the executing task is not deleted
608 * when the test finishes. This is useful if you want to
609 * execute from a shell or similar.
610 */
611#define SSE_TEST_NO_DEL    (1<<0)
612
613/* Task arg is bitmask of these flags */
614rtems_task
615sse_test_lp_task(rtems_task_argument arg)
616{
617rtems_id                hp_task = 0;
618rtems_status_code       sc;
619rtems_task_priority     pri;
620uint16_t                fp_cw,fp_cw_set;
621uint32_t                mxcsr, mxcsr_set;
622rtems_irq_connect_data  irqd;
623int                     flags = (int)arg;
624int                     st;
625int                     errs = 0;
626
627        sse_tests = SSE_TEST_ALL & ~(flags>>1);
628
629        sse_test_ohdl = 0;
630
631        fp_cw_set = _CPU_Null_fp_context.fpucw | FPCW_RC(3) ;
632        mxcsr_set = _CPU_Null_fp_context.mxcsr | MXCSR_RC(3) ;
633        asm volatile("ldmxcsr %0"::"m"(mxcsr_set));
634        asm volatile("fldcw   %0"::"m"(fp_cw_set));
635
636        sc = rtems_semaphore_create(
637                        rtems_build_name('s','s','e','S'),
638                        0,
639                        RTEMS_SIMPLE_BINARY_SEMAPHORE,
640                        0,
641                        &sse_test_sync
642                );
643        if ( RTEMS_SUCCESSFUL != sc ) {
644                rtems_error(sc, "sse_test ERROR: creation of 'sync' semaphore failed");
645                errs++;
646                goto bail;
647        }
648
649        rtems_task_set_priority( RTEMS_SELF, RTEMS_CURRENT_PRIORITY, &pri );
650
651        sc = rtems_task_create(
652                        rtems_build_name('s','s','e','H'),
653                        pri - 2,
654                        20000,
655                        RTEMS_DEFAULT_MODES,
656                        RTEMS_FLOATING_POINT,
657                        &hp_task
658                );
659        if ( RTEMS_SUCCESSFUL != sc ) {
660                hp_task = 0;
661                rtems_error( sc, "sse_test ERROR: creation of high-priority task failed");
662                errs++;
663                goto bail;
664        }
665
666        sc = rtems_task_start( hp_task, sse_test_hp_task, (rtems_task_argument)sse_test_sync );
667        if ( RTEMS_SUCCESSFUL != sc ) {
668                rtems_error( sc, "sse_test ERROR: start of high-priority task failed");
669                goto bail;
670        }
671
672        /* Test if FP/SSE context is saved/restored across an exception */
673        sse_test_ohdl      = _currentExcHandler;
674        _currentExcHandler = sse_test_ehdl;
675
676        if ( (sse_tests & SSE_TEST_FPU_EXC) ) {
677                if ( (st = exc_raise(FP_EXC)) ) {
678                        prstat(st,"FP_EXC");
679                        errs++;
680                }
681
682                /* Test modified FPCW/MXCSR */
683                asm volatile("fstcw   %0":"=m"(fp_cw));
684                asm volatile("stmxcsr %0":"=m"(mxcsr));
685                mxcsr &= ~(MXCSR_ALLE);
686                if ( fp_cw != fp_cw_set ) {
687                        fprintf(stderr,"sse_test ERROR: FPCW mismatch (after FP_EXC): expected 0x%04"PRIx16", got 0x%04"PRIx16"\n", fp_cw_set, fp_cw);
688                        errs++;
689                }
690                if ( mxcsr != mxcsr_set ) {
691                        fprintf(stderr,"sse_test ERROR: MXCSR mismatch (after FP_EXC): expected 0x%08"PRIx32", got 0x%08"PRIx32"\n", mxcsr_set, mxcsr);
692                        errs++;
693                }
694        }
695
696        if ( (sse_tests & SSE_TEST_SSE_EXC) ) {
697                if ( (st = exc_raise(SSE_EXC)) ) {
698                        prstat(st, "SSE_EXC");
699                        errs++;
700                }
701
702                /* Test modified FPCW/MXCSR */
703                asm volatile("fstcw   %0":"=m"(fp_cw));
704                asm volatile("stmxcsr %0":"=m"(mxcsr));
705                mxcsr &= ~(MXCSR_ALLE);
706                if ( fp_cw != fp_cw_set ) {
707                        fprintf(stderr,"sse_test ERROR: FPCW mismatch (after SSE_EXC): expected 0x%04"PRIx16", got 0x%04"PRIx16"\n", fp_cw_set, fp_cw);
708                        errs++;
709                }
710                if ( mxcsr != mxcsr_set ) {
711                        fprintf(stderr,"sse_test ERROR: MXCSR mismatch (after SSE_EXC): expected 0x%08"PRIx32", got 0x%08"PRIx32"\n", mxcsr_set, mxcsr);
712                        errs++;
713                }
714        }
715
716
717        if ( (sse_tests & SSE_TEST_IRQ_EXC) ) {
718                memset( &irqd, 0, sizeof(irqd) );
719                irqd.name   = SSE_TEST_IRQ;
720                irqd.hdl    = (void*)sse_test_ehdl;
721                irqd.handle = 0;
722
723                if ( ! BSP_install_rtems_irq_handler( &irqd ) ) {
724                        fprintf(stderr, "sse_test ERROR: Unable to install ISR\n");
725                        errs++;
726                        goto bail;
727                }
728
729                /* Test if FP/SSE context is saved/restored across an interrupt */
730                if ( (st = exc_raise(IRQ_EXC)) ) {
731                        prstat(st, "IRQ");
732                        errs++;
733                }
734
735                if ( ! BSP_remove_rtems_irq_handler( &irqd ) ) {
736                        fprintf(stderr, "sse_test ERROR: Unable to uninstall ISR\n");
737                }
738
739                /* Test modified FPCW/MXCSR */
740                asm volatile("fstcw   %0":"=m"(fp_cw));
741                asm volatile("stmxcsr %0":"=m"(mxcsr));
742                mxcsr &= ~(MXCSR_ALLE);
743                if ( fp_cw != fp_cw_set ) {
744                        fprintf(stderr,"sse_test ERROR: FPCW mismatch (after IRQ): expected 0x%04"PRIx16", got 0x%04"PRIx16"\n", fp_cw_set, fp_cw);
745                        errs++;
746                }
747                if ( mxcsr != mxcsr_set ) {
748                        fprintf(stderr,"sse_test ERROR: MXCSR mismatch (after IRQ): expected 0x%08"PRIx32", got 0x%08"PRIx32"\n", mxcsr_set, mxcsr);
749                        errs++;
750                }
751        }
752
753
754bail:
755        /* Wait for console to calm down... */
756        rtems_task_wake_after(5);
757        fprintf(stderr,"SSE/FPU Test %s (%u errors)\n", errs ? "FAILED":"PASSED", errs);
758        if ( sse_test_ohdl ) {
759                _currentExcHandler = sse_test_ohdl;
760                sse_test_ohdl      = 0;
761        }
762        if ( sse_test_sync )
763                rtems_semaphore_delete( sse_test_sync );
764        sse_test_sync = 0;
765        if ( hp_task )
766                rtems_task_delete( hp_task );
767
768        if ( ! (flags & SSE_TEST_NO_DEL) )
769                rtems_task_exit();
770}
771
772static void
773sse_test_ehdl(CPU_Exception_frame *p_f)
774{
775int i,j,start = 0;
776int mismatch;
777__vf    f4;
778
779        if ( p_f ) {
780                printk("Got exception #%u\n",        p_f->idtIndex);
781                printk("EIP: 0x%08x, ESP: 0x%08x\n", p_f->eip, p_f->esp0);
782                printk("TID: 0x%08x\n",              _Thread_Executing->Object.id);
783
784                if ( ! p_f->fp_ctxt ) {
785                        printk("ERROR: NO FP/SSE CONTEXT ATTACHED ??\n");
786                        sse_test_ohdl(p_f);
787                }
788                if ( 16 == p_f->idtIndex ) {
789                        printk("Resetting FP status (0x%04"PRIx16")\n", p_f->fp_ctxt->fsw);
790                        p_f->fp_ctxt->fsw = 0;
791                } else if ( 19 == p_f->idtIndex ) {
792                        start = 1;
793                        memcpy(&f4, p_f->fp_ctxt->xmmregs[0], sizeof(f4));
794                        f4 = -f4;
795                        memcpy(p_f->fp_ctxt->xmmregs[0], &f4, sizeof(f4));
796                        p_f->fp_ctxt->mxcsr &= ~MXCSR_ALLE;
797                } else {
798                        printk("(skipping non-FP exception)\n");
799                        sse_test_ohdl(p_f);
800                }
801
802                printk("Checking XMM regs -- ");
803                for ( mismatch=0, i=start; i<8; i++ ) {
804                        for ( j=0; j<16; j++ ) {
805                                if ( p_f->fp_ctxt->xmmregs[i][j] != ((i<<4) | j) )
806                                        mismatch++;
807                        }
808                }
809                if ( mismatch ) {
810                        printk("%u mismatches; dump:\n", mismatch);
811                        for ( i=0; i<8; i++ ) {
812                                for ( j=0; j<16; j++ ) {
813                                        printk("0x%02x ", p_f->fp_ctxt->xmmregs[i][j]);
814                                }
815                                printk("\n");
816                        }
817                } else {
818                        printk("OK\n");
819                }
820        } else {
821                printk("IRQ %u\n", SSE_TEST_IRQ);
822        }
823        printk("Clobbering FPU/SSE state\n");
824        asm volatile("finit");
825        sse_clobber(0xdeadbeef);
826        printk("Notifying task\n");
827        rtems_semaphore_release( sse_test_sync );       
828}
829
830#else
831
832/* Code using signals for testing under linux; unfortunately, 32-bit
833 * linux seems to pass no SSE context info to the sigaction...
834 */
835
836#include <signal.h>
837#include <ucontext.h>
838
839#define MKCASE(X) case FPE_##X: msg="FPE_"#X; break;
840
841#define CLRXMM(i) __asm__ volatile("pxor %%xmm"#i", %%xmm"#i:::"xmm"#i)
842
843static void
844fpe_act(int signum, siginfo_t *p_info, void *arg3)
845{
846ucontext_t *p_ctxt = arg3;
847const char *msg    = "FPE_UNKNOWN";
848uint16_t   *p_fst;
849
850        if ( SIGFPE != signum ) {
851                fprintf(stderr,"WARNING: fpe_act handles SIGFPE\n");
852                return;
853        }
854        switch ( p_info->si_code ) {
855                default:
856                        fprintf(stderr,"WARNING: fpe_act got unkown code %u\n", p_info->si_code);
857                        return;
858                MKCASE(INTDIV);
859                MKCASE(INTOVF);
860                MKCASE(FLTDIV);
861                MKCASE(FLTOVF);
862                MKCASE(FLTUND);
863                MKCASE(FLTRES);
864                MKCASE(FLTINV);
865                MKCASE(FLTSUB);
866        }
867        fprintf(stderr,"Got SIGFPE (%s) @%p\n", msg, p_info->si_addr);
868#ifdef __linux__
869        fprintf(stderr,"Resetting FP status 0x%02lx\n", p_ctxt->uc_mcontext.fpregs->sw);
870        p_ctxt->uc_mcontext.fpregs->sw = 0;
871#ifdef TEST_MISMATCH
872        fp_st1((void*)&p_ctxt->uc_mcontext.fpregs->_st[3],2.345);
873#endif
874#endif
875
876        /* Clear FPU; if context is properly saved/restored around exception
877         * then this shouldn't disturb the register contents of the interrupted
878         * task/process.
879         */
880        asm volatile("finit");
881        sse_clobber(0xdeadbeef);
882}
883
884static void
885test(void)
886{
887Context_Control_sse ctxt;
888
889        stor_ctxt(&ctxt);
890        printf("FPCW: 0x%"PRIx16"\nFPSW: 0x%"PRIx16"\n", ctxt.fcw, ctxt.fsw);
891        printf("FTAG: 0x%"PRIx8"\n",ctxt.ftw);
892}
893
894int
895main(int argc, char **argv)
896{
897struct sigaction a1, a2;
898uint32_t         mxcsr;
899
900        memset(&a1, 0, sizeof(a1));
901
902        a1.sa_sigaction = fpe_act;
903        a1.sa_flags     = SA_SIGINFO;   
904
905        if ( sigaction(SIGFPE, &a1, &a2) ) {
906                perror("sigaction");
907                return 1;
908        }
909
910        asm volatile("stmxcsr %0":"=m"(mxcsr));
911        printf("MXCSR: 0x%08"PRIx32"\n", mxcsr);
912
913        test();
914        exc_raise(0);
915        return 0;
916}
917#endif
918
919/* Helpers to access CR4 and MXCSR */
920
921uint32_t
922mfcr4()
923{
924uint32_t rval;
925        asm volatile("mov %%cr4, %0":"=r"(rval));
926        return rval;
927}
928
929void
930mtcr4(uint32_t rval)
931{
932        asm volatile("mov %0, %%cr4"::"r"(rval));
933}
934
935uint32_t
936mfmxcsr()
937{
938uint32_t rval;
939        asm volatile("stmxcsr %0":"=m"(rval));
940        return rval;
941}
942
943void
944mtmxcsr(uint32_t rval)
945{
946        asm volatile("ldmxcsr %0"::"m"(rval));
947}
948
949
950float
951sseraise()
952{
953__vf f4={-2., -2., -2. -2.};
954float f;
955     f4 = __builtin_ia32_sqrtps( f4 );
956        memcpy(&f,&f4,sizeof(f));
957        return f;
958}
Note: See TracBrowser for help on using the repository browser.