gdb_server, rtos: Fine-grained RTOS register access

1. Add get_thread_reg() to rtos. It's used in rtos_get_gdb_reg() to read
the value of a single register, instead of reading all register values
by calling get_thread_reg_list().
2. Add set_reg() to rtos. gdb_server uses this to change a single
register value for a specific thread.
3. Add target_get_gdb_reg_list_noread() so it's possible for gdb to get
a list of registers without attempting to read their contents.

The clang static checker doesn't find any new problems with this change.

Change-Id: I77f792d1238cb015b91527ca8cb99593ccc8870e
Signed-off-by: Tim Newsome <tim@sifive.com>
Reviewed-on: http://openocd.zylin.com/5114
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>
This commit is contained in:
Tim Newsome
2019-04-08 16:42:48 -07:00
committed by Andreas Fritiofson
parent 16496488d1
commit 0819541366
8 changed files with 158 additions and 50 deletions

View File

@@ -724,7 +724,7 @@ static int gdb_output(struct command_context *context, const char *line)
static void gdb_signal_reply(struct target *target, struct connection *connection)
{
struct gdb_connection *gdb_connection = connection->priv;
char sig_reply[45];
char sig_reply[65];
char stop_reason[20];
char current_thread[25];
int sig_reply_len;
@@ -735,17 +735,25 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio
if (target->debug_reason == DBG_REASON_EXIT) {
sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "W00");
} else {
struct target *ct;
if (target->rtos != NULL) {
target->rtos->current_threadid = target->rtos->current_thread;
target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &ct);
} else {
ct = target;
}
if (gdb_connection->ctrl_c) {
signal_var = 0x2;
} else
signal_var = gdb_last_signal(target);
signal_var = gdb_last_signal(ct);
stop_reason[0] = '\0';
if (target->debug_reason == DBG_REASON_WATCHPOINT) {
if (ct->debug_reason == DBG_REASON_WATCHPOINT) {
enum watchpoint_rw hit_wp_type;
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) {
case WPT_WRITE:
@@ -767,15 +775,9 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio
}
current_thread[0] = '\0';
if (target->rtos != NULL) {
struct target *ct;
snprintf(current_thread, sizeof(current_thread), "thread:%016" PRIx64 ";",
if (target->rtos != NULL)
snprintf(current_thread, sizeof(current_thread), "thread:%" PRIx64 ";",
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",
signal_var, stop_reason, current_thread);
@@ -1300,7 +1302,7 @@ static int gdb_get_register_packet(struct connection *connection,
if ((target->rtos != NULL) && (ERROR_OK == rtos_get_gdb_reg(connection, reg_num)))
return ERROR_OK;
retval = target_get_gdb_reg_list(target, &reg_list, &reg_list_size,
retval = target_get_gdb_reg_list_noread(target, &reg_list, &reg_list_size,
REG_CLASS_ALL);
if (retval != ERROR_OK)
return gdb_error(connection, retval);
@@ -1336,37 +1338,49 @@ static int gdb_set_register_packet(struct connection *connection,
{
struct target *target = get_target_from_connection(connection);
char *separator;
uint8_t *bin_buf;
int reg_num = strtoul(packet + 1, &separator, 16);
struct reg **reg_list;
int reg_list_size;
int retval;
#ifdef _DEBUG_GDB_IO_
LOG_DEBUG("-");
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;
}
#endif
if (*separator != '=') {
LOG_ERROR("GDB 'set register packet', but no '=' following the register number");
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) */
bin_buf = malloc(DIV_ROUND_UP(reg_list[reg_num]->size, 8));
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);
if ((target->rtos != NULL) &&
(ERROR_OK == rtos_set_reg(connection, reg_num, bin_buf))) {
free(bin_buf);
gdb_put_packet(connection, "OK", 2);
return ERROR_OK;
}
retval = target_get_gdb_reg_list_noread(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;
}
@@ -1634,7 +1648,7 @@ static int gdb_breakpoint_watchpoint_packet(struct connection *connection,
char *separator;
int retval;
LOG_DEBUG("-");
LOG_DEBUG("[%s]", target_name(target));
type = strtoul(packet + 1, &separator, 16);
@@ -2200,7 +2214,7 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o
arch_defined_types = calloc(1, sizeof(char *));
retval = target_get_gdb_reg_list(target, &reg_list,
retval = target_get_gdb_reg_list_noread(target, &reg_list,
&reg_list_size, REG_CLASS_ALL);
if (retval != ERROR_OK) {
@@ -2388,7 +2402,7 @@ static int gdb_target_description_supported(struct target *target, int *supporte
char const *architecture = target_get_gdb_arch(target);
retval = target_get_gdb_reg_list(target, &reg_list,
retval = target_get_gdb_reg_list_noread(target, &reg_list,
&reg_list_size, REG_CLASS_ALL);
if (retval != ERROR_OK) {
LOG_ERROR("get register list failed");
@@ -2783,13 +2797,11 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p
if (parse[0] == 'c') {
parse += 1;
packet_size -= 1;
/* check if thread-id follows */
if (parse[0] == ':') {
int64_t tid;
parse += 1;
packet_size -= 1;
tid = strtoll(parse, &endp, 16);
if (tid == thread_id) {
@@ -2879,10 +2891,9 @@ static int gdb_v_packet(struct connection *connection,
char const *packet, int packet_size)
{
struct gdb_connection *gdb_connection = connection->priv;
struct target *target;
int result;
target = get_target_from_connection(connection);
struct target *target = get_target_from_connection(connection);
if (strncmp(packet, "vCont", 5) == 0) {
bool handled;