2005-04-17 05:20:36 +07:00
|
|
|
/*
|
2014-04-26 06:49:14 +07:00
|
|
|
* cp1emu.c: a MIPS coprocessor 1 (FPU) instruction emulator
|
2005-04-17 05:20:36 +07:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* 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.,
|
2014-04-26 06:49:14 +07:00
|
|
|
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
2005-04-17 05:20:36 +07:00
|
|
|
*
|
|
|
|
* 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
|
2014-04-26 06:49:14 +07:00
|
|
|
* the hardware FPU at the boundaries of the IEEE-754 representation
|
2005-04-17 05:20:36 +07:00
|
|
|
* (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.
|
|
|
|
*
|
2014-04-26 06:49:14 +07:00
|
|
|
* Note if you know that you won't have an FPU, then you'll get much
|
2005-04-17 05:20:36 +07:00
|
|
|
* better performance by compiling with -msoft-float!
|
|
|
|
*/
|
|
|
|
#include <linux/sched.h>
|
2007-07-07 21:21:49 +07:00
|
|
|
#include <linux/debugfs.h>
|
2014-04-19 18:11:37 +07:00
|
|
|
#include <linux/kconfig.h>
|
2014-04-16 07:46:11 +07:00
|
|
|
#include <linux/percpu-defs.h>
|
2010-10-12 18:37:21 +07:00
|
|
|
#include <linux/perf_event.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2014-04-16 07:09:53 +07:00
|
|
|
#include <asm/branch.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <asm/inst.h>
|
|
|
|
#include <asm/ptrace.h>
|
|
|
|
#include <asm/signal.h>
|
2014-04-16 07:09:53 +07:00
|
|
|
#include <asm/uaccess.h>
|
|
|
|
|
2015-04-04 05:27:26 +07:00
|
|
|
#include <asm/cpu-info.h>
|
2014-04-16 07:09:53 +07:00
|
|
|
#include <asm/processor.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <asm/fpu_emulator.h>
|
2013-03-26 00:09:02 +07:00
|
|
|
#include <asm/fpu.h>
|
2014-12-03 22:47:03 +07:00
|
|
|
#include <asm/mips-r2-to-r6-emul.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
#include "ieee754.h"
|
|
|
|
|
|
|
|
/* Function which emulates a floating point instruction. */
|
|
|
|
|
2006-05-15 23:26:03 +07:00
|
|
|
static int fpu_emu(struct pt_regs *, struct mips_fpu_struct *,
|
2005-04-17 05:20:36 +07:00
|
|
|
mips_instruction);
|
|
|
|
|
|
|
|
static int fpux_emu(struct pt_regs *,
|
2010-10-22 06:32:26 +07:00
|
|
|
struct mips_fpu_struct *, mips_instruction, void *__user *);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Control registers */
|
|
|
|
|
|
|
|
#define FPCREG_RID 0 /* $0 = revision id */
|
2015-04-04 05:27:33 +07:00
|
|
|
#define FPCREG_FCCR 25 /* $25 = fccr */
|
|
|
|
#define FPCREG_FEXR 26 /* $26 = fexr */
|
|
|
|
#define FPCREG_FENR 28 /* $28 = fenr */
|
2005-04-17 05:20:36 +07:00
|
|
|
#define FPCREG_CSR 31 /* $31 = csr */
|
|
|
|
|
|
|
|
/* convert condition code register number to csr bit */
|
2014-12-03 22:47:03 +07:00
|
|
|
const unsigned int fpucondbit[8] = {
|
2015-04-04 05:27:33 +07:00
|
|
|
FPU_CSR_COND,
|
2005-04-17 05:20:36 +07:00
|
|
|
FPU_CSR_COND1,
|
|
|
|
FPU_CSR_COND2,
|
|
|
|
FPU_CSR_COND3,
|
|
|
|
FPU_CSR_COND4,
|
|
|
|
FPU_CSR_COND5,
|
|
|
|
FPU_CSR_COND6,
|
|
|
|
FPU_CSR_COND7
|
|
|
|
};
|
|
|
|
|
2013-03-26 00:09:02 +07:00
|
|
|
/* (microMIPS) Convert certain microMIPS instructions to MIPS32 format. */
|
|
|
|
static const int sd_format[] = {16, 17, 0, 0, 0, 0, 0, 0};
|
|
|
|
static const int sdps_format[] = {16, 17, 22, 0, 0, 0, 0, 0};
|
|
|
|
static const int dwl_format[] = {17, 20, 21, 0, 0, 0, 0, 0};
|
|
|
|
static const int swl_format[] = {16, 20, 21, 0, 0, 0, 0, 0};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This functions translates a 32-bit microMIPS instruction
|
|
|
|
* into a 32-bit MIPS32 instruction. Returns 0 on success
|
|
|
|
* and SIGILL otherwise.
|
|
|
|
*/
|
|
|
|
static int 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;
|
|
|
|
else if (op == mm_32f_11_op)
|
|
|
|
func = madd_d_op;
|
|
|
|
else if (op == mm_32f_02_op)
|
|
|
|
func = nmadd_s_op;
|
|
|
|
else if (op == mm_32f_12_op)
|
|
|
|
func = nmadd_d_op;
|
|
|
|
else if (op == mm_32f_41_op)
|
|
|
|
func = msub_s_op;
|
|
|
|
else if (op == mm_32f_51_op)
|
|
|
|
func = msub_d_op;
|
|
|
|
else if (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;
|
|
|
|
else if (op == mm_sdxc1_op)
|
|
|
|
func = sdxc1_op;
|
|
|
|
else if (op == mm_lwxc1_op)
|
|
|
|
func = lwxc1_op;
|
|
|
|
else if (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;
|
|
|
|
else if (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;
|
|
|
|
else if (insn.mm_fp0_format.op == mm_fsub_op)
|
|
|
|
func = fsub_op;
|
|
|
|
else if (insn.mm_fp0_format.op == mm_fmul_op)
|
|
|
|
func = fmul_op;
|
|
|
|
else if (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;
|
|
|
|
else if (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;
|
|
|
|
else if ((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;
|
|
|
|
else if (insn.mm_fp1_format.op == mm_ffloorw_op)
|
|
|
|
func = ffloor_op;
|
|
|
|
else if (insn.mm_fp1_format.op == mm_fceill_op)
|
|
|
|
func = fceill_op;
|
|
|
|
else if (insn.mm_fp1_format.op == mm_fceilw_op)
|
|
|
|
func = fceil_op;
|
|
|
|
else if (insn.mm_fp1_format.op == mm_ftruncl_op)
|
|
|
|
func = ftruncl_op;
|
|
|
|
else if (insn.mm_fp1_format.op == mm_ftruncw_op)
|
|
|
|
func = ftrunc_op;
|
|
|
|
else if (insn.mm_fp1_format.op == mm_froundl_op)
|
|
|
|
func = froundl_op;
|
|
|
|
else if (insn.mm_fp1_format.op == mm_froundw_op)
|
|
|
|
func = fround_op;
|
|
|
|
else if (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;
|
|
|
|
else if (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:
|
2013-11-07 19:48:29 +07:00
|
|
|
case mm_mfhc1_op:
|
|
|
|
case mm_mthc1_op:
|
2013-03-26 00:09:02 +07:00
|
|
|
if (insn.mm_fp1_format.op == mm_mfc1_op)
|
|
|
|
op = mfc_op;
|
|
|
|
else if (insn.mm_fp1_format.op == mm_mtc1_op)
|
|
|
|
op = mtc_op;
|
|
|
|
else if (insn.mm_fp1_format.op == mm_cfc1_op)
|
|
|
|
op = cfc_op;
|
2013-11-07 19:48:29 +07:00
|
|
|
else if (insn.mm_fp1_format.op == mm_ctc1_op)
|
2013-03-26 00:09:02 +07:00
|
|
|
op = ctc_op;
|
2013-11-07 19:48:29 +07:00
|
|
|
else if (insn.mm_fp1_format.op == mm_mfhc1_op)
|
|
|
|
op = mfhc_op;
|
|
|
|
else
|
|
|
|
op = mthc_op;
|
2013-03-26 00:09:02 +07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
MIPS: Use per-mm page to execute branch delay slot instructions
In some cases the kernel needs to execute an instruction from the delay
slot of an emulated branch instruction. These cases include:
- Emulated floating point branch instructions (bc1[ft]l?) for systems
which don't include an FPU, or upon which the kernel is run with the
"nofpu" parameter.
- MIPSr6 systems running binaries targeting older revisions of the
architecture, which may include branch instructions whose encodings
are no longer valid in MIPSr6.
Executing instructions from such delay slots is done by writing the
instruction to memory followed by a trap, as part of an "emuframe", and
executing it. This avoids the requirement of an emulator for the entire
MIPS instruction set. Prior to this patch such emuframes are written to
the user stack and executed from there.
This patch moves FP branch delay emuframes off of the user stack and
into a per-mm page. Allocating a page per-mm leaves userland with access
to only what it had access to previously, and compared to other
solutions is relatively simple.
When a thread requires a delay slot emulation, it is allocated a frame.
A thread may only have one frame allocated at any one time, since it may
only ever be executing one instruction at any one time. In order to
ensure that we can free up allocated frame later, its index is recorded
in struct thread_struct. In the typical case, after executing the delay
slot instruction we'll execute a break instruction with the BRK_MEMU
code. This traps back to the kernel & leads to a call to do_dsemulret
which frees the allocated frame & moves the user PC back to the
instruction that would have executed following the emulated branch.
In some cases the delay slot instruction may be invalid, such as a
branch, or may trigger an exception. In these cases the BRK_MEMU break
instruction will not be hit. In order to ensure that frames are freed
this patch introduces dsemul_thread_cleanup() and calls it to free any
allocated frame upon thread exit. If the instruction generated an
exception & leads to a signal being delivered to the thread, or indeed
if a signal simply happens to be delivered to the thread whilst it is
executing from the struct emuframe, then we need to take care to exit
the frame appropriately. This is done by either rolling back the user PC
to the branch or advancing it to the continuation PC prior to signal
delivery, using dsemul_thread_rollback(). If this were not done then a
sigreturn would return to the struct emuframe, and if that frame had
meanwhile been used in response to an emulated branch instruction within
the signal handler then we would execute the wrong user code.
Whilst a user could theoretically place something like a compact branch
to self in a delay slot and cause their thread to become stuck in an
infinite loop with the frame never being deallocated, this would:
- Only affect the users single process.
- Be architecturally invalid since there would be a branch in the
delay slot, which is forbidden.
- Be extremely unlikely to happen by mistake, and provide a program
with no more ability to harm the system than a simple infinite loop
would.
If a thread requires a delay slot emulation & no frame is available to
it (ie. the process has enough other threads that all frames are
currently in use) then the thread joins a waitqueue. It will sleep until
a frame is freed by another thread in the process.
Since we now know whether a thread has an allocated frame due to our
tracking of its index, the cookie field of struct emuframe is removed as
we can be more certain whether we have a valid frame. Since a thread may
only ever have a single frame at any given time, the epc field of struct
emuframe is also removed & the PC to continue from is instead stored in
struct thread_struct. Together these changes simplify & shrink struct
emuframe somewhat, allowing twice as many frames to fit into the page
allocated for them.
The primary benefit of this patch is that we are now free to mark the
user stack non-executable where that is possible.
Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Cc: Leonid Yegoshin <leonid.yegoshin@imgtec.com>
Cc: Maciej Rozycki <maciej.rozycki@imgtec.com>
Cc: Faraz Shahbazker <faraz.shahbazker@imgtec.com>
Cc: Raghu Gandham <raghu.gandham@imgtec.com>
Cc: Matthew Fortune <matthew.fortune@imgtec.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/13764/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2016-07-08 17:06:19 +07:00
|
|
|
int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
|
|
|
|
unsigned long *contpc)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2013-03-26 00:09:02 +07:00
|
|
|
union mips_instruction insn = (union mips_instruction)dec_insn.insn;
|
|
|
|
unsigned int fcr31;
|
|
|
|
unsigned int bit = 0;
|
|
|
|
|
|
|
|
switch (insn.i_format.opcode) {
|
2005-04-17 05:20:36 +07:00
|
|
|
case spec_op:
|
2013-03-26 00:09:02 +07:00
|
|
|
switch (insn.r_format.func) {
|
2005-04-17 05:20:36 +07:00
|
|
|
case jalr_op:
|
2016-04-21 20:04:55 +07:00
|
|
|
if (insn.r_format.rd != 0) {
|
|
|
|
regs->regs[insn.r_format.rd] =
|
|
|
|
regs->cp0_epc + dec_insn.pc_inc +
|
|
|
|
dec_insn.next_pc_inc;
|
|
|
|
}
|
2013-03-26 00:09:02 +07:00
|
|
|
/* Fall through */
|
2005-04-17 05:20:36 +07:00
|
|
|
case jr_op:
|
2014-11-25 22:54:14 +07:00
|
|
|
/* For R6, JR already emulated in jalr_op */
|
2015-06-24 15:52:01 +07:00
|
|
|
if (NO_R6EMU && insn.r_format.func == jr_op)
|
2014-11-25 22:54:14 +07:00
|
|
|
break;
|
2013-03-26 00:09:02 +07:00
|
|
|
*contpc = regs->regs[insn.r_format.rs];
|
2005-04-17 05:20:36 +07:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case bcond_op:
|
2013-03-26 00:09:02 +07:00
|
|
|
switch (insn.i_format.rt) {
|
|
|
|
case bltzal_op:
|
|
|
|
case bltzall_op:
|
2014-11-25 23:02:23 +07:00
|
|
|
if (NO_R6EMU && (insn.i_format.rs ||
|
|
|
|
insn.i_format.rt == bltzall_op))
|
|
|
|
break;
|
|
|
|
|
2013-03-26 00:09:02 +07:00
|
|
|
regs->regs[31] = regs->cp0_epc +
|
|
|
|
dec_insn.pc_inc +
|
|
|
|
dec_insn.next_pc_inc;
|
|
|
|
/* Fall through */
|
2005-04-17 05:20:36 +07:00
|
|
|
case bltzl_op:
|
2014-11-25 23:02:23 +07:00
|
|
|
if (NO_R6EMU)
|
|
|
|
break;
|
|
|
|
case bltz_op:
|
2013-03-26 00:09:02 +07:00
|
|
|
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;
|
2005-04-17 05:20:36 +07:00
|
|
|
case bgezal_op:
|
|
|
|
case bgezall_op:
|
2014-11-25 23:02:23 +07:00
|
|
|
if (NO_R6EMU && (insn.i_format.rs ||
|
|
|
|
insn.i_format.rt == bgezall_op))
|
|
|
|
break;
|
|
|
|
|
2013-03-26 00:09:02 +07:00
|
|
|
regs->regs[31] = regs->cp0_epc +
|
|
|
|
dec_insn.pc_inc +
|
|
|
|
dec_insn.next_pc_inc;
|
|
|
|
/* Fall through */
|
|
|
|
case bgezl_op:
|
2014-11-25 23:02:23 +07:00
|
|
|
if (NO_R6EMU)
|
|
|
|
break;
|
|
|
|
case bgez_op:
|
2013-03-26 00:09:02 +07:00
|
|
|
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;
|
2005-04-17 05:20:36 +07:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case jalx_op:
|
2013-03-26 00:09:02 +07:00
|
|
|
set_isa16_mode(bit);
|
|
|
|
case jal_op:
|
|
|
|
regs->regs[31] = regs->cp0_epc +
|
|
|
|
dec_insn.pc_inc +
|
|
|
|
dec_insn.next_pc_inc;
|
|
|
|
/* Fall through */
|
|
|
|
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;
|
2005-04-17 05:20:36 +07:00
|
|
|
case beql_op:
|
2014-11-25 23:02:23 +07:00
|
|
|
if (NO_R6EMU)
|
|
|
|
break;
|
|
|
|
case beq_op:
|
2013-03-26 00:09:02 +07:00
|
|
|
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;
|
2005-04-17 05:20:36 +07:00
|
|
|
case bnel_op:
|
2014-11-25 23:02:23 +07:00
|
|
|
if (NO_R6EMU)
|
|
|
|
break;
|
|
|
|
case bne_op:
|
2013-03-26 00:09:02 +07:00
|
|
|
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;
|
2005-04-17 05:20:36 +07:00
|
|
|
case blezl_op:
|
MIPS: Fix branch emulation for BLTC and BGEC instructions
Commits f1b44067c19258b7614e3cd09dfe8d8e12ff5895 ("MIPS: Emulate the
new MIPS R6 B{L,G}T{Z,}{AL,}C instructions") and commit
a8ff66f52d3f17b5ae793955270675c197f73d6c ("MIPS: Emulate the new MIPS
R6 B{L,G}E{Z,}{AL,}C instructions") added support for emulating various
branch compact instructions. However, it missed the case for those which
use the old BLEZL and BGTZL opcodes leading to random crashes when the R6
emulator is disabled. We fix this by ensuring that the 'rt' field is not
zero which is always true for these branch compact instructions.
Fixes: f1b44067c192 ("MIPS: Emulate the new MIPS R6 B{L,G}T{Z,}{AL,}C instructions")
Fixes: a8ff66f52d3f ("MIPS: Emulate the new MIPS R6 B{L,G}E{Z,}{AL,}C instructions")
Cc: <stable@vger.kernel.org> # 4.0+
Signed-off-by: Markos Chandras <markos.chandras@imgtec.com>
Cc: linux-mips@linux-mips.org
Cc: Markos Chandras <markos.chandras@imgtec.com>
Patchwork: https://patchwork.linux-mips.org/patch/10582/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-06-24 15:52:00 +07:00
|
|
|
if (!insn.i_format.rt && NO_R6EMU)
|
2014-11-25 23:02:23 +07:00
|
|
|
break;
|
|
|
|
case blez_op:
|
2014-11-26 19:57:54 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Compact branches for R6 for the
|
|
|
|
* blez and blezl opcodes.
|
|
|
|
* BLEZ | rs = 0 | rt != 0 == BLEZALC
|
|
|
|
* BLEZ | rs = rt != 0 == BGEZALC
|
|
|
|
* BLEZ | rs != 0 | rt != 0 == BGEUC
|
|
|
|
* BLEZL | rs = 0 | rt != 0 == BLEZC
|
|
|
|
* BLEZL | rs = rt != 0 == BGEZC
|
|
|
|
* BLEZL | rs != 0 | rt != 0 == BGEC
|
|
|
|
*
|
|
|
|
* For real BLEZ{,L}, rt is always 0.
|
|
|
|
*/
|
|
|
|
if (cpu_has_mips_r6 && insn.i_format.rt) {
|
|
|
|
if ((insn.i_format.opcode == blez_op) &&
|
|
|
|
((!insn.i_format.rs && insn.i_format.rt) ||
|
|
|
|
(insn.i_format.rs == insn.i_format.rt)))
|
|
|
|
regs->regs[31] = regs->cp0_epc +
|
|
|
|
dec_insn.pc_inc;
|
|
|
|
*contpc = regs->cp0_epc + dec_insn.pc_inc +
|
|
|
|
dec_insn.next_pc_inc;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
2013-03-26 00:09:02 +07:00
|
|
|
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;
|
2005-04-17 05:20:36 +07:00
|
|
|
case bgtzl_op:
|
MIPS: Fix branch emulation for BLTC and BGEC instructions
Commits f1b44067c19258b7614e3cd09dfe8d8e12ff5895 ("MIPS: Emulate the
new MIPS R6 B{L,G}T{Z,}{AL,}C instructions") and commit
a8ff66f52d3f17b5ae793955270675c197f73d6c ("MIPS: Emulate the new MIPS
R6 B{L,G}E{Z,}{AL,}C instructions") added support for emulating various
branch compact instructions. However, it missed the case for those which
use the old BLEZL and BGTZL opcodes leading to random crashes when the R6
emulator is disabled. We fix this by ensuring that the 'rt' field is not
zero which is always true for these branch compact instructions.
Fixes: f1b44067c192 ("MIPS: Emulate the new MIPS R6 B{L,G}T{Z,}{AL,}C instructions")
Fixes: a8ff66f52d3f ("MIPS: Emulate the new MIPS R6 B{L,G}E{Z,}{AL,}C instructions")
Cc: <stable@vger.kernel.org> # 4.0+
Signed-off-by: Markos Chandras <markos.chandras@imgtec.com>
Cc: linux-mips@linux-mips.org
Cc: Markos Chandras <markos.chandras@imgtec.com>
Patchwork: https://patchwork.linux-mips.org/patch/10582/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-06-24 15:52:00 +07:00
|
|
|
if (!insn.i_format.rt && NO_R6EMU)
|
2014-11-25 23:02:23 +07:00
|
|
|
break;
|
|
|
|
case bgtz_op:
|
2014-11-26 20:05:09 +07:00
|
|
|
/*
|
|
|
|
* Compact branches for R6 for the
|
|
|
|
* bgtz and bgtzl opcodes.
|
|
|
|
* BGTZ | rs = 0 | rt != 0 == BGTZALC
|
|
|
|
* BGTZ | rs = rt != 0 == BLTZALC
|
|
|
|
* BGTZ | rs != 0 | rt != 0 == BLTUC
|
|
|
|
* BGTZL | rs = 0 | rt != 0 == BGTZC
|
|
|
|
* BGTZL | rs = rt != 0 == BLTZC
|
|
|
|
* BGTZL | rs != 0 | rt != 0 == BLTC
|
|
|
|
*
|
|
|
|
* *ZALC varint for BGTZ &&& rt != 0
|
|
|
|
* For real GTZ{,L}, rt is always 0.
|
|
|
|
*/
|
|
|
|
if (cpu_has_mips_r6 && insn.i_format.rt) {
|
|
|
|
if ((insn.i_format.opcode == blez_op) &&
|
|
|
|
((!insn.i_format.rs && insn.i_format.rt) ||
|
|
|
|
(insn.i_format.rs == insn.i_format.rt)))
|
|
|
|
regs->regs[31] = regs->cp0_epc +
|
|
|
|
dec_insn.pc_inc;
|
|
|
|
*contpc = regs->cp0_epc + dec_insn.pc_inc +
|
|
|
|
dec_insn.next_pc_inc;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-03-26 00:09:02 +07:00
|
|
|
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;
|
2005-04-17 05:20:36 +07:00
|
|
|
return 1;
|
2016-07-05 01:35:08 +07:00
|
|
|
case pop10_op:
|
|
|
|
case pop30_op:
|
2014-11-26 21:08:52 +07:00
|
|
|
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;
|
2013-08-20 02:10:34 +07:00
|
|
|
#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;
|
2014-11-26 20:56:51 +07:00
|
|
|
#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;
|
|
|
|
|
2014-11-26 22:43:11 +07:00
|
|
|
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;
|
|
|
|
|
2014-11-27 16:32:25 +07:00
|
|
|
return 1;
|
2016-07-05 01:35:07 +07:00
|
|
|
case pop66_op:
|
2014-11-27 16:32:25 +07:00
|
|
|
if (!cpu_has_mips_r6)
|
|
|
|
break;
|
|
|
|
*contpc = regs->cp0_epc + dec_insn.pc_inc +
|
|
|
|
dec_insn.next_pc_inc;
|
|
|
|
|
2015-01-08 18:55:20 +07:00
|
|
|
return 1;
|
2016-07-05 01:35:07 +07:00
|
|
|
case pop76_op:
|
2015-01-08 18:55:20 +07:00
|
|
|
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;
|
|
|
|
|
2014-11-26 20:56:51 +07:00
|
|
|
return 1;
|
2013-08-20 02:10:34 +07:00
|
|
|
#endif
|
2005-04-17 05:20:36 +07:00
|
|
|
case cop0_op:
|
|
|
|
case cop1_op:
|
2014-11-26 17:10:18 +07:00
|
|
|
/* 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;
|
|
|
|
switch (insn.i_format.rs) {
|
|
|
|
case bc1eqz_op:
|
|
|
|
if (get_fpr32(¤t->thread.fpu.fpr[insn.i_format.rt], 0) & 0x1)
|
|
|
|
bit = 1;
|
|
|
|
break;
|
|
|
|
case bc1nez_op:
|
|
|
|
if (!(get_fpr32(¤t->thread.fpu.fpr[insn.i_format.rt], 0) & 0x1))
|
|
|
|
bit = 1;
|
|
|
|
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. Fall through */
|
2005-04-17 05:20:36 +07:00
|
|
|
case cop2_op:
|
|
|
|
case cop1x_op:
|
2013-03-26 00:09:02 +07:00
|
|
|
if (insn.i_format.rs == bc_op) {
|
|
|
|
preempt_disable();
|
|
|
|
if (is_fpu_owner())
|
2014-11-07 20:13:54 +07:00
|
|
|
fcr31 = read_32bit_cp1_register(CP1_STATUS);
|
2013-03-26 00:09:02 +07:00
|
|
|
else
|
|
|
|
fcr31 = current->thread.fpu.fcr31;
|
|
|
|
preempt_enable();
|
|
|
|
|
|
|
|
bit = (insn.i_format.rt >> 2);
|
|
|
|
bit += (bit != 0);
|
|
|
|
bit += 23;
|
|
|
|
switch (insn.i_format.rt & 3) {
|
|
|
|
case 0: /* bc1f */
|
|
|
|
case 2: /* bc1fl */
|
|
|
|
if (~fcr31 & (1 << 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;
|
|
|
|
case 1: /* bc1t */
|
|
|
|
case 3: /* bc1tl */
|
|
|
|
if (fcr31 & (1 << 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;
|
|
|
|
}
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In the Linux kernel, we support selection of FPR format on the
|
2013-01-22 18:59:30 +07:00
|
|
|
* basis of the Status.FR bit. If an FPU is not present, the FR bit
|
2009-11-03 02:33:46 +07:00
|
|
|
* is hardwired to zero, which would imply a 32-bit FPU even for
|
2013-11-22 20:12:07 +07:00
|
|
|
* 64-bit CPUs so we rather look at TIF_32BIT_FPREGS.
|
2012-08-16 00:42:19 +07:00
|
|
|
* 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
|
2013-11-22 20:12:07 +07:00
|
|
|
* compatibility enabled and on 32-bit without 64-bit FPU support.
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
2009-11-03 02:33:46 +07:00
|
|
|
static inline int cop1_64bit(struct pt_regs *xcp)
|
|
|
|
{
|
2016-08-04 03:45:50 +07:00
|
|
|
if (IS_ENABLED(CONFIG_64BIT) && !IS_ENABLED(CONFIG_MIPS32_O32))
|
2014-04-19 18:11:37 +07:00
|
|
|
return 1;
|
2016-08-04 03:45:50 +07:00
|
|
|
else if (IS_ENABLED(CONFIG_32BIT) &&
|
|
|
|
!IS_ENABLED(CONFIG_MIPS_O32_FP64_SUPPORT))
|
2014-04-19 18:11:37 +07:00
|
|
|
return 0;
|
|
|
|
|
2013-11-22 20:12:07 +07:00
|
|
|
return !test_thread_flag(TIF_32BIT_FPREGS);
|
2009-11-03 02:33:46 +07:00
|
|
|
}
|
|
|
|
|
2014-09-11 14:30:20 +07:00
|
|
|
static inline bool hybrid_fprs(void)
|
|
|
|
{
|
|
|
|
return test_thread_flag(TIF_HYBRID_FPREGS);
|
|
|
|
}
|
|
|
|
|
2014-04-16 16:00:12 +07:00
|
|
|
#define SIFROMREG(si, x) \
|
|
|
|
do { \
|
2014-09-11 14:30:20 +07:00
|
|
|
if (cop1_64bit(xcp) && !hybrid_fprs()) \
|
2014-09-24 16:45:37 +07:00
|
|
|
(si) = (int)get_fpr32(&ctx->fpr[x], 0); \
|
2014-02-13 18:26:41 +07:00
|
|
|
else \
|
2014-09-24 16:45:37 +07:00
|
|
|
(si) = (int)get_fpr32(&ctx->fpr[(x) & ~1], (x) & 1); \
|
2014-02-13 18:26:41 +07:00
|
|
|
} while (0)
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2014-04-16 16:00:12 +07:00
|
|
|
#define SITOREG(si, x) \
|
|
|
|
do { \
|
2014-09-11 14:30:20 +07:00
|
|
|
if (cop1_64bit(xcp) && !hybrid_fprs()) { \
|
2014-01-28 00:14:47 +07:00
|
|
|
unsigned i; \
|
2014-02-13 18:26:41 +07:00
|
|
|
set_fpr32(&ctx->fpr[x], 0, si); \
|
2014-01-28 00:14:47 +07:00
|
|
|
for (i = 1; i < ARRAY_SIZE(ctx->fpr[x].val32); i++) \
|
|
|
|
set_fpr32(&ctx->fpr[x], i, 0); \
|
|
|
|
} else { \
|
2014-02-13 18:26:41 +07:00
|
|
|
set_fpr32(&ctx->fpr[(x) & ~1], (x) & 1, si); \
|
2014-01-28 00:14:47 +07:00
|
|
|
} \
|
2014-02-13 18:26:41 +07:00
|
|
|
} while (0)
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2014-09-24 16:45:37 +07:00
|
|
|
#define SIFROMHREG(si, x) ((si) = (int)get_fpr32(&ctx->fpr[x], 1))
|
2014-01-28 00:14:47 +07:00
|
|
|
|
2014-04-16 16:00:12 +07:00
|
|
|
#define SITOHREG(si, x) \
|
|
|
|
do { \
|
2014-01-28 00:14:47 +07:00
|
|
|
unsigned 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)
|
2013-11-07 19:48:28 +07:00
|
|
|
|
2014-04-16 16:00:12 +07:00
|
|
|
#define DIFROMREG(di, x) \
|
2014-02-13 18:26:41 +07:00
|
|
|
((di) = get_fpr64(&ctx->fpr[(x) & ~(cop1_64bit(xcp) == 0)], 0))
|
|
|
|
|
2014-04-16 16:00:12 +07:00
|
|
|
#define DITOREG(di, x) \
|
|
|
|
do { \
|
2014-01-28 00:14:47 +07:00
|
|
|
unsigned fpr, i; \
|
|
|
|
fpr = (x) & ~(cop1_64bit(xcp) == 0); \
|
|
|
|
set_fpr64(&ctx->fpr[fpr], 0, di); \
|
|
|
|
for (i = 1; i < ARRAY_SIZE(ctx->fpr[x].val64); i++) \
|
|
|
|
set_fpr64(&ctx->fpr[fpr], i, 0); \
|
|
|
|
} while (0)
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-10-12 05:46:15 +07:00
|
|
|
#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)
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2015-04-04 05:25:04 +07:00
|
|
|
/*
|
|
|
|
* Emulate a CFC1 instruction.
|
|
|
|
*/
|
|
|
|
static inline void cop1_cfc(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
|
|
|
mips_instruction ir)
|
|
|
|
{
|
2015-04-04 05:27:33 +07:00
|
|
|
u32 fcr31 = ctx->fcr31;
|
|
|
|
u32 value = 0;
|
2015-04-04 05:25:04 +07:00
|
|
|
|
2015-04-04 05:27:33 +07:00
|
|
|
switch (MIPSInst_RD(ir)) {
|
|
|
|
case FPCREG_CSR:
|
|
|
|
value = fcr31;
|
2015-04-04 05:25:04 +07:00
|
|
|
pr_debug("%p gpr[%d]<-csr=%08x\n",
|
2015-04-04 05:27:33 +07:00
|
|
|
(void *)xcp->cp0_epc, MIPSInst_RT(ir), value);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FPCREG_FENR:
|
|
|
|
if (!cpu_has_mips_r)
|
|
|
|
break;
|
|
|
|
value = (fcr31 >> (FPU_CSR_FS_S - MIPS_FENR_FS_S)) &
|
|
|
|
MIPS_FENR_FS;
|
|
|
|
value |= fcr31 & (FPU_CSR_ALL_E | FPU_CSR_RM);
|
|
|
|
pr_debug("%p gpr[%d]<-enr=%08x\n",
|
|
|
|
(void *)xcp->cp0_epc, MIPSInst_RT(ir), value);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FPCREG_FEXR:
|
|
|
|
if (!cpu_has_mips_r)
|
|
|
|
break;
|
|
|
|
value = fcr31 & (FPU_CSR_ALL_X | FPU_CSR_ALL_S);
|
|
|
|
pr_debug("%p gpr[%d]<-exr=%08x\n",
|
|
|
|
(void *)xcp->cp0_epc, MIPSInst_RT(ir), value);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FPCREG_FCCR:
|
|
|
|
if (!cpu_has_mips_r)
|
|
|
|
break;
|
|
|
|
value = (fcr31 >> (FPU_CSR_COND_S - MIPS_FCCR_COND0_S)) &
|
|
|
|
MIPS_FCCR_COND0;
|
|
|
|
value |= (fcr31 >> (FPU_CSR_COND1_S - MIPS_FCCR_COND1_S)) &
|
|
|
|
(MIPS_FCCR_CONDX & ~MIPS_FCCR_COND0);
|
|
|
|
pr_debug("%p gpr[%d]<-ccr=%08x\n",
|
|
|
|
(void *)xcp->cp0_epc, MIPSInst_RT(ir), value);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FPCREG_RID:
|
2015-05-12 21:20:57 +07:00
|
|
|
value = boot_cpu_data.fpu_id;
|
2015-04-04 05:27:33 +07:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-04-04 05:25:04 +07:00
|
|
|
if (MIPSInst_RT(ir))
|
|
|
|
xcp->regs[MIPSInst_RT(ir)] = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Emulate a CTC1 instruction.
|
|
|
|
*/
|
|
|
|
static inline void cop1_ctc(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
|
|
|
mips_instruction ir)
|
|
|
|
{
|
2015-04-04 05:27:33 +07:00
|
|
|
u32 fcr31 = ctx->fcr31;
|
2015-04-04 05:25:04 +07:00
|
|
|
u32 value;
|
2015-04-04 05:27:48 +07:00
|
|
|
u32 mask;
|
2015-04-04 05:25:04 +07:00
|
|
|
|
|
|
|
if (MIPSInst_RT(ir) == 0)
|
|
|
|
value = 0;
|
|
|
|
else
|
|
|
|
value = xcp->regs[MIPSInst_RT(ir)];
|
|
|
|
|
2015-04-04 05:27:33 +07:00
|
|
|
switch (MIPSInst_RD(ir)) {
|
|
|
|
case FPCREG_CSR:
|
2015-04-04 05:25:04 +07:00
|
|
|
pr_debug("%p gpr[%d]->csr=%08x\n",
|
2015-04-04 05:27:33 +07:00
|
|
|
(void *)xcp->cp0_epc, MIPSInst_RT(ir), value);
|
2015-04-04 05:25:04 +07:00
|
|
|
|
2015-04-04 05:27:48 +07:00
|
|
|
/* Preserve read-only bits. */
|
2015-05-12 21:20:57 +07:00
|
|
|
mask = boot_cpu_data.fpu_msk31;
|
2015-04-04 05:27:48 +07:00
|
|
|
fcr31 = (value & ~mask) | (fcr31 & mask);
|
2015-04-04 05:27:33 +07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case FPCREG_FENR:
|
|
|
|
if (!cpu_has_mips_r)
|
|
|
|
break;
|
|
|
|
pr_debug("%p gpr[%d]->enr=%08x\n",
|
|
|
|
(void *)xcp->cp0_epc, MIPSInst_RT(ir), value);
|
|
|
|
fcr31 &= ~(FPU_CSR_FS | FPU_CSR_ALL_E | FPU_CSR_RM);
|
|
|
|
fcr31 |= (value << (FPU_CSR_FS_S - MIPS_FENR_FS_S)) &
|
|
|
|
FPU_CSR_FS;
|
|
|
|
fcr31 |= value & (FPU_CSR_ALL_E | FPU_CSR_RM);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FPCREG_FEXR:
|
|
|
|
if (!cpu_has_mips_r)
|
|
|
|
break;
|
|
|
|
pr_debug("%p gpr[%d]->exr=%08x\n",
|
|
|
|
(void *)xcp->cp0_epc, MIPSInst_RT(ir), value);
|
|
|
|
fcr31 &= ~(FPU_CSR_ALL_X | FPU_CSR_ALL_S);
|
|
|
|
fcr31 |= value & (FPU_CSR_ALL_X | FPU_CSR_ALL_S);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FPCREG_FCCR:
|
|
|
|
if (!cpu_has_mips_r)
|
|
|
|
break;
|
|
|
|
pr_debug("%p gpr[%d]->ccr=%08x\n",
|
|
|
|
(void *)xcp->cp0_epc, MIPSInst_RT(ir), value);
|
|
|
|
fcr31 &= ~(FPU_CSR_CONDX | FPU_CSR_COND);
|
|
|
|
fcr31 |= (value << (FPU_CSR_COND_S - MIPS_FCCR_COND0_S)) &
|
|
|
|
FPU_CSR_COND;
|
|
|
|
fcr31 |= (value << (FPU_CSR_COND1_S - MIPS_FCCR_COND1_S)) &
|
|
|
|
FPU_CSR_CONDX;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
2015-04-04 05:25:04 +07:00
|
|
|
}
|
2015-04-04 05:27:33 +07:00
|
|
|
|
|
|
|
ctx->fcr31 = fcr31;
|
2015-04-04 05:25:04 +07:00
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* Emulate the single floating point instruction pointed at by EPC.
|
|
|
|
* Two instructions if the instruction is in a branch delay slot.
|
|
|
|
*/
|
|
|
|
|
2010-10-22 06:32:26 +07:00
|
|
|
static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
2013-03-26 00:09:02 +07:00
|
|
|
struct mm_decoded_insn dec_insn, void *__user *fault_addr)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2013-03-26 00:09:02 +07:00
|
|
|
unsigned long contpc = xcp->cp0_epc + dec_insn.pc_inc;
|
2016-04-21 20:04:45 +07:00
|
|
|
unsigned int cond, cbit, bit0;
|
2014-04-26 06:49:14 +07:00
|
|
|
mips_instruction ir;
|
|
|
|
int likely, pc_inc;
|
2016-04-21 20:04:45 +07:00
|
|
|
union fpureg *fpr;
|
2014-04-26 06:49:14 +07:00
|
|
|
u32 __user *wva;
|
|
|
|
u64 __user *dva;
|
|
|
|
u32 wval;
|
|
|
|
u64 dval;
|
|
|
|
int sig;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2014-04-30 16:09:44 +07:00
|
|
|
/*
|
|
|
|
* 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();
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* XXX NEC Vr54xx bug workaround */
|
2014-04-16 06:59:03 +07:00
|
|
|
if (delay_slot(xcp)) {
|
2013-03-26 00:09:02 +07:00
|
|
|
if (dec_insn.micro_mips_mode) {
|
|
|
|
if (!mm_isBranchInstr(xcp, dec_insn, &contpc))
|
2014-04-16 06:59:03 +07:00
|
|
|
clear_delay_slot(xcp);
|
2013-03-26 00:09:02 +07:00
|
|
|
} else {
|
|
|
|
if (!isBranchInstr(xcp, dec_insn, &contpc))
|
2014-04-16 06:59:03 +07:00
|
|
|
clear_delay_slot(xcp);
|
2013-03-26 00:09:02 +07:00
|
|
|
}
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2014-04-16 06:59:03 +07:00
|
|
|
if (delay_slot(xcp)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* The instruction to be emulated is in a branch delay slot
|
2013-01-22 18:59:30 +07:00
|
|
|
* which means that we have to emulate the branch instruction
|
2005-04-17 05:20:36 +07:00
|
|
|
* 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.
|
|
|
|
*/
|
2013-03-26 00:09:02 +07:00
|
|
|
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;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2013-03-26 00:09:02 +07:00
|
|
|
/*
|
|
|
|
* 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))
|
2005-04-17 05:20:36 +07:00
|
|
|
return SIGILL;
|
|
|
|
}
|
|
|
|
|
2014-04-26 06:49:14 +07:00
|
|
|
emul:
|
2011-06-27 19:41:57 +07:00
|
|
|
perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, xcp, 0);
|
2009-11-06 02:34:26 +07:00
|
|
|
MIPS_FPU_EMU_INC_STATS(emulated);
|
2005-04-17 05:20:36 +07:00
|
|
|
switch (MIPSInst_OPCODE(ir)) {
|
2014-04-26 06:49:14 +07:00
|
|
|
case ldc1_op:
|
|
|
|
dva = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] +
|
|
|
|
MIPSInst_SIMM(ir));
|
2009-11-06 02:34:26 +07:00
|
|
|
MIPS_FPU_EMU_INC_STATS(loads);
|
2010-10-22 06:32:26 +07:00
|
|
|
|
2014-04-26 06:49:14 +07:00
|
|
|
if (!access_ok(VERIFY_READ, dva, sizeof(u64))) {
|
2009-11-06 02:34:26 +07:00
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
2014-04-26 06:49:14 +07:00
|
|
|
*fault_addr = dva;
|
2005-04-17 05:20:36 +07:00
|
|
|
return SIGBUS;
|
|
|
|
}
|
2014-04-26 06:49:14 +07:00
|
|
|
if (__get_user(dval, dva)) {
|
2010-10-22 06:32:26 +07:00
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
2014-04-26 06:49:14 +07:00
|
|
|
*fault_addr = dva;
|
2010-10-22 06:32:26 +07:00
|
|
|
return SIGSEGV;
|
|
|
|
}
|
2014-04-26 06:49:14 +07:00
|
|
|
DITOREG(dval, MIPSInst_RT(ir));
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
|
2014-04-26 06:49:14 +07:00
|
|
|
case sdc1_op:
|
|
|
|
dva = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] +
|
|
|
|
MIPSInst_SIMM(ir));
|
2009-11-06 02:34:26 +07:00
|
|
|
MIPS_FPU_EMU_INC_STATS(stores);
|
2014-04-26 06:49:14 +07:00
|
|
|
DIFROMREG(dval, MIPSInst_RT(ir));
|
|
|
|
if (!access_ok(VERIFY_WRITE, dva, sizeof(u64))) {
|
2009-11-06 02:34:26 +07:00
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
2014-04-26 06:49:14 +07:00
|
|
|
*fault_addr = dva;
|
2005-04-17 05:20:36 +07:00
|
|
|
return SIGBUS;
|
|
|
|
}
|
2014-04-26 06:49:14 +07:00
|
|
|
if (__put_user(dval, dva)) {
|
2010-10-22 06:32:26 +07:00
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
2014-04-26 06:49:14 +07:00
|
|
|
*fault_addr = dva;
|
2010-10-22 06:32:26 +07:00
|
|
|
return SIGSEGV;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
|
2014-04-26 06:49:14 +07:00
|
|
|
case lwc1_op:
|
|
|
|
wva = (u32 __user *) (xcp->regs[MIPSInst_RS(ir)] +
|
|
|
|
MIPSInst_SIMM(ir));
|
2009-11-06 02:34:26 +07:00
|
|
|
MIPS_FPU_EMU_INC_STATS(loads);
|
2014-04-26 06:49:14 +07:00
|
|
|
if (!access_ok(VERIFY_READ, wva, sizeof(u32))) {
|
2009-11-06 02:34:26 +07:00
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
2014-04-26 06:49:14 +07:00
|
|
|
*fault_addr = wva;
|
2005-04-17 05:20:36 +07:00
|
|
|
return SIGBUS;
|
|
|
|
}
|
2014-04-26 06:49:14 +07:00
|
|
|
if (__get_user(wval, wva)) {
|
2010-10-22 06:32:26 +07:00
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
2014-04-26 06:49:14 +07:00
|
|
|
*fault_addr = wva;
|
2010-10-22 06:32:26 +07:00
|
|
|
return SIGSEGV;
|
|
|
|
}
|
2014-04-26 06:49:14 +07:00
|
|
|
SITOREG(wval, MIPSInst_RT(ir));
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
|
2014-04-26 06:49:14 +07:00
|
|
|
case swc1_op:
|
|
|
|
wva = (u32 __user *) (xcp->regs[MIPSInst_RS(ir)] +
|
|
|
|
MIPSInst_SIMM(ir));
|
2009-11-06 02:34:26 +07:00
|
|
|
MIPS_FPU_EMU_INC_STATS(stores);
|
2014-04-26 06:49:14 +07:00
|
|
|
SIFROMREG(wval, MIPSInst_RT(ir));
|
|
|
|
if (!access_ok(VERIFY_WRITE, wva, sizeof(u32))) {
|
2009-11-06 02:34:26 +07:00
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
2014-04-26 06:49:14 +07:00
|
|
|
*fault_addr = wva;
|
2005-04-17 05:20:36 +07:00
|
|
|
return SIGBUS;
|
|
|
|
}
|
2014-04-26 06:49:14 +07:00
|
|
|
if (__put_user(wval, wva)) {
|
2010-10-22 06:32:26 +07:00
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
2014-04-26 06:49:14 +07:00
|
|
|
*fault_addr = wva;
|
2010-10-22 06:32:26 +07:00
|
|
|
return SIGSEGV;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case cop1_op:
|
|
|
|
switch (MIPSInst_RS(ir)) {
|
|
|
|
case dmfc_op:
|
2014-04-19 18:11:37 +07:00
|
|
|
if (!cpu_has_mips_3_4_5 && !cpu_has_mips64)
|
|
|
|
return SIGILL;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* copregister fs -> gpr[rt] */
|
|
|
|
if (MIPSInst_RT(ir) != 0) {
|
|
|
|
DIFROMREG(xcp->regs[MIPSInst_RT(ir)],
|
|
|
|
MIPSInst_RD(ir));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case dmtc_op:
|
2014-04-19 18:11:37 +07:00
|
|
|
if (!cpu_has_mips_3_4_5 && !cpu_has_mips64)
|
|
|
|
return SIGILL;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* copregister fs <- rt */
|
|
|
|
DITOREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir));
|
|
|
|
break;
|
|
|
|
|
2013-11-07 19:48:28 +07:00
|
|
|
case mfhc_op:
|
2015-07-17 16:36:03 +07:00
|
|
|
if (!cpu_has_mips_r2_r6)
|
2013-11-07 19:48:28 +07:00
|
|
|
goto sigill;
|
|
|
|
|
|
|
|
/* copregister rd -> gpr[rt] */
|
|
|
|
if (MIPSInst_RT(ir) != 0) {
|
|
|
|
SIFROMHREG(xcp->regs[MIPSInst_RT(ir)],
|
|
|
|
MIPSInst_RD(ir));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case mthc_op:
|
2015-07-17 16:36:03 +07:00
|
|
|
if (!cpu_has_mips_r2_r6)
|
2013-11-07 19:48:28 +07:00
|
|
|
goto sigill;
|
|
|
|
|
|
|
|
/* copregister rd <- gpr[rt] */
|
|
|
|
SITOHREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir));
|
|
|
|
break;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
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;
|
|
|
|
|
2014-04-26 06:49:14 +07:00
|
|
|
case cfc_op:
|
2005-04-17 05:20:36 +07:00
|
|
|
/* cop control register rd -> gpr[rt] */
|
2015-04-04 05:25:04 +07:00
|
|
|
cop1_cfc(xcp, ctx, ir);
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
|
2014-04-26 06:49:14 +07:00
|
|
|
case ctc_op:
|
2005-04-17 05:20:36 +07:00
|
|
|
/* copregister rd <- rt */
|
2015-04-04 05:25:04 +07:00
|
|
|
cop1_ctc(xcp, ctx, ir);
|
2005-04-17 05:20:36 +07:00
|
|
|
if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) {
|
|
|
|
return SIGFPE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2015-07-17 16:38:32 +07:00
|
|
|
case bc1eqz_op:
|
|
|
|
case bc1nez_op:
|
|
|
|
if (!cpu_has_mips_r6 || delay_slot(xcp))
|
|
|
|
return SIGILL;
|
|
|
|
|
|
|
|
cond = likely = 0;
|
2016-04-21 20:04:45 +07:00
|
|
|
fpr = ¤t->thread.fpu.fpr[MIPSInst_RT(ir)];
|
|
|
|
bit0 = get_fpr32(fpr, 0) & 0x1;
|
2015-07-17 16:38:32 +07:00
|
|
|
switch (MIPSInst_RS(ir)) {
|
|
|
|
case bc1eqz_op:
|
2016-04-21 20:04:45 +07:00
|
|
|
cond = bit0 == 0;
|
2015-07-17 16:38:32 +07:00
|
|
|
break;
|
|
|
|
case bc1nez_op:
|
2016-04-21 20:04:45 +07:00
|
|
|
cond = bit0 != 0;
|
2015-07-17 16:38:32 +07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
goto branch_common;
|
|
|
|
|
2014-04-26 06:49:14 +07:00
|
|
|
case bc_op:
|
2014-04-16 06:59:03 +07:00
|
|
|
if (delay_slot(xcp))
|
2005-04-17 05:20:36 +07:00
|
|
|
return SIGILL;
|
|
|
|
|
2014-04-19 18:11:37 +07:00
|
|
|
if (cpu_has_mips_4_5_r)
|
|
|
|
cbit = fpucondbit[MIPSInst_RT(ir) >> 2];
|
|
|
|
else
|
|
|
|
cbit = FPU_CSR_COND;
|
|
|
|
cond = ctx->fcr31 & cbit;
|
|
|
|
|
2014-04-26 06:49:14 +07:00
|
|
|
likely = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
switch (MIPSInst_RT(ir) & 3) {
|
|
|
|
case bcfl_op:
|
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 05:26:49 +07:00
|
|
|
if (cpu_has_mips_2_3_4_5_r)
|
|
|
|
likely = 1;
|
|
|
|
/* Fall through */
|
2005-04-17 05:20:36 +07:00
|
|
|
case bcf_op:
|
|
|
|
cond = !cond;
|
|
|
|
break;
|
|
|
|
case bctl_op:
|
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 05:26:49 +07:00
|
|
|
if (cpu_has_mips_2_3_4_5_r)
|
|
|
|
likely = 1;
|
|
|
|
/* Fall through */
|
2005-04-17 05:20:36 +07:00
|
|
|
case bct_op:
|
|
|
|
break;
|
|
|
|
}
|
2015-07-17 16:38:32 +07:00
|
|
|
branch_common:
|
2014-04-16 06:59:03 +07:00
|
|
|
set_delay_slot(xcp);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (cond) {
|
2014-04-26 06:49:14 +07:00
|
|
|
/*
|
|
|
|
* Branch taken: emulate dslot instruction
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
2015-04-04 05:26:56 +07:00
|
|
|
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;
|
2013-03-26 00:09:02 +07:00
|
|
|
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.
|
|
|
|
*/
|
2015-04-04 05:26:56 +07:00
|
|
|
sig = mips_dsemul(xcp, ir,
|
MIPS: Use per-mm page to execute branch delay slot instructions
In some cases the kernel needs to execute an instruction from the delay
slot of an emulated branch instruction. These cases include:
- Emulated floating point branch instructions (bc1[ft]l?) for systems
which don't include an FPU, or upon which the kernel is run with the
"nofpu" parameter.
- MIPSr6 systems running binaries targeting older revisions of the
architecture, which may include branch instructions whose encodings
are no longer valid in MIPSr6.
Executing instructions from such delay slots is done by writing the
instruction to memory followed by a trap, as part of an "emuframe", and
executing it. This avoids the requirement of an emulator for the entire
MIPS instruction set. Prior to this patch such emuframes are written to
the user stack and executed from there.
This patch moves FP branch delay emuframes off of the user stack and
into a per-mm page. Allocating a page per-mm leaves userland with access
to only what it had access to previously, and compared to other
solutions is relatively simple.
When a thread requires a delay slot emulation, it is allocated a frame.
A thread may only have one frame allocated at any one time, since it may
only ever be executing one instruction at any one time. In order to
ensure that we can free up allocated frame later, its index is recorded
in struct thread_struct. In the typical case, after executing the delay
slot instruction we'll execute a break instruction with the BRK_MEMU
code. This traps back to the kernel & leads to a call to do_dsemulret
which frees the allocated frame & moves the user PC back to the
instruction that would have executed following the emulated branch.
In some cases the delay slot instruction may be invalid, such as a
branch, or may trigger an exception. In these cases the BRK_MEMU break
instruction will not be hit. In order to ensure that frames are freed
this patch introduces dsemul_thread_cleanup() and calls it to free any
allocated frame upon thread exit. If the instruction generated an
exception & leads to a signal being delivered to the thread, or indeed
if a signal simply happens to be delivered to the thread whilst it is
executing from the struct emuframe, then we need to take care to exit
the frame appropriately. This is done by either rolling back the user PC
to the branch or advancing it to the continuation PC prior to signal
delivery, using dsemul_thread_rollback(). If this were not done then a
sigreturn would return to the struct emuframe, and if that frame had
meanwhile been used in response to an emulated branch instruction within
the signal handler then we would execute the wrong user code.
Whilst a user could theoretically place something like a compact branch
to self in a delay slot and cause their thread to become stuck in an
infinite loop with the frame never being deallocated, this would:
- Only affect the users single process.
- Be architecturally invalid since there would be a branch in the
delay slot, which is forbidden.
- Be extremely unlikely to happen by mistake, and provide a program
with no more ability to harm the system than a simple infinite loop
would.
If a thread requires a delay slot emulation & no frame is available to
it (ie. the process has enough other threads that all frames are
currently in use) then the thread joins a waitqueue. It will sleep until
a frame is freed by another thread in the process.
Since we now know whether a thread has an allocated frame due to our
tracking of its index, the cookie field of struct emuframe is removed as
we can be more certain whether we have a valid frame. Since a thread may
only ever have a single frame at any given time, the epc field of struct
emuframe is also removed & the PC to continue from is instead stored in
struct thread_struct. Together these changes simplify & shrink struct
emuframe somewhat, allowing twice as many frames to fit into the page
allocated for them.
The primary benefit of this patch is that we are now free to mark the
user stack non-executable where that is possible.
Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Cc: Leonid Yegoshin <leonid.yegoshin@imgtec.com>
Cc: Maciej Rozycki <maciej.rozycki@imgtec.com>
Cc: Faraz Shahbazker <faraz.shahbazker@imgtec.com>
Cc: Raghu Gandham <raghu.gandham@imgtec.com>
Cc: Matthew Fortune <matthew.fortune@imgtec.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/13764/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2016-07-08 17:06:19 +07:00
|
|
|
bcpc, contpc);
|
MIPS: math-emu: Correctly handle NOP emulation
Fix an issue introduced with commit 9ab4471c9f1b ("MIPS: math-emu:
Correct delay-slot exception propagation") where the emulation of a NOP
instruction signals the need to terminate the emulation loop. This in
turn, if the PC has not changed from the entry to the loop, will cause
the kernel to terminate the program with SIGILL.
Consider this program:
static double div(double d)
{
do
d /= 2.0;
while (d > .5);
return d;
}
int main(int argc, char **argv)
{
return div(argc);
}
which gets compiled to the following binary code:
00400490 <main>:
400490: 44840000 mtc1 a0,$f0
400494: 3c020040 lui v0,0x40
400498: d44207f8 ldc1 $f2,2040(v0)
40049c: 46800021 cvt.d.w $f0,$f0
4004a0: 46220002 mul.d $f0,$f0,$f2
4004a4: 4620103c c.lt.d $f2,$f0
4004a8: 4501fffd bc1t 4004a0 <main+0x10>
4004ac: 00000000 nop
4004b0: 4620000d trunc.w.d $f0,$f0
4004b4: 03e00008 jr ra
4004b8: 44020000 mfc1 v0,$f0
4004bc: 00000000 nop
Where the FPU emulator is used, depending on the number of command-line
arguments this code will either run to completion or terminate with
SIGILL.
If no arguments are specified, then BC1T will not be taken, NOP will not
be emulated and code will complete successfully.
If one argument is specified, then BC1T will be taken once and NOP will
be emulated. At this point the entry PC value will be 0x400498 and the
new PC value, set by `mips_dsemul' will be 0x4004a0, the target of BC1T.
The emulation loop will terminate, but SIGILL will not be issued,
because the PC has changed. The FPU emulator will be entered again and
on the second execution BC1T will not be taken, NOP will not be emulated
and code will complete successfully.
If two or more arguments are specified, then the first execution of BC1T
will proceed as above. Upon reentering the FPU emulator the emulation
loop will continue to BC1T, at which point the branch will be taken and
NOP emulated again. At this point however the entry PC value will be
0x4004a0, the same as the target of BC1T. This will make the emulator
conclude that execution has not advanced and therefore an unsupported
FPU instruction has been encountered, and SIGILL will be sent to the
process.
Fix the problem by extending the internal API of `mips_dsemul', making
it return -1 if no delay slot emulation frame has been made, the
instruction has been handled and execution of the emulation loop needs
to continue as if nothing happened. Remove code from `mips_dsemul' to
reproduce steps made by the emulation loop at the conclusion of each
iteration, as those will be reached normally now. Adjust call sites
accordingly. Document the API.
Signed-off-by: Maciej W. Rozycki <macro@imgtec.com>
Cc: Aurelien Jarno <aurelien@aurel32.net>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/12172/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2016-01-22 12:20:26 +07:00
|
|
|
if (sig < 0)
|
|
|
|
break;
|
2015-04-04 05:26:56 +07:00
|
|
|
if (sig)
|
|
|
|
xcp->cp0_epc = bcpc;
|
|
|
|
/*
|
|
|
|
* SIGILL forces out of
|
|
|
|
* the emulation loop.
|
|
|
|
*/
|
|
|
|
return sig ? sig : SIGILL;
|
2013-03-26 00:09:02 +07:00
|
|
|
}
|
|
|
|
} else
|
|
|
|
contpc = (xcp->cp0_epc + (contpc << 2));
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
switch (MIPSInst_OPCODE(ir)) {
|
|
|
|
case lwc1_op:
|
|
|
|
case swc1_op:
|
2014-04-19 18:11:37 +07:00
|
|
|
goto emul;
|
2014-04-26 06:49:14 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
case ldc1_op:
|
|
|
|
case sdc1_op:
|
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 05:26:49 +07:00
|
|
|
if (cpu_has_mips_2_3_4_5_r)
|
2014-04-19 18:11:37 +07:00
|
|
|
goto emul;
|
|
|
|
|
2015-04-04 05:26:56 +07:00
|
|
|
goto bc_sigill;
|
2014-04-26 06:49:14 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
case cop1_op:
|
|
|
|
goto emul;
|
2014-04-26 06:49:14 +07:00
|
|
|
|
2014-04-19 18:11:37 +07:00
|
|
|
case cop1x_op:
|
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 05:26:49 +07:00
|
|
|
if (cpu_has_mips_4_5_64_r2_r6)
|
2014-04-19 18:11:37 +07:00
|
|
|
/* its one of ours */
|
|
|
|
goto emul;
|
|
|
|
|
2015-04-04 05:26:56 +07:00
|
|
|
goto bc_sigill;
|
2014-04-26 06:49:14 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
case spec_op:
|
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 05:26:49 +07:00
|
|
|
switch (MIPSInst_FUNC(ir)) {
|
|
|
|
case movc_op:
|
|
|
|
if (cpu_has_mips_4_5_r)
|
|
|
|
goto emul;
|
2014-04-19 18:11:37 +07:00
|
|
|
|
2015-04-04 05:26:56 +07:00
|
|
|
goto bc_sigill;
|
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 05:26:49 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
2015-04-04 05:26:56 +07:00
|
|
|
|
|
|
|
bc_sigill:
|
|
|
|
xcp->cp0_epc = bcpc;
|
|
|
|
return SIGILL;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Single step the non-cp1
|
|
|
|
* instruction in the dslot
|
|
|
|
*/
|
MIPS: Use per-mm page to execute branch delay slot instructions
In some cases the kernel needs to execute an instruction from the delay
slot of an emulated branch instruction. These cases include:
- Emulated floating point branch instructions (bc1[ft]l?) for systems
which don't include an FPU, or upon which the kernel is run with the
"nofpu" parameter.
- MIPSr6 systems running binaries targeting older revisions of the
architecture, which may include branch instructions whose encodings
are no longer valid in MIPSr6.
Executing instructions from such delay slots is done by writing the
instruction to memory followed by a trap, as part of an "emuframe", and
executing it. This avoids the requirement of an emulator for the entire
MIPS instruction set. Prior to this patch such emuframes are written to
the user stack and executed from there.
This patch moves FP branch delay emuframes off of the user stack and
into a per-mm page. Allocating a page per-mm leaves userland with access
to only what it had access to previously, and compared to other
solutions is relatively simple.
When a thread requires a delay slot emulation, it is allocated a frame.
A thread may only have one frame allocated at any one time, since it may
only ever be executing one instruction at any one time. In order to
ensure that we can free up allocated frame later, its index is recorded
in struct thread_struct. In the typical case, after executing the delay
slot instruction we'll execute a break instruction with the BRK_MEMU
code. This traps back to the kernel & leads to a call to do_dsemulret
which frees the allocated frame & moves the user PC back to the
instruction that would have executed following the emulated branch.
In some cases the delay slot instruction may be invalid, such as a
branch, or may trigger an exception. In these cases the BRK_MEMU break
instruction will not be hit. In order to ensure that frames are freed
this patch introduces dsemul_thread_cleanup() and calls it to free any
allocated frame upon thread exit. If the instruction generated an
exception & leads to a signal being delivered to the thread, or indeed
if a signal simply happens to be delivered to the thread whilst it is
executing from the struct emuframe, then we need to take care to exit
the frame appropriately. This is done by either rolling back the user PC
to the branch or advancing it to the continuation PC prior to signal
delivery, using dsemul_thread_rollback(). If this were not done then a
sigreturn would return to the struct emuframe, and if that frame had
meanwhile been used in response to an emulated branch instruction within
the signal handler then we would execute the wrong user code.
Whilst a user could theoretically place something like a compact branch
to self in a delay slot and cause their thread to become stuck in an
infinite loop with the frame never being deallocated, this would:
- Only affect the users single process.
- Be architecturally invalid since there would be a branch in the
delay slot, which is forbidden.
- Be extremely unlikely to happen by mistake, and provide a program
with no more ability to harm the system than a simple infinite loop
would.
If a thread requires a delay slot emulation & no frame is available to
it (ie. the process has enough other threads that all frames are
currently in use) then the thread joins a waitqueue. It will sleep until
a frame is freed by another thread in the process.
Since we now know whether a thread has an allocated frame due to our
tracking of its index, the cookie field of struct emuframe is removed as
we can be more certain whether we have a valid frame. Since a thread may
only ever have a single frame at any given time, the epc field of struct
emuframe is also removed & the PC to continue from is instead stored in
struct thread_struct. Together these changes simplify & shrink struct
emuframe somewhat, allowing twice as many frames to fit into the page
allocated for them.
The primary benefit of this patch is that we are now free to mark the
user stack non-executable where that is possible.
Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Cc: Leonid Yegoshin <leonid.yegoshin@imgtec.com>
Cc: Maciej Rozycki <maciej.rozycki@imgtec.com>
Cc: Faraz Shahbazker <faraz.shahbazker@imgtec.com>
Cc: Raghu Gandham <raghu.gandham@imgtec.com>
Cc: Matthew Fortune <matthew.fortune@imgtec.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/13764/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2016-07-08 17:06:19 +07:00
|
|
|
sig = mips_dsemul(xcp, ir, bcpc, contpc);
|
MIPS: math-emu: Correctly handle NOP emulation
Fix an issue introduced with commit 9ab4471c9f1b ("MIPS: math-emu:
Correct delay-slot exception propagation") where the emulation of a NOP
instruction signals the need to terminate the emulation loop. This in
turn, if the PC has not changed from the entry to the loop, will cause
the kernel to terminate the program with SIGILL.
Consider this program:
static double div(double d)
{
do
d /= 2.0;
while (d > .5);
return d;
}
int main(int argc, char **argv)
{
return div(argc);
}
which gets compiled to the following binary code:
00400490 <main>:
400490: 44840000 mtc1 a0,$f0
400494: 3c020040 lui v0,0x40
400498: d44207f8 ldc1 $f2,2040(v0)
40049c: 46800021 cvt.d.w $f0,$f0
4004a0: 46220002 mul.d $f0,$f0,$f2
4004a4: 4620103c c.lt.d $f2,$f0
4004a8: 4501fffd bc1t 4004a0 <main+0x10>
4004ac: 00000000 nop
4004b0: 4620000d trunc.w.d $f0,$f0
4004b4: 03e00008 jr ra
4004b8: 44020000 mfc1 v0,$f0
4004bc: 00000000 nop
Where the FPU emulator is used, depending on the number of command-line
arguments this code will either run to completion or terminate with
SIGILL.
If no arguments are specified, then BC1T will not be taken, NOP will not
be emulated and code will complete successfully.
If one argument is specified, then BC1T will be taken once and NOP will
be emulated. At this point the entry PC value will be 0x400498 and the
new PC value, set by `mips_dsemul' will be 0x4004a0, the target of BC1T.
The emulation loop will terminate, but SIGILL will not be issued,
because the PC has changed. The FPU emulator will be entered again and
on the second execution BC1T will not be taken, NOP will not be emulated
and code will complete successfully.
If two or more arguments are specified, then the first execution of BC1T
will proceed as above. Upon reentering the FPU emulator the emulation
loop will continue to BC1T, at which point the branch will be taken and
NOP emulated again. At this point however the entry PC value will be
0x4004a0, the same as the target of BC1T. This will make the emulator
conclude that execution has not advanced and therefore an unsupported
FPU instruction has been encountered, and SIGILL will be sent to the
process.
Fix the problem by extending the internal API of `mips_dsemul', making
it return -1 if no delay slot emulation frame has been made, the
instruction has been handled and execution of the emulation loop needs
to continue as if nothing happened. Remove code from `mips_dsemul' to
reproduce steps made by the emulation loop at the conclusion of each
iteration, as those will be reached normally now. Adjust call sites
accordingly. Document the API.
Signed-off-by: Maciej W. Rozycki <macro@imgtec.com>
Cc: Aurelien Jarno <aurelien@aurel32.net>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/12172/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2016-01-22 12:20:26 +07:00
|
|
|
if (sig < 0)
|
|
|
|
break;
|
2015-04-04 05:26:56 +07:00
|
|
|
if (sig)
|
|
|
|
xcp->cp0_epc = bcpc;
|
|
|
|
/* SIGILL forces out of the emulation loop. */
|
|
|
|
return sig ? sig : SIGILL;
|
2014-04-26 06:49:14 +07:00
|
|
|
} else if (likely) { /* branch not taken */
|
2015-04-04 05:24:24 +07:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
if (!(MIPSInst_RS(ir) & 0x10))
|
|
|
|
return SIGILL;
|
|
|
|
|
2014-04-26 06:49:14 +07:00
|
|
|
/* a real fpu computation instruction */
|
|
|
|
if ((sig = fpu_emu(xcp, ctx, ir)))
|
|
|
|
return sig;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2014-04-26 06:49:14 +07:00
|
|
|
case cop1x_op:
|
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 05:26:49 +07:00
|
|
|
if (!cpu_has_mips_4_5_64_r2_r6)
|
2014-04-19 18:11:37 +07:00
|
|
|
return SIGILL;
|
|
|
|
|
|
|
|
sig = fpux_emu(xcp, ctx, ir, fault_addr);
|
2010-10-22 06:32:26 +07:00
|
|
|
if (sig)
|
2005-04-17 05:20:36 +07:00
|
|
|
return sig;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case spec_op:
|
2014-04-19 18:11:37 +07:00
|
|
|
if (!cpu_has_mips_4_5_r)
|
|
|
|
return SIGILL;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
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:
|
2013-11-07 19:48:28 +07:00
|
|
|
sigill:
|
2005-04-17 05:20:36 +07:00
|
|
|
return SIGILL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we did it !! */
|
2007-07-13 21:02:29 +07:00
|
|
|
xcp->cp0_epc = contpc;
|
2014-04-16 06:59:03 +07:00
|
|
|
clear_delay_slot(xcp);
|
2005-03-01 00:55:57 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
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 */
|
|
|
|
};
|
|
|
|
|
2015-08-13 14:56:28 +07:00
|
|
|
static const unsigned char negative_cmptab[8] = {
|
|
|
|
0, /* Reserved */
|
|
|
|
IEEE754_CLT | IEEE754_CGT | IEEE754_CEQ,
|
|
|
|
IEEE754_CLT | IEEE754_CGT | IEEE754_CUN,
|
|
|
|
IEEE754_CLT | IEEE754_CGT,
|
|
|
|
/* Reserved */
|
|
|
|
};
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Additional MIPS4 instructions
|
|
|
|
*/
|
|
|
|
|
2014-04-16 16:00:12 +07:00
|
|
|
#define DEF3OP(name, p, f1, f2, f3) \
|
|
|
|
static union ieee754##p fpemu_##p##_##name(union ieee754##p r, \
|
|
|
|
union ieee754##p s, union 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; \
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2014-04-16 06:31:11 +07:00
|
|
|
static union ieee754dp fpemu_dp_recip(union ieee754dp d)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
return ieee754dp_div(ieee754dp_one(0), d);
|
|
|
|
}
|
|
|
|
|
2014-04-16 06:31:11 +07:00
|
|
|
static union ieee754dp fpemu_dp_rsqrt(union ieee754dp d)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
return ieee754dp_div(ieee754dp_one(0), ieee754dp_sqrt(d));
|
|
|
|
}
|
|
|
|
|
2014-04-16 06:31:11 +07:00
|
|
|
static union ieee754sp fpemu_sp_recip(union ieee754sp s)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
return ieee754sp_div(ieee754sp_one(0), s);
|
|
|
|
}
|
|
|
|
|
2014-04-16 06:31:11 +07:00
|
|
|
static union ieee754sp fpemu_sp_rsqrt(union ieee754sp s)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
return ieee754sp_div(ieee754sp_one(0), ieee754sp_sqrt(s));
|
|
|
|
}
|
|
|
|
|
2007-10-12 05:46:15 +07:00
|
|
|
DEF3OP(madd, sp, ieee754sp_mul, ieee754sp_add, );
|
|
|
|
DEF3OP(msub, sp, ieee754sp_mul, ieee754sp_sub, );
|
2005-04-17 05:20:36 +07:00
|
|
|
DEF3OP(nmadd, sp, ieee754sp_mul, ieee754sp_add, ieee754sp_neg);
|
|
|
|
DEF3OP(nmsub, sp, ieee754sp_mul, ieee754sp_sub, ieee754sp_neg);
|
2007-10-12 05:46:15 +07:00
|
|
|
DEF3OP(madd, dp, ieee754dp_mul, ieee754dp_add, );
|
|
|
|
DEF3OP(msub, dp, ieee754dp_mul, ieee754dp_sub, );
|
2005-04-17 05:20:36 +07:00
|
|
|
DEF3OP(nmadd, dp, ieee754dp_mul, ieee754dp_add, ieee754dp_neg);
|
|
|
|
DEF3OP(nmsub, dp, ieee754dp_mul, ieee754dp_sub, ieee754dp_neg);
|
|
|
|
|
2006-05-15 23:26:03 +07:00
|
|
|
static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
2010-10-22 06:32:26 +07:00
|
|
|
mips_instruction ir, void *__user *fault_addr)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
unsigned rcsr = 0; /* resulting csr */
|
|
|
|
|
2009-11-06 02:34:26 +07:00
|
|
|
MIPS_FPU_EMU_INC_STATS(cp1xops);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
switch (MIPSInst_FMA_FFMT(ir)) {
|
|
|
|
case s_fmt:{ /* 0 */
|
|
|
|
|
2014-04-16 06:31:11 +07:00
|
|
|
union ieee754sp(*handler) (union ieee754sp, union ieee754sp, union ieee754sp);
|
|
|
|
union ieee754sp fd, fr, fs, ft;
|
2005-10-23 19:58:21 +07:00
|
|
|
u32 __user *va;
|
2005-04-17 05:20:36 +07:00
|
|
|
u32 val;
|
|
|
|
|
|
|
|
switch (MIPSInst_FUNC(ir)) {
|
|
|
|
case lwxc1_op:
|
2005-10-23 19:58:21 +07:00
|
|
|
va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +
|
2005-04-17 05:20:36 +07:00
|
|
|
xcp->regs[MIPSInst_FT(ir)]);
|
|
|
|
|
2009-11-06 02:34:26 +07:00
|
|
|
MIPS_FPU_EMU_INC_STATS(loads);
|
2010-10-22 06:32:26 +07:00
|
|
|
if (!access_ok(VERIFY_READ, va, sizeof(u32))) {
|
2009-11-06 02:34:26 +07:00
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
2010-10-22 06:32:26 +07:00
|
|
|
*fault_addr = va;
|
2005-04-17 05:20:36 +07:00
|
|
|
return SIGBUS;
|
|
|
|
}
|
2010-10-22 06:32:26 +07:00
|
|
|
if (__get_user(val, va)) {
|
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
|
*fault_addr = va;
|
|
|
|
return SIGSEGV;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
SITOREG(val, MIPSInst_FD(ir));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case swxc1_op:
|
2005-10-23 19:58:21 +07:00
|
|
|
va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +
|
2005-04-17 05:20:36 +07:00
|
|
|
xcp->regs[MIPSInst_FT(ir)]);
|
|
|
|
|
2009-11-06 02:34:26 +07:00
|
|
|
MIPS_FPU_EMU_INC_STATS(stores);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
SIFROMREG(val, MIPSInst_FS(ir));
|
2010-10-22 06:32:26 +07:00
|
|
|
if (!access_ok(VERIFY_WRITE, va, sizeof(u32))) {
|
2009-11-06 02:34:26 +07:00
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
2010-10-22 06:32:26 +07:00
|
|
|
*fault_addr = va;
|
2005-04-17 05:20:36 +07:00
|
|
|
return SIGBUS;
|
|
|
|
}
|
2010-10-22 06:32:26 +07:00
|
|
|
if (put_user(val, va)) {
|
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
|
*fault_addr = va;
|
|
|
|
return SIGSEGV;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case madd_s_op:
|
|
|
|
handler = fpemu_sp_madd;
|
|
|
|
goto scoptop;
|
|
|
|
case msub_s_op:
|
|
|
|
handler = fpemu_sp_msub;
|
|
|
|
goto scoptop;
|
|
|
|
case nmadd_s_op:
|
|
|
|
handler = fpemu_sp_nmadd;
|
|
|
|
goto scoptop;
|
|
|
|
case nmsub_s_op:
|
|
|
|
handler = fpemu_sp_nmsub;
|
|
|
|
goto scoptop;
|
|
|
|
|
|
|
|
scoptop:
|
|
|
|
SPFROMREG(fr, MIPSInst_FR(ir));
|
|
|
|
SPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
SPFROMREG(ft, MIPSInst_FT(ir));
|
|
|
|
fd = (*handler) (fr, fs, ft);
|
|
|
|
SPTOREG(fd, MIPSInst_FD(ir));
|
|
|
|
|
|
|
|
copcsr:
|
2014-05-30 02:26:45 +07:00
|
|
|
if (ieee754_cxtest(IEEE754_INEXACT)) {
|
|
|
|
MIPS_FPU_EMU_INC_STATS(ieee754_inexact);
|
2005-04-17 05:20:36 +07:00
|
|
|
rcsr |= FPU_CSR_INE_X | FPU_CSR_INE_S;
|
2014-05-30 02:26:45 +07:00
|
|
|
}
|
|
|
|
if (ieee754_cxtest(IEEE754_UNDERFLOW)) {
|
|
|
|
MIPS_FPU_EMU_INC_STATS(ieee754_underflow);
|
2005-04-17 05:20:36 +07:00
|
|
|
rcsr |= FPU_CSR_UDF_X | FPU_CSR_UDF_S;
|
2014-05-30 02:26:45 +07:00
|
|
|
}
|
|
|
|
if (ieee754_cxtest(IEEE754_OVERFLOW)) {
|
|
|
|
MIPS_FPU_EMU_INC_STATS(ieee754_overflow);
|
2005-04-17 05:20:36 +07:00
|
|
|
rcsr |= FPU_CSR_OVF_X | FPU_CSR_OVF_S;
|
2014-05-30 02:26:45 +07:00
|
|
|
}
|
|
|
|
if (ieee754_cxtest(IEEE754_INVALID_OPERATION)) {
|
|
|
|
MIPS_FPU_EMU_INC_STATS(ieee754_invalidop);
|
2005-04-17 05:20:36 +07:00
|
|
|
rcsr |= FPU_CSR_INV_X | FPU_CSR_INV_S;
|
2014-05-30 02:26:45 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
ctx->fcr31 = (ctx->fcr31 & ~FPU_CSR_ALL_X) | rcsr;
|
|
|
|
if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) {
|
2014-04-26 06:49:14 +07:00
|
|
|
/*printk ("SIGFPE: FPU csr = %08x\n",
|
2005-04-17 05:20:36 +07:00
|
|
|
ctx->fcr31); */
|
|
|
|
return SIGFPE;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return SIGILL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case d_fmt:{ /* 1 */
|
2014-04-16 06:31:11 +07:00
|
|
|
union ieee754dp(*handler) (union ieee754dp, union ieee754dp, union ieee754dp);
|
|
|
|
union ieee754dp fd, fr, fs, ft;
|
2005-10-23 19:58:21 +07:00
|
|
|
u64 __user *va;
|
2005-04-17 05:20:36 +07:00
|
|
|
u64 val;
|
|
|
|
|
|
|
|
switch (MIPSInst_FUNC(ir)) {
|
|
|
|
case ldxc1_op:
|
2005-10-23 19:58:21 +07:00
|
|
|
va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +
|
2005-04-17 05:20:36 +07:00
|
|
|
xcp->regs[MIPSInst_FT(ir)]);
|
|
|
|
|
2009-11-06 02:34:26 +07:00
|
|
|
MIPS_FPU_EMU_INC_STATS(loads);
|
2010-10-22 06:32:26 +07:00
|
|
|
if (!access_ok(VERIFY_READ, va, sizeof(u64))) {
|
2009-11-06 02:34:26 +07:00
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
2010-10-22 06:32:26 +07:00
|
|
|
*fault_addr = va;
|
2005-04-17 05:20:36 +07:00
|
|
|
return SIGBUS;
|
|
|
|
}
|
2010-10-22 06:32:26 +07:00
|
|
|
if (__get_user(val, va)) {
|
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
|
*fault_addr = va;
|
|
|
|
return SIGSEGV;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
DITOREG(val, MIPSInst_FD(ir));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case sdxc1_op:
|
2005-10-23 19:58:21 +07:00
|
|
|
va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +
|
2005-04-17 05:20:36 +07:00
|
|
|
xcp->regs[MIPSInst_FT(ir)]);
|
|
|
|
|
2009-11-06 02:34:26 +07:00
|
|
|
MIPS_FPU_EMU_INC_STATS(stores);
|
2005-04-17 05:20:36 +07:00
|
|
|
DIFROMREG(val, MIPSInst_FS(ir));
|
2010-10-22 06:32:26 +07:00
|
|
|
if (!access_ok(VERIFY_WRITE, va, sizeof(u64))) {
|
2009-11-06 02:34:26 +07:00
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
2010-10-22 06:32:26 +07:00
|
|
|
*fault_addr = va;
|
2005-04-17 05:20:36 +07:00
|
|
|
return SIGBUS;
|
|
|
|
}
|
2010-10-22 06:32:26 +07:00
|
|
|
if (__put_user(val, va)) {
|
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
|
*fault_addr = va;
|
|
|
|
return SIGSEGV;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case madd_d_op:
|
|
|
|
handler = fpemu_dp_madd;
|
|
|
|
goto dcoptop;
|
|
|
|
case msub_d_op:
|
|
|
|
handler = fpemu_dp_msub;
|
|
|
|
goto dcoptop;
|
|
|
|
case nmadd_d_op:
|
|
|
|
handler = fpemu_dp_nmadd;
|
|
|
|
goto dcoptop;
|
|
|
|
case nmsub_d_op:
|
|
|
|
handler = fpemu_dp_nmsub;
|
|
|
|
goto dcoptop;
|
|
|
|
|
|
|
|
dcoptop:
|
|
|
|
DPFROMREG(fr, MIPSInst_FR(ir));
|
|
|
|
DPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
DPFROMREG(ft, MIPSInst_FT(ir));
|
|
|
|
fd = (*handler) (fr, fs, ft);
|
|
|
|
DPTOREG(fd, MIPSInst_FD(ir));
|
|
|
|
goto copcsr;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return SIGILL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-03-07 08:05:27 +07:00
|
|
|
case 0x3:
|
|
|
|
if (MIPSInst_FUNC(ir) != pfetch_op)
|
2005-04-17 05:20:36 +07:00
|
|
|
return SIGILL;
|
2014-03-07 08:05:27 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* ignore prefx operation */
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return SIGILL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Emulate a single COP1 arithmetic instruction.
|
|
|
|
*/
|
2006-05-15 23:26:03 +07:00
|
|
|
static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
2005-04-17 05:20:36 +07:00
|
|
|
mips_instruction ir)
|
|
|
|
{
|
|
|
|
int rfmt; /* resulting format */
|
|
|
|
unsigned rcsr = 0; /* resulting csr */
|
2014-04-26 06:49:14 +07:00
|
|
|
unsigned int oldrm;
|
|
|
|
unsigned int cbit;
|
2005-04-17 05:20:36 +07:00
|
|
|
unsigned cond;
|
|
|
|
union {
|
2014-04-16 06:31:11 +07:00
|
|
|
union ieee754dp d;
|
|
|
|
union ieee754sp s;
|
2005-04-17 05:20:36 +07:00
|
|
|
int w;
|
|
|
|
s64 l;
|
|
|
|
} rv; /* resulting value */
|
2014-04-26 06:49:14 +07:00
|
|
|
u64 bits;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-11-06 02:34:26 +07:00
|
|
|
MIPS_FPU_EMU_INC_STATS(cp1ops);
|
2005-04-17 05:20:36 +07:00
|
|
|
switch (rfmt = (MIPSInst_FFMT(ir) & 0xf)) {
|
2014-04-26 06:49:14 +07:00
|
|
|
case s_fmt: { /* 0 */
|
2005-04-17 05:20:36 +07:00
|
|
|
union {
|
2014-04-16 06:31:11 +07:00
|
|
|
union ieee754sp(*b) (union ieee754sp, union ieee754sp);
|
|
|
|
union ieee754sp(*u) (union ieee754sp);
|
2005-04-17 05:20:36 +07:00
|
|
|
} handler;
|
2016-04-21 20:04:48 +07:00
|
|
|
union ieee754sp fd, fs, ft;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
switch (MIPSInst_FUNC(ir)) {
|
|
|
|
/* binary ops */
|
|
|
|
case fadd_op:
|
|
|
|
handler.b = ieee754sp_add;
|
|
|
|
goto scopbop;
|
|
|
|
case fsub_op:
|
|
|
|
handler.b = ieee754sp_sub;
|
|
|
|
goto scopbop;
|
|
|
|
case fmul_op:
|
|
|
|
handler.b = ieee754sp_mul;
|
|
|
|
goto scopbop;
|
|
|
|
case fdiv_op:
|
|
|
|
handler.b = ieee754sp_div;
|
|
|
|
goto scopbop;
|
|
|
|
|
|
|
|
/* unary ops */
|
|
|
|
case fsqrt_op:
|
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 05:26:49 +07:00
|
|
|
if (!cpu_has_mips_2_3_4_5_r)
|
2014-04-19 18:11:37 +07:00
|
|
|
return SIGILL;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
handler.u = ieee754sp_sqrt;
|
|
|
|
goto scopuop;
|
2014-04-26 06:49:14 +07:00
|
|
|
|
2014-04-19 18:11:37 +07:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2005-04-17 05:20:36 +07:00
|
|
|
case frsqrt_op:
|
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 05:26:49 +07:00
|
|
|
if (!cpu_has_mips_4_5_64_r2_r6)
|
2014-04-19 18:11:37 +07:00
|
|
|
return SIGILL;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
handler.u = fpemu_sp_rsqrt;
|
|
|
|
goto scopuop;
|
2014-04-26 06:49:14 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
case frecip_op:
|
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 05:26:49 +07:00
|
|
|
if (!cpu_has_mips_4_5_64_r2_r6)
|
2014-04-19 18:11:37 +07:00
|
|
|
return SIGILL;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
handler.u = fpemu_sp_recip;
|
|
|
|
goto scopuop;
|
2014-04-19 18:11:37 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
case fmovc_op:
|
2014-04-19 18:11:37 +07:00
|
|
|
if (!cpu_has_mips_4_5_r)
|
|
|
|
return SIGILL;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
cond = fpucondbit[MIPSInst_FT(ir) >> 2];
|
|
|
|
if (((ctx->fcr31 & cond) != 0) !=
|
|
|
|
((MIPSInst_FT(ir) & 1) != 0))
|
|
|
|
return 0;
|
|
|
|
SPFROMREG(rv.s, MIPSInst_FS(ir));
|
|
|
|
break;
|
2014-04-26 06:49:14 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
case fmovz_op:
|
2014-04-19 18:11:37 +07:00
|
|
|
if (!cpu_has_mips_4_5_r)
|
|
|
|
return SIGILL;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (xcp->regs[MIPSInst_FT(ir)] != 0)
|
|
|
|
return 0;
|
|
|
|
SPFROMREG(rv.s, MIPSInst_FS(ir));
|
|
|
|
break;
|
2014-04-26 06:49:14 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
case fmovn_op:
|
2014-04-19 18:11:37 +07:00
|
|
|
if (!cpu_has_mips_4_5_r)
|
|
|
|
return SIGILL;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (xcp->regs[MIPSInst_FT(ir)] == 0)
|
|
|
|
return 0;
|
|
|
|
SPFROMREG(rv.s, MIPSInst_FS(ir));
|
|
|
|
break;
|
2014-04-26 06:49:14 +07:00
|
|
|
|
2015-08-13 14:56:29 +07:00
|
|
|
case fseleqz_op:
|
|
|
|
if (!cpu_has_mips_r6)
|
|
|
|
return SIGILL;
|
|
|
|
|
|
|
|
SPFROMREG(rv.s, MIPSInst_FT(ir));
|
|
|
|
if (rv.w & 0x1)
|
|
|
|
rv.w = 0;
|
|
|
|
else
|
|
|
|
SPFROMREG(rv.s, MIPSInst_FS(ir));
|
|
|
|
break;
|
|
|
|
|
2015-08-13 14:56:30 +07:00
|
|
|
case fselnez_op:
|
|
|
|
if (!cpu_has_mips_r6)
|
|
|
|
return SIGILL;
|
|
|
|
|
|
|
|
SPFROMREG(rv.s, MIPSInst_FT(ir));
|
|
|
|
if (rv.w & 0x1)
|
|
|
|
SPFROMREG(rv.s, MIPSInst_FS(ir));
|
|
|
|
else
|
|
|
|
rv.w = 0;
|
|
|
|
break;
|
|
|
|
|
2015-08-13 14:56:31 +07:00
|
|
|
case fmaddf_op: {
|
|
|
|
union ieee754sp ft, fs, fd;
|
|
|
|
|
|
|
|
if (!cpu_has_mips_r6)
|
|
|
|
return SIGILL;
|
|
|
|
|
|
|
|
SPFROMREG(ft, MIPSInst_FT(ir));
|
|
|
|
SPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
SPFROMREG(fd, MIPSInst_FD(ir));
|
|
|
|
rv.s = ieee754sp_maddf(fd, fs, ft);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-08-13 14:56:32 +07:00
|
|
|
case fmsubf_op: {
|
|
|
|
union ieee754sp ft, fs, fd;
|
|
|
|
|
|
|
|
if (!cpu_has_mips_r6)
|
|
|
|
return SIGILL;
|
|
|
|
|
|
|
|
SPFROMREG(ft, MIPSInst_FT(ir));
|
|
|
|
SPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
SPFROMREG(fd, MIPSInst_FD(ir));
|
|
|
|
rv.s = ieee754sp_msubf(fd, fs, ft);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-08-13 14:56:33 +07:00
|
|
|
case frint_op: {
|
|
|
|
union ieee754sp fs;
|
|
|
|
|
|
|
|
if (!cpu_has_mips_r6)
|
|
|
|
return SIGILL;
|
|
|
|
|
|
|
|
SPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
rv.l = ieee754sp_tlong(fs);
|
|
|
|
rv.s = ieee754sp_flong(rv.l);
|
|
|
|
goto copcsr;
|
|
|
|
}
|
|
|
|
|
2015-08-13 14:56:34 +07:00
|
|
|
case fclass_op: {
|
|
|
|
union ieee754sp fs;
|
|
|
|
|
|
|
|
if (!cpu_has_mips_r6)
|
|
|
|
return SIGILL;
|
|
|
|
|
|
|
|
SPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
rv.w = ieee754sp_2008class(fs);
|
|
|
|
rfmt = w_fmt;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-08-13 14:56:35 +07:00
|
|
|
case fmin_op: {
|
|
|
|
union ieee754sp fs, ft;
|
|
|
|
|
|
|
|
if (!cpu_has_mips_r6)
|
|
|
|
return SIGILL;
|
|
|
|
|
|
|
|
SPFROMREG(ft, MIPSInst_FT(ir));
|
|
|
|
SPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
rv.s = ieee754sp_fmin(fs, ft);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case fmina_op: {
|
|
|
|
union ieee754sp fs, ft;
|
|
|
|
|
|
|
|
if (!cpu_has_mips_r6)
|
|
|
|
return SIGILL;
|
|
|
|
|
|
|
|
SPFROMREG(ft, MIPSInst_FT(ir));
|
|
|
|
SPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
rv.s = ieee754sp_fmina(fs, ft);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-08-13 14:56:36 +07:00
|
|
|
case fmax_op: {
|
|
|
|
union ieee754sp fs, ft;
|
|
|
|
|
|
|
|
if (!cpu_has_mips_r6)
|
|
|
|
return SIGILL;
|
|
|
|
|
|
|
|
SPFROMREG(ft, MIPSInst_FT(ir));
|
|
|
|
SPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
rv.s = ieee754sp_fmax(fs, ft);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case fmaxa_op: {
|
|
|
|
union ieee754sp fs, ft;
|
|
|
|
|
|
|
|
if (!cpu_has_mips_r6)
|
|
|
|
return SIGILL;
|
|
|
|
|
|
|
|
SPFROMREG(ft, MIPSInst_FT(ir));
|
|
|
|
SPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
rv.s = ieee754sp_fmaxa(fs, ft);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
case fabs_op:
|
|
|
|
handler.u = ieee754sp_abs;
|
|
|
|
goto scopuop;
|
2014-04-26 06:49:14 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
case fneg_op:
|
|
|
|
handler.u = ieee754sp_neg;
|
|
|
|
goto scopuop;
|
2014-04-26 06:49:14 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
case fmov_op:
|
|
|
|
/* an easy one */
|
|
|
|
SPFROMREG(rv.s, MIPSInst_FS(ir));
|
|
|
|
goto copcsr;
|
|
|
|
|
|
|
|
/* binary op on handler */
|
2014-04-26 06:49:14 +07:00
|
|
|
scopbop:
|
|
|
|
SPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
SPFROMREG(ft, MIPSInst_FT(ir));
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2014-04-26 06:49:14 +07:00
|
|
|
rv.s = (*handler.b) (fs, ft);
|
|
|
|
goto copcsr;
|
|
|
|
scopuop:
|
|
|
|
SPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
rv.s = (*handler.u) (fs);
|
|
|
|
goto copcsr;
|
|
|
|
copcsr:
|
2014-05-30 02:26:45 +07:00
|
|
|
if (ieee754_cxtest(IEEE754_INEXACT)) {
|
|
|
|
MIPS_FPU_EMU_INC_STATS(ieee754_inexact);
|
2005-04-17 05:20:36 +07:00
|
|
|
rcsr |= FPU_CSR_INE_X | FPU_CSR_INE_S;
|
2014-05-30 02:26:45 +07:00
|
|
|
}
|
|
|
|
if (ieee754_cxtest(IEEE754_UNDERFLOW)) {
|
|
|
|
MIPS_FPU_EMU_INC_STATS(ieee754_underflow);
|
2005-04-17 05:20:36 +07:00
|
|
|
rcsr |= FPU_CSR_UDF_X | FPU_CSR_UDF_S;
|
2014-05-30 02:26:45 +07:00
|
|
|
}
|
|
|
|
if (ieee754_cxtest(IEEE754_OVERFLOW)) {
|
|
|
|
MIPS_FPU_EMU_INC_STATS(ieee754_overflow);
|
2005-04-17 05:20:36 +07:00
|
|
|
rcsr |= FPU_CSR_OVF_X | FPU_CSR_OVF_S;
|
2014-05-30 02:26:45 +07:00
|
|
|
}
|
|
|
|
if (ieee754_cxtest(IEEE754_ZERO_DIVIDE)) {
|
|
|
|
MIPS_FPU_EMU_INC_STATS(ieee754_zerodiv);
|
2005-04-17 05:20:36 +07:00
|
|
|
rcsr |= FPU_CSR_DIV_X | FPU_CSR_DIV_S;
|
2014-05-30 02:26:45 +07:00
|
|
|
}
|
|
|
|
if (ieee754_cxtest(IEEE754_INVALID_OPERATION)) {
|
|
|
|
MIPS_FPU_EMU_INC_STATS(ieee754_invalidop);
|
2005-04-17 05:20:36 +07:00
|
|
|
rcsr |= FPU_CSR_INV_X | FPU_CSR_INV_S;
|
2014-05-30 02:26:45 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* unary conv ops */
|
|
|
|
case fcvts_op:
|
|
|
|
return SIGILL; /* not defined */
|
|
|
|
|
2014-04-26 06:49:14 +07:00
|
|
|
case fcvtd_op:
|
2005-04-17 05:20:36 +07:00
|
|
|
SPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
rv.d = ieee754dp_fsp(fs);
|
|
|
|
rfmt = d_fmt;
|
|
|
|
goto copcsr;
|
|
|
|
|
2014-04-26 06:49:14 +07:00
|
|
|
case fcvtw_op:
|
2005-04-17 05:20:36 +07:00
|
|
|
SPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
rv.w = ieee754sp_tint(fs);
|
|
|
|
rfmt = w_fmt;
|
|
|
|
goto copcsr;
|
|
|
|
|
|
|
|
case fround_op:
|
|
|
|
case ftrunc_op:
|
|
|
|
case fceil_op:
|
2014-04-26 06:49:14 +07:00
|
|
|
case ffloor_op:
|
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 05:26:49 +07:00
|
|
|
if (!cpu_has_mips_2_3_4_5_r)
|
2014-04-19 18:11:37 +07:00
|
|
|
return SIGILL;
|
|
|
|
|
2014-04-26 06:49:14 +07:00
|
|
|
oldrm = ieee754_csr.rm;
|
2005-04-17 05:20:36 +07:00
|
|
|
SPFROMREG(fs, MIPSInst_FS(ir));
|
2015-04-04 05:24:56 +07:00
|
|
|
ieee754_csr.rm = MIPSInst_FUNC(ir);
|
2005-04-17 05:20:36 +07:00
|
|
|
rv.w = ieee754sp_tint(fs);
|
|
|
|
ieee754_csr.rm = oldrm;
|
|
|
|
rfmt = w_fmt;
|
|
|
|
goto copcsr;
|
|
|
|
|
2016-04-21 20:04:48 +07:00
|
|
|
case fsel_op:
|
|
|
|
if (!cpu_has_mips_r6)
|
|
|
|
return SIGILL;
|
|
|
|
|
|
|
|
SPFROMREG(fd, MIPSInst_FD(ir));
|
|
|
|
if (fd.bits & 0x1)
|
|
|
|
SPFROMREG(rv.s, MIPSInst_FT(ir));
|
|
|
|
else
|
|
|
|
SPFROMREG(rv.s, MIPSInst_FS(ir));
|
|
|
|
break;
|
|
|
|
|
2014-04-26 06:49:14 +07:00
|
|
|
case fcvtl_op:
|
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 05:26:49 +07:00
|
|
|
if (!cpu_has_mips_3_4_5_64_r2_r6)
|
2014-04-19 18:11:37 +07:00
|
|
|
return SIGILL;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
SPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
rv.l = ieee754sp_tlong(fs);
|
|
|
|
rfmt = l_fmt;
|
|
|
|
goto copcsr;
|
|
|
|
|
|
|
|
case froundl_op:
|
|
|
|
case ftruncl_op:
|
|
|
|
case fceill_op:
|
2014-04-26 06:49:14 +07:00
|
|
|
case ffloorl_op:
|
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 05:26:49 +07:00
|
|
|
if (!cpu_has_mips_3_4_5_64_r2_r6)
|
2014-04-19 18:11:37 +07:00
|
|
|
return SIGILL;
|
|
|
|
|
2014-04-26 06:49:14 +07:00
|
|
|
oldrm = ieee754_csr.rm;
|
2005-04-17 05:20:36 +07:00
|
|
|
SPFROMREG(fs, MIPSInst_FS(ir));
|
2015-04-04 05:24:56 +07:00
|
|
|
ieee754_csr.rm = MIPSInst_FUNC(ir);
|
2005-04-17 05:20:36 +07:00
|
|
|
rv.l = ieee754sp_tlong(fs);
|
|
|
|
ieee754_csr.rm = oldrm;
|
|
|
|
rfmt = l_fmt;
|
|
|
|
goto copcsr;
|
|
|
|
|
|
|
|
default:
|
2015-08-13 14:56:28 +07:00
|
|
|
if (!NO_R6EMU && MIPSInst_FUNC(ir) >= fcmp_op) {
|
2005-04-17 05:20:36 +07:00
|
|
|
unsigned cmpop = MIPSInst_FUNC(ir) - fcmp_op;
|
2014-04-16 06:31:11 +07:00
|
|
|
union ieee754sp fs, ft;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
SPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
SPFROMREG(ft, MIPSInst_FT(ir));
|
|
|
|
rv.w = ieee754sp_cmp(fs, ft,
|
|
|
|
cmptab[cmpop & 0x7], cmpop & 0x8);
|
|
|
|
rfmt = -1;
|
|
|
|
if ((cmpop & 0x8) && ieee754_cxtest
|
|
|
|
(IEEE754_INVALID_OPERATION))
|
|
|
|
rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S;
|
|
|
|
else
|
|
|
|
goto copcsr;
|
|
|
|
|
2014-04-26 06:49:14 +07:00
|
|
|
} else
|
2005-04-17 05:20:36 +07:00
|
|
|
return SIGILL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-04-26 06:49:14 +07:00
|
|
|
case d_fmt: {
|
2016-04-21 20:04:48 +07:00
|
|
|
union ieee754dp fd, fs, ft;
|
2005-04-17 05:20:36 +07:00
|
|
|
union {
|
2014-04-16 06:31:11 +07:00
|
|
|
union ieee754dp(*b) (union ieee754dp, union ieee754dp);
|
|
|
|
union ieee754dp(*u) (union ieee754dp);
|
2005-04-17 05:20:36 +07:00
|
|
|
} handler;
|
|
|
|
|
|
|
|
switch (MIPSInst_FUNC(ir)) {
|
|
|
|
/* binary ops */
|
|
|
|
case fadd_op:
|
|
|
|
handler.b = ieee754dp_add;
|
|
|
|
goto dcopbop;
|
|
|
|
case fsub_op:
|
|
|
|
handler.b = ieee754dp_sub;
|
|
|
|
goto dcopbop;
|
|
|
|
case fmul_op:
|
|
|
|
handler.b = ieee754dp_mul;
|
|
|
|
goto dcopbop;
|
|
|
|
case fdiv_op:
|
|
|
|
handler.b = ieee754dp_div;
|
|
|
|
goto dcopbop;
|
|
|
|
|
|
|
|
/* unary ops */
|
|
|
|
case fsqrt_op:
|
2014-04-19 18:11:37 +07:00
|
|
|
if (!cpu_has_mips_2_3_4_5_r)
|
|
|
|
return SIGILL;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
handler.u = ieee754dp_sqrt;
|
|
|
|
goto dcopuop;
|
2014-04-19 18:11:37 +07:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2005-04-17 05:20:36 +07:00
|
|
|
case frsqrt_op:
|
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 05:26:49 +07:00
|
|
|
if (!cpu_has_mips_4_5_64_r2_r6)
|
2014-04-19 18:11:37 +07:00
|
|
|
return SIGILL;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
handler.u = fpemu_dp_rsqrt;
|
|
|
|
goto dcopuop;
|
|
|
|
case frecip_op:
|
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 05:26:49 +07:00
|
|
|
if (!cpu_has_mips_4_5_64_r2_r6)
|
2014-04-19 18:11:37 +07:00
|
|
|
return SIGILL;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
handler.u = fpemu_dp_recip;
|
|
|
|
goto dcopuop;
|
|
|
|
case fmovc_op:
|
2014-04-19 18:11:37 +07:00
|
|
|
if (!cpu_has_mips_4_5_r)
|
|
|
|
return SIGILL;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
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:
|
2014-04-19 18:11:37 +07:00
|
|
|
if (!cpu_has_mips_4_5_r)
|
|
|
|
return SIGILL;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (xcp->regs[MIPSInst_FT(ir)] != 0)
|
|
|
|
return 0;
|
|
|
|
DPFROMREG(rv.d, MIPSInst_FS(ir));
|
|
|
|
break;
|
|
|
|
case fmovn_op:
|
2014-04-19 18:11:37 +07:00
|
|
|
if (!cpu_has_mips_4_5_r)
|
|
|
|
return SIGILL;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (xcp->regs[MIPSInst_FT(ir)] == 0)
|
|
|
|
return 0;
|
|
|
|
DPFROMREG(rv.d, MIPSInst_FS(ir));
|
|
|
|
break;
|
2015-08-13 14:56:29 +07:00
|
|
|
|
|
|
|
case fseleqz_op:
|
|
|
|
if (!cpu_has_mips_r6)
|
|
|
|
return SIGILL;
|
|
|
|
|
|
|
|
DPFROMREG(rv.d, MIPSInst_FT(ir));
|
|
|
|
if (rv.l & 0x1)
|
|
|
|
rv.l = 0;
|
|
|
|
else
|
|
|
|
DPFROMREG(rv.d, MIPSInst_FS(ir));
|
|
|
|
break;
|
|
|
|
|
2015-08-13 14:56:30 +07:00
|
|
|
case fselnez_op:
|
|
|
|
if (!cpu_has_mips_r6)
|
|
|
|
return SIGILL;
|
|
|
|
|
|
|
|
DPFROMREG(rv.d, MIPSInst_FT(ir));
|
|
|
|
if (rv.l & 0x1)
|
|
|
|
DPFROMREG(rv.d, MIPSInst_FS(ir));
|
|
|
|
else
|
|
|
|
rv.l = 0;
|
|
|
|
break;
|
|
|
|
|
2015-08-13 14:56:31 +07:00
|
|
|
case fmaddf_op: {
|
|
|
|
union ieee754dp ft, fs, fd;
|
|
|
|
|
|
|
|
if (!cpu_has_mips_r6)
|
|
|
|
return SIGILL;
|
|
|
|
|
|
|
|
DPFROMREG(ft, MIPSInst_FT(ir));
|
|
|
|
DPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
DPFROMREG(fd, MIPSInst_FD(ir));
|
|
|
|
rv.d = ieee754dp_maddf(fd, fs, ft);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-08-13 14:56:32 +07:00
|
|
|
case fmsubf_op: {
|
|
|
|
union ieee754dp ft, fs, fd;
|
|
|
|
|
|
|
|
if (!cpu_has_mips_r6)
|
|
|
|
return SIGILL;
|
|
|
|
|
|
|
|
DPFROMREG(ft, MIPSInst_FT(ir));
|
|
|
|
DPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
DPFROMREG(fd, MIPSInst_FD(ir));
|
|
|
|
rv.d = ieee754dp_msubf(fd, fs, ft);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-08-13 14:56:33 +07:00
|
|
|
case frint_op: {
|
|
|
|
union ieee754dp fs;
|
|
|
|
|
|
|
|
if (!cpu_has_mips_r6)
|
|
|
|
return SIGILL;
|
|
|
|
|
|
|
|
DPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
rv.l = ieee754dp_tlong(fs);
|
|
|
|
rv.d = ieee754dp_flong(rv.l);
|
|
|
|
goto copcsr;
|
|
|
|
}
|
|
|
|
|
2015-08-13 14:56:34 +07:00
|
|
|
case fclass_op: {
|
|
|
|
union ieee754dp fs;
|
|
|
|
|
|
|
|
if (!cpu_has_mips_r6)
|
|
|
|
return SIGILL;
|
|
|
|
|
|
|
|
DPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
rv.w = ieee754dp_2008class(fs);
|
|
|
|
rfmt = w_fmt;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-08-13 14:56:35 +07:00
|
|
|
case fmin_op: {
|
|
|
|
union ieee754dp fs, ft;
|
|
|
|
|
|
|
|
if (!cpu_has_mips_r6)
|
|
|
|
return SIGILL;
|
|
|
|
|
|
|
|
DPFROMREG(ft, MIPSInst_FT(ir));
|
|
|
|
DPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
rv.d = ieee754dp_fmin(fs, ft);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case fmina_op: {
|
|
|
|
union ieee754dp fs, ft;
|
|
|
|
|
|
|
|
if (!cpu_has_mips_r6)
|
|
|
|
return SIGILL;
|
|
|
|
|
|
|
|
DPFROMREG(ft, MIPSInst_FT(ir));
|
|
|
|
DPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
rv.d = ieee754dp_fmina(fs, ft);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-08-13 14:56:36 +07:00
|
|
|
case fmax_op: {
|
|
|
|
union ieee754dp fs, ft;
|
|
|
|
|
|
|
|
if (!cpu_has_mips_r6)
|
|
|
|
return SIGILL;
|
|
|
|
|
|
|
|
DPFROMREG(ft, MIPSInst_FT(ir));
|
|
|
|
DPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
rv.d = ieee754dp_fmax(fs, ft);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case fmaxa_op: {
|
|
|
|
union ieee754dp fs, ft;
|
|
|
|
|
|
|
|
if (!cpu_has_mips_r6)
|
|
|
|
return SIGILL;
|
|
|
|
|
|
|
|
DPFROMREG(ft, MIPSInst_FT(ir));
|
|
|
|
DPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
rv.d = ieee754dp_fmaxa(fs, ft);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
case fabs_op:
|
|
|
|
handler.u = ieee754dp_abs;
|
|
|
|
goto dcopuop;
|
|
|
|
|
|
|
|
case fneg_op:
|
|
|
|
handler.u = ieee754dp_neg;
|
|
|
|
goto dcopuop;
|
|
|
|
|
|
|
|
case fmov_op:
|
|
|
|
/* an easy one */
|
|
|
|
DPFROMREG(rv.d, MIPSInst_FS(ir));
|
|
|
|
goto copcsr;
|
|
|
|
|
|
|
|
/* binary op on handler */
|
2014-04-26 06:49:14 +07:00
|
|
|
dcopbop:
|
|
|
|
DPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
DPFROMREG(ft, MIPSInst_FT(ir));
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2014-04-26 06:49:14 +07:00
|
|
|
rv.d = (*handler.b) (fs, ft);
|
|
|
|
goto copcsr;
|
|
|
|
dcopuop:
|
|
|
|
DPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
rv.d = (*handler.u) (fs);
|
|
|
|
goto copcsr;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2014-04-26 06:49:14 +07:00
|
|
|
/*
|
|
|
|
* unary conv ops
|
|
|
|
*/
|
|
|
|
case fcvts_op:
|
2005-04-17 05:20:36 +07:00
|
|
|
DPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
rv.s = ieee754sp_fdp(fs);
|
|
|
|
rfmt = s_fmt;
|
|
|
|
goto copcsr;
|
2014-04-26 06:49:14 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
case fcvtd_op:
|
|
|
|
return SIGILL; /* not defined */
|
|
|
|
|
2014-04-26 06:49:14 +07:00
|
|
|
case fcvtw_op:
|
2005-04-17 05:20:36 +07:00
|
|
|
DPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
rv.w = ieee754dp_tint(fs); /* wrong */
|
|
|
|
rfmt = w_fmt;
|
|
|
|
goto copcsr;
|
|
|
|
|
|
|
|
case fround_op:
|
|
|
|
case ftrunc_op:
|
|
|
|
case fceil_op:
|
2014-04-26 06:49:14 +07:00
|
|
|
case ffloor_op:
|
2014-04-19 18:11:37 +07:00
|
|
|
if (!cpu_has_mips_2_3_4_5_r)
|
|
|
|
return SIGILL;
|
|
|
|
|
2014-04-26 06:49:14 +07:00
|
|
|
oldrm = ieee754_csr.rm;
|
2005-04-17 05:20:36 +07:00
|
|
|
DPFROMREG(fs, MIPSInst_FS(ir));
|
2015-04-04 05:24:56 +07:00
|
|
|
ieee754_csr.rm = MIPSInst_FUNC(ir);
|
2005-04-17 05:20:36 +07:00
|
|
|
rv.w = ieee754dp_tint(fs);
|
|
|
|
ieee754_csr.rm = oldrm;
|
|
|
|
rfmt = w_fmt;
|
|
|
|
goto copcsr;
|
|
|
|
|
2016-04-21 20:04:48 +07:00
|
|
|
case fsel_op:
|
|
|
|
if (!cpu_has_mips_r6)
|
|
|
|
return SIGILL;
|
|
|
|
|
|
|
|
DPFROMREG(fd, MIPSInst_FD(ir));
|
|
|
|
if (fd.bits & 0x1)
|
|
|
|
DPFROMREG(rv.d, MIPSInst_FT(ir));
|
|
|
|
else
|
|
|
|
DPFROMREG(rv.d, MIPSInst_FS(ir));
|
|
|
|
break;
|
|
|
|
|
2014-04-26 06:49:14 +07:00
|
|
|
case fcvtl_op:
|
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 05:26:49 +07:00
|
|
|
if (!cpu_has_mips_3_4_5_64_r2_r6)
|
2014-04-19 18:11:37 +07:00
|
|
|
return SIGILL;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
DPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
rv.l = ieee754dp_tlong(fs);
|
|
|
|
rfmt = l_fmt;
|
|
|
|
goto copcsr;
|
|
|
|
|
|
|
|
case froundl_op:
|
|
|
|
case ftruncl_op:
|
|
|
|
case fceill_op:
|
2014-04-26 06:49:14 +07:00
|
|
|
case ffloorl_op:
|
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 05:26:49 +07:00
|
|
|
if (!cpu_has_mips_3_4_5_64_r2_r6)
|
2014-04-19 18:11:37 +07:00
|
|
|
return SIGILL;
|
|
|
|
|
2014-04-26 06:49:14 +07:00
|
|
|
oldrm = ieee754_csr.rm;
|
2005-04-17 05:20:36 +07:00
|
|
|
DPFROMREG(fs, MIPSInst_FS(ir));
|
2015-04-04 05:24:56 +07:00
|
|
|
ieee754_csr.rm = MIPSInst_FUNC(ir);
|
2005-04-17 05:20:36 +07:00
|
|
|
rv.l = ieee754dp_tlong(fs);
|
|
|
|
ieee754_csr.rm = oldrm;
|
|
|
|
rfmt = l_fmt;
|
|
|
|
goto copcsr;
|
|
|
|
|
|
|
|
default:
|
2015-08-13 14:56:28 +07:00
|
|
|
if (!NO_R6EMU && MIPSInst_FUNC(ir) >= fcmp_op) {
|
2005-04-17 05:20:36 +07:00
|
|
|
unsigned cmpop = MIPSInst_FUNC(ir) - fcmp_op;
|
2014-04-16 06:31:11 +07:00
|
|
|
union ieee754dp fs, ft;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
DPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
DPFROMREG(ft, MIPSInst_FT(ir));
|
|
|
|
rv.w = ieee754dp_cmp(fs, ft,
|
|
|
|
cmptab[cmpop & 0x7], cmpop & 0x8);
|
|
|
|
rfmt = -1;
|
|
|
|
if ((cmpop & 0x8)
|
|
|
|
&&
|
|
|
|
ieee754_cxtest
|
|
|
|
(IEEE754_INVALID_OPERATION))
|
|
|
|
rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S;
|
|
|
|
else
|
|
|
|
goto copcsr;
|
|
|
|
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return SIGILL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
2015-07-16 20:06:45 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
case w_fmt: {
|
|
|
|
union ieee754dp fs;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
switch (MIPSInst_FUNC(ir)) {
|
|
|
|
case fcvts_op:
|
|
|
|
/* convert word to single precision real */
|
|
|
|
SPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
rv.s = ieee754sp_fint(fs.bits);
|
|
|
|
rfmt = s_fmt;
|
|
|
|
goto copcsr;
|
|
|
|
case fcvtd_op:
|
|
|
|
/* convert word to double precision real */
|
|
|
|
SPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
rv.d = ieee754dp_fint(fs.bits);
|
|
|
|
rfmt = d_fmt;
|
|
|
|
goto copcsr;
|
2015-08-13 14:56:28 +07:00
|
|
|
default: {
|
|
|
|
/* Emulating the new CMP.condn.fmt R6 instruction */
|
|
|
|
#define CMPOP_MASK 0x7
|
|
|
|
#define SIGN_BIT (0x1 << 3)
|
|
|
|
#define PREDICATE_BIT (0x1 << 4)
|
|
|
|
|
|
|
|
int cmpop = MIPSInst_FUNC(ir) & CMPOP_MASK;
|
|
|
|
int sig = MIPSInst_FUNC(ir) & SIGN_BIT;
|
|
|
|
union ieee754sp fs, ft;
|
|
|
|
|
|
|
|
/* This is an R6 only instruction */
|
|
|
|
if (!cpu_has_mips_r6 ||
|
|
|
|
(MIPSInst_FUNC(ir) & 0x20))
|
|
|
|
return SIGILL;
|
|
|
|
|
|
|
|
/* fmt is w_fmt for single precision so fix it */
|
|
|
|
rfmt = s_fmt;
|
|
|
|
/* default to false */
|
|
|
|
rv.w = 0;
|
|
|
|
|
|
|
|
/* CMP.condn.S */
|
|
|
|
SPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
SPFROMREG(ft, MIPSInst_FT(ir));
|
|
|
|
|
|
|
|
/* 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 */
|
|
|
|
pr_err("Reserved MIPS R6 CMP.condn.S operation\n");
|
|
|
|
return SIGILL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-26 06:49:14 +07:00
|
|
|
case l_fmt:
|
2014-04-19 18:11:37 +07:00
|
|
|
|
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 05:26:49 +07:00
|
|
|
if (!cpu_has_mips_3_4_5_64_r2_r6)
|
2014-04-19 18:11:37 +07:00
|
|
|
return SIGILL;
|
|
|
|
|
2014-02-13 18:26:41 +07:00
|
|
|
DIFROMREG(bits, MIPSInst_FS(ir));
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
switch (MIPSInst_FUNC(ir)) {
|
|
|
|
case fcvts_op:
|
|
|
|
/* convert long to single precision real */
|
2014-02-13 18:26:41 +07:00
|
|
|
rv.s = ieee754sp_flong(bits);
|
2005-04-17 05:20:36 +07:00
|
|
|
rfmt = s_fmt;
|
|
|
|
goto copcsr;
|
|
|
|
case fcvtd_op:
|
|
|
|
/* convert long to double precision real */
|
2014-02-13 18:26:41 +07:00
|
|
|
rv.d = ieee754dp_flong(bits);
|
2005-04-17 05:20:36 +07:00
|
|
|
rfmt = d_fmt;
|
|
|
|
goto copcsr;
|
2015-08-13 14:56:28 +07:00
|
|
|
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;
|
|
|
|
|
|
|
|
/* fmt is l_fmt for double precision so fix it */
|
|
|
|
rfmt = d_fmt;
|
|
|
|
/* default to false */
|
|
|
|
rv.l = 0;
|
|
|
|
|
|
|
|
/* CMP.condn.D */
|
|
|
|
DPFROMREG(fs, MIPSInst_FS(ir));
|
|
|
|
DPFROMREG(ft, MIPSInst_FT(ir));
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2015-08-13 14:56:28 +07:00
|
|
|
/* positive predicates */
|
|
|
|
if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) {
|
|
|
|
if (ieee754dp_cmp(fs, ft,
|
|
|
|
cmptab[cmpop], sig))
|
|
|
|
rv.l = -1LL; /* 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 (ieee754dp_cmp(fs, ft,
|
|
|
|
negative_cmptab[cmpop],
|
|
|
|
sig))
|
|
|
|
rv.l = -1LL; /* 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 */
|
|
|
|
pr_err("Reserved MIPS R6 CMP.condn.D operation\n");
|
|
|
|
return SIGILL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
default:
|
|
|
|
return SIGILL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Update the fpu CSR register for this operation.
|
|
|
|
* If an exception is required, generate a tidy SIGFPE exception,
|
|
|
|
* without updating the result register.
|
|
|
|
* Note: cause exception bits do not accumulate, they are rewritten
|
|
|
|
* for each op; only the flag/sticky bits accumulate.
|
|
|
|
*/
|
|
|
|
ctx->fcr31 = (ctx->fcr31 & ~FPU_CSR_ALL_X) | rcsr;
|
|
|
|
if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) {
|
2014-04-26 06:49:14 +07:00
|
|
|
/*printk ("SIGFPE: FPU csr = %08x\n",ctx->fcr31); */
|
2005-04-17 05:20:36 +07:00
|
|
|
return SIGFPE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now we can safely write the result back to the register file.
|
|
|
|
*/
|
|
|
|
switch (rfmt) {
|
2014-04-19 18:11:37 +07:00
|
|
|
case -1:
|
|
|
|
|
|
|
|
if (cpu_has_mips_4_5_r)
|
2014-07-23 16:03:58 +07:00
|
|
|
cbit = fpucondbit[MIPSInst_FD(ir) >> 2];
|
2014-04-19 18:11:37 +07:00
|
|
|
else
|
|
|
|
cbit = FPU_CSR_COND;
|
2005-04-17 05:20:36 +07:00
|
|
|
if (rv.w)
|
2014-04-19 18:11:37 +07:00
|
|
|
ctx->fcr31 |= cbit;
|
2005-04-17 05:20:36 +07:00
|
|
|
else
|
2014-04-19 18:11:37 +07:00
|
|
|
ctx->fcr31 &= ~cbit;
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
2014-04-19 18:11:37 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
case d_fmt:
|
|
|
|
DPTOREG(rv.d, MIPSInst_FD(ir));
|
|
|
|
break;
|
|
|
|
case s_fmt:
|
|
|
|
SPTOREG(rv.s, MIPSInst_FD(ir));
|
|
|
|
break;
|
|
|
|
case w_fmt:
|
|
|
|
SITOREG(rv.w, MIPSInst_FD(ir));
|
|
|
|
break;
|
|
|
|
case l_fmt:
|
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 05:26:49 +07:00
|
|
|
if (!cpu_has_mips_3_4_5_64_r2_r6)
|
2014-04-19 18:11:37 +07:00
|
|
|
return SIGILL;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
DITOREG(rv.l, MIPSInst_FD(ir));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return SIGILL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-10-08 22:10:01 +07:00
|
|
|
int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
2010-10-22 06:32:26 +07:00
|
|
|
int has_fpu, void *__user *fault_addr)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-03-01 00:55:57 +07:00
|
|
|
unsigned long oldepc, prevepc;
|
2013-03-26 00:09:02 +07:00
|
|
|
struct mm_decoded_insn dec_insn;
|
|
|
|
u16 instr[4];
|
|
|
|
u16 *instr_ptr;
|
2005-04-17 05:20:36 +07:00
|
|
|
int sig = 0;
|
|
|
|
|
|
|
|
oldepc = xcp->cp0_epc;
|
|
|
|
do {
|
|
|
|
prevepc = xcp->cp0_epc;
|
|
|
|
|
2013-03-26 00:09:02 +07:00
|
|
|
if (get_isa16_mode(prevepc) && cpu_has_mmips) {
|
|
|
|
/*
|
|
|
|
* Get next 2 microMIPS instructions and convert them
|
|
|
|
* into 32-bit instructions.
|
|
|
|
*/
|
|
|
|
if ((get_user(instr[0], (u16 __user *)msk_isa16_mode(xcp->cp0_epc))) ||
|
|
|
|
(get_user(instr[1], (u16 __user *)msk_isa16_mode(xcp->cp0_epc + 2))) ||
|
|
|
|
(get_user(instr[2], (u16 __user *)msk_isa16_mode(xcp->cp0_epc + 4))) ||
|
|
|
|
(get_user(instr[3], (u16 __user *)msk_isa16_mode(xcp->cp0_epc + 6)))) {
|
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
|
return SIGBUS;
|
|
|
|
}
|
|
|
|
instr_ptr = instr;
|
|
|
|
|
|
|
|
/* Get first instruction. */
|
|
|
|
if (mm_insn_16bit(*instr_ptr)) {
|
|
|
|
/* Duplicate the half-word. */
|
|
|
|
dec_insn.insn = (*instr_ptr << 16) |
|
|
|
|
(*instr_ptr);
|
|
|
|
/* 16-bit instruction. */
|
|
|
|
dec_insn.pc_inc = 2;
|
|
|
|
instr_ptr += 1;
|
|
|
|
} else {
|
|
|
|
dec_insn.insn = (*instr_ptr << 16) |
|
|
|
|
*(instr_ptr+1);
|
|
|
|
/* 32-bit instruction. */
|
|
|
|
dec_insn.pc_inc = 4;
|
|
|
|
instr_ptr += 2;
|
|
|
|
}
|
|
|
|
/* Get second instruction. */
|
|
|
|
if (mm_insn_16bit(*instr_ptr)) {
|
|
|
|
/* Duplicate the half-word. */
|
|
|
|
dec_insn.next_insn = (*instr_ptr << 16) |
|
|
|
|
(*instr_ptr);
|
|
|
|
/* 16-bit instruction. */
|
|
|
|
dec_insn.next_pc_inc = 2;
|
|
|
|
} else {
|
|
|
|
dec_insn.next_insn = (*instr_ptr << 16) |
|
|
|
|
*(instr_ptr+1);
|
|
|
|
/* 32-bit instruction. */
|
|
|
|
dec_insn.next_pc_inc = 4;
|
|
|
|
}
|
|
|
|
dec_insn.micro_mips_mode = 1;
|
|
|
|
} else {
|
|
|
|
if ((get_user(dec_insn.insn,
|
|
|
|
(mips_instruction __user *) xcp->cp0_epc)) ||
|
|
|
|
(get_user(dec_insn.next_insn,
|
|
|
|
(mips_instruction __user *)(xcp->cp0_epc+4)))) {
|
|
|
|
MIPS_FPU_EMU_INC_STATS(errors);
|
|
|
|
return SIGBUS;
|
|
|
|
}
|
|
|
|
dec_insn.pc_inc = 4;
|
|
|
|
dec_insn.next_pc_inc = 4;
|
|
|
|
dec_insn.micro_mips_mode = 0;
|
2010-10-22 06:32:26 +07:00
|
|
|
}
|
2013-03-26 00:09:02 +07:00
|
|
|
|
|
|
|
if ((dec_insn.insn == 0) ||
|
|
|
|
((dec_insn.pc_inc == 2) &&
|
|
|
|
((dec_insn.insn & 0xffff) == MM_NOP16)))
|
|
|
|
xcp->cp0_epc += dec_insn.pc_inc; /* Skip NOPs */
|
2005-04-17 05:20:36 +07:00
|
|
|
else {
|
2005-04-28 20:39:10 +07:00
|
|
|
/*
|
2015-04-04 05:24:56 +07:00
|
|
|
* The 'ieee754_csr' is an alias of ctx->fcr31.
|
|
|
|
* No need to copy ctx->fcr31 to ieee754_csr.
|
2005-04-28 20:39:10 +07:00
|
|
|
*/
|
2013-03-26 00:09:02 +07:00
|
|
|
sig = cop1Emulate(xcp, ctx, dec_insn, fault_addr);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2006-10-08 22:10:01 +07:00
|
|
|
if (has_fpu)
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
if (sig)
|
|
|
|
break;
|
|
|
|
|
|
|
|
cond_resched();
|
|
|
|
} while (xcp->cp0_epc > prevepc);
|
|
|
|
|
|
|
|
/* SIGILL indicates a non-fpu instruction */
|
|
|
|
if (sig == SIGILL && xcp->cp0_epc != oldepc)
|
2014-04-26 06:49:14 +07:00
|
|
|
/* but if EPC has advanced, then ignore it */
|
2005-04-17 05:20:36 +07:00
|
|
|
sig = 0;
|
|
|
|
|
|
|
|
return sig;
|
|
|
|
}
|