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