shithub: front

Download patch

ref: 8e03baff8bbba541f87a435e596017e0bd7e5265
parent: 5e34f6ae06c4002cecda9027e759e94b0de0dc9e
author: Keegan Saunders <keegan@undefinedbehaviour.org>
date: Tue May 21 19:54:48 EDT 2024

add honeycomb kernel

This is a kernel for the NXP LX2160A, specifically the SolidRun
Honeycomb board which is available for sale on the SolidRun
website.

It currently boots on U-Boot. UEFI support is planned. Build or
download the U-Boot firmware from the SolidRun site and then write
it to the on-board SD card. Then, plug in a USB with the honeycomb
image and proceed to install as normal. Only NVMe or USB storage
is supported (SATA is planned).

This kernel supports PCIe and USB. On-board ethernet and SFP are
not supported (yet). It uses 2GB of memory by default, but that
can be increased using *maxmem depending on how much RAM you have
in the system. As well, SMP is currently disabled until an
uncommon deadlock issue is fixed (could be a hardware issue, unknown
at this point).

--- a/sys/lib/dist/mkfile
+++ b/sys/lib/dist/mkfile
@@ -126,6 +126,19 @@
 	mv $target.$pid.disk $target && dd -trunc 0 -bs 1024 -oseek 33 -if /n/src9/sys/src/boot/reform/flash.bin -of $target 
 	}
 
+%.honeycomb.img:D:	/n/src9/sys/src/boot/honeycomb/boot.scr
+	@{
+	objtype=arm64
+	kernel=/n/src9/$objtype/9honeycomb.u
+	> /env/plan9.ini {
+		echo 'console=0'
+	}
+	fatfiles=(/n/src9/sys/src/boot/honeycomb/boot.scr /env/plan9.ini $kernel)
+	mb=1885	# storage vendors idea of 2GB
+	mk $target.$pid.disk
+	mv $target.$pid.disk $target
+	}
+
 %.pc.iso:D:	$proto /n/src9/sys/lib/sysconfig/proto/9bootproto $kernel
 	@{rfork n
 	mk binds
@@ -186,7 +199,7 @@
 		disk/prep -bw -a^(nvram fs) $d/plan9
 		disk/format -d $d/dos $fatfiles
 	}
