X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=simavr%2Fsim%2Fsim_gdb.c;h=faa016f3034228cd4875489c67f34f784581c805;hb=dc3747065c3e00715808918b54337d99abba6fc3;hp=43225c8b2d7a9c1534e792286b5023d685e88ec4;hpb=1898613e4ff3926250bc98e9917fc57b078f48f0;p=simavr diff --git a/simavr/sim/sim_gdb.c b/simavr/sim/sim_gdb.c index 43225c8..faa016f 100644 --- a/simavr/sim/sim_gdb.c +++ b/simavr/sim/sim_gdb.c @@ -32,49 +32,135 @@ #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 { - uint32_t pc; - uint32_t len; - int kind; - } watch[32]; + avr_gdb_watchpoints_t breakpoints; + avr_gdb_watchpoints_t watchpoints; } avr_gdb_t; - // decode line text hex to binary -int read_hex_string(const char * src, uint8_t * buffer, int maxlen) + +/** + * 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) { - uint8_t * dst = buffer; - int ls = 0; - uint8_t b = 0; - while (*src && maxlen--) { - char c = *src++; - switch (c) { - case 'a' ... 'f': b = (b << 4) | (c - 'a' + 0xa); break; - case 'A' ... 'F': b = (b << 4) | (c - 'A' + 0xa); break; - case '0' ... '9': b = (b << 4) | (c - '0'); break; - default: - if (c > ' ') { - fprintf(stderr, "%s: huh '%c' (%s)\n", __FUNCTION__, c, src); - return -1; - } - continue; - } - if (ls & 1) { - *dst++ = b; b = 0; - } - ls++; - } - - return dst - buffer; + w->len = 0; } static void gdb_send_reply(avr_gdb_t * g, char * cmd) @@ -103,37 +189,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, uint32_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; } @@ -144,11 +210,12 @@ static int gdb_write_register(avr_gdb_t * g, int regi, uint8_t * src) g->avr->data[regi] = *src; return 1; case 32: - g->avr->data[R_SREG] = * src; + g->avr->data[R_SREG] = *src; + SET_SREG_FROM(g->avr, *src); return 1; case 33: - g->avr->data[R_SPL] = *src++; - g->avr->data[R_SPH] = *src++; + g->avr->data[R_SPL] = src[0]; + g->avr->data[R_SPH] = src[1]; return 2; case 34: g->avr->pc = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); @@ -163,8 +230,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]); @@ -218,10 +288,11 @@ static void gdb_handle_command(avr_gdb_t * g, char * cmd) gdb_send_reply(g, "OK"); } break; case 'm': { // read memory - uint32_t addr, len; + avr_flashaddr_t addr; + uint32_t len; sscanf(cmd, "%x,%x", &addr, &len); uint8_t * src = NULL; - if (addr < 0xffff) { + if (addr < avr->flashend) { src = avr->flash + addr; } else if (addr >= 0x800000 && (addr - 0x800000) <= avr->ramend) { src = avr->data + addr - 0x800000; @@ -230,10 +301,18 @@ static void gdb_handle_command(avr_gdb_t * g, char * cmd) avr_ioctl(avr, AVR_IOCTL_EEPROM_GET, &ee); if (ee.ee) src = ee.ee; - else + else { gdb_send_reply(g, "E01"); + break; + } + } 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 + 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; } @@ -265,7 +344,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; @@ -275,32 +354,48 @@ static void gdb_handle_command(avr_gdb_t * g, char * cmd) case 's': { // step avr->state = cpu_Step; } break; + case 'r': { // deprecated, suggested for AVRStudio compatibility + avr->state = cpu_StepDone; + avr_reset(avr); + } break; 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; } } @@ -337,7 +432,7 @@ static int gdb_network_handler(avr_gdb_t * g, uint32_t dosleep) printf("%s connection opened\n", __FUNCTION__); } - if (FD_ISSET(g->s, &read_set)) { + if (g->s != -1 && FD_ISSET(g->s, &read_set)) { uint8_t buffer[1024]; ssize_t r = recv(g->s, buffer, sizeof(buffer)-1, 0); @@ -345,7 +440,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; @@ -385,21 +481,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; } @@ -416,7 +537,7 @@ int avr_gdb_init(avr_t * avr) avr->gdb = NULL; 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; } @@ -428,7 +549,7 @@ int avr_gdb_init(avr_t * avr) 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)) { @@ -439,6 +560,18 @@ int avr_gdb_init(avr_t * avr) g->avr = avr; g->s = -1; avr->gdb = g; + // change default run behaviour to use the slightly slower versions + avr->run = avr_callback_run_gdb; + avr->sleep = avr_callback_sleep_gdb; 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); +}