Merge pull request #347 from riscv/hwthread

`-rtos hwthread` support
This commit is contained in:
Tim Newsome 2019-01-31 12:20:34 -08:00 committed by GitHub
commit c554246177
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 791 additions and 147 deletions

View File

@ -9869,55 +9869,6 @@ and GDB would require stopping the target to get the prompt back.
Do not use this mode under an IDE like Eclipse as it caches values of Do not use this mode under an IDE like Eclipse as it caches values of
previously shown varibles. previously shown varibles.
@anchor{usingopenocdsmpwithgdb}
@section Using OpenOCD SMP with GDB
@cindex SMP
For SMP support following GDB serial protocol packet have been defined :
@itemize @bullet
@item j - smp status request
@item J - smp set request
@end itemize
OpenOCD implements :
@itemize @bullet
@item @option{jc} packet for reading core id displayed by
GDB connection. Reply is @option{XXXXXXXX} (8 hex digits giving core id) or
@option{E01} for target not smp.
@item @option{JcXXXXXXXX} (8 hex digits) packet for setting core id displayed at next GDB continue
(core id -1 is reserved for returning to normal resume mode). Reply @option{E01}
for target not smp or @option{OK} on success.
@end itemize
Handling of this packet within GDB can be done :
@itemize @bullet
@item by the creation of an internal variable (i.e @option{_core}) by mean
of function allocate_computed_value allowing following GDB command.
@example
set $_core 1
#Jc01 packet is sent
print $_core
#jc packet is sent and result is affected in $
@end example
@item by the usage of GDB maintenance command as described in following example (2 cpus in SMP with
core id 0 and 1 @pxref{definecputargetsworkinginsmp,,Define CPU targets working in SMP}).
@example
# toggle0 : force display of coreid 0
define toggle0
maint packet Jc0
continue
main packet Jc-1
end
# toggle1 : force display of coreid 1
define toggle1
maint packet Jc1
continue
main packet Jc-1
end
@end example
@end itemize
@section RTOS Support @section RTOS Support
@cindex RTOS Support @cindex RTOS Support
@anchor{gdbrtossupport} @anchor{gdbrtossupport}
@ -9948,12 +9899,11 @@ Currently supported rtos's include:
@item @option{mqx} @item @option{mqx}
@item @option{uCOS-III} @item @option{uCOS-III}
@item @option{nuttx} @item @option{nuttx}
@item @option{hwthread} (This is not an actual RTOS. @xref{usingopenocdsmpwithgdb,,Using OpenOCD SMP with GDB}.)
@end itemize @end itemize
@quotation Note
Before an RTOS can be detected, it must export certain symbols; otherwise, it cannot Before an RTOS can be detected, it must export certain symbols; otherwise, it cannot
be used by OpenOCD. Below is a list of the required symbols for each supported RTOS. be used by OpenOCD. Below is a list of the required symbols for each supported RTOS.
@end quotation
@table @code @table @code
@item eCos symbols @item eCos symbols
@ -10000,6 +9950,72 @@ contrib/rtos-helpers/FreeRTOS-openocd.c
contrib/rtos-helpers/uCOS-III-openocd.c contrib/rtos-helpers/uCOS-III-openocd.c
@end table @end table
@anchor{usingopenocdsmpwithgdb}
@section Using OpenOCD SMP with GDB
@cindex SMP
@cindex RTOS
@cindex hwthread
OpenOCD includes a pseudo RTOS called @emph{hwthread} that presents CPU cores
("hardware threads") in an SMP system as threads to GDB. With this extension,
GDB can be used to inspect the state of an SMP system in a natural way.
After halting the system, using the GDB command @command{info threads} will
list the context of each active CPU core in the system. GDB's @command{thread}
command can be used to switch the view to a different CPU core.
The @command{step} and @command{stepi} commands can be used to step a specific core
while other cores are free-running or remain halted, depending on the
scheduler-locking mode configured in GDB.
@section Legacy SMP core switching support
@quotation Note
This method is deprecated in favor of the @emph{hwthread} pseudo RTOS.
@end quotation
For SMP support following GDB serial protocol packet have been defined :
@itemize @bullet
@item j - smp status request
@item J - smp set request
@end itemize
OpenOCD implements :
@itemize @bullet
@item @option{jc} packet for reading core id displayed by
GDB connection. Reply is @option{XXXXXXXX} (8 hex digits giving core id) or
@option{E01} for target not smp.
@item @option{JcXXXXXXXX} (8 hex digits) packet for setting core id displayed at next GDB continue
(core id -1 is reserved for returning to normal resume mode). Reply @option{E01}
for target not smp or @option{OK} on success.
@end itemize
Handling of this packet within GDB can be done :
@itemize @bullet
@item by the creation of an internal variable (i.e @option{_core}) by mean
of function allocate_computed_value allowing following GDB command.
@example
set $_core 1
#Jc01 packet is sent
print $_core
#jc packet is sent and result is affected in $
@end example
@item by the usage of GDB maintenance command as described in following example (2 cpus in SMP with
core id 0 and 1 @pxref{definecputargetsworkinginsmp,,Define CPU targets working in SMP}).
@example
# toggle0 : force display of coreid 0
define toggle0
maint packet Jc0
continue
main packet Jc-1
end
# toggle1 : force display of coreid 1
define toggle1
maint packet Jc1
continue
main packet Jc-1
end
@end example
@end itemize
@node Tcl Scripting API @node Tcl Scripting API
@chapter Tcl Scripting API @chapter Tcl Scripting API
@cindex Tcl Scripting API @cindex Tcl Scripting API

View File

@ -17,6 +17,7 @@ noinst_LTLIBRARIES += %D%/librtos.la
%D%/riscv_debug.c \ %D%/riscv_debug.c \
%D%/uCOS-III.c \ %D%/uCOS-III.c \
%D%/nuttx.c \ %D%/nuttx.c \
%D%/hwthread.c \
%D%/rtos.h \ %D%/rtos.h \
%D%/rtos_standard_stackings.h \ %D%/rtos_standard_stackings.h \
%D%/rtos_ecos_stackings.h \ %D%/rtos_ecos_stackings.h \

382
src/rtos/hwthread.c Normal file
View File

