From: David Daney <ddaney.cavm@gmail.com>
To: linux-mips@linux-mips.org, ralf@linux-mips.org
Cc: Leonid Yegoshin <Leonid.Yegoshin@imgtec.com>,
Zubair.Kakakhel@imgtec.com, geert+renesas@glider.be,
peterz@infradead.org, paul.gortmaker@windriver.com,
macro@linux-mips.org, chenhc@lemote.com, cl@linux.com,
mingo@kernel.org, richard@nod.at, zajec5@gmail.com,
james.hogan@imgtec.com, keescook@chromium.org, tj@kernel.org,
alex@alex-smith.me.uk, pbonzini@redhat.com, blogic@openwrt.org,
paul.burton@imgtec.com, qais.yousef@imgtec.com,
linux-kernel@vger.kernel.org, markos.chandras@imgtec.com,
dengcheng.zhu@imgtec.com, manuel.lauss@gmail.com,
lars.persson@axis.com, David Daney <david.daney@cavium.com>
Subject: [PATCH 2/3] MIPS: Add full ISA emulator.
Date: Wed, 3 Dec 2014 15:44:17 -0800 [thread overview]
Message-ID: <1417650258-2811-3-git-send-email-ddaney.cavm@gmail.com> (raw)
In-Reply-To: <1417650258-2811-1-git-send-email-ddaney.cavm@gmail.com>
From: David Daney <david.daney@cavium.com>
Used in follow-on patch to replace FPU delay-slot instruction
execution on stack.
Only 32-bit instructions are emulated right now, still need to add
64-bit.
Signed-off-by: David Daney <david.daney@cavium.com>
---
arch/mips/kernel/Makefile | 3 +-
arch/mips/kernel/insn-emul.c | 815 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 817 insertions(+), 1 deletion(-)
create mode 100644 arch/mips/kernel/insn-emul.c
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile
index 008a2fe..b6e550d 100644
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -6,7 +6,8 @@ extra-y := head.o vmlinux.lds
obj-y += cpu-probe.o branch.o entry.o genex.o idle.o irq.o process.o \
prom.o ptrace.o reset.o setup.o signal.o syscall.o \
- time.o topology.o traps.o unaligned.o watch.o vdso.o
+ time.o topology.o traps.o unaligned.o watch.o vdso.o \
+ insn-emul.o
ifdef CONFIG_FUNCTION_TRACER
CFLAGS_REMOVE_ftrace.o = -pg
diff --git a/arch/mips/kernel/insn-emul.c b/arch/mips/kernel/insn-emul.c
new file mode 100644
index 0000000..62e39e9
--- /dev/null
+++ b/arch/mips/kernel/insn-emul.c
@@ -0,0 +1,815 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2014 Cavium, Inc.
+ */
+
+#include <asm/inst.h>
+#include <asm/ptrace.h>
+#include <asm/barrier.h>
+#include <asm/signal.h>
+#include <asm/uaccess.h>
+
+static int mips64_spec_emul(struct pt_regs *regs, mips_instruction ir)
+{
+ return SIGILL;
+}
+
+static int mips_spec_emul(struct pt_regs *regs, mips_instruction ir)
+{
+ union mips_instruction insn;
+ u32 rs32, rt32, rd32;
+ u64 t1_64, t2_64;
+ s64 t1_64s, t2_64s;
+ u32 t1_32, t2_32;
+ s32 t1_32s, t2_32s;
+ s32 rs32s, rt32s, rd32s;
+ int sa;
+
+ insn.word = ir;
+ switch (insn.r_format.func) {
+ case sll_op:
+ if (insn.r_format.rs != 0)
+ return SIGILL;
+ if (insn.r_format.rd == 0)
+ return 0; /* ignore result "NOP" */
+ rt32 = regs->regs[insn.r_format.rt];
+ sa = insn.r_format.re;
+ rd32 = rt32 << sa;
+ regs->regs[insn.r_format.rd] = (long)(s32)rd32;
+ return 0;
+ case srl_op:
+ if (insn.r_format.rs == 0) {
+ /* SRL */
+ if (insn.r_format.rd == 0)
+ return 0; /* ignore result */
+ rt32 = regs->regs[insn.r_format.rt];
+ rd32 = rt32 >> insn.r_format.re;
+ regs->regs[insn.r_format.rd] = (long)(s32)rd32;
+ return 0;
+ } else if (insn.r_format.rs == 1) {
+ /* ROTR */
+ if (insn.r_format.rd == 0)
+ return 0; /* ignore result */
+ rt32 = regs->regs[insn.r_format.rt];
+ sa = insn.r_format.re;
+ rd32 = (rt32 >> sa) | (rt32 << (32 - sa));
+ regs->regs[insn.r_format.rd] = (long)(s32)rd32;
+ return 0;
+ }
+ return SIGILL;
+ case sra_op:
+ if (insn.r_format.rs != 0)
+ return SIGILL;
+ if (insn.r_format.rd == 0)
+ return 0; /* ignore result */
+ rt32s = regs->regs[insn.r_format.rt];
+ sa = insn.r_format.re;
+ rd32s = rt32s >> sa;
+ regs->regs[insn.r_format.rd] = (long)rd32s;
+ return 0;
+ case sllv_op:
+ if (insn.r_format.re != 0)
+ return SIGILL;
+ if (insn.r_format.rd == 0)
+ return 0; /* ignore result */
+ rs32 = regs->regs[insn.r_format.rs];
+ rt32 = regs->regs[insn.r_format.rt];
+ sa = rs32 & 0x1f;
+ rd32 = rt32 << sa;
+ regs->regs[insn.r_format.rd] = (long)(s32)rd32;
+ return 0;
+ case srlv_op:
+ if (insn.r_format.re == 0) {
+ /* SRLV */
+ if (insn.r_format.rd == 0)
+ return 0; /* ignore result */
+ rs32 = regs->regs[insn.r_format.rs];
+ rt32 = regs->regs[insn.r_format.rt];
+ sa = rs32 & 0x1f;
+ rd32 = rt32 >> sa;
+ regs->regs[insn.r_format.rd] = (long)(s32)rd32;
+ return 0;
+ } else if (insn.r_format.re == 1) {
+ /* ROTRV */
+ if (insn.r_format.rd == 0)
+ return 0; /* ignore result */
+ rs32 = regs->regs[insn.r_format.rs];
+ rt32 = regs->regs[insn.r_format.rt];
+ sa = rs32 & 0x1f;
+ rd32 = (rt32 >> sa) | (rt32 << (32 - sa));
+ regs->regs[insn.r_format.rd] = (long)(s32)rd32;
+ return 0;
+ }
+ return SIGILL;
+ case srav_op:
+ if (insn.r_format.re != 0)
+ return SIGILL;
+ if (insn.r_format.rd == 0)
+ return 0; /* ignore result */
+ rs32 = regs->regs[insn.r_format.rs];
+ rt32s = regs->regs[insn.r_format.rt];
+ sa = rs32 & 0x1f;
+ rd32s = rt32s >> sa;
+ regs->regs[insn.r_format.rd] = (long)rd32s;
+ return 0;
+ case movz_op:
+ if (insn.r_format.rs != 0)
+ return SIGILL;
+ if (insn.r_format.rd == 0)
+ return 0; /* ignore result */
+ if (regs->regs[insn.r_format.rt] == 0)
+ regs->regs[insn.r_format.rd] = regs->regs[insn.r_format.rs];
+ return 0;
+ case movn_op:
+ if (insn.r_format.rs != 0)
+ return SIGILL;
+ if (insn.r_format.rd == 0)
+ return 0; /* ignore result */
+ if (regs->regs[insn.r_format.rt] != 0)
+ regs->regs[insn.r_format.rd] = regs->regs[insn.r_format.rs];
+ return 0;
+ case sync_op:
+ mb(); /* an mb() is a SYNC */
+ return 0;
+ case mfhi_op:
+ if (insn.r_format.rs != 0 || insn.r_format.rt != 0 || insn.r_format.re != 0)
+ return SIGILL;
+ if (insn.r_format.rd == 0)
+ return 0; /* ignore result */
+ regs->regs[insn.r_format.rd] = regs->hi;
+ return 0;
+ case mthi_op:
+ if (insn.r_format.rt != 0 || insn.r_format.rd != 0 || insn.r_format.re != 0)
+ return SIGILL;
+ if (insn.r_format.rd == 0)
+ regs->hi = 0;
+ regs->hi = regs->regs[insn.r_format.rs];
+ return 0;
+ case mflo_op:
+ if (insn.r_format.rs != 0 || insn.r_format.rt != 0 || insn.r_format.re != 0)
+ return SIGILL;
+ if (insn.r_format.rd == 0)
+ return 0; /* ignore result */
+ regs->regs[insn.r_format.rd] = regs->lo;
+ return 0;
+ case mtlo_op:
+ if (insn.r_format.rt != 0 || insn.r_format.rd != 0 || insn.r_format.re != 0)
+ return SIGILL;
+ if (insn.r_format.rd == 0)
+ regs->lo = 0;
+ regs->lo = regs->regs[insn.r_format.rs];
+ return 0;
+ case mult_op:
+ if (insn.r_format.rd != 0 || insn.r_format.re != 0)
+ return SIGILL;
+ rs32s = regs->regs[insn.r_format.rs];
+ rt32s = regs->regs[insn.r_format.rt];
+ t1_64s = rs32s;
+ t2_64s = rt32s;
+ t1_64s *= t2_64s;
+ t1_64 = t1_64s;
+ t1_32 = t1_64 >> 32;
+ t2_32 = t1_64 & 0xffffffffu;
+ regs->lo = (long)(s32)t2_32;
+ regs->hi = (long)(s32)t1_32;
+ return 0;
+ case multu_op:
+ if (insn.r_format.rd != 0 || insn.r_format.re != 0)
+ return SIGILL;
+ rs32 = regs->regs[insn.r_format.rs];
+ rt32 = regs->regs[insn.r_format.rt];
+ t1_64 = rs32;
+ t2_64 = rt32;
+ t1_64 *= t2_64;
+ t1_32 = t1_64 >> 32;
+ t2_32 = t1_64 & 0xffffffffu;
+ regs->lo = (long)(s32)t2_32;
+ regs->hi = (long)(s32)t1_32;
+ return 0;
+ case div_op:
+ if (insn.r_format.rd != 0 || insn.r_format.re != 0)
+ return SIGILL;
+ rs32s = regs->regs[insn.r_format.rs];
+ rt32s = regs->regs[insn.r_format.rt];
+ if (rt32s == 0)
+ return 0; /* Undefined on div/zero, do nothing */
+ t1_32s = rs32s / rt32s;
+ t2_32s = rs32s % rt32s;
+ regs->lo = (long)t1_32s;
+ regs->hi = (long)t2_32s;
+ return 0;
+ case divu_op:
+ if (insn.r_format.rd != 0 || insn.r_format.re != 0)
+ return SIGILL;
+ rs32 = regs->regs[insn.r_format.rs];
+ rt32 = regs->regs[insn.r_format.rt];
+ if (rt32 == 0)
+ return 0; /* Undefined on div/zero, do nothing */
+ t1_32 = rs32 / rt32;
+ t2_32 = rs32 % rt32;
+ regs->lo = (long)(s32)t1_32;
+ regs->hi = (long)(s32)t2_32;
+ return 0;
+ case addu_op:
+ if (insn.r_format.re != 0)
+ return SIGILL;
+ if (insn.r_format.rd == 0)
+ return 0; /* ignore result */
+ rs32 = regs->regs[insn.r_format.rs];
+ rt32 = regs->regs[insn.r_format.rt];
+ rd32 = rs32 + rt32;
+ regs->regs[insn.r_format.rd] = (long)(s32)rd32;
+ return 0;
+ case subu_op:
+ if (insn.r_format.re != 0)
+ return SIGILL;
+ if (insn.r_format.rd == 0)
+ return 0; /* ignore result */
+ rs32 = regs->regs[insn.r_format.rs];
+ rt32 = regs->regs[insn.r_format.rt];
+ rd32 = rs32 - rt32;
+ regs->regs[insn.r_format.rd] = (long)(s32)rd32;
+ return 0;
+ case and_op:
+ if (insn.r_format.re != 0)
+ return SIGILL;
+ if (insn.r_format.rd == 0)
+ return 0; /* ignore result */
+ regs->regs[insn.r_format.rd] =
+ regs->regs[insn.r_format.rs] & regs->regs[insn.r_format.rt];
+ return 0;
+ case or_op:
+ if (insn.r_format.re != 0)
+ return SIGILL;
+ if (insn.r_format.rd == 0)
+ return 0; /* ignore result */
+ regs->regs[insn.r_format.rd] =
+ regs->regs[insn.r_format.rs] | regs->regs[insn.r_format.rt];
+ return 0;
+ case xor_op:
+ if (insn.r_format.re != 0)
+ return SIGILL;
+ if (insn.r_format.rd == 0)
+ return 0; /* ignore result */
+ regs->regs[insn.r_format.rd] =
+ regs->regs[insn.r_format.rs] ^ regs->regs[insn.r_format.rt];
+ return 0;
+ case nor_op:
+ if (insn.r_format.re != 0)
+ return SIGILL;
+ if (insn.r_format.rd == 0)
+ return 0; /* ignore result */
+ regs->regs[insn.r_format.rd] =
+ ~(regs->regs[insn.r_format.rs] | regs->regs[insn.r_format.rt]);
+ return 0;
+ case slt_op:
+ if (insn.r_format.re != 0)
+ return SIGILL;
+ if (insn.r_format.rd == 0)
+ return 0; /* ignore result */
+ regs->regs[insn.r_format.rd] =
+ ((long)regs->regs[insn.r_format.rs]) < ((long)regs->regs[insn.r_format.rt]) ? 1 : 0;
+ return 0;
+ case sltu_op:
+ if (insn.r_format.re != 0)
+ return SIGILL;
+ if (insn.r_format.rd == 0)
+ return 0; /* ignore result */
+ regs->regs[insn.r_format.rd] =
+ regs->regs[insn.r_format.rs] < regs->regs[insn.r_format.rt] ? 1 : 0;
+ return 0;
+ case dsllv_op:
+ case dsrlv_op:
+ case dsrav_op:
+ case dmult_op:
+ case dmultu_op:
+ case ddiv_op:
+ case ddivu_op:
+ case dadd_op:
+ case daddu_op:
+ case dsub_op:
+ case dsubu_op:
+ case dsll_op:
+ case dsrl_op:
+ case dsra_op:
+ case dsll32_op:
+ case dsrl32_op:
+ case dsra32_op:
+ return mips64_spec_emul(regs, ir);
+ default:
+ return SIGILL;
+ }
+}
+
+static int mips_imm_emul(struct pt_regs *regs, mips_instruction ir)
+{
+ union mips_instruction insn;
+ u32 rs32, rt32;
+ s32 rs32s;
+ u32 t1_32;
+ s32 t1_32s;
+ unsigned long t1;
+
+ insn.word = ir;
+ switch (insn.i_format.opcode) {
+ case addiu_op:
+ if (insn.i_format.rt == 0)
+ return 0; /* ignore result */
+ rs32 = regs->regs[insn.i_format.rs];
+ t1_32s = insn.i_format.simmediate;
+ t1_32 = t1_32s;
+ rt32 = rs32 + t1_32;
+ regs->regs[insn.i_format.rt] = (long)(s32)rt32;
+ return 0;
+ case slti_op:
+ if (insn.i_format.rt == 0)
+ return 0; /* ignore result */
+ rs32s = regs->regs[insn.i_format.rs];
+ t1_32s = insn.i_format.simmediate;
+ regs->regs[insn.i_format.rt] = rs32s > t1_32s ? 1 : 0;
+ return 0;
+ case sltiu_op:
+ if (insn.i_format.rt == 0)
+ return 0; /* ignore result */
+ rs32 = regs->regs[insn.i_format.rs];
+ t1_32s = insn.i_format.simmediate;
+ t1_32 = t1_32s;
+ regs->regs[insn.i_format.rt] = rs32 > t1_32 ? 1 : 0;
+ return 0;
+ case andi_op:
+ if (insn.u_format.rt == 0)
+ return 0; /* ignore result */
+ rs32 = regs->regs[insn.u_format.rs];
+ t1 = insn.u_format.uimmediate;
+ regs->regs[insn.u_format.rt] = t1 & regs->regs[insn.u_format.rs];
+ return 0;
+ case ori_op:
+ if (insn.u_format.rt == 0)
+ return 0; /* ignore result */
+ rs32 = regs->regs[insn.u_format.rs];
+ t1 = insn.u_format.uimmediate;
+ regs->regs[insn.u_format.rt] = t1 | regs->regs[insn.u_format.rs];
+ return 0;
+ case xori_op:
+ if (insn.u_format.rt == 0)
+ return 0; /* ignore result */
+ rs32 = regs->regs[insn.u_format.rs];
+ t1 = insn.u_format.uimmediate;
+ regs->regs[insn.u_format.rt] = t1 ^ regs->regs[insn.u_format.rs];
+ return 0;
+ case lui_op:
+ if (insn.u_format.rs != 0)
+ return SIGILL;
+ if (insn.u_format.rt == 0)
+ return 0; /* ignore result */
+ t1_32 = insn.u_format.uimmediate;
+ t1_32 <<= 16;
+ regs->regs[insn.u_format.rt] = (long)(s32)t1_32;
+ return 0;
+ default:
+ return SIGILL;
+ }
+}
+
+static int mips64_imm_emul(struct pt_regs *regs, mips_instruction ir)
+{
+ return SIGILL;
+}
+
+static int mips64_spec2_emul(struct pt_regs *regs, mips_instruction ir)
+{
+ return SIGILL;
+}
+
+static int mips_spec2_emul(struct pt_regs *regs, mips_instruction ir)
+{
+ union mips_instruction insn;
+ s32 rs32s, rt32s;
+ u32 rs32, rt32;
+ s64 t1_64s, t2_64s;
+ u64 t1_64, t2_64, t3_64;
+ u32 t1_32, t2_32;
+ int i;
+
+ insn.word = ir;
+ switch (insn.r_format.func) {
+ case madd_op:
+ case msub_op:
+
+ if (insn.r_format.re != 0 || insn.r_format.rd != 0)
+ return SIGILL;
+ rs32s = regs->regs[insn.r_format.rs];
+ rt32s = regs->regs[insn.r_format.rt];
+ t1_64s = rs32s;
+ t2_64s = rt32s;
+ t1_64s = t1_64s * t2_64s; /* Product */
+ t1_32 = regs->hi;
+ t2_32 = regs->lo;
+ t1_64 = t1_32;
+ t2_64 = t2_32;
+ t1_64 = (t1_64 << 32) | t2_64;
+ t2_64s = t1_64; /* (hi, lo) */
+ if (insn.r_format.func == madd_op)
+ t2_64s = t2_64s + t1_64s; /* (hi, lo) + Product */
+ else
+ t2_64s = t2_64s - t1_64s; /* (hi, lo) - Product */
+ t1_64 = t2_64s;
+
+ t2_64 = t1_64 & 0xffffffffu;
+ t1_64 = t1_64 >> 32;
+ t1_32 = t1_64;
+ t2_32 = t2_64;
+ regs->hi = (long)(s32)t1_32;
+ regs->lo = (long)(s32)t2_32;
+ return 0;
+
+ case maddu_op:
+ case msubu_op:
+ if (insn.r_format.re != 0 || insn.r_format.rd != 0)
+ return SIGILL;
+ rs32 = regs->regs[insn.r_format.rs];
+ rt32 = regs->regs[insn.r_format.rt];
+ t1_64 = rs32;
+ t2_64 = rt32;
+ t1_64 = t1_64 * t2_64; /* Product */
+ t1_32 = regs->hi;
+ t2_32 = regs->lo;
+ t2_64 = t1_32;
+ t3_64 = t2_32;
+ t2_64 = (t2_64 << 32) | t3_64; /* (hi, lo) */
+
+ if (insn.r_format.func == maddu_op)
+ t1_64 = t2_64 + t1_64; /* (hi, lo) + Product */
+ else
+ t1_64 = t2_64 - t1_64; /* (hi, lo) - Product */
+
+ t2_64 = t1_64 & 0xffffffffu;
+ t1_64 = t1_64 >> 32;
+ t1_32 = t1_64;
+ t2_32 = t2_64;
+ regs->hi = (long)(s32)t1_32;
+ regs->lo = (long)(s32)t2_32;
+ return 0;
+
+ case mul_op:
+ if (insn.r_format.re != 0)
+ return SIGILL;
+ if (insn.r_format.rd == 0)
+ return 0; /* ignore result */
+ rs32s = regs->regs[insn.r_format.rs];
+ rt32s = regs->regs[insn.r_format.rt];
+ t1_64s = rs32s;
+ t2_64s = rt32s;
+ t1_64s = t1_64s * t2_64s;
+ t1_64 = t1_64s;
+ t1_32 = t1_64;
+ regs->regs[insn.r_format.rd] = (long)(s32)t1_32;
+ return 0;
+
+ case clz_op:
+ case clo_op:
+ t2_32 = (insn.r_format.func == clz_op) ? 0 : 1;
+ if (insn.r_format.re != 0)
+ return SIGILL;
+ if (insn.r_format.rd == 0)
+ return 0; /* ignore result */
+ rs32 = regs->regs[insn.r_format.rs];
+ t1_32 = 0;
+ for (i = 31; i <= 0; i--) {
+ if (((rs32 >> i) & 1) == t2_32)
+ t1_32++;
+ else
+ break;
+ }
+ regs->regs[insn.r_format.rd] = t1_32;
+ return 0;
+
+ case dclz_op:
+ case dclo_op:
+ return mips64_spec2_emul(regs, ir);
+ default:
+ return SIGILL;
+ }
+}
+
+static int mips64_spec3_emul(struct pt_regs *regs, mips_instruction ir)
+{
+ return SIGILL;
+}
+
+static int mips_bshfl_emul(struct pt_regs *regs, mips_instruction ir)
+{
+ union mips_instruction insn;
+ u32 rt32, rd32;
+ s8 t8s;
+ s16 t16s;
+
+ insn.word = ir;
+ if (insn.r_format.rs != 0)
+ return SIGILL;
+
+ switch (insn.r_format.re) {
+ case wsbh_op:
+ if (insn.r_format.rd == 0)
+ return 0; /* ignore result */
+ rt32 = regs->regs[insn.r_format.rt];
+ rd32 = ((rt32 >> 8) & 0x00ff00ffu) | ((rt32 << 8) & 0xff00ff00u);
+ regs->regs[insn.r_format.rd] = (long)(s32)rd32;
+ return 0;
+ case seb_op:
+ if (insn.r_format.rd == 0)
+ return 0; /* ignore result */
+ t8s = regs->regs[insn.r_format.rt];
+ regs->regs[insn.r_format.rd] = (long)t8s;
+ return 0;
+ case seh_op:
+ if (insn.r_format.rd == 0)
+ return 0; /* ignore result */
+ t16s = regs->regs[insn.r_format.rt];
+ regs->regs[insn.r_format.rd] = (long)t16s;
+ return 0;
+ default:
+ return SIGILL;
+ }
+}
+
+static int mips_dbshfl_emul(struct pt_regs *regs, mips_instruction ir)
+{
+ union mips_instruction insn;
+
+ insn.word = ir;
+ switch (insn.r_format.re) {
+ case wsbh_op: /* DSBH */
+ case dshd_op:
+ default:
+ return SIGILL;
+ }
+}
+
+static int mips_spec3_emul(struct pt_regs *regs, mips_instruction ir)
+{
+ union mips_instruction insn;
+ u32 rs32, rt32;
+ u32 t1_32;
+
+ insn.word = ir;
+ switch (insn.r_format.func) {
+ case ext_op:
+ if (insn.r_format.rt == 0)
+ return 0; /* ignore result */
+ rs32 = regs->regs[insn.r_format.rs];
+ rt32 = rs32 >> insn.r_format.re;
+ rt32 = rt32 & (0xffffffffu >> (31 - insn.r_format.rd));
+ regs->regs[insn.r_format.rt] = (long)(s32)rt32;
+ return 0;
+ case ins_op:
+ if (insn.r_format.rt == 0)
+ return 0; /* ignore result */
+ t1_32 = 0xffffffffu >> (31 - insn.r_format.rd + insn.r_format.re);
+ t1_32 = t1_32 << insn.r_format.re; /* Mask */
+ rs32 = regs->regs[insn.r_format.rs];
+ rt32 = regs->regs[insn.r_format.rt];
+ rt32 = (rt32 & ~t1_32);
+ rs32 = (rs32 << insn.r_format.re) & t1_32;
+ rt32 = rt32 | rs32;
+ regs->regs[insn.r_format.rt] = (long)(s32)rt32;
+ return 0;
+ case bshfl_op:
+ return mips_bshfl_emul(regs, ir);
+ case dbshfl_op:
+ return mips_dbshfl_emul(regs, ir);
+ case rdhwr_op:
+
+ case dextm_op:
+ case dextu_op:
+ case dext_op:
+ case dinsm_op:
+ case dinsu_op:
+ case dins_op:
+ return mips64_spec3_emul(regs, ir);
+ default:
+ return SIGILL;
+ }
+}
+
+static int mips_load_emul(struct pt_regs *regs, mips_instruction ir, void *__user *fault_addr)
+{
+ s8 __user *va8s;
+ s16 __user *va16s;
+ s32 __user *va32s;
+ u8 __user *va8;
+ u16 __user *va16;
+ union mips_instruction insn;
+ long base;
+ s8 t8s;
+ u8 t8;
+ s16 t16s;
+ u16 t16;
+ s32 t32s;
+
+ insn.word = ir;
+
+ base = regs->regs[insn.i_format.rs];
+ base = base + insn.i_format.simmediate;
+
+ switch (insn.i_format.opcode) {
+ case lb_op:
+ va8s = (s8 __user *)base;
+ if (!access_ok(VERIFY_READ, va8s, sizeof(t8s))) {
+ *fault_addr = va8s;
+ return SIGBUS;
+ }
+ if (__get_user_nocheck(t8s, va8s, sizeof(t8s))) {
+ *fault_addr = va8s;
+ return SIGSEGV;
+ }
+ if (insn.i_format.rt == 0)
+ return 0; /* ignore result */
+ regs->regs[insn.i_format.rt] = (long)t8s;
+ return 0;
+ case lh_op:
+ va16s = (s16 __user *)base;
+ if (!access_ok(VERIFY_READ, va16s, sizeof(t16s))) {
+ *fault_addr = va16s;
+ return SIGBUS;
+ }
+ if (__get_user_nocheck(t16s, va16s, sizeof(t16s))) {
+ *fault_addr = va16s;
+ return SIGSEGV;
+ }
+ if (insn.i_format.rt == 0)
+ return 0; /* ignore result */
+ regs->regs[insn.i_format.rt] = (long)t16s;
+ return 0;
+ case lw_op:
+ va32s = (s32 __user *)base;
+ if (!access_ok(VERIFY_READ, va32s, sizeof(t32s))) {
+ *fault_addr = va32s;
+ return SIGBUS;
+ }
+ if (__get_user_nocheck(t32s, va32s, sizeof(t32s))) {
+ *fault_addr = va32s;
+ return SIGSEGV;
+ }
+ if (insn.i_format.rt == 0)
+ return 0; /* ignore result */
+ regs->regs[insn.i_format.rt] = (long)t32s;
+ return 0;
+ case lbu_op:
+ va8 = (u8 __user *)base;
+ if (!access_ok(VERIFY_READ, va8, sizeof(t8))) {
+ *fault_addr = va8;
+ return SIGBUS;
+ }
+ if (__get_user_nocheck(t8, va8, sizeof(t8))) {
+ *fault_addr = va8;
+ return SIGSEGV;
+ }
+ if (insn.i_format.rt == 0)
+ return 0; /* ignore result */
+ regs->regs[insn.i_format.rt] = t8;
+ return 0;
+ case lhu_op:
+ va16 = (u16 __user *)base;
+ if (!access_ok(VERIFY_READ, va16, sizeof(t16))) {
+ *fault_addr = va16;
+ return SIGBUS;
+ }
+ if (__get_user_nocheck(t16, va16, sizeof(t16))) {
+ *fault_addr = va16;
+ return SIGSEGV;
+ }
+ if (insn.i_format.rt == 0)
+ return 0; /* ignore result */
+ regs->regs[insn.i_format.rt] = t16;
+ return 0;
+ default:
+ return SIGILL;
+ }
+}
+
+static int mips_store_emul(struct pt_regs *regs, mips_instruction ir, void *__user *fault_addr)
+{
+ u8 __user *va8;
+ u16 __user *va16;
+ u32 __user *va32;
+ u8 t8;
+ u16 t16;
+ u32 t32;
+ union mips_instruction insn;
+ long base;
+
+ insn.word = ir;
+
+ base = regs->regs[insn.i_format.rs];
+ base = base + insn.i_format.simmediate;
+
+ switch (insn.i_format.opcode) {
+ case sb_op:
+ va8 = (u8 __user *)base;
+ if (!access_ok(VERIFY_WRITE, va8, sizeof(t8))) {
+ *fault_addr = va8;
+ return SIGBUS;
+ }
+ t8 = (u8)regs->regs[insn.i_format.rt];
+ if (__put_user_nocheck(t8, va8, sizeof(t8))) {
+ *fault_addr = va8;
+ return SIGSEGV;
+ }
+ return 0;
+ case sh_op:
+ va16 = (u16 __user *)base;
+ if (!access_ok(VERIFY_WRITE, va16, sizeof(t16))) {
+ *fault_addr = va16;
+ return SIGBUS;
+ }
+ t16 = (u16)regs->regs[insn.i_format.rt];
+ if (__put_user_nocheck(t16, va16, sizeof(t16))) {
+ *fault_addr = va16;
+ return SIGSEGV;
+ }
+ return 0;
+ case sw_op:
+ va32 = (u32 __user *)base;
+ if (!access_ok(VERIFY_WRITE, va32, sizeof(t32))) {
+ *fault_addr = va32;
+ return SIGBUS;
+ }
+ t32 = (u32)regs->regs[insn.i_format.rt];
+ if (__put_user_nocheck(t32, va32, sizeof(t32))) {
+ *fault_addr = va32;
+ return SIGSEGV;
+ }
+ return 0;
+ default:
+ return SIGILL;
+ }
+}
+
+static int mips64_load_emul(struct pt_regs *regs, mips_instruction ir, void *__user *fault_addr)
+{
+ union mips_instruction insn;
+
+ insn.word = ir;
+ switch (insn.i_format.opcode) {
+ default:
+ return SIGILL;
+ }
+}
+
+static int mips64_store_emul(struct pt_regs *regs, mips_instruction ir, void *__user *fault_addr)
+{
+ union mips_instruction insn;
+
+ insn.word = ir;
+ switch (insn.i_format.opcode) {
+ default:
+ return SIGILL;
+ }
+}
+
+int mips_insn_emul(struct pt_regs *regs, mips_instruction ir, void *__user *fault_addr)
+{
+ switch (MIPSInst_OPCODE(ir)) {
+ case spec_op:
+ return mips_spec_emul(regs, ir);
+ case addi_op:
+ case addiu_op:
+ case slti_op:
+ case sltiu_op:
+ case andi_op:
+ case ori_op:
+ case xori_op:
+ case lui_op:
+ return mips_imm_emul(regs, ir);
+ case daddi_op:
+ case daddiu_op:
+ return mips64_imm_emul(regs, ir);
+ case spec2_op:
+ return mips_spec2_emul(regs, ir);
+ case spec3_op:
+ return mips_spec3_emul(regs, ir);
+ case lb_op:
+ case lh_op:
+ case lw_op:
+ case lbu_op:
+ case lhu_op:
+ return mips_load_emul(regs, ir, fault_addr);
+ case sb_op:
+ case sh_op:
+ case sw_op:
+ return mips_store_emul(regs, ir, fault_addr);
+ case lwu_op:
+ case ld_op:
+ return mips64_load_emul(regs, ir, fault_addr);
+ case sd_op:
+ return mips64_store_emul(regs, ir, fault_addr);
+ case pref_op:
+ return 0; /* OK to ignore PREF */
+ default:
+ return SIGILL;
+ }
+}
--
1.7.11.7
next prev parent reply other threads:[~2014-12-03 23:45 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-12-03 23:44 [PATCH 0/3] MIPS: Get ready for non-executable stack David Daney
2014-12-03 23:44 ` [PATCH 1/3] MIPS: Add FPU emulator counter for non-FPU instructions emulated David Daney
2014-12-03 23:44 ` David Daney [this message]
2014-12-03 23:55 ` [PATCH 2/3] MIPS: Add full ISA emulator Leonid Yegoshin
2014-12-03 23:55 ` Leonid Yegoshin
2014-12-04 0:20 ` David Daney
2014-12-04 0:20 ` David Daney
2014-12-04 0:52 ` Leonid Yegoshin
2014-12-04 0:52 ` Leonid Yegoshin
2014-12-04 1:29 ` David Daney
2014-12-04 1:29 ` David Daney
[not found] ` <547FBF63.70802@imgtec.com>
2014-12-04 2:21 ` David Daney
2014-12-04 2:21 ` David Daney
2014-12-04 10:16 ` Paul Burton
2014-12-04 10:16 ` Paul Burton
2014-12-04 10:45 ` Qais Yousef
2014-12-04 10:45 ` Qais Yousef
2014-12-04 11:49 ` Maciej W. Rozycki
2014-12-04 17:40 ` David Daney
2014-12-04 17:40 ` David Daney
2014-12-04 20:32 ` Maciej W. Rozycki
2014-12-03 23:44 ` [PATCH 3/3] MIPS: Use full instruction emulation for FPU emulator delay slot emulation David Daney
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1417650258-2811-3-git-send-email-ddaney.cavm@gmail.com \
--to=ddaney.cavm@gmail.com \
--cc=Leonid.Yegoshin@imgtec.com \
--cc=Zubair.Kakakhel@imgtec.com \
--cc=alex@alex-smith.me.uk \
--cc=blogic@openwrt.org \
--cc=chenhc@lemote.com \
--cc=cl@linux.com \
--cc=david.daney@cavium.com \
--cc=dengcheng.zhu@imgtec.com \
--cc=geert+renesas@glider.be \
--cc=james.hogan@imgtec.com \
--cc=keescook@chromium.org \
--cc=lars.persson@axis.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mips@linux-mips.org \
--cc=macro@linux-mips.org \
--cc=manuel.lauss@gmail.com \
--cc=markos.chandras@imgtec.com \
--cc=mingo@kernel.org \
--cc=paul.burton@imgtec.com \
--cc=paul.gortmaker@windriver.com \
--cc=pbonzini@redhat.com \
--cc=peterz@infradead.org \
--cc=qais.yousef@imgtec.com \
--cc=ralf@linux-mips.org \
--cc=richard@nod.at \
--cc=tj@kernel.org \
--cc=zajec5@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.