shithub: riscv

Download patch

ref: 8757154228aeef25a2e7bebada5b10a3a5df5900
parent: d44d67979356e66cfeed0dbf88f9a66322972428
parent: 127f8f048047e03d74001c4bbd50b7c0c09d7c0e
author: mischief <mischief@offblast.org>
date: Sat Dec 20 16:42:55 EST 2014

merge

diff: cannot open b/sys/src/cmd/disk/vblade//null: file does not exist: 'b/sys/src/cmd/disk/vblade//null'
--- a/lib/rob
+++ b/lib/rob
@@ -262,3 +262,6 @@
 Wait, teletypes are finally gone so we need *pseudo* teletypes?
 I started complaining about this long ago and nobody cared then, either.
 It is very unlikely to change.
+Don't do that.
+This is just me speaking, not the Go team.
+Go doesn't have it.
--- a/lib/theo
+++ b/lib/theo
@@ -420,3 +420,6 @@
 Sorry.
 Violates POSIX and C89, which violate best practice in this century.
 Look, this is rather simple.
+Oh really.
+It ends badly.
+I explained that.
--- a/lib/troll
+++ b/lib/troll
@@ -194,3 +194,4 @@
 Is there anyway this github repo could be synchronized with the main plan9-labs repo?
 Has anyone used Plan-9 have feedback on running it?
 UX-wise (and generally speaking, for most common uses of a computer these days), Plan9 is, sadly, almost useless.
+Most of the complaining about fossil's stability comes from outdated info.
--- a/sys/games/lib/fortunes
+++ b/sys/games/lib/fortunes
@@ -5108,3 +5108,6 @@
 I'm kind of like a designer who writes code; this may become apparent as you read on
 As an interface designer Plan9 have always fascinated me yet I found it incredible hard to find many other designers who were into it's concepts.
 Tandy Leather, which later grew in to the Tandy Corporation, was a family-owned leather goods company based in Fort Worth, Texas.
+I want to get into web development.
+The modern Plan 9 codebase is officially developed and maintained by Vita Nuova as the product named Inferno.
+User interface improvements included the "layers" windowing system for the DMD 5620 graphics terminal, and the SVR3.2 curses libraries that offered eight or more color pairs and other at this time important features (forms, panels, menus, etc.).
--- a/sys/man/1/sam
+++ b/sys/man/1/sam
@@ -496,11 +496,20 @@
 Send the range to the standard input of the
 Plan 9 command.
 .TP
+.BI ^ " Plan 9-command
+Send the standard output of the Plan 9 command
+to the command window.
+.TP
 .BI | " Plan 9-command
 Send the range to the standard input, and replace it by
 the standard output, of the
 Plan 9 command.
 .TP
+.BI _ " Plan 9-command
+Send the range to the standard input, and send the
+standard output of the Plan 9 command to the command
+window.
+.TP
 .BI \*a! " Plan 9-command
 Run the
 Plan 9 command.
@@ -515,6 +524,8 @@
 In any of
 .BR < ,
 .BR > ,
+.BR ^ ,
+.BR _ ,
 .B |
 or
 .BR ! ,
--- a/sys/man/3/aoe
+++ b/sys/man/3/aoe
@@ -271,7 +271,7 @@
 .\" .IR vblade (1),
 .IR sd (3),
 .IR sdaoe (3),
-.IR aoesrv (8),
+.IR vblade (8),
 .IR snoopy (8)
 .br
 .BR http://www.coraid.com/documents/AoEr10.txt
--- a/sys/man/3/sdaoe
+++ b/sys/man/3/sdaoe
@@ -68,7 +68,7 @@
 .IP
 .EX
 aoeif=ether0 ether1
-aoedev=e!#æ/aoe/42.0
+aoedev=e!42.0
 .EE
 .SH SOURCE
 .B /sys/src/9/port/sdaoe.c
--- a/sys/man/4/hgfs
+++ b/sys/man/4/hgfs
@@ -122,5 +122,10 @@
 Same as
 .B files,
 but contains only the changed files of the changeset.
+.SH SOURCE
+.B /sys/src/cmd/hgfs
 .SH SEE ALSO
 .IR hg (1)
+.SH HISTORY
+.I Hgfs
+first appeared in 9front (June, 2011).
--- /dev/null
+++ b/sys/man/8/vblade
@@ -1,0 +1,85 @@
+.TH VBLADE 8
+.SH NAME
+vblade \- virtual AoE target
+.SH SYNOPSIS
+.B disk/vblade
+[
+.B -ir
+] [
+.B -s
+.I size
+] [
+.B -a
+.I shelf.slot
+] [
+.B -c
+.I config
+]
+.B file
+[
+.B -e
+ether
+]
+.SH DESCRIPTION
+For each
+.I file
+specified,
+.I vblade
+serves it as an AoE (ATA-over-Ethernet) target via
+the specified interfaces.  The default interface is
+.BR /net/ether0 .
+Since AoE uses raw Ethernet frames, the target is
+only visible on the local ethernet segment.
+.PP
+All target-related options are reset for each
+.IR file .
+.SS Options
+.TP \w'\fL-e\ \fIetherXX'u
+.B -i
+Initialize the configuration header in 
+.IR file .
+All previous configuration information is lost.  Without this option,
+configuration is read from
+.I file
+and command like options override previous settings.
+.TP
+.B -r
+Raw.  Do not use a configuration header.  This is useful when
+exporting a device or file not generally exported by
+.IR vblade .
+.TP
+.BI -s " n"
+The exported target will have size
+.IR n ,
+rather than the available space in the target.  A size may end in
+.LR p ,
+.LR t ,
+.LR g ,
+.LR m ,
+or
+.LR k
+to specify a customary
+.I binary
+multiplier.
+.TP
+.BI -a " m.n"
+Specify the shelf and slot (or major and minor) address of the target.
+Valid shelf numbers are between 0 and 65534.  Valid slots are 0-255.
+.TP
+.BI -c " s"
+Set the AoE config string to
+.IR s .
+.TP
+.BI -e " ether"
+Listen to the network port
+.IR ether.
+Multiple ports may be specified.
+.SH SEE ALSO
+.IR aoe (3),
+.IR sdaoe (3),
+.IR http://www.coraid.com/documents/AoEr11.txt .
+.SH SOURCE
+.B /sys/src/cmd/disk/vblade
+.SH BUGS
+Security depends on control of the local Ethernet segment.  It may be
+unwise to serve AoE on a segment bridged to a wireless network.
--- a/sys/src/9/pc/archacpi.c
+++ b/sys/src/9/pc/archacpi.c
@@ -625,12 +625,42 @@
 	mpinit();
 }
 
+static void
+acpireset(void)
+{
+	uchar *p;
+	Tbl *t;
+	int i;
+
+	/* stop application processors */
+	mpshutdown();
+
+	/* locate and write platform reset register */
+	for(i=0; i < ntblmap; i++){
+		t = tblmap[i];
+		if(memcmp(t->sig, "FACP", 4) != 0)
+			continue;
+		if(get32(t->len) <= 128)
+			break;
+		p = (uchar*)t;
+		if((get32(p + 112) & (1<<10)) == 0)
+			break;
+		if(p[116+0] != IoSpace)
+			break;
+		outb(get32(p+116+4), p[128]);
+		break;
+	}
+
+	/* acpi shutdown failed, try generic reset */
+	archreset();
+}
+
 static int identify(void);
 
 PCArch archacpi = {
 .id=		"ACPI",	
 .ident=		identify,
-.reset=		mpshutdown,
+.reset=		acpireset,
 .intrinit=	acpiinit,
 .intrenable=	mpintrenable,
 .intron=	lapicintron,
@@ -886,30 +916,4 @@
 amldelay(int us)
 {
 	microdelay(us);
-}
-
-/*
- * reset machine by writing acpi reset register.
- */
-void
-acpireset(void)
-{
-	uchar *p;
-	Tbl *t;
-	int i;
-
-	for(i=0; i < ntblmap; i++){
-		t = tblmap[i];
-		if(memcmp(t->sig, "FACP", 4) != 0)
-			continue;
-		if(get32(t->len) <= 128)
-			break;
-		p = (uchar*)t;
-		if((get32(p + 112) & (1<<10)) == 0)
-			break;
-		if(p[116+0] != IoSpace)
-			break;
-		outb(get32(p+116+4), p[128]);
-		break;
-	}
 }
--- a/sys/src/9/pc/archmp.c
+++ b/sys/src/9/pc/archmp.c
@@ -354,12 +354,22 @@
 	mpinit();
 }
 
+static void
+mpreset(void)
+{
+	/* stop application processors */
+	mpshutdown();
+
+	/* do generic reset */
+	archreset();
+}
+
 static int identify(void);
 
 PCArch archmp = {
 .id=		"_MP_",	
 .ident=		identify,
-.reset=		mpshutdown,
+.reset=		mpreset,
 .intrinit=	pcmpinit,
 .intrenable=	mpintrenable,
 .intron=	lapicintron,
--- a/sys/src/9/pc/devarch.c
+++ b/sys/src/9/pc/devarch.c
@@ -528,7 +528,7 @@
 {
 }
 
-static void
+void
 archreset(void)
 {
 	i8042reset();
@@ -548,6 +548,7 @@
 	outb(0xcf9, 0x02);
 	outb(0xcf9, 0x06);
 
+	print("can't reset\n");
 	for(;;)
 		idle();
 }
--- a/sys/src/9/pc/fns.h
+++ b/sys/src/9/pc/fns.h
@@ -1,9 +1,9 @@
 #include "../port/portfns.h"
 
 void	aamloop(int);
-void	acpireset(void);
 Dirtab*	addarchfile(char*, int, long(*)(Chan*,void*,long,vlong), long(*)(Chan*,void*,long,vlong));
 void	archinit(void);
+void	archreset(void);
 int	bios32call(BIOS32ci*, u16int[3]);
 int	bios32ci(BIOS32si*, BIOS32ci*);
 void	bios32close(BIOS32si*);
--- a/sys/src/9/pc/main.c
+++ b/sys/src/9/pc/main.c
@@ -121,7 +121,6 @@
 	}
 }
 
