jtag: linuxgpiod: drop extra parenthesis
[openocd.git] / src / server / gdb_server.c
index c369665547f14c545a832c824fea5b87a23759d0..c1ee4aa2a18606c746cf97b7ba58d137d1955d28 100644 (file)
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
 /***************************************************************************
  *   Copyright (C) 2005 by Dominic Rath                                    *
  *   Dominic.Rath@gmx.de                                                   *
  *                                                                         *
  *   Copyright (C) 2013 Franck Jullien                                     *
  *   elec4fun@gmail.com                                                    *
- *                                                                         *
- *   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
  * found in most modern embedded processors.
  */
 
+enum gdb_output_flag {
+       /* GDB doesn't accept 'O' packets */
+       GDB_OUTPUT_NO,
+       /* GDB doesn't accept 'O' packets but accepts notifications */
+       GDB_OUTPUT_NOTIF,
+       /* GDB accepts 'O' packets */
+       GDB_OUTPUT_ALL,
+};
+
 struct target_desc_format {
        char *tdesc;
        uint32_t tdesc_length;
@@ -75,6 +73,8 @@ struct gdb_connection {
        enum target_state frontend_state;
        struct image *vflash_image;
        bool closed;
+       /* set to prevent re-entrance from log messages during gdb_get_packet()
+        * and gdb_put_packet(). */
        bool busy;
        int noack_mode;
        /* set flag to true if you want the next stepi to return immediately.
@@ -97,6 +97,10 @@ struct gdb_connection {
        struct target_desc_format target_desc;
        /* temporarily used for thread list support */
        char *thread_list;
+       /* flag to mask the output from gdb_log_callback() */
+       enum gdb_output_flag output_flag;
+       /* Unique index for this GDB connection. */
+       unsigned int unique_index;
 };
 
 #if 0
@@ -119,7 +123,7 @@ 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;
+static int gdb_actual_connections;
 
 /* set if we are sending a memory map to gdb
  * via qXfer:memory-map:read packet */
@@ -147,6 +151,9 @@ static char gdb_running_type;
 
 static int gdb_last_signal(struct target *target)
 {
+       LOG_TARGET_DEBUG(target, "Debug reason is: %s",
+                       target_debug_reason_str(target->debug_reason));
+
        switch (target->debug_reason) {
                case DBG_REASON_DBGRQ:
                        return 0x2;             /* SIGINT */
@@ -161,8 +168,9 @@ static int gdb_last_signal(struct target *target)
                case DBG_REASON_NOTHALTED:
                        return 0x0;             /* no signal... shouldn't happen */
                default:
-                       LOG_USER("undefined debug reason %d - target needs reset",
-                                       target->debug_reason);
+                       LOG_USER("undefined debug reason %d (%s) - target needs reset",
+                                       target->debug_reason,
+                                       target_debug_reason_str(target->debug_reason));
                        return 0x0;
        }
 }
@@ -177,7 +185,7 @@ static int check_pending(struct connection *connection,
        fd_set read_fds;
        struct gdb_connection *gdb_con = connection->priv;
        int t;
-       if (got_data == NULL)
+       if (!got_data)
                got_data = &t;
        *got_data = 0;
 
@@ -227,44 +235,26 @@ 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) {
+                       LOG_DEBUG("GDB connection closed by the remote client");
                        gdb_con->closed = true;
                        return ERROR_SERVER_REMOTE_CLOSED;
                }
 
 #ifdef _WIN32
-               errno = WSAGetLastError();
-
-               switch (errno) {
-                       case WSAEWOULDBLOCK:
-                               usleep(1000);
-                               break;
-                       case WSAECONNABORTED:
-                               gdb_con->closed = true;
-                               return ERROR_SERVER_REMOTE_CLOSED;
-                       case WSAECONNRESET:
-                               gdb_con->closed = true;
-                               return ERROR_SERVER_REMOTE_CLOSED;
-                       default:
-                               LOG_ERROR("read: %d", errno);
-                               exit(-1);
-               }
+               bool retry = (WSAGetLastError() == WSAEWOULDBLOCK);
 #else
-               switch (errno) {
-                       case EAGAIN:
-                               usleep(1000);
-                               break;
-                       case ECONNABORTED:
-                               gdb_con->closed = true;
-                               return ERROR_SERVER_REMOTE_CLOSED;
-                       case ECONNRESET:
-                               gdb_con->closed = true;
-                               return ERROR_SERVER_REMOTE_CLOSED;
-                       default:
-                               LOG_ERROR("read: %s", strerror(errno));
-                               gdb_con->closed = true;
-                               return ERROR_SERVER_REMOTE_CLOSED;
-               }
+               bool retry = (errno == EAGAIN);
 #endif
+
+               if (retry) {
+                       // Try again after a delay
+                       usleep(1000);
+               } else {
+                       // Print error and close the socket
+                       log_socket_error("GDB");
+                       gdb_con->closed = true;
+                       return ERROR_SERVER_REMOTE_CLOSED;
+               }
        }
 
 #ifdef _DEBUG_GDB_IO_
@@ -348,23 +338,74 @@ static int gdb_putback_char(struct connection *connection, int last_char)
 static int gdb_write(struct connection *connection, void *data, int len)
 {
        struct gdb_connection *gdb_con = connection->priv;
-       if (gdb_con->closed)
+       if (gdb_con->closed) {
+               LOG_DEBUG("GDB socket marked as closed, cannot write to it.");
                return ERROR_SERVER_REMOTE_CLOSED;
+       }
 
        if (connection_write(connection, data, len) == len)
                return ERROR_OK;
+
+       LOG_WARNING("Error writing to GDB socket. Dropping the connection.");
        gdb_con->closed = true;
        return ERROR_SERVER_REMOTE_CLOSED;
 }
 
