- added faster gdb packet handling (thanks to oyvind harboe for the patch)
[openocd.git] / src / server / gdb_server.c
index 42f04680b6acb57b1b4dba4cc9088aa67647149b..01d09791ad97cc6f3118b29e80c233a04630ca5d 100644 (file)
@@ -38,7 +38,7 @@
 #include <unistd.h>
 #include <stdlib.h>
 
-#if 0
+#if 1
 #define _DEBUG_GDB_IO_
 #endif
 
@@ -185,7 +185,9 @@ int gdb_put_packet(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,12 +197,13 @@ 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);
-
+#endif
                write_socket(connection->fd, "$", 1);
                if (len > 0)
                        write_socket(connection->fd, buffer, len);
@@ -283,13 +286,68 @@ int gdb_get_packet(connection_t *connection, char *buffer, int *len)
                } while (character != '$');
 
                my_checksum = 0;
-
-               do
+               
+               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 +365,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;
 
@@ -532,30 +585,33 @@ 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);
+       static const char *DIGITS = "0123456789abcdef";
        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 +654,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 +682,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 +711,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 +785,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 +812,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;
 }
@@ -902,6 +962,18 @@ int gdb_read_memory_packet(connection_t *connection, target_t *target, char *pac
                                retval = target->type->read_memory(target, addr, 1, len, buffer);
        }
 
+#if 0
+       if (retval == ERROR_TARGET_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.
+                * http://sourceware.org/cgi-bin/gnatsweb.pl?cmd=view%20audit-trail&database=gdb&pr=2395
+                */
+               memset(buffer, 0, len);
+               retval = ERROR_OK;
+       }
+#endif
+
        if (retval == ERROR_OK)
        {
                hex_buffer = malloc(len * 2 + 1);
@@ -1240,13 +1312,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;
                        }
                }
@@ -1309,6 +1381,7 @@ int gdb_query_packet(connection_t *connection, target_t *target, char *packet, i
                                cmd[i] = tmp;
                        }
                        cmd[(packet_size - 6)/2] = 0x0;
+                       target_call_timer_callbacks();
                        command_run_line(cmd_ctx, cmd);
                        free(cmd);
                }
@@ -1363,15 +1436,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::"))
        {
@@ -1533,7 +1612,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
@@ -1598,7 +1677,8 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p
                /* 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)

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)