code: 9ferno

Download patch

ref: 10405d31c608fe9d448607545195b758b424e70c
parent: 2a3a8ae0c8cfdc57e4c6055493cc83d60dbfdf1b
author: 9ferno <gophone2015@gmail.com>
date: Thu Jan 27 18:09:37 EST 2022

more stuff from 9front to the forth console

--- a/Inferno/amd64/include/ureg.h
+++ b/Inferno/amd64/include/ureg.h
@@ -12,8 +12,8 @@
 	u64	r11;
 	u64	r12;
 	u64	r13;
-	u64	r14;
-	u64	r15;
+	u64	r14;	/* up-> */
+	u64	r15;	/* m-> */
 
 	u16	ds;
 	u16	es;
@@ -20,8 +20,14 @@
 	u16	fs;
 	u16	gs;
 
-	u64	trap;	/* trap type */
-	u64	ecode;	/* error code (or zero) */
+	union {
+		u64	trap;	/* trap type */
+		u64 type;
+	};
+	union {
+		u64	ecode;	/* error code (or zero) */
+		u64 error;
+	};
 	u64	pc;		/* pc */
 	u64	cs;		/* old context */
 	u64	flags;	/* old flags */
--- a/os/ip/ipifc.c
+++ b/os/ip/ipifc.c
@@ -510,7 +510,7 @@
 	f = ifc->conv->p->f;
 	if(waserror()){
 		wunlock(ifc);
-		return up->env->errstr;
+		return up->errstr;
 	}
 
 	if(mtu > 0)
@@ -1574,7 +1574,7 @@
 ipifcregisteraddr(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *ip)
 {
 	if(waserror()){
-		print("ipifcregisteraddr %s %I %I: %s\n", ifc->dev, lifc->local, ip, up->env->errstr);
+		print("ipifcregisteraddr %s %I %I: %s\n", ifc->dev, lifc->local, ip, up->errstr);
 		return;
 	}
 	if(ifc->m != nil && ifc->m->areg != nil)
--- a/os/ip/plan9.c
+++ b/os/ip/plan9.c
@@ -13,17 +13,17 @@
 char*
 commonuser(void)
 {
-	return up->env->user;
+	return up->user;
 }
 
 Chan*
 commonfdtochan(int fd, int mode, int a, int b)
 {
-	return fdtochan(up->env->fgrp, fd, mode, a, b);
+	return fdtochan(up->fgrp, fd, mode, a, b);
 }
 
 char*
 commonerror(void)
 {
-	return up->env->errstr;
+	return up->errstr;
 }
--- a/os/pc/devrtc.c
+++ b/os/pc/devrtc.c
@@ -85,11 +85,11 @@
 	omode = openmode(omode);
 	switch((u64)c->qid.path){
 	case Qrtc:
-		if(strcmp(up->env->user, eve)!=0 && omode!=OREAD)
+		if(strcmp(up->user, eve)!=0 && omode!=OREAD)
 			error(Eperm);
 		break;
 	case Qnvram:
-		if(strcmp(up->env->user, eve)!=0)
+		if(strcmp(up->user, eve)!=0)
 			error(Eperm);
 	}
 	return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);
--- a/os/pc/devvga.c
+++ b/os/pc/devvga.c
@@ -270,7 +270,7 @@
 	if((u64)c->qid.path == Qvgaovlctl)
 		if(scr->dev && scr->dev->ovlctl){
 			if(waserror()){
-				print("ovlctl error: %s\n", up->env->errstr);
+				print("ovlctl error: %s\n", up->errstr);
 				return;
 			}
 			scr->dev->ovlctl(scr, c, closectl, strlen(closectl));
--- a/os/pc/ether8169.c
+++ b/os/pc/ether8169.c
@@ -795,7 +795,7 @@
 		return;
 	}
 	if(waserror()){
-		print("#l%d: rtl8169: %s\n", edev->ctlrno, up->env->errstr);
+		print("#l%d: rtl8169: %s\n", edev->ctlrno, up->errstr);
 		qunlock(&ctlr->alock);
 		nexterror();
 	}
--- a/os/pc64/fns.h
+++ b/os/pc64/fns.h
@@ -28,6 +28,7 @@
 void	dmaend(int);
 int	dmainit(int, int);
 s32	dmasetup(int, void*, s32, s32);
+void	dumpmcregs(void);
 void	dumpregs(Ureg*);
 int	ecinit(int cmdport, int dataport);
 int	ecread(uchar addr);
@@ -49,6 +50,7 @@
 u64	getcr2(void);
 u64	getcr3(void);
 u64	getcr4(void);
+u64	getdr6(void);
 char*	getconf(char*);
 void	guesscpuhz(int);
 void	mwait(void*);
@@ -204,6 +206,7 @@
 void	upafree(uintptr, u32);
 void	upareserve(uintptr, u32);
 u64	us2fastticks(u64);
+s32	userureg(Ureg*);
 void	vectortable(void);
 void*	vmap(uintptr, s32);
 void	vunmap(void*, s32);
@@ -214,7 +217,6 @@
 void	nmiscreen(void);
 int	kbdinready(void);
 
-#define	userureg(ur)	(((ur)->cs & 3) == 3)
 #define getcallerpc(x)	(((uintptr*)(x))[-1])
 #define KADDR(a)	((void*)((uintptr)(a)|KZERO))
 #define PADDR(a)	((uintptr)(a)&~(uintptr)KZERO)
--- a/os/pc64/forth.s
+++ b/os/pc64/forth.s
@@ -214,6 +214,12 @@
 	POP(TOP)
 	NEXT
 
+/*
+	TODO replace the CALL to validateaddress with a macro
+	using UM and UME masks or CMPQ with UM and UME in
+	fetch, store, cfetch and cstore to speed up these words
+	(a || UM) && ~UME
+ */
 TEXT	fetch(SB), 1, $-4	/* ( a -- n) */
 	PUSH(TOP)
 	CALL validateaddress(SB)	/* a a -- a */
--- a/os/pc64/main.c
+++ b/os/pc64/main.c
@@ -285,23 +285,23 @@
 void
 init0(void)
 {
-	Osenv *o;
 	/*char buf[2*KNAMELEN];*/
 
 	up->nerrlab = 0;
 
 	spllo();
-	if(waserror())
+	if(waserror()){
+		print("init0: waserror() loop\n");
 		panic("init0: %r");
+	}
 	/*
 	 * These are o.k. because rootinit is null.
 	 * Then early kproc's will have a root and dot.
 	 */
-	o = up->env;
-	o->pgrp->slash = namec("#/", Atodir, 0, 0);
-	pathclose(o->pgrp->slash->path);
-	o->pgrp->slash->path = newpath("/");
-	o->pgrp->dot = cclone(o->pgrp->slash);
+	up->pgrp->slash = namec("#/", Atodir, 0, 0);
+	pathclose(up->pgrp->slash->path);
+	up->pgrp->slash->path = newpath("/");
+	up->pgrp->dot = cclone(up->pgrp->slash);
 
 	chandevinit();
 /*	print("devtab\n");
@@ -338,18 +338,16 @@
 userinit(void)
 {
 	Proc *p;
-	Osenv *o;
 
 	up = nil;
 	if((p = newforthproc()) == nil){
 		panic("no procs for userinit");
 	}
-	o = p->env;
 
-	o->fgrp = newfgrp(nil);
-	o->egrp = newegrp();
-	o->pgrp = newpgrp();
-	kstrdup(&o->user, eve);
+	p->fgrp = newfgrp(nil);
+	p->egrp = newegrp();
+	p->pgrp = newpgrp();
+	kstrdup(&p->user, eve);
 
 	strcpy(p->text, "*init*");
 	/*
--- a/os/pc64/mkfile
+++ b/os/pc64/mkfile
@@ -64,7 +64,7 @@
 
 i$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES
 	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
-	$LD -v -a -o $target -T$KTZERO -R4096 -l $OBJ $CONF.$O $LIBFILES > $target.loader >[2=1]
+	$LD -v -a -o $target -T$KTZERO -R4096 -l $OBJ $CONF.$O $LIBFILES > $target.loader
 	$LD -o $target -T$KTZERO -R4096 -l $OBJ $CONF.$O $LIBFILES
 	$KSIZE $target
 
--- a/os/pc64/pc64
+++ b/os/pc64/pc64
@@ -8,12 +8,13 @@
 	mnt
 	pipe
 	proc
-	prog
+#	prog
 	rtc
-	srv
+#	srv
 	ssl
-	dup
+#	dup
 	cap
+	ready
 	shm
 
 	ether		netif
@@ -46,14 +47,17 @@
 #	esp
 #	il
 
+# the order of the libraries here matters. If there is a library
+# with an undefined symbol at the end, the linker throws out an
+# undefined symbol error
 lib
 	aml
 	fis
-	interp
+#	interp
 	keyring
-	draw 
 	memlayer
 	memdraw
+	draw
 	tk
 	sec
 	mp
@@ -117,11 +121,11 @@
 #	vgavmware	+cur
 
 mod
-	sys
-	draw
-	tk
-	keyring
-	math
+#	sys
+#	draw
+#	tk
+#	keyring
+#	math
 
 init
 	disinit
@@ -140,15 +144,16 @@
 	alarm
 	alloc
 	allocb
+	auth
 	chan
 	dev
 	dial
-	dis
-	discall
+#	dis
+#	discall
 	edf
-	exception
-	exportfs
-	inferno
+#	exception
+#	exportfs TODO for forth
+#	inferno TODO for forth
 	iomap
 	irq
 	latin1
@@ -162,6 +167,7 @@
 	qlock
 	random
 	rdb
+#	rebootcmd
 	swcursor
 	sysfile
 	taslock
--- a/os/pc64/trap.c
+++ b/os/pc64/trap.c
@@ -6,6 +6,7 @@
 #include	"io.h"
 #include	"ureg.h"
 #include	"../port/error.h"
+#include	"tos.h"
 
 Vctl *vctl[256];	/* defined in pc/irq.c */
 
@@ -19,7 +20,7 @@
 static void doublefault(Ureg*, void*);
 static void unexpected(Ureg*, void*);
 static void _dumpstack(Ureg*);
-static void dumpureg(Ureg* ureg);
+void dumpureg(Ureg* ureg);
 
 void
 trapinit0(void)
@@ -100,7 +101,7 @@
 	"coprocessor error",
 	"alignment check",
 	"machine check",
-	"19 (reserved)",
+	"simd error",
 	"20 (reserved)",
 	"21 (reserved)",
 	"22 (reserved)",
@@ -115,16 +116,7 @@
 	"31 (reserved)",
 };
 
-/*
- *  keep histogram of interrupt service times
- */
 void
