diff -Nru /n/sources/plan9/sys/src/9/rb/clock.c /sys/src/9/rb/clock.c
--- /n/sources/plan9/sys/src/9/rb/clock.c	Mon Jul 15 22:01:47 2013
+++ /sys/src/9/rb/clock.c	Sun May 19 00:00:00 2019
@@ -12,31 +12,32 @@
 
 enum {
 	Cyccntres	= 2, /* counter advances at ½ clock rate (mips 24k) */
-	Basetickfreq	= 680*Mhz / Cyccntres,	/* rb450g */
 };
 
+ulong basetickfreq = 680*Mhz / Cyccntres;	/* rb450g */
+
 void (*kproftimer)(ulong);
 
 void
 silencewdog(void)
 {
-	*Rstwdogtimer = Basetickfreq * 2 * 5;	/* pet the dog */
+	*Rstwdogtimer = basetickfreq * 2 * 5;	/* pet the dog */
 }
 
 void
 sicwdog(void)
 {
-	*Rstwdogtimer = Basetickfreq * 2;
+	*Rstwdogtimer = basetickfreq * 2;
 	*Rstwdogctl = Wdogreset;		/* wake the dog */
 }
 
 void
 wdogreset(void)
 {
-	*Rstwdogtimer = Basetickfreq / 100;
+	*Rstwdogtimer = basetickfreq / 100;
 	*Rstwdogctl = Wdogreset;		/* wake the dog */
 	coherence();
-	*Rstwdogtimer = Basetickfreq / 10000;
+	*Rstwdogtimer = basetickfreq / 10000;
 	coherence();
 }
 
@@ -74,12 +75,12 @@
 
 	speed = m->speed;
 	if (speed == 0)
-		speed = Basetickfreq / Mhz * Cyccntres;
+		speed = basetickfreq / Mhz * Cyccntres;
 	cyc = (ulong)l * (speed / Cyccntres);
 	s = splhi();
 	cnt = rdcount();
 	x = cnt + cyc;
-	if (x < cnt || x >= ~0ul - Basetickfreq) {
+	if (x < cnt || x >= ~0ul - basetickfreq) {
 		/* counter will wrap between now and x, or x is too near ~0 */
 		wrcount(0);			/* somewhat drastic */
 		wrcompare(rdcompare() - cnt);	/* match new count */
@@ -143,10 +144,10 @@
 			iprint("again...");
 	} while (cyc < 0);
 	/*
-	 * Instrs instructions took cyc cycles @ Basetickfreq Hz.
+	 * Instrs instructions took cyc cycles @ basetickfreq Hz.
 	 * round the result.
 	 */
-	return (((vlong)Basetickfreq * Instrs) / cyc + Mhz/2) / Mhz;
+	return (((vlong)basetickfreq * Instrs) / cyc + Mhz/2) / Mhz;
 }
 
 void
@@ -154,6 +155,7 @@
 {
 	int mips;
 
+	basetickfreq = m->hz / Cyccntres;
 	silencewdog();
 
 	/*
@@ -169,11 +171,8 @@
 	if(m->delayloop == 0)
 		m->delayloop = 1;
 
-	m->speed = mips;
-	m->hz = m->speed*Mhz;
-
-	m->maxperiod = Basetickfreq / HZ;
-	m->minperiod = Basetickfreq / (100*HZ);
+	m->maxperiod = basetickfreq / HZ;
+	m->minperiod = basetickfreq / (100*HZ);
 	wrcompare(rdcount()+m->maxperiod);
 
 	/*
@@ -188,7 +187,7 @@
 
 /*
  * Tval is supposed to be in fastticks units.
- * One fasttick unit is 1/Basetickfreq seconds.
+ * One fasttick unit is 1/basetickfreq seconds.
  */
 void
 timerset(Tval next)
@@ -221,7 +220,7 @@
 	ulong delta, count;
 
 	if(hz)
-		*hz = Basetickfreq;
+		*hz = basetickfreq;
 
 	/* avoid reentry on interrupt or trap, to prevent recursion */
 	x = splhi();
@@ -269,7 +268,7 @@
 	*cycp = fastticks(nil);
 }
 
-Lock mpsynclock;
+static Lock mpsynclock;
 
 /*
  *  synchronize all clocks with processor 0
diff -Nru /n/sources/plan9/sys/src/9/rb/dat.h /sys/src/9/rb/dat.h
--- /n/sources/plan9/sys/src/9/rb/dat.h	Sun Jul 21 06:22:29 2013
+++ /sys/src/9/rb/dat.h	Thu Aug 16 00:00:00 2018
@@ -8,6 +8,7 @@
 typedef struct Notsave	Notsave;
 typedef struct Pcidev	Pcidev;
 typedef struct PMMU	PMMU;
+typedef struct Rbconf	Rbconf;
 typedef struct Softtlb	Softtlb;
 typedef struct Ureg	Ureg;
 typedef struct Proc	Proc;
@@ -60,6 +60,7 @@
 	ulong	nmach;		/* processors */
 	ulong	nproc;		/* processes */
 	Confmem	mem[1];
+	/* npage may exclude kernel pages */
 	ulong	npage;		/* total physical pages of memory */
 	ulong	upages;		/* user page pool */
 	ulong	nimage;		/* number of page cache image headers */
@@ -71,6 +72,14 @@
 	int	nuart;		/* number of uart devices */
 };
 
+struct Rbconf {
+	char	*ether0mac;
+	char	*memsize;
+	char	*hz;
+	char	*console;
+	char	*null;		/* act as end of argv when rebooting */
+};
+
 /*
  * floating point registers
  */
@@ -211,7 +212,11 @@
 struct
 {
 	Lock;
-	long	machs;		/* bitmap of processors */
+	union {
+		uvlong	machs;		/* bitmap of active CPUs */
+		ulong	machsmap[(MAXMACH+BI2WD-1)/BI2WD];
+	};
+	int	nmachs;			/* number of bits set in machs(map) */
 	short	exiting;
 	int	ispanic;
 }active;
@@ -223,3 +228,5 @@
 extern FPsave initfp;
 
 extern	int normalprint;
+extern ulong memsize;
+extern Rbconf rbconf;
diff -Nru /n/sources/plan9/sys/src/9/rb/devether.c /sys/src/9/rb/devether.c
--- /n/sources/plan9/sys/src/9/rb/devether.c	Tue Jul 23 01:37:08 2013
+++ /sys/src/9/rb/devether.c	Tue May 26 00:00:00 2020
@@ -1,15 +1,11 @@
 /*
- * Atheros 71xx ethernets for rb450g.
+ * Atheros 7161 & 8316 ethernets for rb450g.
+ * second 7161 has the 8316 switch with 4 ports on it.
  *
- * all 5 PHYs are accessible only through first ether's register space.
+ * all 5 PHYs are accessible only through first 7161's register space.
  *
- * TODO:
- *	promiscuous mode.
- *	make ether1 work: probably needs mii/phy initialisation,
+ * TODO: make second 7161 work: probably needs mii/phy initialisation,
  *	maybe needs 8316 switch code too (which requires mdio, phy, etc. glop).
- * to maybe do some day:
- *	dig mac addresses out & config phy/mii via spi or other grot and swill
- *	(instead of editing rb config file).
  */
 #include	"u.h"
 #include	"../port/lib.h"
@@ -32,9 +28,6 @@
 	Rbsz	= ETHERMAXTU + 4,	/* 4 for CRC */
 };
 
-extern uchar arge0mac[Eaddrlen];	/* see rb config file */
-extern uchar arge1mac[Eaddrlen];
-
 typedef struct Arge Arge;
 typedef struct Ctlr Ctlr;
 typedef struct Desc Desc;
@@ -282,9 +275,7 @@
 
 	/* receiver */
 	Rendez	rrendez;
-	uint	rintr;			/* count */
 	int	pktstoread;		/* flag */
-	int	discard;
 	/* rx descriptors */
 	Desc*	rdba;			/* base address */
 	Block**	rd;
@@ -294,7 +285,6 @@
 
 	/* transmitter */
 	Rendez	trendez;
-	uint	tintr;			/* count */
 	int	pktstosend;		/* flag */
 	int	ntq;
 	/* tx descriptors */
@@ -302,18 +292,22 @@
 	Block**	td;
 	uint	tdh;			/* head */
 	uint	tdt;			/* tail */
+
+	/* stats */
+	uint	rintr;			/* count */
+	int	discard;
+	uint	tintr;			/* count */
 };
 
 struct Etherif {
 	uintptr	regs;
 	int	irq;
-	uchar	*mac;
 	int	phymask;
 };
 
 static Etherif etherifs[] = {
-	{ 0x1a000000, ILenet0, arge0mac, 1<<4 },
-	{ 0x19000000, ILenet1, arge1mac, MASK(4) },
+	{ 0x1a000000, ILenet0, 1<<4 },
+	{ 0x19000000, ILenet1, MASK(4) },
 };
 
 static Ether *etherxx[MaxEther];
