Initial support for disassembling Thumb2 code.  This works only for
Cortex-M3 cores so far.  Eventually other cores will also need Thumb2
support ... but they don't yet support any kind of disassembly.

 - A new Thumb2 instruction decode routine is provided.

     * This has a different signature:  pass the target, not the
       instruction, so it can fetch a second halfword when needed.
       The instruction size is likewise returned to the caller.

     * All 16-bit instructions are decoded, calling the Thumb1
       decode routine.  That's been taught the new opcodes.

     * The 32-bit instructions are recognized but not yet decoded.

     * Start using the current "UAL" syntax in some cases.  "SWI" is
       renamed as "SVC"; "LDMIA" as "LDM"; "STMIA" as "STM".

 - Define a new "cortex_m3 disassemble addr count" command to give
   access to this disassembly.

Sanity checked against "objdump -d" output; a bunch of the new
instructions checked out fine.  (CBZ, CBNZ, WFI, IT, CPSIE, ...)
---
 doc/openocd.texi              |    5
 src/target/arm_disassembler.c |  237 +++++++++++++++++++++++++++++++++++++---
 src/target/arm_disassembler.h |    5
 src/target/cortex_m3.c        |   53 ++++++++
 4 files changed, 284 insertions(+), 16 deletions(-)

Initial support for disassembling Thumb2 code.  This works only for
Cortex-M3 cores so far.  Eventually other cores will also need Thumb2
support ... but they don't yet support any kind of disassembly.

 - A new Thumb2 instruction decode routine is provided.
 
     * This has a different signature:  pass the target, not the
       instruction, so it can fetch a second halfword when needed.  
       The instruction size is likewise returned to the caller.

     * All 16-bit instructions are decoded, calling the Thumb1
       decode routine.  That's been taught the new opcodes.
       
     * The 32-bit instructions are recognized but not yet decoded.
   
     * Start using the current "UAL" syntax in some cases.  "SWI" is
       renamed as "SVC"; "LDMIA" as "LDM"; "STMIA" as "STM".

 - Define a new "cortex_m3 disassemble addr count" command to give
   access to this disassembly.

Sanity checked against "objdump -d" output; a bunch of the new
instructions checked out fine.  (CBZ, CBNZ, WFI, IT, CPSIE, ...)
---
 doc/openocd.texi              |    5 
 src/target/arm_disassembler.c |  237 +++++++++++++++++++++++++++++++++++++---
 src/target/arm_disassembler.h |    5 
 src/target/cortex_m3.c        |   53 ++++++++
 4 files changed, 284 insertions(+), 16 deletions(-)

--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -5003,6 +5003,11 @@ If @var{value} is defined, first assigns
 @subsection Cortex-M3 specific commands
 @cindex Cortex-M3
 
+...@deffn Command {cortex_m3 disassemble} address count
+...@cindex disassemble
+Disassembles @var{count} Thumb2 instructions starting at @var{address}.
+...@end deffn
+
 @deffn Command {cortex_m3 maskisr} (@option{on}|@option{off})
 Control masking (disabling) interrupts during target step/resume.
 @end deffn
--- a/src/target/arm_disassembler.c
+++ b/src/target/arm_disassembler.c
@@ -21,6 +21,7 @@
 #include "config.h"
 #endif
 
+#include "target.h"
 #include "arm_disassembler.h"
 #include "log.h"
 
