From 05f7a713a37427a57d4dbf3587d58597f805c3f9 Mon Sep 17 00:00:00 2001 From: Colin Date: Mon, 22 Sep 2025 12:18:33 +0000 Subject: [PATCH] Pass qemu gdbstub code. --- .vscode/settings.json | 4 +- gdbstub/CMakeLists.txt | 1 + gdbstub/breakpoint.h | 1 + gdbstub/commands.h | 42 +- gdbstub/cpu.h | 21 +- gdbstub/enums.c | 48 + gdbstub/enums.h | 44 +- gdbstub/gdbstub.c | 3665 +++++++++++-------------- gdbstub/internals.h | 191 +- gdbstub/riscv64-softmmu-gdbstub-xml.c | 509 ++++ gdbstub/system.c | 647 ++--- gdbstub/user-target.c | 424 --- 12 files changed, 2613 insertions(+), 2984 deletions(-) create mode 100644 gdbstub/enums.c create mode 100644 gdbstub/riscv64-softmmu-gdbstub-xml.c delete mode 100644 gdbstub/user-target.c diff --git a/.vscode/settings.json b/.vscode/settings.json index 1b6e058..fbfe117 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -54,6 +54,8 @@ "stdexcept": "cpp", "streambuf": "cpp", "typeinfo": "cpp", - "internals.h": "c" + "internals.h": "c", + "stdint.h": "c", + "ctype.h": "c" } } \ No newline at end of file diff --git a/gdbstub/CMakeLists.txt b/gdbstub/CMakeLists.txt index 99bc37a..75f86de 100644 --- a/gdbstub/CMakeLists.txt +++ b/gdbstub/CMakeLists.txt @@ -56,6 +56,7 @@ link_directories(${GLIB_LIBRARY_DIRS}) # 添加链接库 file(GLOB UTILS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/utils/*.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/*.c ${CMAKE_CURRENT_SOURCE_DIR}/utils/*.c) set_source_files_properties(${UTILS_SRC} PROPERTIES LANGUAGE CXX) diff --git a/gdbstub/breakpoint.h b/gdbstub/breakpoint.h index 7334e78..bf6d784 100644 --- a/gdbstub/breakpoint.h +++ b/gdbstub/breakpoint.h @@ -11,6 +11,7 @@ // #include "qemu/queue.h" // #include "exec/vaddr.h" // #include "exec/memattrs.h" +#include typedef struct CPUBreakpoint { uintptr_t pc; diff --git a/gdbstub/commands.h b/gdbstub/commands.h index bff3674..380f4ee 100644 --- a/gdbstub/commands.h +++ b/gdbstub/commands.h @@ -1,28 +1,30 @@ #ifndef GDBSTUB_COMMANDS_H #define GDBSTUB_COMMANDS_H +#include "enums.h" + typedef void (*GdbCmdHandler)(GArray *params, void *user_ctx); typedef enum GDBThreadIdKind { - GDB_ONE_THREAD = 0, - GDB_ALL_THREADS, /* One process, all threads */ - GDB_ALL_PROCESSES, - GDB_READ_THREAD_ERR + GDB_ONE_THREAD = 0, + GDB_ALL_THREADS, /* One process, all threads */ + GDB_ALL_PROCESSES, + GDB_READ_THREAD_ERR } GDBThreadIdKind; typedef union GdbCmdVariant { - const char *data; - uint8_t opcode; - unsigned long val_ul; - unsigned long long val_ull; - struct { - GDBThreadIdKind kind; - uint32_t pid; - uint32_t tid; - } thread_id; + const char *data; + uint8_t opcode; + unsigned long val_ul; + unsigned long long val_ull; + struct { + GDBThreadIdKind kind; + uint32_t pid; + uint32_t tid; + } thread_id; } GdbCmdVariant; -#define gdb_get_cmd_param(p, i) (&g_array_index(p, GdbCmdVariant, i)) +#define gdb_get_cmd_param(p, i) (&g_array_index(p, GdbCmdVariant, i)) /** * typedef GdbCmdParseEntry - gdb command parser @@ -58,12 +60,12 @@ typedef union GdbCmdVariant { * @need_cpu_context: Pass current CPU context to command handler via user_ctx. */ typedef struct GdbCmdParseEntry { - GdbCmdHandler handler; - const char *cmd; - bool cmd_startswith; - const char *schema; - bool allow_stop_reply; - bool need_cpu_context; + GdbCmdHandler handler; + const char *cmd; + bool cmd_startswith; + const char *schema; + bool allow_stop_reply; + bool need_cpu_context; } GdbCmdParseEntry; /** diff --git a/gdbstub/cpu.h b/gdbstub/cpu.h index e8a3bd2..5e49550 100644 --- a/gdbstub/cpu.h +++ b/gdbstub/cpu.h @@ -163,7 +163,7 @@ struct CPUClass { // void (*parse_features)(const char *tname, char *str, Error **errp); int (*memory_rw_debug)(CPUState *cpu, vaddr addr, uint8_t *buf, size_t len, bool is_write); - void (*dump_state)(CPUState *cpu, FILE *, int flags); + // void (*dump_state)(CPUState *cpu, FILE *, int flags); // void (*query_cpu_fast)(CPUState *cpu, CpuInfoFast *value); int64_t (*get_arch_id)(CPUState *cpu); void (*set_pc)(CPUState *cpu, vaddr value); @@ -600,11 +600,6 @@ struct CPUState { typedef CPUState CPUTailQ; extern CPUTailQ cpus_queue; -#define first_cpu QTAILQ_FIRST_RCU(&cpus_queue) -#define CPU_NEXT(cpu) QTAILQ_NEXT_RCU(cpu, node) -#define CPU_FOREACH(cpu) QTAILQ_FOREACH_RCU(cpu, &cpus_queue, node) -#define CPU_FOREACH_SAFE(cpu, next_cpu) QTAILQ_FOREACH_SAFE_RCU(cpu, &cpus_queue, node, next_cpu) - extern __thread CPUState *current_cpu; /** @@ -691,7 +686,7 @@ enum CPUDumpFlags { * * Dumps CPU state. */ -void cpu_dump_state(CPUState *cpu, FILE *f, int flags); +// void cpu_dump_state(CPUState *cpu, FILE *f, int flags); /** * cpu_get_phys_page_attrs_debug: @@ -1133,13 +1128,13 @@ void qemu_process_cpu_events(CPUState *cpu); /* $(top_srcdir)/cpu.c */ // void cpu_class_init_props(DeviceClass *dc); -void cpu_exec_class_post_init(CPUClass *cc); -void cpu_exec_initfn(CPUState *cpu); -void cpu_vmstate_register(CPUState *cpu); -void cpu_vmstate_unregister(CPUState *cpu); +// void cpu_exec_class_post_init(CPUClass *cc); +// void cpu_exec_initfn(CPUState *cpu); +// void cpu_vmstate_register(CPUState *cpu); +// void cpu_vmstate_unregister(CPUState *cpu); // bool cpu_exec_realizefn(CPUState *cpu, Error **errp); -void cpu_exec_unrealizefn(CPUState *cpu); -void cpu_exec_reset_hold(CPUState *cpu); +// void cpu_exec_unrealizefn(CPUState *cpu); +// void cpu_exec_reset_hold(CPUState *cpu); // extern const VMStateDescription vmstate_cpu_common; diff --git a/gdbstub/enums.c b/gdbstub/enums.c new file mode 100644 index 0000000..8283f34 --- /dev/null +++ b/gdbstub/enums.c @@ -0,0 +1,48 @@ + +#include "enums.h" + +#include + +int qemu_strtoul(const char *nptr, const char **endptr, int base, unsigned long *result) { + char *ep; + + assert((unsigned)base <= 36 && base != 1); + if (!nptr) { + *result = 0; + if (endptr) { + *endptr = nptr; + } + return -EINVAL; + } + + errno = 0; + *result = strtoul(nptr, &ep, base); + /* Windows returns 1 for negative out-of-range values. */ + if (errno == ERANGE) { + *result = -1; + } + return 0; +} + +int qemu_strtou64(const char *nptr, const char **endptr, int base, uint64_t *result) { + char *ep; + + assert((unsigned)base <= 36 && base != 1); + if (!nptr) { + *result = 0; + if (endptr) { + *endptr = nptr; + } + return -EINVAL; + } + + /* This assumes uint64_t is unsigned long long TODO relax */ + // QEMU_BUILD_BUG_ON(sizeof(uint64_t) != sizeof(unsigned long long)); + errno = 0; + *result = strtoull(nptr, &ep, base); + /* Windows returns 1 for negative out-of-range values. */ + if (errno == ERANGE) { + *result = -1; + } + return 0; +} diff --git a/gdbstub/enums.h b/gdbstub/enums.h index 0377e70..3c0e126 100644 --- a/gdbstub/enums.h +++ b/gdbstub/enums.h @@ -11,17 +11,51 @@ #define DEFAULT_GDBSTUB_PORT "1234" +#include +#include +#include +#include typedef uintptr_t vaddr; +typedef uint64_t hwaddr; typedef int Error; #define MMU_ACCESS_COUNT 16 /* GDB breakpoint/watchpoint types */ -#define GDB_BREAKPOINT_SW 0 -#define GDB_BREAKPOINT_HW 1 -#define GDB_WATCHPOINT_WRITE 2 -#define GDB_WATCHPOINT_READ 3 -#define GDB_WATCHPOINT_ACCESS 4 +#define GDB_BREAKPOINT_SW 0 +#define GDB_BREAKPOINT_HW 1 +#define GDB_WATCHPOINT_WRITE 2 +#define GDB_WATCHPOINT_READ 3 +#define GDB_WATCHPOINT_ACCESS 4 + +typedef enum RunState { + RUN_STATE_DEBUG, + RUN_STATE_INMIGRATE, + RUN_STATE_INTERNAL_ERROR, + RUN_STATE_IO_ERROR, + RUN_STATE_PAUSED, + RUN_STATE_POSTMIGRATE, + RUN_STATE_PRELAUNCH, + RUN_STATE_FINISH_MIGRATE, + RUN_STATE_RESTORE_VM, + RUN_STATE_RUNNING, + RUN_STATE_SAVE_VM, + RUN_STATE_SHUTDOWN, + RUN_STATE_SUSPENDED, + RUN_STATE_WATCHDOG, + RUN_STATE_GUEST_PANICKED, + RUN_STATE_COLO, + RUN_STATE__MAX, +} RunState; + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif +#define isxdigit(c) isctype((c), _ISxdigit) + +int qemu_strtoul(const char *nptr, const char **endptr, int base, unsigned long *result); + +int qemu_strtou64(const char *nptr, const char **endptr, int base, uint64_t *result); #endif /* GDBSTUB_ENUMS_H */ diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index dd5fb56..1230f6f 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -23,701 +23,623 @@ * SPDX-License-Identifier: LGPL-2.0-or-later */ -#include "qemu/osdep.h" -#include "qemu/ctype.h" -#include "qemu/cutils.h" -#include "qemu/module.h" -#include "qemu/error-report.h" -#include "qemu/target-info.h" -#include "trace.h" -#include "exec/gdbstub.h" -#include "gdbstub/commands.h" -#include "gdbstub/syscalls.h" -#ifdef CONFIG_USER_ONLY -#include "accel/tcg/vcpu-state.h" -#include "gdbstub/user.h" -#else -#include "hw/cpu/cluster.h" -#include "hw/boards.h" -#endif -#include "hw/core/cpu.h" +// #include "qemu/osdep.h" +// #include "qemu/ctype.h" +// #include "qemu/cutils.h" +// #include "qemu/module.h" +// #include "qemu/error-report.h" +// #include "qemu/target-info.h" +// #include "trace.h" +// #include "exec/gdbstub.h" +// #include "gdbstub/commands.h" +// #include "gdbstub/syscalls.h" +// #include "hw/cpu/cluster.h" +// #include "hw/boards.h" +// #include "hw/core/cpu.h" -#include "system/hw_accel.h" -#include "system/runstate.h" -#include "exec/replay-core.h" -#include "exec/hwaddr.h" +// #include "system/hw_accel.h" +// #include "system/runstate.h" +// #include "exec/replay-core.h" +// #include "exec/hwaddr.h" +#include "gdbstub.h" + +#include + +#include "commands.h" +#include "cpu.h" +#include "enums.h" #include "internals.h" +// #include "trace.h" + typedef struct GDBRegisterState { - int base_reg; - gdb_get_reg_cb get_reg; - gdb_set_reg_cb set_reg; - const GDBFeature *feature; + int base_reg; + gdb_get_reg_cb get_reg; + gdb_set_reg_cb set_reg; + const GDBFeature *feature; } GDBRegisterState; GDBState gdbserver_state; -void gdb_init_gdbserver_state(void) -{ - g_assert(!gdbserver_state.init); - memset(&gdbserver_state, 0, sizeof(GDBState)); - gdbserver_state.init = true; - gdbserver_state.str_buf = g_string_new(NULL); - gdbserver_state.mem_buf = g_byte_array_sized_new(MAX_PACKET_LENGTH); - gdbserver_state.last_packet = g_byte_array_sized_new(MAX_PACKET_LENGTH + 4); +void gdb_init_gdbserver_state(void) { + g_assert(!gdbserver_state.init); + memset(&gdbserver_state, 0, sizeof(GDBState)); + gdbserver_state.init = true; + gdbserver_state.str_buf = g_string_new(NULL); + gdbserver_state.mem_buf = g_byte_array_sized_new(MAX_PACKET_LENGTH); + gdbserver_state.last_packet = g_byte_array_sized_new(MAX_PACKET_LENGTH + 4); - /* - * What single-step modes are supported is accelerator dependent. - * By default try to use no IRQs and no timers while single - * stepping so as to make single stepping like a typical ICE HW step. - */ - gdbserver_state.supported_sstep_flags = accel_supported_gdbstub_sstep_flags(); - gdbserver_state.sstep_flags = SSTEP_ENABLE | SSTEP_NOIRQ | SSTEP_NOTIMER; - gdbserver_state.sstep_flags &= gdbserver_state.supported_sstep_flags; + /* + * What single-step modes are supported is accelerator dependent. + * By default try to use no IRQs and no timers while single + * stepping so as to make single stepping like a typical ICE HW step. + */ + gdbserver_state.supported_sstep_flags = 0; + gdbserver_state.sstep_flags = SSTEP_ENABLE | SSTEP_NOIRQ | SSTEP_NOTIMER; + gdbserver_state.sstep_flags &= gdbserver_state.supported_sstep_flags; } /* writes 2*len+1 bytes in buf */ -void gdb_memtohex(GString *buf, const uint8_t *mem, int len) -{ - int i, c; - for(i = 0; i < len; i++) { - c = mem[i]; - g_string_append_c(buf, tohex(c >> 4)); - g_string_append_c(buf, tohex(c & 0xf)); - } - g_string_append_c(buf, '\0'); +void gdb_memtohex(GString *buf, const uint8_t *mem, int len) { + int i, c; + for (i = 0; i < len; i++) { + c = mem[i]; + g_string_append_c(buf, tohex(c >> 4)); + g_string_append_c(buf, tohex(c & 0xf)); + } + g_string_append_c(buf, '\0'); } -void gdb_hextomem(GByteArray *mem, const char *buf, int len) -{ - int i; +void gdb_hextomem(GByteArray *mem, const char *buf, int len) { + int i; - for(i = 0; i < len; i++) { - guint8 byte = fromhex(buf[0]) << 4 | fromhex(buf[1]); - g_byte_array_append(mem, &byte, 1); - buf += 2; - } + for (i = 0; i < len; i++) { + guint8 byte = fromhex(buf[0]) << 4 | fromhex(buf[1]); + g_byte_array_append(mem, &byte, 1); + buf += 2; + } } -static void hexdump(const char *buf, int len, - void (*trace_fn)(size_t ofs, char const *text)) -{ - char line_buffer[3 * 16 + 4 + 16 + 1]; +static void hexdump(const char *buf, int len, void (*trace_fn)(size_t ofs, char const *text)) { + char line_buffer[3 * 16 + 4 + 16 + 1]; - size_t i; - for (i = 0; i < len || (i & 0xF); ++i) { - size_t byte_ofs = i & 15; + size_t i; + for (i = 0; i < len || (i & 0xF); ++i) { + size_t byte_ofs = i & 15; - if (byte_ofs == 0) { - memset(line_buffer, ' ', 3 * 16 + 4 + 16); - line_buffer[3 * 16 + 4 + 16] = 0; - } - - size_t col_group = (i >> 2) & 3; - size_t hex_col = byte_ofs * 3 + col_group; - size_t txt_col = 3 * 16 + 4 + byte_ofs; - - if (i < len) { - char value = buf[i]; - - line_buffer[hex_col + 0] = tohex((value >> 4) & 0xF); - line_buffer[hex_col + 1] = tohex((value >> 0) & 0xF); - line_buffer[txt_col + 0] = (value >= ' ' && value < 127) - ? value - : '.'; - } - - if (byte_ofs == 0xF) - trace_fn(i & -16, line_buffer); + if (byte_ofs == 0) { + memset(line_buffer, ' ', 3 * 16 + 4 + 16); + line_buffer[3 * 16 + 4 + 16] = 0; } + + size_t col_group = (i >> 2) & 3; + size_t hex_col = byte_ofs * 3 + col_group; + size_t txt_col = 3 * 16 + 4 + byte_ofs; + + if (i < len) { + char value = buf[i]; + + line_buffer[hex_col + 0] = tohex((value >> 4) & 0xF); + line_buffer[hex_col + 1] = tohex((value >> 0) & 0xF); + line_buffer[txt_col + 0] = (value >= ' ' && value < 127) ? value : '.'; + } + + // if (byte_ofs == 0xF) trace_fn(i & -16, line_buffer); + } } /* return -1 if error, 0 if OK */ -int gdb_put_packet_binary(const char *buf, int len, bool dump) -{ - int csum, i; - uint8_t footer[3]; +int gdb_put_packet_binary(const char *buf, int len, bool dump) { + int csum, i; + uint8_t footer[3]; - if (dump && trace_event_get_state_backends(TRACE_GDBSTUB_IO_BINARYREPLY)) { - hexdump(buf, len, trace_gdbstub_io_binaryreply); + // if (dump && trace_event_get_state_backends(TRACE_GDBSTUB_IO_BINARYREPLY)) { + // hexdump(buf, len, trace_gdbstub_io_binaryreply); + // } + + for (;;) { + g_byte_array_set_size(gdbserver_state.last_packet, 0); + g_byte_array_append(gdbserver_state.last_packet, (const uint8_t *)"$", 1); + g_byte_array_append(gdbserver_state.last_packet, (const uint8_t *)buf, len); + csum = 0; + for (i = 0; i < len; i++) { + csum += buf[i]; } + footer[0] = '#'; + footer[1] = tohex((csum >> 4) & 0xf); + footer[2] = tohex((csum) & 0xf); + g_byte_array_append(gdbserver_state.last_packet, footer, 3); - for(;;) { - g_byte_array_set_size(gdbserver_state.last_packet, 0); - g_byte_array_append(gdbserver_state.last_packet, - (const uint8_t *) "$", 1); - g_byte_array_append(gdbserver_state.last_packet, - (const uint8_t *) buf, len); - csum = 0; - for(i = 0; i < len; i++) { - csum += buf[i]; - } - footer[0] = '#'; - footer[1] = tohex((csum >> 4) & 0xf); - footer[2] = tohex((csum) & 0xf); - g_byte_array_append(gdbserver_state.last_packet, footer, 3); + gdb_put_buffer(gdbserver_state.last_packet->data, gdbserver_state.last_packet->len); - gdb_put_buffer(gdbserver_state.last_packet->data, - gdbserver_state.last_packet->len); - - if (gdb_got_immediate_ack()) { - break; - } + if (gdb_got_immediate_ack()) { + break; } - return 0; + } + return 0; } /* return -1 if error, 0 if OK */ -int gdb_put_packet(const char *buf) -{ - trace_gdbstub_io_reply(buf); +int gdb_put_packet(const char *buf) { + // trace_gdbstub_io_reply(buf); - return gdb_put_packet_binary(buf, strlen(buf), false); + return gdb_put_packet_binary(buf, strlen(buf), false); } -void gdb_put_strbuf(void) -{ - gdb_put_packet(gdbserver_state.str_buf->str); -} +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; +void gdb_memtox(GString *buf, const char *mem, int len) { + char c; - while (len--) { - c = *(mem++); - switch (c) { - case '#': case '$': case '*': case '}': - g_string_append_c(buf, '}'); - g_string_append_c(buf, c ^ 0x20); - break; - default: - g_string_append_c(buf, c); - break; - } + while (len--) { + c = *(mem++); + switch (c) { + case '#': + case '$': + case '*': + case '}': + g_string_append_c(buf, '}'); + g_string_append_c(buf, c ^ 0x20); + break; + default: + g_string_append_c(buf, c); + break; } + } } -static uint32_t gdb_get_cpu_pid(CPUState *cpu) -{ -#ifdef CONFIG_USER_ONLY - return getpid(); -#else - if (cpu->cluster_index == UNASSIGNED_CLUSTER_INDEX) { - /* Return the default process' PID */ - int index = gdbserver_state.process_num - 1; - return gdbserver_state.processes[index].pid; - } - return cpu->cluster_index + 1; -#endif +static uint32_t gdb_get_cpu_pid(CPUState *cpu) { + if (cpu->cluster_index == UNASSIGNED_CLUSTER_INDEX) { + /* Return the default process' PID */ + int index = gdbserver_state.process_num - 1; + return gdbserver_state.processes[index].pid; + } + return cpu->cluster_index + 1; } -GDBProcess *gdb_get_process(uint32_t pid) -{ - int i; +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]; + 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]; } + } - for (i = 0; i < gdbserver_state.process_num; i++) { - if (gdbserver_state.processes[i].pid == pid) { - return &gdbserver_state.processes[i]; - } - } - - return NULL; + return NULL; } -static GDBProcess *gdb_get_cpu_process(CPUState *cpu) -{ - return gdb_get_process(gdb_get_cpu_pid(cpu)); +static GDBProcess *gdb_get_cpu_process(CPUState *cpu) { return gdb_get_process(gdb_get_cpu_pid(cpu)); } + +static CPUState *find_cpu(uint32_t thread_id) { + CPUState *cpu; + + CPU_FOREACH(cpu) { + if (gdb_get_cpu_index(c) == thread_id) { + return cpu; + } + } + + return NULL; } -static CPUState *find_cpu(uint32_t thread_id) -{ - CPUState *cpu; +CPUState *gdb_get_first_cpu_in_process(GDBProcess *process) { + CPUState *cpu; - CPU_FOREACH(cpu) { - if (gdb_get_cpu_index(cpu) == thread_id) { - return cpu; - } + CPU_FOREACH(cpu) { + if (gdb_get_cpu_pid(c) == process->pid) { + return c; } + } - return NULL; + return NULL; } -CPUState *gdb_get_first_cpu_in_process(GDBProcess *process) -{ - CPUState *cpu; +static CPUState *gdb_next_cpu_in_process(CPUState *cpu) { + uint32_t pid = gdb_get_cpu_pid(cpu); + cpu = cpu_next(cpu); - CPU_FOREACH(cpu) { - if (gdb_get_cpu_pid(cpu) == process->pid) { - return cpu; - } + while (cpu) { + if (gdb_get_cpu_pid(cpu) == pid) { + break; } - return NULL; -} + cpu = cpu_next(cpu); + } -static CPUState *gdb_next_cpu_in_process(CPUState *cpu) -{ - uint32_t pid = gdb_get_cpu_pid(cpu); - cpu = CPU_NEXT(cpu); - - while (cpu) { - if (gdb_get_cpu_pid(cpu) == pid) { - break; - } - - cpu = CPU_NEXT(cpu); - } - - return cpu; + return cpu; } /* Return the cpu following @cpu, while ignoring unattached processes. */ -static CPUState *gdb_next_attached_cpu(CPUState *cpu) -{ - cpu = CPU_NEXT(cpu); +static CPUState *gdb_next_attached_cpu(CPUState *cpu) { + cpu = cpu_next(cpu); - while (cpu) { - if (gdb_get_cpu_process(cpu)->attached) { - break; - } - - cpu = CPU_NEXT(cpu); + while (cpu) { + if (gdb_get_cpu_process(cpu)->attached) { + break; } - return cpu; + cpu = cpu_next(cpu); + } + + return cpu; } /* Return the first attached cpu */ -CPUState *gdb_first_attached_cpu(void) -{ - CPUState *cpu = first_cpu; - GDBProcess *process = gdb_get_cpu_process(cpu); +CPUState *gdb_first_attached_cpu(void) { + CPUState *cpu = get_cpu(); + GDBProcess *process = gdb_get_cpu_process(cpu); + + if (!process->attached) { + return gdb_next_attached_cpu(cpu); + } + + return cpu; +} + +static 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 gdb_next_attached_cpu(cpu); + 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; + } } -static CPUState *gdb_get_cpu(uint32_t pid, uint32_t tid) -{ - GDBProcess *process; - CPUState *cpu; +static const char *get_feature_xml(const char *p, const char **newp, GDBProcess *process) { + CPUState *cpu = gdb_get_first_cpu_in_process(process); + GDBRegisterState *r; + size_t len; - 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); + /* + * qXfer:features:read:ANNEX:OFFSET,LENGTH' + * ^p ^newp + */ + const char *term = strchr(p, ':'); + *newp = term + 1; + len = term - p; - if (process == NULL) { - return NULL; - } + /* Is it the main target xml? */ + if (strncmp(p, "target.xml", len) == 0) { + if (!process->target_xml) { + g_autoptr(GPtrArray) xml = g_ptr_array_new_with_free_func(g_free); - if (!process->attached) { - return NULL; - } + g_ptr_array_add(xml, g_strdup("" + "" + "")); - 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; - } -} - -static const char *get_feature_xml(const char *p, const char **newp, - GDBProcess *process) -{ - CPUState *cpu = gdb_get_first_cpu_in_process(process); - GDBRegisterState *r; - size_t len; - - /* - * qXfer:features:read:ANNEX:OFFSET,LENGTH' - * ^p ^newp - */ - char *term = strchr(p, ':'); - *newp = term + 1; - len = term - p; - - /* Is it the main target xml? */ - if (strncmp(p, "target.xml", len) == 0) { - if (!process->target_xml) { - g_autoptr(GPtrArray) xml = g_ptr_array_new_with_free_func(g_free); - - g_ptr_array_add( - xml, - g_strdup("" - "" - "")); - - if (cpu->cc->gdb_arch_name) { - g_ptr_array_add( - xml, - g_markup_printf_escaped("%s", - cpu->cc->gdb_arch_name(cpu))); - } - for (guint i = 0; i < cpu->gdb_regs->len; i++) { - r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); - g_ptr_array_add( - xml, - g_markup_printf_escaped("", - r->feature->xmlname)); - } - g_ptr_array_add(xml, g_strdup("")); - g_ptr_array_add(xml, NULL); - - process->target_xml = g_strjoinv(NULL, (void *)xml->pdata); - } - return process->target_xml; - } - /* Is it one of the features? */ - for (guint i = 0; i < cpu->gdb_regs->len; i++) { + if (cpu->cc->gdb_arch_name) { + g_ptr_array_add(xml, g_markup_printf_escaped("%s", cpu->cc->gdb_arch_name(cpu))); + } + for (guint i = 0; i < cpu->gdb_regs->len; i++) { r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); - if (strncmp(p, r->feature->xmlname, len) == 0) { - return r->feature->xml; - } - } + g_ptr_array_add(xml, g_markup_printf_escaped("", r->feature->xmlname)); + } + g_ptr_array_add(xml, g_strdup("")); + g_ptr_array_add(xml, NULL); - /* failed */ - return NULL; + process->target_xml = g_strjoinv(NULL, (gchar **)xml->pdata); + } + return process->target_xml; + } + /* Is it one of the features? */ + for (guint i = 0; i < cpu->gdb_regs->len; i++) { + r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); + if (strncmp(p, r->feature->xmlname, len) == 0) { + return r->feature->xml; + } + } + + /* failed */ + return NULL; } -void gdb_feature_builder_init(GDBFeatureBuilder *builder, GDBFeature *feature, - const char *name, const char *xmlname, - int base_reg) -{ - char *header = g_markup_printf_escaped( - "" - "" - "", - name); +void gdb_feature_builder_init(GDBFeatureBuilder *builder, GDBFeature *feature, const char *name, const char *xmlname, + int base_reg) { + char *header = g_markup_printf_escaped( + "" + "" + "", + name); - builder->feature = feature; - builder->xml = g_ptr_array_new(); - g_ptr_array_add(builder->xml, header); - builder->regs = g_ptr_array_new(); - builder->base_reg = base_reg; - feature->xmlname = xmlname; - feature->name = name; + builder->feature = feature; + builder->xml = g_ptr_array_new(); + g_ptr_array_add(builder->xml, header); + builder->regs = g_ptr_array_new(); + builder->base_reg = base_reg; + feature->xmlname = xmlname; + feature->name = name; } -void gdb_feature_builder_append_tag(const GDBFeatureBuilder *builder, - const char *format, ...) -{ - va_list ap; - va_start(ap, format); - g_ptr_array_add(builder->xml, g_markup_vprintf_escaped(format, ap)); - va_end(ap); +void gdb_feature_builder_append_tag(const GDBFeatureBuilder *builder, const char *format, ...) { + va_list ap; + va_start(ap, format); + g_ptr_array_add(builder->xml, g_markup_vprintf_escaped(format, ap)); + va_end(ap); } -void gdb_feature_builder_append_reg(const GDBFeatureBuilder *builder, - const char *name, - int bitsize, - int regnum, - const char *type, - const char *group) -{ - if (builder->regs->len <= regnum) { - g_ptr_array_set_size(builder->regs, regnum + 1); - } +void gdb_feature_builder_append_reg(const GDBFeatureBuilder *builder, const char *name, int bitsize, int regnum, + const char *type, const char *group) { + if (builder->regs->len <= regnum) { + g_ptr_array_set_size(builder->regs, regnum + 1); + } - builder->regs->pdata[regnum] = (gpointer *)name; + builder->regs->pdata[regnum] = (gpointer *)name; - if (group) { - gdb_feature_builder_append_tag( - builder, - "", - name, bitsize, builder->base_reg + regnum, type, group); - } else { - gdb_feature_builder_append_tag( - builder, - "", - name, bitsize, builder->base_reg + regnum, type); - } + if (group) { + gdb_feature_builder_append_tag(builder, "", + name, bitsize, builder->base_reg + regnum, type, group); + } else { + gdb_feature_builder_append_tag(builder, "", name, + bitsize, builder->base_reg + regnum, type); + } } -void gdb_feature_builder_end(const GDBFeatureBuilder *builder) -{ - g_ptr_array_add(builder->xml, (void *)""); - g_ptr_array_add(builder->xml, NULL); +void gdb_feature_builder_end(const GDBFeatureBuilder *builder) { + g_ptr_array_add(builder->xml, (void *)""); + g_ptr_array_add(builder->xml, NULL); - builder->feature->xml = g_strjoinv(NULL, (void *)builder->xml->pdata); + builder->feature->xml = g_strjoinv(NULL, (gchar **)builder->xml->pdata); - for (guint i = 0; i < builder->xml->len - 2; i++) { - g_free(g_ptr_array_index(builder->xml, i)); - } + for (guint i = 0; i < builder->xml->len - 2; i++) { + g_free(g_ptr_array_index(builder->xml, i)); + } - g_ptr_array_free(builder->xml, TRUE); + g_ptr_array_free(builder->xml, TRUE); - builder->feature->num_regs = builder->regs->len; - builder->feature->regs = (void *)g_ptr_array_free(builder->regs, FALSE); + builder->feature->num_regs = builder->regs->len; + builder->feature->regs = (gchar **)g_ptr_array_free(builder->regs, FALSE); } -const GDBFeature *gdb_find_static_feature(const char *xmlname) -{ - const GDBFeature *feature; +const GDBFeature *gdb_find_static_feature(const char *xmlname) { + const GDBFeature *feature; - for (feature = gdb_static_features; feature->xmlname; feature++) { - if (!strcmp(feature->xmlname, xmlname)) { - return feature; - } + for (feature = gdb_static_features; feature->xmlname; feature++) { + if (!strcmp(feature->xmlname, xmlname)) { + return feature; } + } - g_assert_not_reached(); + g_assert_not_reached(); } -GArray *gdb_get_register_list(CPUState *cpu) -{ - GArray *results = g_array_new(true, true, sizeof(GDBRegDesc)); - - /* registers are only available once the CPU is initialised */ - if (!cpu->gdb_regs) { - return results; - } - - for (int f = 0; f < cpu->gdb_regs->len; f++) { - GDBRegisterState *r = &g_array_index(cpu->gdb_regs, GDBRegisterState, f); - for (int i = 0; i < r->feature->num_regs; i++) { - const char *name = r->feature->regs[i]; - GDBRegDesc desc = { - r->base_reg + i, - name, - r->feature->name - }; - g_array_append_val(results, desc); - } - } +GArray *gdb_get_register_list(CPUState *cpu) { + GArray *results = g_array_new(true, true, sizeof(GDBRegDesc)); + /* registers are only available once the CPU is initialised */ + if (!cpu->gdb_regs) { return results; + } + + for (int f = 0; f < cpu->gdb_regs->len; f++) { + GDBRegisterState *r = &g_array_index(cpu->gdb_regs, GDBRegisterState, f); + for (int i = 0; i < r->feature->num_regs; i++) { + const char *name = r->feature->regs[i]; + GDBRegDesc desc = {r->base_reg + i, name, r->feature->name}; + g_array_append_val(results, desc); + } + } + + return results; } -int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg) -{ - GDBRegisterState *r; +int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg) { + GDBRegisterState *r; - if (reg < cpu->cc->gdb_num_core_regs) { - return cpu->cc->gdb_read_register(cpu, buf, reg); - } + if (reg < cpu->cc->gdb_num_core_regs) { + return cpu->cc->gdb_read_register(cpu, buf, reg); + } - for (guint i = 0; i < cpu->gdb_regs->len; i++) { - r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); - if (r->base_reg <= reg && reg < r->base_reg + r->feature->num_regs) { - return r->get_reg(cpu, buf, reg - r->base_reg); - } + for (guint i = 0; i < cpu->gdb_regs->len; i++) { + r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); + if (r->base_reg <= reg && reg < r->base_reg + r->feature->num_regs) { + return r->get_reg(cpu, buf, reg - r->base_reg); } - return 0; + } + return 0; } -int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg) -{ - GDBRegisterState *r; +int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg) { + GDBRegisterState *r; - if (reg < cpu->cc->gdb_num_core_regs) { - return cpu->cc->gdb_write_register(cpu, mem_buf, reg); - } + if (reg < cpu->cc->gdb_num_core_regs) { + return cpu->cc->gdb_write_register(cpu, mem_buf, reg); + } - for (guint i = 0; i < cpu->gdb_regs->len; i++) { - r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); - if (r->base_reg <= reg && reg < r->base_reg + r->feature->num_regs) { - return r->set_reg(cpu, mem_buf, reg - r->base_reg); - } + for (guint i = 0; i < cpu->gdb_regs->len; i++) { + r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); + if (r->base_reg <= reg && reg < r->base_reg + r->feature->num_regs) { + return r->set_reg(cpu, mem_buf, reg - r->base_reg); } - return 0; + } + 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 - }; +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); + g_array_append_val(cpu->gdb_regs, s); } -static const char *gdb_get_core_xml_file(CPUState *cpu) -{ - CPUClass *cc = cpu->cc; +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); + /* + * 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; } - 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); + gdb_register_feature(cpu, base_reg, get_reg, set_reg, feature); - 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) { - error_report("Error: Bad gdb register numbering for '%s', " - "expected %d got %d", feature->xml, g_pos, base_reg); - } 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); - - while (cpu) { - gdb_breakpoint_remove_all(cpu); - cpu = gdb_next_cpu_in_process(cpu); - } -} - - -static void gdb_set_cpu_pc(vaddr pc) -{ - CPUState *cpu = gdbserver_state.c_cpu; - - cpu_synchronize_state(cpu); - 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)); + /* 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 { - g_string_append_printf(buf, "%02x", gdb_get_cpu_index(cpu)); + cpu->gdb_num_g_regs = cpu->gdb_num_regs; } + } } -static GDBThreadIdKind read_thread_id(const char *buf, const char **end_buf, - uint32_t *pid, uint32_t *tid) -{ - unsigned long p, t; - int ret; +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); - if (*buf == 'p') { - buf++; - ret = qemu_strtoul(buf, &buf, 16, &p); + cpu->gdb_regs = NULL; + cpu->gdb_num_regs = 0; + cpu->gdb_num_g_regs = 0; +} - if (ret) { - return GDB_READ_THREAD_ERR; - } +static void gdb_process_breakpoint_remove_all(GDBProcess *p) { + CPUState *cpu = gdb_get_first_cpu_in_process(p); - /* Skip '.' */ - buf++; - } else { - p = 0; - } + while (cpu) { + gdb_breakpoint_remove_all(cpu); + cpu = gdb_next_cpu_in_process(cpu); + } +} - ret = qemu_strtoul(buf, &buf, 16, &t); +static void gdb_set_cpu_pc(vaddr pc) { + CPUState *cpu = gdbserver_state.c_cpu; + + cpu_synchronize_state(cpu); + 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)); + } +} + +static GDBThreadIdKind read_thread_id(const char *buf, const char **end_buf, uint32_t *pid, uint32_t *tid) { + unsigned long p, t; + int ret; + + if (*buf == 'p') { + buf++; + ret = qemu_strtoul(buf, &buf, 16, &p); if (ret) { - return GDB_READ_THREAD_ERR; + return GDB_READ_THREAD_ERR; } - *end_buf = buf; + /* Skip '.' */ + buf++; + } else { + p = 0; + } - if (p == -1) { - return GDB_ALL_PROCESSES; - } + ret = qemu_strtoul(buf, &buf, 16, &t); - if (pid) { - *pid = p; - } + if (ret) { + return GDB_READ_THREAD_ERR; + } - if (t == -1) { - return GDB_ALL_THREADS; - } + *end_buf = buf; - if (tid) { - *tid = t; - } + if (p == -1) { + return GDB_ALL_PROCESSES; + } - return GDB_ONE_THREAD; + if (pid) { + *pid = p; + } + + if (t == -1) { + return GDB_ALL_THREADS; + } + + if (tid) { + *tid = t; + } + + return GDB_ONE_THREAD; } /** @@ -725,479 +647,442 @@ static GDBThreadIdKind read_thread_id(const char *buf, const char **end_buf, * returns -ENOTSUP if a command is unsupported, -EINVAL or -ERANGE if there is * a format error, 0 on success. */ -static int gdb_handle_vcont(const char *p) -{ - int res, signal = 0; - char cur_action; - unsigned long tmp; - uint32_t pid, tid; - GDBProcess *process; - CPUState *cpu; - GDBThreadIdKind kind; - unsigned int max_cpus = gdb_get_max_cpus(); - /* uninitialised CPUs stay 0 */ - g_autofree char *newstates = g_new0(char, max_cpus); +static int gdb_handle_vcont(const char *p) { + int res, signal = 0; + char cur_action; + unsigned long tmp; + uint32_t pid, tid; + GDBProcess *process; + CPUState *cpu; + GDBThreadIdKind kind; + unsigned int max_cpus = gdb_get_max_cpus(); + /* uninitialised CPUs stay 0 */ + g_autofree char *newstates = g_new0(char, max_cpus); - /* mark valid CPUs with 1 */ - CPU_FOREACH(cpu) { - newstates[cpu->cpu_index] = 1; + /* mark valid CPUs with 1 */ + CPU_FOREACH(cpu) { newstates[c->cpu_index] = 1; } + + /* + * res keeps track of what error we are returning, with -ENOTSUP meaning + * that the command is unknown or unsupported, thus returning an empty + * packet, while -EINVAL and -ERANGE cause an E22 packet, due to invalid, + * or incorrect parameters passed. + */ + res = 0; + + /* + * target_count and last_target keep track of how many CPUs we are going to + * step or resume, and a pointer to the state structure of one of them, + * respectively + */ + int target_count = 0; + CPUState *last_target = NULL; + + while (*p) { + if (*p++ != ';') { + return -ENOTSUP; } - /* - * res keeps track of what error we are returning, with -ENOTSUP meaning - * that the command is unknown or unsupported, thus returning an empty - * packet, while -EINVAL and -ERANGE cause an E22 packet, due to invalid, - * or incorrect parameters passed. - */ - res = 0; - - /* - * target_count and last_target keep track of how many CPUs we are going to - * step or resume, and a pointer to the state structure of one of them, - * respectively - */ - int target_count = 0; - CPUState *last_target = NULL; - - while (*p) { - if (*p++ != ';') { - return -ENOTSUP; - } - - cur_action = *p++; - if (cur_action == 'C' || cur_action == 'S') { - cur_action = qemu_tolower(cur_action); - res = qemu_strtoul(p, &p, 16, &tmp); - if (res) { - return res; - } - signal = gdb_signal_to_target(tmp); - } else if (cur_action != 'c' && cur_action != 's') { - /* unknown/invalid/unsupported command */ - return -ENOTSUP; - } - - if (*p == '\0' || *p == ';') { - /* - * No thread specifier, action is on "all threads". The - * specification is unclear regarding the process to act on. We - * choose all processes. - */ - kind = GDB_ALL_PROCESSES; - } else if (*p++ == ':') { - kind = read_thread_id(p, &p, &pid, &tid); - } else { - return -ENOTSUP; - } - - switch (kind) { - case GDB_READ_THREAD_ERR: - return -EINVAL; - - case GDB_ALL_PROCESSES: - cpu = gdb_first_attached_cpu(); - while (cpu) { - if (newstates[cpu->cpu_index] == 1) { - newstates[cpu->cpu_index] = cur_action; - - target_count++; - last_target = cpu; - } - - cpu = gdb_next_attached_cpu(cpu); - } - break; - - case GDB_ALL_THREADS: - process = gdb_get_process(pid); - - if (!process->attached) { - return -EINVAL; - } - - cpu = gdb_get_first_cpu_in_process(process); - while (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); - - /* invalid CPU/thread specified */ - if (!cpu) { - return -EINVAL; - } - - /* only use if no previous match occourred */ - if (newstates[cpu->cpu_index] == 1) { - newstates[cpu->cpu_index] = cur_action; - - target_count++; - last_target = cpu; - } - break; - } + cur_action = *p++; + if (cur_action == 'C' || cur_action == 'S') { + if (cur_action == 'C') cur_action = 'c'; + if (cur_action == 'S') cur_action = 's'; + res = qemu_strtoul(p, &p, 16, &tmp); + if (res) { + return res; + } + signal = gdb_signal_to_target(tmp); + } else if (cur_action != 'c' && cur_action != 's') { + /* unknown/invalid/unsupported command */ + return -ENOTSUP; } - /* - * if we're about to resume a specific set of CPUs/threads, make it so that - * in case execution gets interrupted, we can send GDB a stop reply with a - * correct value. it doesn't really matter which CPU we tell GDB the signal - * happened in (VM pauses stop all of them anyway), so long as it is one of - * the ones we resumed/single stepped here. - */ - if (target_count > 0) { - gdbserver_state.c_cpu = last_target; - } - - gdbserver_state.signal = signal; - gdb_continue_partial(newstates); - return res; -} - -static const char *cmd_next_param(const char *param, const char delimiter) -{ - static const char all_delimiters[] = ",;:="; - char curr_delimiters[2] = {0}; - const char *delimiters; - - if (delimiter == '?') { - delimiters = all_delimiters; - } else if (delimiter == '0') { - return strchr(param, '\0'); - } else if (delimiter == '.' && *param) { - return param + 1; + if (*p == '\0' || *p == ';') { + /* + * No thread specifier, action is on "all threads". The + * specification is unclear regarding the process to act on. We + * choose all processes. + */ + kind = GDB_ALL_PROCESSES; + } else if (*p++ == ':') { + kind = read_thread_id(p, &p, &pid, &tid); } else { - curr_delimiters[0] = delimiter; - delimiters = curr_delimiters; + return -ENOTSUP; } - param += strcspn(param, delimiters); - if (*param) { - param++; - } - return param; -} + switch (kind) { + case GDB_READ_THREAD_ERR: + return -EINVAL; -static int cmd_parse_params(const char *data, const char *schema, - GArray *params) -{ - const char *curr_schema, *curr_data; + case GDB_ALL_PROCESSES: + cpu = gdb_first_attached_cpu(); + while (cpu) { + if (newstates[cpu->cpu_index] == 1) { + newstates[cpu->cpu_index] = cur_action; - g_assert(schema); - g_assert(params->len == 0); + target_count++; + last_target = cpu; + } - curr_schema = schema; - curr_data = data; - while (curr_schema[0] && curr_schema[1] && *curr_data) { - GdbCmdVariant this_param; - - switch (curr_schema[0]) { - case 'l': - if (qemu_strtoul(curr_data, &curr_data, 16, - &this_param.val_ul)) { - return -EINVAL; - } - curr_data = cmd_next_param(curr_data, curr_schema[1]); - g_array_append_val(params, this_param); - break; - case 'L': - if (qemu_strtou64(curr_data, &curr_data, 16, - (uint64_t *)&this_param.val_ull)) { - return -EINVAL; - } - curr_data = cmd_next_param(curr_data, curr_schema[1]); - g_array_append_val(params, this_param); - break; - case 's': - this_param.data = curr_data; - curr_data = cmd_next_param(curr_data, curr_schema[1]); - g_array_append_val(params, this_param); - break; - case 'o': - this_param.opcode = *(uint8_t *)curr_data; - curr_data = cmd_next_param(curr_data, curr_schema[1]); - g_array_append_val(params, this_param); - break; - case 't': - this_param.thread_id.kind = - read_thread_id(curr_data, &curr_data, - &this_param.thread_id.pid, - &this_param.thread_id.tid); - curr_data = cmd_next_param(curr_data, curr_schema[1]); - g_array_append_val(params, this_param); - break; - case '?': - curr_data = cmd_next_param(curr_data, curr_schema[1]); - break; - default: - return -EINVAL; + cpu = gdb_next_attached_cpu(cpu); } - curr_schema += 2; - } + break; - return 0; + case GDB_ALL_THREADS: + process = gdb_get_process(pid); + + if (!process->attached) { + return -EINVAL; + } + + cpu = gdb_get_first_cpu_in_process(process); + while (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); + + /* invalid CPU/thread specified */ + if (!cpu) { + return -EINVAL; + } + + /* only use if no previous match occourred */ + if (newstates[cpu->cpu_index] == 1) { + newstates[cpu->cpu_index] = cur_action; + + target_count++; + last_target = cpu; + } + break; + } + } + + /* + * if we're about to resume a specific set of CPUs/threads, make it so that + * in case execution gets interrupted, we can send GDB a stop reply with a + * correct value. it doesn't really matter which CPU we tell GDB the signal + * happened in (VM pauses stop all of them anyway), so long as it is one of + * the ones we resumed/single stepped here. + */ + if (target_count > 0) { + gdbserver_state.c_cpu = last_target; + } + + gdbserver_state.signal = signal; + gdb_continue_partial(newstates); + return res; } -static inline int startswith(const char *string, const char *pattern) -{ +static const char *cmd_next_param(const char *param, const char delimiter) { + static const char all_delimiters[] = ",;:="; + char curr_delimiters[2] = {0}; + const char *delimiters; + + if (delimiter == '?') { + delimiters = all_delimiters; + } else if (delimiter == '0') { + return strchr(param, '\0'); + } else if (delimiter == '.' && *param) { + return param + 1; + } else { + curr_delimiters[0] = delimiter; + delimiters = curr_delimiters; + } + + param += strcspn(param, delimiters); + if (*param) { + param++; + } + return param; +} + +static int cmd_parse_params(const char *data, const char *schema, GArray *params) { + const char *curr_schema, *curr_data; + + g_assert(schema); + g_assert(params->len == 0); + + curr_schema = schema; + curr_data = data; + while (curr_schema[0] && curr_schema[1] && *curr_data) { + GdbCmdVariant this_param; + + switch (curr_schema[0]) { + case 'l': + if (qemu_strtoul(curr_data, &curr_data, 16, &this_param.val_ul)) { + return -EINVAL; + } + curr_data = cmd_next_param(curr_data, curr_schema[1]); + g_array_append_val(params, this_param); + break; + case 'L': + if (qemu_strtou64(curr_data, &curr_data, 16, (uint64_t *)&this_param.val_ull)) { + return -EINVAL; + } + curr_data = cmd_next_param(curr_data, curr_schema[1]); + g_array_append_val(params, this_param); + break; + case 's': + this_param.data = curr_data; + curr_data = cmd_next_param(curr_data, curr_schema[1]); + g_array_append_val(params, this_param); + break; + case 'o': + this_param.opcode = *(uint8_t *)curr_data; + curr_data = cmd_next_param(curr_data, curr_schema[1]); + g_array_append_val(params, this_param); + break; + case 't': + this_param.thread_id.kind = + read_thread_id(curr_data, &curr_data, &this_param.thread_id.pid, &this_param.thread_id.tid); + curr_data = cmd_next_param(curr_data, curr_schema[1]); + g_array_append_val(params, this_param); + break; + case '?': + curr_data = cmd_next_param(curr_data, curr_schema[1]); + break; + default: + return -EINVAL; + } + curr_schema += 2; + } + + return 0; +} + +static inline int startswith(const char *string, const char *pattern) { return !strncmp(string, pattern, strlen(pattern)); } -static bool process_string_cmd(const char *data, - const GdbCmdParseEntry *cmds, int num_cmds) -{ - int i; - g_autoptr(GArray) params = g_array_new(false, true, sizeof(GdbCmdVariant)); - - if (!cmds) { - return false; - } - - for (i = 0; i < num_cmds; i++) { - const GdbCmdParseEntry *cmd = &cmds[i]; - void *user_ctx = NULL; - g_assert(cmd->handler && cmd->cmd); - - if ((cmd->cmd_startswith && !startswith(data, cmd->cmd)) || - (!cmd->cmd_startswith && strcmp(cmd->cmd, data))) { - continue; - } - - if (cmd->schema) { - if (cmd_parse_params(&data[strlen(cmd->cmd)], - cmd->schema, params)) { - return false; - } - } - - if (cmd->need_cpu_context) { - user_ctx = (void *)gdbserver_state.g_cpu; - } - - gdbserver_state.allow_stop_reply = cmd->allow_stop_reply; - cmd->handler(params, user_ctx); - return true; - } +static bool process_string_cmd(const char *data, const GdbCmdParseEntry *cmds, int num_cmds) { + int i; + g_autoptr(GArray) params = g_array_new(false, true, sizeof(GdbCmdVariant)); + if (!cmds) { return false; + } + + for (i = 0; i < num_cmds; i++) { + const GdbCmdParseEntry *cmd = &cmds[i]; + void *user_ctx = NULL; + g_assert(cmd->handler && cmd->cmd); + + if ((cmd->cmd_startswith && !startswith(data, cmd->cmd)) || (!cmd->cmd_startswith && strcmp(cmd->cmd, data))) { + continue; + } + + if (cmd->schema) { + if (cmd_parse_params(&data[strlen(cmd->cmd)], cmd->schema, params)) { + return false; + } + } + + if (cmd->need_cpu_context) { + user_ctx = (void *)gdbserver_state.g_cpu; + } + + gdbserver_state.allow_stop_reply = cmd->allow_stop_reply; + cmd->handler(params, user_ctx); + return true; + } + + return false; } -static void run_cmd_parser(const char *data, const GdbCmdParseEntry *cmd) -{ - if (!data) { - return; - } +static void run_cmd_parser(const char *data, const GdbCmdParseEntry *cmd) { + if (!data) { + return; + } - g_string_set_size(gdbserver_state.str_buf, 0); - g_byte_array_set_size(gdbserver_state.mem_buf, 0); + g_string_set_size(gdbserver_state.str_buf, 0); + g_byte_array_set_size(gdbserver_state.mem_buf, 0); - /* In case there was an error during the command parsing we must - * send a NULL packet to indicate the command is not supported */ - if (!process_string_cmd(data, cmd, 1)) { - gdb_put_packet(""); - } + /* In case there was an error during the command parsing we must + * send a NULL packet to indicate the command is not supported */ + if (!process_string_cmd(data, cmd, 1)) { + gdb_put_packet(""); + } } -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; - } - -#ifdef CONFIG_USER_ONLY - if (gdb_handle_detach_user(pid)) { - return; - } -#endif - - process = gdb_get_process(pid); - gdb_process_breakpoint_remove_all(process); - 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(); - } - gdb_put_packet("OK"); -} - -static void handle_thread_alive(GArray *params, void *user_ctx) -{ - CPUState *cpu; +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; + gdb_put_packet("E22"); + return; } - if (gdb_get_cmd_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) { - gdb_put_packet("E22"); - return; - } + pid = gdb_get_cmd_param(params, 0)->val_ul; + } - cpu = gdb_get_cpu(gdb_get_cmd_param(params, 0)->thread_id.pid, - gdb_get_cmd_param(params, 0)->thread_id.tid); - if (!cpu) { - gdb_put_packet("E22"); - return; - } + process = gdb_get_process(pid); + gdb_process_breakpoint_remove_all(process); + process->attached = false; - gdb_put_packet("OK"); + 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(); + } + gdb_put_packet("OK"); } -static void handle_continue(GArray *params, void *user_ctx) -{ - if (params->len) { - gdb_set_cpu_pc(gdb_get_cmd_param(params, 0)->val_ull); - } +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); + if (!cpu) { + gdb_put_packet("E22"); + return; + } + + gdb_put_packet("OK"); +} + +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 + */ + 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; - gdb_continue(); + } + gdb_continue(); } -static void handle_cont_with_sig(GArray *params, void *user_ctx) -{ - unsigned long signal = 0; +static void handle_set_thread(GArray *params, void *user_ctx) { + uint32_t pid, tid; + CPUState *cpu; - /* - * Note: C sig;[addr] is currently unsupported and we simply - * omit the addr parameter - */ - if (params->len) { - signal = gdb_get_cmd_param(params, 0)->val_ul; - } + if (params->len != 2) { + gdb_put_packet("E22"); + return; + } - gdbserver_state.signal = gdb_signal_to_target(signal); - if (gdbserver_state.signal == -1) { - gdbserver_state.signal = 0; - } - gdb_continue(); -} + if (gdb_get_cmd_param(params, 1)->thread_id.kind == GDB_READ_THREAD_ERR) { + gdb_put_packet("E22"); + return; + } -static void handle_set_thread(GArray *params, void *user_ctx) -{ - uint32_t pid, tid; - CPUState *cpu; + if (gdb_get_cmd_param(params, 1)->thread_id.kind != GDB_ONE_THREAD) { + gdb_put_packet("OK"); + return; + } - if (params->len != 2) { - gdb_put_packet("E22"); - return; - } + pid = gdb_get_cmd_param(params, 1)->thread_id.pid; + tid = gdb_get_cmd_param(params, 1)->thread_id.tid; - if (gdb_get_cmd_param(params, 1)->thread_id.kind == GDB_READ_THREAD_ERR) { - gdb_put_packet("E22"); - return; - } + cpu = gdb_get_cpu(pid, tid); + if (!cpu) { + 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; -#ifdef CONFIG_USER_ONLY - if (gdb_handle_set_thread_user(pid, tid)) { - return; - } -#endif - cpu = gdb_get_cpu(pid, tid); - if (!cpu) { - gdb_put_packet("E22"); - return; - } - - /* - * Note: This command is deprecated and modern gdb's will be using the - * vCont command instead. - */ - switch (gdb_get_cmd_param(params, 0)->opcode) { + /* + * Note: This command is deprecated and modern gdb's will be using the + * vCont command instead. + */ + switch (gdb_get_cmd_param(params, 0)->opcode) { case 'c': - gdbserver_state.c_cpu = cpu; - gdb_put_packet("OK"); - break; + gdbserver_state.c_cpu = cpu; + gdb_put_packet("OK"); + break; case 'g': - gdbserver_state.g_cpu = cpu; - gdb_put_packet("OK"); - break; + gdbserver_state.g_cpu = cpu; + gdb_put_packet("OK"); + break; default: - gdb_put_packet("E22"); - break; - } + gdb_put_packet("E22"); + break; + } } -static void handle_insert_bp(GArray *params, void *user_ctx) -{ - int res; - - if (params->len != 3) { - gdb_put_packet("E22"); - return; - } - - res = gdb_breakpoint_insert(gdbserver_state.c_cpu, - gdb_get_cmd_param(params, 0)->val_ul, - gdb_get_cmd_param(params, 1)->val_ull, - gdb_get_cmd_param(params, 2)->val_ull); - if (res >= 0) { - gdb_put_packet("OK"); - return; - } else if (res == -ENOSYS) { - gdb_put_packet(""); - return; - } +static void handle_insert_bp(GArray *params, void *user_ctx) { + int res; + if (params->len != 3) { gdb_put_packet("E22"); + return; + } + + res = gdb_breakpoint_insert(gdbserver_state.c_cpu, gdb_get_cmd_param(params, 0)->val_ul, + gdb_get_cmd_param(params, 1)->val_ull, gdb_get_cmd_param(params, 2)->val_ull); + if (res >= 0) { + gdb_put_packet("OK"); + return; + } else if (res == -ENOSYS) { + gdb_put_packet(""); + return; + } + + gdb_put_packet("E22"); } -static void handle_remove_bp(GArray *params, void *user_ctx) -{ - int res; - - if (params->len != 3) { - gdb_put_packet("E22"); - return; - } - - res = gdb_breakpoint_remove(gdbserver_state.c_cpu, - gdb_get_cmd_param(params, 0)->val_ul, - gdb_get_cmd_param(params, 1)->val_ull, - gdb_get_cmd_param(params, 2)->val_ull); - if (res >= 0) { - gdb_put_packet("OK"); - return; - } else if (res == -ENOSYS) { - gdb_put_packet(""); - return; - } +static void handle_remove_bp(GArray *params, void *user_ctx) { + int res; + if (params->len != 3) { gdb_put_packet("E22"); + return; + } + + res = gdb_breakpoint_remove(gdbserver_state.c_cpu, gdb_get_cmd_param(params, 0)->val_ul, + gdb_get_cmd_param(params, 1)->val_ull, gdb_get_cmd_param(params, 2)->val_ull); + if (res >= 0) { + gdb_put_packet("OK"); + return; + } else if (res == -ENOSYS) { + gdb_put_packet(""); + return; + } + + gdb_put_packet("E22"); } /* @@ -1211,567 +1096,426 @@ static void handle_remove_bp(GArray *params, void *user_ctx) * the remote gdb to fallback to older methods. */ -static void handle_set_reg(GArray *params, void *user_ctx) -{ - int reg_size; +static void handle_set_reg(GArray *params, void *user_ctx) { + int reg_size; - if (params->len != 2) { - gdb_put_packet("E22"); - return; - } + if (params->len != 2) { + gdb_put_packet("E22"); + return; + } - reg_size = strlen(gdb_get_cmd_param(params, 1)->data) / 2; - gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 1)->data, reg_size); - gdb_write_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf->data, - gdb_get_cmd_param(params, 0)->val_ull); - gdb_put_packet("OK"); + reg_size = strlen(gdb_get_cmd_param(params, 1)->data) / 2; + gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 1)->data, reg_size); + gdb_write_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf->data, gdb_get_cmd_param(params, 0)->val_ull); + gdb_put_packet("OK"); } -static void handle_get_reg(GArray *params, void *user_ctx) -{ - int reg_size; +static void handle_get_reg(GArray *params, void *user_ctx) { + int reg_size; - if (!params->len) { + if (!params->len) { + gdb_put_packet("E14"); + return; + } + + reg_size = gdb_read_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf, gdb_get_cmd_param(params, 0)->val_ull); + if (!reg_size) { + gdb_put_packet("E14"); + return; + } else { + g_byte_array_set_size(gdbserver_state.mem_buf, reg_size); + } + + gdb_memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, reg_size); + gdb_put_strbuf(); +} + +static void handle_write_mem(GArray *params, void *user_ctx) { + if (params->len != 3) { + gdb_put_packet("E22"); + return; + } + + /* gdb_hextomem() reads 2*len bytes */ + if (gdb_get_cmd_param(params, 1)->val_ull > strlen(gdb_get_cmd_param(params, 2)->data) / 2) { + gdb_put_packet("E22"); + return; + } + + gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 2)->data, gdb_get_cmd_param(params, 1)->val_ull); + if (gdb_target_memory_rw_debug(gdbserver_state.g_cpu, gdb_get_cmd_param(params, 0)->val_ull, + gdbserver_state.mem_buf->data, gdbserver_state.mem_buf->len, true)) { + gdb_put_packet("E14"); + return; + } + + gdb_put_packet("OK"); +} + +static void handle_read_mem(GArray *params, void *user_ctx) { + if (params->len != 2) { + gdb_put_packet("E22"); + return; + } + + /* gdb_memtohex() doubles the required space */ + if (gdb_get_cmd_param(params, 1)->val_ull > MAX_PACKET_LENGTH / 2) { + gdb_put_packet("E22"); + return; + } + + g_byte_array_set_size(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 1)->val_ull); + + if (gdb_target_memory_rw_debug(gdbserver_state.g_cpu, gdb_get_cmd_param(params, 0)->val_ull, + gdbserver_state.mem_buf->data, gdbserver_state.mem_buf->len, false)) { + gdb_put_packet("E14"); + return; + } + + gdb_memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, gdbserver_state.mem_buf->len); + gdb_put_strbuf(); +} + +static void handle_write_all_regs(GArray *params, void *user_ctx) { + int reg_id; + size_t len; + uint8_t *registers; + int reg_size; + + if (!params->len) { + return; + } + + cpu_synchronize_state(gdbserver_state.g_cpu); + len = strlen(gdb_get_cmd_param(params, 0)->data) / 2; + gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 0)->data, len); + registers = gdbserver_state.mem_buf->data; + for (reg_id = 0; reg_id < gdbserver_state.g_cpu->gdb_num_g_regs && len > 0; reg_id++) { + reg_size = gdb_write_register(gdbserver_state.g_cpu, registers, reg_id); + len -= reg_size; + registers += reg_size; + } + gdb_put_packet("OK"); +} + +static void handle_read_all_regs(GArray *params, void *user_ctx) { + int reg_id; + size_t len; + + cpu_synchronize_state(gdbserver_state.g_cpu); + g_byte_array_set_size(gdbserver_state.mem_buf, 0); + len = 0; + for (reg_id = 0; reg_id < gdbserver_state.g_cpu->gdb_num_g_regs; reg_id++) { + len += gdb_read_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf, reg_id); + g_assert(len == gdbserver_state.mem_buf->len); + } + + gdb_memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, len); + gdb_put_strbuf(); +} + +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': + // if (replay_reverse_step()) { + // gdb_continue(); + // } else { gdb_put_packet("E14"); + // } return; - } - - reg_size = gdb_read_register(gdbserver_state.g_cpu, - gdbserver_state.mem_buf, - gdb_get_cmd_param(params, 0)->val_ull); - if (!reg_size) { + case 'c': + // if (replay_reverse_continue()) { + // gdb_continue(); + // } else { gdb_put_packet("E14"); + // } return; - } else { - g_byte_array_set_size(gdbserver_state.mem_buf, reg_size); } + } - gdb_memtohex(gdbserver_state.str_buf, - gdbserver_state.mem_buf->data, reg_size); - gdb_put_strbuf(); + /* Default invalid command */ + gdb_put_packet(""); } -static void handle_write_mem(GArray *params, void *user_ctx) -{ - if (params->len != 3) { - gdb_put_packet("E22"); - return; - } +static void handle_v_cont_query(GArray *params, void *user_ctx) { gdb_put_packet("vCont;c;C;s;S"); } - /* gdb_hextomem() reads 2*len bytes */ - if (gdb_get_cmd_param(params, 1)->val_ull > - strlen(gdb_get_cmd_param(params, 2)->data) / 2) { - gdb_put_packet("E22"); - return; - } +static void handle_v_cont(GArray *params, void *user_ctx) { + int res; - gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 2)->data, - gdb_get_cmd_param(params, 1)->val_ull); - if (gdb_target_memory_rw_debug(gdbserver_state.g_cpu, - gdb_get_cmd_param(params, 0)->val_ull, - gdbserver_state.mem_buf->data, - gdbserver_state.mem_buf->len, true)) { - gdb_put_packet("E14"); - return; - } + if (!params->len) { + return; + } - gdb_put_packet("OK"); -} - -static void handle_read_mem(GArray *params, void *user_ctx) -{ - if (params->len != 2) { - gdb_put_packet("E22"); - return; - } - - /* gdb_memtohex() doubles the required space */ - if (gdb_get_cmd_param(params, 1)->val_ull > MAX_PACKET_LENGTH / 2) { - gdb_put_packet("E22"); - return; - } - - g_byte_array_set_size(gdbserver_state.mem_buf, - gdb_get_cmd_param(params, 1)->val_ull); - - if (gdb_target_memory_rw_debug(gdbserver_state.g_cpu, - gdb_get_cmd_param(params, 0)->val_ull, - gdbserver_state.mem_buf->data, - gdbserver_state.mem_buf->len, false)) { - gdb_put_packet("E14"); - return; - } - - gdb_memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, - gdbserver_state.mem_buf->len); - gdb_put_strbuf(); -} - -static void handle_write_all_regs(GArray *params, void *user_ctx) -{ - int reg_id; - size_t len; - uint8_t *registers; - int reg_size; - - if (!params->len) { - return; - } - - cpu_synchronize_state(gdbserver_state.g_cpu); - len = strlen(gdb_get_cmd_param(params, 0)->data) / 2; - gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 0)->data, len); - registers = gdbserver_state.mem_buf->data; - for (reg_id = 0; - reg_id < gdbserver_state.g_cpu->gdb_num_g_regs && len > 0; - reg_id++) { - reg_size = gdb_write_register(gdbserver_state.g_cpu, registers, reg_id); - len -= reg_size; - registers += reg_size; - } - gdb_put_packet("OK"); -} - -static void handle_read_all_regs(GArray *params, void *user_ctx) -{ - int reg_id; - size_t len; - - cpu_synchronize_state(gdbserver_state.g_cpu); - g_byte_array_set_size(gdbserver_state.mem_buf, 0); - len = 0; - for (reg_id = 0; reg_id < gdbserver_state.g_cpu->gdb_num_g_regs; reg_id++) { - len += gdb_read_register(gdbserver_state.g_cpu, - gdbserver_state.mem_buf, - reg_id); - g_assert(len == gdbserver_state.mem_buf->len); - } - - gdb_memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, len); - gdb_put_strbuf(); -} - - -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': - if (replay_reverse_step()) { - gdb_continue(); - } else { - gdb_put_packet("E14"); - } - return; - case 'c': - if (replay_reverse_continue()) { - gdb_continue(); - } else { - gdb_put_packet("E14"); - } - return; - } - } - - /* Default invalid command */ + res = gdb_handle_vcont(gdb_get_cmd_param(params, 0)->data); + if ((res == -EINVAL) || (res == -ERANGE)) { + gdb_put_packet("E22"); + } else if (res) { gdb_put_packet(""); + } } -static void handle_v_cont_query(GArray *params, void *user_ctx) -{ - gdb_put_packet("vCont;c;C;s;S"); +static void handle_v_attach(GArray *params, void *user_ctx) { + GDBProcess *process; + CPUState *cpu; + + g_string_assign(gdbserver_state.str_buf, "E22"); + if (!params->len) { + goto cleanup; + } + + process = gdb_get_process(gdb_get_cmd_param(params, 0)->val_ul); + if (!process) { + goto cleanup; + } + + cpu = gdb_get_first_cpu_in_process(process); + if (!cpu) { + goto cleanup; + } + + process->attached = true; + gdbserver_state.g_cpu = cpu; + gdbserver_state.c_cpu = cpu; + + if (gdbserver_state.allow_stop_reply) { + g_string_printf(gdbserver_state.str_buf, "T%02xthread:", GDB_SIGNAL_TRAP); + gdb_append_thread_id(cpu, gdbserver_state.str_buf); + g_string_append_c(gdbserver_state.str_buf, ';'); + gdbserver_state.allow_stop_reply = false; + cleanup: + gdb_put_strbuf(); + } } -static void handle_v_cont(GArray *params, void *user_ctx) -{ - int res; - - if (!params->len) { - return; - } - - res = gdb_handle_vcont(gdb_get_cmd_param(params, 0)->data); - if ((res == -EINVAL) || (res == -ERANGE)) { - gdb_put_packet("E22"); - } else if (res) { - gdb_put_packet(""); - } -} - -static void handle_v_attach(GArray *params, void *user_ctx) -{ - GDBProcess *process; - CPUState *cpu; - - g_string_assign(gdbserver_state.str_buf, "E22"); - if (!params->len) { - goto cleanup; - } - - process = gdb_get_process(gdb_get_cmd_param(params, 0)->val_ul); - if (!process) { - goto cleanup; - } - - cpu = gdb_get_first_cpu_in_process(process); - if (!cpu) { - goto cleanup; - } - - process->attached = true; - gdbserver_state.g_cpu = cpu; - gdbserver_state.c_cpu = cpu; - - if (gdbserver_state.allow_stop_reply) { - g_string_printf(gdbserver_state.str_buf, "T%02xthread:", GDB_SIGNAL_TRAP); - gdb_append_thread_id(cpu, gdbserver_state.str_buf); - g_string_append_c(gdbserver_state.str_buf, ';'); - gdbserver_state.allow_stop_reply = false; -cleanup: - gdb_put_strbuf(); - } -} - -static void handle_v_kill(GArray *params, void *user_ctx) -{ - /* Kill the target */ - gdb_put_packet("OK"); - error_report("QEMU: Terminated via GDBstub"); - gdb_exit(0); - gdb_qemu_exit(0); +static void handle_v_kill(GArray *params, void *user_ctx) { + /* Kill the target */ + gdb_put_packet("OK"); + std::cout << "QEMU: Terminated via GDBstub" << std::endl; + // gdb_exit(0); + // gdb_qemu_exit(0); } static const GdbCmdParseEntry gdb_v_commands_table[] = { /* Order is important if has same prefix */ - { - .handler = handle_v_cont_query, - .cmd = "Cont?", - .cmd_startswith = true - }, - { - .handler = handle_v_cont, - .cmd = "Cont", - .cmd_startswith = true, - .allow_stop_reply = true, - .schema = "s0" - }, - { - .handler = handle_v_attach, - .cmd = "Attach;", - .cmd_startswith = true, - .allow_stop_reply = true, - .schema = "l0" - }, - { - .handler = handle_v_kill, - .cmd = "Kill;", - .cmd_startswith = true - }, -#ifdef CONFIG_USER_ONLY - /* - * Host I/O Packets. See [1] for details. - * [1] https://sourceware.org/gdb/onlinedocs/gdb/Host-I_002fO-Packets.html - */ - { - .handler = gdb_handle_v_file_open, - .cmd = "File:open:", - .cmd_startswith = true, - .schema = "s,L,L0" - }, - { - .handler = gdb_handle_v_file_close, - .cmd = "File:close:", - .cmd_startswith = true, - .schema = "l0" - }, - { - .handler = gdb_handle_v_file_pread, - .cmd = "File:pread:", - .cmd_startswith = true, - .schema = "l,L,L0" - }, - { - .handler = gdb_handle_v_file_readlink, - .cmd = "File:readlink:", - .cmd_startswith = true, - .schema = "s0" - }, -#endif + {.handler = handle_v_cont_query, .cmd = "Cont?", .cmd_startswith = true}, + {.handler = handle_v_cont, .cmd = "Cont", .cmd_startswith = true, .schema = "s0", .allow_stop_reply = true}, + {.handler = handle_v_attach, .cmd = "Attach;", .cmd_startswith = true, .schema = "l0", .allow_stop_reply = true}, + {.handler = handle_v_kill, .cmd = "Kill;", .cmd_startswith = true}, }; -static void handle_v_commands(GArray *params, void *user_ctx) -{ - if (!params->len) { - return; - } +static void handle_v_commands(GArray *params, void *user_ctx) { + if (!params->len) { + return; + } - if (!process_string_cmd(gdb_get_cmd_param(params, 0)->data, - gdb_v_commands_table, - ARRAY_SIZE(gdb_v_commands_table))) { - gdb_put_packet(""); - } + if (!process_string_cmd(gdb_get_cmd_param(params, 0)->data, gdb_v_commands_table, ARRAY_SIZE(gdb_v_commands_table))) { + gdb_put_packet(""); + } } -static void handle_query_qemu_sstepbits(GArray *params, void *user_ctx) -{ - g_string_printf(gdbserver_state.str_buf, "ENABLE=%x", SSTEP_ENABLE); +static void handle_query_qemu_sstepbits(GArray *params, void *user_ctx) { + g_string_printf(gdbserver_state.str_buf, "ENABLE=%x", SSTEP_ENABLE); - if (gdbserver_state.supported_sstep_flags & SSTEP_NOIRQ) { - g_string_append_printf(gdbserver_state.str_buf, ",NOIRQ=%x", - SSTEP_NOIRQ); - } + if (gdbserver_state.supported_sstep_flags & SSTEP_NOIRQ) { + g_string_append_printf(gdbserver_state.str_buf, ",NOIRQ=%x", SSTEP_NOIRQ); + } - if (gdbserver_state.supported_sstep_flags & SSTEP_NOTIMER) { - g_string_append_printf(gdbserver_state.str_buf, ",NOTIMER=%x", - SSTEP_NOTIMER); - } + if (gdbserver_state.supported_sstep_flags & SSTEP_NOTIMER) { + g_string_append_printf(gdbserver_state.str_buf, ",NOTIMER=%x", SSTEP_NOTIMER); + } - gdb_put_strbuf(); + gdb_put_strbuf(); } -static void handle_set_qemu_sstep(GArray *params, void *user_ctx) -{ - int new_sstep_flags; +static void handle_set_qemu_sstep(GArray *params, void *user_ctx) { + int new_sstep_flags; - if (!params->len) { - return; - } + if (!params->len) { + return; + } - new_sstep_flags = gdb_get_cmd_param(params, 0)->val_ul; + new_sstep_flags = gdb_get_cmd_param(params, 0)->val_ul; - if (new_sstep_flags & ~gdbserver_state.supported_sstep_flags) { - gdb_put_packet("E22"); - return; - } + if (new_sstep_flags & ~gdbserver_state.supported_sstep_flags) { + gdb_put_packet("E22"); + return; + } - gdbserver_state.sstep_flags = new_sstep_flags; - gdb_put_packet("OK"); + gdbserver_state.sstep_flags = new_sstep_flags; + gdb_put_packet("OK"); } -static void handle_query_qemu_sstep(GArray *params, void *user_ctx) -{ - g_string_printf(gdbserver_state.str_buf, "0x%x", - gdbserver_state.sstep_flags); - gdb_put_strbuf(); +static void handle_query_qemu_sstep(GArray *params, void *user_ctx) { + g_string_printf(gdbserver_state.str_buf, "0x%x", gdbserver_state.sstep_flags); + gdb_put_strbuf(); } -static void handle_query_curr_tid(GArray *params, void *user_ctx) -{ - CPUState *cpu; - GDBProcess *process; +static void handle_query_curr_tid(GArray *params, void *user_ctx) { + CPUState *cpu; + GDBProcess *process; - /* - * "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(gdbserver_state.g_cpu); - cpu = gdb_get_first_cpu_in_process(process); - g_string_assign(gdbserver_state.str_buf, "QC"); - gdb_append_thread_id(cpu, gdbserver_state.str_buf); - gdb_put_strbuf(); + /* + * "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(gdbserver_state.g_cpu); + cpu = gdb_get_first_cpu_in_process(process); + g_string_assign(gdbserver_state.str_buf, "QC"); + gdb_append_thread_id(cpu, gdbserver_state.str_buf); + gdb_put_strbuf(); } -static void handle_query_threads(GArray *params, void *user_ctx) -{ - if (!gdbserver_state.query_cpu) { - gdb_put_packet("l"); - return; - } +static void handle_query_threads(GArray *params, void *user_ctx) { + if (!gdbserver_state.query_cpu) { + gdb_put_packet("l"); + return; + } - g_string_assign(gdbserver_state.str_buf, "m"); - gdb_append_thread_id(gdbserver_state.query_cpu, gdbserver_state.str_buf); - gdb_put_strbuf(); - gdbserver_state.query_cpu = gdb_next_attached_cpu(gdbserver_state.query_cpu); + g_string_assign(gdbserver_state.str_buf, "m"); + gdb_append_thread_id(gdbserver_state.query_cpu, gdbserver_state.str_buf); + gdb_put_strbuf(); + gdbserver_state.query_cpu = gdb_next_attached_cpu(gdbserver_state.query_cpu); } -static void handle_query_gdb_server_version(GArray *params, void *user_ctx) -{ -#if defined(CONFIG_USER_ONLY) - g_string_printf(gdbserver_state.str_buf, "name:qemu-%s;version:%s;", - target_name(), QEMU_VERSION); -#else - g_string_printf(gdbserver_state.str_buf, "name:qemu-system-%s;version:%s;", - target_name(), QEMU_VERSION); -#endif - gdb_put_strbuf(); +static void handle_query_gdb_server_version(GArray *params, void *user_ctx) { + g_string_printf(gdbserver_state.str_buf, "name:qemu-system-riscv;version:1.9;"); + gdb_put_strbuf(); } -static void handle_query_first_threads(GArray *params, void *user_ctx) -{ - gdbserver_state.query_cpu = gdb_first_attached_cpu(); - handle_query_threads(params, user_ctx); +static void handle_query_first_threads(GArray *params, void *user_ctx) { + gdbserver_state.query_cpu = gdb_first_attached_cpu(); + handle_query_threads(params, user_ctx); } -static void handle_query_thread_extra(GArray *params, void *user_ctx) -{ - g_autoptr(GString) rs = g_string_new(NULL); - CPUState *cpu; +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; - } + 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); - if (!cpu) { - return; - } + cpu = gdb_get_cpu(gdb_get_cmd_param(params, 0)->thread_id.pid, gdb_get_cmd_param(params, 0)->thread_id.tid); + if (!cpu) { + return; + } - cpu_synchronize_state(cpu); + cpu_synchronize_state(cpu); - if (gdbserver_state.multiprocess && (gdbserver_state.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); - const char *cpu_name = - object_get_canonical_path_component(OBJECT(cpu)); - g_string_printf(rs, "%s %s [%s]", cpu_model, cpu_name, - cpu->halted ? "halted " : "running"); - } else { - 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(); + 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(); } - static char **extra_query_flags; -void gdb_extend_qsupported_features(char *qflags) -{ - if (!extra_query_flags) { - extra_query_flags = g_new0(char *, 2); - extra_query_flags[0] = g_strdup(qflags); - } else if (!g_strv_contains((const gchar * const *) extra_query_flags, - qflags)) { - int len = g_strv_length(extra_query_flags); - extra_query_flags = g_realloc_n(extra_query_flags, len + 2, - sizeof(char *)); - extra_query_flags[len] = g_strdup(qflags); - } +void gdb_extend_qsupported_features(char *qflags) { + if (!extra_query_flags) { + extra_query_flags = g_new0(char *, 2); + extra_query_flags[0] = g_strdup(qflags); + } else if (!g_strv_contains((const gchar *const *)extra_query_flags, qflags)) { + int len = g_strv_length(extra_query_flags); + extra_query_flags = (char **)g_realloc_n(extra_query_flags, len + 2, sizeof(char *)); + extra_query_flags[len] = g_strdup(qflags); + } } -static void handle_query_supported(GArray *params, void *user_ctx) -{ - g_string_printf(gdbserver_state.str_buf, "PacketSize=%x", MAX_PACKET_LENGTH); - if (gdb_get_core_xml_file(first_cpu)) { - g_string_append(gdbserver_state.str_buf, ";qXfer:features:read+"); +static void handle_query_supported(GArray *params, void *user_ctx) { + g_string_printf(gdbserver_state.str_buf, "PacketSize=%x", MAX_PACKET_LENGTH); + 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; } + } - if (gdb_can_reverse()) { - g_string_append(gdbserver_state.str_buf, - ";ReverseStep+;ReverseContinue+"); + 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++) { + g_string_append(gdbserver_state.str_buf, extra_query_flags[i]); } + } -#if defined(CONFIG_USER_ONLY) -#if defined(CONFIG_LINUX) - if (get_task_state(gdbserver_state.c_cpu)) { - g_string_append(gdbserver_state.str_buf, ";qXfer:auxv:read+"); - } - g_string_append(gdbserver_state.str_buf, ";QCatchSyscalls+"); - - g_string_append(gdbserver_state.str_buf, ";qXfer:siginfo:read+"); -#endif - g_string_append(gdbserver_state.str_buf, ";qXfer:exec-file:read+"); -#endif - - if (params->len) { - const char *gdb_supported = gdb_get_cmd_param(params, 0)->data; - - if (strstr(gdb_supported, "multiprocess+")) { - gdbserver_state.multiprocess = true; - } -#if defined(CONFIG_USER_ONLY) - gdb_handle_query_supported_user(gdb_supported); -#endif - } - - 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++) { - g_string_append(gdbserver_state.str_buf, extra_query_flags[i]); - } - } - - gdb_put_strbuf(); + gdb_put_strbuf(); } -static void handle_query_xfer_features(GArray *params, void *user_ctx) -{ - GDBProcess *process; - unsigned long len, total_len, addr; - const char *xml; - const char *p; +static void handle_query_xfer_features(GArray *params, void *user_ctx) { + GDBProcess *process; + unsigned long len, total_len, addr; + const char *xml; + const char *p; - if (params->len < 3) { - gdb_put_packet("E22"); - return; - } + if (params->len < 3) { + gdb_put_packet("E22"); + return; + } - process = gdb_get_cpu_process(gdbserver_state.g_cpu); - if (!gdb_get_core_xml_file(gdbserver_state.g_cpu)) { - gdb_put_packet(""); - return; - } + process = gdb_get_cpu_process(gdbserver_state.g_cpu); + if (!gdb_get_core_xml_file(gdbserver_state.g_cpu)) { + gdb_put_packet(""); + return; + } - p = gdb_get_cmd_param(params, 0)->data; - xml = get_feature_xml(p, &p, process); - if (!xml) { - gdb_put_packet("E00"); - return; - } + p = gdb_get_cmd_param(params, 0)->data; + xml = get_feature_xml(p, &p, process); + if (!xml) { + gdb_put_packet("E00"); + return; + } - addr = gdb_get_cmd_param(params, 1)->val_ul; - len = gdb_get_cmd_param(params, 2)->val_ul; - total_len = strlen(xml); - if (addr > total_len) { - gdb_put_packet("E00"); - return; - } + addr = gdb_get_cmd_param(params, 1)->val_ul; + len = gdb_get_cmd_param(params, 2)->val_ul; + total_len = strlen(xml); + if (addr > total_len) { + gdb_put_packet("E00"); + return; + } - if (len > (MAX_PACKET_LENGTH - 5) / 2) { - len = (MAX_PACKET_LENGTH - 5) / 2; - } + if (len > (MAX_PACKET_LENGTH - 5) / 2) { + len = (MAX_PACKET_LENGTH - 5) / 2; + } - if (len < total_len - addr) { - g_string_assign(gdbserver_state.str_buf, "m"); - gdb_memtox(gdbserver_state.str_buf, xml + addr, len); - } else { - g_string_assign(gdbserver_state.str_buf, "l"); - gdb_memtox(gdbserver_state.str_buf, xml + addr, total_len - addr); - } + if (len < total_len - addr) { + g_string_assign(gdbserver_state.str_buf, "m"); + gdb_memtox(gdbserver_state.str_buf, xml + addr, len); + } else { + g_string_assign(gdbserver_state.str_buf, "l"); + gdb_memtox(gdbserver_state.str_buf, xml + addr, total_len - addr); + } - gdb_put_packet_binary(gdbserver_state.str_buf->str, - gdbserver_state.str_buf->len, true); + gdb_put_packet_binary(gdbserver_state.str_buf->str, gdbserver_state.str_buf->len, true); } -static void handle_query_qemu_supported(GArray *params, void *user_ctx) -{ - g_string_printf(gdbserver_state.str_buf, "sstepbits;sstep"); -#ifndef CONFIG_USER_ONLY - g_string_append(gdbserver_state.str_buf, ";PhyMemMode"); -#endif - gdb_put_strbuf(); +static void handle_query_qemu_supported(GArray *params, void *user_ctx) { + g_string_printf(gdbserver_state.str_buf, "sstepbits;sstep"); + g_string_append(gdbserver_state.str_buf, ";PhyMemMode"); + gdb_put_strbuf(); } static const GdbCmdParseEntry gdb_gen_query_set_common_table[] = { @@ -1784,12 +1528,7 @@ static const GdbCmdParseEntry gdb_gen_query_set_common_table[] = { .handler = handle_query_qemu_sstep, .cmd = "qemu.sstep", }, - { - .handler = handle_set_qemu_sstep, - .cmd = "qemu.sstep=", - .cmd_startswith = true, - .schema = "l0" - }, + {.handler = handle_set_qemu_sstep, .cmd = "qemu.sstep=", .cmd_startswith = true, .schema = "l0"}, }; /** @@ -1803,20 +1542,19 @@ static const GdbCmdParseEntry gdb_gen_query_set_common_table[] = { * * Returns (a potentially freshly allocated) GPtrArray of GdbCmdParseEntry */ -static GPtrArray *extend_table(GPtrArray *table, GPtrArray *extensions) -{ - if (!table) { - table = g_ptr_array_new(); - } +static GPtrArray *extend_table(GPtrArray *table, GPtrArray *extensions) { + if (!table) { + table = g_ptr_array_new(); + } - for (int i = 0; i < extensions->len; i++) { - gpointer entry = g_ptr_array_index(extensions, i); - if (!g_ptr_array_find(table, entry, NULL)) { - g_ptr_array_add(table, entry); - } + for (int i = 0; i < extensions->len; i++) { + gpointer entry = g_ptr_array_index(extensions, i); + if (!g_ptr_array_find(table, entry, NULL)) { + g_ptr_array_add(table, entry); } + } - return table; + return table; } /** @@ -1826,24 +1564,21 @@ static GPtrArray *extend_table(GPtrArray *table, GPtrArray *extensions) * * 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 = g_ptr_array_index(table, i); - if (process_string_cmd(data, entry, 1)) { - return true; - } +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); + if (process_string_cmd(data, entry, 1)) { + return true; } - return false; + } + return false; } - /* Ptr to GdbCmdParseEntry */ static GPtrArray *extended_query_table; -void gdb_extend_query_table(GPtrArray *new_queries) -{ - extended_query_table = extend_table(extended_query_table, new_queries); +void gdb_extend_query_table(GPtrArray *new_queries) { + extended_query_table = extend_table(extended_query_table, new_queries); } static const GdbCmdParseEntry gdb_gen_query_table[] = { @@ -1863,69 +1598,12 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = { .handler = handle_query_first_threads, .cmd = "fThreadInfo", }, - { - .handler = handle_query_thread_extra, - .cmd = "ThreadExtraInfo,", - .cmd_startswith = true, - .schema = "t0" - }, -#ifdef CONFIG_USER_ONLY - { - .handler = gdb_handle_query_offsets, - .cmd = "Offsets", - }, -#else - { - .handler = gdb_handle_query_rcmd, - .cmd = "Rcmd,", - .cmd_startswith = true, - .schema = "s0" - }, -#endif - { - .handler = handle_query_supported, - .cmd = "Supported:", - .cmd_startswith = true, - .schema = "s0" - }, - { - .handler = handle_query_supported, - .cmd = "Supported", - .schema = "s0" - }, - { - .handler = handle_query_xfer_features, - .cmd = "Xfer:features:read:", - .cmd_startswith = true, - .schema = "s:l,l0" - }, -#if defined(CONFIG_USER_ONLY) -#if defined(CONFIG_LINUX) - { - .handler = gdb_handle_query_xfer_auxv, - .cmd = "Xfer:auxv:read::", - .cmd_startswith = true, - .schema = "l,l0" - }, - { - .handler = gdb_handle_query_xfer_siginfo, - .cmd = "Xfer:siginfo:read::", - .cmd_startswith = true, - .schema = "l,l0" - }, -#endif - { - .handler = gdb_handle_query_xfer_exec_file, - .cmd = "Xfer:exec-file:read:", - .cmd_startswith = true, - .schema = "l:l,l0" - }, -#endif - { - .handler = gdb_handle_query_attached, - .cmd = "Attached:", - .cmd_startswith = true - }, + {.handler = handle_query_thread_extra, .cmd = "ThreadExtraInfo,", .cmd_startswith = true, .schema = "t0"}, + {.handler = gdb_handle_query_rcmd, .cmd = "Rcmd,", .cmd_startswith = true, .schema = "s0"}, + {.handler = handle_query_supported, .cmd = "Supported:", .cmd_startswith = true, .schema = "s0"}, + {.handler = handle_query_supported, .cmd = "Supported", .schema = "s0"}, + {.handler = handle_query_xfer_features, .cmd = "Xfer:features:read:", .cmd_startswith = true, .schema = "s:l,l0"}, + {.handler = gdb_handle_query_attached, .cmd = "Attached:", .cmd_startswith = true}, { .handler = gdb_handle_query_attached, .cmd = "Attached", @@ -1934,556 +1612,394 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = { .handler = handle_query_qemu_supported, .cmd = "qemu.Supported", }, -#ifndef CONFIG_USER_ONLY { .handler = gdb_handle_query_qemu_phy_mem_mode, .cmd = "qemu.PhyMemMode", }, -#endif }; /* 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); -} +void gdb_extend_set_table(GPtrArray *new_set) { extended_set_table = extend_table(extended_set_table, new_set); } static const GdbCmdParseEntry gdb_gen_set_table[] = { /* Order is important if has same prefix */ - { - .handler = handle_set_qemu_sstep, - .cmd = "qemu.sstep:", - .cmd_startswith = true, - .schema = "l0" - }, -#ifndef CONFIG_USER_ONLY - { - .handler = gdb_handle_set_qemu_phy_mem_mode, - .cmd = "qemu.PhyMemMode:", - .cmd_startswith = true, - .schema = "l0" - }, -#endif -#if defined(CONFIG_USER_ONLY) - { - .handler = gdb_handle_set_catch_syscalls, - .cmd = "CatchSyscalls:", - .cmd_startswith = true, - .schema = "s0", - }, -#endif + {.handler = handle_set_qemu_sstep, .cmd = "qemu.sstep:", .cmd_startswith = true, .schema = "l0"}, + {.handler = gdb_handle_set_qemu_phy_mem_mode, .cmd = "qemu.PhyMemMode:", .cmd_startswith = true, .schema = "l0"}, }; -static void handle_gen_query(GArray *params, void *user_ctx) -{ - const char *data; +static void handle_gen_query(GArray *params, void *user_ctx) { + const char *data; - if (!params->len) { - return; - } + if (!params->len) { + return; + } - data = gdb_get_cmd_param(params, 0)->data; + data = gdb_get_cmd_param(params, 0)->data; - if (process_string_cmd(data, - gdb_gen_query_set_common_table, - ARRAY_SIZE(gdb_gen_query_set_common_table))) { - return; - } + if (process_string_cmd(data, gdb_gen_query_set_common_table, ARRAY_SIZE(gdb_gen_query_set_common_table))) { + return; + } - if (process_string_cmd(data, - gdb_gen_query_table, - ARRAY_SIZE(gdb_gen_query_table))) { - return; - } + if (process_string_cmd(data, gdb_gen_query_table, ARRAY_SIZE(gdb_gen_query_table))) { + return; + } - if (extended_query_table && - process_extended_table(extended_query_table, data)) { - return; - } + if (extended_query_table && process_extended_table(extended_query_table, data)) { + return; + } - /* Can't handle query, return Empty response. */ - gdb_put_packet(""); + /* Can't handle query, return Empty response. */ + gdb_put_packet(""); } -static void handle_gen_set(GArray *params, void *user_ctx) -{ - const char *data; +static void handle_gen_set(GArray *params, void *user_ctx) { + const char *data; - if (!params->len) { - return; - } + if (!params->len) { + return; + } - data = gdb_get_cmd_param(params, 0)->data; + data = gdb_get_cmd_param(params, 0)->data; - if (process_string_cmd(data, - gdb_gen_query_set_common_table, - ARRAY_SIZE(gdb_gen_query_set_common_table))) { - return; - } + if (process_string_cmd(data, gdb_gen_query_set_common_table, ARRAY_SIZE(gdb_gen_query_set_common_table))) { + return; + } - if (process_string_cmd(data, - gdb_gen_set_table, - ARRAY_SIZE(gdb_gen_set_table))) { - return; - } + if (process_string_cmd(data, gdb_gen_set_table, ARRAY_SIZE(gdb_gen_set_table))) { + return; + } - if (extended_set_table && - process_extended_table(extended_set_table, data)) { - return; - } + if (extended_set_table && process_extended_table(extended_set_table, data)) { + return; + } - /* Can't handle set, return Empty response. */ - gdb_put_packet(""); + /* Can't handle set, return Empty response. */ + gdb_put_packet(""); } -static void handle_target_halt(GArray *params, void *user_ctx) -{ - if (gdbserver_state.allow_stop_reply) { - g_string_printf(gdbserver_state.str_buf, "T%02xthread:", GDB_SIGNAL_TRAP); - gdb_append_thread_id(gdbserver_state.c_cpu, gdbserver_state.str_buf); - g_string_append_c(gdbserver_state.str_buf, ';'); - gdb_put_strbuf(); - gdbserver_state.allow_stop_reply = false; - } - /* - * 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(gdbserver_state.c_cpu); -} - -static int gdb_handle_packet(const char *line_buf) -{ - const GdbCmdParseEntry *cmd_parser = NULL; - - trace_gdbstub_io_command(line_buf); - - switch (line_buf[0]) { - case '!': - gdb_put_packet("OK"); - break; - case '?': - { - static const GdbCmdParseEntry target_halted_cmd_desc = { - .handler = handle_target_halt, - .cmd = "?", - .cmd_startswith = true, - .allow_stop_reply = true, - }; - cmd_parser = &target_halted_cmd_desc; - } - break; - case 'c': - { - static const GdbCmdParseEntry continue_cmd_desc = { - .handler = handle_continue, - .cmd = "c", - .cmd_startswith = true, - .allow_stop_reply = true, - .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 = true, - .allow_stop_reply = true, - .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 = true, - .schema = "s0" - }; - cmd_parser = &v_cmd_desc; - } - break; - case 'k': - /* Kill the target */ - error_report("QEMU: Terminated via GDBstub"); - gdb_exit(0); - gdb_qemu_exit(0); - break; - case 'D': - { - static const GdbCmdParseEntry detach_cmd_desc = { - .handler = handle_detach, - .cmd = "D", - .cmd_startswith = true, - .schema = "?.l0" - }; - cmd_parser = &detach_cmd_desc; - } - break; - case 's': - { - static const GdbCmdParseEntry step_cmd_desc = { - .handler = handle_step, - .cmd = "s", - .cmd_startswith = true, - .allow_stop_reply = true, - .schema = "L0" - }; - cmd_parser = &step_cmd_desc; - } - break; - case 'b': - { - static const GdbCmdParseEntry backward_cmd_desc = { - .handler = handle_backward, - .cmd = "b", - .cmd_startswith = true, - .allow_stop_reply = true, - .schema = "o0" - }; - cmd_parser = &backward_cmd_desc; - } - break; - case 'F': - { - static const GdbCmdParseEntry file_io_cmd_desc = { - .handler = gdb_handle_file_io, - .cmd = "F", - .cmd_startswith = true, - .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 = true - }; - 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 = true, - .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 = true, - .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 = true, - .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 = true, - .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 = true, - .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 = true, - .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 = true, - .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 = true, - .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 = true, - .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 = true, - .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 = true, - .schema = "s0" - }; - cmd_parser = &gen_set_cmd_desc; - } - break; - default: - /* put empty packet */ - gdb_put_packet(""); - break; - } - - if (cmd_parser) { - run_cmd_parser(line_buf, cmd_parser); - } - - 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; - +static void handle_target_halt(GArray *params, void *user_ctx) { + if (gdbserver_state.allow_stop_reply) { + g_string_printf(gdbserver_state.str_buf, "T%02xthread:", GDB_SIGNAL_TRAP); + gdb_append_thread_id(gdbserver_state.c_cpu, gdbserver_state.str_buf); + g_string_append_c(gdbserver_state.str_buf, ';'); + gdb_put_strbuf(); gdbserver_state.allow_stop_reply = false; -#ifndef CONFIG_USER_ONLY - if (gdbserver_state.last_packet->len) { - /* Waiting for a response to the last packet. If we see the start - of a new command then abandon the previous response. */ - if (ch == '-') { - trace_gdbstub_err_got_nack(); - gdb_put_buffer(gdbserver_state.last_packet->data, - gdbserver_state.last_packet->len); + } + /* + * 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(gdbserver_state.c_cpu); +} + +static RSState gdb_handle_packet(const char *line_buf) { + const GdbCmdParseEntry *cmd_parser = NULL; + + // trace_gdbstub_io_command(line_buf); + + switch (line_buf[0]) { + case '!': + gdb_put_packet("OK"); + break; + case '?': { + static const GdbCmdParseEntry target_halted_cmd_desc = { + .handler = handle_target_halt, + .cmd = "?", + .cmd_startswith = true, + .allow_stop_reply = true, + }; + cmd_parser = &target_halted_cmd_desc; + } break; + case 'c': { + static const GdbCmdParseEntry continue_cmd_desc = { + .handler = handle_continue, .cmd = "c", .cmd_startswith = true, .schema = "L0", .allow_stop_reply = true}; + 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 = true, + .schema = "l0", + .allow_stop_reply = true}; + cmd_parser = &cont_with_sig_cmd_desc; + } break; + case 'v': { + static const GdbCmdParseEntry v_cmd_desc = { + .handler = handle_v_commands, .cmd = "v", .cmd_startswith = true, .schema = "s0"}; + cmd_parser = &v_cmd_desc; + } break; + case 'k': + /* Kill the target */ + std::cout << "QEMU: Terminated via GDBstub" << std::endl; + gdb_exit(0); + gdb_qemu_exit(0); + break; + case 'D': { + static const GdbCmdParseEntry detach_cmd_desc = { + .handler = handle_detach, .cmd = "D", .cmd_startswith = true, .schema = "?.l0"}; + cmd_parser = &detach_cmd_desc; + } break; + case 's': { + static const GdbCmdParseEntry step_cmd_desc = { + .handler = handle_step, .cmd = "s", .cmd_startswith = true, .schema = "L0", .allow_stop_reply = true}; + cmd_parser = &step_cmd_desc; + } break; + case 'b': { + static const GdbCmdParseEntry backward_cmd_desc = { + .handler = handle_backward, .cmd = "b", .cmd_startswith = true, .schema = "o0", .allow_stop_reply = true}; + cmd_parser = &backward_cmd_desc; + } break; + case 'F': { + static const GdbCmdParseEntry file_io_cmd_desc = { + .handler = gdb_handle_file_io, .cmd = "F", .cmd_startswith = true, .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 = true}; + 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 = true, .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 = true, .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 = true, .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 = true, .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 = true, .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 = true, .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 = true, .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 = true, .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 = true, .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 = true, .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 = true, .schema = "s0"}; + cmd_parser = &gen_set_cmd_desc; + } break; + default: + /* put empty packet */ + gdb_put_packet(""); + break; + } + + if (cmd_parser) { + run_cmd_parser(line_buf, cmd_parser); + } + + 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; + + gdbserver_state.allow_stop_reply = false; + if (gdbserver_state.last_packet->len) { + /* Waiting for a response to the last packet. If we see the start + of a new command then abandon the previous response. */ + if (ch == '-') { + // trace_gdbstub_err_got_nack(); + gdb_put_buffer(gdbserver_state.last_packet->data, gdbserver_state.last_packet->len); + } else if (ch == '+') { + // trace_gdbstub_io_got_ack(); + } else { + // trace_gdbstub_io_got_unexpected(ch); + } + + if (ch == '+' || ch == '$') { + g_byte_array_set_size(gdbserver_state.last_packet, 0); + } + if (ch != '$') return; + } + if (runstate_is_running()) { + /* + * When the CPU is running, we cannot do anything except stop + * it when receiving a char. This is expected on a Ctrl-C in the + * gdb client. Because we are in all-stop mode, gdb sends a + * 0x03 byte which is not a usual packet, so we handle it specially + * here, but it does expect a stop reply. + */ + if (ch != 0x03) { + // trace_gdbstub_err_unexpected_runpkt(ch); + } else { + gdbserver_state.allow_stop_reply = true; + } + vm_stop(RUN_STATE_PAUSED); + } else { + switch (gdbserver_state.state) { + case RS_IDLE: + if (ch == '$') { + /* start of command packet */ + gdbserver_state.line_buf_index = 0; + gdbserver_state.line_sum = 0; + gdbserver_state.state = RS_GETLINE; } else if (ch == '+') { - trace_gdbstub_io_got_ack(); + /* + * do nothing, gdb may preemptively send out ACKs on + * initial connection + */ } else { - trace_gdbstub_io_got_unexpected(ch); + // trace_gdbstub_err_garbage(ch); } - - if (ch == '+' || ch == '$') { - g_byte_array_set_size(gdbserver_state.last_packet, 0); + break; + case RS_GETLINE: + if (ch == '}') { + /* start escape sequence */ + gdbserver_state.state = RS_GETLINE_ESC; + gdbserver_state.line_sum += ch; + } else if (ch == '*') { + /* start run length encoding sequence */ + gdbserver_state.state = RS_GETLINE_RLE; + gdbserver_state.line_sum += ch; + } else if (ch == '#') { + /* end of command, start of checksum*/ + gdbserver_state.state = RS_CHKSUM1; + } else if (gdbserver_state.line_buf_index >= sizeof(gdbserver_state.line_buf) - 1) { + // trace_gdbstub_err_overrun(); + gdbserver_state.state = RS_IDLE; + } else { + /* unescaped command character */ + gdbserver_state.line_buf[gdbserver_state.line_buf_index++] = ch; + gdbserver_state.line_sum += ch; } - if (ch != '$') - return; - } - if (runstate_is_running()) { + break; + case RS_GETLINE_ESC: + if (ch == '#') { + /* unexpected end of command in escape sequence */ + gdbserver_state.state = RS_CHKSUM1; + } else if (gdbserver_state.line_buf_index >= sizeof(gdbserver_state.line_buf) - 1) { + /* command buffer overrun */ + // trace_gdbstub_err_overrun(); + gdbserver_state.state = RS_IDLE; + } else { + /* parse escaped character and leave escape state */ + gdbserver_state.line_buf[gdbserver_state.line_buf_index++] = ch ^ 0x20; + gdbserver_state.line_sum += ch; + gdbserver_state.state = RS_GETLINE; + } + break; + case RS_GETLINE_RLE: /* - * When the CPU is running, we cannot do anything except stop - * it when receiving a char. This is expected on a Ctrl-C in the - * gdb client. Because we are in all-stop mode, gdb sends a - * 0x03 byte which is not a usual packet, so we handle it specially - * here, but it does expect a stop reply. + * Run-length encoding is explained in "Debugging with GDB / + * Appendix E GDB Remote Serial Protocol / Overview". */ - if (ch != 0x03) { - trace_gdbstub_err_unexpected_runpkt(ch); + if (ch < ' ' || ch == '#' || ch == '$' || ch > 126) { + /* invalid RLE count encoding */ + // trace_gdbstub_err_invalid_repeat(ch); + gdbserver_state.state = RS_GETLINE; } else { - gdbserver_state.allow_stop_reply = true; + /* decode repeat length */ + int repeat = ch - ' ' + 3; + if (gdbserver_state.line_buf_index + repeat >= sizeof(gdbserver_state.line_buf) - 1) { + /* that many repeats would overrun the command buffer */ + // trace_gdbstub_err_overrun(); + gdbserver_state.state = RS_IDLE; + } else if (gdbserver_state.line_buf_index < 1) { + /* got a repeat but we have nothing to repeat */ + // trace_gdbstub_err_invalid_rle(); + gdbserver_state.state = RS_GETLINE; + } else { + /* repeat the last character */ + memset(gdbserver_state.line_buf + gdbserver_state.line_buf_index, + gdbserver_state.line_buf[gdbserver_state.line_buf_index - 1], repeat); + gdbserver_state.line_buf_index += repeat; + gdbserver_state.line_sum += ch; + gdbserver_state.state = RS_GETLINE; + } } - vm_stop(RUN_STATE_PAUSED); - } else -#endif - { - switch(gdbserver_state.state) { - case RS_IDLE: - if (ch == '$') { - /* start of command packet */ - gdbserver_state.line_buf_index = 0; - gdbserver_state.line_sum = 0; - gdbserver_state.state = RS_GETLINE; - } else if (ch == '+') { - /* - * do nothing, gdb may preemptively send out ACKs on - * initial connection - */ - } else { - trace_gdbstub_err_garbage(ch); - } - break; - case RS_GETLINE: - if (ch == '}') { - /* start escape sequence */ - gdbserver_state.state = RS_GETLINE_ESC; - gdbserver_state.line_sum += ch; - } else if (ch == '*') { - /* start run length encoding sequence */ - gdbserver_state.state = RS_GETLINE_RLE; - gdbserver_state.line_sum += ch; - } else if (ch == '#') { - /* end of command, start of checksum*/ - gdbserver_state.state = RS_CHKSUM1; - } else if (gdbserver_state.line_buf_index >= sizeof(gdbserver_state.line_buf) - 1) { - trace_gdbstub_err_overrun(); - gdbserver_state.state = RS_IDLE; - } else { - /* unescaped command character */ - gdbserver_state.line_buf[gdbserver_state.line_buf_index++] = ch; - gdbserver_state.line_sum += ch; - } - break; - case RS_GETLINE_ESC: - if (ch == '#') { - /* unexpected end of command in escape sequence */ - gdbserver_state.state = RS_CHKSUM1; - } else if (gdbserver_state.line_buf_index >= sizeof(gdbserver_state.line_buf) - 1) { - /* command buffer overrun */ - trace_gdbstub_err_overrun(); - gdbserver_state.state = RS_IDLE; - } else { - /* parse escaped character and leave escape state */ - gdbserver_state.line_buf[gdbserver_state.line_buf_index++] = ch ^ 0x20; - gdbserver_state.line_sum += ch; - gdbserver_state.state = RS_GETLINE; - } - break; - case RS_GETLINE_RLE: - /* - * Run-length encoding is explained in "Debugging with GDB / - * Appendix E GDB Remote Serial Protocol / Overview". - */ - if (ch < ' ' || ch == '#' || ch == '$' || ch > 126) { - /* invalid RLE count encoding */ - trace_gdbstub_err_invalid_repeat(ch); - gdbserver_state.state = RS_GETLINE; - } else { - /* decode repeat length */ - int repeat = ch - ' ' + 3; - if (gdbserver_state.line_buf_index + repeat >= sizeof(gdbserver_state.line_buf) - 1) { - /* that many repeats would overrun the command buffer */ - trace_gdbstub_err_overrun(); - gdbserver_state.state = RS_IDLE; - } else if (gdbserver_state.line_buf_index < 1) { - /* got a repeat but we have nothing to repeat */ - trace_gdbstub_err_invalid_rle(); - gdbserver_state.state = RS_GETLINE; - } else { - /* repeat the last character */ - memset(gdbserver_state.line_buf + gdbserver_state.line_buf_index, - gdbserver_state.line_buf[gdbserver_state.line_buf_index - 1], repeat); - gdbserver_state.line_buf_index += repeat; - gdbserver_state.line_sum += ch; - gdbserver_state.state = RS_GETLINE; - } - } - break; - case RS_CHKSUM1: - /* get high hex digit of checksum */ - if (!isxdigit(ch)) { - trace_gdbstub_err_checksum_invalid(ch); - gdbserver_state.state = RS_GETLINE; - break; - } - gdbserver_state.line_buf[gdbserver_state.line_buf_index] = '\0'; - gdbserver_state.line_csum = fromhex(ch) << 4; - gdbserver_state.state = RS_CHKSUM2; - break; - case RS_CHKSUM2: - /* get low hex digit of checksum */ - if (!isxdigit(ch)) { - trace_gdbstub_err_checksum_invalid(ch); - gdbserver_state.state = RS_GETLINE; - break; - } - gdbserver_state.line_csum |= fromhex(ch); + break; + case RS_CHKSUM1: + /* get high hex digit of checksum */ + if (!isxdigit(ch)) { + // trace_gdbstub_err_checksum_invalid(ch); + gdbserver_state.state = RS_GETLINE; + break; + } + gdbserver_state.line_buf[gdbserver_state.line_buf_index] = '\0'; + gdbserver_state.line_csum = fromhex(ch) << 4; + gdbserver_state.state = RS_CHKSUM2; + break; + case RS_CHKSUM2: + /* get low hex digit of checksum */ + if (!isxdigit(ch)) { + // trace_gdbstub_err_checksum_invalid(ch); + gdbserver_state.state = RS_GETLINE; + break; + } + gdbserver_state.line_csum |= fromhex(ch); - if (gdbserver_state.line_csum != (gdbserver_state.line_sum & 0xff)) { - trace_gdbstub_err_checksum_incorrect(gdbserver_state.line_sum, gdbserver_state.line_csum); - /* send NAK reply */ - reply = '-'; - gdb_put_buffer(&reply, 1); - gdbserver_state.state = RS_IDLE; - } else { - /* send ACK reply */ - reply = '+'; - gdb_put_buffer(&reply, 1); - gdbserver_state.state = gdb_handle_packet(gdbserver_state.line_buf); - } - break; - default: - abort(); + if (gdbserver_state.line_csum != (gdbserver_state.line_sum & 0xff)) { + // trace_gdbstub_err_checksum_incorrect(gdbserver_state.line_sum, gdbserver_state.line_csum); + /* send NAK reply */ + reply = '-'; + gdb_put_buffer(&reply, 1); + gdbserver_state.state = RS_IDLE; + } else { + /* send ACK reply */ + reply = '+'; + gdb_put_buffer(&reply, 1); + gdbserver_state.state = gdb_handle_packet(gdbserver_state.line_buf); } + break; + default: + abort(); } + } } /* @@ -2491,29 +2007,22 @@ void gdb_read_byte(uint8_t ch) * 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; +void gdb_create_default_process(GDBState *s) { + GDBProcess *process; + int pid; -#ifdef CONFIG_USER_ONLY - assert(gdbserver_state.process_num == 0); - pid = getpid(); -#else - 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++; -#endif + 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; + 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 92466b2..75d9631 100644 --- a/gdbstub/internals.h +++ b/gdbstub/internals.h @@ -9,7 +9,8 @@ #ifndef GDBSTUB_INTERNALS_H #define GDBSTUB_INTERNALS_H -#include "exec/cpu-common.h" +// #include "exec/cpu-common.h" +#include "cpu.h" /* * Most "large" transfers (e.g. memory reads, feature XML @@ -38,58 +39,58 @@ */ enum { - GDB_SIGNAL_0 = 0, - GDB_SIGNAL_INT = 2, - GDB_SIGNAL_QUIT = 3, - GDB_SIGNAL_TRAP = 5, - GDB_SIGNAL_ABRT = 6, - GDB_SIGNAL_ALRM = 14, - GDB_SIGNAL_STOP = 17, - GDB_SIGNAL_IO = 23, - GDB_SIGNAL_XCPU = 24, - GDB_SIGNAL_UNKNOWN = 143 + GDB_SIGNAL_0 = 0, + GDB_SIGNAL_INT = 2, + GDB_SIGNAL_QUIT = 3, + GDB_SIGNAL_TRAP = 5, + GDB_SIGNAL_ABRT = 6, + GDB_SIGNAL_ALRM = 14, + GDB_SIGNAL_STOP = 17, + GDB_SIGNAL_IO = 23, + GDB_SIGNAL_XCPU = 24, + GDB_SIGNAL_UNKNOWN = 143 }; typedef struct GDBProcess { - uint32_t pid; - bool attached; - char *target_xml; + uint32_t pid; + bool attached; + char *target_xml; } GDBProcess; enum RSState { - RS_INACTIVE, - RS_IDLE, - RS_GETLINE, - RS_GETLINE_ESC, - RS_GETLINE_RLE, - RS_CHKSUM1, - RS_CHKSUM2, + RS_INACTIVE, + RS_IDLE, + RS_GETLINE, + RS_GETLINE_ESC, + RS_GETLINE_RLE, + RS_CHKSUM1, + RS_CHKSUM2, }; typedef struct GDBState { - bool init; /* have we been initialised? */ - CPUState *c_cpu; /* current CPU for step/continue ops */ - CPUState *g_cpu; /* current CPU for other ops */ - CPUState *query_cpu; /* for q{f|s}ThreadInfo */ - enum RSState state; /* parsing state */ - char line_buf[MAX_PACKET_LENGTH]; - int line_buf_index; - int line_sum; /* running checksum */ - 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; - GByteArray *mem_buf; - int sstep_flags; - int supported_sstep_flags; - /* - * Whether we are allowed to send a stop reply packet at this moment. - * Must be set off after sending the stop reply itself. - */ - bool allow_stop_reply; + bool init; /* have we been initialised? */ + CPUState *c_cpu; /* current CPU for step/continue ops */ + CPUState *g_cpu; /* current CPU for other ops */ + CPUState *query_cpu; /* for q{f|s}ThreadInfo */ + enum RSState state; /* parsing state */ + char line_buf[MAX_PACKET_LENGTH]; + int line_buf_index; + int line_sum; /* running checksum */ + 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; + GByteArray *mem_buf; + int sstep_flags; + int supported_sstep_flags; + /* + * Whether we are allowed to send a stop reply packet at this moment. + * Must be set off after sending the stop reply itself. + */ + bool allow_stop_reply; } GDBState; /* lives in main gdbstub.c */ @@ -99,26 +100,24 @@ extern GDBState gdbserver_state; * Inline utility function, convert from int to hex and back */ -static inline int fromhex(int v) -{ - if (v >= '0' && v <= '9') { - return v - '0'; - } else if (v >= 'A' && v <= 'F') { - return v - 'A' + 10; - } else if (v >= 'a' && v <= 'f') { - return v - 'a' + 10; - } else { - return 0; - } +static inline int fromhex(int v) { + if (v >= '0' && v <= '9') { + return v - '0'; + } else if (v >= 'A' && v <= 'F') { + return v - 'A' + 10; + } else if (v >= 'a' && v <= 'f') { + return v - 'a' + 10; + } else { + return 0; + } } -static inline int tohex(int v) -{ - if (v < 10) { - return v + '0'; - } else { - return v - 10 + 'a'; - } +static inline int tohex(int v) { + if (v < 10) { + return v + '0'; + } else { + return v - 10 + 'a'; + } } /* @@ -153,14 +152,14 @@ CPUState *gdb_first_attached_cpu(void); 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 */ +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_target_signal_to_gdb(int sig); int gdb_get_char(void); /* user only */ @@ -174,29 +173,23 @@ void gdb_continue(void); */ int gdb_continue_partial(char *newstates); -/* - * Helpers with separate system and user implementations - */ -void gdb_put_buffer(const uint8_t *buf, int len); - /* * Command handlers - either specialised or system or user only */ void gdb_init_gdbserver_state(void); void gdb_handle_query_rcmd(GArray *params, void *ctx); /* system */ -void gdb_handle_query_offsets(GArray *params, void *user_ctx); /* user */ -void gdb_handle_query_xfer_auxv(GArray *params, void *user_ctx); /*user */ -void gdb_handle_query_xfer_siginfo(GArray *params, void *user_ctx); /*user */ -void gdb_handle_v_file_open(GArray *params, void *user_ctx); /* user */ -void gdb_handle_v_file_close(GArray *params, void *user_ctx); /* user */ -void gdb_handle_v_file_pread(GArray *params, void *user_ctx); /* user */ -void gdb_handle_v_file_readlink(GArray *params, void *user_ctx); /* user */ -void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx); /* user */ -void gdb_handle_set_catch_syscalls(GArray *params, void *user_ctx); /* user */ -void gdb_handle_query_supported_user(const char *gdb_supported); /* user */ -bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid); /* user */ -bool gdb_handle_detach_user(uint32_t pid); /* user */ +// void gdb_handle_query_xfer_auxv(GArray *params, void *user_ctx); /*user */ +// void gdb_handle_query_xfer_siginfo(GArray *params, void *user_ctx); /*user */ +// void gdb_handle_v_file_open(GArray *params, void *user_ctx); /* user */ +// void gdb_handle_v_file_close(GArray *params, void *user_ctx); /* user */ +// void gdb_handle_v_file_pread(GArray *params, void *user_ctx); /* user */ +// void gdb_handle_v_file_readlink(GArray *params, void *user_ctx); /* user */ +// void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx); /* user */ +// void gdb_handle_set_catch_syscalls(GArray *params, void *user_ctx); /* user */ +// void gdb_handle_query_supported_user(const char *gdb_supported); /* user */ +// bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid); /* user */ +// bool gdb_handle_detach_user(uint32_t pid); /* user */ void gdb_handle_query_attached(GArray *params, void *ctx); /* both */ @@ -206,21 +199,40 @@ void gdb_handle_set_qemu_phy_mem_mode(GArray *params, void *ctx); /* sycall handling */ void gdb_handle_file_io(GArray *params, void *user_ctx); -bool gdb_handled_syscall(void); void gdb_disable_syscalls(void); -void gdb_syscall_reset(void); - -/* user/system specific syscall handling */ -void gdb_syscall_handling(const char *syscall_packet); /* * Break/Watch point support - there is an implementation for system * and user mode. */ + +// TODO +bool runstate_is_running(); +void vm_stop(RunState rs); +void cpu_synchronize_state(CPUState *cpu); +void gdb_exit(int i); +void gdb_qemu_exit(int i); + +CPUState *get_cpu(); +CPUState *cpu_next(CPUState *cpu); +void gdb_breakpoint_remove_all(CPUState *cs); bool gdb_supports_guest_debug(void); int gdb_breakpoint_insert(CPUState *cs, int type, vaddr addr, vaddr len); int gdb_breakpoint_remove(CPUState *cs, int type, vaddr addr, vaddr len); -void gdb_breakpoint_remove_all(CPUState *cs); + +void gdb_put_buffer(const uint8_t *buf, int len); + +bool gdbserver_start(const char *device, Error **errp); + +static inline void cpu_physical_memory_write(hwaddr addr, const void *buf, hwaddr len); +static inline void cpu_physical_memory_read(hwaddr addr, const void *buf, hwaddr len); + +bool runstate_needs_reset(void); +void vm_start(); +bool vm_prepare_start(bool step_requested); +void qemu_clock_enable(); + +#define CPU_FOREACH(cpu) for (auto c = get_cpu(); false;) /** * gdb_target_memory_rw_debug() - handle debug access to memory @@ -234,7 +246,6 @@ void gdb_breakpoint_remove_all(CPUState *cs); * 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); +int gdb_target_memory_rw_debug(CPUState *cs, hwaddr addr, uint8_t *buf, int len, bool is_write); #endif /* GDBSTUB_INTERNALS_H */ diff --git a/gdbstub/riscv64-softmmu-gdbstub-xml.c b/gdbstub/riscv64-softmmu-gdbstub-xml.c new file mode 100644 index 0000000..06cc163 --- /dev/null +++ b/gdbstub/riscv64-softmmu-gdbstub-xml.c @@ -0,0 +1,509 @@ +#include "gdbstub.h" + +const GDBFeature gdb_static_features[] = { + { + "riscv-64bit-cpu.xml", + "\n" + "\n" + "\n" + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n", + "org.gnu.gdb.riscv.cpu", + (const char * const []) { + [0] = + "zero", + [1] = + "ra", + [2] = + "sp", + [3] = + "gp", + [4] = + "tp", + [5] = + "t0", + [6] = + "t1", + [7] = + "t2", + [8] = + "fp", + [9] = + "s1", + [10] = + "a0", + [11] = + "a1", + [12] = + "a2", + [13] = + "a3", + [14] = + "a4", + [15] = + "a5", + [16] = + "a6", + [17] = + "a7", + [18] = + "s2", + [19] = + "s3", + [20] = + "s4", + [21] = + "s5", + [22] = + "s6", + [23] = + "s7", + [24] = + "s8", + [25] = + "s9", + [26] = + "s10", + [27] = + "s11", + [28] = + "t3", + [29] = + "t4", + [30] = + "t5", + [31] = + "t6", + [32] = + "pc", + }, + 33, + }, + { + "riscv-32bit-fpu.xml", + "\n" + "\n" + "\n" + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n", + "org.gnu.gdb.riscv.fpu", + (const char * const []) { + [0] = + "ft0", + [1] = + "ft1", + [2] = + "ft2", + [3] = + "ft3", + [4] = + "ft4", + [5] = + "ft5", + [6] = + "ft6", + [7] = + "ft7", + [8] = + "fs0", + [9] = + "fs1", + [10] = + "fa0", + [11] = + "fa1", + [12] = + "fa2", + [13] = + "fa3", + [14] = + "fa4", + [15] = + "fa5", + [16] = + "fa6", + [17] = + "fa7", + [18] = + "fs2", + [19] = + "fs3", + [20] = + "fs4", + [21] = + "fs5", + [22] = + "fs6", + [23] = + "fs7", + [24] = + "fs8", + [25] = + "fs9", + [26] = + "fs10", + [27] = + "fs11", + [28] = + "ft8", + [29] = + "ft9", + [30] = + "ft10", + [31] = + "ft11", + }, + 32, + }, + { + "riscv-64bit-fpu.xml", + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n", + "org.gnu.gdb.riscv.fpu", + (const char * const []) { + [0] = + "ft0", + [1] = + "ft1", + [2] = + "ft2", + [3] = + "ft3", + [4] = + "ft4", + [5] = + "ft5", + [6] = + "ft6", + [7] = + "ft7", + [8] = + "fs0", + [9] = + "fs1", + [10] = + "fa0", + [11] = + "fa1", + [12] = + "fa2", + [13] = + "fa3", + [14] = + "fa4", + [15] = + "fa5", + [16] = + "fa6", + [17] = + "fa7", + [18] = + "fs2", + [19] = + "fs3", + [20] = + "fs4", + [21] = + "fs5", + [22] = + "fs6", + [23] = + "fs7", + [24] = + "fs8", + [25] = + "fs9", + [26] = + "fs10", + [27] = + "fs11", + [28] = + "ft8", + [29] = + "ft9", + [30] = + "ft10", + [31] = + "ft11", + }, + 32, + }, + { + "riscv-64bit-virtual.xml", + "\n" + "\n" + "\n" + "\n" + "\n" + " \n" + "\n", + "org.gnu.gdb.riscv.virtual", + (const char * const []) { + [0] = + "priv", + }, + 1, + }, + { + "riscv-32bit-cpu.xml", + "\n" + "\n" + "\n" + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n", + "org.gnu.gdb.riscv.cpu", + (const char * const []) { + [0] = + "zero", + [1] = + "ra", + [2] = + "sp", + [3] = + "gp", + [4] = + "tp", + [5] = + "t0", + [6] = + "t1", + [7] = + "t2", + [8] = + "fp", + [9] = + "s1", + [10] = + "a0", + [11] = + "a1", + [12] = + "a2", + [13] = + "a3", + [14] = + "a4", + [15] = + "a5", + [16] = + "a6", + [17] = + "a7", + [18] = + "s2", + [19] = + "s3", + [20] = + "s4", + [21] = + "s5", + [22] = + "s6", + [23] = + "s7", + [24] = + "s8", + [25] = + "s9", + [26] = + "s10", + [27] = + "s11", + [28] = + "t3", + [29] = + "t4", + [30] = + "t5", + [31] = + "t6", + [32] = + "pc", + }, + 33, + }, + { + "riscv-32bit-virtual.xml", + "\n" + "\n" + "\n" + "\n" + "\n" + " \n" + "\n", + "org.gnu.gdb.riscv.virtual", + (const char * const []) { + [0] = + "priv", + }, + 1, + }, + { NULL } +}; diff --git a/gdbstub/system.c b/gdbstub/system.c index 1436e0e..ab5e787 100644 --- a/gdbstub/system.c +++ b/gdbstub/system.c @@ -10,37 +10,10 @@ * SPDX-License-Identifier: LGPL-2.0-or-later */ -#include "accel/accel-cpu-ops.h" -#include "accel/accel-ops.h" -#include "chardev/char-fe.h" -#include "chardev/char.h" -#include "exec/gdbstub.h" -#include "exec/hwaddr.h" -#include "exec/tb-flush.h" -#include "gdbstub/commands.h" -#include "gdbstub/syscalls.h" -#include "hw/boards.h" -#include "hw/core/cpu.h" -#include "hw/cpu/cluster.h" +#include "commands.h" +#include "cpu.h" +#include "enums.h" #include "internals.h" -#include "monitor/monitor.h" -#include "qapi/error.h" -#include "qemu/cutils.h" -#include "qemu/error-report.h" -#include "qemu/osdep.h" -#include "system/cpus.h" -#include "system/replay.h" -#include "system/runstate.h" -#include "system/tcg.h" -#include "trace.h" - -/* System emulation specific state */ -typedef struct { - CharBackend chr; - Chardev *mon_chr; -} GDBSystemState; - -GDBSystemState gdbserver_system_state; static void reset_gdbserver_state(void) { g_free(gdbserver_state.processes); @@ -55,7 +28,7 @@ static void reset_gdbserver_state(void) { * 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 cpu->cpu_index + 1; } +int gdb_get_cpu_index(CPUState *cpu) { return 0; } /* * We check the status of the last message in the chardev receive code @@ -68,171 +41,163 @@ bool gdb_got_immediate_ack(void) { return true; } * network and unix sockets. */ -void gdb_put_buffer(const uint8_t *buf, int len) { - /* - * XXX this blocks entire thread. Rewrite to use - * qemu_chr_fe_write and background I/O callbacks - */ - qemu_chr_fe_write_all(&gdbserver_system_state.chr, buf, len); -} +// static void gdb_chr_event(void *opaque, QEMUChrEvent event) { +// int i; +// GDBState *s = (GDBState *)opaque; -static void gdb_chr_event(void *opaque, QEMUChrEvent event) { - int i; - GDBState *s = (GDBState *)opaque; +// switch (event) { +// case CHR_EVENT_OPENED: +// /* Start with first process attached, others detached */ +// for (i = 0; i < s->process_num; i++) { +// s->processes[i].attached = !i; +// } - switch (event) { - case CHR_EVENT_OPENED: - /* Start with first process attached, others detached */ - for (i = 0; i < s->process_num; i++) { - s->processes[i].attached = !i; - } +// s->c_cpu = gdb_first_attached_cpu(); +// s->g_cpu = s->c_cpu; - s->c_cpu = gdb_first_attached_cpu(); - s->g_cpu = s->c_cpu; +// vm_stop(RUN_STATE_PAUSED); +// replay_gdb_attached(); +// break; +// default: +// break; +// } +// } - vm_stop(RUN_STATE_PAUSED); - replay_gdb_attached(); - break; - default: - break; - } -} +// /* +// * In system-mode we stop the VM and wait to send the syscall packet +// * until notification that the CPU has stopped. This must be done +// * because if the packet is sent now the reply from the syscall +// * request could be received while the CPU is still in the running +// * state, which can cause packets to be dropped and state transition +// * 'T' packets to be sent while the syscall is still being processed. +// */ +// void gdb_syscall_handling(const char *syscall_packet) { +// vm_stop(RUN_STATE_DEBUG); +// qemu_cpu_kick(gdbserver_state.c_cpu); +// } -/* - * In system-mode we stop the VM and wait to send the syscall packet - * until notification that the CPU has stopped. This must be done - * because if the packet is sent now the reply from the syscall - * request could be received while the CPU is still in the running - * state, which can cause packets to be dropped and state transition - * 'T' packets to be sent while the syscall is still being processed. - */ -void gdb_syscall_handling(const char *syscall_packet) { - vm_stop(RUN_STATE_DEBUG); - qemu_cpu_kick(gdbserver_state.c_cpu); -} +// static void gdb_vm_state_change(void *opaque, bool running, RunState state) { +// CPUState *cpu = gdbserver_state.c_cpu; +// g_autoptr(GString) buf = g_string_new(NULL); +// g_autoptr(GString) tid = g_string_new(NULL); +// const char *type; +// int ret; -static void gdb_vm_state_change(void *opaque, bool running, RunState state) { - CPUState *cpu = gdbserver_state.c_cpu; - g_autoptr(GString) buf = g_string_new(NULL); - g_autoptr(GString) tid = g_string_new(NULL); - const char *type; - int ret; +// if (running || gdbserver_state.state == RS_INACTIVE) { +// return; +// } - if (running || gdbserver_state.state == RS_INACTIVE) { - return; - } +// /* Is there a GDB syscall waiting to be sent? */ +// if (gdb_handled_syscall()) { +// return; +// } - /* Is there a GDB syscall waiting to be sent? */ - if (gdb_handled_syscall()) { - return; - } +// if (cpu == NULL) { +// /* No process attached */ +// return; +// } - if (cpu == NULL) { - /* No process attached */ - return; - } +// if (!gdbserver_state.allow_stop_reply) { +// return; +// } - if (!gdbserver_state.allow_stop_reply) { - return; - } +// gdb_append_thread_id(cpu, tid); - gdb_append_thread_id(cpu, tid); +// switch (state) { +// case RUN_STATE_DEBUG: +// if (cpu->watchpoint_hit) { +// switch (cpu->watchpoint_hit->flags & BP_MEM_ACCESS) { +// case BP_MEM_READ: +// type = "r"; +// break; +// case BP_MEM_ACCESS: +// type = "a"; +// break; +// default: +// type = ""; +// break; +// } +// trace_gdbstub_hit_watchpoint(type, gdb_get_cpu_index(cpu), cpu->watchpoint_hit->vaddr); +// g_string_printf(buf, "T%02xthread:%s;%swatch:%" VADDR_PRIx ";", GDB_SIGNAL_TRAP, tid->str, type, +// cpu->watchpoint_hit->vaddr); +// cpu->watchpoint_hit = NULL; +// goto send_packet; +// } else { +// trace_gdbstub_hit_break(); +// } +// if (tcg_enabled()) { +// tb_flush(cpu); +// } +// ret = GDB_SIGNAL_TRAP; +// break; +// case RUN_STATE_PAUSED: +// trace_gdbstub_hit_paused(); +// ret = GDB_SIGNAL_INT; +// break; +// case RUN_STATE_SHUTDOWN: +// trace_gdbstub_hit_shutdown(); +// ret = GDB_SIGNAL_QUIT; +// break; +// case RUN_STATE_IO_ERROR: +// trace_gdbstub_hit_io_error(); +// ret = GDB_SIGNAL_STOP; +// break; +// case RUN_STATE_WATCHDOG: +// trace_gdbstub_hit_watchdog(); +// ret = GDB_SIGNAL_ALRM; +// break; +// case RUN_STATE_INTERNAL_ERROR: +// trace_gdbstub_hit_internal_error(); +// ret = GDB_SIGNAL_ABRT; +// break; +// case RUN_STATE_SAVE_VM: +// case RUN_STATE_RESTORE_VM: +// return; +// case RUN_STATE_FINISH_MIGRATE: +// ret = GDB_SIGNAL_XCPU; +// break; +// default: +// trace_gdbstub_hit_unknown(state); +// ret = GDB_SIGNAL_UNKNOWN; +// break; +// } +// gdb_set_stop_cpu(cpu); +// g_string_printf(buf, "T%02xthread:%s;", ret, tid->str); - switch (state) { - case RUN_STATE_DEBUG: - if (cpu->watchpoint_hit) { - switch (cpu->watchpoint_hit->flags & BP_MEM_ACCESS) { - case BP_MEM_READ: - type = "r"; - break; - case BP_MEM_ACCESS: - type = "a"; - break; - default: - type = ""; - break; - } - trace_gdbstub_hit_watchpoint(type, gdb_get_cpu_index(cpu), cpu->watchpoint_hit->vaddr); - g_string_printf(buf, "T%02xthread:%s;%swatch:%" VADDR_PRIx ";", GDB_SIGNAL_TRAP, tid->str, type, - cpu->watchpoint_hit->vaddr); - cpu->watchpoint_hit = NULL; - goto send_packet; - } else { - trace_gdbstub_hit_break(); - } - if (tcg_enabled()) { - tb_flush(cpu); - } - ret = GDB_SIGNAL_TRAP; - break; - case RUN_STATE_PAUSED: - trace_gdbstub_hit_paused(); - ret = GDB_SIGNAL_INT; - break; - case RUN_STATE_SHUTDOWN: - trace_gdbstub_hit_shutdown(); - ret = GDB_SIGNAL_QUIT; - break; - case RUN_STATE_IO_ERROR: - trace_gdbstub_hit_io_error(); - ret = GDB_SIGNAL_STOP; - break; - case RUN_STATE_WATCHDOG: - trace_gdbstub_hit_watchdog(); - ret = GDB_SIGNAL_ALRM; - break; - case RUN_STATE_INTERNAL_ERROR: - trace_gdbstub_hit_internal_error(); - ret = GDB_SIGNAL_ABRT; - break; - case RUN_STATE_SAVE_VM: - case RUN_STATE_RESTORE_VM: - return; - case RUN_STATE_FINISH_MIGRATE: - ret = GDB_SIGNAL_XCPU; - break; - default: - trace_gdbstub_hit_unknown(state); - ret = GDB_SIGNAL_UNKNOWN; - break; - } - gdb_set_stop_cpu(cpu); - g_string_printf(buf, "T%02xthread:%s;", ret, tid->str); +// send_packet: +// gdb_put_packet(buf->str); +// gdbserver_state.allow_stop_reply = false; -send_packet: - gdb_put_packet(buf->str); - gdbserver_state.allow_stop_reply = false; +// /* disable single step if it was enabled */ +// cpu_single_step(cpu, 0); +// } - /* disable single step if it was enabled */ - cpu_single_step(cpu, 0); -} +// static int gdb_monitor_write(Chardev *chr, const uint8_t *buf, int len) { +// g_autoptr(GString) hex_buf = g_string_new("O"); +// gdb_memtohex(hex_buf, buf, len); +// gdb_put_packet(hex_buf->str); +// return len; +// } -static int gdb_monitor_write(Chardev *chr, const uint8_t *buf, int len) { - g_autoptr(GString) hex_buf = g_string_new("O"); - gdb_memtohex(hex_buf, buf, len); - gdb_put_packet(hex_buf->str); - return len; -} +// static void gdb_monitor_open(Chardev *chr, ChardevBackend *backend, bool *be_opened, Error **errp) { +// *be_opened = false; +// } -static void gdb_monitor_open(Chardev *chr, ChardevBackend *backend, bool *be_opened, Error **errp) { - *be_opened = false; -} +// static void char_gdb_class_init(ObjectClass *oc, const void *data) { +// ChardevClass *cc = CHARDEV_CLASS(oc); -static void char_gdb_class_init(ObjectClass *oc, const void *data) { - ChardevClass *cc = CHARDEV_CLASS(oc); +// cc->internal = true; +// cc->open = gdb_monitor_open; +// cc->chr_write = gdb_monitor_write; +// } - cc->internal = true; - cc->open = gdb_monitor_open; - cc->chr_write = gdb_monitor_write; -} +// #define TYPE_CHARDEV_GDB "chardev-gdb" -#define TYPE_CHARDEV_GDB "chardev-gdb" - -static const TypeInfo char_gdb_type_info = { - .name = TYPE_CHARDEV_GDB, - .parent = TYPE_CHARDEV, - .class_init = char_gdb_class_init, -}; +// static const TypeInfo char_gdb_type_info = { +// .name = TYPE_CHARDEV_GDB, +// .parent = TYPE_CHARDEV, +// .class_init = char_gdb_class_init, +// }; static int gdb_chr_can_receive(void *opaque) { /* @@ -250,31 +215,31 @@ static void gdb_chr_receive(void *opaque, const uint8_t *buf, int size) { } } -static int find_cpu_clusters(Object *child, void *opaque) { - if (object_dynamic_cast(child, TYPE_CPU_CLUSTER)) { - GDBState *s = (GDBState *)opaque; - CPUClusterState *cluster = CPU_CLUSTER(child); - GDBProcess *process; +// static int find_cpu_clusters(Object *child, void *opaque) { +// if (object_dynamic_cast(child, TYPE_CPU_CLUSTER)) { +// GDBState *s = (GDBState *)opaque; +// CPUClusterState *cluster = CPU_CLUSTER(child); +// GDBProcess *process; - s->processes = g_renew(GDBProcess, s->processes, ++s->process_num); +// s->processes = g_renew(GDBProcess, s->processes, ++s->process_num); - process = &s->processes[s->process_num - 1]; +// process = &s->processes[s->process_num - 1]; - /* - * GDB process IDs -1 and 0 are reserved. To avoid subtle errors at - * runtime, we enforce here that the machine does not use a cluster ID - * that would lead to PID 0. - */ - assert(cluster->cluster_id != UINT32_MAX); - process->pid = cluster->cluster_id + 1; - process->attached = false; - process->target_xml = NULL; +// /* +// * GDB process IDs -1 and 0 are reserved. To avoid subtle errors at +// * runtime, we enforce here that the machine does not use a cluster ID +// * that would lead to PID 0. +// */ +// assert(cluster->cluster_id != UINT32_MAX); +// process->pid = cluster->cluster_id + 1; +// process->attached = false; +// process->target_xml = NULL; - return 0; - } +// return 0; +// } - return object_child_foreach(child, find_cpu_clusters, opaque); -} +// return object_child_foreach(child, find_cpu_clusters, opaque); +// } static int pid_order(const void *a, const void *b) { GDBProcess *pa = (GDBProcess *)a; @@ -289,160 +254,139 @@ static int pid_order(const void *a, const void *b) { } } -static void create_processes(GDBState *s) { - object_child_foreach(object_get_root(), find_cpu_clusters, s); +// static void create_processes(GDBState *s) { +// object_child_foreach(object_get_root(), find_cpu_clusters, s); - if (gdbserver_state.processes) { - /* Sort by PID */ - qsort(gdbserver_state.processes, gdbserver_state.process_num, sizeof(gdbserver_state.processes[0]), pid_order); - } +// if (gdbserver_state.processes) { +// /* Sort by PID */ +// qsort(gdbserver_state.processes, gdbserver_state.process_num, sizeof(gdbserver_state.processes[0]), pid_order); +// } - gdb_create_default_process(s); -} +// gdb_create_default_process(s); +// } -bool gdbserver_start(const char *device, Error **errp) { - Chardev *chr = NULL; - Chardev *mon_chr; - g_autoptr(GString) cs = g_string_new(device); +// bool gdbserver_start(const char *device, Error **errp) { +// Chardev *chr = NULL; +// Chardev *mon_chr; +// g_autoptr(GString) cs = g_string_new(device); - if (!first_cpu) { - error_setg(errp, - "gdbstub: meaningless to attach gdb to a " - "machine without any CPU."); - return false; - } +// if (!get_cpu()) { +// error_setg(errp, +// "gdbstub: meaningless to attach gdb to a " +// "machine without any CPU."); +// return false; +// } - if (!gdb_supports_guest_debug()) { - error_setg(errp, - "gdbstub: current accelerator doesn't " - "support guest debugging"); - return false; - } +// if (!gdb_supports_guest_debug()) { +// error_setg(errp, +// "gdbstub: current accelerator doesn't " +// "support guest debugging"); +// return false; +// } - if (cs->len == 0) { - error_setg(errp, "gdbstub: missing connection string"); - return false; - } +// if (cs->len == 0) { +// error_setg(errp, "gdbstub: missing connection string"); +// return false; +// } - trace_gdbstub_op_start(cs->str); +// trace_gdbstub_op_start(cs->str); - if (g_strcmp0(cs->str, "none") != 0) { - if (g_str_has_prefix(cs->str, "tcp:")) { - /* enforce required TCP attributes */ - g_string_append_printf(cs, ",wait=off,nodelay=on,server=on"); - } -#ifndef _WIN32 - else if (strcmp(device, "stdio") == 0) { - struct sigaction act; +// if (g_strcmp0(cs->str, "none") != 0) { +// if (g_str_has_prefix(cs->str, "tcp:")) { +// /* enforce required TCP attributes */ +// g_string_append_printf(cs, ",wait=off,nodelay=on,server=on"); +// } +// /* +// * FIXME: it's a bit weird to allow using a mux chardev here +// * and implicitly setup a monitor. We may want to break this. +// */ +// chr = qemu_chr_new_noreplay("gdb", cs->str, true, NULL); +// if (!chr) { +// error_setg(errp, "gdbstub: couldn't create chardev"); +// return false; +// } +// } - memset(&act, 0, sizeof(act)); - act.sa_handler = gdb_sigterm_handler; - sigaction(SIGINT, &act, NULL); - } -#endif - /* - * FIXME: it's a bit weird to allow using a mux chardev here - * and implicitly setup a monitor. We may want to break this. - */ - chr = qemu_chr_new_noreplay("gdb", cs->str, true, NULL); - if (!chr) { - error_setg(errp, "gdbstub: couldn't create chardev"); - return false; - } - } +// if (!gdbserver_state.init) { +// gdb_init_gdbserver_state(); - if (!gdbserver_state.init) { - gdb_init_gdbserver_state(); +// qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL); - qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL); +// /* Initialize a monitor terminal for gdb */ +// mon_chr = qemu_chardev_new(NULL, TYPE_CHARDEV_GDB, NULL, NULL, &error_abort); +// monitor_init_hmp(mon_chr, false, &error_abort); +// } else { +// qemu_chr_fe_deinit(&gdbserver_system_state.chr, true); +// mon_chr = gdbserver_system_state.mon_chr; +// reset_gdbserver_state(); +// } - /* Initialize a monitor terminal for gdb */ - mon_chr = qemu_chardev_new(NULL, TYPE_CHARDEV_GDB, NULL, NULL, &error_abort); - monitor_init_hmp(mon_chr, false, &error_abort); - } else { - qemu_chr_fe_deinit(&gdbserver_system_state.chr, true); - mon_chr = gdbserver_system_state.mon_chr; - reset_gdbserver_state(); - } +// create_processes(&gdbserver_state); - create_processes(&gdbserver_state); +// if (chr) { +// qemu_chr_fe_init(&gdbserver_system_state.chr, chr, &error_abort); +// qemu_chr_fe_set_handlers(&gdbserver_system_state.chr, gdb_chr_can_receive, gdb_chr_receive, gdb_chr_event, NULL, +// &gdbserver_state, NULL, true); +// } +// gdbserver_state.state = chr ? RS_IDLE : RS_INACTIVE; +// gdbserver_system_state.mon_chr = mon_chr; +// gdb_syscall_reset(); - if (chr) { - qemu_chr_fe_init(&gdbserver_system_state.chr, chr, &error_abort); - qemu_chr_fe_set_handlers(&gdbserver_system_state.chr, gdb_chr_can_receive, gdb_chr_receive, gdb_chr_event, NULL, - &gdbserver_state, NULL, true); - } - gdbserver_state.state = chr ? RS_IDLE : RS_INACTIVE; - gdbserver_system_state.mon_chr = mon_chr; - gdb_syscall_reset(); +// return true; +// } - return true; -} +// static void register_types(void) { type_register_static(&char_gdb_type_info); } -static void register_types(void) { type_register_static(&char_gdb_type_info); } - -type_init(register_types); +// type_init(register_types); /* Tell the remote gdb that the process has exited. */ -void gdb_exit(int code) { - char buf[4]; +// void gdb_exit(int code) { +// char buf[4]; - if (!gdbserver_state.init) { - return; - } +// if (!gdbserver_state.init) { +// return; +// } - trace_gdbstub_op_exiting((uint8_t)code); +// trace_gdbstub_op_exiting((uint8_t)code); - if (gdbserver_state.allow_stop_reply) { - snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code); - gdb_put_packet(buf); - gdbserver_state.allow_stop_reply = false; - } +// if (gdbserver_state.allow_stop_reply) { +// snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code); +// gdb_put_packet(buf); +// gdbserver_state.allow_stop_reply = false; +// } - qemu_chr_fe_deinit(&gdbserver_system_state.chr, true); -} +// qemu_chr_fe_deinit(&gdbserver_system_state.chr, true); +// } -void gdb_qemu_exit(int code) { qemu_system_shutdown_request_with_code(SHUTDOWN_CAUSE_GUEST_SHUTDOWN, code); } +// void gdb_qemu_exit(int code) { qemu_system_shutdown_request_with_code(SHUTDOWN_CAUSE_GUEST_SHUTDOWN, code); } /* * Memory access */ -static int phy_memory_mode; int gdb_target_memory_rw_debug(CPUState *cpu, hwaddr addr, uint8_t *buf, int len, bool is_write) { - if (phy_memory_mode) { - if (is_write) { - cpu_physical_memory_write(addr, buf, len); - } else { - cpu_physical_memory_read(addr, buf, len); - } - return 0; + if (is_write) { + cpu_physical_memory_write(addr, buf, len); + } else { + cpu_physical_memory_read(addr, buf, len); } - - if (cpu->cc->memory_rw_debug) { - return cpu->cc->memory_rw_debug(cpu, addr, buf, len, is_write); - } - - return cpu_memory_rw_debug(cpu, addr, buf, len, is_write); + return 0; } /* * cpu helpers */ -unsigned int gdb_get_max_cpus(void) { - MachineState *ms = MACHINE(qdev_get_machine()); - return ms->smp.max_cpus; -} +unsigned int gdb_get_max_cpus(void) { return 1; } -bool gdb_can_reverse(void) { return replay_mode == REPLAY_MODE_PLAY; } +bool gdb_can_reverse(void) { return false; } /* * Softmmu specific command helpers */ void gdb_handle_query_qemu_phy_mem_mode(GArray *params, void *ctx) { - g_string_printf(gdbserver_state.str_buf, "%d", phy_memory_mode); + g_string_printf(gdbserver_state.str_buf, "%d", 1); gdb_put_strbuf(); } @@ -451,12 +395,6 @@ void gdb_handle_set_qemu_phy_mem_mode(GArray *params, void *ctx) { gdb_put_packet("E22"); return; } - - if (!gdb_get_cmd_param(params, 0)->val_ul) { - phy_memory_mode = 0; - } else { - phy_memory_mode = 1; - } gdb_put_packet("OK"); } @@ -479,7 +417,10 @@ void gdb_handle_query_rcmd(GArray *params, void *ctx) { len = len / 2; gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 0)->data, len); g_byte_array_append(gdbserver_state.mem_buf, &zero, 1); - qemu_chr_be_write(gdbserver_system_state.mon_chr, gdbserver_state.mem_buf->data, gdbserver_state.mem_buf->len); + + // qemu_chr_be_write(gdbserver_system_state.mon_chr, gdbserver_state.mem_buf->data, gdbserver_state.mem_buf->len); + gdb_put_buffer(gdbserver_state.mem_buf->data, gdbserver_state.mem_buf->len); + gdb_put_packet("OK"); } @@ -491,7 +432,7 @@ void gdb_handle_query_attached(GArray *params, void *ctx) { gdb_put_packet("1"); void gdb_continue(void) { if (!runstate_needs_reset()) { - trace_gdbstub_op_continue(); + // trace_gdbstub_op_continue(); vm_start(); } } @@ -507,7 +448,7 @@ int gdb_continue_partial(char *newstates) { if (!runstate_needs_reset()) { bool step_requested = false; CPU_FOREACH(cpu) { - if (newstates[cpu->cpu_index] == 's') { + if (newstates[c->cpu_index] == 's') { step_requested = true; break; } @@ -518,19 +459,19 @@ int gdb_continue_partial(char *newstates) { } CPU_FOREACH(cpu) { - switch (newstates[cpu->cpu_index]) { + switch (newstates[c->cpu_index]) { case 0: case 1: break; /* nothing to do here */ case 's': - trace_gdbstub_op_stepping(cpu->cpu_index); - cpu_single_step(cpu, gdbserver_state.sstep_flags); - cpu_resume(cpu); + // 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(cpu->cpu_index); - cpu_resume(cpu); + // trace_gdbstub_op_continue_cpu(c->cpu_index); + cpu_resume(c); flag = 1; break; default: @@ -540,7 +481,7 @@ int gdb_continue_partial(char *newstates) { } } if (flag) { - qemu_clock_enable(QEMU_CLOCK_VIRTUAL, true); + qemu_clock_enable(); } return res; } @@ -563,37 +504,37 @@ int gdb_signal_to_target(int sig) { } } -/* - * Break/Watch point helpers - */ +bool runstate_is_running() { return true; } +void vm_stop(RunState rs) {} +void cpu_synchronize_state(CPUState *cpu) {} +void gdb_exit(int i) {}; +void gdb_qemu_exit(int i) {}; -bool gdb_supports_guest_debug(void) { - const AccelOpsClass *ops = cpus_get_accel(); - if (ops->supports_guest_debug) { - return ops->supports_guest_debug(); - } - return false; +CPUState *get_cpu() { return (CPUState *)1; } +CPUState *cpu_next(CPUState *cpu) { + if (cpu) + return (CPUState *)0; + else + return (CPUState *)1; } +void gdb_breakpoint_remove_all(CPUState *cs) {} +bool gdb_supports_guest_debug(void) { return false; } +int gdb_breakpoint_insert(CPUState *cs, int type, vaddr addr, vaddr len) { return 0; } +int gdb_breakpoint_remove(CPUState *cs, int type, vaddr addr, vaddr len) { return 0; } -int gdb_breakpoint_insert(CPUState *cs, int type, vaddr addr, vaddr len) { - const AccelOpsClass *ops = cpus_get_accel(); - if (ops->insert_breakpoint) { - return ops->insert_breakpoint(cs, type, addr, len); - } - return -ENOSYS; -} +void gdb_put_buffer(const uint8_t *buf, int len) {} -int gdb_breakpoint_remove(CPUState *cs, int type, vaddr addr, vaddr len) { - const AccelOpsClass *ops = cpus_get_accel(); - if (ops->remove_breakpoint) { - return ops->remove_breakpoint(cs, type, addr, len); - } - return -ENOSYS; -} +bool gdbserver_start(const char *device, Error **errp) { return false; } -void gdb_breakpoint_remove_all(CPUState *cs) { - const AccelOpsClass *ops = cpus_get_accel(); - if (ops->remove_all_breakpoints) { - ops->remove_all_breakpoints(cs); - } -} +static inline void cpu_physical_memory_write(hwaddr addr, const void *buf, hwaddr len) {} +static inline void cpu_physical_memory_read(hwaddr addr, const void *buf, hwaddr len) {} + +bool runstate_needs_reset(void) { return false; } +void vm_start() {} +bool vm_prepare_start(bool step_requested) { return true; } +void qemu_clock_enable() {} + +void gdb_handle_file_io(GArray *params, void *user_ctx) {} +void gdb_disable_syscalls(void) {} + +void cpu_single_step(CPUState *cpu, int enabled) {} \ No newline at end of file diff --git a/gdbstub/user-target.c b/gdbstub/user-target.c deleted file mode 100644 index 43231e6..0000000 --- a/gdbstub/user-target.c +++ /dev/null @@ -1,424 +0,0 @@ -/* - * Target specific user-mode handling - * - * Copyright (c) 2003-2005 Fabrice Bellard - * Copyright (c) 2022 Linaro Ltd - * - * SPDX-License-Identifier: LGPL-2.0-or-later - */ - -#include "qemu/osdep.h" -#include "exec/gdbstub.h" -#include "gdbstub/commands.h" -#include "qemu.h" -#include "internals.h" -#ifdef CONFIG_LINUX -#include "linux-user/loader.h" -#include "linux-user/qemu.h" -#endif - -/* - * Map target signal numbers to GDB protocol signal numbers and vice - * versa. For user emulation's currently supported systems, we can - * assume most signals are defined. - */ - -static int gdb_signal_table[] = { - 0, - TARGET_SIGHUP, - TARGET_SIGINT, - TARGET_SIGQUIT, - TARGET_SIGILL, - TARGET_SIGTRAP, - TARGET_SIGABRT, - -1, /* SIGEMT */ - TARGET_SIGFPE, - TARGET_SIGKILL, - TARGET_SIGBUS, - TARGET_SIGSEGV, - TARGET_SIGSYS, - TARGET_SIGPIPE, - TARGET_SIGALRM, - TARGET_SIGTERM, - TARGET_SIGURG, - TARGET_SIGSTOP, - TARGET_SIGTSTP, - TARGET_SIGCONT, - TARGET_SIGCHLD, - TARGET_SIGTTIN, - TARGET_SIGTTOU, - TARGET_SIGIO, - TARGET_SIGXCPU, - TARGET_SIGXFSZ, - TARGET_SIGVTALRM, - TARGET_SIGPROF, - TARGET_SIGWINCH, - -1, /* SIGLOST */ - TARGET_SIGUSR1, - TARGET_SIGUSR2, -#ifdef TARGET_SIGPWR - TARGET_SIGPWR, -#else - -1, -#endif - -1, /* SIGPOLL */ - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, -#ifdef __SIGRTMIN - __SIGRTMIN + 1, - __SIGRTMIN + 2, - __SIGRTMIN + 3, - __SIGRTMIN + 4, - __SIGRTMIN + 5, - __SIGRTMIN + 6, - __SIGRTMIN + 7, - __SIGRTMIN + 8, - __SIGRTMIN + 9, - __SIGRTMIN + 10, - __SIGRTMIN + 11, - __SIGRTMIN + 12, - __SIGRTMIN + 13, - __SIGRTMIN + 14, - __SIGRTMIN + 15, - __SIGRTMIN + 16, - __SIGRTMIN + 17, - __SIGRTMIN + 18, - __SIGRTMIN + 19, - __SIGRTMIN + 20, - __SIGRTMIN + 21, - __SIGRTMIN + 22, - __SIGRTMIN + 23, - __SIGRTMIN + 24, - __SIGRTMIN + 25, - __SIGRTMIN + 26, - __SIGRTMIN + 27, - __SIGRTMIN + 28, - __SIGRTMIN + 29, - __SIGRTMIN + 30, - __SIGRTMIN + 31, - -1, /* SIGCANCEL */ - __SIGRTMIN, - __SIGRTMIN + 32, - __SIGRTMIN + 33, - __SIGRTMIN + 34, - __SIGRTMIN + 35, - __SIGRTMIN + 36, - __SIGRTMIN + 37, - __SIGRTMIN + 38, - __SIGRTMIN + 39, - __SIGRTMIN + 40, - __SIGRTMIN + 41, - __SIGRTMIN + 42, - __SIGRTMIN + 43, - __SIGRTMIN + 44, - __SIGRTMIN + 45, - __SIGRTMIN + 46, - __SIGRTMIN + 47, - __SIGRTMIN + 48, - __SIGRTMIN + 49, - __SIGRTMIN + 50, - __SIGRTMIN + 51, - __SIGRTMIN + 52, - __SIGRTMIN + 53, - __SIGRTMIN + 54, - __SIGRTMIN + 55, - __SIGRTMIN + 56, - __SIGRTMIN + 57, - __SIGRTMIN + 58, - __SIGRTMIN + 59, - __SIGRTMIN + 60, - __SIGRTMIN + 61, - __SIGRTMIN + 62, - __SIGRTMIN + 63, - __SIGRTMIN + 64, - __SIGRTMIN + 65, - __SIGRTMIN + 66, - __SIGRTMIN + 67, - __SIGRTMIN + 68, - __SIGRTMIN + 69, - __SIGRTMIN + 70, - __SIGRTMIN + 71, - __SIGRTMIN + 72, - __SIGRTMIN + 73, - __SIGRTMIN + 74, - __SIGRTMIN + 75, - __SIGRTMIN + 76, - __SIGRTMIN + 77, - __SIGRTMIN + 78, - __SIGRTMIN + 79, - __SIGRTMIN + 80, - __SIGRTMIN + 81, - __SIGRTMIN + 82, - __SIGRTMIN + 83, - __SIGRTMIN + 84, - __SIGRTMIN + 85, - __SIGRTMIN + 86, - __SIGRTMIN + 87, - __SIGRTMIN + 88, - __SIGRTMIN + 89, - __SIGRTMIN + 90, - __SIGRTMIN + 91, - __SIGRTMIN + 92, - __SIGRTMIN + 93, - __SIGRTMIN + 94, - __SIGRTMIN + 95, - -1, /* SIGINFO */ - -1, /* UNKNOWN */ - -1, /* DEFAULT */ - -1, - -1, - -1, - -1, - -1, - -1 -#endif -}; - -int gdb_signal_to_target(int sig) -{ - if (sig < ARRAY_SIZE(gdb_signal_table)) { - return gdb_signal_table[sig]; - } else { - return -1; - } -} - -int gdb_target_signal_to_gdb(int sig) -{ - int i; - for (i = 0; i < ARRAY_SIZE(gdb_signal_table); i++) { - if (gdb_signal_table[i] == sig) { - return i; - } - } - return GDB_SIGNAL_UNKNOWN; -} - -int gdb_get_cpu_index(CPUState *cpu) -{ - TaskState *ts = get_task_state(cpu); - return ts ? ts->ts_tid : -1; -} - -/* - * User-mode specific command helpers - */ - -void gdb_handle_query_offsets(GArray *params, void *user_ctx) -{ - TaskState *ts; - - ts = get_task_state(gdbserver_state.c_cpu); - g_string_printf(gdbserver_state.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); - gdb_put_strbuf(); -} - -#if defined(CONFIG_LINUX) -/* Partial user only duplicate of helper in gdbstub.c */ -static inline int target_memory_rw_debug(CPUState *cpu, target_ulong addr, - uint8_t *buf, int len, bool is_write) -{ - if (cpu->cc->memory_rw_debug) { - return cpu->cc->memory_rw_debug(cpu, addr, buf, len, is_write); - } - return cpu_memory_rw_debug(cpu, addr, buf, len, is_write); -} - -void gdb_handle_query_xfer_auxv(GArray *params, void *user_ctx) -{ - TaskState *ts; - unsigned long offset, len, saved_auxv, auxv_len; - - if (params->len < 2) { - gdb_put_packet("E22"); - return; - } - - offset = gdb_get_cmd_param(params, 0)->val_ul; - len = gdb_get_cmd_param(params, 1)->val_ul; - ts = get_task_state(gdbserver_state.c_cpu); - saved_auxv = ts->info->saved_auxv; - auxv_len = ts->info->auxv_len; - - if (offset >= auxv_len) { - gdb_put_packet("E00"); - return; - } - - if (len > (MAX_PACKET_LENGTH - 5) / 2) { - len = (MAX_PACKET_LENGTH - 5) / 2; - } - - if (len < auxv_len - offset) { - g_string_assign(gdbserver_state.str_buf, "m"); - } else { - g_string_assign(gdbserver_state.str_buf, "l"); - len = auxv_len - offset; - } - - g_byte_array_set_size(gdbserver_state.mem_buf, len); - if (target_memory_rw_debug(gdbserver_state.g_cpu, saved_auxv + offset, - gdbserver_state.mem_buf->data, len, false)) { - gdb_put_packet("E14"); - return; - } - - gdb_memtox(gdbserver_state.str_buf, - (const char *)gdbserver_state.mem_buf->data, len); - gdb_put_packet_binary(gdbserver_state.str_buf->str, - gdbserver_state.str_buf->len, true); -} -#endif - -static const char *get_filename_param(GArray *params, int i) -{ - const char *hex_filename = gdb_get_cmd_param(params, i)->data; - gdb_hextomem(gdbserver_state.mem_buf, hex_filename, - strlen(hex_filename) / 2); - g_byte_array_append(gdbserver_state.mem_buf, (const guint8 *)"", 1); - return (const char *)gdbserver_state.mem_buf->data; -} - -static void hostio_reply_with_data(const void *buf, size_t n) -{ - g_string_printf(gdbserver_state.str_buf, "F%zx;", n); - gdb_memtox(gdbserver_state.str_buf, buf, n); - gdb_put_packet_binary(gdbserver_state.str_buf->str, - gdbserver_state.str_buf->len, true); -} - -void gdb_handle_v_file_open(GArray *params, void *user_ctx) -{ - const char *filename = get_filename_param(params, 0); - uint64_t flags = gdb_get_cmd_param(params, 1)->val_ull; - uint64_t mode = gdb_get_cmd_param(params, 2)->val_ull; - -#ifdef CONFIG_LINUX - int fd = do_guest_openat(cpu_env(gdbserver_state.g_cpu), 0, filename, - flags, mode, false); -#else - int fd = open(filename, flags, mode); -#endif - if (fd < 0) { - g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno); - } else { - g_string_printf(gdbserver_state.str_buf, "F%x", fd); - } - gdb_put_strbuf(); -} - -void gdb_handle_v_file_close(GArray *params, void *user_ctx) -{ - int fd = gdb_get_cmd_param(params, 0)->val_ul; - - if (close(fd) == -1) { - g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno); - gdb_put_strbuf(); - return; - } - - gdb_put_packet("F00"); -} - -void gdb_handle_v_file_pread(GArray *params, void *user_ctx) -{ - int fd = gdb_get_cmd_param(params, 0)->val_ul; - size_t count = gdb_get_cmd_param(params, 1)->val_ull; - off_t offset = gdb_get_cmd_param(params, 2)->val_ull; - - size_t bufsiz = MIN(count, BUFSIZ); - g_autofree char *buf = g_try_malloc(bufsiz); - if (buf == NULL) { - gdb_put_packet("E12"); - return; - } - - ssize_t n = pread(fd, buf, bufsiz, offset); - if (n < 0) { - g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno); - gdb_put_strbuf(); - return; - } - hostio_reply_with_data(buf, n); -} - -void gdb_handle_v_file_readlink(GArray *params, void *user_ctx) -{ - const char *filename = get_filename_param(params, 0); - - g_autofree char *buf = g_try_malloc(BUFSIZ); - if (buf == NULL) { - gdb_put_packet("E12"); - return; - } - -#ifdef CONFIG_LINUX - ssize_t n = do_guest_readlink(filename, buf, BUFSIZ); -#else - ssize_t n = readlink(filename, buf, BUFSIZ); -#endif - if (n < 0) { - g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno); - gdb_put_strbuf(); - return; - } - hostio_reply_with_data(buf, n); -} - -void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx) -{ - uint32_t pid = gdb_get_cmd_param(params, 0)->val_ul; - uint32_t offset = gdb_get_cmd_param(params, 1)->val_ul; - uint32_t length = gdb_get_cmd_param(params, 2)->val_ul; - - GDBProcess *process = gdb_get_process(pid); - if (!process) { - gdb_put_packet("E00"); - return; - } - - CPUState *cpu = gdb_get_first_cpu_in_process(process); - if (!cpu) { - gdb_put_packet("E00"); - return; - } - - TaskState *ts = get_task_state(cpu); - if (!ts || !ts->bprm || !ts->bprm->filename) { - gdb_put_packet("E00"); - return; - } - - size_t total_length = strlen(ts->bprm->filename); - if (offset > total_length) { - gdb_put_packet("E00"); - return; - } - if (offset + length > total_length) { - length = total_length - offset; - } - - g_string_printf(gdbserver_state.str_buf, "l%.*s", length, - ts->bprm->filename + offset); - gdb_put_strbuf(); -} - -int gdb_target_sigtrap(void) -{ - return TARGET_SIGTRAP; -}