@ -0,0 +1,382 @@
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <helper/time_support.h>
#include <jtag/jtag.h>
#include "target/target.h"
#include "target/target_type.h"
#include "target/register.h"
#include "rtos.h"
#include "helper/log.h"
#include "helper/types.h"
#include "server/gdb_server.h"
static bool hwthread_detect_rtos(struct target *target);
static int hwthread_create(struct target *target);
static int hwthread_update_threads(struct rtos *rtos);
static int hwthread_get_thread_reg(struct rtos *rtos, int64_t thread_id,
uint32_t reg_num, struct rtos_reg *rtos_reg);
static int hwthread_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
struct rtos_reg **reg_list, int *num_regs);
static int hwthread_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]);
static int hwthread_smp_init(struct target *target);
int hwthread_set_reg(struct rtos *rtos, uint32_t reg_num, uint8_t *reg_value);
#define HW_THREAD_NAME_STR_SIZE (32)
extern int rtos_thread_packet(struct connection *connection, const char *packet, int packet_size);
static inline threadid_t threadid_from_target(const struct target *target)
{
return target->coreid + 1;
}
const struct rtos_type hwthread_rtos = {
.name = "hwthread",
.detect_rtos = hwthread_detect_rtos,
.create = hwthread_create,
.update_threads = hwthread_update_threads,
.get_thread_reg_list = hwthread_get_thread_reg_list,
.get_thread_reg = hwthread_get_thread_reg,
.get_symbol_list_to_lookup = hwthread_get_symbol_list_to_lookup,
.smp_init = hwthread_smp_init,
.set_reg = hwthread_set_reg,
};
struct hwthread_params {
int dummy_param;
};
static int hwthread_fill_thread(struct rtos *rtos, struct target *curr, int thread_num)
{
char tmp_str[HW_THREAD_NAME_STR_SIZE];
threadid_t tid = threadid_from_target(curr);
memset(tmp_str, 0, HW_THREAD_NAME_STR_SIZE);
/* thread-id is the core-id of this core inside the SMP group plus 1 */
rtos->thread_details[thread_num].threadid = tid;
/* create the thread name */
rtos->thread_details[thread_num].exists = true;
rtos->thread_details[thread_num].thread_name_str = strdup(target_name(curr));
snprintf(tmp_str, HW_THREAD_NAME_STR_SIZE-1, "state: %s", debug_reason_name(curr));
rtos->thread_details[thread_num].extra_info_str = strdup(tmp_str);
return ERROR_OK;
}
static int hwthread_update_threads(struct rtos *rtos)
{
int threads_found = 0;
int thread_list_size = 0;
struct target_list *head;
struct target *target;
int64_t current_thread = 0;
enum target_debug_reason current_reason = DBG_REASON_UNDEFINED;
if (rtos == NULL)
return ERROR_FAIL;
target = rtos->target;
/* determine the number of "threads" */
if (target->smp) {
for (head = target->head; head != NULL; head = head->next) {
struct target *curr = head->target;
if (!target_was_examined(curr))
continue;
++thread_list_size;
}
} else
thread_list_size = 1;
/* Wipe out previous thread details if any, but preserve threadid. */
int64_t current_threadid = rtos->current_threadid;
rtos_free_threadlist(rtos);
rtos->current_threadid = current_threadid;
/* create space for new thread details */
rtos->thread_details = malloc(sizeof(struct thread_detail) * thread_list_size);
if (target->smp) {
/* loop over all threads */
for (head = target->head; head != NULL; head = head->next) {
struct target *curr = head->target;
if (!target_was_examined(curr))
continue;
threadid_t tid = threadid_from_target(curr);
hwthread_fill_thread(rtos, curr, threads_found);
/* find an interesting thread to set as current */
switch (current_reason) {
case DBG_REASON_UNDEFINED:
current_reason = curr->debug_reason;
current_thread = tid;
break;
case DBG_REASON_SINGLESTEP:
/* single-step can only be overridden by itself */
if (curr->debug_reason == DBG_REASON_SINGLESTEP) {
if (tid == rtos->current_threadid)
current_thread = tid;
}
break;
case DBG_REASON_BREAKPOINT:
/* single-step overrides breakpoint */
if (curr->debug_reason == DBG_REASON_SINGLESTEP) {
current_reason = curr->debug_reason;
current_thread = tid;
} else
/* multiple breakpoints, prefer gdbs' threadid */
if (curr->debug_reason == DBG_REASON_BREAKPOINT) {
if (tid == rtos->current_threadid)
current_thread = tid;
}
break;
case DBG_REASON_WATCHPOINT:
/* breakpoint and single-step override watchpoint */
if (curr->debug_reason == DBG_REASON_SINGLESTEP ||
curr->debug_reason == DBG_REASON_BREAKPOINT) {
current_reason = curr->debug_reason;
current_thread = tid;
}
break;
case DBG_REASON_DBGRQ:
/* all other reasons override debug-request */
if (curr->debug_reason == DBG_REASON_SINGLESTEP ||
curr->debug_reason == DBG_REASON_WATCHPOINT ||
curr->debug_reason == DBG_REASON_BREAKPOINT) {
current_reason = curr->debug_reason;
current_thread = tid;
} else
if (curr->debug_reason == DBG_REASON_DBGRQ) {
if (tid == rtos->current_threadid)
current_thread = tid;
}
break;
default:
break;
}
threads_found++;
}
} else {
hwthread_fill_thread(rtos, target, threads_found);
current_thread = threadid_from_target(target);
threads_found++;
}
rtos->thread_count = threads_found;
/* we found an interesting thread, set it as current */
if (current_thread != 0)
rtos->current_thread = current_thread;
else if (rtos->current_threadid != 0)
rtos->current_thread = rtos->current_threadid;
else
rtos->current_thread = threadid_from_target(target);
return 0;
}
static int hwthread_smp_init(struct target *target)
{
return hwthread_update_threads(target->rtos);
}
static struct target *find_thread(struct target *target, int64_t thread_id)
{
/* Find the thread with that thread_id */
if (target == NULL)
return NULL;
if (target->smp) {
for (struct target_list *head = target->head; head != NULL; head = head->next) {
if (thread_id == threadid_from_target(head->target))
return head->target;
}
} else if (thread_id == threadid_from_target(target)) {
return target;
}
return NULL;
}
static int hwthread_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
struct rtos_reg **rtos_reg_list, int *rtos_reg_list_size)
{
if (rtos == NULL)
return ERROR_FAIL;
struct target *target = rtos->target;
struct target *curr = find_thread(target, thread_id);
if (curr == NULL)
return ERROR_FAIL;
if (!target_was_examined(curr))
return ERROR_FAIL;
struct reg **reg_list;
int retval = target_get_gdb_reg_list(curr, &reg_list, rtos_reg_list_size,
REG_CLASS_GENERAL);
if (retval != ERROR_OK)
return retval;
*rtos_reg_list = calloc(*rtos_reg_list_size, sizeof(struct rtos_reg));
if (*rtos_reg_list == NULL) {
free(reg_list);
return ERROR_FAIL;
}
for (int i = 0; i < *rtos_reg_list_size; i++) {
(*rtos_reg_list)[i].number = (*reg_list)[i].number;
(*rtos_reg_list)[i].size = (*reg_list)[i].size;
memcpy((*rtos_reg_list)[i].value, (*reg_list)[i].value,
((*reg_list)[i].size + 7) / 8);
}
free(reg_list);
return ERROR_OK;
}
static int hwthread_get_thread_reg(struct rtos *rtos, int64_t thread_id,
uint32_t reg_num, struct rtos_reg *rtos_reg)
{
if (rtos == NULL)
return ERROR_FAIL;
struct target *target = rtos->target;
struct target *curr = find_thread(target, thread_id);
if (curr == NULL)
return ERROR_FAIL;
if (!target_was_examined(curr))
return ERROR_FAIL;
struct reg *reg = register_get_by_number(curr->reg_cache, reg_num, true);
if (!reg)
return ERROR_FAIL;
if (reg->type->get(reg) != ERROR_OK)
return ERROR_FAIL;
rtos_reg->number = reg->number;
rtos_reg->size = reg->size;
unsigned bytes = (reg->size + 7) / 8;
assert(bytes <= sizeof(rtos_reg->value));
memcpy(rtos_reg->value, reg->value, bytes);
return ERROR_OK;
}
int hwthread_set_reg(struct rtos *rtos, uint32_t reg_num, uint8_t *reg_value)
{
if (rtos == NULL)
return ERROR_FAIL;
struct target *target = rtos->target;
struct target *curr = find_thread(target, rtos->current_thread);
if (curr == NULL)
return ERROR_FAIL;
struct reg *reg = register_get_by_number(curr->reg_cache, reg_num, true);
if (!reg)
return ERROR_FAIL;
return reg->type->set(reg, reg_value);
}
static int hwthread_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[])
{
/* return an empty list, we don't have any symbols to look up */
*symbol_list = calloc(1, sizeof(symbol_table_elem_t));
(*symbol_list)[0].symbol_name = NULL;
return 0;
}
static int hwthread_target_for_threadid(struct connection *connection, int64_t thread_id, struct target **p_target)
{
struct target *target = get_target_from_connection(connection);
struct target *curr = find_thread(target, thread_id);
if (curr == NULL)
return ERROR_FAIL;
*p_target = curr;
return ERROR_OK;
}
static bool hwthread_detect_rtos(struct target *target)
{
/* always return 0, avoid auto-detection */
return false;
}
static int hwthread_thread_packet(struct connection *connection, const char *packet, int packet_size)
{
struct target *target = get_target_from_connection(connection);
struct target *curr = NULL;
int64_t current_threadid;
if (packet[0] == 'H' && packet[1] == 'g') {
/* Never reached, because this case is handled by rtos_thread_packet(). */
sscanf(packet, "Hg%16" SCNx64, &current_threadid);
if (current_threadid > 0) {
if (hwthread_target_for_threadid(connection, current_threadid, &curr) != ERROR_OK) {
LOG_ERROR("hwthread: cannot find thread id %"PRId64, current_threadid);
gdb_put_packet(connection, "E01", 3);
return ERROR_FAIL;
}
target->rtos->current_thread = current_threadid;
} else
if (current_threadid == 0 || current_threadid == -1)
target->rtos->current_thread = threadid_from_target(target);
target->rtos->current_threadid = current_threadid;
LOG_DEBUG("current_threadid=%" PRId64, current_threadid);
gdb_put_packet(connection, "OK", 2);
return ERROR_OK;
}
return rtos_thread_packet(connection, packet, packet_size);
}
static int hwthread_create(struct target *target)
{
LOG_INFO("Hardware thread awareness created");
target->rtos->rtos_specific_params = NULL;
target->rtos->current_thread = 0;
target->rtos->thread_details = NULL;
target->rtos->gdb_target_for_threadid = hwthread_target_for_threadid;
target->rtos->gdb_thread_packet = hwthread_thread_packet;
return 0;
}

