X-Git-Url: http://vcs.maemo.org/git/?a=blobdiff_plain;f=gdbstub.c;h=bca9b1e2a647005e792a6e23a7b4819671e931f6;hb=7ef4da1c3a753888e2678388150f1b846b025168;hp=e15216a5906f97d637a621d7360c11b0f6b8fba9;hpb=e95c8d51c2a47d7ccb422f83446cb91a18f8f37d;p=qemu diff --git a/gdbstub.c b/gdbstub.c index e15216a..bca9b1e 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1,7 +1,7 @@ /* * gdb server stub * - * Copyright (c) 2003 Fabrice Bellard + * Copyright (c) 2003-2005 Fabrice Bellard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -17,7 +17,18 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifdef CONFIG_USER_ONLY +#include +#include +#include +#include +#include +#include + +#include "qemu.h" +#else #include "vl.h" +#endif #include #include @@ -32,17 +43,26 @@ enum RSState { RS_CHKSUM1, RS_CHKSUM2, }; - -static int gdbserver_fd; +/* XXX: This is not thread safe. Do we care? */ +static int gdbserver_fd = -1; typedef struct GDBState { - enum RSState state; + CPUState *env; /* current CPU */ + enum RSState state; /* parsing state */ int fd; char line_buf[4096]; int line_buf_index; int line_csum; +#ifdef CONFIG_USER_ONLY + int running_state; +#endif } GDBState; +#ifdef CONFIG_USER_ONLY +/* XXX: remove this hack. */ +static GDBState gdbserver_state; +#endif + static int get_char(GDBState *s) { uint8_t ch; @@ -218,13 +238,6 @@ static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size) } #elif defined (TARGET_PPC) -static uint32_t from_le32 (uint32_t *buf) -{ - uint8_t *p = (uint8_t *)buf; - - return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); -} - static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf) { uint32_t *registers = (uint32_t *)mem_buf, tmp; @@ -241,14 +254,14 @@ static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf) } /* nip, msr, ccr, lnk, ctr, xer, mq */ registers[96] = tswapl(env->nip); - registers[97] = tswapl(_load_msr(env)); + registers[97] = tswapl(do_load_msr(env)); tmp = 0; for (i = 0; i < 8; i++) tmp |= env->crf[i] << (32 - ((i + 1) * 4)); registers[98] = tswapl(tmp); registers[99] = tswapl(env->lr); registers[100] = tswapl(env->ctr); - registers[101] = tswapl(_load_xer(env)); + registers[101] = tswapl(do_load_xer(env)); registers[102] = 0; return 103 * 4; @@ -270,22 +283,22 @@ static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size) } /* nip, msr, ccr, lnk, ctr, xer, mq */ env->nip = tswapl(registers[96]); - _store_msr(env, tswapl(registers[97])); + do_store_msr(env, tswapl(registers[97])); registers[98] = tswapl(registers[98]); for (i = 0; i < 8; i++) env->crf[i] = (registers[98] >> (32 - ((i + 1) * 4))) & 0xF; env->lr = tswapl(registers[99]); env->ctr = tswapl(registers[100]); - _store_xer(env, tswapl(registers[101])); + do_store_xer(env, tswapl(registers[101])); } #elif defined (TARGET_SPARC) static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf) { - uint32_t *registers = (uint32_t *)mem_buf, tmp; + target_ulong *registers = (target_ulong *)mem_buf; int i; /* fill in g0..g7 */ - for(i = 0; i < 7; i++) { + for(i = 0; i < 8; i++) { registers[i] = tswapl(env->gregs[i]); } /* fill in register window */ @@ -296,14 +309,15 @@ static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf) for (i = 0; i < 32; i++) { registers[i + 32] = tswapl(*((uint32_t *)&env->fpr[i])); } +#ifndef TARGET_SPARC64 /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */ registers[64] = tswapl(env->y); - tmp = (0<<28) | (4<<24) | env->psr \ - | (env->psrs? PSR_S : 0) \ - | (env->psrs? PSR_PS : 0) \ - | (env->psret? PSR_ET : 0) \ - | env->cwp; - registers[65] = tswapl(tmp); + { + target_ulong tmp; + + tmp = GET_PSR(env); + registers[65] = tswapl(tmp); + } registers[66] = tswapl(env->wim); registers[67] = tswapl(env->tbr); registers[68] = tswapl(env->pc); @@ -311,13 +325,24 @@ static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf) registers[70] = tswapl(env->fsr); registers[71] = 0; /* csr */ registers[72] = 0; - - return 73 * 4; + return 73 * sizeof(target_ulong); +#else + for (i = 0; i < 32; i += 2) { + registers[i/2 + 64] = tswapl(*((uint64_t *)&env->fpr[i])); + } + registers[81] = tswapl(env->pc); + registers[82] = tswapl(env->npc); + registers[83] = tswapl(env->tstate[env->tl]); + registers[84] = tswapl(env->fsr); + registers[85] = tswapl(env->fprs); + registers[86] = tswapl(env->y); + return 87 * sizeof(target_ulong); +#endif } static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size) { - uint32_t *registers = (uint32_t *)mem_buf, tmp; + target_ulong *registers = (target_ulong *)mem_buf; int i; /* fill in g0..g7 */ @@ -326,28 +351,182 @@ static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size) } /* fill in register window */ for(i = 0; i < 24; i++) { - env->regwptr[i] = tswapl(registers[i]); + env->regwptr[i] = tswapl(registers[i + 8]); } /* fill in fprs */ for (i = 0; i < 32; i++) { *((uint32_t *)&env->fpr[i]) = tswapl(registers[i + 32]); } +#ifndef TARGET_SPARC64 /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */ env->y = tswapl(registers[64]); - tmp = tswapl(registers[65]); - env->psr = tmp & ~PSR_ICC; - env->psrs = (tmp & PSR_S)? 1 : 0; - env->psrps = (tmp & PSR_PS)? 1 : 0; - env->psret = (tmp & PSR_ET)? 1 : 0; - env->cwp = (tmp & PSR_CWP); + PUT_PSR(env, tswapl(registers[65])); env->wim = tswapl(registers[66]); env->tbr = tswapl(registers[67]); env->pc = tswapl(registers[68]); env->npc = tswapl(registers[69]); env->fsr = tswapl(registers[70]); -} #else + for (i = 0; i < 32; i += 2) { + uint64_t tmp; + tmp = tswapl(registers[i/2 + 64]) << 32; + tmp |= tswapl(registers[i/2 + 64 + 1]); + *((uint64_t *)&env->fpr[i]) = tmp; + } + env->pc = tswapl(registers[81]); + env->npc = tswapl(registers[82]); + env->tstate[env->tl] = tswapl(registers[83]); + env->fsr = tswapl(registers[84]); + env->fprs = tswapl(registers[85]); + env->y = tswapl(registers[86]); +#endif +} +#elif defined (TARGET_ARM) +static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf) +{ + int i; + uint8_t *ptr; + + ptr = mem_buf; + /* 16 core integer registers (4 bytes each). */ + for (i = 0; i < 16; i++) + { + *(uint32_t *)ptr = tswapl(env->regs[i]); + ptr += 4; + } + /* 8 FPA registers (12 bytes each), FPS (4 bytes). + Not yet implemented. */ + memset (ptr, 0, 8 * 12 + 4); + ptr += 8 * 12 + 4; + /* CPSR (4 bytes). */ + *(uint32_t *)ptr = tswapl (cpsr_read(env)); + ptr += 4; + + return ptr - mem_buf; +} + +static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size) +{ + int i; + uint8_t *ptr; + + ptr = mem_buf; + /* Core integer registers. */ + for (i = 0; i < 16; i++) + { + env->regs[i] = tswapl(*(uint32_t *)ptr); + ptr += 4; + } + /* Ignore FPA regs and scr. */ + ptr += 8 * 12 + 4; + cpsr_write (env, tswapl(*(uint32_t *)ptr), 0xffffffff); +} +#elif defined (TARGET_MIPS) +static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf) +{ + int i; + uint8_t *ptr; + + ptr = mem_buf; + for (i = 0; i < 32; i++) + { + *(uint32_t *)ptr = tswapl(env->gpr[i]); + ptr += 4; + } + + *(uint32_t *)ptr = tswapl(env->CP0_Status); + ptr += 4; + + *(uint32_t *)ptr = tswapl(env->LO); + ptr += 4; + + *(uint32_t *)ptr = tswapl(env->HI); + ptr += 4; + + *(uint32_t *)ptr = tswapl(env->CP0_BadVAddr); + ptr += 4; + + *(uint32_t *)ptr = tswapl(env->CP0_Cause); + ptr += 4; + + *(uint32_t *)ptr = tswapl(env->PC); + ptr += 4; + + /* 32 FP registers, fsr, fir, fp. Not yet implemented. */ + + return ptr - mem_buf; +} + +static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size) +{ + int i; + uint8_t *ptr; + + ptr = mem_buf; + for (i = 0; i < 32; i++) + { + env->gpr[i] = tswapl(*(uint32_t *)ptr); + ptr += 4; + } + + env->CP0_Status = tswapl(*(uint32_t *)ptr); + ptr += 4; + + env->LO = tswapl(*(uint32_t *)ptr); + ptr += 4; + + env->HI = tswapl(*(uint32_t *)ptr); + ptr += 4; + + env->CP0_BadVAddr = tswapl(*(uint32_t *)ptr); + ptr += 4; + env->CP0_Cause = tswapl(*(uint32_t *)ptr); + ptr += 4; + + env->PC = tswapl(*(uint32_t *)ptr); + ptr += 4; +} +#elif defined (TARGET_SH4) +static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf) +{ + uint32_t *ptr = (uint32_t *)mem_buf; + int i; + +#define SAVE(x) *ptr++=tswapl(x) + for (i = 0; i < 16; i++) SAVE(env->gregs[i]); + SAVE (env->pc); + SAVE (env->pr); + SAVE (env->gbr); + SAVE (env->vbr); + SAVE (env->mach); + SAVE (env->macl); + SAVE (env->sr); + SAVE (0); /* TICKS */ + SAVE (0); /* STALLS */ + SAVE (0); /* CYCLES */ + SAVE (0); /* INSTS */ + SAVE (0); /* PLR */ + + return ((uint8_t *)ptr - mem_buf); +} + +static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size) +{ + uint32_t *ptr = (uint32_t *)mem_buf; + int i; + +#define LOAD(x) (x)=*ptr++; + for (i = 0; i < 16; i++) LOAD(env->gregs[i]); + LOAD (env->pc); + LOAD (env->pr); + LOAD (env->gbr); + LOAD (env->vbr); + LOAD (env->mach); + LOAD (env->macl); + LOAD (env->sr); +} +#else static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf) { return 0; @@ -359,10 +538,8 @@ static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size) #endif -/* port = 0 means default port */ -static int gdb_handle_packet(GDBState *s, const char *line_buf) +static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf) { - CPUState *env = cpu_single_env; const char *p; int ch, reg_size, type; char buf[4096]; @@ -377,6 +554,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) ch = *p++; switch(ch) { case '?': + /* TODO: Make this return the correct value for user-mode. */ snprintf(buf, sizeof(buf), "S%02x", SIGTRAP); put_packet(s, buf); break; @@ -387,10 +565,21 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) env->eip = addr; #elif defined (TARGET_PPC) env->nip = addr; +#elif defined (TARGET_SPARC) + env->pc = addr; + env->npc = addr + 4; +#elif defined (TARGET_ARM) + env->regs[15] = addr; +#elif defined (TARGET_SH4) + env->pc = addr; #endif } +#ifdef CONFIG_USER_ONLY + s->running_state = 1; +#else vm_start(); - break; +#endif + return RS_IDLE; case 's': if (*p != '\0') { addr = strtoul(p, (char **)&p, 16); @@ -398,11 +587,22 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) env->eip = addr; #elif defined (TARGET_PPC) env->nip = addr; +#elif defined (TARGET_SPARC) + env->pc = addr; + env->npc = addr + 4; +#elif defined (TARGET_ARM) + env->regs[15] = addr; +#elif defined (TARGET_SH4) + env->pc = addr; #endif } cpu_single_step(env, 1); +#ifdef CONFIG_USER_ONLY + s->running_state = 1; +#else vm_start(); - break; +#endif + return RS_IDLE; case 'g': reg_size = cpu_gdb_read_registers(env, mem_buf); memtohex(buf, mem_buf, reg_size); @@ -420,21 +620,23 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) if (*p == ',') p++; len = strtoul(p, NULL, 16); - if (cpu_memory_rw_debug(env, addr, mem_buf, len, 0) != 0) - memset(mem_buf, 0, len); - memtohex(buf, mem_buf, len); - put_packet(s, buf); + if (cpu_memory_rw_debug(env, addr, mem_buf, len, 0) != 0) { + put_packet (s, "E14"); + } else { + memtohex(buf, mem_buf, len); + put_packet(s, buf); + } break; case 'M': addr = strtoul(p, (char **)&p, 16); if (*p == ',') p++; len = strtoul(p, (char **)&p, 16); - if (*p == ',') + if (*p == ':') p++; hextomem(mem_buf, p, len); if (cpu_memory_rw_debug(env, addr, mem_buf, len, 1) != 0) - put_packet(s, "ENN"); + put_packet(s, "E14"); else put_packet(s, "OK"); break; @@ -452,7 +654,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) put_packet(s, "OK"); } else { breakpoint_error: - put_packet(s, "ENN"); + put_packet(s, "E22"); } break; case 'z': @@ -480,6 +682,9 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) return RS_IDLE; } +extern void tb_flush(CPUState *env); + +#ifndef CONFIG_USER_ONLY static void gdb_vm_stopped(void *opaque, int reason) { GDBState *s = opaque; @@ -487,26 +692,35 @@ static void gdb_vm_stopped(void *opaque, int reason) int ret; /* disable single step if it was enable */ - cpu_single_step(cpu_single_env, 0); + cpu_single_step(s->env, 0); - if (reason == EXCP_DEBUG) + if (reason == EXCP_DEBUG) { + tb_flush(s->env); ret = SIGTRAP; - else + } else if (reason == EXCP_INTERRUPT) { + ret = SIGINT; + } else { ret = 0; + } snprintf(buf, sizeof(buf), "S%02x", ret); put_packet(s, buf); } +#endif static void gdb_read_byte(GDBState *s, int ch) { + CPUState *env = s->env; int i, csum; char reply[1]; +#ifndef CONFIG_USER_ONLY if (vm_running) { /* when the CPU is running, we cannot do anything except stop it when receiving a char */ vm_stop(EXCP_INTERRUPT); - } else { + } else +#endif + { switch(s->state) { case RS_IDLE: if (ch == '$') { @@ -541,26 +755,87 @@ static void gdb_read_byte(GDBState *s, int ch) } else { reply[0] = '+'; put_buffer(s, reply, 1); - s->state = gdb_handle_packet(s, s->line_buf); + s->state = gdb_handle_packet(s, env, s->line_buf); } break; } } } -static int gdb_can_read(void *opaque) +#ifdef CONFIG_USER_ONLY +int +gdb_handlesig (CPUState *env, int sig) { - return 256; + GDBState *s; + char buf[256]; + int n; + + if (gdbserver_fd < 0) + return sig; + + s = &gdbserver_state; + + /* disable single step if it was enabled */ + cpu_single_step(env, 0); + tb_flush(env); + + if (sig != 0) + { + snprintf(buf, sizeof(buf), "S%02x", sig); + put_packet(s, buf); + } + + sig = 0; + s->state = RS_IDLE; + s->running_state = 0; + while (s->running_state == 0) { + n = read (s->fd, buf, 256); + if (n > 0) + { + int i; + + for (i = 0; i < n; i++) + gdb_read_byte (s, buf[i]); + } + else if (n == 0 || errno != EAGAIN) + { + /* XXX: Connection closed. Should probably wait for annother + connection before continuing. */ + return sig; + } + } + return sig; } -static void gdb_read(void *opaque, const uint8_t *buf, int size) +/* Tell the remote gdb that the process has exited. */ +void gdb_exit(CPUState *env, int code) +{ + GDBState *s; + char buf[4]; + + if (gdbserver_fd < 0) + return; + + s = &gdbserver_state; + + snprintf(buf, sizeof(buf), "W%02x", code); + put_packet(s, buf); +} + +#else +static void gdb_read(void *opaque) { GDBState *s = opaque; - int i; + int i, size; + uint8_t buf[4096]; + + size = read(s->fd, buf, sizeof(buf)); + if (size < 0) + return; if (size == 0) { /* end of connection */ qemu_del_vm_stop_handler(gdb_vm_stopped, s); - qemu_del_fd_read_handler(s->fd); + qemu_set_fd_handler(s->fd, NULL, NULL, NULL); qemu_free(s); vm_start(); } else { @@ -569,7 +844,9 @@ static void gdb_read(void *opaque, const uint8_t *buf, int size) } } -static void gdb_accept(void *opaque, const uint8_t *buf, int size) +#endif + +static void gdb_accept(void *opaque) { GDBState *s; struct sockaddr_in sockaddr; @@ -591,22 +868,30 @@ static void gdb_accept(void *opaque, const uint8_t *buf, int size) val = 1; setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); +#ifdef CONFIG_USER_ONLY + s = &gdbserver_state; + memset (s, 0, sizeof (GDBState)); +#else s = qemu_mallocz(sizeof(GDBState)); if (!s) { close(fd); return; } +#endif + s->env = first_cpu; /* XXX: allow to change CPU */ s->fd = fd; fcntl(fd, F_SETFL, O_NONBLOCK); +#ifndef CONFIG_USER_ONLY /* stop the VM */ vm_stop(EXCP_INTERRUPT); /* start handling I/O */ - qemu_add_fd_read_handler(s->fd, gdb_can_read, gdb_read, s); + qemu_set_fd_handler(s->fd, gdb_read, NULL, s); /* when the VM is stopped, the following callback is called */ qemu_add_vm_stop_handler(gdb_vm_stopped, s); +#endif } static int gdbserver_open(int port) @@ -637,7 +922,9 @@ static int gdbserver_open(int port) perror("listen"); return -1; } +#ifndef CONFIG_USER_ONLY fcntl(fd, F_SETFL, O_NONBLOCK); +#endif return fd; } @@ -647,6 +934,10 @@ int gdbserver_start(int port) if (gdbserver_fd < 0) return -1; /* accept connections */ - qemu_add_fd_read_handler(gdbserver_fd, NULL, gdb_accept, NULL); +#ifdef CONFIG_USER_ONLY + gdb_accept (NULL); +#else + qemu_set_fd_handler(gdbserver_fd, gdb_accept, NULL, NULL); +#endif return 0; }