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