// SPDX-License-Identifier: GPL-2.0-only /* * cp1emu.c: a MIPS coprocessor 1 (FPU) instruction emulator * * MIPS floating point support * Copyright (C) 1994-2000 Algorithmics Ltd. * * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com * Copyright (C) 2000 MIPS Technologies, Inc. * * 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 <linux/debugfs.h> #include <linux/percpu-defs.h> #include <linux/perf_event.h>
/* * This functions translates a 32-bit microMIPS instruction * into a 32-bit MIPS32 instruction. Returns 0 on success * and SIGILL otherwise.
*/ staticint microMIPS32_to_MIPS32(union mips_instruction *insn_ptr)
{ union mips_instruction insn = *insn_ptr; union mips_instruction mips32_insn = insn; int func, fmt, op;
switch (insn.mm_i_format.opcode) { case mm_ldc132_op:
mips32_insn.mm_i_format.opcode = ldc1_op;
mips32_insn.mm_i_format.rt = insn.mm_i_format.rs;
mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; break; case mm_lwc132_op:
mips32_insn.mm_i_format.opcode = lwc1_op;
mips32_insn.mm_i_format.rt = insn.mm_i_format.rs;
mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; break; case mm_sdc132_op:
mips32_insn.mm_i_format.opcode = sdc1_op;
mips32_insn.mm_i_format.rt = insn.mm_i_format.rs;
mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; break; case mm_swc132_op:
mips32_insn.mm_i_format.opcode = swc1_op;
mips32_insn.mm_i_format.rt = insn.mm_i_format.rs;
mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; break; case mm_pool32i_op: /* NOTE: offset is << by 1 if in microMIPS mode. */ if ((insn.mm_i_format.rt == mm_bc1f_op) ||
(insn.mm_i_format.rt == mm_bc1t_op)) {
mips32_insn.fb_format.opcode = cop1_op;
mips32_insn.fb_format.bc = bc_op;
mips32_insn.fb_format.flag =
(insn.mm_i_format.rt == mm_bc1t_op) ? 1 : 0;
} else return SIGILL; break; case mm_pool32f_op: switch (insn.mm_fp0_format.func) { case mm_32f_01_op: case mm_32f_11_op: case mm_32f_02_op: case mm_32f_12_op: case mm_32f_41_op: case mm_32f_51_op: case mm_32f_42_op: case mm_32f_52_op:
op = insn.mm_fp0_format.func; if (op == mm_32f_01_op)
func = madd_s_op; elseif (op == mm_32f_11_op)
func = madd_d_op; elseif (op == mm_32f_02_op)
func = nmadd_s_op; elseif (op == mm_32f_12_op)
func = nmadd_d_op; elseif (op == mm_32f_41_op)
func = msub_s_op; elseif (op == mm_32f_51_op)
func = msub_d_op; elseif (op == mm_32f_42_op)
func = nmsub_s_op; else
func = nmsub_d_op;
mips32_insn.fp6_format.opcode = cop1x_op;
mips32_insn.fp6_format.fr = insn.mm_fp6_format.fr;
mips32_insn.fp6_format.ft = insn.mm_fp6_format.ft;
mips32_insn.fp6_format.fs = insn.mm_fp6_format.fs;
mips32_insn.fp6_format.fd = insn.mm_fp6_format.fd;
mips32_insn.fp6_format.func = func; break; case mm_32f_10_op:
func = -1; /* Invalid */
op = insn.mm_fp5_format.op & 0x7; if (op == mm_ldxc1_op)
func = ldxc1_op; elseif (op == mm_sdxc1_op)
func = sdxc1_op; elseif (op == mm_lwxc1_op)
func = lwxc1_op; elseif (op == mm_swxc1_op)
func = swxc1_op;
if (func != -1) {
mips32_insn.r_format.opcode = cop1x_op;
mips32_insn.r_format.rs =
insn.mm_fp5_format.base;
mips32_insn.r_format.rt =
insn.mm_fp5_format.index;
mips32_insn.r_format.rd = 0;
mips32_insn.r_format.re = insn.mm_fp5_format.fd;
mips32_insn.r_format.func = func;
} else return SIGILL; break; case mm_32f_40_op:
op = -1; /* Invalid */ if (insn.mm_fp2_format.op == mm_fmovt_op)
op = 1; elseif (insn.mm_fp2_format.op == mm_fmovf_op)
op = 0; if (op != -1) {
mips32_insn.fp0_format.opcode = cop1_op;
mips32_insn.fp0_format.fmt =
sdps_format[insn.mm_fp2_format.fmt];
mips32_insn.fp0_format.ft =
(insn.mm_fp2_format.cc<<2) + op;
mips32_insn.fp0_format.fs =
insn.mm_fp2_format.fs;
mips32_insn.fp0_format.fd =
insn.mm_fp2_format.fd;
mips32_insn.fp0_format.func = fmovc_op;
} else return SIGILL; break; case mm_32f_60_op:
func = -1; /* Invalid */ if (insn.mm_fp0_format.op == mm_fadd_op)
func = fadd_op; elseif (insn.mm_fp0_format.op == mm_fsub_op)
func = fsub_op; elseif (insn.mm_fp0_format.op == mm_fmul_op)
func = fmul_op; elseif (insn.mm_fp0_format.op == mm_fdiv_op)
func = fdiv_op; if (func != -1) {
mips32_insn.fp0_format.opcode = cop1_op;
mips32_insn.fp0_format.fmt =
sdps_format[insn.mm_fp0_format.fmt];
mips32_insn.fp0_format.ft =
insn.mm_fp0_format.ft;
mips32_insn.fp0_format.fs =
insn.mm_fp0_format.fs;
mips32_insn.fp0_format.fd =
insn.mm_fp0_format.fd;
mips32_insn.fp0_format.func = func;
} else return SIGILL; break; case mm_32f_70_op:
func = -1; /* Invalid */ if (insn.mm_fp0_format.op == mm_fmovn_op)
func = fmovn_op; elseif (insn.mm_fp0_format.op == mm_fmovz_op)
func = fmovz_op; if (func != -1) {
mips32_insn.fp0_format.opcode = cop1_op;
mips32_insn.fp0_format.fmt =
sdps_format[insn.mm_fp0_format.fmt];
mips32_insn.fp0_format.ft =
insn.mm_fp0_format.ft;
mips32_insn.fp0_format.fs =
insn.mm_fp0_format.fs;
mips32_insn.fp0_format.fd =
insn.mm_fp0_format.fd;
mips32_insn.fp0_format.func = func;
} else return SIGILL; break; case mm_32f_73_op: /* POOL32FXF */ switch (insn.mm_fp1_format.op) { case mm_movf0_op: case mm_movf1_op: case mm_movt0_op: case mm_movt1_op: if ((insn.mm_fp1_format.op & 0x7f) ==
mm_movf0_op)
op = 0; else
op = 1;
mips32_insn.r_format.opcode = spec_op;
mips32_insn.r_format.rs = insn.mm_fp4_format.fs;
mips32_insn.r_format.rt =
(insn.mm_fp4_format.cc << 2) + op;
mips32_insn.r_format.rd = insn.mm_fp4_format.rt;
mips32_insn.r_format.re = 0;
mips32_insn.r_format.func = movc_op; break; case mm_fcvtd0_op: case mm_fcvtd1_op: case mm_fcvts0_op: case mm_fcvts1_op: if ((insn.mm_fp1_format.op & 0x7f) ==
mm_fcvtd0_op) {
func = fcvtd_op;
fmt = swl_format[insn.mm_fp3_format.fmt];
} else {
func = fcvts_op;
fmt = dwl_format[insn.mm_fp3_format.fmt];
}
mips32_insn.fp0_format.opcode = cop1_op;
mips32_insn.fp0_format.fmt = fmt;
mips32_insn.fp0_format.ft = 0;
mips32_insn.fp0_format.fs =
insn.mm_fp3_format.fs;
mips32_insn.fp0_format.fd =
insn.mm_fp3_format.rt;
mips32_insn.fp0_format.func = func; break; case mm_fmov0_op: case mm_fmov1_op: case mm_fabs0_op: case mm_fabs1_op: case mm_fneg0_op: case mm_fneg1_op: if ((insn.mm_fp1_format.op & 0x7f) ==
mm_fmov0_op)
func = fmov_op; elseif ((insn.mm_fp1_format.op & 0x7f) ==
mm_fabs0_op)
func = fabs_op; else
func = fneg_op;
mips32_insn.fp0_format.opcode = cop1_op;
mips32_insn.fp0_format.fmt =
sdps_format[insn.mm_fp3_format.fmt];
mips32_insn.fp0_format.ft = 0;
mips32_insn.fp0_format.fs =
insn.mm_fp3_format.fs;
mips32_insn.fp0_format.fd =
insn.mm_fp3_format.rt;
mips32_insn.fp0_format.func = func; break; case mm_ffloorl_op: case mm_ffloorw_op: case mm_fceill_op: case mm_fceilw_op: case mm_ftruncl_op: case mm_ftruncw_op: case mm_froundl_op: case mm_froundw_op: case mm_fcvtl_op: case mm_fcvtw_op: if (insn.mm_fp1_format.op == mm_ffloorl_op)
func = ffloorl_op; elseif (insn.mm_fp1_format.op == mm_ffloorw_op)
func = ffloor_op; elseif (insn.mm_fp1_format.op == mm_fceill_op)
func = fceill_op; elseif (insn.mm_fp1_format.op == mm_fceilw_op)
func = fceil_op; elseif (insn.mm_fp1_format.op == mm_ftruncl_op)
func = ftruncl_op; elseif (insn.mm_fp1_format.op == mm_ftruncw_op)
func = ftrunc_op; elseif (insn.mm_fp1_format.op == mm_froundl_op)
func = froundl_op; elseif (insn.mm_fp1_format.op == mm_froundw_op)
func = fround_op; elseif (insn.mm_fp1_format.op == mm_fcvtl_op)
func = fcvtl_op; else
func = fcvtw_op;
mips32_insn.fp0_format.opcode = cop1_op;
mips32_insn.fp0_format.fmt =
sd_format[insn.mm_fp1_format.fmt];
mips32_insn.fp0_format.ft = 0;
mips32_insn.fp0_format.fs =
insn.mm_fp1_format.fs;
mips32_insn.fp0_format.fd =
insn.mm_fp1_format.rt;
mips32_insn.fp0_format.func = func; break; case mm_frsqrt_op: case mm_fsqrt_op: case mm_frecip_op: if (insn.mm_fp1_format.op == mm_frsqrt_op)
func = frsqrt_op; elseif (insn.mm_fp1_format.op == mm_fsqrt_op)
func = fsqrt_op; else
func = frecip_op;
mips32_insn.fp0_format.opcode = cop1_op;
mips32_insn.fp0_format.fmt =
sdps_format[insn.mm_fp1_format.fmt];
mips32_insn.fp0_format.ft = 0;
mips32_insn.fp0_format.fs =
insn.mm_fp1_format.fs;
mips32_insn.fp0_format.fd =
insn.mm_fp1_format.rt;
mips32_insn.fp0_format.func = func; break; case mm_mfc1_op: case mm_mtc1_op: case mm_cfc1_op: case mm_ctc1_op: case mm_mfhc1_op: case mm_mthc1_op: if (insn.mm_fp1_format.op == mm_mfc1_op)
op = mfc_op; elseif (insn.mm_fp1_format.op == mm_mtc1_op)
op = mtc_op; elseif (insn.mm_fp1_format.op == mm_cfc1_op)
op = cfc_op; elseif (insn.mm_fp1_format.op == mm_ctc1_op)
op = ctc_op; elseif (insn.mm_fp1_format.op == mm_mfhc1_op)
op = mfhc_op; else
op = mthc_op;
mips32_insn.fp1_format.opcode = cop1_op;
mips32_insn.fp1_format.op = op;
mips32_insn.fp1_format.rt =
insn.mm_fp1_format.rt;
mips32_insn.fp1_format.fs =
insn.mm_fp1_format.fs;
mips32_insn.fp1_format.fd = 0;
mips32_insn.fp1_format.func = 0; break; default: return SIGILL;
} break; case mm_32f_74_op: /* c.cond.fmt */
mips32_insn.fp0_format.opcode = cop1_op;
mips32_insn.fp0_format.fmt =
sdps_format[insn.mm_fp4_format.fmt];
mips32_insn.fp0_format.ft = insn.mm_fp4_format.rt;
mips32_insn.fp0_format.fs = insn.mm_fp4_format.fs;
mips32_insn.fp0_format.fd = insn.mm_fp4_format.cc << 2;
mips32_insn.fp0_format.func =
insn.mm_fp4_format.cond | MM_MIPS32_COND_FC; break; default: return SIGILL;
} break; default: return SIGILL;
}
*insn_ptr = mips32_insn; return 0;
}
/* * 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.
*/ int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, unsignedlong *contpc)
{ union mips_instruction insn = (union mips_instruction)dec_insn.insn; unsignedint fcr31; unsignedint bit = 0; unsignedint bit0; union fpureg *fpr;
switch (insn.i_format.opcode) { case spec_op: switch (insn.r_format.func) { case jalr_op: if (insn.r_format.rd != 0) {
regs->regs[insn.r_format.rd] =
regs->cp0_epc + dec_insn.pc_inc +
dec_insn.next_pc_inc;
}
fallthrough; case jr_op: /* For R6, JR already emulated in jalr_op */ if (NO_R6EMU && insn.r_format.func == jr_op) break;
*contpc = regs->regs[insn.r_format.rs]; return 1;
} break; case bcond_op: switch (insn.i_format.rt) { case bltzal_op: case bltzall_op: if (NO_R6EMU && (insn.i_format.rs ||
insn.i_format.rt == bltzall_op)) break;
regs->regs[31] = regs->cp0_epc +
dec_insn.pc_inc +
dec_insn.next_pc_inc;
fallthrough; case bltzl_op: if (NO_R6EMU) break;
fallthrough; case bltz_op: if ((long)regs->regs[insn.i_format.rs] < 0)
*contpc = regs->cp0_epc +
dec_insn.pc_inc +
(insn.i_format.simmediate << 2); else
*contpc = regs->cp0_epc +
dec_insn.pc_inc +
dec_insn.next_pc_inc; return 1; case bgezal_op: case bgezall_op: if (NO_R6EMU && (insn.i_format.rs ||
insn.i_format.rt == bgezall_op)) break;
regs->regs[31] = regs->cp0_epc +
dec_insn.pc_inc +
dec_insn.next_pc_inc;
fallthrough; case bgezl_op: if (NO_R6EMU) break;
fallthrough; case bgez_op: if ((long)regs->regs[insn.i_format.rs] >= 0)
*contpc = regs->cp0_epc +
dec_insn.pc_inc +
(insn.i_format.simmediate << 2); else
*contpc = regs->cp0_epc +
dec_insn.pc_inc +
dec_insn.next_pc_inc; return 1;
} break; case jalx_op:
set_isa16_mode(bit);
fallthrough; case jal_op:
regs->regs[31] = regs->cp0_epc +
dec_insn.pc_inc +
dec_insn.next_pc_inc;
fallthrough; case j_op:
*contpc = regs->cp0_epc + dec_insn.pc_inc;
*contpc >>= 28;
*contpc <<= 28;
*contpc |= (insn.j_format.target << 2); /* Set microMIPS mode bit: XOR for jalx. */
*contpc ^= bit; return 1; case beql_op: if (NO_R6EMU) break;
fallthrough; case beq_op: if (regs->regs[insn.i_format.rs] ==
regs->regs[insn.i_format.rt])
*contpc = regs->cp0_epc +
dec_insn.pc_inc +
(insn.i_format.simmediate << 2); else
*contpc = regs->cp0_epc +
dec_insn.pc_inc +
dec_insn.next_pc_inc; return 1; case bnel_op: if (NO_R6EMU) break;
fallthrough; case bne_op: if (regs->regs[insn.i_format.rs] !=
regs->regs[insn.i_format.rt])
*contpc = regs->cp0_epc +
dec_insn.pc_inc +
(insn.i_format.simmediate << 2); else
*contpc = regs->cp0_epc +
dec_insn.pc_inc +
dec_insn.next_pc_inc; return 1; case blezl_op: if (!insn.i_format.rt && NO_R6EMU) break;
fallthrough; case blez_op:
if ((long)regs->regs[insn.i_format.rs] > 0)
*contpc = regs->cp0_epc +
dec_insn.pc_inc +
(insn.i_format.simmediate << 2); else
*contpc = regs->cp0_epc +
dec_insn.pc_inc +
dec_insn.next_pc_inc; return 1; case pop10_op: case pop30_op: if (!cpu_has_mips_r6) break; if (insn.i_format.rt && !insn.i_format.rs)
regs->regs[31] = regs->cp0_epc + 4;
*contpc = regs->cp0_epc + dec_insn.pc_inc +
dec_insn.next_pc_inc;
return 1; #ifdef CONFIG_CPU_CAVIUM_OCTEON case lwc2_op: /* This is bbit0 on Octeon */ if ((regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt)) == 0)
*contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2); else
*contpc = regs->cp0_epc + 8; return 1; case ldc2_op: /* This is bbit032 on Octeon */ if ((regs->regs[insn.i_format.rs] & (1ull<<(insn.i_format.rt + 32))) == 0)
*contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2); else
*contpc = regs->cp0_epc + 8; return 1; case swc2_op: /* This is bbit1 on Octeon */ if (regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt))
*contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2); else
*contpc = regs->cp0_epc + 8; return 1; case sdc2_op: /* This is bbit132 on Octeon */ if (regs->regs[insn.i_format.rs] & (1ull<<(insn.i_format.rt + 32)))
*contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2); else
*contpc = regs->cp0_epc + 8; return 1; #else case bc6_op: /* * Only valid for MIPS R6 but we can still end up * here from a broken userland so just tell emulator * this is not a branch and let it break later on.
*/ if (!cpu_has_mips_r6) break;
*contpc = regs->cp0_epc + dec_insn.pc_inc +
dec_insn.next_pc_inc;
return 1; case balc6_op: if (!cpu_has_mips_r6) break;
regs->regs[31] = regs->cp0_epc + 4;
*contpc = regs->cp0_epc + dec_insn.pc_inc +
dec_insn.next_pc_inc;
return 1; case pop66_op: if (!cpu_has_mips_r6) break;
*contpc = regs->cp0_epc + dec_insn.pc_inc +
dec_insn.next_pc_inc;
return 1; case pop76_op: if (!cpu_has_mips_r6) break; if (!insn.i_format.rs)
regs->regs[31] = regs->cp0_epc + 4;
*contpc = regs->cp0_epc + dec_insn.pc_inc +
dec_insn.next_pc_inc;
return 1; #endif case cop0_op: case cop1_op: /* Need to check for R6 bc1nez and bc1eqz branches */ if (cpu_has_mips_r6 &&
((insn.i_format.rs == bc1eqz_op) ||
(insn.i_format.rs == bc1nez_op))) {
bit = 0;
fpr = ¤t->thread.fpu.fpr[insn.i_format.rt];
bit0 = get_fpr32(fpr, 0) & 0x1; switch (insn.i_format.rs) { case bc1eqz_op:
bit = bit0 == 0; break; case bc1nez_op:
bit = bit0 != 0; break;
} if (bit)
*contpc = regs->cp0_epc +
dec_insn.pc_inc +
(insn.i_format.simmediate << 2); else
*contpc = regs->cp0_epc +
dec_insn.pc_inc +
dec_insn.next_pc_inc;
return 1;
} /* R2/R6 compatible cop1 instruction */
fallthrough; case cop2_op: case cop1x_op: if (insn.i_format.rs == bc_op) {
preempt_disable(); if (is_fpu_owner())
fcr31 = read_32bit_cp1_register(CP1_STATUS); else
fcr31 = current->thread.fpu.fcr31;
preempt_enable();
/* * In the Linux kernel, we support selection of FPR format on the * basis of the Status.FR bit. If an FPU is not present, the FR bit * is hardwired to zero, which would imply a 32-bit FPU even for * 64-bit CPUs so we rather look at TIF_32BIT_FPREGS. * FPU emu is slow and bulky and optimizing this function offers fairly * sizeable benefits so we try to be clever and make this function return * a constant whenever possible, that is on 64-bit kernels without O32 * compatibility enabled and on 32-bit without 64-bit FPU support.
*/ staticinlineint cop1_64bit(struct pt_regs *xcp)
{ if (IS_ENABLED(CONFIG_64BIT) && !IS_ENABLED(CONFIG_MIPS32_O32)) return 1; elseif (IS_ENABLED(CONFIG_32BIT) &&
!IS_ENABLED(CONFIG_MIPS_O32_FP64_SUPPORT)) return 0;
#define SITOHREG(si, x) \ do { \ unsignedint i; \
set_fpr32(&ctx->fpr[x], 1, si); \ for (i = 2; i < ARRAY_SIZE(ctx->fpr[x].val32); i++) \
set_fpr32(&ctx->fpr[x], i, 0); \
} while (0)
/* * 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 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);
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
*/ unsignedlong 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;
} elseif (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;
case d_fmt:{ /* 1 */ union ieee754dp(*handler) (union ieee754dp, union ieee754dp, union ieee754dp); union ieee754dp fd, fr, fs, ft;
u64 __user *va;
u64 val;
switch (MIPSInst_FUNC(ir)) { case ldxc1_op:
va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +
xcp->regs[MIPSInst_FT(ir)]);
/* * Note that on some MIPS IV implementations such as the * R5000 and R8000 the FSQRT and FRECIP instructions do not * achieve full IEEE-754 accuracy - however this emulator does.
*/ case frsqrt_op: if (!cpu_has_mips_4_5_64_r2_r6) return SIGILL;
case fround_op: case ftrunc_op: case fceil_op: case ffloor_op: if (!cpu_has_mips_2_3_4_5_r) return SIGILL;
if (MIPSInst_FUNC(ir) == fceil_op)
MIPS_FPU_EMU_INC_STATS(ceil_w_s); if (MIPSInst_FUNC(ir) == ffloor_op)
MIPS_FPU_EMU_INC_STATS(floor_w_s); if (MIPSInst_FUNC(ir) == fround_op)
MIPS_FPU_EMU_INC_STATS(round_w_s); if (MIPSInst_FUNC(ir) == ftrunc_op)
MIPS_FPU_EMU_INC_STATS(trunc_w_s);
case froundl_op: case ftruncl_op: case fceill_op: case ffloorl_op: if (!cpu_has_mips_3_4_5_64_r2_r6) return SIGILL;
if (MIPSInst_FUNC(ir) == fceill_op)
MIPS_FPU_EMU_INC_STATS(ceil_l_s); if (MIPSInst_FUNC(ir) == ffloorl_op)
MIPS_FPU_EMU_INC_STATS(floor_l_s); if (MIPSInst_FUNC(ir) == froundl_op)
MIPS_FPU_EMU_INC_STATS(round_l_s); if (MIPSInst_FUNC(ir) == ftruncl_op)
MIPS_FPU_EMU_INC_STATS(trunc_l_s);
case d_fmt: { union ieee754dp fd, fs, ft; union { union ieee754dp(*b) (union ieee754dp, union ieee754dp); union ieee754dp(*u) (union ieee754dp);
} handler;
switch (MIPSInst_FUNC(ir)) { /* binary ops */ case fadd_op:
MIPS_FPU_EMU_INC_STATS(add_d);
handler.b = ieee754dp_add; goto dcopbop; case fsub_op:
MIPS_FPU_EMU_INC_STATS(sub_d);
handler.b = ieee754dp_sub; goto dcopbop; case fmul_op:
MIPS_FPU_EMU_INC_STATS(mul_d);
handler.b = ieee754dp_mul; goto dcopbop; case fdiv_op:
MIPS_FPU_EMU_INC_STATS(div_d);
handler.b = ieee754dp_div; goto dcopbop;
/* unary ops */ case fsqrt_op: if (!cpu_has_mips_2_3_4_5_r) return SIGILL;
MIPS_FPU_EMU_INC_STATS(sqrt_d);
handler.u = ieee754dp_sqrt; goto dcopuop; /* * Note that on some MIPS IV implementations such as the * R5000 and R8000 the FSQRT and FRECIP instructions do not * achieve full IEEE-754 accuracy - however this emulator does.
*/ case frsqrt_op: if (!cpu_has_mips_4_5_64_r2_r6) return SIGILL;
MIPS_FPU_EMU_INC_STATS(rsqrt_d);
handler.u = fpemu_dp_rsqrt; goto dcopuop; case frecip_op: if (!cpu_has_mips_4_5_64_r2_r6) return SIGILL;
MIPS_FPU_EMU_INC_STATS(recip_d);
handler.u = fpemu_dp_recip; goto dcopuop; case fmovc_op: if (!cpu_has_mips_4_5_r) return SIGILL;
cond = fpucondbit[MIPSInst_FT(ir) >> 2]; if (((ctx->fcr31 & cond) != 0) !=
((MIPSInst_FT(ir) & 1) != 0)) return 0;
DPFROMREG(rv.d, MIPSInst_FS(ir)); break; case fmovz_op: if (!cpu_has_mips_4_5_r) return SIGILL;
if (xcp->regs[MIPSInst_FT(ir)] != 0) return 0;
DPFROMREG(rv.d, MIPSInst_FS(ir)); break; case fmovn_op: if (!cpu_has_mips_4_5_r) return SIGILL;
if (xcp->regs[MIPSInst_FT(ir)] == 0) return 0;
DPFROMREG(rv.d, MIPSInst_FS(ir)); break;
case fseleqz_op: if (!cpu_has_mips_r6) return SIGILL;
case fround_op: case ftrunc_op: case fceil_op: case ffloor_op: if (!cpu_has_mips_2_3_4_5_r) return SIGILL;
if (MIPSInst_FUNC(ir) == fceil_op)
MIPS_FPU_EMU_INC_STATS(ceil_w_d); if (MIPSInst_FUNC(ir) == ffloor_op)
MIPS_FPU_EMU_INC_STATS(floor_w_d); if (MIPSInst_FUNC(ir) == fround_op)
MIPS_FPU_EMU_INC_STATS(round_w_d); if (MIPSInst_FUNC(ir) == ftrunc_op)
MIPS_FPU_EMU_INC_STATS(trunc_w_d);
case froundl_op: case ftruncl_op: case fceill_op: case ffloorl_op: if (!cpu_has_mips_3_4_5_64_r2_r6) return SIGILL;
if (MIPSInst_FUNC(ir) == fceill_op)
MIPS_FPU_EMU_INC_STATS(ceil_l_d); if (MIPSInst_FUNC(ir) == ffloorl_op)
MIPS_FPU_EMU_INC_STATS(floor_l_d); if (MIPSInst_FUNC(ir) == froundl_op)
MIPS_FPU_EMU_INC_STATS(round_l_d); if (MIPSInst_FUNC(ir) == ftruncl_op)
MIPS_FPU_EMU_INC_STATS(trunc_l_d);
/* positive predicates */ if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) { if (ieee754sp_cmp(fs, ft, cmptab[cmpop],
sig))
rv.w = -1; /* true, all 1s */ if ((sig) &&
ieee754_cxtest(IEEE754_INVALID_OPERATION))
rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; else goto copcsr;
} else { /* negative predicates */ switch (cmpop) { case 1: case 2: case 3: if (ieee754sp_cmp(fs, ft,
negative_cmptab[cmpop],
sig))
rv.w = -1; /* true, all 1s */ if (sig &&
ieee754_cxtest(IEEE754_INVALID_OPERATION))
rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; else goto copcsr; break; default: /* Reserved R6 ops */ return SIGILL;
}
} break;
}
} break;
}
case l_fmt:
if (!cpu_has_mips_3_4_5_64_r2_r6) return SIGILL;
DIFROMREG(bits, MIPSInst_FS(ir));
switch (MIPSInst_FUNC(ir)) { case fcvts_op: /* convert long to single precision real */
MIPS_FPU_EMU_INC_STATS(cvt_s_l);
rv.s = ieee754sp_flong(bits);
rfmt = s_fmt; goto copcsr; case fcvtd_op: /* convert long to double precision real */
MIPS_FPU_EMU_INC_STATS(cvt_d_l);
rv.d = ieee754dp_flong(bits);
rfmt = d_fmt; goto copcsr; default: { /* Emulating the new CMP.condn.fmt R6 instruction */ int cmpop = MIPSInst_FUNC(ir) & CMPOP_MASK; int sig = MIPSInst_FUNC(ir) & SIGN_BIT; union ieee754dp fs, ft;
if (!cpu_has_mips_r6 ||
(MIPSInst_FUNC(ir) & 0x20)) return SIGILL;
if (!sig) { if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) { switch (cmpop) { case 0:
MIPS_FPU_EMU_INC_STATS(cmp_af_d); break; case 1:
MIPS_FPU_EMU_INC_STATS(cmp_un_d); break; case 2:
MIPS_FPU_EMU_INC_STATS(cmp_eq_d); break; case 3:
MIPS_FPU_EMU_INC_STATS(cmp_ueq_d); break; case 4:
MIPS_FPU_EMU_INC_STATS(cmp_lt_d); break; case 5:
MIPS_FPU_EMU_INC_STATS(cmp_ult_d); break; case 6:
MIPS_FPU_EMU_INC_STATS(cmp_le_d); break; case 7:
MIPS_FPU_EMU_INC_STATS(cmp_ule_d); break;
}
} else { switch (cmpop) { case 1:
MIPS_FPU_EMU_INC_STATS(cmp_or_d); break; case 2:
MIPS_FPU_EMU_INC_STATS(cmp_une_d); break; case 3:
MIPS_FPU_EMU_INC_STATS(cmp_ne_d); break;
}
}
} else { if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) { switch (cmpop) { case 0:
MIPS_FPU_EMU_INC_STATS(cmp_saf_d); break; case 1:
MIPS_FPU_EMU_INC_STATS(cmp_sun_d); break; case 2:
MIPS_FPU_EMU_INC_STATS(cmp_seq_d); break; case 3:
MIPS_FPU_EMU_INC_STATS(cmp_sueq_d); break; case 4:
MIPS_FPU_EMU_INC_STATS(cmp_slt_d); break; case 5:
MIPS_FPU_EMU_INC_STATS(cmp_sult_d); break; case 6:
MIPS_FPU_EMU_INC_STATS(cmp_sle_d); break; case 7:
MIPS_FPU_EMU_INC_STATS(cmp_sule_d); break;
}
} else { switch (cmpop) { case 1:
MIPS_FPU_EMU_INC_STATS(cmp_sor_d); break; case 2:
MIPS_FPU_EMU_INC_STATS(cmp_sune_d); break; case 3:
MIPS_FPU_EMU_INC_STATS(cmp_sne_d); break;
}
}
}
/* fmt is l_fmt for double precision so fix it */
rfmt = d_fmt; /* default to false */
rv.l = 0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.