diff options
Diffstat (limited to 'arch/powerpc/math-emu')
| -rw-r--r-- | arch/powerpc/math-emu/Makefile | 23 | ||||
| -rw-r--r-- | arch/powerpc/math-emu/fre.c | 11 | ||||
| -rw-r--r-- | arch/powerpc/math-emu/frsqrtes.c | 11 | ||||
| -rw-r--r-- | arch/powerpc/math-emu/math.c | 99 | ||||
| -rw-r--r-- | arch/powerpc/math-emu/math_efp.c | 459 | ||||
| -rw-r--r-- | arch/powerpc/math-emu/mtfsf.c | 58 | 
6 files changed, 427 insertions, 234 deletions
diff --git a/arch/powerpc/math-emu/Makefile b/arch/powerpc/math-emu/Makefile index 7d1dba0d57f..1b46ab4f641 100644 --- a/arch/powerpc/math-emu/Makefile +++ b/arch/powerpc/math-emu/Makefile @@ -1,14 +1,15 @@ - -obj-$(CONFIG_MATH_EMULATION)	+= fabs.o fadd.o fadds.o fcmpo.o fcmpu.o \ -					fctiw.o fctiwz.o fdiv.o fdivs.o \ -					fmadd.o fmadds.o fmsub.o fmsubs.o \ -					fmul.o fmuls.o fnabs.o fneg.o \ -					fnmadd.o fnmadds.o fnmsub.o fnmsubs.o \ -					fres.o frsp.o frsqrte.o fsel.o lfs.o \ -					fsqrt.o	fsqrts.o fsub.o fsubs.o \ -					mcrfs.o mffs.o mtfsb0.o mtfsb1.o \ -					mtfsf.o mtfsfi.o stfiwx.o stfs.o \ -					math.o fmr.o lfd.o stfd.o +math-emu-common-objs = math.o fre.o fsqrt.o fsqrts.o frsqrtes.o mtfsf.o mtfsfi.o +obj-$(CONFIG_MATH_EMULATION_HW_UNIMPLEMENTED) += $(math-emu-common-objs) +obj-$(CONFIG_MATH_EMULATION_FULL) += $(math-emu-common-objs) fabs.o fadd.o \ +					fadds.o fcmpo.o fcmpu.o fctiw.o \ +					fctiwz.o fdiv.o fdivs.o  fmadd.o \ +					fmadds.o fmsub.o fmsubs.o fmul.o \ +					fmuls.o fnabs.o fneg.o fnmadd.o \ +					fnmadds.o fnmsub.o fnmsubs.o fres.o \ +					frsp.o fsel.o lfs.o frsqrte.o fsub.o \ +					fsubs.o  mcrfs.o mffs.o mtfsb0.o \ +					mtfsb1.o stfiwx.o stfs.o math.o \ +					fmr.o lfd.o stfd.o  obj-$(CONFIG_SPE)		+= math_efp.o diff --git a/arch/powerpc/math-emu/fre.c b/arch/powerpc/math-emu/fre.c new file mode 100644 index 00000000000..49ccf2cc6a5 --- /dev/null +++ b/arch/powerpc/math-emu/fre.c @@ -0,0 +1,11 @@ +#include <linux/types.h> +#include <linux/errno.h> +#include <asm/uaccess.h> + +int fre(void *frD, void *frB) +{ +#ifdef DEBUG +	printk("%s: %p %p\n", __func__, frD, frB); +#endif +	return -ENOSYS; +} diff --git a/arch/powerpc/math-emu/frsqrtes.c b/arch/powerpc/math-emu/frsqrtes.c new file mode 100644 index 00000000000..7e838e38031 --- /dev/null +++ b/arch/powerpc/math-emu/frsqrtes.c @@ -0,0 +1,11 @@ +#include <linux/types.h> +#include <linux/errno.h> +#include <asm/uaccess.h> + +int frsqrtes(void *frD, void *frB) +{ +#ifdef DEBUG +	printk("%s: %p %p\n", __func__, frD, frB); +#endif +	return 0; +} diff --git a/arch/powerpc/math-emu/math.c b/arch/powerpc/math-emu/math.c index 164d55935bd..ab151f04050 100644 --- a/arch/powerpc/math-emu/math.c +++ b/arch/powerpc/math-emu/math.c @@ -7,12 +7,27 @@  #include <asm/uaccess.h>  #include <asm/reg.h> +#include <asm/switch_to.h>  #include <asm/sfp-machine.h>  #include <math-emu/double.h>  #define FLOATFUNC(x)	extern int x(void *, void *, void *, void *) +/* The instructions list which may be not implemented by a hardware FPU */ +FLOATFUNC(fre); +FLOATFUNC(frsqrtes); +FLOATFUNC(fsqrt); +FLOATFUNC(fsqrts); +FLOATFUNC(mtfsf); +FLOATFUNC(mtfsfi); + +#ifdef CONFIG_MATH_EMULATION_HW_UNIMPLEMENTED +#undef FLOATFUNC(x) +#define FLOATFUNC(x)	static inline int x(void *op1, void *op2, void *op3, \ +						 void *op4) { } +#endif +  FLOATFUNC(fadd);  FLOATFUNC(fadds);  FLOATFUNC(fdiv); @@ -42,8 +57,6 @@ FLOATFUNC(mcrfs);  FLOATFUNC(mffs);  FLOATFUNC(mtfsb0);  FLOATFUNC(mtfsb1); -FLOATFUNC(mtfsf); -FLOATFUNC(mtfsfi);  FLOATFUNC(lfd);  FLOATFUNC(lfs); @@ -61,8 +74,6 @@ FLOATFUNC(fneg);  FLOATFUNC(fres);  FLOATFUNC(frsqrte);  FLOATFUNC(fsel); -FLOATFUNC(fsqrt); -FLOATFUNC(fsqrts);  #define OP31		0x1f		/*   31 */ @@ -97,6 +108,7 @@ FLOATFUNC(fsqrts);  #define FSQRTS		0x016		/*   22 */  #define FRES		0x018		/*   24 */  #define FMULS		0x019		/*   25 */ +#define FRSQRTES	0x01a		/*   26 */  #define FMSUBS		0x01c		/*   28 */  #define FMADDS		0x01d		/*   29 */  #define FNMSUBS		0x01e		/*   30 */ @@ -109,6 +121,7 @@ FLOATFUNC(fsqrts);  #define FADD		0x015		/*   21 */  #define FSQRT		0x016		/*   22 */  #define FSEL		0x017		/*   23 */ +#define FRE		0x018		/*   24 */  #define FMUL		0x019		/*   25 */  #define FRSQRTE		0x01a		/*   26 */  #define FMSUB		0x01c		/*   28 */ @@ -150,7 +163,6 @@ FLOATFUNC(fsqrts);  #define XEU	15  #define XFLB	10 -#ifdef CONFIG_MATH_EMULATION  static int  record_exception(struct pt_regs *regs, int eflag)  { @@ -208,7 +220,6 @@ record_exception(struct pt_regs *regs, int eflag)  	return (fpscr & FPSCR_FEX) ? 1 : 0;  } -#endif /* CONFIG_MATH_EMULATION */  int  do_mathemu(struct pt_regs *regs) @@ -218,56 +229,13 @@ do_mathemu(struct pt_regs *regs)  	signed short sdisp;  	u32 insn = 0;  	int idx = 0; -#ifdef CONFIG_MATH_EMULATION  	int (*func)(void *, void *, void *, void *);  	int type = 0;  	int eflag, trap; -#endif  	if (get_user(insn, (u32 *)pc))  		return -EFAULT; -#ifndef CONFIG_MATH_EMULATION -	switch (insn >> 26) { -	case LFD: -		idx = (insn >> 16) & 0x1f; -		sdisp = (insn & 0xffff); -		op0 = (void *)¤t->thread.TS_FPR((insn >> 21) & 0x1f); -		op1 = (void *)((idx ? regs->gpr[idx] : 0) + sdisp); -		lfd(op0, op1, op2, op3); -		break; -	case LFDU: -		idx = (insn >> 16) & 0x1f; -		sdisp = (insn & 0xffff); -		op0 = (void *)¤t->thread.TS_FPR((insn >> 21) & 0x1f); -		op1 = (void *)((idx ? regs->gpr[idx] : 0) + sdisp); -		lfd(op0, op1, op2, op3); -		regs->gpr[idx] = (unsigned long)op1; -		break; -	case STFD: -		idx = (insn >> 16) & 0x1f; -		sdisp = (insn & 0xffff); -		op0 = (void *)¤t->thread.TS_FPR((insn >> 21) & 0x1f); -		op1 = (void *)((idx ? regs->gpr[idx] : 0) + sdisp); -		stfd(op0, op1, op2, op3); -		break; -	case STFDU: -		idx = (insn >> 16) & 0x1f; -		sdisp = (insn & 0xffff); -		op0 = (void *)¤t->thread.TS_FPR((insn >> 21) & 0x1f); -		op1 = (void *)((idx ? regs->gpr[idx] : 0) + sdisp); -		stfd(op0, op1, op2, op3); -		regs->gpr[idx] = (unsigned long)op1; -		break; -	case OP63: -		op0 = (void *)¤t->thread.TS_FPR((insn >> 21) & 0x1f); -		op1 = (void *)¤t->thread.TS_FPR((insn >> 11) & 0x1f); -		fmr(op0, op1, op2, op3); -		break; -	default: -		goto illegal; -	} -#else /* CONFIG_MATH_EMULATION */  	switch (insn >> 26) {  	case LFS:	func = lfs;	type = D;	break;  	case LFSU:	func = lfs;	type = DU;	break; @@ -299,9 +267,10 @@ do_mathemu(struct pt_regs *regs)  		case FDIVS:	func = fdivs;	type = AB;	break;  		case FSUBS:	func = fsubs;	type = AB;	break;  		case FADDS:	func = fadds;	type = AB;	break; -		case FSQRTS:	func = fsqrts;	type = AB;	break; -		case FRES:	func = fres;	type = AB;	break; +		case FSQRTS:	func = fsqrts;	type = XB;	break; +		case FRES:	func = fres;	type = XB;	break;  		case FMULS:	func = fmuls;	type = AC;	break; +		case FRSQRTES:	func = frsqrtes;type = XB;	break;  		case FMSUBS:	func = fmsubs;	type = ABC;	break;  		case FMADDS:	func = fmadds;	type = ABC;	break;  		case FNMSUBS:	func = fnmsubs;	type = ABC;	break; @@ -317,10 +286,11 @@ do_mathemu(struct pt_regs *regs)  			case FDIV:	func = fdiv;	type = AB;	break;  			case FSUB:	func = fsub;	type = AB;	break;  			case FADD:	func = fadd;	type = AB;	break; -			case FSQRT:	func = fsqrt;	type = AB;	break; +			case FSQRT:	func = fsqrt;	type = XB;	break; +			case FRE:	func = fre;	type = XB;	break;  			case FSEL:	func = fsel;	type = ABC;	break;  			case FMUL:	func = fmul;	type = AC;	break; -			case FRSQRTE:	func = frsqrte;	type = AB;	break; +			case FRSQRTE:	func = frsqrte;	type = XB;	break;  			case FMSUB:	func = fmsub;	type = ABC;	break;  			case FMADD:	func = fmadd;	type = ABC;	break;  			case FNMSUB:	func = fnmsub;	type = ABC;	break; @@ -410,21 +380,16 @@ do_mathemu(struct pt_regs *regs)  	case XE:  		idx = (insn >> 16) & 0x1f;  		op0 = (void *)¤t->thread.TS_FPR((insn >> 21) & 0x1f); -		if (!idx) { -			if (((insn >> 1) & 0x3ff) == STFIWX) -				op1 = (void *)(regs->gpr[(insn >> 11) & 0x1f]); -			else -				goto illegal; -		} else { -			op1 = (void *)(regs->gpr[idx] + regs->gpr[(insn >> 11) & 0x1f]); -		} - +		op1 = (void *)((idx ? regs->gpr[idx] : 0) +				+ regs->gpr[(insn >> 11) & 0x1f]);  		break;  	case XEU:  		idx = (insn >> 16) & 0x1f; +		if (!idx) +			goto illegal;  		op0 = (void *)¤t->thread.TS_FPR((insn >> 21) & 0x1f); -		op1 = (void *)((idx ? regs->gpr[idx] : 0) +		op1 = (void *)(regs->gpr[idx]  				+ regs->gpr[(insn >> 11) & 0x1f]);  		break; @@ -459,6 +424,13 @@ do_mathemu(struct pt_regs *regs)  		goto illegal;  	} +	/* +	 * If we support a HW FPU, we need to ensure the FP state +	 * is flushed into the thread_struct before attempting +	 * emulation +	 */ +	flush_fp_to_thread(current); +  	eflag = func(op0, op1, op2, op3);  	if (insn & 1) { @@ -479,7 +451,6 @@ do_mathemu(struct pt_regs *regs)  	default:  		break;  	} -#endif /* CONFIG_MATH_EMULATION */  	regs->nip += 4;  	return 0; diff --git a/arch/powerpc/math-emu/math_efp.c b/arch/powerpc/math-emu/math_efp.c index 41f4ef30e48..28337c9709a 100644 --- a/arch/powerpc/math-emu/math_efp.c +++ b/arch/powerpc/math-emu/math_efp.c @@ -1,7 +1,7 @@  /*   * arch/powerpc/math-emu/math_efp.c   * - * Copyright (C) 2006-2008 Freescale Semiconductor, Inc. All rights reserved. + * Copyright (C) 2006-2008, 2010 Freescale Semiconductor, Inc.   *   * Author: Ebony Zhu,	<ebony.zhu@freescale.com>   *         Yu Liu,	<yu.liu@freescale.com> @@ -20,6 +20,7 @@   */  #include <linux/types.h> +#include <linux/prctl.h>  #include <asm/uaccess.h>  #include <asm/reg.h> @@ -104,6 +105,8 @@  #define FP_EX_MASK	(FP_EX_INEXACT | FP_EX_INVALID | FP_EX_DIVZERO | \  			FP_EX_UNDERFLOW | FP_EX_OVERFLOW) +static int have_e500_cpu_a005_erratum; +  union dw_union {  	u64 dp[1];  	u32 wp[2]; @@ -169,10 +172,6 @@ static unsigned long insn_type(unsigned long speinsn)  	case EFDNABS:	ret = XA;	break;  	case EFDNEG:	ret = XA;	break;  	case EFDSUB:	ret = AB;	break; - -	default: -		printk(KERN_ERR "\nOoops! SPE instruction no type found."); -		printk(KERN_ERR "\ninst code: %08lx\n", speinsn);  	}  	return ret; @@ -193,7 +192,7 @@ int do_spe_mathemu(struct pt_regs *regs)  	type = insn_type(speinsn);  	if (type == NOTYPE) -		return -ENOSYS; +		goto illegal;  	func = speinsn & 0x7ff;  	fc = (speinsn >> 21) & 0x1f; @@ -210,12 +209,10 @@ int do_spe_mathemu(struct pt_regs *regs)  	__FPU_FPSCR = mfspr(SPRN_SPEFSCR); -#ifdef DEBUG -	printk("speinsn:%08lx spefscr:%08lx\n", speinsn, __FPU_FPSCR); -	printk("vc: %08x  %08x\n", vc.wp[0], vc.wp[1]); -	printk("va: %08x  %08x\n", va.wp[0], va.wp[1]); -	printk("vb: %08x  %08x\n", vb.wp[0], vb.wp[1]); -#endif +	pr_debug("speinsn:%08lx spefscr:%08lx\n", speinsn, __FPU_FPSCR); +	pr_debug("vc: %08x  %08x\n", vc.wp[0], vc.wp[1]); +	pr_debug("va: %08x  %08x\n", va.wp[0], va.wp[1]); +	pr_debug("vb: %08x  %08x\n", vb.wp[0], vb.wp[1]);  	switch (src) {  	case SPFP: { @@ -233,10 +230,8 @@ int do_spe_mathemu(struct pt_regs *regs)  			break;  		} -#ifdef DEBUG -		printk("SA: %ld %08lx %ld (%ld)\n", SA_s, SA_f, SA_e, SA_c); -		printk("SB: %ld %08lx %ld (%ld)\n", SB_s, SB_f, SB_e, SB_c); -#endif +		pr_debug("SA: %ld %08lx %ld (%ld)\n", SA_s, SA_f, SA_e, SA_c); +		pr_debug("SB: %ld %08lx %ld (%ld)\n", SB_s, SB_f, SB_e, SB_c);  		switch (func) {  		case EFSABS: @@ -281,21 +276,13 @@ int do_spe_mathemu(struct pt_regs *regs)  		case EFSCTSF:  		case EFSCTUF: -			if (!((vb.wp[1] >> 23) == 0xff && ((vb.wp[1] & 0x7fffff) > 0))) { -				/* NaN */ -				if (((vb.wp[1] >> 23) & 0xff) == 0) { -					/* denorm */ -					vc.wp[1] = 0x0; -				} else if ((vb.wp[1] >> 31) == 0) { -					/* positive normal */ -					vc.wp[1] = (func == EFSCTSF) ? -						0x7fffffff : 0xffffffff; -				} else { /* negative normal */ -					vc.wp[1] = (func == EFSCTSF) ? -						0x80000000 : 0x0; -				} -			} else { /* rB is NaN */ -				vc.wp[1] = 0x0; +			if (SB_c == FP_CLS_NAN) { +				vc.wp[1] = 0; +				FP_SET_EXCEPTION(FP_EX_INVALID); +			} else { +				SB_e += (func == EFSCTSF ? 31 : 32); +				FP_TO_INT_ROUND_S(vc.wp[1], SB, 32, +						(func == EFSCTSF));  			}  			goto update_regs; @@ -303,24 +290,34 @@ int do_spe_mathemu(struct pt_regs *regs)  			FP_DECL_D(DB);  			FP_CLEAR_EXCEPTIONS;  			FP_UNPACK_DP(DB, vb.dp); -#ifdef DEBUG -			printk("DB: %ld %08lx %08lx %ld (%ld)\n", + +			pr_debug("DB: %ld %08lx %08lx %ld (%ld)\n",  					DB_s, DB_f1, DB_f0, DB_e, DB_c); -#endif +  			FP_CONV(S, D, 1, 2, SR, DB);  			goto pack_s;  		}  		case EFSCTSI: -		case EFSCTSIZ:  		case EFSCTUI: +			if (SB_c == FP_CLS_NAN) { +				vc.wp[1] = 0; +				FP_SET_EXCEPTION(FP_EX_INVALID); +			} else { +				FP_TO_INT_ROUND_S(vc.wp[1], SB, 32, +						((func & 0x3) != 0)); +			} +			goto update_regs; + +		case EFSCTSIZ:  		case EFSCTUIZ: -			if (func & 0x4) { -				_FP_ROUND(1, SB); +			if (SB_c == FP_CLS_NAN) { +				vc.wp[1] = 0; +				FP_SET_EXCEPTION(FP_EX_INVALID);  			} else { -				_FP_ROUND_ZERO(1, SB); +				FP_TO_INT_S(vc.wp[1], SB, 32, +						((func & 0x3) != 0));  			} -			FP_TO_INT_S(vc.wp[1], SB, 32, ((func & 0x3) != 0));  			goto update_regs;  		default: @@ -329,9 +326,8 @@ int do_spe_mathemu(struct pt_regs *regs)  		break;  pack_s: -#ifdef DEBUG -		printk("SR: %ld %08lx %ld (%ld)\n", SR_s, SR_f, SR_e, SR_c); -#endif +		pr_debug("SR: %ld %08lx %ld (%ld)\n", SR_s, SR_f, SR_e, SR_c); +  		FP_PACK_SP(vc.wp + 1, SR);  		goto update_regs; @@ -362,12 +358,10 @@ cmp_s:  			break;  		} -#ifdef DEBUG -		printk("DA: %ld %08lx %08lx %ld (%ld)\n", +		pr_debug("DA: %ld %08lx %08lx %ld (%ld)\n",  				DA_s, DA_f1, DA_f0, DA_e, DA_c); -		printk("DB: %ld %08lx %08lx %ld (%ld)\n", +		pr_debug("DB: %ld %08lx %08lx %ld (%ld)\n",  				DB_s, DB_f1, DB_f0, DB_e, DB_c); -#endif  		switch (func) {  		case EFDABS: @@ -412,22 +406,13 @@ cmp_s:  		case EFDCTSF:  		case EFDCTUF: -			if (!((vb.wp[0] >> 20) == 0x7ff && -			   ((vb.wp[0] & 0xfffff) > 0 || (vb.wp[1] > 0)))) { -				/* not a NaN */ -				if (((vb.wp[0] >> 20) & 0x7ff) == 0) { -					/* denorm */ -					vc.wp[1] = 0x0; -				} else if ((vb.wp[0] >> 31) == 0) { -					/* positive normal */ -					vc.wp[1] = (func == EFDCTSF) ? -						0x7fffffff : 0xffffffff; -				} else { /* negative normal */ -					vc.wp[1] = (func == EFDCTSF) ? -						0x80000000 : 0x0; -				} -			} else { /* NaN */ -				vc.wp[1] = 0x0; +			if (DB_c == FP_CLS_NAN) { +				vc.wp[1] = 0; +				FP_SET_EXCEPTION(FP_EX_INVALID); +			} else { +				DB_e += (func == EFDCTSF ? 31 : 32); +				FP_TO_INT_ROUND_D(vc.wp[1], DB, 32, +						(func == EFDCTSF));  			}  			goto update_regs; @@ -435,30 +420,45 @@ cmp_s:  			FP_DECL_S(SB);  			FP_CLEAR_EXCEPTIONS;  			FP_UNPACK_SP(SB, vb.wp + 1); -#ifdef DEBUG -			printk("SB: %ld %08lx %ld (%ld)\n", + +			pr_debug("SB: %ld %08lx %ld (%ld)\n",  					SB_s, SB_f, SB_e, SB_c); -#endif +  			FP_CONV(D, S, 2, 1, DR, SB);  			goto pack_d;  		}  		case EFDCTUIDZ:  		case EFDCTSIDZ: -			_FP_ROUND_ZERO(2, DB); -			FP_TO_INT_D(vc.dp[0], DB, 64, ((func & 0x1) == 0)); +			if (DB_c == FP_CLS_NAN) { +				vc.dp[0] = 0; +				FP_SET_EXCEPTION(FP_EX_INVALID); +			} else { +				FP_TO_INT_D(vc.dp[0], DB, 64, +						((func & 0x1) == 0)); +			}  			goto update_regs;  		case EFDCTUI:  		case EFDCTSI: +			if (DB_c == FP_CLS_NAN) { +				vc.wp[1] = 0; +				FP_SET_EXCEPTION(FP_EX_INVALID); +			} else { +				FP_TO_INT_ROUND_D(vc.wp[1], DB, 32, +						((func & 0x3) != 0)); +			} +			goto update_regs; +  		case EFDCTUIZ:  		case EFDCTSIZ: -			if (func & 0x4) { -				_FP_ROUND(2, DB); +			if (DB_c == FP_CLS_NAN) { +				vc.wp[1] = 0; +				FP_SET_EXCEPTION(FP_EX_INVALID);  			} else { -				_FP_ROUND_ZERO(2, DB); +				FP_TO_INT_D(vc.wp[1], DB, 32, +						((func & 0x3) != 0));  			} -			FP_TO_INT_D(vc.wp[1], DB, 32, ((func & 0x3) != 0));  			goto update_regs;  		default: @@ -467,10 +467,9 @@ cmp_s:  		break;  pack_d: -#ifdef DEBUG -		printk("DR: %ld %08lx %08lx %ld (%ld)\n", +		pr_debug("DR: %ld %08lx %08lx %ld (%ld)\n",  				DR_s, DR_f1, DR_f0, DR_e, DR_c); -#endif +  		FP_PACK_DP(vc.dp, DR);  		goto update_regs; @@ -507,12 +506,14 @@ cmp_d:  			break;  		} -#ifdef DEBUG -		printk("SA0: %ld %08lx %ld (%ld)\n", SA0_s, SA0_f, SA0_e, SA0_c); -		printk("SA1: %ld %08lx %ld (%ld)\n", SA1_s, SA1_f, SA1_e, SA1_c); -		printk("SB0: %ld %08lx %ld (%ld)\n", SB0_s, SB0_f, SB0_e, SB0_c); -		printk("SB1: %ld %08lx %ld (%ld)\n", SB1_s, SB1_f, SB1_e, SB1_c); -#endif +		pr_debug("SA0: %ld %08lx %ld (%ld)\n", +				SA0_s, SA0_f, SA0_e, SA0_c); +		pr_debug("SA1: %ld %08lx %ld (%ld)\n", +				SA1_s, SA1_f, SA1_e, SA1_c); +		pr_debug("SB0: %ld %08lx %ld (%ld)\n", +				SB0_s, SB0_f, SB0_e, SB0_c); +		pr_debug("SB1: %ld %08lx %ld (%ld)\n", +				SB1_s, SB1_f, SB1_e, SB1_c);  		switch (func) {  		case EVFSABS: @@ -562,35 +563,60 @@ cmp_d:  			cmp = -1;  			goto cmp_vs; -		case EVFSCTSF: -			__asm__ __volatile__ ("mtspr 512, %4\n" -				"efsctsf %0, %2\n" -				"efsctsf %1, %3\n" -				: "=r" (vc.wp[0]), "=r" (vc.wp[1]) -				: "r" (vb.wp[0]), "r" (vb.wp[1]), "r" (0)); -			goto update_regs; -  		case EVFSCTUF: -			__asm__ __volatile__ ("mtspr 512, %4\n" -				"efsctuf %0, %2\n" -				"efsctuf %1, %3\n" -				: "=r" (vc.wp[0]), "=r" (vc.wp[1]) -				: "r" (vb.wp[0]), "r" (vb.wp[1]), "r" (0)); +		case EVFSCTSF: +			if (SB0_c == FP_CLS_NAN) { +				vc.wp[0] = 0; +				FP_SET_EXCEPTION(FP_EX_INVALID); +			} else { +				SB0_e += (func == EVFSCTSF ? 31 : 32); +				FP_TO_INT_ROUND_S(vc.wp[0], SB0, 32, +						(func == EVFSCTSF)); +			} +			if (SB1_c == FP_CLS_NAN) { +				vc.wp[1] = 0; +				FP_SET_EXCEPTION(FP_EX_INVALID); +			} else { +				SB1_e += (func == EVFSCTSF ? 31 : 32); +				FP_TO_INT_ROUND_S(vc.wp[1], SB1, 32, +						(func == EVFSCTSF)); +			}  			goto update_regs;  		case EVFSCTUI:  		case EVFSCTSI: +			if (SB0_c == FP_CLS_NAN) { +				vc.wp[0] = 0; +				FP_SET_EXCEPTION(FP_EX_INVALID); +			} else { +				FP_TO_INT_ROUND_S(vc.wp[0], SB0, 32, +						((func & 0x3) != 0)); +			} +			if (SB1_c == FP_CLS_NAN) { +				vc.wp[1] = 0; +				FP_SET_EXCEPTION(FP_EX_INVALID); +			} else { +				FP_TO_INT_ROUND_S(vc.wp[1], SB1, 32, +						((func & 0x3) != 0)); +			} +			goto update_regs; +  		case EVFSCTUIZ:  		case EVFSCTSIZ: -			if (func & 0x4) { -				_FP_ROUND(1, SB0); -				_FP_ROUND(1, SB1); +			if (SB0_c == FP_CLS_NAN) { +				vc.wp[0] = 0; +				FP_SET_EXCEPTION(FP_EX_INVALID); +			} else { +				FP_TO_INT_S(vc.wp[0], SB0, 32, +						((func & 0x3) != 0)); +			} +			if (SB1_c == FP_CLS_NAN) { +				vc.wp[1] = 0; +				FP_SET_EXCEPTION(FP_EX_INVALID);  			} else { -				_FP_ROUND_ZERO(1, SB0); -				_FP_ROUND_ZERO(1, SB1); +				FP_TO_INT_S(vc.wp[1], SB1, 32, +						((func & 0x3) != 0));  			} -			FP_TO_INT_S(vc.wp[0], SB0, 32, ((func & 0x3) != 0)); -			FP_TO_INT_S(vc.wp[1], SB1, 32, ((func & 0x3) != 0));  			goto update_regs;  		default: @@ -599,10 +625,11 @@ cmp_d:  		break;  pack_vs: -#ifdef DEBUG -		printk("SR0: %ld %08lx %ld (%ld)\n", SR0_s, SR0_f, SR0_e, SR0_c); -		printk("SR1: %ld %08lx %ld (%ld)\n", SR1_s, SR1_f, SR1_e, SR1_c); -#endif +		pr_debug("SR0: %ld %08lx %ld (%ld)\n", +				SR0_s, SR0_f, SR0_e, SR0_c); +		pr_debug("SR1: %ld %08lx %ld (%ld)\n", +				SR1_s, SR1_f, SR1_e, SR1_c); +  		FP_PACK_SP(vc.wp, SR0);  		FP_PACK_SP(vc.wp + 1, SR1);  		goto update_regs; @@ -633,25 +660,65 @@ update_ccr:  	regs->ccr |= (IR << ((7 - ((speinsn >> 23) & 0x7)) << 2));  update_regs: -	__FPU_FPSCR &= ~FP_EX_MASK; +	/* +	 * If the "invalid" exception sticky bit was set by the +	 * processor for non-finite input, but was not set before the +	 * instruction being emulated, clear it.  Likewise for the +	 * "underflow" bit, which may have been set by the processor +	 * for exact underflow, not just inexact underflow when the +	 * flag should be set for IEEE 754 semantics.  Other sticky +	 * exceptions will only be set by the processor when they are +	 * correct according to IEEE 754 semantics, and we must not +	 * clear sticky bits that were already set before the emulated +	 * instruction as they represent the user-visible sticky +	 * exception status.  "inexact" traps to kernel are not +	 * required for IEEE semantics and are not enabled by default, +	 * so the "inexact" sticky bit may have been set by a previous +	 * instruction without the kernel being aware of it. +	 */ +	__FPU_FPSCR +	  &= ~(FP_EX_INVALID | FP_EX_UNDERFLOW) | current->thread.spefscr_last;  	__FPU_FPSCR |= (FP_CUR_EXCEPTIONS & FP_EX_MASK);  	mtspr(SPRN_SPEFSCR, __FPU_FPSCR); +	current->thread.spefscr_last = __FPU_FPSCR;  	current->thread.evr[fc] = vc.wp[0];  	regs->gpr[fc] = vc.wp[1]; -#ifdef DEBUG -	printk("ccr = %08lx\n", regs->ccr); -	printk("cur exceptions = %08x spefscr = %08lx\n", +	pr_debug("ccr = %08lx\n", regs->ccr); +	pr_debug("cur exceptions = %08x spefscr = %08lx\n",  			FP_CUR_EXCEPTIONS, __FPU_FPSCR); -	printk("vc: %08x  %08x\n", vc.wp[0], vc.wp[1]); -	printk("va: %08x  %08x\n", va.wp[0], va.wp[1]); -	printk("vb: %08x  %08x\n", vb.wp[0], vb.wp[1]); -#endif - +	pr_debug("vc: %08x  %08x\n", vc.wp[0], vc.wp[1]); +	pr_debug("va: %08x  %08x\n", va.wp[0], va.wp[1]); +	pr_debug("vb: %08x  %08x\n", vb.wp[0], vb.wp[1]); + +	if (current->thread.fpexc_mode & PR_FP_EXC_SW_ENABLE) { +		if ((FP_CUR_EXCEPTIONS & FP_EX_DIVZERO) +		    && (current->thread.fpexc_mode & PR_FP_EXC_DIV)) +			return 1; +		if ((FP_CUR_EXCEPTIONS & FP_EX_OVERFLOW) +		    && (current->thread.fpexc_mode & PR_FP_EXC_OVF)) +			return 1; +		if ((FP_CUR_EXCEPTIONS & FP_EX_UNDERFLOW) +		    && (current->thread.fpexc_mode & PR_FP_EXC_UND)) +			return 1; +		if ((FP_CUR_EXCEPTIONS & FP_EX_INEXACT) +		    && (current->thread.fpexc_mode & PR_FP_EXC_RES)) +			return 1; +		if ((FP_CUR_EXCEPTIONS & FP_EX_INVALID) +		    && (current->thread.fpexc_mode & PR_FP_EXC_INV)) +			return 1; +	}  	return 0;  illegal: +	if (have_e500_cpu_a005_erratum) { +		/* according to e500 cpu a005 erratum, reissue efp inst */ +		regs->nip -= 4; +		pr_debug("re-issue efp inst: %08lx\n", speinsn); +		return 0; +	} +  	printk(KERN_ERR "\nOoops! IEEE-754 compliance handler encountered un-supported instruction.\ninst code: %08lx\n", speinsn);  	return -ENOSYS;  } @@ -660,25 +727,98 @@ int speround_handler(struct pt_regs *regs)  {  	union dw_union fgpr;  	int s_lo, s_hi; -	unsigned long speinsn, type, fc; +	int lo_inexact, hi_inexact; +	int fp_result; +	unsigned long speinsn, type, fb, fc, fptype, func;  	if (get_user(speinsn, (unsigned int __user *) regs->nip))  		return -EFAULT;  	if ((speinsn >> 26) != 4)  		return -EINVAL;         /* not an spe instruction */ -	type = insn_type(speinsn & 0x7ff); +	func = speinsn & 0x7ff; +	type = insn_type(func);  	if (type == XCR) return -ENOSYS; +	__FPU_FPSCR = mfspr(SPRN_SPEFSCR); +	pr_debug("speinsn:%08lx spefscr:%08lx\n", speinsn, __FPU_FPSCR); + +	fptype = (speinsn >> 5) & 0x7; + +	/* No need to round if the result is exact */ +	lo_inexact = __FPU_FPSCR & (SPEFSCR_FG | SPEFSCR_FX); +	hi_inexact = __FPU_FPSCR & (SPEFSCR_FGH | SPEFSCR_FXH); +	if (!(lo_inexact || (hi_inexact && fptype == VCT))) +		return 0; +  	fc = (speinsn >> 21) & 0x1f;  	s_lo = regs->gpr[fc] & SIGN_BIT_S;  	s_hi = current->thread.evr[fc] & SIGN_BIT_S;  	fgpr.wp[0] = current->thread.evr[fc];  	fgpr.wp[1] = regs->gpr[fc]; -	__FPU_FPSCR = mfspr(SPRN_SPEFSCR); +	fb = (speinsn >> 11) & 0x1f; +	switch (func) { +	case EFSCTUIZ: +	case EFSCTSIZ: +	case EVFSCTUIZ: +	case EVFSCTSIZ: +	case EFDCTUIDZ: +	case EFDCTSIDZ: +	case EFDCTUIZ: +	case EFDCTSIZ: +		/* +		 * These instructions always round to zero, +		 * independent of the rounding mode. +		 */ +		return 0; + +	case EFSCTUI: +	case EFSCTUF: +	case EVFSCTUI: +	case EVFSCTUF: +	case EFDCTUI: +	case EFDCTUF: +		fp_result = 0; +		s_lo = 0; +		s_hi = 0; +		break; + +	case EFSCTSI: +	case EFSCTSF: +		fp_result = 0; +		/* Recover the sign of a zero result if possible.  */ +		if (fgpr.wp[1] == 0) +			s_lo = regs->gpr[fb] & SIGN_BIT_S; +		break; + +	case EVFSCTSI: +	case EVFSCTSF: +		fp_result = 0; +		/* Recover the sign of a zero result if possible.  */ +		if (fgpr.wp[1] == 0) +			s_lo = regs->gpr[fb] & SIGN_BIT_S; +		if (fgpr.wp[0] == 0) +			s_hi = current->thread.evr[fb] & SIGN_BIT_S; +		break; + +	case EFDCTSI: +	case EFDCTSF: +		fp_result = 0; +		s_hi = s_lo; +		/* Recover the sign of a zero result if possible.  */ +		if (fgpr.wp[1] == 0) +			s_hi = current->thread.evr[fb] & SIGN_BIT_S; +		break; -	switch ((speinsn >> 5) & 0x7) { +	default: +		fp_result = 1; +		break; +	} + +	pr_debug("round fgpr: %08x  %08x\n", fgpr.wp[0], fgpr.wp[1]); + +	switch (fptype) {  	/* Since SPE instructions on E500 core can handle round to nearest  	 * and round toward zero with IEEE-754 complied, we just need  	 * to handle round toward +Inf and round toward -Inf by software. @@ -687,25 +827,52 @@ int speround_handler(struct pt_regs *regs)  		if ((FP_ROUNDMODE) == FP_RND_PINF) {  			if (!s_lo) fgpr.wp[1]++; /* Z > 0, choose Z1 */  		} else { /* round to -Inf */ -			if (s_lo) fgpr.wp[1]++; /* Z < 0, choose Z2 */ +			if (s_lo) { +				if (fp_result) +					fgpr.wp[1]++; /* Z < 0, choose Z2 */ +				else +					fgpr.wp[1]--; /* Z < 0, choose Z2 */ +			}  		}  		break;  	case DPFP:  		if (FP_ROUNDMODE == FP_RND_PINF) { -			if (!s_hi) fgpr.dp[0]++; /* Z > 0, choose Z1 */ +			if (!s_hi) { +				if (fp_result) +					fgpr.dp[0]++; /* Z > 0, choose Z1 */ +				else +					fgpr.wp[1]++; /* Z > 0, choose Z1 */ +			}  		} else { /* round to -Inf */ -			if (s_hi) fgpr.dp[0]++; /* Z < 0, choose Z2 */ +			if (s_hi) { +				if (fp_result) +					fgpr.dp[0]++; /* Z < 0, choose Z2 */ +				else +					fgpr.wp[1]--; /* Z < 0, choose Z2 */ +			}  		}  		break;  	case VCT:  		if (FP_ROUNDMODE == FP_RND_PINF) { -			if (!s_lo) fgpr.wp[1]++; /* Z_low > 0, choose Z1 */ -			if (!s_hi) fgpr.wp[0]++; /* Z_high word > 0, choose Z1 */ +			if (lo_inexact && !s_lo) +				fgpr.wp[1]++; /* Z_low > 0, choose Z1 */ +			if (hi_inexact && !s_hi) +				fgpr.wp[0]++; /* Z_high word > 0, choose Z1 */  		} else { /* round to -Inf */ -			if (s_lo) fgpr.wp[1]++; /* Z_low < 0, choose Z2 */ -			if (s_hi) fgpr.wp[0]++; /* Z_high < 0, choose Z2 */ +			if (lo_inexact && s_lo) { +				if (fp_result) +					fgpr.wp[1]++; /* Z_low < 0, choose Z2 */ +				else +					fgpr.wp[1]--; /* Z_low < 0, choose Z2 */ +			} +			if (hi_inexact && s_hi) { +				if (fp_result) +					fgpr.wp[0]++; /* Z_high < 0, choose Z2 */ +				else +					fgpr.wp[0]--; /* Z_high < 0, choose Z2 */ +			}  		}  		break; @@ -716,5 +883,49 @@ int speround_handler(struct pt_regs *regs)  	current->thread.evr[fc] = fgpr.wp[0];  	regs->gpr[fc] = fgpr.wp[1]; +	pr_debug("  to fgpr: %08x  %08x\n", fgpr.wp[0], fgpr.wp[1]); + +	if (current->thread.fpexc_mode & PR_FP_EXC_SW_ENABLE) +		return (current->thread.fpexc_mode & PR_FP_EXC_RES) ? 1 : 0; +	return 0; +} + +int __init spe_mathemu_init(void) +{ +	u32 pvr, maj, min; + +	pvr = mfspr(SPRN_PVR); + +	if ((PVR_VER(pvr) == PVR_VER_E500V1) || +	    (PVR_VER(pvr) == PVR_VER_E500V2)) { +		maj = PVR_MAJ(pvr); +		min = PVR_MIN(pvr); + +		/* +		 * E500 revision below 1.1, 2.3, 3.1, 4.1, 5.1 +		 * need cpu a005 errata workaround +		 */ +		switch (maj) { +		case 1: +			if (min < 1) +				have_e500_cpu_a005_erratum = 1; +			break; +		case 2: +			if (min < 3) +				have_e500_cpu_a005_erratum = 1; +			break; +		case 3: +		case 4: +		case 5: +			if (min < 1) +				have_e500_cpu_a005_erratum = 1; +			break; +		default: +			break; +		} +	} +  	return 0;  } + +module_init(spe_mathemu_init); diff --git a/arch/powerpc/math-emu/mtfsf.c b/arch/powerpc/math-emu/mtfsf.c index dbce92e4f04..44b0fc8214f 100644 --- a/arch/powerpc/math-emu/mtfsf.c +++ b/arch/powerpc/math-emu/mtfsf.c @@ -11,48 +11,36 @@ mtfsf(unsigned int FM, u32 *frB)  	u32 mask;  	u32 fpscr; -	if (FM == 0) -		return 0; - -	if (FM == 0xff) -		mask = 0x9fffffff; +	if (likely(FM == 1)) +		mask = 0x0f; +	else if (likely(FM == 0xff)) +		mask = ~0;  	else { -		mask = 0; -		if (FM & (1 << 0)) -			mask |= 0x90000000; -		if (FM & (1 << 1)) -			mask |= 0x0f000000; -		if (FM & (1 << 2)) -			mask |= 0x00f00000; -		if (FM & (1 << 3)) -			mask |= 0x000f0000; -		if (FM & (1 << 4)) -			mask |= 0x0000f000; -		if (FM & (1 << 5)) -			mask |= 0x00000f00; -		if (FM & (1 << 6)) -			mask |= 0x000000f0; -		if (FM & (1 << 7)) -			mask |= 0x0000000f; +		mask = ((FM & 1) | +				((FM << 3) & 0x10) | +				((FM << 6) & 0x100) | +				((FM << 9) & 0x1000) | +				((FM << 12) & 0x10000) | +				((FM << 15) & 0x100000) | +				((FM << 18) & 0x1000000) | +				((FM << 21) & 0x10000000)) * 15;  	} -	__FPU_FPSCR &= ~(mask); -	__FPU_FPSCR |= (frB[1] & mask); +	fpscr = ((__FPU_FPSCR & ~mask) | (frB[1] & mask)) & +		~(FPSCR_VX | FPSCR_FEX | 0x800); -	__FPU_FPSCR &= ~(FPSCR_VX); -	if (__FPU_FPSCR & (FPSCR_VXSNAN | FPSCR_VXISI | FPSCR_VXIDI | +	if (fpscr & (FPSCR_VXSNAN | FPSCR_VXISI | FPSCR_VXIDI |  		     FPSCR_VXZDZ | FPSCR_VXIMZ | FPSCR_VXVC |  		     FPSCR_VXSOFT | FPSCR_VXSQRT | FPSCR_VXCVI)) -		__FPU_FPSCR |= FPSCR_VX; - -	fpscr = __FPU_FPSCR; -	fpscr &= ~(FPSCR_FEX); -	if (((fpscr & FPSCR_VX) && (fpscr & FPSCR_VE)) || -	    ((fpscr & FPSCR_OX) && (fpscr & FPSCR_OE)) || -	    ((fpscr & FPSCR_UX) && (fpscr & FPSCR_UE)) || -	    ((fpscr & FPSCR_ZX) && (fpscr & FPSCR_ZE)) || -	    ((fpscr & FPSCR_XX) && (fpscr & FPSCR_XE))) +		fpscr |= FPSCR_VX; + +	/* The bit order of exception enables and exception status +	 * is the same. Simply shift and mask to check for enabled +	 * exceptions. +	 */ +	if (fpscr & (fpscr >> 22) &  0xf8)  		fpscr |= FPSCR_FEX; +  	__FPU_FPSCR = fpscr;  #ifdef DEBUG  | 