-intrtime(Mach*, int vno)
-{
-	USED(vno);
-}
-
-void
 dumprstack(intptr h, intptr rsp, intptr he)
 {
 	intptr i;
@@ -173,113 +165,70 @@
  *  VectorSYSCALL.
  *  Trap is called with interrupts disabled via interrupt-gates.
  */
-void
-trap(Ureg* ureg)
+static int
+usertrap(int vno)
 {
-	int i, vno;
 	char buf[ERRMAX];
-	Vctl *ctl, *v;
-	Mach *mach;
 
-	vno = ureg->trap;
-	if(ctl = vctl[vno]){
-		if(ctl->isintr){
-			m->intr++;
-			if(vno >= VectorPIC && vno != VectorSYSCALL)
-				m->lastintr = ctl->irq;
-		}
-
-		if(ctl->isr)
-			ctl->isr(vno);
-		for(v = ctl; v != nil; v = v->next){
-			if(v->f)
-				v->f(ureg, v->a);
-		}
-		if(ctl->eoi)
-			ctl->eoi(vno);
-
-		if(ctl->isintr){
-			if(up && ctl->irq != IrqTIMER && ctl->irq != IrqCLOCK)
-				preempted();
-		}
-	}
-	else if(vno <= nelem(excname) && up->type == Interp){
+	if(vno < nelem(excname)){
 		spllo();
-		sprint(buf, "sys: trap: %s pc 0x%zx", excname[vno], ureg->pc);
-		print(buf);
-		dumpregs(ureg);
-		_dumpstack(ureg);
-		error(buf);
+		sprint(buf, "sys: trap: %s", excname[vno]);
+		postnote(up, 1, buf, NDebug);
+		return 1;
 	}
-	else if(vno >= VectorPIC && vno != VectorSYSCALL){
-		/*
-		 * An unknown interrupt.
-		 * Check for a default IRQ7. This can happen when
-		 * the IRQ input goes away before the acknowledge.
-		 * In this case, a 'default IRQ7' is generated, but
-		 * the corresponding bit in the ISR isn't set.
-		 * In fact, just ignore all such interrupts.
-		 */
+	return 0;
+}
 
-		/* call all interrupt routines, just in case */
-		for(i = VectorPIC; i <= MaxIrqLAPIC; i++){
-			ctl = vctl[i];
-			if(ctl == nil)
-				continue;
-			if(!ctl->isintr)
-				continue;
-			for(v = ctl; v != nil; v = v->next){
-				if(v->f)
-					v->f(ureg, v->a);
-			}
-			/* should we do this? */
-			if(ctl->eoi)
-				ctl->eoi(i);
-		}
+/* go to user space */
+void
+trap(Ureg *ureg)
+{
+	int vno, user;
 
-		/* clear the interrupt */
-		i8259isr(vno);
-			
-		if(0)print("cpu%d: spurious interrupt %d, last %d",
-			m->machno, vno, m->lastintr);
-		if(0)if(conf.nmach > 1){
-			for(i = 0; i < MAXMACH; i++){
-				if(active.machs[i] == 0)
-					continue;
-				mach = MACHP(i);
-				if(m->machno == mach->machno)
-					continue;
-				print(" cpu%d: last %d",
-					mach->machno, mach->lastintr);
+	user = kenter(ureg);
+	vno = ureg->type;
+	if(!irqhandled(ureg, vno) && (!user || !usertrap(vno))){
+		if(!user){
+			void (*pc)(void);
+
+			extern void _rdmsrinst(void);
+			extern void _wrmsrinst(void);
+			extern void _peekinst(void);
+
+			pc = (void*)ureg->pc;
+			if(pc == _rdmsrinst || pc == _wrmsrinst){
+				if(vno == VectorGPF){
+					ureg->bp = -1;
+					ureg->pc += 2;
+					return;
+				}
+			} else if(pc == _peekinst){
+				if(vno == VectorGPF || vno == VectorPF){
+					ureg->pc += 2;
+					return;
+				}
 			}
-			print("\n");
+
+			/* early fault before trapinit() */
+			if(vno == VectorPF)
+				faultamd64(ureg, 0);
 		}
-		m->spuriousintr++;
-		return;
-	}
-	else{
-		if(vno == VectorNMI){
-			nmienable();
-			if(m->machno != 0){
-				print("cpu%d: PC %8.8zuX\n",
-					m->machno, ureg->pc);
-			}
-		}
-		dumpureg(ureg);
+
 		dumpregs(ureg);
-		if(vno < nelem(excname)){
-			dumprstack(ureg->r11, ureg->r8, ureg->r12);
-			dumppstack(ureg->r11, ureg->dx, ureg->r12);
+		if(!user){
+			ureg->sp = (uintptr)&ureg->sp;
 			_dumpstack(ureg);
-			panic("%s", excname[vno]);
 		}
-		panic("unknown trap/intr: %d\n", vno);
+		if(vno < nelem(excname))
+			panic("%s", excname[vno]);
+		panic("unknown trap/intr: %d", vno);
 	}
+	splhi();
 
-	/* delaysched set because we held a lock or because our quantum ended */
-	if(up && up->delaysched){
-		sched();
-		splhi();
+	if(user){
+		if(up->procctl || up->nnote)
+			notify(ureg);
+		kexit(ureg);
 	}
 }
 
@@ -287,40 +236,29 @@
  *  dump registers
  */
 void
-dumpregs2(Ureg* ureg)
+dumpregs(Ureg* ureg)
 {
 	if(up)
-		print("cpu%d: registers for %s %ud\n",
+		iprint("cpu%d: registers for %s %ud\n",
 			m->machno, up->text, up->pid);
 	else
-		print("cpu%d: registers for kernel\n", m->machno);
-	print("FLAGS=%zux TRAP=%zux ECODE=%zux PC=%zux",
-		ureg->flags, ureg->trap, ureg->ecode, ureg->pc);
-	print(" SS=%4.4zuX USP=%zux\n", ureg->ss & 0xFFFF, ureg->usp);
-	print("  AX %8.8zuX  BX TOP %8.8zuX  CX %8.8zuX  DX PSP %8.8zuX\n",
-		ureg->ax, ureg->bx, ureg->cx, ureg->dx);
-	print("  SI %8.8zuX  DI %8.8zuX  BP %8.8zuX\n",
-		ureg->si, ureg->di, ureg->bp);
-	print("  CS %4.4zux  DS %4.4ux  ES %4.4ux  FS %4.4ux  GS %4.4ux\n",
-		ureg->cs & 0xFFFF, ureg->ds & 0xFFFF, ureg->es & 0xFFFF,
-		ureg->fs & 0xFFFF, ureg->gs & 0xFFFF);
-	print("  R8 RSP %8.8zux  R9 IP %8.8zzux  R10 W %8.8zux\n"
-			"  R11 UP %8.8zux  R12 UPE %8.8zux  R13 %8.8zux\n",
-		ureg->r8, ureg->r9, ureg->r10,
-		ureg->r11, ureg->r12, ureg->r13);
-	print("  R14 up %8.8zux  R15 m %8.8zux\n",
-		ureg->r14, ureg->r15);
-}
+		iprint("cpu%d: registers for kernel\n", m->machno);
 
-void
-dumpregs(Ureg* ureg)
-{
-	extern ulong etext;
-	vlong mca, mct;
-	intptr *i;
+	iprint("  AX %.16lluX  BX TOP %.16lluX  CX %.16lluX\n",
+		ureg->ax, ureg->bx, ureg->cx);
+	iprint("  DX PSP %.16lluX  SI %.16lluX  DI %.16lluX\n",
+		ureg->dx, ureg->si, ureg->di);
+	iprint("  BP %.16lluX  R8 RSP %.16lluX  R9 IP %.16lluX\n",
+		ureg->bp, ureg->r8, ureg->r9);
+	iprint(" R10 W %.16lluX R11 UM %.16lluX R12 UME %.16lluX\n",
+		ureg->r10, ureg->r11, ureg->r12);
+	iprint(" R13 %.16lluX R14 up %.16lluX R15 m %.16lluX\n",
+		ureg->r13, ureg->r14, ureg->r15);
+	iprint("  CS %.4lluX   SS %.4lluX    PC %.16lluX  SP %.16lluX\n",
+		ureg->cs & 0xffff, ureg->ss & 0xffff, ureg->pc, ureg->sp);
+	iprint("TYPE %.2lluX  ERROR %.4lluX FLAGS %.8lluX\n",
+		ureg->type & 0xff, ureg->error & 0xffff, ureg->flags & 0xffffffff);
 
-	dumpregs2(ureg);
-
 	/*
 	 * Processor control registers.
 	 * If machine check exception, time stamp counter, page size extensions
@@ -328,26 +266,27 @@
 	 * CR4. If there is a CR4 and machine check extensions, read the machine
 	 * check address and machine check type registers if RDMSR supported.
 	 */
-	print("  CR0 %8.8zux CR2 %8.8zux CR3 %8.8zux",
+	iprint(" CR0 %8.8llux CR2 %16.16llux CR3 %16.16llux",
 		getcr0(), getcr2(), getcr3());
-	if(m->cpuiddx & 0x9A){
-		print(" CR4 %8.8zux", getcr4());
-		if((m->cpuiddx & 0xA0) == 0xA0){
-			rdmsr(0x00, &mca);
-			rdmsr(0x01, &mct);
-			print("\n  MCA %8.8zux MCT %8.8zux", mca, mct);
-		}
+	if(m->cpuiddx & (Mce|Tsc|Pse|Vmex)){
+		iprint(" CR4 %16.16llux\n", getcr4());
+		if(ureg->type == 18)
+			dumpmcregs();
 	}
+	iprint("  ur %#p up %#p\n", ureg, up);
+
+	/* my stuff, not in 9front */
 	print("\n  ur %lux up %lux ureg->bp & ~0xFFF %zx\n", ureg, up, ureg->bp & ~0xFFF);
 	if((ureg->bp & ~0xFFF) == FFSTART){
-		for(i = (intptr*)FFSTART; i<=(intptr*)ureg->bp; i++){
+		for(intptr *i = (intptr*)FFSTART; i<=(intptr*)ureg->bp; i++){
 			print("0x%p: 0x%zx\n", i, *i);
 		}
-		for(i = (intptr*)FFEND; i>=(intptr*)ureg->sp; i--){
+		for(intptr *i = (intptr*)FFEND; i>=(intptr*)ureg->sp; i--){
 			print("0x%p: 0x%zx\n", i, *i);
 		}
 	}
 }
+
 /* displays in the order pushed into the stack */
 void
 dumpureg(Ureg* ureg)
@@ -361,7 +300,7 @@
 	print("SS %4.4zuX SP %zux\n", ureg->ss & 0xFFFF, ureg->usp);
 	print("	FLAGS %zux CS %zux PC %zux ECODE %zux TRAP %zux\n",
 		ureg->flags, ureg->cs, ureg->pc, ureg->ecode, ureg->trap);
-	print("	GS %4.4zux  FS %4.4ux  ES %4.4ux  DS %4.4ux\n",
+	print("	GS %4.4ux  FS %4.4ux  ES %4.4ux  DS %4.4ux\n",
 		ureg->gs & 0xFFFF, ureg->fs & 0xFFFF, ureg->es & 0xFFFF,
 		ureg->ds & 0xFFFF);
 
@@ -396,51 +335,64 @@
 _dumpstack(Ureg *ureg)
 {
 	uintptr l, v, i, estack;
-	extern uintptr etext;
-	int onlypc = 0;
+	extern ulong etext;
+	int x;
+	char *s;
 
-	print("ktrace /kernel/path pc 0x%zux sp 0x%zux &l 0x%zux up 0x%p\n", ureg->pc, ureg->sp, &l, up);
+	if((s = getconf("*nodumpstack")) != nil && strcmp(s, "0") != 0){
+		iprint("dumpstack disabled\n");
+		return;
+	}
+	iprint("dumpstack\n");
+
+	x = 0;
+	x += iprint("ktrace /kernel/path %#p %#p <<EOF\n", ureg->pc, ureg->sp);
 	i = 0;
-	if(up &&
-		(uintptr)&l >= (uintptr)up->kstack &&
-		(uintptr)&l <= (uintptr)up->kstack+KSTACK){
+	if(up
+	&& (uintptr)&l >= (uintptr)up->kstack
+	&& (uintptr)&l <= (uintptr)up->kstack+KSTACK)
 		estack = (uintptr)up->kstack+KSTACK;
-		print("up->kstack 0x%zux estack 0x%zux\n",(uintptr)up->kstack, estack);
-	}else if((uintptr)&l >= (uintptr)m->stack &&
-		(uintptr)&l <= (uintptr)m+BY2PG){
+	else if((uintptr)&l >= (uintptr)m->stack
+	&& (uintptr)&l <= (uintptr)m+MACHSIZE)
 		estack = (uintptr)m+MACHSIZE;
-		print("m->stack 0x%zux estack 0x%zux\n",(uintptr)m->stack, estack);
-	}else{
-		if(up)
-			print("up->kstack 0x%zux\n", (uintptr)up->kstack);
-		else
-			print("m->stack 0x%zux\n", (uintptr)m->stack);
+	else
 		return;
-	}
+	x += iprint("estackx %p\n", estack);
 
-	for(l=(uintptr)&l; l<estack; l+=sizeof(intptr)){
+	for(l = (uintptr)&l; l < estack; l += sizeof(uintptr)){
 		v = *(uintptr*)l;
-		if(onlypc){
-			if(KTZERO < v && v < (uintptr)&etext){
-				/*
-				 * we could Pick off general CALL (((uchar*)v)[-5] == 0xE8)
-				 * and CALL indirect through AX (((uchar*)v)[-2] == 0xFF && ((uchar*)v)[-2] == 0xD0),
-				 * but this is too clever and misses faulting address.
-				 */
-				print("%.8zux=%.8zux ", l, v);
-				i++;
-			}
-		}else{
-				print("%.8zux=%.8zux ", l, v);
-				i++;
+		if((KTZERO < v && v < (uintptr)&etext) || estack-l < 32){
+			/*
+			 * Could Pick off general CALL (((uchar*)v)[-5] == 0xE8)
+			 * and CALL indirect through AX
+			 * (((uchar*)v)[-2] == 0xFF && ((uchar*)v)[-2] == 0xD0),
+			 * but this is too clever and misses faulting address.
+			 */
+			x += iprint("%.8lux=%.8lux ", (ulong)l, (ulong)v);
+			i++;
 		}
 		if(i == 4){
 			i = 0;
-			print("\n");
+			x += iprint("\n");
 		}
 	}
 	if(i)
-		print("\n");
+		iprint("\n");
+	iprint("EOF\n");
+
+	if(ureg->type != VectorNMI)
+		return;
+
+	i = 0;
+	for(l = (uintptr)&l; l < estack; l += sizeof(uintptr)){
+		iprint("%.8p ", *(uintptr*)l);
+		if(++i == 8){
+			i = 0;
+			iprint("\n");
+		}
+	}
+	if(i)
+		iprint("\n");
 }
 
 void
@@ -450,20 +402,50 @@
 }
 
 static void
-debugbpt(Ureg* ureg, void*)
+debugexc(Ureg *ureg, void *)
 {
+	u64int dr6, m;
 	char buf[ERRMAX];
+	char *p, *e;
+	int i;
 
-	if(breakhandler != nil){
-		breakhandler(ureg, up);
+	dr6 = getdr6();
+	if(up == nil)
+		panic("kernel debug exception dr6=%#.8ullx", dr6);
+	putdr6(up->dr[6]);
+	if(userureg(ureg))
+		qlock(&up->debug);
+	else if(!canqlock(&up->debug))
 		return;
+	m = up->dr[7];
+	m = (m >> 4 | m >> 3) & 8 | (m >> 3 | m >> 2) & 4 | (m >> 2 | m >> 1) & 2 | (m >> 1 | m) & 1;
+	m &= dr6;
+	if(m == 0){
+		sprint(buf, "sys: debug exception dr6=%#.8ullx", dr6);
+		postnote(up, 0, buf, NDebug);
+	}else{
+		p = buf;
+		e = buf + sizeof(buf);
+		p = seprint(p, e, "sys: watchpoint ");
+		for(i = 0; i < 4; i++)
+			if((m & 1<<i) != 0)
+				p = seprint(p, e, "%d%s", i, (m >> i + 1 != 0) ? "," : "");
+		postnote(up, 0, buf, NDebug);
 	}
+	qunlock(&up->debug);
+}
+
+static void
+debugbpt(Ureg* ureg, void*)
+{
+	char buf[ERRMAX];
+
 	if(up == 0)
 		panic("kernel bpt");
 	/* restore pc to instruction that caused the trap */
 	ureg->pc--;
 	sprint(buf, "sys: breakpoint");
-	error(buf);
+	postnote(up, 1, buf, NDebug);
 }
 
 static void
@@ -482,28 +464,291 @@
 faultamd64(Ureg* ureg, void*)
 {
 	uintptr addr;
-	int read, user;
+	int read, user, n, insyscall, f;
 	char buf[ERRMAX];
 
 	addr = getcr2();
-	user = (ureg->cs & 0xFFFF) == KESEL;
-	if(!user && mmukmapsync(addr))
-		return;
-	read = !(ureg->ecode & 2);
-	spllo();
-	snprint(buf, sizeof(buf), "trap: fault %s pc=0x%zux addr=0x%zux\n",
-			read ? "read" : "write", ureg->pc, addr);
-print(buf);
-	dumpregs(ureg);
+	read = !(ureg->error & 2);
+	user = userureg(ureg);
+	if(!user){
+		{
+			extern void _peekinst(void);
+			
+			if((void(*)(void))ureg->pc == _peekinst){
+				ureg->pc += 2;
+				return;
+			}
+		}
+		if(addr >= (uintptr)USTKTOP)
+			panic("kernel fault: bad address pc=%#p addr=%#p", ureg->pc, addr);
+		if(up == nil)
+			panic("kernel fault: no user process pc=%#p addr=%#p", ureg->pc, addr);
+	}
+	if(up == nil)
+		panic("user fault: up=0 pc=%#p addr=%#p", ureg->pc, addr);
+
+	/* forth specific dump stack */
 	dumprstack(ureg->r11, ureg->r8, ureg->r12);
 	dumppstack(ureg->r11, ureg->dx, ureg->r12);
-dumpstack();
-	if(up->type == Interp)
-		disfault(ureg, buf);
-	dumpregs(ureg);
-	print("fault: %s\n", buf);
-	panic("fault: %s\n", buf);
+
+	insyscall = up->insyscall;
+	up->insyscall = 1;
+	f = fpusave();
+	if(!user && waserror()){
+		if(up->nerrlab == 0){
+			pprint("suicide: sys: %s\n", up->errstr);
+			pexit(up->errstr, 1);
+		}
+		int s = splhi();
+		fpurestore(f);
+		up->insyscall = insyscall;
+		splx(s);
+		nexterror();
+	}
+	n = -1 /* fault(addr, ureg->pc, read) no paging for us*/;
+	if(n < 0){
+		if(!user){
+			dumpregs(ureg);
+			panic("fault: %#p", addr);
+		}
+		/* checkpages(); */
+		sprint(buf, "sys: trap: fault %s addr=%#p",
+			read ? "read" : "write", addr);
+		postnote(up, 1, buf, NDebug);
+	}
+	if(!user) poperror();
+	splhi();
+	fpurestore(f);
+	up->insyscall = insyscall;
 }
+
+/*
+ *  Call user, if necessary, with note.
+ *  Pass user the Ureg struct and the note on his stack.
+ */
+int
+notify(Ureg* ureg)
+{
+	int l;
+	uintptr sp;
+	Note *n;
+
+	if(up->procctl)
+		procctl();
+	if(up->nnote == 0)
+		return 0;
+	spllo();
+	qlock(&up->debug);
+	up->notepending = 0;
+	n = &up->note[0];
+	if(strncmp(n->msg, "sys:", 4) == 0){
+		l = strlen(n->msg);
+		if(l > ERRMAX-15)	/* " pc=0x12345678\0" */
+			l = ERRMAX-15;
+		sprint(n->msg+l, " pc=%#p", ureg->pc);
+	}
+
+	if(n->flag!=NUser && (up->notified || up->notify==0)){
+		qunlock(&up->debug);
+		if(n->flag == NDebug){
+			up->fpstate &= ~FPillegal;
+			pprint("suicide: %s\n", n->msg);
+		}
+		pexit(n->msg, n->flag!=NDebug);
+	}
+
+	if(up->notified){
+		qunlock(&up->debug);
+		splhi();
+		return 0;
+	}
+
+	if(!up->notify){
+		qunlock(&up->debug);
+		pexit(n->msg, n->flag!=NDebug);
+	}
+	sp = ureg->sp;
+	sp -= 256;	/* debugging: preserve context causing problem */
+	sp -= sizeof(Ureg);
+if(0) print("%s %ud: notify %#p %#p %#p %s\n",
+	up->text, up->pid, ureg->pc, ureg->sp, sp, n->msg);
+
+	if(!okaddr((uintptr)up->notify, 1, 0)
+	|| !okaddr(sp-ERRMAX-4*BY2WD, sizeof(Ureg)+ERRMAX+4*BY2WD, 1)){
+		qunlock(&up->debug);
+		up->fpstate &= ~FPillegal;
+		pprint("suicide: bad address in notify\n");
+		pexit("Suicide", 0);
+	}
+
+	memmove((Ureg*)sp, ureg, sizeof(Ureg));
+	*(Ureg**)(sp-BY2WD) = up->ureg;	/* word under Ureg is old up->ureg */
+	up->ureg = (void*)sp;
+	sp -= BY2WD+ERRMAX;
+	memmove((char*)sp, up->note[0].msg, ERRMAX);
+	sp -= 3*BY2WD;
+	((uintptr*)sp)[2] = sp + 3*BY2WD;	/* arg2 string */
+	((uintptr*)sp)[1] = (uintptr)up->ureg;	/* arg1 is ureg* */
+	((uintptr*)sp)[0] = 0;			/* arg0 is pc */
+	ureg->sp = sp;
+	ureg->pc = (uintptr)up->notify;
+	ureg->bp = (uintptr)up->ureg;		/* arg1 passed in RARG */
+	ureg->cs = UESEL;
+	ureg->ss = UDSEL;
+	up->notified = 1;
+	up->nnote--;
+	memmove(&up->lastnote, &up->note[0], sizeof(Note));
+	memmove(&up->note[0], &up->note[1], up->nnote*sizeof(Note));
+	qunlock(&up->debug);
+	splhi();
+	if(up->fpstate == FPactive){
+		fpsave(up->fpsave);
+		up->fpstate = FPinactive;
+	}
+	up->fpstate |= FPillegal;
+	return 1;
+}
+
+/* TODO include 9front port/fault.c */
+int
+okaddr(uintptr, u32, int)
+{
+	return 1;
+}
+void
+validaddr(uintptr addr, ulong len, int write)
+{
+	if(!okaddr(addr, len, write)){
+		pprint("suicide: invalid address %#p/%lud in sys call pc=%#p\n", addr, len, userpc());
+		postnote(up, 1, "sys: bad address in syscall", NDebug);
+		error(Ebadarg);
+	}
+}
+
+/*
+ *   Return user to state before notify()
+ */
+void
+noted(Ureg* ureg, ulong arg0)
+{
+	Ureg *nureg;
+	uintptr oureg, sp;
+
+	up->fpstate &= ~FPillegal;
+	spllo();
+	qlock(&up->debug);
+	if(arg0!=NRSTR && !up->notified) {
+		qunlock(&up->debug);
+		pprint("call to noted() when not notified\n");
+		pexit("Suicide", 0);
+	}
+	up->notified = 0;
+
+	nureg = up->ureg;	/* pointer to user returned Ureg struct */
+
+	/* sanity clause */
+	oureg = (uintptr)nureg;
+/* TODO we check that this register is within the kernel data segment
+	if(!okaddr(oureg-BY2WD, BY2WD+sizeof(Ureg), 0)){
+		qunlock(&up->debug);
+		pprint("bad ureg in noted or call to noted when not notified\n");
+		pexit("Suicide", 0);
+	}
+*/
+	/* don't let user change system flags or segment registers */
+	setregisters(ureg, (char*)ureg, (char*)nureg, sizeof(Ureg));
+
+	switch(arg0){
+	case NCONT:
+	case NRSTR:
+if(0) print("%s %ud: noted %#p %#p\n",
+	up->text, up->pid, nureg->pc, nureg->sp);
+		if(!okaddr(nureg->pc, 1, 0) || !okaddr(nureg->sp, BY2WD, 0)){
+			qunlock(&up->debug);
+			pprint("suicide: trap in noted\n");
+			pexit("Suicide", 0);
+		}
+		up->ureg = (Ureg*)(*(uintptr*)(oureg-BY2WD));
+		qunlock(&up->debug);
+		break;
+
+	case NSAVE:
+		if(!okaddr(nureg->pc, 1, 0)
+		|| !okaddr(nureg->sp, BY2WD, 0)){
+			qunlock(&up->debug);
+			pprint("suicide: trap in noted\n");
+			pexit("Suicide", 0);
+		}
+		qunlock(&up->debug);
+		sp = oureg-4*BY2WD-ERRMAX;
+		splhi();
+		ureg->sp = sp;
+		ureg->bp = oureg;		/* arg 1 passed in RARG */
+		((uintptr*)sp)[1] = oureg;	/* arg 1 0(FP) is ureg* */
+		((uintptr*)sp)[0] = 0;		/* arg 0 is pc */
+		break;
+
+	default:
+		up->lastnote.flag = NDebug;
+		/* fall through */
+
+	case NDFLT:
+		qunlock(&up->debug);
+		if(up->lastnote.flag == NDebug)
+			pprint("suicide: %s\n", up->lastnote.msg);
+		pexit(up->lastnote.msg, up->lastnote.flag!=NDebug);
+	}
+}
+
+/* the USTKTOP macro depends on up being populated
+	this function will double fault as up is being set to 0 before using USTKTOP
+ */
+uintptr
+execregs(uintptr entry, ulong ssize, ulong nargs)
+{
+	uintptr *sp, top;
+	Ureg *ureg;
+
+	top = (uintptr)(USTKTOP-sizeof(Tos));		/* address of kernel/user shared data */
+	sp = (uintptr*)(USTKTOP - ssize);
+	*--sp = nargs;
+	ureg = up->dbgreg;
+	ureg->sp = (uintptr)sp;
+	ureg->pc = entry;
+	ureg->cs = KESEL;
+	ureg->ss = KDSEL;
+	ureg->r14 = ureg->r15 = 0;	/* extern user registers */
+	return top;		/* address of kernel/user shared data */
+}
+
+/*
+ *  return the userpc the last exception happened at
+ */
+uintptr
+userpc(void)
+{
+	Ureg *ureg;
+
+	ureg = (Ureg*)up->dbgreg;
+	return ureg->pc;
+}
+
+/* This routine must save the values of registers the user is not permitted
+ * to write from devproc and noted() and then restore the saved values before returning.
+ */
+void
+setregisters(Ureg* ureg, char* pureg, char* uva, int n)
+{
+	u64int flags;
+
+	flags = ureg->flags;
+	memmove(pureg, uva, n);
+	ureg->cs = KESEL;
+	ureg->ss = KDSEL;
+	ureg->flags = (ureg->flags & 0x00ff) | (flags & 0xff00);
+	/* ureg->pc &= UADDRMASK; */
+}
+
 
 static void
 linkproc(void)
--- a/os/port/alloc.c
+++ b/os/port/alloc.c
@@ -91,7 +91,7 @@
 
 	D2B(b, v);
 	b->magic = MAGIC_A;
-	((Heap*)v)->color = mutator;
+	/* ((Heap*)v)->color = mutator; */
 }
 
 char*
@@ -256,12 +256,12 @@
 
 	// if(asize >= 1024*1024*1024)	/* for sanity and to avoid overflow */
 	//	return nil;
-	if(p->cursize > p->ressize &&
+/*	if(p->cursize > p->ressize &&
 		(prog = currun()) != nil &&
 		prog->flags&Prestricted){
 		print("poolalloc exception\n");
 		return nil;
-	}
+	}*/
 	size = asize;
 	osize = size;
 	size = (size + BHDRSIZE + p->quanta) & ~(p->quanta);
@@ -404,7 +404,7 @@
 poolfree(Pool *p, void *v)
 {
 	Bhdr *b, *c;
-	extern Bhdr *ptr;
+	/* extern Bhdr *ptr; defined in libinterp/gc.c */
 
 	D2B(b, v);
 
@@ -414,8 +414,8 @@
 
 	c = B2NB(b);
 	if(c->magic == MAGIC_F) {	/* Join forward */
-		if(c == ptr)
-			ptr = b;
+		/*if(c == ptr)
+			ptr = b; */
 		pooldel(p, c);
 		c->magic = 0;
 		b->size += c->size;
@@ -424,8 +424,8 @@
 
 	c = B2PT(b)->hdr;
 	if(c->magic == MAGIC_F) {	/* Join backward */
-		if(b == ptr)
-			ptr = c;
+		/*if(b == ptr)
+			ptr = c; */
 		pooldel(p, c);
 		b->magic = 0;
 		c->size += b->size;
--- a/os/port/chan.c
+++ b/os/port/chan.c
@@ -726,7 +726,7 @@
 		poperror();
 	}
 
-	pg = up->env->pgrp;
+	pg = up->pgrp;
 	wlock(&pg->ns);
 	l = &MOUNTH(pg, old->qid);
 	for(m = *l; m != nil; m = m->hash){
@@ -792,7 +792,7 @@
 	 * cclose will take care of freeing the umh.
 	 */
 
-	pg = up->env->pgrp;
+	pg = up->pgrp;
 	wlock(&pg->ns);
 
 	l = &MOUNTH(pg, mnt->qid);
@@ -869,7 +869,7 @@
 	Pgrp *pg;
 	Mhead *m;
 
-	pg = up->env->pgrp;
+	pg = up->pgrp;
 	rlock(&pg->ns);
 	for(m = MOUNTH(pg, qid); m != nil; m = m->hash){
 		if(eqchantdqid(m->from, type, dev, qid, 1)){
@@ -1004,7 +1004,7 @@
 				*nerror = nhave;
 			pathclose(path);
 			cclose(c);
-			kstrcpy(up->env->errstr, Enotdir, ERRMAX);
+			kstrcpy(up->errstr, Enotdir, ERRMAX);
 			putmhead(mh);
 			return -1;
 		}
@@ -1083,11 +1083,11 @@
 					if(wq->nqid == 0 || (wq->qid[wq->nqid-1].type&QTDIR) != 0){
 						if(nerror)
 							*nerror = nhave+wq->nqid+1;
-						kstrcpy(up->env->errstr, Edoesnotexist, ERRMAX);
+						kstrcpy(up->errstr, Edoesnotexist, ERRMAX);
 					}else{
 						if(nerror)
 							*nerror = nhave+wq->nqid;
-						kstrcpy(up->env->errstr, Enotdir, ERRMAX);
+						kstrcpy(up->errstr, Enotdir, ERRMAX);
 					}
 					free(wq);
 					putmhead(mh);
@@ -1264,7 +1264,7 @@
 		snprint(up->genbuf, sizeof up->genbuf, "...%.*s",
 			utfnlen(name, ename-name), name);
 	}
-	snprint(up->env->errstr, ERRMAX, "%#q %s", up->genbuf, err);
+	snprint(up->errstr, ERRMAX, "%#q %s", up->genbuf, err);
 	nexterror();
 }
 
@@ -1333,7 +1333,7 @@
 	nomount = 0;
 	switch(name[0]){
 	case '/':
-		c = up->env->pgrp->slash;
+		c = up->pgrp->slash;
 		incref(c);
 		break;
 
@@ -1360,7 +1360,7 @@
 		 *	   any others left unprotected)
 		 */
 		n = chartorune(&r, up->genbuf+1)+1;
-		if(up->env->pgrp->noattach && utfrune("|decp", r)==nil)
+		if(up->pgrp->noattach && utfrune("|decp", r)==nil)
 			error(Enoattach);
 		t = devno(r, 1);
 		if(t == -1)
@@ -1369,7 +1369,7 @@
 		break;
 
 	default:
-		c = up->env->pgrp->dot;
+		c = up->pgrp->dot;
 		incref(c);
 		break;
 	}
@@ -1382,7 +1382,6 @@
 	e.nelems = 0;
 	e.nerror = 0;
 	if(waserror()){
-print("namec: waserror() loop before parsename pid %d\n", up->pid);
 		cclose(c);
 		free(e.name);
 		free(e.elems);
@@ -1396,9 +1395,9 @@
 				e.nerror, e.off[e.nerror]);
 		len = e.prefix+e.off[e.nerror];
 		free(e.off);
-		err = up->env->errstr;
-		up->env->errstr = up->env->syserrstr;
-		up->env->syserrstr = err;
+		err = up->errstr;
+		up->errstr = up->syserrstr;
+		up->syserrstr = err;
 		namelenerror(aname, len, err);
 	}
 
@@ -1414,17 +1413,20 @@
 		/* perm must have DMDIR if last element is / or /. */
 		if(e.mustbedir && !(perm&DMDIR)){
 			e.nerror = e.nelems;
+			print("namec: Acreate create without DMDIR pid %d\n", up->pid);
 			error("create without DMDIR");
 		}
 
 		/* don't try to walk the last path element just yet. */
-		if(e.nelems == 0)
+		if(e.nelems == 0){
+			print("namec: Acreate Eexist pid %d\n", up->pid);
 			error(Eexist);
+		}
 		e.nelems--;
 	}
 
 	if(walk(&c, e.elems, e.nelems, nomount, &e.nerror) < 0){
-print("namec: walk < 0 e.nerror %d pid %d\n", e.nerror, up->pid);
+		print("namec: walk < 0 e.nerror %d pid %d\n", e.nerror, up->pid);
 		if(e.nerror < 0 || e.nerror > e.nelems){
 			print("namec %s walk error nerror=%d\n", aname, e.nerror);
 			e.nerror = 0;
@@ -1449,6 +1451,7 @@
 		if(!nomount)
 			domount(&c, &m, nil);
 		if(waserror()){
+			print("namec: Abind\n");
 			putmhead(m);
 			nexterror();
 		}
@@ -1465,6 +1468,7 @@
 		path = c->path;
 		incref(path);
 		if(waserror()){
+			print("namec: Aopen\n");
 			pathclose(path);
 			nexterror();
 		}
@@ -1472,6 +1476,7 @@
 		if(!nomount)
 			domount(&c, &m, &path);
 		if(waserror()){
+			print("namec: Aopen 1\n");
 			putmhead(m);
 			nexterror();
 		}
@@ -1509,8 +1514,8 @@
 
 			/* save registers else error() in open has wrong value of c saved */
 			saveregisters();
-			DBG("namec walk c->type %d devtab[c->type]->name %s c->path %s\n",
-					c->type, devtab[c->type]->name, chanpath(c));
+			DBG("namec walk pid %d c->type %d devtab[c->type]->name %s c->path %s\n",
+					up->pid, c->type, devtab[c->type]->name, chanpath(c));
 			c = devtab[c->type]->open(c, omode&~OCEXEC);
 			if(omode & ORCLOSE)
 				c->flag |= CRCLOSE;
@@ -1544,6 +1549,7 @@
 		e.nelems++;
 		e.nerror++;
 		if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) == 0){
+			print("namec: Acreate\n");
 			if(omode&OEXCL)
 				error(Eexist);
 			omode |= OTRUNC;
@@ -1628,16 +1634,16 @@
 		if(omode & OEXCL)
 			nexterror();
 		/* save error */
-		err = up->env->errstr;
-		up->env->errstr = up->env->syserrstr;
-		up->env->syserrstr = err;
+		err = up->errstr;
+		up->errstr = up->syserrstr;
+		up->syserrstr = err;
 		/* note: we depend that walk does not error */
 		if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) < 0)
 			error(err);	/* report true error */
 		/* restore error */
-		err = up->env->syserrstr;
-		up->env->syserrstr = up->env->errstr;
-		up->env->errstr = err;
+		err = up->syserrstr;
+		up->syserrstr = up->errstr;
+		up->errstr = err;
 		omode |= OTRUNC;
 		goto Open;
 
--- a/os/port/dev.c
+++ b/os/port/dev.c
@@ -228,7 +228,7 @@
 /* print("devwalk c %s nc %s n %s name %s caller 0x%p\n", chanpath(c), chanpath(nc), n, *name, getcallerpc(&c)); */
 					error(Enonexist);
 				}
-				kstrcpy(up->env->errstr, Enonexist, ERRMAX);
+				kstrcpy(up->errstr, Enonexist, ERRMAX);
 				goto Done;
 			case 0:
 				continue;
@@ -285,7 +285,7 @@
 				return n;
 			}
 			print("%s %s: devstat %C %llux\n",
-				up->text, up->env->user,
+				up->text, up->user,
 				devtab[c->type]->dc, c->qid.path);
 
 			error(Enonexist);
@@ -338,7 +338,7 @@
 }
 
 /*
- * error(Eperm) if open permission not granted for up->env->user.
+ * error(Eperm) if open permission not granted for up->user.
  */
 void
 devpermcheck(char *fileuid, u32 perm, s32 omode)
@@ -346,17 +346,19 @@
 	u32 t;
 	static int access[] = { 0400, 0200, 0600, 0100 };
 
-	if(strcmp(up->env->user, fileuid) == 0)
+	if(strcmp(up->user, fileuid) == 0)
 		perm <<= 0;
 	else
-	if(strcmp(up->env->user, eve) == 0)
+	if(strcmp(up->user, eve) == 0)
 		perm <<= 3;
 	else
 		perm <<= 6;
 
 	t = access[omode&3];
-	if((t&perm) != t)
+	if((t&perm) != t){
+		print("devpermcheck Eperm up->user %s perm 0x%ux t 0x%ux\n", up->user, perm, t);
 		error(Eperm);
+	}
 }
 
 Chan*
@@ -381,8 +383,10 @@
 	}
 Return:
 	c->offset = 0;
-	if((c->qid.type&QTDIR) && omode!=OREAD)
+	if((c->qid.type&QTDIR) && omode!=OREAD){
+		print("devopen Eperm\n");
 		error(Eperm);
+	}
 	c->mode = openmode(omode);
 	c->flag |= COPEN;
 	return c;
--- a/os/port/devbridge.c
+++ b/os/port/devbridge.c
@@ -955,7 +955,7 @@
 		// release lock to read - error means it is time to quit
 		qunlock(b);
 		if(waserror()) {
-			print("etherread read error: %s\n", up->env->errstr);
+			print("etherread read error: %s\n", up->errstr);
 			qlock(b);
 			break;
 		}
--- a/os/port/devcap.c
+++ b/os/port/devcap.c
@@ -191,9 +191,9 @@
 			*l = c->next;
 			qunlock(&allcaps.l);
 			free(c);
-			if(n == 2 && strcmp(up->env->user, users[0]) != 0)
+			if(n == 2 && strcmp(up->user, users[0]) != 0)
 				return -1;
-			kstrdup(&up->env->user, users[1]);
+			kstrdup(&up->user, users[1]);
 			return 0;
 		}
 	qunlock(&allcaps.l);
--- a/os/port/devcons.c
+++ b/os/port/devcons.c
@@ -10,8 +10,9 @@
 #include	"keyboard.h"
 
 extern int cflag;
-extern int keepbroken;
+int keepbroken = 1;
 extern int rdbstarted;
+extern u32 kerndate;
 
 void	(*serwrite)(char *, int);
 
@@ -47,8 +48,14 @@
 /* above until kbdfs */
 
 char*	sysname;
-char*	eve;
+char	*sysname;
+vlong	fasthz;
 
+static int	readtime(ulong, char*, int);
+static int	readbintime(char*, int);
+static int	writetime(char*, int);
+static int	writebintime(char*, int);
+
 enum
 {
 	CMreboot,
@@ -57,6 +64,7 @@
 	CMbroken,
 	CMnobroken,
 	CMconsole,
+	CMrdb,
 };
 
 static Cmdtab sysctlcmd[] =
@@ -67,25 +75,21 @@
 	CMconsole,	"console", 1,
 	CMbroken,	"broken", 0,
 	CMnobroken,	"nobroken", 0,
+	CMrdb,		"rdb",		0,
 };
 
+Cmdtab rebootmsg[] =
+{
+	CMreboot,	"reboot",	0,
+	CMpanic,	"panic",	0,
+	CMrdb,		"rdb",		0,
+};
+
 void
 printinit(void)
 {
 }
 
-/*
- *  return true if current user is eve
- */
-int
-iseve(void)
-{
-	Osenv *o;
-
-	o = up->env;
-	return strcmp(eve, o->user) == 0;
-}
-
 static int
 consactive(void)
 {
@@ -361,10 +365,10 @@
 	va_list arg;
 	char buf[2*PRINTSIZE];
 
-	if(up == nil || up->env->fgrp == nil)
+	if(up == nil || up->fgrp == nil)
 		return 0;
 
-	c = up->env->fgrp->fd[2];
+	c = up->fgrp->fd[2];
 	if(c==nil || (c->flag&CMSG)!=0 || (c->mode!=OWRITE && c->mode!=ORDWR))
 		return 0;
 	n = snprint(buf, sizeof buf, "%s %ud: ", up->text, up->pid);
@@ -386,47 +390,71 @@
 
 enum{
 	Qdir,
+	Qbintime,
 	Qcons,
-	Qsysctl,
 	Qconsctl,
+	Qcputime,
 	Qdrivers,
+	Qhostdomain,
 	Qhostowner,
+	Qjit,
 	Qkeyboard,
-	Qklog,		/* same as 9front's kmesg */
+	Qkmesg,
 	Qkprint,	/* tail of kprint's and cons. Why not just a tail of klog? Why is this needed? */
-	Qscancode,
 	Qmemory,
-	Qmsec,
 	Qnull,
+	Qosversion,
+	Qpid,
+	Qppid,
 	Qrandom,
-	Qnotquiterandom,
+	Qreboot,
+	Qscancode,
+	Qsysctl,
 	Qsysname,
+	Qsysstat,
 	Qtime,
 	Quser,
-	Qjit,
+	Qzero,
+	Qconfig,
+	Qmordor,
 };
 
+enum
+{
+	DOMLEN=		48,	/* authentication domain name length */
+	VLNUMSIZE=	22,
+};
+
 static Dirtab consdir[]=
 {
 	".",	{Qdir, 0, QTDIR},	0,		DMDIR|0555,
+	"bintime",	{Qbintime},	24,		0664,
 	"cons",		{Qcons},	0,		0660,
 	"consctl",	{Qconsctl},	0,		0220,
-	"sysctl",	{Qsysctl},	0,		0644,
+	"cputime",	{Qcputime},	6*NUMSIZE,	0444,
 	"drivers",	{Qdrivers},	0,		0444,
+	"hostdomain",	{Qhostdomain},	DOMLEN,		0664,
 	"hostowner",	{Qhostowner},	0,	0644,
-	"jit",		{Qjit},	0,	0666,
+	"jit",		{Qjit},	0,	0666,					/* obsolete unless forth wants to use it */
 	"keyboard",	{Qkeyboard},	0,		0666,
-	"klog",		{Qklog},	0,		0444,
+	"kmesg",	{Qkmesg},	0,		0440,
 	"kprint",		{Qkprint},	0,		0444,
-	"scancode",	{Qscancode},	0,		0444,
-	"memory",	{Qmemory},	0,		0444,
-	"msec",		{Qmsec},	NUMSIZE,	0444,
+	"memory",	{Qmemory},	0,		0444,			/* not in 9front */
 	"null",		{Qnull},	0,		0666,
+	"osversion",	{Qosversion},	0,		0444,
+	"pid",		{Qpid},		NUMSIZE,	0444,
+	"ppid",		{Qppid},	NUMSIZE,	0444,
 	"random",	{Qrandom},	0,		0444,
-	"notquiterandom", {Qnotquiterandom}, 0,	0444,
+	"reboot",	{Qreboot},	0,		0220,
+	"scancode",	{Qscancode},	0,		0444,
+	"sysctl",	{Qsysctl},	0,		0644,			/* obsoleted by reboot and osversion */
 	"sysname",	{Qsysname},	0,		0664,
-	"time",		{Qtime},	0,		0664,
-	"user",		{Quser},	0,	0644,
+	"sysstat",	{Qsysstat},	0,		0664,
+	"time",		{Qtime},	NUMSIZE+3*VLNUMSIZE,	0664,
+	"user",		{Quser},	0,		0666,
+	"zero",		{Qzero},	0,		0444,
+	"config",	{Qconfig},	0,		0444,
+	"mordor",	{Qmordor},	0,		0666,
 };
 
 ulong	boottime;		/* seconds since epoch at boot */
@@ -490,14 +518,12 @@
 fddump()
 {
 	Proc *p;
-	Osenv *o;
 	int i;
 	Chan *c;
 
 	p = proctab(6);
-	o = p->env;
-	for(i = 0; i <= o->fgrp->maxfd; i++) {
-		if((c = o->fgrp->fd[i]) == nil)
+	for(i = 0; i <= p->fgrp->maxfd; i++) {
+		if((c = p->fgrp->fd[i]) == nil)
 			continue;
 		print("%d: %s\n", i, c->path == nil? "???": c->path->s);
 	}
@@ -576,8 +602,10 @@
 	c = devopen(c, omode, consdir, nelem(consdir), devgen);
 	switch((u64)c->qid.path){
 	case Qconsctl:
-		if(!iseve())
+		if(!iseve()){
+			print("consopen not eve\n");
 			error(Eperm);
+		}
 		qlock(&kbd);
 		kbd.ctl++;
 		qunlock(&kbd);
@@ -628,19 +656,25 @@
 static s32
 consread(Chan *c, void *buf, s32 n, s64 offset)
 {
-	int l;
-	Osenv *o;
-	int i;
-	char *p, tmp[128];
+	Proc *o;
+	u32 l;
+	Mach *mp;
+	char *b, *bp;
+	char tmp[256];
+	int i, k, id;
+	extern char configfile[];
 
 	if(n <= 0)
 		return n;
-	o = up->env;
+	o = up;
 	switch((u64)c->qid.path){
+
 	case Qdir:
 		return devdirread(c, buf, n, consdir, nelem(consdir), devgen);
+
 	case Qsysctl:
 		return readstr(offset, buf, n, VERSION);
+
 	case Qcons:
 		/* below belongs in kbdfs */
 		qlock(&kbd);
@@ -698,76 +732,154 @@
 		/* commented until the above is removed
 		error(Egreg); */
 
-	case Qtime:
-		snprint(tmp, sizeof(tmp), "%.lld", (vlong)mseconds()*1000);
+	case Qcputime:
+		k = offset;
+		if(k >= 6*NUMSIZE)
+			return 0;
+		if(k+n > 6*NUMSIZE)
+			n = 6*NUMSIZE - k;
+		/* easiest to format in a separate buffer and copy out */
+		for(i=0; i<6 && NUMSIZE*i<k+n; i++){
+			l = up->time[i];
+			if(i == TReal)
+				l = MACHP(0)->ticks - l;
+			readnum(0, tmp+NUMSIZE*i, NUMSIZE, tk2ms(l), NUMSIZE);
+		}
+		memmove(buf, tmp+k, n);
+		return n;
+
+	case Qjit:
+		snprint(tmp, sizeof(tmp), "%d", cflag);
 		return readstr(offset, buf, n, tmp);
 
+	case Qkmesg:
+		/*
+		 * This is unlocked to avoid tying up a process
+		 * that's writing to the buffer.  kmesg.n never 
+		 * gets smaller, so worst case the reader will
+		 * see a slurred buffer.
+		 */
+		if(offset >= kmesg.n)
+			n = 0;
+		else{
+			if(offset+n > kmesg.n)
+				n = kmesg.n - offset;
+			memmove(buf, kmesg.buf+offset, n);
+		}
+		return n;
+
+	case Qkprint:
+		return qread(kprintoq, buf, n);
+
+	case Qpid:
+		return readnum((ulong)offset, buf, n, up->pid, NUMSIZE);
+
+	case Qppid:
+		return readnum((ulong)offset, buf, n, up->parentpid, NUMSIZE);
+
+	case Qtime:
+		return readtime((ulong)offset, buf, n);
+
+	case Qbintime:
+		return readbintime(buf, n);
+
 	case Qhostowner:
-		return readstr(offset, buf, n, eve);
+		return readstr((ulong)offset, buf, n, eve);
 
+	case Qhostdomain:
+		return readstr((ulong)offset, buf, n, hostdomain);
+
 	case Quser:
-		return readstr(offset, buf, n, o->user);
+		return readstr((ulong)offset, buf, n, up->user);
 
-	case Qjit:
-		snprint(tmp, sizeof(tmp), "%d", cflag);
-		return readstr(offset, buf, n, tmp);
-
 	case Qnull:
 		return 0;
 
-	case Qmsec:
-		return readnum(offset, buf, n, TK2MS(MACHP(0)->ticks), NUMSIZE);
+	case Qconfig:
+		return readstr((ulong)offset, buf, n, conffile);
 
+	case Qsysstat:
+		b = smalloc(conf.nmach*(NUMSIZE*11+1) + 1);	/* +1 for NUL */
+		bp = b;
+		for(id = 0; id < MAXMACH; id++) {
+			if(active.machs[id]) {
+				mp = MACHP(id);
+				readnum(0, bp, NUMSIZE, id, NUMSIZE);
+				bp += NUMSIZE;
+				readnum(0, bp, NUMSIZE, mp->cs, NUMSIZE);
+				bp += NUMSIZE;
+				readnum(0, bp, NUMSIZE, mp->intr, NUMSIZE);
+				bp += NUMSIZE;
+				readnum(0, bp, NUMSIZE, mp->syscall, NUMSIZE);
+				bp += NUMSIZE;
+				readnum(0, bp, NUMSIZE, mp->pfault, NUMSIZE);
+				bp += NUMSIZE;
+				readnum(0, bp, NUMSIZE, mp->tlbfault, NUMSIZE);
+				bp += NUMSIZE;
+				readnum(0, bp, NUMSIZE, mp->tlbpurge, NUMSIZE);
+				bp += NUMSIZE;
+				readnum(0, bp, NUMSIZE, mp->load, NUMSIZE);
+				bp += NUMSIZE;
+				l = mp->perf.period;
+				if(l == 0)
+					l = 1;
+				readnum(0, bp, NUMSIZE,
+					(mp->perf.avg_inidle*100)/l, NUMSIZE);
+				bp += NUMSIZE;
+				readnum(0, bp, NUMSIZE,
+					(mp->perf.avg_inintr*100)/l, NUMSIZE);
+				bp += NUMSIZE;
+				*bp++ = '\n';
+			}
+		}
+		if(waserror()){
+			free(b);
+			nexterror();
+		}
+		n = readstr((ulong)offset, buf, n, b);
+		free(b);
+		poperror();
+		return n;
+
 	case Qsysname:
 		if(sysname == nil)
 			return 0;
-		return readstr(offset, buf, n, sysname);
+		return readstr((ulong)offset, buf, n, sysname);
 
-	case Qnotquiterandom:
-		genrandom(buf, n);
-		return n;
-
 	case Qrandom:
 		return randomread(buf, n);
 
-	case Qmemory:
-		return poolread(buf, n, offset);
-
 	case Qdrivers:
-		p = malloc(READSTR);
-		if(p == nil)
-			error(Enomem);
-		l = 0;
+		b = smalloc(READSTR);
+		k = 0;
 		for(i = 0; devtab[i] != nil; i++)
-			l += snprint(p+l, READSTR-l, "#%C %s\n", devtab[i]->dc,  devtab[i]->name);
+			k += snprint(b+k, READSTR-k, "#%C %s\n",
+				devtab[i]->dc, devtab[i]->name);
 		if(waserror()){
-			free(p);
+			free(b);
 			nexterror();
 		}
-		n = readstr(offset, buf, n, p);
-		free(p);
+		n = readstr((ulong)offset, buf, n, b);
 		poperror();
+		free(b);
 		return n;
 
-	case Qklog:
-		/*
-		 * This is unlocked to avoid tying up a process
-		 * that's writing to the buffer.  kmesg.n never 
-		 * gets smaller, so worst case the reader will
-		 * see a slurred buffer.
-		 */
-		if(offset >= kmesg.n)
-			n = 0;
-		else{
-			if(offset+n > kmesg.n)
-				n = kmesg.n - offset;
-			memmove(buf, kmesg.buf+offset, n);
-		}
+	case Qzero:
+		memset(buf, 0, n);
 		return n;
 
-	case Qkprint:
-		return qread(kprintoq, buf, n);
+	case Qmemory:
+		return poolread(buf, n, offset);
 
+	case Qmordor:
+		error("one does not simply read from mordor");
+		return 0;
+
+	case Qosversion:
+		snprint(tmp, sizeof tmp, "2000 %ud", kerndate);
+		n = readstr((ulong)offset, buf, n, tmp);
+		return n;
+
 	default:
 		print("consread %llud\n", c->qid.path);
 		error(Egreg);
@@ -778,14 +890,17 @@
 static s32
 conswrite(Chan *c, void *va, s32 n, s64 offset)
 {
-	s64 t;
+	char buf[256];
 	long l, bp;
-	char *a = va;
+	char *a;
+	Mach *mp;
+	int id;
 	Cmdbuf *cb;
 	Cmdtab *ct;
-	char buf[256];
+	s64 t;
 	int x;
 
+	a = va;
 	switch((u64)c->qid.path){
 	case Qcons:
 		/*
@@ -827,45 +942,21 @@
 
 	
 	case Qtime:
-		if(n >= sizeof(buf))
-			n = sizeof(buf)-1;
-		strncpy(buf, a, n);
-		buf[n] = 0;
-		t = strtoll(buf, 0, 0)/1000000;
-		boottime = t - TK2SEC(MACHP(0)->ticks);
-		break;
-
-	case Qhostowner:
 		if(!iseve())
 			error(Eperm);
-		if(offset != 0 || n >= sizeof(buf))
-			error(Ebadarg);
-		memmove(buf, a, n);
-		buf[n] = '\0';
-		if(n > 0 && buf[n-1] == '\n')
-			buf[--n] = 0;
-		if(n <= 0)
-			error(Ebadarg);
-		renameuser(eve, buf);
-		renameproguser(eve, buf);
-		kstrdup(&eve, buf);
-		kstrdup(&up->env->user, buf);
-		break;
+		return writetime(a, n);
 
-	case Quser:
+	case Qbintime:
 		if(!iseve())
 			error(Eperm);
-		if(offset != 0)
-			error(Ebadarg);
-		if(n <= 0 || n >= sizeof(buf))
-			error(Ebadarg);
-		strncpy(buf, a, n);
-		buf[n] = 0;
-		if(buf[n-1] == '\n')
-			buf[n-1] = 0;
-		kstrdup(&up->env->user, buf);
-		break;
+		return writebintime(a, n);
 
+	case Qhostowner:
+		return hostownerwrite(a, n);
+
+	case Qhostdomain:
+		return hostdomainwrite(a, n);
+
 	case Qjit:
 		if(n >= sizeof(buf))
 			n = sizeof(buf)-1;
@@ -877,13 +968,61 @@
 		cflag = x;
 		return n;
 
+	case Quser:
+		return userwrite(a, n);
+
 	case Qnull:
 		break;
 
+	case Qconfig:
+		error(Eperm);
+		break;
+
+	case Qreboot:
+		if(!iseve())
+			error(Eperm);
+		cb = parsecmd(a, n);
+
+		if(waserror()) {
+			free(cb);
+			nexterror();
+		}
+		ct = lookupcmd(cb, rebootmsg, nelem(rebootmsg));
+		switch(ct->index) {
+		case CMreboot:
+			/* TODO rebootcmd(cb->nf-1, cb->f+1); */
+			break;
+		case CMpanic:
+			*(ulong*)0=0;
+			panic("/dev/reboot");
+		case CMrdb:
+			if(consdebug == nil)
+				consdebug = rdb;
+			consdebug();
+			break;
+		}
+		poperror();
+		free(cb);
+		break;
+
+	case Qsysstat:
+		for(id = 0; id < MAXMACH; id++) {
+			if(active.machs[id]) {
+				mp = MACHP(id);
+				mp->cs = 0;
+				mp->intr = 0;
+				mp->syscall = 0;
+				mp->pfault = 0;
+				mp->tlbfault = 0;
+				mp->tlbpurge = 0;
+			}
+		}
+		break;
+
 	case Qsysname:
 		if(offset != 0)
 			error(Ebadarg);
-		if(n <= 0 || n >= sizeof(buf))
+		if(n <= 0 || n >= sizeof buf)
 			error(Ebadarg);
 		strncpy(buf, a, n);
 		buf[n] = 0;
@@ -891,6 +1030,10 @@
 			buf[n-1] = 0;
 		kstrdup(&sysname, buf);
 		break;
+	
+	case Qmordor:
+		error("one does not simply write into mordor");
+		return 0;
 
 	case Qsysctl:
 		if(!iseve())
@@ -983,6 +1126,203 @@
 
 	randomread(&x, sizeof(x));
 	return x;
+}
+
+static uvlong uvorder = 0x0001020304050607ULL;
+
+static uchar*
+le2vlong(vlong *to, uchar *f)
+{
+	uchar *t, *o;
+	int i;
+
+	t = (uchar*)to;
+	o = (uchar*)&uvorder;
+	for(i = 0; i < sizeof(vlong); i++)
+		t[o[i]] = f[i];
+	return f+sizeof(vlong);
+}
+
+static uchar*
+vlong2le(uchar *t, vlong from)
+{
+	uchar *f, *o;
+	int i;
+
+	f = (uchar*)&from;
+	o = (uchar*)&uvorder;
+	for(i = 0; i < sizeof(vlong); i++)
+		t[i] = f[o[i]];
+	return t+sizeof(vlong);
+}
+
+static long order = 0x00010203;
+
+static uchar*
+le2long(long *to, uchar *f)
+{
+	uchar *t, *o;
+	int i;
+
+	t = (uchar*)to;
+	o = (uchar*)&order;
+	for(i = 0; i < sizeof(long); i++)
+		t[o[i]] = f[i];
+	return f+sizeof(long);
+}
+
+static uchar*
+long2le(uchar *t, long from)
+{
+	uchar *f, *o;
+	int i;
+
+	f = (uchar*)&from;
+	o = (uchar*)&order;
+	for(i = 0; i < sizeof(long); i++)
+		t[i] = f[o[i]];
+	return t+sizeof(long);
+}
+
+char *Ebadtimectl = "bad time control";
+
+/*
+ *  like the old #c/time but with added info.  Return
+ *
+ *	secs	nanosecs	fastticks	fasthz
+ */
+static int
+readtime(ulong off, char *buf, int n)
+{
+	vlong	nsec, ticks;
+	long sec;
+	char str[7*NUMSIZE];
+
+	nsec = todget(&ticks);
+	if(fasthz == 0LL)
+		fastticks((uvlong*)&fasthz);
+	sec = nsec/1000000000ULL;
+	snprint(str, sizeof(str), "%*lud %*llud %*llud %*llud ",
+		NUMSIZE-1, sec,
+		VLNUMSIZE-1, nsec,
+		VLNUMSIZE-1, ticks,
+		VLNUMSIZE-1, fasthz);
+	return readstr(off, buf, n, str);
+}
+
+/*
+ *  set the time in seconds
+ */
+static int
+writetime(char *buf, int n)
+{
+	char b[13];
+	long i;
+	vlong now;
+
+	if(n >= sizeof(b))
+		error(Ebadtimectl);
+	strncpy(b, buf, n);
+	b[n] = 0;
+	i = strtol(b, 0, 0);
+	if(i <= 0)
+		error(Ebadtimectl);
+	now = i*1000000000LL;
+	todset(now, 0, 0);
+	return n;
+}
+
+/*
+ *  read binary time info.  all numbers are little endian.
+ *  ticks and nsec are syncronized.
+ */
+static int
+readbintime(char *buf, int n)
+{
+	int i;
+	vlong nsec, ticks;
+	uchar *b = (uchar*)buf;
+
+	i = 0;
+	if(fasthz == 0LL)
+		fastticks((uvlong*)&fasthz);
+	nsec = todget(&ticks);
+	if(n >= 3*sizeof(uvlong)){
+		vlong2le(b+2*sizeof(uvlong), fasthz);
+		i += sizeof(uvlong);
+	}
+	if(n >= 2*sizeof(uvlong)){
+		vlong2le(b+sizeof(uvlong), ticks);
+		i += sizeof(uvlong);
+	}
+	if(n >= 8){
+		vlong2le(b, nsec);
+		i += sizeof(vlong);
+	}
+	return i;
+}
+
+/*
+ *  set any of the following
+ *	- time in nsec
+ *	- nsec trim applied over some seconds
+ *	- clock frequency
+ */
+static int
+writebintime(char *buf, int n)
+{
+	uchar *p;
+	vlong delta;
+	long period;
+
+	if(--n <= 0)
+		error(Ebadtimectl);
+	p = (uchar*)buf + 1;
+	switch(*buf){
+	case 'n':
+		if(n < sizeof(vlong))
+			error(Ebadtimectl);
+		le2vlong(&delta, p);
+		todset(delta, 0, 0);
+		break;
+	case 'd':
+		if(n < sizeof(vlong)+sizeof(long))
+			error(Ebadtimectl);
+		p = le2vlong(&delta, p);
+		le2long(&period, p);
+		todset(-1, delta, period);
+		break;
+	case 'f':
+		if(n < sizeof(uvlong))
+			error(Ebadtimectl);
+		le2vlong(&fasthz, p);
+		if(fasthz <= 0)
+			error(Ebadtimectl);
+		todsetfreq(fasthz);
+		break;
+	}
+	return n+1;
+}
+
+void
+cpushutdown(void)
+{
+	int ms, once;
+
+	once = active.machs[m->machno];
+	active.machs[m->machno] = 0;
+	active.exiting = 1;
+
+	if(once)
+		iprint("cpu%d: exiting\n", m->machno);
+
+	/* wait for any other processors to shutdown */
+	spllo();
+	for(ms = 5*1000; ms > 0; ms -= TK2MS(2)){
+		delay(TK2MS(2));
+		if(memchr(active.machs, 1, MAXMACH) == nil && consactive() == 0)
+			break;
+	}
 }
 
 /* all the below belongs in kbdfs  */
--- a/os/port/devdup.c
+++ b/os/port/devdup.c
@@ -10,7 +10,7 @@
 static int
 dupgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
 {
-	Fgrp *fgrp = up->env->fgrp;
+	Fgrp *fgrp = up->fgrp;
 	Chan *f;
 	static int perm[] = { 0400, 0200, 0600, 0 };
 	int p;
@@ -83,7 +83,7 @@
 		f->offset = 0;
 	}else{
 		/* fd file */
-		f = fdtochan(up->env->fgrp, fd, openmode(omode), 0, 1);
+		f = fdtochan(up->fgrp, fd, openmode(omode), 0, 1);
 		cclose(c);
 	}
 	if(omode & OCEXEC)
@@ -108,7 +108,7 @@
 	twicefd = c->qid.path - 1;
 	fd = twicefd/2;
 	if(twicefd & 1){
-		c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
+		c = fdtochan(up->fgrp, fd, -1, 0, 1);
 		if(waserror()){
 			cclose(c);
 			nexterror();
--- a/os/port/devenv.c
+++ b/os/port/devenv.c
@@ -10,27 +10,53 @@
 
 enum
 {
+	Maxenvsize = 1*MB,
+	Maxvalsize = Maxenvsize/2,
+
 	DELTAENV = 32,
-	Maxenvsize = 16300,
 };
 
 static Egrp	*envgrp(Chan *c);
-static int	envwriteable(Chan *c);
+static int	envwritable(Chan *c);
 
 static Egrp	confegrp;	/* global environment group containing the kernel configuration */
 
+#define PATH(p,i)	((uvlong)(p) << 32 | (i))
+#define QID(quidpath)	((uint)(quidpath) & 0x7FFFFFFF)
+
 static Evalue*
-envlookup(Egrp *eg, char *name, ulong qidpath)
+envindex(Egrp *eg, uvlong qidpath)
 {
-	Evalue *e, *ee;
+	Evalue *e;
+	int i;
 
-	e = eg->ent;
-	for(ee = e + eg->nent; e < ee; e++){
-		if(e->qid.path == qidpath
-		|| (name != nil && name[0] == e->name[0] && strcmp(e->name, name) == 0))
-			return e;
+	i = QID(qidpath);
+	if(i >= eg->nent)
+		return nil;
+	e = eg->ent[i];
+	if(e != nil && e->path != qidpath)
+		return nil;
+	return e;
+}
+
+static Evalue**
+envhash(Egrp *eg, char *name)
+{
+	uint c, h = 0;
+	while(c = *name++)
+		h = h*131 + c;
+	return &eg->hash[h % ENVHASH];
+}
+
+static Evalue*
+lookupname(Evalue *e, char *name)
+{
+	while(e != nil){
+		if(strcmp(e->name, name) == 0)
+			break;
+		e = e->hash;
 	}
-	return nil;
+	return e;
 }
 
 static int
@@ -38,29 +64,41 @@
 {
 	Egrp *eg;
 	Evalue *e;
+	Qid q;
 
+	eg = envgrp(c);
 	if(s == DEVDOTDOT){
+		c->qid.vers = eg->vers;
 		devdir(c, c->qid, "#e", 0, eve, 0775, dp);
 		return 1;
 	}
-
-	eg = envgrp(c);
 	rlock(eg);
-	if(name != nil)
-		e = envlookup(eg, name, -1);
-	else if(s < eg->nent)
-		e = &eg->ent[s];
-	else
-		e = nil;
-	if(e == nil || name != nil && (strlen(e->name) >= sizeof(up->genbuf))) {
+	if((c->qid.type & QTDIR) == 0) {
+		e = envindex(eg, c->qid.path);
+		if(e == nil)
+			goto Notfound;
+	} else if(name != nil) {
+		if(strlen(name) >= sizeof(up->genbuf))
+			goto Notfound;
+		e = lookupname(*envhash(eg, name), name);
+		if(e == nil)
+			goto Notfound;
+	} else if(s < eg->nent) {
+		e = eg->ent[s];
+		if(e == nil) {
+			runlock(eg);
+			return 0;	/* deleted, try next */
+		}
+	} else {
+Notfound:
 		runlock(eg);
 		return -1;
 	}
-
 	/* make sure name string continues to exist after we release lock */
-	kstrcpy(up->genbuf, e->name, sizeof up->genbuf);
-	devdir(c, e->qid, up->genbuf, e->len, eve,
-		eg == &confegrp || eg != up->env->egrp ? 0664: 0666, dp);
+	kstrcpy(up->genbuf, e->name, sizeof(up->genbuf));
+	mkqid(&q, e->path, e->vers, QTFILE);
+	devdir(c, q, up->genbuf, e->len, eve,
+		eg == &confegrp || eg != up->egrp ? 0664: 0666, dp);
 	runlock(eg);
 	return 1;
 }
@@ -85,23 +123,41 @@
 static Walkqid*
 envwalk(Chan *c, Chan *nc, char **name, int nname)
 {
-	return devwalk(c, nc, name, nname, 0, 0, envgen);
+	return devwalk(c, nc, name, nname, nil, 0, envgen);
 }
 
 static int
 envstat(Chan *c, uchar *db, int n)
 {
-	if(c->qid.type & QTDIR)
-		c->qid.vers = envgrp(c)->vers;
-	return devstat(c, db, n, 0, 0, envgen);
+	return devstat(c, db, n, nil, 0, envgen);
 }
 
+static void*
+envrealloc(Egrp *eg, void *old, int newsize)
+{
+	int oldsize = old != nil ? msize(old) : 0;
+	void *new;
+
+	if(newsize == 0){
+		eg->alloc -= oldsize;
+		free(old);
+		return nil;
+	}
+	if(newsize < 0 || eg != &confegrp && (eg->alloc + newsize) - oldsize > Maxenvsize)
+		error(Enomem);
+	new = realloc(old, newsize);
+	if(new == nil)
+		error(Enomem);
+	eg->alloc += msize(new) - oldsize;
+	setmalloctag(new, getcallerpc(&eg));
+	return new;
+}
+
 static Chan*
 envopen(Chan *c, u32 omode)
 {
 	Egrp *eg;
 	Evalue *e;
-	int trunc;
 
 	eg = envgrp(c);
 	if(c->qid.type & QTDIR) {
@@ -109,14 +165,14 @@
 			error(Eperm);
 	}
 	else {
-		trunc = omode & OTRUNC;
-		if(omode != OREAD && !envwriteable(c))
+		int trunc = omode & OTRUNC;
+		if(omode != OREAD && !envwritable(c))
 			error(Eperm);
 		if(trunc)
 			wlock(eg);
 		else
 			rlock(eg);
-		e = envlookup(eg, nil, c->qid.path);
+		e = envindex(eg, c->qid.path);
 		if(e == nil) {
 			if(trunc)
 				wunlock(eg);
@@ -124,12 +180,12 @@
 				runlock(eg);
 			error(Enonexist);
 		}
-		if(trunc && e->value != nil) {
-			e->qid.vers++;
-			free(e->value);
-			e->value = nil;
+		if(trunc && e->len > 0) {
+			e->value = envrealloc(eg, e->value, 0);	/* free */
 			e->len = 0;
+			e->vers++;
 		}
+		c->qid.vers = e->vers;
 		if(trunc)
 			wunlock(eg);
 		else
@@ -147,12 +203,14 @@
 envcreate(Chan *c, char *name, u32 omode, u32)
 {
 	Egrp *eg;
-	Evalue *e;
+	Evalue *e, **h;
+	int n, i;
 
-	if(c->qid.type != QTDIR || !envwriteable(c))
+	if(c->qid.type != QTDIR || !envwritable(c))
 		error(Eperm);
 
-	if(strlen(name) >= sizeof(up->genbuf))
+	n = strlen(name)+1;
+	if(n > sizeof(up->genbuf))
 		error(Etoolong);
 
 	omode = openmode(omode);
@@ -163,28 +221,33 @@
 		nexterror();
 	}
 
-	if(envlookup(eg, name, -1) != nil)
+	h = envhash(eg, name);
+	if(lookupname(*h, name) != nil)
 		error(Eexist);
 
-	if(eg->nent == eg->ment){
-		Evalue *tmp;
+	for(i = eg->low; i < eg->nent; i++)
+		if(eg->ent[i] == nil)
+			break;
 
-		eg->ment += DELTAENV;
-		if((tmp = realloc(eg->ent, sizeof(eg->ent[0])*eg->ment)) == nil){
-			eg->ment -= DELTAENV;
-			error(Enomem);
-		}
-		eg->ent = tmp;
+	if(i >= eg->nent){
+		if((eg->nent % DELTAENV) == 0)
+			eg->ent = envrealloc(eg, eg->ent, (eg->nent+DELTAENV) * sizeof(Evalue*));
+		i = eg->nent++;
+		eg->ent[i] = nil;
+		eg->low = i;
 	}
-	eg->vers++;
-	e = &eg->ent[eg->nent++];
+
+	e = envrealloc(eg, nil, sizeof(Evalue)+n);
+	memmove(e->name, name, n);
 	e->value = nil;
 	e->len = 0;
-	e->name = smalloc(strlen(name)+1);
-	strcpy(e->name, name);
-	mkqid(&e->qid, ++eg->path, 0, QTFILE);
-	c->qid = e->qid;
-
+	e->vers = 0;
+	e->path = PATH(++eg->path, i);
+	e->hash = *h, *h = e;
+	eg->ent[i] = e;
+	eg->low = i+1;
+	eg->vers++;
+	mkqid(&c->qid, e->path, e->vers, QTFILE);
 	wunlock(eg);
 	poperror();
 	incref(eg);
@@ -192,7 +255,7 @@
 	c->offset = 0;
 	c->mode = omode;
 	c->flag |= COPEN;
-	return;
+	/* return c; */
 }
 
 static void
@@ -199,22 +262,35 @@
 envremove(Chan *c)
 {
 	Egrp *eg;
-	Evalue *e;
+	Evalue *e, **h;
+	int i;
 
-	if(c->qid.type & QTDIR || !envwriteable(c))
+	if(c->qid.type & QTDIR || !envwritable(c))
 		error(Eperm);
 
 	eg = envgrp(c);
 	wlock(eg);
-	e = envlookup(eg, nil, c->qid.path);
+	e = envindex(eg, c->qid.path);
 	if(e == nil){
 		wunlock(eg);
 		error(Enonexist);
 	}
-	free(e->name);
-	free(e->value);
-	*e = eg->ent[--eg->nent];
+	for(h = envhash(eg, e->name); *h != nil; h = &(*h)->hash){
+		if(*h == e){
+			*h = e->hash;
+			break;
+		}
+	}
+	i = QID(c->qid.path);
+	eg->ent[i] = nil;
+	if(i < eg->low)
+		eg->low = i;
 	eg->vers++;
+
+	/* free */
+	envrealloc(eg, e->value, 0);
+	envrealloc(eg, e, 0);
+
 	wunlock(eg);
 }
 
@@ -241,10 +317,9 @@
 {
 	Egrp *eg;
 	Evalue *e;
-	ulong offset = off;
 
 	if(c->qid.type & QTDIR)
-		return devdirread(c, a, n, 0, 0, envgen);
+		return devdirread(c, a, n, nil, 0, envgen);
 
 	eg = envgrp(c);
 	rlock(eg);
@@ -252,19 +327,17 @@
 		runlock(eg);
 		nexterror();
 	}
-
-	e = envlookup(eg, nil, c->qid.path);
+	e = envindex(eg, c->qid.path);
 	if(e == nil)
 		error(Enonexist);
-	if(offset >= e->len || e->value == nil)
+	if(off >= e->len)
 		n = 0;
-	else if(offset + n > e->len)
-		n = e->len - offset;
+	else if(off + n > e->len)
+		n = e->len - off;
 	if(n <= 0)
 		n = 0;
 	else
-		memmove(a, e->value+offset, n);
-
+		memmove(a, e->value+off, n);
 	runlock(eg);
 	poperror();
 	return n;
@@ -273,17 +346,10 @@
 static s32
 envwrite(Chan *c, void *a, s32 n, s64 off)
 {
-	char *s;
-	ulong len;
 	Egrp *eg;
 	Evalue *e;
-	ulong offset = off;
+	int diff;
 
-	if(n <= 0)
-		return 0;
-	if(offset > Maxenvsize || n > (Maxenvsize - offset))
-		error(Etoobig);
-
 	eg = envgrp(c);
 	wlock(eg);
 	if(waserror()){
@@ -290,24 +356,22 @@
 		wunlock(eg);
 		nexterror();
 	}
-
-	e = envlookup(eg, nil, c->qid.path);
+	e = envindex(eg, c->qid.path);
 	if(e == nil)
 		error(Enonexist);
-
-	len = offset+n;
-	if(len > e->len) {
-		s = realloc(e->value, len);
-		if(s == nil)
-			error(Enomem);
-		memset(s+offset, 0, n);
-		e->value = s;
-		e->len = len;
-	}
-	memmove(e->value+offset, a, n);
-	e->qid.vers++;
+	if(off > Maxvalsize || n > (Maxvalsize - off))
+		error(Etoobig);
+	diff = (off+n) - e->len;
+	if(diff > 0)
+		e->value = envrealloc(eg, e->value, e->len+diff);
+	else
+		diff = 0;
+	memmove(e->value+off, a, n);	/* might fault */
+	if(off > e->len)
+		memset(e->value+e->len, 0, off-e->len);
+	e->len += diff;
+	e->vers++;
 	eg->vers++;
-
 	wunlock(eg);
 	poperror();
 	return n;
@@ -331,38 +395,64 @@
 	envwrite,
 	devbwrite,
 	envremove,
-	devwstat
+	devwstat,
 };
 
-/*
- * kernel interface to environment variables
- */
-Egrp*
-newegrp(void)
+void
+envcpy(Egrp *to, Egrp *from)
 {
-	Egrp	*e;
+	Evalue *e, *ne, **h;
+	int i, n;
 
-	e = smalloc(sizeof(Egrp));
-	incref(e);
-	return e;
+	rlock(from);
+	if(waserror()){
+		runlock(from);
+		nexterror();
+	}
+	to->nent = 0;
+	to->ent = envrealloc(to, nil, ROUND(from->nent, DELTAENV) * sizeof(Evalue*));
+	for(i = 0; i < from->nent; i++){
+		e = from->ent[i];
+		if(e == nil)
+			continue;
+		h = envhash(to, e->name);
+		n = strlen(e->name)+1;
+		ne = envrealloc(to, nil, sizeof(Evalue)+n);
+		memmove(ne->name, e->name, n);
+		ne->value = nil;
+		ne->len = 0;
+		ne->vers = 0;
+		ne->path = PATH(++to->path, to->nent);
+		ne->hash = *h, *h = ne;
+		to->ent[to->nent++] = ne;
+		if(e->len > 0){
+			ne->value = envrealloc(to, ne->value, e->len);
+			memmove(ne->value, e->value, e->len);
+			ne->len = e->len;
+		}
+	}
+	to->low = to->nent;
+	runlock(from);
+	poperror();
 }
 
 void
 closeegrp(Egrp *eg)
 {
-	Evalue *e, *ee;
+	Evalue *e;
+	int i;
 
-	if(eg == nil)
+	if(decref(eg) || eg == &confegrp)
 		return;
-	if(decref(eg) == 0 && eg != &confegrp){
-		e = eg->ent;
-		for(ee = e + eg->nent; e < ee; e++){
-			free(e->name);
-			free(e->value);
-		}
-		free(eg->ent);
-		free(eg);
+	for(i = 0; i < eg->nent; i++){
+		e = eg->ent[i];
+		if(e == nil)
+			continue;
+		free(e->value);
+		free(e);
 	}
+	free(eg->ent);
+	free(eg);
 }
 
 static Egrp*
@@ -369,41 +459,16 @@
 envgrp(Chan *c)
 {
 	if(c->aux == nil)
-		return up->env->egrp;
+		return up->egrp;
 	return c->aux;
 }
 
 static int
-envwriteable(Chan *c)
+envwritable(Chan *c)
 {
-	return c->aux == nil || c->aux == up->env->egrp || iseve();
+	return c->aux == nil || c->aux == up->egrp || iseve();
 }
 
-/* same as envcpy() of 9front */
-void
-egrpcpy(Egrp *to, Egrp *from)
-{
-	Evalue *e, *ee, *ne;
-
-	rlock(from);
-	to->ment = ROUND(from->nent, DELTAENV);
-	to->ent = smalloc(to->ment*sizeof(to->ent[0]));
-	ne = to->ent;
-	e = from->ent;
-	for(ee = e + from->nent; e < ee; e++, ne++){
-		ne->name = smalloc(strlen(e->name)+1);
-		strcpy(ne->name, e->name);
-		if(e->value != nil){
-			ne->value = smalloc(e->len);
-			memmove(ne->value, e->value, e->len);
-			ne->len = e->len;
-		}
-		mkqid(&ne->qid, ++to->path, 0, QTFILE);
-	}
-	to->nent = from->nent;
-	runlock(from);
-}
-
 /*
  *  to let the kernel set environment variables
  */
@@ -412,7 +477,7 @@
 {
 	Chan *c;
 	char buf[2*KNAMELEN];
-
+	
 	snprint(buf, sizeof(buf), "#e%s/%s", conf?"c":"", ename);
 	c = namec(buf, Acreate, OWRITE, 0666);
 	devtab[c->type]->write(c, eval, strlen(eval), 0);
@@ -428,30 +493,31 @@
 getconfenv(void)
 {
 	Egrp *eg = &confegrp;
-	Evalue *e, *ee;
+	Evalue *e;
 	char *p, *q;
-	int n;
+	int i, n;
 
 	rlock(eg);
-	if(waserror()) {
-		runlock(eg);
-		nexterror();
+	n = 1;
+	for(i = 0; i < eg->nent; i++){
+		e = eg->ent[i];
+		if(e == nil)
+			continue;
+		n += strlen(e->name)+e->len+2;
 	}
-	
-	/* determine size */
-	n = 0;
-	e = eg->ent;
-	for(ee = e + eg->nent; e < ee; e++)
-		n += strlen(e->name) + e->len + 2;
-
-	p = malloc(n + 1);
-	if(p == nil)
+	p = malloc(n);
+	if(p == nil){
+		runlock(eg);
 		error(Enomem);
+	}
 	q = p;
-	e = eg->ent;
-	for(ee = e + eg->nent; e < ee; e++){
-		strcpy(q, e->name);
-		q += strlen(q) + 1;
+	for(i = 0; i < eg->nent; i++){
+		e = eg->ent[i];
+		if(e == nil)
+			continue;
+		n = strlen(e->name)+1;
+		memmove(q, e->name, n);
+		q += n;
 		memmove(q, e->value, e->len);
 		q[e->len] = 0;
 		/* move up to the first null */
@@ -458,8 +524,19 @@
 		q += strlen(q) + 1;
 	}
 	*q = '\0';
-	
 	runlock(eg);
-	poperror();
+
 	return p;
+}
+
+Egrp*
+newegrp(void)
+{
+	Egrp *e;
+
+	e = malloc(sizeof(Egrp));
+	if(e == nil)
+		panic("no memory for Egrp\n");
+	incref(e);
+	return e;
 }
--- a/os/port/devforth.c
+++ b/os/port/devforth.c
@@ -161,9 +161,9 @@
 			}
 		}else if(cistrncmp("KEEPFDS", s, 7) == 0){
 			s += 7;
-			p.keepfds = smalloc(up->env->fgrp->nfd*sizeof(s32));
+			p.keepfds = smalloc(up->fgrp->nfd*sizeof(s32));
 			for(i=0; ;i++,p.nkeepfds++){
-				if(i>=up->env->fgrp->nfd){
+				if(i>=up->fgrp->nfd){
 					print("should not happen\n");
 					error(Etoobig);
 				}
@@ -174,9 +174,9 @@
 			}
 		}else if(cistrncmp("CLOSEFDS", s, 8) == 0){
 			s += 8;
-			p.closefds = smalloc(up->env->fgrp->nfd*sizeof(s32));
+			p.closefds = smalloc(up->fgrp->nfd*sizeof(s32));
 			for(i=0; ;i++,p.nclosefds++){
-				if(i>=up->env->fgrp->nfd){
+				if(i>=up->fgrp->nfd){
 					print("should not happen\n");
 					error(Etoobig);
 				}
@@ -330,8 +330,6 @@
 void
 goforth(void *fmem)
 {
-	up->type = Forth;
-
 	/* load dictionary */
 	loadforthdictionary((u8*)fmem);
 	print("goforth pid %d forthmem 0x%zx end 0x%zx forthmem+RSTACK 0x%zx\n",
@@ -376,24 +374,24 @@
 	p->fpsave = up->fpsave;
 	p->nerrlab = 0;
 
-	kstrdup(&p->env->user, up->env->user);
+	kstrdup(&p->user, up->user);
 
 	if(params->newenv == 1)
 		eg = newegrp();
 	else{
-		eg = up->env->egrp;
+		eg = up->egrp;
 		if(eg != nil)
 			incref(eg);
 	}
-	p->env->egrp = eg;
+	p->egrp = eg;
 
 	pg = newpgrp();
 	if(params->newns == 0)
-		pgrpcpy(pg, up->env->pgrp);
+		pgrpcpy(pg, up->pgrp);
 	if(pg == nil)
 		panic("newforthproc: nil process group\n");
 	pg->nodevs = params->nodevs;
-	p->env->pgrp = pg;
+	p->pgrp = pg;
 
 	/*
 		shmem = 0, NOSHMEM no shared memory
@@ -410,10 +408,10 @@
 		incref(up->shm);
 	}
 
-	fg = dupfgrp(up->env->fgrp);
+	fg = dupfgrp(up->fgrp);
 	if(fg == nil)
 		panic("newforthproc: nil file descriptor group\n");
-	p->env->fgrp = fg;
+	p->fgrp = fg;
 
 	if(params->redirfds == 1){
 		/* similar to kdup() */
@@ -630,7 +628,7 @@
 		if(name != nil && strcmp(name, up->genbuf) != 0)
 			return -1;
 		mkqid(&q, Qfprocdir|((slot+1)<<QSHIFT), pid, QTDIR);
-		devdir(c, q, up->genbuf, 0, p->env->user, 0555, dp);
+		devdir(c, q, up->genbuf, 0, p->user, 0555, dp);
 		DBG("forthgen Qforthdir s %d returning Dir\n"
 				"	name %s qid.path 0x%zux slot %d qid %d qid.vers %d qid.type %d 0x%ux\n"
 				"	mode 0x%ux type 0x%ux\n",
@@ -649,11 +647,11 @@
 	switch(s){
 	case 0:
 		mkqid(&q, path|Qctl, c->qid.vers, QTFILE);
-		devdir(c, q, "ctl", 0, p->env->user, 0600, dp);
+		devdir(c, q, "ctl", 0, p->user, 0600, dp);
 		break;
 	case 1:
 		mkqid(&q, path|Qvars, c->qid.vers, QTFILE);
-		devdir(c, q, "vars", 0, p->env->user, 0600, dp);
+		devdir(c, q, "vars", 0, p->user, 0600, dp);
 		break;
 	default:
 		return -1;
@@ -756,7 +754,7 @@
 {
 	if(p == up)
 		return;
-	if(strcmp(up->env->user, "none") != 0)
+	if(strcmp(up->user, "none") != 0)
 		return;
 	if(iseve())
 		return;
@@ -778,7 +776,7 @@
 		resrcwait("no procs for kproc");
 	}
 	p->fisgo = 0; /* until the pctl message comes through */
-	kstrdup(&p->env->user, up->env->user);
+	kstrdup(&p->user, up->user);
 
 	if(fhead == nil){
 		fhead = ftail = p;
--- a/os/port/devmnt.c
+++ b/os/port/devmnt.c
@@ -283,7 +283,7 @@
 
 	r->request.type = Tauth;
 	r->request.afid = c->fid;
-	r->request.uname = up->env->user;
+	r->request.uname = up->user;
 	r->request.aname = spec;
 	mountrpc(m, r);
 
@@ -340,7 +340,7 @@
 		r->request.afid = NOFID;
 	else
 		r->request.afid = ac->fid;
-	r->request.uname = up->env->user;
+	r->request.uname = up->user;
 	r->request.aname = spec;
 	mountrpc(m, r);
 
@@ -396,6 +396,7 @@
 	alloc = 0;
 	wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
 	if(waserror()){
+print("mntwalk waserror() for mntralloc pid %ud %r\n", up->pid);
 		if(alloc && wq->clone!=nil)
 			cclose(wq->clone);
 		free(wq);
@@ -418,6 +419,7 @@
 	wq->clone = nc;
 
 	if(waserror()) {
+print("mntwalk waserror() for mountrpc pid %ud %r\n", up->pid);
 		mntfree(r);
 		nexterror();
 	}
@@ -994,13 +996,21 @@
 	t = r->reply.type;
 	switch(t) {
 	case Rerror:
+		print("mountrpc: Rerror %s proc %s pid %ud: mismatch from %s %s rep %#p tag %d fid %d T%d R%d rp %d\n",
+			r->reply.ename, up->text, up->pid, chanpath(m->c), chanpath(r->c),
+			r, r->request.tag, r->request.fid, r->request.type,
+			r->reply.type, r->reply.tag);
 		error(r->reply.ename);
 	case Rflush:
+		print("mountrpc: Rflush proc %s pid %ud: mismatch from %s %s rep %#p tag %d fid %d T%d R%d rp %d\n",
+			up->text, up->pid, chanpath(m->c), chanpath(r->c),
+			r, r->request.tag, r->request.fid, r->request.type,
+			r->reply.type, r->reply.tag);
 		error(Eintr);
 	default:
 		if(t == r->request.type+1)
 			break;
-		print("mnt: proc %s %ud: mismatch from %s %s rep %#p tag %d fid %d T%d R%d rp %d\n",
+		print("mountrpc: default proc %s pid %ud: mismatch from %s %s rep %#p tag %d fid %d T%d R%d rp %d\n",
 			up->text, up->pid, chanpath(m->c), chanpath(r->c),
 			r, r->request.tag, r->request.fid, r->request.type,
 			r->reply.type, r->reply.tag);
@@ -1018,13 +1028,13 @@
 print("mountio waserror() at the start pid %ud\n", up->pid);
 		if(m->rip == up)
 			mntgate(m);
-		if(strcmp(up->env->errstr, Eintr) != 0 || waserror()){
+		if(strcmp(up->errstr, Eintr) != 0 || waserror()){
 			r = mntflushfree(m, r);
 			switch(r->request.type){
 			case Tremove:
 			case Tclunk:
 				/* botch, abandon fid */ 
-				if(strcmp(up->env->errstr, Ehungup) != 0)
+				if(strcmp(up->errstr, Ehungup) != 0)
 					r->c->fid = 0;
 			}
 			nexterror();
@@ -1044,12 +1054,13 @@
 	n = sizeS2M(&r->request);
 	b = allocb(n);
 	if(waserror()){
+print("mountio waserror() convS2M pid %ud\n", up->pid);
 		freeb(b);
 		nexterror();
 	}
 	n = convS2M(&r->request, b->wp, n);
 	if(n <= 0 || n > m->msize) {
-		print("mountio: proc %s %lud: convS2M returned %d for tag %d fid %d T%d\n",
+		print("mountio: proc %s pid %lud: convS2M returned %d for tag %d fid %d T%d\n",
 			up->text, up->pid, n, r->request.tag, r->request.fid, r->request.type);
 		error(Emountrpc);
 	}
--- a/os/port/devpipe.c
+++ b/os/port/devpipe.c
@@ -4,30 +4,25 @@
 #include	"dat.h"
 #include	"fns.h"
 #include	"../port/error.h"
-#include "interp.h"
 
-#define NETTYPE(x)	((ulong)(x)&0x1f)
-#define NETID(x)	(((ulong)(x))>>5)
-#define NETQID(i,t)	(((i)<<5)|(t))
+#include	"netif.h"
 
 typedef struct Pipe	Pipe;
 struct Pipe
 {
 	QLock;
-	Pipe*	next;
+	Pipe	*next;
 	int	ref;
 	ulong	path;
-	Queue*	q[2];
+	long	perm;
+	Queue	*q[2];
 	int	qref[2];
-	Dirtab	*pipedir;
-	char*	user;
 };
 
-static struct
+struct
 {
 	Lock;
 	ulong	path;
-	int	pipeqsize;	
 } pipealloc;
 
 enum
@@ -37,32 +32,25 @@
 	Qdata1,
 };
 
-static 
 Dirtab pipedir[] =
 {
 	".",		{Qdir,0,QTDIR},	0,		DMDIR|0500,
-	"data",		{Qdata0},	0,			0660,
-	"data1",	{Qdata1},	0,			0660,
+	"data",		{Qdata0},	0,		0600,
+	"data1",	{Qdata1},	0,		0600,
 };
+#define NPIPEDIR 3
 
 static void
-freepipe(Pipe *p)
+pipeinit(void)
 {
-	if(p != nil){
-		free(p->user);
-		free(p->q[0]);
-		free(p->q[1]);
-		free(p->pipedir);
-		free(p);
+	if(conf.pipeqsize == 0){
+		if(conf.nmach > 1)
+			conf.pipeqsize = 256*1024;
+		else
+			conf.pipeqsize = 32*1024;
 	}
 }
 
-static void
-pipeinit(void)
-{
-	pipealloc.pipeqsize = 32*1024;
-}
-
 /*
  *  create a pipe, no streams are created until an open
  */
@@ -73,35 +61,34 @@
 	Chan *c;
 
 	c = devattach('|', spec);
-	p = malloc(sizeof(Pipe));
-	if(p == 0)
-		error(Enomem);
 	if(waserror()){
-		freepipe(p);
+		chanfree(c);
 		nexterror();
 	}
-	p->pipedir = malloc(sizeof(pipedir));
-	if (p->pipedir == 0)
-		error(Enomem);
-	memmove(p->pipedir, pipedir, sizeof(pipedir));
-	kstrdup(&p->user, up->env->user);
+	p = malloc(sizeof(Pipe));
+	if(p == 0)
+		exhausted("memory");
 	p->ref = 1;
 
-	p->q[0] = qopen(pipealloc.pipeqsize, 0, 0, 0);
-	if(p->q[0] == 0)
-		error(Enomem);
-	p->q[1] = qopen(pipealloc.pipeqsize, 0, 0, 0);
-	if(p->q[1] == 0)
-		error(Enomem);
+	p->q[0] = qopen(conf.pipeqsize, 0, 0, 0);
+	if(p->q[0] == 0){
+		free(p);
+		exhausted("memory");
+	}
+	p->q[1] = qopen(conf.pipeqsize, 0, 0, 0);
+	if(p->q[1] == 0){
+		qfree(p->q[0]);
+		free(p);
+		exhausted("memory");
+	}
 	poperror();
 
 	lock(&pipealloc);
 	p->path = ++pipealloc.path;
 	unlock(&pipealloc);
+	p->perm = pipedir[Qdata0].perm;
 
-	c->qid.path = NETQID(2*p->path, Qdir);
-	c->qid.vers = 0;
-	c->qid.type = QTDIR;
+	mkqid(&c->qid, NETQID(2*p->path, Qdir), 0, QTDIR);
 	c->aux = p;
 	c->dev = 0;
 	return c;
@@ -108,10 +95,10 @@
 }
 
 static int
-pipegen(Chan *c, char *, Dirtab *tab, int ntab, int i, Dir *dp)
+pipegen(Chan *c, char*, Dirtab *tab, int ntab, int i, Dir *dp)
 {
-	int id, len;
-	Qid qid;
+	Qid q;
+	int len;
 	Pipe *p;
 
 	if(i == DEVDOTDOT){
@@ -121,9 +108,10 @@
 	i++;	/* skip . */
 	if(tab==0 || i>=ntab)
 		return -1;
+
 	tab += i;
 	p = c->aux;
-	switch(NETTYPE(tab->qid.path)){
+	switch((ulong)tab->qid.path){
 	case Qdata0:
 		len = qlen(p->q[0]);
 		break;
@@ -134,27 +122,25 @@
 		len = tab->length;
 		break;
 	}
-	id = NETID(c->qid.path);
-	qid.path = NETQID(id, tab->qid.path);
-	qid.vers = 0;
-	qid.type = QTFILE;
-	devdir(c, qid, tab->name, len, eve, tab->perm, dp);
+	mkqid(&q, NETQID(NETID(c->qid.path), tab->qid.path), 0, QTFILE);
+	devdir(c, q, tab->name, len, eve, p->perm, dp);
 	return 1;
 }
 
 
 static Walkqid*
-pipewalk(Chan *c, Chan *nc, char **name, s32 nname)
+pipewalk(Chan *c, Chan *nc, char **name, int nname)
 {
 	Walkqid *wq;
 	Pipe *p;
 
-	p = c->aux;
-	wq = devwalk(c, nc, name, nname, p->pipedir, nelem(pipedir), pipegen);
+	wq = devwalk(c, nc, name, nname, pipedir, NPIPEDIR, pipegen);
 	if(wq != nil && wq->clone != nil && wq->clone != c){
+		p = c->aux;
 		qlock(p);
 		p->ref++;
 		if(c->flag & COPEN){
+			print("channel open in pipewalk\n");
 			switch(NETTYPE(c->qid.path)){
 			case Qdata0:
 				p->qref[0]++;
@@ -174,20 +160,18 @@
 {
 	Pipe *p;
 	Dir dir;
-	Dirtab *tab;
 
 	p = c->aux;
-	tab = p->pipedir;
 
 	switch(NETTYPE(c->qid.path)){
 	case Qdir:
-		devdir(c, c->qid, ".", 0, eve, DMDIR|0555, &dir);
+		devdir(c, c->qid, ".", 0, eve, 0555, &dir);
 		break;
 	case Qdata0:
-		devdir(c, c->qid, tab[1].name, qlen(p->q[0]), eve, tab[1].perm, &dir);
+		devdir(c, c->qid, "data", qlen(p->q[0]), eve, p->perm, &dir);
 		break;
 	case Qdata1:
-		devdir(c, c->qid, tab[2].name, qlen(p->q[1]), eve, tab[2].perm, &dir);
+		devdir(c, c->qid, "data1", qlen(p->q[1]), eve, p->perm, &dir);
 		break;
 	default:
 		panic("pipestat");
@@ -198,6 +182,36 @@
 	return n;
 }
 
+static int
+pipewstat(Chan* c, uchar* db, int n)
+{
+	int m;
+	Dir *dir;
+	Pipe *p;
+
+	p = c->aux;
+	if(strcmp(up->user, eve) != 0)
+		error(Eperm);
+	if(NETTYPE(c->qid.path) == Qdir)
+		error(Eisdir);
+
+	dir = smalloc(sizeof(Dir)+n);
+	if(waserror()){
+		free(dir);
+		nexterror();
+	}
+	m = convM2D(db, n, &dir[0], (char*)&dir[1]);
+	if(m == 0)
+		error(Eshortstat);
+	if(!emptystr(dir[0].uid))
+		error("can't change owner");
+	if(dir[0].mode != ~0UL)
+		p->perm = dir[0].mode;
+	poperror();
+	free(dir);
+	return m;
+}
+
 /*
  *  if the stream doesn't exist, create it
  */
@@ -215,25 +229,16 @@
 		return c;
 	}
 
-	openmode(omode);	/* check it */
-
 	p = c->aux;
 	qlock(p);
-	if(waserror()){
-		qunlock(p);
-		nexterror();
-	}
 	switch(NETTYPE(c->qid.path)){
 	case Qdata0:
-		devpermcheck(p->user, p->pipedir[1].perm, omode);
 		p->qref[0]++;
 		break;
 	case Qdata1:
-		devpermcheck(p->user, p->pipedir[2].perm, omode);
 		p->qref[1]++;
 		break;
 	}
-	poperror();
 	qunlock(p);
 
 	c->mode = openmode(omode);
@@ -273,7 +278,7 @@
 		}
 	}
 
-	
+
 	/*
 	 *  if both sides are closed, they are reusable
 	 */
@@ -288,7 +293,9 @@
 	p->ref--;
 	if(p->ref == 0){
 		qunlock(p);
-		freepipe(p);
+		free(p->q[0]);
+		free(p->q[1]);
+		free(p);
 	} else
 		qunlock(p);
 }
@@ -302,7 +309,7 @@
 
 	switch(NETTYPE(c->qid.path)){
 	case Qdir:
-		return devdirread(c, va, n, p->pipedir, nelem(pipedir), pipegen);
+		return devdirread(c, va, n, pipedir, NPIPEDIR, pipegen);
 	case Qdata0:
 		return qread(p->q[0], va, n);
 	case Qdata1:
@@ -331,22 +338,20 @@
 }
 
 /*
- *  a write to a closed pipe causes an exception to be sent to
- *  the prog.
+ *  a write to a closed pipe causes a note to be sent to
+ *  the process.
  */
 static s32
 pipewrite(Chan *c, void *va, s32 n, s64)
 {
 	Pipe *p;
-	Prog *r;
 
+	if(!islo())
+		print("pipewrite hi %#p\n", getcallerpc(&c));
 	if(waserror()) {
-		/* avoid exceptions when pipe is a mounted queue */
-		if((c->flag & CMSG) == 0) {
-			r = up->iprog;
-			if(r != nil && r->kill == nil)
-				r->kill = "write on closed pipe";
-		}
+		/* avoid notes when pipe is a mounted queue */
+		if((c->flag & CMSG) == 0)
+			postnote(up, 1, "sys: write on closed pipe", NUser);
 		nexterror();
 	}
 
@@ -370,20 +375,15 @@
 }
 
 static s32
-pipebwrite(Chan *c, Block *bp, u32 junk)
+pipebwrite(Chan *c, Block *bp, u32)
 {
 	long n;
 	Pipe *p;
-	Prog *r;
 
-	USED(junk);
 	if(waserror()) {
-		/* avoid exceptions when pipe is a mounted queue */
-		if((c->flag & CMSG) == 0) {
-			r = up->iprog;
-			if(r != nil && r->kill == nil)
-				r->kill = "write on closed pipe";
-		}
+		/* avoid notes when pipe is a mounted queue */
+		if((c->flag & CMSG) == 0)
+			postnote(up, 1, "sys: write on closed pipe", NUser);
 		nexterror();
 	}
 
@@ -403,42 +403,6 @@
 	}
 
 	poperror();
-	return n;
-}
-
-static s32
-pipewstat(Chan *c, uchar *dp, s32 n)
-{
-	Dir *d;
-	Pipe *p;
-	int d1;
-
-	if (c->qid.type&QTDIR)
-		error(Eperm);
-	p = c->aux;
-	if(strcmp(up->env->user, p->user) != 0)
-		error(Eperm);
-	d = smalloc(sizeof(*d)+n);
-	if(waserror()){
-		free(d);
-		nexterror();
-	}
-	n = convM2D(dp, n, d, (char*)&d[1]);
-	if(n == 0)
-		error(Eshortstat);
-	d1 = NETTYPE(c->qid.path) == Qdata1;
-	if(!emptystr(d->name)){
-		validwstatname(d->name);
-		if(strlen(d->name) >= KNAMELEN)
-			error(Efilename);
-		if(strcmp(p->pipedir[1+!d1].name, d->name) == 0)
-			error(Eexist);
-		kstrcpy(p->pipedir[1+d1].name, d->name, KNAMELEN);
-	}
-	if(d->mode != ~0UL)
-		p->pipedir[d1 + 1].perm = d->mode & 0777;
-	poperror();
-	free(d);
 	return n;
 }
 
--- a/os/port/devproc.c
+++ b/os/port/devproc.c
@@ -210,7 +210,7 @@
 		if(name != nil && strcmp(name, up->genbuf) != 0)
 			return -1;
 		mkqid(&qid, (s+1)<<QSHIFT, pid, QTDIR);
-		devdir(c, qid, up->genbuf, 0, p->env->user, 0555, dp);
+		devdir(c, qid, up->genbuf, 0, p->user, 0555, dp);
 		return 1;
 	}
 	if(c->qid.path == Qtrace){
@@ -254,7 +254,7 @@
 	}
 
 	mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE);
-	devdir(c, qid, tab->name, len, p->env->user, perm, dp);
+	devdir(c, qid, tab->name, len, p->user, perm, dp);
 	return 1;
 }
 
@@ -313,7 +313,7 @@
 {
 	if(p == up)
 		return;
-	if(strcmp(up->env->user, "none") != 0)
+	if(strcmp(up->user, "none") != 0)
 		return;
 	if(iseve())
 		return;
@@ -338,7 +338,7 @@
 		pp = proctab(i);
 		if(pp->noteid != noteid || pp->kp)
 			continue;
-		if(strcmp(pp->env->user, p->env->user) == 0){
+		if(strcmp(pp->user, p->user) == 0){
 			nonone(pp);
 			setnoteid(p, noteid);
 			return;
@@ -534,13 +534,13 @@
 		error(Eprocdied);
 
 	nonone(p);
-	if(strcmp(up->env->user, p->env->user) != 0 && !iseve())
+	if(strcmp(up->user, p->user) != 0 && !iseve())
 		error(Eperm);
 
-	if(!emptystr(d->uid) && strcmp(d->uid, p->env->user) != 0){
+	if(!emptystr(d->uid) && strcmp(d->uid, p->user) != 0){
 		if(strcmp(d->uid, "none") != 0 && !iseve())
 			error(Eperm);
-		kstrdup(&p->env->user, d->uid);
+		kstrdup(&p->user, d->uid);
 	}
 	/* p->procmode determines default mode for files in /proc */
 	if(d->mode != ~0UL)
@@ -653,7 +653,7 @@
 	char flag[10], *srv;
 	int i;
 
-	pg = p->env->pgrp;
+	pg = p->pgrp;
 	if(pg == nil || pg->dot == nil || p->pid != PID(c->qid))
 		error(Eprocdied);
 
@@ -1017,7 +1017,7 @@
 			"%11lud %11lud %11lud "
 			"%11lud %11lud %11lud\n",*/
 			"%11ud\n",
-			p->text, p->env->user, sps,
+			p->text, p->user, sps,
 /*			tk2ms(p->time[TUser]),
 			tk2ms(p->time[TSys]),
 			tk2ms(MACHP(0)->ticks - p->time[TReal]),
@@ -1274,7 +1274,7 @@
 }
 
 Dev procdevtab = {
-	'o',
+	'p',
 	"proc",
 
 	devreset,
@@ -1372,7 +1372,7 @@
 
 	if(fd < 0)
 		error(Ebadfd);
-	f = p->env->fgrp;
+	f = p->fgrp;
 	if(f == nil)
 		error(Eprocdied);
 
--- a/os/port/devready.c
+++ b/os/port/devready.c
@@ -17,7 +17,7 @@
  */
 enum
 {
-	NFD = 16,
+	NRFD = 16,
 
 	Qcanread = 0,
 };
@@ -36,7 +36,7 @@
 typedef struct Canread Canread;
 struct Canread
 {
-	Readyfd rfd[NFD];
+	Readyfd rfd[NRFD];
 	s32 nrfd;
 	Queue *commq;	/* queue for the different watcher kproc's to communicate */
 };
@@ -206,7 +206,7 @@
 			break;
 		s = p;
 		nfd++;
-		if(nfd > NFD)
+		if(nfd > NRFD)
 			error(Etoobig);
 		i = r->nrfd;
 		r->rfd[i].fd = fd;
--- a/os/port/devsd.c
+++ b/os/port/devsd.c
@@ -160,7 +160,7 @@
 	}
 	if(i >= unit->npart)
 		error(Ebadctl);
-	if(strcmp(up->env->user, pp->user) && !iseve())
+	if(strcmp(up->user, pp->user) && !iseve())
 		error(Eperm);
 	pp->valid = 0;
 	pp->vers++;
@@ -793,7 +793,7 @@
 	qlock(&unit->ctl);
 	while(waserror()){
 		/* notification of media change; go around again */
-		if(strcmp(up->env->errstr, Eio) == 0 && unit->sectors == 0 && nchange++ == 0){
+		if(strcmp(up->errstr, Eio) == 0 && unit->sectors == 0 && nchange++ == 0){
 			sdinitpart(unit);
 			continue;
 		}
@@ -1590,7 +1590,7 @@
 		break;
 	}
 
-	if(strcmp(up->env->user, perm->user) && !iseve())
+	if(strcmp(up->user, perm->user) && !iseve())
 		error(Eperm);
 
 	d = smalloc(sizeof(Dir)+n);
--- a/os/port/devsrv.c
+++ b/os/port/devsrv.c
@@ -121,7 +121,7 @@
 static int
 srvcanattach(SrvFile *d)
 {
-	if(strcmp(d->user, up->env->user) == 0)
+	if(strcmp(d->user, up->user) == 0)
 		return 1;
 
 	/*
@@ -169,8 +169,8 @@
 
 	d->ref = 1;
 	kstrdup(&d->spec, spec);
-	kstrdup(&d->user, up->env->user);
-	snprint(srvname, sizeof(srvname), "srv%d", up->env->pgrp->pgrpid);
+	kstrdup(&d->user, up->user);
+	snprint(srvname, sizeof(srvname), "srv%d", up->pgrp->pgrpid);
 	kstrdup(&d->name, srvname);
 	d->perm = DMDIR|0770;
 	mkqid(&d->qid, dev.pathgen++, 0, QTDIR);
@@ -259,7 +259,7 @@
 		nexterror();
 	}
 	devpermcheck(sf->user, sf->perm, omode);
-	if(omode&ORCLOSE && strcmp(sf->user, up->env->user) != 0)
+	if(omode&ORCLOSE && strcmp(sf->user, up->user) != 0)
 		error(Eperm);
 	if(sf->perm & DMEXCL && sf->opens != 0)
 		error(Einuse);
@@ -283,7 +283,7 @@
 	SrvFile *sf, *f;
 
 	sf = c->aux;
-	if(strcmp(up->env->user, sf->user) != 0)
+	if(strcmp(up->user, sf->user) != 0)
 		error(Eperm);
 
 	d = smalloc(sizeof(*d)+n);
@@ -502,7 +502,7 @@
 	}
 
 	noperm = 0;
-	if(remove && strcmp(sf->dir->user, up->env->user) != 0){
+	if(remove && strcmp(sf->dir->user, up->user) != 0){
 		noperm = 1;
 		remove = 0;
 	}
@@ -793,7 +793,7 @@
 	f->waitlist.prev = &f->waitlist;
 
 	kstrdup(&f->name, file);
-	kstrdup(&f->user, up->env->user);
+	kstrdup(&f->user, up->user);
 	f->perm = 0666 & (~0666 | (s->perm & 0666));
 	f->length = 0;
 	f->ref = 2;
--- a/os/port/devssl.c
+++ b/os/port/devssl.c
@@ -276,7 +276,7 @@
 			dsnew(c, pp);
 		else {
 			if((perm & (s->perm>>6)) != perm
-			   && (strcmp(up->env->user, s->user) != 0
+			   && (strcmp(up->user, s->user) != 0
 			     || (perm & s->perm) != perm))
 				error(Eperm);
 
@@ -307,7 +307,7 @@
 	s = dstate[CONV(c->qid)];
 	if(s == 0)
 		error(Ebadusefd);
-	if(strcmp(s->user, up->env->user) != 0)
+	if(strcmp(s->user, up->user) != 0)
 		error(Eperm);
 
 	dir = smalloc(sizeof(Dir)+n);
@@ -1352,7 +1352,7 @@
 	fd = strtoul(p, 0, 0);
 	if(fd < 0)
 		error(Ebadarg);
-	c = fdtochan(up->env->fgrp, fd, -1, 0, 1);	/* error check and inc ref */
+	c = fdtochan(up->fgrp, fd, -1, 0, 1);	/* error check and inc ref */
 	return c;
 }
 
@@ -1421,7 +1421,7 @@
 		dshiwat++;
 	s->state = Sincomplete;
 	s->ref = 1;
-	kstrdup(&s->user, up->env->user);
+	kstrdup(&s->user, up->user);
 	s->perm = 0660;
 	t = TYPE(ch->qid);
 	if(t == Qclonus)
--- a/os/port/dial.c
+++ b/os/port/dial.c
@@ -158,7 +158,7 @@
 	name[n] = 0;
 	for(p = name; *p == ' '; p++)
 		;
-	sprint(name, "%d", strtoul(p, 0, 0));
+	sprint(name, "%lud", strtoul(p, 0, 0));
 	p = strrchr(clone, '/');
 	*p = 0;
 	if(ds->dir)
--- a/os/port/dis.c
+++ b/os/port/dis.c
@@ -310,11 +310,16 @@
 swiprog(Prog *p)
 {
 	Proc *q, *eq;
+	char c[ERRMAX];
 
+	snprint(c, ERRMAX, "interrupted by swiprog 0x%p\n", getcallerpc(&p));
+	/* print("swiprog: %s\n", c); */
 	q = proctab(0);
 	for(eq = q+conf.nproc; q < eq; q++) {
 		if(q->iprog == p) {
-			postnote(q, 1, "interrupted", NUser);
+			/*	postnote(q, 1, "interrupted by swiprog", NUser); */
+			postnote(q, 1, c, NUser);
+			/* dumpstack(); */
 			return;
 		}
 	}
@@ -954,12 +959,12 @@
 	Prog *r;
 	Module *m;
 	int broken;
-	char *estr, msg[ERRMAX+2*KNAMELEN];
+	char *estr, msg[ERRMAX+2*KNAMELEN] = {'\0'};
 
 	estr = up->env->errstr;
 	broken = 0;
 	DBG("progexit estr %s\n", estr);
-	if(estr[0] != '\0' && strcmp(estr, Eintr) != 0 && strncmp(estr, "fail:", 5) != 0)
+	if(emptystr(up->env->errstr) == 0 && strcmp(estr, Eintr) != 0 && strncmp(estr, "fail:", 5) != 0)
 		broken = 1;
 
 	r = up->iprog;
@@ -973,21 +978,13 @@
 
 	m = R.M->m;
 	if(broken){
-		if(cflag){	/* only works on Plan9 for now */
-			DBG("progexit cflag set\n");
-			char *pc = strstr(estr, "pc=");
-
-			if(pc != nil)
-				R.PC = r->R.PC = (Inst*)strtol(pc+3, nil, 0);	/* for debugging */
-		}
-		print("[%s] Broken: \"%s\" at 0x%p\n", m->name, estr, up->env->errpc);
+		print("[%s] Broken: \"%s\" at 0x%p pid %d\n", m->name, estr, up->env->errpc, up->pid);
+		if(1) dumpstack();
 	}
-	if(r->exstr != nil)
+	if(r->exval != H)
 		DBG("progexit pid %d name %s estr %s exval %p exstr %s\n",
 			r->pid, m->name, estr, r->exval, r->exstr);
-	else
-		DBG("progexit pid %d name %s estr %s exval %p\n",
-			r->pid, m->name, estr, r->exval);
+
 	// sh.b is matching on fail: not on "<pid> fail: "
 	snprint(msg, sizeof(msg), "%d \"%s\":%s", r->pid, m->name, estr);
 	// snprint(msg, sizeof(msg), "%s", estr);
@@ -1037,6 +1034,7 @@
 		panic("Interp faults with no dis prog");
 
 	/* cause an exception in the dis prog. */
+print("disfault caller 0x%p\n", getcallerpc(&reg));
 	error(msg);
 }
 
@@ -1049,14 +1047,21 @@
 
 	startup();
 
+	/* cleans out the exception stack (error labels) */
 	while(waserror()) {
+		DBG("vmachine waserror() loop up->env->errstr %s\n",  up->env->errstr);
 		if(up->type != Interp)
 			panic("vmachine: non-interp kproc");
-		if(up->iprog != nil)
+		if(up->iprog != nil){
+			DBG("vmachine waserror() before acquire up->env->errstr %s\n",  up->env->errstr);
 			acquire();
+		}
+		DBG("vmachine waserror() after acquire up->env->errstr %s\n",  up->env->errstr);
 		if(handler(up->env->errstr) == 0) {
 			propex(currun(), up->env->errstr);
+			DBG("vmachine waserror() after propex up->env->errstr %s\n",  up->env->errstr);
 			progexit();
+			DBG("vmachine waserror() after progexit\n");
 		}
 		up->env = &up->defenv;
 	}
--- a/os/port/exportfs.c
+++ b/os/port/exportfs.c
@@ -135,7 +135,7 @@
 
 	if(waserror())
 		return -1;
-	c = fdtochan(up->env->fgrp, fd, ORDWR, 1, 1);
+	c = fdtochan(up->fgrp, fd, ORDWR, 1, 1);
 	poperror();
 
 	if(waserror()){
@@ -153,15 +153,15 @@
 	}
 
 	fs->r.ref = 1;
-	pg = up->env->pgrp;
+	pg = up->pgrp;
 	fs->pgrp = pg;
 	incref(pg);
-	eg = up->env->egrp;
+	eg = up->egrp;
 	fs->egrp = eg;
 	if(eg != nil)
 		incref(eg);
 	fs->fgrp = newfgrp(nil);
-	kstrdup(&fs->user, up->env->user);
+	kstrdup(&fs->user, up->user);
 	fs->root = dc;
 	fs->io = c;
 	fs->pathgen = 0;
@@ -354,7 +354,7 @@
 
 	if(exdebug){
 		if(n < 0)
-			print("exportproc %d shut down: %s\n", up->pid, up->env->errstr);
+			print("exportproc %d shut down: %s\n", up->pid, up->errstr);
 		else
 			print("exportproc %d shut down\n", up->pid);
 	}
@@ -550,17 +550,17 @@
 		unlock(fs);
 		unlock(&exq.l);
 
-		up->env->pgrp = q->export->pgrp;
-		up->env->egrp = q->export->egrp;
-		up->env->fgrp = q->export->fgrp;
-		kstrdup(&up->env->user, q->export->user);
+		up->pgrp = q->export->pgrp;
+		up->egrp = q->export->egrp;
+		up->fgrp = q->export->fgrp;
+		kstrdup(&up->user, q->export->user);
 
 		if(exdebug > 1)
 			print("exslave %d dispatch %F\n", up->pid, &q->in);
 
 		if(waserror()){
-			print("exslave %d err %s\n", up->pid, up->env->errstr);	/* shouldn't happen */
-			err = up->env->errstr;
+			print("exslave %d err %s\n", up->pid, up->errstr);	/* shouldn't happen */
+			err = up->errstr;
 		}else{
 			if(q->in.type >= Tmax || !fcalls[q->in.type]){
 				snprint(up->genbuf, sizeof(up->genbuf), "unknown message: %F", &q->in);
@@ -830,7 +830,7 @@
 	if(waserror()){
 		f->attached = 0;
 		Exputfid(fs, f);
-		return up->env->errstr;
+		return up->errstr;
 	}
 	f->chan = cclone(fs->root);
 	f->qid = uqidalloc(fs, f->chan);
@@ -885,7 +885,7 @@
 	}
 
 	if(waserror())
-		return up->env->errstr;
+		return up->errstr;
 	c = cclone(f->chan);
 	poperror();
 	qid = f->qid;
@@ -901,7 +901,7 @@
 					freeuqid(fs, qid);
 					Exputfid(fs, f);
 					if(i == 0)
-						return up->env->errstr;
+						return up->errstr;
 					return nil;
 				}
 				freeuqid(fs, qid);
@@ -952,7 +952,7 @@
 	if(waserror()){
 		cclose(c);
 		Exputfid(fs, f);
-		return up->env->errstr;
+		return up->errstr;
 	}
 
 	/* only save the mount head if it's a multiple element union */
@@ -996,7 +996,7 @@
 	}
 	if(waserror()){
 		Exputfid(fs, f);
-		return up->env->errstr;
+		return up->errstr;
 	}
 	validname(t->name, 0);
 	if(t->name[0] == '.' && (t->name[1] == '\0' || t->name[1] == '.' && t->name[2] == '\0'))
@@ -1062,7 +1062,7 @@
 
 	if(waserror()) {
 		Exputfid(fs, f);
-		return up->env->errstr;
+		return up->errstr;
 	}
 	c = f->chan;
 	if((c->flag & COPEN) == 0)
@@ -1127,7 +1127,7 @@
 		return Enofid;
 	if(waserror()){
 		Exputfid(fs, f);
-		return up->env->errstr;
+		return up->errstr;
 	}
 	c = f->chan;
 	if((c->flag & COPEN) == 0)
@@ -1160,7 +1160,7 @@
 	if(waserror()){
 		cclose(c);
 		Exputfid(fs, f);
-		return up->env->errstr;
+		return up->errstr;
 	}
 	n = devtab[c->type]->stat(c, r->stat, r->nstat);
 	if(n <= BIT16SZ)
@@ -1184,7 +1184,7 @@
 		return Enofid;
 	if(waserror()){
 		Exputfid(fs, f);
-		return up->env->errstr;
+		return up->errstr;
 	}
 	validstat(t->stat, t->nstat);	/* check name */
 
@@ -1215,7 +1215,7 @@
 	if(waserror()){
 		f->attached = 0;
 		Exputfid(fs, f);
-		return up->env->errstr;
+		return up->errstr;
 	}
 	c = exmount(f->chan, nil, 0);
 	if(waserror()){
--- a/os/port/netif.c
+++ b/os/port/netif.c
@@ -184,7 +184,7 @@
 		case Ndataqid:
 		case Nctlqid:
 			f = nif->f[id];
-			if(netown(f, up->env->user, omode&7) < 0)
+			if(netown(f, up->user, omode&7) < 0)
 				error(Eperm);
 			break;
 		}
@@ -216,9 +216,9 @@
 		p = malloc(READSTR);
 		if(p == nil)
 			return 0;
-		j = snprint(p, READSTR, "in: %d\n", nif->inpackets);
+		j = snprint(p, READSTR, "in: %llud\n", nif->inpackets);
 		j += snprint(p+j, READSTR-j, "link: %d\n", nif->link);
-		j += snprint(p+j, READSTR-j, "out: %d\n", nif->outpackets);
+		j += snprint(p+j, READSTR-j, "out: %llud\n", nif->outpackets);
 		j += snprint(p+j, READSTR-j, "crc errs: %d\n", nif->crcs);
 		j += snprint(p+j, READSTR-j, "overflows: %d\n", nif->overflows);
 		j += snprint(p+j, READSTR-j, "soft overflows: %d\n", nif->soverflows);
@@ -370,7 +370,7 @@
 	if(f == 0)
 		error(Enonexist);
 
-	if(netown(f, up->env->user, OWRITE) < 0)
+	if(netown(f, up->user, OWRITE) < 0)
 		error(Eperm);
 
 	dir = smalloc(sizeof(Dir)+n);
@@ -530,7 +530,7 @@
 		}
 		f->inuse = 1;
 		qreopen(f->in);
-		netown(f, up->env->user, 0);
+		netown(f, up->user, 0);
 		qunlock(f);
 		qunlock(nif);
 		poperror();
--- a/os/port/pgrp.c
+++ b/os/port/pgrp.c
@@ -283,13 +283,13 @@
 	 * If we get into trouble, forceclosefgrp
 	 * will bail us out.
 	 */
-	up->env->closingfgrp = f;
+	up->closingfgrp = f;
 	for(i = 0; i <= f->maxfd; i++)
 		if((c = f->fd[i]) != nil){
 			f->fd[i] = nil;
 			cclose(c);
 		}
-	up->env->closingfgrp = nil;
+	up->closingfgrp = nil;
 
 	free(f->fd);
 	free(f->flag);
@@ -313,12 +313,12 @@
 	Chan *c;
 	Fgrp *f;
 
-	if(up->procctl != Proc_exitme || up->env->closingfgrp == nil){
+	if(up->procctl != Proc_exitme || up->closingfgrp == nil){
 		print("bad forceclosefgrp call");
 		return;
 	}
 
-	f = up->env->closingfgrp;
+	f = up->closingfgrp;
 	for(i = 0; i <= f->maxfd; i++)
 		if((c = f->fd[i]) != nil){
 			f->fd[i] = nil;
@@ -406,3 +406,42 @@
 	(*key->pkfree)(key->pk);
 	free(key);
 }
+
+/*
+ * kernel interface to shm
+ */
+Sgrp*
+shmgrpnew(void)
+{
+	Sgrp	*e;
+
+	e = smalloc(sizeof(Sgrp));
+	incref(e);
+	return e;
+}
+
+void
+shmgrpclose(Sgrp *g)
+{
+	Svalue **ent, **eent;
+	s32 i;
+
+	if(g == nil)
+		return;
+	if(decref(g) <= 0){
+		ent = g->ent;
+		for(i = 0, eent = ent + g->nent; ent < eent; ent++, i++){
+			if(ent == nil)
+				continue;
+			wlock(*ent);
+			free((*ent)->name);
+			free((*ent)->value);
+			g->ent[i] = nil;
+			/* wunlock(ent); */
+			free(ent);
+		}
+		free(g->ent);
+		free(g);
+	}
+}
+
--- a/os/port/portdat.h
+++ b/os/port/portdat.h
@@ -27,7 +27,6 @@
 typedef struct Mnt	Mnt;
 typedef struct Mhead	Mhead;
 typedef struct Note	Note;
-typedef struct Osenv	Osenv;
 typedef struct Path	Path;
 typedef struct Perf	Perf;
 typedef struct Pgrp	Pgrp;
@@ -94,31 +93,6 @@
 	void	(*f)(void*);	/* called with VM acquire()'d */
 };
 
-struct Osenv
-{
-	char	*syserrstr;	/* last error from a system call, errbuf0 or 1 - obsolete in inferno */
-	intptr	errpc;
-	char	*errstr;	/* reason we're unwinding the error stack, errbuf1 or 0 */
-	char	errbuf0[ERRMAX];
-	char	errbuf1[ERRMAX];
-	Pgrp	*pgrp;		/* Ref to namespace, working dir and root */
-	Fgrp	*fgrp;		/* Ref to file descriptors */
-	Egrp	*egrp;	/* Environment vars */
-	Skeyset	*sigs;		/* Signed module keys */
-	Rendez	*rend;		/* Synchro point */
-	Queue	*waitq;		/* Info about dead children */
-	Queue	*childq;		/* Info about children for debuggers */
-	void	*debug;		/* Debugging master */
-	s32	uid;		/* Numeric user id for system */
-	s32	gid;		/* Numeric group id for system */
-	char	*user;		/* Inferno user name */
-	int	fpuostate;
-
-	/* from 9front */
-	Rgrp	*rgrp;		/* Rendez group */
-	Fgrp	*closingfgrp;	/* used during teardown */
-};
-
 enum
 {
 	Nopin =	-1
@@ -397,6 +371,9 @@
 	DELTAFD=		20,		/* allocation quantum for process file descriptors */
 	MAXNFD =		4000,		/* max per process file descriptors */
 	MAXKEY =		8,	/* keys for signed modules */
+	NFD =		100,		/* per process file descriptors */
+	ENVLOG =	5,
+	ENVHASH =	1<<ENVLOG,	/* Egrp hash for variable lookup */
 };
 #define REND(p,s)	((p)->rendhash[(s)&((1<<RENDLOG)-1)])
 #define MOUNTH(p,qid)	((p)->mnthash[(qid).path&((1<<MNTLOG)-1)])
@@ -455,10 +432,12 @@
 
 struct Evalue
 {
-	char	*name;
 	char	*value;
-	s32	len;
-	Qid	qid;
+	int	len;
+	ulong	vers;
+	uvlong	path;			/* qid.path: Egrp.path << 32 | index (of Egrp.ent[]) */
+	Evalue	*hash;
+	char	name[];
 };
 
 struct Egrp
@@ -465,14 +444,13 @@
 {
 	Ref;
 	RWlock;
-	union{		/* array of Evalue's */
-		Evalue	*entries;
-		Evalue	*ent;
-	};
-	int	nent;
-	int	ment;
-	u32	path;	/* qid.path of next Evalue to be allocated */
-	u32	vers;	/* of Egrp */
+	Evalue	**ent;
+	int	nent;			/* numer of slots in ent[] */
+	int	low;			/* lowest free index in ent[] */
+	int	alloc;			/* bytes allocated for env */
+	ulong	path;			/* generator for qid path */
+	ulong	vers;			/* of Egrp */
+	Evalue	*hash[ENVHASH];		/* hashtable for name lookup */
 };
 
 struct Signerkey
@@ -593,8 +571,7 @@
 	int	n;
 };
 
-/* inferno uses Osenv for environment information. It is cheaper to create
- * new processes with a default environment (spawn, not fork)
+/* Needs a default environment to create new processes (spawn, not fork)
  *
  * When using forth for the userspace, forth's stack is in fmem.
  * The kstack is used for everything else. Hence, there is no
@@ -608,7 +585,7 @@
 	char	*kstack;	/* known to l.s in 9front to switch to the kernel stack on syscallentry */
 	Mach	*mach;		/* machine running this proc */
 	char	*text;
-	char	*user;		/* inferno uses Osenv.user */
+	char	*user;
 
 	char	*args;
 	int	nargs;		/* number of bytes of args */
@@ -632,15 +609,15 @@
 	QLock	qwaitr;
 	Rendez	waitr;		/* Place to hang out in wait */
 
-/*	QLock	seglock;	*//* locked whenever seg[] changes */
-/*	Segment	*seg[NSEG]; */
+/*	QLock	seglock;	\/* locked whenever seg[] changes *\/
+	Segment	*seg[NSEG];*/
 
-/*	Pgrp	*pgrp;	*/	/* Process group for namespace */
-/*	Egrp 	*egrp;	*/	/* Environment group */
-/*	Fgrp	*fgrp;	*/	/* File descriptor group */
+	Pgrp	*pgrp;		/* Process group for namespace */
+	Egrp 	*egrp;		/* Environment group */
+	Fgrp	*fgrp;		/* File descriptor group */
 	Rgrp	*rgrp;		/* Rendez group */
 
-/*	Fgrp	*closingfgrp;*/	/* used during teardown */
+	Fgrp	*closingfgrp;	/* used during teardown */
 
 	int	insyscall;
 	u32	time[6];	/* User, Sys, Real; child U, S, R */
@@ -688,18 +665,17 @@
 	void	(*kpfun)(void*);
 	void	*kparg;
 
-/*	Sargs	s;		*//* syscall arguments */
+/*	Sargs	s;		*//* TODO uncomment this and scallnr syscall arguments */
 /*	int	scallnr;	*//* sys call number */
 	int	nerrlab;
 	Label	errlab[NERR];
-						/* below fields are in Osenv */
-/*	char	*syserrstr;	*//* last error from a system call, errbuf0 or 1 */
-/*	char	*errstr;	*//* reason we're unwinding the error stack, errbuf1 or 0 */
-/*	char	errbuf0[ERRMAX];*/
-/*	char	errbuf1[ERRMAX];*/
-	char	genbuf[ERRMAX];	/* buffer used e.g. for last name element from namec */
-/*	Chan	*slash; part of Pgrp in inferno */
-/*	Chan	*dot; part of Pgrp in inferno */
+	char	*syserrstr;	/* last error from a system call, errbuf0 or 1 */
+	char	*errstr;	/* reason we're unwinding the error stack, errbuf1 or 0 */
+	char	errbuf0[ERRMAX];
+	char	errbuf1[ERRMAX];
+	char	genbuf[128];	/* buffer used e.g. for last name element from namec */
+	Chan	*slash;
+	Chan	*dot;
 
 	Note	note[NNOTE];
 	short	nnote;
@@ -742,28 +718,15 @@
 	Watchpt	*watchpt;	/* watchpoints */
 	int	nwatchpt;
 
-	/* inferno specific fields */
-	s32		type;
-	void	*prog;		/* Dummy Prog for interp release */
-	void	*iprog;
-	Osenv	*env;
-	Osenv	defenv;
-	s32		swipend;	/* software interrupt pending for Prog TODO replace with notepending? */
-	Lock	sysio;		/* note handler lock */ 
-
-	/* inferno specific fields that are obsolete? */
-	int		fpstate;
-	int		killed;		/* by swiproc */
-	Proc	*tlink;
-	ulong	movetime;	/* next time process should switch processors */
- 	int		dbgstop;		/* don't run this kproc */
-
 	/* forth specific fields */
 	Proc	*fprev, *fnext;	/* forth processes linked list */
 	void	*fmem;			/* forth process memory - sandboxed except for macro primitives */
-	void	*shm;		/* for devshm. move this into Osenv or maybe remove Osenv at some point to get more in sync with 9front */
+	void	*shm;		/* for devshm */
 	void	*canread;	/* for devready.c */
 	u8		fisgo;	/* 0 while waiting for the pctl message */
+
+	/* not used by 9front. get rid of it at some point */
+	intptr errpc;
 };
 
 enum
@@ -812,6 +775,7 @@
 extern	int	consoleprint;
 extern	Dev*	devtab[];
 extern	char*	eve;
+extern	char	hostdomain[];
 extern	int	hwcurs;
 extern	Queue*	kprintoq;
 extern  Queue	*kbdq;
--- a/os/port/portfns.h
+++ b/os/port/portfns.h
@@ -129,6 +129,8 @@
 void 		(*hwrandbuf)(void*, u32);
 void		hnputl(void*, u32);
 void		hnputs(void*, u16);
+long		hostdomainwrite(char*, int);
+long		hostownerwrite(char*, int);
 void		hzsched(void);
 Block*		iallocb(int);
 void		iallocsummary(void);
@@ -149,10 +151,12 @@
 void		kbdprocesschar(int);	/* will go away with kbdfs */
 int		kbdputc(Queue*, int);	/* will go away with kbdfs */
 void		kbdrepeat(int);		/* will go away with kbdfs */
-void		kproc(char*, void(*)(void*), void*, int);
+int		kenter(Ureg*);
+void		kexit(Ureg*);
 int		kfgrpclose(Fgrp*, int);
-void		kprocchild(Proc*, void (*)(void*), void*);
 int		kprint(char*, ...);
+void		kproc(char*, void(*)(void*), void*, int);
+void		kprocchild(Proc*, void (*)(void*), void*);
 void	(*kproftick)(ulong);
 void		ksetenv(char*, char*, int);
 void		kstrcpy(char*, char*, int);
@@ -218,7 +222,7 @@
 void	notkilled(void);
 int		nrand(int);
 uvlong		ns2fastticks(uvlong);
-int		okaddr(ulong, ulong, int);
+int		okaddr(uintptr, u32, int);
 u32		openmode(u32);
 Block*		packblock(Block*);
 Block*		padblock(Block*, int);
@@ -292,6 +296,7 @@
 int		readnum_vlong(ulong, char*, ulong, vlong, int);
 int		readstr(ulong, char*, ulong, char*);
 void		ready(Proc*);
+void		rebootcmd(int, char**);
 void		reboot(void);
 void		renameproguser(char*, char*);
 void		renameuser(char*, char*);
@@ -310,6 +315,7 @@
 ulong		setnoteid(Proc*, ulong);
 int		setpri(int);
 void		setrealloctag(void*, uintptr);
+void		setregisters(Ureg*, char*, char*, int);
 void		setupwatchpts(Proc*, Watchpt*, int);
 Sgrp*	shmgrpnew(void);
 void	shmgrpclose(Sgrp *g);
@@ -353,7 +359,8 @@
 long		unionread(Chan*, void*, long);
 void		unlock(Lock*);
 void		userinit(void);
-ulong		userpc(void);
+uintptr		userpc(void);
+long		userwrite(char*, int);
 void		validname(char*, int);
 char*		validnamedup(char*, int);
 void		validstat(uchar*, int);
@@ -372,7 +379,7 @@
 void*		xspanalloc(uintptr, s32, uintptr);
 void		xsummary(void);
  
-void		validaddr(void*, ulong, int);
+void		validaddr(uintptr, ulong, int);
 void*	vmemchr(void*, int, int);
 void		hnputv(void*, u64);
 u64		nhgetv(void*);
--- a/os/port/proc.c
+++ b/os/port/proc.c
@@ -153,7 +153,20 @@
 }
 
 /*
+ * in 9front, userureg() checks if the current code segment
+ * is the user's code segment. We use the same segment for
+ * the kernel and the user. Hence, return !Proc.kp. Proc.kp
+ * is true for a kernel process.
+ */
 int
+userureg(Ureg *ur)
+{
+	if(ur == nil || ur->r14 == 0 || ((Proc*)(ur->r14))->kp == 0)
+		return 0;
+	return 1;
+}
+
+int
 kenter(Ureg *ureg)
 {
 	int user;
@@ -174,13 +187,12 @@
 
 	cycles(&t);
 
-	\/* precise time accounting, kernel exit *\/
+	/* precise time accounting, kernel exit */
 	tos = (Tos*)(USTKTOP-sizeof(Tos));
 	tos->kcycles += t - up->kentry;
 	tos->pcycles = t + up->pcycles;
 	tos->pid = up->pid;
 }
-*/
 
 static void
 procswitch(void)
@@ -687,16 +699,13 @@
 	p->ureg = nil;
 	p->dbgreg = nil;
 	p->nerrlab = 0;
-	p->type = Unknown;
 	p->nlocks = 0;
 	p->delaysched = 0;
 	p->trace = 0;
 
-	memset(&p->defenv, 0, sizeof(p->defenv));
-	p->env = &p->defenv;
-	kstrdup(&p->env->user, "*nouser");
-	p->env->errstr = p->env->errbuf0;
-	p->env->syserrstr = p->env->errbuf1;
+	kstrdup(&p->user, "*nouser");
+	p->errstr = p->errbuf0;
+	p->syserrstr = p->errbuf1;
 
 	/*
 	 * a user process. kproc() can change it as it needs.
@@ -878,7 +887,7 @@
 void
 interrupted(void)
 {
-	if(up->procctl == Proc_exitme && up->env->closingfgrp != nil)
+	if(up->procctl == Proc_exitme && up->closingfgrp != nil)
 		forceclosefgrp();
 	error(Eintr);
 }
@@ -1074,11 +1083,11 @@
 		break;
 	case Rendezvous:
 		/* Try and pull out of a rendezvous */
-		lock(p->env->rgrp);
+		lock(p->rgrp);
 		if(p->state == Rendezvous) {
 			Proc *d, **l;
 
-			l = &REND(p->env->rgrp, p->rendtag);
+			l = &REND(p->rgrp, p->rendtag);
 			for(d = *l; d != nil; d = d->rendhash) {
 				if(d == p) {
 					*l = p->rendhash;
@@ -1089,7 +1098,7 @@
 				l = &d->rendhash;
 			}
 		}
-		unlock(p->env->rgrp);
+		unlock(p->rgrp);
 		break;
 	}
 	return ret;
@@ -1158,6 +1167,7 @@
 }
 
 
+/*
 void
 notkilled(void)
 {
@@ -1165,6 +1175,7 @@
 	up->killed = 0;
 	unlock(&up->rlock);
 }
+*/
 
 void
 pexit(char *exitstr, int freemem)
@@ -1189,16 +1200,16 @@
 
 	/* nil out all the resources under lock (free later) */
 	qlock(&up->debug);
-	fgrp = up->env->fgrp;
-	up->env->fgrp = nil;
-	egrp = up->env->egrp;
-	up->env->egrp = nil;
-	rgrp = up->env->rgrp;
-	up->env->rgrp = nil;
-/*	dot = up->env->pgrp->dot;
-	up->env->pgrp->dot = nil;*/
-	pgrp = up->env->pgrp;
-	up->env->pgrp = nil;
+	fgrp = up->fgrp;
+	up->fgrp = nil;
+	egrp = up->egrp;
+	up->egrp = nil;
+	rgrp = up->rgrp;
+	up->rgrp = nil;
+/*	dot = up->pgrp->dot;
+	up->pgrp->dot = nil;*/
+	pgrp = up->pgrp;
+	up->pgrp = nil;
 	qunlock(&up->debug);
 
 	if(fgrp != nil)
@@ -1395,7 +1406,7 @@
 			continue;
 
 		dumpaproc(p);
-		dumppgrp("	", p->env->pgrp);
+		dumppgrp("	", p->pgrp);
 	}
 }
 
@@ -1415,20 +1426,20 @@
 
 	qlock(&p->debug);
 	if(flags & KPDUPPG) {
-		pg = up->env->pgrp;
+		pg = up->pgrp;
 		incref(pg);
-		p->env->pgrp = pg;
+		p->pgrp = pg;
 	}
 	if(flags & KPDUPFDG) {
-		fg = up->env->fgrp;
+		fg = up->fgrp;
 		incref(fg);
-		p->env->fgrp = fg;
+		p->fgrp = fg;
 	}
 	if(flags & KPDUPENVG) {
-		eg = up->env->egrp;
+		eg = up->egrp;
 		if(eg != nil)
 			incref(eg);
-		p->env->egrp = eg;
+		p->egrp = eg;
 	}
 
 	p->nnote = 0;
@@ -1448,7 +1459,7 @@
 /* this does all of the above 3 lines */
 	kprocchild(p, func, arg);
 
-	kstrdup(&p->env->user, up->env->user);
+	kstrdup(&p->user, up->user);
 	kstrdup(&p->text, name);
 	kstrdup(&p->args, "");
 	p->nargs = 0;
@@ -1562,15 +1573,15 @@
 		panic("error stack too deep");
 	if(err == nil)
 		panic("error: nil parameter");
-	kstrcpy(up->env->errstr, err, ERRMAX);
+	kstrcpy(up->errstr, err, ERRMAX);
 	setlabel(&up->errlab[NERR-1]);
 	if(emptystr(err) == 1){
 		DBG("error nil error err %s caller 0x%p up->pid %d\n", err, getcallerpc(&err), up->pid);
-		up->env->errpc = 0;
+		up->errpc = 0;
 		/* showerrlabs("error == nil"); */
 	}else{
 		DBG("error err %s caller 0x%p up->pid %d\n", err, getcallerpc(&err), up->pid);
-		up->env->errpc = getcallerpc(&err);
+		up->errpc = getcallerpc(&err);
 		/* proactively show issues */
 		/* print("up->nerrlab %d error %s raised by 0x%zx\n",
 			up->nerrlab, err, getcallerpc(&err)); */
@@ -1595,8 +1606,8 @@
 
 	char tmp[ERRMAX];
 
-	kstrcpy(tmp, up->env->errstr, sizeof(tmp));
-	kstrcpy(up->env->errstr, err, ERRMAX);
+	kstrcpy(tmp, up->errstr, sizeof(tmp));
+	kstrcpy(up->errstr, err, ERRMAX);
 	kstrcpy(err, tmp, size);
 }
 
@@ -1606,8 +1617,8 @@
 {
 	char tmp[ERRMAX];
 
-	kstrcpy(tmp, up->env->errstr, sizeof(tmp));
-	kstrcpy(up->env->errstr, err, ERRMAX);
+	kstrcpy(tmp, up->errstr, sizeof(tmp));
+	kstrcpy(up->errstr, err, ERRMAX);
 	kstrcpy(err, tmp, size);
 }
 
@@ -1621,7 +1632,7 @@
 	va_start(arg, fmt);
 	vseprint(buf, buf+sizeof(buf), fmt, arg);
 	va_end(arg);
-	kstrcpy(up->env->errstr, buf, ERRMAX);
+	kstrcpy(up->errstr, buf, ERRMAX);
 }
 
 void
@@ -1633,7 +1644,7 @@
 	va_start(arg, fmt);
 	vseprint(buf, buf+sizeof(buf), fmt, arg);
 	va_end(arg);
-	kstrcpy(up->env->errstr, buf, ERRMAX);
+	kstrcpy(up->errstr, buf, ERRMAX);
 }
 
 /* for dynamic modules - functions not macros */
@@ -1654,7 +1665,7 @@
 char*
 enverror(void)
 {
-	return up->env->errstr;
+	return up->errstr;
 }
 
 void
@@ -1677,13 +1688,13 @@
 setid(char *name, int owner)
 {
 	if(!owner || iseve())
-		kstrdup(&up->env->user, name);
+		kstrdup(&up->user, name);
 }
 
 /* TODO no idea what this rptproc() does
  * something to do with repeat of tk actions
  */
-void
+/*void
 rptwakeup(void *o, void *ar)
 {
 	Rept *r;
@@ -1735,7 +1746,7 @@
 		poperror();
 		if(i == -1)
 			goto Wait;
-		if(i == 0)
+		if(i == 0) 
 			continue;
 		then = now;
 		acquire();
@@ -1766,7 +1777,7 @@
 	kproc(s, rproc, r, KPDUP);
 	return r;
 }
-
+*/
 s32
 getpid(void)
 {
--- a/os/port/sysfile.c
+++ b/os/port/sysfile.c
@@ -87,7 +87,7 @@
 	int fd, flag;
 	Fgrp *f;
 
-	f = up->env->fgrp;
+	f = up->fgrp;
 	lock(f);
 	fd = findfreefd(f, 0);
 	if(fd < 0){
@@ -113,7 +113,7 @@
 {
 	Fgrp *f;
 
-	f = up->env->fgrp;
+	f = up->fgrp;
 	lock(f);
 	fd[0] = findfreefd(f, 0);
 	if(fd[0] < 0){
@@ -249,7 +249,7 @@
 		return -1;
 
 	c = namec(path, Atodir, 0, 0);
-	pg = up->env->pgrp;
+	pg = up->pgrp;
 	cclose(pg->dot);
 	pg->dot = c;
 	poperror();
@@ -277,7 +277,7 @@
 int
 kclose(int fd)
 {
-	return kfgrpclose(up->env->fgrp, fd);
+	return kfgrpclose(up->fgrp, fd);
 }
 
 int
@@ -309,12 +309,12 @@
 {
 	int fd;
 	Chan *c, *oc;
-	Fgrp *f = up->env->fgrp;
+	Fgrp *f = up->fgrp;
 
 	if(waserror())
 		return -1;
 
-	c = fdtochan(up->env->fgrp, old, -1, 0, 1);
+	c = fdtochan(up->fgrp, old, -1, 0, 1);
 	if(c->qid.type & QTAUTH)
 		error(Eperm);
 	fd = new;
@@ -355,7 +355,7 @@
 	if(waserror())
 		return -1;
 
-	c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
+	c = fdtochan(up->fgrp, fd, -1, 0, 1);
 	if(waserror()) {
 		cclose(c);
 		nexterror();
@@ -377,7 +377,7 @@
 
 	if(waserror())
 		return nil;
-	c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
+	c = fdtochan(up->fgrp, fd, -1, 0, 1);
 	s = nil;
 	if(c->path != nil){
 		s = malloc(c->path->len+1);
@@ -401,7 +401,7 @@
 		return -1;
 
 	validname(aname, 1);
-	c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1);
+	c = fdtochan(up->fgrp, fd, ORDWR, 0, 1);
 	if(waserror()){
 		cclose(c);
 		nexterror();
@@ -441,7 +441,7 @@
 	if(arglen==0 || memchr(vers, 0, arglen)==0)
 		error(Ebadarg);
 
-	c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1);
+	c = fdtochan(up->fgrp, fd, ORDWR, 0, 1);
 	if(waserror()){
 		cclose(c);
 		nexterror();
@@ -774,7 +774,7 @@
 		return -1;
 
 	validstat(buf, n);
-	c = fdtochan(up->env->fgrp, fd, -1, 1, 1);
+	c = fdtochan(up->fgrp, fd, -1, 1, 1);
 	return (wstat(c, buf, n));
 }
 
@@ -794,11 +794,11 @@
 			nexterror();
 		}
 
-		if(up->env->pgrp->noattach)
+		if(up->pgrp->noattach)
 			error(Enoattach);
 
 		ac = nil;
-		bc = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1);
+		bc = fdtochan(up->fgrp, fd, ORDWR, 0, 1);
 		if(waserror()) {
 			if(ac != nil)
 				cclose(ac);
@@ -807,7 +807,7 @@
 		}
 
 		if(afd >= 0)
-			ac = fdtochan(up->env->fgrp, afd, ORDWR, 0, 1);
+			ac = fdtochan(up->fgrp, afd, ORDWR, 0, 1);
 
 		c0 = mntattach(bc, ac, spec, flag&MCACHE);
 		poperror();	/* ac bc */
@@ -837,7 +837,7 @@
 	poperror();
 	cclose(c0);
 	if(ismount){
-		fdclose(up->env->fgrp, fd, 0);
+		fdclose(up->fgrp, fd, 0);
 		poperror();
 		free(spec);
 	}
@@ -987,7 +987,7 @@
 		return -1;
 	}
 
-	c = fdtochan(up->env->fgrp, fd, OREAD, 1, 1);
+	c = fdtochan(up->fgrp, fd, OREAD, 1, 1);
 
 	if(waserror()){
 		print("rread fd %d p 0x%p n %d fdtochan failed: %r\n", fd, p, n);
@@ -1122,7 +1122,7 @@
 	int n;
 	s64 off;
 
-	c = fdtochan(up->env->fgrp, fd, -1, 1, 1);
+	c = fdtochan(up->fgrp, fd, -1, 1, 1);
 	if(waserror()){
 		cclose(c);
 		nexterror();
@@ -1261,7 +1261,7 @@
 	s64 off;
 
 	n = 0;
-	c = fdtochan(up->env->fgrp, fd, OWRITE, 1, 1);
+	c = fdtochan(up->fgrp, fd, OWRITE, 1, 1);
 	if(c == nil)
 		print("rwrite fdtochan nil %r\n");
 	if(waserror()) {
@@ -1419,7 +1419,7 @@
 	if(waserror())
 		return nil;
 
-	c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
+	c = fdtochan(up->fgrp, fd, -1, 0, 1);
 	if(waserror()) {
 		cclose(c);
 		nexterror();
@@ -1540,7 +1540,7 @@
 	Chan *c;
 	int n;
 
-	c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
+	c = fdtochan(up->fgrp, fd, -1, 0, 1);
 	if(waserror()){
 		cclose(c);
 		return 0;	/* n.b. */