#include #include #include #include "gdb.h" #define BUFSIZE (64*1024) static char Echecksum[] = "bad checksum"; static char Eescape[] = "bad escape"; static char Enotsup[] = "not supported"; static char Epacket[] = "bad packet"; static char Erunlen[] = "bad run-length"; static char Etimeout[] = "timed out"; static int fd; static Biobuf bin; static int acked; static int errored; static int notified; int chattygdb; static int checksum(char *s) { int sum; sum = 0; while(*s != '\0') sum += *s++; return sum % 256; } int gdbsend(char *s) { long n; if(chattygdb) fprint(2, "<- %s\n", s); n = fprint(fd, s); if(n < 0) sysfatal("%r"); return n; } int gdbsendp(char *s) { long n; if(chattygdb) fprint(2, "<- %s\n", s); n = fprint(fd, "$%s#%02ux", s, checksum(s)); if(n < 0) sysfatal("%r"); return n; } int gdback(void) { return gdbsend("+"); } int gdbresend(void) { return gdbsend("-"); } static int recv(char *buf, int len) { char *p, *e; int state, sum, c; static char cksum[2+1]; enum { None, Data, Checksum }; sum = 0; state = None; acked = errored = notified = 0; p = buf; e = buf + len; while(p < e) switch(state){ case None: switch(Bgetc(&bin)){ case -1: sysfatal("%r"); case '%': notified++; /* fall through */ case '$': state = Data; break; case '+': acked++; return 0; case '-': acked++; return -1; } break; case Data: switch(c = Bgetc(&bin)){ case -1: sysfatal("%r"); case '#': state = Checksum; break; default: *p++ = c; sum += c; } break; case Checksum: if(Bread(&bin, cksum, sizeof cksum - 1) < 0) sysfatal("%r"); if(strtoul(cksum, nil, 16) != sum%256){ werrstr(Echecksum); return -1; } return p - buf; } werrstr(Epacket); return -1; } static int expand(char *s, char *buf, int len) { char *p, *e, c; int rlen; p = buf; e = buf + len; while(*s != '\0') switch(c = *s++){ case '*': if(*s == '\0' || p == buf){ werrstr(Erunlen); return -1; } rlen = *s++ - 29; while(rlen--) p = seprint(p, e, "%c", s[-3]); break; case '}': if(*s == '\0'){ werrstr(Eescape); return -1; } c = *s++ ^ 0x20; /* fall through */ default: p = seprint(p, e, "%c", c); } return p - buf; } int gdbrecv(char *buf, int len) { int n; static char buf2[BUFSIZE]; /* * Unfortunately, some targets persist in use of run-length * encoding. We first read the packet to a holding buffer and * expand to the user-specified buffer. */ n = recv(buf2, sizeof buf2); if(n < 0) return -1; if(acked) return 0; buf2[n] = '\0'; if(chattygdb) fprint(2, "-> %s\n", buf2); n = expand(buf2, buf, len); if(n < 0) return -1; else if(n == 0){ /* $#00 */ werrstr(Enotsup); errored++; } else if(*buf == 'E'){ werrstr(buf); errored++; } return n; } int wasack(void) { return acked; } int waserror(void) { return errored; } int wasnotify(void) { return notified; } int gdbcommand(char *s, char *buf, int len) { int i; long n; enum { Nretries = 10 }; i = 0; again: for(; i < Nretries; ++i) if(gdbsendp(s) > 0) if(gdbrecv(buf, len) == 0) if(wasack()) break; for(; i < Nretries; ++i){ n = gdbrecv(buf, len); if(n < 0) gdbresend(); else{ gdback(); if(wasnotify()) goto again; return n; } } werrstr(Etimeout); return -1; } int gdbopen(char *target) { /* * Remote targets support both serial and network access. We * assume serial will either be served by consolefs(4) or * uart(3), hence we have a file to open. */ if(access(target, AEXIST) == 0) fd = open(target, ORDWR); else fd = dial(target, nil, nil, nil); Binit(&bin, fd, OREAD); return fd; } void gdbclose(void) { Bterm(&bin); close(fd); } void gdbkill(void) { /* * The kill request (understandably) may not provide a * response. We short circuit gdbcommand to send the packet. * It is not clear that waiting for an acknowlegement is * worthwhile; a resend will fail miserably anyway. */ gdbsendp("k"); gdbclose(); }