-	if not if(~ $target *.reform.img.* *.arm64.qcow2.*){
+	if not if(~ $target *.reform.img.* *.arm64.qcow2.* *.honeycomb.img.*){
 		{
 			echo 'a p1 4M 100M'
 			echo 't p1 FAT32'
--- /dev/null
+++ b/sys/src/9/lx2k/archlx2k.c
@@ -1,0 +1,22 @@
+#include "u.h"
+#include "mem.h"
+
+enum {
+	/* ARM SBSA */
+	Wdogwcs = 0x00,
+};
+
+static void
+wdogoff(void)
+{
+	u32int *r = (u32int *)(VIRTIO+0x13a0000ULL);
+
+	/* Doghouse (Disable) */
+	r[Wdogwcs] = 0;
+}
+
+void
+archlx2klink(void)
+{
+	wdogoff();
+}
--- /dev/null
+++ b/sys/src/9/lx2k/clock.c
@@ -1,0 +1,134 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../arm64/sysreg.h"
+
+static uvlong freq;
+
+enum {
+	Enable	= 1<<0,
+	Imask	= 1<<1,
+	Istatus = 1<<2,
+};
+
+void
+clockshutdown(void)
+{
+}
+
+static void
+localclockintr(Ureg *ureg, void *)
+{
+	timerintr(ureg, 0);
+}
+
+void
+clockinit(void)
+{
+	uvlong tstart, tend;
+	ulong t0, t1;
+
+	syswr(PMCR_EL0, 1<<6 | 7);
+	syswr(PMCNTENSET, 1<<31);
+	syswr(PMUSERENR_EL0, 1<<2);
+	syswr(CNTKCTL_EL1, 1<<1);
+
+	syswr(CNTP_TVAL_EL0, ~0UL);
+	syswr(CNTP_CTL_EL0, Enable);
+
+	if(m->machno == 0){
+		freq = sysrd(CNTFRQ_EL0);
+		print("timer frequency %lld Hz\n", freq);
+	}
+	tstart = sysrd(CNTPCT_EL0);
+	do{
+		t0 = lcycles();
+	}while(sysrd(CNTPCT_EL0) == tstart);
+	tend = tstart + (freq/100);
+	do{
+		t1 = lcycles();
+	}while(sysrd(CNTPCT_EL0) < tend);
+	t1 -= t0;
+	m->cpuhz = 100 * t1;
+	m->cpumhz = (m->cpuhz + Mhz/2 - 1) / Mhz;
+
+	/*
+	 * we are using virtual counter register CNTVCT_EL0
+	 * instead of the performance counter in userspace.
+	 */
+	m->cyclefreq = freq;
+
+	intrenable(IRQcntpns, localclockintr, nil, BUSUNKNOWN, "clock");
+}
+
+void
+timerset(uvlong next)
+{
+	uvlong now;
+	long period;
+
+	now = fastticks(nil);
+	period = next - now;
+	syswr(CNTP_TVAL_EL0, period);
+}
+
+uvlong
+fastticks(uvlong *hz)
+{
+	if(hz)
+		*hz = freq;
+	return sysrd(CNTPCT_EL0);
+}
+
+ulong
+perfticks(void)
+{
+	return fastticks(nil);
+}
+
+ulong
+µs(void)
+{
+	uvlong hz;
+	uvlong t = fastticks(&hz);
+	return (t * 1000000ULL) / hz;
+}
+
+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/lx2k/dat.h
@@ -1,0 +1,231 @@
+/*
+ * 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,
+
+	GpioLow = 0,
+	GpioHigh,
+	GpioRising,
+	GpioFalling,
+	GpioEdge,
+};
+
+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 Pcidev	Pcidev;
+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 Pcidev
+#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;
+};
+
+#define KFPSTATE
+
+struct PFPU
+{
+	int	fpstate;
+	int	kfpstate;
+	FPsave	*fpsave;
+	FPsave	*kfpsave;
+};
+
+enum
+{
+	FPinit,
+	FPactive,
+	FPinactive,
+	FPprotected,
+
+	/* bits or'd with the state */
+	FPillegal= 0x100,
+};
+
+struct Confmem
+{
+	uintptr	base;
+	ulong	npage;
+	uintptr	limit;
+	uintptr	kbase;
+	uintptr	klimit;
+};
+
+struct Conf
+{
+	ulong	nmach;		/* processors */
+	ulong	nproc;		/* processes */
+	Confmem	mem[3];		/* physical memory */
+	ulong	npage;		/* total physical pages of memory */
+	ulong	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*	mmutop;		/* first level user page table */
+};
+
+/*
+ *  MMU stuff in proc
+ */
+#define NCOLOR	1		/* 1 level cache, don't worry about VCE's */
+
+struct PMMU
+{
+	union {
+	Page	*mmufree;	/* mmuhead[0] is freelist head */
+	Page	*mmuhead[PTLEVELS];
+	};
+	Page	*mmutail[PTLEVELS];
+	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 on this processor */
+	/* end of offsets known to asm */
+
+	MMMU;
+
+	PMach;
+
+	int	fpstate;
+	FPsave	*fpsave;
+
+	int	cputype;
+	ulong	delayloop;
+	int	cpumhz;
+	uvlong	cpuhz;			/* speed of cpu */
+
+	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;
+
+/*
+ *  a parsed plan9.ini line
+ */
+#define NISAOPT		8
+
+struct ISAConf {
+	char	*type;
+	uvlong	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 */
+};
--- /dev/null
+++ b/sys/src/9/lx2k/fns.h
@@ -1,0 +1,172 @@
+#include "../port/portfns.h"
+
+/* 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 vcycles(void);
+#define cycles(ip) *(ip) = vcycles()
+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 fpon(void);
+extern void fpoff(void);
+extern void fpsaveregs(void*);
+extern void fploadregs(void*);
+extern void smccall(Ureg*);
+
+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);
+
+extern void flushlocaltlb(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 void kmapram(uintptr, uintptr);
+extern uintptr mmukmap(uintptr, uintptr, usize);
+extern void* vmap(uvlong, vlong);
+extern void vunmap(void*, vlong);
+extern void mmu1init(void);
+extern void putasid(Proc*);
+
+/* mem */
+extern void mmuidmap(uintptr*);
+extern void mmu0init(uintptr*);
+extern void meminit(void);
+
+extern void* ucalloc(usize);
+
+/* clock */
+extern void clockinit(void);
+extern void synccycles(void);
+extern void armtimerset(int);
+extern void clockshutdown(void);
+
+/* fpu */
+extern void fpuinit(void);
+extern void fpuprocsetup(Proc*);
+extern void fpuprocfork(Proc*);
+extern void fpuprocsave(Proc*);
+extern void fpuprocrestore(Proc*);
+extern FPsave* fpukenter(Ureg*);
+extern void fpukexit(Ureg*, 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 intrinit(void);
+extern void intrcpushutdown(void);
+extern void intrsoff(void);
+extern void intrenable(int, void (*)(Ureg*, void*), void*, int, char*);
+extern void intrdisable(int, void (*)(Ureg*, void*), void*, int, char*);
+extern int irq(Ureg*);
+extern void fiq(Ureg*);
+
+/* sysreg */
+extern uvlong	sysrd(ulong);
+extern void	syswr(ulong, uvlong);
+
+/* uartlx2k */
+extern void uartconsinit(void);
+
+/* dma */
+extern void dmaflush(int, void*, ulong);
+
+/* main */
+extern char *getconf(char *name);
+extern void setconfenv(void);
+extern void writeconf(void);
+
+extern int isaconfig(char*, int, ISAConf*);
+extern void links(void);
+
+/* ccm */
+extern void setclkgate(char *name, int on);
+extern void setclkrate(char *name, char *source, int freq);
+extern int getclkrate(char *name);
+
+/* gpc */
+extern void powerup(char *dom);
+
+/* lcd */
+extern void lcdinit(void);
+
+/* iomux */
+extern void iomuxpad(char *pads, char *sel, char *cfg);
+extern uint iomuxgpr(int gpr, uint set, uint mask);
+
+/* gpio */
+#define GPIO_PIN(n, m)	((n)<<5 | (m))
+extern void gpioout(uint pin, int set);
+extern int gpioin(uint pin);
+void gpiointrenable(uint pin, int mode, void (*f)(uint pin, void *a), void *a);
+void gpiointrdisable(uint pin);
+
+/* pcilx2k */
+extern int pcicfgrw8(int tbdf, int rno, int data, int read);
+extern int pcicfgrw16(int tbdf, int rno, int data, int read);
+extern int pcicfgrw32(int tbdf, int rno, int data, int read);
+extern void pciintrenable(int tbdf, void (*f)(Ureg*, void*), void *a);
+extern void pciintrdisable(int tbdf, void (*f)(Ureg*, void*), void *a);
--- /dev/null
+++ b/sys/src/9/lx2k/gic.c
@@ -1,0 +1,319 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/pci.h"
+#include "ureg.h"
+#include "../arm64/sysreg.h"
+#include "../port/error.h"
+
+enum {
+	GICD_CTLR	= 0x000/4,	/* RW, Distributor Control Register */
+	GICD_TYPER	= 0x004/4,	/* RO, Interrupt Controller Type */
+	GICD_IIDR	= 0x008/4,	/* RO, Distributor Implementer Identification Register */
+
+	GICD_IGROUPR0	= 0x080/4,	/* RW, Interrupt Group Registers (0x80-0xBC) */
+
+	GICD_ISENABLER0	= 0x100/4,	/* RW, Interrupt Set-Enable Registers (0x100-0x13C) */
+	GICD_ICENABLER0	= 0x180/4,	/* RW, Interrupt Clear-Enable Registers (0x180-0x1BC) */
+
+	GICD_ISPENDR0	= 0x200/4,	/* RW, Interrupt Set-Pending Registers (0x200-0x23C) */
+	GICD_ICPENDR0	= 0x280/4,	/* RW, Interrupt Clear-Pending Registers (0x280-0x2BC) */
+
+	GICD_ISACTIVER0	= 0x300/4,	/* RW, Interrupt Set-Active Registers (0x300-0x33C) */
+	GICD_ICACTIVER0 = 0x380/4,	/* RW, Interrupt Clear-Active Registers (0x380-0x3BC) */
+
+	GICD_IPRIORITYR0= 0x400/4,	/* RW, Interrupt Priority Registers (0x400-0x5FC) */
+	GICD_TARGETSR0	= 0x800/4,	/* RW, Interrupt Target Registers (0x800-0x9FC) */
+	GICD_ICFGR0	= 0xC00/4,	/* RW, Interrupt Configuration Registers (0xC00-0xC7C) */
+
+	GICD_ISR0	= 0xD00/4,
+	GICD_PPISR	= GICD_ISR0,	/* RO, Private Peripheral Interrupt Status Register */
+	GICD_SPISR0	= GICD_ISR0+1,	/* RO, Shared Peripheral Interrupt Status Register */
+	GICD_SGIR	= 0xF00/4,	/* WO, Software Generated Interrupt Register */
+
+	GICD_CPENDSGIR0	= 0xF10/4,	/* RW, SGI Clear-Pending Registers (0xF10-0xF1C) */
+	GICD_SPENDSGIR0	= 0xF20/4,	/* RW, SGI Set-Pending Registers (0xF20-0xF2C) */
+
+	GICD_PIDR4	= 0xFD0/4,	/* RO, Perpheral ID Registers */
+	GICD_PIDR5	= 0xFD4/4,
+	GICD_PIDR6	= 0xFD8/4,
+	GICD_PIDR7	= 0xFDC/4,
+	GICD_PIDR0	= 0xFE0/4,
+	GICD_PIDR1	= 0xFE4/4,
+	GICD_PIDR2	= 0xFE8/4,
+	GICD_PIDR3	= 0xFEC/4,
+
+	GICD_CIDR0	= 0xFF0/4,	/* RO, Component ID Registers */
+	GICD_CIDR1	= 0xFF4/4,
+	GICD_CIDR2	= 0xFF8/4,
+	GICD_CIDR3	= 0xFFC/4,
+
+	RD_base		= 0x00000,
+	GICR_CTLR	= (RD_base+0x000)/4,
+	GICR_IIDR	= (RD_base+0x004)/4,
+	GICR_TYPER	= (RD_base+0x008)/4,
+	GICR_STATUSR	= (RD_base+0x010)/4,
+	GICR_WAKER	= (RD_base+0x014)/4,
+	GICR_SETLPIR	= (RD_base+0x040)/4,
+	GICR_CLRLPIR	= (RD_base+0x048)/4,
+	GICR_PROPBASER	= (RD_base+0x070)/4,
+	GICR_PENDBASER	= (RD_base+0x078)/4,
+	GICR_INVLPIR	= (RD_base+0x0A0)/4,
+	GICR_INVALLR	= (RD_base+0x0B0)/4,
+	GICR_SYNCR	= (RD_base+0x0C0)/4,
+
+	SGI_base	= 0x10000,
+	GICR_IGROUPR0	= (SGI_base+0x080)/4,
+	GICR_ISENABLER0	= (SGI_base+0x100)/4,
+	GICR_ICENABLER0	= (SGI_base+0x180)/4,
+	GICR_ISPENDR0	= (SGI_base+0x200)/4,
+	GICR_ICPENDR0	= (SGI_base+0x280)/4,
+	GICR_ISACTIVER0	= (SGI_base+0x300)/4,
+	GICR_ICACTIVER0	= (SGI_base+0x380)/4,
+	GICR_IPRIORITYR0= (SGI_base+0x400)/4,
+	GICR_ICFGR0	= (SGI_base+0xC00)/4,
+	GICR_ICFGR1	= (SGI_base+0xC04)/4,
+	GICR_IGRPMODR0	= (SGI_base+0xD00)/4,
+	GICR_NSACR	= (SGI_base+0xE00)/4,
+};
+
+typedef struct Vctl Vctl;
+struct Vctl {
+	Vctl	*next;
+	void	(*f)(Ureg*, void*);
+	void	*a;
+	int	irq;
+	u32int	intid;
+};
+
+static Lock vctllock;
+static Vctl *vctl[MAXMACH][32], *vfiq;
+static u32int *dregs = (u32int*)(VIRTIO + 0x5000000);
+
+static u32int*
+getrregs(int machno)
+{
+	u32int *rregs = (u32int*)(VIRTIO + 0x5200000);
+
+	for(;;){
+		if((rregs[GICR_TYPER] & 0xFFFF00) == (machno << 8))
+			return rregs;
+		if(rregs[GICR_TYPER] & (1<<4))
+			break;
+		rregs += (0x20000/4);
+	}
+	panic("getrregs: no re-distributor for cpu %d\n", machno);
+	return nil;
+}
+
+void
+intrcpushutdown(void)
+{
+	/* disable cpu interface */
+	syswr(ICC_IGRPEN1_EL1, 0);
+	coherence();
+}
+
+void
+intrsoff(void)
+{
+	/* disable distributor */
+	dregs[GICD_CTLR] = 0;
+	coherence();
+	while(dregs[GICD_CTLR]&(1<<31))
+		;
+}
+
+void
+intrinit(void)
+{
+	u32int *rregs;
+	int i, n;
+
+	if(m->machno == 0){
+		intrsoff();
+
+		/* clear all interrupts */
+		n = ((dregs[GICD_TYPER] & 0x1F)+1) << 5;
+		for(i = 32; i < n; i += 32){
+			dregs[GICD_IGROUPR0 + (i/32)] = -1;
+
+			dregs[GICD_ISENABLER0 + (i/32)] = -1;
+			while(dregs[GICD_CTLR]&(1<<31))
+				;
+			dregs[GICD_ICENABLER0 + (i/32)] = -1;
+			while(dregs[GICD_CTLR]&(1<<31))
+				;
+			dregs[GICD_ICACTIVER0 + (i/32)] = -1;
+		}
+		for(i = 0; i < n; i += 4){
+			dregs[GICD_IPRIORITYR0 + (i/4)] = 0;
+			dregs[GICD_TARGETSR0 + (i/4)] = 0;
+		}
+		for(i = 32; i < n; i += 16){
+			dregs[GICD_ICFGR0 + (i/16)] = 0;
+		}
+		coherence();
+		while(dregs[GICD_CTLR]&(1<<31))
+			;
+		dregs[GICD_CTLR] = (1<<0) | (1<<1) | (1<<4);
+	}
+
+	rregs = getrregs(m->machno);
+	n = 32;
+	for(i = 0; i < n; i += 32){
+		rregs[GICR_IGROUPR0 + (i/32)] = -1;
+
+		rregs[GICR_ISENABLER0 + (i/32)] = -1;
+		while(rregs[GICR_CTLR]&(1<<3))
+			;
+		rregs[GICR_ICENABLER0 + (i/32)] = -1;
+		while(dregs[GICD_CTLR]&(1<<31))
+			;
+		rregs[GICR_ICACTIVER0 + (i/32)] = -1;
+	}
+	for(i = 0; i < n; i += 4){
+		rregs[GICR_IPRIORITYR0 + (i/4)] = 0;
+	}
+	coherence();
+	while(rregs[GICR_CTLR]&(1<<3))
+		;
+
+	coherence();
+
+	/* enable cpu interface */
+	syswr(ICC_CTLR_EL1, 0);
+	syswr(ICC_BPR1_EL1, 7);
+	syswr(ICC_PMR_EL1, 0xFF);
+
+	coherence();
+}
+
+
+/*
+ *  called by trap to handle irq interrupts.
+ *  returns true iff a clock interrupt, thus maybe reschedule.
+ */
+int
+irq(Ureg* ureg)
+{
+	Vctl *v;
+	int clockintr;
+	u32int intid;
+
+	m->intr++;
+	intid = sysrd(ICC_IAR1_EL1) & 0xFFFFFF;
+// iprint("i<%d>", intid);
+	if((intid & ~3) == 1020)
+		return 0; // spurious
+	clockintr = 0;
+	for(v = vctl[m->machno][intid%32]; v != nil; v = v->next)
+		if(v->intid == intid){
+			coherence();
+			v->f(ureg, v->a);
+			coherence();
+			if(v->irq == IRQcntpns)
+				clockintr = 1;
+		}
+	coherence();
+	syswr(ICC_EOIR1_EL1, intid);
+	return clockintr;
+}
+
+/*
+ * called direct from lexception.s to handle fiq interrupt.
+ */
+void
+fiq(Ureg *ureg)
+{
+	Vctl *v;
+	u32int intid;
+
+	m->intr++;
+	intid = sysrd(ICC_IAR1_EL1) & 0xFFFFFF;
+// iprint("f<%d>", intid);
+	if((intid & ~3) == 1020)
+		return;	// spurious
+	v = vfiq;
+	if(v != nil && v->intid == intid && m->machno == 0){
+		coherence();
+		v->f(ureg, v->a);
+		coherence();
+	}
+	syswr(ICC_EOIR1_EL1, intid);
+}
+
+void
+intrenable(int irq, void (*f)(Ureg*, void*), void *a, int tbdf, char *)
+{
+	Vctl *v;
+	u32int intid;
+	int cpu, prio;
+
+	if(BUSTYPE(tbdf) == BusPCI){
+		pciintrenable(tbdf, f, a);
+		return;
+	}
+
+	if(tbdf != BUSUNKNOWN)
+		return;
+
+	prio = 0x80;
+	intid = irq;
+	if((v = xalloc(sizeof(Vctl))) == nil)
+		panic("intrenable: no mem");
+	v->irq = irq;
+	v->intid = intid;
+	v->f = f;
+	v->a = a;
+
+	lock(&vctllock);
+	if(intid < SPI)
+		cpu = m->machno;
+	else
+		cpu = 0;
+	if(irq == IRQfiq){
+		vfiq = v;
+		prio = 0;
+	}else{
+		v->next = vctl[cpu][intid%32];
+		vctl[cpu][intid%32] = v;
+	}
+	syswr(ICC_IGRPEN1_EL1, sysrd(ICC_IGRPEN1_EL1)|1);
+	coherence();
+
+	syswr(ICC_EOIR1_EL1, intid);
+	coherence();
+
+	/* setup */
+	if(intid < 32){
+		u32int *rregs = getrregs(cpu);
+		rregs[GICR_IPRIORITYR0 + (intid/4)] |= prio << ((intid%4) << 3);
+		coherence();
+		rregs[GICR_ISENABLER0] = 1 << (intid%32);
+		coherence();
+		while(rregs[GICR_CTLR]&(1<<3))
+			;
+	} else {
+		dregs[GICD_IPRIORITYR0 + (intid/4)] |= prio << ((intid%4) << 3);
+		dregs[GICD_TARGETSR0 + (intid/4)] |= (1<<cpu) << ((intid%4) << 3);
+		coherence();
+		dregs[GICD_ISENABLER0 + (intid/32)] = 1 << (intid%32);
+		coherence();
+		while(dregs[GICD_CTLR]&(1<<31))
+			;
+	}
+	unlock(&vctllock);
+}
+
+void
+intrdisable(int, void (*f)(Ureg*, void*), void *a, int tbdf, char*)
+{
+	if(BUSTYPE(tbdf) == BusPCI){
+		pciintrdisable(tbdf, f, a);
+		return;
+	}
+}
--- /dev/null
+++ b/sys/src/9/lx2k/honeycomb
@@ -1,0 +1,51 @@
+dev
+	root
+	cons
+	swap
+	env
+	pipe
+	proc
+	mnt
+	srv
+	shr
+	dup
+	tls
+	cap
+	fs
+	ether	netif
+	bridge	log
+	ip	arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium inferno
+	uart
+	usb
+	pci	pci
+	sd
+
+link
+	usbxhcilx2k	usbxhci
+	ethersink
+	ethermedium
+	loopbackmedium
+	netdevmedium
+	pcilx2k		pci
+	archlx2k
+
+ip
+	tcp
+	udp
+	il
+	ipifc
+	icmp
+	icmp6
+	igmp
+	ipmux
+misc
+	gic
+	uartlx2k
+	sdnvme	pci
+port
+	int cpuserver = 0;
+bootdir
+	/$objtype/bin/paqfs
+	/$objtype/bin/auth/factotum
+	bootfs.paq
+	boot
--- /dev/null
+++ b/sys/src/9/lx2k/io.h
@@ -1,0 +1,21 @@
+enum {
+	IRQfiq		= -1,
+
+	PPI		= 16,
+	SPI		= 32,
+
+	IRQcntps	= PPI+13,
+	IRQcntpns	= PPI+14,
+
+	IRQuart		= SPI+32,
+
+	IRQusb1		= SPI+80,
+	IRQusb2		= SPI+81,
+
+	IRQpci3		= SPI+119,
+	IRQpci5		= SPI+129,
+};
+
+#define BUSUNKNOWN (-1)
+#define PCIWINDOW	0
+#define	PCIWADDR(x)	(PADDR(x)+PCIWINDOW)
--- /dev/null
+++ b/sys/src/9/lx2k/l.s
@@ -1,0 +1,729 @@
+#include "mem.h"
+#include "../arm64/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	R0, R26		/* save */
+
+	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	cachedwbinv(SB)
+	BL	l2cacheuwbinv(SB)
+	BL	cacheiinv(SB)
+
+	/* getmachno in R0 */
+	MRS	MPIDR_EL1, R0
+	BL	mpidindex(SB)
+
+	/* get MACHP(R0) in R27 */
+	MOV	$(MACHADDR(0)-KZERO), R27
+	MOVWU	$MACHSIZE, R2
+	MULW	R0, R2, R2
+	SUB	R2, R27
+
+	/* set the stack pointer */
+	ADD	$(MACHSIZE-16), R27, R1
+	MOV	R1, SP
+
+	CBNZ	R0, _startup
+
+	/* clear page table and machs */
+	MOV	$(L1BOT-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	$(L1BOT-KZERO), R0
+	BL	mmuidmap(SB)
+
+	MOV	$(L1-KZERO), R0
+	BL	mmu0init(SB)
+
+	SEVL
+_startup:
+	WFE
+	BL	mmuenable<>(SB)
+
+	MOV	R26, R0
+	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 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
+
+	/* set virtual timer offset to zero */
+	MOV	$0, R0
+	MSR	R0, CNTVOFF_EL2
+
+	/* 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	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	$0x7, R2	// PARange
+	ADD	R2<<32, R1	// IPS
+	MSR	R1, TCR_EL1
+	ISB	$SY
+
+	/* load the page tables */
+	MOV	$(L1BOT-KZERO), R0
+	MOV	$(L1TOP-KZERO), R1
+	ISB	$SY
+	MSR	R0, TTBR0_EL1
+	MSR	R1, 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
+	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
+	MOVWU	ov+8(FP), R1
+	MOVWU	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 idlehands(SB), 1, $-4
+	DMB	$ISH
+	MOVW	nrdy(SB), R0
+	CBNZ	R0, _ready
+	WFI
+_ready:
+	RETURN
+
+TEXT vcycles(SB), 1, $-4
+	MRS	CNTVCT_EL0, R0
+	RETURN
+
+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
+	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
+
+/*
+ * floating-point support.
+ */
+TEXT fpon(SB), 1, $-4
+	MOVW $(3<<20), R0
+	MSR R0, CPACR_EL1
+	ISB $SY
+	RETURN
+
+TEXT fpoff(SB), 1, $-4
+	MOVW $(0<<20), R0
+	MSR R0, CPACR_EL1
+	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.
+
+	MOVP	R26, R27, 224(RSP)
+	MOVP	R28, R29, 240(RSP)
+
+	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
+
+	MOVP	224(RSP), R26, R27
+	MOVP	240(RSP), R28, R29
+
+	MOV	256(RSP), R30	// link
+
+	ADD	$TRAPFRAMESIZE, RSP
+	ERET
+
+TEXT itsatrap<>(SB), 1, $-4
+_itsatrap:
+	MOVP	R1, R2, 24(RSP)
+	MOVP	R3, R4, 40(RSP)
+	MOVP	R5, R6, 56(RSP)
+	MOVP	R7, R8, 72(RSP)
+	MOVP	R9, R10, 88(RSP)
+	MOVP	R11, R12, 104(RSP)
+	MOVP	R13, R14, 120(RSP)
+	MOVP	R15, R16, 136(RSP)
+
+	MOVP	R18, R19, 160(RSP)
+	MOVP	R20, R21, 176(RSP)
+	MOVP	R22, R23, 192(RSP)
+	MOVP	R24, R25, 208(RSP)
+
+// trap/irq/fiq/serr from EL0
+TEXT vtrap0(SB), 1, $-4
+	MOVP	R26, R27, 224(RSP)
+	MOVP	R28, R29, 240(RSP)
+
+	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
+
+	MOVP	224(RSP), R26, R27
+	MOVP	240(RSP), R28, R29
+
+_intrreturn:
+	MOVP	16(RSP), R0, R1
+	MOVP	32(RSP), R2, R3
+	MOVP	48(RSP), R4, R5
+	MOVP	64(RSP), R6, R7
+	MOVP	80(RSP), R8, R9
+	MOVP	96(RSP), R10, R11
+	MOVP	112(RSP), R12, R13
+	MOVP	128(RSP), R14, R15
+	MOVP	144(RSP), R16, R17
+	MOVP	160(RSP), R18, R19
+	MOVP	176(RSP), R20, R21
+	MOVP	192(RSP), R22, R23
+	MOVP	208(RSP), R24, 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
+
+	MOVP	R0, R1, 16(RSP)
+	MOVP	R2, R3, 32(RSP)
+	MOVP	R4, R5, 48(RSP)
+	MOVP	R6, R7, 64(RSP)
+	MOVP	R8, R9, 80(RSP)
+	MOVP	R10, R11, 96(RSP)
+	MOVP	R12, R13, 112(RSP)
+	MOVP	R14, R15, 128(RSP)
+	MOVP	R16, R17, 144(RSP)
+	MOVP	R18, R19, 160(RSP)
+	MOVP	R20, R21, 176(RSP)
+	MOVP	R22, R23, 192(RSP)
+	MOVP	R24, R25, 208(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
+
+	MOVP	R0, R1, 16(RSP)
+	MOVP	R2, R3, 32(RSP)
+	MOVP	R4, R5, 48(RSP)
+	MOVP	R6, R7, 64(RSP)
+	MOVP	R8, R9, 80(RSP)
+	MOVP	R10, R11, 96(RSP)
+	MOVP	R12, R13, 112(RSP)
+	MOVP	R14, R15, 128(RSP)
+	MOVP	R16, R17, 144(RSP)
+	MOVP	R18, R19, 160(RSP)
+	MOVP	R20, R21, 176(RSP)
+	MOVP	R22, R23, 192(RSP)
+	MOVP	R24, R25, 208(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
+
+	MOVP	R0, R1, 16(RSP)
+	MOVP	R2, R3, 32(RSP)
+	MOVP	R4, R5, 48(RSP)
+	MOVP	R6, R7, 64(RSP)
+	MOVP	R8, R9, 80(RSP)
+	MOVP	R10, R11, 96(RSP)
+	MOVP	R12, R13, 112(RSP)
+	MOVP	R14, R15, 128(RSP)
+	MOVP	R16, R17, 144(RSP)
+	MOVP	R18, R19, 160(RSP)
+	MOVP	R20, R21, 176(RSP)
+	MOVP	R22, R23, 192(RSP)
+	MOVP	R24, R25, 208(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
+
+	MOVP	R0, R1, 16(RSP)
+	MOVP	R2, R3, 32(RSP)
+	MOVP	R4, R5, 48(RSP)
+	MOVP	R6, R7, 64(RSP)
+	MOVP	R8, R9, 80(RSP)
+	MOVP	R10, R11, 96(RSP)
+	MOVP	R12, R13, 112(RSP)
+	MOVP	R14, R15, 128(RSP)
+	MOVP	R16, R17, 144(RSP)
+	MOVP	R18, R19, 160(RSP)
+	MOVP	R20, R21, 176(RSP)
+	MOVP	R22, R23, 192(RSP)
+	MOVP	R24, R25, 208(RSP)
+
+	MOV	R30, 256(RSP)	// link
+
+	MRS	ESR_EL1, R0
+	ORR	$(3<<32), R0	// type
+_vserrpatch:
+	B	_vserrpatch	// branch to vtrapX() patched in
+
+/* fault-proof memcpy */
+TEXT peek(SB), 1, $-4
+	MOV	R0, R1
+	MOV	dst+8(FP), R2
+	MOVWU	len+16(FP), R0
+TEXT _peekinst(SB), 1, $-4
+_peekloop:
+	MOVBU	(R1)1!, R3
+	MOVBU	R3, (R2)1!
+	SUBS	$1, R0
+	BNE	_peekloop
+	RETURN
+
+TEXT smccall(SB), 1, $32
+	/* save extern registers */
+	MOVP	R26, R27, (RSP)
+
+	/* R0 = Ureg */
+	MOV	R0, R8
+
+	/* save ureg pointer */
+	MOV	R8, 16(RSP)
+
+	MOVP	0(R8), R0, R1
+	MOVP	16(R8), R2, R3
+	MOVP	32(R8), R4, R5
+	MOVP	48(R8), R6, R7
+
+	SMC
+
+	/* restore ureg pointer */
+	MOV	16(RSP), R8
+
+	MOVP	R0, R1, 0(R8)
+	MOVP	R2, R3, 16(R8)
+
+	/* restore extern registers */
+	MOVP	(RSP), R26, R27
+
+	RETURN
+
+
+	
--- /dev/null
+++ b/sys/src/9/lx2k/main.c
@@ -1,0 +1,448 @@
+#include "u.h"
+#include "tos.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "pool.h"
+#include "io.h"
+#include "../arm64/sysreg.h"
+#include "ureg.h"
+
+#include "rebootcode.i"
+
+Conf conf;
+
+#define	MAXCONF 64
+static char *confname[MAXCONF];
+static char *confval[MAXCONF];
+static int nconf = -1;
+
+void
+bootargsinit(void)
+{
+	int i, j, n;
+	char *cp, *line[MAXCONF], *p, *q;
+
+	/*
+	 *  parse configuration args from dos file plan9.ini
+	 */
+	cp = BOOTARGS;
+	cp[BOOTARGSLEN-1] = 0;
+
+	/*
+	 * Strip out '\r', change '\t' -> ' '.
+	 */
+	p = cp;
+	for(q = cp; *q; q++){
+		if(*q == -1)
+			break;
+		if(*q == '\r')
+			continue;
+		if(*q == '\t')
+			*q = ' ';
+		*p++ = *q;
+	}
+	*p = 0;
+
+	n = getfields(cp, line, MAXCONF, 1, "\n");
+	if(n <= 0){
+		/* empty plan9.ini, no configuration passed */
+		return;
+	}
+
+	nconf = 0;
+	for(i = 0; i < n; i++){
+		if(*line[i] == '#')
+			continue;
+		cp = strchr(line[i], '=');
+		if(cp == nil)
+			continue;
+		*cp++ = '\0';
+		for(j = 0; j < nconf; j++){
+			if(cistrcmp(confname[j], line[i]) == 0)
+				break;
+		}
+		confname[j] = line[i];
+		confval[j] = cp;
+		if(j == nconf)
+			nconf++;
+	}
+}
+
+char*
+getconf(char *name)
+{
+	int i;
+
+	for(i = 0; i < nconf; i++)
+		if(cistrcmp(confname[i], name) == 0)
+			return confval[i];
+	return nil;
+}
+
+void
+setconfenv(void)
+{
+	int i;
+
+	if(nconf < 0){
+		/* use defaults when there was no configuration */
+		ksetenv("console", "0", 1);
+		return;
+	}
+
+	for(i = 0; i < nconf; i++){
+		if(confname[i][0] != '*')
+			ksetenv(confname[i], confval[i], 0);
+		ksetenv(confname[i], confval[i], 1);
+	}
+}
+
+void
+writeconf(void)
+{
+	char *p, *q;
+	int n;
+
+	p = getconfenv();
+	if(waserror()) {
+		free(p);
+		nexterror();
+	}
+
+	/* convert to name=value\n format */
+	for(q=p; *q; q++) {
+		q += strlen(q);
+		*q = '=';
+		q += strlen(q);
+		*q = '\n';
+	}
+	n = q - p + 1;
+	if(n >= BOOTARGSLEN)
+		error("kernel configuration too large");
+	memmove(BOOTARGS, p, n);
+	memset(BOOTARGS+n, 0, BOOTARGSLEN-n);
+	poperror();
+	free(p);
+}
+
+int
+isaconfig(char *, int, ISAConf *)
+{
+	return 0;
+}
+
+/*
+ *  starting place for first process
+ */
+void
+init0(void)
+{
+	char buf[2*KNAMELEN], **sp;
+
+	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);
+		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];
+
+	splhi();
+	fpukexit(nil, nil);
+	touser((uintptr)sp);
+}
+
+void
+confinit(void)
+{
+	int userpcnt;
+	ulong kpages;
+	char *p;
+	int i;
+
+	conf.nmach = MAXMACH;
+	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(userpcnt < 10)
+		userpcnt = 60 + cpuserver*10;
+
+	conf.npage = 0;
+	for(i = 0; i < nelem(conf.mem); i++)
+		conf.npage += conf.mem[i].npage;
+
+	kpages = conf.npage - (conf.npage*userpcnt)/100;
+	if(kpages > ((uintptr)-VDRAM)/BY2PG)
+		kpages = ((uintptr)-VDRAM)/BY2PG;
+
+	conf.upages = conf.npage - kpages;
+	conf.ialloc = (kpages/2)*BY2PG;
+
+	/* set up other configuration parameters */
+	conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
+	if(cpuserver)
+		conf.nproc *= 3;
+	if(conf.nproc > 4000)
+		conf.nproc = 4000;
+	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;
+	imagmem->maxsize = kpages;
+}
+
+void
+machinit(void)
+{
+	m->ticks = 1;
+	m->perf.period = 1;
+	active.machs[m->machno] = 1;
+}
+
+int
+mpidindex(uvlong mpid)
+{
+	switch(mpid&((1<<MPIDMASK)-1)){
+	case 0x1:	return 1;
+	case 0x100:	return 2;
+	case 0x101:	return 3;
+	case 0x200:	return 4;
+	case 0x201:	return 5;
+	case 0x300:	return 6;
+	case 0x301:	return 7;
+	case 0x400:	return 8;
+	case 0x401:	return 9;
+	case 0x500:	return 10;
+	case 0x501:	return 11;
+	case 0x600:	return 12;
+	case 0x601:	return 13;
+	case 0x700:	return 14;
+	case 0x701:	return 15;
+	}
+	return 0;	
+}
+
+static uvlong
+machmpid(int machno)
+{
+	static uvlong ids[] = {
+		0x0,   0x1,
+		0x100, 0x101,
+		0x200, 0x201,
+		0x300, 0x301,
+		0x400, 0x401,
+		0x500, 0x501,
+		0x600, 0x601,
+		0x700, 0x701,
+	};
+	return ids[machno];
+}
+
+void
+mpinit(void)
+{
+	extern void _start(void);
+	int i;
+
+	for(i = 1; i < conf.nmach; i++){
+		Ureg u = {0};
+
+		assert(mpidindex(machmpid(i)) == i);
+
+		MACHP(i)->machno = i;
+		cachedwbinvse(MACHP(i), MACHSIZE);
+
+		u.r0 = 0x84000003;	/* CPU_ON */
+		u.r1 = machmpid(i);
+		u.r2 = PADDR(_start);
+		u.r3 = i;
+		smccall(&u);
+	}
+	synccycles();
+}
+
+void
+cpuidprint(void)
+{
+	iprint("cpu%d: %dMHz ARM Cortex A72\n", m->machno, m->cpumhz);
+}
+
+void
+main(void)
+{
+	machinit();
+	if(m->machno){
+		trapinit();
+		fpuinit();
+		intrinit();
+		clockinit();
+		cpuidprint();
+		synccycles();
+		timersinit();
+		mmu1init();
+		m->ticks = MACHP(0)->ticks;
+		schedinit();
+		return;
+	}
+	quotefmtinstall();
+	bootargsinit();
+	meminit();
+	confinit();
+	xinit();
+	uartconsinit();
+	printinit();
+	print("\nPlan 9\n");
+	trapinit();
+	fpuinit();
+	intrinit();
+	clockinit();
+	cpuidprint();
+	timersinit();
+	pageinit();
+	procinit0();
+	initseg();
+	links();
+	chandevreset();
+	userinit();
+	mpinit();
+	mmu1init();
+	schedinit();
+}
+
+void
+exit(int)
+{
+	Ureg u = { .r0 = 0x84000002 };	/* CPU_OFF */
+
+	cpushutdown();
+	splfhi();
+
+	if(m->machno == 0){
+		/* clear secrets */
+		zeroprivatepages();
+		poolreset(secrmem);
+
+		u.r0 = 0x84000009;	/* SYSTEM RESET */
+	}
+	smccall(&u);
+}
+
+static void
+rebootjump(void *entry, void *code, ulong size)
+{
+	void (*f)(void*, void*, ulong);
+
+	intrcpushutdown();
+
+	/* redo identity map */
+	setttbr(PADDR(L1BOT));
+
+	/* setup reboot trampoline function */
+	f = (void*)REBOOTADDR;
+	memmove(f, rebootcode, sizeof(rebootcode));
+
+	cachedwbinvse(f, sizeof(rebootcode));
+	cacheiinvse(f, sizeof(rebootcode));
+
+	(*f)(entry, code, size);
+
+	for(;;);
+}
+
+void
+reboot(void*, void *code, ulong size)
+{
+	writeconf();
+	while(m->machno != 0){
+		procwired(up, 0);
+		sched();
+	}
+
+	cpushutdown();
+	delay(2000);
+
+	splfhi();
+
+	/* turn off buffered serial console */
+	serialoq = nil;
+
+	/* shutdown devices */
+	chandevshutdown();
+
+	/* stop the clock */
+	clockshutdown();
+	intrsoff();
+
+	/* clear secrets */
+	zeroprivatepages();
+	poolreset(secrmem);
+
+	/* off we go - never to return */
+	rebootjump((void*)(KTZERO-KZERO), code, size);
+}
+
+void
+dmaflush(int clean, void *p, ulong len)
+{
+	uintptr s = (uintptr)p;
+	uintptr e = (uintptr)p + len;
+
+	if(clean){
+		s &= ~(BLOCKALIGN-1);
+		e += BLOCKALIGN-1;
+		e &= ~(BLOCKALIGN-1);
+		cachedwbse((void*)s, e - s);
+		return;
+	}
+	if(s & BLOCKALIGN-1){
+		s &= ~(BLOCKALIGN-1);
+		cachedwbinvse((void*)s, BLOCKALIGN);
+		s += BLOCKALIGN;
+	}
+	if(e & BLOCKALIGN-1){
+		e &= ~(BLOCKALIGN-1);
+		if(e < s)
+			return;
+		cachedwbinvse((void*)e, BLOCKALIGN);
+	}
+	if(s < e)
+		cachedinvse((void*)s, e - s);
+}
--- /dev/null
+++ b/sys/src/9/lx2k/mem.c
@@ -1,0 +1,85 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+#define INITMAP	(ROUND((uintptr)end + BY2PG, PGLSZ(1))-KZERO)
+
+/*
+ * Create initial identity map in top-level page table
+ * (L1BOT) for TTBR0. This page table is only used until
+ * mmu1init() loads m->mmutop.
+ */
+void
+mmuidmap(uintptr *l1bot)
+{
+	uintptr pa, pe, attr;
+
+	/* VDRAM */
+	attr = PTEWRITE | PTEAF | PTEKERNEL | PTEUXN | PTESH(SHARE_INNER);
+	pe = -KZERO;
+	for(pa = VDRAM - KZERO; pa < pe; pa += PGLSZ(PTLEVELS-1))
+		l1bot[PTLX(pa, PTLEVELS-1)] = pa | PTEVALID | PTEBLOCK | attr;
+}
+
+/*
+ * Create initial shared kernel page table (L1) for TTBR1.
+ * This page table coveres the INITMAP and VIRTIO,
+ * and later we fill the ram mappings in meminit().
+ */
+void
+mmu0init(uintptr *l1)
+{
+	uintptr va, pa, pe, attr;
+
+	/* DRAM - INITMAP */
+	attr = PTEWRITE | PTEAF | PTEKERNEL | PTEUXN | PTESH(SHARE_INNER);
+	pe = INITMAP;
+	for(pa = VDRAM - KZERO, va = VDRAM; pa < pe; pa += PGLSZ(1), va += PGLSZ(1))
+		l1[PTL1X(va, 1)] = pa | PTEVALID | PTEBLOCK | attr;
+
+	/* VIRTIO */
+	attr = PTEWRITE | PTEAF | PTEKERNEL | PTEUXN | PTEPXN | PTESH(SHARE_OUTER) | PTEDEVICE;
+	pe = PHYSIOEND;
+	for(pa = PHYSIO, va = VIRTIO; pa < pe; pa += PGLSZ(1), va += PGLSZ(1)){
+		if(((pa|va) & PGLSZ(1)-1) != 0){
+			l1[PTL1X(va, 1)] = (uintptr)l1 | PTEVALID | PTETABLE;
+			for(; pa < pe && ((va|pa) & PGLSZ(1)-1) != 0; pa += PGLSZ(0), va += PGLSZ(0)){
+				assert(l1[PTLX(va, 0)] == 0);
+				l1[PTLX(va, 0)] = pa | PTEVALID | PTEPAGE | attr;
+			}
+			break;
+		}
+		l1[PTL1X(va, 1)] = pa | PTEVALID | PTEBLOCK | attr;
+	}
+
+	if(PTLEVELS > 2)
+	for(va = KSEG0; va != 0; va += PGLSZ(2))
+		l1[PTL1X(va, 2)] = (uintptr)&l1[L1TABLEX(va, 1)] | PTEVALID | PTETABLE;
+
+	if(PTLEVELS > 3)
+	for(va = KSEG0; va != 0; va += PGLSZ(3))
+		l1[PTL1X(va, 3)] = (uintptr)&l1[L1TABLEX(va, 2)] | PTEVALID | PTETABLE;
+}
+
+void
+meminit(void)
+{
+	char *p;
+
+	/* GPP DRAM Region #1 */
+	conf.mem[0].base = PGROUND((uintptr)end - KZERO);
+	conf.mem[0].limit = 0x100000000ULL;
+
+	kmapram(conf.mem[0].base, conf.mem[0].limit);
+
+	conf.mem[0].npage = (conf.mem[0].limit - conf.mem[0].base)/BY2PG;
+
+	if(p = getconf("*maxmem")){
+		/* GPP DRAM Region #2 */
+		conf.mem[1].base = 0x2080000000ULL;
+		conf.mem[1].limit = strtoull(p, 0, 0);
+		conf.mem[1].npage = (conf.mem[1].limit - conf.mem[1].base)/BY2PG;
+	}
+}
--- /dev/null
+++ b/sys/src/9/lx2k/mem.h
@@ -1,0 +1,148 @@
+/*
+ * 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 */
+
+/*
+ * 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 */
+#define	ROUND(s, sz)	(((s)+(sz-1))&~(sz-1))
+#define	PGROUND(s)	ROUND(s, BY2PG)
+
+/* effective virtual address space */
+#define EVASHIFT	34
+#define EVAMASK		((1ULL<<EVASHIFT)-1)
+
+#define PTSHIFT		(PGSHIFT-3)
+#define PTLEVELS	(((EVASHIFT-PGSHIFT)+PTSHIFT-1)/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	((-KSEG0+PGLSZ(2)-1)/PGLSZ(2))
+#define L1TABLE(v, l)	(L1TABLES - ((PTLX(v, 2) % L1TABLES) >> (((l)-1)*PTSHIFT)) + (l)-1)
+#define L1TOPSIZE	(1ULL << (EVASHIFT - PTLEVELS*PTSHIFT))
+
+#define MPIDMASK	11
+#define	MAXMACH		1			/* 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)
+
+#define VDRAM		(0xFFFFFFFFC0000000ULL)	/* 0x80000000 - 0xc0000000 */
+#define	KTZERO		(VDRAM + 0x100000)	/* 0x80100000 - kernel text start */
+
+#define PHYSIO		(0x1000000ULL)
+#define PHYSIOEND	(0x10000000ULL)
+
+#define	VIRTIO		(0xFFFFFFFFB0000000ULL)
+
+#define	KZERO		(0xFFFFFFFF40000000ULL)	/* 0x00000000 - kernel address space */
+
+#define VMAP		(0xFFFFFFFF00000000ULL)	/* 0x00000000 - 0x40000000 */
+
+#define KMAPEND		(0xFFFFFFFF00000000ULL)	/* 0x140000000 */
+#define KMAP		(0xFFFFFFFE00000000ULL)	/*  0x40000000 */
+
+#define KSEG0		(0xFFFFFFFE00000000ULL)
+
+/* temporary identity map for TTBR0 (using only top-level) */
+#define L1BOT		((L1-L1TOPSIZE)&-BY2PG)
+
+/* shared kernel page table for TTBR1 */
+#define L1		(L1TOP-L1SIZE)
+#define L1SIZE		((L1TABLES+PTLEVELS-2)*BY2PG)
+#define L1TOP		((MACHADDR(MAXMACH-1)-L1TOPSIZE)&-BY2PG)
+
+#define MACHADDR(n)	(KTZERO-((n)+1)*MACHSIZE)
+
+#define CONFADDR	(VDRAM + 0x10000)	/* 0x40010000 */
+
+#define BOOTARGS	((char*)CONFADDR)
+#define BOOTARGSLEN	0x10000
+
+#define	REBOOTADDR	(VDRAM-KZERO + 0x20000)	/* 0x40020000 */
+
+#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	8192
+#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 PTEPXN		(1ULL<<53)
+#define PTEUXN		(1ULL<<54)
+
+#define PTEKERNEL	PTEAP(0)
+#define PTEUSER		PTEAP(1)
+#define PTEWRITE	PTEAP(0)
+#define PTERONLY	PTEAP(2)
+#define PTENOEXEC	(PTEPXN|PTEUXN)
+
+#define PTECACHED	PTEMA(MA_MEM_WB)
+#define PTEWT		PTEMA(MA_MEM_WT)
+#define PTEUNCACHED	PTEMA(MA_MEM_UC)
+#define PTEDEVICE	PTEMA(MA_DEV_nGnRE)
+
+/*
+ * Physical machine information from here on.
+ *	PHYS addresses as seen from the arm cpu.
+ *	BUS  addresses as seen from peripherals
+ */
+#define	PHYSDRAM	0
+
+#define MIN(a, b)	((a) < (b)? (a): (b))
+#define MAX(a, b)	((a) > (b)? (a): (b))
--- /dev/null
+++ b/sys/src/9/lx2k/mkfile
@@ -1,0 +1,115 @@
+CONF=honeycomb
+CONFLIST=honeycomb
+
+kzero=0xffffffff40000000
+loadaddr=0xffffffffc0100000
+
+objtype=arm64
+</$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\
+	rdb.$O\
+	rebootcmd.$O\
+	segment.$O\
+	syscallfmt.$O\
+	sysfile.$O\
+	sysproc.$O\
+	taslock.$O\
+	tod.$O\
+	xalloc.$O\
+	userinit.$O\
+
+OBJ=\
+	l.$O\
+	cache.v8.$O\
+	clock.$O\
+	fpu.$O\
+	main.$O\
+	mmu.$O\
+	mem.$O\
+	sysreg.$O\
+	random.$O\
+	trap.$O\
+	$CONF.root.$O\
+	$CONF.rootc.$O\
+	$DEVS\
+	$PORT\
+
+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\
+#	/$objtype/lib/libdtracy.a\
+
+9:V:	$p$CONF $p$CONF.u
+
+$p$CONF.u:D:	$p$CONF
+	aux/aout2uimage -Z$kzero $p$CONF
+
+$p$CONF:D:	$OBJ $CONF.$O $LIB
+	$LD -o $target -T$loadaddr -l $prereq
+	size $target
+
+$OBJ: $HFILES
+
+install:V: /$objtype/$p$CONF
+
+/$objtype/$p$CONF:D: $p$CONF $p$CONF.u
+	cp -x $p$CONF $p$CONF.u /$objtype/
+
+ARM64FILES=`{../port/mkfilelist ../arm64}
+^($ARM64FILES)\.$O:R:	'../arm64/\1.c'
+	$CC $CFLAGS -I. -. ../arm64/$stem1.c
+
+cache.v8.$O:	../arm64/cache.v8.s
+	$AS $AFLAGS -I. -. ../arm64/cache.v8.s
+init9.$O:	../arm64/init9.s
+	$AS $AFLAGS -I. -. ../arm64/init9.s
+rebootcode.$O:	../arm64/rebootcode.s
+	$AS $AFLAGS -I. -. ../arm64/rebootcode.s
+
+<../boot/bootmkfile
+<../port/portmkfile
+<|../port/mkbootrules $CONF
+
+main.$O: rebootcode.i
+
+pcilx2k.$O: ../port/pci.h
+usbxhcilx2k.$O: ../port/usbxhci.h
+usdhc.$O: ../port/sd.h
+
+l.$O main.$O clock.$O gic.$O cache.v8.$O fpu.$O trap.$O rebootcode.$O: ../arm64/sysreg.h
+
+initcode.out:		init9.$O initcode.$O /$objtype/lib/libc.a
+	$LD -l -R1 -s -o $target $prereq
+
+rebootcode.out:		rebootcode.$O cache.v8.$O
+	$LD -l -H6 -R1 -T0x80020000 -s -o $target $prereq
+
+$CONF.clean:
+	rm -rf $p$CONF $p$CONF.u errstr.h $CONF.c boot$CONF.c
--- /dev/null
+++ b/sys/src/9/lx2k/pcilx2k.c
@@ -1,0 +1,372 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/pci.h"
+
+typedef struct Intvec Intvec;
+struct Intvec
+{
+	Pcidev *p;
+	void (*f)(Ureg*, void*);
+	void *a;
+};
+
+typedef struct Ctlr Ctlr;
+struct Ctlr
+{
+	uvlong	mem_base;
+	uvlong	mem_size;
+	uvlong	cfg_base;
+	uvlong	cfg_size;
+	uvlong	io_base;
+	uvlong	io_size;
+
+	int	bno, ubn;
+	int	irq;
+
+	u32int	*dbi;
+	u32int	*cfg;
+	Pcidev	*bridge;
+
+	Lock;
+	Intvec	vec[32];
+};
+
+static Ctlr ctlrs[2] = {
+	{
+		0x9040000000ULL, 0x40000000,
+		0x9000000000ULL, 0x2000,
+		0x9000020000ULL, 0x10000,
+		0, 127, IRQpci3,
+		(u32int*)(VIRTIO + 0x2600000),
+	},
+	{
+		0xa040000000ULL, 0x40000000,
+		0xa000000000ULL, 0x2000,
+		0xa000020000ULL, 0x10000,
+		128, 255, IRQpci5,
+		(u32int*)(VIRTIO + 0x2800000),
+	},
+};
+
+enum {
+	IATU_MAX		= 8,
+	IATU_INBOUND		= 1<<31,
+
+	IATU_OFFSET		= 0x900/4,
+
+	IATU_REGION_INDEX	= 0x00/4,
+
+	IATU_REGION_CTRL_1	= 0x04/4,
+		CTRL_1_INCREASE_REGION_SIZ	= 1<<13,
+
+		CTRL_1_TYPE_SHIFT		= 0,
+		CTRL_1_TYPE_MASK		= 0x1F<<CTRL_1_TYPE_SHIFT,
+		CTRL_1_TYPE_MEM			= 0x0<<CTRL_1_TYPE_SHIFT,
+		CTRL_1_TYPE_IO			= 0x2<<CTRL_1_TYPE_SHIFT,
+		CTRL_1_TYPE_CFG0		= 0x4<<CTRL_1_TYPE_SHIFT,
+		CTRL_1_TYPE_CFG1		= 0x5<<CTRL_1_TYPE_SHIFT,
+
+	IATU_REGION_CTRL_2	= 0x08/4,
+		CTRL_2_REGION_EN		= 1<<31,
+
+	IATU_LWR_BASE_ADDR	= 0x0C/4,
+	IATU_UPPER_BASE_ADDR	= 0x10/4,
+	IATU_LWR_LIMIT_ADDR	= 0x14/4,
+	IATU_LWR_TARGET_ADDR	= 0x18/4,
+	IATU_UPPER_TARGET_ADDR	= 0x1C/4,
+};
+
+/* disable all iATU's */
+static void
+iatuinit(Ctlr *ctlr)
+{
+	u32int *reg = &ctlr->dbi[IATU_OFFSET];
+	int index;
+
+	for(index=0; index < IATU_MAX; index++){
+		reg[IATU_REGION_INDEX] = index;
+		reg[IATU_REGION_CTRL_2] &= ~CTRL_2_REGION_EN;
+
+		reg[IATU_REGION_INDEX] |= IATU_INBOUND;
+		reg[IATU_REGION_CTRL_2] &= ~CTRL_2_REGION_EN;
+	}
+}
+
+static void
+iatucfg(Ctlr *ctlr, int index, u32int type, uvlong target, uvlong base, uvlong size)
+{
+	uvlong limit = base + size - 1;
+	u32int *reg = &ctlr->dbi[IATU_OFFSET];
+
+	assert(size > 0);
+	assert(index < IATU_MAX);
+	assert((index & IATU_INBOUND) == 0);
+
+	reg[IATU_REGION_INDEX] = index;
+	reg[IATU_REGION_CTRL_2] &= ~CTRL_2_REGION_EN;
+
+	reg[IATU_LWR_BASE_ADDR] = base;
+	reg[IATU_UPPER_BASE_ADDR] = base >> 32;
+	reg[IATU_LWR_LIMIT_ADDR] = limit;
+	reg[IATU_LWR_TARGET_ADDR] = target;
+	reg[IATU_UPPER_TARGET_ADDR] = target >> 32;
+
+	type &= CTRL_1_TYPE_MASK;
+	if(((size-1)>>32) != 0)
+		type |= CTRL_1_INCREASE_REGION_SIZ;
+
+	reg[IATU_REGION_CTRL_1] = type;
+	reg[IATU_REGION_CTRL_2] = CTRL_2_REGION_EN;
+
+	while((reg[IATU_REGION_CTRL_2] & CTRL_2_REGION_EN) == 0)
+		microdelay(10);
+}
+
+static Ctlr*
+bus2ctlr(int bno)
+{
+	Ctlr *ctlr;
+
+	for(ctlr = ctlrs; ctlr < &ctlrs[nelem(ctlrs)]; ctlr++)
+		if(bno >= ctlr->bno && bno <= ctlr->ubn)
+			return ctlr;
+	return nil;
+}
+
+static void*
+cfgaddr(int tbdf, int rno)
+{
+	Ctlr *ctlr;
+
+	ctlr = bus2ctlr(BUSBNO(tbdf));
+	if(ctlr == nil)
+		return nil;
+
+	if(pciparentdev == nil){
+		if(BUSDNO(tbdf) != 0 || BUSFNO(tbdf) != 0)
+			return nil;
+		return (uchar*)ctlr->dbi + rno;
+	}
+
+	iatucfg(ctlr, 0,
+		pciparentdev->parent==nil? CTRL_1_TYPE_CFG0: CTRL_1_TYPE_CFG1,
+		BUSBNO(tbdf)<<20 | BUSDNO(tbdf)<<15 | BUSFNO(tbdf)<<12,
+		ctlr->cfg_base, ctlr->cfg_size);
+
+	return (uchar*)ctlr->cfg + rno;
+}
+
+int
+pcicfgrw32(int tbdf, int rno, int data, int read)
+{
+	u32int *p;
+
+	if((p = cfgaddr(tbdf, rno & ~3)) != nil){
+		if(read)
+			data = *p;
+		else
+			*p = data;
+	} else {
+		data = -1;
+	}
+	return data;
+}
+
+int
+pcicfgrw16(int tbdf, int rno, int data, int read)
+{
+	u16int *p;
+
+	if((p = cfgaddr(tbdf, rno & ~1)) != nil){
+		if(read)
+			data = *p;
+		else
+			*p = data;
+	} else {
+		data = -1;
+	}
+	return data;
+}
+
+int
+pcicfgrw8(int tbdf, int rno, int data, int read)
+{
+	u8int *p;
+
+	if((p = cfgaddr(tbdf, rno)) != nil){
+		if(read)
+			data = *p;
+		else
+			*p = data;
+	} else {
+		data = -1;
+	}
+	return data;
+}
+
+enum {
+	MISC_CONTROL_1		= 0x8BC/4,
+		DBI_RO_WR_EN 	= 1<<0,
+};
+
+static void
+pciinterrupt(Ureg *ureg, void *arg)
+{
+	Ctlr *ctlr = arg;
+	Intvec *vec;
+
+	ilock(ctlr);
+	for(vec = ctlr->vec; vec < &ctlr->vec[nelem(ctlr->vec)]; vec++){
+		if(vec->f != nil)
+			(*vec->f)(ureg, vec->a);
+	}
+	iunlock(ctlr);
+}
+
+static void
+pciintrinit(Ctlr *ctlr)
+{
+
+	intrenable(ctlr->irq+0, pciinterrupt, ctlr, BUSUNKNOWN, "pci");
+	intrenable(ctlr->irq+1, pciinterrupt, ctlr, BUSUNKNOWN, "pci");
+	intrenable(ctlr->irq+2, pciinterrupt, ctlr, BUSUNKNOWN, "pci");
+	intrenable(ctlr->irq+3, pciinterrupt, ctlr, BUSUNKNOWN, "pci");
+}
+
+void
+pciintrenable(int tbdf, void (*f)(Ureg*, void*), void *a)
+{
+	Ctlr *ctlr;
+	Intvec *vec;
+	Pcidev *p;
+
+	ctlr = bus2ctlr(BUSBNO(tbdf));
+	if(ctlr == nil){
+		print("pciintrenable: %T: unknown controller\n", tbdf);
+		return;
+	}
+
+	if((p = pcimatchtbdf(tbdf)) == nil){
+		print("pciintrenable: %T: unknown device\n", tbdf);
+		return;
+	}
+	if(pcimsidisable(p) < 0){
+		print("pciintrenable: %T: device doesnt support vec\n", tbdf);
+		return;
+	}
+
+	ilock(ctlr);
+	for(vec = ctlr->vec; vec < &ctlr->vec[nelem(ctlr->vec)]; vec++){
+		if(vec->p == p){
+			vec->p = nil;
+			break;
+		}
+	}
+	for(vec = ctlr->vec; vec < &ctlr->vec[nelem(ctlr->vec)]; vec++){
+		if(vec->p == nil){
+			vec->p = p;
+			vec->a = a;
+			vec->f = f;
+			break;
+		}
+	}
+	iunlock(ctlr);
+
+	if(vec >= &ctlr->vec[nelem(ctlr->vec)]){
+		print("pciintrenable: %T: out of isr slots\n", tbdf);
+		return;
+	}
+}
+
+void
+pciintrdisable(int tbdf, void (*f)(Ureg*, void*), void *a)
+{
+	Ctlr *ctlr;
+	Intvec *vec;
+
+	ctlr = bus2ctlr(BUSBNO(tbdf));
+	if(ctlr == nil){
+		print("pciintrenable: %T: unknown controller\n", tbdf);
+		return;
+	}
+
+	ilock(ctlr);
+	for(vec = ctlr->vec; vec < &ctlr->vec[nelem(ctlr->vec)]; vec++){
+		if(vec->p == nil)
+			continue;
+		if(vec->p->tbdf == tbdf && vec->f == f && vec->a == a){
+			vec->f = nil;
+			vec->a = nil;
+			vec->p = nil;
+			break;
+		}
+	}
+	iunlock(ctlr);
+}
+
+static void
+rootinit(Ctlr *ctlr)
+{
+	uvlong base;
+	ulong ioa;
+
+	iatuinit(ctlr);
+
+	ctlr->cfg = vmap(ctlr->cfg_base, ctlr->cfg_size);
+	if(ctlr->cfg == nil)
+		return;
+
+	ctlr->dbi[MISC_CONTROL_1] |= DBI_RO_WR_EN;
+
+	/* bus number */
+	ctlr->dbi[PciPBN/4] &= ~0xFFFFFF;
+	ctlr->dbi[PciPBN/4] |= ctlr->bno | (ctlr->bno+1)<<8 | ctlr->ubn<<16;
+
+	/* command */
+	ctlr->dbi[PciPCR/4] &= ~0xFFFF;
+	ctlr->dbi[PciPCR/4] |= IOen | MEMen | MASen | SErrEn;
+
+	/* device class/subclass */
+	ctlr->dbi[PciRID/4] &= ~0xFFFF0000;
+	ctlr->dbi[PciRID/4] |=  0x06040000;
+
+	ctlr->dbi[PciBAR0/4] = 0;
+	ctlr->dbi[PciBAR1/4] = 0;
+
+	ctlr->dbi[MISC_CONTROL_1] &= ~DBI_RO_WR_EN;
+
+	ctlr->ubn = pciscan(ctlr->bno, &ctlr->bridge, nil);
+	if(ctlr->bridge == nil || ctlr->bridge->bridge == nil)
+		return;
+
+	pciintrinit(ctlr);
+
+	iatucfg(ctlr, 1, CTRL_1_TYPE_IO, ctlr->io_base, ctlr->io_base, ctlr->io_size);
+	iatucfg(ctlr, 2, CTRL_1_TYPE_MEM, ctlr->mem_base, ctlr->mem_base, ctlr->mem_size);
+
+	ioa = ctlr->io_base;
+	base = ctlr->mem_base;
+	pcibusmap(ctlr->bridge, &base, &ioa, 1);
+
+	pcihinv(ctlr->bridge);
+}
+
+static void
+pcicfginit(void)
+{
+	int i;
+
+	fmtinstall('T', tbdffmt);
+	for(i = 0; i < nelem(ctlrs); i++)
+		rootinit(&ctlrs[i]);
+}
+
+void
+pcilx2klink(void)
+{
+	pcicfginit();
+}
--- /dev/null
+++ b/sys/src/9/lx2k/uartlx2k.c
@@ -1,0 +1,366 @@
+/*
+ * PL011 UART from Miller's BCM2835 driver
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "../port/error.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+enum {
+	DR	=	0x00>>2,
+	RSRECR	=	0x04>>2,
+	FR	=	0x18>>2,
+		TXFE	= 1<<7,
+		RXFF	= 1<<6,
+		TXFF	= 1<<5,
+		RXFE	= 1<<4,
+		BUSY	= 1<<3,
+
+	ILPR	=	0x20>>2,
+	IBRD	=	0x24>>2,
+	FBRD	=	0x28>>2,
+	LCRH	=	0x2c>>2,
+		WLENM	= 3<<5,
+		WLEN8	= 3<<5,
+		WLEN7	= 2<<5,
+		WLEN6	= 1<<5,
+		WLEN5	= 0<<5,
+		FEN	= 1<<4,	/* fifo enable */
+		STP2	= 1<<3,	/* 2 stop bits */
+		EPS	= 1<<2,	/* even parity select */
+		PEN	= 1<<1,	/* parity enabled */
+		BRK	= 1<<0,	/* send break */
+
+	CR	=	0x30>>2,
+		CTSEN	= 1<<15,
+		RTSEN	= 1<<14,
+		RTS	= 1<<11,
+		RXE	= 1<<9,
+		TXE	= 1<<8,
+		LBE	= 1<<7,
+		UARTEN	= 1<<0,
+		
+	IFLS	=	0x34>>2,
+	IMSC	=	0x38>>2,
+		TXIM	= 1<<5,
+		RXIM	= 1<<4,
+
+	RIS	=	0x3c>>2,
+	MIS	=	0x40>>2,
+	ICR	=	0x44>>2,
+	DMACR	=	0x48>>2,
+	ITCR	=	0x80>>2,
+	ITIP	=	0x84>>2,
+	ITOP	=	0x88>>2,
+	TDR	=	0x8c>>2,
+};
+
+extern PhysUart lx2kphysuart;
+
+static Uart lx2kuart = {
+	.regs	= (u32int*)(VIRTIO+0x11c0000ULL),
+	.name	= "uart0",
+	.freq	= 24*Mhz,
+	.baud	= 115200,
+	.phys	= &lx2kphysuart,
+};
+
+static Uart*
+pnp(void)
+{
+	return &lx2kuart;
+}
+
+static void
+interrupt(Ureg*, void *arg)
+{
+	Uart *uart = arg;
+	u32int *reg = (u32int*)uart->regs;
+
+	coherence();
+	while((reg[FR] & RXFE) == 0)
+		uartrecv(uart, reg[DR] & 0xFF);
+	if((reg[FR] & TXFF) == 0)
+		uartkick(uart);
+	reg[ICR] = 1<<5 | 1<<6 | 1<<7 | 1<<8 | 1<<9 | 1<<10;
+	coherence();
+}
+
+static void
+disable(Uart *uart)
+{
+	u32int *reg = (u32int*)uart->regs;
+
+	/* disable interrupt */
+	reg[IMSC] = 0;
+	coherence();
+
+	/* clear interrupt */
+	reg[ICR] = 1<<5 | 1<<6 | 1<<7 | 1<<8 | 1<<9 | 1<<10;
+	coherence();
+
+	/* wait for last transmission to complete */
+	while((reg[FR] & BUSY) != 0)
+		delay(1);
+
+	/* disable uart */
+	reg[CR] = 0;
+	coherence();
+
+	/* flush rx fifo */
+	reg[LCRH] &= ~FEN;
+	coherence();
+}
+
+static void
+uartoff(Uart *uart)
+{
+	u32int *reg = (u32int*)uart->regs;
+	u32int im;
+
+	im = reg[IMSC];
+	disable(uart);
+	reg[IMSC] = im;
+}
+
+static void
+uarton(Uart *uart)
+{
+	u32int *reg = (u32int*)uart->regs;
+
+	/* disable fifo */
+	reg[LCRH] &= ~FEN;
+	coherence();
+
+	/* enable uart */
+	reg[CR] = UARTEN | RXE | TXE;
+	coherence();
+}
+
+static void
+enable(Uart *uart, int ie)
+{
+	u32int *reg = (u32int*)uart->regs;
+
+	disable(uart);
+	if(ie){
+		intrenable(IRQuart, interrupt, uart, BUSUNKNOWN, uart->name);
+		reg[IMSC] = TXIM|RXIM;
+	}
+	uarton(uart);
+}
+
+static void
+linectl(Uart *uart, u32int set, u32int clr)
+{
+	u32int *reg = (u32int*)uart->regs;
+
+	if(uart->enabled)
+		uartoff(uart);
+
+	reg[LCRH] = set | (reg[LCRH] & ~clr);
+
+	if(uart->enabled)
+		uarton(uart);
+}
+
+static void
+kick(Uart *uart)
+{
+	u32int *reg = (u32int*)uart->regs;
+
+	if(uart->blocked)
+		return;
+	coherence();
+	while((reg[FR] & TXFF) == 0){
+		if(uart->op >= uart->oe && uartstageoutput(uart) == 0)
+			break;
+		reg[DR] = *(uart->op++);
+	}
+	coherence();
+}
+
+static void
+dobreak(Uart *uart, int ms)
+{
+	linectl(uart, BRK, 0);
+	delay(ms);
+	linectl(uart, 0, BRK);
+}
+
+static int
+baud(Uart *uart, int n)
+{
+	u32int *reg = (u32int*)uart->regs;
+
+	if(uart->freq <= 0 || n <= 0)
+		return -1;
+
+	if(uart->enabled)
+		uartoff(uart);
+
+	reg[IBRD] = (uart->freq >> 4) / n;
+	reg[FBRD] = (uart->freq >> 4) % n;
+
+	if(uart->enabled)
+		uarton(uart);
+
+	uart->baud = n;
+	return 0;
+}
+
+static int
+bits(Uart *uart, int n)
+{
+	switch(n){
+	case 8:
+		linectl(uart, WLEN8, WLENM);
+		break;
+	case 7:
+		linectl(uart, WLEN7, WLENM);
+		break;
+	case 6:
+		linectl(uart, WLEN6, WLENM);
+		break;
+	case 5:
+		linectl(uart, WLEN5, WLENM);
+		break;
+	default:
+		return -1;
+	}
+	uart->bits = n;
+	return 0;
+}
+
+static int
+stop(Uart *uart, int n)
+{
+	switch(n){
+	case 1:
+		linectl(uart, 0, STP2);
+		break;
+	case 2:
+		linectl(uart, STP2, 0);
+		break;
+	default:
+		return -1;
+	}
+	uart->stop = n;
+	return 0;
+}
+
+static int
+parity(Uart *uart, int n)
+{
+	switch(n){
+	case 'n':
+		linectl(uart, 0, PEN);
+		break;
+	case 'e':
+		linectl(uart, EPS|PEN, 0);
+		break;
+	case 'o':
+		linectl(uart, PEN, EPS);
+		break;
+	default:
+		return -1;
+	}
+	uart->parity = n;
+	return 0;
+}
+
+static void
+modemctl(Uart *uart, int on)
+{
+	uart->modem = on;
+}
+
+static void
+rts(Uart*, int)
+{
+}
+
+static long
+status(Uart *uart, void *buf, long n, long offset)
+{
+	char *p;
+
+	p = malloc(READSTR);
+	if(p == nil)
+		error(Enomem);
+	snprint(p, READSTR,
+		"b%d\n"
+		"dev(%d) type(%d) framing(%d) overruns(%d) "
+		"berr(%d) serr(%d)\n",
+
+		uart->baud,
+		uart->dev,
+		uart->type,
+		uart->ferr,
+		uart->oerr,
+		uart->berr,
+		uart->serr
+	);
+	n = readstr(offset, buf, n, p);
+	free(p);
+
+	return n;
+}
+
+static void
+donothing(Uart*, int)
+{
+}
+
+static void
+putc(Uart *uart, int c)
+{
+	u32int *reg = (u32int*)uart->regs;
+
+	while((reg[FR] & TXFF) != 0)
+		;
+	reg[DR] = c & 0xFF;
+}
+
+static int
+getc(Uart *uart)
+{
+	u32int *reg = (u32int*)uart->regs;
+
+	while((reg[FR] & RXFE) != 0)
+		;
+	return reg[DR] & 0xFF;
+}
+
+void
+uartconsinit(void)
+{
+	consuart = &lx2kuart;
+	consuart->console = 1;
+	uartctl(consuart, "l8 pn s1");
+	uartputs(kmesg.buf, kmesg.n);
+}
+
+PhysUart lx2kphysuart = {
+	.name		= "lx2k",
+	.pnp		= pnp,
+	.enable		= enable,
+	.disable	= disable,
+	.kick		= kick,
+	.dobreak	= dobreak,
+	.baud		= baud,
+	.bits		= bits,
+	.stop		= stop,
+	.parity		= parity,
+	.modemctl	= donothing,
+	.rts		= rts,
+	.dtr		= donothing,
+	.status		= status,
+	.fifo		= donothing,
+	.getc		= getc,
+	.putc		= putc,
+};
--- /dev/null
+++ b/sys/src/9/lx2k/usbxhcilx2k.c
@@ -1,0 +1,71 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+#include	"../port/usb.h"
+#include	"../port/usbxhci.h"
+
+static void
+coreinit(u32int *reg)
+{
+	enum {
+		GCTL	= 0xC110/4,
+			PWRDNSCALE_SHIFT = 19,
+			PWRDNSCALE_MASK = 0x3FFF << PWRDNSCALE_SHIFT,
+			PRTCAPDIR_SHIFT = 12,
+			PRTCAPDIR_MASK = 3 << PRTCAPDIR_SHIFT,
+			DISSCRAMBLE = 1<<3,
+			DSBLCLKGTNG = 1<<0,
+
+		GUCTL	= 0xC12C/4,
+			USBHSTINAUTORETRY = 1<<14,
+
+		GFLADJ	= 0xC630/4,
+			GFLADJ_30MHZ_SDBND_SEL = 1<<7,
+			GFLADJ_30MHZ_SHIFT = 0,
+			GFLADJ_30MHZ_MASK = 0x3F << GFLADJ_30MHZ_SHIFT,
+
+	};
+	reg[GCTL] &= ~(PWRDNSCALE_MASK | DISSCRAMBLE | DSBLCLKGTNG | PRTCAPDIR_MASK);
+	reg[GCTL] |= 2<<PWRDNSCALE_SHIFT | 1<<PRTCAPDIR_SHIFT;
+	reg[GUCTL] |= USBHSTINAUTORETRY;
+	reg[GFLADJ] = (reg[GFLADJ] & ~GFLADJ_30MHZ_MASK) | 0x20<<GFLADJ_30MHZ_SHIFT | GFLADJ_30MHZ_SDBND_SEL;
+}
+
+static int
+reset(Hci *hp)
+{
+	static Xhci *ctlrs[2];
+	Xhci *ctlr;
+	int i;
+
+	for(i=0; i<nelem(ctlrs); i++){
+		if(ctlrs[i] == nil){
+			uintptr base = VIRTIO + 0x2100000 + i*0x10000;
+			ctlr = xhcialloc((u32int*)base, base - KZERO, 0x10000);
+			if(ctlr == nil)
+				break;
+			ctlrs[i] = ctlr;
+			goto Found;
+		}
+	}
+	return -1;
+
+Found:
+	hp->tbdf = BUSUNKNOWN;
+	hp->irq = IRQusb1 + i;
+	xhcilinkage(hp, ctlr);
+
+	coreinit(ctlr->mmio);
+
+	return 0;
+}
+
+void
+usbxhcilx2klink(void)
+{
+	addhcitype("xhci", reset);
+}
--- a/sys/src/boot/mkfile
+++ b/sys/src/boot/mkfile
@@ -2,6 +2,7 @@
 	bcm\
 	bitsy\
 	efi\
+	honeycomb\
 	pc\
 	qemu\
 	reform\
--