View File

@ -36,6 +36,7 @@ extern struct rtos_type embKernel_rtos;
extern struct rtos_type mqx_rtos; extern struct rtos_type mqx_rtos;
extern struct rtos_type uCOS_III_rtos; extern struct rtos_type uCOS_III_rtos;
extern struct rtos_type nuttx_rtos; extern struct rtos_type nuttx_rtos;
extern struct rtos_type hwthread_rtos;
extern struct rtos_type riscv_rtos; extern struct rtos_type riscv_rtos;
static struct rtos_type *rtos_types[] = { static struct rtos_type *rtos_types[] = {
@ -48,6 +49,7 @@ static struct rtos_type *rtos_types[] = {
&mqx_rtos, &mqx_rtos,
&uCOS_III_rtos, &uCOS_III_rtos,
&nuttx_rtos, &nuttx_rtos,
&hwthread_rtos,
&riscv_rtos, &riscv_rtos,
NULL NULL
}; };
@ -461,6 +463,7 @@ static int rtos_put_gdb_reg_list(struct connection *connection,
return ERROR_OK; return ERROR_OK;
} }
/** Look through all registers to find this register. */
int rtos_get_gdb_reg(struct connection *connection, int reg_num) int rtos_get_gdb_reg(struct connection *connection, int reg_num)
{ {
struct target *target = get_target_from_connection(connection); struct target *target = get_target_from_connection(connection);
@ -478,10 +481,18 @@ int rtos_get_gdb_reg(struct connection *connection, int reg_num)
current_threadid, current_threadid,
target->rtos->current_thread); target->rtos->current_thread);
int retval = target->rtos->type->get_thread_reg_list(target->rtos, int retval;
current_threadid, if (target->rtos->type->get_thread_reg) {
&reg_list, reg_list = calloc(1, sizeof(*reg_list));
&num_regs); num_regs = 1;
retval = target->rtos->type->get_thread_reg(target->rtos,
current_threadid, reg_num, &reg_list[0]);
} else {
retval = target->rtos->type->get_thread_reg_list(target->rtos,
current_threadid,
&reg_list,
&num_regs);
}
if (retval != ERROR_OK) { if (retval != ERROR_OK) {
LOG_ERROR("RTOS: failed to get register list"); LOG_ERROR("RTOS: failed to get register list");
return retval; return retval;
@ -500,6 +511,7 @@ int rtos_get_gdb_reg(struct connection *connection, int reg_num)
return ERROR_FAIL; return ERROR_FAIL;
} }
/** Return a list of general registers. */
int rtos_get_gdb_reg_list(struct connection *connection) int rtos_get_gdb_reg_list(struct connection *connection)
{ {
struct target *target = get_target_from_connection(connection); struct target *target = get_target_from_connection(connection);
@ -533,6 +545,20 @@ int rtos_get_gdb_reg_list(struct connection *connection)
return ERROR_FAIL; return ERROR_FAIL;
} }
int rtos_set_reg(struct connection *connection, int reg_num,
uint8_t *reg_value)
{
struct target *target = get_target_from_connection(connection);
int64_t current_threadid = target->rtos->current_threadid;
if ((target->rtos != NULL) &&
(target->rtos->type->set_reg != NULL) &&
(current_threadid != -1) &&
(current_threadid != 0)) {
return target->rtos->type->set_reg(target->rtos, reg_num, reg_value);
}
return ERROR_FAIL;
}
int rtos_generic_stack_read(struct target *target, int rtos_generic_stack_read(struct target *target,
const struct rtos_register_stacking *stacking, const struct rtos_register_stacking *stacking,
int64_t stack_ptr, int64_t stack_ptr,

View File

@ -20,6 +20,7 @@
#define OPENOCD_RTOS_RTOS_H #define OPENOCD_RTOS_RTOS_H
#include "server/server.h" #include "server/server.h"
#include "target/target.h"
#include <jim-nvp.h> #include <jim-nvp.h>
typedef int64_t threadid_t; typedef int64_t threadid_t;
@ -49,7 +50,9 @@ struct rtos {
symbol_table_elem_t *symbols; symbol_table_elem_t *symbols;
struct target *target; struct target *target;
/* add a context variable instead of global variable */ /* add a context variable instead of global variable */
/* The thread currently selected by gdb. */
int64_t current_threadid; int64_t current_threadid;
/* The currently selected thread according to the target. */
threadid_t current_thread; threadid_t current_thread;
struct thread_detail *thread_details; struct thread_detail *thread_details;
int thread_count; int thread_count;
@ -71,11 +74,15 @@ struct rtos_type {
int (*create)(struct target *target); int (*create)(struct target *target);
int (*smp_init)(struct target *target); int (*smp_init)(struct target *target);
int (*update_threads)(struct rtos *rtos); int (*update_threads)(struct rtos *rtos);
/** Return a list of general registers, with their values filled out. */
int (*get_thread_reg_list)(struct rtos *rtos, int64_t thread_id, int (*get_thread_reg_list)(struct rtos *rtos, int64_t thread_id,
struct rtos_reg **reg_list, int *num_regs); struct rtos_reg **reg_list, int *num_regs);
int (*get_thread_reg)(struct rtos *rtos, int64_t thread_id,
uint32_t reg_num, struct rtos_reg *reg);
int (*get_symbol_list_to_lookup)(symbol_table_elem_t *symbol_list[]); int (*get_symbol_list_to_lookup)(symbol_table_elem_t *symbol_list[]);
int (*clean)(struct target *target); int (*clean)(struct target *target);
char * (*ps_command)(struct target *target); char * (*ps_command)(struct target *target);
int (*set_reg)(struct rtos *rtos, uint32_t reg_num, uint8_t *reg_value);
}; };
struct stack_register_offset { struct stack_register_offset {
@ -105,6 +112,8 @@ struct rtos_register_stacking {
#define GDB_THREAD_PACKET_NOT_CONSUMED (-40) #define GDB_THREAD_PACKET_NOT_CONSUMED (-40)
int rtos_create(Jim_GetOptInfo *goi, struct target *target); int rtos_create(Jim_GetOptInfo *goi, struct target *target);
int rtos_set_reg(struct connection *connection, int reg_num,
uint8_t *reg_value);
int rtos_generic_stack_read(struct target *target, int rtos_generic_stack_read(struct target *target,
const struct rtos_register_stacking *stacking, const struct rtos_register_stacking *stacking,
int64_t stack_ptr, int64_t stack_ptr,

View File

@ -733,17 +733,26 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio
if (target->debug_reason == DBG_REASON_EXIT) { if (target->debug_reason == DBG_REASON_EXIT) {
sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "W00"); sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "W00");
} else { } else {
struct target *ct;
if (target->rtos != NULL) {
target->rtos->current_threadid = target->rtos->current_thread;
LOG_DEBUG("current_threadid=%" PRId64, target->rtos->current_threadid);
target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &ct);
} else {
ct = target;
}
if (gdb_connection->ctrl_c) { if (gdb_connection->ctrl_c) {
signal_var = 0x2; signal_var = 0x2;
} else } else
signal_var = gdb_last_signal(target); signal_var = gdb_last_signal(ct);
stop_reason[0] = '\0'; stop_reason[0] = '\0';
if (target->debug_reason == DBG_REASON_WATCHPOINT) { if (ct->debug_reason == DBG_REASON_WATCHPOINT) {
enum watchpoint_rw hit_wp_type; enum watchpoint_rw hit_wp_type;
target_addr_t hit_wp_address; target_addr_t hit_wp_address;
if (watchpoint_hit(target, &hit_wp_type, &hit_wp_address) == ERROR_OK) { if (watchpoint_hit(ct, &hit_wp_type, &hit_wp_address) == ERROR_OK) {
switch (hit_wp_type) { switch (hit_wp_type) {
case WPT_WRITE: case WPT_WRITE:
@ -765,15 +774,9 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio
} }
current_thread[0] = '\0'; current_thread[0] = '\0';
if (target->rtos != NULL) { if (target->rtos != NULL)
struct target *ct;
snprintf(current_thread, sizeof(current_thread), "thread:%" PRIx64 ";", snprintf(current_thread, sizeof(current_thread), "thread:%" PRIx64 ";",
target->rtos->current_thread); target->rtos->current_thread);
target->rtos->current_threadid = target->rtos->current_thread;
target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &ct);
if (!gdb_connection->ctrl_c)
signal_var = gdb_last_signal(ct);
}
sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "T%2.2x%s%s", sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "T%2.2x%s%s",
signal_var, stop_reason, current_thread); signal_var, stop_reason, current_thread);
@ -1340,37 +1343,49 @@ static int gdb_set_register_packet(struct connection *connection,
{ {
struct target *target = get_target_from_connection(connection); struct target *target = get_target_from_connection(connection);
char *separator; char *separator;
uint8_t *bin_buf;
int reg_num = strtoul(packet + 1, &separator, 16); int reg_num = strtoul(packet + 1, &separator, 16);
struct reg **reg_list; struct reg **reg_list;
int reg_list_size; int reg_list_size;
int retval; int retval;
#ifdef _DEBUG_GDB_IO_
LOG_DEBUG("-"); LOG_DEBUG("-");
#endif
retval = target_get_gdb_reg_list(target, &reg_list, &reg_list_size,
REG_CLASS_ALL);
if (retval != ERROR_OK)
return gdb_error(connection, retval);
if (reg_list_size <= reg_num) {
LOG_ERROR("gdb requested a non-existing register");
return ERROR_SERVER_REMOTE_CLOSED;
}
if (*separator != '=') { if (*separator != '=') {
LOG_ERROR("GDB 'set register packet', but no '=' following the register number"); LOG_ERROR("GDB 'set register packet', but no '=' following the register number");
return ERROR_SERVER_REMOTE_CLOSED; return ERROR_SERVER_REMOTE_CLOSED;
} }
size_t chars = strlen(separator + 1);
uint8_t *bin_buf = malloc(chars / 2);
gdb_target_to_reg(target, separator + 1, chars, bin_buf);
/* convert from GDB-string (target-endian) to hex-string (big-endian) */ if ((target->rtos != NULL) &&
bin_buf = malloc(DIV_ROUND_UP(reg_list[reg_num]->size, 8)); (ERROR_OK == rtos_set_reg(connection, reg_num, bin_buf))) {
int chars = (DIV_ROUND_UP(reg_list[reg_num]->size, 8) * 2);
if ((unsigned int)chars != strlen(separator + 1)) {
LOG_ERROR("gdb sent %zu bits for a %d-bit register (%s)",
strlen(separator + 1) * 4, chars * 4, reg_list[reg_num]->name);
free(bin_buf); free(bin_buf);
gdb_put_packet(connection, "OK", 2);
return ERROR_OK;
}
retval = target_get_gdb_reg_list(target, &reg_list, &reg_list_size,
REG_CLASS_ALL);
if (retval != ERROR_OK) {
free(bin_buf);
return gdb_error(connection, retval);
}
if (reg_list_size <= reg_num) {
LOG_ERROR("gdb requested a non-existing register");
free(bin_buf);
free(reg_list);
return ERROR_SERVER_REMOTE_CLOSED;
}
if (chars != (DIV_ROUND_UP(reg_list[reg_num]->size, 8) * 2)) {
LOG_ERROR("gdb sent %d bits for a %d-bit register (%s)",
(int) chars * 4, reg_list[reg_num]->size, reg_list[reg_num]->name);
free(bin_buf);
free(reg_list);
return ERROR_SERVER_REMOTE_CLOSED; return ERROR_SERVER_REMOTE_CLOSED;
} }
@ -1640,7 +1655,7 @@ static int gdb_breakpoint_watchpoint_packet(struct connection *connection,
char *separator; char *separator;
int retval; int retval;
LOG_DEBUG("-"); LOG_DEBUG("[%d]", target->coreid);
type = strtoul(packet + 1, &separator, 16); type = strtoul(packet + 1, &separator, 16);
@ -2398,6 +2413,7 @@ static int gdb_target_description_supported(struct target *target, int *supporte
&reg_list_size, REG_CLASS_ALL); &reg_list_size, REG_CLASS_ALL);
if (retval != ERROR_OK) { if (retval != ERROR_OK) {
LOG_ERROR("get register list failed"); LOG_ERROR("get register list failed");
reg_list = NULL;
goto error; goto error;
} }

View File

@ -98,7 +98,9 @@ fail:
return retval; return retval;
} }
LOG_DEBUG("added %s breakpoint at " TARGET_ADDR_FMT " of length 0x%8.8x, (BPID: %" PRIu32 ")", LOG_DEBUG("[%d] added %s breakpoint at " TARGET_ADDR_FMT
" of length 0x%8.8x, (BPID: %" PRIu32 ")",
target->coreid,
breakpoint_type_strings[(*breakpoint_p)->type], breakpoint_type_strings[(*breakpoint_p)->type],
(*breakpoint_p)->address, (*breakpoint_p)->length, (*breakpoint_p)->address, (*breakpoint_p)->length,
(*breakpoint_p)->unique_id); (*breakpoint_p)->unique_id);
@ -385,8 +387,8 @@ struct breakpoint *breakpoint_find(struct target *target, target_addr_t address)
return NULL; return NULL;
} }
int watchpoint_add(struct target *target, target_addr_t address, uint32_t length, int watchpoint_add_internal(struct target *target, target_addr_t address,
enum watchpoint_rw rw, uint32_t value, uint32_t mask) uint32_t length, enum watchpoint_rw rw, uint32_t value, uint32_t mask)
{ {
struct watchpoint *watchpoint = target->watchpoints; struct watchpoint *watchpoint = target->watchpoints;
struct watchpoint **watchpoint_p = &target->watchpoints; struct watchpoint **watchpoint_p = &target->watchpoints;
@ -451,6 +453,29 @@ bye:
return ERROR_OK; return ERROR_OK;
} }
int watchpoint_add(struct target *target, target_addr_t address,
uint32_t length, enum watchpoint_rw rw, uint32_t value, uint32_t mask)
{
int retval = ERROR_OK;
if (target->smp) {
struct target_list *head;
struct target *curr;
head = target->head;
while (head != (struct target_list *)NULL) {
curr = head->target;
retval = watchpoint_add_internal(curr, address, length, rw, value,
mask);
if (retval != ERROR_OK)
return retval;
head = head->next;
}
return retval;
} else
return watchpoint_add_internal(target, address, length, rw, value,
mask);
}
static void watchpoint_free(struct target *target, struct watchpoint *watchpoint_to_remove) static void watchpoint_free(struct target *target, struct watchpoint *watchpoint_to_remove)
{ {
struct watchpoint *watchpoint = target->watchpoints; struct watchpoint *watchpoint = target->watchpoints;
@ -472,7 +497,7 @@ static void watchpoint_free(struct target *target, struct watchpoint *watchpoint
free(watchpoint); free(watchpoint);
} }
void watchpoint_remove(struct target *target, target_addr_t address) int watchpoint_remove_internal(struct target *target, target_addr_t address)
{ {
struct watchpoint *watchpoint = target->watchpoints; struct watchpoint *watchpoint = target->watchpoints;
@ -482,10 +507,32 @@ void watchpoint_remove(struct target *target, target_addr_t address)
watchpoint = watchpoint->next; watchpoint = watchpoint->next;
} }
if (watchpoint) if (watchpoint) {
watchpoint_free(target, watchpoint); watchpoint_free(target, watchpoint);
else return 1;
LOG_ERROR("no watchpoint at address " TARGET_ADDR_FMT " found", address); } else {
if (!target->smp)
LOG_ERROR("no watchpoint at address " TARGET_ADDR_FMT " found", address);
return 0;
}
}
void watchpoint_remove(struct target *target, target_addr_t address)
{
int found = 0;
if (target->smp) {
struct target_list *head;
struct target *curr;
head = target->head;
while (head != (struct target_list *)NULL) {
curr = head->target;
found += watchpoint_remove_internal(curr, address);
head = head->next;
}
if (found == 0)
LOG_ERROR("no watchpoint at address " TARGET_ADDR_FMT " found", address);
} else
watchpoint_remove_internal(target, address);
} }
void watchpoint_clear_target(struct target *target) void watchpoint_clear_target(struct target *target)

View File

@ -36,6 +36,29 @@
* may be separate registers associated with debug or trace modules. * may be separate registers associated with debug or trace modules.
*/ */
struct reg *register_get_by_number(struct reg_cache *first,
uint32_t reg_num, bool search_all)
{
unsigned i;
struct reg_cache *cache = first;
while (cache) {
for (i = 0; i < cache->num_regs; i++) {
if (cache->reg_list[i].exist == false)
continue;
if (cache->reg_list[i].number == reg_num)
return &(cache->reg_list[i]);
}
if (search_all)
cache = cache->next;
else
break;
}
return NULL;
}
struct reg *register_get_by_name(struct reg_cache *first, struct reg *register_get_by_name(struct reg_cache *first,
const char *name, bool search_all) const char *name, bool search_all)
{ {

View File

@ -159,6 +159,8 @@ struct reg_arch_type {
int (*set)(struct reg *reg, uint8_t *buf); int (*set)(struct reg *reg, uint8_t *buf);
}; };
struct reg *register_get_by_number(struct reg_cache *first,
uint32_t reg_num, bool search_all);
struct reg *register_get_by_name(struct reg_cache *first, struct reg *register_get_by_name(struct reg_cache *first,
const char *name, bool search_all); const char *name, bool search_all);
struct reg_cache **register_get_last_cache_p(struct reg_cache **first); struct reg_cache **register_get_last_cache_p(struct reg_cache **first);

View File

@ -1146,7 +1146,6 @@ static int register_write_direct(struct target *target, unsigned number,
if (result == ERROR_OK && target->reg_cache) { if (result == ERROR_OK && target->reg_cache) {
struct reg *reg = &target->reg_cache->reg_list[number]; struct reg *reg = &target->reg_cache->reg_list[number];
buf_set_u64(reg->value, 0, reg->size, value); buf_set_u64(reg->value, 0, reg->size, value);
reg->valid = true;
} }
if (result == ERROR_OK || info->progbufsize + r->impebreak < 2 || if (result == ERROR_OK || info->progbufsize + r->impebreak < 2 ||
!riscv_is_halted(target)) !riscv_is_halted(target))
@ -1205,7 +1204,6 @@ static int register_write_direct(struct target *target, unsigned number,
if (exec_out == ERROR_OK && target->reg_cache) { if (exec_out == ERROR_OK && target->reg_cache) {
struct reg *reg = &target->reg_cache->reg_list[number]; struct reg *reg = &target->reg_cache->reg_list[number];
buf_set_u64(reg->value, 0, reg->size, value); buf_set_u64(reg->value, 0, reg->size, value);
reg->valid = true;
} }
if (use_scratch) if (use_scratch)
@ -1225,24 +1223,12 @@ static int register_read(struct target *target, uint64_t *value, uint32_t number
*value = 0; *value = 0;
return ERROR_OK; return ERROR_OK;
} }
if (target->reg_cache &&
(number <= GDB_REGNO_XPR31 ||
(number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31))) {
/* Only check the cache for registers that we know won't spontaneously
* change. */
struct reg *reg = &target->reg_cache->reg_list[number];
if (reg && reg->valid) {
*value = buf_get_u64(reg->value, 0, reg->size);
return ERROR_OK;
}
}
int result = register_read_direct(target, value, number); int result = register_read_direct(target, value, number);
if (result != ERROR_OK) if (result != ERROR_OK)
return ERROR_FAIL; return ERROR_FAIL;
if (target->reg_cache) { if (target->reg_cache) {
struct reg *reg = &target->reg_cache->reg_list[number]; struct reg *reg = &target->reg_cache->reg_list[number];
buf_set_u64(reg->value, 0, reg->size, *value); buf_set_u64(reg->value, 0, reg->size, *value);
reg->valid = true;
} }
return ERROR_OK; return ERROR_OK;
} }
@ -2834,7 +2820,7 @@ static int riscv013_get_register(struct target *target,
int result = ERROR_OK; int result = ERROR_OK;
if (rid == GDB_REGNO_PC) { if (rid == GDB_REGNO_PC) {
result = register_read(target, value, GDB_REGNO_DPC); result = register_read(target, value, GDB_REGNO_DPC);
LOG_DEBUG("read PC from DPC: 0x%016" PRIx64, *value); LOG_DEBUG("read PC from DPC: 0x%" PRIx64, *value);
} else if (rid == GDB_REGNO_PRIV) { } else if (rid == GDB_REGNO_PRIV) {
uint64_t dcsr; uint64_t dcsr;
result = register_read(target, &dcsr, GDB_REGNO_DCSR); result = register_read(target, &dcsr, GDB_REGNO_DCSR);
@ -2858,7 +2844,7 @@ static int riscv013_set_register(struct target *target, int hid, int rid, uint64
if (rid <= GDB_REGNO_XPR31) { if (rid <= GDB_REGNO_XPR31) {
return register_write_direct(target, rid, value); return register_write_direct(target, rid, value);
} else if (rid == GDB_REGNO_PC) { } else if (rid == GDB_REGNO_PC) {
LOG_DEBUG("writing PC to DPC: 0x%016" PRIx64, value); LOG_DEBUG("writing PC to DPC: 0x%" PRIx64, value);
register_write_direct(target, GDB_REGNO_DPC, value); register_write_direct(target, GDB_REGNO_DPC, value);
uint64_t actual_value; uint64_t actual_value;
register_read_direct(target, &actual_value, GDB_REGNO_DPC); register_read_direct(target, &actual_value, GDB_REGNO_DPC);
@ -3002,6 +2988,7 @@ static enum riscv_halt_reason riscv013_halt_reason(struct target *target)
* already set when we connected. Force enumeration now, which has the * already set when we connected. Force enumeration now, which has the
* side effect of clearing any triggers we did not set. */ * side effect of clearing any triggers we did not set. */
riscv_enumerate_triggers(target); riscv_enumerate_triggers(target);
LOG_DEBUG("[%d] halted because of trigger", target->coreid);
return RISCV_HALT_TRIGGER; return RISCV_HALT_TRIGGER;
case CSR_DCSR_CAUSE_STEP: case CSR_DCSR_CAUSE_STEP:
return RISCV_HALT_SINGLESTEP; return RISCV_HALT_SINGLESTEP;

View File

@ -489,8 +489,8 @@ static int add_trigger(struct target *target, struct trigger *trigger)
if (result != ERROR_OK) if (result != ERROR_OK)
continue; continue;
LOG_DEBUG("Using trigger %d (type %d) for bp %d", i, type, LOG_DEBUG("[%d] Using trigger %d (type %d) for bp %d", target->coreid,
trigger->unique_id); i, type, trigger->unique_id);
r->trigger_unique_id[i] = trigger->unique_id; r->trigger_unique_id[i] = trigger->unique_id;
break; break;
} }
@ -512,6 +512,7 @@ static int add_trigger(struct target *target, struct trigger *trigger)
int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint) int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint)
{ {
LOG_DEBUG("[%d] @0x%" TARGET_PRIxADDR, target->coreid, breakpoint->address);
assert(breakpoint); assert(breakpoint);
if (breakpoint->type == BKPT_SOFT) { if (breakpoint->type == BKPT_SOFT) {
/** @todo check RVC for size/alignment */ /** @todo check RVC for size/alignment */
@ -585,7 +586,8 @@ static int remove_trigger(struct target *target, struct trigger *trigger)
"trigger."); "trigger.");
return ERROR_FAIL; return ERROR_FAIL;
} }
LOG_DEBUG("Stop using resource %d for bp %d", i, trigger->unique_id); LOG_DEBUG("[%d] Stop using resource %d for bp %d", target->coreid, i,
trigger->unique_id);
for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) { for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) {
if (!riscv_hart_enabled(target, hartid)) if (!riscv_hart_enabled(target, hartid))
continue; continue;
@ -660,6 +662,8 @@ int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint)
int riscv_remove_watchpoint(struct target *target, int riscv_remove_watchpoint(struct target *target,
struct watchpoint *watchpoint) struct watchpoint *watchpoint)
{ {
LOG_DEBUG("[%d] @0x%" TARGET_PRIxADDR, target->coreid, watchpoint->address);
struct trigger trigger; struct trigger trigger;
trigger_from_watchpoint(&trigger, watchpoint); trigger_from_watchpoint(&trigger, watchpoint);
@ -829,13 +833,15 @@ static int old_or_new_riscv_halt(struct target *target)
static int riscv_assert_reset(struct target *target) static int riscv_assert_reset(struct target *target)
{ {
LOG_DEBUG("[%d]", target->coreid);
struct target_type *tt = get_target_type(target); struct target_type *tt = get_target_type(target);
riscv_invalidate_register_cache(target);
return tt->assert_reset(target); return tt->assert_reset(target);
} }
static int riscv_deassert_reset(struct target *target) static int riscv_deassert_reset(struct target *target)
{ {
LOG_DEBUG("RISCV DEASSERT RESET"); LOG_DEBUG("[%d]", target->coreid);
struct target_type *tt = get_target_type(target); struct target_type *tt = get_target_type(target);
return tt->deassert_reset(target); return tt->deassert_reset(target);
} }
@ -856,8 +862,28 @@ static int old_or_new_riscv_resume(
int handle_breakpoints, int handle_breakpoints,
int debug_execution int debug_execution
){ ){
RISCV_INFO(r);
LOG_DEBUG("handle_breakpoints=%d", handle_breakpoints); LOG_DEBUG("handle_breakpoints=%d", handle_breakpoints);
if (target->smp) {
struct target_list *targets = target->head;
int result = ERROR_OK;
while (targets) {
struct target *t = targets->target;
riscv_info_t *r = riscv_info(t);
if (r->is_halted == NULL) {
if (oldriscv_resume(t, current, address, handle_breakpoints,
debug_execution) != ERROR_OK)
result = ERROR_FAIL;
} else {
if (riscv_openocd_resume(t, current, address,
handle_breakpoints, debug_execution) != ERROR_OK)
result = ERROR_FAIL;
}
targets = targets->next;
}
return result;
}
RISCV_INFO(r);
if (r->is_halted == NULL) if (r->is_halted == NULL)
return oldriscv_resume(target, current, address, handle_breakpoints, debug_execution); return oldriscv_resume(target, current, address, handle_breakpoints, debug_execution);
else else
@ -911,7 +937,7 @@ static int riscv_get_gdb_reg_list(struct target *target,
switch (reg_class) { switch (reg_class) {
case REG_CLASS_GENERAL: case REG_CLASS_GENERAL:
*reg_list_size = 32; *reg_list_size = 33;
break; break;
case REG_CLASS_ALL: case REG_CLASS_ALL:
*reg_list_size = target->reg_cache->num_regs; *reg_list_size = target->reg_cache->num_regs;
@ -925,10 +951,19 @@ static int riscv_get_gdb_reg_list(struct target *target,
if (!*reg_list) if (!*reg_list)
return ERROR_FAIL; return ERROR_FAIL;
bool read = true;
for (int i = 0; i < *reg_list_size; i++) { for (int i = 0; i < *reg_list_size; i++) {
assert(!target->reg_cache->reg_list[i].valid || assert(!target->reg_cache->reg_list[i].valid ||
target->reg_cache->reg_list[i].size > 0); target->reg_cache->reg_list[i].size > 0);
(*reg_list)[i] = &target->reg_cache->reg_list[i]; (*reg_list)[i] = &target->reg_cache->reg_list[i];
if (read && !target->reg_cache->reg_list[i].valid) {
/* This function gets called from
* gdb_target_description_supported(), and we end up failing in
* that case. Allow failures for now. */
if (target->reg_cache->reg_list[i].type->get(
&target->reg_cache->reg_list[i]) != ERROR_OK)
read = false;
}
} }
return ERROR_OK; return ERROR_OK;
@ -1110,6 +1145,30 @@ static enum riscv_poll_hart riscv_poll_hart(struct target *target, int hartid)
return RPH_NO_CHANGE; return RPH_NO_CHANGE;
} }
int set_debug_reason(struct target *target, int hartid)
{
switch (riscv_halt_reason(target, hartid)) {
case RISCV_HALT_BREAKPOINT:
target->debug_reason = DBG_REASON_BREAKPOINT;
break;
case RISCV_HALT_TRIGGER:
target->debug_reason = DBG_REASON_WATCHPOINT;
break;
case RISCV_HALT_INTERRUPT:
target->debug_reason = DBG_REASON_DBGRQ;
break;
case RISCV_HALT_SINGLESTEP:
target->debug_reason = DBG_REASON_SINGLESTEP;
break;
case RISCV_HALT_UNKNOWN:
target->debug_reason = DBG_REASON_UNDEFINED;
break;
case RISCV_HALT_ERROR:
return ERROR_FAIL;
}
return ERROR_OK;
}
/*** OpenOCD Interface ***/ /*** OpenOCD Interface ***/
int riscv_openocd_poll(struct target *target) int riscv_openocd_poll(struct target *target)
{ {
@ -1144,6 +1203,64 @@ int riscv_openocd_poll(struct target *target)
* harts. */ * harts. */
for (int i = 0; i < riscv_count_harts(target); ++i) for (int i = 0; i < riscv_count_harts(target); ++i)
riscv_halt_one_hart(target, i); riscv_halt_one_hart(target, i);
} else if (target->smp) {
bool halt_discovered = false;
bool newly_halted[128] = {0};
unsigned i = 0;
for (struct target_list *list = target->head; list != NULL;
list = list->next, i++) {
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;
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:
return ERROR_FAIL;
}
}
if (halt_discovered) {
LOG_DEBUG("Halt other targets in this SMP group.");
i = 0;
for (struct target_list *list = target->head; list != NULL;
list = list->next, i++) {
struct target *t = list->target;
riscv_info_t *r = riscv_info(t);
if (t->state != TARGET_HALTED) {
if (riscv_halt_one_hart(t, r->current_hartid) != ERROR_OK)
return ERROR_FAIL;
t->state = TARGET_HALTED;
if (set_debug_reason(t, r->current_hartid) != ERROR_OK)
return ERROR_FAIL;
newly_halted[i] = true;
}
}
/* Now that we have all our ducks in a row, tell the higher layers
* what just happened. */
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);
}
}
return ERROR_OK;
} else { } else {
enum riscv_poll_hart out = riscv_poll_hart(target, enum riscv_poll_hart out = riscv_poll_hart(target,
riscv_current_hartid(target)); riscv_current_hartid(target));
@ -1157,25 +1274,8 @@ int riscv_openocd_poll(struct target *target)
} }
target->state = TARGET_HALTED; target->state = TARGET_HALTED;
switch (riscv_halt_reason(target, halted_hart)) { if (set_debug_reason(target, halted_hart) != ERROR_OK)
case RISCV_HALT_BREAKPOINT:
target->debug_reason = DBG_REASON_BREAKPOINT;
break;
case RISCV_HALT_TRIGGER:
target->debug_reason = DBG_REASON_WATCHPOINT;
break;
case RISCV_HALT_INTERRUPT:
target->debug_reason = DBG_REASON_DBGRQ;
break;
case RISCV_HALT_SINGLESTEP:
target->debug_reason = DBG_REASON_SINGLESTEP;
break;
case RISCV_HALT_UNKNOWN:
target->debug_reason = DBG_REASON_UNDEFINED;
break;
case RISCV_HALT_ERROR:
return ERROR_FAIL; return ERROR_FAIL;
}
if (riscv_rtos_enabled(target)) { if (riscv_rtos_enabled(target)) {
target->rtos->current_threadid = halted_hart + 1; target->rtos->current_threadid = halted_hart + 1;
@ -1198,16 +1298,26 @@ int riscv_openocd_poll(struct target *target)
int riscv_openocd_halt(struct target *target) int riscv_openocd_halt(struct target *target)
{ {
RISCV_INFO(r); RISCV_INFO(r);
int result;
LOG_DEBUG("halting all harts"); LOG_DEBUG("[%d] halting all harts", target->coreid);
int out = riscv_halt_all_harts(target); if (target->smp) {
if (out != ERROR_OK) { LOG_DEBUG("Halt other targets in this SMP group.");
LOG_ERROR("Unable to halt all harts"); struct target_list *targets = target->head;
return out; result = ERROR_OK;
while (targets) {
struct target *t = targets->target;
targets = targets->next;
if (t->state != TARGET_HALTED) {
if (riscv_halt_all_harts(t) != ERROR_OK)
result = ERROR_FAIL;
}
}
} else {
result = riscv_halt_all_harts(target);
} }
register_cache_invalidate(target->reg_cache);
if (riscv_rtos_enabled(target)) { if (riscv_rtos_enabled(target)) {
if (r->rtos_hartid != -1) { if (r->rtos_hartid != -1) {
LOG_DEBUG("halt requested on RTOS hartid %d", r->rtos_hartid); LOG_DEBUG("halt requested on RTOS hartid %d", r->rtos_hartid);
@ -1220,7 +1330,7 @@ int riscv_openocd_halt(struct target *target)
target->state = TARGET_HALTED; target->state = TARGET_HALTED;
target->debug_reason = DBG_REASON_DBGRQ; target->debug_reason = DBG_REASON_DBGRQ;
target_call_event_callbacks(target, TARGET_EVENT_HALTED); target_call_event_callbacks(target, TARGET_EVENT_HALTED);
return out; return result;
} }
int riscv_openocd_resume( int riscv_openocd_resume(
@ -1909,7 +2019,9 @@ int riscv_halt_one_hart(struct target *target, int hartid)
return ERROR_OK; return ERROR_OK;
} }
return r->halt_current_hart(target); int result = r->halt_current_hart(target);
register_cache_invalidate(target->reg_cache);
return result;
} }
int riscv_resume_all_harts(struct target *target) int riscv_resume_all_harts(struct target *target)
@ -1997,9 +2109,10 @@ int riscv_xlen_of_hart(const struct target *target, int hartid)
return r->xlen[hartid]; return r->xlen[hartid];
} }
extern struct rtos_type riscv_rtos;
bool riscv_rtos_enabled(const struct target *target) bool riscv_rtos_enabled(const struct target *target)
{ {
return target->rtos != NULL; return target->rtos && target->rtos->type == &riscv_rtos;
} }
int riscv_set_current_hartid(struct target *target, int hartid) int riscv_set_current_hartid(struct target *target, int hartid)
@ -2017,10 +2130,9 @@ int riscv_set_current_hartid(struct target *target, int hartid)
/* This might get called during init, in which case we shouldn't be /* This might get called during init, in which case we shouldn't be
* setting up the register cache. */ * setting up the register cache. */
if (!target_was_examined(target)) if (target_was_examined(target) && riscv_rtos_enabled(target))
return ERROR_OK; riscv_invalidate_register_cache(target);
riscv_invalidate_register_cache(target);
return ERROR_OK; return ERROR_OK;
} }
@ -2104,6 +2216,12 @@ int riscv_get_register_on_hart(struct target *target, riscv_reg_t *value,
if (hartid != riscv_current_hartid(target)) if (hartid != riscv_current_hartid(target))
riscv_invalidate_register_cache(target); riscv_invalidate_register_cache(target);
struct reg *reg = &target->reg_cache->reg_list[regid];
if (reg && reg->valid) {
*value = buf_get_u64(reg->value, 0, reg->size);
return ERROR_OK;
}
int result = r->get_register(target, value, hartid, regid); int result = r->get_register(target, value, hartid, regid);
if (hartid != riscv_current_hartid(target)) if (hartid != riscv_current_hartid(target))
@ -2317,6 +2435,15 @@ static int register_get(struct reg *reg)
if (result != ERROR_OK) if (result != ERROR_OK)
return result; return result;
buf_set_u64(reg->value, 0, reg->size, value); buf_set_u64(reg->value, 0, reg->size, value);
/* CSRs (and possibly other extension) registers may change value at any
* time. */
if (reg->number <= GDB_REGNO_XPR31 ||
(reg->number >= GDB_REGNO_FPR0 && reg->number <= GDB_REGNO_FPR31) ||
reg->number == GDB_REGNO_PC)
reg->valid = true;
LOG_DEBUG("[%d,%d] read 0x%" PRIx64 " from %s (valid=%d)",
target->coreid, riscv_current_hartid(target), value, reg->name,
reg->valid);
return ERROR_OK; return ERROR_OK;
} }
@ -2327,9 +2454,16 @@ static int register_set(struct reg *reg, uint8_t *buf)
uint64_t value = buf_get_u64(buf, 0, reg->size); uint64_t value = buf_get_u64(buf, 0, reg->size);
LOG_DEBUG("write 0x%" PRIx64 " to %s", value, reg->name); LOG_DEBUG("[%d,%d] write 0x%" PRIx64 " to %s (valid=%d)",
target->coreid, riscv_current_hartid(target), value, reg->name,
reg->valid);
struct reg *r = &target->reg_cache->reg_list[reg->number]; struct reg *r = &target->reg_cache->reg_list[reg->number];
r->valid = true; /* CSRs (and possibly other extension) registers may change value at any
* time. */
if (reg->number <= GDB_REGNO_XPR31 ||
(reg->number >= GDB_REGNO_FPR0 && reg->number <= GDB_REGNO_FPR31) ||
reg->number == GDB_REGNO_PC)
r->valid = true;
memcpy(r->value, buf, (r->size + 7) / 8); memcpy(r->value, buf, (r->size + 7) / 8);
riscv_set_register(target, reg->number, value); riscv_set_register(target, reg->number, value);

View File

@ -1575,8 +1575,9 @@ int target_call_event_callbacks(struct target *target, enum target_event event)
target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT); target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT);
} }
LOG_DEBUG("target event %i (%s)", event, LOG_DEBUG("target event %i (%s) for core %d", event,
Jim_Nvp_value2name_simple(nvp_target_event, event)->name); Jim_Nvp_value2name_simple(nvp_target_event, event)->name,
target->coreid);
target_handle_event(target, event); target_handle_event(target, event);