target/xtensa: avoid IHI for writes to non-executable memory
[openocd.git] / src / server / gdb_server.c
index 497f62c4290dc3663f858041d1ce7dc04973afaf..dfd7cd52010e3991320000514c4a52b29129c8c5 100644 (file)
@@ -53,6 +53,8 @@
 enum gdb_output_flag {
        /* GDB doesn't accept 'O' packets */
        GDB_OUTPUT_NO,
+       /* GDB doesn't accept 'O' packets but accepts notifications */
+       GDB_OUTPUT_NOTIF,
        /* GDB accepts 'O' packets */
        GDB_OUTPUT_ALL,
 };
@@ -71,6 +73,8 @@ struct gdb_connection {
        enum target_state frontend_state;
        struct image *vflash_image;
        bool closed;
+       /* set to prevent re-entrance from log messages during gdb_get_packet()
+        * and gdb_put_packet(). */
        bool busy;
        int noack_mode;
        /* set flag to true if you want the next stepi to return immediately.
@@ -95,6 +99,8 @@ struct gdb_connection {
        char *thread_list;
        /* flag to mask the output from gdb_log_callback() */
        enum gdb_output_flag output_flag;
+       /* Unique index for this GDB connection. */
+       unsigned int unique_index;
 };
 
 #if 0
@@ -351,6 +357,7 @@ static void gdb_log_incoming_packet(struct connection *connection, char *packet)
                return;
 
        struct target *target = get_target_from_connection(connection);
+       struct gdb_connection *gdb_connection = connection->priv;
 
        /* Avoid dumping non-printable characters to the terminal */
        const unsigned packet_len = strlen(packet);
@@ -365,14 +372,15 @@ static void gdb_log_incoming_packet(struct connection *connection, char *packet)
                if (packet_prefix_printable) {
                        const unsigned int prefix_len = colon - packet + 1;  /* + 1 to include the ':' */
                        const unsigned int payload_len = packet_len - prefix_len;
-                       LOG_TARGET_DEBUG(target, "received packet: %.*s<binary-data-%u-bytes>", prefix_len,
-                               packet, payload_len);
+                       LOG_TARGET_DEBUG(target, "{%d} received packet: %.*s<binary-data-%u-bytes>",
+                               gdb_connection->unique_index, prefix_len, packet, payload_len);
                } else {
-                       LOG_TARGET_DEBUG(target, "received packet: <binary-data-%u-bytes>", packet_len);
+                       LOG_TARGET_DEBUG(target, "{%d} received packet: <binary-data-%u-bytes>",
+                               gdb_connection->unique_index, packet_len);
                }
        } else {
                /* All chars printable, dump the packet as is */
-               LOG_TARGET_DEBUG(target, "received packet: %s", packet);
+               LOG_TARGET_DEBUG(target, "{%d} received packet: %s", gdb_connection->unique_index, packet);
        }
 }
 
@@ -383,13 +391,14 @@ static void gdb_log_outgoing_packet(struct connection *connection, char *packet_
                return;
 
        struct target *target = get_target_from_connection(connection);
+       struct gdb_connection *gdb_connection = connection->priv;
 
        if (find_nonprint_char(packet_buf, packet_len))
-               LOG_TARGET_DEBUG(target, "sending packet: $<binary-data-%u-bytes>#%2.2x",
-                       packet_len, checksum);
+               LOG_TARGET_DEBUG(target, "{%d} sending packet: $<binary-data-%u-bytes>#%2.2x",
+                       gdb_connection->unique_index, packet_len, checksum);
        else
-               LOG_TARGET_DEBUG(target, "sending packet: $%.*s#%2.2x", packet_len, packet_buf,
-                       checksum);
+               LOG_TARGET_DEBUG(target, "{%d} sending packet: $%.*s#%2.2x",
+                       gdb_connection->unique_index, packet_len, packet_buf, checksum);
 }
 
 static int gdb_put_packet_inner(struct connection *connection,
@@ -971,6 +980,7 @@ static int gdb_new_connection(struct connection *connection)
        struct target *target;
        int retval;
        int initial_ack;
+       static unsigned int next_unique_id = 1;
 
        target = get_target_from_connection(connection);
        connection->priv = gdb_connection;
@@ -993,9 +1003,7 @@ static int gdb_new_connection(struct connection *connection)
        gdb_connection->target_desc.tdesc_length = 0;
        gdb_connection->thread_list = NULL;
        gdb_connection->output_flag = GDB_OUTPUT_NO;
-
-       /* send ACK to GDB for debug request */
-       gdb_write(connection, "+", 1);
+       gdb_connection->unique_index = next_unique_id++;
 
        /* output goes through gdb connection */
        command_set_output_handler(connection->cmd_ctx, gdb_output, connection);
@@ -1051,20 +1059,19 @@ static int gdb_new_connection(struct connection *connection)
                }
        }
 