@@ -381,7 +375,7 @@
 		Swglobalmtumask	= 0x7fff,
 };
 
-#ifdef NOTYET
+#ifdef INIT_SWITCH
 void *
 devicegetparent(int)
 {
@@ -1051,17 +1045,14 @@
 		delay(100);
 	}
 
-	/*
-	 * Set all Ethernet address registers to the same initial values
-	 * set all four addresses to 66-88-aa-cc-dd-ee
-	 */
 	eaddr = ether->ea;
 	arge->staaddr1 = eaddr[2]<<24 | eaddr[3]<<16 | eaddr[4]<<8  | eaddr[5];
 	arge->staaddr2 = eaddr[0]<< 8 | eaddr[1];
 
-	arge->fifocfg[0] = Fifocfg0all << Fifocfg0enshift; /* undocumented magic */
-	arge->fifocfg[1] = 0x0fff0000;	/* undocumented magic */
-	arge->fifocfg[2] = 0x00001fff;	/* undocumented magic */
+	/* undocumented magic */
+	arge->fifocfg[0] = Fifocfg0all << Fifocfg0enshift;
+	arge->fifocfg[1] = 0x0fff0000;
+	arge->fifocfg[2] = 0x00001fff;
 
 	arge->fiforxfiltmatch = Ffmatchdflt;
 	arge->fiforxfiltmask  = Ffmaskdflt;
@@ -1121,11 +1112,14 @@
 
 /*
  * strategy: RouterBOOT has initialised arge0, try to leave it alone.
+ * sadly, staaddr[12] seem to have been tampered with.
  * copy arge0 registers to arge1, with a few exceptions.
  */
 static int
 athreset(Ether *ether)
 {
+	char *p;
+	uchar zeromac[Eaddrlen];
 	Arge *arge;
 	Ctlr *ctlr;
 	Etherif *ep;
@@ -1146,7 +1140,13 @@
 
 		ether->port = (uint)arge;
 		ether->irq = ep->irq;
-		memmove(ether->ea, ep->mac, Eaddrlen);
+		if (rbconf.ether0mac && (p = strchr(rbconf.ether0mac, '=')))
+			parseether(ether->ea, p+1);
+		if(memcmp(ether->ea, zeromac, sizeof zeromac) == 0)
+			panic("ether%d: no mac", ether->ctlrno);
+		ether->ea[5] += ether->ctlrno;
+		if (ether->ea[5] == 0)
+			ether->ea[4]++;
 		ether->ifstat = ifstat;
 		ether->promiscuous = promiscuous;
 		ether->multicast = multicast;
@@ -1155,6 +1155,9 @@
 	athhwreset(ether);
 	return 0;
 }
+
+extern	int	sprint(char*, char*, ...);
+#pragma	varargck	argpos	sprint		2
 
 static Ether*
 etherprobe(int ctlrno)
diff -Nru /n/sources/plan9/sys/src/9/rb/faultmips.c /sys/src/9/rb/faultmips.c
--- /n/sources/plan9/sys/src/9/rb/faultmips.c	Wed May 14 14:44:59 2014
+++ /sys/src/9/rb/faultmips.c	Wed May  7 00:00:00 2014
@@ -226,10 +226,10 @@
 }
 
 /*
- * called in syscallfmt.c, sysfile.c, sysproc.c
+ * called in sysfile.c
  */
 void
-validalign(uintptr addr, unsigned align)
+validalign(ulong addr, ulong align)
 {
 	/*
 	 * Plan 9 is a 32-bit O/S, and the hardware it runs on
diff -Nru /n/sources/plan9/sys/src/9/rb/fns.h /sys/src/9/rb/fns.h
--- /n/sources/plan9/sys/src/9/rb/fns.h	Wed May 14 14:45:00 2014
+++ /sys/src/9/rb/fns.h	Wed Jun 13 00:00:00 2018
@@ -12,11 +12,11 @@
 void	coherence(void);
 void	cycles(uvlong *);
 void	dcflush(void*, ulong);
+void	validalign(ulong, ulong);
 void	faultmips(Ureg*, int, int);
 ulong	fcr31(void);
 void	firmware(int);
 void	fpclear(void);
-void	fpsave(FPsave *);
 void	fptrap(Ureg*);
 int	fpuemu(Ureg *);
 void	fpwatch(Ureg *);
@@ -122,11 +122,13 @@
 void	touser(uintptr);
 void	unleash(void);
 #define	userureg(ur) ((ur)->status & KUSER)
-void	validalign(uintptr, unsigned);
 void	vecinit(void);
 void	vector0(void);
 void	vector100(void);
 void	vector180(void);
+void*	vmap(uintptr, usize);
+int	vmapsync(ulong);
+void	vunmap(void*, usize);
 void	wdogreset(void);
 void	wrcompare(ulong);
 void	wrcount(ulong);
diff -Nru /n/sources/plan9/sys/src/9/rb/fpimips.c /sys/src/9/rb/fpimips.c
--- /n/sources/plan9/sys/src/9/rb/fpimips.c	Tue Jul 23 00:38:26 2013
+++ /sys/src/9/rb/fpimips.c	Tue Sep 29 00:00:00 2015
@@ -37,9 +37,9 @@
 #define dbgstuck _dbgstuck
 #else
 #define DBG(bits) (0)
-#define internsane(i, ur)	do { USED(ur); } while(0)
-#define intpr(i, reg, fmt, ufp)	do {} while(0)
-#define dbgstuck(pc, ur, ufp)	do {} while(0)
+#define internsane(i, ur)	do { USED(i, ur); } while(0)
+#define intpr(i, reg, fmt, ufp)	do { USED(i, reg, fmt, ufp); } while(0)
+#define dbgstuck(pc, ur, ufp)	do { USED(pc, ur, ufp); } while(0)
 #endif
 
 #define	OFR(memb) (uintptr)&((Ureg*)0)->memb	/* offset into Ureg of memb */
@@ -306,19 +306,15 @@
 
 /* debugging: print internal representation of an fp reg */
 static void
-_intpr(Internal *i, int reg, int fmt, FPsave *ufp)
+_intpr(Internal *i, uint reg, int fmt, FPsave *)
 {
-	USED(i);
+	USED(i, reg, fmt);
 	if (!(DBG(Dbgregs)))
 		return;
-	if (fmt == Fdouble && reg < 31)
-		iprint("\tD%02d: l %08lux h %08lux =\ts %d e %04d h %08lux l %08lux\n",
-			reg, FREG(ufp, reg), FREG(ufp, reg+1),
-			i->s, i->e, i->h, i->l);
-	else
-		iprint("\tF%02d: %08lux =\ts %d e %04d h %08lux l %08lux\n",
-			reg, FREG(ufp, reg),
-			i->s, i->e, i->h, i->l);
+	if (reg < Nfpregs)
+		iprint("\t%c%02d: %c e %04d %08lux%08lux\n",
+			(fmt == Fdouble || fmt == Fvlong? 'D': 'F'), reg,
+			(i->s? '-': '+'), i->e, i->h, i->l);
 	delay(75);
 }
 
@@ -364,27 +360,25 @@
 	switch (fmt) {
 	case Ffloat:
 		fpis2i(&intrn, &FREG(ufp, rm));
-		internsane(&intrn, ur);
-		fpii2d(&d, &intrn);
 		break;
 	case Fdouble:
 		dreg2dbl(&d, rm, ufp);
 		break;
 	case Flong:
 		fpiw2i(&intrn, &FREG(ufp, rm));
-		internsane(&intrn, ur);
-		fpii2d(&d, &intrn);
 		break;
 	case Fvlong:
 		vreg2dbl(&d, rm, ufp);
 		fpiv2i(&intrn, &d);
+		break;
+	}
+	if (fmt != Fdouble) {
+		if (DBG(Dbgregs))
+			intpr(&intrn, rd, Fdouble, ufp);
 		internsane(&intrn, ur);
 		fpii2d(&d, &intrn);
-		break;
 	}
 	dbl2dreg(rd, &d, ufp);
-	if (fmt != Fdouble && DBG(Dbgregs))
-		intpr(&intrn, rm, Fdouble, ufp);
 }
 
 /* convert fmt (rm) to single (rd) */
@@ -412,7 +406,7 @@
 	}
 	if (fmt != Ffloat) {
 		if(DBG(Dbgregs))
-			intpr(&intrn, rm, Ffloat, ufp);
+			intpr(&intrn, rd, Ffloat, ufp);
 		internsane(&intrn, ur);
 		fpii2s(&FREG(ufp, rd), &intrn);
 	}
