162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * arch/sparc64/math-emu/math.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1997,1999 Jakub Jelinek (jj@ultra.linux.cz) 662306a36Sopenharmony_ci * Copyright (C) 1999 David S. Miller (davem@redhat.com) 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Emulation routines originate from soft-fp package, which is part 962306a36Sopenharmony_ci * of glibc and has appropriate copyrights in it. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/types.h> 1362306a36Sopenharmony_ci#include <linux/sched.h> 1462306a36Sopenharmony_ci#include <linux/errno.h> 1562306a36Sopenharmony_ci#include <linux/perf_event.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <asm/fpumacro.h> 1862306a36Sopenharmony_ci#include <asm/ptrace.h> 1962306a36Sopenharmony_ci#include <linux/uaccess.h> 2062306a36Sopenharmony_ci#include <asm/cacheflush.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "sfp-util_64.h" 2362306a36Sopenharmony_ci#include <math-emu/soft-fp.h> 2462306a36Sopenharmony_ci#include <math-emu/single.h> 2562306a36Sopenharmony_ci#include <math-emu/double.h> 2662306a36Sopenharmony_ci#include <math-emu/quad.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* QUAD - ftt == 3 */ 2962306a36Sopenharmony_ci#define FMOVQ 0x003 3062306a36Sopenharmony_ci#define FNEGQ 0x007 3162306a36Sopenharmony_ci#define FABSQ 0x00b 3262306a36Sopenharmony_ci#define FSQRTQ 0x02b 3362306a36Sopenharmony_ci#define FADDQ 0x043 3462306a36Sopenharmony_ci#define FSUBQ 0x047 3562306a36Sopenharmony_ci#define FMULQ 0x04b 3662306a36Sopenharmony_ci#define FDIVQ 0x04f 3762306a36Sopenharmony_ci#define FDMULQ 0x06e 3862306a36Sopenharmony_ci#define FQTOX 0x083 3962306a36Sopenharmony_ci#define FXTOQ 0x08c 4062306a36Sopenharmony_ci#define FQTOS 0x0c7 4162306a36Sopenharmony_ci#define FQTOD 0x0cb 4262306a36Sopenharmony_ci#define FITOQ 0x0cc 4362306a36Sopenharmony_ci#define FSTOQ 0x0cd 4462306a36Sopenharmony_ci#define FDTOQ 0x0ce 4562306a36Sopenharmony_ci#define FQTOI 0x0d3 4662306a36Sopenharmony_ci/* SUBNORMAL - ftt == 2 */ 4762306a36Sopenharmony_ci#define FSQRTS 0x029 4862306a36Sopenharmony_ci#define FSQRTD 0x02a 4962306a36Sopenharmony_ci#define FADDS 0x041 5062306a36Sopenharmony_ci#define FADDD 0x042 5162306a36Sopenharmony_ci#define FSUBS 0x045 5262306a36Sopenharmony_ci#define FSUBD 0x046 5362306a36Sopenharmony_ci#define FMULS 0x049 5462306a36Sopenharmony_ci#define FMULD 0x04a 5562306a36Sopenharmony_ci#define FDIVS 0x04d 5662306a36Sopenharmony_ci#define FDIVD 0x04e 5762306a36Sopenharmony_ci#define FSMULD 0x069 5862306a36Sopenharmony_ci#define FSTOX 0x081 5962306a36Sopenharmony_ci#define FDTOX 0x082 6062306a36Sopenharmony_ci#define FDTOS 0x0c6 6162306a36Sopenharmony_ci#define FSTOD 0x0c9 6262306a36Sopenharmony_ci#define FSTOI 0x0d1 6362306a36Sopenharmony_ci#define FDTOI 0x0d2 6462306a36Sopenharmony_ci#define FXTOS 0x084 /* Only Ultra-III generates this. */ 6562306a36Sopenharmony_ci#define FXTOD 0x088 /* Only Ultra-III generates this. */ 6662306a36Sopenharmony_ci#if 0 /* Optimized inline in sparc64/kernel/entry.S */ 6762306a36Sopenharmony_ci#define FITOS 0x0c4 /* Only Ultra-III generates this. */ 6862306a36Sopenharmony_ci#endif 6962306a36Sopenharmony_ci#define FITOD 0x0c8 /* Only Ultra-III generates this. */ 7062306a36Sopenharmony_ci/* FPOP2 */ 7162306a36Sopenharmony_ci#define FCMPQ 0x053 7262306a36Sopenharmony_ci#define FCMPEQ 0x057 7362306a36Sopenharmony_ci#define FMOVQ0 0x003 7462306a36Sopenharmony_ci#define FMOVQ1 0x043 7562306a36Sopenharmony_ci#define FMOVQ2 0x083 7662306a36Sopenharmony_ci#define FMOVQ3 0x0c3 7762306a36Sopenharmony_ci#define FMOVQI 0x103 7862306a36Sopenharmony_ci#define FMOVQX 0x183 7962306a36Sopenharmony_ci#define FMOVQZ 0x027 8062306a36Sopenharmony_ci#define FMOVQLE 0x047 8162306a36Sopenharmony_ci#define FMOVQLZ 0x067 8262306a36Sopenharmony_ci#define FMOVQNZ 0x0a7 8362306a36Sopenharmony_ci#define FMOVQGZ 0x0c7 8462306a36Sopenharmony_ci#define FMOVQGE 0x0e7 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci#define FSR_TEM_SHIFT 23UL 8762306a36Sopenharmony_ci#define FSR_TEM_MASK (0x1fUL << FSR_TEM_SHIFT) 8862306a36Sopenharmony_ci#define FSR_AEXC_SHIFT 5UL 8962306a36Sopenharmony_ci#define FSR_AEXC_MASK (0x1fUL << FSR_AEXC_SHIFT) 9062306a36Sopenharmony_ci#define FSR_CEXC_SHIFT 0UL 9162306a36Sopenharmony_ci#define FSR_CEXC_MASK (0x1fUL << FSR_CEXC_SHIFT) 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* All routines returning an exception to raise should detect 9462306a36Sopenharmony_ci * such exceptions _before_ rounding to be consistent with 9562306a36Sopenharmony_ci * the behavior of the hardware in the implemented cases 9662306a36Sopenharmony_ci * (and thus with the recommendations in the V9 architecture 9762306a36Sopenharmony_ci * manual). 9862306a36Sopenharmony_ci * 9962306a36Sopenharmony_ci * We return 0 if a SIGFPE should be sent, 1 otherwise. 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_cistatic inline int record_exception(struct pt_regs *regs, int eflag) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci u64 fsr = current_thread_info()->xfsr[0]; 10462306a36Sopenharmony_ci int would_trap; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* Determine if this exception would have generated a trap. */ 10762306a36Sopenharmony_ci would_trap = (fsr & ((long)eflag << FSR_TEM_SHIFT)) != 0UL; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* If trapping, we only want to signal one bit. */ 11062306a36Sopenharmony_ci if(would_trap != 0) { 11162306a36Sopenharmony_ci eflag &= ((fsr & FSR_TEM_MASK) >> FSR_TEM_SHIFT); 11262306a36Sopenharmony_ci if((eflag & (eflag - 1)) != 0) { 11362306a36Sopenharmony_ci if(eflag & FP_EX_INVALID) 11462306a36Sopenharmony_ci eflag = FP_EX_INVALID; 11562306a36Sopenharmony_ci else if(eflag & FP_EX_OVERFLOW) 11662306a36Sopenharmony_ci eflag = FP_EX_OVERFLOW; 11762306a36Sopenharmony_ci else if(eflag & FP_EX_UNDERFLOW) 11862306a36Sopenharmony_ci eflag = FP_EX_UNDERFLOW; 11962306a36Sopenharmony_ci else if(eflag & FP_EX_DIVZERO) 12062306a36Sopenharmony_ci eflag = FP_EX_DIVZERO; 12162306a36Sopenharmony_ci else if(eflag & FP_EX_INEXACT) 12262306a36Sopenharmony_ci eflag = FP_EX_INEXACT; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* Set CEXC, here is the rule: 12762306a36Sopenharmony_ci * 12862306a36Sopenharmony_ci * In general all FPU ops will set one and only one 12962306a36Sopenharmony_ci * bit in the CEXC field, this is always the case 13062306a36Sopenharmony_ci * when the IEEE exception trap is enabled in TEM. 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_ci fsr &= ~(FSR_CEXC_MASK); 13362306a36Sopenharmony_ci fsr |= ((long)eflag << FSR_CEXC_SHIFT); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* Set the AEXC field, rule is: 13662306a36Sopenharmony_ci * 13762306a36Sopenharmony_ci * If a trap would not be generated, the 13862306a36Sopenharmony_ci * CEXC just generated is OR'd into the 13962306a36Sopenharmony_ci * existing value of AEXC. 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_ci if(would_trap == 0) 14262306a36Sopenharmony_ci fsr |= ((long)eflag << FSR_AEXC_SHIFT); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* If trapping, indicate fault trap type IEEE. */ 14562306a36Sopenharmony_ci if(would_trap != 0) 14662306a36Sopenharmony_ci fsr |= (1UL << 14); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci current_thread_info()->xfsr[0] = fsr; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* If we will not trap, advance the program counter over 15162306a36Sopenharmony_ci * the instruction being handled. 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_ci if(would_trap == 0) { 15462306a36Sopenharmony_ci regs->tpc = regs->tnpc; 15562306a36Sopenharmony_ci regs->tnpc += 4; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return (would_trap ? 0 : 1); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_citypedef union { 16262306a36Sopenharmony_ci u32 s; 16362306a36Sopenharmony_ci u64 d; 16462306a36Sopenharmony_ci u64 q[2]; 16562306a36Sopenharmony_ci} *argp; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ciint do_mathemu(struct pt_regs *regs, struct fpustate *f, bool illegal_insn_trap) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci unsigned long pc = regs->tpc; 17062306a36Sopenharmony_ci unsigned long tstate = regs->tstate; 17162306a36Sopenharmony_ci u32 insn = 0; 17262306a36Sopenharmony_ci int type = 0; 17362306a36Sopenharmony_ci /* ftt tells which ftt it may happen in, r is rd, b is rs2 and a is rs1. The *u arg tells 17462306a36Sopenharmony_ci whether the argument should be packed/unpacked (0 - do not unpack/pack, 1 - unpack/pack) 17562306a36Sopenharmony_ci non-u args tells the size of the argument (0 - no argument, 1 - single, 2 - double, 3 - quad */ 17662306a36Sopenharmony_ci#define TYPE(ftt, r, ru, b, bu, a, au) type = (au << 2) | (a << 0) | (bu << 5) | (b << 3) | (ru << 8) | (r << 6) | (ftt << 9) 17762306a36Sopenharmony_ci int freg; 17862306a36Sopenharmony_ci static u64 zero[2] = { 0L, 0L }; 17962306a36Sopenharmony_ci int flags; 18062306a36Sopenharmony_ci FP_DECL_EX; 18162306a36Sopenharmony_ci FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR); 18262306a36Sopenharmony_ci FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR); 18362306a36Sopenharmony_ci FP_DECL_Q(QA); FP_DECL_Q(QB); FP_DECL_Q(QR); 18462306a36Sopenharmony_ci int IR; 18562306a36Sopenharmony_ci long XR, xfsr; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (tstate & TSTATE_PRIV) 18862306a36Sopenharmony_ci die_if_kernel("unfinished/unimplemented FPop from kernel", regs); 18962306a36Sopenharmony_ci perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0); 19062306a36Sopenharmony_ci if (test_thread_flag(TIF_32BIT)) 19162306a36Sopenharmony_ci pc = (u32)pc; 19262306a36Sopenharmony_ci if (get_user(insn, (u32 __user *) pc) != -EFAULT) { 19362306a36Sopenharmony_ci if ((insn & 0xc1f80000) == 0x81a00000) /* FPOP1 */ { 19462306a36Sopenharmony_ci switch ((insn >> 5) & 0x1ff) { 19562306a36Sopenharmony_ci /* QUAD - ftt == 3 */ 19662306a36Sopenharmony_ci case FMOVQ: 19762306a36Sopenharmony_ci case FNEGQ: 19862306a36Sopenharmony_ci case FABSQ: TYPE(3,3,0,3,0,0,0); break; 19962306a36Sopenharmony_ci case FSQRTQ: TYPE(3,3,1,3,1,0,0); break; 20062306a36Sopenharmony_ci case FADDQ: 20162306a36Sopenharmony_ci case FSUBQ: 20262306a36Sopenharmony_ci case FMULQ: 20362306a36Sopenharmony_ci case FDIVQ: TYPE(3,3,1,3,1,3,1); break; 20462306a36Sopenharmony_ci case FDMULQ: TYPE(3,3,1,2,1,2,1); break; 20562306a36Sopenharmony_ci case FQTOX: TYPE(3,2,0,3,1,0,0); break; 20662306a36Sopenharmony_ci case FXTOQ: TYPE(3,3,1,2,0,0,0); break; 20762306a36Sopenharmony_ci case FQTOS: TYPE(3,1,1,3,1,0,0); break; 20862306a36Sopenharmony_ci case FQTOD: TYPE(3,2,1,3,1,0,0); break; 20962306a36Sopenharmony_ci case FITOQ: TYPE(3,3,1,1,0,0,0); break; 21062306a36Sopenharmony_ci case FSTOQ: TYPE(3,3,1,1,1,0,0); break; 21162306a36Sopenharmony_ci case FDTOQ: TYPE(3,3,1,2,1,0,0); break; 21262306a36Sopenharmony_ci case FQTOI: TYPE(3,1,0,3,1,0,0); break; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* We can get either unimplemented or unfinished 21562306a36Sopenharmony_ci * for these cases. Pre-Niagara systems generate 21662306a36Sopenharmony_ci * unfinished fpop for SUBNORMAL cases, and Niagara 21762306a36Sopenharmony_ci * always gives unimplemented fpop for fsqrt{s,d}. 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_ci case FSQRTS: { 22062306a36Sopenharmony_ci unsigned long x = current_thread_info()->xfsr[0]; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci x = (x >> 14) & 0x7; 22362306a36Sopenharmony_ci TYPE(x,1,1,1,1,0,0); 22462306a36Sopenharmony_ci break; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci case FSQRTD: { 22862306a36Sopenharmony_ci unsigned long x = current_thread_info()->xfsr[0]; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci x = (x >> 14) & 0x7; 23162306a36Sopenharmony_ci TYPE(x,2,1,2,1,0,0); 23262306a36Sopenharmony_ci break; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* SUBNORMAL - ftt == 2 */ 23662306a36Sopenharmony_ci case FADDD: 23762306a36Sopenharmony_ci case FSUBD: 23862306a36Sopenharmony_ci case FMULD: 23962306a36Sopenharmony_ci case FDIVD: TYPE(2,2,1,2,1,2,1); break; 24062306a36Sopenharmony_ci case FADDS: 24162306a36Sopenharmony_ci case FSUBS: 24262306a36Sopenharmony_ci case FMULS: 24362306a36Sopenharmony_ci case FDIVS: TYPE(2,1,1,1,1,1,1); break; 24462306a36Sopenharmony_ci case FSMULD: TYPE(2,2,1,1,1,1,1); break; 24562306a36Sopenharmony_ci case FSTOX: TYPE(2,2,0,1,1,0,0); break; 24662306a36Sopenharmony_ci case FDTOX: TYPE(2,2,0,2,1,0,0); break; 24762306a36Sopenharmony_ci case FDTOS: TYPE(2,1,1,2,1,0,0); break; 24862306a36Sopenharmony_ci case FSTOD: TYPE(2,2,1,1,1,0,0); break; 24962306a36Sopenharmony_ci case FSTOI: TYPE(2,1,0,1,1,0,0); break; 25062306a36Sopenharmony_ci case FDTOI: TYPE(2,1,0,2,1,0,0); break; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* Only Ultra-III generates these */ 25362306a36Sopenharmony_ci case FXTOS: TYPE(2,1,1,2,0,0,0); break; 25462306a36Sopenharmony_ci case FXTOD: TYPE(2,2,1,2,0,0,0); break; 25562306a36Sopenharmony_ci#if 0 /* Optimized inline in sparc64/kernel/entry.S */ 25662306a36Sopenharmony_ci case FITOS: TYPE(2,1,1,1,0,0,0); break; 25762306a36Sopenharmony_ci#endif 25862306a36Sopenharmony_ci case FITOD: TYPE(2,2,1,1,0,0,0); break; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci else if ((insn & 0xc1f80000) == 0x81a80000) /* FPOP2 */ { 26262306a36Sopenharmony_ci IR = 2; 26362306a36Sopenharmony_ci switch ((insn >> 5) & 0x1ff) { 26462306a36Sopenharmony_ci case FCMPQ: TYPE(3,0,0,3,1,3,1); break; 26562306a36Sopenharmony_ci case FCMPEQ: TYPE(3,0,0,3,1,3,1); break; 26662306a36Sopenharmony_ci /* Now the conditional fmovq support */ 26762306a36Sopenharmony_ci case FMOVQ0: 26862306a36Sopenharmony_ci case FMOVQ1: 26962306a36Sopenharmony_ci case FMOVQ2: 27062306a36Sopenharmony_ci case FMOVQ3: 27162306a36Sopenharmony_ci /* fmovq %fccX, %fY, %fZ */ 27262306a36Sopenharmony_ci if (!((insn >> 11) & 3)) 27362306a36Sopenharmony_ci XR = current_thread_info()->xfsr[0] >> 10; 27462306a36Sopenharmony_ci else 27562306a36Sopenharmony_ci XR = current_thread_info()->xfsr[0] >> (30 + ((insn >> 10) & 0x6)); 27662306a36Sopenharmony_ci XR &= 3; 27762306a36Sopenharmony_ci IR = 0; 27862306a36Sopenharmony_ci switch ((insn >> 14) & 0x7) { 27962306a36Sopenharmony_ci /* case 0: IR = 0; break; */ /* Never */ 28062306a36Sopenharmony_ci case 1: if (XR) IR = 1; break; /* Not Equal */ 28162306a36Sopenharmony_ci case 2: if (XR == 1 || XR == 2) IR = 1; break; /* Less or Greater */ 28262306a36Sopenharmony_ci case 3: if (XR & 1) IR = 1; break; /* Unordered or Less */ 28362306a36Sopenharmony_ci case 4: if (XR == 1) IR = 1; break; /* Less */ 28462306a36Sopenharmony_ci case 5: if (XR & 2) IR = 1; break; /* Unordered or Greater */ 28562306a36Sopenharmony_ci case 6: if (XR == 2) IR = 1; break; /* Greater */ 28662306a36Sopenharmony_ci case 7: if (XR == 3) IR = 1; break; /* Unordered */ 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci if ((insn >> 14) & 8) 28962306a36Sopenharmony_ci IR ^= 1; 29062306a36Sopenharmony_ci break; 29162306a36Sopenharmony_ci case FMOVQI: 29262306a36Sopenharmony_ci case FMOVQX: 29362306a36Sopenharmony_ci /* fmovq %[ix]cc, %fY, %fZ */ 29462306a36Sopenharmony_ci XR = regs->tstate >> 32; 29562306a36Sopenharmony_ci if ((insn >> 5) & 0x80) 29662306a36Sopenharmony_ci XR >>= 4; 29762306a36Sopenharmony_ci XR &= 0xf; 29862306a36Sopenharmony_ci IR = 0; 29962306a36Sopenharmony_ci freg = ((XR >> 2) ^ XR) & 2; 30062306a36Sopenharmony_ci switch ((insn >> 14) & 0x7) { 30162306a36Sopenharmony_ci /* case 0: IR = 0; break; */ /* Never */ 30262306a36Sopenharmony_ci case 1: if (XR & 4) IR = 1; break; /* Equal */ 30362306a36Sopenharmony_ci case 2: if ((XR & 4) || freg) IR = 1; break; /* Less or Equal */ 30462306a36Sopenharmony_ci case 3: if (freg) IR = 1; break; /* Less */ 30562306a36Sopenharmony_ci case 4: if (XR & 5) IR = 1; break; /* Less or Equal Unsigned */ 30662306a36Sopenharmony_ci case 5: if (XR & 1) IR = 1; break; /* Carry Set */ 30762306a36Sopenharmony_ci case 6: if (XR & 8) IR = 1; break; /* Negative */ 30862306a36Sopenharmony_ci case 7: if (XR & 2) IR = 1; break; /* Overflow Set */ 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci if ((insn >> 14) & 8) 31162306a36Sopenharmony_ci IR ^= 1; 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci case FMOVQZ: 31462306a36Sopenharmony_ci case FMOVQLE: 31562306a36Sopenharmony_ci case FMOVQLZ: 31662306a36Sopenharmony_ci case FMOVQNZ: 31762306a36Sopenharmony_ci case FMOVQGZ: 31862306a36Sopenharmony_ci case FMOVQGE: 31962306a36Sopenharmony_ci freg = (insn >> 14) & 0x1f; 32062306a36Sopenharmony_ci if (!freg) 32162306a36Sopenharmony_ci XR = 0; 32262306a36Sopenharmony_ci else if (freg < 16) 32362306a36Sopenharmony_ci XR = regs->u_regs[freg]; 32462306a36Sopenharmony_ci else if (!test_thread_64bit_stack(regs->u_regs[UREG_FP])) { 32562306a36Sopenharmony_ci struct reg_window32 __user *win32; 32662306a36Sopenharmony_ci flushw_user (); 32762306a36Sopenharmony_ci win32 = (struct reg_window32 __user *)((unsigned long)((u32)regs->u_regs[UREG_FP])); 32862306a36Sopenharmony_ci get_user(XR, &win32->locals[freg - 16]); 32962306a36Sopenharmony_ci } else { 33062306a36Sopenharmony_ci struct reg_window __user *win; 33162306a36Sopenharmony_ci flushw_user (); 33262306a36Sopenharmony_ci win = (struct reg_window __user *)(regs->u_regs[UREG_FP] + STACK_BIAS); 33362306a36Sopenharmony_ci get_user(XR, &win->locals[freg - 16]); 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci IR = 0; 33662306a36Sopenharmony_ci switch ((insn >> 10) & 3) { 33762306a36Sopenharmony_ci case 1: if (!XR) IR = 1; break; /* Register Zero */ 33862306a36Sopenharmony_ci case 2: if (XR <= 0) IR = 1; break; /* Register Less Than or Equal to Zero */ 33962306a36Sopenharmony_ci case 3: if (XR < 0) IR = 1; break; /* Register Less Than Zero */ 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci if ((insn >> 10) & 4) 34262306a36Sopenharmony_ci IR ^= 1; 34362306a36Sopenharmony_ci break; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci if (IR == 0) { 34662306a36Sopenharmony_ci /* The fmov test was false. Do a nop instead */ 34762306a36Sopenharmony_ci current_thread_info()->xfsr[0] &= ~(FSR_CEXC_MASK); 34862306a36Sopenharmony_ci regs->tpc = regs->tnpc; 34962306a36Sopenharmony_ci regs->tnpc += 4; 35062306a36Sopenharmony_ci return 1; 35162306a36Sopenharmony_ci } else if (IR == 1) { 35262306a36Sopenharmony_ci /* Change the instruction into plain fmovq */ 35362306a36Sopenharmony_ci insn = (insn & 0x3e00001f) | 0x81a00060; 35462306a36Sopenharmony_ci TYPE(3,3,0,3,0,0,0); 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci if (type) { 35962306a36Sopenharmony_ci argp rs1 = NULL, rs2 = NULL, rd = NULL; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* Starting with UltraSPARC-T2, the cpu does not set the FP Trap 36262306a36Sopenharmony_ci * Type field in the %fsr to unimplemented_FPop. Nor does it 36362306a36Sopenharmony_ci * use the fp_exception_other trap. Instead it signals an 36462306a36Sopenharmony_ci * illegal instruction and leaves the FP trap type field of 36562306a36Sopenharmony_ci * the %fsr unchanged. 36662306a36Sopenharmony_ci */ 36762306a36Sopenharmony_ci if (!illegal_insn_trap) { 36862306a36Sopenharmony_ci int ftt = (current_thread_info()->xfsr[0] >> 14) & 0x7; 36962306a36Sopenharmony_ci if (ftt != (type >> 9)) 37062306a36Sopenharmony_ci goto err; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci current_thread_info()->xfsr[0] &= ~0x1c000; 37362306a36Sopenharmony_ci freg = ((insn >> 14) & 0x1f); 37462306a36Sopenharmony_ci switch (type & 0x3) { 37562306a36Sopenharmony_ci case 3: if (freg & 2) { 37662306a36Sopenharmony_ci current_thread_info()->xfsr[0] |= (6 << 14) /* invalid_fp_register */; 37762306a36Sopenharmony_ci goto err; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci case 2: freg = ((freg & 1) << 5) | (freg & 0x1e); 38062306a36Sopenharmony_ci case 1: rs1 = (argp)&f->regs[freg]; 38162306a36Sopenharmony_ci flags = (freg < 32) ? FPRS_DL : FPRS_DU; 38262306a36Sopenharmony_ci if (!(current_thread_info()->fpsaved[0] & flags)) 38362306a36Sopenharmony_ci rs1 = (argp)&zero; 38462306a36Sopenharmony_ci break; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci switch (type & 0x7) { 38762306a36Sopenharmony_ci case 7: FP_UNPACK_QP (QA, rs1); break; 38862306a36Sopenharmony_ci case 6: FP_UNPACK_DP (DA, rs1); break; 38962306a36Sopenharmony_ci case 5: FP_UNPACK_SP (SA, rs1); break; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci freg = (insn & 0x1f); 39262306a36Sopenharmony_ci switch ((type >> 3) & 0x3) { 39362306a36Sopenharmony_ci case 3: if (freg & 2) { 39462306a36Sopenharmony_ci current_thread_info()->xfsr[0] |= (6 << 14) /* invalid_fp_register */; 39562306a36Sopenharmony_ci goto err; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci case 2: freg = ((freg & 1) << 5) | (freg & 0x1e); 39862306a36Sopenharmony_ci case 1: rs2 = (argp)&f->regs[freg]; 39962306a36Sopenharmony_ci flags = (freg < 32) ? FPRS_DL : FPRS_DU; 40062306a36Sopenharmony_ci if (!(current_thread_info()->fpsaved[0] & flags)) 40162306a36Sopenharmony_ci rs2 = (argp)&zero; 40262306a36Sopenharmony_ci break; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci switch ((type >> 3) & 0x7) { 40562306a36Sopenharmony_ci case 7: FP_UNPACK_QP (QB, rs2); break; 40662306a36Sopenharmony_ci case 6: FP_UNPACK_DP (DB, rs2); break; 40762306a36Sopenharmony_ci case 5: FP_UNPACK_SP (SB, rs2); break; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci freg = ((insn >> 25) & 0x1f); 41062306a36Sopenharmony_ci switch ((type >> 6) & 0x3) { 41162306a36Sopenharmony_ci case 3: if (freg & 2) { 41262306a36Sopenharmony_ci current_thread_info()->xfsr[0] |= (6 << 14) /* invalid_fp_register */; 41362306a36Sopenharmony_ci goto err; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci case 2: freg = ((freg & 1) << 5) | (freg & 0x1e); 41662306a36Sopenharmony_ci case 1: rd = (argp)&f->regs[freg]; 41762306a36Sopenharmony_ci flags = (freg < 32) ? FPRS_DL : FPRS_DU; 41862306a36Sopenharmony_ci if (!(current_thread_info()->fpsaved[0] & FPRS_FEF)) { 41962306a36Sopenharmony_ci current_thread_info()->fpsaved[0] = FPRS_FEF; 42062306a36Sopenharmony_ci current_thread_info()->gsr[0] = 0; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci if (!(current_thread_info()->fpsaved[0] & flags)) { 42362306a36Sopenharmony_ci if (freg < 32) 42462306a36Sopenharmony_ci memset(f->regs, 0, 32*sizeof(u32)); 42562306a36Sopenharmony_ci else 42662306a36Sopenharmony_ci memset(f->regs+32, 0, 32*sizeof(u32)); 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci current_thread_info()->fpsaved[0] |= flags; 42962306a36Sopenharmony_ci break; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci switch ((insn >> 5) & 0x1ff) { 43262306a36Sopenharmony_ci /* + */ 43362306a36Sopenharmony_ci case FADDS: FP_ADD_S (SR, SA, SB); break; 43462306a36Sopenharmony_ci case FADDD: FP_ADD_D (DR, DA, DB); break; 43562306a36Sopenharmony_ci case FADDQ: FP_ADD_Q (QR, QA, QB); break; 43662306a36Sopenharmony_ci /* - */ 43762306a36Sopenharmony_ci case FSUBS: FP_SUB_S (SR, SA, SB); break; 43862306a36Sopenharmony_ci case FSUBD: FP_SUB_D (DR, DA, DB); break; 43962306a36Sopenharmony_ci case FSUBQ: FP_SUB_Q (QR, QA, QB); break; 44062306a36Sopenharmony_ci /* * */ 44162306a36Sopenharmony_ci case FMULS: FP_MUL_S (SR, SA, SB); break; 44262306a36Sopenharmony_ci case FSMULD: FP_CONV (D, S, 1, 1, DA, SA); 44362306a36Sopenharmony_ci FP_CONV (D, S, 1, 1, DB, SB); 44462306a36Sopenharmony_ci case FMULD: FP_MUL_D (DR, DA, DB); break; 44562306a36Sopenharmony_ci case FDMULQ: FP_CONV (Q, D, 2, 1, QA, DA); 44662306a36Sopenharmony_ci FP_CONV (Q, D, 2, 1, QB, DB); 44762306a36Sopenharmony_ci case FMULQ: FP_MUL_Q (QR, QA, QB); break; 44862306a36Sopenharmony_ci /* / */ 44962306a36Sopenharmony_ci case FDIVS: FP_DIV_S (SR, SA, SB); break; 45062306a36Sopenharmony_ci case FDIVD: FP_DIV_D (DR, DA, DB); break; 45162306a36Sopenharmony_ci case FDIVQ: FP_DIV_Q (QR, QA, QB); break; 45262306a36Sopenharmony_ci /* sqrt */ 45362306a36Sopenharmony_ci case FSQRTS: FP_SQRT_S (SR, SB); break; 45462306a36Sopenharmony_ci case FSQRTD: FP_SQRT_D (DR, DB); break; 45562306a36Sopenharmony_ci case FSQRTQ: FP_SQRT_Q (QR, QB); break; 45662306a36Sopenharmony_ci /* mov */ 45762306a36Sopenharmony_ci case FMOVQ: rd->q[0] = rs2->q[0]; rd->q[1] = rs2->q[1]; break; 45862306a36Sopenharmony_ci case FABSQ: rd->q[0] = rs2->q[0] & 0x7fffffffffffffffUL; rd->q[1] = rs2->q[1]; break; 45962306a36Sopenharmony_ci case FNEGQ: rd->q[0] = rs2->q[0] ^ 0x8000000000000000UL; rd->q[1] = rs2->q[1]; break; 46062306a36Sopenharmony_ci /* float to int */ 46162306a36Sopenharmony_ci case FSTOI: FP_TO_INT_S (IR, SB, 32, 1); break; 46262306a36Sopenharmony_ci case FDTOI: FP_TO_INT_D (IR, DB, 32, 1); break; 46362306a36Sopenharmony_ci case FQTOI: FP_TO_INT_Q (IR, QB, 32, 1); break; 46462306a36Sopenharmony_ci case FSTOX: FP_TO_INT_S (XR, SB, 64, 1); break; 46562306a36Sopenharmony_ci case FDTOX: FP_TO_INT_D (XR, DB, 64, 1); break; 46662306a36Sopenharmony_ci case FQTOX: FP_TO_INT_Q (XR, QB, 64, 1); break; 46762306a36Sopenharmony_ci /* int to float */ 46862306a36Sopenharmony_ci case FITOQ: IR = rs2->s; FP_FROM_INT_Q (QR, IR, 32, int); break; 46962306a36Sopenharmony_ci case FXTOQ: XR = rs2->d; FP_FROM_INT_Q (QR, XR, 64, long); break; 47062306a36Sopenharmony_ci /* Only Ultra-III generates these */ 47162306a36Sopenharmony_ci case FXTOS: XR = rs2->d; FP_FROM_INT_S (SR, XR, 64, long); break; 47262306a36Sopenharmony_ci case FXTOD: XR = rs2->d; FP_FROM_INT_D (DR, XR, 64, long); break; 47362306a36Sopenharmony_ci#if 0 /* Optimized inline in sparc64/kernel/entry.S */ 47462306a36Sopenharmony_ci case FITOS: IR = rs2->s; FP_FROM_INT_S (SR, IR, 32, int); break; 47562306a36Sopenharmony_ci#endif 47662306a36Sopenharmony_ci case FITOD: IR = rs2->s; FP_FROM_INT_D (DR, IR, 32, int); break; 47762306a36Sopenharmony_ci /* float to float */ 47862306a36Sopenharmony_ci case FSTOD: FP_CONV (D, S, 1, 1, DR, SB); break; 47962306a36Sopenharmony_ci case FSTOQ: FP_CONV (Q, S, 2, 1, QR, SB); break; 48062306a36Sopenharmony_ci case FDTOQ: FP_CONV (Q, D, 2, 1, QR, DB); break; 48162306a36Sopenharmony_ci case FDTOS: FP_CONV (S, D, 1, 1, SR, DB); break; 48262306a36Sopenharmony_ci case FQTOS: FP_CONV (S, Q, 1, 2, SR, QB); break; 48362306a36Sopenharmony_ci case FQTOD: FP_CONV (D, Q, 1, 2, DR, QB); break; 48462306a36Sopenharmony_ci /* comparison */ 48562306a36Sopenharmony_ci case FCMPQ: 48662306a36Sopenharmony_ci case FCMPEQ: 48762306a36Sopenharmony_ci FP_CMP_Q(XR, QB, QA, 3); 48862306a36Sopenharmony_ci if (XR == 3 && 48962306a36Sopenharmony_ci (((insn >> 5) & 0x1ff) == FCMPEQ || 49062306a36Sopenharmony_ci FP_ISSIGNAN_Q(QA) || 49162306a36Sopenharmony_ci FP_ISSIGNAN_Q(QB))) 49262306a36Sopenharmony_ci FP_SET_EXCEPTION (FP_EX_INVALID); 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci if (!FP_INHIBIT_RESULTS) { 49562306a36Sopenharmony_ci switch ((type >> 6) & 0x7) { 49662306a36Sopenharmony_ci case 0: xfsr = current_thread_info()->xfsr[0]; 49762306a36Sopenharmony_ci if (XR == -1) XR = 2; 49862306a36Sopenharmony_ci switch (freg & 3) { 49962306a36Sopenharmony_ci /* fcc0, 1, 2, 3 */ 50062306a36Sopenharmony_ci case 0: xfsr &= ~0xc00; xfsr |= (XR << 10); break; 50162306a36Sopenharmony_ci case 1: xfsr &= ~0x300000000UL; xfsr |= (XR << 32); break; 50262306a36Sopenharmony_ci case 2: xfsr &= ~0xc00000000UL; xfsr |= (XR << 34); break; 50362306a36Sopenharmony_ci case 3: xfsr &= ~0x3000000000UL; xfsr |= (XR << 36); break; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci current_thread_info()->xfsr[0] = xfsr; 50662306a36Sopenharmony_ci break; 50762306a36Sopenharmony_ci case 1: rd->s = IR; break; 50862306a36Sopenharmony_ci case 2: rd->d = XR; break; 50962306a36Sopenharmony_ci case 5: FP_PACK_SP (rd, SR); break; 51062306a36Sopenharmony_ci case 6: FP_PACK_DP (rd, DR); break; 51162306a36Sopenharmony_ci case 7: FP_PACK_QP (rd, QR); break; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if(_fex != 0) 51662306a36Sopenharmony_ci return record_exception(regs, _fex); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci /* Success and no exceptions detected. */ 51962306a36Sopenharmony_ci current_thread_info()->xfsr[0] &= ~(FSR_CEXC_MASK); 52062306a36Sopenharmony_ci regs->tpc = regs->tnpc; 52162306a36Sopenharmony_ci regs->tnpc += 4; 52262306a36Sopenharmony_ci return 1; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_cierr: return 0; 52562306a36Sopenharmony_ci} 526