/arch/sparc/math-emu/math_64.c
C | 515 lines | 420 code | 26 blank | 69 comment | 101 complexity | 45dd8c8197511df4b5380412d2b8dc5b MD5 | raw file
Possible License(s): LGPL-2.0, AGPL-1.0, GPL-2.0
- /*
- * arch/sparc64/math-emu/math.c
- *
- * Copyright (C) 1997,1999 Jakub Jelinek (jj@ultra.linux.cz)
- * Copyright (C) 1999 David S. Miller (davem@redhat.com)
- *
- * Emulation routines originate from soft-fp package, which is part
- * of glibc and has appropriate copyrights in it.
- */
- #include <linux/types.h>
- #include <linux/sched.h>
- #include <linux/errno.h>
- #include <linux/perf_event.h>
- #include <asm/fpumacro.h>
- #include <asm/ptrace.h>
- #include <asm/uaccess.h>
- #include "sfp-util_64.h"
- #include <math-emu/soft-fp.h>
- #include <math-emu/single.h>
- #include <math-emu/double.h>
- #include <math-emu/quad.h>
- /* QUAD - ftt == 3 */
- #define FMOVQ 0x003
- #define FNEGQ 0x007
- #define FABSQ 0x00b
- #define FSQRTQ 0x02b
- #define FADDQ 0x043
- #define FSUBQ 0x047
- #define FMULQ 0x04b
- #define FDIVQ 0x04f
- #define FDMULQ 0x06e
- #define FQTOX 0x083
- #define FXTOQ 0x08c
- #define FQTOS 0x0c7
- #define FQTOD 0x0cb
- #define FITOQ 0x0cc
- #define FSTOQ 0x0cd
- #define FDTOQ 0x0ce
- #define FQTOI 0x0d3
- /* SUBNORMAL - ftt == 2 */
- #define FSQRTS 0x029
- #define FSQRTD 0x02a
- #define FADDS 0x041
- #define FADDD 0x042
- #define FSUBS 0x045
- #define FSUBD 0x046
- #define FMULS 0x049
- #define FMULD 0x04a
- #define FDIVS 0x04d
- #define FDIVD 0x04e
- #define FSMULD 0x069
- #define FSTOX 0x081
- #define FDTOX 0x082
- #define FDTOS 0x0c6
- #define FSTOD 0x0c9
- #define FSTOI 0x0d1
- #define FDTOI 0x0d2
- #define FXTOS 0x084 /* Only Ultra-III generates this. */
- #define FXTOD 0x088 /* Only Ultra-III generates this. */
- #if 0 /* Optimized inline in sparc64/kernel/entry.S */
- #define FITOS 0x0c4 /* Only Ultra-III generates this. */
- #endif
- #define FITOD 0x0c8 /* Only Ultra-III generates this. */
- /* FPOP2 */
- #define FCMPQ 0x053
- #define FCMPEQ 0x057
- #define FMOVQ0 0x003
- #define FMOVQ1 0x043
- #define FMOVQ2 0x083
- #define FMOVQ3 0x0c3
- #define FMOVQI 0x103
- #define FMOVQX 0x183
- #define FMOVQZ 0x027
- #define FMOVQLE 0x047
- #define FMOVQLZ 0x067
- #define FMOVQNZ 0x0a7
- #define FMOVQGZ 0x0c7
- #define FMOVQGE 0x0e7
- #define FSR_TEM_SHIFT 23UL
- #define FSR_TEM_MASK (0x1fUL << FSR_TEM_SHIFT)
- #define FSR_AEXC_SHIFT 5UL
- #define FSR_AEXC_MASK (0x1fUL << FSR_AEXC_SHIFT)
- #define FSR_CEXC_SHIFT 0UL
- #define FSR_CEXC_MASK (0x1fUL << FSR_CEXC_SHIFT)
- /* All routines returning an exception to raise should detect
- * such exceptions _before_ rounding to be consistent with
- * the behavior of the hardware in the implemented cases
- * (and thus with the recommendations in the V9 architecture
- * manual).
- *
- * We return 0 if a SIGFPE should be sent, 1 otherwise.
- */
- static inline int record_exception(struct pt_regs *regs, int eflag)
- {
- u64 fsr = current_thread_info()->xfsr[0];
- int would_trap;
- /* Determine if this exception would have generated a trap. */
- would_trap = (fsr & ((long)eflag << FSR_TEM_SHIFT)) != 0UL;
- /* If trapping, we only want to signal one bit. */
- if(would_trap != 0) {
- eflag &= ((fsr & FSR_TEM_MASK) >> FSR_TEM_SHIFT);
- if((eflag & (eflag - 1)) != 0) {
- if(eflag & FP_EX_INVALID)
- eflag = FP_EX_INVALID;
- else if(eflag & FP_EX_OVERFLOW)
- eflag = FP_EX_OVERFLOW;
- else if(eflag & FP_EX_UNDERFLOW)
- eflag = FP_EX_UNDERFLOW;
- else if(eflag & FP_EX_DIVZERO)
- eflag = FP_EX_DIVZERO;
- else if(eflag & FP_EX_INEXACT)
- eflag = FP_EX_INEXACT;
- }
- }
- /* Set CEXC, here is the rule:
- *
- * In general all FPU ops will set one and only one
- * bit in the CEXC field, this is always the case
- * when the IEEE exception trap is enabled in TEM.
- */
- fsr &= ~(FSR_CEXC_MASK);
- fsr |= ((long)eflag << FSR_CEXC_SHIFT);
- /* Set the AEXC field, rule is:
- *
- * If a trap would not be generated, the
- * CEXC just generated is OR'd into the
- * existing value of AEXC.
- */
- if(would_trap == 0)
- fsr |= ((long)eflag << FSR_AEXC_SHIFT);
- /* If trapping, indicate fault trap type IEEE. */
- if(would_trap != 0)
- fsr |= (1UL << 14);
- current_thread_info()->xfsr[0] = fsr;
- /* If we will not trap, advance the program counter over
- * the instruction being handled.
- */
- if(would_trap == 0) {
- regs->tpc = regs->tnpc;
- regs->tnpc += 4;
- }
- return (would_trap ? 0 : 1);
- }
- typedef union {
- u32 s;
- u64 d;
- u64 q[2];
- } *argp;
- int do_mathemu(struct pt_regs *regs, struct fpustate *f)
- {
- unsigned long pc = regs->tpc;
- unsigned long tstate = regs->tstate;
- u32 insn = 0;
- int type = 0;
- /* ftt tells which ftt it may happen in, r is rd, b is rs2 and a is rs1. The *u arg tells
- whether the argument should be packed/unpacked (0 - do not unpack/pack, 1 - unpack/pack)
- non-u args tells the size of the argument (0 - no argument, 1 - single, 2 - double, 3 - quad */
- #define TYPE(ftt, r, ru, b, bu, a, au) type = (au << 2) | (a << 0) | (bu << 5) | (b << 3) | (ru << 8) | (r << 6) | (ftt << 9)
- int freg;
- static u64 zero[2] = { 0L, 0L };
- int flags;
- FP_DECL_EX;
- FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
- FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
- FP_DECL_Q(QA); FP_DECL_Q(QB); FP_DECL_Q(QR);
- int IR;
- long XR, xfsr;
- if (tstate & TSTATE_PRIV)
- die_if_kernel("unfinished/unimplemented FPop from kernel", regs);
- perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, 0, regs, 0);
- if (test_thread_flag(TIF_32BIT))
- pc = (u32)pc;
- if (get_user(insn, (u32 __user *) pc) != -EFAULT) {
- if ((insn & 0xc1f80000) == 0x81a00000) /* FPOP1 */ {
- switch ((insn >> 5) & 0x1ff) {
- /* QUAD - ftt == 3 */
- case FMOVQ:
- case FNEGQ:
- case FABSQ: TYPE(3,3,0,3,0,0,0); break;
- case FSQRTQ: TYPE(3,3,1,3,1,0,0); break;
- case FADDQ:
- case FSUBQ:
- case FMULQ:
- case FDIVQ: TYPE(3,3,1,3,1,3,1); break;
- case FDMULQ: TYPE(3,3,1,2,1,2,1); break;
- case FQTOX: TYPE(3,2,0,3,1,0,0); break;
- case FXTOQ: TYPE(3,3,1,2,0,0,0); break;
- case FQTOS: TYPE(3,1,1,3,1,0,0); break;
- case FQTOD: TYPE(3,2,1,3,1,0,0); break;
- case FITOQ: TYPE(3,3,1,1,0,0,0); break;
- case FSTOQ: TYPE(3,3,1,1,1,0,0); break;
- case FDTOQ: TYPE(3,3,1,2,1,0,0); break;
- case FQTOI: TYPE(3,1,0,3,1,0,0); break;
- /* We can get either unimplemented or unfinished
- * for these cases. Pre-Niagara systems generate
- * unfinished fpop for SUBNORMAL cases, and Niagara
- * always gives unimplemented fpop for fsqrt{s,d}.
- */
- case FSQRTS: {
- unsigned long x = current_thread_info()->xfsr[0];
- x = (x >> 14) & 0xf;
- TYPE(x,1,1,1,1,0,0);
- break;
- }
- case FSQRTD: {
- unsigned long x = current_thread_info()->xfsr[0];
- x = (x >> 14) & 0xf;
- TYPE(x,2,1,2,1,0,0);
- break;
- }
- /* SUBNORMAL - ftt == 2 */
- case FADDD:
- case FSUBD:
- case FMULD:
- case FDIVD: TYPE(2,2,1,2,1,2,1); break;
- case FADDS:
- case FSUBS:
- case FMULS:
- case FDIVS: TYPE(2,1,1,1,1,1,1); break;
- case FSMULD: TYPE(2,2,1,1,1,1,1); break;
- case FSTOX: TYPE(2,2,0,1,1,0,0); break;
- case FDTOX: TYPE(2,2,0,2,1,0,0); break;
- case FDTOS: TYPE(2,1,1,2,1,0,0); break;
- case FSTOD: TYPE(2,2,1,1,1,0,0); break;
- case FSTOI: TYPE(2,1,0,1,1,0,0); break;
-