-extern void mmuinit0(void);
 extern void (*i8237alloc)(void);
 extern void bootscreeninit(void);
 
@@ -138,8 +137,6 @@
 	print("\nPlan 9\n");
 
 	trapinit0();
-	mmuinit0();
-
 	kbdinit();
 	i8253init();
 	cpuidentify();
@@ -908,8 +905,8 @@
 	once = active.machs & (1<<m->machno);
 	/*
 	 * setting exiting will make hzclock() on each processor call exit(0),
-	 * which calls shutdown(0) and arch->reset(), which on mp systems is
-	 * mpshutdown, from which there is no return: the processor is idled
+	 * which calls shutdown(0) and arch->reset(), which on mp systems calls
+	 * mpshutdown(), from which there is no return: the processor is idled
 	 * or initiates a reboot.  clearing our bit in machs avoids calling
 	 * exit(0) from hzclock() on this processor.
 	 */
--- a/sys/src/9/pc/mmu.c
+++ b/sys/src/9/pc/mmu.c
@@ -67,12 +67,6 @@
 #define	vpd (vpt+VPTX(VPT))
 
 void
-mmuinit0(void)
-{
-	memmove(m->gdt, gdt, sizeof gdt);
-}
-
-void
 mmuinit(void)
 {
 	ulong x, *p;
--- a/sys/src/9/pc/mp.c
+++ b/sys/src/9/pc/mp.c
@@ -562,7 +562,6 @@
 	return -1;
 }
 
-
 void
 mpshutdown(void)
 {
@@ -572,7 +571,7 @@
 	if(m->machno != 0){
 		splhi();
 		arch->introff();
-		idle();
+		for(;;) idle();
 	}
 
 	print("mpshutdown: active = %#8.8ux\n", active.machs);
@@ -585,24 +584,4 @@
 	lapicicrw(0, 0x000C0000|ApicINIT);
 
 	pcireset();
-	acpireset();
-	i8042reset();
-
-	/*
-	 * Often the BIOS hangs during restart if a conventional 8042
-	 * warm-boot sequence is tried. The following is Intel specific and
-	 * seems to perform a cold-boot, but at least it comes back.
-	 * And sometimes there is no keyboard...
-	 *
-	 * The reset register (0xcf9) is usually in one of the bridge
-	 * chips. The actual location and sequence could be extracted from
-	 * ACPI but why bother, this is the end of the line anyway.
-	 */
-	print("no kbd; trying bios warm boot...");
-	*(ushort*)KADDR(0x472) = 0x1234;	/* BIOS warm-boot flag */
-	outb(0xCF9, 0x02);
-	outb(0xCF9, 0x06);
-
-	print("can't reset\n");
-	idle();
 }
--- a/sys/src/9/pc64/fns.h
+++ b/sys/src/9/pc64/fns.h
@@ -1,9 +1,9 @@
 #include "../port/portfns.h"
 
 void	aamloop(int);
-void	acpireset(void);
 Dirtab*	addarchfile(char*, int, long(*)(Chan*,void*,long,vlong), long(*)(Chan*,void*,long,vlong));
 void	archinit(void);
+void	archreset(void);
 int	bios32call(BIOS32ci*, u16int[3]);
 int	bios32ci(BIOS32si*, BIOS32ci*);
 void	bios32close(BIOS32si*);
--- a/sys/src/9/pc64/main.c
+++ b/sys/src/9/pc64/main.c
@@ -537,8 +537,8 @@
 	once = active.machs & (1<<m->machno);
 	/*
 	 * setting exiting will make hzclock() on each processor call exit(0),
-	 * which calls shutdown(0) and arch->reset(), which on mp systems is
-	 * mpshutdown, from which there is no return: the processor is idled
+	 * which calls shutdown(0) and arch->reset(), which on mp systems calls
+	 * mpshutdown(), from which there is no return: the processor is idled
 	 * or initiates a reboot.  clearing our bit in machs avoids calling
 	 * exit(0) from hzclock() on this processor.
 	 */
--- a/sys/src/9/pc64/pc64
+++ b/sys/src/9/pc64/pc64
@@ -28,7 +28,7 @@
 
 	sd
 #	floppy		dma
-#	aoe
+	aoe
 #	lpt
 
 	audio		dma
@@ -87,7 +87,7 @@
 	archmp		mp apic squidboy
 	mtrr
 
-#	sdaoe
+	sdaoe
 	sdide		pci sdscsi
 #	sd53c8xx	pci sdscsi
 #	sdmylex		pci sdscsi
@@ -95,7 +95,7 @@
 #	sdodin		pci sdscsi led
 	sdvirtio	pci sdscsi
 	sdmmc		pci pmmc
-#	sdloop
+	sdloop
 
 	uarti8250
 #	uartisa
--- a/sys/src/9/pc64/pccpu64
+++ b/sys/src/9/pc64/pccpu64
@@ -28,7 +28,7 @@
 
 	sd
 #	floppy		dma
-#	aoe
+	aoe
 #	lpt
 
 	audio		dma
@@ -87,7 +87,7 @@
 	archmp		mp apic squidboy
 	mtrr
 
-#	sdaoe
+	sdaoe
 	sdide		pci sdscsi
 #	sd53c8xx	pci sdscsi
 #	sdmylex		pci sdscsi
@@ -94,8 +94,8 @@
 	sdiahci		pci sdscsi led
 #	sdodin		pci sdscsi led
 	sdvirtio	pci sdscsi
-#	sdmmc		pci pmmc
-#	sdloop
+	sdmmc		pci pmmc
+	sdloop
 
 	uarti8250
 #	uartisa
--- a/sys/src/9/port/devdup.c
+++ b/sys/src/9/port/devdup.c
@@ -109,7 +109,7 @@
 	fd = twicefd/2;
 	if(twicefd & 1){
 		c = fdtochan(fd, -1, 0, 1);
-		procfdprint(c, fd, 0, buf, sizeof buf);
+		procfdprint(c, fd, buf, sizeof buf);
 		cclose(c);
 		return readstr((ulong)offset, va, n, buf);
 	}
--- a/sys/src/9/port/devproc.c
+++ b/sys/src/9/port/devproc.c
@@ -155,7 +155,6 @@
 int	procctlmemio(Proc*, uintptr, int, void*, int);
 Chan*	proctext(Chan*, Proc*);
 int	procstopped(void*);
-void	mntscan(Mntwalk*, Proc*);
 ulong	procpagecount(Proc *);
 
 static Traceevent *tevents;
@@ -400,6 +399,7 @@
 	case Qkregs:
 	case Qsegment:
 	case Qprofile:
+	case Qns:
 	case Qfd:
 		if(omode != OREAD)
 			error(Eperm);
@@ -428,12 +428,6 @@
 		nonone(p);
 		break;
 
-	case Qns:
-		if(omode != OREAD)
-			error(Eperm);
-		c->aux = smalloc(sizeof(Mntwalk));
-		break;
-
 	case Qnotepg:
 		nonone(p);
 		pg = p->pgrp;
@@ -514,103 +508,6 @@
 	return n;
 }
 
-
-static long
-procoffset(long offset, char *va, int *np)
-{
-	if(offset > 0) {
-		offset -= *np;
-		if(offset < 0) {
-			memmove(va, va+*np+offset, -offset);
-			*np = -offset;
-		}
-		else
-			*np = 0;
-	}
-	return offset;
-}
-
-static int
-procqidwidth(Chan *c)
-{
-	char buf[32];
-
-	return sprint(buf, "%lud", c->qid.vers);
-}
-
-int
-procfdprint(Chan *c, int fd, int w, char *s, int ns)
-{
-	int n;
-
-	if(w == 0)
-		w = procqidwidth(c);
-	n = snprint(s, ns, "%3d %.2s %C %4ld (%.16llux %*lud %.2ux) %5ld %8lld %s\n",
-		fd,
-		&"r w rw"[(c->mode&3)<<1],
-		devtab[c->type]->dc, c->dev,
-		c->qid.path, w, c->qid.vers, c->qid.type,
-		c->iounit, c->offset, c->path->s);
-	return n;
-}
-
-static int
-procfds(Proc *p, char *va, int count, long offset)
-{
-	Fgrp *f;
-	Chan *c;
-	char buf[256];
-	int n, i, w, ww;
-	char *a;
-
-	/* print to buf to avoid holding fgrp lock while writing to user space */
-	if(count > sizeof buf)
-		count = sizeof buf;
-	a = buf;
-
-	eqlock(&p->debug);
-	f = p->fgrp;
-	if(f == nil || p->dot == nil){
-		qunlock(&p->debug);
-		return 0;
-	}
-	lock(f);
-	if(waserror()){
-		unlock(f);
-		qunlock(&p->debug);
-		nexterror();
-	}
-
-	n = readstr(0, a, count, p->dot->path->s);
-	n += snprint(a+n, count-n, "\n");
-	offset = procoffset(offset, a, &n);
-	/* compute width of qid.path */
-	w = 0;
-	for(i = 0; i <= f->maxfd; i++) {
-		c = f->fd[i];
-		if(c == nil)
-			continue;
-		ww = procqidwidth(c);
-		if(ww > w)
-			w = ww;
-	}
-	for(i = 0; i <= f->maxfd; i++) {
-		c = f->fd[i];
-		if(c == nil)
-			continue;
-		n += procfdprint(c, i, w, a+n, count-n);
-		offset = procoffset(offset, a, &n);
-	}
-	unlock(f);
-	qunlock(&p->debug);
-	poperror();
-
-	/* copy result to user space, now that locks are released */
-	memmove(va, buf, n);
-
-	return n;
-}
-
 static void
 procclose(Chan *c)
 {
@@ -622,31 +519,8 @@
 			proctrace = nil;
 		unlock(&tlock);
 	}
