use COMMAND_HANDLER macro to define all commands
[openocd.git] / src / server / gdb_server.c
index 3e4ba8248acda76c14e0b73a656aad33c6a9143a..c0c5d7751b1192639c98f25efc728e143f73c201 100644 (file)
@@ -2,6 +2,12 @@
  *   Copyright (C) 2005 by Dominic Rath                                    *
  *   Dominic.Rath@gmx.de                                                   *
  *                                                                         *
+ *   Copyright (C) 2007,2008 Ã˜yvind Harboe                                 *
+ *   oyvind.harboe@zylin.com                                               *
+ *                                                                         *
+ *   Copyright (C) 2008 by Spencer Oliver                                  *
+ *   spen@spen-soft.co.uk                                                  *
+ *                                                                         *
  *   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     *
 #include "config.h"
 #endif
 
-#include "replacements.h"
-
 #include "gdb_server.h"
-
+#include "target_request.h"
+#include "register.h"
 #include "server.h"
-#include "log.h"
-#include "binarybuffer.h"
-#include "jtag.h"
-#include "breakpoints.h"
 #include "flash.h"
-#include "target_request.h"
-#include "configuration.h"
+#include "image.h"
+#include "jtag.h"
 
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdlib.h>
 
 #if 0
 #define _DEBUG_GDB_IO_
 #endif
 
+static gdb_connection_t *current_gdb_connection;
+
+static int gdb_breakpoint_override;
+static enum breakpoint_type gdb_breakpoint_override_type;
+
 extern int gdb_error(connection_t *connection, int retval);
-static unsigned short gdb_port;
+static unsigned short gdb_port = 3333;
 static const char *DIGITS = "0123456789abcdef";
 
-static void gdb_log_callback(void *priv, const char *file, int line,
+static void gdb_log_callback(void *priv, const char *file, unsigned line,
                const char *function, const char *string);
 
-enum gdb_detach_mode
-{
-       GDB_DETACH_RESUME,
-       GDB_DETACH_RESET,
-       GDB_DETACH_HALT,
-       GDB_DETACH_NOTHING
-};
-
-/* target behaviour on gdb detach */
-enum gdb_detach_mode detach_mode = GDB_DETACH_RESUME;
+/* number of gdb connections, mainly to supress gdb related debugging spam
+ * in helper/log.c when no gdb connections are actually active */
+int gdb_actual_connections;
 
 /* set if we are sending a memory map to gdb
  * via qXfer:memory-map:read packet */
-int gdb_use_memory_map = 0;
-int gdb_flash_program = 0;
+/* enabled by default*/
+int gdb_use_memory_map = 1;
+/* enabled by default*/
+int gdb_flash_program = 1;
 
 /* if set, data aborts cause an error to be reported in memory read packets
  * see the code in gdb_read_memory_packet() for further explanations */
@@ -99,27 +96,27 @@ int check_pending(connection_t *connection, int timeout_s, int *got_data)
        fd_set read_fds;
        gdb_connection_t *gdb_con = connection->priv;
        int t;
-       if (got_data==NULL)
+       if (got_data == NULL)
                got_data=&t;
-       *got_data=0;
+       *got_data = 0;
 
-       if (gdb_con->buf_cnt>0)
+       if (gdb_con->buf_cnt > 0)
        {
                *got_data = 1;
                return ERROR_OK;
        }
-       
+
        FD_ZERO(&read_fds);
        FD_SET(connection->fd, &read_fds);
-       
+
        tv.tv_sec = timeout_s;
        tv.tv_usec = 0;
-       if (select(connection->fd + 1, &read_fds, NULL, NULL, &tv) == 0)
+       if (socket_select(connection->fd + 1, &read_fds, NULL, NULL, &tv) == 0)
        {
                /* This can typically be because a "monitor" command took too long
                 * before printing any progress messages
                 */
-               if (timeout_s>0)
+               if (timeout_s > 0)
                {
                        return ERROR_GDB_TIMEOUT;
                } else
@@ -127,14 +124,14 @@ int check_pending(connection_t *connection, int timeout_s, int *got_data)
                        return ERROR_OK;
                }
        }
