aboutsummaryrefslogtreecommitdiff
path: root/gdbstub.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdbstub.c')
-rw-r--r--gdbstub.c276
1 files changed, 194 insertions, 82 deletions
diff --git a/gdbstub.c b/gdbstub.c
index a0fc62d3d7..b522a8923a 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -69,7 +69,9 @@ enum RSState {
RS_SYSCALL,
};
typedef struct GDBState {
- CPUState *env; /* current CPU */
+ CPUState *c_cpu; /* current CPU for step/continue ops */
+ CPUState *g_cpu; /* current CPU for other ops */
+ CPUState *query_cpu; /* for q{f|s}ThreadInfo */
enum RSState state; /* parsing state */
char line_buf[MAX_PACKET_LENGTH];
int line_buf_index;
@@ -90,6 +92,8 @@ typedef struct GDBState {
*/
static int sstep_flags = SSTEP_ENABLE|SSTEP_NOIRQ|SSTEP_NOTIMER;
+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. */
@@ -99,9 +103,6 @@ static int gdb_has_xml;
/* XXX: This is not thread safe. Do we care? */
static int gdbserver_fd = -1;
-/* XXX: remove this hack. */
-static GDBState gdbserver_state;
-
static int get_char(GDBState *s)
{
uint8_t ch;
@@ -126,8 +127,6 @@ static int get_char(GDBState *s)
}
#endif
-/* GDB stub state for use by semihosting syscalls. */
-static GDBState *gdb_syscall_state;
static gdb_syscall_complete_cb gdb_current_syscall_cb;
enum {
@@ -141,8 +140,8 @@ enum {
int use_gdb_syscalls(void)
{
if (gdb_syscall_mode == GDB_SYS_UNKNOWN) {
- gdb_syscall_mode = (gdb_syscall_state ? GDB_SYS_ENABLED
- : GDB_SYS_DISABLED);
+ gdb_syscall_mode = (gdbserver_state ? GDB_SYS_ENABLED
+ : GDB_SYS_DISABLED);
}
return gdb_syscall_mode == GDB_SYS_ENABLED;
}
@@ -1031,7 +1030,7 @@ static int memtox(char *buf, const char *mem, int len)
return p - buf;
}
-const char *get_feature_xml(CPUState *env, const char *p, const char **newp)
+const char *get_feature_xml(const char *p, const char **newp)
{
extern const char *const xml_builtin[][2];
size_t len;
@@ -1057,7 +1056,7 @@ const char *get_feature_xml(CPUState *env, const char *p, const char **newp)
"<xi:include href=\"%s\"/>",
GDB_CORE_XML);
- for (r = env->gdb_regs; r; r = r->next) {
+ for (r = first_cpu->gdb_regs; r; r = r->next) {
strcat(target_xml, "<xi:include href=\"");
strcat(target_xml, r->xml);
strcat(target_xml, "\"/>");
@@ -1160,55 +1159,84 @@ static const int xlat_gdb_type[] = {
};
#endif
-static int gdb_breakpoint_insert(CPUState *env, target_ulong addr,
- target_ulong len, int type)
+static int gdb_breakpoint_insert(target_ulong addr, target_ulong len, int type)
{
+ CPUState *env;
+ int err = 0;
+
switch (type) {
case GDB_BREAKPOINT_SW:
case GDB_BREAKPOINT_HW:
- return cpu_breakpoint_insert(env, addr, BP_GDB, NULL);
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ err = cpu_breakpoint_insert(env, 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:
- return cpu_watchpoint_insert(env, addr, len, xlat_gdb_type[type],
- NULL);
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ err = cpu_watchpoint_insert(env, addr, len, xlat_gdb_type[type],
+ NULL);
+ if (err)
+ break;
+ }
+ return err;
#endif
default:
return -ENOSYS;
}
}
-static int gdb_breakpoint_remove(CPUState *env, target_ulong addr,
- target_ulong len, int type)
+static int gdb_breakpoint_remove(target_ulong addr, target_ulong len, int type)
{
+ CPUState *env;
+ int err = 0;
+
switch (type) {
case GDB_BREAKPOINT_SW:
case GDB_BREAKPOINT_HW:
- return cpu_breakpoint_remove(env, addr, BP_GDB);
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ err = cpu_breakpoint_remove(env, addr, BP_GDB);
+ if (err)
+ break;
+ }
+ return err;
#ifndef CONFIG_USER_ONLY
case GDB_WATCHPOINT_WRITE:
case GDB_WATCHPOINT_READ:
case GDB_WATCHPOINT_ACCESS:
- return cpu_watchpoint_remove(env, addr, len, xlat_gdb_type[type]);
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ err = cpu_watchpoint_remove(env, addr, len, xlat_gdb_type[type]);
+ if (err)
+ break;
+ }
+ return err;
#endif
default:
return -ENOSYS;
}
}
-static void gdb_breakpoint_remove_all(CPUState *env)
+static void gdb_breakpoint_remove_all(void)
{
- cpu_breakpoint_remove_all(env, BP_GDB);
+ CPUState *env;
+
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ cpu_breakpoint_remove_all(env, BP_GDB);
#ifndef CONFIG_USER_ONLY
- cpu_watchpoint_remove_all(env, BP_GDB);
+ cpu_watchpoint_remove_all(env, BP_GDB);
#endif
+ }
}
-static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
+static int gdb_handle_packet(GDBState *s, const char *line_buf)
{
+ CPUState *env;
const char *p;
- int ch, reg_size, type, res;
+ int ch, reg_size, type, res, thread;
char buf[MAX_PACKET_LENGTH];
uint8_t mem_buf[MAX_PACKET_LENGTH];
uint8_t *registers;
@@ -1222,32 +1250,33 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
switch(ch) {
case '?':
/* TODO: Make this return the correct value for user-mode. */
- snprintf(buf, sizeof(buf), "S%02x", SIGTRAP);
+ snprintf(buf, sizeof(buf), "T%02xthread:%02x;", SIGTRAP,
+ s->c_cpu->cpu_index+1);
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(env);
+ gdb_breakpoint_remove_all();
break;
case 'c':
if (*p != '\0') {
addr = strtoull(p, (char **)&p, 16);
#if defined(TARGET_I386)
- env->eip = addr;
+ s->c_cpu->eip = addr;
#elif defined (TARGET_PPC)
- env->nip = addr;
+ s->c_cpu->nip = addr;
#elif defined (TARGET_SPARC)
- env->pc = addr;
- env->npc = addr + 4;
+ s->c_cpu->pc = addr;
+ s->c_cpu->npc = addr + 4;
#elif defined (TARGET_ARM)
- env->regs[15] = addr;
+ s->c_cpu->regs[15] = addr;
#elif defined (TARGET_SH4)
- env->pc = addr;
+ s->c_cpu->pc = addr;
#elif defined (TARGET_MIPS)
- env->active_tc.PC = addr;
+ s->c_cpu->active_tc.PC = addr;
#elif defined (TARGET_CRIS)
- env->pc = addr;
+ s->c_cpu->pc = addr;
#endif
}
gdb_continue(s);
@@ -1262,7 +1291,7 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
exit(0);
case 'D':
/* Detach packet */
- gdb_breakpoint_remove_all(env);
+ gdb_breakpoint_remove_all();
gdb_continue(s);
put_packet(s, "OK");
break;
@@ -1270,23 +1299,23 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
if (*p != '\0') {
addr = strtoull(p, (char **)&p, 16);
#if defined(TARGET_I386)
- env->eip = addr;
+ s->c_cpu->eip = addr;
#elif defined (TARGET_PPC)
- env->nip = addr;
+ s->c_cpu->nip = addr;
#elif defined (TARGET_SPARC)
- env->pc = addr;
- env->npc = addr + 4;
+ s->c_cpu->pc = addr;
+ s->c_cpu->npc = addr + 4;
#elif defined (TARGET_ARM)
- env->regs[15] = addr;
+ s->c_cpu->regs[15] = addr;
#elif defined (TARGET_SH4)
- env->pc = addr;
+ s->c_cpu->pc = addr;
#elif defined (TARGET_MIPS)
- env->active_tc.PC = addr;
+ s->c_cpu->active_tc.PC = addr;
#elif defined (TARGET_CRIS)
- env->pc = addr;
+ s->c_cpu->pc = addr;
#endif
}
- cpu_single_step(env, sstep_flags);
+ cpu_single_step(s->c_cpu, sstep_flags);
gdb_continue(s);
return RS_IDLE;
case 'F':
@@ -1305,7 +1334,7 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
p++;
type = *p;
if (gdb_current_syscall_cb)
- gdb_current_syscall_cb(s->env, ret, err);
+ gdb_current_syscall_cb(s->c_cpu, ret, err);
if (type == 'C') {
put_packet(s, "T02");
} else {
@@ -1316,7 +1345,7 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
case 'g':
len = 0;
for (addr = 0; addr < num_g_regs; addr++) {
- reg_size = gdb_read_register(env, mem_buf + len, addr);
+ reg_size = gdb_read_register(s->g_cpu, mem_buf + len, addr);
len += reg_size;
}
memtohex(buf, mem_buf, len);
@@ -1327,7 +1356,7 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
len = strlen(p) / 2;
hextomem((uint8_t *)registers, p, len);
for (addr = 0; addr < num_g_regs && len > 0; addr++) {
- reg_size = gdb_write_register(env, registers, addr);
+ reg_size = gdb_write_register(s->g_cpu, registers, addr);
len -= reg_size;
registers += reg_size;
}
@@ -1338,7 +1367,7 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
if (*p == ',')
p++;
len = strtoull(p, NULL, 16);
- if (cpu_memory_rw_debug(env, addr, mem_buf, len, 0) != 0) {
+ if (cpu_memory_rw_debug(s->g_cpu, addr, mem_buf, len, 0) != 0) {
put_packet (s, "E14");
} else {
memtohex(buf, mem_buf, len);
@@ -1353,7 +1382,7 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
if (*p == ':')
p++;
hextomem(mem_buf, p, len);
- if (cpu_memory_rw_debug(env, addr, mem_buf, len, 1) != 0)
+ if (cpu_memory_rw_debug(s->g_cpu, addr, mem_buf, len, 1) != 0)
put_packet(s, "E14");
else
put_packet(s, "OK");
@@ -1365,7 +1394,7 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
if (!gdb_has_xml)
goto unknown_command;
addr = strtoull(p, (char **)&p, 16);
- reg_size = gdb_read_register(env, mem_buf, addr);
+ reg_size = gdb_read_register(s->g_cpu, mem_buf, addr);
if (reg_size) {
memtohex(buf, mem_buf, reg_size);
put_packet(s, buf);
@@ -1381,7 +1410,7 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
p++;
reg_size = strlen(p) / 2;
hextomem(mem_buf, p, reg_size);
- gdb_write_register(env, mem_buf, addr);
+ gdb_write_register(s->g_cpu, mem_buf, addr);
put_packet(s, "OK");
break;
case 'Z':
@@ -1394,9 +1423,9 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
p++;
len = strtoull(p, (char **)&p, 16);
if (ch == 'Z')
- res = gdb_breakpoint_insert(env, addr, len, type);
+ res = gdb_breakpoint_insert(addr, len, type);
else
- res = gdb_breakpoint_remove(env, addr, len, type);
+ res = gdb_breakpoint_remove(addr, len, type);
if (res >= 0)
put_packet(s, "OK");
else if (res == -ENOSYS)
@@ -1404,6 +1433,45 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
else
put_packet(s, "E22");
break;
+ case 'H':
+ type = *p++;
+ thread = strtoull(p, (char **)&p, 16);
+ if (thread == -1 || thread == 0) {
+ put_packet(s, "OK");
+ break;
+ }
+ for (env = first_cpu; env != NULL; env = env->next_cpu)
+ if (env->cpu_index + 1 == thread)
+ break;
+ if (env == NULL) {
+ put_packet(s, "E22");
+ break;
+ }
+ switch (type) {
+ case 'c':
+ s->c_cpu = env;
+ put_packet(s, "OK");
+ break;
+ case 'g':
+ s->g_cpu = env;
+ put_packet(s, "OK");
+ break;
+ default:
+ put_packet(s, "E22");
+ break;
+ }
+ break;
+ case 'T':
+ thread = strtoull(p, (char **)&p, 16);
+#ifndef CONFIG_USER_ONLY
+ if (thread > 0 && thread < smp_cpus + 1)
+#else
+ if (thread == 1)
+#endif
+ put_packet(s, "OK");
+ else
+ put_packet(s, "E22");
+ break;
case 'q':
case 'Q':
/* parse any 'q' packets here */
@@ -1429,10 +1497,39 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
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 CPU (gdb returns the first thread). */
+ put_packet(s, "QC1");
+ break;
+ } else if (strcmp(p,"fThreadInfo") == 0) {
+ s->query_cpu = first_cpu;
+ goto report_cpuinfo;
+ } else if (strcmp(p,"sThreadInfo") == 0) {
+ report_cpuinfo:
+ if (s->query_cpu) {
+ snprintf(buf, sizeof(buf), "m%x", s->query_cpu->cpu_index+1);
+ put_packet(s, buf);
+ s->query_cpu = s->query_cpu->next_cpu;
+ } else
+ put_packet(s, "l");
+ break;
+ } else if (strncmp(p,"ThreadExtraInfo,", 16) == 0) {
+ thread = strtoull(p+16, (char **)&p, 16);
+ for (env = first_cpu; env != NULL; env = env->next_cpu)
+ if (env->cpu_index + 1 == thread) {
+ len = snprintf((char *)mem_buf, sizeof(mem_buf),
+ "CPU#%d [%s]", env->cpu_index,
+ env->halted ? "halted " : "running");
+ memtohex(buf, mem_buf, len);
+ put_packet(s, buf);
+ break;
+ }
+ break;
}
#ifdef CONFIG_LINUX_USER
else if (strncmp(p, "Offsets", 7) == 0) {
- TaskState *ts = env->opaque;
+ TaskState *ts = s->c_cpu->opaque;
snprintf(buf, sizeof(buf),
"Text=" TARGET_ABI_FMT_lx ";Data=" TARGET_ABI_FMT_lx
@@ -1459,7 +1556,7 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
gdb_has_xml = 1;
p += 19;
- xml = get_feature_xml(env, p, &p);
+ xml = get_feature_xml(p, &p);
if (!xml) {
snprintf(buf, sizeof(buf), "E00");
put_packet(s, buf);
@@ -1507,10 +1604,17 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
extern void tb_flush(CPUState *env);
+void gdb_set_stop_cpu(CPUState *env)
+{
+ gdbserver_state->c_cpu = env;
+ gdbserver_state->g_cpu = env;
+}
+
#ifndef CONFIG_USER_ONLY
static void gdb_vm_stopped(void *opaque, int reason)
{
- GDBState *s = opaque;
+ GDBState *s = gdbserver_state;
+ CPUState *env = s->c_cpu;
char buf[256];
const char *type;
int ret;
@@ -1519,11 +1623,11 @@ static void gdb_vm_stopped(void *opaque, int reason)
return;
/* disable single step if it was enable */
- cpu_single_step(s->env, 0);
+ cpu_single_step(env, 0);
if (reason == EXCP_DEBUG) {
- if (s->env->watchpoint_hit) {
- switch (s->env->watchpoint_hit->flags & BP_MEM_ACCESS) {
+ if (env->watchpoint_hit) {
+ switch (env->watchpoint_hit->flags & BP_MEM_ACCESS) {
case BP_MEM_READ:
type = "r";
break;
@@ -1534,20 +1638,22 @@ static void gdb_vm_stopped(void *opaque, int reason)
type = "";
break;
}
- snprintf(buf, sizeof(buf), "T%02x%swatch:" TARGET_FMT_lx ";",
- SIGTRAP, type, s->env->watchpoint_hit->vaddr);
+ snprintf(buf, sizeof(buf),
+ "T%02xthread:%02x;%swatch:" TARGET_FMT_lx ";",
+ SIGTRAP, env->cpu_index+1, type,
+ env->watchpoint_hit->vaddr);
put_packet(s, buf);
- s->env->watchpoint_hit = NULL;
+ env->watchpoint_hit = NULL;
return;
}
- tb_flush(s->env);
+ tb_flush(env);
ret = SIGTRAP;
} else if (reason == EXCP_INTERRUPT) {
ret = SIGINT;
} else {
ret = 0;
}
- snprintf(buf, sizeof(buf), "S%02x", ret);
+ snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, env->cpu_index+1);
put_packet(s, buf);
}
#endif
@@ -1566,7 +1672,7 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...)
uint64_t i64;
GDBState *s;
- s = gdb_syscall_state;
+ s = gdbserver_state;
if (!s)
return;
gdb_current_syscall_cb = cb;
@@ -1611,15 +1717,14 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...)
va_end(va);
put_packet(s, buf);
#ifdef CONFIG_USER_ONLY
- gdb_handlesig(s->env, 0);
+ gdb_handlesig(s->c_cpu, 0);
#else
- cpu_interrupt(s->env, CPU_INTERRUPT_EXIT);
+ cpu_interrupt(s->c_cpu, CPU_INTERRUPT_EXIT);
#endif
}
static void gdb_read_byte(GDBState *s, int ch)
{
- CPUState *env = s->env;
int i, csum;
uint8_t reply;
@@ -1685,7 +1790,7 @@ static void gdb_read_byte(GDBState *s, int ch)
} else {
reply = '+';
put_buffer(s, &reply, 1);
- s->state = gdb_handle_packet(s, env, s->line_buf);
+ s->state = gdb_handle_packet(s, s->line_buf);
}
break;
default:
@@ -1702,7 +1807,7 @@ gdb_handlesig (CPUState *env, int sig)
char buf[256];
int n;
- s = &gdbserver_state;
+ s = gdbserver_state;
if (gdbserver_fd < 0 || s->fd < 0)
return sig;
@@ -1750,7 +1855,7 @@ void gdb_exit(CPUState *env, int code)
GDBState *s;
char buf[4];
- s = &gdbserver_state;
+ s = gdbserver_state;
if (gdbserver_fd < 0 || s->fd < 0)
return;
@@ -1759,7 +1864,7 @@ void gdb_exit(CPUState *env, int code)
}
-static void gdb_accept(void *opaque)
+static void gdb_accept(void)
{
GDBState *s;
struct sockaddr_in sockaddr;
@@ -1781,13 +1886,20 @@ static void gdb_accept(void *opaque)
val = 1;
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
- s = &gdbserver_state;
+ s = qemu_mallocz(sizeof(GDBState));
+ if (!s) {
+ errno = ENOMEM;
+ perror("accept");
+ return;
+ }
+
memset (s, 0, sizeof (GDBState));
- s->env = first_cpu; /* XXX: allow to change CPU */
+ s->c_cpu = first_cpu;
+ s->g_cpu = first_cpu;
s->fd = fd;
gdb_has_xml = 0;
- gdb_syscall_state = s;
+ gdbserver_state = s;
fcntl(fd, F_SETFL, O_NONBLOCK);
}
@@ -1829,7 +1941,7 @@ int gdbserver_start(int port)
if (gdbserver_fd < 0)
return -1;
/* accept connections */
- gdb_accept (NULL);
+ gdb_accept();
return 0;
}
#else
@@ -1842,11 +1954,10 @@ static int gdb_chr_can_receive(void *opaque)
static void gdb_chr_receive(void *opaque, const uint8_t *buf, int size)
{
- GDBState *s = opaque;
int i;
for (i = 0; i < size; i++) {
- gdb_read_byte(s, buf[i]);
+ gdb_read_byte(gdbserver_state, buf[i]);
}
}
@@ -1855,7 +1966,6 @@ static void gdb_chr_event(void *opaque, int event)
switch (event) {
case CHR_EVENT_RESET:
vm_stop(EXCP_INTERRUPT);
- gdb_syscall_state = opaque;
gdb_has_xml = 0;
break;
default:
@@ -1890,11 +2000,13 @@ int gdbserver_start(const char *port)
if (!s) {
return -1;
}
- s->env = first_cpu; /* XXX: allow to change CPU */
+ s->c_cpu = first_cpu;
+ s->g_cpu = first_cpu;
s->chr = chr;
+ gdbserver_state = s;
qemu_chr_add_handlers(chr, gdb_chr_can_receive, gdb_chr_receive,
- gdb_chr_event, s);
- qemu_add_vm_stop_handler(gdb_vm_stopped, s);
+ gdb_chr_event, NULL);
+ qemu_add_vm_stop_handler(gdb_vm_stopped, NULL);
return 0;
}
#endif