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

📄 cp1emu.c

📁 microwindows移植到S3C44B0的源码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * cp1emu.c: a MIPS coprocessor 1 (fpu) instruction emulator *  * MIPS floating point support * Copyright (C) 1994-2000 Algorithmics Ltd.  All rights reserved. * 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/compiler.h>#include <linux/mm.h>#include <linux/signal.h>#include <linux/smp.h>#include <linux/smp_lock.h>#include <asm/asm.h>#include <asm/branch.h>#include <asm/bootinfo.h>#include <asm/byteorder.h>#include <asm/cpu.h>#include <asm/inst.h>#include <asm/uaccess.h>#include <asm/processor.h>#include <asm/mipsregs.h>#include <asm/system.h>#include <asm/pgtable.h>#include <asm/fpu_emulator.h>#include "ieee754.h"/* Strap kernel emulator for full MIPS IV emulation */#ifdef __mips#undef __mips#endif#define __mips 4typedef void *vaddr_t;/* Function which emulates the instruction in a branch delay slot. */static int mips_dsemul(struct pt_regs *, mips_instruction, unsigned long);/* 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_private fpuemuprivate;/* 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] = {	IEEE754_RN, IEEE754_RZ, IEEE754_RU, IEEE754_RD};#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;}#define REG_TO_VA (vaddr_t)#define VA_TO_REG (unsigned long)/* * 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, normal MIPS ABI uses * only the even FPRs (Status.FR = 0). */#define CP0_STATUS_FR_SUPPORT/* * 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 *regs, struct mips_fpu_soft_struct *ctx){	vaddr_t emulpc, contpc;	mips_instruction ir;	unsigned int cond;	int err = 0;	err = get_user(ir, (mips_instruction *) regs->cp0_epc);	if (err) {		fpuemuprivate.stats.errors++;		return SIGBUS;	}	/* XXX NEC Vr54xx bug workaround */	if ((regs->cp0_cause & CAUSEF_BD) && !isBranchInstr(&ir))		regs->cp0_cause &= ~CAUSEF_BD;	if (regs->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 = REG_TO_VA(regs->cp0_epc + 4);	/* Snapshot emulation target */		if (__compute_return_epc(regs)) {#ifdef CP1DBG			printk("failed to emulate branch at %p\n",				    REG_TO_VA(regs->cp0_epc));#endif			return SIGILL;		}		err = get_user(ir, (mips_instruction *) emulpc);		if (err) {			fpuemuprivate.stats.errors++;			return SIGBUS;		}		/* __computer_return_epc() will have updated cp0_epc */		contpc = REG_TO_VA regs->cp0_epc;		/* In order not to confuse ptrace() et al, tweak context */		regs->cp0_epc = VA_TO_REG emulpc - 4;	} else {		emulpc = REG_TO_VA regs->cp0_epc;		contpc = REG_TO_VA regs->cp0_epc + 4;	}emul:	fpuemuprivate.stats.emulated++;	switch (MIPSInst_OPCODE(ir)) {#ifdef CP0_STATUS_FR_SUPPORT		/* R4000+ 64-bit fpu registers */#ifndef SINGLE_ONLY_FPU	case ldc1_op:		{			u64 *va = REG_TO_VA(regs->regs[MIPSInst_RS(ir)]) +			          MIPSInst_SIMM(ir);			int ft = MIPSInst_RT(ir);			if (!(regs->cp0_status & ST0_FR))				ft &= ~1;			err = get_user(ctx->regs[ft], va);			fpuemuprivate.stats.loads++;			if (err) {				fpuemuprivate.stats.errors++;				return SIGBUS;			}		}		break;	case sdc1_op:		{			fpureg_t *va = REG_TO_VA(regs->regs[MIPSInst_RS(ir)]) +			               MIPSInst_SIMM(ir);			int ft = MIPSInst_RT(ir);			if (!(regs->cp0_status & ST0_FR))				ft &= ~1;			fpuemuprivate.stats.stores++;			if (put_user(ctx->regs[ft], va)) {				fpuemuprivate.stats.errors++;				return SIGBUS;			}		}		break;#endif	case lwc1_op:		{			u32 *va = REG_TO_VA(regs->regs[MIPSInst_RS(ir)]) +			               MIPSInst_SIMM(ir);			int ft = MIPSInst_RT(ir);			u32 val;			fpuemuprivate.stats.loads++;			err = get_user(val, va);			if (err) {				fpuemuprivate.stats.errors++;				return SIGBUS;			}			if (regs->cp0_status & ST0_FR) {				/* load whole register */				ctx->regs[ft] = (s64) val;			} else if (ft & 1) {				/* load to m.s. 32 bits */#ifdef SINGLE_ONLY_FPU				/* illegal register in single-float mode */				return SIGILL;#else				ctx->regs[(ft & ~1)] &= 0xffffffff;				ctx->regs[(ft & ~1)] |= (fpureg_t) val << 32;#endif			} else {				/* load to l.s. 32 bits */				ctx->regs[ft] &= ~0xffffffffLL;				ctx->regs[ft] |= val;			}		}		break;	case swc1_op:		{			u32 *va = REG_TO_VA(regs->regs[MIPSInst_RS(ir)]) +			          MIPSInst_SIMM(ir);			unsigned int val;			int ft = MIPSInst_RT(ir);			fpuemuprivate.stats.stores++;			if (regs->cp0_status & ST0_FR) {				/* store whole register */				val = ctx->regs[ft];			} else if (ft & 1) {#ifdef SINGLE_ONLY_FPU				/* illegal register in single-float mode */				return SIGILL;#else				/* store from m.s. 32 bits */				val = ctx->regs[(ft & ~1)] >> 32;#endif			} else {				/* store from l.s. 32 bits */				val = ctx->regs[ft];			}			if (put_user(val, va)) {				fpuemuprivate.stats.errors++;				return SIGBUS;			}		}		break;#else				/* old 32-bit fpu registers */	case lwc1_op:		{			u32 *va = REG_TO_VA(regs->regs[MIPSInst_RS(ir)]) +			          MIPSInst_SIMM(ir);			err = get_user(ctx->regs[MIPSInst_RT(ir)], va);			fpuemuprivate.stats.loads++;			if (err) {				fpuemuprivate.stats.errors++;				return SIGBUS;			}		}		break;	case swc1_op:		{			u32 *va = REG_TO_VA(regs->regs[MIPSInst_RS(ir)]) +			          MIPSInst_SIMM(ir);			fpuemuprivate.stats.stores++;			if (put_user(ctx->regs[MIPSInst_RT(ir)], va)) {				fpuemuprivate.stats.errors++;				return SIGBUS;			}		}		break;	case ldc1_op:		{			u32 *va = REG_TO_VA(regs->regs[MIPSInst_RS(ir)])			    + MIPSInst_SIMM(ir);			unsigned int rt = MIPSInst_RT(ir) & ~1;			int errs = 0;			fpuemuprivate.stats.loads++;#if (defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN) || defined(__MIPSEB__)			err = get_user(ctx->regs[rt + 1], va + 0);			err |= get_user(ctx->regs[rt + 0], va + 1);#else			err = get_user(ctx->regs[rt + 0], va + 0);			err |= get_user(ctx->regs[rt + 1], va + 1);#endif			if (err)				return SIGBUS;		}		break;	case sdc1_op:		{			u32 *va = REG_TO_VA(regs->regs[MIPSInst_RS(ir)]) +			          MIPSInst_SIMM(ir);			unsigned int rt = MIPSInst_RT(ir) & ~1;			fpuemuprivate.stats.stores++;#if (defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN) || defined(__MIPSEB__)			if (put_user(ctx->regs[rt + 1], va + 0)			    || put_user(ctx->regs[rt + 0], va + 1))				return SIGBUS;#else			if (put_user(ctx->regs[rt + 0], va + 0)			    || put_user(ctx->regs[rt + 1], va + 1))				return SIGBUS;#endif		}		break;#endif	case cop1_op:		switch (MIPSInst_RS(ir)) {#ifdef CP0_STATUS_FR_SUPPORT#if __mips64 && !defined(SINGLE_ONLY_FPU)		case dmfc_op:			/* copregister fs -> gpr[rt] */			if (MIPSInst_RT(ir) != 0) {				int fs = MIPSInst_RD(ir);				if (!(regs->cp0_status & ST0_FR))					fs &= ~1;				regs->regs[MIPSInst_RT(ir)] = ctx->regs[fs];			}			break;		case dmtc_op: {			/* copregister fs <- rt */				fpureg_t value;				int fs = MIPSInst_RD(ir);				if (!(regs->cp0_status & ST0_FR))					fs &= ~1;				value =				    (MIPSInst_RT(ir) ==				     0) ? 0 : regs->regs[MIPSInst_RT(ir)];				ctx->regs[fs] = value;			break;		}#endif		case mfc_op:			/* copregister rd -> gpr[rt] */			if (MIPSInst_RT(ir) != 0) {				/* default value from l.s. 32 bits */				int value = ctx->regs[MIPSInst_RD(ir)];				if (MIPSInst_RD(ir) & 1) {#ifdef SINGLE_ONLY_FPU					/* illegal register in single-float mode */					return SIGILL;#else					if (!(regs->cp0_status & ST0_FR)) {						/* move from m.s. 32 bits */						value =						    ctx->						    regs[MIPSInst_RD(ir) &							 ~1] >> 32;					}#endif				}				regs->regs[MIPSInst_RT(ir)] = value;			}			break;		case mtc_op:			/* copregister rd <- rt */			{				fpureg_t value;				if (MIPSInst_RT(ir) == 0)					value = 0;				else					value =					    (unsigned int) regs->					    regs[MIPSInst_RT(ir)];				if (MIPSInst_RD(ir) & 1) {#ifdef SINGLE_ONLY_FPU					/* illegal register in single-float mode */					return SIGILL;#else					if (!(regs->cp0_status & ST0_FR)) {						/* move to m.s. 32 bits */						ctx->						    regs[							 (MIPSInst_RD(ir) &							  ~1)] &=						    0xffffffff;						ctx->						    regs[							 (MIPSInst_RD(ir) &							  ~1)] |=						    value << 32;						break;					}#endif				}				/* move to l.s. 32 bits */				ctx->regs[MIPSInst_RD(ir)] &=				    ~0xffffffffLL;				ctx->regs[MIPSInst_RD(ir)] |= value;			}			break;#else		case mfc_op:			/* copregister rd -> gpr[rt] */			if (MIPSInst_RT(ir) != 0) {				unsigned value =				    ctx->regs[MIPSInst_RD(ir)];				regs->regs[MIPSInst_RT(ir)] = value;			}			break;		case mtc_op:			/* copregister rd <- rt */			{				unsigned value;				value =				    (MIPSInst_RT(ir) ==				     0) ? 0 : regs->regs[MIPSInst_RT(ir)];				ctx->regs[MIPSInst_RD(ir)] = value;			}			break;#endif		case cfc_op:			/* cop control register rd -> gpr[rt] */			{				unsigned value;				if (MIPSInst_RD(ir) == FPCREG_CSR) {					value = ctx->sr;#ifdef CSRTRACE					printk					    ("%p gpr[%d]<-csr=%08x\n",					     REG_TO_VA(regs->cp0_epc),					     MIPSInst_RT(ir), value);#endif				} else if (MIPSInst_RD(ir) == FPCREG_RID)					value = 0;				else					value = 0;				if (MIPSInst_RT(ir))					regs->regs[MIPSInst_RT(ir)] = value;			}			break;		case ctc_op:			/* copregister rd <- rt */			{				unsigned value;				if (MIPSInst_RT(ir) == 0)					value = 0;				else					value = regs->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",					     REG_TO_VA(regs->cp0_epc),					     MIPSInst_RT(ir), value);#endif					ctx->sr = value;					/* copy new rounding mode and					   flush bit to ieee library state! */					ieee754_csr.nod =					    (ctx->sr & 0x1000000) != 0;

⌨️ 快捷键说明

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