From d79f526ce2581d2d0d9320297b2468c742cc3116 Mon Sep 17 00:00:00 2001 From: Colin Date: Tue, 23 Sep 2025 07:14:30 +0000 Subject: [PATCH] Refine gdbstub. --- gdbstub/cpu.h | 11 -- gdbstub/gdbstub.c | 271 ++++---------------------------------------- gdbstub/internals.h | 117 ++----------------- gdbstub/system.c | 268 +++++++++++++++++++++---------------------- 4 files changed, 156 insertions(+), 511 deletions(-) diff --git a/gdbstub/cpu.h b/gdbstub/cpu.h index 5e49550..23ebf5d 100644 --- a/gdbstub/cpu.h +++ b/gdbstub/cpu.h @@ -796,17 +796,6 @@ enum CPUDumpFlags { */ CPUState *cpu_create(const char *tname); -/** - * parse_cpu_option: - * @cpu_option: The -cpu option including optional parameters. - * - * processes optional parameters and registers them as global properties - * - * Returns: type of CPU to create or prints error and terminates process - * if an error occurred. - */ -const char *parse_cpu_option(const char *cpu_option); - /** * qemu_cpu_is_self: * @cpu: The vCPU to check against. diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index be26a05..3db3a25 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -101,7 +101,6 @@ int gdb_put_packet(const char *buf) { return gdb_put_packet_binary(buf, strlen(b void gdb_put_strbuf(void) { gdb_put_packet(gdbserver_state.str_buf->str); } -/* Encode data using the encoding for 'x' packets. */ void gdb_memtox(GString *buf, const char *mem, int len) { char c; @@ -122,23 +121,6 @@ void gdb_memtox(GString *buf, const char *mem, int len) { } } -GDBProcess *gdb_get_process(uint32_t pid) { - int i; - - if (!pid) { - /* 0 means any process, we take the first one */ - return &gdbserver_state.processes[0]; - } - - for (i = 0; i < gdbserver_state.process_num; i++) { - if (gdbserver_state.processes[i].pid == pid) { - return &gdbserver_state.processes[i]; - } - } - - return NULL; -} - void gdb_feature_builder_append_tag(const GDBFeatureBuilder *builder, const char *format, ...) { va_list ap; va_start(ap, format); @@ -243,87 +225,8 @@ int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg) { return 0; } -static void gdb_register_feature(CPUState *cpu, int base_reg, gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg, - const GDBFeature *feature) { - GDBRegisterState s = {.base_reg = base_reg, .get_reg = get_reg, .set_reg = set_reg, .feature = feature}; - - g_array_append_val(cpu->gdb_regs, s); -} - -static const char *gdb_get_core_xml_file(CPUState *cpu) { - CPUClass *cc = cpu->cc; - - /* - * The CPU class can provide the XML filename via a method, - * or as a simple fixed string field. - */ - if (cc->gdb_get_core_xml_file) { - return cc->gdb_get_core_xml_file(cpu); - } - return cc->gdb_core_xml_file; -} - -void gdb_init_cpu(CPUState *cpu) { - CPUClass *cc = cpu->cc; - const GDBFeature *feature; - const char *xmlfile = gdb_get_core_xml_file(cpu); - - cpu->gdb_regs = g_array_new(false, false, sizeof(GDBRegisterState)); - - if (xmlfile) { - feature = gdb_find_static_feature(xmlfile); - gdb_register_feature(cpu, 0, cc->gdb_read_register, cc->gdb_write_register, feature); - cpu->gdb_num_regs = cpu->gdb_num_g_regs = feature->num_regs; - } - - if (cc->gdb_num_core_regs) { - cpu->gdb_num_regs = cpu->gdb_num_g_regs = cc->gdb_num_core_regs; - } -} - -void gdb_register_coprocessor(CPUState *cpu, gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg, const GDBFeature *feature, - int g_pos) { - GDBRegisterState *s; - guint i; - int base_reg = cpu->gdb_num_regs; - - for (i = 0; i < cpu->gdb_regs->len; i++) { - /* Check for duplicates. */ - s = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); - if (s->feature == feature) { - return; - } - } - - gdb_register_feature(cpu, base_reg, get_reg, set_reg, feature); - - /* Add to end of list. */ - cpu->gdb_num_regs += feature->num_regs; - if (g_pos) { - if (g_pos != base_reg) { - std::cout << "Error: Bad gdb register numbering for" << feature->xml << ", xpected " << g_pos << " got " - << base_reg << std::endl; - } else { - cpu->gdb_num_g_regs = cpu->gdb_num_regs; - } - } -} - -void gdb_unregister_coprocessor_all(CPUState *cpu) { - /* - * Safe to nuke everything. GDBRegisterState::xml is static const char so - * it won't be freed - */ - g_array_free(cpu->gdb_regs, true); - - cpu->gdb_regs = NULL; - cpu->gdb_num_regs = 0; - cpu->gdb_num_g_regs = 0; -} - -static void gdb_process_breakpoint_remove_all(GDBProcess *p) { - CPUState *cpu = gdb_get_first_cpu_in_process(p); - +static void gdb_process_breakpoint_remove_all() { + CPUState *cpu = get_cpu(); while (cpu) { gdb_breakpoint_remove_all(cpu); cpu = gdb_next_cpu_in_process(cpu); @@ -337,13 +240,7 @@ static void gdb_set_cpu_pc(vaddr pc) { cpu_set_pc(cpu, pc); } -void gdb_append_thread_id(CPUState *cpu, GString *buf) { - if (gdbserver_state.multiprocess) { - g_string_append_printf(buf, "p%02x.%02x", gdb_get_cpu_pid(cpu), gdb_get_cpu_index(cpu)); - } else { - g_string_append_printf(buf, "%02x", gdb_get_cpu_index(cpu)); - } -} +void gdb_append_thread_id(CPUState *cpu, GString *buf) { g_string_append_printf(buf, "%02x", gdb_get_cpu_index(cpu)); } static GDBThreadIdKind read_thread_id(const char *buf, const char **end_buf, uint32_t *pid, uint32_t *tid) { unsigned long p, t; @@ -402,8 +299,7 @@ static int gdb_handle_vcont(const char *p) { /* uninitialised CPUs stay 0 */ g_autofree char *newstates = g_new0(char, max_cpus); - /* mark valid CPUs with 1 */ - CPU_FOREACH(cpu) { newstates[c->cpu_index] = 1; } + newstates[get_cpu()->cpu_index] = 1; /* * res keeps track of what error we are returning, with -ENOTSUP meaning @@ -472,27 +368,22 @@ static int gdb_handle_vcont(const char *p) { break; case GDB_ALL_THREADS: - process = gdb_get_process(pid); - + process = gdb_get_process(); if (!process->attached) { return -EINVAL; } - - cpu = gdb_get_first_cpu_in_process(process); - while (cpu) { + cpu = get_cpu(); + if (cpu) { if (newstates[cpu->cpu_index] == 1) { newstates[cpu->cpu_index] = cur_action; - target_count++; last_target = cpu; } - - cpu = gdb_next_cpu_in_process(cpu); } break; case GDB_ONE_THREAD: - cpu = gdb_get_cpu(pid, tid); + cpu = gdb_get_cpu(); /* invalid CPU/thread specified */ if (!cpu) { @@ -660,30 +551,16 @@ static void run_cmd_parser(const char *data, const GdbCmdParseEntry *cmd) { static void handle_detach(GArray *params, void *user_ctx) { GDBProcess *process; uint32_t pid = 1; - - if (gdbserver_state.multiprocess) { - if (!params->len) { - gdb_put_packet("E22"); - return; - } - - pid = gdb_get_cmd_param(params, 0)->val_ul; - } - - process = gdb_get_process(pid); - gdb_process_breakpoint_remove_all(process); + process = gdb_get_process(); + gdb_process_breakpoint_remove_all(); process->attached = false; - if (pid == gdb_get_cpu_pid(gdbserver_state.c_cpu)) { gdbserver_state.c_cpu = gdb_first_attached_cpu(); } - if (pid == gdb_get_cpu_pid(gdbserver_state.g_cpu)) { gdbserver_state.g_cpu = gdb_first_attached_cpu(); } - if (!gdbserver_state.c_cpu) { - /* No more process attached */ gdb_disable_syscalls(); gdb_continue(); } @@ -692,23 +569,19 @@ static void handle_detach(GArray *params, void *user_ctx) { static void handle_thread_alive(GArray *params, void *user_ctx) { CPUState *cpu; - if (!params->len) { gdb_put_packet("E22"); return; } - if (gdb_get_cmd_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) { gdb_put_packet("E22"); return; } - - cpu = gdb_get_cpu(gdb_get_cmd_param(params, 0)->thread_id.pid, gdb_get_cmd_param(params, 0)->thread_id.tid); + cpu = gdb_get_cpu(); if (!cpu) { gdb_put_packet("E22"); return; } - gdb_put_packet("OK"); } @@ -716,14 +589,12 @@ static void handle_continue(GArray *params, void *user_ctx) { if (params->len) { gdb_set_cpu_pc(gdb_get_cmd_param(params, 0)->val_ull); } - gdbserver_state.signal = 0; gdb_continue(); } static void handle_cont_with_sig(GArray *params, void *user_ctx) { unsigned long signal = 0; - /* * Note: C sig;[addr] is currently unsupported and we simply * omit the addr parameter @@ -731,7 +602,6 @@ static void handle_cont_with_sig(GArray *params, void *user_ctx) { if (params->len) { signal = gdb_get_cmd_param(params, 0)->val_ul; } - gdbserver_state.signal = gdb_signal_to_target(signal); if (gdbserver_state.signal == -1) { gdbserver_state.signal = 0; @@ -742,31 +612,25 @@ static void handle_cont_with_sig(GArray *params, void *user_ctx) { static void handle_set_thread(GArray *params, void *user_ctx) { uint32_t pid, tid; CPUState *cpu; - if (params->len != 2) { gdb_put_packet("E22"); return; } - if (gdb_get_cmd_param(params, 1)->thread_id.kind == GDB_READ_THREAD_ERR) { gdb_put_packet("E22"); return; } - if (gdb_get_cmd_param(params, 1)->thread_id.kind != GDB_ONE_THREAD) { gdb_put_packet("OK"); return; } - pid = gdb_get_cmd_param(params, 1)->thread_id.pid; tid = gdb_get_cmd_param(params, 1)->thread_id.tid; - - cpu = gdb_get_cpu(pid, tid); + cpu = gdb_get_cpu(); if (!cpu) { gdb_put_packet("E22"); return; } - /* * Note: This command is deprecated and modern gdb's will be using the * vCont command instead. @@ -950,29 +814,11 @@ static void handle_step(GArray *params, void *user_ctx) { if (params->len) { gdb_set_cpu_pc(gdb_get_cmd_param(params, 0)->val_ull); } - cpu_single_step(gdbserver_state.c_cpu, gdbserver_state.sstep_flags); gdb_continue(); } -static void handle_backward(GArray *params, void *user_ctx) { - if (!gdb_can_reverse()) { - gdb_put_packet("E22"); - } - if (params->len == 1) { - switch (gdb_get_cmd_param(params, 0)->opcode) { - case 's': - gdb_put_packet("E14"); - return; - case 'c': - gdb_put_packet("E14"); - return; - } - } - - /* Default invalid command */ - gdb_put_packet(""); -} +static void handle_backward(GArray *params, void *user_ctx) { gdb_put_packet("E22"); } static void handle_v_cont_query(GArray *params, void *user_ctx) { gdb_put_packet("vCont;c;C;s;S"); } @@ -1000,12 +846,12 @@ static void handle_v_attach(GArray *params, void *user_ctx) { goto cleanup; } - process = gdb_get_process(gdb_get_cmd_param(params, 0)->val_ul); + process = gdb_get_process(); if (!process) { goto cleanup; } - cpu = gdb_get_first_cpu_in_process(process); + cpu = get_cpu(); if (!cpu) { goto cleanup; } @@ -1096,8 +942,8 @@ static void handle_query_curr_tid(GArray *params, void *user_ctx) { * the first thread of the current process (gdb returns the * first thread). */ - process = gdb_get_cpu_process(gdbserver_state.g_cpu); - cpu = gdb_get_first_cpu_in_process(process); + process = gdb_get_process(); + cpu = get_cpu(); g_string_assign(gdbserver_state.str_buf, "QC"); gdb_append_thread_id(cpu, gdbserver_state.str_buf); gdb_put_strbuf(); @@ -1128,21 +974,16 @@ static void handle_query_first_threads(GArray *params, void *user_ctx) { static void handle_query_thread_extra(GArray *params, void *user_ctx) { g_autoptr(GString) rs = g_string_new(NULL); CPUState *cpu; - if (!params->len || gdb_get_cmd_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) { gdb_put_packet("E22"); return; } - - cpu = gdb_get_cpu(gdb_get_cmd_param(params, 0)->thread_id.pid, gdb_get_cmd_param(params, 0)->thread_id.tid); + cpu = gdb_get_cpu(); if (!cpu) { return; } - cpu_synchronize_state(cpu); - g_string_printf(rs, "CPU#%d [%s]", cpu->cpu_index, cpu->halted ? "halted " : "running"); - // trace_gdbstub_op_extra_info(rs->str); gdb_memtohex(gdbserver_state.str_buf, (uint8_t *)rs->str, rs->len); gdb_put_strbuf(); } @@ -1165,20 +1006,6 @@ static void handle_query_supported(GArray *params, void *user_ctx) { if (gdb_get_core_xml_file(get_cpu())) { g_string_append(gdbserver_state.str_buf, ";qXfer:features:read+"); } - - if (gdb_can_reverse()) { - g_string_append(gdbserver_state.str_buf, ";ReverseStep+;ReverseContinue+"); - } - - if (params->len) { - const char *gdb_supported = gdb_get_cmd_param(params, 0)->data; - if (strstr(gdb_supported, "multiprocess+")) { - gdbserver_state.multiprocess = true; - } - } - - g_string_append(gdbserver_state.str_buf, ";vContSupported+;multiprocess+"); - if (extra_query_flags) { int extras = g_strv_length(extra_query_flags); for (int i = 0; i < extras; i++) { @@ -1200,7 +1027,7 @@ static void handle_query_xfer_features(GArray *params, void *user_ctx) { return; } - process = gdb_get_cpu_process(gdbserver_state.g_cpu); + process = gdb_get_process(); if (!gdb_get_core_xml_file(gdbserver_state.g_cpu)) { gdb_put_packet(""); return; @@ -1255,17 +1082,6 @@ static const GdbCmdParseEntry gdb_gen_query_set_common_table[] = { {.handler = handle_set_qemu_sstep, .cmd = "qemu.sstep=", .cmd_startswith = true, .schema = "l0"}, }; -/** - * extend_table() - extend one of the command tables - * @table: the command table to extend (or NULL) - * @extensions: a list of GdbCmdParseEntry pointers - * - * The entries themselves should be pointers to static const - * GdbCmdParseEntry entries. If the entry is already in the table we - * skip adding it again. - * - * Returns (a potentially freshly allocated) GPtrArray of GdbCmdParseEntry - */ static GPtrArray *extend_table(GPtrArray *table, GPtrArray *extensions) { if (!table) { table = g_ptr_array_new(); @@ -1281,13 +1097,6 @@ static GPtrArray *extend_table(GPtrArray *table, GPtrArray *extensions) { return table; } -/** - * process_extended_table() - run through an extended command table - * @table: the command table to check - * @data: parameters - * - * returns true if the command was found and executed - */ static bool process_extended_table(GPtrArray *table, const char *data) { for (int i = 0; i < table->len; i++) { const GdbCmdParseEntry *entry = (GdbCmdParseEntry *)g_ptr_array_index(table, i); @@ -1298,7 +1107,6 @@ static bool process_extended_table(GPtrArray *table, const char *data) { return false; } -/* Ptr to GdbCmdParseEntry */ static GPtrArray *extended_query_table; void gdb_extend_query_table(GPtrArray *new_queries) { @@ -1342,7 +1150,6 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = { }, }; -/* Ptr to GdbCmdParseEntry */ static GPtrArray *extended_set_table; void gdb_extend_set_table(GPtrArray *new_set) { extended_set_table = extend_table(extended_set_table, new_set); } @@ -1554,21 +1361,6 @@ static RSState gdb_handle_packet(const char *line_buf) { return RS_IDLE; } -void gdb_set_stop_cpu(CPUState *cpu) { - GDBProcess *p = gdb_get_cpu_process(cpu); - - if (!p->attached) { - /* - * Having a stop CPU corresponding to a process that is not attached - * confuses GDB. So we ignore the request. - */ - return; - } - - gdbserver_state.c_cpu = cpu; - gdbserver_state.g_cpu = cpu; -} - void gdb_read_byte(uint8_t ch) { uint8_t reply; @@ -1725,28 +1517,3 @@ void gdb_read_byte(uint8_t ch) { } } } - -/* - * Create the process that will contain all the "orphan" CPUs (that are not - * part of a CPU cluster). Note that if this process contains no CPUs, it won't - * be attachable and thus will be invisible to the user. - */ -void gdb_create_default_process(GDBState *s) { - GDBProcess *process; - int pid; - - if (gdbserver_state.process_num) { - pid = s->processes[s->process_num - 1].pid; - } else { - pid = 0; - } - /* We need an available PID slot for this process */ - assert(pid < UINT32_MAX); - pid++; - - s->processes = g_renew(GDBProcess, s->processes, ++s->process_num); - process = &s->processes[s->process_num - 1]; - process->pid = pid; - process->attached = false; - process->target_xml = NULL; -} diff --git a/gdbstub/internals.h b/gdbstub/internals.h index 670242d..1a174a0 100644 --- a/gdbstub/internals.h +++ b/gdbstub/internals.h @@ -80,7 +80,6 @@ typedef struct GDBState { int line_csum; /* checksum at the end of the packet */ GByteArray *last_packet; int signal; - bool multiprocess; GDBProcess *processes; int process_num; GString *str_buf; @@ -129,44 +128,13 @@ void gdb_put_strbuf(void); void gdb_hextomem(GByteArray *mem, const char *buf, int len); void gdb_read_byte(uint8_t ch); -/** - * gdb_init_cpu(): Initialize the CPU for gdbstub. - * @cpu: The CPU to be initialized. - */ void gdb_init_cpu(CPUState *cpu); -/** - * gdb_register_coprocessor() - register a supplemental set of registers - * @cpu - the CPU associated with registers - * @get_reg - get function (gdb reading) - * @set_reg - set function (gdb modifying) - * @num_regs - number of registers in set - * @xml - xml name of set - * @gpos - non-zero to append to "general" register set at @gpos - */ void gdb_register_coprocessor(CPUState *cpu, gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg, const GDBFeature *feature, int g_pos); -/** - * gdb_unregister_coprocessor_all() - unregisters supplemental set of registers - * @cpu - the CPU associated with registers - */ void gdb_unregister_coprocessor_all(CPUState *cpu); -/** - * gdbserver_start: start the gdb server - * @port_or_device: connection spec for gdb - * @errp: error handle - * - * For CONFIG_USER this is either a tcp port or a path to a fifo. For - * system emulation you can use a full chardev spec for your gdbserver - * port. - * - * The error handle should be either &error_fatal (for start-up) or - * &error_warn (for QMP/HMP initiated sessions). - * - * Returns true when server successfully started. - */ bool gdbserver_start(const char *port_or_device, Error **errp); /** @@ -193,85 +161,33 @@ void gdb_feature_builder_end(const GDBFeatureBuilder *builder); const GDBFeature *gdb_find_static_feature(const char *xmlname); -/** - * gdb_read_register() - Read a register associated with a CPU. - * @cpu: The CPU associated with the register. - * @buf: The buffer that the read register will be appended to. - * @reg: The register's number returned by gdb_find_feature_register(). - * - * Return: The number of read bytes. - */ int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); -/** - * gdb_write_register() - Write a register associated with a CPU. - * @cpu: The CPU associated with the register. - * @buf: The buffer that the register contents will be set to. - * @reg: The register's number returned by gdb_find_feature_register(). - * - * The size of @buf must be at least the size of the register being - * written. - * - * Return: The number of written bytes, or 0 if an error occurred (for - * example, an unknown register was provided). - */ int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg); -/** - * typedef GDBRegDesc - a register description from gdbstub - */ typedef struct { int gdb_reg; const char *name; const char *feature_name; } GDBRegDesc; -/** - * gdb_get_register_list() - Return list of all registers for CPU - * @cpu: The CPU being searched - * - * Returns a GArray of GDBRegDesc, caller frees array but not the - * const strings. - */ GArray *gdb_get_register_list(CPUState *cpu); -void gdb_set_stop_cpu(CPUState *cpu); - /* in gdbstub-xml.c, generated by scripts/feature_to_c.py */ extern const GDBFeature gdb_static_features[]; -/* - * Packet acknowledgement - we handle this slightly differently - * between user and system mode, mainly to deal with the differences - * between the flexible chardev and the direct fd approaches. - * - * We currently don't support a negotiated QStartNoAckMode - */ - -/** - * gdb_got_immediate_ack() - check ok to continue - * - * Returns true to continue, false to re-transmit for user only, the - * system stub always returns true. - */ +void gdb_chr_receive(const uint8_t *buf, int size); bool gdb_got_immediate_ack(void); + /* utility helpers */ -GDBProcess *gdb_get_process(uint32_t pid); -CPUState *gdb_get_first_cpu_in_process(GDBProcess *process); -CPUState *gdb_first_attached_cpu(void); +GDBProcess *gdb_get_process(); void gdb_append_thread_id(CPUState *cpu, GString *buf); int gdb_get_cpu_index(CPUState *cpu); unsigned int gdb_get_max_cpus(void); /* both */ -bool gdb_can_reverse(void); /* system emulation, stub for user */ -// int gdb_target_sigtrap(void); /* user */ void gdb_create_default_process(GDBState *s); -/* signal mapping, common for system, specialised for user-mode */ int gdb_signal_to_target(int sig); -// int gdb_target_signal_to_gdb(int sig); - -int gdb_get_char(void); /* user only */ void gdb_continue(void); @@ -291,11 +207,6 @@ void gdb_handle_set_qemu_phy_mem_mode(GArray *params, void *ctx); void gdb_handle_file_io(GArray *params, void *user_ctx); void gdb_disable_syscalls(void); -/* - * Break/Watch point support - there is an implementation for system - * and user mode. - */ - // TODO bool runstate_is_running(); void vm_stop(RunState rs); @@ -326,27 +237,19 @@ uint32_t gdb_get_cpu_pid(CPUState *cpu); #define CPU_FOREACH(cpu) for (auto c = get_cpu(); false;) -/** - * gdb_target_memory_rw_debug() - handle debug access to memory - * @cs: CPUState - * @addr: nominal address, could be an entire physical address - * @buf: data - * @len: length of access - * @is_write: is it a write operation - * - * This function is specialised depending on the mode we are running - * in. For system guests we can switch the interpretation of the - * address to a physical address. - */ int gdb_target_memory_rw_debug(CPUState *cs, hwaddr addr, uint8_t *buf, int len, bool is_write); -GDBProcess *gdb_get_cpu_process(CPUState *cpu); CPUState *find_cpu(uint32_t thread_id); -CPUState *gdb_get_first_cpu_in_process(GDBProcess *process); CPUState *gdb_next_cpu_in_process(CPUState *cpu); CPUState *gdb_next_attached_cpu(CPUState *cpu); CPUState *gdb_first_attached_cpu(void); -CPUState *gdb_get_cpu(uint32_t pid, uint32_t tid); +CPUState *gdb_get_cpu(); const char *get_feature_xml(const char *p, const char **newp, GDBProcess *process); +void gdb_register_coprocessor(CPUState *cpu, gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg, const GDBFeature *feature, + int g_pos); + +void gdb_unregister_coprocessor_all(CPUState *cpu); +const char *gdb_get_core_xml_file(CPUState *cpu); + #endif /* GDBSTUB_INTERNALS_H */ diff --git a/gdbstub/system.c b/gdbstub/system.c index aecba3d..8ffb965 100644 --- a/gdbstub/system.c +++ b/gdbstub/system.c @@ -10,6 +10,8 @@ * SPDX-License-Identifier: LGPL-2.0-or-later */ +#include + #include "commands.h" #include "cpu.h" #include "enums.h" @@ -22,48 +24,16 @@ static void reset_gdbserver_state(void) { gdbserver_state.allow_stop_reply = false; } -/* - * Return the GDB index for a given vCPU state. - * - * In system mode GDB numbers CPUs from 1 as 0 is reserved as an "any - * cpu" index. - */ int gdb_get_cpu_index(CPUState *cpu) { return 0; } -/* - * We check the status of the last message in the chardev receive code - */ bool gdb_got_immediate_ack(void) { return true; } -static int gdb_chr_can_receive(void *opaque) { - /* - * We can handle an arbitrarily large amount of data. - * Pick the maximum packet size, which is as good as anything. - */ - return MAX_PACKET_LENGTH; -} - -static void gdb_chr_receive(void *opaque, const uint8_t *buf, int size) { - int i; - - for (i = 0; i < size; i++) { +void gdb_chr_receive(const uint8_t *buf, int size) { + for (int i = 0; i < size; i++) { gdb_read_byte(buf[i]); } } -static int pid_order(const void *a, const void *b) { - GDBProcess *pa = (GDBProcess *)a; - GDBProcess *pb = (GDBProcess *)b; - - if (pa->pid < pb->pid) { - return -1; - } else if (pa->pid > pb->pid) { - return 1; - } else { - return 0; - } -} - int gdb_target_memory_rw_debug(CPUState *cpu, hwaddr addr, uint8_t *buf, int len, bool is_write) { if (is_write) { cpu_physical_memory_write(addr, buf, len); @@ -75,8 +45,6 @@ int gdb_target_memory_rw_debug(CPUState *cpu, hwaddr addr, uint8_t *buf, int len unsigned int gdb_get_max_cpus(void) { return 1; } -bool gdb_can_reverse(void) { return false; } - void gdb_handle_query_qemu_phy_mem_mode(GArray *params, void *ctx) { g_string_printf(gdbserver_state.str_buf, "%d", 1); gdb_put_strbuf(); @@ -124,9 +92,6 @@ void gdb_continue(void) { } } -/* - * Resume execution, per CPU actions. - */ int gdb_continue_partial(char *newstates) { CPUState *cpu; int res = 0; @@ -134,37 +99,33 @@ int gdb_continue_partial(char *newstates) { if (!runstate_needs_reset()) { bool step_requested = false; - CPU_FOREACH(cpu) { - if (newstates[c->cpu_index] == 's') { - step_requested = true; - break; - } + if (newstates[get_cpu()->cpu_index] == 's') { + step_requested = true; } if (vm_prepare_start(step_requested)) { return 0; } - CPU_FOREACH(cpu) { - switch (newstates[c->cpu_index]) { - case 0: - case 1: - break; /* nothing to do here */ - case 's': - // trace_gdbstub_op_stepping(c->cpu_index); - cpu_single_step(c, gdbserver_state.sstep_flags); - cpu_resume(c); - flag = 1; - break; - case 'c': - // trace_gdbstub_op_continue_cpu(c->cpu_index); - cpu_resume(c); - flag = 1; - break; - default: - res = -1; - break; - } + auto c = get_cpu(); + switch (newstates[c->cpu_index]) { + case 0: + case 1: + break; /* nothing to do here */ + case 's': + // trace_gdbstub_op_stepping(c->cpu_index); + cpu_single_step(c, gdbserver_state.sstep_flags); + cpu_resume(c); + flag = 1; + break; + case 'c': + // trace_gdbstub_op_continue_cpu(c->cpu_index); + cpu_resume(c); + flag = 1; + break; + default: + res = -1; + break; } } if (flag) { @@ -173,11 +134,6 @@ int gdb_continue_partial(char *newstates) { return res; } -/* - * Signal Handling - in system mode we only need SIGINT and SIGTRAP; other - * signals are not yet supported. - */ - enum { TARGET_SIGINT = 2, TARGET_SIGTRAP = 5 }; int gdb_signal_to_target(int sig) { @@ -235,29 +191,11 @@ uint32_t gdb_get_cpu_pid(CPUState *cpu) { return cpu->cluster_index + 1; } -GDBProcess *gdb_get_cpu_process(CPUState *cpu) { return gdb_get_process(gdb_get_cpu_pid(cpu)); } - CPUState *find_cpu(uint32_t thread_id) { - CPUState *cpu; - - CPU_FOREACH(cpu) { - if (gdb_get_cpu_index(c) == thread_id) { - return cpu; - } + auto cpu = get_cpu(); + if (gdb_get_cpu_index(cpu) == thread_id) { + return cpu; } - - return NULL; -} - -CPUState *gdb_get_first_cpu_in_process(GDBProcess *process) { - CPUState *cpu; - - CPU_FOREACH(cpu) { - if (gdb_get_cpu_pid(c) == process->pid) { - return c; - } - } - return NULL; } @@ -279,74 +217,29 @@ CPUState *gdb_next_cpu_in_process(CPUState *cpu) { /* Return the cpu following @cpu, while ignoring unattached processes. */ CPUState *gdb_next_attached_cpu(CPUState *cpu) { cpu = cpu_next(cpu); - while (cpu) { - if (gdb_get_cpu_process(cpu)->attached) { + if (gdb_get_process()->attached) { break; } - cpu = cpu_next(cpu); } - return cpu; } /* Return the first attached cpu */ CPUState *gdb_first_attached_cpu(void) { CPUState *cpu = get_cpu(); - GDBProcess *process = gdb_get_cpu_process(cpu); - + GDBProcess *process = gdb_get_process(); if (!process->attached) { return gdb_next_attached_cpu(cpu); } - return cpu; } -CPUState *gdb_get_cpu(uint32_t pid, uint32_t tid) { - GDBProcess *process; - CPUState *cpu; - - if (!pid && !tid) { - /* 0 means any process/thread, we take the first attached one */ - return gdb_first_attached_cpu(); - } else if (pid && !tid) { - /* any thread in a specific process */ - process = gdb_get_process(pid); - - if (process == NULL) { - return NULL; - } - - if (!process->attached) { - return NULL; - } - - return gdb_get_first_cpu_in_process(process); - } else { - /* a specific thread */ - cpu = find_cpu(tid); - - if (cpu == NULL) { - return NULL; - } - - process = gdb_get_cpu_process(cpu); - - if (pid && process->pid != pid) { - return NULL; - } - - if (!process->attached) { - return NULL; - } - - return cpu; - } -} +CPUState *gdb_get_cpu() { return gdb_first_attached_cpu(); } const char *get_feature_xml(const char *p, const char **newp, GDBProcess *process) { - CPUState *cpu = gdb_get_first_cpu_in_process(process); + CPUState *cpu = get_cpu(); GDBRegisterState *r; size_t len; @@ -384,7 +277,100 @@ const char *get_feature_xml(const char *p, const char **newp, GDBProcess *proces return r->feature->xml; } } - - /* failed */ return NULL; } + +GDBProcess *gdb_get_process() { return gdbserver_state.processes; } + +void gdb_create_default_process(GDBState *s) { + GDBProcess *process; + s->processes = g_renew(GDBProcess, s->processes, ++s->process_num); + process = &s->processes[s->process_num - 1]; + process->pid = 0; + process->attached = false; + process->target_xml = NULL; +} + +static void gdb_register_feature(CPUState *cpu, int base_reg, gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg, + const GDBFeature *feature) { + GDBRegisterState s = {.base_reg = base_reg, .get_reg = get_reg, .set_reg = set_reg, .feature = feature}; + + g_array_append_val(cpu->gdb_regs, s); +} + +void gdb_init_cpu(CPUState *cpu) { + CPUClass *cc = cpu->cc; + const GDBFeature *feature; + const char *xmlfile = gdb_get_core_xml_file(cpu); + + cpu->gdb_regs = g_array_new(false, false, sizeof(GDBRegisterState)); + + if (xmlfile) { + feature = gdb_find_static_feature(xmlfile); + gdb_register_feature(cpu, 0, cc->gdb_read_register, cc->gdb_write_register, feature); + cpu->gdb_num_regs = cpu->gdb_num_g_regs = feature->num_regs; + } + + if (cc->gdb_num_core_regs) { + cpu->gdb_num_regs = cpu->gdb_num_g_regs = cc->gdb_num_core_regs; + } +} + +void gdb_register_coprocessor(CPUState *cpu, gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg, const GDBFeature *feature, + int g_pos) { + GDBRegisterState *s; + guint i; + int base_reg = cpu->gdb_num_regs; + + for (i = 0; i < cpu->gdb_regs->len; i++) { + /* Check for duplicates. */ + s = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); + if (s->feature == feature) { + return; + } + } + + gdb_register_feature(cpu, base_reg, get_reg, set_reg, feature); + + /* Add to end of list. */ + cpu->gdb_num_regs += feature->num_regs; + if (g_pos) { + if (g_pos != base_reg) { + std::cout << "Error: Bad gdb register numbering for" << feature->xml << ", xpected " << g_pos << " got " + << base_reg << std::endl; + } else { + cpu->gdb_num_g_regs = cpu->gdb_num_regs; + } + } +} + +void gdb_unregister_coprocessor_all(CPUState *cpu) { + /* + * Safe to nuke everything. GDBRegisterState::xml is static const char so + * it won't be freed + */ + g_array_free(cpu->gdb_regs, true); + + cpu->gdb_regs = NULL; + cpu->gdb_num_regs = 0; + cpu->gdb_num_g_regs = 0; +} + +const char *gdb_get_core_xml_file(CPUState *cpu) { + CPUClass *cc = cpu->cc; + + /* + * The CPU class can provide the XML filename via a method, + * or as a simple fixed string field. + */ + if (cc->gdb_get_core_xml_file) { + return cc->gdb_get_core_xml_file(cpu); + } + return cc->gdb_core_xml_file; +} + +void cpu_resume(CPUState *cpu) { + cpu->stop = false; + cpu->stopped = false; + // qemu_cpu_kick(cpu); +} \ No newline at end of file