shithub: riscv

Download patch

ref: fc968ae333c3e34f3b3b4cc36a3b856f0078ed16
parent: af517539e09f690d2fa3911a8d652fd45b39ce6e
author: moody <moody@sakuya>
date: Sat May 24 16:04:56 EDT 2025

riscv64: add non working WIP kernel

Bootstrap MMU looks ok, uart causes fault

--- /dev/null
+++ b/sys/src/9/riscv64/clock.c
@@ -1,0 +1,56 @@
+#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"
+
+/* Fixed tick frequency in QEMU */
+static uvlong freq = 10000000;
+
+extern uvlong rdtime(void);
+
+void
+clockinit(void)
+{
+}
+
+void
+timerset(uvlong next)
+{
+	// TODO
+}
+
+uvlong
+fastticks(uvlong *hz)
+{
+	if(hz)
+		*hz = freq;
+	return rdtime();
+}
+
+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);
+}
--- /dev/null
+++ b/sys/src/9/riscv64/dat.h
@@ -1,0 +1,227 @@
+/*
+ * 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 */
+	FPnotify = 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 */
+	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	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/riscv64/fns.h
@@ -1,0 +1,173 @@
+#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 hvccall(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 uintptr mmukmap(uintptr, uintptr, usize);
+extern void kmapram(uintptr, uintptr);
+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);
+
+/* 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);
+
+/* uartimx */
+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);
+
+/* pciqemu */
+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);
+
+/* bootargs */
+extern void bootargsinit(void);
--- /dev/null
+++ b/sys/src/9/riscv64/fpu.c
@@ -1,0 +1,24 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+#include "ureg.h"
+#include "../riscv64/sysreg.h"
+
+FPsave*
+notefpsave(Proc *p)
+{
+#ifdef XXX
+	if(p->fpsave == nil)
+		return nil;
+	if(p->fpstate == (FPinactive|FPnotify)){
+		p->fpsave = fpalloc(p->fpsave);
+		memmove(p->fpsave, p->fpsave->link, sizeof(FPsave));
+		p->fpstate = FPinactive;
+	}
+	return p->fpsave->link;
+#endif
+	return nil;
+}
--- /dev/null
+++ b/sys/src/9/riscv64/init9.s
@@ -1,0 +1,4 @@
+TEXT main(SB), 1, $8
+	MOV	$setSB(SB), R3		/* load the SB */
+	MOV	$boot(SB), R0
+	JAL	R1, startboot(SB)
--- /dev/null
+++ b/sys/src/9/riscv64/io.h
@@ -1,0 +1,3 @@
+#define BUSUNKNOWN (-1)
+#define PCIWINDOW	0
+#define	PCIWADDR(x)	(PADDR(x)+PCIWINDOW)
--- /dev/null
+++ b/sys/src/9/riscv64/l.s
@@ -1,0 +1,167 @@
+#include <atom.h>
+#define ARG 8
+
+#include "mem.h"
+#include "sysreg.h"
+
+#define SFENCE	WORD	$0x12000073
+#define WFI	WORD	$0x10500073
+
+#define SATP_SV39	(8ULL<<60)
+#define MAKE_SATP(x)	(SATP_SV39|((x)>>12))
+
+TEXT	_start(SB), 1, $-4
+	MOV	$setSB(SB), R3
+	JAL	R1, mmudisable<>(SB)
+
+	MOV	$(MACHADDR(0)-KZERO), R7
+	AND	$(MAXMACH-1), R10
+	MOVW	$MACHSIZE, R11
+	MULW	R10, R11, R11
+	SUB	R11, R7
+
+	ADD	$(MACHSIZE-16), R7, R11
+	MOV	R11, R2
+
+	MOV	$(L1-KZERO), R11
+	MOV	$(MACHADDR(-1)-KZERO), R12
+_zerol1:
+	MOV	R0, (R11)
+	ADD	$8, R11
+	BNE	R11, R12, _zerol1
+
+	MOV	$edata(SB), R11
+	MOV	$end(SB), R12
+_zerobss:
+	MOV	R0, (R11)
+	ADD	$8, R11
+	BNE	R11, R12, _zerobss
+
+	MOV	$(L1-KZERO), R8
+	JAL	R1, mmu0init(SB)
+
+	JAL	R1, mmuenable<>(SB)
+
+	MOV	$KTZERO, R11
+	MOV	$zoinked<>(SB), R12
+	AND	$(BY2PG-1), R12
+	ADD	R11, R12, R12
+	JMP	(R12)
+
+TEXT	zoinked<>(SB), 1, $-4
+	MOV	$setSB(SB), R3
+	JAL	R1, main(SB)
+
+TEXT	stop<>(SB), 1, $-4
+_stop:
+	WFI
+	JMP	_stop
+
+TEXT	mmudisable<>(SB), 1, $-4
+	SFENCE
+	MOV	R0, CSR(SATP)
+	SFENCE
+	RET
+
+TEXT	mmuenable<>(SB), 1, $-4
+	MOV	$MAKE_SATP(L1-KZERO), R8
+	SFENCE
+	MOV	R8, CSR(SATP)
+	SFENCE
+	RET
+
+TEXT	cas(SB), 1, $-4
+TEXT	cmpswap(SB), 1, $-4
+	MOVWU	ov+8(FP), R12
+	MOVWU	nv+(8+4)(FP), R13
+	MOV	R0, R11
+	FENCE_RW
+_spincas:
+	LRW(ARG, 14)
+	SLL	$32, R14
+	SRL	$32, R14
+	BNE	R12, R14, _fail
+	FENCE_RW
+	SCW(13, ARG, 14)
+	BNE	R14, _spincas
+	MOV	$1, R11
+_fail:
+	FENCE_RW
+	MOV	R11, R(ARG)
+	RET
+
+TEXT	tas(SB), 1, $-4
+TEXT	_tas(SB), 1, $-4
+	MOV	$1, R10
+	FENCE_RW
+	AMOW(Amoswap, AQ|RL, 10, ARG, ARG)
+	FENCE_RW
+	RET
+
+TEXT	setlabel(SB), 1, $-4
+	MOV	R2, (R8)
+	MOV	R1, 8(R8)
+	MOV	R0, R8
+	RET
+
+TEXT	gotolabel(SB), 1, $-4
+	MOVW	r+8(FP), R13
+	BNE	R13, _ok
+	MOV	$1, R13
+_ok:	MOV	(R8), R2
+	MOV	8(R8), R1
+	MOV	R13, R8
+	RET
+
+TEXT	rdtime(SB), 1, $-4
+	FENCE
+	MOV	CSR(TIME), R8
+	RET
+
+TEXT	coherence(SB), 1, $-4
+	FENCE
+	RET
+
+TEXT	islo(SB), 1, $-4
+	RET
+
+TEXT	splhi(SB), 1, $-4
+	RET
+
+TEXT	spllo(SB), 1, $-4
+	RET
+
+TEXT	splx(SB), 1, $-4
+	RET
+
+TEXT	flushtlb(SB), 1, $-4
+	RET
+TEXT	flushasidvall(SB), 1, $-4
+	RET
+TEXT	flushasidva(SB), 1, $-4
+	RET
+TEXT	flushasid(SB), 1, $-4
+	RET
+
+TEXT	cachedwbinvse(SB), 1, $-4
+	RET
+TEXT	cacheiinvse(SB), 1, $-4
+	RET
+
+TEXT	vcycles(SB), 1, $-4
+	RET
+TEXT	perfticks(SB), 1, $-4
+	RET
+
+	/* rename as we don't have ttbr */
+TEXT	setttbr(SB), 1, $-4
+	RET
+
+
+TEXT	idlehands(SB), 1, $-4
+	FENCE
+	MOVW	nrdy(SB), R8
+	BNE	R8, R0, _ready
+	WFI
+_ready:
+	RET
--- /dev/null
+++ b/sys/src/9/riscv64/main.c
@@ -1,0 +1,323 @@
+#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 "sysreg.h"
+#include "ureg.h"
+
+//#include "rebootcode.i"
+
+Conf conf;
+
+int
+isaconfig(char *, int, ISAConf *)
+{
+	return 0;
+}
+
+/*
+ *  starting place for first process
+ */
+void
+init0(void)
+{
+#ifdef XXX
+	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);
+#endif
+}
+
+void
+confinit(void)
+{
+	int userpcnt;
+	ulong kpages;
+	char *p;
+	int i;
+
+	conf.nmach = 1;
+	if(p = getconf("*ncpu"))
+		conf.nmach = strtol(p, 0, 0);
+	if(conf.nmach > MAXMACH)
+		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;
+}
+
+void
+mpinit(void)
+{
+#ifdef XXX
+	extern void _start(void);
+	int i;
+
+	for(i = 1; i < conf.nmach; i++){
+		Ureg u = {0};
+
+		MACHP(i)->machno = i;
+		cachedwbinvse(MACHP(i), MACHSIZE);
+
+		u.r0 = 0x84000003;	/* CPU_ON */
+		u.r1 = (sysrd(MPIDR_EL1) & ~(0xFF0000FFULL)) | i;
+		u.r2 = PADDR(_start);
+		u.r3 = i;
+		hvccall(&u);
+	}
+	synccycles();
+#endif
+}
+
+void
+cpuidprint(void)
+{
+	iprint("cpu%d: 1000MHz QEMU\n", m->machno);
+}
+
+char *
+getconf(char *name)
+{
+	return nil;
+}
+
+void
+main(void)
+{
+	machinit();
+#ifdef XXX
+	if(m->machno){
+		trapinit();
+		fpuinit();
+		intrinit();
+		clockinit();
+		cpuidprint();
+		synccycles();
+		timersinit();
+		mmu1init();
+		m->ticks = MACHP(0)->ticks;
+		schedinit();
+		return;
+	}
+#endif
+	uartconsinit();
+#ifdef XXX
+	quotefmtinstall();
+	bootargsinit();
+	meminit();
+	confinit();
+	xinit();
+	printinit();
+	print("\nPlan 9\n");
+#endif
+	for(;;);
+#ifdef XXX
+	trapinit();
+	fpuinit();
+	intrinit();
+	clockinit();
+	cpuidprint();
+	timersinit();
+	pageinit();
+	procinit0();
+	initseg();
+	links();
+	chandevreset();
+	userinit();
+	mpinit();
+	mmu1init();
+	schedinit();
+#endif
+}
+
+void
+exit(int)
+{
+#ifdef XXX
+	Ureg u = { .r0 = 0x84000002 };	/* CPU_OFF */
+
+	cpushutdown();
+	splfhi();
+
+	if(m->machno == 0){
+		/* clear secrets */
+		zeroprivatepages();
+		poolreset(secrmem);
+
+		u.r0 = 0x84000009;	/* SYSTEM RESET */
+	}
+	hvccall(&u);
+#endif
+}
+
+static void
+rebootjump(void *entry, void *code, ulong size)
+{
+#ifdef XXX
+	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);
+#endif
+
+	for(;;);
+}
+
+void
+reboot(void*, void *code, ulong size)
+{
+#ifdef XXX
+	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);
+#endif
+}
+
+void
+dmaflush(int clean, void *p, ulong len)
+{
+#ifdef XXX
+	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);
+#endif
+}
--- /dev/null
+++ b/sys/src/9/riscv64/mem.c
@@ -1,0 +1,61 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+#define INITMAP	(ROUND((uintptr)end + BY2PG, PGLSZ(1)))
+
+void
+mmu0init(uintptr *l1)
+{
+	uintptr va, pa, pe, attr;
+
+	/* VDRAM */
+	attr = PTEREAD | PTEWRITE | PTEEXEC;
+	pe = -KZERO;
+	for(pa = VDRAM - KZERO; pa < pe; pa += PGLSZ(PTLEVELS-1))
+		l1[PTLX(pa, PTLEVELS-1)] = PA2PTE(pa) | PTEVALID | attr;
+
+	/* DRAM - INITMAP */
+	pe = INITMAP;
+	for(pa = VDRAM - KZERO, va = VDRAM; pa < pe; pa += PGLSZ(1), va += PGLSZ(1))
+		l1[PTL1X(va, 1)] = PA2PTE(pa) | PTEVALID | attr;
+
+	/* VIRTIO */
+	attr = PTEREAD | PTEWRITE | 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)] = PA2PTE((uintptr)l1) | PTEVALID;
+			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)] = PA2PTE(pa) | PTEVALID | attr;
+			}
+			break;
+		}
+		l1[PTL1X(va, 1)] = PA2PTE(pa) | PTEVALID | attr;
+	}
+
+	if(PTLEVELS > 2)
+	for(va = KSEG0; va != 0; va += PGLSZ(2))
+		l1[PTLX(va, 2)] = PA2PTE((uintptr)&l1[L1TABLEX(va, 1)]) | PTEVALID;
+}
+
+void
+meminit(void)
+{
+	char *p;
+	uintptr l = GiB + 128 * MiB;
+
+	if(p = getconf("*maxmem"))
+		l = strtoull(p, 0, 0);
+	conf.mem[0].base = PGROUND((uintptr)end);
+	conf.mem[0].limit = l;
+
+	if(l > KLIMIT)
+		l = KLIMIT;
+	kmapram(conf.mem[0].base, l);
+
+	conf.mem[0].npage = (conf.mem[0].limit - conf.mem[0].base)/BY2PG;
+}
--- /dev/null
+++ b/sys/src/9/riscv64/mem.h
@@ -1,0 +1,118 @@
+/*
+ * 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	39
+#define EVAMASK		((1ULL<<EVASHIFT)-1)
+
+#define PTSHIFT		(PGSHIFT-3)
+#define PTLEVELS	(((EVASHIFT-PGSHIFT)+PTSHIFT-1)/PTSHIFT)
+/* PPN */
+#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	MAXMACH		8			/* 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 - */
+#define	KTZERO		(VDRAM + 0x200000)	/* 0x80200000 - kernel text start */
+
+#define PHYSIO		0
+#define PHYSIOEND	0x20000000
+
+#define	VIRTIO		(0xFFFFFFFFA0000000ULL)
+
+#define	KZERO		(0xFFFFFFFF40000000ULL)	/* 0x00000000 - kernel address space */
+
+#define VMAP		(0xFFFFFFFF00000000ULL)	/* 0x00000000 - 0x40000000 */
+
+#define KMAPEND		(0xFFFFFFFF00000000ULL)	/* 0x140000000 */
+#define KMAP		(0xFFFFFFFE00000000ULL)	/*  0x40000000 */
+
+#define KLIMIT		(VDRAM - KZERO + KMAPEND - KMAP)	/* 0x140000000 */
+
+#define KSEG0		(0xFFFFFFFE00000000ULL)
+
+/* 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 PTEVALID	(1<<0)
+#define PTEREAD		(1<<1)
+#define PTEWRITE	(1<<2)
+#define PTEEXEC		(1<<3)
+#define PTEUSER		(1<<4)
+#define PTECACHED	0
+#define PTEUNCACHED	0
+#define PTEDEVICE	0		/* FIXME */
+#define PTERONLY	PTEREAD
+
+#define PA2PTE(pa)	((pa>>12)<<10)
+#define PTE2PA(pte)	((pte>>10)<<12)
+
+/*
+ * 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/riscv64/mkfile
@@ -1,0 +1,99 @@
+CONF=qemu
+CONFLIST=qemu
+
+loadaddr=0xffffffffc0200000
+
+objtype=riscv64
+</$objtype/mkfile
+p=9
+
+DEVS=`{rc ../port/mkdevlist $CONF}
+
+PORT=\
+	alarm.$O\
+	alloc.$O\
+	allocb.$O\
+	auth.$O\
+	cache.$O\
+	chan.$O\
+	dev.$O\
+	edf.$O\
+	fault.$O\
+	mul64fract.$O\
+	page.$O\
+	parse.$O\
+	pgrp.$O\
+	portclock.$O\
+	print.$O\
+	proc.$O\
+	qio.$O\
+	qlock.$O\
+	random.$O\
+	rdb.$O\
+	rebootcmd.$O\
+	segment.$O\
+	syscallfmt.$O\
+	sysfile.$O\
+	sysproc.$O\
+	taslock.$O\
+	tod.$O\
+	xalloc.$O\
+	userinit.$O\
+
+OBJ=\
+	l.$O\
+	main.$O\
+	mmu.$O\
+	mem.$O\
+	trap.$O\
+	clock.$O\
+	fpu.$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\
+#	/$objtype/lib/libdtracy.a\
+
+9:V:	$p$CONF s$p$CONF
+
+$p$CONF:D:	$OBJ $CONF.$O $LIB
+	$LD -s -l -o $target -H6 -R0x1000 -T$loadaddr $prereq
+
+s$p$CONF:D:	$OBJ $CONF.$O $LIB
+	$LD -l -o $target -R0x1000 -T$loadaddr $prereq
+	size $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/
+
+<../boot/bootmkfile
+<../port/portmkfile
+<|../port/mkbootrules $CONF
+
+#main.$O: rebootcode.i
+
+pciqemu.$O: ../port/pci.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 -T0x40020000 -s -o $target $prereq
+
+$CONF.clean:
+	rm -rf $p$CONF s$p$CONF errstr.h $CONF.c boot$CONF.c
--- /dev/null
+++ b/sys/src/9/riscv64/mmu.c
@@ -1,0 +1,372 @@
+#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)
+
+void
+mmu1init(void)
+{
+	m->mmutop = mallocalign(L1TOPSIZE, BY2PG, 0, 0);
+	if(m->mmutop == nil)
+		panic("mmu1init: no memory for mmutop");
+	memset(m->mmutop, 0, L1TOPSIZE);
+	mmuswitch(nil);
+}
+
+/* KZERO maps the first 1GB of ram */
+uintptr
+paddr(void *va)
+{
+	if((uintptr)va >= KZERO)
+		return (uintptr)va-KZERO;
+	panic("paddr: va=%#p pc=%#p", va, getcallerpc(&va));
+}
+
+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));
+}
+
+static void*
+kmapaddr(uintptr pa)
+{
+	if(pa < (uintptr)-KZERO)
+		return (void*)(pa + KZERO);
+	if(pa < (VDRAM - KZERO) || pa >= (VDRAM - KZERO) + (KMAPEND - KMAP))
+		panic("kmapaddr: pa=%#p pc=%#p", pa, getcallerpc(&pa));
+	return (void*)(pa + KMAP - (VDRAM - KZERO));
+}
+
+KMap*
+kmap(Page *p)
+{
+	return kmapaddr(p->pa);
+}
+
+void
+kunmap(KMap*)
+{
+}
+
+void
+kmapinval(void)
+{
+}
+
+static void*
+rampage(void)
+{
+	uintptr pa;
+
+	if(conf.npage)
+		return mallocalign(BY2PG, BY2PG, 0, 0);
+
+	pa = conf.mem[0].base;
+	assert((pa % BY2PG) == 0);
+	conf.mem[0].base += BY2PG;
+	return KADDR(pa);
+}
+
+static void
+l1map(uintptr va, uintptr pa, uintptr pe, uintptr attr)
+{
+	uintptr *l1, *l0;
+
+	assert(pa < pe);
+
+	va &= -BY2PG;
+	pa &= -BY2PG;
+	pe = PGROUND(pe);
+
+	l1 = (uintptr*)L1;
+
+	while(pa < pe){
+		if(l1[PTL1X(va, 1)] == 0 && (pe-pa) >= PGLSZ(1) && ((va|pa) & PGLSZ(1)-1) == 0){
+			l1[PTL1X(va, 1)] = PTEVALID | PA2PTE(pa) | attr;
+			va += PGLSZ(1);
+			pa += PGLSZ(1);
+			continue;
+		}
+		if(l1[PTL1X(va, 1)] & PTEVALID) {
+//			assert((l1[PTL1X(va, 1)] & PTETABLE) == PTETABLE);
+			l0 = KADDR(l1[PTL1X(va, 1)] & -PGLSZ(0));
+		} else {
+			l0 = rampage();
+			memset(l0, 0, BY2PG);
+			l1[PTL1X(va, 1)] = PTEVALID | PA2PTE(PADDR(l0));
+		}
+		assert(l0[PTLX(va, 0)] == 0);
+		l0[PTLX(va, 0)] = PTEVALID | PA2PTE(pa) | attr;
+		va += BY2PG;
+		pa += BY2PG;
+	}
+}
+
+void
+kmapram(uintptr base, uintptr limit)
+{
+	if(base < (uintptr)-KZERO && limit > (uintptr)-KZERO){
+		kmapram(base, (uintptr)-KZERO);
+		kmapram((uintptr)-KZERO, limit);
+		return;
+	}
+	if(base < INITMAP)
+		base = INITMAP;
+	if(base >= limit || limit <= INITMAP)
+		return;
+
+	l1map((uintptr)kmapaddr(base), base, limit,
+		PTEWRITE | PTEREAD);
+}
+
+uintptr
+mmukmap(uintptr va, uintptr pa, usize size)
+{
+	uintptr attr, off;
+
+	if(va == 0)
+		return 0;
+
+	off = pa & BY2PG-1;
+
+//	TODO: When we support memory access cache flags, need to do a copy here
+//	attr = va & PTEMA(7);
+	attr |= PTEWRITE | PTEREAD;
+
+	va &= -BY2PG;
+	pa &= -BY2PG;
+
+	l1map(va, pa, pa + off + size, attr);
+	flushtlb();
+
+	return va + off;
+}
+
+void*
+vmap(uvlong pa, vlong size)
+{
+	static uintptr base = VMAP;
+	uvlong pe = pa + size;
+	uintptr va;
+
+	va = base;
+	base += PGROUND(pe) - (pa & -BY2PG);
+	
+	return (void*)mmukmap(va | PTEDEVICE, pa, size);
+}
+
+void
+vunmap(void *, vlong)
+{
+}
+
+static uintptr*
+mmuwalk(uintptr va, int level)
+{
+	uintptr *table, pte;
+	Page *pg;
+	int i, x;
+
+	x = PTLX(va, PTLEVELS-1);
+	table = m->mmutop;
+	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);
+		} else {
+			pg = up->mmufree;
+			if(pg == nil)
+				return nil;
+			up->mmufree = pg->next;
+			pg->va = va & -PGLSZ(i+1);
+			if((pg->next = up->mmuhead[i+1]) == nil)
+				up->mmutail[i+1] = pg;
+			up->mmuhead[i+1] = pg;
+			pte = pg->pa;
+			memset(kmapaddr(pte), 0, BY2PG);
+			coherence();
+			table[x] = pte | PTEVALID;	// XXX: Does this need PA2PTE
+		}
+		table = kmapaddr(pte);
+		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 mmutop
+	 *	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 mmutop 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;
+
+	s = splhi();
+	while((pte = mmuwalk(va, 0)) == nil){
+		spllo();
+		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 = PA2PTE(pa) | PTEVALID | PTEUSER;
+	if(needtxtflush(pg)){
+		cachedwbinvse(kmap(pg), BY2PG);
+		cacheiinvse((void*)va, BY2PG);
+		donetxtflush(pg);
+	}
+	splx(s);
+}
+
+static void
+mmufree(Proc *p)
+{
+	int i;
+
+	freeasid(p);
+
+	for(i=1; i<PTLEVELS; i++){
+		if(p->mmuhead[i] == nil)
+			break;
+		p->mmutail[i]->next = p->mmufree;
+		p->mmufree = p->mmuhead[i];
+		p->mmuhead[i] = p->mmutail[i] = nil;
+	}
+}
+
+void
+mmuswitch(Proc *p)
+{
+	uintptr va;
+	Page *t;
+
+	for(va = UZERO; va < USTKTOP; va += PGLSZ(PTLEVELS-1))
+		m->mmutop[PTLX(va, PTLEVELS-1)] = 0;
+
+	if(p == nil){
+		setttbr(PADDR(m->mmutop));
+		return;
+	}
+
+	if(p->newtlb){
+		mmufree(p);
+		p->newtlb = 0;
+	}
+
+	if(allocasid(p))
+		flushasid((uvlong)p->asid<<48);
+
+	setttbr((uvlong)p->asid<<48 | PADDR(m->mmutop));
+
+	for(t = p->mmuhead[PTLEVELS-1]; t != nil; t = t->next){
+		va = t->va;
+		m->mmutop[PTLX(va, PTLEVELS-1)] = t->pa | PTEVALID;
+	}
+}
+
+void
+mmurelease(Proc *p)
+{
+	mmuswitch(nil);
+	mmufree(p);
+	freepages(p->mmufree, nil, 0);
+	p->mmufree = nil;
+}
+
+void
+flushmmu(void)
+{
+	int x;
+
+	x = splhi();
+	up->newtlb = 1;
+	mmuswitch(up);
+	splx(x);
+}
+
+void
+checkmmu(uintptr, uintptr)
+{
+}
--- /dev/null
+++ b/sys/src/9/riscv64/qemu
@@ -1,0 +1,52 @@
+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
+#	rtc
+#	pci	pci
+	sd
+
+#link
+#	usbxhcipci	pci usbxhci
+#	ethervirtio10	pci
+#	ethersink
+#	ethermedium
+#	loopbackmedium
+#	netdevmedium
+#	pciqemu		pci
+
+#ip
+#	tcp
+#	udp
+#	il
+#	ipifc
+#	icmp
+#	icmp6
+#	igmp
+#	ipmux
+misc
+#	gic
+	uartqemu
+#	sdvirtio10	pci sdscsi
+port
+	int cpuserver = 0;
+bootdir
+	/$objtype/bin/paqfs
+	/$objtype/bin/auth/factotum
+	bootfs.paq
+	boot
--- /dev/null
+++ b/sys/src/9/riscv64/sysreg.h
@@ -1,0 +1,2 @@
+#define SATP	0x180
+#define TIME	0xc01
--- /dev/null
+++ b/sys/src/9/riscv64/trap.c
@@ -1,0 +1,161 @@
+#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 "../riscv64/sysreg.h"
+
+void
+setupwatchpts(Proc*, Watchpt*, int)
+{
+}
+
+void
+dumpstack(void) 
+{
+	// TODO
+}
+
+void
+setregisters(Ureg* ureg, char* pureg, char* uva, int n)
+{
+	ulong v;
+
+	v = ureg->ie;
+	memmove(pureg, uva, n);
+	ureg->ie = v;
+}
+
+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)
+{
+//	fpuprocfork(p);
+//	p->tpidr = up->tpidr;
+}
+
+void
+procsetup(Proc *p)
+{
+//	fpuprocsetup(p);
+//	p->tpidr = 0;
+//	syswr(TPIDR_EL0, p->tpidr);
+}
+
+void
+procsave(Proc *p)
+{
+//	fpuprocsave(p);
+//	if(p->kp == 0)
+//		p->tpidr = sysrd(TPIDR_EL0);
+//	putasid(p);	// release asid
+}
+
+void
+procrestore(Proc *p)
+{
+//	fpuprocrestore(p);
+//	if(p->kp == 0)
+//		syswr(TPIDR_EL0, p->tpidr);
+}
+
+void
+setkernur(Ureg *ureg, Proc *p)
+{
+	ureg->pc = p->sched.pc;
+	ureg->sp = p->sched.sp;
+	ureg->link = (uintptr)sched;
+}
+
+int
+userureg(Ureg* ureg)
+{
+	return 0;
+}
+
+void
+callwithureg(void (*f) (Ureg *))
+{
+#ifdef XXX
+	Ureg u;
+	
+	u.pc = getcallerpc(&f);
+	u.sp = (uintptr) &f;
+	f(&u);
+#endif
+}
+
+void
+kprocchild(Proc *p, void (*entry)(void))
+{
+#ifdef XXX
+	p->sched.pc = (uintptr) entry;
+	p->sched.sp = (uintptr) p - 16;
+	*(void**)p->sched.sp = kprocchild;	/* fake */
+#endif
+}
+
+void
+evenaddr(uintptr addr)
+{
+#ifdef XXX
+	if(addr & 3){
+		postnote(up, 1, "sys: odd address", NDebug);
+		error(Ebadarg);
+	}
+#endif
+}
+
+void
+forkchild(Proc *p, Ureg *ureg)
+{
+#ifdef XXX
+	Ureg *cureg;
+
+	p->sched.pc = (uintptr) forkret;
+	p->sched.sp = (uintptr) p - TRAPFRAMESIZE;
+
+	cureg = (Ureg*) (p->sched.sp + 16);
+	memmove(cureg, ureg, sizeof(Ureg));
+	cureg->r0 = 0;
+#endif
+}
+
+uintptr
+execregs(uintptr entry, ulong ssize, ulong nargs)
+{
+#ifdef XXX
+	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);
+#endif
+	return 0;
+}
--- /dev/null
+++ b/sys/src/9/riscv64/uartqemu.c
@@ -1,0 +1,336 @@
+/*
+ * 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 qemuphysuart;
+
+static Uart qemuuart = {
+	.regs	= (u32int*)(VIRTIO+0x10000000ULL),
+	.name	= "uart0",
+	.freq	= 24*Mhz,
+	.baud	= 115200,
+	.phys	= &qemuphysuart,
+};
+
+static Uart*
+pnp(void)
+{
+	return &qemuuart;
+}
+
+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;
+
+	/* enable 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;
+
+	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 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 = &qemuuart;
+	consuart->console = 1;
+	uartctl(consuart, "l8 pn s1");
+	for(;;) putc(consuart, 'A');
+}
+
+PhysUart qemuphysuart = {
+	.name		= "qemu",
+	.pnp		= pnp,
+	.enable		= enable,
+	.disable	= disable,
+	.kick		= kick,
+	.dobreak	= dobreak,
+	.baud		= baud,
+	.bits		= bits,
+	.stop		= stop,
+	.parity		= parity,
+	.modemctl	= donothing,
+	.rts		= rts,
+	.dtr		= donothing,
+	.fifo		= donothing,
+	.getc		= getc,
+	.putc		= putc,
+};
--