/* * \file trc_idec_arminst.cpp * \brief OpenCSD : * * \copyright Copyright (c) 2015, ARM Limited. All Rights Reserved. */ /* * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its contributors * may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Basic ARM/Thumb/A64 instruction decode, suitable for e.g. basic block identification and trace decode. */ #include "i_dec/trc_idec_arminst.h" #include /* for NULL */ #include static ocsd_instr_subtype instr_sub_type = OCSD_S_INSTR_NONE; /* need to spot the architecture version for certain instructions */ static uint16_t arch_version = 0x70; ocsd_instr_subtype get_instr_subtype() { return instr_sub_type; } void clear_instr_subtype() { instr_sub_type = OCSD_S_INSTR_NONE; } void set_arch_version(uint16_t version) { arch_version = version; } int inst_ARM_is_direct_branch(uint32_t inst) { int is_direct_branch = 1; if ((inst & 0xf0000000) == 0xf0000000) { /* NV space */ if ((inst & 0xfe000000) == 0xfa000000){ /* BLX (imm) */ } else { is_direct_branch = 0; } } else if ((inst & 0x0e000000) == 0x0a000000) { /* B, BL */ } else { is_direct_branch = 0; } return is_direct_branch; } int inst_ARM_wfiwfe(uint32_t inst) { if ( ((inst & 0xf0000000) != 0xf0000000) && ((inst & 0x0ffffffe) == 0x0320f002) ) /* WFI & WFE may be traced as branches in etm4.3 ++ */ return 1; return 0; } int inst_ARM_is_indirect_branch(uint32_t inst) { int is_indirect_branch = 1; if ((inst & 0xf0000000) == 0xf0000000) { /* NV space */ if ((inst & 0xfe500000) == 0xf8100000) { /* RFE */ } else { is_indirect_branch = 0; } } else if ((inst & 0x0ff000d0) == 0x01200010) { /* BLX (register), BX */ if ((inst & 0xFF) == 0x1E) instr_sub_type = OCSD_S_INSTR_V7_IMPLIED_RET; /* BX LR */ } else if ((inst & 0x0ff000f0) == 0x01200020) { /* BXJ: in v8 this behaves like BX */ } else if ((inst & 0x0e108000) == 0x08108000) { /* POP {...,pc} or LDMxx {...,pc} */ if ((inst & 0x0FFFA000) == 0x08BD8000) /* LDMIA SP!,{...,pc} */ instr_sub_type = OCSD_S_INSTR_V7_IMPLIED_RET; } else if ((inst & 0x0e50f000) == 0x0410f000) { /* LDR PC,imm... inc. POP {PC} */ if ( (inst & 0x01ff0000) == 0x009D0000) instr_sub_type = OCSD_S_INSTR_V7_IMPLIED_RET; /* LDR PC, [SP], #imm */ } else if ((inst & 0x0e50f010) == 0x0610f000) { /* LDR PC,reg */ } else if ((inst & 0x0fe0f000) == 0x01a0f000) { /* MOV PC,rx */ if ((inst & 0x00100FFF) == 0x00E) /* ensure the S=0, LSL #0 variant - i.e plain MOV */ instr_sub_type = OCSD_S_INSTR_V7_IMPLIED_RET; /* MOV PC, R14 */ } else if ((inst & 0x0f900080) == 0x01000000) { /* "Miscellaneous instructions" - in DP space */ is_indirect_branch = 0; } else if ((inst & 0x0f9000f0) == 0x01800090) { /* Some extended loads and stores */ is_indirect_branch = 0; } else if ((inst & 0x0fb0f000) == 0x0320f000) { /* MSR #imm */ is_indirect_branch = 0; } else if ((inst & 0x0e00f000) == 0x0200f000) { /* DP PC,imm shift */ if ((inst & 0x0f90f000) == 0x0310f000) { /* TST/CMP */ is_indirect_branch = 0; } } else if ((inst & 0x0e00f000) == 0x0000f000) { /* DP PC,reg */ } else { is_indirect_branch = 0; } return is_indirect_branch; } int inst_Thumb_is_direct_branch(uint32_t inst) { uint8_t link, cond; return inst_Thumb_is_direct_branch_link(inst, &link, &cond); } int inst_Thumb_is_direct_branch_link(uint32_t inst, uint8_t *is_link, uint8_t *is_cond) { int is_direct_branch = 1; if ((inst & 0xf0000000) == 0xd0000000 && (inst & 0x0e000000) != 0x0e000000) { /* B (encoding T1) */ *is_cond = 1; } else if ((inst & 0xf8000000) == 0xe0000000) { /* B (encoding T2) */ } else if ((inst & 0xf800d000) == 0xf0008000 && (inst & 0x03800000) != 0x03800000) { /* B (encoding T3) */ *is_cond = 1; } else if ((inst & 0xf8009000) == 0xf0009000) { /* B (encoding T4); BL (encoding T1) */ if (inst & 0x00004000) { *is_link = 1; instr_sub_type = OCSD_S_INSTR_BR_LINK; } } else if ((inst & 0xf800d001) == 0xf000c000) { /* BLX (imm) (encoding T2) */ *is_link = 1; instr_sub_type = OCSD_S_INSTR_BR_LINK; } else if ((inst & 0xf5000000) == 0xb1000000) { /* CB(NZ) */ *is_cond = 1; } else { is_direct_branch = 0; } return is_direct_branch; } int inst_Thumb_wfiwfe(uint32_t inst) { int is_wfiwfe = 1; /* WFI, WFE may be branches in etm4.3++ */ if ((inst & 0xfffffffe) == 0xf3af8002) { /* WFI & WFE (encoding T2) */ } else if ((inst & 0xffef0000) == 0xbf200000) { /* WFI & WFE (encoding T1) */ } else { is_wfiwfe = 0; } return is_wfiwfe; } int inst_Thumb_is_indirect_branch(uint32_t inst) { uint8_t link; return inst_Thumb_is_indirect_branch_link(inst, &link); } int inst_Thumb_is_indirect_branch_link(uint32_t inst, uint8_t *is_link) { /* See e.g. PFT Table 2-3 and Table 2-5 */ int is_branch = 1; if ((inst & 0xff000000) == 0x47000000) { /* BX, BLX (reg) [v8M includes BXNS, BLXNS] */ if (inst & 0x00800000) { *is_link = 1; instr_sub_type = OCSD_S_INSTR_BR_LINK; } else if ((inst & 0x00780000) == 0x00700000) { instr_sub_type = OCSD_S_INSTR_V7_IMPLIED_RET; /* BX LR */ } } else if ((inst & 0xfff0d000) == 0xf3c08000) { /* BXJ: in v8 this behaves like BX */ } else if ((inst & 0xff000000) == 0xbd000000) { /* POP {pc} */ instr_sub_type = OCSD_S_INSTR_V7_IMPLIED_RET; } else if ((inst & 0xfd870000) == 0x44870000) { /* MOV PC,reg or ADD PC,reg */ if ((inst & 0xffff0000) == 0x46f700000) instr_sub_type = OCSD_S_INSTR_V7_IMPLIED_RET; /* MOV PC,LR */ } else if ((inst & 0xfff0ffe0) == 0xe8d0f000) { /* TBB/TBH */ } else if ((inst & 0xffd00000) == 0xe8100000) { /* RFE (T1) */ } else if ((inst & 0xffd00000) == 0xe9900000) { /* RFE (T2) */ } else if ((inst & 0xfff0d000) == 0xf3d08000) { /* SUBS PC,LR,#imm inc.ERET */ } else if ((inst & 0xfff0f000) == 0xf8d0f000) { /* LDR PC,imm (T3) */ } else if ((inst & 0xff7ff000) == 0xf85ff000) { /* LDR PC,literal (T2) */ } else if ((inst & 0xfff0f800) == 0xf850f800) { /* LDR PC,imm (T4) */ if((inst & 0x000f0f00) == 0x000d0b00) instr_sub_type = OCSD_S_INSTR_V7_IMPLIED_RET; /* LDR PC, [SP], #imm*/ } else if ((inst & 0xfff0ffc0) == 0xf850f000) { /* LDR PC,reg (T2) */ } else if ((inst & 0xfe508000) == 0xe8108000) { /* LDM PC */ if ((inst & 0x0FFF0000) == 0x08BD0000) /* LDMIA [SP]!, */ instr_sub_type = OCSD_S_INSTR_V7_IMPLIED_RET; /* POP {...,pc} */ } else { is_branch = 0; } return is_branch; } int inst_A64_is_direct_branch(uint32_t inst) { uint8_t link = 0; return inst_A64_is_direct_branch_link(inst, &link); } int inst_A64_is_direct_branch_link(uint32_t inst, uint8_t *is_link) { int is_direct_branch = 1; if ((inst & 0x7c000000) == 0x34000000) { /* CB, TB */ } else if ((inst & 0xff000010) == 0x54000000) { /* B */ } else if ((inst & 0x7c000000) == 0x14000000) { /* B, BL imm */ if (inst & 0x80000000) { *is_link = 1; instr_sub_type = OCSD_S_INSTR_BR_LINK; } } else { is_direct_branch = 0; } return is_direct_branch; } int inst_A64_wfiwfe(uint32_t inst) { /* WFI, WFE may be traced as branches in etm 4.3++ */ if ((inst & 0xffffffdf) == 0xd503205f) return 1; return 0; } int inst_A64_is_indirect_branch(uint32_t inst) { uint8_t link = 0; return inst_A64_is_indirect_branch_link(inst, &link); } int inst_A64_is_indirect_branch_link(uint32_t inst, uint8_t *is_link) { int is_indirect_branch = 1; if ((inst & 0xffdffc1f) == 0xd61f0000) { /* BR, BLR */ if (inst & 0x00200000) { *is_link = 1; instr_sub_type = OCSD_S_INSTR_BR_LINK; } } else if ((inst & 0xfffffc1f) == 0xd65f0000) { instr_sub_type = OCSD_S_INSTR_V8_RET; /* RET */ } else if ((inst & 0xffffffff) == 0xd69f03e0) { /* ERET */ instr_sub_type = OCSD_S_INSTR_V8_ERET; } else if (arch_version >= 0x0803) { /* new pointer auth instr for v8.3 arch */ if ((inst & 0xffdff800) == 0xd61f0800) { /* BRAA, BRAB, BLRAA, BLRBB */ if (inst & 0x00200000) { *is_link = 1; instr_sub_type = OCSD_S_INSTR_BR_LINK; } } else if ((inst & 0xffdff81F) == 0xd71f081F) { /* BRAAZ, BRABZ, BLRAAZ, BLRBBZ */ if (inst & 0x00200000) { *is_link = 1; instr_sub_type = OCSD_S_INSTR_BR_LINK; } } else if ((inst & 0xfffffbff) == 0xd69f0bff) { /* ERETAA, ERETAB */ instr_sub_type = OCSD_S_INSTR_V8_ERET; } else if ((inst & 0xfffffbff) == 0xd65f0bff) { /* RETAA, RETAB */ instr_sub_type = OCSD_S_INSTR_V8_RET; } else { is_indirect_branch = 0; } } else { is_indirect_branch = 0; } return is_indirect_branch; } int inst_ARM_branch_destination(uint32_t addr, uint32_t inst, uint32_t *pnpc) { uint32_t npc; int is_direct_branch = 1; if ((inst & 0x0e000000) == 0x0a000000) { /* B: cccc:1010:imm24 BL: cccc:1011:imm24 BLX: 1111:101H:imm24 */ npc = addr + 8 + ((int32_t)((inst & 0xffffff) << 8) >> 6); if ((inst & 0xf0000000) == 0xf0000000) { npc |= 1; /* indicate ISA is now Thumb */ npc |= ((inst >> 23) & 2); /* apply the H bit */ } } else { is_direct_branch = 0; } if (is_direct_branch && pnpc != NULL) { *pnpc = npc; } return is_direct_branch; } int inst_Thumb_branch_destination(uint32_t addr, uint32_t inst, uint32_t *pnpc) { uint32_t npc; int is_direct_branch = 1; if ((inst & 0xf0000000) == 0xd0000000 && (inst & 0x0e000000) != 0x0e000000) { /* B (encoding T1) */ npc = addr + 4 + ((int32_t)((inst & 0x00ff0000) << 8) >> 23); npc |= 1; } else if ((inst & 0xf8000000) == 0xe0000000) { /* B (encoding T2) */ npc = addr + 4 + ((int32_t)((inst & 0x07ff0000) << 5) >> 20); npc |= 1; } else if ((inst & 0xf800d000) == 0xf0008000 && (inst & 0x03800000) != 0x03800000) { /* B (encoding T3) */ npc = addr + 4 + ((int32_t)(((inst & 0x04000000) << 5) | ((inst & 0x0800) << 19) | ((inst & 0x2000) << 16) | ((inst & 0x003f0000) << 7) | ((inst & 0x000007ff) << 12)) >> 11); npc |= 1; } else if ((inst & 0xf8009000) == 0xf0009000) { /* B (encoding T4); BL (encoding T1) */ uint32_t S = ((inst & 0x04000000) >> 26)-1; /* ffffffff or 0 according to S bit */ npc = addr + 4 + ((int32_t)(((inst & 0x04000000) << 5) | (((inst^S) & 0x2000) << 17) | (((inst^S) & 0x0800) << 18) | ((inst & 0x03ff0000) << 3) | ((inst & 0x000007ff) << 8)) >> 7); npc |= 1; } else if ((inst & 0xf800d001) == 0xf000c000) { /* BLX (encoding T2) */ uint32_t S = ((inst & 0x04000000) >> 26)-1; /* ffffffff or 0 according to S bit */ addr &= 0xfffffffc; /* Align(PC,4) */ npc = addr + 4 + ((int32_t)(((inst & 0x04000000) << 5) | (((inst^S) & 0x2000) << 17) | (((inst^S) & 0x0800) << 18) | ((inst & 0x03ff0000) << 3) | ((inst & 0x000007fe) << 8)) >> 7); /* don't set the Thumb bit, as we're transferring to ARM */ } else if ((inst & 0xf5000000) == 0xb1000000) { /* CB(NZ) */ /* Note that it's zero-extended - always a forward branch */ npc = addr + 4 + ((((inst & 0x02000000) << 6) | ((inst & 0x00f80000) << 7)) >> 25); npc |= 1; } else { is_direct_branch = 0; } if (is_direct_branch && pnpc != NULL) { *pnpc = npc; } return is_direct_branch; } int inst_A64_branch_destination(uint64_t addr, uint32_t inst, uint64_t *pnpc) { uint64_t npc; int is_direct_branch = 1; if ((inst & 0xff000010) == 0x54000000) { /* B */ npc = addr + ((int32_t)((inst & 0x00ffffe0) << 8) >> 11); } else if ((inst & 0x7c000000) == 0x14000000) { /* B, BL imm */ npc = addr + ((int32_t)((inst & 0x03ffffff) << 6) >> 4); } else if ((inst & 0x7e000000) == 0x34000000) { /* CB */ npc = addr + ((int32_t)((inst & 0x00ffffe0) << 8) >> 11); } else if ((inst & 0x7e000000) == 0x36000000) { /* TB */ npc = addr + ((int32_t)((inst & 0x0007ffe0) << 13) >> 16); } else { is_direct_branch = 0; } if (is_direct_branch && pnpc != NULL) { *pnpc = npc; } return is_direct_branch; } int inst_ARM_is_branch(uint32_t inst) { return inst_ARM_is_indirect_branch(inst) || inst_ARM_is_direct_branch(inst); } int inst_Thumb_is_branch(uint32_t inst) { return inst_Thumb_is_indirect_branch(inst) || inst_Thumb_is_direct_branch(inst); } int inst_A64_is_branch(uint32_t inst) { return inst_A64_is_indirect_branch(inst) || inst_A64_is_direct_branch(inst); } int inst_ARM_is_branch_and_link(uint32_t inst) { int is_branch = 1; if ((inst & 0xf0000000) == 0xf0000000) { if ((inst & 0xfe000000) == 0xfa000000){ instr_sub_type = OCSD_S_INSTR_BR_LINK; /* BLX (imm) */ } else { is_branch = 0; } } else if ((inst & 0x0f000000) == 0x0b000000) { instr_sub_type = OCSD_S_INSTR_BR_LINK; /* BL */ } else if ((inst & 0x0ff000f0) == 0x01200030) { instr_sub_type = OCSD_S_INSTR_BR_LINK; /* BLX (reg) */ } else { is_branch = 0; } return is_branch; } int inst_Thumb_is_branch_and_link(uint32_t inst) { int is_branch = 1; if ((inst & 0xff800000) == 0x47800000) { instr_sub_type = OCSD_S_INSTR_BR_LINK; /* BLX (reg) */ } else if ((inst & 0xf800c000) == 0xf000c000) { instr_sub_type = OCSD_S_INSTR_BR_LINK; /* BL, BLX (imm) */ } else { is_branch = 0; } return is_branch; } int inst_A64_is_branch_and_link(uint32_t inst) { int is_branch = 1; if ((inst & 0xfffffc1f) == 0xd63f0000) { /* BLR */ instr_sub_type = OCSD_S_INSTR_BR_LINK; } else if ((inst & 0xfc000000) == 0x94000000) { /* BL */ instr_sub_type = OCSD_S_INSTR_BR_LINK; } else if (arch_version >= 0x0803) { /* new pointer auth instr for v8.3 arch */ if ((inst & 0xfffff800) == 0xd73f0800) { /* BLRAA, BLRBB */ instr_sub_type = OCSD_S_INSTR_BR_LINK; } else if ((inst & 0xfffff81F) == 0xd63f081F) { /* BLRAAZ, BLRBBZ */ instr_sub_type = OCSD_S_INSTR_BR_LINK; } else { is_branch = 0; } } else { is_branch = 0; } return is_branch; } int inst_ARM_is_conditional(uint32_t inst) { return (inst & 0xe0000000) != 0xe0000000; } int inst_Thumb_is_conditional(uint32_t inst) { if ((inst & 0xf0000000) == 0xd0000000 && (inst & 0x0e000000) != 0x0e000000) { /* B (encoding T1) */ return 1; } else if ((inst & 0xf800d000) == 0xf0008000 && (inst & 0x03800000) != 0x03800000) { /* B (encoding T3) */ return 1; } else if ((inst & 0xf5000000) == 0xb1000000) { /* CB(N)Z */ return 1; } return 0; } unsigned int inst_Thumb_is_IT(uint32_t inst) { if ((inst & 0xff000000) == 0xbf000000 && (inst & 0x000f0000) != 0x00000000) { if (inst & 0x00010000) { return 4; } else if (inst & 0x00020000) { return 3; } else if (inst & 0x00040000) { return 2; } else { assert(inst & 0x00080000); return 1; } } else { return 0; } } /* Test whether an A64 instruction is conditional. Instructions like CSEL, CSINV, CCMP are not classed as conditional. They use the condition code but do one of two things with it, neither a NOP. The "intruction categories" section of ETMv4 lists no (non branch) conditional instructions for A64. */ int inst_A64_is_conditional(uint32_t inst) { if ((inst & 0x7c000000) == 0x34000000) { /* CB, TB */ return 1; } else if ((inst & 0xff000010) == 0x54000000) { /* B.cond */ return 1; } return 0; } arm_barrier_t inst_ARM_barrier(uint32_t inst) { if ((inst & 0xfff00000) == 0xf5700000) { switch (inst & 0xf0) { case 0x40: return ARM_BARRIER_DSB; case 0x50: return ARM_BARRIER_DMB; case 0x60: return ARM_BARRIER_ISB; default: return ARM_BARRIER_NONE; } } else if ((inst & 0x0fff0f00) == 0x0e070f00) { switch (inst & 0xff) { case 0x9a: return ARM_BARRIER_DSB; /* mcr p15,0,Rt,c7,c10,4 */ case 0xba: return ARM_BARRIER_DMB; /* mcr p15,0,Rt,c7,c10,5 */ case 0x95: return ARM_BARRIER_ISB; /* mcr p15,0,Rt,c7,c5,4 */ default: return ARM_BARRIER_NONE; } } else { return ARM_BARRIER_NONE; } } arm_barrier_t inst_Thumb_barrier(uint32_t inst) { if ((inst & 0xffffff00) == 0xf3bf8f00) { switch (inst & 0xf0) { case 0x40: return ARM_BARRIER_DSB; case 0x50: return ARM_BARRIER_DMB; case 0x60: return ARM_BARRIER_ISB; default: return ARM_BARRIER_NONE; } } else if ((inst & 0xffff0f00) == 0xee070f00) { /* Thumb2 CP15 barriers are unlikely... 1156T2 only? */ switch (inst & 0xff) { case 0x9a: return ARM_BARRIER_DSB; /* mcr p15,0,Rt,c7,c10,4 */ case 0xba: return ARM_BARRIER_DMB; /* mcr p15,0,Rt,c7,c10,5 */ case 0x95: return ARM_BARRIER_ISB; /* mcr p15,0,Rt,c7,c5,4 */ default: return ARM_BARRIER_NONE; } return ARM_BARRIER_NONE; } else { return ARM_BARRIER_NONE; } } arm_barrier_t inst_A64_barrier(uint32_t inst) { if ((inst & 0xfffff09f) == 0xd503309f) { switch (inst & 0x60) { case 0x0: return ARM_BARRIER_DSB; case 0x20: return ARM_BARRIER_DMB; case 0x40: return ARM_BARRIER_ISB; default: return ARM_BARRIER_NONE; } } else { return ARM_BARRIER_NONE; } } int inst_ARM_is_UDF(uint32_t inst) { return (inst & 0xfff000f0) == 0xe7f000f0; } int inst_Thumb_is_UDF(uint32_t inst) { return (inst & 0xff000000) == 0xde000000 || /* T1 */ (inst & 0xfff0f000) == 0xf7f0a000; /* T2 */ } int inst_A64_is_UDF(uint32_t inst) { /* No A64 encodings are formally allocated as permanently undefined, but it is intended not to allocate any instructions in the 21-bit regions at the bottom or top of the range. */ return (inst & 0xffe00000) == 0x00000000 || (inst & 0xffe00000) == 0xffe00000; } /* End of File trc_idec_arminst.cpp */