static int cop1Emulate()

in math-emu/cp1emu.c [971:1388]


static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
		struct mm_decoded_insn dec_insn, void __user **fault_addr)
{
	unsigned long contpc = xcp->cp0_epc + dec_insn.pc_inc;
	unsigned int cond, cbit, bit0;
	mips_instruction ir;
	int likely, pc_inc;
	union fpureg *fpr;
	u32 __user *wva;
	u64 __user *dva;
	u32 wval;
	u64 dval;
	int sig;

	/*
	 * These are giving gcc a gentle hint about what to expect in
	 * dec_inst in order to do better optimization.
	 */
	if (!cpu_has_mmips && dec_insn.micro_mips_mode)
		unreachable();

	/* XXX NEC Vr54xx bug workaround */
	if (delay_slot(xcp)) {
		if (dec_insn.micro_mips_mode) {
			if (!mm_isBranchInstr(xcp, dec_insn, &contpc))
				clear_delay_slot(xcp);
		} else {
			if (!isBranchInstr(xcp, dec_insn, &contpc))
				clear_delay_slot(xcp);
		}
	}

	if (delay_slot(xcp)) {
		/*
		 * 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.
		 */
		ir = dec_insn.next_insn;  /* process delay slot instr */
		pc_inc = dec_insn.next_pc_inc;
	} else {
		ir = dec_insn.insn;       /* process current instr */
		pc_inc = dec_insn.pc_inc;
	}

	/*
	 * Since microMIPS FPU instructios are a subset of MIPS32 FPU
	 * instructions, we want to convert microMIPS FPU instructions
	 * into MIPS32 instructions so that we could reuse all of the
	 * FPU emulation code.
	 *
	 * NOTE: We cannot do this for branch instructions since they
	 *       are not a subset. Example: Cannot emulate a 16-bit
	 *       aligned target address with a MIPS32 instruction.
	 */
	if (dec_insn.micro_mips_mode) {
		/*
		 * If next instruction is a 16-bit instruction, then it
		 * it cannot be a FPU instruction. This could happen
		 * since we can be called for non-FPU instructions.
		 */
		if ((pc_inc == 2) ||
			(microMIPS32_to_MIPS32((union mips_instruction *)&ir)
			 == SIGILL))
			return SIGILL;
	}

emul:
	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, xcp, 0);
	MIPS_FPU_EMU_INC_STATS(emulated);
	switch (MIPSInst_OPCODE(ir)) {
	case ldc1_op:
		dva = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] +
				     MIPSInst_SIMM(ir));
		MIPS_FPU_EMU_INC_STATS(loads);

		if (!access_ok(dva, sizeof(u64))) {
			MIPS_FPU_EMU_INC_STATS(errors);
			*fault_addr = dva;
			return SIGBUS;
		}
		if (__get_user(dval, dva)) {
			MIPS_FPU_EMU_INC_STATS(errors);
			*fault_addr = dva;
			return SIGSEGV;
		}
		DITOREG(dval, MIPSInst_RT(ir));
		break;

	case sdc1_op:
		dva = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] +
				      MIPSInst_SIMM(ir));
		MIPS_FPU_EMU_INC_STATS(stores);
		DIFROMREG(dval, MIPSInst_RT(ir));
		if (!access_ok(dva, sizeof(u64))) {
			MIPS_FPU_EMU_INC_STATS(errors);
			*fault_addr = dva;
			return SIGBUS;
		}
		if (__put_user(dval, dva)) {
			MIPS_FPU_EMU_INC_STATS(errors);
			*fault_addr = dva;
			return SIGSEGV;
		}
		break;

	case lwc1_op:
		wva = (u32 __user *) (xcp->regs[MIPSInst_RS(ir)] +
				      MIPSInst_SIMM(ir));
		MIPS_FPU_EMU_INC_STATS(loads);
		if (!access_ok(wva, sizeof(u32))) {
			MIPS_FPU_EMU_INC_STATS(errors);
			*fault_addr = wva;
			return SIGBUS;
		}
		if (__get_user(wval, wva)) {
			MIPS_FPU_EMU_INC_STATS(errors);
			*fault_addr = wva;
			return SIGSEGV;
		}
		SITOREG(wval, MIPSInst_RT(ir));
		break;

	case swc1_op:
		wva = (u32 __user *) (xcp->regs[MIPSInst_RS(ir)] +
				      MIPSInst_SIMM(ir));
		MIPS_FPU_EMU_INC_STATS(stores);
		SIFROMREG(wval, MIPSInst_RT(ir));
		if (!access_ok(wva, sizeof(u32))) {
			MIPS_FPU_EMU_INC_STATS(errors);
			*fault_addr = wva;
			return SIGBUS;
		}
		if (__put_user(wval, wva)) {
			MIPS_FPU_EMU_INC_STATS(errors);
			*fault_addr = wva;
			return SIGSEGV;
		}
		break;

	case cop1_op:
		switch (MIPSInst_RS(ir)) {
		case dmfc_op:
			if (!cpu_has_mips_3_4_5 && !cpu_has_mips64)
				return SIGILL;

			/* copregister fs -> gpr[rt] */
			if (MIPSInst_RT(ir) != 0) {
				DIFROMREG(xcp->regs[MIPSInst_RT(ir)],
					MIPSInst_RD(ir));
			}
			break;

		case dmtc_op:
			if (!cpu_has_mips_3_4_5 && !cpu_has_mips64)
				return SIGILL;

			/* copregister fs <- rt */
			DITOREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir));
			break;

		case mfhc_op:
			if (!cpu_has_mips_r2_r6)
				return SIGILL;

			/* copregister rd -> gpr[rt] */
			if (MIPSInst_RT(ir) != 0) {
				SIFROMHREG(xcp->regs[MIPSInst_RT(ir)],
					MIPSInst_RD(ir));
			}
			break;

		case mthc_op:
			if (!cpu_has_mips_r2_r6)
				return SIGILL;

			/* copregister rd <- gpr[rt] */
			SITOHREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir));
			break;

		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] */
			cop1_cfc(xcp, ctx, ir);
			break;

		case ctc_op:
			/* copregister rd <- rt */
			cop1_ctc(xcp, ctx, ir);
			if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) {
				return SIGFPE;
			}
			break;

		case bc1eqz_op:
		case bc1nez_op:
			if (!cpu_has_mips_r6 || delay_slot(xcp))
				return SIGILL;

			likely = 0;
			cond = 0;
			fpr = &current->thread.fpu.fpr[MIPSInst_RT(ir)];
			bit0 = get_fpr32(fpr, 0) & 0x1;
			switch (MIPSInst_RS(ir)) {
			case bc1eqz_op:
				MIPS_FPU_EMU_INC_STATS(bc1eqz);
				cond = bit0 == 0;
				break;
			case bc1nez_op:
				MIPS_FPU_EMU_INC_STATS(bc1nez);
				cond = bit0 != 0;
				break;
			}
			goto branch_common;

		case bc_op:
			if (delay_slot(xcp))
				return SIGILL;

			if (cpu_has_mips_4_5_r)
				cbit = fpucondbit[MIPSInst_RT(ir) >> 2];
			else
				cbit = FPU_CSR_COND;
			cond = ctx->fcr31 & cbit;

			likely = 0;
			switch (MIPSInst_RT(ir) & 3) {
			case bcfl_op:
				if (cpu_has_mips_2_3_4_5_r)
					likely = 1;
				fallthrough;
			case bcf_op:
				cond = !cond;
				break;
			case bctl_op:
				if (cpu_has_mips_2_3_4_5_r)
					likely = 1;
				fallthrough;
			case bct_op:
				break;
			}
