core: Deinitialize GDB in avr_terminate()
[simavr] / simavr / sim / sim_gdb.c
index a69bbf1..faa016f 100644 (file)
@@ -32,6 +32,7 @@
 #include <poll.h>
 #include <pthread.h>
 #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"
@@ -55,6 +56,7 @@ typedef struct avr_gdb_t {
        int             s;              // current gdb connection
 
        avr_gdb_watchpoints_t breakpoints;
+       avr_gdb_watchpoints_t watchpoints;
 } avr_gdb_t;
 
 
@@ -64,7 +66,26 @@ typedef struct avr_gdb_t {
 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) {
+               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;
                }
        }
@@ -91,12 +112,25 @@ static int gdb_watch_add_or_update(avr_gdb_watchpoints_t * w, enum avr_gdb_watch
                return -1;
        }
 
-       w->points[w->len].kind = kind;
-       w->points[w->len].addr = addr;
-       w->points[w->len].size = size;
+       /* 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;
 }
 
@@ -177,6 +211,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];
@@ -195,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]);
@@ -270,11 +308,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;
                        }
@@ -306,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;
@@ -337,13 +375,23 @@ static void gdb_handle_command(avr_gdb_t * g, char * cmd)
 
                                        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, "");
@@ -393,6 +441,7 @@ static int gdb_network_handler(avr_gdb_t * g, uint32_t dosleep)
                        printf("%s connection closed\n", __FUNCTION__);
                        close(g->s);
                        gdb_watch_clear(&g->breakpoints);
+                       gdb_watch_clear(&g->watchpoints);
                        g->avr->state = cpu_Running;    // resume
                        g->s = -1;
                        return 1;
@@ -432,6 +481,35 @@ 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)
@@ -459,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;
        }
 
@@ -471,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)) {
@@ -488,3 +566,12 @@ 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);
+}