PageRenderTime 44ms CodeModel.GetById 11ms app.highlight 28ms RepoModel.GetById 1ms app.codeStats 0ms

/arch/sparc64/math-emu/math.c

https://bitbucket.org/evzijst/gittest
C | 493 lines | 408 code | 21 blank | 64 comment | 101 complexity | 31bbe96bfc7fd99b3d4c5742ff5ce292 MD5 | raw file
  1/* $Id: math.c,v 1.11 1999/12/20 05:02:25 davem Exp $
  2 * arch/sparc64/math-emu/math.c
  3 *
  4 * Copyright (C) 1997,1999 Jakub Jelinek (jj@ultra.linux.cz)
  5 * Copyright (C) 1999 David S. Miller (davem@redhat.com)
  6 *
  7 * Emulation routines originate from soft-fp package, which is part
  8 * of glibc and has appropriate copyrights in it.
  9 */
 10
 11#include <linux/types.h>
 12#include <linux/sched.h>
 13#include <linux/errno.h>
 14
 15#include <asm/fpumacro.h>
 16#include <asm/ptrace.h>
 17#include <asm/uaccess.h>
 18
 19#include "sfp-util.h"
 20#include <math-emu/soft-fp.h>
 21#include <math-emu/single.h>
 22#include <math-emu/double.h>
 23#include <math-emu/quad.h>
 24
 25/* QUAD - ftt == 3 */
 26#define FMOVQ	0x003
 27#define FNEGQ	0x007
 28#define FABSQ	0x00b
 29#define FSQRTQ	0x02b
 30#define FADDQ	0x043
 31#define FSUBQ	0x047
 32#define FMULQ	0x04b
 33#define FDIVQ	0x04f
 34#define FDMULQ	0x06e
 35#define FQTOX	0x083
 36#define FXTOQ	0x08c
 37#define FQTOS	0x0c7
 38#define FQTOD	0x0cb
 39#define FITOQ	0x0cc
 40#define FSTOQ	0x0cd
 41#define FDTOQ	0x0ce
 42#define FQTOI	0x0d3
 43/* SUBNORMAL - ftt == 2 */
 44#define FSQRTS	0x029
 45#define FSQRTD	0x02a
 46#define FADDS	0x041
 47#define FADDD	0x042
 48#define FSUBS	0x045
 49#define FSUBD	0x046
 50#define FMULS	0x049
 51#define FMULD	0x04a
 52#define FDIVS	0x04d
 53#define FDIVD	0x04e
 54#define FSMULD	0x069
 55#define FSTOX	0x081
 56#define FDTOX	0x082
 57#define FDTOS	0x0c6
 58#define FSTOD	0x0c9
 59#define FSTOI	0x0d1
 60#define FDTOI	0x0d2
 61#define FXTOS	0x084 /* Only Ultra-III generates this. */
 62#define FXTOD	0x088 /* Only Ultra-III generates this. */
 63#if 0	/* Optimized inline in sparc64/kernel/entry.S */
 64#define FITOS	0x0c4 /* Only Ultra-III generates this. */
 65#endif
 66#define FITOD	0x0c8 /* Only Ultra-III generates this. */
 67/* FPOP2 */
 68#define FCMPQ	0x053
 69#define FCMPEQ	0x057
 70#define FMOVQ0	0x003
 71#define FMOVQ1	0x043
 72#define FMOVQ2	0x083
 73#define FMOVQ3	0x0c3
 74#define FMOVQI	0x103
 75#define FMOVQX	0x183
 76#define FMOVQZ	0x027
 77#define FMOVQLE	0x047
 78#define FMOVQLZ 0x067
 79#define FMOVQNZ	0x0a7
 80#define FMOVQGZ	0x0c7
 81#define FMOVQGE 0x0e7
 82
 83#define FSR_TEM_SHIFT	23UL
 84#define FSR_TEM_MASK	(0x1fUL << FSR_TEM_SHIFT)
 85#define FSR_AEXC_SHIFT	5UL
 86#define FSR_AEXC_MASK	(0x1fUL << FSR_AEXC_SHIFT)
 87#define FSR_CEXC_SHIFT	0UL
 88#define FSR_CEXC_MASK	(0x1fUL << FSR_CEXC_SHIFT)
 89
 90/* All routines returning an exception to raise should detect
 91 * such exceptions _before_ rounding to be consistent with
 92 * the behavior of the hardware in the implemented cases
 93 * (and thus with the recommendations in the V9 architecture
 94 * manual).
 95 *
 96 * We return 0 if a SIGFPE should be sent, 1 otherwise.
 97 */
 98static inline int record_exception(struct pt_regs *regs, int eflag)
 99{
100	u64 fsr = current_thread_info()->xfsr[0];
101	int would_trap;
102
103	/* Determine if this exception would have generated a trap. */
104	would_trap = (fsr & ((long)eflag << FSR_TEM_SHIFT)) != 0UL;
105
106	/* If trapping, we only want to signal one bit. */
107	if(would_trap != 0) {
108		eflag &= ((fsr & FSR_TEM_MASK) >> FSR_TEM_SHIFT);
109		if((eflag & (eflag - 1)) != 0) {
110			if(eflag & FP_EX_INVALID)
111				eflag = FP_EX_INVALID;
112			else if(eflag & FP_EX_OVERFLOW)
113				eflag = FP_EX_OVERFLOW;
114			else if(eflag & FP_EX_UNDERFLOW)
115				eflag = FP_EX_UNDERFLOW;
116			else if(eflag & FP_EX_DIVZERO)
117				eflag = FP_EX_DIVZERO;
118			else if(eflag & FP_EX_INEXACT)
119				eflag = FP_EX_INEXACT;
120		}
121	}
122
123	/* Set CEXC, here is the rule:
124	 *
125	 *    In general all FPU ops will set one and only one
126	 *    bit in the CEXC field, this is always the case
127	 *    when the IEEE exception trap is enabled in TEM.
128	 */
129	fsr &= ~(FSR_CEXC_MASK);
130	fsr |= ((long)eflag << FSR_CEXC_SHIFT);
131
132	/* Set the AEXC field, rule is:
133	 *
134	 *    If a trap would not be generated, the
135	 *    CEXC just generated is OR'd into the
136	 *    existing value of AEXC.
137	 */
138	if(would_trap == 0)
139		fsr |= ((long)eflag << FSR_AEXC_SHIFT);
140
141	/* If trapping, indicate fault trap type IEEE. */
142	if(would_trap != 0)
143		fsr |= (1UL << 14);
144
145	current_thread_info()->xfsr[0] = fsr;
146
147	/* If we will not trap, advance the program counter over
148	 * the instruction being handled.
149	 */
150	if(would_trap == 0) {
151		regs->tpc = regs->tnpc;
152		regs->tnpc += 4;
153	}
154
155	return (would_trap ? 0 : 1);
156}
157
158typedef union {
159	u32 s;
160	u64 d;
161	u64 q[2];
162} *argp;
163
164int do_mathemu(struct pt_regs *regs, struct fpustate *f)
165{
166	unsigned long pc = regs->tpc;
167	unsigned long tstate = regs->tstate;
168	u32 insn = 0;
169	int type = 0;
170	/* ftt tells which ftt it may happen in, r is rd, b is rs2 and a is rs1. The *u arg tells
171	   whether the argument should be packed/unpacked (0 - do not unpack/pack, 1 - unpack/pack)
172	   non-u args tells the size of the argument (0 - no argument, 1 - single, 2 - double, 3 - quad */
173#define TYPE(ftt, r, ru, b, bu, a, au) type = (au << 2) | (a << 0) | (bu << 5) | (b << 3) | (ru << 8) | (r << 6) | (ftt << 9)
174	int freg;
175	static u64 zero[2] = { 0L, 0L };
176	int flags;
177	FP_DECL_EX;
178	FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
179	FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
180	FP_DECL_Q(QA); FP_DECL_Q(QB); FP_DECL_Q(QR);
181	int IR;
182	long XR, xfsr;
183
184	if (tstate & TSTATE_PRIV)
185		die_if_kernel("unfinished/unimplemented FPop from kernel", regs);
186	if (test_thread_flag(TIF_32BIT))
187		pc = (u32)pc;
188	if (get_user(insn, (u32 __user *) pc) != -EFAULT) {
189		if ((insn & 0xc1f80000) == 0x81a00000) /* FPOP1 */ {
190			switch ((insn >> 5) & 0x1ff) {
191			/* QUAD - ftt == 3 */
192			case FMOVQ:
193			case FNEGQ:
194			case FABSQ: TYPE(3,3,0,3,0,0,0); break;
195			case FSQRTQ: TYPE(3,3,1,3,1,0,0); break;
196			case FADDQ:
197			case FSUBQ:
198			case FMULQ:
199			case FDIVQ: TYPE(3,3,1,3,1,3,1); break;
200			case FDMULQ: TYPE(3,3,1,2,1,2,1); break;
201			case FQTOX: TYPE(3,2,0,3,1,0,0); break;
202			case FXTOQ: TYPE(3,3,1,2,0,0,0); break;
203			case FQTOS: TYPE(3,1,1,3,1,0,0); break;
204			case FQTOD: TYPE(3,2,1,3,1,0,0); break;
205			case FITOQ: TYPE(3,3,1,1,0,0,0); break;
206			case FSTOQ: TYPE(3,3,1,1,1,0,0); break;
207			case FDTOQ: TYPE(3,3,1,2,1,0,0); break;
208			case FQTOI: TYPE(3,1,0,3,1,0,0); break;
209			/* SUBNORMAL - ftt == 2 */
210			case FSQRTS: TYPE(2,1,1,1,1,0,0); break;
211			case FSQRTD: TYPE(2,2,1,2,1,0,0); break;
212			case FADDD:
213			case FSUBD:
214			case FMULD:
215			case FDIVD: TYPE(2,2,1,2,1,2,1); break;
216			case FADDS:
217			case FSUBS:
218			case FMULS:
219			case FDIVS: TYPE(2,1,1,1,1,1,1); break;
220			case FSMULD: TYPE(2,2,1,1,1,1,1); break;
221			case FSTOX: TYPE(2,2,0,1,1,0,0); break;
222			case FDTOX: TYPE(2,2,0,2,1,0,0); break;
223			case FDTOS: TYPE(2,1,1,2,1,0,0); break;
224			case FSTOD: TYPE(2,2,1,1,1,0,0); break;
225			case FSTOI: TYPE(2,1,0,1,1,0,0); break;
226			case FDTOI: TYPE(2,1,0,2,1,0,0); break;
227
228			/* Only Ultra-III generates these */
229			case FXTOS: TYPE(2,1,1,2,0,0,0); break;
230			case FXTOD: TYPE(2,2,1,2,0,0,0); break;
231#if 0			/* Optimized inline in sparc64/kernel/entry.S */
232			case FITOS: TYPE(2,1,1,1,0,0,0); break;
233#endif
234			case FITOD: TYPE(2,2,1,1,0,0,0); break;
235			}
236		}
237		else if ((insn & 0xc1f80000) == 0x81a80000) /* FPOP2 */ {
238			IR = 2;
239			switch ((insn >> 5) & 0x1ff) {
240			case FCMPQ: TYPE(3,0,0,3,1,3,1); break;
241			case FCMPEQ: TYPE(3,0,0,3,1,3,1); break;
242			/* Now the conditional fmovq support */
243			case FMOVQ0:
244			case FMOVQ1:
245			case FMOVQ2:
246			case FMOVQ3:
247				/* fmovq %fccX, %fY, %fZ */
248				if (!((insn >> 11) & 3))
249					XR = current_thread_info()->xfsr[0] >> 10;
250				else
251					XR = current_thread_info()->xfsr[0] >> (30 + ((insn >> 10) & 0x6));
252				XR &= 3;
253				IR = 0;
254				switch ((insn >> 14) & 0x7) {
255				/* case 0: IR = 0; break; */			/* Never */
256				case 1: if (XR) IR = 1; break;			/* Not Equal */
257				case 2: if (XR == 1 || XR == 2) IR = 1; break;	/* Less or Greater */
258				case 3: if (XR & 1) IR = 1; break;		/* Unordered or Less */
259				case 4: if (XR == 1) IR = 1; break;		/* Less */
260				case 5: if (XR & 2) IR = 1; break;		/* Unordered or Greater */
261				case 6: if (XR == 2) IR = 1; break;		/* Greater */
262				case 7: if (XR == 3) IR = 1; break;		/* Unordered */
263				}
264				if ((insn >> 14) & 8)
265					IR ^= 1;
266				break;
267			case FMOVQI:
268			case FMOVQX:
269				/* fmovq %[ix]cc, %fY, %fZ */
270				XR = regs->tstate >> 32;
271				if ((insn >> 5) & 0x80)
272					XR >>= 4;
273				XR &= 0xf;
274				IR = 0;
275				freg = ((XR >> 2) ^ XR) & 2;
276				switch ((insn >> 14) & 0x7) {
277				/* case 0: IR = 0; break; */			/* Never */
278				case 1: if (XR & 4) IR = 1; break;		/* Equal */
279				case 2: if ((XR & 4) || freg) IR = 1; break;	/* Less or Equal */
280				case 3: if (freg) IR = 1; break;		/* Less */
281				case 4: if (XR & 5) IR = 1; break;		/* Less or Equal Unsigned */
282				case 5: if (XR & 1) IR = 1; break;		/* Carry Set */
283				case 6: if (XR & 8) IR = 1; break;		/* Negative */
284				case 7: if (XR & 2) IR = 1; break;		/* Overflow Set */
285				}
286				if ((insn >> 14) & 8)
287					IR ^= 1;
288				break;
289			case FMOVQZ:
290			case FMOVQLE:
291			case FMOVQLZ:
292			case FMOVQNZ:
293			case FMOVQGZ:
294			case FMOVQGE:
295				freg = (insn >> 14) & 0x1f;
296				if (!freg)
297					XR = 0;
298				else if (freg < 16)
299					XR = regs->u_regs[freg];
300				else if (test_thread_flag(TIF_32BIT)) {
301					struct reg_window32 __user *win32;
302					flushw_user ();
303					win32 = (struct reg_window32 __user *)((unsigned long)((u32)regs->u_regs[UREG_FP]));
304					get_user(XR, &win32->locals[freg - 16]);
305				} else {
306					struct reg_window __user *win;
307					flushw_user ();
308					win = (struct reg_window __user *)(regs->u_regs[UREG_FP] + STACK_BIAS);
309					get_user(XR, &win->locals[freg - 16]);
310				}
311				IR = 0;
312				switch ((insn >> 10) & 3) {
313				case 1: if (!XR) IR = 1; break;			/* Register Zero */
314				case 2: if (XR <= 0) IR = 1; break;		/* Register Less Than or Equal to Zero */
315				case 3: if (XR < 0) IR = 1; break;		/* Register Less Than Zero */
316				}
317				if ((insn >> 10) & 4)
318					IR ^= 1;
319				break;
320			}
321			if (IR == 0) {
322				/* The fmov test was false. Do a nop instead */
323				current_thread_info()->xfsr[0] &= ~(FSR_CEXC_MASK);
324				regs->tpc = regs->tnpc;
325				regs->tnpc += 4;
326				return 1;
327			} else if (IR == 1) {
328				/* Change the instruction into plain fmovq */
329				insn = (insn & 0x3e00001f) | 0x81a00060;
330				TYPE(3,3,0,3,0,0,0); 
331			}
332		}
333	}
334	if (type) {
335		argp rs1 = NULL, rs2 = NULL, rd = NULL;
336		
337		freg = (current_thread_info()->xfsr[0] >> 14) & 0xf;
338		if (freg != (type >> 9))
339			goto err;
340		current_thread_info()->xfsr[0] &= ~0x1c000;
341		freg = ((insn >> 14) & 0x1f);
342		switch (type & 0x3) {
343		case 3: if (freg & 2) {
344				current_thread_info()->xfsr[0] |= (6 << 14) /* invalid_fp_register */;
345				goto err;
346			}
347		case 2: freg = ((freg & 1) << 5) | (freg & 0x1e);
348		case 1: rs1 = (argp)&f->regs[freg];
349			flags = (freg < 32) ? FPRS_DL : FPRS_DU; 
350			if (!(current_thread_info()->fpsaved[0] & flags))
351				rs1 = (argp)&zero;
352			break;
353		}
354		switch (type & 0x7) {
355		case 7: FP_UNPACK_QP (QA, rs1); break;
356		case 6: FP_UNPACK_DP (DA, rs1); break;
357		case 5: FP_UNPACK_SP (SA, rs1); break;
358		}
359		freg = (insn & 0x1f);
360		switch ((type >> 3) & 0x3) {
361		case 3: if (freg & 2) {
362				current_thread_info()->xfsr[0] |= (6 << 14) /* invalid_fp_register */;
363				goto err;
364			}
365		case 2: freg = ((freg & 1) << 5) | (freg & 0x1e);
366		case 1: rs2 = (argp)&f->regs[freg];
367			flags = (freg < 32) ? FPRS_DL : FPRS_DU; 
368			if (!(current_thread_info()->fpsaved[0] & flags))
369				rs2 = (argp)&zero;
370			break;
371		}
372		switch ((type >> 3) & 0x7) {
373		case 7: FP_UNPACK_QP (QB, rs2); break;
374		case 6: FP_UNPACK_DP (DB, rs2); break;
375		case 5: FP_UNPACK_SP (SB, rs2); break;
376		}
377		freg = ((insn >> 25) & 0x1f);
378		switch ((type >> 6) & 0x3) {
379		case 3: if (freg & 2) {
380				current_thread_info()->xfsr[0] |= (6 << 14) /* invalid_fp_register */;
381				goto err;
382			}
383		case 2: freg = ((freg & 1) << 5) | (freg & 0x1e);
384		case 1: rd = (argp)&f->regs[freg];
385			flags = (freg < 32) ? FPRS_DL : FPRS_DU; 
386			if (!(current_thread_info()->fpsaved[0] & FPRS_FEF)) {
387				current_thread_info()->fpsaved[0] = FPRS_FEF;
388				current_thread_info()->gsr[0] = 0;
389			}
390			if (!(current_thread_info()->fpsaved[0] & flags)) {
391				if (freg < 32)
392					memset(f->regs, 0, 32*sizeof(u32));
393				else
394					memset(f->regs+32, 0, 32*sizeof(u32));
395			}
396			current_thread_info()->fpsaved[0] |= flags;
397			break;
398		}
399		switch ((insn >> 5) & 0x1ff) {
400		/* + */
401		case FADDS: FP_ADD_S (SR, SA, SB); break;
402		case FADDD: FP_ADD_D (DR, DA, DB); break;
403		case FADDQ: FP_ADD_Q (QR, QA, QB); break;
404		/* - */
405		case FSUBS: FP_SUB_S (SR, SA, SB); break;
406		case FSUBD: FP_SUB_D (DR, DA, DB); break;
407		case FSUBQ: FP_SUB_Q (QR, QA, QB); break;
408		/* * */
409		case FMULS: FP_MUL_S (SR, SA, SB); break;
410		case FSMULD: FP_CONV (D, S, 1, 1, DA, SA);
411			     FP_CONV (D, S, 1, 1, DB, SB);
412		case FMULD: FP_MUL_D (DR, DA, DB); break;
413		case FDMULQ: FP_CONV (Q, D, 2, 1, QA, DA);
414			     FP_CONV (Q, D, 2, 1, QB, DB);
415		case FMULQ: FP_MUL_Q (QR, QA, QB); break;
416		/* / */
417		case FDIVS: FP_DIV_S (SR, SA, SB); break;
418		case FDIVD: FP_DIV_D (DR, DA, DB); break;
419		case FDIVQ: FP_DIV_Q (QR, QA, QB); break;
420		/* sqrt */
421		case FSQRTS: FP_SQRT_S (SR, SB); break;
422		case FSQRTD: FP_SQRT_D (DR, DB); break;
423		case FSQRTQ: FP_SQRT_Q (QR, QB); break;
424		/* mov */
425		case FMOVQ: rd->q[0] = rs2->q[0]; rd->q[1] = rs2->q[1]; break;
426		case FABSQ: rd->q[0] = rs2->q[0] & 0x7fffffffffffffffUL; rd->q[1] = rs2->q[1]; break;
427		case FNEGQ: rd->q[0] = rs2->q[0] ^ 0x8000000000000000UL; rd->q[1] = rs2->q[1]; break;
428		/* float to int */
429		case FSTOI: FP_TO_INT_S (IR, SB, 32, 1); break;
430		case FDTOI: FP_TO_INT_D (IR, DB, 32, 1); break;
431		case FQTOI: FP_TO_INT_Q (IR, QB, 32, 1); break;
432		case FSTOX: FP_TO_INT_S (XR, SB, 64, 1); break;
433		case FDTOX: FP_TO_INT_D (XR, DB, 64, 1); break;
434		case FQTOX: FP_TO_INT_Q (XR, QB, 64, 1); break;
435		/* int to float */
436		case FITOQ: IR = rs2->s; FP_FROM_INT_Q (QR, IR, 32, int); break;
437		case FXTOQ: XR = rs2->d; FP_FROM_INT_Q (QR, XR, 64, long); break;
438		/* Only Ultra-III generates these */
439		case FXTOS: XR = rs2->d; FP_FROM_INT_S (SR, XR, 64, long); break;
440		case FXTOD: XR = rs2->d; FP_FROM_INT_D (DR, XR, 64, long); break;
441#if 0		/* Optimized inline in sparc64/kernel/entry.S */
442		case FITOS: IR = rs2->s; FP_FROM_INT_S (SR, IR, 32, int); break;
443#endif
444		case FITOD: IR = rs2->s; FP_FROM_INT_D (DR, IR, 32, int); break;
445		/* float to float */
446		case FSTOD: FP_CONV (D, S, 1, 1, DR, SB); break;
447		case FSTOQ: FP_CONV (Q, S, 2, 1, QR, SB); break;
448		case FDTOQ: FP_CONV (Q, D, 2, 1, QR, DB); break;
449		case FDTOS: FP_CONV (S, D, 1, 1, SR, DB); break;
450		case FQTOS: FP_CONV (S, Q, 1, 2, SR, QB); break;
451		case FQTOD: FP_CONV (D, Q, 1, 2, DR, QB); break;
452		/* comparison */
453		case FCMPQ:
454		case FCMPEQ:
455			FP_CMP_Q(XR, QB, QA, 3);
456			if (XR == 3 &&
457			    (((insn >> 5) & 0x1ff) == FCMPEQ ||
458			     FP_ISSIGNAN_Q(QA) ||
459			     FP_ISSIGNAN_Q(QB)))
460				FP_SET_EXCEPTION (FP_EX_INVALID);
461		}
462		if (!FP_INHIBIT_RESULTS) {
463			switch ((type >> 6) & 0x7) {
464			case 0: xfsr = current_thread_info()->xfsr[0];
465				if (XR == -1) XR = 2;
466				switch (freg & 3) {
467				/* fcc0, 1, 2, 3 */
468				case 0: xfsr &= ~0xc00; xfsr |= (XR << 10); break;
469				case 1: xfsr &= ~0x300000000UL; xfsr |= (XR << 32); break;
470				case 2: xfsr &= ~0xc00000000UL; xfsr |= (XR << 34); break;
471				case 3: xfsr &= ~0x3000000000UL; xfsr |= (XR << 36); break;
472				}
473				current_thread_info()->xfsr[0] = xfsr;
474				break;
475			case 1: rd->s = IR; break;
476			case 2: rd->d = XR; break;
477			case 5: FP_PACK_SP (rd, SR); break;
478			case 6: FP_PACK_DP (rd, DR); break;
479			case 7: FP_PACK_QP (rd, QR); break;
480			}
481		}
482
483		if(_fex != 0)
484			return record_exception(regs, _fex);
485
486		/* Success and no exceptions detected. */
487		current_thread_info()->xfsr[0] &= ~(FSR_CEXC_MASK);
488		regs->tpc = regs->tnpc;
489		regs->tnpc += 4;
490		return 1;
491	}
492err:	return 0;
493}