GNU Linux-libre 4.14.266-gnu1
[releases.git] / arch / sh / kernel / cpu / sh4 / fpu.c
1 /*
2  * Save/restore floating point context for signal handlers.
3  *
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
6  * for more details.
7  *
8  * Copyright (C) 1999, 2000  Kaz Kojima & Niibe Yutaka
9  * Copyright (C) 2006  ST Microelectronics Ltd. (denorm support)
10  *
11  * FIXME! These routines have not been tested for big endian case.
12  */
13 #include <linux/sched/signal.h>
14 #include <linux/io.h>
15 #include <cpu/fpu.h>
16 #include <asm/processor.h>
17 #include <asm/fpu.h>
18 #include <asm/traps.h>
19
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.
23  */
24
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);
38 extern unsigned long int float64_to_float32(unsigned long long a);
39 static unsigned int fpu_exception_flags;
40
41 /*
42  * Save FPU registers onto task structure.
43  */
44 void save_fpu(struct task_struct *tsk)
45 {
46         unsigned long dummy;
47
48         enable_fpu();
49         asm volatile ("sts.l    fpul, @-%0\n\t"
50                       "sts.l    fpscr, @-%0\n\t"
51                       "lds      %2, fpscr\n\t"
52                       "frchg\n\t"
53                       "fmov.s   fr15, @-%0\n\t"
54                       "fmov.s   fr14, @-%0\n\t"
55                       "fmov.s   fr13, @-%0\n\t"
56                       "fmov.s   fr12, @-%0\n\t"
57                       "fmov.s   fr11, @-%0\n\t"
58                       "fmov.s   fr10, @-%0\n\t"
59                       "fmov.s   fr9, @-%0\n\t"
60                       "fmov.s   fr8, @-%0\n\t"
61                       "fmov.s   fr7, @-%0\n\t"
62                       "fmov.s   fr6, @-%0\n\t"
63                       "fmov.s   fr5, @-%0\n\t"
64                       "fmov.s   fr4, @-%0\n\t"
65                       "fmov.s   fr3, @-%0\n\t"
66                       "fmov.s   fr2, @-%0\n\t"
67                       "fmov.s   fr1, @-%0\n\t"
68                       "fmov.s   fr0, @-%0\n\t"
69                       "frchg\n\t"
70                       "fmov.s   fr15, @-%0\n\t"
71                       "fmov.s   fr14, @-%0\n\t"
72                       "fmov.s   fr13, @-%0\n\t"
73                       "fmov.s   fr12, @-%0\n\t"
74                       "fmov.s   fr11, @-%0\n\t"
75                       "fmov.s   fr10, @-%0\n\t"
76                       "fmov.s   fr9, @-%0\n\t"
77                       "fmov.s   fr8, @-%0\n\t"
78                       "fmov.s   fr7, @-%0\n\t"
79                       "fmov.s   fr6, @-%0\n\t"
80                       "fmov.s   fr5, @-%0\n\t"
81                       "fmov.s   fr4, @-%0\n\t"
82                       "fmov.s   fr3, @-%0\n\t"
83                       "fmov.s   fr2, @-%0\n\t"
84                       "fmov.s   fr1, @-%0\n\t"
85                       "fmov.s   fr0, @-%0\n\t"
86                       "lds      %3, fpscr\n\t":"=r" (dummy)
87                       :"0"((char *)(&tsk->thread.xstate->hardfpu.status)),
88                       "r"(FPSCR_RCHG), "r"(FPSCR_INIT)
89                       :"memory");
90
91         disable_fpu();
92 }
93
94 void restore_fpu(struct task_struct *tsk)
95 {
96         unsigned long dummy;
97
98         enable_fpu();
99         asm volatile ("lds      %2, fpscr\n\t"
100                       "fmov.s   @%0+, fr0\n\t"
101                       "fmov.s   @%0+, fr1\n\t"
102                       "fmov.s   @%0+, fr2\n\t"
103                       "fmov.s   @%0+, fr3\n\t"
104                       "fmov.s   @%0+, fr4\n\t"
105                       "fmov.s   @%0+, fr5\n\t"
106                       "fmov.s   @%0+, fr6\n\t"
107                       "fmov.s   @%0+, fr7\n\t"
108                       "fmov.s   @%0+, fr8\n\t"
109                       "fmov.s   @%0+, fr9\n\t"
110                       "fmov.s   @%0+, fr10\n\t"
111                       "fmov.s   @%0+, fr11\n\t"
112                       "fmov.s   @%0+, fr12\n\t"
113                       "fmov.s   @%0+, fr13\n\t"
114                       "fmov.s   @%0+, fr14\n\t"
115                       "fmov.s   @%0+, fr15\n\t"
116                       "frchg\n\t"
117                       "fmov.s   @%0+, fr0\n\t"
118                       "fmov.s   @%0+, fr1\n\t"
119                       "fmov.s   @%0+, fr2\n\t"
120                       "fmov.s   @%0+, fr3\n\t"
121                       "fmov.s   @%0+, fr4\n\t"
122                       "fmov.s   @%0+, fr5\n\t"
123                       "fmov.s   @%0+, fr6\n\t"
124                       "fmov.s   @%0+, fr7\n\t"
125                       "fmov.s   @%0+, fr8\n\t"
126                       "fmov.s   @%0+, fr9\n\t"
127                       "fmov.s   @%0+, fr10\n\t"
128                       "fmov.s   @%0+, fr11\n\t"
129                       "fmov.s   @%0+, fr12\n\t"
130                       "fmov.s   @%0+, fr13\n\t"
131                       "fmov.s   @%0+, fr14\n\t"
132                       "fmov.s   @%0+, fr15\n\t"
133                       "frchg\n\t"
134                       "lds.l    @%0+, fpscr\n\t"
135                       "lds.l    @%0+, fpul\n\t"
136                       :"=r" (dummy)
137                       :"0" (tsk->thread.xstate), "r" (FPSCR_RCHG)
138                       :"memory");
139         disable_fpu();
140 }
141
142 /**
143  *      denormal_to_double - Given denormalized float number,
144  *                           store double float
145  *
146  *      @fpu: Pointer to sh_fpu_hard structure
147  *      @n: Index to FP register
148  */
149 static void denormal_to_double(struct sh_fpu_hard_struct *fpu, int n)
150 {
151         unsigned long du, dl;
152         unsigned long x = fpu->fpul;
153         int exp = 1023 - 126;
154
155         if (x != 0 && (x & 0x7f800000) == 0) {
156                 du = (x & 0x80000000);
157                 while ((x & 0x00800000) == 0) {
158                         x <<= 1;
159                         exp--;
160                 }
161                 x &= 0x007fffff;
162                 du |= (exp << 20) | (x >> 3);
163                 dl = x << 29;
164
165                 fpu->fp_regs[n] = du;
166                 fpu->fp_regs[n + 1] = dl;
167         }
168 }
169
170 /**
171  *      ieee_fpe_handler - Handle denormalized number exception
172  *
173  *      @regs: Pointer to register structure
174  *
175  *      Returns 1 when it's handled (should not cause exception).
176  */
177 static int ieee_fpe_handler(struct pt_regs *regs)
178 {
179         unsigned short insn = *(unsigned short *)regs->pc;
180         unsigned short finsn;
181         unsigned long nextpc;
182         int nib[4] = {
183                 (insn >> 12) & 0xf,
184                 (insn >> 8) & 0xf,
185                 (insn >> 4) & 0xf,
186                 insn & 0xf
187         };
188
189         if (nib[0] == 0xb || (nib[0] == 0x4 && nib[2] == 0x0 && nib[3] == 0xb))
190                 regs->pr = regs->pc + 4;  /* bsr & jsr */
191
192         if (nib[0] == 0xa || nib[0] == 0xb) {
193                 /* bra & bsr */
194                 nextpc = regs->pc + 4 + ((short)((insn & 0xfff) << 4) >> 3);
195                 finsn = *(unsigned short *)(regs->pc + 2);
196         } else if (nib[0] == 0x8 && nib[1] == 0xd) {
197                 /* bt/s */
198                 if (regs->sr & 1)
199                         nextpc = regs->pc + 4 + ((char)(insn & 0xff) << 1);
200                 else
201                         nextpc = regs->pc + 4;
202                 finsn = *(unsigned short *)(regs->pc + 2);
203         } else if (nib[0] == 0x8 && nib[1] == 0xf) {
204                 /* bf/s */
205                 if (regs->sr & 1)
206                         nextpc = regs->pc + 4;
207                 else
208                         nextpc = regs->pc + 4 + ((char)(insn & 0xff) << 1);
209                 finsn = *(unsigned short *)(regs->pc + 2);
210         } else if (nib[0] == 0x4 && nib[3] == 0xb &&
211                    (nib[2] == 0x0 || nib[2] == 0x2)) {
212                 /* jmp & jsr */
213                 nextpc = regs->regs[nib[1]];
214                 finsn = *(unsigned short *)(regs->pc + 2);
215         } else if (nib[0] == 0x0 && nib[3] == 0x3 &&
216                    (nib[2] == 0x0 || nib[2] == 0x2)) {
217                 /* braf & bsrf */
218                 nextpc = regs->pc + 4 + regs->regs[nib[1]];
219                 finsn = *(unsigned short *)(regs->pc + 2);
220         } else if (insn == 0x000b) {
221                 /* rts */
222                 nextpc = regs->pr;
223                 finsn = *(unsigned short *)(regs->pc + 2);
224         } else {
225                 nextpc = regs->pc + instruction_size(insn);
226                 finsn = insn;
227         }
228
229         if ((finsn & 0xf1ff) == 0xf0ad) {
230                 /* fcnvsd */
231                 struct task_struct *tsk = current;
232
233                 if ((tsk->thread.xstate->hardfpu.fpscr & FPSCR_CAUSE_ERROR))
234                         /* FPU error */
235                         denormal_to_double(&tsk->thread.xstate->hardfpu,
236                                            (finsn >> 8) & 0xf);
237                 else
238                         return 0;
239
240                 regs->pc = nextpc;
241                 return 1;
242         } else if ((finsn & 0xf00f) == 0xf002) {
243                 /* fmul */
244                 struct task_struct *tsk = current;
245                 int fpscr;
246                 int n, m, prec;
247                 unsigned int hx, hy;
248
249                 n = (finsn >> 8) & 0xf;
250                 m = (finsn >> 4) & 0xf;
251                 hx = tsk->thread.xstate->hardfpu.fp_regs[n];
252                 hy = tsk->thread.xstate->hardfpu.fp_regs[m];
253                 fpscr = tsk->thread.xstate->hardfpu.fpscr;
254                 prec = fpscr & FPSCR_DBL_PRECISION;
255
256                 if ((fpscr & FPSCR_CAUSE_ERROR)
257                     && (prec && ((hx & 0x7fffffff) < 0x00100000
258                                  || (hy & 0x7fffffff) < 0x00100000))) {
259                         long long llx, lly;
260
261                         /* FPU error because of denormal (doubles) */
262                         llx = ((long long)hx << 32)
263                             | tsk->thread.xstate->hardfpu.fp_regs[n + 1];
264                         lly = ((long long)hy << 32)
265                             | tsk->thread.xstate->hardfpu.fp_regs[m + 1];
266                         llx = float64_mul(llx, lly);
267                         tsk->thread.xstate->hardfpu.fp_regs[n] = llx >> 32;
268                         tsk->thread.xstate->hardfpu.fp_regs[n + 1] = llx & 0xffffffff;
269                 } else if ((fpscr & FPSCR_CAUSE_ERROR)
270                            && (!prec && ((hx & 0x7fffffff) < 0x00800000
271                                          || (hy & 0x7fffffff) < 0x00800000))) {
272                         /* FPU error because of denormal (floats) */
273                         hx = float32_mul(hx, hy);
274                         tsk->thread.xstate->hardfpu.fp_regs[n] = hx;
275                 } else
276                         return 0;
277
278                 regs->pc = nextpc;
279                 return 1;
280         } else if ((finsn & 0xf00e) == 0xf000) {
281                 /* fadd, fsub */
282                 struct task_struct *tsk = current;
283                 int fpscr;
284                 int n, m, prec;
285                 unsigned int hx, hy;
286
287                 n = (finsn >> 8) & 0xf;
288                 m = (finsn >> 4) & 0xf;
289                 hx = tsk->thread.xstate->hardfpu.fp_regs[n];
290                 hy = tsk->thread.xstate->hardfpu.fp_regs[m];
291                 fpscr = tsk->thread.xstate->hardfpu.fpscr;
292                 prec = fpscr & FPSCR_DBL_PRECISION;
293
294                 if ((fpscr & FPSCR_CAUSE_ERROR)
295                     && (prec && ((hx & 0x7fffffff) < 0x00100000
296                                  || (hy & 0x7fffffff) < 0x00100000))) {
297                         long long llx, lly;
298
299                         /* FPU error because of denormal (doubles) */
300                         llx = ((long long)hx << 32)
301                             | tsk->thread.xstate->hardfpu.fp_regs[n + 1];
302                         lly = ((long long)hy << 32)
303                             | tsk->thread.xstate->hardfpu.fp_regs[m + 1];
304                         if ((finsn & 0xf00f) == 0xf000)
305                                 llx = float64_add(llx, lly);
306                         else
307                                 llx = float64_sub(llx, lly);
308                         tsk->thread.xstate->hardfpu.fp_regs[n] = llx >> 32;
309                         tsk->thread.xstate->hardfpu.fp_regs[n + 1] = llx & 0xffffffff;
310                 } else if ((fpscr & FPSCR_CAUSE_ERROR)
311                            && (!prec && ((hx & 0x7fffffff) < 0x00800000
312                                          || (hy & 0x7fffffff) < 0x00800000))) {
313                         /* FPU error because of denormal (floats) */
314                         if ((finsn & 0xf00f) == 0xf000)
315                                 hx = float32_add(hx, hy);
316                         else
317                                 hx = float32_sub(hx, hy);
318                         tsk->thread.xstate->hardfpu.fp_regs[n] = hx;
319                 } else
320                         return 0;
321
322                 regs->pc = nextpc;
323                 return 1;
324         } else if ((finsn & 0xf003) == 0xf003) {
325                 /* fdiv */
326                 struct task_struct *tsk = current;
327                 int fpscr;
328                 int n, m, prec;
329                 unsigned int hx, hy;
330
331                 n = (finsn >> 8) & 0xf;
332                 m = (finsn >> 4) & 0xf;
333                 hx = tsk->thread.xstate->hardfpu.fp_regs[n];
334                 hy = tsk->thread.xstate->hardfpu.fp_regs[m];
335                 fpscr = tsk->thread.xstate->hardfpu.fpscr;
336                 prec = fpscr & FPSCR_DBL_PRECISION;
337
338                 if ((fpscr & FPSCR_CAUSE_ERROR)
339                     && (prec && ((hx & 0x7fffffff) < 0x00100000
340                                  || (hy & 0x7fffffff) < 0x00100000))) {
341                         long long llx, lly;
342
343                         /* FPU error because of denormal (doubles) */
344                         llx = ((long long)hx << 32)
345                             | tsk->thread.xstate->hardfpu.fp_regs[n + 1];
346                         lly = ((long long)hy << 32)
347                             | tsk->thread.xstate->hardfpu.fp_regs[m + 1];
348
349                         llx = float64_div(llx, lly);
350
351                         tsk->thread.xstate->hardfpu.fp_regs[n] = llx >> 32;
352                         tsk->thread.xstate->hardfpu.fp_regs[n + 1] = llx & 0xffffffff;
353                 } else if ((fpscr & FPSCR_CAUSE_ERROR)
354                            && (!prec && ((hx & 0x7fffffff) < 0x00800000
355                                          || (hy & 0x7fffffff) < 0x00800000))) {
356                         /* FPU error because of denormal (floats) */
357                         hx = float32_div(hx, hy);
358                         tsk->thread.xstate->hardfpu.fp_regs[n] = hx;
359                 } else
360                         return 0;
361
362                 regs->pc = nextpc;
363                 return 1;
364         } else if ((finsn & 0xf0bd) == 0xf0bd) {
365                 /* fcnvds - double to single precision convert */
366                 struct task_struct *tsk = current;
367                 int m;
368                 unsigned int hx;
369
370                 m = (finsn >> 8) & 0x7;
371                 hx = tsk->thread.xstate->hardfpu.fp_regs[m];
372
373                 if ((tsk->thread.xstate->hardfpu.fpscr & FPSCR_CAUSE_ERROR)
374                         && ((hx & 0x7fffffff) < 0x00100000)) {
375                         /* subnormal double to float conversion */
376                         long long llx;
377
378                         llx = ((long long)tsk->thread.xstate->hardfpu.fp_regs[m] << 32)
379                             | tsk->thread.xstate->hardfpu.fp_regs[m + 1];
380
381                         tsk->thread.xstate->hardfpu.fpul = float64_to_float32(llx);
382                 } else
383                         return 0;
384
385                 regs->pc = nextpc;
386                 return 1;
387         }
388
389         return 0;
390 }
391
392 void float_raise(unsigned int flags)
393 {
394         fpu_exception_flags |= flags;
395 }
396
397 int float_rounding_mode(void)
398 {
399         struct task_struct *tsk = current;
400         int roundingMode = FPSCR_ROUNDING_MODE(tsk->thread.xstate->hardfpu.fpscr);
401         return roundingMode;
402 }
403
404 BUILD_TRAP_HANDLER(fpu_error)
405 {
406         struct task_struct *tsk = current;
407         TRAP_HANDLER_DECL;
408
409         __unlazy_fpu(tsk, regs);
410         fpu_exception_flags = 0;
411         if (ieee_fpe_handler(regs)) {
412                 tsk->thread.xstate->hardfpu.fpscr &=
413                     ~(FPSCR_CAUSE_MASK | FPSCR_FLAG_MASK);
414                 tsk->thread.xstate->hardfpu.fpscr |= fpu_exception_flags;
415                 /* Set the FPSCR flag as well as cause bits - simply
416                  * replicate the cause */
417                 tsk->thread.xstate->hardfpu.fpscr |= (fpu_exception_flags >> 10);
418                 grab_fpu(regs);
419                 restore_fpu(tsk);
420                 task_thread_info(tsk)->status |= TS_USEDFPU;
421                 if ((((tsk->thread.xstate->hardfpu.fpscr & FPSCR_ENABLE_MASK) >> 7) &
422                      (fpu_exception_flags >> 2)) == 0) {
423                         return;
424                 }
425         }
426
427         force_sig(SIGFPE, tsk);
428 }