Skip to content

Commit

Permalink
target/riscv: check abstractcs.busy
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
en-sc committed Apr 9, 2024
1 parent 02c8855 commit 8f6965b
Showing 1 changed file with 73 additions and 6 deletions.
79 changes: 73 additions & 6 deletions src/target/riscv/riscv-013.c
Original file line number Diff line number Diff line change
Expand Up @@ -1931,6 +1931,26 @@ static int set_group(struct target *target, bool *supported, unsigned int group,
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)
{
dm013_info_t *dm = get_dm(target);
Expand Down Expand Up @@ -2028,7 +2048,12 @@ static int examine_dm(struct target *target)

if (get_field(s, DM_DMSTATUS_ANYHAVERESET)) {
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);
result = dm_write(target, DM_DMCONTROL, dmcontrol);
if (result != ERROR_OK)
Expand Down Expand Up @@ -2803,8 +2828,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
* that it is halted again once the request goes through.
*/
if (target->state == TARGET_HALTED)
if (target->state == TARGET_HALTED) {
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);
}
if (get_field(dmstatus, DM_DMSTATUS_ALLNONEXISTENT)) {
Expand Down Expand Up @@ -2925,11 +2956,24 @@ static int assert_reset(struct target *target)
/* Run the user-supplied script if there is one. */
target_handle_event(target, TARGET_EVENT_RESET_ASSERT);
} else {
dm013_info_t *dm = get_dm(target);
if (!dm)
return ERROR_FAIL;

uint32_t control = set_field(0, DM_DMCONTROL_DMACTIVE, 1);
control = set_dmcontrol_hartsel(control, info->index);
control = set_field(control, DM_DMCONTROL_HALTREQ,
target->reset_halt ? 1 : 0);
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);
if (result != ERROR_OK)
return result;
Expand All @@ -2946,6 +2990,9 @@ static int assert_reset(struct target *target)
static int deassert_reset(struct target *target)
{
RISCV013_INFO(info);
dm013_info_t *dm = get_dm(target);
if (!dm)
return ERROR_FAIL;
int result;

select_dmi(target);
Expand All @@ -2954,6 +3001,15 @@ static int deassert_reset(struct target *target)
control = set_field(control, DM_DMCONTROL_DMACTIVE, 1);
control = set_field(control, DM_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0);
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);
if (result != ERROR_OK)
return result;
Expand Down Expand Up @@ -5008,6 +5064,11 @@ static int dm013_select_hart(struct target *target, int hart_index)
if (hart_index == dm->current_hartid)
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;
dmcontrol = set_dmcontrol_hartsel(dmcontrol, hart_index);
if (dm_write(target, DM_DMCONTROL, dmcontrol) != ERROR_OK) {
Expand Down Expand Up @@ -5102,6 +5163,11 @@ static int riscv013_halt_go(struct target *target)

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. */
uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_HALTREQ;
dmcontrol = set_dmcontrol_hartsel(dmcontrol, dm->current_hartid);
Expand Down Expand Up @@ -5144,11 +5210,8 @@ static int riscv013_halt_go(struct target *target)
/* Only some harts were halted/unavailable. Read
* dmstatus for this one to see what its status
* is. */
riscv013_info_t *info = get_info(t);
dmcontrol = set_dmcontrol_hartsel(dmcontrol, info->index);
if (dm_write(target, DM_DMCONTROL, dmcontrol) != ERROR_OK)
if (dm013_select_target(target) != ERROR_OK)
return ERROR_FAIL;
dm->current_hartid = info->index;
if (dm_read(target, &t_dmstatus, DM_DMSTATUS) != ERROR_OK)
return ERROR_FAIL;
}
Expand Down Expand Up @@ -5370,6 +5433,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. */
uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_RESUMEREQ;
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);

dmcontrol = set_field(dmcontrol, DM_DMCONTROL_RESUMEREQ, 0);
Expand Down

0 comments on commit 8f6965b

Please sign in to comment.