shithub: drawcpu

Download patch

ref: b35328f248cab12ae0760c237d1544842200510c
parent: 82dc85253c9ab0f7b9925c69e4b065ca4347ab04
author: halfwit <michaelmisch1985@gmail.com>
date: Tue Aug 26 12:11:58 EDT 2025

Updates to support rc expectations, etc

--- a/include/lib.h
+++ b/include/lib.h
@@ -27,11 +27,11 @@
 #define	nil	((void*)0)
 
 typedef unsigned char	p9_uchar;
+typedef unsigned char   p9_u8int;
 typedef unsigned int	p9_uint;
-typedef uint8_t         p9_u8int;
 typedef unsigned int	p9_ulong;
-typedef int		p9_long;
-typedef signed char	p9_schar;
+typedef int				p9_long;
+typedef signed char		p9_schar;
 typedef unsigned short	p9_ushort;
 typedef unsigned int	Rune;
 typedef unsigned int	p9_u32int;
@@ -88,8 +88,8 @@
 	RFCNAMEG	= (1<<10),
 	RFCENVG		= (1<<11),
 	RFCFDG		= (1<<12),
-	/*RFREND    - (1<<13),*/
-	/*RFNOMNT     = (1<<14),*/
+	RFREND    	= (1<<13),
+	RFNOMNT     = (1<<14),
 };
 
 /*
--- a/kern/Makefile
+++ b/kern/Makefile
@@ -18,7 +18,6 @@
 	devip-$(OS).$O\
 	devip.$O\
 	devmnt.$O\
-	devproc.$O\
 	devpipe.$O\
 	devroot.$O\
 	devssl.$O\
--- a/kern/arm.c
+++ b/kern/arm.c
@@ -111,7 +111,7 @@
 	Rn = up->R + ((instr >> 16) & 15);
 	Rd = up->R + ((instr >> 12) & 15);
     if (debug)
-        print("single: Rn=%d Rd=%d offset=%.8ux\n",  ((instr >> 16) & 15), ((instr >> 12) & 15), offset);
+        print("single: Rn=%d Rd=%d offset=%.8ux pid=%d\n",  ((instr >> 16) & 15), ((instr >> 12) & 15), offset, up->pid);
 	if((instr & (fW | fP)) == fW)
 		invalid(instr);
 	if(Rn == up->R + 15) {
@@ -161,7 +161,7 @@
     Rd = up->R + ((instr >> 12) & 15);
     Rn = up->R + ((instr >> 16) & 15);
     if(debug)
-        print("swap: Rm=%d Rd=%d Rn=%d\n", (instr & 15), ((instr >> 12) & 15), ((instr >> 16) & 15));
+        print("swap: Rm=%d Rd=%d Rn=%d pid=%d\n", (instr & 15), ((instr >> 12) & 15), ((instr >> 16) & 15), up->pid);
     if(Rm == up->R + 15 || Rd == up->R + 15 || Rn == up->R + 15)
         invalid(instr);
     addr = *Rn;
@@ -254,7 +254,7 @@
 	default: result = 0; /* never happens */
 	}
     if(debug)
-        print("alu: Rn=%d Rd=%d op=%d operand=%.8ux\n", ((instr >> 16) & 15), ((instr >> 12) & 15), ((instr >> 21) & 15), operand);
+        print("alu: Rn=%d Rd=%d op=%d operand=%.8ux pid=%d\n", ((instr >> 16) & 15), ((instr >> 12) & 15), ((instr >> 21) & 15), operand, up->pid);
 	if(instr & fS) {
 		up->CPSR &= ~FLAGS;
 		if(result == 0)
@@ -280,7 +280,7 @@
         offset |= ~((1 << 24) - 1);
     offset *= 4;
     if(debug)
-        print("branch: offset=%.8ux\n", offset);
+        print("branch: offset=%.8ux pid=%d\n", offset, up->pid);
     if(instr & fLi)
         up->R[14] = up->R[15];
     up->R[15] += offset + 4;
@@ -306,7 +306,7 @@
     Rn = up->R + ((instr >> 16) & 15);
     Rd = up->R + ((instr >> 12) & 15);
     if(debug)
-        print("halfword: Rn=%d, Rd=%d\n", ((instr >> 16) & 15), ((instr >> 12) & 15));  
+        print("halfword: Rn=%d, Rd=%d pid=%d\n", ((instr >> 16) & 15), ((instr >> 12) & 15), up->pid);  
     target = *Rn;
     if(instr & fP)
         target += offset;
@@ -337,7 +337,7 @@
         invalid(instr); 
     Rn = up->R +((instr >> 16) & 15);
     if(debug)
-        print("block: Rn=%d\n", ((instr >> 16) & 15));   
+        print("block: Rn=%d pid=%d\n", ((instr >> 16) & 15), up->pid);   
     targ = evenaddr(*Rn, 3);
     if(instr & fU) {
         for(i = 0; i < 16; i++) {
@@ -382,7 +382,7 @@
     Rn = up->R + ((instr >> 12) & 15);
     Rd = up->R + ((instr >> 16) & 15);
     if(debug)
-        print("multiply: Rm=%d Rs=%d Rn=%d Rd=%d\n", (instr & 15), ((instr >> 8) & 15), ((instr >> 12) & 15), ((instr >> 16) & 15));
+        print("multiply: Rm=%d Rs=%d Rn=%d Rd=%d pid=%d\n", (instr & 15), ((instr >> 8) & 15), ((instr >> 12) & 15), ((instr >> 16) & 15), up->pid);
     if(Rd == Rm || Rm == up->R + 15 || Rs == up->R + 15 || Rn == up->R + 15 || Rd == up->R + 15)
         invalid(instr);
     res = *Rm * *Rs;
@@ -438,7 +438,7 @@
     u32int *Rn, *Rd, *Rm, *targ, addr;
     Segment *seg; 
     if(debug)
-        print("singleex: Rd=%d, Rn=%d\n", ((instr >> 12) & 15), ((instr >> 16) & 15));
+        print("singleex: Rd=%d Rn=%d pid=%d\n", ((instr >> 12) & 15), ((instr >> 16) & 15), up->pid);
     Rd = up->R + ((instr >> 12) & 15);
     Rn = up->R + ((instr >> 16) & 15);
     if(Rd == up->R + 15 || Rn == up->R + 15)
--- a/kern/dat.h
+++ b/kern/dat.h
@@ -31,6 +31,7 @@
 typedef struct Rgrp	Rgrp;
 typedef struct RWlock	RWlock;
 typedef struct Segment  Segment;
+typedef struct Ureg		Ureg;
 typedef struct Waitq	Waitq;
 typedef struct Walkqid	Walkqid;
 typedef struct Kmesg	Kmesg;
@@ -125,7 +126,6 @@
 	NSMAX	=	1000,
 	NNOTE = 5,
 	NSLOG = 7,
-	SEGNUM = 8,
 	Nfpregs = 16,
 	NSCACHE	=	(1<<NSLOG),
 
@@ -307,6 +307,33 @@
 	Mhead	*mnthash[MNTHASH];
 };
 
+typedef struct Ureg {
+	ulong	r0;
+	ulong	r1;
+	ulong	r2;
+	ulong	r3;
+	ulong	r4;
+	ulong	r5;
+	ulong	r6;
+	ulong	r7;
+	ulong	r8;
+	ulong	r9;
+	ulong	r10;
+	ulong	r11;
+	ulong	r12;	/* sb */
+	union {
+		ulong	r13;
+		ulong	sp;
+	};
+	union {
+		ulong	r14;
+		ulong	link;
+	};
+	ulong	type;	/* of exception */
+	ulong	psr;
+	ulong	pc;	/* interrupted addr */
+} Ureg;
+
 struct Rgrp
 {
 	Ref ref;
@@ -348,13 +375,29 @@
 	NERR = 20
 };
 
-typedef uvlong	Ticks;
+/* Segment types */
+enum
+{
+	SG_TYPE		= 07,		/* Mask type of segment */
+	SG_TEXT		= 00,
+	SG_DATA		= 01,
+	SG_BSS		= 02,
+	SG_STACK	= 03,
+	SG_SHARED	= 04,
+	SG_PHYSICAL	= 05,
+	SG_FIXED	= 06,
+	SG_STICKY	= 07,
 
+	SG_RONLY	= 0040,		/* Segment is read only */
+	SG_CEXEC	= 0100,		/* Detach at exec */
+	SG_FAULT	= 0200,		/* Fault on access */
+	SG_CACHED	= 0400,		/* Normal cached memory */
+	SG_DEVICE	= 01000,	/* Memory mapped device */
+	SG_NOEXEC	= 02000,	/* No execute */
+};
+
 enum {
-	SEGTEXT,
-	SEGDATA,
-	SEGBSS,
-	SEGSTACK,
+	TSEG, DSEG, BSEG, ESEG, LSEG, SEG1, SEG2, SEG3, NSEG,
 	SEGFLLOCK = 1,
 };
 
@@ -365,6 +408,7 @@
 	Rendezvous,
 	Wakeme,
 	Dead,
+	Stopped,
 };
 
 struct Fd
@@ -381,6 +425,7 @@
 	uint	mach;
 
 	ulong	pid;
+	ulong	procmode;
 
 	Pgrp	*pgrp;		/* Process group for namespace */
 	Fgrp	*fgrp;		/* File descriptor group */
@@ -387,6 +432,7 @@
 	Rgrp	*rgrp;
 	Egrp    *egrp;
 
+	QLock	debug;
 	Lock	rlock;		/* sync sleep/wakeup with postnote */
 	Rendez	*r;		/* rendezvous point slept on */
 	Rendez	rsleep;		/* place for syssleep/debug */
@@ -411,9 +457,7 @@
 
 	u32int lladdr;		/* LL/SC emulation */
 	u32int llval;
-
 	u32int CPSR;		/* status register */
-
 	u32int FPSR;
 	double F[Nfpregs];
 	Fd	   *fd; 		/* fd for syscall emulation */
@@ -435,7 +479,10 @@
 	char oproc[1024];	/* reserved for os */
 
 	u32int R[16];		/* general purpose registers / PC (R15) */
-	Segment *S[SEGNUM];
+	Ureg    *ureg;
+	Ureg	*dbgreg;
+	Segment *seg[NSEG];
+	QLock    seglock;
 };
 
 struct Segment {
@@ -445,6 +492,8 @@
 	u32int start, size;
 	void *data;
 	Ref *dref;
+	
+	int type;
 };
 
 enum
--- a/kern/devlfd-posix.c
+++ b/kern/devlfd-posix.c
@@ -1,9 +1,9 @@
-#include	"u.h"
-#include 	<errno.h>
-#include	"lib.h"
-#include	"dat.h"
-#include	"fns.h"
-#include	"error.h"
+#include "u.h"
+#include <errno.h>
+#include "lib.h"
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
 
 #undef read
 #undef write
@@ -11,111 +11,224 @@
 Chan*
 lfdchan(void *fd)
 {
-	Chan *c;
-	
-	c = newchan();
-	c->type = devno('L', 0);
-	c->aux = fd;
-	c->path = newpath("fd");
-	c->mode = ORDWR;
-	c->qid.type = 0;
-	c->qid.path = 0;
-	c->qid.vers = 0;
-	c->dev = 0;
-	c->offset = 0;
-	return c;
+    Chan *c;
+    
+    c = newchan();
+    c->type = devno('L', 0);
+    c->aux = fd;
+    c->path = newpath("/fd");
+    c->mode = ORDWR;
+    c->qid.type = QTFILE;
+    c->qid.path = (uintptr)fd;  /* Use fd number as qid.path */
+    c->qid.vers = 0;
+    c->dev = 0;
+    c->offset = 0;
+    return c;
 }
 
 int
 lfdfd(int fd)
 {
-	return newfd(lfdchan((void*)(uintptr)fd));
+    Fgrp *fg = up->fgrp;
+    Chan *c;
+
+    /* Check if fd is valid */
+    if(fd < 0 || fd >= fg->nfd || fg->fd[fd] == nil) {
+        c = lfdchan((void*)(uintptr)fd);
+        lock(&fg->ref.lk);
+        if(fd >= fg->nfd) {
+            int newnfd = fd + DELTAFD;
+            Chan **newfd = smalloc(newnfd * sizeof(Chan*));
+            if(fg->nfd > 0)
+                memmove(newfd, fg->fd, fg->nfd * sizeof(Chan*));
+            free(fg->fd);
+            fg->fd = newfd;
+            fg->nfd = newnfd;
+        }
+        fg->fd[fd] = c;
+        if(fd > fg->maxfd)
+            fg->maxfd = fd;
+        unlock(&fg->ref.lk);
+    } else {
+        c = fg->fd[fd];
+    }
+    return newfd(c);
 }
 
