+ struct gdb_connection *gdb_connection = connection->priv;
+
+ if (gdb_connection->mem_write_error)
+ retval = ERROR_FAIL;
+
+ if (retval == ERROR_OK) {
+ if (len >= fast_limit) {
+ /* By replying the packet *immediately* GDB will send us a new packet
+ * while we write the last one to the target.
+ * We only do this for larger writes, so that users who do something like:
+ * p *((int*)0xdeadbeef)=8675309
+ * will get immediate feedback that that write failed.
+ */
+ gdb_put_packet(connection, "OK", 2);
+ }
+ } else {
+ retval = gdb_error(connection, retval);
+ /* now that we have reported the memory write error, we can clear the condition */
+ gdb_connection->mem_write_error = false;
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
+ if (len) {
+ LOG_DEBUG("addr: 0x%" PRIx64 ", len: 0x%8.8" PRIx32 "", addr, len);
+
+ retval = target_write_buffer(target, addr, len, (uint8_t *)separator);
+ if (retval != ERROR_OK)
+ gdb_connection->mem_write_error = true;
+ }
+
+ if (len < fast_limit) {
+ if (retval != ERROR_OK) {
+ gdb_error(connection, retval);
+ gdb_connection->mem_write_error = false;
+ } else {
+ gdb_put_packet(connection, "OK", 2);
+ }
+ }
+
+ return ERROR_OK;
+}
+
+static int gdb_step_continue_packet(struct connection *connection,
+ char const *packet, int packet_size)
+{
+ struct target *target = get_target_from_connection(connection);
+ int current = 0;
+ uint64_t address = 0x0;
+ int retval = ERROR_OK;
+
+ LOG_DEBUG("-");
+
+ if (packet_size > 1)
+ address = strtoull(packet + 1, NULL, 16);
+ else
+ current = 1;
+
+ gdb_running_type = packet[0];
+ if (packet[0] == 'c') {
+ LOG_DEBUG("continue");
+ /* resume at current address, don't handle breakpoints, not debugging */
+ retval = target_resume(target, current, address, 0, 0);
+ } else if (packet[0] == 's') {
+ LOG_DEBUG("step");
+ /* step at current or address, don't handle breakpoints */
+ retval = target_step(target, current, address, 0);
+ }
+ return retval;
+}
+
+static int gdb_breakpoint_watchpoint_packet(struct connection *connection,
+ char const *packet, int packet_size)
+{
+ struct target *target = get_target_from_connection(connection);
+ int type;
+ enum breakpoint_type bp_type = BKPT_SOFT /* dummy init to avoid warning */;
+ enum watchpoint_rw wp_type = WPT_READ /* dummy init to avoid warning */;
+ uint64_t address;
+ uint32_t size;
+ char *separator;
+ int retval;
+
+ LOG_DEBUG("-");
+
+ type = strtoul(packet + 1, &separator, 16);
+
+ if (type == 0) /* memory breakpoint */
+ bp_type = BKPT_SOFT;
+ else if (type == 1) /* hardware breakpoint */
+ bp_type = BKPT_HARD;
+ else if (type == 2) /* write watchpoint */
+ wp_type = WPT_WRITE;
+ else if (type == 3) /* read watchpoint */
+ wp_type = WPT_READ;
+ else if (type == 4) /* access watchpoint */
+ wp_type = WPT_ACCESS;
+ else {
+ LOG_ERROR("invalid gdb watch/breakpoint type(%d), dropping connection", type);
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+
+ if (gdb_breakpoint_override && ((bp_type == BKPT_SOFT) || (bp_type == BKPT_HARD)))
+ bp_type = gdb_breakpoint_override_type;
+
+ if (*separator != ',') {
+ LOG_ERROR("incomplete breakpoint/watchpoint packet received, dropping connection");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+
+ address = strtoull(separator + 1, &separator, 16);
+
+ if (*separator != ',') {
+ LOG_ERROR("incomplete breakpoint/watchpoint packet received, dropping connection");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+
+ size = strtoul(separator + 1, &separator, 16);
+
+ switch (type) {
+ case 0:
+ case 1:
+ if (packet[0] == 'Z') {
+ retval = breakpoint_add(target, address, size, bp_type);
+ if (retval != ERROR_OK) {
+ retval = gdb_error(connection, retval);
+ if (retval != ERROR_OK)
+ return retval;
+ } else
+ gdb_put_packet(connection, "OK", 2);
+ } else {
+ breakpoint_remove(target, address);
+ gdb_put_packet(connection, "OK", 2);
+ }
+ break;
+ case 2:
+ case 3:
+ case 4:
+ {
+ if (packet[0] == 'Z') {
+ retval = watchpoint_add(target, address, size, wp_type, 0, 0xffffffffu);
+ if (retval != ERROR_OK) {
+ retval = gdb_error(connection, retval);
+ if (retval != ERROR_OK)
+ return retval;
+ } else
+ gdb_put_packet(connection, "OK", 2);
+ } else {
+ watchpoint_remove(target, address);
+ gdb_put_packet(connection, "OK", 2);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ return ERROR_OK;
+}
+
+/* print out a string and allocate more space as needed,
+ * mainly used for XML at this point
+ */
+static void xml_printf(int *retval, char **xml, int *pos, int *size,
+ const char *fmt, ...)
+{
+ if (*retval != ERROR_OK)
+ return;
+ int first = 1;
+
+ for (;; ) {
+ if ((*xml == NULL) || (!first)) {
+ /* start by 0 to exercise all the code paths.
+ * Need minimum 2 bytes to fit 1 char and 0 terminator. */
+
+ *size = *size * 2 + 2;
+ char *t = *xml;
+ *xml = realloc(*xml, *size);
+ if (*xml == NULL) {
+ if (t)
+ free(t);
+ *retval = ERROR_SERVER_REMOTE_CLOSED;
+ return;
+ }
+ }
+
+ va_list ap;
+ int ret;
+ va_start(ap, fmt);
+ ret = vsnprintf(*xml + *pos, *size - *pos, fmt, ap);
+ va_end(ap);
+ if ((ret > 0) && ((ret + 1) < *size - *pos)) {
+ *pos += ret;
+ return;
+ }
+ /* there was just enough or not enough space, allocate more. */
+ first = 0;
+ }
+}
+
+static int decode_xfer_read(char const *buf, char **annex, int *ofs, unsigned int *len)
+{
+ /* Locate the annex. */
+ const char *annex_end = strchr(buf, ':');
+ if (annex_end == NULL)
+ return ERROR_FAIL;
+
+ /* After the read marker and annex, qXfer looks like a
+ * traditional 'm' packet. */
+ char *separator;
+ *ofs = strtoul(annex_end + 1, &separator, 16);
+
+ if (*separator != ',')
+ return ERROR_FAIL;
+
+ *len = strtoul(separator + 1, NULL, 16);
+
+ /* Extract the annex if needed */
+ if (annex != NULL) {
+ *annex = strndup(buf, annex_end - buf);
+ if (*annex == NULL)
+ return ERROR_FAIL;
+ }
+
+ return ERROR_OK;
+}
+
+static int compare_bank(const void *a, const void *b)
+{
+ struct flash_bank *b1, *b2;
+ b1 = *((struct flash_bank **)a);
+ b2 = *((struct flash_bank **)b);
+
+ if (b1->base == b2->base)
+ return 0;
+ else if (b1->base > b2->base)
+ return 1;
+ else
+ return -1;
+}
+
+static int gdb_memory_map(struct connection *connection,
+ char const *packet, int packet_size)
+{
+ /* We get away with only specifying flash here. Regions that are not
+ * specified are treated as if we provided no memory map(if not we
+ * could detect the holes and mark them as RAM).
+ * Normally we only execute this code once, but no big deal if we
+ * have to regenerate it a couple of times.
+ */
+
+ struct target *target = get_target_from_connection(connection);
+ struct flash_bank *p;
+ char *xml = NULL;
+ int size = 0;
+ int pos = 0;
+ int retval = ERROR_OK;
+ struct flash_bank **banks;
+ int offset;
+ int length;
+ char *separator;
+ target_addr_t ram_start = 0;
+ int i;
+ int target_flash_banks = 0;
+
+ /* skip command character */
+ packet += 23;
+
+ offset = strtoul(packet, &separator, 16);
+ length = strtoul(separator + 1, &separator, 16);
+
+ xml_printf(&retval, &xml, &pos, &size, "<memory-map>\n");
+
+ /* 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).
+ */
+ banks = malloc(sizeof(struct flash_bank *)*flash_get_bank_count());
+
+ for (i = 0; i < flash_get_bank_count(); i++) {
+ p = get_flash_bank_by_num_noprobe(i);
+ if (p->target != target)
+ continue;
+ retval = get_flash_bank_by_num(i, &p);
+ if (retval != ERROR_OK) {
+ free(banks);
+ gdb_error(connection, retval);
+ return retval;
+ }
+ banks[target_flash_banks++] = p;
+ }
+
+ qsort(banks, target_flash_banks, sizeof(struct flash_bank *),
+ compare_bank);
+
+ for (i = 0; i < target_flash_banks; i++) {
+ int j;
+ unsigned sector_size = 0;
+ unsigned group_len = 0;
+
+ p = banks[i];
+
+ if (ram_start < p->base)
+ xml_printf(&retval, &xml, &pos, &size,
+ "<memory type=\"ram\" start=\"" TARGET_ADDR_FMT "\" "
+ "length=\"0x%x\"/>\n",
+ ram_start, p->base - ram_start);
+
+ /* Report adjacent groups of same-size sectors. So for
+ * example top boot CFI flash will list an initial region
+ * with several large sectors (maybe 128KB) and several
+ * smaller ones at the end (maybe 32KB). STR7 will have
+ * regions with 8KB, 32KB, and 64KB sectors; etc.
+ */
+ for (j = 0; j < p->num_sectors; j++) {
+
+ /* 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=\"" 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
+ && 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,
+ "length=\"0x%x\">\n"
+ "<property name=\"blocksize\">"
+ "0x%x</property>\n"
+ "</memory>\n",
+ group_len,
+ sector_size);
+ sector_size = 0;
+ }
+
+ ram_start = p->base + p->size;
+ }
+
+ if (ram_start != 0)
+ xml_printf(&retval, &xml, &pos, &size,
+ "<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
+ * space, in which case ram_start will be precisely 0
+ */
+
+ free(banks);
+
+ xml_printf(&retval, &xml, &pos, &size, "</memory-map>\n");
+
+ if (retval != ERROR_OK) {
+ free(xml);
+ gdb_error(connection, retval);
+ return retval;
+ }
+
+ if (offset + length > pos)
+ length = pos - offset;
+
+ char *t = malloc(length + 1);
+ t[0] = 'l';
+ memcpy(t + 1, xml + offset, length);
+ gdb_put_packet(connection, t, length + 1);
+
+ free(t);
+ free(xml);
+ return ERROR_OK;
+}
+
+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:
+ return "int8";
+ case REG_TYPE_INT16:
+ return "int16";
+ case REG_TYPE_INT32:
+ return "int32";
+ case REG_TYPE_INT64:
+ return "int64";
+ case REG_TYPE_INT128:
+ return "int128";
+ case REG_TYPE_UINT:
+ return "uint";
+ case REG_TYPE_UINT8:
+ return "uint8";
+ case REG_TYPE_UINT16:
+ return "uint16";
+ case REG_TYPE_UINT32:
+ return "uint32";
+ case REG_TYPE_UINT64:
+ return "uint64";
+ case REG_TYPE_UINT128:
+ return "uint128";
+ case REG_TYPE_CODE_PTR:
+ return "code_ptr";
+ case REG_TYPE_DATA_PTR:
+ return "data_ptr";
+ case REG_TYPE_FLOAT:
+ return "float";
+ case REG_TYPE_IEEE_SINGLE:
+ return "ieee_single";
+ case REG_TYPE_IEEE_DOUBLE:
+ return "ieee_double";
+ case REG_TYPE_ARCH_DEFINED:
+ return "int"; /* return arbitrary string to avoid compile warning. */
+ }
+
+ 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 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",
+ type->id, type->reg_type_vector->type->id,
+ 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> */
+ xml_printf(&retval, tdesc, pos, size,
+ "<union id=\"%s\">\n",
+ type->id);
+
+ field = type->reg_type_union->fields;
+ while (field != NULL) {
+ xml_printf(&retval, tdesc, pos, size,
+ "<field name=\"%s\" type=\"%s\"/>\n",
+ field->name, field->type->id);
+
+ field = field->next;
+ }
+
+ xml_printf(&retval, tdesc, pos, size,
+ "</union>\n");
+
+ } else if (type->type_class == REG_TYPE_CLASS_STRUCT) {
+ struct reg_data_type_struct_field *field;
+ field = type->reg_type_struct->fields;
+
+ if (field->use_bitfields) {
+ /* <struct id="id" size="size">
+ * <field name="name" start="start" end="end"/> ...
+ * </struct> */
+ xml_printf(&retval, tdesc, pos, size,
+ "<struct id=\"%s\" size=\"%d\">\n",
+ type->id, type->reg_type_struct->size);
+ while (field != NULL) {
+ xml_printf(&retval, tdesc, pos, size,
+ "<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> */
+ xml_printf(&retval, tdesc, pos, size,
+ "<struct id=\"%s\">\n",
+ type->id);
+ while (field != NULL) {
+ xml_printf(&retval, tdesc, pos, size,
+ "<field name=\"%s\" type=\"%s\"/>\n",
+ field->name, field->type->id);
+
+ field = field->next;
+ }
+ }
+
+ xml_printf(&retval, tdesc, pos, size,
+ "</struct>\n");
+
+ } else if (type->type_class == REG_TYPE_CLASS_FLAGS) {
+ /* <flags id="id" size="size">
+ * <field name="name" start="start" end="end"/> ...
+ * </flags> */
+ xml_printf(&retval, tdesc, pos, size,
+ "<flags id=\"%s\" size=\"%d\">\n",
+ type->id, type->reg_type_flags->size);
+
+ struct reg_data_type_flags_field *field;
+ field = type->reg_type_flags->fields;
+ while (field != NULL) {
+ xml_printf(&retval, tdesc, pos, size,
+ "<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;
+ }
+
+ xml_printf(&retval, tdesc, pos, size,
+ "</flags>\n");
+
+ }
+
+ return ERROR_OK;
+}
+
+/* Get a list of available target registers features. feature_list must
+ * be freed by caller.
+ */
+static int get_reg_features_list(struct target *target, char const **feature_list[], int *feature_list_size,
+ struct reg **reg_list, int reg_list_size)
+{
+ int tbl_sz = 0;
+
+ /* Start with only one element */
+ *feature_list = calloc(1, sizeof(char *));
+
+ for (int i = 0; i < reg_list_size; i++) {
+ if (reg_list[i]->exist == false)
+ continue;
+
+ if (reg_list[i]->feature != NULL
+ && reg_list[i]->feature->name != NULL
+ && (strcmp(reg_list[i]->feature->name, ""))) {
+ /* We found a feature, check if the feature is already in the
+ * table. If not, allocate a new entry for the table and
+ * put the new feature in it.
+ */
+ for (int j = 0; j < (tbl_sz + 1); j++) {
+ if (!((*feature_list)[j])) {
+ (*feature_list)[tbl_sz++] = reg_list[i]->feature->name;
+ *feature_list = realloc(*feature_list, sizeof(char *) * (tbl_sz + 1));
+ (*feature_list)[tbl_sz] = NULL;
+ break;
+ } else {
+ if (!strcmp((*feature_list)[j], reg_list[i]->feature->name))
+ break;
+ }
+ }
+ }
+ }
+
+ if (feature_list_size)
+ *feature_list_size = tbl_sz;
+
+ return ERROR_OK;
+}
+
+static int gdb_generate_target_description(struct target *target, char **tdesc_out)
+{
+ int retval = ERROR_OK;
+ 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, ®_list,
+ ®_list_size, REG_CLASS_ALL);
+
+ if (retval != ERROR_OK) {
+ LOG_ERROR("get register list failed");
+ retval = ERROR_FAIL;
+ goto error;
+ }
+
+ if (reg_list_size <= 0) {
+ LOG_ERROR("get register list failed");
+ retval = ERROR_FAIL;
+ goto error;
+ }
+
+ /* Get a list of available target registers features */
+ retval = get_reg_features_list(target, &features, &feature_list_size, reg_list, reg_list_size);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Can't get the registers feature list");
+ retval = ERROR_FAIL;
+ goto error;
+ }
+
+ /* If we found some features associated with registers, create sections */
+ int current_feature = 0;
+
+ xml_printf(&retval, &tdesc, &pos, &size,
+ "<?xml version=\"1.0\"?>\n"
+ "<!DOCTYPE target SYSTEM \"gdb-target.dtd\">\n"
+ "<target version=\"1.0\">\n");
+
+ /* generate target description according to register list */
+ if (features != NULL) {
+ while (features[current_feature]) {
+
+ xml_printf(&retval, &tdesc, &pos, &size,
+ "<feature name=\"%s\">\n",
+ features[current_feature]);
+
+ int i;
+ for (i = 0; i < reg_list_size; i++) {
+
+ if (reg_list[i]->exist == false)
+ continue;
+
+ if (strcmp(reg_list[i]->feature->name, features[current_feature]))
+ continue;
+
+ const char *type_str;
+ 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. */
+ 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 {
+ /* predefined type */
+ type_str = gdb_get_reg_type_name(
+ reg_list[i]->reg_data_type->type);
+ }
+ } else {
+ /* Default type is "int" */
+ type_str = "int";
+ }
+
+ xml_printf(&retval, &tdesc, &pos, &size,
+ "<reg name=\"%s\"", reg_list[i]->name);
+ xml_printf(&retval, &tdesc, &pos, &size,
+ " bitsize=\"%d\"", reg_list[i]->size);
+ xml_printf(&retval, &tdesc, &pos, &size,
+ " regnum=\"%d\"", reg_list[i]->number);
+ if (reg_list[i]->caller_save)
+ xml_printf(&retval, &tdesc, &pos, &size,
+ " save-restore=\"yes\"");
+ else
+ xml_printf(&retval, &tdesc, &pos, &size,
+ " save-restore=\"no\"");
+
+ xml_printf(&retval, &tdesc, &pos, &size,
+ " type=\"%s\"", type_str);
+
+ if (reg_list[i]->group != NULL)
+ xml_printf(&retval, &tdesc, &pos, &size,
+ " group=\"%s\"", reg_list[i]->group);
+
+ xml_printf(&retval, &tdesc, &pos, &size,
+ "/>\n");
+ }
+
+ xml_printf(&retval, &tdesc, &pos, &size,
+ "</feature>\n");
+
+ current_feature++;
+ }
+ }
+
+ xml_printf(&retval, &tdesc, &pos, &size,
+ "</target>\n");
+
+error:
+ free(features);
+ free(reg_list);
+ free(arch_defined_types);
+
+ if (retval == ERROR_OK)
+ *tdesc_out = tdesc;
+ else
+ free(tdesc);
+
+ return retval;
+}
+
+static int gdb_get_target_description_chunk(struct target *target, struct target_desc_format *target_desc,
+ char **chunk, int32_t offset, uint32_t length)
+{
+ if (target_desc == NULL) {
+ LOG_ERROR("Unable to Generate Target Description");
+ return ERROR_FAIL;
+ }
+
+ char *tdesc = target_desc->tdesc;
+ uint32_t tdesc_length = target_desc->tdesc_length;
+
+ if (tdesc == NULL) {
+ int retval = gdb_generate_target_description(target, &tdesc);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Unable to Generate Target Description");
+ return ERROR_FAIL;
+ }
+
+ tdesc_length = strlen(tdesc);
+ }
+
+ char transfer_type;
+
+ if (length < (tdesc_length - offset))
+ transfer_type = 'm';
+ else
+ transfer_type = 'l';
+
+ *chunk = malloc(length + 2);
+ if (*chunk == NULL) {
+ LOG_ERROR("Unable to allocate memory");
+ return ERROR_FAIL;
+ }
+
+ (*chunk)[0] = transfer_type;
+ if (transfer_type == 'm') {
+ strncpy((*chunk) + 1, tdesc + offset, length);
+ (*chunk)[1 + length] = '\0';
+ } else {
+ strncpy((*chunk) + 1, tdesc + offset, tdesc_length - offset);
+ (*chunk)[1 + (tdesc_length - offset)] = '\0';
+
+ /* After gdb-server sends out last chunk, invalidate tdesc. */
+ free(tdesc);
+ tdesc = NULL;
+ tdesc_length = 0;
+ }
+
+ target_desc->tdesc = tdesc;
+ target_desc->tdesc_length = tdesc_length;
+
+ return ERROR_OK;
+}
+
+static int gdb_target_description_supported(struct target *target, int *supported)
+{
+ int retval = ERROR_OK;
+ struct reg **reg_list = NULL;
+ int reg_list_size = 0;
+ char const **features = NULL;
+ int feature_list_size = 0;
+
+ retval = target_get_gdb_reg_list(target, ®_list,
+ ®_list_size, REG_CLASS_ALL);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("get register list failed");
+ goto error;
+ }
+
+ if (reg_list_size <= 0) {
+ LOG_ERROR("get register list failed");
+ retval = ERROR_FAIL;
+ goto error;
+ }
+
+ /* Get a list of available target registers features */
+ retval = get_reg_features_list(target, &features, &feature_list_size, reg_list, reg_list_size);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Can't get the registers feature list");
+ goto error;
+ }
+
+ if (supported) {
+ if (feature_list_size)
+ *supported = 1;
+ else
+ *supported = 0;
+ }
+
+error:
+ free(features);
+
+ free(reg_list);
+
+ return retval;
+}
+
+static int gdb_generate_thread_list(struct target *target, char **thread_list_out)
+{
+ struct rtos *rtos = target->rtos;
+ int retval = ERROR_OK;
+ char *thread_list = NULL;
+ int pos = 0;
+ int size = 0;
+
+ xml_printf(&retval, &thread_list, &pos, &size,
+ "<?xml version=\"1.0\"?>\n"
+ "<threads>\n");
+
+ if (rtos != NULL) {
+ for (int i = 0; i < rtos->thread_count; i++) {
+ struct thread_detail *thread_detail = &rtos->thread_details[i];
+
+ if (!thread_detail->exists)
+ continue;
+
+ xml_printf(&retval, &thread_list, &pos, &size,
+ "<thread id=\"%" PRIx64 "\">", thread_detail->threadid);
+
+ if (thread_detail->thread_name_str != NULL)
+ xml_printf(&retval, &thread_list, &pos, &size,
+ "Name: %s", thread_detail->thread_name_str);
+
+ if (thread_detail->extra_info_str != NULL) {
+ if (thread_detail->thread_name_str != NULL)
+ xml_printf(&retval, &thread_list, &pos, &size,
+ ", ");
+ xml_printf(&retval, &thread_list, &pos, &size,
+ thread_detail->extra_info_str);
+ }
+
+ xml_printf(&retval, &thread_list, &pos, &size,
+ "</thread>\n");
+ }
+ }
+
+ xml_printf(&retval, &thread_list, &pos, &size,
+ "</threads>\n");
+
+ if (retval == ERROR_OK)
+ *thread_list_out = thread_list;
+ else
+ free(thread_list);
+
+ return retval;
+}
+
+static int gdb_get_thread_list_chunk(struct target *target, char **thread_list,
+ char **chunk, int32_t offset, uint32_t length)
+{
+ if (*thread_list == NULL) {
+ int retval = gdb_generate_thread_list(target, thread_list);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Unable to Generate Thread List");
+ return ERROR_FAIL;
+ }
+ }
+
+ size_t thread_list_length = strlen(*thread_list);
+ char transfer_type;
+
+ length = MIN(length, thread_list_length - offset);
+ if (length < (thread_list_length - offset))
+ transfer_type = 'm';
+ else
+ transfer_type = 'l';
+
+ *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;
+ }
+
+ (*chunk)[0] = transfer_type;
+ strncpy((*chunk) + 1, (*thread_list) + offset, length);
+ (*chunk)[1 + length] = '\0';
+
+ /* After gdb-server sends out last chunk, invalidate thread list. */
+ if (transfer_type == 'l') {
+ free(*thread_list);
+ *thread_list = NULL;
+ }
+
+ return ERROR_OK;
+}
+
+static int gdb_query_packet(struct connection *connection,
+ char const *packet, int packet_size)
+{
+ struct command_context *cmd_ctx = connection->cmd_ctx;
+ struct gdb_connection *gdb_connection = connection->priv;
+ struct target *target = get_target_from_connection(connection);
+
+ if (strncmp(packet, "qRcmd,", 6) == 0) {
+ if (packet_size > 6) {
+ char *cmd;
+ cmd = malloc((packet_size - 6) / 2 + 1);
+ size_t len = unhexify((uint8_t *)cmd, packet + 6, (packet_size - 6) / 2);
+ cmd[len] = 0;
+
+ /* We want to print all debug output to GDB connection */
+ log_add_callback(gdb_log_callback, connection);
+ target_call_timer_callbacks_now();
+ /* some commands need to know the GDB connection, make note of current
+ * GDB connection. */
+ current_gdb_connection = gdb_connection;
+ command_run_line(cmd_ctx, cmd);
+ current_gdb_connection = NULL;
+ target_call_timer_callbacks_now();
+ log_remove_callback(gdb_log_callback, connection);
+ free(cmd);
+ }
+ gdb_put_packet(connection, "OK", 2);
+ return ERROR_OK;
+ } else if (strncmp(packet, "qCRC:", 5) == 0) {
+ if (packet_size > 5) {
+ int retval;
+ char gdb_reply[10];
+ char *separator;
+ uint32_t checksum;
+ target_addr_t addr = 0;
+ uint32_t len = 0;
+
+ /* skip command character */
+ packet += 5;
+
+ addr = strtoull(packet, &separator, 16);
+
+ if (*separator != ',') {
+ LOG_ERROR("incomplete read memory packet received, dropping connection");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+
+ len = strtoul(separator + 1, NULL, 16);
+
+ retval = target_checksum_memory(target, addr, len, &checksum);
+
+ if (retval == ERROR_OK) {
+ snprintf(gdb_reply, 10, "C%8.8" PRIx32 "", checksum);
+ gdb_put_packet(connection, gdb_reply, 9);
+ } else {
+ retval = gdb_error(connection, retval);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
+ return ERROR_OK;
+ }
+ } else if (strncmp(packet, "qSupported", 10) == 0) {
+ /* we currently support packet size and qXfer:memory-map:read (if enabled)
+ * qXfer:features:read is supported for some targets */
+ int retval = ERROR_OK;
+ char *buffer = NULL;
+ int pos = 0;
+ int size = 0;
+ int gdb_target_desc_supported = 0;
+
+ /* we need to test that the target supports target descriptions */
+ retval = gdb_target_description_supported(target, &gdb_target_desc_supported);
+ if (retval != ERROR_OK) {
+ LOG_INFO("Failed detecting Target Description Support, disabling");
+ gdb_target_desc_supported = 0;
+ }
+
+ /* support may be disabled globally */
+ if (gdb_use_target_description == 0) {
+ if (gdb_target_desc_supported)
+ LOG_WARNING("Target Descriptions Supported, but disabled");
+ gdb_target_desc_supported = 0;
+ }
+
+ xml_printf(&retval,
+ &buffer,
+ &pos,
+ &size,
+ "PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read%c;qXfer:threads:read+;QStartNoAckMode+;vContSupported+",
+ (GDB_BUFFER_SIZE - 1),
+ ((gdb_use_memory_map == 1) && (flash_get_bank_count() > 0)) ? '+' : '-',
+ (gdb_target_desc_supported == 1) ? '+' : '-');
+
+ if (retval != ERROR_OK) {
+ gdb_send_error(connection, 01);
+ return ERROR_OK;
+ }
+
+ gdb_put_packet(connection, buffer, strlen(buffer));
+ free(buffer);
+
+ return ERROR_OK;
+ } else if ((strncmp(packet, "qXfer:memory-map:read::", 23) == 0)
+ && (flash_get_bank_count() > 0))
+ return gdb_memory_map(connection, packet, packet_size);
+ else if (strncmp(packet, "qXfer:features:read:", 20) == 0) {
+ char *xml = NULL;
+ int retval = ERROR_OK;
+
+ int offset;
+ unsigned int length;
+
+ /* skip command character */
+ packet += 20;
+
+ if (decode_xfer_read(packet, NULL, &offset, &length) < 0) {
+ gdb_send_error(connection, 01);
+ return ERROR_OK;
+ }
+
+ /* Target should prepare correct target description for annex.
+ * The first character of returned xml is 'm' or 'l'. 'm' for
+ * there are *more* chunks to transfer. 'l' for it is the *last*
+ * chunk of target description.
+ */
+ retval = gdb_get_target_description_chunk(target, &gdb_connection->target_desc,
+ &xml, offset, length);
+ if (retval != ERROR_OK) {
+ gdb_error(connection, retval);
+ return retval;
+ }
+
+ gdb_put_packet(connection, xml, strlen(xml));
+
+ free(xml);
+ return ERROR_OK;
+ } else if (strncmp(packet, "qXfer:threads:read:", 19) == 0) {
+ char *xml = NULL;
+ int retval = ERROR_OK;
+
+ int offset;
+ unsigned int length;
+
+ /* skip command character */
+ packet += 19;
+
+ if (decode_xfer_read(packet, NULL, &offset, &length) < 0) {
+ gdb_send_error(connection, 01);
+ return ERROR_OK;
+ }
+
+ /* Target should prepare correct thread list for annex.
+ * The first character of returned xml is 'm' or 'l'. 'm' for
+ * there are *more* chunks to transfer. 'l' for it is the *last*
+ * chunk of target description.
+ */
+ retval = gdb_get_thread_list_chunk(target, &gdb_connection->thread_list,
+ &xml, offset, length);
+ if (retval != ERROR_OK) {
+ gdb_error(connection, retval);
+ return retval;
+ }
+
+ gdb_put_packet(connection, xml, strlen(xml));
+
+ free(xml);
+ return ERROR_OK;
+ } else if (strncmp(packet, "QStartNoAckMode", 15) == 0) {
+ gdb_connection->noack_mode = 1;
+ gdb_put_packet(connection, "OK", 2);
+ return ERROR_OK;
+ }
+
+ gdb_put_packet(connection, "", 0);
+ return ERROR_OK;
+}
+
+static bool gdb_handle_vcont_packet(struct connection *connection, const char *packet, int packet_size)
+{
+ struct gdb_connection *gdb_connection = connection->priv;
+ struct target *target = get_target_from_connection(connection);
+ const char *parse = packet;
+ int retval;
+
+ /* query for vCont supported */
+ if (parse[0] == '?') {
+ if (target->type->step != NULL) {
+ /* gdb doesn't accept c without C and s without S */
+ gdb_put_packet(connection, "vCont;c;C;s;S", 13);
+ return true;
+ }
+ return false;
+ }
+
+ if (parse[0] == ';') {
+ ++parse;
+ --packet_size;
+ }
+
+ /* 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_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) {
+ struct target *ct = target;
+ int current_pc = 1;
+ int64_t thread_id;
+ char *endp;
+
+ parse += 2;
+ packet_size -= 2;
+
+ thread_id = strtoll(parse, &endp, 16);
+ if (endp != NULL) {
+ packet_size -= endp - parse;
+ 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;
+
+ if (parse[0] == 'c') {
+ parse += 1;
+ packet_size -= 1;
+
+ /* 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 %"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) {
+ 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
+ gdb_connection->frontend_state = TARGET_RUNNING;
+ } else {
+ LOG_ERROR("Unknown vCont packet");
+ return false;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+static int gdb_v_packet(struct connection *connection,
+ char const *packet, int packet_size)
+{
+ struct gdb_connection *gdb_connection = connection->priv;
+ struct target *target;
+ int result;
+
+ target = get_target_from_connection(connection);
+
+ if (strncmp(packet, "vCont", 5) == 0) {
+ bool handled;
+
+ packet += 5;
+ packet_size -= 5;
+
+ handled = gdb_handle_vcont_packet(connection, packet, packet_size);
+ if (!handled)
+ gdb_put_packet(connection, "", 0);
+
+ return ERROR_OK;
+ }
+
+ /* if flash programming disabled - send a empty reply */
+
+ if (gdb_flash_program == 0) {
+ gdb_put_packet(connection, "", 0);
+ return ERROR_OK;
+ }
+
+ if (strncmp(packet, "vFlashErase:", 12) == 0) {
+ unsigned long addr;
+ unsigned long length;
+
+ char const *parse = packet + 12;
+ if (*parse == '\0') {
+ LOG_ERROR("incomplete vFlashErase packet received, dropping connection");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+
+ addr = strtoul(parse, (char **)&parse, 16);
+
+ if (*(parse++) != ',' || *parse == '\0') {
+ LOG_ERROR("incomplete vFlashErase packet received, dropping connection");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+
+ length = strtoul(parse, (char **)&parse, 16);
+
+ if (*parse != '\0') {
+ LOG_ERROR("incomplete vFlashErase packet received, dropping connection");
+ return ERROR_SERVER_REMOTE_CLOSED;