-       *got_data=FD_ISSET(connection->fd, &read_fds)!=0;
+       *got_data = FD_ISSET(connection->fd, &read_fds) != 0;
        return ERROR_OK;
 }
 
 int gdb_get_char(connection_t *connection, int* next_char)
 {
        gdb_connection_t *gdb_con = connection->priv;
-       int retval=ERROR_OK;
+       int retval = ERROR_OK;
 
 #ifdef _DEBUG_GDB_IO_
        char *debug_buffer;
@@ -157,10 +154,18 @@ int gdb_get_char(connection_t *connection, int* next_char)
 
        for (;;)
        {
-               retval=check_pending(connection, 1, NULL);
-               if (retval!=ERROR_OK)
-                       return retval;
-               gdb_con->buf_cnt = read_socket(connection->fd, gdb_con->buffer, GDB_BUFFER_SIZE);
+               if (connection->service->type == CONNECTION_PIPE)
+               {
+                       gdb_con->buf_cnt = read(connection->fd, gdb_con->buffer, GDB_BUFFER_SIZE);
+               }
+               else
+               {
+                       retval = check_pending(connection, 1, NULL);
+                       if (retval != ERROR_OK)
+                               return retval;
+                       gdb_con->buf_cnt = read_socket(connection->fd, gdb_con->buffer, GDB_BUFFER_SIZE);
+               }
+
                if (gdb_con->buf_cnt > 0)
                {
                        break;
@@ -174,7 +179,7 @@ int gdb_get_char(connection_t *connection, int* next_char)
 #ifdef _WIN32
                errno = WSAGetLastError();
 
-               switch(errno)
+               switch (errno)
                {
                        case WSAEWOULDBLOCK:
                                usleep(1000);
@@ -190,7 +195,7 @@ int gdb_get_char(connection_t *connection, int* next_char)
                                exit(-1);
                }
 #else
-               switch(errno)
+               switch (errno)
                {
                        case EAGAIN:
                                usleep(1000);
@@ -257,9 +262,20 @@ int gdb_write(connection_t *connection, void *data, int len)
        if (gdb_con->closed)
                return ERROR_SERVER_REMOTE_CLOSED;
 
-       if (write_socket(connection->fd, data, len) == len)
+       if (connection->service->type == CONNECTION_PIPE)
        {
-               return ERROR_OK;
+               /* write to stdout */
+               if (write(STDOUT_FILENO, data, len) == len)
+               {
+                       return ERROR_OK;
+               }
+       }
+       else
+       {
+               if (write_socket(connection->fd, data, len) == len)
+               {
+                       return ERROR_OK;
+               }
        }
        gdb_con->closed = 1;
        return ERROR_SERVER_REMOTE_CLOSED;
@@ -280,24 +296,29 @@ int gdb_put_packet_inner(connection_t *connection, char *buffer, int len)
                my_checksum += buffer[i];
 
 #ifdef _DEBUG_GDB_IO_
-       /* 
+       /*
         * At this point we should have nothing in the input queue from GDB,
         * however sometimes '-' is sent even though we've already received
         * an ACK (+) for everything we've sent off.
         */
-#ifndef _WIN32
        int gotdata;
        for (;;)
        {
-               if ((retval=check_pending(connection, 0, &gotdata))!=ERROR_OK)
+               if ((retval = check_pending(connection, 0, &gotdata)) != ERROR_OK)
                        return retval;
                if (!gotdata)
                        break;
                if ((retval = gdb_get_char(connection, &reply)) != ERROR_OK)
                        return retval;
+               if (reply == '$') {
+                       /* fix a problem with some IAR tools */
+                       gdb_putback_char(connection, reply);
+                       LOG_DEBUG("Unexpected start of new packet");
+                       break;
+               }
+
                LOG_WARNING("Discard unexpected char %c", reply);
        }
-#endif
 #endif
 
        while (1)
@@ -312,14 +333,17 @@ int gdb_put_packet_inner(connection_t *connection, char *buffer, int len)
 
                char local_buffer[1024];
                local_buffer[0] = '$';
-               if (len+4 <= sizeof(local_buffer))
+               if ((size_t)len + 4 <= sizeof(local_buffer))
                {
                        /* performance gain on smaller packets by only a single call to gdb_write() */
-                       memcpy(local_buffer+1, buffer, len++);
+                       memcpy(local_buffer + 1, buffer, len++);
                        local_buffer[len++] = '#';
                        local_buffer[len++] = DIGITS[(my_checksum >> 4) & 0xf];
                        local_buffer[len++] = DIGITS[my_checksum & 0xf];
-                       gdb_write(connection, local_buffer, len);
+                       if ((retval = gdb_write(connection, local_buffer, len)) != ERROR_OK)
+                       {
+                               return retval;
+                       }
                }
                else
                {
@@ -328,11 +352,23 @@ int gdb_put_packet_inner(connection_t *connection, char *buffer, int len)
                        local_buffer[1] = '#';
                        local_buffer[2] = DIGITS[(my_checksum >> 4) & 0xf];
                        local_buffer[3] = DIGITS[my_checksum & 0xf];
-                       gdb_write(connection, local_buffer, 1);
-                       gdb_write(connection, buffer, len);
-                       gdb_write(connection, local_buffer+1, 3);
+                       if ((retval = gdb_write(connection, local_buffer, 1)) != ERROR_OK)
+                       {
+                               return retval;
+                       }
+                       if ((retval = gdb_write(connection, buffer, len)) != ERROR_OK)
+                       {
+                               return retval;
+                       }
+                       if ((retval = gdb_write(connection, local_buffer + 1, 3)) != ERROR_OK)
+                       {
+                               return retval;
+                       }
                }
 
+               if (gdb_con->noack_mode)
+                       break;
+
                if ((retval = gdb_get_char(connection, &reply)) != ERROR_OK)
                        return retval;
 
@@ -357,17 +393,26 @@ int gdb_put_packet_inner(connection_t *connection, char *buffer, int len)
                                log_remove_callback(gdb_log_callback, connection);
                                LOG_WARNING("negative reply, retrying");
                        }
-                       else
-                       {
-                               LOG_ERROR("unknown character 0x%2.2x in reply, dropping connection", reply);
-                               gdb_con->closed=1;
+                       else if (reply == '$') {
+                               LOG_ERROR("GDB missing ack(1) - assumed good");
+                               gdb_putback_char(connection, reply);
+                               return ERROR_OK;
+                       } else {
+
+                               LOG_ERROR("unknown character(1) 0x%2.2x in reply, dropping connection", reply);
+                               gdb_con->closed = 1;
                                return ERROR_SERVER_REMOTE_CLOSED;
                        }
                }
+               else if (reply == '$') {
+                       LOG_ERROR("GDB missing ack(2) - assumed good");
+                       gdb_putback_char(connection, reply);
+                       return ERROR_OK;
+               }
                else
                {
-                       LOG_ERROR("unknown character 0x%2.2x in reply, dropping connection", reply);
-                       gdb_con->closed=1;
+                       LOG_ERROR("unknown character(2) 0x%2.2x in reply, dropping connection", reply);
+                       gdb_con->closed = 1;
                        return ERROR_SERVER_REMOTE_CLOSED;
                }
        }
@@ -383,16 +428,126 @@ int gdb_put_packet(connection_t *connection, char *buffer, int len)
        gdb_con->busy = 1;
        int retval = gdb_put_packet_inner(connection, buffer, len);
        gdb_con->busy = 0;
+
+       /* we sent some data, reset timer for keep alive messages */
+       kept_alive();
+
        return retval;
 }
 
-int gdb_get_packet_inner(connection_t *connection, char *buffer, int *len)
+static __inline__ int fetch_packet(connection_t *connection, int *checksum_ok, int noack, int *len, char *buffer)
 {
+       unsigned char my_checksum = 0;
+       char checksum[3];
        int character;
+       int retval;
+
+       gdb_connection_t *gdb_con = connection->priv;
+       my_checksum = 0;
        int count = 0;
+       count = 0;
+       for (;;)
+       {
+               /* The common case is that we have an entire packet with no escape chars.
+                * We need to leave at least 2 bytes in the buffer to have
+                * gdb_get_char() update various bits and bobs correctly.
+                */
+               if ((gdb_con->buf_cnt > 2) && ((gdb_con->buf_cnt + count) < *len))
+               {
+                       /* The compiler will struggle a bit with constant propagation and
+                        * aliasing, so we help it by showing that these values do not
+                        * change inside the loop
+                        */
+                       int i;
+                       char *buf = gdb_con->buf_p;
+                       int run = gdb_con->buf_cnt - 2;
+                       i = 0;
+                       int done = 0;
+                       while (i < run)
+                       {
+                               character = *buf++;
+                               i++;
+                               if (character == '#')
+                               {
+                                       /* Danger! character can be '#' when esc is
+                                        * used so we need an explicit boolean for done here.
+                                        */
+                                       done = 1;
+                                       break;
+                               }
+
+                               if (character == '}')
+                               {
+                                       /* data transmitted in binary mode (X packet)
+                                        * uses 0x7d as escape character */
+                                       my_checksum += character & 0xff;
+                                       character = *buf++;
+                                       i++;
+                                       my_checksum += character & 0xff;
+                                       buffer[count++] = (character ^ 0x20) & 0xff;
+                               }
+                               else
+                               {
+                                       my_checksum += character & 0xff;
+                                       buffer[count++] = character & 0xff;
+                               }
+                       }
+                       gdb_con->buf_p += i;
+                       gdb_con->buf_cnt -= i;
+                       if (done)
+                               break;
+               }
+               if (count > *len)
+               {
+                       LOG_ERROR("packet buffer too small");
+                       return ERROR_GDB_BUFFER_TOO_SMALL;
+               }
+
+               if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)
+                       return retval;
+
+               if (character == '#')
+                       break;
+
+               if (character == '}')
+               {
+                       /* data transmitted in binary mode (X packet)
+                        * uses 0x7d as escape character */
+                       my_checksum += character & 0xff;
+                       if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)
+                               return retval;
+                       my_checksum += character & 0xff;
+                       buffer[count++] = (character ^ 0x20) & 0xff;
+               }
+               else
+               {
+                       my_checksum += character & 0xff;
+                       buffer[count++] = character & 0xff;
+               }
+       }
+
+       *len = count;
+
+       if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)
+               return retval;
+       checksum[0] = character;
+       if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)
+               return retval;
+       checksum[1] = character;
+       checksum[2] = 0;
+
+       if (!noack)
+       {
+               *checksum_ok = (my_checksum == strtoul(checksum, NULL, 16));
+       }
+
+       return ERROR_OK;
+}
+
+int gdb_get_packet_inner(connection_t *connection, char *buffer, int *len)
+{
+       int character;
        int retval;
-       char checksum[3];
-       unsigned char my_checksum = 0;
        gdb_connection_t *gdb_con = connection->priv;
 
        while (1)
@@ -411,6 +566,8 @@ int gdb_get_packet_inner(connection_t *connection, char *buffer, int *len)
                                case '$':
                                        break;
                                case '+':
+                                       /* gdb sends a dummy ack '+' at every remote connect - see remote_start_remote (remote.c)
+                                        * incase anyone tries to debug why they receive this warning every time */
                                        LOG_WARNING("acknowledgment received, but no packet pending");
                                        break;
                                case '-':
@@ -426,108 +583,35 @@ int gdb_get_packet_inner(connection_t *connection, char *buffer, int *len)
                        }
                } while (character != '$');
 
-               my_checksum = 0;
 
-               count = 0;
-               gdb_connection_t *gdb_con = connection->priv;
-               for (;;)
-               {
-                       /* The common case is that we have an entire packet with no escape chars.
-                        * We need to leave at least 2 bytes in the buffer to have
-                        * gdb_get_char() update various bits and bobs correctly.
-                        */
-                       if ((gdb_con->buf_cnt > 2) && ((gdb_con->buf_cnt+count) < *len))
-                       {
-                               /* The compiler will struggle a bit with constant propagation and
-                                * aliasing, so we help it by showing that these values do not
-                                * change inside the loop
-                                */
-                               int i;
-                               char *buf = gdb_con->buf_p;
-                               int run = gdb_con->buf_cnt - 2;
-                               i = 0;
-                               int done = 0;
-                               while (i < run)
-                               {
-                                       character = *buf++;
-                                       i++;
-                                       if (character == '#')
-                                       {
-                                               /* Danger! character can be '#' when esc is
-                                                * used so we need an explicit boolean for done here.
-                                                */
-                                               done = 1;
-                                               break;
-                                       }
 
-                                       if (character == '}')
-                                       {
-                                               /* data transmitted in binary mode (X packet)
-                                                * uses 0x7d as escape character */
-                                               my_checksum += character & 0xff;
-                                               character = *buf++;
-                                               i++;
-                                               my_checksum += character & 0xff;
-                                               buffer[count++] = (character ^ 0x20) & 0xff;
-                                       } else
-                                       {
-                                               my_checksum += character & 0xff;
-                                               buffer[count++] = character & 0xff;
-                                       }
-                               }
-                               gdb_con->buf_p += i;
-                               gdb_con->buf_cnt -= i;
-                               if (done)
-                                       break;
-                       }
-                       if (count > *len)
-                       {
-                               LOG_ERROR("packet buffer too small");
-                               return ERROR_GDB_BUFFER_TOO_SMALL;
-                       }
-
-                       if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)
+               int checksum_ok = 0;
+               /* explicit code expansion here to get faster inlined code in -O3 by not
+                * calculating checksum
+                */
+               if (gdb_con->noack_mode)
+               {
+                       if ((retval = fetch_packet(connection, &checksum_ok, 1, len, buffer)) != ERROR_OK)
+                               return retval;
+               } else
+               {
+                       if ((retval = fetch_packet(connection, &checksum_ok, 0, len, buffer)) != ERROR_OK)
                                return retval;
-
-                       if (character == '#')
-                               break;
-
-                       if (character == '}')
-                       {
-                               /* data transmitted in binary mode (X packet)
-                                * uses 0x7d as escape character */
-                               my_checksum += character & 0xff;
-                               if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)
-                                       return retval;
-                               my_checksum += character & 0xff;
-                               buffer[count++] = (character ^ 0x20) & 0xff;
-                       }
-                       else
-                       {
-                               my_checksum += character & 0xff;
-                               buffer[count++] = character & 0xff;
-                       }
-
                }
 
