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 (&regs))
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(&regs)) {
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