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