-               *len = count;
-
-               if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)
-                       return retval;
-               checksum[0] = character;
-               if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)
-                       return retval;
-               checksum[1] = character;
-               checksum[2] = 0;
-
-               if (my_checksum == strtoul(checksum, NULL, 16))
+               if (gdb_con->noack_mode)
                {
-                       gdb_write(connection, "+", 1);
+                       /* checksum is not checked in noack mode */
+                       break;
+               }
+               if (checksum_ok)
+               {
+                       if ((retval = gdb_write(connection, "+", 1)) != ERROR_OK)
+                       {
+                               return retval;
+                       }
                        break;
                }
-
-               LOG_WARNING("checksum error, requesting retransmission");
-               gdb_write(connection, "-", 1);
        }
        if (gdb_con->closed)
                return ERROR_SERVER_REMOTE_CLOSED;
@@ -556,89 +640,82 @@ int gdb_output_con(connection_t *connection, const char* line)
                return ERROR_GDB_BUFFER_TOO_SMALL;
 
        hex_buffer[0] = 'O';
-       for (i=0; i<bin_size; i++)
+       for (i = 0; i < bin_size; i++)
                snprintf(hex_buffer + 1 + i*2, 3, "%2.2x", line[i]);
-       hex_buffer[bin_size*2+1] = 0;
+       hex_buffer[bin_size*2 + 1] = 0;
 
-       gdb_put_packet(connection, hex_buffer, bin_size*2 + 1);
+       int retval = gdb_put_packet(connection, hex_buffer, bin_size*2 + 1);
 
        free(hex_buffer);
-       return ERROR_OK;
+       return retval;
 }
 
-int gdb_output(struct command_context_s *context, char* line)
+int gdb_output(struct command_context_s *context, const char* line)
 {
        /* this will be dumped to the log and also sent as an O packet if possible */
        LOG_USER_N("%s", line);
        return ERROR_OK;
 }
 
-int gdb_program_handler(struct target_s *target, enum target_event event, void *priv)
+
+static void gdb_frontend_halted(struct target_s *target, connection_t *connection)
 {
-       FILE *script;
-       struct command_context_s *cmd_ctx = priv;
+       gdb_connection_t *gdb_connection = connection->priv;
 
-       if (target->gdb_program_script)
+       /* In the GDB protocol when we are stepping or continuing execution,
+        * we have a lingering reply. Upon receiving a halted event
+        * when we have that lingering packet, we reply to the original
+        * step or continue packet.
+        *
+        * Executing monitor commands can bring the target in and
+        * out of the running state so we'll see lots of TARGET_EVENT_XXX
+        * that are to be ignored.
+        */
+       if (gdb_connection->frontend_state == TARGET_RUNNING)
        {
-               script = open_file_from_path(target->gdb_program_script, "r");
-               if (!script)
+               char sig_reply[4];
+               int signal;
+
+               /* stop forwarding log packets! */
+               log_remove_callback(gdb_log_callback, connection);
+
+               if (gdb_connection->ctrl_c)
                {
-                       LOG_ERROR("couldn't open script file %s", target->gdb_program_script);
-                               return ERROR_OK;
+                       signal = 0x2;
+                       gdb_connection->ctrl_c = 0;
+               }
+               else
+               {
+                       signal = gdb_last_signal(target);
                }
 
-               LOG_INFO("executing gdb_program script '%s'", target->gdb_program_script);
-               command_run_file(cmd_ctx, script, COMMAND_EXEC);
-               fclose(script);
-
-               jtag_execute_queue();
+               snprintf(sig_reply, 4, "T%2.2x", signal);
+               gdb_put_packet(connection, sig_reply, 3);
+               gdb_connection->frontend_state = TARGET_HALTED;
        }
-
-       return ERROR_OK;
 }
 
 int gdb_target_callback_event_handler(struct target_s *target, enum target_event event, void *priv)
 {
+       int retval;
        connection_t *connection = priv;
-       gdb_connection_t *gdb_connection = connection->priv;
-       char sig_reply[4];
-       int signal;
 
+       target_handle_event(target, event);
        switch (event)
        {
+               case TARGET_EVENT_GDB_HALT:
+                       gdb_frontend_halted(target, connection);
+                       break;
                case TARGET_EVENT_HALTED:
-                       /* In the GDB protocol when we are stepping or coninuing execution,
-                        * we have a lingering reply. Upon receiving a halted event
-                        * when we have that lingering packet, we reply to the original
-                        * step or continue packet.
-                        *
-                        * Executing monitor commands can bring the target in and
-                        * out of the running state so we'll see lots of TARGET_EVENT_XXX
-                        * that are to be ignored.
-                        */
-                       if (gdb_connection->frontend_state == TARGET_RUNNING)
+                       target_call_event_callbacks(target, TARGET_EVENT_GDB_END);
+                       break;
+               case TARGET_EVENT_GDB_FLASH_ERASE_START:
+                       target_handle_event(target, TARGET_EVENT_OLD_gdb_program_config);
+                       if ((retval = jtag_execute_queue()) != ERROR_OK)
                        {
-                               /* stop forwarding log packets! */
-                               log_remove_callback(gdb_log_callback, connection);
-
-                               if (gdb_connection->ctrl_c)
-                               {
-                                       signal = 0x2;
-                                       gdb_connection->ctrl_c = 0;
-                               }
-                               else
-                               {
-                                       signal = gdb_last_signal(target);
-                               }
-
-                               snprintf(sig_reply, 4, "T%2.2x", signal);
-                               gdb_put_packet(connection, sig_reply, 3);
-                               gdb_connection->frontend_state = TARGET_HALTED;
+                               return retval;
                        }
                        break;
-               case TARGET_EVENT_GDB_PROGRAM:
-                       gdb_program_handler(target, event, connection->cmd_ctx);
-                       break;
                default:
                        break;
        }
@@ -646,7 +723,6 @@ int gdb_target_callback_event_handler(struct target_s *target, enum target_event
        return ERROR_OK;
 }
 
-
 int gdb_new_connection(connection_t *connection)
 {
        gdb_connection_t *gdb_connection = malloc(sizeof(gdb_connection_t));
@@ -664,43 +740,41 @@ int gdb_new_connection(connection_t *connection)
        gdb_connection->vflash_image = NULL;
        gdb_connection->closed = 0;
        gdb_connection->busy = 0;
-       
+       gdb_connection->noack_mode = 0;
+       gdb_connection->sync = true;
+
        /* send ACK to GDB for debug request */
        gdb_write(connection, "+", 1);
 
        /* output goes through gdb connection */
        command_set_output_handler(connection->cmd_ctx, gdb_output, connection);
 
+       /* we must remove all breakpoints registered to the target as a previous
+        * GDB session could leave dangling breakpoints if e.g. communication
+        * timed out.
+        */
+       breakpoint_clear_target(gdb_service->target);
+       watchpoint_clear_target(gdb_service->target);
+
        /* register callback to be informed about target events */
        target_register_event_callback(gdb_target_callback_event_handler, connection);
 
-       /* a gdb session just attached, try to put the target in halt mode.
-        * 
-        * DANGER!!!! 
-        * 
-        * If the halt fails(e.g. target needs a reset, JTAG communication not
-        * working, etc.), then the GDB connect will succeed as
-        * the get_gdb_reg_list() will lie and return a register list with
-        * dummy values.
-        * 
-        * This allows GDB monitor commands to be run from a GDB init script to
-        * initialize the target
-        * 
-        * Also, since the halt() is asynchronous target connect will be
-        * instantaneous and thus avoiding annoying timeout problems during
-        * connect. 
-        */
-       target_halt(gdb_service->target);
-       
        /* remove the initial ACK from the incoming buffer */
        if ((retval = gdb_get_char(connection, &initial_ack)) != ERROR_OK)
                return retval;
 
-       /* FIX!!!??? would we actually ever receive a + here??? 
+       /* FIX!!!??? would we actually ever receive a + here???
         * Not observed.
         */
        if (initial_ack != '+')
                gdb_putback_char(connection, initial_ack);
+       target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_ATTACH);
+
+       gdb_actual_connections++;
+       LOG_DEBUG("New GDB Connection: %d, Target %s, state: %s",
+                 gdb_actual_connections,
+                 gdb_service->target->cmd_name,
+                 target_state_name(gdb_service->target));
 
        return ERROR_OK;
 }
