X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=blobdiff_plain;f=src%2Ftarget%2Farm_disassembler.c;h=36566c9932ec725a52814100e1fbfceb5429cdec;hp=0478ee9c6797ce35a1f347f8720ce61749c574e5;hb=f6315d5e5b7b71515ef051711e5f818a42d6b3b3;hpb=49e2267f1f0a689eadbf44da7852659326324044 diff --git a/src/target/arm_disassembler.c b/src/target/arm_disassembler.c index 0478ee9c67..36566c9932 100644 --- a/src/target/arm_disassembler.c +++ b/src/target/arm_disassembler.c @@ -25,26 +25,103 @@ #include "target.h" #include "arm_disassembler.h" -#include "log.h" +#include +/* + * This disassembler supports two main functions for OpenOCD: + * + * - Various "disassemble" commands. OpenOCD can serve as a + * machine-language debugger, without help from GDB. + * + * - Single stepping. Not all ARM cores support hardware single + * stepping. To work without that support, the debugger must + * be able to decode instructions to find out where to put a + * "next instruction" breakpoint. + * + * In addition, interpretation of ETM trace data needs some of the + * decoding mechanisms. + * + * At this writing (September 2009) neither function is complete. + * + * - ARM decoding + * * Old-style syntax (not UAL) is generally used + * * VFP instructions are not understood (ARMv5 and later) + * except as coprocessor 10/11 operations + * * Most ARM instructions through ARMv6 are decoded, but some + * of the post-ARMv4 opcodes may not be handled yet + * CPS, SDIV, UDIV, LDREX*, STREX*, QASX, ... + * * NEON instructions are not understood (ARMv7-A) + * + * - Thumb/Thumb2 decoding + * * UAL syntax should be consistently used + * * Any Thumb2 instructions used in Cortex-M3 (ARMv7-M) should + * be handled properly. Accordingly, so should the subset + * used in Cortex-M0/M1; and "original" 16-bit Thumb from + * ARMv4T and ARMv5T. + * * Conditional effects of Thumb2 "IT" (if-then) instructions + * are not handled: the affected instructions are not shown + * with their now-conditional suffixes. + * * Some ARMv6 and ARMv7-M Thumb2 instructions may not be + * handled (minimally for coprocessor access). + * * SIMD instructions, and some other Thumb2 instructions + * from ARMv7-A, are not understood. + * + * - ThumbEE decoding + * * As a Thumb2 variant, the Thumb2 comments (above) apply. + * * Opcodes changed by ThumbEE mode are not handled; these + * instructions wrongly decode as LDM and STM. + * + * - Jazelle decoding ... no support whatsoever for Jazelle mode + * or decoding. ARM encourages use of the more generic ThumbEE + * mode, instead of Jazelle mode, in current chips. + * + * - Single-step/emulation ... spotty support, which is only weakly + * tested. Thumb2 is not supported. (Arguably a full simulator + * is not needed to support just single stepping. Recognizing + * branch vs non-branch instructions suffices, except when the + * instruction faults and triggers a synchronous exception which + * can be intercepted using other means.) + * + * ARM DDI 0406B "ARM Architecture Reference Manual, ARM v7-A and + * ARM v7-R edition" gives the most complete coverage of the various + * generations of ARM instructions. At this writing it is publicly + * accessible to anyone willing to create an account at the ARM + * web site; see http://www.arm.com/documentation/ for information. + * + * ARM DDI 0403C "ARMv7-M Architecture Reference Manual" provides + * more details relevant to the Thumb2-only processors (such as + * the Cortex-M implementations). + */ + /* textual represenation of the condition field */ /* ALways (default) is ommitted (empty string) */ -char *arm_condition_strings[] = +static const char *arm_condition_strings[] = { "EQ", "NE", "CS", "CC", "MI", "PL", "VS", "VC", "HI", "LS", "GE", "LT", "GT", "LE", "", "NV" }; /* make up for C's missing ROR */ -uint32_t ror(uint32_t value, int places) +static uint32_t ror(uint32_t value, int places) { return (value >> places) | (value << (32 - places)); } -int evaluate_pld(uint32_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_unknown(uint32_t opcode, + uint32_t address, struct arm_instruction *instruction) +{ + instruction->type = ARM_UNDEFINED_INSTRUCTION; + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 + "\tUNDEFINED INSTRUCTION", address, opcode); + return ERROR_OK; +} + +static int evaluate_pld(uint32_t opcode, + uint32_t address, struct arm_instruction *instruction) { /* PLD */ - if ((opcode & 0x0d70f0000) == 0x0550f000) + if ((opcode & 0x0d70f000) == 0x0550f000) { instruction->type = ARM_PLD; @@ -52,17 +129,55 @@ int evaluate_pld(uint32_t opcode, uint32_t address, arm_instruction_t *instructi return ERROR_OK; } - else - { - instruction->type = ARM_UNDEFINED_INSTRUCTION; - return ERROR_OK; + return evaluate_unknown(opcode, address, instruction); +} + +static int evaluate_srs(uint32_t opcode, + uint32_t address, struct arm_instruction *instruction) +{ + const char *wback = (opcode & (1 << 21)) ? "!" : ""; + const char *mode = ""; + + switch ((opcode >> 23) & 0x3) { + case 0: + mode = "DA"; + break; + case 1: + /* "IA" is default */ + break; + case 2: + mode = "DB"; + break; + case 3: + mode = "IB"; + break; } - LOG_ERROR("should never reach this point"); - return -1; + switch (opcode & 0x0e500000) { + case 0x08400000: + snprintf(instruction->text, 128, "0x%8.8" PRIx32 + "\t0x%8.8" PRIx32 + "\tSRS%s\tSP%s, #%d", + address, opcode, + mode, wback, + (unsigned)(opcode & 0x1f)); + break; + case 0x08100000: + snprintf(instruction->text, 128, "0x%8.8" PRIx32 + "\t0x%8.8" PRIx32 + "\tRFE%s\tr%d%s", + address, opcode, + mode, + (unsigned)((opcode >> 16) & 0xf), wback); + break; + default: + return evaluate_unknown(opcode, address, instruction); + } + return ERROR_OK; } -int evaluate_swi(uint32_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_swi(uint32_t opcode, + uint32_t address, struct arm_instruction *instruction) { instruction->type = ARM_SWI; @@ -73,7 +188,8 @@ int evaluate_swi(uint32_t opcode, uint32_t address, arm_instruction_t *instructi return ERROR_OK; } -int evaluate_blx_imm(uint32_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_blx_imm(uint32_t opcode, + uint32_t address, struct arm_instruction *instruction) { int offset; uint32_t immediate; @@ -105,7 +221,8 @@ int evaluate_blx_imm(uint32_t opcode, uint32_t address, arm_instruction_t *instr return ERROR_OK; } -int evaluate_b_bl(uint32_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_b_bl(uint32_t opcode, + uint32_t address, struct arm_instruction *instruction) { uint8_t L; uint32_t immediate; @@ -142,7 +259,8 @@ int evaluate_b_bl(uint32_t opcode, uint32_t address, arm_instruction_t *instruct /* Coprocessor load/store and double register transfers */ /* both normal and extended instruction space (condition field b1111) */ -int evaluate_ldc_stc_mcrr_mrrc(uint32_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_ldc_stc_mcrr_mrrc(uint32_t opcode, + uint32_t address, struct arm_instruction *instruction) { uint8_t cp_num = (opcode & 0xf00) >> 8; @@ -171,19 +289,24 @@ int evaluate_ldc_stc_mcrr_mrrc(uint32_t opcode, uint32_t address, arm_instructio mnemonic = "MRRC"; } - snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s p%i, %x, r%i, r%i, c%i", - address, opcode, mnemonic, COND(opcode), cp_num, cp_opcode, Rd, Rn, CRm); + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 + "\t%s%s%s p%i, %x, r%i, r%i, c%i", + address, opcode, mnemonic, + ((opcode & 0xf0000000) == 0xf0000000) + ? "2" : COND(opcode), + COND(opcode), cp_num, cp_opcode, Rd, Rn, CRm); } else /* LDC or STC */ { uint8_t CRd, Rn, offset; - uint8_t U, N; + uint8_t U; char *mnemonic; char addressing_mode[32]; CRd = (opcode & 0xf000) >> 12; Rn = (opcode & 0xf0000) >> 16; - offset = (opcode & 0xff); + offset = (opcode & 0xff) << 2; /* load/store */ if (opcode & 0x00100000) @@ -198,22 +321,29 @@ int evaluate_ldc_stc_mcrr_mrrc(uint32_t opcode, uint32_t address, arm_instructio } U = (opcode & 0x00800000) >> 23; - N = (opcode & 0x00400000) >> 22; /* addressing modes */ - if ((opcode & 0x01200000) == 0x01000000) /* immediate offset */ - snprintf(addressing_mode, 32, "[r%i, #%s0x%2.2x*4]", Rn, (U) ? "" : "-", offset); - else if ((opcode & 0x01200000) == 0x01200000) /* immediate pre-indexed */ - snprintf(addressing_mode, 32, "[r%i, #%s0x%2.2x*4]!", Rn, (U) ? "" : "-", offset); - else if ((opcode & 0x01200000) == 0x00200000) /* immediate post-indexed */ - snprintf(addressing_mode, 32, "[r%i], #%s0x%2.2x*4", Rn, (U) ? "" : "-", offset); + if ((opcode & 0x01200000) == 0x01000000) /* offset */ + snprintf(addressing_mode, 32, "[r%i, #%s%d]", + Rn, U ? "" : "-", offset); + else if ((opcode & 0x01200000) == 0x01200000) /* pre-indexed */ + snprintf(addressing_mode, 32, "[r%i, #%s%d]!", + Rn, U ? "" : "-", offset); + else if ((opcode & 0x01200000) == 0x00200000) /* post-indexed */ + snprintf(addressing_mode, 32, "[r%i], #%s%d", + Rn, U ? "" : "-", offset); else if ((opcode & 0x01200000) == 0x00000000) /* unindexed */ - snprintf(addressing_mode, 32, "[r%i], #0x%2.2x", Rn, offset); - - snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s%s p%i, c%i, %s", - address, opcode, mnemonic, ((opcode & 0xf0000000) == 0xf0000000) ? COND(opcode) : "2", - (N) ? "L" : "", - cp_num, CRd, addressing_mode); + snprintf(addressing_mode, 32, "[r%i], {%d}", + Rn, offset >> 2); + + snprintf(instruction->text, 128, "0x%8.8" PRIx32 + "\t0x%8.8" PRIx32 + "\t%s%s%s p%i, c%i, %s", + address, opcode, mnemonic, + ((opcode & 0xf0000000) == 0xf0000000) + ? "2" : COND(opcode), + (opcode & (1 << 22)) ? "L" : "", + cp_num, CRd, addressing_mode); } return ERROR_OK; @@ -222,9 +352,10 @@ int evaluate_ldc_stc_mcrr_mrrc(uint32_t opcode, uint32_t address, arm_instructio /* Coprocessor data processing instructions */ /* Coprocessor register transfer instructions */ /* both normal and extended instruction space (condition field b1111) */ -int evaluate_cdp_mcr_mrc(uint32_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_cdp_mcr_mrc(uint32_t opcode, + uint32_t address, struct arm_instruction *instruction) { - char* cond; + const char *cond; char* mnemonic; uint8_t cp_num, opcode_1, CRd_Rd, CRn, CRm, opcode_2; @@ -271,7 +402,8 @@ int evaluate_cdp_mcr_mrc(uint32_t opcode, uint32_t address, arm_instruction_t *i } /* Load/store instructions */ -int evaluate_load_store(uint32_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_load_store(uint32_t opcode, + uint32_t address, struct arm_instruction *instruction) { uint8_t I, P, U, B, W, L; uint8_t Rn, Rd; @@ -438,8 +570,326 @@ int evaluate_load_store(uint32_t opcode, uint32_t address, arm_instruction_t *in return ERROR_OK; } +static int evaluate_extend(uint32_t opcode, uint32_t address, char *cp) +{ + unsigned rm = (opcode >> 0) & 0xf; + unsigned rd = (opcode >> 12) & 0xf; + unsigned rn = (opcode >> 16) & 0xf; + char *type, *rot; + + switch ((opcode >> 24) & 0x3) { + case 0: + type = "B16"; + break; + case 1: + sprintf(cp, "UNDEFINED"); + return ARM_UNDEFINED_INSTRUCTION; + case 2: + type = "B"; + break; + default: + type = "H"; + break; + } + + switch ((opcode >> 10) & 0x3) { + case 0: + rot = ""; + break; + case 1: + rot = ", ROR #8"; + break; + case 2: + rot = ", ROR #16"; + break; + default: + rot = ", ROR #24"; + break; + } + + if (rn == 0xf) { + sprintf(cp, "%cXT%s%s\tr%d, r%d%s", + (opcode & (1 << 22)) ? 'U' : 'S', + type, COND(opcode), + rd, rm, rot); + return ARM_MOV; + } else { + sprintf(cp, "%cXTA%s%s\tr%d, r%d, r%d%s", + (opcode & (1 << 22)) ? 'U' : 'S', + type, COND(opcode), + rd, rn, rm, rot); + return ARM_ADD; + } +} + +static int evaluate_p_add_sub(uint32_t opcode, uint32_t address, char *cp) +{ + char *prefix; + char *op; + int type; + + switch ((opcode >> 20) & 0x7) { + case 1: + prefix = "S"; + break; + case 2: + prefix = "Q"; + break; + case 3: + prefix = "SH"; + break; + case 5: + prefix = "U"; + break; + case 6: + prefix = "UQ"; + break; + case 7: + prefix = "UH"; + break; + default: + goto undef; + } + + switch ((opcode >> 5) & 0x7) { + case 0: + op = "ADD16"; + type = ARM_ADD; + break; + case 1: + op = "ADDSUBX"; + type = ARM_ADD; + break; + case 2: + op = "SUBADDX"; + type = ARM_SUB; + break; + case 3: + op = "SUB16"; + type = ARM_SUB; + break; + case 4: + op = "ADD8"; + type = ARM_ADD; + break; + case 7: + op = "SUB8"; + type = ARM_SUB; + break; + default: + goto undef; + } + + sprintf(cp, "%s%s%s\tr%d, r%d, r%d", prefix, op, COND(opcode), + (int) (opcode >> 12) & 0xf, + (int) (opcode >> 16) & 0xf, + (int) (opcode >> 0) & 0xf); + return type; + +undef: + /* these opcodes might be used someday */ + sprintf(cp, "UNDEFINED"); + return ARM_UNDEFINED_INSTRUCTION; +} + +/* ARMv6 and later support "media" instructions (includes SIMD) */ +static int evaluate_media(uint32_t opcode, uint32_t address, + struct arm_instruction *instruction) +{ + char *cp = instruction->text; + char *mnemonic = NULL; + + sprintf(cp, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t", + address, opcode); + cp = strchr(cp, 0); + + /* parallel add/subtract */ + if ((opcode & 0x01800000) == 0x00000000) { + instruction->type = evaluate_p_add_sub(opcode, address, cp); + return ERROR_OK; + } + + /* halfword pack */ + if ((opcode & 0x01f00020) == 0x00800000) { + char *type, *shift; + unsigned imm = (unsigned) (opcode >> 7) & 0x1f; + + if (opcode & (1 << 6)) { + type = "TB"; + shift = "ASR"; + if (imm == 0) + imm = 32; + } else { + type = "BT"; + shift = "LSL"; + } + sprintf(cp, "PKH%s%s\tr%d, r%d, r%d, %s #%d", + type, COND(opcode), + (int) (opcode >> 12) & 0xf, + (int) (opcode >> 16) & 0xf, + (int) (opcode >> 0) & 0xf, + shift, imm); + return ERROR_OK; + } + + /* word saturate */ + if ((opcode & 0x01a00020) == 0x00a00000) { + char *shift; + unsigned imm = (unsigned) (opcode >> 7) & 0x1f; + + if (opcode & (1 << 6)) { + shift = "ASR"; + if (imm == 0) + imm = 32; + } else { + shift = "LSL"; + } + + sprintf(cp, "%cSAT%s\tr%d, #%d, r%d, %s #%d", + (opcode & (1 << 22)) ? 'U' : 'S', + COND(opcode), + (int) (opcode >> 12) & 0xf, + (int) (opcode >> 16) & 0x1f, + (int) (opcode >> 0) & 0xf, + shift, imm); + return ERROR_OK; + } + + /* sign extension */ + if ((opcode & 0x018000f0) == 0x00800070) { + instruction->type = evaluate_extend(opcode, address, cp); + return ERROR_OK; + } + + /* multiplies */ + if ((opcode & 0x01f00080) == 0x01000000) { + unsigned rn = (opcode >> 12) & 0xf; + + if (rn != 0xf) + sprintf(cp, "SML%cD%s%s\tr%d, r%d, r%d, r%d", + (opcode & (1 << 6)) ? 'S' : 'A', + (opcode & (1 << 5)) ? "X" : "", + COND(opcode), + (int) (opcode >> 16) & 0xf, + (int) (opcode >> 0) & 0xf, + (int) (opcode >> 8) & 0xf, + rn); + else + sprintf(cp, "SMU%cD%s%s\tr%d, r%d, r%d", + (opcode & (1 << 6)) ? 'S' : 'A', + (opcode & (1 << 5)) ? "X" : "", + COND(opcode), + (int) (opcode >> 16) & 0xf, + (int) (opcode >> 0) & 0xf, + (int) (opcode >> 8) & 0xf); + return ERROR_OK; + } + if ((opcode & 0x01f00000) == 0x01400000) { + sprintf(cp, "SML%cLD%s%s\tr%d, r%d, r%d, r%d", + (opcode & (1 << 6)) ? 'S' : 'A', + (opcode & (1 << 5)) ? "X" : "", + COND(opcode), + (int) (opcode >> 12) & 0xf, + (int) (opcode >> 16) & 0xf, + (int) (opcode >> 0) & 0xf, + (int) (opcode >> 8) & 0xf); + return ERROR_OK; + } + if ((opcode & 0x01f00000) == 0x01500000) { + unsigned rn = (opcode >> 12) & 0xf; + + switch (opcode & 0xc0) { + case 3: + if (rn == 0xf) + goto undef; + /* FALL THROUGH */ + case 0: + break; + default: + goto undef; + } + + if (rn != 0xf) + sprintf(cp, "SMML%c%s%s\tr%d, r%d, r%d, r%d", + (opcode & (1 << 6)) ? 'S' : 'A', + (opcode & (1 << 5)) ? "R" : "", + COND(opcode), + (int) (opcode >> 16) & 0xf, + (int) (opcode >> 0) & 0xf, + (int) (opcode >> 8) & 0xf, + rn); + else + sprintf(cp, "SMMUL%s%s\tr%d, r%d, r%d", + (opcode & (1 << 5)) ? "R" : "", + COND(opcode), + (int) (opcode >> 16) & 0xf, + (int) (opcode >> 0) & 0xf, + (int) (opcode >> 8) & 0xf); + return ERROR_OK; + } + + + /* simple matches against the remaining decode bits */ + switch (opcode & 0x01f000f0) { + case 0x00a00030: + case 0x00e00030: + /* parallel halfword saturate */ + sprintf(cp, "%cSAT16%s\tr%d, #%d, r%d", + (opcode & (1 << 22)) ? 'U' : 'S', + COND(opcode), + (int) (opcode >> 12) & 0xf, + (int) (opcode >> 16) & 0xf, + (int) (opcode >> 0) & 0xf); + return ERROR_OK; + case 0x00b00030: + mnemonic = "REV"; + break; + case 0x00b000b0: + mnemonic = "REV16"; + break; + case 0x00f000b0: + mnemonic = "REVSH"; + break; + case 0x008000b0: + /* select bytes */ + sprintf(cp, "SEL%s\tr%d, r%d, r%d", COND(opcode), + (int) (opcode >> 12) & 0xf, + (int) (opcode >> 16) & 0xf, + (int) (opcode >> 0) & 0xf); + return ERROR_OK; + case 0x01800010: + /* unsigned sum of absolute differences */ + if (((opcode >> 12) & 0xf) == 0xf) + sprintf(cp, "USAD8%s\tr%d, r%d, r%d", COND(opcode), + (int) (opcode >> 16) & 0xf, + (int) (opcode >> 0) & 0xf, + (int) (opcode >> 8) & 0xf); + else + sprintf(cp, "USADA8%s\tr%d, r%d, r%d, r%d", COND(opcode), + (int) (opcode >> 16) & 0xf, + (int) (opcode >> 0) & 0xf, + (int) (opcode >> 8) & 0xf, + (int) (opcode >> 12) & 0xf); + return ERROR_OK; + } + if (mnemonic) { + unsigned rm = (opcode >> 0) & 0xf; + unsigned rd = (opcode >> 12) & 0xf; + + sprintf(cp, "%s%s\tr%d, r%d", mnemonic, COND(opcode), rm, rd); + return ERROR_OK; + } + +undef: + /* these opcodes might be used someday */ + sprintf(cp, "UNDEFINED"); + return ERROR_OK; +} + /* Miscellaneous load/store instructions */ -int evaluate_misc_load_store(uint32_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_misc_load_store(uint32_t opcode, + uint32_t address, struct arm_instruction *instruction) { uint8_t P, U, I, W, L, S, H; uint8_t Rn, Rd; @@ -566,7 +1016,8 @@ int evaluate_misc_load_store(uint32_t opcode, uint32_t address, arm_instruction_ } /* Load/store multiples instructions */ -int evaluate_ldm_stm(uint32_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_ldm_stm(uint32_t opcode, + uint32_t address, struct arm_instruction *instruction) { uint8_t P, U, S, W, L, Rn; uint32_t register_list; @@ -646,15 +1097,19 @@ int evaluate_ldm_stm(uint32_t opcode, uint32_t address, arm_instruction_t *instr } } - snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s%s r%i%s, {%s}%s", - address, opcode, mnemonic, COND(opcode), addressing_mode, + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 + "\t%s%s%s r%i%s, {%s}%s", + address, opcode, + mnemonic, addressing_mode, COND(opcode), Rn, (W) ? "!" : "", reg_list, (S) ? "^" : ""); return ERROR_OK; } /* Multiplies, extra load/stores */ -int evaluate_mul_and_extra_ld_st(uint32_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_mul_and_extra_ld_st(uint32_t opcode, + uint32_t address, struct arm_instruction *instruction) { /* Multiply (accumulate) (long) and Swap/swap byte */ if ((opcode & 0x000000f0) == 0x00000090) @@ -745,7 +1200,8 @@ int evaluate_mul_and_extra_ld_st(uint32_t opcode, uint32_t address, arm_instruct return evaluate_misc_load_store(opcode, address, instruction); } -int evaluate_mrs_msr(uint32_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_mrs_msr(uint32_t opcode, + uint32_t address, struct arm_instruction *instruction) { int R = (opcode & 0x00400000) >> 22; char *PSR = (R) ? "SPSR" : "CPSR"; @@ -799,7 +1255,8 @@ int evaluate_mrs_msr(uint32_t opcode, uint32_t address, arm_instruction_t *instr } /* Miscellaneous instructions */ -int evaluate_misc_instr(uint32_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_misc_instr(uint32_t opcode, + uint32_t address, struct arm_instruction *instruction) { /* MRS/MSR */ if ((opcode & 0x000000f0) == 0x00000000) @@ -821,6 +1278,21 @@ int evaluate_misc_instr(uint32_t opcode, uint32_t address, arm_instruction_t *in instruction->info.b_bl_bx_blx.target_address = -1; } + /* BXJ - "Jazelle" support (ARMv5-J) */ + if ((opcode & 0x006000f0) == 0x00200020) + { + uint8_t Rm; + instruction->type = ARM_BX; + Rm = opcode & 0xf; + + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tBXJ%s r%i", + address, opcode, COND(opcode), Rm); + + instruction->info.b_bl_bx_blx.reg_operand = Rm; + instruction->info.b_bl_bx_blx.target_address = -1; + } + /* CLZ */ if ((opcode & 0x006000f0) == 0x00600010) { @@ -974,7 +1446,8 @@ int evaluate_misc_instr(uint32_t opcode, uint32_t address, arm_instruction_t *in return ERROR_OK; } -int evaluate_data_proc(uint32_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_data_proc(uint32_t opcode, + uint32_t address, struct arm_instruction *instruction) { uint8_t I, op, S, Rn, Rd; char *mnemonic = NULL; @@ -1181,10 +1654,11 @@ int evaluate_data_proc(uint32_t opcode, uint32_t address, arm_instruction_t *ins return ERROR_OK; } -int arm_evaluate_opcode(uint32_t opcode, uint32_t address, arm_instruction_t *instruction) +int arm_evaluate_opcode(uint32_t opcode, uint32_t address, + struct arm_instruction *instruction) { /* clear fields, to avoid confusion */ - memset(instruction, 0, sizeof(arm_instruction_t)); + memset(instruction, 0, sizeof(struct arm_instruction)); instruction->opcode = opcode; instruction->instruction_size = 4; @@ -1195,13 +1669,9 @@ int arm_evaluate_opcode(uint32_t opcode, uint32_t address, arm_instruction_t *in if ((opcode & 0x08000000) == 0x00000000) return evaluate_pld(opcode, address, instruction); - /* Undefined instruction */ + /* Undefined instruction (or ARMv6+ SRS/RFE) */ if ((opcode & 0x0e000000) == 0x08000000) - { - instruction->type = ARM_UNDEFINED_INSTRUCTION; - snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tUNDEFINED INSTRUCTION", address, opcode); - return ERROR_OK; - } + return evaluate_srs(opcode, address, instruction); /* Branch and branch with link and change to Thumb */ if ((opcode & 0x0e000000) == 0x0a000000) @@ -1272,17 +1742,24 @@ int arm_evaluate_opcode(uint32_t opcode, uint32_t address, arm_instruction_t *in /* catch opcodes with [27:25] = b011 */ if ((opcode & 0x0e000000) == 0x06000000) { - /* Undefined instruction */ - if ((opcode & 0x00000010) == 0x00000010) + /* Load/store register offset */ + if ((opcode & 0x00000010) == 0x00000000) + return evaluate_load_store(opcode, address, instruction); + + /* Architecturally Undefined instruction + * ... don't expect these to ever be used + */ + if ((opcode & 0x07f000f0) == 0x07f000f0) { instruction->type = ARM_UNDEFINED_INSTRUCTION; - snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tUNDEFINED INSTRUCTION", address, opcode); + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tUNDEF", + address, opcode); return ERROR_OK; } - /* Load/store register offset */ - return evaluate_load_store(opcode, address, instruction); - + /* "media" instructions */ + return evaluate_media(opcode, address, instruction); } /* catch opcodes with [27:25] = b100 */ @@ -1300,7 +1777,7 @@ int arm_evaluate_opcode(uint32_t opcode, uint32_t address, arm_instruction_t *in } /* catch opcodes with [27:25] = b110 */ - if ((opcode & 0x0e000000) == 0x0a000000) + if ((opcode & 0x0e000000) == 0x0c000000) { /* Coprocessor load/store and double register transfers */ return evaluate_ldc_stc_mcrr_mrrc(opcode, address, instruction); @@ -1322,11 +1799,13 @@ int arm_evaluate_opcode(uint32_t opcode, uint32_t address, arm_instruction_t *in return evaluate_cdp_mcr_mrc(opcode, address, instruction); } - LOG_ERROR("should never reach this point"); + LOG_ERROR("ARM: should never reach this point (opcode=%08x)", + (unsigned) opcode); return -1; } -int evaluate_b_bl_blx_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_b_bl_blx_thumb(uint16_t opcode, + uint32_t address, struct arm_instruction *instruction) { uint32_t offset = opcode & 0x7ff; uint32_t opc = (opcode >> 11) & 0x3; @@ -1350,6 +1829,7 @@ int evaluate_b_bl_blx_thumb(uint16_t opcode, uint32_t address, arm_instruction_t case 1: instruction->type = ARM_BLX; mnemonic = "BLX"; + target_address &= 0xfffffffc; break; /* BL/BLX prefix */ case 2: @@ -1365,8 +1845,12 @@ int evaluate_b_bl_blx_thumb(uint16_t opcode, uint32_t address, arm_instruction_t } /* TODO: deal correctly with dual opcode (prefixed) BL/BLX; - * these are effectively 32-bit instructions even in Thumb1. - * Might be simplest to always use the Thumb2 decoder. + * these are effectively 32-bit instructions even in Thumb1. For + * disassembly, it's simplest to always use the Thumb2 decoder. + * + * But some cores will evidently handle them as two instructions, + * where exceptions may occur between the two. The ETMv3.2+ ID + * register has a bit which exposes this behavior. */ snprintf(instruction->text, 128, @@ -1379,7 +1863,8 @@ int evaluate_b_bl_blx_thumb(uint16_t opcode, uint32_t address, arm_instruction_t return ERROR_OK; } -int evaluate_add_sub_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_add_sub_thumb(uint16_t opcode, + uint32_t address, struct arm_instruction *instruction) { uint8_t Rd = (opcode >> 0) & 0x7; uint8_t Rn = (opcode >> 3) & 0x7; @@ -1395,6 +1880,7 @@ int evaluate_add_sub_thumb(uint16_t opcode, uint32_t address, arm_instruction_t } else { + /* REVISIT: if reg_imm == 0, display as "MOVS" */ instruction->type = ARM_ADD; mnemonic = "ADDS"; } @@ -1423,7 +1909,8 @@ int evaluate_add_sub_thumb(uint16_t opcode, uint32_t address, arm_instruction_t return ERROR_OK; } -int evaluate_shift_imm_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_shift_imm_thumb(uint16_t opcode, + uint32_t address, struct arm_instruction *instruction) { uint8_t Rd = (opcode >> 0) & 0x7; uint8_t Rm = (opcode >> 3) & 0x7; @@ -1468,7 +1955,8 @@ int evaluate_shift_imm_thumb(uint16_t opcode, uint32_t address, arm_instruction_ return ERROR_OK; } -int evaluate_data_proc_imm_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_data_proc_imm_thumb(uint16_t opcode, + uint32_t address, struct arm_instruction *instruction) { uint8_t imm = opcode & 0xff; uint8_t Rd = (opcode >> 8) & 0x7; @@ -1510,7 +1998,8 @@ int evaluate_data_proc_imm_thumb(uint16_t opcode, uint32_t address, arm_instruct return ERROR_OK; } -int evaluate_data_proc_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_data_proc_thumb(uint16_t opcode, + uint32_t address, struct arm_instruction *instruction) { uint8_t high_reg, op, Rm, Rd,H1,H2; char *mnemonic = NULL; @@ -1695,7 +2184,8 @@ static inline uint32_t thumb_alignpc4(uint32_t addr) return (addr + 4) & ~3; } -int evaluate_load_literal_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_load_literal_thumb(uint16_t opcode, + uint32_t address, struct arm_instruction *instruction) { uint32_t immediate; uint8_t Rd = (opcode >> 8) & 0x7; @@ -1719,7 +2209,8 @@ int evaluate_load_literal_thumb(uint16_t opcode, uint32_t address, arm_instructi return ERROR_OK; } -int evaluate_load_store_reg_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_load_store_reg_thumb(uint16_t opcode, + uint32_t address, struct arm_instruction *instruction) { uint8_t Rd = (opcode >> 0) & 0x7; uint8_t Rn = (opcode >> 3) & 0x7; @@ -1776,7 +2267,8 @@ int evaluate_load_store_reg_thumb(uint16_t opcode, uint32_t address, arm_instruc return ERROR_OK; } -int evaluate_load_store_imm_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_load_store_imm_thumb(uint16_t opcode, + uint32_t address, struct arm_instruction *instruction) { uint32_t offset = (opcode >> 6) & 0x1f; uint8_t Rd = (opcode >> 0) & 0x7; @@ -1822,7 +2314,8 @@ int evaluate_load_store_imm_thumb(uint16_t opcode, uint32_t address, arm_instruc return ERROR_OK; } -int evaluate_load_store_stack_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_load_store_stack_thumb(uint16_t opcode, + uint32_t address, struct arm_instruction *instruction) { uint32_t offset = opcode & 0xff; uint8_t Rd = (opcode >> 8) & 0x7; @@ -1853,13 +2346,14 @@ int evaluate_load_store_stack_thumb(uint16_t opcode, uint32_t address, arm_instr return ERROR_OK; } -int evaluate_add_sp_pc_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_add_sp_pc_thumb(uint16_t opcode, + uint32_t address, struct arm_instruction *instruction) { uint32_t imm = opcode & 0xff; uint8_t Rd = (opcode >> 8) & 0x7; uint8_t Rn; uint32_t SP = opcode & (1 << 11); - char *reg_name; + const char *reg_name; instruction->type = ARM_ADD; @@ -1886,7 +2380,8 @@ int evaluate_add_sp_pc_thumb(uint16_t opcode, uint32_t address, arm_instruction_ return ERROR_OK; } -int evaluate_adjust_stack_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_adjust_stack_thumb(uint16_t opcode, + uint32_t address, struct arm_instruction *instruction) { uint32_t imm = opcode & 0x7f; uint8_t opc = opcode & (1 << 7); @@ -1916,7 +2411,8 @@ int evaluate_adjust_stack_thumb(uint16_t opcode, uint32_t address, arm_instructi return ERROR_OK; } -int evaluate_breakpoint_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_breakpoint_thumb(uint16_t opcode, + uint32_t address, struct arm_instruction *instruction) { uint32_t imm = opcode & 0xff; @@ -1929,7 +2425,8 @@ int evaluate_breakpoint_thumb(uint16_t opcode, uint32_t address, arm_instruction return ERROR_OK; } -int evaluate_load_store_multiple_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_load_store_multiple_thumb(uint16_t opcode, + uint32_t address, struct arm_instruction *instruction) { uint32_t reg_list = opcode & 0xff; uint32_t L = opcode & (1 << 11); @@ -1942,6 +2439,10 @@ int evaluate_load_store_multiple_thumb(uint16_t opcode, uint32_t address, arm_in char ptr_name[7] = ""; int i; + /* REVISIT: in ThumbEE mode, there are no LDM or STM instructions. + * The STMIA and LDMIA opcodes are used for other instructions. + */ + if ((opcode & 0xf000) == 0xc000) { /* generic load/store multiple */ char *wback = "!"; @@ -2002,7 +2503,8 @@ int evaluate_load_store_multiple_thumb(uint16_t opcode, uint32_t address, arm_in return ERROR_OK; } -int evaluate_cond_branch_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction) +static int evaluate_cond_branch_thumb(uint16_t opcode, + uint32_t address, struct arm_instruction *instruction) { uint32_t offset = opcode & 0xff; uint8_t cond = (opcode >> 8) & 0xf; @@ -2044,7 +2546,7 @@ int evaluate_cond_branch_thumb(uint16_t opcode, uint32_t address, arm_instructio } static int evaluate_cb_thumb(uint16_t opcode, uint32_t address, - arm_instruction_t *instruction) + struct arm_instruction *instruction) { unsigned offset; @@ -2062,7 +2564,7 @@ static int evaluate_cb_thumb(uint16_t opcode, uint32_t address, } static int evaluate_extend_thumb(uint16_t opcode, uint32_t address, - arm_instruction_t *instruction) + struct arm_instruction *instruction) { /* added in ARMv6 */ snprintf(instruction->text, 128, @@ -2076,7 +2578,7 @@ static int evaluate_extend_thumb(uint16_t opcode, uint32_t address, } static int evaluate_cps_thumb(uint16_t opcode, uint32_t address, - arm_instruction_t *instruction) + struct arm_instruction *instruction) { /* added in ARMv6 */ if ((opcode & 0x0ff0) == 0x0650) @@ -2097,7 +2599,7 @@ static int evaluate_cps_thumb(uint16_t opcode, uint32_t address, } static int evaluate_byterev_thumb(uint16_t opcode, uint32_t address, - arm_instruction_t *instruction) + struct arm_instruction *instruction) { char *suffix; @@ -2122,7 +2624,7 @@ static int evaluate_byterev_thumb(uint16_t opcode, uint32_t address, } static int evaluate_hint_thumb(uint16_t opcode, uint32_t address, - arm_instruction_t *instruction) + struct arm_instruction *instruction) { char *hint; @@ -2155,7 +2657,7 @@ static int evaluate_hint_thumb(uint16_t opcode, uint32_t address, } static int evaluate_ifthen_thumb(uint16_t opcode, uint32_t address, - arm_instruction_t *instruction) + struct arm_instruction *instruction) { unsigned cond = (opcode >> 4) & 0x0f; char *x = "", *y = "", *z = ""; @@ -2179,10 +2681,10 @@ static int evaluate_ifthen_thumb(uint16_t opcode, uint32_t address, return ERROR_OK; } -int thumb_evaluate_opcode(uint16_t opcode, uint32_t address, arm_instruction_t *instruction) +int thumb_evaluate_opcode(uint16_t opcode, uint32_t address, struct arm_instruction *instruction) { /* clear fields, to avoid confusion */ - memset(instruction, 0, sizeof(arm_instruction_t)); + memset(instruction, 0, sizeof(struct arm_instruction)); instruction->opcode = opcode; instruction->instruction_size = 2; @@ -2312,12 +2814,12 @@ int thumb_evaluate_opcode(uint16_t opcode, uint32_t address, arm_instruction_t * } } - LOG_ERROR("should never reach this point (opcode=%04x)",opcode); + LOG_ERROR("Thumb: should never reach this point (opcode=%04x)", opcode); return -1; } static int t2ev_b_bl(uint32_t opcode, uint32_t address, - arm_instruction_t *instruction, char *cp) + struct arm_instruction *instruction, char *cp) { unsigned offset; unsigned b21 = 1 << 21; @@ -2358,7 +2860,7 @@ static int t2ev_b_bl(uint32_t opcode, uint32_t address, } static int t2ev_cond_b(uint32_t opcode, uint32_t address, - arm_instruction_t *instruction, char *cp) + struct arm_instruction *instruction, char *cp) { unsigned offset; unsigned b17 = 1 << 17; @@ -2447,7 +2949,7 @@ static const char *special_name(int number) } static int t2ev_hint(uint32_t opcode, uint32_t address, - arm_instruction_t *instruction, char *cp) + struct arm_instruction *instruction, char *cp) { const char *mnemonic; @@ -2487,11 +2989,17 @@ static int t2ev_hint(uint32_t opcode, uint32_t address, } static int t2ev_misc(uint32_t opcode, uint32_t address, - arm_instruction_t *instruction, char *cp) + struct arm_instruction *instruction, char *cp) { const char *mnemonic; switch ((opcode >> 4) & 0x0f) { + case 0: + mnemonic = "LEAVEX"; + break; + case 1: + mnemonic = "ENTERX"; + break; case 2: mnemonic = "CLREX"; break; @@ -2512,7 +3020,7 @@ static int t2ev_misc(uint32_t opcode, uint32_t address, } static int t2ev_b_misc(uint32_t opcode, uint32_t address, - arm_instruction_t *instruction, char *cp) + struct arm_instruction *instruction, char *cp) { /* permanently undefined */ if ((opcode & 0x07f07000) == 0x07f02000) { @@ -2545,6 +3053,9 @@ static int t2ev_b_misc(uint32_t opcode, uint32_t address, return t2ev_hint(opcode, address, instruction, cp); case 0x3b: return t2ev_misc(opcode, address, instruction, cp); + case 0x3c: + sprintf(cp, "BXJ\tr%d", (int) (opcode >> 16) & 0x0f); + return ERROR_OK; case 0x3e: case 0x3f: sprintf(cp, "MRS\tr%d, %s", (int) (opcode >> 8) & 0x0f, @@ -2557,7 +3068,7 @@ undef: } static int t2ev_data_mod_immed(uint32_t opcode, uint32_t address, - arm_instruction_t *instruction, char *cp) + struct arm_instruction *instruction, char *cp) { char *mnemonic = NULL; int rn = (opcode >> 16) & 0xf; @@ -2703,7 +3214,7 @@ static int t2ev_data_mod_immed(uint32_t opcode, uint32_t address, } static int t2ev_data_immed(uint32_t opcode, uint32_t address, - arm_instruction_t *instruction, char *cp) + struct arm_instruction *instruction, char *cp) { char *mnemonic = NULL; int rn = (opcode >> 16) & 0xf; @@ -2736,7 +3247,7 @@ static int t2ev_data_immed(uint32_t opcode, uint32_t address, case 0x0c: /* move constant to top 16 bits of register */ immed |= (opcode >> 4) & 0xf000; - sprintf(cp, "MOVT\tr%d, #%d\t; %#4.4x", rn, immed, immed); + sprintf(cp, "MOVT\tr%d, #%d\t; %#4.4x", rd, immed, immed); return ERROR_OK; case 0x10: case 0x12: @@ -2798,7 +3309,7 @@ do_adr: } static int t2ev_store_single(uint32_t opcode, uint32_t address, - arm_instruction_t *instruction, char *cp) + struct arm_instruction *instruction, char *cp) { unsigned op = (opcode >> 20) & 0xf; char *size = ""; @@ -2891,7 +3402,7 @@ imm8: } static int t2ev_mul32(uint32_t opcode, uint32_t address, - arm_instruction_t *instruction, char *cp) + struct arm_instruction *instruction, char *cp) { int ra = (opcode >> 12) & 0xf; @@ -2921,7 +3432,7 @@ static int t2ev_mul32(uint32_t opcode, uint32_t address, } static int t2ev_mul64_div(uint32_t opcode, uint32_t address, - arm_instruction_t *instruction, char *cp) + struct arm_instruction *instruction, char *cp) { int op = (opcode >> 4) & 0xf; char *infix = "MUL"; @@ -2958,17 +3469,34 @@ static int t2ev_mul64_div(uint32_t opcode, uint32_t address, } static int t2ev_ldm_stm(uint32_t opcode, uint32_t address, - arm_instruction_t *instruction, char *cp) + struct arm_instruction *instruction, char *cp) { int rn = (opcode >> 16) & 0xf; int op = (opcode >> 22) & 0x6; int t = (opcode >> 21) & 1; unsigned registers = opcode & 0xffff; + char *mode = ""; if (opcode & (1 << 20)) op |= 1; switch (op) { + case 0: + mode = "DB"; + /* FALL THROUGH */ + case 6: + sprintf(cp, "SRS%s\tsp%s, #%d", mode, + t ? "!" : "", + (unsigned) (opcode & 0x1f)); + return ERROR_OK; + case 1: + mode = "DB"; + /* FALL THROUGH */ + case 7: + sprintf(cp, "RFE%s\tr%d%s", mode, + (unsigned) ((opcode >> 16) & 0xf), + t ? "!" : ""); + return ERROR_OK; case 2: sprintf(cp, "STM.W\tr%d%s, ", rn, t ? "!" : ""); break; @@ -3006,8 +3534,135 @@ static int t2ev_ldm_stm(uint32_t opcode, uint32_t address, return ERROR_OK; } +/* load/store dual or exclusive, table branch */ +static int t2ev_ldrex_strex(uint32_t opcode, uint32_t address, + struct arm_instruction *instruction, char *cp) +{ + unsigned op1op2 = (opcode >> 20) & 0x3; + unsigned op3 = (opcode >> 4) & 0xf; + char *mnemonic; + unsigned rn = (opcode >> 16) & 0xf; + unsigned rt = (opcode >> 12) & 0xf; + unsigned rd = (opcode >> 8) & 0xf; + unsigned imm = opcode & 0xff; + char *p1 = ""; + char *p2 = "]"; + + op1op2 |= (opcode >> 21) & 0xc; + switch (op1op2) { + case 0: + mnemonic = "STREX"; + goto strex; + case 1: + mnemonic = "LDREX"; + goto ldrex; + case 2: + case 6: + case 8: + case 10: + case 12: + case 14: + mnemonic = "STRD"; + goto immediate; + case 3: + case 7: + case 9: + case 11: + case 13: + case 15: + mnemonic = "LDRD"; + if (rn == 15) + goto literal; + else + goto immediate; + case 4: + switch (op3) { + case 4: + mnemonic = "STREXB"; + break; + case 5: + mnemonic = "STREXH"; + break; + default: + return ERROR_INVALID_ARGUMENTS; + } + rd = opcode & 0xf; + imm = 0; + goto strex; + case 5: + switch (op3) { + case 0: + sprintf(cp, "TBB\t[r%u, r%u]", rn, imm & 0xf); + return ERROR_OK; + case 1: + sprintf(cp, "TBH\t[r%u, r%u, LSL #1]", rn, imm & 0xf); + return ERROR_OK; + case 4: + mnemonic = "LDREXB"; + break; + case 5: + mnemonic = "LDREXH"; + break; + default: + return ERROR_INVALID_ARGUMENTS; + } + imm = 0; + goto ldrex; + } + return ERROR_INVALID_ARGUMENTS; + +strex: + imm <<= 2; + if (imm) + sprintf(cp, "%s\tr%u, r%u, [r%u, #%u]\t; %#2.2x", + mnemonic, rd, rt, rn, imm, imm); + else + sprintf(cp, "%s\tr%u, r%u, [r%u]", + mnemonic, rd, rt, rn); + return ERROR_OK; + +ldrex: + imm <<= 2; + if (imm) + sprintf(cp, "%s\tr%u, [r%u, #%u]\t; %#2.2x", + mnemonic, rt, rn, imm, imm); + else + sprintf(cp, "%s\tr%u, [r%u]", + mnemonic, rt, rn); + return ERROR_OK; + +immediate: + /* two indexed modes will write back rn */ + if (opcode & (1 << 21)) { + if (opcode & (1 << 24)) /* pre-indexed */ + p2 = "]!"; + else { /* post-indexed */ + p1 = "]"; + p2 = ""; + } + } + + imm <<= 2; + sprintf(cp, "%s\tr%u, r%u, [r%u%s, #%s%u%s\t; %#2.2x", + mnemonic, rt, rd, rn, p1, + (opcode & (1 << 23)) ? "" : "-", + imm, p2, imm); + return ERROR_OK; + +literal: + address = thumb_alignpc4(address); + imm <<= 2; + if (opcode & (1 << 23)) + address += imm; + else + address -= imm; + sprintf(cp, "%s\tr%u, r%u, %#8.8" PRIx32, + mnemonic, rt, rd, address); + return ERROR_OK; +} + static int t2ev_data_shift(uint32_t opcode, uint32_t address, - arm_instruction_t *instruction, char *cp) + struct arm_instruction *instruction, char *cp) { int op = (opcode >> 21) & 0xf; int rd = (opcode >> 8) & 0xf; @@ -3017,17 +3672,18 @@ static int t2ev_data_shift(uint32_t opcode, uint32_t address, char *mnemonic; char *suffix = ""; - immed |= (opcode >> 10) & 0x7; - if (opcode & (1 << 21)) + immed |= (opcode >> 10) & 0x1c; + if (opcode & (1 << 20)) suffix = "S"; switch (op) { case 0: if (rd == 0xf) { - if (!(opcode & (1 << 21))) + if (!(opcode & (1 << 20))) return ERROR_INVALID_ARGUMENTS; instruction->type = ARM_TST; mnemonic = "TST"; + suffix = ""; goto two; } instruction->type = ARM_AND; @@ -3058,7 +3714,7 @@ static int t2ev_data_shift(uint32_t opcode, uint32_t address, break; default: if (immed == 0) { - sprintf(cp, "RRX%s.W\tr%d, r%d", + sprintf(cp, "RRX%s\tr%d, r%d", suffix, rd, (int) (opcode & 0xf)); return ERROR_OK; @@ -3085,10 +3741,11 @@ static int t2ev_data_shift(uint32_t opcode, uint32_t address, break; case 4: if (rd == 0xf) { - if (!(opcode & (1 << 21))) + if (!(opcode & (1 << 20))) return ERROR_INVALID_ARGUMENTS; instruction->type = ARM_TEQ; mnemonic = "TEQ"; + suffix = ""; goto two; } instruction->type = ARM_EOR; @@ -3096,10 +3753,11 @@ static int t2ev_data_shift(uint32_t opcode, uint32_t address, break; case 8: if (rd == 0xf) { - if (!(opcode & (1 << 21))) + if (!(opcode & (1 << 20))) return ERROR_INVALID_ARGUMENTS; instruction->type = ARM_CMN; mnemonic = "CMN"; + suffix = ""; goto two; } instruction->type = ARM_ADD; @@ -3119,6 +3777,7 @@ static int t2ev_data_shift(uint32_t opcode, uint32_t address, return ERROR_INVALID_ARGUMENTS; instruction->type = ARM_CMP; mnemonic = "CMP"; + suffix = ""; goto two; } instruction->type = ARM_SUB; @@ -3146,13 +3805,17 @@ shift: break; case 1: suffix = "LSR"; + if (immed == 32) + immed = 0; break; case 2: suffix = "ASR"; + if (immed == 32) + immed = 0; break; case 3: if (immed == 0) { - strcpy(cp, "RRX"); + strcpy(cp, ", RRX"); return ERROR_OK; } suffix = "ROR"; @@ -3174,7 +3837,7 @@ immediate: } static int t2ev_data_reg(uint32_t opcode, uint32_t address, - arm_instruction_t *instruction, char *cp) + struct arm_instruction *instruction, char *cp) { char *mnemonic; char * suffix = ""; @@ -3275,7 +3938,7 @@ static int t2ev_data_reg(uint32_t opcode, uint32_t address, } static int t2ev_load_word(uint32_t opcode, uint32_t address, - arm_instruction_t *instruction, char *cp) + struct arm_instruction *instruction, char *cp) { int rn = (opcode >> 16) & 0xf; int immed; @@ -3349,7 +4012,7 @@ static int t2ev_load_word(uint32_t opcode, uint32_t address, } static int t2ev_load_byte_hints(uint32_t opcode, uint32_t address, - arm_instruction_t *instruction, char *cp) + struct arm_instruction *instruction, char *cp) { int rn = (opcode >> 16) & 0xf; int rt = (opcode >> 12) & 0xf; @@ -3513,7 +4176,7 @@ ldrsb_literal: case 3: if (rt == 0xf) { immed = opcode & 0xfff; - sprintf(cp, "PLI\t[r%d, #%d]\t; %#3.3" PRIx32, + sprintf(cp, "PLI\t[r%d, #%d]\t; %#3.3x", rn, immed, immed); return ERROR_OK; } @@ -3528,7 +4191,7 @@ ldrsb_literal: } static int t2ev_load_halfword(uint32_t opcode, uint32_t address, - arm_instruction_t *instruction, char *cp) + struct arm_instruction *instruction, char *cp) { int rn = (opcode >> 16) & 0xf; int rt = (opcode >> 12) & 0xf; @@ -3610,7 +4273,7 @@ ldrh_literal: * always set. That means eventual arm_simulate_step() support for Thumb2 * will need work in this area. */ -int thumb2_opcode(target_t *target, uint32_t address, arm_instruction_t *instruction) +int thumb2_opcode(struct target *target, uint32_t address, struct arm_instruction *instruction) { int retval; uint16_t op; @@ -3621,7 +4284,7 @@ int thumb2_opcode(target_t *target, uint32_t address, arm_instruction_t *instruc address &= ~1; /* clear fields, to avoid confusion */ - memset(instruction, 0, sizeof(arm_instruction_t)); + memset(instruction, 0, sizeof(struct arm_instruction)); /* read first halfword, see if this is the only one */ retval = target_read_u16(target, address, &op); @@ -3668,6 +4331,10 @@ int thumb2_opcode(target_t *target, uint32_t address, arm_instruction_t *instruc else if ((opcode & 0x1e400000) == 0x08000000) retval = t2ev_ldm_stm(opcode, address, instruction, cp); + /* ARMv7-M: A5.3.6 Load/store dual or exclusive, table branch */ + else if ((opcode & 0x1e400000) == 0x08400000) + retval = t2ev_ldrex_strex(opcode, address, instruction, cp); + /* ARMv7-M: A5.3.7 Load word */ else if ((opcode & 0x1f700000) == 0x18500000) retval = t2ev_load_word(opcode, address, instruction, cp); @@ -3702,11 +4369,14 @@ int thumb2_opcode(target_t *target, uint32_t address, arm_instruction_t *instruc else if ((opcode & 0x1f800000) == 0x1b800000) retval = t2ev_mul64_div(opcode, address, instruction, cp); - /* FIXME decode more 32-bit instructions */ - if (retval == ERROR_OK) return retval; + /* + * Thumb2 also supports coprocessor, ThumbEE, and DSP/Media (SIMD) + * instructions; not yet handled here. + */ + if (retval == ERROR_INVALID_ARGUMENTS) { instruction->type = ARM_UNDEFINED_INSTRUCTION; strcpy(cp, "UNDEFINED OPCODE"); @@ -3720,7 +4390,7 @@ int thumb2_opcode(target_t *target, uint32_t address, arm_instruction_t *instruc return ERROR_OK; } -int arm_access_size(arm_instruction_t *instruction) +int arm_access_size(struct arm_instruction *instruction) { if ((instruction->type == ARM_LDRB) || (instruction->type == ARM_LDRBT)