]> git.sur5r.net Git - openocd/blobdiff - src/server/gdb_server.c
Add detail to `wrong register size` error.
[openocd] / src / server / gdb_server.c
index d866fc6e2a4641167875f2294e4aae2bcd2cb980..69afb2fd4e95b9d8483722d4cbbb73d103b931ff 100644 (file)
@@ -130,6 +130,9 @@ static int gdb_flash_program = 1;
  * Disabled by default.
  */
 static int gdb_report_data_abort;
+/* If set, errors when accessing registers are reported to gdb. Disabled by
+ * default. */
+static int gdb_report_register_access_error;
 
 /* set if we are sending target descriptions to gdb
  * via qXfer:features:read packet */
@@ -732,7 +735,6 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio
        } else {
                if (gdb_connection->ctrl_c) {
                        signal_var = 0x2;
-                       gdb_connection->ctrl_c = 0;
                } else
                        signal_var = gdb_last_signal(target);
 
@@ -764,12 +766,19 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio
 
                current_thread[0] = '\0';
                if (target->rtos != NULL) {
-                       snprintf(current_thread, sizeof(current_thread), "thread:%016" PRIx64 ";", target->rtos->current_thread);
+                       struct target *ct;
+                       snprintf(current_thread, sizeof(current_thread), "thread:%016" PRIx64 ";",
+                                       target->rtos->current_thread);
                        target->rtos->current_threadid = target->rtos->current_thread;
+                       target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &ct);
+                       if (!gdb_connection->ctrl_c)
+                               signal_var = gdb_last_signal(ct);
                }
 
                sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "T%2.2x%s%s",
                                signal_var, stop_reason, current_thread);
+
+               gdb_connection->ctrl_c = 0;
        }
 
        gdb_put_packet(connection, sig_reply, sig_reply_len);
@@ -784,64 +793,64 @@ static void gdb_fileio_reply(struct target *target, struct connection *connectio
        bool program_exited = false;
 
        if (strcmp(target->fileio_info->identifier, "open") == 0)
-               sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
+               sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64 ",%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
                                target->fileio_info->param_1,
                                target->fileio_info->param_2,
                                target->fileio_info->param_3,
                                target->fileio_info->param_4);
        else if (strcmp(target->fileio_info->identifier, "close") == 0)
-               sprintf(fileio_command, "F%s,%" PRIx32, target->fileio_info->identifier,
+               sprintf(fileio_command, "F%s,%" PRIx64, target->fileio_info->identifier,
                                target->fileio_info->param_1);
        else if (strcmp(target->fileio_info->identifier, "read") == 0)
-               sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
+               sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
                                target->fileio_info->param_1,
                                target->fileio_info->param_2,
                                target->fileio_info->param_3);
        else if (strcmp(target->fileio_info->identifier, "write") == 0)
-               sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
+               sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
                                target->fileio_info->param_1,
                                target->fileio_info->param_2,
                                target->fileio_info->param_3);
        else if (strcmp(target->fileio_info->identifier, "lseek") == 0)
-               sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
+               sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
                                target->fileio_info->param_1,
                                target->fileio_info->param_2,
                                target->fileio_info->param_3);
        else if (strcmp(target->fileio_info->identifier, "rename") == 0)
-               sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32 ",%" PRIx32 "/%" PRIx32, target->fileio_info->identifier,
+               sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64 ",%" PRIx64 "/%" PRIx64, target->fileio_info->identifier,
                                target->fileio_info->param_1,
                                target->fileio_info->param_2,
                                target->fileio_info->param_3,
                                target->fileio_info->param_4);
        else if (strcmp(target->fileio_info->identifier, "unlink") == 0)
-               sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32, target->fileio_info->identifier,
+               sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64, target->fileio_info->identifier,
                                target->fileio_info->param_1,
                                target->fileio_info->param_2);
        else if (strcmp(target->fileio_info->identifier, "stat") == 0)