-	if(QID(c->qid) == Qns && c->aux != nil){
-		free(c->aux);
-		c->aux = nil;
-	}
 }
 
-static void
-int2flag(int flag, char *s)
-{
-	if(flag == 0){
-		*s = '\0';
-		return;
-	}
-	*s++ = '-';
-	if(flag & MAFTER)
-		*s++ = 'a';
-	if(flag & MBEFORE)
-		*s++ = 'b';
-	if(flag & MCREATE)
-		*s++ = 'c';
-	if(flag & MCACHE)
-		*s++ = 'C';
-	*s = '\0';
-}
-
 static int
 procargs(Proc *p, char *buf, int nbuf)
 {
@@ -690,6 +564,127 @@
 	return p->pid != PID(c->qid) || p->waitq != nil;
 }
 
+static void
+int2flag(int flag, char *s)
+{
+	if(flag == 0){
+		*s = '\0';
+		return;
+	}
+	*s++ = '-';
+	if(flag & MAFTER)
+		*s++ = 'a';
+	if(flag & MBEFORE)
+		*s++ = 'b';
+	if(flag & MCREATE)
+		*s++ = 'c';
+	if(flag & MCACHE)
+		*s++ = 'C';
+	*s = '\0';
+}
+
+static int
+readns1(Chan *c, Proc *p, char *buf, int nbuf)
+{
+	Pgrp *pg;
+	Mount *t, *cm;
+	Mhead *f, *mh;
+	ulong minid, bestmid;
+	char flag[10], *srv;
+	int i;
+
+	pg = p->pgrp;
+	if(pg == nil || p->dot == nil || p->pid != PID(c->qid))
+		error(Eprocdied);
+
+	bestmid = ~0;
+	minid = c->nrock;
+	if(minid == bestmid)
+		return 0;
+
+	rlock(&pg->ns);
+
+	mh = nil;
+	cm = nil;
+	for(i = 0; i < MNTHASH; i++) {
+		for(f = pg->mnthash[i]; f != nil; f = f->hash) {
+			for(t = f->mount; t != nil; t = t->next) {
+				if(t->mountid >= minid && t->mountid < bestmid) {
+					bestmid = t->mountid;
+					cm = t;
+					mh = f;
+				}
+			}
+		}
+	}
+
+	if(bestmid == ~0) {
+		c->nrock = bestmid;
+		i = snprint(buf, nbuf, "cd %s\n", p->dot->path->s);
+	} else {
+		c->nrock = bestmid+1;
+
+		int2flag(cm->mflag, flag);
+		if(strcmp(cm->to->path->s, "#M") == 0){
+			srv = srvname(cm->to->mchan);
+			i = snprint(buf, nbuf, "mount %s %s %s %s\n", flag,
+				srv==nil? cm->to->mchan->path->s : srv,
+				mh->from->path->s, cm->spec? cm->spec : "");
+			free(srv);
+		}else{
+			i = snprint(buf, nbuf, "bind %s %s %s\n", flag,
+				cm->to->path->s, mh->from->path->s);
+		}
+	}
+
+	runlock(&pg->ns);
+
+	return i;
+}
+
+int
+procfdprint(Chan *c, int fd, char *s, int ns)
+{
+	return snprint(s, ns, "%3d %.2s %C %4ld (%.16llux %lud %.2ux) %5ld %8lld %s\n",
+		fd,
+		&"r w rw"[(c->mode&3)<<1],
+		devtab[c->type]->dc, c->dev,
+		c->qid.path, c->qid.vers, c->qid.type,
+		c->iounit, c->offset, c->path->s);
+}
+
+static int
+readfd1(Chan *c, Proc *p, char *buf, int nbuf)
+{
+	Fgrp *fg;
+	int n, i;
+
+	fg = p->fgrp;
+	if(fg == nil || p->dot == nil || p->pid != PID(c->qid))
+		return 0;
+
+	if(c->nrock == 0){
+		c->nrock = 1;
+		return snprint(buf, nbuf, "%s\n", p->dot->path->s);
+	}
+
+	lock(fg);
+	n = 0;
+	for(;;){
+		i = c->nrock-1;
+		if(i < 0 || i > fg->maxfd)
+			break;
+		c->nrock++;
+		if(fg->fd[i] != nil){
+			n = procfdprint(fg->fd[i], i, buf, nbuf);
+			break;
+		}
+	}
+	unlock(fg);
+
+	return n;
+}
+
 /*
  * userspace can't pass negative file offset for a
  * 64 bit kernel address, so we use 63 bit and sign
@@ -706,15 +701,13 @@
 static long
 procread(Chan *c, void *va, long n, vlong off)
 {
-	/* NSEG*32 was too small for worst cases */
-	char *a, flag[10], *sps, *srv, statbuf[NSEG*64];
-	int i, j, m, navail, ne, rsize;
+	char *a, *sps, statbuf[1024];
+	int i, j, navail, ne, rsize;
 	long l;
 	uchar *rptr;
 	uintptr addr;
 	ulong offset;
 	Confmem *cm;
-	Mntwalk *mw;
 	Proc *p;
 	Segment *sg, *s;
 	Ureg kur;
@@ -753,14 +746,16 @@
 	switch(QID(c->qid)){
 	case Qargs:
 		eqlock(&p->debug);
-		j = procargs(p, up->genbuf, sizeof up->genbuf);
+		j = procargs(p, statbuf, sizeof(statbuf));
 		qunlock(&p->debug);
 		if(offset >= j)
 			return 0;
 		if(offset+n > j)
 			n = j-offset;
-		memmove(a, &up->genbuf[offset], n);
+	statbufread:
+		memmove(a, statbuf+offset, n);
 		return n;
+
 	case Qsyscall:
 		eqlock(&p->debug);
 		if(waserror()){
@@ -769,13 +764,12 @@
 		}
 		if(p->pid != PID(c->qid))
 			error(Eprocdied);
+		j = 0;
 		if(p->syscalltrace != nil)
-			n = readstr(offset, a, n, p->syscalltrace);
-		else
-			n = 0;
+			j = readstr(offset, a, n, p->syscalltrace);
 		qunlock(&p->debug);
 		poperror();
-		return n;
+		return j;
 
 	case Qmem:
 		addr = off2addr(off);
@@ -830,17 +824,15 @@
 		if(p->nnote == 0)
 			n = 0;
 		else {
-			m = strlen(p->note[0].msg) + 1;
-			if(m > n)
-				m = n;
-			memmove(va, p->note[0].msg, m-1);
-			((char*)va)[m-1] = '\0';
-			p->nnote--;
+			i = strlen(p->note[0].msg) + 1;
+			if(i < n)
+				n = i;
+			memmove(a, p->note[0].msg, n-1);
+			a[n-1] = '\0';
+			if(--p->nnote == 0)
+				p->notepending = 0;
 			memmove(p->note, p->note+1, p->nnote*sizeof(Note));
-			n = m;
 		}
-		if(p->nnote == 0)
-			p->notepending = 0;
 		poperror();
 		qunlock(&p->debug);
 		return n;
@@ -905,8 +897,7 @@
 		readnum(0, statbuf+j+NUMSIZE*6, NUMSIZE, procpagecount(p)*BY2PG/1024, NUMSIZE);
 		readnum(0, statbuf+j+NUMSIZE*7, NUMSIZE, p->basepri, NUMSIZE);
 		readnum(0, statbuf+j+NUMSIZE*8, NUMSIZE, p->priority, NUMSIZE);
-		memmove(a, statbuf+offset, n);
-		return n;
+		goto statbufread;
 
 	case Qsegment:
 		j = 0;
@@ -924,10 +915,7 @@
 			return 0;
 		if(offset+n > j)
 			n = j-offset;
-		if(n == 0 && offset == 0)
-			exhausted("segments");
-		memmove(a, &statbuf[offset], n);
-		return n;
+		goto statbufread;
 
 	case Qwait:
 		if(!canqlock(&p->qwaitr))
@@ -959,96 +947,55 @@
 
 		qunlock(&p->qwaitr);
 		poperror();
-		n = snprint(a, n, "%d %lud %lud %lud %q",
+
+		j = snprint(statbuf, sizeof(statbuf), "%d %lud %lud %lud %q",
 			wq->w.pid,
 			wq->w.time[TUser], wq->w.time[TSys], wq->w.time[TReal],
 			wq->w.msg);
 		free(wq);
-		return n;
+		if(j < n)
+			n = j;
+		offset = 0;
+		goto statbufread;
 
 	case Qns:
+	case Qfd:
 		eqlock(&p->debug);
 		if(waserror()){
 			qunlock(&p->debug);
 			nexterror();
 		}
-		if(p->pgrp == nil || p->dot == nil || p->pid != PID(c->qid))
-			error(Eprocdied);
-		mw = c->aux;
-		if(mw->cddone){
-			qunlock(&p->debug);
-			poperror();
-			return 0;
-		}
-		mntscan(mw, p);
-		if(mw->mh == nil){
-			mw->cddone = 1;
-			i = snprint(a, n, "cd %s\n", p->dot->path->s);
-			qunlock(&p->debug);
-			poperror();
-			return i;
-		}
-		int2flag(mw->cm->mflag, flag);
-		if(strcmp(mw->cm->to->path->s, "#M") == 0){
-			srv = srvname(mw->cm->to->mchan);
-			i = snprint(a, n, "mount %s %s %s %s\n", flag,
-				srv==nil? mw->cm->to->mchan->path->s : srv,
-				mw->mh->from->path->s, mw->cm->spec? mw->cm->spec : "");
-			free(srv);
-		}else
-			i = snprint(a, n, "bind %s %s %s\n", flag,
-				mw->cm->to->path->s, mw->mh->from->path->s);
+		if(offset == 0 || offset != c->mrock)
+			c->nrock = c->mrock = 0;
+		do {
+			if(QID(c->qid) == Qns)
+				j = readns1(c, p, statbuf, sizeof(statbuf));
+			else
+				j = readfd1(c, p, statbuf, sizeof(statbuf));
+			if(j == 0)
+				break;
+			c->mrock += j;
+		} while(c->mrock <= offset);
+		i = c->mrock - offset;
 		qunlock(&p->debug);
 		poperror();
-		return i;
 
+		if(i <= 0)
+			return 0;
+		if(i < n)
+			n = i;
+		offset = j - i;
+		goto statbufread;
+
 	case Qnoteid:
 		return readnum(offset, va, n, p->noteid, NUMSIZE);
+
 	case Qppid:
 		return readnum(offset, va, n, p->parentpid, NUMSIZE);
-	case Qfd:
-		return procfds(p, va, n, offset);
+
 	}
 	error(Egreg);
 	return 0;		/* not reached */
-}
-
-void
-mntscan(Mntwalk *mw, Proc *p)
-{
-	Pgrp *pg;
-	Mount *t;
-	Mhead *f;
-	int nxt, i;
-	ulong last, bestmid;
-
-	pg = p->pgrp;
-	rlock(&pg->ns);
-
-	nxt = 0;
-	bestmid = ~0;
-
-	last = 0;
-	if(mw->mh != nil)
-		last = mw->cm->mountid;
-
-	for(i = 0; i < MNTHASH; i++) {
-		for(f = pg->mnthash[i]; f != nil; f = f->hash) {
-			for(t = f->mount; t != nil; t = t->next) {
-				if(mw->mh == nil ||
-				  (t->mountid > last && t->mountid < bestmid)) {
-					mw->cm = t;
-					mw->mh = f;
-					bestmid = mw->cm->mountid;
-					nxt = 1;
-				}
-			}
-		}
-	}
-	if(nxt == 0)
-		mw->mh = nil;
-
-	runlock(&pg->ns);
 }
 
 static long