-       gdb_actual_connections++;
        log_printf_lf(all_targets->next ? LOG_LVL_INFO : LOG_LVL_DEBUG,
                        __FILE__, __LINE__, __func__,
                        "New GDB Connection: %d, Target %s, state: %s",
-                       gdb_actual_connections,
+                       gdb_connection->unique_index,
                        target_name(target),
                        target_state_name(target));
 
        if (!target_was_examined(target)) {
                LOG_ERROR("Target %s not examined yet, refuse gdb connection %d!",
-                                 target_name(target), gdb_actual_connections);
-               gdb_actual_connections--;
+                                 target_name(target), gdb_connection->unique_index);
                return ERROR_TARGET_NOT_EXAMINED;
        }
+       gdb_actual_connections++;
 
        if (target->state != TARGET_HALTED)
                LOG_WARNING("GDB connection %d on target %s not halted",
@@ -1095,7 +1102,8 @@ static int gdb_connection_closed(struct connection *connection)
        log_remove_callback(gdb_log_callback, connection);
 
        gdb_actual_connections--;
-       LOG_DEBUG("GDB Close, Target: %s, state: %s, gdb_actual_connections=%d",
+       LOG_DEBUG("{%d} GDB Close, Target: %s, state: %s, gdb_actual_connections=%d",
+               gdb_connection->unique_index,
                target_name(target),
                target_state_name(target),
                gdb_actual_connections);
@@ -1482,9 +1490,6 @@ static int gdb_error(struct connection *connection, int retval)
        return ERROR_OK;
 }
 
-/* We don't have to worry about the default 2 second timeout for GDB packets,
- * because GDB breaks up large memory reads into smaller reads.
- */
 static int gdb_read_memory_packet(struct connection *connection,
                char const *packet, int packet_size)
 {
@@ -2840,7 +2845,9 @@ static int gdb_query_packet(struct connection *connection,
 
                        len = strtoul(separator + 1, NULL, 16);
 
+                       gdb_connection->output_flag = GDB_OUTPUT_NOTIF;
                        retval = target_checksum_memory(target, addr, len, &checksum);
+                       gdb_connection->output_flag = GDB_OUTPUT_NO;
 
                        if (retval == ERROR_OK) {
                                snprintf(gdb_reply, 10, "C%8.8" PRIx32 "", checksum);
@@ -3279,7 +3286,7 @@ static int gdb_v_packet(struct connection *connection,
        }
 
        if (strncmp(packet, "vFlashErase:", 12) == 0) {
-               unsigned long addr;
+               target_addr_t addr;
                unsigned long length;
 
                char const *parse = packet + 12;
@@ -3288,7 +3295,7 @@ static int gdb_v_packet(struct connection *connection,
                        return ERROR_SERVER_REMOTE_CLOSED;
                }
 
-               addr = strtoul(parse, (char **)&parse, 16);
+               addr = strtoull(parse, (char **)&parse, 16);
 
                if (*(parse++) != ',' || *parse == '\0') {
                        LOG_ERROR("incomplete vFlashErase packet received, dropping connection");
@@ -3336,7 +3343,7 @@ static int gdb_v_packet(struct connection *connection,
 
        if (strncmp(packet, "vFlashWrite:", 12) == 0) {
                int retval;
-               unsigned long addr;
+               target_addr_t addr;
                unsigned long length;
                char const *parse = packet + 12;
 
@@ -3344,7 +3351,8 @@ static int gdb_v_packet(struct connection *connection,
                        LOG_ERROR("incomplete vFlashErase packet received, dropping connection");
                        return ERROR_SERVER_REMOTE_CLOSED;
                }
-               addr = strtoul(parse, (char **)&parse, 16);
+
+               addr = strtoull(parse, (char **)&parse, 16);
                if (*(parse++) != ':') {
                        LOG_ERROR("incomplete vFlashErase packet received, dropping connection");
                        return ERROR_SERVER_REMOTE_CLOSED;
@@ -3371,6 +3379,13 @@ static int gdb_v_packet(struct connection *connection,
        if (strncmp(packet, "vFlashDone", 10) == 0) {
                uint32_t written;
 
+               /* GDB command 'flash-erase' does not send a vFlashWrite,
+                * so nothing to write here. */
+               if (!gdb_connection->vflash_image) {
+                       gdb_put_packet(connection, "OK", 2);
+                       return ERROR_OK;
+               }
+
                /* process the flashing buffer. No need to erase as GDB
                 * always issues a vFlashErase first. */
                target_call_event_callbacks(target,
@@ -3465,7 +3480,7 @@ static void gdb_log_callback(void *priv, const char *file, unsigned line,
        struct connection *connection = priv;
        struct gdb_connection *gdb_con = connection->priv;
 
-       if (gdb_con->output_flag == GDB_OUTPUT_NO)
+       if (gdb_con->output_flag != GDB_OUTPUT_ALL)
                /* No out allowed */
                return;
 
@@ -3550,10 +3565,14 @@ static int gdb_input_inner(struct connection *connection)
                                        retval = gdb_set_register_packet(connection, packet, packet_size);
                                        break;
                                case 'm':
+                                       gdb_con->output_flag = GDB_OUTPUT_NOTIF;
                                        retval = gdb_read_memory_packet(connection, packet, packet_size);
+                                       gdb_con->output_flag = GDB_OUTPUT_NO;
                                        break;
                                case 'M':
+                                       gdb_con->output_flag = GDB_OUTPUT_NOTIF;
                                        retval = gdb_write_memory_packet(connection, packet, packet_size);
+                                       gdb_con->output_flag = GDB_OUTPUT_NO;
                                        break;
                                case 'z':
                                case 'Z':
@@ -3644,9 +3663,9 @@ static int gdb_input_inner(struct connection *connection)
                                        retval = gdb_detach(connection);
                                        break;
                                case 'X':
+                                       gdb_con->output_flag = GDB_OUTPUT_NOTIF;
                                        retval = gdb_write_memory_binary_packet(connection, packet, packet_size);
-                                       if (retval != ERROR_OK)
-                                               return retval;
+                                       gdb_con->output_flag = GDB_OUTPUT_NO;
                                        break;
                                case 'k':
                                        if (gdb_con->extended_protocol) {
@@ -3742,19 +3761,51 @@ static int gdb_input(struct connection *connection)
        return ERROR_OK;
 }
 
+/*
+ * Send custom notification packet as keep-alive during memory read/write.
+ *
+ * From gdb 7.0 (released 2009-10-06) an unknown notification received during
+ * memory read/write would be silently dropped.
+ * Before gdb 7.0 any character, with exclusion of "+-$", would be considered
+ * as junk and ignored.
+ * In both cases the reception will reset the timeout counter in gdb, thus
+ * working as a keep-alive.
+ * Check putpkt_binary() and getpkt_sane() in gdb commit
+ * 74531fed1f2d662debc2c209b8b3faddceb55960
+ *
+ * Enable remote debug in gdb with 'set debug remote 1' to either dump the junk
+ * characters in gdb pre-7.0 and the notification from gdb 7.0.
+ */
+static void gdb_async_notif(struct connection *connection)
+{
+       static unsigned char count;
+       unsigned char checksum = 0;
+       char buf[22];
+
+       int len = sprintf(buf, "%%oocd_keepalive:%2.2x", count++);
+       for (int i = 1; i < len; i++)
+               checksum += buf[i];
+       len += sprintf(buf + len, "#%2.2x", checksum);
+
+#ifdef _DEBUG_GDB_IO_
+       LOG_DEBUG("sending packet '%s'", buf);
+#endif
+
+       gdb_write(connection, buf, len);
+}
+
 static void gdb_keep_client_alive(struct connection *connection)
 {
        struct gdb_connection *gdb_con = connection->priv;
 
-       if (gdb_con->busy) {
-               /* do not send packets, retry asap */
-               return;
-       }
-
        switch (gdb_con->output_flag) {
        case GDB_OUTPUT_NO:
                /* no need for keep-alive */
                break;
+       case GDB_OUTPUT_NOTIF:
+               /* send asynchronous notification */
+               gdb_async_notif(connection);
+               break;
        case GDB_OUTPUT_ALL:
                /* send an empty O packet */
                gdb_output_con(connection, "");

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)