gdb_server: run control fixes for vCont
[openocd.git] / src / server / gdb_server.c
index d7fff66a06e32bd9f1278d667fa92075d4143bd8..a15c5bb810499ce0c285924e3de9ef2429fa9ef0 100644 (file)
@@ -41,6 +41,8 @@
 #include <target/breakpoints.h>
 #include <target/target_request.h>
 #include <target/register.h>
+#include <target/target.h>
+#include <target/target_type.h>
 #include "server.h"
 #include <flash/nor/core.h>
 #include "gdb_server.h"
@@ -71,8 +73,8 @@ struct gdb_connection {
        int ctrl_c;
        enum target_state frontend_state;
        struct image *vflash_image;
-       int closed;
-       int busy;
+       bool closed;
+       bool busy;
        int noack_mode;
        /* set flag to true if you want the next stepi to return immediately.
         * allowing GDB to pick up a fresh set of register values from the target
@@ -110,6 +112,8 @@ static char *gdb_port_next;
 static void gdb_log_callback(void *priv, const char *file, unsigned line,
                const char *function, const char *string);
 
+static void gdb_sig_halted(struct connection *connection);
+
 /* number of gdb connections, mainly to suppress gdb related debugging spam
  * in helper/log.c when no gdb connections are actually active */
 int gdb_actual_connections;
@@ -215,7 +219,7 @@ static int gdb_get_char_inner(struct connection *connection, int *next_char)
                if (gdb_con->buf_cnt > 0)
                        break;
                if (gdb_con->buf_cnt == 0) {
-                       gdb_con->closed = 1;
+                       gdb_con->closed = true;
                        return ERROR_SERVER_REMOTE_CLOSED;
                }
 
@@ -227,10 +231,10 @@ static int gdb_get_char_inner(struct connection *connection, int *next_char)
                                usleep(1000);
                                break;
                        case WSAECONNABORTED:
-                               gdb_con->closed = 1;
+                               gdb_con->closed = true;
                                return ERROR_SERVER_REMOTE_CLOSED;
                        case WSAECONNRESET:
-                               gdb_con->closed = 1;
+                               gdb_con->closed = true;
                                return ERROR_SERVER_REMOTE_CLOSED;
                        default:
                                LOG_ERROR("read: %d", errno);
@@ -242,14 +246,14 @@ static int gdb_get_char_inner(struct connection *connection, int *next_char)
                                usleep(1000);
                                break;
                        case ECONNABORTED:
-                               gdb_con->closed = 1;
+                               gdb_con->closed = true;
                                return ERROR_SERVER_REMOTE_CLOSED;
                        case ECONNRESET:
-                               gdb_con->closed = 1;
+                               gdb_con->closed = true;
                                return ERROR_SERVER_REMOTE_CLOSED;
                        default:
                                LOG_ERROR("read: %s", strerror(errno));
-                               gdb_con->closed = 1;
+                               gdb_con->closed = true;
                                return ERROR_SERVER_REMOTE_CLOSED;
                }
 #endif
@@ -341,7 +345,7 @@ static int gdb_write(struct connection *connection, void *data, int len)
 
        if (connection_write(connection, data, len) == len)
                return ERROR_OK;
-       gdb_con->closed = 1;
+       gdb_con->closed = true;
        return ERROR_SERVER_REMOTE_CLOSED;
 }
 
@@ -448,7 +452,7 @@ static int gdb_put_packet_inner(struct connection *connection,
                                return ERROR_OK;
                        } else {
                                LOG_ERROR("unknown character(1) 0x%2.2x in reply, dropping connection", reply);
-                               gdb_con->closed = 1;
+                               gdb_con->closed = true;
                                return ERROR_SERVER_REMOTE_CLOSED;
                        }
                } else if (reply == '$') {
@@ -458,7 +462,7 @@ static int gdb_put_packet_inner(struct connection *connection,
                } else {
                        LOG_ERROR("unknown character(2) 0x%2.2x in reply, dropping connection",
                                reply);
-                       gdb_con->closed = 1;
+                       gdb_con->closed = true;
                        return ERROR_SERVER_REMOTE_CLOSED;
                }
        }
