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