--- a/sys/src/9/port/fault.c
+++ b/sys/src/9/port/fault.c
@@ -389,7 +389,6 @@
 void
 checkpages(void)
 {
-	int checked;
 	uintptr addr, off;
 	Pte *p;
 	Page *pg;
@@ -398,7 +397,6 @@
 	if(up == nil)
 		return;
 
-	checked = 0;
 	for(sp=up->seg, ep=&up->seg[NSEG]; sp<ep; sp++){
 		if((s = *sp) == nil)
 			continue;
@@ -411,9 +409,7 @@
 			if(pagedout(pg))
 				continue;
 			checkmmu(addr, pg->pa);
-			checked++;
 		}
 		qunlock(s);
 	}
-	print("%ld %s: checked %d page table entries\n", up->pid, up->text, checked);
 }
--- a/sys/src/9/port/portdat.h
+++ b/sys/src/9/port/portdat.h
@@ -17,7 +17,6 @@
 typedef struct Mntcache Mntcache;
 typedef struct Mount	Mount;
 typedef struct Mntrpc	Mntrpc;
-typedef struct Mntwalk	Mntwalk;
 typedef struct Mnt	Mnt;
 typedef struct Mhead	Mhead;
 typedef struct Note	Note;
@@ -240,13 +239,6 @@
 	NSMAX	=	1000,
 	NSLOG	=	7,
 	NSCACHE	=	(1<<NSLOG),
-};
-
-struct Mntwalk				/* state for /proc/#/ns */
-{
-	int	cddone;
-	Mhead*	mh;
-	Mount*	cm;
 };
 
 struct Mount
--- a/sys/src/9/port/portfns.h
+++ b/sys/src/9/port/portfns.h
@@ -224,7 +224,7 @@
 ulong		procalarm(ulong);
 void		procctl(void);
 void		procdump(void);
-int		procfdprint(Chan*, int, int, char*, int);
+int		procfdprint(Chan*, int, char*, int);
 void		procflushseg(Segment*);
 int		procindex(ulong);
 void		procinit0(void);
--- a/sys/src/9/port/sdaoe.c
+++ b/sys/src/9/port/sdaoe.c
@@ -249,19 +249,6 @@
 static char	*probebuf;
 static int 	nprobe;
 
