Jon Doron <ari...@gmail.com> writes:
> The generic set/query packets contains implementation for varioius > sub-commands which are required for GDB and also additional commands > which are QEMU specific. > > To see which QEMU specific commands are available use the command > gdb> maintenance packet qqemu.Supported > > Currently the only implemented QEMU specific command is the command > that sets the single step behavior. > > gdb> maintenance packet qqemu.sstepbits > Will display the MASK bits used to control the single stepping. > > gdb> maintenance packet qqemu.sstep > Will display the current value of the mask used when single stepping. > > gdb> maintenance packet Qqemu.sstep:HEX_VALUE > Will change the single step mask. > > Signed-off-by: Jon Doron <ari...@gmail.com> Reviewed-by: Alex Bennée <alex.ben...@linaro.org> Tested-by: Alex Bennée <alex.ben...@linaro.org> > --- > gdbstub.c | 559 ++++++++++++++++++++++++++++++++++++------------------ > 1 file changed, 373 insertions(+), 186 deletions(-) > > diff --git a/gdbstub.c b/gdbstub.c > index 648191a3b0..80fe5b2d0c 100644 > --- a/gdbstub.c > +++ b/gdbstub.c > @@ -1130,14 +1130,6 @@ static GDBThreadIdKind read_thread_id(const char *buf, > const char **end_buf, > return GDB_ONE_THREAD; > } > > -static int is_query_packet(const char *p, const char *query, char separator) > -{ > - unsigned int query_len = strlen(query); > - > - return strncmp(p, query, query_len) == 0 && > - (p[query_len] == '\0' || p[query_len] == separator); > -} > - > /** > * gdb_handle_vcont - Parses and handles a vCont packet. > * returns -ENOTSUP if a command is unsupported, -EINVAL or -ERANGE if there > is > @@ -1918,18 +1910,368 @@ static void handle_v_commands(GdbCmdContext > *gdb_ctx, void *user_ctx) > } > } > > -static int gdb_handle_packet(GDBState *s, const char *line_buf) > +static void handle_query_qemu_sstepbits(GdbCmdContext *gdb_ctx, void > *user_ctx) > +{ > + snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), > + "ENABLE=%x,NOIRQ=%x,NOTIMER=%x", SSTEP_ENABLE, > + SSTEP_NOIRQ, SSTEP_NOTIMER); > + put_packet(gdb_ctx->s, gdb_ctx->str_buf); > +} > + > +static void handle_set_qemu_sstep(GdbCmdContext *gdb_ctx, void *user_ctx) > +{ > + if (!gdb_ctx->num_params) { > + return; > + } > + > + sstep_flags = gdb_ctx->params[0].val_ul; > + put_packet(gdb_ctx->s, "OK"); > +} > + > +static void handle_query_qemu_sstep(GdbCmdContext *gdb_ctx, void *user_ctx) > +{ > + snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "0x%x", > sstep_flags); > + put_packet(gdb_ctx->s, gdb_ctx->str_buf); > +} > + > +static void handle_query_curr_tid(GdbCmdContext *gdb_ctx, void *user_ctx) > { > CPUState *cpu; > GDBProcess *process; > + char thread_id[16]; > + > + /* > + * "Current thread" remains vague in the spec, so always return > + * the first thread of the current process (gdb returns the > + * first thread). > + */ > + process = gdb_get_cpu_process(gdb_ctx->s, gdb_ctx->s->g_cpu); > + cpu = get_first_cpu_in_process(gdb_ctx->s, process); > + gdb_fmt_thread_id(gdb_ctx->s, cpu, thread_id, sizeof(thread_id)); > + snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "QC%s", thread_id); > + put_packet(gdb_ctx->s, gdb_ctx->str_buf); > +} > + > +static void handle_query_threads(GdbCmdContext *gdb_ctx, void *user_ctx) > +{ > + char thread_id[16]; > + > + if (!gdb_ctx->s->query_cpu) { > + put_packet(gdb_ctx->s, "l"); > + return; > + } > + > + gdb_fmt_thread_id(gdb_ctx->s, gdb_ctx->s->query_cpu, thread_id, > + sizeof(thread_id)); > + snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "m%s", thread_id); > + put_packet(gdb_ctx->s, gdb_ctx->str_buf); > + gdb_ctx->s->query_cpu = > + gdb_next_attached_cpu(gdb_ctx->s, gdb_ctx->s->query_cpu); > +} > + > +static void handle_query_first_threads(GdbCmdContext *gdb_ctx, void > *user_ctx) > +{ > + gdb_ctx->s->query_cpu = gdb_first_attached_cpu(gdb_ctx->s); > + handle_query_threads(gdb_ctx, user_ctx); > +} > + > +static void handle_query_thread_extra(GdbCmdContext *gdb_ctx, void *user_ctx) > +{ > + CPUState *cpu; > + int len; > + > + if (!gdb_ctx->num_params || > + gdb_ctx->params[0].thread_id.kind == GDB_READ_THREAD_ERR) { > + put_packet(gdb_ctx->s, "E22"); > + return; > + } > + > + cpu = gdb_get_cpu(gdb_ctx->s, gdb_ctx->params[0].thread_id.pid, > + gdb_ctx->params[0].thread_id.tid); > + if (!cpu) { > + return; > + } > + > + cpu_synchronize_state(cpu); > + > + if (gdb_ctx->s->multiprocess && (gdb_ctx->s->process_num > 1)) { > + /* Print the CPU model and name in multiprocess mode */ > + ObjectClass *oc = object_get_class(OBJECT(cpu)); > + const char *cpu_model = object_class_get_name(oc); > + char *cpu_name = object_get_canonical_path_component(OBJECT(cpu)); > + len = snprintf((char *)gdb_ctx->mem_buf, sizeof(gdb_ctx->str_buf) / > 2, > + "%s %s [%s]", cpu_model, cpu_name, > + cpu->halted ? "halted " : "running"); > + g_free(cpu_name); > + } else { > + /* memtohex() doubles the required space */ > + len = snprintf((char *)gdb_ctx->mem_buf, sizeof(gdb_ctx->str_buf) / > 2, > + "CPU#%d [%s]", cpu->cpu_index, > + cpu->halted ? "halted " : "running"); > + } > + trace_gdbstub_op_extra_info((char *)gdb_ctx->mem_buf); > + memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, len); > + put_packet(gdb_ctx->s, gdb_ctx->str_buf); > +} > + > +#ifdef CONFIG_USER_ONLY > +static void handle_query_offsets(GdbCmdContext *gdb_ctx, void *user_ctx) > +{ > + TaskState *ts; > + > + ts = gdb_ctx->s->c_cpu->opaque; > + snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), > + "Text=" TARGET_ABI_FMT_lx ";Data=" TARGET_ABI_FMT_lx > + ";Bss=" TARGET_ABI_FMT_lx, > + ts->info->code_offset, > + ts->info->data_offset, > + ts->info->data_offset); > + put_packet(gdb_ctx->s, gdb_ctx->str_buf); > +} > +#else > +static void handle_query_rcmd(GdbCmdContext *gdb_ctx, void *user_ctx) > +{ > + int len; > + > + if (!gdb_ctx->num_params) { > + put_packet(gdb_ctx->s, "E22"); > + return; > + } > + > + len = strlen(gdb_ctx->params[0].data); > + if (len % 2) { > + put_packet(gdb_ctx->s, "E01"); > + return; > + } > + > + len = len / 2; > + hextomem(gdb_ctx->mem_buf, gdb_ctx->params[0].data, len); > + gdb_ctx->mem_buf[len++] = 0; > + qemu_chr_be_write(gdb_ctx->s->mon_chr, gdb_ctx->mem_buf, len); > + put_packet(gdb_ctx->s, "OK"); > + > +} > +#endif > + > +static void handle_query_supported(GdbCmdContext *gdb_ctx, void *user_ctx) > +{ > CPUClass *cc; > + > + snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "PacketSize=%x", > + MAX_PACKET_LENGTH); > + cc = CPU_GET_CLASS(first_cpu); > + if (cc->gdb_core_xml_file) { > + pstrcat(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), > + ";qXfer:features:read+"); > + } > + > + if (gdb_ctx->num_params && > + strstr(gdb_ctx->params[0].data, "multiprocess+")) { > + gdb_ctx->s->multiprocess = true; > + } > + > + pstrcat(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), ";multiprocess+"); > + put_packet(gdb_ctx->s, gdb_ctx->str_buf); > +} > + > +static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void > *user_ctx) > +{ > + GDBProcess *process; > + CPUClass *cc; > + unsigned long len, total_len, addr; > + const char *xml; > const char *p; > - uint32_t pid, tid; > - int ch, type; > + > + if (gdb_ctx->num_params < 3) { > + put_packet(gdb_ctx->s, "E22"); > + return; > + } > + > + process = gdb_get_cpu_process(gdb_ctx->s, gdb_ctx->s->g_cpu); > + cc = CPU_GET_CLASS(gdb_ctx->s->g_cpu); > + if (!cc->gdb_core_xml_file) { > + put_packet(gdb_ctx->s, ""); > + return; > + } > + > + gdb_has_xml = true; > + p = gdb_ctx->params[0].data; > + xml = get_feature_xml(gdb_ctx->s, p, &p, process); > + if (!xml) { > + put_packet(gdb_ctx->s, "E00"); > + return; > + } > + > + addr = gdb_ctx->params[1].val_ul; > + len = gdb_ctx->params[2].val_ul; > + total_len = strlen(xml); > + if (addr > total_len) { > + put_packet(gdb_ctx->s, "E00"); > + return; > + } > + > + if (len > (MAX_PACKET_LENGTH - 5) / 2) { > + len = (MAX_PACKET_LENGTH - 5) / 2; > + } > + > + if (len < total_len - addr) { > + gdb_ctx->str_buf[0] = 'm'; > + len = memtox(gdb_ctx->str_buf + 1, xml + addr, len); > + } else { > + gdb_ctx->str_buf[0] = 'l'; > + len = memtox(gdb_ctx->str_buf + 1, xml + addr, total_len - addr); > + } > + > + put_packet_binary(gdb_ctx->s, gdb_ctx->str_buf, len + 1, true); > +} > + > +static void handle_query_attached(GdbCmdContext *gdb_ctx, void *user_ctx) > +{ > + put_packet(gdb_ctx->s, GDB_ATTACHED); > +} > + > +static void handle_query_qemu_supported(GdbCmdContext *gdb_ctx, void > *user_ctx) > +{ > + put_packet(gdb_ctx->s, "sstepbits;sstep"); > +} > + > +static GdbCmdParseEntry gdb_gen_query_set_common_table[] = { > + /* Order is important if has same prefix */ > + { > + .handler = handle_query_qemu_sstepbits, > + .cmd = "qemu.sstepbits", > + }, > + { > + .handler = handle_query_qemu_sstep, > + .cmd = "qemu.sstep", > + }, > + { > + .handler = handle_set_qemu_sstep, > + .cmd = "qemu.sstep=", > + .cmd_startswith = 1, > + .schema = "l0" > + }, > +}; > + > +static GdbCmdParseEntry gdb_gen_query_table[] = { > + { > + .handler = handle_query_curr_tid, > + .cmd = "C", > + }, > + { > + .handler = handle_query_threads, > + .cmd = "sThreadInfo", > + }, > + { > + .handler = handle_query_first_threads, > + .cmd = "fThreadInfo", > + }, > + { > + .handler = handle_query_thread_extra, > + .cmd = "ThreadExtraInfo,", > + .cmd_startswith = 1, > + .schema = "t0" > + }, > +#ifdef CONFIG_USER_ONLY > + { > + .handler = handle_query_offsets, > + .cmd = "Offsets", > + }, > +#else > + { > + .handler = handle_query_rcmd, > + .cmd = "Rcmd,", > + .cmd_startswith = 1, > + .schema = "s0" > + }, > +#endif > + { > + .handler = handle_query_supported, > + .cmd = "Supported:", > + .cmd_startswith = 1, > + .schema = "s0" > + }, > + { > + .handler = handle_query_supported, > + .cmd = "Supported", > + .schema = "s0" > + }, > + { > + .handler = handle_query_xfer_features, > + .cmd = "Xfer:features:read:", > + .cmd_startswith = 1, > + .schema = "s:l,l0" > + }, > + { > + .handler = handle_query_attached, > + .cmd = "Attached:", > + .cmd_startswith = 1 > + }, > + { > + .handler = handle_query_attached, > + .cmd = "Attached", > + }, > + { > + .handler = handle_query_qemu_supported, > + .cmd = "qemu.Supported", > + }, > +}; > + > +static GdbCmdParseEntry gdb_gen_set_table[] = { > + /* Order is important if has same prefix */ > + { > + .handler = handle_set_qemu_sstep, > + .cmd = "qemu.sstep:", > + .cmd_startswith = 1, > + .schema = "l0" > + }, > +}; > + > +static void handle_gen_query(GdbCmdContext *gdb_ctx, void *user_ctx) > +{ > + if (!gdb_ctx->num_params) { > + return; > + } > + > + if (!process_string_cmd(gdb_ctx->s, NULL, gdb_ctx->params[0].data, > + gdb_gen_query_set_common_table, > + ARRAY_SIZE(gdb_gen_query_set_common_table))) { > + return; > + } > + > + if (process_string_cmd(gdb_ctx->s, NULL, gdb_ctx->params[0].data, > + gdb_gen_query_table, > + ARRAY_SIZE(gdb_gen_query_table))) { > + put_packet(gdb_ctx->s, ""); > + } > +} > + > +static void handle_gen_set(GdbCmdContext *gdb_ctx, void *user_ctx) > +{ > + if (!gdb_ctx->num_params) { > + return; > + } > + > + if (!process_string_cmd(gdb_ctx->s, NULL, gdb_ctx->params[0].data, > + gdb_gen_query_set_common_table, > + ARRAY_SIZE(gdb_gen_query_set_common_table))) { > + return; > + } > + > + if (process_string_cmd(gdb_ctx->s, NULL, gdb_ctx->params[0].data, > + gdb_gen_set_table, > + ARRAY_SIZE(gdb_gen_set_table))) { > + put_packet(gdb_ctx->s, ""); > + } > +} > + > +static int gdb_handle_packet(GDBState *s, const char *line_buf) > +{ > + const char *p; > + int ch; > uint8_t mem_buf[MAX_PACKET_LENGTH]; > char buf[sizeof(mem_buf) + 1 /* trailing NUL */]; > char thread_id[16]; > - target_ulong addr, len; > const GdbCmdParseEntry *cmd_parser = NULL; > > trace_gdbstub_io_command(line_buf); > @@ -2131,183 +2473,28 @@ static int gdb_handle_packet(GDBState *s, const char > *line_buf) > } > break; > case 'q': > - case 'Q': > - /* parse any 'q' packets here */ > - if (!strcmp(p,"qemu.sstepbits")) { > - /* Query Breakpoint bit definitions */ > - snprintf(buf, sizeof(buf), "ENABLE=%x,NOIRQ=%x,NOTIMER=%x", > - SSTEP_ENABLE, > - SSTEP_NOIRQ, > - SSTEP_NOTIMER); > - put_packet(s, buf); > - break; > - } else if (is_query_packet(p, "qemu.sstep", '=')) { > - /* Display or change the sstep_flags */ > - p += 10; > - if (*p != '=') { > - /* Display current setting */ > - snprintf(buf, sizeof(buf), "0x%x", sstep_flags); > - put_packet(s, buf); > - break; > - } > - p++; > - type = strtoul(p, (char **)&p, 16); > - sstep_flags = type; > - put_packet(s, "OK"); > - break; > - } else if (strcmp(p,"C") == 0) { > - /* > - * "Current thread" remains vague in the spec, so always return > - * the first thread of the current process (gdb returns the > - * first thread). > - */ > - cpu = get_first_cpu_in_process(s, gdb_get_cpu_process(s, > s->g_cpu)); > - snprintf(buf, sizeof(buf), "QC%s", > - gdb_fmt_thread_id(s, cpu, thread_id, > sizeof(thread_id))); > - put_packet(s, buf); > - break; > - } else if (strcmp(p,"fThreadInfo") == 0) { > - s->query_cpu = gdb_first_attached_cpu(s); > - goto report_cpuinfo; > - } else if (strcmp(p,"sThreadInfo") == 0) { > - report_cpuinfo: > - if (s->query_cpu) { > - snprintf(buf, sizeof(buf), "m%s", > - gdb_fmt_thread_id(s, s->query_cpu, > - thread_id, sizeof(thread_id))); > - put_packet(s, buf); > - s->query_cpu = gdb_next_attached_cpu(s, s->query_cpu); > - } else > - put_packet(s, "l"); > - break; > - } else if (strncmp(p,"ThreadExtraInfo,", 16) == 0) { > - if (read_thread_id(p + 16, &p, &pid, &tid) == > GDB_READ_THREAD_ERR) { > - put_packet(s, "E22"); > - break; > - } > - cpu = gdb_get_cpu(s, pid, tid); > - if (cpu != NULL) { > - cpu_synchronize_state(cpu); > - > - if (s->multiprocess && (s->process_num > 1)) { > - /* Print the CPU model and name in multiprocess mode */ > - ObjectClass *oc = object_get_class(OBJECT(cpu)); > - const char *cpu_model = object_class_get_name(oc); > - char *cpu_name = > - object_get_canonical_path_component(OBJECT(cpu)); > - len = snprintf((char *)mem_buf, sizeof(buf) / 2, > - "%s %s [%s]", cpu_model, cpu_name, > - cpu->halted ? "halted " : "running"); > - g_free(cpu_name); > - } else { > - /* memtohex() doubles the required space */ > - len = snprintf((char *)mem_buf, sizeof(buf) / 2, > - "CPU#%d [%s]", cpu->cpu_index, > - cpu->halted ? "halted " : "running"); > - } > - trace_gdbstub_op_extra_info((char *)mem_buf); > - memtohex(buf, mem_buf, len); > - put_packet(s, buf); > - } > - break; > - } > -#ifdef CONFIG_USER_ONLY > - else if (strcmp(p, "Offsets") == 0) { > - TaskState *ts = s->c_cpu->opaque; > - > - snprintf(buf, sizeof(buf), > - "Text=" TARGET_ABI_FMT_lx ";Data=" TARGET_ABI_FMT_lx > - ";Bss=" TARGET_ABI_FMT_lx, > - ts->info->code_offset, > - ts->info->data_offset, > - ts->info->data_offset); > - put_packet(s, buf); > - break; > - } > -#else /* !CONFIG_USER_ONLY */ > - else if (strncmp(p, "Rcmd,", 5) == 0) { > - int len = strlen(p + 5); > - > - if ((len % 2) != 0) { > - put_packet(s, "E01"); > - break; > - } > - len = len / 2; > - hextomem(mem_buf, p + 5, len); > - mem_buf[len++] = 0; > - qemu_chr_be_write(s->mon_chr, mem_buf, len); > - put_packet(s, "OK"); > - break; > - } > -#endif /* !CONFIG_USER_ONLY */ > - if (is_query_packet(p, "Supported", ':')) { > - snprintf(buf, sizeof(buf), "PacketSize=%x", MAX_PACKET_LENGTH); > - cc = CPU_GET_CLASS(first_cpu); > - if (cc->gdb_core_xml_file != NULL) { > - pstrcat(buf, sizeof(buf), ";qXfer:features:read+"); > - } > - > - if (strstr(p, "multiprocess+")) { > - s->multiprocess = true; > - } > - pstrcat(buf, sizeof(buf), ";multiprocess+"); > - > - put_packet(s, buf); > - break; > - } > - if (strncmp(p, "Xfer:features:read:", 19) == 0) { > - const char *xml; > - target_ulong total_len; > - > - process = gdb_get_cpu_process(s, s->g_cpu); > - cc = CPU_GET_CLASS(s->g_cpu); > - if (cc->gdb_core_xml_file == NULL) { > - goto unknown_command; > - } > - > - gdb_has_xml = true; > - p += 19; > - xml = get_feature_xml(s, p, &p, process); > - if (!xml) { > - snprintf(buf, sizeof(buf), "E00"); > - put_packet(s, buf); > - break; > - } > - > - if (*p == ':') > - p++; > - addr = strtoul(p, (char **)&p, 16); > - if (*p == ',') > - p++; > - len = strtoul(p, (char **)&p, 16); > - > - total_len = strlen(xml); > - if (addr > total_len) { > - snprintf(buf, sizeof(buf), "E00"); > - put_packet(s, buf); > - break; > - } > - if (len > (MAX_PACKET_LENGTH - 5) / 2) > - len = (MAX_PACKET_LENGTH - 5) / 2; > - if (len < total_len - addr) { > - buf[0] = 'm'; > - len = memtox(buf + 1, xml + addr, len); > - } else { > - buf[0] = 'l'; > - len = memtox(buf + 1, xml + addr, total_len - addr); > - } > - put_packet_binary(s, buf, len + 1, true); > - break; > + { > + static const GdbCmdParseEntry gen_query_cmd_desc = { > + .handler = handle_gen_query, > + .cmd = "q", > + .cmd_startswith = 1, > + .schema = "s0" > + }; > + cmd_parser = &gen_query_cmd_desc; > } > - if (is_query_packet(p, "Attached", ':')) { > - put_packet(s, GDB_ATTACHED); > - break; > + break; > + case 'Q': > + { > + static const GdbCmdParseEntry gen_set_cmd_desc = { > + .handler = handle_gen_set, > + .cmd = "Q", > + .cmd_startswith = 1, > + .schema = "s0" > + }; > + cmd_parser = &gen_set_cmd_desc; > } > - /* Unrecognised 'q' command. */ > - goto unknown_command; > - > + break; > default: > - unknown_command: > /* put empty packet */ > buf[0] = '\0'; > put_packet(s, buf); -- Alex Bennée