X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/5d0e57bd6889af947027da25ef6dd7b772ebe5b9..31e5784a0d822269dc2674495f9f17e3ee0fb68f:/gdbstub.c diff --git a/gdbstub.c b/gdbstub.c index e9b23ff3f9..171e150950 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1,6 +1,10 @@ /* * gdb server stub * + * This implements a subset of the remote protocol as described in: + * + * https://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html + * * Copyright (c) 2003-2005 Fabrice Bellard * * This library is free software; you can redistribute it and/or @@ -15,6 +19,8 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . + * + * SPDX-License-Identifier: LGPL-2.0+ */ #include "qemu/osdep.h" @@ -34,6 +40,7 @@ #include "sysemu/sysemu.h" #include "exec/gdbstub.h" #include "hw/cpu/cluster.h" +#include "hw/boards.h" #endif #define MAX_PACKET_LENGTH 4096 @@ -41,6 +48,7 @@ #include "qemu/sockets.h" #include "sysemu/hw_accel.h" #include "sysemu/kvm.h" +#include "sysemu/runstate.h" #include "hw/semihosting/semihost.h" #include "exec/exec-all.h" @@ -50,11 +58,27 @@ #define GDB_ATTACHED "1" #endif +#ifndef CONFIG_USER_ONLY +static int phy_memory_mode; +#endif + static inline int target_memory_rw_debug(CPUState *cpu, target_ulong addr, uint8_t *buf, int len, bool is_write) { - CPUClass *cc = CPU_GET_CLASS(cpu); + CPUClass *cc; + +#ifndef CONFIG_USER_ONLY + if (phy_memory_mode) { + if (is_write) { + cpu_physical_memory_write(addr, buf, len); + } else { + cpu_physical_memory_read(addr, buf, len); + } + return 0; + } +#endif + cc = CPU_GET_CLASS(cpu); if (cc->memory_rw_debug) { return cc->memory_rw_debug(cpu, addr, buf, len, is_write); } @@ -295,8 +319,8 @@ static int gdb_signal_to_target (int sig) typedef struct GDBRegisterState { int base_reg; int num_regs; - gdb_reg_cb get_reg; - gdb_reg_cb set_reg; + gdb_get_reg_cb get_reg; + gdb_set_reg_cb set_reg; const char *xml; struct GDBRegisterState *next; } GDBRegisterState; @@ -318,6 +342,7 @@ enum RSState { 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 */ @@ -326,8 +351,7 @@ typedef struct GDBState { int line_buf_index; int line_sum; /* running checksum */ int line_csum; /* checksum at the end of the packet */ - uint8_t last_packet[MAX_PACKET_LENGTH + 4]; - int last_packet_len; + GByteArray *last_packet; int signal; #ifdef CONFIG_USER_ONLY int fd; @@ -341,6 +365,8 @@ typedef struct GDBState { int process_num; char syscall_buf[256]; gdb_syscall_complete_cb current_syscall_cb; + GString *str_buf; + GByteArray *mem_buf; } GDBState; /* By default use no IRQs and no timers while single stepping so as to @@ -348,7 +374,26 @@ typedef struct GDBState { */ static int sstep_flags = SSTEP_ENABLE|SSTEP_NOIRQ|SSTEP_NOTIMER; -static GDBState *gdbserver_state; +static GDBState gdbserver_state; + +static void 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); +} + +#ifndef CONFIG_USER_ONLY +static void reset_gdbserver_state(void) +{ + g_free(gdbserver_state.processes); + gdbserver_state.processes = NULL; + gdbserver_state.process_num = 0; +} +#endif bool gdb_has_xml; @@ -356,21 +401,21 @@ bool gdb_has_xml; /* XXX: This is not thread safe. Do we care? */ static int gdbserver_fd = -1; -static int get_char(GDBState *s) +static int get_char(void) { uint8_t ch; int ret; for(;;) { - ret = qemu_recv(s->fd, &ch, 1, 0); + ret = qemu_recv(gdbserver_state.fd, &ch, 1, 0); if (ret < 0) { if (errno == ECONNRESET) - s->fd = -1; + gdbserver_state.fd = -1; if (errno != EINTR) return -1; } else if (ret == 0) { - close(s->fd); - s->fd = -1; + close(gdbserver_state.fd); + gdbserver_state.fd = -1; return -1; } else { break; @@ -401,18 +446,18 @@ int use_gdb_syscalls(void) /* -semihosting-config target=auto */ /* On the first call check if gdb is connected and remember. */ if (gdb_syscall_mode == GDB_SYS_UNKNOWN) { - gdb_syscall_mode = (gdbserver_state ? GDB_SYS_ENABLED - : GDB_SYS_DISABLED); + gdb_syscall_mode = gdbserver_state.init ? + GDB_SYS_ENABLED : GDB_SYS_DISABLED; } return gdb_syscall_mode == GDB_SYS_ENABLED; } /* Resume execution. */ -static inline void gdb_continue(GDBState *s) +static inline void gdb_continue(void) { #ifdef CONFIG_USER_ONLY - s->running_state = 1; + gdbserver_state.running_state = 1; trace_gdbstub_op_continue(); #else if (!runstate_needs_reset()) { @@ -426,7 +471,7 @@ static inline void gdb_continue(GDBState *s) * Resume execution, per CPU actions. For user-mode emulation it's * equivalent to gdb_continue. */ -static int gdb_continue_partial(GDBState *s, char *newstates) +static int gdb_continue_partial(char *newstates) { CPUState *cpu; int res = 0; @@ -441,7 +486,7 @@ static int gdb_continue_partial(GDBState *s, char *newstates) cpu_single_step(cpu, sstep_flags); } } - s->running_state = 1; + gdbserver_state.running_state = 1; #else int flag = 0; @@ -479,13 +524,13 @@ static int gdb_continue_partial(GDBState *s, char *newstates) return res; } -static void put_buffer(GDBState *s, const uint8_t *buf, int len) +static void put_buffer(const uint8_t *buf, int len) { #ifdef CONFIG_USER_ONLY int ret; while (len > 0) { - ret = send(s->fd, buf, len, 0); + ret = send(gdbserver_state.fd, buf, len, 0); if (ret < 0) { if (errno != EINTR) return; @@ -497,7 +542,7 @@ static void put_buffer(GDBState *s, const uint8_t *buf, int len) #else /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(&s->chr, buf, len); + qemu_chr_fe_write_all(&gdbserver_state.chr, buf, len); #endif } @@ -522,25 +567,24 @@ static inline int tohex(int v) } /* writes 2*len+1 bytes in buf */ -static void memtohex(char *buf, const uint8_t *mem, int len) +static void memtohex(GString *buf, const uint8_t *mem, int len) { int i, c; - char *q; - q = buf; for(i = 0; i < len; i++) { c = mem[i]; - *q++ = tohex(c >> 4); - *q++ = tohex(c & 0xf); + g_string_append_c(buf, tohex(c >> 4)); + g_string_append_c(buf, tohex(c & 0xf)); } - *q = '\0'; + g_string_append_c(buf, '\0'); } -static void hextomem(uint8_t *mem, const char *buf, int len) +static void hextomem(GByteArray *mem, const char *buf, int len) { int i; for(i = 0; i < len; i++) { - mem[i] = (fromhex(buf[0]) << 4) | fromhex(buf[1]); + guint8 byte = fromhex(buf[0]) << 4 | fromhex(buf[1]); + g_byte_array_append(mem, &byte, 1); buf += 2; } } @@ -579,33 +623,35 @@ static void hexdump(const char *buf, int len, } /* return -1 if error, 0 if OK */ -static int put_packet_binary(GDBState *s, const char *buf, int len, bool dump) +static int put_packet_binary(const char *buf, int len, bool dump) { int csum, i; - uint8_t *p; + uint8_t footer[3]; if (dump && trace_event_get_state_backends(TRACE_GDBSTUB_IO_BINARYREPLY)) { hexdump(buf, len, trace_gdbstub_io_binaryreply); } for(;;) { - p = s->last_packet; - *(p++) = '$'; - memcpy(p, buf, len); - p += len; + 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]; } - *(p++) = '#'; - *(p++) = tohex((csum >> 4) & 0xf); - *(p++) = tohex((csum) & 0xf); + footer[0] = '#'; + footer[1] = tohex((csum >> 4) & 0xf); + footer[2] = tohex((csum) & 0xf); + g_byte_array_append(gdbserver_state.last_packet, footer, 3); - s->last_packet_len = p - s->last_packet; - put_buffer(s, (uint8_t *)s->last_packet, s->last_packet_len); + put_buffer(gdbserver_state.last_packet->data, + gdbserver_state.last_packet->len); #ifdef CONFIG_USER_ONLY - i = get_char(s); + i = get_char(); if (i < 0) return -1; if (i == '+') @@ -618,65 +664,69 @@ static int put_packet_binary(GDBState *s, const char *buf, int len, bool dump) } /* return -1 if error, 0 if OK */ -static int put_packet(GDBState *s, const char *buf) +static int put_packet(const char *buf) { trace_gdbstub_io_reply(buf); - return put_packet_binary(s, buf, strlen(buf), false); + return put_packet_binary(buf, strlen(buf), false); +} + +static void put_strbuf(void) +{ + put_packet(gdbserver_state.str_buf->str); } /* Encode data using the encoding for 'x' packets. */ -static int memtox(char *buf, const char *mem, int len) +static void memtox(GString *buf, const char *mem, int len) { - char *p = buf; char c; while (len--) { c = *(mem++); switch (c) { case '#': case '$': case '*': case '}': - *(p++) = '}'; - *(p++) = c ^ 0x20; + g_string_append_c(buf, '}'); + g_string_append_c(buf, c ^ 0x20); break; default: - *(p++) = c; + g_string_append_c(buf, c); break; } } - return p - buf; } -static uint32_t gdb_get_cpu_pid(const GDBState *s, CPUState *cpu) +static uint32_t gdb_get_cpu_pid(CPUState *cpu) { /* TODO: In user mode, we should use the task state PID */ if (cpu->cluster_index == UNASSIGNED_CLUSTER_INDEX) { /* Return the default process' PID */ - return s->processes[s->process_num - 1].pid; + int index = gdbserver_state.process_num - 1; + return gdbserver_state.processes[index].pid; } return cpu->cluster_index + 1; } -static GDBProcess *gdb_get_process(const GDBState *s, uint32_t pid) +static GDBProcess *gdb_get_process(uint32_t pid) { int i; if (!pid) { /* 0 means any process, we take the first one */ - return &s->processes[0]; + return &gdbserver_state.processes[0]; } - for (i = 0; i < s->process_num; i++) { - if (s->processes[i].pid == pid) { - return &s->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; } -static GDBProcess *gdb_get_cpu_process(const GDBState *s, CPUState *cpu) +static GDBProcess *gdb_get_cpu_process(CPUState *cpu) { - return gdb_get_process(s, gdb_get_cpu_pid(s, cpu)); + return gdb_get_process(gdb_get_cpu_pid(cpu)); } static CPUState *find_cpu(uint32_t thread_id) @@ -692,13 +742,12 @@ static CPUState *find_cpu(uint32_t thread_id) return NULL; } -static CPUState *get_first_cpu_in_process(const GDBState *s, - GDBProcess *process) +static CPUState *get_first_cpu_in_process(GDBProcess *process) { CPUState *cpu; CPU_FOREACH(cpu) { - if (gdb_get_cpu_pid(s, cpu) == process->pid) { + if (gdb_get_cpu_pid(cpu) == process->pid) { return cpu; } } @@ -706,13 +755,13 @@ static CPUState *get_first_cpu_in_process(const GDBState *s, return NULL; } -static CPUState *gdb_next_cpu_in_process(const GDBState *s, CPUState *cpu) +static CPUState *gdb_next_cpu_in_process(CPUState *cpu) { - uint32_t pid = gdb_get_cpu_pid(s, cpu); + uint32_t pid = gdb_get_cpu_pid(cpu); cpu = CPU_NEXT(cpu); while (cpu) { - if (gdb_get_cpu_pid(s, cpu) == pid) { + if (gdb_get_cpu_pid(cpu) == pid) { break; } @@ -723,12 +772,12 @@ static CPUState *gdb_next_cpu_in_process(const GDBState *s, CPUState *cpu) } /* Return the cpu following @cpu, while ignoring unattached processes. */ -static CPUState *gdb_next_attached_cpu(const GDBState *s, CPUState *cpu) +static CPUState *gdb_next_attached_cpu(CPUState *cpu) { cpu = CPU_NEXT(cpu); while (cpu) { - if (gdb_get_cpu_process(s, cpu)->attached) { + if (gdb_get_cpu_process(cpu)->attached) { break; } @@ -739,29 +788,29 @@ static CPUState *gdb_next_attached_cpu(const GDBState *s, CPUState *cpu) } /* Return the first attached cpu */ -static CPUState *gdb_first_attached_cpu(const GDBState *s) +static CPUState *gdb_first_attached_cpu(void) { CPUState *cpu = first_cpu; - GDBProcess *process = gdb_get_cpu_process(s, cpu); + GDBProcess *process = gdb_get_cpu_process(cpu); if (!process->attached) { - return gdb_next_attached_cpu(s, cpu); + return gdb_next_attached_cpu(cpu); } return cpu; } -static CPUState *gdb_get_cpu(const GDBState *s, uint32_t pid, uint32_t tid) +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(s); + return gdb_first_attached_cpu(); } else if (pid && !tid) { /* any thread in a specific process */ - process = gdb_get_process(s, pid); + process = gdb_get_process(pid); if (process == NULL) { return NULL; @@ -771,7 +820,7 @@ static CPUState *gdb_get_cpu(const GDBState *s, uint32_t pid, uint32_t tid) return NULL; } - return get_first_cpu_in_process(s, process); + return get_first_cpu_in_process(process); } else { /* a specific thread */ cpu = find_cpu(tid); @@ -780,7 +829,7 @@ static CPUState *gdb_get_cpu(const GDBState *s, uint32_t pid, uint32_t tid) return NULL; } - process = gdb_get_cpu_process(s, cpu); + process = gdb_get_cpu_process(cpu); if (pid && process->pid != pid) { return NULL; @@ -794,13 +843,13 @@ static CPUState *gdb_get_cpu(const GDBState *s, uint32_t pid, uint32_t tid) } } -static const char *get_feature_xml(const GDBState *s, const char *p, - const char **newp, GDBProcess *process) +static const char *get_feature_xml(const char *p, const char **newp, + GDBProcess *process) { size_t len; int i; const char *name; - CPUState *cpu = get_first_cpu_in_process(s, process); + CPUState *cpu = get_first_cpu_in_process(process); CPUClass *cc = CPU_GET_CLASS(cpu); len = 0; @@ -857,19 +906,19 @@ static const char *get_feature_xml(const GDBState *s, const char *p, return name ? xml_builtin[i][1] : NULL; } -static int gdb_read_register(CPUState *cpu, uint8_t *mem_buf, int reg) +static int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg) { CPUClass *cc = CPU_GET_CLASS(cpu); CPUArchState *env = cpu->env_ptr; GDBRegisterState *r; if (reg < cc->gdb_num_core_regs) { - return cc->gdb_read_register(cpu, mem_buf, reg); + return cc->gdb_read_register(cpu, buf, reg); } for (r = cpu->gdb_regs; r; r = r->next) { if (r->base_reg <= reg && reg < r->base_reg + r->num_regs) { - return r->get_reg(env, mem_buf, reg - r->base_reg); + return r->get_reg(env, buf, reg - r->base_reg); } } return 0; @@ -900,7 +949,7 @@ static int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg) */ void gdb_register_coprocessor(CPUState *cpu, - gdb_reg_cb get_reg, gdb_reg_cb set_reg, + gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg, int num_regs, const char *xml, int g_pos) { GDBRegisterState *s; @@ -960,7 +1009,7 @@ static int gdb_breakpoint_insert(int type, target_ulong addr, target_ulong len) int err = 0; if (kvm_enabled()) { - return kvm_insert_breakpoint(gdbserver_state->c_cpu, addr, len, type); + return kvm_insert_breakpoint(gdbserver_state.c_cpu, addr, len, type); } switch (type) { @@ -997,7 +1046,7 @@ static int gdb_breakpoint_remove(int type, target_ulong addr, target_ulong len) int err = 0; if (kvm_enabled()) { - return kvm_remove_breakpoint(gdbserver_state->c_cpu, addr, len, type); + return kvm_remove_breakpoint(gdbserver_state.c_cpu, addr, len, type); } switch (type) { @@ -1035,13 +1084,13 @@ static inline void gdb_cpu_breakpoint_remove_all(CPUState *cpu) #endif } -static void gdb_process_breakpoint_remove_all(const GDBState *s, GDBProcess *p) +static void gdb_process_breakpoint_remove_all(GDBProcess *p) { - CPUState *cpu = get_first_cpu_in_process(s, p); + CPUState *cpu = get_first_cpu_in_process(p); while (cpu) { gdb_cpu_breakpoint_remove_all(cpu); - cpu = gdb_next_cpu_in_process(s, cpu); + cpu = gdb_next_cpu_in_process(cpu); } } @@ -1050,7 +1099,7 @@ static void gdb_breakpoint_remove_all(void) CPUState *cpu; if (kvm_enabled()) { - kvm_remove_all_breakpoints(gdbserver_state->c_cpu); + kvm_remove_all_breakpoints(gdbserver_state.c_cpu); return; } @@ -1059,25 +1108,22 @@ static void gdb_breakpoint_remove_all(void) } } -static void gdb_set_cpu_pc(GDBState *s, target_ulong pc) +static void gdb_set_cpu_pc(target_ulong pc) { - CPUState *cpu = s->c_cpu; + CPUState *cpu = gdbserver_state.c_cpu; cpu_synchronize_state(cpu); cpu_set_pc(cpu, pc); } -static char *gdb_fmt_thread_id(const GDBState *s, CPUState *cpu, - char *buf, size_t buf_size) +static void gdb_append_thread_id(CPUState *cpu, GString *buf) { - if (s->multiprocess) { - snprintf(buf, buf_size, "p%02x.%02x", - gdb_get_cpu_pid(s, cpu), cpu_gdb_index(cpu)); + if (gdbserver_state.multiprocess) { + g_string_append_printf(buf, "p%02x.%02x", + gdb_get_cpu_pid(cpu), cpu_gdb_index(cpu)); } else { - snprintf(buf, buf_size, "%02x", cpu_gdb_index(cpu)); + g_string_append_printf(buf, "%02x", cpu_gdb_index(cpu)); } - - return buf; } typedef enum GDBThreadIdKind { @@ -1134,20 +1180,12 @@ static GDBThreadIdKind read_thread_id(const char *buf, const char **end_buf, return GDB_ONE_THREAD; } -static int is_query_packet(const char *p, const char *query, char separator) -{ - unsigned int query_len = strlen(query); - - return strncmp(p, query, query_len) == 0 && - (p[query_len] == '\0' || p[query_len] == separator); -} - /** * gdb_handle_vcont - Parses and handles a vCont packet. * returns -ENOTSUP if a command is unsupported, -EINVAL or -ERANGE if there is * a format error, 0 on success. */ -static int gdb_handle_vcont(GDBState *s, const char *p) +static int gdb_handle_vcont(const char *p) { int res, signal = 0; char cur_action; @@ -1163,6 +1201,9 @@ static int gdb_handle_vcont(GDBState *s, const char *p) CPU_FOREACH(cpu) { max_cpus = max_cpus <= cpu->cpu_index ? cpu->cpu_index + 1 : max_cpus; } +#else + MachineState *ms = MACHINE(qdev_get_machine()); + unsigned int max_cpus = ms->smp.max_cpus; #endif /* uninitialised CPUs stay 0 */ newstates = g_new0(char, max_cpus); @@ -1219,36 +1260,36 @@ static int gdb_handle_vcont(GDBState *s, const char *p) goto out; case GDB_ALL_PROCESSES: - cpu = gdb_first_attached_cpu(s); + cpu = gdb_first_attached_cpu(); while (cpu) { if (newstates[cpu->cpu_index] == 1) { newstates[cpu->cpu_index] = cur_action; } - cpu = gdb_next_attached_cpu(s, cpu); + cpu = gdb_next_attached_cpu(cpu); } break; case GDB_ALL_THREADS: - process = gdb_get_process(s, pid); + process = gdb_get_process(pid); if (!process->attached) { res = -EINVAL; goto out; } - cpu = get_first_cpu_in_process(s, process); + cpu = get_first_cpu_in_process(process); while (cpu) { if (newstates[cpu->cpu_index] == 1) { newstates[cpu->cpu_index] = cur_action; } - cpu = gdb_next_cpu_in_process(s, cpu); + cpu = gdb_next_cpu_in_process(cpu); } break; case GDB_ONE_THREAD: - cpu = gdb_get_cpu(s, pid, tid); + cpu = gdb_get_cpu(pid, tid); /* invalid CPU/thread specified */ if (!cpu) { @@ -1263,8 +1304,8 @@ static int gdb_handle_vcont(GDBState *s, const char *p) break; } } - s->signal = signal; - gdb_continue_partial(s, newstates); + gdbserver_state.signal = signal; + gdb_continue_partial(newstates); out: g_free(newstates); @@ -1373,11 +1414,8 @@ static int cmd_parse_params(const char *data, const char *schema, } typedef struct GdbCmdContext { - GDBState *s; GdbCmdVariant *params; int num_params; - uint8_t mem_buf[MAX_PACKET_LENGTH]; - char str_buf[MAX_PACKET_LENGTH + 1]; } GdbCmdContext; typedef void (*GdbCmdHandler)(GdbCmdContext *gdb_ctx, void *user_ctx); @@ -1417,7 +1455,7 @@ static inline int startswith(const char *string, const char *pattern) return !strncmp(string, pattern, strlen(pattern)); } -static int process_string_cmd(GDBState *s, void *user_ctx, const char *data, +static int process_string_cmd(void *user_ctx, const char *data, const GdbCmdParseEntry *cmds, int num_cmds) { int i, schema_len, max_num_params = 0; @@ -1454,7 +1492,6 @@ static int process_string_cmd(GDBState *s, void *user_ctx, const char *data, return -1; } - gdb_ctx.s = s; cmd->handler(&gdb_ctx, user_ctx); return 0; } @@ -1462,53 +1499,54 @@ static int process_string_cmd(GDBState *s, void *user_ctx, const char *data, return -1; } -static void run_cmd_parser(GDBState *s, const char *data, - const GdbCmdParseEntry *cmd) +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); + /* 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(s, NULL, data, cmd, 1)) { - put_packet(s, ""); + if (process_string_cmd(NULL, data, cmd, 1)) { + put_packet(""); } } static void handle_detach(GdbCmdContext *gdb_ctx, void *user_ctx) { GDBProcess *process; - GDBState *s = gdb_ctx->s; uint32_t pid = 1; - if (s->multiprocess) { + if (gdbserver_state.multiprocess) { if (!gdb_ctx->num_params) { - put_packet(s, "E22"); + put_packet("E22"); return; } pid = gdb_ctx->params[0].val_ul; } - process = gdb_get_process(s, pid); - gdb_process_breakpoint_remove_all(s, process); + process = gdb_get_process(pid); + gdb_process_breakpoint_remove_all(process); process->attached = false; - if (pid == gdb_get_cpu_pid(s, s->c_cpu)) { - s->c_cpu = gdb_first_attached_cpu(s); + if (pid == gdb_get_cpu_pid(gdbserver_state.c_cpu)) { + gdbserver_state.c_cpu = gdb_first_attached_cpu(); } - if (pid == gdb_get_cpu_pid(s, s->g_cpu)) { - s->g_cpu = gdb_first_attached_cpu(s); + if (pid == gdb_get_cpu_pid(gdbserver_state.g_cpu)) { + gdbserver_state.g_cpu = gdb_first_attached_cpu(); } - if (!s->c_cpu) { + if (!gdbserver_state.c_cpu) { /* No more process attached */ gdb_syscall_mode = GDB_SYS_DISABLED; - gdb_continue(s); + gdb_continue(); } - put_packet(s, "OK"); + put_packet("OK"); } static void handle_thread_alive(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1516,33 +1554,33 @@ static void handle_thread_alive(GdbCmdContext *gdb_ctx, void *user_ctx) CPUState *cpu; if (!gdb_ctx->num_params) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } if (gdb_ctx->params[0].thread_id.kind == GDB_READ_THREAD_ERR) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } - cpu = gdb_get_cpu(gdb_ctx->s, gdb_ctx->params[0].thread_id.pid, + cpu = gdb_get_cpu(gdb_ctx->params[0].thread_id.pid, gdb_ctx->params[0].thread_id.tid); if (!cpu) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); } static void handle_continue(GdbCmdContext *gdb_ctx, void *user_ctx) { if (gdb_ctx->num_params) { - gdb_set_cpu_pc(gdb_ctx->s, gdb_ctx->params[0].val_ull); + gdb_set_cpu_pc(gdb_ctx->params[0].val_ull); } - gdb_ctx->s->signal = 0; - gdb_continue(gdb_ctx->s); + gdbserver_state.signal = 0; + gdb_continue(); } static void handle_cont_with_sig(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1557,11 +1595,11 @@ static void handle_cont_with_sig(GdbCmdContext *gdb_ctx, void *user_ctx) signal = gdb_ctx->params[0].val_ul; } - gdb_ctx->s->signal = gdb_signal_to_target(signal); - if (gdb_ctx->s->signal == -1) { - gdb_ctx->s->signal = 0; + gdbserver_state.signal = gdb_signal_to_target(signal); + if (gdbserver_state.signal == -1) { + gdbserver_state.signal = 0; } - gdb_continue(gdb_ctx->s); + gdb_continue(); } static void handle_set_thread(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1569,24 +1607,24 @@ static void handle_set_thread(GdbCmdContext *gdb_ctx, void *user_ctx) CPUState *cpu; if (gdb_ctx->num_params != 2) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } if (gdb_ctx->params[1].thread_id.kind == GDB_READ_THREAD_ERR) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } if (gdb_ctx->params[1].thread_id.kind != GDB_ONE_THREAD) { - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); return; } - cpu = gdb_get_cpu(gdb_ctx->s, gdb_ctx->params[1].thread_id.pid, + cpu = gdb_get_cpu(gdb_ctx->params[1].thread_id.pid, gdb_ctx->params[1].thread_id.tid); if (!cpu) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } @@ -1596,15 +1634,15 @@ static void handle_set_thread(GdbCmdContext *gdb_ctx, void *user_ctx) */ switch (gdb_ctx->params[0].opcode) { case 'c': - gdb_ctx->s->c_cpu = cpu; - put_packet(gdb_ctx->s, "OK"); + gdbserver_state.c_cpu = cpu; + put_packet("OK"); break; case 'g': - gdb_ctx->s->g_cpu = cpu; - put_packet(gdb_ctx->s, "OK"); + gdbserver_state.g_cpu = cpu; + put_packet("OK"); break; default: - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); break; } } @@ -1614,7 +1652,7 @@ static void handle_insert_bp(GdbCmdContext *gdb_ctx, void *user_ctx) int res; if (gdb_ctx->num_params != 3) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } @@ -1622,14 +1660,14 @@ static void handle_insert_bp(GdbCmdContext *gdb_ctx, void *user_ctx) gdb_ctx->params[1].val_ull, gdb_ctx->params[2].val_ull); if (res >= 0) { - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); return; } else if (res == -ENOSYS) { - put_packet(gdb_ctx->s, ""); + put_packet(""); return; } - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); } static void handle_remove_bp(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1637,7 +1675,7 @@ static void handle_remove_bp(GdbCmdContext *gdb_ctx, void *user_ctx) int res; if (gdb_ctx->num_params != 3) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } @@ -1645,100 +1683,724 @@ static void handle_remove_bp(GdbCmdContext *gdb_ctx, void *user_ctx) gdb_ctx->params[1].val_ull, gdb_ctx->params[2].val_ull); if (res >= 0) { - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); return; } else if (res == -ENOSYS) { - put_packet(gdb_ctx->s, ""); + put_packet(""); return; } - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); } +/* + * handle_set/get_reg + * + * Older gdb are really dumb, and don't use 'G/g' if 'P/p' is available. + * This works, but can be very slow. Anything new enough to understand + * XML also knows how to use this properly. However to use this we + * need to define a local XML file as well as be talking to a + * reasonably modern gdb. Responding with an empty packet will cause + * the remote gdb to fallback to older methods. + */ + static void handle_set_reg(GdbCmdContext *gdb_ctx, void *user_ctx) { int reg_size; if (!gdb_has_xml) { - put_packet(gdb_ctx->s, "E00"); + put_packet(""); return; } if (gdb_ctx->num_params != 2) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } reg_size = strlen(gdb_ctx->params[1].data) / 2; - hextomem(gdb_ctx->mem_buf, gdb_ctx->params[1].data, reg_size); - gdb_write_register(gdb_ctx->s->g_cpu, gdb_ctx->mem_buf, + hextomem(gdbserver_state.mem_buf, gdb_ctx->params[1].data, reg_size); + gdb_write_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf->data, gdb_ctx->params[0].val_ull); - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); } static void handle_get_reg(GdbCmdContext *gdb_ctx, void *user_ctx) { int reg_size; - /* - * Older gdb are really dumb, and don't use 'g' if 'p' is avaialable. - * This works, but can be very slow. Anything new enough to - * understand XML also knows how to use this properly. - */ if (!gdb_has_xml) { - put_packet(gdb_ctx->s, ""); + put_packet(""); return; } if (!gdb_ctx->num_params) { - put_packet(gdb_ctx->s, "E14"); + put_packet("E14"); return; } - reg_size = gdb_read_register(gdb_ctx->s->g_cpu, gdb_ctx->mem_buf, + reg_size = gdb_read_register(gdbserver_state.g_cpu, + gdbserver_state.mem_buf, gdb_ctx->params[0].val_ull); if (!reg_size) { - put_packet(gdb_ctx->s, "E14"); + put_packet("E14"); + return; + } else { + g_byte_array_set_size(gdbserver_state.mem_buf, reg_size); + } + + memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, reg_size); + put_strbuf(); +} + +static void handle_write_mem(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + if (gdb_ctx->num_params != 3) { + put_packet("E22"); + return; + } + + /* hextomem() reads 2*len bytes */ + if (gdb_ctx->params[1].val_ull > strlen(gdb_ctx->params[2].data) / 2) { + put_packet("E22"); + return; + } + + hextomem(gdbserver_state.mem_buf, gdb_ctx->params[2].data, + gdb_ctx->params[1].val_ull); + if (target_memory_rw_debug(gdbserver_state.g_cpu, gdb_ctx->params[0].val_ull, + gdbserver_state.mem_buf->data, + gdbserver_state.mem_buf->len, true)) { + put_packet("E14"); + return; + } + + put_packet("OK"); +} + +static void handle_read_mem(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + if (gdb_ctx->num_params != 2) { + put_packet("E22"); + return; + } + + /* memtohex() doubles the required space */ + if (gdb_ctx->params[1].val_ull > MAX_PACKET_LENGTH / 2) { + put_packet("E22"); + return; + } + + g_byte_array_set_size(gdbserver_state.mem_buf, gdb_ctx->params[1].val_ull); + + if (target_memory_rw_debug(gdbserver_state.g_cpu, gdb_ctx->params[0].val_ull, + gdbserver_state.mem_buf->data, + gdbserver_state.mem_buf->len, false)) { + put_packet("E14"); + return; + } + + memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, + gdbserver_state.mem_buf->len); + put_strbuf(); +} + +static void handle_write_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + target_ulong addr, len; + uint8_t *registers; + int reg_size; + + if (!gdb_ctx->num_params) { + return; + } + + cpu_synchronize_state(gdbserver_state.g_cpu); + len = strlen(gdb_ctx->params[0].data) / 2; + hextomem(gdbserver_state.mem_buf, gdb_ctx->params[0].data, len); + registers = gdbserver_state.mem_buf->data; + for (addr = 0; addr < gdbserver_state.g_cpu->gdb_num_g_regs && len > 0; + addr++) { + reg_size = gdb_write_register(gdbserver_state.g_cpu, registers, addr); + len -= reg_size; + registers += reg_size; + } + put_packet("OK"); +} + +static void handle_read_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + target_ulong addr, len; + + cpu_synchronize_state(gdbserver_state.g_cpu); + g_byte_array_set_size(gdbserver_state.mem_buf, 0); + len = 0; + for (addr = 0; addr < gdbserver_state.g_cpu->gdb_num_g_regs; addr++) { + len += gdb_read_register(gdbserver_state.g_cpu, + gdbserver_state.mem_buf, + addr); + } + g_assert(len == gdbserver_state.mem_buf->len); + + memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, len); + put_strbuf(); +} + +static void handle_file_io(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + if (gdb_ctx->num_params >= 1 && gdbserver_state.current_syscall_cb) { + target_ulong ret, err; + + ret = (target_ulong)gdb_ctx->params[0].val_ull; + if (gdb_ctx->num_params >= 2) { + err = (target_ulong)gdb_ctx->params[1].val_ull; + } else { + err = 0; + } + gdbserver_state.current_syscall_cb(gdbserver_state.c_cpu, ret, err); + gdbserver_state.current_syscall_cb = NULL; + } + + if (gdb_ctx->num_params >= 3 && gdb_ctx->params[2].opcode == (uint8_t)'C') { + put_packet("T02"); + return; + } + + gdb_continue(); +} + +static void handle_step(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + if (gdb_ctx->num_params) { + gdb_set_cpu_pc((target_ulong)gdb_ctx->params[0].val_ull); + } + + cpu_single_step(gdbserver_state.c_cpu, sstep_flags); + gdb_continue(); +} + +static void handle_v_cont_query(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + put_packet("vCont;c;C;s;S"); +} + +static void handle_v_cont(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + int res; + + if (!gdb_ctx->num_params) { return; } - memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, reg_size); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + res = gdb_handle_vcont(gdb_ctx->params[0].data); + if ((res == -EINVAL) || (res == -ERANGE)) { + put_packet("E22"); + } else if (res) { + put_packet(""); + } +} + +static void handle_v_attach(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + GDBProcess *process; + CPUState *cpu; + + g_string_assign(gdbserver_state.str_buf, "E22"); + if (!gdb_ctx->num_params) { + goto cleanup; + } + + process = gdb_get_process(gdb_ctx->params[0].val_ul); + if (!process) { + goto cleanup; + } + + cpu = get_first_cpu_in_process(process); + if (!cpu) { + goto cleanup; + } + + process->attached = true; + gdbserver_state.g_cpu = cpu; + gdbserver_state.c_cpu = cpu; + + 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, ';'); +cleanup: + put_strbuf(); +} + +static void handle_v_kill(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + /* Kill the target */ + put_packet("OK"); + error_report("QEMU: Terminated via GDBstub"); + exit(0); +} + +static GdbCmdParseEntry gdb_v_commands_table[] = { + /* Order is important if has same prefix */ + { + .handler = handle_v_cont_query, + .cmd = "Cont?", + .cmd_startswith = 1 + }, + { + .handler = handle_v_cont, + .cmd = "Cont", + .cmd_startswith = 1, + .schema = "s0" + }, + { + .handler = handle_v_attach, + .cmd = "Attach;", + .cmd_startswith = 1, + .schema = "l0" + }, + { + .handler = handle_v_kill, + .cmd = "Kill;", + .cmd_startswith = 1 + }, +}; + +static void handle_v_commands(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + if (!gdb_ctx->num_params) { + return; + } + + if (process_string_cmd(NULL, gdb_ctx->params[0].data, + gdb_v_commands_table, + ARRAY_SIZE(gdb_v_commands_table))) { + put_packet(""); + } +} + +static void handle_query_qemu_sstepbits(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + g_string_printf(gdbserver_state.str_buf, "ENABLE=%x,NOIRQ=%x,NOTIMER=%x", + SSTEP_ENABLE, SSTEP_NOIRQ, SSTEP_NOTIMER); + put_strbuf(); +} + +static void handle_set_qemu_sstep(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + if (!gdb_ctx->num_params) { + return; + } + + sstep_flags = gdb_ctx->params[0].val_ul; + put_packet("OK"); +} + +static void handle_query_qemu_sstep(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + g_string_printf(gdbserver_state.str_buf, "0x%x", sstep_flags); + put_strbuf(); +} + +static void handle_query_curr_tid(GdbCmdContext *gdb_ctx, 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 = get_first_cpu_in_process(process); + g_string_assign(gdbserver_state.str_buf, "QC"); + gdb_append_thread_id(cpu, gdbserver_state.str_buf); + put_strbuf(); +} + +static void handle_query_threads(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + if (!gdbserver_state.query_cpu) { + put_packet("l"); + return; + } + + g_string_assign(gdbserver_state.str_buf, "m"); + gdb_append_thread_id(gdbserver_state.query_cpu, gdbserver_state.str_buf); + put_strbuf(); + gdbserver_state.query_cpu = gdb_next_attached_cpu(gdbserver_state.query_cpu); +} + +static void handle_query_first_threads(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + gdbserver_state.query_cpu = gdb_first_attached_cpu(); + handle_query_threads(gdb_ctx, user_ctx); } -static int gdb_handle_packet(GDBState *s, const char *line_buf) +static void handle_query_thread_extra(GdbCmdContext *gdb_ctx, void *user_ctx) { + g_autoptr(GString) rs = g_string_new(NULL); CPUState *cpu; + + if (!gdb_ctx->num_params || + gdb_ctx->params[0].thread_id.kind == GDB_READ_THREAD_ERR) { + put_packet("E22"); + return; + } + + cpu = gdb_get_cpu(gdb_ctx->params[0].thread_id.pid, + gdb_ctx->params[0].thread_id.tid); + if (!cpu) { + return; + } + + 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); + g_autofree 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); + memtohex(gdbserver_state.str_buf, (uint8_t *)rs->str, rs->len); + put_strbuf(); +} + +#ifdef CONFIG_USER_ONLY +static void handle_query_offsets(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + TaskState *ts; + + ts = gdbserver_state.c_cpu->opaque; + 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); + put_strbuf(); +} +#else +static void handle_query_rcmd(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + const guint8 zero = 0; + int len; + + if (!gdb_ctx->num_params) { + put_packet("E22"); + return; + } + + len = strlen(gdb_ctx->params[0].data); + if (len % 2) { + put_packet("E01"); + return; + } + + g_assert(gdbserver_state.mem_buf->len == 0); + len = len / 2; + hextomem(gdbserver_state.mem_buf, gdb_ctx->params[0].data, len); + g_byte_array_append(gdbserver_state.mem_buf, &zero, 1); + qemu_chr_be_write(gdbserver_state.mon_chr, gdbserver_state.mem_buf->data, + gdbserver_state.mem_buf->len); + put_packet("OK"); +} +#endif + +static void handle_query_supported(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + CPUClass *cc; + + g_string_printf(gdbserver_state.str_buf, "PacketSize=%x", MAX_PACKET_LENGTH); + cc = CPU_GET_CLASS(first_cpu); + if (cc->gdb_core_xml_file) { + g_string_append(gdbserver_state.str_buf, ";qXfer:features:read+"); + } + + if (gdb_ctx->num_params && + strstr(gdb_ctx->params[0].data, "multiprocess+")) { + gdbserver_state.multiprocess = true; + } + + g_string_append(gdbserver_state.str_buf, ";vContSupported+;multiprocess+"); + put_strbuf(); +} + +static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void *user_ctx) +{ GDBProcess *process; CPUClass *cc; + unsigned long len, total_len, addr; + const char *xml; const char *p; - uint32_t pid, tid; - int ch, reg_size, type, res; - uint8_t mem_buf[MAX_PACKET_LENGTH]; - char buf[sizeof(mem_buf) + 1 /* trailing NUL */]; - char thread_id[16]; - uint8_t *registers; - target_ulong addr, len; + + if (gdb_ctx->num_params < 3) { + put_packet("E22"); + return; + } + + process = gdb_get_cpu_process(gdbserver_state.g_cpu); + cc = CPU_GET_CLASS(gdbserver_state.g_cpu); + if (!cc->gdb_core_xml_file) { + put_packet(""); + return; + } + + gdb_has_xml = true; + p = gdb_ctx->params[0].data; + xml = get_feature_xml(p, &p, process); + if (!xml) { + put_packet("E00"); + return; + } + + addr = gdb_ctx->params[1].val_ul; + len = gdb_ctx->params[2].val_ul; + total_len = strlen(xml); + if (addr > total_len) { + put_packet("E00"); + return; + } + + 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"); + memtox(gdbserver_state.str_buf, xml + addr, len); + } else { + g_string_assign(gdbserver_state.str_buf, "l"); + memtox(gdbserver_state.str_buf, xml + addr, total_len - addr); + } + + put_packet_binary(gdbserver_state.str_buf->str, + gdbserver_state.str_buf->len, true); +} + +static void handle_query_attached(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + put_packet(GDB_ATTACHED); +} + +static void handle_query_qemu_supported(GdbCmdContext *gdb_ctx, 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 + put_strbuf(); +} + +#ifndef CONFIG_USER_ONLY +static void handle_query_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, + void *user_ctx) +{ + g_string_printf(gdbserver_state.str_buf, "%d", phy_memory_mode); + put_strbuf(); +} + +static void handle_set_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + if (!gdb_ctx->num_params) { + put_packet("E22"); + return; + } + + if (!gdb_ctx->params[0].val_ul) { + phy_memory_mode = 0; + } else { + phy_memory_mode = 1; + } + put_packet("OK"); +} +#endif + +static GdbCmdParseEntry gdb_gen_query_set_common_table[] = { + /* Order is important if has same prefix */ + { + .handler = handle_query_qemu_sstepbits, + .cmd = "qemu.sstepbits", + }, + { + .handler = handle_query_qemu_sstep, + .cmd = "qemu.sstep", + }, + { + .handler = handle_set_qemu_sstep, + .cmd = "qemu.sstep=", + .cmd_startswith = 1, + .schema = "l0" + }, +}; + +static GdbCmdParseEntry gdb_gen_query_table[] = { + { + .handler = handle_query_curr_tid, + .cmd = "C", + }, + { + .handler = handle_query_threads, + .cmd = "sThreadInfo", + }, + { + .handler = handle_query_first_threads, + .cmd = "fThreadInfo", + }, + { + .handler = handle_query_thread_extra, + .cmd = "ThreadExtraInfo,", + .cmd_startswith = 1, + .schema = "t0" + }, +#ifdef CONFIG_USER_ONLY + { + .handler = handle_query_offsets, + .cmd = "Offsets", + }, +#else + { + .handler = handle_query_rcmd, + .cmd = "Rcmd,", + .cmd_startswith = 1, + .schema = "s0" + }, +#endif + { + .handler = handle_query_supported, + .cmd = "Supported:", + .cmd_startswith = 1, + .schema = "s0" + }, + { + .handler = handle_query_supported, + .cmd = "Supported", + .schema = "s0" + }, + { + .handler = handle_query_xfer_features, + .cmd = "Xfer:features:read:", + .cmd_startswith = 1, + .schema = "s:l,l0" + }, + { + .handler = handle_query_attached, + .cmd = "Attached:", + .cmd_startswith = 1 + }, + { + .handler = handle_query_attached, + .cmd = "Attached", + }, + { + .handler = handle_query_qemu_supported, + .cmd = "qemu.Supported", + }, +#ifndef CONFIG_USER_ONLY + { + .handler = handle_query_qemu_phy_mem_mode, + .cmd = "qemu.PhyMemMode", + }, +#endif +}; + +static GdbCmdParseEntry gdb_gen_set_table[] = { + /* Order is important if has same prefix */ + { + .handler = handle_set_qemu_sstep, + .cmd = "qemu.sstep:", + .cmd_startswith = 1, + .schema = "l0" + }, +#ifndef CONFIG_USER_ONLY + { + .handler = handle_set_qemu_phy_mem_mode, + .cmd = "qemu.PhyMemMode:", + .cmd_startswith = 1, + .schema = "l0" + }, +#endif +}; + +static void handle_gen_query(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + if (!gdb_ctx->num_params) { + return; + } + + if (!process_string_cmd(NULL, gdb_ctx->params[0].data, + gdb_gen_query_set_common_table, + ARRAY_SIZE(gdb_gen_query_set_common_table))) { + return; + } + + if (process_string_cmd(NULL, gdb_ctx->params[0].data, + gdb_gen_query_table, + ARRAY_SIZE(gdb_gen_query_table))) { + put_packet(""); + } +} + +static void handle_gen_set(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + if (!gdb_ctx->num_params) { + return; + } + + if (!process_string_cmd(NULL, gdb_ctx->params[0].data, + gdb_gen_query_set_common_table, + ARRAY_SIZE(gdb_gen_query_set_common_table))) { + return; + } + + if (process_string_cmd(NULL, gdb_ctx->params[0].data, + gdb_gen_set_table, + ARRAY_SIZE(gdb_gen_set_table))) { + put_packet(""); + } +} + +static void handle_target_halt(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + 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, ';'); + put_strbuf(); + /* + * Remove all the breakpoints when this query is issued, + * because gdb is doing an initial connect and the state + * should be cleaned up. + */ + gdb_breakpoint_remove_all(); +} + +static int gdb_handle_packet(const char *line_buf) +{ const GdbCmdParseEntry *cmd_parser = NULL; trace_gdbstub_io_command(line_buf); - p = line_buf; - ch = *p++; - switch(ch) { + switch (line_buf[0]) { case '!': - put_packet(s, "OK"); + put_packet("OK"); break; case '?': - /* TODO: Make this return the correct value for user-mode. */ - snprintf(buf, sizeof(buf), "T%02xthread:%s;", GDB_SIGNAL_TRAP, - gdb_fmt_thread_id(s, s->c_cpu, thread_id, sizeof(thread_id))); - put_packet(s, buf); - /* Remove all the breakpoints when this query is issued, - * because gdb is doing and initial connect and the state - * should be cleaned up. - */ - gdb_breakpoint_remove_all(); + { + static const GdbCmdParseEntry target_halted_cmd_desc = { + .handler = handle_target_halt, + .cmd = "?", + .cmd_startswith = 1 + }; + cmd_parser = &target_halted_cmd_desc; + } break; case 'c': { @@ -1763,66 +2425,16 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; case 'v': - if (strncmp(p, "Cont", 4) == 0) { - p += 4; - if (*p == '?') { - put_packet(s, "vCont;c;C;s;S"); - break; - } - - res = gdb_handle_vcont(s, p); - - if (res) { - if ((res == -EINVAL) || (res == -ERANGE)) { - put_packet(s, "E22"); - break; - } - goto unknown_command; - } - break; - } else if (strncmp(p, "Attach;", 7) == 0) { - unsigned long pid; - - p += 7; - - if (qemu_strtoul(p, &p, 16, &pid)) { - put_packet(s, "E22"); - break; - } - - process = gdb_get_process(s, pid); - - if (process == NULL) { - put_packet(s, "E22"); - break; - } - - cpu = get_first_cpu_in_process(s, process); - - if (cpu == NULL) { - /* Refuse to attach an empty process */ - put_packet(s, "E22"); - break; - } - - process->attached = true; - - s->g_cpu = cpu; - s->c_cpu = cpu; - - snprintf(buf, sizeof(buf), "T%02xthread:%s;", GDB_SIGNAL_TRAP, - gdb_fmt_thread_id(s, cpu, thread_id, sizeof(thread_id))); - - put_packet(s, buf); - break; - } else if (strncmp(p, "Kill;", 5) == 0) { - /* Kill the target */ - put_packet(s, "OK"); - error_report("QEMU: Terminated via GDBstub"); - exit(0); - } else { - goto unknown_command; + { + static const GdbCmdParseEntry v_cmd_desc = { + .handler = handle_v_commands, + .cmd = "v", + .cmd_startswith = 1, + .schema = "s0" + }; + cmd_parser = &v_cmd_desc; } + break; case 'k': /* Kill the target */ error_report("QEMU: Terminated via GDBstub"); @@ -1839,99 +2451,68 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; case 's': - if (*p != '\0') { - addr = strtoull(p, (char **)&p, 16); - gdb_set_cpu_pc(s, addr); + { + static const GdbCmdParseEntry step_cmd_desc = { + .handler = handle_step, + .cmd = "s", + .cmd_startswith = 1, + .schema = "L0" + }; + cmd_parser = &step_cmd_desc; } - cpu_single_step(s->c_cpu, sstep_flags); - gdb_continue(s); - return RS_IDLE; + break; case 'F': { - target_ulong ret; - target_ulong err; - - ret = strtoull(p, (char **)&p, 16); - if (*p == ',') { - p++; - err = strtoull(p, (char **)&p, 16); - } else { - err = 0; - } - if (*p == ',') - p++; - type = *p; - if (s->current_syscall_cb) { - s->current_syscall_cb(s->c_cpu, ret, err); - s->current_syscall_cb = NULL; - } - if (type == 'C') { - put_packet(s, "T02"); - } else { - gdb_continue(s); - } + static const GdbCmdParseEntry file_io_cmd_desc = { + .handler = handle_file_io, + .cmd = "F", + .cmd_startswith = 1, + .schema = "L,L,o0" + }; + cmd_parser = &file_io_cmd_desc; } break; case 'g': - cpu_synchronize_state(s->g_cpu); - len = 0; - for (addr = 0; addr < s->g_cpu->gdb_num_g_regs; addr++) { - reg_size = gdb_read_register(s->g_cpu, mem_buf + len, addr); - len += reg_size; - } - memtohex(buf, mem_buf, len); - put_packet(s, buf); + { + static const GdbCmdParseEntry read_all_regs_cmd_desc = { + .handler = handle_read_all_regs, + .cmd = "g", + .cmd_startswith = 1 + }; + cmd_parser = &read_all_regs_cmd_desc; + } break; case 'G': - cpu_synchronize_state(s->g_cpu); - registers = mem_buf; - len = strlen(p) / 2; - hextomem((uint8_t *)registers, p, len); - for (addr = 0; addr < s->g_cpu->gdb_num_g_regs && len > 0; addr++) { - reg_size = gdb_write_register(s->g_cpu, registers, addr); - len -= reg_size; - registers += reg_size; - } - put_packet(s, "OK"); + { + static const GdbCmdParseEntry write_all_regs_cmd_desc = { + .handler = handle_write_all_regs, + .cmd = "G", + .cmd_startswith = 1, + .schema = "s0" + }; + cmd_parser = &write_all_regs_cmd_desc; + } break; case 'm': - addr = strtoull(p, (char **)&p, 16); - if (*p == ',') - p++; - len = strtoull(p, NULL, 16); - - /* memtohex() doubles the required space */ - if (len > MAX_PACKET_LENGTH / 2) { - put_packet (s, "E22"); - break; - } - - if (target_memory_rw_debug(s->g_cpu, addr, mem_buf, len, false) != 0) { - put_packet (s, "E14"); - } else { - memtohex(buf, mem_buf, len); - put_packet(s, buf); + { + static const GdbCmdParseEntry read_mem_cmd_desc = { + .handler = handle_read_mem, + .cmd = "m", + .cmd_startswith = 1, + .schema = "L,L0" + }; + cmd_parser = &read_mem_cmd_desc; } break; case 'M': - addr = strtoull(p, (char **)&p, 16); - if (*p == ',') - p++; - len = strtoull(p, (char **)&p, 16); - if (*p == ':') - p++; - - /* hextomem() reads 2*len bytes */ - if (len > strlen(p) / 2) { - put_packet (s, "E22"); - break; - } - hextomem(mem_buf, p, len); - if (target_memory_rw_debug(s->g_cpu, addr, mem_buf, len, - true) != 0) { - put_packet(s, "E14"); - } else { - put_packet(s, "OK"); + { + static const GdbCmdParseEntry write_mem_cmd_desc = { + .handler = handle_write_mem, + .cmd = "M", + .cmd_startswith = 1, + .schema = "L,L:s0" + }; + cmd_parser = &write_mem_cmd_desc; } break; case 'p': @@ -2001,197 +2582,43 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; case 'q': - case 'Q': - /* parse any 'q' packets here */ - if (!strcmp(p,"qemu.sstepbits")) { - /* Query Breakpoint bit definitions */ - snprintf(buf, sizeof(buf), "ENABLE=%x,NOIRQ=%x,NOTIMER=%x", - SSTEP_ENABLE, - SSTEP_NOIRQ, - SSTEP_NOTIMER); - put_packet(s, buf); - break; - } else if (is_query_packet(p, "qemu.sstep", '=')) { - /* Display or change the sstep_flags */ - p += 10; - if (*p != '=') { - /* Display current setting */ - snprintf(buf, sizeof(buf), "0x%x", sstep_flags); - put_packet(s, buf); - break; - } - p++; - type = strtoul(p, (char **)&p, 16); - sstep_flags = type; - put_packet(s, "OK"); - break; - } else if (strcmp(p,"C") == 0) { - /* - * "Current thread" remains vague in the spec, so always return - * the first thread of the current process (gdb returns the - * first thread). - */ - cpu = get_first_cpu_in_process(s, gdb_get_cpu_process(s, s->g_cpu)); - snprintf(buf, sizeof(buf), "QC%s", - gdb_fmt_thread_id(s, cpu, thread_id, sizeof(thread_id))); - put_packet(s, buf); - break; - } else if (strcmp(p,"fThreadInfo") == 0) { - s->query_cpu = gdb_first_attached_cpu(s); - goto report_cpuinfo; - } else if (strcmp(p,"sThreadInfo") == 0) { - report_cpuinfo: - if (s->query_cpu) { - snprintf(buf, sizeof(buf), "m%s", - gdb_fmt_thread_id(s, s->query_cpu, - thread_id, sizeof(thread_id))); - put_packet(s, buf); - s->query_cpu = gdb_next_attached_cpu(s, s->query_cpu); - } else - put_packet(s, "l"); - break; - } else if (strncmp(p,"ThreadExtraInfo,", 16) == 0) { - if (read_thread_id(p + 16, &p, &pid, &tid) == GDB_READ_THREAD_ERR) { - put_packet(s, "E22"); - break; - } - cpu = gdb_get_cpu(s, pid, tid); - if (cpu != NULL) { - cpu_synchronize_state(cpu); - - if (s->multiprocess && (s->process_num > 1)) { - /* Print the CPU model and name in multiprocess mode */ - ObjectClass *oc = object_get_class(OBJECT(cpu)); - const char *cpu_model = object_class_get_name(oc); - char *cpu_name = - object_get_canonical_path_component(OBJECT(cpu)); - len = snprintf((char *)mem_buf, sizeof(buf) / 2, - "%s %s [%s]", cpu_model, cpu_name, - cpu->halted ? "halted " : "running"); - g_free(cpu_name); - } else { - /* memtohex() doubles the required space */ - len = snprintf((char *)mem_buf, sizeof(buf) / 2, - "CPU#%d [%s]", cpu->cpu_index, - cpu->halted ? "halted " : "running"); - } - trace_gdbstub_op_extra_info((char *)mem_buf); - memtohex(buf, mem_buf, len); - put_packet(s, buf); - } - break; - } -#ifdef CONFIG_USER_ONLY - else if (strcmp(p, "Offsets") == 0) { - TaskState *ts = s->c_cpu->opaque; - - snprintf(buf, sizeof(buf), - "Text=" TARGET_ABI_FMT_lx ";Data=" TARGET_ABI_FMT_lx - ";Bss=" TARGET_ABI_FMT_lx, - ts->info->code_offset, - ts->info->data_offset, - ts->info->data_offset); - put_packet(s, buf); - break; - } -#else /* !CONFIG_USER_ONLY */ - else if (strncmp(p, "Rcmd,", 5) == 0) { - int len = strlen(p + 5); - - if ((len % 2) != 0) { - put_packet(s, "E01"); - break; - } - len = len / 2; - hextomem(mem_buf, p + 5, len); - mem_buf[len++] = 0; - qemu_chr_be_write(s->mon_chr, mem_buf, len); - put_packet(s, "OK"); - break; - } -#endif /* !CONFIG_USER_ONLY */ - if (is_query_packet(p, "Supported", ':')) { - snprintf(buf, sizeof(buf), "PacketSize=%x", MAX_PACKET_LENGTH); - cc = CPU_GET_CLASS(first_cpu); - if (cc->gdb_core_xml_file != NULL) { - pstrcat(buf, sizeof(buf), ";qXfer:features:read+"); - } - - if (strstr(p, "multiprocess+")) { - s->multiprocess = true; - } - pstrcat(buf, sizeof(buf), ";multiprocess+"); - - put_packet(s, buf); - break; - } - if (strncmp(p, "Xfer:features:read:", 19) == 0) { - const char *xml; - target_ulong total_len; - - process = gdb_get_cpu_process(s, s->g_cpu); - cc = CPU_GET_CLASS(s->g_cpu); - if (cc->gdb_core_xml_file == NULL) { - goto unknown_command; - } - - gdb_has_xml = true; - p += 19; - xml = get_feature_xml(s, p, &p, process); - if (!xml) { - snprintf(buf, sizeof(buf), "E00"); - put_packet(s, buf); - break; - } - - if (*p == ':') - p++; - addr = strtoul(p, (char **)&p, 16); - if (*p == ',') - p++; - len = strtoul(p, (char **)&p, 16); - - total_len = strlen(xml); - if (addr > total_len) { - snprintf(buf, sizeof(buf), "E00"); - put_packet(s, buf); - break; - } - if (len > (MAX_PACKET_LENGTH - 5) / 2) - len = (MAX_PACKET_LENGTH - 5) / 2; - if (len < total_len - addr) { - buf[0] = 'm'; - len = memtox(buf + 1, xml + addr, len); - } else { - buf[0] = 'l'; - len = memtox(buf + 1, xml + addr, total_len - addr); - } - put_packet_binary(s, buf, len + 1, true); - break; + { + static const GdbCmdParseEntry gen_query_cmd_desc = { + .handler = handle_gen_query, + .cmd = "q", + .cmd_startswith = 1, + .schema = "s0" + }; + cmd_parser = &gen_query_cmd_desc; } - if (is_query_packet(p, "Attached", ':')) { - put_packet(s, GDB_ATTACHED); - break; + break; + case 'Q': + { + static const GdbCmdParseEntry gen_set_cmd_desc = { + .handler = handle_gen_set, + .cmd = "Q", + .cmd_startswith = 1, + .schema = "s0" + }; + cmd_parser = &gen_set_cmd_desc; } - /* Unrecognised 'q' command. */ - goto unknown_command; - + break; default: - unknown_command: /* put empty packet */ - buf[0] = '\0'; - put_packet(s, buf); + put_packet(""); break; } - run_cmd_parser(s, line_buf, cmd_parser); + 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(gdbserver_state, cpu); + GDBProcess *p = gdb_get_cpu_process(cpu); if (!p->attached) { /* @@ -2201,26 +2628,25 @@ void gdb_set_stop_cpu(CPUState *cpu) return; } - gdbserver_state->c_cpu = cpu; - gdbserver_state->g_cpu = cpu; + gdbserver_state.c_cpu = cpu; + gdbserver_state.g_cpu = cpu; } #ifndef CONFIG_USER_ONLY static void gdb_vm_state_change(void *opaque, int running, RunState state) { - GDBState *s = gdbserver_state; - CPUState *cpu = s->c_cpu; - char buf[256]; - char thread_id[16]; + 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 || s->state == RS_INACTIVE) { + if (running || gdbserver_state.state == RS_INACTIVE) { return; } /* Is there a GDB syscall waiting to be sent? */ - if (s->current_syscall_cb) { - put_packet(s, s->syscall_buf); + if (gdbserver_state.current_syscall_cb) { + put_packet(gdbserver_state.syscall_buf); return; } @@ -2229,7 +2655,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) return; } - gdb_fmt_thread_id(s, cpu, thread_id, sizeof(thread_id)); + gdb_append_thread_id(cpu, tid); switch (state) { case RUN_STATE_DEBUG: @@ -2247,10 +2673,9 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) } trace_gdbstub_hit_watchpoint(type, cpu_gdb_index(cpu), (target_ulong)cpu->watchpoint_hit->vaddr); - snprintf(buf, sizeof(buf), - "T%02xthread:%s;%swatch:" TARGET_FMT_lx ";", - GDB_SIGNAL_TRAP, thread_id, type, - (target_ulong)cpu->watchpoint_hit->vaddr); + g_string_printf(buf, "T%02xthread:%s;%swatch:" TARGET_FMT_lx ";", + GDB_SIGNAL_TRAP, tid->str, type, + (target_ulong)cpu->watchpoint_hit->vaddr); cpu->watchpoint_hit = NULL; goto send_packet; } else { @@ -2291,10 +2716,10 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) break; } gdb_set_stop_cpu(cpu); - snprintf(buf, sizeof(buf), "T%02xthread:%s;", ret, thread_id); + g_string_printf(buf, "T%02xthread:%s;", ret, tid->str); send_packet: - put_packet(s, buf); + put_packet(buf->str); /* disable single step if it was enabled */ cpu_single_step(cpu, 0); @@ -2312,17 +2737,17 @@ void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va) char *p_end; target_ulong addr; uint64_t i64; - GDBState *s; - s = gdbserver_state; - if (!s) + if (!gdbserver_state.init) { return; - s->current_syscall_cb = cb; + } + + gdbserver_state.current_syscall_cb = cb; #ifndef CONFIG_USER_ONLY vm_stop(RUN_STATE_DEBUG); #endif - p = s->syscall_buf; - p_end = &s->syscall_buf[sizeof(s->syscall_buf)]; + p = &gdbserver_state.syscall_buf[0]; + p_end = &gdbserver_state.syscall_buf[sizeof(gdbserver_state.syscall_buf)]; *(p++) = 'F'; while (*fmt) { if (*fmt == '%') { @@ -2355,14 +2780,14 @@ void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va) } *p = 0; #ifdef CONFIG_USER_ONLY - put_packet(s, s->syscall_buf); + put_packet(gdbserver_state.syscall_buf); /* Return control to gdb for it to process the syscall request. * Since the protocol requires that gdb hands control back to us * using a "here are the results" F packet, we don't need to check * gdb_handlesig's return value (which is the signal to deliver if * execution was resumed via a continue packet). */ - gdb_handlesig(s->c_cpu, 0); + gdb_handlesig(gdbserver_state.c_cpu, 0); #else /* In this case wait to send the syscall packet until notification that the CPU has stopped. This must be done because if the packet is sent @@ -2370,7 +2795,7 @@ void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va) 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. */ - qemu_cpu_kick(s->c_cpu); + qemu_cpu_kick(gdbserver_state.c_cpu); #endif } @@ -2383,25 +2808,27 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...) va_end(va); } -static void gdb_read_byte(GDBState *s, uint8_t ch) +static void gdb_read_byte(uint8_t ch) { uint8_t reply; #ifndef CONFIG_USER_ONLY - if (s->last_packet_len) { + 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(); - put_buffer(s, (uint8_t *)s->last_packet, s->last_packet_len); + 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 == '$') - s->last_packet_len = 0; + if (ch == '+' || ch == '$') { + g_byte_array_set_size(gdbserver_state.last_packet, 0); + } if (ch != '$') return; } @@ -2412,13 +2839,13 @@ static void gdb_read_byte(GDBState *s, uint8_t ch) } else #endif { - switch(s->state) { + switch(gdbserver_state.state) { case RS_IDLE: if (ch == '$') { /* start of command packet */ - s->line_buf_index = 0; - s->line_sum = 0; - s->state = RS_GETLINE; + gdbserver_state.line_buf_index = 0; + gdbserver_state.line_sum = 0; + gdbserver_state.state = RS_GETLINE; } else { trace_gdbstub_err_garbage(ch); } @@ -2426,37 +2853,37 @@ static void gdb_read_byte(GDBState *s, uint8_t ch) case RS_GETLINE: if (ch == '}') { /* start escape sequence */ - s->state = RS_GETLINE_ESC; - s->line_sum += ch; + gdbserver_state.state = RS_GETLINE_ESC; + gdbserver_state.line_sum += ch; } else if (ch == '*') { /* start run length encoding sequence */ - s->state = RS_GETLINE_RLE; - s->line_sum += ch; + gdbserver_state.state = RS_GETLINE_RLE; + gdbserver_state.line_sum += ch; } else if (ch == '#') { /* end of command, start of checksum*/ - s->state = RS_CHKSUM1; - } else if (s->line_buf_index >= sizeof(s->line_buf) - 1) { + gdbserver_state.state = RS_CHKSUM1; + } else if (gdbserver_state.line_buf_index >= sizeof(gdbserver_state.line_buf) - 1) { trace_gdbstub_err_overrun(); - s->state = RS_IDLE; + gdbserver_state.state = RS_IDLE; } else { /* unescaped command character */ - s->line_buf[s->line_buf_index++] = ch; - s->line_sum += ch; + 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 */ - s->state = RS_CHKSUM1; - } else if (s->line_buf_index >= sizeof(s->line_buf) - 1) { + 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(); - s->state = RS_IDLE; + gdbserver_state.state = RS_IDLE; } else { /* parse escaped character and leave escape state */ - s->line_buf[s->line_buf_index++] = ch ^ 0x20; - s->line_sum += ch; - s->state = RS_GETLINE; + 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: @@ -2467,25 +2894,25 @@ static void gdb_read_byte(GDBState *s, uint8_t ch) if (ch < ' ' || ch == '#' || ch == '$' || ch > 126) { /* invalid RLE count encoding */ trace_gdbstub_err_invalid_repeat(ch); - s->state = RS_GETLINE; + gdbserver_state.state = RS_GETLINE; } else { /* decode repeat length */ int repeat = ch - ' ' + 3; - if (s->line_buf_index + repeat >= sizeof(s->line_buf) - 1) { + 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(); - s->state = RS_IDLE; - } else if (s->line_buf_index < 1) { + 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(); - s->state = RS_GETLINE; + gdbserver_state.state = RS_GETLINE; } else { /* repeat the last character */ - memset(s->line_buf + s->line_buf_index, - s->line_buf[s->line_buf_index - 1], repeat); - s->line_buf_index += repeat; - s->line_sum += ch; - s->state = RS_GETLINE; + 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; @@ -2493,33 +2920,33 @@ static void gdb_read_byte(GDBState *s, uint8_t ch) /* get high hex digit of checksum */ if (!isxdigit(ch)) { trace_gdbstub_err_checksum_invalid(ch); - s->state = RS_GETLINE; + gdbserver_state.state = RS_GETLINE; break; } - s->line_buf[s->line_buf_index] = '\0'; - s->line_csum = fromhex(ch) << 4; - s->state = RS_CHKSUM2; + 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); - s->state = RS_GETLINE; + gdbserver_state.state = RS_GETLINE; break; } - s->line_csum |= fromhex(ch); + gdbserver_state.line_csum |= fromhex(ch); - if (s->line_csum != (s->line_sum & 0xff)) { - trace_gdbstub_err_checksum_incorrect(s->line_sum, s->line_csum); + 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 = '-'; - put_buffer(s, &reply, 1); - s->state = RS_IDLE; + put_buffer(&reply, 1); + gdbserver_state.state = RS_IDLE; } else { /* send ACK reply */ reply = '+'; - put_buffer(s, &reply, 1); - s->state = gdb_handle_packet(s, s->line_buf); + put_buffer(&reply, 1); + gdbserver_state.state = gdb_handle_packet(gdbserver_state.line_buf); } break; default: @@ -2531,15 +2958,13 @@ static void gdb_read_byte(GDBState *s, uint8_t ch) /* Tell the remote gdb that the process has exited. */ void gdb_exit(CPUArchState *env, int code) { - GDBState *s; char buf[4]; - s = gdbserver_state; - if (!s) { + if (!gdbserver_state.init) { return; } #ifdef CONFIG_USER_ONLY - if (gdbserver_fd < 0 || s->fd < 0) { + if (gdbserver_fd < 0 || gdbserver_state.fd < 0) { return; } #endif @@ -2547,10 +2972,10 @@ void gdb_exit(CPUArchState *env, int code) trace_gdbstub_op_exiting((uint8_t)code); snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code); - put_packet(s, buf); + put_packet(buf); #ifndef CONFIG_USER_ONLY - qemu_chr_fe_deinit(&s->chr, true); + qemu_chr_fe_deinit(&gdbserver_state.chr, true); #endif } @@ -2564,7 +2989,7 @@ static void create_default_process(GDBState *s) GDBProcess *process; int max_pid = 0; - if (s->process_num) { + if (gdbserver_state.process_num) { max_pid = s->processes[s->process_num - 1].pid; } @@ -2583,12 +3008,10 @@ static void create_default_process(GDBState *s) int gdb_handlesig(CPUState *cpu, int sig) { - GDBState *s; char buf[256]; int n; - s = gdbserver_state; - if (gdbserver_fd < 0 || s->fd < 0) { + if (gdbserver_fd < 0 || gdbserver_state.fd < 0) { return sig; } @@ -2598,58 +3021,55 @@ gdb_handlesig(CPUState *cpu, int sig) if (sig != 0) { snprintf(buf, sizeof(buf), "S%02x", target_signal_to_gdb(sig)); - put_packet(s, buf); + put_packet(buf); } /* put_packet() might have detected that the peer terminated the connection. */ - if (s->fd < 0) { + if (gdbserver_state.fd < 0) { return sig; } sig = 0; - s->state = RS_IDLE; - s->running_state = 0; - while (s->running_state == 0) { - n = read(s->fd, buf, 256); + gdbserver_state.state = RS_IDLE; + gdbserver_state.running_state = 0; + while (gdbserver_state.running_state == 0) { + n = read(gdbserver_state.fd, buf, 256); if (n > 0) { int i; for (i = 0; i < n; i++) { - gdb_read_byte(s, buf[i]); + gdb_read_byte(buf[i]); } } else { /* XXX: Connection closed. Should probably wait for another connection before continuing. */ if (n == 0) { - close(s->fd); + close(gdbserver_state.fd); } - s->fd = -1; + gdbserver_state.fd = -1; return sig; } } - sig = s->signal; - s->signal = 0; + sig = gdbserver_state.signal; + gdbserver_state.signal = 0; return sig; } /* Tell the remote gdb that the process has exited due to SIG. */ void gdb_signalled(CPUArchState *env, int sig) { - GDBState *s; char buf[4]; - s = gdbserver_state; - if (gdbserver_fd < 0 || s->fd < 0) { + if (gdbserver_fd < 0 || gdbserver_state.fd < 0) { return; } snprintf(buf, sizeof(buf), "X%02x", target_signal_to_gdb(sig)); - put_packet(s, buf); + put_packet(buf); } static bool gdb_accept(void) { - GDBState *s; struct sockaddr_in sockaddr; socklen_t len; int fd; @@ -2673,15 +3093,13 @@ static bool gdb_accept(void) return false; } - s = g_malloc0(sizeof(GDBState)); - create_default_process(s); - s->processes[0].attached = true; - s->c_cpu = gdb_first_attached_cpu(s); - s->g_cpu = s->c_cpu; - s->fd = fd; + init_gdbserver_state(); + create_default_process(&gdbserver_state); + gdbserver_state.processes[0].attached = true; + gdbserver_state.c_cpu = gdb_first_attached_cpu(); + gdbserver_state.g_cpu = gdbserver_state.c_cpu; + gdbserver_state.fd = fd; gdb_has_xml = false; - - gdbserver_state = s; return true; } @@ -2734,13 +3152,11 @@ int gdbserver_start(int port) /* Disable gdb stub for child processes. */ void gdbserver_fork(CPUState *cpu) { - GDBState *s = gdbserver_state; - - if (gdbserver_fd < 0 || s->fd < 0) { + if (gdbserver_fd < 0 || gdbserver_state.fd < 0) { return; } - close(s->fd); - s->fd = -1; + close(gdbserver_state.fd); + gdbserver_state.fd = -1; cpu_breakpoint_remove_all(cpu, BP_GDB); cpu_watchpoint_remove_all(cpu, BP_GDB); } @@ -2757,11 +3173,11 @@ static void gdb_chr_receive(void *opaque, const uint8_t *buf, int size) int i; for (i = 0; i < size; i++) { - gdb_read_byte(gdbserver_state, buf[i]); + gdb_read_byte(buf[i]); } } -static void gdb_chr_event(void *opaque, int event) +static void gdb_chr_event(void *opaque, QEMUChrEvent event) { int i; GDBState *s = (GDBState *) opaque; @@ -2773,7 +3189,7 @@ static void gdb_chr_event(void *opaque, int event) s->processes[i].attached = !i; } - s->c_cpu = gdb_first_attached_cpu(s); + s->c_cpu = gdb_first_attached_cpu(); s->g_cpu = s->c_cpu; vm_stop(RUN_STATE_PAUSED); @@ -2784,32 +3200,11 @@ static void gdb_chr_event(void *opaque, int event) } } -static void gdb_monitor_output(GDBState *s, const char *msg, int len) -{ - char buf[MAX_PACKET_LENGTH]; - - buf[0] = 'O'; - if (len > (MAX_PACKET_LENGTH/2) - 1) - len = (MAX_PACKET_LENGTH/2) - 1; - memtohex(buf + 1, (uint8_t *)msg, len); - put_packet(s, buf); -} - static int gdb_monitor_write(Chardev *chr, const uint8_t *buf, int len) { - const char *p = (const char *)buf; - int max_sz; - - max_sz = (sizeof(gdbserver_state->last_packet) - 2) / 2; - for (;;) { - if (len <= max_sz) { - gdb_monitor_output(gdbserver_state, p, len); - break; - } - gdb_monitor_output(gdbserver_state, p, max_sz); - p += max_sz; - len -= max_sz; - } + g_autoptr(GString) hex_buf = g_string_new("O"); + memtohex(hex_buf, buf, len); + put_packet(hex_buf->str); return len; } @@ -2890,26 +3285,18 @@ static void create_processes(GDBState *s) { object_child_foreach(object_get_root(), find_cpu_clusters, s); - if (s->processes) { + if (gdbserver_state.processes) { /* Sort by PID */ - qsort(s->processes, s->process_num, sizeof(s->processes[0]), pid_order); + qsort(gdbserver_state.processes, gdbserver_state.process_num, sizeof(gdbserver_state.processes[0]), pid_order); } create_default_process(s); } -static void cleanup_processes(GDBState *s) -{ - g_free(s->processes); - s->process_num = 0; - s->processes = NULL; -} - int gdbserver_start(const char *device) { trace_gdbstub_op_start(device); - GDBState *s; char gdbstub_device_name[128]; Chardev *chr = NULL; Chardev *mon_chr; @@ -2947,43 +3334,40 @@ int gdbserver_start(const char *device) return -1; } - s = gdbserver_state; - if (!s) { - s = g_malloc0(sizeof(GDBState)); - gdbserver_state = s; + if (!gdbserver_state.init) { + init_gdbserver_state(); 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(mon_chr, 0); + monitor_init_hmp(mon_chr, false, &error_abort); } else { - qemu_chr_fe_deinit(&s->chr, true); - mon_chr = s->mon_chr; - cleanup_processes(s); - memset(s, 0, sizeof(GDBState)); - s->mon_chr = mon_chr; + qemu_chr_fe_deinit(&gdbserver_state.chr, true); + mon_chr = gdbserver_state.mon_chr; + reset_gdbserver_state(); } - create_processes(s); + create_processes(&gdbserver_state); if (chr) { - qemu_chr_fe_init(&s->chr, chr, &error_abort); - qemu_chr_fe_set_handlers(&s->chr, gdb_chr_can_receive, gdb_chr_receive, - gdb_chr_event, NULL, s, NULL, true); + qemu_chr_fe_init(&gdbserver_state.chr, chr, &error_abort); + qemu_chr_fe_set_handlers(&gdbserver_state.chr, gdb_chr_can_receive, + gdb_chr_receive, gdb_chr_event, + NULL, &gdbserver_state, NULL, true); } - s->state = chr ? RS_IDLE : RS_INACTIVE; - s->mon_chr = mon_chr; - s->current_syscall_cb = NULL; + gdbserver_state.state = chr ? RS_IDLE : RS_INACTIVE; + gdbserver_state.mon_chr = mon_chr; + gdbserver_state.current_syscall_cb = NULL; return 0; } void gdbserver_cleanup(void) { - if (gdbserver_state) { - put_packet(gdbserver_state, "W00"); + if (gdbserver_state.init) { + put_packet("W00"); } }