Fix semihosting for multicore targets (#478)
* WIP making semihosting work with -rtos hwthread. Change-Id: Icb46f3eeedc1391e8fdc73c3ad8036f20267eb2e * More WIP. Change-Id: I670a6e1ba2a13a6ef2ae303a99559a16fdd1bbfb * Fix halting due to a trigger. Change-Id: Ie7caa8dde9518bcd5440e34cf31ed0d30ebf29ad * Fix multicore semihosting without halt groups. Change-Id: I53587e5234308ed2cc30a7132c86e4c94eb176c4 * WIP Change-Id: I40630543b08d8b533726cb3f63aa60a62be8ef40 * Fix single core semihosting. This was the last bug! Change-Id: I593abac027fa9707f48b7f58163d7089574a0e28 * Fix whitespace. Change-Id: I285c152970b87864c63803fae61312e5b79dfe6d
This commit is contained in:
parent
1524487a13
commit
4f9e2d7171
|
@ -267,7 +267,7 @@ static int riscv_gdb_v_packet(struct connection *connection, const char *packet,
|
|||
target_call_event_callbacks(target, TARGET_EVENT_GDB_START);
|
||||
target_call_event_callbacks(target, TARGET_EVENT_RESUME_START);
|
||||
riscv_set_all_rtos_harts(target);
|
||||
riscv_resume(target, 1, 0, 0, 0);
|
||||
riscv_resume(target, 1, 0, 0, 0, false);
|
||||
target->state = TARGET_RUNNING;
|
||||
gdb_set_frontend_state_running(connection);
|
||||
target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
|
||||
|
|
|
@ -3645,7 +3645,6 @@ struct target_type riscv013_target = {
|
|||
|
||||
.poll = &riscv_openocd_poll,
|
||||
.halt = &riscv_halt,
|
||||
.resume = &riscv_resume,
|
||||
.step = &riscv_openocd_step,
|
||||
|
||||
.assert_reset = assert_reset,
|
||||
|
@ -4539,7 +4538,7 @@ int riscv013_test_compliance(struct target *target)
|
|||
|
||||
/* resumereq */
|
||||
/* This bit is not actually readable according to the spec, so nothing to check.*/
|
||||
COMPLIANCE_MUST_PASS(riscv_resume(target, true, 0, false, false));
|
||||
COMPLIANCE_MUST_PASS(riscv_resume(target, true, 0, false, false, false));
|
||||
|
||||
/* Halt all harts again so the test can continue.*/
|
||||
COMPLIANCE_MUST_PASS(riscv_halt(target));
|
||||
|
|
|
@ -1059,11 +1059,13 @@ int halt_prep(struct target *target)
|
|||
if (!riscv_hart_enabled(target, i))
|
||||
continue;
|
||||
|
||||
LOG_DEBUG("prep hart %d", i);
|
||||
LOG_DEBUG("[%s] prep hart, debug_reason=%d", target_name(target),
|
||||
target->debug_reason);
|
||||
if (riscv_set_current_hartid(target, i) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
if (riscv_is_halted(target)) {
|
||||
LOG_DEBUG("Hart %d is already halted.", i);
|
||||
LOG_DEBUG("Hart %d is already halted (reason=%d).", i,
|
||||
target->debug_reason);
|
||||
} else {
|
||||
if (r->halt_prep(target) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
@ -1305,7 +1307,7 @@ static int resume_finish(struct target *target)
|
|||
* @par single_hart When true, only resume a single hart even if SMP is
|
||||
* configured. This is used to run algorithms on just one hart.
|
||||
*/
|
||||
int riscv_resume_internal(
|
||||
int riscv_resume(
|
||||
struct target *target,
|
||||
int current,
|
||||
target_addr_t address,
|
||||
|
@ -1353,10 +1355,10 @@ int riscv_resume_internal(
|
|||
return result;
|
||||
}
|
||||
|
||||
int riscv_resume(struct target *target, int current, target_addr_t address,
|
||||
static int riscv_target_resume(struct target *target, int current, target_addr_t address,
|
||||
int handle_breakpoints, int debug_execution)
|
||||
{
|
||||
return riscv_resume_internal(target, current, address, handle_breakpoints,
|
||||
return riscv_resume(target, current, address, handle_breakpoints,
|
||||
debug_execution, false);
|
||||
}
|
||||
|
||||
|
@ -1718,7 +1720,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
|
|||
|
||||
/* Run algorithm */
|
||||
LOG_DEBUG("resume at 0x%" TARGET_PRIxADDR, entry_point);
|
||||
if (riscv_resume_internal(target, 0, entry_point, 0, 0, true) != ERROR_OK)
|
||||
if (riscv_resume(target, 0, entry_point, 0, 0, true) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
int64_t start = timeval_ms();
|
||||
|
@ -1921,9 +1923,9 @@ static enum riscv_poll_hart riscv_poll_hart(struct target *target, int hartid)
|
|||
return RPH_NO_CHANGE;
|
||||
}
|
||||
|
||||
int set_debug_reason(struct target *target, int hartid)
|
||||
int set_debug_reason(struct target *target, enum riscv_halt_reason halt_reason)
|
||||
{
|
||||
switch (riscv_halt_reason(target, hartid)) {
|
||||
switch (halt_reason) {
|
||||
case RISCV_HALT_BREAKPOINT:
|
||||
target->debug_reason = DBG_REASON_BREAKPOINT;
|
||||
break;
|
||||
|
@ -1943,6 +1945,7 @@ int set_debug_reason(struct target *target, int hartid)
|
|||
case RISCV_HALT_ERROR:
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
LOG_DEBUG("[%s] debug_reason=%d", target_name(target), target->debug_reason);
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
|
@ -1973,7 +1976,8 @@ int riscv_openocd_poll(struct target *target)
|
|||
LOG_DEBUG(" hart %d halted", halted_hart);
|
||||
|
||||
target->state = TARGET_HALTED;
|
||||
if (set_debug_reason(target, halted_hart) != ERROR_OK)
|
||||
enum riscv_halt_reason halt_reason = riscv_halt_reason(target, halted_hart);
|
||||
if (set_debug_reason(target, halt_reason) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
target->rtos->current_threadid = halted_hart + 1;
|
||||
|
@ -1989,45 +1993,75 @@ int riscv_openocd_poll(struct target *target)
|
|||
riscv_halt(target);
|
||||
|
||||
} else if (target->smp) {
|
||||
bool halt_discovered = false;
|
||||
unsigned halts_discovered = 0;
|
||||
unsigned total_targets = 0;
|
||||
bool newly_halted[128] = {0};
|
||||
unsigned should_remain_halted = 0;
|
||||
unsigned should_resume = 0;
|
||||
unsigned i = 0;
|
||||
for (struct target_list *list = target->head; list != NULL;
|
||||
list = list->next, i++) {
|
||||
total_targets++;
|
||||
struct target *t = list->target;
|
||||
riscv_info_t *r = riscv_info(t);
|
||||
assert(i < DIM(newly_halted));
|
||||
enum riscv_poll_hart out = riscv_poll_hart(t, r->current_hartid);
|
||||
switch (out) {
|
||||
case RPH_NO_CHANGE:
|
||||
break;
|
||||
case RPH_DISCOVERED_RUNNING:
|
||||
t->state = TARGET_RUNNING;
|
||||
t->debug_reason = DBG_REASON_NOTHALTED;
|
||||
break;
|
||||
case RPH_DISCOVERED_HALTED:
|
||||
halt_discovered = true;
|
||||
newly_halted[i] = true;
|
||||
t->state = TARGET_HALTED;
|
||||
if (set_debug_reason(t, r->current_hartid) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
break;
|
||||
case RPH_ERROR:
|
||||
case RPH_NO_CHANGE:
|
||||
if (t->state == TARGET_HALTED)
|
||||
should_remain_halted++;
|
||||
break;
|
||||
case RPH_DISCOVERED_RUNNING:
|
||||
t->state = TARGET_RUNNING;
|
||||
t->debug_reason = DBG_REASON_NOTHALTED;
|
||||
break;
|
||||
case RPH_DISCOVERED_HALTED:
|
||||
halts_discovered++;
|
||||
newly_halted[i] = true;
|
||||
t->state = TARGET_HALTED;
|
||||
enum riscv_halt_reason halt_reason =
|
||||
riscv_halt_reason(t, r->current_hartid);
|
||||
if (set_debug_reason(t, halt_reason) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
if (halt_reason == RISCV_HALT_BREAKPOINT) {
|
||||
int retval;
|
||||
switch (riscv_semihosting(t, &retval)) {
|
||||
case SEMI_NONE:
|
||||
case SEMI_WAITING:
|
||||
/* This hart should remain halted. */
|
||||
should_remain_halted++;
|
||||
break;
|
||||
case SEMI_HANDLED:
|
||||
/* This hart should be resumed, along with any other
|
||||
* harts that halted due to haltgroups. */
|
||||
should_resume++;
|
||||
break;
|
||||
case SEMI_ERROR:
|
||||
return retval;
|
||||
}
|
||||
} else if (halt_reason != RISCV_HALT_GROUP) {
|
||||
should_remain_halted++;
|
||||
}
|
||||
break;
|
||||
|
||||
case RPH_ERROR:
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
if (halt_discovered) {
|
||||
i = 0;
|
||||
for (struct target_list *list = target->head; list != NULL;
|
||||
list = list->next, i++) {
|
||||
struct target *t = list->target;
|
||||
if (newly_halted[i])
|
||||
target_call_event_callbacks(t, TARGET_EVENT_HALTED);
|
||||
}
|
||||
|
||||
LOG_DEBUG("Halt other targets in this SMP group.");
|
||||
LOG_DEBUG("should_remain_halted=%d, should_resume=%d",
|
||||
should_remain_halted, should_resume);
|
||||
if (should_remain_halted && should_resume) {
|
||||
LOG_WARNING("%d harts should remain halted, and %d should resume.",
|
||||
should_remain_halted, should_resume);
|
||||
}
|
||||
if (should_remain_halted) {
|
||||
LOG_DEBUG("halt all");
|
||||
riscv_halt(target);
|
||||
} else if (should_resume) {
|
||||
LOG_DEBUG("resume all");
|
||||
riscv_resume(target, true, 0, 0, 0, false);
|
||||
}
|
||||
return ERROR_OK;
|
||||
|
||||
|
@ -2042,18 +2076,30 @@ int riscv_openocd_poll(struct target *target)
|
|||
halted_hart = riscv_current_hartid(target);
|
||||
LOG_DEBUG(" hart %d halted", halted_hart);
|
||||
|
||||
if (set_debug_reason(target, halted_hart) != ERROR_OK)
|
||||
enum riscv_halt_reason halt_reason = riscv_halt_reason(target, halted_hart);
|
||||
if (set_debug_reason(target, halt_reason) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
target->state = TARGET_HALTED;
|
||||
}
|
||||
|
||||
if (target->debug_reason == DBG_REASON_BREAKPOINT) {
|
||||
int retval;
|
||||
if (riscv_semihosting(target, &retval) != 0)
|
||||
return retval;
|
||||
switch (riscv_semihosting(target, &retval)) {
|
||||
case SEMI_NONE:
|
||||
case SEMI_WAITING:
|
||||
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
|
||||
break;
|
||||
case SEMI_HANDLED:
|
||||
if (riscv_resume(target, true, 0, 0, 0, false) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
break;
|
||||
case SEMI_ERROR:
|
||||
return retval;
|
||||
}
|
||||
} else {
|
||||
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
|
||||
}
|
||||
|
||||
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
|
@ -2777,7 +2823,7 @@ struct target_type riscv_target = {
|
|||
.poll = old_or_new_riscv_poll,
|
||||
|
||||
.halt = riscv_halt,
|
||||
.resume = riscv_resume,
|
||||
.resume = riscv_target_resume,
|
||||
.step = old_or_new_riscv_step,
|
||||
|
||||
.assert_reset = riscv_assert_reset,
|
||||
|
|
|
@ -239,7 +239,8 @@ int riscv_resume(
|
|||
int current,
|
||||
target_addr_t address,
|
||||
int handle_breakpoints,
|
||||
int debug_execution
|
||||
int debug_execution,
|
||||
bool single_hart
|
||||
);
|
||||
|
||||
int riscv_openocd_step(
|
||||
|
@ -337,7 +338,13 @@ int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_wp_addre
|
|||
int riscv_init_registers(struct target *target);
|
||||
|
||||
void riscv_semihosting_init(struct target *target);
|
||||
int riscv_semihosting(struct target *target, int *retval);
|
||||
typedef enum {
|
||||
SEMI_NONE, /* Not halted for a semihosting call. */
|
||||
SEMI_HANDLED, /* Call handled, and target was resumed. */
|
||||
SEMI_WAITING, /* Call handled, target is halted waiting until we can resume. */
|
||||
SEMI_ERROR /* Something went wrong. */
|
||||
} semihosting_result_t;
|
||||
semihosting_result_t riscv_semihosting(struct target *target, int *retval);
|
||||
|
||||
void riscv_add_bscan_tunneled_scan(struct target *target, struct scan_field *field,
|
||||
riscv_bscan_tunneled_scan_context_t *ctxt);
|
||||
|
|
|
@ -60,35 +60,35 @@ void riscv_semihosting_init(struct target *target)
|
|||
/**
|
||||
* Check for and process a semihosting request using the ARM protocol). This
|
||||
* is meant to be called when the target is stopped due to a debug mode entry.
|
||||
* If the value 0 is returned then there was nothing to process. A non-zero
|
||||
* return value signifies that a request was processed and the target resumed,
|
||||
* or an error was encountered, in which case the caller must return
|
||||
* immediately.
|
||||
*
|
||||
* @param target Pointer to the target to process.
|
||||
* @param retval Pointer to a location where the return code will be stored
|
||||
* @return non-zero value if a request was processed or an error encountered
|
||||
*/
|
||||
int riscv_semihosting(struct target *target, int *retval)
|
||||
semihosting_result_t riscv_semihosting(struct target *target, int *retval)
|
||||
{
|
||||
struct semihosting *semihosting = target->semihosting;
|
||||
if (!semihosting)
|
||||
return 0;
|
||||
if (!semihosting) {
|
||||
LOG_DEBUG(" -> NONE (!semihosting)");
|
||||
return SEMI_NONE;
|
||||
}
|
||||
|
||||
if (!semihosting->is_active)
|
||||
return 0;
|
||||
if (!semihosting->is_active) {
|
||||
LOG_DEBUG(" -> NONE (!semihosting->is_active)");
|
||||
return SEMI_NONE;
|
||||
}
|
||||
|
||||
riscv_reg_t dpc;
|
||||
int result = riscv_get_register(target, &dpc, GDB_REGNO_DPC);
|
||||
riscv_reg_t pc;
|
||||
int result = riscv_get_register(target, &pc, GDB_REGNO_PC);
|
||||
if (result != ERROR_OK)
|
||||
return 0;
|
||||
return SEMI_ERROR;
|
||||
|
||||
uint8_t tmp[12];
|
||||
|
||||
/* Read the current instruction, including the bracketing */
|
||||
*retval = target_read_memory(target, dpc - 4, 2, 6, tmp);
|
||||
*retval = target_read_memory(target, pc - 4, 2, 6, tmp);
|
||||
if (*retval != ERROR_OK)
|
||||
return 0;
|
||||
return SEMI_ERROR;
|
||||
|
||||
/*
|
||||
* The instructions that trigger a semihosting call,
|
||||
|
@ -101,12 +101,12 @@ int riscv_semihosting(struct target *target, int *retval)
|
|||
uint32_t pre = target_buffer_get_u32(target, tmp);
|
||||
uint32_t ebreak = target_buffer_get_u32(target, tmp + 4);
|
||||
uint32_t post = target_buffer_get_u32(target, tmp + 8);
|
||||
LOG_DEBUG("check %08x %08x %08x from 0x%" PRIx64 "-4", pre, ebreak, post, dpc);
|
||||
LOG_DEBUG("check %08x %08x %08x from 0x%" PRIx64 "-4", pre, ebreak, post, pc);
|
||||
|
||||
if (pre != 0x01f01013 || ebreak != 0x00100073 || post != 0x40705013) {
|
||||
|
||||
/* Not the magic sequence defining semihosting. */
|
||||
return 0;
|
||||
LOG_DEBUG(" -> NONE (no magic)");
|
||||
return SEMI_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -114,18 +114,21 @@ int riscv_semihosting(struct target *target, int *retval)
|
|||
* operation to complete.
|
||||
*/
|
||||
if (!semihosting->hit_fileio) {
|
||||
|
||||
/* RISC-V uses A0 and A1 to pass function arguments */
|
||||
riscv_reg_t r0;
|
||||
riscv_reg_t r1;
|
||||
|
||||
result = riscv_get_register(target, &r0, GDB_REGNO_A0);
|
||||
if (result != ERROR_OK)
|
||||
return 0;
|
||||
if (result != ERROR_OK) {
|
||||
LOG_DEBUG(" -> ERROR (couldn't read a0)");
|
||||
return SEMI_ERROR;
|
||||
}
|
||||
|
||||
result = riscv_get_register(target, &r1, GDB_REGNO_A1);
|
||||
if (result != ERROR_OK)
|
||||
return 0;
|
||||
if (result != ERROR_OK) {
|
||||
LOG_DEBUG(" -> ERROR (couldn't read a1)");
|
||||
return SEMI_ERROR;
|
||||
}
|
||||
|
||||
semihosting->op = r0;
|
||||
semihosting->param = r1;
|
||||
|
@ -136,11 +139,12 @@ int riscv_semihosting(struct target *target, int *retval)
|
|||
*retval = semihosting_common(target);
|
||||
if (*retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed semihosting operation");
|
||||
return 0;
|
||||
return SEMI_ERROR;
|
||||
}
|
||||
} else {
|
||||
/* Unknown operation number, not a semihosting call. */
|
||||
return 0;
|
||||
LOG_DEBUG(" -> NONE (unknown operation number)");
|
||||
return SEMI_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,16 +154,16 @@ int riscv_semihosting(struct target *target, int *retval)
|
|||
*/
|
||||
if (semihosting->is_resumable && !semihosting->hit_fileio) {
|
||||
/* Resume right after the EBREAK 4 bytes instruction. */
|
||||
*retval = target_resume(target, 0, dpc+4, 0, 0);
|
||||
if (*retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to resume target");
|
||||
return 0;
|
||||
}
|
||||
*retval = riscv_set_register(target, GDB_REGNO_PC, pc + 4);
|
||||
if (*retval != ERROR_OK)
|
||||
return SEMI_ERROR;
|
||||
|
||||
return 1;
|
||||
LOG_DEBUG(" -> HANDLED");
|
||||
return SEMI_HANDLED;
|
||||
}
|
||||
|
||||
return 0;
|
||||
LOG_DEBUG(" -> WAITING");
|
||||
return SEMI_WAITING;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
|
@ -171,7 +175,7 @@ int riscv_semihosting(struct target *target, int *retval)
|
|||
*/
|
||||
static int riscv_semihosting_setup(struct target *target, int enable)
|
||||
{
|
||||
LOG_DEBUG("enable=%d", enable);
|
||||
LOG_DEBUG("[%s] enable=%d", target_name(target), enable);
|
||||
|
||||
struct semihosting *semihosting = target->semihosting;
|
||||
if (semihosting)
|
||||
|
|
Loading…
Reference in New Issue