diff --git a/src/target/riscv/opcodes.h b/src/target/riscv/opcodes.h index 59c34139ca..70fca250ea 100644 --- a/src/target/riscv/opcodes.h +++ b/src/target/riscv/opcodes.h @@ -71,6 +71,12 @@ static uint32_t jal(unsigned int rd, uint32_t imm) return imm_j(imm) | inst_rd(rd) | MATCH_JAL; } +static uint32_t csrci(unsigned int csr, uint16_t imm) __attribute__ ((unused)); +static uint32_t csrci(unsigned int csr, uint16_t imm) +{ + return imm_i(csr) | inst_rs1(imm) | MATCH_CSRRCI; +} + static uint32_t csrsi(unsigned int csr, uint16_t imm) __attribute__ ((unused)); static uint32_t csrsi(unsigned int csr, uint16_t imm) { @@ -143,6 +149,12 @@ static uint32_t csrr(unsigned int rd, unsigned int csr) return imm_i(csr) | inst_rd(rd) | MATCH_CSRRS; } +static uint32_t csrrc(unsigned int rd, unsigned int rs, unsigned int csr) __attribute__ ((unused)); +static uint32_t csrrc(unsigned int rd, unsigned int rs, unsigned int csr) +{ + return imm_i(csr) | inst_rs1(rs) | inst_rd(rd) | MATCH_CSRRC; +} + static uint32_t csrrs(unsigned int rd, unsigned int rs, unsigned int csr) __attribute__ ((unused)); static uint32_t csrrs(unsigned int rd, unsigned int rs, unsigned int csr) { diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index 0a68bfe98a..44826efa57 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -16,6 +16,7 @@ #include "target/target.h" #include "target/algorithm.h" #include "target/target_type.h" +#include #include #include "jtag/jtag.h" #include "target/register.h" @@ -31,7 +32,7 @@ #include "debug_reg_printer.h" #include "field_helpers.h" -static int riscv013_on_step_or_resume(struct target *target, bool step); +static int riscv013_on_step_or_resume(struct target *target, bool skip, bool step); static int riscv013_step_or_resume_current_hart(struct target *target, bool step); static int riscv013_clear_abstract_error(struct target *target); @@ -44,6 +45,7 @@ static int riscv013_set_register(struct target *target, enum gdb_regno regid, static int dm013_select_hart(struct target *target, int hart_index); static int riscv013_halt_prep(struct target *target); static int riscv013_halt_go(struct target *target); +static int riscv013_halt_target(struct target *target); static int riscv013_resume_go(struct target *target); static int riscv013_step_current_hart(struct target *target); static int riscv013_on_step(struct target *target); @@ -195,6 +197,7 @@ typedef struct { uint8_t datasize; uint8_t dataaccess; int16_t dataaddr; + uint8_t nscratch; /* The width of the hartsel field. */ unsigned hartsellen; @@ -1774,28 +1777,66 @@ static int set_dcsr_ebreak(struct target *target, bool step) RISCV_INFO(r); RISCV013_INFO(info); - riscv_reg_t original_dcsr, dcsr; - /* We want to twiddle some bits in the debug CSR so debugging works. */ - if (riscv_get_register(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK) - return ERROR_FAIL; - original_dcsr = dcsr; - dcsr = set_field(dcsr, CSR_DCSR_STEP, step); - dcsr = set_field(dcsr, CSR_DCSR_EBREAKM, r->riscv_ebreakm); - dcsr = set_field(dcsr, CSR_DCSR_EBREAKS, r->riscv_ebreaks && riscv_supports_extension(target, 'S')); - dcsr = set_field(dcsr, CSR_DCSR_EBREAKU, r->riscv_ebreaku && riscv_supports_extension(target, 'U')); - dcsr = set_field(dcsr, CSR_DCSR_EBREAKVS, r->riscv_ebreaku && riscv_supports_extension(target, 'H')); - dcsr = set_field(dcsr, CSR_DCSR_EBREAKVU, r->riscv_ebreaku && riscv_supports_extension(target, 'H')); - if (dcsr != original_dcsr && - riscv_set_register(target, GDB_REGNO_DCSR, dcsr) != ERROR_OK) - return ERROR_FAIL; + + if ((info->nscratch >= 1) && has_sufficient_progbuf(target, 8)) { + uint64_t set_ebreak_bits = 0; + uint64_t clr_ebreak_bits = 0; + if (r->riscv_ebreakm) + set_ebreak_bits |= CSR_DCSR_EBREAKM; + else + clr_ebreak_bits |= CSR_DCSR_EBREAKM; + if (r->riscv_ebreaks && riscv_supports_extension(target, 'S')) + set_ebreak_bits |= CSR_DCSR_EBREAKS; + else + clr_ebreak_bits |= CSR_DCSR_EBREAKS; + if (r->riscv_ebreaku && riscv_supports_extension(target, 'U')) + set_ebreak_bits |= CSR_DCSR_EBREAKU; + else + clr_ebreak_bits |= CSR_DCSR_EBREAKU; + if (r->riscv_ebreaku && riscv_supports_extension(target, 'H')) + set_ebreak_bits |= CSR_DCSR_EBREAKVS; + else + clr_ebreak_bits |= CSR_DCSR_EBREAKVS; + if (r->riscv_ebreaku && riscv_supports_extension(target, 'H')) + set_ebreak_bits |= CSR_DCSR_EBREAKVU; + else + clr_ebreak_bits |= CSR_DCSR_EBREAKVU; + struct riscv_program program; + riscv_program_init(&program, target); + riscv_program_insert(&program, csrw(S0, CSR_DSCRATCH0)); + riscv_program_insert(&program, lui(S0, set_ebreak_bits)); + riscv_program_insert(&program, csrrs(ZERO, S0, CSR_DCSR)); + riscv_program_insert(&program, lui(S0, clr_ebreak_bits)); + riscv_program_insert(&program, csrrc(ZERO, S0, CSR_DCSR)); + if (step) + riscv_program_insert(&program, csrsi(CSR_DCSR, 0x4)); + else + riscv_program_insert(&program, csrci(CSR_DCSR, 0x4)); + riscv_program_insert(&program, csrr(S0, CSR_DSCRATCH0)); + if (riscv_program_exec(&program, target) != ERROR_OK) + return ERROR_FAIL; + } else { + riscv_reg_t original_dcsr, dcsr; + /* We want to twiddle some bits in the debug CSR so debugging works. */ + if (riscv_get_register(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK) + return ERROR_FAIL; + original_dcsr = dcsr; + dcsr = set_field(dcsr, CSR_DCSR_STEP, step); + dcsr = set_field(dcsr, CSR_DCSR_EBREAKM, r->riscv_ebreakm); + dcsr = set_field(dcsr, CSR_DCSR_EBREAKS, r->riscv_ebreaks && riscv_supports_extension(target, 'S')); + dcsr = set_field(dcsr, CSR_DCSR_EBREAKU, r->riscv_ebreaku && riscv_supports_extension(target, 'U')); + dcsr = set_field(dcsr, CSR_DCSR_EBREAKVS, r->riscv_ebreaku && riscv_supports_extension(target, 'H')); + dcsr = set_field(dcsr, CSR_DCSR_EBREAKVU, r->riscv_ebreaku && riscv_supports_extension(target, 'H')); + if (dcsr != original_dcsr && + riscv_set_register(target, GDB_REGNO_DCSR, dcsr) != ERROR_OK) + return ERROR_FAIL; + } info->dcsr_ebreak_is_set = true; return ERROR_OK; } static int halt_set_dcsr_ebreak(struct target *target) { - RISCV_INFO(r); - RISCV013_INFO(info); LOG_TARGET_DEBUG(target, "Halt to set DCSR.ebreak*"); /* Remove this hart from the halt group. This won't work on all targets @@ -1821,35 +1862,47 @@ static int halt_set_dcsr_ebreak(struct target *target) */ - if (info->haltgroup_supported) { - bool supported; - if (set_group(target, &supported, 0, HALT_GROUP) != ERROR_OK) - return ERROR_FAIL; - if (!supported) - LOG_TARGET_ERROR(target, "Couldn't place hart in halt group 0. " - "Some harts may be unexpectedly halted."); + struct target_list *entry; + struct list_head *targets; + + if (target->smp) { + targets = target->smp_targets; + foreach_smp_target(entry, targets) { + struct target *t = entry->target; + if (riscv013_halt_prep(t) != ERROR_OK) { + LOG_TARGET_INFO(target, "riscv013_halt_prep failed - t_coreid: %d", t->coreid); + return ERROR_FAIL; + } + } } int result = ERROR_OK; - - r->prepped = true; - if (riscv013_halt_go(target) != ERROR_OK || - set_dcsr_ebreak(target, false) != ERROR_OK || - riscv013_step_or_resume_current_hart(target, false) != ERROR_OK) { - result = ERROR_FAIL; + int halt_result = ERROR_OK; + int resume_result = ERROR_OK; + + halt_result = riscv013_halt_go(target); + if (halt_result == ERROR_OK) { + if (riscv013_on_step_or_resume(target, true, false) == ERROR_OK) { + resume_result = riscv013_step_or_resume_current_hart(target, false); + if (resume_result == ERROR_OK) { + target->state = TARGET_RUNNING; + target->debug_reason = DBG_REASON_NOTHALTED; + } else if (resume_result == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) { + LOG_TARGET_INFO(target, "riscv013_resume_go aborted"); + target->state = TARGET_UNAVAILABLE; + } else { + LOG_TARGET_INFO(target, "riscv013_resume_go failed"); + result = ERROR_FAIL; + } + } else { + LOG_TARGET_INFO(target, "resume_prep failed"); + result = ERROR_FAIL; + } + } else if (halt_result == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) { + LOG_TARGET_INFO(target, "riscv013_halt_go aborted"); } else { - target->state = TARGET_RUNNING; - target->debug_reason = DBG_REASON_NOTHALTED; - } - - /* Add it back to the halt group. */ - if (info->haltgroup_supported) { - bool supported; - if (set_group(target, &supported, target->smp, HALT_GROUP) != ERROR_OK) - return ERROR_FAIL; - if (!supported) - LOG_TARGET_ERROR(target, "Couldn't place hart back in halt group %d. " - "Some harts may be unexpectedly halted.", target->smp); + LOG_TARGET_INFO(target, "riscv013_halt_go failed"); + result = ERROR_FAIL; } return result; @@ -1983,6 +2036,7 @@ static int examine(struct target *target) info->datasize = get_field(hartinfo, DM_HARTINFO_DATASIZE); info->dataaccess = get_field(hartinfo, DM_HARTINFO_DATAACCESS); info->dataaddr = get_field(hartinfo, DM_HARTINFO_DATAADDR); + info->nscratch = get_field(hartinfo, DM_HARTINFO_NSCRATCH); if (!get_field(dmstatus, DM_DMSTATUS_AUTHENTICATED)) { LOG_TARGET_ERROR(target, "Debugger is not authenticated to target Debug Module. " @@ -2001,26 +2055,9 @@ static int examine(struct target *target) info->datacount = get_field(abstractcs, DM_ABSTRACTCS_DATACOUNT); info->progbufsize = get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE); - LOG_TARGET_INFO(target, "datacount=%d progbufsize=%d", - info->datacount, info->progbufsize); - RISCV_INFO(r); r->impebreak = get_field(dmstatus, DM_DMSTATUS_IMPEBREAK); - if (!has_sufficient_progbuf(target, 2)) { - LOG_TARGET_WARNING(target, "We won't be able to execute fence instructions on this " - "target. Memory may not always appear consistent. " - "(progbufsize=%d, impebreak=%d)", info->progbufsize, - r->impebreak); - } - - if (info->progbufsize < 4 && riscv_enable_virtual) { - LOG_TARGET_ERROR(target, "set_enable_virtual is not available on this target. It " - "requires a program buffer size of at least 4. (progbufsize=%d) " - "Use `riscv set_enable_virtual off` to continue." - , info->progbufsize); - } - /* Before doing anything else we must first enumerate the harts. */ if (dm->hart_count < 0) { for (int i = 0; i < MIN(RISCV_MAX_HARTS, 1 << info->hartsellen); ++i) { @@ -2056,16 +2093,41 @@ static int examine(struct target *target) enum riscv_hart_state state_at_examine_start; if (riscv_get_hart_state(target, &state_at_examine_start) != ERROR_OK) return ERROR_FAIL; + + /* Skip full examination and reporting of hart if it is currently unavailable */ + const bool hart_unavailable_at_examine_start = state_at_examine_start == RISCV_STATE_UNAVAILABLE; + if (hart_unavailable_at_examine_start) { + LOG_TARGET_DEBUG(target, "Did not fully examine hart %d as it was currently unavailable, deferring examine.", info->index); + target->state = TARGET_UNAVAILABLE; + target->defer_examine = true; + return ERROR_OK; + } const bool hart_halted_at_examine_start = state_at_examine_start == RISCV_STATE_HALTED; if (!hart_halted_at_examine_start) { - r->prepped = true; - if (riscv013_halt_go(target) != ERROR_OK) { + if (riscv013_halt_target(target) != ERROR_OK) { LOG_TARGET_ERROR(target, "Fatal: Hart %d failed to halt during %s", info->index, __func__); return ERROR_FAIL; } } + LOG_TARGET_INFO(target, "datacount=%d progbufsize=%d", + info->datacount, info->progbufsize); + + if (!has_sufficient_progbuf(target, 2)) { + LOG_TARGET_WARNING(target, "We won't be able to execute fence instructions on this " + "target. Memory may not always appear consistent. " + "(progbufsize=%d, impebreak=%d)", info->progbufsize, + r->impebreak); + } + + if (info->progbufsize < 4 && riscv_enable_virtual) { + LOG_TARGET_ERROR(target, "set_enable_virtual is not available on this target. It " + "requires a program buffer size of at least 4. (progbufsize=%d) " + "Use `riscv set_enable_virtual off` to continue." + , info->progbufsize); + } + target->state = TARGET_HALTED; target->debug_reason = hart_halted_at_examine_start ? DBG_REASON_UNDEFINED : DBG_REASON_DBGRQ; @@ -2691,7 +2753,7 @@ static int riscv013_get_hart_state(struct target *target, enum riscv_hart_state if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) return ERROR_FAIL; if (get_field(dmstatus, DM_DMSTATUS_ANYHAVERESET)) { - LOG_TARGET_INFO(target, "Hart unexpectedly reset!"); + LOG_TARGET_DEBUG(target, "Hart unexpectedly reset!"); info->dcsr_ebreak_is_set = false; /* TODO: Can we make this more obvious to eg. a gdb user? */ uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE | @@ -2726,6 +2788,29 @@ static int riscv013_get_hart_state(struct target *target, enum riscv_hart_state return ERROR_FAIL; } +static int handle_became_available(struct target *target, + enum riscv_hart_state previous_riscv_state) +{ + if (dm013_select_target(target) != ERROR_OK) { + LOG_TARGET_INFO(target, "dm013_select_target failed"); + return ERROR_FAIL; + } + target->state = TARGET_HALTED; + int result = riscv013_step_or_resume_current_hart(target, false); + if (result == ERROR_OK) { + target->state = TARGET_RUNNING; + target->debug_reason = DBG_REASON_NOTHALTED; + } else if (result == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) { + LOG_TARGET_INFO(target, "riscv013_step_or_resume_current_hart aborted"); + target->state = TARGET_UNAVAILABLE; + return ERROR_OK; + } else { + LOG_TARGET_INFO(target, "riscv013_step_or_resume_current_hart failed"); + return ERROR_FAIL; + } + return ERROR_OK; +} + static int handle_became_unavailable(struct target *target, enum riscv_hart_state previous_riscv_state) { @@ -2780,6 +2865,7 @@ static int init_target(struct command_context *cmd_ctx, generic_info->data_bits = &riscv013_data_bits; generic_info->print_info = &riscv013_print_info; + generic_info->handle_became_available = &handle_became_available; generic_info->handle_became_unavailable = &handle_became_unavailable; generic_info->tick = &tick; @@ -2893,7 +2979,9 @@ static int deassert_reset(struct target *target) info->dmi_busy_delay = orig_dmi_busy_delay; - if (target->reset_halt) { + if (get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL)) { + target->state = TARGET_UNAVAILABLE; + } else if (target->reset_halt) { target->state = TARGET_HALTED; target->debug_reason = DBG_REASON_DBGRQ; } else { @@ -4906,89 +4994,64 @@ static int dm013_select_hart(struct target *target, int hart_index) return ERROR_OK; } -/* Select all harts that were prepped and that are selectable, clearing the - * prepped flag on the harts that actually were selected. */ -static int select_prepped_harts(struct target *target) +static int riscv013_halt_prep(struct target *target) { - RISCV_INFO(r); - dm013_info_t *dm = get_dm(target); - if (!dm) - return ERROR_FAIL; - if (!dm->hasel_supported) { - r->prepped = false; - return dm013_select_target(target); - } - - assert(dm->hart_count); - unsigned hawindow_count = (dm->hart_count + 31) / 32; - uint32_t *hawindow = calloc(hawindow_count, sizeof(uint32_t)); - if (!hawindow) - return ERROR_FAIL; - - target_list_t *entry; - unsigned total_selected = 0; - unsigned int selected_index = 0; - list_for_each_entry(entry, &dm->target_list, list) { - struct target *t = entry->target; - struct riscv_info *info = riscv_info(t); - riscv013_info_t *info_013 = get_info(t); - unsigned int index = info_013->index; - LOG_TARGET_DEBUG(target, "index=%d, prepped=%d", index, info->prepped); - if (info->prepped) { - info_013->selected = true; - hawindow[index / 32] |= 1 << (index % 32); - info->prepped = false; - total_selected++; - selected_index = index; + LOG_TARGET_DEBUG(target, "grouping hart"); + + if (target->smp) { + /* Let's make sure that all non-halted harts are in the same halt group */ + riscv013_info_t *info = get_info(target); + if (info->haltgroup_supported) { + if (dm013_select_target(target) != ERROR_OK) + return ERROR_FAIL; + bool supported; + if (set_group(target, &supported, target->smp, HALT_GROUP) != ERROR_OK) + return ERROR_FAIL; + if (!supported) + LOG_TARGET_ERROR(target, "Couldn't place hart %d in halt group %d. " + "Some harts may be unexpectedly halted.", target->coreid, target->smp); } } - if (total_selected == 0) { - LOG_TARGET_ERROR(target, "No harts were prepped!"); - free(hawindow); - return ERROR_FAIL; - } else if (total_selected == 1) { - /* Don't use hasel if we only need to talk to one hart. */ - free(hawindow); - return dm013_select_hart(target, selected_index); - } + return ERROR_OK; +} - if (dm013_select_hart(target, HART_INDEX_MULTIPLE) != ERROR_OK) { - free(hawindow); +static int riscv013_halt_go(struct target *target) +{ + LOG_TARGET_DEBUG(target, "halting hart"); + + if (dm013_select_target(target) != ERROR_OK) { return ERROR_FAIL; } - - for (unsigned i = 0; i < hawindow_count; i++) { - if (dm_write(target, DM_HAWINDOWSEL, i) != ERROR_OK) { - free(hawindow); - return ERROR_FAIL; - } - if (dm_write(target, DM_HAWINDOW, hawindow[i]) != ERROR_OK) { - free(hawindow); - return ERROR_FAIL; + if (target->smp) { + /* Let's make sure that harts we want to halt are placed in another group */ + riscv013_info_t *info = get_info(target); + if (info->haltgroup_supported) { + bool supported; + if (set_group(target, &supported, 0, HALT_GROUP) != ERROR_OK) + return ERROR_FAIL; + if (!supported) + LOG_TARGET_ERROR(target, "Couldn't place hart in halt group 0. " + "Some harts may be unexpectedly halted."); } } - free(hawindow); - return ERROR_OK; -} + int result = riscv013_halt_target(target); + if (result == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) { + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + else if (result != ERROR_OK) { + return ERROR_FAIL; + } -static int riscv013_halt_prep(struct target *target) -{ return ERROR_OK; } -static int riscv013_halt_go(struct target *target) +static int riscv013_halt_target(struct target *target) { - dm013_info_t *dm = get_dm(target); - if (!dm) - return ERROR_FAIL; - - if (select_prepped_harts(target) != ERROR_OK) - return ERROR_FAIL; - - LOG_TARGET_DEBUG(target, "halting hart"); + LOG_TARGET_DEBUG(target, "halting one hart"); + dm013_info_t *dm = get_dm(target); /* 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); @@ -4997,13 +5060,12 @@ static int riscv013_halt_go(struct target *target) for (size_t i = 0; i < 256; ++i) { if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) return ERROR_FAIL; - /* When no harts are running, there's no point in continuing this loop. */ + /* When hart is not running, there's no point in continuing this loop. */ if (!get_field(dmstatus, DM_DMSTATUS_ANYRUNNING)) break; } - /* We declare success if no harts are running. One or more of them may be - * unavailable, though. */ + /* We declare success if hart is not running. It may be unavailable, though. */ if ((get_field(dmstatus, DM_DMSTATUS_ANYRUNNING))) { if (dm_read(target, &dmcontrol, DM_DMCONTROL) != ERROR_OK) @@ -5017,47 +5079,14 @@ static int riscv013_halt_go(struct target *target) dmcontrol = set_field(dmcontrol, DM_DMCONTROL_HALTREQ, 0); dm_write(target, DM_DMCONTROL, dmcontrol); - if (dm->current_hartid == HART_INDEX_MULTIPLE) { - target_list_t *entry; - list_for_each_entry(entry, &dm->target_list, list) { - struct target *t = entry->target; - uint32_t t_dmstatus; - if (get_field(dmstatus, DM_DMSTATUS_ALLHALTED) || - get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL)) { - /* All harts are either halted or unavailable. No - * need to read dmstatus for each hart. */ - t_dmstatus = dmstatus; - } else { - /* 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) - return ERROR_FAIL; - dm->current_hartid = info->index; - if (dm_read(target, &t_dmstatus, DM_DMSTATUS) != ERROR_OK) - return ERROR_FAIL; - } - /* Set state for the current target based on its dmstatus. */ - if (get_field(t_dmstatus, DM_DMSTATUS_ALLHALTED)) { - t->state = TARGET_HALTED; - if (t->debug_reason == DBG_REASON_NOTHALTED) - t->debug_reason = DBG_REASON_DBGRQ; - } else if (get_field(t_dmstatus, DM_DMSTATUS_ALLUNAVAIL)) { - t->state = TARGET_UNAVAILABLE; - } - } - - } else { - /* Set state for the current target based on its dmstatus. */ - if (get_field(dmstatus, DM_DMSTATUS_ALLHALTED)) { - target->state = TARGET_HALTED; - if (target->debug_reason == DBG_REASON_NOTHALTED) - target->debug_reason = DBG_REASON_DBGRQ; - } else if (get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL)) { - target->state = TARGET_UNAVAILABLE; - } + /* Set state for the current target based on its dmstatus. */ + if (get_field(dmstatus, DM_DMSTATUS_ALLHALTED)) { + target->state = TARGET_HALTED; + if (target->debug_reason == DBG_REASON_NOTHALTED) + target->debug_reason = DBG_REASON_DBGRQ; + } else if (get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL)) { + target->state = TARGET_UNAVAILABLE; + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } return ERROR_OK; @@ -5065,9 +5094,6 @@ static int riscv013_halt_go(struct target *target) static int riscv013_resume_go(struct target *target) { - if (select_prepped_harts(target) != ERROR_OK) - return ERROR_FAIL; - return riscv013_step_or_resume_current_hart(target, false); } @@ -5079,12 +5105,12 @@ static int riscv013_step_current_hart(struct target *target) static int riscv013_resume_prep(struct target *target) { assert(target->state == TARGET_HALTED); - return riscv013_on_step_or_resume(target, false); + return riscv013_on_step_or_resume(target, false, false); } static int riscv013_on_step(struct target *target) { - return riscv013_on_step_or_resume(target, true); + return riscv013_on_step_or_resume(target, false, true); } static enum riscv_halt_reason riscv013_halt_reason(struct target *target) @@ -5220,17 +5246,17 @@ void riscv013_fill_dm_nop_u64(struct target *target, char *buf) riscv013_fill_dmi_nop_u64(target, buf); } -static int maybe_execute_fence_i(struct target *target) +static int maybe_execute_fence_i(struct target *target, bool skip) { - if (has_sufficient_progbuf(target, 2)) + if (!skip && has_sufficient_progbuf(target, 2)) return execute_fence(target); return ERROR_OK; } /* Helper Functions. */ -static int riscv013_on_step_or_resume(struct target *target, bool step) +static int riscv013_on_step_or_resume(struct target *target, bool skip, bool step) { - if (maybe_execute_fence_i(target) != ERROR_OK) + if (maybe_execute_fence_i(target, skip) != ERROR_OK) return ERROR_FAIL; if (set_dcsr_ebreak(target, step) != ERROR_OK) @@ -5250,8 +5276,14 @@ static int riscv013_step_or_resume_current_hart(struct target *target, } LOG_TARGET_DEBUG(target, "resuming (for step?=%d)", step); - if (riscv_flush_registers(target) != ERROR_OK) + if (riscv_flush_registers(target) != ERROR_OK) { + LOG_TARGET_INFO(target, "riscv_flush_registers failed"); + return ERROR_FAIL; + } + + if (dm013_select_target(target) != ERROR_OK) { return ERROR_FAIL; + } dm013_info_t *dm = get_dm(target); /* Issue the resume command, and then wait for the current hart to resume. */ @@ -5264,24 +5296,50 @@ static int riscv013_step_or_resume_current_hart(struct target *target, uint32_t dmstatus; for (size_t i = 0; i < 256; ++i) { usleep(10); - if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) - return ERROR_FAIL; - if (get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL)) + if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) { + LOG_TARGET_INFO(target, "dmstatus_read failed"); return ERROR_FAIL; + } + if (get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL)) { + LOG_TARGET_INFO(target, "dmstatus_read aborted"); + target->state = TARGET_UNAVAILABLE; + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + if (get_field(dmstatus, DM_DMSTATUS_ANYHAVERESET)) { + dmcontrol = dmcontrol | DM_DMCONTROL_ACKHAVERESET; + } if (get_field(dmstatus, DM_DMSTATUS_ALLRESUMEACK) == 0) continue; if (step && get_field(dmstatus, DM_DMSTATUS_ALLHALTED) == 0) continue; dm_write(target, DM_DMCONTROL, dmcontrol); + + if (target->smp) { + /* Let's make sure that this hart is placed back with all non-halted harts */ + riscv013_info_t *info = get_info(target); + if (info->haltgroup_supported) { + bool supported; + if (set_group(target, &supported, target->smp, HALT_GROUP) != ERROR_OK) { + LOG_TARGET_INFO(target, "set_group failed"); + return ERROR_FAIL; + } + if (!supported) + LOG_TARGET_ERROR(target, "Couldn't place hart back in halt group %d. " + "Some harts may be unexpectedly halted.", target->smp); + } + } + return ERROR_OK; } dm_write(target, DM_DMCONTROL, dmcontrol); LOG_TARGET_ERROR(target, "unable to resume"); - if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) + if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) { + LOG_TARGET_INFO(target, "dmstatus_read failed"); return ERROR_FAIL; + } LOG_TARGET_ERROR(target, " dmstatus=0x%08x", dmstatus); if (step) { @@ -5290,6 +5348,7 @@ static int riscv013_step_or_resume_current_hart(struct target *target, return ERROR_OK; } + LOG_TARGET_INFO(target, "resume_current_hart failed"); return ERROR_FAIL; } diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index 97de01703c..bdbb8e4109 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -534,6 +534,10 @@ static void riscv_deinit_target(struct target *target) { LOG_TARGET_DEBUG(target, "riscv_deinit_target()"); + /* No need to deinit a target that has not been examined */ + if (!target_was_examined(target)) + return; + struct riscv_info *info = target->arch_info; struct target_type *tt = get_target_type(target); if (!tt) @@ -1920,28 +1924,31 @@ int riscv_halt(struct target *target) LOG_TARGET_DEBUG(target, "halting all harts"); + /* Only halt a hart if it has been examined (was available) */ int result = ERROR_OK; if (target->smp) { struct target_list *tlist; foreach_smp_target(tlist, target->smp_targets) { struct target *t = tlist->target; - if (halt_prep(t) != ERROR_OK) - result = ERROR_FAIL; + if (target_was_examined(t)) + if (halt_prep(t) != ERROR_OK) + result = ERROR_FAIL; } foreach_smp_target(tlist, target->smp_targets) { struct target *t = tlist->target; struct riscv_info *i = riscv_info(t); - if (i->prepped) { - if (halt_go(t) != ERROR_OK) - result = ERROR_FAIL; - } + if (target_was_examined(t)) + if (i->prepped) + if (halt_go(t) != ERROR_OK) + result = ERROR_FAIL; } foreach_smp_target(tlist, target->smp_targets) { struct target *t = tlist->target; - if (halt_finish(t) != ERROR_OK) - return ERROR_FAIL; + if (target_was_examined(t)) + if (halt_finish(t) != ERROR_OK) + return ERROR_FAIL; } } else { @@ -3039,11 +3046,21 @@ static int riscv_poll_hart(struct target *target, enum riscv_next_action *next_a return ERROR_FAIL; } + /* The hart apparently became unavailable while halted, so we want to resume it */ + if (state == RISCV_STATE_HALTED && previous_riscv_state == RISCV_STATE_UNAVAILABLE) { + if (r->handle_became_available && + r->handle_became_available(target, previous_riscv_state) != ERROR_OK) + return ERROR_FAIL; + if (riscv_get_hart_state(target, &state) != ERROR_OK) + return ERROR_FAIL; + } + if (target->state == TARGET_UNKNOWN || state != previous_riscv_state) { switch (state) { case RISCV_STATE_HALTED: - if (previous_riscv_state == RISCV_STATE_UNAVAILABLE) + if (previous_riscv_state == RISCV_STATE_UNAVAILABLE) { LOG_TARGET_INFO(target, "became available (halted)"); + } LOG_TARGET_DEBUG(target, " triggered a halt; previous_target_state=%d", previous_target_state); @@ -3090,8 +3107,9 @@ static int riscv_poll_hart(struct target *target, enum riscv_next_action *next_a break; case RISCV_STATE_RUNNING: - if (previous_riscv_state == RISCV_STATE_UNAVAILABLE) + if (previous_riscv_state == RISCV_STATE_UNAVAILABLE) { LOG_TARGET_INFO(target, "became available (running)"); + } LOG_TARGET_DEBUG(target, " triggered running"); target->state = TARGET_RUNNING; @@ -3102,8 +3120,16 @@ static int riscv_poll_hart(struct target *target, enum riscv_next_action *next_a break; case RISCV_STATE_UNAVAILABLE: - LOG_TARGET_DEBUG(target, " became unavailable"); - LOG_TARGET_INFO(target, "became unavailable."); + if (previous_riscv_state == RISCV_STATE_HALTED) { + LOG_TARGET_DEBUG(target, " became unavailable (halted)"); + LOG_TARGET_INFO(target, "became unavailable (halted)."); + } else if (previous_riscv_state == RISCV_STATE_RUNNING) { + LOG_TARGET_DEBUG(target, " became unavailable (running)"); + LOG_TARGET_INFO(target, "became unavailable (running)."); + } else { + LOG_TARGET_DEBUG(target, " became unavailable"); + LOG_TARGET_INFO(target, "became unavailable."); + } target->state = TARGET_UNAVAILABLE; if (r->handle_became_unavailable && r->handle_became_unavailable(target, previous_riscv_state) != ERROR_OK) @@ -3206,8 +3232,10 @@ int riscv_openocd_poll(struct target *target) continue; enum riscv_next_action next_action; - if (riscv_poll_hart(t, &next_action) != ERROR_OK) + if (riscv_poll_hart(t, &next_action) != ERROR_OK) { + LOG_TARGET_INFO(target, "riscv_poll_hart failed - next_action: %d", next_action); return ERROR_FAIL; + } switch (next_action) { case RPH_NONE: @@ -3264,8 +3292,10 @@ int riscv_openocd_poll(struct target *target) foreach_smp_target(entry, targets) { struct target *t = entry->target; struct riscv_info *info = riscv_info(t); - if (info->tick && info->tick(t) != ERROR_OK) + if (info->tick && info->tick(t) != ERROR_OK) { + LOG_TARGET_INFO(target, "info_tick failed - t_coreid: %d", t->coreid); return ERROR_FAIL; + } } /* Sample memory if any target is running. */ diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index 8f28232f27..8ac4ed1d6f 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -213,6 +213,8 @@ struct riscv_info { enum riscv_hart_state previous_riscv_state); int (*handle_became_running)(struct target *target, enum riscv_hart_state previous_riscv_state); + int (*handle_became_available)(struct target *target, + enum riscv_hart_state previous_riscv_state); int (*handle_became_unavailable)(struct target *target, enum riscv_hart_state previous_riscv_state); diff --git a/src/target/startup.tcl b/src/target/startup.tcl index 75e0edc77d..8ddac0cbf5 100644 --- a/src/target/startup.tcl +++ b/src/target/startup.tcl @@ -131,6 +131,11 @@ proc ocd_process_reset_inner { MODE } { } } + # no need to wait for a target that is unavailable anyway + if { [$t curstate] == "unavailable" } { + continue + } + # Wait up to 1 second for target to halt. Why 1sec? Cause # the JTAG tap reset signal might be hooked to a slow # resistor/capacitor circuit - and it might take a while @@ -142,8 +147,11 @@ proc ocd_process_reset_inner { MODE } { # Did we succeed? set s [$t curstate] + if { $s == "unavailable" } { + continue + } if { $s != "halted" } { - return -code error [format "TARGET: %s - Not halted" $t] + return -code error [format "TARGET: %s - Not halted (%s)" $t $s] } } } @@ -160,6 +168,9 @@ proc ocd_process_reset_inner { MODE } { if { ![$t was_examined] && [$t examine_deferred] } { continue } + if { [$t curstate] == "unavailable" } { + continue + } set err [catch "$t arp_waitstate halted 5000"] # Did it halt? diff --git a/src/target/target.c b/src/target/target.c index 630e47eb18..d3e7c6cefe 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -486,8 +486,10 @@ int target_poll(struct target *target) } retval = target->type->poll(target); - if (retval != ERROR_OK) + if (retval != ERROR_OK) { + LOG_TARGET_INFO(target, "target->type->poll failed"); return retval; + } if (target->halt_issued) { if (target->state == TARGET_HALTED) @@ -676,6 +678,7 @@ int target_examine_one(struct target *target) target_call_event_callbacks(target, TARGET_EVENT_EXAMINE_START); + bool defer_state = target->defer_examine; int retval = target->type->examine(target); if (retval != ERROR_OK) { LOG_TARGET_ERROR(target, "Examination failed"); @@ -685,11 +688,17 @@ int target_examine_one(struct target *target) return retval; } - LOG_USER("[%s] Target successfully examined.", target_name(target)); - target_set_examined(target); + if (target->defer_examine) { + LOG_USER("[%s] Target currently unavailable for full examination.", target_name(target)); + target->defer_examine = defer_state; + target_reset_examined(target); + } else { + LOG_USER("[%s] Target successfully examined.", target_name(target)); + target_set_examined(target); + } target_call_event_callbacks(target, TARGET_EVENT_EXAMINE_END); - LOG_TARGET_INFO(target, "Examination succeed"); + LOG_TARGET_DEBUG(target, "Examination succeed"); return ERROR_OK; } @@ -5261,13 +5270,19 @@ COMMAND_HANDLER(handle_target_examine) return ERROR_OK; } + bool defer_state = target->defer_examine; int retval = target->type->examine(target); if (retval != ERROR_OK) { target_reset_examined(target); return retval; } - target_set_examined(target); + if (target->defer_examine) { + LOG_INFO("Unable to do full examination of %s", target_name(target)); + target->defer_examine = defer_state; + target_reset_examined(target); + } else + target_set_examined(target); return ERROR_OK; }