summaryrefslogtreecommitdiff
path: root/sys/src/9/port/devsegment.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/port/devsegment.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/port/devsegment.c')
-rwxr-xr-xsys/src/9/port/devsegment.c578
1 files changed, 578 insertions, 0 deletions
diff --git a/sys/src/9/port/devsegment.c b/sys/src/9/port/devsegment.c
new file mode 100755
index 000000000..8e5091b79
--- /dev/null
+++ b/sys/src/9/port/devsegment.c
@@ -0,0 +1,578 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+enum
+{
+ Qtopdir,
+ Qsegdir,
+ Qctl,
+ Qdata,
+
+ /* commands to kproc */
+ Cnone=0,
+ Cread,
+ Cwrite,
+ Cstart,
+ Cdie,
+};
+
+#define TYPE(x) (int)( (c)->qid.path & 0x7 )
+#define SEG(x) ( ((c)->qid.path >> 3) & 0x3f )
+#define PATH(s, t) ( ((s)<<3) | (t) )
+
+typedef struct Globalseg Globalseg;
+struct Globalseg
+{
+ Ref;
+ Segment *s;
+
+ char *name;
+ char *uid;
+ vlong length;
+ long perm;
+
+ /* kproc to do reading and writing */
+ QLock l; /* sync kproc access */
+ Rendez cmdwait; /* where kproc waits */
+ Rendez replywait; /* where requestor waits */
+ Proc *kproc;
+ char *data;
+ long off;
+ int dlen;
+ int cmd;
+ char err[64];
+};
+
+static Globalseg *globalseg[100];
+static Lock globalseglock;
+
+
+ Segment* (*_globalsegattach)(Proc*, char*);
+static Segment* globalsegattach(Proc *p, char *name);
+static int cmddone(void*);
+static void segmentkproc(void*);
+static void docmd(Globalseg *g, int cmd);
+
+/*
+ * returns with globalseg incref'd
+ */
+static Globalseg*
+getgseg(Chan *c)
+{
+ int x;
+ Globalseg *g;
+
+ x = SEG(c);
+ lock(&globalseglock);
+ if(x >= nelem(globalseg))
+ panic("getgseg");
+ g = globalseg[x];
+ if(g != nil)
+ incref(g);
+ unlock(&globalseglock);
+ if(g == nil)
+ error("global segment disappeared");
+ return g;
+}
+
+static void
+putgseg(Globalseg *g)
+{
+ if(decref(g) > 0)
+ return;
+ if(g->s != nil)
+ putseg(g->s);
+ if(g->kproc)
+ docmd(g, Cdie);
+ free(g->name);
+ free(g->uid);
+ free(g);
+}
+
+static int
+segmentgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp)
+{
+ Qid q;
+ Globalseg *g;
+ ulong size;
+
+ switch(TYPE(c)) {
+ case Qtopdir:
+ if(s == DEVDOTDOT){
+ q.vers = 0;
+ q.path = PATH(0, Qtopdir);
+ q.type = QTDIR;
+ devdir(c, q, "#g", 0, eve, DMDIR|0777, dp);
+ break;
+ }
+
+ if(s >= nelem(globalseg))
+ return -1;
+
+ lock(&globalseglock);
+ g = globalseg[s];
+ if(g == nil){
+ unlock(&globalseglock);
+ return 0;
+ }
+ q.vers = 0;
+ q.path = PATH(s, Qsegdir);
+ q.type = QTDIR;
+ devdir(c, q, g->name, 0, g->uid, DMDIR|0777, dp);
+ unlock(&globalseglock);
+
+ break;
+ case Qsegdir:
+ if(s == DEVDOTDOT){
+ q.vers = 0;
+ q.path = PATH(0, Qtopdir);
+ q.type = QTDIR;
+ devdir(c, q, "#g", 0, eve, DMDIR|0777, dp);
+ break;
+ }
+ /* fall through */
+ case Qctl:
+ case Qdata:
+ switch(s){
+ case 0:
+ g = getgseg(c);
+ q.vers = 0;
+ q.path = PATH(SEG(c), Qctl);
+ q.type = QTFILE;
+ devdir(c, q, "ctl", 0, g->uid, g->perm, dp);
+ putgseg(g);
+ break;
+ case 1:
+ g = getgseg(c);
+ q.vers = 0;
+ q.path = PATH(SEG(c), Qdata);
+ q.type = QTFILE;
+ if(g->s != nil)
+ size = g->s->top - g->s->base;
+ else
+ size = 0;
+ devdir(c, q, "data", size, g->uid, g->perm, dp);
+ putgseg(g);
+ break;
+ default:
+ return -1;
+ }
+ break;
+ }
+ return 1;
+}
+
+static void
+segmentinit(void)
+{
+ _globalsegattach = globalsegattach;
+}
+
+static Chan*
+segmentattach(char *spec)
+{
+ return devattach('g', spec);
+}
+
+static Walkqid*
+segmentwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, 0, 0, segmentgen);
+}
+
+static int
+segmentstat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, 0, 0, segmentgen);
+}
+
+static int
+cmddone(void *arg)
+{
+ Globalseg *g = arg;
+
+ return g->cmd == Cnone;
+}
+
+static Chan*
+segmentopen(Chan *c, int omode)
+{
+ Globalseg *g;
+
+ switch(TYPE(c)){
+ case Qtopdir:
+ case Qsegdir:
+ if(omode != 0)
+ error(Eisdir);
+ break;
+ case Qctl:
+ g = getgseg(c);
+ if(waserror()){
+ putgseg(g);
+ nexterror();
+ }
+ devpermcheck(g->uid, g->perm, omode);
+ c->aux = g;
+ poperror();
+ c->flag |= COPEN;
+ break;
+ case Qdata:
+ g = getgseg(c);
+ if(waserror()){
+ putgseg(g);
+ nexterror();
+ }
+ devpermcheck(g->uid, g->perm, omode);
+ if(g->s == nil)
+ error("segment not yet allocated");
+ if(g->kproc == nil){
+ qlock(&g->l);
+ if(waserror()){
+ qunlock(&g->l);
+ nexterror();
+ }
+ if(g->kproc == nil){
+ g->cmd = Cnone;
+ kproc(g->name, segmentkproc, g);
+ docmd(g, Cstart);
+ }
+ qunlock(&g->l);
+ poperror();
+ }
+ c->aux = g;
+ poperror();
+ c->flag |= COPEN;
+ break;
+ default:
+ panic("segmentopen");
+ }
+ c->mode = openmode(omode);
+ c->offset = 0;
+ return c;
+}
+
+static void
+segmentclose(Chan *c)
+{
+ if(TYPE(c) == Qtopdir)
+ return;
+ if(c->flag & COPEN)
+ putgseg(c->aux);
+}
+
+static void
+segmentcreate(Chan *c, char *name, int omode, ulong perm)
+{
+ int x, xfree;
+ Globalseg *g;
+
+ if(TYPE(c) != Qtopdir)
+ error(Eperm);
+
+ if(isphysseg(name))
+ error(Eexist);
+
+ if((perm & DMDIR) == 0)
+ error(Ebadarg);
+
+ if(waserror()){
+ unlock(&globalseglock);
+ nexterror();
+ }
+ lock(&globalseglock);
+ xfree = -1;
+ for(x = 0; x < nelem(globalseg); x++){
+ g = globalseg[x];
+ if(g == nil){
+ if(xfree < 0)
+ xfree = x;
+ } else {
+ if(strcmp(g->name, name) == 0)
+ error(Eexist);
+ }
+ }
+ if(xfree < 0)
+ error("too many global segments");
+ g = smalloc(sizeof(Globalseg));
+ g->ref = 1;
+ kstrdup(&g->name, name);
+ kstrdup(&g->uid, up->user);
+ g->perm = 0660;
+ globalseg[xfree] = g;
+ unlock(&globalseglock);
+ poperror();
+
+ c->qid.path = PATH(x, Qsegdir);
+ c->qid.type = QTDIR;
+ c->qid.vers = 0;
+ c->mode = openmode(omode);
+ c->mode = OWRITE;
+}
+
+static long
+segmentread(Chan *c, void *a, long n, vlong voff)
+{
+ Globalseg *g;
+ char buf[32];
+
+ if(c->qid.type == QTDIR)
+ return devdirread(c, a, n, (Dirtab *)0, 0L, segmentgen);
+
+ switch(TYPE(c)){
+ case Qctl:
+ g = c->aux;
+ if(g->s == nil)
+ error("segment not yet allocated");
+ sprint(buf, "va %#lux %#lux\n", g->s->base, g->s->top-g->s->base);
+ return readstr(voff, a, n, buf);
+ case Qdata:
+ g = c->aux;
+ if(voff > g->s->top - g->s->base)
+ error(Ebadarg);
+ if(voff + n > g->s->top - g->s->base)
+ n = g->s->top - g->s->base - voff;
+ qlock(&g->l);
+ g->off = voff + g->s->base;
+ g->data = smalloc(n);
+ if(waserror()){
+ free(g->data);
+ qunlock(&g->l);
+ nexterror();
+ }
+ g->dlen = n;
+ docmd(g, Cread);
+ memmove(a, g->data, g->dlen);
+ free(g->data);
+ qunlock(&g->l);
+ poperror();
+ return g->dlen;
+ default:
+ panic("segmentread");
+ }
+ return 0; /* not reached */
+}
+
+static long
+segmentwrite(Chan *c, void *a, long n, vlong voff)
+{
+ Cmdbuf *cb;
+ Globalseg *g;
+ ulong va, len, top;
+
+ if(c->qid.type == QTDIR)
+ error(Eperm);
+
+ switch(TYPE(c)){
+ case Qctl:
+ g = c->aux;
+ cb = parsecmd(a, n);
+ if(strcmp(cb->f[0], "va") == 0){
+ if(g->s != nil)
+ error("already has a virtual address");
+ if(cb->nf < 3)
+ error(Ebadarg);
+ va = strtoul(cb->f[1], 0, 0);
+ len = strtoul(cb->f[2], 0, 0);
+ top = PGROUND(va + len);
+ va = va&~(BY2PG-1);
+ len = (top - va) / BY2PG;
+ if(len == 0)
+ error(Ebadarg);
+ g->s = newseg(SG_SHARED, va, len);
+ } else
+ error(Ebadctl);
+ break;
+ case Qdata:
+ g = c->aux;
+ if(voff + n > g->s->top - g->s->base)
+ error(Ebadarg);
+ qlock(&g->l);
+ g->off = voff + g->s->base;
+ g->data = smalloc(n);
+ if(waserror()){
+ free(g->data);
+ qunlock(&g->l);
+ nexterror();
+ }
+ g->dlen = n;
+ memmove(g->data, a, g->dlen);
+ docmd(g, Cwrite);
+ free(g->data);
+ qunlock(&g->l);
+ poperror();
+ return g->dlen;
+ default:
+ panic("segmentwrite");
+ }
+ return 0; /* not reached */
+}
+
+static int
+segmentwstat(Chan *c, uchar *dp, int n)
+{
+ Globalseg *g;
+ Dir *d;
+
+ if(c->qid.type == QTDIR)
+ error(Eperm);
+
+ g = getgseg(c);
+ if(waserror()){
+ putgseg(g);
+ nexterror();
+ }
+
+ if(strcmp(g->uid, up->user) && !iseve())
+ error(Eperm);
+ d = smalloc(sizeof(Dir)+n);
+ n = convM2D(dp, n, &d[0], (char*)&d[1]);
+ g->perm = d->mode & 0777;
+
+ putgseg(g);
+ poperror();
+
+ free(d);
+ return n;
+}
+
+static void
+segmentremove(Chan *c)
+{
+ Globalseg *g;
+ int x;
+
+ if(TYPE(c) != Qsegdir)
+ error(Eperm);
+ lock(&globalseglock);
+ x = SEG(c);
+ g = globalseg[x];
+ globalseg[x] = nil;
+ unlock(&globalseglock);
+ if(g != nil)
+ putgseg(g);
+}
+
+/*
+ * called by segattach()
+ */
+static Segment*
+globalsegattach(Proc *p, char *name)
+{
+ int x;
+ Globalseg *g;
+ Segment *s;
+
+ g = nil;
+ if(waserror()){
+ unlock(&globalseglock);
+ nexterror();
+ }
+ lock(&globalseglock);
+ for(x = 0; x < nelem(globalseg); x++){
+ g = globalseg[x];
+ if(g != nil && strcmp(g->name, name) == 0)
+ break;
+ }
+ if(x == nelem(globalseg)){
+ unlock(&globalseglock);
+ poperror();
+ return nil;
+ }
+ devpermcheck(g->uid, g->perm, ORDWR);
+ s = g->s;
+ if(s == nil)
+ error("global segment not assigned a virtual address");
+ if(isoverlap(p, s->base, s->top - s->base) != nil)
+ error("overlaps existing segment");
+ incref(s);
+ unlock(&globalseglock);
+ poperror();
+ return s;
+}
+
+static void
+docmd(Globalseg *g, int cmd)
+{
+ g->err[0] = 0;
+ g->cmd = cmd;
+ wakeup(&g->cmdwait);
+ sleep(&g->replywait, cmddone, g);
+ if(g->err[0])
+ error(g->err);
+}
+
+static int
+cmdready(void *arg)
+{
+ Globalseg *g = arg;
+
+ return g->cmd != Cnone;
+}
+
+static void
+segmentkproc(void *arg)
+{
+ Globalseg *g = arg;
+ int done;
+ int sno;
+
+ for(sno = 0; sno < NSEG; sno++)
+ if(up->seg[sno] == nil && sno != ESEG)
+ break;
+ if(sno == NSEG)
+ panic("segmentkproc");
+ g->kproc = up;
+
+ incref(g->s);
+ up->seg[sno] = g->s;
+
+ for(done = 0; !done;){
+ sleep(&g->cmdwait, cmdready, g);
+ if(waserror()){
+ strncpy(g->err, up->errstr, sizeof(g->err));
+ } else {
+ switch(g->cmd){
+ case Cstart:
+ break;
+ case Cdie:
+ done = 1;
+ break;
+ case Cread:
+ memmove(g->data, (char*)g->off, g->dlen);
+ break;
+ case Cwrite:
+ memmove((char*)g->off, g->data, g->dlen);
+ break;
+ }
+ poperror();
+ }
+ g->cmd = Cnone;
+ wakeup(&g->replywait);
+ }
+}
+
+Dev segmentdevtab = {
+ 'g',
+ "segment",
+
+ devreset,
+ segmentinit,
+ devshutdown,
+ segmentattach,
+ segmentwalk,
+ segmentstat,
+ segmentopen,
+ segmentcreate,
+ segmentclose,
+ segmentread,
+ devbread,
+ segmentwrite,
+ devbwrite,
+ segmentremove,
+ segmentwstat,
+};
+