Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/sh/kernel/cpu/sh4/fpu.c
26495 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Save/restore floating point context for signal handlers.
4
*
5
* Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka
6
* Copyright (C) 2006 ST Microelectronics Ltd. (denorm support)
7
*
8
* FIXME! These routines have not been tested for big endian case.
9
*/
10
#include <linux/sched/signal.h>
11
#include <linux/io.h>
12
#include <cpu/fpu.h>
13
#include <asm/processor.h>
14
#include <asm/fpu.h>
15
#include <asm/traps.h>
16
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.
20
*/
21
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;
37
38
/*
39
* Save FPU registers onto task structure.
40
*/
41
void save_fpu(struct task_struct *tsk)
42
{
43
unsigned long dummy;
44
45
enable_fpu();
46
asm volatile ("sts.l fpul, @-%0\n\t"
47
"sts.l fpscr, @-%0\n\t"
48
"lds %2, fpscr\n\t"
49
"frchg\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"
66
"frchg\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)
86
:"memory");
87
88
disable_fpu();
89
}
90
91
void restore_fpu(struct task_struct *tsk)
92
{
93
unsigned long dummy;
94
95
enable_fpu();
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"
113
"frchg\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"
130
"frchg\n\t"
131
"lds.l @%0+, fpscr\n\t"
132
"lds.l @%0+, fpul\n\t"
133
:"=r" (dummy)
134
:"0" (tsk->thread.xstate), "r" (FPSCR_RCHG)
135
:"memory");
136
disable_fpu();
137
}
138
139
/**
140
* denormal_to_double - Given denormalized float number,
141
* store double float
142
*
143
* @fpu: Pointer to sh_fpu_hard structure
144
* @n: Index to FP register
145
*/
146
static void denormal_to_double(struct sh_fpu_hard_struct *fpu, int n)
147
{
148
unsigned long du, dl;
149
unsigned long x = fpu->fpul;
150
int exp = 1023 - 126;
151
152
if (x != 0 && (x & 0x7f800000) == 0) {
153
du = (x & 0x80000000);
154
while ((x & 0x00800000) == 0) {
155
x <<= 1;
156
exp--;
157
}
158
x &= 0x007fffff;
159
du |= (exp << 20) | (x >> 3);
160
dl = x << 29;
161
162
fpu->fp_regs[n] = du;
163
fpu->fp_regs[n + 1] = dl;
164
}
165
}
166
167
/**
168
* ieee_fpe_handler - Handle denormalized number exception
169
*
170
* @regs: Pointer to register structure
171
*
172
* Returns 1 when it's handled (should not cause exception).
173
*/
174
static int ieee_fpe_handler(struct pt_regs *regs)
175
{
176
unsigned short insn = *(unsigned short *)regs->pc;
177
unsigned short finsn;
178
unsigned long nextpc;
179
int nib[4] = {
180
(insn >> 12) & 0xf,
181
(insn >> 8) & 0xf,
182
(insn >> 4) & 0xf,
183
insn & 0xf
184
};
185
186
if (nib[0] == 0xb || (nib[0] == 0x4 && nib[2] == 0x0 && nib[3] == 0xb))
187
regs->pr = regs->pc + 4; /* bsr & jsr */
188
189
if (nib[0] == 0xa || nib[0] == 0xb) {
190
/* bra & bsr */
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) {
194
/* bt/s */
195
if (regs->sr & 1)
196
nextpc = regs->pc + 4 + ((char)(insn & 0xff) << 1);
197
else
198
nextpc = regs->pc + 4;
199
finsn = *(unsigned short *)(regs->pc + 2);
200
} else if (nib[0] == 0x8 && nib[1] == 0xf) {
201
/* bf/s */
202
if (regs->sr & 1)
203
nextpc = regs->pc + 4;
204
else
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)) {
209
/* jmp & jsr */
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)) {
214
/* braf & bsrf */
215
nextpc = regs->pc + 4 + regs->regs[nib[1]];
216
finsn = *(unsigned short *)(regs->pc + 2);
217
} else if (insn == 0x000b) {
218
/* rts */
219
nextpc = regs->pr;
220
finsn = *(unsigned short *)(regs->pc + 2);
221
} else {
222
nextpc = regs->pc + instruction_size(insn);
223
finsn = insn;
224
}
225
226
if ((finsn & 0xf1ff) == 0xf0ad) {
227
/* fcnvsd */
228
struct task_struct *tsk = current;
229
230
if ((tsk->thread.xstate->hardfpu.fpscr & FPSCR_CAUSE_ERROR))
231
/* FPU error */
232
denormal_to_double(&tsk->thread.xstate->hardfpu,
233
(finsn >> 8) & 0xf);
234
else
235
return 0;
236
237
regs->pc = nextpc;
238
return 1;
239
} else if ((finsn & 0xf00f) == 0xf002) {
240
/* fmul */
241
struct task_struct *tsk = current;
242
int fpscr;
243
int n, m, prec;
244
unsigned int hx, hy;
245
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;
252
253
if ((fpscr & FPSCR_CAUSE_ERROR)
254
&& (prec && ((hx & 0x7fffffff) < 0x00100000
255
|| (hy & 0x7fffffff) < 0x00100000))) {
256
long long llx, lly;
257
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;
272
} else
273
return 0;
274
275
regs->pc = nextpc;
276
return 1;
277
} else if ((finsn & 0xf00e) == 0xf000) {
278
/* fadd, fsub */
279
struct task_struct *tsk = current;
280
int fpscr;
281
int n, m, prec;
282
unsigned int hx, hy;
283
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;
290
291
if ((fpscr & FPSCR_CAUSE_ERROR)
292
&& (prec && ((hx & 0x7fffffff) < 0x00100000
293
|| (hy & 0x7fffffff) < 0x00100000))) {
294
long long llx, lly;
295
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);
303
else
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);
313
else
314
hx = float32_sub(hx, hy);
315
tsk->thread.xstate->hardfpu.fp_regs[n] = hx;
316
} else
317
return 0;
318
319
regs->pc = nextpc;
320
return 1;
321
} else if ((finsn & 0xf003) == 0xf003) {
322
/* fdiv */
323
struct task_struct *tsk = current;
324
int fpscr;
325
int n, m, prec;
326
unsigned int hx, hy;
327
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;
334
335
if ((fpscr & FPSCR_CAUSE_ERROR)
336
&& (prec && ((hx & 0x7fffffff) < 0x00100000
337
|| (hy & 0x7fffffff) < 0x00100000))) {
338
long long llx, lly;
339
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];
345
346
llx = float64_div(llx, lly);
347
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;
356
} else
357
return 0;
358
359
regs->pc = nextpc;
360
return 1;
361
} else if ((finsn & 0xf0bd) == 0xf0bd) {
362
/* fcnvds - double to single precision convert */
363
struct task_struct *tsk = current;
364
int m;
365
unsigned int hx;
366
367
m = (finsn >> 8) & 0x7;
368
hx = tsk->thread.xstate->hardfpu.fp_regs[m];
369
370
if ((tsk->thread.xstate->hardfpu.fpscr & FPSCR_CAUSE_ERROR)
371
&& ((hx & 0x7fffffff) < 0x00100000)) {
372
/* subnormal double to float conversion */
373
long long llx;
374
375
llx = ((long long)tsk->thread.xstate->hardfpu.fp_regs[m] << 32)
376
| tsk->thread.xstate->hardfpu.fp_regs[m + 1];
377
378
tsk->thread.xstate->hardfpu.fpul = float64_to_float32(llx);
379
} else
380
return 0;
381
382
regs->pc = nextpc;
383
return 1;
384
}
385
386
return 0;
387
}
388
389
void float_raise(unsigned int flags)
390
{
391
fpu_exception_flags |= flags;
392
}
393
394
int float_rounding_mode(void)
395
{
396
struct task_struct *tsk = current;
397
int roundingMode = FPSCR_ROUNDING_MODE(tsk->thread.xstate->hardfpu.fpscr);
398
return roundingMode;
399
}
400
401
BUILD_TRAP_HANDLER(fpu_error)
402
{
403
struct task_struct *tsk = current;
404
TRAP_HANDLER_DECL;
405
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);
415
grab_fpu(regs);
416
restore_fpu(tsk);
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) {
420
return;
421
}
422
}
423
424
force_sig(SIGFPE);
425
}
426
427