X-Git-Url: http://git.maemo.org/git/?a=blobdiff_plain;f=gdbstub.c;h=518c939f452d4b318c73ca6a9b3474c8dc81b374;hb=a15167446259805dcd7e639261b2413e07654040;hp=a3ff07aed962dd1c60b2df089891f135962b5f5d;hpb=5a37791249c2dc6fe788fcb618a9dd9531695b84;p=qemu diff --git a/gdbstub.c b/gdbstub.c index a3ff07a..518c939 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -30,6 +30,7 @@ #include "qemu.h" #else +#include "monitor.h" #include "qemu-char.h" #include "sysemu.h" #include "gdbstub.h" @@ -38,6 +39,7 @@ #define MAX_PACKET_LENGTH 4096 #include "qemu_socket.h" +#include "kvm.h" enum { @@ -87,7 +89,11 @@ static int gdb_signal_table[] = { -1, /* SIGLOST */ TARGET_SIGUSR1, TARGET_SIGUSR2, +#ifdef TARGET_SIGPWR TARGET_SIGPWR, +#else + -1, +#endif -1, /* SIGPOLL */ -1, -1, @@ -100,6 +106,7 @@ static int gdb_signal_table[] = { -1, -1, -1, +#ifdef __SIGRTMIN __SIGRTMIN + 1, __SIGRTMIN + 2, __SIGRTMIN + 3, @@ -206,6 +213,7 @@ static int gdb_signal_table[] = { -1, -1, -1 +#endif }; #else /* In system mode we only need SIGINT and SIGTRAP; other signals @@ -257,6 +265,7 @@ typedef struct GDBRegisterState { } GDBRegisterState; enum RSState { + RS_INACTIVE, RS_IDLE, RS_GETLINE, RS_CHKSUM1, @@ -279,6 +288,7 @@ typedef struct GDBState { int running_state; #else CharDriverState *chr; + CharDriverState *mon_chr; #endif } GDBState; @@ -614,7 +624,17 @@ static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int i) #elif defined (TARGET_PPC) +/* Old gdb always expects FP registers. Newer (xml-aware) gdb only + expects whatever the target description contains. Due to a + historical mishap the FP registers appear in between core integer + regs and PC, MSR, CR, and so forth. We hack round this by giving the + FP regs zero size when talking to a newer gdb. */ #define NUM_CORE_REGS 71 +#if defined (TARGET_PPC64) +#define GDB_CORE_XML "power64-core.xml" +#else +#define GDB_CORE_XML "power-core.xml" +#endif static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n) { @@ -623,6 +643,8 @@ static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n) GET_REGL(env->gpr[n]); } else if (n < 64) { /* fprs */ + if (gdb_has_xml) + return 0; stfq_p(mem_buf, env->fpr[n-32]); return 8; } else { @@ -640,7 +662,12 @@ static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n) case 67: GET_REGL(env->lr); case 68: GET_REGL(env->ctr); case 69: GET_REGL(env->xer); - case 70: GET_REG32(0); /* fpscr */ + case 70: + { + if (gdb_has_xml) + return 0; + GET_REG32(0); /* fpscr */ + } } } return 0; @@ -654,6 +681,8 @@ static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n) return sizeof(target_ulong); } else if (n < 64) { /* fprs */ + if (gdb_has_xml) + return 0; env->fpr[n-32] = ldfq_p(mem_buf); return 8; } else { @@ -683,6 +712,8 @@ static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n) return sizeof(target_ulong); case 70: /* fpscr */ + if (gdb_has_xml) + return 0; return 4; } } @@ -1389,13 +1420,6 @@ void gdb_register_coprocessor(CPUState * env, } } -/* GDB breakpoint/watchpoint types */ -#define GDB_BREAKPOINT_SW 0 -#define GDB_BREAKPOINT_HW 1 -#define GDB_WATCHPOINT_WRITE 2 -#define GDB_WATCHPOINT_READ 3 -#define GDB_WATCHPOINT_ACCESS 4 - #ifndef CONFIG_USER_ONLY static const int xlat_gdb_type[] = { [GDB_WATCHPOINT_WRITE] = BP_GDB | BP_MEM_WRITE, @@ -1409,6 +1433,9 @@ static int gdb_breakpoint_insert(target_ulong addr, target_ulong len, int type) CPUState *env; int err = 0; + if (kvm_enabled()) + return kvm_insert_breakpoint(gdbserver_state->c_cpu, addr, len, type); + switch (type) { case GDB_BREAKPOINT_SW: case GDB_BREAKPOINT_HW: @@ -1440,6 +1467,9 @@ static int gdb_breakpoint_remove(target_ulong addr, target_ulong len, int type) CPUState *env; int err = 0; + if (kvm_enabled()) + return kvm_remove_breakpoint(gdbserver_state->c_cpu, addr, len, type); + switch (type) { case GDB_BREAKPOINT_SW: case GDB_BREAKPOINT_HW: @@ -1469,6 +1499,11 @@ static void gdb_breakpoint_remove_all(void) { CPUState *env; + if (kvm_enabled()) { + kvm_remove_all_breakpoints(gdbserver_state->c_cpu); + return; + } + for (env = first_cpu; env != NULL; env = env->next_cpu) { cpu_breakpoint_remove_all(env, BP_GDB); #ifndef CONFIG_USER_ONLY @@ -1509,6 +1544,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) addr = strtoull(p, (char **)&p, 16); #if defined(TARGET_I386) s->c_cpu->eip = addr; + cpu_synchronize_state(s->c_cpu, 1); #elif defined (TARGET_PPC) s->c_cpu->nip = addr; #elif defined (TARGET_SPARC) @@ -1550,6 +1586,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) addr = strtoull(p, (char **)&p, 16); #if defined(TARGET_I386) s->c_cpu->eip = addr; + cpu_synchronize_state(s->c_cpu, 1); #elif defined (TARGET_PPC) s->c_cpu->nip = addr; #elif defined (TARGET_SPARC) @@ -1595,6 +1632,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; case 'g': + cpu_synchronize_state(s->g_cpu, 0); len = 0; for (addr = 0; addr < num_g_regs; addr++) { reg_size = gdb_read_register(s->g_cpu, mem_buf + len, addr); @@ -1612,6 +1650,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) len -= reg_size; registers += reg_size; } + cpu_synchronize_state(s->g_cpu, 1); put_packet(s, "OK"); break; case 'm': @@ -1770,6 +1809,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) thread = strtoull(p+16, (char **)&p, 16); for (env = first_cpu; env != NULL; env = env->next_cpu) if (env->cpu_index + 1 == thread) { + cpu_synchronize_state(env, 0); len = snprintf((char *)mem_buf, sizeof(mem_buf), "CPU#%d [%s]", env->cpu_index, env->halted ? "halted " : "running"); @@ -1779,7 +1819,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; } -#ifdef CONFIG_LINUX_USER +#ifdef CONFIG_USER_ONLY else if (strncmp(p, "Offsets", 7) == 0) { TaskState *ts = s->c_cpu->opaque; @@ -1792,7 +1832,22 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) put_packet(s, buf); break; } -#endif +#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; + } + hextomem(mem_buf, p + 5, len); + len = len / 2; + mem_buf[len++] = 0; + qemu_chr_read(s->mon_chr, mem_buf, len); + put_packet(s, "OK"); + break; + } +#endif /* !CONFIG_USER_ONLY */ if (strncmp(p, "Supported", 9) == 0) { snprintf(buf, sizeof(buf), "PacketSize=%x", MAX_PACKET_LENGTH); #ifdef GDB_CORE_XML @@ -1861,7 +1916,7 @@ void gdb_set_stop_cpu(CPUState *env) } #ifndef CONFIG_USER_ONLY -static void gdb_vm_stopped(void *opaque, int reason) +static void gdb_vm_state_change(void *opaque, int running, int reason) { GDBState *s = gdbserver_state; CPUState *env = s->c_cpu; @@ -1869,7 +1924,8 @@ static void gdb_vm_stopped(void *opaque, int reason) const char *type; int ret; - if (s->state == RS_SYSCALL) + if (running || (reason != EXCP_DEBUG && reason != EXCP_INTERRUPT) || + s->state == RS_INACTIVE || s->state == RS_SYSCALL) return; /* disable single step if it was enable */ @@ -1898,10 +1954,8 @@ static void gdb_vm_stopped(void *opaque, int reason) } tb_flush(env); ret = GDB_SIGNAL_TRAP; - } else if (reason == EXCP_INTERRUPT) { - ret = GDB_SIGNAL_INT; } else { - ret = 0; + ret = GDB_SIGNAL_INT; } snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, env->cpu_index+1); put_packet(s, buf); @@ -1969,7 +2023,7 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...) #ifdef CONFIG_USER_ONLY gdb_handlesig(s->c_cpu, 0); #else - cpu_interrupt(s->c_cpu, CPU_INTERRUPT_EXIT); + cpu_exit(s->c_cpu); #endif } @@ -2163,13 +2217,6 @@ static void gdb_accept(void) setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); s = qemu_mallocz(sizeof(GDBState)); - if (!s) { - errno = ENOMEM; - perror("accept"); - return; - } - - memset (s, 0, sizeof (GDBState)); s->c_cpu = first_cpu; s->g_cpu = first_cpu; s->fd = fd; @@ -2261,40 +2308,86 @@ 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(CharDriverState *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; + } + return len; +} + int gdbserver_start(const char *port) { GDBState *s; char gdbstub_port_name[128]; int port_num; char *p; - CharDriverState *chr; + CharDriverState *chr = NULL; + CharDriverState *mon_chr; if (!port || !*port) return -1; + if (strcmp(port, "none") != 0) { + port_num = strtol(port, &p, 10); + if (*p == 0) { + /* A numeric value is interpreted as a port number. */ + snprintf(gdbstub_port_name, sizeof(gdbstub_port_name), + "tcp::%d,nowait,nodelay,server", port_num); + port = gdbstub_port_name; + } - port_num = strtol(port, &p, 10); - if (*p == 0) { - /* A numeric value is interpreted as a port number. */ - snprintf(gdbstub_port_name, sizeof(gdbstub_port_name), - "tcp::%d,nowait,nodelay,server", port_num); - port = gdbstub_port_name; - } + chr = qemu_chr_open("gdb", port, NULL); + if (!chr) + return -1; - chr = qemu_chr_open("gdb", port); - if (!chr) - return -1; + qemu_chr_add_handlers(chr, gdb_chr_can_receive, gdb_chr_receive, + gdb_chr_event, NULL); + } - s = qemu_mallocz(sizeof(GDBState)); + s = gdbserver_state; if (!s) { - return -1; + s = qemu_mallocz(sizeof(GDBState)); + gdbserver_state = s; + + qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL); + + /* Initialize a monitor terminal for gdb */ + mon_chr = qemu_mallocz(sizeof(*mon_chr)); + mon_chr->chr_write = gdb_monitor_write; + monitor_init(mon_chr, 0); + } else { + if (s->chr) + qemu_chr_close(s->chr); + mon_chr = s->mon_chr; + memset(s, 0, sizeof(GDBState)); } 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, NULL); - qemu_add_vm_stop_handler(gdb_vm_stopped, NULL); + s->state = chr ? RS_IDLE : RS_INACTIVE; + s->mon_chr = mon_chr; + return 0; } #endif