+static int
+fdgen(Chan *c, char *name, Dirtab *tab, int ntab, int s, Dir *dp)
+{
+    Fgrp *fg = up->fgrp;
+    int fd;
+
+    if(s == DEVDOTDOT) {
+        devdir(c, c->qid, "#L", 0, eve, DMDIR|0775, dp);
+        return 1;
+    }
+
+    lock(&fg->ref.lk);
+    if(name) {
+        /* Look up by name (e.g., "0", "1", "2", etc.) */
+        char *end;
+        fd = strtol(name, &end, 10);
+        if(*end != '\0' || fd < 0 || fd >= fg->nfd || fg->fd[fd] == nil) {
+            unlock(&fg->ref.lk);
+            return -1;  /* Invalid or closed fd */
+        }
+    } else {
+        /* Generate by index, starting with 0, 1, 2, then others up to maxfd */
+        for(fd = 0, s = s; fd <= fg->maxfd && s >= 0; fd++) {
+            if(fd >= fg->nfd || fg->fd[fd] == nil)
+                continue;
+            if(s-- == 0)
+                break;
+        }
+        if(fd > fg->maxfd || fg->fd[fd] == nil) {
+            unlock(&fg->ref.lk);
+            return -1;
+        }
+    }
+
+    sprint(up->genbuf, "%d", fd);
+    devdir(c, fg->fd[fd]->qid, up->genbuf, 0, eve, 0666, dp);
+    unlock(&fg->ref.lk);
+    return 1;
+}
+
 static Chan*
-lfdattach(char *x)
+lfdattach(char *spec)
 {
-	USED(x);
-	
-	error(Egreg);
-	return nil;
+    Chan *c;
+
+    if(spec && *spec)
+        error(Ebadarg);
+    c = devattach('L', spec);
+    c->qid.type = QTDIR;
+    c->qid.path = 0;
+    return c;
 }
 
 static Walkqid*
 lfdwalk(Chan *c, Chan *nc, char **name, int nname)
 {
-	USED(c);
-	USED(nc);
-	USED(name);
-	USED(nname);
-	error(Egreg);
-	return nil;
+    return devwalk(c, nc, name, nname, nil, 0, fdgen);
 }
 
 static int
-lfdstat(Chan *c, uchar *dp, int n)
+lfdstat(Chan *c, uchar *db, int n)
 {
-	USED(c);
-	USED(dp);
-	USED(n);
-	error(Egreg);
-	return -1;
+    return devstat(c, db, n, nil, 0, fdgen);
 }
 
 static Chan*
 lfdopen(Chan *c, int omode)
 {
-	USED(c);
-	USED(omode);
-	error(Egreg);
-	return nil;
+    Fgrp *fg = up->fgrp;
+    int fd;
+
+    if(c->qid.type & QTDIR) {
+        if(omode != OREAD)
+            error(Eperm);
+        c->mode = openmode(omode);
+        c->flag |= COPEN;
+        c->offset = 0;
+        return c;
+    }
+
+    lock(&fg->ref.lk);
+    fd = (int)c->qid.path;  /* qid.path stores the fd number */
+    if(fd < 0 || fd >= fg->nfd || fg->fd[fd] == nil) {
+        unlock(&fg->ref.lk);
+        error(Enonexist);
+    }
+
+    /* Enforce permissions based on fd */
+    switch(fd) {
+    case 0:  /* stdin */
+        if(omode != OREAD)
+            error(Eperm);
+        break;
+    case 1:  /* stdout */
+    case 2:  /* stderr */
+        if(omode != OWRITE)
+            error(Eperm);
+        break;
+    default:
+        if((omode & (OREAD|OWRITE)) == 0)
+            error(Eperm);
+        break;
+    }
+
+    c->mode = openmode(omode);
+    c->flag |= COPEN;
+    c->offset = 0;
+    c->aux = (void*)(uintptr)fd;
+    unlock(&fg->ref.lk);
+    return c;
 }
 
 static void
 lfdclose(Chan *c)
 {
-	close((int)(uintptr)c->aux);
+    Fgrp *fg = up->fgrp;
+    int fd;
+
+    if(c->qid.type & QTDIR)
+        return;
+
+    lock(&fg->ref.lk);
+    fd = (int)c->qid.path;
+    if(fd >= 0 && fd < fg->nfd && fg->fd[fd] != nil) {
+        close(fd);
+        fg->fd[fd] = nil;
+        if(fd == fg->maxfd) {
+            while(fg->maxfd > 0 && fg->fd[fg->maxfd] == nil)
+                fg->maxfd--;
+        }
+    }
+    unlock(&fg->ref.lk);
 }
 
 static long
 lfdread(Chan *c, void *buf, long n, vlong off)
 {
-	USED(off);	/* can't pread on pipes */
+    USED(off);  /* Can't pread on pipes */
+    if(c->qid.type & QTDIR)
+        return devdirread(c, buf, n, nil, 0, fdgen);
 
-	n = read((int)(uintptr)c->aux, buf, n);
-	if(n < 0)
-		oserror();
-	return n;
+    n = read((int)(uintptr)c->aux, buf, n);
+    if(n < 0)
+        oserror();
+    return n;
 }
 
 static long
 lfdwrite(Chan *c, void *buf, long n, vlong off)
 {
-	USED(off);	/* can't pread on pipes */
-
-	n = write((int)(uintptr)c->aux, buf, n);
-	if(n < 0)
-		oserror();
-	return n;
+    USED(off);  /* Can't pwrite on pipes */
+    n = write((int)(uintptr)c->aux, buf, n);
+    if(n < 0)
+        oserror();
+    return n;
 }
 
 Dev lfddevtab = {
-	'L',
-	"lfd",
-	
-	devreset,
-	devinit,
-	devshutdown,
-	lfdattach,
-	lfdwalk,
-	lfdstat,
-	lfdopen,
-	devcreate,
-	lfdclose,
-	lfdread,
-	devbread,
-	lfdwrite,
-	devbwrite,
-	devremove,
-	devwstat,
-};
+    'L',
+    "lfd",
+    
+    devreset,
+    devinit,
+    devshutdown,
+    lfdattach,
+    lfdwalk,
+    lfdstat,
+    lfdopen,
+    devcreate,
+    lfdclose,
+    lfdread,
+    devbread,
+    lfdwrite,
+    devbwrite,
+    devremove,
+    devwstat,
+};
\ No newline at end of file
--- a/kern/devlfd-win32.c
+++ b/kern/devlfd-win32.c
@@ -1,133 +1,273 @@
-#include	<windows.h>
-#include	"u.h"
-#include	"lib.h"
-#include	"dat.h"
-#include	"fns.h"
-#include	"error.h"
+#include <windows.h>
+#include "u.h"
+#include "lib.h"
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
 
 Chan*
 lfdchan(void *fd)
 {
-	Chan *c;
-	
-	c = newchan();
-	c->type = devno('L', 0);
-	c->aux = fd;
-	c->path = newpath("fd");
-	c->mode = ORDWR;
-	c->qid.type = 0;
-	c->qid.path = 0;
-	c->qid.vers = 0;
-	c->dev = 0;
-	c->offset = 0;
-	return c;
+    Chan *c;
+    
+    c = newchan();
+    c->type = devno('L', 0);
+    c->aux = fd;
+    c->path = newpath("/fd");
+    c->mode = ORDWR;
+    c->qid.type = QTFILE;
+    c->qid.path = (uintptr)fd;  /* Use handle as qid.path (index for std handles) */
+    c->qid.vers = 0;
+    c->dev = 0;
+    c->offset = 0;
+    return c;
 }
 
 /*
- * only good for stdin/stdout/stderr
+ * Initialize or retrieve a handle, supporting stdin/stdout/stderr and additional handles
  */
 int
 lfdfd(int fd)
 {
-	HANDLE h;
+    Fgrp *fg = up->fgrp;
+    HANDLE h;
+    Chan *c;
 
-	switch(fd){
-	case 0: h = GetStdHandle(STD_INPUT_HANDLE); break;
-	case 1: h = GetStdHandle(STD_OUTPUT_HANDLE); break;
-	case 2: h = GetStdHandle(STD_ERROR_HANDLE); break;
-	default:
-		return -1;
-	}
-	if(h == INVALID_HANDLE_VALUE)
-		return -1;
-	return newfd(lfdchan((void*)h));
+    /* Get handle for standard fds or validate custom handle */
+    switch(fd) {
+    case 0: h = GetStdHandle(STD_INPUT_HANDLE); break;
+    case 1: h = GetStdHandle(STD_OUTPUT_HANDLE); break;
+    case 2: h = GetStdHandle(STD_ERROR_HANDLE); break;
+    default:
+        h = (HANDLE)(uintptr)fd;  /* Assume fd is a valid HANDLE */
+        if(h == INVALID_HANDLE_VALUE || h == NULL)
+            return -1;
+        break;
+    }
+    if(h == INVALID_HANDLE_VALUE || h == NULL)
+        return -1;
+
+    lock(&fg->ref.lk);
+    if(fd >= fg->nfd) {
+        int newnfd = fd + DELTAFD;
+        Chan **newfd = smalloc(newnfd * sizeof(Chan*));
+        if(fg->nfd > 0)
+            memmove(newfd, fg->fd, fg->nfd * sizeof(Chan*));
+        free(fg->fd);
+        fg->fd = newfd;
+        fg->nfd = newnfd;
+    }
+    if(fg->fd[fd] == nil) {
+        c = lfdchan((void*)h);
+        fg->fd[fd] = c;
+        if(fd > fg->maxfd)
+            fg->maxfd = fd;
+    } else {
+        c = fg->fd[fd];
+    }
+    unlock(&fg->ref.lk);
+    return newfd(c);
 }
 
+static int
+fdgen(Chan *c, char *name, Dirtab *tab, int ntab, int s, Dir *dp)
+{
+    Fgrp *fg = up->fgrp;
+    int fd;
+
+    if(s == DEVDOTDOT) {
+        devdir(c, c->qid, "#L", 0, eve, DMDIR|0775, dp);
+        return 1;
+    }
+
+    lock(&fg->ref.lk);
+    if(name) {
+        /* Look up by name (e.g., "0", "1", "2", etc.) */
+        char *end;
+        fd = strtol(name, &end, 10);
+        if(*end != '\0' || fd < 0 || fd >= fg->nfd || fg->fd[fd] == nil) {
+            unlock(&fg->ref.lk);
+            return -1;  /* Invalid or closed handle */
+        }
+    } else {
+        /* Generate by index, starting with 0, 1, 2, then others up to maxfd */
+        for(fd = 0, s = s; fd <= fg->maxfd && s >= 0; fd++) {
+            if(fd >= fg->nfd || fg->fd[fd] == nil)
+                continue;
+            if(s-- == 0)
+                break;
+        }
+        if(fd > fg->maxfd || fd >= fg->nfd || fg->fd[fd] == nil) {
+            unlock(&fg->ref.lk);
+            return -1;
+        }
+    }
+
+    sprint(up->genbuf, "%d", fd);
+    devdir(c, fg->fd[fd]->qid, up->genbuf, 0, eve, 0666, dp);
+    unlock(&fg->ref.lk);
+    return 1;
+}
+
 static Chan*
