diff options
Diffstat (limited to 'decoder/source/i_dec/trc_idec_arminst.cpp')
| -rw-r--r-- | decoder/source/i_dec/trc_idec_arminst.cpp | 561 |
1 files changed, 561 insertions, 0 deletions
diff --git a/decoder/source/i_dec/trc_idec_arminst.cpp b/decoder/source/i_dec/trc_idec_arminst.cpp new file mode 100644 index 0000000000000..ed7eb247d3bee --- /dev/null +++ b/decoder/source/i_dec/trc_idec_arminst.cpp @@ -0,0 +1,561 @@ +/* + * \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 <stddef.h> /* for NULL */ +#include <assert.h> + + +static ocsd_instr_subtype instr_sub_type = OCSD_S_INSTR_NONE; + +ocsd_instr_subtype get_instr_subtype() +{ + return instr_sub_type; +} + +void clear_instr_subtype() +{ + instr_sub_type = OCSD_S_INSTR_NONE; +} + +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_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 */ + } else if ((inst & 0x0e108000) == 0x08108000) { + /* POP {...,pc} or LDMxx {...,pc} */ + } else if ((inst & 0x0e50f000) == 0x0410f000) { + /* LDR PC,imm... inc. POP {PC} */ + } else if ((inst & 0x0e50f010) == 0x0610f000) { + /* LDR PC,reg */ + } else if ((inst & 0x0fe0f000) == 0x01a0f000) { + /* MOV PC,rx */ + } 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) +{ + int is_direct_branch = 1; + if ((inst & 0xf0000000) == 0xd0000000 && (inst & 0x0e000000) != 0x0e000000) { + /* B<c> (encoding T1) */ + } else if ((inst & 0xf8000000) == 0xe0000000) { + /* B (encoding T2) */ + } else if ((inst & 0xf800d000) == 0xf0008000 && (inst & 0x03800000) != 0x03800000) { + /* B (encoding T3) */ + } else if ((inst & 0xf8009000) == 0xf0009000) { + /* B (encoding T4); BL (encoding T1) */ + } else if ((inst & 0xf800d001) == 0xf000c000) { + /* BLX (imm) (encoding T2) */ + } else if ((inst & 0xf5000000) == 0xb1000000) { + /* CB(NZ) */ + } else { + is_direct_branch = 0; + } + return is_direct_branch; +} + + +int inst_Thumb_is_indirect_branch(uint32_t inst) +{ + /* See e.g. PFT Table 2-3 and Table 2-5 */ + int is_branch = 1; + if ((inst & 0xff000000) == 0x47000000) { + /* BX, BLX (reg) */ + } else if ((inst & 0xff000000) == 0xbd000000) { + /* POP {pc} */ + } else if ((inst & 0xfd870000) == 0x44870000) { + /* MOV PC,reg or ADD PC,reg */ + } 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) */ + } else if ((inst & 0xfff0ffc0) == 0xf850f000) { + /* LDR PC,reg (T2) */ + } else if ((inst & 0xfe508000) == 0xe8108000) { + /* LDM PC */ + } else { + is_branch = 0; + } + return is_branch; +} + + +int inst_A64_is_direct_branch(uint32_t inst) +{ + int is_direct_branch = 1; + if ((inst & 0x7c000000) == 0x34000000) { + /* CB, TB */ + } else if ((inst & 0xff000010) == 0x54000000) { + /* B<cond> */ + } else if ((inst & 0x7c000000) == 0x14000000) { + /* B, BL imm */ + } else { + is_direct_branch = 0; + } + return is_direct_branch; +} + + +int inst_A64_is_indirect_branch(uint32_t inst) +{ + int is_indirect_branch = 1; + if ((inst & 0xffdffc1f) == 0xd61f0000) { + /* BR, BLR */ + } 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 { + 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<c> (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<cond> */ + 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 { + 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<c> (encoding T1) */ + return 1; + } else if ((inst & 0xf800d000) == 0xf0008000 && (inst & 0x03800000) != 0x03800000) { + /* B<c> (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 */ |
