2 * Save/restore floating point context for signal handlers.
4 * This file is subject to the terms and conditions of the GNU General Public
5 * License. See the file "COPYING" in the main directory of this archive
8 * Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka
9 * Copyright (C) 2006 ST Microelectronics Ltd. (denorm support)
11 * FIXME! These routines have not been tested for big endian case.
13 #include <linux/sched.h>
14 #include <linux/signal.h>
16 #include <asm/cpu/fpu.h>
17 #include <asm/processor.h>
18 #include <asm/system.h>
20 /* The PR (precision) bit in the FP Status Register must be clear when
21 * an frchg instruction is executed, otherwise the instruction is undefined.
22 * Executing frchg with PR set causes a trap on some SH4 implementations.
25 #define FPSCR_RCHG 0x00000000
26 extern unsigned long long float64_div(unsigned long long a,
27 unsigned long long b);
28 extern unsigned long int float32_div(unsigned long int a, unsigned long int b);
29 extern unsigned long long float64_mul(unsigned long long a,
30 unsigned long long b);
31 extern unsigned long int float32_mul(unsigned long int a, unsigned long int b);
32 extern unsigned long long float64_add(unsigned long long a,
33 unsigned long long b);
34 extern unsigned long int float32_add(unsigned long int a, unsigned long int b);
35 extern unsigned long long float64_sub(unsigned long long a,
36 unsigned long long b);
37 extern unsigned long int float32_sub(unsigned long int a, unsigned long int b);
39 static unsigned int fpu_exception_flags;
42 * Save FPU registers onto task structure.
43 * Assume called with FPU enabled (SR.FD=0).
45 void save_fpu(struct task_struct *tsk, struct pt_regs *regs)
49 clear_tsk_thread_flag(tsk, TIF_USEDFPU);
51 asm volatile ("sts.l fpul, @-%0\n\t"
52 "sts.l fpscr, @-%0\n\t"
55 "fmov.s fr15, @-%0\n\t"
56 "fmov.s fr14, @-%0\n\t"
57 "fmov.s fr13, @-%0\n\t"
58 "fmov.s fr12, @-%0\n\t"
59 "fmov.s fr11, @-%0\n\t"
60 "fmov.s fr10, @-%0\n\t"
61 "fmov.s fr9, @-%0\n\t"
62 "fmov.s fr8, @-%0\n\t"
63 "fmov.s fr7, @-%0\n\t"
64 "fmov.s fr6, @-%0\n\t"
65 "fmov.s fr5, @-%0\n\t"
66 "fmov.s fr4, @-%0\n\t"
67 "fmov.s fr3, @-%0\n\t"
68 "fmov.s fr2, @-%0\n\t"
69 "fmov.s fr1, @-%0\n\t"
70 "fmov.s fr0, @-%0\n\t"
72 "fmov.s fr15, @-%0\n\t"
73 "fmov.s fr14, @-%0\n\t"
74 "fmov.s fr13, @-%0\n\t"
75 "fmov.s fr12, @-%0\n\t"
76 "fmov.s fr11, @-%0\n\t"
77 "fmov.s fr10, @-%0\n\t"
78 "fmov.s fr9, @-%0\n\t"
79 "fmov.s fr8, @-%0\n\t"
80 "fmov.s fr7, @-%0\n\t"
81 "fmov.s fr6, @-%0\n\t"
82 "fmov.s fr5, @-%0\n\t"
83 "fmov.s fr4, @-%0\n\t"
84 "fmov.s fr3, @-%0\n\t"
85 "fmov.s fr2, @-%0\n\t"
86 "fmov.s fr1, @-%0\n\t"
87 "fmov.s fr0, @-%0\n\t"
88 "lds %3, fpscr\n\t":"=r" (dummy)
89 :"0"((char *)(&tsk->thread.fpu.hard.status)),
90 "r"(FPSCR_RCHG), "r"(FPSCR_INIT)
97 static void restore_fpu(struct task_struct *tsk)
102 asm volatile ("lds %2, fpscr\n\t"
103 "fmov.s @%0+, fr0\n\t"
104 "fmov.s @%0+, fr1\n\t"
105 "fmov.s @%0+, fr2\n\t"
106 "fmov.s @%0+, fr3\n\t"
107 "fmov.s @%0+, fr4\n\t"
108 "fmov.s @%0+, fr5\n\t"
109 "fmov.s @%0+, fr6\n\t"
110 "fmov.s @%0+, fr7\n\t"
111 "fmov.s @%0+, fr8\n\t"
112 "fmov.s @%0+, fr9\n\t"
113 "fmov.s @%0+, fr10\n\t"
114 "fmov.s @%0+, fr11\n\t"
115 "fmov.s @%0+, fr12\n\t"
116 "fmov.s @%0+, fr13\n\t"
117 "fmov.s @%0+, fr14\n\t"
118 "fmov.s @%0+, fr15\n\t"
120 "fmov.s @%0+, fr0\n\t"
121 "fmov.s @%0+, fr1\n\t"
122 "fmov.s @%0+, fr2\n\t"
123 "fmov.s @%0+, fr3\n\t"
124 "fmov.s @%0+, fr4\n\t"
125 "fmov.s @%0+, fr5\n\t"
126 "fmov.s @%0+, fr6\n\t"
127 "fmov.s @%0+, fr7\n\t"
128 "fmov.s @%0+, fr8\n\t"
129 "fmov.s @%0+, fr9\n\t"
130 "fmov.s @%0+, fr10\n\t"
131 "fmov.s @%0+, fr11\n\t"
132 "fmov.s @%0+, fr12\n\t"
133 "fmov.s @%0+, fr13\n\t"
134 "fmov.s @%0+, fr14\n\t"
135 "fmov.s @%0+, fr15\n\t"
137 "lds.l @%0+, fpscr\n\t"
138 "lds.l @%0+, fpul\n\t"
140 :"0"(&tsk->thread.fpu), "r"(FPSCR_RCHG)
146 * Load the FPU with signalling NANS. This bit pattern we're using
147 * has the property that no matter wether considered as single or as
148 * double precision represents signaling NANS.
151 static void fpu_init(void)
154 asm volatile ( "lds %0, fpul\n\t"
166 "fsts fpul, fr10\n\t"
167 "fsts fpul, fr11\n\t"
168 "fsts fpul, fr12\n\t"
169 "fsts fpul, fr13\n\t"
170 "fsts fpul, fr14\n\t"
171 "fsts fpul, fr15\n\t"
183 "fsts fpul, fr10\n\t"
184 "fsts fpul, fr11\n\t"
185 "fsts fpul, fr12\n\t"
186 "fsts fpul, fr13\n\t"
187 "fsts fpul, fr14\n\t"
188 "fsts fpul, fr15\n\t"
192 :"r" (0), "r"(FPSCR_RCHG), "r"(FPSCR_INIT));
197 * denormal_to_double - Given denormalized float number,
200 * @fpu: Pointer to sh_fpu_hard structure
201 * @n: Index to FP register
203 static void denormal_to_double(struct sh_fpu_hard_struct *fpu, int n)
205 unsigned long du, dl;
206 unsigned long x = fpu->fpul;
207 int exp = 1023 - 126;
209 if (x != 0 && (x & 0x7f800000) == 0) {
210 du = (x & 0x80000000);
211 while ((x & 0x00800000) == 0) {
216 du |= (exp << 20) | (x >> 3);
219 fpu->fp_regs[n] = du;
220 fpu->fp_regs[n + 1] = dl;
225 * ieee_fpe_handler - Handle denormalized number exception
227 * @regs: Pointer to register structure
229 * Returns 1 when it's handled (should not cause exception).
231 static int ieee_fpe_handler(struct pt_regs *regs)
233 unsigned short insn = *(unsigned short *)regs->pc;
234 unsigned short finsn;
235 unsigned long nextpc;
243 if (nib[0] == 0xb || (nib[0] == 0x4 && nib[2] == 0x0 && nib[3] == 0xb))
244 regs->pr = regs->pc + 4; /* bsr & jsr */
246 if (nib[0] == 0xa || nib[0] == 0xb) {
248 nextpc = regs->pc + 4 + ((short)((insn & 0xfff) << 4) >> 3);
249 finsn = *(unsigned short *)(regs->pc + 2);
250 } else if (nib[0] == 0x8 && nib[1] == 0xd) {
253 nextpc = regs->pc + 4 + ((char)(insn & 0xff) << 1);
255 nextpc = regs->pc + 4;
256 finsn = *(unsigned short *)(regs->pc + 2);
257 } else if (nib[0] == 0x8 && nib[1] == 0xf) {
260 nextpc = regs->pc + 4;
262 nextpc = regs->pc + 4 + ((char)(insn & 0xff) << 1);
263 finsn = *(unsigned short *)(regs->pc + 2);
264 } else if (nib[0] == 0x4 && nib[3] == 0xb &&
265 (nib[2] == 0x0 || nib[2] == 0x2)) {
267 nextpc = regs->regs[nib[1]];
268 finsn = *(unsigned short *)(regs->pc + 2);
269 } else if (nib[0] == 0x0 && nib[3] == 0x3 &&
270 (nib[2] == 0x0 || nib[2] == 0x2)) {
272 nextpc = regs->pc + 4 + regs->regs[nib[1]];
273 finsn = *(unsigned short *)(regs->pc + 2);
274 } else if (insn == 0x000b) {
277 finsn = *(unsigned short *)(regs->pc + 2);
279 nextpc = regs->pc + instruction_size(insn);
283 if ((finsn & 0xf1ff) == 0xf0ad) {
285 struct task_struct *tsk = current;
288 if ((tsk->thread.fpu.hard.fpscr & FPSCR_CAUSE_ERROR))
290 denormal_to_double(&tsk->thread.fpu.hard,
297 } else if ((finsn & 0xf00f) == 0xf002) {
299 struct task_struct *tsk = current;
304 n = (finsn >> 8) & 0xf;
305 m = (finsn >> 4) & 0xf;
306 hx = tsk->thread.fpu.hard.fp_regs[n];
307 hy = tsk->thread.fpu.hard.fp_regs[m];
308 fpscr = tsk->thread.fpu.hard.fpscr;
309 prec = fpscr & FPSCR_DBL_PRECISION;
311 if ((fpscr & FPSCR_CAUSE_ERROR)
312 && (prec && ((hx & 0x7fffffff) < 0x00100000
313 || (hy & 0x7fffffff) < 0x00100000))) {
316 /* FPU error because of denormal (doubles) */
317 llx = ((long long)hx << 32)
318 | tsk->thread.fpu.hard.fp_regs[n + 1];
319 lly = ((long long)hy << 32)
320 | tsk->thread.fpu.hard.fp_regs[m + 1];
321 llx = float64_mul(llx, lly);
322 tsk->thread.fpu.hard.fp_regs[n] = llx >> 32;
323 tsk->thread.fpu.hard.fp_regs[n + 1] = llx & 0xffffffff;
324 } else if ((fpscr & FPSCR_CAUSE_ERROR)
325 && (!prec && ((hx & 0x7fffffff) < 0x00800000
326 || (hy & 0x7fffffff) < 0x00800000))) {
327 /* FPU error because of denormal (floats) */
328 hx = float32_mul(hx, hy);
329 tsk->thread.fpu.hard.fp_regs[n] = hx;
335 } else if ((finsn & 0xf00e) == 0xf000) {
337 struct task_struct *tsk = current;
342 n = (finsn >> 8) & 0xf;
343 m = (finsn >> 4) & 0xf;
344 hx = tsk->thread.fpu.hard.fp_regs[n];
345 hy = tsk->thread.fpu.hard.fp_regs[m];
346 fpscr = tsk->thread.fpu.hard.fpscr;
347 prec = fpscr & FPSCR_DBL_PRECISION;
349 if ((fpscr & FPSCR_CAUSE_ERROR)
350 && (prec && ((hx & 0x7fffffff) < 0x00100000
351 || (hy & 0x7fffffff) < 0x00100000))) {
354 /* FPU error because of denormal (doubles) */
355 llx = ((long long)hx << 32)
356 | tsk->thread.fpu.hard.fp_regs[n + 1];
357 lly = ((long long)hy << 32)
358 | tsk->thread.fpu.hard.fp_regs[m + 1];
359 if ((finsn & 0xf00f) == 0xf000)
360 llx = float64_add(llx, lly);
362 llx = float64_sub(llx, lly);
363 tsk->thread.fpu.hard.fp_regs[n] = llx >> 32;
364 tsk->thread.fpu.hard.fp_regs[n + 1] = llx & 0xffffffff;
365 } else if ((fpscr & FPSCR_CAUSE_ERROR)
366 && (!prec && ((hx & 0x7fffffff) < 0x00800000
367 || (hy & 0x7fffffff) < 0x00800000))) {
368 /* FPU error because of denormal (floats) */
369 if ((finsn & 0xf00f) == 0xf000)
370 hx = float32_add(hx, hy);
372 hx = float32_sub(hx, hy);
373 tsk->thread.fpu.hard.fp_regs[n] = hx;
379 } else if ((finsn & 0xf003) == 0xf003) {
381 struct task_struct *tsk = current;
386 n = (finsn >> 8) & 0xf;
387 m = (finsn >> 4) & 0xf;
388 hx = tsk->thread.fpu.hard.fp_regs[n];
389 hy = tsk->thread.fpu.hard.fp_regs[m];
390 fpscr = tsk->thread.fpu.hard.fpscr;
391 prec = fpscr & FPSCR_DBL_PRECISION;
393 if ((fpscr & FPSCR_CAUSE_ERROR)
394 && (prec && ((hx & 0x7fffffff) < 0x00100000
395 || (hy & 0x7fffffff) < 0x00100000))) {
398 /* FPU error because of denormal (doubles) */
399 llx = ((long long)hx << 32)
400 | tsk->thread.fpu.hard.fp_regs[n + 1];
401 lly = ((long long)hy << 32)
402 | tsk->thread.fpu.hard.fp_regs[m + 1];
404 llx = float64_div(llx, lly);
406 tsk->thread.fpu.hard.fp_regs[n] = llx >> 32;
407 tsk->thread.fpu.hard.fp_regs[n + 1] = llx & 0xffffffff;
408 } else if ((fpscr & FPSCR_CAUSE_ERROR)
409 && (!prec && ((hx & 0x7fffffff) < 0x00800000
410 || (hy & 0x7fffffff) < 0x00800000))) {
411 /* FPU error because of denormal (floats) */
412 hx = float32_div(hx, hy);
413 tsk->thread.fpu.hard.fp_regs[n] = hx;
424 void float_raise(unsigned int flags)
426 fpu_exception_flags |= flags;
429 int float_rounding_mode(void)
431 struct task_struct *tsk = current;
432 int roundingMode = FPSCR_ROUNDING_MODE(tsk->thread.fpu.hard.fpscr);
436 BUILD_TRAP_HANDLER(fpu_error)
438 struct task_struct *tsk = current;
442 fpu_exception_flags = 0;
443 if (ieee_fpe_handler(regs)) {
444 tsk->thread.fpu.hard.fpscr &=
445 ~(FPSCR_CAUSE_MASK | FPSCR_FLAG_MASK);
446 tsk->thread.fpu.hard.fpscr |= fpu_exception_flags;
447 /* Set the FPSCR flag as well as cause bits - simply
448 * replicate the cause */
449 tsk->thread.fpu.hard.fpscr |= (fpu_exception_flags >> 10);
452 set_tsk_thread_flag(tsk, TIF_USEDFPU);
453 if ((((tsk->thread.fpu.hard.fpscr & FPSCR_ENABLE_MASK) >> 7) &
454 (fpu_exception_flags >> 2)) == 0) {
459 force_sig(SIGFPE, tsk);
462 BUILD_TRAP_HANDLER(fpu_state_restore)
464 struct task_struct *tsk = current;
468 if (!user_mode(regs)) {
469 printk(KERN_ERR "BUG: FPU is used in kernel mode.\n");
474 /* Using the FPU again. */
477 /* First time FPU user. */
481 set_tsk_thread_flag(tsk, TIF_USEDFPU);