diff options
author | aiju <devnull@localhost> | 2017-06-12 19:03:07 +0000 |
---|---|---|
committer | aiju <devnull@localhost> | 2017-06-12 19:03:07 +0000 |
commit | 773be02aa18095e857c6659416d84951ceb60d41 (patch) | |
tree | 26daede4230e14d46d42efbcd6eab8e68adc7687 /sys/src/9/port/devproc.c | |
parent | 1cfa405d0a272cbd7df22d4b9767eb57e21cc21f (diff) |
kernel: add support for hardware watchpoints
Diffstat (limited to 'sys/src/9/port/devproc.c')
-rw-r--r-- | sys/src/9/port/devproc.c | 162 |
1 files changed, 157 insertions, 5 deletions
diff --git a/sys/src/9/port/devproc.c b/sys/src/9/port/devproc.c index b6b51959c..d960d0eec 100644 --- a/sys/src/9/port/devproc.c +++ b/sys/src/9/port/devproc.c @@ -34,6 +34,7 @@ enum Qwait, Qprofile, Qsyscall, + Qwatchpt, }; enum @@ -101,6 +102,7 @@ Dirtab procdir[] = "wait", {Qwait}, 0, 0400, "profile", {Qprofile}, 0, 0400, "syscall", {Qsyscall}, 0, 0400, + "watchpt", {Qwatchpt}, 0, 0600, }; static @@ -181,6 +183,8 @@ profclock(Ureg *ur, Timer *) } } +static int lenwatchpt(Proc *); + static int procgen(Chan *c, char *name, Dirtab *tab, int, int s, Dir *dp) { @@ -265,6 +269,11 @@ procgen(Chan *c, char *name, Dirtab *tab, int, int s, Dir *dp) } break; } + switch(QID(tab->qid)){ + case Qwatchpt: + len = lenwatchpt(p); + break; + } mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE); devdir(c, qid, tab->name, len, p->user, perm, dp); @@ -334,19 +343,22 @@ nonone(Proc *p) error(Eperm); } +static void clearwatchpt(Proc *p); + static Chan* -procopen(Chan *c, int omode) +procopen(Chan *c, int omode0) { Proc *p; Pgrp *pg; Chan *tc; int pid; + int omode; if(c->qid.type & QTDIR) - return devopen(c, omode, 0, 0, procgen); + return devopen(c, omode0, 0, 0, procgen); if(QID(c->qid) == Qtrace){ - if (omode != OREAD) + if (omode0 != OREAD) error(Eperm); lock(&tlock); if (waserror()){ @@ -366,7 +378,7 @@ procopen(Chan *c, int omode) unlock(&tlock); poperror(); - c->mode = openmode(omode); + c->mode = openmode(omode0); c->flag |= COPEN; c->offset = 0; return c; @@ -382,7 +394,7 @@ procopen(Chan *c, int omode) if(p->pid != pid) error(Eprocdied); - omode = openmode(omode); + omode = openmode(omode0); switch(QID(c->qid)){ case Qtext: @@ -425,6 +437,7 @@ procopen(Chan *c, int omode) case Qfpregs: case Qsyscall: case Qppid: + case Qwatchpt: nonone(p); break; @@ -454,6 +467,19 @@ procopen(Chan *c, int omode) error(Eprocdied); tc = devopen(c, omode, 0, 0, procgen); + if(waserror()){ + cclose(tc); + nexterror(); + } + + switch(QID(c->qid)){ + case Qwatchpt: + if((omode0 & OTRUNC) != 0) + clearwatchpt(p); + break; + } + + poperror(); qunlock(&p->debug); poperror(); @@ -701,6 +727,119 @@ readfd1(Chan *c, Proc *p, char *buf, int nbuf) } /* + * setupwatchpts(Proc *p, Watchpt *wp, int nwp) is defined for all arches separately. + * It tests whether wp is a valid set of watchpoints and errors out otherwise. + * If and only if they are valid, it sets up all watchpoints (clearing any preexisting ones). + * This is to make sure that failed writes to watchpt don't touch the existing watchpoints. + */ + +static void +clearwatchpt(Proc *p) +{ + setupwatchpts(p, nil, 0); + free(p->watchpt); + p->watchpt = nil; + p->nwatchpt = 0; +} + +static int +lenwatchpt(Proc *pr) +{ + /* careful, not holding debug lock */ + return pr->nwatchpt * (10 + 4 * sizeof(uintptr)); +} + +static int +readwatchpt(Proc *pr, char *buf, int nbuf) +{ + char *p, *e; + Watchpt *w; + + p = buf; + e = buf + nbuf; + /* careful, length has to match lenwatchpt() */ + for(w = pr->watchpt; w < pr->watchpt + pr->nwatchpt; w++) + p = seprint(p, e, sizeof(uintptr) == 8 ? "%c%c%c %#.16p %#.16p\n" : "%c%c%c %#.8p %#.8p\n", + (w->type & WATCHRD) != 0 ? 'r' : '-', + (w->type & WATCHWR) != 0 ? 'w' : '-', + (w->type & WATCHEX) != 0 ? 'x' : '-', + (void *) w->addr, (void *) w->len); + return p - buf; +} + +static int +writewatchpt(Proc *pr, char *buf, int nbuf, uvlong offset) +{ + char *p, *q, *e; + char line[256], *f[4]; + Watchpt *wp, *wq; + int rc, nwp, nwp0; + uvlong x; + + p = buf; + e = buf + nbuf; + if(offset != 0) + nwp0 = pr->nwatchpt; + else + nwp0 = 0; + nwp = 0; + for(q = p; q < e; q++) + nwp += *q == '\n'; + if(nwp > 65536) error(Egreg); + wp = malloc((nwp0+nwp) * sizeof(Watchpt)); + if(wp == nil) error(Enomem); + if(waserror()){ + free(wp); + nexterror(); + } + if(nwp0 > 0) + memmove(wp, pr->watchpt, sizeof(Watchpt) * nwp0); + for(wq = wp + nwp0;;){ + q = memchr(p, '\n', e - p); + if(q == nil) + break; + if(q - p > sizeof(line) - 1) + error("line too long"); + memmove(line, p, q - p); + line[q - p] = 0; + p = q + 1; + + rc = tokenize(line, f, nelem(f)); + if(rc == 0) continue; + if(rc != 3) + error("wrong number of fields"); + for(q = f[0]; *q != 0; q++) + switch(*q){ + case 'r': if((wq->type & WATCHRD) != 0) goto tinval; wq->type |= WATCHRD; break; + case 'w': if((wq->type & WATCHWR) != 0) goto tinval; wq->type |= WATCHWR; break; + case 'x': if((wq->type & WATCHEX) != 0) goto tinval; wq->type |= WATCHEX; break; + case '-': break; + default: tinval: error("invalid type"); + } + x = strtoull(f[1], &q, 0); + if(f[1] == q || *q != 0 || x != (uintptr) x) error("invalid address"); + wq->addr = x; + x = strtoull(f[2], &q, 0); + if(f[2] == q || *q != 0 || x != (uintptr) x) error("invalid length"); + wq->len = x; + if(!okaddr(wq->addr, wq->len, 0)) error("bad address"); + wq++; + } + nwp = wq - (wp + nwp0); + if(nwp == 0 && nwp0 == pr->nwatchpt){ + poperror(); + free(wp); + return p - buf; + } + setupwatchpts(pr, wp, nwp0 + nwp); + poperror(); + free(pr->watchpt); + pr->watchpt = wp; + pr->nwatchpt = nwp0 + nwp; + return p - buf; +} + +/* * userspace can't pass negative file offset for a * 64 bit kernel address, so we use 63 bit and sign * extend to 64 bit. @@ -1006,6 +1145,16 @@ procread(Chan *c, void *va, long n, vlong off) case Qppid: return readnum(offset, va, n, p->parentpid, NUMSIZE); + + case Qwatchpt: + eqlock(&p->debug); + j = readwatchpt(p, statbuf, sizeof(statbuf)); + qunlock(&p->debug); + if(offset >= j) + return 0; + if(offset+n > j) + n = j - offset; + goto statbufread; } error(Egreg); @@ -1125,6 +1274,9 @@ procwrite(Chan *c, void *va, long n, vlong off) if(p->noteid != id) error(Ebadarg); break; + case Qwatchpt: + writewatchpt(p, va, n, off); + break; default: print("unknown qid in procwrite\n"); error(Egreg); |