@@ -63,7 +64,9 @@ int evaluate_swi(uint32_t opcode, uint32
 {
 	instruction->type = ARM_SWI;
 
-	snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tSWI 0x%6.6" PRIx32 "", address, opcode, (opcode & 0xffffff));
+	snprintf(instruction->text, 128,
+			"0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tSVC 0x%6.6" PRIx32,
+			address, opcode, (opcode & 0xffffff));
 
 	return ERROR_OK;
 }
@@ -614,7 +617,8 @@ int evaluate_ldm_stm(uint32_t opcode, ui
 		if (U)
 		{
 			instruction->info.load_store_multiple.addressing_mode = 0;
-			addressing_mode = "IA";
+			/* "IA" is the default in UAL syntax */
+			addressing_mode = "";
 		}
 		else
 		{
@@ -1180,6 +1184,7 @@ int arm_evaluate_opcode(uint32_t opcode,
 	/* clear fields, to avoid confusion */
 	memset(instruction, 0, sizeof(arm_instruction_t));
 	instruction->opcode = opcode;
+	instruction->instruction_size = 4;
 
 	/* catch opcodes with condition field [31:28] = b1111 */
 	if ((opcode & 0xf0000000) == 0xf0000000)
@@ -1887,12 +1892,12 @@ int evaluate_load_store_multiple_thumb(u
 		if (L)
 		{
 			instruction->type = ARM_LDM;
-			mnemonic = "LDMIA";
+			mnemonic = "LDM";
 		}
 		else
 		{
 			instruction->type = ARM_STM;
-			mnemonic = "STMIA";
+			mnemonic = "STM";
 		}
 		snprintf(ptr_name,7,"r%i!, ",Rn);
 	}
@@ -1945,7 +1950,9 @@ int evaluate_cond_branch_thumb(uint16_t 
 	if (cond == 0xf)
 	{
 		instruction->type = ARM_SWI;
-		snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%4.4x\tSWI 0x%02" PRIx32 , address, opcode, offset);
+		snprintf(instruction->text, 128,
+				"0x%8.8" PRIx32 "\t0x%4.4x\tSVC 0x%02" PRIx32,
+				address, opcode, offset);
 		return ERROR_OK;
 	}
 	else if (cond == 0xe)
@@ -1971,11 +1978,137 @@ int evaluate_cond_branch_thumb(uint16_t 
 	return ERROR_OK;
 }
 
+static int evaluate_cb_thumb(uint16_t opcode, uint32_t address,
+		arm_instruction_t *instruction)
+{
+	unsigned offset;
+
+	offset = (opcode >> 3) & 0x1f;
+	offset |= (opcode & 0x0200) >> 4;
+
+	snprintf(instruction->text, 128,
+			"0x%8.8" PRIx32 "\t0x%4.4x\tCB%sZ r%d, 0x%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,
+		arm_instruction_t *instruction)
+{
+	snprintf(instruction->text, 128,
+			"0x%8.8" PRIx32 "\t0x%4.4x\t%cXT%c r%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,
+		arm_instruction_t *instruction)
+{
+	snprintf(instruction->text, 128,
+			"0x%8.8" PRIx32 "\t0x%4.4x\tCPSI%c %s%s",
+			address, opcode,
+			(opcode & 0x0010) ? 'D' : 'E',
+			(opcode & 0x0002) ? "I" : "",
+			(opcode & 0x0001) ? "F" : "");
+
+	return ERROR_OK;
+}
+
+static int evaluate_byterev_thumb(uint16_t opcode, uint32_t address,
+		arm_instruction_t *instruction)
+{
+	char *suffix;
+
+	switch (opcode & 0x00c0) {
+	case 0:
+		suffix = "";
+		break;
+	case 1:
+		suffix = "16";
+		break;
+	default:
+		suffix = "SH";
+		break;
+	}
+	snprintf(instruction->text, 128,
+			"0x%8.8" PRIx32 "\t0x%4.4x\tREV%s r%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,
+		arm_instruction_t *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 = "UNRECOGNIZED HINT";
+		break;
+	}
+
+	snprintf(instruction->text, 128,
+			"0x%8.8" PRIx32 "\t0x%4.4x\t%s",
+			address, opcode, hint);
+
+	return ERROR_OK;
+}
+
+static int evaluate_ifthen_thumb(uint16_t opcode, uint32_t address,
+		arm_instruction_t *instruction)
+{
+	unsigned cond = (opcode >> 4) & 0x0f;
+	char *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 "\t0x%4.4x\tIT%s%s%s %s",
+			address, opcode,
+			x, y, z, 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, arm_instruction_t *instruction)
 {
 	/* clear fields, to avoid confusion */
 	memset(instruction, 0, sizeof(arm_instruction_t));
 	instruction->opcode = opcode;
+	instruction->instruction_size = 2;
 
 	if ((opcode & 0xe000) == 0x0000)
 	{
@@ -2033,18 +2166,44 @@ int thumb_evaluate_opcode(uint16_t opcod
 	/* Misc */
 	if ((opcode & 0xf000) == 0xb000)
 	{
-		if ((opcode & 0x0f00) == 0x0000)
+		switch ((opcode >> 8) & 0x0f) {
+		case 0x0:
 			return evaluate_adjust_stack_thumb(opcode, address, instruction);
-		else if ((opcode & 0x0f00) == 0x0e00)
+		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);
-		else if ((opcode & 0x0600) == 0x0400) /* push pop */
-			return evaluate_load_store_multiple_thumb(opcode, address, instruction);
-		else
-		{
-			instruction->type = ARM_UNDEFINED_INSTRUCTION;
-			snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%4.4x\tUNDEFINED INSTRUCTION", address, opcode);
-			return ERROR_OK;
+		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 "\t0x%4.4x\tUNDEFINED INSTRUCTION",
+			address, opcode);
+		return ERROR_OK;
 	}
 
 	/* Load/Store multiple */
@@ -2078,6 +2237,56 @@ int thumb_evaluate_opcode(uint16_t opcod
 	return -1;
 }
 
+/*
+ * REVISIT for Thumb2 instructions, instruction->type isn't always being
+ * set.  That means eventual arm_simulate_step() support for Thumb2 will
+ * need work in this area.
+ */
+int thumb2_opcode(target_t *target, uint32_t address, arm_instruction_t *instruction)
+{
+	int retval;
+	uint16_t op;
+	uint32_t opcode;
+
+	/* clear low bit ... it's set on function pointers */
+	address &= ~1;
+
+	/* clear fields, to avoid confusion */
+	memset(instruction, 0, sizeof(arm_instruction_t));
+
+	/* read first halfword, see if this is the only one */
+	retval = target_read_u16(target, address, &op);
+	if (retval != ERROR_OK)
+		return retval;
+
+	switch (op & 0xf800) {
+	case 0xf800:
+	case 0xf000:
+	case 0xe800:
+		/* 32-bit instructions */
+		instruction->instruction_size = 4;
+		opcode = op << 16;
+		retval = target_read_u16(target, address + 2, &op);
+		if (retval != ERROR_OK)
+			return retval;
+		opcode |= op;
+		instruction->opcode = opcode;
+		break;
+	default:
+		/* 16-bit:  Thumb1 + IT + CBZ/CBNZ + ... */
+		return thumb_evaluate_opcode(op, address, instruction);
+	}
+
+	/* FIXME decode the 32-bit instructions */
+
+	LOG_DEBUG("Can't decode 32-bit Thumb2 yet (opcode=%08x)", opcode);
+
+	snprintf(instruction->text, 128,
+			"0x%8.8" PRIx32 "\t0x%8.8x\t... 32-bit Thumb2 ...",
+			address, opcode);
+	return ERROR_OK;
+}
+
 int arm_access_size(arm_instruction_t *instruction)
 {
 	if ((instruction->type == ARM_LDRB)
--- a/src/target/arm_disassembler.h
+++ b/src/target/arm_disassembler.h
@@ -185,6 +185,9 @@ typedef struct arm_instruction_s
 	char text[128];
 	uint32_t opcode;
 
+	/* return value ... Thumb-2 sizes vary */
+	unsigned instruction_size;
+
 	union {
 		arm_b_bl_bx_blx_instr_t b_bl_bx_blx;
 		arm_data_proc_instr_t data_proc;
@@ -196,6 +199,8 @@ typedef struct arm_instruction_s
 
 extern int arm_evaluate_opcode(uint32_t opcode, uint32_t address, arm_instruction_t *instruction);
 extern int thumb_evaluate_opcode(uint16_t opcode, uint32_t address, arm_instruction_t *instruction);
+extern int thumb2_opcode(target_t *target, uint32_t address,
+		arm_instruction_t *instruction);
 extern int arm_access_size(arm_instruction_t *instruction);
 
 #define COND(opcode) (arm_condition_strings[(opcode & 0xf0000000) >> 28])
--- a/src/target/cortex_m3.c
+++ b/src/target/cortex_m3.c
@@ -34,6 +34,7 @@
 #include "cortex_m3.h"
 #include "target_request.h"
 #include "target_type.h"
+#include "arm_disassembler.h"
 
 
 /* cli handling */
@@ -1643,6 +1644,47 @@ int cortex_m3_target_create(struct targe
 	return ERROR_OK;
 }
 
+/*
+ * REVISIT Thumb2 disassembly should work for all ARMv7 cores, as well
+ * as at least ARM-1156T2.  The interesting thing about Cortex-M is
+ * that *only* Thumb2 disassembly matters.  There are also some small
+ * additions to Thumb2 that are specific to ARMv7-M.
+ */
+static int
+handle_cortex_m3_disassemble_command(struct command_context_s *cmd_ctx,
+		char *cmd, char **args, int argc)
+{
+	int retval = ERROR_OK;
+	target_t *target = get_current_target(cmd_ctx);
+	uint32_t address;
+	unsigned long count;
+	arm_instruction_t cur_instruction;
+
+	if (argc != 2) {
+		command_print(cmd_ctx,
+			"usage: cortex_m3 disassemble <address> <count>");
+		return ERROR_OK;
+	}
+
+	errno = 0;
+	address = strtoul(args[0], NULL, 0);
+	if (errno)
+		return ERROR_FAIL;
+	count = strtoul(args[1], NULL, 0);
+	if (errno)
+		return ERROR_FAIL;
+
+	while (count--) {
+		retval = thumb2_opcode(target, address, &cur_instruction);
+		if (retval != ERROR_OK)
+			return retval;
+		command_print(cmd_ctx, "%s", cur_instruction.text);
+		address += cur_instruction.instruction_size;
+	}
+
+	return ERROR_OK;
+}
+
 int cortex_m3_register_commands(struct command_context_s *cmd_ctx)
 {
 	int retval;
@@ -1650,8 +1692,15 @@ int cortex_m3_register_commands(struct c
 
 	retval = armv7m_register_commands(cmd_ctx);
 
-	cortex_m3_cmd = register_command(cmd_ctx, NULL, "cortex_m3", NULL, COMMAND_ANY, "cortex_m3 specific commands");
-	register_command(cmd_ctx, cortex_m3_cmd, "maskisr", handle_cortex_m3_mask_interrupts_command, COMMAND_EXEC, "mask cortex_m3 interrupts ['on'|'off']");
+	cortex_m3_cmd = register_command(cmd_ctx, NULL, "cortex_m3",
+			NULL, COMMAND_ANY, "cortex_m3 specific commands");
+
+	register_command(cmd_ctx, cortex_m3_cmd, "disassemble",
+			handle_cortex_m3_disassemble_command, COMMAND_EXEC,
+			"disassemble Thumb2 instructions <address> <count>");
+	register_command(cmd_ctx, cortex_m3_cmd, "maskisr",
+			handle_cortex_m3_mask_interrupts_command, COMMAND_EXEC,
+			"mask cortex_m3 interrupts ['on'|'off']");
 
 	return retval;
 }
_______________________________________________
Openocd-development mailing list
[email protected]
https://lists.berlios.de/mailman/listinfo/openocd-development

Reply via email to