- added a test document as a starting point
[openocd.git] / src / server / gdb_server.c
index 42f04680b6acb57b1b4dba4cc9088aa67647149b..71f82dd456d548683d23a8a62560a0fb77d95b20 100644 (file)
@@ -32,6 +32,7 @@
 #include "breakpoints.h"
 #include "flash.h"
 #include "target_request.h"
+#include "configuration.h"
 
 #include <string.h>
 #include <errno.h>
 #endif
 
 static unsigned short gdb_port;
+static const char *DIGITS = "0123456789abcdef";
+
+static void gdb_log_callback(void *priv, const char *file, int line, 
+               const char *function, const char *format, va_list args);
 
 enum gdb_detach_mode
 {
@@ -60,6 +65,10 @@ enum gdb_detach_mode detach_mode = GDB_DETACH_RESUME;
 int gdb_use_memory_map = 0;
 int gdb_flash_program = 0;
 
+/* 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 */
+int gdb_report_data_abort = 0;
+
 int gdb_last_signal(target_t *target)
 {
        switch (target->debug_reason)
@@ -103,10 +112,38 @@ int gdb_get_char(connection_t *connection, int* next_char)
                return ERROR_OK;
        }
 
-       while ((gdb_con->buf_cnt = read_socket(connection->fd, gdb_con->buffer, GDB_BUFFER_SIZE)) <= 0)
+       for (;;)
        {
+#ifndef _WIN32
+               /* a non-blocking socket will block if there is 0 bytes available on the socket,
+                * but return with as many bytes as are available immediately
+                */
+               struct timeval tv;
+               fd_set read_fds;
+               
+               FD_ZERO(&read_fds);
+               FD_SET(connection->fd, &read_fds);
+               
+               tv.tv_sec = 1;
+               tv.tv_usec = 0;
+               if (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
+                        */
+                       return ERROR_GDB_TIMEOUT; 
+               }
+#endif
+               gdb_con->buf_cnt = read_socket(connection->fd, gdb_con->buffer, GDB_BUFFER_SIZE);
+               if (gdb_con->buf_cnt > 0)
+               {
+                       break;
+               }
                if (gdb_con->buf_cnt == 0)
+               {
+                       gdb_con->closed = 1;
                        return ERROR_SERVER_REMOTE_CLOSED;
+               }
 
 #ifdef _WIN32
                errno = WSAGetLastError();
@@ -136,7 +173,7 @@ int gdb_get_char(connection_t *connection, int* next_char)
                                return ERROR_SERVER_REMOTE_CLOSED;
                        default:
                                ERROR("read: %s", strerror(errno));
-                               exit(-1);
+                               return ERROR_SERVER_REMOTE_CLOSED;
                }
 #endif
        }
@@ -180,12 +217,30 @@ int gdb_putback_char(connection_t *connection, int last_char)
        return ERROR_OK;
 }
 
