#include "cpu.h"
#include "qemu/sockets.h"
#include "sysemu/kvm.h"
-#include "qemu/bitops.h"
+#include "exec/semihost.h"
+
+#ifdef CONFIG_USER_ONLY
+#define GDB_ATTACHED "0"
+#else
+#define GDB_ATTACHED "1"
+#endif
static inline int target_memory_rw_debug(CPUState *cpu, target_ulong addr,
uint8_t *buf, int len, bool is_write)
static GDBState *gdbserver_state;
-/* This is an ugly hack to cope with both new and old gdb.
- If gdb sends qXfer:features:read then assume we're talking to a newish
- gdb that understands target descriptions. */
-static int gdb_has_xml;
+bool gdb_has_xml;
#ifdef CONFIG_USER_ONLY
/* XXX: This is not thread safe. Do we care? */
GDB_SYS_DISABLED,
} gdb_syscall_mode;
-/* If gdb is connected when the first semihosting syscall occurs then use
- remote gdb syscalls. Otherwise use native file IO. */
+/* Decide if either remote gdb syscalls or native file IO should be used. */
int use_gdb_syscalls(void)
{
+ SemihostingTarget target = semihosting_get_target();
+ if (target == SEMIHOSTING_TARGET_NATIVE) {
+ /* -semihosting-config target=native */
+ return false;
+ } else if (target == SEMIHOSTING_TARGET_GDB) {
+ /* -semihosting-config target=gdb */
+ return true;
+ }
+
+ /* -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);
#ifdef CONFIG_USER_ONLY
s->running_state = 1;
#else
- if (runstate_check(RUN_STATE_GUEST_PANICKED)) {
- runstate_set(RUN_STATE_DEBUG);
- }
if (!runstate_needs_reset()) {
vm_start();
}
return put_packet_binary(s, buf, strlen(buf));
}
-#if defined(TARGET_I386)
-
-#include "target-i386/gdbstub.c"
-
-#elif defined (TARGET_PPC)
-
-#if defined (TARGET_PPC64)
-#define GDB_CORE_XML "power64-core.xml"
-#else
-#define GDB_CORE_XML "power-core.xml"
-#endif
-
-#include "target-ppc/gdbstub.c"
-
-#elif defined (TARGET_SPARC)
-
-#include "target-sparc/gdbstub.c"
-
-#elif defined (TARGET_ARM)
-
-#define GDB_CORE_XML "arm-core.xml"
-
-#include "target-arm/gdbstub.c"
-
-#elif defined (TARGET_M68K)
-
-#define GDB_CORE_XML "cf-core.xml"
-
-#include "target-m68k/gdbstub.c"
-
-#elif defined (TARGET_MIPS)
-
-#include "target-mips/gdbstub.c"
-
-#elif defined(TARGET_OPENRISC)
-
-#include "target-openrisc/gdbstub.c"
-
-#elif defined (TARGET_SH4)
-
-#include "target-sh4/gdbstub.c"
-
-#elif defined (TARGET_MICROBLAZE)
-
-#include "target-microblaze/gdbstub.c"
-
-#elif defined (TARGET_CRIS)
-
-#include "target-cris/gdbstub.c"
-
-#elif defined (TARGET_ALPHA)
-
-#include "target-alpha/gdbstub.c"
-
-#elif defined (TARGET_S390X)
-
-#include "target-s390x/gdbstub.c"
-
-#elif defined (TARGET_LM32)
-
-#include "target-lm32/gdbstub.c"
-
-#elif defined(TARGET_XTENSA)
-
-#include "target-xtensa/gdbstub.c"
-
-#else
-
-static int cpu_gdb_read_register(CPUArchState *env, uint8_t *mem_buf, int n)
-{
- return 0;
-}
-
-static int cpu_gdb_write_register(CPUArchState *env, uint8_t *mem_buf, int n)
-{
- return 0;
-}
-
-#endif
-
-#ifdef GDB_CORE_XML
/* Encode data using the encoding for 'x' packets. */
static int memtox(char *buf, const char *mem, int len)
{
return p - buf;
}
-static const char *get_feature_xml(const char *p, const char **newp)
+static const char *get_feature_xml(const char *p, const char **newp,
+ CPUClass *cc)
{
size_t len;
int i;
"<!DOCTYPE target SYSTEM \"gdb-target.dtd\">"
"<target>"
"<xi:include href=\"%s\"/>",
- GDB_CORE_XML);
+ cc->gdb_core_xml_file);
for (r = cpu->gdb_regs; r; r = r->next) {
pstrcat(target_xml, sizeof(target_xml), "<xi:include href=\"");
}
return name ? xml_builtin[i][1] : NULL;
}
-#endif
static int gdb_read_register(CPUState *cpu, uint8_t *mem_buf, int reg)
{
GDBRegisterState *r;
if (reg < cc->gdb_num_core_regs) {
- return cpu_gdb_read_register(env, mem_buf, reg);
+ return cc->gdb_read_register(cpu, mem_buf, reg);
}
for (r = cpu->gdb_regs; r; r = r->next) {
GDBRegisterState *r;
if (reg < cc->gdb_num_core_regs) {
- return cpu_gdb_write_register(env, mem_buf, reg);
+ return cc->gdb_write_register(cpu, mem_buf, reg);
}
for (r = cpu->gdb_regs; r; r = r->next) {
if (g_pos != s->base_reg) {
fprintf(stderr, "Error: Bad gdb register numbering for '%s'\n"
"Expected %d got %d\n", xml, g_pos, s->base_reg);
+ } else {
+ cpu->gdb_num_g_regs = cpu->gdb_num_regs;
}
}
}
#ifndef CONFIG_USER_ONLY
-static const int xlat_gdb_type[] = {
- [GDB_WATCHPOINT_WRITE] = BP_GDB | BP_MEM_WRITE,
- [GDB_WATCHPOINT_READ] = BP_GDB | BP_MEM_READ,
- [GDB_WATCHPOINT_ACCESS] = BP_GDB | BP_MEM_ACCESS,
-};
+/* Translate GDB watchpoint type to a flags value for cpu_watchpoint_* */
+static inline int xlat_gdb_type(CPUState *cpu, int gdbtype)
+{
+ static const int xlat[] = {
+ [GDB_WATCHPOINT_WRITE] = BP_GDB | BP_MEM_WRITE,
+ [GDB_WATCHPOINT_READ] = BP_GDB | BP_MEM_READ,
+ [GDB_WATCHPOINT_ACCESS] = BP_GDB | BP_MEM_ACCESS,
+ };
+
+ CPUClass *cc = CPU_GET_CLASS(cpu);
+ int cputype = xlat[gdbtype];
+
+ if (cc->gdb_stop_before_watchpoint) {
+ cputype |= BP_STOP_BEFORE_ACCESS;
+ }
+ return cputype;
+}
#endif
static int gdb_breakpoint_insert(target_ulong addr, target_ulong len, int type)
{
CPUState *cpu;
- CPUArchState *env;
int err = 0;
if (kvm_enabled()) {
switch (type) {
case GDB_BREAKPOINT_SW:
case GDB_BREAKPOINT_HW:
- for (cpu = first_cpu; cpu != NULL; cpu = cpu->next_cpu) {
- env = cpu->env_ptr;
- err = cpu_breakpoint_insert(env, addr, BP_GDB, NULL);
- if (err)
+ CPU_FOREACH(cpu) {
+ err = cpu_breakpoint_insert(cpu, addr, BP_GDB, NULL);
+ if (err) {
break;
+ }
}
return err;
#ifndef CONFIG_USER_ONLY
case GDB_WATCHPOINT_WRITE:
case GDB_WATCHPOINT_READ:
case GDB_WATCHPOINT_ACCESS:
- for (cpu = first_cpu; cpu != NULL; cpu = cpu->next_cpu) {
- env = cpu->env_ptr;
- err = cpu_watchpoint_insert(env, addr, len, xlat_gdb_type[type],
- NULL);
- if (err)
+ CPU_FOREACH(cpu) {
+ err = cpu_watchpoint_insert(cpu, addr, len,
+ xlat_gdb_type(cpu, type), NULL);
+ if (err) {
break;
+ }
}
return err;
#endif
static int gdb_breakpoint_remove(target_ulong addr, target_ulong len, int type)
{
CPUState *cpu;
- CPUArchState *env;
int err = 0;
if (kvm_enabled()) {
switch (type) {
case GDB_BREAKPOINT_SW:
case GDB_BREAKPOINT_HW:
- for (cpu = first_cpu; cpu != NULL; cpu = cpu->next_cpu) {
- env = cpu->env_ptr;
- err = cpu_breakpoint_remove(env, addr, BP_GDB);
- if (err)
+ CPU_FOREACH(cpu) {
+ err = cpu_breakpoint_remove(cpu, addr, BP_GDB);
+ if (err) {
break;
+ }
}
return err;
#ifndef CONFIG_USER_ONLY
case GDB_WATCHPOINT_WRITE:
case GDB_WATCHPOINT_READ:
case GDB_WATCHPOINT_ACCESS:
- for (cpu = first_cpu; cpu != NULL; cpu = cpu->next_cpu) {
- env = cpu->env_ptr;
- err = cpu_watchpoint_remove(env, addr, len, xlat_gdb_type[type]);
+ CPU_FOREACH(cpu) {
+ err = cpu_watchpoint_remove(cpu, addr, len,
+ xlat_gdb_type(cpu, type));
if (err)
break;
}
static void gdb_breakpoint_remove_all(void)
{
CPUState *cpu;
- CPUArchState *env;
if (kvm_enabled()) {
kvm_remove_all_breakpoints(gdbserver_state->c_cpu);
return;
}
- for (cpu = first_cpu; cpu != NULL; cpu = cpu->next_cpu) {
- env = cpu->env_ptr;
- cpu_breakpoint_remove_all(env, BP_GDB);
+ CPU_FOREACH(cpu) {
+ cpu_breakpoint_remove_all(cpu, BP_GDB);
#ifndef CONFIG_USER_ONLY
- cpu_watchpoint_remove_all(env, BP_GDB);
+ cpu_watchpoint_remove_all(cpu, BP_GDB);
#endif
}
}
static void gdb_set_cpu_pc(GDBState *s, target_ulong pc)
{
CPUState *cpu = s->c_cpu;
- CPUClass *cc = CPU_GET_CLASS(cpu);
cpu_synchronize_state(cpu);
- if (cc->set_pc) {
- cc->set_pc(cpu, pc);
- }
+ cpu_set_pc(cpu, pc);
}
static CPUState *find_cpu(uint32_t thread_id)
{
CPUState *cpu;
- for (cpu = first_cpu; cpu != NULL; cpu = cpu->next_cpu) {
+ CPU_FOREACH(cpu) {
if (cpu_index(cpu) == thread_id) {
return cpu;
}
return NULL;
}
+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);
+}
+
static int gdb_handle_packet(GDBState *s, const char *line_buf)
{
CPUState *cpu;
+ CPUClass *cc;
const char *p;
uint32_t thread;
int ch, reg_size, type, res;
action = *p++;
signal = 0;
if (action == 'C' || action == 'S') {
- signal = strtoul(p, (char **)&p, 16);
+ signal = gdb_signal_to_target(strtoul(p, (char **)&p, 16));
+ if (signal == -1) {
+ signal = 0;
+ }
} else if (action != 'c' && action != 's') {
res = 0;
break;
goto unknown_command;
}
case 'k':
-#ifdef CONFIG_USER_ONLY
/* Kill the target */
fprintf(stderr, "\nQEMU: Terminated via GDBstub\n");
exit(0);
-#endif
case 'D':
/* Detach packet */
gdb_breakpoint_remove_all();
case 'g':
cpu_synchronize_state(s->g_cpu);
len = 0;
- for (addr = 0; addr < s->g_cpu->gdb_num_regs; addr++) {
+ 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;
}
registers = mem_buf;
len = strlen(p) / 2;
hextomem((uint8_t *)registers, p, len);
- for (addr = 0; addr < s->g_cpu->gdb_num_regs && len > 0; addr++) {
+ 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;
SSTEP_NOTIMER);
put_packet(s, buf);
break;
- } else if (strncmp(p,"qemu.sstep",10) == 0) {
+ } else if (is_query_packet(p, "qemu.sstep", '=')) {
/* Display or change the sstep_flags */
p += 10;
if (*p != '=') {
if (s->query_cpu) {
snprintf(buf, sizeof(buf), "m%x", cpu_index(s->query_cpu));
put_packet(s, buf);
- s->query_cpu = s->query_cpu->next_cpu;
+ s->query_cpu = CPU_NEXT(s->query_cpu);
} else
put_packet(s, "l");
break;
break;
}
#ifdef CONFIG_USER_ONLY
- else if (strncmp(p, "Offsets", 7) == 0) {
- CPUArchState *env = s->c_cpu->env_ptr;
- TaskState *ts = env->opaque;
+ 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
break;
}
#endif /* !CONFIG_USER_ONLY */
- if (strncmp(p, "Supported", 9) == 0) {
+ if (is_query_packet(p, "Supported", ':')) {
snprintf(buf, sizeof(buf), "PacketSize=%x", MAX_PACKET_LENGTH);
-#ifdef GDB_CORE_XML
- pstrcat(buf, sizeof(buf), ";qXfer:features:read+");
-#endif
+ cc = CPU_GET_CLASS(first_cpu);
+ if (cc->gdb_core_xml_file != NULL) {
+ pstrcat(buf, sizeof(buf), ";qXfer:features:read+");
+ }
put_packet(s, buf);
break;
}
-#ifdef GDB_CORE_XML
if (strncmp(p, "Xfer:features:read:", 19) == 0) {
const char *xml;
target_ulong total_len;
- gdb_has_xml = 1;
+ cc = CPU_GET_CLASS(first_cpu);
+ if (cc->gdb_core_xml_file == NULL) {
+ goto unknown_command;
+ }
+
+ gdb_has_xml = true;
p += 19;
- xml = get_feature_xml(p, &p);
+ xml = get_feature_xml(p, &p, cc);
if (!xml) {
snprintf(buf, sizeof(buf), "E00");
put_packet(s, buf);
put_packet_binary(s, buf, len + 1);
break;
}
-#endif
+ if (is_query_packet(p, "Attached", ':')) {
+ put_packet(s, GDB_ATTACHED);
+ break;
+ }
/* Unrecognised 'q' command. */
goto unknown_command;
static void gdb_vm_state_change(void *opaque, int running, RunState state)
{
GDBState *s = gdbserver_state;
- CPUArchState *env = s->c_cpu->env_ptr;
CPUState *cpu = s->c_cpu;
char buf[256];
const char *type;
}
switch (state) {
case RUN_STATE_DEBUG:
- if (env->watchpoint_hit) {
- switch (env->watchpoint_hit->flags & BP_MEM_ACCESS) {
+ if (cpu->watchpoint_hit) {
+ switch (cpu->watchpoint_hit->flags & BP_MEM_ACCESS) {
case BP_MEM_READ:
type = "r";
break;
snprintf(buf, sizeof(buf),
"T%02xthread:%02x;%swatch:" TARGET_FMT_lx ";",
GDB_SIGNAL_TRAP, cpu_index(cpu), type,
- env->watchpoint_hit->vaddr);
- env->watchpoint_hit = NULL;
+ (target_ulong)cpu->watchpoint_hit->vaddr);
+ cpu->watchpoint_hit = NULL;
goto send_packet;
}
- tb_flush(env);
+ tb_flush(cpu);
ret = GDB_SIGNAL_TRAP;
break;
case RUN_STATE_PAUSED:
ret = GDB_SIGNAL_UNKNOWN;
break;
}
+ gdb_set_stop_cpu(cpu);
snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, cpu_index(cpu));
send_packet:
%x - target_ulong argument printed in hex.
%lx - 64-bit argument printed in hex.
%s - string pointer (target_ulong) and length (int) pair. */
-void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...)
+void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va)
{
- va_list va;
char *p;
char *p_end;
target_ulong addr;
#ifndef CONFIG_USER_ONLY
vm_stop(RUN_STATE_DEBUG);
#endif
- va_start(va, fmt);
p = s->syscall_buf;
p_end = &s->syscall_buf[sizeof(s->syscall_buf)];
*(p++) = 'F';
}
}
*p = 0;
- va_end(va);
#ifdef CONFIG_USER_ONLY
put_packet(s, s->syscall_buf);
gdb_handlesig(s->c_cpu, 0);
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. */
- cpu_exit(s->c_cpu);
+ qemu_cpu_kick(s->c_cpu);
#endif
}
+void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ gdb_do_syscallv(cb, fmt, va);
+ va_end(va);
+}
+
static void gdb_read_byte(GDBState *s, int ch)
{
int i, csum;
if (gdbserver_fd < 0 || s->fd < 0) {
return;
}
+#else
+ if (!s->chr) {
+ return;
+ }
#endif
snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code);
put_packet(s, buf);
#ifndef CONFIG_USER_ONLY
- if (s->chr) {
- qemu_chr_delete(s->chr);
- }
+ qemu_chr_delete(s->chr);
#endif
}
int
gdb_handlesig(CPUState *cpu, int sig)
{
- CPUArchState *env = cpu->env_ptr;
GDBState *s;
char buf[256];
int n;
/* disable single step if it was enabled */
cpu_single_step(cpu, 0);
- tb_flush(env);
+ tb_flush(cpu);
if (sig != 0) {
snprintf(buf, sizeof(buf), "S%02x", target_signal_to_gdb(sig));
s->c_cpu = first_cpu;
s->g_cpu = first_cpu;
s->fd = fd;
- gdb_has_xml = 0;
+ gdb_has_xml = false;
gdbserver_state = s;
static int gdbserver_open(int port)
{
struct sockaddr_in sockaddr;
- int fd, val, ret;
+ int fd, ret;
fd = socket(PF_INET, SOCK_STREAM, 0);
if (fd < 0) {
fcntl(fd, F_SETFD, FD_CLOEXEC);
#endif
- /* allow fast reuse */
- val = 1;
- qemu_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+ socket_set_fast_reuse(fd);
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(port);
}
/* Disable gdb stub for child processes. */
-void gdbserver_fork(CPUArchState *env)
+void gdbserver_fork(CPUState *cpu)
{
GDBState *s = gdbserver_state;
- if (gdbserver_fd < 0 || s->fd < 0)
- return;
+
+ if (gdbserver_fd < 0 || s->fd < 0) {
+ return;
+ }
close(s->fd);
s->fd = -1;
- cpu_breakpoint_remove_all(env, BP_GDB);
- cpu_watchpoint_remove_all(env, BP_GDB);
+ cpu_breakpoint_remove_all(cpu, BP_GDB);
+ cpu_watchpoint_remove_all(cpu, BP_GDB);
}
#else
static int gdb_chr_can_receive(void *opaque)
switch (event) {
case CHR_EVENT_OPENED:
vm_stop(RUN_STATE_PAUSED);
- gdb_has_xml = 0;
+ gdb_has_xml = false;
break;
default:
break;
qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL);
/* Initialize a monitor terminal for gdb */
- mon_chr = g_malloc0(sizeof(*mon_chr));
+ mon_chr = qemu_chr_alloc();
mon_chr->chr_write = gdb_monitor_write;
monitor_init(mon_chr, 0);
} else {