summaryrefslogtreecommitdiff
path: root/emu/Plan9
diff options
context:
space:
mode:
Diffstat (limited to 'emu/Plan9')
-rw-r--r--emu/Plan9/asm-386.s33
-rw-r--r--emu/Plan9/asm-mips.s24
-rw-r--r--emu/Plan9/asm-power.s28
-rw-r--r--emu/Plan9/asm-sparc.s22
-rw-r--r--emu/Plan9/cmd.c178
-rw-r--r--emu/Plan9/devfs.c365
-rw-r--r--emu/Plan9/devsrv9.c403
-rw-r--r--emu/Plan9/emu109
-rw-r--r--emu/Plan9/emusig95
-rw-r--r--emu/Plan9/mkfile49
-rw-r--r--emu/Plan9/os.c422
-rw-r--r--emu/Plan9/win.c587
12 files changed, 2315 insertions, 0 deletions
diff --git a/emu/Plan9/asm-386.s b/emu/Plan9/asm-386.s
new file mode 100644
index 00000000..b8193f71
--- /dev/null
+++ b/emu/Plan9/asm-386.s
@@ -0,0 +1,33 @@
+
+TEXT tramp(SB),$0
+ MOVL nsp+0(FP), BX /* new stack */
+ MOVL fn+4(FP), CX /* func to exec */
+ MOVL arg+8(FP),DX
+
+ LEAL -8(BX), SP /* new stack */
+ PUSHL DX
+ CALL *CX
+ POPL AX
+
+ PUSHL $0
+ CALL _exits(SB)
+ POPL AX
+ RET
+
+TEXT vstack(SB),$0
+ MOVL arg+0(FP), AX
+ MOVL ustack(SB), SP
+ PUSHL AX
+ CALL exectramp(SB)
+ POPL AX /* dammit ken! */
+ RET
+
+TEXT FPsave(SB), 1, $0
+ MOVL fpu+0(FP), AX
+ FSTENV 0(AX)
+ RET
+
+TEXT FPrestore(SB), 1, $0
+ MOVL fpu+0(FP), AX
+ FLDENV 0(AX)
+ RET
diff --git a/emu/Plan9/asm-mips.s b/emu/Plan9/asm-mips.s
new file mode 100644
index 00000000..b03f8209
--- /dev/null
+++ b/emu/Plan9/asm-mips.s
@@ -0,0 +1,24 @@
+
+ TEXT tramp(SB), 1, $0
+ ADDU $-8, R1, R3 /* new stack */
+ MOVW 4(FP), R2 /* func to exec */
+ MOVW 8(FP), R1 /* arg to reg */
+ MOVW R3, R29 /* new stack */
+ JAL (R2)
+ MOVW R0, R1
+ JMP _exits(SB)
+
+ TEXT vstack(SB), 1, $0 /* Passes &targ through R1 */
+ MOVW ustack(SB), R29
+ JMP exectramp(SB)
+ RET
+
+ TEXT FPsave(SB), 1, $0
+ MOVW FCR31, R2
+ MOVW R2, 0(R1)
+ RET
+
+ TEXT FPrestore(SB), 1, $0
+ MOVW 0(R1), R2
+ MOVW R2, FCR31
+ RET
diff --git a/emu/Plan9/asm-power.s b/emu/Plan9/asm-power.s
new file mode 100644
index 00000000..ff1c57f2
--- /dev/null
+++ b/emu/Plan9/asm-power.s
@@ -0,0 +1,28 @@
+ TEXT tramp(SB), 1, $0
+ ADD $-8, R3, R4 /* new stack */
+ MOVW 4(FP), R5 /* func to exec */
+ MOVW R5, LR
+ MOVW 8(FP), R3 /* arg to reg */
+ MOVW R4, R1 /* new stack */
+ BL (LR)
+ MOVW R0, R3
+ MOVW $_exits(SB), R4
+ MOVW R4, LR
+ BR (LR)
+
+ TEXT vstack(SB), 1, $0 /* Passes &targ through R3 */
+ MOVW ustack(SB), R1
+ MOVW $exectramp(SB), R4
+ MOVW R4, CTR
+ BR (CTR)
+ RETURN
+
+ TEXT FPsave(SB), 1, $0
+ MOVFL FPSCR, F0
+ FMOVD F0, 0(R3)
+ RETURN
+
+ TEXT FPrestore(SB), 1, $0
+ FMOVD 0(R3), F0
+ MOVFL F0, FPSCR
+ RETURN
diff --git a/emu/Plan9/asm-sparc.s b/emu/Plan9/asm-sparc.s
new file mode 100644
index 00000000..f68fd02d
--- /dev/null
+++ b/emu/Plan9/asm-sparc.s
@@ -0,0 +1,22 @@
+ TEXT tramp(SB), 1, $0
+ ADD $-8, R7, R3 /* new stack */
+ MOVW 4(FP), R4 /* func to exec */
+ MOVW 8(FP), R7 /* arg to reg */
+ MOVW R3, R1 /* new stack */
+ JMPL (R4)
+ MOVW R0, R7
+ JMPL _exits(SB) /* Leaks the stack in R29 */
+
+ TEXT vstack(SB), 1, $0 /* Passes &targ through R7 */
+ MOVW ustack(SB), R1
+ MOVW $exectramp(SB), R3
+ JMP (R3)
+ RETURN
+
+ TEXT FPsave(SB), 1, $0
+ MOVW FSR, 0(R7)
+ RETURN
+
+ TEXT FPrestore(SB), 1, $0
+ MOVW 0(R7), FSR
+ RETURN
diff --git a/emu/Plan9/cmd.c b/emu/Plan9/cmd.c
new file mode 100644
index 00000000..d3822f50
--- /dev/null
+++ b/emu/Plan9/cmd.c
@@ -0,0 +1,178 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+extern void vstack(void*);
+
+/*
+ * all this is for the benefit of devcmd.
+ * i hope it's grateful.
+ */
+
+typedef struct Targ Targ;
+struct Targ
+{
+ int fd[2];
+ int wfd;
+ int* spin;
+ char** args;
+ char* dir;
+ int pid;
+ int nice;
+};
+
+/*
+ * called by vstack once it has moved to
+ * the unshared stack in the new process.
+ */
+void
+exectramp(Targ *t)
+{
+ int *fd, i, nfd;
+ char filename[128], err[ERRMAX], status[2*ERRMAX];
+
+ t->pid = getpid();
+ *t->spin = 0; /* allow parent to proceed: can't just rendezvous: see below */
+ fd = t->fd;
+
+ snprint(filename, sizeof(filename), "#d/%d", t->wfd);
+ t->wfd = open(filename, OWRITE|OCEXEC);
+ /* if it failed, we'll manage */
+
+ nfd = MAXNFD; /* TO DO: should read from /fd */
+ for(i = 0; i < nfd; i++)
+ if(i != fd[0] && i != fd[1] && i != t->wfd)
+ close(i);
+
+ if(fd[0] != 0){
+ dup(fd[0], 0);
+ close(fd[0]);
+ }
+ if(fd[0] != 1){
+ dup(fd[1], 1);
+ close(fd[1]);
+ }
+ dup(1, 2);
+
+ if(t->dir != nil && chdir(t->dir) < 0){
+ if(t->wfd > 0)
+ fprint(t->wfd, "chdir: %s: %r", t->dir);
+ _exits("bad dir");
+ }
+ if(t->nice)
+ oslopri();
+
+ exec(t->args[0], t->args);
+ err[0] = 0;
+ errstr(err, sizeof(err));
+ if(t->args[0][0] != '/' && t->args[0][0] != '#' &&
+ strncmp(t->args[0], "../", 3) != 0 && strncmp(t->args[0], "./", 2) != 0 &&
+ strlen(t->args[0])+5 < sizeof(filename)){
+ snprint(filename, sizeof(filename), "/bin/%s", t->args[0]);
+ exec(filename, t->args);
+ errstr(err, sizeof(err));
+ }
+ snprint(status, sizeof(status), "%s: can't exec: %s", t->args[0], err);
+ if(t->wfd > 0)
+ write(t->wfd, status, strlen(status));
+ _exits(status);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *rfd, int *sfd)
+{
+ Targ *t;
+ int spin, *spinptr, fd0[2], fd1[2], wfd[2], n;
+ Dir *d;
+
+ up->genbuf[0] = 0;
+ t = mallocz(sizeof(*t), 1);
+ if(t == nil)
+ return nil;
+ t->args = args;
+ t->dir = dir;
+ t->nice = nice;
+ fd0[0] = fd0[1] = -1;
+ fd1[0] = fd1[1] = -1;
+ wfd[0] = wfd[1] = -1;
+ if(dir != nil){
+ d = dirstat(dir);
+ if(d == nil)
+ goto Error;
+ free(d);
+ }
+ if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(wfd) < 0)
+ goto Error;
+
+ spinptr = &spin;
+ spin = 1;
+
+ t->fd[0] = fd0[0];
+ t->fd[1] = fd1[1];
+ t->wfd = wfd[1];
+ t->spin = spinptr;
+ switch(rfork(RFPROC|RFMEM|RFREND|RFNOTEG|RFFDG|RFNAMEG|RFENVG)) {
+ case -1:
+ goto Error;
+ case 0:
+ /* if child returns first from rfork, its call to vstack replaces ... */
+ vstack(t);
+ /* ... parent's return address from rfork and parent returns here */
+ default:
+ /* if parent returns first from rfork, it comes here */
+ /* can't call anything: on shared stack until child releases spin in exectramp */
+ while(*spinptr)
+ ;
+ break;
+ }
+
+ close(fd0[0]);
+ close(fd1[1]);
+ close(wfd[1]);
+ n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+ close(wfd[0]);
+ if(n > 0){
+ close(fd0[1]);
+ close(fd1[0]);
+ up->genbuf[n] = 0;
+ errstr(up->genbuf, sizeof(up->genbuf));
+ free(t);
+ return nil;
+ }
+
+ *sfd = fd0[1];
+ *rfd = fd1[0];
+ return t;
+
+Error:
+ errstr(up->genbuf, sizeof(up->genbuf)); /* save the message before close */
+ close(fd0[0]);
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd1[1]);
+ close(wfd[0]);
+ close(wfd[1]);
+ free(t);
+ errstr(up->genbuf, sizeof(up->genbuf));
+ return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+ Targ *t = a;
+
+ return postnote(PNGROUP, t->pid, "kill");
+}
+
+int
+oscmdwait(void*, char *buf, int n)
+{
+ return await(buf, n);
+}
+
+void
+oscmdfree(void *a)
+{
+ free(a);
+}
diff --git a/emu/Plan9/devfs.c b/emu/Plan9/devfs.c
new file mode 100644
index 00000000..d0db522d
--- /dev/null
+++ b/emu/Plan9/devfs.c
@@ -0,0 +1,365 @@
+/*
+ * Plan 9 file system interface
+ */
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+typedef struct Fsinfo Fsinfo;
+struct Fsinfo
+{
+ int fd;
+ QLock; /* serialise access to offset */
+ ulong offset; /* offset used only for directory reads */
+ Cname* name; /* Plan 9's name for file */
+ Qid rootqid; /* Plan 9's qid for Inferno's root */
+ char* root; /* prefix to strip from all names in diagnostics */
+};
+#define FS(c) ((Fsinfo*)((c)->aux))
+
+char rootdir[MAXROOT] = ROOT;
+
+static void
+fserr(Fsinfo *f)
+{
+ int n;
+ char *p;
+
+ oserrstr(up->env->errstr, ERRMAX);
+ if(f != nil && *up->env->errstr == '\'' && (n = strlen(f->root)) > 1){
+ /* don't reveal full names */
+ if(strncmp(up->env->errstr+1, f->root, n-1) == 0){
+ p = up->env->errstr+1+n;
+ memmove(up->env->errstr+1, p, strlen(p)+1);
+ }
+ }
+ error(up->env->errstr);
+}
+
+static void
+fsfree(Chan *c)
+{
+ cnameclose(FS(c)->name);
+ free(c->aux);
+}
+
+Chan*
+fsattach(char *spec)
+{
+ Chan *c;
+ Dir *d;
+ char *root;
+ Qid rootqid;
+ static int devno;
+ static Lock l;
+
+ if(!emptystr(spec)){
+ if(strcmp(spec, "*") != 0)
+ error(Ebadspec);
+ root = "/";
+ }else
+ root = rootdir;
+
+ d = dirstat(root);
+ if(d == nil)
+ fserr(nil);
+ rootqid = d->qid;
+ free(d);
+
+ c = devattach('U', spec);
+ lock(&l);
+ c->dev = devno++;
+ c->qid = rootqid;
+ unlock(&l);
+ c->aux = smalloc(sizeof(Fsinfo));
+ FS(c)->name = newcname(root);
+ FS(c)->rootqid = rootqid;
+ FS(c)->fd = -1;
+ FS(c)->root = root;
+
+ return c;
+}
+
+Walkqid*
+fswalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ int j, alloc;
+ Walkqid *wq;
+ Dir *dir;
+ char *n;
+ Cname *current, *next;
+ Qid rootqid;
+
+ if(nname > 0)
+ isdir(c); /* do we need this? */
+
+ alloc = 0;
+ current = nil;
+ wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
+ if(waserror()){
+ if(alloc && wq->clone!=nil)
+ cclose(wq->clone);
+ cnameclose(current);
+ free(wq);
+ return nil;
+ }
+ if(nc == nil){
+ nc = devclone(c);
+ nc->type = 0;
+ alloc = 1;
+ }
+ wq->clone = nc;
+
+ rootqid = FS(c)->rootqid;
+ current = FS(c)->name;
+ if(current != nil)
+ incref(&current->r);
+ for(j=0; j<nname; j++){
+ if(!(nc->qid.type&QTDIR)){
+ if(j==0)
+ error(Enotdir);
+ break;
+ }
+ n = name[j];
+ if(strcmp(n, ".") != 0 && !(isdotdot(n) && nc->qid.path == rootqid.path)){ /* TO DO: underlying qids aliased */
+ //print("** ufs walk '%s' -> %s\n", current->s, n);
+ next = current;
+ incref(&next->r);
+ next = addelem(current, n);
+ dir = dirstat(next->s);
+ if(dir == nil){
+ cnameclose(next);
+ if(j == 0)
+ error(Enonexist);
+ strcpy(up->env->errstr, Enonexist);
+ break;
+ }
+ nc->qid = dir->qid;
+ free(dir);
+ cnameclose(current);
+ current = next;
+ }
+ wq->qid[wq->nqid++] = nc->qid;
+ }
+// print("** ufs walk '%s'\n", current->s);
+
+ poperror();
+ if(wq->nqid < nname){
+ cnameclose(current);
+ if(alloc)
+ cclose(wq->clone);
+ wq->clone = nil;
+ }else if(wq->clone){
+ /* now attach to our device */
+ nc->aux = smalloc(sizeof(Fsinfo));
+ nc->type = c->type;
+ FS(nc)->rootqid = FS(c)->rootqid;
+ FS(nc)->name = current;
+ FS(nc)->fd = -1;
+ FS(nc)->root = FS(c)->root;
+ }else
+ panic("fswalk: can't happen");
+ return wq;
+}
+
+int
+fsstat(Chan *c, uchar *dp, int n)
+{
+ if(FS(c)->fd >= 0)
+ n = fstat(FS(c)->fd, dp, n);
+ else
+ n = stat(FS(c)->name->s, dp, n);
+ if(n < 0)
+ fserr(FS(c));
+ /* TO DO: change name to / if rootqid */
+ return n;
+}
+
+Chan*
+fsopen(Chan *c, int mode)
+{
+ osenter();
+ FS(c)->fd = open(FS(c)->name->s, mode);
+ osleave();
+ if(FS(c)->fd < 0)
+ fserr(FS(c));
+ c->mode = openmode(mode);
+ c->offset = 0;
+ FS(c)->offset = 0;
+ c->flag |= COPEN;
+ return c;
+}
+
+void
+fscreate(Chan *c, char *name, int mode, ulong perm)
+{
+ Dir *d;
+ Cname *n;
+
+ if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
+ error(Efilename);
+ n = addelem(newcname(FS(c)->name->s), name);
+ osenter();
+ FS(c)->fd = create(n->s, mode, perm);
+ osleave();
+ if(FS(c)->fd < 0) {
+ cnameclose(n);
+ fserr(FS(c));
+ }
+ d = dirfstat(FS(c)->fd);
+ if(d == nil) {
+ cnameclose(n);
+ close(FS(c)->fd);
+ FS(c)->fd = -1;
+ fserr(FS(c));
+ }
+ c->qid = d->qid;
+ free(d);
+
+ cnameclose(FS(c)->name);
+ FS(c)->name = n;
+
+ c->mode = openmode(mode);
+ c->offset = 0;
+ FS(c)->offset = 0;
+ c->flag |= COPEN;
+}
+
+void
+fsclose(Chan *c)
+{
+ if(c->flag & COPEN){
+ osenter();
+ close(FS(c)->fd);
+ osleave();
+ }
+ /* don't need to check for CRCLOSE, because Plan 9 itself implements ORCLOSE */
+ fsfree(c);
+}
+
+static long
+fsdirread(Chan *c, void *va, long count, vlong offset)
+{
+ long n, r;
+ static char slop[16384];
+
+ if(FS(c)->offset != offset){
+ seek(FS(c)->fd, 0, 0);
+ for(n=0; n<offset;) {
+ r = offset - n;
+ if(r > sizeof(slop))
+ r = sizeof(slop);
+ osenter();
+ r = read(FS(c)->fd, slop, r);
+ osleave();
+ if(r <= 0){
+ FS(c)->offset = n;
+ return 0;
+ }
+ n += r;
+ }
+ FS(c)->offset = offset;
+ }
+ osenter();
+ r = read(FS(c)->fd, va, count);
+ osleave();
+ if(r < 0)
+ return r;
+ FS(c)->offset = offset+r;
+ return r;
+}
+
+long
+fsread(Chan *c, void *va, long n, vlong offset)
+{
+ int r;
+
+ if(c->qid.type & QTDIR){ /* need to maintain offset only for directories */
+ qlock(FS(c));
+ if(waserror()){
+ qunlock(FS(c));
+ nexterror();
+ }
+ r = fsdirread(c, va, n, offset);
+ poperror();
+ qunlock(FS(c));
+ }else{
+ osenter();
+ r = pread(FS(c)->fd, va, n, offset);
+ osleave();
+ }
+ if(r < 0)
+ fserr(FS(c));
+ return r;
+}
+
+long
+fswrite(Chan *c, void *va, long n, vlong offset)
+{
+ int r;
+
+ osenter();
+ r = pwrite(FS(c)->fd, va, n, offset);
+ osleave();
+ if(r < 0)
+ fserr(FS(c));
+ return r;
+}
+
+void
+fsremove(Chan *c)
+{
+ int r;
+
+ if(waserror()){
+ fsfree(c);
+ nexterror();
+ }
+ osenter();
+ r = remove(FS(c)->name->s);
+ osleave();
+ if(r < 0)
+ fserr(FS(c));
+ poperror();
+ fsfree(c);
+}
+
+int
+fswstat(Chan *c, uchar *dp, int n)
+{
+ osenter();
+ if(FS(c)->fd >= 0)
+ n = fwstat(FS(c)->fd, dp, n);
+ else
+ n = wstat(FS(c)->name->s, dp, n);
+ osleave();
+ if(n < 0)
+ fserr(FS(c));
+ return n;
+}
+
+void
+setid(char *name, int owner)
+{
+ if(!owner || iseve())
+ kstrdup(&up->env->user, name);
+}
+
+Dev fsdevtab = {
+ 'U',
+ "fs",
+
+ devinit,
+ fsattach,
+ fswalk,
+ fsstat,
+ fsopen,
+ fscreate,
+ fsclose,
+ fsread,
+ devbread,
+ fswrite,
+ devbwrite,
+ fsremove,
+ fswstat
+};
diff --git a/emu/Plan9/devsrv9.c b/emu/Plan9/devsrv9.c
new file mode 100644
index 00000000..74c97588
--- /dev/null
+++ b/emu/Plan9/devsrv9.c
@@ -0,0 +1,403 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+typedef struct Srv Srv;
+struct Srv
+{
+ Ref;
+ int fd; /* fd for opened /srv or /srv/X, or -1 */
+ int sfd; /* fd for created /srv entry or -1 */
+ uvlong path;
+ Srv *next;
+};
+
+static QLock srv9lk;
+static Srv *srv9;
+static Srv *srvroot;
+
+static void
+oserr(void)
+{
+ up->genbuf[0] = 0;
+ oserrstr(up->genbuf, sizeof(up->genbuf));
+ error(up->genbuf);
+}
+
+static char*
+srvname(Chan *c)
+{
+ char *p;
+
+ p = strrchr(c->name->s, '/');
+ if(p == nil)
+ return "";
+ return p+1;
+}
+
+static Srv*
+srvget(uvlong path)
+{
+ Srv *sv;
+
+ qlock(&srv9lk);
+ for(sv = srv9; sv != nil; sv = sv->next)
+ if(sv->path == path){
+ incref(sv);
+ qunlock(&srv9lk);
+ return sv;
+ }
+ sv = smalloc(sizeof(*sv));
+ sv->path = path;
+ sv->fd = -1;
+ sv->sfd = -1;
+ sv->ref = 1;
+ sv->next = srv9;
+ srv9 = sv;
+ qunlock(&srv9lk);
+ return sv;
+}
+
+static void
+srvput(Srv *sv)
+{
+ Srv **l;
+ int fd, sfd;
+
+ if(sv != nil && decref(sv) == 0){
+ qlock(&srv9lk);
+ for(l = &srv9; *l != nil; l = &(*l)->next)
+ if(*l == sv){
+ *l = sv->next;
+ break;
+ }
+ qunlock(&srv9lk);
+ fd = sv->fd;
+ sfd = sv->sfd;
+ free(sv);
+ if(sfd >= 0){
+ osenter();
+ close(sfd);
+ osleave();
+ }
+ if(fd >= 0){
+ osenter();
+ close(fd);
+ osleave();
+ }
+ }
+}
+
+static void
+srv9init(void)
+{
+ Srv *sv;
+
+ sv = mallocz(sizeof(*srvroot), 1);
+ sv->path = 0;
+ sv->fd = -1;
+ sv->ref = 1; /* subsequently never reaches zero */
+ srvroot = srv9 = sv;
+}
+
+static Chan*
+srv9attach(char *spec)
+{
+ Chan *c;
+
+ if(*spec)
+ error(Ebadspec);
+ c = devattach(L'₪', spec);
+ if(c != nil){
+ incref(srvroot);
+ c->aux = srvroot;
+ }
+ return c;
+}
+
+static Walkqid*
+srv9walk(Chan *c, Chan *nc, char **name, int nname)
+{
+ int j, alloc;
+ Walkqid *wq;
+ char *n;
+ Dir *d;
+
+ if(nname > 0)
+ isdir(c);
+
+ alloc = 0;
+ wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
+ if(waserror()){
+ if(alloc)
+ 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);
+ break;
+ }
+ n = name[j];
+ if(strcmp(n, ".") != 0 && strcmp(n, "..") != 0){
+ snprint(up->genbuf, sizeof(up->genbuf), "/srv/%s", n);
+ d = dirstat(up->genbuf);
+ if(d == nil){
+ if(j == 0)
+ error(Enonexist);
+ kstrcpy(up->env->errstr, Enonexist, ERRMAX);
+ break;
+ }
+ nc->qid = d->qid;
+ free(d);
+ }
+ wq->qid[wq->nqid++] = nc->qid;
+ }
+ poperror();
+ if(wq->nqid < nname){
+ if(alloc)
+ cclose(wq->clone);
+ wq->clone = nil;
+ }else{
+ /* attach cloned channel to device */
+ wq->clone->type = c->type;
+ if(wq->clone != c)
+ nc->aux = srvget(nc->qid.path);
+ }
+ return wq;
+}
+
+static int
+srv9stat(Chan *c, uchar *db, int n)
+{
+ Srv *sv;
+ Dir d;
+
+ if(c->qid.type & QTDIR){
+ devdir(c, c->qid, "#₪", 0, eve, 0775, &d);
+ n = convD2M(&d, db, n);
+ if(n == 0)
+ error(Eshortstat);
+ return n;
+ }
+ sv = c->aux;
+ if(sv->fd >= 0){
+ osenter();
+ n = fstat(sv->fd, db, n);
+ osleave();
+ }else{
+ osenter();
+ n = stat(srvname(c), db, n);
+ osleave();
+ }
+ return n;
+}
+
+static Chan*
+srv9open(Chan *c, int omode)
+{
+ Srv *sv;
+ char *args[10];
+ int fd[2], i, ifd, is9p;
+ Dir *d;
+
+ sv = c->aux;
+ if(c->qid.type == QTDIR){
+ osenter();
+ sv->fd = open("/srv", omode);
+ osleave();
+ if(sv->fd < 0)
+ oserr();
+ c->mode = omode;
+ c->flag |= COPEN;
+ c->offset = 0;
+ return c;
+ }
+
+ if(omode&OTRUNC || openmode(omode) != ORDWR)
+ error(Eperm);
+ if(sv->fd < 0){
+ snprint(up->genbuf, sizeof(up->genbuf), "/srv/%s", srvname(c));
+
+ /* check permission */
+ osenter();
+ ifd = open(up->genbuf, omode);
+ osleave();
+ if(ifd < 0)
+ oserr();
+ osenter();
+ d = dirfstat(ifd);
+ is9p = d != nil && d->qid.type & QTMOUNT;
+ free(d);
+ osleave();
+
+ if(is9p){
+ close(ifd);
+
+ /* spawn exportfs */
+ args[0] = "exportfs";
+ args[1] = "-dS";
+ args[2] = up->genbuf;
+ args[3] = nil;
+ if(pipe(fd) < 0)
+ oserr();
+ /* TO DO: without RFMEM there's a copy made of each page touched by any kproc until the exec */
+ switch(rfork(RFPROC|RFNOWAIT|RFREND|RFFDG|RFNAMEG|RFENVG)){ /* no sharing except NOTEG */
+ case -1:
+ oserrstr(up->genbuf, sizeof(up->genbuf));
+ close(fd[0]);
+ close(fd[1]);
+ error(up->genbuf);
+ case 0:
+ for(i=3; i<MAXNFD; i++)
+ if(i != fd[1])
+ close(i);
+ dup(fd[1], 0);
+ if(fd[0] != 0)
+ close(fd[0]);
+ dup(0, 1);
+ exec("/bin/exportfs", args);
+ exits("exportfs failed");
+ default:
+ sv->fd = fd[0];
+ close(fd[1]);
+ break;
+ }
+ }else
+ sv->fd = ifd;
+ }
+
+ c->mode = ORDWR;
+ c->offset = 0;
+ c->flag |= COPEN;
+ return c;
+}
+
+static void
+srv9close(Chan *c)
+{
+ srvput(c->aux);
+}
+
+static long
+srv9read(Chan *c, void *va, long n, vlong off)
+{
+ Srv *sv;
+
+ sv = c->aux;
+ osenter();
+ n = pread(sv->fd, va, n, off);
+ osleave();
+ if(n < 0)
+ oserr();
+ return n;
+}
+
+static long
+srv9write(Chan *c, void *va, long n, vlong off)
+{
+ Srv *sv;
+
+ sv = c->aux;
+ osenter();
+ n = pwrite(sv->fd, va, n, off);
+ osleave();
+ if(n == 0)
+ error(Ehungup);
+ if(n < 0)
+ oserr();
+ return n;
+}
+
+static void
+srv9create(Chan *c, char *name, int omode, ulong perm)
+{
+ Srv *sv;
+ int sfd, fd[2];
+ vlong path;
+ Dir *d;
+
+ if(openmode(omode) != ORDWR)
+ error(Eperm);
+
+ if(pipe(fd) < 0)
+ oserr();
+ if(waserror()){
+ close(fd[0]);
+ close(fd[1]);
+ nexterror();
+ }
+
+ snprint(up->genbuf, sizeof(up->genbuf), "/srv/%s", name);
+ osenter();
+ sfd = create(up->genbuf, OWRITE|ORCLOSE, perm);
+ osleave();
+ if(sfd < 0)
+ oserr();
+ if(waserror()){
+ close(sfd);
+ nexterror();
+ }
+ osenter();
+ if(fprint(sfd, "%d", fd[1]) < 0){
+ osleave();
+ oserr();
+ }
+ d = dirfstat(sfd);
+ osleave();
+ if(d != nil){
+ path = d->qid.path;
+ free(d);
+ }else
+ oserr();
+
+ poperror();
+ poperror();
+ close(fd[1]);
+
+ if(waserror()){
+ close(sfd);
+ close(fd[0]);
+ nexterror();
+ }
+ sv = srvget(path);
+ sv->fd = fd[0];
+ sv->sfd = sfd;
+ poperror();
+
+ srvput((Srv*)c->aux);
+ c->qid.type = QTFILE;
+ c->qid.path = path;
+ c->aux = sv;
+ c->flag |= COPEN;
+ c->mode = ORDWR;
+ c->offset = 0;
+}
+
+Dev srv9devtab = {
+ L'₪',
+ "srv9",
+
+ srv9init,
+ srv9attach,
+ srv9walk,
+ srv9stat,
+ srv9open,
+ srv9create, /* TO DO */
+ srv9close,
+ srv9read,
+ devbread,
+ srv9write,
+ devbwrite,
+ devremove, /* TO DO */
+ devwstat, /* TO DO */
+};
diff --git a/emu/Plan9/emu b/emu/Plan9/emu
new file mode 100644
index 00000000..4583bca2
--- /dev/null
+++ b/emu/Plan9/emu
@@ -0,0 +1,109 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+ sign
+
+ draw
+ pointer
+
+ dynld
+ mem
+ srv9
+
+# ip and eia are simply bound in from Plan 9
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+
+ memlayer
+ memdraw
+ keyring
+ sec
+ mp
+ dynld
+ 9
+
+link
+
+mod
+ sys
+ draw
+
+ tk
+ math
+# srv not used on Plan 9
+ keyring
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ exptab
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ print
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /chan /
+ /dev /
+ /fd /
+ /prog /
+ /net /
+ /net.alt /
+ /nvfs /
+ /env /
+ /root /
+ /srv /
+# /tmp /
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
diff --git a/emu/Plan9/emusig b/emu/Plan9/emusig
new file mode 100644
index 00000000..20c6f0d4
--- /dev/null
+++ b/emu/Plan9/emusig
@@ -0,0 +1,95 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+ sign
+
+ draw
+
+# ip and eia are simply bound in from Plan 9
+
+lib
+ interp
+ tk
+ freetype
+ math
+ draw
+ memlayer
+ memdraw
+ keyring
+ crypt
+ 9
+
+link
+
+mod
+ sys
+ draw
+ tk
+ math
+# srv not used on Plan 9
+ keyring
+ loader
+ freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ print
+ proc
+ qio
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /dev /
+ /prog /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
diff --git a/emu/Plan9/mkfile b/emu/Plan9/mkfile
new file mode 100644
index 00000000..2b03031a
--- /dev/null
+++ b/emu/Plan9/mkfile
@@ -0,0 +1,49 @@
+SYSTARG=Plan9
+OBJTYPE=$objtype
+<../../mkconfig
+
+#Configurable parameters
+
+CONF=emu #default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+OSX=os
+WINX=win
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+ asm-$OBJTYPE.$O\
+ $OSX.$O\
+ $WINX.$O\
+ $CONF.root.$O\
+ $DEVS\
+ $PORT\
+
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+KERNDATE=`{$NDATE}
+
+default:V: $O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+safeinstall:V: $O.$CONF
+ mv $INSTALLDIR/$CONF $INSTALLDIR/$CONF.`{date -n}
+ cp $O.$CONF $INSTALLDIR/$CONF
+
+install:V: $O.$CONF
+ cp $O.$CONF $INSTALLDIR/$CONF
diff --git a/emu/Plan9/os.c b/emu/Plan9/os.c
new file mode 100644
index 00000000..d681ec67
--- /dev/null
+++ b/emu/Plan9/os.c
@@ -0,0 +1,422 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+enum
+{
+ KSTACK = 16*1024,
+ DELETE = 0x7F,
+};
+
+Proc **Xup;
+
+extern void killrefresh(void);
+extern void tramp(char*, void (*)(void*), void*);
+
+extern int usenewwin;
+
+int *ustack; /* address on unshared stack: see vstack in asm*.s */
+extern int dflag;
+char *hosttype = "Plan9";
+char *cputype;
+
+void
+osblock(void)
+{
+ rendezvous(up, nil);
+}
+
+void
+osready(Proc *p)
+{
+ rendezvous(p, nil);
+}
+
+void
+pexit(char *msg, int)
+{
+ Osenv *e;
+
+ USED(msg);
+
+ lock(&procs.l);
+ if(up->prev)
+ up->prev->next = up->next;
+ else
+ procs.head = up->next;
+
+ if(up->next)
+ up->next->prev = up->prev;
+ else
+ procs.tail = up->prev;
+ unlock(&procs.l);
+
+/* print("pexit: %s: %s\n", up->text, msg); /**/
+ e = up->env;
+ if(e != nil) {
+ closefgrp(e->fgrp);
+ closepgrp(e->pgrp);
+ closeegrp(e->egrp);
+ closesigs(e->sigs);
+ }
+ free(e->user);
+ free(up->prog);
+ up->prog = nil;
+ up->type = Moribund;
+ longjmp(up->privstack, 1);
+}
+
+int
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+ int pid;
+ Proc *p;
+ Pgrp *pg;
+ Fgrp *fg;
+ Egrp *eg;
+
+ p = newproc();
+ if(p == nil)
+ panic("kproc: no memory");
+ p->kstack = mallocz(KSTACK, 0);
+ if(p->kstack == nil)
+ panic("kproc: no memory");
+
+ if(flags & KPDUPPG) {
+ pg = up->env->pgrp;
+ incref(&pg->r);
+ p->env->pgrp = pg;
+ }
+ if(flags & KPDUPFDG) {
+ fg = up->env->fgrp;
+ incref(&fg->r);
+ p->env->fgrp = fg;
+ }
+ if(flags & KPDUPENVG) {
+ eg = up->env->egrp;
+ incref(&eg->r);
+ p->env->egrp = eg;
+ }
+
+ p->env->uid = up->env->uid;
+ p->env->gid = up->env->gid;
+ kstrdup(&p->env->user, up->env->user);
+
+ strcpy(p->text, name);
+
+ p->func = func;
+ p->arg = arg;
+
+ lock(&procs.l);
+ if(procs.tail != nil) {
+ p->prev = procs.tail;
+ procs.tail->next = p;
+ }
+ else {
+ procs.head = p;
+ p->prev = nil;
+ }
+ procs.tail = p;
+ unlock(&procs.l);
+
+ /*
+ * switch back to the unshared stack to do the fork
+ * only the parent returns from kproc
+ */
+ up->kid = p;
+ up->kidsp = p->kstack;
+ pid = setjmp(up->sharestack);
+ if(pid == 0)
+ longjmp(up->privstack, 1);
+ return pid;
+}
+
+void
+traphandler(void *reg, char *msg)
+{
+ int intwait;
+
+ intwait = up->intwait;
+ up->intwait = 0;
+ /* Ignore pipe writes from devcmd */
+ if(strstr(msg, "write on closed pipe") != nil)
+ noted(NCONT);
+
+ if(sflag) {
+ if(intwait && strcmp(msg, Eintr) == 0)
+ noted(NCONT);
+ else
+ noted(NDFLT);
+ }
+ if(intwait == 0)
+ disfault(reg, msg);
+ noted(NCONT);
+}
+
+int
+readfile(char *path, char *buf, int n)
+{
+ int fd;
+
+ fd = open(path, OREAD);
+ if(fd >= 0) {
+ n = read(fd, buf, n-1);
+ if(n > 0) /* both calls to readfile() have a ``default'' */
+ buf[n] = '\0';
+ close(fd);
+ return n;
+ }
+ return 0;
+}
+
+static void
+dobinds(void)
+{
+ char dir[MAXROOT+9];
+
+ snprint(dir, sizeof(dir), "%s/net", rootdir);
+ bind("/net", dir, MREPL);
+
+ snprint(dir, sizeof(dir), "%s/net.alt", rootdir);
+ bind("/net.alt", dir, MREPL);
+
+ snprint(dir, sizeof(dir), "%s/dev", rootdir);
+ bind("#t", dir, MAFTER);
+ bind("#A", dir, MAFTER);
+}
+
+void
+libinit(char *imod)
+{
+ char *sp;
+ Proc *xup, *p;
+ int fd, n, pid;
+ char nbuf[64];
+
+ xup = nil;
+ Xup = &xup;
+
+ /*
+ * setup personality
+ */
+ if(readfile("/dev/user", nbuf, sizeof nbuf))
+ kstrdup(&eve, nbuf);
+ if(readfile("/dev/sysname", nbuf, sizeof nbuf))
+ kstrdup(&ossysname, nbuf);
+ if(readfile("/env/cputype", nbuf, sizeof nbuf))
+ kstrdup(&cputype, nbuf);
+
+ /*
+ * guess at a safe stack for vstack
+ */
+ ustack = &fd;
+
+ rfork(RFNAMEG|RFREND);
+
+ if(!dflag){
+ fd = open("/dev/consctl", OWRITE);
+ if(fd < 0)
+ fprint(2, "libinit: open /dev/consctl: %r\n");
+ n = write(fd, "rawon", 5);
+ if(n != 5)
+ fprint(2, "keyboard rawon (n=%d, %r)\n", n);
+ }
+
+ osmillisec(); /* set the epoch */
+ dobinds();
+
+ notify(traphandler);
+
+ /*
+ * dummy up a up and stack so the first proc
+ * calls emuinit after setting up his private jmp_buf
+ */
+ p = newproc();
+ p->kstack = mallocz(KSTACK, 0);
+ if(p == nil || p->kstack == nil)
+ panic("libinit: no memory");
+ sp = p->kstack;
+ p->func = emuinit;
+ p->arg = imod;
+
+ /*
+ * set up a stack for forking kids on separate stacks.
+ * longjmp back here from kproc.
+ */
+ while(setjmp(p->privstack)){
+ if(up->type == Moribund){
+ free(up->kstack);
+ free(up);
+ _exits("");
+ }
+ p = up->kid;
+ sp = up->kidsp;
+ up->kid = nil;
+ switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){
+ case 0:
+ /*
+ * send the kid around the loop to set up his private jmp_buf
+ */
+ break;
+ default:
+ /*
+ * parent just returns to his shared stack in kproc
+ */
+ longjmp(up->sharestack, pid);
+ panic("longjmp failed");
+ }
+ }
+
+ /*
+ * you get here only once per Proc
+ * go to the shared memory stack
+ */
+ up = p;
+ up->pid = up->sigid = getpid();
+ tramp(sp+KSTACK, up->func, up->arg);
+ panic("tramp returned");
+}
+
+void
+oshostintr(Proc *p)
+{
+ postnote(PNPROC, p->sigid, Eintr);
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+ if(regs != nil)
+ notejmp(regs, env, val);
+ else
+ longjmp(env, val);
+}
+
+void
+osreboot(char*, char**)
+{
+}
+
+void
+cleanexit(int x)
+{
+ USED(x);
+ killrefresh();
+ postnote(PNGROUP, getpid(), "interrupt");
+ exits("interrupt");
+}
+
+int
+readkbd(void)
+{
+ int n;
+ char buf[1];
+
+ n = read(0, buf, sizeof(buf));
+ if(n < 0)
+ fprint(2, "emu: keyboard read error: %r\n");
+ if(n <= 0)
+ pexit("keyboard", 0);
+ switch(buf[0]) {
+ case DELETE:
+ cleanexit(0);
+ case '\r':
+ buf[0] = '\n';
+ }
+ return buf[0];
+}
+
+static vlong
+b2v(uchar *p)
+{
+ int i;
+ vlong v;
+
+ v = 0;
+ for(i=0; i<sizeof(uvlong); i++)
+ v = (v<<8)|p[i];
+ return v;
+}
+
+vlong
+nsec(void)
+{
+ int n;
+ static int nsecfd = -1;
+ uchar buf[sizeof(uvlong)];
+
+ if(nsecfd < 0){
+ nsecfd = open("/dev/bintime", OREAD|OCEXEC); /* never closed */
+ if(nsecfd<0){
+ fprint(2,"can't open /dev/bintime: %r\n");
+ return 0;
+ }
+ }
+ n = read(nsecfd, buf, sizeof(buf));
+ if(n!=sizeof(buf)) {
+ fprint(2,"read err on /dev/bintime: %r\n");
+ return 0;
+ }
+ return b2v(buf);
+}
+
+long
+osmillisec(void)
+{
+ static vlong nsec0 = 0;
+
+ if(nsec0 == 0){
+ nsec0 = nsec();
+ return 0;
+ }
+ return (nsec()-nsec0)/1000000;
+}
+
+/*
+ * Return the time since the epoch in microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osusectime(void)
+{
+ return nsec()/1000;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+ sleep(milsec);
+ return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+ return osmillisleep(milsec);
+}
+
+void
+osyield(void)
+{
+ sleep(0);
+}
+
+void
+ospause(void)
+{
+ for(;;)
+ sleep(1000000);
+}
+
+void
+oslopri(void)
+{
+ int fd;
+ char buf[32];
+
+ snprint(buf, sizeof(buf), "/proc/%d/ctl", getpid());
+ if((fd = open(buf, OWRITE)) >= 0){
+ fprint(fd, "pri 8");
+ close(fd);
+ }
+}
diff --git a/emu/Plan9/win.c b/emu/Plan9/win.c
new file mode 100644
index 00000000..d86eea15
--- /dev/null
+++ b/emu/Plan9/win.c
@@ -0,0 +1,587 @@
+#include "dat.h"
+#include "fns.h"
+#include "kernel.h"
+#include "error.h"
+
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "keyboard.h"
+
+enum
+{
+ Margin = 4,
+ Lsize = 100,
+};
+
+extern Memimage *screenimage;
+
+ulong* attachwindow(Rectangle*, ulong*, int*, int*);
+
+static void plan9readmouse(void*);
+static void plan9readkeybd(void*);
+static int mapspecials(char *s1, char *s2, int *n);
+
+int pixels = 1;
+int usenewwin = 1;
+static int truedepth;
+
+static int datafd;
+static int ctlfd;
+static int mousefd = -1;
+static int keybdfd;
+static int mousepid = -1;
+static int keybdpid = -1;
+static int cursfd;
+static char winname[64];
+
+/* Following updated by attachwindow() asynchronously */
+static QLock ql;
+static Rectangle tiler;
+static ulong* data;
+static uchar* loadbuf;
+static int cursfd;
+static int imageid;
+static Rectangle imager;
+static uchar *chunk;
+static int chunksize;
+static int dispbufsize;
+
+#define NINFO 12*12
+#define HDR 21
+
+
+void
+killrefresh(void)
+{
+ if(mousepid < 0)
+ return;
+ close(mousefd);
+ close(ctlfd);
+ close(datafd);
+ postnote(PNPROC, mousepid, Eintr);
+ postnote(PNPROC, keybdpid, Eintr);
+}
+
+uchar*
+attachscreen(Rectangle *r, ulong *chan, int *d, int *width, int *softscreen)
+{
+ int fd;
+ char *p, buf[128], info[NINFO+1];
+
+ if(usenewwin){
+ p = getenv("wsys");
+ if(p == nil)
+ return nil;
+
+ fd = open(p, ORDWR);
+ if(fd < 0) {
+ fprint(2, "attachscreen: can't open window manager: %r\n");
+ return nil;
+ }
+ sprint(buf, "new -dx %d -dy %d", Xsize+2*Margin, Ysize+2*Margin);
+ if(mount(fd, -1, "/mnt/wsys", MREPL, buf) < 0) {
+ fprint(2, "attachscreen: can't mount window manager: %r\n");
+ return nil;
+ }
+ }
+
+ cursfd = open("/mnt/wsys/cursor", OWRITE);
+ if(cursfd < 0) {
+ fprint(2, "attachscreen: open cursor: %r\n");
+ return nil;
+ }
+
+ /* Set up graphics window console (chars->gkbdq) */
+ keybdfd = open("/mnt/wsys/cons", OREAD);
+ if(keybdfd < 0) {
+ fprint(2, "attachscreen: open keyboard: %r\n");
+ return nil;
+ }
+ mousefd = open("/mnt/wsys/mouse", ORDWR);
+ if(mousefd < 0){
+ fprint(2, "attachscreen: can't open mouse: %r\n");
+ return nil;
+ }
+ if(usenewwin){
+ fd = open("/mnt/wsys/consctl", OWRITE);
+ if(fd < 0)
+ fprint(2, "attachscreen: open /mnt/wsys/consctl: %r\n");
+ if(write(fd, "rawon", 5) != 5)
+ fprint(2, "attachscreen: write /mnt/wsys/consctl: %r\n");
+ }
+
+ /* Set up graphics files */
+ ctlfd = open("/dev/draw/new", ORDWR);
+ if(ctlfd < 0){
+ fprint(2, "attachscreen: can't open graphics control file: %r\n");
+ return nil;
+ }
+ if(read(ctlfd, info, sizeof info) < NINFO){
+ close(ctlfd);
+ fprint(2, "attachscreen: can't read graphics control file: %r\n");
+ return nil;
+ }
+ sprint(buf, "/dev/draw/%d/data", atoi(info+0*12));
+ datafd = open(buf, ORDWR|OCEXEC);
+ if(datafd < 0){
+ close(ctlfd);
+ fprint(2, "attachscreen: can't read graphics data file: %r\n");
+ return nil;
+ }
+ dispbufsize = iounit(datafd);
+ if(dispbufsize <= 0)
+ dispbufsize = 8000;
+ if(dispbufsize < 512){
+ close(ctlfd);
+ close(datafd);
+ fprint(2, "attachscreen: iounit %d too small\n", dispbufsize);
+ return nil;
+ }
+ chunksize = dispbufsize - 64;
+
+ if(attachwindow(r, chan, d, width) == nil){
+ close(ctlfd);
+ close(datafd);
+ return nil;
+ }
+
+ mousepid = kproc("readmouse", plan9readmouse, nil, 0);
+ keybdpid = kproc("readkbd", plan9readkeybd, nil, 0);
+ bind("/mnt/wsys", "/dev", MBEFORE);
+
+ fd = open("/dev/label", OWRITE);
+ if (fd >= 0) {
+ write(fd, "inferno", 7);
+ close(fd);
+ }
+
+ *softscreen = 1;
+ return (uchar*)data;
+}
+
+static int
+depthof(char *s)
+{
+ char *es;
+ int n, c, d;
+
+ es = s+12;
+ while(s<es && *s==' ')
+ s++;
+ if(s == es)
+ return -1;
+ if('0'<=*s && *s<='9'){
+ truedepth = 1<<atoi(s);
+ return truedepth;
+ }
+
+ d = 0;
+ while(s<es && *s!=' '){
+ c = *s++; /* skip letter */
+ n = strtoul(s, &s, 10);
+ d += n;
+ if(c != 'r' && c != 'g' && c != 'b' && c != 'k' && c != 'm')
+ return -1;
+ }
+ truedepth = d;
+ return d;
+}
+
+ulong*
+attachwindow(Rectangle *r, ulong *chan, int *d, int *width)
+{
+ int n, fd, nb;
+ char buf[256];
+ uchar ubuf[128];
+ ulong imagechan;
+
+ /*
+ * Discover name of window
+ */
+ fd = open("/mnt/wsys/winname", OREAD);
+ if(fd<0 || (n=read(fd, winname, sizeof winname))<=0){
+ fprint(2, "attachwindow: can only run inferno under rio, not stand-alone\n");
+ return nil;
+ }
+ close(fd);
+ /*
+ * If had previous window, release it
+ */
+ if(imageid > 0){
+ ubuf[0] = 'f';
+ BPLONG(ubuf+1, imageid);
+ if(write(datafd, ubuf, 1+4) != 1+4)
+ fprint(2, "attachwindow: cannot free old window: %r\n");
+ }
+ /*
+ * Allocate image pointing to window, and discover its ID
+ */
+ ubuf[0] = 'n';
+ ++imageid;
+ BPLONG(ubuf+1, imageid);
+ ubuf[5] = n;
+ memmove(ubuf+6, winname, n);
+ if(write(datafd, ubuf, 6+n) != 6+n){
+ fprint(2, "attachwindow: cannot bind %d to window id '%s': %r\n", imageid, winname);
+ return nil;
+ }
+ if(read(ctlfd, buf, sizeof buf) < 12*12){
+ fprint(2, "attachwindow: cannot read window id: %r\n");
+ return nil;
+ }
+ imagechan = strtochan(buf+2*12);
+ if(depthof(buf+2*12) < 0){
+ fprint(2, "attachwindow: cannot handle window depth specifier %.12s\n", buf+2*12);
+ return nil;
+ }
+
+ /*
+ * Report back
+ */
+ if(chan != nil)
+ *chan = imagechan;
+ if(d != nil)
+ *d = chantodepth(imagechan);
+ nb = 0;
+ if(r != nil){
+ Xsize = atoi(buf+6*12)-atoi(buf+4*12)-2*Margin;
+ Ysize = atoi(buf+7*12)-atoi(buf+5*12)-2*Margin;
+ r->min.x = 0;
+ r->min.y = 0;
+ r->max.x = Xsize;
+ r->max.y = Ysize;
+ nb = bytesperline(*r, truedepth);
+ data = malloc(nb*Ysize);
+ loadbuf = malloc(nb*Lsize+1);
+ chunk = malloc(HDR+chunksize+5); /* +5 for flush (1 old, 5 new) */
+ }
+ imager.min.x = atoi(buf+4*12);
+ imager.min.y = atoi(buf+5*12);
+ imager.max.x = atoi(buf+6*12);
+ imager.max.y = atoi(buf+7*12);
+
+ if(width != nil)
+ *width = nb/4;
+
+ tiler.min.x = atoi(buf+4*12)+Margin;
+ tiler.min.y = atoi(buf+5*12)+Margin;
+ tiler.max.x = atoi(buf+6*12)-Margin;
+ tiler.max.y = atoi(buf+7*12)-Margin;
+
+ return data;
+}
+
+int
+plan9loadimage(Rectangle r, uchar *data, int ndata)
+{
+ long dy;
+ int n, bpl;
+
+ if(!rectinrect(r, imager)){
+ werrstr("loadimage: bad rectangle");
+ return -1;
+ }
+ bpl = bytesperline(r, truedepth);
+ n = bpl*Dy(r);
+ if(n > ndata){
+ werrstr("loadimage: insufficient data");
+ return -1;
+ }
+ ndata = 0;
+ while(r.max.y > r.min.y){
+ dy = r.max.y - r.min.y;
+ if(dy*bpl> chunksize)
+ dy = chunksize/bpl;
+ n = dy*bpl;
+ chunk[0] = 'y';
+ BPLONG(chunk+1, imageid);
+ BPLONG(chunk+5, r.min.x);
+ BPLONG(chunk+9, r.min.y);
+ BPLONG(chunk+13, r.max.x);
+ BPLONG(chunk+17, r.min.y+dy);
+ memmove(chunk+21, data, n);
+ ndata += n;
+ data += n;
+ r.min.y += dy;
+ n += 21;
+ if(r.min.y >= r.max.y) /* flush to screen */
+ chunk[n++] = 'v';
+ if(write(datafd, chunk, n) != n)
+ return -1;
+ }
+ return ndata;
+}
+
+static void
+_flushmemscreen(Rectangle r)
+{
+ int n, dy, l;
+ Rectangle rr;
+
+ if(data == nil || loadbuf == nil || chunk==nil)
+ return;
+ if(!rectclip(&r, Rect(0, 0, Xsize, Ysize)))
+ return;
+ if(!rectclip(&r, Rect(0, 0, Dx(tiler), Dy(tiler))))
+ return;
+ if(Dx(r)<=0 || Dy(r)<=0)
+ return;
+ l = bytesperline(r, truedepth);
+ while(r.min.y < r.max.y){
+ dy = Dy(r);
+ if(dy > Lsize)
+ dy = Lsize;
+ rr = r;
+ rr.max.y = rr.min.y+dy;
+ n = unloadmemimage(screenimage, rr, loadbuf, l*dy);
+ /* offset from (0,0) to window */
+ rr.min.x += tiler.min.x;
+ rr.min.y += tiler.min.y;
+ rr.max.x += tiler.min.x;
+ rr.max.y += tiler.min.y;
+ if(plan9loadimage(rr, loadbuf, n) != n)
+ fprint(2, "flushmemscreen: %d bytes: %r\n", n);
+ r.min.y += dy;
+ }
+}
+
+void
+flushmemscreen(Rectangle r)
+{
+ qlock(&ql);
+ _flushmemscreen(r);
+ qunlock(&ql);
+}
+
+void
+drawcursor(Drawcursor *c)
+{
+ int j, i, h, w, bpl;
+ uchar *bc, *bs, *cclr, *cset, curs[2*4+2*2*16];
+
+ /* Set the default system cursor */
+ if(c->data == nil) {
+ write(cursfd, curs, 0);
+ return;
+ }
+
+ BPLONG(curs+0*4, c->hotx);
+ BPLONG(curs+1*4, c->hoty);
+
+ w = (c->maxx-c->minx);
+ h = (c->maxy-c->miny)/2;
+
+ cclr = curs+2*4;
+ cset = curs+2*4+2*16;
+ bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1);
+ bc = c->data;
+ bs = c->data + h*bpl;
+
+ if(h > 16)
+ h = 16;
+ if(w > 16)
+ w = 16;
+ w /= 8;
+ for(i = 0; i < h; i++) {
+ for(j = 0; j < w; j++) {
+ cclr[j] = bc[j];
+ cset[j] = bs[j];
+ }
+ bc += bpl;
+ bs += bpl;
+ cclr += 2;
+ cset += 2;
+ }
+ write(cursfd, curs, sizeof curs);
+}
+
+int
+checkmouse(char *buf, int n)
+{
+ int x, y, tick, b;
+ static int lastb, lastt, lastx, lasty, lastclick;
+
+ switch(n){
+ default:
+ kwerrstr("atomouse: bad count");
+ return -1;
+
+ case 1+4*12:
+ if(buf[0] == 'r'){
+ qlock(&ql);
+ if(attachwindow(nil, nil, nil, nil) == nil) {
+ qunlock(&ql);
+ return -1;
+ }
+ _flushmemscreen(Rect(0, 0, Xsize, Ysize));
+ qunlock(&ql);
+ }
+ x = atoi(buf+1+0*12) - tiler.min.x;
+ y = atoi(buf+1+1*12) - tiler.min.y;
+ b = atoi(buf+1+2*12);
+ tick = atoi(buf+1+3*12);
+ if(b && lastb == 0){ /* button newly pressed */
+ if(b==lastclick && tick-lastt<400
+ && abs(x-lastx)<10 && abs(y-lasty)<10)
+ b |= (1<<8);
+ lastt = tick;
+ lastclick = b&0xff;
+ lastx = x;
+ lasty = y;
+ }
+ lastb = b&0xff;
+ //mouse.msec = tick;
+ mousetrack(b, x, y, 0);
+ return n;
+ }
+}
+
+static void
+plan9readmouse(void *v)
+{
+ int n;
+ char buf[128];
+
+ USED(v);
+ for(;;){
+ n = read(mousefd, buf, sizeof(buf));
+ if(n < 0) /* probably interrupted */
+ _exits(0);
+ checkmouse(buf, n);
+ }
+}
+
+static void
+plan9readkeybd(void *v)
+{
+ int n, partial;
+ char buf[32];
+ char dbuf[32 * 3]; /* overestimate but safe */
+ USED(v);
+ partial = 0;
+ for(;;){
+ n = read(keybdfd, buf + partial, sizeof(buf) - partial);
+ if(n < 0) /* probably interrupted */
+ _exits(0);
+ partial += n;
+ n = mapspecials(dbuf, buf, &partial);
+ qproduce(gkbdq, dbuf, n);
+ }
+}
+
+void
+setpointer(int x, int y)
+{
+ char buf[50];
+ int n;
+
+ if(mousefd < 0)
+ return;
+ x += tiler.min.x;
+ y += tiler.min.y;
+ n = snprint(buf, sizeof buf, "m%11d %11d ", x, y);
+ write(mousefd, buf, n);
+}
+
+/*
+ * plan9 keyboard codes; from /sys/include/keyboard.h; can't include directly
+ * because constant names clash.
+ */
+enum {
+ P9KF= 0xF000, /* Rune: beginning of private Unicode space */
+ P9Spec= 0xF800,
+ /* KF|1, KF|2, ..., KF|0xC is F1, F2, ..., F12 */
+ Khome= P9KF|0x0D,
+ Kup= P9KF|0x0E,
+ Kpgup= P9KF|0x0F,
+ Kprint= P9KF|0x10,
+ Kleft= P9KF|0x11,
+ Kright= P9KF|0x12,
+ Kdown= P9Spec|0x00,
+ Kview= P9Spec|0x00,
+ Kpgdown= P9KF|0x13,
+ Kins= P9KF|0x14,
+ Kend= KF|0x18,
+
+ Kalt= P9KF|0x15,
+ Kshift= P9KF|0x16,
+ Kctl= P9KF|0x17,
+};
+
+/*
+ * translate plan 9 special characters from s2 (of length *n) into s1;
+ * return number of chars placed into s1.
+ * any trailing incomplete chars are moved to the beginning of s2,
+ * and *n set to the number moved there.
+ */
+static int
+mapspecials(char *s1, char *s2, int *n)
+{
+ char *s, *d, *es2;
+ Rune r;
+ d = s1;
+ s = s2;
+ es2 = s2 + *n;
+ while (fullrune(s, es2 - s)) {
+ s += chartorune(&r, s);
+ switch (r) {
+ case Kshift:
+ r = LShift;
+ break;
+ case Kctl:
+ r = LCtrl;
+ break;
+ case Kalt:
+ r = LAlt;
+ break;
+ case Khome:
+ r = Home;
+ break;
+ case Kend:
+ r = End;
+ break;
+ case Kup:
+ r = Up;
+ break;
+ case Kdown:
+ r = Down;
+ break;
+ case Kleft:
+ r = Left;
+ break;
+ case Kright:
+ r = Right;
+ break;
+ case Kpgup:
+ r = Pgup;
+ break;
+ case Kpgdown:
+ r = Pgdown;
+ break;
+ case Kins:
+ r = Ins;
+ break;
+ /*
+ * function keys
+ */
+ case P9KF|1:
+ case P9KF|2:
+ case P9KF|3:
+ case P9KF|4:
+ case P9KF|5:
+ case P9KF|6:
+ case P9KF|7:
+ case P9KF|8:
+ case P9KF|9:
+ case P9KF|10:
+ case P9KF|11:
+ case P9KF|12:
+ r = (r - P9KF) + KF;
+ }
+ d += runetochar(d, &r);
+ }
+ *n = es2 - s;
+ memmove(s2, s, *n);
+ return d - s1;
+}