+static void gdb_log_incoming_packet(struct connection *connection, char *packet)
+{
+       if (!LOG_LEVEL_IS(LOG_LVL_DEBUG))
+               return;
+
+       struct target *target = get_target_from_connection(connection);
+       struct gdb_connection *gdb_connection = connection->priv;
+
+       /* Avoid dumping non-printable characters to the terminal */
+       const unsigned packet_len = strlen(packet);
+       const char *nonprint = find_nonprint_char(packet, packet_len);
+       if (nonprint) {
+               /* Does packet at least have a prefix that is printable?
+                * Look within the first 50 chars of the packet. */
+               const char *colon = memchr(packet, ':', MIN(50, packet_len));
+               const bool packet_has_prefix = (colon);
+               const bool packet_prefix_printable = (packet_has_prefix && nonprint > colon);
+
+               if (packet_prefix_printable) {
+                       const unsigned int prefix_len = colon - packet + 1;  /* + 1 to include the ':' */
+                       const unsigned int payload_len = packet_len - prefix_len;
+                       LOG_TARGET_DEBUG(target, "{%d} received packet: %.*s<binary-data-%u-bytes>",
+                               gdb_connection->unique_index, prefix_len, packet, payload_len);
+               } else {
+                       LOG_TARGET_DEBUG(target, "{%d} received packet: <binary-data-%u-bytes>",
+                               gdb_connection->unique_index, packet_len);
+               }
+       } else {
+               /* All chars printable, dump the packet as is */
+               LOG_TARGET_DEBUG(target, "{%d} received packet: %s", gdb_connection->unique_index, packet);
+       }
+}
+
+static void gdb_log_outgoing_packet(struct connection *connection, char *packet_buf,
+       unsigned int packet_len, unsigned char checksum)
+{
+       if (!LOG_LEVEL_IS(LOG_LVL_DEBUG))
+               return;
+
+       struct target *target = get_target_from_connection(connection);
+       struct gdb_connection *gdb_connection = connection->priv;
+
+       if (find_nonprint_char(packet_buf, packet_len))
+               LOG_TARGET_DEBUG(target, "{%d} sending packet: $<binary-data-%u-bytes>#%2.2x",
+                       gdb_connection->unique_index, packet_len, checksum);
+       else
+               LOG_TARGET_DEBUG(target, "{%d} sending packet: $%.*s#%2.2x",
+                       gdb_connection->unique_index, packet_len, packet_buf, checksum);
+}
+
 static int gdb_put_packet_inner(struct connection *connection,
                char *buffer, int len)
 {
        int i;
        unsigned char my_checksum = 0;
-#ifdef _DEBUG_GDB_IO_
-       char *debug_buffer;
-#endif
        int reply;
        int retval;
        struct gdb_connection *gdb_con = connection->priv;
@@ -400,11 +441,7 @@ static int gdb_put_packet_inner(struct connection *connection,
 #endif
 
        while (1) {
-#ifdef _DEBUG_GDB_IO_
-               debug_buffer = strndup(buffer, len);
-               LOG_DEBUG("sending packet '$%s#%2.2x'", debug_buffer, my_checksum);
-               free(debug_buffer);
-#endif
+               gdb_log_outgoing_packet(connection, buffer, len, my_checksum);
 
                char local_buffer[1024];
                local_buffer[0] = '$';
@@ -437,22 +474,27 @@ static int gdb_put_packet_inner(struct connection *connection,
                if (retval != ERROR_OK)
                        return retval;
 
-               if (reply == '+')
+               if (reply == '+') {
+                       gdb_log_incoming_packet(connection, "+");
                        break;
-               else if (reply == '-') {
+               else if (reply == '-') {
                        /* Stop sending output packets for now */
-                       log_remove_callback(gdb_log_callback, connection);
+                       gdb_con->output_flag = GDB_OUTPUT_NO;
+                       gdb_log_incoming_packet(connection, "-");
                        LOG_WARNING("negative reply, retrying");
                } else if (reply == 0x3) {
                        gdb_con->ctrl_c = true;
+                       gdb_log_incoming_packet(connection, "<Ctrl-C>");
                        retval = gdb_get_char(connection, &reply);
                        if (retval != ERROR_OK)
                                return retval;
-                       if (reply == '+')
+                       if (reply == '+') {
+                               gdb_log_incoming_packet(connection, "+");
                                break;
-                       else if (reply == '-') {
+                       else if (reply == '-') {
                                /* Stop sending output packets for now */
-                               log_remove_callback(gdb_log_callback, connection);
+                               gdb_con->output_flag = GDB_OUTPUT_NO;
+                               gdb_log_incoming_packet(connection, "-");
                                LOG_WARNING("negative reply, retrying");
                        } else if (reply == '$') {
                                LOG_ERROR("GDB missing ack(1) - assumed good");
@@ -629,6 +671,7 @@ static int gdb_get_packet_inner(struct connection *connection,
                                case '$':
                                        break;
                                case '+':
+                                       gdb_log_incoming_packet(connection, "+");
                                        /* According to the GDB documentation
                                         * (https://sourceware.org/gdb/onlinedocs/gdb/Packet-Acknowledgment.html):
                                         * "gdb sends a final `+` acknowledgment of the stub's `OK`
@@ -646,9 +689,11 @@ static int gdb_get_packet_inner(struct connection *connection,
                                        }
                                        break;
                                case '-':
+                                       gdb_log_incoming_packet(connection, "-");
                                        LOG_WARNING("negative acknowledgment, but no packet pending");
                                        break;
                                case 0x3:
+                                       gdb_log_incoming_packet(connection, "<Ctrl-C>");
                                        gdb_con->ctrl_c = true;
                                        *len = 0;
                                        return ERROR_OK;
@@ -705,7 +750,7 @@ static int gdb_output_con(struct connection *connection, const char *line)
        bin_size = strlen(line);
 
        hex_buffer = malloc(bin_size * 2 + 2);
-       if (hex_buffer == NULL)
+       if (!hex_buffer)
                return ERROR_GDB_BUFFER_TOO_SMALL;
 
        hex_buffer[0] = 'O';
@@ -728,7 +773,7 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio
 {
        struct gdb_connection *gdb_connection = connection->priv;
        char sig_reply[65];
-       char stop_reason[20];
+       char stop_reason[32];
        char current_thread[25];
        int sig_reply_len;
        int signal_var;
@@ -739,7 +784,7 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio
                sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "W00");
        } else {
                struct target *ct;
-               if (target->rtos != NULL) {
+               if (target->rtos) {
                        target->rtos->current_threadid = target->rtos->current_thread;
                        target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &ct);
                } else {
@@ -747,6 +792,7 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio
                }
 
                if (gdb_connection->ctrl_c) {
+                       LOG_TARGET_DEBUG(target, "Responding with signal 2 (SIGINT) to debugger due to Ctrl-C");
                        signal_var = 0x2;
                } else
                        signal_var = gdb_last_signal(ct);
@@ -778,7 +824,7 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio
                }
 
                current_thread[0] = '\0';
-               if (target->rtos != NULL)
+               if (target->rtos)
                        snprintf(current_thread, sizeof(current_thread), "thread:%" PRIx64 ";",
                                        target->rtos->current_thread);
 
@@ -895,7 +941,7 @@ static void gdb_frontend_halted(struct target *target, struct connection *connec
         */
        if (gdb_connection->frontend_state == TARGET_RUNNING) {
                /* stop forwarding log packets! */
-               log_remove_callback(gdb_log_callback, connection);
+               gdb_connection->output_flag = GDB_OUTPUT_NO;
 
                /* check fileio first */
                if (target_get_gdb_fileio_info(target, target->fileio_info) == ERROR_OK)
@@ -934,6 +980,7 @@ static int gdb_new_connection(struct connection *connection)
        struct target *target;
        int retval;
        int initial_ack;
+       static unsigned int next_unique_id = 1;
 
        target = get_target_from_connection(connection);
        connection->priv = gdb_connection;
@@ -955,9 +1002,8 @@ static int gdb_new_connection(struct connection *connection)
        gdb_connection->target_desc.tdesc = NULL;
        gdb_connection->target_desc.tdesc_length = 0;
        gdb_connection->thread_list = NULL;
-
-       /* send ACK to GDB for debug request */
-       gdb_write(connection, "+", 1);
+       gdb_connection->output_flag = GDB_OUTPUT_NO;
+       gdb_connection->unique_index = next_unique_id++;
 
        /* output goes through gdb connection */
        command_set_output_handler(connection->cmd_ctx, gdb_output, connection);
@@ -969,6 +1015,21 @@ static int gdb_new_connection(struct connection *connection)
        breakpoint_clear_target(target);
        watchpoint_clear_target(target);
 
+       /* Since version 3.95 (gdb-19990504), with the exclusion of 6.5~6.8, GDB
+        * sends an ACK at connection with the following comment in its source code:
+        * "Ack any packet which the remote side has already sent."
+        * LLDB does the same since the first gdb-remote implementation.
+        * Remove the initial ACK from the incoming buffer.
+        */
+       retval = gdb_get_char(connection, &initial_ack);
+       if (retval != ERROR_OK)
+               return retval;
+
+       if (initial_ack != '+')
+               gdb_putback_char(connection, initial_ack);
+
+       target_call_event_callbacks(target, TARGET_EVENT_GDB_ATTACH);
+
        if (target->rtos) {
                /* clean previous rtos session if supported*/
                if (target->rtos->type->clean)
@@ -978,18 +1039,6 @@ static int gdb_new_connection(struct connection *connection)
                rtos_update_threads(target);
        }
 
-       /* remove the initial ACK from the incoming buffer */
-       retval = gdb_get_char(connection, &initial_ack);
-       if (retval != ERROR_OK)
-               return retval;
-
-       /* FIX!!!??? would we actually ever receive a + here???
-        * Not observed.
-        */
-       if (initial_ack != '+')
-               gdb_putback_char(connection, initial_ack);
-       target_call_event_callbacks(target, TARGET_EVENT_GDB_ATTACH);
-
        if (gdb_use_memory_map) {
                /* Connect must fail if the memory map can't be set up correctly.
                 *
@@ -1010,20 +1059,19 @@ static int gdb_new_connection(struct connection *connection)
                }
        }
 
-       gdb_actual_connections++;
-       log_printf_lf(all_targets->next != NULL ? LOG_LVL_INFO : LOG_LVL_DEBUG,
+       log_printf_lf(all_targets->next ? LOG_LVL_INFO : LOG_LVL_DEBUG,
                        __FILE__, __LINE__, __func__,
                        "New GDB Connection: %d, Target %s, state: %s",
-                       gdb_actual_connections,
+                       gdb_connection->unique_index,
                        target_name(target),
                        target_state_name(target));
 
        if (!target_was_examined(target)) {
                LOG_ERROR("Target %s not examined yet, refuse gdb connection %d!",
-                                 target_name(target), gdb_actual_connections);
-               gdb_actual_connections--;
+                                 target_name(target), gdb_connection->unique_index);
                return ERROR_TARGET_NOT_EXAMINED;
        }
+       gdb_actual_connections++;
 
        if (target->state != TARGET_HALTED)
                LOG_WARNING("GDB connection %d on target %s not halted",
@@ -1036,6 +1084,8 @@ static int gdb_new_connection(struct connection *connection)
         * register callback to be informed about target events */
        target_register_event_callback(gdb_target_callback_event_handler, connection);
 
+       log_add_callback(gdb_log_callback, connection);
+
        return ERROR_OK;
 }
 
@@ -1052,7 +1102,8 @@ static int gdb_connection_closed(struct connection *connection)
        log_remove_callback(gdb_log_callback, connection);
 
        gdb_actual_connections--;
-       LOG_DEBUG("GDB Close, Target: %s, state: %s, gdb_actual_connections=%d",
+       LOG_DEBUG("{%d} GDB Close, Target: %s, state: %s, gdb_actual_connections=%d",
+               gdb_connection->unique_index,
                target_name(target),
                target_state_name(target),
                gdb_actual_connections);
@@ -1164,6 +1215,27 @@ static void gdb_target_to_reg(struct target *target,
        }
 }
 
+/* get register value if needed and fill the buffer accordingly */
+static int gdb_get_reg_value_as_str(struct target *target, char *tstr, struct reg *reg)
+{
+       int retval = ERROR_OK;
+
+       if (!reg->valid)
+               retval = reg->type->get(reg);
+
+       const unsigned int len = DIV_ROUND_UP(reg->size, 8) * 2;
+       switch (retval) {
+               case ERROR_OK:
+                       gdb_str_to_target(target, tstr, reg);
+                       return ERROR_OK;
+               case ERROR_TARGET_RESOURCE_NOT_AVAILABLE:
+                       memset(tstr, 'x', len);
+                       tstr[len] = '\0';
+                       return ERROR_OK;
+       }
+       return ERROR_FAIL;
+}
+
 static int gdb_get_registers_packet(struct connection *connection,
                char const *packet, int packet_size)
 {
@@ -1180,7 +1252,7 @@ static int gdb_get_registers_packet(struct connection *connection,
        LOG_DEBUG("-");
 #endif
 
-       if ((target->rtos != NULL) && (ERROR_OK == rtos_get_gdb_reg_list(connection)))
+       if ((target->rtos) && (rtos_get_gdb_reg_list(connection) == ERROR_OK))
                return ERROR_OK;
 
        retval = target_get_gdb_reg_list(target, &reg_list, &reg_list_size,
@@ -1189,7 +1261,7 @@ static int gdb_get_registers_packet(struct connection *connection,
                return gdb_error(connection, retval);
 
        for (i = 0; i < reg_list_size; i++) {
-               if (reg_list[i] == NULL || reg_list[i]->exist == false)
+               if (!reg_list[i] || reg_list[i]->exist == false || reg_list[i]->hidden)
                        continue;
                reg_packet_size += DIV_ROUND_UP(reg_list[i]->size, 8) * 2;
        }
@@ -1197,24 +1269,19 @@ static int gdb_get_registers_packet(struct connection *connection,
        assert(reg_packet_size > 0);
 
        reg_packet = malloc(reg_packet_size + 1); /* plus one for string termination null */
-       if (reg_packet == NULL)
+       if (!reg_packet)
                return ERROR_FAIL;
 
        reg_packet_p = reg_packet;
 
        for (i = 0; i < reg_list_size; i++) {
-               if (reg_list[i] == NULL || reg_list[i]->exist == false)
+               if (!reg_list[i] || reg_list[i]->exist == false || reg_list[i]->hidden)
                        continue;
-               if (!reg_list[i]->valid) {
-                       retval = reg_list[i]->type->get(reg_list[i]);
-                       if (retval != ERROR_OK && gdb_report_register_access_error) {
-                               LOG_DEBUG("Couldn't get register %s.", reg_list[i]->name);
-                               free(reg_packet);
-                               free(reg_list);
-                               return gdb_error(connection, retval);
-                       }
+               if (gdb_get_reg_value_as_str(target, reg_packet_p, reg_list[i]) != ERROR_OK) {
+                       free(reg_packet);
+                       free(reg_list);
+                       return gdb_error(connection, retval);
                }
-               gdb_str_to_target(target, reg_packet_p, reg_list[i]);
                reg_packet_p += DIV_ROUND_UP(reg_list[i]->size, 8) * 2;
        }
 
@@ -1266,6 +1333,8 @@ static int gdb_set_registers_packet(struct connection *connection,
        packet_p = packet;
        for (i = 0; i < reg_list_size; i++) {
                uint8_t *bin_buf;
+               if (!reg_list[i] || !reg_list[i]->exist || reg_list[i]->hidden)
+                       continue;
                int chars = (DIV_ROUND_UP(reg_list[i]->size, 8) * 2);
 
                if (packet_p + chars > packet + packet_size)
@@ -1310,7 +1379,7 @@ static int gdb_get_register_packet(struct connection *connection,
        LOG_DEBUG("-");
 #endif
 
-       if ((target->rtos != NULL) && (ERROR_OK == rtos_get_gdb_reg(connection, reg_num)))
+       if ((target->rtos) && (rtos_get_gdb_reg(connection, reg_num) == ERROR_OK))
                return ERROR_OK;
 
        retval = target_get_gdb_reg_list_noread(target, &reg_list, &reg_list_size,
@@ -1318,23 +1387,19 @@ static int gdb_get_register_packet(struct connection *connection,
        if (retval != ERROR_OK)
                return gdb_error(connection, retval);
 
-       if (reg_list_size <= reg_num) {
-               LOG_ERROR("gdb requested a non-existing register");
+       if ((reg_list_size <= reg_num) || !reg_list[reg_num] ||
+               !reg_list[reg_num]->exist || reg_list[reg_num]->hidden) {
+               LOG_ERROR("gdb requested a non-existing register (reg_num=%d)", reg_num);
                return ERROR_SERVER_REMOTE_CLOSED;
        }
 
-       if (!reg_list[reg_num]->valid) {
-               retval = reg_list[reg_num]->type->get(reg_list[reg_num]);
-               if (retval != ERROR_OK && gdb_report_register_access_error) {
-                       LOG_DEBUG("Couldn't get register %s.", reg_list[reg_num]->name);
-                       free(reg_list);
-                       return gdb_error(connection, retval);
-               }
-       }
-
-       reg_packet = malloc(DIV_ROUND_UP(reg_list[reg_num]->size, 8) * 2 + 1); /* plus one for string termination null */
+       reg_packet = calloc(DIV_ROUND_UP(reg_list[reg_num]->size, 8) * 2 + 1, 1); /* plus one for string termination null */
 
-       gdb_str_to_target(target, reg_packet, reg_list[reg_num]);
+       if (gdb_get_reg_value_as_str(target, reg_packet, reg_list[reg_num]) != ERROR_OK) {
+               free(reg_packet);
+               free(reg_list);
+               return gdb_error(connection, retval);
+       }
 
        gdb_put_packet(connection, reg_packet, DIV_ROUND_UP(reg_list[reg_num]->size, 8) * 2);
 
@@ -1366,8 +1431,8 @@ static int gdb_set_register_packet(struct connection *connection,
        uint8_t *bin_buf = malloc(chars / 2);
        gdb_target_to_reg(target, separator + 1, chars, bin_buf);
 
-       if ((target->rtos != NULL) &&
-                       (ERROR_OK == rtos_set_reg(connection, reg_num, bin_buf))) {
+       if ((target->rtos) &&
+                       (rtos_set_reg(connection, reg_num, bin_buf) == ERROR_OK)) {
                free(bin_buf);
                gdb_put_packet(connection, "OK", 2);
                return ERROR_OK;
@@ -1380,8 +1445,9 @@ static int gdb_set_register_packet(struct connection *connection,
                return gdb_error(connection, retval);
        }
 
-       if (reg_list_size <= reg_num) {
-               LOG_ERROR("gdb requested a non-existing register");
+       if ((reg_list_size <= reg_num) || !reg_list[reg_num] ||
+               !reg_list[reg_num]->exist || reg_list[reg_num]->hidden) {
+               LOG_ERROR("gdb requested a non-existing register (reg_num=%d)", reg_num);
                free(bin_buf);
                free(reg_list);
                return ERROR_SERVER_REMOTE_CLOSED;
@@ -1424,9 +1490,6 @@ static int gdb_error(struct connection *connection, int retval)
        return ERROR_OK;
 }
 
-/* We don't have to worry about the default 2 second timeout for GDB packets,
- * because GDB breaks up large memory reads into smaller reads.
- */
 static int gdb_read_memory_packet(struct connection *connection,
                char const *packet, int packet_size)
 {
@@ -1462,7 +1525,11 @@ static int gdb_read_memory_packet(struct connection *connection,
 
        LOG_DEBUG("addr: 0x%16.16" PRIx64 ", len: 0x%8.8" PRIx32 "", addr, len);
 
-       retval = target_read_buffer(target, addr, len, buffer);
+       retval = ERROR_NOT_IMPLEMENTED;
+       if (target->rtos)
+               retval = rtos_read_buffer(target, addr, len, buffer);
+       if (retval == ERROR_NOT_IMPLEMENTED)
+               retval = target_read_buffer(target, addr, len, buffer);
 
        if ((retval != ERROR_OK) && !gdb_report_data_abort) {
                /* TODO : Here we have to lie and send back all zero's lest stack traces won't work.
@@ -1533,7 +1600,11 @@ static int gdb_write_memory_packet(struct connection *connection,
        if (unhexify(buffer, separator, len) != len)
                LOG_ERROR("unable to decode memory packet");
 
-       retval = target_write_buffer(target, addr, len, buffer);
+       retval = ERROR_NOT_IMPLEMENTED;
+       if (target->rtos)
+               retval = rtos_write_buffer(target, addr, len, buffer);
+       if (retval == ERROR_NOT_IMPLEMENTED)
+               retval = target_write_buffer(target, addr, len, buffer);
 
        if (retval == ERROR_OK)
                gdb_put_packet(connection, "OK", 2);
@@ -1602,7 +1673,12 @@ static int gdb_write_memory_binary_packet(struct connection *connection,
        if (len) {
                LOG_DEBUG("addr: 0x%" PRIx64 ", len: 0x%8.8" PRIx32 "", addr, len);
 
-               retval = target_write_buffer(target, addr, len, (uint8_t *)separator);
+               retval = ERROR_NOT_IMPLEMENTED;
+               if (target->rtos)
+                       retval = rtos_write_buffer(target, addr, len, (uint8_t *)separator);
+               if (retval == ERROR_NOT_IMPLEMENTED)
+                       retval = target_write_buffer(target, addr, len, (uint8_t *)separator);
+
                if (retval != ERROR_OK)
                        gdb_connection->mem_write_error = true;
        }
@@ -1700,7 +1776,10 @@ static int gdb_breakpoint_watchpoint_packet(struct connection *connection,
                case 1:
                        if (packet[0] == 'Z') {
                                retval = breakpoint_add(target, address, size, bp_type);
-                               if (retval != ERROR_OK) {
+                               if (retval == ERROR_NOT_IMPLEMENTED) {
+                                       /* Send empty reply to report that breakpoints of this type are not supported */
+                                       gdb_put_packet(connection, "", 0);
+                               } else if (retval != ERROR_OK) {
                                        retval = gdb_error(connection, retval);
                                        if (retval != ERROR_OK)
                                                return retval;
@@ -1716,8 +1795,11 @@ static int gdb_breakpoint_watchpoint_packet(struct connection *connection,
                case 4:
                {
                        if (packet[0] == 'Z') {
-                               retval = watchpoint_add(target, address, size, wp_type, 0, 0xffffffffu);
-                               if (retval != ERROR_OK) {
+                               retval = watchpoint_add(target, address, size, wp_type, 0, WATCHPOINT_IGNORE_DATA_VALUE_MASK);
+                               if (retval == ERROR_NOT_IMPLEMENTED) {
+                                       /* Send empty reply to report that watchpoints of this type are not supported */
+                                       gdb_put_packet(connection, "", 0);
+                               } else if (retval != ERROR_OK) {
                                        retval = gdb_error(connection, retval);
                                        if (retval != ERROR_OK)
                                                return retval;
@@ -1747,14 +1829,14 @@ static __attribute__ ((format (PRINTF_ATTRIBUTE_FORMAT, 5, 6))) void xml_printf(
        int first = 1;
 
        for (;; ) {
-               if ((*xml == NULL) || (!first)) {
+               if ((!*xml) || (!first)) {
                        /* start by 0 to exercise all the code paths.
                         * Need minimum 2 bytes to fit 1 char and 0 terminator. */
 
                        *size = *size * 2 + 2;
                        char *t = *xml;
                        *xml = realloc(*xml, *size);
-                       if (*xml == NULL) {
+                       if (!*xml) {
                                free(t);
                                *retval = ERROR_SERVER_REMOTE_CLOSED;
                                return;
@@ -1779,7 +1861,7 @@ static int decode_xfer_read(char const *buf, char **annex, int *ofs, unsigned in
 {
        /* Locate the annex. */
        const char *annex_end = strchr(buf, ':');
-       if (annex_end == NULL)
+       if (!annex_end)
                return ERROR_FAIL;
 
        /* After the read marker and annex, qXfer looks like a
@@ -1793,9 +1875,9 @@ static int decode_xfer_read(char const *buf, char **annex, int *ofs, unsigned in
        *len = strtoul(separator + 1, NULL, 16);
 
        /* Extract the annex if needed */
-       if (annex != NULL) {
+       if (annex) {
                *annex = strndup(buf, annex_end - buf);
-               if (*annex == NULL)
+               if (!*annex)
                        return ERROR_FAIL;
        }
 
@@ -2014,7 +2096,7 @@ static int lookup_add_arch_defined_types(char const **arch_defined_types_list[],
 {
        int tbl_sz = *num_arch_defined_types;
 
-       if (type_id != NULL && (strcmp(type_id, ""))) {
+       if (type_id && (strcmp(type_id, ""))) {
                for (int j = 0; j < (tbl_sz + 1); j++) {
                        if (!((*arch_defined_types_list)[j])) {
                                (*arch_defined_types_list)[tbl_sz++] = type_id;
@@ -2057,7 +2139,7 @@ static int gdb_generate_reg_type_description(struct target *target,
        } else if (type->type_class == REG_TYPE_CLASS_UNION) {
                struct reg_data_type_union_field *field;
                field = type->reg_type_union->fields;
-               while (field != NULL) {
+               while (field) {
                        struct reg_data_type *data_type = field->type;
                        if (data_type->type == REG_TYPE_ARCH_DEFINED) {
                                if (lookup_add_arch_defined_types(arch_defined_types_list, data_type->id,
@@ -2077,7 +2159,7 @@ static int gdb_generate_reg_type_description(struct target *target,
                                type->id);
 
                field = type->reg_type_union->fields;
-               while (field != NULL) {
+               while (field) {
                        xml_printf(&retval, tdesc, pos, size,
                                        "<field name=\"%s\" type=\"%s\"/>\n",
                                        field->name, field->type->id);
@@ -2099,7 +2181,7 @@ static int gdb_generate_reg_type_description(struct target *target,
                        xml_printf(&retval, tdesc, pos, size,
                                        "<struct id=\"%s\" size=\"%" PRIu32 "\">\n",
                                        type->id, type->reg_type_struct->size);
-                       while (field != NULL) {
+                       while (field) {
                                xml_printf(&retval, tdesc, pos, size,
                                                "<field name=\"%s\" start=\"%" PRIu32 "\" end=\"%" PRIu32 "\" type=\"%s\" />\n",
                                                field->name, field->bitfield->start, field->bitfield->end,
@@ -2108,7 +2190,7 @@ static int gdb_generate_reg_type_description(struct target *target,
                                field = field->next;
                        }
                } else {
-                       while (field != NULL) {
+                       while (field) {
                                struct reg_data_type *data_type = field->type;
                                if (data_type->type == REG_TYPE_ARCH_DEFINED) {
                                        if (lookup_add_arch_defined_types(arch_defined_types_list, data_type->id,
@@ -2125,7 +2207,7 @@ static int gdb_generate_reg_type_description(struct target *target,
                        xml_printf(&retval, tdesc, pos, size,
                                        "<struct id=\"%s\">\n",
                                        type->id);
-                       while (field != NULL) {
+                       while (field) {
                                xml_printf(&retval, tdesc, pos, size,
                                                "<field name=\"%s\" type=\"%s\"/>\n",
                                                field->name, field->type->id);
@@ -2147,7 +2229,7 @@ static int gdb_generate_reg_type_description(struct target *target,
 
                struct reg_data_type_flags_field *field;
                field = type->reg_type_flags->fields;
-               while (field != NULL) {
+               while (field) {
                        xml_printf(&retval, tdesc, pos, size,
                                        "<field name=\"%s\" start=\"%" PRIu32 "\" end=\"%" PRIu32 "\" type=\"%s\" />\n",
                                        field->name, field->bitfield->start, field->bitfield->end,
@@ -2176,11 +2258,11 @@ static int get_reg_features_list(struct target *target, char const **feature_lis
        *feature_list = calloc(1, sizeof(char *));
 
        for (int i = 0; i < reg_list_size; i++) {
-               if (reg_list[i]->exist == false)
+               if (reg_list[i]->exist == false || reg_list[i]->hidden)
                        continue;
 
-               if (reg_list[i]->feature != NULL
-                       && reg_list[i]->feature->name != NULL
+               if (reg_list[i]->feature
+                       && reg_list[i]->feature->name
                        && (strcmp(reg_list[i]->feature->name, ""))) {
                        /* We found a feature, check if the feature is already in the
                         * table. If not, allocate a new entry for the table and
@@ -2206,6 +2288,123 @@ static int get_reg_features_list(struct target *target, char const **feature_lis
        return ERROR_OK;
 }
 
+/* Create a register list that's the union of all the registers of the SMP
+ * group this target is in. If the target is not part of an SMP group, this
+ * returns the same as target_get_gdb_reg_list_noread().
+ */
+static int smp_reg_list_noread(struct target *target,
+               struct reg **combined_list[], int *combined_list_size,
+               enum target_register_class reg_class)
+{
+       if (!target->smp)
+               return target_get_gdb_reg_list_noread(target, combined_list,
+                               combined_list_size, REG_CLASS_ALL);
+
+       unsigned int combined_allocated = 256;
+       struct reg **local_list = malloc(combined_allocated * sizeof(struct reg *));
+       if (!local_list) {
+               LOG_ERROR("malloc(%zu) failed", combined_allocated * sizeof(struct reg *));
+               return ERROR_FAIL;
+       }
+       unsigned int local_list_size = 0;
+
+       struct target_list *head;
+       foreach_smp_target(head, target->smp_targets) {
+               if (!target_was_examined(head->target))
+                       continue;
+
+               struct reg **reg_list = NULL;
+               int reg_list_size;
+               int result = target_get_gdb_reg_list_noread(head->target, &reg_list,
+                               &reg_list_size, reg_class);
+               if (result != ERROR_OK) {
+                       free(local_list);
+                       return result;
+               }
+               for (int i = 0; i < reg_list_size; i++) {
+                       bool found = false;
+                       struct reg *a = reg_list[i];
+                       if (a->exist) {
+                               /* Nested loop makes this O(n^2), but this entire function with
+                                * 5 RISC-V targets takes just 2ms on my computer. Fast enough
+                                * for me. */
+                               for (unsigned int j = 0; j < local_list_size; j++) {
+                                       struct reg *b = local_list[j];
+                                       if (!strcmp(a->name, b->name)) {
+                                               found = true;
+                                               if (a->size != b->size) {
+                                                       LOG_ERROR("SMP register %s is %d bits on one "
+                                                                       "target, but %d bits on another target.",
+                                                                       a->name, a->size, b->size);
+                                                       free(reg_list);
+                                                       free(local_list);
+                                                       return ERROR_FAIL;
+                                               }
+                                               break;
+                                       }
+                               }
+                               if (!found) {
+                                       LOG_DEBUG("[%s] %s not found in combined list", target_name(target), a->name);
+                                       if (local_list_size >= combined_allocated) {
+                                               combined_allocated *= 2;
+                                               local_list = realloc(local_list, combined_allocated * sizeof(struct reg *));
+                                               if (!local_list) {
+                                                       LOG_ERROR("realloc(%zu) failed", combined_allocated * sizeof(struct reg *));
+                                                       free(reg_list);
+                                                       return ERROR_FAIL;
+                                               }
+                                       }
+                                       local_list[local_list_size] = a;
+                                       local_list_size++;
+                               }
+                       }
+               }
+               free(reg_list);
+       }
+
+       if (local_list_size == 0) {
+               LOG_ERROR("Unable to get register list");
+               free(local_list);
+               return ERROR_FAIL;
+       }
+
+       /* Now warn the user about any registers that weren't found in every target. */
+       foreach_smp_target(head, target->smp_targets) {
+               if (!target_was_examined(head->target))
+                       continue;
+
+               struct reg **reg_list = NULL;
+               int reg_list_size;
+               int result = target_get_gdb_reg_list_noread(head->target, &reg_list,
+                               &reg_list_size, reg_class);
+               if (result != ERROR_OK) {
+                       free(local_list);
+                       return result;
+               }
+               for (unsigned int i = 0; i < local_list_size; i++) {
+                       bool found = false;
+                       struct reg *a = local_list[i];
+                       for (int j = 0; j < reg_list_size; j++) {
+                               struct reg *b = reg_list[j];
+                               if (b->exist && !strcmp(a->name, b->name)) {
+                                       found = true;
+                                       break;
+                               }
+                       }
+                       if (!found) {
+                               LOG_WARNING("Register %s does not exist in %s, which is part of an SMP group where "
+                                           "this register does exist.",
+                                           a->name, target_name(head->target));
+                       }
+               }
+               free(reg_list);
+       }
+
+       *combined_list = local_list;
+       *combined_list_size = local_list_size;
+       return ERROR_OK;
+}
+
 static int gdb_generate_target_description(struct target *target, char **tdesc_out)
 {
        int retval = ERROR_OK;
@@ -2219,8 +2418,8 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o
        int size = 0;
 
 
-       retval = target_get_gdb_reg_list_noread(target, &reg_list,
-                       &reg_list_size, REG_CLASS_ALL);
+       retval = smp_reg_list_noread(target, &reg_list, &reg_list_size,
+                       REG_CLASS_ALL);
 
        if (retval != ERROR_OK) {
                LOG_ERROR("get register list failed");
@@ -2252,12 +2451,12 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o
 
        /* generate architecture element if supported by target */
        architecture = target_get_gdb_arch(target);
-       if (architecture != NULL)
+       if (architecture)
                xml_printf(&retval, &tdesc, &pos, &size,
                                "<architecture>%s</architecture>\n", architecture);
 
        /* generate target description according to register list */
-       if (features != NULL) {
+       if (features) {
                while (features[current_feature]) {
                        char const **arch_defined_types = NULL;
                        int num_arch_defined_types = 0;
@@ -2270,14 +2469,14 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o
                        int i;
                        for (i = 0; i < reg_list_size; i++) {
 
-                               if (reg_list[i]->exist == false)
+                               if (reg_list[i]->exist == false || reg_list[i]->hidden)
                                        continue;
 
                                if (strcmp(reg_list[i]->feature->name, features[current_feature]))
                                        continue;
 
                                const char *type_str;
-                               if (reg_list[i]->reg_data_type != NULL) {
+                               if (reg_list[i]->reg_data_type) {
                                        if (reg_list[i]->reg_data_type->type == REG_TYPE_ARCH_DEFINED) {
                                                /* generate <type... first, if there are architecture-defined types. */
                                                if (lookup_add_arch_defined_types(&arch_defined_types,
@@ -2315,7 +2514,7 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o
                                xml_printf(&retval, &tdesc, &pos, &size,
                                                " type=\"%s\"", type_str);
 
-                               if (reg_list[i]->group != NULL)
+                               if (reg_list[i]->group)
                                        xml_printf(&retval, &tdesc, &pos, &size,
                                                        " group=\"%s\"", reg_list[i]->group);
 
@@ -2349,7 +2548,7 @@ error:
 static int gdb_get_target_description_chunk(struct target *target, struct target_desc_format *target_desc,
                char **chunk, int32_t offset, uint32_t length)
 {
-       if (target_desc == NULL) {
+       if (!target_desc) {
                LOG_ERROR("Unable to Generate Target Description");
                return ERROR_FAIL;
        }
@@ -2357,7 +2556,7 @@ static int gdb_get_target_description_chunk(struct target *target, struct target
        char *tdesc = target_desc->tdesc;
        uint32_t tdesc_length = target_desc->tdesc_length;
 
-       if (tdesc == NULL) {
+       if (!tdesc) {
                int retval = gdb_generate_target_description(target, &tdesc);
                if (retval != ERROR_OK) {
                        LOG_ERROR("Unable to Generate Target Description");
@@ -2375,7 +2574,7 @@ static int gdb_get_target_description_chunk(struct target *target, struct target
                transfer_type = 'l';
 
        *chunk = malloc(length + 2);
-       if (*chunk == NULL) {
+       if (!*chunk) {
                LOG_ERROR("Unable to allocate memory");
                return ERROR_FAIL;
        }
@@ -2457,22 +2656,28 @@ static int gdb_generate_thread_list(struct target *target, char **thread_list_ou
                   "<?xml version=\"1.0\"?>\n"
                   "<threads>\n");
 
-       if (rtos != NULL) {
+       if (rtos) {
                for (int i = 0; i < rtos->thread_count; i++) {
                        struct thread_detail *thread_detail = &rtos->thread_details[i];
 
                        if (!thread_detail->exists)
                                continue;
 
-                       xml_printf(&retval, &thread_list, &pos, &size,
-                                  "<thread id=\"%" PRIx64 "\">", thread_detail->threadid);
+                       if (thread_detail->thread_name_str)
+                               xml_printf(&retval, &thread_list, &pos, &size,
+                                          "<thread id=\"%" PRIx64 "\" name=\"%s\">",
+                                          thread_detail->threadid,
+                                          thread_detail->thread_name_str);
+                       else
+                               xml_printf(&retval, &thread_list, &pos, &size,
+                                          "<thread id=\"%" PRIx64 "\">", thread_detail->threadid);
 
-                       if (thread_detail->thread_name_str != NULL)
+                       if (thread_detail->thread_name_str)
                                xml_printf(&retval, &thread_list, &pos, &size,
                                           "Name: %s", thread_detail->thread_name_str);
 
-                       if (thread_detail->extra_info_str != NULL) {
-                               if (thread_detail->thread_name_str != NULL)
+                       if (thread_detail->extra_info_str) {
+                               if (thread_detail->thread_name_str)
                                        xml_printf(&retval, &thread_list, &pos, &size,
                                                   ", ");
                                xml_printf(&retval, &thread_list, &pos, &size,
@@ -2498,7 +2703,7 @@ static int gdb_generate_thread_list(struct target *target, char **thread_list_ou
 static int gdb_get_thread_list_chunk(struct target *target, char **thread_list,
                char **chunk, int32_t offset, uint32_t length)
 {
-       if (*thread_list == NULL) {
+       if (!*thread_list) {
                int retval = gdb_generate_thread_list(target, thread_list);
                if (retval != ERROR_OK) {
                        LOG_ERROR("Unable to Generate Thread List");
@@ -2516,11 +2721,11 @@ static int gdb_get_thread_list_chunk(struct target *target, char **thread_list,
                transfer_type = 'l';
 
        *chunk = malloc(length + 2 + 3);
-    /* Allocating extra 3 bytes prevents false positive valgrind report
+       /* Allocating extra 3 bytes prevents false positive valgrind report
         * of strlen(chunk) word access:
         * Invalid read of size 4
         * Address 0x4479934 is 44 bytes inside a block of size 45 alloc'd */
-       if (*chunk == NULL) {
+       if (!*chunk) {
                LOG_ERROR("Unable to allocate memory");
                return ERROR_FAIL;
        }
@@ -2547,22 +2752,75 @@ static int gdb_query_packet(struct connection *connection,
 
        if (strncmp(packet, "qRcmd,", 6) == 0) {
                if (packet_size > 6) {
+                       Jim_Interp *interp = cmd_ctx->interp;
                        char *cmd;
                        cmd = malloc((packet_size - 6) / 2 + 1);
                        size_t len = unhexify((uint8_t *)cmd, packet + 6, (packet_size - 6) / 2);
                        cmd[len] = 0;
 
                        /* We want to print all debug output to GDB connection */
-                       log_add_callback(gdb_log_callback, connection);
+                       gdb_connection->output_flag = GDB_OUTPUT_ALL;
                        target_call_timer_callbacks_now();
                        /* some commands need to know the GDB connection, make note of current
                         * GDB connection. */
                        current_gdb_connection = gdb_connection;
-                       command_run_line(cmd_ctx, cmd);
+
+                       struct target *saved_target_override = cmd_ctx->current_target_override;
+                       cmd_ctx->current_target_override = NULL;
+
+                       struct command_context *old_context = Jim_GetAssocData(interp, "context");
+                       Jim_DeleteAssocData(interp, "context");
+                       int retval = Jim_SetAssocData(interp, "context", NULL, cmd_ctx);
+                       if (retval == JIM_OK) {
+                               retval = Jim_EvalObj(interp, Jim_NewStringObj(interp, cmd, -1));
+                               Jim_DeleteAssocData(interp, "context");
+                       }
+                       int inner_retval = Jim_SetAssocData(interp, "context", NULL, old_context);
+                       if (retval == JIM_OK)
+                               retval = inner_retval;
+
+                       cmd_ctx->current_target_override = saved_target_override;
+
                        current_gdb_connection = NULL;
                        target_call_timer_callbacks_now();
-                       log_remove_callback(gdb_log_callback, connection);
+                       gdb_connection->output_flag = GDB_OUTPUT_NO;
                        free(cmd);
+                       if (retval == JIM_RETURN)
+                               retval = interp->returnCode;
+                       int lenmsg;
+                       const char *cretmsg = Jim_GetString(Jim_GetResult(interp), &lenmsg);
+                       char *retmsg;
+                       if (lenmsg && cretmsg[lenmsg - 1] != '\n') {
+                               retmsg = alloc_printf("%s\n", cretmsg);
+                               lenmsg++;
+                       } else {
+                               retmsg = strdup(cretmsg);
+                       }
+                       if (!retmsg)
+                               return ERROR_GDB_BUFFER_TOO_SMALL;
+
+                       if (retval == JIM_OK) {
+                               if (lenmsg) {
+                                       char *hex_buffer = malloc(lenmsg * 2 + 1);
+                                       if (!hex_buffer) {
+                                               free(retmsg);
+                                               return ERROR_GDB_BUFFER_TOO_SMALL;
+                                       }
+
+                                       size_t pkt_len = hexify(hex_buffer, (const uint8_t *)retmsg, lenmsg,
+                                                                                       lenmsg * 2 + 1);
+                                       gdb_put_packet(connection, hex_buffer, pkt_len);
+                                       free(hex_buffer);
+                               } else {
+                                       gdb_put_packet(connection, "OK", 2);
+                               }
+                       } else {
+                               if (lenmsg)
+                                       gdb_output_con(connection, retmsg);
+                               gdb_send_error(connection, retval);
+                       }
+                       free(retmsg);
+                       return ERROR_OK;
                }
                gdb_put_packet(connection, "OK", 2);
                return ERROR_OK;
@@ -2710,13 +2968,19 @@ static int gdb_query_packet(struct connection *connection,
                gdb_connection->noack_mode = 1;
                gdb_put_packet(connection, "OK", 2);
                return ERROR_OK;
+       } else if (target->type->gdb_query_custom) {
+               char *buffer = NULL;
+               int ret = target->type->gdb_query_custom(target, packet, &buffer);
+               gdb_put_packet(connection, buffer, strlen(buffer));
+               return ret;
        }
 
        gdb_put_packet(connection, "", 0);
        return ERROR_OK;
 }
 
-static bool gdb_handle_vcont_packet(struct connection *connection, const char *packet, int packet_size)
+static bool gdb_handle_vcont_packet(struct connection *connection, const char *packet,
+       __attribute__((unused)) int packet_size)
 {
        struct gdb_connection *gdb_connection = connection->priv;
        struct target *target = get_target_from_connection(connection);
@@ -2725,7 +2989,7 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p
 
        /* query for vCont supported */
        if (parse[0] == '?') {
-               if (target->type->step != NULL) {
+               if (target->type->step) {
                        /* gdb doesn't accept c without C and s without S */
                        gdb_put_packet(connection, "vCont;c;C;s;S", 13);
                        return true;
@@ -2735,14 +2999,13 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p
 
        if (parse[0] == ';') {
                ++parse;
-               --packet_size;
        }
 
        /* simple case, a continue packet */
        if (parse[0] == 'c') {
                gdb_running_type = 'c';
                LOG_DEBUG("target %s continue", target_name(target));
-               log_add_callback(gdb_log_callback, connection);
+               gdb_connection->output_flag = GDB_OUTPUT_ALL;
                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));
@@ -2770,128 +3033,123 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p
                gdb_running_type = 's';
                bool fake_step = false;
 
-               if (strncmp(parse, "s:", 2) == 0) {
-                       struct target *ct = target;
-                       int current_pc = 1;
-                       int64_t thread_id;
+               struct target *ct = target;
+               int current_pc = 1;
+               int64_t thread_id;
+               parse++;
+               if (parse[0] == ':') {
                        char *endp;
-
-                       parse += 2;
-                       packet_size -= 2;
-
+                       parse++;
                        thread_id = strtoll(parse, &endp, 16);
-                       if (endp != NULL) {
-                               packet_size -= endp - parse;
+                       if (endp) {
                                parse = endp;
                        }
+               } else {
+                       thread_id = 0;
+               }
 
-                       if (target->rtos != NULL) {
-                               /* FIXME: why is this necessary? rtos state should be up-to-date here already! */
-                               rtos_update_threads(target);
+               if (target->rtos) {
+                       /* FIXME: why is this necessary? rtos state should be up-to-date here already! */
+                       rtos_update_threads(target);
 
-                               target->rtos->gdb_target_for_threadid(connection, thread_id, &ct);
+                       target->rtos->gdb_target_for_threadid(connection, thread_id, &ct);
 
-                               /*
-                                * check if the thread to be stepped is the current rtos thread
-                                * if not, we must fake the step
-                                */
-                               if (target->rtos->current_thread != thread_id)
-                                       fake_step = true;
-                       }
+                       /*
+                        * check if the thread to be stepped is the current rtos thread
+                        * if not, we must fake the step
+                        */
+                       if (target->rtos->current_thread != thread_id)
+                               fake_step = true;
+               }
 
-                       if (parse[0] == ';') {
-                               ++parse;
-                               --packet_size;
+               if (parse[0] == ';') {
+                       ++parse;
 
-                               if (parse[0] == 'c') {
+                       if (parse[0] == 'c') {
+                               parse += 1;
+
+                               /* check if thread-id follows */
+                               if (parse[0] == ':') {
+                                       int64_t tid;
                                        parse += 1;
 
-                                       /* check if thread-id follows */
-                                       if (parse[0] == ':') {
-                                               int64_t tid;
-                                               parse += 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;*/
-                                               }
+                                       tid = strtoll(parse, NULL, 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 %"PRIx64, target_name(ct), thread_id);
-                       log_add_callback(gdb_log_callback, connection);
-                       target_call_event_callbacks(ct, TARGET_EVENT_GDB_START);
-
-                       /*
-                        * work around an annoying gdb behaviour: when the current thread
-                        * is changed in gdb, it assumes that the target can follow and also
-                        * make the thread current. This is an assumption that cannot hold
-                        * for a real target running a multi-threading OS. We just fake
-                        * the step to not trigger an internal error in gdb. See
-                        * https://sourceware.org/bugzilla/show_bug.cgi?id=22925 for details
-                        */
-                       if (fake_step) {
-                               int sig_reply_len;
-                               char sig_reply[128];
-
-                               LOG_DEBUG("fake step thread %"PRIx64, thread_id);
+               LOG_DEBUG("target %s single-step thread %"PRIx64, target_name(ct), thread_id);
+               gdb_connection->output_flag = GDB_OUTPUT_ALL;
+               target_call_event_callbacks(ct, TARGET_EVENT_GDB_START);
 
-                               sig_reply_len = snprintf(sig_reply, sizeof(sig_reply),
-                                                                                "T05thread:%016"PRIx64";", thread_id);
+               /*
+                * work around an annoying gdb behaviour: when the current thread
+                * is changed in gdb, it assumes that the target can follow and also
+                * make the thread current. This is an assumption that cannot hold
+                * for a real target running a multi-threading OS. We just fake
+                * the step to not trigger an internal error in gdb. See
+                * https://sourceware.org/bugzilla/show_bug.cgi?id=22925 for details
+                */
+               if (fake_step) {
+                       int sig_reply_len;
+                       char sig_reply[128];
 
-                               gdb_put_packet(connection, sig_reply, sig_reply_len);
-                               log_remove_callback(gdb_log_callback, connection);
+                       LOG_DEBUG("fake step thread %"PRIx64, thread_id);
 
-                               return true;
-                       }
+                       sig_reply_len = snprintf(sig_reply, sizeof(sig_reply),
+                                                                       "T05thread:%016"PRIx64";", thread_id);
 
-                       /* support for gdb_sync command */
-                       if (gdb_connection->sync) {
-                               gdb_connection->sync = false;
-                               if (ct->state == TARGET_HALTED) {
-                                       LOG_DEBUG("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;
-                       }
+                       gdb_put_packet(connection, sig_reply, sig_reply_len);
+                       gdb_connection->output_flag = GDB_OUTPUT_NO;
 
-                       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));
+                       return true;
+               }
 
-                       /* 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);
+               /* support for gdb_sync command */
+               if (gdb_connection->sync) {
+                       gdb_connection->sync = false;
+                       if (ct->state == TARGET_HALTED) {
+                               LOG_DEBUG("stepi ignored. GDB will now fetch the register state "
+                                                               "from the target.");
+                               gdb_sig_halted(connection);
+                               gdb_connection->output_flag = GDB_OUTPUT_NO;
                        } else
                                gdb_connection->frontend_state = TARGET_RUNNING;
-               } else {
-                       LOG_ERROR("Unknown vCont packet");
-                       return false;
+                       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! */
+                       gdb_connection->output_flag = GDB_OUTPUT_NO;
+               } else
+                       gdb_connection->frontend_state = TARGET_RUNNING;
                return true;
        }
-
+       LOG_ERROR("Unknown vCont packet");
        return false;
 }
 
@@ -2903,7 +3161,7 @@ static char *next_hex_encoded_field(const char **str, char sep)
                return NULL;
 
        const char *end = strchr(hex, sep);
-       if (end == NULL)
+       if (!end)
                hexlen = strlen(hex);
        else
                hexlen = end - hex;
@@ -2916,7 +3174,7 @@ static char *next_hex_encoded_field(const char **str, char sep)
 
        size_t count = hexlen / 2;
        char *decoded = malloc(count + 1);
-       if (decoded == NULL)
+       if (!decoded)
                return NULL;
 
        size_t converted = unhexify((void *)decoded, hex, count);
@@ -2961,16 +3219,18 @@ static bool gdb_handle_vrun_packet(struct connection *connection, const char *pa
        free(next_hex_encoded_field(&parse, ';'));
 
        char *cmdline = next_hex_encoded_field(&parse, ';');
-       char *arg;
-       while (cmdline != NULL && (arg = next_hex_encoded_field(&parse, ';')) != NULL) {
+       while (cmdline) {
+               char *arg = next_hex_encoded_field(&parse, ';');
+               if (!arg)
+                       break;
                char *new_cmdline = alloc_printf("%s %s", cmdline, arg);
                free(cmdline);
                free(arg);
                cmdline = new_cmdline;
        }
 
-       if (cmdline != NULL) {
-               if (target->semihosting != NULL) {
+       if (cmdline) {
+               if (target->semihosting) {
                        LOG_INFO("GDB set inferior command line to '%s'", cmdline);
                        free(target->semihosting->cmdline);
                        target->semihosting->cmdline = cmdline;
@@ -3024,7 +3284,7 @@ static int gdb_v_packet(struct connection *connection,
        }
 
        if (strncmp(packet, "vFlashErase:", 12) == 0) {
-               unsigned long addr;
+               target_addr_t addr;
                unsigned long length;
 
                char const *parse = packet + 12;
@@ -3033,7 +3293,7 @@ static int gdb_v_packet(struct connection *connection,
                        return ERROR_SERVER_REMOTE_CLOSED;
                }
 
-               addr = strtoul(parse, (char **)&parse, 16);
+               addr = strtoull(parse, (char **)&parse, 16);
 
                if (*(parse++) != ',' || *parse == '\0') {
                        LOG_ERROR("incomplete vFlashErase packet received, dropping connection");
@@ -3081,7 +3341,7 @@ static int gdb_v_packet(struct connection *connection,
 
        if (strncmp(packet, "vFlashWrite:", 12) == 0) {
                int retval;
-               unsigned long addr;
+               target_addr_t addr;
                unsigned long length;
                char const *parse = packet + 12;
 
@@ -3089,7 +3349,8 @@ static int gdb_v_packet(struct connection *connection,
                        LOG_ERROR("incomplete vFlashErase packet received, dropping connection");
                        return ERROR_SERVER_REMOTE_CLOSED;
                }
-               addr = strtoul(parse, (char **)&parse, 16);
+
+               addr = strtoull(parse, (char **)&parse, 16);
                if (*(parse++) != ':') {
                        LOG_ERROR("incomplete vFlashErase packet received, dropping connection");
                        return ERROR_SERVER_REMOTE_CLOSED;
@@ -3097,7 +3358,7 @@ static int gdb_v_packet(struct connection *connection,
                length = packet_size - (parse - packet);
 
                /* create a new image if there isn't already one */
-               if (gdb_connection->vflash_image == NULL) {
+               if (!gdb_connection->vflash_image) {
                        gdb_connection->vflash_image = malloc(sizeof(struct image));
                        image_open(gdb_connection->vflash_image, "", "build");
                }
@@ -3116,6 +3377,13 @@ static int gdb_v_packet(struct connection *connection,
        if (strncmp(packet, "vFlashDone", 10) == 0) {
                uint32_t written;
 
+               /* GDB command 'flash-erase' does not send a vFlashWrite,
+                * so nothing to write here. */
+               if (!gdb_connection->vflash_image) {
+                       gdb_put_packet(connection, "OK", 2);
+                       return ERROR_OK;
+               }
+
                /* process the flashing buffer. No need to erase as GDB
                 * always issues a vFlashErase first. */
                target_call_event_callbacks(target,
@@ -3210,6 +3478,10 @@ static void gdb_log_callback(void *priv, const char *file, unsigned line,
        struct connection *connection = priv;
        struct gdb_connection *gdb_con = connection->priv;
 
+       if (gdb_con->output_flag != GDB_OUTPUT_ALL)
+               /* No out allowed */
+               return;
+
        if (gdb_con->busy) {
                /* do not reply this using the O packet */
                return;
@@ -3259,20 +3531,10 @@ static int gdb_input_inner(struct connection *connection)
                /* terminate with zero */
                gdb_packet_buffer[packet_size] = '\0';
 
-               if (LOG_LEVEL_IS(LOG_LVL_DEBUG)) {
-                       if (packet[0] == 'X') {
-                               /* binary packets spew junk into the debug log stream */
-                               char buf[50];
-                               int x;
-                               for (x = 0; (x < 49) && (packet[x] != ':'); x++)
-                                       buf[x] = packet[x];
-                               buf[x] = 0;
-                               LOG_DEBUG("received packet: '%s:<binary-data>'", buf);
-                       } else
-                               LOG_DEBUG("received packet: '%s'", packet);
-               }
-
                if (packet_size > 0) {
+
+                       gdb_log_incoming_packet(connection, gdb_packet_buffer);
+
                        retval = ERROR_OK;
                        switch (packet[0]) {
                                case 'T':       /* Is thread alive? */
@@ -3301,10 +3563,14 @@ static int gdb_input_inner(struct connection *connection)
                                        retval = gdb_set_register_packet(connection, packet, packet_size);
                                        break;
                                case 'm':
+                                       gdb_con->output_flag = GDB_OUTPUT_NOTIF;
                                        retval = gdb_read_memory_packet(connection, packet, packet_size);
+                                       gdb_con->output_flag = GDB_OUTPUT_NO;
                                        break;
                                case 'M':
+                                       gdb_con->output_flag = GDB_OUTPUT_NOTIF;
                                        retval = gdb_write_memory_packet(connection, packet, packet_size);
+                                       gdb_con->output_flag = GDB_OUTPUT_NO;
                                        break;
                                case 'z':
                                case 'Z':
@@ -3315,7 +3581,7 @@ static int gdb_input_inner(struct connection *connection)
                                        /* '?' is sent after the eventual '!' */
                                        if (!warn_use_ext && !gdb_con->extended_protocol) {
                                                warn_use_ext = true;
-                                               LOG_WARNING("Prefer GDB command \"target extended-remote %s\" instead of \"target remote %s\"",
+                                               LOG_WARNING("Prefer GDB command \"target extended-remote :%s\" instead of \"target remote :%s\"",
                                                                        connection->service->port, connection->service->port);
                                        }
                                        break;
@@ -3323,7 +3589,7 @@ static int gdb_input_inner(struct connection *connection)
                                case 's':
                                {
                                        gdb_thread_packet(connection, packet, packet_size);
-                                       log_add_callback(gdb_log_callback, connection);
+                                       gdb_con->output_flag = GDB_OUTPUT_ALL;
 
                                        if (gdb_con->mem_write_error) {
                                                LOG_ERROR("Memory write failure!");
@@ -3366,7 +3632,7 @@ static int gdb_input_inner(struct connection *connection)
                                                gdb_sig_halted(connection);
 
                                                /* stop forwarding log packets! */
-                                               log_remove_callback(gdb_log_callback, connection);
+                                               gdb_con->output_flag = GDB_OUTPUT_NO;
                                        } else {
                                                /* We're running/stepping, in which case we can
                                                 * forward log output until the target is halted
@@ -3395,9 +3661,9 @@ static int gdb_input_inner(struct connection *connection)
                                        retval = gdb_detach(connection);
                                        break;
                                case 'X':
+                                       gdb_con->output_flag = GDB_OUTPUT_NOTIF;
                                        retval = gdb_write_memory_binary_packet(connection, packet, packet_size);
-                                       if (retval != ERROR_OK)
-                                               return retval;
+                                       gdb_con->output_flag = GDB_OUTPUT_NO;
                                        break;
                                case 'k':
                                        if (gdb_con->extended_protocol) {
@@ -3417,12 +3683,14 @@ static int gdb_input_inner(struct connection *connection)
                                        break;
 
                                case 'j':
+                                       /* DEPRECATED */
                                        /* packet supported only by smp target i.e cortex_a.c*/
                                        /* handle smp packet replying coreid played to gbd */
                                        gdb_read_smp_packet(connection, packet, packet_size);
                                        break;
 
                                case 'J':
+                                       /* DEPRECATED */
                                        /* packet supported only by smp target i.e cortex_a.c */
                                        /* handle smp packet setting coreid to be played at next
                                         * resume to gdb */
@@ -3438,7 +3706,7 @@ static int gdb_input_inner(struct connection *connection)
                                         * Fretcode,errno,Ctrl-C flag;call-specific attachment
                                         */
                                        gdb_con->frontend_state = TARGET_RUNNING;
-                                       log_add_callback(gdb_log_callback, connection);
+                                       gdb_con->output_flag = GDB_OUTPUT_ALL;
                                        gdb_fileio_response_packet(connection, packet, packet_size);
                                        break;
 
@@ -3491,13 +3759,76 @@ static int gdb_input(struct connection *connection)
        return ERROR_OK;
 }
 
+/*
+ * Send custom notification packet as keep-alive during memory read/write.
+ *
+ * From gdb 7.0 (released 2009-10-06) an unknown notification received during
+ * memory read/write would be silently dropped.
+ * Before gdb 7.0 any character, with exclusion of "+-$", would be considered
+ * as junk and ignored.
+ * In both cases the reception will reset the timeout counter in gdb, thus
+ * working as a keep-alive.
+ * Check putpkt_binary() and getpkt_sane() in gdb commit
+ * 74531fed1f2d662debc2c209b8b3faddceb55960
+ *
+ * Enable remote debug in gdb with 'set debug remote 1' to either dump the junk
+ * characters in gdb pre-7.0 and the notification from gdb 7.0.
+ */
+static void gdb_async_notif(struct connection *connection)
+{
+       static unsigned char count;
+       unsigned char checksum = 0;
+       char buf[22];
+
+       int len = sprintf(buf, "%%oocd_keepalive:%2.2x", count++);
+       for (int i = 1; i < len; i++)
+               checksum += buf[i];
+       len += sprintf(buf + len, "#%2.2x", checksum);
+
+#ifdef _DEBUG_GDB_IO_
+       LOG_DEBUG("sending packet '%s'", buf);
+#endif
+
+       gdb_write(connection, buf, len);
+}
+
+static void gdb_keep_client_alive(struct connection *connection)
+{
+       struct gdb_connection *gdb_con = connection->priv;
+
+       switch (gdb_con->output_flag) {
+       case GDB_OUTPUT_NO:
+               /* no need for keep-alive */
+               break;
+       case GDB_OUTPUT_NOTIF:
+               /* send asynchronous notification */
+               gdb_async_notif(connection);
+               break;
+       case GDB_OUTPUT_ALL:
+               /* send an empty O packet */
+               gdb_output_con(connection, "");
+               break;
+       default:
+               break;
+       }
+}
+
+static const struct service_driver gdb_service_driver = {
+       .name = "gdb",
+       .new_connection_during_keep_alive_handler = NULL,
+       .new_connection_handler = gdb_new_connection,
+       .input_handler = gdb_input,
+       .connection_closed_handler = gdb_connection_closed,
+       .keep_client_alive_handler = gdb_keep_client_alive,
+};
+
 static int gdb_target_start(struct target *target, const char *port)
 {
        struct gdb_service *gdb_service;
        int ret;
        gdb_service = malloc(sizeof(struct gdb_service));
 
-       if (NULL == gdb_service)
+       if (!gdb_service)
                return -ENOMEM;
 
        LOG_INFO("starting gdb server for %s on %s", target_name(target), port);
@@ -3507,19 +3838,14 @@ static int gdb_target_start(struct target *target, const char *port)
        gdb_service->core[1] = -1;
        target->gdb_service = gdb_service;
 
-       ret = add_service("gdb",
-                       port, 1, &gdb_new_connection, &gdb_input,
-                       &gdb_connection_closed, gdb_service);
+       ret = add_service(&gdb_service_driver, port, target->gdb_max_connections, gdb_service);
        /* initialize all targets gdb service with the same pointer */
        {
                struct target_list *head;
-               struct target *curr;
-               head = target->head;
-               while (head != (struct target_list *)NULL) {
-                       curr = head->target;
+               foreach_smp_target(head, target->smp_targets) {
+                       struct target *curr = head->target;
                        if (curr != target)
                                curr->gdb_service = gdb_service;
-                       head = head->next;
                }
        }
        return ret;
@@ -3581,14 +3907,14 @@ static int gdb_target_add_one(struct target *target)
 
 int gdb_target_add_all(struct target *target)
 {
-       if (NULL == target) {
+       if (!target) {
                LOG_WARNING("gdb services need one or more targets defined");
                return ERROR_OK;
        }
 
-       while (NULL != target) {
+       while (target) {
                int retval = gdb_target_add_one(target);
-               if (ERROR_OK != retval)
+               if (retval != ERROR_OK)
                        return retval;
 
                target = target->next;
@@ -3602,7 +3928,7 @@ COMMAND_HANDLER(handle_gdb_sync_command)
        if (CMD_ARGC != 0)
                return ERROR_COMMAND_SYNTAX_ERROR;
 
-       if (current_gdb_connection == NULL) {
+       if (!current_gdb_connection) {
                command_print(CMD,
                        "gdb_sync command can only be run from within gdb using \"monitor gdb_sync\"");
                return ERROR_FAIL;
@@ -3617,7 +3943,7 @@ COMMAND_HANDLER(handle_gdb_sync_command)
 COMMAND_HANDLER(handle_gdb_port_command)
 {
        int retval = CALL_COMMAND_HANDLER(server_pipe_command, &gdb_port);
-       if (ERROR_OK == retval) {
+       if (retval == ERROR_OK) {
                free(gdb_port_next);
                gdb_port_next = strdup(gdb_port);
        }
@@ -3711,7 +4037,7 @@ COMMAND_HANDLER(handle_gdb_save_tdesc_command)
        size_t size_written;
 
        char *tdesc_filename = alloc_printf("%s.xml", target_type_name(target));
-       if (tdesc_filename == NULL) {
+       if (!tdesc_filename) {
                retval = ERROR_FAIL;
                goto out;
        }
@@ -3825,3 +4151,8 @@ void gdb_service_free(void)
        free(gdb_port);
        free(gdb_port_next);
 }
+
+int gdb_get_actual_connections(void)
+{
+       return gdb_actual_connections;
+}

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)