@@ -710,6 +784,17 @@ int gdb_connection_closed(connection_t *connection)
        gdb_service_t *gdb_service = connection->service->priv;
        gdb_connection_t *gdb_connection = connection->priv;
 
+       /* we're done forwarding messages. Tear down callback before
+        * cleaning up connection.
+        */
+       log_remove_callback(gdb_log_callback, connection);
+
+       gdb_actual_connections--;
+       LOG_DEBUG("GDB Close, Target: %s, state: %s, gdb_actual_connections=%d",
+                 gdb_service->target->cmd_name,
+                 target_state_name(gdb_service->target),
+                 gdb_actual_connections);
+
        /* see if an image built with vFlash commands is left */
        if (gdb_connection->vflash_image)
        {
@@ -731,16 +816,20 @@ int gdb_connection_closed(connection_t *connection)
                LOG_ERROR("BUG: connection->priv == NULL");
        }
 
+
        target_unregister_event_callback(gdb_target_callback_event_handler, connection);
-       log_remove_callback(gdb_log_callback, connection);
+
+       target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_END);
+
+       target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_DETACH);
 
        return ERROR_OK;
 }
 
-void gdb_send_error(connection_t *connection, u8 the_error)
+void gdb_send_error(connection_t *connection, uint8_t the_error)
 {
        char err[4];
-       snprintf(err, 4, "E%2.2X", the_error );
+       snprintf(err, 4, "E%2.2X", the_error);
        gdb_put_packet(connection, err, 3);
 }
 
@@ -757,40 +846,72 @@ int gdb_last_signal_packet(connection_t *connection, target_t *target, char* pac
        return ERROR_OK;
 }
 
-/* Convert register to string of bits. NB! The # of bits in the
+static int gdb_reg_pos(target_t *target, int pos, int len)
+{
+       if (target->endianness == TARGET_LITTLE_ENDIAN)
+               return pos;
+       else
+               return len - 1 - pos;
+}
+
+/* Convert register to string of bytes. NB! The # of bits in the
  * register might be non-divisible by 8(a byte), in which
- * case an entire byte is shown. */
+ * case an entire byte is shown.
+ *
+ * NB! the format on the wire is the target endianess
+ *
+ * The format of reg->value is little endian
+ *
+ */
 void gdb_str_to_target(target_t *target, char *tstr, reg_t *reg)
 {
        int i;
 
-       u8 *buf;
+       uint8_t *buf;
        int buf_len;
        buf = reg->value;
        buf_len = CEIL(reg->size, 8);
 
        for (i = 0; i < buf_len; i++)
        {
-               tstr[i*2]   = DIGITS[(buf[i]>>4) & 0xf];
-               tstr[i*2+1] = DIGITS[buf[i]&0xf];
+               int j = gdb_reg_pos(target, i, buf_len);
+               tstr[i*2]   = DIGITS[(buf[j]>>4) & 0xf];
+               tstr[i*2 + 1] = DIGITS[buf[j]&0xf];
        }
 }
 
-void gdb_target_to_str(target_t *target, char *tstr, char *str)
+static int hextoint(char c)
 {
-       int str_len = strlen(tstr);
-       int i;
+       if (c>='0'&&c<='9')
+       {
+               return c-'0';
+       }
+       c = toupper(c);
+       if (c>='A'&&c<='F')
+       {
+               return c-'A'+10;
+       }
+       LOG_ERROR("BUG: invalid register value %08x", c);
+       return 0;
+}
 
+/* copy over in register buffer */
+void gdb_target_to_reg(target_t *target, char *tstr, int str_len, uint8_t *bin)
+{
        if (str_len % 2)
        {
                LOG_ERROR("BUG: gdb value with uneven number of characters encountered");
                exit(-1);
        }
 
-       for (i = 0; i < str_len; i+=2)
+       int i;
+       for (i = 0; i < str_len; i += 2)
        {
-               str[str_len - i - 1] = tstr[i + 1];
-               str[str_len - i - 2] = tstr[i];
+               uint8_t t = hextoint(tstr[i]) << 4;
+               t |= hextoint(tstr[i + 1]);
+
+               int j = gdb_reg_pos(target, i/2, str_len/2);
+               bin[j] = t;
        }
 }
 