-lfdattach(char *x)
+lfdattach(char *spec)
 {
-	USED(x);
-	
-	error(Egreg);
-	return nil;
+    Chan *c;
+    Fgrp *fg = up->fgrp;
+
+    if(spec && *spec)
+        error(Ebadarg);
+
+    /* Ensure standard handles are initialized */
+    lock(&fg->ref.lk);
+    if(fg->nfd < 3) {
+        int newnfd = 3 + DELTAFD;
+        Chan **newfd = smalloc(newnfd * sizeof(Chan*));
+        if(fg->nfd > 0)
+            memmove(newfd, fg->fd, fg->nfd * sizeof(Chan*));
+        free(fg->fd);
+        fg->fd = newfd;
+        fg->nfd = newnfd;
+    }
+    if(fg->fd[0] == nil)
+        fg->fd[0] = lfdchan((void*)GetStdHandle(STD_INPUT_HANDLE));
+    if(fg->fd[1] == nil)
+        fg->fd[1] = lfdchan((void*)GetStdHandle(STD_OUTPUT_HANDLE));
+    if(fg->fd[2] == nil)
+        fg->fd[2] = lfdchan((void*)GetStdHandle(STD_ERROR_HANDLE));
+    if(fg->maxfd < 2)
+        fg->maxfd = 2;
+    unlock(&fg->ref.lk);
+
+    c = devattach('L', spec);
+    c->qid.type = QTDIR;
+    c->qid.path = 0;
+    return c;
 }
 
 static Walkqid*
 lfdwalk(Chan *c, Chan *nc, char **name, int nname)
 {
-	USED(c);
-	USED(nc);
-	USED(name);
-	USED(nname);
-	
-	error(Egreg);
-	return nil;
+    return devwalk(c, nc, name, nname, nil, 0, fdgen);
 }
 
 static int
-lfdstat(Chan *c, uchar *dp, int n)
+lfdstat(Chan *c, uchar *db, int n)
 {
-	USED(c);
-	USED(dp);
-	USED(n);
-	error(Egreg);
-	return -1;
+    return devstat(c, db, n, nil, 0, fdgen);
 }
 
 static Chan*
 lfdopen(Chan *c, int omode)
 {
-	USED(c);
-	USED(omode);
-	error(Egreg);
-	return nil;
+    Fgrp *fg = up->fgrp;
+    int fd;
+
+    if(c->qid.type & QTDIR) {
+        if(omode != OREAD)
+            error(Eperm);
+        c->mode = openmode(omode);
+        c->flag |= COPEN;
+        c->offset = 0;
+        return c;
+    }
+
+    lock(&fg->ref.lk);
+    fd = (int)c->qid.path;  /* qid.path stores the fd index */
+    if(fd < 0 || fd >= fg->nfd || fg->fd[fd] == nil) {
+        unlock(&fg->ref.lk);
+        error(Enonexist);
+    }
+
+    /* Enforce permissions based on fd */
+    switch(fd) {
+    case 0:  /* stdin */
+        if(omode != OREAD)
+            error(Eperm);
+        break;
+    case 1:  /* stdout */
+    case 2:  /* stderr */
+        if(omode != OWRITE)
+            error(Eperm);
+        break;
+    default:
+        if((omode & (OREAD|OWRITE)) == 0)
+            error(Eperm);
+        break;
+    }
+
+    c->mode = openmode(omode);
+    c->flag |= COPEN;
+    c->offset = 0;
+    c->aux = fg->fd[fd]->aux;  /* Use the HANDLE from the stored Chan */
+    unlock(&fg->ref.lk);
+    return c;
 }
 
 static void
 lfdclose(Chan *c)
 {
-	CloseHandle((HANDLE)c->aux);
+    Fgrp *fg = up->fgrp;
+    int fd;
+
+    if(c->qid.type & QTDIR)
+        return;
+
+    lock(&fg->ref.lk);
+    fd = (int)c->qid.path;
+    if(fd >= 0 && fd < fg->nfd && fg->fd[fd] != nil) {
+        CloseHandle((HANDLE)fg->fd[fd]->aux);
+        fg->fd[fd] = nil;
+        if(fd == fg->maxfd) {
+            while(fg->maxfd > 0 && fg->fd[fg->maxfd] == nil)
+                fg->maxfd--;
+        }
+    }
+    unlock(&fg->ref.lk);
 }
 
 static long
 lfdread(Chan *c, void *buf, long n, vlong off)
 {
-	DWORD r;
+    DWORD r;
 
-	USED(off);	/* can't pread on pipes */
-	if(!ReadFile((HANDLE)c->aux, buf, (DWORD)n, &r, NULL))
-		oserror();
-	return r;
+    USED(off);  /* Can't pread on pipes */
+    if(c->qid.type & QTDIR)
+        return devdirread(c, buf, n, nil, 0, fdgen);
+
+    if(!ReadFile((HANDLE)c->aux, buf, (DWORD)n, &r, NULL))
+        oserror();
+    return r;
 }
 
 static long
 lfdwrite(Chan *c, void *buf, long n, vlong off)
 {
-	DWORD r;
+    DWORD r;
 
-	USED(off);	/* can't pread on pipes */
-	if(!WriteFile((HANDLE)c->aux, buf, (DWORD)n, &r, NULL))
-		oserror();
-	return r;
+    USED(off);  /* Can't pwrite on pipes */
+    if(!WriteFile((HANDLE)c->aux, buf, (DWORD)n, &r, NULL))
+        oserror();
+    return r;
 }
 
 Dev lfddevtab = {
-	'L',
-	"lfd",
-	
-	devreset,
-	devinit,
-	devshutdown,
-	lfdattach,
-	lfdwalk,
-	lfdstat,
-	lfdopen,
-	devcreate,
-	lfdclose,
-	lfdread,
-	devbread,
-	lfdwrite,
-	devbwrite,
-	devremove,
-	devwstat,
-};
+    'L',
+    "lfd",
+    
+    devreset,
+    devinit,
+    devshutdown,
+    lfdattach,
+    lfdwalk,
+    lfdstat,
+    lfdopen,
+    devcreate,
+    lfdclose,
+    lfdread,
+    devbread,
+    lfdwrite,
+    devbwrite,
+    devremove,
+    devwstat,
+};
\ No newline at end of file
--- a/kern/devproc.c
+++ b/kern/devproc.c
@@ -14,13 +14,12 @@
 	Qargs,
 	Qctl,
 	Qfd,
-	Qfpregs,
-	Qkregs,
 	Qmem,
 	Qnote,
 	Qnoteid,
 	Qnotepg,
 	Qns,
+	Qppid,
 	Qproc,
 	Qregs,
 	Qsegment,
@@ -29,159 +28,210 @@
 	Qwait,
 	Qprofile,
 	Qsyscall,
+	Qwatchpt,
 };
 
-#define TYPE(x)		((x).path & 0xf)
-typedef struct Aux Aux;
+enum
+{
+	CMclose,
+	CMclosefiles,
+	CMfixedpri,
+	CMhang,
+	CMkill,
+	CMnohang,
+	CMnoswap,
+	CMpri,
+	CMprivate,
+	CMprofile,
+	CMstart,
+	CMstartstop,
+	CMstartsyscall,
+	CMstop,
+	CMwaitstop,
+	CMwired,
+	CMtrace,
+	CMinterrupt,
+	CMnointerrupt,
+};
 
