⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cp1emu.c

📁 LINUX 2.6.17.4的源码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * cp1emu.c: a MIPS coprocessor 1 (fpu) instruction emulator * * MIPS floating point support * Copyright (C) 1994-2000 Algorithmics Ltd. * http://www.algor.co.uk * * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com * Copyright (C) 2000  MIPS Technologies, Inc. * *  This program is free software; you can distribute it and/or modify it *  under the terms of the GNU General Public License (Version 2) as *  published by the Free Software Foundation. * *  This program is distributed in the hope it will be useful, but WITHOUT *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License *  for more details. * *  You should have received a copy of the GNU General Public License along *  with this program; if not, write to the Free Software Foundation, Inc., *  59 Temple Place - Suite 330, Boston MA 02111-1307, USA. * * A complete emulator for MIPS coprocessor 1 instructions.  This is * required for #float(switch) or #float(trap), where it catches all * COP1 instructions via the "CoProcessor Unusable" exception. * * More surprisingly it is also required for #float(ieee), to help out * the hardware fpu at the boundaries of the IEEE-754 representation * (denormalised values, infinities, underflow, etc).  It is made * quite nasty because emulation of some non-COP1 instructions is * required, e.g. in branch delay slots. * * Note if you know that you won't have an fpu, then you'll get much * better performance by compiling with -msoft-float! */#include <linux/sched.h>#include <asm/inst.h>#include <asm/bootinfo.h>#include <asm/cpu.h>#include <asm/cpu-features.h>#include <asm/processor.h>#include <asm/ptrace.h>#include <asm/signal.h>#include <asm/mipsregs.h>#include <asm/fpu_emulator.h>#include <asm/uaccess.h>#include <asm/branch.h>#include "ieee754.h"#include "dsemul.h"/* Strap kernel emulator for full MIPS IV emulation */#ifdef __mips#undef __mips#endif#define __mips 4/* Function which emulates a floating point instruction. */static int fpu_emu(struct pt_regs *, struct mips_fpu_soft_struct *,	mips_instruction);#if __mips >= 4 && __mips != 32static int fpux_emu(struct pt_regs *,	struct mips_fpu_soft_struct *, mips_instruction);#endif/* Further private data for which no space exists in mips_fpu_soft_struct */struct mips_fpu_emulator_stats fpuemustats;/* Control registers */#define FPCREG_RID	0	/* $0  = revision id */#define FPCREG_CSR	31	/* $31 = csr *//* Convert Mips rounding mode (0..3) to IEEE library modes. */static const unsigned char ieee_rm[4] = {	[FPU_CSR_RN] = IEEE754_RN,	[FPU_CSR_RZ] = IEEE754_RZ,	[FPU_CSR_RU] = IEEE754_RU,	[FPU_CSR_RD] = IEEE754_RD,};/* Convert IEEE library modes to Mips rounding mode (0..3). */static const unsigned char mips_rm[4] = {	[IEEE754_RN] = FPU_CSR_RN,	[IEEE754_RZ] = FPU_CSR_RZ,	[IEEE754_RD] = FPU_CSR_RD,	[IEEE754_RU] = FPU_CSR_RU,};#if __mips >= 4/* convert condition code register number to csr bit */static const unsigned int fpucondbit[8] = {	FPU_CSR_COND0,	FPU_CSR_COND1,	FPU_CSR_COND2,	FPU_CSR_COND3,	FPU_CSR_COND4,	FPU_CSR_COND5,	FPU_CSR_COND6,	FPU_CSR_COND7};#endif/* * Redundant with logic already in kernel/branch.c, * embedded in compute_return_epc.  At some point, * a single subroutine should be used across both * modules. */static int isBranchInstr(mips_instruction * i){	switch (MIPSInst_OPCODE(*i)) {	case spec_op:		switch (MIPSInst_FUNC(*i)) {		case jalr_op:		case jr_op:			return 1;		}		break;	case bcond_op:		switch (MIPSInst_RT(*i)) {		case bltz_op:		case bgez_op:		case bltzl_op:		case bgezl_op:		case bltzal_op:		case bgezal_op:		case bltzall_op:		case bgezall_op:			return 1;		}		break;	case j_op:	case jal_op:	case jalx_op:	case beq_op:	case bne_op:	case blez_op:	case bgtz_op:	case beql_op:	case bnel_op:	case blezl_op:	case bgtzl_op:		return 1;	case cop0_op:	case cop1_op:	case cop2_op:	case cop1x_op:		if (MIPSInst_RS(*i) == bc_op)			return 1;		break;	}	return 0;}/* * In the Linux kernel, we support selection of FPR format on the * basis of the Status.FR bit.  This does imply that, if a full 32 * FPRs are desired, there needs to be a flip-flop that can be written * to one at that bit position.  In any case, O32 MIPS ABI uses * only the even FPRs (Status.FR = 0). */#define CP0_STATUS_FR_SUPPORT#ifdef CP0_STATUS_FR_SUPPORT#define FR_BIT ST0_FR#else#define FR_BIT 0#endif#define SIFROMREG(si,x)	((si) = \			(xcp->cp0_status & FR_BIT) || !(x & 1) ? \			(int)ctx->fpr[x] : \			(int)(ctx->fpr[x & ~1] >> 32 ))#define SITOREG(si,x)	(ctx->fpr[x & ~((xcp->cp0_status & FR_BIT) == 0)] = \			(xcp->cp0_status & FR_BIT) || !(x & 1) ? \			ctx->fpr[x & ~1] >> 32 << 32 | (u32)(si) : \			ctx->fpr[x & ~1] << 32 >> 32 | (u64)(si) << 32)#define DIFROMREG(di,x)	((di) = \			ctx->fpr[x & ~((xcp->cp0_status & FR_BIT) == 0)])#define DITOREG(di,x)	(ctx->fpr[x & ~((xcp->cp0_status & FR_BIT) == 0)] \			= (di))#define SPFROMREG(sp,x)	SIFROMREG((sp).bits,x)#define SPTOREG(sp,x)	SITOREG((sp).bits,x)#define DPFROMREG(dp,x)	DIFROMREG((dp).bits,x)#define DPTOREG(dp,x)	DITOREG((dp).bits,x)/* * Emulate the single floating point instruction pointed at by EPC. * Two instructions if the instruction is in a branch delay slot. */static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_soft_struct *ctx){	mips_instruction ir;	void * emulpc, *contpc;	unsigned int cond;	if (get_user(ir, (mips_instruction __user *) xcp->cp0_epc)) {		fpuemustats.errors++;		return SIGBUS;	}	/* XXX NEC Vr54xx bug workaround */	if ((xcp->cp0_cause & CAUSEF_BD) && !isBranchInstr(&ir))		xcp->cp0_cause &= ~CAUSEF_BD;	if (xcp->cp0_cause & CAUSEF_BD) {		/*		 * The instruction to be emulated is in a branch delay slot		 * which means that we have to  emulate the branch instruction		 * BEFORE we do the cop1 instruction.		 *		 * This branch could be a COP1 branch, but in that case we		 * would have had a trap for that instruction, and would not		 * come through this route.		 *		 * Linux MIPS branch emulator operates on context, updating the		 * cp0_epc.		 */		emulpc = (void *) (xcp->cp0_epc + 4);	/* Snapshot emulation target */		if (__compute_return_epc(xcp)) {#ifdef CP1DBG			printk("failed to emulate branch at %p\n",				(void *) (xcp->cp0_epc));#endif			return SIGILL;		}		if (get_user(ir, (mips_instruction __user *) emulpc)) {			fpuemustats.errors++;			return SIGBUS;		}		/* __compute_return_epc() will have updated cp0_epc */		contpc = (void *)  xcp->cp0_epc;		/* In order not to confuse ptrace() et al, tweak context */		xcp->cp0_epc = (unsigned long) emulpc - 4;	} else {		emulpc = (void *)  xcp->cp0_epc;		contpc = (void *) (xcp->cp0_epc + 4);	}      emul:	fpuemustats.emulated++;	switch (MIPSInst_OPCODE(ir)) {	case ldc1_op:{		u64 __user *va = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] +			MIPSInst_SIMM(ir));		u64 val;		fpuemustats.loads++;		if (get_user(val, va)) {			fpuemustats.errors++;			return SIGBUS;		}		DITOREG(val, MIPSInst_RT(ir));		break;	}	case sdc1_op:{		u64 __user *va = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] +			MIPSInst_SIMM(ir));		u64 val;		fpuemustats.stores++;		DIFROMREG(val, MIPSInst_RT(ir));		if (put_user(val, va)) {			fpuemustats.errors++;			return SIGBUS;		}		break;	}	case lwc1_op:{		u32 __user *va = (u32 __user *) (xcp->regs[MIPSInst_RS(ir)] +			MIPSInst_SIMM(ir));		u32 val;		fpuemustats.loads++;		if (get_user(val, va)) {			fpuemustats.errors++;			return SIGBUS;		}		SITOREG(val, MIPSInst_RT(ir));		break;	}	case swc1_op:{		u32 __user *va = (u32 __user *) (xcp->regs[MIPSInst_RS(ir)] +			MIPSInst_SIMM(ir));		u32 val;		fpuemustats.stores++;		SIFROMREG(val, MIPSInst_RT(ir));		if (put_user(val, va)) {			fpuemustats.errors++;			return SIGBUS;		}		break;	}	case cop1_op:		switch (MIPSInst_RS(ir)) {#if defined(__mips64)		case dmfc_op:			/* copregister fs -> gpr[rt] */			if (MIPSInst_RT(ir) != 0) {				DIFROMREG(xcp->regs[MIPSInst_RT(ir)],					MIPSInst_RD(ir));			}			break;		case dmtc_op:			/* copregister fs <- rt */			DITOREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir));			break;#endif		case mfc_op:			/* copregister rd -> gpr[rt] */			if (MIPSInst_RT(ir) != 0) {				SIFROMREG(xcp->regs[MIPSInst_RT(ir)],					MIPSInst_RD(ir));			}			break;		case mtc_op:			/* copregister rd <- rt */			SITOREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir));			break;		case cfc_op:{			/* cop control register rd -> gpr[rt] */			u32 value;			if (ir == CP1UNDEF) {				return do_dsemulret(xcp);			}			if (MIPSInst_RD(ir) == FPCREG_CSR) {				value = ctx->fcr31;				value = (value & ~0x3) | mips_rm[value & 0x3];#ifdef CSRTRACE				printk("%p gpr[%d]<-csr=%08x\n",					(void *) (xcp->cp0_epc),					MIPSInst_RT(ir), value);#endif			}			else if (MIPSInst_RD(ir) == FPCREG_RID)				value = 0;			else				value = 0;			if (MIPSInst_RT(ir))				xcp->regs[MIPSInst_RT(ir)] = value;			break;		}		case ctc_op:{			/* copregister rd <- rt */			u32 value;			if (MIPSInst_RT(ir) == 0)				value = 0;			else				value = xcp->regs[MIPSInst_RT(ir)];			/* we only have one writable control reg			 */			if (MIPSInst_RD(ir) == FPCREG_CSR) {#ifdef CSRTRACE				printk("%p gpr[%d]->csr=%08x\n",					(void *) (xcp->cp0_epc),					MIPSInst_RT(ir), value);#endif				value &= (FPU_CSR_FLUSH | FPU_CSR_ALL_E | FPU_CSR_ALL_S | 0x03);				ctx->fcr31 &= ~(FPU_CSR_FLUSH | FPU_CSR_ALL_E | FPU_CSR_ALL_S | 0x03);				/* convert to ieee library modes */				ctx->fcr31 |= (value & ~0x3) | ieee_rm[value & 0x3];			}			if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) {				return SIGFPE;			}			break;		}		case bc_op:{			int likely = 0;			if (xcp->cp0_cause & CAUSEF_BD)				return SIGILL;#if __mips >= 4			cond = ctx->fcr31 & fpucondbit[MIPSInst_RT(ir) >> 2];#else			cond = ctx->fcr31 & FPU_CSR_COND;#endif			switch (MIPSInst_RT(ir) & 3) {			case bcfl_op:				likely = 1;			case bcf_op:				cond = !cond;				break;			case bctl_op:				likely = 1;			case bct_op:				break;			default:				/* thats an illegal instruction */				return SIGILL;			}			xcp->cp0_cause |= CAUSEF_BD;			if (cond) {				/* branch taken: emulate dslot				 * instruction				 */				xcp->cp0_epc += 4;				contpc = (void *)					(xcp->cp0_epc +					(MIPSInst_SIMM(ir) << 2));				if (get_user(ir,				    (mips_instruction __user *) xcp->cp0_epc)) {					fpuemustats.errors++;					return SIGBUS;				}				switch (MIPSInst_OPCODE(ir)) {				case lwc1_op:				case swc1_op:#if (__mips >= 2 || defined(__mips64))				case ldc1_op:				case sdc1_op:#endif				case cop1_op:#if __mips >= 4 && __mips != 32				case cop1x_op:#endif					/* its one of ours */					goto emul;#if __mips >= 4				case spec_op:					if (MIPSInst_FUNC(ir) == movc_op)						goto emul;					break;#endif				}				/*				 * Single step the non-cp1				 * instruction in the dslot				 */				return mips_dsemul(xcp, ir, (unsigned long) contpc);			}			else {				/* branch not taken */				if (likely) {					/*					 * branch likely nullifies					 * dslot if not taken					 */					xcp->cp0_epc += 4;					contpc += 4;					/*					 * else continue & execute					 * dslot as normal insn					 */				}			}			break;		}		default:			if (!(MIPSInst_RS(ir) & 0x10))				return SIGILL;			{				int sig;				/* a real fpu computation instruction */				if ((sig = fpu_emu(xcp, ctx, ir)))					return sig;			}		}		break;#if __mips >= 4 && __mips != 32	case cop1x_op:{		int sig;		if ((sig = fpux_emu(xcp, ctx, ir)))			return sig;		break;	}#endif#if __mips >= 4	case spec_op:		if (MIPSInst_FUNC(ir) != movc_op)			return SIGILL;		cond = fpucondbit[MIPSInst_RT(ir) >> 2];		if (((ctx->fcr31 & cond) != 0) == ((MIPSInst_RT(ir) & 1) != 0))			xcp->regs[MIPSInst_RD(ir)] =				xcp->regs[MIPSInst_RS(ir)];		break;#endif	default:		return SIGILL;	}	/* we did it !! */	xcp->cp0_epc = (unsigned long) contpc;	xcp->cp0_cause &= ~CAUSEF_BD;	return 0;}/* * Conversion table from MIPS compare ops 48-63 * cond = ieee754dp_cmp(x,y,IEEE754_UN,sig); */static const unsigned char cmptab[8] = {	0,			/* cmp_0 (sig) cmp_sf */	IEEE754_CUN,		/* cmp_un (sig) cmp_ngle */	IEEE754_CEQ,		/* cmp_eq (sig) cmp_seq */	IEEE754_CEQ | IEEE754_CUN,	/* cmp_ueq (sig) cmp_ngl  */	IEEE754_CLT,		/* cmp_olt (sig) cmp_lt */	IEEE754_CLT | IEEE754_CUN,	/* cmp_ult (sig) cmp_nge */	IEEE754_CLT | IEEE754_CEQ,	/* cmp_ole (sig) cmp_le */	IEEE754_CLT | IEEE754_CEQ | IEEE754_CUN,	/* cmp_ule (sig) cmp_ngt */};#if __mips >= 4 && __mips != 32/* * Additional MIPS4 instructions */#define DEF3OP(name, p, f1, f2, f3) \static ieee754##p fpemu_##p##_##name (ieee754##p r, ieee754##p s, \    ieee754##p t) \{ \	struct _ieee754_csr ieee754_csr_save; \	s = f1 (s, t); \	ieee754_csr_save = ieee754_csr; \	s = f2 (s, r); \	ieee754_csr_save.cx |= ieee754_csr.cx; \	ieee754_csr_save.sx |= ieee754_csr.sx; \	s = f3 (s); \	ieee754_csr.cx |= ieee754_csr_save.cx; \	ieee754_csr.sx |= ieee754_csr_save.sx; \	return s; \}static ieee754dp fpemu_dp_recip(ieee754dp d){	return ieee754dp_div(ieee754dp_one(0), d);}static ieee754dp fpemu_dp_rsqrt(ieee754dp d){	return ieee754dp_div(ieee754dp_one(0), ieee754dp_sqrt(d));}static ieee754sp fpemu_sp_recip(ieee754sp s){	return ieee754sp_div(ieee754sp_one(0), s);}static ieee754sp fpemu_sp_rsqrt(ieee754sp s){	return ieee754sp_div(ieee754sp_one(0), ieee754sp_sqrt(s));}DEF3OP(madd, sp, ieee754sp_mul, ieee754sp_add,);DEF3OP(msub, sp, ieee754sp_mul, ieee754sp_sub,);DEF3OP(nmadd, sp, ieee754sp_mul, ieee754sp_add, ieee754sp_neg);DEF3OP(nmsub, sp, ieee754sp_mul, ieee754sp_sub, ieee754sp_neg);DEF3OP(madd, dp, ieee754dp_mul, ieee754dp_add,);DEF3OP(msub, dp, ieee754dp_mul, ieee754dp_sub,);DEF3OP(nmadd, dp, ieee754dp_mul, ieee754dp_add, ieee754dp_neg);DEF3OP(nmsub, dp, ieee754dp_mul, ieee754dp_sub, ieee754dp_neg);static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_soft_struct *ctx,	mips_instruction ir){	unsigned rcsr = 0;	/* resulting csr */	fpuemustats.cp1xops++;	switch (MIPSInst_FMA_FFMT(ir)) {	case s_fmt:{		/* 0 */		ieee754sp(*handler) (ieee754sp, ieee754sp, ieee754sp);		ieee754sp fd, fr, fs, ft;		u32 __user *va;		u32 val;		switch (MIPSInst_FUNC(ir)) {		case lwxc1_op:			va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +				xcp->regs[MIPSInst_FT(ir)]);			fpuemustats.loads++;			if (get_user(val, va)) {				fpuemustats.errors++;				return SIGBUS;			}			SITOREG(val, MIPSInst_FD(ir));			break;		case swxc1_op:			va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +				xcp->regs[MIPSInst_FT(ir)]);			fpuemustats.stores++;			SIFROMREG(val, MIPSInst_FS(ir));			if (put_user(val, va)) {				fpuemustats.errors++;				return SIGBUS;			}			break;		case madd_s_op:			handler = fpemu_sp_madd;			goto scoptop;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -