From f586351e79f304cbd7ca995a0a1b8daad266c2e0 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Wed, 6 Mar 2013 18:34:20 +0100 Subject: [PATCH] T32: Dissassembler to evaluate memory instructions. For the T32 variant we have to evaluate the memory access instruction to find out, which memory address was accessed. Dissassmbly by OpenOCDs arm_disassembler.hpp/.cc: - fine for ARM / Thumb1 - needs fixes for Thumb2 :( (currently doing that..) --- debuggers/t32/src/T32Connector.cc | 2 +- debuggers/t32/src/main.cc | 11 +- src/core/sal/CMakeLists.txt | 2 + src/core/sal/MemoryInstruction.hpp | 40 + src/core/sal/arm/ArmMemoryInstruction.cc | 97 + src/core/sal/arm/ArmMemoryInstruction.hpp | 34 + src/core/sal/arm/arm_disassembler.cc | 4565 ++++++++++++++++++++ src/core/sal/arm/arm_disassembler.hpp | 226 + src/core/util/Disassembler.cc | 21 +- src/core/util/Disassembler.hpp | 18 +- src/core/util/ElfReader.cc | 4 +- src/experiments/kesorefs/experiment.cc | 2 +- src/experiments/vezs-example/experiment.cc | 17 +- 13 files changed, 5016 insertions(+), 23 deletions(-) create mode 100644 src/core/sal/MemoryInstruction.hpp create mode 100644 src/core/sal/arm/ArmMemoryInstruction.cc create mode 100644 src/core/sal/arm/ArmMemoryInstruction.hpp create mode 100644 src/core/sal/arm/arm_disassembler.cc create mode 100644 src/core/sal/arm/arm_disassembler.hpp diff --git a/debuggers/t32/src/T32Connector.cc b/debuggers/t32/src/T32Connector.cc index 1e425159..75bc2bb1 100644 --- a/debuggers/t32/src/T32Connector.cc +++ b/debuggers/t32/src/T32Connector.cc @@ -1,6 +1,7 @@ #include "T32Connector.hpp" #include #include +#include "sal/MemoryInstruction.hpp" using namespace fail; @@ -93,7 +94,6 @@ void T32Connector::brk() { #include "sal/t32/T32Constants.hpp" void T32Connector::test() { - } /* Default T32 error handler */ diff --git a/debuggers/t32/src/main.cc b/debuggers/t32/src/main.cc index c2639edf..7b2f4543 100644 --- a/debuggers/t32/src/main.cc +++ b/debuggers/t32/src/main.cc @@ -23,6 +23,8 @@ #include "T32Connector.hpp" #include "t32config.hpp" +#include "sal/MemoryInstruction.hpp" +#include "util/Disassembler.hpp" using namespace std; using namespace fail; @@ -96,6 +98,8 @@ int main(int argc, char** argv){ // The experiments/traces hopefully set some Breakpoints, we can react on. // We may also provide a timeout, if a TimerListener was set wanted. + MemoryInstruction mem; + address_t ip; while(1) { // Start execution (with next timeout, if any) @@ -105,8 +109,11 @@ int main(int argc, char** argv){ // Evaluate state. t32.test();// TODO // Call appropriate callback of the SimulatorController. - fail::simulator.onBreakpoint(&fail::simulator.getCPU(0), fail::simulator.getCPU(0).getInstructionPointer(), fail::ANY_ADDR); -// TODO fail::simulator.onMemoryAccess(&fail::simulator.getCPU(0), 0x20002074, 1, true, fail::simulator.getCPU(0).getInstructionPointer()); + ip = fail::simulator.getCPU(0).getInstructionPointer(); + fail::simulator.onBreakpoint(&fail::simulator.getCPU(0), ip , fail::ANY_ADDR); + if( meminstruction.eval(ip, mem) ) { + fail::simulator.onMemoryAccess(&fail::simulator.getCPU(0), mem.getAddress(), mem.getWidth(), mem.isWriteAccess(), ip ); + } } cout << "[T32 Backend] After startup" << endl; diff --git a/src/core/sal/CMakeLists.txt b/src/core/sal/CMakeLists.txt index fe5abddb..8a1b200c 100644 --- a/src/core/sal/CMakeLists.txt +++ b/src/core/sal/CMakeLists.txt @@ -64,6 +64,8 @@ elseif(BUILD_T32) if(BUILD_ARM) set(SRCS ${SRCS} t32/T32ArmCPU.cc + arm/ArmMemoryInstruction.cc + arm/arm_disassembler.cc ) endif(BUILD_ARM) endif(BUILD_BOCHS) diff --git a/src/core/sal/MemoryInstruction.hpp b/src/core/sal/MemoryInstruction.hpp new file mode 100644 index 00000000..5e4b6a9a --- /dev/null +++ b/src/core/sal/MemoryInstruction.hpp @@ -0,0 +1,40 @@ +#ifndef __MEMORYINSTRUCTION_HPP__ +#define __MEMORYINSTRUCTION_HPP__ + +#include +#include "SALConfig.hpp" + +namespace fail { + + class MemoryInstruction { + regdata_t address; + regdata_t value; + uint8_t width; + bool writeAccess; + public: + MemoryInstruction(regdata_t address = ADDR_INV, regdata_t value = 0, uint8_t width = 0, bool writeAccess = false) : + address(address), value(value), width(width), writeAccess(writeAccess) { }; + + bool isWriteAccess(void) const { return writeAccess; } + regdata_t getAddress() const { return address; } + regdata_t getValue() const { return value; } + uint8_t getWidth() const { return width; } + + bool isValid(void) const { return address != ADDR_INV; } + + void setAddress(regdata_t addr) { address = addr; } + void setValue(regdata_t val) { value = val; } + void setWidth(uint8_t w) { width = w; } + void setWriteAccess(bool iswrite) { writeAccess = iswrite; } + }; + + class MemoryInstructionAnalyzer { + public: + virtual bool eval(address_t opcode, MemoryInstruction & result) = 0; + }; + + extern MemoryInstructionAnalyzer & meminstruction; +} // end of namespace fail + +#endif // __MEMORYINSTRUCTION_HPP__ + diff --git a/src/core/sal/arm/ArmMemoryInstruction.cc b/src/core/sal/arm/ArmMemoryInstruction.cc new file mode 100644 index 00000000..ae9e9a9f --- /dev/null +++ b/src/core/sal/arm/ArmMemoryInstruction.cc @@ -0,0 +1,97 @@ +#include "ArmMemoryInstruction.hpp" + +#include +using namespace std; + +namespace fail { + static ArmMemoryInstructionAnalyzer anal; + MemoryInstructionAnalyzer & meminstruction = anal; + + address_t ArmMemoryInstructionAnalyzer::findPrevious(address_t address){ + if(m_dis.hasInstructionAt(address-2)) { + return address - 2; + } else if (m_dis.hasInstructionAt(address - 4)) { + return address - 4; + } else { + return ADDR_INV; + } + } + + void ArmMemoryInstructionAnalyzer::evaluate(arm_instruction & inst, MemoryInstruction& result){ + cout << "Memory Access: " << inst.text << " - Size: " << inst.instruction_size << " Type " << inst.type << endl; + inst.info.load_store.evaluate(); + result.setValue(inst.info.load_store.value); + result.setAddress(inst.info.load_store.address); + result.setWidth(4); // TODO; + result.setWriteAccess(inst.isStoreInstruction()); + } + + // The Cortex M3 Lauterbach is a pain in the ass, as a Memory Watchpoint does not stop + // at the accessing instruction, but 1 or 2 instructions later. + bool ArmMemoryInstructionAnalyzer::eval_cm3(address_t address, MemoryInstruction& result){ + arm_instruction inst; + uint32_t opcode =0; + address = findPrevious(address); // Cortex M3: memory access is at the previous instruction + opcode = m_dis.disassemble(address).opcode; + + // OpenOCDs thumb2_opcode evaluation is not complete yet. :( + thumb2_opcode(address, opcode, &inst); + + if(inst.isMemoryAccess()){ + evaluate(inst, result); + return true; + } else if(inst.isBranchInstruction()){ + // The memory access took place within the function previously branched + int regop = inst.info.b_bl_bx_blx.reg_operand; + uint32_t addr = inst.info.b_bl_bx_blx.target_address; + //cout << " Reg:" << hex << regop << " address " << hex << addr << endl; + // Lets look into this function + if( regop == -1 ){ + // address should be set.. + const ElfSymbol & sym = m_elf.getSymbol(addr|1); // | 1 to set first bit -> thumbmode + addr += sym.getSize(); // Go to end of function. + // THIS IS DANGEROUS: The memory access can be anywhere within this function, one instruction before a ret. + // OR, the memory access itself can result in leaving the function :e.g., ldr pc, [r3] + // We cannot be sure :( Here we assume the first memory access from the back. + do { // go backwards until there is a memory instruction. + addr = findPrevious(addr); // find previous + thumb2_opcode(addr, m_dis.disassemble(addr).opcode, &inst); + } while( !inst.isMemoryAccess() ); + evaluate(inst, result); + return true; + } + } else { + // There was a memory access before, but the previous instruction + // is neither an access nor a branch. + // This can happen if we came here from anywhere, e.g. by ldr pc, [r4] + } + return false; + } + + bool ArmMemoryInstructionAnalyzer::eval_ca9(address_t address, MemoryInstruction& result){ + arm_instruction inst; + uint32_t opcode = m_dis.disassemble(address).opcode; + arm_evaluate_opcode(address, opcode, &inst); + if( inst.isMemoryAccess() ){ + evaluate(inst, result); + return true; + } + return false; + } + +#define CORTEXM3 + + bool ArmMemoryInstructionAnalyzer::eval(address_t address, MemoryInstruction & result){ +#ifdef CORTEXM3 +#warning "Memory Accesses cannot be evaluated completedly!" + return eval_cm3(address, result); +#elif defined CORTEXA9 + return eval_ca9(address, result); +#else +#warning "Memory Accesses are not evaluated!" + return false; +#endif + } + + +}; diff --git a/src/core/sal/arm/ArmMemoryInstruction.hpp b/src/core/sal/arm/ArmMemoryInstruction.hpp new file mode 100644 index 00000000..806fc76d --- /dev/null +++ b/src/core/sal/arm/ArmMemoryInstruction.hpp @@ -0,0 +1,34 @@ +#ifndef __ARMMEMORYINSTRUCITON_HPP__ +#define __ARMMEMORYINSTRUCITON_HPP__ + +#include "../MemoryInstruction.hpp" +#include "util/Disassembler.hpp" +#include "util/ElfReader.hpp" +#include "arm_disassembler.hpp" + +namespace fail { + + class ArmMemoryInstructionAnalyzer : public MemoryInstructionAnalyzer { + fail::ElfReader m_elf; + fail::Disassembler m_dis; + + address_t findPrevious(address_t addr); + void evaluate(arm_instruction & inst, MemoryInstruction& result); + bool eval_ca9(address_t address, MemoryInstruction& result); + bool eval_cm3(address_t address, MemoryInstruction& result); + + public: + + ArmMemoryInstructionAnalyzer() { + m_dis.init(); + }; + + bool eval(address_t opcode, MemoryInstruction & result); + + }; + +} //end of namespace fail + + +#endif // __ARMMEMORYINSTRUCITON_HPP__ + diff --git a/src/core/sal/arm/arm_disassembler.cc b/src/core/sal/arm/arm_disassembler.cc new file mode 100644 index 00000000..92eabb54 --- /dev/null +++ b/src/core/sal/arm/arm_disassembler.cc @@ -0,0 +1,4565 @@ +/*************************************************************************** + * Copyright (C) 2006 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * Copyright (C) 2009 by David Brownell * + * * + * 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. * + ***************************************************************************/ + +//#ifdef HAVE_CONFIG_H +//#include "config.h" +//#endif + +//#include "target.h" +#include "arm_disassembler.hpp" +#include "util/Logger.hpp" +#include "sal/SALInst.hpp" +#include "sal/Memory.hpp" + +//#include +/* general failures + * error codes < 100 + */ +#define ERROR_OK (0) +#define ERROR_NO_CONFIG_FILE (-2) +#define ERROR_BUF_TOO_SMALL (-3) +/* see "Error:" log entry for meaningful message to the user. The caller should + * make no assumptions about what went wrong and try to handle the problem. + */ +#define ERROR_FAIL (-4) +#define ERROR_COMMAND_SYNTAX_ERROR (-601) +#define PRIx32 "x" +#define PRId32 "d" +#define PRIi32 "i" +#define PRIu32 "u" +#define PRId8 PRId32 +#define PRIx64 "llx" + +static fail::Logger log("ARMDisas",false); +#include +#include +// ignore deprecated conversion from string constant to char* +#pragma GCC diagnostic ignored "-Wwrite-strings" + +/* + * 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) */ +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 */ +static uint32_t ror(uint32_t value, int places) +{ + return (value >> places) | (value << (32 - places)); +} + +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 & 0x0d70f000) == 0x0550f000) { + instruction->type = ARM_PLD; + + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD ...TODO...", + address, + opcode); + + 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; + } + + 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; +} + +static int evaluate_swi(uint32_t opcode, + uint32_t address, struct arm_instruction *instruction) +{ + instruction->type = ARM_SWI; + + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tSVC %#6.6" PRIx32, + address, opcode, (opcode & 0xffffff)); + + return ERROR_OK; +} + +static int evaluate_blx_imm(uint32_t opcode, + uint32_t address, struct arm_instruction *instruction) +{ + int offset; + uint32_t immediate; + uint32_t target_address; + + instruction->type = ARM_BLX; + immediate = opcode & 0x00ffffff; + + /* sign extend 24-bit immediate */ + if (immediate & 0x00800000) + offset = 0xff000000 | immediate; + else + offset = immediate; + + /* shift two bits left */ + offset <<= 2; + + /* odd/event halfword */ + if (opcode & 0x01000000) + offset |= 0x2; + + target_address = address + 8 + offset; + + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tBLX 0x%8.8" PRIx32 "", + address, + opcode, + target_address); + + instruction->info.b_bl_bx_blx.reg_operand = -1; + instruction->info.b_bl_bx_blx.target_address = target_address; + + return ERROR_OK; +} + +static int evaluate_b_bl(uint32_t opcode, + uint32_t address, struct arm_instruction *instruction) +{ + uint8_t L; + uint32_t immediate; + int offset; + uint32_t target_address; + + immediate = opcode & 0x00ffffff; + L = (opcode & 0x01000000) >> 24; + + /* sign extend 24-bit immediate */ + if (immediate & 0x00800000) + offset = 0xff000000 | immediate; + else + offset = immediate; + + /* shift two bits left */ + offset <<= 2; + + target_address = address + 8 + offset; + + if (L) + instruction->type = ARM_BL; + else + instruction->type = ARM_B; + + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tB%s%s 0x%8.8" PRIx32, + address, + opcode, + (L) ? "L" : "", + COND(opcode), + target_address); + + instruction->info.b_bl_bx_blx.reg_operand = -1; + instruction->info.b_bl_bx_blx.target_address = target_address; + + return ERROR_OK; +} + +/* Coprocessor load/store and double register transfers + * both normal and extended instruction space (condition field b1111) */ +static int evaluate_ldc_stc_mcrr_mrrc(uint32_t opcode, + uint32_t address, struct arm_instruction *instruction) +{ + uint8_t cp_num = (opcode & 0xf00) >> 8; + + /* MCRR or MRRC */ + if (((opcode & 0x0ff00000) == 0x0c400000) || ((opcode & 0x0ff00000) == 0x0c400000)) { + uint8_t cp_opcode, Rd, Rn, CRm; + char *mnemonic; + + cp_opcode = (opcode & 0xf0) >> 4; + Rd = (opcode & 0xf000) >> 12; + Rn = (opcode & 0xf0000) >> 16; + CRm = (opcode & 0xf); + + /* MCRR */ + if ((opcode & 0x0ff00000) == 0x0c400000) { + instruction->type = ARM_MCRR; + mnemonic = "MCRR"; + } else if ((opcode & 0x0ff00000) == 0x0c500000) { + /* MRRC */ + instruction->type = ARM_MRRC; + mnemonic = "MRRC"; + } else { + log << "Unknown instruction" << std::endl; + return ERROR_FAIL; + } + + 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; + char *mnemonic; + char addressing_mode[32]; + + CRd = (opcode & 0xf000) >> 12; + Rn = (opcode & 0xf0000) >> 16; + offset = (opcode & 0xff) << 2; + + /* load/store */ + if (opcode & 0x00100000) { + instruction->type = ARM_LDC; + mnemonic = "LDC"; + } else { + instruction->type = ARM_STC; + mnemonic = "STC"; + } + + U = (opcode & 0x00800000) >> 23; + + /* addressing modes */ + 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], {%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; +} + +/* Coprocessor data processing instructions + * Coprocessor register transfer instructions + * both normal and extended instruction space (condition field b1111) */ +static int evaluate_cdp_mcr_mrc(uint32_t opcode, + uint32_t address, struct arm_instruction *instruction) +{ + const char *cond; + char *mnemonic; + uint8_t cp_num, opcode_1, CRd_Rd, CRn, CRm, opcode_2; + + cond = ((opcode & 0xf0000000) == 0xf0000000) ? "2" : COND(opcode); + cp_num = (opcode & 0xf00) >> 8; + CRd_Rd = (opcode & 0xf000) >> 12; + CRn = (opcode & 0xf0000) >> 16; + CRm = (opcode & 0xf); + opcode_2 = (opcode & 0xe0) >> 5; + + /* CDP or MRC/MCR */ + if (opcode & 0x00000010) { /* bit 4 set -> MRC/MCR */ + if (opcode & 0x00100000) { /* bit 20 set -> MRC */ + instruction->type = ARM_MRC; + mnemonic = "MRC"; + } else {/* bit 20 not set -> MCR */ + instruction->type = ARM_MCR; + mnemonic = "MCR"; + } + + opcode_1 = (opcode & 0x00e00000) >> 21; + + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s p%i, 0x%2.2x, r%i, c%i, c%i, 0x%2.2x", + address, + opcode, + mnemonic, + cond, + cp_num, + opcode_1, + CRd_Rd, + CRn, + CRm, + opcode_2); + } else {/* bit 4 not set -> CDP */ + instruction->type = ARM_CDP; + mnemonic = "CDP"; + + opcode_1 = (opcode & 0x00f00000) >> 20; + + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s p%i, 0x%2.2x, c%i, c%i, c%i, 0x%2.2x", + address, + opcode, + mnemonic, + cond, + cp_num, + opcode_1, + CRd_Rd, + CRn, + CRm, + opcode_2); + } + + return ERROR_OK; +} + +/* Load/store instructions */ +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; + char *operation;/* "LDR" or "STR" */ + char *suffix; /* "", "B", "T", "BT" */ + char offset[32]; + + /* examine flags */ + I = (opcode & 0x02000000) >> 25; + P = (opcode & 0x01000000) >> 24; + U = (opcode & 0x00800000) >> 23; + B = (opcode & 0x00400000) >> 22; + W = (opcode & 0x00200000) >> 21; + L = (opcode & 0x00100000) >> 20; + + /* target register */ + Rd = (opcode & 0xf000) >> 12; + + /* base register */ + Rn = (opcode & 0xf0000) >> 16; + + instruction->info.load_store.Rd = Rd; + instruction->info.load_store.Rn = Rn; + instruction->info.load_store.U = U; + + /* determine operation */ + if (L) + operation = "LDR"; + else + operation = "STR"; + + /* determine instruction type and suffix */ + if (B) { + if ((P == 0) && (W == 1)) { + if (L) + instruction->type = ARM_LDRBT; + else + instruction->type = ARM_STRBT; + suffix = "BT"; + } else { + if (L) + instruction->type = ARM_LDRB; + else + instruction->type = ARM_STRB; + suffix = "B"; + } + } else { + if ((P == 0) && (W == 1)) { + if (L) + instruction->type = ARM_LDRT; + else + instruction->type = ARM_STRT; + suffix = "T"; + } else { + if (L) + instruction->type = ARM_LDR; + else + instruction->type = ARM_STR; + suffix = ""; + } + } + + if (!I) { /* #+- */ + uint32_t offset_12 = (opcode & 0xfff); + if (offset_12) + snprintf(offset, 32, ", #%s0x%" PRIx32 "", (U) ? "" : "-", offset_12); + else + snprintf(offset, 32, "%s", ""); + + instruction->info.load_store.offset_mode = 0; + instruction->info.load_store.offset.offset = offset_12; + } else {/* either +- or +-, , # */ + uint8_t shift_imm, shift; + uint8_t Rm; + + shift_imm = (opcode & 0xf80) >> 7; + shift = (opcode & 0x60) >> 5; + Rm = (opcode & 0xf); + + /* LSR encodes a shift by 32 bit as 0x0 */ + if ((shift == 0x1) && (shift_imm == 0x0)) + shift_imm = 0x20; + + /* ASR encodes a shift by 32 bit as 0x0 */ + if ((shift == 0x2) && (shift_imm == 0x0)) + shift_imm = 0x20; + + /* ROR by 32 bit is actually a RRX */ + if ((shift == 0x3) && (shift_imm == 0x0)) + shift = 0x4; + + instruction->info.load_store.offset_mode = 1; + instruction->info.load_store.offset.reg.Rm = Rm; + instruction->info.load_store.offset.reg.shift = shift; + instruction->info.load_store.offset.reg.shift_imm = shift_imm; + + if ((shift_imm == 0x0) && (shift == 0x0)) /* +- */ + snprintf(offset, 32, ", %sr%i", (U) ? "" : "-", Rm); + else { /* +-, , # */ + switch (shift) { + case 0x0: /* LSL */ + snprintf(offset, 32, ", %sr%i, LSL #0x%x", (U) ? "" : "-", Rm, shift_imm); + break; + case 0x1: /* LSR */ + snprintf(offset, 32, ", %sr%i, LSR #0x%x", (U) ? "" : "-", Rm, shift_imm); + break; + case 0x2: /* ASR */ + snprintf(offset, 32, ", %sr%i, ASR #0x%x", (U) ? "" : "-", Rm, shift_imm); + break; + case 0x3: /* ROR */ + snprintf(offset, 32, ", %sr%i, ROR #0x%x", (U) ? "" : "-", Rm, shift_imm); + break; + case 0x4: /* RRX */ + snprintf(offset, 32, ", %sr%i, RRX", (U) ? "" : "-", Rm); + break; + } + } + } + + if (P == 1) { + if (W == 0) { /* offset */ + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s%s r%i, [r%i%s]", + address, + opcode, + operation, + COND(opcode), + suffix, + Rd, + Rn, + offset); + + instruction->info.load_store.index_mode = 0; + } else {/* pre-indexed */ + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s%s r%i, [r%i%s]!", + address, + opcode, + operation, + COND(opcode), + suffix, + Rd, + Rn, + offset); + + instruction->info.load_store.index_mode = 1; + } + } else {/* post-indexed */ + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s%s r%i, [r%i]%s", + address, + opcode, + operation, + COND(opcode), + suffix, + Rd, + Rn, + offset); + + instruction->info.load_store.index_mode = 2; + } + + 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 = static_cast(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 = static_cast(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 */ +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; + char *operation;/* "LDR" or "STR" */ + char *suffix; /* "H", "SB", "SH", "D" */ + char offset[32]; + + /* examine flags */ + P = (opcode & 0x01000000) >> 24; + U = (opcode & 0x00800000) >> 23; + I = (opcode & 0x00400000) >> 22; + W = (opcode & 0x00200000) >> 21; + L = (opcode & 0x00100000) >> 20; + S = (opcode & 0x00000040) >> 6; + H = (opcode & 0x00000020) >> 5; + + /* target register */ + Rd = (opcode & 0xf000) >> 12; + + /* base register */ + Rn = (opcode & 0xf0000) >> 16; + + instruction->info.load_store.Rd = Rd; + instruction->info.load_store.Rn = Rn; + instruction->info.load_store.U = U; + + /* determine instruction type and suffix */ + if (S) {/* signed */ + if (L) {/* load */ + if (H) { + operation = "LDR"; + instruction->type = ARM_LDRSH; + suffix = "SH"; + } else { + operation = "LDR"; + instruction->type = ARM_LDRSB; + suffix = "SB"; + } + } else {/* there are no signed stores, so this is used to encode double-register + *load/stores */ + suffix = "D"; + if (H) { + operation = "STR"; + instruction->type = ARM_STRD; + } else { + operation = "LDR"; + instruction->type = ARM_LDRD; + } + } + } else {/* unsigned */ + suffix = "H"; + if (L) {/* load */ + operation = "LDR"; + instruction->type = ARM_LDRH; + } else {/* store */ + operation = "STR"; + instruction->type = ARM_STRH; + } + } + + if (I) {/* Immediate offset/index (#+-)*/ + uint32_t offset_8 = ((opcode & 0xf00) >> 4) | (opcode & 0xf); + snprintf(offset, 32, "#%s0x%" PRIx32 "", (U) ? "" : "-", offset_8); + + instruction->info.load_store.offset_mode = 0; + instruction->info.load_store.offset.offset = offset_8; + } else {/* Register offset/index (+-) */ + uint8_t Rm; + Rm = (opcode & 0xf); + snprintf(offset, 32, "%sr%i", (U) ? "" : "-", Rm); + + instruction->info.load_store.offset_mode = 1; + instruction->info.load_store.offset.reg.Rm = Rm; + instruction->info.load_store.offset.reg.shift = 0x0; + instruction->info.load_store.offset.reg.shift_imm = 0x0; + } + + if (P == 1) { + if (W == 0) { /* offset */ + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s%s r%i, [r%i, %s]", + address, + opcode, + operation, + COND(opcode), + suffix, + Rd, + Rn, + offset); + + instruction->info.load_store.index_mode = 0; + } else {/* pre-indexed */ + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s%s r%i, [r%i, %s]!", + address, + opcode, + operation, + COND(opcode), + suffix, + Rd, + Rn, + offset); + + instruction->info.load_store.index_mode = 1; + } + } else {/* post-indexed */ + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s%s r%i, [r%i], %s", + address, + opcode, + operation, + COND(opcode), + suffix, + Rd, + Rn, + offset); + + instruction->info.load_store.index_mode = 2; + } + + return ERROR_OK; +} + +/* Load/store multiples instructions */ +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; + char *addressing_mode; + char *mnemonic; + char reg_list[69]; + char *reg_list_p; + int i; + int first_reg = 1; + + P = (opcode & 0x01000000) >> 24; + U = (opcode & 0x00800000) >> 23; + S = (opcode & 0x00400000) >> 22; + W = (opcode & 0x00200000) >> 21; + L = (opcode & 0x00100000) >> 20; + register_list = (opcode & 0xffff); + Rn = (opcode & 0xf0000) >> 16; + + instruction->info.load_store_multiple.Rn = Rn; + instruction->info.load_store_multiple.register_list = register_list; + instruction->info.load_store_multiple.S = S; + instruction->info.load_store_multiple.W = W; + + if (L) { + instruction->type = ARM_LDM; + mnemonic = "LDM"; + } else { + instruction->type = ARM_STM; + mnemonic = "STM"; + } + + if (P) { + if (U) { + instruction->info.load_store_multiple.addressing_mode = 1; + addressing_mode = "IB"; + } else { + instruction->info.load_store_multiple.addressing_mode = 3; + addressing_mode = "DB"; + } + } else { + if (U) { + instruction->info.load_store_multiple.addressing_mode = 0; + /* "IA" is the default in UAL syntax */ + addressing_mode = ""; + } else { + instruction->info.load_store_multiple.addressing_mode = 2; + addressing_mode = "DA"; + } + } + + reg_list_p = reg_list; + for (i = 0; i <= 15; i++) { + if ((register_list >> i) & 1) { + if (first_reg) { + first_reg = 0; + reg_list_p += snprintf(reg_list_p, + (reg_list + 69 - reg_list_p), + "r%i", + i); + } else + reg_list_p += snprintf(reg_list_p, + (reg_list + 69 - reg_list_p), + ", r%i", + i); + } + } + + 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 */ +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) { + /* Multiply (accumulate) */ + if ((opcode & 0x0f800000) == 0x00000000) { + uint8_t Rm, Rs, Rn, Rd, S; + Rm = opcode & 0xf; + Rs = (opcode & 0xf00) >> 8; + Rn = (opcode & 0xf000) >> 12; + Rd = (opcode & 0xf0000) >> 16; + S = (opcode & 0x00100000) >> 20; + + /* examine A bit (accumulate) */ + if (opcode & 0x00200000) { + instruction->type = ARM_MLA; + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tMLA%s%s r%i, r%i, r%i, r%i", + address, + opcode, + COND(opcode), + (S) ? "S" : "", + Rd, + Rm, + Rs, + Rn); + } else { + instruction->type = ARM_MUL; + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tMUL%s%s r%i, r%i, r%i", + address, + opcode, + COND(opcode), + (S) ? "S" : "", + Rd, + Rm, + Rs); + } + + return ERROR_OK; + } + + /* Multiply (accumulate) long */ + if ((opcode & 0x0f800000) == 0x00800000) { + char *mnemonic = NULL; + uint8_t Rm, Rs, RdHi, RdLow, S; + Rm = opcode & 0xf; + Rs = (opcode & 0xf00) >> 8; + RdHi = (opcode & 0xf000) >> 12; + RdLow = (opcode & 0xf0000) >> 16; + S = (opcode & 0x00100000) >> 20; + + switch ((opcode & 0x00600000) >> 21) { + case 0x0: + instruction->type = ARM_UMULL; + mnemonic = "UMULL"; + break; + case 0x1: + instruction->type = ARM_UMLAL; + mnemonic = "UMLAL"; + break; + case 0x2: + instruction->type = ARM_SMULL; + mnemonic = "SMULL"; + break; + case 0x3: + instruction->type = ARM_SMLAL; + mnemonic = "SMLAL"; + break; + } + + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s%s r%i, r%i, r%i, r%i", + address, + opcode, + mnemonic, + COND(opcode), + (S) ? "S" : "", + RdLow, + RdHi, + Rm, + Rs); + + return ERROR_OK; + } + + /* Swap/swap byte */ + if ((opcode & 0x0f800000) == 0x01000000) { + uint8_t Rm, Rd, Rn; + Rm = opcode & 0xf; + Rd = (opcode & 0xf000) >> 12; + Rn = (opcode & 0xf0000) >> 16; + + /* examine B flag */ + instruction->type = (opcode & 0x00400000) ? ARM_SWPB : ARM_SWP; + + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s r%i, r%i, [r%i]", + address, + opcode, + (opcode & 0x00400000) ? "SWPB" : "SWP", + COND(opcode), + Rd, + Rm, + Rn); + return ERROR_OK; + } + + } + + return evaluate_misc_load_store(opcode, address, instruction); +} + +static int evaluate_mrs_msr(uint32_t opcode, + uint32_t address, struct arm_instruction *instruction) +{ + int R = (opcode & 0x00400000) >> 22; + std::string PSR = (R) ? "SPSR" : "CPSR"; + + /* Move register to status register (MSR) */ + if (opcode & 0x00200000) { + instruction->type = ARM_MSR; + + /* immediate variant */ + if (opcode & 0x02000000) { + uint8_t immediate = (opcode & 0xff); + uint8_t rotate = (opcode & 0xf00); + + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tMSR%s %s_%s%s%s%s, 0x%8.8" PRIx32, + address, + opcode, + COND(opcode), + PSR.c_str(), + (opcode & 0x10000) ? "c" : "", + (opcode & 0x20000) ? "x" : "", + (opcode & 0x40000) ? "s" : "", + (opcode & 0x80000) ? "f" : "", + ror(immediate, (rotate * 2)) + ); + } else {/* register variant */ + uint8_t Rm = opcode & 0xf; + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tMSR%s %s_%s%s%s%s, r%i", + address, + opcode, + COND(opcode), + PSR.c_str(), + (opcode & 0x10000) ? "c" : "", + (opcode & 0x20000) ? "x" : "", + (opcode & 0x40000) ? "s" : "", + (opcode & 0x80000) ? "f" : "", + Rm + ); + } + + } else {/* Move status register to register (MRS) */ + uint8_t Rd; + + instruction->type = ARM_MRS; + Rd = (opcode & 0x0000f000) >> 12; + + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tMRS%s r%i, %s", + address, + opcode, + COND(opcode), + Rd, + PSR.c_str()); + } + + return ERROR_OK; +} + +/* Miscellaneous instructions */ +static int evaluate_misc_instr(uint32_t opcode, + uint32_t address, struct arm_instruction *instruction) +{ + /* MRS/MSR */ + if ((opcode & 0x000000f0) == 0x00000000) + evaluate_mrs_msr(opcode, address, instruction); + + /* BX */ + if ((opcode & 0x006000f0) == 0x00200010) { + uint8_t Rm; + instruction->type = ARM_BX; + Rm = opcode & 0xf; + + snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tBX%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; + } + + /* 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) { + uint8_t Rm, Rd; + instruction->type = ARM_CLZ; + Rm = opcode & 0xf; + Rd = (opcode & 0xf000) >> 12; + + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tCLZ%s r%i, r%i", + address, + opcode, + COND(opcode), + Rd, + Rm); + } + + /* BLX(2) */ + if ((opcode & 0x006000f0) == 0x00200030) { + uint8_t Rm; + instruction->type = ARM_BLX; + Rm = opcode & 0xf; + + snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tBLX%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; + } + + /* Enhanced DSP add/subtracts */ + if ((opcode & 0x0000000f0) == 0x00000050) { + uint8_t Rm, Rd, Rn; + char *mnemonic = NULL; + Rm = opcode & 0xf; + Rd = (opcode & 0xf000) >> 12; + Rn = (opcode & 0xf0000) >> 16; + + switch ((opcode & 0x00600000) >> 21) { + case 0x0: + instruction->type = ARM_QADD; + mnemonic = "QADD"; + break; + case 0x1: + instruction->type = ARM_QSUB; + mnemonic = "QSUB"; + break; + case 0x2: + instruction->type = ARM_QDADD; + mnemonic = "QDADD"; + break; + case 0x3: + instruction->type = ARM_QDSUB; + mnemonic = "QDSUB"; + break; + } + + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s r%i, r%i, r%i", + address, + opcode, + mnemonic, + COND(opcode), + Rd, + Rm, + Rn); + } + + /* Software breakpoints */ + if ((opcode & 0x0000000f0) == 0x00000070) { + uint32_t immediate; + instruction->type = ARM_BKPT; + immediate = ((opcode & 0x000fff00) >> 4) | (opcode & 0xf); + + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tBKPT 0x%4.4" PRIx32 "", + address, + opcode, + immediate); + } + + /* Enhanced DSP multiplies */ + if ((opcode & 0x000000090) == 0x00000080) { + int x = (opcode & 0x20) >> 5; + int y = (opcode & 0x40) >> 6; + + /* SMLA < x> */ + if ((opcode & 0x00600000) == 0x00000000) { + uint8_t Rd, Rm, Rs, Rn; + instruction->type = ARM_SMLAxy; + Rd = (opcode & 0xf0000) >> 16; + Rm = (opcode & 0xf); + Rs = (opcode & 0xf00) >> 8; + Rn = (opcode & 0xf000) >> 12; + + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tSMLA%s%s%s r%i, r%i, r%i, r%i", + address, + opcode, + (x) ? "T" : "B", + (y) ? "T" : "B", + COND(opcode), + Rd, + Rm, + Rs, + Rn); + } + + /* SMLAL < x> */ + if ((opcode & 0x00600000) == 0x00400000) { + uint8_t RdLow, RdHi, Rm, Rs; + instruction->type = ARM_SMLAxy; + RdHi = (opcode & 0xf0000) >> 16; + RdLow = (opcode & 0xf000) >> 12; + Rm = (opcode & 0xf); + Rs = (opcode & 0xf00) >> 8; + + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tSMLA%s%s%s r%i, r%i, r%i, r%i", + address, + opcode, + (x) ? "T" : "B", + (y) ? "T" : "B", + COND(opcode), + RdLow, + RdHi, + Rm, + Rs); + } + + /* SMLAW < y> */ + if (((opcode & 0x00600000) == 0x00100000) && (x == 0)) { + uint8_t Rd, Rm, Rs, Rn; + instruction->type = ARM_SMLAWy; + Rd = (opcode & 0xf0000) >> 16; + Rm = (opcode & 0xf); + Rs = (opcode & 0xf00) >> 8; + Rn = (opcode & 0xf000) >> 12; + + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tSMLAW%s%s r%i, r%i, r%i, r%i", + address, + opcode, + (y) ? "T" : "B", + COND(opcode), + Rd, + Rm, + Rs, + Rn); + } + + /* SMUL < x> */ + if ((opcode & 0x00600000) == 0x00300000) { + uint8_t Rd, Rm, Rs; + instruction->type = ARM_SMULxy; + Rd = (opcode & 0xf0000) >> 16; + Rm = (opcode & 0xf); + Rs = (opcode & 0xf00) >> 8; + + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tSMULW%s%s%s r%i, r%i, r%i", + address, + opcode, + (x) ? "T" : "B", + (y) ? "T" : "B", + COND(opcode), + Rd, + Rm, + Rs); + } + + /* SMULW < y> */ + if (((opcode & 0x00600000) == 0x00100000) && (x == 1)) { + uint8_t Rd, Rm, Rs; + instruction->type = ARM_SMULWy; + Rd = (opcode & 0xf0000) >> 16; + Rm = (opcode & 0xf); + Rs = (opcode & 0xf00) >> 8; + + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tSMULW%s%s r%i, r%i, r%i", + address, + opcode, + (y) ? "T" : "B", + COND(opcode), + Rd, + Rm, + Rs); + } + } + + return ERROR_OK; +} + +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; + char shifter_operand[32]; + + I = (opcode & 0x02000000) >> 25; + op = (opcode & 0x01e00000) >> 21; + S = (opcode & 0x00100000) >> 20; + + Rd = (opcode & 0xf000) >> 12; + Rn = (opcode & 0xf0000) >> 16; + + instruction->info.data_proc.Rd = Rd; + instruction->info.data_proc.Rn = Rn; + instruction->info.data_proc.S = S; + + switch (op) { + case 0x0: + instruction->type = ARM_AND; + mnemonic = "AND"; + break; + case 0x1: + instruction->type = ARM_EOR; + mnemonic = "EOR"; + break; + case 0x2: + instruction->type = ARM_SUB; + mnemonic = "SUB"; + break; + case 0x3: + instruction->type = ARM_RSB; + mnemonic = "RSB"; + break; + case 0x4: + instruction->type = ARM_ADD; + mnemonic = "ADD"; + break; + case 0x5: + instruction->type = ARM_ADC; + mnemonic = "ADC"; + break; + case 0x6: + instruction->type = ARM_SBC; + mnemonic = "SBC"; + break; + case 0x7: + instruction->type = ARM_RSC; + mnemonic = "RSC"; + break; + case 0x8: + instruction->type = ARM_TST; + mnemonic = "TST"; + break; + case 0x9: + instruction->type = ARM_TEQ; + mnemonic = "TEQ"; + break; + case 0xa: + instruction->type = ARM_CMP; + mnemonic = "CMP"; + break; + case 0xb: + instruction->type = ARM_CMN; + mnemonic = "CMN"; + break; + case 0xc: + instruction->type = ARM_ORR; + mnemonic = "ORR"; + break; + case 0xd: + instruction->type = ARM_MOV; + mnemonic = "MOV"; + break; + case 0xe: + instruction->type = ARM_BIC; + mnemonic = "BIC"; + break; + case 0xf: + instruction->type = ARM_MVN; + mnemonic = "MVN"; + break; + } + + if (I) {/* immediate shifter operand (#)*/ + uint8_t immed_8 = opcode & 0xff; + uint8_t rotate_imm = (opcode & 0xf00) >> 8; + uint32_t immediate; + + immediate = ror(immed_8, rotate_imm * 2); + + snprintf(shifter_operand, 32, "#0x%" PRIx32 "", immediate); + + instruction->info.data_proc.variant = 0; + instruction->info.data_proc.shifter_operand.immediate.immediate = immediate; + } else {/* register-based shifter operand */ + uint8_t shift, Rm; + shift = (opcode & 0x60) >> 5; + Rm = (opcode & 0xf); + + if ((opcode & 0x10) != 0x10) { /* Immediate shifts ("" or ", + *#") */ + uint8_t shift_imm; + shift_imm = (opcode & 0xf80) >> 7; + + instruction->info.data_proc.variant = 1; + instruction->info.data_proc.shifter_operand.immediate_shift.Rm = Rm; + instruction->info.data_proc.shifter_operand.immediate_shift.shift_imm = + shift_imm; + instruction->info.data_proc.shifter_operand.immediate_shift.shift = shift; + + /* LSR encodes a shift by 32 bit as 0x0 */ + if ((shift == 0x1) && (shift_imm == 0x0)) + shift_imm = 0x20; + + /* ASR encodes a shift by 32 bit as 0x0 */ + if ((shift == 0x2) && (shift_imm == 0x0)) + shift_imm = 0x20; + + /* ROR by 32 bit is actually a RRX */ + if ((shift == 0x3) && (shift_imm == 0x0)) + shift = 0x4; + + if ((shift_imm == 0x0) && (shift == 0x0)) + snprintf(shifter_operand, 32, "r%i", Rm); + else { + if (shift == 0x0) /* LSL */ + snprintf(shifter_operand, + 32, + "r%i, LSL #0x%x", + Rm, + shift_imm); + else if (shift == 0x1) /* LSR */ + snprintf(shifter_operand, + 32, + "r%i, LSR #0x%x", + Rm, + shift_imm); + else if (shift == 0x2) /* ASR */ + snprintf(shifter_operand, + 32, + "r%i, ASR #0x%x", + Rm, + shift_imm); + else if (shift == 0x3) /* ROR */ + snprintf(shifter_operand, + 32, + "r%i, ROR #0x%x", + Rm, + shift_imm); + else if (shift == 0x4) /* RRX */ + snprintf(shifter_operand, 32, "r%i, RRX", Rm); + } + } else {/* Register shifts (", ") */ + uint8_t Rs = (opcode & 0xf00) >> 8; + + instruction->info.data_proc.variant = 2; + instruction->info.data_proc.shifter_operand.register_shift.Rm = Rm; + instruction->info.data_proc.shifter_operand.register_shift.Rs = Rs; + instruction->info.data_proc.shifter_operand.register_shift.shift = shift; + + if (shift == 0x0) /* LSL */ + snprintf(shifter_operand, 32, "r%i, LSL r%i", Rm, Rs); + else if (shift == 0x1) /* LSR */ + snprintf(shifter_operand, 32, "r%i, LSR r%i", Rm, Rs); + else if (shift == 0x2) /* ASR */ + snprintf(shifter_operand, 32, "r%i, ASR r%i", Rm, Rs); + else if (shift == 0x3) /* ROR */ + snprintf(shifter_operand, 32, "r%i, ROR r%i", Rm, Rs); + } + } + + if ((op < 0x8) || (op == 0xc) || (op == 0xe)) { /* {}{S} , , + * */ + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s%s r%i, r%i, %s", + address, + opcode, + mnemonic, + COND(opcode), + (S) ? "S" : "", + Rd, + Rn, + shifter_operand); + } else if ((op == 0xd) || (op == 0xf)) { /* {}{S} , + * */ + if (opcode == 0xe1a00000) /* print MOV r0,r0 as NOP */ + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tNOP", + address, + opcode); + else + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s%s r%i, %s", + address, + opcode, + mnemonic, + COND(opcode), + (S) ? "S" : "", + Rd, + shifter_operand); + } else {/* {} , */ + snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s r%i, %s", + address, opcode, mnemonic, COND(opcode), + Rn, shifter_operand); + } + + return ERROR_OK; +} + +int arm_evaluate_opcode(uint32_t opcode, uint32_t address, + struct arm_instruction *instruction) +{ + /* clear fields, to avoid confusion */ + memset(instruction, 0, sizeof(struct arm_instruction)); + instruction->opcode = opcode; + instruction->instruction_size = 4; + + /* catch opcodes with condition field [31:28] = b1111 */ + if ((opcode & 0xf0000000) == 0xf0000000) { + /* Undefined instruction (or ARMv5E cache preload PLD) */ + if ((opcode & 0x08000000) == 0x00000000) + return evaluate_pld(opcode, address, instruction); + + /* Undefined instruction (or ARMv6+ SRS/RFE) */ + if ((opcode & 0x0e000000) == 0x08000000) + return evaluate_srs(opcode, address, instruction); + + /* Branch and branch with link and change to Thumb */ + if ((opcode & 0x0e000000) == 0x0a000000) + return evaluate_blx_imm(opcode, address, instruction); + + /* Extended coprocessor opcode space (ARMv5 and higher) + * Coprocessor load/store and double register transfers */ + if ((opcode & 0x0e000000) == 0x0c000000) + return evaluate_ldc_stc_mcrr_mrrc(opcode, address, instruction); + + /* Coprocessor data processing */ + if ((opcode & 0x0f000100) == 0x0c000000) + return evaluate_cdp_mcr_mrc(opcode, address, instruction); + + /* Coprocessor register transfers */ + if ((opcode & 0x0f000010) == 0x0c000010) + return evaluate_cdp_mcr_mrc(opcode, address, instruction); + + /* Undefined instruction */ + if ((opcode & 0x0f000000) == 0x0f000000) { + instruction->type = ARM_UNDEFINED_INSTRUCTION; + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tUNDEFINED INSTRUCTION", + address, + opcode); + return ERROR_OK; + } + } + + /* catch opcodes with [27:25] = b000 */ + if ((opcode & 0x0e000000) == 0x00000000) { + /* Multiplies, extra load/stores */ + if ((opcode & 0x00000090) == 0x00000090) + return evaluate_mul_and_extra_ld_st(opcode, address, instruction); + + /* Miscellaneous instructions */ + if ((opcode & 0x0f900000) == 0x01000000) + return evaluate_misc_instr(opcode, address, instruction); + + return evaluate_data_proc(opcode, address, instruction); + } + + /* catch opcodes with [27:25] = b001 */ + if ((opcode & 0x0e000000) == 0x02000000) { + /* Undefined instruction */ + if ((opcode & 0x0fb00000) == 0x03000000) { + instruction->type = ARM_UNDEFINED_INSTRUCTION; + snprintf(instruction->text, + 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tUNDEFINED INSTRUCTION", + address, + opcode); + return ERROR_OK; + } + + /* Move immediate to status register */ + if ((opcode & 0x0fb00000) == 0x03200000) + return evaluate_mrs_msr(opcode, address, instruction); + + return evaluate_data_proc(opcode, address, instruction); + + } + + /* catch opcodes with [27:25] = b010 */ + if ((opcode & 0x0e000000) == 0x04000000) { + /* Load/store immediate offset */ + return evaluate_load_store(opcode, address, instruction); + } + + /* catch opcodes with [27:25] = b011 */ + if ((opcode & 0x0e000000) == 0x06000000) { + /* 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 "\tUNDEF", + address, opcode); + return ERROR_OK; + } + + /* "media" instructions */ + return evaluate_media(opcode, address, instruction); + } + + /* catch opcodes with [27:25] = b100 */ + if ((opcode & 0x0e000000) == 0x08000000) { + /* Load/store multiple */ + return evaluate_ldm_stm(opcode, address, instruction); + } + + /* catch opcodes with [27:25] = b101 */ + if ((opcode & 0x0e000000) == 0x0a000000) { + /* Branch and branch with link */ + return evaluate_b_bl(opcode, address, instruction); + } + + /* catch opcodes with [27:25] = b110 */ + if ((opcode & 0x0e000000) == 0x0c000000) { + /* Coprocessor load/store and double register transfers */ + return evaluate_ldc_stc_mcrr_mrrc(opcode, address, instruction); + } + + /* catch opcodes with [27:25] = b111 */ + if ((opcode & 0x0e000000) == 0x0e000000) { + /* Software interrupt */ + if ((opcode & 0x0f000000) == 0x0f000000) + return evaluate_swi(opcode, address, instruction); + + /* Coprocessor data processing */ + if ((opcode & 0x0f000010) == 0x0e000000) + return evaluate_cdp_mcr_mrc(opcode, address, instruction); + + /* Coprocessor register transfers */ + if ((opcode & 0x0f000010) == 0x0e000010) + return evaluate_cdp_mcr_mrc(opcode, address, instruction); + } + + log << "ARM: should never reach this point (opcode= << " << std::hex << (unsigned) opcode << ")" << std::endl; + return -1; +} + +void arm_load_store_instr::evaluate() { + fail::Register * reg = fail::simulator.getCPU(0).getRegister(Rd); + value = fail::simulator.getCPU(0).getRegisterContent(reg); + uint32_t offs = 0; + + std::cout << " Value Register: r" << (int)(Rd) << " = 0x" << std::hex << value << std::endl; + + // Address holding register: + reg = fail::simulator.getCPU(0).getRegister(Rn); + address = fail::simulator.getCPU(0).getRegisterContent(reg); + + if(offset_mode == 0) { // immediate + offs = offset.offset; + } else { // (scaled) register + reg = fail::simulator.getCPU(0).getRegister(offset.reg.Rm); + // get scale register + uint32_t rm = fail::simulator.getCPU(0).getRegisterContent(reg); + // get shift value + uint8_t shimm = offset.reg.shift_imm; + + switch(offset.reg.shift) { + case 0: // LSL + offs = rm << shimm; + break; + case 1: // LSR + offs = rm >> shimm; + break; + case 2: // ASR + offs = (rm >> shimm) | (rm & (1 << 31)); + break; + case 3: // ROR + offs = ror(rm, shimm); + break; + case 4: // RRX + // This might be wrong! + // RRX rotates right 1 bit, the carry flag is moved to 31 + // and to original bit 0 is moved to the carry flag. + // Now we have a problem: The original carry flag is updated, + + // The offset is either: + offs = rm >> 1; + // OR: offs = (rm >> 1) | (1 << 31); // if CF = 1 + break; + default: break; + } + } + + // Apply the offset to the address register + if(index_mode == 1) { // pre indexed + // the calculated address was already written back into Rn + } else if (index_mode == 2) { // post indexed + // the address got the offset after the mem access + address = address - offs; + } else { // we have to apply the offset ourselfs + address = address + offs; + } + + + std::cout << " Address Register: r" << (int)(Rn) << " = 0x" << std::hex << address << std::endl; + +} + +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; + uint32_t target_address; + char *mnemonic = NULL; + + /* sign extend 11-bit offset */ + if (((opc == 0) || (opc == 2)) && (offset & 0x00000400)) + offset = 0xfffff800 | offset; + + target_address = address + 4 + (offset << 1); + + switch (opc) { + /* unconditional branch */ + case 0: + instruction->type = ARM_B; + mnemonic = "B"; + break; + /* BLX suffix */ + case 1: + instruction->type = ARM_BLX; + mnemonic = "BLX"; + target_address &= 0xfffffffc; + break; + /* BL/BLX prefix */ + case 2: + instruction->type = ARM_UNKNOWN_INSTUCTION; + mnemonic = "prefix"; + target_address = offset << 12; + break; + /* BL suffix */ + case 3: + instruction->type = ARM_BL; + mnemonic = "BL"; + break; + } + + /* TODO: deal correctly with dual opcode (prefixed) BL/BLX; + * 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, + "0x%8.8" PRIx32 " 0x%4.4x \t%s\t%#8.8" PRIx32, + address, opcode, mnemonic, target_address); + + instruction->info.b_bl_bx_blx.reg_operand = -1; + instruction->info.b_bl_bx_blx.target_address = target_address; + + return ERROR_OK; +} + +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; + uint8_t Rm_imm = (opcode >> 6) & 0x7; + uint32_t opc = opcode & (1 << 9); + uint32_t reg_imm = opcode & (1 << 10); + char *mnemonic; + + if (opc) { + instruction->type = ARM_SUB; + mnemonic = "SUBS"; + } else { + /* REVISIT: if reg_imm == 0, display as "MOVS" */ + instruction->type = ARM_ADD; + mnemonic = "ADDS"; + } + + instruction->info.data_proc.Rd = Rd; + instruction->info.data_proc.Rn = Rn; + instruction->info.data_proc.S = 1; + + if (reg_imm) { + instruction->info.data_proc.variant = 0;/*immediate*/ + instruction->info.data_proc.shifter_operand.immediate.immediate = Rm_imm; + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t%s\tr%i, r%i, #%d", + address, opcode, mnemonic, Rd, Rn, Rm_imm); + } else { + instruction->info.data_proc.variant = 1;/*immediate shift*/ + instruction->info.data_proc.shifter_operand.immediate_shift.Rm = Rm_imm; + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t%s\tr%i, r%i, r%i", + address, opcode, mnemonic, Rd, Rn, Rm_imm); + } + + return ERROR_OK; +} + +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; + uint8_t imm = (opcode >> 6) & 0x1f; + uint8_t opc = (opcode >> 11) & 0x3; + char *mnemonic = NULL; + + switch (opc) { + case 0: + instruction->type = ARM_MOV; + mnemonic = "LSLS"; + instruction->info.data_proc.shifter_operand.immediate_shift.shift = 0; + break; + case 1: + instruction->type = ARM_MOV; + mnemonic = "LSRS"; + instruction->info.data_proc.shifter_operand.immediate_shift.shift = 1; + break; + case 2: + instruction->type = ARM_MOV; + mnemonic = "ASRS"; + instruction->info.data_proc.shifter_operand.immediate_shift.shift = 2; + break; + } + + if ((imm == 0) && (opc != 0)) + imm = 32; + + instruction->info.data_proc.Rd = Rd; + instruction->info.data_proc.Rn = -1; + instruction->info.data_proc.S = 1; + + instruction->info.data_proc.variant = 1;/*immediate_shift*/ + instruction->info.data_proc.shifter_operand.immediate_shift.Rm = Rm; + instruction->info.data_proc.shifter_operand.immediate_shift.shift_imm = imm; + + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t%s\tr%i, r%i, #%#2.2x", + address, opcode, mnemonic, Rd, Rm, imm); + + return ERROR_OK; +} + +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; + uint32_t opc = (opcode >> 11) & 0x3; + char *mnemonic = NULL; + + instruction->info.data_proc.Rd = Rd; + instruction->info.data_proc.Rn = Rd; + instruction->info.data_proc.S = 1; + instruction->info.data_proc.variant = 0;/*immediate*/ + instruction->info.data_proc.shifter_operand.immediate.immediate = imm; + + switch (opc) { + case 0: + instruction->type = ARM_MOV; + mnemonic = "MOVS"; + instruction->info.data_proc.Rn = -1; + break; + case 1: + instruction->type = ARM_CMP; + mnemonic = "CMP"; + instruction->info.data_proc.Rd = -1; + break; + case 2: + instruction->type = ARM_ADD; + mnemonic = "ADDS"; + break; + case 3: + instruction->type = ARM_SUB; + mnemonic = "SUBS"; + break; + } + + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t%s\tr%i, #%#2.2x", + address, opcode, mnemonic, Rd, imm); + + return ERROR_OK; +} + +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; + bool nop = false; + + high_reg = (opcode & 0x0400) >> 10; + op = (opcode & 0x03C0) >> 6; + + Rd = (opcode & 0x0007); + Rm = (opcode & 0x0038) >> 3; + H1 = (opcode & 0x0080) >> 7; + H2 = (opcode & 0x0040) >> 6; + + instruction->info.data_proc.Rd = Rd; + instruction->info.data_proc.Rn = Rd; + instruction->info.data_proc.S = (!high_reg || (instruction->type == ARM_CMP)); + instruction->info.data_proc.variant = 1 /*immediate shift*/; + instruction->info.data_proc.shifter_operand.immediate_shift.Rm = Rm; + + if (high_reg) { + Rd |= H1 << 3; + Rm |= H2 << 3; + op >>= 2; + + switch (op) { + case 0x0: + instruction->type = ARM_ADD; + mnemonic = "ADD"; + break; + case 0x1: + instruction->type = ARM_CMP; + mnemonic = "CMP"; + break; + case 0x2: + instruction->type = ARM_MOV; + mnemonic = "MOV"; + if (Rd == Rm) + nop = true; + break; + case 0x3: + if ((opcode & 0x7) == 0x0) { + instruction->info.b_bl_bx_blx.reg_operand = Rm; + if (H1) { + instruction->type = ARM_BLX; + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 + " 0x%4.4x \tBLX\tr%i", + address, opcode, Rm); + } else { + instruction->type = ARM_BX; + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 + " 0x%4.4x \tBX\tr%i", + address, opcode, Rm); + } + } else { + instruction->type = ARM_UNDEFINED_INSTRUCTION; + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 + " 0x%4.4x \t" + "UNDEFINED INSTRUCTION", + address, opcode); + } + return ERROR_OK; + break; + } + } else { + switch (op) { + case 0x0: + instruction->type = ARM_AND; + mnemonic = "ANDS"; + break; + case 0x1: + instruction->type = ARM_EOR; + mnemonic = "EORS"; + break; + case 0x2: + instruction->type = ARM_MOV; + mnemonic = "LSLS"; + instruction->info.data_proc.variant = 2 /*register shift*/; + instruction->info.data_proc.shifter_operand.register_shift.shift = 0; + instruction->info.data_proc.shifter_operand.register_shift.Rm = Rd; + instruction->info.data_proc.shifter_operand.register_shift.Rs = Rm; + break; + case 0x3: + instruction->type = ARM_MOV; + mnemonic = "LSRS"; + instruction->info.data_proc.variant = 2 /*register shift*/; + instruction->info.data_proc.shifter_operand.register_shift.shift = 1; + instruction->info.data_proc.shifter_operand.register_shift.Rm = Rd; + instruction->info.data_proc.shifter_operand.register_shift.Rs = Rm; + break; + case 0x4: + instruction->type = ARM_MOV; + mnemonic = "ASRS"; + instruction->info.data_proc.variant = 2 /*register shift*/; + instruction->info.data_proc.shifter_operand.register_shift.shift = 2; + instruction->info.data_proc.shifter_operand.register_shift.Rm = Rd; + instruction->info.data_proc.shifter_operand.register_shift.Rs = Rm; + break; + case 0x5: + instruction->type = ARM_ADC; + mnemonic = "ADCS"; + break; + case 0x6: + instruction->type = ARM_SBC; + mnemonic = "SBCS"; + break; + case 0x7: + instruction->type = ARM_MOV; + mnemonic = "RORS"; + instruction->info.data_proc.variant = 2 /*register shift*/; + instruction->info.data_proc.shifter_operand.register_shift.shift = 3; + instruction->info.data_proc.shifter_operand.register_shift.Rm = Rd; + instruction->info.data_proc.shifter_operand.register_shift.Rs = Rm; + break; + case 0x8: + instruction->type = ARM_TST; + mnemonic = "TST"; + break; + case 0x9: + instruction->type = ARM_RSB; + mnemonic = "RSBS"; + instruction->info.data_proc.variant = 0 /*immediate*/; + instruction->info.data_proc.shifter_operand.immediate.immediate = 0; + instruction->info.data_proc.Rn = Rm; + break; + case 0xA: + instruction->type = ARM_CMP; + mnemonic = "CMP"; + break; + case 0xB: + instruction->type = ARM_CMN; + mnemonic = "CMN"; + break; + case 0xC: + instruction->type = ARM_ORR; + mnemonic = "ORRS"; + break; + case 0xD: + instruction->type = ARM_MUL; + mnemonic = "MULS"; + break; + case 0xE: + instruction->type = ARM_BIC; + mnemonic = "BICS"; + break; + case 0xF: + instruction->type = ARM_MVN; + mnemonic = "MVNS"; + break; + } + } + + if (nop) + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \tNOP\t\t\t" + "; (%s r%i, r%i)", + address, opcode, mnemonic, Rd, Rm); + else + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t%s\tr%i, r%i", + address, opcode, mnemonic, Rd, Rm); + + return ERROR_OK; +} + +/* PC-relative data addressing is word-aligned even with Thumb */ +static inline uint32_t thumb_alignpc4(uint32_t addr) +{ + return (addr + 4) & ~3; +} + +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; + + instruction->type = ARM_LDR; + immediate = opcode & 0x000000ff; + immediate *= 4; + + instruction->info.load_store.Rd = Rd; + instruction->info.load_store.Rn = 15 /*PC*/; + instruction->info.load_store.index_mode = 0; /*offset*/ + instruction->info.load_store.offset_mode = 0; /*immediate*/ + instruction->info.load_store.offset.offset = immediate; + + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t" + "LDR\tr%i, [pc, #%#" PRIx32 "]\t; %#8.8" PRIx32, + address, opcode, Rd, immediate, + thumb_alignpc4(address) + immediate); + + return ERROR_OK; +} + +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; + uint8_t Rm = (opcode >> 6) & 0x7; + uint8_t opc = (opcode >> 9) & 0x7; + char *mnemonic = NULL; + + switch (opc) { + case 0: + instruction->type = ARM_STR; + mnemonic = "STR"; + break; + case 1: + instruction->type = ARM_STRH; + mnemonic = "STRH"; + break; + case 2: + instruction->type = ARM_STRB; + mnemonic = "STRB"; + break; + case 3: + instruction->type = ARM_LDRSB; + mnemonic = "LDRSB"; + break; + case 4: + instruction->type = ARM_LDR; + mnemonic = "LDR"; + break; + case 5: + instruction->type = ARM_LDRH; + mnemonic = "LDRH"; + break; + case 6: + instruction->type = ARM_LDRB; + mnemonic = "LDRB"; + break; + case 7: + instruction->type = ARM_LDRSH; + mnemonic = "LDRSH"; + break; + } + + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t%s\tr%i, [r%i, r%i]", + address, opcode, mnemonic, Rd, Rn, Rm); + + instruction->info.load_store.Rd = Rd; + instruction->info.load_store.Rn = Rn; + instruction->info.load_store.index_mode = 0; /*offset*/ + instruction->info.load_store.offset_mode = 1; /*register*/ + instruction->info.load_store.offset.reg.Rm = Rm; + + return ERROR_OK; +} + +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; + uint8_t Rn = (opcode >> 3) & 0x7; + uint32_t L = opcode & (1 << 11); + uint32_t B = opcode & (1 << 12); + char *mnemonic; + char suffix = ' '; + uint32_t shift = 2; + + if (L) { + instruction->type = ARM_LDR; + mnemonic = "LDR"; + } else { + instruction->type = ARM_STR; + mnemonic = "STR"; + } + + if ((opcode&0xF000) == 0x8000) { + suffix = 'H'; + shift = 1; + } else if (B) { + suffix = 'B'; + shift = 0; + } + + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t%s%c\tr%i, [r%i, #%#" PRIx32 "]", + address, opcode, mnemonic, suffix, Rd, Rn, offset << shift); + + instruction->info.load_store.Rd = Rd; + instruction->info.load_store.Rn = Rn; + instruction->info.load_store.index_mode = 0; /*offset*/ + instruction->info.load_store.offset_mode = 0; /*immediate*/ + instruction->info.load_store.offset.offset = offset << shift; + + return ERROR_OK; +} + +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; + uint32_t L = opcode & (1 << 11); + char *mnemonic; + + if (L) { + instruction->type = ARM_LDR; + mnemonic = "LDR"; + } else { + instruction->type = ARM_STR; + mnemonic = "STR"; + } + + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t%s\tr%i, [SP, #%#" PRIx32 "]", + address, opcode, mnemonic, Rd, offset*4); + + instruction->info.load_store.Rd = Rd; + instruction->info.load_store.Rn = 13 /*SP*/; + instruction->info.load_store.index_mode = 0; /*offset*/ + instruction->info.load_store.offset_mode = 0; /*immediate*/ + instruction->info.load_store.offset.offset = offset*4; + + return ERROR_OK; +} + +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); + const char *reg_name; + + instruction->type = ARM_ADD; + + if (SP) { + reg_name = "SP"; + Rn = 13; + } else { + reg_name = "PC"; + Rn = 15; + } + + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \tADD\tr%i, %s, #%#" PRIx32, + address, opcode, Rd, reg_name, imm * 4); + + instruction->info.data_proc.variant = 0 /* immediate */; + instruction->info.data_proc.Rd = Rd; + instruction->info.data_proc.Rn = Rn; + instruction->info.data_proc.shifter_operand.immediate.immediate = imm*4; + + return ERROR_OK; +} + +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); + char *mnemonic; + + + if (opc) { + instruction->type = ARM_SUB; + mnemonic = "SUB"; + } else { + instruction->type = ARM_ADD; + mnemonic = "ADD"; + } + + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t%s\tSP, #%#" PRIx32, + address, opcode, mnemonic, imm*4); + + instruction->info.data_proc.variant = 0 /* immediate */; + instruction->info.data_proc.Rd = 13 /*SP*/; + instruction->info.data_proc.Rn = 13 /*SP*/; + instruction->info.data_proc.shifter_operand.immediate.immediate = imm*4; + + return ERROR_OK; +} + +static int evaluate_breakpoint_thumb(uint16_t opcode, + uint32_t address, struct arm_instruction *instruction) +{ + uint32_t imm = opcode & 0xff; + + instruction->type = ARM_BKPT; + + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \tBKPT\t%#2.2" PRIx32 "", + address, opcode, imm); + + return ERROR_OK; +} + +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); + uint32_t R = opcode & (1 << 8); + uint8_t Rn = (opcode >> 8) & 7; + uint8_t addr_mode = 0 /* IA */; + char reg_names[40]; + char *reg_names_p; + char *mnemonic; + 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 = "!"; + + if (L) { + instruction->type = ARM_LDM; + mnemonic = "LDM"; + if (opcode & (1 << Rn)) + wback = ""; + } else { + instruction->type = ARM_STM; + mnemonic = "STM"; + } + snprintf(ptr_name, sizeof ptr_name, "r%i%s, ", Rn, wback); + } else {/* push/pop */ + Rn = 13;/* SP */ + if (L) { + instruction->type = ARM_LDM; + mnemonic = "POP"; + if (R) + reg_list |= (1 << 15) /*PC*/; + } else { + instruction->type = ARM_STM; + mnemonic = "PUSH"; + addr_mode = 3; /*DB*/ + if (R) + reg_list |= (1 << 14) /*LR*/; + } + } + + reg_names_p = reg_names; + for (i = 0; i <= 15; i++) { + if (reg_list & (1 << i)) + reg_names_p += snprintf(reg_names_p, + (reg_names + 40 - reg_names_p), + "r%i, ", + i); + } + if (reg_names_p > reg_names) + reg_names_p[-2] = '\0'; + else /* invalid op : no registers */ + reg_names[0] = '\0'; + + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t%s\t%s{%s}", + address, opcode, mnemonic, ptr_name, reg_names); + + instruction->info.load_store_multiple.register_list = reg_list; + instruction->info.load_store_multiple.Rn = Rn; + instruction->info.load_store_multiple.addressing_mode = addr_mode; + + return ERROR_OK; +} + +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; + uint32_t target_address; + + if (cond == 0xf) { + instruction->type = ARM_SWI; + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \tSVC\t%#2.2" PRIx32, + address, opcode, offset); + return ERROR_OK; + } else if (cond == 0xe) { + instruction->type = ARM_UNDEFINED_INSTRUCTION; + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \tUNDEFINED INSTRUCTION", + address, opcode); + return ERROR_OK; + } + + /* sign extend 8-bit offset */ + if (offset & 0x00000080) + offset = 0xffffff00 | offset; + + target_address = address + 4 + (offset << 1); + + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \tB%s\t%#8.8" PRIx32, + address, opcode, + arm_condition_strings[cond], target_address); + + instruction->type = ARM_B; + instruction->info.b_bl_bx_blx.reg_operand = -1; + instruction->info.b_bl_bx_blx.target_address = target_address; + + return ERROR_OK; +} + +static int evaluate_cb_thumb(uint16_t opcode, uint32_t address, + struct arm_instruction *instruction) +{ + unsigned offset; + + /* added in Thumb2 */ + offset = (opcode >> 3) & 0x1f; + offset |= (opcode & 0x0200) >> 4; + + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \tCB%sZ\tr%d, %#8.8" PRIx32, + address, opcode, + (opcode & 0x0800) ? "N" : "", + opcode & 0x7, address + 4 + (offset << 1)); + + return ERROR_OK; +} + +static int evaluate_extend_thumb(uint16_t opcode, uint32_t address, + struct arm_instruction *instruction) +{ + /* added in ARMv6 */ + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t%cXT%c\tr%d, r%d", + address, opcode, + (opcode & 0x0080) ? 'U' : 'S', + (opcode & 0x0040) ? 'B' : 'H', + opcode & 0x7, (opcode >> 3) & 0x7); + + return ERROR_OK; +} + +static int evaluate_cps_thumb(uint16_t opcode, uint32_t address, + struct arm_instruction *instruction) +{ + /* added in ARMv6 */ + if ((opcode & 0x0ff0) == 0x0650) + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \tSETEND %s", + address, opcode, + (opcode & 0x80) ? "BE" : "LE"); + else /* ASSUME (opcode & 0x0fe0) == 0x0660 */ + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \tCPSI%c\t%s%s%s", + address, opcode, + (opcode & 0x0010) ? 'D' : 'E', + (opcode & 0x0004) ? "A" : "", + (opcode & 0x0002) ? "I" : "", + (opcode & 0x0001) ? "F" : ""); + + return ERROR_OK; +} + +static int evaluate_byterev_thumb(uint16_t opcode, uint32_t address, + struct arm_instruction *instruction) +{ + char *suffix; + + /* added in ARMv6 */ + switch ((opcode >> 6) & 3) { + case 0: + suffix = ""; + break; + case 1: + suffix = "16"; + break; + default: + suffix = "SH"; + break; + } + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \tREV%s\tr%d, r%d", + address, opcode, suffix, + opcode & 0x7, (opcode >> 3) & 0x7); + + return ERROR_OK; +} + +static int evaluate_hint_thumb(uint16_t opcode, uint32_t address, + struct arm_instruction *instruction) +{ + char *hint; + + switch ((opcode >> 4) & 0x0f) { + case 0: + hint = "NOP"; + break; + case 1: + hint = "YIELD"; + break; + case 2: + hint = "WFE"; + break; + case 3: + hint = "WFI"; + break; + case 4: + hint = "SEV"; + break; + default: + hint = "HINT (UNRECOGNIZED)"; + break; + } + + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t%s", + address, opcode, hint); + + return ERROR_OK; +} + +static int evaluate_ifthen_thumb(uint16_t opcode, uint32_t address, + struct arm_instruction *instruction) +{ + unsigned cond = (opcode >> 4) & 0x0f; + std::string x = "", y = "", z = ""; + + if (opcode & 0x01) + z = (opcode & 0x02) ? "T" : "E"; + if (opcode & 0x03) + y = (opcode & 0x04) ? "T" : "E"; + if (opcode & 0x07) + x = (opcode & 0x08) ? "T" : "E"; + + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \tIT%s%s%s\t%s", + address, opcode, + x.c_str(), y.c_str(), z.c_str(), arm_condition_strings[cond]); + + /* NOTE: strictly speaking, the next 1-4 instructions should + * now be displayed with the relevant conditional suffix... + */ + + return ERROR_OK; +} + +int thumb_evaluate_opcode(uint16_t opcode, uint32_t address, struct arm_instruction *instruction) +{ + /* clear fields, to avoid confusion */ + memset(instruction, 0, sizeof(struct arm_instruction)); + instruction->opcode = opcode; + instruction->instruction_size = 2; + + if ((opcode & 0xe000) == 0x0000) { + /* add/substract register or immediate */ + if ((opcode & 0x1800) == 0x1800) + return evaluate_add_sub_thumb(opcode, address, instruction); + /* shift by immediate */ + else + return evaluate_shift_imm_thumb(opcode, address, instruction); + } + + /* Add/substract/compare/move immediate */ + if ((opcode & 0xe000) == 0x2000) + return evaluate_data_proc_imm_thumb(opcode, address, instruction); + + /* Data processing instructions */ + if ((opcode & 0xf800) == 0x4000) + return evaluate_data_proc_thumb(opcode, address, instruction); + + /* Load from literal pool */ + if ((opcode & 0xf800) == 0x4800) + return evaluate_load_literal_thumb(opcode, address, instruction); + + /* Load/Store register offset */ + if ((opcode & 0xf000) == 0x5000) + return evaluate_load_store_reg_thumb(opcode, address, instruction); + + /* Load/Store immediate offset */ + if (((opcode & 0xe000) == 0x6000) + || ((opcode & 0xf000) == 0x8000)) + return evaluate_load_store_imm_thumb(opcode, address, instruction); + + /* Load/Store from/to stack */ + if ((opcode & 0xf000) == 0x9000) + return evaluate_load_store_stack_thumb(opcode, address, instruction); + + /* Add to SP/PC */ + if ((opcode & 0xf000) == 0xa000) + return evaluate_add_sp_pc_thumb(opcode, address, instruction); + + /* Misc */ + if ((opcode & 0xf000) == 0xb000) { + switch ((opcode >> 8) & 0x0f) { + case 0x0: + return evaluate_adjust_stack_thumb(opcode, address, instruction); + case 0x1: + case 0x3: + case 0x9: + case 0xb: + return evaluate_cb_thumb(opcode, address, instruction); + case 0x2: + return evaluate_extend_thumb(opcode, address, instruction); + case 0x4: + case 0x5: + case 0xc: + case 0xd: + return evaluate_load_store_multiple_thumb(opcode, address, + instruction); + case 0x6: + return evaluate_cps_thumb(opcode, address, instruction); + case 0xa: + if ((opcode & 0x00c0) == 0x0080) + break; + return evaluate_byterev_thumb(opcode, address, instruction); + case 0xe: + return evaluate_breakpoint_thumb(opcode, address, instruction); + case 0xf: + if (opcode & 0x000f) + return evaluate_ifthen_thumb(opcode, address, + instruction); + else + return evaluate_hint_thumb(opcode, address, + instruction); + } + + instruction->type = ARM_UNDEFINED_INSTRUCTION; + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \tUNDEFINED INSTRUCTION", + address, opcode); + return ERROR_OK; + } + + /* Load/Store multiple */ + if ((opcode & 0xf000) == 0xc000) + return evaluate_load_store_multiple_thumb(opcode, address, instruction); + + /* Conditional branch + SWI */ + if ((opcode & 0xf000) == 0xd000) + return evaluate_cond_branch_thumb(opcode, address, instruction); + + if ((opcode & 0xe000) == 0xe000) { + /* Undefined instructions */ + if ((opcode & 0xf801) == 0xe801) { + instruction->type = ARM_UNDEFINED_INSTRUCTION; + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%8.8x\t" + "UNDEFINED INSTRUCTION", + address, opcode); + return ERROR_OK; + } else /* Branch to offset */ + return evaluate_b_bl_blx_thumb(opcode, address, instruction); + } + + log << "Thumb: should never reach this point (opcode=" << opcode << ")" << std::endl; + return -1; +} + +static int t2ev_b_bl(uint32_t opcode, uint32_t address, + struct arm_instruction *instruction, char *cp) +{ + unsigned offset; + unsigned b21 = 1 << 21; + unsigned b22 = 1 << 22; + + /* instead of combining two smaller 16-bit branch instructions, + * Thumb2 uses only one larger 32-bit instruction. + */ + offset = opcode & 0x7ff; + offset |= (opcode & 0x03ff0000) >> 5; + if (opcode & (1 << 26)) { + offset |= 0xff << 23; + if ((opcode & (1 << 11)) == 0) + b21 = 0; + if ((opcode & (1 << 13)) == 0) + b22 = 0; + } else { + if (opcode & (1 << 11)) + b21 = 0; + if (opcode & (1 << 13)) + b22 = 0; + } + offset |= b21; + offset |= b22; + + + address += 4; + address += offset << 1; + + instruction->type = (opcode & (1 << 14)) ? ARM_BL : ARM_B; + instruction->info.b_bl_bx_blx.reg_operand = -1; + instruction->info.b_bl_bx_blx.target_address = address; + sprintf(cp, "%s\t%#8.8" PRIx32, + (opcode & (1 << 14)) ? "BL" : "B.W", + address); + + return ERROR_OK; +} + +static int t2ev_cond_b(uint32_t opcode, uint32_t address, + struct arm_instruction *instruction, char *cp) +{ + unsigned offset; + unsigned b17 = 1 << 17; + unsigned b18 = 1 << 18; + unsigned cond = (opcode >> 22) & 0x0f; + + offset = opcode & 0x7ff; + offset |= (opcode & 0x003f0000) >> 5; + if (opcode & (1 << 26)) { + offset |= 0x1fff << 19; + if ((opcode & (1 << 11)) == 0) + b17 = 0; + if ((opcode & (1 << 13)) == 0) + b18 = 0; + } else { + if (opcode & (1 << 11)) + b17 = 0; + if (opcode & (1 << 13)) + b18 = 0; + } + offset |= b17; + offset |= b18; + + address += 4; + address += offset << 1; + + instruction->type = ARM_B; + instruction->info.b_bl_bx_blx.reg_operand = -1; + instruction->info.b_bl_bx_blx.target_address = address; + sprintf(cp, "B%s.W\t%#8.8" PRIx32, + arm_condition_strings[cond], + address); + + return ERROR_OK; +} + +static const char *special_name(int number) +{ + char *special = "(RESERVED)"; + + switch (number) { + case 0: + special = "apsr"; + break; + case 1: + special = "iapsr"; + break; + case 2: + special = "eapsr"; + break; + case 3: + special = "xpsr"; + break; + case 5: + special = "ipsr"; + break; + case 6: + special = "epsr"; + break; + case 7: + special = "iepsr"; + break; + case 8: + special = "msp"; + break; + case 9: + special = "psp"; + break; + case 16: + special = "primask"; + break; + case 17: + special = "basepri"; + break; + case 18: + special = "basepri_max"; + break; + case 19: + special = "faultmask"; + break; + case 20: + special = "control"; + break; + } + return special; +} + +static int t2ev_hint(uint32_t opcode, uint32_t address, + struct arm_instruction *instruction, char *cp) +{ + const char *mnemonic; + + if (opcode & 0x0700) { + instruction->type = ARM_UNDEFINED_INSTRUCTION; + strcpy(cp, "UNDEFINED"); + return ERROR_OK; + } + + if (opcode & 0x00f0) { + sprintf(cp, "DBG\t#%d", (int) opcode & 0xf); + return ERROR_OK; + } + + switch (opcode & 0x0f) { + case 0: + mnemonic = "NOP.W"; + break; + case 1: + mnemonic = "YIELD.W"; + break; + case 2: + mnemonic = "WFE.W"; + break; + case 3: + mnemonic = "WFI.W"; + break; + case 4: + mnemonic = "SEV.W"; + break; + default: + mnemonic = "HINT.W (UNRECOGNIZED)"; + break; + } + strcpy(cp, mnemonic); + return ERROR_OK; +} + +static int t2ev_misc(uint32_t opcode, uint32_t address, + 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; + case 4: + mnemonic = "DSB"; + break; + case 5: + mnemonic = "DMB"; + break; + case 6: + mnemonic = "ISB"; + break; + default: + return ERROR_COMMAND_SYNTAX_ERROR; + } + strcpy(cp, mnemonic); + return ERROR_OK; +} + +static int t2ev_b_misc(uint32_t opcode, uint32_t address, + struct arm_instruction *instruction, char *cp) +{ + /* permanently undefined */ + if ((opcode & 0x07f07000) == 0x07f02000) { + instruction->type = ARM_UNDEFINED_INSTRUCTION; + strcpy(cp, "UNDEFINED"); + return ERROR_OK; + } + + switch ((opcode >> 12) & 0x5) { + case 0x1: + case 0x5: + return t2ev_b_bl(opcode, address, instruction, cp); + case 0x4: + goto undef; + case 0: + if (((opcode >> 23) & 0x07) != 0x07) + return t2ev_cond_b(opcode, address, instruction, cp); + if (opcode & (1 << 26)) + goto undef; + break; + } + + switch ((opcode >> 20) & 0x7f) { + case 0x38: + case 0x39: + sprintf(cp, "MSR\t%s, r%d", special_name(opcode & 0xff), + (int) (opcode >> 16) & 0x0f); + return ERROR_OK; + case 0x3a: + 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, + special_name(opcode & 0xff)); + return ERROR_OK; + } + +undef: + return ERROR_COMMAND_SYNTAX_ERROR; +} + +static int t2ev_data_mod_immed(uint32_t opcode, uint32_t address, + struct arm_instruction *instruction, char *cp) +{ + char *mnemonic = NULL; + int rn = (opcode >> 16) & 0xf; + int rd = (opcode >> 8) & 0xf; + unsigned immed = opcode & 0xff; + unsigned func; + bool one = false; + char *suffix = ""; + char *suffix2 = ""; + + /* ARMv7-M: A5.3.2 Modified immediate constants */ + func = (opcode >> 11) & 0x0e; + if (immed & 0x80) + func |= 1; + if (opcode & (1 << 26)) + func |= 0x10; + + /* "Modified" immediates */ + switch (func >> 1) { + case 0: + break; + case 2: + immed <<= 8; + /* FALLTHROUGH */ + case 1: + immed += immed << 16; + break; + case 3: + immed += immed << 8; + immed += immed << 16; + break; + default: + immed |= 0x80; + immed = ror(immed, func); + } + + if (opcode & (1 << 20)) + suffix = "S"; + + switch ((opcode >> 21) & 0xf) { + case 0: + if (rd == 0xf) { + instruction->type = ARM_TST; + mnemonic = "TST"; + one = true; + suffix = ""; + rd = rn; + } else { + instruction->type = ARM_AND; + mnemonic = "AND"; + } + break; + case 1: + instruction->type = ARM_BIC; + mnemonic = "BIC"; + break; + case 2: + if (rn == 0xf) { + instruction->type = ARM_MOV; + mnemonic = "MOV"; + one = true; + suffix2 = ".W"; + } else { + instruction->type = ARM_ORR; + mnemonic = "ORR"; + } + break; + case 3: + if (rn == 0xf) { + instruction->type = ARM_MVN; + mnemonic = "MVN"; + one = true; + } else { + /* instruction->type = ARM_ORN; */ + mnemonic = "ORN"; + } + break; + case 4: + if (rd == 0xf) { + instruction->type = ARM_TEQ; + mnemonic = "TEQ"; + one = true; + suffix = ""; + rd = rn; + } else { + instruction->type = ARM_EOR; + mnemonic = "EOR"; + } + break; + case 8: + if (rd == 0xf) { + instruction->type = ARM_CMN; + mnemonic = "CMN"; + one = true; + suffix = ""; + rd = rn; + } else { + instruction->type = ARM_ADD; + mnemonic = "ADD"; + suffix2 = ".W"; + } + break; + case 10: + instruction->type = ARM_ADC; + mnemonic = "ADC"; + suffix2 = ".W"; + break; + case 11: + instruction->type = ARM_SBC; + mnemonic = "SBC"; + break; + case 13: + if (rd == 0xf) { + instruction->type = ARM_CMP; + mnemonic = "CMP"; + one = true; + suffix = ""; + rd = rn; + } else { + instruction->type = ARM_SUB; + mnemonic = "SUB"; + } + suffix2 = ".W"; + break; + case 14: + instruction->type = ARM_RSB; + mnemonic = "RSB"; + suffix2 = ".W"; + break; + default: + return ERROR_COMMAND_SYNTAX_ERROR; + } + + if (one) + sprintf(cp, "%s%s\tr%d, #%d\t; %#8.8x", + mnemonic, suffix2, rd, immed, immed); + else + sprintf(cp, "%s%s%s\tr%d, r%d, #%d\t; %#8.8x", + mnemonic, suffix, suffix2, + rd, rn, immed, immed); + + return ERROR_OK; +} + +static int t2ev_data_immed(uint32_t opcode, uint32_t address, + struct arm_instruction *instruction, char *cp) +{ + char *mnemonic = NULL; + int rn = (opcode >> 16) & 0xf; + int rd = (opcode >> 8) & 0xf; + unsigned immed; + bool add = false; + bool is_signed = false; + + immed = (opcode & 0x0ff) | ((opcode & 0x7000) >> 4); + if (opcode & (1 << 26)) + immed |= (1 << 11); + + switch ((opcode >> 20) & 0x1f) { + case 0: + if (rn == 0xf) { + add = true; + goto do_adr; + } + mnemonic = "ADDW"; + break; + case 4: + immed |= (opcode >> 4) & 0xf000; + sprintf(cp, "MOVW\tr%d, #%d\t; %#3.3x", rd, immed, immed); + return ERROR_OK; + case 0x0a: + if (rn == 0xf) + goto do_adr; + mnemonic = "SUBW"; + break; + case 0x0c: + /* move constant to top 16 bits of register */ + immed |= (opcode >> 4) & 0xf000; + sprintf(cp, "MOVT\tr%d, #%d\t; %#4.4x", rd, immed, immed); + return ERROR_OK; + case 0x10: + case 0x12: + is_signed = true; + case 0x18: + case 0x1a: + /* signed/unsigned saturated add */ + immed = (opcode >> 6) & 0x03; + immed |= (opcode >> 10) & 0x1c; + sprintf(cp, "%sSAT\tr%d, #%d, r%d, %s #%d\t", + is_signed ? "S" : "U", + rd, (int) (opcode & 0x1f) + is_signed, rn, + (opcode & (1 << 21)) ? "ASR" : "LSL", + immed ? immed : 32); + return ERROR_OK; + case 0x14: + is_signed = true; + /* FALLTHROUGH */ + case 0x1c: + /* signed/unsigned bitfield extract */ + immed = (opcode >> 6) & 0x03; + immed |= (opcode >> 10) & 0x1c; + sprintf(cp, "%sBFX\tr%d, r%d, #%d, #%d\t", + is_signed ? "S" : "U", + rd, rn, immed, + (int) (opcode & 0x1f) + 1); + return ERROR_OK; + case 0x16: + immed = (opcode >> 6) & 0x03; + immed |= (opcode >> 10) & 0x1c; + if (rn == 0xf) /* bitfield clear */ + sprintf(cp, "BFC\tr%d, #%d, #%d\t", + rd, immed, + (int) (opcode & 0x1f) + 1 - immed); + else /* bitfield insert */ + sprintf(cp, "BFI\tr%d, r%d, #%d, #%d\t", + rd, rn, immed, + (int) (opcode & 0x1f) + 1 - immed); + return ERROR_OK; + default: + return ERROR_COMMAND_SYNTAX_ERROR; + } + + sprintf(cp, "%s\tr%d, r%d, #%d\t; %#3.3x", mnemonic, + rd, rn, immed, immed); + return ERROR_OK; + +do_adr: + address = thumb_alignpc4(address); + if (add) + address += immed; + else + address -= immed; + /* REVISIT "ADD/SUB Rd, PC, #const ; 0x..." might be better; + * not hiding the pc-relative stuff will sometimes be useful. + */ + sprintf(cp, "ADR.W\tr%d, %#8.8" PRIx32, rd, address); + return ERROR_OK; +} + +static int t2ev_store_single(uint32_t opcode, uint32_t address, + struct arm_instruction *instruction, char *cp) +{ + unsigned op = (opcode >> 20) & 0xf; + char *size = ""; + char *suffix = ""; + char *p1 = ""; + char *p2 = "]"; + unsigned immed; + unsigned rn = (opcode >> 16) & 0x0f; + unsigned rt = (opcode >> 12) & 0x0f; + + if (rn == 0xf) + return ERROR_COMMAND_SYNTAX_ERROR; + + instruction->type = ARM_STR; + if (opcode & 0x0800) + op |= 1; + switch (op) { + /* byte */ + case 0x8: + case 0x9: + size = "B"; + instruction->type = ARM_STRB; + goto imm12; + case 0x1: + size = "B"; + instruction->type = ARM_STRB; + goto imm8; + case 0x0: + size = "B"; + instruction->type = ARM_STRB; + break; + /* halfword */ + case 0xa: + case 0xb: + size = "H"; + instruction->type = ARM_STRH; + goto imm12; + case 0x3: + size = "H"; + instruction->type = ARM_STRH; + goto imm8; + case 0x2: + size = "H"; + instruction->type = ARM_STRH; + break; + /* word */ + case 0xc: + case 0xd: + goto imm12; + case 0x5: + goto imm8; + case 0x4: + break; + /* error */ + default: + return ERROR_COMMAND_SYNTAX_ERROR; + } + + sprintf(cp, "STR%s.W\tr%d, [r%d, r%d, LSL #%d]", + size, rt, rn, (int) opcode & 0x0f, + (int) (opcode >> 4) & 0x03); + instruction->info.load_store.Rn = rn; //MH + instruction->info.load_store.Rd = rt; //MH + instruction->info.load_store.offset_mode = 1; // scaled reg + instruction->info.load_store.offset.reg.Rm = (int)(opcode & 0x0f); + instruction->info.load_store.offset.reg.shift = 0; // LSL = 0 + instruction->info.load_store.offset.reg.shift_imm = (int)((opcode >> 4) & 0x03); + + return ERROR_OK; + +imm12: + immed = opcode & 0x0fff; + sprintf(cp, "STR%s.W\tr%d, [r%d, #%u]\t; %#3.3x", + size, rt, rn, immed, immed); + instruction->info.load_store.Rn = rn; //MH + instruction->info.load_store.Rd = rt; //MH + instruction->info.load_store.offset_mode = 0; // immediate + instruction->info.load_store.offset.offset = immed; +return ERROR_OK; + +imm8: + immed = opcode & 0x00ff; + instruction->info.load_store.Rn = rn; //MH + instruction->info.load_store.Rd = rt; //MH + instruction->info.load_store.offset_mode = 0; // immediate + instruction->info.load_store.offset.offset = immed; + + switch (opcode & 0x700) { + case 0x600: + suffix = "T"; + break; + case 0x000: + case 0x200: + return ERROR_COMMAND_SYNTAX_ERROR; + } + + /* two indexed modes will write back rn */ + if (opcode & 0x100) { + if (opcode & 0x400) { /* pre-indexed */ + p2 = "]!"; + instruction->info.load_store.index_mode = 1; // pre indexed + } else { /* post-indexed */ + p1 = "]"; + p2 = ""; + instruction->info.load_store.index_mode = 2; // post indexed + } + } + + sprintf(cp, "STR%s%s\tr%d, [r%d%s, #%s%u%s\t; %#2.2x", + size, suffix, rt, rn, p1, + (opcode & 0x200) ? "" : "-", + immed, p2, immed); + return ERROR_OK; +} + +static int t2ev_mul32(uint32_t opcode, uint32_t address, + struct arm_instruction *instruction, char *cp) +{ + int ra = (opcode >> 12) & 0xf; + + switch (opcode & 0x007000f0) { + case 0: + if (ra == 0xf) + sprintf(cp, "MUL\tr%d, r%d, r%d", + (int) (opcode >> 8) & 0xf, + (int) (opcode >> 16) & 0xf, + (int) (opcode >> 0) & 0xf); + else + sprintf(cp, "MLA\tr%d, r%d, r%d, r%d", + (int) (opcode >> 8) & 0xf, + (int) (opcode >> 16) & 0xf, + (int) (opcode >> 0) & 0xf, ra); + break; + case 0x10: + sprintf(cp, "MLS\tr%d, r%d, r%d, r%d", + (int) (opcode >> 8) & 0xf, + (int) (opcode >> 16) & 0xf, + (int) (opcode >> 0) & 0xf, ra); + break; + default: + return ERROR_COMMAND_SYNTAX_ERROR; + } + return ERROR_OK; +} + +static int t2ev_mul64_div(uint32_t opcode, uint32_t address, + struct arm_instruction *instruction, char *cp) +{ + int op = (opcode >> 4) & 0xf; + char *infix = "MUL"; + + op += (opcode >> 16) & 0x70; + switch (op) { + case 0x40: + case 0x60: + infix = "MLA"; + /* FALLTHROUGH */ + case 0: + case 0x20: + sprintf(cp, "%c%sL\tr%d, r%d, r%d, r%d", + (op & 0x20) ? 'U' : 'S', + infix, + (int) (opcode >> 12) & 0xf, + (int) (opcode >> 8) & 0xf, + (int) (opcode >> 16) & 0xf, + (int) (opcode >> 0) & 0xf); + break; + case 0x1f: + case 0x3f: + sprintf(cp, "%cDIV\tr%d, r%d, r%d", + (op & 0x20) ? 'U' : 'S', + (int) (opcode >> 8) & 0xf, + (int) (opcode >> 16) & 0xf, + (int) (opcode >> 0) & 0xf); + break; + default: + return ERROR_COMMAND_SYNTAX_ERROR; + } + + return ERROR_OK; +} + +static int t2ev_ldm_stm(uint32_t opcode, uint32_t address, + 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; + case 3: + if (rn == 13 && t) + sprintf(cp, "POP.W\t"); + else + sprintf(cp, "LDM.W\tr%d%s, ", rn, t ? "!" : ""); + break; + case 4: + if (rn == 13 && t) + sprintf(cp, "PUSH.W\t"); + else + sprintf(cp, "STMDB\tr%d%s, ", rn, t ? "!" : ""); + break; + case 5: + sprintf(cp, "LDMDB.W\tr%d%s, ", rn, t ? "!" : ""); + break; + default: + return ERROR_COMMAND_SYNTAX_ERROR; + } + + cp = strchr(cp, 0); + *cp++ = '{'; + for (t = 0; registers; t++, registers >>= 1) { + if ((registers & 1) == 0) + continue; + registers &= ~1; + sprintf(cp, "r%d%s", t, registers ? ", " : ""); + cp = strchr(cp, 0); + } + *cp++ = '}'; + *cp++ = 0; + + 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"; + instruction->type = ARM_STREX; + goto strex; + case 1: + mnemonic = "LDREX"; + instruction->type = ARM_LDREX; + goto ldrex; + case 2: + case 6: + case 8: + case 10: + case 12: + case 14: + mnemonic = "STRD"; + instruction->type = ARM_LDRD; + goto immediate; + case 3: + case 7: + case 9: + case 11: + case 13: + case 15: + mnemonic = "LDRD"; + instruction->type = ARM_LDRD; + if (rn == 15) + goto literal; + else + goto immediate; + case 4: + switch (op3) { + case 4: + mnemonic = "STREXB"; + instruction->type = ARM_STREXB; + break; + case 5: + mnemonic = "STREXH"; + instruction->type = ARM_STREXH; + break; + default: + return ERROR_COMMAND_SYNTAX_ERROR; + } + 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"; + instruction->type = ARM_LDREXB; + break; + case 5: + mnemonic = "LDREXH"; + instruction->type = ARM_LDREXH; + break; + default: + return ERROR_COMMAND_SYNTAX_ERROR; + } + imm = 0; + goto ldrex; + } + return ERROR_COMMAND_SYNTAX_ERROR; + +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, + struct arm_instruction *instruction, char *cp) +{ + int op = (opcode >> 21) & 0xf; + int rd = (opcode >> 8) & 0xf; + int rn = (opcode >> 16) & 0xf; + int type = (opcode >> 4) & 0x3; + int immed = (opcode >> 6) & 0x3; + char *mnemonic; + char *suffix = ""; + + immed |= (opcode >> 10) & 0x1c; + if (opcode & (1 << 20)) + suffix = "S"; + + switch (op) { + case 0: + if (rd == 0xf) { + if (!(opcode & (1 << 20))) + return ERROR_COMMAND_SYNTAX_ERROR; + instruction->type = ARM_TST; + mnemonic = "TST"; + suffix = ""; + goto two; + } + instruction->type = ARM_AND; + mnemonic = "AND"; + break; + case 1: + instruction->type = ARM_BIC; + mnemonic = "BIC"; + break; + case 2: + if (rn == 0xf) { + instruction->type = ARM_MOV; + switch (type) { + case 0: + if (immed == 0) { + sprintf(cp, "MOV%s.W\tr%d, r%d", + suffix, rd, + (int) (opcode & 0xf)); + return ERROR_OK; + } + mnemonic = "LSL"; + break; + case 1: + mnemonic = "LSR"; + break; + case 2: + mnemonic = "ASR"; + break; + default: + if (immed == 0) { + sprintf(cp, "RRX%s\tr%d, r%d", + suffix, rd, + (int) (opcode & 0xf)); + return ERROR_OK; + } + mnemonic = "ROR"; + break; + } + goto immediate; + } else { + instruction->type = ARM_ORR; + mnemonic = "ORR"; + } + break; + case 3: + if (rn == 0xf) { + instruction->type = ARM_MVN; + mnemonic = "MVN"; + rn = rd; + goto two; + } else { + /* instruction->type = ARM_ORN; */ + mnemonic = "ORN"; + } + break; + case 4: + if (rd == 0xf) { + if (!(opcode & (1 << 20))) + return ERROR_COMMAND_SYNTAX_ERROR; + instruction->type = ARM_TEQ; + mnemonic = "TEQ"; + suffix = ""; + goto two; + } + instruction->type = ARM_EOR; + mnemonic = "EOR"; + break; + case 8: + if (rd == 0xf) { + if (!(opcode & (1 << 20))) + return ERROR_COMMAND_SYNTAX_ERROR; + instruction->type = ARM_CMN; + mnemonic = "CMN"; + suffix = ""; + goto two; + } + instruction->type = ARM_ADD; + mnemonic = "ADD"; + break; + case 0xa: + instruction->type = ARM_ADC; + mnemonic = "ADC"; + break; + case 0xb: + instruction->type = ARM_SBC; + mnemonic = "SBC"; + break; + case 0xd: + if (rd == 0xf) { + if (!(opcode & (1 << 21))) + return ERROR_COMMAND_SYNTAX_ERROR; + instruction->type = ARM_CMP; + mnemonic = "CMP"; + suffix = ""; + goto two; + } + instruction->type = ARM_SUB; + mnemonic = "SUB"; + break; + case 0xe: + instruction->type = ARM_RSB; + mnemonic = "RSB"; + break; + default: + return ERROR_COMMAND_SYNTAX_ERROR; + } + + sprintf(cp, "%s%s.W\tr%d, r%d, r%d", + mnemonic, suffix, rd, rn, (int) (opcode & 0xf)); + +shift: + cp = strchr(cp, 0); + + switch (type) { + case 0: + if (immed == 0) + return ERROR_OK; + suffix = "LSL"; + 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"); + return ERROR_OK; + } + suffix = "ROR"; + break; + } + sprintf(cp, ", %s #%d", suffix, immed ? immed : 32); + return ERROR_OK; + +two: + sprintf(cp, "%s%s.W\tr%d, r%d", + mnemonic, suffix, rn, (int) (opcode & 0xf)); + goto shift; + +immediate: + sprintf(cp, "%s%s.W\tr%d, r%d, #%d", + mnemonic, suffix, rd, + (int) (opcode & 0xf), immed ? immed : 32); + return ERROR_OK; +} + +static int t2ev_data_reg(uint32_t opcode, uint32_t address, + struct arm_instruction *instruction, char *cp) +{ + char *mnemonic; + char *suffix = ""; + + if (((opcode >> 4) & 0xf) == 0) { + switch ((opcode >> 21) & 0x7) { + case 0: + mnemonic = "LSL"; + break; + case 1: + mnemonic = "LSR"; + break; + case 2: + mnemonic = "ASR"; + break; + case 3: + mnemonic = "ROR"; + break; + default: + return ERROR_COMMAND_SYNTAX_ERROR; + } + + instruction->type = ARM_MOV; + if (opcode & (1 << 20)) + suffix = "S"; + sprintf(cp, "%s%s.W\tr%d, r%d, r%d", + mnemonic, suffix, + (int) (opcode >> 8) & 0xf, + (int) (opcode >> 16) & 0xf, + (int) (opcode >> 0) & 0xf); + + } else if (opcode & (1 << 7)) { + switch ((opcode >> 20) & 0xf) { + case 0: + case 1: + case 4: + case 5: + switch ((opcode >> 4) & 0x3) { + case 1: + suffix = ", ROR #8"; + break; + case 2: + suffix = ", ROR #16"; + break; + case 3: + suffix = ", ROR #24"; + break; + } + sprintf(cp, "%cXT%c.W\tr%d, r%d%s", + (opcode & (1 << 24)) ? 'U' : 'S', + (opcode & (1 << 26)) ? 'B' : 'H', + (int) (opcode >> 8) & 0xf, + (int) (opcode >> 0) & 0xf, + suffix); + break; + case 8: + case 9: + case 0xa: + case 0xb: + if (opcode & (1 << 6)) + return ERROR_COMMAND_SYNTAX_ERROR; + if (((opcode >> 12) & 0xf) != 0xf) + return ERROR_COMMAND_SYNTAX_ERROR; + if (!(opcode & (1 << 20))) + return ERROR_COMMAND_SYNTAX_ERROR; + + switch (((opcode >> 19) & 0x04) + | ((opcode >> 4) & 0x3)) { + case 0: + mnemonic = "REV.W"; + break; + case 1: + mnemonic = "REV16.W"; + break; + case 2: + mnemonic = "RBIT"; + break; + case 3: + mnemonic = "REVSH.W"; + break; + case 4: + mnemonic = "CLZ"; + break; + default: + return ERROR_COMMAND_SYNTAX_ERROR; + } + sprintf(cp, "%s\tr%d, r%d", + mnemonic, + (int) (opcode >> 8) & 0xf, + (int) (opcode >> 0) & 0xf); + break; + default: + return ERROR_COMMAND_SYNTAX_ERROR; + } + } + + return ERROR_OK; +} + +static int t2ev_load_word(uint32_t opcode, uint32_t address, + struct arm_instruction *instruction, char *cp) +{ + int rn = (opcode >> 16) & 0xf; + int immed; + + instruction->type = ARM_LDR; + + if (rn == 0xf) { + immed = opcode & 0x0fff; + if ((opcode & (1 << 23)) == 0) + immed = -immed; + sprintf(cp, "LDR\tr%d, %#8.8" PRIx32, + (int) (opcode >> 12) & 0xf, + thumb_alignpc4(address) + immed); + //MH TODO pc relative + return ERROR_OK; + } + + if (opcode & (1 << 23)) { + immed = opcode & 0x0fff; + sprintf(cp, "LDR.W\tr%d, [r%d, #%d]\t; %#3.3x", + (int) (opcode >> 12) & 0xf, + rn, immed, immed); + + instruction->info.load_store.Rn = rn; + instruction->info.load_store.Rd = (int) (opcode >> 12) & 0xf; + instruction->info.load_store.offset.offset = immed; + return ERROR_OK; + } + + if (!(opcode & (0x3f << 6))) { + sprintf(cp, "LDR.W\tr%d, [r%d, r%d, LSL #%d]", + (int) (opcode >> 12) & 0xf, + rn, + (int) (opcode >> 0) & 0xf, + (int) (opcode >> 4) & 0x3); + instruction->info.load_store.Rd = (int) (opcode >> 12) & 0xf; + instruction->info.load_store.Rn = rn; + instruction->info.load_store.offset_mode = 1; + instruction->info.load_store.offset.reg.Rm = (int) (opcode >> 0) & 0xf; + instruction->info.load_store.offset.reg.shift = 0; // LSL + instruction->info.load_store.offset.reg.shift_imm = (int) (opcode >> 4) & 0x3; + return ERROR_OK; + } + + + if (((opcode >> 8) & 0xf) == 0xe) { + immed = opcode & 0x00ff; + + sprintf(cp, "LDRT\tr%d, [r%d, #%d]\t; %#2.2x", + (int) (opcode >> 12) & 0xf, + rn, immed, immed); + + instruction->info.load_store.Rn = rn; + instruction->info.load_store.Rd = (int) (opcode >> 12) & 0xf; + instruction->info.load_store.offset.offset = immed; + return ERROR_OK; + } + + if (((opcode >> 8) & 0xf) == 0xc || (opcode & 0x0900) == 0x0900) { + char *p1 = "]", *p2 = ""; + + if (!(opcode & 0x0500)) + return ERROR_COMMAND_SYNTAX_ERROR; + + immed = opcode & 0x00ff; + + /* two indexed modes will write back rn */ + if (opcode & 0x100) { + if (opcode & 0x400) { /* pre-indexed */ + p2 = "]!"; + instruction->info.load_store.index_mode = 1; + } else { /* post-indexed */ + p1 = "]"; + p2 = ""; + instruction->info.load_store.index_mode = 2; + } + } + + sprintf(cp, "LDR\tr%d, [r%d%s, #%s%u%s\t; %#2.2x", + (int) (opcode >> 12) & 0xf, + rn, p1, + (opcode & 0x200) ? "" : "-", + immed, p2, immed); + + instruction->info.load_store.Rd = (int) (opcode >> 12) & 0xf; + instruction->info.load_store.Rn = rn; + instruction->info.load_store.offset_mode = 0; + instruction->info.load_store.offset.offset = immed; + return ERROR_OK; + } + + return ERROR_COMMAND_SYNTAX_ERROR; +} + +static int t2ev_load_byte_hints(uint32_t opcode, uint32_t address, + struct arm_instruction *instruction, char *cp) +{ + int rn = (opcode >> 16) & 0xf; + int rt = (opcode >> 12) & 0xf; + int op2 = (opcode >> 6) & 0x3f; + unsigned immed; + std::string p1 = "", p2 = "]"; + char *mnemonic; + + switch ((opcode >> 23) & 0x3) { + case 0: + if ((rn & rt) == 0xf) { +pld_literal: + immed = opcode & 0xfff; + address = thumb_alignpc4(address); + if (opcode & (1 << 23)) + address += immed; + else + address -= immed; + sprintf(cp, "PLD\tr%d, %#8.8" PRIx32, + rt, address); + return ERROR_OK; + } + if (rn == 0x0f && rt != 0x0f) { +ldrb_literal: + immed = opcode & 0xfff; + address = thumb_alignpc4(address); + if (opcode & (1 << 23)) + address += immed; + else + address -= immed; + sprintf(cp, "LDRB\tr%d, %#8.8" PRIx32, + rt, address); + return ERROR_OK; + } + if (rn == 0x0f) + break; + if ((op2 & 0x3c) == 0x38) { + immed = opcode & 0xff; + sprintf(cp, "LDRBT\tr%d, [r%d, #%d]\t; %#2.2x", + rt, rn, immed, immed); + return ERROR_OK; + } + if ((op2 & 0x3c) == 0x30) { + if (rt == 0x0f) { + immed = opcode & 0xff; + immed = -immed; +preload_immediate: + p1 = (opcode & (1 << 21)) ? "W" : ""; + sprintf(cp, "PLD%s\t[r%d, #%d]\t; %#6.6x", + p1.c_str(), rn, immed, immed); + return ERROR_OK; + } + mnemonic = "LDRB"; +ldrxb_immediate_t3: + immed = opcode & 0xff; + if (!(opcode & 0x200)) + immed = -immed; + + /* two indexed modes will write back rn */ + if (opcode & 0x100) { + if (opcode & 0x400) /* pre-indexed */ + p2 = "]!"; + else { /* post-indexed */ + p1 = "]"; + p2 = ""; + } + } +ldrxb_immediate_t2: + sprintf(cp, "%s\tr%d, [r%d%s, #%d%s\t; %#8.8x", + mnemonic, rt, rn, p1.c_str(), + immed, p2.c_str(), immed); + return ERROR_OK; + } + if ((op2 & 0x24) == 0x24) { + mnemonic = "LDRB"; + goto ldrxb_immediate_t3; + } + if (op2 == 0) { + int rm = opcode & 0xf; + + if (rt == 0x0f) + sprintf(cp, "PLD\t"); + else + sprintf(cp, "LDRB.W\tr%d, ", rt); + immed = (opcode >> 4) & 0x3; + cp = strchr(cp, 0); + sprintf(cp, "[r%d, r%d, LSL #%d]", rn, rm, immed); + return ERROR_OK; + } + break; + case 1: + if ((rn & rt) == 0xf) + goto pld_literal; + if (rt == 0xf) { + immed = opcode & 0xfff; + goto preload_immediate; + } + if (rn == 0x0f) + goto ldrb_literal; + mnemonic = "LDRB.W"; + immed = opcode & 0xfff; + goto ldrxb_immediate_t2; + case 2: + if ((rn & rt) == 0xf) { + immed = opcode & 0xfff; + address = thumb_alignpc4(address); + if (opcode & (1 << 23)) + address += immed; + else + address -= immed; + sprintf(cp, "PLI\t%#8.8" PRIx32, address); + return ERROR_OK; + } + if (rn == 0xf && rt != 0xf) { +ldrsb_literal: + immed = opcode & 0xfff; + address = thumb_alignpc4(address); + if (opcode & (1 << 23)) + address += immed; + else + address -= immed; + sprintf(cp, "LDRSB\t%#8.8" PRIx32, address); + return ERROR_OK; + } + if (rn == 0xf) + break; + if ((op2 & 0x3c) == 0x38) { + immed = opcode & 0xff; + sprintf(cp, "LDRSBT\tr%d, [r%d, #%d]\t; %#2.2x", + rt, rn, immed, immed); + return ERROR_OK; + } + if ((op2 & 0x3c) == 0x30) { + if (rt == 0xf) { + immed = opcode & 0xff; + immed = -immed; /* pli */ + sprintf(cp, "PLI\t[r%d, #%d]\t; -%#2.2x", + rn, immed, -immed); + return ERROR_OK; + } + mnemonic = "LDRSB"; + goto ldrxb_immediate_t3; + } + if ((op2 & 0x24) == 0x24) { + mnemonic = "LDRSB"; + goto ldrxb_immediate_t3; + } + if (op2 == 0) { + int rm = opcode & 0xf; + + if (rt == 0x0f) + sprintf(cp, "PLI\t"); + else + sprintf(cp, "LDRSB.W\tr%d, ", rt); + immed = (opcode >> 4) & 0x3; + cp = strchr(cp, 0); + sprintf(cp, "[r%d, r%d, LSL #%d]", rn, rm, immed); + return ERROR_OK; + } + break; + case 3: + if (rt == 0xf) { + immed = opcode & 0xfff; + sprintf(cp, "PLI\t[r%d, #%d]\t; %#3.3x", + rn, immed, immed); + return ERROR_OK; + } + if (rn == 0xf) + goto ldrsb_literal; + immed = opcode & 0xfff; + mnemonic = "LDRSB"; + goto ldrxb_immediate_t2; + } + + return ERROR_COMMAND_SYNTAX_ERROR; +} + +static int t2ev_load_halfword(uint32_t opcode, uint32_t address, + struct arm_instruction *instruction, char *cp) +{ + int rn = (opcode >> 16) & 0xf; + int rt = (opcode >> 12) & 0xf; + int op2 = (opcode >> 6) & 0x3f; + char *sign = ""; + unsigned immed; + + if (rt == 0xf) { + sprintf(cp, "HINT (UNALLOCATED)"); + return ERROR_OK; + } + + if (opcode & (1 << 24)) + sign = "S"; + + if ((opcode & (1 << 23)) == 0) { + if (rn == 0xf) { +ldrh_literal: + immed = opcode & 0xfff; + address = thumb_alignpc4(address); + if (opcode & (1 << 23)) + address += immed; + else + address -= immed; + sprintf(cp, "LDR%sH\tr%d, %#8.8" PRIx32, + sign, rt, address); + return ERROR_OK; + } + if (op2 == 0) { + int rm = opcode & 0xf; + + immed = (opcode >> 4) & 0x3; + sprintf(cp, "LDR%sH.W\tr%d, [r%d, r%d, LSL #%d]", + sign, rt, rn, rm, immed); + return ERROR_OK; + } + if ((op2 & 0x3c) == 0x38) { + immed = opcode & 0xff; + sprintf(cp, "LDR%sHT\tr%d, [r%d, #%d]\t; %#2.2x", + sign, rt, rn, immed, immed); + return ERROR_OK; + } + if ((op2 & 0x3c) == 0x30 || (op2 & 0x24) == 0x24) { + char *p1 = "", *p2 = "]"; + + immed = opcode & 0xff; + if (!(opcode & 0x200)) + immed = -immed; + + /* two indexed modes will write back rn */ + if (opcode & 0x100) { + if (opcode & 0x400) /* pre-indexed */ + p2 = "]!"; + else { /* post-indexed */ + p1 = "]"; + p2 = ""; + } + } + sprintf(cp, "LDR%sH\tr%d, [r%d%s, #%d%s\t; %#8.8x", + sign, rt, rn, p1, immed, p2, immed); + return ERROR_OK; + } + } else { + if (rn == 0xf) + goto ldrh_literal; + + immed = opcode & 0xfff; + sprintf(cp, "LDR%sH%s\tr%d, [r%d, #%d]\t; %#6.6x", + sign, *sign ? "" : ".W", + rt, rn, immed, immed); + return ERROR_OK; + } + + return ERROR_COMMAND_SYNTAX_ERROR; +} + +/* + * REVISIT for Thumb2 instructions, instruction->type and friends aren't + * always set. That means eventual arm_simulate_step() support for Thumb2 + * will need work in this area. + */ +int thumb2_opcode(uint32_t address, uint32_t opcode, struct arm_instruction *instruction) +{ + int retval; + char *cp; + + /* clear fields, to avoid confusion */ + memset(instruction, 0, sizeof(struct arm_instruction)); + + switch (opcode & 0xf8000000) { + case 0xf8000000: + case 0xf0000000: + case 0xe8000000: + /* 32-bit instructions */ + instruction->instruction_size = 4; + instruction->opcode = opcode; + break; + default: + /* 16-bit: Thumb1 + IT + CBZ/CBNZ + ... */ + return thumb_evaluate_opcode(opcode & 0x0000FFFF, address, instruction); + } + + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%8.8" PRIx32 "\t", + address, opcode); + cp = strchr(instruction->text, 0); + retval = ERROR_FAIL; + + /* ARMv7-M: A5.3.1 Data processing (modified immediate) */ + if ((opcode & 0x1a008000) == 0x10000000) + retval = t2ev_data_mod_immed(opcode, address, instruction, cp); + + /* ARMv7-M: A5.3.3 Data processing (plain binary immediate) */ + else if ((opcode & 0x1a008000) == 0x12000000) + retval = t2ev_data_immed(opcode, address, instruction, cp); + + /* ARMv7-M: A5.3.4 Branches and miscellaneous control */ + else if ((opcode & 0x18008000) == 0x10008000) + retval = t2ev_b_misc(opcode, address, instruction, cp); + + /* ARMv7-M: A5.3.5 Load/store multiple */ + 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); + + /* ARMv7-M: A5.3.8 Load halfword, unallocated memory hints */ + else if ((opcode & 0x1e700000) == 0x18300000) + retval = t2ev_load_halfword(opcode, address, instruction, cp); + + /* ARMv7-M: A5.3.9 Load byte, memory hints */ + else if ((opcode & 0x1e700000) == 0x18100000) + retval = t2ev_load_byte_hints(opcode, address, instruction, cp); + + /* ARMv7-M: A5.3.10 Store single data item */ + else if ((opcode & 0x1f100000) == 0x18000000) + retval = t2ev_store_single(opcode, address, instruction, cp); + + /* ARMv7-M: A5.3.11 Data processing (shifted register) */ + else if ((opcode & 0x1e000000) == 0x0a000000) + retval = t2ev_data_shift(opcode, address, instruction, cp); + + /* ARMv7-M: A5.3.12 Data processing (register) + * and A5.3.13 Miscellaneous operations + */ + else if ((opcode & 0x1f000000) == 0x1a000000) + retval = t2ev_data_reg(opcode, address, instruction, cp); + + /* ARMv7-M: A5.3.14 Multiply, and multiply accumulate */ + else if ((opcode & 0x1f800000) == 0x1b000000) + retval = t2ev_mul32(opcode, address, instruction, cp); + + /* ARMv7-M: A5.3.15 Long multiply, long multiply accumulate, divide */ + else if ((opcode & 0x1f800000) == 0x1b800000) + retval = t2ev_mul64_div(opcode, address, instruction, cp); + + if (retval == ERROR_OK) + return retval; + + /* + * Thumb2 also supports coprocessor, ThumbEE, and DSP/Media (SIMD) + * instructions; not yet handled here. + */ + + if (retval == ERROR_COMMAND_SYNTAX_ERROR) { + instruction->type = ARM_UNDEFINED_INSTRUCTION; + strcpy(cp, "UNDEFINED OPCODE"); + return ERROR_OK; + } + + log << "Can't decode 32-bit Thumb2 yet (opcode=" << opcode << ")" << std::endl; + + strcpy(cp, "(32-bit Thumb2 ...)"); + return ERROR_OK; +} + +int arm_access_size(struct arm_instruction *instruction) +{ + if ((instruction->type == ARM_LDRB) + || (instruction->type == ARM_LDRBT) + || (instruction->type == ARM_LDRSB) + || (instruction->type == ARM_STRB) + || (instruction->type == ARM_STRBT)) + return 1; + else if ((instruction->type == ARM_LDRH) + || (instruction->type == ARM_LDRSH) + || (instruction->type == ARM_STRH)) + return 2; + else if ((instruction->type == ARM_LDR) + || (instruction->type == ARM_LDRT) + || (instruction->type == ARM_STR) + || (instruction->type == ARM_STRT)) + return 4; + else if ((instruction->type == ARM_LDRD) + || (instruction->type == ARM_STRD)) + return 8; + else { + log << "BUG: instruction type " << instruction->type << " isn't a load/store instruction" << std::endl; + return 0; + } +} + diff --git a/src/core/sal/arm/arm_disassembler.hpp b/src/core/sal/arm/arm_disassembler.hpp new file mode 100644 index 00000000..c9ebf64d --- /dev/null +++ b/src/core/sal/arm/arm_disassembler.hpp @@ -0,0 +1,226 @@ +/*************************************************************************** + * Copyright (C) 2006 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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. * + ***************************************************************************/ + +#ifndef ARM_DISASSEMBLER_H +#define ARM_DISASSEMBLER_H +#include + +enum arm_instruction_type { + ARM_UNKNOWN_INSTUCTION, + + /* Branch instructions */ + ARM_B, + ARM_BL, + ARM_BX, + ARM_BLX, + + /* Data processing instructions */ + ARM_AND, + ARM_EOR, + ARM_SUB, + ARM_RSB, + ARM_ADD, + ARM_ADC, + ARM_SBC, + ARM_RSC, + ARM_TST, + ARM_TEQ, + ARM_CMP, + ARM_CMN, + ARM_ORR, + ARM_MOV, + ARM_BIC, + ARM_MVN, + + /* Load/store instructions */ + ARM_LDR, + ARM_LDRB, + ARM_LDRT, + ARM_LDRBT, + + ARM_LDRH, + ARM_LDRSB, + ARM_LDRSH, + ARM_LDREX, + ARM_LDREXB, + ARM_LDREXH, + ARM_LDM, + + ARM_STR, + ARM_STRB, + ARM_STRT, + ARM_STRBT, + + ARM_STRH, + ARM_STREX, + ARM_STREXB, + ARM_STREXH, + ARM_STM, + + /* Status register access instructions */ + ARM_MRS, + ARM_MSR, + + /* Multiply instructions */ + ARM_MUL, + ARM_MLA, + ARM_SMULL, + ARM_SMLAL, + ARM_UMULL, + ARM_UMLAL, + + /* Miscellaneous instructions */ + ARM_CLZ, + + /* Exception generating instructions */ + ARM_BKPT, + ARM_SWI, + + /* Coprocessor instructions */ + ARM_CDP, + ARM_LDC, + ARM_STC, + ARM_MCR, + ARM_MRC, + + /* Semaphore instructions */ + ARM_SWP, + ARM_SWPB, + + /* Enhanced DSP extensions */ + ARM_MCRR, + ARM_MRRC, + ARM_PLD, + ARM_QADD, + ARM_QDADD, + ARM_QSUB, + ARM_QDSUB, + ARM_SMLAxy, + ARM_SMLALxy, + ARM_SMLAWy, + ARM_SMULxy, + ARM_SMULWy, + ARM_LDRD, + ARM_STRD, + + ARM_UNDEFINED_INSTRUCTION = 0xffffffff, +}; + +struct arm_b_bl_bx_blx_instr { + int reg_operand; + uint32_t target_address; +}; + +union arm_shifter_operand { + struct { + uint32_t immediate; + } immediate; + struct { + uint8_t Rm; + uint8_t shift; /* 0: LSL, 1: LSR, 2: ASR, 3: ROR, 4: RRX */ + uint8_t shift_imm; + } immediate_shift; + struct { + uint8_t Rm; + uint8_t shift; + uint8_t Rs; + } register_shift; +}; + +struct arm_data_proc_instr { + int variant; /* 0: immediate, 1: immediate_shift, 2: register_shift */ + uint8_t S; + uint8_t Rn; + uint8_t Rd; + union arm_shifter_operand shifter_operand; +}; + +struct arm_load_store_instr { + uint8_t Rd; + uint8_t Rn; + uint8_t U; + int index_mode; /* 0: offset, 1: pre-indexed, 2: post-indexed */ + int offset_mode; /* 0: immediate, 1: (scaled) register */ + union { + uint32_t offset; + struct { + uint8_t Rm; + uint8_t shift; /* 0: LSL, 1: LSR, 2: ASR, 3: ROR, 4: RRX */ + uint8_t shift_imm; + } reg; + } offset; + + uint32_t address; + uint32_t value; + uint8_t width; + void evaluate(void); +}; + +struct arm_load_store_multiple_instr { + uint8_t Rn; + uint32_t register_list; + uint8_t addressing_mode; /* 0: IA, 1: IB, 2: DA, 3: DB */ + uint8_t S; + uint8_t W; +}; + +struct arm_instruction { + enum arm_instruction_type type; + char text[128]; + uint32_t opcode; + + /* return value ... Thumb-2 sizes vary */ + unsigned instruction_size; + + union { + struct arm_b_bl_bx_blx_instr b_bl_bx_blx; + struct arm_data_proc_instr data_proc; + struct arm_load_store_instr load_store; + struct arm_load_store_multiple_instr load_store_multiple; + } info; + + bool isBranchInstruction(void) const { + return (type == ARM_B) || (type == ARM_BL) || (type == ARM_BX) || (type == ARM_BLX); + }; + + bool isLoadInstruction(void) const { + return (type >= ARM_LDR) && (type <= ARM_LDM); + } + + bool isStoreInstruction(void) const { + return (type >= ARM_STR) && (type <= ARM_STM); + } + + bool isMemoryAccess(void) const { + return isLoadInstruction() || isStoreInstruction(); + }; +}; + +int arm_evaluate_opcode(uint32_t opcode, uint32_t address, + struct arm_instruction *instruction); +int thumb_evaluate_opcode(uint16_t opcode, uint32_t address, + struct arm_instruction *instruction); +int thumb2_opcode(uint32_t address, uint32_t opcode, + struct arm_instruction *instruction); +int arm_access_size(struct arm_instruction *instruction); + +#define COND(opcode) (arm_condition_strings[(opcode & 0xf0000000) >> 28]) + +#endif /* ARM_DISASSEMBLER_H */ diff --git a/src/core/util/Disassembler.cc b/src/core/util/Disassembler.cc index 3645031e..dc77617a 100644 --- a/src/core/util/Disassembler.cc +++ b/src/core/util/Disassembler.cc @@ -53,7 +53,9 @@ namespace fail { } std::ostream& operator <<(std::ostream & os, const fail::Instruction & i) { - os << i.opcode << "\t" << i.instruction << "\t" << i.comment; +#ifndef __puma + os << std::hex << ((int)(i.address)) << "\t" << i.opcode << "\t" << i.instruction << "\t" << i.comment; +#endif return os; } @@ -63,7 +65,7 @@ namespace fail { // Code lines start with a leading whitespace! (hopefully in each objdump implementation!) if(line.size() > 0 && isspace(line[0])){ // a line looks like: 800156c:\tdd14 \tble.n 8001598 <_ZN2hw3hal7T32Term8PutBlockEPci+0x30> - boost::regex expr("\\s+([A-Fa-f0-9]+):((?:\\s+[A-Fa-f0-9]+)+)\\s+(.+?)(;.*)?$"); + boost::regex expr("\\s+([A-Fa-f0-9]+):\\t(.*?)\\t(.+?)(;.*)?$"); boost::smatch res; if(boost::regex_search(line, res, expr)){ std::string address = res[1]; @@ -71,22 +73,31 @@ namespace fail { ss << std::hex << address; address_t addr = 0; ss >> addr; + ss.clear(); + ss.str(""); + std::string opcode = res[2]; + // delete trailing/leading whitespaces boost::trim(opcode); + // delete inner whitespaces and merge nibbles + opcode.erase(std::remove(opcode.begin(), opcode.end(), ' '), opcode.end()); + ss << std::hex << opcode; + unsigned opc = 0; + ss >> opc; + std::string instruction = res[3]; boost::trim(instruction); std::string comment = res[4]; boost::trim(comment); - m_code.insert(std::make_pair(addr, Instruction(opcode, instruction, comment))); + m_code.insert(std::make_pair(addr, Instruction(addr, opc, instruction, comment))); } } #endif } static Instruction g_InstructionNotFound; - - const Instruction& Disassembler::disassemble(address_t address){ + const Instruction & Disassembler::disassemble(address_t address) const { InstructionMap_t::const_iterator it = m_code.find(address); if(it == m_code.end()){ return g_InstructionNotFound; diff --git a/src/core/util/Disassembler.hpp b/src/core/util/Disassembler.hpp index b15dca17..a8a60023 100644 --- a/src/core/util/Disassembler.hpp +++ b/src/core/util/Disassembler.hpp @@ -19,11 +19,12 @@ namespace fail { * @brief An Instruction represents an disassembled opcode */ struct Instruction { - std::string opcode; // TODO convert to integer, size? + address_t address; //!< The instruction address + regdata_t opcode; //!< The opcode itself std::string instruction; //!< The disassembled instruction std::string comment; //!< Comment (rest of line after ; ) - Instruction(std::string opcode = "", const std::string& instr = DISASSEMBLER::FAILED, const std::string& comment = "") - : opcode(opcode), instruction(instr), comment(comment) { }; + Instruction(address_t address = ADDR_INV, regdata_t opcode = 0, const std::string& instr = DISASSEMBLER::FAILED, const std::string& comment = "") + : address(address), opcode(opcode), instruction(instr), comment(comment) { }; }; //getAddress() << "\t" << it->getName().c_str() << "\t" << it->getSize() << std::endl; + m_log << "0x" << std::hex << it->getAddress() << "\t" << it->getName().c_str() << "\t" << it->getSize() << std::endl; } } diff --git a/src/experiments/kesorefs/experiment.cc b/src/experiments/kesorefs/experiment.cc index 3b7b9a08..1d39c8f2 100644 --- a/src/experiments/kesorefs/experiment.cc +++ b/src/experiments/kesorefs/experiment.cc @@ -43,7 +43,7 @@ unsigned KESOrefs::injectBitFlip(address_t data_address, unsigned bitpos){ unsigned value, injectedval; mm.getBytes(data_address, 4, (void*)&value); - injectedval = value ^ bitpos; + injectedval = value ^ (1<