@@ -808,7 +929,7 @@ int gdb_get_registers_packet(connection_t *connection, target_t *target, char* p
        LOG_DEBUG("-");
 #endif
 
-       if ((retval = target->type->get_gdb_reg_list(target, &reg_list, &reg_list_size)) != ERROR_OK)
+       if ((retval = target_get_gdb_reg_list(target, &reg_list, &reg_list_size)) != ERROR_OK)
        {
                return gdb_error(connection, retval);
        }
@@ -866,7 +987,7 @@ int gdb_set_registers_packet(connection_t *connection, target_t *target, char *p
                return ERROR_SERVER_REMOTE_CLOSED;
        }
 
-       if ((retval = target->type->get_gdb_reg_list(target, &reg_list, &reg_list_size)) != ERROR_OK)
+       if ((retval = target_get_gdb_reg_list(target, &reg_list, &reg_list_size)) != ERROR_OK)
        {
                return gdb_error(connection, retval);
        }
@@ -874,32 +995,28 @@ int gdb_set_registers_packet(connection_t *connection, target_t *target, char *p
        packet_p = packet;
        for (i = 0; i < reg_list_size; i++)
        {
-               u8 *bin_buf;
-               char *hex_buf;
-               reg_arch_type_t *arch_type;
+               uint8_t *bin_buf;
+               int chars = (CEIL(reg_list[i]->size, 8) * 2);
 
-               /* convert from GDB-string (target-endian) to hex-string (big-endian) */
-               hex_buf = malloc(CEIL(reg_list[i]->size, 8) * 2);
-               gdb_target_to_str(target, packet_p, hex_buf);
+               if (packet_p + chars > packet + packet_size)
+               {
+                       LOG_ERROR("BUG: register packet is too small for registers");
+               }
 
-               /* convert hex-string to binary buffer */
+               reg_arch_type_t *arch_type;
                bin_buf = malloc(CEIL(reg_list[i]->size, 8));
-               str_to_buf(hex_buf, CEIL(reg_list[i]->size, 8) * 2, bin_buf, reg_list[i]->size, 16);
+               gdb_target_to_reg(target, packet_p, chars, bin_buf);
 
                /* get register arch_type, and call set method */
                arch_type = register_get_arch_type(reg_list[i]->arch_type);
-               if (arch_type == NULL)
-               {
-                       LOG_ERROR("BUG: encountered unregistered arch type");
-                       exit(-1);
-               }
+
                arch_type->set(reg_list[i], bin_buf);
 
                /* advance packet pointer */
-               packet_p += (CEIL(reg_list[i]->size, 8) * 2);
+               packet_p += chars;
+
 
                free(bin_buf);
-               free(hex_buf);
        }
 
        /* free reg_t *reg_list[] array allocated by get_gdb_reg_list */
@@ -922,7 +1039,7 @@ int gdb_get_register_packet(connection_t *connection, target_t *target, char *pa
        LOG_DEBUG("-");
 #endif
 
-       if ((retval = target->type->get_gdb_reg_list(target, &reg_list, &reg_list_size)) != ERROR_OK)
+       if ((retval = target_get_gdb_reg_list(target, &reg_list, &reg_list_size)) != ERROR_OK)
        {
                return gdb_error(connection, retval);
        }
@@ -948,8 +1065,7 @@ int gdb_get_register_packet(connection_t *connection, target_t *target, char *pa
 int gdb_set_register_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
 {
        char *separator;
-       char *hex_buf;
-       u8 *bin_buf;
+       uint8_t *bin_buf;
        int reg_num = strtoul(packet + 1, &separator, 16);
        reg_t **reg_list;
        int reg_list_size;
@@ -958,7 +1074,7 @@ int gdb_set_register_packet(connection_t *connection, target_t *target, char *pa
 
        LOG_DEBUG("-");
 
-       if ((retval = target->type->get_gdb_reg_list(target, &reg_list, &reg_list_size)) != ERROR_OK)
+       if ((retval = target_get_gdb_reg_list(target, &reg_list, &reg_list_size)) != ERROR_OK)
        {
                return gdb_error(connection, retval);
        }
@@ -966,7 +1082,7 @@ int gdb_set_register_packet(connection_t *connection, target_t *target, char *pa
        if (reg_list_size < reg_num)
        {
                LOG_ERROR("gdb requested a non-existing register");
-               return ERROR_SERVER_REMOTE_CLOSED;      
+               return ERROR_SERVER_REMOTE_CLOSED;
        }
 
        if (*separator != '=')
@@ -976,26 +1092,20 @@ int gdb_set_register_packet(connection_t *connection, target_t *target, char *pa
        }
 
        /* convert from GDB-string (target-endian) to hex-string (big-endian) */
-       hex_buf = malloc(CEIL(reg_list[reg_num]->size, 8) * 2);
-       gdb_target_to_str(target, separator + 1, hex_buf);
-
-       /* convert hex-string to binary buffer */
        bin_buf = malloc(CEIL(reg_list[reg_num]->size, 8));
-       str_to_buf(hex_buf, CEIL(reg_list[reg_num]->size, 8) * 2, bin_buf, reg_list[reg_num]->size, 16);
+       int chars = (CEIL(reg_list[reg_num]->size, 8) * 2);
+
+       /* fix!!! add some sanity checks on packet size here */
+
+       gdb_target_to_reg(target, separator + 1, chars, bin_buf);
 
-       /* get register arch_type, and call set method */
+               /* get register arch_type, and call set method */
        arch_type = register_get_arch_type(reg_list[reg_num]->arch_type);
-       if (arch_type == NULL)
-       {
-               LOG_ERROR("BUG: encountered unregistered arch type");
-               exit(-1);
-       }
        arch_type->set(reg_list[reg_num], bin_buf);
 
        gdb_put_packet(connection, "OK", 2);
 
        free(bin_buf);
-       free(hex_buf);
        free(reg_list);
 
        return ERROR_OK;
@@ -1035,10 +1145,10 @@ int gdb_error(connection_t *connection, int retval)
 int gdb_read_memory_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
 {
        char *separator;
-       u32 addr = 0;
-       u32 len = 0;
+       uint32_t addr = 0;
+       uint32_t len = 0;
 
-       u8 *buffer;
+       uint8_t *buffer;
        char *hex_buffer;
 
        int retval = ERROR_OK;
@@ -1054,15 +1164,15 @@ int gdb_read_memory_packet(connection_t *connection, target_t *target, char *pac
                return ERROR_SERVER_REMOTE_CLOSED;
        }
 
-       len = strtoul(separator+1, NULL, 16);
+       len = strtoul(separator + 1, NULL, 16);
 
        buffer = malloc(len);
 
-       LOG_DEBUG("addr: 0x%8.8x, len: 0x%8.8x", addr, len);
+       LOG_DEBUG("addr: 0x%8.8" PRIx32 ", len: 0x%8.8" PRIx32 "", addr, len);
 
        retval = target_read_buffer(target, addr, len, buffer);
 
-       if ((retval == ERROR_TARGET_DATA_ABORT) && (!gdb_report_data_abort))
+       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.
                 * At some point this might be fixed in GDB, in which case this code can be removed.
@@ -1071,7 +1181,7 @@ int gdb_read_memory_packet(connection_t *connection, target_t *target, char *pac
                 * gained by involving the user in this problem that hopefully will get resolved
                 * eventually
                 *
-                * http://sourceware.org/cgi-bin/gnatsweb.pl?cmd=view%20audit-trail&database=gdb&pr=2395
+                * http://sourceware.org/cgi-bin/gnatsweb.pl?cmd = view%20audit-trail&database = gdb&pr = 2395
                 *
                 * For now, the default is to fix up things to make current GDB versions work.
                 * This can be overwritten using the gdb_report_data_abort <'enable'|'disable'> command.
@@ -1084,10 +1194,10 @@ int gdb_read_memory_packet(connection_t *connection, target_t *target, char *pac
        {
                hex_buffer = malloc(len * 2 + 1);
 
-               int i;
+               uint32_t i;
                for (i = 0; i < len; i++)
                {
-                       u8 t = buffer[i];
+                       uint8_t t = buffer[i];
                        hex_buffer[2 * i] = DIGITS[(t >> 4) & 0xf];
                        hex_buffer[2 * i + 1] = DIGITS[t & 0xf];
                }
@@ -1109,12 +1219,12 @@ int gdb_read_memory_packet(connection_t *connection, target_t *target, char *pac
 int gdb_write_memory_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
 {
        char *separator;
-       u32 addr = 0;
-       u32 len = 0;
+       uint32_t addr = 0;
+       uint32_t len = 0;
 
-       u8 *buffer;
+       uint8_t *buffer;
 
-       int i;
+       uint32_t i;
        int retval;
 
        /* skip command character */
@@ -1128,7 +1238,7 @@ int gdb_write_memory_packet(connection_t *connection, target_t *target, char *pa
                return ERROR_SERVER_REMOTE_CLOSED;
        }
 
-       len = strtoul(separator+1, &separator, 16);
+       len = strtoul(separator + 1, &separator, 16);
 
        if (*(separator++) != ':')
        {
@@ -1138,12 +1248,12 @@ int gdb_write_memory_packet(connection_t *connection, target_t *target, char *pa
 
        buffer = malloc(len);
 
-       LOG_DEBUG("addr: 0x%8.8x, len: 0x%8.8x", addr, len);
+       LOG_DEBUG("addr: 0x%8.8" PRIx32 ", len: 0x%8.8" PRIx32 "", addr, len);
 
-       for (i=0; i<len; i++)
+       for (i = 0; i < len; i++)
        {
-               u32 tmp;
-               sscanf(separator + 2*i, "%2x", &tmp);
+               uint32_t tmp;
+               sscanf(separator + 2*i, "%2" SCNx32 , &tmp);
                buffer[i] = tmp;
        }
 
@@ -1166,8 +1276,8 @@ int gdb_write_memory_packet(connection_t *connection, target_t *target, char *pa
 int gdb_write_memory_binary_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
 {
        char *separator;
-       u32 addr = 0;
-       u32 len = 0;
+       uint32_t addr = 0;
+       uint32_t len = 0;
 
        int retval;
 
@@ -1182,7 +1292,7 @@ int gdb_write_memory_binary_packet(connection_t *connection, target_t *target, c
                return ERROR_SERVER_REMOTE_CLOSED;
        }
 
-       len = strtoul(separator+1, &separator, 16);
+       len = strtoul(separator + 1, &separator, 16);
 
        if (*(separator++) != ':')
        {
@@ -1193,9 +1303,9 @@ int gdb_write_memory_binary_packet(connection_t *connection, target_t *target, c
        retval = ERROR_OK;
        if (len)
        {
-               LOG_DEBUG("addr: 0x%8.8x, len: 0x%8.8x", addr, len);
+               LOG_DEBUG("addr: 0x%8.8" PRIx32 ", len: 0x%8.8" PRIx32 "", addr, len);
 
-               retval = target_write_buffer(target, addr, len, (u8*)separator);
+               retval = target_write_buffer(target, addr, len, (uint8_t*)separator);
        }
 
        if (retval == ERROR_OK)
@@ -1211,10 +1321,11 @@ int gdb_write_memory_binary_packet(connection_t *connection, target_t *target, c
        return ERROR_OK;
 }
 
-void gdb_step_continue_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
+int gdb_step_continue_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
 {
        int current = 0;
-       u32 address = 0x0;
+       uint32_t address = 0x0;
+       int retval = ERROR_OK;
 
        LOG_DEBUG("-");
 
@@ -1231,13 +1342,16 @@ void gdb_step_continue_packet(connection_t *connection, target_t *target, char *
        if (packet[0] == 'c')
        {
                LOG_DEBUG("continue");
-               target_resume(target, current, address, 0, 0); /* resume at current address, don't handle breakpoints, not debugging */
+               target_handle_event(target, TARGET_EVENT_OLD_pre_resume);
+               retval = target_resume(target, current, address, 0, 0); /* resume at current address, don't handle breakpoints, not debugging */
        }
        else if (packet[0] == 's')
        {
                LOG_DEBUG("step");
-               target->type->step(target, current, address, 0); /* step at current or address, don't handle breakpoints */
+               /* step at current or address, don't handle breakpoints */
+               retval = target_step(target, current, address, 0);
        }
+       return retval;
 }
 
 int gdb_breakpoint_watchpoint_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
@@ -1245,8 +1359,8 @@ int gdb_breakpoint_watchpoint_packet(connection_t *connection, target_t *target,
        int type;
        enum breakpoint_type bp_type = BKPT_SOFT /* dummy init to avoid warning */;
        enum watchpoint_rw wp_type;
-       u32 address;
-       u32 size;
+       uint32_t address;
+       uint32_t size;
        char *separator;
        int retval;
 
@@ -1265,13 +1379,18 @@ int gdb_breakpoint_watchpoint_packet(connection_t *connection, target_t *target,
        else if (type == 4) /* access watchpoint */
                wp_type = WPT_ACCESS;
 
+       if (gdb_breakpoint_override && ((bp_type == BKPT_SOFT)||(bp_type == BKPT_HARD)))
+       {
+               bp_type = gdb_breakpoint_override_type;
+       }
+
        if (*separator != ',')
        {
                LOG_ERROR("incomplete breakpoint/watchpoint packet received, dropping connection");
                return ERROR_SERVER_REMOTE_CLOSED;
        }
 
-       address = strtoul(separator+1, &separator, 16);
+       address = strtoul(separator + 1, &separator, 16);
 
        if (*separator != ',')
        {
@@ -1279,7 +1398,7 @@ int gdb_breakpoint_watchpoint_packet(connection_t *connection, target_t *target,
                return ERROR_SERVER_REMOTE_CLOSED;
        }
 
-       size = strtoul(separator+1, &separator, 16);
+       size = strtoul(separator + 1, &separator, 16);
 
        switch (type)
        {
@@ -1396,19 +1515,19 @@ static int decode_xfer_read(char *buf, char **annex, int *ofs, unsigned int *len
        if (*separator != ',')
                return -1;
 
-       *len = strtoul(separator+1, NULL, 16);
+       *len = strtoul(separator + 1, NULL, 16);
 
        return 0;
 }
 
 int gdb_calc_blocksize(flash_bank_t *bank)
 {
-       int i;
-       int block_size = 0xffffffff;
+       uint32_t i;
+       uint32_t block_size = 0xffffffff;
 
        /* loop through all sectors and return smallest sector size */
 
-       for (i = 0; i < bank->num_sectors; i++)
+       for (i = 0; i < (uint32_t)bank->num_sectors; i++)
        {
                if (bank->sectors[i].size < block_size)
                        block_size = bank->sectors[i].size;
@@ -1417,9 +1536,28 @@ int gdb_calc_blocksize(flash_bank_t *bank)
        return block_size;
 }
 
+static int compare_bank (const void * a, const void * b)
+{
+       flash_bank_t *b1, *b2;
+       b1=*((flash_bank_t **)a);
+       b2=*((flash_bank_t **)b);
+
+       if (b1->base == b2->base)
+       {
+               return 0;
+       } else if (b1->base > b2->base)
+       {
+               return 1;
+       } else
+       {
+               return -1;
+       }
+}
+
 int gdb_query_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
 {
        command_context_t *cmd_ctx = connection->cmd_ctx;
+       gdb_connection_t *gdb_connection = connection->priv;
 
        if (strstr(packet, "qRcmd,"))
        {
@@ -1428,10 +1566,10 @@ int gdb_query_packet(connection_t *connection, target_t *target, char *packet, i
                        char *cmd;
                        int i;
                        cmd = malloc((packet_size - 6)/2 + 1);
-                       for (i=0; i < (packet_size - 6)/2; i++)
+                       for (i = 0; i < (packet_size - 6)/2; i++)
                        {
-                               u32 tmp;
-                               sscanf(packet + 6 + 2*i, "%2x", &tmp);
+                               uint32_t tmp;
+                               sscanf(packet + 6 + 2*i, "%2" SCNx32 , &tmp);
                                cmd[i] = tmp;
                        }
                        cmd[(packet_size - 6)/2] = 0x0;
@@ -1439,7 +1577,11 @@ int gdb_query_packet(connection_t *connection, target_t *target, char *packet, i
                        /* We want to print all debug output to GDB connection */
                        log_add_callback(gdb_log_callback, connection);
                        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);
+                       current_gdb_connection = NULL;
                        target_call_timer_callbacks_now();
                        log_remove_callback(gdb_log_callback, connection);
                        free(cmd);
@@ -1454,9 +1596,9 @@ int gdb_query_packet(connection_t *connection, target_t *target, char *packet, i
                        int retval;
                        char gdb_reply[10];
                        char *separator;
-                       u32 checksum;
-                       u32 addr = 0;
-                       u32 len = 0;
+                       uint32_t checksum;
+                       uint32_t addr = 0;
+                       uint32_t len = 0;
 
                        /* skip command character */
                        packet += 5;
@@ -1475,7 +1617,7 @@ int gdb_query_packet(connection_t *connection, target_t *target, char *packet, i
 
                        if (retval == ERROR_OK)
                        {
-                               snprintf(gdb_reply, 10, "C%8.8x", checksum);
+                               snprintf(gdb_reply, 10, "C%8.8" PRIx32 "", checksum);
                                gdb_put_packet(connection, gdb_reply, 9);
                        }
                        else
@@ -1497,8 +1639,8 @@ int gdb_query_packet(connection_t *connection, target_t *target, char *packet, i
                int size = 0;
 
                xml_printf(&retval, &buffer, &pos, &size,
-                               "PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read-",
-                               (GDB_BUFFER_SIZE - 1), gdb_use_memory_map == 1 ? '+' : '-');
+                               "PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read-;QStartNoAckMode+",
+                               (GDB_BUFFER_SIZE - 1), ((gdb_use_memory_map == 1) && (flash_get_bank_count() > 0)) ? '+' : '-');
 
                if (retval != ERROR_OK)
                {
@@ -1511,7 +1653,7 @@ int gdb_query_packet(connection_t *connection, target_t *target, char *packet, i
 
                return ERROR_OK;
        }
-       else if (strstr(packet, "qXfer:memory-map:read::"))
+       else if (strstr(packet, "qXfer:memory-map:read::") && (flash_get_bank_count() > 0))
        {
                /* We get away with only specifying flash here. Regions that are not
                 * specified are treated as if we provided no memory map(if not we
@@ -1538,12 +1680,39 @@ int gdb_query_packet(connection_t *connection, target_t *target, char *packet, i
 
                xml_printf(&retval, &xml, &pos, &size, "<memory-map>\n");
 
+               /*
+               sort banks in ascending order, we need to make non-flash memory be ram(or rather
+               read/write) by default for GDB.
+               GDB does not have a concept of non-cacheable read/write memory.
+                */
+               flash_bank_t **banks = malloc(sizeof(flash_bank_t *)*flash_get_bank_count());
                int i;
-               for (i=0; i<flash_get_bank_count(); i++)
+
+               for (i = 0; i < flash_get_bank_count(); i++)
                {
                        p = get_flash_bank_by_num(i);
                        if (p == NULL)
-                               break;
+                       {
+                               free(banks);
+                               retval = ERROR_FAIL;
+                               gdb_send_error(connection, retval);
+                               return retval;
+                       }
+                       banks[i]=p;
+               }
+
+               qsort(banks, flash_get_bank_count(), sizeof(flash_bank_t *), compare_bank);
+
+               uint32_t ram_start = 0;
+               for (i = 0; i < flash_get_bank_count(); i++)
+               {
+                       p = banks[i];
+
+                       if (ram_start < p->base)
+                       {
+                               xml_printf(&retval, &xml, &pos, &size, "<memory type=\"ram\" start=\"0x%x\" length=\"0x%x\"/>\n",
+                                       ram_start, p->base-ram_start);
+                       }
 
                        /* if device has uneven sector sizes, eg. str7, lpc
                         * we pass the smallest sector size to gdb memory map */
@@ -1553,7 +1722,20 @@ int gdb_query_packet(connection_t *connection, target_t *target, char *packet, i
                                "<property name=\"blocksize\">0x%x</property>\n" \
                                "</memory>\n", \
                                p->base, p->size, blocksize);
+                       ram_start = p->base + p->size;
                }
+               if (ram_start != 0)
+               {
+                       xml_printf(&retval, &xml, &pos, &size, "<memory type=\"ram\" start=\"0x%x\" length=\"0x%x\"/>\n",
+                               ram_start, 0-ram_start);
+               } else
+               {
+                       /* a flash chip could be at the very end of the 32 bit address space, in which case
+                       ram_start will be precisely 0 */
+               }
+
+               free(banks);
+               banks = NULL;
 
                xml_printf(&retval, &xml, &pos, &size, "</memory-map>\n");
 
@@ -1604,7 +1786,7 @@ int gdb_query_packet(connection_t *connection, target_t *target, char *packet, i
                }
 
                xml_printf(&retval, &xml, &pos, &size, \
-                       "l<target version=\"1.0\">\n<architecture>arm</architecture>\n</target>\n");
+                       "l < target version=\"1.0\">\n < architecture > arm</architecture>\n</target>\n");
 
                if (retval != ERROR_OK)
                {
@@ -1617,6 +1799,12 @@ int gdb_query_packet(connection_t *connection, target_t *target, char *packet, i
                free(xml);
                return ERROR_OK;
        }
+       else if (strstr(packet, "QStartNoAckMode"))
+       {
+               gdb_connection->noack_mode = 1;
+               gdb_put_packet(connection, "OK", 2);
+               return ERROR_OK;
+       }
 
        gdb_put_packet(connection, "", 0);
        return ERROR_OK;
@@ -1669,10 +1857,12 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p
                flash_set_dirty();
 
                /* perform any target specific operations before the erase */
-               target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_PROGRAM);
+               target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_FLASH_ERASE_START);
+               result = flash_erase_address_range(gdb_service->target, addr, length);
+               target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_FLASH_ERASE_END);
 
                /* perform erase */
-               if ((result = flash_erase_address_range(gdb_service->target, addr, length)) != ERROR_OK)
+               if (result != ERROR_OK)
                {
                        /* GDB doesn't evaluate the actual error number returned,
                         * treat a failed erase as an I/O error
@@ -1688,6 +1878,7 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p
 
        if (strstr(packet, "vFlashWrite:"))
        {
+               int retval;
                unsigned long addr;
                unsigned long length;
                char *parse = packet + 12;
@@ -1713,7 +1904,10 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p
                }
 
                /* create new section with content from packet buffer */
-               image_add_section(gdb_connection->vflash_image, addr, length, 0x0, (u8*)parse);
+               if ((retval = image_add_section(gdb_connection->vflash_image, addr, length, 0x0, (uint8_t*)parse)) != ERROR_OK)
+               {
+                       return retval;
+               }
 
                gdb_put_packet(connection, "OK", 2);
 
@@ -1722,11 +1916,14 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p
 
        if (!strcmp(packet, "vFlashDone"))
        {
-               u32 written;
+               uint32_t written;
 
                /* process the flashing buffer. No need to erase as GDB
                 * always issues a vFlashErase first. */
-               if ((result = flash_write(gdb_service->target, gdb_connection->vflash_image, &written, 0)) != ERROR_OK)
+               target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_FLASH_WRITE_START);
+               result = flash_write(gdb_service->target, gdb_connection->vflash_image, &written, 0);
+               target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_FLASH_WRITE_END);
+               if (result != ERROR_OK)
                {
                        if (result == ERROR_FLASH_DST_OUT_OF_BANK)
                                gdb_put_packet(connection, "E.memtype", 9);
@@ -1735,7 +1932,7 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p
                        }
                else
                {
-                       LOG_DEBUG("wrote %u bytes from vFlash image to flash", written);
+                       LOG_DEBUG("wrote %u bytes from vFlash image to flash", (unsigned)written);
                        gdb_put_packet(connection, "OK", 2);
                }
 
@@ -1752,30 +1949,14 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p
 
 int gdb_detach(connection_t *connection, target_t *target)
 {
-       switch( detach_mode )
-       {
-               case GDB_DETACH_RESUME:
-                       target_resume(target, 1, 0, 1, 0);
-                       break;
-
-               case GDB_DETACH_RESET:
-                       target_process_reset(connection->cmd_ctx);
-                       break;
-
-               case GDB_DETACH_HALT:
-                       target_halt(target);
-                       break;
-
-               case GDB_DETACH_NOTHING:
-                       break;
-       }
+       gdb_service_t *gdb_service = connection->service->priv;
 
-       gdb_put_packet(connection, "OK", 2);
+       target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_DETACH);
 
-       return ERROR_OK;
+       return gdb_put_packet(connection, "OK", 2);
 }
 
-static void gdb_log_callback(void *priv, const char *file, int line,
+static void gdb_log_callback(void *priv, const char *file, unsigned line,
                const char *function, const char *string)
 {
        connection_t *connection = priv;
@@ -1790,11 +1971,22 @@ static void gdb_log_callback(void *priv, const char *file, int line,
        gdb_output_con(connection, string);
 }
 
+/* Do not allocate this on the stack */
+char gdb_packet_buffer[GDB_BUFFER_SIZE];
+
+static void gdb_sig_halted(connection_t *connection)
+{
+       char sig_reply[4];
+       snprintf(sig_reply, 4, "T%2.2x", 2);
+       gdb_put_packet(connection, sig_reply, 3);
+
+}
+
 int gdb_input_inner(connection_t *connection)
 {
        gdb_service_t *gdb_service = connection->service->priv;
        target_t *target = gdb_service->target;
-       char packet[GDB_BUFFER_SIZE];
+       char *packet = gdb_packet_buffer;
        int packet_size;
        int retval;
        gdb_connection_t *gdb_con = connection->priv;
@@ -1812,7 +2004,20 @@ int gdb_input_inner(connection_t *connection)
                /* terminate with zero */
                packet[packet_size] = 0;
 
-               LOG_DEBUG("received packet: '%s'", packet);
+               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)
                {
@@ -1825,6 +2030,7 @@ int gdb_input_inner(connection_t *connection)
                                        gdb_put_packet(connection, NULL, 0);
                                        break;
                                case 'q':
+                               case 'Q':
                                        retval = gdb_query_packet(connection, target, packet, packet_size);
                                        break;
                                case 'g':
@@ -1855,12 +2061,60 @@ int gdb_input_inner(connection_t *connection)
                                case 'c':
                                case 's':
                                        {
-                                       /* We're running/stepping, in which case we can
-                                        * forward log output until the target is halted */
+                                               int retval = ERROR_OK;
+
                                                gdb_connection_t *gdb_con = connection->priv;
-                                               gdb_con->frontend_state = TARGET_RUNNING;
                                                log_add_callback(gdb_log_callback, connection);
-                                               gdb_step_continue_packet(connection, target, packet, packet_size);
+
+                                               bool nostep = false;
+                                               if (target->state == TARGET_RUNNING)
+                                               {
+                                                       LOG_WARNING("The target is already running. Halt target before stepi/continue.");
+                                                       retval = target_halt(target);
+                                                       if (retval == ERROR_OK)
+                                                               retval = target_wait_state(target, TARGET_HALTED, 100);
+                                               } else if (target->state != TARGET_HALTED)
+                                               {
+                                                       LOG_WARNING("The target is not in the halted nor running stated, stepi/continue ignored.");
+                                                       nostep = true;
+                                               } else if ((packet[0] == 's') && gdb_con->sync)
+                                               {
+                                                       /* Hmm..... when you issue a continue in GDB, then a "stepi" is
+                                                        * sent by GDB first to OpenOCD, thus defeating the check to
+                                                        * make only the single stepping have the sync feature...
+                                                        */
+                                                       nostep = true;
+                                                       LOG_WARNING("stepi ignored. GDB will now fetch the register state from the target.");
+                                               }
+                                               gdb_con->sync = false;
+
+                                               if ((retval!=ERROR_OK) || nostep)
+                                               {
+                                                       /* Either the target isn't in the halted state, then we can't
+                                                        * step/continue. This might be early setup, etc.
+                                                        *
+                                                        * Or we want to allow GDB to pick up a fresh set of
+                                                        * register values without modifying the target state.
+                                                        *
+                                                        */
+                                                       gdb_sig_halted(connection);
+
+                                                       /* stop forwarding log packets! */
+                                                       log_remove_callback(gdb_log_callback, connection);
+                                               } else
+                                               {
+                                                       /* We're running/stepping, in which case we can
+                                                        * forward log output until the target is halted
+                                                        */
+                                                       gdb_con->frontend_state = TARGET_RUNNING;
+                                                       target_call_event_callbacks(target, TARGET_EVENT_GDB_START);
+                                                       int retval = gdb_step_continue_packet(connection, target, packet, packet_size);
+                                                       if (retval != ERROR_OK)
+                                                       {
+                                                               /* we'll never receive a halted condition... issue a false one.. */
+                                                               gdb_frontend_halted(target, connection);
+                                                       }
+                                               }
                                        }
                                        break;
                                case 'v':
@@ -1886,7 +2140,11 @@ int gdb_input_inner(connection_t *connection)
                                        break;
                                case 'R':
                                        /* handle extended restart packet */
-                                       target_process_reset(connection->cmd_ctx);
+                                       breakpoint_clear_target(gdb_service->target);
+                                       watchpoint_clear_target(gdb_service->target);
+                                       command_run_linef(connection->cmd_ctx,
+                                                       "ocd_gdb_restart %s",
+                                                       target->cmd_name);
                                        break;
                                default:
                                        /* ignore unkown packets */
@@ -1904,8 +2162,16 @@ int gdb_input_inner(connection_t *connection)
                {
                        if (target->state == TARGET_RUNNING)
                        {
-                               target_halt(target);
+                               retval = target_halt(target);
+                               if (retval != ERROR_OK)
+                               {
+                                       target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT);
+                               }
                                gdb_con->ctrl_c = 0;
+                       } else
+                       {
+                               LOG_INFO("The target is not running when halt was requested, stopping GDB.");
+                               target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT);
                        }
                }
 
@@ -1921,19 +2187,18 @@ int gdb_input(connection_t *connection)
        if (retval == ERROR_SERVER_REMOTE_CLOSED)
                return retval;
 
-       /* logging does not propagate the error, yet can set th gdb_con->closed flag */
+       /* logging does not propagate the error, yet can set the gdb_con->closed flag */
        if (gdb_con->closed)
                return ERROR_SERVER_REMOTE_CLOSED;
-       
+
        /* we'll recover from any other errors(e.g. temporary timeouts, etc.) */
        return ERROR_OK;
 }
 
-int gdb_init()
+int gdb_init(void)
 {
        gdb_service_t *gdb_service;
-       target_t *target = targets;
-       int i = 0;
+       target_t *target = all_targets;
 
        if (!target)
        {
@@ -1941,76 +2206,75 @@ int gdb_init()
                return ERROR_OK;
        }
 
-       if (gdb_port == 0)
+       if (gdb_port == 0 && server_use_pipes == 0)
        {
-               LOG_WARNING("no gdb port specified, using default port 3333");
-               gdb_port = 3333;
+               LOG_INFO("gdb port disabled");
+               return ERROR_OK;
        }
 
-       while (target)
+       if (server_use_pipes)
        {
-               char service_name[8];
-
-               snprintf(service_name, 8, "gdb-%2.2i", i);
+               /* only a single gdb connection when using a pipe */
 
                gdb_service = malloc(sizeof(gdb_service_t));
                gdb_service->target = target;
 
-               add_service("gdb", CONNECTION_GDB, gdb_port + i, 1, gdb_new_connection, gdb_input, gdb_connection_closed, gdb_service);
+               add_service("gdb", CONNECTION_PIPE, 0, 1, gdb_new_connection, gdb_input, gdb_connection_closed, gdb_service);
 
-               LOG_DEBUG("gdb service for target %s at port %i", target->type->name, gdb_port + i);
+               LOG_DEBUG("gdb service for target %s using pipes",
+                               target_get_name(target));
+       }
+       else
+       {
+               unsigned short port = gdb_port;
 
-               i++;
-               target = target->next;
+               while (target)
+               {
+                       gdb_service = malloc(sizeof(gdb_service_t));
+                       gdb_service->target = target;
+
+                       add_service("gdb", CONNECTION_TCP,
+                                       port, 1,
+                                       gdb_new_connection, gdb_input,
+                                       gdb_connection_closed, gdb_service);
+
+                       LOG_DEBUG("gdb service for target %s at TCP port %i",
+                                       target_get_name(target),
+                                       port);
+                       target = target->next;
+                       port++;
+               }
        }
 
        return ERROR_OK;
 }
 
-/* daemon configuration command gdb_port */
-int handle_gdb_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+COMMAND_HANDLER(handle_gdb_sync_command)
 {
-       if (argc == 0)
-               return ERROR_OK;
+       if (argc != 0)
+       {
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       if (current_gdb_connection == NULL)
+       {
+               command_print(cmd_ctx,
+                               "gdb_sync command can only be run from within gdb using \"monitor gdb_sync\"");
+               return ERROR_FAIL;
+       }
 
-       /* only if the port wasn't overwritten by cmdline */
-       if (gdb_port == 0)
-               gdb_port = strtoul(args[0], NULL, 0);
+       current_gdb_connection->sync = true;
 
        return ERROR_OK;
 }
 
-int handle_gdb_detach_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+/* daemon configuration command gdb_port */
+COMMAND_HANDLER(handle_gdb_port_command)
 {
-       if (argc == 1)
-       {
-               if (strcmp(args[0], "resume") == 0)
-               {
-                       detach_mode = GDB_DETACH_RESUME;
-                       return ERROR_OK;
-               }
-               else if (strcmp(args[0], "reset") == 0)
-               {
-                       detach_mode = GDB_DETACH_RESET;
-                       return ERROR_OK;
-               }
-               else if (strcmp(args[0], "halt") == 0)
-               {
-                       detach_mode = GDB_DETACH_HALT;
-                       return ERROR_OK;
-               }
-               else if (strcmp(args[0], "nothing") == 0)
-               {
-                       detach_mode = GDB_DETACH_NOTHING;
-                       return ERROR_OK;
-               }
-       }
-
-       LOG_WARNING("invalid gdb_detach configuration directive: %s", args[0]);
-       return ERROR_OK;
+       return server_port_command(cmd_ctx, cmd, args, argc, &gdb_port);
 }
 
-int handle_gdb_memory_map_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+COMMAND_HANDLER(handle_gdb_memory_map_command)
 {
        if (argc == 1)
        {
@@ -2024,13 +2288,14 @@ int handle_gdb_memory_map_command(struct command_context_s *cmd_ctx, char *cmd,
                        gdb_use_memory_map = 0;
                        return ERROR_OK;
                }
+               else
+                       LOG_WARNING("invalid gdb_memory_map configuration directive %s", args[0]);
        }
 
-       LOG_WARNING("invalid gdb_memory_map configuration directive: %s", args[0]);
-       return ERROR_OK;
+       return ERROR_COMMAND_SYNTAX_ERROR;
 }
 
-int handle_gdb_flash_program_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+COMMAND_HANDLER(handle_gdb_flash_program_command)
 {
        if (argc == 1)
        {
@@ -2044,13 +2309,14 @@ int handle_gdb_flash_program_command(struct command_context_s *cmd_ctx, char *cm
                        gdb_flash_program = 0;
                        return ERROR_OK;
                }
+               else
+                       LOG_WARNING("invalid gdb_flash_program configuration directive: %s", args[0]);
        }
 
-       LOG_WARNING("invalid gdb_memory_map configuration directive: %s", args[0]);
-       return ERROR_OK;
+       return ERROR_COMMAND_SYNTAX_ERROR;
 }
 
-int handle_gdb_report_data_abort_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+COMMAND_HANDLER(handle_gdb_report_data_abort_command)
 {
        if (argc == 1)
        {
@@ -2064,23 +2330,68 @@ int handle_gdb_report_data_abort_command(struct command_context_s *cmd_ctx, char
                        gdb_report_data_abort = 0;
                        return ERROR_OK;
                }
+               else
+                       LOG_WARNING("invalid gdb_report_data_abort configuration directive: %s", args[0]);
+       }
+
+       return ERROR_COMMAND_SYNTAX_ERROR;
+}
+
+/* gdb_breakpoint_override */
+COMMAND_HANDLER(handle_gdb_breakpoint_override_command)
+{
+       if (argc == 0)
+       {
+
+       } else if (argc == 1)
+       {
+               gdb_breakpoint_override = 1;
+               if (strcmp(args[0], "hard") == 0)
+               {
+                       gdb_breakpoint_override_type = BKPT_HARD;
+               } else if (strcmp(args[0], "soft") == 0)
+               {
+                       gdb_breakpoint_override_type = BKPT_SOFT;
+               } else if (strcmp(args[0], "disable") == 0)
+               {
+                       gdb_breakpoint_override = 0;
+               }
+       } else
+       {
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+       if (gdb_breakpoint_override)
+       {
+               LOG_USER("force %s breakpoints", (gdb_breakpoint_override_type == BKPT_HARD)?"hard":"soft");
+       } else
+       {
+               LOG_USER("breakpoint type is not overriden");
        }
 
-       LOG_WARNING("invalid gdb_report_data_abort configuration directive: %s", args[0]);
        return ERROR_OK;
 }
 
 int gdb_register_commands(command_context_t *command_context)
 {
-       register_command(command_context, NULL, "gdb_port", handle_gdb_port_command,
-                       COMMAND_CONFIG, "");
-       register_command(command_context, NULL, "gdb_detach", handle_gdb_detach_command,
-                       COMMAND_CONFIG, "");
-       register_command(command_context, NULL, "gdb_memory_map", handle_gdb_memory_map_command,
-                       COMMAND_CONFIG, "");
-       register_command(command_context, NULL, "gdb_flash_program", handle_gdb_flash_program_command,
-                       COMMAND_CONFIG, "");
-       register_command(command_context, NULL, "gdb_report_data_abort", handle_gdb_report_data_abort_command,
-                       COMMAND_CONFIG, "");
+       register_command(command_context, NULL, "gdb_sync",
+                       handle_gdb_sync_command, COMMAND_ANY,
+                       "next stepi will return immediately allowing GDB to "
+                       "fetch register state without affecting target state");
+       register_command(command_context, NULL, "gdb_port",
+                       handle_gdb_port_command, COMMAND_ANY,
+                       "daemon configuration command gdb_port");
+       register_command(command_context, NULL, "gdb_memory_map",
+                       handle_gdb_memory_map_command, COMMAND_CONFIG,
+                       "enable or disable memory map");
+       register_command(command_context, NULL, "gdb_flash_program",
+                       handle_gdb_flash_program_command, COMMAND_CONFIG,
+                       "enable or disable flash program");
+       register_command(command_context, NULL, "gdb_report_data_abort",
+                       handle_gdb_report_data_abort_command, COMMAND_CONFIG,
+                       "enable or disable reporting data aborts");
+       register_command(command_context, NULL, "gdb_breakpoint_override",
+                       handle_gdb_breakpoint_override_command, COMMAND_EXEC,
+                       "hard/soft/disable - force type of breakpoint "
+                       "used by gdb 'break' commands.");
        return ERROR_OK;
 }

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)