-static int
-pnpprobeid(char *s)
-{
-	int id;
-
-	if(strlen(s) < 2)
-		return 0;
-	id = 'e';
-	if(s[1] == '!')
-		id = s[0];
-	return id;
-}
-
 static SDev*
 aoepnp(void)
 {
@@ -275,9 +262,26 @@
 	nprobe = tokenize(probebuf, probef, nelem(probef));
 	h = t = 0;
 	for(i = 0; i < nprobe; i++){
-		id = pnpprobeid(probef[i]);
-		if(id == 0)
+		p = probef[i];
+		if(strlen(p) < 2)
 			continue;
+		id = 'e';
+		if(p[1] == '!'){
+			id = p[0];
+			p += 2;
+		}
+		/*
+		 * shorthand for: id!lun -> id!#æ/aoe/lun
+		 * because we cannot type æ in the bootloader console.
+		 */
+		if(strchr(p, '/') == nil){
+			char tmp[64];
+
+			snprint(tmp, sizeof(tmp), "%c!#æ/aoe/%s", (char)id, p);
+
+			probef[i] = nil;
+			kstrdup(&probef[i], tmp);
+		}
 		s = malloc(sizeof *s);
 		if(s == nil)
 			break;
--- a/sys/src/9/port/sdloop.c
+++ b/sys/src/9/port/sdloop.c
@@ -14,10 +14,7 @@
 #include "../port/netif.h"
 
 extern	char	Echange[];
-extern	char	Enotup[];
 
-#define uprint(...)	snprint(up->genbuf, sizeof up->genbuf, __VA_ARGS__);
-
 enum {
 	Maxpath		= 256,
 	Devsectsize	= 512,
@@ -302,7 +299,7 @@
 
 	if(waserror()){
 		if(strcmp(up->errstr, Echange) == 0 ||
-		    strcmp(up->errstr, Enotup) == 0)
+		    strstr(up->errstr, "device is down") != nil)
 			u->sectors = 0;
 		nexterror();
 	}
--- a/sys/src/9/xen/fns.h
+++ b/sys/src/9/xen/fns.h
@@ -4,8 +4,6 @@
 void	archinit(void);
 void	bootargs(ulong);
 ulong	cankaddr(ulong);
-int		cistrcmp(char*, char*);
-int		cistrncmp(char*, char*, int);
 #define	clearmmucache()				/* x86 doesn't have one */
 void	clockintr(Ureg*, void*);
 int		(*cmpswap)(long*, long, long);
@@ -63,10 +61,7 @@
 int	isaconfig(char*, int, ISAConf*);
 void	kbdenable(void);
 #define	kmapinval()
-void	lgdt(ushort[3]);					// XXX remove and in l.s
-void	lidt(ushort[3]);					// XXX remove and in l.s
 void	links(void);
-void	ltr(ulong);						// XXX remove?
 void	mach0init(void);
 void	mathinit(void);
 void	mb386(void);
--- a/sys/src/9/xen/l.s
+++ b/sys/src/9/xen/l.s
@@ -54,25 +54,6 @@
  * CR4 and the 'model specific registers' should only be read/written
  * after it has been determined the processor supports them
  */
-TEXT lgdt(SB), $0				/* GDTR - global descriptor table */
-	MOVL	gdtptr+0(FP), AX
-	MOVL	(AX), GDTR
-	RET
-
-TEXT lidt(SB), $0				/* IDTR - interrupt descriptor table */
-	MOVL	idtptr+0(FP), AX
-	MOVL	(AX), IDTR
-	RET
-
-TEXT ltr(SB), $0				/* TR - task register */
-	MOVL	tptr+0(FP), AX
-	MOVW	AX, TASK
-	RET
-
-TEXT rtsr(SB), $0
-	MOVW	TASK, AX
-	RET
-
 TEXT _cycles(SB), $0				/* time stamp counter; cycles since power up */
 	RDTSC
 	MOVL	vlong+0(FP), CX			/* &vlong */
@@ -88,13 +69,9 @@
 	MOVL	DX, 4(CX)			/* hi */
 	RET
 	
+/* Xen doesn't let us do this */
 TEXT wrmsr(SB), $0
-	MOVL	index+0(FP), CX
-	MOVL	lo+4(FP), AX
-	MOVL	hi+8(FP), DX
-/* Xen doesn't let us do this
-	WRMSR
- */
+	MOVL	$-1, AX
 	RET
 
 /*
--- a/sys/src/9/xen/main.c
+++ b/sys/src/9/xen/main.c
@@ -118,9 +118,6 @@
 	conf.nmach = 1;
 	MACHP(0) = (Mach*)CPU0MACH;
 	m->pdb = (ulong*)xenstart->pt_base;
-#ifdef NOT
-	m->gdt = (Segdesc*)CPU0GDT;
-#endif
 
 	machinit();
 
@@ -133,15 +130,12 @@
 {
 	int machno;
 	ulong *pdb;
-	Segdesc *gdt;
 
 	machno = m->machno;
 	pdb = m->pdb;
-	gdt = m->gdt;
 	memset(m, 0, sizeof(Mach));
 	m->machno = machno;
 	m->pdb = pdb;
-	m->gdt = gdt;
 	m->perf.period = 1;
 
 	/*
@@ -585,16 +579,6 @@
 	p->kentry = up->kentry;
 	p->pcycles = -p->kentry;
 
-	/* inherit user descriptors */
-	memmove(p->gdt, up->gdt, sizeof(p->gdt));
-
-	/* copy local descriptor table */
-	if(up->ldt != nil && up->nldt > 0){
-		p->ldt = smalloc(sizeof(Segdesc) * up->nldt);
-		memmove(p->ldt, up->ldt, sizeof(Segdesc) * up->nldt);
-		p->nldt = up->nldt;
-	}
-
 	/* save floating point state */
 	s = splhi();
 	switch(up->fpstate & ~FPillegal){
@@ -750,51 +734,3 @@
 	shutdown(ispanic);
 	arch->reset();
 }
-
-int
-cistrcmp(char *a, char *b)
-{
-	int ac, bc;
-
-	for(;;){
-		ac = *a++;
-		bc = *b++;
-	
-		if(ac >= 'A' && ac <= 'Z')
-			ac = 'a' + (ac - 'A');
-		if(bc >= 'A' && bc <= 'Z')
-			bc = 'a' + (bc - 'A');
-		ac -= bc;
-		if(ac)
-			return ac;
-		if(bc == 0)
-			break;
-	}
-	return 0;
-}
-
-int
-cistrncmp(char *a, char *b, int n)
-{
-	unsigned ac, bc;
-
-	while(n > 0){
-		ac = *a++;
-		bc = *b++;
-		n--;
-
-		if(ac >= 'A' && ac <= 'Z')
-			ac = 'a' + (ac - 'A');
-		if(bc >= 'A' && bc <= 'Z')
-			bc = 'a' + (bc - 'A');
-
-		ac -= bc;
-		if(ac)
-			return ac;
-		if(bc == 0)
-			break;
-	}
-
-	return 0;
-}
-
--- a/sys/src/9/xen/mem.h
+++ b/sys/src/9/xen/mem.h
@@ -63,67 +63,14 @@
 
 /*
  *  known x86 segments (in GDT) and their selectors
+ *  using the selectors that xen gives us.
  */
-#define	NULLSEG	0	/* null segment */
-#define	KDSEG	1	/* kernel data/stack */
-#define	KESEG	2	/* kernel executable */	
-#define	UDSEG	3	/* user data/stack */
-#define	UESEG	4	/* user executable */
-#define	TSSSEG	5	/* task segment */
-#define	APMCSEG		6	/* APM code segment */
-#define	APMCSEG16	7	/* APM 16-bit code segment */
-#define	APMDSEG		8	/* APM data segment */
-#define	PROCSEG0	11	/* per process descriptor0 */
-#define	NPROCSEG	3	/* number of per process descriptors */
-#define	NGDT		13	/* number of GDT entries required */
-/* #define	APM40SEG	8	/* APM segment 0x40 */
-
-#define	SELGDT	(0<<2)	/* selector is in gdt */
-#define	SELLDT	(1<<2)	/* selector is in ldt */
-
-#define	SELECTOR(i, t, p)	(((i)<<3) | (t) | (p))
-
-#define	NULLSEL	SELECTOR(NULLSEG, SELGDT, 0)
-/* these are replaced by XEN entries */
-#ifdef NOPE  // XXX investigate more
-#define	KDSEL	SELECTOR(KDSEG, SELGDT, 0)
-#define	KESEL	SELECTOR(KESEG, SELGDT, 0)
-#define	UESEL	SELECTOR(UESEG, SELGDT, 3)
-#define	UDSEL	SELECTOR(UDSEG, SELGDT, 3)
-/* comment out to make sure unused ... */
-
-#define	TSSSEL	SELECTOR(TSSSEG, SELGDT, 0)
-#define	APMCSEL 	SELECTOR(APMCSEG, SELGDT, 0)
-#define	APMCSEL16	SELECTOR(APMCSEG16, SELGDT, 0)
-#define	APMDSEL		SELECTOR(APMDSEG, SELGDT, 0)
-/* #define	APM40SEL	SELECTOR(APM40SEG, SELGDT, 0) */
-#else
-/* use the selectors that xen gives us */
 #define KESEL FLAT_KERNEL_CS
 #define KDSEL FLAT_KERNEL_DS
 #define UESEL FLAT_USER_CS
 #define UDSEL FLAT_USER_DS
-#endif
 
-/*
- *  fields in segment descriptors
- */
-#define	SEGDATA	(0x10<<8)	/* data/stack segment */
-#define	SEGEXEC	(0x18<<8)	/* executable segment */
-#define	SEGTSS	(0x9<<8)	/* TSS segment */
-#define	SEGCG	(0x0C<<8)	/* call gate */
-#define	SEGIG	(0x0E<<8)	/* interrupt gate */
-#define	SEGTG	(0x0F<<8)	/* trap gate */
-#define	SEGTYPE	(0x1F<<8)
-
-#define	SEGP	(1<<15)		/* segment present */
-#define	SEGPL(x) ((x)<<13)	/* priority level */
-#define	SEGB	(1<<22)		/* granularity 1==4k (for expand-down) */
-#define	SEGG	(1<<23)		/* granularity 1==4k (for other) */
-#define	SEGE	(1<<10)		/* expand down */
-#define	SEGW	(1<<9)		/* writable (for data/stack) */
-#define	SEGR	(1<<9)		/* readable (for code) */
-#define	SEGD	(1<<22)		/* default 1==32bit (for code) */
+#define	NPROCSEG	1	/* number of per process descriptors */
 
 /*
  *  virtual MMU
--- a/sys/src/9/xen/mmu.c
+++ b/sys/src/9/xen/mmu.c
@@ -13,35 +13,10 @@
 #define MFN(pa)		(patomfn[(pa)>>PGSHIFT])
 #define	MAPPN(x)	(paemode? matopfn[*(uvlong*)(&x)>>PGSHIFT]<<PGSHIFT : matopfn[(x)>>PGSHIFT]<<PGSHIFT)
 
-#define	DATASEGM(p) 	{ 0xFFFF, SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(p)|SEGDATA|SEGW }
-#define	EXECSEGM(p) 	{ 0xFFFF, SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(p)|SEGEXEC|SEGR }
-#define	TSSSEGM(b,p)	{ ((b)<<16)|sizeof(Tss),\
-			  ((b)&0xFF000000)|(((b)>>16)&0xFF)|SEGTSS|SEGPL(p)|SEGP }
-
-Segdesc gdt[NGDT] =
-{
-[NULLSEG]	{ 0, 0},		/* null descriptor */
-[KDSEG]		DATASEGM(0),		/* kernel data/stack */
-[KESEG]		EXECSEGM(0),		/* kernel code */
-[UDSEG]		DATASEGM(3),		/* user data/stack */
-[UESEG]		EXECSEGM(3),		/* user code */
-[TSSSEG]	TSSSEGM(0,0),		/* tss segment */
-};
-
 /* note: pdb must already be pinned */
 static void
 taskswitch(Page *pdb, ulong stack)
 {
-	Tss *tss;
-
-	tss = m->tss;
-	tss->ss0 = KDSEL;
-	tss->esp0 = stack;
-	tss->ss1 = KDSEL;
-	tss->esp1 = stack;
-	tss->ss2 = KDSEL;
-	tss->esp2 = stack;
-	//tss->cr3 = pdb;
 	HYPERVISOR_stack_switch(KDSEL, stack);
 	mmuflushtlb(pdb);
 }
@@ -164,10 +139,7 @@
 void
 mmuinit(void)
 {
-//XXX	ulong x;
-//XXX	ushort ptr[3];
 	ulong *pte, npgs, pa;
-	extern int rtsr(void);
 
 	if(paemode){
 		int i;
@@ -193,39 +165,6 @@
 
 	memglobal();
 
-	m->tss = malloc(sizeof(Tss));
-	memset(m->tss, 0, sizeof(Tss));
-	m->tss->iomap = 0xDFFF<<16;
-
-	/*
-	 * We used to keep the GDT in the Mach structure, but it
-	 * turns out that that slows down access to the rest of the
-	 * page.  Since the Mach structure is accessed quite often,
-	 * it pays off anywhere from a factor of 1.25 to 2 on real
-	 * hardware to separate them (the AMDs are more sensitive
-	 * than Intels in this regard).  Under VMware it pays off
-	 * a factor of about 10 to 100.
-	 */
-
-#ifdef we_dont_set_gdt_or_lidt
-	memmove(m->gdt, gdt, sizeof gdt);
-	x = (ulong)m->tss;
-	m->gdt[TSSSEG].d0 = (x<<16)|sizeof(Tss);
-	m->gdt[TSSSEG].d1 = (x&0xFF000000)|((x>>16)&0xFF)|SEGTSS|SEGPL(0)|SEGP;
-
-	ptr[0] = sizeof(gdt)-1;
-	x = (ulong)m->gdt;
-	ptr[1] = x & 0xFFFF;
-	ptr[2] = (x>>16) & 0xFFFF;
-	lgdt(ptr);
-
-	ptr[0] = sizeof(Segdesc)*256-1;
-	x = IDTADDR;
-	ptr[1] = x & 0xFFFF;
-	ptr[2] = (x>>16) & 0xFFFF;
-	lidt(ptr);
-#endif
-
 #ifdef we_may_eventually_want_this
 	/* make kernel text unwritable */
 	for(x = KTZERO; x < (ulong)etext; x += BY2PG){
@@ -237,9 +176,6 @@
 #endif
 
 	taskswitch(0,  (ulong)m + BY2PG);
-#ifdef we_dont_do_this
-	ltr(TSSSEL);
-#endif
 }
 
 void
--- a/sys/src/cmd/disk/mkfile
+++ b/sys/src/cmd/disk/mkfile
@@ -14,6 +14,7 @@
 	prep\
 	smart\
 #	sacfs\
+	vblade\
 
 OFILES=
 
--- /dev/null
+++ b/sys/src/cmd/disk/vblade/aoe.h
@@ -1,0 +1,70 @@
+enum{
+	ACata,
+	ACconfig,
+};
+
+enum{
+	AQCread,
+	AQCtest,
+	AQCprefix,
+	AQCset,
+	AQCfset,
+};
+
+enum{
+	AEcmd	= 1,
+	AEarg,
+	AEdev,
+	AEcfg,
+	AEver,
+};
+
+enum{
+	Aoetype		= 0x88a2,
+	Aoesectsz 	= 512,
+	Aoemaxcfg	= 1024,
+
+	Aoehsz		= 24,
+	Aoeatasz	= 12,
+	Aoecfgsz		= 8,
+	Aoerrsz		= 2,
+	Aoemsz		= 4,
+	Aoemdsz	= 8,
+
+	Aoever		= 1,
+
+	AFerr		= 1<<2,
+	AFrsp		= 1<<3,
+
+	AAFwrite	= 1,
+	AAFext		= 1<<6,
+};
+
+typedef struct{
+	uchar	dst[Eaddrlen];
+	uchar	src[Eaddrlen];
+	uchar	type[2];
+	uchar	verflag;
+	uchar	error;
+	uchar	major[2];
+	uchar	minor;
+	uchar	cmd;
+	uchar	tag[4];
+}Aoehdr;
+
+typedef struct{
+	uchar	aflag;
+	uchar	errfeat;
+	uchar	scnt;
+	uchar	cmdstat;
+	uchar	lba[6];
+	uchar	res[2];
+}Aoeata;
+
+typedef struct{
+	uchar	bufcnt[2];
+	uchar	fwver[2];
+	uchar	scnt;
+	uchar	verccmd;
+	uchar	cslen[2];
+}Aoeqc;
--- /dev/null
+++ b/sys/src/cmd/disk/vblade/mkfile
@@ -1,0 +1,12 @@
+</$objtype/mkfile
+
+TARG=vblade
+
+OFILES=\
+	vblade.$O\
+
+HFILES=aoe.h
+
+BIN=/$objtype/bin/disk
+
+</sys/src/cmd/mkone
--- /dev/null
+++ b/sys/src/cmd/disk/vblade/vblade.c
@@ -1,0 +1,666 @@
+/*
+ * vblade -- virtual aoe target
+ * copyright © 2007 erik quanstrom
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <ip.h>			/* irony */
+#include <fis.h>
+
+enum {
+	Eaddrlen	= 6,		/* only defined in kernel */
+};
+#include "aoe.h"
+
+enum {
+	Fclone,
+	Fdata,
+	Flast,
+
+	Fraw	= 1<<0,
+
+	Nether	= 8,
+	Nvblade	= 8,
+	Maxpkt	= 10000,
+	Hdrlba	= 128,
+	Conflen	= 1024,
+};
+
+typedef struct {
+	int	iflag;
+	int	flag;
+	int	shelf;
+	int	slot;
+	uvlong	maxlba;
+	char	*config;
+} Conf;
+
+typedef struct {
+	char	magic[32];
+	char	size[32];
+	char	address[16];
+	char	configlen[6];
+	char	pad[512-32-32-16-6];
+	char	config[Conflen];
+} Vbhdr;
+
+typedef struct {
+	Vbhdr	hdr;
+	vlong	maxlba;
+	vlong	hdrsz;
+	int	shelf;
+	int	slot;
+	int	clen;
+	int	flag;
+	int	fd;
+} Vblade;
+
+static	Vblade	vblade[Nvblade];
+static	int	nblade;
+
+static	char	*ethertab[Nether] = {
+	"/net/ether0",
+};
+static	int	etheridx = 1;
+static	int	efdtab[Nether*Flast];
+static	char	pkttab[Nether][Maxpkt];
+static	char	bctab[Nether][Maxpkt];
+static	int	mtutab[Nether];
+static	char	Magic[] = "aoe vblade\n";
+
+static int
+getmtu(char *p)
+{
+	char buf[50];
+	int fd, mtu;
+
+	snprint(buf, sizeof buf, "%s/mtu", p);
+	if((fd = open(buf, OREAD)) == -1)
+		return 2;
+	if(read(fd, buf, 36) < 0)
+		return 2;
+	close(fd);
+	buf[36] = 0;
+	mtu = strtoul(buf+12, 0, 0)-Aoehsz;
+	return mtu>>9;
+}
+
+int
+parseshelf(char *s, int *shelf, int *slot)
+{
+	int a, b;
+
+	a = strtoul(s, &s, 0);
+	if(*s++ != '.')
+		return -1;
+	b = strtoul(s, &s, 0);
+	if(*s != 0)
+		return -1;
+	*shelf = a;
+	*slot = b;
+	return 0;
+}
+
+static vlong
+getsize(char *s)
+{
+	static char tab[] = "ptgmk";
+	char *p;
+	vlong v;
+
+	v = strtoull(s, &s, 0);
+	while((p = strchr(tab, *s++)) && *p)
+		while(*p++)
+			v *= 1024;
+	if(s[-1])
+		return -1;
+	return v;
+}
+
+vlong
+sizetolba(vlong size)
+{
+	if(size < 512 || size & 0x1ff){
+		fprint(2, "invalid size %lld\n", size);
+		exits("size");
+	}
+	return size>>9;
+}
+
+static int
+savevblade(int fd, Vblade *vb)
+{
+	int n, r;
+	char *p;
+
+	sprint(vb->hdr.size, "%lld", vb->maxlba<<9);
+	sprint(vb->hdr.address, "%d.%d", vb->shelf, vb->slot);
+	sprint(vb->hdr.configlen, "%d", vb->clen);
+
+	if(vb->flag & Fraw)
+		return 0;	
+	p = (char*)vb;
+	for(n = 0; n < sizeof *vb; n += r)
+		if((r = pwrite(fd, p+n, sizeof *vb-n, n)) <= 0)
+			break;
+	if(n != sizeof *vb)
+		return -1;
+	return 0;
+}
+
+static char*
+chkvblade(int fd, Vblade *vb)
+{
+	Vbhdr *h;
+
+	h = &vb->hdr;
+	if(readn(fd, (char*)h, sizeof *h) != sizeof *h)
+		return "bad read";
+	if(memcmp(h->magic, Magic, sizeof Magic))
+		return "bad magic";
+	h->size[sizeof h->size-1] = 0;
+	vb->maxlba = sizetolba(strtoull(h->size, 0, 0));
+	if(parseshelf(h->address, &vb->shelf, &vb->slot) == -1)
+		return "bad shelf";
+	h->configlen[sizeof h->configlen-1] = 0;
+	vb->clen = strtoul(h->configlen, 0, 0);
+	return 0;
+}
+
+void
+checkfile(char *s, Vblade *vb, int iflag)
+{
+	char *e;
+
+	vb->fd = open(s, ORDWR);
+	if(vb->fd == -1)
+		sysfatal("can't open backing store: %r");
+	if(iflag == 0 && (e = chkvblade(vb->fd, vb)))
+		sysfatal("invalid vblade %s", e);
+}
+
+void
+recheck(int fd, Vblade *vb)
+{
+	Dir *d;
+	vlong v;
+
+	d = dirfstat(fd);
+	if(d == 0)
+		sysfatal("can't stat: %r");
+	if((vb->flag & Fraw) == 0)
+		vb->hdrsz = Hdrlba;
+	v = sizetolba(d->length & ~0x1ff) - vb->hdrsz;
+	free(d);
+	if(vb->maxlba > v)
+		sysfatal("cmdline size too large (%lld sector overhead)", vb->hdrsz);
+	if(vb->maxlba == 0)
+		vb->maxlba = v;
+
+	savevblade(fd, vb);
+}
+
+int
+aoeopen(char *e, int fds[])
+{
+	char buf[128], ctl[13];
+	int n;
+
+	snprint(buf, sizeof buf, "%s/clone", e);
+	if((fds[Fclone] = open(buf, ORDWR)) == -1)
+		return -1;
+	memset(ctl, 0, sizeof ctl);
+	if(read(fds[Fclone], ctl, sizeof ctl - 1) < 0)
+		return -1;
+	n = atoi(ctl);
+	snprint(buf, sizeof buf, "connect %d", Aoetype);
+	if(write(fds[Fclone], buf, strlen(buf)) != strlen(buf))
+		return -1;
+	snprint(buf, sizeof buf, "%s/%d/data", e, n);
+	fds[Fdata] = open(buf, ORDWR);
+	return fds[Fdata];
+}
+
+void
+replyhdr(Aoehdr *h, Vblade *vblade)
+{
+	uchar	ea[Eaddrlen];
+
+	memmove(ea, h->dst, Eaddrlen);
+	memmove(h->dst, h->src, Eaddrlen);
+	memmove(h->src, ea, Eaddrlen);
+
+	hnputs(h->major, vblade->shelf);
+	h->minor = vblade->slot;
+	h->verflag |= AFrsp;
+}
+
+static int
+serveconfig(Aoehdr *h, Vblade *vb, int mtu)
+{
+	int cmd, reqlen, len;
+	char *cfg;
+	Aoeqc *q;
+
+	if(memcmp(h->src, h->dst, Eaddrlen) == 0)
+		return -1;
+
+	q = (Aoeqc*)((char*)h + Aoehsz);
+	reqlen = nhgets(q->cslen);
+	len = vb->clen;
+	cmd = q->verccmd&0xf;
+	cfg = (char*)q + Aoecfgsz;
+
+	switch(cmd){
+	case AQCtest:
+		if(reqlen != len)
+			return -1;
+	case AQCprefix:
+		if(reqlen > len)
+			return -1;
+		if(memcmp(vb->hdr.config, cfg, reqlen) != 0)
+			return -1;
+	case AQCread:
+		break;
+	case AQCset:
+		if(len && len != reqlen || memcmp(vb->hdr.config, cfg, reqlen) != 0){
+			h->verflag |= AFerr;
+			h->error = AEcfg;
+			break;
+		}
+	case AQCfset:
+		if(reqlen > Conflen){
+			h->verflag |= AFerr;
+			h->error = AEarg;
+			break;
+		}
+		memset(vb->hdr.config, 0, sizeof vb->hdr.config);
+		memmove(vb->hdr.config, cfg, reqlen);
+		vb->clen = len = reqlen;
+		savevblade(vb->fd, vb);
+		break;
+	default:
+		h->verflag |= AFerr;
+		h->error = AEarg;
+	}
+
+	memmove(cfg, vb->hdr.config, len);
+	hnputs(q->cslen, len);
+	hnputs(q->bufcnt, 24);
+	q->scnt = mtu;
+	hnputs(q->fwver, 2323);
+	q->verccmd = Aoever<<4 | cmd;
+
+	return Aoehsz+Aoecfgsz + len;
+}
+
+static ushort ident[256] = {
+	[47] 0x8000,
+	[49] 0x0200,
+	[50] 0x4000,
+	[83] 0x5400,
+	[84] 0x4000,
+	[86] 0x1400,
+	[87] 0x4000,
+	[93] 0x400b,
+};
+
+static void
+idmoveto(char *a, int idx, int len, char *s)
+{
+	char *p;
+
+	p = a+idx*2;
+	for(; len > 0; len -= 2) {
+		if(*s == 0)
+			p[1] = ' ';
+		else
+			p[1] = *s++;
+		if (*s == 0)
+			p[0] = ' ';
+		else
+			p[0] = *s++;
+		p += 2;
+	}
+}
+
+static void
+lbamoveto(char *p, int idx, int n, vlong lba)
+{
+	int i;
+
+	p += idx*2;
+	for(i = 0; i < n; i++)
+		*p++ = lba>>i*8;
+}
+
+enum {
+	Crd		= 0x20,
+	Crdext		= 0x24,
+	Cwr		= 0x30,
+	Cwrext		= 0x34,
+	Cid		= 0xec,
+};
+
+static uvlong
+getlba(uchar *p)
+{
+	uvlong v;
+
+	v = p[0];
+	v |= p[1]<<8;
+	v |= p[2]<<16;
+	v |= p[3]<<24;
+	v |= (uvlong)p[4]<<32;
+	v |= (uvlong)p[5]<<40;
+	return v;
+}
+
+static void
+putlba(uchar *p, vlong lba)
+{
+	p[0] = lba;
+	p[1] = lba>>8;
+	p[2] = lba>>16;
+	p[3] = lba>>24;
+	p[5] = lba>>32;
+	p[6] = lba>>40;
+}
+
+static int
+serveata(Aoehdr *h, Vblade *vb, int mtu)
+{
+	Aoeata *a;
+	char *buf;
+	int rbytes, bytes, len;
+	vlong lba, off;
+
+	a = (Aoeata*)((char*)h + Aoehsz);
+	buf = (char*)a + Aoeatasz;
+	lba = getlba(a->lba);
+	len = a->scnt<<9;
+	off = lba+vb->hdrsz<<9;
+
+	rbytes  = 0;
+	if(a->scnt > mtu || a->scnt == 0){
+		h->verflag |= AFerr;
+		a->cmdstat = ASdrdy|ASerr;
+		h->error = AEarg;
+		goto out;
+	}
+	
+	if(a->cmdstat != Cid)
+	if(lba+a->scnt > vb->maxlba){
+		a->errfeat = Eidnf;
+		a->cmdstat = ASdrdy|ASerr;
+		goto out;
+	}
+
+	if(a->cmdstat&0xf0 == 0x20)
+		lba &= 0xfffffff;
+	switch(a->cmdstat){
+	default:
+		a->errfeat = Eabrt;
+		a->cmdstat = ASdrdy|ASerr;
+		goto out;
+	case Cid:
+		memmove(buf, ident, sizeof ident);
+		idmoveto(buf, 27, 40, "Plan 9 Vblade");
+		idmoveto(buf, 10, 20, "serial#");
+		idmoveto(buf, 23, 8, "2");
+		lbamoveto(buf, 60, 4, vb->maxlba);
+		lbamoveto(buf, 100, 8, vb->maxlba);
+		a->cmdstat = ASdrdy;
+		rbytes = 512;
+		goto out;
+	case Crd:
+	case Crdext:
+		bytes = pread(vb->fd, buf, len, off);
+		rbytes = bytes;
+		break;
+	case Cwr:
+	case Cwrext:
+		bytes = pwrite(vb->fd, buf, len, off);
+		break;
+	}
+	if(bytes != len){
+		a->errfeat = Eabrt;
+		a->cmdstat = ASdf|ASerr;
+		putlba(a->lba, lba+(len-bytes)>>9);
+		rbytes = 0;
+		goto out;
+	}
+
+	putlba(a->lba, lba+a->scnt);
+	a->scnt = 0;
+	a->errfeat = 0;
+	a->cmdstat = ASdrdy;
+out:
+	return Aoehsz+Aoeatasz + rbytes;
+}
+
+static int
+myea(uchar ea[6], char *p)
+{
+	char buf[50];
+	int fd;
+
+	snprint(buf, sizeof buf, "%s/addr", p);
+	if((fd = open(buf, OREAD)) == -1)
+		return -1;
+	if(read(fd, buf, 12) < 12)
+		return -1;
+	close(fd);
+	return parseether(ea, buf);
+}
+
+static void
+bcastpkt(Aoehdr *h, uint shelf, uint slot, int i)
+{
+	myea(h->dst, ethertab[i]);
+	memset(h->src, 0xff, Eaddrlen);
+	hnputs(h->type, Aoetype);
+	hnputs(h->major, shelf);
+	h->minor = slot;
+	h->cmd = ACconfig;
+	h->tag[0] = h->tag[1] = h->tag[2] = h->tag[3] = 0;
+}
+
+int
+bladereply(Vblade *v, int i, int fd, char *pkt)
+{
+	int n;
+	Aoehdr *h;
+
+	h = (Aoehdr*)pkt;
+	switch(h->cmd){
+	case ACata:
+		n = serveata(h, v, mtutab[i]);
+		break;
+	case ACconfig:
+		n = serveconfig(h, v, mtutab[i]);
+		break;
+	default:
+		n = -1;
+		break;
+	}
+	if(n == -1)
+		return -1;
+	replyhdr(h, v);
+	if(n < 60){
+		memset(pkt+n, 0, 60-n);
+		n = 60;
+	}
+	if(write(fd, h, n) != n){
+		fprint(2, "write to %s failed: %r\n", ethertab[i]);
+		return -1;
+	}
+	return 0;
+}
+
+void
+serve(void *v)
+{
+	int i, j, popcnt, vec, n, s, efd;
+	char *pkt, *bcpkt;
+	Aoehdr *h;
+
+fmtinstall('E', eipfmt);
+	i = (int)(uintptr)v;
+
+	efd = efdtab[i*Flast+Fdata];
+	pkt = pkttab[i];
+	bcpkt = bctab[i];
+
+	n = 60;
+	h = (Aoehdr*)pkt;
+	bcastpkt(h, 0xffff, 0xff, i);
+	goto start;
+
+	for(;;){
+		n = read(efd, pkt, Maxpkt);
+	start:
+		if(n < 60 || h->verflag & AFrsp)
+			continue;
+		s = nhgets(h->major);
+		popcnt = 0;
+		vec = 0;
+		for(j = 0; j < nblade; j++){
+			if((vblade[j].shelf == s || s == 0xffff)
+			&& (vblade[j].slot == h->minor || h->minor == 0xff)){
+				popcnt++;
+				vec |= 1<<j;
+			}
+		}
+		for(j = 0; popcnt>0 && j < nblade; j++){
+			if((vec & 1<<j) == 0)
+				continue;
+			if(popcnt>0){
+				memcpy(bcpkt, pkt, n);
+				bladereply(vblade + j, i, efd, bcpkt);
+			}else
+				bladereply(vblade + j, i, efd, pkt);
+			popcnt--;
+		}
+	}
+}
+
+void
+launch(char *tab[], int fdtab[])
+{
+	int i;
+
+	for(i = 0; tab[i]; i++){
+		if(aoeopen(tab[i], fdtab+Flast*i) < 0)
+			sysfatal("network open: %r");
+		/*
+		 * use proc not threads.  otherwise we will block on read/write.
+		 */
+		proccreate(serve, (void*)i, 32*1024);
+	}
+}
+
+void
+usage(void)
+{
+	fprint(2, "vblade [-ir] [-s size] [-a shelf.slot] [-c config] [-e ether] file\n");
+	exits("usage");
+}
+
+void
+goblade(Vblade *vblade, char *file, Conf *c)
+{
+	char *anal;
+
+	if(c->iflag == 1)
+		memcpy(vblade->hdr.magic, Magic, sizeof Magic);
+	checkfile(file, vblade, c->iflag);
+
+	vblade->flag = c->flag;
+	if(c->shelf != -1){
+		vblade->shelf = c->shelf;
+		vblade->slot = c->slot;
+	}
+	if(c->maxlba > 0)
+		vblade->maxlba = c->maxlba;
+	if(c->config != nil)
+		memmove(vblade->hdr.config, c->config, vblade->clen = strlen(c->config));
+
+	recheck(vblade->fd, vblade);
+
+	anal = "";
+	if(vblade->maxlba > 1)
+		anal = "s";
+	fprint(2, "lblade %d.%d %lld sector%s\n", vblade->shelf, vblade->slot, vblade->maxlba, anal);
+}
+
+void
+threadmain(int argc, char **argv)
+{
+	int i, lastc, anye;
+	Conf c;
+
+	anye = 0;
+	for(;;){
+		if(nblade == nelem(vblade))
+			sysfatal("too many blades");
+		c = (Conf){0, 0, -1, -1, 0, nil};
+		lastc = 0;
+		ARGBEGIN{
+		case 'a':
+			lastc = 'a';
+			if(parseshelf(EARGF(usage()), &c.shelf, &c.slot) == -1)
+				sysfatal("bad vblade address");
+			break;
+		case 'c':
+			lastc = 'c';
+			c.config = EARGF(usage());
+			break;
+		case 'e':
+			lastc = 'e';
+			if(anye++ == 0)
+				etheridx = 0;
+			if(etheridx == nelem(ethertab))
+				sysfatal("too many interfaces");
+			ethertab[etheridx++] = EARGF(usage());
+			break;
+		case 'i':
+			lastc = 'i';
+			c.iflag = 1;
+			break;
+		case 'r':
+			lastc = 'r';
+			c.flag |= Fraw;
+			c.iflag = 1;
+			break;
+		case 's':
+			lastc = 's';
+			c.maxlba = sizetolba(getsize(EARGF(usage())));
+			break;
+		default:
+			lastc = '?';
+			usage();
+		}ARGEND;
+
+		if(argc == 0 && lastc == 'e')
+			break;
+		if(argc == 0)
+			usage();
+		goblade(vblade + nblade++, *argv, &c);
+		if(argc == 1)
+			break;
+	}
+
+	if(nblade == 0)
+		usage();	
+	for(i = 0; i < etheridx; i++)
+		mtutab[i] = getmtu(ethertab[i]);
+
+	launch(ethertab, efdtab);
+
+	for(; sleep(1*1000) != -1;)
+		;
+	threadexitsall("interrupted");
+}
--- a/sys/src/cmd/sam/cmd.c
+++ b/sys/src/cmd/sam/cmd.c
@@ -35,6 +35,8 @@
 	'>',	0,	0,	0,	0,	aDot,	0,	linex,	plan9_cmd,
 	'<',	0,	0,	0,	0,	aDot,	0,	linex,	plan9_cmd,
 	'|',	0,	0,	0,	0,	aDot,	0,	linex,	plan9_cmd,
+	'^',	0,	0,	0,	0,	aDot,	0,	linex,	plan9_cmd,
+	'_',	0,	0,	0,	0,	aDot,	0,	linex,	plan9_cmd,
 	'=',	0,	0,	0,	0,	aDot,	0,	linex,	eq_cmd,
 	'c'|0x100,0,	0,	0,	0,	aNo,	0,	wordx,	cd_cmd,
 	0,	0,	0,	0,	0,	0,	0,	0,
@@ -76,7 +78,13 @@
 
     Again:
 	nbuf = 0;
-	if(downloaded){
+	if(cmdbufpos > cmdbuf.nc && cmdbuf.nc > 0){
+		cmdbufpos = 0;
+		bufreset(&cmdbuf);
+	}
+	if(cmdbufpos < cmdbuf.nc && cmdbuf.nc > 0)
+		bufread(&cmdbuf, cmdbufpos++, &r, 1);
+	else if(downloaded){
 		while(termoutp == terminp){
 			cmdupdate();
 			if(patset)
--- a/sys/src/cmd/sam/sam.h
+++ b/sys/src/cmd/sam/sam.h
@@ -361,6 +361,8 @@
 extern Address	addr;
 extern Buffer	snarfbuf;
 extern Buffer	plan9buf;
+extern Buffer	cmdbuf;
+extern int	cmdbufpos;
 extern List	file;
 extern List	tempfile;
 extern File	*cmd;
--- a/sys/src/cmd/sam/shell.c
+++ b/sys/src/cmd/sam/shell.c
@@ -7,6 +7,8 @@
 String	plan9cmd;	/* null terminated */
 Buffer	plan9buf;
 void	checkerrs(void);
+Buffer	cmdbuf;
+int	cmdbufpos;
 
 int
 plan9(File *f, int type, String *s, int nest)
@@ -28,7 +30,7 @@
 	}
 	if(type!='!' && pipe(pipe1)==-1)
 		error(Epipe);
-	if(type=='|')
+	if(type=='|' || type=='_')
 		snarf(f, addr.r.p1, addr.r.p2, &plan9buf, 1);
 	if((pid=fork()) == 0){
 		if(downloaded){	/* also put nasty fd's into errfile */
@@ -48,14 +50,14 @@
 			}
 		}
 		if(type != '!') {
-			if(type=='<' || type=='|')
-				dup(pipe1[1], 1);
-			else if(type == '>')
+			if(type == '>')
 				dup(pipe1[0], 0);
+			else
+				dup(pipe1[1], 1);
 			close(pipe1[0]);
 			close(pipe1[1]);
 		}
-		if(type == '|'){
+		if(type == '|' || type == '_'){
 			if(pipe(pipe2) == -1)
 				exits("pipe");
 			if((pid = fork())==0){
@@ -87,7 +89,7 @@
 			close(pipe2[0]);
 			close(pipe2[1]);
 		}
-		if(type=='<'){
+		if(type=='<' || type=='^'){
 			close(0);	/* so it won't read from terminal */
 			open("/dev/null", 0);
 		}
@@ -115,9 +117,14 @@
 		writeio(f);
 		bpipeok = 0;
 		closeio((Posn)-1);
+	}else if(type == '^' || type == '_'){
+		int nulls;
+		close(pipe1[1]);
+		bufload(&cmdbuf, cmdbufpos, pipe1[0], &nulls);
+		close(pipe1[0]);
 	}
 	retmsg = waitfor(pid);
-	if(type=='|' || type=='<')
+	if(type=='|' || type=='<' || type=='_' || type=='^')
 		if(retmsg[0]!=0)
 			warn_s(Wbadstatus, retmsg);
 	if(downloaded)
--- a/sys/src/cmd/tee.c
+++ b/sys/src/cmd/tee.c
@@ -5,9 +5,12 @@
 #include <u.h>
 #include <libc.h>
 
+enum {
+	FDSTART = 3,
+};
+
 int	uflag;
 int	aflag;
-int	openf[100];
 
 char in[8192];
 
@@ -42,19 +45,21 @@
 	n = 0;
 	while(*argv) {
 		if(aflag) {
-			openf[n] = open(argv[0], OWRITE);
-			if(openf[n] < 0)
-				openf[n] = create(argv[0], OWRITE, 0666);
-			seek(openf[n], 0L, 2);
+			i = open(argv[0], OWRITE);
+			if(i < 0)
+				i = create(argv[0], OWRITE, 0666);
+			seek(i, 0L, 2);
 		} else
-			openf[n] = create(argv[0], OWRITE, 0666);
-		if(openf[n] < 0) {
+			i = create(argv[0], OWRITE, 0666);
+		if(i < 0) {
 			fprint(2, "tee: cannot open %s: %r\n", argv[0]);
-		} else
+		} else {
+			if(i != n+FDSTART)
+				dup(i, n+FDSTART);
 			n++;
+		}
 		argv++;
 	}
-	openf[n++] = 1;
 
 	for(;;) {
 		r = read(0, in, sizeof in);
@@ -61,7 +66,8 @@
 		if(r <= 0)
 			exits(nil);
 		for(i=0; i<n; i++)
-			write(openf[i], in, r);
+			write(i+FDSTART, in, r);
+		write(1, in, r);
 	}
 }
 
--- a/sys/src/libflate/inflatezlibblock.c
+++ b/sys/src/libflate/inflatezlibblock.c
@@ -61,7 +61,7 @@
 	if(ok != FlateOk)
 		return ok;
 
-	if(adler32(1, dst, bs.pos - dst) != ((bs.pos[0] << 24) | (bs.pos[1] << 16) | (bs.pos[2] << 8) | bs.pos[3]))
+	if(adler32(1, dst, bd.pos - dst) != ((bs.pos[0] << 24) | (bs.pos[1] << 16) | (bs.pos[2] << 8) | bs.pos[3]))
 		return FlateCorrupted;
 
 	return bd.pos - dst;