X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=simavr%2Fsim%2Fsim_gdb.c;h=7ddfb60f5114db69a86e42bb3ce68c7c404baf4b;hb=29218dcf897373eac0eb750b4e4a3e066e057840;hp=455c56ee82e24e2f8d2af13b279e0fab83a68dbb;hpb=7cad6f9b651144cbb3a5c1bd75cbf2ae75cf513d;p=simavr diff --git a/simavr/sim/sim_gdb.c b/simavr/sim/sim_gdb.c index 455c56e..7ddfb60 100644 --- a/simavr/sim/sim_gdb.c +++ b/simavr/sim/sim_gdb.c @@ -19,39 +19,146 @@ along with simavr. If not, see . */ -#include -#include -#include -#include +#include "sim_network.h" #include #include #include #include #include #include -#include #include #include "sim_avr.h" +#include "sim_core.h" // for SET_SREG_FROM, READ_SREG_INTO #include "sim_hex.h" #include "avr_eeprom.h" #include "sim_gdb.h" #define DBG(w) +#define WATCH_LIMIT (32) + +typedef struct { + uint32_t len; /**< How many points are taken (points[0] .. points[len - 1]). */ + struct { + uint32_t addr; /**< Which address is watched. */ + uint32_t size; /**< How large is the watched segment. */ + uint32_t kind; /**< Bitmask of enum avr_gdb_watch_type values. */ + } points[WATCH_LIMIT]; +} avr_gdb_watchpoints_t; + typedef struct avr_gdb_t { avr_t * avr; int listen; // listen socket int s; // current gdb connection - uint32_t watchmap; - struct { - avr_flashaddr_t pc; - uint32_t len; - int kind; - } watch[32]; + avr_gdb_watchpoints_t breakpoints; + avr_gdb_watchpoints_t watchpoints; } avr_gdb_t; +/** + * Returns the index of the watchpoint if found, -1 otherwise. + */ +static int gdb_watch_find(const avr_gdb_watchpoints_t * w, uint32_t addr) +{ + for (int i = 0; i < w->len; i++) { + if (w->points[i].addr > addr) { + return -1; + } else if (w->points[i].addr == addr) { + return i; + } + } + + return -1; +} + +/** + * Contrary to gdb_watch_find, this actually checks the address against + * a watched memory _range_. + */ +static int gdb_watch_find_range(const avr_gdb_watchpoints_t * w, uint32_t addr) +{ + for (int i = 0; i < w->len; i++) { + if (w->points[i].addr > addr) { + return -1; + } else if (w->points[i].addr <= addr && addr < w->points[i].addr + w->points[i].size) { + return i; + } + } + + return -1; +} + +/** + * Returns -1 on error, 0 otherwise. + */ +static int gdb_watch_add_or_update(avr_gdb_watchpoints_t * w, enum avr_gdb_watch_type kind, uint32_t addr, + uint32_t size) +{ + /* If the watchpoint exists, update it. */ + int i = gdb_watch_find(w, addr); + if (i != -1) { + w->points[i].size = size; + w->points[i].kind |= kind; + return 0; + } + + /* Otherwise add it. */ + if (w->len == WATCH_LIMIT) { + return -1; + } + + /* Find the insertion point. */ + for (i = 0; i < w->len; i++) { + if (w->points[i].addr > addr) { + break; + } + } + + w->len++; + + /* Make space for new element. */ + for (int j = i + 1; j < w->len; j++) { + w->points[j] = w->points[j - 1]; + } + + /* Insert it. */ + w->points[i].kind = kind; + w->points[i].addr = addr; + w->points[i].size = size; + + return 0; +} + +/** + * Returns -1 on error or if the specified point does not exist, 0 otherwise. + */ +static int gdb_watch_rm(avr_gdb_watchpoints_t * w, enum avr_gdb_watch_type kind, uint32_t addr) +{ + int i = gdb_watch_find(w, addr); + if (i == -1) { + return -1; + } + + w->points[i].kind &= ~kind; + if (w->points[i].kind) { + return 0; + } + + for (i = i + 1; i < w->len; i++) { + w->points[i - 1] = w->points[i]; + } + + w->len--; + + return 0; +} + +static void gdb_watch_clear(avr_gdb_watchpoints_t * w) +{ + w->len = 0; +} + static void gdb_send_reply(avr_gdb_t * g, char * cmd) { uint8_t reply[1024]; @@ -78,37 +185,17 @@ static void gdb_send_quick_status(avr_gdb_t * g, uint8_t signal) gdb_send_reply(g, cmd); } -static int gdb_change_breakpoint(avr_gdb_t * g, int set, int kind, avr_flashaddr_t addr, uint32_t len) +static int gdb_change_breakpoint(avr_gdb_watchpoints_t * w, int set, enum avr_gdb_watch_type kind, + uint32_t addr, uint32_t size) { - DBG(printf("set %d kind %d addr %08x len %d (map %08x)\n", set, kind, addr, len, g->watchmap);) + DBG(printf("set %d kind %d addr %08x len %d\n", set, kind, addr, len);) + if (set) { - if (g->watchmap == 0xffffffff) - return -1; // map full - - // check to see if it exists - for (int i = 0; i < 32; i++) - if ((g->watchmap & (1 << i)) && g->watch[i].pc == addr) { - g->watch[i].len = len; - return 0; - } - for (int i = 0; i < 32; i++) - if (!(g->watchmap & (1 << i))) { - g->watchmap |= (1 << i); - g->watch[i].len = len; - g->watch[i].pc = addr; - g->watch[i].kind = kind; - return 0; - } + return gdb_watch_add_or_update(w, kind, addr, size); } else { - for (int i = 0; i < 32; i++) - if ((g->watchmap & (1 << i)) && g->watch[i].pc == addr) { - g->watchmap &= ~(1 << i); - g->watch[i].len = 0; - g->watch[i].pc = 0; - g->watch[i].kind = 0; - return 0; - } + return gdb_watch_rm(w, kind, addr); } + return -1; } @@ -120,6 +207,7 @@ static int gdb_write_register(avr_gdb_t * g, int regi, uint8_t * src) return 1; case 32: g->avr->data[R_SREG] = *src; + SET_SREG_FROM(g->avr, *src); return 1; case 33: g->avr->data[R_SPL] = src[0]; @@ -138,8 +226,11 @@ static int gdb_read_register(avr_gdb_t * g, int regi, char * rep) case 0 ... 31: sprintf(rep, "%02x", g->avr->data[regi]); break; - case 32: - sprintf(rep, "%02x", g->avr->data[R_SREG]); + case 32: { + uint8_t sreg; + READ_SREG_INTO(g->avr, sreg); + sprintf(rep, "%02x", sreg); + } break; case 33: sprintf(rep, "%02x%02x", g->avr->data[R_SPL], g->avr->data[R_SPH]); @@ -213,11 +304,11 @@ static void gdb_handle_command(avr_gdb_t * g, char * cmd) } else if (addr >= 0x800000 && (addr - 0x800000) == avr->ramend+1 && len == 2) { // Allow GDB to read a value just after end of stack. // This is necessary to make instruction stepping work when stack is empty - printf("GDB read just past end of stack %08x, %08x; returning zero\n", addr, len); + AVR_LOG(avr, LOG_TRACE, "GDB: read just past end of stack %08x, %08x; returning zero\n", addr, len); gdb_send_reply(g, "0000"); break; } else { - printf("read memory error %08x, %08x (ramend %04x)\n", addr, len, avr->ramend+1); + AVR_LOG(avr, LOG_ERROR, "GDB: read memory error %08x, %08x (ramend %04x)\n", addr, len, avr->ramend+1); gdb_send_reply(g, "E01"); break; } @@ -249,7 +340,7 @@ static void gdb_handle_command(avr_gdb_t * g, char * cmd) avr_ioctl(avr, AVR_IOCTL_EEPROM_SET, &ee); gdb_send_reply(g, "OK"); } else { - printf("write memory error %08x, %08x\n", addr, len); + AVR_LOG(avr, LOG_ERROR, "GDB: write memory error %08x, %08x\n", addr, len); gdb_send_reply(g, "E01"); } } break; @@ -266,29 +357,41 @@ static void gdb_handle_command(avr_gdb_t * g, char * cmd) case 'Z': // set clear break/watchpoint case 'z': { uint32_t kind, addr, len; + int set = (command == 'Z'); sscanf(cmd, "%d,%x,%x", &kind, &addr, &len); -// printf("breakbpoint %d, %08x, %08x\n", kind, addr, len); +// printf("breakpoint %d, %08x, %08x\n", kind, addr, len); switch (kind) { case 0: // software breakpoint case 1: // hardware breakpoint - if (addr <= avr->flashend) { - if (gdb_change_breakpoint(g, command == 'Z', kind, addr, len)) - gdb_send_reply(g, "E01"); - else - gdb_send_reply(g, "OK"); - } else - gdb_send_reply(g, "E01"); // out of flash address + if (addr > avr->flashend || + gdb_change_breakpoint(&g->breakpoints, set, 1 << kind, addr, len) == -1) { + gdb_send_reply(g, "E01"); + break; + } + + gdb_send_reply(g, "OK"); break; - // TODO case 2: // write watchpoint case 3: // read watchpoint case 4: // access watchpoint + /* Mask out the offset applied to SRAM addresses. */ + addr &= ~0x800000; + if (addr > avr->ramend || + gdb_change_breakpoint(&g->watchpoints, set, 1 << kind, addr, len) == -1) { + gdb_send_reply(g, "E01"); + break; + } + + gdb_send_reply(g, "OK"); + break; default: gdb_send_reply(g, ""); - } + break; + } } break; default: gdb_send_reply(g, ""); + break; } } @@ -333,7 +436,8 @@ static int gdb_network_handler(avr_gdb_t * g, uint32_t dosleep) if (r == 0) { printf("%s connection closed\n", __FUNCTION__); close(g->s); - g->watchmap = 0; // clear breakpoints + gdb_watch_clear(&g->breakpoints); + gdb_watch_clear(&g->watchpoints); g->avr->state = cpu_Running; // resume g->s = -1; return 1; @@ -373,21 +477,46 @@ static int gdb_network_handler(avr_gdb_t * g, uint32_t dosleep) return 1; } +/** + * If an applicable watchpoint exists for addr, stop the cpu and send a status report. + * type is one of AVR_GDB_WATCH_READ, AVR_GDB_WATCH_WRITE depending on the type of access. + */ +void avr_gdb_handle_watchpoints(avr_t * avr, uint16_t addr, enum avr_gdb_watch_type type) +{ + avr_gdb_t *g = avr->gdb; + + int i = gdb_watch_find_range(&g->watchpoints, addr); + if (i == -1) { + return; + } + + int kind = g->watchpoints.points[i].kind; + if (kind & type) { + /* Send gdb reply (see GDB user manual appendix E.3). */ + char cmd[78]; + sprintf(cmd, "T%02x20:%02x;21:%02x%02x;22:%02x%02x%02x00;%s:%06x;", + 5, g->avr->data[R_SREG], + g->avr->data[R_SPL], g->avr->data[R_SPH], + g->avr->pc & 0xff, (g->avr->pc>>8)&0xff, (g->avr->pc>>16)&0xff, + kind & AVR_GDB_WATCH_ACCESS ? "awatch" : kind & AVR_GDB_WATCH_WRITE ? "watch" : "rwatch", + addr | 0x800000); + gdb_send_reply(g, cmd); + + avr->state = cpu_Stopped; + } +} + int avr_gdb_processor(avr_t * avr, int sleep) { if (!avr || !avr->gdb) return 0; avr_gdb_t * g = avr->gdb; - if (g->watchmap && avr->state == cpu_Running) { - for (int i = 0; i < 32; i++) - if ((g->watchmap & (1 << i)) && g->watch[i].pc == avr->pc) { - DBG(printf("avr_gdb_processor hit breakpoint at %08x\n", avr->pc);) - gdb_send_quick_status(g, 0); - avr->state = cpu_Stopped; - } - } - if (avr->state == cpu_StepDone) { + if (avr->state == cpu_Running && gdb_watch_find(&g->breakpoints, avr->pc) != -1) { + DBG(printf("avr_gdb_processor hit breakpoint at %08x\n", avr->pc);) + gdb_send_quick_status(g, 0); + avr->state = cpu_Stopped; + } else if (avr->state == cpu_StepDone) { gdb_send_quick_status(g, 0); avr->state = cpu_Stopped; } @@ -403,20 +532,25 @@ int avr_gdb_init(avr_t * avr) avr->gdb = NULL; + if ( network_init() ) { + AVR_LOG(avr, LOG_ERROR, "GDB: Can't initialize network"); + return -1; + } + if ((g->listen = socket(PF_INET, SOCK_STREAM, 0)) < 0) { - fprintf(stderr, "Can't create socket: %s", strerror(errno)); + AVR_LOG(avr, LOG_ERROR, "GDB: Can't create socket: %s", strerror(errno)); return -1; } - int i = 1; - setsockopt(g->listen, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); + int optval = 1; + setsockopt(g->listen, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); struct sockaddr_in address = { 0 }; address.sin_family = AF_INET; address.sin_port = htons (avr->gdb_port); if (bind(g->listen, (struct sockaddr *) &address, sizeof(address))) { - fprintf(stderr, "Can not bind socket: %s", strerror(errno)); + AVR_LOG(avr, LOG_ERROR, "GDB: Can not bind socket: %s", strerror(errno)); return -1; } if (listen(g->listen, 1)) { @@ -433,3 +567,14 @@ int avr_gdb_init(avr_t * avr) return 0; } + +void avr_deinit_gdb(avr_t * avr) +{ + if (avr->gdb->listen != -1) + close(avr->gdb->listen); + if (avr->gdb->s != -1) + close(avr->gdb->s); + free(avr->gdb); + + network_release(); +}