target/cortex_m: add SMP support for Cortex-M
Cortex-M support for SMP multicore targets. This SMP implementation unlike older ones does not act on the first halted target found. It polls targets until a SMP group is finished and stores eventual 'halted' events instead of emitting them. As soon as polling of a group is done, poll proceeds with SMP related tasks. This approach improves detection of a reason why debug stopped - a correct reason is detected for all targets, not only for the first found. Drawback: SMP target group should be defined in the same order as the targets were defined. Obsolete gdb 'J' packet/smp_gdb command core switching is not implemented, use with rtos hwthread. Only one core is resumed if debug_execution is requested. Some ideas taken from Graham Sanderson's [4936] and src/target/aarch64.c Added error checking of armv7m_restore_context(). Change-Id: I60f5b79e74b624dc2b5835ff10e38ac2ccb23792 Link: [4936]: target/cortex_m: Add smp support for Cortex M | https://review.openocd.org/c/openocd/+/4936 Signed-off-by: Tomas Vanek <vanekt@fbl.cz> Reviewed-on: https://review.openocd.org/c/openocd/+/7239 Tested-by: jenkins Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
This commit is contained in:
parent
083100fca3
commit
59763653c6
|
@ -28,6 +28,7 @@
|
||||||
#include "register.h"
|
#include "register.h"
|
||||||
#include "arm_opcodes.h"
|
#include "arm_opcodes.h"
|
||||||
#include "arm_semihosting.h"
|
#include "arm_semihosting.h"
|
||||||
|
#include "smp.h"
|
||||||
#include <helper/time_support.h>
|
#include <helper/time_support.h>
|
||||||
#include <rtt/rtt.h>
|
#include <rtt/rtt.h>
|
||||||
|
|
||||||
|
@ -871,7 +872,7 @@ static int cortex_m_debug_entry(struct target *target)
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cortex_m_poll(struct target *target)
|
static int cortex_m_poll_one(struct target *target)
|
||||||
{
|
{
|
||||||
int detected_failure = ERROR_OK;
|
int detected_failure = ERROR_OK;
|
||||||
int retval = ERROR_OK;
|
int retval = ERROR_OK;
|
||||||
|
@ -934,21 +935,26 @@ static int cortex_m_poll(struct target *target)
|
||||||
|
|
||||||
if ((prev_target_state == TARGET_RUNNING) || (prev_target_state == TARGET_RESET)) {
|
if ((prev_target_state == TARGET_RUNNING) || (prev_target_state == TARGET_RESET)) {
|
||||||
retval = cortex_m_debug_entry(target);
|
retval = cortex_m_debug_entry(target);
|
||||||
if (retval != ERROR_OK)
|
|
||||||
|
/* arm_semihosting needs to know registers, don't run if debug entry returned error */
|
||||||
|
if (retval == ERROR_OK && arm_semihosting(target, &retval) != 0)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
if (arm_semihosting(target, &retval) != 0)
|
if (target->smp) {
|
||||||
return retval;
|
LOG_TARGET_DEBUG(target, "postpone target event 'halted'");
|
||||||
|
target->smp_halt_event_postponed = true;
|
||||||
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
|
} else {
|
||||||
|
/* regardless of errors returned in previous code update state */
|
||||||
|
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (prev_target_state == TARGET_DEBUG_RUNNING) {
|
if (prev_target_state == TARGET_DEBUG_RUNNING) {
|
||||||
retval = cortex_m_debug_entry(target);
|
retval = cortex_m_debug_entry(target);
|
||||||
if (retval != ERROR_OK)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
target_call_event_callbacks(target, TARGET_EVENT_DEBUG_HALTED);
|
target_call_event_callbacks(target, TARGET_EVENT_DEBUG_HALTED);
|
||||||
}
|
}
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target->state == TARGET_UNKNOWN) {
|
if (target->state == TARGET_UNKNOWN) {
|
||||||
|
@ -981,7 +987,104 @@ static int cortex_m_poll(struct target *target)
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cortex_m_halt(struct target *target)
|
static int cortex_m_halt_one(struct target *target);
|
||||||
|
|
||||||
|
static int cortex_m_smp_halt_all(struct list_head *smp_targets)
|
||||||
|
{
|
||||||
|
int retval = ERROR_OK;
|
||||||
|
struct target_list *head;
|
||||||
|
|
||||||
|
foreach_smp_target(head, smp_targets) {
|
||||||
|
struct target *curr = head->target;
|
||||||
|
if (!target_was_examined(curr))
|
||||||
|
continue;
|
||||||
|
if (curr->state == TARGET_HALTED)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int ret2 = cortex_m_halt_one(curr);
|
||||||
|
if (retval == ERROR_OK)
|
||||||
|
retval = ret2; /* store the first error code ignore others */
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cortex_m_smp_post_halt_poll(struct list_head *smp_targets)
|
||||||
|
{
|
||||||
|
int retval = ERROR_OK;
|
||||||
|
struct target_list *head;
|
||||||
|
|
||||||
|
foreach_smp_target(head, smp_targets) {
|
||||||
|
struct target *curr = head->target;
|
||||||
|
if (!target_was_examined(curr))
|
||||||
|
continue;
|
||||||
|
/* skip targets that were already halted */
|
||||||
|
if (curr->state == TARGET_HALTED)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int ret2 = cortex_m_poll_one(curr);
|
||||||
|
if (retval == ERROR_OK)
|
||||||
|
retval = ret2; /* store the first error code ignore others */
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cortex_m_poll_smp(struct list_head *smp_targets)
|
||||||
|
{
|
||||||
|
int retval = ERROR_OK;
|
||||||
|
struct target_list *head;
|
||||||
|
bool halted = false;
|
||||||
|
|
||||||
|
foreach_smp_target(head, smp_targets) {
|
||||||
|
struct target *curr = head->target;
|
||||||
|
if (curr->smp_halt_event_postponed) {
|
||||||
|
halted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (halted) {
|
||||||
|
retval = cortex_m_smp_halt_all(smp_targets);
|
||||||
|
|
||||||
|
int ret2 = cortex_m_smp_post_halt_poll(smp_targets);
|
||||||
|
if (retval == ERROR_OK)
|
||||||
|
retval = ret2; /* store the first error code ignore others */
|
||||||
|
|
||||||
|
foreach_smp_target(head, smp_targets) {
|
||||||
|
struct target *curr = head->target;
|
||||||
|
if (!curr->smp_halt_event_postponed)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
curr->smp_halt_event_postponed = false;
|
||||||
|
if (curr->state == TARGET_HALTED) {
|
||||||
|
LOG_TARGET_DEBUG(curr, "sending postponed target event 'halted'");
|
||||||
|
target_call_event_callbacks(curr, TARGET_EVENT_HALTED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* There is no need to set gdb_service->target
|
||||||
|
* as hwthread_update_threads() selects an interesting thread
|
||||||
|
* by its own
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cortex_m_poll(struct target *target)
|
||||||
|
{
|
||||||
|
int retval = cortex_m_poll_one(target);
|
||||||
|
|
||||||
|
if (target->smp) {
|
||||||
|
struct target_list *last;
|
||||||
|
last = list_last_entry(target->smp_targets, struct target_list, lh);
|
||||||
|
if (target == last->target)
|
||||||
|
/* After the last target in SMP group has been polled
|
||||||
|
* check for postponed halted events and eventually halt and re-poll
|
||||||
|
* other targets */
|
||||||
|
cortex_m_poll_smp(target->smp_targets);
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cortex_m_halt_one(struct target *target)
|
||||||
{
|
{
|
||||||
LOG_TARGET_DEBUG(target, "target->state: %s", target_state_name(target));
|
LOG_TARGET_DEBUG(target, "target->state: %s", target_state_name(target));
|
||||||
|
|
||||||
|
@ -1019,6 +1122,14 @@ static int cortex_m_halt(struct target *target)
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int cortex_m_halt(struct target *target)
|
||||||
|
{
|
||||||
|
if (target->smp)
|
||||||
|
return cortex_m_smp_halt_all(target->smp_targets);
|
||||||
|
else
|
||||||
|
return cortex_m_halt_one(target);
|
||||||
|
}
|
||||||
|
|
||||||
static int cortex_m_soft_reset_halt(struct target *target)
|
static int cortex_m_soft_reset_halt(struct target *target)
|
||||||
{
|
{
|
||||||
struct cortex_m_common *cortex_m = target_to_cm(target);
|
struct cortex_m_common *cortex_m = target_to_cm(target);
|
||||||
|
@ -1096,8 +1207,8 @@ void cortex_m_enable_breakpoints(struct target *target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cortex_m_resume(struct target *target, int current,
|
static int cortex_m_restore_one(struct target *target, bool current,
|
||||||
target_addr_t address, int handle_breakpoints, int debug_execution)
|
target_addr_t *address, bool handle_breakpoints, bool debug_execution)
|
||||||
{
|
{
|
||||||
struct armv7m_common *armv7m = target_to_armv7m(target);
|
struct armv7m_common *armv7m = target_to_armv7m(target);
|
||||||
struct breakpoint *breakpoint = NULL;
|
struct breakpoint *breakpoint = NULL;
|
||||||
|
@ -1105,7 +1216,7 @@ static int cortex_m_resume(struct target *target, int current,
|
||||||
struct reg *r;
|
struct reg *r;
|
||||||
|
|
||||||
if (target->state != TARGET_HALTED) {
|
if (target->state != TARGET_HALTED) {
|
||||||
LOG_TARGET_WARNING(target, "target not halted");
|
LOG_TARGET_ERROR(target, "target not halted");
|
||||||
return ERROR_TARGET_NOT_HALTED;
|
return ERROR_TARGET_NOT_HALTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1147,7 +1258,7 @@ static int cortex_m_resume(struct target *target, int current,
|
||||||
/* current = 1: continue on current pc, otherwise continue at <address> */
|
/* current = 1: continue on current pc, otherwise continue at <address> */
|
||||||
r = armv7m->arm.pc;
|
r = armv7m->arm.pc;
|
||||||
if (!current) {
|
if (!current) {
|
||||||
buf_set_u32(r->value, 0, 32, address);
|
buf_set_u32(r->value, 0, 32, *address);
|
||||||
r->dirty = true;
|
r->dirty = true;
|
||||||
r->valid = true;
|
r->valid = true;
|
||||||
}
|
}
|
||||||
|
@ -1161,8 +1272,12 @@ static int cortex_m_resume(struct target *target, int current,
|
||||||
armv7m_maybe_skip_bkpt_inst(target, NULL);
|
armv7m_maybe_skip_bkpt_inst(target, NULL);
|
||||||
|
|
||||||
resume_pc = buf_get_u32(r->value, 0, 32);
|
resume_pc = buf_get_u32(r->value, 0, 32);
|
||||||
|
if (current)
|
||||||
|
*address = resume_pc;
|
||||||
|
|
||||||
armv7m_restore_context(target);
|
int retval = armv7m_restore_context(target);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
/* the front-end may request us not to handle breakpoints */
|
/* the front-end may request us not to handle breakpoints */
|
||||||
if (handle_breakpoints) {
|
if (handle_breakpoints) {
|
||||||
|
@ -1172,34 +1287,99 @@ static int cortex_m_resume(struct target *target, int current,
|
||||||
LOG_TARGET_DEBUG(target, "unset breakpoint at " TARGET_ADDR_FMT " (ID: %" PRIu32 ")",
|
LOG_TARGET_DEBUG(target, "unset breakpoint at " TARGET_ADDR_FMT " (ID: %" PRIu32 ")",
|
||||||
breakpoint->address,
|
breakpoint->address,
|
||||||
breakpoint->unique_id);
|
breakpoint->unique_id);
|
||||||
cortex_m_unset_breakpoint(target, breakpoint);
|
retval = cortex_m_unset_breakpoint(target, breakpoint);
|
||||||
cortex_m_single_step_core(target);
|
if (retval == ERROR_OK)
|
||||||
cortex_m_set_breakpoint(target, breakpoint);
|
retval = cortex_m_single_step_core(target);
|
||||||
|
int ret2 = cortex_m_set_breakpoint(target, breakpoint);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
if (ret2 != ERROR_OK)
|
||||||
|
return ret2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cortex_m_restart_one(struct target *target, bool debug_execution)
|
||||||
|
{
|
||||||
|
struct armv7m_common *armv7m = target_to_armv7m(target);
|
||||||
|
|
||||||
/* Restart core */
|
/* Restart core */
|
||||||
cortex_m_set_maskints_for_run(target);
|
cortex_m_set_maskints_for_run(target);
|
||||||
cortex_m_write_debug_halt_mask(target, 0, C_HALT);
|
cortex_m_write_debug_halt_mask(target, 0, C_HALT);
|
||||||
|
|
||||||
target->debug_reason = DBG_REASON_NOTHALTED;
|
target->debug_reason = DBG_REASON_NOTHALTED;
|
||||||
|
|
||||||
/* registers are now invalid */
|
/* registers are now invalid */
|
||||||
register_cache_invalidate(armv7m->arm.core_cache);
|
register_cache_invalidate(armv7m->arm.core_cache);
|
||||||
|
|
||||||
if (!debug_execution) {
|
if (!debug_execution) {
|
||||||
target->state = TARGET_RUNNING;
|
target->state = TARGET_RUNNING;
|
||||||
target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
|
target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
|
||||||
LOG_TARGET_DEBUG(target, "target resumed at 0x%" PRIx32 "", resume_pc);
|
|
||||||
} else {
|
} else {
|
||||||
target->state = TARGET_DEBUG_RUNNING;
|
target->state = TARGET_DEBUG_RUNNING;
|
||||||
target_call_event_callbacks(target, TARGET_EVENT_DEBUG_RESUMED);
|
target_call_event_callbacks(target, TARGET_EVENT_DEBUG_RESUMED);
|
||||||
LOG_TARGET_DEBUG(target, "target debug resumed at 0x%" PRIx32 "", resume_pc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int cortex_m_restore_smp(struct target *target, bool handle_breakpoints)
|
||||||
|
{
|
||||||
|
struct target_list *head;
|
||||||
|
target_addr_t address;
|
||||||
|
foreach_smp_target(head, target->smp_targets) {
|
||||||
|
struct target *curr = head->target;
|
||||||
|
/* skip calling target */
|
||||||
|
if (curr == target)
|
||||||
|
continue;
|
||||||
|
if (!target_was_examined(curr))
|
||||||
|
continue;
|
||||||
|
/* skip running targets */
|
||||||
|
if (curr->state == TARGET_RUNNING)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int retval = cortex_m_restore_one(curr, true, &address,
|
||||||
|
handle_breakpoints, false);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
retval = cortex_m_restart_one(curr, false);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
LOG_TARGET_DEBUG(curr, "SMP resumed at " TARGET_ADDR_FMT, address);
|
||||||
|
}
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cortex_m_resume(struct target *target, int current,
|
||||||
|
target_addr_t address, int handle_breakpoints, int debug_execution)
|
||||||
|
{
|
||||||
|
int retval = cortex_m_restore_one(target, !!current, &address, !!handle_breakpoints, !!debug_execution);
|
||||||
|
if (retval != ERROR_OK) {
|
||||||
|
LOG_TARGET_ERROR(target, "context restore failed, aborting resume");
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target->smp && !debug_execution) {
|
||||||
|
retval = cortex_m_restore_smp(target, !!handle_breakpoints);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
LOG_WARNING("resume of a SMP target failed, trying to resume current one");
|
||||||
|
}
|
||||||
|
|
||||||
|
cortex_m_restart_one(target, !!debug_execution);
|
||||||
|
if (retval != ERROR_OK) {
|
||||||
|
LOG_TARGET_ERROR(target, "resume failed");
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_TARGET_DEBUG(target, "%sresumed at " TARGET_ADDR_FMT,
|
||||||
|
debug_execution ? "debug " : "", address);
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
/* int irqstepcount = 0; */
|
/* int irqstepcount = 0; */
|
||||||
static int cortex_m_step(struct target *target, int current,
|
static int cortex_m_step(struct target *target, int current,
|
||||||
target_addr_t address, int handle_breakpoints)
|
target_addr_t address, int handle_breakpoints)
|
||||||
|
@ -1217,6 +1397,11 @@ static int cortex_m_step(struct target *target, int current,
|
||||||
return ERROR_TARGET_NOT_HALTED;
|
return ERROR_TARGET_NOT_HALTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Just one of SMP cores will step. Set the gdb control
|
||||||
|
* target to current one or gdb miss gdb-end event */
|
||||||
|
if (target->smp && target->gdb_service)
|
||||||
|
target->gdb_service->target = target;
|
||||||
|
|
||||||
/* current = 1: continue on current pc, otherwise continue at <address> */
|
/* current = 1: continue on current pc, otherwise continue at <address> */
|
||||||
if (!current) {
|
if (!current) {
|
||||||
buf_set_u32(pc->value, 0, 32, address);
|
buf_set_u32(pc->value, 0, 32, address);
|
||||||
|
@ -2850,6 +3035,9 @@ static const struct command_registration cortex_m_exec_command_handlers[] = {
|
||||||
.help = "configure software reset handling",
|
.help = "configure software reset handling",
|
||||||
.usage = "['sysresetreq'|'vectreset']",
|
.usage = "['sysresetreq'|'vectreset']",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.chain = smp_command_handlers,
|
||||||
|
},
|
||||||
COMMAND_REGISTRATION_DONE
|
COMMAND_REGISTRATION_DONE
|
||||||
};
|
};
|
||||||
static const struct command_registration cortex_m_command_handlers[] = {
|
static const struct command_registration cortex_m_command_handlers[] = {
|
||||||
|
|
|
@ -193,6 +193,10 @@ struct target {
|
||||||
struct list_head *smp_targets; /* list all targets in this smp group/cluster
|
struct list_head *smp_targets; /* list all targets in this smp group/cluster
|
||||||
* The head of the list is shared between the
|
* The head of the list is shared between the
|
||||||
* cluster, thus here there is a pointer */
|
* cluster, thus here there is a pointer */
|
||||||
|
bool smp_halt_event_postponed; /* Some SMP implementations (currently Cortex-M) stores
|
||||||
|
* 'halted' events and emits them after all targets of
|
||||||
|
* the SMP group has been polled */
|
||||||
|
|
||||||
/* the gdb service is there in case of smp, we have only one gdb server
|
/* the gdb service is there in case of smp, we have only one gdb server
|
||||||
* for all smp target
|
* for all smp target
|
||||||
* the target attached to the gdb is changing dynamically by changing
|
* the target attached to the gdb is changing dynamically by changing
|
||||||
|
|
Loading…
Reference in New Issue