git: 9front

Download patch

ref: f3a54ddb2462444ce6297e381ff72cc19640e757
parent: c9b4b51810552ec37c2d25053a4e6ed0c30dd674
parent: c0371f6c589f18cf9a9ccfe11332732e7c057aac
author: ppatience0 <ppatience0@gmail.com>
date: Mon May 6 15:37:51 EDT 2013

merge

--- a/sys/include/bio.h
+++ b/sys/include/bio.h
@@ -7,7 +7,7 @@
 enum
 {
 	Bsize		= 8*1024,
-	Bungetsize	= 4,		/* space for ungetc */
+	Bungetsize	= UTFmax+1,	/* space for ungetc */
 	Bmagic		= 0x314159,
 	Beof		= -1,
 	Bbad		= -2,
--- a/sys/man/8/mkpaqfs
+++ b/sys/man/8/mkpaqfs
@@ -35,7 +35,7 @@
 byte blocks.
 Larger blocks make large files more compact but take longer to access.
 .I Blocksize
-must be in the range of 512 bytes to 52K bytes.
+must be in the range of 512 bytes to 512K bytes.
 If the
 .B -u
 option is set, the blocks are not compressed.
--- a/sys/src/9/ip/devip.c
+++ b/sys/src/9/ip/devip.c
@@ -407,13 +407,8 @@
 	case Qclone:
 		p = f->p[PROTO(c->qid)];
 		qlock(p);
-		if(waserror()){
-			qunlock(p);
-			nexterror();
-		}
 		cv = Fsprotoclone(p, ATTACHER(c));
 		qunlock(p);
-		poperror();
 		if(cv == nil) {
 			error(Enodev);
 			break;
@@ -1285,21 +1280,33 @@
 		if(c == nil){
 			c = malloc(sizeof(Conv));
 			if(c == nil)
-				error(Enomem);
-			qlock(c);
+				return nil;
+			if(waserror()){
+				qfree(c->rq);
+				qfree(c->wq);
+				qfree(c->eq);
+				qfree(c->sq);
+				free(c->ptcl);
+				free(c);
+				return nil;
+			}
 			c->p = p;
 			c->x = pp - p->conv;
 			if(p->ptclsize != 0){
 				c->ptcl = malloc(p->ptclsize);
-				if(c->ptcl == nil) {
-					free(c);
+				if(c->ptcl == nil)
 					error(Enomem);
-				}
 			}
-			*pp = c;
-			p->ac++;
 			c->eq = qopen(1024, Qmsg, 0, 0);
+			if(c->eq == nil)
+				error(Enomem);
 			(*p->create)(c);
+			if(c->rq == nil || c->wq == nil)
+				error(Enomem);
+			poperror();
+			qlock(c);
+			*pp = c;
+			p->ac++;
 			break;
 		}
 		if(canqlock(c)){
--- a/sys/src/9/ip/ipifc.c
+++ b/sys/src/9/ip/ipifc.c
@@ -316,8 +316,10 @@
 	Ipifc *ifc;
 
 	c->rq = qopen(QMAX, 0, 0, 0);
-	c->sq = qopen(QMAX, 0, 0, 0);
 	c->wq = qopen(QMAX, Qkick, ipifckick, c);
+	c->sq = qopen(QMAX, 0, 0, 0);
+	if(c->rq == nil || c->wq == nil || c->sq == nil)
+		error(Enomem);
 	ifc = (Ipifc*)c->ptcl;
 	ifc->conv = c;
 	ifc->unbinding = 0;
--- a/sys/src/9/ip/netlog.c
+++ b/sys/src/9/ip/netlog.c
@@ -85,8 +85,11 @@
 		nexterror();
 	}
 	if(f->alog->opens == 0){
-		if(f->alog->buf == nil)
+		if(f->alog->buf == nil){
 			f->alog->buf = malloc(Nlog);
+			if(f->alog->buf == nil)
+				error(Enomem);
+		}
 		f->alog->rptr = f->alog->buf;
 		f->alog->end = f->alog->buf + Nlog;
 	}
--- a/sys/src/9/pc/uartpci.c
+++ b/sys/src/9/pc/uartpci.c
@@ -99,8 +99,9 @@
 	perlehead = perletail = nil;
 	ctlrno = 0;
 	for(p = pcimatch(nil, 0, 0); p != nil; p = pcimatch(p, 0, 0)){
-		if(p->ccrb != Pcibccomm || p->ccru > 2)
-			continue;
+		/* StarTech PCI8S9503V has ccru == 0x80 (other) */
+		if(p->ccrb != Pcibccomm || p->ccru > 2 && p->ccru != 0x80)
+ 			continue;
 
 		switch(p->did<<16 | p->vid){
 		default:
@@ -165,7 +166,14 @@
 			freq = 7372800;
 			switch(subid){
 			default:
+				print("uartpci: unknown perle subid %#ux\n", subid);
 				continue;
+			case (0x1588<<16)|0x10B5:	/* StarTech PCI8S9503V (P588UG) */
+				name = "P588UG";
+				/* max. baud rate is 921,600 */
+				freq = 1843200;
+				uart = uartpci(ctlrno, p, 2, 8, freq, name, 8);
+				break;
 			case (0x0011<<16)|0x12E0:	/* Perle PCI-Fast16 */
 				name = "PCI-Fast16";
 				uart = uartpci(ctlrno, p, 2, 16, freq, name, 8);
--- a/sys/src/9/port/aoe.h
+++ b/sys/src/9/port/aoe.h
@@ -65,7 +65,14 @@
 	AAFext		= 1<<6,
 };
 
-typedef struct {
+typedef	struct	Aoehdr	Aoehdr;
+typedef	struct	Aoeata	Aoeata;
+typedef	struct	Aoecfg	Aoecfg;
+typedef	struct	Aoemd	Aoemd;
+typedef	struct	Aoem	Aoem;
+typedef	struct	Aoerr	Aoerr;
+
+struct Aoehdr {
 	uchar	dst[Eaddrlen];
 	uchar	src[Eaddrlen];
 	uchar	type[2];
@@ -75,9 +82,9 @@
 	uchar	minor;
 	uchar	cmd;
 	uchar	tag[4];
-} Aoehdr;
+};
 
-typedef struct {
+struct Aoeata {
 	uchar	aflag;
 	uchar	errfeat;
 	uchar	scnt;
@@ -84,34 +91,34 @@
 	uchar	cmdstat;
 	uchar	lba[6];
 	uchar	res[2];
-} Aoeata;
+};
 
-typedef struct {
+struct Aoecfg {
 	uchar	bufcnt[2];
 	uchar	fwver[2];
 	uchar	scnt;
 	uchar	verccmd;
 	uchar	cslen[2];
-} Aoecfg;
+};
 
-typedef struct {
+struct Aoemd {
 	uchar	dres;
 	uchar	dcmd;
 	uchar	ea[Eaddrlen];
-} Aoemd;
+};
 
-typedef struct {
+struct Aoem {
 	uchar	mres;
 	uchar	mcmd;
 	uchar	merr;
 	uchar	mcnt;
-} Aoem;
+};
 
-typedef struct {
+typedef struct Aoerr {
 	uchar	rcmd;
 	uchar	nea;
 	uchar	ea0[];
-} Aoerr;
+};
 
 extern char Echange[];
 extern char Enotup[];
--- a/sys/src/9/port/chan.c
+++ b/sys/src/9/port/chan.c
@@ -1472,6 +1472,10 @@
 		/* save&update the name; domount might change c */
 		path = c->path;
 		incref(path);
+		if(waserror()){
+			pathclose(path);
+			nexterror();
+		}
 		m = nil;
 		if(!nomount)
 			domount(&c, &m, &path);
@@ -1482,6 +1486,7 @@
 		/* now it's our copy anyway, we can put the name back */
 		pathclose(c->path);
 		c->path = path;
+		poperror();
 
 		/* record whether c is on a mount point */
 		c->ismtpt = m!=nil;
--- a/sys/src/9/port/devaoe.c
+++ b/sys/src/9/port/devaoe.c
@@ -1,5 +1,5 @@
 /*
- *	© 2005-10 coraid
+ *	© 2005-13 coraid
  *	aoe storage initiator
  */
 
@@ -23,7 +23,12 @@
 #define uprint(...)	snprint(up->genbuf, sizeof up->genbuf, __VA_ARGS__);
 
 enum {
-	Maxunits	= 0xff,
+	Typebits		= 4,
+	Unitbits		= 12,
+	L3bits		= 4,
+	Maxtype		= (1<<Typebits)-1,
+	Maxunits	= (1<<Unitbits)-1,
+	Maxl3		= (1<<L3bits)-1,
 	Maxframes	= 128,
 	Maxmtu		= 100000,
 	Ndevlink	= 6,
@@ -31,11 +36,11 @@
 	Nnetlink	= 6,
 };
 
-#define TYPE(q)		((ulong)(q).path & 0xf)
-#define UNIT(q)		(((ulong)(q).path>>4) & 0xff)
-#define L(q)		(((ulong)(q).path>>12) & 0xf)
-#define QID(u, t) 	((u)<<4 | (t))
-#define Q3(l, u, t)	((l)<<8 | QID(u, t))
+#define TYPE(q)		((ulong)(q).path & Maxtype)
+#define UNIT(q)		(((ulong)(q).path>>Typebits) & Maxunits)
+#define L(q)		(((ulong)(q).path>>Typebits+Unitbits) & Maxl3)
+#define QID(u, t) 	((u)<<Typebits | (t))
+#define Q3(l, u, t)	((l)<<Typebits+Unitbits | QID(u, t))
 #define UP(d)		((d)->flag & Dup)
 
 #define	Ticks		MACHP(0)->ticks
@@ -520,7 +525,7 @@
 #define Nofail(d, s)	(((d)->flag&Dnofail) == Dnofail)
 
 static int
-hset(Aoedev *d, Frame *f, Aoehdr *h, int cmd)
+hset(Aoedev *d, Frame *f, Aoehdr *h, int cmd, int new)
 {
 	int i;
 	Devlink *l;
@@ -550,7 +555,9 @@
 	h->minor = d->minor;
 	h->cmd = cmd;
 
-	hnputl(h->tag, f->tag = newtag(d));
+	if(new)
+		f->tag = newtag(d);
+	hnputl(h->tag, f->tag);
 	f->dl = l;
 	f->nl = l->nl;
 	f->eaidx = i;
@@ -567,7 +574,7 @@
 	Aoehdr *h;
 
 	h = (Aoehdr*)f->hdr;
-	if(hset(d, f, h, h->cmd) == -1)
+	if(hset(d, f, h, h->cmd, 0) == -1)
 		return -1;
 	a = (Aoeata*)(f->hdr + Aoehsz);
 	n = f->bcnt;
@@ -771,19 +778,25 @@
 	return c;
 }
 
-static Aoedev*
-unitseq(ulong unit)
+static int
+unitseq(Chan *c, uint unit, Dir *dp)
 {
-	int i;
+	int i, rv;
+	Qid q;
 	Aoedev *d;
 
 	i = 0;
+	rv = -1;
 	rlock(&devs);
 	for(d = devs.d; d; d = d->next)
-		if(i++ == unit)
+		if(i++ == unit){
+			mkqid(&q, QID(d->unit, Qunitdir), 0, QTDIR);
+			devdir(c, q, unitname(d), 0, eve, 0555, dp);
+			rv = 1;
 			break;
+		}
 	runlock(&devs);
-	return d;
+	return rv;
 }
 
 static Aoedev*
@@ -915,11 +928,7 @@
 		if(s < Qtopfiles)
 			return topgen(c, Qtopbase + s, dp);
 		s -= Qtopfiles;
-		if((d = unitseq(s)) == 0)
-			return -1;
-		mkqid(&q, QID(d->unit, Qunitdir), 0, QTDIR);
-		devdir(c, q, unitname(d), 0, eve, 0555, dp);
-		return 1;
+		return unitseq(c, s, dp);
 	case Qtopctl:
 	case Qtoplog:
 		return topgen(c, TYPE(c->qid), dp);
@@ -1031,7 +1040,7 @@
 	f->nhdr = Aoehsz + Aoeatasz;
 	memset(f->hdr, 0, f->nhdr);
 	h = (Aoehdr*)f->hdr;
-	if(hset(d, f, h, ACata) == -1){
+	if(hset(d, f, h, ACata, 1) == -1){
 		d->inprocess = nil;
 		return;
 	}
@@ -1267,7 +1276,7 @@
 	int i;
 	char *state, *s, *p, *e;
 
-	s = p = malloc(READSTR);
+	s = p = smalloc(READSTR);
 	e = p + READSTR;
 
 	state = "down";
@@ -1454,7 +1463,7 @@
 	if(len > sizeof d->config)
 		error(Etoobig);
 	srb = srballoc(len);
-	s = smalloc(len);
+	s = malloc(len);
 	memmove(s, db, len);
 	if(waserror()){
 		srbfree(srb);
@@ -1480,7 +1489,7 @@
 	f->nhdr = Aoehsz + Aoecfgsz;
 	memset(f->hdr, 0, f->nhdr);
 	h = (Aoehdr*)f->hdr;
-	if(hset(d, f, h, ACconfig) == -1)
+	if(hset(d, f, h, ACconfig, 1) == -1)
 		return 0;
 	ch = (Aoecfg*)(f->hdr + Aoehsz);
 	f->srb = srb;
@@ -1705,7 +1714,7 @@
 	nl->mtu = mtu;
 	strncpy(nl->path, path, sizeof(nl->path)-1);
 	nl->path[sizeof(nl->path)-1] = 0;
-	memmove(nl->ea, ea, sizeof(nl->ea));
+	memmove(nl->ea, ea, sizeof nl->ea);
 	poperror();
 	nl->flag |= Dup;
 	unlock(&netlinks);
@@ -1748,7 +1757,7 @@
 
 	d = malloc(sizeof *d);
 	f = malloc(sizeof *f*Maxframes);
-	if(!d || !f) {
+	if(d == nil || f == nil) {
 		free(d);
 		free(f);
 		error("aoe device allocation failure");
@@ -1828,7 +1837,7 @@
 	f->nhdr = Aoehsz + Aoeatasz;
 	memset(f->hdr, 0, f->nhdr);
 	h = (Aoehdr*)f->hdr;
-	if(hset(d, f, h, ACata) == -1)
+	if(hset(d, f, h, ACata, 1) == -1)
 		return;
 	a = (Aoeata*)(f->hdr + Aoehsz);
 	f->srb = srbkalloc(0, 0);
@@ -1911,7 +1920,7 @@
 	if(n == Tmgmt || n == Tfree)
 		return;
 	d = mm2dev(nhgets(h->major), h->minor);
-	if(d == 0)
+	if(d == nil)
 		return;
 	if(f = getframe(d, n))
 		frameerror(d, f, s);
@@ -1923,7 +1932,7 @@
 	int cmd, cslen, blen;
 	uint n, major;
 	Aoedev *d;
-	Aoehdr *h;
+	Aoehdr *h, *h0;
 	Aoecfg *ch;
 	Devlink *l;
 	Frame *f;
@@ -1933,7 +1942,7 @@
 	ch = (Aoecfg*)(b->rp + Aoehsz);
 	major = nhgets(h->major);
 	n = nhgetl(h->tag);
-	if(n != Tmgmt){
+	if(n != Tmgmt && n != Tfree){
 		d = mm2dev(major, h->minor);
 		if(d == nil)
 			return;
@@ -1944,6 +1953,13 @@
 			eventlog("%æ: unknown response tag %ux\n", d, n);
 			return;
 		}
+		h0 = (Aoehdr*)f->hdr;
+		cmd = h0->cmd;
+		if(cmd != ACconfig){
+			qunlock(d);
+			eventlog("%æ: malicious server got ACconfig want %d; tag %ux\n", d, cmd, n);
+			return;
+		}
 		cslen = nhgets(ch->cslen);
 		blen = BLEN(b) - (Aoehsz + Aoecfgsz);
 		if(cslen < blen && BLEN(b) > 60)
@@ -1954,7 +1970,7 @@
 				d, n, cslen, blen);
 			cslen = blen;
 		}
-		memmove(f->dp, (uchar*)ch + Aoehsz + Aoecfgsz, cslen);
+		memmove(f->dp, b->rp + Aoehsz + Aoecfgsz, cslen);
 		srb = f->srb;
 		f->dp = nil;
 		f->srb = nil;
@@ -1997,11 +2013,13 @@
 	l = newdevlink(d, nl, h);		/* add this interface. */
 
 	d->fwver = nhgets(ch->fwver);
-	n = nhgets(ch->cslen);
-	if(n > sizeof d->config)
-		n = sizeof d->config;
-	d->nconfig = n;
-	memmove(d->config, (uchar*)ch + Aoehsz + Aoecfgsz, n);
+	cslen = nhgets(ch->cslen);
+	if(cslen > sizeof d->config)
+		cslen = sizeof d->config;
+	if(Aoehsz + Aoecfgsz + cslen > BLEN(b))
+		cslen = BLEN(b) - (Aoehsz + Aoecfgsz);
+	d->nconfig = cslen;
+	memmove(d->config, b->rp + Aoehsz + Aoecfgsz, cslen);
 
 	/* manually set mtu may be reset lower if conditions warrant */
 	if(l){
@@ -2028,10 +2046,12 @@
 	vlong s;
 
 	s = idfeat(d, id);
-	if(s == -1)
+	if(s == -1){
+		eventlog("%æ: idfeat returns -1\n", d);
 		return -1;
+	}
 	if((d->feat&Dlba) == 0){
-		dprint("%æ: no lba support\n", d);
+		eventlog("%æ: no lba support\n", d);
 		return -1;
 	}
 	d->flag |= Dup;
@@ -2078,10 +2098,10 @@
 static void
 atarsp(Block *b)
 {
-	uint n;
+	uint n, cmd;
 	ushort major;
 	Aoeata *ahin, *ahout;
-	Aoehdr *h;
+	Aoehdr *h, *h0;
 	Aoedev *d;
 	Frame *f;
 	Srb *srb;
@@ -2098,11 +2118,20 @@
 		nexterror();
 	}
 	n = nhgetl(h->tag);
+	if(n == Tfree || n == Tmgmt)
+		goto bail;
 	f = getframe(d, n);
 	if(f == nil){
-		dprint("%æ: unexpected response; tag %ux\n", d, n);
+		eventlog("%æ: unexpected response; tag %ux\n", d, n);
 		goto bail;
 	}
+	h0 = (Aoehdr*)f->hdr;
+	cmd = h0->cmd;
+	if(cmd != ACata){
+		eventlog("%æ: malicious server got ACata want %d; tag %ux\n", d, cmd, n);
+		goto bail;
+	}
+
 	rtupdate(f->dl, tsince(f->tag));
 	ahout = (Aoeata*)(f->hdr + Aoehsz);
 	srb = f->srb;
@@ -2168,7 +2197,7 @@
 netrdaoeproc(void *v)
 {
 	int idx;
-	char name[Maxpath], *s;
+	char name[Maxpath+1], *s;
 	Aoehdr *h;
 	Block *b;
 	Netlink *nl;
@@ -2197,13 +2226,14 @@
 		h = (Aoehdr*)b->rp;
 		if(h->verflag & AFrsp)
 			if(s = aoeerror(h)){
-				eventlog("%s: %s\n", nl->path, s);
+				eventlog("%s: %d.%d %s\n", nl->path,
+					h->major[0]<<8 | h->major[1], h->minor, s);
 				errrsp(b, s);
 			}else if(h->cmd == ACata)
 				atarsp(b);
 			else if(h->cmd == ACconfig)
 				qcfgrsp(b, nl);
-			else if((h->cmd & 0xf0) == 0){
+			else if((h->cmd & 0xf0) != 0xf0){
 				eventlog("%s: unknown cmd %d\n",
 					nl->path, h->cmd);
 				errrsp(b, "unknown command");
--- a/sys/src/9/port/devloopback.c
+++ b/sys/src/9/port/devloopback.c
@@ -131,8 +131,12 @@
 	}
 
 	c = devattach('X', spec);
-	lb = &loopbacks[dev];
+	if(waserror()){
+		chanfree(c);
+		nexterror();
+	}
 
+	lb = &loopbacks[dev];
 	qlock(lb);
 	if(waserror()){
 		lb->ref--;
@@ -167,6 +171,8 @@
 	}
 	poperror();
 	qunlock(lb);
+
+	poperror();
 
 	mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR);
 	c->aux = lb;
--- a/sys/src/9/port/devpipe.c
+++ b/sys/src/9/port/devpipe.c
@@ -61,6 +61,10 @@
 	Chan *c;
 
 	c = devattach('|', spec);
+	if(waserror()){
+		chanfree(c);
+		nexterror();
+	}
 	p = malloc(sizeof(Pipe));
 	if(p == 0)
 		exhausted("memory");
@@ -73,10 +77,11 @@
 	}
 	p->q[1] = qopen(conf.pipeqsize, 0, 0, 0);
 	if(p->q[1] == 0){
-		free(p->q[0]);
+		qfree(p->q[0]);
 		free(p);
 		exhausted("memory");
 	}
+	poperror();
 
 	lock(&pipealloc);
 	p->path = ++pipealloc.path;
--- a/sys/src/9/port/devsd.c
+++ b/sys/src/9/port/devsd.c
@@ -980,6 +980,7 @@
 	SDunit *unit;
 
 	unit = r->unit;
+	unit->sense[0] = 0x80 | 0x70;	/* valid; fixed-format */
 	unit->sense[2] = key;
 	unit->sense[12] = asc;
 	unit->sense[13] = ascq;
--- a/sys/src/ape/lib/ap/plan9/_buf.c
+++ b/sys/src/ape/lib/ap/plan9/_buf.c
@@ -20,10 +20,9 @@
 	int	waittime;		/* time for timer process to wait */
 	fd_set	rwant;			/* fd's that select wants to read */
 	fd_set	ewant;			/* fd's that select wants to know eof info on */
-	Muxbuf	bufs[INITBUFS];		/* can grow, via segbrk() */
+	Muxbuf	bufs[OPEN_MAX];
 } Muxseg;
 
-#define MUXADDR ((void*)0x6000000)
 static Muxseg *mux = 0;			/* shared memory segment */
 
 /* _muxsid and _killmuxsid are known in libbsd's listen.c */
@@ -50,14 +49,13 @@
 int
 _startbuf(int fd)
 {
-	long i, n, slot;
-	int pid, sid;
+	int i, pid, sid;
 	Fdinfo *f;
 	Muxbuf *b;
 
 	if(mux == 0){
 		_RFORK(RFREND);
-		mux = (Muxseg*)_SEGATTACH(0, "shared", MUXADDR, sizeof(Muxseg));
+		mux = (Muxseg*)_SEGATTACH(0, "shared", 0, sizeof(Muxseg));
 		if((long)mux == -1){
 			_syserrno();
 			return -1;
@@ -66,7 +64,7 @@
 		atexit(_killmuxsid);
 	}
 
-	if(fd == -1)
+	if(fd < 0)
 		return 0;
 
 	lock(&mux->lock);
@@ -85,17 +83,16 @@
 		errno = EIO;
 		return -1;
 	}
-
-	slot = mux->curfds++;
-	if(mux->curfds > INITBUFS) {
-		if(_SEGBRK(mux, mux->bufs+mux->curfds) < 0){
-			_syserrno();
-			unlock(&mux->lock);
-			return -1;
-		}
+	for(b = mux->bufs; b < &mux->bufs[mux->curfds]; b++)
+		if(b->fd == -1)
+			goto Found;
+	if(mux->curfds >= OPEN_MAX){
+		unlock(&mux->lock);
+		errno = ENFILE;
+		return -1;
 	}
-
-	b = &mux->bufs[slot];
+	mux->curfds++;
+Found:
 	b->n = 0;
 	b->putnext = b->data;
 	b->getnext = b->data;
@@ -299,7 +296,7 @@
 int
 select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *timeout)
 {
-	int n, i, tmp, t, slots, fd, err;
+	int n, i, t, slots, fd, err;
 	Fdinfo *f;
 	Muxbuf *b;
 
@@ -412,7 +409,7 @@
 static int timerpid;
 
 static void
-alarmed(int v)
+alarmed(int)
 {
 	timerreset = 1;
 }
@@ -500,9 +497,6 @@
 static int
 copynotehandler(void *u, char *msg)
 {
-	int i;
-	void(*f)(int);
-
 	if(_finishing)
 		_finish(0, 0);
 	_NOTED(1);
--- a/sys/src/ape/lib/ap/plan9/frexp.c
+++ b/sys/src/ape/lib/ap/plan9/frexp.c
@@ -6,6 +6,7 @@
 #define	MASK	0x7ffL
 #define	SHIFT	20
 #define	BIAS	1022L
+#define	SIG	52
 
 typedef	union
 {
@@ -25,13 +26,18 @@
 double
 frexp(double d, int *ep)
 {
-	Cheat x;
+	Cheat x, a;
 
-	if(d == 0) {
-		*ep = 0;
-		return 0;
-	}
+	*ep = 0;
+	/* order matters: only isNaN can operate on NaN */
+	if(isNaN(d) || isInf(d, 0) || d == 0)
+		return d;
 	x.d = d;
+	a.d = fabs(d);
+	if((a.ms >> SHIFT) == 0){	/* normalize subnormal numbers */
+		x.d = (double)(1ULL<<SIG) * d;
+		*ep = -SIG;
+	}
 	*ep = ((x.ms >> SHIFT) & MASK) - BIAS;
 	x.ms &= ~(MASK << SHIFT);
 	x.ms |= BIAS << SHIFT;
--- a/sys/src/boot/pc/pbs.s
+++ b/sys/src/boot/pc/pbs.s
@@ -156,43 +156,47 @@
 
 _found:
 	CLR(rBX)
-
 	LW(_rootsize(SB), rAX)		/* calculate and save Xrootsz */
 	LWI(0x20, rCX)
 	MUL(rCX)
 	LW(_sectsize(SB), rCX)
-	PUSHR(rCX)
 	DEC(rCX)
 	ADD(rCX, rAX)
 	ADC(rBX, rDX)
-	POPR(rCX)			/* _sectsize(SB) */
+	INC(rCX)
 	DIV(rCX)
 	PUSHR(rAX)			/* Xrootsz */
 
-	LXW(0x1a, xSI, rAX)		/* starting sector address */
-	DEC(rAX)			/* that's just the way it is */
-	DEC(rAX)
-	LB(_clustsize(SB), rCL)
-	CLRB(rCH)
+	CLR(rCX)
+	LXW(0x1a, xSI, rAX)		/* start cluster low */
+	LXW(0x14, xSI, rBX)		/* start cluster high */
+	SUBI(2, rAX)			/* cluster -= 2 */
+	SBB(rCX, rBX)
+
+	LB(_clustsize(SB), rCL)		/* convert to sectors (AX:DX) */
+	IMUL(rCX, rBX)
 	MUL(rCX)
+	ADD(rBX, rDX)
+
 	LW(_volid(SB), rCX)		/* Xrootlo */
 	ADD(rCX, rAX)
 	LW(_volid+2(SB), rCX)		/* Xroothi */
 	ADC(rCX, rDX)
+
+	CLR(rBX)
 	POPR(rCX)			/* Xrootsz */
 	ADD(rCX, rAX)
 	ADC(rBX, rDX)
 
-	PUSHR(rAX)			/* calculate how many sectors to read */
+	PUSHR(rAX)			/* calculate how many sectors to read (CX) */
 	PUSHR(rDX)
 	LXW(0x1c, xSI, rAX)
 	LXW(0x1e, xSI, rDX)
 	LW(_sectsize(SB), rCX)
-	PUSHR(rCX)
 	DEC(rCX)
 	ADD(rCX, rAX)
 	ADC(rBX, rDX)
-	POPR(rCX)	/* _sectsize(SB) */
+	INC(rCX)
 	DIV(rCX)
 	MW(rAX, rCX)
 	POPR(rBX)
--- a/sys/src/boot/pc/x16.h
+++ b/sys/src/boot/pc/x16.h
@@ -117,6 +117,7 @@
 			BYTE $i;
 #define SHRBI(i, r)	OPrr(0xC0, 0x05, r);	/* r>>i -> r */		\
 			BYTE $i;
+#define SBB(r0, r1)	OPrr(0x19, r0, r1)	/* r1-r0 -> r1 */
 #define SUB(r0, r1)	OPrr(0x29, r0, r1)	/* r1-r0 -> r1 */
 #define SUBI(i, r)	OP(0x81, 0x03, 0x05, r);/* r-i -> r */		\
 			WORD $i;
--- a/sys/src/cmd/cc/lex.c
+++ b/sys/src/cmd/cc/lex.c
@@ -1072,7 +1072,7 @@
 		 */
 		i = 2;
 		if(longflg)
-			i = 4;
+			i = 6;
 		l = 0;
 		for(; i>0; i--) {
 			c = getc();
@@ -1102,7 +1102,7 @@
 		 */
 		i = 2;
 		if(longflg)
-			i = 5;
+			i = 8;
 		l = c - '0';
 		for(; i>0; i--) {
 			c = getc();
--- a/sys/src/cmd/sort.c
+++ b/sys/src/cmd/sort.c
@@ -1430,7 +1430,8 @@
 	k->klen = cl;
 
 	if(args.vflag) {
-		write(2, l->line, l->llen);
+		if(write(2, l->line, l->llen) != l->llen)
+			exits("write");
 		for(i=0; i<k->klen; i++) {
 			fprint(2, " %.2x", k->key[i]);
 			if(k->key[i] == 0x00 || k->key[i] == 0xff)
--- a/sys/src/cmd/unix/u9fs/makefile
+++ b/sys/src/cmd/unix/u9fs/makefile
@@ -42,7 +42,8 @@
 	strecpy.o\
 	tokenize.o\
 	u9fs.o\
-	utfrune.o
+	utflen.o\
+	utfrune.o\
 
 HFILES=\
 	fcall.h\
--- a/sys/src/cmd/unix/u9fs/oldfcall.c
+++ b/sys/src/cmd/unix/u9fs/oldfcall.c
@@ -1,6 +1,7 @@
 #include <plan9.h>
 #include <fcall.h>
 #include <oldfcall.h>
+#include <stdlib.h>
 
 /*
  * routines to package the old protocol in the new structures.
--- a/sys/src/cmd/unix/u9fs/plan9.h
+++ b/sys/src/cmd/unix/u9fs/plan9.h
@@ -97,7 +97,8 @@
 	UTFmax		= 3,		/* maximum bytes per rune */
 	Runesync	= 0x80,		/* cannot represent part of a UTF sequence (<) */
 	Runeself	= 0x80,		/* rune and UTF sequences are the same (<) */
-	Runeerror	= 0x80		/* decoding error in UTF */
+	Runeerror	= 0x80,		/* decoding error in UTF */
+	Runemax		= 0xFFFF,	/* 16 bit rune */
 };
 
 extern	int	runetochar(char*, Rune*);
--- a/sys/src/cmd/unix/u9fs/safecpy.c
+++ b/sys/src/cmd/unix/u9fs/safecpy.c
@@ -1,4 +1,4 @@
-#include <stdio.h>
+#include <string.h>
 
 void
 safecpy(char *to, char *from, int tolen)
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/utflen.c
@@ -1,0 +1,22 @@
+#include <plan9.h>
+
+int
+utflen(char *s)
+{
+	int c;
+	long n;
+	Rune rune;
+
+	n = 0;
+	for(;;) {
+		c = *(uchar*)s;
+		if(c < Runeself) {
+			if(c == 0)
+				return n;
+			s++;
+		} else
+			s += chartorune(&rune, s);
+		n++;
+	}
+	return 0;
+}
--- a/sys/src/cmd/upas/ned/nedmail.c
+++ b/sys/src/cmd/upas/ned/nedmail.c
@@ -62,6 +62,7 @@
 	{ "text",			"txt",	1,	0	},
 	{ "message/rfc822",		"msg",	0,	0	},
 	{ "image/bmp",			"bmp",	0,	"image"	},
+	{ "image/jpg",			"jpg",	0,	"image"	},
 	{ "image/jpeg",			"jpg",	0,	"image"	},
 	{ "image/gif",			"gif",	0,	"image"	},
 	{ "image/png",			"png",	0,	"image"	},
--- a/sys/src/games/gb/disasm.c
+++ b/sys/src/games/gb/disasm.c
@@ -497,7 +497,7 @@
 	[0xE5] 0,
 	[0xE6] 1,
 	[0xE7] 0,
-	[0xE8] 0,
+	[0xE8] 1,
 	[0xE9] 0,
 	[0xEA] 2,
 	[0xEB] 0,
--- a/sys/src/libc/port/frexp.c
+++ b/sys/src/libc/port/frexp.c
@@ -9,18 +9,24 @@
 #define	MASK	0x7ffL
 #define	SHIFT	20
 #define	BIAS	1022L
+#define	SIG	52
 
 double
 frexp(double d, int *ep)
 {
-	FPdbleword x;
+	FPdbleword x, a;
 
-	if(d == 0) {
-		*ep = 0;
-		return 0;
-	}
+	*ep = 0;
+	/* order matters: only isNaN can operate on NaN */
+	if(isNaN(d) || isInf(d, 0) || d == 0)
+		return d;
 	x.x = d;
-	*ep = ((x.hi >> SHIFT) & MASK) - BIAS;
+	a.x = fabs(d);
+	if((a.hi >> SHIFT) == 0){	/* normalize subnormal numbers */
+		x.x = (double)(1ULL<<SIG) * d;
+		*ep = -SIG;
+	}
+	*ep += ((x.hi >> SHIFT) & MASK) - BIAS;
 	x.hi &= ~(MASK << SHIFT);
 	x.hi |= BIAS << SHIFT;
 	return x.x;
--- a/sys/src/libdraw/font.c
+++ b/sys/src/libdraw/font.c
@@ -274,7 +274,8 @@
 	if(fi->width == 0)
 		goto TryPJW;
 	wid = (fi+1)->x - fi->x;
-	if(f->width < wid || f->width == 0 || f->maxdepth < subf->f->bits->depth){
+	if(f->width < wid || f->width == 0 || f->maxdepth < subf->f->bits->depth
+	|| (f->display != nil && f->cacheimage == nil)){
 		/*
 		 * Flush, free, reload (easier than reformatting f->b)
 		 */
--- a/sys/src/libmach/0.c
+++ b/sys/src/libmach/0.c
@@ -4,6 +4,7 @@
  * currently no compiler - not related to 0c
  */
 #include <u.h>
+#include <libc.h>
 #include <bio.h>
 #include "mips2ureg.h"
 #include <mach.h>
--- a/sys/src/libmach/2.c
+++ b/sys/src/libmach/2.c
@@ -3,6 +3,7 @@
  */
 #include <u.h>
 #include "/68020/include/ureg.h"
+#include <libc.h>
 #include <bio.h>
 #include <mach.h>
 
--- a/sys/src/libmach/5.c
+++ b/sys/src/libmach/5.c
@@ -2,6 +2,7 @@
  * arm definition
  */
 #include <u.h>
+#include <libc.h>
 #include <bio.h>
 #include "/arm/include/ureg.h"
 #include <mach.h>
--- a/sys/src/libmach/7.c
+++ b/sys/src/libmach/7.c
@@ -2,6 +2,7 @@
  * alpha definition
  */
 #include <u.h>
+#include <libc.h>
 #include <bio.h>
 #include "/alpha/include/ureg.h"
 #include <mach.h>
--- a/sys/src/libmach/8.c
+++ b/sys/src/libmach/8.c
@@ -2,6 +2,7 @@
  * 386 definition
  */
 #include <u.h>
+#include <libc.h>
 #include <bio.h>
 #include "/386/include/ureg.h"
 #include <mach.h>
--- a/sys/src/libmach/k.c
+++ b/sys/src/libmach/k.c
@@ -2,6 +2,7 @@
  * sparc definition
  */
 #include <u.h>
+#include <libc.h>
 #include <bio.h>
 #include "/sparc/include/ureg.h"
 #include <mach.h>
--- a/sys/src/libmach/q.c
+++ b/sys/src/libmach/q.c
@@ -3,6 +3,7 @@
  *	forsyth@terzarima.net
  */
 #include <u.h>
+#include <libc.h>
 #include <bio.h>
 #include "/power/include/ureg.h"
 #include <mach.h>
--- a/sys/src/libmach/u.c
+++ b/sys/src/libmach/u.c
@@ -2,6 +2,7 @@
  * sparc64 definition
  */
 #include <u.h>
+#include <libc.h>
 #include <bio.h>
 #include "/sparc64/include/ureg.h"
 #include <mach.h>
--- a/sys/src/libmach/v.c
+++ b/sys/src/libmach/v.c
@@ -2,6 +2,7 @@
  * mips definition
  */
 #include <u.h>
+#include <libc.h>
 #include <bio.h>
 #include "/mips/include/ureg.h"
 #include <mach.h>
--- a/sys/src/libmach/vcodas.c
+++ b/sys/src/libmach/vcodas.c
@@ -492,9 +492,13 @@
 		case 16:
 			m = "rfe";
 			break;
-	
-		case 32:
+
+		case 24:
 			m = "eret";
+			break;	
+
+		case 32:
+			m = "wait";
 			break;
 		}
 		if (m) {
--- a/sys/src/libmach/vdb.c
+++ b/sys/src/libmach/vdb.c
@@ -415,7 +415,9 @@
 sll(Opcode *o, Instr *i)
 {
 	if (i->w0 == 0)
-		bprint(i, "NOOP");
+		bprint(i, "NOOP");	/* unofficial nop */
+	else if (i->w0 == 0xc0)		/* 0xc0: SLL $3,R0 */
+		bprint(i, "EHB");
 	else if (i->rd == i->rt)
 		format(o->mnemonic, i, "$%a,R%d");
 	else
@@ -962,8 +964,12 @@
 			m = "RFE";
 			break;
 	
-		case 32:
+		case 24:
 			m = "ERET";
+			break;
+
+		case 32:
+			m = "WAIT";
 			break;
 		}
 		if (m) {
--