1 /* $Id: fpu.c,v 1.4 2004/01/13 05:52:11 kkojima Exp $ 2 * 3 * linux/arch/sh/kernel/fpu.c 4 * 5 * Save/restore floating point context for signal handlers. 6 * 7 * This file is subject to the terms and conditions of the GNU General Public 8 * License. See the file "COPYING" in the main directory of this archive 9 * for more details. 10 * 11 * Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka 12 * 13 * FIXME! These routines can be optimized in big endian case. 14 */ 15 16 #include <linux/sched.h> 17 #include <linux/signal.h> 18 #include <asm/processor.h> 19 #include <asm/system.h> 20 #include <asm/io.h> 21 22 /* The PR (precision) bit in the FP Status Register must be clear when 23 * an frchg instruction is executed, otherwise the instruction is undefined. 24 * Executing frchg with PR set causes a trap on some SH4 implementations. 25 */ 26 27 #define FPSCR_RCHG 0x00000000 28 29 30 /* 31 * Save FPU registers onto task structure. 32 * Assume called with FPU enabled (SR.FD=0). 33 */ 34 void 35 save_fpu(struct task_struct *tsk, struct pt_regs *regs) 36 { 37 unsigned long dummy; 38 39 clear_tsk_thread_flag(tsk, TIF_USEDFPU); 40 enable_fpu(); 41 asm volatile("sts.l fpul, @-%0\n\t" 42 "sts.l fpscr, @-%0\n\t" 43 "lds %2, fpscr\n\t" 44 "frchg\n\t" 45 "fmov.s fr15, @-%0\n\t" 46 "fmov.s fr14, @-%0\n\t" 47 "fmov.s fr13, @-%0\n\t" 48 "fmov.s fr12, @-%0\n\t" 49 "fmov.s fr11, @-%0\n\t" 50 "fmov.s fr10, @-%0\n\t" 51 "fmov.s fr9, @-%0\n\t" 52 "fmov.s fr8, @-%0\n\t" 53 "fmov.s fr7, @-%0\n\t" 54 "fmov.s fr6, @-%0\n\t" 55 "fmov.s fr5, @-%0\n\t" 56 "fmov.s fr4, @-%0\n\t" 57 "fmov.s fr3, @-%0\n\t" 58 "fmov.s fr2, @-%0\n\t" 59 "fmov.s fr1, @-%0\n\t" 60 "fmov.s fr0, @-%0\n\t" 61 "frchg\n\t" 62 "fmov.s fr15, @-%0\n\t" 63 "fmov.s fr14, @-%0\n\t" 64 "fmov.s fr13, @-%0\n\t" 65 "fmov.s fr12, @-%0\n\t" 66 "fmov.s fr11, @-%0\n\t" 67 "fmov.s fr10, @-%0\n\t" 68 "fmov.s fr9, @-%0\n\t" 69 "fmov.s fr8, @-%0\n\t" 70 "fmov.s fr7, @-%0\n\t" 71 "fmov.s fr6, @-%0\n\t" 72 "fmov.s fr5, @-%0\n\t" 73 "fmov.s fr4, @-%0\n\t" 74 "fmov.s fr3, @-%0\n\t" 75 "fmov.s fr2, @-%0\n\t" 76 "fmov.s fr1, @-%0\n\t" 77 "fmov.s fr0, @-%0\n\t" 78 "lds %3, fpscr\n\t" 79 : "=r" (dummy) 80 : "0" ((char *)(&tsk->thread.fpu.hard.status)), 81 "r" (FPSCR_RCHG), 82 "r" (FPSCR_INIT) 83 : "memory"); 84 85 disable_fpu(); 86 release_fpu(regs); 87 } 88 89 static void 90 restore_fpu(struct task_struct *tsk) 91 { 92 unsigned long dummy; 93 94 enable_fpu(); 95 asm volatile("lds %2, fpscr\n\t" 96 "fmov.s @%0+, fr0\n\t" 97 "fmov.s @%0+, fr1\n\t" 98 "fmov.s @%0+, fr2\n\t" 99 "fmov.s @%0+, fr3\n\t" 100 "fmov.s @%0+, fr4\n\t" 101 "fmov.s @%0+, fr5\n\t" 102 "fmov.s @%0+, fr6\n\t" 103 "fmov.s @%0+, fr7\n\t" 104 "fmov.s @%0+, fr8\n\t" 105 "fmov.s @%0+, fr9\n\t" 106 "fmov.s @%0+, fr10\n\t" 107 "fmov.s @%0+, fr11\n\t" 108 "fmov.s @%0+, fr12\n\t" 109 "fmov.s @%0+, fr13\n\t" 110 "fmov.s @%0+, fr14\n\t" 111 "fmov.s @%0+, fr15\n\t" 112 "frchg\n\t" 113 "fmov.s @%0+, fr0\n\t" 114 "fmov.s @%0+, fr1\n\t" 115 "fmov.s @%0+, fr2\n\t" 116 "fmov.s @%0+, fr3\n\t" 117 "fmov.s @%0+, fr4\n\t" 118 "fmov.s @%0+, fr5\n\t" 119 "fmov.s @%0+, fr6\n\t" 120 "fmov.s @%0+, fr7\n\t" 121 "fmov.s @%0+, fr8\n\t" 122 "fmov.s @%0+, fr9\n\t" 123 "fmov.s @%0+, fr10\n\t" 124 "fmov.s @%0+, fr11\n\t" 125 "fmov.s @%0+, fr12\n\t" 126 "fmov.s @%0+, fr13\n\t" 127 "fmov.s @%0+, fr14\n\t" 128 "fmov.s @%0+, fr15\n\t" 129 "frchg\n\t" 130 "lds.l @%0+, fpscr\n\t" 131 "lds.l @%0+, fpul\n\t" 132 : "=r" (dummy) 133 : "0" (&tsk->thread.fpu), "r" (FPSCR_RCHG) 134 : "memory"); 135 disable_fpu(); 136 } 137 138 /* 139 * Load the FPU with signalling NANS. This bit pattern we're using 140 * has the property that no matter wether considered as single or as 141 * double precision represents signaling NANS. 142 */ 143 144 static void 145 fpu_init(void) 146 { 147 enable_fpu(); 148 asm volatile("lds %0, fpul\n\t" 149 "lds %1, fpscr\n\t" 150 "fsts fpul, fr0\n\t" 151 "fsts fpul, fr1\n\t" 152 "fsts fpul, fr2\n\t" 153 "fsts fpul, fr3\n\t" 154 "fsts fpul, fr4\n\t" 155 "fsts fpul, fr5\n\t" 156 "fsts fpul, fr6\n\t" 157 "fsts fpul, fr7\n\t" 158 "fsts fpul, fr8\n\t" 159 "fsts fpul, fr9\n\t" 160 "fsts fpul, fr10\n\t" 161 "fsts fpul, fr11\n\t" 162 "fsts fpul, fr12\n\t" 163 "fsts fpul, fr13\n\t" 164 "fsts fpul, fr14\n\t" 165 "fsts fpul, fr15\n\t" 166 "frchg\n\t" 167 "fsts fpul, fr0\n\t" 168 "fsts fpul, fr1\n\t" 169 "fsts fpul, fr2\n\t" 170 "fsts fpul, fr3\n\t" 171 "fsts fpul, fr4\n\t" 172 "fsts fpul, fr5\n\t" 173 "fsts fpul, fr6\n\t" 174 "fsts fpul, fr7\n\t" 175 "fsts fpul, fr8\n\t" 176 "fsts fpul, fr9\n\t" 177 "fsts fpul, fr10\n\t" 178 "fsts fpul, fr11\n\t" 179 "fsts fpul, fr12\n\t" 180 "fsts fpul, fr13\n\t" 181 "fsts fpul, fr14\n\t" 182 "fsts fpul, fr15\n\t" 183 "frchg\n\t" 184 "lds %2, fpscr\n\t" 185 : /* no output */ 186 : "r" (0), "r" (FPSCR_RCHG), "r" (FPSCR_INIT)); 187 disable_fpu(); 188 } 189 190 /** 191 * denormal_to_double - Given denormalized float number, 192 * store double float 193 * 194 * @fpu: Pointer to sh_fpu_hard structure 195 * @n: Index to FP register 196 */ 197 static void 198 denormal_to_double (struct sh_fpu_hard_struct *fpu, int n) 199 { 200 unsigned long du, dl; 201 unsigned long x = fpu->fpul; 202 int exp = 1023 - 126; 203 204 if (x != 0 && (x & 0x7f800000) == 0) { 205 du = (x & 0x80000000); 206 while ((x & 0x00800000) == 0) { 207 x <<= 1; 208 exp--; 209 } 210 x &= 0x007fffff; 211 du |= (exp << 20) | (x >> 3); 212 dl = x << 29; 213 214 fpu->fp_regs[n] = du; 215 fpu->fp_regs[n+1] = dl; 216 } 217 } 218 219 /** 220 * ieee_fpe_handler - Handle denormalized number exception 221 * 222 * @regs: Pointer to register structure 223 * 224 * Returns 1 when it's handled (should not cause exception). 225 */ 226 static int 227 ieee_fpe_handler (struct pt_regs *regs) 228 { 229 unsigned short insn = *(unsigned short *) regs->pc; 230 unsigned short finsn; 231 unsigned long nextpc; 232 int nib[4] = { 233 (insn >> 12) & 0xf, 234 (insn >> 8) & 0xf, 235 (insn >> 4) & 0xf, 236 insn & 0xf}; 237 238 if (nib[0] == 0xb || 239 (nib[0] == 0x4 && nib[2] == 0x0 && nib[3] == 0xb)) /* bsr & jsr */ 240 regs->pr = regs->pc + 4; 241 242 if (nib[0] == 0xa || nib[0] == 0xb) { /* bra & bsr */ 243 nextpc = regs->pc + 4 + ((short) ((insn & 0xfff) << 4) >> 3); 244 finsn = *(unsigned short *) (regs->pc + 2); 245 } else if (nib[0] == 0x8 && nib[1] == 0xd) { /* bt/s */ 246 if (regs->sr & 1) 247 nextpc = regs->pc + 4 + ((char) (insn & 0xff) << 1); 248 else 249 nextpc = regs->pc + 4; 250 finsn = *(unsigned short *) (regs->pc + 2); 251 } else if (nib[0] == 0x8 && nib[1] == 0xf) { /* bf/s */ 252 if (regs->sr & 1) 253 nextpc = regs->pc + 4; 254 else 255 nextpc = regs->pc + 4 + ((char) (insn & 0xff) << 1); 256 finsn = *(unsigned short *) (regs->pc + 2); 257 } else if (nib[0] == 0x4 && nib[3] == 0xb && 258 (nib[2] == 0x0 || nib[2] == 0x2)) { /* jmp & jsr */ 259 nextpc = regs->regs[nib[1]]; 260 finsn = *(unsigned short *) (regs->pc + 2); 261 } else if (nib[0] == 0x0 && nib[3] == 0x3 && 262 (nib[2] == 0x0 || nib[2] == 0x2)) { /* braf & bsrf */ 263 nextpc = regs->pc + 4 + regs->regs[nib[1]]; 264 finsn = *(unsigned short *) (regs->pc + 2); 265 } else if (insn == 0x000b) { /* rts */ 266 nextpc = regs->pr; 267 finsn = *(unsigned short *) (regs->pc + 2); 268 } else { 269 nextpc = regs->pc + instruction_size(insn); 270 finsn = insn; 271 } 272 273 if ((finsn & 0xf1ff) == 0xf0ad) { /* fcnvsd */ 274 struct task_struct *tsk = current; 275 276 save_fpu(tsk, regs); 277 if ((tsk->thread.fpu.hard.fpscr & (1 << 17))) { 278 /* FPU error */ 279 denormal_to_double (&tsk->thread.fpu.hard, 280 (finsn >> 8) & 0xf); 281 tsk->thread.fpu.hard.fpscr &= 282 ~(FPSCR_CAUSE_MASK | FPSCR_FLAG_MASK); 283 grab_fpu(regs); 284 restore_fpu(tsk); 285 set_tsk_thread_flag(tsk, TIF_USEDFPU); 286 } else 287 force_sig(SIGFPE, tsk); 288 289 regs->pc = nextpc; 290 return 1; 291 } 292 293 return 0; 294 } 295 296 asmlinkage void 297 do_fpu_error(unsigned long r4, unsigned long r5, unsigned long r6, 298 unsigned long r7, struct pt_regs __regs) 299 { 300 struct pt_regs *regs = RELOC_HIDE(&__regs, 0); 301 struct task_struct *tsk = current; 302 303 if (ieee_fpe_handler(regs)) 304 return; 305 306 regs->pc += 2; 307 save_fpu(tsk, regs); 308 force_sig(SIGFPE, tsk); 309 } 310 311 asmlinkage void 312 do_fpu_state_restore(unsigned long r4, unsigned long r5, unsigned long r6, 313 unsigned long r7, struct pt_regs __regs) 314 { 315 struct pt_regs *regs = RELOC_HIDE(&__regs, 0); 316 struct task_struct *tsk = current; 317 318 grab_fpu(regs); 319 if (!user_mode(regs)) { 320 printk(KERN_ERR "BUG: FPU is used in kernel mode.\n"); 321 return; 322 } 323 324 if (used_math()) { 325 /* Using the FPU again. */ 326 restore_fpu(tsk); 327 } else { 328 /* First time FPU user. */ 329 fpu_init(); 330 set_used_math(); 331 } 332 set_tsk_thread_flag(tsk, TIF_USEDFPU); 333 } 334