@@ -443,7 +437,7 @@
 	}
 	if (fmt != Flong) {
 		if(DBG(Dbgregs))
-			intpr(&intrn, rm, Flong, ufp);
+			intpr(&intrn, rd, Flong, ufp);
 		internsane(&intrn, ur);
 		fpii2w((long *)&FREG(ufp, rd), &intrn);
 	}
@@ -474,7 +468,7 @@
 	}
 	if (fmt != Fvlong) {
 		if(DBG(Dbgregs))
-			intpr(&intrn, rm, Fvlong, ufp);
+			intpr(&intrn, rd, Fvlong, ufp);
 		internsane(&intrn, ur);
 		fpii2v((vlong *)&FREG(ufp, rd), &intrn);
 	}
@@ -525,7 +519,7 @@
 fld(int d, ulong ea, int n, FPsave *ufp)
 {
 	if(DBG(Dbgmoves))
-		iprint("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d);
+		iprint("MOV%c\t%#lux, F%d\n", n==8? 'D': 'F', ea, d);
 	if (n == 4)
 		memmove(&FREG(ufp, d), (void *)ea, 4);
 	else if (n == 8){
@@ -541,7 +535,7 @@
 fst(ulong ea, int s, int n, FPsave *ufp)
 {
 	if(DBG(Dbgmoves))
-		iprint("MOV%c	F%d,#%lux\n", n==8? 'D': 'F', s, ea);
+		iprint("MOV%c\tF%d,%#lux\n", n==8? 'D': 'F', s, ea);
 	if (n == 4)
 		memmove((void *)ea, &FREG(ufp, s), 4);
 	else if (n == 8){
@@ -837,7 +831,7 @@
 	qunlock(&watchlock);
 
 	ur->pc = ufp->fpdelaypc;	/* pc of fp branch */
-	ur->cause &= BD;		/* take no chances */
+	ur->cause &= ~BD;		/* take no chances */
 	ufp->fpstatus = ufp->fpdelaysts;
 	followbr(ur);			/* sets ur->pc to fp branch target */
 	if (DBG(Dbgdelay))
@@ -1213,7 +1207,7 @@
 }
 
 static FPsave *
-fpinit(Ureg *ur)
+fpinit(Ureg *)
 {
 	int i, n;
 	Double d;
@@ -1241,7 +1235,6 @@
 			else
 				i = n;
 			tmp = fpconst[i];
-			internsane(&tmp, ur);
 			fpii2d(&d, &tmp);
 			dbl2dreg(n, &d, ufp);
 		}
diff -Nru /n/sources/plan9/sys/src/9/rb/init9.s /sys/src/9/rb/init9.s
--- /n/sources/plan9/sys/src/9/rb/init9.s	Fri May 17 19:32:33 2013
+++ /sys/src/9/rb/init9.s	Wed Mar 11 00:00:00 2020
@@ -5,4 +5,4 @@
 	MOVW	R1, 4(R29)
 	MOVW	R2, 8(R29)
 	JAL	startboot(SB)
-
+	  NOP
diff -Nru /n/sources/plan9/sys/src/9/rb/initreboot.s /sys/src/9/rb/initreboot.s
--- /n/sources/plan9/sys/src/9/rb/initreboot.s	Wed Jun 19 18:25:33 2013
+++ /sys/src/9/rb/initreboot.s	Wed Mar 11 00:00:00 2020
@@ -9,11 +9,12 @@
 TEXT	_main(SB), $0
 	MOVW	$setR30(SB), R30
 	JMP	main(SB)
+	  NOP
 
 /* target for JALRHB in BARRIERS */
 TEXT ret(SB), $-4
 	JMP	(R22)
-	NOP
+	  NOP
 
 TEXT	setsp(SB), $-4
 	MOVW	R1, SP
@@ -26,6 +27,18 @@
 	RETURN
 
 /*
+ * next_kernel(entry, r4, r5, r6, r7)
+ *	set R4-R7 for next kernel, then jump to it.
+ */
+TEXT next_kernel(SB), $-4
+	MOVW	r4+4(FP), R4
+	MOVW	r5+8(FP), R5
+	MOVW	r6+12(FP), R6
+	MOVW	r7+16(FP), R7
+	JMP	(R1)
+	  NOP
+
+/*
  *  cache manipulation
  */
 
@@ -42,7 +55,7 @@
 	SUBU	$CACHELINESZ, R9
 	ADDU	$CACHELINESZ, R1
 	BGTZ	R9, iccache
-	NOP
+	  NOP
 
 	MOVW	R0, R1			/* index, not address */
 	MOVW	$DCACHESIZE, R9
@@ -51,10 +64,10 @@
 	SUBU	$CACHELINESZ, R9
 	ADDU	$CACHELINESZ, R1
 	BGTZ	R9, dccache
-	NOP
+	  NOP
 
 	SYNC
-	MOVW	R10, M(STATUS)
+	MOVW	R10, M(STATUS)		/* restore processor level */
 	JRHB(31)			/* return and clear all hazards */
 
 	SCHED
diff -Nru /n/sources/plan9/sys/src/9/rb/io.h /sys/src/9/rb/io.h
--- /n/sources/plan9/sys/src/9/rb/io.h	Wed Jul  3 21:11:08 2013
+++ /sys/src/9/rb/io.h	Tue Mar 20 00:00:00 2018
@@ -300,8 +300,6 @@
 
 #define PCIWINDOW	0
 #define PCIWADDR(va)	(PADDR(va)+PCIWINDOW)
-#define ISAWINDOW	0
-#define ISAWADDR(va)	(PADDR(va)+ISAWINDOW)
 
 /* SMBus transactions */
 enum
diff -Nru /n/sources/plan9/sys/src/9/rb/l.s /sys/src/9/rb/l.s
--- /n/sources/plan9/sys/src/9/rb/l.s	Thu Sep 19 00:08:25 2013
+++ /sys/src/9/rb/l.s	Sat Jul  4 00:00:00 2020
@@ -12,6 +12,12 @@
  * Boot only processor
  */
 TEXT	start(SB), $-4
+	/* save parameters passed from routerboot */
+	MOVW	R4, R19			/* argc */
+	MOVW	R5, R20			/* argv */
+	MOVW	R6, R21			/* envp */
+	MOVW	R7, R23			/* memory size */
+
 	MOVW	$setR30(SB), R30
 
 PUTC('9', R1, R2)
@@ -21,7 +27,7 @@
 	CONST(SANITY, R2)
 	SUBU	R1, R2, R2
 	BNE	R2, insane
-	NOP
+	  NOP
 
 	MOVW	R0, M(COMPARE)
 	EHB
@@ -42,7 +48,7 @@
 	MOVW	R1, M(CACHEECC)		/* aka ErrCtl */
 	EHB
 	JAL	cleancache(SB)
-	NOP
+	  NOP
 
 	MOVW	$TLBROFF, R1
 	MOVW	R1, M(WIRED)
@@ -68,18 +74,34 @@
 	MOVW	R0, (R1)
 	ADDU	$BY2WD, R1
 	BNE	R1, SP, clrmach
-	NOP
+	  NOP
 	MOVW	R0, 0(R(MACH))			/* m->machno = 0 */
 	MOVW	R0, R(USER)			/* up = nil */
 
-	/* zero bss, byte-by-byte */
+	/* zero bss */
 	MOVW	$edata(SB), R1
 	MOVW	$end(SB), R2
 clrbss:
-	MOVB	R0, (R1)
-	ADDU	$1, R1
+	MOVW	R0, (R1)
+	ADDU	$BY2WD, R1
 	BNE	R1, R2, clrbss
-	NOP
+	  NOP
+
+	MOVW	R0, HI
+	MOVW	R0, LO
+
+PUTC('\r', R1, R2)
+PUTC('\n', R1, R2)
+
+	/*
+	 * restore parameters passed from routerboot and
+	 * pass them as arguments to main().
+	 */
+	SUB	$(5*4), SP	/* reserve space for args to main() + link */
+	MOVW	R19, R1			/* argc */
+	MOVW	R20, 8(SP)		/* argv */
+	MOVW	R21, 12(SP)		/* envp */
+	MOVW	R23, 16(SP)		/* memory size */
 
 	MOVW	$0x16, R16
 	MOVW	$0x17, R17
@@ -90,22 +112,20 @@
 	MOVW	$0x22, R22
 	MOVW	$0x23, R23
 
-	MOVW	R0, HI
-	MOVW	R0, LO
-
-PUTC('\r', R1, R2)
-PUTC('\n', R1, R2)
 	JAL	main(SB)
-	NOP
+	  NOP
+
+	/* shouldn't get here */
 	CONST(ROM, R1)
 	JMP	(R1)			/* back to the rom */
+	  NOP
 
 #define PUT(c) PUTC(c, R1, R2)
 #define DELAY(lab) \
 	CONST(34000000, R3); \
 lab:	SUBU	$1, R3; \
 	BNE	R3, lab; \
-	NOP
+	  NOP
 
 insane:
 	/*
@@ -119,12 +139,12 @@
 	PUT('\r'); PUT('\n'); DELAY(dl5)
 	CONST(ROM, R1)
 	JMP	(R1)			/* back to the rom */
-	NOP
+	  NOP
 
 /* target for JALRHB in BARRIERS */
 TEXT ret(SB), $-4
 	JMP	(R22)
-	NOP
+	  NOP
 
 /* print R1 in hex; clobbers R3—8 */
 TEXT printhex(SB), $-4
@@ -137,16 +157,16 @@
 	AND	$0xf, R6
 	SGTU	R6, R7, R8
 	BEQ	R8, prdec		/* branch if R6 <= 9 */
-	NOP
+	  NOP
 	ADD	$('a'-10), R6
 	JMP	prchar
-	NOP
+	  NOP
 prdec:
 	ADD	$'0', R6
 prchar:
 	PUTC(R6, R3, R4)
 	BNE	R5, prtop
-	NOP
+	  NOP
 	RETURN
 
 /*
@@ -154,6 +174,7 @@
  * 	- argument is stack pointer to user
  */
 TEXT	touser(SB), $-4
+	DI(0)			/* ensure intrs see consistent M(STATUS) */
 	MOVW	R1, SP
 	MOVW	$(UTZERO+32), R2	/* header appears in text */
 	MOVW	R2, M(EPC)
@@ -193,7 +214,7 @@
 
 TEXT	wait(SB), $-4
 	WAIT
-	NOP
+	  NOP
 
 	MOVW	R1, M(STATUS)		/* interrupts restored */
 	EHB
@@ -238,6 +259,14 @@
 	RETURN
 
 /*
+ * bit twiddling
+ */
+
+TEXT	clz(SB), $-4		/* dest = clz(src): count leading zeroes */
+	CLZ(1, 1)
+	RETURN
+
+/*
  * process switching
  */
 
@@ -276,9 +305,9 @@
 	EHB
 	MOVW	M(INDEX), R1
 	BGEZ	R1, index		/* if tlb entry found, use it */
-	NOP
+	  NOP
 	BEQ	R4, dont		/* not valid? cf. kunmap */
-	NOP
+	  NOP
 	MOVW	M(RANDOM), R1		/* write random tlb entry */
 	MOVW	R1, M(INDEX)
 index:
@@ -365,7 +394,7 @@
 	EHB
 	MOVW	M(INDEX), R1
 	BLTZ	R1, gettlbp1		/* if no tlb entry found, return */
-	NOP
+	  NOP
 	EHB
 	TLBR				/* read indexed tlb entry */
 	EHB
@@ -399,14 +428,16 @@
 
 /*
  * exceptions.
+ *
  * mips promises that there will be no current hazards upon entry
  * to exception handlers.
  */
 
+	/* will be copied into low memory vectors; must fit in 32 words */
 TEXT	vector0(SB), $-4
 	MOVW	$utlbmiss(SB), R26
 	JMP	(R26)
-	NOP
+	  NOP
 
 /*
  * compute stlb hash index.
@@ -416,9 +447,8 @@
  * stir in swizzled asid; we get best results with asid in both high & low bits.
  *
  * page = tlbvirt >> (PGSHIFT+1);	// ignoring even/odd bit
- * R27 = ((tlbvirt<<(STLBLOG-8) ^ (uchar)tlbvirt ^ page ^
- *	((page & (MASK(HIPFNBITS) << STLBLOG)) >> HIPFNBITS)) &
- *	(STLBSIZE-1)) * 12;
+ * arg = (tlbvirt<<(STLBLOG-8) ^ (uchar)tlbvirt ^ page ^
+ *	((page & (MASK(HIPFNBITS) << STLBLOG)) >> HIPFNBITS)) & (STLBSIZE-1);
  */
 #define STLBHASH(arg, tmp, tmp2) \
 	MOVW	arg, tmp2; \
@@ -430,10 +460,24 @@
 	MOVW	tmp2, tmp;		/* asid in low byte */ \
 	SLL	$(STLBLOG-8), tmp;	/* move asid to high bits */ \
 	XOR	tmp, arg;		/* include asid in high bits too */ \
-	AND	$0xff, tmp2, tmp;	/* asid in low byte */ \
+	MOVBU	tmp2, tmp;		/* asid in low byte of tlbvirt */ \
 	XOR	tmp, arg;		/* include asid in low bits */ \
 	CONST	(STLBSIZE-1, tmp); \
 	AND	tmp, arg		/* chop to fit */
+/*
+ * vc -S generated essentially this, which uses 4 registers and
+ * R28 for big constants:
+	MOVW	R1, R4
+	SRL	$(PGSHIFT+1), R1, R3	// page = tlbvirt>>(PGSHIFT+1)
+	SLL	$(STLBLOG-8), R1, R1	// tlbvirt<<(STLBLOG-8)
+	MOVBU	R4, R5			// (uchar)tlbvirt
+	XOR	R5, R1
+	XOR	R3, R1
+	AND	$(MASK(HIPFNBITS) << STLBLOG), R3
+	SRL	$HIPFNBITS, R3
+	XOR	R3, R1
+	AND	$(STLBSIZE-1), R1	// chop to fit
+ */
 
 TEXT	utlbmiss(SB), $-4
 	/*
@@ -441,27 +485,25 @@
 	 * it's unsaved so far.  avoid R24 (up in kernel) and R25 (m in kernel).
 	 */
 	/* update statistics */
-	CONST	(MACHADDR, R26)		/* R26 = m-> */
-	MOVW	16(R26), R27
+	CONST	(MACHADDR+16, R26)	/* R26 = &m->tlbfault */
+	MOVW	(R26), R27
 	ADDU	$1, R27
-	MOVW	R27, 16(R26)		/* m->tlbfault++ */
+	MOVW	R27, (R26)		/* m->tlbfault++ */
 
 	MOVW	R23, M(DESAVE)		/* save R23 */
 
-#ifdef	KUTLBSTATS
+#ifdef KUTLBSTATS
+	ADDU	$4, R26			/* &m->ktlbfault */
+
 	MOVW	M(STATUS), R23
 	AND	$KUSER, R23
 	BEQ	R23, kmiss
-
-	MOVW	24(R26), R27
-	ADDU	$1, R27
-	MOVW	R27, 24(R26)		/* m->utlbfault++ */
-	JMP	either
+	  NOP
+	ADDU	$4, R26			/* m->utlbfault */
 kmiss:
-	MOVW	20(R26), R27
+	MOVW	(R26), R27
 	ADDU	$1, R27
-	MOVW	R27, 20(R26)		/* m->ktlbfault++ */
-either:
+	MOVW	R27, (R26)		/* m->[ku]tlbfault++ */
 #endif
 
 	/* compute stlb index */
@@ -470,7 +512,7 @@
 	STLBHASH(R27, R26, R23)
 	MOVW	M(DESAVE), R23		/* restore R23 */
 
-	/* scale to a byte index (multiply by 12) */
+	/* scale to a byte index (multiply by 12 [3 ulongs]) */
 	SLL	$1, R27, R26		/* × 2 */
 	ADDU	R26, R27		/* × 3 */
 	SLL	$2, R27			/* × 12 */
@@ -482,22 +524,22 @@
 	MOVW	M(BADVADDR), R26
 	AND	$BY2PG, R26
 	BNE	R26, utlbodd		/* odd page? */
-	NOP
+	  NOP
 
 utlbeven:
 	MOVW	4(R27), R26		/* R26 = m->stb[hash].phys0 */
 	BEQ	R26, stlbm		/* nothing cached? do it the hard way */
-	NOP
+	  NOP
 	MOVW	R26, M(TLBPHYS0)
 	EHB
 	MOVW	8(R27), R26		/* R26 = m->stb[hash].phys1 */
 	JMP	utlbcom
-	MOVW	R26, M(TLBPHYS1)	/* branch delay slot */
+	  MOVW	R26, M(TLBPHYS1)	/* branch delay slot */
 
 utlbodd:
 	MOVW	8(R27), R26		/* R26 = m->stb[hash].phys1 */
 	BEQ	R26, stlbm		/* nothing cached? do it the hard way */
-	NOP
+	  NOP
 	MOVW	R26, M(TLBPHYS1)
 	EHB
 	MOVW	4(R27), R26		/* R26 = m->stb[hash].phys0 */
@@ -508,10 +550,10 @@
 	MOVW	M(TLBVIRT), R26
 	MOVW	(R27), R27		/* R27 = m->stb[hash].virt */
 	BEQ	R27, stlbm		/* nothing cached? do it the hard way */
-	NOP
+	  NOP
 	/* is the stlb entry for the right virtual address? */
 	BNE	R26, R27, stlbm		/* M(TLBVIRT) != m->stb[hash].virt? */
-	NOP
+	  NOP
 
 	/* if an entry exists, overwrite it, else write a random one */
 	CONST	(PGSZ, R27)
@@ -521,38 +563,36 @@
 	EHB
 	MOVW	M(INDEX), R26
 	BGEZ	R26, utlindex		/* if tlb entry found, rewrite it */
-	EHB				/* delay slot */
+	  EHB				/* delay slot */
 	TLBWR				/* else write random tlb entry */
 	ERET
 utlindex:
 	TLBWI				/* write indexed tlb entry */
 	ERET
 
-/* not in the stlb either; make trap.c figure it out */
-stlbm:
+/*
+ * will be copied into low memory vectors; must fit in 32 words
+ * and avoid relative branches.
+ */
+TEXT	vector100(SB), $-4		/* cache trap */
+TEXT	vector180(SB), $-4		/* most exceptions */
+stlbm:			/* not in the stlb either; make trap.c figure it out */
 	MOVW	$exception(SB), R26
 	JMP	(R26)
-	NOP
+	  NOP
 
 TEXT	stlbhash(SB), $-4
 	STLBHASH(R1, R2, R3)
 	RETURN
 
-TEXT	vector100(SB), $-4
-	MOVW	$exception(SB), R26
-	JMP	(R26)
-	NOP
-
-TEXT	vector180(SB), $-4
-	MOVW	$exception(SB), R26
-	JMP	(R26)
-	NOP
-
+/*
+ * exceptions other than tlb miss come here directly.
+ */
 TEXT	exception(SB), $-4
 	MOVW	M(STATUS), R26
 	AND	$KUSER, R26, R27
 	BEQ	R27, waskernel
-	MOVW	SP, R27			/* delay slot */
+	  MOVW	SP, R27			/* delay slot */
 
 wasuser:
 	CONST	(MACHADDR, SP)		/*  m-> */
@@ -563,7 +603,7 @@
 	MOVW	R31, Ureg_r31(SP)
 
 	JAL	savereg1(SB)
-	NOP
+	  NOP
 
 	MOVW	R30, Ureg_r30(SP)
 	MOVW	R(MACH), Ureg_r25(SP)
@@ -577,17 +617,17 @@
 	AND	$(EXCMASK<<2), R26, R1
 	SUBU	$(CSYS<<2), R1
 	BNE	R1, notsys
-	NOP
+	  NOP
 
 	/* the carrera does this: */
 //	ADDU	$8, SP, R1			/* first arg for syscall */
 
 	MOVW	SP, R1				/* first arg for syscall */
-	JAL	syscall(SB)
-	SUBU	$Notuoffset, SP			/* delay slot */
+	JAL	syscall(SB)			/* to C */
+	  SUBU	$Notuoffset, SP			/* delay slot */
 sysrestore:
 	JAL	restreg1(SB)
-	ADDU	$Notuoffset, SP			/* delay slot */
+	  ADDU	$Notuoffset, SP			/* delay slot */
 
 	MOVW	Ureg_r31(SP), R31
 	MOVW	Ureg_status(SP), R26
@@ -595,36 +635,36 @@
 	MOVW	R26, M(STATUS)
 	EHB
 	MOVW	Ureg_pc(SP), R26		/* old pc */
+erettor26:
 	MOVW	Ureg_sp(SP), SP
 	MOVW	R26, M(EPC)
 	ERET
 
 notsys:
 	JAL	savereg2(SB)
-	NOP
+	  NOP
 
 	/* the carrera does this: */
 //	ADDU	$8, SP, R1			/* first arg for trap */
 
 	MOVW	SP, R1				/* first arg for trap */
-	JAL	trap(SB)
-	SUBU	$Notuoffset, SP			/* delay slot */
+	JAL	trap(SB)			/* to C for user trap */
+	  SUBU	$Notuoffset, SP			/* delay slot */
 
 	ADDU	$Notuoffset, SP
 
 restore:
 	JAL	restreg1(SB)
-	NOP
+	  NOP
 	JAL	restreg2(SB)		/* restores R28, among others */
-	NOP
+	  NOP
 
 	MOVW	Ureg_r30(SP), R30
 	MOVW	Ureg_r31(SP), R31
 	MOVW	Ureg_r25(SP), R(MACH)
 	MOVW	Ureg_r24(SP), R(USER)
-	MOVW	Ureg_sp(SP), SP
-	MOVW	R26, M(EPC)
-	ERET
+	JMP	erettor26
+	  NOP
 
 waskernel:
 	SUBU	$UREGSIZE, SP
@@ -633,21 +673,21 @@
 	MOVW	R31, Ureg_r31(SP)
 
 	JAL	savereg1(SB)
-	NOP
+	  NOP
 	JAL	savereg2(SB)
-	NOP
+	  NOP
 
 	/* the carrera does this: */
 //	ADDU	$8, SP, R1			/* first arg for trap */
 
-	MOVW	SP, R1			/* first arg for trap */
-	JAL	trap(SB)
-	SUBU	$Notuoffset, SP			/* delay slot */
+	MOVW	SP, R1				/* first arg for trap */
+	JAL	trap(SB)			/* to C for kernel trap */
+	  SUBU	$Notuoffset, SP			/* delay slot */
 
 	ADDU	$Notuoffset, SP
 
 	JAL	restreg1(SB)
-	NOP
+	  NOP
 
 	/*
 	 * if about to return to `wait', interrupt arrived just before
@@ -658,21 +698,20 @@
 	MOVW	$wait(SB), R1
 	SUBU	R1, R31
 	BNE	R31, notwait
-	NOP
+	  NOP
 	ADD	$BY2WD, R26		/* advance saved pc */
 	MOVW	R26, Ureg_pc(SP)
 notwait:
 	JAL	restreg2(SB)		/* restores R28, among others */
-	NOP
+	  NOP
 
 	MOVW	Ureg_r31(SP), R31
-	MOVW	Ureg_sp(SP), SP
-	MOVW	R26, M(EPC)
-	ERET
+	JMP	erettor26
+	  NOP
 
 TEXT	forkret(SB), $0
 	JMP	sysrestore
-	MOVW	R0, R1			/* delay slot; child returns 0 */
+	  MOVW	R0, R1			/* delay slot; child returns 0 */
 
 /*
  * save mandatory registers.
@@ -795,14 +834,6 @@
 	MOVW	Ureg_pc(SP), R26
 	RETURN
 
-#ifdef OLD_MIPS_EXAMPLE
-/* this appears to be a dreg from the distant past */
-TEXT	rfnote(SB), $0
-	MOVW	R1, R26			/* 1st arg is &uregpointer */
-	JMP	restore
-	SUBU	$(BY2WD), R26, SP	/* delay slot: pc hole */
-#endif
-
 /*
  * degenerate floating-point stuff
  */
@@ -833,36 +864,7 @@
 	SC(2, 3)
 	NOP
 	BEQ	R3, tas1
-	NOP
-	RETURN
-
-TEXT	ainc(SB), $0
-	MOVW	R1, R2		/* address of counter */
-loop:
-	MOVW	$1, R3
-	LL(2, 1)
-	NOP
-	ADDU	R1, R3
-	MOVW	R3, R1		/* return new value */
-	SC(2, 3)
-	NOP
-	BEQ	R3, loop
-	NOP
-	RETURN
-
-TEXT	adec(SB), $0
-	SYNC
-	MOVW	R1, R2		/* address of counter */
-loop1:
-	MOVW	$-1, R3
-	LL(2, 1)
-	NOP
-	ADDU	R1, R3
-	MOVW	R3, R1		/* return new value */
-	SC(2, 3)
-	NOP
-	BEQ	R3, loop1
-	NOP
+	  NOP
 	RETURN
 
 /* used by the semaphore implementation */
@@ -873,7 +875,7 @@
 	LL(2, 1)		/* R1 = (R2) */
 	NOP
 	BNE	R1, R3, fail
-	NOP
+	  NOP
 	MOVW	R4, R1
 	SC(2, 1)	/* (R2) = R1 if (R2) hasn't changed; R1 = success */
 	NOP
@@ -905,7 +907,7 @@
 	CACHE	PI+HINV, (R8)		/* invalidate in I */
 	SUBU	$CACHELINESZ, R9
 	BGTZ	R9, icflush1
-	ADDU	$CACHELINESZ, R8	/* delay slot */
+	  ADDU	$CACHELINESZ, R8	/* delay slot */
 
 	BARRIERS(7, R7, ic2hb);		/* return to kseg0 (cached) */
 	MOVW	R10, M(STATUS)
@@ -927,7 +929,7 @@
 	CACHE	PD+HWBI, (R8)		/* flush & invalidate in D */
 	SUBU	$CACHELINESZ, R9
 	BGTZ	R9, dcflush1
-	ADDU	$CACHELINESZ, R8	/* delay slot */
+	  ADDU	$CACHELINESZ, R8	/* delay slot */
 	SYNC
 	EHB
 	MOVW	R10, M(STATUS)
@@ -944,7 +946,7 @@
 	CACHE	PI+IWBI, (R1)		/* flush & invalidate I by index */
 	SUBU	$CACHELINESZ, R9
 	BGTZ	R9, iccache
-	ADDU	$CACHELINESZ, R1	/* delay slot */
+	  ADDU	$CACHELINESZ, R1	/* delay slot */
 
 	BARRIERS(7, R7, cc2hb);		/* return to kseg0 (cached) */
 
@@ -954,7 +956,7 @@
 	CACHE	PD+IWBI, (R1)		/* flush & invalidate D by index */
 	SUBU	$CACHELINESZ, R9
 	BGTZ	R9, dccache
-	ADDU	$CACHELINESZ, R1	/* delay slot */
+	  ADDU	$CACHELINESZ, R1	/* delay slot */
 
 	SYNC
 	MOVW	R10, M(STATUS)
diff -Nru /n/sources/plan9/sys/src/9/rb/main.c /sys/src/9/rb/main.c
--- /n/sources/plan9/sys/src/9/rb/main.c	Thu Jul 25 22:46:17 2013
+++ /sys/src/9/rb/main.c	Wed Mar 25 00:00:00 2020
@@ -36,8 +36,10 @@
 
 Conf	conf;
 FPsave	initfp;
+Rbconf	rbconf;
 
 int normalprint;
+ulong memsize;
 
 char *
 getconf(char *)
@@ -156,17 +158,59 @@
 		return -1;
 }
 
+/*
+ * parse args kmac=AA:BB:CC:DD:EE:FF for ether0 mac, mem=256M,
+ * HZ=340000000, console=ttyS0,115200.
+ * args seem to live above 8MB, where they are likely to be clobbered
+ * by kernel allocations, so make copies for rebooting.
+ *
+ * at this early stage, we can't use the usual kernel memory allocators,
+ * thus we can't use kstrdup.
+ */
+static void
+saverbconfig(int argc, char **argv)
+{
+	int i;
+	char *p;
+	static char ether0mac[32], mem[16], hz[16], console[32];
+
+	if (argv == nil)
+		return;
+	rbconf.ether0mac = ether0mac;
+	rbconf.memsize = mem;
+	rbconf.hz = hz;
+	rbconf.console = console;
+	for (i = 0; i < argc; i++) {
+		p = argv[i];
+		if (strncmp(p, "kmac=", 5) == 0)
+			strncpy(rbconf.ether0mac, p, sizeof ether0mac);
+		else if (strncmp(p, "mem=", 4) == 0)
+			strncpy(rbconf.memsize, p, sizeof mem);
+		else if (strncmp(p, "HZ=", 3) == 0)
+			strncpy(rbconf.hz, p, sizeof hz);
+		else if (strncmp(p, "console=", 8) == 0)
+			strncpy(rbconf.console, p, sizeof console);
+	}
+	memsize = 256*MB;			/* routerboard default */
+	p = strchr(rbconf.memsize, '=');
+	if (p)
+		memsize = atoi(p+1) * MB;
+}
+
+/* arguments come from routerboot; only argc and argv are non-zero. */
 void
-main(void)
+main(int argc, char **argv, char **envp, ulong memsz)
 {
 	stopwdog();			/* tranquilise the dog */
 	optionsinit("/boot/boot boot");
+	saverbconfig(argc, argv);
+	USED(envp, memsz);
+
 	confinit();
 	savefpregs(&initfp);
-
 	machinit();			/* calls clockinit */
 	active.exiting = 0;
-	active.machs = 1;
+	cpuactive(0);
 
 	kmapinit();
 	xinit();
@@ -176,7 +220,7 @@
 	vecinit();
 
 	normalprint = 1;
-	print("\nPlan 9\n");
+	print("\nPlan 9 from Bell Labs (mips)\n");
 	prcpuid();
 	if (PTECACHABILITY == PTENONCOHERWT)
 		print("caches configured as write-through\n");
@@ -253,7 +297,7 @@
 
 	if(!waserror()){
 		ksetenv("cputype", "mips", 0);
-		snprint(buf, sizeof buf, "mips %s rb450g", conffile);
+		snprint(buf, sizeof buf, "mips %s", conffile);
 		ksetenv("terminal", buf, 0);
 		if(cpuserver)
 			ksetenv("service", "cpu", 0);
@@ -288,7 +332,7 @@
 	 * of the argument list checked in syscall.
 	 */
 	i = oargblen+1;
-	p = UINT2PTR(STACKALIGN(base + BY2PG - Stkheadroom - i));
+	p = (char *)STACKALIGN(base + BY2PG - Stkheadroom - i);
 	memmove(p, oargb, i);
 
 	/*
@@ -300,7 +344,7 @@
 	 * not the usual (int argc, char* argv[])
 	 */
 	av = (char**)(p - (oargc+1)*sizeof(char*));
-	ssize = base + BY2PG - PTR2UINT(av);
+	ssize = base + BY2PG - (uintptr)av;
 	for(i = 0; i < oargc; i++)
 		*av++ = (oargv[i] - oargb) + (p - base) + (USTKTOP - BY2PG);
 	*av = nil;
@@ -434,16 +478,16 @@
 	ilock(&active);
 	if(ispanic)
 		active.ispanic = ispanic;
-	else if(m->machno == 0 && (active.machs & (1<<m->machno)) == 0)
+	else if(m->machno == 0 && !iscpuactive(m->machno))
 		active.ispanic = 0;
-	once = active.machs & (1<<m->machno);
+	once = iscpuactive(m->machno);
 	/*
 	 * setting exiting will make hzclock() on each processor call exit(0),
 	 * which calls shutdown(0) and idles non-bootstrap cpus and returns
 	 * on bootstrap processors (to permit a reboot).  clearing our bit
 	 * in machs avoids calling exit(0) from hzclock() on this processor.
 	 */
-	active.machs &= ~(1<<m->machno);
+	cpuinactive(m->machno);
 	active.exiting = 1;
 	iunlock(&active);
 
@@ -455,7 +499,7 @@
 	ms = MAXMACH * 1000;
 	for(; ms > 0; ms -= TK2MS(2)){
 		delay(TK2MS(2));
-		if(active.machs == 0 && consactive() == 0)
+		if(active.nmachs == 0 && consactive() == 0)
 			break;
 	}
 	delay(100);
@@ -463,14 +507,25 @@
 
 /*
  * the new kernel is already loaded at address `code'
- * of size `size' and entry point `entry'.
+ * of size `size' and physical entry point `entry'.
  */
 void
 reboot(void *entry, void *code, ulong size)
 {
-	void (*f)(ulong, ulong, ulong);
+	Rbconf *rbc;
+	void (*f)(void *, ulong, ulong, ulong);
 
 	writeconf();
+	/*
+	 * copy rbconf and contents into allocated memory, thus safe from
+	 * being overwritten by the new kernel in the reboot trampoline
+	 * code below.
+	 */
+	rbc = smalloc(sizeof *rbc);
+	kstrdup(&rbc->ether0mac, rbconf.ether0mac);
+	kstrdup(&rbc->memsize, rbconf.memsize);
+	kstrdup(&rbc->hz, rbconf.hz);
+	kstrdup(&rbc->console, rbconf.console);
 
 	/*
 	 * the boot processor is cpu0.  execute this function on it
@@ -513,13 +568,16 @@
 	/* setup reboot trampoline function */
 	f = (void*)REBOOTADDR;
 	memmove(f, rebootcode, sizeof(rebootcode));
+	dcflush(f, sizeof(rebootcode));
 	icflush(f, sizeof(rebootcode));
 
 	setstatus(BEV);		/* also, kernel mode, no interrupts */
 	coherence();
 
 	/* off we go - never to return */
-	(*f)((ulong)entry, (ulong)code, size);
+	if (((ulong)entry & KSEGM) == 0)	/* physical address? */
+		entry = KADDR(entry);		/* make it kernel virtual */
+	(*f)(rbc, (ulong)entry, (ulong)code, size);
 
 	panic("loaded kernel returned!");
 }
@@ -534,14 +592,14 @@
 
 	delay(1000);
 	lock(&active);
-	active.machs &= ~(1<<m->machno);
+	cpuinactive(m->machno);
 	active.exiting = 1;
 	unlock(&active);
 	spllo();
 
 	print("cpu %d exiting\n", m->machno);
 	timer = 0;
-	while(active.machs || consactive()) {
+	while(active.nmachs || consactive()) {
 		if(timer++ > 400)
 			break;
 		delay(10);
@@ -581,14 +639,15 @@
 void
 confinit(void)
 {
+	char *p;
 	ulong kpages, ktop;
 
 	/*
 	 *  divide memory twixt user pages and kernel.
 	 */
 	conf.mem[0].base = ktop = PADDR(PGROUND((ulong)end));
-	/* fixed memory on routerboard */
-	conf.mem[0].npage = MEMSIZE/BY2PG - ktop/BY2PG;
+	assert(memsize > 16*MB);
+	conf.mem[0].npage = memsize/BY2PG - ktop/BY2PG;
 	conf.npage = conf.mem[0].npage;
 	conf.nuart = 1;
 
@@ -601,11 +660,11 @@
 	conf.ialloc = (kpages/2)*BY2PG;
 
 	kpages *= BY2PG;
-	kpages -= conf.upages*sizeof(Page)
-		+ conf.nproc*sizeof(Proc)
-		+ conf.nimage*sizeof(Image)
-		+ conf.nswap
-		+ conf.nswppo*sizeof(Page*);
+	kpages -= conf.upages*sizeof(Page)	/* palloc.pages in pageinit */
+		+ conf.nproc*sizeof(Proc)  /* procalloc.free in procinit0 */
+		+ conf.nimage*sizeof(Image)	/* imagealloc.free in initseg */
+		+ conf.nswap		/* swapalloc.swmap in swapinit */
+		+ conf.nswppo*sizeof(Page*);	/* iolist in swapinit */
 	mainmem->maxsize = kpages;
 
 	/*
@@ -614,7 +673,12 @@
 	 */
 	m->machno = 0;
 	m->speed = 680;			/* initial guess at MHz, for rb450g */
-	m->hz = m->speed * Mhz;
+	m->hz = 680 * Mhz;
+	p = strchr(rbconf.hz, '=');
+	if (p) {
+		m->hz = 2 * strtol(p+1, 0, 10);
+		m->speed = m->hz / Mhz;
+	}
 	conf.nmach = 1;
 
 	/* set up other configuration parameters */
diff -Nru /n/sources/plan9/sys/src/9/rb/mem.h /sys/src/9/rb/mem.h
--- /n/sources/plan9/sys/src/9/rb/mem.h	Tue Jul 23 00:28:29 2013
+++ /sys/src/9/rb/mem.h	Tue May 26 00:00:00 2020
@@ -14,23 +14,15 @@
 #define MAXBY2PG (16*1024) /* rounding for UTZERO in executables; see mkfile */
 #define UTROUND(t)	ROUNDUP((t), MAXBY2PG)
 
-#ifndef BIGPAGES
 #define	BY2PG		4096			/* bytes per page */
 #define	PGSHIFT		12			/* log2(BY2PG) */
 #define	PGSZ		PGSZ4K
 #define MACHSIZE	(2*BY2PG)
-#else
-/* 16K pages work very poorly */
-#define	BY2PG		(16*1024)		/* bytes per page */
-#define	PGSHIFT		14			/* log2(BY2PG) */
-#define PGSZ		PGSZ16K
-#define MACHSIZE	BY2PG
-#endif
 
 #define	KSTACK		8192			/* Size of kernel stack */
 #define	WD2PG		(BY2PG/BY2WD)		/* words per page */
 
-#define	MAXMACH		1   /* max # cpus system can run; see active.machs */
+#define	MAXMACH		1 		  /* max # cpus system can run */
 #define STACKALIGN(sp)	((sp) & ~7)		/* bug: assure with alloc */
 #define	BLOCKALIGN	16
 #define CACHELINESZ	32			/* mips24k */
@@ -261,16 +251,10 @@
 #define PHYSCONS	(KSEG1|0x18020000)		/* i8250 uart */
 
 #define PIDXSHFT	12
-#ifndef BIGPAGES
+/* future ref.: no cache aliases are possible with pages of 16K or larger */
 #define NCOLOR		8
 #define PIDX		((NCOLOR-1)<<PIDXSHFT)
 #define getpgcolor(a)	(((ulong)(a)>>PIDXSHFT) % NCOLOR)
-#else
-/* no cache aliases are possible with pages of 16K or larger */
-#define NCOLOR		1
-#define PIDX		0
-#define getpgcolor(a)	0
-#endif
 #define KMAPSHIFT	15
 
 #define	PTEGLOBL	(1<<0)
@@ -294,10 +278,10 @@
 #define	PTEPID(n)	(n)
 #define PTEMAPMEM	(1024*1024)
 #define	PTEPERTAB	(PTEMAPMEM/BY2PG)
-#define SEGMAPSIZE	512
+#define SEGMAPSIZE	(ROUND(USTKTOP, PTEMAPMEM) / PTEMAPMEM)
 #define SSEGMAPSIZE	16
 
-#define STLBLOG		15
+#define STLBLOG		16	/* was 15 */
 #define STLBSIZE	(1<<STLBLOG)	/* entries in the soft TLB */
 /* page # bits that don't fit in STLBLOG bits */
 #define HIPFNBITS	(BI2WD - (PGSHIFT+1) - STLBLOG)
@@ -316,6 +300,7 @@
 /*
  * Address spaces
  */
+#define PHYSDRAM 0
 #define	UZERO	KUSEG			/* base of user address space */
 #define	UTZERO	(UZERO+MAXBY2PG)	/* 1st user text address; see mkfile */
 #define	USTKTOP	(KZERO-BY2PG)		/* byte just beyond user stack */
@@ -324,5 +309,4 @@
 #define TSTKSIZ (1024*1024/BY2PG)	/* can be at most UTSKSIZE/BY2PG */
 #define	KZERO	KSEG0			/* base of kernel address space */
 #define	KTZERO	(KZERO+0x20000)		/* first address in kernel text */
-#define MEMSIZE	(256*MB)		/* fixed memory on routerboard */
 #define PCIMEM	0x10000000		/* on rb450g */
diff -Nru /n/sources/plan9/sys/src/9/rb/mips.s /sys/src/9/rb/mips.s
--- /n/sources/plan9/sys/src/9/rb/mips.s	Tue Jul 23 00:34:43 2013
+++ /sys/src/9/rb/mips.s	Mon Dec 21 00:00:00 2020
@@ -7,6 +7,8 @@
 #define	SP	R29
 
 #define NOP	NOR R0, R0, R0
+/* a SPECIAL2 op-code from MIPS32 */
+#define CLZ(rs,rd) WORD $(0x70000020 | (rs)<<21 | (rd)<<16 | (rd)<<11)
 
 #define	CONST(x,r) MOVW $((x)&0xffff0000), r; OR  $((x)&0xffff), r
 
@@ -42,24 +44,6 @@
 /* same but return to KSEG1 */
 #define UBARRIERS(r, Reg, label) \
 	SYNC; EHB; MOVW $ret(SB), Reg; OR $KSEG1, Reg; JALRHB(r)
-
-/* alternative definitions using labels */
-#ifdef notdef
-/* all barriers, clears all hazards; clobbers r/Reg */
-#define BARRIERS(r, Reg, label) \
-	SYNC; EHB; \
-	MOVW	$label(SB), Reg; \
-	JRHB(r); \
-TEXT label(SB), $-4; \
-	NOP
-#define UBARRIERS(r, Reg, label) \
-	SYNC; EHB; \
-	MOVW	$label(SB), Reg; \
-	OR	$KSEG1, Reg; \
-	JRHB(r); \
-TEXT label(SB), $-4; \
-	NOP
-#endif
 
 #define PUTC(c, r1, r2)	CONST(PHYSCONS, r1); MOVW $(c), r2; MOVW r2, (r1); NOP
 
diff -Nru /n/sources/plan9/sys/src/9/rb/mkfile /sys/src/9/rb/mkfile
--- /n/sources/plan9/sys/src/9/rb/mkfile	Tue Jul 23 22:48:17 2013
+++ /sys/src/9/rb/mkfile	Thu Oct 17 00:00:00 2019
@@ -1,7 +1,7 @@
 CONF=rb
 CONFLIST=rb
 # no rb with nvram on bovril (outside)
-EXTRACOPIES=piestand lookout boundary 
+EXTRACOPIES=fsstand # pxe0
 
 objtype=mips
 </$objtype/mkfile
@@ -32,6 +33,7 @@
 	fault.$O\
 	latin1.$O\
 	mul64fract.$O\
+	logpow.$O\
 	page.$O\
 	parse.$O\
 	pgrp.$O\
diff -Nru /n/sources/plan9/sys/src/9/rb/mmu.c /sys/src/9/rb/mmu.c
--- /n/sources/plan9/sys/src/9/rb/mmu.c	Wed Jul 17 03:39:04 2013
+++ /sys/src/9/rb/mmu.c	Sun May 19 00:00:00 2019
@@ -534,7 +534,26 @@
 ulong
 cankaddr(ulong pa)
 {
-	if(pa >= KZERO || pa >= MEMSIZE)
+	if(pa >= KZERO || pa >= memsize)
 		return 0;
-	return MEMSIZE - pa;
+	return memsize - pa;
+}
+
+/*
+ * although needed by the pc port, this mapping can be trivial on our mips systems,
+ * which have less memory.
+ */
+void*
+vmap(uintptr pa, usize)
+{
+	return UINT2PTR(KSEG0|pa);
+}
+
+void
+vunmap(void* v, usize size)
+{
+	/*
+	upafree(PADDR(v), size);
+	 */
+	USED(v, size);
 }
diff -Nru /n/sources/plan9/sys/src/9/rb/notes/9rb.ms /sys/src/9/rb/notes/9rb.ms
--- /n/sources/plan9/sys/src/9/rb/notes/9rb.ms	Tue Jul 23 22:25:12 2013
+++ /sys/src/9/rb/notes/9rb.ms	Mon Jun  8 00:00:00 2020
@@ -1,11 +1,16 @@
+.nr PS 11
+.nr VS 13
 .FP palatino
+.
 .TM
+.DA 23 July 2013
 .TL
 Plan 9 on the Mikrotik RB450G Routerboard
 .AU
 Geoff Collyer
 .AI
 .MH
+.
 .NH 1
 Motivation
 .LP
@@ -57,7 +62,7 @@
 The fix could be better, in particular the technique for
 keeping stores out of branch delay slots.
 .NH 2
-Driver for Undocumented Ethernet Controller
+Driver for Undocumented Atheros 7161 Ethernet Controller
 .LP
 The FreeBSD Atheros
 .I arge
@@ -151,11 +156,6 @@
 (around 20k–30k per second).
 It probably doesn't help that one 16K page is half of the L1 data cache
 and one quarter of the L1 instruction cache.
-.LP
-Page size is controlled by
-.CW BIGPAGES
-in
-.CW mem.h .
 .NH 3
 Combined TLB Pool
 .LP
@@ -179,11 +179,3 @@
 .CW uarti8250.c
 that makes output work most of the time
 (characters do sometimes get dropped).
-.LP
-The Ethernet driver currently does not
-dig out the MAC addresses from the hardware,
-so you'll need to edit the
-.CW rb
-configuration file for each Routerboard; the format should be obvious.
-I don't have the stomach to dig the MAC address out of the hardware
-via SPI or whatever vile interface it requires.
Binary files /n/sources/plan9/sys/src/9/rb/nvram and /sys/src/9/rb/nvram differ
diff -Nru /n/sources/plan9/sys/src/9/rb/rb /sys/src/9/rb/rb
--- /n/sources/plan9/sys/src/9/rb/rb	Wed Jul  3 23:32:59 2013
+++ /sys/src/9/rb/rb	Thu Mar  5 00:00:00 2020
@@ -9,15 +9,15 @@
 	mnt
 	srv
 	dup
-#	rtc
+#	rtc		# no hardware tod clock
 	ssl
 	tls
 	cap
 	kprof
-	fs
+#	fs
 
-	ether		netif
+	ether		ethermii netif
	ip		arp chandial inferno ip ipv6 ipaux iproute netlog nullmedium pktmedium ptclbsum
 
 	uart
 link
@@ -30,7 +30,6 @@
 	fpi
 	fpimips
 	fpimem
-	ethermii
 
 ip
 	tcp
@@ -38,15 +37,13 @@
 	ipifc
 	icmp
 	icmp6
-	gre
+# only used by mobile ip code
+#	gre
 	ipmux
 	esp
 
 port
 	int cpuserver = 1;
-	uchar arge0mac[] = { 0xd4, 0xca, 0x6d, 0x7d, 0xf1, 0xce, };
-	uchar arge1mac[] = { 0xd4, 0xca, 0x6d, 0x7d, 0xf1, 0xcf, };
-};
 
 boot cpu
 	tcp
diff -Nru /n/sources/plan9/sys/src/9/rb/rebootcode.c /sys/src/9/rb/rebootcode.c
--- /n/sources/plan9/sys/src/9/rb/rebootcode.c	Thu Jul 18 17:57:31 2013
+++ /sys/src/9/rb/rebootcode.c	Wed Mar 11 00:00:00 2020
@@ -20,36 +20,40 @@
 };
 
 void	putc(int);
+void	next_kernel(void *, ulong, char **, char **, ulong);
 
 /*
- * Copy the new kernel to its correct location in physical memory,
+ * Copy the new kernel to its correct location in memory,
  * flush caches, ignore TLBs (we're in KSEG0 space), and jump to
- * the start of the kernel.
+ * the start of the kernel.  Argument addresses are virtual (KSEG0).
  */
 void
-main(ulong aentry, ulong acode, ulong asize)
+main(Rbconf *rbconf, ulong aentry, ulong acode, ulong asize)
 {
-	void (*kernel)(void);
-	static ulong entry, code, size;
+	static ulong entry, code, size, argc;
+	static char **argv;
 
-	putc('B'); putc('o'); putc('o'); putc('t');
+	putc('B'); putc('o');
 	/* copy args to heap before moving stack to before a.out header */
+	argv = (char **)rbconf;
 	entry = aentry;
 	code = acode;
 	size = asize;
 	setsp(entry-0x20-4);
+	putc('o');
 
 	memmove((void *)entry, (void *)code, size);
-
+	putc('t');
 	cleancache();
 	coherence();
+	putc(' ');
 
 	/*
-	 * jump to kernel entry point.
+	 * jump to new kernel's entry point.
+	 * pass routerboot arg vector in R4-R5.
+	 * off we go - never to return.
 	 */
-	putc(' ');
-	kernel = (void*)entry;
-	(*kernel)();			/* off we go - never to return */
+	next_kernel((void*)entry, 4, argv, 0, 0);
 
 	putc('?');
 	putc('!');
diff -Nru /n/sources/plan9/sys/src/9/rb/trap.c /sys/src/9/rb/trap.c
--- /n/sources/plan9/sys/src/9/rb/trap.c	Tue Jul 23 01:51:54 2013
+++ /sys/src/9/rb/trap.c	Mon Jun  8 00:00:00 2020
@@ -548,7 +548,7 @@
 	return buf;
 }
 
-#define KERNPC(x) (KTZERO <= (ulong)(x) && (ulong)(x) < (ulong)&etext)
+// #define KERNPC(x) (KTZERO <= (ulong)(x) && (ulong)(x) < (ulong)&etext)
 
 void
 kernfault(Ureg *ur, int code)
diff -Nru /n/sources/plan9/sys/src/9/rb/uarti8250.c /sys/src/9/rb/uarti8250.c
--- /n/sources/plan9/sys/src/9/rb/uarti8250.c	Tue Jul 23 00:25:37 2013
+++ /sys/src/9/rb/uarti8250.c	Tue May 26 00:00:00 2020
@@ -118,7 +118,7 @@
 
 
 typedef struct Ctlr {
-	u32int*	io;
+	ulong*	io;
 	int	irq;
 	int	tbdf;
 	int	iena;
@@ -135,7 +135,7 @@
 extern PhysUart i8250physuart;
 
 static Ctlr i8250ctlr[] = {
-{	.io	= (u32int*)PHYSCONS,
+{	.io	= (ulong*)PHYSCONS,
 	.irq	= ILduart0,
 	.tbdf	= -1,
 	.poll	= 0, },
@@ -396,7 +396,7 @@
 static int
 i8250baud(Uart* uart, int baud)
 {
-#ifdef notdef				/* don't change the speed */
+#ifdef CHANGE_SPEED
 	ulong bgc;
 	Ctlr *ctlr;
 	extern int i8250freq;	/* In the config file */
diff -Nru /n/sources/plan9/sys/src/9/rb/words /sys/src/9/rb/words
--- /n/sources/plan9/sys/src/9/rb/words	Tue Jul 23 22:14:11 2013
+++ /sys/src/9/rb/words	Thu May 14 00:00:00 2015
@@ -22,7 +22,7 @@
 ll/sc target memory must be cached.
 
 1 uart		8250 (actually 16550ish)
-2 ethers	arge[01] ar71xx, 2nd has 4 ports & a bridge
+2 ethers	arge[01] ar7161, 2nd has 4 ports & a bridge
 pci bus(es)
 no video
 no disk but has flash, alas
@@ -44,8 +44,8 @@
 irqs
 2	pci
 3	ehci
-4	arge1 ar71xx @ 0x19:: + ar8316 switch
-5	arge0 ar71xx @ 0x1a::
+4	arge1 ar7161 @ 0x19:: + ar8316 switch
+5	arge0 ar7161 @ 0x1a::
 6	uart
 7	clock