target/riscv: check `abstractcs.busy`

According to the RISC-V Debug Spec (1.0.0-rc1)[3.7 Abstract Commands]:
> While an abstract command is executing (busy in abstractcs is high), a
debugger must not change hartsel, and must not write 1 to haltreq,
resumereq, ackhavereset, setresethaltreq, or clrresethaltreq.

The patch ensures the rule is followed.

Change-Id: Id7d363d9fdeb365181b7058e0ceb0be0df39654f
Signed-off-by: Evgeniy Naydanov <evgeniy.naydanov@syntacore.com>
This commit is contained in:
Evgeniy Naydanov 2024-02-20 20:02:14 +03:00
parent 8319eee9e1
commit 34d6fe3676
1 changed files with 73 additions and 6 deletions

View File

@ -1932,6 +1932,26 @@ static int set_group(struct target *target, bool *supported, unsigned int group,
return ERROR_OK; return ERROR_OK;
} }
static int wait_for_idle_if_needed(struct target *target)
{
dm013_info_t *dm = get_dm(target);
if (!dm)
return ERROR_FAIL;
if (!dm->abstract_cmd_maybe_busy)
/* The previous abstract command ended correctly
* and busy was cleared. No need to do anything. */
return ERROR_OK;
/* The previous abstract command timed out and abstractcs.busy
* may have remained set. Wait for it to get cleared. */
uint32_t abstractcs;
int result = wait_for_idle(target, &abstractcs);
if (result != ERROR_OK)
return result;
LOG_DEBUG_REG(target, DM_ABSTRACTCS, abstractcs);
return ERROR_OK;
}
static int examine_dm(struct target *target) static int examine_dm(struct target *target)
{ {
dm013_info_t *dm = get_dm(target); dm013_info_t *dm = get_dm(target);
@ -2033,7 +2053,12 @@ static int examine_dm(struct target *target)
if (get_field(s, DM_DMSTATUS_ANYHAVERESET)) { if (get_field(s, DM_DMSTATUS_ANYHAVERESET)) {
dmcontrol = DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_ACKHAVERESET; dmcontrol = DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_ACKHAVERESET;
/* TODO: Check `abstractcs.busy` here. */ /* If `abstractcs.busy` is set, debugger should not
* change `hartsel`.
*/
result = wait_for_idle_if_needed(target);
if (result != ERROR_OK)
return result;
dmcontrol = set_dmcontrol_hartsel(dmcontrol, i); dmcontrol = set_dmcontrol_hartsel(dmcontrol, i);
result = dm_write(target, DM_DMCONTROL, dmcontrol); result = dm_write(target, DM_DMCONTROL, dmcontrol);
if (result != ERROR_OK) if (result != ERROR_OK)
@ -2808,8 +2833,14 @@ static int riscv013_get_hart_state(struct target *target, enum riscv_hart_state
* message that a reset happened, that the target is running, and then * message that a reset happened, that the target is running, and then
* that it is halted again once the request goes through. * that it is halted again once the request goes through.
*/ */
if (target->state == TARGET_HALTED) if (target->state == TARGET_HALTED) {
dmcontrol |= DM_DMCONTROL_HALTREQ; dmcontrol |= DM_DMCONTROL_HALTREQ;
/* `haltreq` should not be issued if `abstractcs.busy`
* is set. */
int result = wait_for_idle_if_needed(target);
if (result != ERROR_OK)
return result;
}
dm_write(target, DM_DMCONTROL, dmcontrol); dm_write(target, DM_DMCONTROL, dmcontrol);
} }
if (get_field(dmstatus, DM_DMSTATUS_ALLNONEXISTENT)) { if (get_field(dmstatus, DM_DMSTATUS_ALLNONEXISTENT)) {
@ -2930,11 +2961,24 @@ static int assert_reset(struct target *target)
/* Run the user-supplied script if there is one. */ /* Run the user-supplied script if there is one. */
target_handle_event(target, TARGET_EVENT_RESET_ASSERT); target_handle_event(target, TARGET_EVENT_RESET_ASSERT);
} else { } else {
dm013_info_t *dm = get_dm(target);
if (!dm)
return ERROR_FAIL;
uint32_t control = set_field(0, DM_DMCONTROL_DMACTIVE, 1); uint32_t control = set_field(0, DM_DMCONTROL_DMACTIVE, 1);
control = set_dmcontrol_hartsel(control, info->index); control = set_dmcontrol_hartsel(control, info->index);
control = set_field(control, DM_DMCONTROL_HALTREQ, control = set_field(control, DM_DMCONTROL_HALTREQ,
target->reset_halt ? 1 : 0); target->reset_halt ? 1 : 0);
control = set_field(control, DM_DMCONTROL_NDMRESET, 1); control = set_field(control, DM_DMCONTROL_NDMRESET, 1);
/* If `abstractcs.busy` is set, debugger should not
* change `hartsel` or set `haltreq`
*/
const bool hartsel_changed = (int)info->index != dm->current_hartid;
if (hartsel_changed || target->reset_halt) {
result = wait_for_idle_if_needed(target);
if (result != ERROR_OK)
return result;
}
result = dm_write(target, DM_DMCONTROL, control); result = dm_write(target, DM_DMCONTROL, control);
if (result != ERROR_OK) if (result != ERROR_OK)
return result; return result;
@ -2951,6 +2995,9 @@ static int assert_reset(struct target *target)
static int deassert_reset(struct target *target) static int deassert_reset(struct target *target)
{ {
RISCV013_INFO(info); RISCV013_INFO(info);
dm013_info_t *dm = get_dm(target);
if (!dm)
return ERROR_FAIL;
int result; int result;
select_dmi(target); select_dmi(target);
@ -2959,6 +3006,15 @@ static int deassert_reset(struct target *target)
control = set_field(control, DM_DMCONTROL_DMACTIVE, 1); control = set_field(control, DM_DMCONTROL_DMACTIVE, 1);
control = set_field(control, DM_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0); control = set_field(control, DM_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0);
control = set_dmcontrol_hartsel(control, info->index); control = set_dmcontrol_hartsel(control, info->index);
/* If `abstractcs.busy` is set, debugger should not
* change `hartsel`.
*/
const bool hartsel_changed = (int)info->index != dm->current_hartid;
if (hartsel_changed) {
result = wait_for_idle_if_needed(target);
if (result != ERROR_OK)
return result;
}
result = dm_write(target, DM_DMCONTROL, control); result = dm_write(target, DM_DMCONTROL, control);
if (result != ERROR_OK) if (result != ERROR_OK)
return result; return result;
@ -5013,6 +5069,11 @@ static int dm013_select_hart(struct target *target, int hart_index)
if (hart_index == dm->current_hartid) if (hart_index == dm->current_hartid)
return ERROR_OK; return ERROR_OK;
/* `hartsel` should not be changed if `abstractcs.busy` is set. */
int result = wait_for_idle_if_needed(target);
if (result != ERROR_OK)
return result;
uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE; uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE;
dmcontrol = set_dmcontrol_hartsel(dmcontrol, hart_index); dmcontrol = set_dmcontrol_hartsel(dmcontrol, hart_index);
if (dm_write(target, DM_DMCONTROL, dmcontrol) != ERROR_OK) { if (dm_write(target, DM_DMCONTROL, dmcontrol) != ERROR_OK) {
@ -5107,6 +5168,11 @@ static int riscv013_halt_go(struct target *target)
LOG_TARGET_DEBUG(target, "halting hart"); LOG_TARGET_DEBUG(target, "halting hart");
/* `haltreq` should not be issued if `abstractcs.busy` is set. */
int result = wait_for_idle_if_needed(target);
if (result != ERROR_OK)
return result;
/* Issue the halt command, and then wait for the current hart to halt. */ /* Issue the halt command, and then wait for the current hart to halt. */
uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_HALTREQ; uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_HALTREQ;
dmcontrol = set_dmcontrol_hartsel(dmcontrol, dm->current_hartid); dmcontrol = set_dmcontrol_hartsel(dmcontrol, dm->current_hartid);
@ -5149,11 +5215,8 @@ static int riscv013_halt_go(struct target *target)
/* Only some harts were halted/unavailable. Read /* Only some harts were halted/unavailable. Read
* dmstatus for this one to see what its status * dmstatus for this one to see what its status
* is. */ * is. */
riscv013_info_t *info = get_info(t); if (dm013_select_target(target) != ERROR_OK)
dmcontrol = set_dmcontrol_hartsel(dmcontrol, info->index);
if (dm_write(target, DM_DMCONTROL, dmcontrol) != ERROR_OK)
return ERROR_FAIL; return ERROR_FAIL;
dm->current_hartid = info->index;
if (dm_read(target, &t_dmstatus, DM_DMSTATUS) != ERROR_OK) if (dm_read(target, &t_dmstatus, DM_DMSTATUS) != ERROR_OK)
return ERROR_FAIL; return ERROR_FAIL;
} }
@ -5375,6 +5438,10 @@ static int riscv013_step_or_resume_current_hart(struct target *target,
/* Issue the resume command, and then wait for the current hart to resume. */ /* Issue the resume command, and then wait for the current hart to resume. */
uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_RESUMEREQ; uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_RESUMEREQ;
dmcontrol = set_dmcontrol_hartsel(dmcontrol, dm->current_hartid); dmcontrol = set_dmcontrol_hartsel(dmcontrol, dm->current_hartid);
/* `resumereq` should not be issued if `abstractcs.busy` is set. */
int result = wait_for_idle_if_needed(target);
if (result != ERROR_OK)
return result;
dm_write(target, DM_DMCONTROL, dmcontrol); dm_write(target, DM_DMCONTROL, dmcontrol);
dmcontrol = set_field(dmcontrol, DM_DMCONTROL_RESUMEREQ, 0); dmcontrol = set_field(dmcontrol, DM_DMCONTROL_RESUMEREQ, 0);