/*
* 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
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.0+
*/
#include "qemu/osdep.h"
#include "sysemu/sysemu.h"
#include "exec/gdbstub.h"
#include "hw/cpu/cluster.h"
+#include "hw/boards.h"
#endif
#define MAX_PACKET_LENGTH 4096
#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"
#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);
}
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);
put_packet(gdb_ctx->s, "E22");
}
+/*
+ * handle_set/get_reg
+ *
+ * Older gdb are really dumb, and don't use 'G/g' if 'P/p' is available.
+ * This works, but can be very slow. Anything new enough to understand
+ * XML also knows how to use this properly. However to use this we
+ * need to define a local XML file as well as be talking to a
+ * reasonably modern gdb. Responding with an empty packet will cause
+ * the remote gdb to fallback to older methods.
+ */
+
static void handle_set_reg(GdbCmdContext *gdb_ctx, void *user_ctx)
{
int reg_size;
if (!gdb_has_xml) {
- put_packet(gdb_ctx->s, "E00");
+ put_packet(gdb_ctx->s, "");
return;
}
{
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, "");
return;
static void handle_file_io(GdbCmdContext *gdb_ctx, void *user_ctx)
{
- if (gdb_ctx->num_params >= 2 && gdb_ctx->s->current_syscall_cb) {
+ if (gdb_ctx->num_params >= 1 && gdb_ctx->s->current_syscall_cb) {
target_ulong ret, err;
ret = (target_ulong)gdb_ctx->params[0].val_ull;
- err = (target_ulong)gdb_ctx->params[1].val_ull;
+ if (gdb_ctx->num_params >= 2) {
+ err = (target_ulong)gdb_ctx->params[1].val_ull;
+ } else {
+ err = 0;
+ }
gdb_ctx->s->current_syscall_cb(gdb_ctx->s->c_cpu, ret, err);
gdb_ctx->s->current_syscall_cb = NULL;
}
static void handle_query_qemu_supported(GdbCmdContext *gdb_ctx, void *user_ctx)
{
- put_packet(gdb_ctx->s, "sstepbits;sstep");
+ snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "sstepbits;sstep");
+#ifndef CONFIG_USER_ONLY
+ pstrcat(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), ";PhyMemMode");
+#endif
+ put_packet(gdb_ctx->s, gdb_ctx->str_buf);
+}
+
+#ifndef CONFIG_USER_ONLY
+static void handle_query_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx,
+ void *user_ctx)
+{
+ snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "%d", phy_memory_mode);
+ put_packet(gdb_ctx->s, gdb_ctx->str_buf);
}
+static void handle_set_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, void *user_ctx)
+{
+ if (!gdb_ctx->num_params) {
+ put_packet(gdb_ctx->s, "E22");
+ return;
+ }
+
+ if (!gdb_ctx->params[0].val_ul) {
+ phy_memory_mode = 0;
+ } else {
+ phy_memory_mode = 1;
+ }
+ put_packet(gdb_ctx->s, "OK");
+}
+#endif
+
static GdbCmdParseEntry gdb_gen_query_set_common_table[] = {
/* Order is important if has same prefix */
{
.handler = handle_query_qemu_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[] = {
.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)
}
}
-static int gdb_handle_packet(GDBState *s, const char *line_buf)
+static void handle_target_halt(GdbCmdContext *gdb_ctx, void *user_ctx)
{
- const char *p;
- int ch;
- uint8_t mem_buf[MAX_PACKET_LENGTH];
- char buf[sizeof(mem_buf) + 1 /* trailing NUL */];
char thread_id[16];
+
+ gdb_fmt_thread_id(gdb_ctx->s, gdb_ctx->s->c_cpu, thread_id,
+ sizeof(thread_id));
+ snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "T%02xthread:%s;",
+ GDB_SIGNAL_TRAP, thread_id);
+ put_packet(gdb_ctx->s, gdb_ctx->str_buf);
+ /*
+ * Remove all the breakpoints when this query is issued,
+ * because gdb is doing an initial connect and the state
+ * should be cleaned up.
+ */
+ gdb_breakpoint_remove_all();
+}
+
+static int gdb_handle_packet(GDBState *s, const char *line_buf)
+{
const GdbCmdParseEntry *cmd_parser = NULL;
trace_gdbstub_io_command(line_buf);
- p = line_buf;
- ch = *p++;
- switch(ch) {
+ switch (line_buf[0]) {
case '!':
put_packet(s, "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':
{
break;
default:
/* put empty packet */
- buf[0] = '\0';
- put_packet(s, buf);
+ put_packet(s, "");
break;
}
- run_cmd_parser(s, line_buf, cmd_parser);
+ if (cmd_parser) {
+ run_cmd_parser(s, line_buf, cmd_parser);
+ }
return RS_IDLE;
}
/* 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);
} else {
qemu_chr_fe_deinit(&s->chr, true);
mon_chr = s->mon_chr;