/*--------------------------------------------------------------------*/ /*--- begin guest_nanomips_toIR.c ---*/ /*--------------------------------------------------------------------*/ /* This file is part of Valgrind, a dynamic binary instrumentation framework. Copyright (C) 2017-2018 RT-RK This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The GNU General Public License is contained in the file COPYING. */ /* Translates nanoMIPS code to IR. */ #include "libvex_basictypes.h" #include "libvex_ir.h" #include "libvex.h" #include "libvex_guest_mips32.h" #include "main_util.h" #include "main_globals.h" #include "guest_generic_bb_to_IR.h" #include "guest_nanomips_defs.h" #define P16 0x4 #define DIP(format, args...) \ if (vex_traceflags & VEX_TRACE_FE) \ vex_printf(format, ## args) #define OFFB_PC offsetof(VexGuestMIPS32State, guest_PC) #define ILLEGAL_INSTRUCTON \ dres->jk_StopHere = Ijk_SigILL; \ dres->whatNext = Dis_StopHere; #define LLADDR_INVALID (mkU32(0xFFFFFFFF)) /* MOD: The IRSB* into which we're generating code. */ static IRSB *irsb; /* CONST: The guest address for the instruction currently being translated. */ static Addr32 guest_PC_curr_instr; /* Do a endian load of a 16-bit word, regardless of the endianness of the underlying host. */ static inline UShort getUShort(const UChar * p) { UShort w = 0; #if defined (_MIPSEL) w = (w << 8) | p[1]; w = (w << 8) | p[0]; #elif defined (_MIPSEB) w = (w << 8) | p[0]; w = (w << 8) | p[1]; #endif return w; } /* Do a endian load of a 32-bit code word. */ static inline UInt getUInt(const UChar * p) { return (getUShort(p) << 16) | getUShort(p + 2); } const UChar GPR3_list[] = { 16, 17, 18, 19, 4, 5, 6, 7 }; const UChar GPR4_list[] = { 8, 9, 10, 11, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23 }; const UChar GPR4_zero_list[] = { 8, 9, 10, 0, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23 }; const UChar GPR3_src_store_list[] = { 0, 17, 18, 19, 4, 5, 6, 7 }; const UChar GPR2_reg1_list[] = { 4, 5, 6, 7 }; const UChar GPR2_reg2_list[] = { 5, 6, 7, 8 }; static UInt integerGuestRegOffset(UInt iregNo) { /* Maybe we should use formula ??? */ switch (iregNo) { case 0: return offsetof(VexGuestMIPS32State, guest_r0); case 1: return offsetof(VexGuestMIPS32State, guest_r1); case 2: return offsetof(VexGuestMIPS32State, guest_r2); case 3: return offsetof(VexGuestMIPS32State, guest_r3); case 4: return offsetof(VexGuestMIPS32State, guest_r4); case 5: return offsetof(VexGuestMIPS32State, guest_r5); case 6: return offsetof(VexGuestMIPS32State, guest_r6); case 7: return offsetof(VexGuestMIPS32State, guest_r7); case 8: return offsetof(VexGuestMIPS32State, guest_r8); case 9: return offsetof(VexGuestMIPS32State, guest_r9); case 10: return offsetof(VexGuestMIPS32State, guest_r10); case 11: return offsetof(VexGuestMIPS32State, guest_r11); case 12: return offsetof(VexGuestMIPS32State, guest_r12); case 13: return offsetof(VexGuestMIPS32State, guest_r13); case 14: return offsetof(VexGuestMIPS32State, guest_r14); case 15: return offsetof(VexGuestMIPS32State, guest_r15); case 16: return offsetof(VexGuestMIPS32State, guest_r16); case 17: return offsetof(VexGuestMIPS32State, guest_r17); case 18: return offsetof(VexGuestMIPS32State, guest_r18); case 19: return offsetof(VexGuestMIPS32State, guest_r19); case 20: return offsetof(VexGuestMIPS32State, guest_r20); case 21: return offsetof(VexGuestMIPS32State, guest_r21); case 22: return offsetof(VexGuestMIPS32State, guest_r22); case 23: return offsetof(VexGuestMIPS32State, guest_r23); case 24: return offsetof(VexGuestMIPS32State, guest_r24); case 25: return offsetof(VexGuestMIPS32State, guest_r25); case 26: return offsetof(VexGuestMIPS32State, guest_r26); case 27: return offsetof(VexGuestMIPS32State, guest_r27); case 28: return offsetof(VexGuestMIPS32State, guest_r28); case 29: return offsetof(VexGuestMIPS32State, guest_r29); case 30: return offsetof(VexGuestMIPS32State, guest_r30); case 31: return offsetof(VexGuestMIPS32State, guest_r31); } vassert(0); return 0; } /* Add a statement to the list held by "irsb". */ static void stmt(IRStmt * st) { addStmtToIRSB(irsb, st); } static IRExpr *mkU8(UInt i) { vassert(i < 256); return IRExpr_Const(IRConst_U8((UChar) i)); } /* Create an expression node for a 32-bit integer constant. */ static IRExpr *mkU32(UInt i) { return IRExpr_Const(IRConst_U32(i)); } static void putPC(IRExpr * e) { stmt(IRStmt_Put(OFFB_PC, e)); } static void putIReg(UInt archreg, IRExpr * e) { vassert(archreg < 32); if (archreg != 0) stmt(IRStmt_Put(integerGuestRegOffset(archreg), e)); } static IRExpr *getIReg(UInt iregNo) { if (0 == iregNo) { return mkU32(0x0); } else { IRType ty = Ity_I32; vassert(iregNo < 32); return IRExpr_Get(integerGuestRegOffset(iregNo), ty); } } static void putLLaddr(IRExpr * e) { stmt(IRStmt_Put(offsetof(VexGuestMIPS32State, guest_LLaddr), e)); } static IRExpr *getLLaddr(void) { return IRExpr_Get(offsetof(VexGuestMIPS32State, guest_LLaddr), Ity_I32); } static void putLLdata(IRExpr * e) { stmt(IRStmt_Put(offsetof(VexGuestMIPS32State, guest_LLdata), e)); } static IRExpr *getLLdata(void) { return IRExpr_Get(offsetof(VexGuestMIPS32State, guest_LLdata), Ity_I32); } static void putLLdata64(IRExpr * e) { stmt(IRStmt_Put(offsetof(VexGuestMIPS32State, guest_LLdata64), e)); } static IRExpr *getLLdata64(void) { return IRExpr_Get(offsetof(VexGuestMIPS32State, guest_LLdata64), Ity_I64); } static IRExpr *unop(IROp op, IRExpr * a) { return IRExpr_Unop(op, a); } static IRExpr *binop(IROp op, IRExpr * a1, IRExpr * a2) { return IRExpr_Binop(op, a1, a2); } static IRExpr *qop(IROp op, IRExpr * a1, IRExpr * a2, IRExpr * a3, IRExpr * a4) { return IRExpr_Qop(op, a1, a2, a3, a4); } /* Generate a new temporary of the given type. */ static IRTemp newTemp(IRType ty) { vassert(isPlausibleIRType(ty)); return newIRTemp(irsb->tyenv, ty); } static void assign(IRTemp dst, IRExpr * e) { stmt(IRStmt_WrTmp(dst, e)); } static void store(IRExpr * addr, IRExpr * data) { #if defined (_MIPSEL) stmt(IRStmt_Store(Iend_LE, addr, data)); #elif defined (_MIPSEB) stmt(IRStmt_Store(Iend_BE, addr, data)); #endif } static IRExpr *load(IRType ty, IRExpr * addr) { IRExpr *load1 = NULL; #if defined (_MIPSEL) load1 = IRExpr_Load(Iend_LE, ty, addr); #elif defined (_MIPSEB) load1 = IRExpr_Load(Iend_BE, ty, addr); #endif return load1; } static IRExpr *mkexpr(IRTemp tmp) { return IRExpr_RdTmp(tmp); } static UInt extend_sign(UInt value, UChar from_nbits) { UChar shift = 32 - from_nbits; return (UInt)((((Int) value) << shift) >> shift); } static void ir_for_branch(DisResult *dres, IRExpr *guard, UChar length, Int offset) { dres->whatNext = Dis_StopHere; dres->jk_StopHere = Ijk_Boring; stmt(IRStmt_Exit(guard, Ijk_Boring, IRConst_U32(guest_PC_curr_instr + length + offset), OFFB_PC)); putPC(mkU32(guest_PC_curr_instr + length)); } static void nano_plsu12(DisResult *dres, UInt cins) { UChar rs = (cins >> 16) & 0x1F; UChar rt = (cins >> 21) & 0x1F; UShort u = cins & 0x0FFF; switch ((cins >> 12) & 0x0F) { case PLSU12_LB: { /* lb[u12] */ DIP("lb[u12] r%u %u(r%u)", rt, u, rs); putIReg(rt, unop(Iop_8Sto32, load(Ity_I8, binop(Iop_Add32, getIReg(rs), mkU32(u))))); break; } case PLSU12_LH: { /* lh[u12] */ DIP("lh[u12] r%u %u(r%u)", rt, u, rs); putIReg(rt, unop(Iop_16Sto32, load(Ity_I16, binop(Iop_Add32, getIReg(rs), mkU32(u))))); break; } case PLSU12_LW: { /* lw[u12] */ DIP("lw[u12] r%u %u(r%u)", rt, u, rs); putIReg(rt, load(Ity_I32, binop(Iop_Add32, getIReg(rs), mkU32(u)))); break; } case PLSU12_LD: { /* ld[u12] */ DIP("ld[u12] r%u %u(r%u)", rt, u, rs); vassert(0); break; } case PLSU12_SB: { /* sb[u12] */ DIP("sb[12] r%u %u(r%u)", rt, u, rs); store(binop(Iop_Add32, getIReg(rs), mkU32(u)), unop(Iop_32to8, getIReg(rt))); break; } case PLSU12_SH: { /* sh[u12] */ DIP("sh[u12] r%u %u(r%u)", rt, u, rs); store(binop(Iop_Add32, getIReg(rs), mkU32(u)), unop(Iop_32to16, getIReg(rt))); break; } case PLSU12_SW: { /* sw[u12] */ DIP("sw[u12] r%u, %u(r%u)", rt, u, rs); store(binop(Iop_Add32, getIReg(rs), mkU32(u)), getIReg(rt)); break; } case PLSU12_SD: { /* sd[u12] */ DIP("sd[u12] r%u, %u(r%u)", rt, u, rs); vassert(0); } case PLSU12_LBU: { /* lbu[u12] */ DIP("lbu r%u, %u(r%u)", rt, u, rs); putIReg(rt, unop(Iop_8Uto32, load(Ity_I8, binop(Iop_Add32, getIReg(rs), mkU32(u))))); break; } case PLSU12_LHU: { /* lhu[u12] */ DIP("lhu[u12] r%u %u(r%u)", rt, u, rs); putIReg(rt, unop(Iop_16Uto32, load(Ity_I16, binop(Iop_Add32, getIReg(rs), mkU32(u))))); break; } case PLSU12_LWC1: { /* lwc1[u12] */ DIP("lwc1[u12] r%u %u(r%u)", rt, u, rs); vassert(0); break; } case PLSU12_LDC1: { /* ldc1[u12] */ DIP("ldc1[u12] r%u %u(r%u)", rt, u, rs); vassert(0); break; } case PLSU12_PREF: { /* pref[u12] */ DIP("pref[u12] r%u %u(r%u)", rt, u, rs); break; } case PLSU12_LWU: { /* lwu[u12] */ DIP("lwu[u12] r%u %u(r%u)", rt, u, rs); vassert(0); break; } case PLSU12_SWC1: { /* swc1[u12] */ DIP("swc1[u12] r%u %u(r%u)", rt, u, rs); vassert(0); break; } case PLSU12_SDC1: { /* sdc1[u12] */ DIP("sdc1[u12] r%u %u(r%u)", rt, u, rs); vassert(0); break; } default: vassert(0); } } static void nano_pl32a0(DisResult *dres, UInt cins) { UChar rd = (cins >> 11) & 0x1F; UChar rs = (cins >> 16) & 0x1F; UChar rt = (cins >> 21) & 0x1F; IRTemp t1 = newTemp(Ity_I32); IRTemp t2 = newTemp(Ity_I64); switch ((cins >> 3) & 0x07F) { case _POOL32A0_PTRAP: { if ((cins >> 10) & 0x01) { /* tne */ DIP("tne r%u, r%u", rs, rt); stmt(IRStmt_Exit(binop(Iop_CmpNE32, getIReg(rs), getIReg(rt)), Ijk_SigTRAP, IRConst_U32(guest_PC_curr_instr + 4), OFFB_PC)); } else { /* teq */ UChar trap_code = (cins >> 11) & 0x1F; DIP("teq r%u, r%u %u", rs, rt, trap_code); if (trap_code == 7) stmt(IRStmt_Exit(binop(Iop_CmpEQ32, getIReg(rs), getIReg(rt)), Ijk_SigFPE_IntDiv, IRConst_U32(guest_PC_curr_instr + 4), OFFB_PC)); else if (trap_code == 6) stmt(IRStmt_Exit(binop(Iop_CmpEQ32, getIReg(rs), getIReg(rt)), Ijk_SigFPE_IntOvf, IRConst_U32(guest_PC_curr_instr + 4), OFFB_PC)); else stmt(IRStmt_Exit(binop(Iop_CmpEQ32, getIReg(rs), getIReg(rt)), Ijk_SigTRAP, IRConst_U32(guest_PC_curr_instr + 4), OFFB_PC)); } break; } case _POOL32A0_SEB: { /* seb */ DIP("seb r%u, r%u", rs, rt); putIReg(rt, unop(Iop_8Sto32, unop(Iop_32to8, getIReg(rs)))); break; } case _POOL32A0_SEH: { /* seh */ DIP("seh r%u, r%u", rs, rt); putIReg(rt, unop(Iop_16Sto32, unop(Iop_32to16, getIReg(rs)))); break; } case _POOL32A0_SLLV: { /* sllv */ DIP("sllv r%u, r%u, r%u", rd, rs, rt); assign(t1, binop(Iop_And32, getIReg(rt), mkU32(0x1f))); putIReg(rd, binop(Iop_Shl32, getIReg(rs), unop(Iop_32to8, mkexpr(t1)))); break; } case _POOL32A0_MUL32: { /* mul */ DIP("mul[32] r%u, r%u, r%u", rd, rs, rt); putIReg(rd, unop(Iop_64to32, binop(Iop_MullS32, getIReg(rt), getIReg(rs)))); break; } case _POOL32A0_MUH: { /* muh */ DIP("muh r%u, r%u, r%u", rd, rs, rt); putIReg(rd, unop(Iop_64HIto32, binop(Iop_MullS32, getIReg(rt), getIReg(rs)))); break; } case _POOL32A0_MULU: { /* mulu */ DIP("mulu r%u, r%u, r%u", rd, rs, rt); putIReg(rd, unop(Iop_64to32, binop(Iop_MullU32, getIReg(rt), getIReg(rs)))); break; } case _POOL32A0_MUHU: { /* muhu */ DIP("muhu r%u, r%u, r%u", rd, rs, rt); putIReg(rd, unop(Iop_64HIto32, binop(Iop_MullU32, getIReg(rt), getIReg(rs)))); break; } case _POOL32A0_DIV: { /* div */ DIP("div r%u, r%u, r%u", rd, rs, rt); putIReg(rd, binop(Iop_DivS32, getIReg(rs), getIReg(rt))); break; } case _POOL32A0_MOD: { /* mod */ DIP("mod r%u, r%u, r%u", rd, rs, rt); putIReg(rd, unop(Iop_64HIto32, binop(Iop_DivModS32to32, getIReg(rs), getIReg(rt)))); break; } case _POOL32A0_DIVU: { /* divu */ DIP("divu r%u, r%u, r%u", rd, rs, rt); putIReg(rd, binop(Iop_DivU32, getIReg(rs), getIReg(rt))); break; } case _POOL32A0_MODU: { /* modu */ DIP("modu r%u, r%u, r%u", rd, rs, rt); putIReg(rd, unop(Iop_64HIto32, binop(Iop_DivModU32to32, getIReg(rs), getIReg(rt)))); break; } case _POOL32A0_SRLV: { /* srlv */ DIP("srlv r%u, r%u, r%u", rd, rs, rt); assign(t1, binop(Iop_And32, getIReg(rt), mkU32(0x1f))); putIReg(rd, binop(Iop_Shr32, getIReg(rs), unop(Iop_32to8, mkexpr(t1)))); break; } case _POOL32A0_SRAV: { /* srav */ DIP("srav r%u, r%u, r%u", rd, rs, rt); assign(t1, binop(Iop_And32, getIReg(rt), mkU32(0x1f))); putIReg(rd, binop(Iop_Sar32, getIReg(rs), unop(Iop_32to8, mkexpr(t1)))); break; } case _POOL32A0_ROTRV: { /* rotrv */ DIP("rotv r%u, r%u, r%u", rd, rs, rt); assign(t1, binop(Iop_And32, getIReg(rt), mkU32(0x1f))); assign(t2, binop(Iop_32HLto64, getIReg(rs), getIReg(rs))); putIReg(rd, unop(Iop_64to32, binop(Iop_Shr64, mkexpr(t2), unop(Iop_32to8, mkexpr(t1))))); break; } case _POOL32A0_AND32: { /* and[32] */ DIP("and[32] r%u, r%u, r%u", rd, rs, rt); putIReg(rd, binop(Iop_And32, getIReg(rs), getIReg(rt))); break; } case _POOL32A0_ADD: { /* add */ DIP("add r%u, r%u, r%u", rd, rs, rt); // if overflows(sum, nbits=32): raise exception('OV') putIReg(rd, binop(Iop_Add32, getIReg(rs), getIReg(rt))); break; } case _POOL32A0_ADDU32: { /* addu[32] */ DIP("addu[32] r%u, r%u, r%u", rd, rs, rt); putIReg(rd, binop(Iop_Add32, getIReg(rs), getIReg(rt))); break; } case _POOL32A0_SUB: { /* sub */ DIP("sub r%u, r%u, r%u", rd, rs, rt); // if overflows(result, nbits=32): raise exception('OV') putIReg(rd, binop(Iop_Sub32, getIReg(rs), getIReg(rt))); break; } case _POOL32A0_SUBU32: { /* subu[32] */ DIP("subu[32] r%u, r%u, r%u", rd, rs, rt); putIReg(rd, binop(Iop_Sub32, getIReg(rs), getIReg(rt))); break; } case _POOL32A0_OR32: { /* or[32] */ DIP("or[32] r%u, r%u, r%u", rd, rs, rt); putIReg(rd, binop(Iop_Or32, getIReg(rs), getIReg(rt))); break; } case _POOL32A0_NOR: { /* nor */ DIP("nor r%u, r%u, r%u", rd, rs, rt); putIReg(rd, unop(Iop_Not32, binop(Iop_Or32, getIReg(rs), getIReg(rt)))); break; } case _POOL32A0_XOR32: { /* xor[32] */ DIP("xor[32] r%u, r%u, r%u", rd, rs, rt); putIReg(rd, binop(Iop_Xor32, getIReg(rs), getIReg(rt))); break; } case _POOL32A0_SLT: { /* slt */ DIP("slt r%u, r%u, r%u", rd, rs, rt); putIReg(rd, unop(Iop_1Uto32, binop(Iop_CmpLT32S, getIReg(rs), getIReg(rt)))); break; } case _POOL32A0_PSLTU: { /* p.sltu */ if (rd == 0) { vassert(0); } else { /* sltu */ DIP("sltu r%u, r%u, r%u", rd, rs, rt); putIReg(rd, unop(Iop_1Uto32, binop(Iop_CmpLT32U, getIReg(rs), getIReg(rt)))); } break; } case _POOL32A0_SOV: { /* sov */ IRTemp t33 = newTemp(Ity_I32); IRTemp t0 = newTemp(Ity_I32); DIP("sov r%u, r%u, r%u", rd, rs, rt); assign(t1, binop(Iop_Add32, getIReg(rs), getIReg(rt))); assign(t33, binop(Iop_Add32, binop(Iop_Sar32, getIReg(rs), mkU8(1)), binop(Iop_Sar32, getIReg(rt), mkU8(1)))); assign(t0, binop(Iop_And32, binop(Iop_And32, getIReg(rs), getIReg(rt)), mkU32(1))); putIReg(rd, unop(Iop_1Uto32, binop(Iop_CmpNE32, binop(Iop_Sar32, mkexpr(t1), mkU8(1)), binop(Iop_Add32, mkexpr(t33), mkexpr(t0))))); // GPR[rd] = 1 if overflows(sum, nbits=32) else 0 break; } case _POOL32A0_PCMOVE: { /* p.cmove */ if (cins & 0x400) { /* movn */ DIP("movn r%u, r%u, r%u", rd, rs, rt); putIReg(rd, IRExpr_ITE(binop(Iop_CmpNE32, getIReg(rt), mkU32(0x00)), getIReg(rs), getIReg(rd))); } else { /* movz */ DIP("movz r%u, r%u, r%u", rd, rs, rt); putIReg(rd, IRExpr_ITE(binop(Iop_CmpEQ32, getIReg(rt), mkU32(0x00)), getIReg(rs), getIReg(rd))); } break; } case _POOL32A0_RDHWR: /* RDHWR */ DIP("rdhwr r%u, r%u", rt, rs); if (rs == 29) { putIReg(rt, IRExpr_Get(offsetof(VexGuestMIPS32State, guest_ULR), Ity_I32)); break; } else if (rs <= 3) { IRExpr** arg = mkIRExprVec_1(mkU32(rs)); IRTemp val = newTemp(Ity_I32); IRDirty *d = unsafeIRDirty_1_N(val, 0, "nanomips_dirtyhelper_rdhwr", &nanomips_dirtyhelper_rdhwr, arg); stmt(IRStmt_Dirty(d)); putIReg(rt, mkexpr(val)); break; } else { vex_printf("Unsupported RDHWR variant"); vassert(0); } default: vex_printf("Unrecognized _POOL32A0 instruction %08X", (cins >> 3) & 0x07F); vassert(0); } } static void nano_pplsx(DisResult *dres, UInt cins) { UChar rd = (cins >> 11) & 0x1F; UChar rs = (cins >> 16) & 0x1F; UChar rt = (cins >> 21) & 0x1F; switch ((cins >> 7) & 0x0F) { case LBX: { /* lbx */ DIP("lbx r%u, %u(r%u)", rd, rs, rt); putIReg(rd, unop(Iop_8Sto32, load(Ity_I8, binop(Iop_Add32, getIReg(rs), getIReg(rt))))); break; } case SBX: { /* sbx */ DIP("sbx r%u %u(r%u)", rd, rs, rt); store(binop(Iop_Add32, getIReg(rs), getIReg(rt)), unop(Iop_32to8, getIReg(rd))); break; } case LBUX: { /* lbux */ DIP("lbux r%u, %u(r%u)", rd, rs, rt); putIReg(rd, unop(Iop_8Uto32, load(Ity_I8, binop(Iop_Add32, getIReg(rs), getIReg(rt))))); break; } case LHX: { DIP("lhx r%u, %u(r%u)", rd, rs, rt); putIReg(rd, unop(Iop_16Sto32, load(Ity_I16, binop(Iop_Add32, getIReg(rs), getIReg(rt))))); break; } case SHX: { DIP("shx r%u %u(r%u)", rd, rs, rt); store(binop(Iop_Add32, getIReg(rs), getIReg(rt)), unop(Iop_32to16, getIReg(rd))); break; } case LHUX: { DIP("lbux r%u, %u(r%u)", rd, rs, rt); putIReg(rd, unop(Iop_16Uto32, load(Ity_I16, binop(Iop_Add32, getIReg(rs), getIReg(rt))))); break; } case LWX: { DIP("lwx r%u, %u(r%u)", rd, rs, rt); putIReg(rd, load(Ity_I32, binop(Iop_Add32, getIReg(rs), getIReg(rt)))); break; } case SWX: { DIP("swx r%u %u(r%u)", rd, rs, rt); store(binop(Iop_Add32, getIReg(rs), getIReg(rt)), getIReg(rd)); break; } default: vassert(0); break; } } static void nano_pplsxs(DisResult *dres, UInt cins) { UChar rd = (cins >> 11) & 0x1F; UChar rs = (cins >> 16) & 0x1F; UChar rt = (cins >> 21) & 0x1F; switch ((cins >> 7) & 0x0F) { case LHXS: { DIP("lhxs r%u, %u(r%u)", rd, rs, rt); putIReg(rd, unop(Iop_16Sto32, load(Ity_I16, binop(Iop_Add32, binop(Iop_Shl32, getIReg(rs), mkU8(0x01)), getIReg(rt))))); break; } case SHXS: { DIP("shxs r%u %u(r%u)", rd, rs, rt); store(binop(Iop_Add32, binop(Iop_Shl32, getIReg(rs), mkU8(0x01)), getIReg(rt)), unop(Iop_32to16, getIReg(rd))); break; } case LHUXS: { DIP("lbuxs r%u, %u(r%u)", rd, rs, rt); putIReg(rd, unop(Iop_16Uto32, load(Ity_I16, binop(Iop_Add32, binop(Iop_Shl32, getIReg(rs), mkU8(0x01)), getIReg(rt))))); break; } case LWXS32: { DIP("lwxs[32] r%u, r%u(r%u)", rd, rs, rt); putIReg(rd, load(Ity_I32, binop(Iop_Add32, binop(Iop_Shl32, getIReg(rs), mkU8(0x02)), getIReg(rt)))); break; } case SWXS: { DIP("swxs r%u %u(r%u)", rd, rs, rt); store(binop(Iop_Add32, binop(Iop_Shl32, getIReg(rs), mkU8(0x02)), getIReg(rt)), getIReg(rd)); break; } default: vassert(0); break; } } static void nano_plsx(DisResult *dres, UInt cins) { if ((cins >> 6) & 0x01) { nano_pplsxs(dres, cins); } else { nano_pplsx(dres, cins); } } static void nano_pool32Axf_4(DisResult *dres, UInt cins) { UChar rs = (cins >> 16) & 0x1F; UChar rt = (cins >> 21) & 0x1F; IRTemp t1; switch ((cins >> 9) & 0x7F) { case nano_POOL32Axf4_CLO: { /* clo */ DIP("clo r%u, r%u", rt, rs); t1 = newTemp(Ity_I1); assign(t1, binop(Iop_CmpEQ32, getIReg(rs), mkU32(0xffffffff))); putIReg(rt, IRExpr_ITE(mkexpr(t1), mkU32(0x00000020), unop(Iop_Clz32, unop(Iop_Not32, getIReg(rs))))); break; } case nano_POOL32Axf4_CLZ: { /* clz */ DIP("clz r%u, r%u", rt, rs); putIReg(rt, unop(Iop_Clz32, getIReg(rs))); break; } } } static void nano_p32Axf(DisResult *dres, UInt cins) { switch ((cins >> 6) & 0x7) { case POOL32aXF_4: nano_pool32Axf_4(dres, cins); break; case POOL32aXF_5: vassert(0); break; default: vex_printf("Unrecognized pool32Axf instruction %08X\n", cins); vassert(0); break; } } static void nano_pool32a7(DisResult *dres, UInt cins) { UChar rd = (cins >> 11) & 0x1F; UChar rs = (cins >> 16) & 0x1F; UChar rt = (cins >> 21) & 0x1F; UChar u2 = (cins >> 9) & 0x03; UChar shift = (cins >> 6) & 0x1F; switch ((cins >> 3) & 7) { case _POOL32A7_PLSX: nano_plsx(dres, cins); break; case _POOL32A7_LSA: { /* lsa */ DIP("lsa r%u r%u, r%u", rd, rs, rt); putIReg(rd, binop(Iop_Add32, binop(Iop_Shl32, getIReg(rs), mkU8(u2)), getIReg(rt))); break; } case _POOL32A7_EXTW: { /*extw*/ DIP("extw r%u r%u, r%u, %u", rd, rs, rt, shift); IRTemp t1 = newTemp(Ity_I64); assign(t1, binop(Iop_32HLto64, getIReg(rt), getIReg(rs))); putIReg(rd, unop(Iop_64to32, binop(Iop_Shr64, mkexpr(t1), mkU8(shift)))); break; } case _POOL32A7_P32Axf: { nano_p32Axf(dres, cins); break; } default: vex_printf("Unrecognized _POOL32A7 instruction %08X", cins); vassert(0); } } static void nano_p32a(DisResult *dres, UInt cins) { switch (cins & 0x7) { case P32A_POOL32A0: nano_pl32a0(dres, cins); break; case P32A_POOL32A7: nano_pool32a7(dres, cins); break; default: vex_printf("Unrecognized P32A instruction %08X", cins); vassert(0); } } static void nano_pbal(DisResult *dres, UInt cins) { Int s = extend_sign((cins & 0x1FFFFFE) | ((cins & 1) << 25), 26); if (cins & 0x2000000) { /* BALC[32] */ DIP("balc %0X", guest_PC_curr_instr + 4 + s); putIReg(31, mkU32(guest_PC_curr_instr + 4)); dres->jk_StopHere = Ijk_Call; } else { /* BC[32] */ DIP("bc %0X", guest_PC_curr_instr + 4 + s); dres->jk_StopHere = Ijk_Boring; } putPC(mkU32(guest_PC_curr_instr + 4 + s)); dres->whatNext = Dis_StopHere; } static void nano_ppsr(DisResult *dres, UInt cins) { UInt u = cins & 0xFF8; UChar count = (cins >> 16) & 0x0F; UChar rt = (cins >> 21) & 0x1F; UChar counter = 0; Bool jr = True; switch (cins & 0x03) { case 0x00: { /* save[32] */ DIP("save %u, r%u-r%u", u, (rt & 0x1fu) | (rt & 0x10u), ((rt + count - 1) & 0x1fu) | (rt & 0x10u)); IRTemp t1 = newTemp(Ity_I32); assign(t1, getIReg(29)); putIReg(29, binop(Iop_Sub32, mkexpr(t1), mkU32(u))); while (counter != count) { Bool use_gp = (cins & 0x04) && (counter + 1 == count); UChar this_rt = use_gp ? 28 : (UChar)((rt + counter) & 0x1f) | (rt & 0x10); Int offset = -((counter + 1) << 2); store(binop(Iop_Add32, mkexpr(t1), mkU32(offset)), getIReg(this_rt)); counter++; } break; } case 0x02: /* restore[32] */ jr = False; //falls through common restore(.jrc) implementation case 0x03: { /* restore.jrc[32] */ DIP("restore%s %u, r%u-r%u", jr ? ".jrc" : "", u, ((rt + count - 1) & 0x1fu) | (rt & 0x10u), (rt & 0x1fu) | (rt & 0x10u)); while (counter != count) { Bool use_gp = (cins & 0x04) && (counter + 1 == count); UChar this_rt = use_gp ? 28 : ((rt + counter) & 0x1F) | (rt & 0x10); Int offset = u - ((counter + 1) << 2); putIReg(this_rt,load(Ity_I32, binop(Iop_Add32, getIReg(29), mkU32(offset)))); // if this_rt == 29: raise UNPREDICTABLE() counter++; } putIReg(29, binop(Iop_Add32, getIReg(29), mkU32(u))); if (jr) { putPC(getIReg(31)); dres->whatNext = Dis_StopHere; dres->jk_StopHere = Ijk_Ret; } break; } default: vassert(0); } } static void nano_psrf(UInt cins) { switch (cins & 0x03) { case 0x00: { /* savef */ vex_printf("Instruction savef is missing documentation.\n"); vassert(0); break; } case 0x02: { /* restoref */ vex_printf("Instruction restoref is missing documentation.\n"); vassert(0); break; } default: vassert(0); } } static void nano_psr(DisResult *dres, UInt cins) { switch ((cins >> 20) & 0x1) { case 0x00: /* pp.sr */ nano_ppsr(dres, cins); break; case 0x01: /* p.sr.f */ nano_psrf(cins); break; default: vassert(0); break; } } static void nano_pri(DisResult *dres, UInt cins) { switch ((cins >> 19) & 3) { case PRI_SIGRIE: ILLEGAL_INSTRUCTON break; case PRI_PSYSCALL: if (cins & 0x40000) { /* HYPCALL */ vex_printf("Instruction HYPCALL is missing documentation.\n"); vassert(0); } else { /* SYSCALL[32] */ DIP("syscall %u", cins & 0x3FFFF); dres->jk_StopHere = Ijk_Sys_syscall; dres->whatNext = Dis_StopHere; } break; case PRI_BREAK: /* BREAK[32] */ DIP("break %u", cins & 0x7FFFF); dres->jk_StopHere = Ijk_SigTRAP; dres->whatNext = Dis_StopHere; break; case PRI_SDBBP: vex_printf("Instruction SDBBP is not supported.\n"); vassert(0); break; default: vassert(0); } } static void nano_psll(UInt cins) { UChar rt = (cins >> 21) & 0x1F; UChar rs = (cins >> 16) & 0x1F; UChar shift = cins & 0x1F; if (rt == 0 && shift == 0) { /* nop[32] */ DIP("nop[32]"); return; } if (rt == 0 && shift == 3) { /* ehb */ DIP("ehb"); vassert(0); return; } if (rt == 0 && shift == 5) { /* pause */ DIP("pause"); vassert(0); // pause_until_llbit_clears(); return; } if (rt == 0 && shift == 6) { /* sync */ DIP("sync 0x%x", rs); /* Just ignore it. */ return; } DIP("sll r%u, r%u, %u", rt, rs, shift); putIReg(rt, binop(Iop_Shl32, getIReg(rs), mkU8(shift))); return; } static void nano_pshift(UInt cins) { UChar rt = (cins >> 21) & 0x1F; UChar rs = (cins >> 16) & 0x1F; UChar shift = cins & 0x1F; switch ((cins >> 5) & 0xF) { case PSLL: { /* p.sll */ nano_psll(cins); break; } case SRL32: /* srl[32] */ DIP("srl[32] r%u, r%u, %u", rt, rs, shift); putIReg(rt, binop(Iop_Shr32, getIReg(rs), mkU8(shift))); break; case SRA: /* sra */ DIP("sra r%u, r%u, %u", rt, rs, shift); putIReg(rt, binop(Iop_Sar32, getIReg(rs), mkU8(shift))); break; case ROTR: /* rotr */ DIP("rotr r%u, r%u, %u", rt, rs, shift); IRTemp t1 = newTemp(Ity_I64); assign(t1, binop(Iop_32HLto64, getIReg(rs), getIReg(rs))); putIReg(rt, unop(Iop_64to32, binop(Iop_Shr64, mkexpr(t1), mkU8(shift)))); break; case DSLL: { /* dsll */ DIP("dsll r%u, r%u, %u", rt, rs, shift); vassert(0); break; } case DSLL32: { /* dsll32 */ DIP("dsll32 r%u, r%u, %u", rt, rs, shift); vassert(0); break; } case DSRL: { /* dsrl */ DIP("dsrl r%u, r%u, %u", rt, rs, shift); vassert(0); break; } case DSRL32: { /* dsrl32 */ DIP("dsrl32 r%u, r%u, %u", rt, rs, shift); vassert(0); break; } case DSRA: { /* dsra */ DIP("dsra r%u, r%u, %u", rt, rs, shift); vassert(0); break; } case DSRA32: { /* dsra32 */ DIP("dsra32 r%u, r%u, %u", rt, rs, shift); vassert(0); break; } case DROTR: { /* drotr */ DIP("drotr r%u, r%u, %u", rt, rs, shift); vassert(0); break; } case DROTR32: { /* drotr32 */ DIP("drotr32 r%u, r%u, %u", rt, rs, shift); vassert(0); break; } default: vassert(0); } } static void nano_protx(UInt cins) { UChar rt = (cins >> 21) & 0x1F; UChar rs = (cins >> 16) & 0x1F; UChar shift = cins & 0x1F; UChar shiftx = ((cins >> 7) & 0xF) << 1; UChar stripe = (cins & 0x40) ? 1 : 0; switch ((cins >> 5) & 0x41) { case 0x00: { /* rotx */ DIP("rotx r%u, r%u, %u, %u, %u", rt, rs, shift, shiftx, stripe); putIReg(rt, qop(Iop_Rotx32, getIReg(rs), mkU8(shift), mkU8(shiftx), mkU8(stripe))); break; } default: vassert(0); break; } } static void nano_pins(UInt cins) { UChar rt = (cins >> 21) & 0x1F; UChar rs = (cins >> 16) & 0x1F; UChar lsb = cins & 0x1F; UChar msbd = (cins >> 6) & 0x1F; switch ((cins >> 5) & 0x41) { case 0x00: { /* ins */ UChar size = 1 + msbd - lsb; DIP("ins r%u, r%u, %u, %u", rt, rs, lsb, size); UInt mask = ((1 << size) - 1) << lsb; putIReg(rt, binop(Iop_Or32, binop(Iop_And32, getIReg(rt), mkU32(~mask)), binop(Iop_And32, binop(Iop_Shl32, getIReg(rs), mkU8(lsb)), mkU32(mask)))); break; } case 0x01: { /* dins */ vassert(0); break; } case 0x40: { /* dinsm */ vassert(0); break; } case 0x41: { /* dins */ vassert(0); break; } default: vassert(0); } } static void nano_pext(UInt cins) { UChar rt = (cins >> 21) & 0x1F; UChar rs = (cins >> 16) & 0x1F; UChar lsb = cins & 0x1F; UChar msbd = (cins >> 6) & 0x1F; switch ((cins >> 5) & 0x41) { case 0x00: { /* ext */ DIP("ext r%u, r%u, %u, %u", rt, rs, lsb, msbd + 1u); if (msbd + 1 + lsb > 32) vassert(0); putIReg(rt, binop(Iop_And32, binop(Iop_Shr32, getIReg(rs), mkU8(lsb)), mkU32((1 << (msbd + 1)) - 1))); break; } case 0x01: { /* dextu */ vassert(0); break; } case 0x40: { /* dextm */ vassert(0); break; } case 0x41: { /* dext */ vassert(0); break; } default: vassert(0); } } static void nano_pool16c00(UInt cins) { UChar rs = GPR3_list[(cins >> 4) & 0x07]; UChar rt = GPR3_list[(cins >> 7) & 0x07]; switch (cins & 0x0C) { case POOL16C00_NOT: { /* not[16] */ DIP("not[16] r%u, r%u", rt, rs); putIReg(rt, unop(Iop_Not32, getIReg(rs))); break; } case POOL16C00_XOR: { /* xor[16] */ DIP("xor[16] r%u, r%u", rt, rs); putIReg(rt, binop(Iop_Xor32, getIReg(rs), getIReg(rt))); break; } case POOL16C00_AND: { /* and[16] */ DIP("and[16] r%u, r%u", rt, rs); putIReg(rt, binop(Iop_And32, getIReg(rs), getIReg(rt))); break; } case POOL16C00_OR: { /* or[16] */ DIP("or[16] r%u, r%u", rt, rs); putIReg(rt, binop(Iop_Or32, getIReg(rs), getIReg(rt))); break; } } } static void nano_pu12(DisResult *dres, UInt cins) { UChar rs = (cins >> 16) & 0x1F; UChar rt = (cins >> 21) & 0x1F; UShort u = cins & 0x0FFF; switch ((cins >> 12) & 0x0F) { case PU12_ORI: { /* ori */ DIP("ori r%u, r%u, %u", rt, rs, u); putIReg(rt, binop(Iop_Or32, getIReg(rs), mkU32(u))); break; } case PU12_XORI: { /* xori */ DIP("xori r%u, r%u, %u", rt, rs, u); putIReg(rt, binop(Iop_Xor32, getIReg(rs), mkU32(u))); break; } case PU12_ANDI: { /* andi */ DIP("andi r%u, r%u, %u", rt, rs, u); putIReg(rt, binop(Iop_And32, getIReg(rs), mkU32(u))); break; } case PU12_PSR: /* p.sr */ nano_psr(dres, cins); break; case PU12_SLTI: { /* slti */ DIP("slti r%u, r%u, %u", rt, rs, u); putIReg(rt, unop(Iop_1Uto32, binop(Iop_CmpLT32S, getIReg(rs), mkU32(u)))); break; } case PU12_SLTIU: { /* sltiu */ DIP("sltiu r%u, r%u, %u", rt, rs, u); putIReg(rt, unop(Iop_1Uto32, binop(Iop_CmpLT32U, getIReg(rs), mkU32(u)))); break; } case PU12_SEQI: { /* seqi */ DIP("seqi r%u, r%u, %u", rt, rs, u); putIReg(rt, unop(Iop_1Uto32, binop(Iop_CmpEQ32, getIReg(rs), mkU32(u)))); break; } case PU12_ADDIU_NEG: { /* addiu[neg] */ DIP("addiu[neg] r%u, r%u, %u", rt, rs, u); putIReg(rt, binop(Iop_Sub32, getIReg(rs), mkU32(u))); break; } case PU12_PSHIFT: /* p.shift */ nano_pshift(cins); break; case PU12_PROTX: /* p.rotx */ nano_protx(cins); break; case PU12_PINS: /* p.ins */ nano_pins(cins); break; case PU12_PEXT: /* p.ext */ nano_pext(cins); break; default: vassert(0); } } static void nano_pbr1(DisResult *dres, UInt cins) { UChar rs = (cins >> 16) & 0x1F; UChar rt = (cins >> 21) & 0x1F; Short s = (Short)((cins & 0x3FFE) | ((cins & 1) << 14) | ((cins & 1) << 15)); switch ((cins >> 14) & 0x3) { case PBR1_BEQC32: { /* BEQC[32] */ DIP("beqc[32] r%u, r%u, %X", rt, rs, guest_PC_curr_instr + 4 + (Int)s); ir_for_branch(dres, binop(Iop_CmpEQ32, getIReg(rt), getIReg(rs)), 4, (Int)s); break; } case PBR1_PBR3A: { /* P.BR3A */ vassert(0); break; } case PBR1_BGEC: { /* BGEC */ DIP("bgec r%u, r%u, %X", rs, rt, guest_PC_curr_instr + 4 + (Int)s); ir_for_branch(dres, binop(Iop_CmpLE32S, getIReg(rt), getIReg(rs)), 4, (Int)s); break; } case PBR1_BGEUC: { /* bgeuc */ DIP("bgeuc r%u, r%u, %X", rs, rt, guest_PC_curr_instr + 4 + (Int)s); ir_for_branch(dres, binop(Iop_CmpLE32U, getIReg(rt), getIReg(rs)), 4, (Int)s); break; } default: vex_printf("Unsupported p.br1 instruction %08X", cins); vassert(0); } } static void nano_pbr2(DisResult *dres, UInt cins) { UChar rs = (cins >> 16) & 0x1F; UChar rt = (cins >> 21) & 0x1F; Short s = (Short)((cins & 0x3FFE) | ((cins & 1) << 14) | ((cins & 1) << 15)); switch ((cins >> 14) & 0x3) { case PBR2_BNEC32: { /* BNEC[32] */ DIP("bnec r%u, r%u, %X", rt, rs, guest_PC_curr_instr + 4 + (Int)s); ir_for_branch(dres, binop(Iop_CmpNE32, getIReg(rt), getIReg(rs)), 4, (Int)s); break; } case PBR2_BLTC: { /* BLTC */ DIP("bltc r%u, r%u, %X", rt, rs, guest_PC_curr_instr + 4 + (Int)s); ir_for_branch(dres, binop(Iop_CmpLT32S, getIReg(rs), getIReg(rt)), 4, (Int)s); break; } case PBR2_BLTUC: { /* BLTUC */ DIP("bltuc r%u, r%u, %X", rt, rs, guest_PC_curr_instr + 4 + (Int)s); ir_for_branch(dres, binop(Iop_CmpLT32U, getIReg(rs), getIReg(rt)), 4, (Int)s); break; } default: vex_printf("Unsupported p.br2 instruction %08X", cins); vassert(0); } } static void nano_pbri(DisResult *dres, UInt cins) { UChar rt = (cins >> 21) & 0x1F; Int s = extend_sign((cins & 0x7FE) | ((cins & 0x01) << 11), 12); UChar bit = (cins >> 11) & 0x3F; UInt u = (cins >> 11) & 0x7F; switch ((cins >> 18) & 0x07) { case PBRI_BEQIC: { DIP("beqic r%u, %u, %0X", rt, u, guest_PC_curr_instr + 4 + s); ir_for_branch(dres, binop(Iop_CmpEQ32, getIReg(rt), mkU32(u)), 4, (Int)s); break; } case PBRI_BBEQZC: { DIP("bbeqzc r%u, %u, %0X", rt, bit, guest_PC_curr_instr + 4 + s); if (bit >= 32) { ILLEGAL_INSTRUCTON return; } ir_for_branch(dres, binop(Iop_CmpEQ32, binop(Iop_And32, binop(Iop_Shr32, getIReg(rt), mkU8(bit)), mkU32(1)), mkU32(0)), 4, s); break; } case PBRI_BGEIC: { /* bgeic */ DIP("bgeic r%u, %u, %0X", rt, u, guest_PC_curr_instr + 4 + s); ir_for_branch(dres, binop(Iop_CmpLE32S, mkU32(u), getIReg(rt)), 4, (Int)s); break; } case PBRI_BGEIUC: { /* bgeiuc */ DIP("bgeiuc r%u, %u, %0X", rt, u, guest_PC_curr_instr + 4 + s); ir_for_branch(dres, binop(Iop_CmpLE32U, mkU32(u), getIReg(rt)), 4, (Int)s); break; } case PBRI_BNEIC: { DIP("bneic r%u, %u, %0X", rt, u, guest_PC_curr_instr + 4 + s); ir_for_branch(dres, binop(Iop_CmpNE32, getIReg(rt), mkU32(u)), 4, s); break; } case PBRI_BBNEZC: { DIP("bbnezc r%u, %u, %0X", rt, bit, guest_PC_curr_instr + 4 + s); if (bit >= 32) { ILLEGAL_INSTRUCTON return; } ir_for_branch(dres, binop(Iop_CmpNE32, binop(Iop_And32, binop(Iop_Shr32, getIReg(rt), mkU8(bit)), mkU32(1)), mkU32(0)), 4, s); break; } case PBRI_BLTIC: { DIP("bltic r%u, %u, %0X", rt, u, guest_PC_curr_instr + 4 + s); ir_for_branch(dres, binop(Iop_CmpLT32S, getIReg(rt), mkU32(u)), 4, (Int)s); break; } case PBRI_BLTIUC: { DIP("bltiuc r%u, %u, %0X", rt, u, guest_PC_curr_instr + 4 + s); ir_for_branch(dres, binop(Iop_CmpLT32U, getIReg(rt), mkU32(u)), 4, (Int)s); break; } default: vex_printf("Unsupported p.bri instruction %08X", cins); vassert(0); } } static void nano_pprefs9(DisResult *dres, UInt cins) { UChar hint = (cins >> 21) & 0x1F; UChar rs = (cins >> 16) & 0x1F; UChar s = extend_sign((cins & 0xFF) | ((cins >> 7) & 0x100), 9); if (hint == 31) { /* synci */ DIP("synci %u(r%u)", s, rs); vassert(0); } else { /* pref[s9] */ DIP("pref[s9] %u, %u(r%u)", hint, s, rs); vassert(0); } } static void nano_plss0(DisResult *dres, UInt cins) { UChar rs = (cins >> 16) & 0x1F; UChar rt = (cins >> 21) & 0x1F; Int s = extend_sign(((cins >> 7) & 0x100) | (cins & 0xFF), 9); switch ((cins >> 11) & 0xf) { case LBS9: { /* lb[s9] */ DIP("lb[s9] r%u %d(r%u)", rt, s, rs); putIReg(rt, unop(Iop_8Sto32, load(Ity_I8, binop(Iop_Add32, getIReg(rs), mkU32(s))))); break; } case LHS9: { /* lh[s9] */ DIP("lh[s9] r%u %d(r%u)", rt, s, rs); putIReg(rt, unop(Iop_16Sto32, load(Ity_I16, binop(Iop_Add32, getIReg(rs), mkU32(s))))); break; } case LWS9: { /* lw[s9] */ DIP("lw[s9] r%u %d(r%u)", rt, s, rs); putIReg(rt, load(Ity_I32, binop(Iop_Add32, getIReg(rs), mkU32(s)))); break; } case LDS9: { /* ld[s9] */ DIP("ld[s9] r%u %d(r%u)", rt, s, rs); vassert(0); break; } case SBS9: { /* sb[s9] */ DIP("sb[s9] r%u %d(r%u)", rt, s, rs); store(binop(Iop_Add32, getIReg(rs), mkU32(s)), unop(Iop_32to8, getIReg(rt))); break; } case SHS9: { /* sh[s9] */ DIP("sh[s9] r%u %d(r%u)", rt, s, rs); store(binop(Iop_Add32, getIReg(rs), mkU32(s)), unop(Iop_32to16, getIReg(rt))); break; } case SWS9: { /* sw[s9] */ DIP("sw[s9] r%u %d(r%u)", rt, s, rs); store(binop(Iop_Add32, getIReg(rs), mkU32(s)), getIReg(rt)); break; } case SDS9: { /* sd[s9] */ DIP("sd[s9] r%u %d(r%u)", rt, s, rs); vassert(0); break; } case LBUS9: { /* lbu[s9] */ DIP("lbu[s9] r%u %d(r%u)", rt, s, rs); putIReg(rt, unop(Iop_8Uto32, load(Ity_I8, binop(Iop_Add32, getIReg(rs), mkU32(s))))); break; } case LHUS9: { /* lhu[s9] */ DIP("lhu[s9] r%u %d(r%u)", rt, s, rs); putIReg(rt, unop(Iop_16Uto32, load(Ity_I16, binop(Iop_Add32, getIReg(rs), mkU32(s))))); break; } case LWC1S9: { /* lwc1[s9] */ DIP("lwc1[s9] r%u %d(r%u)", rt, s, rs); vassert(0); break; } case LDC1S9: { /* ldc1[s9] */ DIP("ldc1[s9] r%u %d(r%u)", rt, s, rs); vassert(0); break; } case PPREFS9: { /* p.pref[s9] pool */ nano_pprefs9(dres, cins); break; } case LWUS9: { /* lwu[s9] */ DIP("lwu[s9] r%u %d(r%u)", rt, s, rs); vassert(0); break; } case SWC1S9: { /* swc1[s9] */ DIP("swc1[s9] r%u %d(r%u)", rt, s, rs); vassert(0); break; } case SDC1S9: { /* sdc1[s9] */ DIP("sdc1[s9] r%u %d(r%u)", rt, s, rs); vassert(0); break; } } } static void nano_pll(DisResult *dres, UInt cins) { IRTemp t1, t2; UChar rs = (cins >> 16) & 0x1F; UChar rt = (cins >> 21) & 0x1F; UInt s = extend_sign(((cins >> 7) & 0x100) | (cins & 0xFC), 9); switch (cins & 0x03) { case LL: { DIP("ll r%u %u(r%u)", rt, s, rs); t1 = newTemp(Ity_I32); t2 = newTemp(Ity_I32); assign(t1, binop(Iop_Add32, getIReg(rs), mkU32(s))); assign(t2, load(Ity_I32, mkexpr(t1))); putLLaddr(mkexpr(t1)); putLLdata(mkexpr(t2)); putIReg(rt, mkexpr(t2)); break; } case LLWP: { UChar ru = (cins >> 3) & 0x1F; DIP("llwp r%u %u(r%u)", rt, s, rs); if (rt == ru) vassert(0); t1 = newTemp(Ity_I32); t2 = newTemp(Ity_I64); assign(t1, getIReg(rs)); assign(t2, load(Ity_I64, mkexpr(t1))); putLLaddr(mkexpr(t1)); putLLdata64(mkexpr(t2)); putIReg(rt, unop(Iop_64to32, mkexpr(t2))); putIReg(ru, unop(Iop_64HIto32, mkexpr(t2))); break; } default: vassert(0); } } static void nano_psc(DisResult *dres, UInt cins) { IRTemp t1, t2, t3, t4, t5; UChar rs = (cins >> 16) & 0x1F; UChar rt = (cins >> 21) & 0x1F; UInt s = extend_sign(((cins >> 7) & 0x100) | (cins & 0xFC), 9); switch (cins & 0x03) { case SC: { DIP("sc r%u %u(r%u)", rt, s, rs); t1 = newTemp(Ity_I32); t2 = newTemp(Ity_I1); t3 = newTemp(Ity_I32); assign(t1, binop(Iop_Add32, getIReg(rs), mkU32(s))); assign(t2, binop(Iop_CmpNE32, mkexpr(t1), getLLaddr())); assign(t3, getIReg(rt)); putLLaddr(LLADDR_INVALID); putIReg(rt, getIReg(0)); stmt(IRStmt_Exit(mkexpr(t2), Ijk_Boring, IRConst_U32(guest_PC_curr_instr + 4), OFFB_PC)); t4 = newTemp(Ity_I32); t5 = newTemp(Ity_I32); assign(t5, getLLdata()); stmt(IRStmt_CAS(mkIRCAS(IRTemp_INVALID, t4, /* old_mem */ MIPS_IEND, mkexpr(t1), /* addr */ NULL, mkexpr(t5), /* expected value */ NULL, mkexpr(t3) /* new value */))); putIReg(rt, unop(Iop_1Uto32, binop(Iop_CmpEQ32, mkexpr(t4), mkexpr(t5)))); break; } case SCWP: { UChar ru = (cins >> 3) & 0x1F; DIP("scwp r%u %u(r%u)", rt, s, rs); t1 = newTemp(Ity_I32); t2 = newTemp(Ity_I1); IRTemp oldHi, oldLo, expHi, expLo; oldHi = newTemp(Ity_I32); oldLo = newTemp(Ity_I32); expHi = newTemp(Ity_I32); expLo = newTemp(Ity_I32); assign(t2, binop(Iop_CmpNE32, getIReg(rs), getLLaddr())); putLLaddr(LLADDR_INVALID); putIReg(rt, getIReg(0)); stmt(IRStmt_Exit(mkexpr(t2), Ijk_Boring, IRConst_U32(guest_PC_curr_instr + 4), OFFB_PC)); assign(expHi, unop(Iop_64HIto32, getLLdata64())); assign(expLo, unop(Iop_64to32, getLLdata64())); stmt(IRStmt_CAS(mkIRCAS(oldHi, oldLo, /* old_mem */ MIPS_IEND, getIReg(rs), /* addr */ mkexpr(expHi), mkexpr(expLo), /* expected value */ getIReg(ru), getIReg(rt) /* new value */))); putIReg(rt, binop(Iop_And32, unop(Iop_1Uto32, binop(Iop_CmpEQ32, mkexpr(oldHi), mkexpr(expHi))), unop(Iop_1Uto32, binop(Iop_CmpEQ32, mkexpr(oldLo), mkexpr(expLo))))); break; } default: vassert(0); } } static void nano_plss1(DisResult *dres, UInt cins) { UChar rs = (cins >> 16) & 0x1F; UChar rt = (cins >> 21) & 0x1F; UInt s = extend_sign(((cins >> 7) & 0x100) | (cins & 0xFF), 9); switch ((cins >> 11) & 0x0F) { case ASET_ACLER: { vassert(0); break; } case UALH: { /* ualh */ DIP("ualh r%u %u(r%u)", rt, s, rs); putIReg(rt, unop(Iop_16Sto32, load(Ity_I16, binop(Iop_Add32, getIReg(rs), mkU32(s))))); break; } case UASH: { /* uash */ DIP("uash r%u %u(r%u)", rt, s, rs); store(binop(Iop_Add32, getIReg(rs), mkU32(s)), unop(Iop_32to16, getIReg(rt))); break; } case CACHE: { /* cache */ vassert(0); break; } case LWC2: { vassert(0); break; } case SWC2: { vassert(0); break; } case LDC2: { vassert(0); break; } case SDC2: { vassert(0); break; } case PLL: { nano_pll(dres, cins); break; } case PSC: { nano_psc(dres, cins); break; } case PLLD: { vassert(0); break; } case PSCD: { vassert(0); break; } default: vex_printf("Unrecognized P.LS.S1 instruction %08X", cins); vassert(0); } } static void nano_plswm(DisResult *dres, UInt cins) { UChar rs = (cins >> 16) & 0x1F; UChar rt = (cins >> 21) & 0x1F; Int offset = extend_sign(((cins >> 7) & 0x100) | (cins & 0xFF), 9); UChar count3 = (cins >> 12) & 0x07; UChar count = count3 ? count3 : 8; UChar counter = 0; UChar rt_tmp; Int offset_tmp; if ((cins >> 11) & 0x01) { /* swm */ DIP("swm r%u, %d(r%u), %u", rt, offset, rs, count); while (counter != count) { rt_tmp = rt ? (rt & 0x10) | ((rt + counter) & 0x1F) : 0; offset_tmp = offset + (counter << 2); store(binop(Iop_Add32, getIReg(rs), mkU32(offset_tmp)), getIReg(rt_tmp)); counter++; } } else { /* lwm */ DIP("lwm r%u, %d(r%u), %u", rt, offset, rs, count); while (counter != count) { rt_tmp = (rt & 0x10) | ((rt + counter) & 0x1F); offset_tmp = offset + (counter << 2); putIReg(rt_tmp, load(Ity_I32, binop(Iop_Add32, getIReg(rs), mkU32(offset_tmp)))); if ((rt_tmp == rs) && (counter != count - 1)) { // raise UNPREDICTABLE() } counter++; } } } static void nano_plsuawm(DisResult *dres, UInt cins) { UChar rs = (cins >> 16) & 0x1F; UChar rt = (cins >> 21) & 0x1F; UInt s = extend_sign(((cins >> 7) & 0x100) | (cins & 0xFF), 9); UChar count3 = (cins >> 12) & 0x07; UChar count = count3 ? count3 : 8; UChar counter = 0; UInt offset = s; UChar rt_tmp, offset_tmp; if ((cins >> 11) & 0x01) { /* uaswm */ DIP("uaswm r%u, %d(r%u), %u", rt, (int)offset, rs, count); while (counter != count) { rt_tmp = rt ? (rt & 0x10) | ((rt + counter) & 0x1F) : 0; offset_tmp = offset + (counter << 2); store(binop(Iop_Add32, getIReg(rs), mkU32(offset_tmp)), getIReg(rt_tmp)); counter+=1; } } else { /* ualwm */ DIP("ualwm r%u, %d(r%u), %u", rt, (int)offset, rs, count); while (counter != count) { rt_tmp = (rt & 0x10) | ((rt + counter) & 0x1F); offset_tmp = offset + (counter << 2); putIReg(rt_tmp, load(Ity_I32, binop(Iop_Add32, getIReg(rs), mkU32(offset_tmp)))); if ((rt_tmp == rs) && (counter != count - 1)) { vassert(0); // raise UNPREDICTABLE() } counter+=1; } } } static void nano_plss9(DisResult *dres, UInt cins) { switch ((cins >> 8) & 0x7) { case PLSS0: { /* p.ls.s0 */ nano_plss0(dres, cins); break; } case PLSS1: { nano_plss1(dres, cins); break; } case PLSE0: { vassert(0); break; } case PLSWM: { nano_plswm(dres, cins); break; } case PLSUAWM: { nano_plsuawm(dres, cins); break; } case PLSDM: { vassert(0); break; } case PLSUADM: { vassert(0); break; } } } static void nano_p16a1(DisResult *dres, UShort cins) { if (cins & 0x40) { /* ADDIU[R1.SP] */ UChar rs = 29; UChar rt = GPR3_list[(cins >> 7) & 0x07]; UChar u = (cins & 0x3F) << 2; DIP("ADDIU[R1.SP] r%u, %u", rt, u); putIReg(rt, binop(Iop_Add32, getIReg(rs), mkU32(u))); } else { vassert(0); } } static void nano_p16a2(DisResult *dres, UShort cins) { if (cins & 0x08) { /* P.ADDIU[RS5] */ UChar rt = (cins >> 5) & 0x1F; if (rt != 0) { /* ADDIU[RS5] */ Int s = extend_sign((cins & 0x07) | ((cins >> 1) & 0x08), 4); DIP("addiu r%u, r%u, %d", rt, rt, s); putIReg(rt, binop(Iop_Add32, getIReg(rt), mkU32(s))); } else { DIP("nop"); } } else { /* ADDIU[R2] */ UChar rs = GPR3_list[(cins >> 4) & 0x07]; UChar rt = GPR3_list[(cins >> 7) & 0x07]; UChar u = (cins & 0x07) << 2; DIP("addiu r%u, r%u, 0x%X", rt, rs, u); putIReg(rt, binop(Iop_Add32, getIReg(rs), mkU32(u))); } } static void nano_p16ri(DisResult *dres, UShort cins) { switch ((cins >> 3) & 0x03) { case RI_PSYSCALL: { if (cins & 0x4) { /* HYPCALL[16] */ vex_printf("Instruction HYPCALL is missing documentation.\n"); vassert(0); } else { /* SYSCALL[16] */ DIP("syscall %u", cins & 0x3u); dres->jk_StopHere = Ijk_Sys_syscall; dres->whatNext = Dis_StopHere; } break; } case RI_BREAK: { /* BREAK[16] */ DIP("break %u", cins & 0x7u); dres->jk_StopHere = Ijk_SigTRAP; dres->whatNext = Dis_StopHere; break; } case RI_SDBBP: { vex_printf("Instruction SDBBP is not supported.\n"); vassert(0); } } } static void nano_p16mv(DisResult *dres, UShort cins) { UChar rs = cins & 0x1F; UChar rt = (cins >> 5) & 0x1f; if (rt != 0) { DIP("move r%u, r%u", rt, rs); putIReg(rt, getIReg(rs)); } else { nano_p16ri(dres, cins); } } static void nano_p16shift(DisResult *dres, UShort cins) { UChar rs = GPR3_list[(cins >> 4) & 0x07]; UChar rt = GPR3_list[(cins >> 7) & 0x07]; UChar shift = cins & 0x07; if (cins & 0x08) { /* slr[16] */ DIP("slr r%u, r%u, %u ", rt, rs, shift); putIReg(rt, binop(Iop_Shr32, getIReg(rs), mkU8(shift != 0 ? shift : 8))); } else { /* sll[16] */ DIP("sll r%u, r%u, %u ", rt, rs, shift); putIReg(rt, binop(Iop_Shl32, getIReg(rs), mkU8(shift != 0 ? shift : 8))); } } static void nano_p16c(DisResult *dres, UShort cins) { switch (cins & 0x03) { case 0x00: { /* POOL16C_0 */ nano_pool16c00(cins); break; } case 0x01: case 0x03: { /* LWXS[16] */ UChar rt = GPR3_list[(cins >> 7) & 0x07]; UChar rs = GPR3_list[(cins >> 4) & 0x07]; UChar rd = GPR3_list[(cins >> 1) & 0x07]; DIP("lwxs[32] r%u, %u(r%u)", rd, rs, rt); putIReg(rd, load(Ity_I32, binop(Iop_Add32, binop(Iop_Shl32, getIReg(rs), mkU8(0x02)), getIReg(rt)))); break; } default: vassert(0); } } static void nano_p16br(DisResult *dres, UShort cins) { UChar u = (cins & 0x0f) << 1; if (0 == u) { UChar rt = (cins >> 5) & 0x1F; if (cins & 0x10) { /* JALRC[16] */ DIP("jalrc r%u", rt); putIReg(31, mkU32(guest_PC_curr_instr + 2)); dres->jk_StopHere = Ijk_Call; } else { /* JRC */ DIP("jrc r%u", rt); dres->jk_StopHere = rt != 31 ? Ijk_Boring : Ijk_Ret; } putPC(getIReg(rt)); dres->whatNext = Dis_StopHere; } else { UChar rt3 = (cins >> 7) & 0x07; UChar rs3 = (cins >> 4) & 0x07; UChar rt = GPR3_list[rt3]; UChar rs = GPR3_list[rs3]; if (rs3 < rt3) { /* beqc[16] */ DIP("beqc r%u, r%u, %X", rt, rs, guest_PC_curr_instr + 2 + u); ir_for_branch(dres, binop(Iop_CmpEQ32, getIReg(rt), getIReg(rs)), 2, (Int)u); } else { /* bnec[16] */ DIP("bnec r%u, r%u, %X", rt, rs, guest_PC_curr_instr + 2 + u); ir_for_branch(dres, binop(Iop_CmpNE32, getIReg(rt), getIReg(rs)), 2, (Int)u); } } } static void nano_p16sr(DisResult *dres, UShort cins) { UChar u = cins & 0xF0; UChar count = cins & 0x0F; UChar counter = 0; UChar rt = cins & 0x200 ? 31 : 30; if (cins & 0x100) { /* RESTORE.JRC[16] */ DIP("restore.jrc %u, r%u-r%u", u, (rt & 0x1fu) | (rt & 0x10u), ((rt + count - 1) & 0x1fu) | (rt & 0x10u)); while (counter != count) { UChar this_rt = ((rt + counter) & 0x1F) | (rt & 0x10); Int offset = u - ((counter + 1) << 2); putIReg(this_rt, load(Ity_I32, binop(Iop_Add32, getIReg(29), mkU32(offset)))); // if this_rt == 29: raise UNPREDICTABLE() counter++; } putIReg(29, binop(Iop_Add32, getIReg(29), mkU32(u))); putPC(getIReg(31)); dres->whatNext = Dis_StopHere; dres->jk_StopHere = Ijk_Ret; } else { /* SAVE[16] */ DIP("save %u, r%u-r%u", u, (rt & 0x1fu) | (rt & 0x10u), ((rt + count - 1) & 0x1fu) | (rt & 0x10u)); IRTemp t1 = newTemp(Ity_I32); assign(t1, getIReg(29)); putIReg(29, binop(Iop_Sub32, mkexpr(t1), mkU32(u))); while (counter != count) { UChar this_rt = ((rt + counter) & 0x1f) | (rt & 0x10); Int offset = -((counter + 1) << 2); store(binop(Iop_Add32, mkexpr(t1), mkU32(offset)), getIReg(this_rt)); counter++; } } } static void nano_p16lb(DisResult *dres, UShort cins) { UChar rt = GPR3_list[(cins >> 7) & 0x7]; UChar rs = GPR3_list[(cins >> 4) & 0x7]; UChar u = cins & 0x3; switch ((cins >> 2) & 0x3) { case 0x0: /* LB[16] */ DIP("lb[16] r%u %u(r%u)", rt, u, rs); putIReg(rt, unop(Iop_8Sto32, load(Ity_I8, binop(Iop_Add32, getIReg(rs), mkU32(u))))); break; case 0x1: /* SB[16] */ rt = GPR3_src_store_list[(cins >> 7) & 0x7]; DIP("sb[16] r%u %u(r%u)", rt, u, rs); store(binop(Iop_Add32, getIReg(rs), mkU32(u)), unop(Iop_32to8, getIReg(rt))); break; case 0x2: /* LBU[16] */ DIP("lbu[16] r%u %u(r%u)", rt, u, rs); putIReg(rt, unop(Iop_8Uto32, load(Ity_I8, binop(Iop_Add32, getIReg(rs), mkU32(u))))); break; default: vex_printf("Unrecognized bytes %04x\n", cins); vassert(0); } } static void nano_p16lh(DisResult *dres, UShort cins) { UChar rt = GPR3_list[(cins >> 7) & 0x7]; UChar rs = GPR3_list[(cins >> 4) & 0x7]; UChar u = cins & 0x06; switch (cins & 0x09) { case 0x0: /* LH[16] */ DIP("lh[16] r%u %u(r%u)", rt, u, rs); putIReg(rt, unop(Iop_16Sto32, load(Ity_I16, binop(Iop_Add32, getIReg(rs), mkU32(u))))); break; case 0x1: /* SH[16] */ rt = GPR3_src_store_list[(cins >> 7) & 0x7]; DIP("sh[16] r%u %u(r%u)", rt, u, rs); store(binop(Iop_Add32, getIReg(rs), mkU32(u)), unop(Iop_32to16, getIReg(rt))); break; case 0x8: /* LHU[16] */ DIP("lhu[16] r%u %u(r%u)", rt, u, rs); putIReg(rt, unop(Iop_16Uto32, load(Ity_I16, binop(Iop_Add32, getIReg(rs), mkU32(u))))); break; default: vex_printf("Unrecognized bytes %04x\n", cins); vassert(0); } } static void nano_p164x4(DisResult *dres, UShort cins) { UChar rt = GPR4_list[((cins >> 6) & 0x08) | ((cins >> 5) & 0x07)]; UChar rs = GPR4_list[((cins >> 1) & 0x08) | (cins & 0x07)]; UChar rd = rt; switch (cins & 0x108) { case 0x00: { /* ADDU[4x4] */ DIP("addu[4x4] r%u, r%u, r%u", rd, rs, rt); putIReg(rd, binop(Iop_Add32, getIReg(rs), getIReg(rt))); break; } case 0x08: { /* MUL[4x4] */ DIP("mul[4x4] r%u, r%u, r%u", rd, rs, rt); putIReg(rd, binop(Iop_Mul32, getIReg(rs), getIReg(rt))); break; } default: vassert(0); } } static void nano_pgpbh(DisResult *dres, UInt cins) { UChar rt = (cins >> 21) & 0x1F; UChar rs = 28; UInt u = cins & 0x3FFFF; switch ((cins >> 18) & 7) { case LBGP: { /* lb[gp] */ DIP("lb[gp] r%u %u(r%u)", rt, u, rs); putIReg(rt, unop(Iop_8Sto32, load(Ity_I8, binop(Iop_Add32, getIReg(rs), mkU32(u))))); break; } case SBGP: { /* sb[gp] */ DIP("sb[gp] r%u %u(r%u)", rt, u, rs); store(binop(Iop_Add32, getIReg(rs), mkU32(u)), unop(Iop_32to8, getIReg(rt))); break; } case LBUGP: { /* lbu[gp] */ DIP("lbu[gp] r%u %u(r%u)", rt, u, rs); putIReg(rt, unop(Iop_8Uto32, load(Ity_I8, binop(Iop_Add32, getIReg(rs), mkU32(u))))); break; } case ADDIUGPB: { /* addiu[gp.b] */ DIP("addiu r%u, r%u, 0x%X", rt, rs, u); putIReg(rt, binop(Iop_Add32, getIReg(rs), mkU32(u))); break; } case PGPLH: { if (cins & 0x01) { /* lhu[gp] */ DIP("lhu[gp] r%u %u(r%u)", rt, u, rs); putIReg(rt, unop(Iop_16Uto32, load(Ity_I16, binop(Iop_Add32, getIReg(rs), mkU32(u & 0x3FFFE))))); } else { /* lh[gp] */ DIP("lh[gp] r%u %u(r%u)", rt, u, rs); putIReg(rt, unop(Iop_16Sto32, load(Ity_I16, binop(Iop_Add32, getIReg(rs), mkU32(u & 0x3FFFE))))); } break; } case PGPSH: { if (cins & 0x01) { vassert(0); } else { /* sh[gp] */ DIP("sh[gp] r%u %u(r%u)", rt, u, rs); store(binop(Iop_Add32, getIReg(rs), mkU32(u & 0x3FFFE)), unop(Iop_32to16, getIReg(rt))); } break; } case PGPCP1: { vassert(0); break; } case PGPM64: { vassert(0); break; } } } static void nano_pj(DisResult *dres, UInt cins) { UChar rt = (cins >> 21) & 0x1F; UChar rs = (cins >> 16) & 0x1F; switch ((cins >> 12) & 0x0F) { case JALRC32: { /* JARLC[32] */ DIP("jalrc[32] r%u, r%u", rt, rs); putIReg(rt, mkU32(guest_PC_curr_instr + 4)); putPC(getIReg(rs)); dres->jk_StopHere = Ijk_Call; dres->whatNext = Dis_StopHere; break; } case JALRCHB: { DIP("jalrc.hb r%u r%u", rt, rs); putIReg(rt, mkU32(guest_PC_curr_instr + 4)); putPC(getIReg(rs)); dres->jk_StopHere = Ijk_Call; dres->whatNext = Dis_StopHere; // clear_hazards() break; } case PBALRSC: { if (rt == 0) { /* brsc */ DIP("brsc r%u", rs); IRTemp t1 = newTemp(Ity_I32); assign(t1, binop(Iop_Add32, mkU32(guest_PC_curr_instr + 4), binop(Iop_Shl32, getIReg(rs), mkU8(0x01)))); putPC(mkexpr(t1)); } else { /* balrsc */ DIP("balrsc r%u, r%u", rs, rt); IRTemp t1 = newTemp(Ity_I32); assign(t1, binop(Iop_Add32, mkU32(guest_PC_curr_instr + 4), binop(Iop_Shl32, getIReg(rs), mkU8(0x01)))); putIReg(rt, mkU32(guest_PC_curr_instr + 4)); putPC(mkexpr(t1)); } dres->jk_StopHere = Ijk_Boring; dres->whatNext = Dis_StopHere; break; } } } static void nano_pgpw(DisResult *dres, UInt cins) { UChar rt = (cins >> 21) & 0x1F; UChar rs = 28; UInt u = cins & 0x1FFFFC; switch (cins & 0x03) { case PGPW_ADDIU: { /* addiu[gp.w] */ DIP("addiu[gp.w] r%u, r%u, %u", rt, rs, u); putIReg(rt, binop(Iop_Add32, getIReg(rs), mkU32(u))); break; } case PGPW_LW: { /* lw[gp] */ DIP("lw[gp] r%u, %u(r%u)", rt, u, rs); putIReg(rt, load(Ity_I32, binop(Iop_Add32, getIReg(rs), mkU32(u)))); break; } case PGPW_SW: { /* sw[gp] */ DIP("sw[gp] r%u, %u(r%u)", rt, u, rs); store(binop(Iop_Add32, getIReg(rs), mkU32(u)), getIReg(rt)); break; } } } static void dis_nanoMIPS16(DisResult *dres, UShort cins) { switch (cins >> 10) { case P16A2: nano_p16a2(dres, cins); break; case BC16: { Int s = extend_sign((cins & 0x3FE) | ((cins & 1) << 10), 11); DIP("bc %0X", guest_PC_curr_instr + 2 + s); putPC(mkU32(guest_PC_curr_instr + 2 + s)); dres->jk_StopHere = Ijk_Boring; dres->whatNext = Dis_StopHere; break; } case P16MV: nano_p16mv(dres, cins); break; case P16SHIFT: nano_p16shift(dres, cins); break; case P16C: nano_p16c(dres, cins); break; case P16BR: nano_p16br(dres, cins); break; break; case P16ADDU: { UChar rt = GPR3_list[(cins >> 7) & 0x7]; UChar rs = GPR3_list[(cins >> 4) & 0x7]; UChar rd = GPR3_list[(cins >> 1) & 0x7]; if (cins & 1) { /* SUBU[16] */ DIP("subu r%u, r%u, r%u", rd, rs, rt); putIReg(rd, binop(Iop_Sub32, getIReg(rs), getIReg(rt))); } else { /* ADDU[16] */ DIP("addu r%u, r%u, r%u", rd, rs, rt); putIReg(rd, binop(Iop_Add32, getIReg(rs), getIReg(rt))); } break; } case LI16: { UChar rt = GPR3_list[(cins >> 7) & 0x07]; UChar eu = cins & 0x7F; Int s; if (eu == 127) s = -1; else s = eu; DIP("li r%u, %d", rt, s); putIReg(rt, mkU32(s)); break; } case BNEZC16: { UChar rt = GPR3_list[(cins >> 7) & 0x7]; Int s = (Char)((cins & 0x7E) | (cins << 7)); DIP("bnezc r%u, %X", rt, guest_PC_curr_instr + 2 + s); ir_for_branch(dres, binop(Iop_CmpNE32, getIReg(rt), mkU32(0)), 2, s); break; } case BEQZC16: { UChar rt = GPR3_list[(cins >> 7) & 0x7]; Int s = (Char)((cins & 0x7E) | (cins << 7)); DIP("beqzc r%u, %X", rt, guest_PC_curr_instr + 2 + s); ir_for_branch(dres, binop(Iop_CmpEQ32, getIReg(rt), mkU32(0)), 2, s); break; } case P16LB: nano_p16lb(dres, cins); break; case P16LH: nano_p16lh(dres, cins); break; case P16SR: nano_p16sr(dres, cins); break; case P16A1: nano_p16a1(dres, cins); break; case ANDI16: { /* ANDI[16] */ UChar rt = GPR3_list[(cins >> 7) & 0x7]; UChar rs = GPR3_list[(cins >> 4) & 0x7]; UChar eu = cins & 0xF; UInt u; if (eu == 12) u = 0x00FF; else if (eu == 13) u = 0xFFFF; else u = (UInt)eu; DIP("andi[16] r%u, r%u, %u", rt, rs, u); putIReg(rt, binop(Iop_And32, getIReg(rs), mkU32(u))); break; } case LW16: { /* LW[16] */ UChar rt = GPR3_list[(cins >> 7) & 0x7]; UChar rs = GPR3_list[(cins >> 4) & 0x7]; UChar u = (cins & 0x0F) << 2; DIP("lw[16] r%u, %u(r%u)", rt, u, rs); putIReg(rt, load(Ity_I32, binop(Iop_Add32, getIReg(rs), mkU32(u)))); break; } case LWSP: { /* LW[SP] */ UChar rt = (cins >> 5) & 0x1F; UChar rs = 29; UChar u = (cins & 0x1F) << 2; DIP("lw[SP] r%u, %u(r%u)", rt, u, rs); putIReg(rt, load(Ity_I32, binop(Iop_Add32, getIReg(rs), mkU32(u)))); break; } case LWGP16: { /* LW[GP16] */ UChar rt = GPR3_list[(cins >> 7) & 0x07]; UChar rs = 28; UInt u = (cins & 0x7F) << 2; DIP("lw[GP16] r%u, %u(r%u)", rt, u, rs); putIReg(rt, load(Ity_I32, binop(Iop_Add32, getIReg(rs), mkU32(u)))); break; } case LW4X4: { /* LW[4x4] */ UChar rt = GPR4_list[((cins >> 6) & 0x08) | ((cins >> 5) & 0x07)]; UChar rs = GPR4_list[((cins >> 1) & 0x08) | (cins & 0x07)]; UChar u = (cins & 0x08) | ((cins >> 6) & 0x04); DIP("lw[4x4] r%u, %u(r%u)", rt, u, rs); putIReg(rt, load(Ity_I32, binop(Iop_Add32, getIReg(rs), mkU32(u)))); break; } case SW16: { /* SW[16] */ UChar rt = GPR3_src_store_list[(cins >> 7) & 0x7]; UChar rs = GPR3_list[(cins >> 4) & 0x7]; UChar u = (cins & 0x0F) << 2; DIP("sw[16] r%u, %u(r%u)", rt, u, rs); store(binop(Iop_Add32, getIReg(rs), mkU32(u)), getIReg(rt)); break; } case SWSP: { /* SW[SP] */ UChar rt = (cins >> 5) & 0x1F; UChar rs = 29; UChar u = (cins & 0x1F) << 2; DIP("sw[SP] r%u, %u(r%u)", rt, u, rs); store(binop(Iop_Add32, getIReg(rs), mkU32(u)), getIReg(rt)); break; } case SWGP16: { /* SW[GP16] */ UChar rt = GPR3_src_store_list[(cins >> 7) & 0x07]; UChar rs = 28; UInt u = (cins & 0x7F) << 2; DIP("sw[GP16] r%u, %u(r%u)", rt, u, rs); store(binop(Iop_Add32, getIReg(rs), mkU32(u)), getIReg(rt)); break; } case SW4X4: { /* SW[4x4] */ UChar rt = GPR4_zero_list[((cins >> 6) & 0x08) | ((cins >> 5) & 0x07)]; UChar rs = GPR4_list[((cins >> 1) & 0x08) | (cins & 0x07)]; UChar u = (cins & 0x08) | ((cins >> 6) & 0x04); DIP("sw[4x4] r%u, %u(r%u)", rt, u, rs); store(binop(Iop_Add32, getIReg(rs), mkU32(u)), getIReg(rt)); break; } case P164X4: { /* P16.4X4 pool */ nano_p164x4(dres, cins); break; } case MOVEP: { /* MOVEP */ UChar rd1 = GPR2_reg1_list[((cins >> 2) & 0x02) | ((cins >> 8) & 0x01)]; UChar rd2 = GPR2_reg2_list[((cins >> 2) & 0x02) | ((cins >> 8) & 0x01)]; UChar rs1 = GPR4_zero_list[((cins >> 1) & 0x08) | (cins & 0x07)]; UChar rs2 = GPR4_zero_list[((cins >> 6) & 0x08) | ((cins >> 5) & 0x07)]; DIP("MOVEP r%u, r%u, r%u, r%u", rd1, rd2, rs1, rs2); putIReg(rd1, getIReg(rs1)); putIReg(rd2, getIReg(rs2)); break; } case MOVEPREV: { /* MOVEP[REV] */ UChar rd1 = GPR4_list[((cins >> 1) & 0x08) | (cins & 0x07)]; UChar rd2 = GPR4_list[((cins >> 6) & 0x08) | ((cins >> 5) & 0x07)]; UChar rs1 = GPR2_reg1_list[((cins >> 2) & 0x02) | ((cins >> 8) & 0x01)]; UChar rs2 = GPR2_reg2_list[((cins >> 2) & 0x02) | ((cins >> 8) & 0x01)]; DIP("MOVEP r%u, r%u, r%u, r%u", rd1, rd2, rs1, rs2); putIReg(rd1, getIReg(rs1)); putIReg(rd2, getIReg(rs2)); break; } case BALC16: { Int s = extend_sign((cins & 0x3FE) | ((cins & 0x1) << 10), 11); DIP("balc %0X", guest_PC_curr_instr + 2 + s); putIReg(31, mkU32(guest_PC_curr_instr + 2)); putPC(mkU32(guest_PC_curr_instr + 2 + s)); dres->whatNext = Dis_StopHere; dres->jk_StopHere = Ijk_Call; break; } default: vex_printf("Unsupported 16bit: %04X\n", cins); vassert(0); break; } dres->len = 2; } static void dis_nanoMIPS32(DisResult *dres, UInt cins) { switch (cins >> 26) { case P_ADDIURI: { UChar rt = (cins >> 21) & 0x1F; if (rt != 0) { UChar rs = (cins >> 16) & 0x1F; UInt u = cins & 0xFFFF; DIP("addiu r%u, r%u, %u", rt, rs, u); putIReg(rt, binop(Iop_Add32, getIReg(rs), mkU32(u))); } else { nano_pri(dres, cins); } break; } case ADDIUPC32: { UChar rt = (cins >> 21) & 0x1F; Int s = extend_sign((cins & 0x1FFFFE) | ((cins & 1) << 21), 22); DIP("addiupc r%u, 0x%X", rt, guest_PC_curr_instr + 4 + s); putIReg(rt, mkU32(guest_PC_curr_instr + 4 + s)); break; } case MOVE_BALC: { Int s = extend_sign((cins & 0x1FFFFE) | ((cins & 1) << 21), 22); UChar rt = GPR4_zero_list[((cins >> 21) & 0x07) | ((cins >> 22) & 0x08)]; UChar rd = (cins & 0x1000000) ? 5 : 4; /* GPR1_list */ DIP("move.balc r%u, r%u, %0X", rd, rt, guest_PC_curr_instr + 4 + s); putIReg(rd, getIReg(rt)); putIReg(31, mkU32(guest_PC_curr_instr + 4)); putPC(mkU32(guest_PC_curr_instr + 4 + s)); dres->jk_StopHere = Ijk_Call; dres->whatNext = Dis_StopHere; break; } case PLSU12: nano_plsu12(dres, cins); break; case PGPW: nano_pgpw(dres, cins); break; case PU12: nano_pu12(dres, cins); break; case P32A: nano_p32a(dres, cins); break; case PGPBH: nano_pgpbh(dres, cins); break; case PBAL: nano_pbal(dres, cins); break; case PBR1: nano_pbr1(dres, cins); break; case PBR2: nano_pbr2(dres, cins); break; case PBRI: nano_pbri(dres, cins); break; case PLSS9: nano_plss9(dres, cins); break; case P_LUI: if (cins & 0x02) { /* ALUIPC */ UChar rt = (cins >> 21) & 0x1F; UInt s = (cins & 0x1FF000) | ((cins & 0xFFC) << 19) | (cins << 31); DIP("aluipc r%u %08X", rt, s); putIReg(rt, binop(Iop_And32, mkU32(guest_PC_curr_instr + s + 4), unop(Iop_Not32, mkU32(0xFFF)))); } else { /* LUI */ UChar rt = (cins >> 21) & 0x1F; UInt s = (cins & 0x1FF000) | ((cins & 0xFFC) << 19) | (cins << 31); DIP("lui r%u %08X", rt, s); putIReg(rt, mkU32(s)); } break; case PJ: { nano_pj(dres, cins); break; } default: vex_printf("Unsupported 32bit: %08X\n", cins); vassert(0); break; } dres->len = 4; } static void dis_nanoMIPS48(DisResult *dres, ULong cins) { UChar rt = (cins >> 37) & 0x1F; UInt x = (UInt)cins; switch ((cins >> 32) & 0x1F) { case P48I_LI: DIP("li r%u, 0x%X", rt, x); putIReg(rt, mkU32(x)); break; case P48I_ADDIU: DIP("addiu r%u, r%u, 0x%X", rt, rt, x); putIReg(rt, binop(Iop_Add32, getIReg(rt), mkU32(x))); break; case P48I_ADDIU_GP: DIP("addiu r%u, r28, 0x%X", rt, x); putIReg(rt, binop(Iop_Add32, getIReg(28), mkU32(x))); break; case P48I_ADDIUPC: DIP("addiupc r%u, 0x%X", rt, x); putIReg(rt, mkU32(guest_PC_curr_instr + x + 6)); break; case P48I_LWPC: DIP("lwpc r%u, 0x%X", rt, x); putIReg(rt, load(Ity_I32, mkU32(guest_PC_curr_instr + 6 + x))); break; case P48I_SWPC: DIP("swpc r%u, 0x%X", rt, x); store(mkU32(guest_PC_curr_instr + 6 + x), getIReg(rt)); break; default: vex_printf("Unsupported 48bit: %012llX\n", cins); vassert(0); break; } dres->len = 6; } static Bool check_for_special_requests_nanoMIPS(DisResult *dres, const UChar *code) { /* 8000 c04d srl zero, zero, 13 8000 c05d srl zero, zero, 29 8000 c043 srl zero, zero, 3 8000 c053 srl zero, zero, 19 */ const UInt word1 = 0x8000C04D; const UInt word2 = 0x8000C05D; const UInt word3 = 0x8000C043; const UInt word4 = 0x8000C053; if (getUInt(code + 0) == word1 && getUInt(code + 4) == word2 && getUInt(code + 8) == word3 && getUInt(code + 12) == word4) { /* Got a "Special" instruction preamble. Which one is it? */ if (getUInt(code + 16) == 0x218C6290 /* or t0, t0, t0 */ ) { /* $a7 = client_request ( $t0 ) */ DIP("a7 = client_request(t0)"); dres->jk_StopHere = Ijk_ClientReq; dres->whatNext = Dis_StopHere; dres->len = 20; return True; } else if (getUInt(code + 16) == 0x21AD6A90 /* or t1, t1, t1 */ ) { /* $a7 = guest_NRADDR */ DIP("a7 = guest_NRADDR"); putIReg(11, IRExpr_Get(offsetof(VexGuestMIPS32State, guest_NRADDR), Ity_I32)); dres->len = 20; return True; } else if (getUInt(code + 16) == 0x21CE7290 /* or t2, t2, t2 */ ) { /* branch-and-link-to-noredir $25 */ DIP("branch-and-link-to-noredir t9"); putIReg(31, mkU32(guest_PC_curr_instr + 20)); putPC(getIReg(25)); dres->jk_StopHere = Ijk_NoRedir; dres->whatNext = Dis_StopHere; dres->len = 20; return True; } else if (getUInt(code + 16) == 0x21EF7A90 /* or t3, t3, t3 */ ) { /* IR injection */ DIP("IR injection"); #if defined (_MIPSEL) vex_inject_ir(irsb, Iend_LE); #elif defined (_MIPSEB) vex_inject_ir(irsb, Iend_BE); #endif stmt(IRStmt_Put(offsetof(VexGuestMIPS32State, guest_CMSTART), mkU32(guest_PC_curr_instr))); stmt(IRStmt_Put(offsetof(VexGuestMIPS32State, guest_CMLEN), mkU32(20))); dres->whatNext = Dis_StopHere; dres->jk_StopHere = Ijk_InvalICache; dres->len = 20; return True; } } return False; } /*------------------------------------------------------------*/ /*--- Disassemble a single instruction ---*/ /*------------------------------------------------------------*/ /* Disassemble a single instruction into IR. The instruction is located in host memory at guest_instr, and has guest IP of guest_PC_curr_instr, which will have been set before the call here. */ /* Disassemble a single instruction into IR. The instruction is located in host memory at &guest_code[delta]. */ DisResult disInstr_nanoMIPS( IRSB* irsb_IN, const UChar* guest_code_IN, Long delta, Addr guest_IP, VexArch guest_arch, const VexArchInfo* archinfo, const VexAbiInfo* abiinfo, VexEndness host_endness_IN, Bool sigill_diag_IN ) { DisResult dres; const UChar *code; vassert(guest_arch == VexArchNANOMIPS); /* Set result defaults. */ dres.whatNext = Dis_Continue; dres.len = 0; dres.jk_StopHere = Ijk_INVALID; dres.hint = Dis_HintNone; irsb = irsb_IN; guest_PC_curr_instr = (Addr32)guest_IP; code = guest_code_IN + delta; if (!check_for_special_requests_nanoMIPS(&dres, code)) { UShort cins = getUShort(code); nanoMIPSopcodes opcode = cins >> 10; if (opcode & P16) dis_nanoMIPS16(&dres, cins); else if (opcode == P48I) { ULong cinsl = (((ULong) cins ) << 32) | (((ULong) getUShort(code + 4)) << 16) | getUShort(code + 2); dis_nanoMIPS48(&dres, cinsl); } else { UInt cinsi = (((UInt) cins ) << 16) | getUShort(code + 2); dis_nanoMIPS32(&dres, cinsi); } } if ((dres.whatNext == Dis_Continue) || (dres.jk_StopHere == Ijk_Sys_syscall) || (dres.jk_StopHere == Ijk_SigTRAP) || (dres.jk_StopHere == Ijk_SigILL) || (dres.jk_StopHere == Ijk_ClientReq) || (dres.jk_StopHere == Ijk_NoRedir) || (dres.jk_StopHere == Ijk_InvalICache)) { putPC(mkU32(guest_PC_curr_instr + dres.len)); } return dres; } /*--------------------------------------------------------------------*/ /*--- end guest_nanomips_toIR.c ---*/ /*--------------------------------------------------------------------*/