diff options
Diffstat (limited to 'os/port')
101 files changed, 46045 insertions, 0 deletions
diff --git a/os/port/NOTICE b/os/port/NOTICE new file mode 100644 index 00000000..3b325e40 --- /dev/null +++ b/os/port/NOTICE @@ -0,0 +1,18 @@ +The following files are subject to the Lucent Public License 1.02: + +devbridge.c +devds.c +devdup.c +devloopback.c +devpci.c (portions) +devpnp.c +devsd.c +devuart.c +edf.c +edf.h +ethermii.c +ethermii.h +log.c +mul64fract.c +rdb.c +sd.h diff --git a/os/port/alarm.c b/os/port/alarm.c new file mode 100644 index 00000000..4f7662c3 --- /dev/null +++ b/os/port/alarm.c @@ -0,0 +1,41 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +Talarm talarm; + +/* + * called every clock tick + */ +void +checkalarms(void) +{ + Proc *p; + ulong now; + + now = MACHP(0)->ticks; + + if(talarm.list == 0 || canlock(&talarm) == 0) + return; + + for(;;) { + p = talarm.list; + if(p == 0) + break; + + if(p->twhen == 0) { + talarm.list = p->tlink; + p->trend = 0; + continue; + } + if(now < p->twhen) + break; + wakeup(p->trend); + talarm.list = p->tlink; + p->trend = 0; + } + + unlock(&talarm); +} diff --git a/os/port/alloc.c b/os/port/alloc.c new file mode 100644 index 00000000..810560a4 --- /dev/null +++ b/os/port/alloc.c @@ -0,0 +1,963 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "interp.h" + +#define left u.s.bhl +#define right u.s.bhr +#define fwd u.s.bhf +#define prev u.s.bhv +#define parent u.s.bhp + +#define RESERVED 256*1024 + +struct Pool +{ + char* name; + int pnum; + ulong maxsize; + int quanta; + int chunk; + ulong ressize; + ulong cursize; + ulong arenasize; + ulong hw; + Lock l; + Bhdr* root; + Bhdr* chain; + ulong nalloc; + ulong nfree; + int nbrk; + int lastfree; + int warn; + void (*move)(void*, void*); +}; + +static +struct +{ + int n; + Pool pool[MAXPOOL]; + // Lock l; +} table = { + 3, + { + { "main", 0, 4*1024*1024, 31, 128*1024, 15*256*1024 }, + { "heap", 1, 16*1024*1024, 31, 128*1024, 15*1024*1024 }, + { "image", 2, 8*1024*1024, 31, 300*1024, 15*512*1024 }, + } +}; + +Pool* mainmem = &table.pool[0]; +Pool* heapmem = &table.pool[1]; +Pool* imagmem = &table.pool[2]; + +static void _auditmemloc(char *, void *); +void (*auditmemloc)(char *, void *) = _auditmemloc; +static void _poolfault(void *, char *, ulong); +void (*poolfault)(void *, char *, ulong) = _poolfault; + +/* non tracing + * +enum { + Npadlong = 0, + MallocOffset = 0, + ReallocOffset = 0 +}; + * + */ + +/* tracing */ +enum { + Npadlong = 2, + MallocOffset = 0, + ReallocOffset = 1 +}; + +int +memusehigh(void) +{ + return mainmem->cursize > mainmem->ressize || + heapmem->cursize > heapmem->ressize || + imagmem->cursize > imagmem->ressize; +} + +void +poolimmutable(void *v) +{ + Bhdr *b; + + D2B(b, v); + b->magic = MAGIC_I; +} + +void +poolmutable(void *v) +{ + Bhdr *b; + + D2B(b, v); + b->magic = MAGIC_A; + ((Heap*)v)->color = mutator; +} + +char* +poolname(Pool *p) +{ + return p->name; +} + +Bhdr* +poolchain(Pool *p) +{ + return p->chain; +} + +void +pooldel(Pool *p, Bhdr *t) +{ + Bhdr *s, *f, *rp, *q; + + if(t->parent == nil && p->root != t) { + t->prev->fwd = t->fwd; + t->fwd->prev = t->prev; + return; + } + + if(t->fwd != t) { + f = t->fwd; + s = t->parent; + f->parent = s; + if(s == nil) + p->root = f; + else { + if(s->left == t) + s->left = f; + else + s->right = f; + } + + rp = t->left; + f->left = rp; + if(rp != nil) + rp->parent = f; + rp = t->right; + f->right = rp; + if(rp != nil) + rp->parent = f; + + t->prev->fwd = t->fwd; + t->fwd->prev = t->prev; + return; + } + + if(t->left == nil) + rp = t->right; + else { + if(t->right == nil) + rp = t->left; + else { + f = t; + rp = t->right; + s = rp->left; + while(s != nil) { + f = rp; + rp = s; + s = rp->left; + } + if(f != t) { + s = rp->right; + f->left = s; + if(s != nil) + s->parent = f; + s = t->right; + rp->right = s; + if(s != nil) + s->parent = rp; + } + s = t->left; + rp->left = s; + s->parent = rp; + } + } + q = t->parent; + if(q == nil) + p->root = rp; + else { + if(t == q->left) + q->left = rp; + else + q->right = rp; + } + if(rp != nil) + rp->parent = q; +} + +void +pooladd(Pool *p, Bhdr *q) +{ + int size; + Bhdr *tp, *t; + + q->magic = MAGIC_F; + + q->left = nil; + q->right = nil; + q->parent = nil; + q->fwd = q; + q->prev = q; + + t = p->root; + if(t == nil) { + p->root = q; + return; + } + + size = q->size; + + tp = nil; + while(t != nil) { + if(size == t->size) { + q->prev = t->prev; + q->prev->fwd = q; + q->fwd = t; + t->prev = q; + return; + } + tp = t; + if(size < t->size) + t = t->left; + else + t = t->right; + } + + q->parent = tp; + if(size < tp->size) + tp->left = q; + else + tp->right = q; +} + +void +poolsummary(void) +{ + int x = 0; + char buf[400]; + + print("\n"); + print(" cursize maxsize hw nalloc nfree nbrk max name\n"); + + x=poolread( buf, sizeof buf - 1, x ); + buf[x] = 0; + putstrn(buf, x); + print("\n"); +} + +void* +poolalloc(Pool *p, ulong asize) +{ + Bhdr *q, *t; + int alloc, ldr, ns, frag; + int osize, size; + Prog *prog; + + if(asize >= 1024*1024*1024) /* for sanity and to avoid overflow */ + return nil; + if(p->cursize > p->ressize && (prog = currun()) != nil && prog->flags&Prestricted) + return nil; + size = asize; + osize = size; + size = (size + BHDRSIZE + p->quanta) & ~(p->quanta); + + ilock(&p->l); + p->nalloc++; + + t = p->root; + q = nil; + while(t) { + if(t->size == size) { + t = t->fwd; + pooldel(p, t); + t->magic = MAGIC_A; + p->cursize += t->size; + if(p->cursize > p->hw) + p->hw = p->cursize; + iunlock(&p->l); + return B2D(t); + } + if(size < t->size) { + q = t; + t = t->left; + } + else + t = t->right; + } + if(q != nil) { + pooldel(p, q); + q->magic = MAGIC_A; + frag = q->size - size; + if(frag < (size>>2) && frag < 0x8000) { + p->cursize += q->size; + if(p->cursize > p->hw) + p->hw = p->cursize; + iunlock(&p->l); + return B2D(q); + } + /* Split */ + ns = q->size - size; + q->size = size; + B2T(q)->hdr = q; + t = B2NB(q); + t->size = ns; + B2T(t)->hdr = t; + pooladd(p, t); + p->cursize += q->size; + if(p->cursize > p->hw) + p->hw = p->cursize; + iunlock(&p->l); + return B2D(q); + } + + ns = p->chunk; + if(size > ns) + ns = size; + ldr = p->quanta+1; + + alloc = ns+ldr+ldr; + p->arenasize += alloc; + if(p->arenasize > p->maxsize) { + p->arenasize -= alloc; + ns = p->maxsize-p->arenasize-ldr-ldr; + ns &= ~p->quanta; + if (ns < size) { + if(poolcompact(p)) { + iunlock(&p->l); + return poolalloc(p, osize); + } + + iunlock(&p->l); + if(p->warn) + return nil; + p->warn = 1; + if (p != mainmem || ns > 512) + print("arena too large: %s size %d cursize %lud arenasize %lud maxsize %lud, alloc = %d\n", p->name, osize, p->cursize, p->arenasize, p->maxsize, alloc); + return nil; + } + alloc = ns+ldr+ldr; + p->arenasize += alloc; + } + + p->nbrk++; + t = xalloc(alloc); + if(t == nil) { + p->nbrk--; + iunlock(&p->l); + return nil; + } + /* Double alignment */ + t = (Bhdr *)(((ulong)t + 7) & ~7); + + /* TBS xmerge */ + if(0 && p->chain != nil && (char*)t-(char*)B2LIMIT(p->chain)-ldr == 0){ + /* can merge chains */ + if(0)print("merging chains %p and %p in %s\n", p->chain, t, p->name); + q = B2LIMIT(p->chain); + q->magic = MAGIC_A; + q->size = alloc; + B2T(q)->hdr = q; + t = B2NB(q); + t->magic = MAGIC_E; + p->chain->csize += alloc; + p->cursize += alloc; + iunlock(&p->l); + poolfree(p, B2D(q)); /* for backward merge */ + return poolalloc(p, osize); + } + + t->magic = MAGIC_E; /* Make a leader */ + t->size = ldr; + t->csize = ns+ldr; + t->clink = p->chain; + p->chain = t; + B2T(t)->hdr = t; + t = B2NB(t); + + t->magic = MAGIC_A; /* Make the block we are going to return */ + t->size = size; + B2T(t)->hdr = t; + q = t; + + ns -= size; /* Free the rest */ + if(ns > 0) { + q = B2NB(t); + q->size = ns; + B2T(q)->hdr = q; + pooladd(p, q); + } + B2NB(q)->magic = MAGIC_E; /* Mark the end of the chunk */ + + p->cursize += t->size; + if(p->cursize > p->hw) + p->hw = p->cursize; + iunlock(&p->l); + return B2D(t); +} + +void +poolfree(Pool *p, void *v) +{ + Bhdr *b, *c; + extern Bhdr *ptr; + + D2B(b, v); + + ilock(&p->l); + p->nfree++; + p->cursize -= b->size; + + c = B2NB(b); + if(c->magic == MAGIC_F) { /* Join forward */ + if(c == ptr) + ptr = b; + pooldel(p, c); + c->magic = 0; + b->size += c->size; + B2T(b)->hdr = b; + } + + c = B2PT(b)->hdr; + if(c->magic == MAGIC_F) { /* Join backward */ + if(b == ptr) + ptr = c; + pooldel(p, c); + b->magic = 0; + c->size += b->size; + b = c; + B2T(b)->hdr = b; + } + + pooladd(p, b); + iunlock(&p->l); +} + +void * +poolrealloc(Pool *p, void *v, ulong size) +{ + Bhdr *b; + void *nv; + int osize; + + if(size >= 1024*1024*1024) /* for sanity and to avoid overflow */ + return nil; + if(size == 0){ + poolfree(p, v); + return nil; + } + SET(osize); + if(v != nil){ + ilock(&p->l); + D2B(b, v); + osize = b->size - BHDRSIZE; + iunlock(&p->l); + if(osize >= size) + return v; + } + nv = poolalloc(p, size); + if(nv != nil && v != nil){ + memmove(nv, v, osize); + poolfree(p, v); + } + return nv; +} + +ulong +poolmsize(Pool *p, void *v) +{ + Bhdr *b; + ulong size; + + if(v == nil) + return 0; + ilock(&p->l); + D2B(b, v); + size = b->size - BHDRSIZE; + iunlock(&p->l); + return size; +} + +static ulong +poolmax(Pool *p) +{ + Bhdr *t; + ulong size; + + ilock(&p->l); + size = p->maxsize - p->cursize; + t = p->root; + if(t != nil) { + while(t->right != nil) + t = t->right; + if(size < t->size) + size = t->size; + } + if(size >= BHDRSIZE) + size -= BHDRSIZE; + iunlock(&p->l); + return size; +} + +int +poolread(char *va, int count, ulong offset) +{ + Pool *p; + int n, i, signed_off; + + n = 0; + signed_off = offset; + for(i = 0; i < table.n; i++) { + p = &table.pool[i]; + n += snprint(va+n, count-n, "%11lud %11lud %11lud %11lud %11lud %11d %11lud %s\n", + p->cursize, + p->maxsize, + p->hw, + p->nalloc, + p->nfree, + p->nbrk, + poolmax(p), + p->name); + + if(signed_off > 0) { + signed_off -= n; + if(signed_off < 0) { + memmove(va, va+n+signed_off, -signed_off); + n = -signed_off; + } + else + n = 0; + } + + } + return n; +} + +void* +malloc(ulong size) +{ + void *v; + + v = poolalloc(mainmem, size+Npadlong*sizeof(ulong)); + if(v != nil){ + if(Npadlong){ + v = (ulong*)v+Npadlong; + setmalloctag(v, getcallerpc(&size)); + setrealloctag(v, 0); + } + memset(v, 0, size); + } + return v; +} + +void* +smalloc(ulong size) +{ + void *v; + + for(;;) { + v = poolalloc(mainmem, size+Npadlong*sizeof(ulong)); + if(v != nil) + break; + tsleep(&up->sleep, return0, 0, 100); + } + if(Npadlong){ + v = (ulong*)v+Npadlong; + setmalloctag(v, getcallerpc(&size)); + setrealloctag(v, 0); + } + memset(v, 0, size); + return v; +} + +void* +mallocz(ulong size, int clr) +{ + void *v; + + v = poolalloc(mainmem, size+Npadlong*sizeof(ulong)); + if(v != nil){ + if(Npadlong){ + v = (ulong*)v+Npadlong; + setmalloctag(v, getcallerpc(&size)); + setrealloctag(v, 0); + } + if(clr) + memset(v, 0, size); + } + return v; +} + +void +free(void *v) +{ + Bhdr *b; + + if(v != nil) { + if(Npadlong) + v = (ulong*)v-Npadlong; + D2B(b, v); + poolfree(mainmem, v); + } +} + +void* +realloc(void *v, ulong size) +{ + void *nv; + + if(size == 0) + return malloc(size); /* temporary change until realloc calls can be checked */ + if(v != nil) + v = (ulong*)v-Npadlong; + if(Npadlong!=0 && size!=0) + size += Npadlong*sizeof(ulong); + nv = poolrealloc(mainmem, v, size); + if(nv != nil) { + nv = (ulong*)nv+Npadlong; + setrealloctag(nv, getcallerpc(&v)); + if(v == nil) + setmalloctag(v, getcallerpc(&v)); + } + return nv; +} + +void +setmalloctag(void *v, ulong pc) +{ + ulong *u; + + USED(v); + USED(pc); + if(Npadlong <= MallocOffset || v == nil) + return; + u = v; + u[-Npadlong+MallocOffset] = pc; +} + +ulong +getmalloctag(void *v) +{ + USED(v); + if(Npadlong <= MallocOffset) + return ~0; + return ((ulong*)v)[-Npadlong+MallocOffset]; +} + +void +setrealloctag(void *v, ulong pc) +{ + ulong *u; + + USED(v); + USED(pc); + if(Npadlong <= ReallocOffset || v == nil) + return; + u = v; + u[-Npadlong+ReallocOffset] = pc; +} + +ulong +getrealloctag(void *v) +{ + USED(v); + if(Npadlong <= ReallocOffset) + return ((ulong*)v)[-Npadlong+ReallocOffset]; + return ~0; +} + +ulong +msize(void *v) +{ + if(v == nil) + return 0; + return poolmsize(mainmem, (ulong*)v-Npadlong)-Npadlong*sizeof(ulong); +} + +void* +calloc(ulong n, ulong szelem) +{ + return malloc(n*szelem); +} + +void +pooldump(Bhdr *b, int d, int c) +{ + Bhdr *t; + + if(b == nil) + return; + + print("%.8lux %.8lux %.8lux %c %4d %lud (f %.8lux p %.8lux)\n", + b, b->left, b->right, c, d, b->size, b->fwd, b->prev); + d++; + for(t = b->fwd; t != b; t = t->fwd) + print("\t%.8lux %.8lux %.8lux\n", t, t->prev, t->fwd); + pooldump(b->left, d, 'l'); + pooldump(b->right, d, 'r'); +} + +void +poolshow(void) +{ + int i; + + for(i = 0; i < table.n; i++) { + print("Arena: %s root=%.8lux\n", table.pool[i].name, table.pool[i].root); + pooldump(table.pool[i].root, 0, 'R'); + } +} + +void +poolsetcompact(Pool *p, void (*move)(void*, void*)) +{ + p->move = move; +} + +int +poolcompact(Pool *pool) +{ + Bhdr *base, *limit, *ptr, *end, *next; + int compacted, nb; + + if(pool->move == nil || pool->lastfree == pool->nfree) + return 0; + + pool->lastfree = pool->nfree; + + base = pool->chain; + ptr = B2NB(base); /* First Block in arena has clink */ + limit = B2LIMIT(base); + compacted = 0; + + pool->root = nil; + end = ptr; + while(base != nil) { + next = B2NB(ptr); + if(ptr->magic == MAGIC_A || ptr->magic == MAGIC_I) { + if(ptr != end) { + memmove(end, ptr, ptr->size); + pool->move(B2D(ptr), B2D(end)); + compacted = 1; + } + end = B2NB(end); + } + if(next >= limit) { + nb = (uchar*)limit - (uchar*)end; + if(nb > 0){ + if(nb < pool->quanta+1) + panic("poolcompact: leftover too small\n"); + end->size = nb; + B2T(end)->hdr = end; + pooladd(pool, end); + } + base = base->clink; + if(base == nil) + break; + ptr = B2NB(base); + end = ptr; /* could do better by copying between chains */ + limit = B2LIMIT(base); + } else + ptr = next; + } + + return compacted; +} + +void +poolsize(Pool *p, int max, int contig) +{ + void *x; + + p->maxsize = max; + if(max == 0) + p->ressize = max; + else if(max < RESERVED) + p->ressize = max; + else + p->ressize = max-RESERVED; + if (contig && max > 0) { + p->chunk = max-1024; + x = poolalloc(p, p->chunk); + if(x == nil) + panic("poolsize: don't have %d bytes\n", p->chunk); + poolfree(p, x); + p->hw = 0; + } +} + +static void +_poolfault(void *v, char *msg, ulong c) +{ + setpanic(); + auditmemloc(msg, v); + panic("%s %lux (from %lux/%lux)", msg, v, getcallerpc(&v), c); +} + +static void +dumpvl(char *msg, ulong *v, int n) +{ + int i, l; + + l = print("%s at %p: ", msg, v); + for(i = 0; i < n; i++) { + if(l >= 60) { + print("\n"); + l = print(" %p: ", v); + } + l += print(" %lux", *v++); + } + print("\n"); + USED(l); +} + +static void +corrupted(char *str, char *msg, Pool *p, Bhdr *b, void *v) +{ + print("%s(%p): pool %s CORRUPT: %s at %p'%lud(magic=%lux)\n", + str, v, p->name, msg, b, b->size, b->magic); + dumpvl("bad Bhdr", (ulong *)((ulong)b & ~3)-4, 10); +} + +static void +_auditmemloc(char *str, void *v) +{ + Pool *p; + Bhdr *bc, *ec, *b, *nb, *fb = nil; + char *fmsg, *msg; + ulong fsz; + + SET(fsz, fmsg); + for (p = &table.pool[0]; p < &table.pool[nelem(table.pool)]; p++) { + ilock(&p->l); + for (bc = p->chain; bc != nil; bc = bc->clink) { + if (bc->magic != MAGIC_E) { + iunlock(&p->l); + corrupted(str, "chain hdr!=MAGIC_E", p, bc, v); + goto nextpool; + } + ec = B2LIMIT(bc); + if (((Bhdr*)v >= bc) && ((Bhdr*)v < ec)) + goto found; + } + iunlock(&p->l); +nextpool: ; + } + print("%s: %lux not in pools\n", str, v); + return; + +found: + for (b = bc; b < ec; b = nb) { + switch(b->magic) { + case MAGIC_F: + msg = "free blk"; + break; + case MAGIC_I: + msg = "immutable block"; + break; + case MAGIC_A: + msg = "block"; + break; + default: + if (b == bc && b->magic == MAGIC_E) { + msg = "pool hdr"; + break; + } + iunlock(&p->l); + corrupted(str, "bad magic", p, b, v); + goto badchunk; + } + if (b->size <= 0 || (b->size & p->quanta)) { + iunlock(&p->l); + corrupted(str, "bad size", p, b, v); + goto badchunk; + } + if (fb != nil) + break; + nb = B2NB(b); + if ((Bhdr*)v < nb) { + fb = b; + fsz = b->size; + fmsg = msg; + } + } + iunlock(&p->l); + if (b >= ec) { + if (b > ec) + corrupted(str, "chain size mismatch", p, b, v); + else if (b->magic != MAGIC_E) + corrupted(str, "chain end!=MAGIC_E", p, b, v); + } +badchunk: + if (fb != nil) { + print("%s: %lux in %s:", str, v, p->name); + if (fb == v) + print(" is %s '%lux\n", fmsg, fsz); + else + print(" in %s at %lux'%lux\n", fmsg, fb, fsz); + dumpvl("area", (ulong *)((ulong)v & ~3)-4, 20); + } +} + +char * +poolaudit(char*(*audit)(int, Bhdr *)) +{ + Pool *p; + Bhdr *bc, *ec, *b; + char *r = nil; + + for (p = &table.pool[0]; p < &table.pool[nelem(table.pool)]; p++) { + ilock(&p->l); + for (bc = p->chain; bc != nil; bc = bc->clink) { + if (bc->magic != MAGIC_E) { + iunlock(&p->l); + return "bad chain hdr"; + } + ec = B2LIMIT(bc); + for (b = bc; b < ec; b = B2NB(b)) { + if (b->size <= 0 || (b->size & p->quanta)) + r = "bad size in bhdr"; + else + switch(b->magic) { + case MAGIC_E: + if (b != bc) { + r = "unexpected MAGIC_E"; + break; + } + case MAGIC_F: + case MAGIC_A: + case MAGIC_I: + r = audit(p->pnum, b); + break; + default: + r = "bad magic"; + } + if (r != nil) { + iunlock(&p->l); + return r; + } + } + if (b != ec || b->magic != MAGIC_E) { + iunlock(&p->l); + return "bad chain ending"; + } + } + iunlock(&p->l); + } + return r; +} + +void +poolinit(void) +{ + debugkey('m', "memory pools", poolsummary, 0); +} diff --git a/os/port/allocb.c b/os/port/allocb.c new file mode 100644 index 00000000..6ab67d60 --- /dev/null +++ b/os/port/allocb.c @@ -0,0 +1,159 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +enum +{ + Hdrspc = 64, /* leave room for high-level headers */ + Bdead = 0x51494F42, /* "QIOB" */ +}; + +struct +{ + Lock; + ulong bytes; +} ialloc; + +/* + * allocate blocks (round data base address to 64 bit boundary). + * if mallocz gives us more than we asked for, leave room at the front + * for header. + */ +Block* +_allocb(int size) +{ + Block *b; + ulong addr; + int n; + + b = mallocz(sizeof(Block)+size+Hdrspc+(BY2V-1), 0); + if(b == nil) + return nil; + + b->next = nil; + b->list = nil; + b->free = nil; + b->flag = 0; + + addr = (ulong)b; + addr = ROUND(addr + sizeof(Block), BY2V); + b->base = (uchar*)addr; + b->lim = ((uchar*)b) + msize(b); + b->rp = b->base; + n = b->lim - b->base - size; + b->rp += n & ~(BY2V-1); + b->wp = b->rp; + + return b; +} + +Block* +allocb(int size) +{ + Block *b; + + if(0 && up == nil) + panic("allocb outside process: %8.8lux", getcallerpc(&size)); + b = _allocb(size); + if(b == 0) + exhausted("Blocks"); + setmalloctag(b, getcallerpc(&size)); + return b; +} + +/* + * interrupt time allocation + */ +Block* +iallocb(int size) +{ + Block *b; + + if(ialloc.bytes > conf.ialloc){ + //print("iallocb: limited %lud/%lud\n", ialloc.bytes, conf.ialloc); + return nil; + } + + b = _allocb(size); + if(b == nil){ + //print("iallocb: no memory %lud/%lud\n", ialloc.bytes, conf.ialloc); + return nil; + } + setmalloctag(b, getcallerpc(&size)); + b->flag = BINTR; + + ilock(&ialloc); + ialloc.bytes += b->lim - b->base; + iunlock(&ialloc); + + return b; +} + +void +freeb(Block *b) +{ + void *dead = (void*)Bdead; + + if(b == nil) + return; + + /* + * drivers which perform non cache coherent DMA manage their own buffer + * pool of uncached buffers and provide their own free routine. + */ + if(b->free) { + b->free(b); + return; + } + if(b->flag & BINTR) { + ilock(&ialloc); + ialloc.bytes -= b->lim - b->base; + iunlock(&ialloc); + } + + /* poison the block in case someone is still holding onto it */ + b->next = dead; + b->rp = dead; + b->wp = dead; + b->lim = dead; + b->base = dead; + + free(b); +} + +void +checkb(Block *b, char *msg) +{ + void *dead = (void*)Bdead; + + if(b == dead) + panic("checkb b %s %lux", msg, b); + if(b->base == dead || b->lim == dead || b->next == dead + || b->rp == dead || b->wp == dead){ + print("checkb: base 0x%8.8luX lim 0x%8.8luX next 0x%8.8luX\n", + b->base, b->lim, b->next); + print("checkb: rp 0x%8.8luX wp 0x%8.8luX\n", b->rp, b->wp); + panic("checkb dead: %s\n", msg); + } + + if(b->base > b->lim) + panic("checkb 0 %s %lux %lux", msg, b->base, b->lim); + if(b->rp < b->base) + panic("checkb 1 %s %lux %lux", msg, b->base, b->rp); + if(b->wp < b->base) + panic("checkb 2 %s %lux %lux", msg, b->base, b->wp); + if(b->rp > b->lim) + panic("checkb 3 %s %lux %lux", msg, b->rp, b->lim); + if(b->wp > b->lim) + panic("checkb 4 %s %lux %lux", msg, b->wp, b->lim); + +} + +void +iallocsummary(void) +{ + print("ialloc %lud/%lud\n", ialloc.bytes, conf.ialloc); +} diff --git a/os/port/chan.c b/os/port/chan.c new file mode 100644 index 00000000..a5d213aa --- /dev/null +++ b/os/port/chan.c @@ -0,0 +1,1431 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +char* +channame(Chan *c) /* DEBUGGING */ +{ + if(c == nil) + return "<nil chan>"; + if(c->name == nil) + return "<nil name>"; + if(c->name->s == nil) + return "<nil name.s>"; + return c->name->s; +} + +enum +{ + CNAMESLOP = 20 +}; + +struct +{ + Lock; + int fid; + Chan *free; + Chan *list; +}chanalloc; + +typedef struct Elemlist Elemlist; + +struct Elemlist +{ + char *name; /* copy of name, so '/' can be overwritten */ + int nelems; + char **elems; + int *off; + int mustbedir; +}; + +#define SEP(c) ((c) == 0 || (c) == '/') +void cleancname(Cname*); + +int +isdotdot(char *p) +{ + return p[0]=='.' && p[1]=='.' && p[2]=='\0'; +} + +int +incref(Ref *r) +{ + int x; + + lock(&r->l); + x = ++r->ref; + unlock(&r->l); + return x; +} + +int +decref(Ref *r) +{ + int x; + + lock(&r->l); + x = --r->ref; + unlock(&r->l); + if(x < 0) + panic("decref, pc=0x%lux", getcallerpc(&r)); + + return x; +} + +/* + * Rather than strncpy, which zeros the rest of the buffer, kstrcpy + * truncates if necessary, always zero terminates, does not zero fill, + * and puts ... at the end of the string if it's too long. Usually used to + * save a string in up->genbuf; + */ +void +kstrcpy(char *s, char *t, int ns) +{ + int nt; + + nt = strlen(t); + if(nt+1 <= ns){ + memmove(s, t, nt+1); + return; + } + /* too long */ + if(ns < 4){ + /* but very short! */ + strncpy(s, t, ns); + return; + } + /* truncate with ... at character boundary (very rare case) */ + memmove(s, t, ns-4); + ns -= 4; + s[ns] = '\0'; + /* look for first byte of UTF-8 sequence by skipping continuation bytes */ + while(ns>0 && (s[--ns]&0xC0)==0x80) + ; + strcpy(s+ns, "..."); +} + +int +emptystr(char *s) +{ + if(s == nil) + return 1; + if(s[0] == '\0') + return 1; + return 0; +} + +/* + * Atomically replace *p with copy of s + */ +void +kstrdup(char **p, char *s) +{ + int n; + char *t, *prev; + + n = strlen(s)+1; + /* if it's a user, we can wait for memory; if not, something's very wrong */ + if(up){ + t = smalloc(n); + setmalloctag(t, getcallerpc(&p)); + }else{ + t = malloc(n); + if(t == nil) + panic("kstrdup: no memory"); + } + memmove(t, s, n); + prev = *p; + *p = t; + free(prev); +} + +void +chandevreset(void) +{ + int i; + + for(i=0; devtab[i] != nil; i++) + devtab[i]->reset(); +} + +void +chandevinit(void) +{ + int i; + + for(i=0; devtab[i] != nil; i++) + devtab[i]->init(); +} + +void +chandevshutdown(void) +{ + int i; + + /* shutdown in reverse order */ + for(i=0; devtab[i] != nil; i++) + ; + for(i--; i >= 0; i--) + devtab[i]->shutdown(); +} + +Chan* +newchan(void) +{ + Chan *c; + + lock(&chanalloc); + c = chanalloc.free; + if(c != 0) + chanalloc.free = c->next; + unlock(&chanalloc); + + if(c == nil) { + c = smalloc(sizeof(Chan)); + lock(&chanalloc); + c->fid = ++chanalloc.fid; + c->link = chanalloc.list; + chanalloc.list = c; + unlock(&chanalloc); + } + + /* if you get an error before associating with a dev, + close calls rootclose, a nop */ + c->type = 0; + c->flag = 0; + c->ref = 1; + c->dev = 0; + c->offset = 0; + c->iounit = 0; + c->umh = 0; + c->uri = 0; + c->dri = 0; + c->aux = 0; + c->mchan = 0; + c->mcp = 0; + c->mux = 0; + c->mqid.path = 0; + c->mqid.vers = 0; + c->mqid.type = 0; + c->name = 0; + return c; +} + +static Ref ncname; + +Cname* +newcname(char *s) +{ + Cname *n; + int i; + + n = smalloc(sizeof(Cname)); + i = strlen(s); + n->len = i; + n->alen = i+CNAMESLOP; + n->s = smalloc(n->alen); + memmove(n->s, s, i+1); + n->ref = 1; + incref(&ncname); + return n; +} + +void +cnameclose(Cname *n) +{ + if(n == nil) + return; + if(decref(n)) + return; + decref(&ncname); + free(n->s); + free(n); +} + +Cname* +addelem(Cname *n, char *s) +{ + int i, a; + char *t; + Cname *new; + + if(s[0]=='.' && s[1]=='\0') + return n; + + if(n->ref > 1){ + /* copy on write */ + new = newcname(n->s); + cnameclose(n); + n = new; + } + + i = strlen(s); + if(n->len+1+i+1 > n->alen){ + a = n->len+1+i+1 + CNAMESLOP; + t = smalloc(a); + memmove(t, n->s, n->len+1); + free(n->s); + n->s = t; + n->alen = a; + } + if(n->len>0 && n->s[n->len-1]!='/' && s[0]!='/') /* don't insert extra slash if one is present */ + n->s[n->len++] = '/'; + memmove(n->s+n->len, s, i+1); + n->len += i; + if(isdotdot(s)) + cleancname(n); + return n; +} + +void +chanfree(Chan *c) +{ + c->flag = CFREE; + + if(c->umh != nil){ + putmhead(c->umh); + c->umh = nil; + } + if(c->umc != nil){ + cclose(c->umc); + c->umc = nil; + } + if(c->mux != nil){ + muxclose(c->mux); + c->mux = nil; + } + if(c->mchan != nil){ + cclose(c->mchan); + c->mchan = nil; + } + + cnameclose(c->name); + + lock(&chanalloc); + c->next = chanalloc.free; + chanalloc.free = c; + unlock(&chanalloc); +} + +void +cclose(Chan *c) +{ + if(c == 0) + return; + + if(c->flag&CFREE) + panic("cclose %lux", getcallerpc(&c)); + + if(decref(c)) + return; + + if(!waserror()){ + devtab[c->type]->close(c); + poperror(); + } + chanfree(c); +} + +/* + * Make sure we have the only copy of c. (Copy on write.) + */ +Chan* +cunique(Chan *c) +{ + Chan *nc; + + if(c->ref != 1) { + nc = cclone(c); + cclose(c); + c = nc; + } + + return c; +} + +int +eqqid(Qid a, Qid b) +{ + return a.path==b.path && a.vers==b.vers; +} + +int +eqchan(Chan *a, Chan *b, int pathonly) +{ + if(a->qid.path != b->qid.path) + return 0; + if(!pathonly && a->qid.vers!=b->qid.vers) + return 0; + if(a->type != b->type) + return 0; + if(a->dev != b->dev) + return 0; + return 1; +} + +int +eqchantdqid(Chan *a, int type, int dev, Qid qid, int pathonly) +{ + if(a->qid.path != qid.path) + return 0; + if(!pathonly && a->qid.vers!=qid.vers) + return 0; + if(a->type != type) + return 0; + if(a->dev != dev) + return 0; + return 1; +} + +Mhead* +newmhead(Chan *from) +{ + Mhead *mh; + + mh = smalloc(sizeof(Mhead)); + mh->ref = 1; + mh->from = from; + incref(from); + +/* + n = from->name->len; + if(n >= sizeof(mh->fromname)) + n = sizeof(mh->fromname)-1; + memmove(mh->fromname, from->name->s, n); + mh->fromname[n] = 0; +*/ + return mh; +} + +int +cmount(Chan *new, Chan *old, int flag, char *spec) +{ + Pgrp *pg; + int order, flg; + Mhead *m, **l, *mh; + Mount *nm, *f, *um, **h; + + if(QTDIR & (old->qid.type^new->qid.type)) + error(Emount); + +if(old->umh) + print("cmount old extra umh\n"); + + order = flag&MORDER; + + if((old->qid.type&QTDIR)==0 && order != MREPL) + error(Emount); + + mh = new->umh; + + /* + * Not allowed to bind when the old directory + * is itself a union. (Maybe it should be allowed, but I don't see + * what the semantics would be.) + * + * We need to check mh->mount->next to tell unions apart from + * simple mount points, so that things like + * mount -c fd /root + * bind -c /root / + * work. The check of mount->mflag catches things like + * mount fd /root + * bind -c /root / + * + * This is far more complicated than it should be, but I don't + * see an easier way at the moment. -rsc + */ + if((flag&MCREATE) && mh && mh->mount + && (mh->mount->next || !(mh->mount->mflag&MCREATE))) + error(Emount); + + pg = up->env->pgrp; + wlock(&pg->ns); + + l = &MOUNTH(pg, old->qid); + for(m = *l; m; m = m->hash) { + if(eqchan(m->from, old, 1)) + break; + l = &m->hash; + } + + if(m == nil) { + /* + * nothing mounted here yet. create a mount + * head and add to the hash table. + */ + m = newmhead(old); + *l = m; + + /* + * if this is a union mount, add the old + * node to the mount chain. + */ + if(order != MREPL) + m->mount = newmount(m, old, 0, 0); + } + wlock(&m->lock); + if(waserror()){ + wunlock(&m->lock); + nexterror(); + } + wunlock(&pg->ns); + + nm = newmount(m, new, flag, spec); + if(mh != nil && mh->mount != nil) { + /* + * copy a union when binding it onto a directory + */ + flg = order; + if(order == MREPL) + flg = MAFTER; + h = &nm->next; + um = mh->mount; + for(um = um->next; um; um = um->next) { + f = newmount(m, um->to, flg, um->spec); + *h = f; + h = &f->next; + } + } + + if(m->mount && order == MREPL) { + mountfree(m->mount); + m->mount = 0; + } + + if(flag & MCREATE) + nm->mflag |= MCREATE; + + if(m->mount && order == MAFTER) { + for(f = m->mount; f->next; f = f->next) + ; + f->next = nm; + } + else { + for(f = nm; f->next; f = f->next) + ; + f->next = m->mount; + m->mount = nm; + } + + wunlock(&m->lock); + poperror(); + return nm->mountid; +} + +void +cunmount(Chan *mnt, Chan *mounted) +{ + Pgrp *pg; + Mhead *m, **l; + Mount *f, **p; + + if(mnt->umh) /* should not happen */ + print("cunmount newp extra umh %p has %p\n", mnt, mnt->umh); + + /* + * It _can_ happen that mounted->umh is non-nil, + * because mounted is the result of namec(Aopen) + * (see sysfile.c:/^sysunmount). + * If we open a union directory, it will have a umh. + * Although surprising, this is okay, since the + * cclose will take care of freeing the umh. + */ + + pg = up->env->pgrp; + wlock(&pg->ns); + + l = &MOUNTH(pg, mnt->qid); + for(m = *l; m; m = m->hash) { + if(eqchan(m->from, mnt, 1)) + break; + l = &m->hash; + } + + if(m == 0) { + wunlock(&pg->ns); + error(Eunmount); + } + + wlock(&m->lock); + if(mounted == 0) { + *l = m->hash; + wunlock(&pg->ns); + mountfree(m->mount); + m->mount = nil; + cclose(m->from); + wunlock(&m->lock); + putmhead(m); + return; + } + + p = &m->mount; + for(f = *p; f; f = f->next) { + /* BUG: Needs to be 2 pass */ + if(eqchan(f->to, mounted, 1) || + (f->to->mchan && eqchan(f->to->mchan, mounted, 1))) { + *p = f->next; + f->next = 0; + mountfree(f); + if(m->mount == nil) { + *l = m->hash; + cclose(m->from); + wunlock(&m->lock); + wunlock(&pg->ns); + putmhead(m); + return; + } + wunlock(&m->lock); + wunlock(&pg->ns); + return; + } + p = &f->next; + } + wunlock(&m->lock); + wunlock(&pg->ns); + error(Eunion); +} + +Chan* +cclone(Chan *c) +{ + Chan *nc; + Walkqid *wq; + + wq = devtab[c->type]->walk(c, nil, nil, 0); + if(wq == nil) + error("clone failed"); + nc = wq->clone; + free(wq); + nc->name = c->name; + if(c->name) + incref(c->name); + return nc; +} + +int +findmount(Chan **cp, Mhead **mp, int type, int dev, Qid qid) +{ + Pgrp *pg; + Mhead *m; + + pg = up->env->pgrp; + rlock(&pg->ns); + for(m = MOUNTH(pg, qid); m; m = m->hash){ + rlock(&m->lock); +if(m->from == nil){ + print("m %p m->from 0\n", m); + runlock(&m->lock); + continue; +} + if(eqchantdqid(m->from, type, dev, qid, 1)) { + runlock(&pg->ns); + if(mp != nil){ + incref(m); + if(*mp != nil) + putmhead(*mp); + *mp = m; + } + if(*cp != nil) + cclose(*cp); + incref(m->mount->to); + *cp = m->mount->to; + runlock(&m->lock); + return 1; + } + runlock(&m->lock); + } + + runlock(&pg->ns); + return 0; +} + +int +domount(Chan **cp, Mhead **mp) +{ + return findmount(cp, mp, (*cp)->type, (*cp)->dev, (*cp)->qid); +} + +Chan* +undomount(Chan *c, Cname *name) +{ + Chan *nc; + Pgrp *pg; + Mount *t; + Mhead **h, **he, *f; + + pg = up->env->pgrp; + rlock(&pg->ns); + if(waserror()) { + runlock(&pg->ns); + nexterror(); + } + + he = &pg->mnthash[MNTHASH]; + for(h = pg->mnthash; h < he; h++) { + for(f = *h; f; f = f->hash) { + if(strcmp(f->from->name->s, name->s) != 0) + continue; + for(t = f->mount; t; t = t->next) { + if(eqchan(c, t->to, 1)) { + /* + * We want to come out on the left hand side of the mount + * point using the element of the union that we entered on. + * To do this, find the element that has a from name of + * c->name->s. + */ + if(strcmp(t->head->from->name->s, name->s) != 0) + continue; + nc = t->head->from; + incref(nc); + cclose(c); + c = nc; + break; + } + } + } + } + poperror(); + runlock(&pg->ns); + return c; +} + +/* + * Either walks all the way or not at all. No partial results in *cp. + * *nerror is the number of names to display in an error message. + */ +static char Edoesnotexist[] = "does not exist"; +int +walk(Chan **cp, char **names, int nnames, int nomount, int *nerror) +{ + int dev, dotdot, i, n, nhave, ntry, type; + Chan *c, *nc; + Cname *cname; + Mount *f; + Mhead *mh, *nmh; + Walkqid *wq; + + c = *cp; + incref(c); + cname = c->name; + incref(cname); + mh = nil; + + /* + * While we haven't gotten all the way down the path: + * 1. step through a mount point, if any + * 2. send a walk request for initial dotdot or initial prefix without dotdot + * 3. move to the first mountpoint along the way. + * 4. repeat. + * + * An invariant is that each time through the loop, c is on the undomount + * side of the mount point, and c's name is cname. + */ + for(nhave=0; nhave<nnames; nhave+=n){ + if((c->qid.type&QTDIR)==0){ + if(nerror) + *nerror = nhave; + cnameclose(cname); + cclose(c); + strcpy(up->env->errstr, Enotdir); + if(mh != nil) + putmhead(mh); + return -1; + } + ntry = nnames - nhave; + if(ntry > MAXWELEM) + ntry = MAXWELEM; + dotdot = 0; + for(i=0; i<ntry; i++){ + if(isdotdot(names[nhave+i])){ + if(i==0) { + dotdot = 1; + ntry = 1; + } else + ntry = i; + break; + } + } + + if(!dotdot && !nomount) + domount(&c, &mh); + + type = c->type; + dev = c->dev; + + if((wq = devtab[type]->walk(c, nil, names+nhave, ntry)) == nil){ + /* try a union mount, if any */ + if(mh && !nomount){ + /* + * mh->mount == c, so start at mh->mount->next + */ + rlock(&mh->lock); + for(f = mh->mount->next; f; f = f->next) + if((wq = devtab[f->to->type]->walk(f->to, nil, names+nhave, ntry)) != nil) + break; + runlock(&mh->lock); + if(f != nil){ + type = f->to->type; + dev = f->to->dev; + } + } + if(wq == nil){ + cclose(c); + cnameclose(cname); + if(nerror) + *nerror = nhave+1; + if(mh != nil) + putmhead(mh); + return -1; + } + } + + nmh = nil; + if(dotdot) { + assert(wq->nqid == 1); + assert(wq->clone != nil); + + cname = addelem(cname, ".."); + nc = undomount(wq->clone, cname); + n = 1; + } else { + nc = nil; + if(!nomount) + for(i=0; i<wq->nqid && i<ntry-1; i++) + if(findmount(&nc, &nmh, type, dev, wq->qid[i])) + break; + if(nc == nil){ /* no mount points along path */ + if(wq->clone == nil){ + cclose(c); + cnameclose(cname); + if(wq->nqid==0 || (wq->qid[wq->nqid-1].type&QTDIR)){ + if(nerror) + *nerror = nhave+wq->nqid+1; + strcpy(up->env->errstr, Edoesnotexist); + }else{ + if(nerror) + *nerror = nhave+wq->nqid; + strcpy(up->env->errstr, Enotdir); + } + free(wq); + if(mh != nil) + putmhead(mh); + return -1; + } + n = wq->nqid; + nc = wq->clone; + }else{ /* stopped early, at a mount point */ + if(wq->clone != nil){ + cclose(wq->clone); + wq->clone = nil; + } + n = i+1; + } + for(i=0; i<n; i++) + cname = addelem(cname, names[nhave+i]); + } + cclose(c); + c = nc; + putmhead(mh); + mh = nmh; + free(wq); + } + + putmhead(mh); + + c = cunique(c); + + if(c->umh != nil){ //BUG + print("walk umh\n"); + putmhead(c->umh); + c->umh = nil; + } + + cnameclose(c->name); + c->name = cname; + + cclose(*cp); + *cp = c; + if(nerror) + *nerror = 0; + return 0; +} + +/* + * c is a mounted non-creatable directory. find a creatable one. + */ +Chan* +createdir(Chan *c, Mhead *m) +{ + Chan *nc; + Mount *f; + + rlock(&m->lock); + if(waserror()) { + runlock(&m->lock); + nexterror(); + } + for(f = m->mount; f; f = f->next) { + if(f->mflag&MCREATE) { + nc = cclone(f->to); + runlock(&m->lock); + poperror(); + cclose(c); + return nc; + } + } + error(Enocreate); + return 0; +} + +void +saveregisters(void) +{ +} + +/* + * In place, rewrite name to compress multiple /, eliminate ., and process .. + */ +void +cleancname(Cname *n) +{ + char *p; + + if(n->s[0] == '#'){ + p = strchr(n->s, '/'); + if(p == nil) + return; + cleanname(p); + + /* + * The correct name is #i rather than #i/, + * but the correct name of #/ is #/. + */ + if(strcmp(p, "/")==0 && n->s[1] != '/') + *p = '\0'; + }else + cleanname(n->s); + n->len = strlen(n->s); +} + +static void +growparse(Elemlist *e) +{ + char **new; + int *inew; + enum { Delta = 8 }; + + if(e->nelems % Delta == 0){ + new = smalloc((e->nelems+Delta) * sizeof(char*)); + memmove(new, e->elems, e->nelems*sizeof(char*)); + free(e->elems); + e->elems = new; + inew = smalloc((e->nelems+Delta+1) * sizeof(int)); + memmove(inew, e->off, e->nelems*sizeof(int)); + free(e->off); + e->off = inew; + } +} + +/* + * The name is known to be valid. + * Copy the name so slashes can be overwritten. + * An empty string will set nelem=0. + * A path ending in / or /. or /.//./ etc. will have + * e.mustbedir = 1, so that we correctly + * reject, e.g., "/adm/users/." when /adm/users is a file + * rather than a directory. + */ +static void +parsename(char *name, Elemlist *e) +{ + char *slash; + + kstrdup(&e->name, name); + name = e->name; + e->nelems = 0; + e->elems = nil; + e->off = smalloc(sizeof(int)); + e->off[0] = skipslash(name) - name; + for(;;){ + name = skipslash(name); + if(*name=='\0'){ + e->mustbedir = 1; + break; + } + growparse(e); + + e->elems[e->nelems++] = name; + slash = utfrune(name, '/'); + if(slash == nil){ + e->off[e->nelems] = name+strlen(name) - e->name; + e->mustbedir = 0; + break; + } + e->off[e->nelems] = slash - e->name; + *slash++ = '\0'; + name = slash; + } +} + +void* +memrchr(void *va, int c, long n) +{ + uchar *a, *e; + + a = va; + for(e=a+n-1; e>a; e--) + if(*e == c) + return e; + return nil; +} + +/* + * Turn a name into a channel. + * &name[0] is known to be a valid address. It may be a kernel address. + * + * Opening with amode Aopen, Acreate, or Aremove guarantees + * that the result will be the only reference to that particular fid. + * This is necessary since we might pass the result to + * devtab[]->remove(). + * + * Opening Atodir, Amount, or Aaccess does not guarantee this. + * + * Opening Aaccess can, under certain conditions, return a + * correct Chan* but with an incorrect Cname attached. + * Since the functions that open Aaccess (sysstat, syswstat, sys_stat) + * do not use the Cname*, this avoids an unnecessary clone. + */ +Chan* +namec(char *aname, int amode, int omode, ulong perm) +{ + int n, prefix, len, t, nomount, npath; + Chan *c, *cnew; + Cname *cname; + Elemlist e; + Rune r; + Mhead *m; + char *createerr, tmperrbuf[ERRMAX]; + char *name; + + name = aname; + if(name[0] == '\0') + error("empty file name"); + validname(name, 1); + + /* + * Find the starting off point (the current slash, the root of + * a device tree, or the current dot) as well as the name to + * evaluate starting there. + */ + nomount = 0; + switch(name[0]){ + case '/': + c = up->env->pgrp->slash; + incref(c); + break; + + case '#': + nomount = 1; + up->genbuf[0] = '\0'; + n = 0; + while(*name!='\0' && (*name != '/' || n < 2)){ + if(n >= sizeof(up->genbuf)-1) + error(Efilename); + up->genbuf[n++] = *name++; + } + up->genbuf[n] = '\0'; + n = chartorune(&r, up->genbuf+1)+1; + if(r == 'M') + error(Enoattach); + /* + * the nodevs exceptions are + * | it only gives access to pipes you create + * e this process's environment + * s private file2chan creation space + * D private secure sockets name space + * a private TLS name space + */ + if(up->env->pgrp->nodevs && + (utfrune("|esDa", r) == nil || r == 's' && up->genbuf[n]!='\0')) + error(Enoattach); + t = devno(r, 1); + if(t == -1) + error(Ebadsharp); + c = devtab[t]->attach(up->genbuf+n); + break; + + default: + c = up->env->pgrp->dot; + incref(c); + break; + } + prefix = name - aname; + + e.name = nil; + e.elems = nil; + e.off = nil; + e.nelems = 0; + if(waserror()){ + cclose(c); + free(e.name); + free(e.elems); + free(e.off); +//dumpmount(); + nexterror(); + } + + /* + * Build a list of elements in the path. + */ + parsename(name, &e); + + /* + * On create, .... + */ + if(amode == Acreate){ + /* perm must have DMDIR if last element is / or /. */ + if(e.mustbedir && !(perm&DMDIR)){ + npath = e.nelems; + strcpy(tmperrbuf, "create without DMDIR"); + goto NameError; + } + + /* don't try to walk the last path element just yet. */ + if(e.nelems == 0) + error(Eexist); + e.nelems--; + } + + if(walk(&c, e.elems, e.nelems, nomount, &npath) < 0){ + if(npath < 0 || npath > e.nelems){ + print("namec %s walk error npath=%d\n", aname, npath); + nexterror(); + } + strcpy(tmperrbuf, up->env->errstr); + NameError: + len = prefix+e.off[npath]; + if(len < ERRMAX/3 || (name=memrchr(aname, '/', len))==nil || name==aname) + snprint(up->genbuf, sizeof up->genbuf, "%.*s", len, aname); + else + snprint(up->genbuf, sizeof up->genbuf, "...%.*s", (int)(len-(name-aname)), name); + snprint(up->env->errstr, ERRMAX, "%#q %s", up->genbuf, tmperrbuf); + nexterror(); + } + + if(e.mustbedir && !(c->qid.type&QTDIR)){ + npath = e.nelems; + strcpy(tmperrbuf, "not a directory"); + goto NameError; + } + + if(amode == Aopen && (omode&3) == OEXEC && (c->qid.type&QTDIR)){ + npath = e.nelems; + error("cannot exec directory"); + } + + switch(amode){ + case Aaccess: + if(!nomount) + domount(&c, nil); + break; + + case Abind: + m = nil; + if(!nomount) + domount(&c, &m); + if(c->umh != nil) + putmhead(c->umh); + c->umh = m; + break; + + case Aremove: + case Aopen: + Open: + /* save the name; domount might change c */ + cname = c->name; + incref(cname); + m = nil; + if(!nomount) + domount(&c, &m); + + /* our own copy to open or remove */ + c = cunique(c); + + /* now it's our copy anyway, we can put the name back */ + cnameclose(c->name); + c->name = cname; + + switch(amode){ + case Aremove: + putmhead(m); + break; + + case Aopen: + case Acreate: +if(c->umh != nil){ + print("cunique umh\n"); + putmhead(c->umh); + c->umh = nil; +} + + /* only save the mount head if it's a multiple element union */ + if(m && m->mount && m->mount->next) + c->umh = m; + else + putmhead(m); + + /* save registers else error() in open has wrong value of c saved */ + saveregisters(); + + if(omode == OEXEC) + c->flag &= ~CCACHE; + + c = devtab[c->type]->open(c, omode&~OCEXEC); + + if(omode & OCEXEC) + c->flag |= CCEXEC; + if(omode & ORCLOSE) + c->flag |= CRCLOSE; + break; + } + break; + + case Atodir: + /* + * Directories (e.g. for cd) are left before the mount point, + * so one may mount on / or . and see the effect. + */ + if(!(c->qid.type & QTDIR)) + error(Enotdir); + break; + + case Amount: + /* + * When mounting on an already mounted upon directory, + * one wants subsequent mounts to be attached to the + * original directory, not the replacement. Don't domount. + */ + break; + + case Acreate: + /* + * We've already walked all but the last element. + * If the last exists, try to open it OTRUNC. + * If omode&OEXCL is set, just give up. + */ + e.nelems++; + if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) == 0){ + if(omode&OEXCL) + error(Eexist); + omode |= OTRUNC; + goto Open; + } + + /* + * The semantics of the create(2) system call are that if the + * file exists and can be written, it is to be opened with truncation. + * On the other hand, the create(5) message fails if the file exists. + * If we get two create(2) calls happening simultaneously, + * they might both get here and send create(5) messages, but only + * one of the messages will succeed. To provide the expected create(2) + * semantics, the call with the failed message needs to try the above + * walk again, opening for truncation. This correctly solves the + * create/create race, in the sense that any observable outcome can + * be explained as one happening before the other. + * The create/create race is quite common. For example, it happens + * when two rc subshells simultaneously update the same + * environment variable. + * + * The implementation still admits a create/create/remove race: + * (A) walk to file, fails + * (B) walk to file, fails + * (A) create file, succeeds, returns + * (B) create file, fails + * (A) remove file, succeeds, returns + * (B) walk to file, return failure. + * + * This is hardly as common as the create/create race, and is really + * not too much worse than what might happen if (B) got a hold of a + * file descriptor and then the file was removed -- either way (B) can't do + * anything with the result of the create call. So we don't care about this race. + * + * Applications that care about more fine-grained decision of the races + * can use the OEXCL flag to get at the underlying create(5) semantics; + * by default we provide the common case. + * + * We need to stay behind the mount point in case we + * need to do the first walk again (should the create fail). + * + * We also need to cross the mount point and find the directory + * in the union in which we should be creating. + * + * The channel staying behind is c, the one moving forward is cnew. + */ + m = nil; + cnew = nil; /* is this assignment necessary? */ + if(!waserror()){ /* try create */ + if(!nomount && findmount(&cnew, &m, c->type, c->dev, c->qid)) + cnew = createdir(cnew, m); + else{ + cnew = c; + incref(cnew); + } + + /* + * We need our own copy of the Chan because we're + * about to send a create, which will move it. Once we have + * our own copy, we can fix the name, which might be wrong + * if findmount gave us a new Chan. + */ + cnew = cunique(cnew); + cnameclose(cnew->name); + cnew->name = c->name; + incref(cnew->name); + + devtab[cnew->type]->create(cnew, e.elems[e.nelems-1], omode&~(OEXCL|OCEXEC), perm); + poperror(); + if(omode & OCEXEC) + cnew->flag |= CCEXEC; + if(omode & ORCLOSE) + cnew->flag |= CRCLOSE; + if(m) + putmhead(m); + cclose(c); + c = cnew; + c->name = addelem(c->name, e.elems[e.nelems-1]); + break; + }else{ /* create failed */ + cclose(cnew); + if(m) + putmhead(m); + if(omode & OEXCL) + nexterror(); + /* save error */ + createerr = up->env->errstr; + up->env->errstr = tmperrbuf; + /* note: we depend that walk does not error */ + if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) < 0){ + up->env->errstr = createerr; + error(createerr); /* report true error */ + } + up->env->errstr = createerr; + omode |= OTRUNC; + goto Open; + } + panic("namec: not reached"); + + default: + panic("unknown namec access %d\n", amode); + } + + poperror(); + + /* place final element in genbuf for e.g. exec */ + if(e.nelems > 0) + kstrcpy(up->genbuf, e.elems[e.nelems-1], sizeof up->genbuf); + else + kstrcpy(up->genbuf, ".", sizeof up->genbuf); + free(e.name); + free(e.elems); + free(e.off); + + return c; +} + +/* + * name is valid. skip leading / and ./ as much as possible + */ +char* +skipslash(char *name) +{ + while(name[0]=='/' || (name[0]=='.' && (name[1]==0 || name[1]=='/'))) + name++; + return name; +} + +char isfrog[256]={ + /*NUL*/ 1, 1, 1, 1, 1, 1, 1, 1, + /*BKS*/ 1, 1, 1, 1, 1, 1, 1, 1, + /*DLE*/ 1, 1, 1, 1, 1, 1, 1, 1, + /*CAN*/ 1, 1, 1, 1, 1, 1, 1, 1, + ['/'] 1, + [0x7f] 1, +}; + +/* + * Check that the name + * a) is in valid memory. + * b) is shorter than 2^16 bytes, so it can fit in a 9P string field. + * c) contains no frogs. + * The first byte is known to be addressible by the requester, so the + * routine works for kernel and user memory both. + * The parameter slashok flags whether a slash character is an error + * or a valid character. + */ +void +validname(char *aname, int slashok) +{ + char *ename, *name; + int c; + Rune r; + + name = aname; + ename = memchr(name, 0, (1<<16)); + + if(ename==nil || ename-name>=(1<<16)) + error("name too long"); + + while(*name){ + /* all characters above '~' are ok */ + c = *(uchar*)name; + if(c >= Runeself) + name += chartorune(&r, name); + else{ + if(isfrog[c]) + if(!slashok || c!='/'){ + snprint(up->genbuf, sizeof(up->genbuf), "%s: %q", Ebadchar, aname); + error(up->genbuf); + } + name++; + } + } +} + +void +isdir(Chan *c) +{ + if(c->qid.type & QTDIR) + return; + error(Enotdir); +} + +/* + * This is necessary because there are many + * pointers to the top of a given mount list: + * + * - the mhead in the namespace hash table + * - the mhead in chans returned from findmount: + * used in namec and then by unionread. + * - the mhead in chans returned from createdir: + * used in the open/create race protect, which is gone. + * + * The RWlock in the Mhead protects the mount list it contains. + * The mount list is deleted when we cunmount. + * The RWlock ensures that nothing is using the mount list at that time. + * + * It is okay to replace c->mh with whatever you want as + * long as you are sure you have a unique reference to it. + * + * This comment might belong somewhere else. + */ +void +putmhead(Mhead *m) +{ + if(m && decref(m) == 0){ + m->mount = (Mount*)0xCafeBeef; + free(m); + } +} diff --git a/os/port/cis.c b/os/port/cis.c new file mode 100644 index 00000000..368eb6d6 --- /dev/null +++ b/os/port/cis.c @@ -0,0 +1,539 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +enum{ + Linktarget = 0x13, +}; + +/* + * read and crack the card information structure enough to set + * important parameters like power + */ +/* cis memory walking */ +typedef struct Cisdat { + uchar *cisbase; + int cispos; + int cisskip; + int cislen; +} Cisdat; + +static void tcfig(PCMslot*, Cisdat*, int); +static void tentry(PCMslot*, Cisdat*, int); +static void tvers1(PCMslot*, Cisdat*, int); +static void tlonglnkmfc(PCMslot*, Cisdat*, int); + +static int +readc(Cisdat *cis, uchar *x) +{ + if(cis->cispos >= cis->cislen) + return 0; + *x = cis->cisbase[cis->cisskip*cis->cispos]; + cis->cispos++; + return 1; +} + +static int +xcistuple(int slotno, int tuple, int subtuple, void *v, int nv, int attr) +{ + PCMmap *m; + Cisdat cis; + int i, l; + uchar *p; + uchar type, link, n, c; + int this, subtype; + + m = pcmmap(slotno, 0, 0, attr); + if(m == 0) + return -1; + + cis.cisbase = KADDR(m->isa); + cis.cispos = 0; + cis.cisskip = attr ? 2 : 1; + cis.cislen = m->len; + + /* loop through all the tuples */ + for(i = 0; i < 1000; i++){ + this = cis.cispos; + if(readc(&cis, &type) != 1) + break; + if(type == 0xFF) + break; + if(readc(&cis, &link) != 1) + break; + if(link == 0xFF) + break; + + n = link; + if(link > 1 && subtuple != -1){ + if(readc(&cis, &c) != 1) + break; + subtype = c; + n--; + }else + subtype = -1; + + if(type == tuple && subtype == subtuple){ + p = v; + for(l=0; l<nv && l<n; l++) + if(readc(&cis, p++) != 1) + break; + pcmunmap(slotno, m); + return nv; + } + cis.cispos = this + (2+link); + } + pcmunmap(slotno, m); + return -1; +} + +int +pcmcistuple(int slotno, int tuple, int subtuple, void *v, int nv) +{ + int n; + + /* try attribute space, then memory */ + if((n = xcistuple(slotno, tuple, subtuple, v, nv, 1)) >= 0) + return n; + return xcistuple(slotno, tuple, subtuple, v, nv, 0); +} + +void +pcmcisread(PCMslot *pp) +{ + int this; + Cisdat cis; + PCMmap *m; + uchar type, link; + + memset(pp->ctab, 0, sizeof(pp->ctab)); + pp->ncfg = 0; + memset(pp->cfg, 0, sizeof(pp->cfg)); + pp->configed = 0; + pp->nctab = 0; + pp->verstr[0] = 0; + + /* + * Read all tuples in attribute space. + */ + m = pcmmap(pp->slotno, 0, 0, 1); + if(m == 0) + return; + + cis.cisbase = KADDR(m->isa); + cis.cispos = 0; + cis.cisskip = 2; + cis.cislen = m->len; + + /* loop through all the tuples */ + for(;;){ + this = cis.cispos; + if(readc(&cis, &type) != 1) + break; + if(type == 0xFF) + break; + if(readc(&cis, &link) != 1) + break; + + switch(type){ + default: + break; + case 6: + tlonglnkmfc(pp, &cis, type); + break; + case 0x15: + tvers1(pp, &cis, type); + break; + case 0x1A: + tcfig(pp, &cis, type); + break; + case 0x1B: + tentry(pp, &cis, type); + break; + } + + if(link == 0xFF) + break; + cis.cispos = this + (2+link); + } + pcmunmap(pp->slotno, m); +} + +static ulong +getlong(Cisdat *cis, int size) +{ + uchar c; + int i; + ulong x; + + x = 0; + for(i = 0; i < size; i++){ + if(readc(cis, &c) != 1) + break; + x |= c<<(i*8); + } + return x; +} + +static void +tcfig(PCMslot *pp, Cisdat *cis, int ) +{ + uchar size, rasize, rmsize; + uchar last; + + if(readc(cis, &size) != 1) + return; + rasize = (size&0x3) + 1; + rmsize = ((size>>2)&0xf) + 1; + if(readc(cis, &last) != 1) + return; + + if(pp->ncfg >= 8){ + print("tcfig: too many configuration registers\n"); + return; + } + + pp->cfg[pp->ncfg].caddr = getlong(cis, rasize); + pp->cfg[pp->ncfg].cpresent = getlong(cis, rmsize); + pp->ncfg++; +} + +static ulong vexp[8] = +{ + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000 +}; +static ulong vmant[16] = +{ + 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, 90, +}; + +static ulong +microvolt(Cisdat *cis) +{ + uchar c; + ulong microvolts; + ulong exp; + + if(readc(cis, &c) != 1) + return 0; + exp = vexp[c&0x7]; + microvolts = vmant[(c>>3)&0xf]*exp; + while(c & 0x80){ + if(readc(cis, &c) != 1) + return 0; + switch(c){ + case 0x7d: + break; /* high impedence when sleeping */ + case 0x7e: + case 0x7f: + microvolts = 0; /* no connection */ + break; + default: + exp /= 10; + microvolts += exp*(c&0x7f); + } + } + return microvolts; +} + +static ulong +nanoamps(Cisdat *cis) +{ + uchar c; + ulong nanoamps; + + if(readc(cis, &c) != 1) + return 0; + nanoamps = vexp[c&0x7]*vmant[(c>>3)&0xf]; + while(c & 0x80){ + if(readc(cis, &c) != 1) + return 0; + if(c == 0x7d || c == 0x7e || c == 0x7f) + nanoamps = 0; + } + return nanoamps; +} + +/* + * only nominal voltage (feature 1) is important for config, + * other features must read card to stay in sync. + */ +static ulong +power(Cisdat *cis) +{ + uchar feature; + ulong mv; + + mv = 0; + if(readc(cis, &feature) != 1) + return 0; + if(feature & 1) + mv = microvolt(cis); + if(feature & 2) + microvolt(cis); + if(feature & 4) + microvolt(cis); + if(feature & 8) + nanoamps(cis); + if(feature & 0x10) + nanoamps(cis); + if(feature & 0x20) + nanoamps(cis); + if(feature & 0x40) + nanoamps(cis); + return mv/1000000; +} + +static ulong mantissa[16] = +{ 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, }; + +static ulong exponent[8] = +{ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, }; + +static ulong +ttiming(Cisdat *cis, int scale) +{ + uchar unscaled; + ulong nanosecs; + + if(readc(cis, &unscaled) != 1) + return 0; + nanosecs = (mantissa[(unscaled>>3)&0xf]*exponent[unscaled&7])/10; + nanosecs = nanosecs * vexp[scale]; + return nanosecs; +} + +static void +timing(Cisdat *cis, PCMconftab *ct) +{ + uchar c, i; + + if(readc(cis, &c) != 1) + return; + i = c&0x3; + if(i != 3) + ct->maxwait = ttiming(cis, i); /* max wait */ + i = (c>>2)&0x7; + if(i != 7) + ct->readywait = ttiming(cis, i); /* max ready/busy wait */ + i = (c>>5)&0x7; + if(i != 7) + ct->otherwait = ttiming(cis, i); /* reserved wait */ +} + +static void +iospaces(Cisdat *cis, PCMconftab *ct) +{ + uchar c; + int i, nio; + + ct->nio = 0; + if(readc(cis, &c) != 1) + return; + + ct->bit16 = ((c>>5)&3) >= 2; + if(!(c & 0x80)){ + ct->io[0].start = 0; + ct->io[0].len = 1<<(c&0x1f); + ct->nio = 1; + return; + } + + if(readc(cis, &c) != 1) + return; + + /* + * For each of the range descriptions read the + * start address and the length (value is length-1). + */ + nio = (c&0xf)+1; + for(i = 0; i < nio; i++){ + ct->io[i].start = getlong(cis, (c>>4)&0x3); + ct->io[i].len = getlong(cis, (c>>6)&0x3)+1; + } + ct->nio = nio; +} + +static void +irq(Cisdat *cis, PCMconftab *ct) +{ + uchar c; + + if(readc(cis, &c) != 1) + return; + ct->irqtype = c & 0xe0; + if(c & 0x10) + ct->irqs = getlong(cis, 2); + else + ct->irqs = 1<<(c&0xf); + ct->irqs &= 0xDEB8; /* levels available to card */ +} + +static void +memspace(Cisdat *cis, int asize, int lsize, int host) +{ + ulong haddress, address, len; + + len = getlong(cis, lsize)*256; + address = getlong(cis, asize)*256; + USED(len, address); + if(host){ + haddress = getlong(cis, asize)*256; + USED(haddress); + } +} + +static void +tentry(PCMslot *pp, Cisdat *cis, int ) +{ + uchar c, i, feature; + PCMconftab *ct; + + if(pp->nctab >= nelem(pp->ctab)) + return; + if(readc(cis, &c) != 1) + return; + ct = &pp->ctab[pp->nctab++]; + + /* copy from last default config */ + if(pp->def) + *ct = *pp->def; + + ct->index = c & 0x3f; + + /* is this the new default? */ + if(c & 0x40) + pp->def = ct; + + /* memory wait specified? */ + if(c & 0x80){ + if(readc(cis, &i) != 1) + return; + if(i&0x80) + ct->memwait = 1; + } + + if(readc(cis, &feature) != 1) + return; + switch(feature&0x3){ + case 1: + ct->vpp1 = ct->vpp2 = power(cis); + break; + case 2: + power(cis); + ct->vpp1 = ct->vpp2 = power(cis); + break; + case 3: + power(cis); + ct->vpp1 = power(cis); + ct->vpp2 = power(cis); + break; + default: + break; + } + if(feature&0x4) + timing(cis, ct); + if(feature&0x8) + iospaces(cis, ct); + if(feature&0x10) + irq(cis, ct); + switch((feature>>5)&0x3){ + case 1: + memspace(cis, 0, 2, 0); + break; + case 2: + memspace(cis, 2, 2, 0); + break; + case 3: + if(readc(cis, &c) != 1) + return; + for(i = 0; i <= (c&0x7); i++) + memspace(cis, (c>>5)&0x3, (c>>3)&0x3, c&0x80); + break; + } + pp->configed++; +} + +static void +tvers1(PCMslot *pp, Cisdat *cis, int ) +{ + uchar c, major, minor, last; + int i; + + if(readc(cis, &major) != 1) + return; + if(readc(cis, &minor) != 1) + return; + last = 0; + for(i = 0; i < sizeof(pp->verstr)-1; i++){ + if(readc(cis, &c) != 1) + return; + if(c == 0) + c = ';'; + if(c == '\n') + c = ';'; + if(c == 0xff) + break; + if(c == ';' && last == ';') + continue; + pp->verstr[i] = c; + last = c; + } + pp->verstr[i] = 0; +} + +static void +tlonglnkmfc(PCMslot *pp, Cisdat *cis, int) +{ + int i, npos, opos; + uchar nfn, space, expect, type, this, link; + + readc(cis, &nfn); + for(i = 0; i < nfn; i++){ + readc(cis, &space); + npos = getlong(cis, 4); + opos = cis->cispos; + cis->cispos = npos; + expect = Linktarget; + + while(1){ + this = cis->cispos; + if(readc(cis, &type) != 1) + break; + if(type == 0xFF) + break; + if(readc(cis, &link) != 1) + break; + + if(expect && expect != type){ + print("tlonglnkmfc: expected %X found %X\n", + expect, type); + break; + } + expect = 0; + + switch(type){ + default: + break; + case 0x15: + tvers1(pp, cis, type); + break; + case 0x1A: + tcfig(pp, cis, type); + break; + case 0x1B: + tentry(pp, cis, type); + break; + } + + if(link == 0xFF) + break; + cis->cispos = this + (2+link); + } + cis->cispos = opos; + } +} diff --git a/os/port/dev.c b/os/port/dev.c new file mode 100644 index 00000000..4e91b8d0 --- /dev/null +++ b/os/port/dev.c @@ -0,0 +1,434 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +extern ulong kerndate; + +void +mkqid(Qid *q, vlong path, ulong vers, int type) +{ + q->type = type; + q->vers = vers; + q->path = path; +} + +int +devno(int c, int user) +{ + int i; + + for(i = 0; devtab[i] != nil; i++) { + if(devtab[i]->dc == c) + return i; + } + if(user == 0) + panic("devno %C 0x%ux", c, c); + + return -1; +} + +void +devdir(Chan *c, Qid qid, char *n, vlong length, char *user, long perm, Dir *db) +{ + db->name = n; + if(c->flag&CMSG) + qid.type |= QTMOUNT; + db->qid = qid; + db->type = devtab[c->type]->dc; + db->dev = c->dev; + db->mode = perm; + db->mode |= qid.type << 24; + db->atime = seconds(); + db->mtime = kerndate; + db->length = length; + db->uid = user; + db->gid = eve; + db->muid = user; +} + +/* + * the zeroth element of the table MUST be the directory itself for .. +*/ +int +devgen(Chan *c, char*, Dirtab *tab, int ntab, int i, Dir *dp) +{ + if(tab == 0) + return -1; + if(i != DEVDOTDOT){ + /* skip over the first element, that for . itself */ + i++; + if(i >= ntab) + return -1; + tab += i; + } + devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp); + return 1; +} + +void +devreset(void) +{ +} + +void +devinit(void) +{ +} + +void +devshutdown(void) +{ +} + +Chan* +devattach(int tc, char *spec) +{ + Chan *c; + char *buf; + + c = newchan(); + mkqid(&c->qid, 0, 0, QTDIR); + c->type = devno(tc, 0); + if(spec == nil) + spec = ""; + buf = smalloc(4+strlen(spec)+1); + sprint(buf, "#%C%s", tc, spec); + c->name = newcname(buf); + free(buf); + return c; +} + + +Chan* +devclone(Chan *c) +{ + Chan *nc; + + if(c->flag & COPEN) + panic("clone of open file type %C\n", devtab[c->type]->dc); + + nc = newchan(); + + nc->type = c->type; + nc->dev = c->dev; + nc->mode = c->mode; + nc->qid = c->qid; + nc->offset = c->offset; + nc->umh = nil; + nc->mountid = c->mountid; + nc->aux = c->aux; + nc->mqid = c->mqid; + nc->mcp = c->mcp; + return nc; +} + +Walkqid* +devwalk(Chan *c, Chan *nc, char **name, int nname, Dirtab *tab, int ntab, Devgen *gen) +{ + int i, j, alloc; + Walkqid *wq; + char *n; + Dir dir; + + if(nname > 0) + isdir(c); + + alloc = 0; + wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); + if(waserror()){ + if(alloc && wq->clone!=nil) + cclose(wq->clone); + free(wq); + return nil; + } + if(nc == nil){ + nc = devclone(c); + nc->type = 0; /* device doesn't know about this channel yet */ + alloc = 1; + } + wq->clone = nc; + + for(j=0; j<nname; j++){ + if(!(nc->qid.type&QTDIR)){ + if(j==0) + error(Enotdir); + goto Done; + } + n = name[j]; + if(strcmp(n, ".") == 0){ + Accept: + wq->qid[wq->nqid++] = nc->qid; + continue; + } + if(strcmp(n, "..") == 0){ + (*gen)(nc, nil, tab, ntab, DEVDOTDOT, &dir); + nc->qid = dir.qid; + goto Accept; + } + /* + * Ugly problem: If we're using devgen, make sure we're + * walking the directory itself, represented by the first + * entry in the table, and not trying to step into a sub- + * directory of the table, e.g. /net/net. Devgen itself + * should take care of the problem, but it doesn't have + * the necessary information (that we're doing a walk). + */ + if(gen==devgen && nc->qid.path!=tab[0].qid.path) + goto Notfound; + for(i=0;; i++) { + switch((*gen)(nc, n, tab, ntab, i, &dir)){ + case -1: + Notfound: + if(j == 0) + error(Enonexist); + kstrcpy(up->env->errstr, Enonexist, ERRMAX); + goto Done; + case 0: + continue; + case 1: + if(strcmp(n, dir.name) == 0){ + nc->qid = dir.qid; + goto Accept; + } + continue; + } + } + } + /* + * We processed at least one name, so will return some data. + * If we didn't process all nname entries succesfully, we drop + * the cloned channel and return just the Qids of the walks. + */ +Done: + poperror(); + if(wq->nqid < nname){ + if(alloc) + cclose(wq->clone); + wq->clone = nil; + }else if(wq->clone){ + /* attach cloned channel to same device */ + wq->clone->type = c->type; + } + return wq; +} + +int +devstat(Chan *c, uchar *db, int n, Dirtab *tab, int ntab, Devgen *gen) +{ + int i; + Dir dir; + char *p, *elem; + + for(i=0;; i++) + switch((*gen)(c, nil, tab, ntab, i, &dir)){ + case -1: + if(c->qid.type & QTDIR){ + if(c->name == nil) + elem = "???"; + else if(strcmp(c->name->s, "/") == 0) + elem = "/"; + else + for(elem=p=c->name->s; *p; p++) + if(*p == '/') + elem = p+1; + devdir(c, c->qid, elem, 0, eve, DMDIR|0555, &dir); + n = convD2M(&dir, db, n); + if(n == 0) + error(Ebadarg); + return n; + } + print("%s %s: devstat %C %llux\n", + up->text, up->env->user, + devtab[c->type]->dc, c->qid.path); + + error(Enonexist); + case 0: + break; + case 1: + if(c->qid.path == dir.qid.path) { + if(c->flag&CMSG) + dir.mode |= DMMOUNT; + n = convD2M(&dir, db, n); + if(n == 0) + error(Ebadarg); + return n; + } + break; + } + error(Egreg); /* not reached? */ + return -1; +} + +long +devdirread(Chan *c, char *d, long n, Dirtab *tab, int ntab, Devgen *gen) +{ + long m, dsz; + struct{ + Dir; + char slop[100]; + }dir; + + for(m=0; m<n; c->dri++) { + switch((*gen)(c, nil, tab, ntab, c->dri, &dir)){ + case -1: + return m; + + case 0: + break; + + case 1: + dsz = convD2M(&dir, (uchar*)d, n-m); + if(dsz <= BIT16SZ){ /* <= not < because this isn't stat; read is stuck */ + if(m == 0) + error(Eshort); + return m; + } + m += dsz; + d += dsz; + break; + } + } + + return m; +} + +/* + * error(Eperm) if open permission not granted for up->env->user. + */ +void +devpermcheck(char *fileuid, ulong perm, int omode) +{ + ulong t; + static int access[] = { 0400, 0200, 0600, 0100 }; + + if(strcmp(up->env->user, fileuid) == 0) + perm <<= 0; + else + if(strcmp(up->env->user, eve) == 0) + perm <<= 3; + else + perm <<= 6; + + t = access[omode&3]; + if((t&perm) != t) + error(Eperm); +} + +Chan* +devopen(Chan *c, int omode, Dirtab *tab, int ntab, Devgen *gen) +{ + int i; + Dir dir; + + for(i=0;; i++) { + switch((*gen)(c, nil, tab, ntab, i, &dir)){ + case -1: + goto Return; + case 0: + break; + case 1: + if(c->qid.path == dir.qid.path) { + devpermcheck(dir.uid, dir.mode, omode); + goto Return; + } + break; + } + } +Return: + c->offset = 0; + if((c->qid.type&QTDIR) && omode!=OREAD) + error(Eperm); + c->mode = openmode(omode); + c->flag |= COPEN; + return c; +} + +void +devcreate(Chan*, char*, int, ulong) +{ + error(Eperm); +} + +Block* +devbread(Chan *c, long n, ulong offset) +{ + Block *bp; + + bp = allocb(n); + if(bp == 0) + error(Enomem); + if(waserror()) { + freeb(bp); + nexterror(); + } + bp->wp += devtab[c->type]->read(c, bp->wp, n, offset); + poperror(); + return bp; +} + +long +devbwrite(Chan *c, Block *bp, ulong offset) +{ + long n; + + if(waserror()) { + freeb(bp); + nexterror(); + } + n = devtab[c->type]->write(c, bp->rp, BLEN(bp), offset); + poperror(); + freeb(bp); + + return n; +} + +void +devremove(Chan*) +{ + error(Eperm); +} + +int +devwstat(Chan*, uchar*, int) +{ + error(Eperm); + return 0; +} + +void +devpower(int) +{ + error(Eperm); +} + +int +devconfig(int, char *, DevConf *) +{ + error(Eperm); + return 0; +} + +/* + * check that the name in a wstat is plausible + */ +void +validwstatname(char *name) +{ + validname(name, 0); + if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) + error(Efilename); +} + +Dev* +devbyname(char *name) +{ + int i; + + for(i = 0; devtab[i] != nil; i++) + if(strcmp(devtab[i]->name, name) == 0) + return devtab[i]; + return nil; +} diff --git a/os/port/devXXX.c b/os/port/devXXX.c new file mode 100644 index 00000000..2c76abcd --- /dev/null +++ b/os/port/devXXX.c @@ -0,0 +1,147 @@ +/* + * template for making a new device + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + + +enum{ + Qdir, + Qdata, +}; + +static +Dirtab XXXtab[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, /* entry for "." must be first if devgen used */ + "data", {Qdata, 0}, 0, 0666, +}; + +static void +XXXreset(void) /* default in dev.c */ +{ +} + +static void +XXXinit(void) /* default in dev.c */ +{ +} + +static Chan* +XXXattach(char* spec) +{ + return devattach('X', spec); +} + +static Walkqid* +XXXwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, XXXtab, nelem(XXXtab), devgen); +} + +static int +XXXstat(Chan* c, uchar *db, int n) +{ + return devstat(c, db, n, XXXtab, nelem(XXXtab), devgen); +} + +static Chan* +XXXopen(Chan* c, int omode) +{ + return devopen(c, omode, XXXtab, nelem(XXXtab), devgen); +} + +static void +XXXcreate(Chan* c, char* name, int omode, ulong perm) /* default in dev.c */ +{ + USED(c, name, omode, perm); + error(Eperm); +} + +static void +XXXremove(Chan* c) /* default in dev.c */ +{ + USED(c); + error(Eperm); +} + +static int +XXXwstat(Chan* c, uchar *dp, int n) /* default in dev.c */ +{ + USED(c, dp); + error(Eperm); + return n; +} + +static void +XXXclose(Chan* c) +{ + USED(c); +} + +static long +XXXread(Chan* c, void* a, long n, vlong offset) +{ + USED(offset); + + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, XXXtab, nelem(XXXtab), devgen); + case Qdata: + break; + default: + n=0; + break; + } + return n; +} + +static Block* +XXXbread(Chan* c, long n, ulong offset) /* default in dev.c */ +{ + return devbread(c, n, offset); +} + +static long +XXXwrite(Chan* c, void* a, long n, vlong offset) +{ + USED(a, offset); + + switch((ulong)c->qid.path){ + case Qdata: + break; + default: + error(Ebadusefd); + } + return n; +} + +static long +XXXbwrite(Chan* c, Block* bp, ulong offset) /* default in dev.c */ +{ + return devbwrite(c, bp, offset); +} + +Dev XXXdevtab = { /* defaults in dev.c */ + 'X', + "XXX", + + XXXreset, /* devreset */ + XXXinit, /* devinit */ + devshutdown, + XXXattach, + XXXwalk, + XXXstat, + XXXopen, + XXXcreate, /* devcreate */ + XXXclose, + XXXread, + XXXbread, /* devbread */ + XXXwrite, + XXXbwrite, /* devbwrite */ + XXXremove, /* devremove */ + XXXwstat, /* devwstat */ +}; diff --git a/os/port/devaudio.c b/os/port/devaudio.c new file mode 100644 index 00000000..b1441e03 --- /dev/null +++ b/os/port/devaudio.c @@ -0,0 +1,947 @@ +/* + * SB 16 driver + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" +#include "audio.h" + +typedef struct AQueue AQueue; +typedef struct Buf Buf; + +enum +{ + Qdir = 0, + Qaudio, + Qvolume, + + Fmono = 1, + Fin = 2, + Fout = 4, + + Aclosed = 0, + Aread, + Awrite, + + Vaudio = 0, + Vsynth, + Vcd, + Vline, + Vmic, + Vspeaker, + Vtreb, + Vbass, + Vspeed, + Nvol, + + Speed = 44100, + Ncmd = 50, /* max volume command words */ +}; + +static +Dirtab audiodir[] = +{ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "audio", {Qaudio}, 0, 0666, + "volume", {Qvolume}, 0, 0666, +}; + +struct Buf +{ + uchar* virt; + ulong phys; + Buf* next; +}; +struct AQueue +{ + Lock; + Buf* first; + Buf* last; +}; +static struct +{ + QLock; + Rendez vous; + int bufinit; /* boolean if buffers allocated */ + int curcount; /* how much data in current buffer */ + int active; /* boolean dma running */ + int intr; /* boolean an interrupt has happened */ + int amode; /* Aclosed/Aread/Awrite for /audio */ + int rivol[Nvol]; /* right/left input/output volumes */ + int livol[Nvol]; + int rovol[Nvol]; + int lovol[Nvol]; + int major; /* SB16 major version number (sb 4) */ + int minor; /* SB16 minor version number */ + + Buf buf[Nbuf]; /* buffers and queues */ + AQueue empty; + AQueue full; + Buf* current; + Buf* filling; +} audio; + +static struct +{ + char* name; + int flag; + int ilval; /* initial values */ + int irval; +} volumes[] = +{ +[Vaudio] "audio", Fout, 50, 50, +[Vsynth] "synth", Fin|Fout, 0, 0, +[Vcd] "cd", Fin|Fout, 0, 0, +[Vline] "line", Fin|Fout, 0, 0, +[Vmic] "mic", Fin|Fout|Fmono, 0, 0, +[Vspeaker] "speaker", Fout|Fmono, 0, 0, + +[Vtreb] "treb", Fout, 50, 50, +[Vbass] "bass", Fout, 50, 50, + +[Vspeed] "speed", Fin|Fout|Fmono, Speed, Speed, + 0 +}; + +static struct +{ + Lock; + int reset; /* io ports to the sound blaster */ + int read; + int write; + int wstatus; + int rstatus; + int mixaddr; + int mixdata; + int clri8; + int clri16; + int clri401; + int dma; +} blaster; + +static void swab(uchar*); + +static char Emajor[] = "soundblaster not responding/wrong version"; +static char Emode[] = "illegal open mode"; +static char Evolume[] = "illegal volume specifier"; + +static int +sbcmd(int val) +{ + int i, s; + + for(i=1<<16; i!=0; i--) { + s = inb(blaster.wstatus); + if((s & 0x80) == 0) { + outb(blaster.write, val); + return 0; + } + } +/* print("#A: sbcmd (#%.2x) timeout\n", val); /**/ + return 1; +} + +static int +sbread(void) +{ + int i, s; + + for(i=1<<16; i!=0; i--) { + s = inb(blaster.rstatus); + if((s & 0x80) != 0) { + return inb(blaster.read); + } + } +/* print("#A: sbread did not respond\n"); /**/ + return 0xbb; +} + +static int +mxcmd(int addr, int val) +{ + + outb(blaster.mixaddr, addr); + outb(blaster.mixdata, val); + return 1; +} + +static int +mxread(int addr) +{ + int s; + + outb(blaster.mixaddr, addr); + s = inb(blaster.mixdata); + return s; +} + +static void +mxcmds(int s, int v) +{ + + if(v > 100) + v = 100; + if(v < 0) + v = 0; + mxcmd(s, (v*255)/100); +} + +static void +mxcmdt(int s, int v) +{ + + if(v > 100) + v = 100; + if(v <= 0) + mxcmd(s, 0); + else + mxcmd(s, 255-100+v); +} + +static void +mxcmdu(int s, int v) +{ + + if(v > 100) + v = 100; + if(v <= 0) + v = 0; + mxcmd(s, 128-50+v); +} + +static void +mxvolume(void) +{ + int *left, *right; + int source; + + if(audio.amode == Aread){ + left = audio.livol; + right = audio.rivol; + }else{ + left = audio.lovol; + right = audio.rovol; + } + + ilock(&blaster); + + mxcmd(0x30, 255); /* left master */ + mxcmd(0x31, 255); /* right master */ + mxcmd(0x3f, 0); /* left igain */ + mxcmd(0x40, 0); /* right igain */ + mxcmd(0x41, 0); /* left ogain */ + mxcmd(0x42, 0); /* right ogain */ + + mxcmds(0x32, left[Vaudio]); + mxcmds(0x33, right[Vaudio]); + + mxcmds(0x34, left[Vsynth]); + mxcmds(0x35, right[Vsynth]); + + mxcmds(0x36, left[Vcd]); + mxcmds(0x37, right[Vcd]); + + mxcmds(0x38, left[Vline]); + mxcmds(0x39, right[Vline]); + + mxcmds(0x3a, left[Vmic]); + mxcmds(0x3b, left[Vspeaker]); + + mxcmdu(0x44, left[Vtreb]); + mxcmdu(0x45, right[Vtreb]); + + mxcmdu(0x46, left[Vbass]); + mxcmdu(0x47, right[Vbass]); + + source = 0; + if(left[Vsynth]) + source |= 1<<6; + if(right[Vsynth]) + source |= 1<<5; + if(left[Vaudio]) + source |= 1<<4; + if(right[Vaudio]) + source |= 1<<3; + if(left[Vcd]) + source |= 1<<2; + if(right[Vcd]) + source |= 1<<1; + if(left[Vmic]) + source |= 1<<0; + if(audio.amode == Aread) + mxcmd(0x3c, 0); /* output switch */ + else + mxcmd(0x3c, source); + mxcmd(0x3d, source); /* input left switch */ + mxcmd(0x3e, source); /* input right switch */ + iunlock(&blaster); +} + +static Buf* +getbuf(AQueue *q) +{ + Buf *b; + + ilock(q); + b = q->first; + if(b) + q->first = b->next; + iunlock(q); + + return b; +} + +static void +putbuf(AQueue *q, Buf *b) +{ + + ilock(q); + b->next = 0; + if(q->first) + q->last->next = b; + else + q->first = b; + q->last = b; + iunlock(q); +} + +/* + * move the dma to the next buffer + */ +static void +contindma(void) +{ + Buf *b; + + if(!audio.active) + goto shutdown; + + b = audio.current; + if(audio.amode == Aread) { + if(b) /* shouldnt happen */ + putbuf(&audio.full, b); + b = getbuf(&audio.empty); + } else { + if(b) /* shouldnt happen */ + putbuf(&audio.empty, b); + b = getbuf(&audio.full); + } + audio.current = b; + if(b == 0) + goto shutdown; + + dmasetup(blaster.dma, b->virt, Bufsize, audio.amode == Aread); + return; + +shutdown: + dmaend(blaster.dma); + sbcmd(0xd9); /* exit at end of count */ + sbcmd(0xd5); /* pause */ + audio.curcount = 0; + audio.active = 0; +} + +/* + * cause sb to get an interrupt per buffer. + * start first dma + */ +static void +startdma(void) +{ + ulong count; + int speed; + + ilock(&blaster); + dmaend(blaster.dma); + if(audio.amode == Aread) { + sbcmd(0x42); /* input sampling rate */ + speed = audio.livol[Vspeed]; + } else { + sbcmd(0x41); /* output sampling rate */ + speed = audio.lovol[Vspeed]; + } + sbcmd(speed>>8); + sbcmd(speed); + + count = (Bufsize >> 1) - 1; + if(audio.amode == Aread) + sbcmd(0xbe); /* A/D, autoinit */ + else + sbcmd(0xb6); /* D/A, autoinit */ + sbcmd(0x30); /* stereo, 16 bit */ + sbcmd(count); + sbcmd(count>>8); + + audio.active = 1; + contindma(); + iunlock(&blaster); +} + +/* + * if audio is stopped, + * start it up again. + */ +static void +pokeaudio(void) +{ + if(!audio.active) + startdma(); +} + +static void +audiosbintr(void) +{ + int stat, dummy; + + stat = mxread(0x82) & 7; /* get irq status */ + if(stat) { + dummy = 0; + if(stat & 2) { + ilock(&blaster); + dummy = inb(blaster.clri16); + contindma(); + iunlock(&blaster); + audio.intr = 1; + wakeup(&audio.vous); + } + if(stat & 1) { + dummy = inb(blaster.clri8); + } + if(stat & 4) { + dummy = inb(blaster.clri401); + } + USED(dummy); + } +} + +static void +pcaudiosbintr(Ureg*, void*) +{ +/* print("#A: audio interrupt\n"); /**/ + audiosbintr(); +} + +static void +audiodmaintr(void) +{ +/* print("#A: dma interrupt\n"); /**/ +} + +static int +anybuf(void*) +{ + return audio.intr; +} + +/* + * wait for some output to get + * empty buffers back. + */ +static void +waitaudio(void) +{ + + audio.intr = 0; + pokeaudio(); + tsleep(&audio.vous, anybuf, 0, 10*1000); + if(audio.intr == 0) { +/* print("#A: audio timeout\n"); /**/ + audio.active = 0; + pokeaudio(); + } +} + +static void +sbbufinit(void) +{ + int i; + void *p; + + for(i=0; i<Nbuf; i++) { + p = xspanalloc(Bufsize, CACHELINESZ, 64*1024); + dcflush(p, Bufsize); + audio.buf[i].virt = UNCACHED(uchar, p); + audio.buf[i].phys = (ulong)PADDR(p); + } +} + +static void +setempty(void) +{ + int i; + + ilock(&blaster); + audio.empty.first = 0; + audio.empty.last = 0; + audio.full.first = 0; + audio.full.last = 0; + audio.current = 0; + audio.filling = 0; + for(i=0; i<Nbuf; i++) + putbuf(&audio.empty, &audio.buf[i]); + iunlock(&blaster); +} + +static void +resetlevel(void) +{ + int i; + + for(i=0; volumes[i].name; i++) { + audio.lovol[i] = volumes[i].ilval; + audio.rovol[i] = volumes[i].irval; + audio.livol[i] = volumes[i].ilval; + audio.rivol[i] = volumes[i].irval; + } +} + +static void +audioinit(void) +{ + ISAConf sbconf; + int i; + + sbconf.port = 0x220; + sbconf.dma = Dma; + sbconf.irq = 7; + if(isaconfig("audio", 0, &sbconf) == 0) + return; + if(strcmp(sbconf.type, "sb16") != 0) + return; + switch(sbconf.port){ + case 0x220: + case 0x240: + case 0x260: + case 0x280: + break; + default: + print("#A: bad port 0x%lx\n", sbconf.port); + return; + } + switch(sbconf.irq){ + case 2: + case 5: + case 7: + case 10: + break; + default: + print("#A: bad irq %d\n", sbconf.irq); + return; + } + + blaster.reset = sbconf.port + 0x6; + blaster.read = sbconf.port + 0xa; + blaster.write = sbconf.port + 0xc; + blaster.wstatus = sbconf.port + 0xc; + blaster.rstatus = sbconf.port + 0xe; + blaster.mixaddr = sbconf.port + 0x4; + blaster.mixdata = sbconf.port + 0x5; + blaster.clri8 = sbconf.port + 0xe; + blaster.clri16 = sbconf.port + 0xf; + blaster.clri401 = sbconf.port + 0x100; + blaster.dma = sbconf.dma; + + seteisadma(blaster.dma, audiodmaintr); + setvec(Int0vec+sbconf.irq, pcaudiosbintr, 0); + + audio.amode = Aclosed; + resetlevel(); + + outb(blaster.reset, 1); + delay(1); /* >3 υs */ + outb(blaster.reset, 0); + delay(1); + + i = sbread(); + if(i != 0xaa) { + print("#A: no response #%.2x\n", i); + return; + } + + sbcmd(0xe1); /* get version */ + audio.major = sbread(); + audio.minor = sbread(); + + if(audio.major != 4) { + print("#A: model #%.2x #%.2x; not SB 16\n", audio.major, audio.minor); + return; + } + /* + * initialize the mixer + */ + mxcmd(0x00, 0); /* Reset mixer */ + mxvolume(); + + /* + * set up irq/dma chans + */ + mxcmd(0x80, /* irq */ + (sbconf.irq==2)? 1: + (sbconf.irq==5)? 2: + (sbconf.irq==7)? 4: + (sbconf.irq==10)? 8: + 0); + mxcmd(0x81, 1<<blaster.dma); /* dma */ +} + +static Chan* +audioattach(char *param) +{ + return devattach('A', param); +} + +static Walkqid* +audiowalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen); +} + +static int +audiostat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, audiodir, nelem(audiodir), devgen); +} + +static Chan* +audioopen(Chan *c, int omode) +{ + int amode; + + if(audio.major != 4) + error(Emajor); + + switch((ulong)c->qid.path) { + default: + error(Eperm); + break; + + case Qvolume: + case Qdir: + break; + + case Qaudio: + amode = Awrite; + if((omode&7) == OREAD) + amode = Aread; + qlock(&audio); + if(audio.amode != Aclosed){ + qunlock(&audio); + error(Einuse); + } + if(audio.bufinit == 0) { + audio.bufinit = 1; + sbbufinit(); + } + audio.amode = amode; + setempty(); + audio.curcount = 0; + qunlock(&audio); + mxvolume(); + break; + } + c = devopen(c, omode, audiodir, nelem(audiodir), devgen); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + + return c; +} + +static void +audioclose(Chan *c) +{ + + switch((ulong)c->qid.path) { + default: + error(Eperm); + break; + + case Qdir: + case Qvolume: + break; + + case Qaudio: + if(c->flag & COPEN) { + qlock(&audio); + audio.amode = Aclosed; + if(waserror()){ + qunlock(&audio); + nexterror(); + } + while(audio.active) + waitaudio(); + setempty(); + poperror(); + qunlock(&audio); + } + break; + } +} + +static long +audioread(Chan *c, void *vp, long n, vlong offset) +{ + int liv, riv, lov, rov; + long m, n0; + char buf[300]; + Buf *b; + int j; + char *a; + + a = vp; + n0 = n; + switch((ulong)c->qid.path) { + default: + error(Eperm); + break; + + case Qdir: + return devdirread(c, a, n, audiodir, nelem(audiodir), devgen); + + case Qaudio: + if(audio.amode != Aread) + error(Emode); + qlock(&audio); + if(waserror()){ + qunlock(&audio); + nexterror(); + } + while(n > 0) { + b = audio.filling; + if(b == 0) { + b = getbuf(&audio.full); + if(b == 0) { + waitaudio(); + continue; + } + audio.filling = b; + swab(b->virt); + audio.curcount = 0; + } + m = Bufsize-audio.curcount; + if(m > n) + m = n; + memmove(a, b->virt+audio.curcount, m); + + audio.curcount += m; + n -= m; + a += m; + if(audio.curcount >= Bufsize) { + audio.filling = 0; + putbuf(&audio.empty, b); + } + } + poperror(); + qunlock(&audio); + break; + + case Qvolume: + j = 0; + buf[0] = 0; + for(m=0; volumes[m].name; m++){ + liv = audio.livol[m]; + riv = audio.rivol[m]; + lov = audio.lovol[m]; + rov = audio.rovol[m]; + j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name); + if((volumes[m].flag & Fmono) || liv==riv && lov==rov){ + if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov) + j += snprint(buf+j, sizeof(buf)-j, " %d", liv); + else{ + if(volumes[m].flag & Fin) + j += snprint(buf+j, sizeof(buf)-j, " in %d", liv); + if(volumes[m].flag & Fout) + j += snprint(buf+j, sizeof(buf)-j, " out %d", lov); + } + }else{ + if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov && riv==rov) + j += snprint(buf+j, sizeof(buf)-j, " left %d right %d", + liv, riv); + else{ + if(volumes[m].flag & Fin) + j += snprint(buf+j, sizeof(buf)-j, " in left %d right %d", + liv, riv); + if(volumes[m].flag & Fout) + j += snprint(buf+j, sizeof(buf)-j, " out left %d right %d", + lov, rov); + } + } + j += snprint(buf+j, sizeof(buf)-j, "\n"); + } + + return readstr(offset, a, n, buf); + } + return n0-n; +} + +static long +audiowrite(Chan *c, void *vp, long n, vlong) +{ + long m, n0; + int i, nf, v, left, right, in, out; + char buf[255], *field[Ncmd]; + Buf *b; + char *a; + + a = vp; + n0 = n; + switch((ulong)c->qid.path) { + default: + error(Eperm); + break; + + case Qvolume: + v = Vaudio; + left = 1; + right = 1; + in = 1; + out = 1; + if(n > sizeof(buf)-1) + n = sizeof(buf)-1; + memmove(buf, a, n); + buf[n] = '\0'; + + nf = getfields(buf, field, Ncmd, 1, " \t\n"); + for(i = 0; i < nf; i++){ + /* + * a number is volume + */ + if(field[i][0] >= '0' && field[i][0] <= '9') { + m = strtoul(field[i], 0, 10); + if(left && out) + audio.lovol[v] = m; + if(left && in) + audio.livol[v] = m; + if(right && out) + audio.rovol[v] = m; + if(right && in) + audio.rivol[v] = m; + mxvolume(); + goto cont0; + } + + for(m=0; volumes[m].name; m++) { + if(strcmp(field[i], volumes[m].name) == 0) { + v = m; + in = 1; + out = 1; + left = 1; + right = 1; + goto cont0; + } + } + + if(strcmp(field[i], "reset") == 0) { + resetlevel(); + mxvolume(); + goto cont0; + } + if(strcmp(field[i], "in") == 0) { + in = 1; + out = 0; + goto cont0; + } + if(strcmp(field[i], "out") == 0) { + in = 0; + out = 1; + goto cont0; + } + if(strcmp(field[i], "left") == 0) { + left = 1; + right = 0; + goto cont0; + } + if(strcmp(field[i], "right") == 0) { + left = 0; + right = 1; + goto cont0; + } + error(Evolume); + break; + cont0:; + } + break; + + case Qaudio: + if(audio.amode != Awrite) + error(Emode); + qlock(&audio); + if(waserror()){ + qunlock(&audio); + nexterror(); + } + while(n > 0) { + b = audio.filling; + if(b == 0) { + b = getbuf(&audio.empty); + if(b == 0) { + waitaudio(); + continue; + } + audio.filling = b; + audio.curcount = 0; + } + + m = Bufsize-audio.curcount; + if(m > n) + m = n; + memmove(b->virt+audio.curcount, a, m); + + audio.curcount += m; + n -= m; + a += m; + if(audio.curcount >= Bufsize) { + audio.filling = 0; + swab(b->virt); + putbuf(&audio.full, b); + } + } + poperror(); + qunlock(&audio); + break; + } + return n0 - n; +} + +static void +swab(uchar *a) +{ + ulong *p, *ep, b; + + if(!SBswab) + return; + p = (ulong*)a; + ep = p + (Bufsize>>2); + while(p < ep) { + b = *p; + b = (b>>24) | (b<<24) | + ((b&0xff0000) >> 8) | + ((b&0x00ff00) << 8); + *p++ = b; + } +} + +Dev audiodevtab = { + 'A', + "audio", + + devreset, + audioinit, + devshutdown, + audioattach, + audiowalk, + audiostat, + audioopen, + devcreate, + audioclose, + audioread, + devbread, + audiowrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devbench.c b/os/port/devbench.c new file mode 100644 index 00000000..22ab5e34 --- /dev/null +++ b/os/port/devbench.c @@ -0,0 +1,1165 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include <interp.h> +#include "io.h" +#include "../port/error.h" +#include <isa.h> +#include "kernel.h" + +/* Builtin module support */ +#include "bench.h" +#include "benchmod.h" + +typedef enum { None, Calibrate, Base, Op, Intr, Dis, Gc, MS2T, xTest}; +static struct { + int inuse; /* reference count */ + int test; + void* scratch; + char* buf; + int bufsz; + char* wpos; + void (*op)(void); + vlong tickstart; +} bench; + +static void +log(char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + bench.wpos = vseprint(bench.wpos, bench.buf+bench.bufsz, msg, ap); + va_end(ap); +} + +void +elog(char *msg, ...) +{ + va_list ap; + + if(bench.buf == 0) + return; + va_start(ap, msg); + bench.wpos = vseprint(bench.wpos, bench.buf+bench.bufsz, msg, ap); + va_end(ap); +} + +static void +clear(void) +{ + bench.wpos = bench.buf; +} + +static long +rep(void *to, long n, ulong offset) +{ + long left = bench.wpos - bench.buf - offset; + if(left < 0) + left = 0; + if(n > left) + n = left; + memmove(to, bench.buf+offset, n); + return n; +} + +static long +notest(int report, void *va, long n, ulong offset) +{ + USED(report, va, n, offset); + if(report) + return rep(va, n, offset); + return 0; +} + +// Calibration +static long MS2TS = 0; // time stamps per millisec +static long US2TS = 0; // time stamps per microsecond + +static long +cal(int report, void *va, long n, ulong offset) +{ + int tot, i, lim, low, max, pl, mdelay; + ulong t; + if(report) + return rep(va, n, offset); + clear(); + setpri(PriRealtime); + lim = 1000; + low = 64000000; + max = 0; + tot = 0; + mdelay = 1000; + for(i=0; i<lim; i++){ + do{ + pl = splhi(); + t = archrdtsc32(); + microdelay(mdelay); + t = archrdtsc32() - t; + splx(pl); + } while(t < 0); + if(t < low) + low = t; + if(t > max) + max = t; + tot += t; + } + MS2TS = tot/lim; + US2TS = MS2TS/1000; + if(va) + log("mdelay=%lud lim=%lud tot=%lud low=%lud max=%lud\n", mdelay, lim, tot, low, max); + setpri(PriNormal); + return n; +} + +/* + * ticks to format string + */ +/*static*/ char * +ts2str(vlong ticks) +{ +#define Nbuf 5 + static char b[Nbuf][40]; + static int n=Nbuf-1; + char *fmt, *unit; + double d; + + if(0){ + print("ticks=%lld MS2TS=%ld\n", ticks, MS2TS); + d = (double)ticks; + print("1:%f\n", d); + d = (double)ticks*1000; + //print("2:%f\n", d); + d = ((double)ticks)/MS2TS; + //print("3:%f\n", d); + } + n = (n+1)%Nbuf; + if(ticks > MS2TS*1000) { + fmt = "%.2f %s"; + unit = "s"; + d = ((double)ticks/MS2TS) * 1000.0; + } else if(ticks > MS2TS) { + fmt = "%.2f %s"; + unit = "ms"; + d = (double)ticks/MS2TS; + } else if(ticks > MS2TS/1000) { + fmt = "%.2f %s"; + unit = "us"; + d = ((double)ticks*1000)/MS2TS; + } else { + fmt = "%.2f %s"; + unit = "ns"; + d = ((double)ticks*1000*1000)/MS2TS; + } + sprint(b[n], fmt, d, unit); + return b[n]; +} + +/* + * ticks to microseconds + */ +static double +ts2us(vlong ticks) +{ + return ((double)ticks*1000)/MS2TS; +} + +/* + * microseconds timestamp + */ +static vlong +bus(int reset) +{ + vlong now; + if(US2TS == 0) + return 0; + if(reset) { + bench.tickstart = archrdtsc(); + return 0; + } + now = archrdtsc(); + return ((now-bench.tickstart))/US2TS; +} + +// Base +static long +base(int report, void *va, long n, ulong offset) +{ + int tot, i, lim, low, max, pl; + ulong t; + char *bm; + + if(report) + return rep(va, n, offset); + clear(); + setpri(PriRealtime); + lim = 1000; + low = 64000000; + max = 0; + tot = 0; + for(i=0; i<lim; i++){ + do { + pl = splhi(); + t = archrdtsc32(); + // do nothing + t = archrdtsc32() - t; + splx(pl); + } while(t < 0); + if(t < low) + low = t; + if(t > max) + max = t; + tot += t; + } + bm = ts2str(tot/lim); + log("%d %lud %lud %lud %lud (%s)\n", up->pid, lim, tot, low, max, bm); + setpri(PriNormal); + return n; +} + +// Timeop + +typedef struct Psync Psync; + +enum { + Maxprocs=3, +}; + +struct Psync { + Rendez r; + int flag; + int id; + int awaken; +}; +static Psync timesync[Maxprocs]; +static Ref nactive; +static Ref nbusy; +static RWlock sync; + +static void +nilop(void) +{ +} + +static int +timev(void *a) +{ + return *(int*)a; +} + +static void +timeop0(void *ap) +{ + int tot, i, lim, low, max; + ulong t; + Psync *ps; + char *bm; + + ps = ap; + setpri(PriRealtime); + incref(&nactive); + sleep(&ps->r, timev, &ps->flag); + rlock(&sync); + lim = 1000; + low = 64000000; + max = 0; + tot = 0; + for(i=0; i<lim; i++){ + do{ + t = archrdtsc32(); + (*bench.op)(); + t = archrdtsc32() - t; + }while(t < 0); + if(t < low) + low = t; + if(t > max) + max = t; + tot += t; + } + bm = ts2str(tot/lim); + log("%d %lud %lud %lud %lud (%s)\n", up->pid, lim, tot, low, max, bm); + runlock(&sync); + pexit("", 0); +} + +static long +timeop(int report, void *va, long n, ulong offset) +{ + int i, np, pl; + + if(report) + return rep(va, n, offset); + clear(); + bench.op = 0; + if(strncmp(va, "nil", 3) == 0) + bench.op = nilop; + else if(strncmp(va, "sched", 5) == 0) + bench.op = sched; + else + return 0; + for(np=1; np<=Maxprocs; np++) { + nactive.ref = 0; + wlock(&sync); + log("%d procs\n", np); + setpri(PriRealtime); + for(i=0; i<np; i++) { + timesync[i].id = i; + kproc("timeop", timeop0, ×ync[i], 0); + } + while(nactive.ref < np) + tsleep(&up->sleep, return0, 0, 20); + for(i=0; i<np; i++){ + timesync[i].flag = 1; + wakeup(×ync[i].r); + } + sched(); + pl = splhi(); + setpri(PriNormal); + wunlock(&sync); + // now they run + wlock(&sync); // wait for last reader + wunlock(&sync); + splx(pl); + } + return n; +} + +typedef struct Ictr Ictr; +struct Ictr { + ulong base; + ulong sleep; + ulong spllo; + ulong intr; + ulong isave; + ulong arrive; + ulong wakeup; + ulong awake; +}; +static Ictr counters[5/*100*/], *curct; +static int intrwant; +static Rendez vous; +int spltbl; /* set by spllo */ +int intrtbl; /* set by intrvec() */ +int isavetbl; /* set by intrvec() */ + +static int ienable; + + +static void +intrwake(void) +{ + if(ienable == 0) + return; + ienable = 0; + if(spltbl == 0) // not used here + curct->spllo = curct->intr = curct->isave = archrdtsc32(); + else { + curct->spllo = spltbl; + curct->intr = intrtbl; + curct->isave = isavetbl; + } + curct->arrive = archrdtsc32(); + intrwant = 0; + wakeup(&vous); + curct->wakeup = archrdtsc32(); +} + +/* + * sleep calls intrtest with splhi (under lock): + * provoke the interrupt now, so that it is guaranteed + * not to happen until sleep has queued the process, + * forcing wakeup to do something. + */ +static int +intrtest(void*) +{ + ienable = 1; /* enable recording on interrupt */ + curct->sleep = archrdtsc32(); + return intrwant==0; +} + +static long +intrtime(int report, void *va, long n, ulong offset) +{ + Ictr *ic; + long t; + int i; + char *bm; + if(report) + return rep(va, n, offset); + clear(); + + setpri(PriRealtime); + sched(); + curct = counters; + ienable = 0; + addclock0link(intrwake, MS2HZ); + for(i=0; i<nelem(counters); i++){ + curct = &counters[i]; + intrwant = 1; + curct->base = archrdtsc32(); + sleep(&vous, intrtest, nil); + curct->awake = archrdtsc32(); + sched(); /* just to slow it down between trials */ + } + log("interrupt\n"); + for(i=0; i<nelem(counters); i++){ + ic = &counters[i]; + t = ic->awake - ic->base; + bm = ts2str(ic->awake - ic->arrive); + ic->awake -= ic->wakeup; + ic->wakeup -= ic->arrive; + ic->arrive -= ic->isave; + ic->isave -= ic->intr; + ic->intr -= ic->spllo; + ic->spllo -= ic->sleep; + ic->sleep -= ic->base; + log("%ld\t%ld\t%ld\t%ld\t%ld\t%ld\t%ld\t%ld (%s)\n", ic->sleep, ic->spllo, ic->intr, ic->isave, ic->arrive, ic->wakeup, ic->awake, t, bm); + } + setpri(PriNormal); + return n; +} + + +/* DIS operation timing */ + +typedef struct { + vlong n; /* count */ + vlong min; + vlong max; + vlong sum; + vlong sumsq; /* sum of squares */ +} Stat; + +static void +stat(enum { Reset, Inc } op, Stat *c, vlong val) +{ + switch(op) { + case Reset: + c->n = 0; + c->sum = 0; + c->sumsq = 0; + c->min = 0; + c->max = 0; + break; + case Inc: + c->n++; + c->sum += val; + c->sumsq += val*val; + break; + } + if(val < c->min || c->n == 1) + c->min = val; + if(val > c->max || c->n == 1) + c->max = val; +} + +static void +statinc(Stat *d, Stat *s) +{ + d->n += s->n; + d->sum += s->sum; + d->sumsq += s->sumsq; + if(s->min < d->min || d->n == s->n) + d->min = s->min; + if(s->max > d->max || d->n == s->n) + d->max = s->max; +} + +enum +{ + HSIZE = 31, + MAXCOUNT = 100000000L, +}; + +typedef struct { + int op; + int pc; + long count; + Stat t; /* combined dec and execution time */ +} Istat; + +typedef struct Mstat Mstat; +struct Mstat { + char* name; + char* path; + int ninst; + Istat* inst; + Inst* base; + Mstat* hash; + Mstat* link; +}; + +struct +{ + Mstat* hash[HSIZE]; + Mstat* list; +} vmstat; + +extern struct /* see ../../interp/tab.h:/keywds/ */ +{ + char* name; + int op; + int terminal; +}keywds[]; + +static char * +opname(int op) +{ + char *name; + + if(op < 0 || op >= MAXDIS) + return "Unknown"; + return keywds[op].name; + if(name == 0) + name = "<Noname>"; + return name; +} + +static void +mreset(void) +{ + Mstat *mp, *link; + + for(mp=vmstat.list; mp; mp=link) { + link = mp->link; + free(mp->inst); + free(mp); + } + vmstat.list = 0; + memset(vmstat.hash, 0, HSIZE*sizeof(Mstat*)); +} + +static ulong +hash(void *s) +{ + ulong sum = 0; + uchar *a = s; + + while(*a) + sum = (sum << 1) + *a++; + return sum%HSIZE; +} + +static Mstat * +mlookup(Module *mod) +{ + Mstat *m; + ulong h; + + for(m=vmstat.hash[hash(mod->name)]; m; m=m->hash) + if(strcmp(m->name, mod->name) == 0 + && strcmp(m->path, mod->path) == 0) { + return m; + } + + + m = malloc(sizeof(Mstat)); + if(m == 0) + return 0; + kstrdup(&m->name, mod->name); + kstrdup(&m->path, mod->path); + m->ninst = mod->nprog; + m->inst = malloc(m->ninst*sizeof(Istat)); + if(m->path == 0 || m->inst == 0) + return 0; + m->base = mod->prog; + m->link = vmstat.list; + vmstat.list = m; + h = hash(m->name); + m->hash = vmstat.hash[h]; + vmstat.hash[h] = m; + return m; +} + +/* interpreted code Dis timing */ +void +bxec(Prog *p) +{ + int op, pc; + vlong t0, t; + Mstat* ms; + Istat* is; + Module *om; + + R = p->R; + R.MP = R.M->MP; + R.IC = p->quanta; + + if(p->kill != nil) { + char *m; + m = p->kill; + p->kill = nil; + error(m); + } + + if(R.M->compiled) + comvec(); + else { + om = 0; + ms = mlookup(R.M->m); + do { + op = R.PC->op; + pc = R.PC-R.M->prog; + if(om != R.M->m) { + om = R.M->m; + ms = mlookup(R.M->m); + } + + t0 = archrdtsc(); + dec[R.PC->add](); + R.PC++; + optab[op](); + t = archrdtsc(); + if(ms) { + is = &ms->inst[pc]; + if(is->count < MAXCOUNT) { + if(is->count++ == 0) { + is->op = op; + is->pc = pc; + } + stat(Inc, &is->t, t-t0); + } + } + if(op==ISPAWN || op==IMSPAWN) { + Prog *new = delruntail(Pdebug); + new->xec = bxec; + addrun(new); + } + } while(--R.IC != 0); + } + + p->R = R; +} + +/* compiled code Dis timing */ + +static struct { /* compiled code timing */ + int set; + int op, pc; /* Dis opcode and program counter */ + vlong t0, t; /* time-in and time-out */ + vlong base; /* cost of doing the timing */ + Mstat *ms; + Module *om; + int timing; /* between "dis timer start" and stop */ +} C; + +enum { Nop = 0 }; /* opcode value for Dis NOP instruction */ +void +dopostcomp(vlong t) +{ + Istat* is; + + C.t = t; + C.set = 0; + if(C.ms != 0) { + is = &C.ms->inst[C.pc]; + if(C.op == Nop) { /* NOP calibration */ + vlong newbase = C.t - C.t0; + if(C.base == 0 || newbase < C.base) + C.base = newbase; + } + if(is->count < MAXCOUNT) { + if(is->count++ == 0) { + is->op = C.op; + is->pc = C.pc; + } + stat(Inc, &is->t, C.t-C.t0/*-C.base*/); + } + } +} + +void +postcomp(void) +{ + vlong t; + + t = archrdtsc(); + if(C.timing == 0 || C.set == 0) + return; + dopostcomp(t); +} + +void +precomp(void) +{ + vlong t; + + t = archrdtsc(); + if(C.timing == 0) + return; + if(C.set) + dopostcomp(t); + C.pc = *(ulong *)R.m; + C.op = *(ulong *)R.s; + if(C.om != R.M->m) { + C.om = R.M->m; + C.ms = mlookup(R.M->m); + } + C.set = 1; + C.t0 = archrdtsc(); +} + +/* standard deviation */ +static vlong +sdev(Stat *s) +{ + extern double sqrt(double); + vlong var; + var = s->sum; + var *= var/s->n; + var = (s->sumsq - var)/s->n; + return (vlong)sqrt(var); +} + +/* + * Use the sequence: + * 1. "timer startclr" or "timer start", then, + * 2. Any DIS operations, and, + * 3. "timer stop", to stop timing. + * 4. Read the results from the data file after: + * a) "timer report" to get module/pc level results, or + * b) "timer summary" to get opcode level results + */ +static long +distime(int report, void *va, long n, ulong offset) +{ + Prog *p; + Mstat *mp; + Istat *ip, *ep; + + if(report) + return rep(va, n, offset); + clear(); + acquire(); + p = currun(); + if(strncmp(va, "timer startclr", 14) == 0) { + mreset(); + memset(&C, 0, sizeof(C)); + C.timing = 1; + p->xec = bxec; + } else if(strncmp(va, "timer start", 11) == 0) { + p->xec = bxec; + C.timing = 1; + } else if(strncmp(va, "timer stop", 10) == 0) { + p->xec = xec; /* bug: stop all xec threads */ + C.timing = 0; + } else if(strncmp(va, "timer nilop", 11) == 0) { + } else if(strncmp(va, "timer report", 12) == 0) /* by address */ + for(mp=vmstat.list; mp; mp=mp->link) { + ep = mp->inst + mp->ninst; + for(ip=mp->inst; ip<ep; ip++) + if(ip->count > 0) { + char *mean = ts2str(ip->t.sum/ip->count); + char *min = ts2str(ip->t.min); + char *max = ts2str(ip->t.max); + char *std = ts2str(sdev(&ip->t)); + log("%s %d %s %ld %s %s %s %s\n", mp->path, ip->pc, opname(ip->op), ip->count, mean, min, max, std); + } + } + else if(strncmp(va, "timer summary", 13) == 0) { /* by opcode */ + static Stat T[MAXDIS]; + int i; + + for(i=0; i<MAXDIS; i++) + stat(Reset, &T[i], 0); + for(mp=vmstat.list; mp; mp=mp->link) { + ep = mp->inst + mp->ninst; + for(ip=mp->inst; ip<ep; ip++) + if(ip->count > 0) + statinc(&T[ip->op], &ip->t); + } + for(i=0; i<MAXDIS; i++) { + Stat *t = &T[i]; + char *mean = "0.00 ms"; + char *min = "0.00 ms"; + char *max = "0.00 ms"; + char *std = "0.00 ms"; + if(t->n > 0) { + mean = ts2str(t->sum/t->n); + min = ts2str(t->min); + max = ts2str(t->max); + std = ts2str(sdev(t)); + } + log("%d %s %lld %s %s %s %s\n", i, opname(i), t->n, mean, min, max, std); + } + } else + n = 0; + R.IC = 1; + release(); + + return n; +} + +/* + * Garbage collection + */ +static int nidle; + +int +idlegc(void *p) +{ + int done; + Prog *head; + vlong t0, t1, tot; + USED(p); + + head = progn(0); /* isched.head */ + done = gccolor + 3; + tot = 0; + while(gccolor < done && gcruns()) { + if(tready(nil)) + break; + t0 = archrdtsc(); + rungc(head); + t1 = archrdtsc(); + t1 -= t0; + tot += t1; +// log(" %.2f", ts2us(t1)); + } + log(" %.2f", ts2us(tot)); + nidle--; + if(nidle == 0) { + log("\n"); + return 1; + } + return 0; +} + +static long +gctime(int report, void *va, long n, ulong offset) +{ + int i; + vlong t0, t1; + Prog *head; + + if(report) + return rep(va, n, offset); + clear(); + acquire(); + head = progn(0); /* isched.head */ +/* + if(strncmp(va, "idle", 4) == 0) { + nidle = 100; + log("GCIDLE:1l:Observation:n:Time:us"); + atidle(idlegc, 0); + } else if(strncmp(va, "stop", 4) == 0) { + atidledont(idlegc, 0); + } else +*/ + if(strncmp(va, "sched", 5) == 0) { + log("GCSCHED:1l:Observation:n:Time:us"); + for(i=0; i<1000; i++) { + t0 = archrdtsc(); + rungc(head); + t1 = archrdtsc(); + log(" %.2f", ts2us(t1-t0)); + release(); + acquire(); + } + log("\n"); + } else if(strncmp(va, "acquire", 7) == 0) { + log("GCACQUIRE:1l:Observation:n:Time:us"); + for(i=0; i<1000; i++) { + t0 = archrdtsc(); + release(); + acquire(); + head = progn(0); /* isched.head */ + rungc(head); + release(); + acquire(); + t1 = archrdtsc(); + log(" %.2f", ts2us(t1-t0)); + } + log("\n"); + } + + release(); + + return n; +} + + +/* + * Request the number of time stamp ticks per millisecond + */ +static long +ms2ts(int report, void *va, long n, ulong offset) +{ + if(report) + return rep(va, n, offset); + log("%.ld\n", MS2TS); + return n; +} + +/* + * test + */ + +static long +test(int report, void *va, long n, ulong offset) +{ +// vlong v; + double d; + if(report) + return rep(va, n, offset); +// v = 5; +// print("vlong %lld\n", v); +// print("before cast\n"); +// d = (double)v; +// print("after cast\n"); +// print("before assign\n"); + d=100.0; + print("after assign\n"); + print("double %f\n", d); +// log("%lld %f\n", v, d); + return n; +} + +/* + * $Bench builtin support + */ +void +Bench_reset(void *) +{ + bus(1); +} + +void +Bench_microsec(void *fp) +{ + F_Bench_microsec *f; + + f = fp; + *f->ret = bus(0); +} + +void +Bench_disablegc(void *) +{ + gclock(); +} + +void +Bench_enablegc(void *) +{ + gcunlock(); +} + + +#define fdchk(x) ((x) == (Bench_FD*)H ? -1 : (x)->fd) +void +Bench_read(void *fp) +{ + int n; + F_Bench_read *f; + vlong usrelease, uskread, usacquire, ussched; + + f = fp; + n = f->n; + if(f->buf == (Array*)H) { + *f->ret = 0; + return; + } + if(n > f->buf->len) + n = f->buf->len; + + bus(1); + release(); + usrelease = bus(0); + *f->ret = kread(fdchk(f->fd), f->buf->data, n); + uskread = bus(0); + acquire(); + usacquire = bus(0); + sched(); + ussched = bus(0); + log("%lld %lld %lld %lud %lld\n", usrelease, uskread, usacquire, m->ticks, ussched); +} + + +/* + * driver support + */ +long (*Test[])(int report, void *va, long n, ulong offset) = { + [None] notest, + [Calibrate] cal, + [Base] base, + [Op] timeop, + [Intr] intrtime, + [Dis] distime, + [Gc] gctime, + [MS2T] ms2ts, + [xTest] test, +}; + +enum { + Benchdirqid, + Benchdataqid, + Benchctlqid, + Benchusqid, +}; +#define Data 0 +static Dirtab benchtab[]={ + ".", {Benchdirqid,0,QTDIR}, 0, 0555, + "bdata", {Benchdataqid}, 0, 0444, + "bctl", {Benchctlqid}, 0, 0660, + "busec", {Benchusqid}, 0, 0660, +}; + +static void +benchreset(void) +{ + builtinmod("$Bench", Benchmodtab); +} + +static Chan* +benchattach(char *spec) +{ + bench.inuse++; + if(bench.inuse == 1) { + bench.bufsz = 100*READSTR; + bench.buf = xalloc(bench.bufsz); + bench.wpos = bench.buf; + if(bench.buf == 0) + error(Enomem); + bench.test = None; + cal(0, 0, 0, 0); + } + return devattach('x', spec); +} + +void +benchshutdown(void) +{ + bench.inuse--; + if(bench.inuse == 0) + xfree(bench.buf); +} + +static Walkqid* +benchwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, benchtab, nelem(benchtab), devgen); +} + +static Chan* +benchopen(Chan *c, int omode) +{ + if(c->qid.path == Benchdirqid){ + if(omode != OREAD) + error(Eperm); + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static int +benchstat(Chan *c, uchar *dp, int n) +{ + switch((ulong)c->qid.path){ + case Benchdataqid: + benchtab[Data].length = bench.wpos - bench.buf; + } + return devstat(c, dp, n, benchtab, nelem(benchtab), devgen); +} + +static void +benchclose(Chan*) +{ +} + +static long +benchread(Chan *c, void *buf, long n, vlong offset) +{ + vlong us; + char tmp[64]; + + switch((ulong)c->qid.path){ + case Benchdirqid: + return devdirread(c, buf, n, benchtab, nelem(benchtab), devgen); + + case Benchdataqid: + return Test[bench.test](1, buf, n, offset); + + case Benchusqid: + us = archrdtsc(); + us /= US2TS; + snprint(tmp, sizeof(tmp), "%.lld", us); + return readstr(0, buf, n, tmp); + default: + n = 0; + break; + } + return n; +} + +static long +benchwrite(Chan *c, void *buf, long n, vlong offset) +{ + int argn = n; + + switch((ulong)c->qid.path){ + case Benchctlqid: + bench.test = None; + memset((char *)bench.buf, 0, bench.bufsz); + bench.wpos = bench.buf; + if(strncmp(buf, "test", 4) == 0) + bench.test = xTest; + else if(strncmp(buf, "calibrate", 9) == 0) + bench.test = Calibrate; + else if(strncmp(buf, "base", 4) == 0) + bench.test = Base; + else if(strncmp(buf, "intr", 4) == 0) + bench.test = Intr; + else if(strncmp(buf, "op ", 3) == 0) { + bench.test = Op; + buf = (char *)buf + 3; + argn -= 3; + } else if(strncmp(buf, "dis ", 4) == 0) { + bench.test = Dis; + buf = (char *)buf + 4; + argn -= 4; + } else if(strncmp(buf, "gc ", 3) == 0) { + bench.test = Gc; + buf = (char *)buf + 3; + argn -= 3; + } else if(strncmp(buf, "ms2ts", 5) == 0) + bench.test = MS2T; + else + error(Ebadctl); + Test[bench.test](0, buf, argn, offset); + break; + case Benchusqid: + bench.tickstart = archrdtsc(); + break; + default: + error(Ebadusefd); + } + return n; +} + +Dev benchdevtab = { + 'x', + "bench", + + benchreset, + devinit, + benchshutdown, + benchattach, + benchwalk, + benchstat, + benchopen, + devcreate, + benchclose, + benchread, + devbread, + benchwrite, + devbwrite, + devremove, + devwstat, + +}; diff --git a/os/port/devboot.c b/os/port/devboot.c new file mode 100644 index 00000000..66babe38 --- /dev/null +++ b/os/port/devboot.c @@ -0,0 +1,150 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +enum{ + Qdir, + Qboot, + Qmem, + Qkexec, + + Maxkexec = 1536*1024, +}; + +static +Dirtab bootdir[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "boot", {Qboot}, 0, 0220, + "mem", {Qmem}, 0, 0660, + "kexec", {Qkexec}, 0, 0220, +}; + +static Chan* +bootattach(char *spec) +{ + return devattach('B', spec); +} + +static Walkqid* +bootwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, bootdir, nelem(bootdir), devgen); +} + +static int +bootstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, bootdir, nelem(bootdir), devgen); +} + +static Chan* +bootopen(Chan *c, int omode) +{ + if (c->qid.path == Qkexec) { + c->aux = malloc(Maxkexec); + print("kexec buffer: %lux\n", c->aux); + } + return devopen(c, omode, bootdir, nelem(bootdir), devgen); +} + +static void +bootclose(Chan *c) +{ + if(c->qid.path == Qkexec && c->aux != nil){ + print("exec new kernel @%lux\n", (ulong)c->aux); + splhi(); + segflush(c->aux, 64*1024); + gotopc((ulong)c->aux); + } +} + +static long +bootread(Chan *c, void *buf, long n, vlong offset) +{ + switch((ulong)c->qid.path){ + + case Qdir: + return devdirread(c, buf, n, bootdir, nelem(bootdir), devgen); + + case Qmem: + /* kernel memory */ + if(offset>=KZERO && offset<KZERO+conf.npage*BY2PG){ + if(offset+n > KZERO+conf.npage*BY2PG) + n = KZERO+conf.npage*BY2PG - offset; + memmove(buf, (char*)offset, n); + return n; + } + error(Ebadarg); + } + + error(Egreg); + return 0; /* not reached */ +} + +static long +bootwrite(Chan *c, void *buf, long n, vlong offset) +{ + ulong pc; + uchar *p; + + switch((ulong)c->qid.path){ + case Qmem: + /* kernel memory */ + if(offset>=KZERO && offset<KZERO+conf.npage*BY2PG){ + if(offset+n > KZERO+conf.npage*BY2PG) + n = KZERO+conf.npage*BY2PG - offset; + memmove((char*)offset, buf, n); + segflush((void*)offset, n); + return n; + } + error(Ebadarg); + + case Qboot: + p = (uchar*)buf; + pc = (((((p[0]<<8)|p[1])<<8)|p[2])<<8)|p[3]; + if(pc < KZERO || pc >= KZERO+conf.npage*BY2PG) + error(Ebadarg); + splhi(); + segflush((void*)pc, 64*1024); + gotopc(pc); + + case Qkexec: + print("."); + if(c->aux != nil && offset <= Maxkexec){ + if(offset+n > Maxkexec) + n = Maxkexec - offset; + memmove((char*)c->aux+offset, buf, n); + segflush((char*)c->aux+offset, n); + return n; + } + free(c->aux); + c->aux = nil; + error(Ebadarg); + } + error(Ebadarg); + return 0; /* not reached */ +} + +Dev bootdevtab = { + 'B', + "boot", + + devreset, + devinit, + devshutdown, + bootattach, + bootwalk, + bootstat, + bootopen, + devcreate, + bootclose, + bootread, + devbread, + bootwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devbridge.c b/os/port/devbridge.c new file mode 100644 index 00000000..9237728e --- /dev/null +++ b/os/port/devbridge.c @@ -0,0 +1,1206 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/netif.h" +#include "../port/error.h" + +typedef struct Bridge Bridge; +typedef struct Port Port; +typedef struct Centry Centry; +typedef struct Iphdr Iphdr; +typedef struct Tcphdr Tcphdr; + +enum +{ + Qtopdir= 1, /* top level directory */ + + Qbridgedir, /* bridge* directory */ + Qbctl, + Qstats, + Qcache, + Qlog, + + Qportdir, /* directory for a protocol */ + Qpctl, + Qlocal, + Qstatus, + + MaxQ, + + Maxbridge= 4, + Maxport= 128, // power of 2 + CacheHash= 257, // prime + CacheLook= 5, // how many cache entries to examine + CacheSize= (CacheHash+CacheLook-1), + CacheTimeout= 5*60, // timeout for cache entry in seconds + + TcpMssMax = 1300, // max desirable Tcp MSS value + TunnelMtu = 1400, +}; + +static Dirtab bridgedirtab[]={ + "ctl", {Qbctl}, 0, 0666, + "stats", {Qstats}, 0, 0444, + "cache", {Qcache}, 0, 0444, + "log", {Qlog}, 0, 0666, +}; + +static Dirtab portdirtab[]={ + "ctl", {Qpctl}, 0, 0666, + "local", {Qlocal}, 0, 0444, + "status", {Qstatus}, 0, 0444, +}; + +enum { + Logcache= (1<<0), + Logmcast= (1<<1), +}; + +// types of interfaces +enum +{ + Tether, + Ttun, +}; + +static Logflag logflags[] = +{ + { "cache", Logcache, }, + { "multicast", Logmcast, }, + { nil, 0, }, +}; + +static Dirtab *dirtab[MaxQ]; + +#define TYPE(x) (((ulong)(x).path) & 0xff) +#define PORT(x) ((((ulong)(x).path) >> 8)&(Maxport-1)) +#define QID(x, y) (((x)<<8) | (y)) + +struct Centry +{ + uchar d[Eaddrlen]; + int port; + long expire; // entry expires this number of seconds after bootime + long src; + long dst; +}; + +struct Bridge +{ + QLock; + int nport; + Port *port[Maxport]; + Centry cache[CacheSize]; + ulong hit; + ulong miss; + ulong copy; + long delay0; // constant microsecond delay per packet + long delayn; // microsecond delay per byte + int tcpmss; // modify tcpmss value + + Log; +}; + +struct Port +{ + int id; + Bridge *bridge; + int ref; + int closed; + + Chan *data[2]; // channel to data + + int mcast; // send multi cast packets + + Proc *readp; // read proc + + // the following uniquely identifies the port + int type; + char name[KNAMELEN]; + + // owner hash - avoids bind/unbind races + ulong ownhash; + + // various stats + int in; // number of packets read + int inmulti; // multicast or broadcast + int inunknown; // unknown address + int out; // number of packets read + int outmulti; // multicast or broadcast + int outunknown; // unknown address + int outfrag; // fragmented the packet + int nentry; // number of cache entries for this port +}; + +enum { + IP_VER = 0x40, /* Using IP version 4 */ + IP_HLEN = 0x05, /* Header length in characters */ + IP_DF = 0x4000, /* Don't fragment */ + IP_MF = 0x2000, /* More fragments */ + IP_MAX = (32*1024), /* Maximum Internet packet size */ + IP_TCPPROTO = 6, + EOLOPT = 0, + NOOPOPT = 1, + MSSOPT = 2, + MSS_LENGTH = 4, /* Mean segment size */ + SYN = 0x02, /* Pkt. is synchronise */ + IPHDR = 20, /* sizeof(Iphdr) */ +}; + +struct Iphdr +{ + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* ip->identification */ + uchar frag[2]; /* Fragment information */ + uchar ttl; /* Time to live */ + uchar proto; /* Protocol */ + uchar cksum[2]; /* Header checksum */ + uchar src[4]; /* IP source */ + uchar dst[4]; /* IP destination */ +}; + +struct Tcphdr +{ + uchar sport[2]; + uchar dport[2]; + uchar seq[4]; + uchar ack[4]; + uchar flag[2]; + uchar win[2]; + uchar cksum[2]; + uchar urg[2]; +}; + +static Bridge bridgetab[Maxbridge]; + +static int m2p[] = { + [OREAD] 4, + [OWRITE] 2, + [ORDWR] 6 +}; + +static int bridgegen(Chan *c, char*, Dirtab*, int, int s, Dir *dp); +static void portbind(Bridge *b, int argc, char *argv[]); +static void portunbind(Bridge *b, int argc, char *argv[]); +static void etherread(void *a); +static char *cachedump(Bridge *b); +static void portfree(Port *port); +static void cacheflushport(Bridge *b, int port); +static void etherwrite(Port *port, Block *bp); + +extern ulong parseip(uchar*, char*); +extern ushort ipcsum(uchar *addr); + +static void +bridgeinit(void) +{ + int i; + Dirtab *dt; + // setup dirtab with non directory entries + for(i=0; i<nelem(bridgedirtab); i++) { + dt = bridgedirtab + i; + dirtab[TYPE(dt->qid)] = dt; + } + for(i=0; i<nelem(portdirtab); i++) { + dt = portdirtab + i; + dirtab[TYPE(dt->qid)] = dt; + } +} + +static Chan* +bridgeattach(char* spec) +{ + Chan *c; + int dev; + + dev = atoi(spec); + if(dev<0 || dev >= Maxbridge) + error("bad specification"); + + c = devattach('B', spec); + mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR); + c->dev = dev; + + return c; +} + +static Walkqid* +bridgewalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, (Dirtab*)0, 0, bridgegen); +} + +static int +bridgestat(Chan* c, uchar* db, int n) +{ + return devstat(c, db, n, (Dirtab *)0, 0L, bridgegen); +} + +static Chan* +bridgeopen(Chan* c, int omode) +{ + int perm; + Bridge *b; + + omode &= 3; + perm = m2p[omode]; + USED(perm); + + b = bridgetab + c->dev; + USED(b); + + switch(TYPE(c->qid)) { + default: + break; + case Qlog: + logopen(b); + break; + case Qcache: + c->aux = cachedump(b); + break; + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +bridgeclose(Chan* c) +{ + Bridge *b = bridgetab + c->dev; + + switch(TYPE(c->qid)) { + case Qcache: + if(c->flag & COPEN) + free(c->aux); + break; + case Qlog: + if(c->flag & COPEN) + logclose(b); + break; + } +} + +static long +bridgeread(Chan *c, void *a, long n, vlong off) +{ + char buf[256]; + Bridge *b = bridgetab + c->dev; + Port *port; + int i, ingood, outgood; + + USED(off); + switch(TYPE(c->qid)) { + default: + error(Eperm); + case Qtopdir: + case Qbridgedir: + case Qportdir: + return devdirread(c, a, n, 0, 0, bridgegen); + case Qlog: + return logread(b, a, off, n); + case Qstatus: + qlock(b); + port = b->port[PORT(c->qid)]; + if(port == 0) + strcpy(buf, "unbound\n"); + else { + i = 0; + switch(port->type) { + default: panic("bridgeread: unknown port type: %d", port->type); + case Tether: + i += snprint(buf+i, sizeof(buf)-i, "ether %s: ", port->name); + break; + case Ttun: + i += snprint(buf+i, sizeof(buf)-i, "tunnel %s: ", port->name); + break; + } + ingood = port->in-port->inmulti-port->inunknown; + outgood = port->out-port->outmulti-port->outunknown; + i += snprint(buf+i, sizeof(buf)-i, "in=%d(%d:%d:%d) out=%d(%d:%d:%d:%d)\n", + port->in, ingood, port->inmulti, port->inunknown, + port->out, outgood, port->outmulti, port->outunknown, port->outfrag); + USED(i); + } + n = readstr(off, a, n, buf); + qunlock(b); + return n; + case Qbctl: + snprint(buf, sizeof(buf), "%s tcpmss\ndelay %ld %ld\n", b->tcpmss ? "set" : "clear", + b->delay0, b->delayn); + n = readstr(off, a, n, buf); + return n; + case Qcache: + n = readstr(off, a, n, c->aux); + return n; + case Qstats: + snprint(buf, sizeof(buf), "hit=%uld miss=%uld copy=%uld\n", + b->hit, b->miss, b->copy); + n = readstr(off, a, n, buf); + return n; + } +} + +static void +bridgeoption(Bridge *b, char *option, int value) +{ + if(strcmp(option, "tcpmss") == 0) + b->tcpmss = value; + else + error("unknown bridge option"); +} + + +static long +bridgewrite(Chan *c, void *a, long n, vlong off) +{ + Bridge *b = bridgetab + c->dev; + Cmdbuf *cb; + char *arg0; + char *p; + + USED(off); + switch(TYPE(c->qid)) { + default: + error(Eperm); + case Qbctl: + cb = parsecmd(a, n); + qlock(b); + if(waserror()) { + qunlock(b); + free(cb); + nexterror(); + } + if(cb->nf == 0) + error("short write"); + arg0 = cb->f[0]; + if(strcmp(arg0, "bind") == 0) { + portbind(b, cb->nf-1, cb->f+1); + } else if(strcmp(arg0, "unbind") == 0) { + portunbind(b, cb->nf-1, cb->f+1); + } else if(strcmp(arg0, "cacheflush") == 0) { + logb(b, Logcache, "cache flush\n"); + memset(b->cache, 0, CacheSize*sizeof(Centry)); + } else if(strcmp(arg0, "set") == 0) { + if(cb->nf != 2) + error("usage: set option"); + bridgeoption(b, cb->f[1], 1); + } else if(strcmp(arg0, "clear") == 0) { + if(cb->nf != 2) + error("usage: clear option"); + bridgeoption(b, cb->f[1], 0); + } else if(strcmp(arg0, "delay") == 0) { + if(cb->nf != 3) + error("usage: delay delay0 delayn"); + b->delay0 = strtol(cb->f[1], nil, 10); + b->delayn = strtol(cb->f[2], nil, 10); + } else + error("unknown control request"); + poperror(); + qunlock(b); + free(cb); + return n; + case Qlog: + cb = parsecmd(a, n); + p = logctl(b, cb->nf, cb->f, logflags); + free(cb); + if(p != nil) + error(p); + return n; + } +} + +static int +bridgegen(Chan *c, char *, Dirtab*, int, int s, Dir *dp) +{ + Bridge *b = bridgetab + c->dev; + int type = TYPE(c->qid); + Dirtab *dt; + Qid qid; + + if(s == DEVDOTDOT){ + switch(TYPE(c->qid)){ + case Qtopdir: + case Qbridgedir: + snprint(up->genbuf, sizeof(up->genbuf), "#B%ld", c->dev); + mkqid(&qid, Qtopdir, 0, QTDIR); + devdir(c, qid, up->genbuf, 0, eve, 0555, dp); + break; + case Qportdir: + snprint(up->genbuf, sizeof(up->genbuf), "bridge%ld", c->dev); + mkqid(&qid, Qbridgedir, 0, QTDIR); + devdir(c, qid, up->genbuf, 0, eve, 0555, dp); + break; + default: + panic("bridgewalk %llux", c->qid.path); + } + return 1; + } + + switch(type) { + default: + // non directory entries end up here + if(c->qid.type & QTDIR) + panic("bridgegen: unexpected directory"); + if(s != 0) + return -1; + dt = dirtab[TYPE(c->qid)]; + if(dt == nil) + panic("bridgegen: unknown type: %lud", TYPE(c->qid)); + devdir(c, c->qid, dt->name, dt->length, eve, dt->perm, dp); + return 1; + case Qtopdir: + if(s != 0) + return -1; + snprint(up->genbuf, sizeof(up->genbuf), "bridge%ld", c->dev); + mkqid(&qid, QID(0, Qbridgedir), 0, QTDIR); + devdir(c, qid, up->genbuf, 0, eve, 0555, dp); + return 1; + case Qbridgedir: + if(s<nelem(bridgedirtab)) { + dt = bridgedirtab+s; + devdir(c, dt->qid, dt->name, dt->length, eve, dt->perm, dp); + return 1; + } + s -= nelem(bridgedirtab); + if(s >= b->nport) + return -1; + mkqid(&qid, QID(s, Qportdir), 0, QTDIR); + snprint(up->genbuf, sizeof(up->genbuf), "%d", s); + devdir(c, qid, up->genbuf, 0, eve, 0555, dp); + return 1; + case Qportdir: + if(s>=nelem(portdirtab)) + return -1; + dt = portdirtab+s; + mkqid(&qid, QID(PORT(c->qid),TYPE(dt->qid)), 0, QTFILE); + devdir(c, qid, dt->name, dt->length, eve, dt->perm, dp); + return 1; + } +} + +// also in netif.c +static int +parseaddr(uchar *to, char *from, int alen) +{ + char nip[4]; + char *p; + int i; + + p = from; + for(i = 0; i < alen; i++){ + if(*p == 0) + return -1; + nip[0] = *p++; + if(*p == 0) + return -1; + nip[1] = *p++; + nip[2] = 0; + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return 0; +} + +// assumes b is locked +static void +portbind(Bridge *b, int argc, char *argv[]) +{ + Port *port; + char path[8*KNAMELEN]; + char buf[100]; + char *dev, *dev2=nil, *p; + Chan *ctl; + int type=0, i, n; + char *usage = "usage: bind ether|tunnel name ownhash dev [dev2]"; + char name[KNAMELEN]; + ulong ownhash; + + memset(name, 0, KNAMELEN); + if(argc < 4) + error(usage); + if(strcmp(argv[0], "ether") == 0) { + if(argc != 4) + error(usage); + type = Tether; + strncpy(name, argv[1], KNAMELEN); + name[KNAMELEN-1] = 0; +// parseaddr(addr, argv[1], Eaddrlen); + } else if(strcmp(argv[0], "tunnel") == 0) { + if(argc != 5) + error(usage); + type = Ttun; + strncpy(name, argv[1], KNAMELEN); + name[KNAMELEN-1] = 0; +// parseip(addr, argv[1]); + dev2 = argv[4]; + } else + error(usage); + ownhash = atoi(argv[2]); + dev = argv[3]; + for(i=0; i<b->nport; i++) { + port = b->port[i]; + if(port != nil) + if(port->type == type) + if(memcmp(port->name, name, KNAMELEN) == 0) + error("port in use"); + } + for(i=0; i<Maxport; i++) + if(b->port[i] == nil) + break; + if(i == Maxport) + error("no more ports"); + port = smalloc(sizeof(Port)); + port->ref = 1; + port->id = i; + port->ownhash = ownhash; + + if(waserror()) { + portfree(port); + nexterror(); + } + port->type = type; + memmove(port->name, name, KNAMELEN); + switch(port->type) { + default: panic("portbind: unknown port type: %d", type); + case Tether: + snprint(path, sizeof(path), "%s/clone", dev); + ctl = namec(path, Aopen, ORDWR, 0); + if(waserror()) { + cclose(ctl); + nexterror(); + } + // check addr? + + // get directory name + n = devtab[ctl->type]->read(ctl, buf, sizeof(buf), 0); + buf[n] = 0; + for(p = buf; *p == ' '; p++) + ; + snprint(path, sizeof(path), "%s/%lud/data", dev, strtoul(p, 0, 0)); + + // setup connection to be promiscuous + snprint(buf, sizeof(buf), "connect -1"); + devtab[ctl->type]->write(ctl, buf, strlen(buf), 0); + snprint(buf, sizeof(buf), "promiscuous"); + devtab[ctl->type]->write(ctl, buf, strlen(buf), 0); + snprint(buf, sizeof(buf), "bridge"); + devtab[ctl->type]->write(ctl, buf, strlen(buf), 0); + + // open data port + port->data[0] = namec(path, Aopen, ORDWR, 0); + // dup it + incref(port->data[0]); + port->data[1] = port->data[0]; + + poperror(); + cclose(ctl); + + break; + case Ttun: + port->data[0] = namec(dev, Aopen, OREAD, 0); + port->data[1] = namec(dev2, Aopen, OWRITE, 0); + break; + } + + poperror(); + + // commited to binding port + b->port[port->id] = port; + port->bridge = b; + if(b->nport <= port->id) + b->nport = port->id+1; + + // assumes kproc always succeeds + kproc("etherread", etherread, port, 0); // poperror must be next + port->ref++; +} + +// assumes b is locked +static void +portunbind(Bridge *b, int argc, char *argv[]) +{ + Port *port=nil; + int type=0, i; + char *usage = "usage: unbind ether|tunnel addr [ownhash]"; + char name[KNAMELEN]; + ulong ownhash; + + memset(name, 0, KNAMELEN); + if(argc < 2 || argc > 3) + error(usage); + if(strcmp(argv[0], "ether") == 0) { + type = Tether; + strncpy(name, argv[1], KNAMELEN); + name[KNAMELEN-1] = 0; +// parseaddr(addr, argv[1], Eaddrlen); + } else if(strcmp(argv[0], "tunnel") == 0) { + type = Ttun; + strncpy(name, argv[1], KNAMELEN); + name[KNAMELEN-1] = 0; +// parseip(addr, argv[1]); + } else + error(usage); + if(argc == 3) + ownhash = atoi(argv[2]); + else + ownhash = 0; + for(i=0; i<b->nport; i++) { + port = b->port[i]; + if(port != nil) + if(port->type == type) + if(memcmp(port->name, name, KNAMELEN) == 0) + break; + } + if(i == b->nport) + error("port not found"); + if(ownhash != 0 && port->ownhash != 0 && ownhash != port->ownhash) + error("bad owner hash"); + + port->closed = 1; + b->port[i] = nil; // port is now unbound + cacheflushport(b, i); + + // try and stop reader + if(port->readp) + postnote(port->readp, 1, "unbind", 0); + portfree(port); +} + +// assumes b is locked +static Centry * +cachelookup(Bridge *b, uchar d[Eaddrlen]) +{ + int i; + uint h; + Centry *p; + long sec; + + // dont cache multicast or broadcast + if(d[0] & 1) + return 0; + + h = 0; + for(i=0; i<Eaddrlen; i++) { + h *= 7; + h += d[i]; + } + h %= CacheHash; + p = b->cache + h; + sec = TK2SEC(m->ticks); + for(i=0; i<CacheLook; i++,p++) { + if(memcmp(d, p->d, Eaddrlen) == 0) { + p->dst++; + if(sec >= p->expire) { + logb(b, Logcache, "expired cache entry: %E %d\n", + d, p->port); + return nil; + } + p->expire = sec + CacheTimeout; + return p; + } + } + logb(b, Logcache, "cache miss: %E\n", d); + return nil; +} + +// assumes b is locked +static void +cacheupdate(Bridge *b, uchar d[Eaddrlen], int port) +{ + int i; + uint h; + Centry *p, *pp; + long sec; + + // dont cache multicast or broadcast + if(d[0] & 1) { + logb(b, Logcache, "bad source address: %E\n", d); + return; + } + + h = 0; + for(i=0; i<Eaddrlen; i++) { + h *= 7; + h += d[i]; + } + h %= CacheHash; + p = b->cache + h; + pp = p; + sec = p->expire; + + // look for oldest entry + for(i=0; i<CacheLook; i++,p++) { + if(memcmp(p->d, d, Eaddrlen) == 0) { + p->expire = TK2SEC(m->ticks) + CacheTimeout; + if(p->port != port) { + logb(b, Logcache, "NIC changed port %d->%d: %E\n", + p->port, port, d); + p->port = port; + } + p->src++; + return; + } + if(p->expire < sec) { + sec = p->expire; + pp = p; + } + } + if(pp->expire != 0) + logb(b, Logcache, "bumping from cache: %E %d\n", pp->d, pp->port); + pp->expire = TK2SEC(m->ticks) + CacheTimeout; + memmove(pp->d, d, Eaddrlen); + pp->port = port; + pp->src = 1; + pp->dst = 0; + logb(b, Logcache, "adding to cache: %E %d\n", pp->d, pp->port); +} + +// assumes b is locked +static void +cacheflushport(Bridge *b, int port) +{ + Centry *ce; + int i; + + ce = b->cache; + for(i=0; i<CacheSize; i++,ce++) { + if(ce->port != port) + continue; + memset(ce, 0, sizeof(Centry)); + } +} + +static char * +cachedump(Bridge *b) +{ + int i, n; + long sec, off; + char *buf, *p, *ep; + Centry *ce; + char c; + + qlock(b); + if(waserror()) { + qunlock(b); + nexterror(); + } + sec = TK2SEC(m->ticks); + n = 0; + for(i=0; i<CacheSize; i++) + if(b->cache[i].expire != 0) + n++; + + n *= 51; // change if print format is changed + n += 10; // some slop at the end + buf = malloc(n); + p = buf; + ep = buf + n; + ce = b->cache; + off = seconds() - sec; + for(i=0; i<CacheSize; i++,ce++) { + if(ce->expire == 0) + continue; + c = (sec < ce->expire)?'v':'e'; + p += snprint(p, ep-p, "%E %2d %10ld %10ld %10ld %c\n", ce->d, + ce->port, ce->src, ce->dst, ce->expire+off, c); + } + *p = 0; + poperror(); + qunlock(b); + + return buf; +} + + + +// assumes b is locked +static void +ethermultiwrite(Bridge *b, Block *bp, Port *port) +{ + Port *oport; + Block *bp2; + Etherpkt *ep; + int i, mcast, bcast; + static uchar bcastaddr[Eaddrlen] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + + if(waserror()) { + if(bp) + freeb(bp); + nexterror(); + } + + ep = (Etherpkt*)bp->rp; + mcast = ep->d[0] & 1; + if(mcast) + bcast = memcmp(ep->d, bcastaddr, Eaddrlen) == 0; + else + bcast = 0; + + oport = nil; + for(i=0; i<b->nport; i++) { + if(i == port->id || b->port[i] == nil) + continue; + if(mcast && !bcast && !b->port[i]->mcast) + continue; + if(mcast) + b->port[i]->outmulti++; + else + b->port[i]->outunknown++; + + // delay one so that the last write does not copy + if(oport != nil) { + b->copy++; + bp2 = copyblock(bp, blocklen(bp)); + if(!waserror()) { + etherwrite(oport, bp2); + poperror(); + } + } + oport = b->port[i]; + } + + // last write free block + if(oport) { + bp2 = bp; bp = nil; USED(bp); + if(!waserror()) { + etherwrite(oport, bp2); + poperror(); + } + } else + freeb(bp); + + poperror(); +} + +static void +tcpmsshack(Etherpkt *epkt, int n) +{ + int hl; + Iphdr *iphdr; + Tcphdr *tcphdr; + ulong mss; + ulong cksum; + int optlen; + uchar *optr; + + // check it is an ip packet + if(nhgets(epkt->type) != 0x800) + return; + iphdr = (Iphdr*)(epkt->data); + n -= ETHERHDRSIZE; + if(n < IPHDR) + return; + + // check it is ok IP packet + if(iphdr->vihl != (IP_VER|IP_HLEN)) { + hl = (iphdr->vihl&0xF)<<2; + if((iphdr->vihl&0xF0) != IP_VER || hl < (IP_HLEN<<2)) + return; + } else + hl = IP_HLEN<<2; + + // check TCP + if(iphdr->proto != IP_TCPPROTO) + return; + n -= hl; + if(n < sizeof(Tcphdr)) + return; + tcphdr = (Tcphdr*)((uchar*)(iphdr) + hl); + // MSS can only appear in SYN packet + if(!(tcphdr->flag[1] & SYN)) + return; + hl = (tcphdr->flag[0] & 0xf0)>>2; + if(n < hl) + return; + + // check for MSS option + optr = (uchar*)(tcphdr) + sizeof(Tcphdr); + n = hl - sizeof(Tcphdr); + for(;;) { + if(n <= 0 || *optr == EOLOPT) + return; + if(*optr == NOOPOPT) { + n--; + optr++; + continue; + } + optlen = optr[1]; + if(optlen < 2 || optlen > n) + return; + if(*optr == MSSOPT && optlen == MSS_LENGTH) + break; + n -= optlen; + optr += optlen; + } + + mss = nhgets(optr+2); + if(mss <= TcpMssMax) + return; + // fit checksum + cksum = nhgets(tcphdr->cksum); + if(optr-(uchar*)tcphdr & 1) { +print("tcpmsshack: odd alignment!\n"); + // odd alignments are a pain + cksum += nhgets(optr+1); + cksum -= (optr[1]<<8)|(TcpMssMax>>8); + cksum += (cksum>>16); + cksum &= 0xffff; + cksum += nhgets(optr+3); + cksum -= ((TcpMssMax&0xff)<<8)|optr[4]; + cksum += (cksum>>16); + } else { + cksum += mss; + cksum -= TcpMssMax; + cksum += (cksum>>16); + } + hnputs(tcphdr->cksum, cksum); + hnputs(optr+2, TcpMssMax); +} + +/* + * process to read from the ethernet + */ +static void +etherread(void *a) +{ + Port *port = a; + Bridge *b = port->bridge; + Block *bp, *bp2; + Etherpkt *ep; + Centry *ce; + long md; + + qlock(b); + port->readp = up; /* hide identity under a rock for unbind */ + + while(!port->closed){ + // release lock to read - error means it is time to quit + qunlock(b); + if(waserror()) { +print("etherread read error: %s\n", up->env->errstr); + qlock(b); + break; + } +if(0)print("devbridge: etherread: reading\n"); + bp = devtab[port->data[0]->type]->bread(port->data[0], ETHERMAXTU, 0); +if(0)print("devbridge: etherread: blocklen = %d\n", blocklen(bp)); + poperror(); + qlock(b); + if(bp == nil || port->closed) + break; + if(waserror()) { +//print("etherread bridge error\n"); + if(bp) + freeb(bp); + continue; + } + if(blocklen(bp) < ETHERMINTU) + error("short packet"); + port->in++; + + ep = (Etherpkt*)bp->rp; + cacheupdate(b, ep->s, port->id); + if(b->tcpmss) + tcpmsshack(ep, BLEN(bp)); + + /* + * delay packets to simulate a slow link + */ + if(b->delay0 || b->delayn){ + md = b->delay0 + b->delayn * BLEN(bp); + if(md > 0) + microdelay(md); + } + + if(ep->d[0] & 1) { + logb(b, Logmcast, "multicast: port=%d src=%E dst=%E type=%#.4ux\n", + port->id, ep->s, ep->d, (ep->type[0]<<8)|ep->type[1] ); + port->inmulti++; + bp2 = bp; bp = nil; + ethermultiwrite(b, bp2, port); + } else { + ce = cachelookup(b, ep->d); + if(ce == nil) { + b->miss++; + port->inunknown++; + bp2 = bp; bp = nil; + ethermultiwrite(b, bp2, port); + }else if(ce->port != port->id){ + b->hit++; + bp2 = bp; bp = nil; + etherwrite(b->port[ce->port], bp2); + } + } + + poperror(); + if(bp) + freeb(bp); + } +//print("etherread: trying to exit\n"); + port->readp = nil; + portfree(port); + qunlock(b); + pexit("hangup", 1); +} + +static int +fragment(Etherpkt *epkt, int n) +{ + Iphdr *iphdr; + + if(n <= TunnelMtu) + return 0; + + // check it is an ip packet + if(nhgets(epkt->type) != 0x800) + return 0; + iphdr = (Iphdr*)(epkt->data); + n -= ETHERHDRSIZE; + if(n < IPHDR) + return 0; + + // check it is ok IP packet - I don't handle IP options for the momment + if(iphdr->vihl != (IP_VER|IP_HLEN)) + return 0; + + // check for don't fragment + if(iphdr->frag[0] & (IP_DF>>8)) + return 0; + + // check for short block + if(nhgets(iphdr->length) > n) + return 0; + + return 1; +} + + +static void +etherwrite(Port *port, Block *bp) +{ + Iphdr *eh, *feh; + Etherpkt *epkt; + int n, lid, len, seglen, chunk, dlen, blklen, offset, mf; + Block *xp, *nb; + ushort fragoff, frag; + + port->out++; + epkt = (Etherpkt*)bp->rp; + n = blocklen(bp); + if(port->type != Ttun || !fragment(epkt, n)) { + devtab[port->data[1]->type]->bwrite(port->data[1], bp, 0); + return; + } + port->outfrag++; + if(waserror()){ + freeblist(bp); + nexterror(); + } + + seglen = (TunnelMtu - ETHERHDRSIZE - IPHDR) & ~7; + eh = (Iphdr*)(epkt->data); + len = nhgets(eh->length); + frag = nhgets(eh->frag); + mf = frag & IP_MF; + frag <<= 3; + dlen = len - IPHDR; + xp = bp; + lid = nhgets(eh->id); + offset = ETHERHDRSIZE+IPHDR; + while(xp != nil && offset && offset >= BLEN(xp)) { + offset -= BLEN(xp); + xp = xp->next; + } + xp->rp += offset; + +if(0) print("seglen=%d, dlen=%d, mf=%x, frag=%d\n", seglen, dlen, mf, frag); + for(fragoff = 0; fragoff < dlen; fragoff += seglen) { + nb = allocb(ETHERHDRSIZE+IPHDR+seglen); + + feh = (Iphdr*)(nb->wp+ETHERHDRSIZE); + + memmove(nb->wp, epkt, ETHERHDRSIZE+IPHDR); + nb->wp += ETHERHDRSIZE+IPHDR; + + if((fragoff + seglen) >= dlen) { + seglen = dlen - fragoff; + hnputs(feh->frag, (frag+fragoff)>>3 | mf); + } + else + hnputs(feh->frag, (frag+fragoff>>3) | IP_MF); + + hnputs(feh->length, seglen + IPHDR); + hnputs(feh->id, lid); + + /* Copy up the data area */ + chunk = seglen; + while(chunk) { + blklen = chunk; + if(BLEN(xp) < chunk) + blklen = BLEN(xp); + memmove(nb->wp, xp->rp, blklen); + nb->wp += blklen; + xp->rp += blklen; + chunk -= blklen; + if(xp->rp == xp->wp) + xp = xp->next; + } + + feh->cksum[0] = 0; + feh->cksum[1] = 0; + hnputs(feh->cksum, ipcsum(&feh->vihl)); + + // don't generate small packets + if(BLEN(nb) < ETHERMINTU) + nb->wp = nb->rp + ETHERMINTU; + devtab[port->data[1]->type]->bwrite(port->data[1], nb, 0); + } + poperror(); + freeblist(bp); +} + +// hold b lock +static void +portfree(Port *port) +{ + port->ref--; + if(port->ref < 0) + panic("portfree: bad ref"); + if(port->ref > 0) + return; + + if(port->data[0]) + cclose(port->data[0]); + if(port->data[1]) + cclose(port->data[1]); + memset(port, 0, sizeof(Port)); + free(port); +} + +Dev bridgedevtab = { + 'B', + "bridge", + + devreset, + bridgeinit, + devshutdown, + bridgeattach, + bridgewalk, + bridgestat, + bridgeopen, + devcreate, + bridgeclose, + bridgeread, + devbread, + bridgewrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devcap.c b/os/port/devcap.c new file mode 100644 index 00000000..64232c57 --- /dev/null +++ b/os/port/devcap.c @@ -0,0 +1,248 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "mp.h" +#include "libsec.h" + +/* + * Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved. + */ + +enum { + Captimeout = 15, /* seconds until expiry */ + Capidletime = 60 /* idle seconds before capwatch exits */ +}; + +typedef struct Caps Caps; +struct Caps +{ + uchar hash[SHA1dlen]; + ulong time; + Caps* next; +}; + +struct { + QLock l; + Caps* caps; + int kpstarted; +} allcaps; + +enum { + Qdir, + Qhash, + Quse +}; + +static Dirtab capdir[] = +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "capuse", {Quse, 0}, 0, 0222, + "caphash", {Qhash, 0}, 0, 0200, +}; + +static int ncapdir = nelem(capdir); + +static void +capwatch(void *a) +{ + Caps *c, **l; + int idletime; + + USED(a); + idletime = 0; + for(;;){ + tsleep(&up->sleep, return0, nil, 30*1000); + qlock(&allcaps.l); + for(l = &allcaps.caps; (c = *l) != nil;) + if(++c->time > Captimeout){ + *l = c->next; + free(c); + }else + l = &c->next; + if(allcaps.caps == nil){ + if(++idletime > Capidletime){ + allcaps.kpstarted = 0; + qunlock(&allcaps.l); + pexit("", 0); + } + }else + idletime = 0; + qunlock(&allcaps.l); + } +} + +static Chan * +capattach(char *spec) +{ + return devattach(L'¤', spec); +} + +static Walkqid* +capwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, capdir, nelem(capdir), devgen); +} + +static int +capstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, capdir, nelem(capdir), devgen); +} + +static Chan* +capopen(Chan *c, int omode) +{ + if(c->qid.type & QTDIR) { + if(omode != OREAD) + error(Eisdir); + c->mode = omode; + c->flag |= COPEN; + c->offset = 0; + return c; + } + + if(c->qid.path == Qhash && !iseve()) + error(Eperm); + + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +capclose(Chan *c) +{ + USED(c); +} + +static long +capread(Chan *c, void *va, long n, vlong vl) +{ + USED(vl); + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, va, n, capdir, ncapdir, devgen); + + default: + error(Eperm); + break; + } + return n; +} + +static int +capwritehash(uchar *a, int l) +{ + Caps *c; + + if(l != SHA1dlen) + return -1; + c = malloc(sizeof(*c)); + if(c == nil) + return -1; + memmove(c->hash, a, l); + c->time = 0; + qlock(&allcaps.l); + c->next = allcaps.caps; + allcaps.caps = c; + if(!allcaps.kpstarted){ + allcaps.kpstarted = 1; + kproc("capwatch", capwatch, 0, 0); + } + qunlock(&allcaps.l); + return 0; +} + +static int +capwriteuse(uchar *a, int len) +{ + int n; + uchar digest[SHA1dlen]; + char buf[128], *p, *users[3]; + Caps *c, **l; + + if(len >= sizeof(buf)-1) + return -1; + memmove(buf, a, len); + buf[len] = 0; + p = strrchr(buf, '@'); + if(p == nil) + return -1; + *p++ = 0; + len = strlen(p); + n = strlen(buf); + if(len == 0 || n == 0) + return -1; + hmac_sha1((uchar*)buf, n, (uchar*)p, len, digest, nil); + n = getfields(buf, users, nelem(users), 0, "@"); + if(n == 1) + users[1] = users[0]; + else if(n != 2) + return -1; + if(*users[0] == 0 || *users[1] == 0) + return -1; + qlock(&allcaps.l); + for(l = &allcaps.caps; (c = *l) != nil; l = &c->next) + if(memcmp(c->hash, digest, sizeof(c->hash)) == 0){ + *l = c->next; + qunlock(&allcaps.l); + free(c); + if(n == 2 && strcmp(up->env->user, users[0]) != 0) + return -1; + kstrdup(&up->env->user, users[1]); + return 0; + } + qunlock(&allcaps.l); + return -1; +} + +static long +capwrite(Chan* c, void* buf, long n, vlong offset) +{ + USED(offset); + switch((ulong)c->qid.path){ + case Qhash: + if(capwritehash(buf, n) < 0) + error(Ebadarg); + return n; + case Quse: + if(capwriteuse(buf, n) < 0) + error("invalid capability"); + return n; + } + error(Ebadarg); + return 0; +} + +static void +capremove(Chan *c) +{ + if(c->qid.path != Qhash || !iseve()) + error(Eperm); + ncapdir = nelem(capdir)-1; +} + +Dev capdevtab = { + L'¤', + "cap", + + devreset, + devinit, + devshutdown, + capattach, + capwalk, + capstat, + capopen, + devcreate, + capclose, + capread, + devbread, + capwrite, + devbwrite, + capremove, + devwstat +}; diff --git a/os/port/devcons.c b/os/port/devcons.c new file mode 100644 index 00000000..7460cffc --- /dev/null +++ b/os/port/devcons.c @@ -0,0 +1,1274 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include <version.h> +#include "mp.h" +#include "libsec.h" +#include "keyboard.h" + +extern int cflag; +extern int keepbroken; + +void (*serwrite)(char *, int); + +Queue* kscanq; /* keyboard raw scancodes (when needed) */ +char* kscanid; /* name of raw scan format (if defined) */ +Queue* kbdq; /* unprocessed console input */ +Queue* lineq; /* processed console input */ +Queue* printq; /* console output */ +Queue* klogq; /* kernel print (log) output */ +int iprintscreenputs; + +static struct +{ + RWlock; + Queue* q; +} kprintq; + +static struct +{ + QLock; + + int raw; /* true if we shouldn't process input */ + int ctl; /* number of opens to the control file */ + int kbdr; /* number of open reads to the keyboard */ + int scan; /* true if reading raw scancodes */ + int x; /* index into line */ + char line[1024]; /* current input line */ + + char c; + int count; + int repeat; +} kbd; + +char* sysname; +char* eve; + +enum +{ + CMreboot, + CMhalt, + CMpanic, + CMbroken, + CMnobroken, + CMconsole, +}; + +static Cmdtab sysctlcmd[] = +{ + CMreboot, "reboot", 0, + CMhalt, "halt", 0, + CMpanic, "panic", 0, + CMconsole, "console", 1, + CMbroken, "broken", 0, + CMnobroken, "nobroken", 0, +}; + +void +printinit(void) +{ + lineq = qopen(2*1024, 0, nil, nil); + if(lineq == nil) + panic("printinit"); + qnoblock(lineq, 1); +} + +/* + * return true if current user is eve + */ +int +iseve(void) +{ + Osenv *o; + + o = up->env; + return strcmp(eve, o->user) == 0; +} + +static int +consactive(void) +{ + if(printq) + return qlen(printq) > 0; + return 0; +} + +static void +prflush(void) +{ + ulong now; + + now = m->ticks; + while(serwrite==nil && consactive()) + if(m->ticks - now >= HZ) + break; +} + +/* + * Print a string on the console. Convert \n to \r\n for serial + * line consoles. Locking of the queues is left up to the screen + * or uart code. Multi-line messages to serial consoles may get + * interspersed with other messages. + */ +static void +putstrn0(char *str, int n, int usewrite) +{ + int m; + char *t; + char buf[PRINTSIZE+2]; + + /* + * if kprint is open, put the message there, otherwise + * if there's an attached bit mapped display, + * put the message there. + */ + m = consoleprint; + if(canrlock(&kprintq)){ + if(kprintq.q != nil){ + if(waserror()){ + runlock(&kprintq); + nexterror(); + } + if(usewrite) + qwrite(kprintq.q, str, n); + else + qiwrite(kprintq.q, str, n); + poperror(); + m = 0; + } + runlock(&kprintq); + } + if(m && screenputs != nil) + screenputs(str, n); + + /* + * if there's a serial line being used as a console, + * put the message there. + */ + if(serwrite != nil) { + serwrite(str, n); + return; + } + + if(printq == 0) + return; + + while(n > 0) { + t = memchr(str, '\n', n); + if(t && !kbd.raw) { + m = t - str; + if(m > sizeof(buf)-2) + m = sizeof(buf)-2; + memmove(buf, str, m); + buf[m] = '\r'; + buf[m+1] = '\n'; + if(usewrite) + qwrite(printq, buf, m+2); + else + qiwrite(printq, buf, m+2); + str = t + 1; + n -= m + 1; + } else { + if(usewrite) + qwrite(printq, str, n); + else + qiwrite(printq, str, n); + break; + } + } +} + +void +putstrn(char *str, int n) +{ + putstrn0(str, n, 0); +} + +int +snprint(char *s, int n, char *fmt, ...) +{ + va_list arg; + + va_start(arg, fmt); + n = vseprint(s, s+n, fmt, arg) - s; + va_end(arg); + + return n; +} + +int +sprint(char *s, char *fmt, ...) +{ + int n; + va_list arg; + + va_start(arg, fmt); + n = vseprint(s, s+PRINTSIZE, fmt, arg) - s; + va_end(arg); + + return n; +} + +int +print(char *fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + putstrn(buf, n); + + return n; +} + +int +fprint(int fd, char *fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + USED(fd); + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + putstrn(buf, n); + + return n; +} + +int +kprint(char *fmt, ...) +{ + va_list arg; + char buf[PRINTSIZE]; + int n; + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + if(qfull(klogq)) + qflush(klogq); + return qproduce(klogq, buf, n); +} + +int +iprint(char *fmt, ...) +{ + int n, s; + va_list arg; + char buf[PRINTSIZE]; + + s = splhi(); + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + if(screenputs != nil && iprintscreenputs) + screenputs(buf, n); + uartputs(buf, n); + splx(s); + + return n; +} + +void +panic(char *fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + setpanic(); + kprintq.q = nil; + strcpy(buf, "panic: "); + va_start(arg, fmt); + n = vseprint(buf+strlen(buf), buf+sizeof(buf)-1, fmt, arg) - buf; + va_end(arg); + buf[n] = '\n'; + putstrn(buf, n+1); + spllo(); + dumpstack(); + + exit(1); +} + +void +_assert(char *fmt) +{ + panic("assert failed: %s", fmt); +} + +void +sysfatal(char *fmt, ...) +{ + va_list arg; + char buf[64]; + + va_start(arg, fmt); + vsnprint(buf, sizeof(buf), fmt, arg); + va_end(arg); + panic("sysfatal: %s", buf); +} + +int +pprint(char *fmt, ...) +{ + int n; + Chan *c; + Osenv *o; + va_list arg; + char buf[2*PRINTSIZE]; + + n = sprint(buf, "%s %ld: ", up->text, up->pid); + va_start(arg, fmt); + n = vseprint(buf+n, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + + o = up->env; + if(o->fgrp == 0) { + print("%s", buf); + return 0; + } + c = o->fgrp->fd[2]; + if(c==0 || (c->mode!=OWRITE && c->mode!=ORDWR)) { + print("%s", buf); + return 0; + } + + if(waserror()) { + print("%s", buf); + return 0; + } + devtab[c->type]->write(c, buf, n, c->offset); + poperror(); + + lock(c); + c->offset += n; + unlock(c); + + return n; +} + +void +echo(Rune r, char *buf, int n) +{ + if(kbd.raw) + return; + + if(r == '\n'){ + if(printq) + qiwrite(printq, "\r", 1); + } else if(r == 0x15){ + buf = "^U\n"; + n = 3; + } + if(consoleprint && screenputs != nil) + screenputs(buf, n); + if(printq) + qiwrite(printq, buf, n); +} + +/* + * Debug key support. Allows other parts of the kernel to register debug + * key handlers, instead of devcons.c having to know whatever's out there. + * A kproc is used to invoke most handlers, rather than tying up the CPU at + * splhi, which can choke some device drivers (eg softmodem). + */ +typedef struct { + Rune r; + char *m; + void (*f)(Rune); + int i; /* function called at interrupt time */ +} Dbgkey; + +static struct { + Rendez; + Dbgkey *work; + Dbgkey keys[50]; + int nkeys; + int on; +} dbg; + +static Dbgkey * +finddbgkey(Rune r) +{ + int i; + Dbgkey *dp; + + for(dp = dbg.keys, i = 0; i < dbg.nkeys; i++, dp++) + if(dp->r == r) + return dp; + return nil; +} + +static int +dbgwork(void *) +{ + return dbg.work != 0; +} + +static void +dbgproc(void *) +{ + Dbgkey *dp; + + setpri(PriRealtime); + for(;;) { + do { + sleep(&dbg, dbgwork, 0); + dp = dbg.work; + } while(dp == nil); + dp->f(dp->r); + dbg.work = nil; + } +} + +void +debugkey(Rune r, char *msg, void (*fcn)(), int iflag) +{ + Dbgkey *dp; + + if(dbg.nkeys >= nelem(dbg.keys)) + return; + if(finddbgkey(r) != nil) + return; + for(dp = &dbg.keys[dbg.nkeys++] - 1; dp >= dbg.keys; dp--) { + if(strcmp(dp->m, msg) < 0) + break; + dp[1] = dp[0]; + } + dp++; + dp->r = r; + dp->m = msg; + dp->f = fcn; + dp->i = iflag; +} + +static int +isdbgkey(Rune r) +{ + static int ctrlt; + Dbgkey *dp; + int echoctrlt = ctrlt; + + /* + * ^t hack BUG + */ + if(dbg.on || (ctrlt >= 2)) { + if(r == 0x14 || r == 0x05) { + ctrlt++; + return 0; + } + if(dp = finddbgkey(r)) { + if(dp->i || ctrlt > 2) + dp->f(r); + else { + dbg.work = dp; + wakeup(&dbg); + } + ctrlt = 0; + return 1; + } + ctrlt = 0; + } + else if(r == 0x14){ + ctrlt++; + return 1; + } + else + ctrlt = 0; + if(echoctrlt){ + char buf[3]; + + buf[0] = 0x14; + while(--echoctrlt >= 0){ + echo(buf[0], buf, 1); + qproduce(kbdq, buf, 1); + } + } + return 0; +} + +static void +dbgtoggle(Rune) +{ + dbg.on = !dbg.on; + print("Debug keys %s\n", dbg.on ? "HOT" : "COLD"); +} + +static void +dbghelp(void) +{ + int i; + Dbgkey *dp; + Dbgkey *dp2; + static char fmt[] = "%c: %-22s"; + + dp = dbg.keys; + dp2 = dp + (dbg.nkeys + 1)/2; + for(i = dbg.nkeys; i > 1; i -= 2, dp++, dp2++) { + print(fmt, dp->r, dp->m); + print(fmt, dp2->r, dp2->m); + print("\n"); + } + if(i) + print(fmt, dp->r, dp->m); + print("\n"); +} + +static void +debuginit(void) +{ + kproc("consdbg", dbgproc, nil, 0); + debugkey('|', "HOT|COLD keys", dbgtoggle, 0); + debugkey('?', "help", dbghelp, 0); +} + +/* + * Called by a uart interrupt for console input. + * + * turn '\r' into '\n' before putting it into the queue. + */ +int +kbdcr2nl(Queue *q, int ch) +{ + if(ch == '\r') + ch = '\n'; + return kbdputc(q, ch); +} + +/* + * Put character, possibly a rune, into read queue at interrupt time. + * Performs translation for compose sequences + * Called at interrupt time to process a character. + */ +int +kbdputc(Queue *q, int ch) +{ + int n; + char buf[3]; + Rune r; + static Rune kc[15]; + static int nk, collecting = 0; + + r = ch; + if(r == Latin) { + collecting = 1; + nk = 0; + return 0; + } + if(collecting) { + int c; + nk += runetochar((char*)&kc[nk], &r); + c = latin1(kc, nk); + if(c < -1) /* need more keystrokes */ + return 0; + collecting = 0; + if(c == -1) { /* invalid sequence */ + echo(kc[0], (char*)kc, nk); + qproduce(q, kc, nk); + return 0; + } + r = (Rune)c; + } + kbd.c = r; + n = runetochar(buf, &r); + if(n == 0) + return 0; + if(!isdbgkey(r)) { + echo(r, buf, n); + qproduce(q, buf, n); + } + return 0; +} + +void +kbdrepeat(int rep) +{ + kbd.repeat = rep; + kbd.count = 0; +} + +void +kbdclock(void) +{ + if(kbd.repeat == 0) + return; + if(kbd.repeat==1 && ++kbd.count>HZ){ + kbd.repeat = 2; + kbd.count = 0; + return; + } + if(++kbd.count&1) + kbdputc(kbdq, kbd.c); +} + +enum{ + Qdir, + Qcons, + Qsysctl, + Qconsctl, + Qdrivers, + Qhostowner, + Qkeyboard, + Qklog, + Qkprint, + Qscancode, + Qmemory, + Qmsec, + Qnull, + Qpin, + Qrandom, + Qnotquiterandom, + Qsysname, + Qtime, + Quser, + Qjit, +}; + +static Dirtab consdir[]= +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "cons", {Qcons}, 0, 0660, + "consctl", {Qconsctl}, 0, 0220, + "sysctl", {Qsysctl}, 0, 0644, + "drivers", {Qdrivers}, 0, 0444, + "hostowner", {Qhostowner}, 0, 0644, + "keyboard", {Qkeyboard}, 0, 0666, + "klog", {Qklog}, 0, 0444, + "kprint", {Qkprint}, 0, 0444, + "scancode", {Qscancode}, 0, 0444, + "memory", {Qmemory}, 0, 0444, + "msec", {Qmsec}, NUMSIZE, 0444, + "null", {Qnull}, 0, 0666, + "pin", {Qpin}, 0, 0666, + "random", {Qrandom}, 0, 0444, + "notquiterandom", {Qnotquiterandom}, 0, 0444, + "sysname", {Qsysname}, 0, 0664, + "time", {Qtime}, 0, 0664, + "user", {Quser}, 0, 0644, + "jit", {Qjit}, 0, 0666, +}; + +ulong boottime; /* seconds since epoch at boot */ + +long +seconds(void) +{ + return boottime + TK2SEC(MACHP(0)->ticks); +} + +vlong +mseconds(void) +{ + return ((vlong)boottime*1000)+((vlong)(TK2MS(MACHP(0)->ticks))); +} + +vlong +osusectime(void) +{ + return (((vlong)boottime*1000)+((vlong)(TK2MS(MACHP(0)->ticks)))*1000); +} + +vlong +nsec(void) +{ + return osusectime()*1000; /* TO DO */ +} + +int +readnum(ulong off, char *buf, ulong n, ulong val, int size) +{ + char tmp[64]; + + if(size > 64) size = 64; + + snprint(tmp, sizeof(tmp), "%*.0lud ", size, val); + if(off >= size) + return 0; + if(off+n > size) + n = size-off; + memmove(buf, tmp+off, n); + return n; +} + +int +readstr(ulong off, char *buf, ulong n, char *str) +{ + int size; + + size = strlen(str); + if(off >= size) + return 0; + if(off+n > size) + n = size-off; + memmove(buf, str+off, n); + return n; +} + +void +fddump() +{ + Proc *p; + Osenv *o; + int i; + Chan *c; + + p = proctab(6); + o = p->env; + for(i = 0; i <= o->fgrp->maxfd; i++) { + if((c = o->fgrp->fd[i]) == nil) + continue; + print("%d: %s\n", i, c->name == nil? "???": c->name->s); + } +} + +static void +qpanic(Rune) +{ + panic("User requested panic."); +} + +static void +rexit(Rune) +{ + exit(0); +} + +static void +consinit(void) +{ + randominit(); + debuginit(); + debugkey('f', "files/6", fddump, 0); + debugkey('q', "panic", qpanic, 1); + debugkey('r', "exit", rexit, 1); + klogq = qopen(128*1024, 0, 0, 0); +} + +static Chan* +consattach(char *spec) +{ + return devattach('c', spec); +} + +static Walkqid* +conswalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, consdir, nelem(consdir), devgen); +} + +static int +consstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, consdir, nelem(consdir), devgen); +} + +static void +flushkbdline(Queue *q) +{ + if(kbd.x){ + qwrite(q, kbd.line, kbd.x); + kbd.x = 0; + } +} + +static Chan* +consopen(Chan *c, int omode) +{ + c->aux = 0; + switch((ulong)c->qid.path){ + case Qconsctl: + if(!iseve()) + error(Eperm); + qlock(&kbd); + kbd.ctl++; + qunlock(&kbd); + break; + + case Qkeyboard: + if((omode & 3) != OWRITE) { + qlock(&kbd); + kbd.kbdr++; + flushkbdline(kbdq); + kbd.raw = 1; + qunlock(&kbd); + } + break; + + case Qscancode: + qlock(&kbd); + if(kscanq || !kscanid) { + qunlock(&kbd); + c->flag &= ~COPEN; + if(kscanq) + error(Einuse); + else + error(Ebadarg); + } + kscanq = qopen(256, 0, nil, nil); + qunlock(&kbd); + break; + + case Qkprint: + if((omode & 3) != OWRITE) { + wlock(&kprintq); + if(kprintq.q != nil){ + wunlock(&kprintq); + error(Einuse); + } + kprintq.q = qopen(32*1024, Qcoalesce, nil, nil); + if(kprintq.q == nil){ + wunlock(&kprintq); + error(Enomem); + } + qnoblock(kprintq.q, 1); + wunlock(&kprintq); + c->iounit = qiomaxatomic; + } + break; + } + return devopen(c, omode, consdir, nelem(consdir), devgen); +} + +static void +consclose(Chan *c) +{ + if((c->flag&COPEN) == 0) + return; + + switch((ulong)c->qid.path){ + case Qconsctl: + /* last close of control file turns off raw */ + qlock(&kbd); + if(--kbd.ctl == 0) + kbd.raw = 0; + qunlock(&kbd); + break; + + case Qkeyboard: + if(c->mode != OWRITE) { + qlock(&kbd); + --kbd.kbdr; + qunlock(&kbd); + } + break; + + case Qscancode: + qlock(&kbd); + if(kscanq) { + qfree(kscanq); + kscanq = 0; + } + qunlock(&kbd); + break; + + case Qkprint: + wlock(&kprintq); + qfree(kprintq.q); + kprintq.q = nil; + wunlock(&kprintq); + break; + } +} + +static long +consread(Chan *c, void *buf, long n, vlong offset) +{ + int l; + Osenv *o; + int ch, eol, i; + char *p, tmp[128]; + char *cbuf = buf; + + if(n <= 0) + return n; + o = up->env; + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, buf, n, consdir, nelem(consdir), devgen); + case Qsysctl: + return readstr(offset, buf, n, VERSION); + case Qcons: + case Qkeyboard: + qlock(&kbd); + if(waserror()) { + qunlock(&kbd); + nexterror(); + } + if(kbd.raw || kbd.kbdr) { + if(qcanread(lineq)) + n = qread(lineq, buf, n); + else { + /* read as much as possible */ + do { + i = qread(kbdq, cbuf, n); + cbuf += i; + n -= i; + } while(n>0 && qcanread(kbdq)); + n = cbuf - (char*)buf; + } + } else { + while(!qcanread(lineq)) { + qread(kbdq, &kbd.line[kbd.x], 1); + ch = kbd.line[kbd.x]; + eol = 0; + switch(ch){ + case '\b': + if(kbd.x) + kbd.x--; + break; + case 0x15: + kbd.x = 0; + break; + case '\n': + case 0x04: + eol = 1; + default: + kbd.line[kbd.x++] = ch; + break; + } + if(kbd.x == sizeof(kbd.line) || eol) { + if(ch == 0x04) + kbd.x--; + qwrite(lineq, kbd.line, kbd.x); + kbd.x = 0; + } + } + n = qread(lineq, buf, n); + } + qunlock(&kbd); + poperror(); + return n; + + case Qscancode: + if(offset == 0) + return readstr(0, buf, n, kscanid); + else + return qread(kscanq, buf, n); + + case Qpin: + p = "pin set"; + if(up->env->pgrp->pin == Nopin) + p = "no pin"; + return readstr(offset, buf, n, p); + + case Qtime: + snprint(tmp, sizeof(tmp), "%.lld", (vlong)mseconds()*1000); + return readstr(offset, buf, n, tmp); + + case Qhostowner: + return readstr(offset, buf, n, eve); + + case Quser: + return readstr(offset, buf, n, o->user); + + case Qjit: + snprint(tmp, sizeof(tmp), "%d", cflag); + return readstr(offset, buf, n, tmp); + + case Qnull: + return 0; + + case Qmsec: + return readnum(offset, buf, n, TK2MS(MACHP(0)->ticks), NUMSIZE); + + case Qsysname: + if(sysname == nil) + return 0; + return readstr(offset, buf, n, sysname); + + case Qnotquiterandom: + genrandom(buf, n); + return n; + + case Qrandom: + return randomread(buf, n); + + case Qmemory: + return poolread(buf, n, offset); + + case Qdrivers: + p = malloc(READSTR); + if(p == nil) + error(Enomem); + l = 0; + for(i = 0; devtab[i] != nil; i++) + l += snprint(p+l, READSTR-l, "#%C %s\n", devtab[i]->dc, devtab[i]->name); + if(waserror()){ + free(p); + nexterror(); + } + n = readstr(offset, buf, n, p); + free(p); + poperror(); + return n; + + case Qklog: + return qread(klogq, buf, n); + + case Qkprint: + rlock(&kprintq); + if(waserror()){ + runlock(&kprintq); + nexterror(); + } + n = qread(kprintq.q, buf, n); + poperror(); + runlock(&kprintq); + return n; + + default: + print("consread %llud\n", c->qid.path); + error(Egreg); + } + return -1; /* never reached */ +} + +static long +conswrite(Chan *c, void *va, long n, vlong offset) +{ + vlong t; + long l, bp; + char *a = va; + Cmdbuf *cb; + Cmdtab *ct; + char buf[256]; + int x; + + switch((ulong)c->qid.path){ + case Qcons: + /* + * Can't page fault in putstrn, so copy the data locally. + */ + l = n; + while(l > 0){ + bp = l; + if(bp > sizeof buf) + bp = sizeof buf; + memmove(buf, a, bp); + putstrn0(a, bp, 1); + a += bp; + l -= bp; + } + break; + + case Qconsctl: + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + strncpy(buf, a, n); + buf[n] = 0; + for(a = buf; a;){ + if(strncmp(a, "rawon", 5) == 0){ + qlock(&kbd); + flushkbdline(kbdq); + kbd.raw = 1; + qunlock(&kbd); + } else if(strncmp(a, "rawoff", 6) == 0){ + qlock(&kbd); + kbd.raw = 0; + kbd.x = 0; + qunlock(&kbd); + } + if(a = strchr(a, ' ')) + a++; + } + break; + + case Qkeyboard: + for(x=0; x<n; ) { + Rune r; + x += chartorune(&r, &a[x]); + kbdputc(kbdq, r); + } + break; + + case Qtime: + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + strncpy(buf, a, n); + buf[n] = 0; + t = strtoll(buf, 0, 0)/1000000; + boottime = t - TK2SEC(MACHP(0)->ticks); + break; + + case Qhostowner: + if(!iseve()) + error(Eperm); + if(offset != 0 || n >= sizeof(buf)) + error(Ebadarg); + memmove(buf, a, n); + buf[n] = '\0'; + if(n > 0 && buf[n-1] == '\n') + buf[--n] = 0; + if(n <= 0) + error(Ebadarg); + renameuser(eve, buf); + renameproguser(eve, buf); + kstrdup(&eve, buf); + kstrdup(&up->env->user, buf); + break; + + case Quser: + if(!iseve()) + error(Eperm); + if(offset != 0) + error(Ebadarg); + if(n <= 0 || n >= sizeof(buf)) + error(Ebadarg); + strncpy(buf, a, n); + buf[n] = 0; + if(buf[n-1] == '\n') + buf[n-1] = 0; + kstrdup(&up->env->user, buf); + break; + + case Qjit: + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + strncpy(buf, va, n); + buf[n] = '\0'; + x = atoi(buf); + if(x < 0 || x > 9) + error(Ebadarg); + cflag = x; + return n; + + case Qnull: + break; + + case Qpin: + if(up->env->pgrp->pin != Nopin) + error("pin already set"); + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + strncpy(buf, va, n); + buf[n] = '\0'; + up->env->pgrp->pin = atoi(buf); + return n; + + case Qsysname: + if(offset != 0) + error(Ebadarg); + if(n <= 0 || n >= sizeof(buf)) + error(Ebadarg); + strncpy(buf, a, n); + buf[n] = 0; + if(buf[n-1] == '\n') + buf[n-1] = 0; + kstrdup(&sysname, buf); + break; + + case Qsysctl: + if(!iseve()) + error(Eperm); + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + ct = lookupcmd(cb, sysctlcmd, nelem(sysctlcmd)); + switch(ct->index){ + case CMreboot: + reboot(); + break; + case CMhalt: + halt(); + break; + case CMpanic: + panic("sysctl"); + case CMconsole: + consoleprint = strcmp(cb->f[1], "off") != 0; + break; + case CMbroken: + keepbroken = 1; + break; + case CMnobroken: + keepbroken = 0; + break; + } + poperror(); + free(cb); + break; + + default: + print("conswrite: %llud\n", c->qid.path); + error(Egreg); + } + return n; +} + +Dev consdevtab = { + 'c', + "cons", + + devreset, + consinit, + devshutdown, + consattach, + conswalk, + consstat, + consopen, + devcreate, + consclose, + consread, + devbread, + conswrite, + devbwrite, + devremove, + devwstat, +}; + +static ulong randn; + +static void +seedrand(void) +{ + randomread((void*)&randn, sizeof(randn)); +} + +int +nrand(int n) +{ + if(randn == 0) + seedrand(); + randn = randn*1103515245 + 12345 + MACHP(0)->ticks; + return (randn>>16) % n; +} + +int +rand(void) +{ + nrand(1); + return randn; +} + +ulong +truerand(void) +{ + ulong x; + + randomread(&x, sizeof(x)); + return x; +} + +QLock grandomlk; + +void +_genrandomqlock(void) +{ + qlock(&grandomlk); +} + + +void +_genrandomqunlock(void) +{ + qunlock(&grandomlk); +} diff --git a/os/port/devdbg.c b/os/port/devdbg.c new file mode 100644 index 00000000..daaf314c --- /dev/null +++ b/os/port/devdbg.c @@ -0,0 +1,1000 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "ureg.h" +#include "../port/error.h" +#include "rdbg.h" + +#include <kernel.h> +#include <interp.h> + +/* + * The following should be set in the config file to override + * the defaults. + */ +int dbgstart; +char *dbgdata; +char *dbgctl; +char *dbgctlstart; +char *dbgctlstop; +char *dbgctlflush; + +// +// Error messages sent to the remote debugger +// +static uchar Ereset[9] = { 'r', 'e', 's', 'e', 't' }; +static uchar Ecount[9] = { 'c', 'o', 'u', 'n', 't' }; +static uchar Eunk[9] = { 'u', 'n', 'k' }; +static uchar Einval[9] = { 'i', 'n', 'v', 'a', 'l' }; +static uchar Ebadpid[9] = {'p', 'i', 'd'}; +static uchar Eunsup[9] = { 'u', 'n', 's', 'u', 'p' }; +static uchar Enotstop[9] = { 'n', 'o', 't', 's', 't', 'o', 'p' }; + +// +// Error messages raised via call to error() +// +static char Erunning[] = "Not allowed while debugger is running"; +static char Enumarg[] = "Not enough args"; +static char Ebadcmd[] = "Unknown command"; + +static int PROCREG; +static struct { + Rendez; + Bkpt *b; +} brk; + +static Queue *logq; + +int dbgchat = 0; + +typedef struct Debugger Debugger; +struct Debugger { + RWlock; + int running; + char data[PRINTSIZE]; + char ctl[PRINTSIZE]; + char ctlstart[PRINTSIZE]; + char ctlstop[PRINTSIZE]; + char ctlflush[PRINTSIZE]; +}; + +static Debugger debugger = { + .data= "#t/eia0", + .ctl= "#t/eia0ctl", + .ctlstart= "b19200", + .ctlstop= "h", + .ctlflush= "f", +}; + +enum { + BkptStackSize= 256, +}; + +typedef struct SkipArg SkipArg; +struct SkipArg +{ + Bkpt *b; + Proc *p; +}; + +Bkpt *breakpoints; +void freecondlist(BkptCond *l); + +static int +getbreaks(ulong addr, Bkpt **a, int nb) +{ + Bkpt *b; + int n; + + n = 0; + for(b = breakpoints; b != nil; b = b->next){ + if(b->addr == addr){ + a[n++] = b; + if(n == nb) + break; + } + } + return n; +} + +Bkpt* +newbreak(int id, ulong addr, BkptCond *conds, void(*handler)(Bkpt*), void *aux) +{ + Bkpt *b; + + b = malloc(sizeof(*b)); + if(b == nil) + error(Enomem); + + b->id = id; + b->conditions = conds; + b->addr = addr; + b->handler = handler; + b->aux = aux; + b->next = nil; + + return b; +} + +void +freebreak(Bkpt *b) +{ + freecondlist(b->conditions); + free(b); +} + +BkptCond* +newcondition(uchar cmd, ulong val) +{ + BkptCond *c; + + c = mallocz(sizeof(*c), 0); + if(c == nil) + error(Enomem); + + c->op = cmd; + c->val = val; + c->next = nil; + + return c; +} + +void +freecondlist(BkptCond *l) +{ + BkptCond *next; + + while(l != nil){ + next = l->next; + free(l); + l = next; + } +} + + +void +breakset(Bkpt *b) +{ + Bkpt *e[1]; + + if(getbreaks(b->addr, e, 1) != 0){ + b->instr = e[0]->instr; + } else { + b->instr = machinstr(b->addr); + machbreakset(b->addr); + } + + b->next = breakpoints; + breakpoints = b; +} + +void +breakrestore(Bkpt *b) +{ + b->next = breakpoints; + breakpoints = b; + machbreakset(b->addr); +} + +Bkpt* +breakclear(int id) +{ + Bkpt *b, *e, *p; + + for(b=breakpoints, p=nil; b != nil && b->id != id; p = b, b = b->next) + ; + + if(b != nil){ + if(p == nil) + breakpoints = b->next; + else + p->next = b->next; + + if(getbreaks(b->addr, &e, 1) == 0) + machbreakclear(b->addr, b->instr); + } + + return b; +} + +void +breaknotify(Bkpt *b, Proc *p) +{ + p->dbgstop = 1; // stop running this process. + b->handler(b); +} + +int +breakmatch(BkptCond *cond, Ureg *ur, Proc *p) +{ + ulong a, b; + int pos; + ulong s[BkptStackSize]; + + memset(s, 0, sizeof(s)); + pos = 0; + + for(;cond != nil; cond = cond->next){ + switch(cond->op){ + default: + panic("breakmatch: unknown operator %c", cond->op); + break; + case 'k': + if(p == nil || p->pid != cond->val) + return 0; + s[pos++] = 1; + break; + case 'b': + if(ur->pc != cond->val) + return 0; + s[pos++] = 1; + break; + case 'p': s[pos++] = cond->val; break; + case '*': a = *(ulong*)s[--pos]; s[pos++] = a; break; + case '&': a = s[--pos]; b = s[--pos]; s[pos++] = a & b; break; + case '=': a = s[--pos]; b = s[--pos]; s[pos++] = a == b; break; + case '!': a = s[--pos]; b = s[--pos]; s[pos++] = a != b; break; + case 'a': a = s[--pos]; b = s[--pos]; s[pos++] = a && b; break; + case 'o': a = s[--pos]; b = s[--pos]; s[pos++] = a || b; break; + } + } + + if(pos && s[pos-1]) + return 1; + return 0; +} + +void +breakinit(void) +{ + machbreakinit(); +} + +static void +dbglog(char *fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + qwrite(logq, buf, n); +} + +static int +get(int dbgfd, uchar *b) +{ + int i; + uchar c; + + if(kread(dbgfd, &c, 1) < 0) + error(Eio); + for(i=0; i<9; i++){ + if(kread(dbgfd, b++, 1) < 0) + error(Eio); + } + return c; +} + +static void +mesg(int dbgfd, int m, uchar *buf) +{ + int i; + uchar c; + + c = m; + if(kwrite(dbgfd, &c, 1) < 0) + error(Eio); + for(i=0; i<9; i++){ + if(kwrite(dbgfd, buf+i, 1) < 0) + error(Eio); + } +} + +static ulong +dbglong(uchar *s) +{ + return (s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]<<0); +} + +static Proc * +dbgproc(ulong pid, int dbgok) +{ + int i; + Proc *p; + + if(!dbgok && pid == up->pid) + return 0; + p = proctab(0); + for(i = 0; i < conf.nproc; i++){ + if(p->pid == pid) + return p; + p++; + } + return 0; +} + +static void* +addr(uchar *s) +{ + ulong a; + Proc *p; + static Ureg ureg; + + a = ((s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]<<0)); + if(a < sizeof(Ureg)){ + p = dbgproc(PROCREG, 0); + if(p == 0){ + dbglog("dbg: invalid pid\n"); + return 0; + } + if(p->dbgreg){ + /* in trap(), registers are all on stack */ + memmove(&ureg, p->dbgreg, sizeof(ureg)); + } + else { + /* not in trap, only pc and sp are available */ + memset(&ureg, 0, sizeof(ureg)); + ureg.sp = p->sched.sp; + ureg.pc = p->sched.pc; + } + return (uchar*)&ureg+a; + } + return (void*)a; +} + + +static void +dumpcmd(uchar cmd, uchar *min) +{ + char *s; + int n; + + switch(cmd){ + case Terr: s = "Terr"; break; + case Tmget: s = "Tmget"; break; + case Tmput: s = "Tmput"; break; + case Tspid: s = "Tspid"; break; + case Tproc: s = "Tproc"; break; + case Tstatus: s = "Tstatus"; break; + case Trnote: s = "Trnote"; break; + case Tstartstop: s = "Tstartstop"; break; + case Twaitstop: s = "Twaitstop"; break; + case Tstart: s = "Tstart"; break; + case Tstop: s = "Tstop"; break; + case Tkill: s = "Tkill"; break; + case Tcondbreak: s = "Tcondbreak"; break; + default: s = "<Unknown>"; break; + } + dbglog("%s: [%2.2ux]: ", s, cmd); + for(n = 0; n < 9; n++) + dbglog("%2.2ux", min[n]); + dbglog("\n"); +} + +static int +brkpending(void *a) +{ + Proc *p; + + p = a; + if(brk.b != nil) return 1; + + p->dbgstop = 0; /* atomic */ + if(p->state == Stopped) + ready(p); + + return 0; +} + +static void +gotbreak(Bkpt *b) +{ + Bkpt *cur, *prev; + + b->link = nil; + + for(prev = nil, cur = brk.b; cur != nil; prev = cur, cur = cur->link) + ; + if(prev == nil) + brk.b = b; + else + prev->link = b; + + wakeup(&brk); +} + +static int +startstop(Proc *p) +{ + int id; + int s; + Bkpt *b; + + sleep(&brk, brkpending, p); + + s = splhi(); + b = brk.b; + brk.b = b->link; + splx(s); + + id = b->id; + + return id; +} + +static int +condbreak(char cmd, ulong val) +{ + BkptCond *c; + static BkptCond *head = nil; + static BkptCond *tail = nil; + static Proc *p = nil; + static int id = -1; + int s; + + if(waserror()){ + dbglog(up->env->errstr); + freecondlist(head); + head = tail = nil; + p = nil; + id = -1; + return 0; + } + + switch(cmd){ + case 'b': case 'p': + case '*': case '&': case '=': + case '!': case 'a': case 'o': + break; + case 'n': + id = val; + poperror(); + return 1; + case 'k': + p = dbgproc(val, 0); + if(p == nil) + error("k: unknown pid"); + break; + case 'd': { + Bkpt *b; + + s = splhi(); + b = breakclear(val); + if(b != nil){ + Bkpt *cur, *prev; + + prev = nil; + cur = brk.b; + while(cur != nil){ + if(cur->id == b->id){ + if(prev == nil) + brk.b = cur->link; + else + prev->link = cur->link; + break; + } + cur = cur->link; + } + freebreak(b); + } + splx(s); + poperror(); + return 1; + } + default: + dbglog("condbreak(): unknown op %c %lux\n", cmd, val); + error("unknown op"); + } + + c = newcondition(cmd, val); + + // + // the 'b' command comes last, (so we know we have reached the end + // of the condition list), but it should be the first thing + // checked, so put it at the head. + // + if(cmd == 'b'){ + if(p == nil) error("no pid"); + if(id == -1) error("no id"); + + c->next = head; + s = splhi(); + breakset(newbreak(id, val, c, gotbreak, p)); + splx(s); + head = tail = nil; + p = nil; + id = -1; + } else if(tail != nil){ + tail->next = c; + tail = c; + } else + head = tail = c; + + poperror(); + + return 1; +} + +static void +dbg(void*) +{ + Proc *p; + ulong val; + int n, cfd, dfd; + uchar cmd, *a, min[RDBMSGLEN-1], mout[RDBMSGLEN-1]; + + rlock(&debugger); + + setpri(PriRealtime); + + closefgrp(up->env->fgrp); + up->env->fgrp = newfgrp(nil); + + if(waserror()){ + dbglog("dbg: quits: %s\n", up->env->errstr); + runlock(&debugger); + wlock(&debugger); + debugger.running = 0; + wunlock(&debugger); + pexit("", 0); + } + + dfd = kopen(debugger.data, ORDWR); + if(dfd < 0){ + dbglog("dbg: can't open %s: %s\n",debugger.data, up->env->errstr); + error(Eio); + } + if(waserror()){ + kclose(dfd); + nexterror(); + } + + if(debugger.ctl[0] != 0){ + cfd = kopen(debugger.ctl, ORDWR); + if(cfd < 0){ + dbglog("dbg: can't open %s: %s\n", debugger.ctl, up->env->errstr); + error(Eio); + } + if(kwrite(cfd, debugger.ctlstart, strlen(debugger.ctlstart)) < 0){ + dbglog("dbg: write %s: %s\n", debugger.ctl, up->env->errstr); + error(Eio); + } + }else + cfd = -1; + if(waserror()){ + if(cfd != -1){ + kwrite(cfd, debugger.ctlflush, strlen(debugger.ctlflush)); + kclose(cfd); + } + nexterror(); + } + + mesg(dfd, Rerr, Ereset); + + for(;;){ + memset(mout, 0, sizeof(mout)); + cmd = get(dfd, min); + if(dbgchat) + dumpcmd(cmd, min); + switch(cmd){ + case Tmget: + n = min[4]; + if(n > 9){ + mesg(dfd, Rerr, Ecount); + break; + } + a = addr(min+0); + if(!isvalid_va(a)){ + mesg(dfd, Rerr, Einval); + break; + } + memmove(mout, a, n); + mesg(dfd, Rmget, mout); + break; + case Tmput: + n = min[4]; + if(n > 4){ + mesg(dfd, Rerr, Ecount); + break; + } + a = addr(min+0); + if(!isvalid_va(a)){ + mesg(dfd, Rerr, Einval); + break; + } + memmove(a, min+5, n); + segflush(a, n); + mesg(dfd, Rmput, mout); + break; + case Tproc: + p = dbgproc(dbglong(min+0), 0); + if(p == 0){ + mesg(dfd, Rerr, Ebadpid); + break; + } + PROCREG = p->pid; /* try this instead of Tspid */ + sprint((char*)mout, "%8.8lux", p); + mesg(dfd, Rproc, mout); + break; + case Tstatus: + p = dbgproc(dbglong(min+0), 1); + if(p == 0){ + mesg(dfd, Rerr, Ebadpid); + break; + } + if(p->state > Rendezvous || p->state < Dead) + sprint((char*)mout, "%8.8ux", p->state); + else if(p->dbgstop == 1) + strncpy((char*)mout, statename[Stopped], sizeof(mout)); + else + strncpy((char*)mout, statename[p->state], sizeof(mout)); + mesg(dfd, Rstatus, mout); + break; + case Trnote: + p = dbgproc(dbglong(min+0), 0); + if(p == 0){ + mesg(dfd, Rerr, Ebadpid); + break; + } + mout[0] = 0; /* should be trap status, if any */ + mesg(dfd, Rrnote, mout); + break; + case Tstop: + p = dbgproc(dbglong(min+0), 0); + if(p == 0){ + mesg(dfd, Rerr, Ebadpid); + break; + } + p->dbgstop = 1; /* atomic */ + mout[0] = 0; + mesg(dfd, Rstop, mout); + break; + case Tstart: + p = dbgproc(dbglong(min+0), 0); + if(p == 0){ + mesg(dfd, Rerr, Ebadpid); + break; + } + p->dbgstop = 0; /* atomic */ + if(p->state == Stopped) + ready(p); + mout[0] = 0; + mesg(dfd, Rstart, mout); + break; + case Tstartstop: + p = dbgproc(dbglong(min+0), 0); + if(p == 0){ + mesg(dfd, Rerr, Ebadpid); + break; + } + if(!p->dbgstop){ + mesg(dfd, Rerr, Enotstop); + break; + } + mout[0] = startstop(p); + mesg(dfd, Rstartstop, mout); + break; + case Tcondbreak: + val = dbglong(min+0); + if(!condbreak(min[4], val)){ + mesg(dfd, Rerr, Eunk); + break; + } + mout[0] = 0; + mesg(dfd, Rcondbreak, mout); + break; + default: + dumpcmd(cmd, min); + mesg(dfd, Rerr, Eunk); + break; + } + } +} + +static void +dbgnote(Proc *p, Ureg *ur) +{ + if(p){ + p->dbgreg = ur; + PROCREG = p->pid; /* acid can get the trap info from regs */ + } +} + +enum { + Qdir, + Qdbgctl, + Qdbglog, + + DBGrun = 1, + DBGstop = 2, + + Loglimit = 4096, +}; + +static Dirtab dbgdir[]= +{ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "dbgctl", {Qdbgctl}, 0, 0660, + "dbglog", {Qdbglog}, 0, 0440, +}; + +static void +start_debugger(void) +{ + breakinit(); + dbglog("starting debugger\n"); + debugger.running++; + kproc("dbg", dbg, 0, KPDUPPG); +} + +static void +dbginit(void) +{ + + logq = qopen(Loglimit, 0, 0, 0); + if(logq == nil) + error(Enomem); + qnoblock(logq, 1); + + wlock(&debugger); + if(waserror()){ + wunlock(&debugger); + qfree(logq); + logq = nil; + nexterror(); + } + + if(dbgdata != nil){ + strncpy(debugger.data, dbgdata, sizeof(debugger.data)); + debugger.data[sizeof(debugger.data)-1] = 0; + } + if(dbgctl != nil){ + strncpy(debugger.ctl, dbgctl, sizeof(debugger.ctl)); + debugger.ctl[sizeof(debugger.ctl)-1] = 0; + } + if(dbgctlstart != nil){ + strncpy(debugger.ctlstart, dbgctlstart, sizeof(debugger.ctlstart)); + debugger.ctlstart[sizeof(debugger.ctlstart)-1] = 0; + } + if(dbgctlstop != nil){ + strncpy(debugger.ctlstop, dbgctlstop, sizeof(debugger.ctlstop)); + debugger.ctlstop[sizeof(debugger.ctlstop)-1] = 0; + } + if(dbgctlflush != nil){ + strncpy(debugger.ctlflush, dbgctlflush, sizeof(debugger.ctlflush)); + debugger.ctlflush[sizeof(debugger.ctlflush)-1] = 0; + } + if(dbgstart) + start_debugger(); + + poperror(); + wunlock(&debugger); +} + +static Chan* +dbgattach(char *spec) +{ + return devattach('b', spec); +} + +static Walkqid* +dbgwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, dbgdir, nelem(dbgdir), devgen); +} + +static int +dbgstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, dbgdir, nelem(dbgdir), devgen); +} + +static Chan* +dbgopen(Chan *c, int omode) +{ + return devopen(c, omode, dbgdir, nelem(dbgdir), devgen); +} + +static long +dbgread(Chan *c, void *buf, long n, vlong offset) +{ + char *ctlstate; + + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, buf, n, dbgdir, nelem(dbgdir), devgen); + case Qdbgctl: + rlock(&debugger); + ctlstate = smprint("%s data %s ctl %s ctlstart %s ctlstop %s ctlflush %s\n", + debugger.running ? "running" : "stopped", + debugger.data, debugger.ctl, + debugger.ctlstart, debugger.ctlstop, debugger.ctlflush); + runlock(&debugger); + if(ctlstate == nil) + error(Enomem); + if(waserror()){ + free(ctlstate); + nexterror(); + } + n = readstr(offset, buf, n, ctlstate); + poperror(); + free(ctlstate); + return n; + case Qdbglog: + return qread(logq, buf, n); + default: + error(Egreg); + } + return -1; /* never reached */ +} + +static void +ctl(Cmdbuf *cb) +{ + Debugger d; + int dbgstate = 0; + int i; + char *df; + int dfsize; + int setval; + + memset(&d, 0, sizeof(d)); + for(i=0; i < cb->nf; i++){ + setval = 0; + df = nil; + dfsize = 0; + switch(cb->f[i][0]){ + case 'd': + df = d.data; + dfsize = sizeof(d.data); + setval=1; + break; + case 'c': + df = d.ctl; + dfsize = sizeof(d.ctl); + setval=1; + break; + case 'i': + df = d.ctlstart; + dfsize = sizeof(d.ctlstart); + setval=1; + break; + case 'h': + df = d.ctlstop; + dfsize = sizeof(d.ctlstop); + setval=1; + break; + case 'f': + df = d.ctlflush; + dfsize = sizeof(d.ctlflush); + setval=1; + break; + case 'r': + dbgstate = DBGrun; + break; + case 's': + dbgstate = DBGstop; + break; + default: + error(Ebadcmd); + } + if(setval){ + if(i+1 >= cb->nf) + cmderror(cb, Enumarg); + strncpy(df, cb->f[i+1], dfsize-1); + df[dfsize-1] = 0; + ++d.running; + ++i; + } + } + + if(d.running){ + wlock(&debugger); + if(debugger.running){ + wunlock(&debugger); + error(Erunning); + } + if(d.data[0] != 0){ + strcpy(debugger.data, d.data); + dbglog("data %s\n",debugger.data); + } + if(d.ctl[0] != 0){ + strcpy(debugger.ctl, d.ctl); + dbglog("ctl %s\n",debugger.ctl); + } + if(d.ctlstart[0] != 0){ + strcpy(debugger.ctlstart, d.ctlstart); + dbglog("ctlstart %s\n",debugger.ctlstart); + } + if(d.ctlstop[0] != 0){ + strcpy(debugger.ctlstop, d.ctlstop); + dbglog("ctlstop %s\n",debugger.ctlstop); + } + wunlock(&debugger); + } + + if(dbgstate == DBGrun){ + if(!debugger.running){ + wlock(&debugger); + if(waserror()){ + wunlock(&debugger); + nexterror(); + } + if(!debugger.running) + start_debugger(); + else + dbglog("debugger already running\n"); + poperror(); + wunlock(&debugger); + } else + dbglog("debugger already running\n"); + } else if(dbgstate == DBGstop){ + if(debugger.running){ + /* force hangup to stop the dbg process */ + int cfd; + if(debugger.ctl[0] == 0) + return; + cfd = kopen(debugger.ctl, OWRITE); + if(cfd == -1) + error(up->env->errstr); + dbglog("stopping debugger\n"); + if(kwrite(cfd, debugger.ctlstop, strlen(debugger.ctlstop)) == -1) + error(up->env->errstr); + kclose(cfd); + } else + dbglog("debugger not running\n"); + } +} + +static long +dbgwrite(Chan *c, void *va, long n, vlong) +{ + Cmdbuf *cb; + + switch((ulong)c->qid.path){ + default: + error(Egreg); + break; + case Qdbgctl: + cb = parsecmd(va, n); + if(waserror()){ + free(cb); + nexterror(); + } + ctl(cb); + poperror(); + break; + } + return n; +} + +static void +dbgclose(Chan*) +{ +} + +Dev dbgdevtab = { + 'b', + "dbg", + + devreset, + dbginit, + devshutdown, + dbgattach, + dbgwalk, + dbgstat, + dbgopen, + devcreate, + dbgclose, + dbgread, + devbread, + dbgwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devdraw.c b/os/port/devdraw.c new file mode 100644 index 00000000..a7c7151d --- /dev/null +++ b/os/port/devdraw.c @@ -0,0 +1,2088 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <memlayer.h> +#include <cursor.h> +#include "screen.h" + +enum +{ + Qtopdir = 0, + Qnew, + Q3rd, + Q2nd, + Qcolormap, + Qctl, + Qdata, + Qrefresh, +}; + +/* + * Qid path is: + * 4 bits of file type (qids above) + * 24 bits of mux slot number +1; 0 means not attached to client + */ +#define QSHIFT 4 /* location in qid of client # */ + +#define QID(q) ((((ulong)(q).path)&0x0000000F)>>0) +#define CLIENTPATH(q) ((((ulong)q)&0x7FFFFFF0)>>QSHIFT) +#define CLIENT(q) CLIENTPATH((q).path) + +#define NHASH (1<<5) +#define HASHMASK (NHASH-1) +#define IOUNIT (64*1024) + +typedef struct Client Client; +typedef struct Draw Draw; +typedef struct DImage DImage; +typedef struct DScreen DScreen; +typedef struct CScreen CScreen; +typedef struct FChar FChar; +typedef struct Refresh Refresh; +typedef struct Refx Refx; +typedef struct DName DName; + +ulong blanktime = 30; /* in minutes; a half hour */ + +struct Draw +{ + QLock; + int clientid; + int nclient; + Client** client; + int nname; + DName* name; + int vers; + int softscreen; + int blanked; /* screen turned off */ + ulong blanktime; /* time of last operation */ + ulong savemap[3*256]; +}; + +struct Client +{ + Ref r; + DImage* dimage[NHASH]; + CScreen* cscreen; + Refresh* refresh; + Rendez refrend; + uchar* readdata; + int nreaddata; + int busy; + int clientid; + int slot; + int refreshme; + int infoid; + int op; +}; + +struct Refresh +{ + DImage* dimage; + Rectangle r; + Refresh* next; +}; + +struct Refx +{ + Client* client; + DImage* dimage; +}; + +struct DName +{ + char *name; + Client *client; + DImage* dimage; + int vers; +}; + +struct FChar +{ + int minx; /* left edge of bits */ + int maxx; /* right edge of bits */ + uchar miny; /* first non-zero scan-line */ + uchar maxy; /* last non-zero scan-line + 1 */ + schar left; /* offset of baseline */ + uchar width; /* width of baseline */ +}; + +/* + * Reference counts in DImages: + * one per open by original client + * one per screen image or fill + * one per image derived from this one by name + */ +struct DImage +{ + int id; + int ref; + char *name; + int vers; + Memimage* image; + int ascent; + int nfchar; + FChar* fchar; + DScreen* dscreen; /* 0 if not a window */ + DImage* fromname; /* image this one is derived from, by name */ + DImage* next; +}; + +struct CScreen +{ + DScreen* dscreen; + CScreen* next; +}; + +struct DScreen +{ + int id; + int public; + int ref; + DImage *dimage; + DImage *dfill; + Memscreen* screen; + Client* owner; + DScreen* next; +}; + +static Draw sdraw; +static Memimage *screenimage; +static Memdata screendata; +static Rectangle flushrect; +static int waste; +static DScreen* dscreen; +extern void flushmemscreen(Rectangle); + void drawmesg(Client*, void*, int); + void drawuninstall(Client*, int); + void drawfreedimage(DImage*); + Client* drawclientofpath(ulong); + +static char Enodrawimage[] = "unknown id for draw image"; +static char Enodrawscreen[] = "unknown id for draw screen"; +static char Eshortdraw[] = "short draw message"; +static char Eshortread[] = "draw read too short"; +static char Eimageexists[] = "image id in use"; +static char Escreenexists[] = "screen id in use"; +static char Edrawmem[] = "out of memory: image"; +static char Ereadoutside[] = "readimage outside image"; +static char Ewriteoutside[] = "writeimage outside image"; +static char Enotfont[] = "image not a font"; +static char Eindex[] = "character index out of range"; +static char Enoclient[] = "no such draw client"; +static char Enameused[] = "image name in use"; +static char Enoname[] = "no image with that name"; +static char Eoldname[] = "named image no longer valid"; +static char Enamed[] = "image already has name"; +static char Ewrongname[] = "wrong name for image"; + +static int +drawgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp) +{ + int t; + Qid q; + ulong path; + Client *cl; + + q.vers = 0; + + if(s == DEVDOTDOT){ + switch(QID(c->qid)){ + case Qtopdir: + case Q2nd: + mkqid(&q, Qtopdir, 0, QTDIR); + devdir(c, q, "#i", 0, eve, 0500, dp); + break; + case Q3rd: + cl = drawclientofpath(c->qid.path); + if(cl == nil) + strcpy(up->genbuf, "??"); + else + sprint(up->genbuf, "%d", cl->clientid); + mkqid(&q, Q2nd, 0, QTDIR); + devdir(c, q, up->genbuf, 0, eve, 0500, dp); + break; + default: + panic("drawwalk %llux", c->qid.path); + } + return 1; + } + + /* + * Top level directory contains the name of the device. + */ + t = QID(c->qid); + if(t == Qtopdir){ + switch(s){ + case 0: + mkqid(&q, Q2nd, 0, QTDIR); + devdir(c, q, "draw", 0, eve, 0555, dp); + break; + default: + return -1; + } + return 1; + } + + /* + * Second level contains "new" plus all the clients. + */ + if(t == Q2nd || t == Qnew){ + if(s == 0){ + mkqid(&q, Qnew, 0, QTFILE); + devdir(c, q, "new", 0, eve, 0666, dp); + } + else if(s <= sdraw.nclient){ + cl = sdraw.client[s-1]; + if(cl == 0) + return 0; + sprint(up->genbuf, "%d", cl->clientid); + mkqid(&q, (s<<QSHIFT)|Q3rd, 0, QTDIR); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + else + return -1; + return 1; + } + + /* + * Third level. + */ + path = c->qid.path&~((1<<QSHIFT)-1); /* slot component */ + q.vers = c->qid.vers; + q.type = QTFILE; + switch(s){ + case 0: + q.path = path|Qcolormap; + devdir(c, q, "colormap", 0, eve, 0600, dp); + break; + case 1: + q.path = path|Qctl; + devdir(c, q, "ctl", 0, eve, 0600, dp); + break; + case 2: + q.path = path|Qdata; + devdir(c, q, "data", 0, eve, 0600, dp); + break; + case 3: + q.path = path|Qrefresh; + devdir(c, q, "refresh", 0, eve, 0400, dp); + break; + default: + return -1; + } + return 1; +} + +static +int +drawrefactive(void *a) +{ + Client *c; + + c = a; + return c->refreshme || c->refresh!=0; +} + +static +void +drawrefreshscreen(DImage *l, Client *client) +{ + while(l != nil && l->dscreen == nil) + l = l->fromname; + if(l != nil && l->dscreen->owner != client) + l->dscreen->owner->refreshme = 1; +} + +static +void +drawrefresh(Memimage*, Rectangle r, void *v) +{ + Refx *x; + DImage *d; + Client *c; + Refresh *ref; + + if(v == 0) + return; + x = v; + c = x->client; + d = x->dimage; + for(ref=c->refresh; ref; ref=ref->next) + if(ref->dimage == d){ + combinerect(&ref->r, r); + return; + } + ref = malloc(sizeof(Refresh)); + if(ref){ + ref->dimage = d; + ref->r = r; + ref->next = c->refresh; + c->refresh = ref; + } +} + +static void +addflush(Rectangle r) +{ + int abb, ar, anbb; + Rectangle nbb; + + if(sdraw.softscreen==0 || !rectclip(&r, screenimage->r)) + return; + + if(flushrect.min.x >= flushrect.max.x){ + flushrect = r; + waste = 0; + return; + } + nbb = flushrect; + combinerect(&nbb, r); + ar = Dx(r)*Dy(r); + abb = Dx(flushrect)*Dy(flushrect); + anbb = Dx(nbb)*Dy(nbb); + /* + * Area of new waste is area of new bb minus area of old bb, + * less the area of the new segment, which we assume is not waste. + * This could be negative, but that's OK. + */ + waste += anbb-abb - ar; + if(waste < 0) + waste = 0; + /* + * absorb if: + * total area is small + * waste is less than half total area + * rectangles touch + */ + if(anbb<=1024 || waste*2<anbb || rectXrect(flushrect, r)){ + flushrect = nbb; + return; + } + /* emit current state */ + if(flushrect.min.x < flushrect.max.x) + flushmemscreen(flushrect); + flushrect = r; + waste = 0; +} + +static +void +dstflush(Memimage *dst, Rectangle r) +{ + Memlayer *l; + + if(dst == screenimage){ + combinerect(&flushrect, r); + return; + } + l = dst->layer; + if(l == nil) + return; + do{ + if(l->screen->image->data != screenimage->data) + return; + r = rectaddpt(r, l->delta); + l = l->screen->image->layer; + }while(l); + addflush(r); +} + +void +drawflush(void) +{ + if(flushrect.min.x < flushrect.max.x) + flushmemscreen(flushrect); + flushrect = Rect(10000, 10000, -10000, -10000); +} + +static +int +drawcmp(char *a, char *b, int n) +{ + if(strlen(a) != n) + return 1; + return memcmp(a, b, n); +} + +DName* +drawlookupname(int n, char *str) +{ + DName *name, *ename; + + name = sdraw.name; + ename = &name[sdraw.nname]; + for(; name<ename; name++) + if(drawcmp(name->name, str, n) == 0) + return name; + return 0; +} + +int +drawgoodname(DImage *d) +{ + DName *n; + + /* if window, validate the screen's own images */ + if(d->dscreen) + if(drawgoodname(d->dscreen->dimage) == 0 + || drawgoodname(d->dscreen->dfill) == 0) + return 0; + if(d->name == nil) + return 1; + n = drawlookupname(strlen(d->name), d->name); + if(n==nil || n->vers!=d->vers) + return 0; + return 1; +} + +DImage* +drawlookup(Client *client, int id, int checkname) +{ + DImage *d; + + d = client->dimage[id&HASHMASK]; + while(d){ + if(d->id == id){ + if(checkname && !drawgoodname(d)) + error(Eoldname); + return d; + } + d = d->next; + } + return 0; +} + +DScreen* +drawlookupdscreen(int id) +{ + DScreen *s; + + s = dscreen; + while(s){ + if(s->id == id) + return s; + s = s->next; + } + return 0; +} + +DScreen* +drawlookupscreen(Client *client, int id, CScreen **cs) +{ + CScreen *s; + + s = client->cscreen; + while(s){ + if(s->dscreen->id == id){ + *cs = s; + return s->dscreen; + } + s = s->next; + } + error(Enodrawscreen); + return 0; +} + +Memimage* +drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen) +{ + DImage *d; + + d = malloc(sizeof(DImage)); + if(d == 0) + return 0; + d->id = id; + d->ref = 1; + d->name = 0; + d->vers = 0; + d->image = i; + d->nfchar = 0; + d->fchar = 0; + d->fromname = 0; + d->dscreen = dscreen; + d->next = client->dimage[id&HASHMASK]; + client->dimage[id&HASHMASK] = d; + return i; +} + +Memscreen* +drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *dfill, int public) +{ + Memscreen *s; + CScreen *c; + + c = malloc(sizeof(CScreen)); + if(dimage && dimage->image && dimage->image->chan == 0) + panic("bad image %p in drawinstallscreen", dimage->image); + + if(c == 0) + return 0; + if(d == 0){ + d = malloc(sizeof(DScreen)); + if(d == 0){ + free(c); + return 0; + } + s = malloc(sizeof(Memscreen)); + if(s == 0){ + free(c); + free(d); + return 0; + } + s->frontmost = 0; + s->rearmost = 0; + d->dimage = dimage; + if(dimage){ + s->image = dimage->image; + dimage->ref++; + } + d->dfill = dfill; + if(dfill){ + s->fill = dfill->image; + dfill->ref++; + } + d->ref = 0; + d->id = id; + d->screen = s; + d->public = public; + d->next = dscreen; + d->owner = client; + dscreen = d; + } + c->dscreen = d; + d->ref++; + c->next = client->cscreen; + client->cscreen = c; + return d->screen; +} + +void +drawdelname(DName *name) +{ + int i; + + i = name-sdraw.name; + memmove(name, name+1, (sdraw.nname-(i+1))*sizeof(DName)); + sdraw.nname--; +} + +void +drawfreedscreen(DScreen *this) +{ + DScreen *ds, *next; + + this->ref--; + if(this->ref < 0) + print("negative ref in drawfreedscreen\n"); + if(this->ref > 0) + return; + ds = dscreen; + if(ds == this){ + dscreen = this->next; + goto Found; + } + while(next = ds->next){ /* assign = */ + if(next == this){ + ds->next = this->next; + goto Found; + } + ds = next; + } + error(Enodrawimage); + + Found: + if(this->dimage) + drawfreedimage(this->dimage); + if(this->dfill) + drawfreedimage(this->dfill); + free(this->screen); + free(this); +} + +void +drawfreedimage(DImage *dimage) +{ + int i; + Memimage *l; + DScreen *ds; + + dimage->ref--; + if(dimage->ref < 0) + print("negative ref in drawfreedimage\n"); + if(dimage->ref > 0) + return; + + /* any names? */ + for(i=0; i<sdraw.nname; ) + if(sdraw.name[i].dimage == dimage) + drawdelname(sdraw.name+i); + else + i++; + if(dimage->fromname){ /* acquired by name; owned by someone else*/ + drawfreedimage(dimage->fromname); + goto Return; + } + if(dimage->image == screenimage) /* don't free the display */ + goto Return; + ds = dimage->dscreen; + if(ds){ + l = dimage->image; + if(l->data == screenimage->data) + dstflush(l->layer->screen->image, l->layer->screenr); + if(l->layer->refreshfn == drawrefresh) /* else true owner will clean up */ + free(l->layer->refreshptr); + l->layer->refreshptr = nil; + if(drawgoodname(dimage)) + memldelete(l); + else + memlfree(l); + drawfreedscreen(ds); + }else + freememimage(dimage->image); + Return: + free(dimage->fchar); + free(dimage); +} + +void +drawuninstallscreen(Client *client, CScreen *this) +{ + CScreen *cs, *next; + + cs = client->cscreen; + if(cs == this){ + client->cscreen = this->next; + drawfreedscreen(this->dscreen); + free(this); + return; + } + while(next = cs->next){ /* assign = */ + if(next == this){ + cs->next = this->next; + drawfreedscreen(this->dscreen); + free(this); + return; + } + cs = next; + } +} + +void +drawuninstall(Client *client, int id) +{ + DImage *d, *next; + + d = client->dimage[id&HASHMASK]; + if(d == 0) + error(Enodrawimage); + if(d->id == id){ + client->dimage[id&HASHMASK] = d->next; + drawfreedimage(d); + return; + } + while(next = d->next){ /* assign = */ + if(next->id == id){ + d->next = next->next; + drawfreedimage(next); + return; + } + d = next; + } + error(Enodrawimage); +} + +void +drawaddname(Client *client, DImage *di, int n, char *str) +{ + DName *name, *ename, *new, *t; + + name = sdraw.name; + ename = &name[sdraw.nname]; + for(; name<ename; name++) + if(drawcmp(name->name, str, n) == 0) + error(Enameused); + t = smalloc((sdraw.nname+1)*sizeof(DName)); + memmove(t, sdraw.name, sdraw.nname*sizeof(DName)); + free(sdraw.name); + sdraw.name = t; + new = &sdraw.name[sdraw.nname++]; + new->name = smalloc(n+1); + memmove(new->name, str, n); + new->name[n] = 0; + new->dimage = di; + new->client = client; + new->vers = ++sdraw.vers; +} + +Client* +drawnewclient(void) +{ + Client *cl, **cp; + int i; + + for(i=0; i<sdraw.nclient; i++){ + cl = sdraw.client[i]; + if(cl == 0) + break; + } + if(i == sdraw.nclient){ + cp = malloc((sdraw.nclient+1)*sizeof(Client*)); + if(cp == 0) + return 0; + memmove(cp, sdraw.client, sdraw.nclient*sizeof(Client*)); + free(sdraw.client); + sdraw.client = cp; + sdraw.nclient++; + cp[i] = 0; + } + cl = malloc(sizeof(Client)); + if(cl == 0) + return 0; + memset(cl, 0, sizeof(Client)); + cl->slot = i; + cl->clientid = ++sdraw.clientid; + cl->op = SoverD; + sdraw.client[i] = cl; + return cl; +} + +static int +drawclientop(Client *cl) +{ + int op; + + op = cl->op; + cl->op = SoverD; + return op; +} + +int +drawhasclients(void) +{ + /* + * if draw has ever been used, we can't resize the frame buffer, + * even if all clients have exited (nclients is cumulative); it's too + * hard to make work. + */ + return sdraw.nclient != 0; +} + +Client* +drawclientofpath(ulong path) +{ + Client *cl; + int slot; + + slot = CLIENTPATH(path); + if(slot == 0) + return nil; + cl = sdraw.client[slot-1]; + if(cl==0 || cl->clientid==0) + return nil; + return cl; +} + + +Client* +drawclient(Chan *c) +{ + Client *client; + + client = drawclientofpath(c->qid.path); + if(client == nil) + error(Enoclient); + return client; +} + +Memimage* +drawimage(Client *client, uchar *a) +{ + DImage *d; + + d = drawlookup(client, BGLONG(a), 1); + if(d == nil) + error(Enodrawimage); + return d->image; +} + +void +drawrectangle(Rectangle *r, uchar *a) +{ + r->min.x = BGLONG(a+0*4); + r->min.y = BGLONG(a+1*4); + r->max.x = BGLONG(a+2*4); + r->max.y = BGLONG(a+3*4); +} + +void +drawpoint(Point *p, uchar *a) +{ + p->x = BGLONG(a+0*4); + p->y = BGLONG(a+1*4); +} + +Point +drawchar(Memimage *dst, Point p, Memimage *src, Point *sp, DImage *font, int index, int op) +{ + FChar *fc; + Rectangle r; + Point sp1; + + fc = &font->fchar[index]; + r.min.x = p.x+fc->left; + r.min.y = p.y-(font->ascent-fc->miny); + r.max.x = r.min.x+(fc->maxx-fc->minx); + r.max.y = r.min.y+(fc->maxy-fc->miny); + sp1.x = sp->x+fc->left; + sp1.y = sp->y+fc->miny; + memdraw(dst, r, src, sp1, font->image, Pt(fc->minx, fc->miny), op); + p.x += fc->width; + sp->x += fc->width; + return p; +} + +static int +initscreenimage(void) +{ + int width, depth; + ulong chan; + Rectangle r; + + if(screenimage != nil) + return 1; + + screendata.base = nil; + screendata.bdata = attachscreen(&r, &chan, &depth, &width, &sdraw.softscreen); + if(screendata.bdata == nil) + return 0; + screendata.ref = 1; + + screenimage = allocmemimaged(r, chan, &screendata); + if(screenimage == nil){ + /* RSC: BUG: detach screen */ + return 0; + } + + screenimage->width = width; + screenimage->clipr = r; + return 1; +} + +void +deletescreenimage(void) +{ + qlock(&sdraw); + /* RSC: BUG: detach screen */ + if(screenimage) + freememimage(screenimage); + screenimage = nil; + qunlock(&sdraw); +} + +static Chan* +drawattach(char *spec) +{ + qlock(&sdraw); + if(!initscreenimage()){ + qunlock(&sdraw); + error("no frame buffer"); + } + qunlock(&sdraw); + return devattach('i', spec); +} + +static Walkqid* +drawwalk(Chan *c, Chan *nc, char **name, int nname) +{ + if(screendata.bdata == nil) + error("no frame buffer"); + return devwalk(c, nc, name, nname, 0, 0, drawgen); +} + +static int +drawstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, drawgen); +} + +static Chan* +drawopen(Chan *c, int omode) +{ + Client *cl; + + if(c->qid.type & QTDIR) + return devopen(c, omode, 0, 0, drawgen); + + qlock(&sdraw); + if(waserror()){ + qunlock(&sdraw); + nexterror(); + } + + if(QID(c->qid) == Qnew){ + cl = drawnewclient(); + if(cl == 0) + error(Enodev); + c->qid.path = Qctl|((cl->slot+1)<<QSHIFT); + } + + switch(QID(c->qid)){ + case Qnew: + break; + + case Qctl: + cl = drawclient(c); + if(cl->busy) + error(Einuse); + cl->busy = 1; + flushrect = Rect(10000, 10000, -10000, -10000); + drawinstall(cl, 0, screenimage, 0); + incref(&cl->r); + break; + case Qcolormap: + case Qdata: + case Qrefresh: + cl = drawclient(c); + incref(&cl->r); + break; + } + qunlock(&sdraw); + poperror(); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + c->iounit = IOUNIT; + return c; +} + +static void +drawclose(Chan *c) +{ + int i; + DImage *d, **dp; + Client *cl; + Refresh *r; + + if(QID(c->qid) < Qcolormap) /* Qtopdir, Qnew, Q3rd, Q2nd have no client */ + return; + qlock(&sdraw); + if(waserror()){ + qunlock(&sdraw); + nexterror(); + } + + cl = drawclient(c); + if(QID(c->qid) == Qctl) + cl->busy = 0; + if((c->flag&COPEN) && (decref(&cl->r)==0)){ + while(r = cl->refresh){ /* assign = */ + cl->refresh = r->next; + free(r); + } + /* free names */ + for(i=0; i<sdraw.nname; ) + if(sdraw.name[i].client == cl) + drawdelname(sdraw.name+i); + else + i++; + while(cl->cscreen) + drawuninstallscreen(cl, cl->cscreen); + /* all screens are freed, so now we can free images */ + dp = cl->dimage; + for(i=0; i<NHASH; i++){ + while((d = *dp) != nil){ + *dp = d->next; + drawfreedimage(d); + } + dp++; + } + sdraw.client[cl->slot] = 0; + drawflush(); /* to erase visible, now dead windows */ + free(cl); + } + qunlock(&sdraw); + poperror(); +} + +long +drawread(Chan *c, void *a, long n, vlong off) +{ + int index, m; + ulong red, green, blue; + Client *cl; + uchar *p; + Refresh *r; + DImage *di; + Memimage *i; + ulong offset = off; + char buf[16]; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, drawgen); + cl = drawclient(c); + qlock(&sdraw); + if(waserror()){ + qunlock(&sdraw); + nexterror(); + } + switch(QID(c->qid)){ + case Qctl: + if(n < 12*12) + error(Eshortread); + if(cl->infoid < 0) + error(Enodrawimage); + if(cl->infoid == 0){ + i = screenimage; + if(i == nil) + error(Enodrawimage); + }else{ + di = drawlookup(cl, cl->infoid, 1); + if(di == nil) + error(Enodrawimage); + i = di->image; + } + n = sprint(a, "%11d %11d %11s %11d %11d %11d %11d %11d %11d %11d %11d %11d ", + cl->clientid, cl->infoid, chantostr(buf, i->chan), (i->flags&Frepl)==Frepl, + i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y, + i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y); + cl->infoid = -1; + break; + + case Qcolormap: + drawactive(1); /* to restore map from backup */ + p = malloc(4*12*256+1); + if(p == 0) + error(Enomem); + m = 0; + for(index = 0; index < 256; index++){ + getcolor(index, &red, &green, &blue); + m += sprint((char*)p+m, "%11d %11lud %11lud %11lud\n", index, red>>24, green>>24, blue>>24); + } + n = readstr(offset, a, n, (char*)p); + free(p); + break; + + case Qdata: + if(cl->readdata == nil) + error("no draw data"); + if(n < cl->nreaddata) + error(Eshortread); + n = cl->nreaddata; + memmove(a, cl->readdata, cl->nreaddata); + free(cl->readdata); + cl->readdata = nil; + break; + + case Qrefresh: + if(n < 5*4) + error(Ebadarg); + for(;;){ + if(cl->refreshme || cl->refresh) + break; + qunlock(&sdraw); + if(waserror()){ + qlock(&sdraw); /* restore lock for waserror() above */ + nexterror(); + } + sleep(&cl->refrend, drawrefactive, cl); + poperror(); + qlock(&sdraw); + } + p = a; + while(cl->refresh && n>=5*4){ + r = cl->refresh; + BPLONG(p+0*4, r->dimage->id); + BPLONG(p+1*4, r->r.min.x); + BPLONG(p+2*4, r->r.min.y); + BPLONG(p+3*4, r->r.max.x); + BPLONG(p+4*4, r->r.max.y); + cl->refresh = r->next; + free(r); + p += 5*4; + n -= 5*4; + } + cl->refreshme = 0; + n = p-(uchar*)a; + } + qunlock(&sdraw); + poperror(); + return n; +} + +void +drawwakeall(void) +{ + Client *cl; + int i; + + for(i=0; i<sdraw.nclient; i++){ + cl = sdraw.client[i]; + if(cl && (cl->refreshme || cl->refresh)) + wakeup(&cl->refrend); + } +} + +static long +drawwrite(Chan *c, void *a, long n, vlong) +{ + char buf[128], *fields[4], *q; + Client *cl; + int i, m, red, green, blue, x; + + if(c->qid.type & QTDIR) + error(Eisdir); + cl = drawclient(c); + qlock(&sdraw); + if(waserror()){ + drawwakeall(); + qunlock(&sdraw); + nexterror(); + } + switch(QID(c->qid)){ + case Qctl: + if(n != 4) + error("unknown draw control request"); + cl->infoid = BGLONG((uchar*)a); + break; + + case Qcolormap: + drawactive(1); /* to restore map from backup */ + m = n; + n = 0; + while(m > 0){ + x = m; + if(x > sizeof(buf)-1) + x = sizeof(buf)-1; + q = memccpy(buf, a, '\n', x); + if(q == 0) + break; + i = q-buf; + n += i; + a = (char*)a + i; + m -= i; + *q = 0; + if(tokenize(buf, fields, nelem(fields)) != 4) + error(Ebadarg); + i = strtoul(fields[0], 0, 0); + red = strtoul(fields[1], 0, 0); + green = strtoul(fields[2], 0, 0); + blue = strtoul(fields[3], &q, 0); + if(fields[3] == q) + error(Ebadarg); + if(red>255 || green>255 || blue>255 || i<0 || i>255) + error(Ebadarg); + red |= red<<8; + red |= red<<16; + green |= green<<8; + green |= green<<16; + blue |= blue<<8; + blue |= blue<<16; + setcolor(i, red, green, blue); + } + break; + + case Qdata: + drawmesg(cl, a, n); + drawwakeall(); + break; + + default: + error(Ebadusefd); + } + qunlock(&sdraw); + poperror(); + return n; +} + +uchar* +drawcoord(uchar *p, uchar *maxp, int oldx, int *newx) +{ + int b, x; + + if(p >= maxp) + error(Eshortdraw); + b = *p++; + x = b & 0x7F; + if(b & 0x80){ + if(p+1 >= maxp) + error(Eshortdraw); + x |= *p++ << 7; + x |= *p++ << 15; + if(x & (1<<22)) + x |= ~0<<23; + }else{ + if(b & 0x40) + x |= ~0<<7; + x += oldx; + } + *newx = x; + return p; +} + +static void +printmesg(char *fmt, uchar *a, int plsprnt) +{ + char buf[256]; + char *p, *q; + int s; + + if(1|| plsprnt==0){ + SET(s,q,p); + USED(fmt, a, buf, p, q, s); + return; + } + q = buf; + *q++ = *a++; + for(p=fmt; *p; p++){ + switch(*p){ + case 'l': + q += sprint(q, " %ld", (long)BGLONG(a)); + a += 4; + break; + case 'L': + q += sprint(q, " %.8lux", (ulong)BGLONG(a)); + a += 4; + break; + case 'R': + q += sprint(q, " [%d %d %d %d]", BGLONG(a), BGLONG(a+4), BGLONG(a+8), BGLONG(a+12)); + a += 16; + break; + case 'P': + q += sprint(q, " [%d %d]", BGLONG(a), BGLONG(a+4)); + a += 8; + break; + case 'b': + q += sprint(q, " %d", *a++); + break; + case 's': + q += sprint(q, " %d", BGSHORT(a)); + a += 2; + break; + case 'S': + q += sprint(q, " %.4ux", BGSHORT(a)); + a += 2; + break; + } + } + *q++ = '\n'; + *q = 0; + iprint("%.*s", (int)(q-buf), buf); +} + +void +drawmesg(Client *client, void *av, int n) +{ + int c, op, repl, m, y, dstid, scrnid, ni, ci, j, nw, e0, e1, ox, oy, esize, oesize, doflush; + uchar *u, *a, refresh; + char *fmt; + ulong value, chan; + Rectangle r, clipr; + Point p, q, *pp, sp; + Memimage *i, *dst, *src, *mask; + Memimage *l, **lp; + Memscreen *scrn; + DImage *font, *ll, *di, *ddst, *dsrc; + DName *dn; + DScreen *dscrn; + FChar *fc; + Refx *refx; + CScreen *cs; + Refreshfn reffn; + + a = av; + m = 0; + fmt = nil; + if(waserror()){ + if(fmt) printmesg(fmt, a, 1); + /* iprint("error: %s\n", up->env->errstr); */ + nexterror(); + } + while((n-=m) > 0){ + USED(fmt); + a += m; + switch(*a){ + default: + error("bad draw command"); + /* new allocate: 'b' id[4] screenid[4] refresh[1] chan[4] repl[1] R[4*4] clipR[4*4] rrggbbaa[4] */ + case 'b': + printmesg(fmt="LLbLbRRL", a, 0); + m = 1+4+4+1+4+1+4*4+4*4+4; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + scrnid = BGSHORT(a+5); + refresh = a[9]; + chan = BGLONG(a+10); + repl = a[14]; + drawrectangle(&r, a+15); + drawrectangle(&clipr, a+31); + value = BGLONG(a+47); + if(drawlookup(client, dstid, 0)) + error(Eimageexists); + if(scrnid){ + dscrn = drawlookupscreen(client, scrnid, &cs); + scrn = dscrn->screen; + if(repl || chan!=scrn->image->chan) + error("image parameters incompatible with screen"); + reffn = nil; + switch(refresh){ + case Refbackup: + break; + case Refnone: + reffn = memlnorefresh; + break; + case Refmesg: + reffn = drawrefresh; + break; + default: + error("unknown refresh method"); + } + l = memlalloc(scrn, r, reffn, 0, value); + if(l == 0) + error(Edrawmem); + dstflush(l->layer->screen->image, l->layer->screenr); + l->clipr = clipr; + rectclip(&l->clipr, r); + if(drawinstall(client, dstid, l, dscrn) == 0){ + memldelete(l); + error(Edrawmem); + } + dscrn->ref++; + if(reffn){ + refx = nil; + if(reffn == drawrefresh){ + refx = malloc(sizeof(Refx)); + if(refx == 0){ + drawuninstall(client, dstid); + error(Edrawmem); + } + refx->client = client; + refx->dimage = drawlookup(client, dstid, 1); + } + memlsetrefresh(l, reffn, refx); + } + continue; + } + i = allocmemimage(r, chan); + if(i == 0) + error(Edrawmem); + if(repl) + i->flags |= Frepl; + i->clipr = clipr; + if(!repl) + rectclip(&i->clipr, r); + if(drawinstall(client, dstid, i, 0) == 0){ + freememimage(i); + error(Edrawmem); + } + memfillcolor(i, value); + continue; + + /* allocate screen: 'A' id[4] imageid[4] fillid[4] public[1] */ + case 'A': + printmesg(fmt="LLLb", a, 1); + m = 1+4+4+4+1; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + if(dstid == 0) + error(Ebadarg); + if(drawlookupdscreen(dstid)) + error(Escreenexists); + ddst = drawlookup(client, BGLONG(a+5), 1); + dsrc = drawlookup(client, BGLONG(a+9), 1); + if(ddst==0 || dsrc==0) + error(Enodrawimage); + if(drawinstallscreen(client, 0, dstid, ddst, dsrc, a[13]) == 0) + error(Edrawmem); + continue; + + /* set repl and clip: 'c' dstid[4] repl[1] clipR[4*4] */ + case 'c': + printmesg(fmt="LbR", a, 0); + m = 1+4+1+4*4; + if(n < m) + error(Eshortdraw); + ddst = drawlookup(client, BGLONG(a+1), 1); + if(ddst == nil) + error(Enodrawimage); + if(ddst->name) + error("cannot change repl/clipr of shared image"); + dst = ddst->image; + if(a[5]) + dst->flags |= Frepl; + drawrectangle(&dst->clipr, a+6); + continue; + + /* draw: 'd' dstid[4] srcid[4] maskid[4] R[4*4] P[2*4] P[2*4] */ + case 'd': + printmesg(fmt="LLLRPP", a, 0); + m = 1+4+4+4+4*4+2*4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + src = drawimage(client, a+5); + mask = drawimage(client, a+9); + drawrectangle(&r, a+13); + drawpoint(&p, a+29); + drawpoint(&q, a+37); + op = drawclientop(client); + memdraw(dst, r, src, p, mask, q, op); + dstflush(dst, r); + continue; + + /* toggle debugging: 'D' val[1] */ + case 'D': + printmesg(fmt="b", a, 0); + m = 1+1; + if(n < m) + error(Eshortdraw); + drawdebug = a[1]; + continue; + + /* ellipse: 'e' dstid[4] srcid[4] center[2*4] a[4] b[4] thick[4] sp[2*4] alpha[4] phi[4]*/ + case 'e': + case 'E': + printmesg(fmt="LLPlllPll", a, 0); + m = 1+4+4+2*4+4+4+4+2*4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + src = drawimage(client, a+5); + drawpoint(&p, a+9); + e0 = BGLONG(a+17); + e1 = BGLONG(a+21); + if(e0<0 || e1<0) + error("invalid ellipse semidiameter"); + j = BGLONG(a+25); + if(j < 0) + error("negative ellipse thickness"); + drawpoint(&sp, a+29); + c = j; + if(*a == 'E') + c = -1; + ox = BGLONG(a+37); + oy = BGLONG(a+41); + op = drawclientop(client); + /* high bit indicates arc angles are present */ + if(ox & (1<<31)){ + if((ox & (1<<30)) == 0) + ox &= ~(1<<31); + memarc(dst, p, e0, e1, c, src, sp, ox, oy, op); + }else + memellipse(dst, p, e0, e1, c, src, sp, op); + dstflush(dst, Rect(p.x-e0-j, p.y-e1-j, p.x+e0+j+1, p.y+e1+j+1)); + continue; + + /* free: 'f' id[4] */ + case 'f': + printmesg(fmt="L", a, 1); + m = 1+4; + if(n < m) + error(Eshortdraw); + ll = drawlookup(client, BGLONG(a+1), 0); + if(ll && ll->dscreen && ll->dscreen->owner != client) + ll->dscreen->owner->refreshme = 1; + drawuninstall(client, BGLONG(a+1)); + continue; + + /* free screen: 'F' id[4] */ + case 'F': + printmesg(fmt="L", a, 1); + m = 1+4; + if(n < m) + error(Eshortdraw); + drawlookupscreen(client, BGLONG(a+1), &cs); + drawuninstallscreen(client, cs); + continue; + + /* initialize font: 'i' fontid[4] nchars[4] ascent[1] */ + case 'i': + printmesg(fmt="Llb", a, 1); + m = 1+4+4+1; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + dst = drawimage(client, a+1); + if(dstid == 0 || dst == screenimage) + error("cannot use display as font"); + font = drawlookup(client, dstid, 1); + if(font == 0) + error(Enodrawimage); + if(font->image->layer) + error("cannot use window as font"); + ni = BGLONG(a+5); + if(ni<=0 || ni>4096) + error("bad font size (4096 chars max)"); + free(font->fchar); /* should we complain if non-zero? */ + font->fchar = malloc(ni*sizeof(FChar)); + if(font->fchar == 0) + error(Enomem); + memset(font->fchar, 0, ni*sizeof(FChar)); + font->nfchar = ni; + font->ascent = a[9]; + continue; + + /* load character: 'l' fontid[4] srcid[4] index[2] R[4*4] P[2*4] left[1] width[1] */ + case 'l': + printmesg(fmt="LLSRPbb", a, 0); + m = 1+4+4+2+4*4+2*4+1+1; + if(n < m) + error(Eshortdraw); + font = drawlookup(client, BGLONG(a+1), 1); + if(font == 0) + error(Enodrawimage); + if(font->nfchar == 0) + error(Enotfont); + src = drawimage(client, a+5); + ci = BGSHORT(a+9); + if(ci >= font->nfchar) + error(Eindex); + drawrectangle(&r, a+11); + drawpoint(&p, a+27); + memdraw(font->image, r, src, p, memopaque, p, S); + fc = &font->fchar[ci]; + fc->minx = r.min.x; + fc->maxx = r.max.x; + fc->miny = r.min.y; + fc->maxy = r.max.y; + fc->left = a[35]; + fc->width = a[36]; + continue; + + /* draw line: 'L' dstid[4] p0[2*4] p1[2*4] end0[4] end1[4] radius[4] srcid[4] sp[2*4] */ + case 'L': + printmesg(fmt="LPPlllLP", a, 0); + m = 1+4+2*4+2*4+4+4+4+4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + drawpoint(&p, a+5); + drawpoint(&q, a+13); + e0 = BGLONG(a+21); + e1 = BGLONG(a+25); + j = BGLONG(a+29); + if(j < 0) + error("negative line width"); + src = drawimage(client, a+33); + drawpoint(&sp, a+37); + op = drawclientop(client); + memline(dst, p, q, e0, e1, j, src, sp, op); + /* avoid memlinebbox if possible */ + if(dst == screenimage || dst->layer!=nil){ + /* BUG: this is terribly inefficient: update maximal containing rect*/ + r = memlinebbox(p, q, e0, e1, j); + dstflush(dst, insetrect(r, -(1+1+j))); + } + continue; + + /* create image mask: 'm' newid[4] id[4] */ +/* + * + case 'm': + printmesg("LL", a, 0); + m = 4+4; + if(n < m) + error(Eshortdraw); + break; + * + */ + + /* attach to a named image: 'n' dstid[4] j[1] name[j] */ + case 'n': + printmesg(fmt="Lz", a, 0); + m = 1+4+1; + if(n < m) + error(Eshortdraw); + j = a[5]; + if(j == 0) /* give me a non-empty name please */ + error(Eshortdraw); + m += j; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + if(drawlookup(client, dstid, 0)) + error(Eimageexists); + dn = drawlookupname(j, (char*)a+6); + if(dn == nil) + error(Enoname); + if(drawinstall(client, dstid, dn->dimage->image, 0) == 0) + error(Edrawmem); + di = drawlookup(client, dstid, 0); + if(di == 0) + error("draw: cannot happen"); + di->vers = dn->vers; + di->name = smalloc(j+1); + di->fromname = dn->dimage; + di->fromname->ref++; + memmove(di->name, a+6, j); + di->name[j] = 0; + client->infoid = dstid; + continue; + + /* name an image: 'N' dstid[4] in[1] j[1] name[j] */ + case 'N': + printmesg(fmt="Lbz", a, 0); + m = 1+4+1+1; + if(n < m) + error(Eshortdraw); + c = a[5]; + j = a[6]; + if(j == 0) /* give me a non-empty name please */ + error(Eshortdraw); + m += j; + if(n < m) + error(Eshortdraw); + di = drawlookup(client, BGLONG(a+1), 0); + if(di == 0) + error(Enodrawimage); + if(di->name) + error(Enamed); + if(c) + drawaddname(client, di, j, (char*)a+7); + else{ + dn = drawlookupname(j, (char*)a+7); + if(dn == nil) + error(Enoname); + if(dn->dimage != di) + error(Ewrongname); + drawdelname(dn); + } + continue; + + /* position window: 'o' id[4] r.min [2*4] screenr.min [2*4] */ + case 'o': + printmesg(fmt="LPP", a, 0); + m = 1+4+2*4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + if(dst->layer){ + drawpoint(&p, a+5); + drawpoint(&q, a+13); + r = dst->layer->screenr; + ni = memlorigin(dst, p, q); + if(ni < 0) + error("image origin failed"); + if(ni > 0){ + dstflush(dst->layer->screen->image, r); + dstflush(dst->layer->screen->image, dst->layer->screenr); + ll = drawlookup(client, BGLONG(a+1), 1); + drawrefreshscreen(ll, client); + } + } + continue; + + /* set compositing operator for next draw operation: 'O' op */ + case 'O': + printmesg(fmt="b", a, 0); + m = 1+1; + if(n < m) + error(Eshortdraw); + client->op = a[1]; + continue; + + /* filled polygon: 'P' dstid[4] n[2] wind[4] ignore[2*4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */ + /* polygon: 'p' dstid[4] n[2] end0[4] end1[4] radius[4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */ + case 'p': + case 'P': + printmesg(fmt="LslllLPP", a, 0); + m = 1+4+2+4+4+4+4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + ni = BGSHORT(a+5); + if(ni < 0) + error("negative count in polygon"); + e0 = BGLONG(a+7); + e1 = BGLONG(a+11); + j = 0; + if(*a == 'p'){ + j = BGLONG(a+15); + if(j < 0) + error("negative polygon line width"); + } + src = drawimage(client, a+19); + drawpoint(&sp, a+23); + drawpoint(&p, a+31); + ni++; + pp = malloc(ni*sizeof(Point)); + if(pp == nil) + error(Enomem); + doflush = 0; + if(dst == screenimage || (dst->layer && dst->layer->screen->image->data == screenimage->data)) + doflush = 1; /* simplify test in loop */ + ox = oy = 0; + esize = 0; + u = a+m; + for(y=0; y<ni; y++){ + q = p; + oesize = esize; + u = drawcoord(u, a+n, ox, &p.x); + u = drawcoord(u, a+n, oy, &p.y); + ox = p.x; + oy = p.y; + if(doflush){ + esize = j; + if(*a == 'p'){ + if(y == 0){ + c = memlineendsize(e0); + if(c > esize) + esize = c; + } + if(y == ni-1){ + c = memlineendsize(e1); + if(c > esize) + esize = c; + } + } + if(*a=='P' && e0!=1 && e0 !=~0) + r = dst->clipr; + else if(y > 0){ + r = Rect(q.x-oesize, q.y-oesize, q.x+oesize+1, q.y+oesize+1); + combinerect(&r, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); + } + if(rectclip(&r, dst->clipr)) /* should perhaps be an arg to dstflush */ + dstflush(dst, r); + } + pp[y] = p; + } + if(y == 1) + dstflush(dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); + op = drawclientop(client); + if(*a == 'p') + mempoly(dst, pp, ni, e0, e1, j, src, sp, op); + else + memfillpoly(dst, pp, ni, e0, src, sp, op); + free(pp); + m = u-a; + continue; + + /* read: 'r' id[4] R[4*4] */ + case 'r': + printmesg(fmt="LR", a, 0); + m = 1+4+4*4; + if(n < m) + error(Eshortdraw); + i = drawimage(client, a+1); + drawrectangle(&r, a+5); + if(!rectinrect(r, i->r)) + error(Ereadoutside); + c = bytesperline(r, i->depth); + c *= Dy(r); + free(client->readdata); + client->readdata = mallocz(c, 0); + if(client->readdata == nil) + error("readimage malloc failed"); + client->nreaddata = memunload(i, r, client->readdata, c); + if(client->nreaddata < 0){ + free(client->readdata); + client->readdata = nil; + error("bad readimage call"); + } + continue; + + /* string: 's' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] ni*(index[2]) */ + /* stringbg: 'x' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] bgid[4] bgpt[2*4] ni*(index[2]) */ + case 's': + case 'x': + printmesg(fmt="LLLPRPs", a, 0); + m = 1+4+4+4+2*4+4*4+2*4+2; + if(*a == 'x') + m += 4+2*4; + if(n < m) + error(Eshortdraw); + + dst = drawimage(client, a+1); + src = drawimage(client, a+5); + font = drawlookup(client, BGLONG(a+9), 1); + if(font == 0) + error(Enodrawimage); + if(font->nfchar == 0) + error(Enotfont); + drawpoint(&p, a+13); + drawrectangle(&r, a+21); + drawpoint(&sp, a+37); + ni = BGSHORT(a+45); + u = a+m; + m += ni*2; + if(n < m) + error(Eshortdraw); + clipr = dst->clipr; + dst->clipr = r; + op = drawclientop(client); + if(*a == 'x'){ + /* paint background */ + l = drawimage(client, a+47); + drawpoint(&q, a+51); + r.min.x = p.x; + r.min.y = p.y-font->ascent; + r.max.x = p.x; + r.max.y = r.min.y+Dy(font->image->r); + j = ni; + while(--j >= 0){ + ci = BGSHORT(u); + if(ci<0 || ci>=font->nfchar){ + dst->clipr = clipr; + error(Eindex); + } + r.max.x += font->fchar[ci].width; + u += 2; + } + memdraw(dst, r, l, q, memopaque, ZP, op); + u -= 2*ni; + } + q = p; + while(--ni >= 0){ + ci = BGSHORT(u); + if(ci<0 || ci>=font->nfchar){ + dst->clipr = clipr; + error(Eindex); + } + q = drawchar(dst, q, src, &sp, font, ci, op); + u += 2; + } + dst->clipr = clipr; + p.y -= font->ascent; + dstflush(dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r))); + continue; + + /* use public screen: 'S' id[4] chan[4] */ + case 'S': + printmesg(fmt="Ll", a, 0); + m = 1+4+4; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + if(dstid == 0) + error(Ebadarg); + dscrn = drawlookupdscreen(dstid); + if(dscrn==0 || (dscrn->public==0 && dscrn->owner!=client)) + error(Enodrawscreen); + if(dscrn->screen->image->chan != BGLONG(a+5)) + error("inconsistent chan"); + if(drawinstallscreen(client, dscrn, 0, 0, 0, 0) == 0) + error(Edrawmem); + continue; + + /* top or bottom windows: 't' top[1] nw[2] n*id[4] */ + case 't': + printmesg(fmt="bsL", a, 0); + m = 1+1+2; + if(n < m) + error(Eshortdraw); + nw = BGSHORT(a+2); + if(nw < 0) + error(Ebadarg); + if(nw == 0) + continue; + m += nw*4; + if(n < m) + error(Eshortdraw); + lp = malloc(nw*sizeof(Memimage*)); + if(lp == 0) + error(Enomem); + if(waserror()){ + free(lp); + nexterror(); + } + for(j=0; j<nw; j++) + lp[j] = drawimage(client, a+1+1+2+j*4); + if(lp[0]->layer == 0) + error("images are not windows"); + for(j=1; j<nw; j++) + if(lp[j]->layer->screen != lp[0]->layer->screen) + error("images not on same screen"); + if(a[1]) + memltofrontn(lp, nw); + else + memltorearn(lp, nw); + if(lp[0]->layer->screen->image->data == screenimage->data) + for(j=0; j<nw; j++) + dstflush(lp[j]->layer->screen->image, lp[j]->layer->screenr); + ll = drawlookup(client, BGLONG(a+1+1+2), 1); + drawrefreshscreen(ll, client); + poperror(); + free(lp); + continue; + + /* visible: 'v' */ + case 'v': + printmesg(fmt="", a, 0); + m = 1; + drawflush(); + continue; + + /* write: 'y' id[4] R[4*4] data[x*1] */ + /* write from compressed data: 'Y' id[4] R[4*4] data[x*1] */ + case 'y': + case 'Y': + printmesg(fmt="LR", a, 0); + // iprint("load %c\n", *a); + m = 1+4+4*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + drawrectangle(&r, a+5); + if(!rectinrect(r, dst->r)) + error(Ewriteoutside); + y = memload(dst, r, a+m, n-m, *a=='Y'); + if(y < 0) + error("bad writeimage call"); + dstflush(dst, r); + m += y; + continue; + } + } + poperror(); +} + +int +drawlsetrefresh(ulong qidpath, int id, void *reffn, void *refx) +{ + DImage *d; + Memimage *i; + Client *client; + + client = drawclientofpath(qidpath); + if(client == 0) + return 0; + d = drawlookup(client, id, 0); + if(d == nil) + return 0; + i = d->image; + if(i->layer == nil) + return 0; + return memlsetrefresh(i, reffn, refx); +} + +Dev drawdevtab = { + 'i', + "draw", + + devreset, + devinit, + devshutdown, + drawattach, + drawwalk, + drawstat, + drawopen, + devcreate, + drawclose, + drawread, + devbread, + drawwrite, + devbwrite, + devremove, + devwstat, +}; + +/* + * On 8 bit displays, load the default color map + */ +void +drawcmap(void) +{ + int r, g, b, cr, cg, cb, v; + int num, den; + int i, j; + + drawactive(1); /* to restore map from backup */ + for(r=0,i=0; r!=4; r++) + for(v=0; v!=4; v++,i+=16){ + for(g=0,j=v-r; g!=4; g++) + for(b=0;b!=4;b++,j++){ + den = r; + if(g > den) + den = g; + if(b > den) + den = b; + if(den == 0) /* divide check -- pick grey shades */ + cr = cg = cb = v*17; + else{ + num = 17*(4*den+v); + cr = r*num/den; + cg = g*num/den; + cb = b*num/den; + } + setcolor(i+(j&15), + cr*0x01010101, cg*0x01010101, cb*0x01010101); + } + } +} + +void +drawblankscreen(int blank) +{ + int i, nc; + ulong *p; + + if(blank == sdraw.blanked) + return; + if(!canqlock(&sdraw)) + return; + if(!initscreenimage()){ + qunlock(&sdraw); + return; + } + p = sdraw.savemap; + nc = screenimage->depth > 8 ? 256 : 1<<screenimage->depth; + + /* + * blankscreen uses the hardware to blank the screen + * when possible. to help in cases when it is not possible, + * we set the color map to be all black. + */ + if(blank == 0){ /* turn screen on */ + for(i=0; i<nc; i++, p+=3) + setcolor(i, p[0], p[1], p[2]); + blankscreen(0); + }else{ /* turn screen off */ + blankscreen(1); + for(i=0; i<nc; i++, p+=3){ + getcolor(i, &p[0], &p[1], &p[2]); + setcolor(i, 0, 0, 0); + } + } + sdraw.blanked = blank; + qunlock(&sdraw); +} + +/* + * record activity on screen, changing blanking as appropriate + */ +void +drawactive(int active) +{ + if(active){ + drawblankscreen(0); + sdraw.blanktime = 0; + }else{ + if(blanktime && TK2SEC(sdraw.blanktime)/60 >= blanktime) + drawblankscreen(1); + else + sdraw.blanktime++; + } +} + +void +interf(void) +{ + /* force it to load */ + drawreplxy(0, 0, 0); +} + +int +drawidletime(void) +{ + return TK2SEC(MACHP(0)->ticks - sdraw.blanktime)/60; +} diff --git a/os/port/devds.c b/os/port/devds.c new file mode 100644 index 00000000..6be12007 --- /dev/null +++ b/os/port/devds.c @@ -0,0 +1,605 @@ +/* + * (file system) device subsystems + * '#k'. + * Follows device config in Ken's file server. + * Builds mirrors, device cats, interleaving, and partition of devices out of + * other (inner) devices. + * + * This code is from Plan 9, and subject to the Lucent Public License 1.02. + * Only the name changed for Inferno (name clash). + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" + +enum { + Fmirror, // mirror of others + Fcat, // catenation of others + Finter, // interleaving of others + Fpart, // part of others + + Blksize = 8*1024, // for Finter only + Maxconf = 1024, // max length for config + + Nfsdevs = 64, + Ndevs = 8, + + Qtop = 0, // top dir (contains "ds") + Qdir = 1, // actual dir + Qctl = 2, // ctl file + Qfirst = 3, // first fs file +}; + +#define Cfgstr "fsdev:\n" + +typedef struct Fsdev Fsdev; + +struct Fsdev +{ + int type; + char *name; // name for this fsdev + vlong start; // start address (for Fpart) + vlong size; // min(idev sizes) + int ndevs; // number of inner devices + char *iname[Ndevs]; // inner device names + Chan *idev[Ndevs]; // inner devices + vlong isize[Ndevs]; // sizes for inneer devices +}; + +/* + * Once configured, a fsdev is never removed. The name of those + * configured is never nil. We have no locks here. + */ +static Fsdev fsdev[Nfsdevs]; + +static Qid tqid = {Qtop, 0, QTDIR}; +static Qid dqid = {Qdir, 0, QTDIR}; +static Qid cqid = {Qctl, 0, 0}; + +static Cmdtab configs[] = { + Fmirror,"mirror", 0, + Fcat, "cat", 0, + Finter, "inter", 0, + Fpart, "part", 5, +}; + +static char confstr[Maxconf]; +static int configed; + + +static Fsdev* +path2dev(int i, int mustexist) +{ + if (i < 0 || i >= nelem(fsdev)) + error("bug: bad index in devfsdev"); + if (mustexist && fsdev[i].name == nil) + error(Enonexist); + + if (fsdev[i].name == nil) + return nil; + else + return &fsdev[i]; +} + +static Fsdev* +devalloc(void) +{ + int i; + + for (i = 0; i < nelem(fsdev); i++) + if (fsdev[i].name == nil) + break; + if (i == nelem(fsdev)) + error(Enodev); + + return &fsdev[i]; +} + +static void +setdsize(Fsdev* mp) +{ + uchar buf[128]; /* old DIRLEN plus a little should be plenty */ + int i; + Chan *mc; + Dir d; + long l; + + if (mp->type != Fpart){ + mp->start= 0; + mp->size = 0LL; + } + for (i = 0; i < mp->ndevs; i++){ + mc = mp->idev[i]; + l = devtab[mc->type]->stat(mc, buf, sizeof(buf)); + convM2D(buf, l, &d, nil); + mp->isize[i] = d.length; + switch(mp->type){ + case Fmirror: + if (mp->size == 0LL || mp->size > d.length) + mp->size = d.length; + break; + case Fcat: + mp->size += d.length; + break; + case Finter: + // truncate to multiple of Blksize + d.length = (d.length & ~(Blksize-1)); + mp->isize[i] = d.length; + mp->size += d.length; + break; + case Fpart: + // should raise errors here? + if (mp->start > d.length) + mp->start = d.length; + if (d.length < mp->start + mp->size) + mp->size = d.length - mp->start; + break; + } + } +} + +static void +mpshut(Fsdev *mp) +{ + int i; + char *nm; + + nm = mp->name; + mp->name = nil; // prevent others from using this. + if (nm) + free(nm); + for (i = 0; i < mp->ndevs; i++){ + if (mp->idev[i] != nil) + cclose(mp->idev[i]); + if (mp->iname[i]) + free(mp->iname[i]); + } + memset(mp, 0, sizeof(*mp)); +} + + +static void +mconfig(char* a, long n) // "name idev0 idev1" +{ + static QLock lck; + Cmdbuf *cb; + Cmdtab *ct; + Fsdev *mp; + int i; + char *oldc; + char *c; + vlong size, start; + + size = 0; + start = 0; + if (confstr[0] == 0) + seprint(confstr, confstr+sizeof(confstr), Cfgstr); + oldc = confstr + strlen(confstr); + qlock(&lck); + if (waserror()){ + *oldc = 0; + qunlock(&lck); + nexterror(); + } + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + c = oldc; + for (i = 0; i < cb->nf; i++) + c = seprint(c, confstr+sizeof(confstr), "%s ", cb->f[i]); + *(c-1) = '\n'; + ct = lookupcmd(cb, configs, nelem(configs)); + cb->f++; // skip command + cb->nf--; + if (ct->index == Fpart){ + size = strtoll(cb->f[3], nil, 10); + cb->nf--; + start = strtoll(cb->f[2], nil, 10); + cb->nf--; + } + for (i = 0; i < nelem(fsdev); i++) + if (fsdev[i].name != nil && strcmp(fsdev[i].name, cb->f[0])==0) + error(Eexist); + if (cb->nf - 1 > Ndevs) + error("too many devices; fix me"); + for (i = 0; i < cb->nf; i++) + validname(cb->f[i], (i != 0)); + mp = devalloc(); + if(waserror()){ + mpshut(mp); + nexterror(); + } + mp->type = ct->index; + if (mp->type == Fpart){ + mp->size = size; + mp->start = start; + } + kstrdup(&mp->name, cb->f[0]); + for (i = 1; i < cb->nf; i++){ + kstrdup(&mp->iname[i-1], cb->f[i]); + mp->idev[i-1] = namec(mp->iname[i-1], Aopen, ORDWR, 0); + if (mp->idev[i-1] == nil) + error(Egreg); + mp->ndevs++; + } + setdsize(mp); + poperror(); + free(cb); + poperror(); + poperror(); + configed = 1; + qunlock(&lck); + +} + +static void +rdconf(void) +{ + int mustrd; + char *s; + char *c; + char *p; + char *e; + Chan *cc; + + s = getconf("fsconfig"); + if (s == nil){ + mustrd = 0; + s = "/dev/sdC0/fscfg"; + } else + mustrd = 1; + if (waserror()){ + configed = 1; + if (!mustrd) + return; + nexterror(); + } + cc = namec(s, Aopen, OREAD, 0); + if(waserror()){ + cclose(cc); + nexterror(); + } + devtab[cc->type]->read(cc, confstr, sizeof(confstr), 0); + poperror(); + cclose(cc); + if (strncmp(confstr, Cfgstr, strlen(Cfgstr)) != 0) + error("config string must start with `fsdev:'"); + kstrdup(&c, confstr + strlen(Cfgstr)); + if(waserror()){ + free(c); + nexterror(); + } + memset(confstr, 0, sizeof(confstr)); + for (p = c; p != nil && *p != 0; p = e){ + e = strchr(p, '\n'); + if (e == p){ + e++; + continue; + } + if (e == nil) + e = p + strlen(p); + mconfig(p, e - p); + } + poperror(); + poperror(); +} + + +static int +mgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp) +{ + Qid qid; + Fsdev *mp; + + if (c->qid.path == Qtop){ + switch(i){ + case DEVDOTDOT: + devdir(c, tqid, "#k", 0, eve, DMDIR|0775, dp); + return 1; + case 0: + devdir(c, dqid, "ds", 0, eve, DMDIR|0775, dp); + return 1; + default: + return -1; + } + } + if (c->qid.path != Qdir){ + switch(i){ + case DEVDOTDOT: + devdir(c, dqid, "ds", 0, eve, DMDIR|0775, dp); + return 1; + default: + return -1; + } + } + switch(i){ + case DEVDOTDOT: + devdir(c, tqid, "#k", 0, eve, DMDIR|0775, dp); + return 1; + case 0: + devdir(c, cqid, "ctl", 0, eve, 0664, dp); + return 1; + } + i--; // for ctl + qid.path = Qfirst + i; + qid.vers = 0; + qid.type = 0; + mp = path2dev(i, 0); + if (mp == nil) + return -1; + kstrcpy(up->genbuf, mp->name, sizeof(up->genbuf)); + devdir(c, qid, up->genbuf, mp->size, eve, 0664, dp); + return 1; +} + +static Chan* +mattach(char *spec) +{ + *confstr = 0; + return devattach(L'k', spec); +} + +static Walkqid* +mwalk(Chan *c, Chan *nc, char **name, int nname) +{ + if (!configed) + rdconf(); + return devwalk(c, nc, name, nname, 0, 0, mgen); +} + +static int +mstat(Chan *c, uchar *db, int n) +{ + Dir d; + Fsdev *mp; + int p; + + p = c->qid.path; + memset(&d, 0, sizeof(d)); + switch(p){ + case Qtop: + devdir(c, tqid, "#k", 0, eve, DMDIR|0775, &d); + break; + case Qdir: + devdir(c, dqid, "ds", 0, eve, DMDIR|0775, &d); + break; + case Qctl: + devdir(c, cqid, "ctl", 0, eve, 0664, &d); + break; + default: + mp = path2dev(p - Qfirst, 1); + devdir(c, c->qid, mp->name, mp->size, eve, 0664, &d); + } + n = convD2M(&d, db, n); + if (n == 0) + error(Ebadarg); + return n; +} + +static Chan* +mopen(Chan *c, int omode) +{ + if((c->qid.type & QTDIR) && omode != OREAD) + error(Eperm); + if (omode & OTRUNC) + omode &= ~OTRUNC; + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +mclose(Chan*) +{ + // that's easy +} + +static long +catio(Fsdev *mp, int isread, void *a, long n, vlong off) +{ + int i; + Chan* mc; + long l, wl, res; + //print("catio %d %p %ld %lld\n", isread, a, n, off); + res = n; + for (i = 0; n >= 0 && i < mp->ndevs ; i++){ + mc = mp->idev[i]; + if (off > mp->isize[i]){ + off -= mp->isize[i]; + continue; + } + if (off + n > mp->isize[i]) + l = mp->isize[i] - off; + else + l = n; + //print("\tdev %d %p %ld %lld\n", i, a, l, off); + + if (isread) + wl = devtab[mc->type]->read(mc, a, l, off); + else + wl = devtab[mc->type]->write(mc, a, l, off); + if (wl != l) + error("#k: write failed"); + a = (char*)a + l; + off = 0; + n -= l; + } + //print("\tres %ld\n", res - n); + return res - n; +} + +static long +interio(Fsdev *mp, int isread, void *a, long n, vlong off) +{ + int i; + Chan* mc; + long l, wl, wsz; + vlong woff, blk, mblk; + long boff, res; + + blk = off / Blksize; + boff = off % Blksize; + wsz = Blksize - boff; + res = n; + while(n > 0){ + i = blk % mp->ndevs; + mc = mp->idev[i]; + mblk = blk / mp->ndevs; + woff = mblk * Blksize + boff; + if (n > wsz) + l = wsz; + else + l = n; + if (isread) + wl = devtab[mc->type]->read(mc, a, l, woff); + else + wl = devtab[mc->type]->write(mc, a, l, woff); + if (wl != l || l == 0) + error(Eio); + a = (char*)a + l; + n -= l; + blk++; + boff = 0; + wsz = Blksize; + } + return res; +} + +static long +mread(Chan *c, void *a, long n, vlong off) +{ + int i; + Fsdev *mp; + Chan *mc; + long l; + long res; + + if (c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, mgen); + if (c->qid.path == Qctl) + return readstr((long)off, a, n, confstr + strlen(Cfgstr)); + i = c->qid.path - Qfirst; + mp = path2dev(i, 1); + + if (off >= mp->size) + return 0; + if (off + n > mp->size) + n = mp->size - off; + if (n == 0) + return 0; + + res = -1; + switch(mp->type){ + case Fmirror: + for (i = 0; i < mp->ndevs; i++){ + mc = mp->idev[i]; + if (waserror()){ + // if a read fails we let the user know and try + // another device. + print("#k: mread: (%llx %d): %s\n", + c->qid.path, i, up->env->errstr); + continue; + } + l = devtab[mc->type]->read(mc, a, n, off); + poperror(); + if (l >=0){ + res = l; + break; + } + } + if (i == mp->ndevs) + error(Eio); + break; + case Fcat: + res = catio(mp, 1, a, n, off); + break; + case Finter: + res = interio(mp, 1, a, n, off); + break; + case Fpart: + off += mp->start; + mc = mp->idev[0]; + res = devtab[mc->type]->read(mc, a, n, off); + break; + } + return res; +} + +static long +mwrite(Chan *c, void *a, long n, vlong off) +{ + Fsdev *mp; + long l, res; + int i; + Chan *mc; + + if (c->qid.type & QTDIR) + error(Eperm); + if (c->qid.path == Qctl){ + mconfig(a, n); + return n; + } + mp = path2dev(c->qid.path - Qfirst, 1); + + if (off >= mp->size) + return 0; + if (off + n > mp->size) + n = mp->size - off; + if (n == 0) + return 0; + res = n; + switch(mp->type){ + case Fmirror: + for (i = mp->ndevs-1; i >=0; i--){ + mc = mp->idev[i]; + l = devtab[mc->type]->write(mc, a, n, off); + if (l < res) + res = l; + } + break; + case Fcat: + res = catio(mp, 0, a, n, off); + break; + case Finter: + res = interio(mp, 0, a, n, off); + break; + case Fpart: + mc = mp->idev[0]; + off += mp->start; + l = devtab[mc->type]->write(mc, a, n, off); + if (l < res) + res = l; + break; + } + return res; +} + +Dev dsdevtab = { + 'k', + "ds", + + devreset, + devinit, + devshutdown, + mattach, + mwalk, + mstat, + mopen, + devcreate, + mclose, + mread, + devbread, + mwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devdup.c b/os/port/devdup.c new file mode 100644 index 00000000..b176d6c0 --- /dev/null +++ b/os/port/devdup.c @@ -0,0 +1,151 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +/* Qid is (2*fd + (file is ctl))+1 */ + +static int +dupgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp) +{ + Fgrp *fgrp = up->env->fgrp; + Chan *f; + static int perm[] = { 0400, 0200, 0600, 0 }; + int p; + Qid q; + + if(s == DEVDOTDOT){ + devdir(c, c->qid, ".", 0, eve, DMDIR|0555, dp); + return 1; + } + if(s == 0) + return 0; + s--; + if(s/2 > fgrp->maxfd) + return -1; + if((f=fgrp->fd[s/2]) == nil) + return 0; + if(s & 1){ + p = 0400; + sprint(up->genbuf, "%dctl", s/2); + }else{ + p = perm[f->mode&3]; + sprint(up->genbuf, "%d", s/2); + } + mkqid(&q, s+1, 0, QTFILE); + devdir(c, q, up->genbuf, 0, eve, p, dp); + return 1; +} + +static Chan* +dupattach(char *spec) +{ + return devattach('d', spec); +} + +static Walkqid* +dupwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, dupgen); +} + +static int +dupstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, nil, 0, dupgen); +} + +static Chan* +dupopen(Chan *c, int omode) +{ + Chan *f; + int fd, twicefd; + + if(c->qid.type & QTDIR){ + if(omode != 0) + error(Eisdir); + c->mode = 0; + c->flag |= COPEN; + c->offset = 0; + return c; + } + if(c->qid.type & QTAUTH) + error(Eperm); + twicefd = c->qid.path - 1; + fd = twicefd/2; + if((twicefd & 1)){ + /* ctl file */ + f = c; + f->mode = openmode(omode); + f->flag |= COPEN; + f->offset = 0; + }else{ + /* fd file */ + f = fdtochan(up->env->fgrp, fd, openmode(omode), 0, 1); + cclose(c); + } + if(omode & OCEXEC) + f->flag |= CCEXEC; + return f; +} + +static void +dupclose(Chan*) +{ +} + +static long +dupread(Chan *c, void *va, long n, vlong offset) +{ + char *a = va; + char buf[256]; + int fd, twicefd; + + if(c->qid.type == QTDIR) + return devdirread(c, a, n, nil, 0, dupgen); + twicefd = c->qid.path - 1; + fd = twicefd/2; + if(twicefd & 1){ + c = fdtochan(up->env->fgrp, fd, -1, 0, 1); + if(waserror()){ + cclose(c); + nexterror(); + } + progfdprint(c, fd, 0, buf, sizeof buf); + poperror(); + cclose(c); + return readstr((ulong)offset, va, n, buf); + } + panic("dupread"); + return 0; +} + +static long +dupwrite(Chan*, void*, long, vlong) +{ + panic("dupwrite"); + return 0; /* not reached */ +} + +Dev dupdevtab = { + 'd', + "dup", + + devreset, + devinit, + devshutdown, + dupattach, + dupwalk, + dupstat, + dupopen, + devcreate, + dupclose, + dupread, + devbread, + dupwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devdynld.c b/os/port/devdynld.c new file mode 100644 index 00000000..cc53308c --- /dev/null +++ b/os/port/devdynld.c @@ -0,0 +1,365 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include <a.out.h> +#include <dynld.h> +#include <kernel.h> + +/* + * TO DO + * - dynamic allocation of Dev.dc + * - inter-module dependencies + * - reference count on Dev to allow error("inuse") [or how else to do it] + * - is Dev.shutdown the right function to call? Dev.config perhaps? + */ + +#define DBG if(1) print +#define NATIVE + + +extern ulong ndevs; + +enum +{ + Qdir, + Qdynld, + Qdynsyms, + + DEVCHAR = 'L', +}; + +static Dirtab dltab[] = +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "dynld", {Qdynld}, 0, 0644, + "dynsyms", {Qdynsyms}, 0, 0444, +}; + +typedef struct Dyndev Dyndev; + +struct Dyndev +{ + char* name; /* device name (eg, "dynld") */ + char* tag; /* version tag (eg, MD5 or SHA1 hash of content) */ + char* path; /* file from whence it came */ + Dynobj* o; + Dev* dev; + Dyndev* next; +}; + +static Dyndev *loaded; +static QLock dllock; + +static Dyndev** finddyndev(char*); +static int matched(Dyndev*, char*, char*); + +extern Dynobj* kdynloadfd(int, Dynsym*, int, ulong); + +static void +dlfree(Dyndev *l) +{ + if(l != nil){ + free(l->tag); + free(l->name); + free(l->path); + dynobjfree(l->o); + free(l); + } +} + +static Dyndev* +dlload(char *path, Dynsym *tab, int ntab) +{ + Dyndev *l; + int fd; + + /* in Plan 9, would probably use Chan* interface here */ + fd = kopen(path, OREAD); + if(fd < 0) + error("cannot open"); + if(waserror()){ + kclose(fd); + nexterror(); + } + l = mallocz(sizeof(Dyndev), 1); + if(l == nil) + error(Enomem); + if(waserror()){ + dlfree(l); + nexterror(); + } + l->path = strdup(path); + if(l->path == nil) + error(Enomem); + l->o = kdynloadfd(fd, tab, ntab, 0); + if(l->o == nil) + error(up->env->errstr); + poperror(); + poperror(); + kclose(fd); + return l; +} + +static void +devload(char *name, char *path, char *tag) +{ + int i; + Dyndev *l, **lp; + Dev *dev; + char tabname[32]; + + lp = finddyndev(name); + if(*lp != nil) + error("already loaded"); /* i'm assuming the name (eg, "cons") is to be unique */ + l = dlload(path, _exporttab, dyntabsize(_exporttab)); + if(waserror()){ + dlfree(l); + nexterror(); + } + snprint(tabname, sizeof(tabname), "%sdevtab", name); + dev = dynimport(l->o, tabname, signof(*dev)); + if(dev == nil) + errorf("can't find %sdevtab in module", name); + kstrdup(&l->name, name); + kstrdup(&l->tag, tag != nil? tag: ""); + if(dev->name == nil) + dev->name = l->name; + else if(strcmp(dev->name, l->name) != 0) + errorf("module file has device %s", dev->name); + /* TO DO: allocate dev->dc dynamically (cf. brucee's driver) */ + if(devno(dev->dc, 1) >= 0) + errorf("devchar %C already used", dev->dc); + for(i = 0; devtab[i] != nil; i++) + ; + if(i >= ndevs || devtab[i+1] != nil) + error("device table full"); +#ifdef NATIVE + i = splhi(); + dev->reset(); + splx(i); +#endif + dev->init(); + l->dev = devtab[i] = dev; + l->next = loaded; + loaded = l; /* recently loaded ones first: good unload order? */ + poperror(); +} + +static Dyndev** +finddyndev(char *name) +{ + Dyndev *l, **lp; + + for(lp = &loaded; (l = *lp) != nil; lp = &l->next) + if(strcmp(l->name, name) == 0) + break; + return lp; +} + +static int +matched(Dyndev *l, char *path, char *tag) +{ + if(path != nil && strcmp(l->path, path) != 0) + return 0; + if(tag != nil && strcmp(l->tag, tag) != 0) + return 0; + return 1; +} + +static void +devunload(char *name, char *path, char *tag) +{ + int i; + Dyndev *l, **lp; + + lp = finddyndev(name); + l = *lp; + if(l == nil) + error("not loaded"); + if(!matched(l, path, tag)) + error("path/tag mismatch"); + for(i = 0; i < ndevs; i++) + if(l->dev == devtab[i]){ + devtab[i] = nil; /* TO DO: ensure driver is not currently in use */ + break; + } +#ifdef NATIVE + l->dev->shutdown(); +#endif + *lp = l->next; + dlfree(l); +} + +static long +readdynld(void *a, ulong n, ulong offset) +{ + char *p; + Dyndev *l; + int m, len; + + m = 0; + for(l = loaded; l != nil; l = l->next) + m += 48 + strlen(l->name) + strlen(l->path) + strlen(l->tag); + p = malloc(m); + if(p == nil) + error(Enomem); + if(waserror()){ + free(p); + nexterror(); + } + *p = 0; + len = 0; + for(l = loaded; l != nil; l = l->next) + if(l->dev) + len += snprint(p+len, m-len, "#%C\t%.8p\t%.8lud\t%q\t%q\t%q\n", + l->dev->dc, l->o->base, l->o->size, l->name, l->path, l->tag); + n = readstr(offset, a, n, p); + poperror(); + free(p); + return n; +} + +static long +readsyms(char *a, ulong n, ulong offset) +{ + char *p; + Dynsym *t; + long l, nr; + + p = malloc(READSTR); + if(p == nil) + error(Enomem); + if(waserror()){ + free(p); + nexterror(); + } + nr = 0; + for(t = _exporttab; n > 0 && t->name != nil; t++){ + l = snprint(p, READSTR, "%.8lux %.8lux %s\n", t->addr, t->sig, t->name); + if(offset >= l){ + offset -= l; + continue; + } + l = readstr(offset, a, n, p); + offset = 0; + n -= l; + a += l; + nr += l; + } + poperror(); + free(p); + return nr; +} + +static Chan* +dlattach(char *spec) +{ + return devattach(DEVCHAR, spec); +} + +static Walkqid* +dlwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, dltab, nelem(dltab), devgen); +} + +static int +dlstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, dltab, nelem(dltab), devgen); +} + +static Chan* +dlopen(Chan *c, int omode) +{ + return devopen(c, omode, dltab, nelem(dltab), devgen); +} + +static void +dlclose(Chan *c) +{ + USED(c); +} + +static long +dlread(Chan *c, void *a, long n, vlong voffset) +{ + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, dltab, nelem(dltab), devgen); + case Qdynld: + return readdynld(a, n, voffset); + case Qdynsyms: + return readsyms(a, n, voffset); + default: + error(Egreg); + } + return n; +} + +static long +dlwrite(Chan *c, void *a, long n, vlong voffset) +{ + Cmdbuf *cb; + char *name, *tag, *path; + + USED(voffset); + switch((ulong)c->qid.path){ + case Qdynld: + cb = parsecmd(a, n); + qlock(&dllock); + if(waserror()){ + qunlock(&dllock); + free(cb); + nexterror(); + } + if(cb->nf < 3 || strcmp(cb->f[1], "dev") != 0) /* only do devices */ + cmderror(cb, Ebadctl); + name = cb->f[2]; + path = nil; + if(cb->nf > 3) + path = cb->f[3]; + tag = nil; + if(cb->nf > 4) + tag = cb->f[4]; + if(strcmp(cb->f[0], "load") == 0){ + if(path == nil) + cmderror(cb, "missing load path"); + devload(name, path, tag); /* TO DO: remaining parameters might be dependencies? */ + }else if(strcmp(cb->f[0], "unload") == 0) + devunload(name, path, tag); + else + cmderror(cb, Ebadctl); + poperror(); + qunlock(&dllock); + free(cb); + break; + default: + error(Egreg); + } + return n; +} + +Dev dynlddevtab = { + DEVCHAR, + "dynld", + + devreset, + devinit, + devshutdown, /* TO DO */ + dlattach, + dlwalk, + dlstat, + dlopen, + devcreate, + dlclose, + dlread, + devbread, + dlwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devenv.c b/os/port/devenv.c new file mode 100644 index 00000000..da6373a2 --- /dev/null +++ b/os/port/devenv.c @@ -0,0 +1,338 @@ +/* + * devenv - environment + */ +#include "u.h" +#include "../port/lib.h" +#include "../port/error.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +static void envremove(Chan*); + +enum +{ + Maxenvsize = 16300, +}; + +static int +envgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp) +{ + Egrp *eg; + Evalue *e; + + if(s == DEVDOTDOT){ + devdir(c, c->qid, "#e", 0, eve, DMDIR|0775, dp); + return 1; + } + eg = up->env->egrp; + qlock(eg); + for(e = eg->entries; e != nil && s != 0; e = e->next) + s--; + if(e == nil) { + qunlock(eg); + return -1; + } + /* make sure name string continues to exist after we release lock */ + kstrcpy(up->genbuf, e->var, sizeof up->genbuf); + devdir(c, e->qid, up->genbuf, e->len, eve, 0666, dp); + qunlock(eg); + return 1; +} + +static Chan* +envattach(char *spec) +{ + if(up->env == nil || up->env->egrp == nil) + error(Enodev); + return devattach('e', spec); +} + +static Walkqid* +envwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, envgen); +} + +static int +envstat(Chan *c, uchar *db, int n) +{ + if(c->qid.type & QTDIR) + c->qid.vers = up->env->egrp->vers; + return devstat(c, db, n, 0, 0, envgen); +} + +static Chan * +envopen(Chan *c, int mode) +{ + Egrp *eg; + Evalue *e; + + if(c->qid.type & QTDIR) { + if(mode != OREAD) + error(Eperm); + c->mode = mode; + c->flag |= COPEN; + c->offset = 0; + return c; + } + eg = up->env->egrp; + qlock(eg); + for(e = eg->entries; e != nil; e = e->next) + if(e->qid.path == c->qid.path) + break; + if(e == nil) { + qunlock(eg); + error(Enonexist); + } + if((mode & OTRUNC) && e->val) { + free(e->val); + e->val = 0; + e->len = 0; + e->qid.vers++; + } + qunlock(eg); + c->mode = openmode(mode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +envcreate(Chan *c, char *name, int mode, ulong) +{ + Egrp *eg; + Evalue *e, **le; + + if(c->qid.type != QTDIR) + error(Eperm); + if(strlen(name) >= sizeof(up->genbuf)) + error("name too long"); /* needs to fit for stat */ + mode = openmode(mode); + eg = up->env->egrp; + qlock(eg); + if(waserror()){ + qunlock(eg); + nexterror(); + } + for(le = &eg->entries; (e = *le) != nil; le = &e->next) + if(strcmp(e->var, name) == 0) + error(Eexist); + e = smalloc(sizeof(Evalue)); + e->var = smalloc(strlen(name)+1); + strcpy(e->var, name); + e->val = 0; + e->len = 0; + e->qid.path = ++eg->path; + e->next = nil; + e->qid.vers = 0; + *le = e; + c->qid = e->qid; + eg->vers++; + poperror(); + qunlock(eg); + c->offset = 0; + c->flag |= COPEN; + c->mode = mode; +} + +static void +envclose(Chan *c) +{ + if(c->flag & CRCLOSE) + envremove(c); +} + +static long +envread(Chan *c, void *a, long n, vlong offset) +{ + Egrp *eg; + Evalue *e; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, envgen); + eg = up->env->egrp; + qlock(eg); + if(waserror()){ + qunlock(eg); + nexterror(); + } + for(e = eg->entries; e != nil; e = e->next) + if(e->qid.path == c->qid.path) + break; + if(e == nil) + error(Enonexist); + if(offset > e->len) /* protects against overflow converting vlong to ulong */ + n = 0; + else if(offset + n > e->len) + n = e->len - offset; + if(n <= 0) + n = 0; + else + memmove(a, e->val+offset, n); + poperror(); + qunlock(eg); + return n; +} + +static long +envwrite(Chan *c, void *a, long n, vlong offset) +{ + char *s; + int ve; + Egrp *eg; + Evalue *e; + + if(n <= 0) + return 0; + eg = up->env->egrp; + qlock(eg); + if(waserror()){ + qunlock(eg); + nexterror(); + } + for(e = eg->entries; e != nil; e = e->next) + if(e->qid.path == c->qid.path) + break; + if(e == nil) + error(Enonexist); + ve = offset+n; + if(ve > Maxenvsize) + error(Etoobig); + if(ve > e->len) { + s = smalloc(ve); + memmove(s, e->val, e->len); + if(e->val != nil) + free(e->val); + e->val = s; + e->len = ve; + } + memmove(e->val+offset, a, n); + e->qid.vers++; + poperror(); + qunlock(eg); + return n; +} + +static void +envremove(Chan *c) +{ + Egrp *eg; + Evalue *e, **l; + + if(c->qid.type & QTDIR) + error(Eperm); + eg = up->env->egrp; + qlock(eg); + for(l = &eg->entries; (e = *l) != nil; l = &e->next) + if(e->qid.path == c->qid.path) + break; + if(e == nil) { + qunlock(eg); + error(Enonexist); + } + *l = e->next; + eg->vers++; + qunlock(eg); + free(e->var); + if(e->val != nil) + free(e->val); + free(e); +} + +Dev envdevtab = { + 'e', + "env", + + devreset, + devinit, + devshutdown, + envattach, + envwalk, + envstat, + envopen, + envcreate, + envclose, + envread, + devbread, + envwrite, + devbwrite, + envremove, + devwstat +}; + +/* + * kernel interface to environment variables + */ +Egrp* +newegrp(void) +{ + Egrp *e; + + e = smalloc(sizeof(Egrp)); + e->ref = 1; + return e; +} + +void +closeegrp(Egrp *e) +{ + Evalue *el, *nl; + + if(e == nil || decref(e) != 0) + return; + for (el = e->entries; el != nil; el = nl) { + free(el->var); + if (el->val) + free(el->val); + nl = el->next; + free(el); + } + free(e); +} + +void +egrpcpy(Egrp *to, Egrp *from) +{ + Evalue *e, *ne, **last; + + if(from == nil) + return; + last = &to->entries; + qlock(from); + for (e = from->entries; e != nil; e = e->next) { + ne = smalloc(sizeof(Evalue)); + ne->var = smalloc(strlen(e->var)+1); + strcpy(ne->var, e->var); + if (e->val) { + ne->val = smalloc(e->len); + memmove(ne->val, e->val, e->len); + ne->len = e->len; + } + ne->qid.path = ++to->path; + *last = ne; + last = &ne->next; + } + qunlock(from); +} + +void +ksetenv(char *var, char *val, int) +{ + Chan *c; + char buf[2*KNAMELEN]; + + snprint(buf, sizeof(buf), "#e/%s", var); + if(waserror()) + return; + c = namec(buf, Acreate, OWRITE, 0600); + poperror(); + if(!waserror()){ + if(!waserror()){ + devtab[c->type]->write(c, val, strlen(val), 0); + poperror(); + } + poperror(); + } + cclose(c); +} diff --git a/os/port/devflash.c b/os/port/devflash.c new file mode 100644 index 00000000..a014fa46 --- /dev/null +++ b/os/port/devflash.c @@ -0,0 +1,641 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +/* + * flash memory + */ + +#include "../port/flashif.h" + +typedef struct Flashtype Flashtype; +struct Flashtype { + char* name; + int (*reset)(Flash*); + Flashtype* next; +}; + +enum { + Nbanks = 2, +}; + +static struct +{ + Flash* card[Nbanks]; /* actual card type, reset for access */ + Flashtype* types; /* possible card types */ +}flash; + +enum{ + Qtopdir, + Qflashdir, + Qdata, + Qctl, +}; + +#define TYPE(q) ((ulong)(q) & 0xFF) +#define PART(q) ((ulong)(q)>>8) +#define QID(p,t) (((p)<<8) | (t)) + +static Flashregion* flashregion(Flash*, ulong); +static char* flashnewpart(Flash*, char*, ulong, ulong); +static ulong flashaddr(Flash*, Flashpart*, char*); +static void protect(Flash*, ulong); +static void eraseflash(Flash*, Flashregion*, ulong); +static long readflash(Flash*, void*, long, int); +static long writeflash(Flash*, long, void*,int); + +static char Eprotect[] = "flash region protected"; + +static int +flash2gen(Chan *c, ulong p, Dir *dp) +{ + Flashpart *fp; + Flash *f; + Qid q; + int mode; + + f = flash.card[c->dev]; + fp = &f->part[PART(p)]; + if(fp->name == nil) + return 0; + mkqid(&q, p, 0, QTFILE); + switch(TYPE(p)){ + case Qdata: + mode = 0660; + if(f->write == nil) + mode = 0440; + devdir(c, q, fp->name, fp->end-fp->start, eve, mode, dp); + return 1; + case Qctl: + snprint(up->genbuf, sizeof(up->genbuf), "%sctl", fp->name); + devdir(c, q, up->genbuf, 0, eve, 0660, dp); + return 1; + default: + return -1; + } +} + +static int +flashgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp) +{ + Qid q; + char *n; + + if(s == DEVDOTDOT){ + mkqid(&q, QID(0, Qtopdir), 0, QTDIR); + n = "#F"; + if(c->dev != 0){ + sprint(up->genbuf, "#F%ld", c->dev); + n = up->genbuf; + } + devdir(c, q, n, 0, eve, 0555, dp); + return 1; + } + switch(TYPE(c->qid.path)){ + case Qtopdir: + if(s != 0) + break; + mkqid(&q, QID(0, Qflashdir), 0, QTDIR); + n = "flash"; + if(c->dev != 0){ + sprint(up->genbuf, "flash%ld", c->dev); + n = up->genbuf; + } + devdir(c, q, n, 0, eve, 0555, dp); + return 1; + case Qflashdir: + if(s >= 2*nelem(flash.card[c->dev]->part)) + return -1; + return flash2gen(c, QID(s>>1, s&1?Qctl:Qdata), dp); + case Qctl: + case Qdata: + return flash2gen(c, (ulong)c->qid.path, dp); + } + return -1; +} + +static void +flashreset(void) +{ + Flash *f; + Flashtype *t; + char *e; + int bank; + + for(bank = 0; bank < Nbanks; bank++){ + f = malloc(sizeof(*f)); + if(f == nil){ + print("#F%d: can't allocate Flash data\n", bank); + return; + } + f->cmask = ~(ulong)0; + if(archflashreset(bank, f) < 0 || f->type == nil || f->addr == nil){ + free(f); + return; + } + for(t = flash.types; t != nil; t = t->next) + if(strcmp(f->type, t->name) == 0) + break; + if(t == nil){ + iprint("#F%d: no flash driver for type %s (addr %p)\n", bank, f->type, f->addr); + free(f); + return; + } + f->reset = t->reset; + f->protect = 1; + if(f->reset(f) == 0){ + flash.card[bank] = f; + iprint("#F%d: %s addr 0x%lux len %lud width %d interleave %d\n", bank, f->type, PADDR(f->addr), f->size, f->width, f->interleave); + e = flashnewpart(f, "flash", 0, f->size); + if(e != nil) + panic("#F%d: couldn't init table: %s\n", bank, e); /* shouldn't happen */ + }else + iprint("#F%d: reset failed (%s)\n", bank, f->type); + } +} + +static Chan* +flashattach(char *spec) +{ + Flash *f; + int bank; + Chan *c; + + bank = strtol(spec, nil, 0); + if(bank < 0 || bank >= Nbanks || + (f = flash.card[bank]) == nil || + f->attach != nil && f->attach(f) < 0) + error(Enodev); + c = devattach('F', spec); + c->dev = bank; + return c; +} + +static Walkqid* +flashwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, flashgen); +} + +static int +flashstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, nil, 0, flashgen); +} + +static Chan* +flashopen(Chan *c, int omode) +{ + omode = openmode(omode); + switch(TYPE(c->qid.path)){ + case Qdata: + case Qctl: + if(flash.card[c->dev] == nil) + error(Enodev); + break; + } + return devopen(c, omode, nil, 0, flashgen); +} + +static void +flashclose(Chan*) +{ +} + +static long +flashread(Chan *c, void *buf, long n, vlong offset) +{ + Flash *f; + char *s, *o; + Flashpart *fp; + Flashregion *r; + int i; + ulong start, end; + + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, nil, 0, flashgen); + + f = flash.card[c->dev]; + fp = &f->part[PART(c->qid.path)]; + if(fp->name == nil) + error(Egreg); + switch(TYPE(c->qid.path)){ + case Qdata: + offset += fp->start; + if(offset >= fp->end) + return 0; + if(offset+n > fp->end) + n = fp->end - offset; + n = readflash(f, buf, offset, n); + if(n < 0) + error(Eio); + return n; + case Qctl: + s = malloc(READSTR); + if(waserror()){ + free(s); + nexterror(); + } + o = seprint(s, s+READSTR, "%#2.2ux %#4.4ux %d %q\n", + f->id, f->devid, f->width, f->sort!=nil? f->sort: "nor"); + for(i=0; i<f->nr; i++){ + r = &f->regions[i]; + if(r->start < fp->end && fp->start < r->end){ + start = r->start; + if(fp->start > start) + start = fp->start; + end = r->end; + if(fp->end < end) + end = fp->end; + o = seprint(o, s+READSTR, "%#8.8lux %#8.8lux %#8.8lux", start, end, r->erasesize); + if(r->pagesize) + o = seprint(o, s+READSTR, " %#8.8lux", r->pagesize); + o = seprint(o, s+READSTR, "\n"); + } + } + n = readstr(offset, buf, n, s); + poperror(); + free(s); + return n; + } + error(Egreg); + return 0; /* not reached */ +} + +enum { + CMerase, + CMadd, + CMremove, + CMsync, + CMprotectboot, +}; + +static Cmdtab flashcmds[] = { + {CMerase, "erase", 2}, + {CMadd, "add", 0}, + {CMremove, "remove", 2}, + {CMsync, "sync", 0}, + {CMprotectboot, "protectboot", 0}, +}; + +static long +flashwrite(Chan *c, void *buf, long n, vlong offset) +{ + Cmdbuf *cb; + Cmdtab *ct; + ulong addr, start, end; + char *e; + Flashpart *fp; + Flashregion *r; + Flash *f; + + f = flash.card[c->dev]; + fp = &f->part[PART(c->qid.path)]; + if(fp->name == nil) + error(Egreg); + switch(TYPE(c->qid.path)){ + case Qdata: + if(f->write == nil) + error(Eperm); + offset += fp->start; + if(offset >= fp->end) + return 0; + if(offset+n > fp->end) + n = fp->end - offset; + n = writeflash(f, offset, buf, n); + if(n < 0) + error(Eio); + return n; + case Qctl: + cb = parsecmd(buf, n); + if(waserror()){ + free(cb); + nexterror(); + } + ct = lookupcmd(cb, flashcmds, nelem(flashcmds)); + switch(ct->index){ + case CMerase: + if(strcmp(cb->f[1], "all") != 0){ + addr = flashaddr(f, fp, cb->f[1]); + r = flashregion(f, addr); + if(r == nil) + error("nonexistent flash region"); + if(addr%r->erasesize != 0) + error("invalid erase block address"); + eraseflash(f, r, addr); + }else if(fp->start == 0 && fp->end == f->size && f->eraseall != nil){ + eraseflash(f, nil, 0); + }else{ + for(addr = fp->start; addr < fp->end; addr += r->erasesize){ + r = flashregion(f, addr); + if(r == nil) + error("nonexistent flash region"); + if(addr%r->erasesize != 0) + error("invalid erase block address"); + eraseflash(f, r, addr); + } + } + break; + case CMadd: + if(cb->nf < 3) + error(Ebadarg); + start = flashaddr(f, fp, cb->f[2]); + if(cb->nf > 3 && strcmp(cb->f[3], "end") != 0) + end = flashaddr(f, fp, cb->f[3]); + else + end = fp->end; + if(start > end || start >= fp->end || end > fp->end) + error(Ebadarg); + e = flashnewpart(f, cb->f[1], start, end); + if(e != nil) + error(e); + break; + case CMremove: + /* TO DO */ + break; + case CMprotectboot: + if(cb->nf > 1 && strcmp(cb->f[1], "off") == 0) + f->protect = 0; + else + f->protect = 1; + break; + case CMsync: + /* TO DO? */ + break; + default: + error(Ebadarg); + } + poperror(); + free(cb); + return n; + } + error(Egreg); + return 0; /* not reached */ +} + +static char* +flashnewpart(Flash *f, char *name, ulong start, ulong end) +{ + Flashpart *fp, *empty; + int i; + + empty = nil; + for(i = 0; i < nelem(f->part); i++){ + fp = &f->part[i]; + if(fp->name == nil){ + if(empty == nil) + empty = fp; + }else if(strcmp(fp->name, name) == 0) + return Eexist; + } + if((fp = empty) == nil) + return "partition table full"; + fp->name = strdup(name); + if(fp->name == nil) + return Enomem; + fp->start = start; + fp->end = end; + return nil; +} + +static ulong +flashaddr(Flash *f, Flashpart *fp, char *s) +{ + Flashregion *r; + ulong addr; + + addr = strtoul(s, &s, 0); + if(*s) + error(Ebadarg); + if(fp->name == nil) + error("partition removed"); + addr += fp->start; + r = flashregion(f, addr); + if(r != nil && addr%r->erasesize != 0) + error("invalid erase unit address"); + if(addr < fp->start || addr > fp->end || addr > f->size) + error(Ebadarg); + return addr; +} + +static Flashregion* +flashregion(Flash *f, ulong a) +{ + int i; + Flashregion *r; + + for(i=0; i<f->nr; i++){ + r = &f->regions[i]; + if(r->start <= a && a < r->end) + return r; + } + return nil; +} + +Dev flashdevtab = { + 'F', + "flash", + + flashreset, + devinit, + devshutdown, + flashattach, + flashwalk, + flashstat, + flashopen, + devcreate, + flashclose, + flashread, + devbread, + flashwrite, + devbwrite, + devremove, + devwstat, +}; + +/* + * called by flash card types named in link section (eg, flashamd.c) + */ +void +addflashcard(char *name, int (*reset)(Flash*)) +{ + Flashtype *f, **l; + + f = (Flashtype*)malloc(sizeof(*f)); + f->name = name; + f->reset = reset; + f->next = nil; + for(l = &flash.types; *l != nil; l = &(*l)->next) + ; + *l = f; +} + +static long +readflash(Flash *f, void *buf, long offset, int n) +{ + int r, width, wmask; + uchar tmp[16]; + uchar *p; + ulong o; + + if(offset < 0 || offset+n > f->size) + error(Ebadarg); + qlock(f); + if(waserror()){ + qunlock(f); + nexterror(); + } + if(f->read != nil){ + width = f->width; + wmask = width-1; + p = buf; + if(offset&wmask) { + o = offset & ~wmask; + if(f->read(f, o, (ulong*)tmp, width) < 0) + error(Eio); + memmove(tmp, (uchar*)f->addr+o, width); + for(; n > 0 && offset&wmask; n--) + *p++ = tmp[offset++&wmask]; + } + r = n&wmask; + n &= ~wmask; + if(n){ + if(f->read(f, offset, (ulong*)p, n) < 0) + error(Eio); + offset += n; + p += n; + } + if(r){ + if(f->read(f, offset, (ulong*)tmp, width)) + error(Eio); + memmove(p, tmp, r); + } + }else + memmove(buf, (uchar*)f->addr+offset, n); /* assumes hardware supports byte access */ + poperror(); + qunlock(f); + return n; +} + +static long +writeflash(Flash *f, long offset, void *buf, int n) +{ + uchar tmp[16]; + uchar *p; + ulong o; + int r, width, wmask; + Flashregion *rg; + + if(f->write == nil || offset < 0 || offset+n > f->size) + error(Ebadarg); + rg = flashregion(f, offset); + if(f->protect && rg != nil && rg->start == 0 && offset < rg->erasesize) + error(Eprotect); + width = f->width; + wmask = width-1; + qlock(f); + archflashwp(f, 0); + if(waserror()){ + archflashwp(f, 1); + qunlock(f); + nexterror(); + } + p = buf; + if(offset&wmask){ + o = offset & ~wmask; + if(f->read != nil){ + if(f->read(f, o, tmp, width) < 0) + error(Eio); + }else + memmove(tmp, (uchar*)f->addr+o, width); + for(; n > 0 && offset&wmask; n--) + tmp[offset++&wmask] = *p++; + if(f->write(f, o, tmp, width) < 0) + error(Eio); + } + r = n&wmask; + n &= ~wmask; + if(n){ + if(f->write(f, offset, p, n) < 0) + error(Eio); + offset += n; + p += n; + } + if(r){ + if(f->read != nil){ + if(f->read(f, offset, tmp, width) < 0) + error(Eio); + }else + memmove(tmp, (uchar*)f->addr+offset, width); + memmove(tmp, p, r); + if(f->write(f, offset, tmp, width) < 0) + error(Eio); + } + poperror(); + archflashwp(f, 1); + qunlock(f); + return n; +} + +static void +eraseflash(Flash *f, Flashregion *r, ulong addr) +{ + int rv; + + if(f->protect && r != nil && r->start == 0 && addr < r->erasesize) + error(Eprotect); + qlock(f); + archflashwp(f, 0); + if(waserror()){ + archflashwp(f, 1); + qunlock(f); + nexterror(); + } + if(r == nil){ + if(f->eraseall != nil) + rv = f->eraseall(f); + else + rv = -1; + }else + rv = f->erasezone(f, r, addr); + if(rv < 0) + error(Eio); + poperror(); + archflashwp(f, 1); + qunlock(f); +} + +/* + * flash access taking width and interleave into account + */ +int +flashget(Flash *f, ulong a) +{ + switch(f->width){ + default: + return ((uchar*)f->addr)[a<<f->bshift]; + case 2: + return ((ushort*)f->addr)[a]; + case 4: + return ((ulong*)f->addr)[a]; + } +} + +void +flashput(Flash *f, ulong a, int v) +{ + switch(f->width){ + default: + ((uchar*)f->addr)[a<<f->bshift] = v; + break; + case 2: + ((ushort*)f->addr)[a] = v; + break; + case 4: + ((ulong*)f->addr)[a] = v; + break; + } +} diff --git a/os/port/devftl.c b/os/port/devftl.c new file mode 100644 index 00000000..ab24588a --- /dev/null +++ b/os/port/devftl.c @@ -0,0 +1,1365 @@ +/* + * basic Flash Translation Layer driver + * see for instance the Intel technical paper + * ``Understanding the Flash Translation Layer (FTL) Specification'' + * Order number 297816-001 (online at www.intel.com) + * + * a public driver by David Hinds, dhinds@allegro.stanford.edu + * further helps with some details. + * + * this driver uses the common simplification of never storing + * the VBM on the medium (a waste of precious flash!) but + * rather building it on the fly as the block maps are read. + * + * Plan 9 driver (c) 1997 by C H Forsyth (forsyth@terzarima.net) + * This driver may be used or adapted by anyone for any non-commercial purpose. + * + * adapted for Inferno 1998 by C H Forsyth, Vita Nuova Limited, York, England (forsyth@vitanuova.com) + * + * TO DO: + * check error handling details for get/put flash + * bad block handling + * reserved space in formatted size + * possibly block size as parameter + */ + +#include "u.h" +#include "../port/lib.h" +#include "../port/error.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +#include "kernel.h" + +#ifndef offsetof +#define offsetof(s, m) (ulong)(&(((s*)0)->m)) +#endif + +typedef struct Ftl Ftl; +typedef struct Merase Merase; +typedef struct Terase Terase; + +enum { + Eshift = 18, /* 2^18=256k; log2(eraseunit) */ + Flashseg = 1<<Eshift, + Bshift = 9, /* 2^9=512 */ + Bsize = 1<<Bshift, + BAMoffset = 0x100, + Nolimit = ~0, + USABLEPCT = 95, /* release only this % to client */ + + FTLDEBUG = 0, + + NPART = 4, +}; + +/* erase unit header (defined by FTL specification) */ +struct Merase { + uchar linktuple[5]; + uchar orgtuple[10]; + uchar nxfer; + uchar nerase[4]; + uchar id[2]; + uchar bshift; + uchar eshift; + uchar pstart[2]; + uchar nunits[2]; + uchar psize[4]; + uchar vbmbase[4]; + uchar nvbm[2]; + uchar flags; + uchar code; + uchar serial[4]; + uchar altoffset[4]; + uchar bamoffset[4]; + uchar rsv2[12]; +}; +#define ERASEHDRLEN 64 + +enum { + /* special unit IDs */ + XferID = 0xffff, + XferBusy = 0x7fff, + + /* special BAM addresses */ + Bfree = 0xffffffff, + Bwriting = 0xfffffffe, + Bdeleted = 0, + + /* block types */ + TypeShift = 7, + BlockType = (1<<TypeShift)-1, + ControlBlock = 0x30, + DataBlock = 0x40, + ReplacePage = 0x60, + BadBlock = 0x70, +}; + +#define BTYPE(b) ((b) & BlockType) +#define BADDR(b) ((b) & ~BlockType) +#define BNO(va) (((ulong)(va))>>Bshift) +#define MKBAM(b,t) (((b)<<Bshift)|(t)) + +struct Terase { + int x; + int id; + ulong offset; + ulong bamoffset; + ulong nbam; + ulong* bam; + ulong bamx; + ulong nfree; + ulong nused; + ulong ndead; + ulong nbad; + ulong nerase; +}; + +struct Ftl { + QLock; + Ref; + + Chan* flash; + Chan* flashctl; + ulong base; /* base of flash region */ + ulong size; /* size of flash region */ + ulong segsize; /* size of flash segment (erase unit) */ + int eshift; /* log2(erase-unit-size) */ + int bshift; /* log2(bsize) */ + int bsize; + int nunit; /* number of segments (erase units) */ + Terase** unit; + int lastx; /* index in unit of last allocation */ + int xfer; /* index in unit of current transfer unit (-1 if none) */ + ulong nfree; /* total free space in blocks */ + ulong nblock; /* total space in blocks */ + ulong rwlimit; /* user-visible block limit (`formatted size') */ + ulong* vbm; /* virtual block map */ + ulong fstart; /* address of first block of data in a segment */ + int trace; /* (debugging) trace of read/write actions */ + int detach; /* free Ftl on last close */ + + /* scavenging variables */ + QLock wantq; + Rendez wantr; + Rendez workr; + int needspace; + int hasproc; + int npart; /* over and above ftldata */ + struct { + ulong start, size; + ulong rwlimit; + char *name; /* nil if slot unused */ + } part[NPART]; +}; + +enum { + /* Ftl.detach */ + Detached = 1, /* detach on close */ + Deferred /* scavenger must free it */ +}; + +/* little endian */ +#define GET2(p) (((p)[1]<<8)|(p)[0]) +#define GET4(p) (((((((p)[3]<<8)|(p)[2])<<8)|(p)[1])<<8)|(p)[0]) +#define PUT2(p,v) (((p)[1]=(v)>>8),((p)[0]=(v))) +#define PUT4(p,v) (((p)[3]=(v)>>24),((p)[2]=(v)>>16),((p)[1]=(v)>>8),((p)[0]=(v))) + +static Lock ftllock; +static Ftl *ftls; +static int ftlpct = USABLEPCT; + +static ulong allocblk(Ftl*); +static int erasedetect(Ftl *ftl, ulong base, ulong size, ushort *pstart, ushort *nunits); +static void eraseflash(Ftl*, ulong); +static void erasefree(Terase*); +static void eraseinit(Ftl*, ulong, int, int); +static Terase* eraseload(Ftl*, int, ulong); +static void ftlfree(Ftl*); +static void getflash(Ftl*, void*, ulong, long); +static int mapblk(Ftl*, ulong, Terase**, ulong*); +static Ftl* mkftl(char*, ulong, ulong, int, char*); +static void putbam(Ftl*, Terase*, int, ulong); +static void putflash(Ftl*, ulong, void*, long); +static int scavenge(Ftl*); + +enum { + Qdir, + Qctl, + Qdata, +}; + +#define DATAQID(q) ((q) >= Qdata && (q) <= Qdata + NPART) + +static void +ftlpartcmd(Ftl *ftl, char **fields, int nfields) +{ + ulong start, end; + char *p; + int n, newn; + + /* name start [end] */ + if(nfields < 2 || nfields > 3) + error(Ebadarg); + if(ftl->npart >= NPART) + error("table full"); + if(strcmp(fields[0], "ctl") == 0 || strcmp(fields[0], "data") == 0) + error(Ebadarg); + newn = -1; + for(n = 0; n < NPART; n++){ + if(ftl->part[n].name == nil){ + if(newn < 0) + newn = n; + continue; + } + if(strcmp(fields[0], ftl->part[n].name + 3) == 0) + error(Ebadarg); + } + start = strtoul(fields[1], 0, 0); + if(nfields > 2) + end = strtoul(fields[2], 0, 0); + else + end = ftl->rwlimit * Bsize; + if(start >= end || start % Bsize || end % Bsize) + error(Ebadarg); + ftl->part[newn].start = start; + ftl->part[newn].size = end - start; + ftl->part[newn].rwlimit = end / Bsize; + free(ftl->part[newn].name); + p = malloc(strlen(fields[0]) + 3 + 1); + strcpy(p, "ftl"); + strcat(p, fields[0]); + ftl->part[newn].name = p; + ftl->npart++; +} + +static void +ftldelpartcmd(Ftl *ftl, char **fields, int nfields) +{ + int n; + // name + if(nfields != 1) + error(Ebadarg); + for(n = 0; n < NPART; n++) + if(strcmp(fields[0], ftl->part[n].name + 3) == 0){ + free(ftl->part[n].name); + ftl->part[n].name = nil; + ftl->npart--; + return; + } + error(Ebadarg); +} + +static int +ftlgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp) +{ + int n; + switch(i){ + case DEVDOTDOT: + devdir(c, (Qid){Qdir, 0, QTDIR}, "#X", 0, eve, 0555, dp); + break; + case 0: + devdir(c, (Qid){Qctl, 0, QTFILE}, "ftlctl", 0, eve, 0660, dp); + break; + case 1: + devdir(c, (Qid){Qdata, 0, QTFILE}, "ftldata", ftls ? ftls->rwlimit * Bsize : 0, eve, 0660, dp); + break; + default: + if(ftls == nil) + return -1; + i -= 2; + if(i >= ftls->npart) + return -1; + for(n = 0; n < NPART; n++) + if(ftls->part[n].name != nil){ + if(i == 0) + break; + i--; + } + if(i != 0){ + print("wierd\n"); + return -1; + } + devdir(c, (Qid){Qdata + 1 + n, 0, QTFILE}, ftls->part[n].name, ftls->part[n].size, eve, 0660, dp); + } + return 1; +} + +static Ftl * +ftlget(void) +{ + Ftl *ftl; + + lock(&ftllock); + ftl = ftls; + if(ftl != nil) + incref(ftl); + unlock(&ftllock); + return ftl; +} + +static void +ftlput(Ftl *ftl) +{ + if(ftl != nil){ + lock(&ftllock); + if(decref(ftl) == 0 && ftl->detach == Detached){ + ftls = nil; + if(ftl->hasproc){ /* no lock needed: can't change if ftl->ref==0 */ + ftl->detach = Deferred; + wakeup(&ftl->workr); + }else + ftlfree(ftl); + } + unlock(&ftllock); + } +} + +static Chan * +ftlattach(char *spec) +{ + return devattach('X', spec); +} + +static Walkqid* +ftlwalk(Chan *c, Chan *nc, char **name, int nname) +{ + Walkqid *wq; + + wq = devwalk(c, nc, name, nname, 0, 0, ftlgen); + if(wq != nil && wq->clone != nil && wq->clone != c) + if(DATAQID(wq->clone->qid.path)) + wq->clone->aux = ftlget(); + return wq; +} + +static int +ftlstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, 0, 0, ftlgen); +} + +static Chan* +ftlopen(Chan *c, int omode) +{ + Ftl *ftl; + omode = openmode(omode); + if(DATAQID(c->qid.path)){ + ftl = ftls; + if(ftl == nil) + error(Enodev); + if(strcmp(up->env->user, eve)!=0) + error(Eperm); + } + else if(c->qid.path == Qctl){ + if(strcmp(up->env->user, eve)!=0) + error(Eperm); + } + c = devopen(c, omode, 0, 0, ftlgen); + if(DATAQID(c->qid.path)){ + c->aux = ftlget(); + if(c->aux == nil) + error(Enodev); + } + return c; +} + +static void +ftlclose(Chan *c) +{ + if(DATAQID(c->qid.path) && (c->flag&COPEN) != 0) + ftlput((Ftl*)c->aux); +} + +static long +ftlread(Chan *c, void *buf, long n, vlong offset) +{ + Ftl *ftl; + Terase *e; + int nb; + uchar *a; + ulong pb; + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, 0, 0, ftlgen); + + if(DATAQID(c->qid.path)){ + ulong rwlimit; + + if(n <= 0 || n%Bsize || offset%Bsize) + error(Eio); + ftl = c->aux; + if(c->qid.path > Qdata){ + int p = c->qid.path - Qdata - 1; + offset += ftl->part[p].start; + rwlimit = ftl->part[p].rwlimit; + } + else + rwlimit = ftl->rwlimit; + nb = n/Bsize; + offset /= Bsize; + if(offset >= rwlimit) + return 0; + if(offset+nb > rwlimit) + nb = rwlimit - offset; + a = buf; + for(n = 0; n < nb; n++){ + qlock(ftl); + if(waserror()){ + qunlock(ftl); + nexterror(); + } + if(mapblk(ftl, offset+n, &e, &pb)) + getflash(ftl, a, e->offset + pb*Bsize, Bsize); + else + memset(a, 0, Bsize); + poperror(); + qunlock(ftl); + a += Bsize; + } + return a-(uchar*)buf; + } + + if(c->qid.path != Qctl) + error(Egreg); + + return 0; +} + +static long +ftlwrite(Chan *c, void *buf, long n, vlong offset) +{ + char cmd[64], *fields[6]; + int ns, i, k, nb; + uchar *a; + Terase *e, *oe; + ulong ob, v, base, size, segsize; + Ftl *ftl; + + if(n <= 0) + return 0; + + if(DATAQID(c->qid.path)){ + ulong rwlimit; + ftl = c->aux; + if(n <= 0 || n%Bsize || offset%Bsize) + error(Eio); + if(c->qid.path > Qdata){ + int p = c->qid.path - Qdata - 1; + offset += ftl->part[p].start; + rwlimit = ftl->part[p].rwlimit; + } + else + rwlimit = ftl->rwlimit; + nb = n/Bsize; + offset /= Bsize; + if(offset >= rwlimit) + return 0; + if(offset+nb > rwlimit) + nb = rwlimit - offset; + a = buf; + for(n = 0; n < nb; n++){ + ns = 0; + while((v = allocblk(ftl)) == 0) + if(!scavenge(ftl) || ++ns > 3){ + static int stop; + + if(stop < 3){ + stop++; + print("ftl: flash memory full\n"); + } + error("flash memory full"); + } + qlock(ftl); + if(waserror()){ + qunlock(ftl); + nexterror(); + } + if(!mapblk(ftl, offset+n, &oe, &ob)) + oe = nil; + e = ftl->unit[v>>16]; + v &= 0xffff; + putflash(ftl, e->offset + v*Bsize, a, Bsize); + putbam(ftl, e, v, MKBAM(offset+n, DataBlock)); + /* both old and new block references exist in this window (can't be closed?) */ + ftl->vbm[offset+n] = (e->x<<16) | v; + if(oe != nil){ + putbam(ftl, oe, ob, Bdeleted); + oe->ndead++; + } + poperror(); + qunlock(ftl); + a += Bsize; + } + return a-(uchar*)buf; + } + else if(c->qid.path == Qctl){ + if(n > sizeof(cmd)-1) + n = sizeof(cmd)-1; + memmove(cmd, buf, n); + cmd[n] = 0; + i = getfields(cmd, fields, 6, 1, " \t\n"); + if(i <= 0) + error(Ebadarg); + if(i >= 2 && (strcmp(fields[0], "init") == 0 || strcmp(fields[0], "format") == 0)){ + if(i > 2) + base = strtoul(fields[2], nil, 0); + else + base = 0; /* TO DO: hunt for signature */ + if(i > 3) + size = strtoul(fields[3], nil, 0); + else + size = Nolimit; + if(i > 4) + segsize = strtoul(fields[4], nil, 0); + else + segsize = 0; + /* segsize must be power of two and size and base must be multiples of it + * if segsize is zero, then use whatever the device returns + */ + if(segsize != 0 && (segsize > size + || segsize&(segsize-1) + || (base != Nolimit && base&(segsize-1)) + || size == 0 + || (size != Nolimit && size&(segsize-1)))) + error(Ebadarg); + if(segsize == 0) + k = 0; + else { + for(k=0; k<32 && (1<<k) != segsize; k++) + ; + } + if(ftls != nil) + error(Einuse); + ftls = mkftl(fields[1], base, size, k, fields[0]); + }else if(strcmp(fields[0], "scavenge") == 0){ + if(ftls != nil) + print("scavenge %d\n", scavenge(ftls)); + }else if(strcmp(fields[0], "trace") == 0){ + if(ftls != nil) + ftls->trace = i>1? strtol(fields[1], nil, 0): 1; + }else if(strcmp(fields[0], "detach") == 0){ + if((ftl = ftlget()) != nil){ + if(ftl->ref > 1){ + ftlput(ftl); + error(Einuse); + } + ftl->detach = Detached; + ftlput(ftl); + }else + error(Enodev); + }else if(strcmp(fields[0], "part")==0){ + if((ftl = ftlget()) != nil){ + if(ftl->ref > 1){ + ftlput(ftl); + error(Einuse); + } + if(waserror()){ + ftlput(ftl); + nexterror(); + } + ftlpartcmd(ftl, fields + 1, i - 1); + poperror(); + ftlput(ftl); + }else + error(Enodev); + }else if(strcmp(fields[0], "delpart")==0){ + if((ftl = ftlget()) != nil){ + if(ftl->ref > 1){ + ftlput(ftl); + error(Einuse); + } + if(waserror()){ + ftlput(ftl); + nexterror(); + } + ftldelpartcmd(ftls, fields + 1, i - 1); + poperror(); + ftlput(ftl); + }else + error(Enodev); + }else if(i >= 2 && strcmp(fields[0], "pct")==0){ + v = strtoul(fields[1], nil, 0); + if(v >= 50) + ftlpct = v; + }else + error(Ebadarg); + return n; + } + error(Egreg); + return 0; /* not reached */ +} + +static Chan * +ftlkopen(char *name, char *suffix, int mode) +{ + Chan *c; + char *fn; + int fd; + + if(suffix != nil && *suffix){ + fn = smalloc(strlen(name)+strlen(suffix)+1); + if(fn == nil) + return nil; + strcpy(fn, name); + strcat(fn, suffix); + fd = kopen(fn, mode); + free(fn); + }else + fd = kopen(name, mode); + if(fd < 0) + return nil; + c = fdtochan(up->env->fgrp, fd, mode, 0, 1); + kclose(fd); + return c; +} + +static ulong +ftlfsize(Chan *c) +{ + uchar dbuf[STATFIXLEN+32*4]; + Dir d; + int n; + + n = devtab[c->type]->stat(c, dbuf, sizeof(dbuf)); + if(convM2D(dbuf, n, &d, nil) == 0) + return 0; + return d.length; +} + +static Ftl * +mkftl(char *fname, ulong base, ulong size, int eshift, char *op) +{ + int i, j, nov, segblocks, n, badseg, old, valid; + ulong limit; + Terase *e; + Ftl *ftl; + char buf[64], *fields[8]; + ulong ea; + Chan *statfd; + + ftl = malloc(sizeof(*ftl)); + if(ftl == nil) + error(Enomem); + e = nil; + if(waserror()){ + ftlfree(ftl); + if(e) + free(e); + nexterror(); + } + ftl->lastx = 0; + ftl->trace = 0; + ftl->flash = ftlkopen(fname, "", ORDWR); + if(ftl->flash == nil) + error(up->env->errstr); + ftl->flashctl = ftlkopen(fname, "ctl", ORDWR); + if(ftl->flashctl == nil) + error(up->env->errstr); + old = 1; + statfd = ftlkopen(fname, "stat", OREAD); /* old scheme */ + if(statfd == nil){ + statfd = ftl->flashctl; /* new just uses ctl */ + old = 0; + } + statfd->offset = 0; + if((n = kchanio(statfd, buf, sizeof(buf), OREAD)) <= 0){ + print("ftl: read stat/ctl failed: %s\n", up->env->errstr); + error(up->env->errstr); + } + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + buf[n] = 0; + if(statfd != ftl->flashctl) + cclose(statfd); + + n = getfields(buf, fields, nelem(fields), 1, " \t\n"); + ea = 0; + if(old){ + if(n >= 4) + if((ea = strtoul(fields[3], nil, 16)) < 8*1024) + ea = 0; /* assume the format is wrong */ + }else{ + if(n >= 7) + if((ea = strtoul(fields[6], nil, 0)) < 2*1024) + ea = 0; /* assume the format is wrong */ + } + if(ea != 0){ + for(i=0; i < 32 && (1<<i) != ea; i++) + ; + if(eshift && i != eshift) + print("ftl: overriding erasesize %d with %d\n", 1 << eshift, 1 << i); + eshift = i; + if(FTLDEBUG) + print("ftl: e=%lud eshift=%d\n", ea, i); + } + if(eshift == 0) + error("no erasesize"); + + limit = ftlfsize(ftl->flash); + if(limit == 0) + error("no space for flash translation"); + ftl->segsize = 1 << eshift; + if(base == Nolimit){ + ushort pstart, nunits; + erasedetect(ftl, 0, limit, &pstart, &nunits); + base = pstart * ftl->segsize; + size = nunits * ftl->segsize; + print("ftl: partition in %s at 0x%.8lux, length 0x%.8lux\n", fname, base, size); + } else if(size == Nolimit) + size = limit-base; + if(base >= limit || size > limit || base+size > limit || eshift < 8 || (1<<eshift) > size){ + print("ftl: bad: base=%#lux limit=%#lux size=%ld eshift=%d\n", base, limit, size, eshift); + error("bad flash space parameters"); + } + if(FTLDEBUG) + print("%s flash %s #%lux:#%lux limit #%lux\n", op, fname, base, size, limit); + ftl->base = base; + ftl->size = size; + ftl->bshift = Bshift; + ftl->bsize = Bsize; + ftl->eshift = eshift; + ftl->nunit = size>>eshift; + nov = ((ftl->segsize/Bsize)*4 + BAMoffset + Bsize - 1)/Bsize; /* number of overhead blocks per segment (header, and BAM itself) */ + ftl->fstart = nov; + segblocks = ftl->segsize/Bsize - nov; + ftl->nblock = ftl->nunit*segblocks; + if(ftl->nblock > 0x10000){ + /* oops - too many blocks */ + ftl->nunit = 0x10000 / segblocks; + ftl->nblock = ftl->nunit * segblocks; + size = ftl->nunit * ftl->segsize; + ftl->size = size; + print("ftl: too many blocks - limiting to %ld bytes %d units %lud blocks\n", + size, ftl->nunit, ftl->nblock); + } + ftl->vbm = malloc(ftl->nblock*sizeof(*ftl->vbm)); + ftl->unit = malloc(ftl->nunit*sizeof(*ftl->unit)); + if(ftl->vbm == nil || ftl->unit == nil) + error(Enomem); + if(strcmp(op, "format") == 0){ + for(i=0; i<ftl->nunit-1; i++) + eraseinit(ftl, i*ftl->segsize, i, 1); + eraseinit(ftl, i*ftl->segsize, XferID, 1); + } + badseg = -1; + ftl->xfer = -1; + valid = 0; + for(i=0; i<ftl->nunit; i++){ + e = eraseload(ftl, i, i*ftl->segsize); + if(e == nil){ + print("ftl: logical segment %d: bad format\n", i); + if(badseg == -1) + badseg = i; + else + badseg = -2; + continue; + } + if(e->id == XferBusy){ + e->nerase++; + eraseinit(ftl, e->offset, XferID, e->nerase); + e->id = XferID; + } + for(j=0; j<ftl->nunit; j++) + if(ftl->unit[j] != nil && ftl->unit[j]->id == e->id){ + print("ftl: duplicate erase unit #%x\n", e->id); + erasefree(e); + e = nil; + break; + } + if(e){ + valid++; + ftl->unit[e->x] = e; + if(e->id == XferID) + ftl->xfer = e->x; + if(FTLDEBUG) + print("ftl: unit %d:#%x used %lud free %lud dead %lud bad %lud nerase %lud\n", + e->x, e->id, e->nused, e->nfree, e->ndead, e->nbad, e->nerase); + e = nil; + USED(e); + } + } + if(badseg >= 0){ + if(ftl->xfer >= 0) + error("invalid ftl format"); + i = badseg; + eraseinit(ftl, i*ftl->segsize, XferID, 1); + e = eraseload(ftl, i, i*ftl->segsize); + if(e == nil) + error("bad ftl format"); + ftl->unit[e->x] = e; + ftl->xfer = e->x; + print("ftl: recovered transfer unit %d\n", e->x); + valid++; + e = nil; + USED(e); + } + if(ftl->xfer < 0 && valid <= 0 || ftl->xfer >= 0 && valid <= 1) + error("no valid flash data units"); + if(ftl->xfer < 0) + error("ftl: no transfer unit: device is WORM\n"); + else + ftl->nblock -= segblocks; /* discount transfer segment */ + if(ftl->nblock >= 1000) + ftl->rwlimit = ftl->nblock-100; /* TO DO: variable reserve */ + else + ftl->rwlimit = ftl->nblock*ftlpct/100; + poperror(); + return ftl; +} + +static void +ftlfree(Ftl *ftl) +{ + int i, n; + + if(ftl != nil){ + if(ftl->flashctl != nil) + cclose(ftl->flashctl); + if(ftl->flash != nil) + cclose(ftl->flash); + + if(ftl->unit){ + for(i = 0; i < ftl->nunit; i++) + erasefree(ftl->unit[i]); + free(ftl->unit); + } + free(ftl->vbm); + for(n = 0; n < NPART; n++) + free(ftl->part[n].name); + free(ftl); + } +} + +/* + * this simple greedy algorithm weighted by nerase does seem to lead + * to even wear of erase units (cf. the eNVy file system) + */ +static Terase * +bestcopy(Ftl *ftl) +{ + Terase *e, *be; + int i; + + be = nil; + for(i=0; i<ftl->nunit; i++) + if((e = ftl->unit[i]) != nil && e->id != XferID && e->id != XferBusy && e->ndead+e->nbad && + (be == nil || e->nerase <= be->nerase && e->ndead >= be->ndead)) + be = e; + return be; +} + +static int +copyunit(Ftl *ftl, Terase *from, Terase *to) +{ + int i, nb; + uchar id[2]; + ulong *bam; + uchar *buf; + ulong v, bno; + + if(FTLDEBUG || ftl->trace) + print("ftl: copying %d (#%lux) to #%lux\n", from->id, from->offset, to->offset); + to->nbam = 0; + free(to->bam); + to->bam = nil; + bam = nil; + buf = malloc(Bsize); + if(buf == nil) + return 0; + if(waserror()){ + free(buf); + free(bam); + return 0; + } + PUT2(id, XferBusy); + putflash(ftl, to->offset+offsetof(Merase,id[0]), id, 2); + /* make new BAM */ + nb = from->nbam*sizeof(*to->bam); + bam = malloc(nb); + if(bam == nil) + error(Enomem); + memmove(bam, from->bam, nb); + to->nused = 0; + to->nbad = 0; + to->nfree = 0; + to->ndead = 0; + for(i = 0; i < from->nbam; i++) + switch(bam[i]){ + case Bwriting: + case Bdeleted: + case Bfree: + bam[i] = Bfree; + to->nfree++; + break; + default: + switch(bam[i]&BlockType){ + default: + case BadBlock: /* it isn't necessarily bad in this unit */ + to->nfree++; + bam[i] = Bfree; + break; + case DataBlock: + case ReplacePage: + v = bam[i]; + bno = BNO(v & ~BlockType); + if(i < ftl->fstart || bno >= ftl->nblock){ + print("ftl: unit %d:#%x bad bam[%d]=#%lux\n", from->x, from->id, i, v); + to->nfree++; + bam[i] = Bfree; + break; + } + getflash(ftl, buf, from->offset+i*Bsize, Bsize); + putflash(ftl, to->offset+i*Bsize, buf, Bsize); + to->nused++; + break; + case ControlBlock: + to->nused++; + break; + } + } + for(i=0; i<from->nbam; i++){ + uchar *p = (uchar*)&bam[i]; + v = bam[i]; + if(v != Bfree && ftl->trace > 1) + print("to[%d]=#%lux\n", i, v); + PUT4(p, v); + } + putflash(ftl, to->bamoffset, bam, nb); /* BUG: PUT4 */ + for(i=0; i<from->nbam; i++){ + uchar *p = (uchar*)&bam[i]; + v = bam[i]; + PUT4(p, v); + } + to->id = from->id; + PUT2(id, to->id); + putflash(ftl, to->offset+offsetof(Merase,id[0]), id, 2); + to->nbam = from->nbam; + to->bam = bam; + ftl->nfree += to->nfree - from->nfree; + poperror(); + free(buf); + return 1; +} + +static int +mustscavenge(void *a) +{ + return ((Ftl*)a)->needspace || ((Ftl*)a)->detach == Deferred; +} + +static int +donescavenge(void *a) +{ + return ((Ftl*)a)->needspace == 0; +} + +static void +scavengeproc(void *arg) +{ + Ftl *ftl; + int i; + Terase *e, *ne; + + ftl = arg; + if(waserror()){ + print("ftl: kproc noted\n"); + pexit("ftldeath", 0); + } + for(;;){ + sleep(&ftl->workr, mustscavenge, ftl); + if(ftl->detach == Deferred){ + ftlfree(ftl); + pexit("", 0); + } + if(FTLDEBUG || ftl->trace) + print("ftl: scavenge %ld\n", ftl->nfree); + qlock(ftl); + if(waserror()){ + qunlock(ftl); + nexterror(); + } + e = bestcopy(ftl); + if(e == nil || ftl->xfer < 0 || (ne = ftl->unit[ftl->xfer]) == nil || ne->id != XferID || e == ne) + goto Fail; + if(copyunit(ftl, e, ne)){ + i = ne->x; ne->x = e->x; e->x = i; + ftl->unit[ne->x] = ne; + ftl->unit[e->x] = e; + ftl->xfer = e->x; + e->id = XferID; + e->nbam = 0; + free(e->bam); + e->bam = nil; + e->bamx = 0; + e->nerase++; + eraseinit(ftl, e->offset, XferID, e->nerase); + } + Fail: + if(FTLDEBUG || ftl->trace) + print("ftl: end scavenge %ld\n", ftl->nfree); + ftl->needspace = 0; + wakeup(&ftl->wantr); + poperror(); + qunlock(ftl); + } +} + +static int +scavenge(Ftl *ftl) +{ + if(ftl->xfer < 0 || bestcopy(ftl) == nil) + return 0; /* you worm! */ + + qlock(ftl); + if(waserror()){ + qunlock(ftl); + return 0; + } + if(!ftl->hasproc){ + ftl->hasproc = 1; + kproc("ftl.scavenge", scavengeproc, ftl, 0); + } + ftl->needspace = 1; + wakeup(&ftl->workr); + poperror(); + qunlock(ftl); + + qlock(&ftl->wantq); + if(waserror()){ + qunlock(&ftl->wantq); + nexterror(); + } + while(ftl->needspace) + sleep(&ftl->wantr, donescavenge, ftl); + poperror(); + qunlock(&ftl->wantq); + + return ftl->nfree; +} + +static void +putbam(Ftl *ftl, Terase *e, int n, ulong entry) +{ + uchar b[4]; + + e->bam[n] = entry; + PUT4(b, entry); + putflash(ftl, e->bamoffset + n*4, b, 4); +} + +static ulong +allocblk(Ftl *ftl) +{ + Terase *e; + int i, j; + + qlock(ftl); + i = ftl->lastx; + do{ + e = ftl->unit[i]; + if(e != nil && e->id != XferID && e->nfree){ + ftl->lastx = i; + for(j=e->bamx; j<e->nbam; j++) + if(e->bam[j] == Bfree){ + putbam(ftl, e, j, Bwriting); + ftl->nfree--; + e->nfree--; + e->bamx = j+1; + qunlock(ftl); + return (e->x<<16) | j; + } + e->nfree = 0; + qunlock(ftl); + print("ftl: unit %d:#%x nfree %ld but not free in BAM\n", e->x, e->id, e->nfree); + qlock(ftl); + } + if(++i >= ftl->nunit) + i = 0; + }while(i != ftl->lastx); + qunlock(ftl); + return 0; +} + +static int +mapblk(Ftl *ftl, ulong bno, Terase **ep, ulong *bp) +{ + ulong v; + int x; + + if(bno < ftl->nblock){ + v = ftl->vbm[bno]; + if(v == 0 || v == ~0) + return 0; + x = v>>16; + if(x >= ftl->nunit || x == ftl->xfer || ftl->unit[x] == nil){ + print("ftl: corrupt format: bad block mapping %lud -> unit #%x\n", bno, x); + return 0; + } + *ep = ftl->unit[x]; + *bp = v & 0xFFFF; + return 1; + } + return 0; +} + +static void +eraseinit(Ftl *ftl, ulong offset, int id, int nerase) +{ + union { + Merase; + uchar block[ERASEHDRLEN]; + } *m; + uchar *bam, *p; + int i, nov; + + nov = ((ftl->segsize/Bsize)*4 + BAMoffset + Bsize - 1)/Bsize; /* number of overhead blocks (header, and BAM itself) */ + if(nov*Bsize >= ftl->segsize) + error("ftl -- too small for files"); + eraseflash(ftl, offset); + m = malloc(sizeof(*m)); + if(m == nil) + error(Enomem); + memset(m, 0xFF, sizeof(*m)); + m->linktuple[0] = 0x13; + m->linktuple[1] = 0x3; + memmove(m->linktuple+2, "CIS", 3); + m->orgtuple[0] = 0x46; + m->orgtuple[1] = 0x57; + m->orgtuple[2] = 0x00; + memmove(m->orgtuple+3, "FTL100", 7); + m->nxfer = 1; + PUT4(m->nerase, nerase); + PUT2(m->id, id); + m->bshift = ftl->bshift; + m->eshift = ftl->eshift; + PUT2(m->pstart, ftl->base >> ftl->eshift); + PUT2(m->nunits, ftl->nunit); + PUT4(m->psize, ftl->size - nov*Bsize*ftl->nunit); + PUT4(m->vbmbase, 0xffffffff); /* we always calculate the VBM */ + PUT2(m->nvbm, 0); + m->flags = 0; + m->code = 0xFF; + memmove(m->serial, "Inf1", 4); + PUT4(m->altoffset, 0); + PUT4(m->bamoffset, BAMoffset); + putflash(ftl, offset, m, ERASEHDRLEN); + free(m); + if(id == XferID) + return; + nov *= 4; /* now bytes of BAM */ + bam = malloc(nov); + if(bam == nil) + error(Enomem); + for(i=0; i<nov; i += 4){ + p = bam+i; + PUT4(p, ControlBlock); /* reserve them */ + } + putflash(ftl, offset+BAMoffset, bam, nov); + free(bam); +} + +static int +erasedetect(Ftl *ftl, ulong base, ulong size, ushort *pstart, ushort *nunits) +{ + ulong o; + int rv; + + union { + Merase; + uchar block[ERASEHDRLEN]; + } *m; + m = malloc(sizeof(*m)); + if(m == nil) + error(Enomem); + rv = 0; + for(o = base; o < base + size; o += ftl->segsize){ + if(waserror()) + continue; + getflash(ftl, m, o, ERASEHDRLEN); + poperror(); + if(memcmp(m->orgtuple + 3, "FTL100", 7) == 0 + && memcmp(m->serial, "Inf1", 4) == 0){ + *pstart = GET2(m->pstart); + *nunits = GET2(m->nunits); + rv = 1; + break; + } + } + free(m); + return rv; +} + +static Terase * +eraseload(Ftl *ftl, int x, ulong offset) +{ + union { + Merase; + uchar block[ERASEHDRLEN]; + } *m; + Terase *e; + uchar *p; + int i, nbam; + ulong bno, v; + + m = malloc(sizeof(*m)); + if(m == nil) + error(Enomem); + if(waserror()){ + free(m); + return nil; + } + getflash(ftl, m, offset, ERASEHDRLEN); + poperror(); + if(memcmp(m->orgtuple+3, "FTL100", 7) != 0 || + memcmp(m->serial, "Inf1", 4) != 0){ + free(m); +print("%8.8lux: bad sig\n", offset); + return nil; + } + e = malloc(sizeof(*e)); + if(e == nil){ + free(m); + error(Enomem); + } + e->x = x; + e->id = GET2(m->id); + e->offset = offset; + e->bamoffset = GET4(m->bamoffset); + e->nerase = GET4(m->nerase); + free(m); + m = nil; + USED(m); + if(e->bamoffset != BAMoffset){ + free(e); +print("%8.8lux: bad bamoffset %8.8lux\n", offset, e->bamoffset); + return nil; + } + e->bamoffset += offset; + if(e->id == XferID || e->id == XferBusy){ + e->bam = nil; + e->nbam = 0; + return e; + } + nbam = ftl->segsize/Bsize; + e->bam = malloc(nbam*sizeof(*e->bam)); + e->nbam = nbam; + if(waserror()){ + free(e); + nexterror(); + } + getflash(ftl, e->bam, e->bamoffset, nbam*4); + poperror(); + /* scan BAM to build VBM */ + e->bamx = 0; + for(i=0; i<nbam; i++){ + p = (uchar*)&e->bam[i]; + e->bam[i] = v = GET4(p); + if(v == Bwriting || v == Bdeleted) + e->ndead++; + else if(v == Bfree){ + if(e->bamx == 0) + e->bamx = i; + e->nfree++; + ftl->nfree++; + }else{ + switch(v & BlockType){ + case ControlBlock: + break; + case DataBlock: + /* add to VBM */ + if(v & (1<<31)) + break; /* negative => VBM page, ignored */ + bno = BNO(v & ~BlockType); + if(i < ftl->fstart || bno >= ftl->nblock){ + print("ftl: unit %d:#%x bad bam[%d]=#%lux\n", e->x, e->id, i, v); + e->nbad++; + break; + } + ftl->vbm[bno] = (e->x<<16) | i; + e->nused++; + break; + case ReplacePage: + /* replacement VBM page; ignored */ + break; + default: + print("ftl: unit %d:#%x bad bam[%d]=%lux\n", e->x, e->id, i, v); + case BadBlock: + e->nbad++; + break; + } + } + } + return e; +} + +static void +erasefree(Terase *e) +{ + if(e){ + free(e->bam); + free(e); + } +} + +static void +eraseflash(Ftl *ftl, ulong offset) +{ + char cmd[40]; + + offset += ftl->base; + if(FTLDEBUG || ftl->trace) + print("ftl: erase seg @#%lux\n", offset); + snprint(cmd, sizeof(cmd), "erase 0x%8.8lux", offset); + if(kchanio(ftl->flashctl, cmd, strlen(cmd), OWRITE) <= 0){ + print("ftl: erase failed: %s\n", up->env->errstr); + error(up->env->errstr); + } +} + +static void +putflash(Ftl *ftl, ulong offset, void *buf, long n) +{ + offset += ftl->base; + if(ftl->trace) + print("ftl: write(#%lux, %ld)\n", offset, n); + ftl->flash->offset = offset; + if(kchanio(ftl->flash, buf, n, OWRITE) != n){ + print("ftl: flash write error: %s\n", up->env->errstr); + error(up->env->errstr); + } +} + +static void +getflash(Ftl *ftl, void *buf, ulong offset, long n) +{ + offset += ftl->base; + if(ftl->trace) + print("ftl: read(#%lux, %ld)\n", offset, n); + ftl->flash->offset = offset; + if(kchanio(ftl->flash, buf, n, OREAD) != n){ + print("ftl: flash read error %s\n", up->env->errstr); + error(up->env->errstr); + } +} + +Dev ftldevtab = { + 'X', /* TO DO */ + "ftl", + + devreset, + devinit, + devshutdown, + ftlattach, + ftlwalk, + ftlstat, + ftlopen, + devcreate, + ftlclose, + ftlread, + devbread, + ftlwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devi2c.c b/os/port/devi2c.c new file mode 100644 index 00000000..50512248 --- /dev/null +++ b/os/port/devi2c.c @@ -0,0 +1,227 @@ +/* + * i2c + * + * Copyright © 1998, 2003 Vita Nuova Limited. + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +typedef struct I2Cdir I2Cdir; + +enum{ + Qdir, + Qdata, + Qctl, +}; + +static +Dirtab i2ctab[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "i2cdata", {Qdata, 0}, 256, 0660, + "i2cctl", {Qctl, 0}, 0, 0660, +}; + +struct I2Cdir { + Ref; + I2Cdev; + Dirtab tab[nelem(i2ctab)]; +}; + +static void +i2creset(void) +{ + i2csetup(0); +} + +static Chan* +i2cattach(char* spec) +{ + char *s; + ulong addr; + I2Cdir *d; + Chan *c; + + addr = strtoul(spec, &s, 16); + if(*spec == 0 || *s || addr >= (1<<10)) + error("invalid i2c address"); + d = malloc(sizeof(I2Cdir)); + if(d == nil) + error(Enomem); + d->ref = 1; + d->addr = addr; + d->salen = 0; + d->tenbit = addr >= 128; + memmove(d->tab, i2ctab, sizeof(d->tab)); + sprint(d->tab[1].name, "i2c.%lux.data", addr); + sprint(d->tab[2].name, "i2c.%lux.ctl", addr); + + c = devattach('J', spec); + c->aux = d; + return c; +} + +static Walkqid* +i2cwalk(Chan* c, Chan *nc, char **name, int nname) +{ + Walkqid *wq; + I2Cdir *d; + + d = c->aux; + wq = devwalk(c, nc, name, nname, d->tab, nelem(d->tab), devgen); + if(wq != nil && wq->clone != nil && wq->clone != c) + incref(d); + return wq; +} + +static int +i2cstat(Chan* c, uchar *dp, int n) +{ + I2Cdir *d; + + d = c->aux; + return devstat(c, dp, n, d->tab, nelem(d->tab), devgen); +} + +static Chan* +i2copen(Chan* c, int omode) +{ + I2Cdir *d; + + d = c->aux; + return devopen(c, omode, d->tab, nelem(d->tab), devgen); +} + +static void +i2cclose(Chan *c) +{ + I2Cdir *d; + + d = c->aux; + if(decref(d) == 0) + free(d); +} + +static long +i2cread(Chan *c, void *a, long n, vlong offset) +{ + I2Cdir *d; + char *s, *e; + ulong len; + + d = c->aux; + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, d->tab, nelem(d->tab), devgen); + case Qdata: + len = d->tab[1].length; + if(offset+n >= len){ + n = len - offset; + if(n <= 0) + return 0; + } + n = i2crecv(d, a, n, offset); + break; + case Qctl: + s = smalloc(READSTR); + if(waserror()){ + free(s); + nexterror(); + } + e = seprint(s, s+READSTR, "size %lud\n", (ulong)d->tab[1].length); + if(d->salen) + e = seprint(e, s+READSTR, "subaddress %d\n", d->salen); + if(d->tenbit) + seprint(e, s+READSTR, "a10\n"); + n = readstr(offset, a, n, s); + poperror(); + free(s); + return n; + default: + n=0; + break; + } + return n; +} + +static long +i2cwrite(Chan *c, void *a, long n, vlong offset) +{ + I2Cdir *d; + long len; + Cmdbuf *cb; + + USED(offset); + switch((ulong)c->qid.path){ + case Qdata: + d = c->aux; + len = d->tab[1].length; + if(offset+n >= len){ + n = len - offset; + if(n <= 0) + return 0; + } + n = i2csend(d, a, n, offset); + break; + case Qctl: + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + if(cb->nf < 1) + error(Ebadctl); + d = c->aux; + if(strcmp(cb->f[0], "subaddress") == 0){ + if(cb->nf > 1){ + len = strtol(cb->f[1], nil, 0); + if(len <= 0) + len = 0; + if(len > 4) + cmderror(cb, "subaddress too long"); + }else + len = 1; + d->salen = len; + }else if(cb->nf > 1 && strcmp(cb->f[0], "size") == 0){ + len = strtol(cb->f[1], nil, 0); + if(len < 0) + cmderror(cb, "size is negative"); + d->tab[1].length = len; + }else if(strcmp(cb->f[0], "a10") == 0) + d->tenbit = 1; + else + cmderror(cb, "unknown control request"); + poperror(); + free(cb); + break; + default: + error(Ebadusefd); + } + return n; +} + +Dev i2cdevtab = { + 'J', + "i2c", + + i2creset, + devinit, + devshutdown, + i2cattach, + i2cwalk, + i2cstat, + i2copen, + devcreate, + i2cclose, + i2cread, + devbread, + i2cwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devindir.c b/os/port/devindir.c new file mode 100644 index 00000000..9f713bbc --- /dev/null +++ b/os/port/devindir.c @@ -0,0 +1,40 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +static Chan * +indirattach(char *spec) +{ + char *p; + Dev *d; + + if(*spec == 0) + error(Ebadspec); + p = strrchr(spec, '!'); + if(p == nil) + p = ""; + else + *p++ = 0; + d = devbyname(spec); + if(d == nil || d->dc == '*'){ + snprint(up->env->errstr, ERRMAX, "unknown device: %s", spec); + error(up->env->errstr); + } + if(up->env->pgrp->nodevs && + (utfrune("|esDa", d->dc) == nil || d->dc == 's' && *p!='\0')) + error(Enoattach); + return d->attach(p); +} + +Dev indirdevtab = { + '*', + "indir", + + devreset, + devinit, + devshutdown, + indirattach, +}; diff --git a/os/port/devkprof.c b/os/port/devkprof.c new file mode 100644 index 00000000..134f346a --- /dev/null +++ b/os/port/devkprof.c @@ -0,0 +1,230 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#ifndef LRES +#define LRES 3 /* log of PC resolution */ +#endif + +#define SZ 4 /* sizeof of count cell; well known as 4 */ + +enum { + SpecialTotalTicks, + SpecialOutsideTicks, + SpecialMicroSecondsPerTick, + SpecialSamples, + SpecialSampleSize, + SpecialSampleLogBucketSize, + SpecialMax +}; + +struct +{ + int minpc; + int maxpc; + int nbuf; + int time; + ulong *buf; +}kprof; + +enum{ + Qdir, + Qdata, + Qctl, + Kprofmaxqid, +}; + +Dirtab kproftab[]={ + ".", {Qdir, 0, QTDIR}, 0, 0500, + "kpdata", {Qdata}, 0, 0600, + "kpctl", {Qctl}, 0, 0600, +}; + +void kproftimer(ulong); +void (*kproftick)(ulong); + +static void +kprofinit(void) +{ + if(SZ != sizeof kprof.buf[0]) + panic("kprof size"); +} + +static void +kprofbufinit(void) +{ + kprof.buf[SpecialMicroSecondsPerTick] = archkprofmicrosecondspertick(); + kprof.buf[SpecialSamples] = kprof.nbuf; + kprof.buf[SpecialSampleSize] = SZ; + kprof.buf[SpecialSampleLogBucketSize] = LRES; +} + +static Chan * +kprofattach(char *spec) +{ + ulong n; + + /* allocate when first used */ + kproftick = kproftimer; + kprof.minpc = KTZERO; + kprof.maxpc = (ulong)etext; + kprof.nbuf = (kprof.maxpc-kprof.minpc) >> LRES; + n = kprof.nbuf*SZ; + if(kprof.buf == 0) { + kprof.buf = xalloc(n); + if(kprof.buf == 0) + error(Enomem); + } + kproftab[0].length = n; + kprofbufinit(); + return devattach('K', spec); +} + +static Walkqid* +kprofwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, kproftab, nelem(kproftab), devgen); +} + +static int +kprofstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, kproftab, nelem(kproftab), devgen); +} + +static Chan * +kprofopen(Chan *c, int omode) +{ + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Eperm); + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +void +kprofclose(Chan*) +{ +} + +static long +kprofread(Chan *c, void *va, long n, vlong offset) +{ + ulong tabend; + ulong w, *bp; + uchar *a, *ea; + + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, va, n, kproftab, nelem(kproftab), devgen); + + case Qdata: + tabend = kprof.nbuf*SZ; + if(offset & (SZ-1)) + error(Ebadarg); + if(offset >= tabend){ + n = 0; + break; + } + if(offset+n > tabend) + n = tabend-offset; + n &= ~(SZ-1); + a = va; + ea = a + n; + bp = kprof.buf + offset/SZ; + while(a < ea){ + w = *bp++; + *a++ = w>>24; + *a++ = w>>16; + *a++ = w>>8; + *a++ = w>>0; + } + break; + + default: + n = 0; + break; + } + return n; +} + +static long +kprofwrite(Chan *c, void *vp, long n, vlong offset) +{ + char *a; + USED(offset); + + a = vp; + switch((ulong)c->qid.path){ + case Qctl: + if(strncmp(a, "startclr", 8) == 0){ + memset((char *)kprof.buf, 0, kprof.nbuf*SZ); + kprofbufinit(); + archkprofenable(1); + kprof.time = 1; + }else if(strncmp(a, "start", 5) == 0) { + archkprofenable(1); + kprof.time = 1; + } + else if(strncmp(a, "stop", 4) == 0) { + archkprofenable(0); + kprof.time = 0; + } + else + error(Ebadctl); + break; + default: + error(Ebadusefd); + } + return n; +} + +void +kproftimer(ulong pc) +{ + extern void spldone(void); + + if(kprof.time == 0) + return; + /* + * if the pc is coming out of spllo or splx, + * use the pc saved when we went splhi. + */ +// if(pc>=(ulong)splx && pc<=(ulong)spldone) +// pc = m->splpc; + + kprof.buf[SpecialTotalTicks]++; + if(kprof.minpc + (SpecialMax << LRES) <= pc && pc < kprof.maxpc){ + pc -= kprof.minpc; + pc >>= LRES; + kprof.buf[pc]++; + }else + kprof.buf[SpecialOutsideTicks]++; +} + +Dev kprofdevtab = { + 'K', + "kprof", + + devreset, + kprofinit, + devshutdown, + kprofattach, + kprofwalk, + kprofstat, + kprofopen, + devcreate, + kprofclose, + kprofread, + devbread, + kprofwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devlogfs.c b/os/port/devlogfs.c new file mode 100755 index 00000000..630d3eba --- /dev/null +++ b/os/port/devlogfs.c @@ -0,0 +1,1531 @@ +#ifndef EMU +#include "u.h" +#include "../port/lib.h" +#include "../port/error.h" +#else +#include "error.h" +#endif +#include "dat.h" +#include "fns.h" +#include "kernel.h" +#include "logfs.h" +#include "nandfs.h" + +#ifndef EMU +#define Sleep sleep +#define Wakeup wakeup +#endif + +#ifndef offsetof +#define offsetof(T,X) ((ulong)&(((T*)0)->X)) +#endif + +typedef struct Devlogfs Devlogfs; +typedef struct DevlogfsSession DevlogfsSession; + +//#define CALLTRACE + +enum { + DEVLOGFSDEBUG = 0, + DEVLOGFSIODEBUG = 0, + DEVLOGFSBAD = 1, +}; + +enum { + Qdir, + Qctl, + Qusers, + Qdump, + Qfs, + Qfsboot, + Qend, +}; + +typedef enum DevlogfsServerState { Closed, BootOpen, NeedVersion, NeedAttach, Attached, Hungup } DevlogfsServerState; + +struct Devlogfs { + QLock qlock; + QLock rlock; + QLock wlock; + Ref ref; + int instance; + int trace; /* (debugging) trace of read/write actions */ + int nand; + char *name; + char *device; + char *filename[Qend - Qfs]; + LogfsLowLevel *ll; + Chan *flash, *flashctl; + QLock bootqlock; + int logfstrace; + LogfsBoot *lb; + /* stuff for server */ + ulong openflags; + Fcall in; + Fcall out; + int reading; + DevlogfsServerState state; + Rendez readrendez; + Rendez writerendez; + uint readcount; + ulong readbufsize; + uchar *readbuf; + uchar *readp; + LogfsServer *server; + Devlogfs *next; +}; + +#define MAXMSIZE 8192 + +static struct { + RWlock rwlock; /* rlock when walking, wlock when changing */ + QLock configqlock; /* serialises addition of new configurations */ + Devlogfs *head; + char *defname; +} devlogfslist; + +static LogfsIdentityStore *is; + +#ifndef EMU +char Eunknown[] = "unknown user or group id"; +#endif + +static void devlogfsfree(Devlogfs*); + +#define SPLITPATH(path, qtype, instance, qid, qt) { instance = path >> 4; qid = path & 0xf; qt = qtype & QTDIR; } +#define DATAQID(q, qt) (!(qt) && (q) >= Qfs && (q) < Qend) +#define MKPATH(instance, qid) ((instance << 4) | qid) + +#define PREFIX "logfs" + +static char *devlogfsprefix = PREFIX; +static char *devlogfsctlname = PREFIX "ctl"; +static char *devlogfsusersname = PREFIX "users"; +static char *devlogfsdumpname = PREFIX "dump"; +static char *devlogfsbootsuffix = "boot"; +static char *devlogfs9pversion = "9P2000"; + +static void +errorany(char *errmsg) +{ + if (errmsg) + error(errmsg); +} + +static void * +emalloc(ulong size) +{ + void *p; + p = logfsrealloc(nil, size); + if (p == nil) + error(Enomem); + return p; +} + +static char * +estrdup(char *q) +{ + void *p; + if (q == nil) + return nil; + p = logfsrealloc(nil, strlen(q) + 1); + if (p == nil) + error(Enomem); + return strcpy(p, q); +} + +static char * +estrconcat(char *a, ...) +{ + va_list l; + char *p, *r; + int t; + + t = strlen(a); + va_start(l, a); + while ((p = va_arg(l, char *)) != nil) + t += strlen(p); + + r = logfsrealloc(nil, t + 1); + if (r == nil) + error(Enomem); + + strcpy(r, a); + va_start(l, a); + while ((p = va_arg(l, char *)) != nil) + strcat(r, p); + + va_end(l); + + return r; +} + +static int +gen(Chan *c, int i, Dir *dp, int lockit) +{ + Devlogfs *l; + long size; + Qid qid; + qid.vers = 0; + qid.type = 0; + + if (i + Qctl < Qfs) { + switch (i + Qctl) { + case Qctl: + qid.path = Qctl; + devdir(c, qid, devlogfsctlname, 0, eve, 0666, dp); + return 1; + case Qusers: + qid.path = Qusers; + devdir(c, qid, devlogfsusersname, 0, eve, 0444, dp); + return 1; + case Qdump: + qid.path = Qdump; + devdir(c, qid, devlogfsdumpname, 0, eve, 0444, dp); + return 1; + } + } + + i -= Qfs - Qctl; + + if (lockit) + rlock(&devlogfslist.rwlock); + + if (waserror()) { + if (lockit) + runlock(&devlogfslist.rwlock); + nexterror(); + } + + for (l = devlogfslist.head; l; l = l->next) { + if (i < Qend - Qfs) + break; + i -= Qend - Qfs; + } + + if (l == nil) { + poperror(); + if (lockit) + runlock(&devlogfslist.rwlock); + return -1; + } + + switch (Qfs + i) { + case Qfsboot: + size = l->lb ? logfsbootgetsize(l->lb) : 0; + break; + default: + size = 0; + break; + } + /* perhaps the user id should come from the underlying file */ + qid.path = MKPATH(l->instance, Qfs + i); + devdir(c, qid, l->filename[i], size, eve, 0666, dp); + + poperror(); + if (lockit) + runlock(&devlogfslist.rwlock); + + return 1; +} + +static int +devlogfsgen(Chan *c, char *n, Dirtab *tab, int ntab, int i, Dir *dp) +{ + USED(n); + USED(tab); + USED(ntab); + return gen(c, i, dp, 1); +} + +static int +devlogfsgennolock(Chan *c, char *n, Dirtab *tab, int ntab, int i, Dir *dp) +{ + USED(n); + USED(tab); + USED(ntab); + return gen(c, i, dp, 0); +} + +/* called under lock */ +static Devlogfs * +devlogfsfind(int instance) +{ + Devlogfs *l; + + for (l = devlogfslist.head; l; l = l->next) + if (l->instance == instance) + break; + return l; +} + +static Devlogfs * +devlogfsget(int instance) +{ + Devlogfs *l; + rlock(&devlogfslist.rwlock); + for (l = devlogfslist.head; l; l = l->next) + if (l->instance == instance) + break; + if (l) + incref(&l->ref); + runlock(&devlogfslist.rwlock); + return l; +} + +static Devlogfs * +devlogfsfindbyname(char *name) +{ + Devlogfs *l; + + rlock(&devlogfslist.rwlock); + for (l = devlogfslist.head; l; l = l->next) + if (strcmp(l->name, name) == 0) + break; + runlock(&devlogfslist.rwlock); + return l; +} + +static Devlogfs * +devlogfssetdefname(char *name) +{ + Devlogfs *l; + char *searchname; + wlock(&devlogfslist.rwlock); + if (waserror()) { + wunlock(&devlogfslist.rwlock); + nexterror(); + } + if (name == nil) + searchname = devlogfslist.defname; + else + searchname = name; + for (l = devlogfslist.head; l; l = l->next) + if (strcmp(l->name, searchname) == 0) + break; + if (l == nil) { + logfsfreemem(devlogfslist.defname); + devlogfslist.defname = nil; + } + else if (name) { + if (devlogfslist.defname) { + logfsfreemem(devlogfslist.defname); + devlogfslist.defname = nil; + } + devlogfslist.defname = estrdup(name); + } + poperror(); + wunlock(&devlogfslist.rwlock); + return l; +} + +static Chan * +devlogfskopen(char *name, char *suffix, int mode) +{ + Chan *c; + char *fn; + int fd; + + fn = estrconcat(name, suffix, 0); + fd = kopen(fn, mode); + logfsfreemem(fn); + if (fd < 0) + error(up->env->errstr); + c = fdtochan(up->env->fgrp, fd, mode, 0, 1); + kclose(fd); + return c; +} + +static char * +xread(void *a, void *buf, long nbytes, ulong offset) +{ + Devlogfs *l = a; + long rv; + + if (DEVLOGFSIODEBUG || l->trace) + print("devlogfs: %s: read(0x%lux, %ld)\n", l->device, offset, nbytes); + l->flash->offset = offset; + rv = kchanio(l->flash, buf, nbytes, OREAD); + if (rv < 0) { + print("devlogfs: %s: flash read error: %s\n", l->device, up->env->errstr); + return up->env->errstr; + } + if (rv != nbytes) { + print("devlogfs: %s: short flash read: offset %lud, %ld not %ld\n", l->device, offset, rv, nbytes); + return "short read"; + } + return nil; +} + +static char * +xwrite(void *a, void *buf, long nbytes, ulong offset) +{ + Devlogfs *l = a; + long rv; + + if (DEVLOGFSIODEBUG || l->trace) + print("devlogfs: %s: write(0x%lux, %ld)\n", l->device, offset, nbytes); + l->flash->offset = offset; + rv = kchanio(l->flash, buf, nbytes, OWRITE); + if (rv < 0) { + print("devlogfs: %s: flash write error: %s\n", l->device, up->env->errstr); + return up->env->errstr; + } + if (rv != nbytes) { + print("devlogfs: %s: short flash write: offset %lud, %ld not %ld\n", l->device, offset, rv, nbytes); + return "short write"; + } + return nil; +} + +static char * +xerase(void *a, long address) +{ + Devlogfs *l = a; + char cmd[40]; + + if (DEVLOGFSIODEBUG || l->trace) + print("devlogfs: %s: erase(0x%lux)\n", l->device, address); + snprint(cmd, sizeof(cmd), "erase 0x%8.8lux", address); + if (kchanio(l->flashctl, cmd, strlen(cmd), OWRITE) <= 0) { + print("devlogfs: %s: flash erase error: %s\n", l->device, up->env->errstr); + return up->env->errstr; + } + return nil; +} + +static char * +xsync(void *a) +{ + Devlogfs *l = a; + + if (DEVLOGFSIODEBUG || l->trace) + print("devlogfs: %s: sync()\n", l->device); + if (kchanio(l->flashctl, "sync", 4, OWRITE) <= 0){ + print("devlogfs: %s: flash sync error: %s\n", l->device, up->env->errstr); + return up->env->errstr; + } + return nil; +} + +//#define LEAKHUNT +#ifdef LEAKHUNT +#define MAXLIVE 2000 +typedef struct Live { + void *p; + int freed; + ulong callerpc; +} Live; + +static Live livemem[MAXLIVE]; + +static void +leakalloc(void *p, ulong callerpc) +{ + int x; + int use = -1; + for (x = 0; x < MAXLIVE; x++) { + if (livemem[x].p == p) { + if (!livemem[x].freed) + print("leakalloc: unexpected realloc of 0x%.8lux from 0x%.8lux\n", p, callerpc); +// else +// print("leakalloc: reusing address 0x%.8lux from 0x%.8lux\n", p, callerpc); + livemem[x].freed = 0; + livemem[x].callerpc = callerpc; + return; + } + else if (use < 0 && livemem[x].p == 0) + use = x; + } + if (use < 0) + panic("leakalloc: too many live entries"); + livemem[use].p = p; + livemem[use].freed = 0; + livemem[use].callerpc = callerpc; +} + +static void +leakaudit(void) +{ + int x; + for (x = 0; x < MAXLIVE; x++) { + if (livemem[x].p && !livemem[x].freed) + print("leakaudit: 0x%.8lux from 0x%.8lux\n", livemem[x].p, livemem[x].callerpc); + } +} + +static void +leakfree(void *p, ulong callerpc) +{ + int x; + if (p == nil) + return; + for (x = 0; x < MAXLIVE; x++) { + if (livemem[x].p == p) { + if (livemem[x].freed) + print("leakfree: double free of 0x%.8lux from 0x%.8lux, originally by 0x%.8lux\n", + p, callerpc, livemem[x].callerpc); + livemem[x].freed = 1; + livemem[x].callerpc = callerpc; + return; + } + } + print("leakfree: free of unalloced address 0x%.8lux from 0x%.8lux\n", p, callerpc); + leakaudit(); +} + +static void +leakrealloc(void *newp, void *oldp, ulong callerpc) +{ + leakfree(oldp, callerpc); + leakalloc(newp, callerpc); +} +#endif + + +#ifdef LEAKHUNT +static void *_realloc(void *p, ulong size, ulong callerpc) +#else +void * +logfsrealloc(void *p, ulong size) +#endif +{ + void *q; + ulong osize; + if (waserror()) { + print("wobbly thrown in memory allocator: %s\n", up->env->errstr); + nexterror(); + } + if (p == nil) { + q = smalloc(size); + poperror(); +#ifdef LEAKHUNT + leakrealloc(q, nil, callerpc); +#endif + return q; + } + q = realloc(p, size); + if (q) { + poperror(); +#ifdef LEAKHUNT + leakrealloc(q, p, callerpc); +#endif + return q; + } + q = smalloc(size); + osize = msize(p); + if (osize > size) + osize = size; + memmove(q, p, osize); + free(p); + poperror(); +#ifdef LEAKHUNT + leakrealloc(q, p, callerpc); +#endif + return q; +} + +#ifdef LEAKHUNT +void * +logfsrealloc(void *p, ulong size) +{ + return _realloc(p, size, getcallerpc(&p)); +} + +void * +nandfsrealloc(void *p, ulong size) +{ + return _realloc(p, size, getcallerpc(&p)); +} +#else +void * +nandfsrealloc(void *p, ulong size) +{ + return logfsrealloc(p, size); +} +#endif + +void +logfsfreemem(void *p) +{ +#ifdef LEAKHUNT + leakfree(p, getcallerpc(&p)); +#endif + free(p); +} + +void +nandfsfreemem(void *p) +{ +#ifdef LEAKHUNT + leakfree(p, getcallerpc(&p)); +#endif + free(p); +} + +static Devlogfs * +devlogfsconfig(char *name, char *device) +{ + Devlogfs *newl, *l; + int i; + int n; + char buf[100], *fields[12]; + long rawblocksize, rawsize; + + newl = nil; + + qlock(&devlogfslist.configqlock); + + if (waserror()) { + qunlock(&devlogfslist.configqlock); + devlogfsfree(newl); + nexterror(); + } + + rlock(&devlogfslist.rwlock); + for (l = devlogfslist.head; l; l = l->next) + if (strcmp(l->name, name) == 0) { + runlock(&devlogfslist.rwlock); + error(Einuse); + } + + /* horrid n^2 solution to finding a unique instance number */ + + for (i = 0;; i++) { + for (l = devlogfslist.head; l; l = l->next) + if (l->instance == i) + break; + if (l == nil) + break; + } + runlock(&devlogfslist.rwlock); + + newl = emalloc(sizeof(Devlogfs)); + newl->instance = i; + newl->name = estrdup(name); + newl->device = estrdup(device); + newl->filename[Qfs - Qfs] = estrconcat(devlogfsprefix, name, nil); + newl->filename[Qfsboot - Qfs] = estrconcat(devlogfsprefix, name, devlogfsbootsuffix, nil); + newl->flash = devlogfskopen(device, nil, ORDWR); + newl->flashctl = devlogfskopen(device, "ctl", ORDWR); + newl->flashctl->offset = 0; + if ((n = kchanio(newl->flashctl, buf, sizeof(buf), OREAD)) <= 0) { + print("devlogfsconfig: read ctl failed: %s\n", up->env->errstr); + error(up->env->errstr); + } + + if (n >= sizeof(buf)) + n = sizeof(buf) - 1; + buf[n] = 0; + n = tokenize(buf, fields, nelem(fields)); + if(n < 7) + error("unexpected flashctl format"); + newl->nand = strcmp(fields[3], "nand") == 0; + rawblocksize = strtol(fields[6], nil, 0); + rawsize = strtol(fields[5], nil, 0)-strtol(fields[4], nil, 0); + if(newl->nand == 0) + error("only NAND supported at the moment"); + errorany(nandfsinit(newl, rawsize, rawblocksize, xread, xwrite, xerase, xsync, &newl->ll)); + wlock(&devlogfslist.rwlock); + newl->next = devlogfslist.head; + devlogfslist.head = newl; + logfsfreemem(devlogfslist.defname); + devlogfslist.defname = nil; + if (!waserror()){ + devlogfslist.defname = estrdup(name); + poperror(); + } + wunlock(&devlogfslist.rwlock); + poperror(); + qunlock(&devlogfslist.configqlock); + return newl; +} + +static void +devlogfsunconfig(Devlogfs *devlogfs) +{ + Devlogfs **lp; + + qlock(&devlogfslist.configqlock); + + if (waserror()) { + qunlock(&devlogfslist.configqlock); + nexterror(); + } + + wlock(&devlogfslist.rwlock); + + if (waserror()) { + wunlock(&devlogfslist.rwlock); + nexterror(); + } + + for (lp = &devlogfslist.head; *lp && (*lp) != devlogfs; lp = &(*lp)->next) + ; + if (*lp == nil) { + if (DEVLOGFSBAD) + print("devlogfsunconfig: not in list\n"); + } + else + *lp = devlogfs->next; + + poperror(); + wunlock(&devlogfslist.rwlock); + + /* now invisible to the naked eye */ + devlogfsfree(devlogfs); + poperror(); + qunlock(&devlogfslist.configqlock); +} + +static void +devlogfsllopen(Devlogfs *l) +{ + qlock(&l->qlock); + if (waserror()) { + qunlock(&l->qlock); + nexterror(); + } + if (l->lb == nil) + errorany(logfsbootopen(l->ll, 0, 0, l->logfstrace, 1, &l->lb)); + l->state = BootOpen; + poperror(); + qunlock(&l->qlock); +} + +static void +devlogfsllformat(Devlogfs *l, long bootsize) +{ + qlock(&l->qlock); + if (waserror()) { + qunlock(&l->qlock); + nexterror(); + } + if (l->lb == nil) + errorany(logfsformat(l->ll, 0, 0, bootsize, l->logfstrace)); + poperror(); + qunlock(&l->qlock); +} + +static Chan * +devlogfsattach(char *spec) +{ + Chan *c; +#ifdef CALLTRACE + print("devlogfsattach(spec = %s) - start\n", spec); +#endif + /* create the identity store on first attach */ + if (is == nil) + errorany(logfsisnew(&is)); + c = devattach(0x29f, spec); +// c = devattach(L'ʟ', spec); +#ifdef CALLTRACE + print("devlogfsattach(spec = %s) - return %.8lux\n", spec, (ulong)c); +#endif + return c; +} + +static Walkqid* +devlogfswalk(Chan *c, Chan *nc, char **name, int nname) +{ + int instance, qid, qt, clone; + Walkqid *wq; + +#ifdef CALLTRACE + print("devlogfswalk(c = 0x%.8lux, nc = 0x%.8lux, name = 0x%.8lux, nname = %d) - start\n", + (ulong)c, (ulong)nc, (ulong)name, nname); +#endif + clone = 0; + if(nc == nil){ + nc = devclone(c); + nc->type = 0; + SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt); + if(DATAQID(qid, qt)) + nc->aux = devlogfsget(instance); + clone = 1; + } + wq = devwalk(c, nc, name, nname, 0, 0, devlogfsgen); + if (wq == nil || wq->nqid < nname) { + if(clone) + cclose(nc); + } + else if (clone) { + wq->clone = nc; + nc->type = c->type; + } +#ifdef CALLTRACE + print("devlogfswalk(c = 0x%.8lux, nc = 0x%.8lux, name = 0x%.8lux, nname = %d) - return\n", + (ulong)c, (ulong)nc, (ulong)name, nname); +#endif + return wq; +} + +static int +devlogfsstat(Chan *c, uchar *dp, int n) +{ +#ifdef CALLTRACE + print("devlogfsstat(c = 0x%.8lux, dp = 0x%.8lux n= %d)\n", + (ulong)c, (ulong)dp, n); +#endif + return devstat(c, dp, n, 0, 0, devlogfsgen); +} + +static Chan* +devlogfsopen(Chan *c, int omode) +{ + int instance, qid, qt; + + omode = openmode(omode); + SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt); +#ifdef CALLTRACE + print("devlogfsopen(c = 0x%.8lux, omode = %o, instance = %d, qid = %d, qt = %d)\n", + (ulong)c, omode, instance, qid, qt); +#endif + + + rlock(&devlogfslist.rwlock); + if (waserror()) { + runlock(&devlogfslist.rwlock); +#ifdef CALLTRACE + print("devlogfsopen(c = 0x%.8lux, omode = %o) - error %s\n", (ulong)c, omode, up->env->errstr); +#endif + nexterror(); + } + + if (DATAQID(qid, qt)) { + Devlogfs *d; + d = devlogfsfind(instance); + if (d == nil) + error(Enodev); + if (strcmp(up->env->user, eve) != 0) + error(Eperm); + if (qid == Qfs && d->state != BootOpen) + error(Eperm); + if (d->server == nil) { + errorany(logfsservernew(d->lb, d->ll, is, d->openflags, d->logfstrace, &d->server)); + d->state = NeedVersion; + } + c = devopen(c, omode, 0, 0, devlogfsgennolock); + incref(&d->ref); + c->aux = d; + } + else if (qid == Qctl || qid == Qusers) { + if (strcmp(up->env->user, eve) != 0) + error(Eperm); + c = devopen(c, omode, 0, 0, devlogfsgennolock); + } + else + c = devopen(c, omode, 0, 0, devlogfsgennolock); + poperror(); + runlock(&devlogfslist.rwlock); +#ifdef CALLTRACE + print("devlogfsopen(c = 0x%.8lux, omode = %o) - return\n", (ulong)c, omode); +#endif + return c; +} + +static void +devlogfsclose(Chan *c) +{ + int instance, qid, qt; +#ifdef CALLTRACE + print("devlogfsclose(c = 0x%.8lux)\n", (ulong)c); +#endif + SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt); + USED(instance); + if(DATAQID(qid, qt) && (c->flag & COPEN) != 0) { + Devlogfs *d; + d = c->aux; + qlock(&d->qlock); + if (qid == Qfs && d->state == Attached) { + logfsserverflush(d->server); + logfsserverfree(&d->server); + d->state = BootOpen; + } + qunlock(&d->qlock); + decref(&d->ref); + } +#ifdef CALLTRACE + print("devlogfsclose(c = 0x%.8lux) - return\n", (ulong)c); +#endif +} + +typedef char *(SMARTIOFN)(void *magic, void *buf, long n, ulong offset, int write); + +static void +smartio(SMARTIOFN *io, void *magic, void *buf, long n, ulong offset, long blocksize, int write) +{ + void *tmp = nil; + ulong blocks, toread; + + if (waserror()) { + logfsfreemem(tmp); + nexterror(); + } + if (offset % blocksize) { + ulong aoffset; + int tmpoffset; + int tocopy; + + if (tmp == nil) + tmp = emalloc(blocksize); + aoffset = offset / blocksize; + aoffset *= blocksize; + errorany((*io)(magic, tmp, blocksize, aoffset, 0)); + tmpoffset = offset - aoffset; + tocopy = blocksize - tmpoffset; + if (tocopy > n) + tocopy = n; + if (write) { + memmove((uchar *)tmp + tmpoffset, buf, tocopy); + errorany((*io)(magic, tmp, blocksize, aoffset, 1)); + } + else + memmove(buf, (uchar *)tmp + tmpoffset, tocopy); + buf = (uchar *)buf + tocopy; + n -= tocopy; + offset = aoffset + blocksize; + } + blocks = n / blocksize; + toread = blocks * blocksize; + errorany((*io)(magic, buf, toread, offset, write)); + buf = (uchar *)buf + toread; + n -= toread; + offset += toread; + if (n) { + if (tmp == nil) + tmp = emalloc(blocksize); + errorany((*io)(magic, tmp, blocksize, offset, 0)); + if (write) { + memmove(tmp, buf, n); + errorany((*io)(magic, tmp, blocksize, offset, 1)); + } + memmove(buf, tmp, n); + } + poperror(); + logfsfreemem(tmp); +} + +static int +readok(void *a) +{ + Devlogfs *d = a; + return d->reading; +} + +static int +writeok(void *a) +{ + Devlogfs *d = a; + return !d->reading; +} + +static long +lfsrvread(Devlogfs *d, void *buf, long n) +{ + qlock(&d->rlock); + if(waserror()){ + qunlock(&d->rlock); + nexterror(); + } + if (d->state == Hungup) + error(Ehungup); + Sleep(&d->readrendez, readok, d); + if (n > d->readcount) + n = d->readcount; + memmove(buf, d->readp, n); + d->readp += n; + d->readcount -= n; + if (d->readcount == 0) { + d->reading = 0; + Wakeup(&d->writerendez); + } + poperror(); + qunlock(&d->rlock); + return n; +} + +static void +reply(Devlogfs *d) +{ + d->readp = d->readbuf; + d->readcount = convS2M(&d->out, d->readp, d->readbufsize); +//print("reply is %d bytes\n", d->readcount); + if (d->readcount == 0) + panic("logfs: reply: did not fit\n"); + d->reading = 1; + Wakeup(&d->readrendez); +} + +static void +rerror(Devlogfs *d, char *ename) +{ + d->out.type = Rerror; + d->out.ename = ename; + reply(d); +} + +static struct { + QLock qlock; + int (*read)(void *magic, Devlogfs *d, int line, char *buf, int buflen); + void *magic; + Devlogfs *d; + int line; +} dump; + +static void * +extentdumpinit(Devlogfs *d, int argc, char **argv) +{ + int *p; + ulong path; + u32int flashaddr, length; + long block; + int page, offset; + + if (argc != 1) + error(Ebadarg); + path = strtoul(argv[0], 0, 0); + errorany(logfsserverreadpathextent(d->server, path, 0, &flashaddr, &length, &block, &page, &offset)); + p = emalloc(sizeof(ulong)); + *p = path; + return p; +} + +static int +extentdumpread(void *magic, Devlogfs *d, int line, char *buf, int buflen) +{ + ulong *p = magic; + u32int flashaddr, length; + long block; + int page, offset; + USED(d); + errorany(logfsserverreadpathextent(d->server, *p, line, &flashaddr, &length, &block, &page, &offset)); + if (length == 0) + return 0; + return snprint(buf, buflen, "%.8ux %ud %ld %d %d\n", flashaddr, length, block, page, offset); +} + +static void +devlogfsdumpinit(Devlogfs *d, + void *(*init)(Devlogfs *d, int argc, char **argv), + int (*read)(void *magic, Devlogfs *d, int line, char *buf, int buflen), int argc, char **argv) +{ + qlock(&dump.qlock); + if (waserror()) { + qunlock(&dump.qlock); + nexterror(); + } + if (d) { + if (d->state < NeedVersion) + error("not mounted"); + qlock(&d->qlock); + if (waserror()) { + qunlock(&d->qlock); + nexterror(); + } + } + if (dump.magic) { + logfsfreemem(dump.magic); + dump.magic = nil; + } + dump.d = d; + dump.magic = (*init)(d, argc, argv); + dump.read = read; + dump.line = 0; + if (d) { + poperror(); + qunlock(&d->qlock); + } + poperror(); + qunlock(&dump.qlock); +} + +static long +devlogfsdumpread(char *buf, int buflen) +{ + char *tmp = nil; + long n; + qlock(&dump.qlock); + if (waserror()) { + logfsfreemem(tmp); + qunlock(&dump.qlock); + nexterror(); + } + if (dump.magic == nil) + error(Eio); + tmp = emalloc(READSTR); + if (dump.d) { + if (dump.d->state < NeedVersion) + error("not mounted"); + qlock(&dump.d->qlock); + if (waserror()) { + qunlock(&dump.d->qlock); + nexterror(); + } + } + n = (*dump.read)(dump.magic, dump.d, dump.line, tmp, READSTR); + if (n) { + dump.line++; + n = readstr(0, buf, buflen, tmp); + } + if (dump.d) { + poperror(); + qunlock(&dump.d->qlock); + } + logfsfreemem(tmp); + poperror(); + qunlock(&dump.qlock); + return n; +} + +static void +devlogfsserverlogsweep(Devlogfs *d, int justone) +{ + int didsomething; + if (d->state < NeedVersion) + error("not mounted"); + qlock(&d->qlock); + if (waserror()) { + qunlock(&d->qlock); + nexterror(); + } + errorany(logfsserverlogsweep(d->server, justone, &didsomething)); + poperror(); + qunlock(&d->qlock); +} + +static void +devlogfsserversync(Devlogfs *d) +{ + if (d->state < NeedVersion) + return; + qlock(&d->qlock); + if (waserror()) { + qunlock(&d->qlock); + nexterror(); + } + errorany(logfsserverflush(d->server)); + poperror(); + qunlock(&d->qlock); +} + +static void +lfssrvwrite(Devlogfs *d, void *buf, long n) +{ + volatile int locked = 0; + + qlock(&d->wlock); + if(waserror()){ + qunlock(&d->wlock); + nexterror(); + } + if (d->state == Hungup) + error(Ehungup); + Sleep(&d->writerendez, writeok, d); + if (convM2S(buf, n, &d->in) != n) { + /* + * someone is writing drivel; have nothing to do with them anymore + * most common cause; trying to mount authenticated + */ + d->state = Hungup; + error(Ehungup); + } + d->out.tag = d->in.tag; + d->out.fid = d->in.fid; + d->out.type = d->in.type + 1; + if (waserror()) { + if (locked) + qunlock(&d->qlock); + rerror(d, up->env->errstr); + goto Replied; + } + if (d->in.type != Tversion && d->in.type != Tattach) { + if (d->state != Attached) + error("must be attached"); + qlock(&d->qlock); + locked = 1; + } + switch (d->in.type) { + case Tauth: + error("no authentication needed"); + case Tversion: { + char *rversion; + if (d->state != NeedVersion) + error("unexpected Tversion"); + if (d->in.tag != NOTAG) + error("protocol botch"); + /* + * check the version string + */ + if (strcmp(d->in.version, devlogfs9pversion) != 0) + rversion = "unknown"; + else + rversion = devlogfs9pversion; + /* + * allocate the reply buffer + */ + d->readbufsize = d->in.msize; + if (d->readbufsize > MAXMSIZE) + d->readbufsize = MAXMSIZE; + d->readbuf = emalloc(d->readbufsize); + /* + * compose the Rversion + */ + d->out.msize = d->readbufsize; + d->out.version = rversion; + d->state = NeedAttach; + break; + } + case Tattach: + if (d->state != NeedAttach) + error("unexpected attach"); + if (d->in.afid != NOFID) + error("unexpected afid"); + errorany(logfsserverattach(d->server, d->in.fid, d->in.uname, &d->out.qid)); + d->state = Attached; + break; + case Tclunk: + errorany(logfsserverclunk(d->server, d->in.fid)); + break; + case Tcreate: + errorany(logfsservercreate(d->server, d->in.fid, d->in.name, d->in.perm, d->in.mode, &d->out.qid)); + d->out.iounit = d->readbufsize - 11; + break; + case Tflush: + break; + case Topen: + errorany(logfsserveropen(d->server, d->in.fid, d->in.mode, &d->out.qid)); + d->out.iounit = d->readbufsize - 11; + break; + case Tread: + d->out.data = (char *)d->readbuf + 11; + /* TODO - avoid memmove */ + errorany(logfsserverread(d->server, d->in.fid, d->in.offset, d->in.count, (uchar *)d->out.data, + d->readbufsize - 11, &d->out.count)); + break; + case Tremove: + errorany(logfsserverremove(d->server, d->in.fid)); + break; + case Tstat: + d->out.stat = d->readbuf + 9; + /* TODO - avoid memmove */ + errorany(logfsserverstat(d->server, d->in.fid, d->out.stat, d->readbufsize - 9, &d->out.nstat)); +// print("nstat %d\n", d->out.nstat); + break; + case Twalk: + errorany(logfsserverwalk(d->server, d->in.fid, d->in.newfid, + d->in.nwname, d->in.wname, &d->out.nwqid, d->out.wqid)); + break; + case Twrite: + errorany(logfsserverwrite(d->server, d->in.fid, d->in.offset, d->in.count, (uchar *)d->in.data, + &d->out.count)); + break; + case Twstat: + errorany(logfsserverwstat(d->server, d->in.fid, d->in.stat, d->in.nstat)); + break; + default: + print("lfssrvwrite: msg %d unimplemented\n", d->in.type); + error("unimplemented"); + } + poperror(); + if (locked) + qunlock(&d->qlock); + reply(d); +Replied: + poperror(); + qunlock(&d->wlock); +} + +static long +devlogfsread(Chan *c, void *buf, long n, vlong off) +{ + int instance, qid, qt; + + SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt); + USED(instance); +#ifdef CALLTRACE + print("devlogfsread(c = 0x%.8lux, buf = 0x%.8lux, n = %ld, instance = %d, qid = %d, qt = %d) - start\n", + (ulong)c, (ulong)buf, n, instance, qid, qt); +#endif + if(qt & QTDIR) { +#ifdef CALLTRACE + print("devlogfsread(c = 0x%.8lux, buf = 0x%.8lux, n = %ld, instance = %d, qid = %d, qt = %d) - calling devdirread\n", + (ulong)c, (ulong)buf, n, instance, qid, qt); +#endif + return devdirread(c, buf, n, 0, 0, devlogfsgen); + } + + if(DATAQID(qid, qt)) { + if (qid == Qfsboot) { + Devlogfs *l = c->aux; + qlock(&l->bootqlock); + if (waserror()) { + qunlock(&l->bootqlock); + nexterror(); + } + smartio((SMARTIOFN *)logfsbootio, l->lb, buf, n, off, logfsbootgetiosize(l->lb), 0); + poperror(); + qunlock(&l->bootqlock); + return n; + } + else if (qid == Qfs) { + Devlogfs *d = c->aux; + return lfsrvread(d, buf, n); + } + error(Eio); + } + + if (qid == Qusers) { + long nr; + errorany(logfsisusersread(is, buf, n, (ulong)off, &nr)); + return nr; + } + else if (qid == Qdump) + return devlogfsdumpread(buf, n); + + if (qid != Qctl) + error(Egreg); + + return 0; +} + +enum { + CMconfig, + CMformat, + CMopen, + CMsweep, + CMtrace, + CMunconfig, + CMextent, + CMsweepone, + CMtest, + CMleakaudit, + CMsync +}; + +static Cmdtab fscmds[] = { + {CMconfig, "config", 2}, + {CMformat, "format", 2}, + {CMopen, "open", 0}, + {CMsweep, "sweep", 1}, + {CMsweepone, "sweepone", 1}, + {CMtrace, "trace", 0}, + {CMunconfig, "unconfig", 1}, + {CMextent, "extent", 0}, + {CMtest, "test", 0}, + {CMleakaudit, "leakaudit", 1}, + {CMsync, "sync", 1}, +}; + +static long +devlogfswrite(Chan *c, void *buf, long n, vlong off) +{ + int instance, qid, qt, i; + Cmdbuf *cmd; + Cmdtab *ct; + + if(n <= 0) + return 0; + SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt); +#ifdef CALLTRACE + print("devlogfswrite(c = 0x%.8lux, buf = 0x%.8lux, n = %ld, instance = %d, qid = %d, qt = %d) - start\n", + (ulong)c, (ulong)buf, n, instance, qid, qt); +#endif + USED(instance); + if(DATAQID(qid, qt)){ + if (qid == Qfsboot) { + Devlogfs *l = c->aux; + qlock(&l->bootqlock); + if (waserror()) { + qunlock(&l->bootqlock); + nexterror(); + } + smartio((SMARTIOFN *)logfsbootio, l->lb, buf, n, off, logfsbootgetiosize(l->lb), 1); + poperror(); + qunlock(&l->bootqlock); + return n; + } + else if (qid == Qfs) { + Devlogfs *d = c->aux; + lfssrvwrite(d, buf, n); + return n; + } + error(Eio); + } + else if (qid == Qctl) { + Devlogfs *l = nil; + int a; + + cmd = parsecmd(buf, n); + if(waserror()){ + free(cmd); + nexterror(); + } + i = cmd->nf; + if(0){print("i=%d", i); for(i=0; i<cmd->nf; i++)print(" %q", cmd->f[i]); print("\n");} + if (i <= 0) + error(Ebadarg); + if (i == 3 && strcmp(cmd->f[0], "uname") == 0) { + switch (cmd->f[2][0]) { + default: + errorany(logfsisgroupcreate(is, cmd->f[1], cmd->f[2])); + break; + case ':': + errorany(logfsisgroupcreate(is, cmd->f[1], cmd->f[2] + 1)); + break; + case '%': + errorany(logfsisgrouprename(is, cmd->f[1], cmd->f[2] + 1)); + break; + case '=': + errorany(logfsisgroupsetleader(is, cmd->f[1], cmd->f[2] + 1)); + break; + case '+': + errorany(logfsisgroupaddmember(is, cmd->f[1], cmd->f[2] + 1)); + break; + case '-': + errorany(logfsisgroupremovemember(is, cmd->f[1], cmd->f[2] + 1)); + break; + } + i = 0; + } + if (i == 4 && strcmp(cmd->f[0], "fsys") == 0 && strcmp(cmd->f[2], "config") == 0) { + l = devlogfsconfig(cmd->f[1], cmd->f[3]); + i = 0; + } + else if (i >= 2 && strcmp(cmd->f[0], "fsys") == 0) { + l = devlogfssetdefname(cmd->f[1]); + if (l == nil) + errorf("file system %q not configured", cmd->f[1]); + i -= 2; + cmd->f += 2; + cmd->nf = i; + } + if (i != 0) { + ct = lookupcmd(cmd, fscmds, nelem(fscmds)); + if (l == nil) + l = devlogfssetdefname(nil); + if(l == nil && ct->index != CMleakaudit) + error("file system not configured"); + switch(ct->index){ + case CMopen: + for (a = 1; a < i; a++) + if (cmd->f[a][0] == '-') + switch (cmd->f[a][1]) { + case 'P': + l->openflags |= LogfsOpenFlagNoPerm; + break; + case 'W': + l->openflags |= LogfsOpenFlagWstatAllow; + break; + default: + error(Ebadarg); + } + devlogfsllopen(l); + break; + case CMformat: + devlogfsllformat(l, strtol(cmd->f[1], nil, 0)); + break; + case CMsweep: + devlogfsserverlogsweep(l, 0); + break; + case CMsweepone: + devlogfsserverlogsweep(l, 1); + break; + case CMtrace: + l->logfstrace = i > 1 ? strtol(cmd->f[1], nil, 0) : 0; + if (l->server) + logfsservertrace(l->server, l->logfstrace); + if (l->lb) + logfsboottrace(l->lb, l->logfstrace); + break; + case CMunconfig: + if (l->ref.ref > 0) + error(Einuse); + devlogfsunconfig(l); + break; + case CMextent: + if(i < 2) + error(Ebadarg); + devlogfsdumpinit(l, extentdumpinit, extentdumpread, i - 1, cmd->f + 1); + break; + case CMtest: + if(i < 2) + error(Ebadarg); + errorany(logfsservertestcmd(l->server, i - 1, cmd->f + 1)); + break; + case CMleakaudit: +#ifdef LEAKHUNT + leakaudit(); +#endif + break; + case CMsync: + devlogfsserversync(l); + break; + default: + error(Ebadarg); + } + } + poperror(); + free(cmd); + return n; + } + error(Egreg); + return 0; /* not reached */ +} + +static void +devlogfsfree(Devlogfs *devlogfs) +{ + if (devlogfs != nil) { + int i; + logfsfreemem(devlogfs->device); + logfsfreemem(devlogfs->name); + for (i = 0; i < Qend - Qfs; i++) + logfsfreemem(devlogfs->filename[i]); + cclose(devlogfs->flash); + cclose(devlogfs->flashctl); + qlock(&devlogfs->qlock); + logfsserverfree(&devlogfs->server); + logfsbootfree(devlogfs->lb); + if (devlogfs->ll) + (*devlogfs->ll->free)(devlogfs->ll); + logfsfreemem(devlogfs->readbuf); + qunlock(&devlogfs->qlock); + logfsfreemem(devlogfs); + } +} + +#ifdef EMU +ulong +logfsnow(void) +{ + extern vlong timeoffset; + return (timeoffset + osusectime()) / 1000000; +} +#endif + +Dev logfsdevtab = { + 0x29f, +// L'ʟ', + "logfs", + +#ifndef EMU + devreset, +#endif + devinit, +#ifndef EMU + devshutdown, +#endif + devlogfsattach, + devlogfswalk, + devlogfsstat, + devlogfsopen, + devcreate, + devlogfsclose, + devlogfsread, + devbread, + devlogfswrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devloopback.c b/os/port/devloopback.c new file mode 100644 index 00000000..e6d6508d --- /dev/null +++ b/os/port/devloopback.c @@ -0,0 +1,743 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +typedef struct Link Link; +typedef struct Loop Loop; + +struct Link +{ + Lock; + + int ref; + + long packets; /* total number of packets sent */ + long bytes; /* total number of bytes sent */ + int indrop; /* enable dropping on iq overflow */ + long soverflows; /* packets dropped because iq overflowed */ + long droprate; /* drop 1/droprate packets in tq */ + long drops; /* packets deliberately dropped */ + + vlong delay0ns; /* nanosec of delay in the link */ + long delaynns; /* nanosec of delay per byte */ + + Block *tq; /* transmission queue */ + Block *tqtail; + vlong tout; /* time the last packet in tq is really out */ + vlong tin; /* time the head packet in tq enters the remote side */ + + long limit; /* queue buffering limit */ + Queue *oq; /* output queue from other side & packets in the link */ + Queue *iq; + + Timer ci; /* time to move packets from next packet from oq */ +}; + +struct Loop +{ + QLock; + int ref; + int minmtu; /* smallest block transmittable */ + Loop *next; + ulong path; + Link link[2]; +}; + +static struct +{ + Lock; + ulong path; +} loopbackalloc; + +enum +{ + Qtopdir= 1, /* top level directory */ + + Qloopdir, /* loopback* directory */ + + Qportdir, /* directory each end of the loop */ + Qctl, + Qstatus, + Qstats, + Qdata, + + MaxQ, + + Nloopbacks = 5, + + Statelen = 23*1024, /* status buffer size */ + + Tmsize = 8, + Delayn = 10000, /* default delays in ns */ + Delay0 = 2500000, + + Loopqlim = 32*1024, /* default size of queues */ +}; + +static Dirtab loopportdir[] = +{ + "ctl", {Qctl}, 0, 0222, + "status", {Qstatus}, 0, 0444, + "stats", {Qstats}, 0, 0444, + "data", {Qdata}, 0, 0666, +}; +static Dirtab loopdirs[MaxQ]; + +static Loop loopbacks[Nloopbacks]; + +#define TYPE(x) (((ulong)(x))&0xff) +#define ID(x) (((ulong)(x))>>8) +#define QID(x,y) ((((ulong)(x))<<8)|((ulong)(y))) + +static void looper(Loop *lb); +static long loopoput(Loop *lb, Link *link, Block *bp); +static void ptime(uchar *p, vlong t); +static vlong gtime(uchar *p); +static void closelink(Link *link, int dofree); +static void pushlink(Link *link, vlong now); +static void freelb(Loop *lb); +static void linkintr(Ureg*, Timer *ci); + +static void +loopbackinit(void) +{ + int i; + + for(i = 0; i < Nloopbacks; i++) + loopbacks[i].path = i; + + /* invert directory tables for non-directory entries */ + for(i=0; i<nelem(loopportdir); i++) + loopdirs[loopportdir[i].qid.path] = loopportdir[i]; +} + +static Chan* +loopbackattach(char *spec) +{ + Loop *volatile lb; + Queue *q; + Chan *c; + int chan; + int dev; + + dev = 0; + if(spec != nil){ + dev = atoi(spec); + if(dev >= Nloopbacks) + error(Ebadspec); + } + + c = devattach('X', spec); + lb = &loopbacks[dev]; + + qlock(lb); + if(waserror()){ + lb->ref--; + qunlock(lb); + nexterror(); + } + + lb->ref++; + if(lb->ref == 1){ + for(chan = 0; chan < 2; chan++){ + lb->link[chan].ci.tmode = Tabsolute; + lb->link[chan].ci.ta = &lb->link[chan]; + lb->link[chan].ci.tf = linkintr; + lb->link[chan].limit = Loopqlim; + q = qopen(lb->link[chan].limit, 0, 0, 0); + lb->link[chan].iq = q; + if(q == nil){ + freelb(lb); + exhausted("memory"); + } + q = qopen(lb->link[chan].limit, 0, 0, 0); + lb->link[chan].oq = q; + if(q == nil){ + freelb(lb); + exhausted("memory"); + } + lb->link[chan].indrop = 1; + + lb->link[chan].delaynns = Delayn; + lb->link[chan].delay0ns = Delay0; + } + } + poperror(); + qunlock(lb); + + mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR); + c->aux = lb; + c->dev = dev; + return c; +} + +static int +loopbackgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp) +{ + Dirtab *tab; + int len, type; + Qid qid; + + type = TYPE(c->qid.path); + if(i == DEVDOTDOT){ + switch(type){ + case Qtopdir: + case Qloopdir: + snprint(up->genbuf, sizeof(up->genbuf), "#X%ld", c->dev); + mkqid(&qid, QID(0, Qtopdir), 0, QTDIR); + devdir(c, qid, up->genbuf, 0, eve, 0555, dp); + break; + case Qportdir: + snprint(up->genbuf, sizeof(up->genbuf), "loopback%ld", c->dev); + mkqid(&qid, QID(0, Qloopdir), 0, QTDIR); + devdir(c, qid, up->genbuf, 0, eve, 0555, dp); + break; + default: + panic("loopbackgen %llux", c->qid.path); + } + return 1; + } + + switch(type){ + case Qtopdir: + if(i != 0) + return -1; + snprint(up->genbuf, sizeof(up->genbuf), "loopback%ld", c->dev); + mkqid(&qid, QID(0, Qloopdir), 0, QTDIR); + devdir(c, qid, up->genbuf, 0, eve, 0555, dp); + return 1; + case Qloopdir: + if(i >= 2) + return -1; + snprint(up->genbuf, sizeof(up->genbuf), "%d", i); + mkqid(&qid, QID(i, QID(0, Qportdir)), 0, QTDIR); + devdir(c, qid, up->genbuf, 0, eve, 0555, dp); + return 1; + case Qportdir: + if(i >= nelem(loopportdir)) + return -1; + tab = &loopportdir[i]; + mkqid(&qid, QID(ID(c->qid.path), tab->qid.path), 0, QTFILE); + devdir(c, qid, tab->name, tab->length, eve, tab->perm, dp); + return 1; + default: + /* non directory entries end up here; must be in lowest level */ + if(c->qid.type & QTDIR) + panic("loopbackgen: unexpected directory"); + if(i != 0) + return -1; + tab = &loopdirs[type]; + if(tab == nil) + panic("loopbackgen: unknown type: %d", type); + len = tab->length; + devdir(c, c->qid, tab->name, len, eve, tab->perm, dp); + return 1; + } +} + + +static Walkqid* +loopbackwalk(Chan *c, Chan *nc, char **name, int nname) +{ + Walkqid *wq; + Loop *lb; + + wq = devwalk(c, nc, name, nname, nil, 0, loopbackgen); + if(wq != nil && wq->clone != nil && wq->clone != c){ + lb = c->aux; + qlock(lb); + lb->ref++; + if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata) + lb->link[ID(c->qid.path)].ref++; + qunlock(lb); + } + return wq; +} + +static int +loopbackstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, nil, 0, loopbackgen); +} + +/* + * if the stream doesn't exist, create it + */ +static Chan* +loopbackopen(Chan *c, int omode) +{ + Loop *lb; + + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Ebadarg); + c->mode = omode; + c->flag |= COPEN; + c->offset = 0; + return c; + } + + lb = c->aux; + qlock(lb); + if(TYPE(c->qid.path) == Qdata){ + if(lb->link[ID(c->qid.path)].ref){ + qunlock(lb); + error(Einuse); + } + lb->link[ID(c->qid.path)].ref++; + } + qunlock(lb); + + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + c->iounit = qiomaxatomic; + return c; +} + +static void +loopbackclose(Chan *c) +{ + Loop *lb; + int ref, chan; + + lb = c->aux; + + qlock(lb); + + /* + * closing either side hangs up the stream + */ + if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata){ + chan = ID(c->qid.path); + if(--lb->link[chan].ref == 0){ + qhangup(lb->link[chan ^ 1].oq, nil); + looper(lb); + } + } + + + /* + * if both sides are closed, they are reusable + */ + if(lb->link[0].ref == 0 && lb->link[1].ref == 0){ + for(chan = 0; chan < 2; chan++){ + closelink(&lb->link[chan], 0); + qreopen(lb->link[chan].iq); + qreopen(lb->link[chan].oq); + qsetlimit(lb->link[chan].oq, lb->link[chan].limit); + qsetlimit(lb->link[chan].iq, lb->link[chan].limit); + } + } + ref = --lb->ref; + if(ref == 0) + freelb(lb); + qunlock(lb); +} + +static void +freelb(Loop *lb) +{ + int chan; + + for(chan = 0; chan < 2; chan++) + closelink(&lb->link[chan], 1); +} + +/* + * called with the Loop qlocked, + * so only pushlink can mess with the queues + */ +static void +closelink(Link *link, int dofree) +{ + Queue *iq, *oq; + Block *bp; + + ilock(link); + iq = link->iq; + oq = link->oq; + bp = link->tq; + link->tq = nil; + link->tqtail = nil; + link->tout = 0; + link->tin = 0; + timerdel(&link->ci); + iunlock(link); + if(iq != nil){ + qclose(iq); + if(dofree){ + ilock(link); + free(iq); + link->iq = nil; + iunlock(link); + } + } + if(oq != nil){ + qclose(oq); + if(dofree){ + ilock(link); + free(oq); + link->oq = nil; + iunlock(link); + } + } + freeblist(bp); +} + +static long +loopbackread(Chan *c, void *va, long n, vlong offset) +{ + Loop *lb; + Link *link; + char *buf; + long rv; + + lb = c->aux; + switch(TYPE(c->qid.path)){ + default: + error(Eperm); + return -1; /* not reached */ + case Qtopdir: + case Qloopdir: + case Qportdir: + return devdirread(c, va, n, nil, 0, loopbackgen); + case Qdata: + return qread(lb->link[ID(c->qid.path)].iq, va, n); + case Qstatus: + link = &lb->link[ID(c->qid.path)]; + buf = smalloc(Statelen); + rv = snprint(buf, Statelen, "delay %lld %ld\n", link->delay0ns, link->delaynns); + rv += snprint(buf+rv, Statelen-rv, "limit %ld\n", link->limit); + rv += snprint(buf+rv, Statelen-rv, "indrop %d\n", link->indrop); + snprint(buf+rv, Statelen-rv, "droprate %ld\n", link->droprate); + rv = readstr(offset, va, n, buf); + free(buf); + break; + case Qstats: + link = &lb->link[ID(c->qid.path)]; + buf = smalloc(Statelen); + rv = snprint(buf, Statelen, "packets: %ld\n", link->packets); + rv += snprint(buf+rv, Statelen-rv, "bytes: %ld\n", link->bytes); + rv += snprint(buf+rv, Statelen-rv, "dropped: %ld\n", link->drops); + snprint(buf+rv, Statelen-rv, "soft overflows: %ld\n", link->soverflows); + rv = readstr(offset, va, n, buf); + free(buf); + break; + } + return rv; +} + +static Block* +loopbackbread(Chan *c, long n, ulong offset) +{ + Loop *lb; + + lb = c->aux; + if(TYPE(c->qid.path) == Qdata) + return qbread(lb->link[ID(c->qid.path)].iq, n); + + return devbread(c, n, offset); +} + +static long +loopbackbwrite(Chan *c, Block *bp, ulong off) +{ + Loop *lb; + + lb = c->aux; + if(TYPE(c->qid.path) == Qdata) + return loopoput(lb, &lb->link[ID(c->qid.path) ^ 1], bp); + return devbwrite(c, bp, off); +} + +static long +loopbackwrite(Chan *c, void *va, long n, vlong off) +{ + Loop *lb; + Link *link; + Cmdbuf *volatile cb; + Block *volatile bp; + vlong d0ns; + long dnns; + + switch(TYPE(c->qid.path)){ + case Qdata: + bp = allocb(n); + if(waserror()){ + freeb(bp); + nexterror(); + } + memmove(bp->wp, va, n); + poperror(); + bp->wp += n; + return loopbackbwrite(c, bp, off); + case Qctl: + lb = c->aux; + link = &lb->link[ID(c->qid.path)]; + cb = parsecmd(va, n); + if(waserror()){ + free(cb); + nexterror(); + } + if(cb->nf < 1) + error("short control request"); + if(strcmp(cb->f[0], "delay") == 0){ + if(cb->nf != 3) + error("usage: delay latency bytedelay"); + d0ns = strtoll(cb->f[1], nil, 10); + dnns = strtol(cb->f[2], nil, 10); + + /* + * it takes about 20000 cycles on a pentium ii + * to run pushlink; perhaps this should be accounted. + */ + + ilock(link); + link->delay0ns = d0ns; + link->delaynns = dnns; + iunlock(link); + }else if(strcmp(cb->f[0], "indrop") == 0){ + if(cb->nf != 2) + error("usage: indrop [01]"); + ilock(link); + link->indrop = strtol(cb->f[1], nil, 0) != 0; + iunlock(link); + }else if(strcmp(cb->f[0], "droprate") == 0){ + if(cb->nf != 2) + error("usage: droprate ofn"); + ilock(link); + link->droprate = strtol(cb->f[1], nil, 0); + iunlock(link); + }else if(strcmp(cb->f[0], "limit") == 0){ + if(cb->nf != 2) + error("usage: limit maxqsize"); + ilock(link); + link->limit = strtol(cb->f[1], nil, 0); + qsetlimit(link->oq, link->limit); + qsetlimit(link->iq, link->limit); + iunlock(link); + }else if(strcmp(cb->f[0], "reset") == 0){ + if(cb->nf != 1) + error("usage: reset"); + ilock(link); + link->packets = 0; + link->bytes = 0; + link->indrop = 0; + link->soverflows = 0; + link->drops = 0; + iunlock(link); + }else + error("unknown control request"); + poperror(); + free(cb); + break; + default: + error(Eperm); + } + + return n; +} + +static long +loopoput(Loop *lb, Link *link, Block *volatile bp) +{ + long n; + + n = BLEN(bp); + + /* make it a single block with space for the loopback timing header */ + if(waserror()){ + freeb(bp); + nexterror(); + } + bp = padblock(bp, Tmsize); + if(bp->next) + bp = concatblock(bp); + if(BLEN(bp) < lb->minmtu) + bp = adjustblock(bp, lb->minmtu); + poperror(); + ptime(bp->rp, todget(nil)); + + link->packets++; + link->bytes += n; + + qbwrite(link->oq, bp); + + looper(lb); + return n; +} + +static void +looper(Loop *lb) +{ + vlong t; + int chan; + + t = todget(nil); + for(chan = 0; chan < 2; chan++) + pushlink(&lb->link[chan], t); +} + +static void +linkintr(Ureg*, Timer *ci) +{ + Link *link; + + link = ci->ta; + pushlink(link, ci->tns); +} + +/* + * move blocks between queues if they are ready. + * schedule an interrupt for the next interesting time. + * + * must be called with the link ilocked. + */ +static void +pushlink(Link *link, vlong now) +{ + Block *bp; + vlong tout, tin; + + /* + * put another block in the link queue + */ + ilock(link); + if(link->iq == nil || link->oq == nil){ + iunlock(link); + return; + + } + timerdel(&link->ci); + + /* + * put more blocks into the xmit queue + * use the time the last packet was supposed to go out + * as the start time for the next packet, rather than + * the current time. this more closely models a network + * device which can queue multiple output packets. + */ + tout = link->tout; + if(!tout) + tout = now; + while(tout <= now){ + bp = qget(link->oq); + if(bp == nil){ + tout = 0; + break; + } + + /* + * can't send the packet before it gets queued + */ + tin = gtime(bp->rp); + if(tin > tout) + tout = tin; + tout = tout + (BLEN(bp) - Tmsize) * link->delaynns; + + /* + * drop packets + */ + if(link->droprate && nrand(link->droprate) == 0) + link->drops++; + else{ + ptime(bp->rp, tout + link->delay0ns); + if(link->tq == nil) + link->tq = bp; + else + link->tqtail->next = bp; + link->tqtail = bp; + } + } + + /* + * record the next time a packet can be sent, + * but don't schedule an interrupt if none is waiting + */ + link->tout = tout; + if(!qcanread(link->oq)) + tout = 0; + + /* + * put more blocks into the receive queue + */ + tin = 0; + while(bp = link->tq){ + tin = gtime(bp->rp); + if(tin > now) + break; + bp->rp += Tmsize; + link->tq = bp->next; + bp->next = nil; + if(!link->indrop) + qpassnolim(link->iq, bp); + else if(qpass(link->iq, bp) < 0) + link->soverflows++; + tin = 0; + } + if(bp == nil && qisclosed(link->oq) && !qcanread(link->oq) && !qisclosed(link->iq)) + qhangup(link->iq, nil); + link->tin = tin; + if(!tin || tin > tout && tout) + tin = tout; + + link->ci.tns = tin; + if(tin){ + if(tin < now) + panic("loopback unfinished business"); + timeradd(&link->ci); + } + iunlock(link); +} + +static void +ptime(uchar *p, vlong t) +{ + ulong tt; + + tt = t >> 32; + p[0] = tt >> 24; + p[1] = tt >> 16; + p[2] = tt >> 8; + p[3] = tt; + tt = t; + p[4] = tt >> 24; + p[5] = tt >> 16; + p[6] = tt >> 8; + p[7] = tt; +} + +static vlong +gtime(uchar *p) +{ + ulong t1, t2; + + t1 = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; + t2 = (p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]; + return ((vlong)t1 << 32) | t2; +} + +Dev loopbackdevtab = { + 'X', + "loopback", + + devreset, + loopbackinit, + devshutdown, + loopbackattach, + loopbackwalk, + loopbackstat, + loopbackopen, + devcreate, + loopbackclose, + loopbackread, + loopbackbread, + loopbackwrite, + loopbackbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devmnt.c b/os/port/devmnt.c new file mode 100644 index 00000000..6fd3ec6b --- /dev/null +++ b/os/port/devmnt.c @@ -0,0 +1,1204 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +/* + * References are managed as follows: + * The channel to the server - a network connection or pipe - has one + * reference for every Chan open on the server. The server channel has + * c->mux set to the Mnt used for muxing control to that server. Mnts + * have no reference count; they go away when c goes away. + * Each channel derived from the mount point has mchan set to c, + * and increfs/decrefs mchan to manage references on the server + * connection. + */ + +#define MAXRPC (IOHDRSZ+8192) + +struct Mntrpc +{ + Chan* c; /* Channel for whom we are working */ + Mntrpc* list; /* Free/pending list */ + Fcall request; /* Outgoing file system protocol message */ + Fcall reply; /* Incoming reply */ + Mnt* m; /* Mount device during rpc */ + Rendez r; /* Place to hang out */ + uchar* rpc; /* I/O Data buffer */ + uint rpclen; /* len of buffer */ + Block *b; /* reply blocks */ + char done; /* Rpc completed */ + uvlong stime; /* start time for mnt statistics */ + ulong reqlen; /* request length for mnt statistics */ + ulong replen; /* reply length for mnt statistics */ + Mntrpc* flushed; /* message this one flushes */ +}; + +enum +{ + TAGSHIFT = 5, /* ulong has to be 32 bits */ + TAGMASK = (1<<TAGSHIFT)-1, + NMASK = (64*1024)>>TAGSHIFT, +}; + +struct Mntalloc +{ + Lock; + Mnt* list; /* Mount devices in use */ + Mnt* mntfree; /* Free list */ + Mntrpc* rpcfree; + int nrpcfree; + int nrpcused; + ulong id; + ulong tagmask[NMASK]; +}mntalloc; + +void mattach(Mnt*, Chan*, char*); +Mnt* mntchk(Chan*); +void mntdirfix(uchar*, Chan*); +Mntrpc* mntflushalloc(Mntrpc*, ulong); +void mntflushfree(Mnt*, Mntrpc*); +void mntfree(Mntrpc*); +void mntgate(Mnt*); +void mntpntfree(Mnt*); +void mntqrm(Mnt*, Mntrpc*); +Mntrpc* mntralloc(Chan*, ulong); +long mntrdwr(int, Chan*, void*, long, vlong); +int mntrpcread(Mnt*, Mntrpc*); +void mountio(Mnt*, Mntrpc*); +void mountmux(Mnt*, Mntrpc*); +void mountrpc(Mnt*, Mntrpc*); +int rpcattn(void*); +Chan* mntchan(void); + +char Esbadstat[] = "invalid directory entry received from server"; +char Enoversion[] = "version not established for mount channel"; + + +void (*mntstats)(int, Chan*, uvlong, ulong); + +static void +mntreset(void) +{ + mntalloc.id = 1; + mntalloc.tagmask[0] = 1; /* don't allow 0 as a tag */ + mntalloc.tagmask[NMASK-1] = 0x80000000UL; /* don't allow NOTAG */ + fmtinstall('F', fcallfmt); +/* fmtinstall('D', dirfmt); */ +/* fmtinstall('M', dirmodefmt); */ + + cinit(); +} + +/* + * Version is not multiplexed: message sent only once per connection. + */ +long +mntversion(Chan *c, char *version, int msize, int returnlen) +{ + Fcall f; + uchar *msg; + Mnt *m; + char *v; + long k, l; + uvlong oo; + char buf[128]; + + qlock(&c->umqlock); /* make sure no one else does this until we've established ourselves */ + if(waserror()){ + qunlock(&c->umqlock); + nexterror(); + } + + /* defaults */ + if(msize == 0) + msize = MAXRPC; + if(msize > c->iounit && c->iounit != 0) + msize = c->iounit; + v = version; + if(v == nil || v[0] == '\0') + v = VERSION9P; + + /* validity */ + if(msize < 0) + error("bad iounit in version call"); + if(strncmp(v, VERSION9P, strlen(VERSION9P)) != 0) + error("bad 9P version specification"); + + m = c->mux; + + if(m != nil){ + qunlock(&c->umqlock); + poperror(); + + strecpy(buf, buf+sizeof buf, m->version); + k = strlen(buf); + if(strncmp(buf, v, k) != 0){ + snprint(buf, sizeof buf, "incompatible 9P versions %s %s", m->version, v); + error(buf); + } + if(returnlen > 0){ + if(returnlen < k) + error(Eshort); + memmove(version, buf, k); + } + return k; + } + + f.type = Tversion; + f.tag = NOTAG; + f.msize = msize; + f.version = v; + msg = malloc(8192+IOHDRSZ); + if(msg == nil) + exhausted("version memory"); + if(waserror()){ + free(msg); + nexterror(); + } + k = convS2M(&f, msg, 8192+IOHDRSZ); + if(k == 0) + error("bad fversion conversion on send"); + + lock(c); + oo = c->offset; + c->offset += k; + unlock(c); + + l = devtab[c->type]->write(c, msg, k, oo); + + if(l < k){ + lock(c); + c->offset -= k - l; + unlock(c); + error("short write in fversion"); + } + + /* message sent; receive and decode reply */ + k = devtab[c->type]->read(c, msg, 8192+IOHDRSZ, c->offset); + if(k <= 0) + error("EOF receiving fversion reply"); + + lock(c); + c->offset += k; + unlock(c); + + l = convM2S(msg, k, &f); + if(l != k) + error("bad fversion conversion on reply"); + if(f.type != Rversion){ + if(f.type == Rerror) + error(f.ename); + error("unexpected reply type in fversion"); + } + if(f.msize > msize) + error("server tries to increase msize in fversion"); + if(f.msize<256 || f.msize>1024*1024) + error("nonsense value of msize in fversion"); + if(strncmp(f.version, v, strlen(f.version)) != 0) + error("bad 9P version returned from server"); + + /* now build Mnt associated with this connection */ + lock(&mntalloc); + m = mntalloc.mntfree; + if(m != 0) + mntalloc.mntfree = m->list; + else { + m = malloc(sizeof(Mnt)); + if(m == 0) { + unlock(&mntalloc); + exhausted("mount devices"); + } + } + m->list = mntalloc.list; + mntalloc.list = m; + m->version = nil; + kstrdup(&m->version, f.version); + m->id = mntalloc.id++; + m->q = qopen(10*MAXRPC, 0, nil, nil); + m->msize = f.msize; + unlock(&mntalloc); + + poperror(); /* msg */ + free(msg); + + lock(m); + m->queue = 0; + m->rip = 0; + + c->flag |= CMSG; + c->mux = m; + m->c = c; + unlock(m); + + poperror(); /* c */ + qunlock(&c->umqlock); + + k = strlen(f.version); + if(returnlen > 0){ + if(returnlen < k) + error(Eshort); + memmove(version, f.version, k); + } + + return k; +} + +Chan* +mntauth(Chan *c, char *spec) +{ + Mnt *m; + Mntrpc *r; + + m = c->mux; + + if(m == nil){ + mntversion(c, VERSION9P, MAXRPC, 0); + m = c->mux; + if(m == nil) + error(Enoversion); + } + + c = mntchan(); + if(waserror()) { + /* Close must not be called since it will + * call mnt recursively + */ + chanfree(c); + nexterror(); + } + + r = mntralloc(0, m->msize); + + if(waserror()) { + mntfree(r); + nexterror(); + } + + r->request.type = Tauth; + r->request.afid = c->fid; + r->request.uname = up->env->user; + r->request.aname = spec; + mountrpc(m, r); + + c->qid = r->reply.aqid; + c->mchan = m->c; + incref(m->c); + c->mqid = c->qid; + c->mode = ORDWR; + + poperror(); /* r */ + mntfree(r); + + poperror(); /* c */ + + return c; + +} + +static Chan* +mntattach(char *muxattach) +{ + Mnt *m; + Chan *c; + Mntrpc *r; + struct bogus{ + Chan *chan; + Chan *authchan; + char *spec; + int flags; + }bogus; + + bogus = *((struct bogus *)muxattach); + c = bogus.chan; + + m = c->mux; + + if(m == nil){ + mntversion(c, nil, 0, 0); + m = c->mux; + if(m == nil) + error(Enoversion); + } + + c = mntchan(); + if(waserror()) { + /* Close must not be called since it will + * call mnt recursively + */ + chanfree(c); + nexterror(); + } + + r = mntralloc(0, m->msize); + + if(waserror()) { + mntfree(r); + nexterror(); + } + + r->request.type = Tattach; + r->request.fid = c->fid; + if(bogus.authchan == nil) + r->request.afid = NOFID; + else + r->request.afid = bogus.authchan->fid; + r->request.uname = up->env->user; + r->request.aname = bogus.spec; + mountrpc(m, r); + + c->qid = r->reply.qid; + c->mchan = m->c; + incref(m->c); + c->mqid = c->qid; + + poperror(); /* r */ + mntfree(r); + + poperror(); /* c */ + + if(bogus.flags&MCACHE) + c->flag |= CCACHE; + return c; +} + +Chan* +mntchan(void) +{ + Chan *c; + + c = devattach('M', 0); + lock(&mntalloc); + c->dev = mntalloc.id++; + unlock(&mntalloc); + + if(c->mchan) + panic("mntchan non-zero %p", c->mchan); + return c; +} + +static Walkqid* +mntwalk(Chan *c, Chan *nc, char **name, int nname) +{ + int i, alloc; + Mnt *m; + Mntrpc *r; + Walkqid *wq; + + if(nc != nil) + print("mntwalk: nc != nil\n"); + if(nname > MAXWELEM) + error("devmnt: too many name elements"); + alloc = 0; + wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); + if(waserror()){ + if(alloc && wq->clone!=nil) + cclose(wq->clone); + free(wq); + return nil; + } + + alloc = 0; + m = mntchk(c); + r = mntralloc(c, m->msize); + if(nc == nil){ + nc = devclone(c); + /* + * Until the other side accepts this fid, we can't mntclose it. + * Therefore set type to 0 for now; rootclose is known to be safe. + */ + nc->type = 0; + alloc = 1; + } + wq->clone = nc; + + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = Twalk; + r->request.fid = c->fid; + r->request.newfid = nc->fid; + r->request.nwname = nname; + memmove(r->request.wname, name, nname*sizeof(char*)); + + mountrpc(m, r); + + if(r->reply.nwqid > nname) + error("too many QIDs returned by walk"); + if(r->reply.nwqid < nname){ + if(alloc) + cclose(nc); + wq->clone = nil; + if(r->reply.nwqid == 0){ + free(wq); + wq = nil; + goto Return; + } + } + + /* move new fid onto mnt device and update its qid */ + if(wq->clone != nil){ + if(wq->clone != c){ + wq->clone->type = c->type; + wq->clone->mchan = c->mchan; + incref(c->mchan); + } + if(r->reply.nwqid > 0) + wq->clone->qid = r->reply.wqid[r->reply.nwqid-1]; + } + wq->nqid = r->reply.nwqid; + for(i=0; i<wq->nqid; i++) + wq->qid[i] = r->reply.wqid[i]; + + Return: + poperror(); + mntfree(r); + poperror(); + return wq; +} + +static int +mntstat(Chan *c, uchar *dp, int n) +{ + Mnt *m; + Mntrpc *r; + + if(n < BIT16SZ) + error(Eshortstat); + m = mntchk(c); + r = mntralloc(c, m->msize); + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = Tstat; + r->request.fid = c->fid; + mountrpc(m, r); + + if(r->reply.nstat > n){ + /* doesn't fit; just patch the count and return */ + PBIT16((uchar*)dp, r->reply.nstat); + n = BIT16SZ; + }else{ + n = r->reply.nstat; + memmove(dp, r->reply.stat, n); + validstat(dp, n); + mntdirfix(dp, c); + } + poperror(); + mntfree(r); + return n; +} + +static Chan* +mntopencreate(int type, Chan *c, char *name, int omode, ulong perm) +{ + Mnt *m; + Mntrpc *r; + + m = mntchk(c); + r = mntralloc(c, m->msize); + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = type; + r->request.fid = c->fid; + r->request.mode = omode; + if(type == Tcreate){ + r->request.perm = perm; + r->request.name = name; + } + mountrpc(m, r); + + c->qid = r->reply.qid; + c->offset = 0; + c->mode = openmode(omode); + c->iounit = r->reply.iounit; + if(c->iounit == 0 || c->iounit > m->msize-IOHDRSZ) + c->iounit = m->msize-IOHDRSZ; + c->flag |= COPEN; + poperror(); + mntfree(r); + + if(c->flag & CCACHE) + copen(c); + + return c; +} + +static Chan* +mntopen(Chan *c, int omode) +{ + return mntopencreate(Topen, c, nil, omode, 0); +} + +static void +mntcreate(Chan *c, char *name, int omode, ulong perm) +{ + mntopencreate(Tcreate, c, name, omode, perm); +} + +static void +mntclunk(Chan *c, int t) +{ + Mnt *m; + Mntrpc *r; + + m = mntchk(c); + r = mntralloc(c, m->msize); + if(waserror()){ + mntfree(r); + nexterror(); + } + + r->request.type = t; + r->request.fid = c->fid; + mountrpc(m, r); + mntfree(r); + poperror(); +} + +void +muxclose(Mnt *m) +{ + Mntrpc *q, *r; + + for(q = m->queue; q; q = r) { + r = q->list; + mntfree(q); + } + m->id = 0; + free(m->version); + m->version = nil; + mntpntfree(m); +} + +void +mntpntfree(Mnt *m) +{ + Mnt *f, **l; + Queue *q; + + lock(&mntalloc); + l = &mntalloc.list; + for(f = *l; f; f = f->list) { + if(f == m) { + *l = m->list; + break; + } + l = &f->list; + } + m->list = mntalloc.mntfree; + mntalloc.mntfree = m; + q = m->q; + unlock(&mntalloc); + + qfree(q); +} + +static void +mntclose(Chan *c) +{ + mntclunk(c, Tclunk); +} + +static void +mntremove(Chan *c) +{ + mntclunk(c, Tremove); +} + +static int +mntwstat(Chan *c, uchar *dp, int n) +{ + Mnt *m; + Mntrpc *r; + + m = mntchk(c); + r = mntralloc(c, m->msize); + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = Twstat; + r->request.fid = c->fid; + r->request.nstat = n; + r->request.stat = dp; + mountrpc(m, r); + poperror(); + mntfree(r); + return n; +} + +static long +mntread(Chan *c, void *buf, long n, vlong off) +{ + uchar *p, *e; + int nc, cache, isdir, dirlen; + + isdir = 0; + cache = c->flag & CCACHE; + if(c->qid.type & QTDIR) { + cache = 0; + isdir = 1; + } + + p = buf; + if(cache) { + nc = cread(c, buf, n, off); + if(nc > 0) { + n -= nc; + if(n == 0) + return nc; + p += nc; + off += nc; + } + n = mntrdwr(Tread, c, p, n, off); + cupdate(c, p, n, off); + return n + nc; + } + + n = mntrdwr(Tread, c, buf, n, off); + if(isdir) { + for(e = &p[n]; p+BIT16SZ < e; p += dirlen){ + dirlen = BIT16SZ+GBIT16(p); + if(p+dirlen > e) + break; + validstat(p, dirlen); + mntdirfix(p, c); + } + if(p != e) + error(Esbadstat); + } + return n; +} + +static long +mntwrite(Chan *c, void *buf, long n, vlong off) +{ + return mntrdwr(Twrite, c, buf, n, off); +} + +long +mntrdwr(int type, Chan *c, void *buf, long n, vlong off) +{ + Mnt *m; + Mntrpc *r; + char *uba; + int cache; + ulong cnt, nr, nreq; + + m = mntchk(c); + uba = buf; + cnt = 0; + cache = c->flag & CCACHE; + if(c->qid.type & QTDIR) + cache = 0; + for(;;) { + r = mntralloc(c, m->msize); + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = type; + r->request.fid = c->fid; + r->request.offset = off; + r->request.data = uba; + nr = n; + if(nr > m->msize-IOHDRSZ) + nr = m->msize-IOHDRSZ; + r->request.count = nr; + mountrpc(m, r); + nreq = r->request.count; + nr = r->reply.count; + if(nr > nreq) + nr = nreq; + + if(type == Tread) + r->b = bl2mem((uchar*)uba, r->b, nr); + else if(cache) + cwrite(c, (uchar*)uba, nr, off); + + poperror(); + mntfree(r); + off += nr; + uba += nr; + cnt += nr; + n -= nr; + if(nr != nreq || n == 0 || up->killed) + break; + } + return cnt; +} + +void +mountrpc(Mnt *m, Mntrpc *r) +{ + char *sn, *cn; + int t; + + r->reply.tag = 0; + r->reply.type = Tmax; /* can't ever be a valid message type */ + + mountio(m, r); + + t = r->reply.type; + switch(t) { + case Rerror: + error(r->reply.ename); + case Rflush: + error(Eintr); + default: + if(t == r->request.type+1) + break; + sn = "?"; + if(m->c->name != nil) + sn = m->c->name->s; + cn = "?"; + if(r->c != nil && r->c->name != nil) + cn = r->c->name->s; + print("mnt: proc %s %lud: mismatch from %s %s rep 0x%lux tag %d fid %d T%d R%d rp %d\n", + up->text, up->pid, sn, cn, + r, r->request.tag, r->request.fid, r->request.type, + r->reply.type, r->reply.tag); + error(Emountrpc); + } +} + +void +mountio(Mnt *m, Mntrpc *r) +{ + int n; + + while(waserror()) { + if(m->rip == up) + mntgate(m); + if(strcmp(up->env->errstr, Eintr) != 0){ + mntflushfree(m, r); + nexterror(); + } + r = mntflushalloc(r, m->msize); + } + + lock(m); + r->m = m; + r->list = m->queue; + m->queue = r; + unlock(m); + + /* Transmit a file system rpc */ + if(m->msize == 0) + panic("msize"); + n = convS2M(&r->request, r->rpc, m->msize); + if(n < 0) + panic("bad message type in mountio"); + if(devtab[m->c->type]->write(m->c, r->rpc, n, 0) != n) + error(Emountrpc); +/* r->stime = fastticks(nil); */ + r->reqlen = n; + + /* Gate readers onto the mount point one at a time */ + for(;;) { + lock(m); + if(m->rip == 0) + break; + unlock(m); + sleep(&r->r, rpcattn, r); + if(r->done){ + poperror(); + mntflushfree(m, r); + return; + } + } + m->rip = up; + unlock(m); + while(r->done == 0) { + if(mntrpcread(m, r) < 0) + error(Emountrpc); + mountmux(m, r); + } + mntgate(m); + poperror(); + mntflushfree(m, r); +} + +static int +doread(Mnt *m, int len) +{ + Block *b; + + while(qlen(m->q) < len){ + b = devtab[m->c->type]->bread(m->c, m->msize, 0); + if(b == nil) + return -1; + if(blocklen(b) == 0){ + freeblist(b); + return -1; + } + qaddlist(m->q, b); + } + return 0; +} + +int +mntrpcread(Mnt *m, Mntrpc *r) +{ + int i, t, len, hlen; + Block *b, **l, *nb; + + r->reply.type = 0; + r->reply.tag = 0; + + /* read at least length, type, and tag and pullup to a single block */ + if(doread(m, BIT32SZ+BIT8SZ+BIT16SZ) < 0) + return -1; + nb = pullupqueue(m->q, BIT32SZ+BIT8SZ+BIT16SZ); + + /* read in the rest of the message, avoid ridiculous (for now) message sizes */ + len = GBIT32(nb->rp); + if(len > m->msize){ + qdiscard(m->q, qlen(m->q)); + return -1; + } + if(doread(m, len) < 0) + return -1; + + /* pullup the header (i.e. everything except data) */ + t = nb->rp[BIT32SZ]; + switch(t){ + case Rread: + hlen = BIT32SZ+BIT8SZ+BIT16SZ+BIT32SZ; + break; + default: + hlen = len; + break; + } + nb = pullupqueue(m->q, hlen); + + if(convM2S(nb->rp, len, &r->reply) <= 0){ + /* bad message, dump it */ + print("mntrpcread: convM2S failed\n"); + qdiscard(m->q, len); + return -1; + } + + /* hang the data off of the fcall struct */ + l = &r->b; + *l = nil; + do { + b = qremove(m->q); + if(hlen > 0){ + b->rp += hlen; + len -= hlen; + hlen = 0; + } + i = BLEN(b); + if(i <= len){ + len -= i; + *l = b; + l = &(b->next); + } else { + /* split block and put unused bit back */ + nb = allocb(i-len); + memmove(nb->wp, b->rp+len, i-len); + b->wp = b->rp+len; + nb->wp += i-len; + qputback(m->q, nb); + *l = b; + return 0; + } + }while(len > 0); + + return 0; +} + +void +mntgate(Mnt *m) +{ + Mntrpc *q; + + lock(m); + m->rip = 0; + for(q = m->queue; q; q = q->list) { + if(q->done == 0) + if(wakeup(&q->r)) + break; + } + unlock(m); +} + +void +mountmux(Mnt *m, Mntrpc *r) +{ + Mntrpc **l, *q; + + lock(m); + l = &m->queue; + for(q = *l; q; q = q->list) { + /* look for a reply to a message */ + if(q->request.tag == r->reply.tag) { + *l = q->list; + if(q != r) { + /* + * Completed someone else. + * Trade pointers to receive buffer. + */ + q->reply = r->reply; + q->b = r->b; + r->b = nil; + } + q->done = 1; + unlock(m); + if(mntstats != nil) + (*mntstats)(q->request.type, + m->c, q->stime, + q->reqlen + r->replen); + if(q != r) + wakeup(&q->r); + return; + } + l = &q->list; + } + unlock(m); + print("unexpected reply tag %ud; type %d\n", r->reply.tag, r->reply.type); +} + +/* + * Create a new flush request and chain the previous + * requests from it + */ +Mntrpc* +mntflushalloc(Mntrpc *r, ulong iounit) +{ + Mntrpc *fr; + + fr = mntralloc(0, iounit); + + fr->request.type = Tflush; + if(r->request.type == Tflush) + fr->request.oldtag = r->request.oldtag; + else + fr->request.oldtag = r->request.tag; + fr->flushed = r; + + return fr; +} + +/* + * Free a chain of flushes. Remove each unanswered + * flush and the original message from the unanswered + * request queue. Mark the original message as done + * and if it hasn't been answered set the reply to to + * Rflush. + */ +void +mntflushfree(Mnt *m, Mntrpc *r) +{ + Mntrpc *fr; + + while(r){ + fr = r->flushed; + if(!r->done){ + r->reply.type = Rflush; + mntqrm(m, r); + } + if(fr) + mntfree(r); + r = fr; + } +} + +static int +alloctag(void) +{ + int i, j; + ulong v; + + for(i = 0; i < NMASK; i++){ + v = mntalloc.tagmask[i]; + if(v == ~0UL) + continue; + for(j = 0; j < 1<<TAGSHIFT; j++) + if((v & (1<<j)) == 0){ + mntalloc.tagmask[i] |= 1<<j; + return (i<<TAGSHIFT) + j; + } + } + /* panic("no devmnt tags left"); */ + return NOTAG; +} + +static void +freetag(int t) +{ + mntalloc.tagmask[t>>TAGSHIFT] &= ~(1<<(t&TAGMASK)); +} + +Mntrpc* +mntralloc(Chan *c, ulong msize) +{ + Mntrpc *new; + + lock(&mntalloc); + new = mntalloc.rpcfree; + if(new == nil){ + new = malloc(sizeof(Mntrpc)); + if(new == nil) { + unlock(&mntalloc); + exhausted("mount rpc header"); + } + /* + * The header is split from the data buffer as + * mountmux may swap the buffer with another header. + */ + new->rpc = mallocz(msize, 0); + if(new->rpc == nil){ + free(new); + unlock(&mntalloc); + exhausted("mount rpc buffer"); + } + new->rpclen = msize; + new->request.tag = alloctag(); + if(new->request.tag == NOTAG){ + free(new); + unlock(&mntalloc); + exhausted("rpc tags"); + } + } + else { + mntalloc.rpcfree = new->list; + mntalloc.nrpcfree--; + if(new->rpclen < msize){ + free(new->rpc); + new->rpc = mallocz(msize, 0); + if(new->rpc == nil){ + free(new); + mntalloc.nrpcused--; + unlock(&mntalloc); + exhausted("mount rpc buffer"); + } + new->rpclen = msize; + } + } + mntalloc.nrpcused++; + unlock(&mntalloc); + new->c = c; + new->done = 0; + new->flushed = nil; + new->b = nil; + return new; +} + +void +mntfree(Mntrpc *r) +{ + if(r->b != nil) + freeblist(r->b); + lock(&mntalloc); + if(mntalloc.nrpcfree >= 10){ + free(r->rpc); + free(r); + freetag(r->request.tag); + } + else{ + r->list = mntalloc.rpcfree; + mntalloc.rpcfree = r; + mntalloc.nrpcfree++; + } + mntalloc.nrpcused--; + unlock(&mntalloc); +} + +void +mntqrm(Mnt *m, Mntrpc *r) +{ + Mntrpc **l, *f; + + lock(m); + r->done = 1; + + l = &m->queue; + for(f = *l; f; f = f->list) { + if(f == r) { + *l = r->list; + break; + } + l = &f->list; + } + unlock(m); +} + +Mnt* +mntchk(Chan *c) +{ + Mnt *m; + + /* This routine is mostly vestiges of prior lives; now it's just sanity checking */ + + if(c->mchan == nil) + panic("mntchk 1: nil mchan c %s\n", channame(c)); + + m = c->mchan->mux; + + if(m == nil) + print("mntchk 2: nil mux c %s c->mchan %s \n", channame(c), channame(c->mchan)); + + /* + * Was it closed and reused (was error(Eshutdown); now, it can't happen) + */ + if(m->id == 0 || m->id >= c->dev) + panic("mntchk 3: can't happen"); + + return m; +} + +/* + * Rewrite channel type and dev for in-flight data to + * reflect local values. These entries are known to be + * the first two in the Dir encoding after the count. + */ +void +mntdirfix(uchar *dirbuf, Chan *c) +{ + uint r; + + r = devtab[c->type]->dc; + dirbuf += BIT16SZ; /* skip count */ + PBIT16(dirbuf, r); + dirbuf += BIT16SZ; + PBIT32(dirbuf, c->dev); +} + +int +rpcattn(void *v) +{ + Mntrpc *r; + + r = v; + return r->done || r->m->rip == 0; +} + +Dev mntdevtab = { + 'M', + "mnt", + + mntreset, + devinit, + devshutdown, + mntattach, + mntwalk, + mntstat, + mntopen, + mntcreate, + mntclose, + mntread, + devbread, + mntwrite, + devbwrite, + mntremove, + mntwstat, +}; diff --git a/os/port/devns16552.c b/os/port/devns16552.c new file mode 100644 index 00000000..b260eec8 --- /dev/null +++ b/os/port/devns16552.c @@ -0,0 +1,1090 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "../port/netif.h" + +/* + * Driver for the ns16552. + */ +enum +{ + /* + * register numbers + */ + Data= 0, /* xmit/rcv buffer */ + Iena= 1, /* interrupt enable */ + Ircv= (1<<0), /* for char rcv'd */ + Ixmt= (1<<1), /* for xmit buffer empty */ + Irstat=(1<<2), /* for change in rcv'er status */ + Imstat=(1<<3), /* for change in modem status */ + Istat= 2, /* interrupt flag (read) */ + Ipend= 1, /* interrupt pending (not) */ + Fenabd=(3<<6), /* on if fifo's enabled */ + Fifoctl=2, /* fifo control (write) */ + Fena= (1<<0), /* enable xmit/rcv fifos */ + Ftrig= (1<<6), /* trigger after 4 input characters */ + Fclear=(3<<1), /* clear xmit & rcv fifos */ + Format= 3, /* byte format */ + Bits8= (3<<0), /* 8 bits/byte */ + Stop2= (1<<2), /* 2 stop bits */ + Pena= (1<<3), /* generate parity */ + Peven= (1<<4), /* even parity */ + Pforce=(1<<5), /* force parity */ + Break= (1<<6), /* generate a break */ + Dra= (1<<7), /* address the divisor */ + Mctl= 4, /* modem control */ + Dtr= (1<<0), /* data terminal ready */ + Rts= (1<<1), /* request to send */ + Ri= (1<<2), /* ring */ + Inton= (1<<3), /* turn on interrupts */ + Loop= (1<<4), /* loop back */ + Lstat= 5, /* line status */ + Inready=(1<<0), /* receive buffer full */ + Oerror=(1<<1), /* receiver overrun */ + Perror=(1<<2), /* receiver parity error */ + Ferror=(1<<3), /* rcv framing error */ + Berror=(1<<4), /* break alarm */ + Outready=(1<<5), /* output buffer full */ + Mstat= 6, /* modem status */ + Ctsc= (1<<0), /* clear to send changed */ + Dsrc= (1<<1), /* data set ready changed */ + Rire= (1<<2), /* rising edge of ring indicator */ + Dcdc= (1<<3), /* data carrier detect changed */ + Cts= (1<<4), /* complement of clear to send line */ + Dsr= (1<<5), /* complement of data set ready line */ + Ring= (1<<6), /* complement of ring indicator line */ + Dcd= (1<<7), /* complement of data carrier detect line */ + Scratch=7, /* scratchpad */ + Dlsb= 0, /* divisor lsb */ + Dmsb= 1, /* divisor msb */ + + CTLS= 023, + CTLQ= 021, + + Stagesize= 1024, + Nuart= 32, /* max per machine */ +}; + +typedef struct Uart Uart; +struct Uart +{ + QLock; + int opens; + + int enabled; + Uart *elist; /* next enabled interface */ + char name[KNAMELEN]; + + uchar sticky[8]; /* sticky write register values */ + ulong port; + ulong freq; /* clock frequency */ + uchar mask; /* bits/char */ + int dev; + int baud; /* baud rate */ + + uchar istat; /* last istat read */ + int frame; /* framing errors */ + int overrun; /* rcvr overruns */ + + /* buffers */ + int (*putc)(Queue*, int); + Queue *iq; + Queue *oq; + + Lock flock; /* fifo */ + uchar fifoon; /* fifo's enabled */ + uchar nofifo; /* earlier chip version with nofifo */ + + Lock rlock; /* receive */ + uchar istage[Stagesize]; + uchar *ip; + uchar *ie; + + int haveinput; + + Lock tlock; /* transmit */ + uchar ostage[Stagesize]; + uchar *op; + uchar *oe; + + int modem; /* hardware flow control on */ + int xonoff; /* software flow control on */ + int blocked; + int cts, dsr, dcd; /* keep track of modem status */ + int ctsbackoff; + int hup_dsr, hup_dcd; /* send hangup upstream? */ + int dohup; + + Rendez r; +}; + +static Uart *uart[Nuart]; +static int nuart; +static Uart *consuart; + +struct Uartalloc { + Lock; + Uart *elist; /* list of enabled interfaces */ +} uartalloc; + +void ns16552intr(int); + +/* + * pick up architecture specific routines and definitions + */ +#include "ns16552.h" + +/* + * set the baud rate by calculating and setting the baudrate + * generator constant. This will work with fairly non-standard + * baud rates. + */ +static void +ns16552setbaud(Uart *p, int rate) +{ + ulong brconst; + + if(rate <= 0) + return; + + brconst = (p->freq+8*rate-1)/(16*rate); + + uartwrreg(p, Format, Dra); + outb(p->port + Dmsb, (brconst>>8) & 0xff); + outb(p->port + Dlsb, brconst & 0xff); + uartwrreg(p, Format, 0); + + p->baud = rate; +} + +/* + * decide if we should hangup when dsr or dcd drops. + */ +static void +ns16552dsrhup(Uart *p, int n) +{ + p->hup_dsr = n; +} + +static void +ns16552dcdhup(Uart *p, int n) +{ + p->hup_dcd = n; +} + +static void +ns16552parity(Uart *p, char type) +{ + switch(type){ + case 'e': + p->sticky[Format] |= Pena|Peven; + break; + case 'o': + p->sticky[Format] &= ~Peven; + p->sticky[Format] |= Pena; + break; + default: + p->sticky[Format] &= ~(Pena|Peven); + break; + } + uartwrreg(p, Format, 0); +} + +/* + * set bits/character, default 8 + */ +void +ns16552bits(Uart *p, int bits) +{ + if(bits < 5 || bits > 8) + error(Ebadarg); + + p->sticky[Format] &= ~3; + p->sticky[Format] |= bits-5; + + uartwrreg(p, Format, 0); +} + + +/* + * toggle DTR + */ +void +ns16552dtr(Uart *p, int n) +{ + if(n) + p->sticky[Mctl] |= Dtr; + else + p->sticky[Mctl] &= ~Dtr; + + uartwrreg(p, Mctl, 0); +} + +/* + * toggle RTS + */ +void +ns16552rts(Uart *p, int n) +{ + if(n) + p->sticky[Mctl] |= Rts; + else + p->sticky[Mctl] &= ~Rts; + + uartwrreg(p, Mctl, 0); +} + +/* + * send break + */ +static void +ns16552break(Uart *p, int ms) +{ + if(ms == 0) + ms = 200; + + uartwrreg(p, Format, Break); + tsleep(&up->sleep, return0, 0, ms); + uartwrreg(p, Format, 0); +} + +static void +ns16552fifoon(Uart *p) +{ + ulong i, x; + + if(p->nofifo || uartrdreg(p, Istat) & Fenabd) + return; + + x = splhi(); + + /* reset fifos */ + p->sticky[Fifoctl] = 0; + uartwrreg(p, Fifoctl, Fclear); + + /* empty buffer and interrupt conditions */ + for(i = 0; i < 16; i++){ + if(uartrdreg(p, Istat)){ + /* nothing to do */ + } + if(uartrdreg(p, Data)){ + /* nothing to do */ + } + } + + /* turn on fifo */ + p->fifoon = 1; + p->sticky[Fifoctl] = Fena|Ftrig; + uartwrreg(p, Fifoctl, 0); + p->istat = uartrdreg(p, Istat); + if((p->istat & Fenabd) == 0) { + /* didn't work, must be an earlier chip type */ + p->nofifo = 1; + } + + splx(x); +} + +/* + * modem flow control on/off (rts/cts) + */ +static void +ns16552mflow(Uart *p, int n) +{ + ilock(&p->tlock); + if(n){ + p->sticky[Iena] |= Imstat; + uartwrreg(p, Iena, 0); + p->modem = 1; + p->cts = uartrdreg(p, Mstat) & Cts; + } else { + p->sticky[Iena] &= ~Imstat; + uartwrreg(p, Iena, 0); + p->modem = 0; + p->cts = 1; + } + iunlock(&p->tlock); + +// ilock(&p->flock); +// if(1) +// /* turn on fifo's */ +// ns16552fifoon(p); +// else { +// /* turn off fifo's */ +// p->fifoon = 0; +// p->sticky[Fifoctl] = 0; +// uartwrreg(p, Fifoctl, Fclear); +// } +// iunlock(&p->flock); +} + +/* + * turn on a port's interrupts. set DTR and RTS + */ +static void +ns16552enable(Uart *p) +{ + Uart **l; + + if(p->enabled) + return; + + uartpower(p->dev, 1); + + p->hup_dsr = p->hup_dcd = 0; + p->cts = p->dsr = p->dcd = 0; + + /* + * turn on interrupts + */ + p->sticky[Iena] = Ircv | Ixmt | Irstat; + uartwrreg(p, Iena, 0); + + /* + * turn on DTR and RTS + */ + ns16552dtr(p, 1); + ns16552rts(p, 1); + + ns16552fifoon(p); + + /* + * assume we can send + */ + ilock(&p->tlock); + p->cts = 1; + p->blocked = 0; + iunlock(&p->tlock); + + /* + * set baud rate to the last used + */ + ns16552setbaud(p, p->baud); + + lock(&uartalloc); + for(l = &uartalloc.elist; *l; l = &(*l)->elist){ + if(*l == p) + break; + } + if(*l == 0){ + p->elist = uartalloc.elist; + uartalloc.elist = p; + } + p->enabled = 1; + unlock(&uartalloc); +} + +/* + * turn off a port's interrupts. reset DTR and RTS + */ +static void +ns16552disable(Uart *p) +{ + Uart **l; + + /* + * turn off interrupts + */ + p->sticky[Iena] = 0; + uartwrreg(p, Iena, 0); + + /* + * revert to default settings + */ + p->sticky[Format] = Bits8; + uartwrreg(p, Format, 0); + + /* + * turn off DTR, RTS, hardware flow control & fifo's + */ + ns16552dtr(p, 0); + ns16552rts(p, 0); + ns16552mflow(p, 0); + ilock(&p->tlock); + p->xonoff = p->blocked = 0; + iunlock(&p->tlock); + + uartpower(p->dev, 0); + + lock(&uartalloc); + for(l = &uartalloc.elist; *l; l = &(*l)->elist){ + if(*l == p){ + *l = p->elist; + break; + } + } + p->enabled = 0; + unlock(&uartalloc); +} + +/* + * put some bytes into the local queue to avoid calling + * qconsume for every character + */ +static int +stageoutput(Uart *p) +{ + int n; + + n = qconsume(p->oq, p->ostage, Stagesize); + if(n <= 0) + return 0; + p->op = p->ostage; + p->oe = p->ostage + n; + return n; +} + +/* + * (re)start output + */ +static void +ns16552kick0(Uart *p) +{ + int i; + if((p->modem && (p->cts == 0)) || p->blocked) + return; + + /* + * 128 here is an arbitrary limit to make sure + * we don't stay in this loop too long. If the + * chips output queue is longer than 128, too + * bad -- presotto + */ + for(i = 0; i < 128; i++){ + if(!(uartrdreg(p, Lstat) & Outready)) + break; + if(p->op >= p->oe && stageoutput(p) == 0) + break; + outb(p->port + Data, *(p->op++)); + } +} + +static void +ns16552kick(void *v) +{ + Uart *p; + + p = v; + ilock(&p->tlock); + ns16552kick0(p); + iunlock(&p->tlock); +} + +/* + * restart input if it's off + */ +static void +ns16552flow(void *v) +{ + Uart *p; + + p = v; + if(p->modem) + ns16552rts(p, 1); + ilock(&p->rlock); + p->haveinput = 1; + iunlock(&p->rlock); +} + +/* + * default is 9600 baud, 1 stop bit, 8 bit chars, no interrupts, + * transmit and receive enabled, interrupts disabled. + */ +static void +ns16552setup0(Uart *p) +{ + memset(p->sticky, 0, sizeof(p->sticky)); + /* + * set rate to 9600 baud. + * 8 bits/character. + * 1 stop bit. + * interrupts enabled. + */ + p->sticky[Format] = Bits8; + uartwrreg(p, Format, 0); + p->sticky[Mctl] |= Inton; + uartwrreg(p, Mctl, 0x0); + + ns16552setbaud(p, 9600); + + p->iq = qopen(4*1024, 0, ns16552flow, p); + p->oq = qopen(4*1024, 0, ns16552kick, p); + if(p->iq == nil || p->oq == nil) + panic("ns16552setup0"); + + p->ip = p->istage; + p->ie = &p->istage[Stagesize]; + p->op = p->ostage; + p->oe = p->ostage; +} + +/* + * called by main() to create a new duart + */ +void +ns16552setup(ulong port, ulong freq, char *name) +{ + Uart *p; + + if(nuart >= Nuart) + return; + + p = xalloc(sizeof(Uart)); + uart[nuart] = p; + strcpy(p->name, name); + p->dev = nuart; + nuart++; + p->port = port; + p->freq = freq; + ns16552setup0(p); +} + +/* + * called by main() to configure a duart port as a console or a mouse + */ +void +ns16552special(int port, int baud, Queue **in, Queue **out, int (*putc)(Queue*, int)) +{ + Uart *p = uart[port]; + ns16552enable(p); + if(baud) + ns16552setbaud(p, baud); + p->putc = putc; + if(in) + *in = p->iq; + if(out) + *out = p->oq; + p->opens++; +} + +/* + * handle an interrupt to a single uart + */ +void +ns16552intr(int dev) +{ + uchar ch; + int s, l; + Uart *p = uart[dev]; + + for (s = uartrdreg(p, Istat); !(s&Ipend); s = uartrdreg(p, Istat)) { + switch(s&0x3f){ + case 4: /* received data available */ + case 6: /* receiver line status (alarm or error) */ + case 12: /* character timeout indication */ + while ((l = uartrdreg(p, Lstat)) & Inready) { + if(l & Ferror) + p->frame++; + if(l & Oerror) + p->overrun++; + ch = uartrdreg(p, Data) & 0xff; + if (l & (Berror|Perror|Ferror)) { + /* ch came with break, parity or framing error - consume */ + continue; + } + if (ch == CTLS || ch == CTLQ) { + ilock(&p->tlock); + if(p->xonoff){ + if(ch == CTLS) + p->blocked = 1; + else + p->blocked = 0; /* clock gets output going again */ + } + iunlock(&p->tlock); + } + if(p->putc) + p->putc(p->iq, ch); + else { + ilock(&p->rlock); + if(p->ip < p->ie) + *p->ip++ = ch; + else + p->overrun++; + p->haveinput = 1; + iunlock(&p->rlock); + } + } + break; + + case 2: /* transmitter not full */ + ns16552kick(p); + break; + + case 0: /* modem status */ + ch = uartrdreg(p, Mstat); + if(ch & Ctsc){ + ilock(&p->tlock); + l = p->cts; + p->cts = ch & Cts; + if(l == 0 && p->cts) + p->ctsbackoff = 2; /* clock gets output going again */ + iunlock(&p->tlock); + } + if (ch & Dsrc) { + l = ch & Dsr; + if(p->hup_dsr && p->dsr && !l){ + ilock(&p->rlock); + p->dohup = 1; + iunlock(&p->rlock); + } + p->dsr = l; + } + if (ch & Dcdc) { + l = ch & Dcd; + if(p->hup_dcd && p->dcd && !l){ + ilock(&p->rlock); + p->dohup = 1; + iunlock(&p->rlock); + } + p->dcd = l; + } + break; + + default: + print("weird uart interrupt #%2.2ux\n", s); + break; + } + } + p->istat = s; +} + +/* + * we save up input characters till clock time + * + * There's also a bit of code to get a stalled print going. + * It shouldn't happen, but it does. Obviously I don't + * understand something. Since it was there, I bundled a + * restart after flow control with it to give some hysteresis + * to the hardware flow control. This makes compressing + * modems happier but will probably bother something else. + * -- presotto + */ +void +uartclock(void) +{ + int n; + Uart *p; + + for(p = uartalloc.elist; p; p = p->elist){ + + /* this amortizes cost of qproduce to many chars */ + if(p->haveinput){ + ilock(&p->rlock); + if(p->haveinput){ + n = p->ip - p->istage; + if(n > 0 && p->iq){ + if(n > Stagesize) + panic("uartclock"); + if(qproduce(p->iq, p->istage, n) < 0) + ns16552rts(p, 0); + else + p->ip = p->istage; + } + p->haveinput = 0; + } + iunlock(&p->rlock); + } + if(p->dohup){ + ilock(&p->rlock); + if(p->dohup){ + qhangup(p->iq, 0); + qhangup(p->oq, 0); + } + p->dohup = 0; + iunlock(&p->rlock); + } + + /* this adds hysteresis to hardware flow control */ + if(p->ctsbackoff){ + ilock(&p->tlock); + if(p->ctsbackoff){ + if(--(p->ctsbackoff) == 0) + ns16552kick0(p); + } + iunlock(&p->tlock); + } + } +} + +Dirtab *ns16552dir; +int ndir; + +static void +setlength(int i) +{ + Uart *p; + + if(i > 0){ + p = uart[i]; + if(p && p->opens && p->iq) + ns16552dir[1+3*i].length = qlen(p->iq); + } else for(i = 0; i < nuart; i++){ + p = uart[i]; + if(p && p->opens && p->iq) + ns16552dir[1+3*i].length = qlen(p->iq); + } +} + +/* + * all uarts must be ns16552setup() by this point or inside of ns16552install() + */ +static void +ns16552reset(void) +{ + int i; + Dirtab *dp; + ns16552install(); /* architecture specific */ + + ndir = 1+3*nuart; + ns16552dir = xalloc(ndir * sizeof(Dirtab)); + dp = ns16552dir; + strcpy(dp->name, "."); + mkqid(&dp->qid, 0, 0, QTDIR); + dp->length = 0; + dp->perm = DMDIR|0555; + dp++; + for(i = 0; i < nuart; i++){ + /* 3 directory entries per port */ + strcpy(dp->name, uart[i]->name); + dp->qid.path = NETQID(i, Ndataqid); + dp->perm = 0666; + dp++; + sprint(dp->name, "%sctl", uart[i]->name); + dp->qid.path = NETQID(i, Nctlqid); + dp->perm = 0666; + dp++; + sprint(dp->name, "%sstatus", uart[i]->name); + dp->qid.path = NETQID(i, Nstatqid); + dp->perm = 0444; + dp++; + } +} + +static Chan* +ns16552attach(char *spec) +{ + return devattach('t', spec); +} + +static Walkqid* +ns16552walk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, ns16552dir, ndir, devgen); +} + +static int +ns16552stat(Chan *c, uchar *dp, int n) +{ + if(NETTYPE(c->qid.path) == Ndataqid) + setlength(NETID(c->qid.path)); + return devstat(c, dp, n, ns16552dir, ndir, devgen); +} + +static Chan* +ns16552open(Chan *c, int omode) +{ + Uart *p; + + c = devopen(c, omode, ns16552dir, ndir, devgen); + + switch(NETTYPE(c->qid.path)){ + case Nctlqid: + case Ndataqid: + p = uart[NETID(c->qid.path)]; + qlock(p); + if(p->opens++ == 0){ + ns16552enable(p); + qreopen(p->iq); + qreopen(p->oq); + } + qunlock(p); + break; + } + + return c; +} + +static void +ns16552close(Chan *c) +{ + Uart *p; + + if(c->qid.type & QTDIR) + return; + if((c->flag & COPEN) == 0) + return; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + case Nctlqid: + p = uart[NETID(c->qid.path)]; + qlock(p); + if(--(p->opens) == 0){ + ns16552disable(p); + qclose(p->iq); + qclose(p->oq); + p->ip = p->istage; + p->dcd = p->dsr = p->dohup = 0; + } + qunlock(p); + break; + } +} + +static long +uartstatus(Chan*, Uart *p, void *buf, long n, long offset) +{ + uchar mstat, fstat, istat, tstat; + char str[256]; + + str[0] = 0; + tstat = p->sticky[Mctl]; + mstat = uartrdreg(p, Mstat); + istat = p->sticky[Iena]; + fstat = p->sticky[Format]; + snprint(str, sizeof str, + "b%d c%d d%d e%d l%d m%d p%c r%d s%d\n" + "%d %d %d%s%s%s%s%s\n", + + p->baud, + p->hup_dcd, + (tstat & Dtr) != 0, + p->hup_dsr, + (fstat & Bits8) + 5, + (istat & Imstat) != 0, + (fstat & Pena) ? ((fstat & Peven) ? 'e' : 'o') : 'n', + (tstat & Rts) != 0, + (fstat & Stop2) ? 2 : 1, + + p->dev, + p->frame, + p->overrun, + uartrdreg(p, Istat) & Fenabd ? " fifo" : "", + (mstat & Cts) ? " cts" : "", + (mstat & Dsr) ? " dsr" : "", + (mstat & Dcd) ? " dcd" : "", + (mstat & Ring) ? " ring" : "" + ); + return readstr(offset, buf, n, str); +} + +static long +ns16552read(Chan *c, void *buf, long n, vlong off) +{ + Uart *p; + ulong offset = off; + + if(c->qid.type & QTDIR){ + setlength(-1); + return devdirread(c, buf, n, ns16552dir, ndir, devgen); + } + + p = uart[NETID(c->qid.path)]; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + return qread(p->iq, buf, n); + case Nctlqid: + return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE); + case Nstatqid: + return uartstatus(c, p, buf, n, offset); + } + + return 0; +} + +static void +ns16552ctl(Uart *p, char *cmd) +{ + int i, n; + + /* let output drain for a while */ + for(i = 0; i < 16 && qlen(p->oq); i++) + tsleep(&p->r, (int(*)(void*))qlen, p->oq, 125); + + if(strncmp(cmd, "break", 5) == 0){ + ns16552break(p, 0); + return; + } + + + n = atoi(cmd+1); + switch(*cmd){ + case 'B': + case 'b': + ns16552setbaud(p, n); + break; + case 'C': + case 'c': + ns16552dcdhup(p, n); + break; + case 'D': + case 'd': + ns16552dtr(p, n); + break; + case 'E': + case 'e': + ns16552dsrhup(p, n); + break; + case 'f': + case 'F': + qflush(p->oq); + break; + case 'H': + case 'h': + qhangup(p->iq, 0); + qhangup(p->oq, 0); + break; + case 'L': + case 'l': + ns16552bits(p, n); + break; + case 'm': + case 'M': + ns16552mflow(p, n); + break; + case 'n': + case 'N': + qnoblock(p->oq, n); + break; + case 'P': + case 'p': + ns16552parity(p, *(cmd+1)); + break; + case 'K': + case 'k': + ns16552break(p, n); + break; + case 'R': + case 'r': + ns16552rts(p, n); + break; + case 'Q': + case 'q': + qsetlimit(p->iq, n); + qsetlimit(p->oq, n); + break; + case 'W': + case 'w': + /* obsolete */ + break; + case 'X': + case 'x': + ilock(&p->tlock); + p->xonoff = n; + iunlock(&p->tlock); + break; + } +} + +static long +ns16552write(Chan *c, void *buf, long n, vlong) +{ + Uart *p; + char cmd[32]; + + if(c->qid.type & QTDIR) + error(Eperm); + + p = uart[NETID(c->qid.path)]; + + /* + * The fifo's turn themselves off sometimes. + * It must be something I don't understand. -- presotto + */ + lock(&p->flock); + if((p->istat & Fenabd) == 0 && p->fifoon && p->nofifo == 0) + ns16552fifoon(p); + unlock(&p->flock); + + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + return qwrite(p->oq, buf, n); + case Nctlqid: + if(n >= sizeof(cmd)) + n = sizeof(cmd)-1; + memmove(cmd, buf, n); + cmd[n] = 0; + ns16552ctl(p, cmd); + return n; + } +} + +static int +ns16552wstat(Chan *c, uchar *dp, int n) +{ + Dir d; + Dirtab *dt; + + if(!iseve()) + error(Eperm); + if(c->qid.type & QTDIR) + error(Eperm); + if(NETTYPE(c->qid.path) == Nstatqid) + error(Eperm); + + dt = &ns16552dir[1+3 * NETID(c->qid.path)]; + n = convM2D(dp, n, &d, nil); + if(n == 0) + error(Eshortstat); + if(d.mode != ~0UL){ + d.mode &= 0666; + dt[0].perm = dt[1].perm = d.mode; + } + return n; +} + +Dev ns16552devtab = { + 't', + "ns16552", + + ns16552reset, + devinit, + devshutdown, + ns16552attach, + ns16552walk, + ns16552stat, + ns16552open, + devcreate, + ns16552close, + ns16552read, + devbread, + ns16552write, + devbwrite, + devremove, + ns16552wstat, +}; + +void +uartputc(int c) +{ + Uart *p; + int i; + + p = consuart; + if(p == nil) + return; + for(i = 0; !(uartrdreg(p, Lstat)&Outready) && i < 128; i++) + delay(1); + outb(p->port+Data, c); + for(i = 0; !(uartrdreg(p, Lstat)&Outready) && i < 128; i++) + delay(1); +} + +void +uartputs(char *s, int n) +{ + char *e; + + if(consuart == nil) + return; + e = s+n; + for(; s < e; s++){ + if(*s == '\n') + uartputc('\r'); + uartputc(*s); + } +} diff --git a/os/port/devpci.c b/os/port/devpci.c new file mode 100644 index 00000000..90967e49 --- /dev/null +++ b/os/port/devpci.c @@ -0,0 +1,227 @@ +/* + * access to PCI configuration space (devpnp.c without PNP) + * + * TODO + * - extend PCI raw access to configuration space (writes, byte/short access?) + * - implement PCI access to memory/io space/BIOS ROM + * - use c->aux instead of performing lookup on each read/write? + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define DPRINT if(0) print +#define XPRINT if(1) print + +enum { + Qtopdir = 0, + + Qpcidir, + Qpcictl, + Qpciraw, +}; + +#define TYPE(q) ((ulong)(q).path & 0x0F) +#define QID(c, t) (((c)<<4)|(t)) + +static Dirtab topdir[] = { + ".", { Qtopdir, 0, QTDIR }, 0, 0555, + "pci", { Qpcidir, 0, QTDIR }, 0, 0555, +}; + +extern Dev pcidevtab; + +static int +pcigen2(Chan *c, int t, int tbdf, Dir *dp) +{ + Qid q; + + q = (Qid){BUSBDF(tbdf)|t, 0, 0}; + switch(t) { + case Qpcictl: + sprint(up->genbuf, "%d.%d.%dctl", BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf)); + devdir(c, q, up->genbuf, 0, eve, 0444, dp); + return 1; + case Qpciraw: + sprint(up->genbuf, "%d.%d.%draw", BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf)); + devdir(c, q, up->genbuf, 128, eve, 0444, dp); + return 1; + } + return -1; +} + +static int +pcigen(Chan *c, char *, Dirtab*, int, int s, Dir *dp) +{ + Qid q; + Pcidev *p; + int tbdf; + + switch(TYPE(c->qid)){ + case Qtopdir: + if(s == DEVDOTDOT){ + q = (Qid){QID(0, Qtopdir), 0, QTDIR}; + sprint(up->genbuf, "#%C", pcidevtab.dc); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + return devgen(c, nil, topdir, nelem(topdir), s, dp); + case Qpcidir: + if(s == DEVDOTDOT){ + q = (Qid){QID(0, Qtopdir), 0, QTDIR}; + sprint(up->genbuf, "#%C", pcidevtab.dc); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + p = pcimatch(nil, 0, 0); + while(s >= 2 && p != nil) { + p = pcimatch(p, 0, 0); + s -= 2; + } + if(p == nil) + return -1; + return pcigen2(c, s+Qpcictl, p->tbdf, dp); + case Qpcictl: + case Qpciraw: + tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path); + p = pcimatchtbdf(tbdf); + if(p == nil) + return -1; + return pcigen2(c, TYPE(c->qid), tbdf, dp); + default: + break; + } + return -1; +} + +static Chan* +pciattach(char *spec) +{ + return devattach(pcidevtab.dc, spec); +} + +Walkqid* +pciwalk(Chan* c, Chan *nc, char** name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, pcigen); +} + +static int +pcistat(Chan* c, uchar* dp, int n) +{ + return devstat(c, dp, n, nil, 0, pcigen); +} + +static Chan* +pciopen(Chan *c, int omode) +{ + return devopen(c, omode, nil, 0, pcigen); +} + +static void +pciclose(Chan*) +{ +} + +static long +pciread(Chan *c, void *va, long n, vlong offset) +{ + ulong x; + Pcidev *p; + char buf[256], *ebuf, *w; + char *a = va; + int i, tbdf, r; + + switch(TYPE(c->qid)){ + case Qtopdir: + case Qpcidir: + return devdirread(c, a, n, nil, 0, pcigen); + case Qpcictl: + tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path); + p = pcimatchtbdf(tbdf); + if(p == nil) + error(Egreg); + ebuf = buf+sizeof buf-1; /* -1 for newline */ + w = seprint(buf, ebuf, "%.2x.%.2x.%.2x %.4x/%.4x %3d", + p->ccrb, p->ccru, p->ccrp, p->vid, p->did, p->intl); + for(i=0; i<nelem(p->mem); i++){ + if(p->mem[i].size == 0) + continue; + w = seprint(w, ebuf, " %d:%.8lux %d", i, p->mem[i].bar, p->mem[i].size); + } + *w++ = '\n'; + *w = '\0'; + return readstr(offset, a, n, buf); + case Qpciraw: + tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path); + p = pcimatchtbdf(tbdf); + if(p == nil) + error(Egreg); + if(offset > 256) + return 0; + if(n+offset > 256) + n = 256-offset; + if(offset%4) + error(Ebadarg); + r = offset; + for(i = 0; i+4 <= n; i+=4) { + x = pcicfgr32(p, r); + a[0] = x; + a[1] = (x>>8); + a[2] = (x>>16); + a[3] = (x>>24); + a += 4; + r += 4; + } + return i; + default: + error(Egreg); + } + return n; +} + +static long +pciwrite(Chan *c, void *a, long n, vlong) +{ + char buf[256]; + + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + strncpy(buf, a, n); + buf[n] = 0; + + switch(TYPE(c->qid)){ + case Qpcictl: + case Qpciraw: + error(Eperm); + default: + error(Egreg); + } + return n; +} + + +Dev pcidevtab = { + '$', + "pci", + + devreset, + devinit, + devshutdown, + pciattach, + pciwalk, + pcistat, + pciopen, + devcreate, + pciclose, + pciread, + devbread, + pciwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devpipe.c b/os/port/devpipe.c new file mode 100644 index 00000000..2aa799aa --- /dev/null +++ b/os/port/devpipe.c @@ -0,0 +1,464 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "interp.h" + +#define NETTYPE(x) ((ulong)(x)&0x1f) +#define NETID(x) (((ulong)(x))>>5) +#define NETQID(i,t) (((i)<<5)|(t)) + +typedef struct Pipe Pipe; +struct Pipe +{ + QLock; + Pipe* next; + int ref; + ulong path; + Queue* q[2]; + int qref[2]; + Dirtab *pipedir; + char* user; +}; + +static struct +{ + Lock; + ulong path; + int pipeqsize; +} pipealloc; + +enum +{ + Qdir, + Qdata0, + Qdata1, +}; + +static +Dirtab pipedir[] = +{ + ".", {Qdir,0,QTDIR}, 0, DMDIR|0500, + "data", {Qdata0}, 0, 0660, + "data1", {Qdata1}, 0, 0660, +}; + +static void +freepipe(Pipe *p) +{ + if(p != nil){ + free(p->user); + free(p->q[0]); + free(p->q[1]); + free(p->pipedir); + free(p); + } +} + +static void +pipeinit(void) +{ + pipealloc.pipeqsize = 32*1024; +} + +/* + * create a pipe, no streams are created until an open + */ +static Chan* +pipeattach(char *spec) +{ + Pipe *p; + Chan *c; + + c = devattach('|', spec); + p = malloc(sizeof(Pipe)); + if(p == 0) + error(Enomem); + if(waserror()){ + freepipe(p); + nexterror(); + } + p->pipedir = malloc(sizeof(pipedir)); + if (p->pipedir == 0) + error(Enomem); + memmove(p->pipedir, pipedir, sizeof(pipedir)); + kstrdup(&p->user, up->env->user); + p->ref = 1; + + p->q[0] = qopen(pipealloc.pipeqsize, 0, 0, 0); + if(p->q[0] == 0) + error(Enomem); + p->q[1] = qopen(pipealloc.pipeqsize, 0, 0, 0); + if(p->q[1] == 0) + error(Enomem); + poperror(); + + lock(&pipealloc); + p->path = ++pipealloc.path; + unlock(&pipealloc); + + c->qid.path = NETQID(2*p->path, Qdir); + c->qid.vers = 0; + c->qid.type = QTDIR; + c->aux = p; + c->dev = 0; + return c; +} + +static int +pipegen(Chan *c, char *, Dirtab *tab, int ntab, int i, Dir *dp) +{ + int id, len; + Qid qid; + Pipe *p; + + if(i == DEVDOTDOT){ + devdir(c, c->qid, "#|", 0, eve, 0555, dp); + return 1; + } + i++; /* skip . */ + if(tab==0 || i>=ntab) + return -1; + tab += i; + p = c->aux; + switch(NETTYPE(tab->qid.path)){ + case Qdata0: + len = qlen(p->q[0]); + break; + case Qdata1: + len = qlen(p->q[1]); + break; + default: + len = tab->length; + break; + } + id = NETID(c->qid.path); + qid.path = NETQID(id, tab->qid.path); + qid.vers = 0; + qid.type = QTFILE; + devdir(c, qid, tab->name, len, eve, tab->perm, dp); + return 1; +} + + +static Walkqid* +pipewalk(Chan *c, Chan *nc, char **name, int nname) +{ + Walkqid *wq; + Pipe *p; + + p = c->aux; + wq = devwalk(c, nc, name, nname, p->pipedir, nelem(pipedir), pipegen); + if(wq != nil && wq->clone != nil && wq->clone != c){ + qlock(p); + p->ref++; + if(c->flag & COPEN){ + switch(NETTYPE(c->qid.path)){ + case Qdata0: + p->qref[0]++; + break; + case Qdata1: + p->qref[1]++; + break; + } + } + qunlock(p); + } + return wq; +} + +static int +pipestat(Chan *c, uchar *db, int n) +{ + Pipe *p; + Dir dir; + Dirtab *tab; + + p = c->aux; + tab = p->pipedir; + + switch(NETTYPE(c->qid.path)){ + case Qdir: + devdir(c, c->qid, ".", 0, eve, DMDIR|0555, &dir); + break; + case Qdata0: + devdir(c, c->qid, tab[1].name, qlen(p->q[0]), eve, tab[1].perm, &dir); + break; + case Qdata1: + devdir(c, c->qid, tab[2].name, qlen(p->q[1]), eve, tab[2].perm, &dir); + break; + default: + panic("pipestat"); + } + n = convD2M(&dir, db, n); + if(n < BIT16SZ) + error(Eshortstat); + return n; +} + +/* + * if the stream doesn't exist, create it + */ +static Chan* +pipeopen(Chan *c, int omode) +{ + Pipe *p; + + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Ebadarg); + c->mode = omode; + c->flag |= COPEN; + c->offset = 0; + return c; + } + + openmode(omode); /* check it */ + + p = c->aux; + qlock(p); + if(waserror()){ + qunlock(p); + nexterror(); + } + switch(NETTYPE(c->qid.path)){ + case Qdata0: + devpermcheck(p->user, p->pipedir[1].perm, omode); + p->qref[0]++; + break; + case Qdata1: + devpermcheck(p->user, p->pipedir[2].perm, omode); + p->qref[1]++; + break; + } + poperror(); + qunlock(p); + + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + c->iounit = qiomaxatomic; + return c; +} + +static void +pipeclose(Chan *c) +{ + Pipe *p; + + p = c->aux; + qlock(p); + + if(c->flag & COPEN){ + /* + * closing either side hangs up the stream + */ + switch(NETTYPE(c->qid.path)){ + case Qdata0: + p->qref[0]--; + if(p->qref[0] == 0){ + qhangup(p->q[1], 0); + qclose(p->q[0]); + } + break; + case Qdata1: + p->qref[1]--; + if(p->qref[1] == 0){ + qhangup(p->q[0], 0); + qclose(p->q[1]); + } + break; + } + } + + + /* + * if both sides are closed, they are reusable + */ + if(p->qref[0] == 0 && p->qref[1] == 0){ + qreopen(p->q[0]); + qreopen(p->q[1]); + } + + /* + * free the structure on last close + */ + p->ref--; + if(p->ref == 0){ + qunlock(p); + freepipe(p); + } else + qunlock(p); +} + +static long +piperead(Chan *c, void *va, long n, vlong) +{ + Pipe *p; + + p = c->aux; + + switch(NETTYPE(c->qid.path)){ + case Qdir: + return devdirread(c, va, n, p->pipedir, nelem(pipedir), pipegen); + case Qdata0: + return qread(p->q[0], va, n); + case Qdata1: + return qread(p->q[1], va, n); + default: + panic("piperead"); + } + return -1; /* not reached */ +} + +static Block* +pipebread(Chan *c, long n, ulong offset) +{ + Pipe *p; + + p = c->aux; + + switch(NETTYPE(c->qid.path)){ + case Qdata0: + return qbread(p->q[0], n); + case Qdata1: + return qbread(p->q[1], n); + } + + return devbread(c, n, offset); +} + +/* + * a write to a closed pipe causes an exception to be sent to + * the prog. + */ +static long +pipewrite(Chan *c, void *va, long n, vlong) +{ + Pipe *p; + Prog *r; + + if(waserror()) { + /* avoid exceptions when pipe is a mounted queue */ + if((c->flag & CMSG) == 0) { + r = up->iprog; + if(r != nil && r->kill == nil) + r->kill = "write on closed pipe"; + } + nexterror(); + } + + p = c->aux; + + switch(NETTYPE(c->qid.path)){ + case Qdata0: + n = qwrite(p->q[1], va, n); + break; + + case Qdata1: + n = qwrite(p->q[0], va, n); + break; + + default: + panic("pipewrite"); + } + + poperror(); + return n; +} + +static long +pipebwrite(Chan *c, Block *bp, ulong junk) +{ + long n; + Pipe *p; + Prog *r; + + USED(junk); + if(waserror()) { + /* avoid exceptions when pipe is a mounted queue */ + if((c->flag & CMSG) == 0) { + r = up->iprog; + if(r != nil && r->kill == nil) + r->kill = "write on closed pipe"; + } + nexterror(); + } + + p = c->aux; + switch(NETTYPE(c->qid.path)){ + case Qdata0: + n = qbwrite(p->q[1], bp); + break; + + case Qdata1: + n = qbwrite(p->q[0], bp); + break; + + default: + n = 0; + panic("pipebwrite"); + } + + poperror(); + return n; +} + +static int +pipewstat(Chan *c, uchar *dp, int n) +{ + Dir *d; + Pipe *p; + int d1; + + if (c->qid.type&QTDIR) + error(Eperm); + p = c->aux; + if(strcmp(up->env->user, p->user) != 0) + error(Eperm); + d = smalloc(sizeof(*d)+n); + if(waserror()){ + free(d); + nexterror(); + } + n = convM2D(dp, n, d, (char*)&d[1]); + if(n == 0) + error(Eshortstat); + d1 = NETTYPE(c->qid.path) == Qdata1; + if(!emptystr(d->name)){ + validwstatname(d->name); + if(strlen(d->name) >= KNAMELEN) + error(Efilename); + if(strcmp(p->pipedir[1+!d1].name, d->name) == 0) + error(Eexist); + kstrcpy(p->pipedir[1+d1].name, d->name, KNAMELEN); + } + if(d->mode != ~0UL) + p->pipedir[d1 + 1].perm = d->mode & 0777; + poperror(); + free(d); + return n; +} + +Dev pipedevtab = { + '|', + "pipe", + + devreset, + pipeinit, + devshutdown, + pipeattach, + pipewalk, + pipestat, + pipeopen, + devcreate, + pipeclose, + piperead, + pipebread, + pipewrite, + pipebwrite, + devremove, + pipewstat, +}; diff --git a/os/port/devpointer.c b/os/port/devpointer.c new file mode 100644 index 00000000..7b069397 --- /dev/null +++ b/os/port/devpointer.c @@ -0,0 +1,279 @@ +/* + * mouse or stylus + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +enum{ + Qdir, + Qpointer, + Qcursor, +}; + +typedef struct Pointer Pointer; + +struct Pointer { + int x; + int y; + int b; + ulong msec; +}; + +static struct +{ + Pointer; + int modify; + int lastb; + Rendez r; + Ref ref; + QLock q; +} mouse; + +static +Dirtab pointertab[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "pointer", {Qpointer}, 0, 0666, + "cursor", {Qcursor}, 0, 0222, +}; + +enum { + Nevent = 16 /* enough for some */ +}; + +static struct { + int rd; + int wr; + Pointer clicks[Nevent]; + Rendez r; + int full; + int put; + int get; +} ptrq; + +/* + * called by any source of pointer data + */ +void +mousetrack(int b, int x, int y, int isdelta) +{ + int lastb; + ulong msec; + Pointer e; + + if(isdelta){ + x += mouse.x; + y += mouse.y; + } + msec = TK2MS(MACHP(0)->ticks); + if(b && (mouse.b ^ b)&0x1f){ + if(msec - mouse.msec < 300 && mouse.lastb == b + && abs(mouse.x - x) < 12 && abs(mouse.y - y) < 12) + b |= 1<<8; + mouse.lastb = b & 0x1f; + mouse.msec = msec; + } + if(x == mouse.x && y == mouse.y && mouse.b == b) + return; + lastb = mouse.b; + mouse.x = x; + mouse.y = y; + mouse.b = b; + mouse.msec = msec; + if(!ptrq.full && lastb != b){ + e = mouse.Pointer; + ptrq.clicks[ptrq.wr] = e; + if(++ptrq.wr >= Nevent) + ptrq.wr = 0; + if(ptrq.wr == ptrq.rd) + ptrq.full = 1; + } + mouse.modify = 1; + ptrq.put++; + wakeup(&ptrq.r); + drawactive(1); + /* TO DO: cursor update */ +} + +static int +ptrqnotempty(void*) +{ + return ptrq.full || ptrq.put != ptrq.get; +} + +static Pointer +mouseconsume(void) +{ + Pointer e; + + sleep(&ptrq.r, ptrqnotempty, 0); + ptrq.full = 0; + ptrq.get = ptrq.put; + if(ptrq.rd != ptrq.wr){ + e = ptrq.clicks[ptrq.rd]; + if(++ptrq.rd >= Nevent) + ptrq.rd = 0; + }else + e = mouse.Pointer; + return e; +} + +Point +mousexy(void) +{ + return Pt(mouse.x, mouse.y); +} + + +static Chan* +pointerattach(char* spec) +{ + return devattach('m', spec); +} + +static Walkqid* +pointerwalk(Chan *c, Chan *nc, char **name, int nname) +{ + Walkqid *wq; + + wq = devwalk(c, nc, name, nname, pointertab, nelem(pointertab), devgen); + if(wq != nil && wq->clone != c && wq->clone != nil && (ulong)c->qid.path == Qpointer) + incref(&mouse.ref); /* can this happen? */ + return wq; +} + +static int +pointerstat(Chan* c, uchar *db, int n) +{ + return devstat(c, db, n, pointertab, nelem(pointertab), devgen); +} + +static Chan* +pointeropen(Chan* c, int omode) +{ + c = devopen(c, omode, pointertab, nelem(pointertab), devgen); + if((ulong)c->qid.path == Qpointer){ + if(waserror()){ + c->flag &= ~COPEN; + nexterror(); + } + if(!canqlock(&mouse.q)) + error(Einuse); + if(incref(&mouse.ref) != 1){ + qunlock(&mouse.q); + error(Einuse); + } + cursorenable(); + qunlock(&mouse.q); + poperror(); + } + return c; +} + +static void +pointerclose(Chan* c) +{ + if((c->flag & COPEN) == 0) + return; + switch((ulong)c->qid.path){ + case Qpointer: + qlock(&mouse.q); + if(decref(&mouse.ref) == 0) + cursordisable(); + qunlock(&mouse.q); + break; + } +} + +static long +pointerread(Chan* c, void* a, long n, vlong) +{ + Pointer mt; + char tmp[128]; + int l; + + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, pointertab, nelem(pointertab), devgen); + case Qpointer: + qlock(&mouse.q); + if(waserror()) { + qunlock(&mouse.q); + nexterror(); + } + mt = mouseconsume(); + poperror(); + qunlock(&mouse.q); + l = sprint(tmp, "m%11d %11d %11d %11lud ", mt.x, mt.y, mt.b, mt.msec); + if(l < n) + n = l; + memmove(a, tmp, n); + break; + case Qcursor: + /* TO DO: interpret data written as Image; give to drawcursor() */ + break; + default: + n=0; + break; + } + return n; +} + +static long +pointerwrite(Chan* c, void* va, long n, vlong) +{ + char *a = va; + char buf[128]; + int b, x, y; + + switch((ulong)c->qid.path){ + case Qpointer: + if(n > sizeof buf-1) + n = sizeof buf -1; + memmove(buf, va, n); + buf[n] = 0; + x = strtoul(buf+1, &a, 0); + if(*a == 0) + error(Eshort); + y = strtoul(a, &a, 0); + if(*a != 0) + b = strtoul(a, 0, 0); + else + b = mouse.b; + mousetrack(b, x, y, 0); + break; + default: + error(Ebadusefd); + } + return n; +} + +Dev pointerdevtab = { + 'm', + "pointer", + + devreset, + devinit, + devshutdown, + pointerattach, + pointerwalk, + pointerstat, + pointeropen, + devcreate, + pointerclose, + pointerread, + devbread, + pointerwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devprof.c b/os/port/devprof.c new file mode 100644 index 00000000..f0ee3ba8 --- /dev/null +++ b/os/port/devprof.c @@ -0,0 +1,722 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include <interp.h> +#include <isa.h> +#include "runt.h" + +static void cpxec(Prog *); +static void memprof(int, Heap*, ulong); + +extern Inst* pc2dispc(Inst*, Module*); + +static int interval = 100; /* Sampling interval in milliseconds */ + +enum +{ + HSIZE = 32, +}; + +#define HASH(m) ((m)%HSIZE) + +/* cope with multiple profilers some day */ + +typedef struct Record Record; +struct Record +{ + int id; + char* name; + char* path; + Inst* base; + int size; + /*Module* m; */ + ulong mtime; + Qid qid; + Record* hash; + Record* link; + ulong bucket[1]; +}; + +struct +{ + Lock l; + vlong time; + Record* hash[HSIZE]; + Record* list; +} profile; + +typedef struct Pmod Pmod; +struct Pmod +{ + char* name; + Pmod* link; +} *pmods; + +#define QSHIFT 4 +#define QID(q) ((ulong)(q).path&0xf) +#define QPID(pid) ((pid)<<QSHIFT) +#define PID(q) ((q).vers) +#define PATH(q) ((ulong)(q).path&~((1<<QSHIFT)-1)) + +enum +{ + Qdir, + Qname, + Qpath, + Qhist, + Qpctl, + Qctl, +}; + +Dirtab profdir[] = +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "name", {Qname}, 0, 0444, + "path", {Qpath}, 0, 0444, + "histogram", {Qhist}, 0, 0444, + "pctl", {Qpctl}, 0, 0222, + "ctl", {Qctl}, 0, 0222, +}; + +enum{ + Pnil, /* null profiler */ + Psam, /* sampling profiler */ + Pcov, /* coverage profiler */ + Pmem, /* memory profiler */ +}; + +static int profiler = Pnil; + +static int ids; +static int samplefn; + +static void sampler(void*); + +static Record* +getrec(int id) +{ + Record *r; + + for(r = profile.list; r != nil; r = r->link) + if(r->id == id) + break; + return r; +} + +static void +addpmod(char *m) +{ + Pmod *p = malloc(sizeof(Pmod)); + + if(p == nil) + return; + p->name = malloc(strlen(m)+1); + if(p->name == nil){ + free(p); + return; + } + strcpy(p->name, m); + p->link = pmods; + pmods = p; +} + +static void +freepmods(void) +{ + Pmod *p, *np; + + for(p = pmods; p != nil; p = np){ + free(p->name); + np = p->link; + free(p); + } + pmods = nil; +} + +static int +inpmods(char *m) +{ + Pmod *p; + + for(p = pmods; p != nil; p = p->link) + if(strcmp(p->name, m) == 0) + return 1; + return 0; +} + +static void +freeprof(void) +{ + int i; + Record *r, *nr; + + ids = 0; + profiler = Pnil; + freepmods(); + for(r = profile.list; r != nil; r = nr){ + free(r->path); + nr = r->link; + free(r); + } + profile.list = nil; + profile.time = 0; + for(i = 0; i < HSIZE; i++) + profile.hash[i] = nil; +} + +static int +profgen(Chan *c, char *name, Dirtab *d, int nd, int s, Dir *dp) +{ + Qid qid; + Record *r; + ulong path, perm, len; + Dirtab *tab; + + USED(name); + USED(d); + USED(nd); + + if(s == DEVDOTDOT) { + mkqid(&qid, Qdir, 0, QTDIR); + devdir(c, qid, "#P", 0, eve, 0555, dp); + return 1; + } + + if(c->qid.path == Qdir && c->qid.type & QTDIR) { + acquire(); + if(s-- == 0){ + tab = &profdir[Qctl]; + mkqid(&qid, PATH(c->qid)|tab->qid.path, c->qid.vers, QTFILE); + devdir(c, qid, tab->name, tab->length, eve, tab->perm, dp); + release(); + return 1; + } + r = profile.list; + while(s-- && r != nil) + r = r->link; + if(r == nil) { + release(); + return -1; + } + sprint(up->genbuf, "%.8lux", (ulong)r->id); + mkqid(&qid, (r->id<<QSHIFT), r->id, QTDIR); + devdir(c, qid, up->genbuf, 0, eve, DMDIR|0555, dp); + release(); + return 1; + } + if(s >= nelem(profdir)-1) + error(Enonexist); /* was return -1; */ + tab = &profdir[s]; + path = PATH(c->qid); + + acquire(); + r = getrec(PID(c->qid)); + if(r == nil) { + release(); + error(Enonexist); /* was return -1; */ + } + + perm = tab->perm; + len = tab->length; + mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE); + devdir(c, qid, tab->name, len, eve, perm, dp); + release(); + return 1; +} + +static Chan* +profattach(char *spec) +{ + return devattach('P', spec); +} + +static Walkqid* +profwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, profgen); +} + +static int +profstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, profgen); +} + +static Chan* +profopen(Chan *c, int omode) +{ + int qid; + Record *r; + + if(c->qid.type & QTDIR) { + if(omode != OREAD) + error(Eisdir); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; + } + + if(omode&OTRUNC) + error(Eperm); + + qid = QID(c->qid); + if(qid == Qctl || qid == Qpctl){ + if (omode != OWRITE) + error(Eperm); + } + else{ + if(omode != OREAD) + error(Eperm); + } + + if(qid != Qctl){ + acquire(); + r = getrec(PID(c->qid)); + release(); + if(r == nil) + error(Ethread); + } + + c->offset = 0; + c->flag |= COPEN; + c->mode = openmode(omode); + if(QID(c->qid) == Qhist) + c->aux = nil; + return c; +} + +static int +profwstat(Chan *c, uchar *dp, int n) +{ + Dir d; + Record *r; + + if(strcmp(up->env->user, eve)) + error(Eperm); + if(c->qid.type & QTDIR) + error(Eperm); + acquire(); + r = getrec(PID(c->qid)); + release(); + if(r == nil) + error(Ethread); + n = convM2D(dp, n, &d, nil); + if(n == 0) + error(Eshortstat); + d.mode &= 0777; + /* TO DO: copy to c->aux->perm, once that exists */ + return n; +} + +static void +profclose(Chan *c) +{ + USED(c); +} + +static long +profread(Chan *c, void *va, long n, vlong offset) +{ + int i; + Record *r; + char *a = va; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, profgen); + acquire(); + r = getrec(PID(c->qid)); + release(); + if(r == nil) + error(Ethread); + switch(QID(c->qid)){ + case Qname: + return readstr(offset, va, n, r->name); + case Qpath: + return readstr(offset, va, n, r->path); + case Qhist: + i = (int)c->aux; + while(i < r->size && r->bucket[i] == 0) + i++; + if(i >= r->size) + return 0; + c->aux = (void*)(i+1); + if(n < 20) + error(Etoosmall); + return sprint(a, "%d %lud", i, r->bucket[i]); + case Qctl: + error(Eperm); + } + return 0; +} + +static long +profwrite(Chan *c, void *va, long n, vlong offset) +{ + int i; + char *a = va; + char buf[128], *fields[128]; + + USED(va); + USED(n); + USED(offset); + + if(c->qid.type & QTDIR) + error(Eisdir); + switch(QID(c->qid)){ + case Qctl: + if(n > sizeof(buf)-1) + n = sizeof(buf)-1; + memmove(buf, a, n); + buf[n] = 0; + i = getfields(buf, fields, nelem(fields), 1, " \t\n"); + if(i > 0 && strcmp(fields[0], "module") == 0){ + freepmods(); + while(--i > 0) + addpmod(fields[i]); + return n; + } + if(i == 1){ + if(strcmp(fields[0], "start") == 0){ + if(profiler == Pnil) { + profiler = Psam; + if(!samplefn){ + samplefn = 1; + kproc("prof", sampler, 0, 0); + } + } + } + else if(strcmp(fields[0], "startmp") == 0){ + if(profiler == Pnil){ + profiler = Pmem; + heapmonitor = memprof; + } + } + else if(strcmp(fields[0], "stop") == 0) + profiler = Pnil; + else if(strcmp(fields[0], "end") == 0){ + profiler = Pnil; + freeprof(); + interval = 100; + } + else + error(Ebadarg); + } + else if (i == 2){ + if(strcmp(fields[0], "interval") == 0) + interval = strtoul(fields[1], nil, 0); + else if(strcmp(fields[0], "startcp") == 0){ + Prog *p; + + acquire(); + p = progpid(strtoul(fields[1], nil, 0)); + if(p == nil){ + release(); + return -1; + } + if(profiler == Pnil){ + profiler = Pcov; + p->xec = cpxec; + } + release(); + } + else + error(Ebadarg); + } + else + error(Ebadarg); + return n; + default: + error(Eperm); + } + return 0; +} + +static Record* +newmodule(Module *m, int vm, int scale, int origin) +{ + int dsize; + Record *r, **l; + + if(!vm) + acquire(); + if((m->compiled && m->pctab == nil) || m->prog == nil) { + if(!vm) + release(); + return nil; + } +/* print("newmodule %x %s %s %d %d %d\n", m, m->name, m->path, m->mtime, m->qid.path, m->qid.vers); */ + if(m->compiled) + dsize = m->nprog * sizeof(r->bucket[0]); + else + dsize = (msize(m->prog)/sizeof(Inst)) * sizeof(r->bucket[0]); + dsize *= scale; + dsize += origin; + r = malloc(sizeof(Record)+dsize); + if(r == nil) { + if(!vm) + release(); + return nil; + } + + r->id = ++ids; + if(ids == (1<<8)-1) + ids = 0; + kstrdup(&r->name, m->name); + kstrdup(&r->path, m->path); + r->base = m->prog; + r->size = dsize/sizeof(r->bucket[0]); + // r->m = m; + r->mtime = m->mtime; + r->qid.path = m->qid.path; + r->qid.vers = m->qid.vers; + memset(r->bucket, 0, dsize); + r->link = profile.list; + profile.list = r; + + l = &profile.hash[HASH(m->mtime)]; + r->hash = *l; + *l = r; + + if(!vm) + release(); + return r; +} + +static Record* +mlook(Module *m, int vm, int scale, int origin) +{ + Record *r; + + for(r = profile.hash[HASH(m->mtime)]; r; r = r->hash){ + /* if(r->m == m){ /* bug - r->m could be old exited module */ + if(r->mtime == m->mtime && r->qid.path == m->qid.path && r->qid.vers == m->qid.vers && strcmp(r->name, m->name) == 0 && strcmp(r->path, m->path) == 0){ + r->base = m->prog; + return r; + } + } + if(pmods == nil || inpmods(m->name) || inpmods(m->path)){ + if(0 && profiler == Pmem) + heapmonitor = nil; + r = newmodule(m, vm, scale, origin); + if(0 && profiler == Pmem) + heapmonitor = memprof; + return r; + } + return nil; +} + +static void +sampler(void* a) +{ + int i; + Module *m; + Record *r; + Inst *p; + + USED(a); + for(;;) { + tsleep(&up->sleep, return0, nil, interval); + if(profiler != Psam) + break; + lock(&profile.l); + profile.time += interval; + if(R.M == H || (m = R.M->m) == nil){ + unlock(&profile.l); + continue; + } + p = R.PC; + r = mlook(m, 0, 1, 0); + if(r == nil){ + unlock(&profile.l); + continue; + } + if(m->compiled && m->pctab != nil) + p = pc2dispc(p, m); + if((i = p-r->base) >= 0 && i < r->size) + r->bucket[i]++; + unlock(&profile.l); + } + samplefn = 0; + pexit("", 0); +} + +/* + * coverage profiling + */ + +static void +cpxec(Prog *p) +{ + int op, i; + Module *m; + Record *r; + Prog *n; + + R = p->R; + R.MP = R.M->MP; + R.IC = p->quanta; + + if(p->kill != nil){ + char *m; + m = p->kill; + p->kill = nil; + error(m); + } + + if(R.M->compiled) + comvec(); + else{ + m = R.M->m; + r = profiler == Pcov ? mlook(m, 1, 1, 0) : nil; + do{ + dec[R.PC->add](); + op = R.PC->op; + if(r != nil){ + i = R.PC-r->base; + if(i >= 0 && i < r->size) + r->bucket[i]++; + } + R.PC++; + optab[op](); + if(op == ISPAWN || op == IMSPAWN){ + n = delruntail(Pdebug); // any state will do + n->xec = cpxec; + addrun(n); + } + if(m != R.M->m){ + m = R.M->m; + r = profiler == Pcov ? mlook(m, 1, 1, 0) : nil; + } + }while(--R.IC != 0); + } + + p->R = R; +} + +/* memory profiling */ + +enum{ + Halloc, + Hfree, + Hgcfree, +}; + +#define MPAD sizeof(double) + +static void +memprof(int c, Heap *h, ulong n) +{ + int i, j, k; + ulong kk, *b; + Module *m; + Record *r; + Inst *p; + +/* print("%d %x %uld\n", c, h, n); */ + USED(h); + if(profiler != Pmem){ + heapmonitor = nil; + return; + } + lock(&profile.l); + m = nil; + if(c != Hgcfree && (R.M == H || (m = R.M->m) == nil)){ + unlock(&profile.l); + return; + } + j = n; + if(c == 0 || c == 4){ /* heap or main allocation */ + p = R.PC; + if(m->compiled && m->pctab != nil) + p = pc2dispc(p, m); + if((r = mlook(m, 1, 2, 2)) == nil){ + unlock(&profile.l); + return; + } + i = p-r->base; + k = (r->id<<24) | i; + if(c == 0){ + h->pad = k; + k = sizeof(Heap); + } + else{ + *(ulong*)h = k; + k = MPAD; + } + /* 31 is pool quanta - dependency on alloc.c */ + j = ((j+k+BHDRSIZE+31)&~31) - (k+BHDRSIZE); + } + else{ + /* c == 1 is ref count free */ + /* c == 2 is gc free */ + /* c == 3 is main free */ + if(c == 3) + k = *(ulong*)h; + else + k = h->pad; + if((r = getrec(k>>24)) == nil){ + unlock(&profile.l); + return; + } + i = k&0xffffff; + if(c == 3) + j = msize(h)-MPAD; + else + j = hmsize(h)-sizeof(Heap); + j = -j; + } + i = 2*(i+1); + b = r->bucket; + if(i >= 0 && i < r->size){ + if(0){ + if(c == 1){ + b[0] -= j; + b[i] -= j; + } + else if(c == 2){ + b[1] -= j; + b[i+1] -= j; + } + } + else{ + b[0] += j; + if((int)b[0] < 0) + b[0] = 0; + b[i] += j; + if((int)b[i] < 0) + b[i] = 0; + if(j > 0){ + if((kk = b[0]) > b[1]) + b[1] = kk; + if((kk = b[i]) > b[i+1]) + b[i+1] = kk; + } + } + } + unlock(&profile.l); +} + +Dev profdevtab = { + 'P', + "prof", + + devreset, + devinit, + devshutdown, + profattach, + profwalk, + profstat, + profopen, + devcreate, + profclose, + profread, + devbread, + profwrite, + devbwrite, + devremove, + profwstat +}; diff --git a/os/port/devprog.c b/os/port/devprog.c new file mode 100644 index 00000000..c6c97efc --- /dev/null +++ b/os/port/devprog.c @@ -0,0 +1,1510 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "error.h" +#include <interp.h> +#include <isa.h> +#include "runt.h" + +/* + * Enable the heap device for environments that allow debugging => + * Must be 1 for a production environment. + */ +int SECURE = 0; + +enum +{ + Qdir, + Qctl, + Qdbgctl, + Qheap, + Qns, + Qnsgrp, + Qpgrp, + Qstack, + Qstatus, + Qtext, + Qwait, + Qfd, + Qexception, +}; + +/* + * must be in same order as enum + */ +Dirtab progdir[] = +{ + "ctl", {Qctl}, 0, 0200, + "dbgctl", {Qdbgctl}, 0, 0600, + "heap", {Qheap}, 0, 0600, + "ns", {Qns}, 0, 0400, + "nsgrp", {Qnsgrp}, 0, 0444, + "pgrp", {Qpgrp}, 0, 0444, + "stack", {Qstack}, 0, 0400, + "status", {Qstatus}, 0, 0444, + "text", {Qtext}, 0, 0000, + "wait", {Qwait}, 0, 0400, + "fd", {Qfd}, 0, 0400, + "exception", {Qexception}, 0, 0400, +}; + +enum +{ + CMkill, + CMkillgrp, + CMrestricted, + CMexceptions, + CMprivate +}; + +static +Cmdtab progcmd[] = { + CMkill, "kill", 1, + CMkillgrp, "killgrp", 1, + CMrestricted, "restricted", 1, + CMexceptions, "exceptions", 2, + CMprivate, "private", 1, +}; + +enum +{ + CDstep, + CDtoret, + CDcont, + CDstart, + CDstop, + CDunstop, + CDmaim, + CDbpt +}; + +static +Cmdtab progdbgcmd[] = { + CDstep, "step", 0, /* known below to be first, to cope with stepN */ + CDtoret, "toret", 1, + CDcont, "cont", 1, + CDstart, "start", 1, + CDstop, "stop", 1, + CDunstop, "unstop", 1, + CDmaim, "maim", 1, + CDbpt, "bpt", 4, +}; + +typedef struct Heapqry Heapqry; +struct Heapqry +{ + char fmt; + ulong addr; + ulong module; + int count; +}; + +typedef struct Bpt Bpt; + +struct Bpt +{ + Bpt *next; + int pc; + char *file; + char path[1]; +}; + +typedef struct Progctl Progctl; +struct Progctl +{ + Rendez r; + int ref; + Proc *debugger; /* waiting for dbgxec */ + char *msg; /* reply from dbgxec */ + int step; /* instructions to try */ + int stop; /* stop running the program */ + Bpt* bpts; /* active breakpoints */ + Queue* q; /* status queue */ +}; + +#define QSHIFT 4 /* location in qid of pid */ +#define QID(q) (((ulong)(q).path&0x0000000F)>>0) +#define QPID(pid) (((pid)<<QSHIFT)) +#define PID(q) ((q).vers) +#define PATH(q) ((ulong)(q).path&~((1<<QSHIFT)-1)) + +static char *progstate[] = /* must correspond to include/interp.h */ +{ + "alt", /* blocked in alt instruction */ + "send", /* waiting to send */ + "recv", /* waiting to recv */ + "debug", /* debugged */ + "ready", /* ready to be scheduled */ + "release", /* interpreter released */ + "exiting", /* exit because of kill or error */ + "broken", /* thread crashed */ +}; + +static void dbgstep(Progctl*, Prog*, int); +static void dbgstart(Prog*); +static void freebpts(Bpt*); +static Bpt* delbpt(Bpt*, char*, int); +static Bpt* setbpt(Bpt*, char*, int); +static void mntscan(Mntwalk*, Pgrp*); +extern Type *Trdchan; +extern Type *Twrchan; +extern Module* modules; +static char Emisalign[] = "misaligned address"; + +static int +proggen(Chan *c, char *name, Dirtab *tab, int, int s, Dir *dp) +{ + Qid qid; + Prog *p; + char *e; + Osenv *o; + ulong pid, path, perm, len; + + if(s == DEVDOTDOT){ + mkqid(&qid, Qdir, 0, QTDIR); + devdir(c, qid, "#p", 0, eve, DMDIR|0555, dp); + return 1; + } + + if((ulong)c->qid.path == Qdir) { + if(name != nil){ + /* ignore s and use name to find pid */ + pid = strtoul(name, &e, 0); + if(pid == 0 || *e != '\0') + return -1; + acquire(); + p = progpid(pid); + if(p == nil){ + release(); + return -1; + } + }else{ + acquire(); + p = progn(s); + if(p == nil) { + release(); + return -1; + } + pid = p->pid; + } + o = p->osenv; + sprint(up->genbuf, "%lud", pid); + if(name != nil && strcmp(name, up->genbuf) != 0){ + release(); + return -1; + } + mkqid(&qid, pid<<QSHIFT, pid, QTDIR); + devdir(c, qid, up->genbuf, 0, o->user, DMDIR|0555, dp); + release(); + return 1; + } + + if(s >= nelem(progdir)) + return -1; + tab = &progdir[s]; + path = PATH(c->qid); + + acquire(); + p = progpid(PID(c->qid)); + if(p == nil) { + release(); + return -1; + } + + o = p->osenv; + + perm = tab->perm; + if((perm & 7) == 0) + perm = (perm|(perm>>3)|(perm>>6)) & o->pgrp->progmode; + + len = tab->length; + mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE); + devdir(c, qid, tab->name, len, o->user, perm, dp); + release(); + return 1; +} + +static Chan* +progattach(char *spec) +{ + return devattach('p', spec); +} + +static Walkqid* +progwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, proggen); +} + +static int +progstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, proggen); +} + +static Chan* +progopen(Chan *c, int omode) +{ + Prog *p; + Osenv *o; + Progctl *ctl; + int perm; + + if(c->qid.type & QTDIR) + return devopen(c, omode, 0, 0, proggen); + + acquire(); + if (waserror()) { + release(); + nexterror(); + } + p = progpid(PID(c->qid)); + if(p == nil) + error(Ethread); + + o = p->osenv; + perm = progdir[QID(c->qid)-1].perm; + if((perm & 7) == 0) + perm = (perm|(perm>>3)|(perm>>6)) & o->pgrp->progmode; + devpermcheck(o->user, perm, omode); + omode = openmode(omode); + + switch(QID(c->qid)){ + default: + error(Egreg); + case Qnsgrp: + case Qpgrp: + case Qtext: + case Qstatus: + case Qstack: + case Qctl: + case Qfd: + case Qexception: + break; + case Qwait: + c->aux = qopen(1024, Qmsg, nil, nil); + if(c->aux == nil) + error(Enomem); + o->childq = c->aux; + break; + case Qns: + c->aux = malloc(sizeof(Mntwalk)); + if(c->aux == nil) + error(Enomem); + break; + case Qheap: + if(SECURE || o->pgrp->privatemem || omode != ORDWR) + error(Eperm); + c->aux = malloc(sizeof(Heapqry)); + if(c->aux == nil) + error(Enomem); + break; + case Qdbgctl: + if(SECURE || o->pgrp->privatemem || omode != ORDWR) + error(Eperm); + ctl = malloc(sizeof(Progctl)); + if(ctl == nil) + error(Enomem); + ctl->q = qopen(1024, Qmsg, nil, nil); + if(ctl->q == nil) { + free(ctl); + error(Enomem); + } + ctl->bpts = nil; + ctl->ref = 1; + c->aux = ctl; + break; + } + if(p->state != Pexiting) + c->qid.vers = p->pid; + + poperror(); + release(); + c->offset = 0; + c->mode = omode; + c->flag |= COPEN; + return c; +} + +static int +progwstat(Chan *c, uchar *db, int n) +{ + Dir d; + Prog *p; + char *u; + Osenv *o; + + if(c->qid.type&QTDIR) + error(Eperm); + acquire(); + p = progpid(PID(c->qid)); + if(p == nil) { + release(); + error(Ethread); + } + + u = up->env->user; + o = p->osenv; + if(strcmp(u, o->user) != 0 && strcmp(u, eve) != 0) { + release(); + error(Eperm); + } + + n = convM2D(db, n, &d, nil); + if(n == 0){ + release(); + error(Eshortstat); + } + if(d.mode != ~0UL) + o->pgrp->progmode = d.mode&0777; + release(); + return n; +} + +static void +closedbgctl(Progctl *ctl, Prog *p) +{ + Osenv *o; + + if(ctl->ref-- > 1) + return; + freebpts(ctl->bpts); + if(p != nil){ + o = p->osenv; + if(o->debug == ctl){ + o->debug = nil; + p->xec = xec; + } + } + qfree(ctl->q); + free(ctl); +} + +static void +progclose(Chan *c) +{ + int i; + Prog *f; + Osenv *o; + Progctl *ctl; + + switch(QID(c->qid)) { + case Qns: + case Qheap: + free(c->aux); + break; + case Qdbgctl: + if((c->flag & COPEN) == 0) + return; + ctl = c->aux; + acquire(); + closedbgctl(ctl, progpid(PID(c->qid))); + release(); + break; + case Qwait: + acquire(); + i = 0; + for(;;) { + f = progn(i++); + if(f == nil) + break; + o = f->osenv; + if(o->waitq == c->aux) + o->waitq = nil; + if(o->childq == c->aux) + o->childq = nil; + } + release(); + qfree(c->aux); + } +} + +static int +progsize(Prog *p) +{ + int size; + Frame *f; + uchar *fp; + Modlink *m; + + m = p->R.M; + size = 0; + if(m->MP != H) + size += hmsize(D2H(m->MP)); + if(m->prog != nil) + size += msize(m->prog); + + fp = p->R.FP; + while(fp != nil) { + f = (Frame*)fp; + fp = f->fp; + if(f->mr != nil) { + if(f->mr->MP != H) + size += hmsize(D2H(f->mr->MP)); + if(f->mr->prog != nil) + size += msize(f->mr->prog); + } + if(f->t == nil) + size += msize(SEXTYPE(f)); + } + return size/1024; +} + +static long +progoffset(long offset, char *va, int *np) +{ + if(offset > 0) { + offset -= *np; + if(offset < 0) { + memmove(va, va+*np+offset, -offset); + *np = -offset; + } + else + *np = 0; + } + return offset; +} + +static int +progqidwidth(Chan *c) +{ + char buf[32]; + + return sprint(buf, "%lud", c->qid.vers); +} + +int +progfdprint(Chan *c, int fd, int w, char *s, int ns) +{ + int n; + + if(w == 0) + w = progqidwidth(c); + n = snprint(s, ns, "%3d %.2s %C %4ld (%.16llux %*lud %.2ux) %5ld %8lld %s\n", + fd, + &"r w rw"[(c->mode&3)<<1], + devtab[c->type]->dc, c->dev, + c->qid.path, w, c->qid.vers, c->qid.type, + c->iounit, c->offset, c->name->s); + return n; +} + +static int +progfds(Osenv *o, char *va, int count, long offset) +{ + Fgrp *f; + Chan *c; + int n, i, w, ww; + + f = o->fgrp; /* f is not locked because we've acquired */ + n = readstr(0, va, count, o->pgrp->dot->name->s); + n += snprint(va+n, count-n, "\n"); + offset = progoffset(offset, va, &n); + /* compute width of qid.path */ + w = 0; + for(i = 0; i <= f->maxfd; i++) { + c = f->fd[i]; + if(c == nil) + continue; + ww = progqidwidth(c); + if(ww > w) + w = ww; + } + for(i = 0; i <= f->maxfd; i++) { + c = f->fd[i]; + if(c == nil) + continue; + n += progfdprint(c, i, w, va+n, count-n); + offset = progoffset(offset, va, &n); + } + return n; +} + +Inst * +pc2dispc(Inst *pc, Module *mod) +{ + ulong l, u, m, v; + ulong *tab = mod->pctab; + + v = (ulong)pc - (ulong)mod->prog; + l = 0; + u = mod->nprog-1; + while(l < u){ + m = (l+u+1)/2; + if(tab[m] < v) + l = m; + else if(tab[m] > v) + u = m-1; + else + l = u = m; + } + if(l == u && tab[u] <= v && u != mod->nprog-1 && tab[u+1] > v) + return &mod->prog[u]; + return 0; +} + +static int +progstack(REG *reg, int state, char *va, int count, long offset) +{ + int n; + Frame *f; + Inst *pc; + uchar *fp; + Modlink *m; + + n = 0; + m = reg->M; + fp = reg->FP; + pc = reg->PC; + + /* + * all states other than debug and ready block, + * but interp has already advanced the PC + */ + if(!m->compiled && state != Pready && state != Pdebug && pc > m->prog) + pc--; + if(m->compiled && m->m->pctab != nil) + pc = pc2dispc(pc, m->m); + + while(fp != nil) { + f = (Frame*)fp; + n += snprint(va+n, count-n, "%.8lux %.8lux %.8lux %.8lux %d %s\n", + (ulong)f, /* FP */ + (ulong)(pc - m->prog), /* PC in dis instructions */ + (ulong)m->MP, /* MP */ + (ulong)m->prog, /* Code for module */ + m->compiled && m->m->pctab == nil, /* True if native assembler: fool stack utility for now */ + m->m->path); /* File system path */ + + if(offset > 0) { + offset -= n; + if(offset < 0) { + memmove(va, va+n+offset, -offset); + n = -offset; + } + else + n = 0; + } + + pc = f->lr; + fp = f->fp; + if(f->mr != nil) + m = f->mr; + if(!m->compiled) + pc--; + else if(m->m->pctab != nil) + pc = pc2dispc(pc, m->m)-1; + } + return n; +} + +static int +calldepth(REG *reg) +{ + int n; + uchar *fp; + + n = 0; + for(fp = reg->FP; fp != nil; fp = ((Frame*)fp)->fp) + n++; + return n; +} + +static int +progheap(Heapqry *hq, char *va, int count, ulong offset) +{ + WORD *w; + void *p; + List *hd; + Array *a; + char *fmt, *str; + Module *m; + Modlink *ml; + Channel *c; + ulong addr; + String *ss; + union { REAL r; LONG l; WORD w[2]; } rock; + int i, s, n, len, signed_off; + Type *t; + + n = 0; + s = 0; + signed_off = offset; + addr = hq->addr; + for(i = 0; i < hq->count; i++) { + switch(hq->fmt) { + case 'W': + if(addr & 3) + return -1; + n += snprint(va+n, count-n, "%d\n", *(WORD*)addr); + s = sizeof(WORD); + break; + case 'B': + n += snprint(va+n, count-n, "%d\n", *(BYTE*)addr); + s = sizeof(BYTE); + break; + case 'V': + if(addr & 3) + return -1; + w = (WORD*)addr; + rock.w[0] = w[0]; + rock.w[1] = w[1]; + n += snprint(va+n, count-n, "%lld\n", rock.l); + s = sizeof(LONG); + break; + case 'R': + if(addr & 3) + return -1; + w = (WORD*)addr; + rock.w[0] = w[0]; + rock.w[1] = w[1]; + n += snprint(va+n, count-n, "%g\n", rock.r); + s = sizeof(REAL); + break; + case 'I': + if(addr & 3) + return -1; + for(m = modules; m != nil; m = m->link) + if(m == (Module*)hq->module) + break; + if(m == nil) + error(Ebadctl); + addr = (ulong)(m->prog+addr); + n += snprint(va+n, count-n, "%D\n", (Inst*)addr); + s = sizeof(Inst); + break; + case 'P': + if(addr & 3) + return -1; + p = *(void**)addr; + fmt = "nil\n"; + if(p != H) + fmt = "%lux\n"; + n += snprint(va+n, count-n, fmt, p); + s = sizeof(WORD); + break; + case 'L': + if(addr & 3) + return -1; + hd = *(List**)addr; + if(hd == H || D2H(hd)->t != &Tlist) + return -1; + n += snprint(va+n, count-n, "%lux.%lux\n", (ulong)&hd->tail, (ulong)hd->data); + s = sizeof(WORD); + break; + case 'A': + if(addr & 3) + return -1; + a = *(Array**)addr; + if(a == H) + n += snprint(va+n, count-n, "nil\n"); + else { + if(D2H(a)->t != &Tarray) + return -1; + n += snprint(va+n, count-n, "%d.%lux\n", a->len, (ulong)a->data); + } + s = sizeof(WORD); + break; + case 'C': + if(addr & 3) + return -1; + ss = *(String**)addr; + if(ss == H) + ss = &snil; + else + if(D2H(ss)->t != &Tstring) + return -1; + n += snprint(va+n, count-n, "%d.", abs(ss->len)); + str = string2c(ss); + len = strlen(str); + if(count-n < len) + len = count-n; + if(len > 0) { + memmove(va+n, str, len); + n += len; + } + break; + case 'M': + if(addr & 3) + return -1; + ml = *(Modlink**)addr; + fmt = ml == H ? "nil\n" : "%lux\n"; + n += snprint(va+n, count-n, fmt, ml->MP); + s = sizeof(WORD); + break; + case 'c': + if(addr & 3) + return -1; + c = *(Channel**)addr; + if(c == H) + n += snprint(va+n, count-n, "nil\n"); + else{ + t = D2H(c)->t; + if(t != &Tchannel && t != Trdchan && t != Twrchan) + return -1; + if(c->buf == H) + n += snprint(va+n, count-n, "0.%lux\n", (ulong)c); + else + n += snprint(va+n, count-n, "%d.%lux.%d.%d\n", c->buf->len, (ulong)c->buf->data, c->front, c->size); + } + break; + + } + addr += s; + if(signed_off > 0) { + signed_off -= n; + if(signed_off < 0) { + memmove(va, va+n+signed_off, -signed_off); + n = -signed_off; + } + else + n = 0; + } + } + return n; +} + +WORD +modstatus(REG *r, char *ptr, int len) +{ + Inst *PC; + Frame *f; + + if(r->M->m->name[0] == '$') { + f = (Frame*)r->FP; + snprint(ptr, len, "%s[%s]", f->mr->m->name, r->M->m->name); + if(f->mr->compiled) + return (WORD)f->lr; + return f->lr - f->mr->prog; + } + memmove(ptr, r->M->m->name, len); + if(r->M->compiled) + return (WORD)r->PC; + PC = r->PC; + /* should really check for blocked states */ + if(PC > r->M->prog) + PC--; + return PC - r->M->prog; +} + +static void +int2flag(int flag, char *s) +{ + if(flag == 0){ + *s = '\0'; + return; + } + *s++ = '-'; + if(flag & MAFTER) + *s++ = 'a'; + if(flag & MBEFORE) + *s++ = 'b'; + if(flag & MCREATE) + *s++ = 'c'; + if(flag & MCACHE) + *s++ = 'C'; + *s = '\0'; +} + +static char* +progtime(ulong msec, char *buf, char *ebuf) +{ + int tenths, sec; + + tenths = msec/100; + sec = tenths/10; + seprint(buf, ebuf, "%4d:%2.2d.%d", sec/60, sec%60, tenths%10); + return buf; +} + +static long +progread(Chan *c, void *va, long n, vlong offset) +{ + int i; + Prog *p; + Osenv *o; + Mntwalk *mw; + ulong grpid; + char *a = va; + Progctl *ctl; + char mbuf[64], timebuf[12]; + char flag[10]; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, proggen); + + switch(QID(c->qid)){ + case Qdbgctl: + ctl = c->aux; + return qread(ctl->q, va, n); + case Qstatus: + acquire(); + p = progpid(PID(c->qid)); + if(p == nil || p->state == Pexiting || p->R.M == H) { + release(); + snprint(up->genbuf, sizeof(up->genbuf), "%8lud %8d %10s %s %10s %5dK %s", + PID(c->qid), + 0, + eve, + progtime(0, timebuf, timebuf+sizeof(timebuf)), + progstate[Pexiting], + 0, + "[$Sys]"); + return readstr(offset, va, n, up->genbuf); + } + modstatus(&p->R, mbuf, sizeof(mbuf)); + o = p->osenv; + snprint(up->genbuf, sizeof(up->genbuf), "%8d %8d %10s %s %10s %5dK %s", + p->pid, + p->group!=nil? p->group->id: 0, + o->user, + progtime(TK2MS(p->ticks), timebuf, timebuf+sizeof(timebuf)), + progstate[p->state], + progsize(p), + mbuf); + release(); + return readstr(offset, va, n, up->genbuf); + case Qwait: + return qread(c->aux, va, n); + case Qns: + acquire(); + if(waserror()){ + release(); + nexterror(); + } + p = progpid(PID(c->qid)); + if(p == nil) + error(Ethread); + mw = c->aux; + if(mw->cddone){ + poperror(); + release(); + return 0; + } + o = p->osenv; + mntscan(mw, o->pgrp); + if(mw->mh == 0) { + mw->cddone = 1; + i = snprint(a, n, "cd %s\n", o->pgrp->dot->name->s); + poperror(); + release(); + return i; + } + int2flag(mw->cm->mflag, flag); + if(strcmp(mw->cm->to->name->s, "#M") == 0){ + i = snprint(a, n, "mount %s %s %s %s\n", flag, + mw->cm->to->mchan->name->s, + mw->mh->from->name->s, mw->cm->spec? mw->cm->spec : ""); + }else + i = snprint(a, n, "bind %s %s %s\n", flag, + mw->cm->to->name->s, mw->mh->from->name->s); + poperror(); + release(); + return i; + case Qnsgrp: + acquire(); + p = progpid(PID(c->qid)); + if(p == nil) { + release(); + error(Ethread); + } + grpid = ((Osenv *)p->osenv)->pgrp->pgrpid; + release(); + return readnum(offset, va, n, grpid, NUMSIZE); + case Qpgrp: + acquire(); + p = progpid(PID(c->qid)); + if(p == nil) { + release(); + error(Ethread); + } + grpid = p->group!=nil? p->group->id: 0; + release(); + return readnum(offset, va, n, grpid, NUMSIZE); + case Qstack: + acquire(); + p = progpid(PID(c->qid)); + if(p == nil || p->state == Pexiting) { + release(); + error(Ethread); + } + if(p->state == Pready) { + release(); + error(Estopped); + } + n = progstack(&p->R, p->state, va, n, offset); + release(); + return n; + case Qheap: + acquire(); + if(waserror()){ + release(); + nexterror(); + } + n = progheap(c->aux, va, n, offset); + if(n == -1) + error(Emisalign); + poperror(); + release(); + return n; + case Qfd: + acquire(); + if(waserror()) { + release(); + nexterror(); + } + p = progpid(PID(c->qid)); + if(p == nil) + error(Ethread); + o = p->osenv; + n = progfds(o, va, n, offset); + poperror(); + release(); + return n; + case Qexception: + acquire(); + p = progpid(PID(c->qid)); + if(p == nil) { + release(); + error(Ethread); + } + if(p->exstr == nil) + up->genbuf[0] = 0; + else + snprint(up->genbuf, sizeof(up->genbuf), p->exstr); + release(); + return readstr(offset, va, n, up->genbuf); + } + error(Egreg); + return 0; +} + +static void +mntscan(Mntwalk *mw, Pgrp *pg) +{ + Mount *t; + Mhead *f; + int nxt, i; + ulong last, bestmid; + + rlock(&pg->ns); + + nxt = 0; + bestmid = ~0; + + last = 0; + if(mw->mh) + last = mw->cm->mountid; + + for(i = 0; i < MNTHASH; i++) { + for(f = pg->mnthash[i]; f; f = f->hash) { + for(t = f->mount; t; t = t->next) { + if(mw->mh == 0 || + (t->mountid > last && t->mountid < bestmid)) { + mw->cm = t; + mw->mh = f; + bestmid = mw->cm->mountid; + nxt = 1; + } + } + } + } + if(nxt == 0) + mw->mh = 0; + + runlock(&pg->ns); +} + +static long +progwrite(Chan *c, void *va, long n, vlong offset) +{ + Prog *p, *f; + Heapqry *hq; + char buf[128]; + Progctl *ctl; + char *b; + int i, pc; + Cmdbuf *cb; + Cmdtab *ct; + Osenv *o; + + USED(offset); + USED(va); + + if(c->qid.type & QTDIR) + error(Eisdir); + + acquire(); + if(waserror()) { + release(); + nexterror(); + } + p = progpid(PID(c->qid)); + if(p == nil) + error(Ethread); + + switch(QID(c->qid)){ + case Qctl: + cb = parsecmd(va, n); + if(waserror()){ + free(cb); + nexterror(); + } + ct = lookupcmd(cb, progcmd, nelem(progcmd)); + switch(ct->index){ + case CMkillgrp: + killgrp(p, "killed"); + break; + case CMkill: + killprog(p, "killed"); + break; + case CMrestricted: + p->flags |= Prestrict; + break; + case CMexceptions: + if(p->group->id != p->pid) + error(Eperm); + if(strcmp(cb->f[1], "propagate") == 0) + p->flags |= Ppropagate; + else if(strcmp(cb->f[1], "notifyleader") == 0) + p->flags |= Pnotifyleader; + else + error(Ebadctl); + break; + case CMprivate: + o = p->osenv; + o->pgrp->privatemem = 1; + break; + } + poperror(); + free(cb); + break; + case Qdbgctl: + cb = parsecmd(va, n); + if(waserror()){ + free(cb); + nexterror(); + } + if(cb->nf == 1 && strncmp(cb->f[0], "step", 4) == 0) + ct = progdbgcmd; + else + ct = lookupcmd(cb, progdbgcmd, nelem(progdbgcmd)); + switch(ct->index){ + case CDstep: + if(cb->nf == 1) + i = strtoul(cb->f[0]+4, nil, 0); + else + i = strtoul(cb->f[1], nil, 0); + dbgstep(c->aux, p, i); + break; + case CDtoret: + f = currun(); + i = calldepth(&p->R); + while(f->kill == nil) { + dbgstep(c->aux, p, 1024); + if(i > calldepth(&p->R)) + break; + } + break; + case CDcont: + f = currun(); + while(f->kill == nil) + dbgstep(c->aux, p, 1024); + break; + case CDstart: + dbgstart(p); + break; + case CDstop: + ctl = c->aux; + ctl->stop = 1; + break; + case CDunstop: + ctl = c->aux; + ctl->stop = 0; + break; + case CDbpt: + pc = strtoul(cb->f[3], nil, 10); + ctl = c->aux; + if(strcmp(cb->f[1], "set") == 0) + ctl->bpts = setbpt(ctl->bpts, cb->f[2], pc); + else if(strcmp(cb->f[1], "del") == 0) + ctl->bpts = delbpt(ctl->bpts, cb->f[2], pc); + else + error(Ebadctl); + break; + case CDmaim: + p->kill = "maim"; + break; + } + poperror(); + free(cb); + break; + case Qheap: + /* + * Heap query: + * addr.Fn + * pc+module.In + */ + i = n; + if(i > sizeof(buf)-1) + i = sizeof(buf)-1; + memmove(buf, va, i); + buf[i] = '\0'; + hq = c->aux; + hq->addr = strtoul(buf, &b, 0); + if(*b == '+') + hq->module = strtoul(b, &b, 0); + if(*b++ != '.') + error(Ebadctl); + hq->fmt = *b++; + hq->count = strtoul(b, nil, 0); + break; + default: + print("unknown qid in procwrite\n"); + error(Egreg); + } + poperror(); + release(); + return n; +} + +static Bpt* +setbpt(Bpt *bpts, char *path, int pc) +{ + int n; + Bpt *b; + + n = strlen(path); + b = mallocz(sizeof *b + n, 0); + if(b == nil) + return bpts; + b->pc = pc; + memmove(b->path, path, n+1); + b->file = b->path; + path = strrchr(b->path, '/'); + if(path != nil) + b->file = path + 1; + b->next = bpts; + return b; +} + +static Bpt* +delbpt(Bpt *bpts, char *path, int pc) +{ + Bpt *b, **last; + + last = &bpts; + for(b = bpts; b != nil; b = b->next){ + if(b->pc == pc && strcmp(b->path, path) == 0) { + *last = b->next; + free(b); + break; + } + last = &b->next; + } + return bpts; +} + +static void +freebpts(Bpt *b) +{ + Bpt *next; + + for(; b != nil; b = next){ + next = b->next; + free(b); + } +} + +static void +telldbg(Progctl *ctl, char *msg) +{ + kstrcpy(ctl->msg, msg, ERRMAX); + ctl->debugger = nil; + wakeup(&ctl->r); +} + +static void +dbgstart(Prog *p) +{ + Osenv *o; + Progctl *ctl; + + o = p->osenv; + ctl = o->debug; + if(ctl != nil && ctl->debugger != nil) + error("prog debugged"); + if(p->state == Pdebug) + addrun(p); + o->debug = nil; + p->xec = xec; +} + +static int +xecdone(void *vc) +{ + Progctl *ctl = vc; + + return ctl->debugger == nil; +} + +static void +dbgstep(Progctl *vctl, Prog *p, int n) +{ + Osenv *o; + Progctl *ctl; + char buf[ERRMAX+20], *msg; + + if(p == currun()) + error("cannot step yourself"); + + if(p->state == Pbroken) + error("prog broken"); + + ctl = vctl; + if(ctl->debugger != nil) + error("prog already debugged"); + + o = p->osenv; + if(o->debug == nil) { + o->debug = ctl; + p->xec = dbgxec; + }else if(o->debug != ctl) + error("prog already debugged"); + + ctl->step = n; + if(p->state == Pdebug) + addrun(p); + ctl->debugger = up; + strcpy(buf, "child: "); + msg = buf+7; + ctl->msg = msg; + + /* + * wait for reply from dbgxec; release is okay because prog is now + * debugged, cannot exit. + */ + if(waserror()){ + acquire(); + ctl->debugger = nil; + ctl->msg = nil; + o->debug = nil; + p->xec = xec; + nexterror(); + } + release(); + sleep(&ctl->r, xecdone, ctl); + poperror(); + acquire(); + if(msg[0] != '\0') + error(buf); +} + +void +dbgexit(Prog *kid, int broken, char *estr) +{ + int n; + Osenv *e; + Progctl *ctl; + char buf[ERRMAX+20]; + + e = kid->osenv; + ctl = e->debug; + e->debug = nil; + kid->xec = xec; + + if(broken) + n = snprint(buf, sizeof(buf), "broken: %s", estr); + else + n = snprint(buf, sizeof(buf), "exited"); + if(ctl->debugger) + telldbg(ctl, buf); + qproduce(ctl->q, buf, n); +} + +static void +dbgaddrun(Prog *p) +{ + Osenv *o; + + p->state = Pdebug; + p->addrun = nil; + o = p->osenv; + if(o->rend != nil) + wakeup(o->rend); + o->rend = nil; +} + +static int +bdone(void *vp) +{ + Prog *p = vp; + + return p->addrun == nil; +} + +static void +dbgblock(Prog *p) +{ + Osenv *o; + Progctl *ctl; + + o = p->osenv; + ctl = o->debug; + qproduce(ctl->q, progstate[p->state], strlen(progstate[p->state])); + pushrun(p); + p->addrun = dbgaddrun; + o->rend = &up->sleep; + + /* + * bdone(p) is safe after release because p is being debugged, + * so cannot exit. + */ + if(waserror()){ + acquire(); + nexterror(); + } + release(); + if(o->rend != nil) + sleep(o->rend, bdone, p); + poperror(); + acquire(); + if(p->kill != nil) + error(p->kill); + ctl = o->debug; + if(ctl != nil) + qproduce(ctl->q, "run", 3); +} + +void +dbgxec(Prog *p) +{ + Bpt *b; + Prog *kid; + Osenv *env; + Progctl *ctl; + int op, pc, n; + char buf[ERRMAX+10]; + extern void (*dec[])(void); + + env = p->osenv; + ctl = env->debug; + ctl->ref++; + if(waserror()){ + closedbgctl(ctl, p); + nexterror(); + } + + R = p->R; + R.MP = R.M->MP; + + R.IC = p->quanta; + if((ulong)R.IC > ctl->step) + R.IC = ctl->step; + if(ctl->stop) + R.IC = 0; + + + buf[0] = '\0'; + + if(R.IC != 0 && R.M->compiled) { + comvec(); + if(p != currun()) + dbgblock(p); + goto save; + } + + while(R.IC != 0) { + if(0) + print("step: %lux: %s %4ld %D\n", + (ulong)p, R.M->m->name, R.PC-R.M->prog, R.PC); + + dec[R.PC->add](); + op = R.PC->op; + R.PC++; + optab[op](); + + /* + * check notification about new progs + */ + if(op == ISPAWN || op == IMSPAWN) { + /* pick up the kid from the end of the run queue */ + kid = delruntail(Pdebug); + n = snprint(buf, sizeof buf, "new %d", kid->pid); + qproduce(ctl->q, buf, n); + buf[0] = '\0'; + } + if(op == ILOAD) { + n = snprint(buf, sizeof buf, "load %s", string2c(*(String**)R.s)); + qproduce(ctl->q, buf, n); + buf[0] = '\0'; + } + + /* + * check for returns at big steps + */ + if(op == IRET) + R.IC = 1; + + /* + * check for blocked progs + */ + if(R.IC == 1 && p != currun()) + dbgblock(p); + if(ctl->stop) + R.IC = 1; + R.IC--; + if(ctl->bpts == nil) + continue; + pc = R.PC - R.M->prog; + for(b = ctl->bpts; b != nil; b = b->next) { + if(pc == b->pc && + (strcmp(R.M->m->path, b->path) == 0 || + strcmp(R.M->m->path, b->file) == 0)) { + strcpy(buf, "breakpoint"); + goto save; + } + } + } + +save: + if(ctl->stop) + strcpy(buf, "stopped"); + + p->R = R; + + if(env->debug == nil) { + poperror(); + return; + } + + if(p == currun()) + delrun(Pdebug); + + telldbg(env->debug, buf); + poperror(); + closedbgctl(env->debug, p); +} + +Dev progdevtab = { + 'p', + "prog", + + devreset, + devinit, + devshutdown, + progattach, + progwalk, + progstat, + progopen, + devcreate, + progclose, + progread, + devbread, + progwrite, + devbwrite, + devremove, + progwstat, +}; diff --git a/os/port/devroot.c b/os/port/devroot.c new file mode 100644 index 00000000..02fd8833 --- /dev/null +++ b/os/port/devroot.c @@ -0,0 +1,153 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +extern Rootdata rootdata[]; +extern Dirtab roottab[]; +extern int rootmaxq; + +static Chan* +rootattach(char *spec) +{ + int i; + ulong len; + Rootdata *r; + + if(*spec) + error(Ebadspec); + for (i = 0; i < rootmaxq; i++){ + r = &rootdata[i]; + if (r->sizep){ + len = *r->sizep; + r->size = len; + roottab[i].length = len; + } + } + return devattach('/', spec); +} + +static int +rootgen(Chan *c, char *name, Dirtab *tab, int nd, int s, Dir *dp) +{ + int p, i; + Rootdata *r; + + if(s == DEVDOTDOT){ + p = rootdata[c->qid.path].dotdot; + c->qid.path = p; + c->qid.type = QTDIR; + name = "#/"; + if(p != 0){ + for(i = 0; i < rootmaxq; i++) + if(roottab[i].qid.path == c->qid.path){ + name = roottab[i].name; + break; + } + } + devdir(c, c->qid, name, 0, eve, 0555, dp); + return 1; + } + if(name != nil){ + isdir(c); + r = &rootdata[(int)c->qid.path]; + tab = r->ptr; + for(i=0; i<r->size; i++, tab++) + if(strcmp(tab->name, name) == 0){ + devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp); + return 1; + } + return -1; + } + if(s >= nd) + return -1; + tab += s; + devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp); + return 1; +} + +static Walkqid* +rootwalk(Chan *c, Chan *nc, char **name, int nname) +{ + ulong p; + + p = c->qid.path; + if(nname == 0) + p = rootdata[p].dotdot; + return devwalk(c, nc, name, nname, rootdata[p].ptr, rootdata[p].size, rootgen); +} + +static int +rootstat(Chan *c, uchar *dp, int n) +{ + int p; + + p = rootdata[c->qid.path].dotdot; + return devstat(c, dp, n, rootdata[p].ptr, rootdata[p].size, rootgen); +} + +static Chan* +rootopen(Chan *c, int omode) +{ + int p; + + p = rootdata[c->qid.path].dotdot; + return devopen(c, omode, rootdata[p].ptr, rootdata[p].size, rootgen); +} + +/* + * sysremove() knows this is a nop + */ +static void +rootclose(Chan*) +{ +} + +static long +rootread(Chan *c, void *buf, long n, vlong offset) +{ + ulong p, len; + uchar *data; + + p = c->qid.path; + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, rootdata[p].ptr, rootdata[p].size, rootgen); + len = rootdata[p].size; + if(offset < 0 || offset >= len) + return 0; + if(offset+n > len) + n = len - offset; + data = rootdata[p].ptr; + memmove(buf, data+offset, n); + return n; +} + +static long +rootwrite(Chan*, void*, long, vlong) +{ + error(Eperm); + return 0; +} + +Dev rootdevtab = { + '/', + "root", + + devreset, + devinit, + devshutdown, + rootattach, + rootwalk, + rootstat, + rootopen, + devcreate, + rootclose, + rootread, + devbread, + rootwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devsd.c b/os/port/devsd.c new file mode 100644 index 00000000..5bdc4048 --- /dev/null +++ b/os/port/devsd.c @@ -0,0 +1,1474 @@ +/* + * Storage Device. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" + +#include "../port/sd.h" + +extern Dev sddevtab; +extern SDifc* sdifc[]; + +typedef struct SDevgrp { + SDev* dev; + int nunits; /* num units in dev */ +} SDevgrp; + +static SDevgrp* devs; /* all devices */ +static QLock devslock; /* insertion and removal of devices */ +static int ndevs; /* total number of devices in the system */ + +enum { + Rawcmd, + Rawdata, + Rawstatus, +}; + +enum { + Qtopdir = 1, /* top level directory */ + Qtopbase, + Qtopctl = Qtopbase, + Qtopstat, + + Qunitdir, /* directory per unit */ + Qunitbase, + Qctl = Qunitbase, + Qraw, + Qpart, + + TypeLOG = 4, + NType = (1<<TypeLOG), + TypeMASK = (NType-1), + TypeSHIFT = 0, + + PartLOG = 8, + NPart = (1<<PartLOG), + PartMASK = (NPart-1), + PartSHIFT = TypeLOG, + + UnitLOG = 8, + NUnit = (1<<UnitLOG), + UnitMASK = (NUnit-1), + UnitSHIFT = (PartLOG+TypeLOG), + + DevLOG = 8, + NDev = (1 << DevLOG), + DevMASK = (NDev-1), + DevSHIFT = (UnitLOG+PartLOG+TypeLOG), + + Ncmd = 20, +}; + +#define TYPE(q) ((((ulong)(q).path)>>TypeSHIFT) & TypeMASK) +#define PART(q) ((((ulong)(q).path)>>PartSHIFT) & PartMASK) +#define UNIT(q) ((((ulong)(q).path)>>UnitSHIFT) & UnitMASK) +#define DEV(q) ((((ulong)(q).path)>>DevSHIFT) & DevMASK) +#define QID(d,u, p, t) (((d)<<DevSHIFT)|((u)<<UnitSHIFT)|\ + ((p)<<PartSHIFT)|((t)<<TypeSHIFT)) + + +static void +sdaddpart(SDunit* unit, char* name, ulong start, ulong end) +{ + SDpart *pp; + int i, partno; + + /* + * Check name not already used + * and look for a free slot. + */ + if(unit->part != nil){ + partno = -1; + for(i = 0; i < unit->npart; i++){ + pp = &unit->part[i]; + if(!pp->valid){ + if(partno == -1) + partno = i; + break; + } + if(strcmp(name, pp->name) == 0){ + if(pp->start == start && pp->end == end) + return; + error(Ebadctl); + } + } + } + else{ + if((unit->part = malloc(sizeof(SDpart)*SDnpart)) == nil) + error(Enomem); + unit->npart = SDnpart; + partno = 0; + } + + /* + * If no free slot found then increase the + * array size (can't get here with unit->part == nil). + */ + if(partno == -1){ + if(unit->npart >= NPart) + error(Enomem); + if((pp = malloc(sizeof(SDpart)*(unit->npart+SDnpart))) == nil) + error(Enomem); + memmove(pp, unit->part, sizeof(SDpart)*unit->npart); + free(unit->part); + unit->part = pp; + partno = unit->npart; + unit->npart += SDnpart; + } + + /* + * Check size and extent are valid. + */ + if(start > end || end > unit->sectors) + error(Eio); + pp = &unit->part[partno]; + pp->start = start; + pp->end = end; + kstrdup(&pp->name, name); + kstrdup(&pp->user, eve); + pp->perm = 0640; + pp->valid = 1; +} + +static void +sddelpart(SDunit* unit, char* name) +{ + int i; + SDpart *pp; + + /* + * Look for the partition to delete. + * Can't delete if someone still has it open. + */ + pp = unit->part; + for(i = 0; i < unit->npart; i++){ + if(strcmp(name, pp->name) == 0) + break; + pp++; + } + if(i >= unit->npart) + error(Ebadctl); + if(strcmp(up->env->user, pp->user) && !iseve()) + error(Eperm); + pp->valid = 0; + pp->vers++; +} + +static int +sdinitpart(SDunit* unit) +{ + int i, nf; + ulong start, end; + char *f[4], *p, *q, buf[10]; + + unit->vers++; + unit->sectors = unit->secsize = 0; + if(unit->part){ + for(i = 0; i < unit->npart; i++){ + unit->part[i].valid = 0; + unit->part[i].vers++; + } + } + + if(unit->inquiry[0] & 0xC0) + return 0; + switch(unit->inquiry[0] & 0x1F){ + case 0x00: /* DA */ + case 0x04: /* WORM */ + case 0x05: /* CD-ROM */ + case 0x07: /* MO */ + break; + default: + return 0; + } + + if(unit->dev->ifc->online) + unit->dev->ifc->online(unit); + if(unit->sectors){ + sdaddpart(unit, "data", 0, unit->sectors); + + /* + * Use partitions passed from boot program, + * e.g. + * sdC0part=dos 63 123123/plan9 123123 456456 + * This happens before /boot sets hostname so the + * partitions will have the null-string for user. + * The gen functions patch it up. + */ + snprint(buf, sizeof buf, "%spart", unit->name); + for(p = getconf(buf); p != nil; p = q){ + if(q = strchr(p, '/')) + *q++ = '\0'; + nf = tokenize(p, f, nelem(f)); + if(nf < 3) + continue; + + start = strtoul(f[1], 0, 0); + end = strtoul(f[2], 0, 0); + if(!waserror()){ + sdaddpart(unit, f[0], start, end); + poperror(); + } + } + } + + return 1; +} + +static SDev* +sdgetdev(int idno) +{ + SDev *sdev; + int i; + + qlock(&devslock); + for(i = 0; i != ndevs; i++) + if(devs[i].dev->idno == idno) + break; + + if(i == ndevs) + sdev = nil; + else{ + sdev = devs[i].dev; + incref(&sdev->r); + } + qunlock(&devslock); + return sdev; +} + +static SDunit* +sdgetunit(SDev* sdev, int subno) +{ + SDunit *unit; + char buf[32]; + + /* + * Associate a unit with a given device and sub-unit + * number on that device. + * The device will be probed if it has not already been + * successfully accessed. + */ + qlock(&sdev->unitlock); + if(subno > sdev->nunit){ + qunlock(&sdev->unitlock); + return nil; + } + + unit = sdev->unit[subno]; + if(unit == nil){ + /* + * Probe the unit only once. This decision + * may be a little severe and reviewed later. + */ + if(sdev->unitflg[subno]){ + qunlock(&sdev->unitlock); + return nil; + } + if((unit = malloc(sizeof(SDunit))) == nil){ + qunlock(&sdev->unitlock); + return nil; + } + sdev->unitflg[subno] = 1; + + snprint(buf, sizeof(buf), "%s%d", sdev->name, subno); + kstrdup(&unit->name, buf); + kstrdup(&unit->user, eve); + unit->perm = 0555; + unit->subno = subno; + unit->dev = sdev; + + if(sdev->enabled == 0 && sdev->ifc->enable) + sdev->ifc->enable(sdev); + sdev->enabled = 1; + + /* + * No need to lock anything here as this is only + * called before the unit is made available in the + * sdunit[] array. + */ + if(unit->dev->ifc->verify(unit) == 0){ + qunlock(&sdev->unitlock); + free(unit); + return nil; + } + sdev->unit[subno] = unit; + } + qunlock(&sdev->unitlock); + return unit; +} + +static void +sdreset(void) +{ + int i; + SDev *sdev, *tail, *sdlist; + + /* + * Probe all configured controllers and make a list + * of devices found, accumulating a possible maximum number + * of units attached and marking each device with an index + * into the linear top-level directory array of units. + */ + tail = sdlist = nil; + for(i = 0; sdifc[i] != nil; i++){ + if(sdifc[i]->pnp == nil || (sdev = sdifc[i]->pnp()) == nil) + continue; + if(sdlist != nil) + tail->next = sdev; + else + sdlist = sdev; + for(tail = sdev; tail->next != nil; tail = tail->next){ + tail->unit = (SDunit**)malloc(tail->nunit * sizeof(SDunit*)); + tail->unitflg = (int*)malloc(tail->nunit * sizeof(int)); + assert(tail->unit && tail->unitflg); + ndevs++; + } + tail->unit = (SDunit**)malloc(tail->nunit * sizeof(SDunit*)); + tail->unitflg = (int*)malloc(tail->nunit * sizeof(int)); + ndevs++; + } + + /* + * Legacy and option code goes here. This will be hard... + */ + + /* + * The maximum number of possible units is known, allocate + * placeholders for their datastructures; the units will be + * probed and structures allocated when attached. + * Allocate controller names for the different types. + */ + if(ndevs == 0) + return; + for(i = 0; sdifc[i] != nil; i++){ + /* + * BUG: no check is made here or later when a + * unit is attached that the id and name are set. + */ + if(sdifc[i]->id) + sdifc[i]->id(sdlist); + } + + /* + * The IDs have been set, unlink the sdlist and copy the spec to + * the devtab. + */ + devs = (SDevgrp*)malloc(ndevs * sizeof(SDevgrp)); + memset(devs, 0, ndevs * sizeof(SDevgrp)); + i = 0; + while(sdlist != nil){ + devs[i].dev = sdlist; + devs[i].nunits = sdlist->nunit; + sdlist = sdlist->next; + devs[i].dev->next = nil; + i++; + } +} + +static int +sd2gen(Chan* c, int i, Dir* dp) +{ + Qid q; + vlong l; + SDpart *pp; + SDperm *perm; + SDunit *unit; + SDev *sdev; + int rv; + + sdev = sdgetdev(DEV(c->qid)); + assert(sdev); + unit = sdev->unit[UNIT(c->qid)]; + + rv = -1; + switch(i){ + case Qctl: + mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qctl), + unit->vers, QTFILE); + perm = &unit->ctlperm; + if(emptystr(perm->user)){ + kstrdup(&perm->user, eve); + perm->perm = 0640; + } + devdir(c, q, "ctl", 0, perm->user, perm->perm, dp); + rv = 1; + break; + + case Qraw: + mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qraw), + unit->vers, QTFILE); + perm = &unit->rawperm; + if(emptystr(perm->user)){ + kstrdup(&perm->user, eve); + perm->perm = DMEXCL|0600; + } + devdir(c, q, "raw", 0, perm->user, perm->perm, dp); + rv = 1; + break; + + case Qpart: + pp = &unit->part[PART(c->qid)]; + l = (pp->end - pp->start) * (vlong)unit->secsize; + mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qpart), + unit->vers+pp->vers, QTFILE); + if(emptystr(pp->user)) + kstrdup(&pp->user, eve); + devdir(c, q, pp->name, l, pp->user, pp->perm, dp); + rv = 1; + break; + } + + decref(&sdev->r); + return rv; +} + +static int +sd1gen(Chan* c, int i, Dir* dp) +{ + Qid q; + + switch(i){ + case Qtopctl: + mkqid(&q, QID(0, 0, 0, Qtopctl), 0, QTFILE); + devdir(c, q, "sdctl", 0, eve, 0640, dp); + return 1; + case Qtopstat: + mkqid(&q, QID(0, 0, 0, Qtopstat), 0, QTFILE); + devdir(c, q, "sdstat", 0, eve, 0640, dp); + return 1; + } + return -1; +} + +static int +sdgen(Chan* c, char*, Dirtab*, int, int s, Dir* dp) +{ + Qid q; + vlong l; + int i, r; + SDpart *pp; + SDunit *unit; + SDev *sdev; + + switch(TYPE(c->qid)){ + case Qtopdir: + if(s == DEVDOTDOT){ + mkqid(&q, QID(0, 0, 0, Qtopdir), 0, QTDIR); + sprint(up->genbuf, "#%C", sddevtab.dc); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + + if(s == 0 || s == 1) + return sd1gen(c, s + Qtopbase, dp); + s -= 2; + + qlock(&devslock); + for(i = 0; i != ndevs; i++){ + if(s < devs[i].nunits) + break; + s -= devs[i].nunits; + } + + if(i == ndevs){ + /* Run of the end of the list */ + qunlock(&devslock); + return -1; + } + + if((sdev = devs[i].dev) == nil){ + qunlock(&devslock); + return 0; + } + + incref(&sdev->r); + qunlock(&devslock); + + if((unit = sdev->unit[s]) == nil) + if((unit = sdgetunit(sdev, s)) == nil){ + decref(&sdev->r); + return 0; + } + + mkqid(&q, QID(sdev->idno, s, 0, Qunitdir), 0, QTDIR); + if(emptystr(unit->user)) + kstrdup(&unit->user, eve); + devdir(c, q, unit->name, 0, unit->user, unit->perm, dp); + decref(&sdev->r); + return 1; + + case Qunitdir: + if(s == DEVDOTDOT){ + mkqid(&q, QID(0, 0, 0, Qtopdir), 0, QTDIR); + sprint(up->genbuf, "#%C", sddevtab.dc); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + + if((sdev = sdgetdev(DEV(c->qid))) == nil){ + devdir(c, q, "unavailable", 0, eve, 0, dp); + return 1; + } + + unit = sdev->unit[UNIT(c->qid)]; + qlock(&unit->ctl); + + /* + * Check for media change. + * If one has already been detected, sectors will be zero. + * If there is one waiting to be detected, online + * will return > 1. + * Online is a bit of a large hammer but does the job. + */ + if(unit->sectors == 0 + || (unit->dev->ifc->online && unit->dev->ifc->online(unit) > 1)) + sdinitpart(unit); + + i = s+Qunitbase; + if(i < Qpart){ + r = sd2gen(c, i, dp); + qunlock(&unit->ctl); + decref(&sdev->r); + return r; + } + i -= Qpart; + if(unit->part == nil || i >= unit->npart){ + qunlock(&unit->ctl); + decref(&sdev->r); + break; + } + pp = &unit->part[i]; + if(!pp->valid){ + qunlock(&unit->ctl); + decref(&sdev->r); + return 0; + } + l = (pp->end - pp->start) * (vlong)unit->secsize; + mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), i, Qpart), + unit->vers+pp->vers, QTFILE); + if(emptystr(pp->user)) + kstrdup(&pp->user, eve); + devdir(c, q, pp->name, l, pp->user, pp->perm, dp); + qunlock(&unit->ctl); + decref(&sdev->r); + return 1; + case Qraw: + case Qctl: + case Qpart: + if((sdev = sdgetdev(DEV(c->qid))) == nil){ + devdir(c, q, "unavailable", 0, eve, 0, dp); + return 1; + } + unit = sdev->unit[UNIT(c->qid)]; + qlock(&unit->ctl); + r = sd2gen(c, TYPE(c->qid), dp); + qunlock(&unit->ctl); + decref(&sdev->r); + return r; + case Qtopctl: + case Qtopstat: + return sd1gen(c, TYPE(c->qid), dp); + default: + break; + } + + return -1; +} + +static Chan* +sdattach(char* spec) +{ + Chan *c; + char *p; + SDev *sdev; + int idno, subno, i; + + if(ndevs == 0 || *spec == '\0'){ + c = devattach(sddevtab.dc, spec); + mkqid(&c->qid, QID(0, 0, 0, Qtopdir), 0, QTDIR); + return c; + } + + if(spec[0] != 's' || spec[1] != 'd') + error(Ebadspec); + idno = spec[2]; + subno = strtol(&spec[3], &p, 0); + if(p == &spec[3]) + error(Ebadspec); + + qlock(&devslock); + for (sdev = nil, i = 0; i != ndevs; i++) + if((sdev = devs[i].dev) != nil && sdev->idno == idno) + break; + + if(i == ndevs || subno >= sdev->nunit || sdgetunit(sdev, subno) == nil){ + qunlock(&devslock); + error(Enonexist); + } + incref(&sdev->r); + qunlock(&devslock); + + c = devattach(sddevtab.dc, spec); + mkqid(&c->qid, QID(sdev->idno, subno, 0, Qunitdir), 0, QTDIR); + c->dev = (sdev->idno << UnitLOG) + subno; + decref(&sdev->r); + return c; +} + +static Walkqid* +sdwalk(Chan* c, Chan* nc, char** name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, sdgen); +} + +static int +sdstat(Chan* c, uchar* db, int n) +{ + return devstat(c, db, n, nil, 0, sdgen); +} + +static Chan* +sdopen(Chan* c, int omode) +{ + SDpart *pp; + SDunit *unit; + SDev *sdev; + uchar tp; + + c = devopen(c, omode, 0, 0, sdgen); + if((tp = TYPE(c->qid)) != Qctl && tp != Qraw && tp != Qpart) + return c; + + sdev = sdgetdev(DEV(c->qid)); + if(sdev == nil) + error(Enonexist); + unit = sdev->unit[UNIT(c->qid)]; + + switch(TYPE(c->qid)){ + case Qctl: + c->qid.vers = unit->vers; + break; + case Qraw: + c->qid.vers = unit->vers; + if(_tas(&unit->rawinuse) != 0){ + c->flag &= ~COPEN; + error(Einuse); + } + unit->state = Rawcmd; + break; + case Qpart: + qlock(&unit->ctl); + if(waserror()){ + qunlock(&unit->ctl); + c->flag &= ~COPEN; + nexterror(); + } + pp = &unit->part[PART(c->qid)]; + c->qid.vers = unit->vers+pp->vers; + qunlock(&unit->ctl); + poperror(); + break; + } + decref(&sdev->r); + return c; +} + +static void +sdclose(Chan* c) +{ + SDunit *unit; + SDev *sdev; + + if(c->qid.type & QTDIR) + return; + if(!(c->flag & COPEN)) + return; + + switch(TYPE(c->qid)){ + default: + break; + case Qraw: + sdev = sdgetdev(DEV(c->qid)); + if(sdev){ + unit = sdev->unit[UNIT(c->qid)]; + unit->rawinuse = 0; + decref(&sdev->r); + } + break; + } +} + +static long +sdbio(Chan* c, int write, char* a, long len, vlong off) +{ + int nchange; + long l; + uchar *b; + SDpart *pp; + SDunit *unit; + SDev *sdev; + ulong bno, max, nb, offset; + + sdev = sdgetdev(DEV(c->qid)); + if(sdev == nil) + error(Enonexist); + unit = sdev->unit[UNIT(c->qid)]; + if(unit == nil) + error(Enonexist); + + nchange = 0; + qlock(&unit->ctl); + while(waserror()){ + /* notification of media change; go around again */ + if(strcmp(up->env->errstr, Eio) == 0 && unit->sectors == 0 && nchange++ == 0){ + sdinitpart(unit); + continue; + } + + /* other errors; give up */ + qunlock(&unit->ctl); + decref(&sdev->r); + nexterror(); + } + pp = &unit->part[PART(c->qid)]; + if(unit->vers+pp->vers != c->qid.vers) + error(Eio); + + /* + * Check the request is within bounds. + * Removeable drives are locked throughout the I/O + * in case the media changes unexpectedly. + * Non-removeable drives are not locked during the I/O + * to allow the hardware to optimise if it can; this is + * a little fast and loose. + * It's assumed that non-removeable media parameters + * (sectors, secsize) can't change once the drive has + * been brought online. + */ + bno = (off/unit->secsize) + pp->start; + nb = ((off+len+unit->secsize-1)/unit->secsize) + pp->start - bno; + max = SDmaxio/unit->secsize; + if(nb > max) + nb = max; + if(bno+nb > pp->end) + nb = pp->end - bno; + if(bno >= pp->end || nb == 0){ + if(write) + error(Eio); + qunlock(&unit->ctl); + decref(&sdev->r); + poperror(); + return 0; + } + if(!(unit->inquiry[1] & 0x80)){ + qunlock(&unit->ctl); + poperror(); + } + + b = sdmalloc(nb*unit->secsize); + if(b == nil) + error(Enomem); + if(waserror()){ + sdfree(b); + if(!(unit->inquiry[1] & 0x80)) + decref(&sdev->r); /* gadverdamme! */ + nexterror(); + } + + offset = off%unit->secsize; + if(offset+len > nb*unit->secsize) + len = nb*unit->secsize - offset; + if(write){ + if(offset || (len%unit->secsize)){ + l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno); + if(l < 0) + error(Eio); + if(l < (nb*unit->secsize)){ + nb = l/unit->secsize; + l = nb*unit->secsize - offset; + if(len > l) + len = l; + } + } + memmove(b+offset, a, len); + l = unit->dev->ifc->bio(unit, 0, 1, b, nb, bno); + if(l < 0) + error(Eio); + if(l < offset) + len = 0; + else if(len > l - offset) + len = l - offset; + } + else{ + l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno); + if(l < 0) + error(Eio); + if(l < offset) + len = 0; + else if(len > l - offset) + len = l - offset; + memmove(a, b+offset, len); + } + sdfree(b); + poperror(); + + if(unit->inquiry[1] & 0x80){ + qunlock(&unit->ctl); + poperror(); + } + + decref(&sdev->r); + return len; +} + +static long +sdrio(SDreq* r, void* a, long n) +{ + void *data; + + if(n >= SDmaxio || n < 0) + error(Etoobig); + + data = nil; + if(n){ + if((data = sdmalloc(n)) == nil) + error(Enomem); + if(r->write) + memmove(data, a, n); + } + r->data = data; + r->dlen = n; + + if(waserror()){ + if(data != nil){ + sdfree(data); + r->data = nil; + } + nexterror(); + } + + if(r->unit->dev->ifc->rio(r) != SDok) + error(Eio); + + if(!r->write && r->rlen > 0) + memmove(a, data, r->rlen); + if(data != nil){ + sdfree(data); + r->data = nil; + } + poperror(); + + return r->rlen; +} + +static long +sdread(Chan *c, void *a, long n, vlong off) +{ + char *p, *e, *buf; + SDpart *pp; + SDunit *unit; + SDev *sdev; + ulong offset; + int i, l, status; + + offset = off; + switch(TYPE(c->qid)){ + default: + error(Eperm); + case Qtopstat: + p = buf = malloc(READSTR); + assert(p); + e = p + READSTR; + qlock(&devslock); + for(i = 0; i != ndevs; i++){ + SDev *sdev = devs[i].dev; + + if(sdev->ifc->stat) + p = sdev->ifc->stat(sdev, p, e); + else + p = seprint(e, "%s; no statistics available\n", sdev->name); + } + qunlock(&devslock); + n = readstr(off, a, n, buf); + free(buf); + return n; + + case Qtopdir: + case Qunitdir: + return devdirread(c, a, n, 0, 0, sdgen); + + case Qctl: + sdev = sdgetdev(DEV(c->qid)); + if(sdev == nil) + error(Enonexist); + + unit = sdev->unit[UNIT(c->qid)]; + p = malloc(READSTR); + l = snprint(p, READSTR, "inquiry %.48s\n", + (char*)unit->inquiry+8); + qlock(&unit->ctl); + /* + * If there's a device specific routine it must + * provide all information pertaining to night geometry + * and the garscadden trains. + */ + if(unit->dev->ifc->rctl) + l += unit->dev->ifc->rctl(unit, p+l, READSTR-l); + if(unit->sectors == 0) + sdinitpart(unit); + if(unit->sectors){ + if(unit->dev->ifc->rctl == nil) + l += snprint(p+l, READSTR-l, + "geometry %ld %ld\n", + unit->sectors, unit->secsize); + pp = unit->part; + for(i = 0; i < unit->npart; i++){ + if(pp->valid) + l += snprint(p+l, READSTR-l, + "part %s %lud %lud\n", + pp->name, pp->start, pp->end); + pp++; + } + } + qunlock(&unit->ctl); + decref(&sdev->r); + l = readstr(offset, a, n, p); + free(p); + return l; + + case Qraw: + sdev = sdgetdev(DEV(c->qid)); + if(sdev == nil) + error(Enonexist); + + unit = sdev->unit[UNIT(c->qid)]; + qlock(&unit->raw); + if(waserror()){ + qunlock(&unit->raw); + decref(&sdev->r); + nexterror(); + } + if(unit->state == Rawdata){ + unit->state = Rawstatus; + i = sdrio(unit->req, a, n); + } + else if(unit->state == Rawstatus){ + status = unit->req->status; + unit->state = Rawcmd; + free(unit->req); + unit->req = nil; + i = readnum(0, a, n, status, NUMSIZE); + } else + i = 0; + qunlock(&unit->raw); + decref(&sdev->r); + poperror(); + return i; + + case Qpart: + return sdbio(c, 0, a, n, off); + } + + return 0; +} + +typedef struct Confdata Confdata; +struct Confdata { + int on; + char* spec; + DevConf cf; +}; + +static void +parseswitch(Confdata* cd, char* option) +{ + if(!strcmp("on", option)) + cd->on = 1; + else if(!strcmp("off", option)) + cd->on = 0; + else + error(Ebadarg); +} + +static void +parsespec(Confdata* cd, char* option) +{ + if(strlen(option) > 1) + error(Ebadarg); + cd->spec = option; +} + +static Devport* +getnewport(DevConf* dc) +{ + Devport *p; + + p = (Devport *)malloc((dc->nports + 1) * sizeof(Devport)); + if(dc->nports > 0){ + memmove(p, dc->ports, dc->nports * sizeof(Devport)); + free(dc->ports); + } + dc->ports = p; + p = &dc->ports[dc->nports++]; + p->size = -1; + p->port = (ulong)-1; + return p; +} + +static void +parseport(Confdata* cd, char* option) +{ + char *e; + Devport *p; + + if(cd->cf.nports == 0 || cd->cf.ports[cd->cf.nports-1].port != (ulong)-1) + p = getnewport(&cd->cf); + else + p = &cd->cf.ports[cd->cf.nports-1]; + p->port = strtol(option, &e, 0); + if(e == nil || *e != '\0') + error(Ebadarg); +} + +static void +parsesize(Confdata* cd, char* option) +{ + char *e; + Devport *p; + + if(cd->cf.nports == 0 || cd->cf.ports[cd->cf.nports-1].size != -1) + p = getnewport(&cd->cf); + else + p = &cd->cf.ports[cd->cf.nports-1]; + p->size = (int)strtol(option, &e, 0); + if(e == nil || *e != '\0') + error(Ebadarg); +} + +static void +parseirq(Confdata* cd, char* option) +{ + char *e; + + cd->cf.intnum = strtoul(option, &e, 0); + if(e == nil || *e != '\0') + error(Ebadarg); +} + +static void +parsetype(Confdata* cd, char* option) +{ + cd->cf.type = option; +} + +static struct { + char *option; + void (*parse)(Confdata*, char*); +} options[] = { + { "switch", parseswitch, }, + { "spec", parsespec, }, + { "port", parseport, }, + { "size", parsesize, }, + { "irq", parseirq, }, + { "type", parsetype, }, +}; + +static long +sdwrite(Chan* c, void* a, long n, vlong off) +{ + Cmdbuf *cb; + SDreq *req; + SDunit *unit; + SDev *sdev; + ulong end, start; + + switch(TYPE(c->qid)){ + default: + error(Eperm); + case Qtopctl: { + Confdata cd; + char buf[256], *field[Ncmd]; + int nf, i, j; + + memset(&cd, 0, sizeof(Confdata)); + if(n > sizeof(buf)-1) n = sizeof(buf)-1; + memmove(buf, a, n); + buf[n] = '\0'; + + cd.on = -1; + cd.spec = '\0'; + memset(&cd.cf, 0, sizeof(DevConf)); + + nf = tokenize(buf, field, Ncmd); + for(i = 0; i < nf; i++){ + char *opt = field[i++]; + if(i >= nf) + error(Ebadarg); + for(j = 0; j != nelem(options); j++) + if(!strcmp(opt, options[j].option)) + break; + + if(j == nelem(options)) + error(Ebadarg); + options[j].parse(&cd, field[i]); + } + + if(cd.on < 0) + error(Ebadarg); + + if(cd.on){ + if(cd.spec == '\0' || cd.cf.nports == 0 || + cd.cf.intnum == 0 || cd.cf.type == nil) + error(Ebadarg); + } + else{ + if(cd.spec == '\0') + error(Ebadarg); + } + + if(sddevtab.config == nil) + error("No configuration function"); + sddevtab.config(cd.on, cd.spec, &cd.cf); + break; + } + case Qctl: + cb = parsecmd(a, n); + sdev = sdgetdev(DEV(c->qid)); + if(sdev == nil) + error(Enonexist); + unit = sdev->unit[UNIT(c->qid)]; + + qlock(&unit->ctl); + if(waserror()){ + qunlock(&unit->ctl); + decref(&sdev->r); + free(cb); + nexterror(); + } + if(unit->vers != c->qid.vers) + error(Eio); + + if(cb->nf < 1) + error(Ebadctl); + if(strcmp(cb->f[0], "part") == 0){ + if(cb->nf != 4) + error(Ebadctl); + if(unit->sectors == 0 && !sdinitpart(unit)) + error(Eio); + start = strtoul(cb->f[2], 0, 0); + end = strtoul(cb->f[3], 0, 0); + sdaddpart(unit, cb->f[1], start, end); + } + else if(strcmp(cb->f[0], "delpart") == 0){ + if(cb->nf != 2 || unit->part == nil) + error(Ebadctl); + sddelpart(unit, cb->f[1]); + } + else if(unit->dev->ifc->wctl) + unit->dev->ifc->wctl(unit, cb); + else + error(Ebadctl); + qunlock(&unit->ctl); + decref(&sdev->r); + poperror(); + free(cb); + break; + + case Qraw: + sdev = sdgetdev(DEV(c->qid)); + if(sdev == nil) + error(Enonexist); + unit = sdev->unit[UNIT(c->qid)]; + qlock(&unit->raw); + if(waserror()){ + qunlock(&unit->raw); + decref(&sdev->r); + nexterror(); + } + switch(unit->state){ + case Rawcmd: + if(n < 6 || n > sizeof(req->cmd)) + error(Ebadarg); + if((req = malloc(sizeof(SDreq))) == nil) + error(Enomem); + req->unit = unit; + memmove(req->cmd, a, n); + req->clen = n; + req->flags = SDnosense; + req->status = ~0; + + unit->req = req; + unit->state = Rawdata; + break; + + case Rawstatus: + unit->state = Rawcmd; + free(unit->req); + unit->req = nil; + error(Ebadusefd); + + case Rawdata: + if(unit->state != Rawdata) + error(Ebadusefd); + unit->state = Rawstatus; + + unit->req->write = 1; + n = sdrio(unit->req, a, n); + } + qunlock(&unit->raw); + decref(&sdev->r); + poperror(); + break; + case Qpart: + return sdbio(c, 1, a, n, off); + } + + return n; +} + +static int +sdwstat(Chan* c, uchar* dp, int n) +{ + Dir *d; + SDpart *pp; + SDperm *perm; + SDunit *unit; + SDev *sdev; + + if(c->qid.type & QTDIR) + error(Eperm); + + sdev = sdgetdev(DEV(c->qid)); + if(sdev == nil) + error(Enonexist); + unit = sdev->unit[UNIT(c->qid)]; + qlock(&unit->ctl); + d = nil; + if(waserror()){ + free(d); + qunlock(&unit->ctl); + decref(&sdev->r); + nexterror(); + } + + switch(TYPE(c->qid)){ + default: + error(Eperm); + case Qctl: + perm = &unit->ctlperm; + break; + case Qraw: + perm = &unit->rawperm; + break; + case Qpart: + pp = &unit->part[PART(c->qid)]; + if(unit->vers+pp->vers != c->qid.vers) + error(Enonexist); + perm = &pp->SDperm; + break; + } + + if(strcmp(up->env->user, perm->user) && !iseve()) + error(Eperm); + + d = smalloc(sizeof(Dir)+n); + n = convM2D(dp, n, &d[0], (char*)&d[1]); + if(n == 0) + error(Eshortstat); + if(!emptystr(d[0].uid)) + kstrdup(&perm->user, d[0].uid); + if(d[0].mode != ~0UL) + perm->perm = (perm->perm & ~0777) | (d[0].mode & 0777); + + free(d); + qunlock(&unit->ctl); + decref(&sdev->r); + poperror(); + return n; +} + +static char +getspec(char base) +{ + while(1){ + int i; + SDev *sdev; + + for(i = 0; i != ndevs; i++) + if((sdev = devs[i].dev) != nil && (char)sdev->idno == base) + break; + + if(i == ndevs) + return base; + base++; + } + return '\0'; +} + +static int +configure(char* spec, DevConf* cf) +{ + ISAConf isa; + SDevgrp *tmpdevs; + SDev *tail, *sdev, *(*probe)(DevConf*); + char *p, name[32]; + int i, nnew; + + if((p = strchr(cf->type, '/')) != nil) + *p++ = '\0'; + + for(i = 0; sdifc[i] != nil; i++) + if(!strcmp(sdifc[i]->name, cf->type)) + break; + + if(sdifc[i] == nil) + error("type not found"); + + if((probe = sdifc[i]->probe) == nil) + error("No probe function"); + + if(p){ + /* Try to find the card on the ISA bus. This code really belongs + in sdata and I'll move it later. Really! */ + memset(&isa, 0, sizeof(isa)); + isa.port = cf->ports[0].port; + isa.irq = cf->intnum; + + if(pcmspecial(p, &isa) < 0) + error("Cannot find controller"); + } + + qlock(&devslock); + if(waserror()){ + qunlock(&devslock); + nexterror(); + } + + for(i = 0; i != ndevs; i++) + if((sdev = devs[i].dev) != nil && sdev->idno == *spec) + break; + if(i != ndevs) + error(Eexist); + + if((sdev = (*probe)(cf)) == nil) + error("Cannot probe controller"); + poperror(); + + nnew = 0; + tail = sdev; + while(tail){ + nnew++; + tail = tail->next; + } + + tmpdevs = (SDevgrp*)malloc((ndevs + nnew) * sizeof(SDevgrp)); + memmove(tmpdevs, devs, ndevs * sizeof(SDevgrp)); + free(devs); + devs = tmpdevs; + + while(sdev){ + /* Assign `spec' to the device */ + *spec = getspec(*spec); + snprint(name, sizeof(name), "sd%c", *spec); + kstrdup(&sdev->name, name); + sdev->idno = *spec; + sdev->unit = (SDunit **)malloc(sdev->nunit * sizeof(SDunit*)); + sdev->unitflg = (int *)malloc(sdev->nunit * sizeof(int)); + assert(sdev->unit && sdev->unitflg); + + devs[ndevs].dev = sdev; + devs[ndevs].nunits = sdev->nunit; + sdev = sdev->next; + devs[ndevs].dev->next = nil; + ndevs++; + } + + qunlock(&devslock); + return 0; +} + +static int +unconfigure(char* spec) +{ + int i; + SDev *sdev; + + qlock(&devslock); + if(waserror()){ + qunlock(&devslock); + nexterror(); + } + + sdev = nil; + for(i = 0; i != ndevs; i++) + if((sdev = devs[i].dev) != nil && sdev->idno == *spec) + break; + + if(i == ndevs) + error(Enonexist); + + if(sdev->r.ref) + error(Einuse); + + /* make sure no interrupts arrive anymore before removing resources */ + if(sdev->enabled && sdev->ifc->disable) + sdev->ifc->disable(sdev); + + /* we're alone and the device tab is locked; make the device unavailable */ + memmove(&devs[i], &devs[ndevs - 1], sizeof(SDevgrp)); + memset(&devs[ndevs - 1], 0, sizeof(SDevgrp)); + ndevs--; + + qunlock(&devslock); + poperror(); + + for(i = 0; i != sdev->nunit; i++) + if(sdev->unit[i]){ + SDunit *unit = sdev->unit[i]; + + free(unit->name); + free(unit->user); + free(unit); + } + + if(sdev->ifc->clear) + sdev->ifc->clear(sdev); + return 0; +} + +static int +sdconfig(int on, char* spec, DevConf* cf) +{ + if(on) + return configure(spec, cf); + return unconfigure(spec); +} + +Dev sddevtab = { + 'S', + "sd", + + sdreset, + devinit, + devshutdown, + sdattach, + sdwalk, + sdstat, + sdopen, + devcreate, + sdclose, + sdread, + devbread, + sdwrite, + devbwrite, + devremove, + sdwstat, + devpower, + sdconfig, +}; diff --git a/os/port/devsign.c b/os/port/devsign.c new file mode 100644 index 00000000..4fe8edb3 --- /dev/null +++ b/os/port/devsign.c @@ -0,0 +1,446 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "interp.h" +#include <isa.h> +#include "runt.h" +#include "mp.h" +#include "libsec.h" +#include "../../libkeyring/keys.h" + +/* + * experimental version of signed modules + */ + +enum +{ + Qdir, + Qkey, + Qctl, + + Maxkey = 2048 +}; + +static Dirtab signdir[] = +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "signerkey", {Qkey}, 0, 0644, + "signerctl", {Qctl}, 0, 0600, +}; + +typedef struct Get Get; +struct Get { + uchar* p; + uchar* ep; +}; + +#define G32(b) ((b[0]<<24)|(b[1]<<16)|(b[2]<<8)|b[3]) + +static int vc(Get*); +static int vs(void*, int, Get*, int); +static Signerkey* findsignerkey(Skeyset*, char*, int, char*); +extern vlong osusectime(void); + +int +verifysigner(uchar *sign, int len, uchar *data, ulong ndata) +{ + Get sig; + int alg; + ulong issued, expires, now; + int footprint, r, n; + uchar buf[128], digest[SHA1dlen]; + DigestState *ds; + volatile struct {BigInt b;} b; + volatile struct {BigInt s;} s; + SigAlgVec *sa; + Signerkey *key; + Skeyset *sigs; + + /* alg[1] issued[4] expires[4] footprint[2] signer[n] sig[m] */ + sigs = up->env->sigs; + if(sigs == nil) + return 1; /* not enforcing signed modules */ + sig.p = sign; + sig.ep = sign+len; + alg = vc(&sig); + if(alg != 2) + return 0; /* we do only SHA1/RSA */ + sa = findsigalg("rsa"); + if(sa == nil) + return 0; + if(vs(buf, sizeof(buf), &sig, 4) < 0) + return 0; + now = osusectime()/1000000; + issued = G32(buf); + if(vs(buf, sizeof(buf), &sig, 4) < 0) + return 0; + if(issued != 0 && now < issued) + return 0; + expires = G32(buf); + if(expires != 0 && now >= expires) + return 0; + footprint = vc(&sig) << 8; + footprint |= vc(&sig); + if(footprint < 0) + return 0; + r = 0; + b.b = nil; + s.s = nil; + qlock(sigs); + if(waserror()) + goto out; + if((n = vs(buf, sizeof(buf)-NUMSIZE-1, &sig, -1)) < 0) /* owner */ + goto out; + buf[n] = 0; + key = findsignerkey(sigs, sa->name, footprint, (char*)buf); + if(key == nil) + goto out; + n += snprint((char*)buf+n, NUMSIZE, " %lud", expires); + ds = sha1(buf, n, nil, nil); + sha1(data, ndata, digest, ds); + b.b = betomp(digest, SHA1dlen, nil); + if(b.b == nil) + goto out; + s.s = betomp(sig.p, sig.ep-sig.p, nil); + if(s.s == nil) + goto out; + r = (*sa->verify)(b.b, s.s, key->pk); +out: + qunlock(sigs); + if(b.b != nil) + mpfree(b.b); + if(s.s != nil) + mpfree(s.s); + return r; +} + +int +mustbesigned(char *path, uchar*, ulong, Dir *dir) +{ + USED(path); +if(0)print("load %s: %d %C\n", path, up->env->sigs!=nil, dir==nil?'?':dir->type); + /* allow only signed modules and those in #/; already loaded modules are reloaded from cache */ + return up->env->sigs != nil && (dir == nil || dir->type != '/'); +} + +static int +vc(Get *g) +{ + return g->p < g->ep? *g->p++: -1; +} + +static int +vs(void *s, int lim, Get *g, int n) +{ + int nr; + + if(n < 0){ + if(g->p >= g->ep) + return -1; + n = *g->p++; + lim--; + } + if(n > lim) + return -1; + nr = g->ep - g->p; + if(n > nr) + return -1; + if(s != nil) + memmove(s, g->p, n); + g->p += n; + return n; +} + +static char* +cstring(char *str, char **strp) +{ + char *p, *s; + int n; + + p = strchr(str, '\n'); + if(p == 0) + p = str + strlen(str); + n = p - str; + s = malloc(n+1); + if(s == nil) + return nil; + memmove(s, str, n); + s[n] = 0; + + if(strp){ + if(*p) + p++; + *strp = p; + } + + return s; +} + +static SigAlgVec* +cstrtoalg(char *str, char **strp) +{ + int n; + char *p, name[KNAMELEN]; + + p = strchr(str, '\n'); + if(p == 0){ + p = str + strlen(str); + if(strp) + *strp = p; + } else { + if(strp) + *strp = p+1; + } + + n = p - str; + if(n >= sizeof(name)) + return nil; + strncpy(name, str, n); + name[n] = 0; + return findsigalg(name); +} + +static Signerkey* +strtopk(char *buf) +{ + SigAlgVec *sa; + char *p; + Signerkey *key; + + key = malloc(sizeof(*key)); + if(key == nil) + return nil; + key->ref = 1; + sa = cstrtoalg(buf, &p); + if(sa == nil){ + free(key); + return nil; + } + key->alg = sa; + key->pkfree = sa->pkfree; + key->owner = cstring(p, &p); + if(key->owner == nil){ + free(key); + return nil; + } + key->pk = (*sa->str2pk)(p, &p); + if(key->pk == nil){ + free(key->owner); + free(key); + return nil; + } + return key; +} + +static Signerkey* +findsignerkey(Skeyset *sigs, char *alg, int footprint, char *owner) +{ + int i; + Signerkey *key; + + for(i=0; i<sigs->nkey; i++){ + key = sigs->keys[i]; + if(key->footprint == footprint && + strcmp(alg, ((SigAlgVec*)key->alg)->name) == 0 && + strcmp(key->owner, owner) == 0) + return key; + } + return nil; +} + +static Chan* +signattach(char *spec) +{ + return devattach(L'Σ', spec); +} + +static Walkqid* +signwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, signdir, nelem(signdir), devgen); +} + +static int +signstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, signdir, nelem(signdir), devgen); +} + +static Chan* +signopen(Chan *c, int omode) +{ + if(c->qid.type & QTDIR) { + if(omode != OREAD) + error(Eisdir); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; + } + + switch((ulong)c->qid.path){ + case Qctl: + if(!iseve()) + error(Eperm); + break; + + case Qkey: + if(omode != OREAD && !iseve()) + error(Eperm); + break; + } + + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +signclose(Chan *c) +{ + USED(c); +} + +static long +signread(Chan *c, void *va, long n, vlong offset) +{ + char *buf, *p; + SigAlgVec *sa; + Skeyset *sigs; + Signerkey *key; + int i; + + if(c->qid.type & QTDIR) + return devdirread(c, va, n, signdir, nelem(signdir), devgen); + sigs = up->env->sigs; + if(sigs == nil) + return 0; + switch((ulong)c->qid.path){ + case Qkey: + buf = smalloc(Maxkey); + if(waserror()){ + free(buf); + nexterror(); + } + qlock(sigs); + if(waserror()){ + qunlock(sigs); + nexterror(); + } + p = buf; + for(i=0; i<sigs->nkey; i++){ + key = sigs->keys[i]; + sa = key->alg; + p = seprint(p, buf+Maxkey, "owner=%s alg=%s footprint=%ud expires=%lud\n", + key->owner, sa->name, key->footprint, key->expires); + } + poperror(); + qunlock(sigs); + n = readstr(offset, va, n, buf); + poperror(); + free(buf); + return n; + + case Qctl: + return readnum(offset, va, n, sigs->nkey, NUMSIZE); + } + return 0; +} + +static long +signwrite(Chan *c, void *va, long n, vlong offset) +{ + char *buf; + Skeyset *sigs; + Signerkey *okey, *key; + int i; + + if(c->qid.type & QTDIR) + error(Eisdir); + USED(offset); + switch((ulong)c->qid.path){ + case Qkey: + if(n >= Maxkey) + error(Etoobig); + buf = smalloc(Maxkey); + if(waserror()){ + free(buf); + nexterror(); + } + memmove(buf, va, n); + buf[n] = 0; + + key = strtopk(buf); + if(key == nil) + error("bad key syntax"); + poperror(); + free(buf); + + if(waserror()){ + freeskey(key); + nexterror(); + } + sigs = up->env->sigs; + if(sigs == nil){ + sigs = malloc(sizeof(*sigs)); + if(sigs == nil) + error(Enomem); + sigs->ref = 1; + up->env->sigs = sigs; + } + qlock(sigs); + if(waserror()){ + qunlock(sigs); + nexterror(); + } + for(i=0; i<sigs->nkey; i++){ + okey = sigs->keys[i]; + if(strcmp(okey->owner, key->owner) == 0){ + /* replace existing key */ + sigs->keys[i] = key; + freeskey(okey); + break; + } + } + if(i >= sigs->nkey){ + if(sigs->nkey >= nelem(sigs->keys)) + error("too many keys"); + sigs->keys[sigs->nkey++] = key; + } + poperror(); + qunlock(sigs); + poperror(); /* key */ + + return n; + case Qctl: + error(Ebadctl); + break; + } + return 0; +} + +Dev signdevtab = { + L'Σ', + "sign", + + devreset, + devinit, + devshutdown, + signattach, + signwalk, + signstat, + signopen, + devcreate, + signclose, + signread, + devbread, + signwrite, + devbwrite, + devremove, + devwstat +}; diff --git a/os/port/devsrv.c b/os/port/devsrv.c new file mode 100644 index 00000000..a80a3984 --- /dev/null +++ b/os/port/devsrv.c @@ -0,0 +1,726 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "interp.h" +#include "isa.h" +#include "runt.h" + +typedef struct SrvFile SrvFile; +struct SrvFile +{ + char* spec; + char* name; + char* user; + ulong perm; + vlong length; + Qid qid; + int ref; + int opens; + int flags; + Channel* read; + Channel* write; + SrvFile* entry; + SrvFile* dir; + SrvFile* devlist; +}; + +enum +{ + SORCLOSE = (1<<0), + SRDCLOSE = (1<<1), + SWRCLOSE = (1<<2), + SREMOVED = (1<<3), +}; + +typedef struct SrvDev SrvDev; +struct SrvDev +{ + Type* Rread; + Type* Rwrite; + QLock l; + ulong pathgen; + SrvFile* devices; +}; + +static SrvDev dev; + +void freechan(Heap*, int); +static void freerdchan(Heap*, int); +static void freewrchan(Heap*, int); + +Type *Trdchan; +Type *Twrchan; + +static int +srvgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp) +{ + SrvFile *f; + + if(s == DEVDOTDOT){ + devdir(c, c->qid, "#s", 0, eve, 0555, dp); + return 1; + } + f = c->aux; + if((c->qid.type & QTDIR) == 0){ + if(s > 0) + return -1; + devdir(c, f->qid, f->name, f->length, f->user, f->perm, dp); + return 1; + } + + for(f = f->entry; f != nil; f = f->entry){ + if(s-- == 0) + break; + } + if(f == nil) + return -1; + + devdir(c, f->qid, f->name, f->length, f->user, f->perm, dp); + return 1; +} + +static void +srvinit(void) +{ + static uchar rmap[] = Sys_Rread_map; + static uchar wmap[] = Sys_Rwrite_map; + + Trdchan = dtype(freerdchan, sizeof(Channel), Tchannel.map, Tchannel.np); + Twrchan = dtype(freewrchan, sizeof(Channel), Tchannel.map, Tchannel.np); + + dev.pathgen = 1; + dev.Rread = dtype(freeheap, Sys_Rread_size, rmap, sizeof(rmap)); + dev.Rwrite = dtype(freeheap, Sys_Rwrite_size, wmap, sizeof(wmap)); +} + +static int +srvchkattach(SrvFile *d) +{ + if(strcmp(d->user, up->env->user) == 0) + return 1; + + /* + * Need write permission in other to allow attaches if + * we are not the owner + */ + if(d->perm & 2) + return 1; + + return 0; +} + +static Chan* +srvattach(char *spec) +{ + Chan *c; + SrvFile *d; + + if(spec[0] != '\0'){ + qlock(&dev.l); + for(d = dev.devices; d != nil; d = d->devlist){ + if(strcmp(spec, d->spec) == 0){ + if(srvchkattach(d) == 0){ + qunlock(&dev.l); + error(Eperm); + } + d->ref++; + break; + } + } + qunlock(&dev.l); + + if(d != nil){ + c = devattach('s', spec); + c->aux = d; + c->qid = d->qid; + return c; + } + } + + d = malloc(sizeof(SrvFile)); + if(d == nil) + error(Enomem); + + c = devattach('s', spec); + + d->ref = 1; + kstrdup(&d->spec, spec); + kstrdup(&d->user, up->env->user); + snprint(up->genbuf, sizeof(up->genbuf), "srv%ld", up->env->pgrp->pgrpid); + kstrdup(&d->name, up->genbuf); + d->perm = DMDIR|0770; + + qlock(&dev.l); + mkqid(&d->qid, dev.pathgen++, 0, QTDIR); + d->devlist = dev.devices; + dev.devices = d; + qunlock(&dev.l); + + c->aux = d; + c->qid = d->qid; + + return c; +} + +static Walkqid* +srvwalk(Chan *c, Chan *nc, char **name, int nname) +{ + SrvFile *d, *pd; + Walkqid *w; + + pd = c->aux; + qlock(&dev.l); + if(waserror()){ + qunlock(&dev.l); + nexterror(); + } + + w = devwalk(c, nc, name, nname, nil, 0, srvgen); + if(w != nil && w->clone != nil){ + if(nname != 0){ + for(d = pd->entry; d != nil; d = d->entry) + if(d->qid.path == w->clone->qid.path) + break; + if(d == nil) + panic("srvwalk"); + if(w->clone == c) + pd->ref--; + }else + d = pd; + w->clone->aux = d; + d->ref++; + } + poperror(); + qunlock(&dev.l); + return w; +} + +static int +srvstat(Chan *c, uchar *db, int n) +{ + qlock(&dev.l); + if(waserror()){ + qunlock(&dev.l); + nexterror(); + } + n = devstat(c, db, n, 0, 0, srvgen); + poperror(); + qunlock(&dev.l); + return n; +} + +static Chan* +srvopen(Chan *c, int omode) +{ + SrvFile *sf; + + openmode(omode); /* check it */ + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Eisdir); + c->mode = omode; + c->flag |= COPEN; + c->offset = 0; + return c; + } + + sf = c->aux; + + qlock(&dev.l); + if(waserror()){ + qunlock(&dev.l); + nexterror(); + } + devpermcheck(sf->user, sf->perm, omode); + if(omode&ORCLOSE && strcmp(sf->user, up->env->user) != 0) + error(Eperm); + if(sf->perm & DMEXCL && sf->opens != 0) + error(Einuse); + sf->opens++; + if(omode&ORCLOSE) + sf->flags |= SORCLOSE; + poperror(); + qunlock(&dev.l); + + c->offset = 0; + c->flag |= COPEN; + c->mode = openmode(omode); + + return c; +} + +static int +srvwstat(Chan *c, uchar *dp, int n) +{ + Dir *d; + SrvFile *sf, *f; + + sf = c->aux; + if(strcmp(up->env->user, sf->user) != 0) + error(Eperm); + + d = smalloc(sizeof(*d)+n); + if(waserror()){ + free(d); + nexterror(); + } + n = convM2D(dp, n, d, (char*)&d[1]); + if(n == 0) + error(Eshortstat); + if(!emptystr(d->name)){ + if(sf->dir == nil) + error(Eperm); + validwstatname(d->name); + qlock(&dev.l); + for(f = sf->dir; f != nil; f = f->entry) + if(strcmp(f->name, d->name) == 0){ + qunlock(&dev.l); + error(Eexist); + } + kstrdup(&sf->name, d->name); + qunlock(&dev.l); + } + if(d->mode != ~0UL) + sf->perm = d->mode & (DMEXCL|DMAPPEND|0777); + if(d->length != (vlong)-1) + sf->length = d->length; + poperror(); + free(d); + return n; +} + +static void +srvputdir(SrvFile *sf) +{ + SrvFile **l, *d; + + sf->ref--; + if(sf->ref != 0) + return; + + for(l = &dev.devices; (d = *l) != nil; l = &d->devlist) + if(d == sf){ + *l = d->devlist; + break; + } + free(sf->spec); + free(sf->user); + free(sf->name); + free(sf); +} + +static void +srvunblock(SrvFile *sf, int fid) +{ + Channel *d; + Sys_FileIO_read rreq; + Sys_FileIO_write wreq; + + acquire(); + if(waserror()){ + release(); + nexterror(); + } + d = sf->read; + if(d != H){ + rreq.t0 = 0; + rreq.t1 = 0; + rreq.t2 = fid; + rreq.t3 = H; + csendalt(d, &rreq, d->mid.t, -1); + } + + d = sf->write; + if(d != H){ + wreq.t0 = 0; + wreq.t1 = H; + wreq.t2 = fid; + wreq.t3 = H; + csendalt(d, &wreq, d->mid.t, -1); + } + poperror(); + release(); +} + +static void +srvdecr(SrvFile *sf, int remove) +{ + SrvFile *f, **l; + + if(remove){ + l = &sf->dir->entry; + for(f = *l; f != nil; f = f->entry){ + if(sf == f){ + *l = f->entry; + break; + } + l = &f->entry; + } + sf->ref--; + sf->flags |= SREMOVED; + } + + if(sf->ref != 0) + return; + + if(sf->dir != nil) + srvputdir(sf->dir); + + free(sf->spec); + free(sf->user); + free(sf->name); + free(sf); +} + +static void +srvfree(SrvFile *sf, int flag) +{ + sf->flags |= flag; + if((sf->flags & (SRDCLOSE | SWRCLOSE)) == (SRDCLOSE | SWRCLOSE)){ + sf->ref--; + srvdecr(sf, (sf->flags & SREMOVED) == 0); + } +} + +static void +freerdchan(Heap *h, int swept) +{ + SrvFile *sf; + + release(); + qlock(&dev.l); + sf = H2D(Channel*, h)->aux; + sf->read = H; + srvfree(sf, SRDCLOSE); + qunlock(&dev.l); + acquire(); + freechan(h, swept); +} + +static void +freewrchan(Heap *h, int swept) +{ + SrvFile *sf; + + release(); + qlock(&dev.l); + sf = H2D(Channel*, h)->aux; + sf->write = H; + srvfree(sf, SWRCLOSE); + qunlock(&dev.l); + acquire(); + freechan(h, swept); +} + +static void +srvclunk(Chan *c, int remove) +{ + int opens, noperm; + SrvFile *sf; + + sf = c->aux; + qlock(&dev.l); + if(c->qid.type & QTDIR){ + srvputdir(sf); + qunlock(&dev.l); + if(remove) + error(Eperm); + return; + } + + opens = 0; + if(c->flag & COPEN){ + opens = sf->opens--; + if (sf->read != H || sf->write != H) + srvunblock(sf, c->fid); + } + + sf->ref--; + if(opens == 1){ + if((sf->flags & (SORCLOSE | SREMOVED)) == SORCLOSE) + remove = 1; + } + + noperm = 0; + if(remove && strcmp(sf->dir->user, up->env->user) != 0){ + noperm = 1; + remove = 0; + } + + srvdecr(sf, remove); + qunlock(&dev.l); + + if(noperm) + error(Eperm); +} + +static void +srvclose(Chan *c) +{ + srvclunk(c, 0); +} + +static void +srvremove(Chan *c) +{ + srvclunk(c, 1); +} + +static long +srvread(Chan *c, void *va, long count, vlong offset) +{ + int l; + Heap * volatile h; + Array *a; + SrvFile *sp; + Channel *rc; + Channel *rd; + Sys_Rread * volatile r; + Sys_FileIO_read req; + + if(c->qid.type & QTDIR){ + qlock(&dev.l); + if(waserror()){ + qunlock(&dev.l); + nexterror(); + } + l = devdirread(c, va, count, 0, 0, srvgen); + poperror(); + qunlock(&dev.l); + return l; + } + + sp = c->aux; + + acquire(); + if(waserror()){ + release(); + nexterror(); + } + + rd = sp->read; + if(rd == H) + error(Eshutdown); + + rc = cnewc(dev.Rread, movtmp, 1); + ptradd(D2H(rc)); + if(waserror()){ + ptrdel(D2H(rc)); + destroy(rc); + nexterror(); + } + + req.t0 = offset; + req.t1 = count; + req.t2 = c->fid; + req.t3 = rc; + csend(rd, &req); + + h = heap(dev.Rread); + r = H2D(Sys_Rread *, h); + ptradd(h); + if(waserror()){ + ptrdel(h); + destroy(r); + nexterror(); + } + + crecv(rc, r); + if(r->t1 != H) + error(string2c(r->t1)); + + a = r->t0; + l = 0; + if(a != H){ + l = a->len; + if(l > count) + l = count; + memmove(va, a->data, l); + } + + poperror(); + ptrdel(h); + destroy(r); + + poperror(); + ptrdel(D2H(rc)); + destroy(rc); + + poperror(); + release(); + + return l; +} + +static long +srvwrite(Chan *c, void *va, long count, vlong offset) +{ + long l; + Heap * volatile h; + SrvFile *sp; + Channel *wc; + Channel *wr; + Sys_Rwrite * volatile w; + Sys_FileIO_write req; + + if(c->qid.type & QTDIR) + error(Eperm); + + acquire(); + if(waserror()){ + release(); + nexterror(); + } + + sp = c->aux; + wr = sp->write; + if(wr == H) + error(Eshutdown); + + wc = cnewc(dev.Rwrite, movtmp, 1); + ptradd(D2H(wc)); + if(waserror()){ + ptrdel(D2H(wc)); + destroy(wc); + nexterror(); + } + + req.t0 = offset; + req.t1 = mem2array(va, count); + req.t2 = c->fid; + req.t3 = wc; + + ptradd(D2H(req.t1)); + if(waserror()){ + ptrdel(D2H(req.t1)); + destroy(req.t1); + nexterror(); + } + + csend(wr, &req); + + poperror(); + ptrdel(D2H(req.t1)); + destroy(req.t1); + + poperror(); + ptrdel(D2H(wc)); + destroy(wc); + + h = heap(dev.Rwrite); + w = H2D(Sys_Rwrite *, h); + ptradd(h); + if(waserror()){ + ptrdel(h); + destroy(w); + nexterror(); + } + crecv(wc, w); + if(w->t1 != H) + error(string2c(w->t1)); + poperror(); + ptrdel(h); + l = w->t0; + destroy(w); + + poperror(); + release(); + if(l < 0) + l = 0; + return l; +} + +static void +srvretype(Channel *c, SrvFile *f, Type *t) +{ + Heap *h; + + h = D2H(c); + h->t->ref--; + h->t = t; + t->ref++; + c->aux = f; +} + +int +srvf2c(char *dir, char *file, Sys_FileIO *io) +{ + SrvFile *s, *f; + volatile struct { Chan *c; } c; + + c.c = nil; + if(waserror()){ + cclose(c.c); + return -1; + } + + if(strchr(file, '/') != nil || strlen(file) >= 64 || strcmp(file, ".") == 0 || strcmp(file, "..") == 0) + error(Efilename); + + c.c = namec(dir, Aaccess, 0, 0); + if((c.c->qid.type&QTDIR) == 0 || devtab[c.c->type]->dc != 's') + error("directory not a srv device"); + + s = c.c->aux; + + qlock(&dev.l); + for(f = s->entry; f != nil; f = f->entry){ + if(strcmp(f->name, file) == 0){ + qunlock(&dev.l); + error(Eexist); + } + } + + f = malloc(sizeof(SrvFile)); + if(f == nil){ + qunlock(&dev.l); + error(Enomem); + } + + srvretype(io->read, f, Trdchan); + srvretype(io->write, f, Twrchan); + f->read = io->read; + f->write = io->write; + + kstrdup(&f->name, file); + kstrdup(&f->user, up->env->user); + f->perm = 0666 & (~0666 | (s->perm & 0666)); + f->length = 0; + f->ref = 2; + mkqid(&f->qid, dev.pathgen++, 0, QTFILE); + + f->entry = s->entry; + s->entry = f; + s->ref++; + f->dir = s; + qunlock(&dev.l); + + cclose(c.c); + poperror(); + + return 0; +} + +Dev srvdevtab = { + 's', + "srv", + + devreset, + srvinit, + devshutdown, + srvattach, + srvwalk, + srvstat, + srvopen, + devcreate, + srvclose, + srvread, + devbread, + srvwrite, + devbwrite, + srvremove, + srvwstat +}; diff --git a/os/port/devssl.c b/os/port/devssl.c new file mode 100644 index 00000000..23c3fec5 --- /dev/null +++ b/os/port/devssl.c @@ -0,0 +1,1436 @@ +/* + * devssl - secure sockets layer + */ +#include "u.h" +#include "../port/lib.h" +#include "../port/error.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "kernel.h" + +#include "mp.h" +#include "libsec.h" + +typedef struct OneWay OneWay; +struct OneWay +{ + QLock q; + QLock ctlq; + + void *state; /* encryption state */ + int slen; /* secret data length */ + uchar *secret; /* secret */ + ulong mid; /* message id */ +}; + +enum +{ + /* connection states */ + Sincomplete= 0, + Sclear= 1, + Sencrypting= 2, + Sdigesting= 4, + Sdigenc= Sencrypting|Sdigesting, + + /* encryption algorithms */ + Noencryption= 0, + DESCBC= 1, + DESECB= 2, + RC4= 3, + IDEACBC= 4, + IDEAECB= 5 +}; + +typedef struct Dstate Dstate; +struct Dstate +{ + Chan *c; /* io channel */ + uchar state; /* state of connection */ + int ref; /* serialized by dslock for atomic destroy */ + + uchar encryptalg; /* encryption algorithm */ + ushort blocklen; /* blocking length */ + + ushort diglen; /* length of digest */ + DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*); /* hash func */ + + /* for SSL format */ + int max; /* maximum unpadded data per msg */ + int maxpad; /* maximum padded data per msg */ + + /* input side */ + OneWay in; + Block *processed; + Block *unprocessed; + + /* output side */ + OneWay out; + + /* protections */ + char* user; + int perm; +}; + +Lock dslock; +int dshiwat; +int maxdstate = 20; +Dstate** dstate; +char** dsname; + +enum +{ + Maxdmsg= 1<<16, + Maxdstate= 1<<10, +}; + +enum{ + Qtopdir = 1, /* top level directory */ + Qclonus, + Qconvdir, /* directory for a conversation */ + Qdata, + Qctl, + Qsecretin, + Qsecretout, + Qencalgs, + Qhashalgs +}; + +#define TYPE(x) ((ulong)(x).path & 0xf) +#define CONV(x) (((ulong)(x).path >> 4)&(Maxdstate-1)) +#define QID(c, y) (((c)<<4) | (y)) + +/* for generating random fill */ +ulong badlong; +uchar *badarray = (uchar*)&badlong; +static char* encalgs; +static char* hashalgs; + +void producerand(void); + +static void alglistinit(void); +static void ensure(Dstate*, Block**, int); +static void consume(Block**, uchar*, int); +static void setsecret(OneWay*, uchar*, int); +static Block* encryptb(Dstate*, Block*, int); +static Block* decryptb(Dstate*, Block*); +static Block* digestb(Dstate*, Block*, int); +static void checkdigestb(Dstate*, Block*); +static Chan* buftochan(char*); +static void sslhangup(Dstate*); +static Dstate* dsclone(Chan *c); +static void dsnew(Chan *c, Dstate **); + +static int +sslgen(Chan *c, char*, Dirtab *d, int nd, int s, Dir *dp) +{ + Qid q; + Dstate *ds; + char name[16], *p, *nm; + + USED(nd); + USED(d); + q.type = QTFILE; + q.vers = 0; + if(s == DEVDOTDOT){ + q.path = QID(0, Qtopdir); + q.type = QTDIR; + devdir(c, q, "#D", 0, eve, 0555, dp); + return 1; + } + switch(TYPE(c->qid)) { + case Qtopdir: + if(s < dshiwat) { + q.path = QID(s, Qconvdir); + q.type = QTDIR; + ds = dstate[s]; + if(ds != 0) + nm = ds->user; + else + nm = eve; + if(dsname[s] == nil){ + sprint(name, "%d", s); + kstrdup(&dsname[s], name); + } + devdir(c, q, dsname[s], 0, nm, DMDIR|0555, dp); + return 1; + } + if(s > dshiwat) + return -1; + /* fall through */ + case Qclonus: + q.path = QID(0, Qclonus); + devdir(c, q, "clone", 0, eve, 0666, dp); + return 1; + case Qconvdir: + ds = dstate[CONV(c->qid)]; + if(ds != 0) + nm = ds->user; + else + nm = eve; + switch(s) { + default: + return -1; + case 0: + q.path = QID(CONV(c->qid), Qctl); + p = "ctl"; + break; + case 1: + q.path = QID(CONV(c->qid), Qdata); + p = "data"; + break; + case 2: + q.path = QID(CONV(c->qid), Qsecretin); + p = "secretin"; + break; + case 3: + q.path = QID(CONV(c->qid), Qsecretout); + p = "secretout"; + break; + case 4: + q.path = QID(CONV(c->qid), Qencalgs); + p = "encalgs"; + break; + case 5: + q.path = QID(CONV(c->qid), Qhashalgs); + p = "hashalgs"; + break; + } + devdir(c, q, p, 0, nm, 0660, dp); + return 1; + } + return -1; +} + +static void +sslinit(void) +{ + if((dstate = malloc(sizeof(Dstate*) * maxdstate)) == 0) + panic("sslinit"); + if((dsname = malloc(sizeof(*dsname) * maxdstate)) == 0) + panic("sslinit"); + alglistinit(); +} + +static Chan * +sslattach(char *spec) +{ + Chan *c; + + c = devattach('D', spec); + c->qid.path = QID(0, Qtopdir); + c->qid.vers = 0; + c->qid.type = QTDIR; + return c; +} + +static Walkqid* +sslwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, sslgen); +} + +static int +sslstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, sslgen); +} + +static Chan* +sslopen(Chan *c, int omode) +{ + Dstate *s, **pp; + int perm; + + perm = 0; + omode &= 3; + switch(omode) { + case OREAD: + perm = 4; + break; + case OWRITE: + perm = 2; + break; + case ORDWR: + perm = 6; + break; + } + + switch(TYPE(c->qid)) { + default: + panic("sslopen"); + case Qtopdir: + case Qconvdir: + if(omode != OREAD) + error(Eperm); + break; + case Qclonus: + s = dsclone(c); + if(s == 0) + error(Enodev); + break; + case Qctl: + case Qdata: + case Qsecretin: + case Qsecretout: + if(waserror()) { + unlock(&dslock); + nexterror(); + } + lock(&dslock); + pp = &dstate[CONV(c->qid)]; + s = *pp; + if(s == 0) + dsnew(c, pp); + else { + if((perm & (s->perm>>6)) != perm + && (strcmp(up->env->user, s->user) != 0 + || (perm & s->perm) != perm)) + error(Eperm); + + s->ref++; + } + unlock(&dslock); + poperror(); + break; + case Qencalgs: + case Qhashalgs: + if(omode != OREAD) + error(Eperm); + break; + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static int +sslwstat(Chan *c, uchar *dp, int n) +{ + Dir d; + Dstate *s; + + n = convM2D(dp, n, &d, nil); + if(n == 0) + error(Eshortstat); + s = dstate[CONV(c->qid)]; + if(s == 0) + error(Ebadusefd); + if(strcmp(s->user, up->env->user) != 0) + error(Eperm); + if(!emptystr(d.uid)) + kstrdup(&s->user, d.uid); + if(d.mode != ~0UL) + s->perm = d.mode; + return n; +} + +static void +sslclose(Chan *c) +{ + Dstate *s; + + switch(TYPE(c->qid)) { + case Qctl: + case Qdata: + case Qsecretin: + case Qsecretout: + if((c->flag & COPEN) == 0) + break; + + s = dstate[CONV(c->qid)]; + if(s == 0) + break; + + lock(&dslock); + if(--s->ref > 0) { + unlock(&dslock); + break; + } + dstate[CONV(c->qid)] = 0; + unlock(&dslock); + + sslhangup(s); + if(s->c) + cclose(s->c); + free(s->user); + free(s->in.secret); + free(s->out.secret); + free(s->in.state); + free(s->out.state); + free(s); + } +} + +/* + * make sure we have at least 'n' bytes in list 'l' + */ +static void +ensure(Dstate *s, Block **l, int n) +{ + int sofar, i; + Block *b, *bl; + + sofar = 0; + for(b = *l; b; b = b->next){ + sofar += BLEN(b); + if(sofar >= n) + return; + l = &b->next; + } + + while(sofar < n){ + bl = devtab[s->c->type]->bread(s->c, Maxdmsg, 0); + if(bl == 0) + error(Ehungup); + *l = bl; + i = 0; + for(b = bl; b; b = b->next){ + i += BLEN(b); + l = &b->next; + } + if(i == 0) + error(Ehungup); + + sofar += i; + } +} + +/* + * copy 'n' bytes from 'l' into 'p' and free + * the bytes in 'l' + */ +static void +consume(Block **l, uchar *p, int n) +{ + Block *b; + int i; + + for(; *l && n > 0; n -= i){ + b = *l; + i = BLEN(b); + if(i > n) + i = n; + memmove(p, b->rp, i); + b->rp += i; + p += i; + if(BLEN(b) < 0) + panic("consume"); + if(BLEN(b)) + break; + *l = b->next; + freeb(b); + } +} + +/* + * remove at most n bytes from the queue, if discard is set + * dump the remainder + */ +static Block* +qtake(Block **l, int n, int discard) +{ + Block *nb, *b, *first; + int i; + + first = *l; + for(b = first; b; b = b->next){ + i = BLEN(b); + if(i == n){ + if(discard){ + freeblist(b->next); + *l = 0; + } else + *l = b->next; + b->next = 0; + return first; + } else if(i > n){ + i -= n; + if(discard){ + freeblist(b->next); + *l = 0; + } else { + nb = allocb(i); + memmove(nb->wp, b->rp+n, i); + nb->wp += i; + nb->next = b->next; + *l = nb; + } + b->wp -= i; + b->next = 0; + if(BLEN(b) < 0) + panic("qtake"); + return first; + } else + n -= i; + if(BLEN(b) < 0) + panic("qtake"); + } + *l = 0; + return first; +} + +static Block* +sslbread(Chan *c, long n, ulong offset) +{ + volatile struct { Dstate *s; } s; + Block *b; + uchar count[2]; + int len, pad; + + USED(offset); + + s.s = dstate[CONV(c->qid)]; + if(s.s == 0) + panic("sslbread"); + if(s.s->state == Sincomplete) + error(Ebadusefd); + + if(waserror()){ + qunlock(&s.s->in.q); + sslhangup(s.s); + nexterror(); + } + qlock(&s.s->in.q); + + if(s.s->processed == 0){ + /* read in the whole message */ + ensure(s.s, &s.s->unprocessed, 2); + consume(&s.s->unprocessed, count, 2); + if(count[0] & 0x80){ + len = ((count[0] & 0x7f)<<8) | count[1]; + ensure(s.s, &s.s->unprocessed, len); + pad = 0; + } else { + len = ((count[0] & 0x3f)<<8) | count[1]; + ensure(s.s, &s.s->unprocessed, len+1); + consume(&s.s->unprocessed, count, 1); + pad = count[0]; + if(pad > len){ + print("pad %d buf len %d\n", pad, len); + error("bad pad in ssl message"); + } + } + + /* put extra on unprocessed queue */ + s.s->processed = qtake(&s.s->unprocessed, len, 0); + + if(waserror()){ + qunlock(&s.s->in.ctlq); + nexterror(); + } + qlock(&s.s->in.ctlq); + switch(s.s->state){ + case Sencrypting: + s.s->processed = decryptb(s.s, s.s->processed); + break; + case Sdigesting: + s.s->processed = pullupblock(s.s->processed, s.s->diglen); + if(s.s->processed == 0) + error("ssl message too short"); + checkdigestb(s.s, s.s->processed); + s.s->processed->rp += s.s->diglen; + break; + case Sdigenc: + s.s->processed = decryptb(s.s, s.s->processed); + s.s->processed = pullupblock(s.s->processed, s.s->diglen); + if(s.s->processed == 0) + error("ssl message too short"); + checkdigestb(s.s, s.s->processed); + s.s->processed->rp += s.s->diglen; + len -= s.s->diglen; + break; + } + s.s->in.mid++; + qunlock(&s.s->in.ctlq); + poperror(); + + /* remove pad */ + if(pad) + s.s->processed = qtake(&s.s->processed, len - pad, 1); + } + + /* return at most what was asked for */ + b = qtake(&s.s->processed, n, 0); + + qunlock(&s.s->in.q); + poperror(); + + return b; +} + +static long +sslread(Chan *c, void *a, long n, vlong offset) +{ + volatile struct { Block *b; } b; + Block *nb; + uchar *va; + int i; + char buf[128]; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, sslgen); + + switch(TYPE(c->qid)) { + default: + error(Ebadusefd); + case Qctl: + sprint(buf, "%ld", CONV(c->qid)); + return readstr(offset, a, n, buf); + case Qdata: + b.b = sslbread(c, n, offset); + break; + case Qencalgs: + return readstr(offset, a, n, encalgs); + case Qhashalgs: + return readstr(offset, a, n, hashalgs); + } + + n = 0; + va = a; + for(nb = b.b; nb; nb = nb->next){ + i = BLEN(nb); + memmove(va+n, nb->rp, i); + n += i; + } + + freeblist(b.b); + + return n; +} + +/* + * this algorithm doesn't have to be great since we're just + * trying to obscure the block fill + */ +static void +randfill(uchar *buf, int len) +{ + while(len-- > 0) + *buf++ = nrand(256); +} + +/* + * use SSL record format, add in count and digest or encrypt + */ +static long +sslbwrite(Chan *c, Block *b, ulong offset) +{ + volatile struct { Dstate *s; } s; + volatile struct { Block *b; } bb; + Block *nb; + int h, n, m, pad, rv; + uchar *p; + + bb.b = b; + + s.s = dstate[CONV(c->qid)]; + if(s.s == 0) + panic("sslbwrite"); + if(s.s->state == Sincomplete){ + freeb(b); + error(Ebadusefd); + } + + if(waserror()){ + qunlock(&s.s->out.q); + if(bb.b) + freeb(bb.b); + sslhangup(s.s); + nexterror(); + } + qlock(&s.s->out.q); + + rv = 0; + while(bb.b){ + m = n = BLEN(bb.b); + h = s.s->diglen + 2; + + /* trim to maximum block size */ + pad = 0; + if(m > s.s->max){ + m = s.s->max; + } else if(s.s->blocklen != 1){ + pad = (m + s.s->diglen)%s.s->blocklen; + if(pad){ + if(m > s.s->maxpad){ + pad = 0; + m = s.s->maxpad; + } else { + pad = s.s->blocklen - pad; + h++; + } + } + } + + rv += m; + if(m != n){ + nb = allocb(m + h + pad); + memmove(nb->wp + h, bb.b->rp, m); + nb->wp += m + h; + bb.b->rp += m; + } else { + /* add header space */ + nb = padblock(bb.b, h); + bb.b = 0; + } + m += s.s->diglen; + + /* SSLv2 style count */ + if(pad){ + nb = padblock(nb, -pad); + randfill(nb->wp, pad); + nb->wp += pad; + m += pad; + + p = nb->rp; + p[0] = (m>>8); + p[1] = m; + p[2] = pad; + offset = 3; + } else { + p = nb->rp; + p[0] = (m>>8) | 0x80; + p[1] = m; + offset = 2; + } + + switch(s.s->state){ + case Sencrypting: + nb = encryptb(s.s, nb, offset); + break; + case Sdigesting: + nb = digestb(s.s, nb, offset); + break; + case Sdigenc: + nb = digestb(s.s, nb, offset); + nb = encryptb(s.s, nb, offset); + break; + } + + s.s->out.mid++; + + m = BLEN(nb); + devtab[s.s->c->type]->bwrite(s.s->c, nb, s.s->c->offset); + s.s->c->offset += m; + } + qunlock(&s.s->out.q); + poperror(); + + return rv; +} + +static void +setsecret(OneWay *w, uchar *secret, int n) +{ + free(w->secret); + w->secret = mallocz(n, 0); + if(w->secret == nil) + error(Enomem); + memmove(w->secret, secret, n); + w->slen = n; +} + +static void +initIDEAkey(OneWay *w) +{ + free(w->state); + w->state = malloc(sizeof(IDEAstate)); + if(w->state == nil) + error(Enomem); + if(w->slen >= 24) + setupIDEAstate(w->state, w->secret, w->secret+16); + else if(w->slen >= 16) + setupIDEAstate(w->state, w->secret, 0); + else + error("secret too short"); +} + +static void +initDESkey(OneWay *w) +{ + free(w->state); + w->state = malloc(sizeof(DESstate)); + if (!w->state) + error(Enomem); + if(w->slen >= 16) + setupDESstate(w->state, w->secret, w->secret+8); + else if(w->slen >= 8) + setupDESstate(w->state, w->secret, 0); + else + error("secret too short"); +} + +/* + * 40 bit DES is the same as 56 bit DES. However, + * 16 bits of the key are masked to zero. + */ +static void +initDESkey_40(OneWay *w) +{ + uchar key[8]; + + if(w->slen >= 8) { + memmove(key, w->secret, 8); + key[0] &= 0x0f; + key[2] &= 0x0f; + key[4] &= 0x0f; + key[6] &= 0x0f; + } + + free(w->state); + w->state = malloc(sizeof(DESstate)); + if (!w->state) + error(Enomem); + if(w->slen >= 16) + setupDESstate(w->state, key, w->secret+8); + else if(w->slen >= 8) + setupDESstate(w->state, key, 0); + else + error("secret too short"); +} + +static void +initRC4key(OneWay *w) +{ + free(w->state); + w->state = malloc(sizeof(RC4state)); + if (!w->state) + error(Enomem); + setupRC4state(w->state, w->secret, w->slen); +} + +/* + * 40 bit RC4 is the same as n-bit RC4. However, + * we ignore all but the first 40 bits of the key. + */ +static void +initRC4key_40(OneWay *w) +{ + int slen = w->slen; + + if(slen > 5) + slen = 5; + + free(w->state); + w->state = malloc(sizeof(RC4state)); + if (!w->state) + error(Enomem); + setupRC4state(w->state, w->secret, slen); +} + +/* + * 128 bit RC4 is the same as n-bit RC4. However, + * we ignore all but the first 128 bits of the key. + */ +static void +initRC4key_128(OneWay *w) +{ + int slen = w->slen; + + if(slen > 16) + slen = 16; + + free(w->state); + w->state = malloc(sizeof(RC4state)); + if (!w->state) + error(Enomem); + setupRC4state(w->state, w->secret, slen); +} + +typedef struct Hashalg Hashalg; +struct Hashalg +{ + char *name; + int diglen; + DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*); +}; + +Hashalg hashtab[] = +{ + { "md4", MD4dlen, md4, }, + { "md5", MD5dlen, md5, }, + { "sha1", SHA1dlen, sha1, }, + { "sha", SHA1dlen, sha1, }, + { 0 } +}; + +static int +parsehashalg(char *p, Dstate *s) +{ + Hashalg *ha; + + for(ha = hashtab; ha->name; ha++){ + if(strcmp(p, ha->name) == 0){ + s->hf = ha->hf; + s->diglen = ha->diglen; + s->state &= ~Sclear; + s->state |= Sdigesting; + return 0; + } + } + return -1; +} + +typedef struct Encalg Encalg; +struct Encalg +{ + char *name; + int blocklen; + int alg; + void (*keyinit)(OneWay*); +}; + +Encalg encrypttab[] = +{ + { "descbc", 8, DESCBC, initDESkey, }, /* DEPRECATED -- use des_56_cbc */ + { "desecb", 8, DESECB, initDESkey, }, /* DEPRECATED -- use des_56_ecb */ + { "des_56_cbc", 8, DESCBC, initDESkey, }, + { "des_56_ecb", 8, DESECB, initDESkey, }, + { "des_40_cbc", 8, DESCBC, initDESkey_40, }, + { "des_40_ecb", 8, DESECB, initDESkey_40, }, + { "rc4", 1, RC4, initRC4key_40, }, /* DEPRECATED -- use rc4_X */ + { "rc4_256", 1, RC4, initRC4key, }, + { "rc4_128", 1, RC4, initRC4key_128, }, + { "rc4_40", 1, RC4, initRC4key_40, }, + { "ideacbc", 8, IDEACBC, initIDEAkey, }, + { "ideaecb", 8, IDEAECB, initIDEAkey, }, + { 0 } +}; + +static int +parseencryptalg(char *p, Dstate *s) +{ + Encalg *ea; + + for(ea = encrypttab; ea->name; ea++){ + if(strcmp(p, ea->name) == 0){ + s->encryptalg = ea->alg; + s->blocklen = ea->blocklen; + (*ea->keyinit)(&s->in); + (*ea->keyinit)(&s->out); + s->state &= ~Sclear; + s->state |= Sencrypting; + return 0; + } + } + return -1; +} + +static void +alglistinit(void) +{ + Hashalg *h; + Encalg *e; + int n; + + n = 1; + for(e = encrypttab; e->name != nil; e++) + n += strlen(e->name) + 1; + encalgs = malloc(n); + if(encalgs == nil) + panic("sslinit"); + n = 0; + for(e = encrypttab; e->name != nil; e++){ + strcpy(encalgs+n, e->name); + n += strlen(e->name); + if(e[1].name == nil) + break; + encalgs[n++] = ' '; + } + encalgs[n] = 0; + + n = 1; + for(h = hashtab; h->name != nil; h++) + n += strlen(h->name) + 1; + hashalgs = malloc(n); + if(hashalgs == nil) + panic("sslinit"); + n = 0; + for(h = hashtab; h->name != nil; h++){ + strcpy(hashalgs+n, h->name); + n += strlen(h->name); + if(h[1].name == nil) + break; + hashalgs[n++] = ' '; + } + hashalgs[n] = 0; +} + +static long +sslwrite(Chan *c, void *a, long n, vlong offset) +{ + volatile struct { Dstate *s; } s; + volatile struct { Block *b; } b; + int m, t; + char *p, *np, *e, buf[32]; + uchar *x; + + s.s = dstate[CONV(c->qid)]; + if(s.s == 0) + panic("sslwrite"); + + t = TYPE(c->qid); + if(t == Qdata){ + if(s.s->state == Sincomplete) + error(Ebadusefd); + + p = a; + e = p + n; + do { + m = e - p; + if(m > s.s->max) + m = s.s->max; + + b.b = allocb(m); + memmove(b.b->wp, p, m); + b.b->wp += m; + + sslbwrite(c, b.b, offset); + + p += m; + } while(p < e); + return n; + } + + /* mutex with operations using what we're about to change */ + if(waserror()){ + qunlock(&s.s->in.ctlq); + qunlock(&s.s->out.q); + nexterror(); + } + qlock(&s.s->in.ctlq); + qlock(&s.s->out.q); + + switch(t){ + default: + panic("sslwrite"); + case Qsecretin: + setsecret(&s.s->in, a, n); + goto out; + case Qsecretout: + setsecret(&s.s->out, a, n); + goto out; + case Qctl: + break; + } + + if(n >= sizeof(buf)) + error(Ebadarg); + strncpy(buf, a, n); + buf[n] = 0; + p = strchr(buf, '\n'); + if(p) + *p = 0; + p = strchr(buf, ' '); + if(p) + *p++ = 0; + + if(strcmp(buf, "fd") == 0){ + s.s->c = buftochan(p); + + /* default is clear (msg delimiters only) */ + s.s->state = Sclear; + s.s->blocklen = 1; + s.s->diglen = 0; + s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1; + s.s->in.mid = 0; + s.s->out.mid = 0; + } else if(strcmp(buf, "alg") == 0 && p != 0){ + s.s->blocklen = 1; + s.s->diglen = 0; + + if(s.s->c == 0) + error("must set fd before algorithm"); + + if(strcmp(p, "clear") == 0){ + s.s->state = Sclear; + s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1; + goto out; + } + + if(s.s->in.secret && s.s->out.secret == 0) + setsecret(&s.s->out, s.s->in.secret, s.s->in.slen); + if(s.s->out.secret && s.s->in.secret == 0) + setsecret(&s.s->in, s.s->out.secret, s.s->out.slen); + if(s.s->in.secret == 0 || s.s->out.secret == 0) + error("algorithm but no secret"); + + s.s->hf = 0; + s.s->encryptalg = Noencryption; + s.s->blocklen = 1; + + for(;;){ + np = strchr(p, ' '); + if(np) + *np++ = 0; + else{ + np = strchr(p, '/'); + if(np) + *np++ = 0; + } + if(parsehashalg(p, s.s) < 0) + if(parseencryptalg(p, s.s) < 0) + error(Ebadarg); + + if(np == 0) + break; + p = np; + } + + if(s.s->hf == 0 && s.s->encryptalg == Noencryption) + error(Ebadarg); + + if(s.s->blocklen != 1){ + /* make multiple of blocklen */ + s.s->max = (1<<15) - s.s->diglen - 1; + s.s->max -= s.s->max % s.s->blocklen; + s.s->maxpad = (1<<14) - s.s->diglen - 1; + s.s->maxpad -= s.s->maxpad % s.s->blocklen; + } else + s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1; + } else if(strcmp(buf, "secretin") == 0 && p != 0) { + m = (strlen(p)*3)/2; + x = smalloc(m); + if(waserror()){ + free(x); + nexterror(); + } + t = dec64(x, m, p, strlen(p)); + setsecret(&s.s->in, x, t); + poperror(); + free(x); + } else if(strcmp(buf, "secretout") == 0 && p != 0) { + m = (strlen(p)*3)/2; + x = smalloc(m); + if(waserror()){ + free(x); + nexterror(); + } + t = dec64(x, m, p, strlen(p)); + setsecret(&s.s->out, x, t); + poperror(); + free(x); + } else + error(Ebadarg); + +out: + qunlock(&s.s->in.ctlq); + qunlock(&s.s->out.q); + poperror(); + return n; +} + +Dev ssldevtab = { + 'D', + "ssl", + + devreset, + sslinit, + devshutdown, + sslattach, + sslwalk, + sslstat, + sslopen, + devcreate, + sslclose, + sslread, + sslbread, + sslwrite, + sslbwrite, + devremove, + sslwstat, +}; + +static Block* +encryptb(Dstate *s, Block *b, int offset) +{ + uchar *p, *ep, *p2, *ip, *eip; + DESstate *ds; + IDEAstate *is; + + switch(s->encryptalg){ + case DESECB: + ds = s->out.state; + ep = b->rp + BLEN(b); + for(p = b->rp + offset; p < ep; p += 8) + block_cipher(ds->expanded, p, 0); + break; + case DESCBC: + ds = s->out.state; + ep = b->rp + BLEN(b); + for(p = b->rp + offset; p < ep; p += 8){ + p2 = p; + ip = ds->ivec; + for(eip = ip+8; ip < eip; ) + *p2++ ^= *ip++; + block_cipher(ds->expanded, p, 0); + memmove(ds->ivec, p, 8); + } + break; + case IDEAECB: + is = s->out.state; + ep = b->rp + BLEN(b); + for(p = b->rp + offset; p < ep; p += 8) + idea_cipher(is->edkey, p, 0); + break; + case IDEACBC: + is = s->out.state; + ep = b->rp + BLEN(b); + for(p = b->rp + offset; p < ep; p += 8){ + p2 = p; + ip = is->ivec; + for(eip = ip+8; ip < eip; ) + *p2++ ^= *ip++; + idea_cipher(is->edkey, p, 0); + memmove(is->ivec, p, 8); + } + break; + case RC4: + rc4(s->out.state, b->rp + offset, BLEN(b) - offset); + break; + } + return b; +} + +static Block* +decryptb(Dstate *s, Block *inb) +{ + Block *b, **l; + uchar *p, *ep, *tp, *ip, *eip; + DESstate *ds; + IDEAstate *is; + uchar tmp[8]; + int i; + + l = &inb; + for(b = inb; b; b = b->next){ + /* make sure we have a multiple of s->blocklen */ + if(s->blocklen > 1){ + i = BLEN(b); + if(i % s->blocklen){ + *l = b = pullupblock(b, i + s->blocklen - (i%s->blocklen)); + if(b == 0) + error("ssl encrypted message too short"); + } + } + l = &b->next; + + /* decrypt */ + switch(s->encryptalg){ + case DESECB: + ds = s->in.state; + ep = b->rp + BLEN(b); + for(p = b->rp; p < ep; p += 8) + block_cipher(ds->expanded, p, 1); + break; + case DESCBC: + ds = s->in.state; + ep = b->rp + BLEN(b); + for(p = b->rp; p < ep;){ + memmove(tmp, p, 8); + block_cipher(ds->expanded, p, 1); + tp = tmp; + ip = ds->ivec; + for(eip = ip+8; ip < eip; ){ + *p++ ^= *ip; + *ip++ = *tp++; + } + } + break; + case IDEAECB: + is = s->in.state; + ep = b->rp + BLEN(b); + for(p = b->rp; p < ep; p += 8) + idea_cipher(is->edkey, p, 1); + break; + case IDEACBC: + is = s->in.state; + ep = b->rp + BLEN(b); + for(p = b->rp; p < ep;){ + memmove(tmp, p, 8); + idea_cipher(is->edkey, p, 1); + tp = tmp; + ip = is->ivec; + for(eip = ip+8; ip < eip; ){ + *p++ ^= *ip; + *ip++ = *tp++; + } + } + break; + case RC4: + rc4(s->in.state, b->rp, BLEN(b)); + break; + } + } + return inb; +} + +static Block* +digestb(Dstate *s, Block *b, int offset) +{ + uchar *p; + DigestState ss; + uchar msgid[4]; + ulong n, h; + OneWay *w; + + w = &s->out; + + memset(&ss, 0, sizeof(ss)); + h = s->diglen + offset; + n = BLEN(b) - h; + + /* hash secret + message */ + (*s->hf)(w->secret, w->slen, 0, &ss); + (*s->hf)(b->rp + h, n, 0, &ss); + + /* hash message id */ + p = msgid; + n = w->mid; + *p++ = n>>24; + *p++ = n>>16; + *p++ = n>>8; + *p = n; + (*s->hf)(msgid, 4, b->rp + offset, &ss); + + return b; +} + +static void +checkdigestb(Dstate *s, Block *inb) +{ + uchar *p; + DigestState ss; + uchar msgid[4]; + int n, h; + OneWay *w; + uchar digest[128]; + Block *b; + + w = &s->in; + + memset(&ss, 0, sizeof(ss)); + + /* hash secret */ + (*s->hf)(w->secret, w->slen, 0, &ss); + + /* hash message */ + h = s->diglen; + for(b = inb; b; b = b->next){ + n = BLEN(b) - h; + if(n < 0) + panic("checkdigestb"); + (*s->hf)(b->rp + h, n, 0, &ss); + h = 0; + } + + /* hash message id */ + p = msgid; + n = w->mid; + *p++ = n>>24; + *p++ = n>>16; + *p++ = n>>8; + *p = n; + (*s->hf)(msgid, 4, digest, &ss); + + /* requires pullupblock */ + if(memcmp(digest, inb->rp, s->diglen) != 0) + error("bad digest"); +} + +/* get channel associated with an fd */ +static Chan* +buftochan(char *p) +{ + Chan *c; + int fd; + + if(p == 0) + error(Ebadarg); + fd = strtoul(p, 0, 0); + if(fd < 0) + error(Ebadarg); + c = fdtochan(up->env->fgrp, fd, -1, 0, 1); /* error check and inc ref */ + return c; +} + +/* hang up a digest connection */ +static void +sslhangup(Dstate *s) +{ + qlock(&s->in.q); + freeblist(s->processed); + s->processed = 0; + freeblist(s->unprocessed); + s->unprocessed = 0; + s->state = Sincomplete; + qunlock(&s->in.q); +} + +extern void rbcheck(char*); + +static Dstate* +dsclone(Chan *ch) +{ + Dstate **pp, **ep, **np; + int newmax; + + if(waserror()) { + unlock(&dslock); + nexterror(); + } + lock(&dslock); + ep = &dstate[maxdstate]; + for(pp = dstate; pp < ep; pp++) { + if(*pp == 0) { + dsnew(ch, pp); + break; + } + } + if(pp >= ep) { + if(maxdstate >= Maxdstate) { + unlock(&dslock); + poperror(); + return 0; + } + newmax = 2 * maxdstate; + if(newmax > Maxdstate) + newmax = Maxdstate; + np = realloc(dstate, sizeof(Dstate*) * newmax); + if(np == 0) + error(Enomem); + dstate = np; + pp = &dstate[maxdstate]; + memset(pp, 0, sizeof(Dstate*)*(newmax - maxdstate)); + maxdstate = newmax; + dsnew(ch, pp); + } + unlock(&dslock); + poperror(); + return *pp; +} + +static void +dsnew(Chan *ch, Dstate **pp) +{ + Dstate *s; + int t; + + *pp = s = malloc(sizeof(*s)); + if(!s) + error(Enomem); + if(pp - dstate >= dshiwat) + dshiwat++; + s->state = Sincomplete; + s->ref = 1; + kstrdup(&s->user, up->env->user); + s->perm = 0660; + t = TYPE(ch->qid); + if(t == Qclonus) + t = Qctl; + ch->qid.path = QID(pp - dstate, t); + ch->qid.vers = 0; + ch->qid.type = QTFILE; +} diff --git a/os/port/devtest.c b/os/port/devtest.c new file mode 100644 index 00000000..2c935014 --- /dev/null +++ b/os/port/devtest.c @@ -0,0 +1,125 @@ +/* + * Test device + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "libcrypt.h" + +#include <kernel.h> + +#define DEBUG 0 + +extern void _startup(void); + +enum{ + Qdir, + Qkt5sum, + Qkerndate, +}; + +static +Dirtab testtab[]={ + ".", { Qdir, 0, QTDIR}, 0, 0555, + "kt5sum", { Qkt5sum }, 0, 0444, + "kerndate", { Qkerndate }, 0, 0444, +}; + + +void ktsum(char *digest) +{ + uchar rawdigest[MD5dlen+1]; + int i; + void *start = _startup; + ulong size = (ulong)etext - (ulong) start; + md5(start, size, rawdigest, nil); + for (i=0; i<MD5dlen; i++) + sprint(&digest[2*i], "%2.2x", rawdigest[i]); + digest[MD5dlen*2] = 0; + strcat(digest, "\n"); +} + +static Chan* +testattach(char *spec) +{ + return devattach('Z', spec); +} + +static Walkqid* +testwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, testtab, nelem(testtab), devgen); +} + +static int +teststat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, testtab, nelem(testtab), devgen); +} + +static Chan* +testopen(Chan *c, int omode) +{ + return devopen(c, omode, testtab, nelem(testtab), devgen); +} + +static void +testclose(Chan *) +{ +} + +extern ulong kerndate; + +static long +testread(Chan* c, void* a, long n, vlong offset) +{ + char digest[MD5dlen*2+1]; + switch ((ulong)c->qid.path) { + case Qdir: + return devdirread(c, a, n, testtab, nelem(testtab), devgen); + case Qkt5sum: + ktsum(digest); + return readstr(offset, a, n, digest); + case Qkerndate: + sprint(digest, "%ld\n", kerndate); + return readstr(offset, a, n, digest); + default: + n = 0; + break; + } + return n; +} + + +static long +testwrite(Chan*, void*, long, vlong) +{ + error(Ebadusefd); + return 0; +} + +Dev testdevtab = { + 'Z', + "test", + + devreset, + devinit, + devshutdown, + testattach, + testwalk, + teststat, + testopen, + devcreate, + testclose, + testread, + devbread, + testwrite, + devbwrite, + devremove, + devwstat, +}; + diff --git a/os/port/devtinyfs.c b/os/port/devtinyfs.c new file mode 100644 index 00000000..b63caf3f --- /dev/null +++ b/os/port/devtinyfs.c @@ -0,0 +1,915 @@ +/* + * a pity the code isn't also tiny... + */ +#include "u.h" +#include "../port/lib.h" +#include "../port/error.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +enum{ + Qdir, + Qmedium, + + Maxfs= 10, /* max file systems */ + + Blen= 48, /* block length */ + Nlen= 28, /* name length */ + Dlen= Blen - 4, + + Tagdir= 'd', + Tagdata= 'D', + Tagend= 'e', + Tagfree= 'f', + + Notapin= 0xffff, + Notabno= 0xffff, + + Fcreating= 1, + Frmonclose= 2 +}; + +/* representation of a Tdir on medium */ +typedef struct Mdir Mdir; +struct Mdir { + uchar type; + uchar bno[2]; + uchar pin[2]; + char name[Nlen]; + char pad[Blen - Nlen - 6]; + uchar sum; +}; + +/* representation of a Tdata/Tend on medium */ +typedef struct Mdata Mdata; +struct Mdata { + uchar type; + uchar bno[2]; + uchar data[Dlen]; + uchar sum; +}; + +typedef struct Tfile Tfile; +struct Tfile { + int r; + char name[Nlen]; + ushort bno; + ushort dbno; + ushort pin; + uchar flag; + ulong length; + + /* hint to avoid egregious reading */ + ushort fbno; + ulong finger; +}; + +typedef struct Tfs Tfs; +struct Tfs { + QLock ql; + int r; + Chan *c; + uchar *map; + int nblocks; + Tfile *f; + int nf; + int fsize; +}; + +static struct { + Tfs fs[Maxfs]; +} tinyfs; + +#define GETS(x) ((x)[0]|((x)[1]<<8)) +#define PUTS(x, v) {(x)[0] = (v);(x)[1] = ((v)>>8);} + +#define GETL(x) (GETS(x)|(GETS(x+2)<<16)) +#define PUTL(x, v) {PUTS(x, v);PUTS(x+2, (v)>>16)}; + +static uchar +checksum(uchar *p) +{ + uchar *e; + uchar s; + + s = 0; + for(e = p + Blen; p < e; p++) + s += *p; + return s; +} + +static void +mapclr(Tfs *fs, ulong bno) +{ + fs->map[bno>>3] &= ~(1<<(bno&7)); +} + +static void +mapset(Tfs *fs, ulong bno) +{ + fs->map[bno>>3] |= 1<<(bno&7); +} + +static int +isalloced(Tfs *fs, ulong bno) +{ + return fs->map[bno>>3] & (1<<(bno&7)); +} + +static int +mapalloc(Tfs *fs) +{ + int i, j, lim; + uchar x; + + lim = (fs->nblocks + 8 - 1)/8; + for(i = 0; i < lim; i++){ + x = fs->map[i]; + if(x == 0xff) + continue; + for(j = 0; j < 8; j++) + if((x & (1<<j)) == 0){ + fs->map[i] = x|(1<<j); + return i*8 + j; + } + } + + return Notabno; +} + +static Mdir* +validdir(Tfs *fs, uchar *p) +{ + Mdir *md; + ulong x; + + if(checksum(p) != 0) + return 0; + if(p[0] != Tagdir) + return 0; + md = (Mdir*)p; + x = GETS(md->bno); + if(x >= fs->nblocks) + return 0; + return md; +} + +static Mdata* +validdata(Tfs *fs, uchar *p, int *lenp) +{ + Mdata *md; + ulong x; + + if(checksum(p) != 0) + return 0; + md = (Mdata*)p; + switch(md->type){ + case Tagdata: + x = GETS(md->bno); + if(x >= fs->nblocks) + return 0; + if(lenp) + *lenp = Dlen; + break; + case Tagend: + x = GETS(md->bno); + if(x > Dlen) + return 0; + if(lenp) + *lenp = x; + break; + default: + return 0; + } + return md; +} + +static Mdata* +readdata(Tfs *fs, ulong bno, uchar *buf, int *lenp) +{ + if(bno >= fs->nblocks) + return 0; + if(devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno) != Blen) + error(Eio); + return validdata(fs, buf, lenp); +} + +static void +writedata(Tfs *fs, ulong bno, ulong next, uchar *buf, int len, int last) +{ + Mdata md; + + if(bno >= fs->nblocks) + error(Eio); + if(len > Dlen) + len = Dlen; + if(len < 0) + error(Eio); + memset(&md, 0, sizeof(md)); + if(last){ + md.type = Tagend; + PUTS(md.bno, len); + } else { + md.type = Tagdata; + PUTS(md.bno, next); + } + memmove(md.data, buf, len); + md.sum = 0 - checksum((uchar*)&md); + + if(devtab[fs->c->type]->write(fs->c, &md, Blen, Blen*bno) != Blen) + error(Eio); +} + +static void +writedir(Tfs *fs, Tfile *f) +{ + Mdir *md; + uchar buf[Blen]; + + if(f->bno == Notabno) + return; + + md = (Mdir*)buf; + memset(buf, 0, Blen); + md->type = Tagdir; + strncpy(md->name, f->name, sizeof(md->name)-1); + PUTS(md->bno, f->dbno); + PUTS(md->pin, f->pin); + md->sum = 0 - checksum(buf); + + if(devtab[fs->c->type]->write(fs->c, buf, Blen, Blen*f->bno) != Blen) + error(Eio); +} + +static void +freeblocks(Tfs *fs, ulong bno, ulong bend) +{ + uchar buf[Blen]; + Mdata *md; + + if(waserror()) + return; + + while(bno != bend && bno != Notabno){ + mapclr(fs, bno); + if(devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno) != Blen) + break; + md = validdata(fs, buf, 0); + if(md == 0) + break; + if(md->type == Tagend) + break; + bno = GETS(md->bno); + } + + poperror(); +} + +static void +freefile(Tfs *fs, Tfile *f, ulong bend) +{ + uchar buf[Blen]; + + /* remove blocks from map */ + freeblocks(fs, f->dbno, bend); + + /* change file type to free on medium */ + if(f->bno != Notabno){ + memset(buf, 0x55, Blen); + devtab[fs->c->type]->write(fs->c, buf, Blen, Blen*f->bno); + mapclr(fs, f->bno); + } + + /* forget we ever knew about it */ + memset(f, 0, sizeof(*f)); +} + +static void +expand(Tfs *fs) +{ + Tfile *f; + + fs->fsize += 8; + f = malloc(fs->fsize*sizeof(*f)); + if(f == nil) + error(Enomem); + + if(fs->f){ + memmove(f, fs->f, fs->nf*sizeof(*f)); + free(fs->f); + } + fs->f = f; +} + +static Tfile* +newfile(Tfs *fs, char *name) +{ + int i; + volatile struct { + Tfile *f; + Tfs *fs; + } rock; + + /* find free entry in file table */ + rock.f = 0; + rock.fs = fs; + for(;;) { + for(i = 0; i < rock.fs->fsize; i++){ + rock.f = &rock.fs->f[i]; + if(rock.f->name[0] == 0){ + strncpy(rock.f->name, name, sizeof(rock.f->name)-1); + break; + } + } + + if(i < rock.fs->fsize){ + if(i >= rock.fs->nf) + rock.fs->nf = i+1; + break; + } + + expand(rock.fs); + } + + rock.f->flag = Fcreating; + rock.f->dbno = Notabno; + rock.f->bno = mapalloc(rock.fs); + rock.f->fbno = Notabno; + rock.f->r = 1; + rock.f->pin = up->env->pgrp->pin; + + /* write directory block */ + if(waserror()){ + freefile(rock.fs, rock.f, Notabno); + nexterror(); + } + if(rock.f->bno == Notabno) + error("out of space"); + writedir(rock.fs, rock.f); + poperror(); + + return rock.f; +} + +/* + * Read the whole medium and build a file table and used + * block bitmap. Inconsistent files are purged. The medium + * had better be small or this could take a while. + */ +static void +tfsinit(Tfs *fs) +{ + uchar dbuf[STATFIXLEN+4*KNAMELEN]; + Dir d; + uchar buf[Blen]; + ulong x, bno; + int n, done; + Tfile *f; + Mdir *mdir; + Mdata *mdata; + + n = devtab[fs->c->type]->stat(fs->c, dbuf, sizeof(dbuf)); + if(n == 0) + error(Eshortstat); + n = convM2D(dbuf, n, &d, nil); + if(n == 0) + error(Eshortstat); + fs->nblocks = d.length/Blen; + if(fs->nblocks < 3) + error("tinyfs medium too small"); + + /* bitmap for block usage */ + x = (fs->nblocks + 8 - 1)/8; + fs->map = malloc(x); + if(fs->map == nil) + error(Enomem); + memset(fs->map, 0x0, x); + for(bno = fs->nblocks; bno < x*8; bno++) + mapset(fs, bno); + + /* find files */ + for(bno = 0; bno < fs->nblocks; bno++){ + n = devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno); + if(n != Blen) + break; + + mdir = validdir(fs, buf); + if(mdir == 0) + continue; + + if(fs->nf >= fs->fsize) + expand(fs); + + f = &fs->f[fs->nf++]; + + x = GETS(mdir->bno); + mapset(fs, bno); + strncpy(f->name, mdir->name, sizeof(f->name)); + f->pin = GETS(mdir->pin); + f->bno = bno; + f->dbno = x; + f->fbno = Notabno; + } + + /* follow files */ + for(f = fs->f; f < &(fs->f[fs->nf]); f++){ + bno = f->dbno; + for(done = 0; !done;) { + if(isalloced(fs, bno)){ + freefile(fs, f, bno); + break; + } + n = devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno); + if(n != Blen){ + freefile(fs, f, bno); + break; + } + mdata = validdata(fs, buf, 0); + if(mdata == 0){ + freefile(fs, f, bno); + break; + } + mapset(fs, bno); + switch(mdata->type){ + case Tagdata: + bno = GETS(mdata->bno); + f->length += Dlen; + break; + case Tagend: + f->length += GETS(mdata->bno); + done = 1; + break; + } + if(done) + f->flag &= ~Fcreating; + } + } +} + +/* + * single directory + */ +static int +tinyfsgen(Chan *c, char*, Dirtab *tab, int ntab, int i, Dir *dp) +{ + Tfs *fs; + Tfile *f; + Qid qid; + + USED(ntab); + USED(tab); + + fs = &tinyfs.fs[c->dev]; + if(i >= fs->nf) + return -1; + if(i == DEVDOTDOT){ + mkqid(&qid, Qdir, 0, QTDIR); + devdir(c, qid, ".", 0, eve, 0555, dp); + return 1; + } + f = &fs->f[i]; + if(f->name[0] == 0) + return 0; + mkqid(&qid, i, 0, QTFILE); + devdir(c, qid, f->name, f->length, eve, 0664, dp); + return 1; +} + +static void +tinyfsinit(void) +{ + if(Nlen > KNAMELEN) + panic("tinyfsinit"); +} + +/* + * specifier is the name of a device in /dev + */ +static Chan* +tinyfsattach(char *spec) +{ + Tfs *fs; + Chan *c; + volatile struct { Chan *cc; } rock; + int i; + char buf[KNAMELEN*3]; + + if(*spec == 0) + error("bad specifier"); + + snprint(buf, sizeof(buf), "/dev/%s", spec); + rock.cc = namec(buf, Aopen, ORDWR, 0); + if(waserror()){ + cclose(rock.cc); + nexterror(); + } + + fs = 0; + for(i = 0; i < Maxfs; i++){ + fs = &tinyfs.fs[i]; + qlock(&fs->ql); + if(fs->r && eqchan(rock.cc, fs->c, 1)) + break; + qunlock(&fs->ql); + } + if(i < Maxfs){ + fs->r++; + qunlock(&fs->ql); + cclose(rock.cc); + } else { + for(fs = tinyfs.fs; fs < &tinyfs.fs[Maxfs]; fs++){ + qlock(&fs->ql); + if(fs->r == 0) + break; + qunlock(&fs->ql); + } + if(fs == &tinyfs.fs[Maxfs]) + error("too many tinyfs's"); + fs->c = rock.cc; + fs->r = 1; + fs->f = 0; + fs->nf = 0; + fs->fsize = 0; + tfsinit(fs); + qunlock(&fs->ql); + } + poperror(); + + c = devattach('F', spec); + c->dev = fs - tinyfs.fs; + c->qid.path = Qdir; + c->qid.type = QTDIR; + c->qid.vers = 0; + + return c; +} + +static Walkqid* +tinyfswalk(Chan *c, Chan *nc, char **name, int nname) +{ + Tfs *fs; + Walkqid *w; + + fs = &tinyfs.fs[c->dev]; + + qlock(&fs->ql); + if(waserror()){ + qunlock(&fs->ql); + nexterror(); + } + w = devwalk(c, nc, name, nname, 0, 0, tinyfsgen); + if(w != nil && w->clone!=nil && w->clone->qid.type != QTDIR){ + fs = &tinyfs.fs[w->clone->dev]; + fs->r++; + fs->f[(ulong)w->clone->qid.path].r++; + } + poperror(); + qunlock(&fs->ql); + return w; +} + +static int +tinyfsstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, tinyfsgen); +} + +static Chan* +tinyfsopen(Chan *c, int omode) +{ + Tfile *f; + volatile struct { Tfs *fs; } rock; + + rock.fs = &tinyfs.fs[c->dev]; + + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Eperm); + } else { + qlock(&rock.fs->ql); + if(waserror()){ + qunlock(&rock.fs->ql); + nexterror(); + } + switch(omode){ + case OTRUNC|ORDWR: + case OTRUNC|OWRITE: + f = newfile(rock.fs, rock.fs->f[c->qid.path].name); + rock.fs->f[c->qid.path].r--; + c->qid.path = f - rock.fs->f; + break; + case OREAD: + break; + default: + error(Eperm); + } + qunlock(&rock.fs->ql); + poperror(); + } + + return devopen(c, omode, 0, 0, tinyfsgen); +} + +static void +tinyfscreate(Chan *c, char *name, int omode, ulong perm) +{ + volatile struct { Tfs *fs; } rock; + Tfile *f; + + USED(perm); + + rock.fs = &tinyfs.fs[c->dev]; + + qlock(&rock.fs->ql); + if(waserror()){ + qunlock(&rock.fs->ql); + nexterror(); + } + f = newfile(rock.fs, name); + qunlock(&rock.fs->ql); + poperror(); + + c->qid.path = f - rock.fs->f; + c->qid.vers = 0; + c->mode = openmode(omode); +} + +static void +tinyfsremove(Chan *c) +{ + Tfs *fs; + Tfile *f; + + if(c->qid.type & QTDIR) + error(Eperm); + fs = &tinyfs.fs[c->dev]; + f = &fs->f[c->qid.path]; + qlock(&fs->ql); + freefile(fs, f, Notabno); + qunlock(&fs->ql); +} + +static void +tinyfsclose(Chan *c) +{ + volatile struct { Tfs *fs; } rock; + Tfile *f, *nf; + int i; + + rock.fs = &tinyfs.fs[c->dev]; + + qlock(&rock.fs->ql); + + /* dereference file and remove old versions */ + if(!waserror()){ + if(c->qid.path != Qdir){ + f = &rock.fs->f[c->qid.path]; + f->r--; + if(f->r == 0){ + if(f->flag & Frmonclose) + freefile(rock.fs, f, Notabno); + else if(f->flag & Fcreating){ + /* remove all other files with this name */ + for(i = 0; i < rock.fs->fsize; i++){ + nf = &rock.fs->f[i]; + if(f == nf) + continue; + if(strcmp(nf->name, f->name) == 0){ + if(nf->r) + nf->flag |= Frmonclose; + else + freefile(rock.fs, nf, Notabno); + } + } + f->flag &= ~Fcreating; + } + } + } + poperror(); + } + + /* dereference rock.fs and remove on zero refs */ + rock.fs->r--; + if(rock.fs->r == 0){ + if(rock.fs->f) + free(rock.fs->f); + rock.fs->f = 0; + rock.fs->nf = 0; + rock.fs->fsize = 0; + if(rock.fs->map) + free(rock.fs->map); + rock.fs->map = 0; + cclose(rock.fs->c); + rock.fs->c = 0; + } + qunlock(&rock.fs->ql); +} + +static long +tinyfsread(Chan *c, void *a, long n, vlong offset) +{ + volatile struct { Tfs *fs; } rock; + Tfile *f; + int sofar, i, off; + ulong bno; + Mdata *md; + uchar buf[Blen]; + uchar *p; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, tinyfsgen); + + p = a; + rock.fs = &tinyfs.fs[c->dev]; + f = &rock.fs->f[c->qid.path]; + if(offset >= f->length) + return 0; + + qlock(&rock.fs->ql); + if(waserror()){ + qunlock(&rock.fs->ql); + nexterror(); + } + if(n + offset >= f->length) + n = f->length - offset; + + /* walk to starting data block */ + if(0 && f->finger <= offset && f->fbno != Notabno){ + sofar = f->finger; + bno = f->fbno; + } else { + sofar = 0; + bno = f->dbno; + } + for(; sofar + Dlen <= offset; sofar += Dlen){ + md = readdata(rock.fs, bno, buf, 0); + if(md == 0) + error(Eio); + bno = GETS(md->bno); + } + + /* read data */ + off = offset%Dlen; + offset -= off; + for(sofar = 0; sofar < n; sofar += i){ + md = readdata(rock.fs, bno, buf, &i); + if(md == 0) + error(Eio); + + /* update finger for successful read */ + f->finger = offset; + f->fbno = bno; + offset += Dlen; + + i -= off; + if(i > n - sofar) + i = n - sofar; + memmove(p, md->data+off, i); + p += i; + bno = GETS(md->bno); + off = 0; + } + qunlock(&rock.fs->ql); + poperror(); + + return sofar; +} + +/* + * if we get a write error in this routine, blocks will + * be lost. They should be recovered next fsinit. + */ +static long +tinyfswrite(Chan *c, void *a, long n, vlong offset) +{ + Tfile *f; + int last, next, i, finger, off, used; + ulong bno, fbno; + Mdata *md; + uchar buf[Blen]; + uchar *p; + volatile struct { + Tfs *fs; + ulong dbno; + } rock; + + if(c->qid.type & QTDIR) + error(Eperm); + + if(n == 0) + return 0; + + p = a; + rock.fs = &tinyfs.fs[c->dev]; + f = &rock.fs->f[c->qid.path]; + + qlock(&rock.fs->ql); + rock.dbno = Notabno; + if(waserror()){ + freeblocks(rock.fs, rock.dbno, Notabno); + qunlock(&rock.fs->ql); + nexterror(); + } + + /* files are append only, anything else is illegal */ + if(offset != f->length) + error("append only"); + + /* write blocks backwards */ + p += n; + last = offset + n; + fbno = Notabno; + finger = 0; + off = offset; /* so we have something signed to compare against */ + for(next = ((last-1)/Dlen)*Dlen; next >= off; next -= Dlen){ + bno = mapalloc(rock.fs); + if(bno == Notabno) + error("out of space"); + i = last - next; + p -= i; + if(last == n+offset){ + writedata(rock.fs, bno, rock.dbno, p, i, 1); + finger = next; /* remember for later */ + fbno = bno; + } else { + writedata(rock.fs, bno, rock.dbno, p, i, 0); + } + rock.dbno = bno; + last = next; + } + + /* walk to last data block */ + md = (Mdata*)buf; + if(0 && f->finger < offset && f->fbno != Notabno){ + next = f->finger; + bno = f->fbno; + } else { + next = 0; + bno = f->dbno; + } + + used = 0; + while(bno != Notabno){ + md = readdata(rock.fs, bno, buf, &used); + if(md == 0) + error(Eio); + if(md->type == Tagend){ + if(next + Dlen < offset) + panic("devtinyfs1"); + break; + } + next += Dlen; + if(next > offset) + panic("devtinyfs1"); + bno = GETS(md->bno); + } + + /* point to new blocks */ + if(offset == 0){ + /* first block in a file */ + f->dbno = rock.dbno; + writedir(rock.fs, f); + } else { + /* updating a current block */ + i = last - offset; + if(i > 0){ + p -= i; + memmove(md->data + used, p, i); + used += i; + } + writedata(rock.fs, bno, rock.dbno, md->data, used, last == n+offset); + } + f->length += n; + + /* update finger */ + if(fbno != Notabno){ + f->finger = finger; + f->fbno = fbno; + } + poperror(); + qunlock(&rock.fs->ql); + + return n; +} + +Dev tinyfsdevtab = { + 'F', + "tinyfs", + + devreset, + tinyfsinit, + devshutdown, + tinyfsattach, + tinyfswalk, + tinyfsstat, + tinyfsopen, + tinyfscreate, + tinyfsclose, + tinyfsread, + devbread, + tinyfswrite, + devbwrite, + tinyfsremove, + devwstat, +}; diff --git a/os/port/devtk.c b/os/port/devtk.c new file mode 100644 index 00000000..61afd667 --- /dev/null +++ b/os/port/devtk.c @@ -0,0 +1,180 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include <interp.h> + +#include "draw.h" + +enum{ + Qdir, + Qtkevents +}; + +static +Dirtab tkdirtab[]={ + {".", {Qdir,0,QTDIR}, 0, 0500}, + {"tkevents", {Qtkevents, 0}, 0, 0600}, +}; + +static struct { + QLock l; + Queue* eq; + Ref inuse; +} tkevents; + +static void +tkwiretapper(void *top, char *cmd, char *result, void *image, Rectangle *rp) +{ + Block *b; + int n; + char *s, *e; + + n = 12; + if(cmd != nil) + n += strlen(cmd)+2+1; + if(result != nil) + n += strlen(result)+2+1; + if(image != nil) + n += 12; + if(rp != nil) + n += 4*20; + n++; + b = allocb(n); + if(b != nil){ + s = (char*)b->wp; + e = s+n; + s += snprint(s, e-s, "%p", top); + if(cmd != nil){ + *s++ = ' '; + *s++ = '['; + n = strlen(cmd); + memmove(s, cmd, n); + s += n; + *s++ = ']'; + } + /* ignore result for now */ + if(image != nil) + s += snprint(s, e-s, " %p", image); + if(rp != nil) + s += snprint(s, e-s, " %d %d %d %d", rp->min.x, rp->min.y, rp->max.x, rp->max.y); + *s++ = '\n'; + b->wp = (uchar*)s; + release(); + qlock(&tkevents.l); + if(waserror()){ + qunlock(&tkevents.l); + acquire(); + return; + } + if(tkevents.eq != nil) + qbwrite(tkevents.eq, b); + poperror(); + qunlock(&tkevents.l); + acquire(); + } +} + +void (*tkwiretap)(void*, char*, char*, void*, Rectangle*); + +static Chan* +tkattach(char* spec) +{ + return devattach(L'τ', spec); +} + +static Walkqid* +tkwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, tkdirtab, nelem(tkdirtab), devgen); +} + +static int +tkstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, tkdirtab, nelem(tkdirtab), devgen); +} + +static Chan* +tkopen(Chan* c, int omode) +{ + if(c->qid.type & QTDIR) + return devopen(c, omode, tkdirtab, nelem(tkdirtab), devgen); + switch((ulong)c->qid.path){ + case Qtkevents: + c = devopen(c, omode, tkdirtab, nelem(tkdirtab), devgen); + qlock(&tkevents.l); + if(incref(&tkevents.inuse) != 1){ + qunlock(&tkevents.l); + error(Einuse); + } + if(tkevents.eq == nil) + tkevents.eq = qopen(256*1024, 0, nil, nil); + else + qreopen(tkevents.eq); + tkwiretap = tkwiretapper; + qunlock(&tkevents.l); + break; + } + return c; +} + +static void +tkclose(Chan* c) +{ + if(c->qid.type & QTDIR || (c->flag & COPEN) == 0) + return; + qlock(&tkevents.l); + if(decref(&tkevents.inuse) == 0){ + tkwiretap = nil; + qclose(tkevents.eq); + } + qunlock(&tkevents.l); +} + +static long +tkread(Chan* c, void* a, long n, vlong offset) +{ + USED(offset); + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, tkdirtab, nelem(tkdirtab), devgen); + case Qtkevents: + return qread(tkevents.eq, a, n); + default: + n=0; + break; + } + return n; +} + +static long +tkwrite(Chan*, void*, long, vlong) +{ + error(Ebadusefd); + return 0; +} + +Dev tkdevtab = { + L'τ', + "tk", + + devreset, + devinit, + devshutdown, + tkattach, + tkwalk, + tkstat, + tkopen, + devcreate, + tkclose, + tkread, + devbread, + tkwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devuart.c b/os/port/devuart.c new file mode 100644 index 00000000..52e1841f --- /dev/null +++ b/os/port/devuart.c @@ -0,0 +1,748 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "../port/netif.h" + +#include "../port/uart.h" + +enum +{ + /* soft flow control chars */ + CTLS= 023, + CTLQ= 021, +}; + +extern Dev uartdevtab; +extern PhysUart* physuart[]; + +static Uart* uartlist; +static Uart** uart; +static int uartnuart; +static Dirtab *uartdir; +static int uartndir; + +struct Uartalloc { + Lock; + Uart *elist; /* list of enabled interfaces */ +} uartalloc; + +static void uartclock(void); +static void uartflow(void*); + +/* + * enable/disable uart and add/remove to list of enabled uarts + */ +static Uart* +uartenable(Uart *p) +{ + Uart **l; + + if(p->iq == nil){ + if((p->iq = qopen(4*1024, 0, uartflow, p)) == nil) + return nil; + } + else + qreopen(p->iq); + if(p->oq == nil){ + if((p->oq = qopen(4*1024, 0, uartkick, p)) == nil){ + qfree(p->iq); + p->iq = nil; + return nil; + } + } + else + qreopen(p->oq); + + p->ir = p->istage; + p->iw = p->istage; + p->ie = &p->istage[Stagesize]; + p->op = p->ostage; + p->oe = p->ostage; + + p->hup_dsr = p->hup_dcd = 0; + p->dsr = p->dcd = 0; + + /* assume we can send */ + p->cts = 1; + p->ctsbackoff = 0; + + if(p->bits == 0) + uartctl(p, "l8"); + if(p->stop == 0) + uartctl(p, "s1"); + if(p->parity == 0) + uartctl(p, "pn"); + if(p->baud == 0) + uartctl(p, "b9600"); + (*p->phys->enable)(p, 1); + + lock(&uartalloc); + for(l = &uartalloc.elist; *l; l = &(*l)->elist){ + if(*l == p) + break; + } + if(*l == 0){ + p->elist = uartalloc.elist; + uartalloc.elist = p; + } + p->enabled = 1; + unlock(&uartalloc); + + return p; +} + +static void +uartdisable(Uart *p) +{ + Uart **l; + + (*p->phys->disable)(p); + + lock(&uartalloc); + for(l = &uartalloc.elist; *l; l = &(*l)->elist){ + if(*l == p){ + *l = p->elist; + break; + } + } + p->enabled = 0; + unlock(&uartalloc); +} + +void +uartmouse(Uart* p, int (*putc)(Queue*, int), int setb1200) +{ + qlock(p); + if(p->opens++ == 0 && uartenable(p) == nil){ + qunlock(p); + error(Enodev); + } + if(setb1200) + uartctl(p, "b1200"); + p->putc = putc; + p->special = 1; + qunlock(p); +} + +void +uartsetmouseputc(Uart* p, int (*putc)(Queue*, int)) +{ + qlock(p); + if(p->opens == 0 || p->special == 0){ + qunlock(p); + error(Enodev); + } + p->putc = putc; + qunlock(p); +} + +static void +setlength(int i) +{ + Uart *p; + + if(i > 0){ + p = uart[i]; + if(p && p->opens && p->iq) + uartdir[1+3*i].length = qlen(p->iq); + } else for(i = 0; i < uartnuart; i++){ + p = uart[i]; + if(p && p->opens && p->iq) + uartdir[1+3*i].length = qlen(p->iq); + } +} + +/* + * set up the '#t' directory + */ +static void +uartreset(void) +{ + int i; + Dirtab *dp; + Uart *p, *tail; + + tail = nil; + for(i = 0; physuart[i] != nil; i++){ + if(physuart[i]->pnp == nil) + continue; + if((p = physuart[i]->pnp()) == nil) + continue; + if(uartlist != nil) + tail->next = p; + else + uartlist = p; + for(tail = p; tail->next != nil; tail = tail->next) + uartnuart++; + uartnuart++; + } + + if(uartnuart) + uart = xalloc(uartnuart*sizeof(Uart*)); + + uartndir = 1 + 3*uartnuart; + uartdir = xalloc(uartndir * sizeof(Dirtab)); + dp = uartdir; + strcpy(dp->name, "."); + mkqid(&dp->qid, 0, 0, QTDIR); + dp->length = 0; + dp->perm = DMDIR|0555; + dp++; + p = uartlist; + for(i = 0; i < uartnuart; i++){ + /* 3 directory entries per port */ + sprint(dp->name, "eia%d", i); + dp->qid.path = NETQID(i, Ndataqid); + dp->perm = 0660; + dp++; + sprint(dp->name, "eia%dctl", i); + dp->qid.path = NETQID(i, Nctlqid); + dp->perm = 0660; + dp++; + sprint(dp->name, "eia%dstatus", i); + dp->qid.path = NETQID(i, Nstatqid); + dp->perm = 0444; + dp++; + + uart[i] = p; + p->dev = i; + if(p->console || p->special){ + if(uartenable(p) != nil){ + if(p->console){ + kbdq = p->iq; + printq = p->oq; + p->putc = kbdcr2nl; + } + p->opens++; + } + } + p = p->next; + } + + if(uartnuart){ + /* + * at 115200 baud, the 1024 char buffer takes 56 ms to process, + * processing it every 22 ms should be fine + */ + addclock0link(uartclock, 22); + } +} + + +static Chan* +uartattach(char *spec) +{ + return devattach('t', spec); +} + +static Walkqid* +uartwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, uartdir, uartndir, devgen); +} + +static int +uartstat(Chan *c, uchar *dp, int n) +{ + if(NETTYPE(c->qid.path) == Ndataqid) + setlength(NETID(c->qid.path)); + return devstat(c, dp, n, uartdir, uartndir, devgen); +} + +static Chan* +uartopen(Chan *c, int omode) +{ + Uart *p; + + c = devopen(c, omode, uartdir, uartndir, devgen); + + switch(NETTYPE(c->qid.path)){ + case Nctlqid: + case Ndataqid: + p = uart[NETID(c->qid.path)]; + qlock(p); + if(p->opens++ == 0 && uartenable(p) == nil){ + qunlock(p); + c->flag &= ~COPEN; + error(Enodev); + } + qunlock(p); + break; + } + + c->iounit = qiomaxatomic; + return c; +} + +static int +uartdrained(void* arg) +{ + Uart *p; + + p = arg; + return qlen(p->oq) == 0 && p->op == p->oe; +} + +static void +uartdrainoutput(Uart *p) +{ + if(!p->enabled) + return; + + p->drain = 1; + if(waserror()){ + p->drain = 0; + nexterror(); + } + sleep(&p->r, uartdrained, p); + poperror(); +} + +static void +uartclose(Chan *c) +{ + Uart *p; + + if(c->qid.type & QTDIR) + return; + if((c->flag & COPEN) == 0) + return; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + case Nctlqid: + p = uart[NETID(c->qid.path)]; + qlock(p); + if(--(p->opens) == 0){ + qclose(p->iq); + p->ir = p->iw = p->istage; + + /* + */ + qhangup(p->oq, nil); + if(!waserror()){ + uartdrainoutput(p); + poperror(); + } + qclose(p->oq); + uartdisable(p); + p->dcd = p->dsr = p->dohup = 0; + } + qunlock(p); + break; + } +} + +static long +uartread(Chan *c, void *buf, long n, vlong off) +{ + Uart *p; + ulong offset = off; + + if(c->qid.type & QTDIR){ + setlength(-1); + return devdirread(c, buf, n, uartdir, uartndir, devgen); + } + + p = uart[NETID(c->qid.path)]; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + return qread(p->iq, buf, n); + case Nctlqid: + return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE); + case Nstatqid: + return (*p->phys->status)(p, buf, n, offset); + } + + return 0; +} + +int +uartctl(Uart *p, char *cmd) +{ + char *f[16]; + int i, n, nf; + + nf = tokenize(cmd, f, nelem(f)); + for(i = 0; i < nf; i++){ + if(strncmp(f[i], "break", 5) == 0){ + (*p->phys->dobreak)(p, 0); + continue; + } + + n = atoi(f[i]+1); + switch(*f[i]){ + case 'B': + case 'b': + uartdrainoutput(p); + if((*p->phys->baud)(p, n) < 0) + return -1; + break; + case 'C': + case 'c': + p->hup_dcd = n; + break; + case 'D': + case 'd': + uartdrainoutput(p); + (*p->phys->dtr)(p, n); + break; + case 'E': + case 'e': + p->hup_dsr = n; + break; + case 'f': + case 'F': + if(p->oq != nil) + qflush(p->oq); + break; + case 'H': + case 'h': + if(p->iq != nil) + qhangup(p->iq, 0); + if(p->oq != nil) + qhangup(p->oq, 0); + break; + case 'i': + case 'I': + uartdrainoutput(p); + (*p->phys->fifo)(p, n); + break; + case 'K': + case 'k': + uartdrainoutput(p); + (*p->phys->dobreak)(p, n); + break; + case 'L': + case 'l': + uartdrainoutput(p); + if((*p->phys->bits)(p, n) < 0) + return -1; + break; + case 'm': + case 'M': + uartdrainoutput(p); + (*p->phys->modemctl)(p, n); + break; + case 'n': + case 'N': + if(p->oq != nil) + qnoblock(p->oq, n); + break; + case 'P': + case 'p': + uartdrainoutput(p); + if((*p->phys->parity)(p, *(f[i]+1)) < 0) + return -1; + break; + case 'Q': + case 'q': + if(p->iq != nil) + qsetlimit(p->iq, n); + if(p->oq != nil) + qsetlimit(p->oq, n); + break; + case 'R': + case 'r': + uartdrainoutput(p); + (*p->phys->rts)(p, n); + break; + case 'S': + case 's': + uartdrainoutput(p); + if((*p->phys->stop)(p, n) < 0) + return -1; + break; + case 'T': + case 't': + p->dcdts = n; + break; + case 'W': + case 'w': + /* obsolete */ + break; + case 'X': + case 'x': + if(p->enabled){ + ilock(&p->tlock); + p->xonoff = n; + iunlock(&p->tlock); + } + break; + } + } + return 0; +} + +static long +uartwrite(Chan *c, void *buf, long n, vlong) +{ + Uart *p; + char *cmd; + + if(c->qid.type & QTDIR) + error(Eperm); + + p = uart[NETID(c->qid.path)]; + + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + qlock(p); + if(waserror()){ + qunlock(p); + nexterror(); + } + + n = qwrite(p->oq, buf, n); + + qunlock(p); + poperror(); + break; + case Nctlqid: + cmd = malloc(n+1); + memmove(cmd, buf, n); + cmd[n] = 0; + qlock(p); + if(waserror()){ + qunlock(p); + free(cmd); + nexterror(); + } + + /* let output drain */ + if(uartctl(p, cmd) < 0) + error(Ebadarg); + + qunlock(p); + poperror(); + free(cmd); + break; + } + + return n; +} + +static int +uartwstat(Chan *c, uchar *dp, int n) +{ + Dir d; + Dirtab *dt; + + if(!iseve()) + error(Eperm); + if(QTDIR & c->qid.type) + error(Eperm); + if(NETTYPE(c->qid.path) == Nstatqid) + error(Eperm); + + dt = &uartdir[1 + 3 * NETID(c->qid.path)]; + n = convM2D(dp, n, &d, nil); + if(n == 0) + error(Eshortstat); + if(d.mode != ~0UL) + dt[0].perm = dt[1].perm = d.mode; + return n; +} + +void +uartpower(int on) +{ + Uart *p; + + for(p = uartlist; p != nil; p = p->next) { + if(p->phys->power) + (*p->phys->power)(p, on); + } +} + +Dev uartdevtab = { + 't', + "uart", + + uartreset, + devinit, + devshutdown, + uartattach, + uartwalk, + uartstat, + uartopen, + devcreate, + uartclose, + uartread, + devbread, + uartwrite, + devbwrite, + devremove, + uartwstat, + uartpower, +}; + +/* + * restart input if it's off + */ +static void +uartflow(void *v) +{ + Uart *p; + + p = v; + if(p->modem) + (*p->phys->rts)(p, 1); +} + +/* + * put some bytes into the local queue to avoid calling + * qconsume for every character + */ +int +uartstageoutput(Uart *p) +{ + int n; + + n = qconsume(p->oq, p->ostage, Stagesize); + if(n <= 0) + return 0; + p->op = p->ostage; + p->oe = p->ostage + n; + return n; +} + +/* + * restart output + */ +void +uartkick(void *v) +{ + Uart *p = v; + + if(p->blocked) + return; + + ilock(&p->tlock); + (*p->phys->kick)(p); + iunlock(&p->tlock); + + if(p->drain && uartdrained(p)){ + p->drain = 0; + wakeup(&p->r); + } +} + +/* + * receive a character at interrupt time + */ +void +uartrecv(Uart *p, char ch) +{ + uchar *next; + + /* software flow control */ + if(p->xonoff){ + if(ch == CTLS){ + p->blocked = 1; + }else if(ch == CTLQ){ + p->blocked = 0; + p->ctsbackoff = 2; /* clock gets output going again */ + } + } + + /* receive the character */ + if(p->putc) + p->putc(p->iq, ch); + else{ + next = p->iw + 1; + if(next == p->ie) + next = p->istage; + if(next != p->ir){ + *p->iw = ch; + p->iw = next; + } + } +} + +/* + * we save up input characters till clock time to reduce + * per character interrupt overhead. + */ +static void +uartclock(void) +{ + Uart *p; + uchar *iw; + + for(p = uartalloc.elist; p; p = p->elist){ + + /* this amortizes cost of qproduce to many chars */ + if(p->iw != p->ir){ + iw = p->iw; + if(iw < p->ir){ + if(qproduce(p->iq, p->ir, p->ie-p->ir) < 0) + (*p->phys->rts)(p, 0); + p->ir = p->istage; + } + if(iw > p->ir) + if(qproduce(p->iq, p->ir, iw-p->ir) < 0) + (*p->phys->rts)(p, 0); + p->ir = iw; + } + + /* hang up if requested */ + if(p->dohup){ + qhangup(p->iq, 0); + qhangup(p->oq, 0); + p->dohup = 0; + } + + /* this adds hysteresis to hardware/software flow control */ + if(p->ctsbackoff){ + ilock(&p->tlock); + if(p->ctsbackoff){ + if(--(p->ctsbackoff) == 0) + (*p->phys->kick)(p); + } + iunlock(&p->tlock); + } + } +} + +/* + * polling console input, output + */ + +Uart* consuart; + +int +uartgetc(void) +{ + if(consuart == nil || consuart->phys->getc == nil) + return -1; + return consuart->phys->getc(consuart); +} + +void +uartputc(int c) +{ + if(consuart == nil || consuart->phys->putc == nil) + return; + consuart->phys->putc(consuart, c); +} + +void +uartputs(char *s, int n) +{ + char *e; + + if(consuart == nil || consuart->phys->putc == nil) + return; + + e = s+n; + for(; s<e; s++){ + if(*s == '\n') + consuart->phys->putc(consuart, '\r'); + consuart->phys->putc(consuart, *s); + } +} diff --git a/os/port/dial.c b/os/port/dial.c new file mode 100644 index 00000000..d07671dc --- /dev/null +++ b/os/port/dial.c @@ -0,0 +1,417 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "kernel.h" + +typedef struct DS DS; + +static int call(char*, char*, DS*); +static int csdial(DS*); +static void _dial_string_parse(char*, DS*); +static int nettrans(char*, char*, int na, char*, int); + +enum +{ + Maxstring= 128, + Maxpath= 100 +}; + +struct DS +{ + char buf[Maxstring]; /* dist string */ + char *netdir; + char *proto; + char *rem; + char *local; /* other args */ + char *dir; + int *cfdp; +}; + +/* + * the dialstring is of the form '[/net/]proto!dest' + */ +int +kdial(char *dest, char *local, char *dir, int *cfdp) +{ + DS ds; + int rv; + char err[ERRMAX], alterr[ERRMAX]; + + ds.local = local; + ds.dir = dir; + ds.cfdp = cfdp; + + _dial_string_parse(dest, &ds); + if(ds.netdir) + return csdial(&ds); + + ds.netdir = "/net"; + rv = csdial(&ds); + if(rv >= 0) + return rv; + + err[0] = 0; + kerrstr(err, sizeof err); + if(strstr(err, "refused") != 0){ + kerrstr(err, sizeof err); + return rv; + } + + ds.netdir = "/net.alt"; + rv = csdial(&ds); + if(rv >= 0) + return rv; + + alterr[0] = 0; + kerrstr(alterr, sizeof err); + + if(strstr(alterr, "translate") || strstr(alterr, "does not exist")) + kerrstr(err, sizeof err); + else + kerrstr(alterr, sizeof alterr); + return rv; +} + +static int +csdial(DS *ds) +{ + int n, fd, rv; + char *p, buf[Maxstring], clone[Maxpath], err[ERRMAX], besterr[ERRMAX]; + + /* + * open connection server + */ + snprint(buf, sizeof(buf), "%s/cs", ds->netdir); + fd = kopen(buf, ORDWR); + if(fd < 0){ + /* no connection server, don't translate */ + snprint(clone, sizeof(clone), "%s/%s/clone", ds->netdir, ds->proto); + return call(clone, ds->rem, ds); + } + + /* + * ask connection server to translate + */ + sprint(buf, "%s!%s", ds->proto, ds->rem); + if(kwrite(fd, buf, strlen(buf)) < 0){ + kerrstr(err, sizeof err); + kclose(fd); + kwerrstr("%s (%s)", err, buf); + return -1; + } + + /* + * loop through each address from the connection server till + * we get one that works. + */ + *besterr = 0; + strcpy(err, Egreg); + rv = -1; + kseek(fd, 0, 0); + while((n = kread(fd, buf, sizeof(buf) - 1)) > 0){ + buf[n] = 0; + p = strchr(buf, ' '); + if(p == 0) + continue; + *p++ = 0; + rv = call(buf, p, ds); + if(rv >= 0) + break; + err[0] = 0; + kerrstr(err, sizeof err); + if(strstr(err, "does not exist") == 0) + memmove(besterr, err, sizeof besterr); + } + kclose(fd); + + if(rv < 0 && *besterr) + kerrstr(besterr, sizeof besterr); + else + kerrstr(err, sizeof err); + return rv; +} + +static int +call(char *clone, char *dest, DS *ds) +{ + int fd, cfd, n; + char name[Maxpath], data[Maxpath], err[ERRMAX], *p; + + cfd = kopen(clone, ORDWR); + if(cfd < 0){ + kerrstr(err, sizeof err); + kwerrstr("%s (%s)", err, clone); + return -1; + } + + /* get directory name */ + n = kread(cfd, name, sizeof(name)-1); + if(n < 0){ + kerrstr(err, sizeof err); + kclose(cfd); + kwerrstr("read %s: %s", clone, err); + return -1; + } + name[n] = 0; + for(p = name; *p == ' '; p++) + ; + sprint(name, "%ld", strtoul(p, 0, 0)); + p = strrchr(clone, '/'); + *p = 0; + if(ds->dir) + snprint(ds->dir, NETPATHLEN, "%s/%s", clone, name); + snprint(data, sizeof(data), "%s/%s/data", clone, name); + + /* connect */ + if(ds->local) + snprint(name, sizeof(name), "connect %s %s", dest, ds->local); + else + snprint(name, sizeof(name), "connect %s", dest); + if(kwrite(cfd, name, strlen(name)) < 0){ + err[0] = 0; + kerrstr(err, sizeof err); + kclose(cfd); + kwerrstr("%s (%s)", err, name); + return -1; + } + + /* open data connection */ + fd = kopen(data, ORDWR); + if(fd < 0){ + err[0] = 0; + kerrstr(err, sizeof err); + kwerrstr("%s (%s)", err, data); + kclose(cfd); + return -1; + } + if(ds->cfdp) + *ds->cfdp = cfd; + else + kclose(cfd); + + return fd; +} + +/* + * parse a dial string + */ +static void +_dial_string_parse(char *str, DS *ds) +{ + char *p, *p2; + + strncpy(ds->buf, str, Maxstring); + ds->buf[Maxstring-1] = 0; + + p = strchr(ds->buf, '!'); + if(p == 0) { + ds->netdir = 0; + ds->proto = "net"; + ds->rem = ds->buf; + } else { + if(*ds->buf != '/' && *ds->buf != '#'){ + ds->netdir = 0; + ds->proto = ds->buf; + } else { + for(p2 = p; *p2 != '/'; p2--) + ; + *p2++ = 0; + ds->netdir = ds->buf; + ds->proto = p2; + } + *p = 0; + ds->rem = p + 1; + } +} + +/* + * announce a network service. + */ +int +kannounce(char *addr, char *dir) +{ + int ctl, n, m; + char buf[NETPATHLEN]; + char buf2[Maxpath]; + char netdir[NETPATHLEN]; + char naddr[Maxpath]; + char *cp; + + /* + * translate the address + */ + if(nettrans(addr, naddr, sizeof(naddr), netdir, sizeof(netdir)) < 0) + return -1; + + /* + * get a control channel + */ + ctl = kopen(netdir, ORDWR); + if(ctl<0) + return -1; + cp = strrchr(netdir, '/'); + *cp = 0; + + /* + * find out which line we have + */ + n = sprint(buf, "%.*s/", sizeof buf, netdir); + m = kread(ctl, &buf[n], sizeof(buf)-n-1); + if(m <= 0){ + kclose(ctl); + return -1; + } + buf[n+m] = 0; + + /* + * make the call + */ + n = snprint(buf2, sizeof buf2, "announce %s", naddr); + if(kwrite(ctl, buf2, n)!=n){ + kclose(ctl); + return -1; + } + + /* + * return directory etc. + */ + if(dir) + strcpy(dir, buf); + return ctl; +} + +/* + * listen for an incoming call + */ +int +klisten(char *dir, char *newdir) +{ + int ctl, n, m; + char buf[NETPATHLEN]; + char *cp; + + /* + * open listen, wait for a call + */ + snprint(buf, sizeof buf, "%s/listen", dir); + ctl = kopen(buf, ORDWR); + if(ctl < 0) + return -1; + + /* + * find out which line we have + */ + strcpy(buf, dir); + cp = strrchr(buf, '/'); + *++cp = 0; + n = cp-buf; + m = kread(ctl, cp, sizeof(buf) - n - 1); + if(m <= 0){ + kclose(ctl); + return -1; + } + buf[n+m] = 0; + + /* + * return directory etc. + */ + if(newdir) + strcpy(newdir, buf); + return ctl; + +} + +/* + * perform the identity translation (in case we can't reach cs) + */ +static int +identtrans(char *netdir, char *addr, char *naddr, int na, char *file, int nf) +{ + char proto[Maxpath]; + char *p; + + USED(nf); + + /* parse the protocol */ + strncpy(proto, addr, sizeof(proto)); + proto[sizeof(proto)-1] = 0; + p = strchr(proto, '!'); + if(p) + *p++ = 0; + + snprint(file, nf, "%s/%s/clone", netdir, proto); + strncpy(naddr, p, na); + naddr[na-1] = 0; + + return 1; +} + +/* + * call up the connection server and get a translation + */ +static int +nettrans(char *addr, char *naddr, int na, char *file, int nf) +{ + int i, fd; + char buf[Maxpath]; + char netdir[NETPATHLEN]; + char *p, *p2; + long n; + + /* + * parse, get network directory + */ + p = strchr(addr, '!'); + if(p == 0){ + kwerrstr("bad dial string: %s", addr); + return -1; + } + if(*addr != '/'){ + strcpy(netdir, "/net"); + } else { + for(p2 = p; *p2 != '/'; p2--) + ; + i = p2 - addr; + if(i == 0 || i >= sizeof(netdir)){ + kwerrstr("bad dial string: %s", addr); + return -1; + } + strncpy(netdir, addr, i); + netdir[i] = 0; + addr = p2 + 1; + } + + /* + * ask the connection server + */ + sprint(buf, "%s/cs", netdir); + fd = kopen(buf, ORDWR); + if(fd < 0) + return identtrans(netdir, addr, naddr, na, file, nf); + if(kwrite(fd, addr, strlen(addr)) < 0){ + kclose(fd); + return -1; + } + kseek(fd, 0, 0); + n = kread(fd, buf, sizeof(buf)-1); + kclose(fd); + if(n <= 0) + return -1; + buf[n] = 0; + + /* + * parse the reply + */ + p = strchr(buf, ' '); + if(p == 0) + return -1; + *p++ = 0; + strncpy(naddr, p, na); + naddr[na-1] = 0; + strncpy(file, buf, nf); + file[nf-1] = 0; + return 0; +} diff --git a/os/port/dis.c b/os/port/dis.c new file mode 100644 index 00000000..2b5ac937 --- /dev/null +++ b/os/port/dis.c @@ -0,0 +1,1125 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include <isa.h> +#include <interp.h> +#include <kernel.h> +#include "raise.h" + + void vmachine(void*); + +struct +{ + Lock l; + Prog* runhd; + Prog* runtl; + Prog* head; + Prog* tail; + Rendez irend; + int idle; + int nyield; + int creating; + Proc* vmq; /* queue of procs wanting vm */ + Proc* vmqt; + Proc* idlevmq; /* queue of procs wanting work */ + Atidle* idletasks; +} isched; + +int bflag; +int cflag; +uvlong gcbusy; +uvlong gcidle; +uvlong gcidlepass; +uvlong gcpartial; +int keepbroken = 1; +static Prog* proghash[64]; + +static Progs* delgrp(Prog*); +static void addgrp(Prog*, Prog*); +void printgrp(Prog*, char*); + +static Prog** +pidlook(int pid) +{ + ulong h; + Prog **l; + + h = (ulong)pid % nelem(proghash); + for(l = &proghash[h]; *l != nil && (*l)->pid != pid; l = &(*l)->pidlink) + ; + return l; +} + +int +tready(void *a) +{ + USED(a); + return isched.runhd != nil || isched.vmq != nil; +} + +Prog* +progpid(int pid) +{ + return *pidlook(pid); +} + +Prog* +progn(int n) +{ + Prog *p; + + for(p = isched.head; p && n--; p = p->next) + ; + return p; +} + +int +nprog(void) +{ + int n; + Prog *p; + + n = 0; + for(p = isched.head; p; p = p->next) + n++; + return n; +} + +static void +accountant(void) +{ + Prog *p; + + p = isched.runhd; + if(p != nil) + p->ticks++; +} + +static void +execatidle(void) +{ + int done; + + if(tready(nil)) + return; + + gcidle++; + up->type = IdleGC; + up->iprog = nil; + addrun(up->prog); + + done = gccolor+3; + while(gccolor < done && gcruns()) { + if(isched.vmq != nil || isched.runhd != isched.runtl) { + gcpartial++; + break; + } + rungc(isched.head); + gcidlepass++; + sched(); + } + + up->type = Interp; + delrunq(up->prog); +} + +Prog* +newprog(Prog *p, Modlink *m) +{ + Heap *h; + Prog *n, **ph; + Osenv *on, *op; + static int pidnum; + + n = malloc(sizeof(Prog)+sizeof(Osenv)); + if(n == 0){ + if(p == nil) + panic("no memory"); + else + error(exNomem); + } + + n->pid = ++pidnum; + if(n->pid <= 0) + panic("no pids"); + n->group = nil; + + if(isched.tail != nil) { + n->prev = isched.tail; + isched.tail->next = n; + } + else { + isched.head = n; + n->prev = nil; + } + isched.tail = n; + + ph = pidlook(n->pid); + if(*ph != nil) + panic("dup pid"); + n->pidlink = nil; + *ph = n; + + n->osenv = (Osenv*)((uchar*)n + sizeof(Prog)); + n->xec = xec; + n->quanta = PQUANTA; + n->flags = 0; + n->exval = H; + + h = D2H(m); + h->ref++; + Setmark(h); + n->R.M = m; + n->R.MP = m->MP; + if(m->MP != H) + Setmark(D2H(m->MP)); + addrun(n); + + if(p == nil){ + newgrp(n); + return n; + } + + addgrp(n, p); + n->flags = p->flags; + if(p->flags & Prestrict) + n->flags |= Prestricted; + memmove(n->osenv, p->osenv, sizeof(Osenv)); + op = p->osenv; + on = n->osenv; + on->waitq = op->childq; + on->childq = nil; + on->debug = nil; + incref(on->pgrp); + incref(on->fgrp); + if(on->egrp != nil) + incref(on->egrp); + if(on->sigs != nil) + incref(on->sigs); + on->user = nil; + kstrdup(&on->user, op->user); + on->errstr = on->errbuf0; + on->syserrstr = on->errbuf1; + + return n; +} + +void +delprog(Prog *p, char *msg) +{ + Osenv *o; + Prog **ph; + + tellsomeone(p, msg); /* call before being removed from prog list */ + + o = p->osenv; + release(); + closepgrp(o->pgrp); + closefgrp(o->fgrp); + closeegrp(o->egrp); + closesigs(o->sigs); + acquire(); + + delgrp(p); + + if(p->prev) + p->prev->next = p->next; + else + isched.head = p->next; + + if(p->next) + p->next->prev = p->prev; + else + isched.tail = p->prev; + + ph = pidlook(p->pid); + if(*ph == nil) + panic("lost pid"); + *ph = p->pidlink; + + if(p == isched.runhd) { + isched.runhd = p->link; + if(p->link == nil) + isched.runtl = nil; + } + p->state = 0xdeadbeef; + free(o->user); + free(p->killstr); + free(p->exstr); + free(p); +} + +void +renameproguser(char *old, char *new) +{ + Prog *p; + Osenv *o; + + acquire(); + for(p = isched.head; p; p = p->next){ + o = p->osenv; + if(o->user != nil && strcmp(o->user, old) == 0) + kstrdup(&o->user, new); + } + release(); +} + +void +tellsomeone(Prog *p, char *buf) +{ + Osenv *o; + + if(waserror()) + return; + o = p->osenv; + if(o->childq != nil) + qproduce(o->childq, buf, strlen(buf)); + if(o->waitq != nil) + qproduce(o->waitq, buf, strlen(buf)); + poperror(); +} + +static void +swiprog(Prog *p) +{ + Proc *q, *eq; + + q = proctab(0); + for(eq = q+conf.nproc; q < eq; q++) { + if(q->iprog == p) { + swiproc(q, 1); + return; + } + } + /*print("didn't find\n");*/ +} + +static Prog* +grpleader(Prog *p) +{ + Progs *g; + Prog *l; + + g = p->group; + if(g != nil && (l = g->head) != nil && l->pid == g->id) + return l; + return nil; +} + +int +exprog(Prog *p, char *exc) +{ + switch(p->state) { + case Palt: + altdone(p->R.s, p, nil, -1); + break; + case Psend: + cqdelp(&p->chan->send, p); + break; + case Precv: + cqdelp(&p->chan->recv, p); + break; + case Pready: + break; + case Prelease: + swiprog(p); + break; + case Pexiting: + case Pbroken: + case Pdebug: + return 0; + default: + panic("exprog - bad state 0x%x\n", p->state); + } + if(p->state != Pready && p->state != Prelease) + addrun(p); + if(p->kill == nil){ + if(p->killstr == nil){ + p->killstr = malloc(ERRMAX); + if(p->killstr == nil){ + p->kill = Enomem; + return 1; + } + } + kstrcpy(p->killstr, exc, ERRMAX); + p->kill = p->killstr; + } + return 1; +} + +static void +propex(Prog *p, char *estr) +{ + Prog *f, *nf, *pgl; + + if(!(p->flags & (Ppropagate|Pnotifyleader)) || p->group == nil) + return; + if(*estr == 0){ + if((p->flags & Pkilled) == 0) + return; + estr = "killed"; + } + pgl = grpleader(p); + if(pgl == nil) + pgl = p; + if(!(pgl->flags & (Ppropagate|Pnotifyleader))) + return; /* exceptions are local; don't propagate */ + for(f = p->group->head; f != nil; f = nf){ + nf = f->grpnext; + if(f != p && f != pgl){ + if(pgl->flags & Ppropagate) + exprog(f, estr); + else{ + f->flags &= ~(Ppropagate|Pnotifyleader); /* prevent recursion */ + killprog(f, "killed"); + } + } + } + if(p != pgl) + exprog(pgl, estr); +} + +int +killprog(Prog *p, char *cause) +{ + Osenv *env; + char msg[ERRMAX+2*KNAMELEN]; + + if(p == isched.runhd) { + p->kill = ""; + p->flags |= Pkilled; + p->state = Pexiting; + return 0; + } + + switch(p->state) { + case Palt: + altdone(p->R.s, p, nil, -1); + break; + case Psend: + cqdelp(&p->chan->send, p); + break; + case Precv: + cqdelp(&p->chan->recv, p); + break; + case Pready: + delrunq(p); + break; + case Prelease: + p->kill = ""; + p->flags |= Pkilled; + p->state = Pexiting; + swiprog(p); + /* No break */ + case Pexiting: + return 0; + case Pbroken: + case Pdebug: + break; + default: + panic("killprog - bad state 0x%x\n", p->state); + } + + if(p->addrun != nil) { + p->kill = ""; + p->flags |= Pkilled; + p->addrun(p); + p->addrun = nil; + return 0; + } + + env = p->osenv; + if(env->debug != nil) { + p->state = Pbroken; + dbgexit(p, 0, cause); + return 0; + } + + propex(p, "killed"); + + snprint(msg, sizeof(msg), "%d \"%s\":%s", p->pid, p->R.M->m->name, cause); + + p->state = Pexiting; + gclock(); + destroystack(&p->R); + delprog(p, msg); + gcunlock(); + + return 1; +} + +void +newgrp(Prog *p) +{ + Progs *pg, *g; + + if(p->group != nil && p->group->id == p->pid) + return; + g = malloc(sizeof(*g)); + if(g == nil) + error(Enomem); + p->flags &= ~(Ppropagate|Pnotifyleader); + g->id = p->pid; + g->flags = 0; + g->child = nil; + pg = delgrp(p); + g->head = g->tail = p; + p->group = g; + if(pg != nil){ + g->sib = pg->child; + pg->child = g; + } + g->parent = pg; +} + +static void +addgrp(Prog *n, Prog *p) +{ + Progs *g; + + n->group = p->group; + if((g = n->group) != nil){ + n->grpnext = nil; + if(g->head != nil){ + n->grpprev = g->tail; + g->tail->grpnext = n; + }else{ + n->grpprev = nil; + g->head = n; + } + g->tail = n; + } +} + +static Progs* +delgrp(Prog *p) +{ + Progs *g, *pg, *cg, **l; + + g = p->group; + if(g == nil) + return nil; + if(p->grpprev) + p->grpprev->grpnext = p->grpnext; + else + g->head = p->grpnext; + if(p->grpnext) + p->grpnext->grpprev = p->grpprev; + else + g->tail = p->grpprev; + p->grpprev = p->grpnext = nil; + p->group = nil; + + if(g->head == nil){ + /* move up, giving subgroups of groups with no Progs to their parents */ + do{ + if((pg = g->parent) != nil){ + pg = g->parent; + for(l = &pg->child; *l != nil && *l != g; l = &(*l)->sib) + ; + *l = g->sib; + } + /* put subgroups in new parent group */ + while((cg = g->child) != nil){ + g->child = cg->sib; + cg->parent = pg; + if(pg != nil){ + cg->sib = pg->child; + pg->child = cg; + } + } + free(g); + }while((g = pg) != nil && g->head == nil); + } + return g; +} + +void +printgrp(Prog *p, char *v) +{ + Progs *g; + Prog *q; + + g = p->group; + print("%s pid %d grp %d pgrp %d: [pid", v, p->pid, g->id, g->parent!=nil?g->parent->id:0); + for(q = g->head; q != nil; q = q->grpnext) + print(" %d", q->pid); + print(" subgrp"); + for(g = g->child; g != nil; g = g->sib) + print(" %d", g->id); + print("]\n"); +} + +int +killgrp(Prog *p, char *msg) +{ + int i, npid, *pids; + Prog *f; + Progs *g; + + /* interpreter has been acquired */ + g = p->group; + if(g == nil || g->head == nil) + return 0; + npid = 0; + for(f = g->head; f != nil; f = f->grpnext) + if(f->group != g) + panic("killgrp"); + else + npid++; + /* use pids not Prog* because state can change during killprog */ + pids = malloc(npid*sizeof(int)); + if(pids == nil) + error(Enomem); + npid = 0; + for(f = g->head; f != nil; f = f->grpnext) + pids[npid++] = f->pid; + if(waserror()) { + free(pids); + nexterror(); + } + for(i = 0; i < npid; i++) { + f = progpid(pids[i]); + if(f != nil && f != currun()) + killprog(f, msg); + } + poperror(); + free(pids); + return 1; +} + +char changup[] = "channel hangup"; + +void +killcomm(Progq **q) +{ + Prog *p; + Progq *f; + + for (f = *q; f != nil; f = *q) { + *q = f->next; + p = f->prog; + free(f); + if(p == nil) + return; + p->ptr = nil; + switch(p->state) { + case Prelease: + swiprog(p); + break; + case Psend: + case Precv: + p->kill = changup; + addrun(p); + break; + case Palt: + altgone(p); + break; + } + } +} + +void +addprog(Proc *p) +{ + Prog *n; + + if((n = p->prog) == nil) { + n = malloc(sizeof(Prog)); + if(n == nil) + panic("no memory"); + p->prog = n; + } else + memset(n, 0, sizeof(Prog)); + n->osenv = p->env; +} + +static void +cwakeme(Prog *p) +{ + Osenv *o; + + p->addrun = nil; + o = p->osenv; + wakeup(o->rend); +} + +static int +cdone(void *vp) +{ + Prog *p = vp; + + return p->addrun == nil || p->kill != nil; +} + +void +cblock(Prog *p) +{ + Osenv *o; + + p->addrun = cwakeme; + o = p->osenv; + o->rend = &up->sleep; + release(); + + /* + * To allow cdone(p) safely after release, + * p must be currun before the release. + * Exits in the error case with the vm acquired. + */ + if(waserror()) { + acquire(); + p->addrun = nil; + nexterror(); + } + sleep(o->rend, cdone, p); + if (p->kill != nil) + error(Eintr); + poperror(); + acquire(); +} + +void +addrun(Prog *p) +{ + if(p->addrun != 0) { + p->addrun(p); + return; + } + if(p->state == Pready && p != (Prog *)up->prog) + panic("addrun of ready prog %8.8p by %8.8lux\n", p, getcallerpc(&p)); + p->state = Pready; + p->link = nil; + if(isched.runhd == nil) + isched.runhd = p; + else + isched.runtl->link = p; + + isched.runtl = p; +} + +Prog* +delrun(int state) +{ + Prog *p; + + p = isched.runhd; + p->state = state; + isched.runhd = p->link; + if(p->link == nil) + isched.runtl = nil; + + return p; +} + +void +delrunq(Prog *p) +{ + Prog *prev, *f; + + prev = nil; + for(f = isched.runhd; f; f = f->link) { + if(f == p) + break; + prev = f; + } + if(f == nil) + return; + if(prev == nil) + isched.runhd = p->link; + else + prev->link = p->link; + if(p == isched.runtl) + isched.runtl = prev; +} + +Prog* +delruntail(int state) +{ + Prog *p; + + p = isched.runtl; + delrunq(isched.runtl); + p->state = state; + return p; +} + +Prog* +currun(void) +{ + return isched.runhd; +} + +Prog* +schedmod(Module *m) +{ + Heap *h; + Type *t; + Prog *p; + Modlink *ml; + Frame f, *fp; + + ml = mklinkmod(m, 0); + + if(m->origmp != H && m->ntype > 0) { + t = m->type[0]; + h = nheap(t->size); + h->t = t; + t->ref++; + ml->MP = H2D(uchar*, h); + newmp(ml->MP, m->origmp, t); + } + + p = newprog(nil, ml); + h = D2H(ml); + h->ref--; + p->R.PC = m->entry; + fp = &f; + R.s = &fp; + f.t = m->entryt; + newstack(p); + initmem(m->entryt, p->R.FP); + + return p; +} + +void +acquire(void) +{ + int empty; + Prog *p; + + lock(&isched.l); + if(isched.idle) { + isched.idle = 0; + unlock(&isched.l); + } + else { + up->qnext = nil; + if(isched.vmq != nil){ + empty = 0; + isched.vmqt->qnext = up; + }else{ + isched.vmq = up; + empty = 1; + } + isched.vmqt = up; + + up->state = Queueing; + up->pc = getcallerpc(&empty); + unlock(&isched.l); + if(empty) + wakeup(&isched.irend); + sched(); + } + + if(up->type == Interp) { + p = up->iprog; + up->iprog = nil; + irestore(p); + } + else + p = up->prog; + + p->state = Pready; + p->link = isched.runhd; + isched.runhd = p; + if(p->link == nil) + isched.runtl = p; +} + +void +release(void) +{ + Proc *p, **pq; + int f; + + if(up->type == Interp){ + if(up->iprog != nil) + panic("Double release (Interp)?"); + up->iprog = isave(); + }else{ + if(((Prog *)up->prog)->state != Pready) panic("double release (GC)?"); + delrun(Prelease); + } + + lock(&isched.l); + if(*(pq = &isched.vmq) == nil && *(pq = &isched.idlevmq) == nil) { + isched.idle = 1; + f = isched.creating; + isched.creating = 1; + unlock(&isched.l); + if(f == 0) + kproc("dis", vmachine, nil, 0); + return; + } + p = *pq; + *pq = p->qnext; + unlock(&isched.l); + + ready(p); +} + +void +iyield(void) +{ + Proc *p; + + lock(&isched.l); + p = isched.vmq; + if(p == nil) { + unlock(&isched.l); + return; + } + isched.nyield++; + isched.vmq = p->qnext; + + if(up->iprog != nil) + panic("iyield but iprog, type %d", up->type); + if(up->type != Interp){ + static int once; + if(!once++) + print("tell charles: #%p->type==%d\n", up, up->type); + } + up->qnext = isched.idlevmq; + isched.idlevmq = up; + + up->state = Queueing; + up->pc = getcallerpc(&p); + unlock(&isched.l); + ready(p); + sched(); +} + +void +startup(void) +{ + int x; + + up->type = Interp; + up->iprog = nil; + + lock(&isched.l); + isched.creating = 0; + if(isched.idle) { + isched.idle = 0; + unlock(&isched.l); + return; + } + up->qnext = isched.idlevmq; + isched.idlevmq = up; + up->state = Queueing; + up->pc = getcallerpc(&x); + unlock(&isched.l); + sched(); +} + +void +progexit(void) +{ + Prog *r; + Module *m; + int broken; + char *estr, msg[ERRMAX+2*KNAMELEN]; + + estr = up->env->errstr; + broken = 0; + if(estr[0] != '\0' && strcmp(estr, Eintr) != 0 && strncmp(estr, "fail:", 5) != 0) + broken = 1; + + r = up->iprog; + if(r != nil) + acquire(); + else + r = currun(); + + if(*estr == '\0' && r->flags & Pkilled) + estr = "killed"; + + m = R.M->m; + if(broken) + print("[%s] Broken: \"%s\"\n", m->name, estr); + + snprint(msg, sizeof(msg), "%d \"%s\":%s", r->pid, m->name, estr); + + if(up->env->debug != nil) { + dbgexit(r, broken, estr); + broken = 1; + /* must force it to break if in debug */ + }else if(broken && (!keepbroken || strncmp(estr, "out of memory", 13)==0 || memusehigh())) + broken = 0; /* don't want them or short of memory */ + + if(broken){ + tellsomeone(r, msg); + r = isave(); + r->state = Pbroken; + return; + } + + gclock(); + destroystack(&R); + delprog(r, msg); + gcunlock(); +} + +void +disfault(void *reg, char *msg) +{ + Prog *p; + + USED(reg); + + if(strncmp(msg, Eintr, 6) == 0 || up == nil) { + print("EMU: faults: %s\n", msg); + panic("disfault"); + } + if(up->type != Interp) { + print("SYS: process %s faults: %s\n", up->text, msg); + panic("disfault"); + } + + if(up->iprog != nil) + acquire(); + + p = currun(); + if(p == nil) + panic("Interp faults with no dis prog"); + + /* cause an exception in the dis prog. */ + error(msg); +} + +void +vmachine(void*) +{ + Prog *r; + Osenv *o; + int cycles; + + startup(); + + while(waserror()) { + if(up->type != Interp) + panic("vmachine: non-interp kproc"); + if(up->iprog != nil) + acquire(); + if(handler(up->env->errstr) == 0) { + propex(currun(), up->env->errstr); + progexit(); + } + up->env = &up->defenv; + } + + cycles = 0; + for(;;) { + if(tready(nil) == 0) { + execatidle(); + sleep(&isched.irend, tready, 0); + } + + if(isched.vmq != nil && (isched.runhd == nil || ++cycles > 2)){ + iyield(); + cycles = 0; + } + + r = isched.runhd; + if(r != nil) { + o = r->osenv; + up->env = o; + + FPrestore(&o->fpu); + r->xec(r); + FPsave(&o->fpu); + + if(isched.runhd != nil) + if(r == isched.runhd) + if(isched.runhd != isched.runtl) { + isched.runhd = r->link; + r->link = nil; + isched.runtl->link = r; + isched.runtl = r; + } + up->env = &up->defenv; + if(isched.runhd != nil) + if (up->iprog == nil) { + up->type = BusyGC; + pushrun(up->prog); + rungc(isched.head); + up->type = Interp; + delrunq(up->prog); + } else + print("up->iprog not nil (%lux)\n", up->iprog); + } + } +} + +void +disinit(void *a) +{ + Prog *p; + Osenv *o; + Module *root; + char *initmod = a; + + if(waserror()) + panic("disinit error: %r"); + + print("Initial Dis: \"%s\"\n", initmod); + + fmtinstall('D', Dconv); + + addclock0link(accountant, MS2HZ); + + FPinit(); + FPsave(&up->env->fpu); + + opinit(); + modinit(); + excinit(); + + root = load(initmod); + if(root == 0) { + kgerrstr(up->genbuf, sizeof up->genbuf); + panic("loading \"%s\": %s", initmod, up->genbuf); + } + + p = schedmod(root); + + memmove(p->osenv, up->env, sizeof(Osenv)); + o = p->osenv; + incref(o->pgrp); + incref(o->fgrp); + if(o->egrp != nil) + incref(o->egrp); + if(o->sigs != nil) + incref(o->sigs); + o->user = nil; + kstrdup(&o->user, up->env->user); + o->errstr = o->errbuf0; + o->syserrstr = o->errbuf1; + + isched.idle = 1; + + if(kopen("#c/cons", OREAD) != 0) + panic("failed to make fd0 from #c/cons"); + kopen("#c/cons", OWRITE); + kopen("#c/cons", OWRITE); + + poperror(); + vmachine(nil); +} + +void +pushrun(Prog *p) +{ + if(p->addrun != nil) + panic("pushrun addrun"); + p->state = Pready; + p->link = isched.runhd; + isched.runhd = p; + if(p->link == nil) + isched.runtl = p; +} diff --git a/os/port/discall.c b/os/port/discall.c new file mode 100644 index 00000000..c5db5d79 --- /dev/null +++ b/os/port/discall.c @@ -0,0 +1,185 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include <isa.h> +#include <interp.h> +#include "kernel.h" + +#define QP(l) (Prog**)((char*)(l)+sizeof(QLock)) + +void* +libqlowner(void *l) +{ + return *QP(l); +} + +void +libqlock(void *l) +{ + Prog *p; + QLock *q; + + q = l; + p = currun(); + if(p == nil) + panic("libqlock"); + + if(!canqlock(q)) { + release(); + qlock(q); + acquire(); + } + *QP(l) = p; +} + +void +libqunlock(void *l) +{ + Prog *p; + QLock *q; + + q = l; + p = currun(); + if(p == nil) + panic("libqunlock 1"); + if(*QP(l) != p) + panic("libqunlock 2"); + + *QP(l) = nil; + qunlock(q); +} + +void* +libqlalloc(void) +{ + QLock *q; + + q = mallocz(sizeof(QLock)+sizeof(Prog*), 1); + return q; +} + +void +libqlfree(void *l) +{ + free(l); +} + +vlong +libseek(int fd, vlong off, int whence) +{ + release(); + off = kseek(fd, off, whence); + acquire(); + return off; +} + +int +libread(int fd, void *buf, int n) +{ + release(); + n = kread(fd, buf, n); + acquire(); + return n; +} + +int +libreadn(int fd, void *av, long n) +{ + char *a; + long m, t; + + a = av; + t = 0; + release(); + while(t < n){ + m = kread(fd, a+t, n-t); + if(m <= 0){ + if(t == 0){ + acquire(); + return m; + } + break; + } + t += m; + } + acquire(); + return t; +} + +int +libwrite(int fd, void *buf, int n) +{ + release(); + n = kwrite(fd, buf, n); + acquire(); + return n; +} + +int +libopen(char *name, int omode) +{ + int fd; + + release(); + fd = kopen(name, omode); + acquire(); + return fd; +} + +int +libclose(int fd) +{ + release(); + fd = kclose(fd); + acquire(); + return fd; +} + +Dir* +libdirfstat(int fd) +{ + Dir *d; + + release(); + d = kdirfstat(fd); + acquire(); + return d; +} + +int +libbind(char *s, char *t, int f) +{ + int n; + + release(); + n = kbind(s, t, f); + acquire(); + return n; +} + +void +libchanclose(void *chan) +{ + release(); + cclose(chan); + acquire(); +} + +void* +libfdtochan(int fd, int mode) +{ + Chan *c; + + release(); + if(waserror()) { + acquire(); + return nil; + } + c = fdtochan(up->env->fgrp, fd, mode, 0, 1); + poperror(); + acquire(); + return c; +} diff --git a/os/port/dynld.c b/os/port/dynld.c new file mode 100644 index 00000000..77f74230 --- /dev/null +++ b/os/port/dynld.c @@ -0,0 +1,75 @@ +#include "u.h" +#include "../port/lib.h" +#include "../port/error.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include <a.out.h> +#include <dynld.h> +#include <kernel.h> + +/* + * kernel interface to dynld, for use by devdynld.c, + * libinterp/dlm.c, and possibly others + */ + +typedef struct Fd Fd; +struct Fd { + int fd; +}; + +static long +readfd(void *a, void *buf, long nbytes) +{ + return kread(((Fd*)a)->fd, buf, nbytes); +} + +static vlong +seekfd(void *a, vlong off, int t) +{ + return kseek(((Fd*)a)->fd, off, t); +} + +static void +errfd(char *s) +{ + kstrcpy(up->env->errstr, s, ERRMAX); +} + +Dynobj* +kdynloadfd(int fd, Dynsym *tab, int ntab) +{ + Fd f; + + f.fd = fd; + return dynloadgen(&f, readfd, seekfd, errfd, tab, ntab, 0); +} + +int +kdynloadable(int fd) +{ + Fd f; + + f.fd = fd; + return dynloadable(&f, readfd, seekfd); +} + +/* auxiliary routines for dynamic loading of C modules */ + +Dynobj* +dynld(int fd) +{ + Fd f; + + f.fd = fd; + return dynloadgen(&f, readfd, seekfd, errfd, _exporttab, dyntabsize(_exporttab), 0); +} + +int +dynldable(int fd) +{ + Fd f; + + f.fd = fd; + return dynloadable(&f, readfd, seekfd); +} diff --git a/os/port/edf.c b/os/port/edf.c new file mode 100644 index 00000000..c406f4cc --- /dev/null +++ b/os/port/edf.c @@ -0,0 +1,626 @@ +/* EDF scheduling */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "../port/edf.h" +#include <trace.h> + +/* debugging */ +int edfprint = 0; +#define DPRINT if(edfprint)print + +static vlong now; +extern ulong delayedscheds; +extern Schedq runq[Nrq]; +extern int nrdy; +extern ulong runvec; + +/* Statistics stuff */ +ulong nilcount; +ulong scheds; +vlong edfruntime; +ulong edfnrun; +int misseddeadlines; + +/* Edfschedlock protects modification of admission params */ +int edfinited; +QLock edfschedlock; +static Lock thelock; + +enum{ + Dl, /* Invariant for schedulability test: Dl < Rl */ + Rl, +}; + +static char *testschedulability(Proc*); +static Proc *qschedulability; + +enum { + Onemicrosecond = 1000ULL, + Onemillisecond = 1000000ULL, + Onesecond = 1000000000ULL, + OneRound = Onemillisecond/2LL, + MilliRound = Onemicrosecond/2LL, +}; + +static int +timeconv(Fmt *f) +{ + char buf[128], *sign; + vlong t; + + buf[0] = 0; + switch(f->r) { + case 'U': + t = va_arg(f->args, uvlong); + break; + case 't': // vlong in nanoseconds + t = va_arg(f->args, vlong); + break; + default: + return fmtstrcpy(f, "(timeconv)"); + } + if (t < 0) { + sign = "-"; + t = -t; + } + else + sign = ""; + if (t > Onesecond){ + t += OneRound; + sprint(buf, "%s%d.%.3ds", sign, (int)(t / Onesecond), (int)(t % Onesecond)/1000000); + }else if (t > Onemillisecond){ + t += MilliRound; + sprint(buf, "%s%d.%.3dms", sign, (int)(t / Onemillisecond), (int)(t % Onemillisecond)/1000); + }else if (t > Onemicrosecond) + sprint(buf, "%s%d.%.3dµs", sign, (int)(t / Onemicrosecond), (int)(t % Onemicrosecond)); + else + sprint(buf, "%s%dns", sign, (int)t); + return fmtstrcpy(f, buf); +} + +Edf* +edflock(Proc *p) +{ + Edf *e; + + if (p->edf == nil) + return nil; + ilock(&thelock); + if ((e = p->edf) && (e->flags & Admitted)){ + now = todget(nil); + return e; + } + iunlock(&thelock); + return nil; +} + +void +edfunlock(void) +{ + edfruntime += todget(nil) - now; + edfnrun++; + iunlock(&thelock); +} + +void +edfinit(Proc*p) +{ + if(!edfinited){ + fmtinstall('t', timeconv); + edfinited++; + } + now = todget(nil); + DPRINT("%t edfinit %lud[%s]\n", now, p->pid, statename[p->state]); + p->edf = malloc(sizeof(Edf)); + if(p->edf == nil) + error(Enomem); + return; +} + +static void +deadlineintr(Ureg*, Timer *t) +{ + /* Proc reached deadline */ + extern int panicking; + Proc *p; + void (*pt)(Proc*, int, vlong); + + if(panicking || active.exiting) + return; + + p = t->ta; + + DPRINT("%t deadlineintr %lud[%s]\n", todget(nil), p->pid, statename[p->state]); + /* If we're interrupting something other than the proc pointed to by t->a, + * we've already achieved recheduling, so we need not do anything + * Otherwise, we must cause a reschedule, but if we call sched() + * here directly, the timer interrupt routine will not finish its business + * Instead, we cause the resched to happen when the interrupted proc + * returns to user space + */ + if (p == up){ + pt = proctrace; + if(up->trace && pt) + pt(up, SInts, 0); + up->delaysched++; + delayedscheds++; + } +} + +static void +release(Proc *p) +{ + /* Called with edflock held */ + Edf *e; + void (*pt)(Proc*, int, vlong); + + e = p->edf; + e->flags &= ~Yield; + if (e->d < now){ + e->periods++; + e->r = now; + if ((e->flags & Sporadic) == 0){ + /* Non sporadic processes stay true to their period; + * calculate next release time + */ + while(e->t <= now) + e->t += e->T; + }else{ + /* Sporadic processes may not be released earlier than + * one period after this release + */ + e->t = e->r + e->T; + } + e->d = e->r + e->D; + e->S = e->C; + DPRINT("%t release %lud[%s], r=%t, d=%t, t=%t, S=%t\n", + now, p->pid, statename[p->state], e->r, e->d, e->t, e->S); + if (pt = proctrace){ + pt(p, SRelease, e->r); + pt(p, SDeadline, e->d); + } + }else{ + DPRINT("%t release %lud[%s], too late t=%t, called from 0x%lux\n", + now, p->pid, statename[p->state], e->t, getcallerpc(&p)); + } +} + +static void +releaseintr(Ureg*, Timer *t) +{ + Proc *p; + extern int panicking; + Schedq *rq; + + if(panicking || active.exiting) + return; + + p = t->ta; + if((edflock(p)) == nil) + return; + DPRINT("%t releaseintr %lud[%s]\n", now, p->pid, statename[p->state]); + switch(p->state){ + default: + edfunlock(); + return; + case Ready: + /* remove proc from current runq */ + rq = &runq[p->pri]; + if (dequeueproc(rq, p) != p){ + DPRINT("releaseintr: can't find proc or lock race\n"); + release(p); /* It'll start best effort */ + edfunlock(); + return; + } + p->state = Waitrelease; + /* fall through */ + case Waitrelease: + release(p); + edfunlock(); + ready(p); + if (up){ + up->delaysched++; + delayedscheds++; + } + return; + case Running: + release(p); + edfrun(p, 1); + break; + case Wakeme: + release(p); + edfunlock(); + if (p->trend) + wakeup(p->trend); + p->trend = nil; + if (up){ + up->delaysched++; + delayedscheds++; + } + return; + } + edfunlock(); +} + +void +edfrecord(Proc *p) +{ + vlong used; + Edf *e; + void (*pt)(Proc*, int, vlong); + + if((e = edflock(p)) == nil) + return; + used = now - e->s; + if (e->d <= now) + e->edfused += used; + else + e->extraused += used; + if (e->S > 0){ + if (e->S <= used){ + if(pt = proctrace) + pt(p, SSlice, now); + DPRINT("%t edfrecord slice used up\n", now); + e->d = now; + e->S = 0; + }else + e->S -= used; + } + e->s = now; + edfunlock(); +} + +void +edfrun(Proc *p, int edfpri) +{ + Edf *e; + void (*pt)(Proc*, int, vlong); + + e = p->edf; + /* Called with edflock held */ + if(edfpri){ + if (e->d <= now || e->S == 0){ + /* Deadline reached or resources exhausted, + * deschedule forthwith + */ + p->delaysched++; + delayedscheds++; + e->s = now; + return; + } + e->tns = now + e->S; + if (e->d < e->tns) + e->tns = e->d; + if(e->tt == nil || e->tf != deadlineintr){ + DPRINT("%t edfrun, deadline=%t\n", now, e->tns); + }else{ + DPRINT("v"); + } + if(p->trace && (pt = proctrace)) + pt(p, SInte, e->tns); + e->tmode = Tabsolute; + e->tf = deadlineintr; + e->ta = p; + timeradd(e); + }else{ + DPRINT("<"); + } + e->s = now; +} + +char * +edfadmit(Proc *p) +{ + char *err; + Edf *e; + int i; + Proc *r; + void (*pt)(Proc*, int, vlong); + + e = p->edf; + if (e->flags & Admitted) + return "task state"; /* should never happen */ + + /* simple sanity checks */ + if (e->T == 0) + return "T not set"; + if (e->C == 0) + return "C not set"; + if (e->D > e->T) + return "D > T"; + if (e->D == 0) /* if D is not set, set it to T */ + e->D = e->T; + if (e->C > e->D) + return "C > D"; + + qlock(&edfschedlock); + if (err = testschedulability(p)){ + qunlock(&edfschedlock); + return err; + } + e->flags |= Admitted; + + edflock(p); + + if(pt = proctrace) + pt(p, SAdmit, now); + + /* Look for another proc with the same period to synchronize to */ + SET(r); + for(i=0; i<conf.nproc; i++) { + r = proctab(i); + if(r->state == Dead || r == p) + continue; + if (r->edf == nil || (r->edf->flags & Admitted) == 0) + continue; + if (r->edf->T == e->T) + break; + } + if (i == conf.nproc){ + /* Can't synchronize to another proc, release now */ + e->t = now; + e->d = 0; + release(p); + if (p == up){ + DPRINT("%t edfadmit self %lud[%s], release now: r=%t d=%t t=%t\n", + now, p->pid, statename[p->state], e->r, e->d, e->t); + /* We're already running */ + edfrun(p, 1); + }else{ + /* We're releasing another proc */ + DPRINT("%t edfadmit other %lud[%s], release now: r=%t d=%t t=%t\n", + now, p->pid, statename[p->state], e->r, e->d, e->t); + p->ta = p; + edfunlock(); + qunlock(&edfschedlock); + releaseintr(nil, p); + return nil; + } + }else{ + /* Release in synch to something else */ + e->t = r->edf->t; + if (p == up){ + DPRINT("%t edfadmit self %lud[%s], release at %t\n", + now, p->pid, statename[p->state], e->t); + edfunlock(); + qunlock(&edfschedlock); + return nil; + }else{ + DPRINT("%t edfadmit other %lud[%s], release at %t\n", + now, p->pid, statename[p->state], e->t); + if(e->tt == nil){ + e->tf = releaseintr; + e->ta = p; + e->tns = e->t; + e->tmode = Tabsolute; + timeradd(e); + } + } + } + edfunlock(); + qunlock(&edfschedlock); + return nil; +} + +void +edfstop(Proc *p) +{ + Edf *e; + void (*pt)(Proc*, int, vlong); + + if (e = edflock(p)){ + DPRINT("%t edfstop %lud[%s]\n", now, p->pid, statename[p->state]); + if(pt = proctrace) + pt(p, SExpel, now); + e->flags &= ~Admitted; + if (e->tt) + timerdel(e); + edfunlock(); + } +} + +static int +yfn(void *) +{ + return up->trend == nil || todget(nil) >= up->edf->r; +} + +void +edfyield(void) +{ + /* sleep until next release */ + Edf *e; + void (*pt)(Proc*, int, vlong); + + if((e = edflock(up)) == nil) + return; + if(pt = proctrace) + pt(up, SYield, now); + while(e->t < now) + e->t += e->T; + e->r = e->t; + e->flags |= Yield; + e->d = now; + if (up->tt == nil){ + up->tns = e->t; + up->tf = releaseintr; + up->tmode = Tabsolute; + up->ta = up; + up->trend = &up->sleep; + timeradd(up); + }else if(up->tf != releaseintr) + print("edfyield: surprise! 0x%lux\n", up->tf); + edfunlock(); + sleep(&up->sleep, yfn, nil); +} + +int +edfready(Proc *p) +{ + Edf *e; + Schedq *rq; + Proc *l, *pp; + void (*pt)(Proc*, int, vlong); + + if((e = edflock(p)) == nil) + return 0; + if (e->d <= now){ + /* past deadline, arrange for next release */ + if ((e->flags & Sporadic) == 0){ + /* Non sporadic processes stay true to their period, calculate next release time */ + while(e->t < now) + e->t += e->T; + } + if (now < e->t){ + /* Next release is in the future, schedule it */ + if (e->tt == nil || e->tf != releaseintr){ + e->tns = e->t; + e->tmode = Tabsolute; + e->tf = releaseintr; + e->ta = p; + timeradd(e); + DPRINT("%t edfready %lud[%s], release=%t\n", + now, p->pid, statename[p->state], e->t); + } + if(p->state == Running && (e->flags & (Yield|Yieldonblock)) == 0 && (e->flags & Extratime)){ + /* If we were running, we've overrun our CPU allocation + * or missed the deadline, continue running best-effort at low priority + * Otherwise we were blocked. If we don't yield on block, we continue + * best effort + */ + DPRINT(">"); + p->pri = PriExtra; + edfunlock(); + return 0; /* Stick on runq[PriExtra] */ + } + DPRINT("%t edfready %lud[%s] wait release at %t\n", + now, p->pid, statename[p->state], e->t); + p->state = Waitrelease; + edfunlock(); + return 1; /* Make runnable later */ + } + DPRINT("%t edfready %lud %s release now\n", now, p->pid, statename[p->state]); + /* release now */ + release(p); + } + edfunlock(); + DPRINT("^"); + rq = &runq[PriEdf]; + /* insert in queue in earliest deadline order */ + lock(runq); + l = nil; + for(pp = rq->head; pp; pp = pp->rnext){ + if(pp->edf->d > e->d) + break; + l = pp; + } + p->rnext = pp; + if (l == nil) + rq->head = p; + else + l->rnext = p; + if(pp == nil) + rq->tail = p; + rq->n++; + nrdy++; + runvec |= 1 << PriEdf; + p->pri = PriEdf; + p->readytime = m->ticks; + p->state = Ready; + unlock(runq); + if(pt = proctrace) + pt(p, SReady, now); + return 1; +} + + +static void +testenq(Proc *p) +{ + Proc *xp, **xpp; + Edf *e; + + e = p->edf; + e->testnext = nil; + if (qschedulability == nil) { + qschedulability = p; + return; + } + SET(xp); + for (xpp = &qschedulability; *xpp; xpp = &xp->edf->testnext) { + xp = *xpp; + if (e->testtime < xp->edf->testtime + || (e->testtime == xp->edf->testtime && e->testtype < xp->edf->testtype)){ + e->testnext = xp; + *xpp = p; + return; + } + } + assert(xp->edf->testnext == nil); + xp->edf->testnext = p; +} + +static char * +testschedulability(Proc *theproc) +{ + Proc *p; + vlong H, G, Cb, ticks; + int steps, i; + + /* initialize */ + DPRINT("schedulability test %lud\n", theproc->pid); + qschedulability = nil; + for(i=0; i<conf.nproc; i++) { + p = proctab(i); + if(p->state == Dead) + continue; + if ((p->edf == nil || (p->edf->flags & Admitted) == 0) && p != theproc) + continue; + p->edf->testtype = Rl; + p->edf->testtime = 0; + DPRINT("\tInit: edfenqueue %lud\n", p->pid); + testenq(p); + } + H=0; + G=0; + for(steps = 0; steps < Maxsteps; steps++){ + p = qschedulability; + qschedulability = p->edf->testnext; + ticks = p->edf->testtime; + switch (p->edf->testtype){ + case Dl: + H += p->edf->C; + Cb = 0; + DPRINT("\tStep %3d, Ticks %t, pid %lud, deadline, H += %t → %t, Cb = %t\n", + steps, ticks, p->pid, p->edf->C, H, Cb); + if (H+Cb>ticks){ + DPRINT("not schedulable\n"); + return "not schedulable"; + } + p->edf->testtime += p->edf->T - p->edf->D; + p->edf->testtype = Rl; + testenq(p); + break; + case Rl: + DPRINT("\tStep %3d, Ticks %t, pid %lud, release, G %t, C%t\n", + steps, ticks, p->pid, p->edf->C, G); + if(ticks && G <= ticks){ + DPRINT("schedulable\n"); + return nil; + } + G += p->edf->C; + p->edf->testtime += p->edf->D; + p->edf->testtype = Dl; + testenq(p); + break; + default: + assert(0); + } + } + DPRINT("probably not schedulable\n"); + return "probably not schedulable"; +} diff --git a/os/port/edf.h b/os/port/edf.h new file mode 100644 index 00000000..dfd2b4a9 --- /dev/null +++ b/os/port/edf.h @@ -0,0 +1,53 @@ +enum { + Maxsteps = 200 * 100 * 2, /* 100 periods of 200 procs */ + + /* Edf.flags field */ + Admitted = 0x01, + Sporadic = 0x02, + Yieldonblock = 0x04, + Sendnotes = 0x08, + Deadline = 0x10, + Yield = 0x20, + Extratime = 0x40, + + Infinity = ~0ULL, +}; + +typedef struct Edf Edf; + +struct Edf { + /* time intervals */ + vlong D; /* Deadline */ + vlong Delta; /* Inherited deadline */ + vlong T; /* period */ + vlong C; /* Cost */ + vlong S; /* Slice: time remaining in this period */ + /* times */ + vlong r; /* (this) release time */ + vlong d; /* (this) deadline */ + vlong t; /* Start of next period, t += T at release */ + vlong s; /* Time at which this proc was last scheduled */ + /* for schedulability testing */ + vlong testDelta; + int testtype; /* Release or Deadline */ + vlong testtime; + Proc *testnext; + /* other */ + ushort flags; + Timer; + /* Stats */ + vlong edfused; + vlong extraused; + vlong aged; + ulong periods; + ulong missed; +}; + +extern Lock edftestlock; /* for atomic admitting/expelling */ + +#pragma varargck type "t" vlong +#pragma varargck type "U" uvlong + +/* Interface: */ +Edf* edflock(Proc*); +void edfunlock(void); diff --git a/os/port/error.h b/os/port/error.h new file mode 100644 index 00000000..5d5c2f66 --- /dev/null +++ b/os/port/error.h @@ -0,0 +1,64 @@ +extern char Enoerror[]; /* no error */ +extern char Emount[]; /* inconsistent mount */ +extern char Eunmount[]; /* not mounted */ +extern char Eunion[]; /* not in union */ +extern char Emountrpc[]; /* mount rpc error */ +extern char Eshutdown[]; /* mounted device shut down */ +extern char Enocreate[]; /* mounted directory forbids creation */ +extern char Enonexist[]; /* file does not exist */ +extern char Eexist[]; /* file already exists */ +extern char Ebadsharp[]; /* unknown device in # filename */ +extern char Enotdir[]; /* not a directory */ +extern char Eisdir[]; /* file is a directory */ +extern char Ebadchar[]; /* bad character in file name */ +extern char Efilename[]; /* file name syntax */ +extern char Eperm[]; /* permission denied */ +extern char Ebadusefd[]; /* inappropriate use of fd */ +extern char Ebadarg[]; /* bad arg in system call */ +extern char Einuse[]; /* device or object already in use */ +extern char Eio[]; /* i/o error */ +extern char Etoobig[]; /* read or write too large */ +extern char Etoosmall[]; /* read or write too small */ +extern char Enetaddr[]; /* bad network address */ +extern char Emsgsize[]; /* message is too big for protocol */ +extern char Enetbusy[]; /* network device is busy or allocated */ +extern char Enoproto[]; /* network protocol not supported */ +extern char Enoport[]; /* network port not available */ +extern char Enoifc[]; /* bad interface or no free interface slots */ +extern char Enolisten[]; /* not announced */ +extern char Ehungup[]; /* i/o on hungup channel */ +extern char Ebadctl[]; /* bad process or channel control request */ +extern char Enodev[]; /* no free devices */ +extern char Enoenv[]; /* no free environment resources */ +extern char Emuxshutdown[]; /* mux server shut down */ +extern char Emuxbusy[]; /* all mux channels busy */ +extern char Emuxmsg[]; /* bad mux message format or mismatch */ +extern char Ethread[]; /* thread exited */ +extern char Estopped[]; /* thread must be stopped */ +extern char Enochild[]; /* no living children */ +extern char Eioload[]; /* i/o error in demand load */ +extern char Enovmem[]; /* out of memory: virtual memory */ +extern char Ebadld[]; /* illegal line discipline */ +extern char Ebadfd[]; /* fd out of range or not open */ +extern char Eisstream[]; /* seek on a stream */ +extern char Ebadexec[]; /* exec header invalid */ +extern char Etimedout[]; /* connection timed out */ +extern char Econrefused[]; /* connection refused */ +extern char Econinuse[]; /* connection in use */ +extern char Eintr[]; /* interrupted */ +extern char Eneedservice[]; /* service required for tcp/udp/il calls */ +extern char Enomem[]; /* out of memory: kernel */ +extern char Esfnotcached[]; /* subfont not cached */ +extern char Esoverlap[]; /* segments overlap */ +extern char Emouseset[]; /* mouse type already set */ +extern char Erecover[]; /* failed to recover fd */ +extern char Eshort[]; /* i/o count too small */ +extern char Enobitstore[]; /* out of screen memory */ +extern char Egreg[]; /* jim'll fix it */ +extern char Ebadspec[]; /* bad attach specifier */ +extern char Enoattach[]; /* mount/attach disallowed */ +extern char Eshortstat[]; /* stat buffer too small */ +extern char Enegoff[]; /* negative i/o offset */ +extern char Ecmdargs[]; /* wrong #args in control message */ +extern char Ebadstat[]; /* malformed stat buffer */ +extern char Enofd[]; /* no free file descriptors */ diff --git a/os/port/ethermii.c b/os/port/ethermii.c new file mode 100644 index 00000000..584cdf08 --- /dev/null +++ b/os/port/ethermii.c @@ -0,0 +1,238 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" +#include "ethermii.h" + +int +mii(Mii* mii, int mask) +{ + MiiPhy *miiphy; + int bit, oui, phyno, r, rmask; + + /* + * Probe through mii for PHYs in mask; + * return the mask of those found in the current probe. + * If the PHY has not already been probed, update + * the Mii information. + */ + rmask = 0; + for(phyno = 0; phyno < NMiiPhy; phyno++){ + bit = 1<<phyno; + if(!(mask & bit)) + continue; + if(mii->mask & bit){ + rmask |= bit; + continue; + } + if(mii->mir(mii, phyno, Bmsr) == -1) + continue; + r = mii->mir(mii, phyno, Phyidr1); + oui = (r & 0x3FFF)<<6; + r = mii->mir(mii, phyno, Phyidr2); + oui |= r>>10; + if(oui == 0xFFFFF || oui == 0) + continue; + + if((miiphy = malloc(sizeof(MiiPhy))) == nil) + continue; + + miiphy->mii = mii; + miiphy->oui = oui; + miiphy->phyno = phyno; + + miiphy->anar = ~0; + miiphy->fc = ~0; + miiphy->mscr = ~0; + + mii->phy[phyno] = miiphy; + if(mii->curphy == nil) + mii->curphy = miiphy; + mii->mask |= bit; + mii->nphy++; + + rmask |= bit; + } + return rmask; +} + +int +miimir(Mii* mii, int r) +{ + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + return mii->mir(mii, mii->curphy->phyno, r); +} + +int +miimiw(Mii* mii, int r, int data) +{ + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + return mii->miw(mii, mii->curphy->phyno, r, data); +} + +int +miireset(Mii* mii) +{ + int bmcr; + + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + bmcr = mii->mir(mii, mii->curphy->phyno, Bmcr); + bmcr |= BmcrR; + mii->miw(mii, mii->curphy->phyno, Bmcr, bmcr); +/* microdelay(1);*/ + microdelay(500); /* DP83847, at least */ + + return 0; +} + +int +miiane(Mii* mii, int a, int p, int e) +{ + int anar, bmsr, mscr, r, phyno; + + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + phyno = mii->curphy->phyno; + + bmsr = mii->mir(mii, phyno, Bmsr); + if(!(bmsr & BmsrAna)) + return -1; + + if(a != ~0) + anar = (AnaTXFD|AnaTXHD|Ana10FD|Ana10HD) & a; + else if(mii->curphy->anar != ~0) + anar = mii->curphy->anar; + else{ + anar = mii->mir(mii, phyno, Anar); + anar &= ~(AnaAP|AnaP|AnaT4|AnaTXFD|AnaTXHD|Ana10FD|Ana10HD); + if(bmsr & Bmsr10THD) + anar |= Ana10HD; + if(bmsr & Bmsr10TFD) + anar |= Ana10FD; + if(bmsr & Bmsr100TXHD) + anar |= AnaTXHD; + if(bmsr & Bmsr100TXFD) + anar |= AnaTXFD; + } + mii->curphy->anar = anar; + + if(p != ~0) + anar |= (AnaAP|AnaP) & p; + else if(mii->curphy->fc != ~0) + anar |= mii->curphy->fc; + mii->curphy->fc = (AnaAP|AnaP) & anar; + + if(bmsr & BmsrEs){ + mscr = mii->mir(mii, phyno, Mscr); + mscr &= ~(Mscr1000TFD|Mscr1000THD); + if(e != ~0) + mscr |= (Mscr1000TFD|Mscr1000THD) & e; + else if(mii->curphy->mscr != ~0) + mscr = mii->curphy->mscr; + else{ + r = mii->mir(mii, phyno, Esr); + if(r & Esr1000THD) + mscr |= Mscr1000THD; + if(r & Esr1000TFD) + mscr |= Mscr1000TFD; + } + mii->curphy->mscr = mscr; + mii->miw(mii, phyno, Mscr, mscr); + } + mii->miw(mii, phyno, Anar, anar); + + r = mii->mir(mii, phyno, Bmcr); + if(!(r & BmcrR)){ + r |= BmcrAne|BmcrRan; + mii->miw(mii, phyno, Bmcr, r); + } + + return 0; +} + +int +miistatus(Mii* mii) +{ + MiiPhy *phy; + int anlpar, bmsr, p, r, phyno; + + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + phy = mii->curphy; + phyno = phy->phyno; + + /* + * Check Auto-Negotiation is complete and link is up. + * (Read status twice as the Ls bit is sticky). + */ + bmsr = mii->mir(mii, phyno, Bmsr); + if(!(bmsr & (BmsrAnc|BmsrAna))) +{ +print("miistatus 1\n"); + return -1; +} + + bmsr = mii->mir(mii, phyno, Bmsr); + if(!(bmsr & BmsrLs)){ +print("miistatus 2\n"); + phy->link = 0; + return -1; + } + + phy->speed = phy->fd = phy->rfc = phy->tfc = 0; + if(phy->mscr){ + r = mii->mir(mii, phyno, Mssr); + if((phy->mscr & Mscr1000TFD) && (r & Mssr1000TFD)){ + phy->speed = 1000; + phy->fd = 1; + } + else if((phy->mscr & Mscr1000THD) && (r & Mssr1000THD)) + phy->speed = 1000; + } + + anlpar = mii->mir(mii, phyno, Anlpar); + if(phy->speed == 0){ + r = phy->anar & anlpar; + if(r & AnaTXFD){ + phy->speed = 100; + phy->fd = 1; + } + else if(r & AnaTXHD) + phy->speed = 100; + else if(r & Ana10FD){ + phy->speed = 10; + phy->fd = 1; + } + else if(r & Ana10HD) + phy->speed = 10; + } + if(phy->speed == 0) +{ +print("miistatus 3\n"); + return -1; +} + + if(phy->fd){ + p = phy->fc; + r = anlpar & (AnaAP|AnaP); + if(p == AnaAP && r == (AnaAP|AnaP)) + phy->tfc = 1; + else if(p == (AnaAP|AnaP) && r == AnaAP) + phy->rfc = 1; + else if((p & AnaP) && (r & AnaP)) + phy->rfc = phy->tfc = 1; + } + + phy->link = 1; + + return 0; +} diff --git a/os/port/ethermii.h b/os/port/ethermii.h new file mode 100644 index 00000000..02a45ee5 --- /dev/null +++ b/os/port/ethermii.h @@ -0,0 +1,116 @@ +typedef struct Mii Mii; +typedef struct MiiPhy MiiPhy; + +enum { /* registers */ + Bmcr = 0x00, /* Basic Mode Control */ + Bmsr = 0x01, /* Basic Mode Status */ + Phyidr1 = 0x02, /* PHY Identifier #1 */ + Phyidr2 = 0x03, /* PHY Identifier #2 */ + Anar = 0x04, /* Auto-Negotiation Advertisement */ + Anlpar = 0x05, /* AN Link Partner Ability */ + Aner = 0x06, /* AN Expansion */ + Annptr = 0x07, /* AN Next Page TX */ + Annprr = 0x08, /* AN Next Page RX */ + Mscr = 0x09, /* MASTER-SLAVE Control */ + Mssr = 0x0A, /* MASTER-SLAVE Status */ + Esr = 0x0F, /* Extended Status */ + + NMiiPhyr = 32, + NMiiPhy = 32, +}; + +enum { /* Bmcr */ + BmcrSs1 = 0x0040, /* Speed Select[1] */ + BmcrCte = 0x0080, /* Collision Test Enable */ + BmcrDm = 0x0100, /* Duplex Mode */ + BmcrRan = 0x0200, /* Restart Auto-Negotiation */ + BmcrI = 0x0400, /* Isolate */ + BmcrPd = 0x0800, /* Power Down */ + BmcrAne = 0x1000, /* Auto-Negotiation Enable */ + BmcrSs0 = 0x2000, /* Speed Select[0] */ + BmcrLe = 0x4000, /* Loopback Enable */ + BmcrR = 0x8000, /* Reset */ +}; + +enum { /* Bmsr */ + BmsrEc = 0x0001, /* Extended Capability */ + BmsrJd = 0x0002, /* Jabber Detect */ + BmsrLs = 0x0004, /* Link Status */ + BmsrAna = 0x0008, /* Auto-Negotiation Ability */ + BmsrRf = 0x0010, /* Remote Fault */ + BmsrAnc = 0x0020, /* Auto-Negotiation Complete */ + BmsrPs = 0x0040, /* Preamble Suppression Capable */ + BmsrEs = 0x0100, /* Extended Status */ + Bmsr100T2HD = 0x0200, /* 100BASE-T2 HD Capable */ + Bmsr100T2FD = 0x0400, /* 100BASE-T2 FD Capable */ + Bmsr10THD = 0x0800, /* 10BASE-T HD Capable */ + Bmsr10TFD = 0x1000, /* 10BASE-T FD Capable */ + Bmsr100TXHD = 0x2000, /* 100BASE-TX HD Capable */ + Bmsr100TXFD = 0x4000, /* 100BASE-TX FD Capable */ + Bmsr100T4 = 0x8000, /* 100BASE-T4 Capable */ +}; + +enum { /* Anar/Anlpar */ + Ana10HD = 0x0020, /* Advertise 10BASE-T */ + Ana10FD = 0x0040, /* Advertise 10BASE-T FD */ + AnaTXHD = 0x0080, /* Advertise 100BASE-TX */ + AnaTXFD = 0x0100, /* Advertise 100BASE-TX FD */ + AnaT4 = 0x0200, /* Advertise 100BASE-T4 */ + AnaP = 0x0400, /* Pause */ + AnaAP = 0x0800, /* Asymmetrical Pause */ + AnaRf = 0x2000, /* Remote Fault */ + AnaAck = 0x4000, /* Acknowledge */ + AnaNp = 0x8000, /* Next Page Indication */ +}; + +enum { /* Mscr */ + Mscr1000THD = 0x0100, /* Advertise 1000BASE-T HD */ + Mscr1000TFD = 0x0200, /* Advertise 1000BASE-T FD */ +}; + +enum { /* Mssr */ + Mssr1000THD = 0x0400, /* Link Partner 1000BASE-T HD able */ + Mssr1000TFD = 0x0800, /* Link Partner 1000BASE-T FD able */ +}; + +enum { /* Esr */ + Esr1000THD = 0x1000, /* 1000BASE-T HD Capable */ + Esr1000TFD = 0x2000, /* 1000BASE-T FD Capable */ + Esr1000XHD = 0x4000, /* 1000BASE-X HD Capable */ + Esr1000XFD = 0x8000, /* 1000BASE-X FD Capable */ +}; + +typedef struct Mii { + Lock; + int nphy; + int mask; + MiiPhy* phy[NMiiPhy]; + MiiPhy* curphy; + + void* ctlr; + int (*mir)(Mii*, int, int); + int (*miw)(Mii*, int, int, int); +} Mii; + +typedef struct MiiPhy { + Mii* mii; + int oui; + int phyno; + + int anar; + int fc; + int mscr; + + int link; + int speed; + int fd; + int rfc; + int tfc; +}; + +extern int mii(Mii*, int); +extern int miiane(Mii*, int, int, int); +extern int miimir(Mii*, int); +extern int miimiw(Mii*, int, int); +extern int miireset(Mii*); +extern int miistatus(Mii*); diff --git a/os/port/ethersink.c b/os/port/ethersink.c new file mode 100644 index 00000000..dd701804 --- /dev/null +++ b/os/port/ethersink.c @@ -0,0 +1,65 @@ +/* + * An ethernet /dev/null. + * Useful as a bridging target with ethernet-based VPN. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" +#include "etherif.h" + +static long +ctl(Ether *ether, void *buf, long n) +{ + uchar ea[Eaddrlen]; + Cmdbuf *cb; + + cb = parsecmd(buf, n); + if(cb->nf >= 2 + && strcmp(cb->f[0], "ea")==0 + && parseether(ea, cb->f[1]) == 0){ + free(cb); + memmove(ether->ea, ea, Eaddrlen); + memmove(ether->addr, ether->ea, Eaddrlen); + return 0; + } + free(cb); + error(Ebadctl); + return -1; /* not reached */ +} + +static void +nop(Ether*) +{ +} + +static int +reset(Ether* ether) +{ + uchar ea[Eaddrlen]; + + if(ether->type==nil) + return -1; + memset(ea, 0, sizeof ea); + ether->mbps = 1000; + ether->attach = nop; + ether->transmit = nop; + ether->irq = -1; + ether->interrupt = nil; + ether->ifstat = nil; + ether->ctl = ctl; + ether->promiscuous = nil; + ether->multicast = nil; + ether->arg = ether; + return 0; +} + +void +ethersinklink(void) +{ + addethercard("sink", reset); +} diff --git a/os/port/exception.c b/os/port/exception.c new file mode 100644 index 00000000..84a74711 --- /dev/null +++ b/os/port/exception.c @@ -0,0 +1,216 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "interp.h" +#include "isa.h" +#include "runt.h" +#include "kernel.h" +#include "raise.h" + +static int +ematch(char *pat, char *exp) +{ + int l; + + if(strcmp(pat, exp) == 0) + return 1; + + l = strlen(pat); + if(l == 0) + return 0; + if(pat[l-1] == '*') { + if(l == 1) + return 1; + if(strncmp(pat, exp, l-1) == 0) + return 1; + } + return 0; +} + +static void +setstr(String *s, char *p) +{ + if(s == H) + return; + if(s->len < 0 || s->max < 4) + return; + kstrcpy(s->Sascii, p, s->max); /* TO DO: we are assuming they aren't runes */ + s->len = strlen(s->Sascii); +} + +static String *exstr; + +void +excinit(void) +{ + exstr = newstring(ERRMAX); + poolimmutable(D2H(exstr)); +} + +static String* +newestring(char *estr) +{ + String *s; + + if(waserror()){ + setstr(exstr, estr); + D2H(exstr)->ref++; + return exstr; + } + s = c2string(estr, strlen(estr)); + poperror(); + return s; +} + +#define NOPC 0xffffffff + +#define FRTYPE(f) ((f)->t == nil ? SEXTYPE(f)->reg.TR : (f)->t) + +/* + * clear up an uncalled frame + */ +static void +freeframe(uchar *fp, int setsp) +{ + Frame *f; + + f = (Frame*)fp; + if(f->t == nil) + unextend(f); + else if(f->t->np) + freeptrs(f, f->t); + if(setsp) + R.SP = fp; +} + +int +handler(char *estr) +{ + Prog *p; + Modlink *m, *mr; + int str, ne; + ulong pc, newpc; + long eoff; + uchar *fp, **eadr; + Frame *f; + Type *t, *zt; + Handler *h; + Except *e; + void *v; + + p = currun(); + if(*estr == 0 || p == nil) + return 0; + str = p->exval == H || D2H(p->exval)->t == &Tstring; + m = R.M; + if(m->compiled) + pc = (ulong)R.PC-(ulong)m->prog; + else + pc = R.PC-m->prog; + pc--; + fp = R.FP; + while(fp != nil){ /* look for a handler */ + if((h = m->m->htab) != nil){ + for( ; h->etab != nil; h++){ + if(pc < h->pc1 || pc >= h->pc2) + continue; + eoff = h->eoff; + zt = h->t; + for(e = h->etab, ne = h->ne; e->s != nil; e++, ne--){ + if(ematch(e->s, estr) && (str && ne <= 0 || !str && ne > 0)){ + newpc = e->pc; + goto found; + } + } + newpc = e->pc; + if(newpc != NOPC) + goto found; + } + } + if(!str && fp != R.FP){ /* becomes a string exception in immediate caller */ + v = p->exval; + p->exval = *(String**)v; + D2H(p->exval)->ref++; + destroy(v); + str = 1; + continue; + } + f = (Frame*)fp; + if(f->mr != nil) + m = f->mr; + if(m->compiled) + pc = (ulong)f->lr-(ulong)m->prog; + else + pc = f->lr-m->prog; + pc--; + fp = f->fp; + } + destroy(p->exval); + p->exval = H; + return 0; +found: + { + int n; + char name[3*KNAMELEN]; + + pc = modstatus(&R, name, sizeof(name)); + n = 10+1+strlen(name)+1+strlen(estr)+1; + p->exstr = realloc(p->exstr, n); + if(p->exstr != nil) + snprint(p->exstr, n, "%lud %s %s", pc, name, estr); + } + + /* + * there may be an uncalled frame at the top of the stack + */ + f = (Frame*)R.FP; + t = FRTYPE(f); + if(R.FP < R.EX || R.FP >= R.TS) + freeframe(R.EX+OA(Stkext, reg.tos.fr), 0); + else if(R.FP+t->size < R.SP) + freeframe(R.FP+t->size, 1); + + m = R.M; + while(R.FP != fp){ + f = (Frame*)R.FP; + R.PC = f->lr; + R.FP = f->fp; + R.SP = (uchar*)f; + mr = f->mr; + if(f->t == nil) + unextend(f); + else if(f->t->np) + freeptrs(f, f->t); + if(mr != nil){ + m = mr; + destroy(R.M); + R.M = m; + R.MP = m->MP; + } + } + if(zt != nil){ + freeptrs(fp, zt); + initmem(zt, fp); + } + eadr = (uchar**)(fp+eoff); + destroy(*eadr); + *eadr = H; + if(p->exval == H) + *eadr = (uchar*)newestring(estr); /* might fail */ + else{ + D2H(p->exval)->ref++; + *eadr = p->exval; + } + if(m->compiled) + R.PC = (Inst*)((ulong)m->prog+newpc); + else + R.PC = m->prog+newpc; + memmove(&p->R, &R, sizeof(R)); + p->kill = nil; + destroy(p->exval); + p->exval = H; + return 1; +} diff --git a/os/port/exportfs.c b/os/port/exportfs.c new file mode 100644 index 00000000..354574ef --- /dev/null +++ b/os/port/exportfs.c @@ -0,0 +1,1325 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "kernel.h" + +typedef struct Fid Fid; +typedef struct Export Export; +typedef struct Exq Exq; +typedef struct Uqid Uqid; + +enum +{ + Nfidhash = 32, + Nqidhash = 32, + QIDMASK = ((vlong)1<<48)-1, + MAXFDATA = 8192, + MAXRPCDEF = IOHDRSZ+MAXFDATA, /* initial/default */ + MAXRPCMAX = IOHDRSZ+64*1024, /* most every allowed */ + MSGHDRSZ = BIT32SZ+BIT8SZ+BIT16SZ +}; + +struct Export +{ + Lock; + Ref r; + Exq* work; + Lock fidlock; + Fid* fid[Nfidhash]; + QLock qidlock; + Uqid* qids[Nqidhash]; + ulong pathgen; + Chan* io; + Chan* root; + Pgrp* pgrp; + Egrp* egrp; + Fgrp* fgrp; + int async; + int readonly; + int msize; + char* user; +}; + +struct Fid +{ + Fid* next; + Fid** last; + Chan* chan; + int fid; + int ref; /* fcalls using the fid; locked by Export.Lock */ + vlong offset; /* last offset used (within directory) */ + int attached; /* fid attached or cloned but not clunked */ + Uqid* qid; /* generated qid */ +}; + +struct Uqid +{ + Ref; + int type; + int dev; + vlong oldpath; + vlong newpath; + Uqid* next; +}; + +struct Exq +{ + Lock; + int busy; /* fcall in progress */ + int finished; /* will do no more work on this request or flushes */ + Exq* next; + int shut; /* has been noted for shutdown */ + Exq* flush; /* queued flush requests */ + Exq* flusht; /* tail of flush queue */ + Export* export; + Proc* slave; + Fcall in, out; + uchar* buf; + int bsize; +}; + +struct +{ + Lock l; + QLock qwait; + Rendez rwait; + Exq *head; /* work waiting for a slave */ + Exq *tail; +}exq; + +static void exshutdown(Export*); +static int exflushed(Export*, Exq*); +static void exslave(void*); +static void exfree(Export*); +static void exfreeq(Exq*); +static void exportproc(void*); +static void exreply(Exq*, char*); +static int exisroot(Export*, Chan*); +static Uqid* uqidalloc(Export*, Chan*); +static void freeuqid(Export*, Uqid*); + +static char* Exversion(Export*, Fcall*, Fcall*); +static char* Exauth(Export*, Fcall*, Fcall*); +static char* Exattach(Export*, Fcall*, Fcall*); +static char* Exclunk(Export*, Fcall*, Fcall*); +static char* Excreate(Export*, Fcall*, Fcall*); +static char* Exopen(Export*, Fcall*, Fcall*); +static char* Exread(Export*, Fcall*, Fcall*); +static char* Exremove(Export*, Fcall*, Fcall*); +static char* Exstat(Export*, Fcall*, Fcall*); +static char* Exwalk(Export*, Fcall*, Fcall*); +static char* Exwrite(Export*, Fcall*, Fcall*); +static char* Exwstat(Export*, Fcall*, Fcall*); + +static char *(*fcalls[Tmax])(Export*, Fcall*, Fcall*); + +static char Enofid[] = "no such fid"; +static char Eseekdir[] = "can't seek on a directory"; +static char Eopen[] = "walk of open fid"; +static char Emode[] = "open/create -- unknown mode"; +static char Edupfid[] = "fid in use"; +static char Eaccess[] = "read/write -- not open in suitable mode"; +static char Ecount[] = "read/write -- count too big"; +int exdebug = 0; + +int +export(int fd, char *dir, int async) +{ + Chan *c, *dc; + Pgrp *pg; + Egrp *eg; + Export *fs; + + if(waserror()) + return -1; + c = fdtochan(up->env->fgrp, fd, ORDWR, 1, 1); + poperror(); + + if(waserror()){ + cclose(c); + return -1; + } + dc = namec(dir, Atodir, 0, 0); + poperror(); + + fs = malloc(sizeof(Export)); + if(fs == nil){ + cclose(c); + cclose(dc); + error(Enomem); + } + + fs->r.ref = 1; + pg = up->env->pgrp; + fs->pgrp = pg; + incref(pg); + eg = up->env->egrp; + fs->egrp = eg; + if(eg != nil) + incref(eg); + fs->fgrp = newfgrp(nil); + kstrdup(&fs->user, up->env->user); + fs->root = dc; + fs->io = c; + fs->pathgen = 0; + fs->msize = 0; + c->flag |= CMSG; + fs->async = async; + + if(async){ + if(waserror()) + return -1; + kproc("exportfs", exportproc, fs, 0); + poperror(); + }else + exportproc(fs); + + return 0; +} + +static void +exportinit(void) +{ + lock(&exq.l); + if(fcalls[Tversion] != nil) { + unlock(&exq.l); + return; + } + fcalls[Tversion] = Exversion; + fcalls[Tauth] = Exauth; + fcalls[Tattach] = Exattach; + fcalls[Twalk] = Exwalk; + fcalls[Topen] = Exopen; + fcalls[Tcreate] = Excreate; + fcalls[Tread] = Exread; + fcalls[Twrite] = Exwrite; + fcalls[Tclunk] = Exclunk; + fcalls[Tremove] = Exremove; + fcalls[Tstat] = Exstat; + fcalls[Twstat] = Exwstat; + unlock(&exq.l); +} + +static int +exisroot(Export *fs, Chan *c) +{ + return eqchan(fs->root, c, 1); +} + +static int +exreadn(Chan *c, void *buf, int n) +{ + int nr, t; + + if(waserror()) + return -1; + for(nr = 0; nr < n;){ + t = devtab[c->type]->read(c, (char*)buf+nr, n-nr, 0); + if(t <= 0) + break; + nr += t; + } + poperror(); + return nr; +} + +static int +exreadmsg(Chan *c, void *a, uint n) +{ + int m, len; + uchar *buf; + + buf = a; + m = exreadn(c, buf, BIT32SZ); + if(m < BIT32SZ){ + if(m < 0) + return -1; + return 0; + } + len = GBIT32(buf); + if(len <= BIT32SZ || len > n){ + kwerrstr("bad length in Styx message header"); + return -1; + } + len -= BIT32SZ; + m = exreadn(c, buf+BIT32SZ, len); + if(m < len){ + if(m < 0) + return -1; + return 0; + } + return BIT32SZ+m; +} + +static void +exportproc(void *a) +{ + Exq *q; + int async, msize; + int n, type; + Export *fs = a; + + exportinit(); + + for(;;){ + + msize = fs->msize; + if(msize == 0) + msize = MAXRPCDEF; + for(n=0;; n++){ /* we don't use smalloc, to avoid memset */ + q = mallocz(sizeof(*q)+msize, 0); + if(q != nil || n > 6000) + break; + if(n%600 == 0) + print("exportproc %ld: waiting for memory (%d) for request\n", up->pid, msize); + tsleep(&up->sleep, return0, nil, 100); + } + if(q == nil){ + kwerrstr("out of memory: read request"); + n = -1; + break; + } + memset(q, 0, sizeof(*q)); + q->buf = (uchar*)q + sizeof(*q); + q->bsize = msize; + + n = exreadmsg(fs->io, q->buf, msize); /* TO DO: avoid copy */ + if(n <= 0) + break; + if(convM2S(q->buf, n, &q->in) != n){ + kwerrstr("bad T-message"); + n = -1; + break; + } + type = q->in.type; + if(type < Tversion || type >= Tmax || type&1 || type == Terror){ + kwerrstr("invalid T-message type %d", type); + n = -1; + break; + } + + if(exdebug) + print("export %ld <- %F\n", up->pid, &q->in); + + q->out.type = type+1; + q->out.tag = q->in.tag; + + q->export = fs; + incref(&fs->r); + + if(fs->readonly){ + switch(type){ + case Topen: + if((q->in.mode & (ORCLOSE|OTRUNC|3)) == OREAD) + break; + /* FALL THROUGH */ + case Tcreate: + case Twrite: + case Tremove: + case Twstat: + q->out.type = Rerror; + q->out.ename = "file system read only"; + exreply(q, "exportproc"); + exfreeq(q); + continue; + } + } + + if(q->in.type == Tflush){ + if(exflushed(fs, q)){ + /* not yet started or not found (flush arrived after reply); we reply */ + if(exdebug) + print("export: flush %d\n", q->in.oldtag); + exreply(q, "exportproc"); + exfreeq(q); + } + continue; + } + + lock(&exq.l); + if(exq.head == nil) + exq.head = q; + else + exq.tail->next = q; + q->next = nil; + exq.tail = q; + unlock(&exq.l); + if(exq.qwait.head == nil) + kproc("exslave", exslave, nil, 0); + wakeup(&exq.rwait); + } + + if(exdebug){ + if(n < 0) + print("exportproc %ld shut down: %s\n", up->pid, up->env->errstr); + else + print("exportproc %ld shut down\n", up->pid); + } + + free(q); + exshutdown(fs); + async = fs->async; + exfree(fs); + + if(async) + pexit("mount shut down", 0); +} + +static int +exflushed(Export *fs, Exq *fq) +{ + Exq *q, **last; + ulong pid; + + /* not yet started? */ + lock(&exq.l); + for(last = &exq.head; (q = *last) != nil; last = &q->next) + if(q->export == fs && q->in.tag == fq->in.oldtag){ + *last = q->next; + unlock(&exq.l); + /* not yet started: discard, and Rflush */ + exfreeq(q); + return 1; + } + unlock(&exq.l); + + /* tricky case: in progress */ + lock(fs); + for(q = fs->work; q != nil; q = q->next) + if(q->in.tag == fq->in.oldtag){ + pid = 0; + lock(q); + if(q->finished){ + /* slave replied and emptied its flush queue; we can Rflush now */ + unlock(q); + return 1; + } + /* append to slave's flush queue */ + fq->next = nil; + if(q->flush != nil) + q->flusht->next = fq; + else + q->flush = fq; + q->flusht = fq; + if(q->busy){ + pid = q->slave->pid; + swiproc(q->slave, 0); + } + unlock(q); + unlock(fs); + if(exdebug && pid) + print("export: swiproc %ld to flush %d\n", pid, fq->in.oldtag); + return 0; + } + unlock(fs); + + /* not found */ + return 1; +} + +static void +exfreeq(Exq *q) +{ + Exq *fq; + + while((fq = q->flush) != nil){ + q->flush = fq->next; + exfree(fq->export); + free(fq); + } + exfree(q->export); + free(q); +} + +static void +exshutdown(Export *fs) +{ + Exq *q, **last; + + /* work not started */ + lock(&exq.l); + for(last = &exq.head; (q = *last) != nil;) + if(q->export == fs){ + *last = q->next; + exfreeq(q); + }else + last = &q->next; + unlock(&exq.l); + + /* tell slaves to abandon work in progress */ + lock(fs); + while((q = fs->work) != nil){ + fs->work = q->next; + lock(q); + q->shut = 1; + swiproc(q->slave, 0); /* whether busy or not */ + unlock(q); + } + unlock(fs); +} + +static void +exfreefids(Export *fs) +{ + Fid *f, *n; + int i; + + for(i = 0; i < Nfidhash; i++){ + for(f = fs->fid[i]; f != nil; f = n){ + n = f->next; + f->attached = 0; + if(f->ref == 0) { + if(f->chan != nil) + cclose(f->chan); + freeuqid(fs, f->qid); + free(f); + } else + print("exfreefids: busy fid\n"); + } + } +} + +static void +exfree(Export *fs) +{ + if(exdebug) + print("export p/s %ld free %p ref %ld\n", up->pid, fs, fs->r.ref); + if(decref(&fs->r) != 0) + return; + closepgrp(fs->pgrp); + closeegrp(fs->egrp); + closefgrp(fs->fgrp); + cclose(fs->root); + cclose(fs->io); + exfreefids(fs); + free(fs->user); + free(fs); +} + +static int +exwork(void*) +{ + return exq.head != nil; +} + +static void +exslave(void*) +{ + Export *fs; + Exq *q, *t, *fq, **last; + char *err; + + for(;;){ + qlock(&exq.qwait); + if(waserror()){ + qunlock(&exq.qwait); + continue; + } + sleep(&exq.rwait, exwork, nil); + poperror(); + + lock(&exq.l); + q = exq.head; + if(q == nil) { + unlock(&exq.l); + qunlock(&exq.qwait); + continue; + } + exq.head = q->next; + + qunlock(&exq.qwait); + + /* + * put the job on the work queue before it's + * visible as off of the head queue, so it's always + * findable for flushes and shutdown + */ + notkilled(); + q->slave = up; + q->busy = 1; /* fcall in progress: interruptible */ + fs = q->export; + lock(fs); + q->next = fs->work; + fs->work = q; + unlock(fs); + unlock(&exq.l); + + up->env->pgrp = q->export->pgrp; + up->env->egrp = q->export->egrp; + up->env->fgrp = q->export->fgrp; + kstrdup(&up->env->user, q->export->user); + + if(exdebug > 1) + print("exslave %ld dispatch %F\n", up->pid, &q->in); + + if(waserror()){ + print("exslave %ld err %s\n", up->pid, up->env->errstr); /* shouldn't happen */ + err = up->env->errstr; + }else{ + if(q->in.type >= Tmax || !fcalls[q->in.type]){ + snprint(up->genbuf, sizeof(up->genbuf), "unknown message: %F", &q->in); + err = up->genbuf; + }else{ + switch(q->in.type){ + case Tread: + q->out.data = (char*)q->buf + IOHDRSZ; + break; + case Tstat: + q->out.stat = q->buf + MSGHDRSZ + BIT16SZ; /* leaves it just where we want it */ + q->out.nstat = q->bsize-(MSGHDRSZ+BIT16SZ); + break; + } + err = (*fcalls[q->in.type])(fs, &q->in, &q->out); + } + poperror(); + } + + /* + * if the fcall completed without error we must reply, + * even if a flush is pending (because the underlying server + * might have changed state), unless the export has shut down completely. + * must also reply to each flush in order, and only after the original reply (if sent). + */ + lock(q); + notkilled(); + q->busy = 0; /* operation complete */ + if(!q->shut){ + if(q->flush == nil || err == nil){ + unlock(q); + q->out.type = q->in.type+1; + q->out.tag = q->in.tag; + if(err){ + q->out.type = Rerror; + q->out.ename = err; + } + exreply(q, "exslave"); + lock(q); + } + while((fq = q->flush) != nil && !q->shut){ + q->flush = fq->next; + unlock(q); + exreply(fq, "exslave"); + exfreeq(fq); + lock(q); + } + } + q->finished = 1; /* promise not to send any more */ + unlock(q); + + lock(fs); + for(last = &fs->work; (t = *last) != nil; last = &t->next) + if(t == q){ + *last = q->next; + break; + } + unlock(fs); + + notkilled(); + exfreeq(q); + } + print("exslave %ld shut down", up->pid); /* not reached */ + pexit("exslave shut down", 0); +} + +static void +exreply(Exq *q, char *who) +{ + Export *fs; + Fcall *r; + int n; + + fs = q->export; + r = &q->out; + + n = convS2M(r, q->buf, q->bsize); + if(n == 0){ + r->type = Rerror; + if(fs->msize == 0) + r->ename = "Tversion not seen"; + else + r->ename = "failed to convert R-message"; + n = convS2M(r, q->buf, q->bsize); + } + + if(exdebug) + print("%s %ld -> %F\n", who, up->pid, r); + + if(!waserror()){ + devtab[fs->io->type]->write(fs->io, q->buf, n, 0); + poperror(); + } +} + +static int +exiounit(Export *fs, Chan *c) +{ + int iounit; + + iounit = fs->msize-IOHDRSZ; + if(c->iounit != 0 && c->iounit < fs->msize) + iounit = c->iounit; + return iounit; +} + +static Qid +Exrmtqid(Chan *c, Uqid *qid) +{ + Qid q; + + q.path = qid->newpath; + q.vers = c->qid.vers; + q.type = c->qid.type; + return q; +} + +static Fid* +Exmkfid(Export *fs, ulong fid) +{ + ulong h; + Fid *f, *nf; + + nf = malloc(sizeof(Fid)); + if(nf == nil) + return nil; + lock(&fs->fidlock); + h = fid % Nfidhash; + for(f = fs->fid[h]; f != nil; f = f->next){ + if(f->fid == fid){ + unlock(&fs->fidlock); + free(nf); + return nil; + } + } + + nf->next = fs->fid[h]; + if(nf->next != nil) + nf->next->last = &nf->next; + nf->last = &fs->fid[h]; + fs->fid[h] = nf; + + nf->fid = fid; + nf->ref = 1; + nf->attached = 1; + nf->offset = 0; + nf->chan = nil; + nf->qid = nil; + unlock(&fs->fidlock); + return nf; +} + +static Fid* +Exgetfid(Export *fs, ulong fid) +{ + Fid *f; + ulong h; + + lock(&fs->fidlock); + h = fid % Nfidhash; + for(f = fs->fid[h]; f; f = f->next) { + if(f->fid == fid){ + if(f->attached == 0) + break; + f->ref++; + unlock(&fs->fidlock); + return f; + } + } + unlock(&fs->fidlock); + return nil; +} + +static void +Exputfid(Export *fs, Fid *f) +{ + Chan *c; + + lock(&fs->fidlock); + f->ref--; + if(f->ref == 0 && f->attached == 0){ + c = f->chan; + f->chan = nil; + *f->last = f->next; + if(f->next != nil) + f->next->last = f->last; + unlock(&fs->fidlock); + if(c != nil) + cclose(c); + freeuqid(fs, f->qid); + free(f); + return; + } + unlock(&fs->fidlock); +} + +static Chan* +exmount(Chan *c, Mhead **mp, int doname) +{ + Chan *nc; + Cname *oname; + + nc = nil; + if((c->flag & COPEN) == 0 && findmount(&nc, mp, c->type, c->dev, c->qid)){ + if(waserror()){ + cclose(nc); + nexterror(); + } + nc = cunique(nc); + poperror(); + if(doname){ + oname = c->name; + incref(oname); + cnameclose(nc->name); + nc->name = oname; + } + return nc; + } + incref(c); + return c; +} + +static char* +Exversion(Export *fs, Fcall *t, Fcall *r) +{ + char *p; + static char version[] = VERSION9P; + int iounit; + + r->msize = t->msize; + if(r->msize > MAXRPCMAX) + r->msize = MAXRPCMAX; + iounit = fs->io->iounit; + if(iounit != 0 && iounit > 64 && iounit < r->msize) + r->msize = iounit; + if(r->msize < 64) + return "message size too small"; + if((p = strchr(t->version, '.')) != nil) + *p = 0; + if(strncmp(t->version, "9P", 2) ==0 && strcmp(version, t->version) <= 0){ + r->version = version; + fs->msize = r->msize; + }else + r->version = "unknown"; + return nil; +} + +static char* +Exauth(Export *fs, Fcall *t, Fcall *r) +{ + USED(fs); + USED(t); + USED(r); + return "authentication not required"; +} + +static char* +Exattach(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + + f = Exmkfid(fs, t->fid); + if(f == nil) + return Edupfid; + if(waserror()){ + f->attached = 0; + Exputfid(fs, f); + return up->env->errstr; + } + f->chan = cclone(fs->root); + f->qid = uqidalloc(fs, f->chan); + poperror(); + r->qid = Exrmtqid(f->chan, f->qid); + Exputfid(fs, f); + return nil; +} + +static char* +Exclunk(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + + USED(r); + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + f->attached = 0; + Exputfid(fs, f); + return nil; +} + +static int +safewalk(Chan **cp, char **names, int nnames, int nomount, int *nerror) +{ + int r; + + /* walk can raise error */ + if(waserror()) + return -1; + r = walk(cp, names, nnames, nomount, nerror); + poperror(); + return r; +} + +static char* +Exwalk(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f, *nf; + Chan *c; + char *name; + Uqid *qid; + int i; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(f->chan->flag & COPEN){ + Exputfid(fs, f); + return Eopen; + } + + if(waserror()) + return up->env->errstr; + c = cclone(f->chan); + poperror(); + qid = f->qid; + incref(qid); + r->nwqid = 0; + if(t->nwname > 0){ + for(i=0; i<t->nwname; i++){ + name = t->wname[i]; + if(!exisroot(fs, c) || *name != '\0' && strcmp(name, "..") != 0){ + if(safewalk(&c, &name, 1, 0, nil) < 0){ + /* leave the original state on error */ + cclose(c); + freeuqid(fs, qid); + Exputfid(fs, f); + if(i == 0) + return up->env->errstr; + return nil; + } + freeuqid(fs, qid); + qid = uqidalloc(fs, c); + } + r->wqid[r->nwqid++] = Exrmtqid(c, qid); + } + } + + if(t->newfid != t->fid){ + nf = Exmkfid(fs, t->newfid); + if(nf == nil){ + cclose(c); + freeuqid(fs, qid); + Exputfid(fs, f); + return Edupfid; + } + nf->chan = c; + nf->qid = qid; + Exputfid(fs, nf); + }else{ + cclose(f->chan); + f->chan = c; + freeuqid(fs, f->qid); + f->qid = qid; + } + Exputfid(fs, f); + return nil; +} + +static char* +Exopen(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + Uqid *qid; + Mhead *m; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(f->chan->flag & COPEN){ + Exputfid(fs, f); + return Emode; + } + m = nil; + c = exmount(f->chan, &m, 1); + if(waserror()){ + cclose(c); + Exputfid(fs, f); + return up->env->errstr; + } + + /* only save the mount head if it's a multiple element union */ + if(m && m->mount && m->mount->next) + c->umh = m; + else + putmhead(m); + + c = devtab[c->type]->open(c, t->mode); + if(t->mode & ORCLOSE) + c->flag |= CRCLOSE; + + qid = uqidalloc(fs, c); + poperror(); + freeuqid(fs, f->qid); + cclose(f->chan); + f->chan = c; + f->qid = qid; + f->offset = 0; + r->qid = Exrmtqid(c, f->qid); + r->iounit = exiounit(fs, c); + Exputfid(fs, f); + return nil; +} + +static char* +Excreate(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + volatile struct {Chan *c;} c, dc; + Cname *oname; + Uqid *qid; + Mhead *m; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(f->chan->flag & COPEN){ + Exputfid(fs, f); + return Emode; + } + if(waserror()){ + Exputfid(fs, f); + return up->env->errstr; + } + validname(t->name, 0); + if(t->name[0] == '.' && (t->name[1] == '\0' || t->name[1] == '.' && t->name[2] == '\0')) + error(Efilename); /* underlying server should check, but stop it here */ + + m = nil; + c.c = exmount(f->chan, &m, 1); + if(waserror()){ + cclose(c.c); + if(m != nil) + putmhead(m); + nexterror(); + } + if(m != nil){ + oname = c.c->name; + incref(oname); + if(waserror()){ + cnameclose(oname); + nexterror(); + } + dc.c = createdir(c.c, m); + if(waserror()){ + cclose(dc.c); + nexterror(); + } + c.c = cunique(dc.c); + poperror(); + cnameclose(c.c->name); + poperror(); + c.c->name = oname; + } + devtab[c.c->type]->create(c.c, t->name, t->mode, t->perm); + c.c->name = addelem(c.c->name, t->name); + if(t->mode & ORCLOSE) + c.c->flag |= CRCLOSE; + qid = uqidalloc(fs, c.c); + poperror(); + if(m != nil) + putmhead(m); + + poperror(); + cclose(f->chan); + f->chan = c.c; + freeuqid(fs, f->qid); + f->qid = qid; + r->qid = Exrmtqid(c.c, f->qid); + r->iounit = exiounit(fs, c.c); + Exputfid(fs, f); + return nil; +} + +static char* +Exread(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + long off; + int dir, n, seek; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + + if(waserror()) { + Exputfid(fs, f); + return up->env->errstr; + } + c = f->chan; + if((c->flag & COPEN) == 0) + error(Emode); + if(c->mode != OREAD && c->mode != ORDWR) + error(Eaccess); + if(t->count < 0 || t->count > fs->msize-IOHDRSZ) + error(Ecount); + if(t->offset < 0) + error(Enegoff); + dir = c->qid.type & QTDIR; + if(dir && t->offset != f->offset){ + if(t->offset != 0) + error(Eseekdir); + f->offset = 0; + c->uri = 0; + c->dri = 0; + } + + for(;;){ + n = t->count; + seek = 0; + off = t->offset; + if(dir && f->offset != off){ + off = f->offset; + n = t->offset - off; + if(n > MAXFDATA) + n = MAXFDATA; + seek = 1; + } + if(dir && c->umh != nil){ + if(0) + print("union read %d uri %d dri %d\n", seek, c->uri, c->dri); + n = unionread(c, r->data, n); + } + else{ + c->offset = off; + n = devtab[c->type]->read(c, r->data, n, off); + lock(c); + c->offset += n; + unlock(c); + } + f->offset = off + n; + if(n == 0 || !seek) + break; + } + r->count = n; + + poperror(); + Exputfid(fs, f); + return nil; +} + +static char* +Exwrite(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->env->errstr; + } + c = f->chan; + if((c->flag & COPEN) == 0) + error(Emode); + if(c->mode != OWRITE && c->mode != ORDWR) + error(Eaccess); + if(c->qid.type & QTDIR) + error(Eisdir); + if(t->count < 0 || t->count > fs->msize-IOHDRSZ) + error(Ecount); + if(t->offset < 0) + error(Enegoff); + r->count = devtab[c->type]->write(c, t->data, t->count, t->offset); + poperror(); + Exputfid(fs, f); + return nil; +} + +static char* +Exstat(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + int n; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + c = exmount(f->chan, nil, 1); + if(waserror()){ + cclose(c); + Exputfid(fs, f); + return up->env->errstr; + } + n = devtab[c->type]->stat(c, r->stat, r->nstat); + if(n <= BIT16SZ) + error(Eshortstat); + r->nstat = n; + poperror(); + cclose(c); + Exputfid(fs, f); + return nil; +} + +static char* +Exwstat(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + + USED(r); + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->env->errstr; + } + validstat(t->stat, t->nstat); /* check name */ + + c = exmount(f->chan, nil, 0); + if(waserror()){ + cclose(c); + nexterror(); + } + devtab[c->type]->wstat(c, t->stat, t->nstat); + poperror(); + + cclose(c); + poperror(); + Exputfid(fs, f); + return nil; +} + +static char* +Exremove(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + + USED(r); + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(waserror()){ + f->attached = 0; + Exputfid(fs, f); + return up->env->errstr; + } + c = exmount(f->chan, nil, 0); + if(waserror()){ + c->type = 0; /* see below */ + cclose(c); + nexterror(); + } + devtab[c->type]->remove(c); + poperror(); + poperror(); + + /* + * chan is already clunked by remove. + * however, we need to recover the chan, + * and follow sysremove's lead in making it point to root. + */ + c->type = 0; + + cclose(c); + f->attached = 0; + Exputfid(fs, f); + return nil; +} + +/* + * unique path generation + */ + +static int +uqidhash(vlong path) +{ + ulong p; + p = (ulong)path; + return ((p>>16) ^ (p>>8) ^ p) & (Nqidhash-1); +} + +static Uqid ** +uqidlook(Uqid **tab, Chan *c, vlong path) +{ + Uqid **hp, *q; + + for(hp = &tab[uqidhash(path)]; (q = *hp) != nil; hp = &q->next) + if(q->type == c->type && q->dev == c->dev && q->oldpath == path) + break; + return hp; +} + +static int +uqidexists(Uqid **tab, vlong path) +{ + int i; + Uqid *q; + + for(i=0; i<Nqidhash; i++) + for(q = tab[i]; q != nil; q = q->next) + if(q->newpath == path) + return 1; + return 0; +} + +static Uqid * +uqidalloc(Export *fs, Chan *c) +{ + Uqid **hp, *q; + + qlock(&fs->qidlock); + hp = uqidlook(fs->qids, c, c->qid.path); + if((q = *hp) != nil){ + incref(q); + qunlock(&fs->qidlock); + return q; + } + q = mallocz(sizeof(*q), 1); + if(q == nil){ + qunlock(&fs->qidlock); + error(Enomem); + } + q->ref = 1; + q->type = c->type; + q->dev = c->dev; + q->oldpath = c->qid.path; + q->newpath = c->qid.path; + while(uqidexists(fs->qids, q->newpath)){ + if(++fs->pathgen >= (1<<16)) + fs->pathgen = 1; + q->newpath = ((vlong)fs->pathgen<<48) | (q->newpath & QIDMASK); + } + q->next = nil; + *hp = q; + qunlock(&fs->qidlock); + return q; +} + +static void +freeuqid(Export *fs, Uqid *q) +{ + Uqid **hp; + + if(q == nil) + return; + qlock(&fs->qidlock); + if(decref(q) == 0){ + hp = &fs->qids[uqidhash(q->oldpath)]; + for(; *hp != nil; hp = &(*hp)->next) + if(*hp == q){ + *hp = q->next; + free(q); + break; + } + } + qunlock(&fs->qidlock); +} diff --git a/os/port/flashamd29f0x0.c b/os/port/flashamd29f0x0.c new file mode 100644 index 00000000..06798d05 --- /dev/null +++ b/os/port/flashamd29f0x0.c @@ -0,0 +1,167 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "../port/flashif.h" + +/* + * AMD29F0x0 with 4 interleaved to give 32 bits + */ + +enum { + DQ7 = 0x80808080, + DQ6 = 0x40404040, + DQ5 = 0x20202020, + DQ3 = 0x08080808, + DQ2 = 0x04040404, +}; + +#define DPRINT if(0)print +#define EPRINT if(1)print + +static char* +amdwait(ulong *p, ulong ticks) +{ + ulong v0, v1; + + ticks += m->ticks+1; + v0 = *p; + for(;;){ + sched(); + v1 = *p; + if((v1 & DQ6) == (v0 & DQ6)) + break; + if((v1 & DQ5) == DQ5){ + v0 = *p; + v1 = *p; + if((v1 & DQ6) == (v0 & DQ6)) + break; + EPRINT("flash: DQ5 error: %8.8lux %8.8lux\n", v0, v1); + return "flash write error"; + } + if(m->ticks >= ticks){ + EPRINT("flash: timed out: %8.8lux\n", *p); + return "flash write timed out"; + } + v0 = v1; + } + return nil; +} + +static int +eraseall(Flash *f) +{ + ulong *p; + int s; + char *e; + + DPRINT("flash: erase all\n"); + p = (ulong*)f->addr; + s = splhi(); + *(p+0x555) = 0xAAAAAAAA; + *(p+0x2AA) = 0x55555555; + *(p+0x555) = 0x80808080; + *(p+0x555) = 0xAAAAAAAA; + *(p+0x2AA) = 0x55555555; + *(p+0x555) = 0x10101010; /* chip erase */ + splx(s); + e = amdwait(p, MS2TK(64*1000)); + *p = 0xF0F0F0F0; /* reset */ + if(e != nil) + error(e); + return 0; +} + +static int +erasezone(Flash *f, Flashregion *r, ulong addr) +{ + ulong *p; + int s; + char *e; + + DPRINT("flash: erase %8.8lux\n", addr); + if(addr & (r->erasesize-1)) + return -1; /* bad zone */ + p = (ulong*)f->addr; + s = splhi(); + *(p+0x555) = 0xAAAAAAAA; + *(p+0x2AA) = 0x55555555; + *(p+0x555) = 0x80808080; + *(p+0x555) = 0xAAAAAAAA; + *(p+0x2AA) = 0x55555555; + p += addr>>2; + *p = 0x30303030; /* sector erase */ + splx(s); + e = amdwait(p, MS2TK(8*1000)); + *p = 0xF0F0F0F0; /* reset */ + if(e != nil) + error(e); + return 0; +} + +static int +write4(Flash *f, ulong offset, void *buf, long n) +{ + ulong *p, *a, *v, w; + int s; + char *e; + + p = (ulong*)f->addr; + if(((ulong)p|offset|n)&3) + return -1; + n >>= 2; + a = p + (offset>>2); + v = buf; + for(; --n >= 0; v++, a++){ + w = *a; + DPRINT("flash: write %lux %lux -> %lux\n", (ulong)a, w, *v); + if(w == *v) + continue; /* already set */ + if(~w & *v) + error("flash not erased"); + s = splhi(); + *(p+0x555) = 0xAAAAAAAA; + *(p+0x2AA) = 0x55555555; + *(p+0x555) = 0xA0A0A0A0; /* program */ + *a = *v; + splx(s); + microdelay(8); + if(*a != *v){ + microdelay(8); + while(*a != *v){ + e = amdwait(a, 1); + if(e != nil) + error(e); + } + } + } + return 0; +} + +static int +reset(Flash *f) +{ + f->id = 0x01; /* can't use autoselect: might be running in flash */ + f->devid = 0; + f->write = write4; + f->eraseall = eraseall; + f->erasezone = erasezone; + f->suspend = nil; + f->resume = nil; + f->width = 4; + f->interleave = 0; /* TO DO */ + f->nr = 1; + f->regions[0] = (Flashregion){f->size/(4*64*1024), 0, f->size, 4*64*1024, 0}; + *(ulong*)f->addr = 0xF0F0F0F0; /* reset (just in case) */ + return 0; +} + +void +flashamd29f0x0link(void) +{ + addflashcard("AMD29F0x0", reset); +} diff --git a/os/port/flashcfi16.c b/os/port/flashcfi16.c new file mode 100644 index 00000000..3c22f475 --- /dev/null +++ b/os/port/flashcfi16.c @@ -0,0 +1,134 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "../port/flashif.h" + +/* + * Common Flash Interface (1x16 and 2x16) + */ + +/* interleaved flash has chips at even and odd word addresses */ +#define I(x) (((x)<<24)|((x)<<16)|((x)<<8)|(x)) + +enum { + ReadArray = I(0xFF), + ReadQuery = I(0x98), +}; + +#include "flashintel" + +static int +cfiget1(Flash *f, ulong a) +{ + ulong v; + + v = flashget(f, a); +//iprint("%.8lux->%.4ux\n", a, v); + if(f->width == 2 && v == 0xFFFF) + return 0; /* get this on old, partially-conforming CFI */ + return v & 0xFF; +} + +static int +cfiget2(Flash *f, ulong i) +{ + return (cfiget1(f, i+1)<<8) | cfiget1(f, i); +} + +static int +cfiquery(Flash *f) +{ + Flashregion *r; + ulong addr; + int i; + + flashput(f, 0x55, ReadQuery); + if(!(cfiget1(f, 0x10) == 'Q' && cfiget1(f, 0x11) == 'R' && cfiget1(f, 0x12) == 'Y')) /* TO DO: detect interleave */ + return 0; + f->alg = cfiget2(f, 0x13); + i = cfiget1(f, 0x27); + if(i > 0 && i < 32) + i = 1<<i; + else + i = 0; + f->devsize = i; + f->size = f->devsize; + if(f->interleave) + f->size *= 2; + i = cfiget2(f, 0x2A); + if(i > 0 && i < 32) + i = 1<<i; + else + i = 0; + f->maxwb = i; + f->nr = cfiget1(f, 0x2C); + if(f->nr != 0){ + addr = 0; + for(i=0; i<f->nr; i++){ + r = &f->regions[i]; + r->n = cfiget2(f, 0x2D+4*i)+1; + r->erasesize = cfiget2(f, 0x2D+2+4*i)*256; + if(r->erasesize == 0) + r->erasesize = 128; + if(f->interleave) + r->erasesize *= 2; /* TO DO */ + r->start = addr; + r->end = r->start + r->n*r->erasesize; + } + if(1){ + iprint("cfi: devsize=%lud maxwb=%d\n", f->devsize, f->maxwb); + for(i=0; i<f->nr; i++){ + r = &f->regions[i]; + iprint("flash %d: %d %lud %8.8lux %8.8lux\n", i, r->n, r->erasesize, r->start, r->end); + } + } + }else{ + f->nr = 1; + f->regions[0] = (Flashregion){1, 0, f->devsize, f->devsize, 0}; + } + return 1; +} + +static int +reset(Flash *f) +{ + if(f->xip) + return -1; /* can't use this interface if executing from flash */ + if(f->width == 0) + f->width = 2; + if(!cfiquery(f) || f->alg != 1 && f->alg != 3){ + /* apparently not CFI: try to reset to read mode before return */ + flashput(f, 0x55, ClearStatus); + flashput(f, 0x55, ReadArray); + return -1; + } + f->cmask = 0x00FF00FF; + flashput(f, 0x55, ClearStatus); + flashput(f, 0x55, ReadID); + f->id = cfiget1(f, 0x00); + f->devid = cfiget1(f, 0x01); + flashput(f, 0x55, ClearStatus); + flashput(f, 0x55, ReadArray); + if(f->width == 2){ + f->cmask = 0x00FF; + f->write = intelwrite2; + }else{ + f->cmask = 0x00FF00FF; + f->write = intelwrite4; + } + f->erasezone = intelerase; + f->suspend = nil; + f->resume = nil; + return 0; +} + +void +flashcfi16link(void) +{ + addflashcard("cfi16", reset); +} diff --git a/os/port/flashcfi8.c b/os/port/flashcfi8.c new file mode 100644 index 00000000..099267b6 --- /dev/null +++ b/os/port/flashcfi8.c @@ -0,0 +1,143 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "../port/flashif.h" + +/* + * Common Flash Interface (1x8 and 2x8) + */ + +/* interleaved flash has chips at even and odd word addresses */ +#define I(x) (((x)<<24)|((x)<<16)|((x)<<8)|(x)) + +enum { + ReadArray = I(0xFF), + ReadQuery = I(0x98), +}; + +/* TO DO: flash amd */ +#include "flashintel" + +static int +cfiget1(Flash *f, ulong a) +{ + ulong v; + + v = flashget(f, a); +//iprint("%.8lux->%.4ux\n", a, v); + return v & 0xFF; +} + +static int +cfiget2(Flash *f, ulong i) +{ + return (cfiget1(f, i+1)<<8) | cfiget1(f, i); +} + +static int +cfiquery(Flash *f) +{ + Flashregion *r; + ulong addr; + int i; + + flashput(f, 0x55, ReadQuery); + if(!(cfiget1(f, 0x10) == 'Q' && cfiget1(f, 0x11) == 'R' && cfiget1(f, 0x12) == 'Y')) /* TO DO: detect interleave */ + return 0; + f->alg = cfiget2(f, 0x13); + i = cfiget1(f, 0x27); + if(i > 0 && i < 32) + i = 1<<i; + else + i = 0; + f->devsize = i; + f->size = f->devsize; + if(f->interleave) + f->size *= 2; + i = cfiget2(f, 0x2A); + if(i > 0 && i < 32) + i = 1<<i; + else + i = 0; + f->maxwb = i; + f->nr = cfiget1(f, 0x2C); + if(f->nr != 0){ + addr = 0; + for(i=0; i<f->nr; i++){ + r = &f->regions[i]; + r->n = cfiget2(f, 0x2D+4*i)+1; + r->erasesize = cfiget2(f, 0x2D+2+4*i)*256; + if(r->erasesize == 0) + r->erasesize = 128; + if(f->interleave) + r->erasesize *= 2; /* TO DO */ + r->start = addr; + r->end = r->start + r->n*r->erasesize; + } + if(1){ + iprint("cfi: devsize=%lud maxwb=%d\n", f->devsize, f->maxwb); + for(i=0; i<f->nr; i++){ + r = &f->regions[i]; + iprint("flash %d: %d %lud %8.8lux %8.8lux\n", i, r->n, r->erasesize, r->start, r->end); + } + } + }else{ + f->nr = 1; + f->regions[0] = (Flashregion){1, 0, f->devsize, f->devsize, 0}; + } + return 1; +} + +static int +reset(Flash *f) +{ + if(f->xip) + return -1; /* can't use this interface if executing from flash */ + if(f->width == 0) + f->width = 1; + if(!cfiquery(f) && f->bshift==0) + f->bshift = 1; /* try this */ + if(!cfiquery(f) || f->alg != 1 && f->alg != 3){ + if(f->alg == 2){ + print("amd algorithm\n"); + goto ok; + } + /* apparently not CFI: try to reset to read mode before return */ + flashput(f, 0x55, ClearStatus); + flashput(f, 0x55, ReadArray); + return -1; + } +ok: + switch(f->width){ + case 1: + f->cmask = 0x00FF; + break; + case 2: + f->cmask = 0xFFFF; + break; + case 4: + f->cmask = 0xFFFFFFFF; + break; + } + flashput(f, 0x55, ClearStatus); + flashput(f, 0x55, ReadID); + f->id = cfiget1(f, 0x00); + f->devid = cfiget1(f, 0x01); + flashput(f, 0x55, ClearStatus); + flashput(f, 0x55, ReadArray); + f->erasezone = nil; + f->suspend = nil; + f->resume = nil; + return 0; +} + +void +flashcfi8link(void) +{ + addflashcard("cfi8", reset); +} diff --git a/os/port/flashif.h b/os/port/flashif.h new file mode 100644 index 00000000..cc013760 --- /dev/null +++ b/os/port/flashif.h @@ -0,0 +1,147 @@ +typedef struct Flash Flash; +typedef struct Flashchip Flashchip; +typedef struct Flashpart Flashpart; +typedef struct Flashregion Flashregion; + +/* + * logical partitions + */ +enum { + Maxflashpart = 8 +}; + +struct Flashpart { + char* name; + ulong start; + ulong end; +}; + +enum { + Maxflashregion = 4 +}; + +/* + * physical erase block regions + */ +struct Flashregion { + int n; /* number of blocks in region */ + ulong start; /* physical base address (allowing for banks) */ + ulong end; + ulong erasesize; + ulong pagesize; /* if non-zero, the size of pages within the erase block */ +}; + +/* + * one of a set of chips in a given region + */ +struct Flashchip { + int nr; + Flashregion regions[Maxflashregion]; + + uchar id; /* flash manufacturer ID */ + ushort devid; /* flash device ID */ + int width; /* bytes per flash line */ + int maxwb; /* max write buffer size */ + ulong devsize; /* physical device size */ + int alg; /* programming algorithm (if CFI) */ + int protect; /* software protection */ +}; + +/* + * structure defining a contiguous region of flash memory + */ +struct Flash { + QLock; /* interlock on flash operations */ + Flash* next; + + /* the following are filled in before calling Flash.reset */ + char* type; + void* addr; + ulong size; + int xip; /* executing in place: don't query */ + int (*reset)(Flash*); + + /* the following are filled in by the reset routine */ + int (*eraseall)(Flash*); + int (*erasezone)(Flash*, Flashregion*, ulong); + int (*read)(Flash*, ulong, void*, long); /* (optional) reads of correct width and alignment */ + int (*write)(Flash*, ulong, void*, long); /* writes of correct width and alignment */ + int (*suspend)(Flash*); + int (*resume)(Flash*); + int (*attach)(Flash*); + + /* the following might be filled in by either archflashreset or the reset routine */ + int nr; + Flashregion regions[Maxflashregion]; + + uchar id; /* flash manufacturer ID */ + ushort devid; /* flash device ID */ + int width; /* bytes per flash line */ + int interleave; /* addresses are interleaved across set of chips */ + int bshift; /* byte addresses are shifted */ + ulong cmask; /* command mask for interleaving */ + int maxwb; /* max write buffer size */ + ulong devsize; /* physical device size */ + int alg; /* programming algorithm (if CFI) */ + void* data; /* flash type routines' private storage, or nil */ + Flashpart part[Maxflashpart]; /* logical partitions */ + int protect; /* software protection */ + char* sort; /* "nand", "nor", "serial", nil (unspecified) */ +}; + +/* + * called by link routine of driver for specific flash type: arguments are + * conventional name for card type/model, and card driver's reset routine. + */ +void addflashcard(char*, int (*)(Flash*)); + +/* + * called by devflash.c:/^flashreset; if flash exists, + * sets type, address, and size in bytes of flash + * and returns 0; returns -1 if flash doesn't exist + */ +int archflashreset(int, Flash*); + +/* + * enable/disable write protect + */ +void archflashwp(Flash*, int); + +/* + * flash access taking width and interleave into account + */ +int flashget(Flash*, ulong); +void flashput(Flash*, ulong, int); + +/* + * Architecture specific routines for managing nand devices + */ + +/* + * do any device spcific initialisation + */ +void archnand_init(Flash*); + +/* + * if claim is 1, claim device exclusively, and enable it (power it up) + * if claim is 0, release, and disable it (power it down) + * claiming may be as simple as a qlock per device + */ +void archnand_claim(Flash*, int claim); + +/* + * set command latch enable (CLE) and address latch enable (ALE) + * appropriately + */ +void archnand_setCLEandALE(Flash*, int cle, int ale); + +/* + * write a sequence of bytes to the device + */ +void archnand_write(Flash*, void *buf, int len); + +/* + * read a sequence of bytes from the device + * if buf is 0, throw away the data + */ +void archnand_read(Flash*, void *buf, int len); diff --git a/os/port/flashintel b/os/port/flashintel new file mode 100644 index 00000000..f0093acf --- /dev/null +++ b/os/port/flashintel @@ -0,0 +1,179 @@ + +enum { + DQ7 = I(0x80), + DQ6 = I(0x40), + DQ5 = I(0x20), + DQ4 = I(0x10), + DQ3 = I(0x08), + DQ2 = I(0x04), + DQ1 = I(0x02), + DQ0 = I(0x01), +}; + +/* + * intel algorithm + */ + +enum { + ReadID = I(0x90), + ClearStatus = I(0x50), + ReadStatus = I(0x70), + Program = I(0x40), + BlockErase = I(0x20), + Confirm = I(0xD0), +}; + +#define DPRINT if(0)print +#define EPRINT if(1)print + +static char* +intelwait(Flash *f, void *p, ulong ticks) +{ + ulong csr, mask; + + ticks += m->ticks+1; + mask = f->cmask; + for(;;){ + if(f->width == 2) + csr = *(ushort*)p; + else + csr = *(ulong*)p; + if((csr & mask) == (DQ7 & mask)) + break; + sched(); + if(m->ticks >= ticks) + return "flash write timed out"; + } + if(csr & (DQ5|DQ4|DQ3|DQ1)){ + EPRINT("flash: error: %8.8lux %8.8lux\n", p, csr); + if(csr & DQ1) + return "flash block locked"; + if(csr & DQ3) + return "low flash programming voltage"; + return Eio; + } + return nil; +} + +static int +intelerase(Flash *f, Flashregion *r, ulong addr) +{ + int s; + char *e; + + DPRINT("flash: erase zone %8.8lux\n", addr); + if(addr & (r->erasesize-1)) + return -1; /* bad zone */ + if(f->width == 2){ + ushort *p = (ushort*)((ulong)f->addr + addr); + s = splhi(); + *p = BlockErase & f->cmask; + *p = Confirm & f->cmask; + splx(s); + e = intelwait(f, p, MS2TK(8*1000)); + *p = ClearStatus & f->cmask; + *p = ReadArray & f->cmask; + }else{ + ulong *p = (ulong*)((ulong)f->addr + addr); + s = splhi(); + *p = BlockErase & f->cmask; + *p = Confirm & f->cmask; + splx(s); + e = intelwait(f, p, MS2TK(8*1000)); + *p = ClearStatus & f->cmask; + *p = ReadArray & f->cmask; + } + if(e != nil) + error(e); + return 0; +} + +static int +intelwrite2(Flash *f, ulong offset, void *buf, long n) +{ + ushort *a, *v; + ulong w; + int s; + char *e; + + if(((ulong)f->addr|offset|n)&(f->width-1)) + return -1; + a = (ushort*)((ulong)f->addr + offset); + n /= f->width; + v = buf; + if(waserror()){ + *a = ClearStatus & f->cmask; + *a = ReadArray & f->cmask; + nexterror(); + } + for(; --n >= 0; v++, a++){ + w = *a; + DPRINT("flash: write %p %#ulx -> %#lux\n", a, w, (ulong)*v); + if(w == *v) + continue; /* already set */ + if(~w & *v) + error("flash not erased"); + s = splhi(); + *a = Program & f->cmask; /* program */ + *a = *v; + splx(s); + microdelay(8); + e = intelwait(f, a, 5); + *a = ClearStatus & f->cmask; + *a = ReadArray & f->cmask; + if(e != nil) + error(e); + w = *a; + if(w != *v){ + EPRINT("flash: write %p %#8.8lux -> %#8.8lux failed\n", a, w, (ulong)*v); + error(Eio); + } + } + poperror(); + return 0; +} + +static int +intelwrite4(Flash *f, ulong offset, void *buf, long n) +{ + ulong *a, *v; + ulong w; + int s; + char *e; + + if(((ulong)f->addr|offset|n)&(f->width-1)) + return -1; + a = (ulong*)((ulong)f->addr + offset); + n /= f->width; + v = buf; + if(waserror()){ + *a = ClearStatus & f->cmask; + *a = ReadArray & f->cmask; + nexterror(); + } + for(; --n >= 0; v++, a++){ + w = *a; + DPRINT("flash: write %p %#ulx -> %#lux\n", a, w, (ulong)*v); + if(w == *v) + continue; /* already set */ + if(~w & *v) + error("flash not erased"); + s = splhi(); + *a = Program; /* program */ + *a = *v; + splx(s); + microdelay(8); + e = intelwait(f, a, 5); + *a = ClearStatus & f->cmask; + *a = ReadArray & f->cmask; + if(e != nil) + error(e); + w = *a; + if(w != *v){ + EPRINT("flash: write %p %#8.8lux -> %#8.8lux failed\n", a, w, *v); + error(Eio); + } + } + poperror(); + return 0; +} diff --git a/os/port/flashnand.c b/os/port/flashnand.c new file mode 100644 index 00000000..e58c5a63 --- /dev/null +++ b/os/port/flashnand.c @@ -0,0 +1,337 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "flashif.h" + +typedef struct Nandtab Nandtab; + +struct Nandtab { + short manufacturer; + uchar id; + uchar l2bytesperpage; + ushort pagesperblock; + ushort blocks; + uchar tPROGms; + ushort tBERASEms; + uchar tRus; +}; + +static Nandtab nandtab[] = { + { 0xec, 0xe6, 9, 16, 1024, 1, 4, 7 }, /* Samsung KM29U64000T */ + + { 0x98, 0xe6, 9, 16, 1024, 1, 4, 25 }, /* Toshiba TC58V64AFT */ + { 0x98, 0x73, 9, 32, 1024, 1, 10, 25}, /* Toshiba TC56V128AFT */ + /* Generic entries which take timings from Toshiba SMIL example code */ + { -1, 0xea, 8, 16, 512, 20, 400, 100 }, + { -1, 0xe3, 9, 16, 512, 20, 400, 100 }, + { -1, 0xe5, 9, 16, 512, 20, 400, 100 }, + { -1, 0x73, 9, 32, 1024, 20, 400, 100 }, + { -1, 0x75, 9, 32, 2048, 20, 400, 100 }, + { -1, 0x76, 9, 32, 4096, 20, 400, 100 }, +}; + +enum { + ReadMode1 = 0x00, + ReadMode2 = 0x01, + Program = 0x10, + ReadMode3 = 0x50, + Erase1 = 0x60, + ReadStatus = 0x70, + Write = 0x80, + Identify = 0x90, + Erase2 = 0xd0, + + StatusReady = 0x40, + StatusFail = 0x01, +}; + +/* + * NAND flash driver + */ + +#define DPRINT if(0)print +#define EPRINT if(1)print + +static int idchip(Flash *f); + +static void +nand_writebyte(Flash *f, uchar b) +{ + archnand_write(f, &b, 1); +} + +static uchar +nand_readbyte(Flash *f) +{ + uchar b; + archnand_read(f, &b, 1); + return b; +} + +static int +idchip(Flash *f) +{ + int x; + uchar maker, device; + + f->id = 0; + f->devid = 0; + f->width = 1; + archnand_claim(f, 1); + archnand_setCLEandALE(f, 1, 0); + nand_writebyte(f, Identify); + archnand_setCLEandALE(f, 0, 1); + nand_writebyte(f, 0); + archnand_setCLEandALE(f, 0, 0); + maker = nand_readbyte(f); + device = nand_readbyte(f); + archnand_claim(f, 0); + iprint("man=%#ux device=%#ux\n", maker, device); + for(x = 0; x < sizeof(nandtab) / sizeof(nandtab[0]); x++){ + if(nandtab[x].id == (device & 0xff) + && (nandtab[x].manufacturer == maker || nandtab[x].manufacturer == -1)){ + ulong bpp; + f->id = maker; + f->devid = device; + f->nr = 1; + bpp = 1 << nandtab[x].l2bytesperpage; + bpp |= bpp >> 5; + f->regions[0].erasesize = bpp * nandtab[x].pagesperblock; + f->size = f->regions[0].erasesize * nandtab[x].blocks; + f->regions[0].n = nandtab[x].blocks; + f->regions[0].start = 0; + f->regions[0].end = f->size; + f->regions[0].pagesize = bpp; + f->data = &nandtab[x]; + return 0; + } + } + print("nand: device %#.2ux/%#.2ux not recognised\n", maker, device); + return -1; +} + +static int +erasezone(Flash *f, Flashregion *r, ulong byteaddr) +{ + Nandtab *nt = f->data; + int paddress; + uchar val; + int rv; + uchar addr[2]; + + if(byteaddr%r->erasesize || byteaddr >= f->size) + return -1; /* bad zone */ + paddress = byteaddr/r->erasesize * nt->pagesperblock; /* can simplify ... */ +//print("erasezone(%.8lux) page %d %.8lux\n", byteaddr, paddress, r->erasesize); + archnand_claim(f, 1); + archnand_setCLEandALE(f, 1, 0); // command mode + nand_writebyte(f, Erase1); + archnand_setCLEandALE(f, 0, 1); // address mode + addr[0] = paddress; + addr[1] = paddress >> 8; + archnand_write(f, addr, 2); + archnand_setCLEandALE(f, 1, 0); // command mode + nand_writebyte(f, Erase2); + nand_writebyte(f, ReadStatus); + archnand_setCLEandALE(f, 0, 0); // data mode + + do { + val = nand_readbyte(f); + } while((val & StatusReady) != StatusReady); + + if((val & StatusFail) != 0){ + print("erasezone failed: %.2ux\n", val); + rv = -1; + } + else + rv = 0; + archnand_claim(f, 0); // turn off chip + return rv; +} + +static int +writepage(Flash *f, ulong page, ushort addr, void *buf, long n) +{ + uchar cmd; + uchar val; + int rv; + uchar cmdbuf[3]; + +//print("writepage(%ld, %d, %ld)\n", page, addr, n); + // Fake a read to set the pointer + if(addr < 256) + cmd = ReadMode1; + else if(addr < 512){ + cmd = ReadMode2; + addr -= 256; + }else{ + cmd = ReadMode3; + addr -= 512; + } + archnand_claim(f, 1); + archnand_setCLEandALE(f, 1, 0); // command mode + nand_writebyte(f, cmd); + nand_writebyte(f, Write); + archnand_setCLEandALE(f, 0, 1); // address mode + cmdbuf[0] = addr; + cmdbuf[1] = page; + cmdbuf[2] = page >> 8; + archnand_write(f, cmdbuf, 3); + archnand_setCLEandALE(f, 0, 0); // data mode + archnand_write(f, buf, n); + archnand_setCLEandALE(f, 1, 0); // command mode + nand_writebyte(f, Program); + nand_writebyte(f, ReadStatus); + archnand_setCLEandALE(f, 0, 0); // data mode + + do { + val = nand_readbyte(f); + }while((val & StatusReady) != StatusReady); + + if((val & StatusFail) != 0){ + print("writepage failed: %.2ux\n", val); + rv = -1; + }else + rv = 0; + + archnand_claim(f, 0); + return rv; +} + +static int +write(Flash *f, ulong offset, void *buf, long n) +{ + Nandtab *nt = f->data; + ulong page; + ulong addr; + ulong xbpp; + +//print("write(%ld, %ld)\n", offset, n); + + xbpp = (1 << nt->l2bytesperpage); + xbpp |= (xbpp >> 5); + page = offset / xbpp; + addr = offset % xbpp; + + while(n > 0){ + int count; + count = xbpp - addr; + if(count > n) + count = n; + if(writepage(f, page, addr, buf, count) < 0) + return -1; + offset += count; + n -= count; + buf = (uchar *)buf + count; + addr = 0; + } +//print("write done\n"); + return 0; +} + +static int +read(Flash *f, ulong offset, void *buf, long n) +{ + Nandtab *nt = f->data; + uchar cmd; + ulong page; + ulong addr; + ushort bytesperpage, xbytesperpage, skip, partialaddr; + uchar cmdbuf[3]; + int toread; + +//print("read(%ld, %.8lux, %ld)\n", offset, buf, n); + + bytesperpage = (1 << nt->l2bytesperpage); + xbytesperpage = bytesperpage; + xbytesperpage += bytesperpage >> 5; // 512 => 16, 256 => 8 + page = offset / xbytesperpage; + partialaddr = offset % xbytesperpage; + skip = 0; + if(partialaddr >= bytesperpage && xbytesperpage - partialaddr < n){ + // cannot start read in extended area, and then chain into main area, + // so start on last byte of main area, and skip the extra bytes + // stupid chip design this one + skip = partialaddr - bytesperpage + 1; + n += skip; + partialaddr = bytesperpage - 1; + } + addr = partialaddr; + if(addr >= bytesperpage){ + cmd = ReadMode3; + addr -= bytesperpage; + }else if(addr >= 256){ + cmd = ReadMode2; + addr -= 256; + }else + cmd = ReadMode1; + +//print("cmd %.2x page %.4lux addr %.8lux partialaddr %d skip %d\n", cmd, page, addr, partialaddr, skip); + // Read first page + archnand_claim(f, 1); + archnand_setCLEandALE(f, 1, 0); + nand_writebyte(f, cmd); + archnand_setCLEandALE(f, 0, 1); + cmdbuf[0] = addr; + cmdbuf[1] = page; + cmdbuf[2] = page >> 8; + archnand_write(f, cmdbuf, 3); + archnand_setCLEandALE(f, 0, 0); + if(partialaddr){ + // partial first page + microdelay(nt->tRus); + toread = partialaddr < xbytesperpage ? xbytesperpage - partialaddr : 0; + if(toread > n) + toread = n; + if(skip){ + archnand_read(f, 0, skip); + toread -= skip; + n -= skip; +// partialaddr += skip; + } + archnand_read(f, buf, toread); + n -= toread; +// partialaddr += toread; + buf = (uchar *)buf + toread; + } + while(n){ + microdelay(nt->tRus); + toread = xbytesperpage; + if(n < toread) + toread = n; + archnand_read(f, buf, toread); + n -= toread; + buf = (uchar *)buf + toread; + } + archnand_claim(f, 0); +//print("readn done\n"); + return 0; +} + +static int +reset(Flash *f) +{ +//iprint("nandreset\n"); + if(f->data != nil) + return 1; + f->write = write; + f->read = read; + f->eraseall = nil; + f->erasezone = erasezone; + f->suspend = nil; + f->resume = nil; + f->sort = "nand"; + archnand_init(f); + return idchip(f); +} + +void +flashnandlink(void) +{ + addflashcard("nand", reset); +} diff --git a/os/port/fpi.c b/os/port/fpi.c new file mode 100644 index 00000000..fc6d4e80 --- /dev/null +++ b/os/port/fpi.c @@ -0,0 +1,304 @@ +/* + * Floating Point Interpreter. + * shamelessly stolen from an original by ark. + */ +#include "fpi.h" + +void +fpiround(Internal *i) +{ + unsigned long guard; + + guard = i->l & GuardMask; + i->l &= ~GuardMask; + if(guard > (LsBit>>1) || (guard == (LsBit>>1) && (i->l & LsBit))){ + i->l += LsBit; + if(i->l & CarryBit){ + i->l &= ~CarryBit; + i->h++; + if(i->h & CarryBit){ + if (i->h & 0x01) + i->l |= CarryBit; + i->l >>= 1; + i->h >>= 1; + i->e++; + } + } + } +} + +static void +matchexponents(Internal *x, Internal *y) +{ + int count; + + count = y->e - x->e; + x->e = y->e; + if(count >= 2*FractBits){ + x->l = x->l || x->h; + x->h = 0; + return; + } + if(count >= FractBits){ + count -= FractBits; + x->l = x->h|(x->l != 0); + x->h = 0; + } + while(count > 0){ + count--; + if(x->h & 0x01) + x->l |= CarryBit; + if(x->l & 0x01) + x->l |= 2; + x->l >>= 1; + x->h >>= 1; + } +} + +static void +shift(Internal *i) +{ + i->e--; + i->h <<= 1; + i->l <<= 1; + if(i->l & CarryBit){ + i->l &= ~CarryBit; + i->h |= 0x01; + } +} + +static void +normalise(Internal *i) +{ + while((i->h & HiddenBit) == 0) + shift(i); +} + +static void +renormalise(Internal *i) +{ + if(i->e < -2 * FractBits) + i->e = -2 * FractBits; + while(i->e < 1){ + i->e++; + if(i->h & 0x01) + i->l |= CarryBit; + i->h >>= 1; + i->l = (i->l>>1)|(i->l & 0x01); + } + if(i->e >= ExpInfinity) + SetInfinity(i); +} + +void +fpinormalise(Internal *x) +{ + if(!IsWeird(x) && !IsZero(x)) + normalise(x); +} + +void +fpiadd(Internal *x, Internal *y, Internal *i) +{ + Internal *t; + + i->s = x->s; + if(IsWeird(x) || IsWeird(y)){ + if(IsNaN(x) || IsNaN(y)) + SetQNaN(i); + else + SetInfinity(i); + return; + } + if(x->e > y->e){ + t = x; + x = y; + y = t; + } + matchexponents(x, y); + i->e = x->e; + i->h = x->h + y->h; + i->l = x->l + y->l; + if(i->l & CarryBit){ + i->h++; + i->l &= ~CarryBit; + } + if(i->h & (HiddenBit<<1)){ + if(i->h & 0x01) + i->l |= CarryBit; + i->l = (i->l>>1)|(i->l & 0x01); + i->h >>= 1; + i->e++; + } + if(IsWeird(i)) + SetInfinity(i); +} + +void +fpisub(Internal *x, Internal *y, Internal *i) +{ + Internal *t; + + if(y->e < x->e + || (y->e == x->e && (y->h < x->h || (y->h == x->h && y->l < x->l)))){ + t = x; + x = y; + y = t; + } + i->s = y->s; + if(IsNaN(y)){ + SetQNaN(i); + return; + } + if(IsInfinity(y)){ + if(IsInfinity(x)) + SetQNaN(i); + else + SetInfinity(i); + return; + } + matchexponents(x, y); + i->e = y->e; + i->h = y->h - x->h; + i->l = y->l - x->l; + if(i->l < 0){ + i->l += CarryBit; + i->h--; + } + if(i->h == 0 && i->l == 0) + SetZero(i); + else while(i->e > 1 && (i->h & HiddenBit) == 0) + shift(i); +} + +#define CHUNK (FractBits/2) +#define CMASK ((1<<CHUNK)-1) +#define HI(x) ((short)((x)>>CHUNK) & CMASK) +#define LO(x) ((short)(x) & CMASK) +#define SPILL(x) ((x)>>CHUNK) +#define M(x, y) ((long)a[x]*(long)b[y]) +#define C(h, l) (((long)((h) & CMASK)<<CHUNK)|((l) & CMASK)) + +void +fpimul(Internal *x, Internal *y, Internal *i) +{ + long a[4], b[4], c[7], f[4]; + + i->s = x->s^y->s; + if(IsWeird(x) || IsWeird(y)){ + if(IsNaN(x) || IsNaN(y) || IsZero(x) || IsZero(y)) + SetQNaN(i); + else + SetInfinity(i); + return; + } + else if(IsZero(x) || IsZero(y)){ + SetZero(i); + return; + } + normalise(x); + normalise(y); + i->e = x->e + y->e - (ExpBias - 1); + + a[0] = HI(x->h); b[0] = HI(y->h); + a[1] = LO(x->h); b[1] = LO(y->h); + a[2] = HI(x->l); b[2] = HI(y->l); + a[3] = LO(x->l); b[3] = LO(y->l); + + c[6] = M(3, 3); + c[5] = M(2, 3) + M(3, 2) + SPILL(c[6]); + c[4] = M(1, 3) + M(2, 2) + M(3, 1) + SPILL(c[5]); + c[3] = M(0, 3) + M(1, 2) + M(2, 1) + M(3, 0) + SPILL(c[4]); + c[2] = M(0, 2) + M(1, 1) + M(2, 0) + SPILL(c[3]); + c[1] = M(0, 1) + M(1, 0) + SPILL(c[2]); + c[0] = M(0, 0) + SPILL(c[1]); + + f[0] = c[0]; + f[1] = C(c[1], c[2]); + f[2] = C(c[3], c[4]); + f[3] = C(c[5], c[6]); + + if((f[0] & HiddenBit) == 0){ + f[0] <<= 1; + f[1] <<= 1; + f[2] <<= 1; + f[3] <<= 1; + if(f[1] & CarryBit){ + f[0] |= 1; + f[1] &= ~CarryBit; + } + if(f[2] & CarryBit){ + f[1] |= 1; + f[2] &= ~CarryBit; + } + if(f[3] & CarryBit){ + f[2] |= 1; + f[3] &= ~CarryBit; + } + i->e--; + } + i->h = f[0]; + i->l = f[1]; + if(f[2] || f[3]) + i->l |= 1; + renormalise(i); +} + +void +fpidiv(Internal *x, Internal *y, Internal *i) +{ + i->s = x->s^y->s; + if(IsNaN(x) || IsNaN(y) + || (IsInfinity(x) && IsInfinity(y)) || (IsZero(x) && IsZero(y))){ + SetQNaN(i); + return; + } + else if(IsZero(x) || IsInfinity(y)){ + SetInfinity(i); + return; + } + else if(IsInfinity(x) || IsZero(y)){ + SetZero(i); + return; + } + normalise(x); + normalise(y); + i->h = 0; + i->l = 0; + i->e = y->e - x->e + (ExpBias + 2*FractBits - 1); + do{ + if(y->h > x->h || (y->h == x->h && y->l >= x->l)){ + i->l |= 0x01; + y->h -= x->h; + y->l -= x->l; + if(y->l < 0){ + y->l += CarryBit; + y->h--; + } + } + shift(y); + shift(i); + }while ((i->h & HiddenBit) == 0); +/* + if(y->h > x->h || (y->h == x->h && y->l >= x->l)) + i->l |= 0x01; +*/ + if(y->h || y->l) + i->l |= 0x01; + renormalise(i); +} + +int +fpicmp(Internal *x, Internal *y) +{ + if(IsNaN(x) && IsNaN(y)) + return 0; + if(IsInfinity(x) && IsInfinity(y)) + return y->s - x->s; + if(x->e == y->e && x->h == y->h && x->l == y->l) + return y->s - x->s; + if(x->e < y->e + || (x->e == y->e && (x->h < y->h || (x->h == y->h && x->l < y->l)))) + return y->s ? 1: -1; + return x->s ? -1: 1; +} diff --git a/os/port/fpimem.c b/os/port/fpimem.c new file mode 100644 index 00000000..4799d0a5 --- /dev/null +++ b/os/port/fpimem.c @@ -0,0 +1,136 @@ +#include "fpi.h" + +/* + * the following routines depend on memory format, not the machine + */ + +void +fpis2i(Internal *i, void *v) +{ + Single *s = v; + + i->s = (*s & 0x80000000) ? 1: 0; + if((*s & ~0x80000000) == 0){ + SetZero(i); + return; + } + i->e = ((*s>>23) & 0x00FF) - SingleExpBias + ExpBias; + i->h = (*s & 0x007FFFFF)<<(1+NGuardBits); + i->l = 0; + if(i->e) + i->h |= HiddenBit; + else + i->e++; +} + +void +fpid2i(Internal *i, void *v) +{ + Double *d = v; + + i->s = (d->h & 0x80000000) ? 1: 0; + i->e = (d->h>>20) & 0x07FF; + i->h = ((d->h & 0x000FFFFF)<<(4+NGuardBits))|((d->l>>25) & 0x7F); + i->l = (d->l & 0x01FFFFFF)<<NGuardBits; + if(i->e) + i->h |= HiddenBit; + else + i->e++; +} + +void +fpiw2i(Internal *i, void *v) +{ + Word w, word = *(Word*)v; + int e; + + if(word < 0){ + i->s = 1; + word = -word; + } + else + i->s = 0; + if(word == 0){ + SetZero(i); + return; + } + if(word > 0){ + for (e = 0, w = word; w; w >>= 1, e++) + ; + } else + e = 32; + if(e > FractBits){ + i->h = word>>(e - FractBits); + i->l = (word & ((1<<(e - FractBits)) - 1))<<(2*FractBits - e); + } + else { + i->h = word<<(FractBits - e); + i->l = 0; + } + i->e = (e - 1) + ExpBias; +} + +void +fpii2s(void *v, Internal *i) +{ + int e; + Single *s = (Single*)v; + + fpiround(i); + if(i->h & HiddenBit) + i->h &= ~HiddenBit; + else + i->e--; + *s = i->s ? 0x80000000: 0; + e = i->e; + if(e < ExpBias){ + if(e <= (ExpBias - SingleExpBias)) + return; + e = SingleExpBias - (ExpBias - e); + } + else if(e >= (ExpBias + (SingleExpMax-SingleExpBias))){ + *s |= SingleExpMax<<23; + return; + } + else + e = SingleExpBias + (e - ExpBias); + *s |= (e<<23)|(i->h>>(1+NGuardBits)); +} + +void +fpii2d(void *v, Internal *i) +{ + Double *d = (Double*)v; + + fpiround(i); + if(i->h & HiddenBit) + i->h &= ~HiddenBit; + else + i->e--; + i->l = ((i->h & GuardMask)<<25)|(i->l>>NGuardBits); + i->h >>= NGuardBits; + d->h = i->s ? 0x80000000: 0; + d->h |= (i->e<<20)|((i->h & 0x00FFFFFF)>>4); + d->l = (i->h<<28)|i->l; +} + +void +fpii2w(Word *word, Internal *i) +{ + Word w; + int e; + + fpiround(i); + e = (i->e - ExpBias) + 1; + if(e <= 0) + w = 0; + else if(e > 31) + w = 0x7FFFFFFF; + else if(e > FractBits) + w = (i->h<<(e - FractBits))|(i->l>>(2*FractBits - e)); + else + w = i->h>>(FractBits-e); + if(i->s) + w = -w; + *word = w; +} diff --git a/os/port/inferno.c b/os/port/inferno.c new file mode 100644 index 00000000..de9ea600 --- /dev/null +++ b/os/port/inferno.c @@ -0,0 +1,1030 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "isa.h" +#include "interp.h" +#include "runt.h" +#include "kernel.h" + +/* + * here because Sys_FileIO is not public + */ +extern int srvf2c(char*, char*, Sys_FileIO*); + +/* + * System types connected to gc + */ +uchar FDmap[] = Sys_FD_map; +uchar FileIOmap[] = Sys_FileIO_map; +void freeFD(Heap*, int); +void freeFileIO(Heap*, int); +Type* TFD; +Type* TFileIO; + +static uchar rmap[] = Sys_FileIO_read_map; +static uchar wmap[] = Sys_FileIO_write_map; +static Type* FioTread; +static Type* FioTwrite; +static uchar dmap[] = Sys_Dir_map; +static Type* Tdir; + +typedef struct FD FD; +struct FD +{ + Sys_FD fd; + Fgrp* grp; +}; + +void +sysinit(void) +{ + TFD = dtype(freeFD, sizeof(FD), FDmap, sizeof(FDmap)); + TFileIO = dtype(freeFileIO, Sys_FileIO_size, FileIOmap, sizeof(FileIOmap)); + + /* Support for devsrv.c */ + FioTread = dtype(freeheap, Sys_FileIO_read_size, rmap, sizeof(rmap)); + FioTwrite = dtype(freeheap, Sys_FileIO_write_size, wmap, sizeof(wmap)); + + /* Support for dirread */ + Tdir = dtype(freeheap, Sys_Dir_size, dmap, sizeof(dmap)); +} + +void +freeFD(Heap *h, int swept) +{ + FD *handle; + + USED(swept); + + handle = H2D(FD*, h); + + release(); + kfgrpclose(handle->grp, handle->fd.fd); + closefgrp(handle->grp); + acquire(); +} + +void +freeFileIO(Heap *h, int swept) +{ + Sys_FileIO *fio; + + if(swept) + return; + + fio = H2D(Sys_FileIO*, h); + destroy(fio->read); + destroy(fio->write); +} + +Sys_FD* +mkfd(int fd) +{ + Heap *h; + Fgrp *fg; + FD *handle; + + h = heap(TFD); + handle = H2D(FD*, h); + handle->fd.fd = fd; + fg = up->env->fgrp; + handle->grp = fg; + incref(fg); + return (Sys_FD*)handle; +} +#define fdchk(x) ((x) == (Sys_FD*)H ? -1 : (x)->fd) + +void +seterror(char *err, ...) +{ + char *estr; + va_list arg; + + estr = up->env->errstr; + va_start(arg, err); + vseprint(estr, estr+ERRMAX, err, arg); + va_end(arg); +} + +char* +syserr(char *s, char *es, Prog *p) +{ + Osenv *o; + + o = p->osenv; + kstrcpy(s, o->errstr, es - s); + return s + strlen(s); +} + +void +Sys_millisec(void *fp) +{ + F_Sys_millisec *f; + + f = fp; + *f->ret = TK2MS(MACHP(0)->ticks); +} + +void +Sys_open(void *fp) +{ + int fd; + F_Sys_open *f; + + f = fp; + destroy(*f->ret); + *f->ret = H; + release(); + fd = kopen(string2c(f->s), f->mode); + acquire(); + if(fd == -1) + return; + + *f->ret = mkfd(fd); +} + +void +Sys_pipe(void *fp) +{ + Array *a; + int fd[2]; + Sys_FD **sfd; + F_Sys_pipe *f; + + f = fp; + *f->ret = -1; + + a = f->fds; + if(a->len < 2) + return; + if(kpipe(fd) < 0) + return; + + sfd = (Sys_FD**)a->data; + destroy(sfd[0]); + destroy(sfd[1]); + sfd[0] = H; + sfd[1] = H; + sfd[0] = mkfd(fd[0]); + sfd[1] = mkfd(fd[1]); + *f->ret = 0; +} + +void +Sys_fildes(void *fp) +{ + F_Sys_fildes *f; + int fd; + + f = fp; + destroy(*f->ret); + *f->ret = H; + release(); + fd = kdup(f->fd, -1); + acquire(); + if(fd == -1) + return; + *f->ret = mkfd(fd); +} + +void +Sys_dup(void *fp) +{ + F_Sys_dup *f; + + f = fp; + release(); + *f->ret = kdup(f->old, f->new); + acquire(); +} + +void +Sys_create(void *fp) +{ + int fd; + F_Sys_create *f; + + f = fp; + destroy(*f->ret); + *f->ret = H; + release(); + fd = kcreate(string2c(f->s), f->mode, f->perm); + acquire(); + if(fd == -1) + return; + + *f->ret = mkfd(fd); +} + +void +Sys_remove(void *fp) +{ + F_Sys_remove *f; + + f = fp; + release(); + *f->ret = kremove(string2c(f->s)); + acquire(); +} + +void +Sys_seek(void *fp) +{ + F_Sys_seek *f; + + f = fp; + release(); + *f->ret = kseek(fdchk(f->fd), f->off, f->start); + acquire(); +} + +void +Sys_unmount(void *fp) +{ + F_Sys_unmount *f; + + f = fp; + release(); + *f->ret = kunmount(string2c(f->s1), string2c(f->s2)); + acquire(); +} + +void +Sys_read(void *fp) +{ + int n; + F_Sys_read *f; + + f = fp; + n = f->n; + if(f->buf == (Array*)H || n < 0) { + *f->ret = 0; + return; + } + if(n > f->buf->len) + n = f->buf->len; + + release(); + *f->ret = kread(fdchk(f->fd), f->buf->data, n); + acquire(); +} + +void +Sys_pread(void *fp) +{ + int n; + F_Sys_pread *f; + + f = fp; + n = f->n; + if(f->buf == (Array*)H || n < 0) { + *f->ret = 0; + return; + } + if(n > f->buf->len) + n = f->buf->len; + + release(); + *f->ret = kpread(fdchk(f->fd), f->buf->data, n, f->off); + acquire(); +} + +void +Sys_chdir(void *fp) +{ + F_Sys_chdir *f; + + f = fp; + release(); + *f->ret = kchdir(string2c(f->path)); + acquire(); +} + +void +Sys_write(void *fp) +{ + int n; + F_Sys_write *f; + + f = fp; + n = f->n; + if(f->buf == (Array*)H || n < 0) { + *f->ret = 0; + return; + } + if(n > f->buf->len) + n = f->buf->len; + + release(); + *f->ret = kwrite(fdchk(f->fd), f->buf->data, n); + acquire(); +} + +void +Sys_pwrite(void *fp) +{ + int n; + F_Sys_pwrite *f; + + f = fp; + n = f->n; + if(f->buf == (Array*)H || n < 0) { + *f->ret = 0; + return; + } + if(n > f->buf->len) + n = f->buf->len; + + release(); + *f->ret = kpwrite(fdchk(f->fd), f->buf->data, n, f->off); + acquire(); +} + +static void +unpackdir(Dir *d, Sys_Dir *sd) +{ + retstr(d->name, &sd->name); + retstr(d->uid, &sd->uid); + retstr(d->gid, &sd->gid); + retstr(d->muid, &sd->muid); + sd->qid.path = d->qid.path; + sd->qid.vers = d->qid.vers; + sd->qid.qtype = d->qid.type; + sd->mode = d->mode; + sd->atime = d->atime; + sd->mtime = d->mtime; + sd->length = d->length; + sd->dtype = d->type; + sd->dev = d->dev; +} + +static Dir* +packdir(Sys_Dir *sd) +{ + char *nm[4], *p; + int i, n; + Dir *d; + + nm[0] = string2c(sd->name); + nm[1] = string2c(sd->uid); + nm[2] = string2c(sd->gid); + nm[3] = string2c(sd->muid); + n = 0; + for(i=0; i<4; i++) + n += strlen(nm[i])+1; + d = smalloc(sizeof(*d)+n); + p = (char*)d+sizeof(*d); + for(i=0; i<4; i++){ + n = strlen(nm[i])+1; + memmove(p, nm[i], n); + p += n; + } + d->name = nm[0]; + d->uid = nm[1]; + d->gid = nm[2]; + d->muid = nm[3]; + d->qid.path = sd->qid.path; + d->qid.vers = sd->qid.vers; + d->qid.type = sd->qid.qtype; + d->mode = sd->mode; + d->atime = sd->atime; + d->mtime = sd->mtime; + d->length = sd->length; + d->type = sd->dtype; + d->dev = sd->dev; + return d; +} + +void +Sys_fstat(void *fp) +{ + Dir *d; + F_Sys_fstat *f; + + f = fp; + f->ret->t0 = -1; + release(); + d = kdirfstat(fdchk(f->fd)); + acquire(); + if(d == nil) + return; + if(waserror() == 0){ + unpackdir(d, &f->ret->t1); + f->ret->t0 = 0; + poperror(); + } + free(d); +} + +void +Sys_stat(void *fp) +{ + Dir *d; + F_Sys_stat *f; + + f = fp; + f->ret->t0 = -1; + release(); + d = kdirstat(string2c(f->s)); + acquire(); + if(d == nil) + return; + if(waserror() == 0){ + unpackdir(d, &f->ret->t1); + f->ret->t0 = 0; + poperror(); + } + free(d); +} + +void +Sys_fd2path(void *fp) +{ + F_Sys_fd2path *f; + char *s; + void *r; + + f = fp; + r = *f->ret; + *f->ret = H; + destroy(r); + release(); + s = kfd2path(fdchk(f->fd)); + acquire(); + if(waserror() == 0){ + retstr(s, f->ret); + poperror(); + } + free(s); +} + +void +Sys_mount(void *fp) +{ + F_Sys_mount *f; + + f = fp; + release(); + *f->ret = kmount(fdchk(f->fd), fdchk(f->afd), string2c(f->on), f->flags, string2c(f->spec)); + acquire(); +} + +void +Sys_bind(void *fp) +{ + F_Sys_bind *f; + + f = fp; + release(); + *f->ret = kbind(string2c(f->s), string2c(f->on), f->flags); + acquire(); +} + +void +Sys_wstat(void *fp) +{ + Dir *d; + F_Sys_wstat *f; + + f = fp; + d = packdir(&f->d); + release(); + *f->ret = kdirwstat(string2c(f->s), d); + acquire(); + free(d); +} + +void +Sys_fwstat(void *fp) +{ + Dir *d; + F_Sys_fwstat *f; + + f = fp; + d = packdir(&f->d); + release(); + *f->ret = kdirfwstat(fdchk(f->fd), d); + acquire(); + free(d); +} + +void +Sys_print(void *fp) +{ + int n; + Prog *p; + Chan *c; + char buf[1024], *b = buf; + F_Sys_print *f; + f = fp; + c = up->env->fgrp->fd[1]; + if(c == nil) + return; + p = currun(); + + release(); + n = xprint(p, f, &f->vargs, f->s, buf, sizeof(buf)); + if (n >= sizeof(buf)-UTFmax-2) + n = bigxprint(p, f, &f->vargs, f->s, &b, sizeof(buf)); + *f->ret = kwrite(1, b, n); + if (b != buf) + free(b); + acquire(); +} + +void +Sys_fprint(void *fp) +{ + int n; + Prog *p; + char buf[1024], *b = buf; + F_Sys_fprint *f; + + f = fp; + p = currun(); + release(); + n = xprint(p, f, &f->vargs, f->s, buf, sizeof(buf)); + if (n >= sizeof(buf)-UTFmax-2) + n = bigxprint(p, f, &f->vargs, f->s, &b, sizeof(buf)); + *f->ret = kwrite(fdchk(f->fd), b, n); + if (b != buf) + free(b); + acquire(); +} + +void +Sys_werrstr(void *fp) +{ + F_Sys_werrstr *f; + + f = fp; + *f->ret = 0; + kstrcpy(up->env->errstr, string2c(f->s), ERRMAX); +} + +void +Sys_dial(void *fp) +{ + int cfd; + char dir[NETPATHLEN], *a, *l; + F_Sys_dial *f; + + f = fp; + a = string2c(f->addr); + l = string2c(f->local); + release(); + f->ret->t0 = kdial(a, l, dir, &cfd); + acquire(); + destroy(f->ret->t1.dfd); + f->ret->t1.dfd = H; + destroy(f->ret->t1.cfd); + f->ret->t1.cfd = H; + if(f->ret->t0 == -1) + return; + + f->ret->t1.dfd = mkfd(f->ret->t0); + f->ret->t1.cfd = mkfd(cfd); + retstr(dir, &f->ret->t1.dir); +} + +void +Sys_announce(void *fp) +{ + char dir[NETPATHLEN], *a; + F_Sys_announce *f; + + f = fp; + a = string2c(f->addr); + release(); + f->ret->t0 = kannounce(a, dir); + acquire(); + destroy(f->ret->t1.dfd); + f->ret->t1.dfd = H; + destroy(f->ret->t1.cfd); + f->ret->t1.cfd = H; + if(f->ret->t0 == -1) + return; + + f->ret->t1.cfd = mkfd(f->ret->t0); + retstr(dir, &f->ret->t1.dir); +} + +void +Sys_listen(void *fp) +{ + F_Sys_listen *f; + char dir[NETPATHLEN], *d; + + f = fp; + d = string2c(f->c.dir); + release(); + f->ret->t0 = klisten(d, dir); + acquire(); + + destroy(f->ret->t1.dfd); + f->ret->t1.dfd = H; + destroy(f->ret->t1.cfd); + f->ret->t1.cfd = H; + if(f->ret->t0 == -1) + return; + + f->ret->t1.cfd = mkfd(f->ret->t0); + retstr(dir, &f->ret->t1.dir); +} + +void +Sys_sleep(void *fp) +{ + F_Sys_sleep *f; + + f = fp; + release(); + if(f->period > 0){ + if(waserror()){ + acquire(); + error(""); + } + tsleep(&up->sleep, return0, 0, f->period); + poperror(); + } + acquire(); + *f->ret = 0; +} + +void +Sys_stream(void *fp) +{ + Prog *p; + uchar *buf; + int src, dst; + F_Sys_stream *f; + int nbytes, t, n; + + f = fp; + buf = malloc(f->bufsiz); + if(buf == nil) { + kwerrstr(Enomem); + *f->ret = -1; + return; + } + + src = fdchk(f->src); + dst = fdchk(f->dst); + + p = currun(); + + release(); + t = 0; + nbytes = 0; + while(p->kill == nil) { + n = kread(src, buf+t, f->bufsiz-t); + if(n <= 0) + break; + t += n; + if(t >= f->bufsiz) { + if(kwrite(dst, buf, t) != t) { + t = 0; + break; + } + + nbytes += t; + t = 0; + } + } + if(t != 0) { + kwrite(dst, buf, t); + nbytes += t; + } + acquire(); + free(buf); + *f->ret = nbytes; +} + +void +Sys_export(void *fp) +{ + F_Sys_export *f; + + f = fp; + release(); + *f->ret = export(fdchk(f->c), string2c(f->dir), f->flag&Sys_EXPASYNC); + acquire(); +} + +void +Sys_file2chan(void *fp) +{ + int r; + Heap *h; + Channel *c; + Sys_FileIO *fio; + F_Sys_file2chan *f; + void *sv; + + h = heap(TFileIO); + + fio = H2D(Sys_FileIO*, h); + + c = cnewc(FioTread, movtmp, 16); + fio->read = c; + + c = cnewc(FioTwrite, movtmp, 16); + fio->write = c; + + f = fp; + sv = *f->ret; + *f->ret = fio; + destroy(sv); + + release(); + r = srvf2c(string2c(f->dir), string2c(f->file), fio); + acquire(); + if(r == -1) { + *f->ret = H; + destroy(fio); + } +} + +enum +{ + /* the following pctl calls can block and must release the virtual machine */ + BlockingPctl= Sys_NEWFD|Sys_FORKFD|Sys_NEWNS|Sys_FORKNS|Sys_NEWENV|Sys_FORKENV +}; + +void +Sys_pctl(void *fp) +{ + int fd; + Prog *p; + List *l; + Chan *c; + volatile struct {Pgrp *np;} np; + Pgrp *opg; + Chan *dot; + Osenv *o; + F_Sys_pctl *f; + Fgrp *fg, *ofg, *nfg; + volatile struct {Egrp *ne;} ne; + Egrp *oe; + + f = fp; + + p = currun(); + if(f->flags & BlockingPctl) + release(); + + np.np = nil; + ne.ne = nil; + if(waserror()) { + closepgrp(np.np); + closeegrp(ne.ne); + if(f->flags & BlockingPctl) + acquire(); + *f->ret = -1; + return; + } + + o = p->osenv; + if(f->flags & Sys_NEWFD) { + ofg = o->fgrp; + nfg = newfgrp(ofg); + lock(ofg); + /* file descriptors to preserve */ + for(l = f->movefd; l != H; l = l->tail) { + fd = *(int*)l->data; + if(fd >= 0 && fd <= ofg->maxfd) { + c = ofg->fd[fd]; + if(c != nil && fd < nfg->nfd && nfg->fd[fd] == nil) { + incref(c); + nfg->fd[fd] = c; + if(nfg->maxfd < fd) + nfg->maxfd = fd; + } + } + } + unlock(ofg); + o->fgrp = nfg; + closefgrp(ofg); + } + else + if(f->flags & Sys_FORKFD) { + ofg = o->fgrp; + fg = dupfgrp(ofg); + /* file descriptors to close */ + for(l = f->movefd; l != H; l = l->tail) + kclose(*(int*)l->data); + o->fgrp = fg; + closefgrp(ofg); + } + + if(f->flags & Sys_NEWNS) { + np.np = newpgrp(); + dot = o->pgrp->dot; + np.np->dot = cclone(dot); + np.np->slash = cclone(dot); + cnameclose(np.np->slash->name); + np.np->slash->name = newcname("/"); + np.np->pin = o->pgrp->pin; /* pin is ALWAYS inherited */ + np.np->nodevs = o->pgrp->nodevs; + opg = o->pgrp; + o->pgrp = np.np; + np.np = nil; + closepgrp(opg); + } + else + if(f->flags & Sys_FORKNS) { + np.np = newpgrp(); + pgrpcpy(np.np, o->pgrp); + opg = o->pgrp; + o->pgrp = np.np; + np.np = nil; + closepgrp(opg); + } + + if(f->flags & Sys_NEWENV) { + oe = o->egrp; + o->egrp = newegrp(); + closeegrp(oe); + } + else + if(f->flags & Sys_FORKENV) { + ne.ne = newegrp(); + egrpcpy(ne.ne, o->egrp); + oe = o->egrp; + o->egrp = ne.ne; + ne.ne = nil; + closeegrp(oe); + } + + if(f->flags & Sys_NEWPGRP) + newgrp(p); + + if(f->flags & Sys_NODEVS) + o->pgrp->nodevs = 1; + + poperror(); + + if(f->flags & BlockingPctl) + acquire(); + + *f->ret = p->pid; +} + +void +Sys_dirread(void *fp) +{ + + Dir *b; + int i, n; + Heap *h; + uchar *d; + void *r; + F_Sys_dirread *f; + + f = fp; + f->ret->t0 = -1; + r = f->ret->t1; + f->ret->t1 = H; + destroy(r); + release(); + n = kdirread(fdchk(f->fd), &b); + acquire(); + if(n <= 0) { + f->ret->t0 = n; + free(b); + return; + } + if(waserror()){ + free(b); + return; + } + h = heaparray(Tdir, n); + poperror(); + d = H2D(Array*, h)->data; + for(i = 0; i < n; i++) { + unpackdir(b+i, (Sys_Dir*)d); + d += Sys_Dir_size; + } + f->ret->t0 = n; + f->ret->t1 = H2D(Array*, h); + free(b); +} + +void +Sys_fauth(void *fp) +{ + + int fd; + F_Sys_fauth *f; + void *r; + + f = fp; + r = *f->ret; + *f->ret = H; + destroy(r); + release(); + fd = kfauth(fdchk(f->fd), string2c(f->aname)); + acquire(); + if(fd >= 0) + *f->ret = mkfd(fd); +} + +void +Sys_fversion(void *fp) +{ + void *r; + F_Sys_fversion *f; + int n; + char buf[20], *s; + + f = fp; + f->ret->t0 = -1; + r = f->ret->t1; + f->ret->t1 = H; + destroy(r); + s = string2c(f->version); + n = strlen(s); + if(n >= sizeof(buf)-1) + n = sizeof(buf)-1; + memmove(buf, s, n); + buf[n] = 0; + release(); + n = kfversion(fdchk(f->fd), f->msize, buf, sizeof(buf)); + acquire(); + if(n >= 0){ + f->ret->t0 = f->msize; + retnstr(buf, n, &f->ret->t1); + } +} + +void +Sys_iounit(void *fp) +{ + F_Sys_iounit *f; + + f = fp; + release(); + *f->ret = kiounit(fdchk(f->fd)); + acquire(); +} + +void +ccom(Progq **cl, Prog *p) +{ + volatile struct {Progq **cl;} vcl; + + cqadd(cl, p); + vcl.cl = cl; + if(waserror()) { + if(p->ptr != nil) { /* no killcomm */ + cqdelp(vcl.cl, p); + p->ptr = nil; + } + nexterror(); + } + cblock(p); + poperror(); +} + +void +crecv(Channel *c, void *ip) +{ + Prog *p; + REG rsav; + + if(c->send->prog == nil && c->size == 0) { + p = currun(); + p->ptr = ip; + ccom(&c->recv, p); + return; + } + + rsav = R; + R.s = &c; + R.d = ip; + irecv(); + R = rsav; +} + +void +csend(Channel *c, void *ip) +{ + Prog *p; + REG rsav; + + if(c->recv->prog == nil && (c->buf == H || c->size == c->buf->len)) { + p = currun(); + p->ptr = ip; + ccom(&c->send, p); + return; + } + + rsav = R; + R.s = ip; + R.d = &c; + isend(); + R = rsav; +} diff --git a/os/port/latin1.c b/os/port/latin1.c new file mode 100644 index 00000000..8dcb0d7e --- /dev/null +++ b/os/port/latin1.c @@ -0,0 +1,76 @@ +#include <u.h> + +/* + * The code makes two assumptions: strlen(ld) is 1 or 2; latintab[i].ld can be a + * prefix of latintab[j].ld only when j<i. + */ +struct cvlist +{ + char *ld; /* must be seen before using this conversion */ + char *si; /* options for last input characters */ + Rune *so; /* the corresponding Rune for each si entry */ +} latintab[] = { +#include "../port/latin1.h" + 0, 0, 0 +}; + +/* + * Given 5 characters k[0]..k[4], find the rune or return -1 for failure. + */ +long +unicode(Rune *k) +{ + long i, c; + + k++; /* skip 'X' */ + c = 0; + for(i=0; i<4; i++,k++){ + c <<= 4; + if('0'<=*k && *k<='9') + c += *k-'0'; + else if('a'<=*k && *k<='f') + c += 10 + *k-'a'; + else if('A'<=*k && *k<='F') + c += 10 + *k-'A'; + else + return -1; + } + return c; +} + +/* + * Given n characters k[0]..k[n-1], find the corresponding rune or return -1 for + * failure, or something < -1 if n is too small. In the latter case, the result + * is minus the required n. + */ +long +latin1(Rune *k, int n) +{ + struct cvlist *l; + int c; + char* p; + + if(k[0] == 'X') + if(n>=5) + return unicode(k); + else + return -5; + for(l=latintab; l->ld!=0; l++) + if(k[0] == l->ld[0]){ + if(n == 1) + return -2; + if(l->ld[1] == 0) + c = k[1]; + else if(l->ld[1] != k[1]) + continue; + else if(n == 2) + return -3; + else + c = k[2]; + for(p=l->si; *p!=0; p++) + if(*p == c) + return l->so[p - l->si]; + return -1; + } + return -1; +} diff --git a/os/port/latin1.h b/os/port/latin1.h new file mode 100644 index 00000000..382df2bf --- /dev/null +++ b/os/port/latin1.h @@ -0,0 +1,100 @@ + " ", " i", L"␣ı", + "!~", "-=~", L"≄≇≉", + "!", "!<=>?bmp", L"¡≮≠≯‽⊄∉⊅", + "\"*", "IUiu", L"ΪΫϊϋ", + "\"", "\"AEIOUYaeiouy", L"¨ÄËÏÖÜŸäëïöüÿ", + "$*", "fhk", L"ϕϑϰ", + "$", "BEFHILMRVaefglopv", L"ℬℰℱℋℐℒℳℛƲɑℯƒℊℓℴ℘ʋ", + "\'\"", "Uu", L"Ǘǘ", + "\'", "\'ACEILNORSUYZacegilnorsuyz", L"´ÁĆÉÍĹŃÓŔŚÚÝŹáćéģíĺńóŕśúýź", + "*", "*ABCDEFGHIKLMNOPQRSTUWXYZabcdefghiklmnopqrstuwxyz", L"∗ΑΒΞΔΕΦΓΘΙΚΛΜΝΟΠΨΡΣΤΥΩΧΗΖαβξδεφγθικλμνοπψρστυωχηζ", + "+", "-O", L"±⊕", + ",", ",ACEGIKLNORSTUacegiklnorstu", L"¸ĄÇĘĢĮĶĻŅǪŖŞŢŲąçęģįķļņǫŗşţų", + "-*", "l", L"ƛ", + "-", "+-2:>DGHILOTZbdghiltuz~", L"∓ƻ÷→ÐǤĦƗŁ⊖ŦƵƀðǥℏɨłŧʉƶ≂", + ".", ".CEGILOZceglz", L"·ĊĖĠİĿ⊙Żċėġŀż", + "/", "Oo", L"Øø", + "1", "234568", L"½⅓¼⅕⅙⅛", + "2", "-35", L"ƻ⅔⅖", + "3", "458", L"¾⅗⅜", + "4", "5", L"⅘", + "5", "68", L"⅚⅝", + "7", "8", L"⅞", + ":", "()-=", L"☹☺÷≔", + "<!", "=~", L"≨⋦", + "<", "-<=>~", L"←«≤≶≲", + "=", ":<=>OV", L"≕⋜≡⋝⊜⇒", + ">!", "=~", L"≩⋧", + ">", "<=>~", L"≷≥»≳", + "?", "!?", L"‽¿", + "@\'", "\'", L"ъ", + "@@", "\'EKSTYZekstyz", L"ьЕКСТЫЗекстыз", + "@C", "Hh", L"ЧЧ", + "@E", "Hh", L"ЭЭ", + "@K", "Hh", L"ХХ", + "@S", "CHch", L"ЩШЩШ", + "@T", "Ss", L"ЦЦ", + "@Y", "AEOUaeou", L"ЯЕЁЮЯЕЁЮ", + "@Z", "Hh", L"ЖЖ", + "@c", "h", L"ч", + "@e", "h", L"э", + "@k", "h", L"х", + "@s", "ch", L"щш", + "@t", "s", L"ц", + "@y", "aeou", L"яеёю", + "@z", "h", L"ж", + "@", "ABDFGIJLMNOPRUVXabdfgijlmnopruvx", L"АБДФГИЙЛМНОПРУВХабдфгийлмнопрувх", + "A", "E", L"Æ", + "C", "ACU", L"⋂ℂ⋃", + "Dv", "Zz", L"DŽDž", + "D", "-e", L"Ð∆", + "G", "-", L"Ǥ", + "H", "-H", L"Ħℍ", + "I", "-J", L"ƗIJ", + "L", "&-Jj|", L"⋀ŁLJLj⋁", + "M", "#48bs", L"♮♩♪♭♯", + "N", "JNj", L"NJℕNj", + "O", "*+-./=EIcoprx", L"⊛⊕⊖⊙⊘⊜ŒƢ©⊚℗®⊗", + "P", "P", L"ℙ", + "Q", "Q", L"ℚ", + "R", "R", L"ℝ", + "S", "123S", L"¹²³§", + "T", "-u", L"Ŧ⊨", + "V", "=", L"⇐", + "Y", "R", L"Ʀ", + "Z", "-ACSZ", L"Ƶℤ", + "^", "ACEGHIJOSUWYaceghijosuwy", L"ÂĈÊĜĤÎĴÔŜÛŴŶâĉêĝĥîĵôŝûŵŷ", + "_\"", "AUau", L"ǞǕǟǖ", + "_,", "Oo", L"Ǭǭ", + "_.", "Aa", L"Ǡǡ", + "_", "AEIOU_aeiou", L"ĀĒĪŌŪ¯āēīōū", + "`\"", "Uu", L"Ǜǜ", + "`", "AEIOUaeiou", L"ÀÈÌÒÙàèìòù", + "a", "ben", L"↔æ∠", + "b", "()+-0123456789=bknpqru", L"₍₎₊₋₀₁₂₃₄₅₆₇₈₉₌♝♚♞♟♛♜•", + "c", "$Oagu", L"¢©∩≅∪", + "dv", "z", L"dž", + "d", "-adegz", L"ð↓‡°†ʣ", + "e", "$lmns", L"€⋯—–∅", + "f", "a", L"∀", + "g", "$-r", L"¤ǥ∇", + "h", "-v", L"ℏƕ", + "i", "-bfjps", L"ɨ⊆∞ij⊇∫", + "l", "\"$&\'-jz|", L"“£∧‘łlj⋄∨", + "m", "iou", L"µ∈×", + "n", "jo", L"nj¬", + "o", "AOUaeiu", L"Å⊚Ůåœƣů", + "p", "Odgrt", L"℗∂¶∏∝", + "r", "\"\'O", L"”’®", + "s", "()+-0123456789=abnoprstu", L"⁽⁾⁺⁻⁰ⁱ⁴⁵⁶⁷⁸⁹⁼ª⊂ⁿº⊃√ß∍∑", + "t", "-efmsu", L"ŧ∃∴™ς⊢", + "u", "-AEGIOUaegiou", L"ʉĂĔĞĬŎŬ↑ĕğĭŏŭ", + "v\"", "Uu", L"Ǚǚ", + "v", "ACDEGIKLNORSTUZacdegijklnorstuz", L"ǍČĎĚǦǏǨĽŇǑŘŠŤǓŽǎčďěǧǐǰǩľňǒřšťǔž", + "w", "bknpqr", L"♗♔♘♙♕♖", + "x", "O", L"⊗", + "y", "$", L"¥", + "z", "-", L"ƶ", + "|", "Pp|", L"Þþ¦", + "~!", "=", L"≆", + "~", "-=AINOUainou~", L"≃≅ÃĨÑÕŨãĩñõũ≈", diff --git a/os/port/lib.h b/os/port/lib.h new file mode 100644 index 00000000..0233dc72 --- /dev/null +++ b/os/port/lib.h @@ -0,0 +1,211 @@ + +/* + * functions (possibly) linked in, complete, from libc. + */ + +/* + * mem routines + */ +extern void* memccpy(void*, void*, int, ulong); +extern void* memset(void*, int, ulong); +extern int memcmp(void*, void*, ulong); +extern void* memmove(void*, void*, ulong); +extern void* memchr(void*, int, ulong); + +/* + * string routines + */ +extern char* strcat(char*, char*); +extern char* strchr(char*, int); +extern char* strrchr(char*, int); +extern int strcmp(char*, char*); +extern char* strcpy(char*, char*); +extern char* strecpy(char*, char*, char*); +extern char* strncat(char*, char*, long); +extern char* strncpy(char*, char*, long); +extern int strncmp(char*, char*, long); +extern long strlen(char*); +extern char* strdup(char*); +extern char* strstr(char*, char*); +extern int cistrncmp(char*, char*, int); +extern int cistrcmp(char*, char*); +extern char* cistrstr(char*, char*); +extern int atoi(char*); +extern int fullrune(char*, int); + +enum +{ + UTFmax = 3, /* maximum bytes per rune */ + Runesync = 0x80, /* cannot represent part of a UTF sequence */ + Runeself = 0x80, /* rune and UTF sequences are the same (<) */ + Runeerror = 0x80, /* decoding error in UTF */ +}; + +/* + * rune routines + */ +extern int runetochar(char*, Rune*); +extern int chartorune(Rune*, char*); +extern char* utfrune(char*, long); +extern int utflen(char*); +extern int runelen(long); + +extern int abs(int); + +/* + * print routines + */ +typedef struct Fmt Fmt; +typedef int (*Fmts)(Fmt*); +struct Fmt{ + uchar runes; /* output buffer is runes or chars? */ + void *start; /* of buffer */ + void *to; /* current place in the buffer */ + void *stop; /* end of the buffer; overwritten if flush fails */ + int (*flush)(Fmt *); /* called when to == stop */ + void *farg; /* to make flush a closure */ + int nfmt; /* num chars formatted so far */ + va_list args; /* args passed to dofmt */ + int r; /* % format Rune */ + int width; + int prec; + ulong flags; +}; +extern int print(char*, ...); +extern char* seprint(char*, char*, char*, ...); +extern char* vseprint(char*, char*, char*, va_list); +extern char* smprint(char*, ...); +extern char* vsmprint(char*, va_list); +extern int snprint(char*, int, char*, ...); +extern int vsnprint(char*, int, char*, va_list); +extern int sprint(char*, char*, ...); + +extern int fmtinstall(int, int (*)(Fmt*)); +extern void quotefmtinstall(void); +extern int fmtprint(Fmt*, char*, ...); +extern int fmtstrcpy(Fmt*, char*); + +#pragma varargck argpos fmtprint 2 +#pragma varargck argpos print 1 +#pragma varargck argpos seprint 3 +#pragma varargck argpos snprint 3 +#pragma varargck argpos sprint 2 +#pragma varargck argpos vseprint 3 +#pragma varargck argpos vsnprint 3 + +extern int fltconv(Fmt*); + +/* + * math + */ +extern int isNaN(double); +extern int isInf(double, int); +extern double floor(double); +extern double frexp(double, int*); +extern double pow10(int); + +/* + * one-of-a-kind + */ +extern char* cleanname(char*); +extern ulong getcallerpc(void*); + +extern long strtol(char*, char**, int); +extern ulong strtoul(char*, char**, int); +extern vlong strtoll(char*, char**, int); +extern uvlong strtoull(char*, char**, int); +extern char etext[]; +extern char edata[]; +extern char end[]; +extern int getfields(char*, char**, int, int, char*); +extern int tokenize(char*, char**, int); +extern int dec64(uchar*, int, char*, int); + +extern int toupper(int); +extern char* netmkaddr(char*, char*, char*); +extern int myetheraddr(uchar*, char*); +extern int parseether(uchar*, char*); + +/* + * network dialling + */ +#define NETPATHLEN 40 + +/* + * Syscall data structures + */ +#define MORDER 0x0003 /* mask for bits defining order of mounting */ +#define MREPL 0x0000 /* mount replaces object */ +#define MBEFORE 0x0001 /* mount goes before others in union directory */ +#define MAFTER 0x0002 /* mount goes after others in union directory */ +#define MCREATE 0x0004 /* permit creation in mounted directory */ +#define MCACHE 0x0010 /* cache some data */ +#define MMASK 0x0017 /* all bits on */ + +#define OREAD 0 /* open for read */ +#define OWRITE 1 /* write */ +#define ORDWR 2 /* read and write */ +#define OEXEC 3 /* execute, == read but check execute permission */ +#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */ +#define OCEXEC 32 /* or'ed in, close on exec */ +#define ORCLOSE 64 /* or'ed in, remove on close */ +#define OEXCL 0x1000 /* or'ed in, exclusive create */ + +#define NCONT 0 /* continue after note */ +#define NDFLT 1 /* terminate after note */ +#define NSAVE 2 /* clear note but hold state */ +#define NRSTR 3 /* restore saved state */ + +typedef struct Qid Qid; +typedef struct Dir Dir; +typedef struct Waitmsg Waitmsg; + +#define ERRMAX 128 /* max length of error string */ +#define KNAMELEN 28 /* max length of name held in kernel */ + +/* bits in Qid.type */ +#define QTDIR 0x80 /* type bit for directories */ +#define QTAPPEND 0x40 /* type bit for append only files */ +#define QTEXCL 0x20 /* type bit for exclusive use files */ +#define QTMOUNT 0x10 /* type bit for mounted channel */ +#define QTAUTH 0x08 /* type bit for authentication file */ +#define QTFILE 0x00 /* plain file */ + +/* bits in Dir.mode */ +#define DMDIR 0x80000000 /* mode bit for directories */ +#define DMAPPEND 0x40000000 /* mode bit for append only files */ +#define DMEXCL 0x20000000 /* mode bit for exclusive use files */ +#define DMMOUNT 0x10000000 /* mode bit for mounted channel */ +#define DMREAD 0x4 /* mode bit for read permission */ +#define DMWRITE 0x2 /* mode bit for write permission */ +#define DMEXEC 0x1 /* mode bit for execute permission */ + +struct Qid +{ + uvlong path; + ulong vers; + uchar type; +}; + +struct Dir { + /* system-modified data */ + ushort type; /* server type */ + uint dev; /* server subtype */ + /* file data */ + Qid qid; /* unique id from server */ + ulong mode; /* permissions */ + ulong atime; /* last read time */ + ulong mtime; /* last write time */ + vlong length; /* file length: see <u.h> */ + char *name; /* last element of path */ + char *uid; /* owner name */ + char *gid; /* group name */ + char *muid; /* last modifier name */ +}; + +struct Waitmsg +{ + int pid; /* of loved one */ + ulong time[3]; /* of loved one and descendants */ + char msg[ERRMAX]; /* actually variable-size in user mode */ +}; diff --git a/os/port/log.c b/os/port/log.c new file mode 100644 index 00000000..14708233 --- /dev/null +++ b/os/port/log.c @@ -0,0 +1,186 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +static char Ebadlogctl[] = "unknown log ctl message"; + +void +logopen(Log *alog) +{ + lock(alog); + if(waserror()){ + unlock(alog); + nexterror(); + } + if(alog->opens == 0){ + if(alog->nlog == 0) + alog->nlog = 4*1024; + if(alog->minread == 0) + alog->minread = 1; + if(alog->buf == nil) + alog->buf = malloc(alog->nlog); + alog->rptr = alog->buf; + alog->end = alog->buf + alog->nlog; + alog->len = 0; + } + alog->opens++; + unlock(alog); + poperror(); +} + +void +logclose(Log *alog) +{ + lock(alog); + alog->opens--; + if(alog->opens == 0){ + free(alog->buf); + alog->buf = nil; + } + unlock(alog); +} + +static int +logready(void *a) +{ + Log *alog = a; + + return alog->len >= alog->minread; +} + +long +logread(Log *alog, void *a, ulong, long n) +{ + int i, d; + char *p, *rptr; + + qlock(&alog->readq); + if(waserror()){ + qunlock(&alog->readq); + nexterror(); + } + + for(;;){ + lock(alog); + if(alog->len >= alog->minread || alog->len >= n){ + if(n > alog->len) + n = alog->len; + d = 0; + rptr = alog->rptr; + alog->rptr += n; + if(alog->rptr >= alog->end){ + d = alog->rptr - alog->end; + alog->rptr = alog->buf + d; + } + alog->len -= n; + unlock(alog); + + i = n-d; + p = a; + memmove(p, rptr, i); + memmove(p+i, alog->buf, d); + break; + } + else + unlock(alog); + + sleep(&alog->readr, logready, alog); + } + + qunlock(&alog->readq); + poperror(); + + return n; +} + +char* +logctl(Log *alog, int argc, char *argv[], Logflag *flags) +{ + int i, set; + Logflag *fp; + + if(argc < 2) + return Ebadlogctl; + + if(strcmp("set", argv[0]) == 0) + set = 1; + else if(strcmp("clear", argv[0]) == 0) + set = 0; + else + return Ebadlogctl; + + for(i = 1; i < argc; i++){ + for(fp = flags; fp->name; fp++) + if(strcmp(fp->name, argv[i]) == 0) + break; + if(fp->name == nil) + continue; + if(set) + alog->logmask |= fp->mask; + else + alog->logmask &= ~fp->mask; + } + + return nil; +} + +void +logn(Log *alog, int mask, void *buf, int n) +{ + char *fp, *t; + int dowake, i; + + if(!(alog->logmask & mask)) + return; + + if(alog->opens == 0) + return; + + if(n > alog->nlog) + return; + + lock(alog); + i = alog->len + n - alog->nlog; + if(i > 0){ + alog->len -= i; + alog->rptr += i; + if(alog->rptr >= alog->end) + alog->rptr = alog->buf + (alog->rptr - alog->end); + } + t = alog->rptr + alog->len; + fp = buf; + alog->len += n; + while(n-- > 0){ + if(t >= alog->end) + t = alog->buf + (t - alog->end); + *t++ = *fp++; + } + dowake = alog->len >= alog->minread; + unlock(alog); + + if(dowake) + wakeup(&alog->readr); +} + +void +logb(Log *alog, int mask, char *fmt, ...) +{ + int n; + va_list arg; + char buf[128]; + + if(!(alog->logmask & mask)) + return; + + if(alog->opens == 0) + return; + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + + logn(alog, mask, buf, n); +} diff --git a/os/port/master b/os/port/master new file mode 100644 index 00000000..fe8ffa00 --- /dev/null +++ b/os/port/master @@ -0,0 +1,89 @@ +/ root +A audio +A beep +B boot +B bridge +D ssl +E mpeg +F flash +F tinyfs +G fpga +G gpio +H ata +I ip +J i2c +J isdn +K kprof +L ds1620 +L lm78 +L lpt +M mnt +O omap +O sm # altivec soft modem +P prof +P tad +P telephony +Q ssfdc +R cdrom +R msg +S scsi +T kprof # old letter +T pri +T touch +U usb +V tv +V voice +W flash +X XXX +X ftl +Y pbus +Z doc +Z sadb +Z test +Z zt5512 +a atm +a beep +b dbg +c cons +d dup +e env +f floppy +i draw +k kprof +k fs +l ether +l lance +m midi +m mouse +m pointer +p prog +r rtc +s srv +t duart +t ns16552 +t uart +t z8530 +v vid +v vsc +w sd +x bench +y i82365 +y pcmcia +y sapcm +¤ cap +Û isp1161 +Û usbh +Σ sign +τ tk +↓ power + +α local use +β local use +γ local use +δ local use +ε local use +ζ local use +η local use +θ local use +ι local use +κ local use diff --git a/os/port/master.local b/os/port/master.local new file mode 100644 index 00000000..5d9438d3 --- /dev/null +++ b/os/port/master.local @@ -0,0 +1,10 @@ +α local use +β local use +γ local use +δ local use +ε local use +ζ local use +η local use +θ local use +ι local use +κ local use diff --git a/os/port/mkdevc b/os/port/mkdevc new file mode 100644 index 00000000..afb7caa7 --- /dev/null +++ b/os/port/mkdevc @@ -0,0 +1,216 @@ +$AWK -v 'objtype='$OBJTYPE ' +BEGIN{ + if(ARGC < 2) + exit +} + +/^$/{ + next; +} +/^#/{ + next; +} +collect && /^[^ \t]/{ + collect = 0; +} +collect && section ~ "dev"{ + dev[ndev++] = $1; + if($1 ~ "vga") + devvga = 1; +} +collect && section ~ "ip"{ + ip[nip++] = $1; +} +collect && (section ~ "ether" || section ~ "link") { + link[nlink++] = $1; +} +collect && section ~ "mod"{ + mod[nmod++] = $1; +} +collect && section ~ "vga"{ + option = 0; + for(i = 2; i < NF; i++){ + if($i ~ "[+]hwgc"){ + hwgc[nhwgc++] = $1; + option = 1; + } else if($i ~ "[+=]hwgc"){ + hwgc[nhwgc++] = $1; + if(option == 0) + option = 2; + } + } + if(option < 2) + vga[nvga++] = $1; +} +collect && section ~ "misc"{ + misc[nmisc++] = $1; + if($1 ~ "^arch.*") + arch[narch++] = $1; + else if($1 ~ "^sd.*") + sdifc[nsdifc++] = $1; + else if($1 ~ "^uart.*") + physuart[nphysuart++] = substr($1, 5, length($1)-4) "physuart"; + else if($1 ~ "^vga.*"){ + if(NF == 1) + vgadev[nvgadev++] = $1; + else for(i = 2; i <= NF; i++){ + if($i ~ "[+]cur") + vgadev[nvgadev++] = $1; + if($i ~ "[+=]cur") + vgacur[nvgacur++] = $1; + } + } + else if($1 ~ ".*\.root"){ + x = substr($1, 1, index($1, ".")-1); + if(x ~ "(dossrv|kfs)") + x = "fs"; + fs[nfs++] = x; + } +} +collect && section ~ "port"{ + port[nport++] = $0; +} +collect && section ~ "code"{ + code[ncode++] = $0; +} +$0 ~ /^[^ \t]/{ + if($0 ~ "(code|dev|ether|ip|lib|link|mod|misc|port|root|vga)"){ + section = $0; + collect = 1; + } + next; +} + +END{ + if(ARGC < 2) + exit "usage" + + printf "#include \"u.h\"\n" + printf "#include \"../port/lib.h\"\n" + printf "#include \"mem.h\"\n" + printf "#include \"dat.h\"\n" + printf "#include \"fns.h\"\n" + printf "#include \"io.h\"\n" + if(nphysuart) + printf "#include \"../port/uart.h\"\n" + printf "#include \"../port/error.h\"\n" + printf "#include \"interp.h\"\n\n" + printf "#include \"%s.root.h\"\n\n", ARGV[1]; + + printf "ulong ndevs = %d;\n", ndev+8 + for(i = 0; i < ndev; i++) + printf "extern Dev %sdevtab;\n", dev[i]; + printf "Dev* devtab[%d]={\n", ndev+8 + for(i = 0; i < ndev; i++) + printf "\t&%sdevtab,\n", dev[i]; + printf "\tnil,\n};\n\n"; + + + for(i = 0; i < nfs; i++){ + printf "extern uchar %scode[];\n", fs[i]; + printf "extern ulong %slen;\n", fs[i]; + } + for(i = 0; i < nlink; i++) + printf "extern void %slink(void);\n", link[i]; + + printf "void links(void){\n"; + for(i = 0; i < nfs; i++) + printf "\taddrootfile(\"%s\", %scode, %slen);\n", fs[i], fs[i], fs[i]; + for(i = 0; i < nlink; i++) + printf "\t%slink();\n", link[i]; + printf "}\n\n"; + + for(i = 0; i < nmod; i++) + printf "extern void %smodinit(void);\n", mod[i]; + printf "void modinit(void){\n"; + for(i = 0; i < nmod; i++) + printf("\t%smodinit();\n",mod[i]); + printf("}\n\n"); + + if(narch || objtype ~ "386"){ + for(i = 0; i < narch; i++) + printf "extern PCArch %s;\n", arch[i]; + printf "PCArch* knownarch[] = {\n"; + for(i = 0; i < narch; i++) + printf "\t&%s,\n", arch[i]; + printf "\tnil,\n};\n\n"; + } + + if(nsdifc){ + printf "#include \"../port/sd.h\"\n"; + for(i = 0; i < nsdifc; i++) + printf "extern SDifc %sifc;\n", sdifc[i]; + printf "SDifc* sdifc[] = {\n"; + for(i = 0; i < nsdifc; i++) + printf "\t&%sifc,\n", sdifc[i]; + printf "\tnil,\n};\n\n"; + } + + if(nphysuart){ + for(i = 0; i < nphysuart; i++) + printf "extern PhysUart %s;\n", physuart[i]; + printf "PhysUart* physuart[] = {\n"; + for(i = 0; i < nphysuart; i++) + printf "\t&%s,\n", physuart[i]; + printf "\tnil,\n};\n\n"; + } + + if(devvga || nvga || nvgadev){ + printf "#include <draw.h>\n" + printf "#include <memdraw.h>\n" + + if(nvga){ + printf "#include \"vga.h\"\n" + for(i = 0; i < nvga; i++) + printf "extern Vgac %s;\n", vga[i]; + printf "Vgac* knownvga[] = {\n"; + for(i = 0; i < nvga; i++) + printf "\t&%s,\n", vga[i]; + printf "\tnil,\n};\n\n"; + + if(nhwgc){ + for(i = 0; i < nhwgc; i++) + printf "extern Hwgc %shwgc;\n", hwgc[i]; + printf "Hwgc* knownhwgc[] = {\n"; + for(i = 0; i < nhwgc; i++) + printf "\t&%shwgc,\n", hwgc[i]; + printf "\tnil,\n};\n\n"; + } + } + + if(nvgadev){ + printf "#include \"screen.h\"\n"; + for(i = 0; i < nvgadev; i++) + printf "extern VGAdev %sdev;\n", vgadev[i]; + printf "VGAdev* vgadev[] = {\n"; + for(i = 0; i < nvgadev; i++) + printf "\t&%sdev,\n", vgadev[i]; + printf "\tnil,\n};\n\n"; + + for(i = 0; i < nvgacur; i++) + printf "extern VGAcur %scur;\n", vgacur[i]; + printf "VGAcur* vgacur[] = {\n"; + for(i = 0; i < nvgacur; i++) + printf "\t&%scur,\n", vgacur[i]; + printf "\tnil,\n};\n\n"; + } + } + + if(nip){ + printf "#include \"../ip/ip.h\"\n"; + for(i = 0; i < nip; i++) + printf "extern void %sinit(Fs*);\n", ip[i]; + printf "void (*ipprotoinit[])(Fs*) = {\n"; + for(i = 0; i < nip; i++) + printf "\t%sinit,\n", ip[i]; + printf "\tnil,\n};\n\n"; + } + + for(i = 0; i < ncode; i++) + printf "%s\n", code[i]; + + printf "char* conffile = \"%s\";\n", ARGV[1]; + printf "ulong kerndate = KERNDATE;\n"; + + exit +}' $* diff --git a/os/port/mkdevlist b/os/port/mkdevlist new file mode 100644 index 00000000..6fdc6866 --- /dev/null +++ b/os/port/mkdevlist @@ -0,0 +1,86 @@ +$AWK ' +BEGIN{ + var["dev"] = "DEVS="; + var["vga"] = "VGAS="; + var["ether"] = "ETHERS="; + var["init"] = "INIT="; + var["ip"] = "IP="; + var["port"] = "PORT="; + var["misc"] = "MISC="; + var["lib"] = "LIBS="; + var["link"] = "LINKS="; + var["root"] = "ROOTFILES="; + infernoroot = ENVIRON["ROOT"]; +} +/^$/{ next; +} +/^#/{ next; +} +/^env/{ + inenv = 1; + next; +} +inenv != 0 && /^[ ]/{ + sub("^[ ]*", "", $0) + printf "%s\n", $0 + next +} +/^(code|dev|ether|init|ip|lib|link|mod|misc|port|root|vga)/{ + inenv = 0; + if(current != "") + print current; + current = var[$1]; + type = $1; + next; +} +/^[^ ]/ { + inenv = 0; + if(current != "") + print current; + current = ""; +} +current && /^[ ]/{ + if(type == "dev") + file = "dev" $1; + else if(type == "root"){ + if (NF > 1) + file = $2; + else if ($1 == "/osinit.dis") + next; # handled via explicit dependency + else + file = $1; + if(have[file] == 0){ + current = current " " infernoroot file; + have[file]++; + } + next; + } + else + file = $1; + if(type == "init" || type == "lib") + current = current " " file; + else if(have[file] == 0){ + if(type == "lib") + current = current " " file; + else + current = current " " file "'.$O'"; + have[file]++; + } + for(i = 2; i <= NF; i++){ + if($i !~ "^[+=-].*"){ + if(have[$i] == 0){ + others[++nothers] = $i; + have[$i]++; + } + } + } + next; +} +END{ + if(current != "") + print current; + for(i = 1; i <= nothers; i++) + x = x " " others[i] "'.$O' "; + if(x) + printf("OTHERS=%s\n", x); +}' $* diff --git a/os/port/mkroot b/os/port/mkroot new file mode 100644 index 00000000..9bcf3598 --- /dev/null +++ b/os/port/mkroot @@ -0,0 +1,119 @@ +$AWK ' +BEGIN{ + if (ARGC < 2) + exit "usage"; + + conf = ARGV[1]; + infernoroot = ENVIRON["ROOT"]; + init = ENVIRON["INIT"]; + data2s = ENVIRON["DATA2S"]; + nroot = 0; +} +/^$/{ + next; +} +/^#/{ + next; +} +collect && /^[^ \t]/{ + collect = 0; +} +collect && section ~ "root"{ + dst[nroot] = $1; + if (NF > 1) + src[nroot] = infernoroot $2; + else if (dst[nroot] == "/osinit.dis") + src[nroot] = infernoroot "/os/init/" init ".dis"; + else + src[nroot] = infernoroot $1; + for(i=0; i<nroot; i++) + if(dst[i] == dst[nroot]) + break; + if(i == nroot) + nroot++; +} +$0 ~ /^[^ \t]/{ + if($0 ~ "(code|dev|ether|ip|lib|link|mod|misc|port|root|vga)"){ + section = $0; + collect = 1; + } + next; +} +END{ + rootdata = conf ".root.s"; + system("rm -f " rootdata); + print("/* Generated by /os/port/mkroot */") >rootdata; + close(rootdata); + isdir[0] = 1; + dotdot[0] = 0; + qid = 1; + for (i = 0; i < nroot; i++) { + ncomp = split(dst[i], comp, "/"); + if (comp[1] != "" || ncomp < 2) + continue; + q = 0; + for (j = 2; j <= ncomp; j++) { + key = q "/" comp[j]; + if (walk[key] == 0) { + walk[key] = qid; + dotdot[qid] = q; + q = qid++; + name[q] = comp[j]; + if (j < ncomp) + isdir[q] = 1; + } + else + q = walk[key]; + } + if (system("test -d " src[i]) == 0) + isdir[q] = 1; + else { + if (system(data2s " root" q " <" src[i] " >>" rootdata) != 0) + exit 1; + print("extern unsigned char root" q "code[];"); + print("extern int root" q "len;"); + } + } + + x = 1; + sort[0] = 0; + unsort[0] = 0; + for (q = 0; q < qid; q++) { + if (isdir[q]) { + nchild[q] = 0; + for (q2 = 1; q2 < qid; q2++) { + if (dotdot[q2] == q) { + if (nchild[q]++ == 0) + child0[q] = x; + sort[q2] = x++; + unsort[sort[q2]] = q2; + } + } + } + } + + print("int rootmaxq = " qid ";"); + + print("Dirtab roottab[" qid "] = {"); + for (oq = 0; oq < qid; oq++) { + q = unsort[oq]; + if (!isdir[q]) + print("\t\"" name[q] "\",\t{" oq ", 0, QTFILE},\t", "0,\t0444,"); + else + print("\t\"" name[q] "\",\t{" oq ", 0, QTDIR},\t", "0,\t0555,"); + } + print("};"); + + print("Rootdata rootdata[" qid "] = {"); + for (oq = 0; oq < qid; oq++) { + q = unsort[oq]; + if (!isdir[q]) + print("\t" sort[dotdot[q]] ",\t", "root" q "code,\t", "0,\t", "&root" q "len,"); + else if (nchild[q]) + print("\t" sort[dotdot[q]] ",\t", "&roottab[" child0[q] "],\t", nchild[q] ",\tnil,"); + else + print("\t" sort[dotdot[q]] ",\t", "nil,\t", "0,\t", "nil,"); + } + print("};"); +} +' $1 >$1.root.h diff --git a/os/port/mul64fract.c b/os/port/mul64fract.c new file mode 100644 index 00000000..ca627073 --- /dev/null +++ b/os/port/mul64fract.c @@ -0,0 +1,39 @@ +#include <u.h> + +/* mul64fract(uvlong*r, uvlong a, uvlong b) + * + * Multiply two 64 numbers and return the middle 64 bits of the 128 bit result. + * + * The assumption is that one of the numbers is a + * fixed point number with the integer portion in the + * high word and the fraction in the low word. + * + * There should be an assembler version of this routine + * for each architecture. This one is intended to + * make ports easier. + * + * ignored r0 = lo(a0*b0) + * lsw of result r1 = hi(a0*b0) +lo(a0*b1) +lo(a1*b0) + * msw of result r2 = hi(a0*b1) +hi(a1*b0) +lo(a1*b1) + * ignored r3 = hi(a1*b1) + */ + +void +mul64fract(uvlong *r, uvlong a, uvlong b) +{ + uvlong bh, bl; + uvlong ah, al; + uvlong res; + + bl = b & 0xffffffffULL; + bh = b >> 32; + al = a & 0xffffffffULL; + ah = a >> 32; + + res = (al*bl)>>32; + res += (al*bh); + res += (ah*bl); + res += (ah*bh)<<32; + + *r = res; +} diff --git a/os/port/netaux.c b/os/port/netaux.c new file mode 100644 index 00000000..0c5d6a55 --- /dev/null +++ b/os/port/netaux.c @@ -0,0 +1,67 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "../port/netif.h" + + +void +hnputv(void *p, vlong v) +{ + uchar *a; + + a = p; + hnputl(a, v>>32); + hnputl(a+4, v); +} + +void +hnputl(void *p, ulong v) +{ + uchar *a; + + a = p; + a[0] = v>>24; + a[1] = v>>16; + a[2] = v>>8; + a[3] = v; +} + +void +hnputs(void *p, ushort v) +{ + uchar *a; + + a = p; + a[0] = v>>8; + a[1] = v; +} + +vlong +nhgetv(void *p) +{ + uchar *a; + + a = p; + return ((vlong)nhgetl(a) << 32) | nhgetl(a+4); +} + +ulong +nhgetl(void *p) +{ + uchar *a; + + a = p; + return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0); +} + +ushort +nhgets(void *p) +{ + uchar *a; + + a = p; + return (a[0]<<8)|(a[1]<<0); +} diff --git a/os/port/netif.c b/os/port/netif.c new file mode 100644 index 00000000..60567f5e --- /dev/null +++ b/os/port/netif.c @@ -0,0 +1,671 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "../port/netif.h" + +static int netown(Netfile*, char*, int); +static int openfile(Netif*, int); +static char* matchtoken(char*, char*); +static char* netmulti(Netif*, Netfile*, uchar*, int); +static int parseaddr(uchar*, char*, int); + +/* + * set up a new network interface + */ +void +netifinit(Netif *nif, char *name, int nfile, ulong limit) +{ + strncpy(nif->name, name, KNAMELEN-1); + nif->name[KNAMELEN-1] = 0; + nif->nfile = nfile; + nif->f = malloc(nfile*sizeof(Netfile*)); + if(nif->f) + memset(nif->f, 0, nfile*sizeof(Netfile*)); + else + nif->nfile = 0; + nif->limit = limit; +} + +/* + * generate a 3 level directory + */ +static int +netifgen(Chan *c, char*, Dirtab *vp, int, int i, Dir *dp) +{ + Qid q; + Netif *nif = (Netif*)vp; + Netfile *f; + int t; + int perm; + char *o; + + q.type = QTFILE; + q.vers = 0; + + /* top level directory contains the name of the network */ + if(c->qid.path == 0){ + switch(i){ + case DEVDOTDOT: + q.path = 0; + q.type = QTDIR; + devdir(c, q, ".", 0, eve, 0555, dp); + break; + case 0: + q.path = N2ndqid; + q.type = QTDIR; + strcpy(up->genbuf, nif->name); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + break; + default: + return -1; + } + return 1; + } + + /* second level contains clone plus all the conversations */ + t = NETTYPE(c->qid.path); + if(t == N2ndqid || t == Ncloneqid || t == Naddrqid){ + switch(i) { + case DEVDOTDOT: + q.type = QTDIR; + q.path = 0; + devdir(c, q, ".", 0, eve, DMDIR|0555, dp); + break; + case 0: + q.path = Ncloneqid; + devdir(c, q, "clone", 0, eve, 0666, dp); + break; + case 1: + q.path = Naddrqid; + devdir(c, q, "addr", 0, eve, 0666, dp); + break; + case 2: + q.path = Nstatqid; + devdir(c, q, "stats", 0, eve, 0444, dp); + break; + case 3: + q.path = Nifstatqid; + devdir(c, q, "ifstats", 0, eve, 0444, dp); + break; + default: + i -= 4; + if(i >= nif->nfile) + return -1; + if(nif->f[i] == 0) + return 0; + q.type = QTDIR; + q.path = NETQID(i, N3rdqid); + sprint(up->genbuf, "%d", i); + devdir(c, q, up->genbuf, 0, eve, DMDIR|0555, dp); + break; + } + return 1; + } + + /* third level */ + f = nif->f[NETID(c->qid.path)]; + if(f == 0) + return 0; + if(*f->owner){ + o = f->owner; + perm = f->mode; + } else { + o = eve; + perm = 0666; + } + switch(i){ + case DEVDOTDOT: + q.type = QTDIR; + q.path = N2ndqid; + strcpy(up->genbuf, nif->name); + devdir(c, q, up->genbuf, 0, eve, DMDIR|0555, dp); + break; + case 0: + q.path = NETQID(NETID(c->qid.path), Ndataqid); + devdir(c, q, "data", 0, o, perm, dp); + break; + case 1: + q.path = NETQID(NETID(c->qid.path), Nctlqid); + devdir(c, q, "ctl", 0, o, perm, dp); + break; + case 2: + q.path = NETQID(NETID(c->qid.path), Nstatqid); + devdir(c, q, "stats", 0, eve, 0444, dp); + break; + case 3: + q.path = NETQID(NETID(c->qid.path), Ntypeqid); + devdir(c, q, "type", 0, eve, 0444, dp); + break; + case 4: + q.path = NETQID(NETID(c->qid.path), Nifstatqid); + devdir(c, q, "ifstats", 0, eve, 0444, dp); + break; + default: + return -1; + } + return 1; +} + +Walkqid* +netifwalk(Netif *nif, Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, (Dirtab *)nif, 0, netifgen); +} + +Chan* +netifopen(Netif *nif, Chan *c, int omode) +{ + int id; + Netfile *f; + + id = 0; + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Eperm); + } else { + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + case Nctlqid: + id = NETID(c->qid.path); + openfile(nif, id); + break; + case Ncloneqid: + id = openfile(nif, -1); + c->qid.path = NETQID(id, Nctlqid); + break; + default: + if(omode != OREAD) + error(Ebadarg); + } + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + case Nctlqid: + f = nif->f[id]; + if(netown(f, up->env->user, omode&7) < 0) + error(Eperm); + break; + } + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + c->iounit = qiomaxatomic; + return c; +} + +long +netifread(Netif *nif, Chan *c, void *a, long n, ulong offset) +{ + int i, j; + Netfile *f; + char *p; + + if(c->qid.type&QTDIR) + return devdirread(c, a, n, (Dirtab*)nif, 0, netifgen); + + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + f = nif->f[NETID(c->qid.path)]; + return qread(f->in, a, n); + case Nctlqid: + return readnum(offset, a, n, NETID(c->qid.path), NUMSIZE); + case Nstatqid: + p = malloc(READSTR); + if(p == nil) + return 0; + j = snprint(p, READSTR, "in: %d\n", nif->inpackets); + j += snprint(p+j, READSTR-j, "link: %d\n", nif->link); + j += snprint(p+j, READSTR-j, "out: %d\n", nif->outpackets); + j += snprint(p+j, READSTR-j, "crc errs: %d\n", nif->crcs); + j += snprint(p+j, READSTR-j, "overflows: %d\n", nif->overflows); + j += snprint(p+j, READSTR-j, "soft overflows: %d\n", nif->soverflows); + j += snprint(p+j, READSTR-j, "framing errs: %d\n", nif->frames); + j += snprint(p+j, READSTR-j, "buffer errs: %d\n", nif->buffs); + j += snprint(p+j, READSTR-j, "output errs: %d\n", nif->oerrs); + j += snprint(p+j, READSTR-j, "prom: %d\n", nif->prom); + j += snprint(p+j, READSTR-j, "mbps: %d\n", nif->mbps); + j += snprint(p+j, READSTR-j, "addr: "); + for(i = 0; i < nif->alen; i++) + j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]); + snprint(p+j, READSTR-j, "\n"); + n = readstr(offset, a, n, p); + free(p); + return n; + case Naddrqid: + p = malloc(READSTR); + if(p == nil) + return 0; + j = 0; + for(i = 0; i < nif->alen; i++) + j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]); + n = readstr(offset, a, n, p); + free(p); + return n; + case Ntypeqid: + f = nif->f[NETID(c->qid.path)]; + return readnum(offset, a, n, f->type, NUMSIZE); + case Nifstatqid: + return 0; + } + error(Ebadarg); + return -1; /* not reached */ +} + +Block* +netifbread(Netif *nif, Chan *c, long n, ulong offset) +{ + if((c->qid.type & QTDIR) || NETTYPE(c->qid.path) != Ndataqid) + return devbread(c, n, offset); + + return qbread(nif->f[NETID(c->qid.path)]->in, n); +} + +/* + * make sure this type isn't already in use on this device + */ +static int +typeinuse(Netif *nif, int type) +{ + Netfile *f, **fp, **efp; + + if(type <= 0) + return 0; + + efp = &nif->f[nif->nfile]; + for(fp = nif->f; fp < efp; fp++){ + f = *fp; + if(f == 0) + continue; + if(f->type == type) + return 1; + } + return 0; +} + +/* + * the devxxx.c that calls us handles writing data, it knows best + */ +long +netifwrite(Netif *nif, Chan *c, void *a, long n) +{ + Netfile *f; + int type; + char *p, buf[64]; + uchar binaddr[Nmaxaddr]; + + if(NETTYPE(c->qid.path) != Nctlqid) + error(Eperm); + + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + memmove(buf, a, n); + buf[n] = 0; + + if(waserror()){ + qunlock(nif); + nexterror(); + } + + qlock(nif); + f = nif->f[NETID(c->qid.path)]; + if((p = matchtoken(buf, "connect")) != 0){ + type = atoi(p); + if(typeinuse(nif, type)) + error(Einuse); + f->type = type; + if(f->type < 0) + nif->all++; + } else if(matchtoken(buf, "promiscuous")){ + if(f->prom == 0){ + if(nif->prom == 0 && nif->promiscuous != nil) + nif->promiscuous(nif->arg, 1); + f->prom = 1; + nif->prom++; + } + } else if((p = matchtoken(buf, "scanbs")) != 0){ + /* scan for base stations */ + if(f->scan == 0){ + type = atoi(p); + if(type < 5) + type = 5; + if(nif->scanbs != nil) + nif->scanbs(nif->arg, type); + f->scan = type; + nif->scan++; + } + } else if(matchtoken(buf, "bridge")){ + f->bridge = 1; + } else if(matchtoken(buf, "headersonly")){ + f->headersonly = 1; + } else if((p = matchtoken(buf, "addmulti")) != 0){ + if(parseaddr(binaddr, p, nif->alen) < 0) + error("bad address"); + p = netmulti(nif, f, binaddr, 1); + if(p) + error(p); + } else if((p = matchtoken(buf, "remmulti")) != 0){ + if(parseaddr(binaddr, p, nif->alen) < 0) + error("bad address"); + p = netmulti(nif, f, binaddr, 0); + if(p) + error(p); + } else + n = -1; + qunlock(nif); + poperror(); + return n; +} + +int +netifwstat(Netif *nif, Chan *c, uchar *db, int n) +{ + Dir *dir; + Netfile *f; + int m; + + f = nif->f[NETID(c->qid.path)]; + if(f == 0) + error(Enonexist); + + if(netown(f, up->env->user, OWRITE) < 0) + error(Eperm); + + dir = smalloc(sizeof(Dir)+n); + m = convM2D(db, n, &dir[0], (char*)&dir[1]); + if(m == 0){ + free(dir); + error(Eshortstat); + } + if(!emptystr(dir[0].uid)) + strncpy(f->owner, dir[0].uid, KNAMELEN); + if(dir[0].mode != ~0UL) + f->mode = dir[0].mode; + free(dir); + return m; +} + +int +netifstat(Netif *nif, Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, (Dirtab *)nif, 0, netifgen); +} + +void +netifclose(Netif *nif, Chan *c) +{ + Netfile *f; + int t; + Netaddr *ap; + + if((c->flag & COPEN) == 0) + return; + + t = NETTYPE(c->qid.path); + if(t != Ndataqid && t != Nctlqid) + return; + + f = nif->f[NETID(c->qid.path)]; + qlock(f); + if(--(f->inuse) == 0){ + if(f->prom){ + qlock(nif); + if(--(nif->prom) == 0 && nif->promiscuous != nil) + nif->promiscuous(nif->arg, 0); + qunlock(nif); + f->prom = 0; + } + if(f->scan){ + qlock(nif); + if(--(nif->scan) == 0 && nif->scanbs != nil) + nif->scanbs(nif->arg, 0); + qunlock(nif); + f->prom = 0; + f->scan = 0; + } + if(f->nmaddr){ + qlock(nif); + t = 0; + for(ap = nif->maddr; ap; ap = ap->next){ + if(f->maddr[t/8] & (1<<(t%8))) + netmulti(nif, f, ap->addr, 0); + } + qunlock(nif); + f->nmaddr = 0; + } + if(f->type < 0){ + qlock(nif); + --(nif->all); + qunlock(nif); + } + f->owner[0] = 0; + f->type = 0; + f->bridge = 0; + f->headersonly = 0; + qclose(f->in); + } + qunlock(f); +} + +Lock netlock; + +static int +netown(Netfile *p, char *o, int omode) +{ + static int access[] = { 0400, 0200, 0600, 0100 }; + int mode; + int t; + + lock(&netlock); + if(*p->owner){ + if(strncmp(o, p->owner, KNAMELEN) == 0) /* User */ + mode = p->mode; + else if(strncmp(o, eve, KNAMELEN) == 0) /* Bootes is group */ + mode = p->mode<<3; + else + mode = p->mode<<6; /* Other */ + + t = access[omode&3]; + if((t & mode) == t){ + unlock(&netlock); + return 0; + } else { + unlock(&netlock); + return -1; + } + } + strncpy(p->owner, o, KNAMELEN); + p->mode = 0660; + unlock(&netlock); + return 0; +} + +/* + * Increment the reference count of a network device. + * If id < 0, return an unused ether device. + */ +static int +openfile(Netif *nif, int id) +{ + Netfile *f, **fp, **efp; + + if(id >= 0){ + f = nif->f[id]; + if(f == 0) + error(Enodev); + qlock(f); + qreopen(f->in); + f->inuse++; + qunlock(f); + return id; + } + + qlock(nif); + if(waserror()){ + qunlock(nif); + nexterror(); + } + efp = &nif->f[nif->nfile]; + for(fp = nif->f; fp < efp; fp++){ + f = *fp; + if(f == 0){ + f = malloc(sizeof(Netfile)); + if(f == 0) + exhausted("memory"); + f->in = qopen(nif->limit, Qmsg, 0, 0); + if(f->in == nil){ + free(f); + exhausted("memory"); + } + *fp = f; + qlock(f); + } else { + qlock(f); + if(f->inuse){ + qunlock(f); + continue; + } + } + f->inuse = 1; + qreopen(f->in); + netown(f, up->env->user, 0); + qunlock(f); + qunlock(nif); + poperror(); + return fp - nif->f; + } + error(Enodev); + return -1; /* not reached */ +} + +/* + * look for a token starting a string, + * return a pointer to first non-space char after it + */ +static char* +matchtoken(char *p, char *token) +{ + int n; + + n = strlen(token); + if(strncmp(p, token, n)) + return 0; + p += n; + if(*p == 0) + return p; + if(*p != ' ' && *p != '\t' && *p != '\n') + return 0; + while(*p == ' ' || *p == '\t' || *p == '\n') + p++; + return p; +} + +static ulong +hash(uchar *a, int len) +{ + ulong sum = 0; + + while(len-- > 0) + sum = (sum << 1) + *a++; + return sum%Nmhash; +} + +int +activemulti(Netif *nif, uchar *addr, int alen) +{ + Netaddr *hp; + + for(hp = nif->mhash[hash(addr, alen)]; hp; hp = hp->hnext) + if(memcmp(addr, hp->addr, alen) == 0){ + if(hp->ref) + return 1; + else + break; + } + return 0; +} + +static int +parseaddr(uchar *to, char *from, int alen) +{ + char nip[4]; + char *p; + int i; + + p = from; + for(i = 0; i < alen; i++){ + if(*p == 0) + return -1; + nip[0] = *p++; + if(*p == 0) + return -1; + nip[1] = *p++; + nip[2] = 0; + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return 0; +} + +/* + * keep track of multicast addresses + */ +static char* +netmulti(Netif *nif, Netfile *f, uchar *addr, int add) +{ + Netaddr **l, *ap; + int i; + ulong h; + + if(nif->multicast == nil) + return "interface does not support multicast"; + + l = &nif->maddr; + i = 0; + for(ap = *l; ap; ap = *l){ + if(memcmp(addr, ap->addr, nif->alen) == 0) + break; + i++; + l = &ap->next; + } + + if(add){ + if(ap == 0){ + *l = ap = smalloc(sizeof(*ap)); + memmove(ap->addr, addr, nif->alen); + ap->next = 0; + ap->ref = 1; + h = hash(addr, nif->alen); + ap->hnext = nif->mhash[h]; + nif->mhash[h] = ap; + } else { + ap->ref++; + } + if(ap->ref == 1){ + nif->nmaddr++; + nif->multicast(nif->arg, addr, 1); + } + if(i < 8*sizeof(f->maddr)){ + if((f->maddr[i/8] & (1<<(i%8))) == 0) + f->nmaddr++; + f->maddr[i/8] |= 1<<(i%8); + } + } else { + if(ap == 0 || ap->ref == 0) + return 0; + ap->ref--; + if(ap->ref == 0){ + nif->nmaddr--; + nif->multicast(nif->arg, addr, 0); + } + if(i < 8*sizeof(f->maddr)){ + if((f->maddr[i/8] & (1<<(i%8))) != 0) + f->nmaddr--; + f->maddr[i/8] &= ~(1<<(i%8)); + } + } + return 0; +} diff --git a/os/port/netif.h b/os/port/netif.h new file mode 100644 index 00000000..6fbe6b82 --- /dev/null +++ b/os/port/netif.h @@ -0,0 +1,134 @@ +typedef struct Etherpkt Etherpkt; +typedef struct Netaddr Netaddr; +typedef struct Netfile Netfile; +typedef struct Netif Netif; + +enum +{ + Nmaxaddr= 64, + Nmhash= 31, + + Ncloneqid= 1, + Naddrqid, + N2ndqid, + N3rdqid, + Ndataqid, + Nctlqid, + Nstatqid, + Ntypeqid, + Nifstatqid, +}; + +/* + * Macros to manage Qid's used for multiplexed devices + */ +#define NETTYPE(x) (((ulong)x)&0x1f) +#define NETID(x) ((((ulong)x))>>5) +#define NETQID(i,t) ((((ulong)i)<<5)|(t)) + +/* + * one per multiplexed connection + */ +struct Netfile +{ + QLock; + + int inuse; + ulong mode; + char owner[KNAMELEN]; + + int type; /* multiplexor type */ + int prom; /* promiscuous mode */ + int scan; /* base station scanning interval */ + int bridge; /* bridge mode */ + int headersonly; /* headers only - no data */ + uchar maddr[8]; /* bitmask of multicast addresses requested */ + int nmaddr; /* number of multicast addresses */ + + Queue *in; /* input buffer */ +}; + +/* + * a network address + */ +struct Netaddr +{ + Netaddr *next; /* allocation chain */ + Netaddr *hnext; + uchar addr[Nmaxaddr]; + int ref; +}; + +/* + * a network interface + */ +struct Netif +{ + QLock; + + /* multiplexing */ + char name[KNAMELEN]; /* for top level directory */ + int nfile; /* max number of Netfiles */ + Netfile **f; + + /* about net */ + int limit; /* flow control */ + int alen; /* address length */ + int mbps; /* megabits per sec */ + int link; /* link status */ + uchar addr[Nmaxaddr]; + uchar bcast[Nmaxaddr]; + Netaddr *maddr; /* known multicast addresses */ + int nmaddr; /* number of known multicast addresses */ + Netaddr *mhash[Nmhash]; /* hash table of multicast addresses */ + int prom; /* number of promiscuous opens */ + int scan; /* number of base station scanners */ + int all; /* number of -1 multiplexors */ + + /* statistics */ + int misses; + int inpackets; + int outpackets; + int crcs; /* input crc errors */ + int oerrs; /* output errors */ + int frames; /* framing errors */ + int overflows; /* packet overflows */ + int buffs; /* buffering errors */ + int soverflows; /* software overflow */ + + /* routines for touching the hardware */ + void *arg; + void (*promiscuous)(void*, int); + void (*multicast)(void*, uchar*, int); + void (*scanbs)(void*, uint); /* scan for base stations */ +}; + +void netifinit(Netif*, char*, int, ulong); +Walkqid* netifwalk(Netif*, Chan*, Chan*, char **, int); +Chan* netifopen(Netif*, Chan*, int); +void netifclose(Netif*, Chan*); +long netifread(Netif*, Chan*, void*, long, ulong); +Block* netifbread(Netif*, Chan*, long, ulong); +long netifwrite(Netif*, Chan*, void*, long); +int netifwstat(Netif*, Chan*, uchar*, int); +int netifstat(Netif*, Chan*, uchar*, int); +int activemulti(Netif*, uchar*, int); + +/* + * Ethernet specific + */ +enum +{ + Eaddrlen= 6, + ETHERMINTU = 60, /* minimum transmit size */ + ETHERMAXTU = 1514, /* maximum transmit size */ + ETHERHDRSIZE = 14, /* size of an ethernet header */ +}; + +struct Etherpkt +{ + uchar d[Eaddrlen]; + uchar s[Eaddrlen]; + uchar type[2]; + uchar data[1500]; +}; diff --git a/os/port/nocache.c b/os/port/nocache.c new file mode 100644 index 00000000..0eed67e1 --- /dev/null +++ b/os/port/nocache.c @@ -0,0 +1,49 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +/* + * stubs when no devmnt cache + */ +void +cinit(void) +{ +} + +void +copen(Chan *c) +{ + c->flag &= ~CCACHE; +} + +int +cread(Chan *c, uchar *b, int n, vlong off) +{ + USED(c); + USED(b); + USED(n); + USED(off); + return 0; +} + +void +cwrite(Chan *c, uchar *buf, int n, vlong off) +{ + USED(c); + USED(buf); + USED(n); + USED(off); +} + +void +cupdate(Chan *c, uchar *buf, int n, vlong off) +{ + USED(c); + USED(buf); + USED(n); + USED(off); +} + diff --git a/os/port/nodynld.c b/os/port/nodynld.c new file mode 100644 index 00000000..2ba8c7c2 --- /dev/null +++ b/os/port/nodynld.c @@ -0,0 +1,48 @@ +#include "u.h" +#include "../port/lib.h" +#include "../port/error.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include <a.out.h> +#include <dynld.h> + +/* + * null kernel interface to dynld, to stop libinterp moaning + */ + +void* +dynimport(Dynobj*, char*, ulong) +{ + return nil; +} + +void +dynobjfree(Dynobj*) +{ +} + +Dynobj* +kdynloadfd(int fd, Dynsym *tab, int ntab) +{ + USED(fd, tab, ntab); + return nil; +} + +int +kdynloadable(int) +{ + return 0; +} + +Dynobj* +dynld(int) +{ + return nil; +} + +int +dynldable(int) +{ + return 0; +} diff --git a/os/port/noenv.c b/os/port/noenv.c new file mode 100644 index 00000000..446945ac --- /dev/null +++ b/os/port/noenv.c @@ -0,0 +1,33 @@ +/* + * use this when devenv.c not used + */ +#include "u.h" +#include "../port/lib.h" +#include "../port/error.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +/* + * null kernel interface + */ +Egrp* +newegrp(void) +{ + return nil; +} + +void +closeegrp(Egrp*) +{ +} + +void +egrpcpy(Egrp*, Egrp*) +{ +} + +void +ksetenv(char*, char*, int) +{ +} diff --git a/os/port/noscreen.c b/os/port/noscreen.c new file mode 100644 index 00000000..af76178e --- /dev/null +++ b/os/port/noscreen.c @@ -0,0 +1,9 @@ +void +screeninit(void) +{ +} + +void +screenrotate(int) +{ +} diff --git a/os/port/parse.c b/os/port/parse.c new file mode 100644 index 00000000..9d59b567 --- /dev/null +++ b/os/port/parse.c @@ -0,0 +1,114 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +/* + * Generous estimate of number of fields, including terminal nil pointer + */ +static int +ncmdfield(char *p, int n) +{ + int white, nwhite; + char *ep; + int nf; + + if(p == nil) + return 1; + + nf = 0; + ep = p+n; + white = 1; /* first text will start field */ + while(p < ep){ + nwhite = (strchr(" \t\r\n", *p++ & 0xFF) != 0); /* UTF is irrelevant */ + if(white && !nwhite) /* beginning of field */ + nf++; + white = nwhite; + } + return nf+1; /* +1 for nil */ +} + +/* + * parse a command written to a device + */ +Cmdbuf* +parsecmd(char *p, int n) +{ + Cmdbuf *volatile cb; + int nf; + char *sp; + + nf = ncmdfield(p, n); + + /* allocate Cmdbuf plus string pointers plus copy of string including \0 */ + sp = smalloc(sizeof(*cb) + nf * sizeof(char*) + n + 1); + cb = (Cmdbuf*)sp; + cb->f = (char**)(&cb[1]); + cb->buf = (char*)(&cb->f[nf]); + + if(up!=nil && waserror()){ + free(cb); + nexterror(); + } + memmove(cb->buf, p, n); + if(up != nil) + poperror(); + + /* dump new line and null terminate */ + if(n > 0 && cb->buf[n-1] == '\n') + n--; + cb->buf[n] = '\0'; + + cb->nf = tokenize(cb->buf, cb->f, nf-1); + cb->f[cb->nf] = nil; + + return cb; +} + +/* + * Reconstruct original message, for error diagnostic + */ +void +cmderror(Cmdbuf *cb, char *s) +{ + int i; + char *p, *e; + + p = up->genbuf; + e = p+ERRMAX-10; + p = seprint(p, e, "%s \"", s); + for(i=0; i<cb->nf; i++){ + if(i > 0) + p = seprint(p, e, " "); + p = seprint(p, e, "%q", cb->f[i]); + } + strcpy(p, "\""); + error(up->genbuf); +} + +/* + * Look up entry in table + */ +Cmdtab* +lookupcmd(Cmdbuf *cb, Cmdtab *ctab, int nctab) +{ + int i; + Cmdtab *ct; + + if(cb->nf == 0) + error("empty control message"); + + for(ct = ctab, i=0; i<nctab; i++, ct++){ + if(strcmp(ct->cmd, "*") !=0) /* wildcard always matches */ + if(strcmp(ct->cmd, cb->f[0]) != 0) + continue; + if(ct->narg != 0 && ct->narg != cb->nf) + cmderror(cb, Ecmdargs); + return ct; + } + + cmderror(cb, "unknown control message"); + return nil; +} diff --git a/os/port/pgrp.c b/os/port/pgrp.c new file mode 100644 index 00000000..6b2d7d5c --- /dev/null +++ b/os/port/pgrp.c @@ -0,0 +1,278 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +static Ref pgrpid; +static Ref mountid; + +Pgrp* +newpgrp(void) +{ + Pgrp *p; + + p = smalloc(sizeof(Pgrp)); + p->ref = 1; + p->pgrpid = incref(&pgrpid); + p->pin = Nopin; + p->progmode = 0644; + p->privatemem = 0; + return p; +} + +void +closepgrp(Pgrp *p) +{ + Mhead **h, **e, *f, *next; + + if(p == nil || decref(p) != 0) + return; + + wlock(&p->ns); + p->pgrpid = -1; + + e = &p->mnthash[MNTHASH]; + for(h = p->mnthash; h < e; h++) { + for(f = *h; f; f = next) { + wlock(&f->lock); + cclose(f->from); + mountfree(f->mount); + f->mount = nil; + next = f->hash; + wunlock(&f->lock); + putmhead(f); + } + } + wunlock(&p->ns); + cclose(p->dot); + cclose(p->slash); + free(p); +} + +void +pgrpinsert(Mount **order, Mount *m) +{ + Mount *f; + + m->order = 0; + if(*order == 0) { + *order = m; + return; + } + for(f = *order; f; f = f->order) { + if(m->mountid < f->mountid) { + m->order = f; + *order = m; + return; + } + order = &f->order; + } + *order = m; +} + +/* + * pgrpcpy MUST preserve the mountid allocation order of the parent group + */ +void +pgrpcpy(Pgrp *to, Pgrp *from) +{ + int i; + Mount *n, *m, **link, *order; + Mhead *f, **tom, **l, *mh; + + wlock(&from->ns); + order = 0; + tom = to->mnthash; + for(i = 0; i < MNTHASH; i++) { + l = tom++; + for(f = from->mnthash[i]; f; f = f->hash) { + rlock(&f->lock); + mh = malloc(sizeof(Mhead)); + if(mh == nil) { + runlock(&f->lock); + wunlock(&from->ns); + error(Enomem); + } + mh->from = f->from; + mh->ref = 1; + incref(mh->from); + *l = mh; + l = &mh->hash; + link = &mh->mount; + for(m = f->mount; m; m = m->next) { + n = newmount(mh, m->to, m->mflag, m->spec); + if(n == nil) { + runlock(&f->lock); + wunlock(&from->ns); + error(Enomem); + } + m->copy = n; + pgrpinsert(&order, m); + *link = n; + link = &n->next; + } + runlock(&f->lock); + } + } + /* + * Allocate mount ids in the same sequence as the parent group + */ + lock(&mountid.l); + for(m = order; m; m = m->order) + m->copy->mountid = mountid.ref++; + unlock(&mountid.l); + + to->pin = from->pin; + + to->slash = cclone(from->slash); + to->dot = cclone(from->dot); + to->nodevs = from->nodevs; + + wunlock(&from->ns); +} + +Fgrp* +newfgrp(Fgrp *old) +{ + Fgrp *new; + int n; + + new = smalloc(sizeof(Fgrp)); + new->ref = 1; + n = DELTAFD; + if(old != nil){ + lock(old); + if(old->maxfd >= n) + n = (old->maxfd+1 + DELTAFD-1)/DELTAFD * DELTAFD; + new->maxfd = old->maxfd; + unlock(old); + } + new->nfd = n; + new->fd = smalloc(n*sizeof(Chan*)); + return new; +} + +Fgrp* +dupfgrp(Fgrp *f) +{ + int i; + Chan *c; + Fgrp *new; + int n; + + new = smalloc(sizeof(Fgrp)); + new->ref = 1; + lock(f); + n = DELTAFD; + if(f->maxfd >= n) + n = (f->maxfd+1 + DELTAFD-1)/DELTAFD * DELTAFD; + new->nfd = n; + new->fd = malloc(n*sizeof(Chan*)); + if(new->fd == nil){ + unlock(f); + free(new); + error(Enomem); + } + new->maxfd = f->maxfd; + new->minfd = f->minfd; + for(i = 0; i <= f->maxfd; i++) { + if(c = f->fd[i]){ + incref(c); + new->fd[i] = c; + } + } + unlock(f); + + return new; +} + +void +closefgrp(Fgrp *f) +{ + int i; + Chan *c; + + if(f == nil || decref(f) != 0) + return; + + for(i = 0; i <= f->maxfd; i++) + if(c = f->fd[i]) + cclose(c); + + free(f->fd); + free(f); +} + +Mount* +newmount(Mhead *mh, Chan *to, int flag, char *spec) +{ + Mount *m; + + m = smalloc(sizeof(Mount)); + m->to = to; + m->head = mh; + incref(to); + m->mountid = incref(&mountid); + m->mflag = flag; + if(spec != 0) + kstrdup(&m->spec, spec); + + return m; +} + +void +mountfree(Mount *m) +{ + Mount *f; + + while(m) { + f = m->next; + cclose(m->to); + m->mountid = 0; + free(m->spec); + free(m); + m = f; + } +} + +void +resrcwait(char *reason) +{ + char *p; + + if(up == 0) + panic("resrcwait"); + + p = up->psstate; + if(reason) { + up->psstate = reason; + print("%s\n", reason); + } + + tsleep(&up->sleep, return0, 0, 300); + up->psstate = p; +} + +void +closesigs(Skeyset *s) +{ + int i; + + if(s == nil || decref(s) != 0) + return; + for(i=0; i<s->nkey; i++) + freeskey(s->keys[i]); + free(s); +} + +void +freeskey(Signerkey *key) +{ + if(key == nil || decref(key) != 0) + return; + free(key->owner); + (*key->pkfree)(key->pk); + free(key); +} diff --git a/os/port/portbreak.c b/os/port/portbreak.c new file mode 100644 index 00000000..d64f40f6 --- /dev/null +++ b/os/port/portbreak.c @@ -0,0 +1,161 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "portfns.h" +#include "ureg.h" +#include "../port/error.h" + +// +// These bits used to be in port/devdbg but were removed in +// order to allow for using hardware debug features on certain +// architectures +// + +extern void breakset(Bkpt *b); +extern void breakrestore(Bkpt *b); +extern Bkpt* breakclear(int id); +extern void breaknotify(Bkpt *b, Proc *p); +extern int breakmatch(BkptCond *cond, Ureg *ur, Proc *p); + +void skipfree(Bkpt *b); +Bkpt*newskip(ulong addr, Bkpt *skipb, Proc *skipp); +Bkpt *skipalloc; +extern Bkpt *breakpoints; +typedef struct SkipArg SkipArg; +struct SkipArg +{ + Bkpt *b; + Proc *p; +}; + +void +skiphandler(Bkpt *b) +{ + SkipArg *a = b->aux; + Bkpt *l; + + if(breakclear(b->id) == nil) + panic("skiphandler: breakclear() failed"); + breakrestore(a->b); + l = a->b->link; + while(l != nil) { + breakrestore(l); + l = l->link; + } + skipfree(b); + a->p->dbgstop = 0; // Whoo! + if(a->p->state == Stopped) + ready(a->p); +} + +Bkpt* +newskip(ulong addr, Bkpt *skipb, Proc *skipp) +{ + Bkpt *b; + SkipArg *a; + + b = skipalloc; + if(b == nil) + panic("newskip(): no free skips\n"); + skipalloc = b->next; + + b->addr = addr; + b->conditions->val = addr; + b->link = nil; + a = b->aux; + a->b = skipb; + a->p = skipp; + + return b; +} + +void +skipfree(Bkpt *b) +{ + b->next = skipalloc; + skipalloc = b; +} + +// +// Called from the exception handler when a breakpoint instruction has been +// hit. This cannot not be called unless at least one breakpoint with this +// address is in the list of breakpoints. (All breakpoint notifications must +// previously have been set via setbreak()) +// +// foreach breakpoint in list +// if breakpoint matches conditions +// notify the break handler +// if no breakpoints matched the conditions +// pick a random breakpoint set to this address +// +// set a breakpoint at the next instruction to be executed, +// and pass the current breakpoint to the "skiphandler" +// +// clear the current breakpoint +// +// Tell the scheduler to stop scheduling, so the caller is +// guaranteed to execute the instruction, followed by the +// added breakpoint. +// +// +int +breakhit(Ureg *ur, Proc *p) +{ + Bkpt *b; + int nmatched; + Bkpt *skip; + + nmatched = 0; + for(b = breakpoints; b != nil; b = b->next) { + if(breakmatch(b->conditions, ur, p)) { + breaknotify(b, p); + ++nmatched; + } + } + + if(nmatched) + return BrkSched; + + skip = nil; + for(b = breakpoints; b != nil; b = b->next) { + if(b->addr == ur->pc) { + if(breakclear(b->id) == nil) + panic("breakhit: breakclear() failed"); + + if(skip == nil) + skip = newskip(machnextaddr(ur), b, p); + else { + b->link = skip->link; + skip->link = b; + } + } + } + if(skip == nil) + return BrkSched; + breakset(skip); + return BrkNoSched; +} + +void +portbreakinit(void) +{ + Bkpt *b; + int i; + + skipalloc = mallocz(conf.nproc*(sizeof(Bkpt)+sizeof(BkptCond)+sizeof(SkipArg)), 1); + if(skipalloc == nil) + error(Enomem); + + b = skipalloc; + for(i=0; i < conf.nproc-1; i++) { + b->id = -(i+1); + b->conditions = (BkptCond*)((uchar*)b + sizeof(Bkpt)); + b->conditions->op = 'b'; + b->handler = skiphandler; + b->aux = (SkipArg*)((uchar*)b+sizeof(Bkpt)+sizeof(BkptCond)); + b->next = (Bkpt*)((uchar*)b+sizeof(Bkpt)+sizeof(BkptCond)+sizeof(SkipArg)); + b = b->next; + } + b->next = nil; +} diff --git a/os/port/portclock.c b/os/port/portclock.c new file mode 100644 index 00000000..04d1b110 --- /dev/null +++ b/os/port/portclock.c @@ -0,0 +1,277 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +struct Timers +{ + Lock; + Timer *head; +}; + +static Timers timers[MAXMACH]; + +ulong intrcount[MAXMACH]; +ulong fcallcount[MAXMACH]; + +static uvlong +tadd(Timers *tt, Timer *nt) +{ + Timer *t, **last; + + /* Called with tt locked */ + assert(nt->tt == nil); + switch(nt->tmode){ + default: + panic("timer"); + break; + case Trelative: + assert(nt->tns > 0); + nt->twhen = fastticks(nil) + ns2fastticks(nt->tns); + break; + case Tabsolute: + nt->twhen = tod2fastticks(nt->tns); + break; + case Tperiodic: + assert(nt->tns >= 100000); /* At least 100 µs period */ + if(nt->twhen == 0){ + /* look for another timer at same frequency for combining */ + for(t = tt->head; t; t = t->tnext){ + if(t->tmode == Tperiodic && t->tns == nt->tns) + break; + } + if (t) + nt->twhen = t->twhen; + else + nt->twhen = fastticks(nil); + } + nt->twhen += ns2fastticks(nt->tns); + break; + } + + for(last = &tt->head; t = *last; last = &t->tnext){ + if(t->twhen > nt->twhen) + break; + } + nt->tnext = *last; + *last = nt; + nt->tt = tt; + if(last == &tt->head) + return nt->twhen; + return 0; +} + +static uvlong +tdel(Timer *dt) +{ + + Timer *t, **last; + Timers *tt; + + tt = dt->tt; + if (tt == nil) + return 0; + for(last = &tt->head; t = *last; last = &t->tnext){ + if(t == dt){ + assert(dt->tt); + dt->tt = nil; + *last = t->tnext; + break; + } + } + if(last == &tt->head && tt->head) + return tt->head->twhen; + return 0; +} + +/* add or modify a timer */ +void +timeradd(Timer *nt) +{ + Timers *tt; + vlong when; + + if (nt->tmode == Tabsolute){ + when = todget(nil); + if (nt->tns <= when){ + // if (nt->tns + MS2NS(10) <= when) /* small deviations will happen */ + // print("timeradd (%lld %lld) %lld too early 0x%lux\n", + // when, nt->tns, when - nt->tns, getcallerpc(&nt)); + nt->tns = when; + } + } + /* Must lock Timer struct before Timers struct */ + ilock(nt); + if(tt = nt->tt){ + ilock(tt); + tdel(nt); + iunlock(tt); + } + tt = &timers[m->machno]; + ilock(tt); + when = tadd(tt, nt); + if(when) + timerset(when); + iunlock(tt); + iunlock(nt); +} + + +void +timerdel(Timer *dt) +{ + Timers *tt; + uvlong when; + + ilock(dt); + if(tt = dt->tt){ + ilock(tt); + when = tdel(dt); + if(when && tt == &timers[m->machno]) + timerset(tt->head->twhen); + iunlock(tt); + } + iunlock(dt); +} + +void +hzclock(Ureg *ur) +{ + m->ticks++; + if(m->proc) + m->proc->pc = ur->pc; + + kmapinval(); + + if(kproftick != nil) + kproftick(ur->pc); + + if((active.machs&(1<<m->machno)) == 0) + return; + + if(active.exiting) { + print("someone's exiting\n"); + exit(0); + } + + checkalarms(); + + if(up && up->state == Running){ + if(anyready()){ + sched(); + splhi(); + } + } +} + +void +timerintr(Ureg *u, uvlong) +{ + Timer *t; + Timers *tt; + uvlong when, now; + int callhzclock; + static int sofar; + + intrcount[m->machno]++; + callhzclock = 0; + tt = &timers[m->machno]; + now = fastticks(nil); + ilock(tt); + while(t = tt->head){ + /* + * No need to ilock t here: any manipulation of t + * requires tdel(t) and this must be done with a + * lock to tt held. We have tt, so the tdel will + * wait until we're done + */ + when = t->twhen; + if(when > now){ + timerset(when); + iunlock(tt); + if(callhzclock) + hzclock(u); + return; + } + tt->head = t->tnext; + assert(t->tt == tt); + t->tt = nil; + fcallcount[m->machno]++; + iunlock(tt); + if(t->tf) + (*t->tf)(u, t); + else + callhzclock++; + ilock(tt); + if(t->tmode == Tperiodic) + tadd(tt, t); + } + iunlock(tt); +} + +void +timersinit(void) +{ + Timer *t; + + todinit(); + t = malloc(sizeof(*t)); + t->tmode = Tperiodic; + t->tt = nil; + t->tns = 1000000000/HZ; + t->tf = nil; + timeradd(t); +} + +Timer* +addclock0link(void (*f)(void), int ms) +{ + Timer *nt; + uvlong when; + + /* Synchronize to hztimer if ms is 0 */ + nt = malloc(sizeof(Timer)); + if(ms == 0) + ms = 1000/HZ; + nt->tns = (vlong)ms*1000000LL; + nt->tmode = Tperiodic; + nt->tt = nil; + nt->tf = (void (*)(Ureg*, Timer*))f; + + ilock(&timers[0]); + when = tadd(&timers[0], nt); + if(when) + timerset(when); + iunlock(&timers[0]); + return nt; +} + +/* + * This tk2ms avoids overflows that the macro version is prone to. + * It is a LOT slower so shouldn't be used if you're just converting + * a delta. + */ +ulong +tk2ms(ulong ticks) +{ + uvlong t, hz; + + t = ticks; + hz = HZ; + t *= 1000L; + t = t/hz; + ticks = t; + return ticks; +} + +ulong +ms2tk(ulong ms) +{ + /* avoid overflows at the cost of precision */ + if(ms >= 1000000000/HZ) + return (ms/1000)*HZ; + return (ms*HZ+500)/1000; +} diff --git a/os/port/portdat.h b/os/port/portdat.h new file mode 100644 index 00000000..103961e0 --- /dev/null +++ b/os/port/portdat.h @@ -0,0 +1,673 @@ +typedef struct Alarms Alarms; +typedef struct Block Block; +typedef struct Bkpt Bkpt; +typedef struct BkptCond BkptCond; +typedef struct Chan Chan; +typedef struct Cmdbuf Cmdbuf; +typedef struct Cmdtab Cmdtab; +typedef struct Cname Cname; +typedef struct Crypt Crypt; +typedef struct Dev Dev; +typedef struct DevConf DevConf; +typedef struct Dirtab Dirtab; +typedef struct Edf Edf; +typedef struct Egrp Egrp; +typedef struct Evalue Evalue; +typedef struct Fgrp Fgrp; +typedef struct List List; +typedef struct Log Log; +typedef struct Logflag Logflag; +typedef struct Mntcache Mntcache; +typedef struct Mntparam Mntparam; +typedef struct Mount Mount; +typedef struct Mntrpc Mntrpc; +typedef struct Mntwalk Mntwalk; +typedef struct Mnt Mnt; +typedef struct Mhead Mhead; +typedef struct Osenv Osenv; +typedef struct Pgrp Pgrp; +typedef struct Proc Proc; +typedef struct QLock QLock; +typedef struct Queue Queue; +typedef struct Ref Ref; +typedef struct Rendez Rendez; +typedef struct Rept Rept; +typedef struct Rootdata Rootdata; +typedef struct RWlock RWlock; +typedef struct Signerkey Signerkey; +typedef struct Skeyset Skeyset; +typedef struct Talarm Talarm; +typedef struct Timer Timer; +typedef struct Timers Timers; +typedef struct Uart Uart; +typedef struct Walkqid Walkqid; +typedef int Devgen(Chan*, char*, Dirtab*, int, int, Dir*); + +#pragma incomplete DevConf +#pragma incomplete Edf +#pragma incomplete Mntcache +#pragma incomplete Mntrpc +#pragma incomplete Queue +#pragma incomplete Timers + +#include "fcall.h" +#include <pool.h> + +#define nelem(n) (sizeof(n)/sizeof(n[0])) + +struct Ref +{ + Lock l; + long ref; +}; + +struct Rendez +{ + Lock; + Proc *p; +}; + +struct Rept +{ + Lock l; + Rendez r; + void *o; + int t; + int (*active)(void*); + int (*ck)(void*, int); + void (*f)(void*); /* called with VM acquire()'d */ +}; + +struct Osenv +{ + char *syserrstr; /* last error from a system call, errbuf0 or 1 */ + char *errstr; /* reason we're unwinding the error stack, errbuf1 or 0 */ + char errbuf0[ERRMAX]; + char errbuf1[ERRMAX]; + Pgrp* pgrp; /* Ref to namespace, working dir and root */ + Fgrp* fgrp; /* Ref to file descriptors */ + Egrp* egrp; /* Environment vars */ + Skeyset* sigs; /* Signed module keys */ + Rendez* rend; /* Synchro point */ + Queue* waitq; /* Info about dead children */ + Queue* childq; /* Info about children for debuggers */ + void* debug; /* Debugging master */ + int uid; /* Numeric user id for system */ + int gid; /* Numeric group id for system */ + char* user; /* Inferno user name */ + FPenv fpu; /* Floating point thread state */ +}; + +enum +{ + Nopin = -1 +}; + +struct QLock +{ + Lock use; /* to access Qlock structure */ + Proc *head; /* next process waiting for object */ + Proc *tail; /* last process waiting for object */ + int locked; /* flag */ +}; + +struct RWlock +{ + Lock; /* Lock modify lock */ + QLock x; /* Mutual exclusion lock */ + QLock k; /* Lock for waiting writers */ + int readers; /* Count of readers in lock */ +}; + +struct Talarm +{ + Lock; + Proc* list; +}; + +struct Alarms +{ + QLock; + Proc* head; +}; + +struct Rootdata +{ + int dotdot; + void *ptr; + int size; + int *sizep; +}; + +/* + * Access types in namec & channel flags + */ +enum +{ + Aaccess, /* as in stat, wstat */ + Abind, /* for left-hand-side of bind */ + Atodir, /* as in chdir */ + Aopen, /* for i/o */ + Amount, /* to be mounted or mounted upon */ + Acreate, /* is to be created */ + Aremove, /* will be removed by caller */ + + COPEN = 0x0001, /* for i/o */ + CMSG = 0x0002, /* the message channel for a mount */ + CCEXEC = 0x0008, /* close on exec */ + CFREE = 0x0010, /* not in use */ + CRCLOSE = 0x0020, /* remove on close */ + CCACHE = 0x0080, /* client cache */ +}; + +enum +{ + BINTR = (1<<0), + BFREE = (1<<1), + Bipck = (1<<2), /* ip checksum */ + Budpck = (1<<3), /* udp checksum */ + Btcpck = (1<<4), /* tcp checksum */ + Bpktck = (1<<5), /* packet checksum */ +}; + +struct Block +{ + Block* next; + Block* list; + uchar* rp; /* first unconsumed byte */ + uchar* wp; /* first empty byte */ + uchar* lim; /* 1 past the end of the buffer */ + uchar* base; /* start of the buffer */ + void (*free)(Block*); + ushort flag; + ushort checksum; /* IP checksum of complete packet (minus media header) */ +}; +#define BLEN(s) ((s)->wp - (s)->rp) +#define BALLOC(s) ((s)->lim - (s)->base) + +struct Chan +{ + Lock; + Ref; + Chan* next; /* allocation */ + Chan* link; + vlong offset; /* in file */ + ushort type; + ulong dev; + ushort mode; /* read/write */ + ushort flag; + Qid qid; + int fid; /* for devmnt */ + ulong iounit; /* chunk size for i/o; 0==default */ + Mhead* umh; /* mount point that derived Chan; used in unionread */ + Chan* umc; /* channel in union; held for union read */ + QLock umqlock; /* serialize unionreads */ + int uri; /* union read index */ + int dri; /* devdirread index */ + ulong mountid; + Mntcache *mcp; /* Mount cache pointer */ + Mnt *mux; /* Mnt for clients using me for messages */ + union { + void* aux; + char tag[4]; /* for iproute */ + }; + Chan* mchan; /* channel to mounted server */ + Qid mqid; /* qid of root of mount point */ + Cname *name; +}; + +struct Cname +{ + Ref; + int alen; /* allocated length */ + int len; /* strlen(s) */ + char *s; +}; + +struct Dev +{ + int dc; + char* name; + + void (*reset)(void); + void (*init)(void); + void (*shutdown)(void); + Chan* (*attach)(char*); + Walkqid* (*walk)(Chan*, Chan*, char**, int); + int (*stat)(Chan*, uchar*, int); + Chan* (*open)(Chan*, int); + void (*create)(Chan*, char*, int, ulong); + void (*close)(Chan*); + long (*read)(Chan*, void*, long, vlong); + Block* (*bread)(Chan*, long, ulong); + long (*write)(Chan*, void*, long, vlong); + long (*bwrite)(Chan*, Block*, ulong); + void (*remove)(Chan*); + int (*wstat)(Chan*, uchar*, int); + void (*power)(int); /* power mgt: power(1) → on, power (0) → off */ + int (*config)(int, char*, DevConf*); +}; + +struct Dirtab +{ + char name[KNAMELEN]; + Qid qid; + vlong length; + long perm; +}; + +struct Walkqid +{ + Chan *clone; + int nqid; + Qid qid[1]; +}; + +enum +{ + NSMAX = 1000, + NSLOG = 7, + NSCACHE = (1<<NSLOG), +}; + +struct Mntwalk /* state for /proc/#/ns */ +{ + int cddone; + ulong id; + Mhead* mh; + Mount* cm; +}; + +struct Mount +{ + ulong mountid; + Mount* next; + Mhead* head; + Mount* copy; + Mount* order; + Chan* to; /* channel replacing channel */ + int mflag; + char *spec; +}; + +struct Mhead +{ + Ref; + RWlock lock; + Chan* from; /* channel mounted upon */ + Mount* mount; /* what's mounted upon it */ + Mhead* hash; /* Hash chain */ +}; + +struct Mnt +{ + Lock; + /* references are counted using c->ref; channels on this mount point incref(c->mchan) == Mnt.c */ + Chan *c; /* Channel to file service */ + Proc *rip; /* Reader in progress */ + Mntrpc *queue; /* Queue of pending requests on this channel */ + ulong id; /* Multiplexer id for channel check */ + Mnt *list; /* Free list */ + int flags; /* cache */ + int msize; /* data + IOHDRSZ */ + char *version; /* 9P version */ + Queue *q; /* input queue */ +}; + +enum +{ + RENDLOG = 5, + RENDHASH = 1<<RENDLOG, /* Hash to lookup rendezvous tags */ + MNTLOG = 5, + MNTHASH = 1<<MNTLOG, /* Hash to walk mount table */ + DELTAFD= 20, /* allocation quantum for process file descriptors */ + MAXNFD = 4000, /* max per process file descriptors */ + MAXKEY = 8, /* keys for signed modules */ +}; +#define MOUNTH(p,qid) ((p)->mnthash[(qid).path&((1<<MNTLOG)-1)]) + +struct Mntparam { + Chan* chan; + Chan* authchan; + char* spec; + int flags; +}; + +struct Pgrp +{ + Ref; /* also used as a lock when mounting */ + ulong pgrpid; + QLock debug; /* single access via devproc.c */ + RWlock ns; /* Namespace n read/one write lock */ + QLock nsh; + Mhead* mnthash[MNTHASH]; + int progmode; + int privatemem; /* deny access to /prog by debuggers */ + Chan* dot; + Chan* slash; + int nodevs; + int pin; +}; + +struct Fgrp +{ + Lock; + Ref; + Chan** fd; + int nfd; /* number of fd slots */ + int maxfd; /* highest fd in use */ + int minfd; /* lower bound on free fd */ +}; + +struct Evalue +{ + char *var; + char *val; + int len; + Qid qid; + Evalue *next; +}; + +struct Egrp +{ + Ref; + QLock; + Evalue *entries; + ulong path; /* qid.path of next Evalue to be allocated */ + ulong vers; /* of Egrp */ +}; + +struct Signerkey +{ + Ref; + char* owner; + ushort footprint; + ulong expires; + void* alg; + void* pk; + void (*pkfree)(void*); +}; + +struct Skeyset +{ + Ref; + QLock; + ulong flags; + char* devs; + int nkey; + Signerkey *keys[MAXKEY]; +}; + +/* + * fasttick timer interrupts + */ +enum { + /* Mode */ + Trelative, /* timer programmed in ns from now */ + Tabsolute, /* timer programmed in ns since epoch */ + Tperiodic, /* periodic timer, period in ns */ +}; + +struct Timer +{ + /* Public interface */ + int tmode; /* See above */ + vlong tns; /* meaning defined by mode */ + void (*tf)(Ureg*, Timer*); + void *ta; + /* Internal */ + Lock; + Timers *tt; /* Timers queue this timer runs on */ + vlong twhen; /* ns represented in fastticks */ + Timer *tnext; +}; + +enum +{ + Dead = 0, /* Process states */ + Moribund, + Ready, + Scheding, + Running, + Queueing, + Wakeme, + Broken, + Stopped, + Rendezvous, + Waitrelease, + + Proc_stopme = 1, /* devproc requests */ + Proc_exitme, + Proc_traceme, + Proc_exitbig, + + NERR = 30, + + Unknown = 0, + IdleGC, + Interp, + BusyGC, + + PriLock = 0, /* Holding Spin lock */ + PriEdf, /* active edf processes */ + PriRelease, /* released edf processes */ + PriRealtime, /* Video telephony */ + PriHicodec, /* MPEG codec */ + PriLocodec, /* Audio codec */ + PriHi, /* Important task */ + PriNormal, + PriLo, + PriBackground, + PriExtra, /* edf processes we don't care about */ + Nrq +}; + +struct Proc +{ + Label sched; /* known to l.s */ + char* kstack; /* known to l.s */ + Mach* mach; /* machine running this proc */ + char text[KNAMELEN]; + Proc* rnext; /* next process in run queue */ + Proc* qnext; /* next process on queue for a QLock */ + QLock* qlock; /* addrof qlock being queued for DEBUG */ + int state; + int type; + void* prog; /* Dummy Prog for interp release */ + void* iprog; + Osenv* env; + Osenv defenv; + int swipend; /* software interrupt pending for Prog */ + Lock sysio; /* note handler lock */ + char* psstate; /* What /proc/#/status reports */ + ulong pid; + int fpstate; + int procctl; /* Control for /proc debugging */ + ulong pc; /* DEBUG only */ + Lock rlock; /* sync between sleep/swiproc for r */ + Rendez* r; /* rendezvous point slept on */ + Rendez sleep; /* place for syssleep/debug */ + int killed; /* by swiproc */ + int kp; /* true if a kernel process */ + ulong alarm; /* Time of call */ + int pri; /* scheduler priority */ + ulong twhen; + Rendez* trend; + Proc* tlink; + int (*tfn)(void*); + void (*kpfun)(void*); + void* arg; + FPU fpsave; + int scallnr; + int nerrlab; + Label errlab[NERR]; + char genbuf[128]; /* buffer used e.g. for last name element from namec */ + Mach* mp; /* machine this process last ran on */ + Mach* wired; + ulong movetime; /* next time process should switch processors */ + ulong delaysched; + int preempted; /* process yielding in interrupt */ + ulong qpc; /* last call that blocked in qlock */ + void* dbgreg; /* User registers for devproc */ + int dbgstop; /* don't run this kproc */ + Edf* edf; /* if non-null, real-time proc, edf contains scheduling params */ +}; + +enum +{ + /* kproc flags */ + KPDUPPG = (1<<0), + KPDUPFDG = (1<<1), + KPDUPENVG = (1<<2), + KPDUP = KPDUPPG | KPDUPFDG | KPDUPENVG +}; + +enum { + BrkSched, + BrkNoSched, +}; + +struct BkptCond +{ + uchar op; + ulong val; + BkptCond *next; +}; + +struct Bkpt +{ + int id; + ulong addr; + BkptCond *conditions; + Instr instr; + void (*handler)(Bkpt*); + void *aux; + Bkpt *next; + Bkpt *link; +}; + +enum +{ + PRINTSIZE = 256, + NUMSIZE = 12, /* size of formatted number */ + MB = (1024*1024), + READSTR = 1000, /* temporary buffer size for device reads */ +}; + +extern Conf conf; +extern char* conffile; +extern int consoleprint; +extern Dev* devtab[]; +extern char* eve; +extern int hwcurs; +extern FPU initfp; +extern Queue *kbdq; +extern Queue *kscanq; +extern Ref noteidalloc; +extern Queue *printq; +extern uint qiomaxatomic; +extern char* statename[]; +extern char* sysname; +extern Talarm talarm; + +/* + * action log + */ +struct Log { + Lock; + int opens; + char* buf; + char *end; + char *rptr; + int len; + int nlog; + int minread; + + int logmask; /* mask of things to debug */ + + QLock readq; + Rendez readr; +}; + +struct Logflag { + char* name; + int mask; +}; + +struct Cmdbuf +{ + char *buf; + char **f; + int nf; +}; + +struct Cmdtab +{ + int index; /* used by client to switch on result */ + char *cmd; /* command name */ + int narg; /* expected #args; 0 ==> variadic */ +}; + +enum +{ + MAXPOOL = 8, +}; + +extern Pool* mainmem; +extern Pool* heapmem; +extern Pool* imagmem; + +/* queue state bits, Qmsg, Qcoalesce, and Qkick can be set in qopen */ +enum +{ + /* Queue.state */ + Qstarve = (1<<0), /* consumer starved */ + Qmsg = (1<<1), /* message stream */ + Qclosed = (1<<2), /* queue has been closed/hungup */ + Qflow = (1<<3), /* producer flow controlled */ + Qcoalesce = (1<<4), /* coallesce packets on read */ + Qkick = (1<<5), /* always call the kick routine after qwrite */ +}; + +#define DEVDOTDOT -1 + +#pragma varargck argpos print 1 +#pragma varargck argpos snprint 3 +#pragma varargck argpos seprint 3 +#pragma varargck argpos sprint 2 +#pragma varargck argpos fprint 2 +#pragma varargck argpos iprint 1 +#pragma varargck argpos panic 1 +#pragma varargck argpos kwerrstr 1 +#pragma varargck argpos kprint 1 + +#pragma varargck type "lld" vlong +#pragma varargck type "llx" vlong +#pragma varargck type "lld" uvlong +#pragma varargck type "llx" uvlong +#pragma varargck type "lx" void* +#pragma varargck type "ld" long +#pragma varargck type "lx" long +#pragma varargck type "ld" ulong +#pragma varargck type "lx" ulong +#pragma varargck type "d" int +#pragma varargck type "x" int +#pragma varargck type "c" int +#pragma varargck type "C" int +#pragma varargck type "d" uint +#pragma varargck type "x" uint +#pragma varargck type "c" uint +#pragma varargck type "C" uint +#pragma varargck type "f" double +#pragma varargck type "e" double +#pragma varargck type "g" double +#pragma varargck type "s" char* +#pragma varargck type "S" Rune* +#pragma varargck type "r" void +#pragma varargck type "%" void +#pragma varargck type "I" uchar* +#pragma varargck type "V" uchar* +#pragma varargck type "E" uchar* +#pragma varargck type "M" uchar* +#pragma varargck type "p" void* +#pragma varargck type "q" char* diff --git a/os/port/portfns.h b/os/port/portfns.h new file mode 100644 index 00000000..d6bcdcf0 --- /dev/null +++ b/os/port/portfns.h @@ -0,0 +1,319 @@ +#define FPinit() fpinit() /* remove this if math lib is linked */ +void FPrestore(void*); +void FPsave(void*); +Timer* addclock0link(void (*)(void), int); +Cname* addelem(Cname*, char*); +void addprog(Proc*); +void addrootfile(char*, uchar*, ulong); +Block* adjustblock(Block*, int); +Block* allocb(int); +int anyhigher(void); +int anyready(void); +#define assert(x) if(x){}else _assert("assert(x) failed") +void _assert(char*); +Block* bl2mem(uchar*, Block*, int); +int blocklen(Block*); +int breakhit(Ureg *ur, Proc*); +void callwithureg(void(*)(Ureg*)); +char* channame(Chan*); +int canlock(Lock*); +int canqlock(QLock*); +void cclose(Chan*); +int canrlock(RWlock*); +void chandevinit(void); +void chandevreset(void); +void chandevshutdown(void); +Dir* chandirstat(Chan*); +void chanfree(Chan*); +void chanrec(Mnt*); +void checkalarms(void); +void checkb(Block*, char*); +void cinit(void); +Chan* cclone(Chan*); +void cclose(Chan*); +void closeegrp(Egrp*); +void closefgrp(Fgrp*); +void closemount(Mount*); +void closepgrp(Pgrp*); +void closesigs(Skeyset*); +void cmderror(Cmdbuf*, char*); +int cmount(Chan*, Chan*, int, char*); +void cnameclose(Cname*); +Block* concatblock(Block*); +void confinit(void); +void copen(Chan*); +Block* copyblock(Block*, int); +int cread(Chan*, uchar*, int, vlong); +Chan* cunique(Chan*); +Chan* createdir(Chan*, Mhead*); +void cunmount(Chan*, Chan*); +void cupdate(Chan*, uchar*, int, vlong); +void cursorenable(void); +void cursordisable(void); +int cursoron(int); +void cursoroff(int); +void cwrite(Chan*, uchar*, int, vlong); +void debugkey(Rune, char *, void(*)(), int); +int decref(Ref*); +Chan* devattach(int, char*); +Block* devbread(Chan*, long, ulong); +long devbwrite(Chan*, Block*, ulong); +Chan* devclone(Chan*); +void devcreate(Chan*, char*, int, ulong); +void devdir(Chan*, Qid, char*, vlong, char*, long, Dir*); +long devdirread(Chan*, char*, long, Dirtab*, int, Devgen*); +Devgen devgen; +void devinit(void); +int devno(int, int); +void devpower(int); +Dev* devbyname(char*); +Chan* devopen(Chan*, int, Dirtab*, int, Devgen*); +void devpermcheck(char*, ulong, int); +void devremove(Chan*); +void devreset(void); +void devshutdown(void); +int devstat(Chan*, uchar*, int, Dirtab*, int, Devgen*); +Walkqid* devwalk(Chan*, Chan*, char**, int, Dirtab*, int, Devgen*); +int devwstat(Chan*, uchar*, int); +void disinit(void*); +void disfault(void*, char*); +int domount(Chan**, Mhead**); +void drawactive(int); +void drawcmap(void); +void dumpstack(void); +Fgrp* dupfgrp(Fgrp*); +void egrpcpy(Egrp*, Egrp*); +int emptystr(char*); +int eqchan(Chan*, Chan*, int); +int eqqid(Qid, Qid); +void error(char*); +void errorf(char*, ...); +#pragma varargck argpos errorf 1 +void errstr(char*, int); +void excinit(void); +void exhausted(char*); +void exit(int); +void reboot(void); +void halt(void); +int export(int, char*, int); +uvlong fastticks(uvlong*); +uvlong fastticks2ns(uvlong); +void fdclose(Fgrp*, int); +Chan* fdtochan(Fgrp*, int, int, int, int); +int findmount(Chan**, Mhead**, int, int, Qid); +void free(void*); +void freeb(Block*); +void freeblist(Block*); +void freeskey(Signerkey*); +void getcolor(ulong, ulong*, ulong*, ulong*); +ulong getmalloctag(void*); +ulong getrealloctag(void*); +void gotolabel(Label*); +void hnputl(void*, ulong); +void hnputs(void*, ushort); +Block* iallocb(int); +void iallocsummary(void); +void ilock(Lock*); +int incref(Ref*); +int iprint(char*, ...); +#pragma varargck argpos iprint 1 +void isdir(Chan*); +int iseve(void); +int islo(void); +void iunlock(Lock*); +void ixsummary(void); +void kbdclock(void); +int kbdcr2nl(Queue*, int); +int kbdputc(Queue*, int); +void kbdrepeat(int); +void kproc(char*, void(*)(void*), void*, int); +int kfgrpclose(Fgrp*, int); +void kprocchild(Proc*, void (*)(void*), void*); +int kprint(char*, ...); +void (*kproftick)(ulong); +void ksetenv(char*, char*, int); +void kstrcpy(char*, char*, int); +void kstrdup(char**, char*); +long latin1(Rune*, int); +void lock(Lock*); +void logopen(Log*); +void logclose(Log*); +char* logctl(Log*, int, char**, Logflag*); +void logn(Log*, int, void*, int); +long logread(Log*, void*, ulong, long); +void logb(Log*, int, char*, ...); +#define pragma varargck argpos logb 3 +Cmdtab* lookupcmd(Cmdbuf*, Cmdtab*, int); +void machinit(void); +extern void machbreakinit(void); +extern Instr machinstr(ulong addr); +extern void machbreakset(ulong addr); +extern void machbreakclear(ulong addr, Instr i); +extern ulong machnextaddr(Ureg *ur); +void* malloc(ulong); +void* mallocz(ulong, int); +Block* mem2bl(uchar*, int); +int memusehigh(void); +void microdelay(int); +uvlong mk64fract(uvlong, uvlong); +void mkqid(Qid*, vlong, ulong, int); +void modinit(void); +Chan* mntauth(Chan*, char*); +long mntversion(Chan*, char*, int, int); +void mountfree(Mount*); +void mousetrack(int, int, int, int); +uvlong ms2fastticks(ulong); +ulong msize(void*); +void mul64fract(uvlong*, uvlong, uvlong); +void muxclose(Mnt*); +Chan* namec(char*, int, int, ulong); +Chan* newchan(void); +Egrp* newegrp(void); +Fgrp* newfgrp(Fgrp*); +Mount* newmount(Mhead*, Chan*, int, char*); +Pgrp* newpgrp(void); +Proc* newproc(void); +char* nextelem(char*, char*); +void nexterror(void); +Cname* newcname(char*); +int notify(Ureg*); +void notkilled(void); +int nrand(int); +uvlong ns2fastticks(uvlong); +int okaddr(ulong, ulong, int); +int openmode(ulong); +Block* packblock(Block*); +Block* padblock(Block*, int); +void panic(char*, ...); +Cmdbuf* parsecmd(char*, int); +void pexit(char*, int); +void pgrpcpy(Pgrp*, Pgrp*); +#define poperror() up->nerrlab-- +int poolread(char*, int, ulong); +void poolsize(Pool *, int, int); +int postnote(Proc *, int, char *, int); +int pprint(char*, ...); +int preemption(int); +void printinit(void); +void procctl(Proc*); +void procdump(void); +void procinit(void); +Proc* proctab(int); +void (*proctrace)(Proc*, int, vlong); +int progfdprint(Chan*, int, int, char*, int); +int pullblock(Block**, int); +Block* pullupblock(Block*, int); +Block* pullupqueue(Queue*, int); +void putmhead(Mhead*); +void putstrn(char*, int); +void qaddlist(Queue*, Block*); +Block* qbread(Queue*, int); +long qbwrite(Queue*, Block*); +Queue* qbypass(void (*)(void*, Block*), void*); +int qcanread(Queue*); +void qclose(Queue*); +int qconsume(Queue*, void*, int); +Block* qcopy(Queue*, int, ulong); +int qdiscard(Queue*, int); +void qflush(Queue*); +void qfree(Queue*); +int qfull(Queue*); +Block* qget(Queue*); +void qhangup(Queue*, char*); +int qisclosed(Queue*); +int qiwrite(Queue*, void*, int); +int qlen(Queue*); +void qlock(QLock*); +void qnoblock(Queue*, int); +Queue* qopen(int, int, void (*)(void*), void*); +int qpass(Queue*, Block*); +int qpassnolim(Queue*, Block*); +int qproduce(Queue*, void*, int); +void qputback(Queue*, Block*); +long qread(Queue*, void*, int); +Block* qremove(Queue*); +void qreopen(Queue*); +void qsetlimit(Queue*, int); +void qunlock(QLock*); +int qwindow(Queue*); +int qwrite(Queue*, void*, int); +void randominit(void); +ulong randomread(void*, ulong); +void* realloc(void*, ulong); +int readnum(ulong, char*, ulong, ulong, int); +int readnum_vlong(ulong, char*, ulong, vlong, int); +int readstr(ulong, char*, ulong, char*); +void ready(Proc*); +void renameproguser(char*, char*); +void renameuser(char*, char*); +void resrcwait(char*); +int return0(void*); +void rlock(RWlock*); +void runlock(RWlock*); +Proc* runproc(void); +void sched(void); +void schedinit(void); +long seconds(void); +void (*serwrite)(char*, int); +int setcolor(ulong, ulong, ulong, ulong); +int setlabel(Label*); +void setmalloctag(void*, ulong); +int setpri(int); +void setrealloctag(void*, ulong); +char* skipslash(char*); +void sleep(Rendez*, int(*)(void*), void*); +void* smalloc(ulong); +int splhi(void); +int spllo(void); +void splx(int); +void splxpc(int); +void swiproc(Proc*, int); +ulong _tas(ulong*); +void timeradd(Timer*); +void timerdel(Timer*); +void timersinit(void); +void timerintr(Ureg*, uvlong); +void timerset(uvlong); +ulong tk2ms(ulong); +#define TK2MS(x) ((x)*(1000/HZ)) +uvlong tod2fastticks(vlong); +vlong todget(vlong*); +void todfix(void); +void todsetfreq(vlong); +void todinit(void); +void todset(vlong, vlong, int); +int tready(void*); +Block* trimblock(Block*, int, int); +void tsleep(Rendez*, int (*)(void*), void*, int); +int uartgetc(void); +void uartputc(int); +void uartputs(char*, int); +long unionread(Chan*, void*, long); +void unlock(Lock*); +void userinit(void); +ulong userpc(void); +void validname(char*, int); +void validstat(uchar*, int); +void validwstatname(char*); +int wakeup(Rendez*); +int walk(Chan**, char**, int, int, int*); +void werrstr(char*, ...); +void wlock(RWlock*); +void wunlock(RWlock*); +void* xalloc(ulong); +void* xallocz(ulong, int); +void xfree(void*); +void xhole(ulong, ulong); +void xinit(void); +int xmerge(void*, void*); +void* xspanalloc(ulong, int, ulong); +void xsummary(void); + +void validaddr(void*, ulong, int); +void* vmemchr(void*, int, int); +void hnputv(void*, vlong); +void hnputl(void*, ulong); +void hnputs(void*, ushort); +vlong nhgetv(void*); +ulong nhgetl(void*); +ushort nhgets(void*); diff --git a/os/port/portmkfile b/os/port/portmkfile new file mode 100644 index 00000000..9ee9a7f7 --- /dev/null +++ b/os/port/portmkfile @@ -0,0 +1,152 @@ +PORTHFILES=\ + ../port/error.h\ + ../port/lib.h\ + ../port/portdat.h\ + ../port/portfns.h\ + +LIBFILES=${LIBS:%=$ROOT/Inferno/$OBJTYPE/lib/lib%.a} + +CLEANEXTRA= + +%.$O: %.s + $AS $stem.s + +%.$O: %.c + $CC $CFLAGS $stem.c + +%.$O: ../port/%.c + $CC $CFLAGS -I. ../port/$stem.c + +%.$O: ../ip/%.c + $CC $CFLAGS -I. ../ip/$stem.c + +%.$O: ../kfs/%.c + $CC $CFLAGS -I. ../kfs/$stem.c + +&.$O: $HFILES $PORTHFILES + +$INSTALLDIR/%: % + cp $stem $INSTALLDIR/$stem + +installall:V: install-$SHELLTYPE +all:V: default-$SHELLTYPE + +acid:V: i$CONF.acid +i$CONF.acid:V: $SHELLTYPE-i$CONF.acid + +LIBHDIRS= -I$ROOT/libmp/port -I$ROOT/libsec/port + + +rc-i$CONF.acid nt-i$CONF.acid:V: i$CONF + { + x=i$CONF; test -e i$CONF.p9 && x=i$CONF.p9 + for (i in `{srclist -ec -r $ROOT/ $x}) { + echo '//FILE: ' $i + $CC -I. $CFLAGS $LIBHDIRS '-DKERNDATE='$KERNDATE -a $i + } + echo 'include ("inferno");' + } >i$CONF.acid + +sh-i$CONF.acid:V: i$CONF + x=i$CONF; test -e i$CONF.p9 && x=i$CONF.p9 + for i in `srclist -ec -r $ROOT/ $x` + do + echo '//FILE: ' $i + $CC -I. $CFLAGS $LIBHDIRS '-DKERNDATE='$KERNDATE -a $i + done >i$CONF.acid + echo 'include ("inferno");' >> i$CONF.acid + +lib%.a:V: $SHELLTYPE-lib%.a + +rc-lib%.a nt-lib%.a:VQ: + echo '@{builtin cd' $ROOT/lib$stem ';' mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE install'}' + @{builtin cd $ROOT/lib$stem; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE install} + +sh-lib%.a:VQ: + echo "(cd $ROOT/lib$stem ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install)" + (cd $ROOT/lib$stem; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE install) + +%-rc %-nt:V: + for(i in $CONFLIST) + mk 'CONF='$i $stem + +%-sh:V: + for i in $CONFLIST + do + mk 'CONF='$i $stem + done + +clean:V: cleanconf-$SHELLTYPE + rm -f *.[$OS] *.root.[sh] errstr.h *.out $CLEANEXTRA + +cleanconf-sh:V: + for i in $CONFLIST $CLEANCONFLIST + do + rm -f $i.c i$i i$i.* $i.ver + done + +cleanconf-rc cleanconf-nt:V: + for(i in $CONFLIST $CLEANCONFLIST) + rm -f $i.c i$i i$i.* $i.ver + +nuke-sh:QV: + for i in $LIBDIRS + do + echo "(cd $ROOT/lib$i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE nuke)" + (cd $ROOT/lib$i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE nuke) + done + +nuke-rc nuke-nt:QV: + for (i in $LIBDIRS) + { + echo '@{cd $ROOT/lib$i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE nuke}' + @{cd $ROOT/lib$i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE nuke} + } + +nuke:V: clean nuke-$SHELLTYPE + +$CONF.c: ../port/mkdevc $CONF + $SHELLNAME ../port/mkdevc $CONF > $CONF.c + +errstr.h: ../port/error.h + sed 's/extern //;s,;.*/\* , = ",;s, \*/,";,' < ../port/error.h > errstr.h + +../init/%.dis: ../init/%.b + cd ../init; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE $stem.dis + +$ROOT/libinterp/runt.h: + cd $ROOT/libinterp + mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE runt.h + +RUNT=$ROOT/libinterp/runt.h # for culling dependencies +INTERP=$ROOT/include/interp.h + +alloc.$O: $INTERP +devdbg.$O: $INTERP + +devmnt.$O: $ROOT/include/fcall.h +devns16552.$O: ../port/netif.h +devns16552.$O: ns16552.h +devpipe.$O: $INTERP +devprof.$O: $RUNT $INTERP +devprog.$O: $RUNT $INTERP +devroot.$O: errstr.h +devsign.$O: $RUNT $INTERP +devsrv.$O: $RUNT $INTERP +dis.$O: $INTERP +discall.$O: $INTERP +exception.$O: $RUNT $INTERP +inferno.$O: $RUNT $INTERP +latin1.$O: ../port/latin1.h +main.$O: ../port/error.h +netif.$O: ../port/netif.h +proc.$O: errstr.h $INTERP +screen.$O: screen.h +trap.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h + +devroot.$O: $CONF.root.h +$CONF.$O: $CONF.root.h +$CONF.root.s $CONF.root.h: $CONF ../init/$INIT.dis ../port/mkroot $ROOTFILES + $SHELLNAME ../port/mkroot $CONF + +%.$O: $ROOT/Inferno/$OBJTYPE/include/u.h ../port/lib.h mem.h dat.h fns.h io.h ../port/error.h ../port/portdat.h ../port/portfns.h diff --git a/os/port/print.c b/os/port/print.c new file mode 100644 index 00000000..8a7a86fd --- /dev/null +++ b/os/port/print.c @@ -0,0 +1,31 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +static Lock fmtl; + +void +_fmtlock(void) +{ + lock(&fmtl); +} + +void +_fmtunlock(void) +{ + unlock(&fmtl); +} + +int +_efgfmt(Fmt*) +{ + return -1; +} + +int +errfmt(Fmt*) +{ + return -1; +} diff --git a/os/port/proc.c b/os/port/proc.c new file mode 100644 index 00000000..a652669a --- /dev/null +++ b/os/port/proc.c @@ -0,0 +1,788 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include <interp.h> + +Ref pidalloc; + +struct +{ + Lock; + Proc* arena; + Proc* free; +}procalloc; + +typedef struct +{ + Lock; + Proc* head; + Proc* tail; +}Schedq; + +static Schedq runq[Nrq]; +static ulong occupied; +int nrdy; + +char *statename[] = +{ /* BUG: generate automatically */ + "Dead", + "Moribund", + "Ready", + "Scheding", + "Running", + "Queueing", + "Wakeme", + "Broken", + "Stopped", + "Rendez", +}; + +/* + * Always splhi()'ed. + */ +void +schedinit(void) /* never returns */ +{ + setlabel(&m->sched); + if(up) { +/* + if((e = up->edf) && (e->flags & Admitted)) + edfrecord(up); +*/ + m->proc = nil; + switch(up->state) { + case Running: + ready(up); + break; + case Moribund: + up->state = Dead; +/* + edfstop(up); + if(up->edf){ + free(up->edf); + up->edf = nil; + } +*/ + /* + * Holding locks from pexit: + * procalloc + */ + up->qnext = procalloc.free; + procalloc.free = up; + unlock(&procalloc); + break; + } + up->mach = nil; + up = nil; + } + sched(); +} + +void +sched(void) +{ + if(up) { + splhi(); + procsave(up); + if(setlabel(&up->sched)) { + /* procrestore(up); */ + spllo(); + return; + } + gotolabel(&m->sched); + } + up = runproc(); + up->state = Running; + up->mach = MACHP(m->machno); /* m might be a fixed address; use MACHP */ + m->proc = up; + gotolabel(&up->sched); +} + +void +ready(Proc *p) +{ + int s; + Schedq *rq; + + s = splhi(); +/* + if(edfready(p)){ + splx(s); + return; + } +*/ + rq = &runq[p->pri]; + lock(runq); + p->rnext = 0; + if(rq->tail) + rq->tail->rnext = p; + else + rq->head = p; + rq->tail = p; + + nrdy++; + occupied |= 1<<p->pri; + p->state = Ready; + unlock(runq); + splx(s); +} + +int +anyready(void) +{ + /* same priority only */ + return occupied & (1<<up->pri); +} + +int +anyhigher(void) +{ + return occupied & ((1<<up->pri)-1); +} + +int +preemption(int tick) +{ + if(up != nil && up->state == Running && !up->preempted && + (anyhigher() || tick && anyready())){ + up->preempted = 1; + sched(); + splhi(); + up->preempted = 0; + return 1; + } + return 0; +} + +Proc* +runproc(void) +{ + Proc *p, *l; + Schedq *rq, *erq; + + erq = runq + Nrq - 1; +loop: + splhi(); + for(rq = runq; rq->head == 0; rq++) + if(rq >= erq) { + idlehands(); + spllo(); + goto loop; + } + + if(!canlock(runq)) + goto loop; + /* choose first one we last ran on this processor at this level or hasn't moved recently */ + l = nil; + for(p = rq->head; p != nil; p = p->rnext) + if(p->mp == nil || p->mp == MACHP(m->machno) || p->movetime < MACHP(0)->ticks) + break; + if(p == nil) + p = rq->head; + /* p->mach==0 only when process state is saved */ + if(p == 0 || p->mach) { + unlock(runq); + goto loop; + } + if(p->rnext == nil) + rq->tail = l; + if(l) + l->rnext = p->rnext; + else + rq->head = p->rnext; + if(rq->head == nil){ + rq->tail = nil; + occupied &= ~(1<<p->pri); + } + nrdy--; + if(p->dbgstop){ + p->state = Stopped; + unlock(runq); + goto loop; + } + if(p->state != Ready) + print("runproc %s %lud %s\n", p->text, p->pid, statename[p->state]); + unlock(runq); + p->state = Scheding; + if(p->mp != MACHP(m->machno)) + p->movetime = MACHP(0)->ticks + HZ/10; + p->mp = MACHP(m->machno); + +/* + if(edflock(p)){ + edfrun(p, rq == &runq[PriEdf]); // start deadline timer and do admin + edfunlock(); + } +*/ + return p; +} + +int +setpri(int pri) +{ + int p; + + /* called by up so not on run queue */ + p = up->pri; + up->pri = pri; + if(up->state == Running && anyhigher()) + sched(); + return p; +} + +Proc* +newproc(void) +{ + Proc *p; + + lock(&procalloc); + for(;;) { + if(p = procalloc.free) + break; + + unlock(&procalloc); + resrcwait("no procs"); + lock(&procalloc); + } + procalloc.free = p->qnext; + unlock(&procalloc); + + p->type = Unknown; + p->state = Scheding; + p->pri = PriNormal; + p->psstate = "New"; + p->mach = 0; + p->qnext = 0; + p->fpstate = FPINIT; + p->kp = 0; + p->killed = 0; + p->swipend = 0; + p->mp = 0; + p->movetime = 0; + p->delaysched = 0; + p->edf = nil; + memset(&p->defenv, 0, sizeof(p->defenv)); + p->env = &p->defenv; + p->dbgreg = 0; + kstrdup(&p->env->user, "*nouser"); + p->env->errstr = p->env->errbuf0; + p->env->syserrstr = p->env->errbuf1; + + p->pid = incref(&pidalloc); + if(p->pid == 0) + panic("pidalloc"); + if(p->kstack == 0) + p->kstack = smalloc(KSTACK); + addprog(p); + + return p; +} + +void +procinit(void) +{ + Proc *p; + int i; + + procalloc.free = xalloc(conf.nproc*sizeof(Proc)); + procalloc.arena = procalloc.free; + + p = procalloc.free; + for(i=0; i<conf.nproc-1; i++,p++) + p->qnext = p+1; + p->qnext = 0; + + debugkey('p', "processes", procdump, 0); +} + +void +sleep(Rendez *r, int (*f)(void*), void *arg) +{ + int s; + + if(up == nil) + panic("sleep() not in process (%lux)", getcallerpc(&r)); + /* + * spl is to allow lock to be called + * at interrupt time. lock is mutual exclusion + */ + s = splhi(); + + lock(&up->rlock); + lock(r); + + /* + * if killed or condition happened, never mind + */ + if(up->killed || f(arg)){ + unlock(r); + }else{ + + /* + * now we are committed to + * change state and call scheduler + */ + if(r->p != nil) { + print("double sleep pc=0x%lux %lud %lud r=0x%lux\n", getcallerpc(&r), r->p->pid, up->pid, r); + dumpstack(); + panic("sleep"); + } + up->state = Wakeme; + r->p = up; + unlock(r); + up->swipend = 0; + up->r = r; /* for swiproc */ + unlock(&up->rlock); + + sched(); + splhi(); /* sched does spllo */ + + lock(&up->rlock); + up->r = nil; + } + + if(up->killed || up->swipend) { + up->killed = 0; + up->swipend = 0; + unlock(&up->rlock); + splx(s); + error(Eintr); + } + unlock(&up->rlock); + splx(s); +} + +int +tfn(void *arg) +{ + return MACHP(0)->ticks >= up->twhen || (*up->tfn)(arg); +} + +void +tsleep(Rendez *r, int (*fn)(void*), void *arg, int ms) +{ + ulong when; + Proc *f, **l; + + if(up == nil) + panic("tsleep() not in process (0x%lux)", getcallerpc(&r)); + + when = MS2TK(ms)+MACHP(0)->ticks; + lock(&talarm); + /* take out of list if checkalarm didn't */ + if(up->trend) { + l = &talarm.list; + for(f = *l; f; f = f->tlink) { + if(f == up) { + *l = up->tlink; + break; + } + l = &f->tlink; + } + } + /* insert in increasing time order */ + l = &talarm.list; + for(f = *l; f; f = f->tlink) { + if(f->twhen >= when) + break; + l = &f->tlink; + } + up->trend = r; + up->twhen = when; + up->tfn = fn; + up->tlink = *l; + *l = up; + unlock(&talarm); + + if(waserror()){ + up->twhen = 0; + nexterror(); + } + sleep(r, tfn, arg); + up->twhen = 0; + poperror(); +} + +int +wakeup(Rendez *r) +{ + Proc *p; + int s; + + s = splhi(); + lock(r); + p = r->p; + if(p){ + r->p = nil; + if(p->state != Wakeme) + panic("wakeup: state"); + ready(p); + } + unlock(r); + splx(s); + return p != nil; +} + +void +swiproc(Proc *p, int interp) +{ + ulong s; + Rendez *r; + + if(p == nil) + return; + + s = splhi(); + lock(&p->rlock); + if(!interp) + p->killed = 1; + r = p->r; + if(r != nil) { + lock(r); + if(r->p == p){ + p->swipend = 1; + r->p = nil; + ready(p); + } + unlock(r); + } + unlock(&p->rlock); + splx(s); +} + +void +notkilled(void) +{ + lock(&up->rlock); + up->killed = 0; + unlock(&up->rlock); +} + +void +pexit(char*, int) +{ + Osenv *o; + + up->alarm = 0; + + o = up->env; + if(o != nil){ + closefgrp(o->fgrp); + closepgrp(o->pgrp); + closeegrp(o->egrp); + closesigs(o->sigs); + } + + /* Sched must not loop for this lock */ + lock(&procalloc); + +/* + edfstop(up); +*/ + up->state = Moribund; + sched(); + panic("pexit"); +} + +Proc* +proctab(int i) +{ + return &procalloc.arena[i]; +} + +void +procdump(void) +{ + int i; + char *s; + Proc *p; + char tmp[14]; + + for(i=0; i<conf.nproc; i++) { + p = &procalloc.arena[i]; + if(p->state == Dead) + continue; + + s = p->psstate; + if(s == nil) + s = "kproc"; + if(p->state == Wakeme) + snprint(tmp, sizeof(tmp), " /%.8lux", p->r); + else + *tmp = '\0'; + print("%lux:%3lud:%14s pc %.8lux %s/%s qpc %.8lux pri %d%s\n", + p, p->pid, p->text, p->pc, s, statename[p->state], p->qpc, p->pri, tmp); + } +} + +void +kproc(char *name, void (*func)(void *), void *arg, int flags) +{ + Proc *p; + Pgrp *pg; + Fgrp *fg; + Egrp *eg; + + p = newproc(); + p->psstate = 0; + p->kp = 1; + + p->fpsave = up->fpsave; + p->scallnr = up->scallnr; + p->nerrlab = 0; + + kstrdup(&p->env->user, up->env->user); + if(flags & KPDUPPG) { + pg = up->env->pgrp; + incref(pg); + p->env->pgrp = pg; + } + if(flags & KPDUPFDG) { + fg = up->env->fgrp; + incref(fg); + p->env->fgrp = fg; + } + if(flags & KPDUPENVG) { + eg = up->env->egrp; + if(eg != nil) + incref(eg); + p->env->egrp = eg; + } + + kprocchild(p, func, arg); + + strcpy(p->text, name); + + ready(p); +} + +void +errorf(char *fmt, ...) +{ + va_list arg; + char buf[PRINTSIZE]; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + error(buf); +} + +void +error(char *err) +{ + if(up == nil) + panic("error(%s) not in a process", err); + spllo(); + if(up->nerrlab > NERR) + panic("error stack too deep"); + if(err != up->env->errstr) + kstrcpy(up->env->errstr, err, ERRMAX); + setlabel(&up->errlab[NERR-1]); + nexterror(); +} + +#include "errstr.h" + +/* Set kernel error string */ +void +kerrstr(char *err, uint size) +{ + + char tmp[ERRMAX]; + + kstrcpy(tmp, up->env->errstr, sizeof(tmp)); + kstrcpy(up->env->errstr, err, ERRMAX); + kstrcpy(err, tmp, size); +} + +/* Get kernel error string */ +void +kgerrstr(char *err, uint size) +{ + char tmp[ERRMAX]; + + kstrcpy(tmp, up->env->errstr, sizeof(tmp)); + kstrcpy(up->env->errstr, err, ERRMAX); + kstrcpy(err, tmp, size); +} + +/* Set kernel error string, using formatted print */ +void +kwerrstr(char *fmt, ...) +{ + va_list arg; + char buf[ERRMAX]; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + kstrcpy(up->env->errstr, buf, ERRMAX); +} + +void +werrstr(char *fmt, ...) +{ + va_list arg; + char buf[ERRMAX]; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + kstrcpy(up->env->errstr, buf, ERRMAX); +} + +void +nexterror(void) +{ + gotolabel(&up->errlab[--up->nerrlab]); +} + +/* for dynamic modules - functions not macros */ + +void* +waserr(void) +{ + up->nerrlab++; + return &up->errlab[up->nerrlab-1]; +} + +void +poperr(void) +{ + up->nerrlab--; +} + +char* +enverror(void) +{ + return up->env->errstr; +} + +void +exhausted(char *resource) +{ + char buf[64]; + + snprint(buf, sizeof(buf), "no free %s", resource); + iprint("%s\n", buf); + error(buf); +} + +/* + * change ownership to 'new' of all processes owned by 'old'. Used when + * eve changes. + */ +void +renameuser(char *old, char *new) +{ + Proc *p, *ep; + Osenv *o; + + ep = procalloc.arena+conf.nproc; + for(p = procalloc.arena; p < ep; p++) { + o = &p->defenv; + if(o->user != nil && strcmp(o->user, old) == 0) + kstrdup(&o->user, new); + } +} + +int +return0(void*) +{ + return 0; +} + +void +setid(char *name, int owner) +{ + if(!owner || iseve()) + kstrdup(&up->env->user, name); +} + +void +rptwakeup(void *o, void *ar) +{ + Rept *r; + + r = ar; + if(r == nil) + return; + lock(&r->l); + r->o = o; + unlock(&r->l); + wakeup(&r->r); +} + +static int +rptactive(void *a) +{ + Rept *r = a; + int i; + lock(&r->l); + i = r->active(r->o); + unlock(&r->l); + return i; +} + +static void +rproc(void *a) +{ + long now, then; + ulong t; + int i; + void *o; + Rept *r; + + r = a; + t = r->t; + +Wait: + sleep(&r->r, rptactive, r); + lock(&r->l); + o = r->o; + unlock(&r->l); + then = TK2MS(MACHP(0)->ticks); + for(;;){ + tsleep(&up->sleep, return0, nil, t); + now = TK2MS(MACHP(0)->ticks); + if(waserror()) + break; + i = r->ck(o, now-then); + poperror(); + if(i == -1) + goto Wait; + if(i == 0) + continue; + then = now; + acquire(); + if(waserror()) { + release(); + break; + } + r->f(o); + poperror(); + release(); + } + pexit("", 0); +} + +void* +rptproc(char *s, int t, void *o, int (*active)(void*), int (*ck)(void*, int), void (*f)(void*)) +{ + Rept *r; + + r = mallocz(sizeof(Rept), 1); + if(r == nil) + return nil; + r->t = t; + r->active = active; + r->ck = ck; + r->f = f; + r->o = o; + kproc(s, rproc, r, KPDUP); + return r; +} diff --git a/os/port/qio.c b/os/port/qio.c new file mode 100644 index 00000000..d44ea6b8 --- /dev/null +++ b/os/port/qio.c @@ -0,0 +1,1529 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +static ulong padblockcnt; +static ulong concatblockcnt; +static ulong pullupblockcnt; +static ulong copyblockcnt; +static ulong consumecnt; +static ulong producecnt; +static ulong qcopycnt; + +static int debugging; + +#define QDEBUG if(0) + +/* + * IO queues + */ +typedef struct Queue Queue; + +struct Queue +{ + Lock; + + Block* bfirst; /* buffer */ + Block* blast; + + int len; /* bytes allocated to queue */ + int dlen; /* data bytes in queue */ + int limit; /* max bytes in queue */ + int inilim; /* initial limit */ + int state; + int noblock; /* true if writes return immediately when q full */ + int eof; /* number of eofs read by user */ + + void (*kick)(void*); /* restart output */ + void (*bypass)(void*, Block*); /* bypass queue altogether */ + void* arg; /* argument to kick */ + + QLock rlock; /* mutex for reading processes */ + Rendez rr; /* process waiting to read */ + QLock wlock; /* mutex for writing processes */ + Rendez wr; /* process waiting to write */ + + char err[ERRMAX]; +}; + +enum +{ + Maxatomic = 64*1024, +}; + +uint qiomaxatomic = Maxatomic; + +void +ixsummary(void) +{ + debugging ^= 1; + iallocsummary(); + print("pad %lud, concat %lud, pullup %lud, copy %lud\n", + padblockcnt, concatblockcnt, pullupblockcnt, copyblockcnt); + print("consume %lud, produce %lud, qcopy %lud\n", + consumecnt, producecnt, qcopycnt); +} + +/* + * free a list of blocks + */ +void +freeblist(Block *b) +{ + Block *next; + + for(; b != 0; b = next){ + next = b->next; + b->next = 0; + freeb(b); + } +} + +/* + * pad a block to the front (or the back if size is negative) + */ +Block* +padblock(Block *bp, int size) +{ + int n; + Block *nbp; + + QDEBUG checkb(bp, "padblock 1"); + if(size >= 0){ + if(bp->rp - bp->base >= size){ + bp->rp -= size; + return bp; + } + + if(bp->next) + panic("padblock 0x%luX", getcallerpc(&bp)); + n = BLEN(bp); + padblockcnt++; + nbp = allocb(size+n); + nbp->rp += size; + nbp->wp = nbp->rp; + memmove(nbp->wp, bp->rp, n); + nbp->wp += n; + freeb(bp); + nbp->rp -= size; + } else { + size = -size; + + if(bp->next) + panic("padblock 0x%luX", getcallerpc(&bp)); + + if(bp->lim - bp->wp >= size) + return bp; + + n = BLEN(bp); + padblockcnt++; + nbp = allocb(size+n); + memmove(nbp->wp, bp->rp, n); + nbp->wp += n; + freeb(bp); + } + QDEBUG checkb(nbp, "padblock 1"); + return nbp; +} + +/* + * return count of bytes in a string of blocks + */ +int +blocklen(Block *bp) +{ + int len; + + len = 0; + while(bp) { + len += BLEN(bp); + bp = bp->next; + } + return len; +} + +/* + * return count of space in blocks + */ +int +blockalloclen(Block *bp) +{ + int len; + + len = 0; + while(bp) { + len += BALLOC(bp); + bp = bp->next; + } + return len; +} + +/* + * copy the string of blocks into + * a single block and free the string + */ +Block* +concatblock(Block *bp) +{ + int len; + Block *nb, *f; + + if(bp->next == 0) + return bp; + + nb = allocb(blocklen(bp)); + for(f = bp; f; f = f->next) { + len = BLEN(f); + memmove(nb->wp, f->rp, len); + nb->wp += len; + } + concatblockcnt += BLEN(nb); + freeblist(bp); + QDEBUG checkb(nb, "concatblock 1"); + return nb; +} + +/* + * make sure the first block has at least n bytes + */ +Block* +pullupblock(Block *bp, int n) +{ + int i; + Block *nbp; + + /* + * this should almost always be true, it's + * just to avoid every caller checking. + */ + if(BLEN(bp) >= n) + return bp; + + /* + * if not enough room in the first block, + * add another to the front of the list. + */ + if(bp->lim - bp->rp < n){ + nbp = allocb(n); + nbp->next = bp; + bp = nbp; + } + + /* + * copy bytes from the trailing blocks into the first + */ + n -= BLEN(bp); + while(nbp = bp->next){ + i = BLEN(nbp); + if(i > n) { + memmove(bp->wp, nbp->rp, n); + pullupblockcnt++; + bp->wp += n; + nbp->rp += n; + QDEBUG checkb(bp, "pullupblock 1"); + return bp; + } + else { + memmove(bp->wp, nbp->rp, i); + pullupblockcnt++; + bp->wp += i; + bp->next = nbp->next; + nbp->next = 0; + freeb(nbp); + n -= i; + if(n == 0){ + QDEBUG checkb(bp, "pullupblock 2"); + return bp; + } + } + } + freeb(bp); + return 0; +} + +/* + * make sure the first block has at least n bytes + */ +Block* +pullupqueue(Queue *q, int n) +{ + Block *b; + + if(BLEN(q->bfirst) >= n) + return q->bfirst; + q->bfirst = pullupblock(q->bfirst, n); + for(b = q->bfirst; b != nil && b->next != nil; b = b->next) + ; + q->blast = b; + return q->bfirst; +} + +/* + * trim to len bytes starting at offset + */ +Block * +trimblock(Block *bp, int offset, int len) +{ + ulong l; + Block *nb, *startb; + + QDEBUG checkb(bp, "trimblock 1"); + if(blocklen(bp) < offset+len) { + freeblist(bp); + return nil; + } + + while((l = BLEN(bp)) < offset) { + offset -= l; + nb = bp->next; + bp->next = nil; + freeb(bp); + bp = nb; + } + + startb = bp; + bp->rp += offset; + + while((l = BLEN(bp)) < len) { + len -= l; + bp = bp->next; + } + + bp->wp -= (BLEN(bp) - len); + + if(bp->next) { + freeblist(bp->next); + bp->next = nil; + } + + return startb; +} + +/* + * copy 'count' bytes into a new block + */ +Block* +copyblock(Block *bp, int count) +{ + int l; + Block *nbp; + + QDEBUG checkb(bp, "copyblock 0"); + nbp = allocb(count); + for(; count > 0 && bp != 0; bp = bp->next){ + l = BLEN(bp); + if(l > count) + l = count; + memmove(nbp->wp, bp->rp, l); + nbp->wp += l; + count -= l; + } + if(count > 0){ + memset(nbp->wp, 0, count); + nbp->wp += count; + } + copyblockcnt++; + QDEBUG checkb(nbp, "copyblock 1"); + + return nbp; +} + +Block* +adjustblock(Block* bp, int len) +{ + int n; + Block *nbp; + + if(len < 0){ + freeb(bp); + return nil; + } + + if(bp->rp+len > bp->lim){ + nbp = copyblock(bp, len); + freeblist(bp); + QDEBUG checkb(nbp, "adjustblock 1"); + + return nbp; + } + + n = BLEN(bp); + if(len > n) + memset(bp->wp, 0, len-n); + bp->wp = bp->rp+len; + QDEBUG checkb(bp, "adjustblock 2"); + + return bp; +} + + +/* + * throw away up to count bytes from a + * list of blocks. Return count of bytes + * thrown away. + */ +int +pullblock(Block **bph, int count) +{ + Block *bp; + int n, bytes; + + bytes = 0; + if(bph == nil) + return 0; + + while(*bph != nil && count != 0) { + bp = *bph; + n = BLEN(bp); + if(count < n) + n = count; + bytes += n; + count -= n; + bp->rp += n; + QDEBUG checkb(bp, "pullblock "); + if(BLEN(bp) == 0) { + *bph = bp->next; + bp->next = nil; + freeb(bp); + } + } + return bytes; +} + +/* + * get next block from a queue, return null if nothing there + */ +Block* +qget(Queue *q) +{ + int dowakeup; + Block *b; + + /* sync with qwrite */ + ilock(q); + + b = q->bfirst; + if(b == nil){ + q->state |= Qstarve; + iunlock(q); + return nil; + } + q->bfirst = b->next; + b->next = 0; + q->len -= BALLOC(b); + q->dlen -= BLEN(b); + QDEBUG checkb(b, "qget"); + + /* if writer flow controlled, restart */ + if((q->state & Qflow) && q->len < q->limit/2){ + q->state &= ~Qflow; + dowakeup = 1; + } else + dowakeup = 0; + + iunlock(q); + + if(dowakeup) + wakeup(&q->wr); + + return b; +} + +/* + * throw away the next 'len' bytes in the queue + * returning the number actually discarded + */ +int +qdiscard(Queue *q, int len) +{ + Block *b; + int dowakeup, n, sofar; + + ilock(q); + for(sofar = 0; sofar < len; sofar += n){ + b = q->bfirst; + if(b == nil) + break; + QDEBUG checkb(b, "qdiscard"); + n = BLEN(b); + if(n <= len - sofar){ + q->bfirst = b->next; + b->next = 0; + q->len -= BALLOC(b); + q->dlen -= BLEN(b); + freeb(b); + } else { + n = len - sofar; + b->rp += n; + q->dlen -= n; + } + } + + /* + * if writer flow controlled, restart + * + * This used to be + * q->len < q->limit/2 + * but it slows down tcp too much for certain write sizes. + * I really don't understand it completely. It may be + * due to the queue draining so fast that the transmission + * stalls waiting for the app to produce more data. - presotto + */ + if((q->state & Qflow) && q->len < q->limit){ + q->state &= ~Qflow; + dowakeup = 1; + } else + dowakeup = 0; + + iunlock(q); + + if(dowakeup) + wakeup(&q->wr); + + return sofar; +} + +/* + * Interrupt level copy out of a queue, return # bytes copied. + */ +int +qconsume(Queue *q, void *vp, int len) +{ + Block *b; + int n, dowakeup; + uchar *p = vp; + Block *tofree = nil; + + /* sync with qwrite */ + ilock(q); + + for(;;) { + b = q->bfirst; + if(b == 0){ + q->state |= Qstarve; + iunlock(q); + return -1; + } + QDEBUG checkb(b, "qconsume 1"); + + n = BLEN(b); + if(n > 0) + break; + q->bfirst = b->next; + q->len -= BALLOC(b); + + /* remember to free this */ + b->next = tofree; + tofree = b; + }; + + if(n < len) + len = n; + memmove(p, b->rp, len); + consumecnt += n; + b->rp += len; + q->dlen -= len; + + /* discard the block if we're done with it */ + if((q->state & Qmsg) || len == n){ + q->bfirst = b->next; + b->next = 0; + q->len -= BALLOC(b); + q->dlen -= BLEN(b); + + /* remember to free this */ + b->next = tofree; + tofree = b; + } + + /* if writer flow controlled, restart */ + if((q->state & Qflow) && q->len < q->limit/2){ + q->state &= ~Qflow; + dowakeup = 1; + } else + dowakeup = 0; + + iunlock(q); + + if(dowakeup) + wakeup(&q->wr); + + if(tofree != nil) + freeblist(tofree); + + return len; +} + +int +qpass(Queue *q, Block *b) +{ + int dlen, len, dowakeup; + + /* sync with qread */ + dowakeup = 0; + ilock(q); + if(q->len >= q->limit){ + freeblist(b); + iunlock(q); + return -1; + } + if(q->state & Qclosed){ + len = blocklen(b); + freeblist(b); + iunlock(q); + return len; + } + + /* add buffer to queue */ + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + len = BALLOC(b); + dlen = BLEN(b); + QDEBUG checkb(b, "qpass"); + while(b->next){ + b = b->next; + QDEBUG checkb(b, "qpass"); + len += BALLOC(b); + dlen += BLEN(b); + } + q->blast = b; + q->len += len; + q->dlen += dlen; + + if(q->len >= q->limit/2) + q->state |= Qflow; + + if(q->state & Qstarve){ + q->state &= ~Qstarve; + dowakeup = 1; + } + iunlock(q); + + if(dowakeup) + wakeup(&q->rr); + + return len; +} + +int +qpassnolim(Queue *q, Block *b) +{ + int dlen, len, dowakeup; + + /* sync with qread */ + dowakeup = 0; + ilock(q); + + if(q->state & Qclosed){ + freeblist(b); + iunlock(q); + return BALLOC(b); + } + + /* add buffer to queue */ + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + len = BALLOC(b); + dlen = BLEN(b); + QDEBUG checkb(b, "qpass"); + while(b->next){ + b = b->next; + QDEBUG checkb(b, "qpass"); + len += BALLOC(b); + dlen += BLEN(b); + } + q->blast = b; + q->len += len; + q->dlen += dlen; + + if(q->len >= q->limit/2) + q->state |= Qflow; + + if(q->state & Qstarve){ + q->state &= ~Qstarve; + dowakeup = 1; + } + iunlock(q); + + if(dowakeup) + wakeup(&q->rr); + + return len; +} + +/* + * if the allocated space is way out of line with the used + * space, reallocate to a smaller block + */ +Block* +packblock(Block *bp) +{ + Block **l, *nbp; + int n; + + for(l = &bp; *l; l = &(*l)->next){ + nbp = *l; + n = BLEN(nbp); + if((n<<2) < BALLOC(nbp)){ + *l = allocb(n); + memmove((*l)->wp, nbp->rp, n); + (*l)->wp += n; + (*l)->next = nbp->next; + freeb(nbp); + } + } + + return bp; +} + +int +qproduce(Queue *q, void *vp, int len) +{ + Block *b; + int dowakeup; + uchar *p = vp; + + /* sync with qread */ + dowakeup = 0; + ilock(q); + + /* no waiting receivers, room in buffer? */ + if(q->len >= q->limit){ + q->state |= Qflow; + iunlock(q); + return -1; + } + + /* save in buffer */ + /* use Qcoalesce here to save storage */ + b = q->blast; + if((q->state & Qcoalesce)==0 || q->bfirst==nil || b->lim-b->wp < len){ + /* need a new block */ + b = iallocb(len); + if(b == 0){ + iunlock(q); + return 0; + } + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + q->blast = b; + /* b->next = 0; done by iallocb() */ + q->len += BALLOC(b); + } + memmove(b->wp, p, len); + producecnt += len; + b->wp += len; + q->dlen += len; + QDEBUG checkb(b, "qproduce"); + + if(q->state & Qstarve){ + q->state &= ~Qstarve; + dowakeup = 1; + } + + if(q->len >= q->limit) + q->state |= Qflow; + iunlock(q); + + if(dowakeup) + wakeup(&q->rr); + + return len; +} + +/* + * copy from offset in the queue + */ +Block* +qcopy(Queue *q, int len, ulong offset) +{ + int sofar; + int n; + Block *b, *nb; + uchar *p; + + nb = allocb(len); + + ilock(q); + + /* go to offset */ + b = q->bfirst; + for(sofar = 0; ; sofar += n){ + if(b == nil){ + iunlock(q); + return nb; + } + n = BLEN(b); + if(sofar + n > offset){ + p = b->rp + offset - sofar; + n -= offset - sofar; + break; + } + QDEBUG checkb(b, "qcopy"); + b = b->next; + } + + /* copy bytes from there */ + for(sofar = 0; sofar < len;){ + if(n > len - sofar) + n = len - sofar; + memmove(nb->wp, p, n); + qcopycnt += n; + sofar += n; + nb->wp += n; + b = b->next; + if(b == nil) + break; + n = BLEN(b); + p = b->rp; + } + iunlock(q); + + return nb; +} + +/* + * called by non-interrupt code + */ +Queue* +qopen(int limit, int msg, void (*kick)(void*), void *arg) +{ + Queue *q; + + q = malloc(sizeof(Queue)); + if(q == 0) + return 0; + + q->limit = q->inilim = limit; + q->kick = kick; + q->arg = arg; + q->state = msg; + q->state |= Qstarve; + q->eof = 0; + q->noblock = 0; + + return q; +} + +/* open a queue to be bypassed */ +Queue* +qbypass(void (*bypass)(void*, Block*), void *arg) +{ + Queue *q; + + q = malloc(sizeof(Queue)); + if(q == 0) + return 0; + + q->limit = 0; + q->arg = arg; + q->bypass = bypass; + q->state = 0; + + return q; +} + +static int +notempty(void *a) +{ + Queue *q = a; + + return (q->state & Qclosed) || q->bfirst != 0; +} + +/* + * wait for the queue to be non-empty or closed. + * called with q ilocked. + */ +static int +qwait(Queue *q) +{ + /* wait for data */ + for(;;){ + if(q->bfirst != nil) + break; + + if(q->state & Qclosed){ + if(++q->eof > 3) + return -1; + if(*q->err && strcmp(q->err, Ehungup) != 0) + return -1; + return 0; + } + + q->state |= Qstarve; /* flag requesting producer to wake me */ + iunlock(q); + sleep(&q->rr, notempty, q); + ilock(q); + } + return 1; +} + +/* + * add a block list to a queue + */ +void +qaddlist(Queue *q, Block *b) +{ + /* queue the block */ + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + q->len += blockalloclen(b); + q->dlen += blocklen(b); + while(b->next) + b = b->next; + q->blast = b; +} + +/* + * called with q ilocked + */ +Block* +qremove(Queue *q) +{ + Block *b; + + b = q->bfirst; + if(b == nil) + return nil; + q->bfirst = b->next; + b->next = nil; + q->dlen -= BLEN(b); + q->len -= BALLOC(b); + QDEBUG checkb(b, "qremove"); + return b; +} + +/* + * copy the contents of a string of blocks into + * memory. emptied blocks are freed. return + * pointer to first unconsumed block. + */ +Block* +bl2mem(uchar *p, Block *b, int n) +{ + int i; + Block *next; + + for(; b != nil; b = next){ + i = BLEN(b); + if(i > n){ + memmove(p, b->rp, n); + b->rp += n; + return b; + } + memmove(p, b->rp, i); + n -= i; + p += i; + b->rp += i; + next = b->next; + freeb(b); + } + return nil; +} + +/* + * copy the contents of memory into a string of blocks. + * return nil on error. + */ +Block* +mem2bl(uchar *p, int len) +{ + int n; + Block *b, *first, **l; + + first = nil; + l = &first; + if(waserror()){ + freeblist(first); + nexterror(); + } + do { + n = len; + if(n > Maxatomic) + n = Maxatomic; + + *l = b = allocb(n); + setmalloctag(b, getcallerpc(&p)); + memmove(b->wp, p, n); + b->wp += n; + p += n; + len -= n; + l = &b->next; + } while(len > 0); + poperror(); + + return first; +} + +/* + * put a block back to the front of the queue + * called with q ilocked + */ +void +qputback(Queue *q, Block *b) +{ + b->next = q->bfirst; + if(q->bfirst == nil) + q->blast = b; + q->bfirst = b; + q->len += BALLOC(b); + q->dlen += BLEN(b); +} + +/* + * flow control, get producer going again + * called with q ilocked + */ +static void +qwakeup_iunlock(Queue *q) +{ + int dowakeup = 0; + + /* if writer flow controlled, restart */ + if((q->state & Qflow) && q->len < q->limit/2){ + q->state &= ~Qflow; + dowakeup = 1; + } + + iunlock(q); + + /* wakeup flow controlled writers */ + if(dowakeup){ + if(q->kick) + q->kick(q->arg); + wakeup(&q->wr); + } +} + +/* + * get next block from a queue (up to a limit) + */ +Block* +qbread(Queue *q, int len) +{ + Block *b, *nb; + int n; + + qlock(&q->rlock); + if(waserror()){ + qunlock(&q->rlock); + nexterror(); + } + + ilock(q); + switch(qwait(q)){ + case 0: + /* queue closed */ + iunlock(q); + qunlock(&q->rlock); + poperror(); + return nil; + case -1: + /* multiple reads on a closed queue */ + iunlock(q); + error(q->err); + } + + /* if we get here, there's at least one block in the queue */ + b = qremove(q); + n = BLEN(b); + + /* split block if it's too big and this is not a message queue */ + nb = b; + if(n > len){ + if((q->state&Qmsg) == 0){ + n -= len; + b = allocb(n); + memmove(b->wp, nb->rp+len, n); + b->wp += n; + qputback(q, b); + } + nb->wp = nb->rp + len; + } + + /* restart producer */ + qwakeup_iunlock(q); + + poperror(); + qunlock(&q->rlock); + return nb; +} + +/* + * read a queue. if no data is queued, post a Block + * and wait on its Rendez. + */ +long +qread(Queue *q, void *vp, int len) +{ + Block *b, *first, **l; + int m, n; + + qlock(&q->rlock); + if(waserror()){ + qunlock(&q->rlock); + nexterror(); + } + + ilock(q); +again: + switch(qwait(q)){ + case 0: + /* queue closed */ + iunlock(q); + qunlock(&q->rlock); + poperror(); + return 0; + case -1: + /* multiple reads on a closed queue */ + iunlock(q); + error(q->err); + } + + /* if we get here, there's at least one block in the queue */ + if(q->state & Qcoalesce){ + /* when coalescing, 0 length blocks just go away */ + b = q->bfirst; + if(BLEN(b) <= 0){ + freeb(qremove(q)); + goto again; + } + + /* grab the first block plus as many + * following blocks as will completely + * fit in the read. + */ + n = 0; + l = &first; + m = BLEN(b); + for(;;) { + *l = qremove(q); + l = &b->next; + n += m; + + b = q->bfirst; + if(b == nil) + break; + m = BLEN(b); + if(n+m > len) + break; + } + } else { + first = qremove(q); + n = BLEN(first); + } + + /* copy to user space outside of the ilock */ + iunlock(q); + b = bl2mem(vp, first, len); + ilock(q); + + /* take care of any left over partial block */ + if(b != nil){ + n -= BLEN(b); + if(q->state & Qmsg) + freeb(b); + else + qputback(q, b); + } + + /* restart producer */ + qwakeup_iunlock(q); + + poperror(); + qunlock(&q->rlock); + return n; +} + +static int +qnotfull(void *a) +{ + Queue *q = a; + + return q->len < q->limit || (q->state & Qclosed); +} + +ulong noblockcnt; + +/* + * add a block to a queue obeying flow control + */ +long +qbwrite(Queue *q, Block *b) +{ + int n, dowakeup; + + n = BLEN(b); + + if(q->bypass){ + (*q->bypass)(q->arg, b); + return n; + } + + dowakeup = 0; + qlock(&q->wlock); + if(waserror()){ + if(b != nil) + freeb(b); + qunlock(&q->wlock); + nexterror(); + } + + ilock(q); + + /* give up if the queue is closed */ + if(q->state & Qclosed){ + iunlock(q); + error(q->err); + } + + /* if nonblocking, don't queue over the limit */ + if(q->len >= q->limit){ + if(q->noblock){ + iunlock(q); + freeb(b); + noblockcnt += n; + qunlock(&q->wlock); + poperror(); + return n; + } + } + + /* queue the block */ + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + q->blast = b; + b->next = 0; + q->len += BALLOC(b); + q->dlen += n; + QDEBUG checkb(b, "qbwrite"); + b = nil; + + /* make sure other end gets awakened */ + if(q->state & Qstarve){ + q->state &= ~Qstarve; + dowakeup = 1; + } + iunlock(q); + + /* get output going again */ + if(q->kick && (dowakeup || (q->state&Qkick))) + q->kick(q->arg); + + /* wakeup anyone consuming at the other end */ + if(dowakeup) + wakeup(&q->rr); + + /* + * flow control, wait for queue to get below the limit + * before allowing the process to continue and queue + * more. We do this here so that postnote can only + * interrupt us after the data has been queued. This + * means that things like 9p flushes and ssl messages + * will not be disrupted by software interrupts. + * + * Note - this is moderately dangerous since a process + * that keeps getting interrupted and rewriting will + * queue infinite crud. + */ + for(;;){ + if(q->noblock || qnotfull(q)) + break; + + ilock(q); + q->state |= Qflow; + iunlock(q); + sleep(&q->wr, qnotfull, q); + } + USED(b); + + qunlock(&q->wlock); + poperror(); + return n; +} + +/* + * write to a queue. only Maxatomic bytes at a time is atomic. + */ +int +qwrite(Queue *q, void *vp, int len) +{ + int n, sofar; + Block *b; + uchar *p = vp; + + QDEBUG if(!islo()) + print("qwrite hi %lux\n", getcallerpc(&q)); + + sofar = 0; + do { + n = len-sofar; + if(n > Maxatomic) + n = Maxatomic; + + b = allocb(n); + setmalloctag(b, getcallerpc(&q)); + if(waserror()){ + freeb(b); + nexterror(); + } + memmove(b->wp, p+sofar, n); + poperror(); + b->wp += n; + + qbwrite(q, b); + + sofar += n; + } while(sofar < len && (q->state & Qmsg) == 0); + + return len; +} + +/* + * used by print() to write to a queue. Since we may be splhi or not in + * a process, don't qlock. + */ +int +qiwrite(Queue *q, void *vp, int len) +{ + int n, sofar, dowakeup; + Block *b; + uchar *p = vp; + + dowakeup = 0; + + sofar = 0; + do { + n = len-sofar; + if(n > Maxatomic) + n = Maxatomic; + + b = iallocb(n); + if(b == nil) + break; + memmove(b->wp, p+sofar, n); + b->wp += n; + + ilock(q); + + QDEBUG checkb(b, "qiwrite"); + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + q->blast = b; + q->len += BALLOC(b); + q->dlen += n; + + if(q->state & Qstarve){ + q->state &= ~Qstarve; + dowakeup = 1; + } + + iunlock(q); + + if(dowakeup){ + if(q->kick) + q->kick(q->arg); + wakeup(&q->rr); + } + + sofar += n; + } while(sofar < len && (q->state & Qmsg) == 0); + + return sofar; +} + +/* + * be extremely careful when calling this, + * as there is no reference accounting + */ +void +qfree(Queue *q) +{ + qclose(q); + free(q); +} + +/* + * Mark a queue as closed. No further IO is permitted. + * All blocks are released. + */ +void +qclose(Queue *q) +{ + Block *bfirst; + + if(q == nil) + return; + + /* mark it */ + ilock(q); + q->state |= Qclosed; + q->state &= ~(Qflow|Qstarve); + strcpy(q->err, Ehungup); + bfirst = q->bfirst; + q->bfirst = 0; + q->len = 0; + q->dlen = 0; + q->noblock = 0; + iunlock(q); + + /* free queued blocks */ + freeblist(bfirst); + + /* wake up readers/writers */ + wakeup(&q->rr); + wakeup(&q->wr); +} + +/* + * Mark a queue as closed. Wakeup any readers. Don't remove queued + * blocks. + */ +void +qhangup(Queue *q, char *msg) +{ + /* mark it */ + ilock(q); + q->state |= Qclosed; + if(msg == 0 || *msg == 0) + strcpy(q->err, Ehungup); + else + strncpy(q->err, msg, ERRMAX-1); + iunlock(q); + + /* wake up readers/writers */ + wakeup(&q->rr); + wakeup(&q->wr); +} + +/* + * return non-zero if the q is hungup + */ +int +qisclosed(Queue *q) +{ + return q->state & Qclosed; +} + +/* + * mark a queue as no longer hung up + */ +void +qreopen(Queue *q) +{ + ilock(q); + q->state &= ~Qclosed; + q->state |= Qstarve; + q->eof = 0; + q->limit = q->inilim; + iunlock(q); +} + +/* + * return bytes queued + */ +int +qlen(Queue *q) +{ + return q->dlen; +} + +/* + * return space remaining before flow control + */ +int +qwindow(Queue *q) +{ + int l; + + l = q->limit - q->len; + if(l < 0) + l = 0; + return l; +} + +/* + * return true if we can read without blocking + */ +int +qcanread(Queue *q) +{ + return q->bfirst!=0; +} + +/* + * change queue limit + */ +void +qsetlimit(Queue *q, int limit) +{ + q->limit = limit; +} + +/* + * set blocking/nonblocking + */ +void +qnoblock(Queue *q, int onoff) +{ + q->noblock = onoff; +} + +/* + * flush the output queue + */ +void +qflush(Queue *q) +{ + Block *bfirst; + + /* mark it */ + ilock(q); + bfirst = q->bfirst; + q->bfirst = 0; + q->len = 0; + q->dlen = 0; + iunlock(q); + + /* free queued blocks */ + freeblist(bfirst); + + /* wake up readers/writers */ + wakeup(&q->wr); +} + +int +qfull(Queue *q) +{ + return q->state & Qflow; +} + +int +qstate(Queue *q) +{ + return q->state; +} + +void +qdump(Queue *q) +{ + if(q) + kprint("q=%p bfirst=%p blast=%p len=%d dlen=%d limit=%d state=#%x\n", + q, q->bfirst, q->blast, q->len, q->dlen, q->limit, q->state); +} diff --git a/os/port/qlock.c b/os/port/qlock.c new file mode 100644 index 00000000..63d7c329 --- /dev/null +++ b/os/port/qlock.c @@ -0,0 +1,111 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +void +qlock(QLock *q) +{ + Proc *p, *mp; + + lock(&q->use); + if(!q->locked) { + q->locked = 1; + unlock(&q->use); + return; + } + p = q->tail; + mp = up; + if(p == 0) + q->head = mp; + else + p->qnext = mp; + q->tail = mp; + mp->qnext = 0; + mp->state = Queueing; + up->qpc = getcallerpc(&q); + unlock(&q->use); + sched(); +} + +int +canqlock(QLock *q) +{ + if(!canlock(&q->use)) + return 0; + if(q->locked){ + unlock(&q->use); + return 0; + } + q->locked = 1; + unlock(&q->use); + return 1; +} + +void +qunlock(QLock *q) +{ + Proc *p; + + lock(&q->use); + p = q->head; + if(p) { + q->head = p->qnext; + if(q->head == 0) + q->tail = 0; + unlock(&q->use); + ready(p); + return; + } + q->locked = 0; + unlock(&q->use); +} + +void +rlock(RWlock *l) +{ + qlock(&l->x); /* wait here for writers and exclusion */ + lock(l); + l->readers++; + canqlock(&l->k); /* block writers if we are the first reader */ + unlock(l); + qunlock(&l->x); +} + +/* same as rlock but punts if there are any writers waiting */ +int +canrlock(RWlock *l) +{ + if (!canqlock(&l->x)) + return 0; + lock(l); + l->readers++; + canqlock(&l->k); /* block writers if we are the first reader */ + unlock(l); + qunlock(&l->x); + return 1; +} + +void +runlock(RWlock *l) +{ + lock(l); + if(--l->readers == 0) /* last reader out allows writers */ + qunlock(&l->k); + unlock(l); +} + +void +wlock(RWlock *l) +{ + qlock(&l->x); /* wait here for writers and exclusion */ + qlock(&l->k); /* wait here for last reader */ +} + +void +wunlock(RWlock *l) +{ + qunlock(&l->k); + qunlock(&l->x); +} diff --git a/os/port/random.c b/os/port/random.c new file mode 100644 index 00000000..720541c0 --- /dev/null +++ b/os/port/random.c @@ -0,0 +1,156 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +static struct +{ + QLock; + Rendez producer; + Rendez consumer; + ulong randomcount; + uchar buf[1024]; + uchar *ep; + uchar *rp; + uchar *wp; + uchar next; + uchar bits; + uchar wakeme; + uchar filled; + int target; + int kprocstarted; + ulong randn; +} rb; + +static int +rbnotfull(void*) +{ + int i; + + i = rb.wp - rb.rp; + if(i < 0) + i += sizeof(rb.buf); + return i < rb.target; +} + +static int +rbnotempty(void*) +{ + return rb.wp != rb.rp; +} + +static void +genrandom(void*) +{ + setpri(PriBackground); + + for(;;) { + for(;;) + if(++rb.randomcount > 100000) + break; + if(anyhigher()) + sched(); + if(!rbnotfull(0)) + sleep(&rb.producer, rbnotfull, 0); + } +} + +/* + * produce random bits in a circular buffer + */ +static void +randomclock(void) +{ + uchar *p; + + if(rb.randomcount == 0) + return; + + if(!rbnotfull(0)) { + rb.filled = 1; + return; + } + + rb.bits = (rb.bits<<2) ^ (rb.randomcount&3); + rb.randomcount = 0; + + rb.next += 2; + if(rb.next != 8) + return; + + rb.next = 0; + *rb.wp ^= rb.bits ^ *rb.rp; + p = rb.wp+1; + if(p == rb.ep) + p = rb.buf; + rb.wp = p; + + if(rb.wakeme) + wakeup(&rb.consumer); +} + +void +randominit(void) +{ + /* Frequency close but not equal to HZ */ + addclock0link(randomclock, 13); + rb.target = 16; + rb.ep = rb.buf + sizeof(rb.buf); + rb.rp = rb.wp = rb.buf; +} + +/* + * consume random bytes from a circular buffer + */ +ulong +randomread(void *xp, ulong n) +{ + int i, sofar; + uchar *e, *p; + + p = xp; + + qlock(&rb); + if(waserror()){ + qunlock(&rb); + nexterror(); + } + if(!rb.kprocstarted){ + rb.kprocstarted = 1; + kproc("genrand", genrandom, nil, 0); + } + + for(sofar = 0; sofar < n; sofar += i){ + i = rb.wp - rb.rp; + if(i == 0){ + rb.wakeme = 1; + wakeup(&rb.producer); + sleep(&rb.consumer, rbnotempty, 0); + rb.wakeme = 0; + continue; + } + if(i < 0) + i = rb.ep - rb.rp; + if((i+sofar) > n) + i = n - sofar; + memmove(p + sofar, rb.rp, i); + e = rb.rp + i; + if(e == rb.ep) + e = rb.buf; + rb.rp = e; + } + if(rb.filled && rb.wp == rb.rp){ + i = 2*rb.target; + if(i > sizeof(rb.buf) - 1) + i = sizeof(rb.buf) - 1; + rb.target = i; + rb.filled = 0; + } + poperror(); + qunlock(&rb); + + wakeup(&rb.producer); + + return n; +} diff --git a/os/port/rdb.c b/os/port/rdb.c new file mode 100644 index 00000000..7386bcc6 --- /dev/null +++ b/os/port/rdb.c @@ -0,0 +1,112 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +/* + * alternative debug protocol for plan 9's rdbfs(4) for plan 9's own acid + */ +#define DBG if(0)scrprint +#pragma varargck argpos scrprint 1 +static Ureg ureg; +extern Queue *klogq; + +static void +scrprint(char *fmt, ...) +{ + char buf[128]; + va_list va; + int n; + + va_start(va, fmt); + n = vseprint(buf, buf+sizeof buf, fmt, va)-buf; + va_end(va); + putstrn(buf, n); +} + +static char* +getline(void) +{ + static char buf[128]; + int i, c; + + for(;;){ + for(i=0; i<nelem(buf) && (c=uartgetc()) != '\n'; i++){ + DBG("%c...", c); + buf[i] = c; + } + + if(i < nelem(buf)){ + buf[i] = 0; + return buf; + } + } +} + +static void* +addr(char *s, Ureg *ureg, char **p) +{ + ulong a; + + a = strtoul(s, p, 16); + if(a < sizeof(Ureg)) + return ((uchar*)ureg)+a; + return (void*)a; +} + +static void +talkrdb(Ureg *ureg) +{ + uchar *a; + char *p; + char *req; + + printq = nil; // turn off serial console + klogq = nil; // turn off /dev/kprint if active + iprint("Edebugger reset\n"); + for(;;){ + req = getline(); + switch(*req){ + case 'r': + a = addr(req+1, ureg, nil); + DBG("read %p\n", a); + iprint("R%.8lux %.2ux %.2ux %.2ux %.2ux\n", strtoul(req+1, 0, 16), a[0], a[1], a[2], a[3]); + break; + + case 'w': + a = addr(req+1, ureg, &p); + *(ulong*)a = strtoul(p, nil, 16); + iprint("W\n"); + break; +/* + * case Tmput: + n = min[4]; + if(n > 4){ + mesg(Rerr, Ecount); + break; + } + a = addr(min+0); + scrprint("mput %.8lux\n", a); + memmove(a, min+5, n); + mesg(Rmput, mout); + break; + * + */ + default: + DBG("unknown %c\n", *req); + iprint("Eunknown message\n"); + break; + } + } +} + +void +rdb(void) +{ + splhi(); + iprint("rdb..."); + callwithureg(talkrdb); +} diff --git a/os/port/sd.h b/os/port/sd.h new file mode 100644 index 00000000..a469cff6 --- /dev/null +++ b/os/port/sd.h @@ -0,0 +1,132 @@ +/* + * Storage Device. + */ +typedef struct SDev SDev; +typedef struct SDifc SDifc; +typedef struct SDpart SDpart; +typedef struct SDperm SDperm; +typedef struct SDreq SDreq; +typedef struct SDunit SDunit; + +struct SDperm { + char* name; + char* user; + ulong perm; +}; + +struct SDpart { + ulong start; + ulong end; + SDperm; + int valid; + ulong vers; +}; + +struct SDunit { + SDev* dev; + int subno; + uchar inquiry[256]; /* format follows SCSI spec */ + SDperm; + + QLock ctl; + ulong sectors; + ulong secsize; + SDpart* part; /* nil or array of size npart */ + int npart; + ulong vers; + SDperm ctlperm; + + QLock raw; /* raw read or write in progress */ + ulong rawinuse; /* really just a test-and-set */ + int state; + SDreq* req; + SDperm rawperm; +}; + +/* + * Each controller is represented by a SDev. + * Each controller is responsible for allocating its unit structures. + * Each controller has at least one unit. + */ +struct SDev { + Ref r; /* Number of callers using device */ + SDifc* ifc; /* pnp/legacy */ + void* ctlr; + int idno; + char* name; + SDev* next; + + QLock; /* enable/disable */ + int enabled; + int nunit; /* Number of units */ + QLock unitlock; /* `Loading' of units */ + int* unitflg; /* Unit flags */ + SDunit**unit; +}; + +struct SDifc { + char* name; + + SDev* (*pnp)(void); + SDev* (*legacy)(int, int); + SDev* (*id)(SDev*); + int (*enable)(SDev*); + int (*disable)(SDev*); + + int (*verify)(SDunit*); + int (*online)(SDunit*); + int (*rio)(SDreq*); + int (*rctl)(SDunit*, char*, int); + int (*wctl)(SDunit*, Cmdbuf*); + + long (*bio)(SDunit*, int, int, void*, long, long); + SDev* (*probe)(DevConf*); + void (*clear)(SDev*); + char* (*stat)(SDev*, char*, char*); +}; + +struct SDreq { + SDunit* unit; + int lun; + int write; + uchar cmd[16]; + int clen; + void* data; + int dlen; + + int flags; + + int status; + long rlen; + uchar sense[256]; +}; + +enum { + SDnosense = 0x00000001, + SDvalidsense = 0x00010000, +}; + +enum { + SDretry = -5, /* internal to controllers */ + SDmalloc = -4, + SDeio = -3, + SDtimeout = -2, + SDnostatus = -1, + + SDok = 0, + + SDcheck = 0x02, /* check condition */ + SDbusy = 0x08, /* busy */ + + SDmaxio = 2048*1024, + SDnpart = 16, +}; + +#define sdmalloc(n) malloc(n) +#define sdfree(p) free(p) + +/* sdscsi.c */ +extern int scsiverify(SDunit*); +extern int scsionline(SDunit*); +extern long scsibio(SDunit*, int, int, void*, long, long); +extern SDev* scsiid(SDev*, SDifc*); diff --git a/os/port/swcursor.c b/os/port/swcursor.c new file mode 100644 index 00000000..b4e2628c --- /dev/null +++ b/os/port/swcursor.c @@ -0,0 +1,358 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> + +#include "screen.h" + +typedef struct SWcursor SWcursor; + +/* + * Software cursor code: done by hand, might be better to use memdraw + */ + +struct SWcursor { + ulong *fb; /* screen frame buffer */ + Rectangle r; + int d; /* ldepth of screen */ + int width; /* width of screen in ulongs */ + int x; + int y; + int hotx; + int hoty; + uchar cbwid; /* cursor byte width */ + uchar f; /* flags */ + uchar cwid; + uchar chgt; + int hidecount; + uchar data[CURSWID*CURSHGT]; + uchar mask[CURSWID*CURSHGT]; + uchar save[CURSWID*CURSHGT]; +}; + +enum { + CUR_ENA = 0x01, /* cursor is enabled */ + CUR_DRW = 0x02, /* cursor is currently drawn */ + CUR_SWP = 0x10, /* bit swap */ +}; + +static Rectangle cursoroffrect; +static int cursorisoff; + +static void swcursorflush(int, int); +static void swcurs_draw_or_undraw(SWcursor *); + +static void +cursorupdate0(void) +{ + int inrect, x, y; + Point m; + + m = mousexy(); + x = m.x - swc->hotx; + y = m.y - swc->hoty; + inrect = (x >= cursoroffrect.min.x && x < cursoroffrect.max.x + && y >= cursoroffrect.min.y && y < cursoroffrect.max.y); + if (cursorisoff == inrect) + return; + cursorisoff = inrect; + if (inrect) + swcurs_hide(swc); + else { + swc->hidecount = 0; + swcurs_draw_or_undraw(swc); + } + swcursorflush(m.x, m.y); +} + +void +cursorupdate(Rectangle r) +{ + lock(vd); + r.min.x -= 16; + r.min.y -= 16; + cursoroffrect = r; + if (swc != nil) + cursorupdate0(); + unlock(vd); +} + +void +cursorenable(void) +{ + Point m; + + lock(vd); + if(swc != nil) { + swcurs_enable(swc); + m = mousexy(); + swcursorflush(m.x, m.y); + } + unlock(vd); +} + +void +cursordisable(void) +{ + Point m; + + lock(vd); + if(swc != nil) { + swcurs_disable(swc); + m = mousexy(); + swcursorflush(m.x, m.y); + } + unlock(vd); +} + +void +swcursupdate(int oldx, int oldy, int x, int y) +{ + + if(!canlock(vd)) + return; /* if can't lock, don't wake up stuff */ + + if(x < gscreen->r.min.x) + x = gscreen->r.min.x; + if(x >= gscreen->r.max.x) + x = gscreen->r.max.x; + if(y < gscreen->r.min.y) + y = gscreen->r.min.y; + if(y >= gscreen->r.max.y) + y = gscreen->r.max.y; + if(swc != nil) { + swcurs_hide(swc); + swc->x = x; + swc->y = y; + cursorupdate0(); + swcurs_unhide(swc); + swcursorflush(oldx, oldy); + swcursorflush(x, y); + } + + unlock(vd); +} + +void +drawcursor(Drawcursor* c) +{ + Point p, m; + Cursor curs, *cp; + int j, i, h, bpl; + uchar *bc, *bs, *cclr, *cset; + + if(swc == nil) + return; + + /* Set the default system cursor */ + if(c == nil || c->data == nil){ + swcurs_disable(swc); + return; + } + else { + cp = &curs; + p.x = c->hotx; + p.y = c->hoty; + cp->offset = p; + bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1); + + h = (c->maxy-c->miny)/2; + if(h > 16) + h = 16; + + bc = c->data; + bs = c->data + h*bpl; + + cclr = cp->clr; + cset = cp->set; + for(i = 0; i < h; i++) { + for(j = 0; j < 2; j++) { + cclr[j] = bc[j]; + cset[j] = bs[j]; + } + bc += bpl; + bs += bpl; + cclr += 2; + cset += 2; + } + } + swcurs_load(swc, cp); + m = mousexy(); + swcursorflush(m.x, m.y); + swcurs_enable(swc); +} + +SWcursor* +swcurs_create(ulong *fb, int width, int ldepth, Rectangle r, int bitswap) +{ + SWcursor *swc; + + swc = (SWcursor*)malloc(sizeof(SWcursor)); + swc->fb = fb; + swc->r = r; + swc->d = ldepth; + swc->width = width; + swc->f = bitswap ? CUR_SWP : 0; + swc->x = swc->y = 0; + swc->hotx = swc->hoty = 0; + swc->hidecount = 0; + return swc; +} + +void +swcurs_destroy(SWcursor *swc) +{ + swcurs_disable(swc); + free(swc); +} + +static void +swcursorflush(int x, int y) +{ + Rectangle r; + + /* XXX a little too paranoid here */ + r.min.x = x-16; + r.min.y = y-16; + r.max.x = x+17; + r.max.y = y+17; + flushmemscreen(r); +} + +static void +swcurs_draw_or_undraw(SWcursor *swc) +{ + uchar *p; + uchar *cs; + int w, vw; + int x1 = swc->r.min.x; + int y1 = swc->r.min.y; + int x2 = swc->r.max.x; + int y2 = swc->r.max.y; + int xp = swc->x - swc->hotx; + int yp = swc->y - swc->hoty; + int ofs; + + if(((swc->f & CUR_ENA) && (swc->hidecount <= 0)) + == ((swc->f & CUR_DRW) != 0)) + return; + w = swc->cbwid*BI2BY/(1 << swc->d); + x1 = xp < x1 ? x1 : xp; + y1 = yp < y1 ? y1 : yp; + x2 = xp+w >= x2 ? x2 : xp+w; + y2 = yp+swc->chgt >= y2 ? y2 : yp+swc->chgt; + if(x2 <= x1 || y2 <= y1) + return; + p = (uchar*)(swc->fb + swc->width*y1) + + x1*(1 << swc->d)/BI2BY; + y2 -= y1; + x2 = (x2-x1)*(1 << swc->d)/BI2BY; + vw = swc->width*BY2WD - x2; + w = swc->cbwid - x2; + ofs = swc->cbwid*(y1-yp)+(x1-xp); + cs = swc->save + ofs; + if((swc->f ^= CUR_DRW) & CUR_DRW) { + uchar *cm = swc->mask + ofs; + uchar *cd = swc->data + ofs; + while(y2--) { + x1 = x2; + while(x1--) { + *p = ((*cs++ = *p) & *cm++) ^ *cd++; + p++; + } + cs += w; + cm += w; + cd += w; + p += vw; + } + } else { + while(y2--) { + x1 = x2; + while(x1--) + *p++ = *cs++; + cs += w; + p += vw; + } + } +} + +void +swcurs_hide(SWcursor *swc) +{ + ++swc->hidecount; + swcurs_draw_or_undraw(swc); +} + +void +swcurs_unhide(SWcursor *swc) +{ + if (--swc->hidecount < 0) + swc->hidecount = 0; + swcurs_draw_or_undraw(swc); +} + +void +swcurs_enable(SWcursor *swc) +{ + swc->f |= CUR_ENA; + swcurs_draw_or_undraw(swc); +} + +void +swcurs_disable(SWcursor *swc) +{ + swc->f &= ~CUR_ENA; + swcurs_draw_or_undraw(swc); +} + +void +swcurs_load(SWcursor *swc, Cursor *c) +{ + int i, k; + uchar *bc, *bs, *cd, *cm; + static uchar bdv[4] = {0,Backgnd,Foregnd,0xff}; + static uchar bmv[4] = {0xff,0,0,0xff}; + int bits = 1<<swc->d; + uchar mask = (1<<bits)-1; + int bswp = (swc->f&CUR_SWP) ? 8-bits : 0; + + bc = c->clr; + bs = c->set; + + swcurs_hide(swc); + cd = swc->data; + cm = swc->mask; + swc->hotx = c->offset.x; + swc->hoty = c->offset.y; + swc->chgt = CURSHGT; + swc->cwid = CURSWID; + swc->cbwid = CURSWID*(1<<swc->d)/BI2BY; + for(i = 0; i < CURSWID/BI2BY*CURSHGT; i++) { + uchar bcb = *bc++; + uchar bsb = *bs++; + for(k=0; k<BI2BY;) { + uchar cdv = 0; + uchar cmv = 0; + int z; + for(z=0; z<BI2BY; z += bits) { + int n = ((bsb&(0x80))|((bcb&(0x80))<<1))>>7; + int s = z^bswp; + cdv |= (bdv[n]&mask) << s; + cmv |= (bmv[n]&mask) << s; + bcb <<= 1; + bsb <<= 1; + k++; + } + *cd++ = cdv; + *cm++ = cmv; + } + } + swcurs_unhide(swc); +} diff --git a/os/port/sysfile.c b/os/port/sysfile.c new file mode 100644 index 00000000..1ff0fe5f --- /dev/null +++ b/os/port/sysfile.c @@ -0,0 +1,1125 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +static int +growfd(Fgrp *f, int fd) +{ + int n; + Chan **nfd, **ofd; + + if(fd < f->nfd) + return 0; + n = f->nfd+DELTAFD; + if(n > MAXNFD) + n = MAXNFD; + if(fd >= n) + return -1; + nfd = malloc(n*sizeof(Chan*)); + if(nfd == nil) + return -1; + ofd = f->fd; + memmove(nfd, ofd, f->nfd*sizeof(Chan *)); + f->fd = nfd; + f->nfd = n; + free(ofd); + return 0; +} + +int +newfd(Chan *c) +{ + int i; + Fgrp *f = up->env->fgrp; + + lock(f); + for(i=f->minfd; i<f->nfd; i++) + if(f->fd[i] == 0) + break; + if(i >= f->nfd && growfd(f, i) < 0){ + unlock(f); + exhausted("file descriptors"); + return -1; + } + f->minfd = i + 1; + if(i > f->maxfd) + f->maxfd = i; + f->fd[i] = c; + unlock(f); + return i; +} + +Chan* +fdtochan(Fgrp *f, int fd, int mode, int chkmnt, int iref) +{ + Chan *c; + + c = 0; + + lock(f); + if(fd<0 || f->maxfd<fd || (c = f->fd[fd])==0) { + unlock(f); + error(Ebadfd); + } + if(iref) + incref(c); + unlock(f); + + if(chkmnt && (c->flag&CMSG)) { + if(iref) + cclose(c); + error(Ebadusefd); + } + + if(mode<0 || c->mode==ORDWR) + return c; + + if((mode&OTRUNC) && c->mode==OREAD) { + if(iref) + cclose(c); + error(Ebadusefd); + } + + if((mode&~OTRUNC) != c->mode) { + if(iref) + cclose(c); + error(Ebadusefd); + } + + return c; +} + +long +kchanio(void *vc, void *buf, int n, int mode) +{ + int r; + Chan *c; + + c = vc; + if(waserror()) + return -1; + + if(mode == OREAD) + r = devtab[c->type]->read(c, buf, n, c->offset); + else + r = devtab[c->type]->write(c, buf, n, c->offset); + + lock(c); + c->offset += r; + unlock(c); + poperror(); + return r; +} + +int +openmode(ulong o) +{ + if(o >= (OTRUNC|OCEXEC|ORCLOSE|OEXEC)) + error(Ebadarg); + o &= ~(OTRUNC|OCEXEC|ORCLOSE); + if(o > OEXEC) + error(Ebadarg); + if(o == OEXEC) + return OREAD; + return o; +} + +void +fdclose(Fgrp *f, int fd) +{ + int i; + Chan *c; + + lock(f); + c = f->fd[fd]; + if(c == 0){ + /* can happen for users with shared fd tables */ + unlock(f); + return; + } + f->fd[fd] = 0; + if(fd == f->maxfd) + for(i=fd; --i>=0 && f->fd[i]==0; ) + f->maxfd = i; + if(fd < f->minfd) + f->minfd = fd; + unlock(f); + cclose(c); +} + +int +kchdir(char *path) +{ + Chan *c; + Pgrp *pg; + + if(waserror()) + return -1; + + c = namec(path, Atodir, 0, 0); + pg = up->env->pgrp; + cclose(pg->dot); + pg->dot = c; + poperror(); + return 0; +} + +int +kfgrpclose(Fgrp *f, int fd) +{ + if(waserror()) + return -1; + + /* + * Take no reference on the chan because we don't really need the + * data structure, and are calling fdtochan only for error checks. + * fdclose takes care of processes racing through here. + */ + fdtochan(f, fd, -1, 0, 0); + fdclose(f, fd); + poperror(); + return 0; +} + +int +kclose(int fd) +{ + return kfgrpclose(up->env->fgrp, fd); +} + +int +kcreate(char *path, int mode, ulong perm) +{ + int fd; + Chan *c; + + if(waserror()) + return -1; + + openmode(mode&~OEXCL); /* error check only; OEXCL okay here */ + c = namec(path, Acreate, mode, perm); + if(waserror()) { + cclose(c); + nexterror(); + } + fd = newfd(c); + if(fd < 0) + error(Enofd); + poperror(); + + poperror(); + return fd; +} + +int +kdup(int old, int new) +{ + int fd; + Chan *c, *oc; + Fgrp *f = up->env->fgrp; + + if(waserror()) + return -1; + + c = fdtochan(up->env->fgrp, old, -1, 0, 1); + if(c->qid.type & QTAUTH) + error(Eperm); + fd = new; + if(fd != -1){ + lock(f); + if(fd<0 || growfd(f, fd) < 0) { + unlock(f); + cclose(c); + error(Ebadfd); + } + if(fd > f->maxfd) + f->maxfd = fd; + oc = f->fd[fd]; + f->fd[fd] = c; + unlock(f); + if(oc) + cclose(oc); + }else{ + if(waserror()) { + cclose(c); + nexterror(); + } + fd = newfd(c); + if(fd < 0) + error(Enofd); + poperror(); + } + poperror(); + return fd; +} + +int +kfstat(int fd, uchar *buf, int n) +{ + Chan *c; + + if(waserror()) + return -1; + + c = fdtochan(up->env->fgrp, fd, -1, 0, 1); + if(waserror()) { + cclose(c); + nexterror(); + } + devtab[c->type]->stat(c, buf, n); + + poperror(); + cclose(c); + + poperror(); + return n; +} + +char* +kfd2path(int fd) +{ + Chan *c; + char *s; + + if(waserror()) + return nil; + c = fdtochan(up->env->fgrp, fd, -1, 0, 1); + s = nil; + if(c->name != nil){ + s = malloc(c->name->len+1); + if(s == nil){ + cclose(c); + error(Enomem); + } + memmove(s, c->name->s, c->name->len+1); + cclose(c); + } + poperror(); + return s; +} + +int +kfauth(int fd, char *aname) +{ + Chan *c, *ac; + + if(waserror()) + return -1; + + validname(aname, 0); + c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1); + if(waserror()){ + cclose(c); + nexterror(); + } + + ac = mntauth(c, aname); + + /* at this point ac is responsible for keeping c alive */ + poperror(); /* c */ + cclose(c); + + if(waserror()){ + cclose(ac); + nexterror(); + } + + fd = newfd(ac); + if(fd < 0) + error(Enofd); + poperror(); /* ac */ + + poperror(); + + return fd; +} + +int +kfversion(int fd, uint msize, char *vers, uint arglen) +{ + int m; + Chan *c; + + if(waserror()) + return -1; + + /* check there's a NUL in the version string */ + if(arglen==0 || memchr(vers, 0, arglen)==0) + error(Ebadarg); + + c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1); + if(waserror()){ + cclose(c); + nexterror(); + } + + m = mntversion(c, vers, msize, arglen); + + poperror(); + cclose(c); + + poperror(); + return m; +} + +int +kpipe(int fd[2]) +{ + Dev *d; + Fgrp *f; + Chan *c[2]; + static char *names[] = {"data", "data1"}; + + f = up->env->fgrp; + + d = devtab[devno('|', 0)]; + c[0] = namec("#|", Atodir, 0, 0); + c[1] = 0; + fd[0] = -1; + fd[1] = -1; + if(waserror()) { + if(c[0] != 0) + cclose(c[0]); + if(c[1] != 0) + cclose(c[1]); + if(fd[0] >= 0) + f->fd[fd[0]]=0; + if(fd[1] >= 0) + f->fd[fd[1]]=0; + return -1; + } + c[1] = cclone(c[0]); + if(walk(&c[0], &names[0], 1, 1, nil) < 0) + error(Egreg); + if(walk(&c[1], &names[1], 1, 1, nil) < 0) + error(Egreg); + c[0] = d->open(c[0], ORDWR); + c[1] = d->open(c[1], ORDWR); + fd[0] = newfd(c[0]); + if(fd[0] < 0) + error(Enofd); + fd[1] = newfd(c[1]); + if(fd[1] < 0) + error(Enofd); + poperror(); + return 0; +} + +int +kfwstat(int fd, uchar *buf, int n) +{ + Chan *c; + + if(waserror()) + return -1; + + validstat(buf, n); + c = fdtochan(up->env->fgrp, fd, -1, 1, 1); + if(waserror()) { + cclose(c); + nexterror(); + } + n = devtab[c->type]->wstat(c, buf, n); + poperror(); + cclose(c); + + poperror(); + return n; +} + +long +bindmount(Chan *c, char *old, int flag, char *spec) +{ + int ret; + Chan *c1; + + if(flag>MMASK || (flag&MORDER) == (MBEFORE|MAFTER)) + error(Ebadarg); + + c1 = namec(old, Amount, 0, 0); + if(waserror()){ + cclose(c1); + nexterror(); + } + ret = cmount(c, c1, flag, spec); + + poperror(); + cclose(c1); + return ret; +} + +int +kbind(char *new, char *old, int flags) +{ + long r; + Chan *c0; + + if(waserror()) + return -1; + + c0 = namec(new, Abind, 0, 0); + if(waserror()) { + cclose(c0); + nexterror(); + } + r = bindmount(c0, old, flags, ""); + poperror(); + cclose(c0); + + poperror(); + return r; +} + +int +kmount(int fd, int afd, char *old, int flags, char *spec) +{ + long r; + volatile struct { Chan *c; } c0; + volatile struct { Chan *c; } bc; + volatile struct { Chan *c; } ac; + Mntparam mntparam; + + ac.c = nil; + bc.c = nil; + c0.c = nil; + if(waserror()) { + cclose(ac.c); + cclose(bc.c); + cclose(c0.c); + return -1; + } + bc.c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1); + if(afd >= 0) + ac.c = fdtochan(up->env->fgrp, afd, ORDWR, 0, 1); + mntparam.chan = bc.c; + mntparam.authchan = ac.c; + mntparam.spec = spec; + mntparam.flags = flags; + c0.c = devtab[devno('M', 0)]->attach((char*)&mntparam); + + r = bindmount(c0.c, old, flags, spec); + poperror(); + cclose(ac.c); + cclose(bc.c); + cclose(c0.c); + + return r; +} + +int +kunmount(char *old, char *new) +{ + volatile struct { Chan *c; } cmount; + volatile struct { Chan *c; } cmounted; + + cmount.c = nil; + cmounted.c = nil; + if(waserror()) { + cclose(cmount.c); + cclose(cmounted.c); + return -1; + } + + cmount.c = namec(new, Amount, 0, 0); + if(old != nil && old[0] != '\0') { + /* + * This has to be namec(..., Aopen, ...) because + * if arg[0] is something like /srv/cs or /fd/0, + * opening it is the only way to get at the real + * Chan underneath. + */ + cmounted.c = namec(old, Aopen, OREAD, 0); + } + + cunmount(cmount.c, cmounted.c); + poperror(); + cclose(cmount.c); + cclose(cmounted.c); + return 0; +} + +int +kopen(char *path, int mode) +{ + int fd; + Chan *c; + + if(waserror()) + return -1; + + openmode(mode); /* error check only */ + c = namec(path, Aopen, mode, 0); + if(waserror()){ + cclose(c); + nexterror(); + } + fd = newfd(c); + if(fd < 0) + error(Enofd); + poperror(); + + poperror(); + return fd; +} + +long +unionread(Chan *c, void *va, long n) +{ + int i; + long nr; + Mhead *m; + Mount *mount; + + qlock(&c->umqlock); + m = c->umh; + rlock(&m->lock); + mount = m->mount; + /* bring mount in sync with c->uri and c->umc */ + for(i = 0; mount != nil && i < c->uri; i++) + mount = mount->next; + + nr = 0; + while(mount != nil) { + /* Error causes component of union to be skipped */ + if(mount->to && !waserror()) { + if(c->umc == nil){ + c->umc = cclone(mount->to); + c->umc = devtab[c->umc->type]->open(c->umc, OREAD); + } + + nr = devtab[c->umc->type]->read(c->umc, va, n, c->umc->offset); + if(nr < 0) + nr = 0; /* dev.c can return -1 */ + c->umc->offset += nr; + poperror(); + } + if(nr > 0) + break; + + /* Advance to next element */ + c->uri++; + if(c->umc) { + cclose(c->umc); + c->umc = nil; + } + mount = mount->next; + } + runlock(&m->lock); + qunlock(&c->umqlock); + return nr; +} + +static void +unionrewind(Chan *c) +{ + qlock(&c->umqlock); + c->uri = 0; + if(c->umc){ + cclose(c->umc); + c->umc = nil; + } + qunlock(&c->umqlock); +} + +static long +rread(int fd, void *va, long n, vlong *offp) +{ + int dir; + Chan *c; + vlong off; + + if(waserror()) + return -1; + + c = fdtochan(up->env->fgrp, fd, OREAD, 1, 1); + if(waserror()) { + cclose(c); + nexterror(); + } + + if(n < 0) + error(Etoosmall); + + dir = c->qid.type & QTDIR; + if(dir && c->umh) + n = unionread(c, va, n); + else{ + if(offp == nil){ + lock(c); /* lock for vlong assignment */ + off = c->offset; + unlock(c); + }else + off = *offp; + if(off < 0) + error(Enegoff); + if(off == 0){ + if(offp == nil){ + lock(c); + c->offset = 0; + c->dri = 0; + unlock(c); + } + unionrewind(c); + } + n = devtab[c->type]->read(c, va, n, off); + lock(c); + c->offset += n; + unlock(c); + } + + poperror(); + cclose(c); + + poperror(); + return n; +} + +long +kread(int fd, void *va, long n) +{ + return rread(fd, va, n, nil); +} + +long +kpread(int fd, void *va, long n, vlong off) +{ + return rread(fd, va, n, &off); +} + +int +kremove(char *path) +{ + Chan *c; + + if(waserror()) + return -1; + + c = namec(path, Aremove, 0, 0); + if(waserror()) { + c->type = 0; /* see below */ + cclose(c); + nexterror(); + } + devtab[c->type]->remove(c); + /* + * Remove clunks the fid, but we need to recover the Chan + * so fake it up. rootclose() is known to be a nop. + */ + c->type = 0; + poperror(); + cclose(c); + + poperror(); + return 0; +} + +vlong +kseek(int fd, vlong off, int whence) +{ + Dir *dir; + Chan *c; + + if(waserror()) + return -1; + + c = fdtochan(up->env->fgrp, fd, -1, 1, 1); + if(waserror()) { + cclose(c); + nexterror(); + } + + if(devtab[c->type]->dc == '|') + error(Eisstream); + + switch(whence) { + case 0: + if(c->qid.type & QTDIR){ + if(off != 0) + error(Eisdir); + unionrewind(c); + }else if(off < 0) + error(Enegoff); + lock(c); /* lock for vlong assignment */ + c->offset = off; + unlock(c); + break; + + case 1: + if(c->qid.type & QTDIR) + error(Eisdir); + lock(c); /* lock for read/write update */ + off += c->offset; + if(off < 0){ + unlock(c); + error(Enegoff); + } + c->offset = off; + unlock(c); + break; + + case 2: + if(c->qid.type & QTDIR) + error(Eisdir); + dir = chandirstat(c); + if(dir == nil) + error("internal error: stat error in seek"); + off += dir->length; + free(dir); + if(off < 0) + error(Enegoff); + lock(c); /* lock for read/write update */ + c->offset = off; + unlock(c); + break; + + default: + error(Ebadarg); + break; + } + poperror(); + c->dri = 0; + cclose(c); + poperror(); + return off; +} + +void +validstat(uchar *s, int n) +{ + int m; + char buf[64]; + + if(statcheck(s, n) < 0) + error(Ebadstat); + /* verify that name entry is acceptable */ + s += STATFIXLEN - 4*BIT16SZ; /* location of first string */ + /* + * s now points at count for first string. + * if it's too long, let the server decide; this is + * only for his protection anyway. otherwise + * we'd have to allocate and waserror. + */ + m = GBIT16(s); + s += BIT16SZ; + if(m+1 > sizeof buf) + return; + memmove(buf, s, m); + buf[m] = '\0'; + /* name could be '/' */ + if(strcmp(buf, "/") != 0) + validname(buf, 0); +} + +int +kstat(char *path, uchar *buf, int n) +{ + Chan *c; + + if(waserror()) + return -1; + + c = namec(path, Aaccess, 0, 0); + if(waserror()){ + cclose(c); + nexterror(); + } + devtab[c->type]->stat(c, buf, n); + poperror(); + cclose(c); + + poperror(); + return 0; +} + +static long +rwrite(int fd, void *va, long n, vlong *offp) +{ + Chan *c; + vlong off; + long m; + + if(waserror()) + return -1; + c = fdtochan(up->env->fgrp, fd, OWRITE, 1, 1); + if(waserror()) { + cclose(c); + nexterror(); + } + if(c->qid.type & QTDIR) + error(Eisdir); + + if(n < 0) + error(Etoosmall); + + if(offp == nil){ + lock(c); + off = c->offset; + c->offset += n; + unlock(c); + }else + off = *offp; + + if(waserror()){ + if(offp == nil){ + lock(c); + c->offset -= n; + unlock(c); + } + nexterror(); + } + if(off < 0) + error(Enegoff); + m = devtab[c->type]->write(c, va, n, off); + poperror(); + + if(offp == nil && m < n){ + lock(c); + c->offset -= n - m; + unlock(c); + } + + poperror(); + cclose(c); + + poperror(); + return n; +} + +long +kwrite(int fd, void *va, long n) +{ + return rwrite(fd, va, n, nil); +} + +long +kpwrite(int fd, void *va, long n, vlong off) +{ + return rwrite(fd, va, n, &off); +} + +int +kwstat(char *path, uchar *buf, int n) +{ + Chan *c; + + if(waserror()) + return -1; + + validstat(buf, n); + c = namec(path, Aaccess, 0, 0); + if(waserror()){ + cclose(c); + nexterror(); + } + n = devtab[c->type]->wstat(c, buf, n); + poperror(); + cclose(c); + + poperror(); + return n; +} + +enum +{ + DIRSIZE = STATFIXLEN + 32 * 4, + DIRREADLIM = 2048, /* should handle the largest reasonable directory entry */ +}; + +Dir* +chandirstat(Chan *c) +{ + Dir *d; + uchar *buf; + int n, nd, i; + + nd = DIRSIZE; + for(i=0; i<2; i++){ /* should work by the second try */ + d = smalloc(sizeof(Dir) + nd); + buf = (uchar*)&d[1]; + if(waserror()){ + free(d); + return nil; + } + n = devtab[c->type]->stat(c, buf, nd); + poperror(); + if(n < BIT16SZ){ + free(d); + return nil; + } + nd = GBIT16((uchar*)buf) + BIT16SZ; /* size needed to store whole stat buffer including count */ + if(nd <= n){ + convM2D(buf, n, d, (char*)&d[1]); + return d; + } + /* else sizeof(Dir)+nd is plenty */ + free(d); + } + return nil; + +} + +Dir* +kdirstat(char *name) +{ + Chan *c; + Dir *d; + + if(waserror()) + return nil; + + c = namec(name, Aaccess, 0, 0); + if(waserror()){ + cclose(c); + nexterror(); + } + d = chandirstat(c); + poperror(); + cclose(c); + + poperror(); + return d; +} + +Dir* +kdirfstat(int fd) +{ + Chan *c; + Dir *d; + + if(waserror()) + return nil; + + c = fdtochan(up->env->fgrp, fd, -1, 0, 1); + if(waserror()) { + cclose(c); + nexterror(); + } + d = chandirstat(c); + poperror(); + cclose(c); + + poperror(); + return d; +} + +int +kdirwstat(char *name, Dir *dir) +{ + uchar *buf; + int r; + + r = sizeD2M(dir); + buf = smalloc(r); + convD2M(dir, buf, r); + r = kwstat(name, buf, r); + free(buf); + return r < 0? r: 0; +} + +int +kdirfwstat(int fd, Dir *dir) +{ + uchar *buf; + int r; + + r = sizeD2M(dir); + buf = smalloc(r); + convD2M(dir, buf, r); + r = kfwstat(fd, buf, r); + free(buf); + return r < 0? r: 0; +} + +static long +dirpackage(uchar *buf, long ts, Dir **d) +{ + char *s; + long ss, i, n, nn, m; + + *d = nil; + if(ts <= 0) + return ts; + + /* + * first find number of all stats, check they look like stats, & size all associated strings + */ + ss = 0; + n = 0; + for(i = 0; i < ts; i += m){ + m = BIT16SZ + GBIT16(&buf[i]); + if(statcheck(&buf[i], m) < 0) + break; + ss += m; + n++; + } + + if(i != ts) + error("bad directory format"); + + *d = malloc(n * sizeof(Dir) + ss); + if(*d == nil) + error(Enomem); + + /* + * then convert all buffers + */ + s = (char*)*d + n * sizeof(Dir); + nn = 0; + for(i = 0; i < ts; i += m){ + m = BIT16SZ + GBIT16((uchar*)&buf[i]); + if(nn >= n || convM2D(&buf[i], m, *d + nn, s) != m){ + free(*d); + *d = nil; + error("bad directory entry"); + } + nn++; + s += m; + } + + return nn; +} + +long +kdirread(int fd, Dir **d) +{ + uchar *buf; + long ts; + + *d = nil; + if(waserror()) + return -1; + buf = malloc(DIRREADLIM); + if(buf == nil) + error(Enomem); + if(waserror()){ + free(buf); + nexterror(); + } + ts = kread(fd, buf, DIRREADLIM); + if(ts >= 0) + ts = dirpackage(buf, ts, d); + poperror(); + free(buf); + poperror(); + return ts; +} + +int +kiounit(int fd) +{ + Chan *c; + int n; + + c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1); + if(waserror()){ + cclose(c); + return 0; /* n.b. */ + } + n = c->iounit; + poperror(); + cclose(c); + return n; +} diff --git a/os/port/taslock.c b/os/port/taslock.c new file mode 100644 index 00000000..7914b80a --- /dev/null +++ b/os/port/taslock.c @@ -0,0 +1,126 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +static void +lockloop(Lock *l, ulong pc) +{ + setpanic(); + print("lock loop 0x%lux key 0x%lux pc 0x%lux held by pc 0x%lux\n", l, l->key, pc, l->pc); + panic("lockloop"); +} + +void +lock(Lock *l) +{ + int i; + ulong pc; + + pc = getcallerpc(&l); + if(up == 0) { + if (_tas(&l->key) != 0) { + for(i=0; ; i++) { + if(_tas(&l->key) == 0) + break; + if (i >= 1000000) { + lockloop(l, pc); + break; + } + } + } + l->pc = pc; + return; + } + + for(i=0; ; i++) { + if(_tas(&l->key) == 0) + break; + if (i >= 1000) { + lockloop(l, pc); + break; + } + if(conf.nmach == 1 && up->state == Running && islo()) { + up->pc = pc; + sched(); + } + } + l->pri = up->pri; + up->pri = PriLock; + l->pc = pc; +} + +void +ilock(Lock *l) +{ + ulong x, pc; + int i; + + pc = getcallerpc(&l); + x = splhi(); + for(;;) { + if(_tas(&l->key) == 0) { + l->sr = x; + l->pc = pc; + return; + } + if(conf.nmach < 2) + panic("ilock: no way out: pc 0x%lux: lock 0x%lux held by pc 0x%lux", pc, l, l->pc); + for(i=0; ; i++) { + if(l->key == 0) + break; + clockcheck(); + if (i > 100000) { + lockloop(l, pc); + break; + } + } + } +} + +int +canlock(Lock *l) +{ + if(_tas(&l->key)) + return 0; + if(up){ + l->pri = up->pri; + up->pri = PriLock; + } + l->pc = getcallerpc(&l); + return 1; +} + +void +unlock(Lock *l) +{ + int p; + + if(l->key == 0) + print("unlock: not locked: pc %lux\n", getcallerpc(&l)); + p = l->pri; + l->pc = 0; + l->key = 0; + coherence(); + if(up){ + up->pri = p; + if(up->state == Running && anyhigher()) + sched(); + } +} + +void +iunlock(Lock *l) +{ + ulong sr; + + if(l->key == 0) + print("iunlock: not locked: pc %lux\n", getcallerpc(&l)); + sr = l->sr; + l->pc = 0; + l->key = 0; + coherence(); + splxpc(sr); +} diff --git a/os/port/tod.c b/os/port/tod.c new file mode 100644 index 00000000..55486453 --- /dev/null +++ b/os/port/tod.c @@ -0,0 +1,283 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +/* compute nanosecond epoch time from the fastest ticking clock + * on the system. converting the time to nanoseconds requires + * the following formula + * + * t = (((1000000000<<31)/f)*ticks)>>31 + * + * where + * + * 'f' is the clock frequency + * 'ticks' are clock ticks + * + * to avoid too much calculation in todget(), we calculate + * + * mult = (1000000000<<32)/f + * + * each time f is set. f is normally set by a user level + * program writing to /dev/fastclock. mul64fract will then + * take that fractional multiplier and a 64 bit integer and + * return the resulting integer product. + * + * We assume that the cpu's of a multiprocessor are synchronized. + * This assumption needs to be questioned with each new architecture. + */ + +/* frequency of the tod clock */ +#define TODFREQ 1000000000ULL + +struct { + int init; // true if initialized + ulong cnt; + Lock; + uvlong multiplier; // t = off + (multiplier*ticks)>>31 + uvlong divider; // ticks = (divider*(ticks-off))>>31 + vlong hz; // frequency of fast clock + vlong last; // last reading of fast clock + vlong off; // offset from epoch to last + vlong lasttime; // last return value from todget + vlong delta; // add 'delta' each slow clock tick from sstart to send + ulong sstart; // ... + ulong send; // ... +} tod; + +void +todinit(void) +{ + if(tod.init) + return; + ilock(&tod); + tod.last = fastticks((uvlong*)&tod.hz); + iunlock(&tod); + todsetfreq(tod.hz); + tod.init = 1; + addclock0link(todfix, 100); +} + +/* + * calculate multiplier + */ +void +todsetfreq(vlong f) +{ + ilock(&tod); + tod.hz = f; + + /* calculate multiplier for time conversion */ + tod.multiplier = mk64fract(TODFREQ, f); + tod.divider = mk64fract(f, TODFREQ); + + iunlock(&tod); +} + +/* + * Set the time of day struct + */ +void +todset(vlong t, vlong delta, int n) +{ + if(!tod.init) + todinit(); + + ilock(&tod); + if(t >= 0){ + tod.off = t; + tod.last = fastticks(nil); + tod.lasttime = 0; + tod.delta = 0; + tod.sstart = tod.send; + } else { + if(n <= 0) + n = 1; + n *= HZ; + if(delta < 0 && n > -delta) + n = -delta; + if(delta > 0 && n > delta) + n = delta; + delta = delta/n; + tod.sstart = MACHP(0)->ticks; + tod.send = tod.sstart + n; + tod.delta = delta; + } + iunlock(&tod); +} + +/* + * get time of day + */ +vlong +todget(vlong *ticksp) +{ + uvlong x; + vlong ticks, diff; + ulong t; + + if(!tod.init) + todinit(); + + // we don't want time to pass twixt the measuring of fastticks + // and grabbing tod.last. Also none of the vlongs are atomic so + // we have to look at them inside the lock. + ilock(&tod); + tod.cnt++; + ticks = fastticks(nil); + + // add in correction + if(tod.sstart != tod.send){ + t = MACHP(0)->ticks; + if(t >= tod.send) + t = tod.send; + tod.off = tod.off + tod.delta*(t - tod.sstart); + tod.sstart = t; + } + + // convert to epoch + diff = ticks - tod.last; + if(diff < 0) + diff = 0; + mul64fract(&x, diff, tod.multiplier); + x += tod.off; + + // time can't go backwards + if(x < tod.lasttime) + x = tod.lasttime; + else + tod.lasttime = x; + + iunlock(&tod); + + if(ticksp != nil) + *ticksp = ticks; + + return x; +} + +/* + * convert time of day to ticks + */ +uvlong +tod2fastticks(vlong ns) +{ + uvlong x; + + ilock(&tod); + mul64fract(&x, ns-tod.off, tod.divider); + x += tod.last; + iunlock(&tod); + return x; +} + +/* + * called regularly to avoid calculation overflows + */ +void +todfix(void) +{ + vlong ticks, diff; + uvlong x; + + ticks = fastticks(nil); + + diff = ticks - tod.last; + if(diff > tod.hz){ + ilock(&tod); + + // convert to epoch + mul64fract(&x, diff, tod.multiplier); +if(x > 30000000000ULL) print("todfix %llud\n", x); + x += tod.off; + + // protect against overflows + tod.last = ticks; + tod.off = x; + + iunlock(&tod); + } +} + +long +tseconds(void) +{ + vlong x; + int i; + + x = todget(nil); + x = x/TODFREQ; + i = x; + return i; +} + +// convert milliseconds to fast ticks +// +uvlong +ms2fastticks(ulong ms) +{ + if(!tod.init) + todinit(); + return (tod.hz*ms)/1000ULL; +} + +/* + * convert nanoseconds to fast ticks + */ +uvlong +ns2fastticks(uvlong ns) +{ + uvlong res; + + if(!tod.init) + todinit(); + mul64fract(&res, ns, tod.divider); + return res; +} + +/* + * convert fast ticks to ns + */ +uvlong +fastticks2ns(uvlong ticks) +{ + uvlong res; + + if(!tod.init) + todinit(); + mul64fract(&res, ticks, tod.multiplier); + return res; +} + +/* + * Make a 64 bit fixed point number that has a decimal point + * to the left of the low order 32 bits. This is used with + * mul64fract for converting twixt nanoseconds and fastticks. + * + * multiplier = (to<<32)/from + */ +uvlong +mk64fract(uvlong to, uvlong from) +{ +/* + int shift; + + if(to == 0ULL) + return 0ULL; + + shift = 0; + while(shift < 32 && to < (1ULL<<(32+24))){ + to <<= 8; + shift += 8; + } + while(shift < 32 && to < (1ULL<<(32+31))){ + to <<= 1; + shift += 1; + } + + return (to/from)<<(32-shift); +*/ + return (to<<32)/from; +} diff --git a/os/port/uart.h b/os/port/uart.h new file mode 100644 index 00000000..370ee09a --- /dev/null +++ b/os/port/uart.h @@ -0,0 +1,104 @@ +typedef struct PhysUart PhysUart; +typedef struct Uart Uart; + +/* + * routines to access UART hardware + */ +struct PhysUart +{ + char* name; + Uart* (*pnp)(void); + void (*enable)(Uart*, int); + void (*disable)(Uart*); + void (*kick)(Uart*); + void (*dobreak)(Uart*, int); + int (*baud)(Uart*, int); + int (*bits)(Uart*, int); + int (*stop)(Uart*, int); + int (*parity)(Uart*, int); + void (*modemctl)(Uart*, int); + void (*rts)(Uart*, int); + void (*dtr)(Uart*, int); + long (*status)(Uart*, void*, long, long); + void (*fifo)(Uart*, int); + void (*power)(Uart*, int); + int (*getc)(Uart*); /* polling versions, for iprint, rdb */ + void (*putc)(Uart*, int); +}; + +enum { + Stagesize= 1024 +}; + +/* + * software UART + */ +struct Uart +{ + void* regs; /* hardware stuff */ + void* saveregs; /* place to put registers on power down */ + char* name; /* internal name */ + ulong freq; /* clock frequency */ + int bits; /* bits per character */ + int stop; /* stop bits */ + int parity; /* even, odd or no parity */ + int baud; /* baud rate */ + PhysUart*phys; + int console; /* used as a serial console */ + int special; /* internal kernel device */ + Uart* next; /* list of allocated uarts */ + + QLock; + int type; /* ?? */ + int dev; + int opens; + + int enabled; + Uart *elist; /* next enabled interface */ + + int perr; /* parity errors */ + int ferr; /* framing errors */ + int oerr; /* rcvr overruns */ + int berr; /* no input buffers */ + int serr; /* input queue overflow */ + + /* buffers */ + int (*putc)(Queue*, int); + Queue *iq; + Queue *oq; + + Lock rlock; + uchar istage[Stagesize]; + uchar *iw; + uchar *ir; + uchar *ie; + + Lock tlock; /* transmit */ + uchar ostage[Stagesize]; + uchar *op; + uchar *oe; + int drain; + + int modem; /* hardware flow control on */ + int xonoff; /* software flow control on */ + int blocked; + int cts, dsr, dcd, dcdts; /* keep track of modem status */ + int ctsbackoff; + int hup_dsr, hup_dcd; /* send hangup upstream? */ + int dohup; + + Rendez r; +}; + +extern Uart* consuart; + +extern int uartctl(Uart*, char*); +extern int uartgetc(void); +extern void uartkick(void*); +extern void uartmouse(Uart*, int (*)(Queue*, int), int); +extern void uartsetmouseputc(Uart*, int (*)(Queue*, int)); +extern void uartputc(int); +extern void uartputs(char*, int); +extern void uartrecv(Uart*, char); +extern Uart* uartsetup(Uart*); +extern int uartstageoutput(Uart*); diff --git a/os/port/xalloc.c b/os/port/xalloc.c new file mode 100644 index 00000000..73310089 --- /dev/null +++ b/os/port/xalloc.c @@ -0,0 +1,246 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +#define datoff ((ulong)((Xhdr*)0)->data) + +enum +{ + Chunk = 64*1024, + Nhole = 128, + Magichole = 0xDeadBabe, +}; + +typedef struct Hole Hole; +typedef struct Xalloc Xalloc; +typedef struct Xhdr Xhdr; + +struct Hole +{ + ulong addr; + ulong size; + ulong top; + Hole* link; +}; + +struct Xhdr +{ + ulong size; + ulong magix; + char data[1]; +}; + +struct Xalloc +{ + Lock; + Hole hole[Nhole]; + Hole* flist; + Hole* table; +}; + +static Xalloc xlists; + +static void +ixprt() +{ + xsummary(); + ixsummary(); +} + +void +xinit(void) +{ + Hole *h, *eh; + + eh = &xlists.hole[Nhole-1]; + for(h = xlists.hole; h < eh; h++) + h->link = h+1; + + xlists.flist = xlists.hole; + + if(conf.npage1) + xhole(conf.base1, conf.npage1*BY2PG); + conf.npage1 = conf.base1+(conf.npage1*BY2PG); + + if(conf.npage0) + xhole(conf.base0, conf.npage0*BY2PG); + conf.npage0 = conf.base0+(conf.npage0*BY2PG); + + /* Save the bounds of kernel alloc memory for kernel mmu mapping */ + conf.base0 = (ulong)KADDR(conf.base0); + conf.base1 = (ulong)KADDR(conf.base1); + conf.npage0 = (ulong)KADDR(conf.npage0); + conf.npage1 = (ulong)KADDR(conf.npage1); + + debugkey('x', "xalloc/ialloc", ixprt, 0); +} + +void* +xspanalloc(ulong size, int align, ulong span) +{ + ulong a, v, t; + + a = (ulong)xalloc(size+align+span); + if(a == 0) + panic("xspanalloc: %lud %d %lux\n", size, align, span); + + if(span > 2) { + v = (a + span) & ~(span-1); + t = v - a; + if(t > 0) + xhole(PADDR(a), t); + t = a + span - v; + if(t > 0) + xhole(PADDR(v+size+align), t); + } + else + v = a; + + if(align > 1) + v = (v + align) & ~(align-1); + + return (void*)v; +} + +void* +xallocz(ulong size, int zero) +{ + Xhdr *p; + Hole *h, **l; + + size += BY2V + sizeof(Xhdr); + size &= ~(BY2V-1); + + ilock(&xlists); + l = &xlists.table; + for(h = *l; h; h = h->link) { + if(h->size >= size) { + p = (Xhdr*)h->addr; + h->addr += size; + h->size -= size; + if(h->size == 0) { + *l = h->link; + h->link = xlists.flist; + xlists.flist = h; + } + iunlock(&xlists); + p = KADDR(p); + p->magix = Magichole; + p->size = size; + if(zero) + memset(p->data, 0, size); + return p->data; + } + l = &h->link; + } + iunlock(&xlists); + return nil; +} + +void* +xalloc(ulong size) +{ + return xallocz(size, 1); +} + +void +xfree(void *p) +{ + Xhdr *x; + + x = (Xhdr*)((ulong)p - datoff); + if(x->magix != Magichole) { + xsummary(); + panic("xfree(0x%lux) 0x%lux!=0x%lux", p, (ulong)Magichole, x->magix); + } + xhole(PADDR(x), x->size); +} + +int +xmerge(void *vp, void *vq) +{ + Xhdr *p, *q; + + p = vp; + if((uchar*)vp+p->size == (uchar*)vq) { + q = vq; + p->size += q->size; + return 1; + } + return 0; +} + +void +xhole(ulong addr, ulong size) +{ + ulong top; + Hole *h, *c, **l; + + if(size == 0) + return; + + top = addr + size; + ilock(&xlists); + l = &xlists.table; + for(h = *l; h; h = h->link) { + if(h->top == addr) { + h->size += size; + h->top = h->addr+h->size; + c = h->link; + if(c && h->top == c->addr) { + h->top += c->size; + h->size += c->size; + h->link = c->link; + c->link = xlists.flist; + xlists.flist = c; + } + iunlock(&xlists); + return; + } + if(h->addr > addr) + break; + l = &h->link; + } + if(h && top == h->addr) { + h->addr -= size; + h->size += size; + iunlock(&xlists); + return; + } + + if(xlists.flist == nil) { + iunlock(&xlists); + print("xfree: no free holes, leaked %lud bytes\n", size); + return; + } + + h = xlists.flist; + xlists.flist = h->link; + h->addr = addr; + h->top = top; + h->size = size; + h->link = *l; + *l = h; + iunlock(&xlists); +} + +void +xsummary(void) +{ + int i; + Hole *h; + + i = 0; + for(h = xlists.flist; h; h = h->link) + i++; + + print("%d holes free\n", i); + i = 0; + for(h = xlists.table; h; h = h->link) { + print("%.8lux %.8lux %lud\n", h->addr, h->top, h->size); + i += h->size; + } + print("%d bytes free\n", i); +} |
