Fix target not halting when GDB jumps to a hardware breakpoint
This issue affects riscv-0.11. It is caused by OpenOCD manually stepping over hardware breakpoints to resume or step after a halt. This is not necessary as GDB should remove and add breakpoints as required. At the moment OpenOCD still steps over hardware watchpoints manually as GDB needs to know which address triggered the watchpoint and OpenOCD does not currently provide this information. Tested on the freedom-e310-arty using the GDB regression suite. There is one regression which is a corner case caused by a GDB bug. If a breakpoint is set in GDB and then the executable file is discarded, GDB reverts to its default information about address sizes e.g. on the freedom-e310-arty, 0x20400000 becomes 0xffff20400000. As a result, GDB is unable to step over breakpoints set before the executable was discarded.
This commit is contained in:
parent
2d9904ada7
commit
e2717e4cfa
|
@ -1848,9 +1848,13 @@ static int handle_halt(struct target *target, bool announce)
|
|||
break;
|
||||
case DCSR_CAUSE_HWBP:
|
||||
target->debug_reason = DBG_REASON_WPTANDBKPT;
|
||||
/* If we halted because of a data trigger, gdb doesn't know to do
|
||||
* the disable-breakpoints-step-enable-breakpoints dance. */
|
||||
info->need_strict_step = true;
|
||||
/* If we halted because of a watchpoint trigger, gdb doesn't know
|
||||
* which watchpoint to step over. Setting need_strict_step to true
|
||||
* flags OpenOCD to disable triggers -> step -> enable triggers on
|
||||
* the next step or resume*/
|
||||
if (! riscv_breakpoint_hit(target, 0, info->dpc)) {
|
||||
info->need_strict_step = true;
|
||||
}
|
||||
break;
|
||||
case DCSR_CAUSE_DEBUGINT:
|
||||
target->debug_reason = DBG_REASON_DBGRQ;
|
||||
|
|
|
@ -1986,6 +1986,64 @@ int riscv_enumerate_triggers(struct target *target)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if dpc matches the address of a breakpoint set on hartid
|
||||
**/
|
||||
bool riscv_breakpoint_hit(struct target *target, int hartid, uint64_t dpc)
|
||||
{
|
||||
if (!riscv_hart_enabled(target, hartid))
|
||||
return false;
|
||||
|
||||
riscv_reg_t tselect;
|
||||
int result = riscv_get_register_on_hart(target, &tselect, hartid,
|
||||
GDB_REGNO_TSELECT);
|
||||
if (result != ERROR_OK)
|
||||
return false;
|
||||
|
||||
bool hit = false;
|
||||
|
||||
for (unsigned t = 0; t < RISCV_MAX_TRIGGERS; ++t) {
|
||||
riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, t);
|
||||
uint64_t tselect_rb;
|
||||
result = riscv_get_register_on_hart(target, &tselect_rb, hartid,
|
||||
GDB_REGNO_TSELECT);
|
||||
if (result != ERROR_OK)
|
||||
break;
|
||||
/* Mask off the top bit, which is used as tdrmode in old
|
||||
* implementations. */
|
||||
tselect_rb &= ~(1ULL << (riscv_xlen(target)-1));
|
||||
if (tselect_rb != t)
|
||||
break;
|
||||
uint64_t tdata1;
|
||||
result = riscv_get_register_on_hart(target, &tdata1, hartid,
|
||||
GDB_REGNO_TDATA1);
|
||||
if (result != ERROR_OK)
|
||||
break;
|
||||
|
||||
int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target)));
|
||||
if (!(type == 2 && (tdata1 & MCONTROL_EXECUTE)))
|
||||
continue;
|
||||
|
||||
uint64_t tdata2;
|
||||
result = riscv_get_register_on_hart(target, &tdata2, hartid,
|
||||
GDB_REGNO_TDATA2);
|
||||
if (result != ERROR_OK)
|
||||
break;
|
||||
|
||||
if (tdata2 == dpc) {
|
||||
LOG_DEBUG("breakpoint %d matches dpc", t);
|
||||
hit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// restore tselect
|
||||
riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, tselect);
|
||||
LOG_DEBUG("no breakpoint matches dpc");
|
||||
|
||||
return hit;
|
||||
}
|
||||
|
||||
const char *gdb_regno_name(enum gdb_regno regno)
|
||||
{
|
||||
static char buf[32];
|
||||
|
|
|
@ -253,6 +253,7 @@ int riscv_remove_breakpoint(struct target *target,
|
|||
int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint);
|
||||
int riscv_remove_watchpoint(struct target *target,
|
||||
struct watchpoint *watchpoint);
|
||||
bool riscv_breakpoint_hit(struct target *target, int hartid, uint64_t dpc);
|
||||
|
||||
int riscv_init_registers(struct target *target);
|
||||
|
||||
|
|
Loading…
Reference in New Issue