-               sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
+               sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
                                target->fileio_info->param_1,
                                target->fileio_info->param_2,
                                target->fileio_info->param_3);
        else if (strcmp(target->fileio_info->identifier, "fstat") == 0)
-               sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
+               sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
                                target->fileio_info->param_1,
                                target->fileio_info->param_2);
        else if (strcmp(target->fileio_info->identifier, "gettimeofday") == 0)
-               sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
+               sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
                                target->fileio_info->param_1,
                                target->fileio_info->param_2);
        else if (strcmp(target->fileio_info->identifier, "isatty") == 0)
-               sprintf(fileio_command, "F%s,%" PRIx32, target->fileio_info->identifier,
+               sprintf(fileio_command, "F%s,%" PRIx64, target->fileio_info->identifier,
                                target->fileio_info->param_1);
        else if (strcmp(target->fileio_info->identifier, "system") == 0)
-               sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32, target->fileio_info->identifier,
+               sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64, target->fileio_info->identifier,
                                target->fileio_info->param_1,
                                target->fileio_info->param_2);
        else if (strcmp(target->fileio_info->identifier, "exit") == 0) {
                /* If target hits exit syscall, report to GDB the program is terminated.
                 * In addition, let target run its own exit syscall handler. */
                program_exited = true;
-               sprintf(fileio_command, "W%02" PRIx32, target->fileio_info->param_1);
+               sprintf(fileio_command, "W%02" PRIx64, target->fileio_info->param_1);
        } else {
                LOG_DEBUG("Unknown syscall: %s", target->fileio_info->identifier);
 
@@ -927,6 +936,7 @@ static int gdb_new_connection(struct connection *connection)
 
        target = get_target_from_connection(connection);
        connection->priv = gdb_connection;
+       connection->cmd_ctx->current_target = target;
 
        /* initialize gdb connection information */
        gdb_connection->buf_p = gdb_connection->buffer;
@@ -957,9 +967,14 @@ static int gdb_new_connection(struct connection *connection)
        breakpoint_clear_target(target);
        watchpoint_clear_target(target);
 
-       /* clean previous rtos session if supported*/
-       if ((target->rtos) && (target->rtos->type->clean))
-               target->rtos->type->clean(target);
+       if (target->rtos) {
+               /* clean previous rtos session if supported*/
+               if (target->rtos->type->clean)
+                       target->rtos->type->clean(target);
+
+               /* update threads */
+               rtos_update_threads(target);
+       }
 
        /* remove the initial ACK from the incoming buffer */
        retval = gdb_get_char(connection, &initial_ack);
@@ -1176,8 +1191,15 @@ static int gdb_get_registers_packet(struct connection *connection,
        reg_packet_p = reg_packet;
 
        for (i = 0; i < reg_list_size; i++) {
-               if (!reg_list[i]->valid)
-                       reg_list[i]->type->get(reg_list[i]);
+               if (!reg_list[i]->valid) {
+                       retval = reg_list[i]->type->get(reg_list[i]);
+                       if (retval != ERROR_OK && gdb_report_register_access_error) {
+                               LOG_DEBUG("Couldn't get register %s.", reg_list[i]->name);
+                               free(reg_packet);
+                               free(reg_list);
+                               return gdb_error(connection, retval);
+                       }
+               }
                gdb_str_to_target(target, reg_packet_p, reg_list[i]);
                reg_packet_p += DIV_ROUND_UP(reg_list[i]->size, 8) * 2;
        }
@@ -1238,7 +1260,13 @@ static int gdb_set_registers_packet(struct connection *connection,
                bin_buf = malloc(DIV_ROUND_UP(reg_list[i]->size, 8));
                gdb_target_to_reg(target, packet_p, chars, bin_buf);
 
-               reg_list[i]->type->set(reg_list[i], bin_buf);
+               retval = reg_list[i]->type->set(reg_list[i], bin_buf);
+               if (retval != ERROR_OK && gdb_report_register_access_error) {
+                       LOG_DEBUG("Couldn't set register %s.", reg_list[i]->name);
+                       free(reg_list);
+                       free(bin_buf);
+                       return gdb_error(connection, retval);
+               }
 
                /* advance packet pointer */
                packet_p += chars;
@@ -1278,8 +1306,14 @@ static int gdb_get_register_packet(struct connection *connection,
                return ERROR_SERVER_REMOTE_CLOSED;
        }
 
-       if (!reg_list[reg_num]->valid)
-               reg_list[reg_num]->type->get(reg_list[reg_num]);
+       if (!reg_list[reg_num]->valid) {
+               retval = reg_list[reg_num]->type->get(reg_list[reg_num]);
+               if (retval != ERROR_OK && gdb_report_register_access_error) {
+                       LOG_DEBUG("Couldn't get register %s.", reg_list[reg_num]->name);
+                       free(reg_list);
+                       return gdb_error(connection, retval);
+               }
+       }
 
        reg_packet = malloc(DIV_ROUND_UP(reg_list[reg_num]->size, 8) * 2 + 1); /* plus one for string termination null */
 
@@ -1326,14 +1360,21 @@ static int gdb_set_register_packet(struct connection *connection,
        int chars = (DIV_ROUND_UP(reg_list[reg_num]->size, 8) * 2);
 
        if ((unsigned int)chars != strlen(separator + 1)) {
-               LOG_ERROR("gdb sent a packet with wrong register size");
+               LOG_ERROR("gdb sent %zu bits for a %d-bit register (%s)",
+                               strlen(separator + 1) * 4, chars * 4, reg_list[reg_num]->name);
                free(bin_buf);
                return ERROR_SERVER_REMOTE_CLOSED;
        }
 
        gdb_target_to_reg(target, separator + 1, chars, bin_buf);
 
-       reg_list[reg_num]->type->set(reg_list[reg_num], bin_buf);
+       retval = reg_list[reg_num]->type->set(reg_list[reg_num], bin_buf);
+       if (retval != ERROR_OK && gdb_report_register_access_error) {
+               LOG_DEBUG("Couldn't set register %s.", reg_list[reg_num]->name);
+               free(bin_buf);
+               free(reg_list);
+               return gdb_error(connection, retval);
+       }
 
        gdb_put_packet(connection, "OK", 2);
 
@@ -1769,7 +1810,7 @@ static int gdb_memory_map(struct connection *connection,
        int offset;
        int length;
        char *separator;
-       uint32_t ram_start = 0;
+       target_addr_t ram_start = 0;
        int i;
        int target_flash_banks = 0;
 
@@ -1784,9 +1825,6 @@ static int gdb_memory_map(struct connection *connection,
        /* Sort banks in ascending order.  We need to report non-flash
         * memory as ram (or rather read/write) by default for GDB, since
         * it has no concept of non-cacheable read/write memory (i/o etc).
-        *
-        * FIXME Most non-flash addresses are *NOT* RAM!  Don't lie.
-        * Current versions of GDB assume unlisted addresses are RAM...
         */
        banks = malloc(sizeof(struct flash_bank *)*flash_get_bank_count());
 
@@ -1809,14 +1847,13 @@ static int gdb_memory_map(struct connection *connection,
        for (i = 0; i < target_flash_banks; i++) {
                int j;
                unsigned sector_size = 0;
-               uint32_t start;
+               unsigned group_len = 0;
 
                p = banks[i];
-               start = p->base;
 
                if (ram_start < p->base)
                        xml_printf(&retval, &xml, &pos, &size,
-                               "<memory type=\"ram\" start=\"0x%x\" "
+                               "<memory type=\"ram\" start=\"" TARGET_ADDR_FMT "\" "
                                "length=\"0x%x\"/>\n",
                                ram_start, p->base - ram_start);
 
@@ -1827,27 +1864,35 @@ static int gdb_memory_map(struct connection *connection,
                 * regions with 8KB, 32KB, and 64KB sectors; etc.
                 */
                for (j = 0; j < p->num_sectors; j++) {
-                       unsigned group_len;
 
                        /* Maybe start a new group of sectors. */
                        if (sector_size == 0) {
+                               if (p->sectors[j].offset + p->sectors[j].size > p->size) {
+                                       LOG_WARNING("The flash sector at offset 0x%08" PRIx32
+                                               " overflows the end of %s bank.",
+                                               p->sectors[j].offset, p->name);
+                                       LOG_WARNING("The rest of bank will not show in gdb memory map.");
+                                       break;
+                               }
+                               target_addr_t start;
                                start = p->base + p->sectors[j].offset;
                                xml_printf(&retval, &xml, &pos, &size,
                                        "<memory type=\"flash\" "
-                                       "start=\"0x%x\" ",
+                                       "start=\"" TARGET_ADDR_FMT "\" ",
                                        start);
                                sector_size = p->sectors[j].size;
+                               group_len = sector_size;
+                       } else {
+                               group_len += sector_size; /* equal to p->sectors[j].size */
                        }
 
                        /* Does this finish a group of sectors?
                         * If not, continue an already-started group.
                         */
-                       if (j == p->num_sectors - 1)
-                               group_len = (p->base + p->size) - start;
-                       else if (p->sectors[j + 1].size != sector_size)
-                               group_len = p->base + p->sectors[j + 1].offset
-                                       - start;
-                       else
+                       if (j < p->num_sectors - 1
+                                       && p->sectors[j + 1].size == sector_size
+                                       && p->sectors[j + 1].offset == p->sectors[j].offset + sector_size
+                                       && p->sectors[j + 1].offset + p->sectors[j + 1].size <= p->size)
                                continue;
 
                        xml_printf(&retval, &xml, &pos, &size,
@@ -1865,7 +1910,7 @@ static int gdb_memory_map(struct connection *connection,
 
        if (ram_start != 0)
                xml_printf(&retval, &xml, &pos, &size,
-                       "<memory type=\"ram\" start=\"0x%x\" "
+                       "<memory type=\"ram\" start=\"" TARGET_ADDR_FMT "\" "
                        "length=\"0x%x\"/>\n",
                        ram_start, 0-ram_start);
        /* ELSE a flash chip could be at the very end of the 32 bit address
@@ -1873,11 +1918,11 @@ static int gdb_memory_map(struct connection *connection,
         */
 
        free(banks);
-       banks = NULL;
 
        xml_printf(&retval, &xml, &pos, &size, "</memory-map>\n");
 
        if (retval != ERROR_OK) {
+               free(xml);
                gdb_error(connection, retval);
                return retval;
        }
@@ -1898,6 +1943,8 @@ static int gdb_memory_map(struct connection *connection,
 static const char *gdb_get_reg_type_name(enum reg_type type)
 {
        switch (type) {
+               case REG_TYPE_BOOL:
+                       return "bool";
                case REG_TYPE_INT:
                        return "int";
                case REG_TYPE_INT8:
@@ -1910,6 +1957,8 @@ static const char *gdb_get_reg_type_name(enum reg_type type)
                        return "int64";
                case REG_TYPE_INT128:
                        return "int128";
+               case REG_TYPE_UINT:
+                       return "uint";
                case REG_TYPE_UINT8:
                        return "uint8";
                case REG_TYPE_UINT16:
@@ -1937,12 +1986,45 @@ static const char *gdb_get_reg_type_name(enum reg_type type)
        return "int"; /* "int" as default value */
 }
 
+static int lookup_add_arch_defined_types(char const **arch_defined_types_list[], const char *type_id,
+                                       int *num_arch_defined_types)
+{
+       int tbl_sz = *num_arch_defined_types;
+
+       if (type_id != NULL && (strcmp(type_id, ""))) {
+               for (int j = 0; j < (tbl_sz + 1); j++) {
+                       if (!((*arch_defined_types_list)[j])) {
+                               (*arch_defined_types_list)[tbl_sz++] = type_id;
+                               *arch_defined_types_list = realloc(*arch_defined_types_list,
+                                                               sizeof(char *) * (tbl_sz + 1));
+                               (*arch_defined_types_list)[tbl_sz] = NULL;
+                               *num_arch_defined_types = tbl_sz;
+                               return 1;
+                       } else {
+                               if (!strcmp((*arch_defined_types_list)[j], type_id))
+                                       return 0;
+                       }
+               }
+       }
+
+       return -1;
+}
+
 static int gdb_generate_reg_type_description(struct target *target,
-               char **tdesc, int *pos, int *size, struct reg_data_type *type)
+               char **tdesc, int *pos, int *size, struct reg_data_type *type,
+               char const **arch_defined_types_list[], int * num_arch_defined_types)
 {
        int retval = ERROR_OK;
 
        if (type->type_class == REG_TYPE_CLASS_VECTOR) {
+               struct reg_data_type *data_type = type->reg_type_vector->type;
+               if (data_type->type == REG_TYPE_ARCH_DEFINED) {
+                       if (lookup_add_arch_defined_types(arch_defined_types_list, data_type->id,
+                                                       num_arch_defined_types))
+                               gdb_generate_reg_type_description(target, tdesc, pos, size, data_type,
+                                                               arch_defined_types_list,
+                                                               num_arch_defined_types);
+               }
                /* <vector id="id" type="type" count="count"/> */
                xml_printf(&retval, tdesc, pos, size,
                                "<vector id=\"%s\" type=\"%s\" count=\"%d\"/>\n",
@@ -1950,6 +2032,20 @@ static int gdb_generate_reg_type_description(struct target *target,
                                type->reg_type_vector->count);
 
        } else if (type->type_class == REG_TYPE_CLASS_UNION) {
+               struct reg_data_type_union_field *field;
+               field = type->reg_type_union->fields;
+               while (field != NULL) {
+                       struct reg_data_type *data_type = field->type;
+                       if (data_type->type == REG_TYPE_ARCH_DEFINED) {
+                               if (lookup_add_arch_defined_types(arch_defined_types_list, data_type->id,
+                                                               num_arch_defined_types))
+                                       gdb_generate_reg_type_description(target, tdesc, pos, size, data_type,
+                                                                       arch_defined_types_list,
+                                                                       num_arch_defined_types);
+                       }
+
+                       field = field->next;
+               }
                /* <union id="id">
                 *  <field name="name" type="type"/> ...
                 * </union> */
@@ -1957,7 +2053,6 @@ static int gdb_generate_reg_type_description(struct target *target,
                                "<union id=\"%s\">\n",
                                type->id);
 
-               struct reg_data_type_union_field *field;
                field = type->reg_type_union->fields;
                while (field != NULL) {
                        xml_printf(&retval, tdesc, pos, size,
@@ -1983,13 +2078,24 @@ static int gdb_generate_reg_type_description(struct target *target,
                                        type->id, type->reg_type_struct->size);
                        while (field != NULL) {
                                xml_printf(&retval, tdesc, pos, size,
-                                               "<field name=\"%s\" start=\"%d\" end=\"%d\"/>\n",
-                                               field->name, field->bitfield->start,
-                                               field->bitfield->end);
+                                               "<field name=\"%s\" start=\"%d\" end=\"%d\" type=\"%s\" />\n",
+                                               field->name, field->bitfield->start, field->bitfield->end,
+                                               gdb_get_reg_type_name(field->bitfield->type));
 
                                field = field->next;
                        }
                } else {
+                       while (field != NULL) {
+                               struct reg_data_type *data_type = field->type;
+                               if (data_type->type == REG_TYPE_ARCH_DEFINED) {
+                                       if (lookup_add_arch_defined_types(arch_defined_types_list, data_type->id,
+                                                                       num_arch_defined_types))
+                                               gdb_generate_reg_type_description(target, tdesc, pos, size, data_type,
+                                                                               arch_defined_types_list,
+                                                                               num_arch_defined_types);
+                               }
+                       }
+
                        /* <struct id="id">
                         *  <field name="name" type="type"/> ...
                         * </struct> */
@@ -2020,8 +2126,9 @@ static int gdb_generate_reg_type_description(struct target *target,
                field = type->reg_type_flags->fields;
                while (field != NULL) {
                        xml_printf(&retval, tdesc, pos, size,
-                                       "<field name=\"%s\" start=\"%d\" end=\"%d\"/>\n",
-                                       field->name, field->bitfield->start, field->bitfield->end);
+                                       "<field name=\"%s\" start=\"%d\" end=\"%d\" type=\"%s\" />\n",
+                                       field->name, field->bitfield->start, field->bitfield->end,
+                                       gdb_get_reg_type_name(field->bitfield->type));
 
                        field = field->next;
                }
@@ -2082,11 +2189,15 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o
        struct reg **reg_list = NULL;
        int reg_list_size;
        char const **features = NULL;
+       char const **arch_defined_types = NULL;
        int feature_list_size = 0;
+       int num_arch_defined_types = 0;
        char *tdesc = NULL;
        int pos = 0;
        int size = 0;
 
+       arch_defined_types = calloc(1, sizeof(char *));
+
        retval = target_get_gdb_reg_list(target, &reg_list,
                        &reg_list_size, REG_CLASS_ALL);
 
@@ -2139,8 +2250,13 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o
                                if (reg_list[i]->reg_data_type != NULL) {
                                        if (reg_list[i]->reg_data_type->type == REG_TYPE_ARCH_DEFINED) {
                                                /* generate <type... first, if there are architecture-defined types. */
-                                               gdb_generate_reg_type_description(target, &tdesc, &pos, &size,
-                                                               reg_list[i]->reg_data_type);
+                                               if (lookup_add_arch_defined_types(&arch_defined_types,
+                                                                               reg_list[i]->reg_data_type->id,
+                                                                               &num_arch_defined_types))
+                                                       gdb_generate_reg_type_description(target, &tdesc, &pos, &size,
+                                                                                       reg_list[i]->reg_data_type,
+                                                                                       &arch_defined_types,
+                                                                                       &num_arch_defined_types);
 
                                                type_str = reg_list[i]->reg_data_type->id;
                                        } else {
@@ -2190,6 +2306,7 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o
 error:
        free(features);
        free(reg_list);
+       free(arch_defined_types);
 
        if (retval == ERROR_OK)
                *tdesc_out = tdesc;
@@ -2366,7 +2483,11 @@ static int gdb_get_thread_list_chunk(struct target *target, char **thread_list,
        else
                transfer_type = 'l';
 
-       *chunk = malloc(length + 2);
+       *chunk = malloc(length + 2 + 3);
+    /* Allocating extra 3 bytes prevents false positive valgrind report
+        * of strlen(chunk) word access:
+        * Invalid read of size 4
+        * Address 0x4479934 is 44 bytes inside a block of size 45 alloc'd */
        if (*chunk == NULL) {
                LOG_ERROR("Unable to allocate memory");
                return ERROR_FAIL;
@@ -2587,21 +2708,39 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p
 
        /* simple case, a continue packet */
        if (parse[0] == 'c') {
+               gdb_running_type = 'c';
                LOG_DEBUG("target %s continue", target_name(target));
                log_add_callback(gdb_log_callback, connection);
                retval = target_resume(target, 1, 0, 0, 0);
-               if (retval == ERROR_OK) {
-                       gdb_connection->frontend_state = TARGET_RUNNING;
-                       target_call_event_callbacks(target, TARGET_EVENT_GDB_START);
+               if (retval == ERROR_TARGET_NOT_HALTED)
+                       LOG_INFO("target %s was not halted when resume was requested", target_name(target));
+
+               /* poll target in an attempt to make its internal state consistent */
+               if (retval != ERROR_OK) {
+                       retval = target_poll(target);
+                       if (retval != ERROR_OK)
+                               LOG_DEBUG("error polling target %s after failed resume", target_name(target));
                }
+
+               /*
+                * We don't report errors to gdb here, move frontend_state to
+                * TARGET_RUNNING to stay in sync with gdb's expectation of the
+                * target state
+                */
+               gdb_connection->frontend_state = TARGET_RUNNING;
+               target_call_event_callbacks(target, TARGET_EVENT_GDB_START);
+
                return true;
        }
 
        /* single-step or step-over-breakpoint */
        if (parse[0] == 's') {
+               gdb_running_type = 's';
+               bool fake_step = false;
+
                if (strncmp(parse, "s:", 2) == 0) {
-                       int handle_breakpoint = 1;
                        struct target *ct = target;
+                       int current_pc = 1;
                        int64_t thread_id;
                        char *endp;
 
@@ -2614,6 +2753,20 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p
                                parse = endp;
                        }
 
+                       if (target->rtos != NULL) {
+                               /* FIXME: why is this necessary? rtos state should be up-to-date here already! */
+                               rtos_update_threads(target);
+
+                               target->rtos->gdb_target_for_threadid(connection, thread_id, &ct);
+
+                               /*
+                                * check if the thread to be stepped is the current rtos thread
+                                * if not, we must fake the step
+                                */
+                               if (target->rtos->current_thread != thread_id)
+                                       fake_step = true;
+                       }
+
                        if (parse[0] == ';') {
                                ++parse;
                                --packet_size;
@@ -2622,21 +2775,86 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p
                                        parse += 1;
                                        packet_size -= 1;
 
-                                       handle_breakpoint = 0;
+                                       /* check if thread-id follows */
+                                       if (parse[0] == ':') {
+                                               int64_t tid;
+                                               parse += 1;
+                                               packet_size -= 1;
+
+                                               tid = strtoll(parse, &endp, 16);
+                                               if (tid == thread_id) {
+                                                       /*
+                                                        * Special case: only step a single thread (core),
+                                                        * keep the other threads halted. Currently, only
+                                                        * aarch64 target understands it. Other target types don't
+                                                        * care (nobody checks the actual value of 'current')
+                                                        * and it doesn't really matter. This deserves
+                                                        * a symbolic constant and a formal interface documentation
+                                                        * at a later time.
+                                                        */
+                                                       LOG_DEBUG("request to step current core only");
+                                                       /* uncomment after checking that indeed other targets are safe */
+                                                       /*current_pc = 2;*/
+                                               }
+                                       }
                                }
                        }
 
-                       LOG_DEBUG("target %s single-step thread %"PRId64, target_name(ct), thread_id);
-                       retval = target_step(ct, 1, 0, handle_breakpoint);
+                       LOG_DEBUG("target %s single-step thread %"PRIx64, target_name(ct), thread_id);
+                       log_add_callback(gdb_log_callback, connection);
+                       target_call_event_callbacks(ct, TARGET_EVENT_GDB_START);
+
+                       /*
+                        * work around an annoying gdb behaviour: when the current thread
+                        * is changed in gdb, it assumes that the target can follow and also
+                        * make the thread current. This is an assumption that cannot hold
+                        * for a real target running a multi-threading OS. We just fake
+                        * the step to not trigger an internal error in gdb. See
+                        * https://sourceware.org/bugzilla/show_bug.cgi?id=22925 for details
+                        */
+                       if (fake_step) {
+                               int sig_reply_len;
+                               char sig_reply[128];
+
+                               LOG_DEBUG("fake step thread %"PRIx64, thread_id);
+
+                               sig_reply_len = snprintf(sig_reply, sizeof(sig_reply),
+                                                                                "T05thread:%016"PRIx64";", thread_id);
+
+                               gdb_put_packet(connection, sig_reply, sig_reply_len);
+                               log_remove_callback(gdb_log_callback, connection);
+
+                               return true;
+                       }
+
+                       /* support for gdb_sync command */
+                       if (gdb_connection->sync) {
+                               gdb_connection->sync = false;
+                               if (ct->state == TARGET_HALTED) {
+                                       LOG_WARNING("stepi ignored. GDB will now fetch the register state " \
+                                                                       "from the target.");
+                                       gdb_sig_halted(connection);
+                                       log_remove_callback(gdb_log_callback, connection);
+                               } else
+                                       gdb_connection->frontend_state = TARGET_RUNNING;
+                               return true;
+                       }
+
+                       retval = target_step(ct, current_pc, 0, 0);
+                       if (retval == ERROR_TARGET_NOT_HALTED)
+                               LOG_INFO("target %s was not halted when step was requested", target_name(ct));
+
+                       /* if step was successful send a reply back to gdb */
                        if (retval == ERROR_OK) {
-                               gdb_signal_reply(target, connection);
+                               retval = target_poll(ct);
+                               if (retval != ERROR_OK)
+                                       LOG_DEBUG("error polling target %s after successful step", target_name(ct));
+                               /* send back signal information */
+                               gdb_signal_reply(ct, connection);
                                /* stop forwarding log packets! */
                                log_remove_callback(gdb_log_callback, connection);
                        } else
-                       if (retval == ERROR_TARGET_TIMEOUT) {
                                gdb_connection->frontend_state = TARGET_RUNNING;
-                               target_call_event_callbacks(ct, TARGET_EVENT_GDB_START);
-                       }
                } else {
                        LOG_ERROR("Unknown vCont packet");
                        return false;
@@ -2800,9 +3018,12 @@ static int gdb_v_packet(struct connection *connection,
 
 static int gdb_detach(struct connection *connection)
 {
-       target_call_event_callbacks(get_target_from_connection(connection),
-               TARGET_EVENT_GDB_DETACH);
-
+       /*
+        * Only reply "OK" to GDB
+        * it will close the connection and this will trigger a call to
+        * gdb_connection_closed() that will in turn trigger the event
+        * TARGET_EVENT_GDB_DETACH
+        */
        return gdb_put_packet(connection, "OK", 2);
 }
 
@@ -3108,7 +3329,12 @@ static int gdb_input_inner(struct connection *connection)
 
                if (gdb_con->ctrl_c) {
                        if (target->state == TARGET_RUNNING) {
-                               retval = target_halt(target);
+                               struct target *t = target;
+                               if (target->rtos)
+                                       target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &t);
+                               retval = target_halt(t);
+                               if (retval == ERROR_OK)
+                                       retval = target_poll(t);
                                if (retval != ERROR_OK)
                                        target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT);
                                gdb_con->ctrl_c = 0;
@@ -3281,6 +3507,15 @@ COMMAND_HANDLER(handle_gdb_report_data_abort_command)
        return ERROR_OK;
 }
 
+COMMAND_HANDLER(handle_gdb_report_register_access_error)
+{
+       if (CMD_ARGC != 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       COMMAND_PARSE_ENABLE(CMD_ARGV[0], gdb_report_register_access_error);
+       return ERROR_OK;
+}
+
 /* gdb_breakpoint_override */
 COMMAND_HANDLER(handle_gdb_breakpoint_override_command)
 {
@@ -3402,6 +3637,13 @@ static const struct command_registration gdb_command_handlers[] = {
                .help = "enable or disable reporting data aborts",
                .usage = "('enable'|'disable')"
        },
+       {
+               .name = "gdb_report_register_access_error",
+               .handler = handle_gdb_report_register_access_error,
+               .mode = COMMAND_CONFIG,
+               .help = "enable or disable reporting register access errors",
+               .usage = "('enable'|'disable')"
+       },
        {
                .name = "gdb_breakpoint_override",
                .handler = handle_gdb_breakpoint_override_command,
@@ -3432,3 +3674,9 @@ int gdb_register_commands(struct command_context *cmd_ctx)
        gdb_port_next = strdup("3333");
        return register_commands(cmd_ctx, NULL, gdb_command_handlers);
 }
+
+void gdb_service_free(void)
+{
+       free(gdb_port);
+       free(gdb_port_next);
+}