From da41bce3aee99f1fd2c661f874c90d20b4fefa04 Mon Sep 17 00:00:00 2001 From: Evgeniy Didin Date: Mon, 16 Mar 2020 20:38:00 +0300 Subject: [PATCH] target/arc: introduce breakpoint functionality With this patch we introduce set/unset breakpoints routines and add/remove bp handlers. Currently soft breakpoints are only supported. Changes since v1: * Change if-statement in arc_remove_breakpoint * Squash changes from http://openocd.zylin.com/#/c/5641/ in this commit to fix build. Change-Id: Ib10ccdb02fd1606e4f407f012b1bee106a8ffccd Signed-off-by: Evgeniy Didin Reviewed-on: http://openocd.zylin.com/5641 Tested-by: jenkins Reviewed-by: Oleksij Rempel --- src/target/arc.c | 158 ++++++++++++++++++++++++++++++++++++++++++++++- src/target/arc.h | 6 ++ 2 files changed, 162 insertions(+), 2 deletions(-) diff --git a/src/target/arc.c b/src/target/arc.c index 5bc6134d0c..bf744962a2 100644 --- a/src/target/arc.c +++ b/src/target/arc.c @@ -1342,6 +1342,158 @@ int arc_read_instruction_u32(struct target *target, uint32_t address, return ERROR_OK; } +static int arc_set_breakpoint(struct target *target, + struct breakpoint *breakpoint) +{ + + if (breakpoint->set) { + LOG_WARNING("breakpoint already set"); + return ERROR_OK; + } + + if (breakpoint->type == BKPT_SOFT) { + LOG_DEBUG("bpid: %" PRIu32, breakpoint->unique_id); + + if (breakpoint->length == 4) { + uint32_t verify = 0xffffffff; + + CHECK_RETVAL(target_read_buffer(target, breakpoint->address, breakpoint->length, + breakpoint->orig_instr)); + + CHECK_RETVAL(arc_write_instruction_u32(target, breakpoint->address, + ARC_SDBBP_32)); + + CHECK_RETVAL(arc_read_instruction_u32(target, breakpoint->address, &verify)); + + if (verify != ARC_SDBBP_32) { + LOG_ERROR("Unable to set 32bit breakpoint at address @0x%" TARGET_PRIxADDR + " - check that memory is read/writable", breakpoint->address); + return ERROR_FAIL; + } + } else if (breakpoint->length == 2) { + uint16_t verify = 0xffff; + + CHECK_RETVAL(target_read_buffer(target, breakpoint->address, breakpoint->length, + breakpoint->orig_instr)); + CHECK_RETVAL(target_write_u16(target, breakpoint->address, ARC_SDBBP_16)); + + CHECK_RETVAL(target_read_u16(target, breakpoint->address, &verify)); + if (verify != ARC_SDBBP_16) { + LOG_ERROR("Unable to set 16bit breakpoint at address @0x%" TARGET_PRIxADDR + " - check that memory is read/writable", breakpoint->address); + return ERROR_FAIL; + } + } else { + LOG_ERROR("Invalid breakpoint length: target supports only 2 or 4"); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + + breakpoint->set = 64; /* Any nice value but 0 */ + } else if (breakpoint->type == BKPT_HARD) { + LOG_DEBUG("Hardware breakpoints are not supported yet!"); + return ERROR_FAIL; + } else { + LOG_DEBUG("ERROR: setting unknown breakpoint type"); + return ERROR_FAIL; + } + /* core instruction cache is now invalid, + * TODO: add cache invalidation function here (when implemented). */ + + return ERROR_OK; +} + +static int arc_unset_breakpoint(struct target *target, + struct breakpoint *breakpoint) +{ + int retval = ERROR_OK; + + if (!breakpoint->set) { + LOG_WARNING("breakpoint not set"); + return ERROR_OK; + } + + if (breakpoint->type == BKPT_SOFT) { + /* restore original instruction (kept in target endianness) */ + LOG_DEBUG("bpid: %" PRIu32, breakpoint->unique_id); + if (breakpoint->length == 4) { + uint32_t current_instr; + + /* check that user program has not modified breakpoint instruction */ + CHECK_RETVAL(arc_read_instruction_u32(target, breakpoint->address, ¤t_instr)); + + if (current_instr == ARC_SDBBP_32) { + retval = target_write_buffer(target, breakpoint->address, + breakpoint->length, breakpoint->orig_instr); + if (retval != ERROR_OK) + return retval; + } else { + LOG_WARNING("Software breakpoint @0x%" TARGET_PRIxADDR + " has been overwritten outside of debugger." + "Expected: @0x%" PRIx32 ", got: @0x%" PRIx32, + breakpoint->address, ARC_SDBBP_32, current_instr); + } + } else if (breakpoint->length == 2) { + uint16_t current_instr; + + /* check that user program has not modified breakpoint instruction */ + CHECK_RETVAL(target_read_u16(target, breakpoint->address, ¤t_instr)); + if (current_instr == ARC_SDBBP_16) { + retval = target_write_buffer(target, breakpoint->address, + breakpoint->length, breakpoint->orig_instr); + if (retval != ERROR_OK) + return retval; + } else { + LOG_WARNING("Software breakpoint @0x%" TARGET_PRIxADDR + " has been overwritten outside of debugger. " + "Expected: 0x%04" PRIx16 ", got: 0x%04" PRIx16, + breakpoint->address, ARC_SDBBP_16, current_instr); + } + } else { + LOG_ERROR("Invalid breakpoint length: target supports only 2 or 4"); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + breakpoint->set = 0; + + } else if (breakpoint->type == BKPT_HARD) { + LOG_WARNING("Hardware breakpoints are not supported yet!"); + return ERROR_FAIL; + } else { + LOG_DEBUG("ERROR: unsetting unknown breakpoint type"); + return ERROR_FAIL; + } + + /* core instruction cache is now invalid. + * TODO: Add cache invalidation function */ + + return retval; +} + + +static int arc_add_breakpoint(struct target *target, struct breakpoint *breakpoint) +{ + if (target->state == TARGET_HALTED) { + return arc_set_breakpoint(target, breakpoint); + + } else { + LOG_WARNING(" > core was not halted, please try again."); + return ERROR_TARGET_NOT_HALTED; + } +} + +static int arc_remove_breakpoint(struct target *target, + struct breakpoint *breakpoint) +{ + if (target->state == TARGET_HALTED) { + if (breakpoint->set) + CHECK_RETVAL(arc_unset_breakpoint(target, breakpoint)); + } else { + LOG_WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + return ERROR_OK; +} + /* Helper function which swiches core to single_step mode by * doing aux r/w operations. */ int arc_config_step(struct target *target, int enable_step) @@ -1443,6 +1595,8 @@ int arc_step(struct target *target, int current, target_addr_t address, return ERROR_OK; } + + /* ARC v2 target */ struct target_type arcv2_target = { .name = "arcv2", @@ -1472,10 +1626,10 @@ struct target_type arcv2_target = { .checksum_memory = NULL, .blank_check_memory = NULL, - .add_breakpoint = NULL, + .add_breakpoint = arc_add_breakpoint, .add_context_breakpoint = NULL, .add_hybrid_breakpoint = NULL, - .remove_breakpoint = NULL, + .remove_breakpoint = arc_remove_breakpoint, .add_watchpoint = NULL, .remove_watchpoint = NULL, .hit_watchpoint = NULL, diff --git a/src/target/arc.h b/src/target/arc.h index 55a1ead21f..defa3fa97d 100644 --- a/src/target/arc.h +++ b/src/target/arc.h @@ -54,6 +54,12 @@ /* Limit reg_type/reg_type_field name to 20 symbols */ #define REG_TYPE_MAX_NAME_LENGTH 20 +/* ARC 32bits opcodes */ +#define ARC_SDBBP_32 0x256F003F /* BRK */ + +/* ARC 16bits opcodes */ +#define ARC_SDBBP_16 0x7FFF /* BRK_S */ + struct arc_reg_bitfield { struct reg_data_type_bitfield bitfield; char name[REG_TYPE_MAX_NAME_LENGTH]; -- 2.30.2