#include "cpu.h"
#include "qemu/sockets.h"
#include "sysemu/kvm.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)
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();
}
}
#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()) {
case GDB_BREAKPOINT_SW:
case GDB_BREAKPOINT_HW:
CPU_FOREACH(cpu) {
- env = cpu->env_ptr;
- err = cpu_breakpoint_insert(env, addr, BP_GDB, NULL);
- if (err)
+ err = cpu_breakpoint_insert(cpu, addr, BP_GDB, NULL);
+ if (err) {
break;
+ }
}
return err;
#ifndef CONFIG_USER_ONLY
case GDB_WATCHPOINT_READ:
case GDB_WATCHPOINT_ACCESS:
CPU_FOREACH(cpu) {
- env = cpu->env_ptr;
- err = cpu_watchpoint_insert(env, addr, len, xlat_gdb_type[type],
- NULL);
- if (err)
+ 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()) {
case GDB_BREAKPOINT_SW:
case GDB_BREAKPOINT_HW:
CPU_FOREACH(cpu) {
- env = cpu->env_ptr;
- err = cpu_breakpoint_remove(env, addr, BP_GDB);
- if (err)
+ err = cpu_breakpoint_remove(cpu, addr, BP_GDB);
+ if (err) {
break;
+ }
}
return err;
#ifndef CONFIG_USER_ONLY
case GDB_WATCHPOINT_READ:
case GDB_WATCHPOINT_ACCESS:
CPU_FOREACH(cpu) {
- env = cpu->env_ptr;
- err = cpu_watchpoint_remove(env, addr, len, xlat_gdb_type[type]);
+ 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);
}
CPU_FOREACH(cpu) {
- env = cpu->env_ptr;
- cpu_breakpoint_remove_all(env, BP_GDB);
+ 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)
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;
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();
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 != '=') {
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);
cc = CPU_GET_CLASS(first_cpu);
if (cc->gdb_core_xml_file != NULL) {
put_packet_binary(s, buf, len + 1);
break;
}
+ 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:
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));
}
/* 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)
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 {