+ gdb_ctx->s->signal = 0;
+ gdb_continue(gdb_ctx->s);
+}
+
+static void handle_cont_with_sig(GdbCmdContext *gdb_ctx, void *user_ctx)
+{
+ unsigned long signal = 0;
+
+ /*
+ * Note: C sig;[addr] is currently unsupported and we simply
+ * omit the addr parameter
+ */
+ if (gdb_ctx->num_params) {
+ signal = gdb_ctx->params[0].val_ul;
+ }
+
+ gdb_ctx->s->signal = gdb_signal_to_target(signal);
+ if (gdb_ctx->s->signal == -1) {
+ gdb_ctx->s->signal = 0;
+ }
+ gdb_continue(gdb_ctx->s);
+}
+
+static void handle_set_thread(GdbCmdContext *gdb_ctx, void *user_ctx)
+{
+ CPUState *cpu;
+
+ if (gdb_ctx->num_params != 2) {
+ put_packet(gdb_ctx->s, "E22");
+ return;
+ }
+
+ if (gdb_ctx->params[1].thread_id.kind == GDB_READ_THREAD_ERR) {
+ put_packet(gdb_ctx->s, "E22");
+ return;
+ }
+
+ if (gdb_ctx->params[1].thread_id.kind != GDB_ONE_THREAD) {
+ put_packet(gdb_ctx->s, "OK");
+ return;
+ }
+
+ cpu = gdb_get_cpu(gdb_ctx->s, gdb_ctx->params[1].thread_id.pid,
+ gdb_ctx->params[1].thread_id.tid);
+ if (!cpu) {
+ put_packet(gdb_ctx->s, "E22");
+ return;
+ }
+
+ /*
+ * Note: This command is deprecated and modern gdb's will be using the
+ * vCont command instead.
+ */
+ switch (gdb_ctx->params[0].opcode) {
+ case 'c':
+ gdb_ctx->s->c_cpu = cpu;
+ put_packet(gdb_ctx->s, "OK");
+ break;
+ case 'g':
+ gdb_ctx->s->g_cpu = cpu;
+ put_packet(gdb_ctx->s, "OK");
+ break;
+ default:
+ put_packet(gdb_ctx->s, "E22");
+ break;
+ }
+}
+
+static void handle_insert_bp(GdbCmdContext *gdb_ctx, void *user_ctx)
+{
+ int res;
+
+ if (gdb_ctx->num_params != 3) {
+ put_packet(gdb_ctx->s, "E22");
+ return;
+ }
+
+ res = gdb_breakpoint_insert(gdb_ctx->params[0].val_ul,
+ gdb_ctx->params[1].val_ull,
+ gdb_ctx->params[2].val_ull);
+ if (res >= 0) {
+ put_packet(gdb_ctx->s, "OK");
+ return;
+ } else if (res == -ENOSYS) {
+ put_packet(gdb_ctx->s, "");
+ return;
+ }
+
+ put_packet(gdb_ctx->s, "E22");
+}
+
+static void handle_remove_bp(GdbCmdContext *gdb_ctx, void *user_ctx)
+{
+ int res;
+
+ if (gdb_ctx->num_params != 3) {
+ put_packet(gdb_ctx->s, "E22");
+ return;
+ }
+
+ res = gdb_breakpoint_remove(gdb_ctx->params[0].val_ul,
+ gdb_ctx->params[1].val_ull,
+ gdb_ctx->params[2].val_ull);
+ if (res >= 0) {
+ put_packet(gdb_ctx->s, "OK");
+ return;
+ } else if (res == -ENOSYS) {
+ put_packet(gdb_ctx->s, "");
+ return;
+ }
+
+ put_packet(gdb_ctx->s, "E22");
+}
+
+/*
+ * handle_set/get_reg
+ *
+ * Older gdb are really dumb, and don't use 'G/g' if 'P/p' is available.
+ * This works, but can be very slow. Anything new enough to understand
+ * XML also knows how to use this properly. However to use this we
+ * need to define a local XML file as well as be talking to a
+ * reasonably modern gdb. Responding with an empty packet will cause
+ * the remote gdb to fallback to older methods.
+ */
+
+static void handle_set_reg(GdbCmdContext *gdb_ctx, void *user_ctx)
+{
+ int reg_size;
+
+ if (!gdb_has_xml) {
+ put_packet(gdb_ctx->s, "");
+ return;
+ }
+
+ if (gdb_ctx->num_params != 2) {
+ put_packet(gdb_ctx->s, "E22");
+ return;
+ }
+
+ reg_size = strlen(gdb_ctx->params[1].data) / 2;
+ hextomem(gdb_ctx->mem_buf, gdb_ctx->params[1].data, reg_size);
+ gdb_write_register(gdb_ctx->s->g_cpu, gdb_ctx->mem_buf,
+ gdb_ctx->params[0].val_ull);
+ put_packet(gdb_ctx->s, "OK");
+}
+
+static void handle_get_reg(GdbCmdContext *gdb_ctx, void *user_ctx)
+{
+ int reg_size;
+
+ if (!gdb_has_xml) {
+ put_packet(gdb_ctx->s, "");
+ return;
+ }
+
+ if (!gdb_ctx->num_params) {
+ put_packet(gdb_ctx->s, "E14");
+ return;
+ }
+
+ reg_size = gdb_read_register(gdb_ctx->s->g_cpu, gdb_ctx->mem_buf,
+ gdb_ctx->params[0].val_ull);
+ if (!reg_size) {
+ put_packet(gdb_ctx->s, "E14");
+ return;
+ }
+
+ memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, reg_size);
+ put_packet(gdb_ctx->s, gdb_ctx->str_buf);
+}
+
+static void handle_write_mem(GdbCmdContext *gdb_ctx, void *user_ctx)
+{
+ if (gdb_ctx->num_params != 3) {
+ put_packet(gdb_ctx->s, "E22");
+ return;
+ }
+
+ /* hextomem() reads 2*len bytes */
+ if (gdb_ctx->params[1].val_ull > strlen(gdb_ctx->params[2].data) / 2) {
+ put_packet(gdb_ctx->s, "E22");
+ return;
+ }
+
+ hextomem(gdb_ctx->mem_buf, gdb_ctx->params[2].data,
+ gdb_ctx->params[1].val_ull);
+ if (target_memory_rw_debug(gdb_ctx->s->g_cpu, gdb_ctx->params[0].val_ull,
+ gdb_ctx->mem_buf,
+ gdb_ctx->params[1].val_ull, true)) {
+ put_packet(gdb_ctx->s, "E14");
+ return;
+ }
+
+ put_packet(gdb_ctx->s, "OK");
+}
+
+static void handle_read_mem(GdbCmdContext *gdb_ctx, void *user_ctx)
+{
+ if (gdb_ctx->num_params != 2) {
+ put_packet(gdb_ctx->s, "E22");
+ return;
+ }
+
+ /* memtohex() doubles the required space */
+ if (gdb_ctx->params[1].val_ull > MAX_PACKET_LENGTH / 2) {
+ put_packet(gdb_ctx->s, "E22");
+ return;
+ }
+
+ if (target_memory_rw_debug(gdb_ctx->s->g_cpu, gdb_ctx->params[0].val_ull,
+ gdb_ctx->mem_buf,
+ gdb_ctx->params[1].val_ull, false)) {
+ put_packet(gdb_ctx->s, "E14");
+ return;
+ }
+
+ memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, gdb_ctx->params[1].val_ull);
+ put_packet(gdb_ctx->s, gdb_ctx->str_buf);
+}
+
+static void handle_write_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx)
+{
+ target_ulong addr, len;
+ uint8_t *registers;
+ int reg_size;
+
+ if (!gdb_ctx->num_params) {
+ return;
+ }
+
+ cpu_synchronize_state(gdb_ctx->s->g_cpu);
+ registers = gdb_ctx->mem_buf;
+ len = strlen(gdb_ctx->params[0].data) / 2;
+ hextomem(registers, gdb_ctx->params[0].data, len);
+ for (addr = 0; addr < gdb_ctx->s->g_cpu->gdb_num_g_regs && len > 0;
+ addr++) {
+ reg_size = gdb_write_register(gdb_ctx->s->g_cpu, registers, addr);
+ len -= reg_size;
+ registers += reg_size;
+ }
+ put_packet(gdb_ctx->s, "OK");
+}
+
+static void handle_read_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx)
+{
+ target_ulong addr, len;
+
+ cpu_synchronize_state(gdb_ctx->s->g_cpu);
+ len = 0;
+ for (addr = 0; addr < gdb_ctx->s->g_cpu->gdb_num_g_regs; addr++) {
+ len += gdb_read_register(gdb_ctx->s->g_cpu, gdb_ctx->mem_buf + len,
+ addr);
+ }
+
+ memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, len);
+ put_packet(gdb_ctx->s, gdb_ctx->str_buf);
+}
+
+static void handle_file_io(GdbCmdContext *gdb_ctx, void *user_ctx)
+{
+ if (gdb_ctx->num_params >= 2 && gdb_ctx->s->current_syscall_cb) {
+ target_ulong ret, err;
+
+ ret = (target_ulong)gdb_ctx->params[0].val_ull;
+ err = (target_ulong)gdb_ctx->params[1].val_ull;
+ gdb_ctx->s->current_syscall_cb(gdb_ctx->s->c_cpu, ret, err);
+ gdb_ctx->s->current_syscall_cb = NULL;
+ }
+
+ if (gdb_ctx->num_params >= 3 && gdb_ctx->params[2].opcode == (uint8_t)'C') {
+ put_packet(gdb_ctx->s, "T02");
+ return;
+ }
+
+ gdb_continue(gdb_ctx->s);
+}
+
+static void handle_step(GdbCmdContext *gdb_ctx, void *user_ctx)
+{
+ if (gdb_ctx->num_params) {
+ gdb_set_cpu_pc(gdb_ctx->s, (target_ulong)gdb_ctx->params[0].val_ull);
+ }
+
+ cpu_single_step(gdb_ctx->s->c_cpu, sstep_flags);
+ gdb_continue(gdb_ctx->s);
+}
+
+static void handle_v_cont_query(GdbCmdContext *gdb_ctx, void *user_ctx)
+{
+ put_packet(gdb_ctx->s, "vCont;c;C;s;S");
+}
+
+static void handle_v_cont(GdbCmdContext *gdb_ctx, void *user_ctx)
+{
+ int res;
+
+ if (!gdb_ctx->num_params) {
+ return;
+ }
+
+ res = gdb_handle_vcont(gdb_ctx->s, gdb_ctx->params[0].data);
+ if ((res == -EINVAL) || (res == -ERANGE)) {
+ put_packet(gdb_ctx->s, "E22");
+ } else if (res) {
+ put_packet(gdb_ctx->s, "");
+ }
+}
+
+static void handle_v_attach(GdbCmdContext *gdb_ctx, void *user_ctx)
+{
+ GDBProcess *process;
+ CPUState *cpu;
+ char thread_id[16];
+
+ pstrcpy(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "E22");
+ if (!gdb_ctx->num_params) {
+ goto cleanup;
+ }
+
+ process = gdb_get_process(gdb_ctx->s, gdb_ctx->params[0].val_ul);
+ if (!process) {
+ goto cleanup;
+ }
+
+ cpu = get_first_cpu_in_process(gdb_ctx->s, process);
+ if (!cpu) {
+ goto cleanup;
+ }
+
+ process->attached = true;
+ gdb_ctx->s->g_cpu = cpu;
+ gdb_ctx->s->c_cpu = cpu;
+
+ gdb_fmt_thread_id(gdb_ctx->s, cpu, thread_id, sizeof(thread_id));
+ snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "T%02xthread:%s;",
+ GDB_SIGNAL_TRAP, thread_id);
+cleanup:
+ put_packet(gdb_ctx->s, gdb_ctx->str_buf);
+}
+
+static void handle_v_kill(GdbCmdContext *gdb_ctx, void *user_ctx)
+{
+ /* Kill the target */
+ put_packet(gdb_ctx->s, "OK");
+ error_report("QEMU: Terminated via GDBstub");
+ exit(0);
+}
+
+static GdbCmdParseEntry gdb_v_commands_table[] = {
+ /* Order is important if has same prefix */
+ {
+ .handler = handle_v_cont_query,
+ .cmd = "Cont?",
+ .cmd_startswith = 1
+ },
+ {
+ .handler = handle_v_cont,
+ .cmd = "Cont",
+ .cmd_startswith = 1,
+ .schema = "s0"
+ },
+ {
+ .handler = handle_v_attach,
+ .cmd = "Attach;",
+ .cmd_startswith = 1,
+ .schema = "l0"
+ },
+ {
+ .handler = handle_v_kill,
+ .cmd = "Kill;",
+ .cmd_startswith = 1
+ },
+};
+
+static void handle_v_commands(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_v_commands_table,
+ ARRAY_SIZE(gdb_v_commands_table))) {
+ put_packet(gdb_ctx->s, "");
+ }
+}
+
+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;
+
+ 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)
+{
+ snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "sstepbits;sstep");
+#ifndef CONFIG_USER_ONLY
+ pstrcat(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), ";PhyMemMode");
+#endif
+ put_packet(gdb_ctx->s, gdb_ctx->str_buf);
+}
+
+#ifndef CONFIG_USER_ONLY
+static void handle_query_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx,
+ void *user_ctx)
+{
+ snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "%d", phy_memory_mode);
+ put_packet(gdb_ctx->s, gdb_ctx->str_buf);
+}
+
+static void handle_set_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, void *user_ctx)
+{
+ if (!gdb_ctx->num_params) {
+ put_packet(gdb_ctx->s, "E22");
+ return;
+ }
+
+ if (!gdb_ctx->params[0].val_ul) {
+ phy_memory_mode = 0;
+ } else {
+ phy_memory_mode = 1;
+ }
+ put_packet(gdb_ctx->s, "OK");
+}
+#endif
+
+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",
+ },
+#ifndef CONFIG_USER_ONLY
+ {
+ .handler = handle_query_qemu_phy_mem_mode,
+ .cmd = "qemu.PhyMemMode",
+ },
+#endif
+};
+
+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"
+ },
+#ifndef CONFIG_USER_ONLY
+ {
+ .handler = handle_set_qemu_phy_mem_mode,
+ .cmd = "qemu.PhyMemMode:",
+ .cmd_startswith = 1,
+ .schema = "l0"
+ },
+#endif
+};
+
+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 void handle_target_halt(GdbCmdContext *gdb_ctx, void *user_ctx)
+{
+ char thread_id[16];
+
+ gdb_fmt_thread_id(gdb_ctx->s, gdb_ctx->s->c_cpu, thread_id,
+ sizeof(thread_id));
+ snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "T%02xthread:%s;",
+ GDB_SIGNAL_TRAP, thread_id);
+ put_packet(gdb_ctx->s, gdb_ctx->str_buf);
+ /*
+ * Remove all the breakpoints when this query is issued,
+ * because gdb is doing an initial connect and the state
+ * should be cleaned up.
+ */
+ gdb_breakpoint_remove_all();
+}
+
+static int gdb_handle_packet(GDBState *s, const char *line_buf)
+{
+ const GdbCmdParseEntry *cmd_parser = NULL;
+
+ trace_gdbstub_io_command(line_buf);
+
+ switch (line_buf[0]) {
+ case '!':
+ put_packet(s, "OK");
+ break;
+ case '?':
+ {
+ static const GdbCmdParseEntry target_halted_cmd_desc = {
+ .handler = handle_target_halt,
+ .cmd = "?",
+ .cmd_startswith = 1
+ };
+ cmd_parser = &target_halted_cmd_desc;
+ }
+ break;
+ case 'c':
+ {
+ static const GdbCmdParseEntry continue_cmd_desc = {
+ .handler = handle_continue,
+ .cmd = "c",
+ .cmd_startswith = 1,
+ .schema = "L0"
+ };
+ cmd_parser = &continue_cmd_desc;
+ }
+ break;
+ case 'C':
+ {
+ static const GdbCmdParseEntry cont_with_sig_cmd_desc = {
+ .handler = handle_cont_with_sig,
+ .cmd = "C",
+ .cmd_startswith = 1,
+ .schema = "l0"
+ };
+ cmd_parser = &cont_with_sig_cmd_desc;
+ }
+ break;
+ case 'v':
+ {
+ static const GdbCmdParseEntry v_cmd_desc = {
+ .handler = handle_v_commands,
+ .cmd = "v",
+ .cmd_startswith = 1,
+ .schema = "s0"
+ };
+ cmd_parser = &v_cmd_desc;
+ }
+ break;
+ case 'k':
+ /* Kill the target */
+ error_report("QEMU: Terminated via GDBstub");
+ exit(0);
+ case 'D':
+ {
+ static const GdbCmdParseEntry detach_cmd_desc = {
+ .handler = handle_detach,
+ .cmd = "D",
+ .cmd_startswith = 1,
+ .schema = "?.l0"
+ };
+ cmd_parser = &detach_cmd_desc;
+ }
+ break;
+ case 's':
+ {
+ static const GdbCmdParseEntry step_cmd_desc = {
+ .handler = handle_step,
+ .cmd = "s",
+ .cmd_startswith = 1,
+ .schema = "L0"
+ };
+ cmd_parser = &step_cmd_desc;
+ }
+ break;
+ case 'F':
+ {
+ static const GdbCmdParseEntry file_io_cmd_desc = {
+ .handler = handle_file_io,
+ .cmd = "F",
+ .cmd_startswith = 1,
+ .schema = "L,L,o0"
+ };
+ cmd_parser = &file_io_cmd_desc;
+ }
+ break;
+ case 'g':
+ {
+ static const GdbCmdParseEntry read_all_regs_cmd_desc = {
+ .handler = handle_read_all_regs,
+ .cmd = "g",
+ .cmd_startswith = 1
+ };
+ cmd_parser = &read_all_regs_cmd_desc;
+ }
+ break;
+ case 'G':
+ {
+ static const GdbCmdParseEntry write_all_regs_cmd_desc = {
+ .handler = handle_write_all_regs,
+ .cmd = "G",
+ .cmd_startswith = 1,
+ .schema = "s0"
+ };
+ cmd_parser = &write_all_regs_cmd_desc;
+ }
+ break;
+ case 'm':
+ {
+ static const GdbCmdParseEntry read_mem_cmd_desc = {
+ .handler = handle_read_mem,
+ .cmd = "m",
+ .cmd_startswith = 1,
+ .schema = "L,L0"
+ };
+ cmd_parser = &read_mem_cmd_desc;
+ }
+ break;
+ case 'M':
+ {
+ static const GdbCmdParseEntry write_mem_cmd_desc = {
+ .handler = handle_write_mem,
+ .cmd = "M",
+ .cmd_startswith = 1,
+ .schema = "L,L:s0"
+ };
+ cmd_parser = &write_mem_cmd_desc;
+ }
+ break;
+ case 'p':
+ {
+ static const GdbCmdParseEntry get_reg_cmd_desc = {
+ .handler = handle_get_reg,
+ .cmd = "p",
+ .cmd_startswith = 1,
+ .schema = "L0"
+ };
+ cmd_parser = &get_reg_cmd_desc;
+ }
+ break;
+ case 'P':
+ {
+ static const GdbCmdParseEntry set_reg_cmd_desc = {
+ .handler = handle_set_reg,
+ .cmd = "P",
+ .cmd_startswith = 1,
+ .schema = "L?s0"
+ };
+ cmd_parser = &set_reg_cmd_desc;
+ }
+ break;
+ case 'Z':
+ {
+ static const GdbCmdParseEntry insert_bp_cmd_desc = {
+ .handler = handle_insert_bp,
+ .cmd = "Z",
+ .cmd_startswith = 1,
+ .schema = "l?L?L0"
+ };
+ cmd_parser = &insert_bp_cmd_desc;
+ }
+ break;
+ case 'z':
+ {
+ static const GdbCmdParseEntry remove_bp_cmd_desc = {
+ .handler = handle_remove_bp,
+ .cmd = "z",
+ .cmd_startswith = 1,
+ .schema = "l?L?L0"
+ };
+ cmd_parser = &remove_bp_cmd_desc;
+ }
+ break;
+ case 'H':
+ {
+ static const GdbCmdParseEntry set_thread_cmd_desc = {
+ .handler = handle_set_thread,
+ .cmd = "H",
+ .cmd_startswith = 1,
+ .schema = "o.t0"
+ };
+ cmd_parser = &set_thread_cmd_desc;
+ }
+ break;
+ case 'T':
+ {
+ static const GdbCmdParseEntry thread_alive_cmd_desc = {
+ .handler = handle_thread_alive,
+ .cmd = "T",
+ .cmd_startswith = 1,
+ .schema = "t0"
+ };
+ cmd_parser = &thread_alive_cmd_desc;
+ }
+ break;
+ case 'q':
+ {
+ static const GdbCmdParseEntry gen_query_cmd_desc = {
+ .handler = handle_gen_query,
+ .cmd = "q",
+ .cmd_startswith = 1,
+ .schema = "s0"
+ };
+ cmd_parser = &gen_query_cmd_desc;
+ }
+ 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;
+ }
+ break;
+ default:
+ /* put empty packet */
+ put_packet(s, "");
+ break;
+ }
+
+ run_cmd_parser(s, line_buf, cmd_parser);
+
+ return RS_IDLE;
+}
+
+void gdb_set_stop_cpu(CPUState *cpu)
+{
+ GDBProcess *p = gdb_get_cpu_process(gdbserver_state, cpu);
+
+ if (!p->attached) {