-int gdb_put_packet(connection_t *connection, char *buffer, int len)
+/* The only way we can detect that the socket is closed is the first time
+ * we write to it, we will fail. Subsequent write operations will
+ * succeed. Shudder! */
+int gdb_write(connection_t *connection, void *data, int len)
+{
+       gdb_connection_t *gdb_con = connection->priv;
+       if (gdb_con->closed)
+               return ERROR_SERVER_REMOTE_CLOSED;
+
+       if (write_socket(connection->fd, data, len) == len)
+       {
+               return ERROR_OK;
+       }
+       gdb_con->closed = 1;
+       return ERROR_SERVER_REMOTE_CLOSED;
+}
+
+int gdb_put_packet_inner(connection_t *connection, char *buffer, int len)
 {
        int i;
        unsigned char my_checksum = 0;
-       char checksum[3];
+#ifdef _DEBUG_GDB_IO_
        char *debug_buffer;
+#endif
        int reply;
        int retval;
        gdb_connection_t *gdb_con = connection->priv;
@@ -195,28 +250,62 @@ int gdb_put_packet(connection_t *connection, char *buffer, int len)
 
        while (1)
        {
+#ifdef _DEBUG_GDB_IO_
                debug_buffer = malloc(len + 1);
                memcpy(debug_buffer, buffer, len);
                debug_buffer[len] = 0;
                DEBUG("sending packet '$%s#%2.2x'", debug_buffer, my_checksum);
                free(debug_buffer);
-
-               write_socket(connection->fd, "$", 1);
+#endif
+#if 0
+               char checksum[3];
+               gdb_write(connection, "$", 1);
                if (len > 0)
-                       write_socket(connection->fd, buffer, len);
-               write_socket(connection->fd, "#", 1);
-
+                       gdb_write(connection, buffer, len);
+               gdb_write(connection, "#", 1);
+               
                snprintf(checksum, 3, "%2.2x", my_checksum);
-
-               write_socket(connection->fd, checksum, 2);
-
+               
+               gdb_write(connection, checksum, 2);
+#else
+               void *allocated = NULL;
+               char stackAlloc[1024];
+               char *t = stackAlloc;
+               int totalLen = 1 + len + 1 + 2;
+               if (totalLen > sizeof(stackAlloc))
+               {
+                       allocated = malloc(totalLen);
+                       t = allocated;
+                       if (allocated == NULL)
+                       {
+                               ERROR("Ran out of memory trying to reply packet %d\n", totalLen);
+                               exit(-1);
+                       }
+               }
+               t[0] = '$';
+               memcpy(t + 1, buffer, len);
+               t[1 + len] = '#';
+               t[1 + len + 1] = DIGITS[(my_checksum >> 4) & 0xf];
+               t[1 + len + 2] = DIGITS[my_checksum & 0xf];
+               
+               gdb_write(connection, t, totalLen);
+               
+               if (allocated)
+               {
+                       free(allocated);
+               }
+#endif
                if ((retval = gdb_get_char(connection, &reply)) != ERROR_OK)
                        return retval;
 
                if (reply == '+')
                        break;
                else if (reply == '-')
+               {
+                       /* Stop sending output packets for now */
+                       log_setCallback(NULL, NULL);
                        WARNING("negative reply, retrying");
+               }
                else if (reply == 0x3)
                {
                        gdb_con->ctrl_c = 1;
@@ -225,7 +314,11 @@ int gdb_put_packet(connection_t *connection, char *buffer, int len)
                        if (reply == '+')
                                break;
                        else if (reply == '-')
+                       {
+                               /* Stop sending output packets for now */
+                               log_setCallback(NULL, NULL);
                                WARNING("negative reply, retrying");
+                       }
                        else
                        {
                                ERROR("unknown character 0x%2.2x in reply, dropping connection", reply);
@@ -238,11 +331,22 @@ int gdb_put_packet(connection_t *connection, char *buffer, int len)
                        return ERROR_SERVER_REMOTE_CLOSED;
                }
        }
+       if (gdb_con->closed)
+               return ERROR_SERVER_REMOTE_CLOSED;
 
        return ERROR_OK;
 }
 
-int gdb_get_packet(connection_t *connection, char *buffer, int *len)
+int gdb_put_packet(connection_t *connection, char *buffer, int len)
+{
+       gdb_connection_t *gdb_con = connection->priv;
+       gdb_con->busy = 1;
+       int retval = gdb_put_packet_inner(connection, buffer, len);
+       gdb_con->busy = 0;
+       return retval;
+}
+
+int gdb_get_packet_inner(connection_t *connection, char *buffer, int *len)
 {
        int character;
        int count = 0;
@@ -283,13 +387,70 @@ int gdb_get_packet(connection_t *connection, char *buffer, int *len)
                } while (character != '$');
 
                my_checksum = 0;
-
-               do
+               
+               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)
+                       {
+                               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 == '#')
+                               break;
 
                        if (character == '}')
                        {
@@ -307,12 +468,7 @@ int gdb_get_packet(connection_t *connection, char *buffer, int *len)
                                buffer[count++] = character & 0xff;
                        }
 
-                       if (count > *len)
-                       {
-                               ERROR("packet buffer too small");
-                               return ERROR_GDB_BUFFER_TOO_SMALL;
-                       }
-               } while (1);
+               }
 
                *len = count;
 
@@ -326,31 +482,33 @@ int gdb_get_packet(connection_t *connection, char *buffer, int *len)
 
                if (my_checksum == strtoul(checksum, NULL, 16))
                {
-                       write_socket(connection->fd, "+", 1);
+                       gdb_write(connection, "+", 1);
                        break;
                }
 
                WARNING("checksum error, requesting retransmission");
-               write_socket(connection->fd, "-", 1);
+               gdb_write(connection, "-", 1);
        }
