/*
 * 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;
}
Beispiel #2
0
static int
cop1Emulate(int xcptno, struct pt_regs *xcp,
	    struct mips_fpu_soft_struct *ctx)
{
	mips_instruction ir;
	vaddr_t emulpc;
	vaddr_t contpc;
	unsigned int cond;
	int err = 0;


	ir = mips_get_word(xcp, REG_TO_VA xcp->cp0_epc, &err);
	if (err) {
		fpuemuprivate.stats.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 = REG_TO_VA(xcp->cp0_epc + 4);	/* Snapshot emulation target */

		if (__compute_return_epc(xcp)) {
#ifdef CP1DBG
			printk("failed to emulate branch at %p\n",
				    REG_TO_VA(xcp->cp0_epc));
#endif
			return SIGILL;;
		}
		ir = mips_get_word(xcp, emulpc, &err);
		if (err) {
			fpuemuprivate.stats.errors++;
			return SIGBUS;
		}
		contpc = REG_TO_VA xcp->cp0_epc;
	} else {
		emulpc = REG_TO_VA xcp->cp0_epc;
		contpc = REG_TO_VA xcp->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:
		{
			void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)])
			    + MIPSInst_SIMM(ir);
			int ft = MIPSInst_RT(ir);
			if (!(xcp->cp0_status & ST0_FR))
				ft &= ~1;
			ctx->regs[ft] = mips_get_dword(xcp, va, &err);
			fpuemuprivate.stats.loads++;
			if (err) {
				fpuemuprivate.stats.errors++;
				return SIGBUS;
			}
		}
		break;

	case sdc1_op:
		{
			void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)])
			    + MIPSInst_SIMM(ir);
			int ft = MIPSInst_RT(ir);
			if (!(xcp->cp0_status & ST0_FR))
				ft &= ~1;
			fpuemuprivate.stats.stores++;
			if (mips_put_dword(xcp, va, ctx->regs[ft])) {
				fpuemuprivate.stats.errors++;
				return SIGBUS;
			}
		}
		break;
#endif

	case lwc1_op:
		{
			void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)])
			    + MIPSInst_SIMM(ir);
			fpureg_t val;
			int ft = MIPSInst_RT(ir);
			fpuemuprivate.stats.loads++;
			val = mips_get_word(xcp, va, &err);
			if (err) {
				fpuemuprivate.stats.errors++;
				return SIGBUS;
			}
			if (xcp->cp0_status & ST0_FR) {
				/* load whole register */
				ctx->regs[ft] = 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)] |= val << 32;
#endif
			} else {
				/* load to l.s. 32 bits */
				ctx->regs[ft] &= ~0xffffffffLL;
				ctx->regs[ft] |= val;
			}
		}
		break;

	case swc1_op:
		{
			void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)])
			    + MIPSInst_SIMM(ir);
			unsigned int val;
			int ft = MIPSInst_RT(ir);
			fpuemuprivate.stats.stores++;
			if (xcp->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 (mips_put_word(xcp, va, val)) {
				fpuemuprivate.stats.errors++;
				return SIGBUS;
			}
		}
		break;
#else				/* old 32-bit fpu registers */
	case lwc1_op:
		{
			void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)])
			    + MIPSInst_SIMM(ir);
			ctx->regs[MIPSInst_RT(ir)] =
			    mips_get_word(xcp, va, &err);
			fpuemuprivate.stats.loads++;
			if (err) {
				fpuemuprivate.stats.errors++;
				return SIGBUS;
			}
		}
		break;

	case swc1_op:
		{
			void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)])
			    + MIPSInst_SIMM(ir);
			fpuemuprivate.stats.stores++;
			if (mips_put_word
			    (xcp, va, ctx->regs[MIPSInst_RT(ir)])) {
				fpuemuprivate.stats.errors++;
				return SIGBUS;
			}
		}
		break;
	case ldc1_op:
		{
			void *va = REG_TO_VA(xcp->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__)
			ctx->regs[rt + 1] =
			    mips_get_word(xcp, va + 0, &err);
			errs += err;
			ctx->regs[rt + 0] =
			    mips_get_word(xcp, va + 4, &err);
			errs += err;
#else
			ctx->regs[rt + 0] =
			    mips_get_word(xcp, va + 0, &err);
			errs += err;
			ctx->regs[rt + 1] =
			    mips_get_word(xcp, va + 4, &err);
			errs += err;
#endif
			if (err)
				return SIGBUS;
		}
		break;

	case sdc1_op:
		{
			void *va = REG_TO_VA(xcp->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 (mips_put_word(xcp, va + 0, ctx->regs[rt + 1]))
				return SIGBUS;
			if (mips_put_word(xcp, va + 4, ctx->regs[rt + 0]))
				return SIGBUS;
#else
			if (mips_put_word(xcp, va + 0, ctx->regs[rt + 0]))
				return SIGBUS;
			if (mips_put_word(xcp, va + 4, ctx->regs[rt + 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 (!(xcp->cp0_status & ST0_FR))
					fs &= ~1;
				xcp->regs[MIPSInst_RT(ir)] = ctx->regs[fs];
			}
			break;

		case dmtc_op:
			/* copregister fs <- rt */
			{
				fpureg_t value;
				int fs = MIPSInst_RD(ir);
				if (!(xcp->cp0_status & ST0_FR))
					fs &= ~1;
				value =
				    (MIPSInst_RT(ir) ==
				     0) ? 0 : xcp->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 (!(xcp->cp0_status & ST0_FR)) {
						/* move from m.s. 32 bits */
						value =
						    ctx->
						    regs[MIPSInst_RD(ir) &
							 ~1] >> 32;
					}
#endif
				}
				xcp->regs[MIPSInst_RT(ir)] = value;
			}