diff options
Diffstat (limited to 'arch/powerpc/math-emu/math.c')
| -rw-r--r-- | arch/powerpc/math-emu/math.c | 99 | 
1 files changed, 35 insertions, 64 deletions
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;  | 