+       if (gdb_con->closed)
+               return ERROR_SERVER_REMOTE_CLOSED;
 
        return ERROR_OK;
 }
 
-int gdb_output(struct command_context_s *context, char* line)
+int gdb_get_packet(connection_t *connection, char *buffer, int *len)
 {
-       connection_t *connection = context->output_handler_priv;
-       gdb_connection_t *gdb_connection = connection->priv;
+       gdb_connection_t *gdb_con = connection->priv;
+       gdb_con->busy = 1;
+       int retval = gdb_get_packet_inner(connection, buffer, len);
+       gdb_con->busy = 0;
+       return retval;
+}
        
+int gdb_output_con(connection_t *connection, char* line)
+{
        char *hex_buffer;
        int i, bin_size;
 
-       /* check if output is enabled */
-       if (gdb_connection->output_disable)
-       {
-               return ERROR_OK;
-       }
-       
        bin_size = strlen(line);
 
        hex_buffer = malloc(bin_size*2 + 4);
@@ -368,6 +526,13 @@ int gdb_output(struct command_context_s *context, char* line)
        return ERROR_OK;
 }
 
+int gdb_output(struct command_context_s *context, char* line)
+{
+       /* this will be dumped to the log and also sent as an O packet if possible */
+       USER(line); 
+       return ERROR_OK;
+}
+
 int gdb_program_handler(struct target_s *target, enum target_event event, void *priv)
 {
        FILE *script;
@@ -375,7 +540,7 @@ int gdb_program_handler(struct target_s *target, enum target_event event, void *
        
        if (target->gdb_program_script)
        {
-               script = fopen(target->gdb_program_script, "r");
+               script = open_file_from_path(cmd_ctx, target->gdb_program_script, "r");
                if (!script)
                {
                        ERROR("couldn't open script file %s", target->gdb_program_script);
@@ -402,8 +567,20 @@ int gdb_target_callback_event_handler(struct target_s *target, enum target_event
        switch (event)
        {
                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)
                        {
+                               /* stop forwarding log packets! */
+                               log_setCallback(NULL, NULL);
+                               
                                if (gdb_connection->ctrl_c)
                                {
                                        signal = 0x2;
@@ -419,12 +596,6 @@ int gdb_target_callback_event_handler(struct target_s *target, enum target_event
                                gdb_connection->frontend_state = TARGET_HALTED;
                        }
                        break;
-               case TARGET_EVENT_RESUMED:
-                       if (gdb_connection->frontend_state == TARGET_HALTED)
-                       {
-                               gdb_connection->frontend_state = TARGET_RUNNING;
-                       }
-                       break;
                case TARGET_EVENT_GDB_PROGRAM:
                        gdb_program_handler(target, event, connection->cmd_ctx);
                        break;
@@ -450,7 +621,8 @@ int gdb_new_connection(connection_t *connection)
        gdb_connection->ctrl_c = 0;
        gdb_connection->frontend_state = TARGET_HALTED;
        gdb_connection->vflash_image = NULL;
-       gdb_connection->output_disable = 0;
+       gdb_connection->closed = 0;
+       gdb_connection->busy = 0;
        
        /* output goes through gdb connection */
        command_set_output_handler(connection->cmd_ctx, gdb_output, connection);
@@ -462,14 +634,12 @@ int gdb_new_connection(connection_t *connection)
        if (((retval = gdb_service->target->type->halt(gdb_service->target)) != ERROR_OK) &&
                        (retval != ERROR_TARGET_ALREADY_HALTED))
        {
-               ERROR("error when trying to halt target");
-               exit(-1);
+               ERROR("error(%d) when trying to halt target, falling back to \"reset halt\"", retval);
+               command_run_line(connection->cmd_ctx, "reset halt");
        }
 
-       while (gdb_service->target->state != TARGET_HALTED)
-       {
-               gdb_service->target->type->poll(gdb_service->target);
-       }
+       /* This will time out after 1 second */
+       command_run_line(connection->cmd_ctx, "wait_halt 1");
 
        /* remove the initial ACK from the incoming buffer */
        if ((retval = gdb_get_char(connection, &initial_ack)) != ERROR_OK)
@@ -532,30 +702,32 @@ int gdb_last_signal_packet(connection_t *connection, target_t *target, char* pac
        return ERROR_OK;
 }
 
-void gdb_str_to_target(target_t *target, char *str, char *tstr)
+/* Convert register to string of bits. NB! The # of bits in the
+ * register might be non-divisible by 8(a byte), in which
+ * case an entire byte is shown. */
+void gdb_str_to_target(target_t *target, char *tstr, reg_t *reg)
 {
-       int str_len = strlen(str);
        int i;
 
-       if (str_len % 2)
-       {
-               ERROR("BUG: gdb value with uneven number of characters encountered: %s", str);
-               exit(-1);
-       }
+       u8 *buf;
+       int buf_len;
+       buf = reg->value;
+       buf_len = CEIL(reg->size, 8); 
 
        if (target->endianness == TARGET_LITTLE_ENDIAN)
        {
-               for (i = 0; i < str_len; i+=2)
+               for (i = 0; i < buf_len; i++)
                {
-                       tstr[str_len - i - 1] = str[i + 1];
-                       tstr[str_len - i - 2] = str[i];
+                       tstr[i*2]   = DIGITS[(buf[i]>>4) & 0xf];
+                       tstr[i*2+1] = DIGITS[buf[i]&0xf];
                }
        }
        else
        {
-               for (i = 0; i < str_len; i++)
+               for (i = 0; i < buf_len; i++)
                {
-                       tstr[i] = str[i];
+                       tstr[(buf_len-1-i)*2]   = DIGITS[(buf[i]>>4)&0xf];
+                       tstr[(buf_len-1-i)*2+1] = DIGITS[buf[i]&0xf];
                }
        }       
 }
@@ -598,7 +770,9 @@ int gdb_get_registers_packet(connection_t *connection, target_t *target, char* p
        char *reg_packet_p;
        int i;
 
+#ifdef _DEBUG_GDB_IO_
        DEBUG("-");
+#endif
 
        if ((retval = target->type->get_gdb_reg_list(target, &reg_list, &reg_list_size)) != ERROR_OK)
        {
@@ -624,16 +798,18 @@ int gdb_get_registers_packet(connection_t *connection, target_t *target, char* p
 
        for (i = 0; i < reg_list_size; i++)
        {
-               char *hex_buf = buf_to_str(reg_list[i]->value, reg_list[i]->size, 16);
-               DEBUG("hex_buf: %s", hex_buf);
-               gdb_str_to_target(target, hex_buf, reg_packet_p);
+               gdb_str_to_target(target, reg_packet_p, reg_list[i]);
                reg_packet_p += CEIL(reg_list[i]->size, 8) * 2;
-               free(hex_buf);
        }
 
-       reg_packet_p = strndup(reg_packet, CEIL(reg_packet_size, 8) * 2);
-       DEBUG("reg_packet: %s", reg_packet_p);
-       free(reg_packet_p);
+#ifdef _DEBUG_GDB_IO_
+       {
+               char *reg_packet_p;
+               reg_packet_p = strndup(reg_packet, CEIL(reg_packet_size, 8) * 2);
+               DEBUG("reg_packet: %s", reg_packet_p);
+               free(reg_packet_p);
+       }
+#endif
 
        gdb_put_packet(connection, reg_packet, CEIL(reg_packet_size, 8) * 2);
        free(reg_packet);
@@ -651,7 +827,9 @@ int gdb_set_registers_packet(connection_t *connection, target_t *target, char *p
        int retval;
        char *packet_p;
 
+#ifdef _DEBUG_GDB_IO_
        DEBUG("-");
+#endif
 
        /* skip command character */
        packet++;
@@ -723,9 +901,10 @@ int gdb_get_register_packet(connection_t *connection, target_t *target, char *pa
        reg_t **reg_list;
        int reg_list_size;
        int retval;
-       char *hex_buf;
 
+#ifdef _DEBUG_GDB_IO_
        DEBUG("-");
+#endif
 
        if ((retval = target->type->get_gdb_reg_list(target, &reg_list, &reg_list_size)) != ERROR_OK)
        {
@@ -749,15 +928,12 @@ int gdb_get_register_packet(connection_t *connection, target_t *target, char *pa
 
        reg_packet = malloc(CEIL(reg_list[reg_num]->size, 8) * 2);
 
-       hex_buf = buf_to_str(reg_list[reg_num]->value, reg_list[reg_num]->size, 16);
-
-       gdb_str_to_target(target, hex_buf, reg_packet);
+       gdb_str_to_target(target, reg_packet, reg_list[reg_num]);
 
        gdb_put_packet(connection, reg_packet, CEIL(reg_list[reg_num]->size, 8) * 2);
 
        free(reg_list);
        free(reg_packet);
-       free(hex_buf);
 
        return ERROR_OK;
 }
@@ -834,7 +1010,6 @@ int gdb_memory_packet_error(connection_t *connection, int retval)
                case ERROR_TARGET_NOT_HALTED:
                        ERROR("gdb tried to read memory but we're not halted, dropping connection");
                        return ERROR_SERVER_REMOTE_CLOSED;
-                       break;
                case ERROR_TARGET_DATA_ABORT:
                        gdb_send_error(connection, EIO);
                        break;
@@ -845,13 +1020,19 @@ int gdb_memory_packet_error(connection_t *connection, int retval)
                        gdb_send_error(connection, EFAULT);
                        break;
                default:
-                       ERROR("BUG: unexpected error %i", retval);
-                       exit(-1);
+                       /* This could be that the target reset itself. */
+                       ERROR("unexpected error %i. Dropping connection.", retval);
+                       return ERROR_SERVER_REMOTE_CLOSED;
        }
 
        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.
+ * 
+ * 8191 bytes by the looks of it. Why 8191 bytes instead of 8192?????
+ */
 int gdb_read_memory_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
 {
        char *separator;
@@ -861,8 +1042,7 @@ int gdb_read_memory_packet(connection_t *connection, target_t *target, char *pac
        u8 *buffer;
        char *hex_buffer;
 
-       int i;
-       int retval;
+       int retval = ERROR_OK;
 
        /* skip command character */
        packet++;
@@ -881,33 +1061,37 @@ int gdb_read_memory_packet(connection_t *connection, target_t *target, char *pac
 
        DEBUG("addr: 0x%8.8x, len: 0x%8.8x", addr, len);
 
-       switch (len)
+       retval = target_read_buffer(target, addr, len, buffer);
+
+       if ((retval == ERROR_TARGET_DATA_ABORT) && (!gdb_report_data_abort))
        {
-               case 4:
-                       if ((addr % 4) == 0)
-                               retval = target->type->read_memory(target, addr, 4, 1, buffer);
-                       else
-                               retval = target->type->read_memory(target, addr, 1, len, buffer);
-                       break;
-               case 2:
-                       if ((addr % 2) == 0)
-                               retval = target->type->read_memory(target, addr, 2, 1, buffer);
-                       else
-                               retval = target->type->read_memory(target, addr, 1, len, buffer);
-                       break;
-               default:
-                       if (((addr % 4) == 0) && ((len % 4) == 0))
-                               retval = target->type->read_memory(target, addr, 4, len / 4, buffer);
-                       else
-                               retval = target->type->read_memory(target, addr, 1, len, buffer);
+               /* 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.
+                * 
+                * OpenOCD developers are acutely aware of this problem, but there is nothing
+                * 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
+                *
+                * 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.
+                */
+               memset(buffer, 0, len);
+               retval = ERROR_OK;
        }
 
        if (retval == ERROR_OK)
        {
                hex_buffer = malloc(len * 2 + 1);
 
-               for (i=0; i<len; i++)
-                       snprintf(hex_buffer + 2*i, 3, "%2.2x", buffer[i]);
+               int i;
+               for (i = 0; i < len; i++)
+               {
+                       u8 t = buffer[i];
+                       hex_buffer[2 * i] = DIGITS[(t >> 4) & 0xf];
+                       hex_buffer[2 * i + 1] = DIGITS[t & 0xf];
+               }
 
                gdb_put_packet(connection, hex_buffer, len * 2);
 
@@ -915,13 +1099,12 @@ int gdb_read_memory_packet(connection_t *connection, target_t *target, char *pac
        }
        else
        {
-               if ((retval = gdb_memory_packet_error(connection, retval)) != ERROR_OK)
-                       return retval; 
+               retval = gdb_memory_packet_error(connection, retval);
        }
 
        free(buffer);
 
-       return ERROR_OK;
+       return retval;
 }
 
 int gdb_write_memory_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
@@ -965,31 +1148,7 @@ int gdb_write_memory_packet(connection_t *connection, target_t *target, char *pa
                buffer[i] = tmp;
        }
 
-       retval = ERROR_OK;
-       switch (len)
-       {
-               /* handle sized writes */
-               case 4:
-                       if ((addr % 4) == 0)
-                               retval = target->type->write_memory(target, addr, 4, 1, buffer);
-                       else
-                               retval = target->type->write_memory(target, addr, 1, len, buffer);
-                       break;
-               case 2:
-                       if ((addr % 2) == 0)
-                               retval = target->type->write_memory(target, addr, 2, 1, buffer);
-                       else
-                               retval = target->type->write_memory(target, addr, 1, len, buffer);
-                       break;
-               case 3:
-               case 1:
-                       retval = target->type->write_memory(target, addr, 1, len, buffer);
-                       break;
-                       /* handle bulk writes */
-               default:
-                       retval = target_write_buffer(target, addr, len, buffer);
-                       break;
-       }
+       retval = target_write_buffer(target, addr, len, buffer);
 
        if (retval == ERROR_OK)
        {
@@ -1012,7 +1171,6 @@ int gdb_write_memory_binary_packet(connection_t *connection, target_t *target, c
        u32 addr = 0;
        u32 len = 0;
 
-       u8 *buffer;
        int retval;
 
        /* skip command character */
@@ -1035,38 +1193,11 @@ int gdb_write_memory_binary_packet(connection_t *connection, target_t *target, c
        }
 
        retval = ERROR_OK;
-       if( len ) {
-
-               buffer = malloc(len);
-
+       if (len)
+       {
                DEBUG("addr: 0x%8.8x, len: 0x%8.8x", addr, len);
 
-               memcpy( buffer, separator, len );
-
-               switch (len)
-               {
-                       case 4:
-                               if ((addr % 4) == 0)
-                                       retval = target->type->write_memory(target, addr, 4, 1, buffer);
-                               else
-                                       retval = target->type->write_memory(target, addr, 1, len, buffer);
-                               break;
-                       case 2:
-                               if ((addr % 2) == 0)
-                                       retval = target->type->write_memory(target, addr, 2, 1, buffer);
-                               else
-                                       retval = target->type->write_memory(target, addr, 1, len, buffer);
-                               break;
-                       case 3:
-                       case 1:
-                               retval = target->type->write_memory(target, addr, 1, len, buffer);
-                               break;
-                       default:
-                               retval = target_write_buffer(target, addr, len, buffer);
-                               break;
-               }
-
-               free(buffer);
+               retval = target_write_buffer(target, addr, len, (u8*)separator);
        }
 
        if (retval == ERROR_OK)
@@ -1240,13 +1371,13 @@ void xml_printf(int *retval, char **xml, int *pos, int *size, const char *fmt, .
                         * Need minimum 2 bytes to fit 1 char and 0 terminator. */
                         
                        *size = *size * 2 + 2;
-                       char *t=*xml;
+                       char *t = *xml;
                        *xml = realloc(*xml, *size);
                        if (*xml == NULL)
                        {
                                if (t)
                                        free(t);
-                               *retval=ERROR_SERVER_REMOTE_CLOSED;
+                               *retval = ERROR_SERVER_REMOTE_CLOSED;
                                return;
                        }
                }
@@ -1266,7 +1397,7 @@ void xml_printf(int *retval, char **xml, int *pos, int *size, const char *fmt, .
        }
 }
 
-static int decode_xfer_read (char *buf, char **annex, int *ofs, unsigned int *len)
+static int decode_xfer_read(char *buf, char **annex, int *ofs, unsigned int *len)
 {
        char *separator;
        
@@ -1291,6 +1422,22 @@ static int decode_xfer_read (char *buf, char **annex, int *ofs, unsigned int *le
        return 0;
 }
 
+int gdb_calc_blocksize(flash_bank_t *bank)
+{
+       int i;
+       int block_size = 0xffffffff;
+       
+       /* loop through all sectors and return smallest sector size */
+       
+       for (i = 0; i < bank->num_sectors; i++)
+       {
+               if (bank->sectors[i].size < block_size)
+                       block_size = bank->sectors[i].size;
+       }
+       
+       return block_size;
+}
+
 int gdb_query_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
 {
        command_context_t *cmd_ctx = connection->cmd_ctx;
@@ -1309,6 +1456,10 @@ int gdb_query_packet(connection_t *connection, target_t *target, char *packet, i
                                cmd[i] = tmp;
                        }
                        cmd[(packet_size - 6)/2] = 0x0;
+                       
+                       /* We want to print all debug output to GDB connection */
+                       log_setCallback(gdb_log_callback, connection);
+                       target_call_timer_callbacks();
                        command_run_line(cmd_ctx, cmd);
                        free(cmd);
                }
@@ -1320,7 +1471,7 @@ int gdb_query_packet(connection_t *connection, target_t *target, char *packet, i
                if (packet_size > 5)
                {
                        int retval;
-                       u8 gdb_reply[10];
+                       char gdb_reply[10];
                        char *separator;
                        u32 checksum;
                        u32 addr = 0;
@@ -1363,15 +1514,21 @@ int gdb_query_packet(connection_t *connection, target_t *target, char *packet, i
                char *buffer = NULL;
                int pos = 0;
                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 ? '+' : '-');
-               if (buffer!=NULL)
+               
+               if (retval != ERROR_OK)
                {
-                       gdb_put_packet(connection, buffer, strlen(buffer));
-                       free(buffer);
+                       gdb_send_error(connection, 01);
+                       return ERROR_OK;
                }
-               return retval;
+               
+               gdb_put_packet(connection, buffer, strlen(buffer));
+               free(buffer);
+               
+               return ERROR_OK;
        }
        else if (strstr(packet, "qXfer:memory-map:read::"))
        {
@@ -1390,6 +1547,7 @@ int gdb_query_packet(connection_t *connection, target_t *target, char *packet, i
                int offset;
                int length;
                char *separator;
+               int blocksize;
                
                /* skip command character */
                packet += 23;
@@ -1406,10 +1564,14 @@ int gdb_query_packet(connection_t *connection, target_t *target, char *packet, i
                        if (p == NULL)
                                break;
                        
+                       /* if device has uneven sector sizes, eg. str7, lpc
+                        * we pass the smallest sector size to gdb memory map */
+                       blocksize = gdb_calc_blocksize(p);
+                       
                        xml_printf(&retval, &xml, &pos, &size, "<memory type=\"flash\" start=\"0x%x\" length=\"0x%x\">\n" \
                                "<property name=\"blocksize\">0x%x</property>\n" \
                                "</memory>\n", \
-                               p->base, p->size, p->size/p->num_sectors);
+                               p->base, p->size, blocksize);
                        i++;
                }
                
@@ -1443,13 +1605,13 @@ int gdb_query_packet(connection_t *connection, target_t *target, char *packet, i
                int retval = ERROR_OK;
                
                int offset;
-               int length;
+               unsigned int length;
                char *annex;
                
                /* skip command character */
                packet += 20;
                
-               if (decode_xfer_read( packet, &annex, &offset, &length ) < 0)
+               if (decode_xfer_read(packet, &annex, &offset, &length) < 0)
                {
                        gdb_send_error(connection, 01);
                        return ERROR_OK;
@@ -1522,9 +1684,6 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p
                        return ERROR_SERVER_REMOTE_CLOSED;
                }
                
-               /* disable gdb output while programming */
-               gdb_connection->output_disable = 1;
-               
                /* assume all sectors need erasing - stops any problems
                 * when flash_write is called multiple times */
                flash_set_dirty();
@@ -1533,7 +1692,7 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p
                target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_PROGRAM);
                
                /* perform erase */
-               if ((result = flash_erase(gdb_service->target, addr, length)) != ERROR_OK)
+               if ((result = flash_erase_address_range(gdb_service->target, addr, length)) != ERROR_OK)
                {
                        /* GDB doesn't evaluate the actual error number returned,
                         * treat a failed erase as an I/O error
@@ -1544,9 +1703,6 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p
                else
                        gdb_put_packet(connection, "OK", 2);
                
-               /* reenable gdb output */
-               gdb_connection->output_disable = 0;
-               
                return ERROR_OK;
        }
 
@@ -1569,9 +1725,6 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p
                }
                length = packet_size - (parse - packet);
                
-               /* disable gdb output while programming */
-               gdb_connection->output_disable = 1;
-               
                /* create a new image if there isn't already one */
                if (gdb_connection->vflash_image == NULL)
                {
@@ -1584,9 +1737,6 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p
 
                gdb_put_packet(connection, "OK", 2);
 
-               /* reenable gdb output */
-               gdb_connection->output_disable = 0;
-               
                return ERROR_OK;
        }
 
@@ -1595,10 +1745,8 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p
                u32 written;
                char *error_str;
 
-               /* disable gdb output while programming */
-               gdb_connection->output_disable = 1;
-               
-               /* process the flashing buffer */
+               /* 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, &error_str, NULL, 0)) != ERROR_OK)
                {
                        if (result == ERROR_FLASH_DST_OUT_OF_BANK)
@@ -1622,9 +1770,6 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p
                free(gdb_connection->vflash_image);
                gdb_connection->vflash_image = NULL;
                
-               /* reenable gdb output */
-               gdb_connection->output_disable = 0;
-               
                return ERROR_OK;
        }
 
@@ -1657,7 +1802,28 @@ int gdb_detach(connection_t *connection, target_t *target)
        return ERROR_OK;
 }
 
-int gdb_input(connection_t *connection)
+static void gdb_log_callback(void *priv, const char *file, int line, 
+               const char *function, const char *format, va_list args)
+{
+       connection_t *connection = priv;
+       gdb_connection_t *gdb_con = connection->priv;
+       
+       if (gdb_con->busy)
+       {
+               /* do not reply this using the O packet */
+               return;
+       }
+
+       char *t = allocPrintf(format, args);
+       if (t == NULL)
+               return;
+       
+       gdb_output_con(connection, t); 
+       
+       free(t);
+}
+
+int gdb_input_inner(connection_t *connection)
 {
        gdb_service_t *gdb_service = connection->service->priv;
        target_t *target = gdb_service->target;
@@ -1673,17 +1839,7 @@ int gdb_input(connection_t *connection)
                packet_size = GDB_BUFFER_SIZE-1;
                if ((retval = gdb_get_packet(connection, packet, &packet_size)) != ERROR_OK)
                {
-                       switch (retval)
-                       {
-                               case ERROR_GDB_BUFFER_TOO_SMALL:
-                                       ERROR("BUG: buffer supplied for gdb packet was too small");
-                                       exit(-1);
-                               case ERROR_SERVER_REMOTE_CLOSED:
-                                       return ERROR_SERVER_REMOTE_CLOSED;
-                               default:
-                                       ERROR("BUG: unexpected error");
-                                       exit(-1);
-                       }
+                       return retval;
                }
 
                /* terminate with zero */
@@ -1731,7 +1887,14 @@ int gdb_input(connection_t *connection)
                                        break;
                                case 'c':
                                case 's':
-                                       gdb_step_continue_packet(connection, target, packet, packet_size);
+                                       {
+                                       /* We're running/stepping, in which case we can 
+                                        * forward log output until the target is halted */
+                                               gdb_connection_t *gdb_con = connection->priv;
+                                               gdb_con->frontend_state = TARGET_RUNNING;
+                                               log_setCallback(gdb_log_callback, connection);
+                                               gdb_step_continue_packet(connection, target, packet, packet_size);
+                                       }
                                        break;
                                case 'v':
                                        retval = gdb_v_packet(connection, target, packet, packet_size);
@@ -1784,6 +1947,15 @@ int gdb_input(connection_t *connection)
        return ERROR_OK;
 }
 
+int gdb_input(connection_t *connection)
+{
+       int retval = gdb_input_inner(connection);
+       if (retval == ERROR_SERVER_REMOTE_CLOSED)
+               return retval;
+       /* we'll recover from any other errors(e.g. temporary timeouts, etc.) */
+       return ERROR_OK;
+}
+
 int gdb_init()
 {
        gdb_service_t *gdb_service;
@@ -1905,6 +2077,26 @@ int handle_gdb_flash_program_command(struct command_context_s *cmd_ctx, char *cm
        return ERROR_OK;
 }
 
+int handle_gdb_report_data_abort_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+       if (argc == 1)
+       {
+               if (strcmp(args[0], "enable") == 0)
+               {
+                       gdb_report_data_abort = 1;
+                       return ERROR_OK;
+               }
+               else if (strcmp(args[0], "disable") == 0)
+               {
+                       gdb_report_data_abort = 0;
+                       return ERROR_OK;
+               }
+       }
+       
+       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,
@@ -1915,5 +2107,7 @@ int gdb_register_commands(command_context_t *command_context)
                        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, "");
        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)