shithub: sitara

Download patch

ref: a7b1ab8236b7ccd41e96e9e4008cbc3213294724
author: Alex Musolino <musolinoa@gmail.com>
date: Sat Nov 28 23:38:41 EST 2020

initial commit

--- /dev/null	Thu Dec 31 12:39:09 2020
+++ b/bbb	Sat Nov 28 23:38:41 2020
@@ -0,0 +1,48 @@
+dev
+	cap
+	cons
+	dup
+	env
+	ether netif
+	gpio
+	ip arp chandial inferno ip ipaux iproute ipv6 netlog nullmedium pktmedium
+	mnt
+	pipe
+	proc
+	root
+	segment
+	shr
+	srv
+	ssl
+	tls
+	uart
+	swap
+
+link
+	etherbbb
+	ethermedium
+	loopbackmedium
+	netdevmedium
+
+ip
+	esp
+	gre
+	icmp
+	icmp6
+	ipifc
+	ipmux
+	rudp
+	tcp
+	udp
+
+misc
+	uartbbb
+
+port
+	int cpuserver = 0;
+
+bootdir
+	/$objtype/bin/paqfs
+	/$objtype/bin/auth/factotum
+	boot
+	bootfs.paq
--- /dev/null	Thu Dec 31 12:39:09 2020
+++ b/dat.h	Sat Nov 28 23:38:41 2020
@@ -0,0 +1,184 @@
+typedef struct Conf Conf;
+typedef struct Confmem Confmem;
+typedef struct FPsave FPsave;
+typedef struct PFPU PFPU;
+typedef struct L1 L1;
+typedef struct Label Label;
+typedef struct Lock Lock;
+typedef struct KMap KMap;
+typedef struct MMMU MMMU;
+typedef struct Mach Mach;
+typedef struct Notsave Notsave;
+typedef struct Page Page;
+typedef struct Proc Proc;
+typedef struct PMMU PMMU;
+typedef struct Ureg Ureg;
+typedef struct ISAConf ISAConf;
+typedef uvlong Tval;
+
+#pragma incomplete Ureg
+
+#define MAXSYSARG	5	/* for mount(fd, mpt, flag, arg, srv) */
+
+#define AOUT_MAGIC	(E_MAGIC)
+
+struct Lock
+{
+	ulong	key;
+	u32int	sr;
+	uintptr	pc;
+	Proc*	p;
+	Mach*	m;
+	int	isilock;
+};
+
+struct Label
+{
+	uintptr	sp;
+	uintptr	pc;
+};
+
+struct FPsave
+{
+	ulong	exc, scr;
+	uchar	regs[256];
+};
+
+struct PFPU
+{
+	int	fpstate;
+	FPsave	fpsave[1];
+};
+
+enum
+{
+	FPinit,
+	FPactive,
+	FPinactive,
+	FPillegal = 0x100
+};
+
+struct
+{
+	Lock;
+	char	machs[MAXMACH];		/* active CPUs */
+	int	exiting;		/* shutdown */
+}active;
+
+extern register Mach* m;			/* R10 */
+extern register Proc* up;			/* R9 */
+
+extern ulong *intcps;
+extern ulong *cprm;
+extern ulong *timer2;
+extern ulong *timer3;
+extern ulong *gpio[4];
+
+struct Confmem
+{
+	uintptr	base;
+	uintptr	limit;
+	usize	npage;
+	uintptr	kbase;
+	uintptr	klimit;
+};
+
+struct Conf
+{
+	ulong	nmach;		/* processors */
+	ulong	nproc;		/* processes */
+	Confmem	mem[1];		/* physical memory */
+	ulong	npage;		/* total physical pages of memory */
+	usize	upages;		/* user page pool */
+	ulong	copymode;	/* 0 is copy on write, 1 is copy on reference */
+	ulong	ialloc;		/* max interrupt time allocation in bytes */
+	ulong	pipeqsize;	/* size in bytes of pipe queues */
+	ulong	nimage;		/* number of page cache image headers */
+	ulong	nswap;		/* number of swap pages */
+	int	nswppo;		/* max # of pageouts per segment pass */
+	int	monitor;
+};
+
+/*
+ *  things saved in the Proc structure during a notify
+ */
+struct Notsave {
+	int	emptiness;
+};
+
+/*
+ *  MMU stuff in proc
+ */
+#define NCOLOR	1
+
+struct PMMU
+{
+	L1 *l1;
+	Page *mmuused, *mmufree;
+	
+	int nkmap;
+	Page *kmaptable;
+};
+
+#include "../port/portdat.h"
+
+struct L1
+{
+	Ref;
+	uintptr pa;
+	ulong *va;
+	L1 *next;
+};
+
+struct MMMU
+{
+	L1 l1;
+	L1 *l1free;
+	int nfree;
+	uchar asid;
+};
+
+struct Mach
+{
+	/* known to assembly */
+	int	machno;			/* physical id of processor */
+	uintptr	splpc;			/* pc of last caller to splhi */
+	Proc*	proc;			/* current process */
+	ulong	excregs[3];
+	ulong	cycleshi;
+	/* end of known to assembly */
+
+	int	flushmmu;		/* flush current proc mmu state */
+
+	ulong	ticks;			/* of the clock since boot time */
+	Label	sched;			/* scheduler wakeup */
+	Lock	alarmlock;		/* access to alarm list */
+	void*	alarm;			/* alarms bound to this clock */
+	int	inclockintr;
+
+	Proc*	readied;		/* for runproc */
+	ulong	schedticks;		/* next forced context switch */
+
+	ulong	delayloop;
+
+	/* stats */
+	int	tlbfault;
+	int	tlbpurge;
+	int	pfault;
+	int	cs;
+	int	syscall;
+	int	load;
+	int	intr;
+	int	lastintr;
+	int	ilockdepth;
+	Perf	perf;			/* performance counters */
+
+
+	int	cpumhz;
+	uvlong	cpuhz;			/* speed of cpu */
+	uvlong	cyclefreq;		/* Frequency of user readable cycle counter */
+	
+	MMMU;
+
+	int	stack[1];
+};
--- /dev/null	Thu Dec 31 12:39:09 2020
+++ b/devether.c	Sat Nov 28 23:38:41 2020
@@ -0,0 +1,509 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "pool.h"
+#include "ureg.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+static Ether *etherxx[MaxEther];
+
+Chan*
+etherattach(char* spec)
+{
+	ulong ctlrno;
+	char *p;
+	Chan *chan;
+
+	ctlrno = 0;
+	if(spec && *spec){
+		ctlrno = strtoul(spec, &p, 0);
+		if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther))
+			error(Ebadarg);
+	}
+	if(etherxx[ctlrno] == 0)
+		error(Enodev);
+
+	chan = devattach('l', spec);
+	if(waserror()){
+		chanfree(chan);
+		nexterror();
+	}
+	chan->dev = ctlrno;
+	if(etherxx[ctlrno]->attach)
+		etherxx[ctlrno]->attach(etherxx[ctlrno]);
+	poperror();
+	return chan;
+}
+
+static Walkqid*
+etherwalk(Chan* chan, Chan* nchan, char** name, int nname)
+{
+	return netifwalk(etherxx[chan->dev], chan, nchan, name, nname);
+}
+
+static int
+etherstat(Chan* chan, uchar* dp, int n)
+{
+	return netifstat(etherxx[chan->dev], chan, dp, n);
+}
+
+static Chan*
+etheropen(Chan* chan, int omode)
+{
+	return netifopen(etherxx[chan->dev], chan, omode);
+}
+
+static Chan*
+ethercreate(Chan*, char*, int, ulong)
+{
+	error(Eperm);
+	return 0;
+}
+
+static void
+etherclose(Chan* chan)
+{
+	netifclose(etherxx[chan->dev], chan);
+}
+
+static long
+etherread(Chan* chan, void* buf, long n, vlong off)
+{
+	Ether *ether;
+	ulong offset = off;
+
+	ether = etherxx[chan->dev];
+	if((chan->qid.type & QTDIR) == 0 && ether->ifstat){
+		/*
+		 * With some controllers it is necessary to reach
+		 * into the chip to extract statistics.
+		 */
+		if(NETTYPE(chan->qid.path) == Nifstatqid)
+			return ether->ifstat(ether, buf, n, offset);
+		else if(NETTYPE(chan->qid.path) == Nstatqid)
+			ether->ifstat(ether, buf, 0, offset);
+	}
+
+	return netifread(ether, chan, buf, n, offset);
+}
+
+static Block*
+etherbread(Chan* chan, long n, ulong offset)
+{
+	return netifbread(etherxx[chan->dev], chan, n, offset);
+}
+
+static int
+etherwstat(Chan* chan, uchar* dp, int n)
+{
+	return netifwstat(etherxx[chan->dev], chan, dp, n);
+}
+
+static void
+etherrtrace(Netfile* f, Etherpkt* pkt, int len)
+{
+	int i, n;
+	Block *bp;
+
+	if(qwindow(f->in) <= 0)
+		return;
+	if(len > 58)
+		n = 58;
+	else
+		n = len;
+	bp = iallocb(64);
+	if(bp == nil)
+		return;
+	memmove(bp->wp, pkt->d, n);
+	i = TK2MS(MACHP(0)->ticks);
+	bp->wp[58] = len>>8;
+	bp->wp[59] = len;
+	bp->wp[60] = i>>24;
+	bp->wp[61] = i>>16;
+	bp->wp[62] = i>>8;
+	bp->wp[63] = i;
+	bp->wp += 64;
+	qpass(f->in, bp);
+}
+
+Block*
+etheriq(Ether* ether, Block* bp, int fromwire)
+{
+	Etherpkt *pkt;
+	ushort type;
+	int len, multi, tome, fromme;
+	Netfile **ep, *f, **fp, *fx;
+	Block *xbp;
+
+	ether->inpackets++;
+
+	pkt = (Etherpkt*)bp->rp;
+	len = BLEN(bp);
+	type = (pkt->type[0]<<8)|pkt->type[1];
+	fx = 0;
+	ep = &ether->f[Ntypes];
+
+	multi = pkt->d[0] & 1;
+	/* check for valid multicast addresses */
+	if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) != 0 && ether->prom == 0){
+		if(!activemulti(ether, pkt->d, sizeof(pkt->d))){
+			if(fromwire){
+				freeb(bp);
+				bp = 0;
+			}
+			return bp;
+		}
+	}
+
+	/* is it for me? */
+	tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
+	fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0;
+
+	/*
+	 * Multiplex the packet to all the connections which want it.
+	 * If the packet is not to be used subsequently (fromwire != 0),
+	 * attempt to simply pass it into one of the connections, thereby
+	 * saving a copy of the data (usual case hopefully).
+	 */
+	for(fp = ether->f; fp < ep; fp++){
+		if(f = *fp)
+		if(f->type == type || f->type < 0)
+		if(tome || multi || f->prom){
+			/* Don't want to hear bridged packets */
+			if(f->bridge && !fromwire && !fromme)
+				continue;
+			if(!f->headersonly){
+				if(fromwire && fx == 0)
+					fx = f;
+				else if(xbp = iallocb(len)){
+					memmove(xbp->wp, pkt, len);
+					xbp->wp += len;
+					if(qpass(f->in, xbp) < 0) {
+						// print("soverflow for f->in\n");
+						ether->soverflows++;
+					}
+				}
+				else {
+					// print("soverflow iallocb\n");
+					ether->soverflows++;
+				}
+			}
+			else
+				etherrtrace(f, pkt, len);
+		}
+	}
+
+	if(fx){
+		if(qpass(fx->in, bp) < 0) {
+			// print("soverflow for fx->in\n");
+			ether->soverflows++;
+		}
+		return 0;
+	}
+	if(fromwire){
+		freeb(bp);
+		return 0;
+	}
+
+	return bp;
+}
+
+static int
+etheroq(Ether* ether, Block* bp)
+{
+	int len, loopback;
+	Etherpkt *pkt;
+
+	ether->outpackets++;
+
+	/*
+	 * Check if the packet has to be placed back onto the input queue,
+	 * i.e. if it's a loopback or broadcast packet or the interface is
+	 * in promiscuous mode.
+	 * If it's a loopback packet indicate to etheriq that the data isn't
+	 * needed and return, etheriq will pass-on or free the block.
+	 * To enable bridging to work, only packets that were originated
+	 * by this interface are fed back.
+	 */
+	pkt = (Etherpkt*)bp->rp;
+	len = BLEN(bp);
+	loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
+	if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom)
+		if(etheriq(ether, bp, loopback) == 0)
+			return len;
+
+	qbwrite(ether->oq, bp);
+	if(ether->transmit != nil)
+		ether->transmit(ether);
+	return len;
+}
+
+static long
+etherwrite(Chan* chan, void* buf, long n, vlong)
+{
+	Ether *ether;
+	Block *bp;
+	int nn, onoff;
+	Cmdbuf *cb;
+
+	ether = etherxx[chan->dev];
+	if(NETTYPE(chan->qid.path) != Ndataqid) {
+		nn = netifwrite(ether, chan, buf, n);
+		if(nn >= 0)
+			return nn;
+		cb = parsecmd(buf, n);
+		if(cb->f[0] && strcmp(cb->f[0], "nonblocking") == 0){
+			if(cb->nf <= 1)
+				onoff = 1;
+			else
+				onoff = atoi(cb->f[1]);
+			qnoblock(ether->oq, onoff);
+			free(cb);
+			return n;
+		}
+		free(cb);
+		if(ether->ctl!=nil)
+			return ether->ctl(ether,buf,n);
+
+		error(Ebadctl);
+	}
+
+	if(n > ether->maxmtu)
+		error(Etoobig);
+	if(n < ether->minmtu)
+		error(Etoosmall);
+
+	bp = allocb(n);
+	if(waserror()){
+		freeb(bp);
+		nexterror();
+	}
+	memmove(bp->rp, buf, n);
+	memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen);
+	poperror();
+	bp->wp += n;
+
+	return etheroq(ether, bp);
+}
+
+static long
+etherbwrite(Chan* chan, Block* bp, ulong)
+{
+	Ether *ether;
+	long n;
+
+	n = BLEN(bp);
+	if(NETTYPE(chan->qid.path) != Ndataqid){
+		if(waserror()) {
+			freeb(bp);
+			nexterror();
+		}
+		n = etherwrite(chan, bp->rp, n, 0);
+		poperror();
+		freeb(bp);
+		return n;
+	}
+	ether = etherxx[chan->dev];
+
+	if(n > ether->maxmtu){
+		freeb(bp);
+		error(Etoobig);
+	}
+	if(n < ether->minmtu){
+		freeb(bp);
+		error(Etoosmall);
+	}
+
+	return etheroq(ether, bp);
+}
+
+static struct {
+	char*	type;
+	int	(*reset)(Ether*);
+} cards[MaxEther+1];
+
+void
+addethercard(char* t, int (*r)(Ether*))
+{
+	static int ncard;
+
+	if(ncard == MaxEther)
+		panic("too many ether cards");
+	cards[ncard].type = t;
+	cards[ncard].reset = r;
+	ncard++;
+}
+
+int
+parseether(uchar *to, char *from)
+{
+	char nip[4];
+	char *p;
+	int i;
+
+	p = from;
+	for(i = 0; i < Eaddrlen; i++){
+		if(*p == 0)
+			return -1;
+		nip[0] = *p++;
+		if(*p == 0)
+			return -1;
+		nip[1] = *p++;
+		nip[2] = 0;
+		to[i] = strtoul(nip, 0, 16);
+		if(*p == ':')
+			p++;
+	}
+	return 0;
+}
+
+static Ether*
+etherprobe(int cardno, int ctlrno)
+{
+	int i, lg;
+	ulong mb, bsz;
+	Ether *ether;
+	char buf[128], name[32];
+
+	ether = malloc(sizeof(Ether));
+	if(ether == nil){
+		print("etherprobe: no memory for Ether\n");
+		return nil;
+	}
+	memset(ether, 0, sizeof(Ether));
+	ether->ctlrno = ctlrno;
+	ether->mbps = 10;
+	ether->minmtu = ETHERMINTU;
+	ether->maxmtu = ETHERMAXTU;
+
+	if(cardno >= MaxEther || cards[cardno].type == nil){
+		free(ether);
+		return nil;
+	}
+	if(cards[cardno].reset(ether) < 0){
+		free(ether);
+		return nil;
+	}
+
+	snprint(name, sizeof(name), "ether%d", ctlrno);
+
+	i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %d-%d",
+		ctlrno, cards[cardno].type, ether->mbps, ether->port, ether->irq0, ether->irqn);
+	i += sprint(buf+i, ": %2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux",
+		ether->ea[0], ether->ea[1], ether->ea[2],
+		ether->ea[3], ether->ea[4], ether->ea[5]);
+	sprint(buf+i, "\n");
+	print(buf);
+
+	/* compute log10(ether->mbps) into lg */
+	for(lg = 0, mb = ether->mbps; mb >= 10; lg++)
+		mb /= 10;
+	if (lg > 0)
+		lg--;
+	if (lg > 14)			/* 2^(14+17) = 2³¹ */
+		lg = 14;
+	/* allocate larger output queues for higher-speed interfaces */
+	bsz = 1UL << (lg + 17);		/* 2¹⁷ = 128K, bsz = 2ⁿ × 128K */
+	while (bsz > mainmem->maxsize / 8 && bsz > 128*1024)
+		bsz /= 2;
+
+	netifinit(ether, name, Ntypes, bsz);
+	if(ether->oq == nil) {
+		ether->oq = qopen(bsz, Qmsg, 0, 0);
+		ether->limit = bsz;
+	}
+	if(ether->oq == nil)
+		panic("etherreset %s: can't allocate output queue of %ld bytes", name, bsz);
+	ether->alen = Eaddrlen;
+	memmove(ether->addr, ether->ea, Eaddrlen);
+	memset(ether->bcast, 0xFF, Eaddrlen);
+
+	return ether;
+}
+
+static void
+etherreset(void)
+{
+	Ether *ether;
+	int cardno, ctlrno;
+
+	cardno = ctlrno = 0;
+	while(cards[cardno].type != nil && ctlrno < MaxEther){
+		if(etherxx[ctlrno] != nil){
+			ctlrno++;
+			continue;
+		}
+		if((ether = etherprobe(cardno, ctlrno)) == nil){
+			cardno++;
+			continue;
+		}
+		etherxx[ctlrno] = ether;
+		ctlrno++;
+	}
+}
+
+static void
+ethershutdown(void)
+{
+	Ether *ether;
+	int i;
+
+	for(i = 0; i < MaxEther; i++){
+		ether = etherxx[i];
+		if(ether == nil)
+			continue;
+		if(ether->shutdown == nil) {
+			print("#l%d: no shutdown function\n", i);
+			continue;
+		}
+		(*ether->shutdown)(ether);
+	}
+}
+
+
+#define POLY 0xedb88320
+
+/* really slow 32 bit crc for ethers */
+ulong
+ethercrc(uchar *p, int len)
+{
+	int i, j;
+	ulong crc, b;
+
+	crc = 0xffffffff;
+	for(i = 0; i < len; i++){
+		b = *p++;
+		for(j = 0; j < 8; j++){
+			crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0);
+			b >>= 1;
+		}
+	}
+	return crc;
+}
+
+Dev etherdevtab = {
+	'l',
+	"ether",
+
+	etherreset,
+	devinit,
+	ethershutdown,
+	etherattach,
+	etherwalk,
+	etherstat,
+	etheropen,
+	ethercreate,
+	etherclose,
+	etherread,
+	etherbread,
+	etherwrite,
+	etherbwrite,
+	devremove,
+	etherwstat,
+};
--- /dev/null	Thu Dec 31 12:39:09 2020
+++ b/devgpio.c	Sat Nov 28 23:38:41 2020
@@ -0,0 +1,268 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+enum{
+	GPIO_SYSCONFIG = 0x10/4,
+		SOFTRESET = 1<<1,
+	GPIO_SYSSTATUS = 0x114/4,
+		RESETDONE = 1<<0,
+	GPIO_OE = 0x134/4,
+	GPIO_DATAOUT = 0x13c/4,
+	GPIO_CLEARDATAOUT = 0x190/4,
+	GPIO_SETDATAOUT = 0x194/4,
+};
+
+enum{
+	Qroot,
+	Qgpio,
+	Qdir,
+		Qdata,
+		Qctl,
+		Qevent,
+};
+
+static Dirtab rootdir = { "#G",   { Qroot, 0, QTDIR }, 0, 0555 };
+static Dirtab gpiodir = { "gpio", { Qgpio, 0, QTDIR }, 0, 0555 };
+
+static Dirtab pinfiles[] = {
+	"data",  { Qdata,  0, QTFILE }, 0, 0666,
+	"ctl",   { Qctl,   0, QTFILE }, 0, 0666,
+	"event", { Qevent, 0, QTFILE }, 0, 0444,
+};
+
+static void
+gpioinit(void)
+{
+	int i;
+
+	for(i = 0; i < 4; i++){
+		gpio[i][GPIO_SYSCONFIG] = SOFTRESET;
+		while((gpio[i][GPIO_SYSSTATUS] & RESETDONE) != 0);
+		gpio[i][GPIO_OE] = 0;
+	}
+}
+
+static void
+gpioshutdown(void)
+{
+}
+
+static Chan*
+gpioattach(char *spec)
+{
+	return devattach('G', spec);
+}
+
+static int
+gpiogen(Chan *c, char*, Dirtab*, int, int s, Dir *db)
+{
+	Qid qid;
+
+	if(s == DEVDOTDOT){
+		switch((ulong)c->qid.path&0xff){
+		case Qroot:
+		case Qgpio:
+			qid.type = QTDIR;
+			qid.path = Qroot;
+			qid.vers = 0;
+			devdir(c, qid, 0, 0, eve, 0555, db);
+			return 1;
+		case Qdir:
+			qid.type = QTDIR;
+			qid.path = Qgpio;
+			qid.vers = 0;
+			devdir(c, qid, 0, 0, eve, 0555, db);
+			return 1;
+		case Qdata:
+		case Qctl:
+		case Qevent:
+			qid.type = QTDIR;
+			qid.path = (c->qid.path&0xff00)|Qdir;
+			qid.vers = 0;
+			devdir(c, qid, 0, 0, eve, 0555, db);
+			return 1;
+		}
+		return -1;
+	}
+
+	if(c->qid.path == Qroot){
+		if(s == 0){
+			qid.type = QTDIR;
+			qid.path = Qgpio;
+			qid.vers = 0;
+			devdir(c, qid, "gpio", 0, eve, 0555, db);
+			return 1;
+		}
+		return -1;
+	}
+
+	if(c->qid.path == Qgpio){
+		if(s < 128){
+			qid.type = QTDIR;
+			qid.path = s<<8|Qdir;
+			qid.vers = 0;
+			snprint(up->genbuf, sizeof(up->genbuf), "%d", s);
+			devdir(c, qid, up->genbuf, 0, eve, 0555, db);
+			return 1;
+		}
+		return -1;
+	}
+
+	if((c->qid.path & 0xff) == Qdir){
+		qid.type = QTFILE;
+		qid.path = (c->qid.path&0xff00);
+		qid.vers = 0;
+		switch(s){
+		case 0:
+			qid.path |= Qdata;
+			devdir(c, qid, "data", 0, eve, 0666, db);
+			return 1;
+		case 1:
+			qid.path |= Qctl;
+			devdir(c, qid, "ctl", 0, eve, 0666, db);
+			return 1;
+		case 2:
+			qid.path |= Qevent;
+			devdir(c, qid, "event", 0, eve, 0444, db);
+			return 1;
+		}
+		return -1;
+	}
+
+	qid.type = QTFILE;
+	qid.path = c->qid.path+s;
+	qid.vers = 0;
+
+	switch((ulong)c->qid.path & 0xff){
+	case Qdata:
+		devdir(c, qid, "data", 0, eve, 0666, db);
+		return 1;
+	case Qctl:
+		devdir(c, qid, "ctl", 0, eve, 0666, db);
+		return 1;
+	case Qevent:
+		devdir(c, qid, "event", 0, eve, 0666, db);
+		return 1;
+	}
+
+	return -1;
+}
+
+static Walkqid*
+gpiowalk(Chan *c, Chan *nc, char** name, int nname)
+{
+	return devwalk(c, nc, name, nname, 0, 0, gpiogen);
+}
+
+static int
+gpiostat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, 0, 0, gpiogen);
+}
+
+static Chan*
+gpioopen(Chan *c, int omode)
+{
+	c = devopen(c, omode, 0, 0, gpiogen);
+	return c;
+}
+
+static void
+gpioclose(Chan*)
+{
+}
+
+static long
+gpioread(Chan *c, void *a, long n, vlong off)
+{
+	ulong pin, reg, i;
+
+	pin = (ulong)c->qid.path>>8;
+	reg = pin>>5;
+	i = pin&0x1f;
+	switch((ulong)c->qid.path&0xff){
+	case Qroot:
+	case Qgpio:
+	case Qdir:
+		return devdirread(c, a, n, 0, 0, gpiogen);
+	case Qdata:
+		if(n > 2)
+			n = 2;
+		if(off > 1)
+			return 0;
+		if((gpio[reg][GPIO_DATAOUT] & 1<<i) != 0)
+			memmove(a, "1\n", n);
+		else
+			memmove(a, "0\n", n);
+		return 2;
+	case Qctl:
+		if(n > 8)
+			n = 8;
+		if(off > 8)
+			return 0;
+		memmove(a, "dig out\n" + off, n - off);
+		return n - off;
+	case Qevent:
+		return 0;
+	}
+	error(Egreg);
+	return -1;
+}
+
+static char Ebaddata[] = "bad data message";
+
+static long
+gpiowrite(Chan *c, void *va, long, vlong)
+{
+	char *a;
+	ulong pin, reg, i;
+
+	if(c->qid.type & QTDIR)
+		error(Eisdir);
+
+	a = va;
+	pin = (ulong)c->qid.path>>8;
+	reg = pin>>5;
+	i = pin&0x1f;
+	switch((ulong)c->qid.path&0xff){
+	case Qdata:
+		if(a[0] == '1'){
+			gpio[reg][GPIO_SETDATAOUT] = 1<<i;
+			return 1;
+		}
+		if(a[0] == '0'){
+			gpio[reg][GPIO_CLEARDATAOUT] = 1<<i;
+			return 1;
+		}
+		error(Ebaddata);
+		return -1;
+	}
+	error(Egreg);
+	return -1;
+}
+
+Dev gpiodevtab = {
+	'G',
+	"gpio",
+
+	devreset,
+	gpioinit,
+	gpioshutdown,
+	gpioattach,
+	gpiowalk,
+	gpiostat,
+	gpioopen,
+	devcreate,
+	gpioclose,
+	gpioread,
+	devbread,
+	gpiowrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null	Thu Dec 31 12:39:09 2020
+++ b/etherbbb.c	Sat Nov 28 23:38:41 2020
@@ -0,0 +1,501 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/netif.h"
+#include "etherif.h"
+
+enum{
+	CPSW_SS,
+		SS_SOFT_RESET = 0x08/4,
+
+	CPSW_CPDMA = 0x0800/4,
+		TX_CONTROL = CPSW_CPDMA+0x04/4,
+			TX_EN = 1<<0,
+		RX_CONTROL = CPSW_CPDMA+0x14/4,
+			RX_EN = 1<<0,
+		CPDMA_SOFT_RESET = CPSW_CPDMA+0x1c/4,
+		DMASTATUS = CPSW_CPDMA+0x24/4,
+		RX_BUFFER_OFFSET = CPSW_CPDMA+0x28/4,
+		TX_INTMASK_SET = CPSW_CPDMA+0x88/4,
+		TX_INTMASK_CLEAR = CPSW_CPDMA+0x8c/4,
+			TX_MASK_ALL = 0xff,
+		CPDMA_EOI_VECTOR = CPSW_CPDMA+0x94/4,
+		RX_INTMASK_SET = CPSW_CPDMA+0xa8/4,
+			RX_MASK_ALL = 0xff,
+		DMA_INTMASK_SET = CPSW_CPDMA+0xb8/4,
+			STAT_INT_MASK = 1<<0,
+			HOST_ERR_INT_MASK = 1<<1,
+
+	CPSW_STATERAM = 0x0a00/4,
+		TX0_HDP = CPSW_STATERAM+0x00/4,
+		RX0_HDP = CPSW_STATERAM+0x20/4,
+		TX0_CP	= CPSW_STATERAM+0x40/4,
+		RX0_CP	= CPSW_STATERAM+0x60/4,
+
+	CPSW_ALE = 0x0d00/4,
+		ALE_CONTROL = CPSW_ALE+0x08/4,
+			BYPASS = 1<<4,
+			CLEAR_TABLE = 1<<30,
+			ENABLE_ALE = 1<<31,
+		PORTCTL0 = CPSW_ALE+0x40/4,
+		PORTCTL1 = CPSW_ALE+0x44/4,
+			PORT_STATE = 3,
+
+	CPSW_SL1 = 0x0d80/4,
+		MACCONTROL = CPSW_SL1+0x04/4,
+			IFCTL_A = 1<<15,
+			GIG = 1<<7,
+			GMII_EN = 1<<5,
+			FULLDUPLEX = 1<<0,
+		MACSTATUS = CPSW_SL1+0x08/4,
+		SL1_SOFT_RESET = CPSW_SL1+0x0c/4,
+
+	CPSW_WR = 0x1200/4,
+		WR_SOFT_RESET = CPSW_WR+0x04/4,
+		C0_RX_THRESH_EN = CPSW_WR+0x10/4,
+		C0_RX_EN = CPSW_WR+0x14/4,
+		C0_TX_EN = CPSW_WR+0x18/4,
+		C0_MISC_EN = CPSW_WR+0x1c/4,
+			MDIO_USERINT = 1<<0,
+			MDIO_LINKINT = 1<<1,
+			HOST_PEND = 1<<2,
+			STAT_PEND = 1<<3,
+			EVNT_PEND = 1<<4,
+		C0_RX_THRESH_STAT = CPSW_WR+0x40/4,
+		C0_RX_STAT = CPSW_WR+0x44/4,
+		C0_TX_STAT = CPSW_WR+0x48/4,
+		C0_MISC_STAT = CPSW_WR+0x4c/4,
+		RGMII_CTL = CPSW_WR+0x88/4,
+			RGMII1_LINK = 1<<0,
+			RGMII2_LINK = 1<<4,
+
+	MDIO = 0x1000/4,
+		MDIOCONTROL = MDIO+04/4,
+		MDIOALIVE = MDIO+0x08/4,
+		MDIOLINK = MDIO+0x0c/4,
+		MDIOLINKINTRAW = MDIO+0x10/4,
+		MDIOLINKINTMASKED = MDIO+0x14/4,
+		MDIOUSERPHYSEL0 = MDIO+0x84/4,
+		MDIOUSERPHYSEL1 = MDIO+0x88/4,
+			PHYADDRMON = 0x1f,
+			LINKINTENB = 1<<6,
+			LINKSEL = 1<<7,
+};
+
+#define TX_HDP(i)	(TX0_HDP+i)
+#define RX_HDP(i)	(RX0_HDP+i)
+#define TX_CP(i)	(TX0_CP+i)
+#define RX_CP(i)	(RX0_CP+i)
+
+enum{
+	EOQ = 1<<12,
+	OWNER = 1<<13,
+	EOP = 1<<14,
+	SOP = 1<<15,
+};
+
+typedef struct Desc Desc;
+struct Desc
+{
+	uintptr next;
+	uintptr bufptr;
+	ushort buflen;
+	ushort bufoff;
+	ushort pktlen;
+	ushort flags;
+};
+
+#define Rbsz		ROUNDUP(sizeof(Etherpkt)+16, 64)
+
+enum{
+	RXRING = 0x200,
+	TXRING = 0x200,
+};
+
+typedef struct Ctlr Ctlr;
+struct Ctlr
+{
+	ulong *r;
+	int attach;
+	int rxconsi;
+	int rxprodi;
+	Desc *rxd;
+	Block **rxb;
+	int txconsi;
+	int txprodi;
+	Desc *txd;
+	Block **txb;
+	Lock txlock;
+};
+
+static int ethinit(Ether*);
+
+static int
+replenish(Ctlr *c)
+{
+	int n;
+	Block *b;
+	Desc *d;
+
+	for(;;){
+		n = (c->rxprodi+1) & (RXRING-1);
+		if(n == c->rxconsi){
+			c->rxd[c->rxprodi].next = 0;
+			break;
+		}
+		b = iallocb(Rbsz);
+		if(b == nil){
+			iprint("cpsw: out of memory for rx buffers\n");
+			return -1;
+		}
+		c->rxb[n] = b;
+		d = &c->rxd[n];
+		d->bufptr = PADDR(b->rp);
+		d->bufoff = 0;
+		d->buflen = BALLOC(b);
+		if((d->buflen & 0x7ff) != d->buflen)
+			d->buflen = 0x7ff;
+		d->flags = OWNER;
+		assert(d->buflen > 0);
+		//cleandse(d, d+1);
+		cleandse(b->base, b->lim);
+		//coherence();
+		assert(c->rxd[c->rxprodi].next == 0);
+		d = &c->rxd[c->rxprodi];
+		d->next = PADDR(&c->rxd[n]);
+		//cleandse(d, d+1);
+		c->rxprodi = n;
+	}
+	return 0;
+}
+
+static void
+ethrxirq(Ureg*, void *arg)
+{
+	static int x;
+
+	int n;
+	Ether *edev;
+	Ctlr *c;
+	Desc *d, *d0;
+	Block *b;
+
+	edev = arg;
+	c = edev->ctlr;
+
+ 	//iprint("rx enter (%d)!\n", x);
+	n = 0;
+	d0 = nil;
+	for(;;){
+		d = &c->rxd[c->rxconsi];
+		//invaldse(d, d+1);
+		if((d->flags & OWNER) != 0)
+			break;
+		if((d->flags & SOP) == 0 || (d->flags & EOP) == 0)
+			iprint("cpsw: partial frame received -- shouldn't happen\n");
+		if((d->flags & OWNER) == 0 && (d->flags & EOQ) != 0 && d->next != 0){
+			//iprint("cpsw: rx misqueue detected\n");
+			d0 = d;
+		}
+		b = c->rxb[c->rxconsi];
+		b->wp = b->rp + (d->pktlen & 0x7ff);
+		invaldse(b->rp, b->wp);
+		//coherence();
+		etheriq(edev, b, 1);
+		c->r[RX0_CP] = PADDR(d);
+		c->rxconsi = (c->rxconsi+1) & (RXRING-1);
+		replenish(c);
+		n++;
+	}
+	if(n == 0)
+		iprint("cpsw: rx buffer full\n");
+	if(d0 != nil){
+		assert(d0->next == PADDR(d));
+		c->r[RX0_HDP] = PADDR(d);
+	}
+	c->r[CPDMA_EOI_VECTOR] = 1;
+	//iprint("rx leave (%d)!\n", x++);
+}
+
+static void
+ethtx(Ether *edev)
+{
+	int i, n;
+	Block *b;
+	Ctlr *c;
+	Desc *d0, *dn, *dp;
+
+	c = edev->ctlr;
+	ilock(&c->txlock);
+
+	for(;;){
+		dp = &c->txd[c->txconsi];
+		if(c->txconsi == c->txprodi){
+			dp->flags |= EOQ;
+			break;
+		}
+		if((dp->flags & OWNER) != 0)
+			break;
+		c->txconsi = (c->txconsi+1) & (TXRING-1);
+	}
+	c->r[TX0_CP] = PADDR(&c->txd[c->txconsi]);
+	n = 0;
+	d0 = nil;
+	for(;;){
+		i = (c->txprodi+1) & (TXRING-1);
+		dn = &c->txd[i];
+		if((dn->flags & OWNER) != 0){
+			iprint("cpsw: tx buffer full\n");
+			break;
+		}
+		b = qget(edev->oq);
+		if(b == nil)
+			break;
+		if(c->txb[c->txprodi] != nil)
+			freeb(c->txb[c->txprodi]);
+		c->txb[c->txprodi] = b;
+		dp->next = PADDR(dn);
+		dn->next = 0;
+		dn->bufptr = PADDR(b->rp);
+		dn->buflen = BLEN(b);
+		dn->flags = SOP | EOP | OWNER;
+		dn->pktlen = dn->buflen;
+		if(d0 == nil)
+			d0 = dp;
+		dp = dn;
+		c->txprodi = i;
+		n++;
+	}
+	//iprint("tx: queued %d packets\n", n);
+	if(d0 != nil){ // queued at least 1 packet
+		if((d0->flags & OWNER) == 0 && (d0->flags & EOQ) != 0){
+			//iprint("tx: misqueue detected!\n");
+			c->r[TX0_HDP] = d0->next;
+		}
+	}
+	iunlock(&c->txlock);
+}
+
+static void
+ethtxirq(Ureg*, void *arg)
+{
+	Ether *edev;
+	Ctlr *c;
+
+	edev = arg;
+	c = edev->ctlr;
+	//assert(c->r[TX0_HDP] == 0);
+	ethtx(edev);
+	c->r[CPDMA_EOI_VECTOR] = 2;
+}
+
+static void
+debugctlr(Ctlr *c)
+{
+	int i;
+	uintptr hdp;
+	Desc *d;
+
+	hdp = c->r[RX0_HDP];
+	if(hdp != 0){
+		d = (Desc*)kaddr(hdp);
+		iprint("pa(RX0_HDP)=%#p\n", hdp);
+		iprint("va(RX0_HDP)=%#p\n", d);
+		iprint("ii(RX0_HDP)=%ld\n",  d - &c->rxd[0]);	
+		iprint("next=%#p\n", d->next);
+	}
+	for(i = 0; i < RXRING; i++){
+		d = &c->rxd[i];
+		if(d->buflen <= d->bufoff)
+			iprint("i=%d -> {buflen=%d, bufoff=%d, pktlen=%d }\n", i, d->buflen, d->bufoff, d->pktlen);
+	}
+}
+
+static void
+ethmiscirq(Ureg*, void *arg)
+{
+	Ether *edev;
+	Ctlr *c;
+	ulong r;
+
+	edev = arg;
+	c = edev->ctlr;
+	r = c->r[C0_MISC_STAT];
+
+	if((r & MDIO_LINKINT) != 0){
+		if((c->r[MDIOLINK] & 1) == 0){
+			edev->link = 0;
+			iprint("cpsw: no link\n");
+		}else{
+			edev->link = 1;
+			edev->mbps = c->r[MACCONTROL] & IFCTL_A ? 100 : 10;
+			iprint("cpsw: %dMbps %s duplex link\n", edev->mbps, c->r[MACCONTROL] & FULLDUPLEX ? "full" : "half");
+		}
+		c->r[MDIOLINKINTRAW] |= 1;
+	}
+	if((r & HOST_PEND) != 0){
+		debugctlr(c);
+		r = c->r[DMASTATUS];
+		iprint("cpsw: dma host error (tx=0x%ulx, rx=0x%ulx)\n", (r>>20)&0xf, (r>>12)&0xf);
+		ethinit(edev);
+	}
+
+	c->r[CPDMA_EOI_VECTOR] = 3;
+}
+
+static int
+ethinit(Ether *edev)
+{
+	int i;
+	Ctlr *c;
+
+	c = edev->ctlr;
+
+	c->r[SS_SOFT_RESET] |= 1;
+	c->r[SL1_SOFT_RESET] |= 1;
+	c->r[CPDMA_SOFT_RESET] |= 1;
+
+	while((c->r[SS_SOFT_RESET] & 1) != 0);
+	while((c->r[SL1_SOFT_RESET] & 1) != 0);
+	while((c->r[CPDMA_SOFT_RESET] & 1) != 0);
+
+	for(i = 0; i < 8; i++){
+		c->r[TX_HDP(i)] = 0;
+		c->r[RX_HDP(i)] = 0;
+		c->r[TX_CP(i)] = 0;
+		c->r[RX_CP(i)] = 0;
+	}
+
+	c->r[ALE_CONTROL] |= ENABLE_ALE | CLEAR_TABLE | BYPASS;
+	c->r[PORTCTL0] |= PORT_STATE;
+	c->r[PORTCTL1] |= PORT_STATE;
+
+	//intrenable(ETHIRQRXTHR, ethrxthrirq, edev, edev->name);
+	intrenable(ETHIRQRX, ethrxirq, edev, edev->name);
+	intrenable(ETHIRQTX, ethtxirq, edev, edev->name);
+	intrenable(ETHIRQMISC, ethmiscirq, edev, edev->name);
+
+	c->r[TX_INTMASK_SET] |= TX_MASK_ALL;
+	c->r[RX_INTMASK_SET] |= RX_MASK_ALL;
+	c->r[DMA_INTMASK_SET] |= STAT_INT_MASK | HOST_ERR_INT_MASK;
+
+	c->r[MDIOUSERPHYSEL0] |= LINKINTENB;
+
+	c->r[C0_RX_THRESH_EN] |= 1;
+	c->r[C0_RX_EN] |= 1;
+	c->r[C0_TX_EN] |= 1;
+	c->r[C0_MISC_EN] |= HOST_PEND | MDIO_LINKINT;
+
+	c->r[RX_CONTROL] |= RX_EN;
+	c->r[TX_CONTROL] |= TX_EN;
+
+	c->r[MACCONTROL] &= ~GIG;
+	c->r[MACCONTROL] |= GMII_EN | FULLDUPLEX | IFCTL_A;
+
+	if(c->rxd == nil)
+		c->rxd = ucalloc(RXRING*sizeof(Desc));
+	memset(c->rxd, 0, RXRING*sizeof(Desc));
+	if(c->rxb == nil)
+		c->rxb = ucalloc(RXRING*sizeof(Block*));
+	memset(c->rxb, 0, RXRING*sizeof(Block*));
+
+	if(c->txd == nil)
+		c->txd = ucalloc(TXRING*sizeof(Desc));
+	memset(c->txd, 0, TXRING*sizeof(Desc));
+	if(c->txb == nil)
+		c->txb = ucalloc(TXRING*sizeof(Block*));
+	memset(c->txb, 0, TXRING*sizeof(Block*));
+
+#ifdef nope
+	ulong x;
+	x = va2pa(c->txd);
+	if((x&1) != 0)
+		iprint("ethinit: va2pa failed\n");
+	else
+		iprint("ethinit: %#p, SH=%ulx, OUTER=%ulx, INNER=%ulx\n", x, (x>>7)&1, (x>>2)&3, (x>>4)&7);
+
+	x = va2pa(c->txb);
+	if((x&1) != 0)
+		iprint("ethinit: va2pa failed\n");
+	else
+		iprint("ethinit: %#p, SH=%ulx, OUTER=%ulx, INNER=%ulx\n", x, (x>>7)&1, (x>>2)&3, (x>>4)&7);
+#endif
+
+	c->rxprodi = RXRING-1;
+	c->rxconsi = RXRING-1;
+	replenish(c);
+	c->rxconsi = 0;
+	replenish(c);
+
+	c->r[RX0_HDP] = PADDR(&c->rxd[c->rxconsi]);
+
+	return 0;
+}
+
+static void
+ethprom(void*, int)
+{
+}
+
+static void
+ethmcast(void*, uchar*, int)
+{
+}
+
+static void
+ethattach(Ether *edev)
+{
+	Ctlr *c;
+
+	c = edev->ctlr;
+	if(c->attach)
+		return;
+	c->attach = 1;
+}
+
+static int
+etherpnp(Ether *edev)
+{
+	static Ctlr ct;
+	static uchar mac[] = {0x6c, 0xec, 0xeb, 0xaf, 0x1c, 0xed};
+
+	ulong x;
+
+	if(ct.r != nil)
+		return -1;
+	memmove(edev->ea, mac, 6);
+	edev->ctlr = &ct;
+	edev->port = ETH_BASE;
+	ct.r = vmap(edev->port, 2*BY2PG);
+
+#ifdef nope
+	x = va2pa(ct.r);
+	if((x&1) != 0)
+		iprint("etherpnp: va2pa failed\n");
+	else
+		iprint("etherpnp: %#p, SH=%ulx, OUTER=%ulx, INNER=%ulx\n", x, (x>>7)&1, (x>>2)&3, (x>>4)&7);
+#endif
+
+	edev->irq0 = ETHIRQRXTHR;
+	edev->irqn = ETHIRQMISC;
+	edev->transmit = ethtx;
+	edev->attach = ethattach;
+	edev->promiscuous = ethprom;
+	edev->multicast = ethmcast;
+	edev->mbps = 100;
+	edev->arg = edev;
+	if(ethinit(edev) < 0){
+		edev->ctlr = nil;
+		return -1;
+	}
+	return 0;
+}
+
+void
+etherbbblink(void)
+{
+	addethercard("cpsw", etherpnp);
+}
--- /dev/null	Thu Dec 31 12:39:09 2020
+++ b/etherif.h	Sat Nov 28 23:38:41 2020
@@ -0,0 +1,40 @@
+enum {
+	MaxEther	= 1,
+	Ntypes		= 8,
+};
+
+typedef struct Ether Ether;
+struct Ether {
+
+	int	ctlrno;
+	int	minmtu;
+	int 	maxmtu;
+	uchar	ea[Eaddrlen];
+
+	int irq0;
+	int irqn;
+	uintptr port;
+
+	void	(*attach)(Ether*);	/* filled in by reset routine */
+	void	(*detach)(Ether*);
+	void	(*transmit)(Ether*);
+	long	(*ifstat)(Ether*, void*, long, ulong);
+	long 	(*ctl)(Ether*, void*, long); /* custom ctl messages */
+	void	(*power)(Ether*, int);	/* power on/off */
+	void	(*shutdown)(Ether*);	/* shutdown hardware before reboot */
+	void	*ctlr;
+
+	Queue*	oq;
+
+	Netif;
+};
+
+extern Block* etheriq(Ether*, Block*, int);
+extern void addethercard(char*, int(*)(Ether*));
+extern ulong ethercrc(uchar*, int);
+extern int parseether(uchar*, char*);
+
+#define NEXT(x, l)	(((x)+1)%(l))
+#define PREV(x, l)	(((x) == 0) ? (l)-1: (x)-1)
+#define	HOWMANY(x, y)	(((x)+((y)-1))/(y))
+#define ROUNDUP(x, y)	(HOWMANY((x), (y))*(y))
--- /dev/null	Thu Dec 31 12:39:09 2020
+++ b/fns.h	Sat Nov 28 23:38:41 2020
@@ -0,0 +1,61 @@
+#include "../port/portfns.h"
+
+int tas(void*);
+int cmpswap(long*, long, long);
+void evenaddr(uintptr);
+void* kaddr(uintptr);
+uintptr paddr(void*);
+uintptr cankaddr(uintptr);
+void procsave(Proc*);
+void procrestore(Proc*);
+void idlehands(void);
+void coherence(void);
+void procfork(Proc*);
+void procsetup(Proc*);
+KMap* kmap(Page*);
+void kunmap(KMap*);
+void* ucalloc(ulong);
+
+#define	waserror()	(up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
+#define getpgcolor(a) 0
+#define kmapinval()
+#define KADDR(a) kaddr(a)
+#define PADDR(a) paddr((void*)(a))
+#define userureg(ur) (((ur)->psr & PsrMask) == PsrMusr)
+#define VA(k) ((void*)(k))
+#define PTR2UINT(p) ((uintptr)(p))
+
+void uartinit(void);
+void mmuinit(void);
+uintptr ttbget(void);
+void ttbput(uintptr);
+void cycles(uvlong*);
+void kexit(Ureg*);
+ulong getifsr(void);
+ulong getdfsr(void);
+uintptr getifar(void);
+uintptr getdfar(void);
+void links(void);
+void* vmap(uintptr, ulong);
+void timerinit(void);
+void* tmpmap(uintptr);
+void tmpunmap(void*);
+void flushpg(void*);
+void setasid(ulong);
+void flushtlb(void);
+void noted(Ureg*, ulong);
+void l1switch(L1*, int);
+void intrenable(int, void (*)(Ureg*, void*), void*, char*);
+void intrinit(void);
+void intr(Ureg*);
+void fpoff(void);
+void fpsave(FPsave*);
+void fprestore(FPsave*);
+void fpinit(void);
+void fpclear(void);
+char* getconf(char*);
+void invalise(void*, void*);
+void cleandse(void*, void*);
+void invaldse(void*, void*);
+void touser(void*);
+int uartconsole(void);
--- /dev/null	Thu Dec 31 12:39:09 2020
+++ b/init9.s	Sat Nov 28 23:38:41 2020
@@ -0,0 +1,7 @@
+TEXT _main(SB), $-4
+	MOVW $setR12(SB), R12
+	MOVW 4(R13), R0
+	ADD $4, R13, R1
+	SUB $4, R13
+	MOVW R1, 8(R13)
+	MOVW $startboot(SB), R15
--- /dev/null	Thu Dec 31 12:39:09 2020
+++ b/intr.c	Sat Nov 28 23:38:41 2020
@@ -0,0 +1,108 @@
+#include "u.h"
+#include <ureg.h>
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+enum {
+	NINTR = 128,
+};
+
+static struct irq {
+	void (*f)(Ureg*, void*);
+	void *arg;
+	char *name;
+} irqs[NINTR];
+
+enum {
+	SYSCFG = 0x10/4,
+		AutoIdle = 1<<0,
+		SoftReset = 1<<1,
+	SYSSTATUS = 0x14/4,
+		ResetDone = 1<<0,
+	SIRIRQ = 0x40/4,
+	CONTROL = 0x48/4,
+		NewIRQAgr = 1<<0,
+	PROTECTION = 0x4c/4,
+		Protection = 1<<0,
+	IDLE = 0x50/4,
+		Turbo = 1<<1,
+
+	MIRCLRBASE = 0x88/4,
+	MIRSETBASE = 0x8c/4,
+	MIRREGSTEP = 0x20/4,
+};
+
+#define MIRSET(n)	(MIRSETBASE+MIRREGSTEP*(n))
+#define MIRCLR(n)	(MIRCLRBASE+MIRREGSTEP*(n))
+
+void
+intrinit(void)
+{
+	int i;
+
+	intcps[SYSCFG] |= SoftReset;
+	while((intcps[SYSSTATUS] & ResetDone) == 0);
+
+	for(i = 0; i < 4; i++)
+		intcps[MIRSET(i)] = ~0;
+
+	intcps[PROTECTION] |= Protection;
+}
+
+void
+intrenable(int irq, void (*f)(Ureg *, void *), void *arg, char *name)
+{
+	struct irq *i;
+
+	if(f == nil)
+		panic("intrenable: f == nil");
+	if(irq < 0 || irq >= NINTR)
+		panic("intrenable: invalid irq %d", irq);
+	if(irqs[irq].f != nil && irqs[irq].f != f)
+		panic("intrenable: handler already assigned");
+	i = &irqs[irq];
+	i->f = f;
+	i->arg = arg;
+	i->name = name;
+	intcps[MIRCLR((irq>>5)&3)] = 1<<(irq&0x1f);
+}
+
+void
+intr(Ureg *ureg)
+{
+	ulong v;
+	int irq;
+	struct irq *i;
+
+	v = intcps[SIRIRQ];
+	irq = v & 0x7f;
+
+	if(v >> 7){
+		print("spurious interrupt\n");
+		intcps[CONTROL] = NewIRQAgr;
+		return;
+	}
+
+	m->intr++;
+	m->lastintr = irq;
+	i = &irqs[irq];
+	if(i->f == nil)
+		print("irq without handler %d\n", irq);
+	else
+		i->f(ureg, i->arg);
+	intcps[CONTROL] = NewIRQAgr;
+
+	if(up != nil){
+		if(irq == TIMER2IRQ){
+			if(up->delaysched){
+				splhi();
+				sched();
+			}
+		}else
+			preempted();
+	}
+}
--- /dev/null	Thu Dec 31 12:39:09 2020
+++ b/io.h	Sat Nov 28 23:38:41 2020
@@ -0,0 +1,37 @@
+#define DRAM_BASE 0x80000000
+#define DRAM_TOP 0xC0000000
+#define UART_BASE 0x44E09000
+#define INTCPS_BASE 0x48200000
+
+#define TIMER0_BASE 0x44e05000
+#define TIMER1_BASE 0x44e31000
+#define TIMER2_BASE 0x48040000
+#define TIMER3_BASE 0x48042000
+#define TIMER4_BASE 0x48044000
+#define TIMER5_BASE 0x48046000
+#define TIMER6_BASE 0x48048000
+#define TIMER7_BASE 0x4804a000
+
+#define GPIO0_BASE	0x44e07000
+#define GPIO1_BASE	0x4804c000
+#define GPIO2_BASE	0x481ac000
+#define GPIO3_BASE	0x481ae000
+
+#define ETH_BASE	0x4a100000
+
+#define CPRM_BASE	0x44e00000
+
+#define SRAM_BASE 0x40300000
+#define SRAM_SIZE 64*1024
+
+#define ETHIRQRXTHR		40
+#define ETHIRQRX		41
+#define ETHIRQTX		42
+#define ETHIRQMISC		43
+
+#define TIMER0IRQ 66
+#define TIMER1IRQ 67
+#define TIMER2IRQ 68
+#define TIMER3IRQ 69
+
+#define UART0IRQ 72
--- /dev/null	Thu Dec 31 12:39:09 2020
+++ b/l.s	Sat Nov 28 23:38:41 2020
@@ -0,0 +1,489 @@
+#include "mem.h"
+#include "io.h"
+
+#define PUTC(c)\
+	ADD $0x14, R8;\
+	MOVW (R8), R9;\
+	AND $(1<<5), R9;\
+	CMP $0, R9;\
+	BEQ -3(PC);\
+	MOVW $(c), R9;\
+	ADD $-0x14, R8;\
+	MOVW R9, (R8);\
+
+#define PUTC2(c)\
+	MOVW $(c), R0;\
+	MOVW R0, (R8);\
+
+#define DUMPDIGIT(Rx, d)\
+	MOVW Rx, R9;\
+	MOVW R9>>d, R9;\
+	AND $0xf, R9;\
+	CMP $9, R9;\
+	ADD.GT $7, R9;\
+	ADD $'0', R9;\
+	MOVW R9, (R8);\
+
+#define DUMPREG(Rx)\
+	DUMPDIGIT(R(Rx), 28)\
+	DUMPDIGIT(R(Rx), 24)\
+	DUMPDIGIT(R(Rx), 20)\
+	DUMPDIGIT(R(Rx), 16)\
+	DUMPDIGIT(R(Rx), 12)\
+	DUMPDIGIT(R(Rx), 8)\
+	DUMPDIGIT(R(Rx), 4)\
+	DUMPDIGIT(R(Rx), 0)\
+
+TEXT _start(SB), $-4
+	MOVW $(KTZERO-KZERO+DRAM_BASE), R13
+	MOVW $(UART_BASE), R8
+
+	PUTC('P')
+	MOVW $0, R0
+	MOVW $DRAM_BASE, R1
+	MOVW $(CONFADDR-KZERO+DRAM_BASE), R2
+_start0:
+	MOVW.P R0, 4(R1)
+	CMP.S R1, R2
+	BNE _start0
+
+	PUTC('l')
+	MOVW $(SECSZ), R0
+	MOVW $(MACHL1(0)-KZERO+DRAM_BASE), R4
+	MOVW $KZERO, R1
+	ADD R1>>(SECSH-2), R4, R1
+	MOVW $((DRAM_BASE&0xffe00000)|L1SEC|L1NORMAL), R2
+	MOVW $(KTOP-KZERO+DRAM_BASE), R3
+_start1:
+	MOVW.P R2, 4(R1)
+	ADD R0, R2
+	CMP.S R2, R3
+	BGE _start1
+
+	PUTC('a')
+	MOVW $L2SZ, R0
+	MOVW $VMAP, R1
+	ADD R1>>(SECSH-2), R4, R1
+	MOVW $((VMAPL2-KZERO+DRAM_BASE)|L1PT), R2
+	MOVW $(VMAPL2-KZERO+DRAM_BASE+VMAPL2SZ), R3
+_start2:
+	MOVW.P R2, 4(R1)
+	ADD R0, R2
+	CMP.S R2, R3
+	BGE _start2
+
+	MOVW $(UART_BASE|L2VALID|L2DEVICE|L2KERRW), R0
+	MOVW $(VMAPL2-KZERO+DRAM_BASE), R1
+	MOVW R0, (R1)
+
+	PUTC('n')
+
+	MOVW $(MACH(0)-KZERO+DRAM_BASE), R(Rmach)
+_start3:
+	/* enable MMU permission checking */
+	MOVW $0x55555555, R0
+	MCR 15, 0, R0, C(3), C(0), 0
+
+	/* invalidate inst/data tlbs */
+	MOVW $0, R0
+	MCR 15, 0, R0, C(8), C(7), 0
+	DSB
+
+	/* set TTBR0 */
+	ORR $TTBATTR, R4, R1
+	MCR 15, 0, R1, C(2), C(0), 0
+
+	/* set TTBCR */
+	MOVW $0, R1
+	MCR 15, 0, R1, C(2), C(0), 2
+
+	/* errata 709718 */
+	MRC 15, 0, R1, C(10), C(2), 0
+	BIC $(1<<17|1<<16), R1
+	MCR 15, 0, R1, C(10), C(2), 0
+
+	MRC 15, 0, R1, C(1), C(0), 0
+	ORR $(1<<29), R1		/* enable access flags */
+	BIC $(1<<28), R1		/* disable TEX remap */
+	ORR $1, R1				/* enable mmu */
+	MOVW $_virt(SB), R2
+	PUTC(' ')
+	MCR 15, 0, R1, C(1), C(0), 0
+	MOVW R2, R15
+
+TEXT _virt(SB), $-4
+	DSB
+	ISB
+
+	ADD $(KZERO-DRAM_BASE), R(Rmach)
+	MOVW R(Rmach), R13
+	ADD $MACHSIZE, R13
+
+	MOVW R(Rmach), R0
+	ADD $12, R0
+	BL loadsp(SB)
+
+	MOVW $vectors(SB), R1
+	MCR 15, 0, R1, C(12), C(0), 0
+
+	/* enable maths coprocessors in CPACR but disable them in FPEXC */
+	MRC 15, 0, R0, C(1), C(0), 2
+	ORR $(15<<20), R0
+	MCR 15, 0, R0, C(1), C(0), 2
+
+	VMRS(0xe, FPEXC, 0)
+	BIC $(3<<30), R0
+	VMSR(0xe, 0, FPEXC)
+
+	/* clear L1 cache */
+	MOVW $0, R0
+	MCR 15, 0, R0, C(7), C(5), 0 /* invalidate instruction caches */
+	MCR 15, 0, R0, C(7), C(5), 6 /* invalidate brach preditor array */
+	BL l1dclear(SB)
+
+	/* disable L1 hw alias support, enable L2 cache, IBE */
+	MRC 15, 0, R0, C(1), C(0), 1
+	ORR $(1<<0|1<<1|1<<6), R0
+	BIC $(1<<1), R0
+	MCR 15, 0, R0, C(1), C(0), 1
+
+	/* enable instruction caching, branch prediction, data-caching */
+	MRC 15, 0, R0, C(1), C(0), 0
+	ORR $(1<<12|1<<11|1<<2), R0
+	BIC $(1<<2), R0
+	MCR 15, 0, R0, C(1), C(0), 0
+
+	DSB
+	ISB
+
+	MOVW $(VMAP), R8
+	PUTC('9')
+
+	/* kernel Mach* in TPIDRPRW */
+	MCR 15, 0, R(Rmach), C(13), C(0), 4
+
+	MOVW $setR12(SB), R12
+	MOVW $0, R(Rup)
+
+	BL main(SB)
+	B idlehands(SB)
+
+	BL _div(SB) /* hack to load _div */
+
+TEXT touser(SB), $-4
+	CPS(CPSID)
+
+	SUB $12, R13
+	MOVW R0, (R13)
+	MOVW $0, R1
+	MOVW R1, 4(R13)
+	MOVW $(UTZERO+0x20), R1
+	MOVW R1, 8(R13)
+
+	MOVW CPSR, R1
+	BIC $(PsrMask|PsrDirq|PsrDfiq), R1
+	ORR $PsrMusr, R1
+	MOVW R1, SPSR
+
+	MOVW $(KTZERO-(15*4)), R0
+	MOVM.IA (R0), [R0-R12]
+	MOVM.IA.S (R13), [R13-R14]
+	ADD $8, R13
+	MOVM.IA.W.S (R13), [R15]
+
+TEXT forkret(SB), $-4
+	MOVW (16*4)(R13), R0
+	MOVW R0, SPSR
+	MOVM.IA.W (R13), [R0-R12]
+	MOVM.IA.S (R13), [R13-R14]
+	ADD $16, R13
+	DSB
+	ISB
+	MOVM.IA.W.S (R13), [R15]
+
+TEXT loadsp(SB), $0
+	CPS(CPSMODE | PsrMabt)
+	MOVW R0, R13
+	CPS(CPSMODE | PsrMund)
+	MOVW R0, R13
+	CPS(CPSMODE | PsrMirq)
+	MOVW R0, R13
+	CPS(CPSMODE | PsrMfiq)
+	MOVW R0, R13
+	CPS(CPSMODE | PsrMsvc)
+	RET
+
+TEXT tas(SB), $0
+TEXT _tas(SB), $0
+	MOVW $0xDEADDEAD, R2
+_tas1:
+	LDREX (R0), R1
+	STREX R2, (R0), R3
+	CMP.S $0, R3
+	BNE _tas1
+	MOVW R1, R0
+	DMB
+	RET
+
+TEXT coherence(SB), $0
+	DSB
+	RET
+
+TEXT idlehands(SB), $0
+	DSB
+	WFE
+	RET
+
+TEXT ttbget(SB), $0
+	MRC 15, 0, R0, C(2), C(0), 0
+	BIC $0x7f, R0
+	RET
+
+TEXT ttbput(SB), $0
+	ORR $TTBATTR, R0
+	MCR 15, 0, R0, C(2), C(0), 0
+	RET
+
+TEXT flushpg(SB), $0
+	MCR 15, 0, R0, C(8), C(7), 0
+	DSB
+	RET
+
+TEXT flushtlb(SB), $0
+	MCR 15, 0, R0, C(8), C(5), 0
+	MCR 15, 0, R0, C(8), C(6), 0
+	MCR 15, 0, R0, C(8), C(7), 0
+	DSB
+	RET
+
+TEXT setasid(SB), $0
+	DSB	/* errata */
+	MCR 15, 0, R0, C(13), C(0), 1
+	RET
+
+TEXT spllo(SB), $-4
+	MOVW CPSR, R0
+	CPS(CPSIE)
+	RET
+
+TEXT splhi(SB), $-4
+	MOVW R14, 4(R(Rmach))
+	MOVW CPSR, R0
+	CPS(CPSID)
+	RET
+
+TEXT splx(SB), $-4
+	MOVW R14, 4(R(Rmach))
+	MOVW R0, R1
+	MOVW CPSR, R0
+	MOVW R1, CPSR
+	RET
+
+TEXT islo(SB), $0
+	MOVW CPSR, R0
+	AND $(PsrDirq), R0
+	EOR $(PsrDirq), R0
+	RET
+
+TEXT setlabel(SB), $-4
+	MOVW R13, 0(R0)
+	MOVW R14, 4(R0)
+	MOVW $0, R0
+	RET
+
+TEXT gotolabel(SB), $-4
+	MOVW 0(R0), R13
+	MOVW 4(R0), R14
+	MOVW $1, R0
+	RET
+
+TEXT cas(SB), $0
+TEXT cmpswap(SB), $0
+	MOVW ov+4(FP), R1
+	MOVW nv+8(FP), R2
+spincas:
+	LDREX (R0), R3
+	CMP.S	R3, R1
+	BNE	fail
+	STREX	R2, (R0), R4
+	CMP.S	$0, R4
+	BNE	spincas
+	MOVW	$1, R0
+	DMB
+	RET
+fail:
+	CLREX
+	MOVW	$0, R0
+	RET
+
+TEXT perfticks(SB), $0
+	MRC 15, 0, R0, C(9), C(13), 0
+	RET
+
+TEXT cycles(SB), $0
+	MRC 15, 0, R1, C(9), C(13), 0
+	MOVW R1, (R0)
+	MOVW 24(R(Rmach)), R1
+	MRC 15, 0, R2, C(9), C(12), 3
+	AND.S $(1<<31), R2
+	BEQ _cycles0
+	MCR 15, 0, R2, C(9), C(12), 3
+	ADD $1, R1
+	MOVW R1, 24(R(Rmach))
+_cycles0:
+	MOVW R1, 4(R0)
+	RET
+
+TEXT fpinit(SB), $0
+	MOVW $(1<<30), R0
+	VMSR(0xe, 0, FPEXC)
+	MOVW $0, R0
+	VMSR(0xe, 0, FPSCR)
+	RET
+
+TEXT fprestore(SB), $0
+	MOVM.IA.W (R0), [R1-R2]
+	VMSR(0xe, 1, FPEXC)
+	VMSR(0xe, 2, FPSCR)
+	WORD $0xecb00b20
+	WORD $0xecf00b20
+	RET
+
+TEXT fpsave(SB), $0
+	VMRS(0xe, FPEXC, 1)
+	VMRS(0xe, FPSCR, 2)
+	MOVM.IA.W [R1-R2], (R0)
+	WORD $0xeca00b20
+	WORD $0xece00b20
+	/* wet floor */
+
+TEXT fpoff(SB), $0
+TEXT fpclear(SB), $0
+	MOVW $0, R1
+	VMSR(0xe, 1, FPEXC)
+	RET
+
+TEXT getifar(SB), $0
+	MRC 15, 0, R0, C(6), C(0), 2
+	RET
+
+TEXT getdfar(SB), $0
+	MRC 15, 0, R0, C(6), C(0), 0
+	RET
+
+TEXT getifsr(SB), $0
+	MRC 15, 0, R0, C(5), C(0), 1
+	RET
+
+TEXT getdfsr(SB), $0
+	MRC 15, 0, R0, C(5), C(0), 0
+	RET
+
+TEXT getexcvec(SB), $0
+	MRC 15, 0, R0, C(12), C(0), 0
+	RET
+
+#define Rnoway R1
+#define Rwayinc R2
+#define Rmaxway R3
+#define Rsetinc R4
+#define Rmaxset R5
+
+TEXT l1dclear(SB), $0
+	MOVW $0, R0
+	MCR 15, 2, R0, C(0), C(0), 0 /* select data cache */
+	MRC 15, 1, R9, C(0), C(0), 0 /* get cache size(s) */
+	AND $7, R9, R8
+	ADD $4, R8
+	MOVW $1, Rsetinc
+	MOVW Rsetinc<<R8, Rsetinc
+	MOVW R9>>13, Rmaxset
+	AND $0x7fff, Rmaxset
+	MOVW Rmaxset<<R8, Rmaxset
+
+	MOVW R9>>3, R0
+	AND $0x3ff, R0
+	MOVW $(1<<31), Rwayinc
+	MOVW $(1<<31), Rnoway
+	MOVW R0, Rmaxway
+	ADD $1, R0
+_l1dclear0:
+	MOVW.S R0>>1, R0
+	BEQ _l1dclear1
+	MOVW Rwayinc>>1, Rwayinc
+	MOVW Rnoway->1, Rnoway
+	MOVW Rmaxway@>1, Rmaxway
+	B _l1dclear0
+_l1dclear1:
+	MOVW Rwayinc<<1, Rwayinc
+	MVN Rnoway<<1, Rnoway
+	BIC Rnoway, Rmaxway
+
+	MOVW $0, R0
+_l1dclear2:
+	MCR 15, 0, R0, C(7), C(14), 2
+	ADD Rwayinc, R0
+	CMP.S Rmaxway, R0
+	BLT _l1dclear2
+	AND Rnoway, R0
+	ADD Rsetinc, R0
+	CMP.S Rmaxset, R0
+	BLT _l1dclear2
+
+	MOVW $2, R0
+_l1dclear3:
+	MCR 15, 0, R0, C(7), C(14), 2
+	ADD Rwayinc, R0
+	CMP.S Rmaxway, R0
+	BLT _l1dclear3
+	AND Rnoway, R0
+	ADD Rsetinc, R0
+	CMP.S Rmaxset, R0
+	BLT _l1dclear3
+	RET
+
+TEXT invalise(SB), $0
+	MOVW 4(FP), R1
+	ADD $(LINSIZ - 1), R1
+	BIC $(LINSIZ - 1), R0
+	BIC $(LINSIZ - 1), R1
+_invalise0:
+	MCR 15, 0, R0, C(7), C(5), 1
+	ADD $LINSIZ, R0
+	CMP.S R1, R0
+	BLT _invalise0	
+	RET
+
+TEXT cleandse(SB), $0
+	DSB
+	MOVW 4(FP), R1
+	ADD $(LINSIZ - 1), R1
+	BIC $(LINSIZ - 1), R0
+	BIC $(LINSIZ - 1), R1
+_cleandse0:
+	MCR 15, 0, R0, C(7), C(10), 1
+	ADD $LINSIZ, R0
+	CMP.U R1, R0
+	BLT _cleandse0
+	DSB
+	RET
+	
+TEXT invaldse(SB), $0
+	MOVW 4(FP), R1
+	ADD $(LINSIZ - 1), R1
+	BIC $(LINSIZ - 1), R0
+	BIC $(LINSIZ - 1), R1
+_invaldse0:
+	MCR 15, 0, R0, C(7), C(6), 1
+	ADD $LINSIZ, R0
+	CMP.S R1, R0
+	BLT _invaldse0
+	MCR 15, 0, R0, C(7), C(5), 0
+	DSB
+	RET
+
+TEXT va2pa(SB), $0
+	MCR 15, 0, R0, C(7), C(8), 1
+	DSB
+	MRC 15, 0, R0, C(7), C(4), 0
+	RET
--- /dev/null	Thu Dec 31 12:39:09 2020
+++ b/ltrap.s	Sat Nov 28 23:38:41 2020
@@ -0,0 +1,100 @@
+#include "mem.h"
+#include "io.h"
+
+TEXT vectors(SB), $-4
+	MOVW $_setup+(DRAM_BASE-KZERO)(SB), R15
+	MOVW $_vexc(SB), R15
+	MOVW $_vsvc(SB), R15
+	MOVW $_viabt(SB), R15
+	MOVW $_vexc(SB), R15
+	MOVW $vectors(SB), R15
+	MOVW $_vexc(SB), R15
+	MOVW $_vexc(SB), R15
+
+TEXT _viabt(SB), $-4
+	CPS(CPSID)
+	CLREX
+	DSB
+	MOVW R14, 8(R13)
+	MOVW SPSR, R14
+	MOVW R14, 4(R13)
+	MOVW CPSR, R14
+	AND $0x1e, R14
+	B _exc
+	
+
+TEXT _vexc(SB), $-4
+	CPS(CPSID)
+	CLREX
+	DSB
+	MOVW R14, 8(R13)
+	MOVW SPSR, R14
+	MOVW R14, 4(R13)
+	MOVW CPSR, R14
+	AND $0x1f, R14
+_exc:
+	MOVW R14, 0(R13)
+	CPS(CPSMODE | PsrMsvc)
+
+	SUB $(18*4), R13
+	MOVM.IA [R0-R14], (R13)
+
+	/* get Mach* from TPIDRPRW */
+	MRC 15, 0, R(Rmach), C(13), C(0), 4
+	MOVW 8(R(Rmach)), R(Rup)
+	MOVW $setR12(SB), R12
+
+	ADD $12, R(Rmach), R0
+	MOVM.IA (R0), [R1-R3]
+	ADD $(15*4), R13, R0
+	MOVM.IA [R1-R3], (R0)
+
+	AND.S $0xf, R2
+	ADD.NE $(18*4), R13, R0
+	MOVW.NE R0, (13*4)(R13)
+	ADD.EQ $(13*4), R13, R0
+	MOVM.IA.S.EQ [R13-R14], (R0)
+
+	MOVW R13, R0
+	SUB $8, R13
+	BL trap(SB)
+	ADD $8, R13
+
+	MOVW (16*4)(R13), R0
+	MOVW R0, SPSR
+	AND.S $0xf, R0
+	BEQ _uret
+	MOVW R(Rmach), (Rmach*4)(R13)
+	MOVM.IA (R13), [R0-R14]
+	DSB
+	MOVM.DB.S (R13), [R15]
+
+TEXT _vsvc(SB), $-4
+	CLREX
+	DSB
+	MOVW.W R14, -4(R13)
+	MOVW SPSR, R14
+	MOVW.W R14, -4(R13)
+	MOVW $PsrMsvc, R14
+	MOVW.W R14, -4(R13)
+	MOVM.DB.S [R0-R14], (R13)
+	SUB $(15*4), R13
+
+	/* get Mach* from TPIDRPRW */
+	MRC 15, 0, R(Rmach), C(13), C(0), 4
+	MOVW 8(R(Rmach)), R(Rup)
+	MOVW $setR12(SB), R12
+
+	MOVW R13, R0
+	SUB $8, R13
+	BL syscall(SB)
+	ADD $8, R13
+
+	MOVW (16*4)(R13), R0
+	MOVW R0, SPSR
+_uret:
+	MOVM.IA.S (R13), [R0-R14]
+	ADD $(17*4), R13
+	DSB
+	ISB
+	MOVM.IA.S.W (R13), [R15]
--- /dev/null	Thu Dec 31 12:39:09 2020
+++ b/main.c	Sat Nov 28 23:38:41 2020
@@ -0,0 +1,316 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "io.h"
+#include "dat.h"
+#include "fns.h"
+#include "init.h"
+#include "pool.h"
+#include "../port/error.h"
+#include "tos.h"
+
+Conf conf;
+uchar *sp;
+
+enum { MAXCONF = 64 };
+
+char *confname[MAXCONF], *confval[MAXCONF];
+int nconf;
+
+enum{
+	PRM_DEVICE = 0x0f00/4,
+		PRM_RSTCTRL = 0,
+			RST_GLOBAL_COLD_SW = 1<<1,
+			RST_GLOBAL_WARM_SW = 1<<0,
+};
+
+void
+exit(int)
+{
+	ulong *r;
+
+	cpushutdown();
+	r = &cprm[PRM_DEVICE];
+	r[PRM_RSTCTRL] |= RST_GLOBAL_COLD_SW;
+	for(;;) idlehands();
+}
+
+void
+reboot(void*, void*, ulong)
+{
+}
+
+void
+evenaddr(uintptr va)
+{
+	if((va & 3) != 0){
+		dumpstack();
+		postnote(up, 1, "sys: odd address", NDebug);
+		error(Ebadarg);
+	}
+}
+
+void
+procfork(Proc *p)
+{
+	ulong s;
+
+	p->kentry = up->kentry;
+	p->pcycles = -p->kentry;
+	
+	s = splhi();
+	switch(up->fpstate & ~FPillegal){
+	case FPactive:
+		fpsave(up->fpsave);
+		up->fpstate = FPinactive;
+	case FPinactive:
+		memmove(p->fpsave, up->fpsave, sizeof(FPsave));
+		p->fpstate = FPinactive;
+	}
+	splx(s);
+}
+
+void
+procsetup(Proc *p)
+{
+	p->fpstate = FPinit;
+	fpoff();
+	
+	cycles(&p->kentry);
+	p->pcycles = -p->kentry;
+}
+
+void
+kexit(Ureg *)
+{
+	Tos *tos;
+	uvlong t;
+
+	tos = (Tos*)(USTKTOP-sizeof(Tos));
+	cycles(&t);
+	tos->kcycles += t - up->kentry;
+	tos->pcycles = t + up->pcycles;
+	tos->pid = up->pid;
+}
+
+char*
+getconf(char *n)
+{
+	int i;
+
+	for(i = 0; i < nconf; i++)
+		if(cistrcmp(confname[i], n) == 0)
+			return confval[i];
+	return nil;
+}
+
+static void
+options(void)
+{
+	long i, n;
+	char *cp, *line[MAXCONF], *p, *q;
+
+	cp = (char *) CONFADDR;
+
+	p = cp;
+	for(q = cp; *q; q++){
+		if(*q == '\r')
+			continue;
+		if(*q == '\t')
+			*q = ' ';
+		*p++ = *q;
+	}
+	*p = 0;
+
+	n = getfields(cp, line, MAXCONF, 1, "\n");
+	for(i = 0; i < n; i++){
+		if(*line[i] == '#')
+			continue;
+		cp = strchr(line[i], '=');
+		if(cp == nil)
+			continue;
+		*cp++ = '\0';
+		confname[nconf] = line[i];
+		confval[nconf] = cp;
+		nconf++;
+	}
+}
+
+void
+confinit(void)
+{
+	ulong kmem;
+	int i;
+
+	conf.nmach = 1;
+	conf.nproc = 2000;
+	conf.ialloc = 16*1024*1024;
+	conf.nimage = 200;
+	conf.mem[0].base = PGROUND((ulong)end - KZERO + DRAM_BASE);
+	conf.mem[0].limit = DRAM_TOP;
+	conf.npage = 0;
+	for(i = 0; i < nelem(conf.mem); i++)
+		conf.npage += conf.mem[i].npage = (conf.mem[i].limit - conf.mem[i].base) >> PGSHIFT;
+	kmem = 200*1024*1024;
+	conf.upages = conf.npage - kmem/BY2PG;
+	kmem -= conf.upages*sizeof(Page)
+		+ conf.nproc*sizeof(Proc)
+		+ conf.nimage*sizeof(Image);
+	mainmem->maxsize = kmem;
+	imagmem->maxsize = kmem - (kmem/10);
+}
+
+void
+cpuidprint(void)
+{
+	print("cpu%d: %dMHz ARM Cortex-A8\n", m->machno, m->cpumhz);
+}
+
+static uchar *
+pusharg(char *p)
+{
+	int n;
+	
+	n = strlen(p) + 1;
+	sp -= n;
+	memmove(sp, p, n);
+	return sp;
+}
+
+static void
+bootargs(void *base)
+{
+	int i, ac;
+	uchar *av[32];
+	uchar **lsp;
+	
+	sp = (uchar *) base + BY2PG - sizeof(Tos);
+	
+	ac = 0;
+	av[ac++] = pusharg("boot");
+	sp = (uchar *) ((ulong) sp & ~3);
+	sp -= (ac + 1) * sizeof(sp);
+	lsp = (uchar **) sp;
+	for(i = 0; i < ac; i++)
+		lsp[i] = av[i] + ((USTKTOP - BY2PG) - (ulong) base);
+	lsp[i] = 0;
+	sp += (USTKTOP - BY2PG) - (ulong) base;
+	sp -= BY2WD;
+}
+
+static void
+init0(void)
+{
+	char buf[ERRMAX];
+	int i;
+
+	up->nerrlab = 0;
+	spllo();
+	up->slash = namec("#/", Atodir, 0, 0);
+	pathclose(up->slash->path);
+	up->slash->path = newpath("/");
+	up->dot = cclone(up->slash);
+	chandevinit();
+	uartconsole();
+
+	if(!waserror()){
+		ksetenv("cputype", "arm", 0);
+		if(cpuserver)
+			ksetenv("service", "cpu", 0);
+		else
+			ksetenv("service", "terminal", 0);
+		ksetenv("console", "0", 0);
+		snprint(buf, sizeof(buf), "am335x %s", conffile);
+		ksetenv("terminal", buf, 0);
+		for(i = 0; i < nconf; i++){
+			if(*confname[i] != '*')
+				ksetenv(confname[i], confval[i], 0);
+			ksetenv(confname[i], confval[i], 1);
+		}
+		poperror();
+	}
+	kproc("alarm", alarmkproc, 0);
+	touser(sp);
+}
+
+void
+userinit(void)
+{
+	Proc *p;
+	Segment *s;
+	void *v;
+	Page *pg;
+	
+	p = newproc();
+	p->pgrp = newpgrp();
+	p->egrp = smalloc(sizeof(Egrp));
+	p->egrp->ref = 1;
+	p->fgrp = dupfgrp(nil);
+	p->rgrp = newrgrp();
+	p->procmode = 0640;
+	
+	kstrdup(&eve, "");
+	kstrdup(&p->text, "*init*");
+	kstrdup(&p->user, eve);
+	
+	procsetup(p);
+	
+	p->sched.pc = (ulong)init0;
+	p->sched.sp = (ulong)p->kstack + KSTACK - (sizeof(Sargs) + BY2WD);
+	
+	s = newseg(SG_STACK, USTKTOP - USTKSIZE, USTKSIZE / BY2PG);
+	p->seg[SSEG] = s;
+	pg = newpage(0, 0, USTKTOP - BY2PG);
+	v = tmpmap(pg->pa);
+	memset(v, 0, BY2PG);
+	segpage(s, pg);
+	bootargs(v);
+	tmpunmap(v);
+
+	s = newseg(SG_TEXT, UTZERO, 1);
+	s->flushme++;
+	p->seg[TSEG] = s;
+	pg = newpage(0, 0, UTZERO);
+	pg->txtflush = ~0;
+
+	segpage(s, pg);
+	v = tmpmap(pg->pa);
+	memset(v, 0, BY2PG);
+	memmove(v, initcode, sizeof(initcode));
+	tmpunmap(v);
+
+	ready(p);
+}
+
+void
+main(void)
+{
+	active.machs[m->machno] = 1;
+	uartinit();
+	mmuinit();
+	intrinit();
+	options();
+	confinit();
+	timerinit();
+	uartputs(" from Bell Labs\n", 16);
+	printinit();
+	xinit();
+	quotefmtinstall();
+	cpuidprint();
+	todinit();
+	timersinit();
+	procinit0();
+	initseg();
+	links();
+	chandevreset();
+	pageinit();
+	userinit();
+	schedinit();
+}
+
+void
+setupwatchpts(Proc*, Watchpt*, int n)
+{
+	if(n > 0)
+		error("no watchpoints");
+}
--- /dev/null	Thu Dec 31 12:39:09 2020
+++ b/mem.h	Sat Nov 28 23:38:41 2020
@@ -0,0 +1,127 @@
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+
+#define MIN(a, b)	((a) < (b)? (a): (b))
+#define MAX(a, b)	((a) > (b)? (a): (b))
+
+/*
+ * Sizes
+ */
+#define	BI2BY		8			/* bits per byte */
+#define	BI2WD		32			/* bits per word */
+#define	BY2WD		4			/* bytes per word */
+#define	BY2V		8			/* bytes per double word */
+#define	BY2PG		4096			/* bytes per page */
+#define	WD2PG		(BY2PG/BY2WD)		/* words per page */
+#define	PGSHIFT		12			/* log(BY2PG) */
+#define	ROUND(s, sz)	(((s)+((sz)-1))&~((sz)-1))
+#define	PGROUND(s)	ROUND(s, BY2PG)
+#define LINSIZ		32
+#define	BLOCKALIGN	LINSIZ
+#define	FPalign		16
+
+#define MAXMACH 1
+#define KSTACK 4096
+
+#define HZ (1000)
+#define MS2HZ (1000/HZ)
+#define TK2SEC(t) ((t)/HZ)
+
+#define KZERO 0x80000000
+#define KTOP  0xA0000000
+#define KTZERO (KZERO+0x80000)
+#define VMAPSZ (SECSZ * 4)
+#define VMAP (KZERO - VMAPSZ)
+#define TMAPSZ SECSZ
+#define TMAP (VMAP - TMAPSZ)
+#define KMAPSZ SECSZ
+#define KMAP (TMAP - KMAPSZ)
+#define NKMAP (KMAPSZ / BY2PG - 1)
+#define MACHSIZE 8192
+#define MACH(n) (KZERO+(n)*MACHSIZE)
+#define MACHP(n) ((Mach *)MACH(n))
+#define MACHL1(n) (ROUND(MACH(MAXMACH), L1SZ) + (n)*L1SZ)
+#define VMAPL2 MACHL1(MAXMACH)
+#define VMAPL2SZ (L2SZ * (VMAPSZ / SECSZ))
+#define TMAPL2(n) (VMAPL2 + VMAPL2SZ + (n) * L2SZ)
+#define TMAPL2SZ (MAXMACH * L2SZ)
+#define CONFSIZE 65536
+#define CONFADDR (KTZERO-CONFSIZE)
+
+#define UZERO 0
+#define UTZERO BY2PG
+#define UTROUND(t) ROUNDUP(t, BY2PG)
+#define USTKTOP 0xE0000000
+#define USTKSIZE (16*1024*1024)
+
+#define PTEMAPMEM (1024*1024)
+#define PTEPERTAB (PTEMAPMEM/BY2PG)
+#define SEGMAPSIZE 1984
+#define SSEGMAPSIZE 16
+
+#define PTEVALID L2VALID
+#define PTERONLY L2RONLY
+#define PTEWRITE L2WRITE
+#define PTEUNCACHED L2DEVICE
+#define PPN(x) ((x)&~(BY2PG-1))
+
+#define PsrDirq (1<<7)
+#define PsrDfiq (1<<6)
+#define PsrMask 0x1f
+#define PsrMusr 0x10
+#define PsrMfiq 0x11
+#define PsrMirq 0x12
+#define PsrMsvc 0x13
+#define PsrMabt 0x17
+#define PsrMiabt 0x16 /* not an actual mode; for ureg->type */
+#define PsrMund 0x1b
+
+#define DMB WORD $0xf57ff05f
+#define DSB WORD $0xf57ff04f
+#define ISB WORD $0xf57ff06f
+#define WFE WORD $0xe320f002
+#define SEV WORD $0xe320f004
+#define CPS(m) WORD $(0xf1000000|(m))
+#define CPSMODE (1<<17)
+#define CPSIE (3<<6|2<<18)
+#define CPSID (3<<6|3<<18)
+#define Rmach 10
+#define Rup 9
+
+#define VMSR(c, r1, r2) WORD $(0x0ee00a10|(c)<<28|(r2)<<16|(r1)<<12)
+#define VMRS(c, r1, r2) WORD $(0x0ef00a10|(c)<<28|(r2)<<12|(r1)<<16)
+#define FPSID 0x0
+#define FPSCR 0x1
+#define MVFR1 0x6
+#define MVFR0 0x7
+#define FPEXC 0x8
+
+#define L1PT 1
+#define L1SEC (1<<10|1<<1)
+//#define L1NORMAL (1<<14|1<<13|1<<12|1<<3|1<<2)
+#define L1NORMAL (1<<14|1<<13|1<<3)
+//#define L1NORMAL (1<<14|1<<13|1<<12|1<<3|1<<2)
+#define L1KERRW 0
+#define L1SZ (4096*4)
+#define L2SZ (256*4)
+#define SECSZ 1048576
+#define SECSH 20
+#define NL2 256
+
+#define L1X(va) (((ulong)(va)) >> 20)
+#define L1RX(va) (((ulong)(va)) >> 20 & ~3)
+#define L2X(va) (((ulong)(va)) >> 12 & 0xff)
+#define L2RX(va) (((ulong)(va)) >> 12 & 0x3ff)
+
+#define L2VALID (1<<4|1<<1)
+#define L2CACHED (1<<8|1<<6|1<<2)
+#define L2DEVICE (1<<0)
+#define L2KERRW L2KERNEL
+#define L2KERNEL 0
+#define L2USER (1<<5)
+#define L2RONLY (1<<9)
+#define L2WRITE 0
+#define L2LOCAL (1<<11)
+
+#define TTBATTR (1<<4|1<<1|1<<0)
--- /dev/null	Thu Dec 31 12:39:09 2020
+++ b/mkfile	Sat Nov 28 23:38:41 2020
@@ -0,0 +1,82 @@
+CONF=bbb
+CONFLIST=bbb
+
+#must match mem.h
+KTZERO=0x80080000
+
+objtype=arm
+</$objtype/mkfile
+p=9
+
+DEVS=`{rc ../port/mkdevlist $CONF}
+
+PORT=\
+	alarm.$O\
+	alloc.$O\
+	allocb.$O\
+	auth.$O\
+	cache.$O\
+	chan.$O\
+	dev.$O\
+	edf.$O\
+	fault.$O\
+	mul64fract.$O\
+	page.$O\
+	parse.$O\
+	pgrp.$O\
+	portclock.$O\
+	print.$O\
+	proc.$O\
+	qio.$O\
+	qlock.$O\
+	random.$O\
+	rdb.$O\
+	rebootcmd.$O\
+	segment.$O\
+	syscallfmt.$O\
+	sysfile.$O\
+	sysproc.$O\
+	taslock.$O\
+	tod.$O\
+	xalloc.$O\
+
+OBJ=\
+	ltrap.$O\
+	l.$O\
+	intr.$O\
+	main.$O\
+	mmu.$O\
+	timer.$O\
+	trap.$O\
+	reloc.$O\
+	$CONF.root.$O\
+	$CONF.rootc.$O\
+	$DEVS\
+	$PORT\
+
+LIB=\
+	/$objtype/lib/libip.a\
+	/$objtype/lib/libsec.a\
+	/$objtype/lib/libc.a\
+
+$p$CONF: $CONF.c $OBJ $LIB mkfile
+	$CC $CFLAGS '-DKERNDATE='`{date -n} $CONF.c
+	$LD -o $target -E _start -T$KTZERO -l $OBJ $CONF.$O $LIB
+
+<../boot/bootmkfile
+<../port/portmkfile
+<|../port/mkbootrules $CONF
+
+init.h:D: ../port/initcode.c init9.s
+	$CC ../port/initcode.c
+	$AS init9.s
+	$LD -l -R1 -s -o init.out init9.$O initcode.$O /arm/lib/libc.a
+	{echo 'uchar initcode[]={'
+	 xd -1x <init.out |
+		sed -e 's/^[0-9a-f]+ //' -e 's/ ([0-9a-f][0-9a-f])/0x\1,/g'
+	 echo '};'} > init.h
+
+install:V:	$p$CONF
+	cp $p$CONF /$objtype/
+	for(i in $EXTRACOPIES)
+		import $i / /n/$i && cp $p$CONF $p$CONF.gz /n/$i/$objtype/
--- /dev/null	Thu Dec 31 12:39:09 2020
+++ b/mmu.c	Sat Nov 28 23:38:41 2020
@@ -0,0 +1,470 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+ulong *intcps;
+ulong *cprm;
+ulong *clocks;
+ulong *timer2;
+ulong *timer3;
+ulong *gpio[4];
+uchar *sram;
+
+void
+mmuinit(void)
+{
+	ulong *e;
+
+	m->l1.pa = ttbget();
+	m->l1.va = KADDR(m->l1.pa);
+	memset((uchar*)TMAPL2(m->machno), 0, TMAPL2SZ);
+	e = &m->l1.va[L1X(TMAP)];
+	*e = PADDR(TMAPL2(m->machno)) | L1PT;
+	cleandse(e, e);
+	incref(&m->l1);
+	if(intcps != nil)
+		return;
+	intcps = vmap(INTCPS_BASE, 4096);
+	timer2 = vmap(TIMER2_BASE, 4096);
+	timer3 = vmap(TIMER3_BASE, 4096);
+
+	cprm = vmap(CPRM_BASE, 4096);
+
+	gpio[0] = vmap(GPIO0_BASE, 4096);
+	gpio[1] = vmap(GPIO1_BASE, 4096);
+	gpio[2] = vmap(GPIO2_BASE, 4096);
+	gpio[3] = vmap(GPIO3_BASE, 4096);
+
+	sram = vmap(SRAM_BASE, SRAM_SIZE);
+}
+
+void
+l1switch(L1 *p, int flush)
+{
+	assert(!islo());
+
+	ttbput(p->pa);
+	if(flush){
+		if(++m->asid == 0)
+			flushtlb();
+		setasid(m->asid);
+	}
+}
+
+static L1 *
+l1alloc(void)
+{
+	L1 *p;
+	int s;
+
+	s = splhi();
+	p = m->l1free;
+	if(p != nil){
+		m->l1free = p->next;
+		p->next = nil;
+		m->nfree--;
+		splx(s);
+		return p;
+	}
+	splx(s);
+	p = smalloc(sizeof(L1));
+	for(;;){
+		p->va = mallocalign(L1SZ, L1SZ, 0, 0);
+		if(p->va != nil)
+			break;
+		if(!waserror()){
+			resrcwait("no memory for L1 table");
+			poperror();
+		}
+	}
+	p->pa = PADDR(p->va);
+	memmove(p->va, m->l1.va, L1SZ);
+	return p;
+}
+
+static void
+l1free(L1 *l1)
+{
+	if(islo())
+		panic("l1free: islo");
+	if(m->nfree >= 40){
+		free(l1->va);
+		free(l1);
+	}else{
+		l1->next = m->l1free;
+		m->l1free = l1;
+		m->nfree++;
+	}
+}
+
+static void
+upallocl1(void)
+{
+	L1 *p;
+	int s;
+
+	if(up->l1 != nil)
+		return;
+	p = l1alloc();
+	s = splhi();
+	if(up->l1 != nil)
+		panic("upalloc1: up->l1 != nil");
+	up->l1 = p;
+	l1switch(p, 1);
+	splx(s);
+}
+
+static void
+l2free(Proc *proc)
+{
+	ulong *t;
+	Page *p, **l;
+
+	if(proc->l1 == nil || proc->mmuused == nil)
+		return;
+	l = &proc->mmuused;
+	for(p = *l; p != nil; p = p->next){
+		t = proc->l1->va + p->daddr;
+		*t++ = 0;
+		*t++ = 0;
+		*t++ = 0;
+		*t = 0;
+		l = &p->next;
+	}
+	proc->l1->va[L1X(TMAP)] = 0;
+	*l = proc->mmufree;
+	proc->mmufree = proc->mmuused;
+	proc->mmuused = 0;
+}
+
+void
+mmuswitch(Proc *p)
+{
+	if(p->newtlb){
+		p->newtlb = 0;
+		l2free(p);
+	}
+	if(p->l1 != nil)
+		l1switch(p->l1, 1);
+	else
+		l1switch(&m->l1, 1);
+}
+
+void
+putmmu(uintptr va, uintptr pa, Page *pg)
+{
+	Page *p;
+	ulong *e;
+	ulong *l2;
+	ulong old;
+	uintptr l2p;
+	int s;
+
+	if(up->l1 == nil)
+		upallocl1();
+	if((pa & PTEUNCACHED) == 0)
+		pa |= L2CACHED;
+	e = &up->l1->va[L1RX(va)];
+	if((*e & 3) == 0){
+		p = up->mmufree;
+		if(p != nil)
+			up->mmufree = p->next;
+		else
+			p = newpage(0, 0, 0);
+		p->daddr = L1RX(va);
+		p->next = up->mmuused;
+		up->mmuused = p;
+		s = splhi();
+		l2p = p->pa;
+		l2 = tmpmap(l2p);
+		memset(l2, 0, BY2PG);
+		coherence();
+		e[0] = p->pa | L1PT;
+		e[1] = e[0] + L2SZ;
+		e[2] = e[1] + L2SZ;
+		e[3] = e[2] + L2SZ;
+		cleandse(e, e+3);
+		coherence();
+	}else{
+		s = splhi();
+		l2p = *e & ~(BY2PG - 1);
+		l2 = tmpmap(l2p);
+	}
+	e = &l2[L2RX(va)];
+	old = *e;
+	*e = pa | L2VALID | L2USER | L2LOCAL;
+	cleandse(e, e);
+	cleandse(l2, l2+L2SZ);
+	tmpunmap(l2);
+	splx(s);
+	if((old & L2VALID) != 0){
+		flushpg((void *) va);
+	}
+	if(pg->txtflush){
+		cleandse((void *) va, (void *) (va + BY2PG));
+		invalise((void *) va, (void *) (va + BY2PG));
+		pg->txtflush = 0;
+	}
+}
+
+void
+checkmmu(uintptr, uintptr)
+{
+}
+
+void
+flushmmu(void)
+{
+	int s;
+
+	s = splhi();
+	up->newtlb = 1;
+	mmuswitch(up);
+	splx(s);
+}
+
+void
+mmurelease(Proc *proc)
+{
+	Page *p, *n;
+
+	if(islo())
+		panic("mmurelease: islo");
+	
+	l1switch(&m->l1, 0);
+	if(proc->kmaptable != nil){
+		if(proc->l1 == nil)
+			panic("mmurelease: no l1");
+		if(decref(proc->kmaptable) != 0)
+			panic("mmurelease: kmap ref %ld", proc->kmaptable->ref);
+		if(proc->nkmap)
+			panic("mmurelease: nkmap %d", proc->nkmap);
+		if(PPN(proc->l1->va[L1X(KMAP)]) != proc->kmaptable->pa)
+			panic("mmurelease: bad kmap l2 %#.8lux kmap %#.8lux", proc->l1->va[L1X(KMAP)], proc->kmaptable->pa);
+		proc->l1->va[L1X(KMAP)] = 0;
+		pagechainhead(proc->kmaptable);
+		proc->kmaptable = nil;
+	}
+	if(proc->l1 != nil){
+		l2free(proc);
+		l1free(proc->l1);
+		proc->l1 = nil;
+	}
+	for(p = proc->mmufree; p != nil; p = n){
+		n = p->next;
+		if(decref(p) != 0)
+			panic("mmurelease: p->ref %ld", p->ref);
+		pagechainhead(p);
+	}
+	if(proc->mmufree != nil)
+		pagechaindone();
+	proc->mmufree = nil;
+}
+
+void
+countpagerefs(ulong *, int)
+{
+	print("countpagerefs\n");
+}
+
+uintptr
+paddr(void *v)
+{
+	if((uintptr)v >= KZERO)
+		return (uintptr)v-KZERO+DRAM_BASE;
+	if((uintptr)v >= VMAP)
+		return ((uintptr)v & (BY2PG-1)) | PPN(((ulong*)VMAPL2)[(uintptr)v-VMAP >> PGSHIFT]);
+	panic("paddr: va=%#p pc=%#p", v, getcallerpc(&v));
+	return 0;
+}
+
+void *
+kaddr(uintptr u)
+{
+	if(u >= (uintptr)DRAM_BASE && u < (uintptr)DRAM_TOP){
+		if(u - DRAM_BASE + KZERO < KTOP)
+			return (void *)(u - DRAM_BASE + KZERO);
+	}
+	if(u >= (uintptr)SRAM_BASE && u < (uintptr)SRAM_BASE + SRAM_SIZE)
+		return (void *)(u - SRAM_BASE + sram);
+	panic("kaddr: pa=%#p pc=%#p", u, getcallerpc(&u));
+	return nil;
+}
+
+uintptr
+cankaddr(uintptr u)
+{
+	if((u-(uintptr)DRAM_BASE) < (uintptr)(KTOP-KZERO))
+		return (KTOP-KZERO) - (u-DRAM_BASE);
+	return 0;
+}
+
+KMap *
+kmap(Page *page)
+{
+	ulong *e, *v;
+	int i, s;
+
+	if(cankaddr(page->pa))
+		return (KMap*)KADDR(page->pa);
+	if(up == nil)
+		panic("kmap: up=0 pc=%#.8lux", getcallerpc(&page));
+	if(up->l1 == nil)
+		upallocl1();
+	if(up->nkmap < 0)
+		panic("kmap %lud %s: nkmap=%d", up->pid, up->text, up->nkmap);
+	up->nkmap++;
+	e = &up->l1->va[L1X(KMAP)];
+	if((*e & 3) == 0){
+		if(up->kmaptable != nil)
+			panic("kmaptable != nil");
+		up->kmaptable = newpage(0, 0, 0);
+		s = splhi();
+		v = tmpmap(up->kmaptable->pa);
+		memset(v, 0, BY2PG);
+		v[0] = page->pa | L2KERRW | L2VALID | L2CACHED | L2LOCAL;
+		v[NKMAP] = up->kmaptable->pa | L2KERRW | L2VALID | L2CACHED | L2LOCAL;
+		cleandse(v, v+NKMAP);
+		tmpunmap(v);
+		splx(s);
+		*e = up->kmaptable->pa | L1PT;
+		cleandse(e, e);
+		coherence();
+		return (KMap *) KMAP;
+	}
+	if(up->kmaptable == nil)
+		panic("kmaptable == nil");
+	e = (ulong *) (KMAP + NKMAP * BY2PG);
+	for(i = 0; i < NKMAP; i++)
+		if((e[i] & 3) == 0){
+			e[i] = page->pa | L2KERRW | L2VALID | L2CACHED | L2LOCAL;
+			cleandse(&e[i], &e[i]);
+			coherence();
+			return (KMap *) (KMAP + i * BY2PG);
+		}
+	panic("out of kmap");
+	return nil;
+}
+
+void
+kunmap(KMap *arg)
+{
+	uintptr va;
+	ulong *e;
+	
+	va = (uintptr) arg;
+	if(va >= KZERO)
+		return;
+	if(up->l1 == nil || (up->l1->va[L1X(KMAP)] & 3) == 0)
+		panic("kunmap: no kmaps");
+	if(va < KMAP || va >= KMAP + NKMAP * BY2PG)
+		panic("kunmap: bad address %#.8lux pc=%#p", va, getcallerpc(&arg));
+	e = (ulong *) (KMAP + NKMAP * BY2PG) + L2X(va);
+	if((*e & 3) == 0)
+		panic("kunmap: not mapped %#.8lux pc=%#p", va, getcallerpc(&arg));
+	up->nkmap--;
+	if(up->nkmap < 0)
+		panic("kunmap %lud %s: nkmap=%d", up->pid, up->text, up->nkmap);
+	*e = 0;
+	cleandse(e, e);
+	coherence();
+	flushpg((void *) va);
+}
+
+void *
+tmpmap(ulong pa)
+{
+	ulong *u, *ub, *ue;
+
+	if(islo())
+		panic("tmpmap: islow %#p", getcallerpc(&pa));
+	if(cankaddr(pa))
+		return KADDR(pa);
+	ub = (ulong *) TMAPL2(m->machno);
+	ue = ub + NL2;
+	for(u = ub; u < ue; u++)
+		if((*u & 3) == 0){
+			*u = pa | L2VALID | L2CACHED | L2KERRW;
+			cleandse(u, u);
+			assert(m->l1.va[L1X(TMAP)] != 0);
+			if(up != nil && up->l1 != nil)
+				up->l1->va[L1X(TMAP)] = m->l1.va[L1X(TMAP)];
+
+			coherence();
+			return (void *) ((u - ub) * BY2PG + TMAP);
+		}
+	panic("tmpmap: full (pa=%#.8lux)", pa);
+	return nil;
+}
+
+void
+tmpunmap(void *v)
+{
+	ulong *u;
+
+	if(v >= (void*) KZERO)
+		return;
+	if(v < (void*)TMAP || v >= (void*)(TMAP + TMAPSZ))
+		panic("tmpunmap: invalid address (va=%#.8lux)", (uintptr) v);
+	u = (ulong *) TMAPL2(m->machno) + L2X(v);
+	if((*u & 3) == 0)
+		panic("tmpunmap: double unmap (va=%#.8lux)", (uintptr) v);
+	*u = 0;
+	cleandse(u, u);
+	coherence();
+	flushpg(v);
+}
+
+void *
+vmap(uintptr pa, ulong sz)
+{
+	ulong np;
+	void *vr, *ve;
+	static ulong *vp = (ulong *) VMAPL2 + 1; /* first page is uart */
+	
+	if((pa & BY2PG - 1) != 0)
+		panic("vmap: misaligned pa=%#.8lux", pa);
+	np = (sz + BY2PG - 1) >> PGSHIFT;
+	vr = (char*) VMAP + (vp - (ulong *)VMAPL2 << PGSHIFT);
+	ve = (ulong *) (VMAPL2 + VMAPL2SZ);
+	while(np-- != 0){
+		if(vp == ve)
+			panic("vmap: out of vmap space (pa=%#.8lux)", pa);
+		*vp = pa | L2VALID | L2DEVICE | L2KERRW;
+		cleandse(vp, vp);
+		vp++;
+		pa += BY2PG;
+	}
+
+	coherence();
+	return vr;
+}
+
+/* nasty things happen when there are cache entries for uncached memory
+   so must make sure memory is not mapped ANYWHERE cached */
+void*
+ucalloc(ulong len)
+{
+	static uchar *free = nil;
+	static Lock l;
+	uchar *va;
+
+	if(len == 0)
+		panic("ualloc: len == 0");
+
+	ilock(&l);
+	if(free == nil)
+		free = sram + SRAM_SIZE - BY2PG;
+	len = PGROUND(len);
+	free -= len;
+	if(free < sram)
+		panic("ualloc: out of uncached memory");
+	va = free;
+	iunlock(&l);
+
+	invaldse(va, va + len);
+	return (void*)va;
+}
--- /dev/null	Thu Dec 31 12:39:09 2020
+++ b/reloc.s	Sat Nov 28 23:38:41 2020
@@ -0,0 +1,104 @@
+#include "mem.h"
+#include "io.h"
+
+#define WDT_BASE 0x44E35000
+#define WDT_WWPS (WDT_BASE+0x34)
+#define WDT_WSPR (WDT_BASE+0x48)
+#define LOAD_ADDR (KTZERO-0x20)
+
+TEXT _setup(SB), $-4
+	// disable wdt
+	MOVW $WDT_WSPR, R0
+	MOVW $0xAAAA, R1
+	MOVW R1, (R0)
+
+	MOVW $WDT_WWPS, R0
+_wdtpoll1:
+	MOVW (R0), R1
+	AND $0x10, R1
+	CMP $0, R1
+	BNE _wdtpoll1
+
+	MOVW $WDT_WSPR, R0
+	MOVW $0x5555, R1
+	MOVW R1, (R0)
+
+	MOVW $WDT_WWPS, R0
+_wdtpoll2:
+	MOVW (R0), R1
+	AND $0x10, R1
+	CMP $0, R1
+	BNE _wdtpoll2
+
+	// turn off mmu and caches
+
+	MRC 15, 0, R0, C(1), C(0), 0
+	AND $0x8fffeff8, R0
+	//ORR $0x20000000, R0
+	BIC $(1<<12), R0 // disable icache
+	BIC $(1<<2), R0 // disable dcache
+	MCR 15, 0, R0, C(1), C(0), 0
+
+	BL l1dclear(SB)
+
+	// relocate initialised data segment
+	// R1: s1, R2: s2, R3: e1, R4: e2
+	MOVW $(LOAD_ADDR+1*4), R0
+	MOVBU.P 1(R0), R6
+	MOVW R6<<24, R1
+	MOVBU.P 1(R0), R6
+	ORR R6<<16, R1
+	MOVBU.P 1(R0), R6
+	ORR R6<<8, R1
+	MOVBU.P 1(R0), R6
+	ORR R6, R1
+	ADD $(KTZERO), R1
+	ADD $0xfff, R1, R2
+	AND $~0xfff, R2
+	MOVW $(LOAD_ADDR+2*4), R0
+	MOVBU.P 1(R0), R6
+	MOVW R6<<24, R3
+	MOVBU.P 1(R0), R6
+	ORR R6<<16, R3
+	MOVBU.P 1(R0), R6
+	ORR R6<<8, R3
+	MOVBU.P 1(R0), R6
+	ORR R6, R3
+	ADD R2, R3, R4
+	MOVW R4, R5
+	ADD R1, R3
+_movedata:
+	MOVW.P -4(R3), R0
+	MOVW.P R0, -4(R4)
+	CMP.S R1, R3
+	BNE _movedata
+
+	// clear bss
+	MOVW $(LOAD_ADDR+3*4), R0
+	MOVBU.P 1(R0), R6
+	MOVW R6<<24, R1
+	MOVBU.P 1(R0), R6
+	ORR R6<<16, R1
+	MOVBU.P 1(R0), R6
+	ORR R6<<8, R1
+	MOVBU.P 1(R0), R6
+	ORR R6, R1
+	ADD R1, R5, R2
+	ADD $0x1000, R2
+	MOVW $0, R0
+_clrbss:
+	MOVW.P R0, 4(R5)
+	CMP.S R5, R2
+	BGE _clrbss
+
+	// jump to start of kernel
+	MOVW $(LOAD_ADDR+5*4), R0
+	MOVBU.P 1(R0), R6
+	MOVW R6<<24, R1
+	MOVBU.P 1(R0), R6
+	ORR R6<<16, R1
+	MOVBU.P 1(R0), R6
+	ORR R6<<8, R1
+	MOVBU.P 1(R0), R6
+	ORR R6, R1
+	B (R1)
--- /dev/null	Thu Dec 31 12:39:09 2020
+++ b/timer.c	Sat Nov 28 23:38:41 2020
@@ -0,0 +1,165 @@
+#include <u.h>
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+enum
+{
+	TIOCP_CFG = 0x10/4,
+		SOFTRESET = 1<<0,
+
+	IRQSTATUS_RAW = 0x24/4,
+	IRQSTATUS = 0x28/4,
+		MAT_IT_FLAG = 1<<0,
+		OVF_IT_FLAG = 1<<1,
+		TCAR_IT_FLAG = 1<<2,
+
+	IRQENABLE_SET = 0x2c/4,
+	IRQENABLE_CLR = 0x30/4,
+		MAT_EN_FLAG = 1<<0,
+		OVF_EN_FLAG = 1<<1,
+		TCAR_EN_FLAG = 1<<2,
+
+	TCLR = 0x38/4,
+		ST = 1<<0,
+		AR = 1<<1,
+		PTV_SHIFT = 2,
+		PTV_MASK = 0x07<<PTV_SHIFT,
+		PRE = 1<<5,
+		CE = 1<<6,
+
+	TCRR = 0x3c/4,
+	TLDR = 0x40/4,
+	TWPS = 0x48/4,
+		W_PEND_TCLR = 1<<0,
+		W_PEND_TCRR = 1<<1,
+		W_PEND_TLDR = 1<<2,
+		W_PEND_TMAR = 1<<4,
+
+	TMAR = 0x4c/4,
+};
+
+enum
+{
+	CM_PER = 0,
+		CM_PER_L4LS_CLKSTCTRL = 0,
+			CLKACTIVITY_TIMER2_GCLK = 1<<14,
+			CLKACTIVITY_TIMER3_GCLK = 1<<15,
+		CM_PER_TIMER2_CLKCTRL = 0x80/4,
+		CM_PER_TIMER3_CLKCTRL = 0x84/4,
+
+	CM_DPLL = 0x500/4,
+		CLKSEL_TIMER2_CLK = 0x8/4,
+		CLKSEL_TIMER3_CLK = 0xc/4,
+			CLK_M_OSC = 0x1,
+};
+
+uvlong timerhz;
+
+ulong
+µs(void)
+{
+	return fastticks2us(fastticks(nil));
+}
+
+void
+microdelay(int n)
+{
+	ulong now;
+
+	now = µs();
+	while(µs() - now < n);
+}
+
+void
+delay(int n)
+{
+	while(--n >= 0)
+		microdelay(1000);
+}
+
+ulong currhi;
+
+uvlong
+fastticks(uvlong *hz)
+{
+	int x;
+	uvlong r;
+
+	if(hz)
+		*hz = timerhz;
+	x = splhi();
+	r = (uvlong)currhi<<32 | timer3[TCRR];
+	splx(x);
+	return r;
+}
+
+void
+timerset(Tval next)
+{
+	int x;
+	vlong period;
+	uvlong ft;
+
+	if(next == 0)
+		return;
+	x = splhi();
+	ft = fastticks(nil);
+	period = next - ft;
+	if(period > 0xffffffffLL)
+		period = 0xffffffffLL;
+	else if(period < 1)
+		period = 1;
+	timer2[TCRR] = -period;
+	timer2[TCLR] |= ST;
+	splx(x);
+}
+
+void
+timer2irq(Ureg *u, void*)
+{
+	timer2[IRQSTATUS] = OVF_IT_FLAG;
+	while((timer2[IRQSTATUS] & OVF_IT_FLAG) != 0);
+	timerintr(u, 0);
+}
+
+void
+timer3irq(Ureg*, void*)
+{
+	currhi++;
+	timer3[IRQSTATUS] = OVF_IT_FLAG;
+	while((timer3[IRQSTATUS] & OVF_IT_FLAG) != 0);
+}
+
+void
+timerinit(void)
+{
+	ulong *dpll, *per;
+
+	m->cpumhz = 1000;
+	m->cpuhz = m->cpumhz * 1000000;
+	timerhz = 24000000;
+
+	dpll = &cprm[CM_DPLL];
+	dpll[CLKSEL_TIMER2_CLK] |= CLK_M_OSC;
+	dpll[CLKSEL_TIMER3_CLK] |= CLK_M_OSC;
+
+	per = &cprm[CM_PER];
+	per[CM_PER_TIMER2_CLKCTRL] |= 2;
+	per[CM_PER_TIMER3_CLKCTRL] |= 2;
+	per[CM_PER_L4LS_CLKSTCTRL] |= CLKACTIVITY_TIMER2_GCLK | CLKACTIVITY_TIMER3_GCLK;
+
+	timer2[TIOCP_CFG] = SOFTRESET;
+	while((timer2[TIOCP_CFG] & SOFTRESET) != 0);
+	timer2[IRQENABLE_SET] = OVF_EN_FLAG;
+
+	timer3[TIOCP_CFG] = SOFTRESET;
+	while((timer3[TIOCP_CFG] & SOFTRESET) != 0);
+	timer3[IRQENABLE_SET] = OVF_EN_FLAG;
+	timer3[TCLR] |= AR | ST;
+
+	intrenable(TIMER2IRQ, timer2irq, nil, "clock");
+	intrenable(TIMER3IRQ, timer3irq, nil, "clock");
+}
--- /dev/null	Thu Dec 31 12:39:09 2020
+++ b/trap.c	Sat Nov 28 23:38:41 2020
@@ -0,0 +1,598 @@
+#include "u.h"
+#include <ureg.h>
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "tos.h"
+
+static void
+_dumpstack(Ureg *ureg)
+{
+	uintptr l, v, i, estack;
+	extern ulong etext;
+	int x;
+	char *s;
+
+	iprint("%s", "DUMP STACK\n");
+
+	if((s = getconf("*nodumpstack")) != nil && strcmp(s, "0") != 0){
+		iprint("dumpstack disabled\n");
+		return;
+	}
+	iprint("cpu%d: dumpstack\n", m->machno);
+
+	x = 0;
+	x += iprint("ktrace /arm/9bbb %.8lux %.8lux %.8lux <<EOF\n", ureg->pc, ureg->sp, ureg->r14);
+	i = 0;
+	if(up
+	&& (uintptr)&l >= (uintptr)up->kstack
+	&& (uintptr)&l <= (uintptr)up->kstack+KSTACK)
+		estack = (uintptr)up->kstack+KSTACK;
+	else if((uintptr)&l >= (uintptr)m->stack
+	&& (uintptr)&l <= (uintptr)m+MACHSIZE)
+		estack = (uintptr)m+MACHSIZE;
+	else
+		return;
+	x += iprint("estackx %p\n", estack);
+
+	for(l = (uintptr)&l; l < estack; l += sizeof(uintptr)){
+		v = *(uintptr*)l;
+		if((KTZERO < v && v < (uintptr)&etext) || estack-l < 32){
+			x += iprint("%.8p=%.8p ", l, v);
+			i++;
+		}
+		if(i == 4){
+			i = 0;
+			x += iprint("\n");
+		}
+	}
+	if(i)
+		iprint("\n");
+	iprint("EOF\n");
+}
+
+static char*
+faulterr[0x20] = {
+[0x01]	"alignement fault",
+[0x02]	"debug event",
+[0x04]	"fault on instruction cache maintenance",
+[0x08]	"synchronous external abort",
+[0x0C]	"synchronous external abort on translation table walk L1",
+[0x0E]	"synchronous external abort on translation table walk L2",
+[0x10]	"tlb conflict abort",
+[0x16]	"asynchronous external abort",
+[0x19]	"synchronous parity error on memory access",
+[0x1C]	"synchronous parity error on translation table walk L1",
+[0x1E]	"synchronous parity error on translation table walk L2",
+};
+
+static void
+faultarm(Ureg *ureg, ulong fsr, uintptr addr)
+{
+	int user, insyscall, read;
+	static char buf[ERRMAX];
+	char *err;
+
+	read = (fsr & (1<<11)) == 0;
+	user = userureg(ureg);
+	if(!user){
+		if(addr >= USTKTOP || up == nil)
+			_dumpstack(ureg);
+		if(addr >= USTKTOP)
+			panic("kernel fault: bad address pc=%#.8lux addr=%#.8lux fsr=%#.8lux", ureg->pc, addr, fsr);
+		if(up == nil)
+			panic("kernel fault: no user process pc=%#.8lux addr=%#.8lux fsr=%#.8lux", ureg->pc, addr, fsr);
+	}
+	if(up == nil)
+		panic("user fault: up=nil pc=%#.8lux addr=%#.8lux fsr=%#.8lux", ureg->pc, addr, fsr);
+
+	insyscall = up->insyscall;
+	up->insyscall = 1;
+	switch(fsr & 0x1F){
+	case 0x05:	/* translation fault L1 */
+	case 0x07:	/* translation fault L2 */
+	case 0x03:	/* access flag fault L1 */
+	case 0x06:	/* access flag fault L2 */
+	case 0x09:	/* domain fault L1 */
+	case 0x0B:	/* domain fault L2 */
+	case 0x0D:	/* permission fault L1 */
+	case 0x0F:	/* permission fault L2 */
+//iprint("fsr=%08x, addr=%#p, read=%d\n", fsr&0x1f, addr, read);
+		if(fault(addr, read) == 0)
+			break;
+//iprint("wet floor\n");
+		/* wet floor */
+	default:
+		err = faulterr[fsr & 0x1F];
+		if(err == nil)
+			err = "fault";
+		if(!user){
+			dumpregs(ureg);
+			_dumpstack(ureg);
+			panic("kernel %s: pc=%#.8lux addr=%#.8lux fsr=%#.8lux", err, ureg->pc, addr, fsr);
+		}
+		sprint(buf, "sys: trap: %s %s addr=%#.8lux", err, read ? "read" : "write", addr);
+print("%s\n", buf);
+		postnote(up, 1, buf, NDebug);
+	}
+	up->insyscall = insyscall;
+}
+
+static void
+mathtrap(Ureg *, ulong)
+{
+	int s;
+
+	if((up->fpstate & FPillegal) != 0){
+		postnote(up, 1, "sys: floating point in note handler", NDebug);
+		return;
+	}
+	switch(up->fpstate){
+	case FPinit:
+		s = splhi();
+		fpinit();
+		up->fpstate = FPactive;
+		splx(s);
+		break;
+	case FPinactive:
+		s = splhi();
+		fprestore(up->fpsave);
+		up->fpstate = FPactive;
+		splx(s);
+		break;
+	case FPactive:
+		postnote(up, 1, "sys: floating point error", NDebug);
+		break;
+	}
+}
+
+void vectors(void);
+
+void
+trap(Ureg *ureg)
+{
+	int user;
+	ulong opc, cp;
+
+	user = userureg(ureg);
+	if(user){
+		if(up == nil)
+			panic("user trap: up=nil");
+		up->dbgreg = ureg;
+		cycles(&up->kentry);
+	}
+	switch(ureg->type){
+	case PsrMund:
+		ureg->pc -= 4;
+		if(user){
+			spllo();
+			if(okaddr(ureg->pc, 4, 0)){
+				opc = *(ulong*)ureg->pc;
+				if((opc & 0x0f000000) == 0x0e000000 || (opc & 0x0e000000) == 0x0c000000){
+					cp = opc >> 8 & 15;
+					if(cp == 10 || cp == 11){
+						mathtrap(ureg, opc);
+						break;
+					}
+				}
+			}
+			postnote(up, 1, "sys: trap: invalid opcode", NDebug);
+			break;
+		}
+		dumpregs(ureg);
+		panic("invalid opcode at pc=%#.8lux lr=%#.8lux", ureg->pc, ureg->r14);
+		break;
+	case PsrMiabt:
+		ureg->pc -= 4;
+		faultarm(ureg, getifsr(), getifar());
+		break;
+	case PsrMabt:
+		ureg->pc -= 8;
+		faultarm(ureg, getdfsr(), getdfar());
+		break;
+	case PsrMirq:
+		ureg->pc -= 4;
+		intr(ureg);
+		break;
+	default:
+		iprint("cpu%d: unknown trap type %ulx\n", m->machno, ureg->type);
+	}
+	splhi();
+	if(user){
+		if(up->procctl || up->nnote)
+			notify(ureg);
+		kexit(ureg);
+	}
+}
+
+#include "../port/systab.h"
+
+void
+syscall(Ureg *ureg)
+{
+	char *e;
+	uintptr sp;
+	long ret;
+	int i, s;
+	ulong scallnr;
+	vlong startns, stopns;
+	
+	if(!userureg(ureg))
+		panic("syscall: pc=%#.8lux", ureg->pc);
+	
+	cycles(&up->kentry);
+	
+	m->syscall++;
+	up->insyscall = 1;
+	up->pc = ureg->pc;
+	up->dbgreg = ureg;
+	
+	sp = ureg->sp;
+	up->scallnr = scallnr = ureg->r0;
+
+	spllo();
+	
+	up->nerrlab = 0;
+	ret = -1;
+	if(!waserror()){
+		if(sp < USTKTOP - BY2PG || sp > USTKTOP - sizeof(Sargs) - BY2WD){
+			validaddr(sp, sizeof(Sargs)+BY2WD, 0);
+			evenaddr(sp);
+		}
+		up->s = *((Sargs*) (sp + BY2WD));
+		
+		if(up->procctl == Proc_tracesyscall){
+			syscallfmt(scallnr, ureg->pc, (va_list) up->s.args);
+			s = splhi();
+			up->procctl = Proc_stopme;
+			procctl();
+			splx(s);
+			startns = todget(nil);
+		}
+		
+		if(scallnr >= nsyscall || systab[scallnr] == 0){
+			pprint("bad sys call number %lud pc %lux", scallnr, ureg->pc);
+			postnote(up, 1, "sys: bad sys call", NDebug);
+			error(Ebadarg);
+		}
+		up->psstate = sysctab[scallnr];
+		ret = systab[scallnr]((va_list)up->s.args);
+		poperror();
+	}else{
+		e = up->syserrstr;
+		up->syserrstr = up->errstr;
+		up->errstr = e;
+	}
+	if(up->nerrlab){
+		print("bad errstack [%lud]: %d extra\n", scallnr, up->nerrlab);
+		for(i = 0; i < NERR; i++)
+			print("sp=%lux pc=%lux\n", up->errlab[i].sp, up->errlab[i].pc);
+		panic("error stack");
+	}
+	
+	ureg->r0 = ret;
+	if(up->procctl == Proc_tracesyscall){
+		stopns = todget(nil);
+		sysretfmt(scallnr, (va_list) up->s.args, ret, startns, stopns);
+		s = splhi();
+		up->procctl = Proc_stopme;
+		procctl();
+		splx(s);
+	}
+	
+	up->insyscall = 0;
+	up->psstate = 0;
+	if(scallnr == NOTED)
+		noted(ureg, *((ulong *) up->s.args));
+
+	if(scallnr != RFORK && (up->procctl || up->nnote)){
+		splhi();
+		notify(ureg);
+	}
+	if(up->delaysched)
+		sched();
+	kexit(ureg);
+	splhi();
+}
+
+int
+notify(Ureg *ureg)
+{
+	int l;
+	ulong s, sp;
+	Note *n;
+
+	if(up->procctl)
+		procctl();
+	if(up->nnote == 0)
+		return 0;
+
+	if(up->fpstate == FPactive){
+		fpsave(up->fpsave);
+		up->fpstate = FPinactive;
+	}
+	up->fpstate |= FPillegal;
+
+	s = spllo();
+	qlock(&up->debug);
+	up->notepending = 0;
+	n = &up->note[0];
+	if(strncmp(n->msg, "sys:", 4) == 0){
+		l = strlen(n->msg);
+		if(l > ERRMAX-15)	/* " pc=0x12345678\0" */
+			l = ERRMAX-15;
+		sprint(n->msg+l, " pc=0x%.8lux", ureg->pc);
+	}
+
+	if(n->flag!=NUser && (up->notified || up->notify==0)){
+		qunlock(&up->debug);
+		if(n->flag == NDebug)
+			pprint("suicide: %s\n", n->msg);
+		pexit(n->msg, n->flag!=NDebug);
+	}
+
+	if(up->notified){
+		qunlock(&up->debug);
+		splhi();
+		return 0;
+	}
+
+	if(!up->notify){
+		qunlock(&up->debug);
+		pexit(n->msg, n->flag!=NDebug);
+	}
+	sp = ureg->sp;
+	sp -= 256;	/* debugging: preserve context causing problem */
+	sp -= sizeof(Ureg);
+
+	if(!okaddr((uintptr)up->notify, 1, 0)
+	|| !okaddr(sp-ERRMAX-4*BY2WD, sizeof(Ureg)+ERRMAX+4*BY2WD, 1)
+	|| ((uintptr) up->notify & 3) != 0
+	|| (sp & 3) != 0){
+		qunlock(&up->debug);
+		pprint("suicide: bad address in notify\n");
+		pexit("Suicide", 0);
+	}
+
+	memmove((Ureg*)sp, ureg, sizeof(Ureg));
+	*(Ureg**)(sp-BY2WD) = up->ureg;	/* word under Ureg is old up->ureg */
+	up->ureg = (void*)sp;
+	sp -= BY2WD+ERRMAX;
+	memmove((char*)sp, up->note[0].msg, ERRMAX);
+	sp -= 3*BY2WD;
+	*(ulong*)(sp+2*BY2WD) = sp+3*BY2WD;
+	*(ulong*)(sp+1*BY2WD) = (ulong)up->ureg;
+	ureg->r0 = (uintptr) up->ureg;
+	ureg->sp = sp;
+	ureg->pc = (uintptr) up->notify;
+	ureg->r14 = 0;
+	up->notified = 1;
+	up->nnote--;
+	memmove(&up->lastnote, &up->note[0], sizeof(Note));
+	memmove(&up->note[0], &up->note[1], up->nnote*sizeof(Note));
+
+	qunlock(&up->debug);
+	splx(s);
+	return 1;
+}
+
+void
+noted(Ureg *ureg, ulong arg0)
+{
+	Ureg *nureg;
+	ulong oureg, sp;
+	
+	qlock(&up->debug);
+	if(arg0 != NRSTR && !up->notified){
+		qunlock(&up->debug);
+		pprint("call to noted() when not notified\n");
+		pexit("Suicide", 0);
+	}
+	up->notified = 0;
+	
+	nureg = up->ureg;
+	up->fpstate &= ~FPillegal;
+	
+	oureg = (ulong) nureg;
+	if(!okaddr(oureg - BY2WD, BY2WD + sizeof(Ureg), 0) || (oureg & 3) != 0){
+		qunlock(&up->debug);
+		pprint("bad ureg in noted or call to noted when not notified\n");
+		pexit("Suicide", 0);
+	}
+	
+	nureg->psr = nureg->psr & 0xf80f0000 | ureg->psr & 0x07f0ffff;
+	
+	memmove(ureg, nureg, sizeof(Ureg));
+	
+	switch(arg0){
+	case NCONT: case NRSTR:
+		if(!okaddr(nureg->pc, BY2WD, 0) || !okaddr(nureg->sp, BY2WD, 0) ||
+				(nureg->pc & 3) != 0 || (nureg->sp & 3) != 0){
+			qunlock(&up->debug);
+			pprint("suicide: trap in noted\n");
+			pexit("Suicide", 0);
+		}
+		up->ureg = (Ureg *) (*(ulong *) (oureg - BY2WD));
+		qunlock(&up->debug);
+		break;
+	
+	case NSAVE:
+		if(!okaddr(nureg->pc, BY2WD, 0) || !okaddr(nureg->sp, BY2WD, 0) ||
+				(nureg->pc & 3) != 0 || (nureg->sp & 3) != 0){
+			qunlock(&up->debug);
+			pprint("suicide: trap in noted\n");
+			pexit("Suicide", 0);
+		}
+		qunlock(&up->debug);
+		sp = oureg - 4 * BY2WD - ERRMAX;
+		splhi();
+		ureg->sp = sp;
+		ureg->r0 = (uintptr) oureg;
+		((ulong *) sp)[1] = oureg;
+		((ulong *) sp)[0] = 0;
+		break;
+	
+	default:
+		up->lastnote.flag = NDebug;
+	
+	case NDFLT:
+		qunlock(&up->debug);
+		if(up->lastnote.flag == NDebug)
+			pprint("suicide: %s\n", up->lastnote.msg);
+		pexit(up->lastnote.msg, up->lastnote.flag != NDebug);
+	}
+}
+
+
+void
+dumpstack(void)
+{
+	callwithureg(_dumpstack);
+}
+
+void
+dumpregs(Ureg *ureg)
+{
+	iprint("trap: %lux psr %8.8lux type %2.2lux pc %8.8lux link %8.8lux\n",
+		ureg->type, ureg->psr, ureg->type, ureg->pc, ureg->link);
+	iprint("R14 %8.8lux R13 %8.8lux R12 %8.8lux R11 %8.8lux R10 %8.8lux\n",
+		ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10);
+	iprint("R9  %8.8lux R8  %8.8lux R7  %8.8lux R6  %8.8lux R5  %8.8lux\n",
+		ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5);
+	iprint("R4  %8.8lux R3  %8.8lux R2  %8.8lux R1  %8.8lux R0  %8.8lux\n",
+		ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0);
+	iprint("pc %#lux link %#lux\n", ureg->pc, ureg->link);
+}
+
+void
+setkernur(Ureg *ureg, Proc *p)
+{
+	ureg->pc = p->sched.pc;
+	ureg->sp = p->sched.sp + 4;
+	ureg->r14 = (uintptr) sched;
+}
+
+void
+setregisters(Ureg* ureg, char* pureg, char* uva, int n)
+{
+	ulong v;
+
+	v = ureg->psr;
+	memmove(pureg, uva, n);
+	ureg->psr = ureg->psr & 0xf80f0000 | v & 0x07f0ffff;
+}
+
+void
+callwithureg(void (*f) (Ureg *))
+{
+	Ureg u;
+	
+	u.pc = getcallerpc(&f);
+	u.sp = (uintptr) &f - 4;
+	f(&u);
+}
+
+uintptr
+userpc(void)
+{
+	Ureg *ur;
+	
+	ur = up->dbgreg;
+	return ur->pc;
+}
+
+uintptr
+dbgpc(Proc *)
+{
+	Ureg *ur;
+	
+	ur = up->dbgreg;
+	if(ur == nil)
+		return 0;
+	return ur->pc;
+}
+
+void
+procsave(Proc *p)
+{
+	uvlong t;
+
+	if(p->fpstate == FPactive){
+		if(p->state == Moribund)
+			fpclear();
+		else
+			fpsave(p->fpsave);
+		p->fpstate = FPinactive;
+	}
+	cycles(&t);
+	p->kentry -= t;
+	p->pcycles += t;
+
+	l1switch(&m->l1, 0);
+}
+
+void
+procrestore(Proc *p)
+{
+	uvlong t;
+
+	if(p->kp)
+		return;
+
+	cycles(&t);
+	p->kentry += t;
+	p->pcycles -= t;
+}
+
+static void
+linkproc(void)
+{
+	spllo();
+	up->kpfun(up->kparg);
+	pexit("kproc dying", 0);
+}
+
+void
+kprocchild(Proc* p, void (*func)(void*), void* arg)
+{
+	p->sched.pc = (uintptr) linkproc;
+	p->sched.sp = (uintptr) p->kstack + KSTACK;
+
+	p->kpfun = func;
+	p->kparg = arg;
+}
+
+void
+forkchild(Proc *p, Ureg *ureg)
+{
+	Ureg *cureg;
+
+	p->sched.pc = (uintptr) forkret;
+	p->sched.sp = (uintptr) p->kstack + KSTACK - sizeof(Ureg);
+
+	cureg = (Ureg*) p->sched.sp;
+	memmove(cureg, ureg, sizeof(Ureg));
+	cureg->r0 = 0;
+
+	p->psstate = 0;
+	p->insyscall = 0;
+}
+
+uintptr
+execregs(uintptr entry, ulong ssize, ulong nargs)
+{
+	ulong *sp;
+	Ureg *ureg;
+
+	sp = (ulong*)(USTKTOP - ssize);
+	*--sp = nargs;
+
+	ureg = up->dbgreg;
+	ureg->sp = (uintptr) sp;
+	ureg->pc = entry;
+	ureg->r14 = 0;
+	return USTKTOP-sizeof(Tos);
+}
--- /dev/null	Thu Dec 31 12:39:09 2020
+++ b/uartbbb.c	Sat Nov 28 23:38:41 2020
@@ -0,0 +1,297 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+typedef struct Ctlr Ctlr;
+struct Ctlr
+{
+	Lock;
+	volatile ulong *r;
+	int irq, iena;
+};
+
+static Ctlr bbbctlr[1] = {
+	{
+		.r = (void*)VMAP,
+		.irq = UART0IRQ,
+	}
+};
+
+#define IT_TYPE(x) (((x)&0x3e)>>1)
+
+enum
+{
+	RHR = 0x00/4,
+	THR = 0x00/4,
+	IER = 0x04/4,
+		RHRIT = 1<<0,
+		THRIT = 1<<1,
+		SLEEPMODE = 1<<4,
+		RTSIT = 1<<6,
+	IIR = 0x08/4,
+		IT_PENDING = 1<<0,
+		IT_TYPE_MODEM = 0,
+		IT_TYPE_THR = 1,
+		IT_TYPE_RHR = 2,
+		IT_TYPE_RXTIMEOUT = 6,
+	EFR = 0x08/4,
+		ENHANCEDEN = 1<<4,
+	FCR = 0x08/4,
+		FIFO_EN = 1<<0,
+		RX_FIFO_CLEAR = 1<<1,
+		TX_FIFO_CLEAR = 1<<2,
+	LCR = 0x0c/4,
+		ConfigModeA = 0x80,
+		ConfigModeB = 0xbf,
+		OperMode = 0x00,
+		DIV_EN = 1<<7,
+	MCR = 0x10/4,
+		DTR = 1<<0,
+		RTS = 1<<1,
+		TCRTLR = 1<<6,
+	LSR = 0x14/4,
+		RXFIFOE = 1<<0,
+		TXFIFOE = 1<<5,
+		TXSRE = 1<<6,
+	MSR = 0x18/4,
+		
+	MDR1 = 0x20/4,
+		MODESELECT = 0x3,
+	SCR = 0x40/4,
+		TXEMPTYCTLIT = 1<<3,
+		TXTRIGGRANU1 = 1<<6,
+		RXTRIGGRANU1 = 1<<7,
+	SSR = 0x44/4,
+		TXFIFOFULL = 1<<0,
+	SYSC = 0x54/4,
+		SOFTRESET = 1<<1,
+	SYSS = 0x58/4,
+		RESETDONE = 1<<0,
+	DLL = 0x00/4,
+	DLH = 0x04/4,
+};
+
+Uart* uartenable(Uart*);
+
+static Uart *bbbuartpnp(void);
+static void bbbuartkick(Uart*);
+static void bbbuartintr(Ureg*, void*);
+static void bbbuartenable(Uart*, int);
+static int bbbuartgetc(Uart*);
+static void bbbuartputc(Uart*, int);
+static int bbbuartbits(Uart*, int);
+static int bbbuartbaud(Uart*, int);
+static int bbbuartparity(Uart*, int);
+static void bbbuartnop(Uart*, int);
+static int bbbuartnope(Uart*, int);
+
+PhysUart bbbphysuart = {
+	.baud = bbbuartbaud,
+	.bits = bbbuartbits,
+	.dobreak = bbbuartnop,
+	.dtr = bbbuartnop,
+	.enable = bbbuartenable,
+	.fifo = bbbuartnop,
+	.getc = bbbuartgetc,
+	.kick = bbbuartkick,
+	.modemctl = bbbuartnop,
+	.parity = bbbuartparity,
+	.pnp = bbbuartpnp,
+	.power = bbbuartnop,
+	.putc = bbbuartputc,
+	.rts = bbbuartnop,
+	.stop = bbbuartnope,
+};
+
+static Uart bbbuart[1] = {
+	{
+		.regs = &bbbctlr[0],
+		.name = "UART0",
+		.freq = 25000000,
+		.phys = &bbbphysuart,
+		.console = 1,
+		.baud = 115200,
+	}
+};
+
+static Uart*
+bbbuartpnp(void)
+{
+	return bbbuart;
+}
+
+static void
+bbbuartkick(Uart *uart)
+{
+	Ctlr *ct;
+	int i;
+
+	if(uart->blocked)
+		return;
+	ct = uart->regs;
+	for(i = 0; i < 128; i++){
+		if((ct->r[SSR] & TXFIFOFULL) != 0)
+			break;
+		if(uart->op >= uart->oe)
+		if(uartstageoutput(uart) == 0)
+				break;
+		ct->r[THR] = *uart->op++;
+		ct->r[IER] |= THRIT;
+		//coherence();
+	}
+}
+
+static int
+bbbuartbits(Uart*, int)
+{
+	return -1;
+}
+
+static int
+bbbuartbaud(Uart*, int)
+{
+	return 0;
+}
+
+static void
+bbbuartintr(Ureg *, void *arg)
+{
+	Uart *uart;
+	Ctlr *ct;
+	ulong iir, c;
+
+	uart = arg;
+	ct = uart->regs;
+	for(iir = ct->r[IIR]; (iir & IT_PENDING) == 0; iir = ct->r[IIR]){
+		switch(IT_TYPE(iir)){
+		case IT_TYPE_RHR:
+		case IT_TYPE_RXTIMEOUT:
+			while((ct->r[LSR] & RXFIFOE) != 0){
+				c = ct->r[RHR];
+				uartrecv(uart, c);
+			}
+			break;
+		case IT_TYPE_THR:
+			bbbuartkick(uart);
+			if(uart->op >= uart->oe)
+			if(qlen(uart->oq) == 0)
+			if((ct->r[LSR] & TXSRE) != 0){
+				ct->r[IER] &= ~THRIT;
+				//coherence();
+			}
+			break;
+		}
+	}
+}
+
+static void
+bbbuartenable(Uart *uart, int ie)
+{
+	Ctlr *ctlr;
+
+	ctlr = uart->regs;
+	ilock(ctlr);
+
+	while((ctlr->r[LSR] & TXSRE) == 0);
+
+	ctlr->r[SYSC] |= SOFTRESET;
+	while((ctlr->r[SYSS] & RESETDONE) == 0);
+
+	ctlr->r[LCR] = ConfigModeB;
+	ctlr->r[EFR] |= ENHANCEDEN;
+	ctlr->r[LCR] = ConfigModeA;
+	ctlr->r[MCR] |= TCRTLR;
+	ctlr->r[FCR] = FIFO_EN;
+	ctlr->r[LCR] = ConfigModeB;
+	ctlr->r[EFR] &= ~ENHANCEDEN;
+	ctlr->r[LCR] = ConfigModeA;
+	ctlr->r[MCR] &= ~TCRTLR;
+
+	ctlr->r[MDR1] = 0x7;
+	ctlr->r[LCR] = ConfigModeB;
+	ctlr->r[EFR] |= ENHANCEDEN;
+	ctlr->r[LCR] = OperMode;
+	ctlr->r[IER] = 0;
+	ctlr->r[LCR] = ConfigModeB;
+	ctlr->r[DLL] = 0x1a;
+	ctlr->r[DLH] = 0x00; // 115200 baud
+	ctlr->r[LCR] = OperMode;
+	ctlr->r[SCR] |= TXEMPTYCTLIT;
+	ctlr->r[IER] = RHRIT | THRIT; // enable interrupts
+	ctlr->r[LCR] = ConfigModeB;
+	ctlr->r[EFR] &= ~ENHANCEDEN;
+	ctlr->r[LCR] = 0x03; // 8-it, 1 stop, no parity
+	ctlr->r[MDR1] = 0;
+	delay(25);
+
+	if(ie){
+		if(!ctlr->iena){
+			intrenable(ctlr->irq, bbbuartintr, uart, uart->name);
+			ctlr->iena = 1;
+		}
+	}
+	iunlock(ctlr);
+}
+
+static int
+bbbuartgetc(Uart *u)
+{
+	Ctlr *ct;
+
+	ct = u->regs;
+	while((ct->r[LSR] & RXFIFOE) == 0)
+		;
+	return ct->r[RHR];
+}
+
+static void
+bbbuartputc(Uart *u, int c)
+{
+	Ctlr *ct;
+
+	ct = u->regs;
+	while((ct->r[SSR] & TXFIFOFULL) != 0)
+		;
+	ct->r[THR] = c;
+}
+
+static int
+bbbuartparity(Uart*, int)
+{
+	return -1;
+}
+
+static void
+bbbuartnop(Uart*, int)
+{
+}
+
+static int
+bbbuartnope(Uart*, int)
+{
+	return -1;
+}
+
+void
+uartinit(void)
+{
+	consuart = bbbuart;
+}
+
+int
+uartconsole(void)
+{
+	Uart *uart = bbbuart;
+
+	if(up == nil)
+		return -1;
+	if(uartenable(uart) != nil){
+		serialoq = uart->oq;
+		uart->opens++;
+		consuart = uart;
+	}
+	return 0;
+}