1234a0538SKuninori Morimoto // SPDX-License-Identifier: GPL-2.0 274d99a5eSPaul Mundt /* 374d99a5eSPaul Mundt * Save/restore floating point context for signal handlers. 474d99a5eSPaul Mundt * 574d99a5eSPaul Mundt * Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka 674d99a5eSPaul Mundt * 774d99a5eSPaul Mundt * FIXME! These routines can be optimized in big endian case. 874d99a5eSPaul Mundt */ 93f07c014SIngo Molnar #include <linux/sched/signal.h> 1074d99a5eSPaul Mundt #include <linux/signal.h> 1174d99a5eSPaul Mundt #include <asm/processor.h> 1274d99a5eSPaul Mundt #include <asm/io.h> 139bbafce2SPaul Mundt #include <asm/fpu.h> 14f03c4866SPaul Mundt #include <asm/traps.h> 1574d99a5eSPaul Mundt 1674d99a5eSPaul Mundt /* The PR (precision) bit in the FP Status Register must be clear when 1774d99a5eSPaul Mundt * an frchg instruction is executed, otherwise the instruction is undefined. 1874d99a5eSPaul Mundt * Executing frchg with PR set causes a trap on some SH4 implementations. 1974d99a5eSPaul Mundt */ 2074d99a5eSPaul Mundt 2174d99a5eSPaul Mundt #define FPSCR_RCHG 0x00000000 2274d99a5eSPaul Mundt 2374d99a5eSPaul Mundt 2474d99a5eSPaul Mundt /* 2574d99a5eSPaul Mundt * Save FPU registers onto task structure. 2674d99a5eSPaul Mundt */ 270ea820cfSPaul Mundt void save_fpu(struct task_struct *tsk) 2874d99a5eSPaul Mundt { 2974d99a5eSPaul Mundt unsigned long dummy; 3074d99a5eSPaul Mundt 3174d99a5eSPaul Mundt enable_fpu(); 3274d99a5eSPaul Mundt asm volatile("sts.l fpul, @-%0\n\t" 3374d99a5eSPaul Mundt "sts.l fpscr, @-%0\n\t" 3474d99a5eSPaul Mundt "fmov.s fr15, @-%0\n\t" 3574d99a5eSPaul Mundt "fmov.s fr14, @-%0\n\t" 3674d99a5eSPaul Mundt "fmov.s fr13, @-%0\n\t" 3774d99a5eSPaul Mundt "fmov.s fr12, @-%0\n\t" 3874d99a5eSPaul Mundt "fmov.s fr11, @-%0\n\t" 3974d99a5eSPaul Mundt "fmov.s fr10, @-%0\n\t" 4074d99a5eSPaul Mundt "fmov.s fr9, @-%0\n\t" 4174d99a5eSPaul Mundt "fmov.s fr8, @-%0\n\t" 4274d99a5eSPaul Mundt "fmov.s fr7, @-%0\n\t" 4374d99a5eSPaul Mundt "fmov.s fr6, @-%0\n\t" 4474d99a5eSPaul Mundt "fmov.s fr5, @-%0\n\t" 4574d99a5eSPaul Mundt "fmov.s fr4, @-%0\n\t" 4674d99a5eSPaul Mundt "fmov.s fr3, @-%0\n\t" 4774d99a5eSPaul Mundt "fmov.s fr2, @-%0\n\t" 4874d99a5eSPaul Mundt "fmov.s fr1, @-%0\n\t" 4974d99a5eSPaul Mundt "fmov.s fr0, @-%0\n\t" 5074d99a5eSPaul Mundt "lds %3, fpscr\n\t" 5174d99a5eSPaul Mundt : "=r" (dummy) 520ea820cfSPaul Mundt : "0" ((char *)(&tsk->thread.xstate->hardfpu.status)), 5374d99a5eSPaul Mundt "r" (FPSCR_RCHG), 5474d99a5eSPaul Mundt "r" (FPSCR_INIT) 5574d99a5eSPaul Mundt : "memory"); 5674d99a5eSPaul Mundt 5774d99a5eSPaul Mundt disable_fpu(); 5874d99a5eSPaul Mundt } 5974d99a5eSPaul Mundt 600ea820cfSPaul Mundt void restore_fpu(struct task_struct *tsk) 6174d99a5eSPaul Mundt { 6274d99a5eSPaul Mundt unsigned long dummy; 6374d99a5eSPaul Mundt 6474d99a5eSPaul Mundt enable_fpu(); 6574d99a5eSPaul Mundt asm volatile("fmov.s @%0+, fr0\n\t" 6674d99a5eSPaul Mundt "fmov.s @%0+, fr1\n\t" 6774d99a5eSPaul Mundt "fmov.s @%0+, fr2\n\t" 6874d99a5eSPaul Mundt "fmov.s @%0+, fr3\n\t" 6974d99a5eSPaul Mundt "fmov.s @%0+, fr4\n\t" 7074d99a5eSPaul Mundt "fmov.s @%0+, fr5\n\t" 7174d99a5eSPaul Mundt "fmov.s @%0+, fr6\n\t" 7274d99a5eSPaul Mundt "fmov.s @%0+, fr7\n\t" 7374d99a5eSPaul Mundt "fmov.s @%0+, fr8\n\t" 7474d99a5eSPaul Mundt "fmov.s @%0+, fr9\n\t" 7574d99a5eSPaul Mundt "fmov.s @%0+, fr10\n\t" 7674d99a5eSPaul Mundt "fmov.s @%0+, fr11\n\t" 7774d99a5eSPaul Mundt "fmov.s @%0+, fr12\n\t" 7874d99a5eSPaul Mundt "fmov.s @%0+, fr13\n\t" 7974d99a5eSPaul Mundt "fmov.s @%0+, fr14\n\t" 8074d99a5eSPaul Mundt "fmov.s @%0+, fr15\n\t" 8174d99a5eSPaul Mundt "lds.l @%0+, fpscr\n\t" 8274d99a5eSPaul Mundt "lds.l @%0+, fpul\n\t" 8374d99a5eSPaul Mundt : "=r" (dummy) 840ea820cfSPaul Mundt : "0" (tsk->thread.xstate), "r" (FPSCR_RCHG) 8574d99a5eSPaul Mundt : "memory"); 8674d99a5eSPaul Mundt disable_fpu(); 8774d99a5eSPaul Mundt } 8874d99a5eSPaul Mundt 8974d99a5eSPaul Mundt /* 9074d99a5eSPaul Mundt * Emulate arithmetic ops on denormalized number for some FPU insns. 9174d99a5eSPaul Mundt */ 9274d99a5eSPaul Mundt 9374d99a5eSPaul Mundt /* denormalized float * float */ 9474d99a5eSPaul Mundt static int denormal_mulf(int hx, int hy) 9574d99a5eSPaul Mundt { 9674d99a5eSPaul Mundt unsigned int ix, iy; 9774d99a5eSPaul Mundt unsigned long long m, n; 9874d99a5eSPaul Mundt int exp, w; 9974d99a5eSPaul Mundt 10074d99a5eSPaul Mundt ix = hx & 0x7fffffff; 10174d99a5eSPaul Mundt iy = hy & 0x7fffffff; 10274d99a5eSPaul Mundt if (iy < 0x00800000 || ix == 0) 10374d99a5eSPaul Mundt return ((hx ^ hy) & 0x80000000); 10474d99a5eSPaul Mundt 10574d99a5eSPaul Mundt exp = (iy & 0x7f800000) >> 23; 10674d99a5eSPaul Mundt ix &= 0x007fffff; 10774d99a5eSPaul Mundt iy = (iy & 0x007fffff) | 0x00800000; 10874d99a5eSPaul Mundt m = (unsigned long long)ix * iy; 10974d99a5eSPaul Mundt n = m; 11074d99a5eSPaul Mundt w = -1; 11174d99a5eSPaul Mundt while (n) { n >>= 1; w++; } 11274d99a5eSPaul Mundt 11374d99a5eSPaul Mundt /* FIXME: use guard bits */ 11474d99a5eSPaul Mundt exp += w - 126 - 46; 11574d99a5eSPaul Mundt if (exp > 0) 11674d99a5eSPaul Mundt ix = ((int) (m >> (w - 23)) & 0x007fffff) | (exp << 23); 11774d99a5eSPaul Mundt else if (exp + 22 >= 0) 11874d99a5eSPaul Mundt ix = (int) (m >> (w - 22 - exp)) & 0x007fffff; 11974d99a5eSPaul Mundt else 12074d99a5eSPaul Mundt ix = 0; 12174d99a5eSPaul Mundt 12274d99a5eSPaul Mundt ix |= (hx ^ hy) & 0x80000000; 12374d99a5eSPaul Mundt return ix; 12474d99a5eSPaul Mundt } 12574d99a5eSPaul Mundt 12674d99a5eSPaul Mundt /* denormalized double * double */ 12774d99a5eSPaul Mundt static void mult64(unsigned long long x, unsigned long long y, 12874d99a5eSPaul Mundt unsigned long long *highp, unsigned long long *lowp) 12974d99a5eSPaul Mundt { 13074d99a5eSPaul Mundt unsigned long long sub0, sub1, sub2, sub3; 13174d99a5eSPaul Mundt unsigned long long high, low; 13274d99a5eSPaul Mundt 13374d99a5eSPaul Mundt sub0 = (x >> 32) * (unsigned long) (y >> 32); 13474d99a5eSPaul Mundt sub1 = (x & 0xffffffffLL) * (unsigned long) (y >> 32); 13574d99a5eSPaul Mundt sub2 = (x >> 32) * (unsigned long) (y & 0xffffffffLL); 13674d99a5eSPaul Mundt sub3 = (x & 0xffffffffLL) * (unsigned long) (y & 0xffffffffLL); 13774d99a5eSPaul Mundt low = sub3; 13874d99a5eSPaul Mundt high = 0LL; 13974d99a5eSPaul Mundt sub3 += (sub1 << 32); 14074d99a5eSPaul Mundt if (low > sub3) 14174d99a5eSPaul Mundt high++; 14274d99a5eSPaul Mundt low = sub3; 14374d99a5eSPaul Mundt sub3 += (sub2 << 32); 14474d99a5eSPaul Mundt if (low > sub3) 14574d99a5eSPaul Mundt high++; 14674d99a5eSPaul Mundt low = sub3; 14774d99a5eSPaul Mundt high += (sub1 >> 32) + (sub2 >> 32); 14874d99a5eSPaul Mundt high += sub0; 14974d99a5eSPaul Mundt *lowp = low; 15074d99a5eSPaul Mundt *highp = high; 15174d99a5eSPaul Mundt } 15274d99a5eSPaul Mundt 15374d99a5eSPaul Mundt static inline long long rshift64(unsigned long long mh, 15474d99a5eSPaul Mundt unsigned long long ml, int n) 15574d99a5eSPaul Mundt { 15674d99a5eSPaul Mundt if (n >= 64) 15774d99a5eSPaul Mundt return mh >> (n - 64); 15874d99a5eSPaul Mundt return (mh << (64 - n)) | (ml >> n); 15974d99a5eSPaul Mundt } 16074d99a5eSPaul Mundt 16174d99a5eSPaul Mundt static long long denormal_muld(long long hx, long long hy) 16274d99a5eSPaul Mundt { 16374d99a5eSPaul Mundt unsigned long long ix, iy; 16474d99a5eSPaul Mundt unsigned long long mh, ml, nh, nl; 16574d99a5eSPaul Mundt int exp, w; 16674d99a5eSPaul Mundt 16774d99a5eSPaul Mundt ix = hx & 0x7fffffffffffffffLL; 16874d99a5eSPaul Mundt iy = hy & 0x7fffffffffffffffLL; 16974d99a5eSPaul Mundt if (iy < 0x0010000000000000LL || ix == 0) 17074d99a5eSPaul Mundt return ((hx ^ hy) & 0x8000000000000000LL); 17174d99a5eSPaul Mundt 17274d99a5eSPaul Mundt exp = (iy & 0x7ff0000000000000LL) >> 52; 17374d99a5eSPaul Mundt ix &= 0x000fffffffffffffLL; 17474d99a5eSPaul Mundt iy = (iy & 0x000fffffffffffffLL) | 0x0010000000000000LL; 17574d99a5eSPaul Mundt mult64(ix, iy, &mh, &ml); 17674d99a5eSPaul Mundt nh = mh; 17774d99a5eSPaul Mundt nl = ml; 17874d99a5eSPaul Mundt w = -1; 17974d99a5eSPaul Mundt if (nh) { 18074d99a5eSPaul Mundt while (nh) { nh >>= 1; w++;} 18174d99a5eSPaul Mundt w += 64; 18274d99a5eSPaul Mundt } else 18374d99a5eSPaul Mundt while (nl) { nl >>= 1; w++;} 18474d99a5eSPaul Mundt 18574d99a5eSPaul Mundt /* FIXME: use guard bits */ 18674d99a5eSPaul Mundt exp += w - 1022 - 52 * 2; 18774d99a5eSPaul Mundt if (exp > 0) 18874d99a5eSPaul Mundt ix = (rshift64(mh, ml, w - 52) & 0x000fffffffffffffLL) 18974d99a5eSPaul Mundt | ((long long)exp << 52); 19074d99a5eSPaul Mundt else if (exp + 51 >= 0) 19174d99a5eSPaul Mundt ix = rshift64(mh, ml, w - 51 - exp) & 0x000fffffffffffffLL; 19274d99a5eSPaul Mundt else 19374d99a5eSPaul Mundt ix = 0; 19474d99a5eSPaul Mundt 19574d99a5eSPaul Mundt ix |= (hx ^ hy) & 0x8000000000000000LL; 19674d99a5eSPaul Mundt return ix; 19774d99a5eSPaul Mundt } 19874d99a5eSPaul Mundt 19974d99a5eSPaul Mundt /* ix - iy where iy: denormal and ix, iy >= 0 */ 20074d99a5eSPaul Mundt static int denormal_subf1(unsigned int ix, unsigned int iy) 20174d99a5eSPaul Mundt { 20274d99a5eSPaul Mundt int frac; 20374d99a5eSPaul Mundt int exp; 20474d99a5eSPaul Mundt 20574d99a5eSPaul Mundt if (ix < 0x00800000) 20674d99a5eSPaul Mundt return ix - iy; 20774d99a5eSPaul Mundt 20874d99a5eSPaul Mundt exp = (ix & 0x7f800000) >> 23; 20974d99a5eSPaul Mundt if (exp - 1 > 31) 21074d99a5eSPaul Mundt return ix; 21174d99a5eSPaul Mundt iy >>= exp - 1; 21274d99a5eSPaul Mundt if (iy == 0) 21374d99a5eSPaul Mundt return ix; 21474d99a5eSPaul Mundt 21574d99a5eSPaul Mundt frac = (ix & 0x007fffff) | 0x00800000; 21674d99a5eSPaul Mundt frac -= iy; 21774d99a5eSPaul Mundt while (frac < 0x00800000) { 21874d99a5eSPaul Mundt if (--exp == 0) 21974d99a5eSPaul Mundt return frac; 22074d99a5eSPaul Mundt frac <<= 1; 22174d99a5eSPaul Mundt } 22274d99a5eSPaul Mundt 22374d99a5eSPaul Mundt return (exp << 23) | (frac & 0x007fffff); 22474d99a5eSPaul Mundt } 22574d99a5eSPaul Mundt 22674d99a5eSPaul Mundt /* ix + iy where iy: denormal and ix, iy >= 0 */ 22774d99a5eSPaul Mundt static int denormal_addf1(unsigned int ix, unsigned int iy) 22874d99a5eSPaul Mundt { 22974d99a5eSPaul Mundt int frac; 23074d99a5eSPaul Mundt int exp; 23174d99a5eSPaul Mundt 23274d99a5eSPaul Mundt if (ix < 0x00800000) 23374d99a5eSPaul Mundt return ix + iy; 23474d99a5eSPaul Mundt 23574d99a5eSPaul Mundt exp = (ix & 0x7f800000) >> 23; 23674d99a5eSPaul Mundt if (exp - 1 > 31) 23774d99a5eSPaul Mundt return ix; 23874d99a5eSPaul Mundt iy >>= exp - 1; 23974d99a5eSPaul Mundt if (iy == 0) 24074d99a5eSPaul Mundt return ix; 24174d99a5eSPaul Mundt 24274d99a5eSPaul Mundt frac = (ix & 0x007fffff) | 0x00800000; 24374d99a5eSPaul Mundt frac += iy; 24474d99a5eSPaul Mundt if (frac >= 0x01000000) { 24574d99a5eSPaul Mundt frac >>= 1; 24674d99a5eSPaul Mundt ++exp; 24774d99a5eSPaul Mundt } 24874d99a5eSPaul Mundt 24974d99a5eSPaul Mundt return (exp << 23) | (frac & 0x007fffff); 25074d99a5eSPaul Mundt } 25174d99a5eSPaul Mundt 25274d99a5eSPaul Mundt static int denormal_addf(int hx, int hy) 25374d99a5eSPaul Mundt { 25474d99a5eSPaul Mundt unsigned int ix, iy; 25574d99a5eSPaul Mundt int sign; 25674d99a5eSPaul Mundt 25774d99a5eSPaul Mundt if ((hx ^ hy) & 0x80000000) { 25874d99a5eSPaul Mundt sign = hx & 0x80000000; 25974d99a5eSPaul Mundt ix = hx & 0x7fffffff; 26074d99a5eSPaul Mundt iy = hy & 0x7fffffff; 26174d99a5eSPaul Mundt if (iy < 0x00800000) { 26274d99a5eSPaul Mundt ix = denormal_subf1(ix, iy); 2639731e287SRoel Kluin if ((int) ix < 0) { 26474d99a5eSPaul Mundt ix = -ix; 26574d99a5eSPaul Mundt sign ^= 0x80000000; 26674d99a5eSPaul Mundt } 26774d99a5eSPaul Mundt } else { 26874d99a5eSPaul Mundt ix = denormal_subf1(iy, ix); 26974d99a5eSPaul Mundt sign ^= 0x80000000; 27074d99a5eSPaul Mundt } 27174d99a5eSPaul Mundt } else { 27274d99a5eSPaul Mundt sign = hx & 0x80000000; 27374d99a5eSPaul Mundt ix = hx & 0x7fffffff; 27474d99a5eSPaul Mundt iy = hy & 0x7fffffff; 27574d99a5eSPaul Mundt if (iy < 0x00800000) 27674d99a5eSPaul Mundt ix = denormal_addf1(ix, iy); 27774d99a5eSPaul Mundt else 27874d99a5eSPaul Mundt ix = denormal_addf1(iy, ix); 27974d99a5eSPaul Mundt } 28074d99a5eSPaul Mundt 28174d99a5eSPaul Mundt return sign | ix; 28274d99a5eSPaul Mundt } 28374d99a5eSPaul Mundt 28474d99a5eSPaul Mundt /* ix - iy where iy: denormal and ix, iy >= 0 */ 28574d99a5eSPaul Mundt static long long denormal_subd1(unsigned long long ix, unsigned long long iy) 28674d99a5eSPaul Mundt { 28774d99a5eSPaul Mundt long long frac; 28874d99a5eSPaul Mundt int exp; 28974d99a5eSPaul Mundt 29074d99a5eSPaul Mundt if (ix < 0x0010000000000000LL) 29174d99a5eSPaul Mundt return ix - iy; 29274d99a5eSPaul Mundt 29374d99a5eSPaul Mundt exp = (ix & 0x7ff0000000000000LL) >> 52; 29474d99a5eSPaul Mundt if (exp - 1 > 63) 29574d99a5eSPaul Mundt return ix; 29674d99a5eSPaul Mundt iy >>= exp - 1; 29774d99a5eSPaul Mundt if (iy == 0) 29874d99a5eSPaul Mundt return ix; 29974d99a5eSPaul Mundt 30074d99a5eSPaul Mundt frac = (ix & 0x000fffffffffffffLL) | 0x0010000000000000LL; 30174d99a5eSPaul Mundt frac -= iy; 30274d99a5eSPaul Mundt while (frac < 0x0010000000000000LL) { 30374d99a5eSPaul Mundt if (--exp == 0) 30474d99a5eSPaul Mundt return frac; 30574d99a5eSPaul Mundt frac <<= 1; 30674d99a5eSPaul Mundt } 30774d99a5eSPaul Mundt 30874d99a5eSPaul Mundt return ((long long)exp << 52) | (frac & 0x000fffffffffffffLL); 30974d99a5eSPaul Mundt } 31074d99a5eSPaul Mundt 31174d99a5eSPaul Mundt /* ix + iy where iy: denormal and ix, iy >= 0 */ 31274d99a5eSPaul Mundt static long long denormal_addd1(unsigned long long ix, unsigned long long iy) 31374d99a5eSPaul Mundt { 31474d99a5eSPaul Mundt long long frac; 31574d99a5eSPaul Mundt long long exp; 31674d99a5eSPaul Mundt 31774d99a5eSPaul Mundt if (ix < 0x0010000000000000LL) 31874d99a5eSPaul Mundt return ix + iy; 31974d99a5eSPaul Mundt 32074d99a5eSPaul Mundt exp = (ix & 0x7ff0000000000000LL) >> 52; 32174d99a5eSPaul Mundt if (exp - 1 > 63) 32274d99a5eSPaul Mundt return ix; 32374d99a5eSPaul Mundt iy >>= exp - 1; 32474d99a5eSPaul Mundt if (iy == 0) 32574d99a5eSPaul Mundt return ix; 32674d99a5eSPaul Mundt 32774d99a5eSPaul Mundt frac = (ix & 0x000fffffffffffffLL) | 0x0010000000000000LL; 32874d99a5eSPaul Mundt frac += iy; 32974d99a5eSPaul Mundt if (frac >= 0x0020000000000000LL) { 33074d99a5eSPaul Mundt frac >>= 1; 33174d99a5eSPaul Mundt ++exp; 33274d99a5eSPaul Mundt } 33374d99a5eSPaul Mundt 33474d99a5eSPaul Mundt return (exp << 52) | (frac & 0x000fffffffffffffLL); 33574d99a5eSPaul Mundt } 33674d99a5eSPaul Mundt 33774d99a5eSPaul Mundt static long long denormal_addd(long long hx, long long hy) 33874d99a5eSPaul Mundt { 33974d99a5eSPaul Mundt unsigned long long ix, iy; 34074d99a5eSPaul Mundt long long sign; 34174d99a5eSPaul Mundt 34274d99a5eSPaul Mundt if ((hx ^ hy) & 0x8000000000000000LL) { 34374d99a5eSPaul Mundt sign = hx & 0x8000000000000000LL; 34474d99a5eSPaul Mundt ix = hx & 0x7fffffffffffffffLL; 34574d99a5eSPaul Mundt iy = hy & 0x7fffffffffffffffLL; 34674d99a5eSPaul Mundt if (iy < 0x0010000000000000LL) { 34774d99a5eSPaul Mundt ix = denormal_subd1(ix, iy); 3489731e287SRoel Kluin if ((int) ix < 0) { 34974d99a5eSPaul Mundt ix = -ix; 35074d99a5eSPaul Mundt sign ^= 0x8000000000000000LL; 35174d99a5eSPaul Mundt } 35274d99a5eSPaul Mundt } else { 35374d99a5eSPaul Mundt ix = denormal_subd1(iy, ix); 35474d99a5eSPaul Mundt sign ^= 0x8000000000000000LL; 35574d99a5eSPaul Mundt } 35674d99a5eSPaul Mundt } else { 35774d99a5eSPaul Mundt sign = hx & 0x8000000000000000LL; 35874d99a5eSPaul Mundt ix = hx & 0x7fffffffffffffffLL; 35974d99a5eSPaul Mundt iy = hy & 0x7fffffffffffffffLL; 36074d99a5eSPaul Mundt if (iy < 0x0010000000000000LL) 36174d99a5eSPaul Mundt ix = denormal_addd1(ix, iy); 36274d99a5eSPaul Mundt else 36374d99a5eSPaul Mundt ix = denormal_addd1(iy, ix); 36474d99a5eSPaul Mundt } 36574d99a5eSPaul Mundt 36674d99a5eSPaul Mundt return sign | ix; 36774d99a5eSPaul Mundt } 36874d99a5eSPaul Mundt 36974d99a5eSPaul Mundt /** 37074d99a5eSPaul Mundt * denormal_to_double - Given denormalized float number, 37174d99a5eSPaul Mundt * store double float 37274d99a5eSPaul Mundt * 37374d99a5eSPaul Mundt * @fpu: Pointer to sh_fpu_hard structure 37474d99a5eSPaul Mundt * @n: Index to FP register 37574d99a5eSPaul Mundt */ 37674d99a5eSPaul Mundt static void 37774d99a5eSPaul Mundt denormal_to_double (struct sh_fpu_hard_struct *fpu, int n) 37874d99a5eSPaul Mundt { 37974d99a5eSPaul Mundt unsigned long du, dl; 38074d99a5eSPaul Mundt unsigned long x = fpu->fpul; 38174d99a5eSPaul Mundt int exp = 1023 - 126; 38274d99a5eSPaul Mundt 38374d99a5eSPaul Mundt if (x != 0 && (x & 0x7f800000) == 0) { 38474d99a5eSPaul Mundt du = (x & 0x80000000); 38574d99a5eSPaul Mundt while ((x & 0x00800000) == 0) { 38674d99a5eSPaul Mundt x <<= 1; 38774d99a5eSPaul Mundt exp--; 38874d99a5eSPaul Mundt } 38974d99a5eSPaul Mundt x &= 0x007fffff; 39074d99a5eSPaul Mundt du |= (exp << 20) | (x >> 3); 39174d99a5eSPaul Mundt dl = x << 29; 39274d99a5eSPaul Mundt 39374d99a5eSPaul Mundt fpu->fp_regs[n] = du; 39474d99a5eSPaul Mundt fpu->fp_regs[n+1] = dl; 39574d99a5eSPaul Mundt } 39674d99a5eSPaul Mundt } 39774d99a5eSPaul Mundt 39874d99a5eSPaul Mundt /** 39974d99a5eSPaul Mundt * ieee_fpe_handler - Handle denormalized number exception 40074d99a5eSPaul Mundt * 40174d99a5eSPaul Mundt * @regs: Pointer to register structure 40274d99a5eSPaul Mundt * 40374d99a5eSPaul Mundt * Returns 1 when it's handled (should not cause exception). 40474d99a5eSPaul Mundt */ 40574d99a5eSPaul Mundt static int 40674d99a5eSPaul Mundt ieee_fpe_handler (struct pt_regs *regs) 40774d99a5eSPaul Mundt { 40874d99a5eSPaul Mundt unsigned short insn = *(unsigned short *) regs->pc; 40974d99a5eSPaul Mundt unsigned short finsn; 41074d99a5eSPaul Mundt unsigned long nextpc; 41174d99a5eSPaul Mundt int nib[4] = { 41274d99a5eSPaul Mundt (insn >> 12) & 0xf, 41374d99a5eSPaul Mundt (insn >> 8) & 0xf, 41474d99a5eSPaul Mundt (insn >> 4) & 0xf, 41574d99a5eSPaul Mundt insn & 0xf}; 41674d99a5eSPaul Mundt 41774d99a5eSPaul Mundt if (nib[0] == 0xb || 41874d99a5eSPaul Mundt (nib[0] == 0x4 && nib[2] == 0x0 && nib[3] == 0xb)) /* bsr & jsr */ 41974d99a5eSPaul Mundt regs->pr = regs->pc + 4; 42074d99a5eSPaul Mundt if (nib[0] == 0xa || nib[0] == 0xb) { /* bra & bsr */ 42174d99a5eSPaul Mundt nextpc = regs->pc + 4 + ((short) ((insn & 0xfff) << 4) >> 3); 42274d99a5eSPaul Mundt finsn = *(unsigned short *) (regs->pc + 2); 42374d99a5eSPaul Mundt } else if (nib[0] == 0x8 && nib[1] == 0xd) { /* bt/s */ 42474d99a5eSPaul Mundt if (regs->sr & 1) 42574d99a5eSPaul Mundt nextpc = regs->pc + 4 + ((char) (insn & 0xff) << 1); 42674d99a5eSPaul Mundt else 42774d99a5eSPaul Mundt nextpc = regs->pc + 4; 42874d99a5eSPaul Mundt finsn = *(unsigned short *) (regs->pc + 2); 42974d99a5eSPaul Mundt } else if (nib[0] == 0x8 && nib[1] == 0xf) { /* bf/s */ 43074d99a5eSPaul Mundt if (regs->sr & 1) 43174d99a5eSPaul Mundt nextpc = regs->pc + 4; 43274d99a5eSPaul Mundt else 43374d99a5eSPaul Mundt nextpc = regs->pc + 4 + ((char) (insn & 0xff) << 1); 43474d99a5eSPaul Mundt finsn = *(unsigned short *) (regs->pc + 2); 43574d99a5eSPaul Mundt } else if (nib[0] == 0x4 && nib[3] == 0xb && 43674d99a5eSPaul Mundt (nib[2] == 0x0 || nib[2] == 0x2)) { /* jmp & jsr */ 43774d99a5eSPaul Mundt nextpc = regs->regs[nib[1]]; 43874d99a5eSPaul Mundt finsn = *(unsigned short *) (regs->pc + 2); 43974d99a5eSPaul Mundt } else if (nib[0] == 0x0 && nib[3] == 0x3 && 44074d99a5eSPaul Mundt (nib[2] == 0x0 || nib[2] == 0x2)) { /* braf & bsrf */ 44174d99a5eSPaul Mundt nextpc = regs->pc + 4 + regs->regs[nib[1]]; 44274d99a5eSPaul Mundt finsn = *(unsigned short *) (regs->pc + 2); 44374d99a5eSPaul Mundt } else if (insn == 0x000b) { /* rts */ 44474d99a5eSPaul Mundt nextpc = regs->pr; 44574d99a5eSPaul Mundt finsn = *(unsigned short *) (regs->pc + 2); 44674d99a5eSPaul Mundt } else { 44774d99a5eSPaul Mundt nextpc = regs->pc + 2; 44874d99a5eSPaul Mundt finsn = insn; 44974d99a5eSPaul Mundt } 45074d99a5eSPaul Mundt 45174d99a5eSPaul Mundt #define FPSCR_FPU_ERROR (1 << 17) 45274d99a5eSPaul Mundt 45374d99a5eSPaul Mundt if ((finsn & 0xf1ff) == 0xf0ad) { /* fcnvsd */ 45474d99a5eSPaul Mundt struct task_struct *tsk = current; 45574d99a5eSPaul Mundt 4560ea820cfSPaul Mundt if ((tsk->thread.xstate->hardfpu.fpscr & FPSCR_FPU_ERROR)) { 45774d99a5eSPaul Mundt /* FPU error */ 4580ea820cfSPaul Mundt denormal_to_double (&tsk->thread.xstate->hardfpu, 45974d99a5eSPaul Mundt (finsn >> 8) & 0xf); 46074d99a5eSPaul Mundt } else 46174d99a5eSPaul Mundt return 0; 46274d99a5eSPaul Mundt 46374d99a5eSPaul Mundt regs->pc = nextpc; 46474d99a5eSPaul Mundt return 1; 46574d99a5eSPaul Mundt } else if ((finsn & 0xf00f) == 0xf002) { /* fmul */ 46674d99a5eSPaul Mundt struct task_struct *tsk = current; 46774d99a5eSPaul Mundt int fpscr; 46874d99a5eSPaul Mundt int n, m, prec; 46974d99a5eSPaul Mundt unsigned int hx, hy; 47074d99a5eSPaul Mundt 47174d99a5eSPaul Mundt n = (finsn >> 8) & 0xf; 47274d99a5eSPaul Mundt m = (finsn >> 4) & 0xf; 4730ea820cfSPaul Mundt hx = tsk->thread.xstate->hardfpu.fp_regs[n]; 4740ea820cfSPaul Mundt hy = tsk->thread.xstate->hardfpu.fp_regs[m]; 4750ea820cfSPaul Mundt fpscr = tsk->thread.xstate->hardfpu.fpscr; 47674d99a5eSPaul Mundt prec = fpscr & (1 << 19); 47774d99a5eSPaul Mundt 47874d99a5eSPaul Mundt if ((fpscr & FPSCR_FPU_ERROR) 47974d99a5eSPaul Mundt && (prec && ((hx & 0x7fffffff) < 0x00100000 48074d99a5eSPaul Mundt || (hy & 0x7fffffff) < 0x00100000))) { 48174d99a5eSPaul Mundt long long llx, lly; 48274d99a5eSPaul Mundt 48374d99a5eSPaul Mundt /* FPU error because of denormal */ 48474d99a5eSPaul Mundt llx = ((long long) hx << 32) 4850ea820cfSPaul Mundt | tsk->thread.xstate->hardfpu.fp_regs[n+1]; 48674d99a5eSPaul Mundt lly = ((long long) hy << 32) 4870ea820cfSPaul Mundt | tsk->thread.xstate->hardfpu.fp_regs[m+1]; 48874d99a5eSPaul Mundt if ((hx & 0x7fffffff) >= 0x00100000) 48974d99a5eSPaul Mundt llx = denormal_muld(lly, llx); 49074d99a5eSPaul Mundt else 49174d99a5eSPaul Mundt llx = denormal_muld(llx, lly); 4920ea820cfSPaul Mundt tsk->thread.xstate->hardfpu.fp_regs[n] = llx >> 32; 4930ea820cfSPaul Mundt tsk->thread.xstate->hardfpu.fp_regs[n+1] = llx & 0xffffffff; 49474d99a5eSPaul Mundt } else if ((fpscr & FPSCR_FPU_ERROR) 49574d99a5eSPaul Mundt && (!prec && ((hx & 0x7fffffff) < 0x00800000 49674d99a5eSPaul Mundt || (hy & 0x7fffffff) < 0x00800000))) { 49774d99a5eSPaul Mundt /* FPU error because of denormal */ 49874d99a5eSPaul Mundt if ((hx & 0x7fffffff) >= 0x00800000) 49974d99a5eSPaul Mundt hx = denormal_mulf(hy, hx); 50074d99a5eSPaul Mundt else 50174d99a5eSPaul Mundt hx = denormal_mulf(hx, hy); 5020ea820cfSPaul Mundt tsk->thread.xstate->hardfpu.fp_regs[n] = hx; 50374d99a5eSPaul Mundt } else 50474d99a5eSPaul Mundt return 0; 50574d99a5eSPaul Mundt 50674d99a5eSPaul Mundt regs->pc = nextpc; 50774d99a5eSPaul Mundt return 1; 50874d99a5eSPaul Mundt } else if ((finsn & 0xf00e) == 0xf000) { /* fadd, fsub */ 50974d99a5eSPaul Mundt struct task_struct *tsk = current; 51074d99a5eSPaul Mundt int fpscr; 51174d99a5eSPaul Mundt int n, m, prec; 51274d99a5eSPaul Mundt unsigned int hx, hy; 51374d99a5eSPaul Mundt 51474d99a5eSPaul Mundt n = (finsn >> 8) & 0xf; 51574d99a5eSPaul Mundt m = (finsn >> 4) & 0xf; 5160ea820cfSPaul Mundt hx = tsk->thread.xstate->hardfpu.fp_regs[n]; 5170ea820cfSPaul Mundt hy = tsk->thread.xstate->hardfpu.fp_regs[m]; 5180ea820cfSPaul Mundt fpscr = tsk->thread.xstate->hardfpu.fpscr; 51974d99a5eSPaul Mundt prec = fpscr & (1 << 19); 52074d99a5eSPaul Mundt 52174d99a5eSPaul Mundt if ((fpscr & FPSCR_FPU_ERROR) 52274d99a5eSPaul Mundt && (prec && ((hx & 0x7fffffff) < 0x00100000 52374d99a5eSPaul Mundt || (hy & 0x7fffffff) < 0x00100000))) { 52474d99a5eSPaul Mundt long long llx, lly; 52574d99a5eSPaul Mundt 52674d99a5eSPaul Mundt /* FPU error because of denormal */ 52774d99a5eSPaul Mundt llx = ((long long) hx << 32) 5280ea820cfSPaul Mundt | tsk->thread.xstate->hardfpu.fp_regs[n+1]; 52974d99a5eSPaul Mundt lly = ((long long) hy << 32) 5300ea820cfSPaul Mundt | tsk->thread.xstate->hardfpu.fp_regs[m+1]; 53174d99a5eSPaul Mundt if ((finsn & 0xf00f) == 0xf000) 53274d99a5eSPaul Mundt llx = denormal_addd(llx, lly); 53374d99a5eSPaul Mundt else 53474d99a5eSPaul Mundt llx = denormal_addd(llx, lly ^ (1LL << 63)); 5350ea820cfSPaul Mundt tsk->thread.xstate->hardfpu.fp_regs[n] = llx >> 32; 5360ea820cfSPaul Mundt tsk->thread.xstate->hardfpu.fp_regs[n+1] = llx & 0xffffffff; 53774d99a5eSPaul Mundt } else if ((fpscr & FPSCR_FPU_ERROR) 53874d99a5eSPaul Mundt && (!prec && ((hx & 0x7fffffff) < 0x00800000 53974d99a5eSPaul Mundt || (hy & 0x7fffffff) < 0x00800000))) { 54074d99a5eSPaul Mundt /* FPU error because of denormal */ 54174d99a5eSPaul Mundt if ((finsn & 0xf00f) == 0xf000) 54274d99a5eSPaul Mundt hx = denormal_addf(hx, hy); 54374d99a5eSPaul Mundt else 54474d99a5eSPaul Mundt hx = denormal_addf(hx, hy ^ 0x80000000); 5450ea820cfSPaul Mundt tsk->thread.xstate->hardfpu.fp_regs[n] = hx; 54674d99a5eSPaul Mundt } else 54774d99a5eSPaul Mundt return 0; 54874d99a5eSPaul Mundt 54974d99a5eSPaul Mundt regs->pc = nextpc; 55074d99a5eSPaul Mundt return 1; 55174d99a5eSPaul Mundt } 55274d99a5eSPaul Mundt 55374d99a5eSPaul Mundt return 0; 55474d99a5eSPaul Mundt } 55574d99a5eSPaul Mundt 55674d99a5eSPaul Mundt BUILD_TRAP_HANDLER(fpu_error) 55774d99a5eSPaul Mundt { 55874d99a5eSPaul Mundt struct task_struct *tsk = current; 55974d99a5eSPaul Mundt TRAP_HANDLER_DECL; 56074d99a5eSPaul Mundt 561d3ea9fa0SStuart Menefy __unlazy_fpu(tsk, regs); 56274d99a5eSPaul Mundt if (ieee_fpe_handler(regs)) { 5630ea820cfSPaul Mundt tsk->thread.xstate->hardfpu.fpscr &= 56474d99a5eSPaul Mundt ~(FPSCR_CAUSE_MASK | FPSCR_FLAG_MASK); 56574d99a5eSPaul Mundt grab_fpu(regs); 56674d99a5eSPaul Mundt restore_fpu(tsk); 567d3ea9fa0SStuart Menefy task_thread_info(tsk)->status |= TS_USEDFPU; 56874d99a5eSPaul Mundt return; 56974d99a5eSPaul Mundt } 57074d99a5eSPaul Mundt 571*3cf5d076SEric W. Biederman force_sig(SIGFPE); 57274d99a5eSPaul Mundt } 573