aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEvgeniy Naydanov <evgeniy.naydanov@syntacore.com>2024-02-20 20:02:14 +0300
committerEvgeniy Naydanov <evgeniy.naydanov@syntacore.com>2024-04-11 12:30:15 +0300
commit34d6fe36760f0e3ceed31d2b6d4700a6a3e96f48 (patch)
tree36cf7137aac06012558adf9abf6f4285e611f48d /src
parent8319eee9e1ffc601b94b4223958180b49f8b8188 (diff)
downloadriscv-openocd-34d6fe36760f0e3ceed31d2b6d4700a6a3e96f48.zip
riscv-openocd-34d6fe36760f0e3ceed31d2b6d4700a6a3e96f48.tar.gz
riscv-openocd-34d6fe36760f0e3ceed31d2b6d4700a6a3e96f48.tar.bz2
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>
Diffstat (limited to 'src')
-rw-r--r--src/target/riscv/riscv-013.c79
1 files changed, 73 insertions, 6 deletions
diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c
index 05ce203..34aba8f 100644
--- a/src/target/riscv/riscv-013.c
+++ b/src/target/riscv/riscv-013.c
@@ -1932,6 +1932,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);
@@ -2033,7 +2053,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)
@@ -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
* 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)) {
@@ -2930,11 +2961,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;
@@ -2951,6 +2995,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);
@@ -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_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;
@@ -5013,6 +5069,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) {
@@ -5107,6 +5168,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);
@@ -5149,11 +5215,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;
}
@@ -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. */
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);