branch_common:
			MIPS_FPU_EMU_INC_STATS(branches);
			set_delay_slot(xcp);
			if (cond) {
				/*
				 * Branch taken: emulate dslot instruction
				 */
				unsigned long bcpc;

				/*
				 * Remember EPC at the branch to point back
				 * at so that any delay-slot instruction
				 * signal is not silently ignored.
				 */
				bcpc = xcp->cp0_epc;
				xcp->cp0_epc += dec_insn.pc_inc;

				contpc = MIPSInst_SIMM(ir);
				ir = dec_insn.next_insn;
				if (dec_insn.micro_mips_mode) {
					contpc = (xcp->cp0_epc + (contpc << 1));

					/* If 16-bit instruction, not FPU. */
					if ((dec_insn.next_pc_inc == 2) ||
						(microMIPS32_to_MIPS32((union mips_instruction *)&ir) == SIGILL)) {

						/*
						 * Since this instruction will
						 * be put on the stack with
						 * 32-bit words, get around
						 * this problem by putting a
						 * NOP16 as the second one.
						 */
						if (dec_insn.next_pc_inc == 2)
							ir = (ir & (~0xffff)) | MM_NOP16;

						/*
						 * Single step the non-CP1
						 * instruction in the dslot.
						 */
						sig = mips_dsemul(xcp, ir,
								  bcpc, contpc);
						if (sig < 0)
							break;
						if (sig)
							xcp->cp0_epc = bcpc;
						/*
						 * SIGILL forces out of
						 * the emulation loop.
						 */
						return sig ? sig : SIGILL;
					}
				} else
					contpc = (xcp->cp0_epc + (contpc << 2));

				switch (MIPSInst_OPCODE(ir)) {
				case lwc1_op:
				case swc1_op:
					goto emul;

				case ldc1_op:
				case sdc1_op:
					if (cpu_has_mips_2_3_4_5_r)
						goto emul;

					goto bc_sigill;

				case cop1_op:
					goto emul;

				case cop1x_op:
					if (cpu_has_mips_4_5_64_r2_r6)
						/* its one of ours */
						goto emul;

					goto bc_sigill;

				case spec_op:
					switch (MIPSInst_FUNC(ir)) {
					case movc_op:
						if (cpu_has_mips_4_5_r)
							goto emul;

						goto bc_sigill;
					}
					break;

				bc_sigill:
					xcp->cp0_epc = bcpc;
					return SIGILL;
				}

				/*
				 * Single step the non-cp1
				 * instruction in the dslot
				 */
				sig = mips_dsemul(xcp, ir, bcpc, contpc);
				if (sig < 0)
					break;
				if (sig)
					xcp->cp0_epc = bcpc;
				/* SIGILL forces out of the emulation loop.  */
				return sig ? sig : SIGILL;
			} else if (likely) {	/* branch not taken */
				/*
				 * branch likely nullifies
				 * dslot if not taken
				 */
				xcp->cp0_epc += dec_insn.pc_inc;
				contpc += dec_insn.pc_inc;
				/*
				 * else continue & execute
				 * dslot as normal insn
				 */
			}
			break;

		default:
			if (!(MIPSInst_RS(ir) & 0x10))
				return SIGILL;

			/* a real fpu computation instruction */
			sig = fpu_emu(xcp, ctx, ir);
			if (sig)
				return sig;
		}
		break;

	case cop1x_op:
		if (!cpu_has_mips_4_5_64_r2_r6)
			return SIGILL;

		sig = fpux_emu(xcp, ctx, ir, fault_addr);
		if (sig)
			return sig;
		break;

	case spec_op:
		if (!cpu_has_mips_4_5_r)
			return SIGILL;

		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;
	default:
		return SIGILL;
	}

	/* we did it !! */
	xcp->cp0_epc = contpc;
	clear_delay_slot(xcp);

	return 0;
}