shithub: riscv

Download patch

ref: c6ad540af56be95b458008ae3abd3432b71d49dd
parent: 1a7c224b3e36342623d4050953ca0cf3cb8a8bd5
author: cinap_lenrek <[email protected]>
date: Fri May 3 19:14:57 EDT 2019

bcm64: add experimental work in progress arm64 kernel for raspberry pi 3

--- /dev/null
+++ b/sys/src/9/bcm64/archbcm3.c
@@ -1,0 +1,168 @@
+/*
+ * bcm2836 (e.g.raspberry pi 3) architecture-specific stuff
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+#include "sysreg.h"
+
+typedef struct Mbox Mbox;
+typedef struct Mboxes Mboxes;
+
+#define	POWERREGS	(VIRTIO+0x100000)
+
+Soc soc = {
+	.dramsize	= GiB,
+	.physio		= 0x3F000000,
+	.busdram	= 0xC0000000,
+	.busio		= 0x7E000000,
+	.armlocal	= 0x40000000,
+};
+
+enum {
+	Wdogfreq	= 65536,
+	Wdogtime	= 10,	/* seconds, ≤ 15 */
+};
+
+/*
+ * Power management / watchdog registers
+ */
+enum {
+	Rstc		= 0x1c>>2,
+		Password	= 0x5A<<24,
+		CfgMask		= 0x03<<4,
+		CfgReset	= 0x02<<4,
+	Rsts		= 0x20>>2,
+	Wdog		= 0x24>>2,
+};
+
+/*
+ * Arm local regs for smp
+ */
+struct Mbox {
+	u32int	doorbell;
+	u32int	mbox1;
+	u32int	mbox2;
+	u32int	startcpu;
+};
+struct Mboxes {
+	Mbox	set[4];
+	Mbox	clr[4];
+};
+
+enum {
+	Mboxregs	= 0x80,
+};
+
+void
+archreset(void)
+{
+}
+
+void
+archreboot(void)
+{
+	u32int *r;
+
+	r = (u32int*)POWERREGS;
+	r[Wdog] = Password | 1;
+	r[Rstc] = Password | (r[Rstc] & ~CfgMask) | CfgReset;
+	coherence();
+	for(;;)
+		;
+}
+
+void
+wdogfeed(void)
+{
+	u32int *r;
+
+	r = (u32int*)POWERREGS;
+	r[Wdog] = Password | (Wdogtime * Wdogfreq);
+	r[Rstc] = Password | (r[Rstc] & ~CfgMask) | CfgReset;
+}
+
+void
+wdogoff(void)
+{
+	u32int *r;
+
+	r = (u32int*)POWERREGS;
+	r[Rstc] = Password | (r[Rstc] & ~CfgMask);
+}
+
+
+char *
+cputype2name(char *buf, int size)
+{
+	u32int r, part;
+	char *p;
+
+	r = sysrd(MIDR_EL1);
+	part = (r >> 4) & 0xFFF;
+	switch(part){
+	case 0xc07:
+		p = seprint(buf, buf + size, "Cortex-A7");
+		break;
+	case 0xd03:
+		p = seprint(buf, buf + size, "Cortex-A53");
+		break;
+	default:
+		p = seprint(buf, buf + size, "Unknown-%#x", part);
+		break;
+	}
+	seprint(p, buf + size, " r%udp%ud", (r >> 20) & 0xF, r & 0xF);
+	return buf;
+}
+
+void
+cpuidprint(void)
+{
+	char name[64];
+
+	cputype2name(name, sizeof name);
+	iprint("cpu%d: %dMHz ARM %s\n", m->machno, m->cpumhz, name);
+}
+
+int
+getncpus(void)
+{
+	int n, max;
+	char *p;
+	n = 4;
+	if(n > MAXMACH)
+		n = MAXMACH;
+	p = getconf("*ncpu");
+	if(p && (max = atoi(p)) > 0 && n > max)
+		n = max;
+	return n;
+}
+
+void
+mboxclear(uint cpu)
+{
+	Mboxes *mb;
+
+	mb = (Mboxes*)(ARMLOCAL + Mboxregs);
+	mb->clr[cpu].mbox1 = 1;
+}
+
+void
+wakecpu(uint cpu)
+{
+	Mboxes *mb;
+
+	mb = (Mboxes*)(ARMLOCAL + Mboxregs);
+	mb->set[cpu].mbox1 = 1;
+}
+
+void
+archbcm3link(void)
+{
+//	addclock0link(wdogfeed, HZ);
+}
--- /dev/null
+++ b/sys/src/9/bcm64/cache.v8.s
@@ -1,0 +1,212 @@
+#include "sysreg.h"
+
+#undef	SYSREG
+#define	SYSREG(op0,op1,Cn,Cm,op2)	SPR(((op0)<<19|(op1)<<16|(Cn)<<12|(Cm)<<8|(op2)<<5))
+
+/*
+ * instruction cache operations
+ */
+TEXT cacheiinvse(SB), 1, $-4
+	MOVWU	len+8(FP), R2
+	ADD	R0, R2
+
+	MRS	DAIF, R11
+	MSR	$0x2, DAIFSet
+	MOVWU	$1, R10
+	MSR	R10, CSSELR_EL1
+	ISB	$SY
+	MRS	CCSIDR_EL1, R4
+
+	ANDW	$7, R4
+	ADDW	$4, R4		// log2(linelen)
+	LSL	R4, R10
+	LSR	R4, R0
+	LSL	R4, R0
+
+_iinvse:
+	IC	R0, 3,7,5,1	// IVAU
+	ADD	R10, R0
+	CMP	R0, R2
+	BGT	_iinvse
+	DSB	$NSH
+	ISB	$SY
+	MSR	R11, DAIF
+	RETURN
+
+TEXT cacheiinv(SB), 1, $-4
+	IC	R0, 0,7,5,0	// IALLU
+	DSB	$NSH
+	ISB	$SY
+	RETURN
+
+TEXT cacheuwbinv(SB), 1, $0
+	BL	cachedwbinv(SB)
+	BL	cacheiinv(SB)
+	RETURN
+
+/*
+ * data cache operations
+ */
+TEXT cachedwbse(SB), 1, $-4
+	MOV	LR, R29
+	BL	cachedva<>(SB)
+TEXT dccvac(SB), 1, $-4
+	DC	R0, 3,7,10,1	// CVAC
+	RETURN
+
+TEXT cacheduwbse(SB), 1, $-4
+	MOV	LR, R29
+	BL	cachedva<>(SB)
+TEXT dccvau(SB), 1, $-4
+	DC	R0, 3,7,11,1	// CVAU
+	RETURN
+
+TEXT cachedinvse(SB), 1, $-4
+	MOV	LR, R29
+	BL	cachedva<>(SB)
+TEXT dcivac(SB), 1, $-4
+	DC	R0, 0,7,6,1	// IVAC
+	RETURN
+
+TEXT cachedwbinvse(SB), 1, $-4
+	MOV	LR, R29
+	BL	cachedva<>(SB)
+TEXT dccivac(SB), 1, $-4
+	DC	R0, 3,7,14,1	// CIVAC
+	RETURN
+
+TEXT cachedva<>(SB), 1, $-4
+	MOV	LR, R1
+	MOVWU	len+8(FP), R2
+	ADD	R0, R2
+
+	MRS	DAIF, R11
+	MSR	$0x2, DAIFSet
+	MOVWU	$0, R10
+	MSR	R10, CSSELR_EL1
+	ISB	$SY
+	MRS	CCSIDR_EL1, R4
+
+	ANDW	$7, R4
+	ADDW	$4, R4		// log2(linelen)
+	MOVWU	$1, R10
+	LSL	R4, R10
+	LSR	R4, R0
+	LSL	R4, R0
+
+	DSB	$SY
+	ISB	$SY
+_cachedva:
+	BL	(R1)
+	ADD	R10, R0
+	CMP	R0, R2
+	BGT	_cachedva
+	DSB	$SY
+	ISB	$SY
+	MSR	R11, DAIF
+	RET	R29
+
+/*
+ * l1 cache operations
+ */
+TEXT cachedwb(SB), 1, $-4
+	MOVWU	$0, R0
+_cachedwb:
+	MOV	LR, R29
+	BL	cachedsw<>(SB)
+TEXT dccsw(SB), 1, $-4
+	DC	R0, 0,7,10,2	// CSW
+	RETURN
+
+TEXT cachedinv(SB), 1, $-4
+	MOVWU	$0, R0
+_cachedinv:
+	MOV	LR, R29
+	BL	cachedsw<>(SB)
+TEXT dcisw(SB), 1, $-4
+	DC	R0, 0,7,6,2	// ISW
+	RETURN
+
+TEXT cachedwbinv(SB), 1, $-4
+	MOVWU	$0, R0
+_cachedwbinv:
+	MOV	LR, R29
+	BL	cachedsw<>(SB)
+TEXT dccisw(SB), 1, $-4
+	DC	R0, 0,7,14,2	// CISW
+	RETURN
+
+/*
+ * l2 cache operations
+ */
+TEXT l2cacheuwb(SB), 1, $-4
+	MOVWU	$1, R0
+	B	_cachedwb
+TEXT l2cacheuinv(SB), 1, $-4
+	MOVWU	$1, R0
+	B	_cachedinv
+TEXT l2cacheuwbinv(SB), 1, $-4
+	MOVWU	$1, R0
+	B	_cachedwbinv
+
+TEXT cachesize(SB), 1, $-4
+	MRS	DAIF, R11
+	MSR	$0x2, DAIFSet
+	MSR	R0, CSSELR_EL1
+	ISB	$SY
+	MRS	CCSIDR_EL1, R0
+	MSR	R11, DAIF
+	RETURN
+
+TEXT cachedsw<>(SB), 1, $-4
+	MOV	LR, R1
+
+	MRS	DAIF, R11
+	MSR	$0x2, DAIFSet
+	ADDW	R0, R0, R8
+	MSR	R8, CSSELR_EL1
+	ISB	$SY
+	MRS	CCSIDR_EL1, R4
+
+	LSR	$3, R4, R7
+	ANDW	$1023, R7	// lastway
+	ADDW	$1, R7, R5	// #ways
+
+	LSR	$13, R4, R2
+	ANDW	$32767, R2	// lastset
+	ADDW	$1, R2		// #sets
+
+	ANDW	$7, R4
+	ADDW	$4, R4		// log2(linelen)
+
+	MOVWU	$32, R3		// wayshift = 32 - log2(#ways)
+_countlog2ways:
+	CBZ	R7, _loop	// lastway == 0?
+	LSR	$1, R7		// lastway >>= 1
+	SUB	$1, R3		// wayshift--
+	B _countlog2ways
+_loop:
+	DSB	$SY
+	ISB	$SY
+_nextway:
+	MOVWU	$0, R6		// set
+_nextset:
+	LSL	R3, R7, R0	// way<<wayshift
+	LSL	R4, R6, R9	// set<<log2(linelen)
+	ORRW	R8, R0		// level
+	ORRW	R9, R0		// setway
+
+	BL	(R1)		// op(setway)
+
+	ADDW	$1, R6		// set++
+	CMPW	R2, R6
+	BLT	_nextset
+
+	ADDW	$1, R7		// way++
+	CMPW	R5, R7
+	BLT	_nextway
+
+	DSB	$SY
+	ISB	$SY
+	MSR	R11, DAIF
+	RET	R29
--- /dev/null
+++ b/sys/src/9/bcm64/clock.c
@@ -1,0 +1,267 @@
+/*
+ * bcm283[56] timers
+ *	System timers run at 1MHz (timers 1 and 2 are used by GPU)
+ *	ARM timer usually runs at 250MHz (may be slower in low power modes)
+ *	Cycle counter runs at 700MHz (unless overclocked)
+ *    All are free-running up-counters
+ *
+ * Use system timer 3 (64 bits) for hzclock interrupts and fastticks
+ *   For smp on bcm2836, use local generic timer for interrupts on cpu1-3
+ * Use ARM timer (32 bits) for perfticks
+ * Use ARM timer to force immediate interrupt
+ * Use cycle counter for cycles()
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "sysreg.h"
+
+enum {
+	SYSTIMERS	= VIRTIO+0x3000,
+	ARMTIMER	= VIRTIO+0xB400,
+
+	Localctl	= 0x00,
+	Prescaler	= 0x08,
+	GPUirqroute	= 0x0C,
+
+	SystimerFreq	= 1*Mhz,
+	MaxPeriod	= SystimerFreq / HZ,
+	MinPeriod	= 10,
+};
+
+typedef struct Systimers Systimers;
+typedef struct Armtimer Armtimer;
+
+struct Systimers {
+	u32int	cs;
+	u32int	clo;
+	u32int	chi;
+	u32int	c0;
+	u32int	c1;
+	u32int	c2;
+	u32int	c3;
+};
+
+struct Armtimer {
+	u32int	load;
+	u32int	val;
+	u32int	ctl;
+	u32int	irqack;
+	u32int	irq;
+	u32int	maskedirq;
+	u32int	reload;
+	u32int	predivider;
+	u32int	count;
+};
+
+enum {
+	CntPrescaleShift= 16,	/* freq is sys_clk/(prescale+1) */
+	CntPrescaleMask	= 0xFF,
+	CntEnable	= 1<<9,
+	TmrDbgHalt	= 1<<8,
+	TmrEnable	= 1<<7,
+	TmrIntEnable	= 1<<5,
+	TmrPrescale1	= 0x00<<2,
+	TmrPrescale16	= 0x01<<2,
+	TmrPrescale256	= 0x02<<2,
+	CntWidth16	= 0<<1,
+	CntWidth32	= 1<<1,
+
+	/* generic timer (cortex-a7) */
+	Enable	= 1<<0,
+	Imask	= 1<<1,
+	Istatus = 1<<2,
+};
+
+static void
+clockintr(Ureg *ureg, void *)
+{
+	Systimers *tn;
+
+	if(m->machno != 0)
+		panic("cpu%d: unexpected system timer interrupt", m->machno);
+	tn = (Systimers*)SYSTIMERS;
+	/* dismiss interrupt */
+	tn->cs = 1<<3;
+	timerintr(ureg, 0);
+}
+
+static void
+localclockintr(Ureg *ureg, void *)
+{
+	if(m->machno == 0)
+		panic("cpu0: Unexpected local generic timer interrupt");
+	timerintr(ureg, 0);
+}
+
+void
+clockshutdown(void)
+{
+	Armtimer *tm;
+
+	tm = (Armtimer*)ARMTIMER;
+	tm->ctl = 0;
+}
+
+void
+clockinit(void)
+{
+	Systimers *tn;
+	Armtimer *tm;
+	ulong t0, t1, tstart, tend;
+
+	syswr(PMCR_EL0, 1<<6 | 7);
+	syswr(PMCNTENSET, 1<<31);
+	syswr(PMUSERENR_EL0, 1<<2);
+
+	syswr(CNTP_TVAL_EL0, ~0UL);
+	if(m->machno == 0){
+		syswr(CNTP_CTL_EL0, Imask);
+
+		*(u32int*)(ARMLOCAL + GPUirqroute) = 0;
+
+		/* input clock is 19.2Mhz crystal */
+		*(u32int*)(ARMLOCAL + Localctl) = 0;
+		/* divide by (2^31/Prescaler) */
+		*(u32int*)(ARMLOCAL + Prescaler) = (((uvlong)SystimerFreq<<31)/19200000)&~1UL;
+	} else {
+		syswr(CNTP_CTL_EL0, Enable);
+		intrenable(IRQcntpns, localclockintr, nil, 0, "clock");
+	}
+
+	tn = (Systimers*)SYSTIMERS;
+	tstart = tn->clo;
+	do{
+		t0 = lcycles();
+	}while(tn->clo == tstart);
+	tend = tstart + (SystimerFreq/100);
+	do{
+		t1 = lcycles();
+	}while(tn->clo < tend);
+	t1 -= t0;
+	m->cpuhz = 100 * t1;
+	m->cpumhz = (m->cpuhz + Mhz/2 - 1) / Mhz;
+	m->cyclefreq = m->cpuhz;
+
+	if(m->machno == 0){
+		tn->cs = 1<<3;
+		tn->c3 = tn->clo - 1;
+		intrenable(IRQtimer3, clockintr, nil, 0, "clock");
+
+		tm = (Armtimer*)ARMTIMER;
+		tm->load = 0;
+		tm->ctl = TmrPrescale1|CntEnable|CntWidth32;
+	}
+}
+
+void
+timerset(uvlong next)
+{
+	Systimers *tn;
+	uvlong now;
+	long period;
+
+	now = fastticks(nil);
+	period = next - now;
+	if(period < MinPeriod)
+		period = MinPeriod;
+	else if(period > MaxPeriod)
+		period = MaxPeriod;
+	if(m->machno)
+		syswr(CNTP_TVAL_EL0, period);
+	else{
+		tn = (Systimers*)SYSTIMERS;
+		tn->c3 = tn->clo + period;
+	}
+}
+
+uvlong
+fastticks(uvlong *hz)
+{
+	Systimers *tn;
+	ulong lo, hi;
+	uvlong now;
+
+	if(hz)
+		*hz = SystimerFreq;
+	tn = (Systimers*)SYSTIMERS;
+	do{
+		hi = tn->chi;
+		lo = tn->clo;
+	}while(tn->chi != hi);
+	now = (uvlong)hi<<32 | lo;
+	return now;
+}
+
+ulong
+perfticks(void)
+{
+	Armtimer *tm;
+
+	tm = (Armtimer*)ARMTIMER;
+	return tm->count;
+}
+
+void
+armtimerset(int n)
+{
+	Armtimer *tm;
+
+	tm = (Armtimer*)ARMTIMER;
+	if(n > 0){
+		tm->ctl |= TmrEnable|TmrIntEnable;
+		tm->load = n;
+	}else{
+		tm->load = 0;
+		tm->ctl &= ~(TmrEnable|TmrIntEnable);
+		tm->irq = 1;
+	}
+}
+
+ulong
+µs(void)
+{
+	if(SystimerFreq != 1*Mhz)
+		return fastticks2us(fastticks(nil));
+	return ((Systimers*)SYSTIMERS)->clo;
+}
+
+void
+microdelay(int n)
+{
+	ulong now;
+
+	now = µs();
+	while(µs() - now < n);
+}
+
+void
+delay(int n)
+{
+	while(--n >= 0)
+		microdelay(1000);
+}
+
+void
+synccycles(void)
+{
+	static Ref r1, r2;
+	int s;
+
+	s = splhi();
+	r2.ref = 0;
+	incref(&r1);
+	while(r1.ref != conf.nmach)
+		;
+//	syswr(PMCR_EL0, 1<<6 | 7);
+	incref(&r2);
+	while(r2.ref != conf.nmach)
+		;
+	r1.ref = 0;
+	splx(s);
+}
--- /dev/null
+++ b/sys/src/9/bcm64/dat.h
@@ -1,0 +1,272 @@
+/*
+ * Time.
+ *
+ * HZ should divide 1000 evenly, ideally.
+ * 100, 125, 200, 250 and 333 are okay.
+ */
+#define	HZ		100			/* clock frequency */
+#define	MS2HZ		(1000/HZ)		/* millisec per clock tick */
+#define	TK2SEC(t)	((t)/HZ)		/* ticks to seconds */
+
+enum {
+	Mhz	= 1000 * 1000,
+};
+
+typedef struct Conf	Conf;
+typedef struct Confmem	Confmem;
+typedef struct FPsave	FPsave;
+typedef struct PFPU	PFPU;
+typedef struct ISAConf	ISAConf;
+typedef struct Label	Label;
+typedef struct Lock	Lock;
+typedef struct Memcache	Memcache;
+typedef struct MMMU	MMMU;
+typedef struct Mach	Mach;
+typedef struct Page	Page;
+typedef struct PhysUart	PhysUart;
+typedef struct PMMU	PMMU;
+typedef struct Proc	Proc;
+typedef u64int		PTE;
+typedef struct Soc	Soc;
+typedef struct Uart	Uart;
+typedef struct Ureg	Ureg;
+typedef uvlong		Tval;
+typedef void		KMap;
+
+#pragma incomplete Ureg
+
+#define MAXSYSARG	5	/* for mount(fd, mpt, flag, arg, srv) */
+
+/*
+ *  parameters for sysproc.c
+ */
+#define AOUT_MAGIC	(R_MAGIC)
+
+struct Lock
+{
+	ulong	key;
+	u32int	sr;
+	uintptr	pc;
+	Proc*	p;
+	Mach*	m;
+	int	isilock;
+};
+
+struct Label
+{
+	uintptr	sp;
+	uintptr	pc;
+};
+
+struct FPsave
+{
+	uvlong	regs[32][2];
+
+	ulong	control;
+	ulong	status;
+};
+
+struct PFPU
+{
+	FPsave	fpsave[1];
+
+	int	fpstate;
+};
+
+enum
+{
+	FPinit,
+	FPactive,
+	FPinactive,
+
+	/* bits or'd with the state */
+	FPillegal= 0x100,
+};
+
+struct Confmem
+{
+	uintptr	base;
+	usize	npage;
+	uintptr	limit;
+	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 */
+	ulong	hz;		/* processor cycle freq */
+	ulong	mhz;
+	int	monitor;	/* flag */
+};
+
+/*
+ *  MMU stuff in Mach.
+ */
+struct MMMU
+{
+	PTE*	mmul1;		/* l1 for this processor */
+};
+
+/*
+ *  MMU stuff in proc
+ */
+#define NCOLOR	1		/* 1 level cache, don't worry about VCE's */
+
+struct PMMU
+{
+	Page*	mmul1;
+	Page*	mmul1tail;
+
+	Page*	mmul2;
+	Page*	mmul2tail;
+
+	Page*	mmufree;
+
+	int	asid;
+
+	uintptr	tpidr;
+};
+
+#include "../port/portdat.h"
+
+struct Mach
+{
+	int	machno;			/* physical id of processor */
+	uintptr	splpc;			/* pc of last caller to splhi */
+
+	Proc*	proc;			/* current process */
+
+	MMMU;
+	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 */
+
+	Proc*	readied;		/* for runproc */
+	ulong	schedticks;		/* next forced context switch */
+
+	int	cputype;
+	ulong	delayloop;
+
+	/* stats */
+	int	tlbfault;
+	int	tlbpurge;
+	int	pfault;
+	int	cs;
+	int	syscall;
+	int	load;
+	int	intr;
+	uvlong	fastclock;		/* last sampled value */
+	uvlong	inidle;			/* time spent in idlehands() */
+	ulong	spuriousintr;
+	int	lastintr;
+	int	ilockdepth;
+	Perf	perf;			/* performance counters */
+
+	int	cpumhz;
+	uvlong	cpuhz;			/* speed of cpu */
+	uvlong	cyclefreq;		/* Frequency of user readable cycle counter */
+
+	int	stack[1];
+};
+
+struct
+{
+	char	machs[MAXMACH];		/* active CPUs */
+	int	exiting;		/* shutdown */
+}active;
+
+#define MACHP(n)	((Mach*)MACHADDR(n))
+
+extern register Mach* m;			/* R27 */
+extern register Proc* up;			/* R26 */
+extern int normalprint;
+extern ulong memsize;
+
+/*
+ *  a parsed plan9.ini line
+ */
+#define NISAOPT		8
+
+struct ISAConf {
+	char	*type;
+	ulong	port;
+	int	irq;
+	ulong	dma;
+	ulong	mem;
+	ulong	size;
+	ulong	freq;
+
+	int	nopt;
+	char	*opt[NISAOPT];
+};
+
+/*
+ * Horrid. But the alternative is 'defined'.
+ */
+#ifdef _DBGC_
+#define DBGFLG		(dbgflg[_DBGC_])
+#else
+#define DBGFLG		(0)
+#endif /* _DBGC_ */
+
+int vflag;
+extern char dbgflg[256];
+
+#define dbgprint	print		/* for now */
+
+/*
+ *  hardware info about a device
+ */
+typedef struct {
+	ulong	port;
+	int	size;
+} Devport;
+
+struct DevConf
+{
+	ulong	intnum;			/* interrupt number */
+	char	*type;			/* card type, malloced */
+	int	nports;			/* Number of ports */
+	Devport	*ports;			/* The ports themselves */
+};
+
+struct Soc {			/* SoC dependent configuration */
+	ulong	dramsize;
+	uintptr	physio;
+	uintptr	busdram;
+	uintptr	busio;
+	uintptr	armlocal;
+	u32int	l1ptedramattrs;
+	u32int	l2ptedramattrs;
+};
+extern Soc soc;
+
+#define BUSUNKNOWN -1
+
+/*
+ * GPIO
+ */
+enum {
+	Input	= 0x0,
+	Output	= 0x1,
+	Alt0	= 0x4,
+	Alt1	= 0x5,
+	Alt2	= 0x6,
+	Alt3	= 0x7,
+	Alt4	= 0x3,
+	Alt5	= 0x2,
+};
--- /dev/null
+++ b/sys/src/9/bcm64/devgen.c
@@ -1,0 +1,34 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+/*
+ * the zeroth element of the table MUST be the directory itself for ..
+*/
+int
+devgen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp)
+{
+	if(tab == 0)
+		return -1;
+	if(i == DEVDOTDOT){
+		/* nothing */
+	}else if(name){
+		for(i=1; i<ntab; i++)
+			if(strcmp(tab[i].name, name) == 0)
+				break;
+		if(i==ntab)
+			return -1;
+		tab += i;
+	}else{
+		/* skip over the first element, that for . itself */
+		i++;
+		if(i >= ntab)
+			return -1;
+		tab += i;
+	}
+	devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
+	return 1;
+}
--- /dev/null
+++ b/sys/src/9/bcm64/fns.h
@@ -1,0 +1,168 @@
+#include "../port/portfns.h"
+
+#define	waserror()	(up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
+
+/* l.s */
+extern void sev(void);
+extern int tas(void *);
+extern int cmpswap(long*, long, long);
+extern void coherence(void);
+extern void idlehands(void);
+extern uvlong cycles(void);
+extern int splfhi(void);
+extern void splflo(void);
+extern void touser(uintptr sp);
+extern void forkret(void);
+extern void noteret(void);
+extern void returnto(void*);
+extern void fpsaveregs(void*);
+extern void fploadregs(void*);
+extern void magic(void);
+
+extern void setttbr(uintptr pa);
+extern uintptr getfar(void);
+
+extern void flushasidva(uintptr asidva);
+extern void tlbivae1is(uintptr asidva);
+
+extern void flushasidvall(uintptr asidva);
+extern void tlbivale1is(uintptr asidva);
+
+extern void flushasid(uintptr asid);
+extern void tlbiaside1is(uintptr asid);
+
+extern void flushtlb(void);
+extern void tlbivmalle1(void);
+
+/* cache */
+extern ulong cachesize(int level);
+
+extern void cacheiinvse(void*, int);
+extern void cacheuwbinv(void);
+extern void cacheiinv(void);
+
+extern void cachedwbse(void*, int);
+extern void cacheduwbse(void*, int);
+extern void cachedinvse(void*, int);
+extern void cachedwbinvse(void*, int);
+
+extern void cachedwb(void);
+extern void cachedinv(void);
+extern void cachedwbinv(void);
+
+extern void l2cacheuwb(void);
+extern void l2cacheuinv(void);
+extern void l2cacheuwbinv(void);
+
+/* mmu */
+#define	getpgcolor(a)	0
+extern uintptr paddr(void*);
+#define PADDR(a) paddr((void*)(a))
+extern uintptr cankaddr(uintptr);
+extern void* kaddr(uintptr);
+#define KADDR(a) kaddr(a)
+extern void kmapinval(void);
+#define	VA(k)	((uintptr)(k))
+extern KMap *kmap(Page*);
+extern void kunmap(KMap*);
+extern uintptr mmukmap(uintptr, uintptr, usize);
+
+extern void mmu0init(uintptr*);
+extern void mmu0clear(uintptr*);
+extern void mmu1init(void);
+
+extern void putasid(Proc*);
+
+/* clock */
+extern void clockinit(void);
+extern void synccycles(void);
+extern void armtimerset(int);
+
+/* fpu */
+extern void fpuinit(void);
+extern void fpoff(void);
+extern void fpinit(void);
+extern void fpclear(void);
+extern void fpsave(FPsave*);
+extern void fprestore(FPsave*);
+extern void mathtrap(Ureg*);
+
+/* trap */
+extern void trapinit(void);
+extern int userureg(Ureg*);
+extern void evenaddr(uintptr);
+extern void setkernur(Ureg*, Proc*);
+extern void procfork(Proc*);
+extern void procsetup(Proc*);
+extern void procsave(Proc*);
+extern void procrestore(Proc *);
+extern void trap(Ureg*);
+extern void syscall(Ureg*);
+extern void noted(Ureg*, ulong);
+extern void faultarm64(Ureg*);
+extern void dumpstack(void);
+extern void dumpregs(Ureg*);
+
+/* irq */
+extern void intrcpushutdown(void);
+extern void intrsoff(void);
+#define intrenable(i, f, a, b, n)	irqenable((i), (f), (a))
+extern void irqenable(int, void (*)(Ureg*, void*), void*);
+extern int irq(Ureg*);
+extern void fiq(Ureg*);
+
+/* sysreg */
+extern uvlong	sysrd(ulong);
+extern void	syswr(ulong, uvlong);
+
+/* gpio */
+extern void gpiosel(uint, int);
+extern void gpiopull(uint, int);
+extern void gpiopullup(uint);
+extern void gpiopulloff(uint);
+extern void gpiopulldown(uint);
+extern void gpioout(uint, int);
+extern int gpioin(uint);
+extern void gpioselevent(uint, int, int);
+extern int gpiogetevent(uint);
+extern void gpiomeminit(void);
+
+/* arch */
+extern char *cputype2name(char*, int);
+extern void cpuidprint(void);
+extern void uartconsinit(void);
+extern void links(void);
+extern int getncpus(void);
+extern int startcpu(uint);
+extern void okay(int);
+
+/* dma */
+extern uintptr dmaaddr(void*);
+extern void dmastart(int, int, int, void*, void*, int);
+extern int dmawait(int);
+
+/* vcore */
+extern void* fbinit(int set, int *width, int *height, int *depth);
+extern int fbblank(int blank);
+extern void setpower(int dev, int on);
+extern int getpower(int dev);
+extern char* getethermac(void);
+extern uint getboardrev(void);
+extern uint getfirmware(void);
+extern void getramsize(Confmem *mem);
+extern ulong getclkrate(int clkid);
+extern void setclkrate(int clkid, ulong hz);
+extern uint getcputemp(void);
+extern void vgpinit(void);
+extern void vgpset(uint port, int on);
+
+/* bootargs */
+extern void bootargsinit(void);
+extern char *getconf(char *name);
+extern void setconfenv(void);
+extern void writeconf(void);
+
+/* screen */
+extern void screeninit(void);
+
+extern int isaconfig(char*, int, ISAConf*);
--- /dev/null
+++ b/sys/src/9/bcm64/fpu.c
@@ -1,0 +1,92 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+#include "ureg.h"
+#include "sysreg.h"
+
+/* libc */
+extern ulong getfcr(void);
+extern void setfcr(ulong fcr);
+extern ulong getfsr(void);
+extern void setfsr(ulong fsr);
+
+void
+fpuinit(void)
+{
+	fpoff();
+}
+
+void
+fpon(void)
+{
+	syswr(CPACR_EL1, 3<<20);
+}
+
+void
+fpoff(void)
+{
+	syswr(CPACR_EL1, 0<<20);
+}
+
+void
+fpinit(void)
+{
+	fpon();
+	setfcr(0);
+	setfsr(0);
+}
+
+void
+fpclear(void)
+{
+	fpoff();
+}
+
+void
+fpsave(FPsave *p)
+{
+	p->control = getfcr();
+	p->status = getfsr();
+	fpsaveregs(p->regs);
+	fpoff();
+}
+
+void
+fprestore(FPsave *p)
+{
+	fpon();
+	setfcr(p->control);
+	setfsr(p->status);
+	fploadregs(p->regs);
+}
+
+void
+mathtrap(Ureg*)
+{
+	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;
+	}
+}
--- /dev/null
+++ b/sys/src/9/bcm64/init9.s
@@ -1,0 +1,4 @@
+TEXT main(SB), 1, $8
+	MOV	$setSB(SB), R28		/* load the SB */
+	MOV	$boot(SB), R0
+	B	startboot(SB)
--- /dev/null
+++ b/sys/src/9/bcm64/l.s
@@ -1,0 +1,749 @@
+#include "mem.h"
+#include "sysreg.h"
+
+#undef	SYSREG
+#define	SYSREG(op0,op1,Cn,Cm,op2)	SPR(((op0)<<19|(op1)<<16|(Cn)<<12|(Cm)<<8|(op2)<<5))
+
+TEXT _start(SB), 1, $-4
+	MOV	$setSB-KZERO(SB), R28
+	BL	svcmode<>(SB)
+
+	/* use dedicated stack pointer per exception level */
+	MOVWU	$1, R1
+	MSR	R1, SPSel
+
+	BL	mmudisable<>(SB)
+
+	/* invalidate local caches */
+	BL	cachedinv(SB)
+	BL	cacheiinv(SB)
+
+	MOV	$(MACHADDR(0)-KZERO), R27
+	MRS	MPIDR_EL1, R1
+	ANDW	$(MAXMACH-1), R1
+	MOVWU	$MACHSIZE, R2
+	MULW	R1, R2, R2
+	SUB	R2, R27
+
+	ADD	$(MACHSIZE-16), R27, R2
+	MOV	R2, SP
+
+	CBNZ	R1, _startup
+
+	/* clear page table and machs */
+	MOV	$(L1-KZERO), R1
+	MOV	$(MACHADDR(-1)-KZERO), R2
+_zerol1:
+	MOV	ZR, (R1)8!
+	CMP	R1, R2
+	BNE	_zerol1
+
+	/* clear BSS */
+	MOV	$edata-KZERO(SB), R1
+	MOV	$end-KZERO(SB), R2
+_zerobss:
+	MOV	ZR, (R1)8!
+	CMP	R1, R2
+	BNE	_zerobss
+
+	/* setup page tables */
+	MOV	$(L1-KZERO), R0
+	BL	mmu0init(SB)
+
+	BL	cachedwbinv(SB)
+	BL	l2cacheuwbinv(SB)
+	SEVL
+_startup:
+	WFE
+	BL	mmuenable<>(SB)
+
+	MOV	$0, R26
+	ORR	$KZERO, R27
+	MSR	R27, TPIDR_EL1
+	MOV	$setSB(SB), R28
+
+	BL	main(SB)
+
+TEXT	stop<>(SB), 1, $-4
+_stop:
+	WFE
+	B	_stop
+
+TEXT sev(SB), 1, $-4
+	SEV
+	WFE
+	RETURN
+
+TEXT PUTC(SB), 1, $-4
+	MOVWU $(0x3F000000+0x215040), R14
+	MOVB R0, (R14)
+	RETURN
+
+TEXT svcmode<>(SB), 1, $-4
+	MSR	$0xF, DAIFSet
+	MRS	CurrentEL, R0
+	ANDW	$(3<<2), R0
+	CMPW	$(1<<2), R0
+	BEQ	el1
+	CMPW	$(2<<2), R0
+	BEQ	el2
+	B	stop<>(SB)
+el2:
+	MOV	$0, R0
+	MSR	R0, MDCR_EL2
+	ISB	$SY
+
+	/* HCR = RW, HCD, SWIO, BSU, FB */
+	MOVWU	$(1<<31 | 1<<29 | 1<<2 | 0<<10 | 0<<9), R0
+	MSR	R0, HCR_EL2
+	ISB	$SY
+
+	/* SCTLR = RES1 */
+	MOVWU	$(3<<4 | 1<<11 | 1<<16 | 1<<18 | 3<<22 | 3<<28), R0
+	ISB	$SY
+	MSR	R0, SCTLR_EL2
+	ISB	$SY
+
+	/* set VMID to zero */
+	MOV	$0, R0
+	MSR	R0, VTTBR_EL2
+	ISB	$SY
+
+	MOVWU	$(0xF<<6 | 4), R0
+	MSR	R0, SPSR_EL2
+	MSR	LR, ELR_EL2
+	ERET
+el1:
+	RETURN
+
+TEXT mmudisable<>(SB), 1, $-4
+#define SCTLRCLR \
+	/* RES0 */	( 3<<30 \
+	/* RES0 */	| 1<<27 \
+	/* UCI */	| 1<<26 \
+	/* EE */	| 1<<25 \
+	/* RES0 */	| 1<<21 \
+	/* E0E */	| 1<<24 \
+	/* WXN */	| 1<<19 \
+	/* nTWE */	| 1<<18 \
+	/* RES0 */	| 1<<17 \
+	/* nTWI */	| 1<<16 \
+	/* UCT */	| 1<<15 \
+	/* DZE */	| 1<<14 \
+	/* RES0 */	| 1<<13 \
+	/* RES0 */	| 1<<10 \
+	/* UMA */	| 1<<9 \
+	/* SA0 */	| 1<<4 \
+	/* SA */	| 1<<3 \
+	/* A */		| 1<<1 )
+#define SCTLRSET \
+	/* RES1 */	( 3<<28 \
+	/* RES1 */	| 3<<22 \
+	/* RES1 */	| 1<<20 \
+	/* RES1 */	| 1<<11 )
+#define SCTLRMMU \
+	/* I */		( 1<<12 \
+	/* C */		| 1<<2 \
+	/* M */		| 1<<0 )
+
+	/* initialise SCTLR, MMU and caches off */
+	ISB	$SY
+	MRS	SCTLR_EL1, R0
+	BIC	$(SCTLRCLR | SCTLRMMU), R0
+	ORR	$SCTLRSET, R0
+	ISB	$SY
+	MSR	R0, SCTLR_EL1
+	ISB	$SY
+
+	B	flushlocaltlb(SB)
+
+TEXT mmuenable<>(SB), 1, $-4
+	/* return to virtual */
+	ORR	$KZERO, LR
+	MOV	LR, -16(RSP)!
+
+	BL	cachedwbinv(SB)
+	BL	flushlocaltlb(SB)
+
+	/* memory attributes */
+#define MAIRINIT \
+	( 0xFF << MA_MEM_WB*8 \
+	| 0x33 << MA_MEM_WT*8 \
+	| 0x44 << MA_MEM_UC*8 \
+	| 0x00 << MA_DEV_nGnRnE*8 \
+	| 0x04 << MA_DEV_nGnRE*8 \
+	| 0x08 << MA_DEV_nGRE*8 \
+	| 0x0C << MA_DEV_GRE*8 )
+	MOV	$MAIRINIT, R1
+	MSR	R1, MAIR_EL1
+	ISB	$SY
+
+	/* translation control */
+#define TCRINIT \
+	/* TBI1 */	( 0<<38 \
+	/* TBI0 */	| 0<<37 \
+	/* AS */	| 0<<36 \
+	/* TG1 */	| (((3<<16|1<<14|2<<12)>>PGSHIFT)&3)<<30 \
+	/* SH1 */	| SHARE_INNER<<28 \
+	/* ORGN1 */	| CACHE_WB<<26 \
+	/* IRGN1 */	| CACHE_WB<<24 \
+	/* EPD1 */	| 0<<23 \
+	/* A1 */	| 0<<22 \
+	/* T1SZ */	| (64-EVASHIFT)<<16 \
+	/* TG0 */	| (((1<<16|2<<14|0<<12)>>PGSHIFT)&3)<<14 \
+	/* SH0 */	| SHARE_INNER<<12 \
+	/* ORGN0 */	| CACHE_WB<<10 \
+	/* IRGN0 */	| CACHE_WB<<8 \
+	/* EPD0 */	| 0<<7 \
+	/* T0SZ */	| (64-EVASHIFT)<<0 )
+	MOV	$TCRINIT, R1
+	MRS	ID_AA64MMFR0_EL1, R2
+	ANDW	$0xF, R2	// IPS
+	ADD	R2<<32, R1
+	MSR	R1, TCR_EL1
+	ISB	$SY
+
+	/* load the page tables */
+	MOV	$(L1TOP-KZERO), R0
+	ISB	$SY
+	MSR	R0, TTBR0_EL1
+	MSR	R0, TTBR1_EL1
+	ISB	$SY
+
+	/* enable MMU and caches */
+	MRS	SCTLR_EL1, R1
+	ORR	$SCTLRMMU, R1
+	ISB	$SY
+	MSR	R1, SCTLR_EL1
+	ISB	$SY
+
+	MOV	RSP, R1
+	ORR	$KZERO, R1
+	MOV	R1, RSP
+	MOV	(RSP)16!, LR
+	B	cacheiinv(SB)
+
+TEXT touser(SB), 1, $-4
+	MSR	$0x3, DAIFSet	// interrupts off
+	MOVWU	$0x10028, R1	// entry
+	MOVWU	$0, R2		// psr
+	MSR	R0, SP_EL0	// sp
+	MSR	R1, ELR_EL1
+	MSR	R2, SPSR_EL1
+	ERET
+
+TEXT cas(SB), 1, $-4
+TEXT cmpswap(SB), 1, $-4
+	MOVW	ov+8(FP), R1
+	MOVW	nv+16(FP), R2
+_cas1:
+	LDXRW	(R0), R3
+	CMP	R3, R1
+	BNE	_cas0
+	STXRW	R2, (R0), R4
+	CBNZ	R4, _cas1
+	MOVW	$1, R0
+	DMB	$ISH
+	RETURN
+_cas0:
+	CLREX
+	MOVW	$0, R0
+	RETURN
+
+TEXT tas(SB), 1, $-4
+TEXT _tas(SB), 1, $-4
+	MOVW	$0xdeaddead, R2
+_tas1:
+	LDXRW	(R0), R1
+	STXRW	R2, (R0), R3
+	CBNZ	R3, _tas1
+	MOVW	R1, R0
+
+TEXT coherence(SB), 1, $-4
+	DMB	$ISH
+	RETURN
+
+TEXT islo(SB), 1, $-4
+	MRS	DAIF, R0
+	AND	$(0x2<<6), R0
+	EOR	$(0x2<<6), R0
+	RETURN
+
+TEXT splhi(SB), 1, $-4
+	MRS	DAIF, R0
+	MSR	$0x2, DAIFSet
+	RETURN
+
+TEXT splfhi(SB), 1, $-4
+	MRS	DAIF, R0
+	MSR	$0x3, DAIFSet
+	RETURN
+
+TEXT spllo(SB), 1, $-4
+	MSR	$0x3, DAIFClr
+	RETURN
+
+TEXT splflo(SB), 1, $-4
+	MSR	$0x1, DAIFClr
+	RETURN
+
+TEXT splx(SB), 1, $-4
+	MSR	R0, DAIF
+	RETURN
+
+TEXT cycles(SB), 1, $-4
+TEXT lcycles(SB), 1, $-4
+	MRS	PMCCNTR_EL0, R0
+	RETURN
+
+TEXT setlabel(SB), 1, $-4
+	MOV	LR, 8(R0)
+	MOV	SP, R1
+	MOV	R1, 0(R0)
+	MOVW	$0, R0
+	RETURN
+
+TEXT gotolabel(SB), 1, $-4
+	MOV	8(R0), LR	/* link */
+	MOV	0(R0), R1	/* sp */
+	MOV	R1, SP
+	MOVW	$1, R0
+	RETURN
+
+TEXT returnto(SB), 1, $-4
+	MOV	R0, 0(SP)
+	RETURN
+
+TEXT getfar(SB), 1, $-4
+	MRS	FAR_EL1, R0
+	RETURN
+
+TEXT setttbr(SB), 1, $-4
+	DSB	$ISHST
+	MSR	R0, TTBR0_EL1
+	DSB	$ISH
+	ISB	$SY
+
+	B	cacheiinv(SB)
+
+TEXT magic(SB), 1, $-4
+	DSB	$SY
+	ISB	$SY
+	DSB	$SY
+	ISB	$SY
+	DSB	$SY
+	ISB	$SY
+	DSB	$SY
+	ISB	$SY
+	RETURN
+
+/*
+ * TLB maintenance operations.
+ * these broadcast to all cpu's in the cluser
+ * (inner sharable domain).
+ */
+TEXT flushasidva(SB), 1, $-4
+TEXT tlbivae1is(SB), 1, $-4
+	DSB	$ISHST
+	TLBI	R0, 0,8,3,1	/* VAE1IS */
+	DSB	$ISH
+	ISB	$SY
+	RETURN
+
+TEXT flushasidvall(SB), 1, $-4
+TEXT tlbivale1is(SB), 1, $-4
+	DSB	$ISHST
+	TLBI	R0, 0,8,3,5	/* VALE1IS */
+	DSB	$ISH
+	ISB	$SY
+	RETURN
+
+TEXT flushasid(SB), 1, $-4
+TEXT tlbiaside1is(SB), 1, $-4
+	DSB	$ISHST
+	TLBI	R0, 0,8,3,2	/* ASIDE1IS */
+	DSB	$ISH
+	ISB	$SY
+	RETURN
+
+TEXT flushtlb(SB), 1, $-4
+TEXT tlbivmalle1is(SB), 1, $-4
+	DSB	$ISHST
+	TLBI	R0, 0,8,3,0	/* VMALLE1IS */
+	DSB	$ISH
+	ISB	$SY
+	RETURN
+
+/*
+ * flush the tlb of this cpu. no broadcast.
+ */
+TEXT flushlocaltlb(SB), 1, $-4
+TEXT tlbivmalle1(SB), 1, $-4
+	DSB	$NSHST
+	TLBI	R0, 0,8,7,0	/* VMALLE1 */
+	DSB	$NSH
+	ISB	$SY
+	RETURN
+
+TEXT fpsaveregs(SB), 1, $-4
+	WORD	$(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 0)  /* MOV { V0, V1, V2, V3  }, (R0)64! */
+	WORD	$(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 4)  /* MOV { V4, V5, V6, V7  }, (R0)64! */
+	WORD	$(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 8)  /* MOV { V8, V9, V10,V11 }, (R0)64! */
+	WORD	$(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 12) /* MOV { V12,V13,V14,V15 }, (R0)64! */
+	WORD	$(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 16) /* MOV { V16,V17,V18,V19 }, (R0)64! */
+	WORD	$(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 20) /* MOV { V20,V21,V22,V23 }, (R0)64! */
+	WORD	$(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 24) /* MOV { V24,V25,V26,V27 }, (R0)64! */
+	WORD	$(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 28) /* MOV { V28,V29,V30,V31 }, (R0)64! */
+	RETURN
+
+TEXT fploadregs(SB), 1, $-4
+	WORD	$(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 0)  /* MOV (R0)64!, { V0, V1, V2, V3  } */
+	WORD	$(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 4)  /* MOV (R0)64!, { V4, V5, V6, V7  } */
+	WORD	$(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 8)  /* MOV (R0)64!, { V8, V9, V10,V11 } */
+	WORD	$(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 12) /* MOV (R0)64!, { V12,V13,V14,V15 } */
+	WORD	$(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 16) /* MOV (R0)64!, { V16,V17,V18,V19 } */
+	WORD	$(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 20) /* MOV (R0)64!, { V20,V21,V22,V23 } */
+	WORD	$(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 24) /* MOV (R0)64!, { V24,V25,V26,V27 } */
+	WORD	$(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 28) /* MOV (R0)64!, { V28,V29,V30,V31 } */
+	RETURN
+
+// syscall or trap from EL0
+TEXT vsys0(SB), 1, $-4
+	LSRW	$26, R0, R17	// ec
+	CMPW	$0x15, R17	// SVC trap?
+	BNE	_itsatrap	// nope.
+
+	MOV	R26, 224(RSP)	// special
+	MOV	R27, 232(RSP)	// special
+	MOV	R28, 240(RSP)	// sb
+	MOV	R29, 248(RSP)	// special
+
+	MRS	SP_EL0, R1
+	MRS	ELR_EL1, R2
+	MRS	SPSR_EL1, R3
+
+	MOV	R0, 288(RSP)	// type
+	MOV	R1, 264(RSP)	// sp
+	MOV	R2, 272(RSP)	// pc
+	MOV	R3, 280(RSP)	// psr
+
+	MOV	$setSB(SB), R28
+	MRS	TPIDR_EL1, R27
+	MOV	16(R27), R26
+
+	ADD	$16, RSP, R0	// ureg
+	BL	syscall(SB)
+
+TEXT forkret(SB), 1, $-4
+	MSR	$0x3, DAIFSet	// interrupts off
+
+	ADD	$16, RSP, R0	// ureg
+
+	MOV	16(RSP), R0	// ret
+	MOV	264(RSP), R1	// sp
+	MOV	272(RSP), R2	// pc
+	MOV	280(RSP), R3	// psr
+
+	MSR	R1, SP_EL0
+	MSR	R2, ELR_EL1
+	MSR	R3, SPSR_EL1
+
+	MOV	224(RSP), R26	// special
+	MOV	232(RSP), R27	// special
+	MOV	240(RSP), R28	// sb
+	MOV	248(RSP), R29	// special
+
+	MOV	256(RSP), R30	// link
+
+	ADD	$TRAPFRAMESIZE, RSP
+	ERET
+
+TEXT itsatrap<>(SB), 1, $-4
+_itsatrap:
+	MOV	R1, 24(RSP)
+	MOV	R2, 32(RSP)
+	MOV	R3, 40(RSP)
+	MOV	R4, 48(RSP)
+	MOV	R5, 56(RSP)
+	MOV	R6, 64(RSP)
+	MOV	R7, 72(RSP)
+	MOV	R8, 80(RSP)
+	MOV	R9, 88(RSP)
+	MOV	R10, 96(RSP)
+	MOV	R11, 104(RSP)
+	MOV	R12, 112(RSP)
+	MOV	R13, 120(RSP)
+	MOV	R14, 128(RSP)
+	MOV	R15, 136(RSP)
+	MOV	R16, 144(RSP)
+
+	MOV	R18, 160(RSP)
+	MOV	R19, 168(RSP)
+	MOV	R20, 176(RSP)
+	MOV	R21, 184(RSP)
+	MOV	R22, 192(RSP)
+	MOV	R23, 200(RSP)
+	MOV	R24, 208(RSP)
+	MOV	R25, 216(RSP)
+
+// trap/irq/fiq/serr from EL0
+TEXT vtrap0(SB), 1, $-4
+	MOV	R26, 224(RSP)	// special
+	MOV	R27, 232(RSP)	// special
+	MOV	R28, 240(RSP)	// sb
+	MOV	R29, 248(RSP)	// special
+
+	MRS	SP_EL0, R1
+	MRS	ELR_EL1, R2
+	MRS	SPSR_EL1, R3
+
+	MOV	R0, 288(RSP)	// type
+	MOV	R1, 264(RSP)	// sp
+	MOV	R2, 272(RSP)	// pc
+	MOV	R3, 280(RSP)	// psr
+
+	MOV	$setSB(SB), R28
+	MRS	TPIDR_EL1, R27
+	MOV	16(R27), R26
+
+	ADD	$16, RSP, R0	// ureg
+	BL	trap(SB)
+
+TEXT noteret(SB), 1, $-4
+	MSR	$0x3, DAIFSet	// interrupts off
+
+	ADD	$16, RSP, R0	// ureg
+
+	MOV	264(RSP), R1	// sp
+	MOV	272(RSP), R2	// pc
+	MOV	280(RSP), R3	// psr
+
+	MSR	R1, SP_EL0
+	MSR	R2, ELR_EL1
+	MSR	R3, SPSR_EL1
+
+	MOV	224(RSP), R26	// special
+	MOV	232(RSP), R27	// special
+	MOV	240(RSP), R28	// sb
+	MOV	248(RSP), R29	// special
+
+_intrreturn:
+	MOV	16(RSP), R0
+	MOV	24(RSP), R1
+	MOV	32(RSP), R2
+	MOV	40(RSP), R3
+	MOV	48(RSP), R4
+	MOV	56(RSP), R5
+	MOV	64(RSP), R6
+	MOV	72(RSP), R7
+	MOV	80(RSP), R8
+	MOV	88(RSP), R9
+	MOV	96(RSP), R10
+	MOV	104(RSP), R11
+	MOV	112(RSP), R12
+	MOV	120(RSP), R13
+	MOV	128(RSP), R14
+	MOV	136(RSP), R15
+	MOV	144(RSP), R16
+	MOV	152(RSP), R17
+	MOV	160(RSP), R18
+	MOV	168(RSP), R19
+	MOV	176(RSP), R20
+	MOV	184(RSP), R21
+	MOV	192(RSP), R22
+	MOV	200(RSP), R23
+	MOV	208(RSP), R24
+	MOV	216(RSP), R25
+
+	MOV	256(RSP), R30	// link
+
+	ADD	$TRAPFRAMESIZE, RSP
+	ERET
+
+// irq/fiq/trap/serr from EL1
+TEXT vtrap1(SB), 1, $-4
+	MOV	R29, 248(RSP)	// special
+
+	ADD	$TRAPFRAMESIZE, RSP, R1
+	MRS	ELR_EL1, R2
+	MRS	SPSR_EL1, R3
+
+	MOV	R0, 288(RSP)	// type
+	MOV	R1, 264(RSP)	// sp
+	MOV	R2, 272(RSP)	// pc
+	MOV	R3, 280(RSP)	// psr
+
+	ADD	$16, RSP, R0	// ureg
+	BL	trap(SB)
+
+	MSR	$0x3, DAIFSet	// interrupts off
+
+	MOV	272(RSP), R2	// pc
+	MOV	280(RSP), R3	// psr
+
+	MSR	R2, ELR_EL1
+	MSR	R3, SPSR_EL1
+
+	MOV	248(RSP), R29	// special
+	B	_intrreturn	
+
+// vector tables
+TEXT vsys(SB), 1, $-4
+	SUB	$TRAPFRAMESIZE, RSP
+
+	MOV	R0, 16(RSP)
+	MOV	R30, 256(RSP)	// link
+
+	MOV	R17, 152(RSP)	// temp
+
+	MRS	ESR_EL1, R0	// type
+
+_vsyspatch:
+	B	_vsyspatch	// branch to vsys0() patched in
+
+TEXT vtrap(SB), 1, $-4
+	SUB	$TRAPFRAMESIZE, RSP
+
+	MOV	R0, 16(RSP)
+	MOV	R1, 24(RSP)
+	MOV	R2, 32(RSP)
+	MOV	R3, 40(RSP)
+	MOV	R4, 48(RSP)
+	MOV	R5, 56(RSP)
+	MOV	R6, 64(RSP)
+	MOV	R7, 72(RSP)
+	MOV	R8, 80(RSP)
+	MOV	R9, 88(RSP)
+	MOV	R10, 96(RSP)
+	MOV	R11, 104(RSP)
+	MOV	R12, 112(RSP)
+	MOV	R13, 120(RSP)
+	MOV	R14, 128(RSP)
+	MOV	R15, 136(RSP)
+	MOV	R16, 144(RSP)
+	MOV	R17, 152(RSP)
+	MOV	R18, 160(RSP)
+	MOV	R19, 168(RSP)
+	MOV	R20, 176(RSP)
+	MOV	R21, 184(RSP)
+	MOV	R22, 192(RSP)
+	MOV	R23, 200(RSP)
+	MOV	R24, 208(RSP)
+	MOV	R25, 216(RSP)
+
+	MOV	R30, 256(RSP)	// link
+
+	MRS	ESR_EL1, R0	// type
+
+_vtrappatch:
+	B	_vtrappatch	// branch to vtrapX() patched in
+
+TEXT virq(SB), 1, $-4
+	SUB	$TRAPFRAMESIZE, RSP
+
+	MOV	R0, 16(RSP)
+	MOV	R1, 24(RSP)
+	MOV	R2, 32(RSP)
+	MOV	R3, 40(RSP)
+	MOV	R4, 48(RSP)
+	MOV	R5, 56(RSP)
+	MOV	R6, 64(RSP)
+	MOV	R7, 72(RSP)
+	MOV	R8, 80(RSP)
+	MOV	R9, 88(RSP)
+	MOV	R10, 96(RSP)
+	MOV	R11, 104(RSP)
+	MOV	R12, 112(RSP)
+	MOV	R13, 120(RSP)
+	MOV	R14, 128(RSP)
+	MOV	R15, 136(RSP)
+	MOV	R16, 144(RSP)
+	MOV	R17, 152(RSP)
+	MOV	R18, 160(RSP)
+	MOV	R19, 168(RSP)
+	MOV	R20, 176(RSP)
+	MOV	R21, 184(RSP)
+	MOV	R22, 192(RSP)
+	MOV	R23, 200(RSP)
+	MOV	R24, 208(RSP)
+	MOV	R25, 216(RSP)
+
+	MOV	R30, 256(RSP)	// link
+
+	MOV	$(1<<32), R0	// type irq
+
+_virqpatch:
+	B	_virqpatch	// branch to vtrapX() patched in
+
+TEXT vfiq(SB), 1, $-4
+	SUB	$TRAPFRAMESIZE, RSP
+
+	MOV	R0, 16(RSP)
+	MOV	R1, 24(RSP)
+	MOV	R2, 32(RSP)
+	MOV	R3, 40(RSP)
+	MOV	R4, 48(RSP)
+	MOV	R5, 56(RSP)
+	MOV	R6, 64(RSP)
+	MOV	R7, 72(RSP)
+	MOV	R8, 80(RSP)
+	MOV	R9, 88(RSP)
+	MOV	R10, 96(RSP)
+	MOV	R11, 104(RSP)
+	MOV	R12, 112(RSP)
+	MOV	R13, 120(RSP)
+	MOV	R14, 128(RSP)
+	MOV	R15, 136(RSP)
+	MOV	R16, 144(RSP)
+	MOV	R17, 152(RSP)
+	MOV	R18, 160(RSP)
+	MOV	R19, 168(RSP)
+	MOV	R20, 176(RSP)
+	MOV	R21, 184(RSP)
+	MOV	R22, 192(RSP)
+	MOV	R23, 200(RSP)
+	MOV	R24, 208(RSP)
+	MOV	R25, 216(RSP)
+
+	MOV	R30, 256(RSP)	// link
+	MOV	$(2<<32), R0	// type fiq
+
+_vfiqpatch:
+	B	_vfiqpatch	// branch to vtrapX() patched in
+
+TEXT vserr(SB), 1, $-4
+	SUB	$TRAPFRAMESIZE, RSP
+
+	MOV	R0, 16(RSP)
+	MOV	R1, 24(RSP)
+	MOV	R2, 32(RSP)
+	MOV	R3, 40(RSP)
+	MOV	R4, 48(RSP)
+	MOV	R5, 56(RSP)
+	MOV	R6, 64(RSP)
+	MOV	R7, 72(RSP)
+	MOV	R8, 80(RSP)
+	MOV	R9, 88(RSP)
+	MOV	R10, 96(RSP)
+	MOV	R11, 104(RSP)
+	MOV	R12, 112(RSP)
+	MOV	R13, 120(RSP)
+	MOV	R14, 128(RSP)
+	MOV	R15, 136(RSP)
+	MOV	R16, 144(RSP)
+	MOV	R17, 152(RSP)
+	MOV	R18, 160(RSP)
+	MOV	R19, 168(RSP)
+	MOV	R20, 176(RSP)
+	MOV	R21, 184(RSP)
+	MOV	R22, 192(RSP)
+	MOV	R23, 200(RSP)
+	MOV	R24, 208(RSP)
+	MOV	R25, 216(RSP)
+
+	MOV	R30, 256(RSP)	// link
+
+	MRS	ESR_EL1, R0
+	ORR	$(3<<32), R0	// type
+_vserrpatch:
+	B	_vserrpatch	// branch to vtrapX() patched in
--- /dev/null
+++ b/sys/src/9/bcm64/main.c
@@ -1,0 +1,337 @@
+#include "u.h"
+#include "tos.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+#include "init.h"
+#include "sysreg.h"
+
+#include <pool.h>
+#include <libsec.h>
+
+Conf conf;
+ulong memsize = GiB;
+
+/*
+ *  starting place for first process
+ */
+void
+init0(void)
+{
+	char buf[2*KNAMELEN], **sp;
+
+	up->nerrlab = 0;
+	spllo();
+
+	/*
+	 * These are o.k. because rootinit is null.
+	 * Then early kproc's will have a root and dot.
+	 */
+	up->slash = namec("#/", Atodir, 0, 0);
+	pathclose(up->slash->path);
+	up->slash->path = newpath("/");
+	up->dot = cclone(up->slash);
+	chandevinit();
+
+	if(!waserror()){
+		snprint(buf, sizeof(buf), "%s %s", "ARM64", conffile);
+		ksetenv("terminal", buf, 0);
+		ksetenv("cputype", "arm64", 0);
+		if(cpuserver)
+			ksetenv("service", "cpu", 0);
+		else
+			ksetenv("service", "terminal", 0);
+		snprint(buf, sizeof(buf), "-a %s", getethermac());
+		ksetenv("etherargs", buf, 0);
+
+		/* convert plan9.ini variables to #e and #ec */
+		setconfenv();
+		poperror();
+	}
+	kproc("alarm", alarmkproc, 0);
+
+	sp = (char**)(USTKTOP-sizeof(Tos) - 8 - sizeof(sp[0])*4);
+	sp[3] = sp[2] = sp[1] = nil;
+	strcpy(sp[1] = (char*)&sp[4], "boot");
+	sp[0] = (void*)&sp[1];
+
+	touser((uintptr)sp);
+
+	assert(0);			/* shouldn't have returned */
+}
+
+/*
+ *  create the first process
+ */
+void
+userinit(void)
+{
+	Proc *p;
+	Segment *s;
+	KMap *k;
+	Page *pg;
+
+	/* no processes yet */
+	up = nil;
+
+	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);
+
+	/*
+	 * Kernel Stack
+	 */
+	p->sched.pc = (uintptr)init0;
+	p->sched.sp = (uintptr)p->kstack+KSTACK-sizeof(up->s.args)-sizeof(uintptr);
+	p->sched.sp = STACKALIGN(p->sched.sp);
+	*(void**)p->sched.sp = kproc; // fake
+
+	/*
+	 * User Stack
+	 *
+	 * Technically, newpage can't be called here because it
+	 * should only be called when in a user context as it may
+	 * try to sleep if there are no pages available, but that
+	 * shouldn't be the case here.
+	 */
+	s = newseg(SG_STACK, USTKTOP-USTKSIZE, USTKSIZE/BY2PG);
+	s->flushme++;
+	p->seg[SSEG] = s;
+	pg = newpage(1, 0, USTKTOP-BY2PG);
+	segpage(s, pg);
+	k = kmap(pg);
+	memset((void*)VA(k), 0, BY2PG);
+	kunmap(k);
+
+	/*
+	 * Text
+	 */
+	s = newseg(SG_TEXT, UTZERO, 1);
+	p->seg[TSEG] = s;
+	pg = newpage(1, 0, UTZERO);
+	pg->txtflush = ~0;
+	segpage(s, pg);
+	k = kmap(s->map[0]->pages[0]);
+	memmove((void*)VA(k), initcode, sizeof initcode);
+	kunmap(k);
+
+	ready(p);
+}
+
+void
+confinit(void)
+{
+	int i, userpcnt;
+	ulong kpages;
+	uintptr pa;
+	char *p;
+
+	if(p = getconf("service")){
+		if(strcmp(p, "cpu") == 0)
+			cpuserver = 1;
+		else if(strcmp(p,"terminal") == 0)
+			cpuserver = 0;
+	}
+
+	if(p = getconf("*kernelpercent"))
+		userpcnt = 100 - strtol(p, 0, 0);
+	else
+		userpcnt = 0;
+
+	if((p = getconf("*maxmem")) != nil){
+		memsize = strtoul(p, 0, 0) - PHYSDRAM;
+		if (memsize < 16*MB)		/* sanity */
+			memsize = 16*MB;
+	}
+
+	getramsize(&conf.mem[0]);
+	if(conf.mem[0].limit == 0){
+		conf.mem[0].base = PHYSDRAM;
+		conf.mem[0].limit = PHYSDRAM + memsize;
+	}else if(p != nil)
+		conf.mem[0].limit = conf.mem[0].base + memsize;
+
+	conf.npage = 0;
+	pa = PADDR(PGROUND((uintptr)end));
+
+	/*
+	 *  we assume that the kernel is at the beginning of one of the
+	 *  contiguous chunks of memory and fits therein.
+	 */
+	for(i=0; i<nelem(conf.mem); i++){
+		/* take kernel out of allocatable space */
+		if(pa > conf.mem[i].base && pa < conf.mem[i].limit)
+			conf.mem[i].base = pa;
+
+		conf.mem[i].npage = (conf.mem[i].limit - conf.mem[i].base)/BY2PG;
+		conf.npage += conf.mem[i].npage;
+	}
+
+	if(userpcnt < 10)
+		userpcnt = 60 + cpuserver*10;
+	kpages = conf.npage - (conf.npage*userpcnt)/100;
+
+	/*
+	 * can't go past the end of virtual memory
+	 * (uintptr)-KZERO is 2^32 - KZERO
+	 */
+	if(kpages > ((uintptr)-KZERO)/BY2PG)
+		kpages = ((uintptr)-KZERO)/BY2PG;
+
+	conf.upages = conf.npage - kpages;
+	conf.ialloc = (kpages/2)*BY2PG;
+
+	conf.nmach = getncpus();
+
+	/* set up other configuration parameters */
+	conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
+	if(cpuserver)
+		conf.nproc *= 3;
+	if(conf.nproc > 2000)
+		conf.nproc = 2000;
+	conf.nswap = conf.npage*3;
+	conf.nswppo = 4096;
+	conf.nimage = 200;
+
+	conf.copymode = conf.nmach > 1;
+
+	/*
+	 * Guess how much is taken by the large permanent
+	 * datastructures. Mntcache and Mntrpc are not accounted for.
+	 */
+	kpages = conf.npage - conf.upages;
+	kpages *= BY2PG;
+	kpages -= conf.upages*sizeof(Page)
+		+ conf.nproc*sizeof(Proc)
+		+ conf.nimage*sizeof(Image)
+		+ conf.nswap
+		+ conf.nswppo*sizeof(Page*);
+	mainmem->maxsize = kpages;
+	if(!cpuserver)
+		/*
+		 * give terminals lots of image memory, too; the dynamic
+		 * allocation will balance the load properly, hopefully.
+		 * be careful with 32-bit overflow.
+		 */
+		imagmem->maxsize = kpages;
+}
+
+void
+machinit(void)
+{
+	m->ticks = 1;
+	m->perf.period = 1;
+	active.machs[m->machno] = 1;
+}
+
+void
+mpinit(void)
+{
+	extern void _start(void);
+	int i;
+
+	for(i = 0; i < MAXMACH; i++)
+		((uintptr*)SPINTABLE)[i] = 0;
+
+	for(i = 1; i < conf.nmach; i++)
+		MACHP(i)->machno = i;
+
+	cachedwbinv();
+
+	for(i = 1; i < conf.nmach; i++)
+		((uintptr*)SPINTABLE)[i] = PADDR(_start);
+
+	cachedwbinvse((void*)SPINTABLE, MAXMACH*8);
+	sev();
+	delay(100);
+	sev();
+	synccycles();
+}
+
+void
+idlehands(void)
+{
+}
+
+void
+main(void)
+{
+	machinit();
+	if(m->machno){
+		trapinit();
+		fpuinit();
+		clockinit();
+		cpuidprint();
+		synccycles();
+		timersinit();
+		flushtlb();
+		mmu1init();
+		delay(4000);	/* usb initilization is busted multicore, let cpu0 do it */
+		m->ticks = MACHP(0)->ticks;
+		schedinit();
+		return;
+	}
+	bootargsinit();
+	confinit();
+	xinit();
+	printinit();
+	fmtinstall('H', encodefmt);
+	quotefmtinstall();
+	uartconsinit();
+	screeninit();
+	print("\nPlan 9\n");
+	xsummary();
+
+	/* set clock rate to arm_freq from config.txt */
+	setclkrate(ClkArm, 0);
+
+	trapinit();
+	fpuinit();
+	clockinit();
+	cpuidprint();
+	timersinit();
+	pageinit();
+	procinit0();
+	initseg();
+	links();
+	chandevreset();
+	userinit();
+	mpinit();
+	mmu0clear((uintptr*)L1);
+	flushtlb();
+	mmu1init();
+	schedinit();
+}
+
+void
+exit(int)
+{
+	cpushutdown();
+	for(;;);
+}
+
+void
+reboot(void*, void*, ulong)
+{
+	error(Egreg);
+}
+
+/*
+ * stub for ../omap/devether.c
+ */
+int
+isaconfig(char *, int, ISAConf *)
+{
+	return 0;
+}
--- /dev/null
+++ b/sys/src/9/bcm64/mem.h
@@ -1,0 +1,139 @@
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+#define KiB		1024u			/* Kibi 0x0000000000000400 */
+#define MiB		1048576u		/* Mebi 0x0000000000100000 */
+#define GiB		1073741824u		/* Gibi 000000000040000000 */
+
+#define	HOWMANY(x,y)	(((x)+((y)-1))/(y))
+#define	ROUNDUP(x,y)	(HOWMANY((x),(y))*(y))
+#define	PGROUND(s)	ROUNDUP(s, BY2PG)
+#define	ROUND(s, sz)	(((s)+(sz-1))&~(sz-1))
+
+/*
+ * Sizes:
+ * 	L0	L1	L2	L3
+ *	4K	2M	1G	512G
+ *	16K	32M	64G	128T
+ *	64K	512M	4T	-
+ */
+#define	PGSHIFT		12		/* log(BY2PG) */
+#define	BY2PG		(1ULL<<PGSHIFT)	/* bytes per page */
+
+/* effective virtual address space */
+#define EVASHIFT	36
+#define EVAMASK		((1ULL<<EVASHIFT)-1)
+
+#define PTSHIFT		(PGSHIFT-3)
+#define PTLEVELS	HOWMANY(EVASHIFT-PGSHIFT, PTSHIFT)
+#define PTLX(v, l)	((((v) & EVAMASK) >> (PGSHIFT + (l)*PTSHIFT)) & ((1 << PTSHIFT)-1))
+#define PGLSZ(l)	(1ULL << (PGSHIFT + (l)*PTSHIFT))
+
+#define PTL1X(v, l)	(L1TABLEX(v, l) | PTLX(v, l))
+#define L1TABLEX(v, l)	(L1TABLE(v, l) << PTSHIFT)
+#define L1TABLES	HOWMANY(-KZERO, PGLSZ(2))
+#define L1TABLE(v, l)	(L1TABLES-1 - ((PTLX(v, 2) % L1TABLES) >> (((l)-1)*PTSHIFT)) + (l)-1)
+#define L1TOPSIZE	(1ULL << (EVASHIFT - PTLEVELS*PTSHIFT))
+
+#define	MAXMACH		4			/* max # cpus system can run */
+#define	MACHSIZE	(8*KiB)
+
+#define KSTACK		(8*KiB)
+#define STACKALIGN(sp)	((sp) & ~7)		/* bug: assure with alloc */
+#define TRAPFRAMESIZE	(38*8)
+
+/*
+ * Address spaces.
+ * KTZERO is used by kprof and dumpstack (if any).
+ *
+ * KZERO is mapped to physical 0 (start of ram).
+ */
+
+#define	KZERO		0xFFFFFFFF80000000ULL	/* kernel address space */
+
+#define SPINTABLE	(KZERO+0xd8)
+#define CONFADDR	(KZERO+0x100)
+#define	REBOOTADDR	(0x1c00)		/* reboot code - physical address */
+#define	VCBUFFER	(KZERO+0x3400)		/* videocore mailbox buffer */
+
+#define L1		(L1TOP-L1SIZE)
+#define L1SIZE		((L1TABLES+PTLEVELS-3)*BY2PG)
+#define L1TOP		((MACHADDR(MAXMACH-1)-L1TOPSIZE)&-BY2PG)
+
+#define MACHADDR(n)	(KTZERO-((n)+1)*MACHSIZE)
+
+#define	KTZERO		(KZERO+0x80000)		/* kernel text start */
+#define FRAMEBUFFER	0xFFFFFFFFC0000000ULL
+#define VIRTIO		0xFFFFFFFFE0000000ULL	/* i/o registers */
+#define	ARMLOCAL	(VIRTIO+IOSIZE)
+#define	VGPIO		0			/* virtual gpio for pi3 ACT LED */
+
+#define	UZERO		0ULL			/* user segment */
+#define	UTZERO		(UZERO+0x10000)		/* user text start */
+#define	USTKTOP		((EVAMASK>>1)-0xFFFF)	/* user segment end +1 */
+#define	USTKSIZE	(16*1024*1024)		/* user stack size */
+
+#define BLOCKALIGN	64			/* only used in allocb.c */
+
+/*
+ * Sizes
+ */
+#define BI2BY		8			/* bits per byte */
+#define BY2SE		4
+#define BY2WD		8
+#define BY2V		8			/* only used in xalloc.c */
+
+#define	PTEMAPMEM	(1024*1024)
+#define	PTEPERTAB	(PTEMAPMEM/BY2PG)
+#define	SEGMAPSIZE	1984
+#define	SSEGMAPSIZE	16
+#define	PPN(x)		((x)&~(BY2PG-1))
+
+#define SHARE_NONE	0
+#define SHARE_OUTER	2
+#define SHARE_INNER	3
+
+#define CACHE_UC	0
+#define CACHE_WB	1
+#define CACHE_WT	2
+#define CACHE_WB_NA	3
+
+#define MA_MEM_WB	0
+#define MA_MEM_WT	1
+#define MA_MEM_UC	2
+#define MA_DEV_nGnRnE	3
+#define MA_DEV_nGnRE	4
+#define MA_DEV_nGRE	5
+#define MA_DEV_GRE	6
+
+#define	PTEVALID	1
+#define PTEBLOCK	0
+#define PTETABLE	2
+#define PTEPAGE		2
+
+#define PTEMA(x)	((x)<<2)
+#define PTEAP(x)	((x)<<6)
+#define PTESH(x)	((x)<<8)
+
+#define PTEAF		(1<<10)
+#define PTENG		(1<<11)
+
+#define PTEKERNEL	PTEAP(0)
+#define PTEUSER		PTEAP(1)
+#define PTEWRITE	PTEAP(0)
+#define PTERONLY	PTEAP(2)
+
+#define PTEWT		PTEMA(MA_MEM_WT)
+#define	PTEUNCACHED	PTEMA(MA_MEM_UC)
+#define	PTEDEVICE	PTEMA(MA_DEV_nGnRnE)
+
+/*
+ * Physical machine information from here on.
+ *	PHYS addresses as seen from the arm cpu.
+ *	BUS  addresses as seen from the videocore gpu.
+ */
+#define	PHYSDRAM	0
+#define	IOSIZE		(16*MiB)
+
+#define MIN(a, b)	((a) < (b)? (a): (b))
+#define MAX(a, b)	((a) > (b)? (a): (b))
--- /dev/null
+++ b/sys/src/9/bcm64/mkfile
@@ -1,0 +1,146 @@
+CONF=pi3
+CONFLIST=pi3
+EXTRACOPIES=
+
+loadaddr=0xffffffff80080000
+
+objtype=arm64
+</$objtype/mkfile
+p=9
+
+OS=7
+
+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\
+	rdb.$O\
+	rebootcmd.$O\
+	segment.$O\
+	syscallfmt.$O\
+	sysfile.$O\
+	sysproc.$O\
+	taslock.$O\
+	tod.$O\
+	xalloc.$O\
+
+OBJ=\
+	l.$O\
+	cache.v8.$O\
+	bootargs.$O\
+	clock.$O\
+	fpu.$O\
+	irq.$O\
+	main.$O\
+	mmu.$O\
+	sysreg.$O\
+	random.$O\
+	trap.$O\
+	$CONF.root.$O\
+	$CONF.rootc.$O\
+	$DEVS\
+	$PORT\
+
+# HFILES=
+
+LIB=\
+	/$objtype/lib/libmemlayer.a\
+	/$objtype/lib/libmemdraw.a\
+	/$objtype/lib/libdraw.a\
+	/$objtype/lib/libip.a\
+	/$objtype/lib/libsec.a\
+	/$objtype/lib/libmp.a\
+	/$objtype/lib/libc.a\
+
+9:V: $p$CONF s$p$CONF
+
+$p$CONF:DQ:	$CONF.c $OBJ $LIB mkfile
+	$CC $CFLAGS '-DKERNDATE='`{date -n} $CONF.c
+	echo '# linking raw kernel'	# H6: no headers, data segment aligned
+	$LD -l -o $target -H6 -R4096 -T$loadaddr $OBJ $CONF.$O $LIB
+
+s$p$CONF:DQ:	$CONF.$O $OBJ $LIB
+	echo '# linking kernel with symbols'
+	$LD -l -o $target -R4096 -T$loadaddr $OBJ $CONF.$O $LIB
+	size $target
+
+$p$CONF.gz:D:	$p$CONF
+	gzip -9 <$p$CONF >$target
+
+$OBJ: $HFILES
+
+install:V: /$objtype/$p$CONF
+
+/$objtype/$p$CONF:D: $p$CONF s$p$CONF
+	cp -x $p$CONF s$p$CONF /$objtype/ &
+	for(i in $EXTRACOPIES)
+		{ 9fs $i && cp $p$CONF s$p$CONF /n/$i/$objtype && echo -n $i... & }
+	wait
+	echo
+	touch $target
+
+
+REPCC=`{../port/mkfilelist ../bcm}
+^($REPCC)\.$O:R:	'../bcm/\1.c'
+	$CC $CFLAGS -I. -. ../bcm/$stem1.c
+
+<../boot/bootmkfile
+<../port/portmkfile
+<|../port/mkbootrules $CONF
+
+arch.$O clock.$O fpiarm.$O main.$O mmu.$O screen.$O syscall.$O trap.$O: \
+	/$objtype/include/ureg.h
+
+l.$O cache.v8.$O lexception.$O lproc.$O mmu.$O: mem.h
+l.$O cache.v8.$O archbcm3.$O clock.$O fpu.$O trap.$O mmu.$O: sysreg.h
+
+devmouse.$O mouse.$O screen.$O: screen.h
+usbdwc.$O: dwcotg.h ../port/usb.h
+
+io.h:D:	../bcm/io.h
+	echo '#include "../bcm/io.h"' > io.h
+screen.h:D: ../bcm/screen.h
+	echo '#include "../bcm/screen.h"' > screen.h
+dwcotg.h:D: ../bcm/dwcotg.h
+	echo '#include "../bcm/dwcotg.h"' > dwcotg.h
+
+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 /$objtype/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
+
+#reboot.h:D:	rebootcode.s arm.s arm.h mem.h
+#	$AS rebootcode.s
+#	# -T arg is REBOOTADDR
+#	$LD -l -s -T0x1c00 -R4 -o reboot.out rebootcode.$O
+#	{echo 'uchar rebootcode[]={'
+#	 xd -1x reboot.out |
+#		sed -e '1,2d' -e 's/^[0-9a-f]+ //' -e 's/ ([0-9a-f][0-9a-f])/0x\1,/g'
+#	 echo '};'} > reboot.h
+
+errstr.h:D:	../port/mkerrstr ../port/error.h
+	rc ../port/mkerrstr > errstr.h
+
+$CONF.clean:
+	rm -rf $p$CONF s$p$CONF errstr.h reboot.h io.h screen.h dwcotg.h $CONF.c boot$CONF.c
--- /dev/null
+++ b/sys/src/9/bcm64/mmu.c
@@ -1,0 +1,384 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "sysreg.h"
+
+void
+mmu0init(uintptr *l1)
+{
+	uintptr va, pa, pe;
+
+	/* 0 identity map */
+	pe = PHYSDRAM + soc.dramsize;
+	for(pa = PHYSDRAM; pa < pe; pa += PGLSZ(1))
+		l1[PTL1X(pa, 1)] = pa | PTEVALID | PTEBLOCK | PTEWRITE | PTEAF
+			 | PTEKERNEL | PTESH(SHARE_INNER);
+	if(PTLEVELS > 2)
+	for(pa = PHYSDRAM; pa < pe; pa += PGLSZ(2))
+		l1[PTL1X(pa, 2)] = (uintptr)&l1[L1TABLEX(pa, 1)] | PTEVALID | PTETABLE;
+	if(PTLEVELS > 3)
+	for(pa = PHYSDRAM; pa < pe; pa += PGLSZ(3))
+		l1[PTL1X(pa, 3)] = (uintptr)&l1[L1TABLEX(pa, 2)] | PTEVALID | PTETABLE;
+
+	/* KZERO */
+	for(pa = PHYSDRAM, va = KZERO; pa < pe; pa += PGLSZ(1), va += PGLSZ(1))
+		l1[PTL1X(va, 1)] = pa | PTEVALID | PTEBLOCK | PTEWRITE | PTEAF
+			| PTEKERNEL | PTESH(SHARE_INNER);
+	if(PTLEVELS > 2)
+	for(pa = PHYSDRAM, va = KZERO; pa < pe; pa += PGLSZ(2), va += PGLSZ(2))
+		l1[PTL1X(va, 2)] = (uintptr)&l1[L1TABLEX(va, 1)] | PTEVALID | PTETABLE;
+	if(PTLEVELS > 3)
+	for(pa = PHYSDRAM, va = KZERO; pa < pe; pa += PGLSZ(3), va += PGLSZ(3))
+		l1[PTL1X(va, 3)] = (uintptr)&l1[L1TABLEX(va, 2)] | PTEVALID | PTETABLE;
+
+	/* VIRTIO */
+	pe = -VIRTIO + soc.physio;
+	for(pa = soc.physio, va = VIRTIO; pa < pe; pa += PGLSZ(1), va += PGLSZ(1))
+		l1[PTL1X(va, 1)] = pa | PTEVALID | PTEBLOCK | PTEWRITE | PTEAF
+			| PTEKERNEL | PTESH(SHARE_OUTER) | PTEDEVICE;
+	if(PTLEVELS > 2)
+	for(pa = soc.physio, va = VIRTIO; pa < pe; pa += PGLSZ(2), va += PGLSZ(2))
+		l1[PTL1X(va, 2)] = (uintptr)&l1[L1TABLEX(va, 1)] | PTEVALID | PTETABLE;
+	if(PTLEVELS > 3)
+	for(pa = soc.physio, va = VIRTIO; pa < pe; pa += PGLSZ(3), va += PGLSZ(3))
+		l1[PTL1X(va, 3)] = (uintptr)&l1[L1TABLEX(va, 2)] | PTEVALID | PTETABLE;
+}
+
+void
+mmu0clear(uintptr *l1)
+{
+	uintptr va, pa, pe;
+
+	pe = PHYSDRAM + soc.dramsize;
+
+	if(PTLEVELS > 3)
+	for(pa = PHYSDRAM, va = KZERO; pa < pe; pa += PGLSZ(3), va += PGLSZ(3)){
+		if(PTL1X(pa, 3) != PTL1X(va, 3))
+			l1[PTL1X(pa, 3)] = 0;
+	}
+	if(PTLEVELS > 2)
+	for(pa = PHYSDRAM, va = KZERO; pa < pe; pa += PGLSZ(2), va += PGLSZ(2)){
+		if(PTL1X(pa, 2) != PTL1X(va, 2))
+			l1[PTL1X(pa, 2)] = 0;
+	}
+	for(pa = PHYSDRAM, va = KZERO; pa < pe; pa += PGLSZ(1), va += PGLSZ(1)){
+		if(PTL1X(pa, 1) != PTL1X(va, 1))
+			l1[PTL1X(pa, 1)] = 0;
+	}
+}
+
+void
+mmu1init(void)
+{
+	m->mmul1 = mallocalign(L1SIZE+L1TOPSIZE, BY2PG, L1SIZE, 0);
+	if(m->mmul1 == nil)
+		panic("mmu1init: no memory for mmul1");
+	memset(m->mmul1, 0, L1SIZE+L1TOPSIZE);
+	mmuswitch(nil);
+}
+
+uintptr
+paddr(void *va)
+{
+	if((uintptr)va >= KZERO)
+		return (uintptr)va-KZERO;
+	panic("paddr: va=%#p pc=%#p", va, getcallerpc(&va));
+	return 0;
+}
+
+uintptr
+cankaddr(uintptr pa)
+{
+	if(pa < (uintptr)-KZERO)
+		return -KZERO - pa;
+	return 0;
+}
+
+void*
+kaddr(uintptr pa)
+{
+	if(pa < (uintptr)-KZERO)
+		return (void*)(pa + KZERO);
+	panic("kaddr: pa=%#p pc=%#p", pa, getcallerpc(&pa));
+	return nil;
+}
+
+void
+kmapinval(void)
+{
+}
+
+KMap*
+kmap(Page *p)
+{
+	return kaddr(p->pa);
+}
+
+void
+kunmap(KMap*)
+{
+}
+
+uintptr
+mmukmap(uintptr va, uintptr pa, usize size)
+{
+	uintptr a, pe, off;
+
+	if(va == 0)
+		return 0;
+
+	assert((va % PGLSZ(1)) == 0);
+	off = pa % PGLSZ(1);
+	a = va + off;
+	pe = (pa + size + (PGLSZ(1)-1)) & -PGLSZ(1);
+	while(pa < pe){
+		((uintptr*)L1)[PTL1X(va, 1)] = pa | PTEVALID | PTEBLOCK | PTEWRITE | PTEAF
+			| PTEKERNEL | PTESH(SHARE_OUTER) | PTEDEVICE;
+		pa += PGLSZ(1);
+		va += PGLSZ(1);
+	}
+	flushtlb();
+	return a;
+}
+
+static uintptr*
+mmuwalk(uintptr va, int level)
+{
+	uintptr *table, pte;
+	Page *pg;
+	int i, x;
+
+	x = PTLX(va, PTLEVELS-1);
+	table = &m->mmul1[L1TABLEX(va, PTLEVELS-1)];
+	for(i = PTLEVELS-2; i >= level; i--){
+		pte = table[x];
+		if(pte & PTEVALID) {
+			if(pte & (0xFFFFULL<<48))
+				iprint("strange pte %#p va %#p\n", pte, va);
+			pte &= ~(0xFFFFULL<<48 | BY2PG-1);
+			table = KADDR(pte);
+		} else {
+			if(i < 2){
+				pg = up->mmufree;
+				if(pg == nil)
+					return nil;
+				up->mmufree = pg->next;
+				switch(i){
+				case 0:
+					pg->va = va & -PGLSZ(1);
+					if((pg->next = up->mmul1) == nil)
+						up->mmul1tail = pg;
+					up->mmul1 = pg;
+					break;
+				case 1:
+					pg->va = va & -PGLSZ(2);
+					if((pg->next = up->mmul2) == nil)
+						up->mmul2tail = pg;
+					up->mmul2 = pg;
+					break;
+				}
+				memset(KADDR(pg->pa), 0, BY2PG);
+				coherence();
+				table[x] = pg->pa | PTEVALID | PTETABLE;
+				table = KADDR(pg->pa);
+			} else {
+				table[x] = PADDR(&m->mmul1[L1TABLEX(va, 2)]) | PTEVALID | PTETABLE;
+				table = &m->mmul1[L1TABLEX(va, 2)];
+			}
+		}
+		x = PTLX(va, (uintptr)i);
+	}
+	return &table[x];
+}
+
+static Proc *asidlist[256];
+
+static int
+allocasid(Proc *p)
+{
+	static Lock lk;
+	Proc *x;
+	int a;
+
+	lock(&lk);
+	a = p->asid;
+	if(a < 0)
+		a = -a;
+	if(a == 0)
+		a = p->pid;
+	for(;; a++){
+		a %= nelem(asidlist);
+		if(a == 0)
+			continue;	// reserved
+		x = asidlist[a];
+		if(x == p || x == nil || (x->asid < 0 && x->mach == nil))
+			break;
+	}
+	p->asid = a;
+	asidlist[a] = p;
+	unlock(&lk);
+
+	return x != p;
+}
+
+static void
+freeasid(Proc *p)
+{
+	int a;
+
+	a = p->asid;
+	if(a < 0)
+		a = -a;
+	if(a > 0 && asidlist[a] == p)
+		asidlist[a] = nil;
+	p->asid = 0;
+}
+
+void
+putasid(Proc *p)
+{
+	/*
+	 * Prevent the following scenario:
+	 *	pX sleeps on cpuA, leaving its page tables in mmul1
+	 *	pX wakes up on cpuB, and exits, freeing its page tables
+	 *  pY on cpuB allocates a freed page table page and overwrites with data
+	 *  cpuA takes an interrupt, and is now running with bad page tables
+	 * In theory this shouldn't hurt because only user address space tables
+	 * are affected, and mmuswitch will clear mmul1 before a user process is
+	 * dispatched.  But empirically it correlates with weird problems, eg
+	 * resetting of the core clock at 0x4000001C which confuses local timers.
+	 */
+	if(conf.nmach > 1)
+		mmuswitch(nil);
+
+	if(p->asid > 0)
+		p->asid = -p->asid;
+}
+
+void
+putmmu(uintptr va, uintptr pa, Page *pg)
+{
+	uintptr *pte, old;
+	int s;
+
+// iprint("cpu%d: putmmu va %#p asid %d proc %lud %s\n", m->machno, va, up->asid, up->pid, up->text);
+	s = splhi();
+	while((pte = mmuwalk(va, 0)) == nil){
+		spllo();
+		assert(up->mmufree == nil);
+		up->mmufree = newpage(0, nil, 0);
+		splhi();
+	}
+	old = *pte;
+	*pte = 0;
+	if((old & PTEVALID) != 0)
+		flushasidvall((uvlong)up->asid<<48 | va>>12);
+	else
+		flushasidva((uvlong)up->asid<<48 | va>>12);
+	*pte = pa | PTEPAGE | PTEUSER | PTENG | PTEAF | PTESH(SHARE_INNER);
+	if(pg->txtflush & (1UL<<m->machno)){
+		/* pio() sets PG_TXTFLUSH whenever a text pg has been written */
+		cachedwbinvse((void*)KADDR(pg->pa), BY2PG);
+		cacheiinvse((void*)va, BY2PG);
+		pg->txtflush &= ~(1UL<<m->machno);
+	}
+	splx(s);
+}
+
+static void
+mmufree(Proc *p)
+{
+	freeasid(p);
+
+	if(p->mmul1 == nil){
+		assert(p->mmul2 == nil);
+		return;
+	}
+	p->mmul1tail->next = p->mmufree;
+	p->mmufree = p->mmul1;
+	p->mmul1 = p->mmul1tail = nil;
+
+	if(PTLEVELS > 2){
+		p->mmul2tail->next = p->mmufree;
+		p->mmufree = p->mmul2;
+		p->mmul2 = p->mmul2tail = nil;
+	}
+}
+
+void
+mmuswitch(Proc *p)
+{
+	uintptr va;
+	Page *t;
+
+	for(va = UZERO; va < USTKTOP; va += PGLSZ(PTLEVELS-1))
+		m->mmul1[PTL1X(va, PTLEVELS-1)] = 0;
+
+	if(p == nil){
+		setttbr(PADDR(&m->mmul1[L1TABLEX(0, PTLEVELS-1)]));
+		return;
+	}
+
+	if(p->newtlb){
+		mmufree(p);
+		p->newtlb = 0;
+	}
+
+	if(PTLEVELS == 2){
+		for(t = p->mmul1; t != nil; t = t->next){
+			va = t->va;
+			m->mmul1[PTL1X(va, 1)] = t->pa | PTEVALID | PTETABLE;
+		}
+	} else {
+		for(t = p->mmul2; t != nil; t = t->next){
+			va = t->va;
+			m->mmul1[PTL1X(va, 2)] = t->pa | PTEVALID | PTETABLE;
+			if(PTLEVELS > 3)
+				m->mmul1[PTL1X(va, 3)] = PADDR(&m->mmul1[L1TABLEX(va, 2)]) |
+					PTEVALID | PTETABLE;
+		}
+	}
+
+	if(allocasid(p))
+		flushasid((uvlong)p->asid<<48);
+
+// iprint("cpu%d: mmuswitch asid %d proc %lud %s\n", m->machno, p->asid, p->pid, p->text);
+	setttbr((uvlong)p->asid<<48 | PADDR(&m->mmul1[L1TABLEX(0, PTLEVELS-1)]));
+}
+
+void
+mmurelease(Proc *p)
+{
+	Page *t;
+
+	mmuswitch(nil);
+	mmufree(p);
+
+	if((t = p->mmufree) != nil){
+		do {
+			p->mmufree = t->next;
+			if(--t->ref != 0)
+				panic("mmurelease: bad page ref");
+			pagechainhead(t);
+		} while((t = p->mmufree) != nil);
+		pagechaindone();
+	}
+}
+
+void
+flushmmu(void)
+{
+	int x;
+
+	x = splhi();
+	up->newtlb = 1;
+	mmuswitch(up);
+	splx(x);
+}
+
+void
+checkmmu(uintptr, uintptr)
+{
+}
--- /dev/null
+++ b/sys/src/9/bcm64/pi3
@@ -1,0 +1,53 @@
+dev
+	root
+	cons
+	swap
+	env
+	pipe
+	proc
+	mnt
+	srv
+	shr
+	dup
+	arch
+	ssl
+	tls
+	cap
+	fs
+	ip		arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium inferno
+	draw	screen swcursor
+	mouse	mouse
+	uart	gpio
+#	gpio	gpio
+	sd
+	usb
+
+link
+	loopbackmedium
+	ethermedium
+	archbcm3
+	usbdwc
+
+ip
+	tcp
+	udp
+	ipifc
+	icmp
+	icmp6
+	ipmux
+
+misc
+	uartmini
+	uartpl011
+	sdmmc	emmc
+	dma
+	vcore
+
+port
+	int cpuserver = 0;
+
+bootdir
+	/$objtype/bin/paqfs
+	/$objtype/bin/auth/factotum
+	bootfs.paq
+	boot
--- /dev/null
+++ b/sys/src/9/bcm64/sysreg.c
@@ -1,0 +1,58 @@
+/*
+ * ARMv8 system registers
+ * mainly to cope with arm hard-wiring register numbers into instructions.
+ *
+ * these routines must be callable from KZERO.
+ *
+ * on a multiprocessor, process switching to another cpu is assumed
+ * to be inhibited by the caller as these registers are local to the cpu.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+static void*
+mkinstr(ulong wd)
+{
+	static ulong ib[256], *ep[MAXMACH+1];
+	static Lock lk;
+	ulong *ip, *ie;
+
+	ie = ep[m->machno];
+	for(ip = ib; ip < ie; ip += 2)
+		if(*ip == wd)
+			return ip;
+
+	ilock(&lk);
+	ie = ep[MAXMACH];
+	for(; ip < ie; ip += 2)
+		if(*ip == wd)
+			goto Found;
+	if(ip >= &ib[nelem(ib)])
+		panic("mkinstr: out of instrucuction buffer");
+	ip[0] = wd;
+	ip[1] = 0xd65f03c0;	// RETURN
+	ep[MAXMACH] = ie = ip + 2;
+	cachedwbinvse(ip, 2*sizeof(*ip));
+Found:
+	iunlock(&lk);
+	cacheiinv();
+	ep[m->machno] = ie;
+	return ip;
+}
+
+uvlong
+sysrd(ulong spr)
+{
+	uvlong (*fp)(void) = mkinstr(0xd5380000UL | spr);
+	return fp();
+}
+
+void
+syswr(ulong spr, uvlong val)
+{
+	void (*fp)(uvlong) = mkinstr(0xd5180000UL | spr);
+	fp(val);
+}
--- /dev/null
+++ b/sys/src/9/bcm64/sysreg.h
@@ -1,0 +1,53 @@
+#define MIDR_EL1			SYSREG(3,0,0,0,0)
+#define MPIDR_EL1			SYSREG(3,0,0,0,5)
+#define ID_AA64MMFR0_EL1		SYSREG(3,0,0,7,0)
+#define SCTLR_EL1			SYSREG(3,0,1,0,0)
+#define CPACR_EL1			SYSREG(3,0,1,0,2)
+#define MAIR_EL1			SYSREG(3,0,10,2,0)
+#define TCR_EL1				SYSREG(3,0,2,0,2)
+#define TTBR0_EL1			SYSREG(3,0,2,0,0)
+#define TTBR1_EL1			SYSREG(3,0,2,0,1)
+#define ESR_EL1				SYSREG(3,0,5,2,0)
+#define FAR_EL1				SYSREG(3,0,6,0,0)
+#define VBAR_EL1			SYSREG(3,0,12,0,0)
+#define VTTBR_EL2			SYSREG(3,4,2,1,0)
+#define SP_EL0				SYSREG(3,0,4,1,0)
+#define SP_EL1				SYSREG(3,4,4,1,0)
+#define SP_EL2				SYSREG(3,6,4,1,0)
+#define SCTLR_EL2			SYSREG(3,4,1,0,0)
+#define HCR_EL2				SYSREG(3,4,1,1,0)
+#define MDCR_EL2			SYSREG(3,4,1,1,1)
+#define PMCR_EL0			SYSREG(3,3,9,12,0)
+#define PMCNTENSET			SYSREG(3,3,9,12,1)
+#define PMCCNTR_EL0			SYSREG(3,3,9,13,0)
+#define PMUSERENR_EL0			SYSREG(3,3,9,14,0)
+
+#define CNTP_TVAL_EL0			SYSREG(3,3,14,2,0)
+#define CNTP_CTL_EL0			SYSREG(3,3,14,2,1)
+#define CNTP_CVAL_EL0			SYSREG(3,3,14,2,2)
+
+#define TPIDR_EL0			SYSREG(3,3,13,0,2)
+#define TPIDR_EL1			SYSREG(3,0,13,0,4)
+
+#define CCSIDR_EL1			SYSREG(3,1,0,0,0)
+#define CSSELR_EL1			SYSREG(3,2,0,0,0)
+
+#define ACTLR_EL2			SYSREG(3,4,1,0,1)
+#define CPUACTLR_EL1			SYSREG(3,1,15,2,0)
+#define CPUECTLR_EL1			SYSREG(3,1,15,2,1)
+
+/* l.s redefines this for the assembler */
+#define SYSREG(op0,op1,Cn,Cm,op2)	((op0)<<19|(op1)<<16|(Cn)<<12|(Cm)<<8|(op2)<<5)
+
+#define	OSHLD	(0<<2 | 1)
+#define OSHST	(0<<2 | 2)
+#define	OSH	(0<<2 | 3)
+#define NSHLD	(1<<2 | 1)
+#define NSHST	(1<<2 | 2)
+#define NSH	(1<<2 | 3)
+#define ISHLD	(2<<2 | 1)
+#define ISHST	(2<<2 | 2)
+#define ISH	(2<<2 | 3)
+#define LD	(3<<2 | 1)
+#define ST	(3<<2 | 2)
+#define SY	(3<<2 | 3)
--- /dev/null
+++ b/sys/src/9/bcm64/trap.c
@@ -1,0 +1,752 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "../port/systab.h"
+
+#include <tos.h>
+#include "ureg.h"
+#include "sysreg.h"
+
+/* SPSR bits user can modify */
+#define USPSRMASK	(0xFULL<<28)
+
+static void
+setupvector(u32int *v, void (*t)(void), void (*f)(void))
+{
+	int i;
+
+	for(i = 0; i < 0x80/4; i++){
+		v[i] = ((u32int*)t)[i];
+		if(v[i] == 0x14000000){
+			v[i] |= ((u32int*)f - &v[i]) & 0x3ffffff;
+			return;
+		}
+	}
+	panic("bug in vector code");
+}
+
+void
+trapinit(void)
+{
+	extern void vsys(void);
+	extern void vtrap(void);
+	extern void virq(void);
+	extern void vfiq(void);
+	extern void vserr(void);
+
+	extern void vsys0(void);
+	extern void vtrap0(void);
+	extern void vtrap1(void);
+
+	static u32int *v;
+
+	intrcpushutdown();
+	if(v == nil){
+		/* disable everything */
+		intrsoff();
+
+		v = mallocalign(0x80*4*4, 1<<11, 0, 0);
+		if(v == nil)
+			panic("no memory for vector table");
+
+		setupvector(&v[0x000/4], vtrap,	vtrap0);
+		setupvector(&v[0x080/4], virq,	vtrap0);
+		setupvector(&v[0x100/4], vfiq,	vtrap0);
+		setupvector(&v[0x180/4], vserr,	vtrap0);
+
+		setupvector(&v[0x200/4], vtrap,	vtrap1);
+		setupvector(&v[0x280/4], virq,	vtrap1);
+		setupvector(&v[0x300/4], vfiq,	vtrap1);
+		setupvector(&v[0x380/4], vserr,	vtrap1);
+
+		setupvector(&v[0x400/4], vsys,	vsys0);
+		setupvector(&v[0x480/4], virq,	vtrap0);
+		setupvector(&v[0x500/4], vfiq,	vtrap0);
+		setupvector(&v[0x580/4], vserr, vtrap0);
+
+		setupvector(&v[0x600/4], vtrap,	vtrap0);
+		setupvector(&v[0x680/4], virq,	vtrap0);
+		setupvector(&v[0x700/4], vfiq,	vtrap0);
+		setupvector(&v[0x780/4], vserr,	vtrap0);
+
+		cacheduwbse(v, 0x80*4*4);
+	}
+	cacheiinvse(v, 0x80*4*4);
+	syswr(VBAR_EL1, (uintptr)v);
+	splx(0x3<<6);	// unmask serr and debug
+}
+
+void
+kexit(Ureg*)
+{
+	Tos *tos;
+	uvlong t;
+
+	t = cycles();
+
+	tos = (Tos*)(USTKTOP-sizeof(Tos));
+	tos->kcycles += t - up->kentry;
+	tos->pcycles = t + up->pcycles;
+	tos->pid = up->pid;
+}
+
+static char *traps[64] = {
+	[0x00]	"sys: trap: unknown",
+	[0x01]	"sys: trap: WFI or WFE instruction execution",
+	[0x0E]	"sys: trap: illegal execution state",
+	[0x18]	"sys: trap: illegal MSR/MRS access",
+	[0x22]	"sys: trap: misaligned pc",
+	[0x26]	"sys: trap: stack pointer misaligned",
+	[0x30]	"sys: trap: breakpoint",
+	[0x32]	"sys: trap: software step",
+	[0x34]	"sys: trap: watchpoint",
+	[0x3C]	"sys: trap: BRK instruction",
+};
+
+void
+trap(Ureg *ureg)
+{
+	u32int type, intr;
+	
+	intr = ureg->type >> 32;
+	if(intr == 2){
+		fiq(ureg);
+		return;
+	}
+	splflo();
+	if(userureg(ureg)){
+		up->dbgreg = ureg;
+		up->kentry = cycles();
+	}
+	type = (u32int)ureg->type >> 26;
+	switch(type){
+	case 0x20:	// instruction abort from lower level
+	case 0x21:	// instruction abort from same level
+	case 0x24:	// data abort from lower level
+	case 0x25:	// data abort from same level
+		faultarm64(ureg);
+		break;
+	case 0x07:	// SIMD/FP
+	case 0x2C:	// FPU exception (A64 only)
+		mathtrap(ureg);
+		break;
+	case 0x00:	// unknown
+		if(intr == 1){
+			if(irq(ureg) && up != nil && up->delaysched)
+				sched();
+			break;
+		}
+		if(intr == 3){
+	case 0x2F:	// SError interrupt
+			dumpregs(ureg);
+			panic("SError interrupt");
+			break;
+		}
+		/* wet floor */
+	case 0x01:	// WFI or WFE instruction execution
+	case 0x03:	// MCR or MRC access to CP15 (A32 only)
+	case 0x04:	// MCRR or MRC access to CP15 (A32 only)
+	case 0x05:	// MCR or MRC access to CP14 (A32 only)
+	case 0x06:	// LDC or STD access to CP14 (A32 only)
+	case 0x08:	// MCR or MRC to CP10 (A32 only)
+	case 0x0C:	// MRC access to CP14 (A32 only)
+	case 0x0E:	// Illegal Execution State
+	case 0x11:	// SVC instruction execution (A32 only)
+	case 0x12:	// HVC instruction execution (A32 only)
+	case 0x13:	// SMC instruction execution (A32 only)
+	case 0x15:	// SVC instruction execution (A64 only)
+	case 0x16:	// HVC instruction execution (A64 only)
+	case 0x17:	// SMC instruction execution (A64 only)
+	case 0x18:	// MSR/MRS (A64)
+	case 0x22:	// misaligned pc
+	case 0x26:	// stack pointer misaligned
+	case 0x28:	// FPU exception (A32 only)
+	case 0x30:	// breakpoint from lower level
+	case 0x31:	// breakpoint from same level
+	case 0x32:	// software step from lower level
+	case 0x33:	// software step from same level
+	case 0x34:	// watchpoint execution from lower level
+	case 0x35:	// watchpoint exception from same level
+	case 0x38:	// breapoint (A32 only)
+	case 0x3A:	// vector catch exception (A32 only)
+	case 0x3C:	// BRK instruction (A64 only)
+	default:
+		if(!userureg(ureg)){
+			dumpregs(ureg);
+			panic("unhandled trap");
+		}
+		if(traps[type] == nil) type = 0;	// unknown
+		postnote(up, 1, traps[type], NDebug);
+		break;
+	}
+	splhi();
+	if(userureg(ureg)){
+		if(up->procctl || up->nnote)
+			notify(ureg);
+		kexit(ureg);
+	}
+}
+
+void
+syscall(Ureg *ureg)
+{
+	vlong startns, stopns;
+	uintptr sp, ret;
+	ulong scallnr;
+	int i, s;
+	char *e;
+
+	up->kentry = cycles();
+	
+	m->syscall++;
+	up->insyscall = 1;
+	up->pc = ureg->pc;
+	up->dbgreg = ureg;
+	
+	sp = ureg->sp;
+	up->scallnr = scallnr = ureg->r0;
+
+	spllo();
+	
+	up->nerrlab = 0;
+	startns = 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] == nil){
+			pprint("bad sys call number %lud pc %#p", 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=%#p pc=%#p\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));
+		/*
+		 * normally, syscall() returns to forkret()
+		 * not restoring general registers when going
+		 * to userspace. to completely restore the
+		 * interrupted context, we have to return thru
+		 * noteret(). we override return pc to jump to
+		 * to it when returning form syscall()
+		 */
+		returnto(noteret);
+	}
+
+	if(scallnr != RFORK && (up->procctl || up->nnote)){
+		splhi();
+		notify(ureg);
+	}
+	if(up->delaysched)
+		sched();
+	kexit(ureg);
+}
+
+int
+notify(Ureg *ureg)
+{
+	int l;
+	uintptr 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-23)	/* " pc=0x0123456789abcdef\0" */
+			l = ERRMAX-23;
+		sprint(n->msg+l, " pc=%#p", 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);
+	sp = STACKALIGN(sp);
+
+	if(!okaddr((uintptr)up->notify, 1, 0)
+	|| !okaddr(sp-ERRMAX-4*BY2WD, sizeof(Ureg)+ERRMAX+4*BY2WD, 1)
+	|| ((uintptr) up->notify & 3) != 0
+	|| (sp & 7) != 0){
+		qunlock(&up->debug);
+		pprint("suicide: bad address in notify: handler=%#p sp=%#p\n",
+			up->notify, sp);
+		pexit("Suicide", 0);
+	}
+
+	memmove((Ureg*)sp, ureg, sizeof(Ureg));
+	*(Ureg**)(sp-BY2WD) = up->ureg;	/* word under Ureg is old up->ureg */
+	up->ureg = (void*)sp;
+	sp -= BY2WD+ERRMAX;
+	memmove((char*)sp, up->note[0].msg, ERRMAX);
+	sp -= 3*BY2WD;
+	*(uintptr*)(sp+2*BY2WD) = sp+3*BY2WD;
+	*(uintptr*)(sp+1*BY2WD) = (uintptr)up->ureg;
+	ureg->r0 = (uintptr) up->ureg;
+	ureg->sp = sp;
+	ureg->pc = (uintptr) up->notify;
+	ureg->link = 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;
+	uintptr 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 = (uintptr) nureg;
+	if(!okaddr(oureg - BY2WD, BY2WD + sizeof(Ureg), 0) || (oureg & 7) != 0){
+		qunlock(&up->debug);
+		pprint("bad ureg in noted or call to noted when not notified\n");
+		pexit("Suicide", 0);
+	}
+
+	nureg->psr = (nureg->psr & USPSRMASK) | (ureg->psr & ~USPSRMASK);
+	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 & 7) != 0){
+			qunlock(&up->debug);
+			pprint("suicide: trap in noted\n");
+			pexit("Suicide", 0);
+		}
+		up->ureg = (Ureg *) (*(uintptr*) (oureg - BY2WD));
+		qunlock(&up->debug);
+		break;
+	
+	case NSAVE:
+		if(!okaddr(nureg->pc, BY2WD, 0) || !okaddr(nureg->sp, BY2WD, 0) ||
+				(nureg->pc & 3) != 0 || (nureg->sp & 7) != 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;
+		((uintptr *) sp)[1] = oureg;
+		((uintptr *) 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
+faultarm64(Ureg *ureg)
+{
+	extern void checkpages(void);
+	char buf[ERRMAX];
+	int read, insyscall;
+	uintptr addr;
+
+	insyscall = up->insyscall;
+	up->insyscall = 1;
+
+	if(!userureg(ureg) && waserror()){
+		if(up->nerrlab == 0){
+			pprint("suicide: sys: %s\n", up->errstr);
+			pexit(up->errstr, 1);
+		}
+		up->insyscall = insyscall;
+		nexterror();
+	}
+
+	addr = getfar();
+	read = (ureg->type & (1<<6)) == 0;
+
+	switch((u32int)ureg->type & 0x3F){
+	case  4: case  5: case  6: case  7:	// Tanslation fault.
+	case  8: case  9: case 10: case 11:	// Access flag fault.
+	case 12: case 13: case 14: case 15:	// Permission fault.
+	case 48:				// tlb conflict fault.
+		if(fault(addr, read) == 0)
+			break;
+
+		/* wet floor */
+	case  0: case  1: case  2: case  3:	// Address size fault.
+	case 16: 				// synchronous external abort.
+	case 24: 				// synchronous parity error on a memory access.
+	case 20: case 21: case 22: case 23:	// synchronous external abort on a table walk.
+	case 28: case 29: case 30: case 31:	// synchronous parity error on table walk.
+	case 33:				// alignment fault.
+	case 52:				// implementation defined, lockdown abort.
+	case 53:				// implementation defined, unsuppoted exclusive.
+	case 61:				// first level domain fault
+	case 62:				// second level domain fault
+	default:
+		if(!userureg(ureg)){
+			dumpregs(ureg);
+			panic("fault: %s addr=%#p", read ? "read" : "write", addr);
+		}
+		checkpages();
+		sprint(buf, "sys: trap: fault %s addr=%#p", read ? "read" : "write", addr);
+		postnote(up, 1, buf, NDebug);
+	}
+
+	if(!userureg(ureg))
+		poperror();
+
+	up->insyscall = insyscall;
+}
+
+int
+userureg(Ureg* ureg)
+{
+	return (ureg->psr & 15) == 0;
+}
+
+uintptr
+userpc(void)
+{
+	Ureg *ur = up->dbgreg;
+	return ur->pc;
+}
+
+uintptr
+dbgpc(Proc *)
+{
+	Ureg *ur = up->dbgreg;
+	if(ur == nil)
+		return 0;
+	return ur->pc;
+}
+
+void
+procfork(Proc *p)
+{
+	int s;
+
+	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);
+
+	p->tpidr = up->tpidr;
+
+	p->kentry = up->kentry;
+	p->pcycles = -p->kentry;
+}
+
+void
+procsetup(Proc *p)
+{
+	p->fpstate = FPinit;
+	fpoff();
+
+	p->tpidr = 0;
+	syswr(TPIDR_EL0, p->tpidr);
+
+	p->kentry = cycles();
+	p->pcycles = -p->kentry;
+}
+
+void
+procsave(Proc *p)
+{
+	uvlong t;
+
+	if(p->fpstate == FPactive){
+		if(p->state == Moribund)
+			fpclear();
+		else
+			fpsave(p->fpsave);
+		p->fpstate = FPinactive;
+	}
+
+	if(p->kp == 0)
+		p->tpidr = sysrd(TPIDR_EL0);
+
+	putasid(p);	// release asid
+
+	t = cycles();
+	p->kentry -= t;
+	p->pcycles += t;
+}
+
+void
+procrestore(Proc *p)
+{
+	uvlong t;
+
+	if(p->kp)
+		return;
+	
+	syswr(TPIDR_EL0, p->tpidr);
+
+	t = cycles();
+	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 - 16;
+	*(void**)p->sched.sp = kprocchild;	/* fake */
+
+	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 - TRAPFRAMESIZE;
+
+	cureg = (Ureg*) (p->sched.sp + 16);
+	memmove(cureg, ureg, sizeof(Ureg));
+	cureg->r0 = 0;
+
+	p->psstate = 0;
+	p->insyscall = 0;
+}
+
+uintptr
+execregs(uintptr entry, ulong ssize, ulong nargs)
+{
+	uintptr *sp;
+	Ureg *ureg;
+
+	sp = (uintptr*)(USTKTOP - ssize);
+	*--sp = nargs;
+
+	ureg = up->dbgreg;
+	ureg->sp = (uintptr)sp;
+	ureg->pc = entry;
+	ureg->link = 0;
+	return USTKTOP-sizeof(Tos);
+}
+
+void
+evenaddr(uintptr addr)
+{
+	if(addr & 3){
+		postnote(up, 1, "sys: odd address", NDebug);
+		error(Ebadarg);
+	}
+}
+
+void
+callwithureg(void (*f) (Ureg *))
+{
+	Ureg u;
+	
+	u.pc = getcallerpc(&f);
+	u.sp = (uintptr) &f;
+	f(&u);
+}
+
+void
+setkernur(Ureg *ureg, Proc *p)
+{
+	ureg->pc = p->sched.pc;
+	ureg->sp = p->sched.sp;
+	ureg->link = (uintptr)sched;
+}
+
+void
+setupwatchpts(Proc*, Watchpt*, int)
+{
+}
+
+void
+setregisters(Ureg* ureg, char* pureg, char* uva, int n)
+{
+	ulong v;
+
+	v = ureg->psr;
+	memmove(pureg, uva, n);
+	ureg->psr = (ureg->psr & USPSRMASK) | (v & ~USPSRMASK);
+}
+
+static void
+dumpstackwithureg(Ureg *ureg)
+{
+	uintptr v, estack, sp;
+	char *s;
+	int i;
+
+	if((s = getconf("*nodumpstack")) != nil && strcmp(s, "0") != 0){
+		iprint("dumpstack disabled\n");
+		return;
+	}
+	iprint("ktrace /kernel/path %#p %#p %#p # pc, sp, link\n",
+		ureg->pc, ureg->sp, ureg->link);
+	delay(2000);
+
+	sp = ureg->sp;
+	if(sp < KZERO || (sp & 7) != 0)
+		sp = (uintptr)&ureg;
+
+	estack = (uintptr)m+MACHSIZE;
+	if(up != nil && sp <= (uintptr)up->kstack+KSTACK)
+		estack = (uintptr)up->kstack+KSTACK;
+
+	if(sp > estack){
+		if(up != nil)
+			iprint("&up->kstack %#p sp %#p\n", up->kstack, sp);
+		else
+			iprint("&m %#p sp %#p\n", m, sp);
+		return;
+	}
+
+	i = 0;
+	for(; sp < estack; sp += sizeof(uintptr)){
+		v = *(uintptr*)sp;
+		if(KTZERO < v && v < (uintptr)etext && (v & 3) == 0){
+			iprint("%#8.8lux=%#8.8lux ", (ulong)sp, (ulong)v);
+			i++;
+		}
+		if(i == 4){
+			i = 0;
+			iprint("\n");
+		}
+	}
+	if(i)
+		iprint("\n");
+}
+
+void
+dumpstack(void)
+{
+	callwithureg(dumpstackwithureg);
+}
+
+void
+dumpregs(Ureg *ureg)
+{
+	u64int *r;
+	int i, x;
+
+	x = splhi();
+	if(up != nil)
+		iprint("cpu%d: dumpregs ureg %#p process %lud: %s\n", m->machno, ureg,
+			up->pid, up->text);
+	else
+		iprint("cpu%d: dumpregs ureg %#p\n", m->machno, ureg);
+	r = &ureg->r0;
+	for(i = 0; i < 30; i += 3)
+		iprint("R%d %.16llux  R%d %.16llux  R%d %.16llux\n", i, r[i], i+1, r[i+1], i+2, r[i+2]);
+	iprint("PC %#p  SP %#p  LR %#p  PSR %llux  TYPE %llux\n",
+		ureg->pc, ureg->sp, ureg->link,
+		ureg->psr, ureg->type);
+	splx(x);
+}