Merge pull request #694 from riscv/trigger_hit

Look at trigger hit bits to see which trigger was hit.
This commit is contained in:
Tim Newsome 2022-05-02 09:40:07 -07:00 committed by GitHub
commit b6dddfacc0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 87 additions and 8 deletions

View File

@ -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);

View File

@ -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:

View File

@ -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;