@@ -471,9 +475,9 @@ static int gdb_put_packet_inner(struct connection *connection,
 int gdb_put_packet(struct connection *connection, char *buffer, int len)
 {
        struct gdb_connection *gdb_con = connection->priv;
-       gdb_con->busy = 1;
+       gdb_con->busy = true;
        int retval = gdb_put_packet_inner(connection, buffer, len);
-       gdb_con->busy = 0;
+       gdb_con->busy = false;
 
        /* we sent some data, reset timer for keep alive messages */
        kept_alive();
@@ -679,9 +683,9 @@ static int gdb_get_packet_inner(struct connection *connection,
 static int gdb_get_packet(struct connection *connection, char *buffer, int *len)
 {
        struct gdb_connection *gdb_con = connection->priv;
-       gdb_con->busy = 1;
+       gdb_con->busy = true;
        int retval = gdb_get_packet_inner(connection, buffer, len);
-       gdb_con->busy = 0;
+       gdb_con->busy = false;
        return retval;
 }
 
@@ -760,8 +764,12 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio
 
                current_thread[0] = '\0';
                if (target->rtos != NULL) {
-                       snprintf(current_thread, sizeof(current_thread), "thread:%016" PRIx64 ";", target->rtos->current_thread);
+                       struct target *ct;
+                       snprintf(current_thread, sizeof(current_thread), "thread:%016" 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);
+                       signal_var = gdb_last_signal(ct);
                }
 
                sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "T%2.2x%s%s",
@@ -930,8 +938,8 @@ static int gdb_new_connection(struct connection *connection)
        gdb_connection->ctrl_c = 0;
        gdb_connection->frontend_state = TARGET_HALTED;
        gdb_connection->vflash_image = NULL;
-       gdb_connection->closed = 0;
-       gdb_connection->busy = 0;
+       gdb_connection->closed = false;
+       gdb_connection->busy = false;
        gdb_connection->noack_mode = 0;
        gdb_connection->sync = false;
        gdb_connection->mem_write_error = false;
@@ -953,9 +961,14 @@ static int gdb_new_connection(struct connection *connection)
        breakpoint_clear_target(target);
        watchpoint_clear_target(target);
 
-       /* clean previous rtos session if supported*/
-       if ((target->rtos) && (target->rtos->type->clean))
-               target->rtos->type->clean(target);
+       if (target->rtos) {
+               /* clean previous rtos session if supported*/
+               if (target->rtos->type->clean)
+                       target->rtos->type->clean(target);
+
+               /* update threads */
+               rtos_update_threads(target);
+       }
 
        /* remove the initial ACK from the incoming buffer */
        retval = gdb_get_char(connection, &initial_ack);
@@ -991,7 +1004,9 @@ static int gdb_new_connection(struct connection *connection)
        }
 
        gdb_actual_connections++;
-       LOG_DEBUG("New GDB Connection: %d, Target %s, state: %s",
+       log_printf_lf(all_targets->next != NULL ? LOG_LVL_INFO : LOG_LVL_DEBUG,
+                       __FILE__, __LINE__, __func__,
+                       "New GDB Connection: %d, Target %s, state: %s",
                        gdb_actual_connections,
                        target_name(target),
                        target_state_name(target));
@@ -2413,13 +2428,13 @@ static int gdb_query_packet(struct connection *connection,
                        char gdb_reply[10];
                        char *separator;
                        uint32_t checksum;
-                       uint32_t addr = 0;
+                       target_addr_t addr = 0;
                        uint32_t len = 0;
 
                        /* skip command character */
                        packet += 5;
 
-                       addr = strtoul(packet, &separator, 16);
+                       addr = strtoull(packet, &separator, 16);
 
                        if (*separator != ',') {
                                LOG_ERROR("incomplete read memory packet received, dropping connection");
@@ -2468,7 +2483,7 @@ static int gdb_query_packet(struct connection *connection,
                        &buffer,
                        &pos,
                        &size,
-                       "PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read%c;qXfer:threads:read+;QStartNoAckMode+",
+                       "PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read%c;qXfer:threads:read+;QStartNoAckMode+;vContSupported+",
                        (GDB_BUFFER_SIZE - 1),
                        ((gdb_use_memory_map == 1) && (flash_get_bank_count() > 0)) ? '+' : '-',
                        (gdb_target_desc_supported == 1) ? '+' : '-');
@@ -2557,6 +2572,149 @@ static int gdb_query_packet(struct connection *connection,
        return ERROR_OK;
 }
 
+static bool gdb_handle_vcont_packet(struct connection *connection, const char *packet, int packet_size)
+{
+       struct gdb_connection *gdb_connection = connection->priv;
+       struct target *target = get_target_from_connection(connection);
+       const char *parse = packet;
+       int retval;
+
+       /* query for vCont supported */
+       if (parse[0] == '?') {
+               if (target->type->step != NULL) {
+                       /* gdb doesn't accept c without C and s without S */
+                       gdb_put_packet(connection, "vCont;c;C;s;S", 13);
+                       return true;
+               }
+               return false;
+       }
+
+       if (parse[0] == ';') {
+               ++parse;
+               --packet_size;
+       }
+
+       /* simple case, a continue packet */
+       if (parse[0] == 'c') {
+               LOG_DEBUG("target %s continue", target_name(target));
+               log_add_callback(gdb_log_callback, connection);
+               retval = target_resume(target, 1, 0, 0, 0);
+               if (retval == ERROR_TARGET_NOT_HALTED)
+                       LOG_INFO("target %s was not halted when resume was requested", target_name(target));
+
+               /* poll target in an attempt to make its internal state consistent */
+               if (retval != ERROR_OK) {
+                       retval = target_poll(target);
+                       if (retval != ERROR_OK)
+                               LOG_DEBUG("error polling target %s after failed resume", target_name(target));
+               }
+
+               /*
+                * We don't report errors to gdb here, move frontend_state to
+                * TARGET_RUNNING to stay in sync with gdb's expectation of the
+                * target state
+                */
+               gdb_connection->frontend_state = TARGET_RUNNING;
+               target_call_event_callbacks(target, TARGET_EVENT_GDB_START);
+
+               return true;
+       }
+
+       /* single-step or step-over-breakpoint */
+       if (parse[0] == 's') {
+               if (strncmp(parse, "s:", 2) == 0) {
+                       struct target *ct = target;
+                       int current_pc = 1;
+                       int64_t thread_id;
+                       char *endp;
+
+                       parse += 2;
+                       packet_size -= 2;
+
+                       thread_id = strtoll(parse, &endp, 16);
+                       if (endp != NULL) {
+                               packet_size -= endp - parse;
+                               parse = endp;
+                       }
+
+                       if (target->rtos != NULL)
+                               target->rtos->gdb_target_for_threadid(connection, thread_id, &ct);
+
+                       if (parse[0] == ';') {
+                               ++parse;
+                               --packet_size;
+
+                               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) {
+                                                       /*
+                                                        * Special case: only step a single thread (core),
+                                                        * keep the other threads halted. Currently, only
+                                                        * aarch64 target understands it. Other target types don't
+                                                        * care (nobody checks the actual value of 'current')
+                                                        * and it doesn't really matter. This deserves
+                                                        * a symbolic constant and a formal interface documentation
+                                                        * at a later time.
+                                                        */
+                                                       LOG_DEBUG("request to step current core only");
+                                                       /* uncomment after checking that indeed other targets are safe */
+                                                       /*current_pc = 2;*/
+                                               }
+                                       }
+                               }
+                       }
+
+                       LOG_DEBUG("target %s single-step thread %"PRId64, target_name(ct), thread_id);
+                       log_add_callback(gdb_log_callback, connection);
+                       target_call_event_callbacks(ct, TARGET_EVENT_GDB_START);
+
+                       /* support for gdb_sync command */
+                       if (gdb_connection->sync) {
+                               gdb_connection->sync = false;
+                               if (ct->state == TARGET_HALTED) {
+                                       LOG_WARNING("stepi ignored. GDB will now fetch the register state " \
+                                                                       "from the target.");
+                                       gdb_sig_halted(connection);
+                                       log_remove_callback(gdb_log_callback, connection);
+                               } else
+                                       gdb_connection->frontend_state = TARGET_RUNNING;
+                               return true;
+                       }
+
+                       retval = target_step(ct, current_pc, 0, 0);
+                       if (retval == ERROR_TARGET_NOT_HALTED)
+                               LOG_INFO("target %s was not halted when step was requested", target_name(ct));
+
+                       /* if step was successful send a reply back to gdb */
+                       if (retval == ERROR_OK) {
+                               retval = target_poll(ct);
+                               if (retval != ERROR_OK)
+                                       LOG_DEBUG("error polling target %s after successful step", target_name(ct));
+                               /* send back signal information */
+                               gdb_signal_reply(ct, connection);
+                               /* stop forwarding log packets! */
+                               log_remove_callback(gdb_log_callback, connection);
+                       } else
+                               gdb_connection->frontend_state = TARGET_RUNNING;
+               } else {
+                       LOG_ERROR("Unknown vCont packet");
+                       return false;
+               }
+               return true;
+       }
+
+       return false;
+}
+
 static int gdb_v_packet(struct connection *connection,
                char const *packet, int packet_size)
 {
@@ -2566,6 +2724,19 @@ static int gdb_v_packet(struct connection *connection,
 
        target = get_target_from_connection(connection);
 
+       if (strncmp(packet, "vCont", 5) == 0) {
+               bool handled;
+
+               packet += 5;
+               packet_size -= 5;
+
+               handled = gdb_handle_vcont_packet(connection, packet, packet_size);
+               if (!handled)
+                       gdb_put_packet(connection, "", 0);
+
+               return ERROR_OK;
+       }
+
        /* if flash programming disabled - send a empty reply */
 
        if (gdb_flash_program == 0) {
@@ -3005,7 +3176,12 @@ static int gdb_input_inner(struct connection *connection)
 
                if (gdb_con->ctrl_c) {
                        if (target->state == TARGET_RUNNING) {
-                               retval = target_halt(target);
+                               struct target *t = target;
+                               if (target->rtos)
+                                       target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &t);
+                               retval = target_halt(t);
+                               if (retval == ERROR_OK)
+                                       retval = target_poll(t);
                                if (retval != ERROR_OK)
                                        target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT);
                                gdb_con->ctrl_c = 0;
@@ -3088,7 +3264,13 @@ static int gdb_target_add_one(struct target *target)
                if (!*end) {
                        if (parse_long(gdb_port_next, &portnumber) == ERROR_OK) {
                                free(gdb_port_next);
-                               gdb_port_next = alloc_printf("%d", portnumber+1);
+                               if (portnumber) {
+                                       gdb_port_next = alloc_printf("%d", portnumber+1);
+                               } else {
+                                       /* Don't increment if gdb_port is 0, since we're just
+                                        * trying to allocate an unused port. */
+                                       gdb_port_next = strdup("0");
+                               }
                        }
                }
        }

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)