xref: /linux/arch/sh/kernel/cpu/sh4/fpu.c (revision 3cf5d076)
16ecc0a4dSKuninori Morimoto // SPDX-License-Identifier: GPL-2.0
2c8c0a1abSStuart Menefy /*
31da177e4SLinus Torvalds  * Save/restore floating point context for signal handlers.
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Copyright (C) 1999, 2000  Kaz Kojima & Niibe Yutaka
6c8c0a1abSStuart Menefy  * Copyright (C) 2006  ST Microelectronics Ltd. (denorm support)
71da177e4SLinus Torvalds  *
8c8c0a1abSStuart Menefy  * FIXME! These routines have not been tested for big endian case.
91da177e4SLinus Torvalds  */
10c3edc401SIngo Molnar #include <linux/sched/signal.h>
11c8c0a1abSStuart Menefy #include <linux/io.h>
12f15cbe6fSPaul Mundt #include <cpu/fpu.h>
131da177e4SLinus Torvalds #include <asm/processor.h>
149bbafce2SPaul Mundt #include <asm/fpu.h>
15f03c4866SPaul Mundt #include <asm/traps.h>
161da177e4SLinus Torvalds 
171da177e4SLinus Torvalds /* The PR (precision) bit in the FP Status Register must be clear when
181da177e4SLinus Torvalds  * an frchg instruction is executed, otherwise the instruction is undefined.
191da177e4SLinus Torvalds  * Executing frchg with PR set causes a trap on some SH4 implementations.
201da177e4SLinus Torvalds  */
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds #define FPSCR_RCHG 0x00000000
23c8c0a1abSStuart Menefy extern unsigned long long float64_div(unsigned long long a,
24c8c0a1abSStuart Menefy 				      unsigned long long b);
25c8c0a1abSStuart Menefy extern unsigned long int float32_div(unsigned long int a, unsigned long int b);
26c8c0a1abSStuart Menefy extern unsigned long long float64_mul(unsigned long long a,
27c8c0a1abSStuart Menefy 				      unsigned long long b);
28c8c0a1abSStuart Menefy extern unsigned long int float32_mul(unsigned long int a, unsigned long int b);
29c8c0a1abSStuart Menefy extern unsigned long long float64_add(unsigned long long a,
30c8c0a1abSStuart Menefy 				      unsigned long long b);
31c8c0a1abSStuart Menefy extern unsigned long int float32_add(unsigned long int a, unsigned long int b);
32c8c0a1abSStuart Menefy extern unsigned long long float64_sub(unsigned long long a,
33c8c0a1abSStuart Menefy 				      unsigned long long b);
34c8c0a1abSStuart Menefy extern unsigned long int float32_sub(unsigned long int a, unsigned long int b);
35b6ad1e8cSCarl Shaw extern unsigned long int float64_to_float32(unsigned long long a);
36c8c0a1abSStuart Menefy static unsigned int fpu_exception_flags;
371da177e4SLinus Torvalds 
381da177e4SLinus Torvalds /*
391da177e4SLinus Torvalds  * Save FPU registers onto task structure.
401da177e4SLinus Torvalds  */
save_fpu(struct task_struct * tsk)41d3ea9fa0SStuart Menefy void save_fpu(struct task_struct *tsk)
421da177e4SLinus Torvalds {
431da177e4SLinus Torvalds 	unsigned long dummy;
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds 	enable_fpu();
461da177e4SLinus Torvalds 	asm volatile ("sts.l	fpul, @-%0\n\t"
471da177e4SLinus Torvalds 		      "sts.l	fpscr, @-%0\n\t"
481da177e4SLinus Torvalds 		      "lds	%2, fpscr\n\t"
491da177e4SLinus Torvalds 		      "frchg\n\t"
501da177e4SLinus Torvalds 		      "fmov.s	fr15, @-%0\n\t"
511da177e4SLinus Torvalds 		      "fmov.s	fr14, @-%0\n\t"
521da177e4SLinus Torvalds 		      "fmov.s	fr13, @-%0\n\t"
531da177e4SLinus Torvalds 		      "fmov.s	fr12, @-%0\n\t"
541da177e4SLinus Torvalds 		      "fmov.s	fr11, @-%0\n\t"
551da177e4SLinus Torvalds 		      "fmov.s	fr10, @-%0\n\t"
561da177e4SLinus Torvalds 		      "fmov.s	fr9, @-%0\n\t"
571da177e4SLinus Torvalds 		      "fmov.s	fr8, @-%0\n\t"
581da177e4SLinus Torvalds 		      "fmov.s	fr7, @-%0\n\t"
591da177e4SLinus Torvalds 		      "fmov.s	fr6, @-%0\n\t"
601da177e4SLinus Torvalds 		      "fmov.s	fr5, @-%0\n\t"
611da177e4SLinus Torvalds 		      "fmov.s	fr4, @-%0\n\t"
621da177e4SLinus Torvalds 		      "fmov.s	fr3, @-%0\n\t"
631da177e4SLinus Torvalds 		      "fmov.s	fr2, @-%0\n\t"
641da177e4SLinus Torvalds 		      "fmov.s	fr1, @-%0\n\t"
651da177e4SLinus Torvalds 		      "fmov.s	fr0, @-%0\n\t"
661da177e4SLinus Torvalds 		      "frchg\n\t"
671da177e4SLinus Torvalds 		      "fmov.s	fr15, @-%0\n\t"
681da177e4SLinus Torvalds 		      "fmov.s	fr14, @-%0\n\t"
691da177e4SLinus Torvalds 		      "fmov.s	fr13, @-%0\n\t"
701da177e4SLinus Torvalds 		      "fmov.s	fr12, @-%0\n\t"
711da177e4SLinus Torvalds 		      "fmov.s	fr11, @-%0\n\t"
721da177e4SLinus Torvalds 		      "fmov.s	fr10, @-%0\n\t"
731da177e4SLinus Torvalds 		      "fmov.s	fr9, @-%0\n\t"
741da177e4SLinus Torvalds 		      "fmov.s	fr8, @-%0\n\t"
751da177e4SLinus Torvalds 		      "fmov.s	fr7, @-%0\n\t"
761da177e4SLinus Torvalds 		      "fmov.s	fr6, @-%0\n\t"
771da177e4SLinus Torvalds 		      "fmov.s	fr5, @-%0\n\t"
781da177e4SLinus Torvalds 		      "fmov.s	fr4, @-%0\n\t"
791da177e4SLinus Torvalds 		      "fmov.s	fr3, @-%0\n\t"
801da177e4SLinus Torvalds 		      "fmov.s	fr2, @-%0\n\t"
811da177e4SLinus Torvalds 		      "fmov.s	fr1, @-%0\n\t"
821da177e4SLinus Torvalds 		      "fmov.s	fr0, @-%0\n\t"
83c8c0a1abSStuart Menefy 		      "lds	%3, fpscr\n\t":"=r" (dummy)
840ea820cfSPaul Mundt 		      :"0"((char *)(&tsk->thread.xstate->hardfpu.status)),
85c8c0a1abSStuart Menefy 		      "r"(FPSCR_RCHG), "r"(FPSCR_INIT)
861da177e4SLinus Torvalds 		      :"memory");
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds 	disable_fpu();
891da177e4SLinus Torvalds }
901da177e4SLinus Torvalds 
restore_fpu(struct task_struct * tsk)910ea820cfSPaul Mundt void restore_fpu(struct task_struct *tsk)
921da177e4SLinus Torvalds {
931da177e4SLinus Torvalds 	unsigned long dummy;
941da177e4SLinus Torvalds 
951da177e4SLinus Torvalds 	enable_fpu();
961da177e4SLinus Torvalds 	asm volatile ("lds	%2, fpscr\n\t"
971da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr0\n\t"
981da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr1\n\t"
991da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr2\n\t"
1001da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr3\n\t"
1011da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr4\n\t"
1021da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr5\n\t"
1031da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr6\n\t"
1041da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr7\n\t"
1051da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr8\n\t"
1061da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr9\n\t"
1071da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr10\n\t"
1081da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr11\n\t"
1091da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr12\n\t"
1101da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr13\n\t"
1111da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr14\n\t"
1121da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr15\n\t"
1131da177e4SLinus Torvalds 		      "frchg\n\t"
1141da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr0\n\t"
1151da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr1\n\t"
1161da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr2\n\t"
1171da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr3\n\t"
1181da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr4\n\t"
1191da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr5\n\t"
1201da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr6\n\t"
1211da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr7\n\t"
1221da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr8\n\t"
1231da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr9\n\t"
1241da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr10\n\t"
1251da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr11\n\t"
1261da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr12\n\t"
1271da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr13\n\t"
1281da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr14\n\t"
1291da177e4SLinus Torvalds 		      "fmov.s	@%0+, fr15\n\t"
1301da177e4SLinus Torvalds 		      "frchg\n\t"
1311da177e4SLinus Torvalds 		      "lds.l	@%0+, fpscr\n\t"
1321da177e4SLinus Torvalds 		      "lds.l	@%0+, fpul\n\t"
1331da177e4SLinus Torvalds 		      :"=r" (dummy)
1340ea820cfSPaul Mundt 		      :"0" (tsk->thread.xstate), "r" (FPSCR_RCHG)
1351da177e4SLinus Torvalds 		      :"memory");
1361da177e4SLinus Torvalds 	disable_fpu();
1371da177e4SLinus Torvalds }
1381da177e4SLinus Torvalds 
1391da177e4SLinus Torvalds /**
1401da177e4SLinus Torvalds  *      denormal_to_double - Given denormalized float number,
1411da177e4SLinus Torvalds  *                           store double float
1421da177e4SLinus Torvalds  *
1431da177e4SLinus Torvalds  *      @fpu: Pointer to sh_fpu_hard structure
1441da177e4SLinus Torvalds  *      @n: Index to FP register
1451da177e4SLinus Torvalds  */
denormal_to_double(struct sh_fpu_hard_struct * fpu,int n)146c8c0a1abSStuart Menefy static void denormal_to_double(struct sh_fpu_hard_struct *fpu, int n)
1471da177e4SLinus Torvalds {
1481da177e4SLinus Torvalds 	unsigned long du, dl;
1491da177e4SLinus Torvalds 	unsigned long x = fpu->fpul;
1501da177e4SLinus Torvalds 	int exp = 1023 - 126;
1511da177e4SLinus Torvalds 
1521da177e4SLinus Torvalds 	if (x != 0 && (x & 0x7f800000) == 0) {
1531da177e4SLinus Torvalds 		du = (x & 0x80000000);
1541da177e4SLinus Torvalds 		while ((x & 0x00800000) == 0) {
1551da177e4SLinus Torvalds 			x <<= 1;
1561da177e4SLinus Torvalds 			exp--;
1571da177e4SLinus Torvalds 		}
1581da177e4SLinus Torvalds 		x &= 0x007fffff;
1591da177e4SLinus Torvalds 		du |= (exp << 20) | (x >> 3);
1601da177e4SLinus Torvalds 		dl = x << 29;
1611da177e4SLinus Torvalds 
1621da177e4SLinus Torvalds 		fpu->fp_regs[n] = du;
1631da177e4SLinus Torvalds 		fpu->fp_regs[n + 1] = dl;
1641da177e4SLinus Torvalds 	}
1651da177e4SLinus Torvalds }
1661da177e4SLinus Torvalds 
1671da177e4SLinus Torvalds /**
1681da177e4SLinus Torvalds  *	ieee_fpe_handler - Handle denormalized number exception
1691da177e4SLinus Torvalds  *
1701da177e4SLinus Torvalds  *	@regs: Pointer to register structure
1711da177e4SLinus Torvalds  *
1721da177e4SLinus Torvalds  *	Returns 1 when it's handled (should not cause exception).
1731da177e4SLinus Torvalds  */
ieee_fpe_handler(struct pt_regs * regs)174c8c0a1abSStuart Menefy static int ieee_fpe_handler(struct pt_regs *regs)
1751da177e4SLinus Torvalds {
1761da177e4SLinus Torvalds 	unsigned short insn = *(unsigned short *)regs->pc;
1771da177e4SLinus Torvalds 	unsigned short finsn;
1781da177e4SLinus Torvalds 	unsigned long nextpc;
1791da177e4SLinus Torvalds 	int nib[4] = {
1801da177e4SLinus Torvalds 		(insn >> 12) & 0xf,
1811da177e4SLinus Torvalds 		(insn >> 8) & 0xf,
1821da177e4SLinus Torvalds 		(insn >> 4) & 0xf,
183c8c0a1abSStuart Menefy 		insn & 0xf
184c8c0a1abSStuart Menefy 	};
1851da177e4SLinus Torvalds 
186c8c0a1abSStuart Menefy 	if (nib[0] == 0xb || (nib[0] == 0x4 && nib[2] == 0x0 && nib[3] == 0xb))
187c8c0a1abSStuart Menefy 		regs->pr = regs->pc + 4;  /* bsr & jsr */
188c8c0a1abSStuart Menefy 
189c8c0a1abSStuart Menefy 	if (nib[0] == 0xa || nib[0] == 0xb) {
190c8c0a1abSStuart Menefy 		/* bra & bsr */
1911da177e4SLinus Torvalds 		nextpc = regs->pc + 4 + ((short)((insn & 0xfff) << 4) >> 3);
1921da177e4SLinus Torvalds 		finsn = *(unsigned short *)(regs->pc + 2);
193c8c0a1abSStuart Menefy 	} else if (nib[0] == 0x8 && nib[1] == 0xd) {
194c8c0a1abSStuart Menefy 		/* bt/s */
1951da177e4SLinus Torvalds 		if (regs->sr & 1)
1961da177e4SLinus Torvalds 			nextpc = regs->pc + 4 + ((char)(insn & 0xff) << 1);
1971da177e4SLinus Torvalds 		else
1981da177e4SLinus Torvalds 			nextpc = regs->pc + 4;
1991da177e4SLinus Torvalds 		finsn = *(unsigned short *)(regs->pc + 2);
200c8c0a1abSStuart Menefy 	} else if (nib[0] == 0x8 && nib[1] == 0xf) {
201c8c0a1abSStuart Menefy 		/* bf/s */
2021da177e4SLinus Torvalds 		if (regs->sr & 1)
2031da177e4SLinus Torvalds 			nextpc = regs->pc + 4;
2041da177e4SLinus Torvalds 		else
2051da177e4SLinus Torvalds 			nextpc = regs->pc + 4 + ((char)(insn & 0xff) << 1);
2061da177e4SLinus Torvalds 		finsn = *(unsigned short *)(regs->pc + 2);
2071da177e4SLinus Torvalds 	} else if (nib[0] == 0x4 && nib[3] == 0xb &&
208c8c0a1abSStuart Menefy 		   (nib[2] == 0x0 || nib[2] == 0x2)) {
209c8c0a1abSStuart Menefy 		/* jmp & jsr */
2101da177e4SLinus Torvalds 		nextpc = regs->regs[nib[1]];
2111da177e4SLinus Torvalds 		finsn = *(unsigned short *)(regs->pc + 2);
2121da177e4SLinus Torvalds 	} else if (nib[0] == 0x0 && nib[3] == 0x3 &&
213c8c0a1abSStuart Menefy 		   (nib[2] == 0x0 || nib[2] == 0x2)) {
214c8c0a1abSStuart Menefy 		/* braf & bsrf */
2151da177e4SLinus Torvalds 		nextpc = regs->pc + 4 + regs->regs[nib[1]];
2161da177e4SLinus Torvalds 		finsn = *(unsigned short *)(regs->pc + 2);
217c8c0a1abSStuart Menefy 	} else if (insn == 0x000b) {
218c8c0a1abSStuart Menefy 		/* rts */
2191da177e4SLinus Torvalds 		nextpc = regs->pr;
2201da177e4SLinus Torvalds 		finsn = *(unsigned short *)(regs->pc + 2);
2211da177e4SLinus Torvalds 	} else {
22253f983a9SPaul Mundt 		nextpc = regs->pc + instruction_size(insn);
2231da177e4SLinus Torvalds 		finsn = insn;
2241da177e4SLinus Torvalds 	}
2251da177e4SLinus Torvalds 
226c8c0a1abSStuart Menefy 	if ((finsn & 0xf1ff) == 0xf0ad) {
227c8c0a1abSStuart Menefy 		/* fcnvsd */
2281da177e4SLinus Torvalds 		struct task_struct *tsk = current;
2291da177e4SLinus Torvalds 
2300ea820cfSPaul Mundt 		if ((tsk->thread.xstate->hardfpu.fpscr & FPSCR_CAUSE_ERROR))
2311da177e4SLinus Torvalds 			/* FPU error */
2320ea820cfSPaul Mundt 			denormal_to_double(&tsk->thread.xstate->hardfpu,
2331da177e4SLinus Torvalds 					   (finsn >> 8) & 0xf);
234c8c0a1abSStuart Menefy 		else
235c8c0a1abSStuart Menefy 			return 0;
236c8c0a1abSStuart Menefy 
237c8c0a1abSStuart Menefy 		regs->pc = nextpc;
238c8c0a1abSStuart Menefy 		return 1;
239c8c0a1abSStuart Menefy 	} else if ((finsn & 0xf00f) == 0xf002) {
240c8c0a1abSStuart Menefy 		/* fmul */
241c8c0a1abSStuart Menefy 		struct task_struct *tsk = current;
242c8c0a1abSStuart Menefy 		int fpscr;
243c8c0a1abSStuart Menefy 		int n, m, prec;
244c8c0a1abSStuart Menefy 		unsigned int hx, hy;
245c8c0a1abSStuart Menefy 
246c8c0a1abSStuart Menefy 		n = (finsn >> 8) & 0xf;
247c8c0a1abSStuart Menefy 		m = (finsn >> 4) & 0xf;
2480ea820cfSPaul Mundt 		hx = tsk->thread.xstate->hardfpu.fp_regs[n];
2490ea820cfSPaul Mundt 		hy = tsk->thread.xstate->hardfpu.fp_regs[m];
2500ea820cfSPaul Mundt 		fpscr = tsk->thread.xstate->hardfpu.fpscr;
251c8c0a1abSStuart Menefy 		prec = fpscr & FPSCR_DBL_PRECISION;
252c8c0a1abSStuart Menefy 
253c8c0a1abSStuart Menefy 		if ((fpscr & FPSCR_CAUSE_ERROR)
254c8c0a1abSStuart Menefy 		    && (prec && ((hx & 0x7fffffff) < 0x00100000
255c8c0a1abSStuart Menefy 				 || (hy & 0x7fffffff) < 0x00100000))) {
256c8c0a1abSStuart Menefy 			long long llx, lly;
257c8c0a1abSStuart Menefy 
258c8c0a1abSStuart Menefy 			/* FPU error because of denormal (doubles) */
259c8c0a1abSStuart Menefy 			llx = ((long long)hx << 32)
2600ea820cfSPaul Mundt 			    | tsk->thread.xstate->hardfpu.fp_regs[n + 1];
261c8c0a1abSStuart Menefy 			lly = ((long long)hy << 32)
2620ea820cfSPaul Mundt 			    | tsk->thread.xstate->hardfpu.fp_regs[m + 1];
263c8c0a1abSStuart Menefy 			llx = float64_mul(llx, lly);
2640ea820cfSPaul Mundt 			tsk->thread.xstate->hardfpu.fp_regs[n] = llx >> 32;
2650ea820cfSPaul Mundt 			tsk->thread.xstate->hardfpu.fp_regs[n + 1] = llx & 0xffffffff;
266c8c0a1abSStuart Menefy 		} else if ((fpscr & FPSCR_CAUSE_ERROR)
267c8c0a1abSStuart Menefy 			   && (!prec && ((hx & 0x7fffffff) < 0x00800000
268c8c0a1abSStuart Menefy 					 || (hy & 0x7fffffff) < 0x00800000))) {
269c8c0a1abSStuart Menefy 			/* FPU error because of denormal (floats) */
270c8c0a1abSStuart Menefy 			hx = float32_mul(hx, hy);
2710ea820cfSPaul Mundt 			tsk->thread.xstate->hardfpu.fp_regs[n] = hx;
272b5a1bcbeSStuart Menefy 		} else
273c8c0a1abSStuart Menefy 			return 0;
274c8c0a1abSStuart Menefy 
275c8c0a1abSStuart Menefy 		regs->pc = nextpc;
276c8c0a1abSStuart Menefy 		return 1;
277c8c0a1abSStuart Menefy 	} else if ((finsn & 0xf00e) == 0xf000) {
278c8c0a1abSStuart Menefy 		/* fadd, fsub */
279c8c0a1abSStuart Menefy 		struct task_struct *tsk = current;
280c8c0a1abSStuart Menefy 		int fpscr;
281c8c0a1abSStuart Menefy 		int n, m, prec;
282c8c0a1abSStuart Menefy 		unsigned int hx, hy;
283c8c0a1abSStuart Menefy 
284c8c0a1abSStuart Menefy 		n = (finsn >> 8) & 0xf;
285c8c0a1abSStuart Menefy 		m = (finsn >> 4) & 0xf;
2860ea820cfSPaul Mundt 		hx = tsk->thread.xstate->hardfpu.fp_regs[n];
2870ea820cfSPaul Mundt 		hy = tsk->thread.xstate->hardfpu.fp_regs[m];
2880ea820cfSPaul Mundt 		fpscr = tsk->thread.xstate->hardfpu.fpscr;
289c8c0a1abSStuart Menefy 		prec = fpscr & FPSCR_DBL_PRECISION;
290c8c0a1abSStuart Menefy 
291c8c0a1abSStuart Menefy 		if ((fpscr & FPSCR_CAUSE_ERROR)
292c8c0a1abSStuart Menefy 		    && (prec && ((hx & 0x7fffffff) < 0x00100000
293c8c0a1abSStuart Menefy 				 || (hy & 0x7fffffff) < 0x00100000))) {
294c8c0a1abSStuart Menefy 			long long llx, lly;
295c8c0a1abSStuart Menefy 
296c8c0a1abSStuart Menefy 			/* FPU error because of denormal (doubles) */
297c8c0a1abSStuart Menefy 			llx = ((long long)hx << 32)
2980ea820cfSPaul Mundt 			    | tsk->thread.xstate->hardfpu.fp_regs[n + 1];
299c8c0a1abSStuart Menefy 			lly = ((long long)hy << 32)
3000ea820cfSPaul Mundt 			    | tsk->thread.xstate->hardfpu.fp_regs[m + 1];
301c8c0a1abSStuart Menefy 			if ((finsn & 0xf00f) == 0xf000)
302c8c0a1abSStuart Menefy 				llx = float64_add(llx, lly);
303c8c0a1abSStuart Menefy 			else
304c8c0a1abSStuart Menefy 				llx = float64_sub(llx, lly);
3050ea820cfSPaul Mundt 			tsk->thread.xstate->hardfpu.fp_regs[n] = llx >> 32;
3060ea820cfSPaul Mundt 			tsk->thread.xstate->hardfpu.fp_regs[n + 1] = llx & 0xffffffff;
307c8c0a1abSStuart Menefy 		} else if ((fpscr & FPSCR_CAUSE_ERROR)
308c8c0a1abSStuart Menefy 			   && (!prec && ((hx & 0x7fffffff) < 0x00800000
309c8c0a1abSStuart Menefy 					 || (hy & 0x7fffffff) < 0x00800000))) {
310c8c0a1abSStuart Menefy 			/* FPU error because of denormal (floats) */
311c8c0a1abSStuart Menefy 			if ((finsn & 0xf00f) == 0xf000)
312c8c0a1abSStuart Menefy 				hx = float32_add(hx, hy);
313c8c0a1abSStuart Menefy 			else
314c8c0a1abSStuart Menefy 				hx = float32_sub(hx, hy);
3150ea820cfSPaul Mundt 			tsk->thread.xstate->hardfpu.fp_regs[n] = hx;
316c8c0a1abSStuart Menefy 		} else
317c8c0a1abSStuart Menefy 			return 0;
318c8c0a1abSStuart Menefy 
319c8c0a1abSStuart Menefy 		regs->pc = nextpc;
320c8c0a1abSStuart Menefy 		return 1;
321c8c0a1abSStuart Menefy 	} else if ((finsn & 0xf003) == 0xf003) {
322c8c0a1abSStuart Menefy 		/* fdiv */
323c8c0a1abSStuart Menefy 		struct task_struct *tsk = current;
324c8c0a1abSStuart Menefy 		int fpscr;
325c8c0a1abSStuart Menefy 		int n, m, prec;
326c8c0a1abSStuart Menefy 		unsigned int hx, hy;
327c8c0a1abSStuart Menefy 
328c8c0a1abSStuart Menefy 		n = (finsn >> 8) & 0xf;
329c8c0a1abSStuart Menefy 		m = (finsn >> 4) & 0xf;
3300ea820cfSPaul Mundt 		hx = tsk->thread.xstate->hardfpu.fp_regs[n];
3310ea820cfSPaul Mundt 		hy = tsk->thread.xstate->hardfpu.fp_regs[m];
3320ea820cfSPaul Mundt 		fpscr = tsk->thread.xstate->hardfpu.fpscr;
333c8c0a1abSStuart Menefy 		prec = fpscr & FPSCR_DBL_PRECISION;
334c8c0a1abSStuart Menefy 
335c8c0a1abSStuart Menefy 		if ((fpscr & FPSCR_CAUSE_ERROR)
336c8c0a1abSStuart Menefy 		    && (prec && ((hx & 0x7fffffff) < 0x00100000
337c8c0a1abSStuart Menefy 				 || (hy & 0x7fffffff) < 0x00100000))) {
338c8c0a1abSStuart Menefy 			long long llx, lly;
339c8c0a1abSStuart Menefy 
340c8c0a1abSStuart Menefy 			/* FPU error because of denormal (doubles) */
341c8c0a1abSStuart Menefy 			llx = ((long long)hx << 32)
3420ea820cfSPaul Mundt 			    | tsk->thread.xstate->hardfpu.fp_regs[n + 1];
343c8c0a1abSStuart Menefy 			lly = ((long long)hy << 32)
3440ea820cfSPaul Mundt 			    | tsk->thread.xstate->hardfpu.fp_regs[m + 1];
345c8c0a1abSStuart Menefy 
346c8c0a1abSStuart Menefy 			llx = float64_div(llx, lly);
347c8c0a1abSStuart Menefy 
3480ea820cfSPaul Mundt 			tsk->thread.xstate->hardfpu.fp_regs[n] = llx >> 32;
3490ea820cfSPaul Mundt 			tsk->thread.xstate->hardfpu.fp_regs[n + 1] = llx & 0xffffffff;
350c8c0a1abSStuart Menefy 		} else if ((fpscr & FPSCR_CAUSE_ERROR)
351c8c0a1abSStuart Menefy 			   && (!prec && ((hx & 0x7fffffff) < 0x00800000
352c8c0a1abSStuart Menefy 					 || (hy & 0x7fffffff) < 0x00800000))) {
353c8c0a1abSStuart Menefy 			/* FPU error because of denormal (floats) */
354c8c0a1abSStuart Menefy 			hx = float32_div(hx, hy);
3550ea820cfSPaul Mundt 			tsk->thread.xstate->hardfpu.fp_regs[n] = hx;
356c8c0a1abSStuart Menefy 		} else
357c8c0a1abSStuart Menefy 			return 0;
3581da177e4SLinus Torvalds 
3591da177e4SLinus Torvalds 		regs->pc = nextpc;
3601da177e4SLinus Torvalds 		return 1;
361b6ad1e8cSCarl Shaw 	} else if ((finsn & 0xf0bd) == 0xf0bd) {
362b6ad1e8cSCarl Shaw 		/* fcnvds - double to single precision convert */
363b6ad1e8cSCarl Shaw 		struct task_struct *tsk = current;
364b6ad1e8cSCarl Shaw 		int m;
365b6ad1e8cSCarl Shaw 		unsigned int hx;
366b6ad1e8cSCarl Shaw 
3670f6dee23SCarmelo AMOROSO 		m = (finsn >> 8) & 0x7;
3680ea820cfSPaul Mundt 		hx = tsk->thread.xstate->hardfpu.fp_regs[m];
369b6ad1e8cSCarl Shaw 
3700ea820cfSPaul Mundt 		if ((tsk->thread.xstate->hardfpu.fpscr & FPSCR_CAUSE_ERROR)
371b6ad1e8cSCarl Shaw 			&& ((hx & 0x7fffffff) < 0x00100000)) {
372b6ad1e8cSCarl Shaw 			/* subnormal double to float conversion */
373b6ad1e8cSCarl Shaw 			long long llx;
374b6ad1e8cSCarl Shaw 
3750ea820cfSPaul Mundt 			llx = ((long long)tsk->thread.xstate->hardfpu.fp_regs[m] << 32)
3760ea820cfSPaul Mundt 			    | tsk->thread.xstate->hardfpu.fp_regs[m + 1];
377b6ad1e8cSCarl Shaw 
3780ea820cfSPaul Mundt 			tsk->thread.xstate->hardfpu.fpul = float64_to_float32(llx);
379b6ad1e8cSCarl Shaw 		} else
380b6ad1e8cSCarl Shaw 			return 0;
381b6ad1e8cSCarl Shaw 
382b6ad1e8cSCarl Shaw 		regs->pc = nextpc;
383b6ad1e8cSCarl Shaw 		return 1;
3841da177e4SLinus Torvalds 	}
3851da177e4SLinus Torvalds 
3861da177e4SLinus Torvalds 	return 0;
3871da177e4SLinus Torvalds }
3881da177e4SLinus Torvalds 
float_raise(unsigned int flags)389c8c0a1abSStuart Menefy void float_raise(unsigned int flags)
390c8c0a1abSStuart Menefy {
391c8c0a1abSStuart Menefy 	fpu_exception_flags |= flags;
392c8c0a1abSStuart Menefy }
393c8c0a1abSStuart Menefy 
float_rounding_mode(void)394c8c0a1abSStuart Menefy int float_rounding_mode(void)
395c8c0a1abSStuart Menefy {
396c8c0a1abSStuart Menefy 	struct task_struct *tsk = current;
3970ea820cfSPaul Mundt 	int roundingMode = FPSCR_ROUNDING_MODE(tsk->thread.xstate->hardfpu.fpscr);
398c8c0a1abSStuart Menefy 	return roundingMode;
399c8c0a1abSStuart Menefy }
400c8c0a1abSStuart Menefy 
BUILD_TRAP_HANDLER(fpu_error)40174d99a5eSPaul Mundt BUILD_TRAP_HANDLER(fpu_error)
4021da177e4SLinus Torvalds {
4031da177e4SLinus Torvalds 	struct task_struct *tsk = current;
40474d99a5eSPaul Mundt 	TRAP_HANDLER_DECL;
4051da177e4SLinus Torvalds 
406d3ea9fa0SStuart Menefy 	__unlazy_fpu(tsk, regs);
407c8c0a1abSStuart Menefy 	fpu_exception_flags = 0;
408c8c0a1abSStuart Menefy 	if (ieee_fpe_handler(regs)) {
4090ea820cfSPaul Mundt 		tsk->thread.xstate->hardfpu.fpscr &=
410c8c0a1abSStuart Menefy 		    ~(FPSCR_CAUSE_MASK | FPSCR_FLAG_MASK);
4110ea820cfSPaul Mundt 		tsk->thread.xstate->hardfpu.fpscr |= fpu_exception_flags;
412c8c0a1abSStuart Menefy 		/* Set the FPSCR flag as well as cause bits - simply
413c8c0a1abSStuart Menefy 		 * replicate the cause */
4140ea820cfSPaul Mundt 		tsk->thread.xstate->hardfpu.fpscr |= (fpu_exception_flags >> 10);
415c8c0a1abSStuart Menefy 		grab_fpu(regs);
416c8c0a1abSStuart Menefy 		restore_fpu(tsk);
417d3ea9fa0SStuart Menefy 		task_thread_info(tsk)->status |= TS_USEDFPU;
4180ea820cfSPaul Mundt 		if ((((tsk->thread.xstate->hardfpu.fpscr & FPSCR_ENABLE_MASK) >> 7) &
419c8c0a1abSStuart Menefy 		     (fpu_exception_flags >> 2)) == 0) {
420c8c0a1abSStuart Menefy 			return;
421c8c0a1abSStuart Menefy 		}
422c8c0a1abSStuart Menefy 	}
423c8c0a1abSStuart Menefy 
424*3cf5d076SEric W. Biederman 	force_sig(SIGFPE);
4251da177e4SLinus Torvalds }
426