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 <arilou@gmail.com>
---
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);
--
2.21.0
Jon Doron <arilou@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 <arilou@gmail.com>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Tested-by: Alex Bennée <alex.bennee@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
© 2016 - 2026 Red Hat, Inc.