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