diff --git a/src/target/breakpoints.h b/src/target/breakpoints.h index 0a5949594..fc5b50b18 100644 --- a/src/target/breakpoints.h +++ b/src/target/breakpoints.h @@ -56,7 +56,7 @@ struct watchpoint { bool is_set; unsigned int number; struct watchpoint *next; - int unique_id; + uint32_t unique_id; }; void breakpoint_clear_target(struct target *target); diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index bde85b5d4..f1dde7c23 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -1022,6 +1022,67 @@ int riscv_remove_watchpoint(struct target *target, return ERROR_OK; } +/** + * Look at the trigger hit bits to find out which trigger is the reason we're + * halted. Sets *unique_id to the unique ID of that trigger. If *unique_id is + * ~0, no match was found. + */ +static int riscv_hit_trigger_hit_bit(struct target *target, uint32_t *unique_id) +{ + RISCV_INFO(r); + + riscv_reg_t tselect; + if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK) + return ERROR_FAIL; + + *unique_id = ~0; + for (unsigned int i = 0; i < r->trigger_count; i++) { + if (r->trigger_unique_id[i] == -1) + continue; + + if (riscv_set_register(target, GDB_REGNO_TSELECT, i) != ERROR_OK) + return ERROR_FAIL; + + uint64_t tdata1; + if (riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK) + return ERROR_FAIL; + int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target))); + + uint64_t hit_mask = 0; + switch (type) { + case 1: + /* Doesn't support hit bit. */ + break; + case 2: + hit_mask = CSR_MCONTROL_HIT; + break; + case 6: + hit_mask = CSR_MCONTROL6_HIT; + break; + default: + LOG_DEBUG("trigger %d has unknown type %d", i, type); + continue; + } + + /* Note: If we ever use chained triggers, then this logic needs + * to be changed to ignore triggers that are not the last one in + * the chain. */ + if (tdata1 & hit_mask) { + LOG_DEBUG("Trigger %d (unique_id=%d) has hit bit set.", i, r->trigger_unique_id[i]); + if (riscv_set_register(target, GDB_REGNO_TDATA1, tdata1 & ~hit_mask) != ERROR_OK) + return ERROR_FAIL; + + *unique_id = r->trigger_unique_id[i]; + break; + } + } + + if (riscv_set_register(target, GDB_REGNO_TSELECT, tselect) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + /* Sets *hit_watchpoint to the first watchpoint identified as causing the * current halt. * @@ -1030,14 +1091,19 @@ int riscv_remove_watchpoint(struct target *target, * and new value. */ int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoint) { - struct watchpoint *wp = target->watchpoints; + RISCV_INFO(r); LOG_DEBUG("Current hartid = %d", riscv_current_hartid(target)); - /*TODO instead of disassembling the instruction that we think caused the - * trigger, check the hit bit of each watchpoint first. The hit bit is - * simpler and more reliable to check but as it is optional and relatively - * new, not all hardware will implement it */ + /* If we identified which trigger caused the halt earlier, then just use + * that. */ + for (struct watchpoint *wp = target->watchpoints; wp; wp = wp->next) { + if (wp->unique_id == r->trigger_hit) { + *hit_watchpoint = wp; + return ERROR_OK; + } + } + riscv_reg_t dpc; riscv_get_register(target, &dpc, GDB_REGNO_DPC); const uint8_t length = 4; @@ -1086,6 +1152,7 @@ int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoi return ERROR_FAIL; } + struct watchpoint *wp = target->watchpoints; while (wp) { /*TODO support length/mask */ if (wp->address == mem_addr) { @@ -1104,7 +1171,6 @@ int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoi return ERROR_FAIL; } - static int oldriscv_step(struct target *target, int current, uint32_t address, int handle_breakpoints) { @@ -1198,12 +1264,21 @@ int riscv_flush_registers(struct target *target) /* Convert: RISC-V hart's halt reason --> OpenOCD's generic debug reason */ int set_debug_reason(struct target *target, enum riscv_halt_reason halt_reason) { + RISCV_INFO(r); + r->trigger_hit = -1; switch (halt_reason) { case RISCV_HALT_BREAKPOINT: target->debug_reason = DBG_REASON_BREAKPOINT; break; case RISCV_HALT_TRIGGER: + if (riscv_hit_trigger_hit_bit(target, &r->trigger_hit) != ERROR_OK) + return ERROR_FAIL; target->debug_reason = DBG_REASON_WATCHPOINT; + /* Check if we hit a hardware breakpoint. */ + for (struct breakpoint *bp = target->breakpoints; bp; bp = bp->next) { + if (bp->unique_id == r->trigger_hit) + target->debug_reason = DBG_REASON_BREAKPOINT; + } break; case RISCV_HALT_INTERRUPT: case RISCV_HALT_GROUP: diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index f3b753038..59fdb38a9 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -124,6 +124,10 @@ typedef struct { * target controls, while otherwise only a single hart is controlled. */ int trigger_unique_id[RISCV_MAX_HWBPS]; + /* The unique id of the trigger that caused the most recent halt. If the + * most recent halt was not caused by a trigger, then this is -1. */ + uint32_t trigger_hit; + /* The number of entries in the debug buffer. */ int debug_buffer_size; @@ -216,7 +220,7 @@ typedef struct { struct reg_data_type_union vector_union; struct reg_data_type type_vector; - /* Set when trigger registers are changed by the user. This indicates we eed + /* Set when trigger registers are changed by the user. This indicates we need * to beware that we may hit a trigger that we didn't realize had been set. */ bool manual_hwbp_set;