From 7e3bc17f6f0f1049f7197988e57fc5baea5491cb Mon Sep 17 00:00:00 2001 From: Evgeniy Naydanov Date: Tue, 20 Feb 2024 20:02:14 +0300 Subject: [PATCH] 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 --- src/target/riscv/riscv-013.c | 75 +++++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index 6d1c70bae..ae340d636 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -1927,6 +1927,22 @@ 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->abtractcs_busy) + return ERROR_OK; + + 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); @@ -2016,7 +2032,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) @@ -2791,8 +2812,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)) { @@ -2913,11 +2940,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; @@ -2934,6 +2974,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); @@ -2942,6 +2985,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; @@ -4996,6 +5048,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) { @@ -5090,6 +5147,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); @@ -5132,11 +5194,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; } @@ -5358,6 +5417,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);