diff options
author | Evgeniy Didin <didin@synopsys.com> | 2020-03-16 20:38:00 +0300 |
---|---|---|
committer | Oleksij Rempel <linux@rempel-privat.de> | 2020-05-08 21:23:05 +0100 |
commit | da41bce3aee99f1fd2c661f874c90d20b4fefa04 (patch) | |
tree | f23a9cc8d2b4bdcf739cf4d11bafc251a4c712b0 /src/target | |
parent | c693508f7717d3e5cf118da38d4a5411c59cef1a (diff) | |
download | riscv-openocd-da41bce3aee99f1fd2c661f874c90d20b4fefa04.zip riscv-openocd-da41bce3aee99f1fd2c661f874c90d20b4fefa04.tar.gz riscv-openocd-da41bce3aee99f1fd2c661f874c90d20b4fefa04.tar.bz2 |
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 <didin@synopsys.com>
Reviewed-on: http://openocd.zylin.com/5641
Tested-by: jenkins
Reviewed-by: Oleksij Rempel <linux@rempel-privat.de>
Diffstat (limited to 'src/target')
-rw-r--r-- | src/target/arc.c | 158 | ||||
-rw-r--r-- | 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 5bc6134..bf74496 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 55a1ead..defa3fa 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]; |