Improved handling of unavailable harts

Added more handling of unexamined harts

Added even more handling of unexamined harts

Added cleaner handling of unexamined harts

Fixing halt_set_dcsr_ebreak not setting hart explicitly enough

Adding riscv013_halt_current_hart function

Handling harts becoming available when halted

Extending handling of unavailable harts

Ensuring target is selected when resuming it

Making halt_set_dcsr_ebreak more robust

OK if target is unavailble after waiting for halt

Can also use progbuf only for dcsr_ebreak

Using progbuf for set_dcsr_break now works

Handle unavailability when deasserting reset
This commit is contained in:
cgsfv 2024-02-13 18:00:52 -08:00
parent 87331a82a2
commit 0c748e52c0
6 changed files with 343 additions and 214 deletions

View File

@ -71,6 +71,12 @@ static uint32_t jal(unsigned int rd, uint32_t imm)
return imm_j(imm) | inst_rd(rd) | MATCH_JAL; 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) __attribute__ ((unused));
static uint32_t csrsi(unsigned int csr, uint16_t imm) 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; 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) __attribute__ ((unused));
static uint32_t csrrs(unsigned int rd, unsigned int rs, unsigned int csr) static uint32_t csrrs(unsigned int rd, unsigned int rs, unsigned int csr)
{ {

View File

@ -16,6 +16,7 @@
#include "target/target.h" #include "target/target.h"
#include "target/algorithm.h" #include "target/algorithm.h"
#include "target/target_type.h" #include "target/target_type.h"
#include <target/smp.h>
#include <helper/log.h> #include <helper/log.h>
#include "jtag/jtag.h" #include "jtag/jtag.h"
#include "target/register.h" #include "target/register.h"
@ -31,7 +32,7 @@
#include "debug_reg_printer.h" #include "debug_reg_printer.h"
#include "field_helpers.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, static int riscv013_step_or_resume_current_hart(struct target *target,
bool step); bool step);
static int riscv013_clear_abstract_error(struct target *target); 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 dm013_select_hart(struct target *target, int hart_index);
static int riscv013_halt_prep(struct target *target); static int riscv013_halt_prep(struct target *target);
static int riscv013_halt_go(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_resume_go(struct target *target);
static int riscv013_step_current_hart(struct target *target); static int riscv013_step_current_hart(struct target *target);
static int riscv013_on_step(struct target *target); static int riscv013_on_step(struct target *target);
@ -195,6 +197,7 @@ typedef struct {
uint8_t datasize; uint8_t datasize;
uint8_t dataaccess; uint8_t dataaccess;
int16_t dataaddr; int16_t dataaddr;
uint8_t nscratch;
/* The width of the hartsel field. */ /* The width of the hartsel field. */
unsigned hartsellen; unsigned hartsellen;
@ -1774,28 +1777,66 @@ static int set_dcsr_ebreak(struct target *target, bool step)
RISCV_INFO(r); RISCV_INFO(r);
RISCV013_INFO(info); RISCV013_INFO(info);
riscv_reg_t original_dcsr, dcsr;
/* We want to twiddle some bits in the debug CSR so debugging works. */ if ((info->nscratch >= 1) && has_sufficient_progbuf(target, 8)) {
if (riscv_get_register(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK) uint64_t set_ebreak_bits = 0;
return ERROR_FAIL; uint64_t clr_ebreak_bits = 0;
original_dcsr = dcsr; if (r->riscv_ebreakm)
dcsr = set_field(dcsr, CSR_DCSR_STEP, step); set_ebreak_bits |= CSR_DCSR_EBREAKM;
dcsr = set_field(dcsr, CSR_DCSR_EBREAKM, r->riscv_ebreakm); else
dcsr = set_field(dcsr, CSR_DCSR_EBREAKS, r->riscv_ebreaks && riscv_supports_extension(target, 'S')); clr_ebreak_bits |= CSR_DCSR_EBREAKM;
dcsr = set_field(dcsr, CSR_DCSR_EBREAKU, r->riscv_ebreaku && riscv_supports_extension(target, 'U')); if (r->riscv_ebreaks && riscv_supports_extension(target, 'S'))
dcsr = set_field(dcsr, CSR_DCSR_EBREAKVS, r->riscv_ebreaku && riscv_supports_extension(target, 'H')); set_ebreak_bits |= CSR_DCSR_EBREAKS;
dcsr = set_field(dcsr, CSR_DCSR_EBREAKVU, r->riscv_ebreaku && riscv_supports_extension(target, 'H')); else
if (dcsr != original_dcsr && clr_ebreak_bits |= CSR_DCSR_EBREAKS;
riscv_set_register(target, GDB_REGNO_DCSR, dcsr) != ERROR_OK) if (r->riscv_ebreaku && riscv_supports_extension(target, 'U'))
return ERROR_FAIL; 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; info->dcsr_ebreak_is_set = true;
return ERROR_OK; return ERROR_OK;
} }
static int halt_set_dcsr_ebreak(struct target *target) static int halt_set_dcsr_ebreak(struct target *target)
{ {
RISCV_INFO(r);
RISCV013_INFO(info);
LOG_TARGET_DEBUG(target, "Halt to set DCSR.ebreak*"); LOG_TARGET_DEBUG(target, "Halt to set DCSR.ebreak*");
/* Remove this hart from the halt group. This won't work on all targets /* 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) { struct target_list *entry;
bool supported; struct list_head *targets;
if (set_group(target, &supported, 0, HALT_GROUP) != ERROR_OK)
return ERROR_FAIL; if (target->smp) {
if (!supported) targets = target->smp_targets;
LOG_TARGET_ERROR(target, "Couldn't place hart in halt group 0. " foreach_smp_target(entry, targets) {
"Some harts may be unexpectedly halted."); 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; int result = ERROR_OK;
int halt_result = ERROR_OK;
int resume_result = ERROR_OK;
r->prepped = true; halt_result = riscv013_halt_go(target);
if (riscv013_halt_go(target) != ERROR_OK || if (halt_result == ERROR_OK) {
set_dcsr_ebreak(target, false) != ERROR_OK || if (riscv013_on_step_or_resume(target, true, false) == ERROR_OK) {
riscv013_step_or_resume_current_hart(target, false) != ERROR_OK) { resume_result = riscv013_step_or_resume_current_hart(target, false);
result = ERROR_FAIL; 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 { } else {
target->state = TARGET_RUNNING; LOG_TARGET_INFO(target, "riscv013_halt_go failed");
target->debug_reason = DBG_REASON_NOTHALTED; result = ERROR_FAIL;
}
/* 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);
} }
return result; return result;
@ -1983,6 +2036,7 @@ static int examine(struct target *target)
info->datasize = get_field(hartinfo, DM_HARTINFO_DATASIZE); info->datasize = get_field(hartinfo, DM_HARTINFO_DATASIZE);
info->dataaccess = get_field(hartinfo, DM_HARTINFO_DATAACCESS); info->dataaccess = get_field(hartinfo, DM_HARTINFO_DATAACCESS);
info->dataaddr = get_field(hartinfo, DM_HARTINFO_DATAADDR); info->dataaddr = get_field(hartinfo, DM_HARTINFO_DATAADDR);
info->nscratch = get_field(hartinfo, DM_HARTINFO_NSCRATCH);
if (!get_field(dmstatus, DM_DMSTATUS_AUTHENTICATED)) { if (!get_field(dmstatus, DM_DMSTATUS_AUTHENTICATED)) {
LOG_TARGET_ERROR(target, "Debugger is not authenticated to target Debug Module. " 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->datacount = get_field(abstractcs, DM_ABSTRACTCS_DATACOUNT);
info->progbufsize = get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE); info->progbufsize = get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE);
LOG_TARGET_INFO(target, "datacount=%d progbufsize=%d",
info->datacount, info->progbufsize);
RISCV_INFO(r); RISCV_INFO(r);
r->impebreak = get_field(dmstatus, DM_DMSTATUS_IMPEBREAK); 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. */ /* Before doing anything else we must first enumerate the harts. */
if (dm->hart_count < 0) { if (dm->hart_count < 0) {
for (int i = 0; i < MIN(RISCV_MAX_HARTS, 1 << info->hartsellen); ++i) { 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; enum riscv_hart_state state_at_examine_start;
if (riscv_get_hart_state(target, &state_at_examine_start) != ERROR_OK) if (riscv_get_hart_state(target, &state_at_examine_start) != ERROR_OK)
return ERROR_FAIL; 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; const bool hart_halted_at_examine_start = state_at_examine_start == RISCV_STATE_HALTED;
if (!hart_halted_at_examine_start) { if (!hart_halted_at_examine_start) {
r->prepped = true; if (riscv013_halt_target(target) != ERROR_OK) {
if (riscv013_halt_go(target) != ERROR_OK) {
LOG_TARGET_ERROR(target, "Fatal: Hart %d failed to halt during %s", LOG_TARGET_ERROR(target, "Fatal: Hart %d failed to halt during %s",
info->index, __func__); info->index, __func__);
return ERROR_FAIL; 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->state = TARGET_HALTED;
target->debug_reason = hart_halted_at_examine_start ? DBG_REASON_UNDEFINED : DBG_REASON_DBGRQ; 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) if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
return ERROR_FAIL; return ERROR_FAIL;
if (get_field(dmstatus, DM_DMSTATUS_ANYHAVERESET)) { 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; info->dcsr_ebreak_is_set = false;
/* TODO: Can we make this more obvious to eg. a gdb user? */ /* TODO: Can we make this more obvious to eg. a gdb user? */
uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE | 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; 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, static int handle_became_unavailable(struct target *target,
enum riscv_hart_state previous_riscv_state) 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->data_bits = &riscv013_data_bits;
generic_info->print_info = &riscv013_print_info; 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->handle_became_unavailable = &handle_became_unavailable;
generic_info->tick = &tick; generic_info->tick = &tick;
@ -2893,7 +2979,9 @@ static int deassert_reset(struct target *target)
info->dmi_busy_delay = orig_dmi_busy_delay; 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->state = TARGET_HALTED;
target->debug_reason = DBG_REASON_DBGRQ; target->debug_reason = DBG_REASON_DBGRQ;
} else { } else {
@ -4906,89 +4994,64 @@ static int dm013_select_hart(struct target *target, int hart_index)
return ERROR_OK; 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)
{
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;
}
}
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);
}
if (dm013_select_hart(target, HART_INDEX_MULTIPLE) != ERROR_OK) {
free(hawindow);
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;
}
}
free(hawindow);
return ERROR_OK;
}
static int riscv013_halt_prep(struct target *target) static int riscv013_halt_prep(struct target *target)
{ {
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);
}
}
return ERROR_OK; return ERROR_OK;
} }
static int riscv013_halt_go(struct target *target) static int riscv013_halt_go(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 hart");
if (dm013_select_target(target) != ERROR_OK) {
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.");
}
}
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;
}
return ERROR_OK;
}
static int riscv013_halt_target(struct target *target)
{
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. */ /* 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);
@ -4997,13 +5060,12 @@ static int riscv013_halt_go(struct target *target)
for (size_t i = 0; i < 256; ++i) { for (size_t i = 0; i < 256; ++i) {
if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
return ERROR_FAIL; 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)) if (!get_field(dmstatus, DM_DMSTATUS_ANYRUNNING))
break; break;
} }
/* We declare success if no harts are running. One or more of them may be /* We declare success if hart is not running. It may be unavailable, though. */
* unavailable, though. */
if ((get_field(dmstatus, DM_DMSTATUS_ANYRUNNING))) { if ((get_field(dmstatus, DM_DMSTATUS_ANYRUNNING))) {
if (dm_read(target, &dmcontrol, DM_DMCONTROL) != ERROR_OK) 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); dmcontrol = set_field(dmcontrol, DM_DMCONTROL_HALTREQ, 0);
dm_write(target, DM_DMCONTROL, dmcontrol); dm_write(target, DM_DMCONTROL, dmcontrol);
if (dm->current_hartid == HART_INDEX_MULTIPLE) { /* Set state for the current target based on its dmstatus. */
target_list_t *entry; if (get_field(dmstatus, DM_DMSTATUS_ALLHALTED)) {
list_for_each_entry(entry, &dm->target_list, list) { target->state = TARGET_HALTED;
struct target *t = entry->target; if (target->debug_reason == DBG_REASON_NOTHALTED)
uint32_t t_dmstatus; target->debug_reason = DBG_REASON_DBGRQ;
if (get_field(dmstatus, DM_DMSTATUS_ALLHALTED) || } else if (get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL)) {
get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL)) { target->state = TARGET_UNAVAILABLE;
/* All harts are either halted or unavailable. No return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
* 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;
}
} }
return ERROR_OK; return ERROR_OK;
@ -5065,9 +5094,6 @@ static int riscv013_halt_go(struct target *target)
static int riscv013_resume_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); 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) static int riscv013_resume_prep(struct target *target)
{ {
assert(target->state == TARGET_HALTED); 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) 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) 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); 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 execute_fence(target);
return ERROR_OK; return ERROR_OK;
} }
/* Helper Functions. */ /* 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; return ERROR_FAIL;
if (set_dcsr_ebreak(target, step) != ERROR_OK) 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); 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; return ERROR_FAIL;
}
if (dm013_select_target(target) != ERROR_OK) {
return ERROR_FAIL;
}
dm013_info_t *dm = get_dm(target); dm013_info_t *dm = get_dm(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. */
@ -5264,24 +5296,50 @@ static int riscv013_step_or_resume_current_hart(struct target *target,
uint32_t dmstatus; uint32_t dmstatus;
for (size_t i = 0; i < 256; ++i) { for (size_t i = 0; i < 256; ++i) {
usleep(10); usleep(10);
if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) {
return ERROR_FAIL; LOG_TARGET_INFO(target, "dmstatus_read failed");
if (get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL))
return ERROR_FAIL; 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) if (get_field(dmstatus, DM_DMSTATUS_ALLRESUMEACK) == 0)
continue; continue;
if (step && get_field(dmstatus, DM_DMSTATUS_ALLHALTED) == 0) if (step && get_field(dmstatus, DM_DMSTATUS_ALLHALTED) == 0)
continue; continue;
dm_write(target, DM_DMCONTROL, dmcontrol); 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; return ERROR_OK;
} }
dm_write(target, DM_DMCONTROL, dmcontrol); dm_write(target, DM_DMCONTROL, dmcontrol);
LOG_TARGET_ERROR(target, "unable to resume"); 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; return ERROR_FAIL;
}
LOG_TARGET_ERROR(target, " dmstatus=0x%08x", dmstatus); LOG_TARGET_ERROR(target, " dmstatus=0x%08x", dmstatus);
if (step) { if (step) {
@ -5290,6 +5348,7 @@ static int riscv013_step_or_resume_current_hart(struct target *target,
return ERROR_OK; return ERROR_OK;
} }
LOG_TARGET_INFO(target, "resume_current_hart failed");
return ERROR_FAIL; return ERROR_FAIL;
} }

View File

@ -534,6 +534,10 @@ static void riscv_deinit_target(struct target *target)
{ {
LOG_TARGET_DEBUG(target, "riscv_deinit_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 riscv_info *info = target->arch_info;
struct target_type *tt = get_target_type(target); struct target_type *tt = get_target_type(target);
if (!tt) if (!tt)
@ -1920,28 +1924,31 @@ int riscv_halt(struct target *target)
LOG_TARGET_DEBUG(target, "halting all harts"); LOG_TARGET_DEBUG(target, "halting all harts");
/* Only halt a hart if it has been examined (was available) */
int result = ERROR_OK; int result = ERROR_OK;
if (target->smp) { if (target->smp) {
struct target_list *tlist; struct target_list *tlist;
foreach_smp_target(tlist, target->smp_targets) { foreach_smp_target(tlist, target->smp_targets) {
struct target *t = tlist->target; struct target *t = tlist->target;
if (halt_prep(t) != ERROR_OK) if (target_was_examined(t))
result = ERROR_FAIL; if (halt_prep(t) != ERROR_OK)
result = ERROR_FAIL;
} }
foreach_smp_target(tlist, target->smp_targets) { foreach_smp_target(tlist, target->smp_targets) {
struct target *t = tlist->target; struct target *t = tlist->target;
struct riscv_info *i = riscv_info(t); struct riscv_info *i = riscv_info(t);
if (i->prepped) { if (target_was_examined(t))
if (halt_go(t) != ERROR_OK) if (i->prepped)
result = ERROR_FAIL; if (halt_go(t) != ERROR_OK)
} result = ERROR_FAIL;
} }
foreach_smp_target(tlist, target->smp_targets) { foreach_smp_target(tlist, target->smp_targets) {
struct target *t = tlist->target; struct target *t = tlist->target;
if (halt_finish(t) != ERROR_OK) if (target_was_examined(t))
return ERROR_FAIL; if (halt_finish(t) != ERROR_OK)
return ERROR_FAIL;
} }
} else { } else {
@ -3039,11 +3046,21 @@ static int riscv_poll_hart(struct target *target, enum riscv_next_action *next_a
return ERROR_FAIL; 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) { if (target->state == TARGET_UNKNOWN || state != previous_riscv_state) {
switch (state) { switch (state) {
case RISCV_STATE_HALTED: 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_INFO(target, "became available (halted)");
}
LOG_TARGET_DEBUG(target, " triggered a halt; previous_target_state=%d", LOG_TARGET_DEBUG(target, " triggered a halt; previous_target_state=%d",
previous_target_state); previous_target_state);
@ -3090,8 +3107,9 @@ static int riscv_poll_hart(struct target *target, enum riscv_next_action *next_a
break; break;
case RISCV_STATE_RUNNING: 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_INFO(target, "became available (running)");
}
LOG_TARGET_DEBUG(target, " triggered running"); LOG_TARGET_DEBUG(target, " triggered running");
target->state = TARGET_RUNNING; target->state = TARGET_RUNNING;
@ -3102,8 +3120,16 @@ static int riscv_poll_hart(struct target *target, enum riscv_next_action *next_a
break; break;
case RISCV_STATE_UNAVAILABLE: case RISCV_STATE_UNAVAILABLE:
LOG_TARGET_DEBUG(target, " became unavailable"); if (previous_riscv_state == RISCV_STATE_HALTED) {
LOG_TARGET_INFO(target, "became unavailable."); 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; target->state = TARGET_UNAVAILABLE;
if (r->handle_became_unavailable && if (r->handle_became_unavailable &&
r->handle_became_unavailable(target, previous_riscv_state) != ERROR_OK) r->handle_became_unavailable(target, previous_riscv_state) != ERROR_OK)
@ -3206,8 +3232,10 @@ int riscv_openocd_poll(struct target *target)
continue; continue;
enum riscv_next_action next_action; 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; return ERROR_FAIL;
}
switch (next_action) { switch (next_action) {
case RPH_NONE: case RPH_NONE:
@ -3264,8 +3292,10 @@ int riscv_openocd_poll(struct target *target)
foreach_smp_target(entry, targets) { foreach_smp_target(entry, targets) {
struct target *t = entry->target; struct target *t = entry->target;
struct riscv_info *info = riscv_info(t); 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; return ERROR_FAIL;
}
} }
/* Sample memory if any target is running. */ /* Sample memory if any target is running. */

View File

@ -213,6 +213,8 @@ struct riscv_info {
enum riscv_hart_state previous_riscv_state); enum riscv_hart_state previous_riscv_state);
int (*handle_became_running)(struct target *target, int (*handle_became_running)(struct target *target,
enum riscv_hart_state previous_riscv_state); 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, int (*handle_became_unavailable)(struct target *target,
enum riscv_hart_state previous_riscv_state); enum riscv_hart_state previous_riscv_state);

View File

@ -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 # Wait up to 1 second for target to halt. Why 1sec? Cause
# the JTAG tap reset signal might be hooked to a slow # the JTAG tap reset signal might be hooked to a slow
# resistor/capacitor circuit - and it might take a while # resistor/capacitor circuit - and it might take a while
@ -142,8 +147,11 @@ proc ocd_process_reset_inner { MODE } {
# Did we succeed? # Did we succeed?
set s [$t curstate] set s [$t curstate]
if { $s == "unavailable" } {
continue
}
if { $s != "halted" } { 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] } { if { ![$t was_examined] && [$t examine_deferred] } {
continue continue
} }
if { [$t curstate] == "unavailable" } {
continue
}
set err [catch "$t arp_waitstate halted 5000"] set err [catch "$t arp_waitstate halted 5000"]
# Did it halt? # Did it halt?

View File

@ -486,8 +486,10 @@ int target_poll(struct target *target)
} }
retval = target->type->poll(target); retval = target->type->poll(target);
if (retval != ERROR_OK) if (retval != ERROR_OK) {
LOG_TARGET_INFO(target, "target->type->poll failed");
return retval; return retval;
}
if (target->halt_issued) { if (target->halt_issued) {
if (target->state == TARGET_HALTED) 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); target_call_event_callbacks(target, TARGET_EVENT_EXAMINE_START);
bool defer_state = target->defer_examine;
int retval = target->type->examine(target); int retval = target->type->examine(target);
if (retval != ERROR_OK) { if (retval != ERROR_OK) {
LOG_TARGET_ERROR(target, "Examination failed"); LOG_TARGET_ERROR(target, "Examination failed");
@ -685,11 +688,17 @@ int target_examine_one(struct target *target)
return retval; return retval;
} }
LOG_USER("[%s] Target successfully examined.", target_name(target)); if (target->defer_examine) {
target_set_examined(target); 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); target_call_event_callbacks(target, TARGET_EVENT_EXAMINE_END);
LOG_TARGET_INFO(target, "Examination succeed"); LOG_TARGET_DEBUG(target, "Examination succeed");
return ERROR_OK; return ERROR_OK;
} }
@ -5261,13 +5270,19 @@ COMMAND_HANDLER(handle_target_examine)
return ERROR_OK; return ERROR_OK;
} }
bool defer_state = target->defer_examine;
int retval = target->type->examine(target); int retval = target->type->examine(target);
if (retval != ERROR_OK) { if (retval != ERROR_OK) {
target_reset_examined(target); target_reset_examined(target);
return retval; 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; return ERROR_OK;
} }