1 // SPDX-License-Identifier: GPL-2.0
3 * Save/restore floating point context for signal handlers.
5 * Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka
6 * Copyright (C) 2006 ST Microelectronics Ltd. (denorm support)
8 * FIXME! These routines have not been tested for big endian case.
10 #include <linux/sched/signal.h>
13 #include <asm/processor.h>
15 #include <asm/traps.h>
17 /* The PR (precision) bit in the FP Status Register must be clear when
18 * an frchg instruction is executed, otherwise the instruction is undefined.
19 * Executing frchg with PR set causes a trap on some SH4 implementations.
22 #define FPSCR_RCHG 0x00000000
23 extern unsigned long long float64_div(unsigned long long a
,
24 unsigned long long b
);
25 extern unsigned long int float32_div(unsigned long int a
, unsigned long int b
);
26 extern unsigned long long float64_mul(unsigned long long a
,
27 unsigned long long b
);
28 extern unsigned long int float32_mul(unsigned long int a
, unsigned long int b
);
29 extern unsigned long long float64_add(unsigned long long a
,
30 unsigned long long b
);
31 extern unsigned long int float32_add(unsigned long int a
, unsigned long int b
);
32 extern unsigned long long float64_sub(unsigned long long a
,
33 unsigned long long b
);
34 extern unsigned long int float32_sub(unsigned long int a
, unsigned long int b
);
35 extern unsigned long int float64_to_float32(unsigned long long a
);
36 static unsigned int fpu_exception_flags
;
39 * Save FPU registers onto task structure.
41 void save_fpu(struct task_struct
*tsk
)
46 asm volatile ("sts.l fpul, @-%0\n\t"
47 "sts.l fpscr, @-%0\n\t"
50 "fmov.s fr15, @-%0\n\t"
51 "fmov.s fr14, @-%0\n\t"
52 "fmov.s fr13, @-%0\n\t"
53 "fmov.s fr12, @-%0\n\t"
54 "fmov.s fr11, @-%0\n\t"
55 "fmov.s fr10, @-%0\n\t"
56 "fmov.s fr9, @-%0\n\t"
57 "fmov.s fr8, @-%0\n\t"
58 "fmov.s fr7, @-%0\n\t"
59 "fmov.s fr6, @-%0\n\t"
60 "fmov.s fr5, @-%0\n\t"
61 "fmov.s fr4, @-%0\n\t"
62 "fmov.s fr3, @-%0\n\t"
63 "fmov.s fr2, @-%0\n\t"
64 "fmov.s fr1, @-%0\n\t"
65 "fmov.s fr0, @-%0\n\t"
67 "fmov.s fr15, @-%0\n\t"
68 "fmov.s fr14, @-%0\n\t"
69 "fmov.s fr13, @-%0\n\t"
70 "fmov.s fr12, @-%0\n\t"
71 "fmov.s fr11, @-%0\n\t"
72 "fmov.s fr10, @-%0\n\t"
73 "fmov.s fr9, @-%0\n\t"
74 "fmov.s fr8, @-%0\n\t"
75 "fmov.s fr7, @-%0\n\t"
76 "fmov.s fr6, @-%0\n\t"
77 "fmov.s fr5, @-%0\n\t"
78 "fmov.s fr4, @-%0\n\t"
79 "fmov.s fr3, @-%0\n\t"
80 "fmov.s fr2, @-%0\n\t"
81 "fmov.s fr1, @-%0\n\t"
82 "fmov.s fr0, @-%0\n\t"
83 "lds %3, fpscr\n\t":"=r" (dummy
)
84 :"0"((char *)(&tsk
->thread
.xstate
->hardfpu
.status
)),
85 "r"(FPSCR_RCHG
), "r"(FPSCR_INIT
)
91 void restore_fpu(struct task_struct
*tsk
)
96 asm volatile ("lds %2, fpscr\n\t"
97 "fmov.s @%0+, fr0\n\t"
98 "fmov.s @%0+, fr1\n\t"
99 "fmov.s @%0+, fr2\n\t"
100 "fmov.s @%0+, fr3\n\t"
101 "fmov.s @%0+, fr4\n\t"
102 "fmov.s @%0+, fr5\n\t"
103 "fmov.s @%0+, fr6\n\t"
104 "fmov.s @%0+, fr7\n\t"
105 "fmov.s @%0+, fr8\n\t"
106 "fmov.s @%0+, fr9\n\t"
107 "fmov.s @%0+, fr10\n\t"
108 "fmov.s @%0+, fr11\n\t"
109 "fmov.s @%0+, fr12\n\t"
110 "fmov.s @%0+, fr13\n\t"
111 "fmov.s @%0+, fr14\n\t"
112 "fmov.s @%0+, fr15\n\t"
114 "fmov.s @%0+, fr0\n\t"
115 "fmov.s @%0+, fr1\n\t"
116 "fmov.s @%0+, fr2\n\t"
117 "fmov.s @%0+, fr3\n\t"
118 "fmov.s @%0+, fr4\n\t"
119 "fmov.s @%0+, fr5\n\t"
120 "fmov.s @%0+, fr6\n\t"
121 "fmov.s @%0+, fr7\n\t"
122 "fmov.s @%0+, fr8\n\t"
123 "fmov.s @%0+, fr9\n\t"
124 "fmov.s @%0+, fr10\n\t"
125 "fmov.s @%0+, fr11\n\t"
126 "fmov.s @%0+, fr12\n\t"
127 "fmov.s @%0+, fr13\n\t"
128 "fmov.s @%0+, fr14\n\t"
129 "fmov.s @%0+, fr15\n\t"
131 "lds.l @%0+, fpscr\n\t"
132 "lds.l @%0+, fpul\n\t"
134 :"0" (tsk
->thread
.xstate
), "r" (FPSCR_RCHG
)
140 * denormal_to_double - Given denormalized float number,
143 * @fpu: Pointer to sh_fpu_hard structure
144 * @n: Index to FP register
146 static void denormal_to_double(struct sh_fpu_hard_struct
*fpu
, int n
)
148 unsigned long du
, dl
;
149 unsigned long x
= fpu
->fpul
;
150 int exp
= 1023 - 126;
152 if (x
!= 0 && (x
& 0x7f800000) == 0) {
153 du
= (x
& 0x80000000);
154 while ((x
& 0x00800000) == 0) {
159 du
|= (exp
<< 20) | (x
>> 3);
162 fpu
->fp_regs
[n
] = du
;
163 fpu
->fp_regs
[n
+ 1] = dl
;
168 * ieee_fpe_handler - Handle denormalized number exception
170 * @regs: Pointer to register structure
172 * Returns 1 when it's handled (should not cause exception).
174 static int ieee_fpe_handler(struct pt_regs
*regs
)
176 unsigned short insn
= *(unsigned short *)regs
->pc
;
177 unsigned short finsn
;
178 unsigned long nextpc
;
186 if (nib
[0] == 0xb || (nib
[0] == 0x4 && nib
[2] == 0x0 && nib
[3] == 0xb))
187 regs
->pr
= regs
->pc
+ 4; /* bsr & jsr */
189 if (nib
[0] == 0xa || nib
[0] == 0xb) {
191 nextpc
= regs
->pc
+ 4 + ((short)((insn
& 0xfff) << 4) >> 3);
192 finsn
= *(unsigned short *)(regs
->pc
+ 2);
193 } else if (nib
[0] == 0x8 && nib
[1] == 0xd) {
196 nextpc
= regs
->pc
+ 4 + ((char)(insn
& 0xff) << 1);
198 nextpc
= regs
->pc
+ 4;
199 finsn
= *(unsigned short *)(regs
->pc
+ 2);
200 } else if (nib
[0] == 0x8 && nib
[1] == 0xf) {
203 nextpc
= regs
->pc
+ 4;
205 nextpc
= regs
->pc
+ 4 + ((char)(insn
& 0xff) << 1);
206 finsn
= *(unsigned short *)(regs
->pc
+ 2);
207 } else if (nib
[0] == 0x4 && nib
[3] == 0xb &&
208 (nib
[2] == 0x0 || nib
[2] == 0x2)) {
210 nextpc
= regs
->regs
[nib
[1]];
211 finsn
= *(unsigned short *)(regs
->pc
+ 2);
212 } else if (nib
[0] == 0x0 && nib
[3] == 0x3 &&
213 (nib
[2] == 0x0 || nib
[2] == 0x2)) {
215 nextpc
= regs
->pc
+ 4 + regs
->regs
[nib
[1]];
216 finsn
= *(unsigned short *)(regs
->pc
+ 2);
217 } else if (insn
== 0x000b) {
220 finsn
= *(unsigned short *)(regs
->pc
+ 2);
222 nextpc
= regs
->pc
+ instruction_size(insn
);
226 if ((finsn
& 0xf1ff) == 0xf0ad) {
228 struct task_struct
*tsk
= current
;
230 if ((tsk
->thread
.xstate
->hardfpu
.fpscr
& FPSCR_CAUSE_ERROR
))
232 denormal_to_double(&tsk
->thread
.xstate
->hardfpu
,
239 } else if ((finsn
& 0xf00f) == 0xf002) {
241 struct task_struct
*tsk
= current
;
246 n
= (finsn
>> 8) & 0xf;
247 m
= (finsn
>> 4) & 0xf;
248 hx
= tsk
->thread
.xstate
->hardfpu
.fp_regs
[n
];
249 hy
= tsk
->thread
.xstate
->hardfpu
.fp_regs
[m
];
250 fpscr
= tsk
->thread
.xstate
->hardfpu
.fpscr
;
251 prec
= fpscr
& FPSCR_DBL_PRECISION
;
253 if ((fpscr
& FPSCR_CAUSE_ERROR
)
254 && (prec
&& ((hx
& 0x7fffffff) < 0x00100000
255 || (hy
& 0x7fffffff) < 0x00100000))) {
258 /* FPU error because of denormal (doubles) */
259 llx
= ((long long)hx
<< 32)
260 | tsk
->thread
.xstate
->hardfpu
.fp_regs
[n
+ 1];
261 lly
= ((long long)hy
<< 32)
262 | tsk
->thread
.xstate
->hardfpu
.fp_regs
[m
+ 1];
263 llx
= float64_mul(llx
, lly
);
264 tsk
->thread
.xstate
->hardfpu
.fp_regs
[n
] = llx
>> 32;
265 tsk
->thread
.xstate
->hardfpu
.fp_regs
[n
+ 1] = llx
& 0xffffffff;
266 } else if ((fpscr
& FPSCR_CAUSE_ERROR
)
267 && (!prec
&& ((hx
& 0x7fffffff) < 0x00800000
268 || (hy
& 0x7fffffff) < 0x00800000))) {
269 /* FPU error because of denormal (floats) */
270 hx
= float32_mul(hx
, hy
);
271 tsk
->thread
.xstate
->hardfpu
.fp_regs
[n
] = hx
;
277 } else if ((finsn
& 0xf00e) == 0xf000) {
279 struct task_struct
*tsk
= current
;
284 n
= (finsn
>> 8) & 0xf;
285 m
= (finsn
>> 4) & 0xf;
286 hx
= tsk
->thread
.xstate
->hardfpu
.fp_regs
[n
];
287 hy
= tsk
->thread
.xstate
->hardfpu
.fp_regs
[m
];
288 fpscr
= tsk
->thread
.xstate
->hardfpu
.fpscr
;
289 prec
= fpscr
& FPSCR_DBL_PRECISION
;
291 if ((fpscr
& FPSCR_CAUSE_ERROR
)
292 && (prec
&& ((hx
& 0x7fffffff) < 0x00100000
293 || (hy
& 0x7fffffff) < 0x00100000))) {
296 /* FPU error because of denormal (doubles) */
297 llx
= ((long long)hx
<< 32)
298 | tsk
->thread
.xstate
->hardfpu
.fp_regs
[n
+ 1];
299 lly
= ((long long)hy
<< 32)
300 | tsk
->thread
.xstate
->hardfpu
.fp_regs
[m
+ 1];
301 if ((finsn
& 0xf00f) == 0xf000)
302 llx
= float64_add(llx
, lly
);
304 llx
= float64_sub(llx
, lly
);
305 tsk
->thread
.xstate
->hardfpu
.fp_regs
[n
] = llx
>> 32;
306 tsk
->thread
.xstate
->hardfpu
.fp_regs
[n
+ 1] = llx
& 0xffffffff;
307 } else if ((fpscr
& FPSCR_CAUSE_ERROR
)
308 && (!prec
&& ((hx
& 0x7fffffff) < 0x00800000
309 || (hy
& 0x7fffffff) < 0x00800000))) {
310 /* FPU error because of denormal (floats) */
311 if ((finsn
& 0xf00f) == 0xf000)
312 hx
= float32_add(hx
, hy
);
314 hx
= float32_sub(hx
, hy
);
315 tsk
->thread
.xstate
->hardfpu
.fp_regs
[n
] = hx
;
321 } else if ((finsn
& 0xf003) == 0xf003) {
323 struct task_struct
*tsk
= current
;
328 n
= (finsn
>> 8) & 0xf;
329 m
= (finsn
>> 4) & 0xf;
330 hx
= tsk
->thread
.xstate
->hardfpu
.fp_regs
[n
];
331 hy
= tsk
->thread
.xstate
->hardfpu
.fp_regs
[m
];
332 fpscr
= tsk
->thread
.xstate
->hardfpu
.fpscr
;
333 prec
= fpscr
& FPSCR_DBL_PRECISION
;
335 if ((fpscr
& FPSCR_CAUSE_ERROR
)
336 && (prec
&& ((hx
& 0x7fffffff) < 0x00100000
337 || (hy
& 0x7fffffff) < 0x00100000))) {
340 /* FPU error because of denormal (doubles) */
341 llx
= ((long long)hx
<< 32)
342 | tsk
->thread
.xstate
->hardfpu
.fp_regs
[n
+ 1];
343 lly
= ((long long)hy
<< 32)
344 | tsk
->thread
.xstate
->hardfpu
.fp_regs
[m
+ 1];
346 llx
= float64_div(llx
, lly
);
348 tsk
->thread
.xstate
->hardfpu
.fp_regs
[n
] = llx
>> 32;
349 tsk
->thread
.xstate
->hardfpu
.fp_regs
[n
+ 1] = llx
& 0xffffffff;
350 } else if ((fpscr
& FPSCR_CAUSE_ERROR
)
351 && (!prec
&& ((hx
& 0x7fffffff) < 0x00800000
352 || (hy
& 0x7fffffff) < 0x00800000))) {
353 /* FPU error because of denormal (floats) */
354 hx
= float32_div(hx
, hy
);
355 tsk
->thread
.xstate
->hardfpu
.fp_regs
[n
] = hx
;
361 } else if ((finsn
& 0xf0bd) == 0xf0bd) {
362 /* fcnvds - double to single precision convert */
363 struct task_struct
*tsk
= current
;
367 m
= (finsn
>> 8) & 0x7;
368 hx
= tsk
->thread
.xstate
->hardfpu
.fp_regs
[m
];
370 if ((tsk
->thread
.xstate
->hardfpu
.fpscr
& FPSCR_CAUSE_ERROR
)
371 && ((hx
& 0x7fffffff) < 0x00100000)) {
372 /* subnormal double to float conversion */
375 llx
= ((long long)tsk
->thread
.xstate
->hardfpu
.fp_regs
[m
] << 32)
376 | tsk
->thread
.xstate
->hardfpu
.fp_regs
[m
+ 1];
378 tsk
->thread
.xstate
->hardfpu
.fpul
= float64_to_float32(llx
);
389 void float_raise(unsigned int flags
)
391 fpu_exception_flags
|= flags
;
394 int float_rounding_mode(void)
396 struct task_struct
*tsk
= current
;
397 int roundingMode
= FPSCR_ROUNDING_MODE(tsk
->thread
.xstate
->hardfpu
.fpscr
);
401 BUILD_TRAP_HANDLER(fpu_error
)
403 struct task_struct
*tsk
= current
;
406 __unlazy_fpu(tsk
, regs
);
407 fpu_exception_flags
= 0;
408 if (ieee_fpe_handler(regs
)) {
409 tsk
->thread
.xstate
->hardfpu
.fpscr
&=
410 ~(FPSCR_CAUSE_MASK
| FPSCR_FLAG_MASK
);
411 tsk
->thread
.xstate
->hardfpu
.fpscr
|= fpu_exception_flags
;
412 /* Set the FPSCR flag as well as cause bits - simply
413 * replicate the cause */
414 tsk
->thread
.xstate
->hardfpu
.fpscr
|= (fpu_exception_flags
>> 10);
417 task_thread_info(tsk
)->status
|= TS_USEDFPU
;
418 if ((((tsk
->thread
.xstate
->hardfpu
.fpscr
& FPSCR_ENABLE_MASK
) >> 7) &
419 (fpu_exception_flags
>> 2)) == 0) {