-struct Aux {
-	Proc *p;
-	int fd;
-	Dirtab *d;
+enum{
+	Nevents = 0x4000,
+	Emask = Nevents - 1,
 };
 
+#define	STATSIZE	(2*28+12+9*12)
+#define REGSIZE     (15*16) /* TODO */ 
+
+#define QSHIFT 	5
+#define	QID(q)		((((ulong)(q).path)&0x0000001F)>>0)
+#define	SLOTMAX		0x4000000
+#define	SLOT(q)		(((((ulong)(q).path)>>QSHIFT)&(SLOTMAX-1))-1)
+#define	PID(q)		((q).vers)
+#define	NOTEID(q)	((q).vers)
+
+static void	procctlreq(Proc*, char*, int);
+static long	procctlmemio(Chan*, Proc*, uintptr, void*, long, int);
+static Chan*	proctext(Chan*, Proc*);
+static int	procstopped(void*);
+
+/*
+ * Status, fd, and ns are left fully readable (0444) because of their use in debugging,
+ * particularly on shared servers.
+ * Arguably, ns and fd shouldn't be readable; if you'd prefer, change them to 0000
+ */
 Dirtab procdir[] =
 {
 	"args",		{Qargs},	0,			0660,
-	"ctl",		{Qctl},		0,			0600,
+	"ctl",		{Qctl},		0,			0000,
 	"fd",		{Qfd},		0,			0444,
-	"fpregs",	{Qfpregs},	0,			0400,
-	"kregs",	{Qkregs},	18 * 4,			0400,
-	"mem",		{Qmem},		0,			0400,
+	"mem",		{Qmem},		0,			0000,
 	"note",		{Qnote},	0,			0000,
 	"noteid",	{Qnoteid},	0,			0664,
 	"notepg",	{Qnotepg},	0,			0000,
 	"ns",		{Qns},		0,			0444,
+	"ppid",		{Qppid},	0,			0444,
 	"proc",		{Qproc},	0,			0400,
-	"regs",		{Qregs},	18 * 4,			0400,
+	"regs",		{Qregs},	REGSIZE,	0000,
 	"segment",	{Qsegment},	0,			0444,
-	"status",	{Qstatus},	176,			0444,
-	"text",		{Qtext},	0,			0400,
+	"status",	{Qstatus},	STATSIZE,	0444,
+	"text",		{Qtext},	0,			0000,
 	"wait",		{Qwait},	0,			0400,
 	"profile",	{Qprofile},	0,			0400,
 	"syscall",	{Qsyscall},	0,			0400,	
+	"watchpt",	{Qwatchpt},	0,			0600,
 };
 
+static
+Cmdtab proccmd[] = {
+	CMclose,		"close",		2,
+	CMclosefiles,	"closefiles",	1,
+	CMfixedpri,		"fixedpri",		2,
+	CMhang,			"hang",			1,
+	CMnohang,		"nohang",		1,
+	CMnoswap,		"noswap",		1,
+	CMkill,			"kill",			1,
+	CMpri,			"pri",			2,
+	CMprivate,		"private",		1,
+	CMprofile,		"profile",		1,
+	CMstart,		"start",		1,
+	CMstartstop,	"startstop",	1,
+	CMstartsyscall,	"startsyscall",	1,
+	CMstop,			"stop",			1,
+	CMwaitstop,		"waitstop",		1,
+	CMwired,		"wired",		2,
+	CMtrace,		"trace",		0,
+	CMinterrupt,	"interrupt",	1,
+	CMnointerrupt,	"nointerrupt",	1,
+};
+
+/* Segment type from dat.h */
+static char *sname[]={ "Text", "Data", "Bss", "Stack", "Shared", "Phys", "Fixed", "Sticky" };
+
 static int
-procgen(Chan *c, char *n, Dirtab *d, int nd, int s, Dir *dp)
+procgen(Chan *c, char *name, Dirtab *tab, int nd, int s, Dir *dp)
 {
-	Qid q;
+	Qid qid;
 	Proc *p;
-	int t;
+	char *ename;
+	Segment *q;
+	ulong pid, path, perm, len;
 
-	q.vers = 0;
-	q.type = QTFILE;
-	
-}
+	if(s == DEVDOTDOT){
+		mkqid(&qid, Qdir, 0, QTDIR);
+		devdir(c, qid, "#p", 0, eve, 0555, dp);
+		return 1;
+	}
 
-static void
-procinit(void)
-{
+	if(c->qid.path == Qdir){
+		if(s == 0){
+			strcpy(up->genbuf, "trace");
+			mkqid(&qid, Qtrace, -1, QTFILE);
+			devdir(c, qid, up->genbuf, 0, eve, 0400, dp);
+			return 1;
+		}
 
-}
+		if(name != nil){
+			/* ignore s and use name to find pid */
+			pid = strtol(name, &ename, 10);
+			if(pid==0 || ename[0]!='\0')
+				return -1;
+			s = procindex(pid);
+			if(s < 0)
+				return -1;
+		}
+		else if(--s >= conf.nproc)
+			return -1;
 
-static char *
-readin(int pid, char *file)
-{
-	char *name, *buf;
-	int fd, rc;
-	
-	name = smprint("/proc/%d/%s", pid, file);
-	fd = open(name, OREAD);
-	if(fd < 0){
-		free(name);
-		return nil;
+		p = proctab(s);
+		if(p == nil)
+			return 0;
+		pid = p->pid;
+		if(pid == 0)
+			return 0;
+		/*
+		 * String comparison is done in devwalk so name must match its formatted pid
+		*/
+		snprint(up->genbuf, sizeof(up->genbuf), "%lud", pid);
+		if(name != nil && strcmp(name, up->genbuf) != 0)
+			return -1;
+		mkqid(&qid, (s+1)<<QSHIFT, pid, QTDIR);
+		devdir(c, qid, up->genbuf, 0, p->user, 0555, dp);
+		return 1;
 	}
-	buf = malloc(1024);
-	rc = read(fd, buf, 1023);
-	if(rc < 0){
-		free(buf);
-		free(name);
-		close(fd);
-		return nil;
+	if(c->qid.path == Qtrace){
+		strcpy(up->genbuf, "trace");
+		mkqid(&qid, Qtrace, -1, QTFILE);
+		devdir(c, qid, up->genbuf, 0, eve, 0400, dp);
+		return 1;
 	}
-	buf[rc] = 0;
-	free(name);
-	close(fd);
-	return buf;
-}
+	if(s >= nelem(procdir))
+		return -1;
+	if(tab)
+		panic("procgen");
 
-static int
-calcmem(Proc *p)
-{
-	int i, r;
-	
-	r = 0;
-	for(i = 0; i < SEGNUM; i++) {
-		if(i == SEGSTACK)
-			continue;
-		if(p->S[i] == nil)
-			continue;
-		r += p->S[i]->size;
-	}
-	r = (r + 1023) / 1024;
-	return r;
-}
+	tab = &procdir[s];
+	path = c->qid.path&~((1<<QSHIFT)-1);	/* slot component */
 
-static int
-copymem(Proc *p, char *buf, u32int addr, int len)
-{
-	int i, n, r;
+	/* p->procmode determines default mode for files in /proc */
+	p = proctab(SLOT(c->qid));
+	perm = tab->perm;
+	if(perm == 0)
+		perm = p->procmode;
+	else	/* just copy read bits */
+		perm |= p->procmode & 0444;
 
-	r = len;
-	while(len > 0) {
-		for(i = 0; i < SEGNUM; i++) {
-			if(p->S[i] == nil)
-				continue;
-			if(p->S[i]->start <= addr && p->S[i]->start + p->S[i]->size > addr)
-				break;
+	len = tab->length;
+	switch(QID(c->qid)) {
+	case Qwait:
+		len = p->nwait;	/* incorrect size, but >0 means there's something to read */
+		break;
+	case Qprofile:
+		q = p->seg[TSEG];
+		if(q != nil && q->profile != nil) {
+			len = (q->size-q->start)>>LRESPROF;
+			len *= sizeof(*q->profile);
 		}
-		if(i == SEGNUM) {
-			up->errstr = "bad arg in syscall";
-			return -1;
-		}
-		n = p->S[i]->start + p->S[i]->size - addr;
-		if(n > len)
-			n = len;
-		memcpy(buf, (char*)p->S[i]->data + addr - p->S[i]->start, n);
-		len -= n;
-		buf += n;
+		break;
+	case Qwatchpt:
+		len = lenwatchpt(p);
+		break;
 	}
-	return r;
+
+	mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE);
+	devdir(c, qid, tab->name, len, p->user, perm, dp);
+	return 1;
 }
 
-static char *
-segments(Proc *p)
+static void
+procinit(void)
 {
-	char *r, *s;
-	static char *names[] = {
-		[SEGTEXT] "Text",
-		[SEGSTACK] "Stack",
-		[SEGDATA] "Data",
-		[SEGBSS] "Bss",
-	};
-	int i;
-	
-	r = malloc(1024);
-	s = r;
-	for(i = 0; i < SEGNUM; i++) {
-		if(p->S[i] == nil)
-			continue;
-		s += sprint(s, "%-7s%c  %.8ux %.8ux %4ld\n", names[i], i == SEGTEXT ? 'R' : ' ', p->S[i]->start, p->S[i]->start + p->S[i]->size, p->S[i]->dref->ref);
-	}
-	return r;
+	return;
 }
 
 static Chan*
 procattach(char *spec)
 {
-	/*
-	req->fid->qid = (Qid) {0, 0, 0x80};
-	req->fid->aux = emallocz(sizeof(Aux));
-	((Aux *) req->fid->aux)->fd = -1;
-	req->ofcall.qid = req->fid->qid;
-	respond(req, nil);
-	*/
+	return devattach('p', spec);
 }
 
 static Walkqid*
@@ -191,209 +241,774 @@
 }
 
 static Chan*
-procopen(Chan *c, int omode)
+procopen(Chan *c, int omode0)
 {
-	Aux *a;
-	int t; 
-/*
-	t = TYPE(c->qid);
-	//a = req->fid->aux;
-	switch(t) {
+	Proc *p;
+	Chan *tc;
+	int pid;
+	int omode;
+
+	if(c->qid.type & QTDIR)
+		return devopen(c, omode0, 0, 0, procgen);
+
+	if(QID(c->qid) == Qtrace){
+		if (omode0 != OREAD || !iseve()) 
+			error(Eperm);
+		lock(&tlock);
+		if (waserror()){
+			topens--;
+			unlock(&tlock);
+			nexterror();
+		}
+		if (topens++ > 0)
+			error("already open");
+		if (tevents == nil){
+			tevents = (Traceevent*)malloc(sizeof(Traceevent) * Nevents);
+			if(tevents == nil)
+				error(Enomem);
+			tproduced = tconsumed = 0;
+		}
+		proctrace = _proctrace;
+		unlock(&tlock);
+		poperror();
+
+		c->mode = openmode(omode0);
+		c->flag |= COPEN;
+		c->offset = 0;
+		return c;
+	}
+		
+	p = proctab(SLOT(c->qid));
+	eqlock(&p->debug);
+	if(waserror()){
+		qunlock(&p->debug);
+		nexterror();
+	}
+	pid = PID(c->qid);
+	if(p == nil || p->pid != pid)
+		error(Eprocdied);
+
+	omode = openmode(omode0);
+
+	switch(QID(c->qid)){
 	case Qtext:
-		a->fd = open((char*)(a->p->path + 1), OREAD);
+		if(omode != OREAD)
+			error(Eperm);
+		nonone(p);
+		qunlock(&p->debug);
+		poperror();
+		tc = proctext(c, p);
+		tc->offset = 0;
+		cclose(c);
+		return tc;
+
+	case Qstatus:
+	case Qppid:
+	case Qproc:
+	case Qsegment:
+	case Qns:
+	case Qfd:
+		if(omode != OREAD)
+			error(Eperm);
 		break;
+
+	case Qctl:
+	case Qargs:
+	case Qwait:
+	case Qnoteid:
+		if(omode == OREAD)
+			break;
+	case Qnote:
+		if(p->kp)
+			error(Eperm);
+		break;
+
+	case Qnotepg:
+		if(p->kp || omode != OWRITE)
+			error(Eperm);
+		pid = p->noteid;
+		break;
+
+	case Qmem:
+	case Qregs:
+	case Qprofile:
+	case Qsyscall:	
+	case Qwatchpt:
+		break;
+
 	default:
-		respond(req, nil);
-		return;
+		print("procopen %#lux\n", QID(c->qid));
+		error(Egreg);
 	}
-*/
-}
+	nonone(p);
 
-/*
-static int
-procgen(int n, Dir *d, void *)
-{
-	int i;
-	Proc *p;
+	/* Affix pid to qid */
+	if(pid == 0)
+		error(Eprocdied);
+	c->qid.vers = pid;
+
+	tc = devopen(c, omode, 0, 0, procgen);
+	if(waserror()){
+		cclose(tc);
+		nexterror();
+	}
 	
-	p = &plist;
-	for(i = 0;; i++) {
-		p = p->next;
-		if(p == &plist)
-			return -1;
-		if(i == n)
-			break;
+	switch(QID(c->qid)){
+	case Qwatchpt:
+		if((omode0 & OTRUNC) != 0)
+			clearwatchpt(p);
+		break;
 	}
-	d->uid = estrdup9p(luser);
-	d->gid = estrdup9p(luser);
-	d->muid = estrdup9p(luser);
-	d->name = smprint("%d", p->pid);
-	d->mode = DMDIR | 0555;
-	d->qid = (Qid) {p->pid * NQid, 0, 0x80};
-	return 0;
-}
-
-static int
-procsubgen(int n, Dir *d, void *)
-{
-	Dirtab *di;
 	
-	if(n >= nelem(procdir) - 1)
-		return -1;
-	
-	di = procdir + n;
-	d->uid = estrdup9p(luser);
-	d->gid = estrdup9p(luser);
-	d->muid = estrdup9p(luser);
-	d->name = estrdup9p(di->name);
-	d->mode = di->perm;
-	d->length = di->length;
-	d->qid = di->qid;
-	return 0;
-}
-*/
+	poperror();
+	qunlock(&p->debug);
+	poperror();
 
-static Block*
-procbread(Chan *c, long n, ulong o)
-{
-	//
+	return tc;
 }
 
 static long
-procread(Chan *c, void *a, long n, vlong offset)
+procread(Chan *c, void *va, long n, vlong off)
 {
+	char statbuf[1024], *sps;
+	ulong offset;
+	int i, j, rsize;
+	uchar *rptr;
+	uintptr addr;
+	Segment *s;
+	Waitq *wq;
 	Proc *p;
-	char *buf;
-	int rc;
+	
+	offset = off;
+	if(c->qid.type & QTDIR)
+		return devdirread(c, va, n, 0, 0, procgen);
 
-	/*
-	a = req->fid->aux;
-	if(a == nil) {
-		respond(req, "the front fell off");
-		return;
+	if(QID(c->qid) == Qtrace){
+		int navail, ne;
+
+		if(!eventsavailable(nil))
+			return 0;
+
+		rptr = (uchar*)va;
+		navail = tproduced - tconsumed;
+		if(navail > n / sizeof(Traceevent))
+			navail = n / sizeof(Traceevent);
+		while(navail > 0) {
+			ne = ((tconsumed & Emask) + navail > Nevents)? 
+					Nevents - (tconsumed & Emask): navail;
+			memmove(rptr, &tevents[tconsumed & Emask], 
+					ne * sizeof(Traceevent));
+
+			tconsumed += ne;
+			rptr += ne * sizeof(Traceevent);
+			navail -= ne;
+		}
+		return rptr - (uchar*)va;
 	}
-	if(req->fid->qid.path == 0) {
-		dirread9p(req, procgen, nil);
-		respond(req, nil);
-		return;
+
+	p = proctab(SLOT(c->qid));
+	if(p->pid != PID(c->qid))
+		error(Eprocdied);
+
+	switch(QID(c->qid)){
+	case Qmem:
+		addr = off2addr(off);
+		if(addr < KZERO)
+			return procctlmemio(c, p, addr, va, n, 1);
+
+		if(!iseve() || poolisoverlap(secrmem, (uchar*)addr, n))
+			error(Eperm);
+
+		// validate kernel addresses
+		if(addr < (uintptr)end) {
+			if(addr+n > (uintptr)end)
+				n = (uintptr)end - addr;
+			memmove(va, (char*)addr, n);
+			return n;
+		}
+		for(i=0; i<nelem(conf.mem); i++){
+			Confmem *cm = &conf.mem[i];
+			// klimit-1 because klimit might be zero!
+			if(cm->kbase <= addr && addr <= cm->klimit-1){
+				if(addr+n >= cm->klimit-1)
+					n = cm->klimit - addr;
+				memmove(va, (char*)addr, n);
+				return n;
+			}
+		}
+		
+		error(Ebadarg);
+
+	case Qctl:
+		return readnum(offset, va, n, p->pid, NUMSIZE);
+
+	case Qnoteid:
+		return readnum(offset, va, n, p->noteid, NUMSIZE);
+
+	case Qppid:
+		return readnum(offset, va, n, p->parentpid, NUMSIZE);
+
+	case Qprofile:
+		s = p->seg[TSEG];
+		if(s == nil || s->profile == nil)
+			error("profile is off");
+		i = (s->size-s->start)>>LRESPROF;
+		i *= sizeof(s->profile[0]);
+		if(i < 0 || offset >= i)
+			return 0;
+		if(offset+n > i)
+			n = i - offset;
+		memmove(va, ((char*)s->profile)+offset, n);
+		return n;
+
+	case Qproc:
+		rptr = (uchar*)p;
+		rsize = sizeof(Proc);
+	regread:
+		if(rptr == nil)
+			error(Enoreg);
+		if(offset >= rsize)
+			return 0;
+		if(offset+n > rsize)
+			n = rsize - offset;
+		memmove(va, rptr+offset, n);
+		return n;
+
+	case Qregs:
+		rptr = (uchar*)p->dbgreg;
+		rsize = REGSIZE;
+		goto regread;
+
+	case Qstatus:
+		sps = p->psstate;
+		if(sps == nil)
+			sps = statename[p->state];
+		/* NOTE: We don't have any p->time tracking
+		         this is handled by the pthreads/windows
+		j = snprint(statbuf, sizeof(statbuf),
+			"%-27s %-27s %-11s "
+			"%11lud %11lud %11lud "
+			"%11lud %11lud %11lud "
+			"%11lud %11lud %11lud\n",
+			p->text, p->user, sps,
+			tk2ms(p->time[TUser]),
+			tk2ms(p->time[TSys]),
+			tk2ms(MACHP(0)->ticks - p->time[TReal]),
+			tk2ms(p->time[TCUser]),
+			tk2ms(p->time[TCSys]),
+			tk2ms(p->time[TCReal]),
+			(ulong)(procpagecount(p)*BY2PG/1024),
+			p->basepri, p->priority);
+		*/
+	statbufread:
+		if(offset >= j)
+			return 0;
+		if(offset+n > j)
+			n = j - offset;
+		memmove(va, statbuf+offset, n);
+		return n;
+
+	case Qsegment:
+		j = 0;
+		for(i = 0; i < NSEG; i++) {
+			s = p->seg[i];
+			if(s == nil)
+				continue;
+			j += snprint(statbuf+j, sizeof(statbuf)-j,
+				"%-6s %c%c %8p %8p %4ld\n",
+				sname[s->type&SG_TYPE],
+				s->type&SG_FAULT ? 'F' : (s->type&SG_RONLY ? 'R' : ' '),
+				s->profile ? 'P' : ' ',
+				s->start, s->size, s->ref);
+		}
+		goto statbufread;
+
+	case Qwait:
+		if(!canqlock(&p->qwaitr))
+			error(Einuse);
+
+		if(waserror()) {
+			qunlock(&p->qwaitr);
+			nexterror();
+		}
+
+		lock(&p->exl);
+		while(p->waitq == nil && p->pid == PID(c->qid)) {
+			if(up == p && p->nchild == 0) {
+				unlock(&p->exl);
+				error(Enochild);
+			}
+			unlock(&p->exl);
+			sleep(&p->waitr, prochaswaitq, c);
+			lock(&p->exl);
+		}
+		if(p->pid != PID(c->qid)){
+			unlock(&p->exl);
+			error(Eprocdied);
+		}
+		wq = p->waitq;
+		p->waitq = wq->next;
+		p->nwait--;
+		unlock(&p->exl);
+
+		qunlock(&p->qwaitr);
+		poperror();
+
+		j = snprint(statbuf, sizeof(statbuf), "%d %lud %lud %lud %q",
+			wq->w.pid,
+			wq->w.time[TUser], wq->w.time[TSys], wq->w.time[TReal],
+			wq->w.msg);
+		free(wq);
+		offset = 0;
+		goto statbufread;
 	}
-	p = a->p;
+
+	eqlock(&p->debug);
+	if(waserror()){
+		qunlock(&p->debug);
+		nexterror();
+	}
+	if(p->pid != PID(c->qid))
+		error(Eprocdied);
+
+	switch(QID(c->qid)){
+	case Qns:
+	case Qfd:
+		if(offset == 0 || offset != c->mrock)
+			c->nrock = c->mrock = 0;
+		do {
+			if(QID(c->qid) == Qns)
+				j = readns1(c, p, statbuf, sizeof(statbuf));
+			else
+				j = readfd1(c, p, statbuf, sizeof(statbuf));
+			if(j == 0)
+				break;
+			c->mrock += j;
+		} while(c->mrock <= offset);
+		i = c->mrock - offset;
+		qunlock(&p->debug);
+		poperror();
+		if(i <= 0 || i > j)
+			return 0;
+		if(i < n)
+			n = i;
+		offset = j - i;
+		goto statbufread;
 	
-	switch((int)(req->fid->qid.path % NQid)) {
-	case Qdir:
-		dirread9p(req, procsubgen, nil);
-		respond(req, nil);
+	case Qargs:
+		j = procargs(p, statbuf, sizeof(statbuf));
+		qunlock(&p->debug);
+		poperror();
+		goto statbufread;
+
+	case Qwatchpt:
+		j = readwatchpt(p, statbuf, sizeof(statbuf));
+		qunlock(&p->debug);
+		poperror();
+		goto statbufread;
+
+	case Qsyscall:
+		if(p->syscalltrace != nil)
+			n = readstr(offset, va, n, p->syscalltrace);
+		else
+			n = 0;
 		break;
-	case Qstatus:
-		buf = readin(p->pid, "status");
-		if(buf == nil)
-			responderror(req);
+
+	case Qnote:
+		if(n < 1)	/* must accept at least the '\0' */
+			error(Etoosmall);
+		if(p->nnote == 0)
+			n = 0;
 		else {
-			memset(buf, ' ', 27);
-			memcpy(buf, p->name, strlen(p->name));
-			sprint(buf + 149, "%d", calcmem(p));
-			buf[strlen(buf)] = ' ';
-			readstr(req, buf);
-			free(buf);
-			respond(req, nil);
+			assert(p->note[0] != nil);
+			i = strlen(p->note[0]->msg) + 1;
+			if(i < n)
+				n = i;
+			memmove(va, p->note[0]->msg, n-1);
+			((char*)va)[n-1] = '\0';
+			freenote(p->note[0]);
+			if(--p->nnote == 0)
+				p->notepending = 0;
+			else
+				memmove(&p->note[0], &p->note[1], p->nnote*sizeof(Note*));
+			p->note[p->nnote] = nil;
 		}
 		break;
-	case Qsegment:
-		buf = segments(p);
-		readstr(req, buf);
-		free(buf);
-		respond(req, nil);
+
+	default:
+		print("unknown qid in procwread\n");
+		error(Egreg);
+	}
+
+	qunlock(&p->debug);
+	poperror();
+	return n;
+}
+
+static long
+procwrite(Chan *c, void *va, long n, vlong off)
+{
+	char buf[ERRMAX];
+	ulong offset;
+	uchar *rptr;
+	Proc *p;
+
+	offset = off;
+	if(c->qid.type & QTDIR)
+		error(Eisdir);
+
+	/* use the remembered noteid in the channel qid */
+	if(QID(c->qid) == Qnotepg) {
+		if(n >= sizeof(buf))
+			error(Etoobig);
+		memmove(buf, va, n);
+		buf[n] = 0;
+		postnotepg(NOTEID(c->qid), buf, NUser);
+		return n;
+	}
+
+	p = proctab(SLOT(c->qid));
+	if(p->pid != PID(c->qid))
+		error(Eprocdied);
+
+	switch(QID(c->qid)){
+	case Qargs:
+		if(offset != 0 || n >= sizeof(buf))
+			error(Etoobig);
+		memmove(buf, va, n);
+		buf[n] = 0;
+		kstrdup(&p->args, buf);
+		p->nargs = 0;
+		p->setargs = 1;
 		break;
-	case Qtext:
-		rc = pread(a->fd, req->ofcall.data, req->ifcall.count, req->ifcall.offset);
-		if(rc >= 0) {
-			req->ofcall.count = rc;
-			respond(req, nil);
-		} else
-			responderror(req);
-		break;
+
 	case Qmem:
-		rc = copymem(p, req->ofcall.data, req->ifcall.offset, req->ifcall.count);
-		if(rc >= 0) {
-			req->ofcall.count = rc;
-			respond(req, nil);
-		} else
-			responderror(req);
+		if(p->state != Stopped)
+			error(Ebadctl);
+		n = procctlmemio(c, p, off2addr(off), va, n, 0);
 		break;
+
 	case Qregs:
-		buf = emallocz(18 * 4);
-		memcpy(buf, p->R, 15 * 4);
-		memcpy(buf + 16 * 4, &p->CPSR, 4);
-		memcpy(buf + 17 * 4, p->R + 15, 4);
-		readbuf(req, buf, 18 * 4);
-		free(buf);
-		respond(req, nil);
+		if(offset >= REGSIZE)
+			n = 0;
+		else if(offset+n > REGSIZE)
+			n = REGSIZE - offset;
+		if(p->dbgreg == nil)
+			error(Enoreg);
+		setregisters(p->dbgreg, (char*)(p->dbgreg)+offset, va, n);
 		break;
+	case Qctl:
+		procctlreq(p, va, n);
+		break;
+
+	case Qnote:
+		if(n >= sizeof(buf))
+			error(Etoobig);
+		memmove(buf, va, n);
+		buf[n] = 0;
+		if(!postnote(p, 0, buf, NUser))
+			error("note not posted");
+		break;
+
+	case Qnoteid:
+		if(offset != 0 || n >= sizeof(buf))
+			error(Etoobig);
+		memmove(buf, va, n);
+		buf[n] = 0;
+		changenoteid(p, atoi(buf));
+		break;
+
 	default:
-		respond(req, "the front fell off");
+		print("unknown qid in procwrite\n");
+		error(Egreg);
 	}
-	*/
+	poperror();
+	return n;
 }
 
-/*
 static void
-writeto(Req *req, char *fmt, ...)
+procclose(Chan *c)
 {
-	int fd, rc;
-	va_list va;
-	char *file;
-	
-	va_start(va, fmt);
-	file = vsmprint(fmt, va);
-	va_end(va);
-	fd = open(file, OWRITE);
-	free(file);
-	if(fd < 0) {
-		responderror(req);
+	Segio *sio;
+
+	if((c->flag & COPEN) == 0)
 		return;
+
+	switch(QID(c->qid)){
+	case Qtrace:
+		lock(&tlock);
+		if(topens > 0)
+			topens--;
+		if(topens == 0)
+			proctrace = nil;
+		unlock(&tlock);
+		return;
+	case Qmem:
+		sio = c->aux;
+		if(sio != nil){
+			c->aux = nil;
+			segio(sio, nil, nil, 0, 0, 0);
+			free(sio);
+		}
+		return;
 	}
-	rc = write(fd, req->ifcall.data, req->ifcall.count);
-	req->ofcall.count = rc;
-	if(rc < req->ifcall.count)
-		responderror(req);
-	else
-		respond(req, nil);
-	close(fd);
 }
-*/
 
-static long
-procbwrite(Chan *c, Block *b, ulong o)
+static int
+procwstat(Chan *c, uchar *db, int n)
 {
-	//
+	Dir *d;
+	Proc *p;
+
+	if(c->qid.type&QTDIR)
+		error(Eperm);
+
+	switch(QID(c->qid)){
+	case Qnotepg:
+	case Qtrace:
+		return devwstat(c, db, n);
+	}
+		
+	d = smalloc(sizeof(Dir)+n);
+	if(waserror()){
+		free(d);
+		nexterror();
+	}
+	n = convM2D(db, n, &d[0], (char*)&d[1]);
+	if(n == 0)
+		error(Eshortstat);
+
+	p = proctab(SLOT(c->qid));
+	eqlock(&p->debug);
+	if(waserror()){
+		qunlock(&p->debug);
+		nexterror();
+	}
+
+	if(p->pid != PID(c->qid))
+		error(Eprocdied);
+
+	nonone(p);
+	if(strcmp(up->user, p->user) != 0 && !iseve())
+		error(Eperm);
+
+	if(!emptystr(d->uid) && strcmp(d->uid, p->user) != 0){
+		if(strcmp(d->uid, "none") != 0 && !iseve())
+			error(Eperm);
+		kstrdup(&p->user, d->uid);
+	}
+	/* p->procmode determines default mode for files in /proc */
+	if(d->mode != ~0UL)
+		p->procmode = d->mode&0777;
+
+	qunlock(&p->debug);
+	poperror();
+	poperror();
+	free(d);
+	return n;
 }
 
-static long
-procwrite(Chan *c, void *va, long n, vlong offset)
+static int
+procstat(Chan *c, uchar *db, int n)
 {
-	/*
-	switch((int)(req->fid->qid.path % NQid)) {
-	case Qnote:
-		writeto(req, "/proc/%lld/note", req->fid->qid.path / NQid);
-		break;
-	default:
-		respond(req, "the front fell off");
-	}
-	*/
+	return devstat(c, db, n, nil, 0, procgen);
 }
 
+
 static void
-procclose(Chan *c)
+procctlreq(Proc *p, char *va, int n)
 {
-	// Any that we care about on close, basically
+	Segment *s;
+	uintptr npc;
+	int pri;
+	Cmdbuf *cb;
+	Cmdtab *ct;
+	vlong time;
+	char *e;
+	void (*pt)(Proc*, int, vlong);
+
+	cb = parsecmd(va, n);
+	if(waserror()){
+		free(cb);
+		nexterror();
+	}
+
+	ct = lookupcmd(cb, proccmd, nelem(proccmd));
+
+	switch(ct->index){
+	case CMclose:
+		procctlclosefiles(p, 0, atoi(cb->f[1]));
+		break;
+	case CMclosefiles:
+		procctlclosefiles(p, 1, 0);
+		break;
+	case CMhang:
+		p->hang = 1;
+		break;
+	case CMkill:
+		killproc(p, Proc_exitme);
+		break;
+	case CMnohang:
+		p->hang = 0;
+		break;
+	case CMnoswap:
+		p->noswap = 1;
+		break;
+	case CMpri:
+		pri = atoi(cb->f[1]);
+		if(pri > PriNormal && !iseve())
+			error(Eperm);
+		procpriority(p, pri, 0);
+		break;
+	case CMfixedpri:
+		pri = atoi(cb->f[1]);
+		if(pri > PriNormal && !iseve())
+			error(Eperm);
+		procpriority(p, pri, 1);
+		break;
+	case CMprivate:
+		p->privatemem = 1;
+		/*
+		 * pages will now get marked private
+		 * when faulted in for writing
+		 * so force a tlb flush.
+		 */
+		p->newtlb = 1;
+		if(p == up)
+			flushmmu();
+		break;
+	case CMprofile:
+		s = p->seg[TSEG];
+		if(s == nil || (s->type&SG_TYPE) != SG_TEXT)	/* won't expand */
+			error(Egreg);
+		eqlock(s);
+		npc = (s->size-s->start)>>LRESPROF;
+		if(s->profile == nil){
+			s->profile = malloc(npc*sizeof(*s->profile));
+			if(s->profile == nil){
+				qunlock(s);
+				error(Enomem);
+			}
+		} else {
+			memset(s->profile, 0, npc*sizeof(*s->profile));
+		}
+		qunlock(s);
+		break;
+	case CMstart:
+		if(p->state != Stopped)
+			error(Ebadctl);
+		ready(p);
+		break;
+	case CMstartstop:
+		if(p->state != Stopped)
+			error(Ebadctl);
+		p->procctl = Proc_traceme;
+		ready(p);
+		procstopwait(p, Proc_traceme);
+		break;
+	case CMstartsyscall:
+		if(p->state != Stopped)
+			error(Ebadctl);
+		p->procctl = Proc_tracesyscall;
+		ready(p);
+		procstopwait(p, Proc_tracesyscall);
+		break;
+	case CMstop:
+		procstopwait(p, Proc_stopme);
+		break;
+	case CMwaitstop:
+		procstopwait(p, 0);
+		break;
+	case CMwired:
+		procwired(p, atoi(cb->f[1]));
+		break;
+	case CMtrace:
+		switch(cb->nf){
+		case 1:
+			p->trace ^= 1;
+			break;
+		case 2:
+			p->trace = (atoi(cb->f[1]) != 0);
+			break;
+		default:
+			error("args");
+		}
+		break;
+	case CMinterrupt:
+		procinterrupt(p);
+		break;
+	case CMnointerrupt:
+		if(p->nnote == 0)
+			p->notepending = 0;
+		else
+			error("notes pending");
+		break;
+	}
+
+	poperror();
+	free(cb);
 }
 
 static int
-procstat(Chan *c, uchar *db, int n)
+procstopped(void *a)
 {
-	return devstat(c, db, n, nil, 0, procgen);
+	return ((Proc*)a)->state == Stopped;
 }
 
+
+static long
+procctlmemio(Chan *c, Proc *p, uintptr offset, void *a, long n, int read)
+{
+	Segment *s;
+	int i;
+
+	eqlock(&p->seglock);
+	if(waserror()) {
+		qunlock(&p->seglock);
+		nexterror();
+	}
+	if(p->state <= New || p->pid != PID(c->qid))
+		error(Eprocdied);
+	s = seg(p, offset, 1);
+	if(s == nil)
+		error(Ebadarg);
+	if(waserror()){
+		qunlock(s);
+		nexterror();
+	}
+	for(i = 0; i < NSEG; i++) {
+		if(p->seg[i] == s)
+			break;
+	}
+	if(i == NSEG)
+		error(Egreg);	/* segment gone */
+	if(!read && (s->type&SG_TYPE) == SG_TEXT) {
+		p->seg[i] = txt2data(s);
+		qunlock(s);
+		putseg(s);
+		s = p->seg[i];
+	} else {
+		qunlock(s);
+	}
+	poperror();
+	incref(s);		/* for us while we copy */
+	qunlock(&p->seglock);
+	poperror();
+
+	if(waserror()) {
+		putseg(s);
+		nexterror();
+	}
+	offset -= s->base;
+	putseg(s);
+	poperror();
+
+	if(!read)
+		p->newtlb = 1;
+
+	return n;
+}
+
 Dev procdevtab = {
 	'P',
 	"proc",
@@ -408,9 +1023,8 @@
 	devcreate,
 	procclose,
 	procread,
-	procbread,
-	procwrite,
-	procbwrite,
+	devbread,
+	devbwrite,
 	devremove,
-	devwstat,
+	procwstat,
 };
--- a/kern/devroot.c
+++ b/kern/devroot.c
@@ -111,10 +111,12 @@
 static void
 rootreset(void)
 {
+	addrootdir("arm");
 	addrootdir("bin");
 	addrootdir("dev");
 	addrootdir("env");
 	addrootdir("fd");
+	addrootdir("rc");
 	addrootdir("net");
 	addrootdir("net.alt");
 	addrootdir("proc");
--- a/kern/devtab.c
+++ b/kern/devtab.c
@@ -16,7 +16,7 @@
 extern Dev lfddevtab;
 extern Dev cmddevtab;
 extern Dev envdevtab;
-extern Dev procdevtab;
+//extern Dev procdevtab;
 
 Dev *devtab[] = {
 	&rootdevtab,
@@ -31,7 +31,7 @@
 	&lfddevtab,
 	&cmddevtab,
 	&envdevtab,
-	&procdevtab,
+//	&procdevtab,
 	0
 };
 
--- a/kern/fns.h
+++ b/kern/fns.h
@@ -58,7 +58,9 @@
 Dir*		dirchanstat(Chan*);
 void		drawcmap(void);
 void        donote(char *, ulong);
+void        dump(void);
 Fgrp*		dupfgrp(Fgrp*);
+Segment*    dupseg(Segment**, int, int);
 int		    emptystr(char*);
 void		envcpy(Egrp*, Egrp*);
 int	    	eqchan(Chan*, Chan*, int);
@@ -67,6 +69,8 @@
 u32int      evenaddr(u32int,u32int);
 void		exhausted(char*);
 void		exit(int);
+Fd*         copyfd(Fd*);
+void        fddecref(Fd*);
 void        fdclose(int, int);
 Chan*		fdtochan(int, int, int, int);
 void        fpatransfer(u32int);
--- a/kern/procinit.c
+++ b/kern/procinit.c
@@ -14,6 +14,7 @@
 	p->fgrp = dupfgrp(nil);
 	p->rgrp = newrgrp();
 	p->pgrp = newpgrp();
+	p->egrp = newegrp();
 	_setproc(p);
 
 	up->slash = namec("#/", Atodir, 0, 0);
@@ -60,6 +61,9 @@
 	p->fgrp = up->fgrp;
 	if(p->fgrp != nil)
 		incref(&p->fgrp->ref);
+	p->egrp = up->egrp;
+	if(p->egrp != nil)
+		incref(&p->egrp->ref);
 	strecpy(p->text, p->text+sizeof p->text, name);
 
 	osproc(p);
--- a/kern/seg.c
+++ b/kern/seg.c
@@ -8,7 +8,7 @@
 {
 	Segment *s;
 
-	if(debug)	
+	if(debug > 1)	
 		print("newseg: size=%.8ux start=%.8ux idx=%d\n", size, start, idx);
 	s = malloc(sizeof *s);
 	if(s == nil)
@@ -21,9 +21,22 @@
 	memset(s->dref, 0, sizeof(Ref));
 	incref(s->dref);
 	s->data = s->dref + 1;
-	if(idx == SEGBSS)
+	if(idx == BSEG)
 		s->flags = SEGFLLOCK;
-	up->S[idx] = s;
+	if(idx == TSEG)
+		s->type = SG_TEXT | SG_RONLY;
+	else if(idx == DSEG)
+		s->type = SG_DATA;
+	else if(idx == BSEG)
+		s->type = SG_BSS;
+	else if(idx == ESEG)
+		s->type = SG_STACK;
+	else
+		panic("newseg: unexpected index %d", idx);
+	if(debug > 1)
+		print("newseg: created idx=%d type=%d start=%.8ux size=%.8ux\n",
+		      idx, s->type, s->start, s->size);
+	up->seg[idx] = s;
 	return s;
 }
 
@@ -32,12 +45,12 @@
 {
 	Segment **s, *ss;
 	
-	for(s = up->S; s < up->S + SEGNUM; s++) {
+	for(s = up->seg; s < up->seg + NSEG; s++) {
 		if(*s == nil)
 			continue;
 		ss = *s;
-		if(decref((*s)->dref) == 0)
-			free((*s)->dref);
+		if(decref(ss->dref) == 0)
+			free(ss->dref);
 		if(decref(&ss->ref) == 0)
 			free(*s);
 		*s = nil;
@@ -47,41 +60,117 @@
 void *
 vaddr(u32int addr, u32int len, Segment **seg)
 {
-    Segment **ss, *s;
+	Segment **ss, *s;
 	int count = 0;
-
-    for (ss = up->S; ss < up->S + SEGNUM; ss++) {
+	
+	qlock(&up->seglock);
+	for (ss = up->seg; ss < up->seg + NSEG; ss++) {
 		count++;
-        if (*ss == nil)
-            continue;
-        s = *ss;
-        if (addr >= s->start && addr < s->start + s->size) {
-            if (addr + len > s->start + s->size) 
-                break;
-            if (s->flags & SEGFLLOCK)
-                rlock(&s->rw);
-            *seg = s;
-			if(debug)
-				print("vaddr addr=%.8ux, (%d), PC=%.8ux index=%.8ux segment=%d\n", addr, len, up->R[15], s->start, count);
-            return (char *)s->data + (addr - s->start);
-        }
-    }
-    panic("segfault addr=%.8ux, (%d), PC=%.8ux", addr, len, up->R[15]);
-    return nil;
+		if (*ss == nil)
+			continue;
+		s = *ss;
+		if (addr >= s->start && addr < s->start + s->size) {
+			if (addr + len > s->start + s->size) 
+				break;
+			if (s->flags & SEGFLLOCK)
+				rlock(&s->rw);
+			*seg = s;
+			if(debug > 1)
+				print("vaddr addr=%.8ux, len=%d, PC=%.8ux, index=%.8ux, segment=%d, type=%d, pid=%d\n",
+				      addr, len, up->R[15], s->start, count - 1, s->type, up->pid);
+			qunlock(&up->seglock);
+			return (char *)s->data + (addr - s->start);
+		}
+	}
+	qunlock(&up->seglock);
+	panic("segfault addr=%.8ux, len=%d, PC=%.8ux, pid=%d\n", addr, len, up->R[15], up->pid);
+	return nil;
 }
 
+Segment*
+data2txt(Segment *s)
+{
+	Segment *ps;
+	
+	if(debug > 1)
+		print("data2txt: converting start=%.8ux size=%.8ux\n", s->start, s->size);
+	ps = newseg(s->start, s->size, TSEG);
+	ps->dref = s->dref;
+	memcpy(ps->data, s->data, s->size);
+	return ps;
+}
+
+Segment *
+dupseg(Segment **seg, int segno, int share)
+{
+	Segment *n, *s;
+
+	if(segno < 0 || segno >= NSEG)
+		panic("dupseg: invalid segment index %d", segno);
+	s = seg[segno];
+	if(s == nil) {
+		if(debug > 1)
+			print("dupseg: null segment for segno=%d, skipping\n", segno);
+		return nil;
+	}
+	if(debug > 1)
+		print("dupseg: segno=%d, type=%d, start=%.8ux, size=%.8ux, share=%d\n", segno, s->type, s->start, s->size, share);
+	qlock(&s->rw);
+	if(waserror()) {
+		qunlock(&s->rw);
+		nexterror();
+	}
+	switch(s->type&SG_TYPE) {
+	case SG_TEXT:		/* New segment shares pte set */
+	case SG_SHARED:
+	case SG_PHYSICAL:
+	case SG_FIXED:
+	case SG_STICKY:
+	default:
+		goto sameseg;
+	case SG_STACK:
+    	n = newseg(s->start, s->size, s->type);
+    	memcpy(n->data, s->data, s->size);
+    	break;
+	case SG_BSS:		/* Just copy on write */
+		if(share)
+			goto sameseg;
+		n = newseg(s->start, s->size, s->type);
+		break;
+	case SG_DATA:		/* Copy on write plus demand load info */
+		if(segno == TSEG){
+			n = data2txt(s);
+			qunlock(s);
+			poperror();
+			return n;
+		}
+		if(share)
+			goto sameseg;
+		n = newseg(s->start, s->size, s->type);
+		memcpy(n->dref, s->dref, s->size);
+		break;
+	}
+	qunlock(s);
+	poperror();
+	return n;
+sameseg:
+	incref(s);
+	qunlock(s);
+	poperror();
+	return s;
+}
+
 void*
 vaddrnol(u32int addr, u32int len)
 {
-    Segment *seg;
+	Segment *seg;
 	void *ret;
 	
-    ret = vaddr(addr, len, &seg);
+	ret = vaddr(addr, len, &seg);
 	segunlock(seg);
 	return ret;
 }
 
-/* might be made a macro for hurr durr performance */
 void
 segunlock(Segment *s)
 {
@@ -146,4 +235,4 @@
 	memmove(targ, data, len);
 	segunlock(seg);
 	free(data);
-}
\ No newline at end of file
+}
--- a/kern/syscall.c
+++ b/kern/syscall.c
@@ -300,11 +300,11 @@
 	v = v + 7 & -8;
 	if(debug)
 		print("sysbrk v=%#lux\n", v);
-	if(v >= up->S[SEGSTACK]->start)
+	if(v >= up->seg[ESEG]->start)
 		panic("bss > stack, wtf?");
-	if(v < up->S[SEGBSS]->start)
+	if(v < up->seg[BSEG]->start)
 		panic("bss length < 0, wtf?");
-	s = up->S[SEGBSS];
+	s = up->seg[BSEG];
 	wlock(&s->rw);
 	s->dref = realloc(s->dref, v - s->start + sizeof(Ref));
 	if(s->dref == nil)
@@ -382,17 +382,18 @@
 	u32int flags;
 	int rc, i;
 	Segment *s, *t;
-	Proc *p;
-	if(debug)
-		print("sysrfork\n");
+	Fd *old;
 	flags = arg(0);
+	if(debug)
+		print("rfork(%#o) for proc pid=%lud\n", flags, up->pid);
 	if((flags & (RFFDG | RFCFDG)) == (RFFDG | RFCFDG) ||
 	   (flags & (RFNAMEG | RFCNAMEG)) == (RFNAMEG | RFCNAMEG) ||
 	   (flags & (RFENVG | RFCENVG)) == (RFENVG | RFCENVG)) {
-		werrstr("bad arg in rfork");
 		up->R[0] = -1;
+		cherrstr("bad arg in syscall");
 		return;
 	}
+
 	if((flags & RFPROC) == 0) {
 		if(flags & RFFDG) {
 			Fgrp *old = up->fgrp;
@@ -407,112 +408,43 @@
 		up->R[0] = noteerr(sysrfork(flags), 0);
 		return;
 	}
-	p = newproc();
-	if(p == nil) {
-		fprint(2, "rfork: malloc failed for Proc");
-		werrstr("rfork: malloc failed");
-		up->R[0] = -1;
-		return;
-	}
-	memset(p, 0, sizeof(Proc));
-	memcpy(p, up, sizeof(Proc));
-	for(i = 0; i < SEGNUM; i++) {
-		s = p->S[i];
-		if(s == nil)
-			continue;
-		if((flags & RFMEM) == 0 && i != SEGTEXT || i == SEGSTACK) {
-			t = newseg(s->start, s->size, s->flags);
-			if(t == nil) {
-				fprint(2, "rfork: newseg failed");
-				werrstr("rfork: newseg failed");
-				up->R[0] = -1;
-				return;
-			}
-			t->dref = mallocz(sizeof(Ref) + s->size, 1);
-			if(t->dref == nil) {
-				fprint(2, "rfork: malloc failed for Segment data");
-				werrstr("rfork: malloc failed");
-				up->R[0] = -1;
-				return;
-			}
-			incref(t->dref);
-			t->data = t->dref + 1;
-			memcpy(t->data, s->data, s->size);
-			p->S[i] = t;
-		} else {
-			incref(s->dref);
-			incref(&s->ref);
-		}
-	}
-	if(flags & RFFDG)
-		p->fgrp = dupfgrp(up->fgrp);
-	else if(flags & RFCFDG)
-		p->fgrp = dupfgrp(nil);
-	else
-		incref(&up->fgrp->ref);
-	if(flags & RFNAMEG)
-		p->pgrp = newpgrp();
-	else
-		incref(&up->pgrp->ref);
-	if(flags & RFENVG)
-		p->egrp = newegrp();
-	else
-		incref(&up->egrp->ref);
+
+	/* Seg could be being shared here, not useful */
 	rc = sysrfork(flags);
-	if(rc < 0) {
-		fprint(2, "rfork: sysrfork failed: %r");
-		werrstr("rfork: %r");
-		up->R[0] = -1;
-		return;
-	}
-	if(rc == 0) {
-		_setproc(p);
-		atexit((void(*)(void))pexit);
-		p->pid = getpid();
-		inittos();
-		osproc(p);
-	}
-	up->R[0] = rc;
+	if(rc < 0) /* this should NEVER happen */
+		panic("rfork failed: %r");
 }
 
 static void
 _sysexec(void)
 {
-	u32int name, argv;
-	char *namet;
-	char **argvv;
+	u32int name, argv, *argvt;
+	char *namet, **argvv;
 	int i, argc, rc;
-	Segment *seg;
-	u32int *argvt;
-
+	Segment *seg1, *seg2;
+	
 	name = arg(0);
 	argv = arg(1);
-	namet = estrdup((char*)vaddrnol(name, 0));
-	argvt = vaddr(argv, 0, &seg);
+	namet = strdup(vaddr(name, 0, &seg1));
+	segunlock(seg1);
+	argvt = vaddr(argv, 0, &seg1);
 	if(debug)
-		print("sysexec: name=%s", namet);
+		fprint(2, "exec(%#ux=\"%s\", %#ux)\n", name, namet, argv);
 	for(argc = 0; argvt[argc]; argc++)
 		;
-	argvv = mallocz(sizeof(char *) * (argc + 1), 1);
-	if(argvv == nil) {
-		fprint(2, "exec: malloc failed for argv");
-		werrstr("exec: malloc failed");
-		up->R[0] = -1;
-		free(namet);
-		return;
-	}
+	argvv = emalloc(sizeof(char *) * argc);
 	for(i = 0; i < argc; i++) {
-		argvv[i] = estrdup((char*)vaddrnol(argvt[i], 0));
+		argvv[i] = strdup(vaddr(argvt[i], 0, &seg2));
+		segunlock(seg2);
 	}
-	argvv[argc] = nil;
-	segunlock(seg);
+	segunlock(seg1);
 	rc = loadtext(namet, argc, argvv);
 	for(i = 0; i < argc; i++)
 		free(argvv[i]);
 	free(argvv);
-	free(namet);
 	if(rc < 0)
 		up->R[0] = noteerr(rc, 0);
+	free(namet);
 }
 
 static void
--- a/kern/sysproc.c
+++ b/kern/sysproc.c
@@ -12,7 +12,6 @@
 #include <sys/mman.h>
 #include <setjmp.h>
 
-#undef rfork
 #undef read
 jmp_buf exec_jmp;
 #define pgsize  0x1000
@@ -163,110 +162,6 @@
 	wunlock(&fd->rw);
 }
 
-int
-sysrfork(int flags)
-{
-    int pid, status;
-    int p[2];
-    int n;
-    char buf[128], *q;
-    extern char **environ;
-    struct sigaction oldchld;
-
-    memset(&oldchld, 0, sizeof oldchld);
-
-    if((flags&(RFPROC|RFFDG|RFMEM)) == (RFPROC|RFFDG)){
-        flags &= ~(RFPROC|RFFDG|RFENVG);
-        n = (flags & ~(RFNOTEG|RFNAMEG|RFNOWAIT|RFCENVG));
-        if(n){
-            werrstr("unknown flags %08ux in rfork", n);
-            return -1;
-        }
-        if(flags&RFNOWAIT){
-            sigaction(SIGCHLD, nil, &oldchld);
-            signal(SIGCHLD, nop);
-            if(pipe(p) < 0)
-                return -1;
-        }
-        pid = fork();
-        if(pid == -1)
-            return -1;
-        if(flags&RFNOWAIT){
-            flags &= ~RFNOWAIT;
-            if(pid){
-                close(p[1]);
-                status = 0;
-                if(wait4(pid, &status, 0, 0) < 0){
-                    werrstr("pipe dance - wait4 - %r");
-                    close(p[0]);
-                    return -1;
-                }
-                n = readn(p[0], buf, sizeof buf-1);
-                close(p[0]);
-                if(!WIFEXITED(status) || WEXITSTATUS(status)!=0 || n <= 0){
-                    if(!WIFEXITED(status))
-                        werrstr("pipe dance - !exited 0x%ux", status);
-                    else if(WEXITSTATUS(status) != 0)
-                        werrstr("pipe dance - non-zero status 0x%ux", status);
-                    else if(n < 0)
-                        werrstr("pipe dance - pipe read error - %r");
-                    else if(n == 0)
-                        werrstr("pipe dance - pipe read eof");
-                    else
-                        werrstr("pipe dance - unknown failure");
-                    return -1;
-                }
-                buf[n] = 0;
-                if(buf[0] == 'x'){
-                    werrstr("%s", buf+2);
-                    return -1;
-                }
-                pid = strtol(buf, &q, 0);
-            }else{
-                signal(SIGCHLD, SIG_IGN);
-                close(p[0]);
-                pid = fork();
-                if(pid){
-                    if(pid > 0)
-                        print("%d\n", pid);
-                    else
-                        print("x %r\n");
-                    close(p[1]);
-                    _exit(0);
-                }else{
-                    close(p[1]);
-                }
-            }
-            sigaction(SIGCHLD, &oldchld, nil);
-        }
-        if(pid != 0)
-            return pid;
-        if(flags&RFCENVG)
-            if(environ)
-                *environ = nil;
-    }
-    if(flags&RFPROC){
-        werrstr("cannot use rfork for shared memory -- use libthread");
-        return -1;
-    }
-    if(flags&RFNAMEG){
-        flags &= ~RFNAMEG;
-    }
-    if(flags&RFNOTEG){
-        setpgid(0, getpid());
-        flags &= ~RFNOTEG;
-    }
-    if(flags&RFNOWAIT){
-        werrstr("cannot use RFNOWAIT without RFPROC");
-        return -1;
-    }
-    if(flags){
-        werrstr("unknown flags %08ux in rfork", flags);
-        return -1;
-    }
-    return 0;
-}
-
 ulong
 beswal(ulong l)
 {
@@ -377,7 +272,7 @@
     
     tos = (USTKTOP & ~7) - sizeof(Tos) * 2;
     sp = tos;
-    if(debug)
+    if(debug > 1)
         print("initstack: tos=%.8ux tossz=%.8ux USTKTOP=%.8ux\n", tos, sizeof(Tos), USTKTOP);
     size = 8;
     for(i = 0; i < argc; i++)
@@ -392,7 +287,7 @@
 	*(ulong *) vaddrnol(sp, 4) = argc;
     sp += 4;
     ap = sp + (argc + 1) * 4;
-    if(debug)
+    if(debug > 1)
         print("initstack: argc=%d sp=%.8ux ap=%.8ux\n", argc, sp, ap);
     for(i = 0; i < argc; i++) {
         *(ulong *) vaddrnol(sp, 4) = ap;
@@ -402,7 +297,7 @@
         ap += len;
     }
     *(ulong *) vaddrnol(sp, 4) = 0;
-    inittos();
+	inittos();
 
 }
 
@@ -475,7 +370,7 @@
     Exec hdr;
 	char buf[2];
     int fd;
-    
+	
     fd = open(file, OREAD);
 	if(fd < 0) return -1;
 	if(pread(fd, buf, 2, 0) == 2 && buf[0] == '#' && buf[1] == '!')
@@ -501,10 +396,10 @@
     freesegs();
     memset(up->R, 0, sizeof(up->R));
     up->CPSR = 0;
-    text = newseg(txtaddr - hdrsz, txtsz + hdrsz, SEGTEXT);
-    data = newseg(dataddr, datsz, SEGDATA);
-    bss = newseg(dataddr + datsz, bsssz, SEGBSS);
-    newseg((USTKTOP & ~7) - USTKSIZE, USTKSIZE, SEGSTACK);
+    text = newseg(txtaddr - hdrsz, txtsz + hdrsz, TSEG);
+    data = newseg(dataddr, datsz, DSEG);
+    bss = newseg(dataddr + datsz, bsssz, BSEG);
+    newseg((USTKTOP & ~7) - USTKSIZE, USTKSIZE, ESEG);
     seek(fd, txtoff - hdrsz, 0);
 	if(readn(fd, text->data, txtsz + hdrsz) < txtsz + hdrsz)
 		panic("%r");
@@ -518,9 +413,198 @@
     fdclear(up->fd);
     initstack(argc, argv);
 
-    if(debug)
+    if(debug > 1)
         print("loadtext: PC=%.8ux, R1=%.8ux, R13=%.8ux\n", up->R[15], up->R[1], up->R[13]);
     
     resetvfp();
     return 0;
-}
\ No newline at end of file
+}
+
+
+void
+procrun(void*)
+{
+	for(;;) {
+		if(debug > 2)
+			dump();
+		step();
+		while((up->notein - up->noteout) % NNOTE) {
+			donote(up->notes[up->noteout % NNOTE], 0);
+			up->noteout++;
+		}
+	}
+}
+
+int
+sysrfork(int flag)
+{
+	Proc *p;
+	Fgrp *ofg;
+	Pgrp *opg;
+	Rgrp *org;
+	Egrp *oeg;
+	ulong pid;
+	char *devs;
+	int i, n;
+
+	/* Check flags before we commit */
+	if((flag & (RFFDG|RFCFDG)) == (RFFDG|RFCFDG))
+		error(Ebadarg);
+	if((flag & (RFNAMEG|RFCNAMEG)) == (RFNAMEG|RFCNAMEG))
+		error(Ebadarg);
+	if((flag & (RFENVG|RFCENVG)) == (RFENVG|RFCENVG))
+		error(Ebadarg);
+	if((flag&RFPROC) == 0) {
+		if(flag & (RFMEM|RFNOWAIT))
+			error(Ebadarg);
+		if(flag & (RFFDG|RFCFDG)) {
+			ofg = up->fgrp;
+			if(flag & RFFDG)
+				up->fgrp = dupfgrp(ofg);
+			else
+				up->fgrp = dupfgrp(nil);
+			closefgrp(ofg);
+		}
+		if(flag & (RFNAMEG|RFCNAMEG)) {
+			opg = up->pgrp;
+			up->pgrp = newpgrp();
+			if(flag & RFNAMEG)
+				pgrpcpy(up->pgrp, opg);
+			closepgrp(opg);
+		}
+		//if(flag & RFNOMNT)
+		//	devmask(up->pgrp, 1, devs);
+		if(flag & RFREND) {
+			org = up->rgrp;
+			up->rgrp = newrgrp();
+			closergrp(org);
+		}
+		if(flag & (RFENVG|RFCENVG)) {
+			oeg = up->egrp;
+			up->egrp = smalloc(sizeof(Egrp));
+			up->egrp->ref.ref = 1;
+			if(flag & RFENVG)
+				envcpy(up->egrp, oeg);
+			closeegrp(oeg);
+		}
+		if(flag & RFNOTEG){
+			qlock(&up->debug);
+			// TODO: Notegroups
+			//setnoteid(up, 0);	/* can't error() with 0 argument */
+			qunlock(&up->debug);
+		}
+		return 0;
+	}
+
+	p = newproc();
+
+	qlock(&up->debug);
+	qlock(&p->debug);
+	p->slash = cclone(up->slash);
+	p->dot = cclone(up->dot);
+	strecpy(p->text, p->text+sizeof p->text, up->text);
+	p->kp = 0;
+
+	/* TODO: Notes */
+	//p->nnote = 0;
+ 	//p->notify = up->notify;
+	//p->notified = 0;
+	//p->notepending = 0;
+	//p->lastnote = nil;
+
+	//if((flag & RFNOTEG) == 0)
+	//	p->noteid = up->noteid;
+
+	/* Copy regs over */
+	for(i = 0; i <= 15; i++)
+		p->R[i] = up->R[i];
+	p->R[0] = 0;
+	p->CPSR = up->CPSR;
+	p->FPSR = up->FPSR;
+
+	strcpy(&p->text, up->text);
+	strcpy(&p->user, up->user);
+	//strcpy(&p->args, "");
+	//p->nargs = 0;
+	//p->setargs = 0;
+	pid = getpid();
+
+	qunlock(&p->debug);
+	qunlock(&up->debug);
+	if(waserror()){
+		p->kp = 1;
+		nexterror();
+	}
+
+	n = flag & RFMEM;
+	qlock(&p->seglock);
+	if(waserror()){
+		qunlock(&p->seglock);
+		nexterror();
+	}
+
+	for(i = 0; i < NSEG; i++)
+		if(up->seg[i] != nil)
+			p->seg[i] = dupseg(up->seg, i, n);
+	qunlock(&p->seglock);
+	poperror();
+
+	if(flag & (RFFDG|RFCFDG)) {
+		if(flag & RFFDG)
+			p->fgrp = dupfgrp(up->fgrp);
+		else
+			p->fgrp = dupfgrp(nil);
+	}
+	else {
+		p->fgrp = up->fgrp;
+		incref(p->fgrp);
+	}
+
+	/* Process groups */
+	if(flag & (RFNAMEG|RFCNAMEG)) {
+		p->pgrp = newpgrp();
+		if(flag & RFNAMEG)
+			pgrpcpy(p->pgrp, up->pgrp);
+		/* inherit notallowed */
+		//memmove(p->pgrp->notallowed, up->pgrp->notallowed, sizeof p->pgrp->notallowed);
+	}
+	else {
+		p->pgrp = up->pgrp;
+		incref(p->pgrp);
+	}
+	/* TODO: devmask 
+	if(flag & RFNOMNT)
+		devmask(p->pgrp, 1, devs);
+	*/
+	if(flag & RFREND)
+		p->rgrp = newrgrp();
+	else {
+		incref(up->rgrp);
+		p->rgrp = up->rgrp;
+	}
+
+	/* Environment group */
+	if(flag & (RFENVG|RFCENVG)) {
+		p->egrp = smalloc(sizeof(Egrp));
+		p->egrp->ref.ref = 1;
+		if(flag & RFENVG)
+			envcpy(p->egrp, up->egrp);
+	}
+	else {
+		p->egrp = up->egrp;
+		incref(p->egrp);
+	}
+	p->fn = procrun;
+	p->arg = 0;
+
+	poperror();
+	if((flag&RFNOWAIT) == 0){
+		//p->parent = up;
+		//lock(&up->exl);
+		//up->nchild++;
+		//unlock(&up->exl);
+	}
+
+	osproc(p);
+	return 0;
+}
--- a/kern/vfp.c
+++ b/kern/vfp.c
@@ -19,7 +19,6 @@
 {
 	u32int *Rt;
 	double *Fn;
-
 	Rt = up->R + ((instr>>12)&0xF);
 	Fn = up->F + ((instr>>16)&0xF);
 	switch((instr>>20)&0xF){
@@ -46,7 +45,6 @@
 	int n, d, off, sz;
 	void* ea;
 	Segment *seg;
-
 	n = (instr>>16) & 0xF;
 	d = (instr>>12) & 0xF;
 	off = (instr & 0xFF) << 2;
@@ -67,6 +65,13 @@
 		else
 			up->F[d] = *(float*)ea;
 		break;
+	case 2:
+        if(sz)
+            *(double*)ea = up->F[d];
+        else
+            *(float*)ea = up->F[d];
+		up->R[n] += off;
+        break;
 	default:
 		sysfatal("unimplemented VFP instruction %8ux @ %8ux", instr, up->R[15] - 4);
 	}
--- a/main.c
+++ b/main.c
@@ -49,18 +49,6 @@
 }
 
 void
-dump(void)
-{
-	int i;
-	
-	for(i = 0; i < 16; i++) {
-		print("R%2d %.8ux", i, up->R[i]);
-		if((i % 4) == 3) print("\n");
-		else print("\t");
-	}
-}
-
-void
 notehandler(void *d, char *note)
 {
 	USED(d);
@@ -74,6 +62,17 @@
 	return;
 }
 
+void
+dump(void)
+{
+	print("R00 %.8ux\tR01 %.8ux\tR02 %.8ux\tR03 %.8ux\tpid: %d\nR04 %.8ux\tR05 %.8ux\tR06 %.8ux\tR07 %.8ux\nR08 %.8ux\tR09 %.8ux\tR10 %.8ux\tR11 %.8ux\nR12 %.8ux\tR13 %.8ux\tR14 %.8ux\tR15 %.8ux\n", 
+		up->R[0], up->R[1], up->R[2], up->R[3], up->pid,
+		up->R[4], up->R[5], up->R[6], up->R[7], 
+		up->R[8], up->R[9], up->R[10], up->R[11], 
+		up->R[12], up->R[13], up->R[14], up->R[15]
+	);
+}
+
 int
 main(int argc, char **argv)
 {
@@ -120,6 +119,9 @@
 		panic("bind #U: %r");
 	//if(bind("#P", "/proc", MBEFORE) < 0)
 	//	panic("bind #P: %r");
+	if(bind("#L", "/fd", MAFTER) < 0)
+		panic("bind #L");
+
 	bind("#A", "/dev", MAFTER);
 	bind("#N", "/dev", MAFTER);
 	bind("#C", "/", MAFTER);
@@ -127,7 +129,9 @@
 	if(bind("/root", "/", MAFTER) < 0)
 		panic("bind /root: %r");
 
-	/* RC Main */
+	/* TODO: /path/to/root, root has /rc and /arm 
+	 * Bind in /arm/bin, /rc/bin/ and /rc/lib
+	 */
 	if(rc != nil)
 		bind(rc, "/rc", MAFTER);
 
@@ -153,7 +157,7 @@
 
 	notify(notehandler);
 	for(;;) {
-		if(debug)
+		if(debug > 2)
 			dump();
 		step();
 		while((up->notein - up->noteout) % NNOTE) {
@@ -161,5 +165,6 @@
 			up->noteout++;
 		}
 	}
+
 	exits(0);
 }
--- /dev/null
+++ b/rc/lib/rcmain
@@ -1,0 +1,39 @@
+# rcmain: Plan 9 version
+if(~ $#home 0) home=/
+if(~ $#ifs 0) ifs=' 	
+'
+switch($#prompt){
+case 0
+	prompt=('% ' '	')
+case 1
+	prompt=($prompt '	')
+}
+if(~ $rcname ?.out) prompt=('broken! ' '	')
+if(flag p) path=/bin
+if not{
+	finit
+	if(~ $#path 0) path=(/bin .)
+}
+fn sigexit
+if(! ~ $#cflag 0){
+	if(flag l){
+		if(/bin/test -r /rc/lib/rcmain.local) . /rc/lib/rcmain.local
+		if(/bin/test -r $home/lib/profile) . $home/lib/profile
+	}
+	status=''
+	eval $cflag
+}
+if not if(flag i){
+	if(flag l){
+		if(/bin/test -r /rc/lib/rcmain.local) . /rc/lib/rcmain.local
+		if(/bin/test -r $home/lib/profile) . $home/lib/profile
+	}
+	status=''
+	if(! ~ $#* 0) . $*
+	. -i /fd/0
+}
+if not if(~ $#* 0) . /fd/0
+if not{
+	status=''
+	. $*
+}
\ No newline at end of file
--