Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * arch/sparc64/math-emu/math.c
0004  *
0005  * Copyright (C) 1997,1999 Jakub Jelinek (jj@ultra.linux.cz)
0006  * Copyright (C) 1999 David S. Miller (davem@redhat.com)
0007  *
0008  * Emulation routines originate from soft-fp package, which is part
0009  * of glibc and has appropriate copyrights in it.
0010  */
0011 
0012 #include <linux/types.h>
0013 #include <linux/sched.h>
0014 #include <linux/errno.h>
0015 #include <linux/perf_event.h>
0016 
0017 #include <asm/fpumacro.h>
0018 #include <asm/ptrace.h>
0019 #include <linux/uaccess.h>
0020 #include <asm/cacheflush.h>
0021 
0022 #include "sfp-util_64.h"
0023 #include <math-emu/soft-fp.h>
0024 #include <math-emu/single.h>
0025 #include <math-emu/double.h>
0026 #include <math-emu/quad.h>
0027 
0028 /* QUAD - ftt == 3 */
0029 #define FMOVQ   0x003
0030 #define FNEGQ   0x007
0031 #define FABSQ   0x00b
0032 #define FSQRTQ  0x02b
0033 #define FADDQ   0x043
0034 #define FSUBQ   0x047
0035 #define FMULQ   0x04b
0036 #define FDIVQ   0x04f
0037 #define FDMULQ  0x06e
0038 #define FQTOX   0x083
0039 #define FXTOQ   0x08c
0040 #define FQTOS   0x0c7
0041 #define FQTOD   0x0cb
0042 #define FITOQ   0x0cc
0043 #define FSTOQ   0x0cd
0044 #define FDTOQ   0x0ce
0045 #define FQTOI   0x0d3
0046 /* SUBNORMAL - ftt == 2 */
0047 #define FSQRTS  0x029
0048 #define FSQRTD  0x02a
0049 #define FADDS   0x041
0050 #define FADDD   0x042
0051 #define FSUBS   0x045
0052 #define FSUBD   0x046
0053 #define FMULS   0x049
0054 #define FMULD   0x04a
0055 #define FDIVS   0x04d
0056 #define FDIVD   0x04e
0057 #define FSMULD  0x069
0058 #define FSTOX   0x081
0059 #define FDTOX   0x082
0060 #define FDTOS   0x0c6
0061 #define FSTOD   0x0c9
0062 #define FSTOI   0x0d1
0063 #define FDTOI   0x0d2
0064 #define FXTOS   0x084 /* Only Ultra-III generates this. */
0065 #define FXTOD   0x088 /* Only Ultra-III generates this. */
0066 #if 0   /* Optimized inline in sparc64/kernel/entry.S */
0067 #define FITOS   0x0c4 /* Only Ultra-III generates this. */
0068 #endif
0069 #define FITOD   0x0c8 /* Only Ultra-III generates this. */
0070 /* FPOP2 */
0071 #define FCMPQ   0x053
0072 #define FCMPEQ  0x057
0073 #define FMOVQ0  0x003
0074 #define FMOVQ1  0x043
0075 #define FMOVQ2  0x083
0076 #define FMOVQ3  0x0c3
0077 #define FMOVQI  0x103
0078 #define FMOVQX  0x183
0079 #define FMOVQZ  0x027
0080 #define FMOVQLE 0x047
0081 #define FMOVQLZ 0x067
0082 #define FMOVQNZ 0x0a7
0083 #define FMOVQGZ 0x0c7
0084 #define FMOVQGE 0x0e7
0085 
0086 #define FSR_TEM_SHIFT   23UL
0087 #define FSR_TEM_MASK    (0x1fUL << FSR_TEM_SHIFT)
0088 #define FSR_AEXC_SHIFT  5UL
0089 #define FSR_AEXC_MASK   (0x1fUL << FSR_AEXC_SHIFT)
0090 #define FSR_CEXC_SHIFT  0UL
0091 #define FSR_CEXC_MASK   (0x1fUL << FSR_CEXC_SHIFT)
0092 
0093 /* All routines returning an exception to raise should detect
0094  * such exceptions _before_ rounding to be consistent with
0095  * the behavior of the hardware in the implemented cases
0096  * (and thus with the recommendations in the V9 architecture
0097  * manual).
0098  *
0099  * We return 0 if a SIGFPE should be sent, 1 otherwise.
0100  */
0101 static inline int record_exception(struct pt_regs *regs, int eflag)
0102 {
0103     u64 fsr = current_thread_info()->xfsr[0];
0104     int would_trap;
0105 
0106     /* Determine if this exception would have generated a trap. */
0107     would_trap = (fsr & ((long)eflag << FSR_TEM_SHIFT)) != 0UL;
0108 
0109     /* If trapping, we only want to signal one bit. */
0110     if(would_trap != 0) {
0111         eflag &= ((fsr & FSR_TEM_MASK) >> FSR_TEM_SHIFT);
0112         if((eflag & (eflag - 1)) != 0) {
0113             if(eflag & FP_EX_INVALID)
0114                 eflag = FP_EX_INVALID;
0115             else if(eflag & FP_EX_OVERFLOW)
0116                 eflag = FP_EX_OVERFLOW;
0117             else if(eflag & FP_EX_UNDERFLOW)
0118                 eflag = FP_EX_UNDERFLOW;
0119             else if(eflag & FP_EX_DIVZERO)
0120                 eflag = FP_EX_DIVZERO;
0121             else if(eflag & FP_EX_INEXACT)
0122                 eflag = FP_EX_INEXACT;
0123         }
0124     }
0125 
0126     /* Set CEXC, here is the rule:
0127      *
0128      *    In general all FPU ops will set one and only one
0129      *    bit in the CEXC field, this is always the case
0130      *    when the IEEE exception trap is enabled in TEM.
0131      */
0132     fsr &= ~(FSR_CEXC_MASK);
0133     fsr |= ((long)eflag << FSR_CEXC_SHIFT);
0134 
0135     /* Set the AEXC field, rule is:
0136      *
0137      *    If a trap would not be generated, the
0138      *    CEXC just generated is OR'd into the
0139      *    existing value of AEXC.
0140      */
0141     if(would_trap == 0)
0142         fsr |= ((long)eflag << FSR_AEXC_SHIFT);
0143 
0144     /* If trapping, indicate fault trap type IEEE. */
0145     if(would_trap != 0)
0146         fsr |= (1UL << 14);
0147 
0148     current_thread_info()->xfsr[0] = fsr;
0149 
0150     /* If we will not trap, advance the program counter over
0151      * the instruction being handled.
0152      */
0153     if(would_trap == 0) {
0154         regs->tpc = regs->tnpc;
0155         regs->tnpc += 4;
0156     }
0157 
0158     return (would_trap ? 0 : 1);
0159 }
0160 
0161 typedef union {
0162     u32 s;
0163     u64 d;
0164     u64 q[2];
0165 } *argp;
0166 
0167 int do_mathemu(struct pt_regs *regs, struct fpustate *f, bool illegal_insn_trap)
0168 {
0169     unsigned long pc = regs->tpc;
0170     unsigned long tstate = regs->tstate;
0171     u32 insn = 0;
0172     int type = 0;
0173     /* ftt tells which ftt it may happen in, r is rd, b is rs2 and a is rs1. The *u arg tells
0174        whether the argument should be packed/unpacked (0 - do not unpack/pack, 1 - unpack/pack)
0175        non-u args tells the size of the argument (0 - no argument, 1 - single, 2 - double, 3 - quad */
0176 #define TYPE(ftt, r, ru, b, bu, a, au) type = (au << 2) | (a << 0) | (bu << 5) | (b << 3) | (ru << 8) | (r << 6) | (ftt << 9)
0177     int freg;
0178     static u64 zero[2] = { 0L, 0L };
0179     int flags;
0180     FP_DECL_EX;
0181     FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
0182     FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
0183     FP_DECL_Q(QA); FP_DECL_Q(QB); FP_DECL_Q(QR);
0184     int IR;
0185     long XR, xfsr;
0186 
0187     if (tstate & TSTATE_PRIV)
0188         die_if_kernel("unfinished/unimplemented FPop from kernel", regs);
0189     perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0);
0190     if (test_thread_flag(TIF_32BIT))
0191         pc = (u32)pc;
0192     if (get_user(insn, (u32 __user *) pc) != -EFAULT) {
0193         if ((insn & 0xc1f80000) == 0x81a00000) /* FPOP1 */ {
0194             switch ((insn >> 5) & 0x1ff) {
0195             /* QUAD - ftt == 3 */
0196             case FMOVQ:
0197             case FNEGQ:
0198             case FABSQ: TYPE(3,3,0,3,0,0,0); break;
0199             case FSQRTQ: TYPE(3,3,1,3,1,0,0); break;
0200             case FADDQ:
0201             case FSUBQ:
0202             case FMULQ:
0203             case FDIVQ: TYPE(3,3,1,3,1,3,1); break;
0204             case FDMULQ: TYPE(3,3,1,2,1,2,1); break;
0205             case FQTOX: TYPE(3,2,0,3,1,0,0); break;
0206             case FXTOQ: TYPE(3,3,1,2,0,0,0); break;
0207             case FQTOS: TYPE(3,1,1,3,1,0,0); break;
0208             case FQTOD: TYPE(3,2,1,3,1,0,0); break;
0209             case FITOQ: TYPE(3,3,1,1,0,0,0); break;
0210             case FSTOQ: TYPE(3,3,1,1,1,0,0); break;
0211             case FDTOQ: TYPE(3,3,1,2,1,0,0); break;
0212             case FQTOI: TYPE(3,1,0,3,1,0,0); break;
0213 
0214             /* We can get either unimplemented or unfinished
0215              * for these cases.  Pre-Niagara systems generate
0216              * unfinished fpop for SUBNORMAL cases, and Niagara
0217              * always gives unimplemented fpop for fsqrt{s,d}.
0218              */
0219             case FSQRTS: {
0220                 unsigned long x = current_thread_info()->xfsr[0];
0221 
0222                 x = (x >> 14) & 0x7;
0223                 TYPE(x,1,1,1,1,0,0);
0224                 break;
0225             }
0226 
0227             case FSQRTD: {
0228                 unsigned long x = current_thread_info()->xfsr[0];
0229 
0230                 x = (x >> 14) & 0x7;
0231                 TYPE(x,2,1,2,1,0,0);
0232                 break;
0233             }
0234 
0235             /* SUBNORMAL - ftt == 2 */
0236             case FADDD:
0237             case FSUBD:
0238             case FMULD:
0239             case FDIVD: TYPE(2,2,1,2,1,2,1); break;
0240             case FADDS:
0241             case FSUBS:
0242             case FMULS:
0243             case FDIVS: TYPE(2,1,1,1,1,1,1); break;
0244             case FSMULD: TYPE(2,2,1,1,1,1,1); break;
0245             case FSTOX: TYPE(2,2,0,1,1,0,0); break;
0246             case FDTOX: TYPE(2,2,0,2,1,0,0); break;
0247             case FDTOS: TYPE(2,1,1,2,1,0,0); break;
0248             case FSTOD: TYPE(2,2,1,1,1,0,0); break;
0249             case FSTOI: TYPE(2,1,0,1,1,0,0); break;
0250             case FDTOI: TYPE(2,1,0,2,1,0,0); break;
0251 
0252             /* Only Ultra-III generates these */
0253             case FXTOS: TYPE(2,1,1,2,0,0,0); break;
0254             case FXTOD: TYPE(2,2,1,2,0,0,0); break;
0255 #if 0           /* Optimized inline in sparc64/kernel/entry.S */
0256             case FITOS: TYPE(2,1,1,1,0,0,0); break;
0257 #endif
0258             case FITOD: TYPE(2,2,1,1,0,0,0); break;
0259             }
0260         }
0261         else if ((insn & 0xc1f80000) == 0x81a80000) /* FPOP2 */ {
0262             IR = 2;
0263             switch ((insn >> 5) & 0x1ff) {
0264             case FCMPQ: TYPE(3,0,0,3,1,3,1); break;
0265             case FCMPEQ: TYPE(3,0,0,3,1,3,1); break;
0266             /* Now the conditional fmovq support */
0267             case FMOVQ0:
0268             case FMOVQ1:
0269             case FMOVQ2:
0270             case FMOVQ3:
0271                 /* fmovq %fccX, %fY, %fZ */
0272                 if (!((insn >> 11) & 3))
0273                     XR = current_thread_info()->xfsr[0] >> 10;
0274                 else
0275                     XR = current_thread_info()->xfsr[0] >> (30 + ((insn >> 10) & 0x6));
0276                 XR &= 3;
0277                 IR = 0;
0278                 switch ((insn >> 14) & 0x7) {
0279                 /* case 0: IR = 0; break; */            /* Never */
0280                 case 1: if (XR) IR = 1; break;          /* Not Equal */
0281                 case 2: if (XR == 1 || XR == 2) IR = 1; break;  /* Less or Greater */
0282                 case 3: if (XR & 1) IR = 1; break;      /* Unordered or Less */
0283                 case 4: if (XR == 1) IR = 1; break;     /* Less */
0284                 case 5: if (XR & 2) IR = 1; break;      /* Unordered or Greater */
0285                 case 6: if (XR == 2) IR = 1; break;     /* Greater */
0286                 case 7: if (XR == 3) IR = 1; break;     /* Unordered */
0287                 }
0288                 if ((insn >> 14) & 8)
0289                     IR ^= 1;
0290                 break;
0291             case FMOVQI:
0292             case FMOVQX:
0293                 /* fmovq %[ix]cc, %fY, %fZ */
0294                 XR = regs->tstate >> 32;
0295                 if ((insn >> 5) & 0x80)
0296                     XR >>= 4;
0297                 XR &= 0xf;
0298                 IR = 0;
0299                 freg = ((XR >> 2) ^ XR) & 2;
0300                 switch ((insn >> 14) & 0x7) {
0301                 /* case 0: IR = 0; break; */            /* Never */
0302                 case 1: if (XR & 4) IR = 1; break;      /* Equal */
0303                 case 2: if ((XR & 4) || freg) IR = 1; break;    /* Less or Equal */
0304                 case 3: if (freg) IR = 1; break;        /* Less */
0305                 case 4: if (XR & 5) IR = 1; break;      /* Less or Equal Unsigned */
0306                 case 5: if (XR & 1) IR = 1; break;      /* Carry Set */
0307                 case 6: if (XR & 8) IR = 1; break;      /* Negative */
0308                 case 7: if (XR & 2) IR = 1; break;      /* Overflow Set */
0309                 }
0310                 if ((insn >> 14) & 8)
0311                     IR ^= 1;
0312                 break;
0313             case FMOVQZ:
0314             case FMOVQLE:
0315             case FMOVQLZ:
0316             case FMOVQNZ:
0317             case FMOVQGZ:
0318             case FMOVQGE:
0319                 freg = (insn >> 14) & 0x1f;
0320                 if (!freg)
0321                     XR = 0;
0322                 else if (freg < 16)
0323                     XR = regs->u_regs[freg];
0324                 else if (!test_thread_64bit_stack(regs->u_regs[UREG_FP])) {
0325                     struct reg_window32 __user *win32;
0326                     flushw_user ();
0327                     win32 = (struct reg_window32 __user *)((unsigned long)((u32)regs->u_regs[UREG_FP]));
0328                     get_user(XR, &win32->locals[freg - 16]);
0329                 } else {
0330                     struct reg_window __user *win;
0331                     flushw_user ();
0332                     win = (struct reg_window __user *)(regs->u_regs[UREG_FP] + STACK_BIAS);
0333                     get_user(XR, &win->locals[freg - 16]);
0334                 }
0335                 IR = 0;
0336                 switch ((insn >> 10) & 3) {
0337                 case 1: if (!XR) IR = 1; break;         /* Register Zero */
0338                 case 2: if (XR <= 0) IR = 1; break;     /* Register Less Than or Equal to Zero */
0339                 case 3: if (XR < 0) IR = 1; break;      /* Register Less Than Zero */
0340                 }
0341                 if ((insn >> 10) & 4)
0342                     IR ^= 1;
0343                 break;
0344             }
0345             if (IR == 0) {
0346                 /* The fmov test was false. Do a nop instead */
0347                 current_thread_info()->xfsr[0] &= ~(FSR_CEXC_MASK);
0348                 regs->tpc = regs->tnpc;
0349                 regs->tnpc += 4;
0350                 return 1;
0351             } else if (IR == 1) {
0352                 /* Change the instruction into plain fmovq */
0353                 insn = (insn & 0x3e00001f) | 0x81a00060;
0354                 TYPE(3,3,0,3,0,0,0); 
0355             }
0356         }
0357     }
0358     if (type) {
0359         argp rs1 = NULL, rs2 = NULL, rd = NULL;
0360         
0361         /* Starting with UltraSPARC-T2, the cpu does not set the FP Trap
0362          * Type field in the %fsr to unimplemented_FPop.  Nor does it
0363          * use the fp_exception_other trap.  Instead it signals an
0364          * illegal instruction and leaves the FP trap type field of
0365          * the %fsr unchanged.
0366          */
0367         if (!illegal_insn_trap) {
0368             int ftt = (current_thread_info()->xfsr[0] >> 14) & 0x7;
0369             if (ftt != (type >> 9))
0370                 goto err;
0371         }
0372         current_thread_info()->xfsr[0] &= ~0x1c000;
0373         freg = ((insn >> 14) & 0x1f);
0374         switch (type & 0x3) {
0375         case 3: if (freg & 2) {
0376                 current_thread_info()->xfsr[0] |= (6 << 14) /* invalid_fp_register */;
0377                 goto err;
0378             }
0379         case 2: freg = ((freg & 1) << 5) | (freg & 0x1e);
0380         case 1: rs1 = (argp)&f->regs[freg];
0381             flags = (freg < 32) ? FPRS_DL : FPRS_DU; 
0382             if (!(current_thread_info()->fpsaved[0] & flags))
0383                 rs1 = (argp)&zero;
0384             break;
0385         }
0386         switch (type & 0x7) {
0387         case 7: FP_UNPACK_QP (QA, rs1); break;
0388         case 6: FP_UNPACK_DP (DA, rs1); break;
0389         case 5: FP_UNPACK_SP (SA, rs1); break;
0390         }
0391         freg = (insn & 0x1f);
0392         switch ((type >> 3) & 0x3) {
0393         case 3: if (freg & 2) {
0394                 current_thread_info()->xfsr[0] |= (6 << 14) /* invalid_fp_register */;
0395                 goto err;
0396             }
0397         case 2: freg = ((freg & 1) << 5) | (freg & 0x1e);
0398         case 1: rs2 = (argp)&f->regs[freg];
0399             flags = (freg < 32) ? FPRS_DL : FPRS_DU; 
0400             if (!(current_thread_info()->fpsaved[0] & flags))
0401                 rs2 = (argp)&zero;
0402             break;
0403         }
0404         switch ((type >> 3) & 0x7) {
0405         case 7: FP_UNPACK_QP (QB, rs2); break;
0406         case 6: FP_UNPACK_DP (DB, rs2); break;
0407         case 5: FP_UNPACK_SP (SB, rs2); break;
0408         }
0409         freg = ((insn >> 25) & 0x1f);
0410         switch ((type >> 6) & 0x3) {
0411         case 3: if (freg & 2) {
0412                 current_thread_info()->xfsr[0] |= (6 << 14) /* invalid_fp_register */;
0413                 goto err;
0414             }
0415         case 2: freg = ((freg & 1) << 5) | (freg & 0x1e);
0416         case 1: rd = (argp)&f->regs[freg];
0417             flags = (freg < 32) ? FPRS_DL : FPRS_DU; 
0418             if (!(current_thread_info()->fpsaved[0] & FPRS_FEF)) {
0419                 current_thread_info()->fpsaved[0] = FPRS_FEF;
0420                 current_thread_info()->gsr[0] = 0;
0421             }
0422             if (!(current_thread_info()->fpsaved[0] & flags)) {
0423                 if (freg < 32)
0424                     memset(f->regs, 0, 32*sizeof(u32));
0425                 else
0426                     memset(f->regs+32, 0, 32*sizeof(u32));
0427             }
0428             current_thread_info()->fpsaved[0] |= flags;
0429             break;
0430         }
0431         switch ((insn >> 5) & 0x1ff) {
0432         /* + */
0433         case FADDS: FP_ADD_S (SR, SA, SB); break;
0434         case FADDD: FP_ADD_D (DR, DA, DB); break;
0435         case FADDQ: FP_ADD_Q (QR, QA, QB); break;
0436         /* - */
0437         case FSUBS: FP_SUB_S (SR, SA, SB); break;
0438         case FSUBD: FP_SUB_D (DR, DA, DB); break;
0439         case FSUBQ: FP_SUB_Q (QR, QA, QB); break;
0440         /* * */
0441         case FMULS: FP_MUL_S (SR, SA, SB); break;
0442         case FSMULD: FP_CONV (D, S, 1, 1, DA, SA);
0443                  FP_CONV (D, S, 1, 1, DB, SB);
0444         case FMULD: FP_MUL_D (DR, DA, DB); break;
0445         case FDMULQ: FP_CONV (Q, D, 2, 1, QA, DA);
0446                  FP_CONV (Q, D, 2, 1, QB, DB);
0447         case FMULQ: FP_MUL_Q (QR, QA, QB); break;
0448         /* / */
0449         case FDIVS: FP_DIV_S (SR, SA, SB); break;
0450         case FDIVD: FP_DIV_D (DR, DA, DB); break;
0451         case FDIVQ: FP_DIV_Q (QR, QA, QB); break;
0452         /* sqrt */
0453         case FSQRTS: FP_SQRT_S (SR, SB); break;
0454         case FSQRTD: FP_SQRT_D (DR, DB); break;
0455         case FSQRTQ: FP_SQRT_Q (QR, QB); break;
0456         /* mov */
0457         case FMOVQ: rd->q[0] = rs2->q[0]; rd->q[1] = rs2->q[1]; break;
0458         case FABSQ: rd->q[0] = rs2->q[0] & 0x7fffffffffffffffUL; rd->q[1] = rs2->q[1]; break;
0459         case FNEGQ: rd->q[0] = rs2->q[0] ^ 0x8000000000000000UL; rd->q[1] = rs2->q[1]; break;
0460         /* float to int */
0461         case FSTOI: FP_TO_INT_S (IR, SB, 32, 1); break;
0462         case FDTOI: FP_TO_INT_D (IR, DB, 32, 1); break;
0463         case FQTOI: FP_TO_INT_Q (IR, QB, 32, 1); break;
0464         case FSTOX: FP_TO_INT_S (XR, SB, 64, 1); break;
0465         case FDTOX: FP_TO_INT_D (XR, DB, 64, 1); break;
0466         case FQTOX: FP_TO_INT_Q (XR, QB, 64, 1); break;
0467         /* int to float */
0468         case FITOQ: IR = rs2->s; FP_FROM_INT_Q (QR, IR, 32, int); break;
0469         case FXTOQ: XR = rs2->d; FP_FROM_INT_Q (QR, XR, 64, long); break;
0470         /* Only Ultra-III generates these */
0471         case FXTOS: XR = rs2->d; FP_FROM_INT_S (SR, XR, 64, long); break;
0472         case FXTOD: XR = rs2->d; FP_FROM_INT_D (DR, XR, 64, long); break;
0473 #if 0       /* Optimized inline in sparc64/kernel/entry.S */
0474         case FITOS: IR = rs2->s; FP_FROM_INT_S (SR, IR, 32, int); break;
0475 #endif
0476         case FITOD: IR = rs2->s; FP_FROM_INT_D (DR, IR, 32, int); break;
0477         /* float to float */
0478         case FSTOD: FP_CONV (D, S, 1, 1, DR, SB); break;
0479         case FSTOQ: FP_CONV (Q, S, 2, 1, QR, SB); break;
0480         case FDTOQ: FP_CONV (Q, D, 2, 1, QR, DB); break;
0481         case FDTOS: FP_CONV (S, D, 1, 1, SR, DB); break;
0482         case FQTOS: FP_CONV (S, Q, 1, 2, SR, QB); break;
0483         case FQTOD: FP_CONV (D, Q, 1, 2, DR, QB); break;
0484         /* comparison */
0485         case FCMPQ:
0486         case FCMPEQ:
0487             FP_CMP_Q(XR, QB, QA, 3);
0488             if (XR == 3 &&
0489                 (((insn >> 5) & 0x1ff) == FCMPEQ ||
0490                  FP_ISSIGNAN_Q(QA) ||
0491                  FP_ISSIGNAN_Q(QB)))
0492                 FP_SET_EXCEPTION (FP_EX_INVALID);
0493         }
0494         if (!FP_INHIBIT_RESULTS) {
0495             switch ((type >> 6) & 0x7) {
0496             case 0: xfsr = current_thread_info()->xfsr[0];
0497                 if (XR == -1) XR = 2;
0498                 switch (freg & 3) {
0499                 /* fcc0, 1, 2, 3 */
0500                 case 0: xfsr &= ~0xc00; xfsr |= (XR << 10); break;
0501                 case 1: xfsr &= ~0x300000000UL; xfsr |= (XR << 32); break;
0502                 case 2: xfsr &= ~0xc00000000UL; xfsr |= (XR << 34); break;
0503                 case 3: xfsr &= ~0x3000000000UL; xfsr |= (XR << 36); break;
0504                 }
0505                 current_thread_info()->xfsr[0] = xfsr;
0506                 break;
0507             case 1: rd->s = IR; break;
0508             case 2: rd->d = XR; break;
0509             case 5: FP_PACK_SP (rd, SR); break;
0510             case 6: FP_PACK_DP (rd, DR); break;
0511             case 7: FP_PACK_QP (rd, QR); break;
0512             }
0513         }
0514 
0515         if(_fex != 0)
0516             return record_exception(regs, _fex);
0517 
0518         /* Success and no exceptions detected. */
0519         current_thread_info()->xfsr[0] &= ~(FSR_CEXC_MASK);
0520         regs->tpc = regs->tnpc;
0521         regs->tnpc += 4;
0522         return 1;
0523     }
0524 err:    return 0;
0525 }