Browse Source

riscv: pull in working riscv files

This won't break any builds but this is the 'working riscv kernel'
set of files. It's essentially a squash.

Signed-off-by: Ronald G. Minnich <rminnich@gmail.com>
Ronald G. Minnich 7 years ago
parent
commit
fec95fd4d9

+ 25 - 0
sys/src/9/riscv/_spl.S

@@ -0,0 +1,25 @@
+/* these are intended to be called from library functions ONLY.
+ * at some point, we can make it all more efficient, but for now,
+ * let's make it correct.
+ */
+
+#define sstatus 0x100
+#define siebitmask 2
+/* int _splhi() */
+.globl _splhi
+_splhi:
+	li	a0, siebitmask
+	csrrc	a0, sstatus, a0
+	ret
+
+/* int _spllo() */
+.globl _spllo
+_spllo:
+	li	a0, siebitmask
+	csrrs	a0, sstatus, a0
+	ret
+
+/* what a hack. Purely a marker for profiling.*/
+.globl spldone
+spldone:
+	ret

+ 3 - 3
sys/src/9/riscv/acore.c

@@ -123,12 +123,12 @@ acsched(void)
 void
 acmmuswitch(void)
 {
-	extern Page mach0pml4;
+	extern Page mach0root;
 
-	DBG("acmmuswitch mpl4 %#p mach0pml4 %#p m0pml4 %#p\n", machp()->MMU.pml4->pa, mach0pml4.pa, sys->machptr[0]->MMU.pml4->pa);
+	DBG("acmmuswitch mpl4 %#p mach0root %#p m0root %#p\n", machp()->MMU.root->pa, mach0root.pa, sys->machptr[0]->MMU.root->pa);
 
 
-	rootput(machp()->MMU.pml4->pa);
+	rootput(machp()->MMU.root->pa);
 }
 
 /*

+ 23 - 4
sys/src/9/riscv/arch.c

@@ -20,6 +20,7 @@
 #include "fns.h"
 #include "../port/error.h"
 #include "ureg.h"
+#include "encoding.h"
 
 /* the rules are different for different compilers. We need to define up. */
 // Initialize it to force it into data.
@@ -57,7 +58,7 @@ decref(Ref *r)
 
 void fpuprocrestore(Proc *p)
 {
-	panic("fpuprocrestore");
+	if (0)print("NOT DOING fpuprocrestore");
 }
 
 void
@@ -76,7 +77,7 @@ procrestore(Proc *p)
 void
 fpuprocsave(Proc *p)
 {
-	panic("fpuprocsave");
+	print("fpuprocsave -- NEED TO IMPLEMENT");
 }
 
 /*
@@ -131,8 +132,26 @@ kprocchild(Proc* p, void (*func)(void*), void* arg)
 void
 idlehands(void)
 {
-/*	if(machp()->NIX.nixtype != NIXAC)
-	halt();*/
+	uint32_t sip = 0;
+	int i;
+	extern uint64_t *mtimecmp;
+	extern uint64_t *mtime;
+	//print("idlehands, mtime is 0x%llx mtimecmp is 0x%llx\n", *mtime, *mtimecmp);
+	//print("spin waiting for an interrupt or until mtimecmp passes mtime. \n");
+	/* toolchain is broken. Again. Puts an sret in for a wfi. Bad idea.
+	if(machp()->NIX.nixtype != NIXAC)
+		__asm__ __volatile__("wfi\n");
+	if (*mtimecmp < *mtime)
+		timerset(0);
+	 */
+	sip = (uint32_t)read_csr(sip);
+	for(i = 0; *mtimecmp < *mtime; i++) {
+		if (sip & 0x666)
+			break;
+		sip = (uint32_t)read_csr(sip);
+	}
+	//print("idlehands, mtime is 0x%llx mtimecmp is 0x%llx\n", *mtime, *mtimecmp);
+	//print("Leaving idlehands. sip is 0x%x, i is %d\n", sip, i);
 }
 
 #if 0

+ 1 - 1
sys/src/9/riscv/archriscv.c

@@ -25,7 +25,7 @@ cpuiddump(void)
 int64_t
 archhz(void)
 {
-	return 1024*1024*1024ULL;
+	return 1000*1000*1000*2ULL;
 }
 
 int

+ 19 - 9
sys/src/9/riscv/asm.c

@@ -18,9 +18,9 @@
 #include "dat.h"
 #include "fns.h"
 
-#undef DBG
+//#undef DBG
 void msg(char *);
-#define DBG(fmt, ...) msg(fmt)
+//#define DBG msg
 /*
  * Address Space Map.
  * Low duty cycle.
@@ -92,7 +92,7 @@ asmfree(uintmem addr, uintmem size, int type)
 {
 	Asm *np, *pp, **ppp;
 
-	DBG("asmfree: %#P@%#P, type %d\n", size, addr, type);
+	DBG("asmfree: %#p@%#p, type 0x%x\n", size, addr, type);
 	if(size == 0)
 		return 0;
 
@@ -157,7 +157,8 @@ asmalloc(uintmem addr, uintmem size, int type, int align)
 	uintmem a, o;
 	Asm *assem, *pp;
 
-	DBG("asmalloc: %#P@%#P, type %d\n", size, addr, type);
+	DBG("asmalloc: %p@%p, type %d\n", size, addr, type);
+//msg("before asmlock\n");
 	lock(&asmlock);
 //msg("after lock\n");
 	for(pp = nil, assem = asmlist; assem != nil; pp = assem, assem = assem->next){
@@ -331,7 +332,7 @@ asmmeminit(void)
 {
 	int i, l;
 	Asm* assem;
-	PTE *pte, *pml4;
+	PTE *pte, *root;
 	uintptr va;
 	uintmem hi, lo, mem, nextmem, pa;
 #ifdef ConfCrap
@@ -342,6 +343,14 @@ asmmeminit(void)
 
 	if((pa = mmuphysaddr(sys->vmunused)) == ~0)
 		panic("asmmeminit 1");
+	// vmunmapped is the START of unmapped memory (there is none on riscv yet).
+	// it is the END of mapped memory we have not used.
+	// vmunused is the START of mapped memory that is not used and the END
+	// of memory that is used.
+	// This code falls apart if sys->vmend - sys->vmunmapped is 0.
+	// The goal is to map memory not mapped. But it's all mapped.
+	root = UINT2PTR(machp()->MMU.root->va);
+#if 0
 	pa += sys->vmunmapped - sys->vmunused;
 	mem = asmalloc(pa, sys->vmend - sys->vmunmapped, 1, 0);
 	if(mem != pa)
@@ -350,14 +359,14 @@ asmmeminit(void)
 
 	/* assume already 2MiB aligned*/
 	assert(ALIGNED(sys->vmunmapped, 2*MiB));
-	pml4 = UINT2PTR(machp()->MMU.pml4->va);
 	while(sys->vmunmapped < sys->vmend){
-		l = mmuwalk(pml4, sys->vmunmapped, 1, &pte, asmwalkalloc);
+		l = mmuwalk(root, sys->vmunmapped, 1, &pte, asmwalkalloc);
 		DBG("%#p l %d\n", sys->vmunmapped, l);
 		*pte = pa|PteRW|PteP;
 		sys->vmunmapped += 2*MiB;
 		pa += 2*MiB;
 	}
+#endif
 
 #ifdef ConfCrap
 	cx = 0;
@@ -370,7 +379,7 @@ asmmeminit(void)
 			DBG("Skipping, it's not AsmMEMORY or AsmRESERVED\n");
 			continue;
 		}
-		va = KSEG2+assem->addr;
+		va = (uintptr_t)kseg2+assem->addr;
 		DBG("asm: addr %#P end %#P type %d size %P\n",
 			assem->addr, assem->addr+assem->size,
 			assem->type, assem->size);
@@ -389,9 +398,10 @@ asmmeminit(void)
 					continue;
 				/* This page fits entirely within the range. */
 				/* Mark it a usable */
-				if((l = mmuwalk(pml4, va, i, &pte, asmwalkalloc)) < 0)
+				if((l = mmuwalk(root, va, i, &pte, asmwalkalloc)) < 0)
 					panic("asmmeminit 3");
 
+				//print("ASMMEMINIT pte is %p\n", pte);
 				if (assem->type == AsmMEMORY)
 					*pte = mem|PteRW|PteP;
 				else

+ 164 - 9
sys/src/9/riscv/assembly.S

@@ -1,26 +1,93 @@
+# main has to be in assembly because there's not telling
+# what stack firmware has set up. It calls the
+# bsp function with stack in a0 and configstring
+# pointer in a1.
+.text
+.globl bsp
+.globl m0
+.globl m0stack
+.globl main
+main:
+	csrw sscratch, x0
+	mv	a3, a0
+        lui     a5,%hi(bsp)
+        add     a5,a5,%lo(bsp)
+        mv      a0,a5
+	//li	a1, $m0
+        lui     a5,%hi(m0)
+        add     a5,a5,%lo(m0)
+        mv      a1,a5
+	//li	a2, $m0stack
+        lui     a5,%hi(m0stack)
+        add     a5,a5,%lo(m0stack)
+	li	a6, 32768
+	add 	a5, a5, a6
+        mv      a2,a5
+	mv	tp, a1
+	mv	ra,a0
+	mv	sp, a2
+	mv 	a0, a2
+	mv	a1, a3
+	ret
 # startmach starts a function after setting up the mach
 # pointer. The function is in a0, the mach pointer in a1.
 .globl startmach
-startmach: 
-	csrw	sscratch, a1
+startmach:
+	csrw sscratch, x0
+	mv	tp, a1
 	mv	ra,a0
+	mv	sp, a2
+	mv 	a0, a2
 	ret
 
 .globl machp
 machp:
-	csrr	a0,sscratch
+	mv	a0,tp
 	ret
 
+
 /*
  * Label consists of a stack pointer and a programme counter
  * 0(%rdi) is the SP, 8(%rdi) is the PC
  */
 .global gotolabel
 gotolabel:
-	LD	sp,0(a0)
-	LD	x2,16(a0)
+	LD	sp,0(a0) // X14
+	LD	s0,16(a0) // X2
+	// X0 is zero
+	// X1 and X2 are done.
+	LD x3, 48(a0)
+	// Don't restore X4, that's reserved for Mach *
+	//LD x4, 56(a0)
+	LD x5, 64(a0)
+	LD x6, 72(a0)
+	LD x7, 80(a0)
+	LD x8, 88(a0)
+	LD x9, 96(a0)
+	//LD x10, 104(a0) this is a0
+	LD x11, 112(a0)
+	LD x12, 120(a0)
+	LD x13, 128(a0)
+	// X14 done already
+	LD x15, 144(a0)
+	LD x16, 152(a0)
+	LD x17, 160(a0)
+	LD x18, 168(a0)
+	LD x19, 176(a0)
+	LD x20, 184(a0)
+	LD x21, 192(a0)
+	LD x22, 200(a0)
+	LD x23, 208(a0)
+	LD x24, 216(a0)
+	LD x25, 224(a0)
+	LD x26, 232(a0)
+	LD x27, 240(a0)
+	LD x28, 248(a0)
+	LD x29, 256(a0)
+	LD x30, 264(a0)
+	LD x30, 272(a0)
 	LD	a0,8(a0)
-	MV	ra,a0
+	MV	ra,a0 // X1 (non zero by definition I hope.
 	RET
 
 	/* save all registers on this stack, the save stack
@@ -28,13 +95,101 @@ gotolabel:
 	*/
 .global slim_setlabel
 slim_setlabel:
-	SD	sp,0(a0)
-	SD	ra,8(a0)
-	SD	x2,16(a0)
+	// don't save X0 of course. Or x31
+	/* not so slim! */
+	SD	sp,0(a0) // X14
+	SD	ra,8(a0) // X1
+	SD	s0,16(a0) // X2
+	// X0 is zero
+	// X1 and X2 are done.
+	sd x3, 48(a0)
+	sd x4, 56(a0)
+	sd x5, 64(a0)
+	sd x6, 72(a0)
+	sd x7, 80(a0)
+	sd x8, 88(a0)
+	sd x9, 96(a0)
+	sd x10, 104(a0)
+	sd x11, 112(a0)
+	sd x12, 120(a0)
+	sd x13, 128(a0)
+	// X14 done already
+	sd x15, 144(a0)
+	sd x16, 152(a0)
+	sd x17, 160(a0)
+	sd x18, 168(a0)
+	sd x19, 176(a0)
+	sd x20, 184(a0)
+	sd x21, 192(a0)
+	sd x22, 200(a0)
+	sd x23, 208(a0)
+	sd x24, 216(a0)
+	sd x25, 224(a0)
+	sd x26, 232(a0)
+	sd x27, 240(a0)
+	sd x28, 248(a0)
+	sd x29, 256(a0)
+	sd x30, 264(a0)
+	sd x31, 272(a0)
+	mv	a0,zero
 	RET
 
 
+.globl stack
+stack:
+	mv a0, sp
+	ret
+#if 0
 .globl rdtsc
 rdtsc:
 	RDCYCLE a0
 	RET
+#endif
+/* set up uregs, then jump to a0 from uregs */
+.globl touser
+touser:
+	csrw sscratch, tp
+	LD	sp,8(a0) // X14
+	LD	s0,16(a0) // X2
+	// X0 is zero
+	// X1 and X2 are done.
+	LD x3, 48(a0)
+	LD x4, 56(a0)
+	LD x5, 64(a0)
+	LD x6, 72(a0)
+	LD x7, 80(a0)
+	LD x8, 88(a0)
+	LD x9, 96(a0)
+	// a0 and a1 are restored below.
+
+	LD x12, 120(a0)
+	LD x13, 128(a0)
+	// X14 done already
+	LD x15, 144(a0)
+	LD x16, 152(a0)
+	LD x17, 160(a0)
+	LD x18, 168(a0)
+	LD x19, 176(a0)
+	LD x20, 184(a0)
+	LD x21, 192(a0)
+	LD x22, 200(a0)
+	LD x23, 208(a0)
+	LD x24, 216(a0)
+	LD x25, 224(a0)
+	LD x26, 232(a0)
+	LD x27, 240(a0)
+	LD x28, 248(a0)
+	LD x29, 256(a0)
+	LD x30, 264(a0)
+	LD x30, 272(a0)
+	LD	a1,0(a0)
+	/* Jump to a0 in S-mode */
+	//csrr t0, mstatus
+	//li t1, ~(1<<8) // clear SPP
+	//and t0, t0, t1
+	//csrw mstatus, t0
+	csrw sepc, a1
+	LD a1, 112(a0)
+	LD a0, 104(a0)
+	.long 0x10200073
+	sret

+ 56 - 0
sys/src/9/riscv/backtrace.c

@@ -0,0 +1,56 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+int backtrace_list(uintptr_t pc, uintptr_t fp, uintptr_t *pcs, size_t nr_slots)
+{
+	size_t nr_pcs = 0;
+	while (fp && nr_pcs < nr_slots) {
+		/* could put some sanity checks in here... */
+		pcs[nr_pcs++] = pc;
+		//iprint("PC %p FP %p\n", pc, fp);
+		if (fp < KTZERO)
+			break;
+		/* PC becomes the retaddr - 1.  the -1 is to put our PC back inside the
+		 * function that called us.  this was necessary in case we called as the
+		 * last instruction in a function (would have to never return).  not
+		 * sure how necessary this still is. */
+		pc = *(uintptr_t*)(fp + sizeof(uintptr_t)) - 1;
+		fp = *(uintptr_t*)fp;
+	}
+	return nr_pcs;
+}
+
+#if 0
+void backtrace_frame(uintptr_t eip, uintptr_t ebp)
+{
+	char *func_name;
+	#define MAX_BT_DEPTH 20
+	uintptr_t pcs[MAX_BT_DEPTH];
+	size_t nr_pcs = backtrace_list(eip, ebp, pcs, MAX_BT_DEPTH);
+
+	for (int i = 0; i < nr_pcs; i++) {
+		func_name = get_fn_name(pcs[i]);
+		print("#%02d [<%p>] in %s\n", i + 1,  pcs[i], func_name);
+		kfree(func_name);
+	}
+}
+
+void backtrace(void)
+{
+	uintptr_t ebp, eip;
+	ebp = read_bp();
+	/* retaddr is right above ebp on the stack.  we subtract an additional 1 to
+	 * make sure the eip we get is actually in the function that called us.
+	 * i had a couple cases early on where call was the last instruction in a
+	 * function, and simply reading the retaddr would point into another
+	 * function (the next one in the object) */
+	eip = *(uintptr_t*)(ebp + sizeof(uintptr_t)) - 1;
+	/* jump back a frame (out of backtrace) */
+	ebp = *(uintptr_t*)ebp;
+	printk("Stack Backtrace on Core %d:\n", core_id());
+	backtrace_frame(eip, ebp);
+}
+#endif

+ 14 - 6
sys/src/9/riscv/build.json

@@ -5,10 +5,10 @@
 		],
 		"Include": [
 			"core.json",
-			"../port/riscvport.json"
+			"../ip/ip.json",
+			"../port/port.json"
 		],
 		"IncludeSomedayWhenRISCVgetsvirtio": [
-			"../ip/ip.json",
 			"../port/port.json"
 		],
 		"Kernel": {
@@ -27,6 +27,7 @@
 					"cons",
 					"dup",
 					"env",
+				        "ip",
 					"mnt",
 					"mntn",
 					"pipe",
@@ -38,7 +39,7 @@
 					"uart",
 					"ws"
 				],
-				"NoIp": [
+				"Ip": [
 					"tcp",
 					"udp",
 					"ipifc",
@@ -46,13 +47,21 @@
 					"icmp6",
 					"gre"
 				],
+				"Link": [
+					"ethermedium",
+					"loopbackmedium",
+					"netdevmedium"
+				],
 				"NoUart": [
 					"pci"
 				]
 			},
-			"NoRamfiles": {
+		        "#RamFiles": [
+				"boot: /sys/src/9/boot/bootcpu.elf.out"
+			    ],
+			"Ramfiles": {
 				"bind": "/$ARCH/bin/bind",
-				"boot": "/sys/src/9/boot/bootcpu.elf.out",
+				"boot": "/$ARCH/bin/kiss",
 				"cat": "/$ARCH/bin/cat",
 				"date": "/$ARCH/bin/date",
 				"echo": "/$ARCH/bin/echo",
@@ -65,7 +74,6 @@
 				"ps": "/$ARCH/bin/ps",
 				"ed": "/$ARCH/bin/ed",
 				"rcmain": "/rc/lib/rcmain",
-				"screenconsole": "/$ARCH/bin/aux/screenconsole",
 				"srv": "/$ARCH/bin/srv"
 			},
 			"Systab": "/sys/src/libc/9syscall/sys.h"

+ 13 - 15
sys/src/9/riscv/core.json

@@ -1,6 +1,7 @@
 {
 	"core": {
 		"Cflags": [
+			"-mcmodel=medany",
 			"-O0",
 			"-static",
 			"-ffreestanding",
@@ -21,13 +22,16 @@
 			"-I", "/sys/src/libc/9syscall"
 		],
 	    "Libs": [
-			"/$ARCH/lib/klibc.a"
+			"/$ARCH/lib/klibc.a",
+                        "/$ARCH/lib/klibip.a",
+			"/$ARCH/lib/klibsec.a"
 		],
 		"Oflags": [
 			"-z",
 			"max-page-size=0x1000",
 			"-nostdlib",
 			"-g",
+			"--no-relax",
 			"-T",
 			"kernel.ld"
 		],
@@ -40,35 +44,29 @@
 			"../boot/bootconf.json",
 			"inith.json"
 		],
-	    "MissingSourceFiles": [
-		        "acore.c",
-			"archriscv.c",
-		        "coreboot.c",
-			"devarch.c",
-			"memory.c",
-			"mmu.c",
-			"mp.c",
-			"physalloc.c",
-			"systab.c",
-			"tcore.c",
-			"trap.c"
-		],
 		"SourceFiles": [
 			"assembly.S",
 			"arch.c",
+			"archriscv.c",
 			"asm.c",
 		        "acore.c",
-			"archriscv.c",
+			"backtrace.c",
+			"coreboot.c",
 			"ctype.c",
 			"devarch.c",
 			"main.c",
 			"map.c",
+			"memory.c",
 			"mmu.c",
 			"physalloc.c",
 			"qmalloc.c",
+			"spl.c",
+			"_spl.S",
 			"syscall.c",
 			"systab.c",
 		        "tcore.c",
+		        "trap.c",
+		        "trapentry.S",
 			"uart.c"
 		]
 	}

+ 43 - 0
sys/src/9/riscv/coreboot.c

@@ -0,0 +1,43 @@
+/*
+ * This file is part of the libpayload project.
+
+ * Copyright (C) 2008 Advanced Micro Devices, Inc.
+ * Copyright (C) 2009 coresystems GmbH
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "coreboot.h"
+
+/* this implements the architecture-dependent call needed for port/devcoreboot.c */
+int get_coreboot_info(struct sysinfo_t *info)
+{
+	return -1;
+}

+ 10 - 4
sys/src/9/riscv/dat.h

@@ -38,7 +38,7 @@ typedef struct Vctl Vctl;
 
 /*
  * Conversion for Ureg to gdb reg. This avoids a lot of nonsense
- * in the outside world. TODO. 
+ * in the outside world. TODO.
  */
 enum regnames {
 	GDB_IP,
@@ -82,6 +82,7 @@ struct Label
 	uintptr_t	sp;
 	uintptr_t	pc;
 	uintptr_t       fp;
+	uintptr_t	x[32];
 };
 
 struct Fxsave {
@@ -168,11 +169,11 @@ struct NIX
  */
 struct MMMU
 {
-	uintptr_t FaultingAddress;
-	Page*	pml4;			/* root for this processor */
+	uintptr_t badaddr;
+	Page*   root;			/* root for this processor */
 	PTE*	pmap;			/* unused as of yet */
 
-	Page	pml4kludge;		/* NIX KLUDGE: we need a page */
+	Page	nixkludge;		/* NIX KLUDGE: we need a page */
 };
 
 /*
@@ -247,6 +248,8 @@ enum {
  *	proc	- syscallentry
  *	stack	- acsyscall
  *	externup - externup()
+ *
+ * riscv uses tp for mach.
  */
 struct Mach
 {
@@ -319,6 +322,8 @@ struct Stackframe
 struct Sys {
 	unsigned char	machstk[MACHSTKSZ];
 
+	Page    root;
+
 	union {
 		Mach	mach;
 		unsigned char	machpage[MACHSZ];
@@ -389,6 +394,7 @@ struct
 extern uintptr_t kseg0;
 
 extern char*rolename[];
+extern void *kseg2;
 
 
 

+ 67 - 4
sys/src/9/riscv/devarch.c

@@ -15,6 +15,7 @@
 #include "../port/error.h"
 
 #include "ureg.h"
+#include "encoding.h"
 
 /* leave this for now; we might want to keep track of MMIO apart from memory. */
 typedef struct IOMap IOMap;
@@ -519,11 +520,53 @@ numcoresread(Chan* c, void *a, int32_t n, int64_t off)
         return readstr(off, a, n, buf);
 }
 
+Queue *keybq;
+void putchar(int);
+/* total hack. */
+void
+kbdputsc(int data, int _)
+{
+	static char line[512];
+	static int len;
+	putchar(data);
+	line[len++] = data;
+	if (keybq && (data == '\n')) {
+		qiwrite(keybq, line, len);
+		len = 0;
+	}
+}
+
+static int32_t
+consoleread(Chan* c, void *vbuf, int32_t len, int64_t off64)
+{
+	int amt;
+	if (!keybq) {
+		keybq = qopen(32, 0, 0, 0);
+		if (!keybq)
+			panic("keyboard queue alloc failed");
+	}
+	amt = qread(keybq, vbuf, len);
+	print("consoleread: amt is %d\n", amt);
+	return amt;
+}
+
+static int32_t
+consolewrite(Chan* _, void *vbuf, int32_t len, int64_t off64)
+{
+	char *c = vbuf;
+
+	for(int i = 0; i < len; i++)
+		putchar(c[i]);
+	return len;
+}
+
+
 void
 archinit(void)
 {
 	addarchfile("cputype", 0444, cputyperead, nil);
 	addarchfile("numcores", 0444, numcoresread, nil);
+	addarchfile("cons", 0666, consoleread, consolewrite);
 }
 
 void
@@ -552,14 +595,34 @@ ms(void)
 
 /*
  *  set next timer interrupt
+ *  incoming units are in ns.
+ *  cycles and mtime are not connected to
+ *  other. Get the delta in ns, convert to
+ *  100 ns units, add to mtime, store in
+ *  mtimecmp.
  */
 void
 timerset(uint64_t x)
 {
-	panic("apictimerset");
-//	extern void apictimerset(uint64_t);
-
-//	apictimerset(x);
+	extern uint64_t *mtimecmp;
+	extern uint64_t *mtime;
+	uint64_t now;
+	int64_t delta;
+
+	now = rdtsc();
+	//print("now 0x%llx timerset to 0x%llx\n", now , x);
+	// I have no fucking clue why scaling it breaks this but it does.
+	//now = fastticks2ns(now);
+	//print("now 0x%llx timerset to 0x%llx\n", now , x);
+	delta = x - now;
+	//print("delta is %llx\n", delta);
+	delta /= 200;
+	if (delta < 1) {
+		print("BUST!\n");
+		delta = 10 /* one microsecond */ * 1000 /* one millisecond */ ;
+	}
+	//print("adjest x to timer ticks, divide by 500 ... 0x%llx %llx %llx \n", *mtime , delta, *mtime + delta);
+	*mtimecmp = *mtime + delta; //+ 10 /* one microsecond */ * 1000 /* one millisecond */ * 100; /* 100 milliseconds */
 }
 
 void

+ 8 - 5
sys/src/9/riscv/fns.h

@@ -10,7 +10,8 @@
 #include "../port/portfns.h"
 
 /* assembly code support from asm.S */
-void startmach(void (*f)(void), void *m);
+/* Startmach is passed an argument which is its own stack. */
+void startmach(void (*f)(void*), void *m, void *stack);
 Mach *machp(void);
 
 /* other functions */
@@ -58,6 +59,7 @@ void	dumpmmu(Proc*);
 void	dumpmmuwalk(uint64_t pa);
 void	dumpptepg(int lvl,uintptr_t pa);
 #define	evenaddr(x)				/* x86 doesn't care */
+void *findKSeg2(void);
 int	fpudevprocio(Proc*, void*, int32_t, uintptr_t, int);
 void	fpuinit(void);
 void	fpunoted(void);
@@ -111,7 +113,7 @@ void	mapraminit(uint64_t, uint64_t);
 void	mapupainit(uint64_t, uint32_t);
 void	meminit(void);
 void	mfence(void);
-void	mmuflushtlb(uint64_t);
+void	mmuflushtlb(void);
 void	mmuinit(void);
 uintptr_t	mmukmap(uintptr_t, uintptr_t, usize);
 int	mmukmapsync(uint64_t);
@@ -183,7 +185,7 @@ uint64_t	fas64(uint64_t*, uint64_t);
 #define TAS(addr)	tas32((addr))
 #define	FASP(p, v)	((void*)fas64((uint64_t*)(p), (uint64_t)(v)))
 
-void	touser(uintptr_t);
+void	touser(Ureg*);
 void	syscallentry(void);
 void	acsyscallentry(void);
 void	syscallreturn(void);
@@ -263,8 +265,9 @@ int backtrace_list(uintptr_t pc, uintptr_t fp, uintptr_t *pcs, size_t nr_slots);
 /* horror */
 static inline void __clobber_callee_regs(void)
 {
-	panic("FIX ME!");
-	//asm volatile ("" : : : "rbx", "r12", "r13", "r14", "r15");
+	// Arrived at through trial and error, this toolchain is a pain.
+	asm volatile ("" : : : "x2", "x3", "x4", "x5", "x6", "x7", /*"x8", */"x9", "x10", "x11", "x12", "x13"/*, "x15"*/);
+	// TODO: f0-f15?
 }
 
 int slim_setlabel(Label*) __attribute__((returns_twice));

+ 10 - 1
sys/src/9/riscv/init9.c

@@ -12,11 +12,20 @@ extern void startboot(char*, char**);
 void
 main(char* argv0)
 {
+	char *ar[2] = {"boot", };
+#if 0
+	// let's do this later when we want true hell.
+	extern int _gp;
+	void *v = &_gp;
+	__asm__ __volatile__ ("1:auipc gp, %pcrel_hi(_gp)\n\taddi gp, gp, %pcrel_lo(1b)\n");
+#endif
+
 	int write(int, void *, int);
+	//write(1, v, 8);
 	//do it this way to make sure it doesn't end up in .data
 	char a[1];
 	a[1] = '0';
 	write(1, a, 1);
-	startboot(argv0, &argv0);
+	startboot("*init*", ar); //argv0, &argv0);
 //	while(1) write(1, "hi\n", 3);
 }

+ 3 - 1
sys/src/9/riscv/inith.json

@@ -17,7 +17,9 @@
 		"Oflags": [
 			"-e_main",
 			"-static",
-			"-Ttext=0x200000"
+			"--no-relax",
+			"-Ttext=0x200000",
+			"-Tdata=0x400000"
 		],
 		"Post": [
 			"elf2c init > init.h"

+ 1 - 38
sys/src/9/riscv/io.h

@@ -8,44 +8,7 @@
  */
 
 enum {
-	VectorNMI	= 2,		/* non-maskable interrupt */
-	VectorBPT	= 3,		/* breakpoint */
-	VectorUD	= 6,		/* invalid opcode exception */
-	VectorCNA	= 7,		/* coprocessor not available */
-	Vector2F	= 8,		/* double fault */
-	VectorCSO	= 9,		/* coprocessor segment overrun */
-	VectorPF	= 14,		/* page fault */
-	Vector15	= 15,		/* reserved */
-	VectorCERR	= 16,		/* coprocessor error */
-
-	VectorPIC	= 32,		/* external i8259 interrupts */
-	IrqCLOCK	= 0,
-	IrqKBD		= 1,
-	IrqUART1	= 3,
-	IrqUART0	= 4,
-	IrqPCMCIA	= 5,
-	IrqFLOPPY	= 6,
-	IrqLPT		= 7,
-	IrqIRQ7		= 7,
-	IrqAUX		= 12,		/* PS/2 port */
-	IrqIRQ13	= 13,		/* coprocessor on 386 */
-	IrqATA0		= 14,
-	IrqATA1		= 15,
-	MaxIrqPIC	= 15,
-
-	VectorLAPIC	= VectorPIC+16,	/* local APIC interrupts */
-	IrqLINT0	= VectorLAPIC+0,
-	IrqLINT1	= VectorLAPIC+1,
-	IrqTIMER	= VectorLAPIC+2,
-	IrqERROR	= VectorLAPIC+3,
-	IrqPCINT	= VectorLAPIC+4,
-	IrqSPURIOUS	= VectorLAPIC+15,
-	MaxIrqLAPIC	= VectorLAPIC+15,
-
-	VectorSYSCALL	= 64,
-
-	VectorAPIC	= 65,		/* external APIC interrupts */
-	MaxVectorAPIC	= 255,
+	IrqTIMER	= 5,
 };
 
 typedef struct Vkey {

+ 488 - 111
sys/src/9/riscv/main.c

@@ -15,15 +15,24 @@
 
 #include "init.h"
 #include "io.h"
+#include "encoding.h"
+#include "ureg.h"
+#include <tos.h>
+
+
+int cpuserver = 1;
 
 extern void (*consuartputs)(char*, int);
+void query_mem(const char *config_string, uintptr_t *base, size_t *size);
+void query_rtc(const char *config_string, uintptr_t *mtime);
+void query_uint(const char *config_string, char *name, uintptr_t *val);
 
-void testPrint(uint8_t c);
+void putchar(uint8_t c);
 
 void msg(char *s)
 {
 	while (*s)
-		testPrint(*s++);
+		putchar(*s++);
 }
 void die(char *s)
 {
@@ -40,24 +49,38 @@ ndnr(void)
 static void puts(char * s, int n)
 {
 	while (n--)
-		testPrint(*s++);
+		putchar(*s++);
 }
 
-static int x = 0x123456;
-
-/* mach struct for hart 0. */
+/* mach info for hart 0. */
 /* in many plan 9 implementations this stuff is all reserved in early assembly.
  * we don't have to do that. */
-static uint64_t m0stack[4096];
-static Mach m0;
+uint64_t m0stack[4096];
+Mach m0;
+
 Sys asys, *sys=&asys;
 Conf conf;
 uintptr_t kseg0 = KZERO;
 char *cputype = "riscv";
+int64_t hz;
+uintptr_t rtc;
 
 /* I forget where this comes from and I don't care just now. */
 uint32_t kerndate;
+int maxcores = 1;
+int nosmp = 1;
+uint64_t mtimepa, mtimecmppa;
+uint64_t *mtime, *mtimecmp;
+/*
+ * kseg2 is the base of the virtual address space.
+ * it is not a constant as in amd64; in riscv there are many possible
+ * values, even on the same SOC. It is determined by firmware.
+ */
+void *kseg2;
 
+char *configstring; /* from coreboot, first arg to main */
+
+static uintptr_t sp;		/* XXX - must go - user stack of init proc */
 
 /* general purpose hart startup. We call this via startmach.
  * When we enter here, the machp() function is usable.
@@ -69,27 +92,348 @@ void hart(void)
 	die("not yet");
 }
 
-void bsp(void)
+uint64_t
+rdtsc(void)
+{
+	uint64_t cycles;
+//	msg("rdtsc\n");
+	cycles = read_csr(/*s*/cycle);
+//print("cycles in rdtsc is 0x%llx\n", cycles);
+//	msg("done rdts\n");
+	return cycles;
+}
+
+void
+loadenv(int argc, char* argv[])
+{
+	char *env[2];
+
+	/*
+	 * Process command line env options
+	 */
+	while(--argc > 0){
+		char* next = *++argv;
+		if(next[0] !='-'){
+			if (gettokens(next, env, 2, "=")  == 2){;
+				ksetenv(env[0], env[1], 0);
+			}else{
+				print("Ignoring parameter with no value: %s\n", env[0]);
+			}
+		}
+	}
+}
+
+void
+init0(void)
+{
+	Proc *up = externup();
+	char buf[2*KNAMELEN];
+	Ureg u;
+
+	up->nerrlab = 0;
+
+	/*
+	 * if(consuart == nil)
+	 * i8250console("0");
+	 */
+	spllo();
+
+	/*
+	 * These are o.k. because rootinit is null.
+	 * Then early kproc's will have a root and dot.
+	 */
+print("init0: up is %p\n", up);
+	up->slash = namec("#/", Atodir, 0, 0);
+print("1\n");
+	pathclose(up->slash->path);
+print("1\n");
+	up->slash->path = newpath("/");
+print("1\n");
+	up->dot = cclone(up->slash);
+print("1\n");
+
+	devtabinit();
+print("1\n");
+
+	if(!waserror()){
+		//snprint(buf, sizeof(buf), "%s %s", "AMD64", conffile);
+		//loadenv(oargc, oargv);
+		ksetenv("terminal", buf, 0);
+		ksetenv("cputype", cputype, 0);
+		ksetenv("pgsz", "2097152", 0);
+		// no longer. 	confsetenv();
+		poperror();
+	}
+	kproc("alarm", alarmkproc, 0);
+	//nixprepage(-1);
+	print("TOUSER: kstack is %p\n", up->kstack);
+	//debugtouser((void *)UTZERO);
+	memset(&u, 0, sizeof(u));
+	u.ip = (uintptr_t)init_main;
+	u.sp = sp;
+	u.a2 = USTKTOP-sizeof(Tos);
+	touser(&u);
+}
+
+/*
+ * Option arguments from the command line.
+ * oargv[0] is the boot file.
+ * TODO: do it.
+ */
+static int64_t oargc;
+static char* oargv[20];
+static char oargb[1024];
+static int oargblen;
+
+void
+bootargs(uintptr_t base)
 {
+	int i;
+	uint32_t ssize;
+	char **av, *p;
+
+	/*
+	 * Push the boot args onto the stack.
+	 * Make sure the validaddr check in syscall won't fail
+	 * because there are fewer than the maximum number of
+	 * args by subtracting sizeof(up->arg).
+	 */
+	i = oargblen+1;
+	p = UINT2PTR(STACKALIGN(base + BIGPGSZ - sizeof(((Proc*)0)->arg) - i));
+	memmove(p, oargb, i);
+
+	/*
+	 * Now push argc and the argv pointers.
+	 * This isn't strictly correct as the code jumped to by
+	 * touser in init9.[cs] calls startboot (port/initcode.c) which
+	 * expects arguments
+	 * 	startboot(char* argv0, char* argv[])
+	 * not the usual (int argc, char* argv[]), but argv0 is
+	 * unused so it doesn't matter (at the moment...).
+	 */
+	av = (char**)(p - (oargc+2)*sizeof(char*));
+	ssize = base + BIGPGSZ - PTR2UINT(av);
+	print("Stack size in boot args is %p\n", ssize);
+	*av++ = (char*)oargc;
+	for(i = 0; i < oargc; i++)
+		*av++ = (oargv[i] - oargb) + (p - base) + (USTKTOP - BIGPGSZ);
+	*av = nil;
+
+	sp = USTKTOP - ssize;
+	print("New sp in bootargs is %p\n", sp);
+}
+
+void
+userinit(void)
+{
+	Proc *up = externup();
+	Proc *p;
+	Segment *s;
+	KMap *k;
+	Page *pg;
+	int sno;
+
+	p = newproc();
+	p->pgrp = newpgrp();
+	p->egrp = smalloc(sizeof(Egrp));
+	p->egrp->r.ref = 1;
+	p->fgrp = dupfgrp(nil);
+	p->rgrp = newrgrp();
+	p->procmode = 0640;
+
+	kstrdup(&eve, "");
+	kstrdup(&p->text, "*init*");
+	kstrdup(&p->user, eve);
+
+	/*
+	 * Kernel Stack
+	 *
+	 * N.B. make sure there's enough space for syscall to check
+	 *	for valid args and
+	 *	space for gotolabel's return PC
+	 * AMD64 stack must be quad-aligned.
+	 */
+	p->sched.pc = PTR2UINT(init0);
+	p->sched.sp = PTR2UINT(p->kstack+KSTACK-sizeof(up->arg)-sizeof(uintptr_t));
+	p->sched.sp = STACKALIGN(p->sched.sp);
+
+	/*
+	 * User Stack
+	 *
+	 * Technically, newpage can't be called here because it
+	 * should only be called when in a user context as it may
+	 * try to sleep if there are no pages available, but that
+	 * shouldn't be the case here.
+	 */
+	sno = 0;
+	print("newseg(0x%x, %p, 0x%llx)\n", SG_STACK|SG_READ|SG_WRITE, (void *)USTKTOP-USTKSIZE, USTKSIZE/ BIGPGSZ);
+	s = newseg(SG_STACK|SG_READ|SG_WRITE, USTKTOP-USTKSIZE, USTKSIZE/ BIGPGSZ);
+	p->seg[sno++] = s;
+	pg = newpage(1, 0, USTKTOP-BIGPGSZ, BIGPGSZ, -1);
+	segpage(s, pg);
+	k = kmap(pg);
+	bootargs(VA(k));
+	kunmap(k);
+
+	/*
+	 * Text
+	 */
+	s = newseg(SG_TEXT|SG_READ|SG_EXEC, UTZERO, 1);
+	s->flushme++;
+	p->seg[sno++] = s;
+	pg = newpage(1, 0, UTZERO, BIGPGSZ, -1);
+	memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl));
+	segpage(s, pg);
+	k = kmap(s->map[0]->pages[0]);
+	/* UTZERO is only needed until we make init not have 2M block of zeros at the front. */
+	memmove(UINT2PTR(VA(k) + init_code_start - UTZERO), init_code_out, sizeof(init_code_out));
+	kunmap(k);
+
+	/*
+	 * Data
+	 */
+	s = newseg(SG_DATA|SG_READ|SG_WRITE, UTZERO + BIGPGSZ, 1);
+	s->flushme++;
+	p->seg[sno++] = s;
+	pg = newpage(1, 0, UTZERO + BIGPGSZ, BIGPGSZ, -1);
+	memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl));
+	segpage(s, pg);
+	k = kmap(s->map[0]->pages[0]);
+	/* This depends on init having a text segment < 2M. */
+	memmove(UINT2PTR(VA(k) + init_data_start - (UTZERO + BIGPGSZ)), init_data_out, sizeof(init_data_out));
+
+	kunmap(k);
+	ready(p);
+}
+
+void
+confinit(void)
+{
+	int i;
+
+	conf.npage = 0;
+	for(i=0; i<nelem(conf.mem); i++)
+		conf.npage += conf.mem[i].npage;
+	conf.nproc = 1000;
+	conf.nimage = 200;
+}
+
+/* check checks simple atomics and anything else that is critical to correct operation.
+ * You can make the prints optional on errors cases, not have it print all tests,
+ * but you should never remove check or the call to it. It found some nasty problems. */
+static void
+check(void)
+{
+	uint64_t f2ns;
+	// cas test
+	uint32_t t = 0;
+	int fail = 0;
+	int _42 = 42;
+	int a = _42, b;
+	int x = cas32(&t, 0, 1);
+
+	print("cas32 done x %d (want 1) t %d (want 1)\n", x, t);
+	if ((t != 1) || (x != 1))
+		fail++;
+
+	x = cas32(&t, 0, 1);
+	print("cas32 done x %d (want 0) t %d (want 1)\n", x, t);
+	if ((t != 1) || (x != 0))
+		fail++;
+
+	print("t is now %d before final cas32\n", t);
+	x = cas32(&t, 1, 2);
+	print("cas32 done x %d (want 1) t %d (want 2)\n", x, t);
+	if ((t != 2) || (x != 1))
+		fail++;
+
+	t = 0;
+	x = tas32(&t);
+	print("tas done x %d (want 0) t %d (want 1)\n", x, t);
+	if ((t != 1) || (x != 0))
+		fail++;
+
+	x = tas32(&t);
+	print("tas done x %d (want 1) t %d (want 1)\n", x, t);
+	if ((t != 1) || (x != 1))
+		fail++;
+
+	t = 0;
+	x = tas32(&t);
+	print("tas done x %d (want ) t %d (want 1)\n", x, t);
+	if ((t != 1) || (x != 0))
+		fail++;
+
+	b = ainc(&a);
+	print("after ainc a is %d (want 43) b is %d (want 43)\n", a, b);
+	if ((b != _42 + 1) || (a != _42 + 1))
+		fail++;
+
+	b = ainc(&a);
+	print("after ainc a is %d (want 44) b is %d (want 44)\n", a, b);
+	if ((b != _42 + 2) || (a != _42 + 2))
+		fail++;
+
+	b = adec(&a);
+	print("after ainc a is %d (want 43) b is %d (want 43)\n", a, b);
+	if ((b != _42 + 1) || (a != _42 + 1))
+		fail++;
+
+	if (fail) {
+		print("%d failures in check();\n", fail);
+		panic("FIX ME");
+	}
+
+	f2ns = fastticks2ns(10);
+	if ((f2ns < 1) || (f2ns > 10)) {
+		print("fastticks2ns(1) is nuts: %d\n", f2ns);
+		panic("Should be in the range 1 to 10, realistically");
+	}
+
+	f2ns = ns2fastticks(1);
+	if ((f2ns < 2) || (f2ns > 100)) {
+		print("ns2fastticks(1) is nuts: %d\n", f2ns);
+		panic("Should be in the range 2 to 100, realistically");
+	}
+
+}
+
+void bsp(void *stack, uintptr_t _configstring)
+{
+	kseg2 = findKSeg2();
+	msg("HIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII\n");
+	configstring = KADDR(_configstring);
+	msg(configstring);
 	Mach *mach = machp();
 	if (mach != &m0)
 		die("MACH NOT MATCH");
 	msg("memset mach\n");
 	memset(mach, 0, sizeof(Mach));
 	msg("done that\n");
+	MACHP(0) = mach;
 
+	msg(configstring);
 	mach->self = (uintptr_t)mach;
 	msg("SET SELF OK\n");
 	mach->machno = 0;
 	mach->online = 1;
 	mach->NIX.nixtype = NIXTC;
-	mach->stack = PTR2UINT(m0stack);
+	mach->stack = PTR2UINT(stack);
 	*(uintptr_t*)mach->stack = STACKGUARD;
+	msg(configstring);
 	mach->externup = nil;
 	active.nonline = 1;
 	active.exiting = 0;
 	active.nbooting = 0;
 
+	consuartputs = puts;
+	msg("call asminit\n");
+	msg("==============================================\n");
+	asminit();
+	msg(",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,\n");
+	asmmapinit(0x81000000, 0x3f000000, 1);
+
+	msg(configstring);
 	/*
 	 * Need something for initial delays
 	 * until a timebase is worked out.
@@ -98,50 +442,118 @@ void bsp(void)
 	mach->cpumhz = 2000;
 	sys->cyclefreq = mach->cpuhz;
 
-	// this is in 386, so ... not yet. i8250console("0");
-	// probably pull in the one from coreboot for riscv.
+	sys->nmach = 1;
 
-	consuartputs = puts;
-	msg("call asminit\n");
-	asminit();
-	msg("call fmtinit\n");
+	msg(configstring);
 	fmtinit();
-	msg("done fmtinit\n");
-	static uint64_t i = 0, j;
-	j = tas32(&i);
-	if (j) msg ("tas is weird, i was set\n"); else msg("i was not set in first tas\n");
-	j = tas32(&i);
-	if (j) msg ("tas is ok, i was set\n"); else die("i was not set in second tas\n");
-
-	i = 5;
-	cas32(&i, 5, 6);
-	if (i != 6) die("i is not 6 after cas\n"); else msg ("i is 6 after cas\n");
-
-	static Lock l; // to ensure initialization.
-	if (canlock(&l)) msg ("L can be locked\n"); else die("Can't lock L\n");
-	ilock(&l);
-	if (canlock(&l)) msg ("L can be NOT be locked OK\n"); else die("Can lock L after lock\n");
-	iunlock(&l);
-	if (canlock(&l)) msg ("L can be locked after unlock\n"); else die("Can't lock L afterunlock\n");
-	
-	
 	print("\nHarvey\n");
-
+print("KADDR OF (uintptr_t) 0x40001000 is %p\n", KADDR((uintptr_t) 0x40001000));
+
+	/* you're going to love this. Print does not print the whole
+	 * string. msg does. Bug. */
+	print("Config string:%p '%s'\n", configstring, configstring);
+	msg("Config string via msg\n");
+	msg(configstring);
+	msg("\n");
+	mach->perf.period = 1;
+	if((hz = archhz()) != 0ll){
+		mach->cpuhz = hz;
+		mach->cyclefreq = hz;
+		sys->cyclefreq = hz;
+		mach->cpumhz = hz/1000000ll;
+	}
+
+	print("print a number like 5 %d\n", 5);
+	/*
+	 * Mmuinit before meminit because it
+	 * flushes the TLB via machp()->pml4->pa.
+	 */
+	mmuinit();
+
+	ioinit(); print("ioinit\n");
+print("IOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIO\n");
+	meminit();print("meminit\n");
+print("CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n");
+	confinit();print("confinit\n");
+print("CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n");
+	archinit();print("archinit\n");
+print("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n");
+	mallocinit();print("mallocinit\n");
+
+	/* test malloc. It's easier to find out it's broken here,
+	 * not deep in some call chain.
+	 * See next note.
+	 *
+	 */
+	if (1) {
+		void *v = malloc(1234);
+		msg("allocated\n ");
+		free(v);
+		msg("free ok\n");
+	}
+
+	query_rtc(configstring, &rtc);
+	print("rtc: %p\n", rtc);
+
+	query_uint(configstring, "rtc{addr", (uintptr_t*)&mtimepa);
+	mtime = KADDR(mtimepa);
+	query_uint(configstring, "core{0{0{timecmp", (uintptr_t*)&mtimecmppa);
+	mtimecmp = KADDR(mtimecmppa);
+
+	print("mtime is %p and mtimecmp is %p\n", mtime, mtimecmp);
+	umeminit();
+
+	procinit0();
+	print("before mpacpi, maxcores %d\n", maxcores);
+	trapinit();
+	print("trapinit done\n");
+	/* Forcing to single core if desired */
+	if(!nosmp) {
+		// smp startup
+	}
+//working.
+	// not needed. teardownidmap(mach);
+	timersinit();print("	timersinit();\n");
+	// ? fpuinit();
+	psinit(conf.nproc);print("	psinit(conf.nproc);\n");
+	initimage();print("	initimage();\n");
+	links();
+
+	devtabreset();print("	devtabreset();\n");
+	pageinit();print("	pageinit();\n");
+	swapinit();print("	swapinit();\n");
+	userinit();print("	userinit();\n");
+	/* Forcing to single core if desired */
+	if(!nosmp) {
+		//nixsquids();
+		//testiccs();
+	}
+
+	print("NO profiling until you set upa alloc_cpu_buffers()\n");
+	//alloc_cpu_buffers();
+
+	print("CPU Freq. %dMHz\n", mach->cpumhz);
+	// set the trap vector
+	void *supervisor_trap_entry(void);
+	write_csr(/*stvec*/0x105, supervisor_trap_entry);
+	// enable all interrupt sources.
+	uint64_t ints = read_csr(sie);
+	ints |= 0x666;
+	write_csr(sie, ints);
+
+	dumpmmuwalk(0xfffffffffffff000ULL);
+
+	check();
+
+	void consread(void);
+	addclock0link(consread, 0);
+
+	print("schedinit...\n");
+
+	schedinit();
 	die("Completed hart for bsp OK!\n");
 }
 
-void
-main(uint32_t mbmagic, uint32_t mbaddress)
-{
-
-	testPrint('0');
-	if (x != 0x123456)
-		die("Data is not set up correctly\n");
-	//memset(edata, 0, end - edata);
-	msg("got somewhere");
-	startmach(bsp, &m0);
-}
-
 /* stubs until we implement in assembly */
 int corecolor(int _)
 {
@@ -150,6 +562,8 @@ int corecolor(int _)
 
 Proc *externup(void)
 {
+	if (! machp())
+		return nil;
 	return machp()->externup;
 }
 
@@ -157,12 +571,6 @@ void errstr(char *s, int i) {
 	panic("errstr");
 }
 
-void
-oprof_alarm_handler(Ureg *u)
-{
-	panic((char *)__func__);
-}
-
 void
 hardhalt(void)
 {
@@ -178,18 +586,15 @@ ureg2gdb(Ureg *u, uintptr_t *g)
 int
 userureg(Ureg*u)
 {
-	panic((char *)__func__);
-	return -1;
-}
-
-uintptr_t
-userpc(Ureg*u)
-{
-	panic((char *)__func__);
-	return 0;
+	int64_t ip = (int64_t)u->ip;
+	if (ip < 0) {
+		//print("RETURNING 0 for userureg\n");
+		return 0;
+	}
+	//print("Returning 1 for userureg; need a better test\n");
+	return 1;
 }
 
-
 void    exit(int _)
 {
 	panic((char *)__func__);
@@ -202,17 +607,23 @@ void fpunoted(void)
 
 void fpunotify(Ureg*_)
 {
-	panic((char *)__func__);
+	print("fpunotify: doing nothing since FPU is disabled\n");
 }
 
 void fpusysrfork(Ureg*_)
 {
-	panic((char *)__func__);
+	print((char *)__func__);
+	print("IGNORING\n");
 }
 
-void kexit(Ureg*_)
+void sysrforkret(void)
 {
-	panic((char *)__func__);
+	void *stack(void);
+	void *sp = stack();
+	if(0) print("sysrforkret: stack is %p\n", sp);
+	if(0) dumpgpr((Ureg *)sp);
+void _sysrforkret();
+	_sysrforkret();
 }
 
 void
@@ -223,17 +634,14 @@ reboot(void*_, void*__, int32_t ___)
 
 void fpusysprocsetup(Proc *_)
 {
-	panic((char *)__func__);
-}
-
-void sysrforkret(void)
-{
-	panic((char *)__func__);
+	print((char *)__func__);
+	print("THIS IS GONNA SCREW YOU IF YOU DO NOT FIX IT\n");
 }
 
 void     fpusysrforkchild(Proc*_, Proc*__)
 {
-	panic((char *)__func__);
+	print((char *)__func__);
+	print("THIS IS GONNA SCREW YOU IF YOU DO NOT FIX IT\n");
 }
 
 int
@@ -243,50 +651,19 @@ fpudevprocio(Proc*p, void*v, int32_t _, uintptr_t __, int ___)
 	return -1;
 }
 
-void
-setregisters(Ureg*u, char*f, char*t, int amt)
-{
-	panic((char *)__func__);
-}
-
 void cycles(uint64_t *p)
 {
-	return;
 	*p = rdtsc();
 }
 
 int islo(void)
 {
-	panic((char *)__func__);
-	return 0;
-}
-
-uintptr_t
-dbgpc(Proc*p)
-{
-	panic((char *)__func__);
-	return 0;
-}
-
-
-void dumpstack(void)
-{
-	panic((char *)__func__);
+//	msg("isloc\n");
+	uint64_t ms = read_csr(sstatus);
+//	msg("read it\n");
+	return ms & MSTATUS_SIE;
 }
 
-void
-dumpgpr(Ureg* ureg)
-{
-	panic((char *)__func__);
-}
-
-void
-setkernur(Ureg*u, Proc*p)
-{
-	panic((char *)__func__);
-}
-
-
 void
 stacksnippet(void)
 {
@@ -326,7 +703,7 @@ HERE(void)
 /* Keep to debug trap.c */
 void wave(int c)
 {
-	testPrint(c);
+	putchar(c);
 }
 
 void hi(char *s)

+ 13 - 12
sys/src/9/riscv/map.c

@@ -16,23 +16,22 @@
 #define _KADDR(pa)	UINT2PTR(kseg0+((uintptr)(pa)))
 #define _PADDR(va)	PTR2UINT(((uintptr)(va)) - kseg0)
 
-#define TMFM		(64*MiB)
+/* physical is from 2 -> 4 GiB */
+#define TMFM		(4ULL*GiB)
 
+/* the wacko hole in RISCV address space makes KADDR a bit more complex. */
 int km, ku, k2;
 void*
 KADDR(uintptr_t pa)
 {
-	uint8_t* va;
-
-	va = UINT2PTR(pa);
-	if(pa < TMFM) {
+	if((pa > 2 * GiB) && (pa < TMFM)) {
 		km++;
-		return KSEG0+va;
+		return (void *)(KSEG0|pa);
 	}
 
-	assert(pa < KSEG2);
+	assert(pa < (uintptr_t)kseg2);
 	k2++;
-	return KSEG2+va;
+	return (void *)((uintptr_t)kseg2|pa);
 }
 
 uintmem
@@ -41,10 +40,12 @@ PADDR(void* va)
 	uintmem pa;
 
 	pa = PTR2UINT(va);
-	if(pa >= KSEG0 && pa < KSEG0+TMFM)
-		return pa-KSEG0;
-	if(pa > KSEG2)
-		return pa-KSEG2;
+	if(pa >= KSEG0) {
+		return (uintmem)(uint32_t)pa; //-KSEG0;
+	}
+	if(pa > (uintptr_t)kseg2) {
+		return pa-(uintptr_t)kseg2;
+	}
 
 	panic("PADDR: va %#p pa #%p @ %#p\n", va, _PADDR(va), getcallerpc());
 	return 0;

+ 4 - 13
sys/src/9/riscv/mem.h

@@ -94,11 +94,13 @@
 #define UTZERO		(0+2*MiB)		/* first address in user text */
 #define UTROUND(t)	ROUNDUP((t), BIGPGSZ)
 #ifndef __ASSEMBLER__
-#define USTKTOP		(0x00007ffffffff000ull & ~(BIGPGSZ-1))
+#define USTKTOP		(0x3fffffffffULL & ~(BIGPGSZ-1))
 #else
-#define USTKTOP         (0x00007ffffffff000 & ~(BIGPGSZ-1))
+#define USTKTOP         (0x3fffffffffULL & ~(BIGPGSZ-1))
 #endif
+/* U means "user" */
 #define USTKSIZE	(16*1024*1024)		/* size of user stack */
+/* T means "temporary" */
 #define TSTKTOP		(USTKTOP-USTKSIZE)	/* end of new stack in sysexec */
 #define	NIXCALL		(TSTKTOP-USTKSIZE)	/* nix syscall queues (2MiB) */
 #ifndef __ASSEMBLER__
@@ -116,25 +118,14 @@
  */
 
 #ifndef __ASSEMBLER__
-#define KSEG2		(0xfffffe0000000000ull)	/* 1TB - KMAP */
-/*			 0xffffff0000000000ull	end of KSEG2 */
-#define VMAP		(0xffffffffe0000000ull)
-#define VMAPSZ		(256*MiB)
 #define KSEG0		(0xffffffff80000000ull)	/* 256MB - this is confused */
 #define KZERO		(0xffffffff80000000ull)
 #define KTZERO		(KZERO+1*MiB+64*KiB)
-#define PDMAP		(0xffffffffff800000ull)
-#define PMAPADDR		(0xffffffffffe00000ull)	/* unused as of yet */
 /*			 0xffffffffffffffffull	end of KSEG0 */
 #else
-#define KSEG2           (0xfffffe0000000000)
-#define VMAPSZ          (256*MiB)
-#define VMAP            (0xffffffffe0000000)
 #define KSEG0           (0xffffffff80000000)
 #define KZERO           (0xffffffff80000000)
 #define KTZERO          (KZERO+1*MiB+64*KiB)
-#define PDMAP           (0xffffffffff800000)
-#define PMAPADDR        (0xffffffffffe00000)
 #endif
 
 /*

+ 31 - 0
sys/src/9/riscv/memory.c

@@ -0,0 +1,31 @@
+/*
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+void
+meminit(void)
+{
+	extern void asmmeminit(void);
+
+print("meminit\n");
+	asmmeminit();
+}
+
+void
+umeminit(void)
+{
+	extern void asmumeminit(void);
+
+	asmumeminit();
+}

+ 224 - 335
sys/src/9/riscv/mmu.c

@@ -16,18 +16,33 @@
 #include "encoding.h"
 #include "mmu.h"
 
+#undef DBGFLG
+#define DBGFLG 0
+
+/* this gets pretty messy. RV64 has *at least* two modes:
+ * 4 level and 3 level page tables. And people wonder why
+ * I like soft TLB so much. Anyway, for now, not sure
+ * how to handle it.
+ * Would be cool to work out a way to Do The Right Thing
+ * without regard to page size, so that's what I'm going to
+ * try to do.
+ */
+void msg(char *);
 /*
  * To do:
  *	PteNX;
  *	mmukmapsync grot for >1 processor;
- *	replace vmap with newer version (no PDMAP);
  *	mmuptcopy (PteSHARED trick?);
  *	calculate and map up to TMFM (conf crap);
  */
 
-#define TMFM		(64*MiB)		/* kernel memory */
+/* strike off 2M so it won't wrap to 0. Sleazy. */
+#define TMFM		(2*GiB-2*MiB)		/* kernel memory */
 
 #define PPN(x)		((x)&~(PGSZ-1))
+#define PTE2PPN(p) ((p)>>10)
+#define PTE2PA(p) (((p)>>10)<<12)
+
 
 #if 0
 /* Print the page table structures to the console */
@@ -71,15 +86,15 @@ rootput(uintptr_t root)
 
 }
 void
-mmuflushtlb(uint64_t u)
+mmuflushtlb(void)
 {
 
 	machp()->tlbpurge++;
-	if(machp()->MMU.pml4->daddr){
-		memset(UINT2PTR(machp()->MMU.pml4->va), 0, machp()->MMU.pml4->daddr*sizeof(PTE));
-		machp()->MMU.pml4->daddr = 0;
+	if(machp()->MMU.root->daddr){
+		memset(UINT2PTR(machp()->MMU.root->va), 0, machp()->MMU.root->daddr*sizeof(PTE));
+		machp()->MMU.root->daddr = 0;
 	}
-	rootput((uintptr_t) machp()->MMU.pml4->pa);
+	rootput((uintptr_t) machp()->MMU.root->pa);
 }
 
 void
@@ -118,7 +133,7 @@ mmuptpfree(Proc* proc, int clear)
 		proc->MMU.mmuptp[l] = nil;
 	}
 
-	machp()->MMU.pml4->daddr = 0;
+	machp()->MMU.root->daddr = 0;
 }
 
 static void
@@ -168,25 +183,32 @@ dumpmmu(Proc *p)
 				" daddr %#lx next %#p prev %#p\n",
 				pg, pg->va, pg->pa, pg->daddr, pg->next, pg->prev);
 	}
-	print("pml4 %#llx\n", machp()->MMU.pml4->pa);
-	if(0)dumpptepg(4, machp()->MMU.pml4->pa);
+	print("root %#llx\n", machp()->MMU.root->pa);
+	if(0)dumpptepg(4, machp()->MMU.root->pa);
 }
 
 void
 dumpmmuwalk(uint64_t addr)
 {
 	int l;
-	PTE *pte, *pml4;
-
-	pml4 = UINT2PTR(machp()->MMU.pml4->va);
-	if((l = mmuwalk(pml4, addr, 3, &pte, nil)) >= 0)
-		print("cpu%d: mmu l%d pte %#p = %llx\n", machp()->machno, l, pte, *pte);
-	if((l = mmuwalk(pml4, addr, 2, &pte, nil)) >= 0)
-		print("cpu%d: mmu l%d pte %#p = %llx\n", machp()->machno, l, pte, *pte);
-	if((l = mmuwalk(pml4, addr, 1, &pte, nil)) >= 0)
-		print("cpu%d: mmu l%d pte %#p = %llx\n", machp()->machno, l, pte, *pte);
-	if((l = mmuwalk(pml4, addr, 0, &pte, nil)) >= 0)
-		print("cpu%d: mmu l%d pte %#p = %llx\n", machp()->machno, l, pte, *pte);
+	PTE *pte, *root;
+
+	root = UINT2PTR(machp()->MMU.root->va);
+	print("root is %p\n", root);
+	if((l = mmuwalk(root, addr, 2, &pte, nil)) >= 0) {
+		print("\tcpu%d: mmu l%d pte %#p = ", machp()->machno, l, pte);
+		print("%llx, PA is %llx\n", *pte, PTE2PA(*pte));
+	}
+	if((l = mmuwalk(root, addr, 1, &pte, nil)) >= 0) {
+		print("\tcpu%d: mmu l%d pte %#p = ", machp()->machno, l, pte);
+		print("%llx, PA is %llx\n", *pte, PTE2PA(*pte));
+	}
+	if((l = mmuwalk(root, addr, 0, &pte, nil)) >= 0) {
+		print("\tcpu%d: mmu l%d pte %#p = ", machp()->machno, l, pte);
+		print("%llx, PA is %llx\n", *pte, PTE2PA(*pte));
+	}
+	if (PTE2PA(*pte) != 0)
+		hexdump(KADDR(PTE2PA(*pte)), 32);
 }
 
 static Page mmuptpfreelist;
@@ -259,21 +281,29 @@ mmuswitch(Proc* proc)
 		proc->newtlb = 0;
 	}
 
-	if(machp()->MMU.pml4->daddr){
-		memset(UINT2PTR(machp()->MMU.pml4->va), 0, machp()->MMU.pml4->daddr*sizeof(PTE));
-		machp()->MMU.pml4->daddr = 0;
+	/* daddr is the number of user PTEs in use in the root. */
+	if(machp()->MMU.root->daddr){
+		print("memsg(%p, 0, %d\n", UINT2PTR(machp()->MMU.root->va), 0, machp()->MMU.root->daddr*sizeof(PTE));
+		memset(UINT2PTR(machp()->MMU.root->va), 0, machp()->MMU.root->daddr*sizeof(PTE));
+		machp()->MMU.root->daddr = 0;
 	}
 
-	pte = UINT2PTR(machp()->MMU.pml4->va);
+	pte = UINT2PTR(machp()->MMU.root->va);
+
+	if (0)print("pte %p\n", pte);
+	/* N.B. On RISCV, we DO NOT SET any of X, R, W  bits at this level since
+	 * that we point to page table pages on level down.  Also, these are
+	 * explicitly user level pages, so PteU is set. */
 	for(page = proc->MMU.mmuptp[3]; page != nil; page = page->next){
-		pte[page->daddr] = PPN(page->pa)|PteU|PteRW|PteP;
-		if(page->daddr >= machp()->MMU.pml4->daddr)
-			machp()->MMU.pml4->daddr = page->daddr+1;
-		page->prev = machp()->MMU.pml4;
+		pte[page->daddr] = PPN(page->pa)|PteU|PteP;
+		if(page->daddr >= machp()->MMU.root->daddr)
+			machp()->MMU.root->daddr = page->daddr+1;
+		page->prev = machp()->MMU.root;
 	}
 
-	//tssrsp0(machp(), STACKALIGN(PTR2UINT(proc->kstack+KSTACK)));
-	rootput((uintptr_t) machp()->MMU.pml4->pa);
+	if (0)print("rootput %p\n", (void *)(uintptr_t) machp()->MMU.root->pa);
+	rootput((uintptr_t) machp()->MMU.root->pa);
+	if (0)print("splx\n");
 	splx(pl);
 }
 
@@ -299,37 +329,30 @@ mmurelease(Proc* proc)
 		wakeup(&pga.rend);
 	proc->MMU.mmuptp[0] = nil;
 
-	panic("tssrsp0");
-	//tssrsp0(machp(), STACKALIGN(machp()->stack+MACHSTKSZ));
-	rootput(machp()->MMU.pml4->pa);
+	rootput(machp()->MMU.root->pa);
 }
 
 static void
 checkpte(uintmem ppn, void *a)
 {
 	int l;
-	PTE *pte, *pml4;
+	PTE *pte, *root;
 	uint64_t addr;
 	char buf[240], *s;
 
 	addr = PTR2UINT(a);
-	pml4 = UINT2PTR(machp()->MMU.pml4->va);
+	root = UINT2PTR(machp()->MMU.root->va);
 	pte = 0;
 	s = buf;
 	*s = 0;
-	if((l = mmuwalk(pml4, addr, 3, &pte, nil)) < 0 || (*pte&PteP) == 0)
-		goto Panic;
-	s = seprint(buf, buf+sizeof buf,
-		"check3: l%d pte %#p = %llx\n",
-		l, pte, pte?*pte:~0);
-	if((l = mmuwalk(pml4, addr, 2, &pte, nil)) < 0 || (*pte&PteP) == 0)
+	if((l = mmuwalk(root, addr, 2, &pte, nil)) < 0 || (*pte&PteP) == 0)
 		goto Panic;
 	s = seprint(s, buf+sizeof buf,
 		"check2: l%d  pte %#p = %llx\n",
 		l, pte, pte?*pte:~0);
 	if(*pte&PteFinal)
 		return;
-	if((l = mmuwalk(pml4, addr, 1, &pte, nil)) < 0 || (*pte&PteP) == 0)
+	if((l = mmuwalk(root, addr, 1, &pte, nil)) < 0 || (*pte&PteP) == 0)
 		goto Panic;
 	seprint(s, buf+sizeof buf,
 		"check1: l%d  pte %#p = %llx\n",
@@ -357,7 +380,7 @@ mmuptpcheck(Proc *proc)
 
 	if(proc == nil)
 		return;
-	lp = machp()->MMU.pml4;
+	lp = machp()->MMU.root;
 	for(lvl = 3; lvl >= 2; lvl--){
 		npgs = 0;
 		for(p = proc->MMU.mmuptp[lvl]; p != nil; p = p->next){
@@ -422,7 +445,8 @@ pteflags(uint attr)
 void
 invlpg(uintptr_t _)
 {
-	panic("invlpage");
+	// TOODO
+	if (0) print("invlpage is not implemented, continuing anyway (addr is %p)\n", _);
 }
 
 /*
@@ -439,20 +463,31 @@ mmuput(uintptr_t va, Page *pg, uint attr)
 	PTE *pte;
 	Page *page, *prev;
 	Mpl pl;
-	uintmem pa, ppn;
+	uintmem pa, ppage;
 	char buf[80];
 
-	ppn = 0;
+	if (DBGFLG) {
+		print("mmuput: %p\n", va);
+		dumpmmuwalk(va);
+		print("now try the put");
+	}
+	ppage = 0;
 	pa = pg->pa;
 	if(pa == 0)
 		panic("mmuput: zero pa");
+	if(va == 0)
+		panic("mmuput: zero va");
 
 	if(DBGFLG){
 		snprint(buf, sizeof buf, "cpu%d: up %#p mmuput %#p %#P %#x\n",
 			machp()->machno, up, va, pa, attr);
 		print("%s", buf);
 	}
-	assert(pg->pgszi >= 0);
+	if (pg->pgszi < 0) {
+		print("mmuput(%p, %p, 0x%x): bad pgszi %d for pa %p\n",
+			va, pg, attr, pg->pgszi, pa);
+		assert(pg->pgszi >= 0);
+	}
 	pgsz = sys->pgsz[pg->pgszi];
 	if(pa & (pgsz-1))
 		panic("mmuput: pa offset non zero: %#llx\n", pa);
@@ -462,13 +497,15 @@ mmuput(uintptr_t va, Page *pg, uint attr)
 	if(DBGFLG)
 		mmuptpcheck(up);
 	user = (va < KZERO);
-	x = PTLX(va, 3);
+	x = PTLX(va, 2);
+	if (0) print("user is %d, index for %p is 0x%x, ", user, va, x);
 
-	pte = UINT2PTR(machp()->MMU.pml4->va);
+	pte = UINT2PTR(machp()->MMU.root->va);
 	pte += x;
-	prev = machp()->MMU.pml4;
+	prev = machp()->MMU.root;
 
-	for(lvl = 3; lvl >= 0; lvl--){
+	if (DBGFLG) print("starting PTE at l2 is %p\n", pte);
+	for(lvl = 2; lvl >= 0; lvl--){
 		if(user){
 			if(pgsz == 2*MiB && lvl == 1)	 /* use 2M */
 				break;
@@ -479,49 +516,59 @@ mmuput(uintptr_t va, Page *pg, uint attr)
 			if(page->prev == prev && page->daddr == x){
 				if(*pte == 0){
 					print("mmu: jmk and nemo had fun\n");
-					*pte = PPN(page->pa)|PteU|PteRW|PteP;
+					*pte = (PPN(page->pa)>>2)|PteP;
+					if (DBGFLG) print("level %d: set pte %p to 0x%llx for pa %p\n", lvl, pte, *pte, pa);
 				}
 				break;
 			}
 
 		if(page == nil){
-			if(up->MMU.mmuptp[0] == nil)
+			if(up->MMU.mmuptp[0] == nil) {
 				page = mmuptpalloc();
-			else {
+				if (DBGFLG) print("\tallocated page %p\n", page);
+			} else {
 				page = up->MMU.mmuptp[0];
 				up->MMU.mmuptp[0] = page->next;
+				if (DBGFLG) print("\tReused page %p\n", page);
 			}
 			page->daddr = x;
 			page->next = up->MMU.mmuptp[lvl];
 			up->MMU.mmuptp[lvl] = page;
 			page->prev = prev;
-			*pte = PPN(page->pa)|PteU|PteRW|PteP;
-			if(lvl == 3 && x >= machp()->MMU.pml4->daddr)
-				machp()->MMU.pml4->daddr = x+1;
+			*pte = (PPN(page->pa)>>2)|PteP;
+			if (DBGFLG) print("\tlevel %d: set pte %p to 0x%llx for pa %p\n", lvl, pte, *pte, PPN(page->pa));
+			if(lvl == 2 && x >= machp()->MMU.root->daddr)
+				machp()->MMU.root->daddr = x+1;
 		}
 		x = PTLX(va, lvl-1);
+		if (DBGFLG) print("\tptlx(%p,%d) is %p\n", va, lvl-1,x);
 
-		ppn = PPN(*pte);
-		if(ppn == 0)
+		ppage = PTE2PA(*pte);
+		if (DBGFLG) print("\tpa for pte %p val 0x%llx ppage %p\n", pte, *pte, ppage);
+		if(ppage == 0)
 			panic("mmuput: ppn=0 l%d pte %#p = %#P\n", lvl, pte, *pte);
 
-		pte = UINT2PTR(KADDR(ppn));
+		pte = UINT2PTR(KADDR(ppage));
 		pte += x;
+		if (DBGFLG) print("\tpte for next iteration is %p\n", pte);
 		prev = page;
 	}
 
+	if (DBGFLG) print("\tAFTER LOOP pte %p val 0x%llx ppn %p\n", pte, *pte, pa);
 	if(DBGFLG)
-		checkpte(ppn, pte);
-	*pte = pa|PteU;
+		checkpte(ppage, pte);
+	*pte = (pa>>2)|PteU;
+	if (DBGFLG) print("\tAFTER SET pte %p val 0x%llx ppn %p\n", pte, *pte, pa);
 
 	if(user)
 		switch(pgsz){
 		case 2*MiB:
 		case 1*GiB:
-			*pte |= attr & PteFinal | PteP;
+			*pte |= attr | PteFinal | PteP | 0x1f;
+			if (DBGFLG) print("\tUSER PAGE pte %p val 0x%llx\n", pte, *pte);
 			break;
 		default:
-			panic("mmuput: user pages must be 2M or 1G");
+			panic("\tmmuput: user pages must be 2M or 1G");
 		}
 	splx(pl);
 
@@ -532,12 +579,14 @@ mmuput(uintptr_t va, Page *pg, uint attr)
 	}
 
 	invlpg(va);			/* only if old entry valid? */
+	//dumpmmuwalk(va);
+	//hexdump((void *)va, 16);
+	if (DBGFLG) print("returning from mmuput\n");
 }
 
 #if 0
 static Lock mmukmaplock;
 #endif
-static Lock vmaplock;
 
 #define PML4X(v)	PTLX((v), 3)
 #define PDPX(v)		PTLX((v), 2)
@@ -552,227 +601,26 @@ mmukmapsync(uint64_t va)
 	return 0;
 }
 
-#if 0
-static PTE
-pdeget(uintptr_t va)
-{
-	PTE *pdp;
-
-	if(va < 0xffffffffc0000000ull)
-		panic("pdeget(%#p)", va);
-
-	pdp = (PTE*)(PDMAP+PDX(PDMAP)*4096);
-
-	return pdp[PDX(va)];
-}
-
-#endif
-/*
- * Add kernel mappings for pa -> va for a section of size bytes.
- * Called only after the va range is known to be unoccupied.
- */
-static int
-pdmap(uintptr_t pa, int attr, uintptr_t va, usize size)
-{
-	uintptr_t pae;
-	PTE *pd, *pde, *pt, *pte;
-	int pdx, pgsz;
-	Page *pg;
-
-	pd = (PTE*)(PDMAP+PDX(PDMAP)*4096);
-
-	for(pae = pa + size; pa < pae; pa += pgsz){
-		pdx = PDX(va);
-		pde = &pd[pdx];
-
-		/*
-		 * Check if it can be mapped using a big page,
-		 * i.e. is big enough and starts on a suitable boundary.
-		 * Assume processor can do it.
-		 */
-		if(ALIGNED(pa, PGLSZ(1)) && ALIGNED(va, PGLSZ(1)) && (pae-pa) >= PGLSZ(1)){
-			assert(*pde == 0);
-			/* attr had better include one of Pte{W,R,X}*/
-			*pde = pa|attr|PteP;
-			pgsz = PGLSZ(1);
-		}
-		else{
-			if(*pde == 0){
-				pg = mmuptpalloc();
-				assert(pg != nil && pg->pa != 0);
-				*pde = pg->pa|PteRW|PteP;
-				memset((PTE*)(PDMAP+pdx*4096), 0, 4096);
-			}
-			assert(*pde != 0);
-
-			pt = (PTE*)(PDMAP+pdx*4096);
-			pte = &pt[PTX(va)];
-			assert(!(*pte & PteP));
-			*pte = pa|attr|PteP;
-			pgsz = PGLSZ(0);
-		}
-		va += pgsz;
-	}
-
-	return 0;
-}
-
-static int
-findhole(PTE* a, int n, int count)
+// findKSeg2 finds kseg2, i.e., the lowest virtual
+// address mapped by firmware. We need to know this so we can
+// correctly and easily compute KADDR and PADDR.
+// TODO: actually to it.
+// It is *possible* that we'll be able to pick this up from
+// the configstring.
+void *
+findKSeg2(void)
 {
-	int have, i;
-
-	have = 0;
-	for(i = 0; i < n; i++){
-		if(a[i] == 0)
-			have++;
-		else
-			have = 0;
-		if(have >= count)
-			return i+1 - have;
-	}
-
-	return -1;
+	// return the Sv39 address that we know coreboot
+	// set up.
+	return (void *)(~0ULL<<38);
 }
-
-/*
- * Look for free space in the vmap.
- */
-static uintptr_t
-vmapalloc(usize size)
-{
-	int i, n, o;
-	PTE *pd, *pt;
-	int pdsz, ptsz;
-
-	pd = (PTE*)(PDMAP+PDX(PDMAP)*4096);
-	pd += PDX(VMAP);
-	pdsz = VMAPSZ/PGLSZ(1);
-
-	/*
-	 * Look directly in the PD entries if the size is
-	 * larger than the range mapped by a single entry.
-	 */
-	if(size >= PGLSZ(1)){
-		n = HOWMANY(size, PGLSZ(1));
-		if((o = findhole(pd, pdsz, n)) != -1)
-			return VMAP + o*PGLSZ(1);
-		return 0;
-	}
-
-	/*
-	 * Size is smaller than that mapped by a single PD entry.
-	 * Look for an already mapped PT page that has room.
-	 */
-	n = HOWMANY(size, PGLSZ(0));
-	ptsz = PGLSZ(0)/sizeof(PTE);
-	for(i = 0; i < pdsz; i++){
-		if(!(pd[i] & PteP) || (pd[i] & PteFinal))
-			continue;
-
-		pt = (PTE*)(PDMAP+(PDX(VMAP)+i)*4096);
-		if((o = findhole(pt, ptsz, n)) != -1)
-			return VMAP + i*PGLSZ(1) + o*PGLSZ(0);
-	}
-
-	/*
-	 * Nothing suitable, start using a new PD entry.
-	 */
-	if((o = findhole(pd, pdsz, 1)) != -1)
-		return VMAP + o*PGLSZ(1);
-
-	return 0;
-}
-
-/*
- * KSEG0 maps low memory.
- * KSEG2 maps almost all memory, but starting at an address determined
- * by the address space map (see asm.c).
- * Thus, almost everything in physical memory is already mapped, but
- * there are things that fall in the gap
- * (acpi tables, device memory-mapped registers, etc.)
- * for those things, we also want to disable caching.
- * vmap() is required to access them.
- */
-void*
-vmap(uintptr_t pa, usize size)
-{
-	uintptr_t va;
-	usize o, sz;
-
-	DBG("vmap(%#p, %lu) pc=%#p\n", pa, size, getcallerpc());
-
-	if(machp()->machno != 0)
-		print("vmap: machp()->machno != 0");
-
-	/*
-	 * This is incomplete; the checks are not comprehensive
-	 * enough.
-	 * Sometimes the request is for an already-mapped piece
-	 * of low memory, in which case just return a good value
-	 * and hope that a corresponding vunmap of the address
-	 * will have the same address.
-	 * To do this properly will require keeping track of the
-	 * mappings; perhaps something like kmap, but kmap probably
-	 * can't be used early enough for some of the uses.
-	 */
-	if(pa+size < 1ull*MiB)
-		return KADDR(pa);
-	if(pa < 1ull*MiB)
-		return nil;
-
-	/*
-	 * Might be asking for less than a page.
-	 * This should have a smaller granularity if
-	 * the page size is large.
-	 */
-	o = pa & ((1<<PGSHFT)-1);
-	pa -= o;
-	sz = ROUNDUP(size+o, PGSZ);
-
-	if(pa == 0){
-		print("vmap(0, %lu) pc=%#p\n", size, getcallerpc());
-		return nil;
-	}
-	ilock(&vmaplock);
-	if((va = vmapalloc(sz)) == 0 || pdmap(pa, /*PtePCD|*/PteRW, va, sz) < 0){
-		iunlock(&vmaplock);
-		return nil;
-	}
-	iunlock(&vmaplock);
-
-	DBG("vmap(%#p, %lu) => %#p\n", pa+o, size, va+o);
-
-	return UINT2PTR(va + o);
-}
-
-void
-vunmap(void* v, usize size)
-{
-	uintptr_t va;
-
-	DBG("vunmap(%#p, %lu)\n", v, size);
-
-	if(machp()->machno != 0)
-		print("vmap: machp()->machno != 0");
-
-	/*
-	 * See the comments above in vmap.
-	 */
-	va = PTR2UINT(v);
-	if(va >= KZERO && va+size < KZERO+1ull*MiB)
-		return;
-
-	/*
-	 * Here will have to deal with releasing any
-	 * resources used for the allocation (e.g. page table
-	 * pages).
-	 */
-	DBG("vunmap(%#p, %lu)\n", v, size);
-}
-
+/* mmuwalk will walk the page tables as far as we ask (level)
+ * or as far as possible (you might hit a tera/giga/mega PTE).
+ * If it gets a valid PTE it will return it in ret; test for
+ * validity by testing PetP. To see how far it got, check
+ * the return value. */
 int
-mmuwalk(PTE* pml4, uintptr_t va, int level, PTE** ret,
+mmuwalk(PTE* root, uintptr_t va, int level, PTE** ret,
 	uint64_t (*alloc)(usize))
 {
 	int l;
@@ -780,12 +628,18 @@ mmuwalk(PTE* pml4, uintptr_t va, int level, PTE** ret,
 	PTE *pte;
 
 	Mpl pl;
-
 	pl = splhi();
-	if(DBGFLG > 1)
-		DBG("mmuwalk%d: va %#p level %d\n", machp()->machno, va, level);
-	pte = &pml4[PTLX(va, 3)];
-	for(l = 3; l >= 0; l--){
+	if(DBGFLG > 1) {
+		print("mmuwalk%d: va %#p level %d\n", machp()->machno, va, level);
+		print("PTLX(%p, 2) is 0x%x\n", va, PTLX(va,2));
+		print("root is %p\n", root);
+	}
+	pte = &root[PTLX(va, 2)];
+	if(DBGFLG > 1) {
+		print("pte is %p\n", pte);
+		print("*pte is %p\n", *pte);
+	}
+	for(l = 2; l >= 0; l--){
 		if(l == level)
 			break;
 		if(!(*pte & PteP)){
@@ -799,8 +653,12 @@ mmuwalk(PTE* pml4, uintptr_t va, int level, PTE** ret,
 		}
 		else if(*pte & PteFinal)
 			break;
-		pte = UINT2PTR(KADDR(PPN(*pte)));
+		pte = UINT2PTR(KADDR((*pte&~0x3ff)<<2)); // PPN(*pte)));
+		if (DBGFLG > 1)
+			print("pte is %p: ", pte);
 		pte += PTLX(va, l-1);
+		if (DBGFLG > 1)
+			print("and pte after index is %p\n", pte);
 	}
 	*ret = pte;
 	splx(pl);
@@ -812,40 +670,49 @@ mmuphysaddr(uintptr_t va)
 {
 	int l;
 	PTE *pte;
+	uint64_t ppn;
 	uintmem mask, pa;
 
+msg("mmyphysaddr\n");
 	/*
 	 * Given a VA, find the PA.
 	 * This is probably not the right interface,
 	 * but will do as an experiment. Usual
 	 * question, should va be void* or uintptr?
 	 */
-	l = mmuwalk(UINT2PTR(machp()->MMU.pml4->va), va, 0, &pte, nil);
-	DBG("physaddr: va %#p l %d\n", va, l);
+	print("machp() %p \n", machp());
+	print("mahcp()->MMU.root %p\n", machp()->MMU.root);
+	print("... va  %p\n", machp()->MMU.root->va);
+	l = mmuwalk(UINT2PTR(machp()->MMU.root->va), va, 0, &pte, nil);
+	print("pte is %p *pte is 0x%llx\n", pte, *pte);
+	print("physaddr: va %#p l %d\n", va, l);
 	if(l < 0)
 		return ~0;
 
+	ppn = (*pte & ~0x3ff) << 2;
+	print("PPN from PTE is %llx\n", ppn);
 	mask = PGLSZ(l)-1;
-	pa = (*pte & ~mask) + (va & mask);
+	pa = (ppn & ~mask) + (va & mask);
+	print("physaddr: mask is %llx, ~mask %llx, ppn & ~mask %llx, \n", mask, ~mask, ppn & ~mask);
 
-	DBG("physaddr: l %d va %#p pa %#llx\n", l, va, pa);
+	print("physaddr: RESULT: l %d va %#p pa %#llx\n", l, va, pa);
 
 	return pa;
 }
 
-Page mach0pml4;
-
+/* to accomodate the weirdness of the rv64 modes, we're going to leave it as a 4
+ * level PT, and fake up the PML4 with one entry when it's 3 levels. Later, we want
+ * to be smarter, but a lot of our code is pretty wired to assume 4 level PT and I'm
+ * not wanting to just rip it all out. */
 void
 mmuinit(void)
 {
-	panic("mmuinit");
-#if 0
 	uint8_t *p;
-	Page *page;
-	uint64_t o, pa, r, sz;
+	uint64_t o, pa, sz, n;
 
-	archmmu();
-	DBG("mach%d: %#p pml4 %#p npgsz %d\n", machp()->machno, machp(), machp()->MMU.pml4, sys->npgsz);
+	n = archmmu();
+	print("%d page sizes\n", n);
+	print("mach%d: %#p root %#p npgsz %d\n", machp()->machno, machp(), machp()->MMU.root, sys->npgsz);
 
 	if(machp()->machno != 0){
 		/* NIX: KLUDGE: Has to go when each mach is using
@@ -853,30 +720,48 @@ mmuinit(void)
 		 */
 		p = UINT2PTR(machp()->stack);
 		p += MACHSTKSZ;
-
-		memmove(p, UINT2PTR(mach0pml4.va), PTSZ);
-		machp()->MMU.pml4 = &machp()->MMU.pml4kludge;
-		machp()->MMU.pml4->va = PTR2UINT(p);
-		machp()->MMU.pml4->pa = PADDR(p);
-		machp()->MMU.pml4->daddr = mach0pml4.daddr;	/* # of user mappings in pml4 */
-
-		r = rdmsr(Efer);
-		r |= Nxe;
-		wrmsr(Efer, r);
-		rootput(machp()->MMU.pml4->pa);
-		DBG("m %#p pml4 %#p\n", machp(), machp()->MMU.pml4);
+		panic("not yet");
+#if 0
+		memmove(p, UINT2PTR(mach0root.va), PTSZ);
+		machp()->MMU.root = &machp()->MMU.root;
+		machp()->MMU.root->va = PTR2UINT(p);
+		machp()->MMU.root->pa = PADDR(p);
+		machp()->MMU.root->daddr = mach0root.daddr;	/* # of user mappings in root */
+
+		rootput(machp()->MMU.root->pa);
+		print("m %#p root %#p\n", machp(), machp()->MMU.root);
+#endif
 		return;
 	}
 
-	page = &mach0pml4;
-	page->pa = read_csr(sptbr);
-	page->va = PTR2UINT(KADDR(page->pa));
+	machp()->MMU.root = &sys->root;
 
-	machp()->MMU.pml4 = page;
+	uintptr_t PhysicalRoot = read_csr(sptbr)<<12;
+	PTE *root = KADDR(PhysicalRoot);
+	print("Physical root is 0x%llx and root 0x %p\n", PhysicalRoot, root);
+	PTE *KzeroPTE;
+	/* As it happens, as this point, we don't know the number of page table levels.
+	 * But a walk to "level 4" will work even if it's only 3, and we can use that
+	 * information to know what to do. Further, KSEG0 is the last 2M so this will
+	 * get us the last PTE on either an L3 or L2 pte page */
+	int l;
+	if((l = mmuwalk(root, KSEG0, 2, &KzeroPTE, nil)) < 0) {
+		panic("Can't walk to PtePML2");
+	}
+	print("KzeroPTE is 0x%llx\n", KzeroPTE);
+	int PTLevels = (*KzeroPTE>>9)&3;
+	switch(PTLevels) {
+	default:
+		panic("unsupported number of page table levels: %d", PTLevels);
+		break;
+	case 0:
+		machp()->MMU.root->pa = PhysicalRoot;
+		print("root is 0x%x\n", machp()->MMU.root->pa);
+		machp()->MMU.root->va = (uintptr_t) KADDR(machp()->MMU.root->pa);
+		break;
+	}
 
-	r = rdmsr(Efer);
-	r |= Nxe;
-	wrmsr(Efer, r);
+	print("mach%d: %#p root %#p npgsz %d\n", machp()->machno, machp(), machp()->MMU.root, sys->npgsz);
 
 	/*
 	 * Set up the various kernel memory allocator limits:
@@ -893,33 +778,37 @@ mmuinit(void)
 	 * This is set up here so meminit can map appropriately.
 	 */
 	o = sys->pmstart;
+print("sys->pmstart is %p\n", o);
 	sz = ROUNDUP(o, 4*MiB) - o;
+print("Size is 0x%x\n", sz);
 	pa = asmalloc(0, sz, 1, 0);
 	if(pa != o)
 		panic("mmuinit: pa %#llx memstart %#llx\n", pa, o);
 	sys->pmstart += sz;
 
 	sys->vmstart = KSEG0;
+print("Going to set vmunused to %p + 0x%x\n", sys->vmstart, ROUNDUP(o, 4*KiB));
+	/* more issues with arithmetic since physmem is at 80000000 */
+	o &= 0x7fffffff;
 	sys->vmunused = sys->vmstart + ROUNDUP(o, 4*KiB);
-	sys->vmunmapped = sys->vmstart + o + sz;
 	sys->vmend = sys->vmstart + TMFM;
 
+	// on amd64, this was set to just the end of the kernel, because
+	// only that much was mapped, and also vmap required a lot of
+	// free *address space* (not memory, *address space*) for the
+	// vmap functions. vmap was a hack we intended to remove.
+	// It's still there. But we can get rid of it on riscv.
+	// There's lots more to do but at least vmap is gone,
+	// as is the PDMAP hack, which was also supposed to
+	// be temporary.
+	// TODO: We get much further now but still
+	// die in meminit(). When that's fixed remove
+	// this TODO.
+	sys->vmunmapped = sys->vmend;
+
 	print("mmuinit: vmstart %#p vmunused %#p vmunmapped %#p vmend %#p\n",
 		sys->vmstart, sys->vmunused, sys->vmunmapped, sys->vmend);
-
-	/*
-	 * Set up the map for PD entry access by inserting
-	 * the relevant PDP entry into the PD. It's equivalent
-	 * to PADDR(sys->pd)|PteRW|PteP.
-	 *
-	 */
-	sys->pd[PDX(PDMAP)] = sys->pdp[PDPX(PDMAP)] & ~(PteD|PteA);
-	print("sys->pd %#p %#p\n", sys->pd[PDX(PDMAP)], sys->pdp[PDPX(PDMAP)]);
-	assert((pdeget(PDMAP) & ~(PteD|PteA)) == (PADDR(sys->pd)|PteRW|PteP));
-
-
 	dumpmmuwalk(KZERO);
 
 	mmuphysaddr(PTR2UINT(end));
-#endif
 }

+ 3 - 0
sys/src/9/riscv/physalloc.c

@@ -22,6 +22,8 @@
 #define ISPOWEROF2(x)	(((x) != 0) && !((x) & ((x)-1)))
 #define UNO		((uintmem)1)
 
+#undef DBG
+#define DBG print
 enum {
 	BKmin		= 21,			/* Minimum lg2 */
 	BKmax		= 30,			/* Maximum lg2 */
@@ -371,6 +373,7 @@ physalloc(uint64_t size, int *colorp, void *tag)
 					*colorp = i;
 					return m;
 				}
+print("physalloc: return %p\n", m);
 	return m;
 }
 

+ 3 - 56
sys/src/9/riscv/qmalloc.c

@@ -265,65 +265,12 @@ qmallocalign(usize nbytes, uintptr_t align, int32_t offset, usize span)
 	return p+1;
 }
 
+/* Force it to allocate on 64 byte boundaries. I want to guarantee this happens as
+ * I don't want to take alignment traps. */
 static void*
 qmalloc(usize nbytes)
 {
-	Qlist *qlist;
-	Header *p, *q;
-	uint nunits, n;
-
-///* FIXME: (ignore for now)
-	if(nbytes == 0)
-		return nil;
-//*/
-
-	qstats[QSmalloc]++;
-	nunits = NUNITS(nbytes);
-	if(nunits <= NQUICK){
-		qlist = &QLIST[nunits];
-		QLOCK(&qlist->lk);
-		if((p = qlist->first) != nil){
-			qlist->first = p->s.next;
-			qlist->nalloc++;
-			QUNLOCK(&qlist->lk);
-			p->s.next = &checkval;
-			return p+1;
-		}
-		QUNLOCK(&qlist->lk);
-	}
-
-	MLOCK;
-	if(nunits > tailsize) {
-		/* hard way */
-		if((q = rover) != nil){
-			do {
-				p = q->s.next;
-				if(p->s.size >= nunits) {
-					if(p->s.size > nunits) {
-						p->s.size -= nunits;
-						p += p->s.size;
-						p->s.size = nunits;
-					} else
-						q->s.next = p->s.next;
-					p->s.next = &checkval;
-					rover = q;
-					qstats[QSmallocrover]++;
-					MUNLOCK;
-					return p+1;
-				}
-			} while((q = p) != rover);
-		}
-		if((n = morecore(nunits)) == 0){
-			MUNLOCK;
-			return nil;
-		}
-		tailsize += n;
-	}
-	qstats[QSmalloctail]++;
-	tailalloc(p, nunits);
-	MUNLOCK;
-
-	return p+1;
+	return qmallocalign(nbytes, 64, 0, 0);
 }
 
 static void

+ 1 - 1
sys/src/9/riscv/spike_util.h

@@ -65,7 +65,7 @@ uintptr_t mcall_query_memory(uintptr_t id, memory_block_info *p);
 uintptr_t mcall_hart_id(void);
 uintptr_t htif_interrupt(uintptr_t mcause, uintptr_t* regs);
 uintptr_t mcall_console_putchar(uint8_t ch);
-void testPrint(void);
+void putchar(void);
 uintptr_t mcall_dev_req(sbi_device_message *m);
 uintptr_t mcall_dev_resp(void);
 uintptr_t mcall_set_timer(unsigned long long when);

+ 38 - 0
sys/src/9/riscv/spl.c

@@ -0,0 +1,38 @@
+#include <u.h>
+#include <libc.h>
+#include <ureg.h>
+#include "encoding.h"
+
+
+/* these are declared in this file as we do not want them externally visible.
+ * inline assembly is not allowed in harvey.
+ */
+
+int64_t _splhi(void);
+int64_t _spllo(void);
+
+// splhi and spllo return 1 if we were at splhi. This is used in splx, below.
+int splhi(void)
+{
+	uint64_t cur;
+	cur = read_csr(sstatus);
+	_splhi();
+	return !(cur & 2);
+}
+
+int spllo(void)
+{
+	uint64_t cur;
+	cur = read_csr(sstatus);
+	_spllo();
+	return !(cur & 2);
+}
+
+void splx(int s)
+{
+	if (s)
+		_splhi();
+	else
+		_spllo();
+}
+

+ 27 - 17
sys/src/9/riscv/syscall.c

@@ -225,7 +225,7 @@ noerrorsleft(void)
 	}
 }
 
-int printallsyscalls;
+int printallsyscalls = 0;
 
 void
 syscall(unsigned int scallnr, Ureg *ureg)
@@ -233,6 +233,8 @@ syscall(unsigned int scallnr, Ureg *ureg)
 	// can only handle 6 args right now.
 	uintptr_t a0, a1, a2, a3;
 	uintptr_t a4, a5;
+	if (0 && printallsyscalls)
+		dumpgpr(ureg);
 
 	a0 = ureg->a0;
 	a1 = ureg->a1;
@@ -241,7 +243,7 @@ syscall(unsigned int scallnr, Ureg *ureg)
 	a4 = ureg->a4;
 	a5 = ureg->a5;
 	Proc *up = externup();
-	if (0) iprint("Syscall %d, %lx, %lx, %lx %lx %lx %lx\n", scallnr, a0, a1, a2, a3, a4, a5);
+	if (1) iprint("Syscall %d, %lx, %lx, %lx %lx %lx %lx\n", scallnr, a0, a1, a2, a3, a4, a5);
 	char *e;
 	uintptr_t	sp;
 	int s;
@@ -249,9 +251,8 @@ syscall(unsigned int scallnr, Ureg *ureg)
 	Ar0 ar0;
 	static Ar0 zar0;
 
-	panic("test userureg");
-	//if(!userureg(ureg))
-		//panic("syscall: cs %#llx\n", ureg->cs);
+	if(!userureg(ureg))
+		panic("syscall: userureg is false; ip %#llx\n", ureg->ip);
 
 	cycles(&up->kentry);
 
@@ -262,13 +263,14 @@ syscall(unsigned int scallnr, Ureg *ureg)
 	up->pc = ureg->ip;
 	up->dbgreg = ureg;
 	sp = ureg->sp;
+	//print("ureg -> sp says %p\n", ureg->sp);
 	startns = stopns = 0;
-	if (0) hi("so far syscall!\n");
+	if (0) print("so far syscall!\n");
 	if (up->pid == 0 || printallsyscalls) {
 		syscallfmt('E', scallnr, nil, startns, stopns, a0, a1, a2, a3, a4, a5);
 		if(up->syscalltrace) {
 			print("E %s\n", up->syscalltrace);
-			free(up->syscalltrace);
+			//free(up->syscalltrace);
 			up->syscalltrace = nil;
 		}
 	}
@@ -292,7 +294,7 @@ syscall(unsigned int scallnr, Ureg *ureg)
 		up->syscalltrace = nil;
 		startns = todget(nil);
 	}
-	if (0) hi("more syscall!\n");
+	if (0) print("more syscall!\n");
 	up->scallnr = scallnr;
 	if(scallnr == RFORK)
 		fpusysrfork(ureg);
@@ -309,14 +311,16 @@ syscall(unsigned int scallnr, Ureg *ureg)
 			error(Ebadarg);
 		}
 
-		if(sp < (USTKTOP-BIGPGSZ) || sp > (USTKTOP-sizeof(up->arg)-BY2SE))
+		if(sp < (USTKTOP-BIGPGSZ) || sp > (USTKTOP-sizeof(up->arg)-BY2SE)){
+			print("check it\n");
 			validaddr(UINT2PTR(sp), sizeof(up->arg)+BY2SE, 0);
+		}
 
 		memmove(up->arg, UINT2PTR(sp+BY2SE), sizeof(up->arg));
 		up->psstate = systab[scallnr].n;
-	if (0) hi("call syscall!\n");
+	//if (1) hi("call syscall!\n");
 		systab[scallnr].f(&ar0, a0, a1, a2, a3, a4, a5);
-	if (0) hi("it returned!\n");
+//	if (1) hi("it returned!\n");
 		poperror();
 	}
 	else{
@@ -341,7 +345,9 @@ syscall(unsigned int scallnr, Ureg *ureg)
 	/*
 	 * Put return value in frame.
 	 */
+	if (0)print("return is %p\n", ar0.p);
 	ureg->a0 = ar0.p;
+	if (0)print("ureg->ip is %p val %p\n", &ureg->ip, ureg->ip);
 
 	if (up->pid == 0 || printallsyscalls) {
 		stopns = todget(nil);
@@ -386,6 +392,7 @@ syscall(unsigned int scallnr, Ureg *ureg)
 		sched();
 		splhi();
 	}
+	if (0) hi("call kexit\n");
 	kexit(ureg);
 	if (0) hi("done kexit\n");
 }
@@ -437,13 +444,13 @@ sysexecregs(uintptr_t entry, uint32_t ssize, void *tos)
 		panic("misaligned stack in sysexecregs");
 	}
 	sp = (uintptr_t*)(USTKTOP - ssize);
-
+	print("sysexecregs: entry %p sp %p tos %p\n", entry, sp, tos);
 	ureg = up->dbgreg;
 	ureg->sp = PTR2UINT(sp);
 	ureg->ip = entry;
-	//ureg->type = 64;			/* fiction for acid */
-	panic("sysexecregs");
-	//ureg->dx = (uintptr_t)tos;
+	ureg->epc = entry;
+	ureg->a2 = USTKTOP-sizeof(Tos);
+	print("SET ip @ %p to %p\n", &ureg->ip, entry);
 
 	/*
 	 * return the address of kernel/user shared data
@@ -464,7 +471,7 @@ sysrforkchild(Proc* child, Proc* parent)
 	Ureg *cureg;
 // If STACKPAD is 1 things go very bad very quickly.
 // But it is the right value ...
-#define STACKPAD 1 /* for return PC? */
+#define STACKPAD 0 /* for return PC? */
 	/*
 	 * Add STACKPAD*BY2SE to the stack to account for
 	 *  - the return PC
@@ -479,7 +486,10 @@ sysrforkchild(Proc* child, Proc* parent)
 	/* Things from bottom of syscall which were never executed */
 	child->psstate = 0;
 	child->insyscall = 0;
-	//iprint("Child SP set tp %p\n", (void *)child->sched.sp);
+	if (0) print("Child SP set to %p\n", (void *)child->sched.sp);
+	if (0) print("NOTE: UP is wrong, ignoreit\n");
+	if (0) dumpgpr(cureg);
+
 
 	fpusysrforkchild(child, parent);
 }

+ 11 - 11
sys/src/9/riscv/tcore.c

@@ -132,7 +132,7 @@ extern int notify(Ureg*);
 
 /*
  * run an arbitrary function with arbitrary args on an ap core
- * first argument is always pml4 for process
+ * first argument is always root for process
  * make a field and a struct for the args cache line.
  *
  * Returns the return-code for the ICC or -1 if the process was
@@ -154,16 +154,16 @@ runac(Mach *mp, APfunc func, int flushtlb, void *a, int32_t n)
 
 	memmove(mp->NIX.icc->data, a, n);
 	if(flushtlb){
-		DBG("runac flushtlb: cppml4 %#p %#p\n", mp->MMU.pml4->pa, machp()->MMU.pml4->pa);
-		dpg = UINT2PTR(mp->MMU.pml4->va);
-		spg = UINT2PTR(machp()->MMU.pml4->va);
+		DBG("runac flushtlb: cproot %#p %#p\n", mp->MMU.root->pa, machp()->MMU.root->pa);
+		dpg = UINT2PTR(mp->MMU.root->va);
+		spg = UINT2PTR(machp()->MMU.root->va);
 		/* We should copy less:
-		 *	memmove(dgp, spg, machp()->MMU.pml4->daddr * sizeof(PTE));
+		 *	memmove(dgp, spg, machp()->MMU.root->daddr * sizeof(PTE));
 		 */
 		memmove(dpg, spg, PTSZ);
 		if(0){
-			print("runac: upac pml4 %#p\n", up->ac->MMU.pml4->pa);
-			dumpptepg(4, up->ac->MMU.pml4->pa);
+			print("runac: upac root %#p\n", up->ac->MMU.root->pa);
+			dumpptepg(4, up->ac->MMU.root->pa);
 		}
 	}
 	mp->NIX.icc->flushtlb = flushtlb;
@@ -287,13 +287,13 @@ runacore(void)
 				ureg->type = IdtIPI;		/* NOP */
 				break;
 			default:
-				rootput(machp()->MMU.pml4->pa);
+				rootput(machp()->MMU.root->pa);
 				if(0 && ureg->type == IdtPF){
 					print("before PF:\n");
 					print("AC:\n");
-					dumpptepg(4, up->ac->MMU.pml4->pa);
+					dumpptepg(4, up->ac->MMU.root->pa);
 					print("\n%s:\n", rolename[NIXTC]);
-					dumpptepg(4, machp()->MMU.pml4->pa);
+					dumpptepg(4, machp()->MMU.root->pa);
 				}
 				trap(ureg);
 			}
@@ -305,7 +305,7 @@ runacore(void)
 		case ICCSYSCALL:
 			DBG("runacore: syscall a0 %#llx ureg %#p\n",
 				ureg->a0, ureg);
-			rootput(machp()->MMU.pml4->pa);
+			rootput(machp()->MMU.root->pa);
 			//syscall(ureg->ax, ureg);
 			flush = 1;
 			fn = acsysret;

+ 872 - 0
sys/src/9/riscv/trap.c

@@ -0,0 +1,872 @@
+/*
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#include	<tos.h>
+#include	"ureg.h"
+#include	"../port/pmc.h"
+
+#include	"io.h"
+#include        "encoding.h"
+
+enum traps {
+	InstructionAlignment = 0,
+	InstructionAccessFault,
+	IllegalInstruction,
+	Breakpoint,
+	Trap4Reserved,
+	LoadAccessFault,
+	AMOAddressMisaligned,
+	Store_AMOAccessFault,
+	EnvironmentCall,
+	LastTrap = EnvironmentCall,
+	InterruptMask = 0x8000000000000000ULL
+};
+
+enum interrupts {
+	UserSoftware,
+	SupervisorSoftware,
+	Interrupt2Reserved,
+	Interrupt3eserved,
+	UserTimer,
+	SupervisorTimer,
+	LastInterrupt = SupervisorTimer
+};
+
+void msg(char *);
+int getchar(void);
+// counters. Set by assembly code.
+// interrupt enter and exit, systecm call enter and exit.
+unsigned long ire, irx, sce, scx;
+// Did we start doing an exit for the interrupts?
+// ir exit entry :-)
+unsigned long irxe;
+
+extern int notify(Ureg*);
+
+//static void debugbpt(Ureg*, void*);
+static void faultarch(Ureg*);
+//static void doublefault(Ureg*, void*);
+//static void unexpected(Ureg*, void*);
+//static void expected(Ureg*, void*);
+static void dumpstackwithureg(Ureg*);
+//extern int bus_irq_setup(Vctl*);
+
+static Lock vctllock;
+static Vctl *vctl[256];
+
+typedef struct Intrtime Intrtime;
+struct Intrtime {
+	uint64_t	count;
+	uint64_t	cycles;
+};
+static Intrtime intrtimes[256];
+
+void*
+intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
+{
+	int vno;
+	Vctl *v;
+	if(f == nil){
+		print("intrenable: nil handler for %d, tbdf %#x for %s\n",
+			irq, tbdf, name);
+		panic("FIX ME");
+		return nil;
+	}
+
+	v = malloc(sizeof(Vctl));
+	v->isintr = 1;
+	v->Vkey.irq = irq;
+	v->Vkey.tbdf = tbdf;
+	v->f = f;
+	v->a = a;
+	strncpy(v->name, name, KNAMELEN-1);
+	v->name[KNAMELEN-1] = 0;
+
+	ilock(&vctllock);
+	print(" ignoring bus_irq_setup");
+	//vno = bus_irq_setup(v);
+	vno = irq;
+	if(vno == -1){
+		iunlock(&vctllock);
+		print("intrenable: couldn't enable irq %d, tbdf %#x for %s\n",
+			irq, tbdf, v->name);
+		panic("DIE");
+		free(v);
+		return nil;
+	}
+	if(vctl[vno]){
+		if(vctl[v->vno]->isr != v->isr || vctl[v->vno]->eoi != v->eoi)
+			print("intrenable: handler: %s %s %#p %#p %#p %#p",
+				vctl[v->vno]->name, v->name,
+				vctl[v->vno]->isr, v->isr, vctl[v->vno]->eoi, v->eoi);
+		panic("CONFLICT");
+	}
+	v->vno = vno;
+	v->next = vctl[vno];
+	vctl[vno] = v;
+	iunlock(&vctllock);
+
+	if(v->mask)
+		v->mask(&v->Vkey, 0);
+
+	/*
+	 * Return the assigned vector so intrdisable can find
+	 * the handler; the IRQ is useless in the wonderful world
+	 * of the IOAPIC.
+	 */
+	return v;
+}
+
+static const char *const excname[] = {
+	"Instruction address misaligned",
+	"Instruction access fault",
+	"Illegal instruction",
+	"Breakpoint",
+	"Load address misaligned",
+	"Load access fault",
+	"Store address misaligned",
+	"Store access fault",
+	"Environment call from U-mode",
+	"Environment call from S-mode",
+	"Environment call from H-mode",
+	"Environment call from M-mode"
+};
+
+static void print_trap_information(const Ureg *ureg)
+{
+	Proc *up = externup();
+	const char *previous_mode;
+	int status = ureg->status;
+	/* Leave some space around the trap message */
+	print("\n PID %d\n", up ? up->pid : -1);
+
+	if (ureg->cause < nelem(excname))
+		print("Exception:          %s\n",
+				excname[ureg->cause]);
+	else
+		print("Trap:               Unknown cause %p\n",
+				(void *)ureg->cause);
+
+	previous_mode = status & 0x100 ? "Supervisor" : "User";
+	print("Previous mode:      %s\n", previous_mode);
+	print("Bad instruction pc: %p\n", (void *)ureg->epc);
+	print("Bad address:        %p\n", (void *)ureg->badaddr);
+	print("Stored ip:          %p\n", (void*) ureg->ip);
+	print("Stored sp:          %p\n", (void*) ureg->sp);
+}
+
+void trap_handler(Ureg *ureg) {
+	switch(ureg->cause) {
+		case CAUSE_MISALIGNED_FETCH:
+			print_trap_information(ureg);
+			panic("misaligned fetch, firmware is supposed to do this");
+			return;
+			break;
+		case CAUSE_ILLEGAL_INSTRUCTION:
+			print_trap_information(ureg);
+			panic("illegal instruction, going to die");
+			return;
+			break;
+		case CAUSE_BREAKPOINT:
+			print_trap_information(ureg);
+			panic("can't handle breakpoints yet\n");
+			return;
+			break;
+		case CAUSE_FAULT_FETCH:
+		case CAUSE_FAULT_LOAD:
+		case CAUSE_FAULT_STORE:
+			if (0) print_trap_information(ureg);
+			faultarch(ureg);
+			return;
+			break;
+		case CAUSE_USER_ECALL:
+		case CAUSE_HYPERVISOR_ECALL:
+		case CAUSE_MACHINE_ECALL:
+			print_trap_information(ureg);
+			panic("Can't do ecalls here");
+			return;
+			break;
+		case CAUSE_MISALIGNED_LOAD:
+			print("hgroup 2\n");
+			print_trap_information(ureg);
+			panic("misaligned LOAD, we don't do these");
+			return;
+			break;
+		case CAUSE_MISALIGNED_STORE:
+			print_trap_information(ureg);
+			panic("misaligned STORE, we don't do these");
+			return;
+			break;
+		default:
+			print_trap_information(ureg);
+			panic("WTF\n");
+			return;
+			break;
+	}
+}
+
+int
+intrdisable(void* vector)
+{
+	Vctl *v, *x, **ll;
+	//extern int ioapicintrdisable(int);
+
+	ilock(&vctllock);
+	v = vector;
+	if(v == nil || vctl[v->vno] != v)
+		panic("intrdisable: v %#p", v);
+	for(ll = vctl+v->vno; x = *ll; ll = &x->next)
+		if(v == x)
+			break;
+	if(x != v)
+		panic("intrdisable: v %#p", v);
+	if(v->mask)
+		v->mask(&v->Vkey, 1);
+	v->f(nil, v->a);
+	*ll = v->next;
+	panic("ioapicintrdisable");
+	//ioapicintrdisable(v->vno);
+	iunlock(&vctllock);
+
+	free(v);
+	return 0;
+}
+
+static int32_t
+irqallocread(Chan* c, void *vbuf, int32_t n, int64_t offset)
+{
+	char *buf, *p, str[2*(11+1)+2*(20+1)+(KNAMELEN+1)+(8+1)+1];
+	int m, vno;
+	int32_t oldn;
+	Intrtime *t;
+	Vctl *v;
+
+	if(n < 0 || offset < 0)
+		error(Ebadarg);
+
+	oldn = n;
+	buf = vbuf;
+	for(vno=0; vno<nelem(vctl); vno++){
+		for(v=vctl[vno]; v; v=v->next){
+			t = intrtimes + vno;
+			m = snprint(str, sizeof str, "%11d %11d %20llu %20llu %-*.*s %.*s\n",
+				vno, v->Vkey.irq, t->count, t->cycles, 8, 8, v->type, KNAMELEN, v->name);
+			if(m <= offset)	/* if do not want this, skip entry */
+				offset -= m;
+			else{
+				/* skip offset bytes */
+				m -= offset;
+				p = str+offset;
+				offset = 0;
+
+				/* write at most max(n,m) bytes */
+				if(m > n)
+					m = n;
+				memmove(buf, p, m);
+				n -= m;
+				buf += m;
+
+				if(n == 0)
+					return oldn;
+			}
+		}
+	}
+	return oldn - n;
+}
+
+void
+trapenable(int vno, void (*f)(Ureg*, void*), void* a, char *name)
+{
+	Vctl *v;
+
+	if(vno < 0 || vno >= 256)
+		panic("trapenable: vno %d\n", vno);
+	v = malloc(sizeof(Vctl));
+	v->type = "trap";
+	v->Vkey.tbdf = -1;
+	v->f = f;
+	v->a = a;
+	strncpy(v->name, name, KNAMELEN);
+	v->name[KNAMELEN-1] = 0;
+
+	ilock(&vctllock);
+	v->next = vctl[vno];
+	vctl[vno] = v;
+	iunlock(&vctllock);
+}
+#if 0
+static void
+nmienable(void)
+{
+	panic("nmienable");
+}
+#endif
+
+static void riscvtimer(struct Ureg *u, void *_)
+{
+	timerintr(u, 0);
+}
+
+void
+trapinit(void)
+{
+	// basically done in firmware.
+	addarchfile("irqalloc", 0444, irqallocread, nil);
+	intrenable(5, riscvtimer, nil, 0, "timer");
+}
+
+/*
+ *  keep interrupt service times and counts
+ */
+void
+intrtime(int vno)
+{
+	Proc *up = externup();
+	uint64_t diff, x;
+
+	x = perfticks();
+	diff = x - machp()->perf.intrts;
+	machp()->perf.intrts = x;
+
+	machp()->perf.inintr += diff;
+	if(up == nil && machp()->perf.inidle > diff)
+		machp()->perf.inidle -= diff;
+
+	intrtimes[vno].cycles += diff;
+	intrtimes[vno].count++;
+}
+
+static void
+pmcnop(Mach *m)
+{
+}
+
+void (*_pmcupdate)(Mach *m) = pmcnop;
+
+/* go to user space */
+void
+kexit(Ureg* u)
+{
+ 	Proc *up = externup();
+ 	uint64_t t;
+	Tos *tos;
+	Mach *mp;
+
+	/*
+	 * precise time accounting, kernel exit
+	 * initialized in exec, sysproc.c
+	 */
+	tos = (Tos*)(USTKTOP-sizeof(Tos));
+	if (0) print("USTKTOP %p sizeof(Tos) %d tos %p\n", (void *)USTKTOP, sizeof(Tos), tos);
+	cycles(&t);
+	if (1) {
+		if (0) print("tos is %p, &tos->kcycles is %p, up is %p\n", tos, &tos->kcycles, up);
+		tos->kcycles += t - up->kentry;
+		tos->pcycles = up->pcycles;
+		tos->pid = up->pid;
+
+		if (up->ac != nil)
+			mp = up->ac;
+		else
+			mp = machp();
+		if (0) print("kexit: mp is %p\n", mp);
+		tos->core = mp->machno;
+		if (0) print("kexit: mp is %p\n", mp);
+		tos->nixtype = mp->NIX.nixtype;
+		if (0) print("kexit: mp is %p\n", mp);
+		//_pmcupdate(m);
+		/*
+	 	* The process may change its core.
+	 	* Be sure it has the right cyclefreq.
+	 	*/
+		tos->cyclefreq = mp->cyclefreq;
+		if (0) print("kexit: mp is %p\n", mp);
+	}
+	if (0) print("kexit: done\n");
+}
+
+void
+kstackok(void)
+{
+	Proc *up = externup();
+
+	if(up == nil){
+		uintptr_t *stk = (uintptr_t*)machp()->stack;
+		if(*stk != STACKGUARD)
+			panic("trap: mach %d machstk went through bottom %p\n", machp()->machno, machp()->stack);
+	} else {
+		uintptr_t *stk = (uintptr_t*)up->kstack;
+		if(*stk != STACKGUARD)
+			panic("trap: proc %d kstack went through bottom %p\n", up->pid, up->kstack);
+	}
+}
+
+void
+_trap(Ureg *ureg)
+{
+	if (0) msg("+trap\n");
+	if (0) print("_trap\n");
+	/*
+	 * If it's a real trap in this core, then we want to
+	 * use the hardware cr2 register.
+	 * We cannot do this in trap() because application cores
+	 * would update m->cr2 with their cr2 values upon page faults,
+	 * and then call trap().
+	 * If we do this in trap(), we would overwrite that with our own cr2.
+	 */
+	switch(ureg->cause){
+	case CAUSE_FAULT_FETCH:
+		ureg->ftype = FT_EXEC;
+		machp()->MMU.badaddr = ureg->badaddr;
+		break;
+	case CAUSE_FAULT_LOAD:
+		ureg->ftype = FT_READ;
+		machp()->MMU.badaddr = ureg->badaddr;
+		break;
+	case CAUSE_FAULT_STORE:
+		ureg->ftype = FT_WRITE;
+		machp()->MMU.badaddr = ureg->badaddr;
+		break;
+	}
+	trap(ureg);
+}
+
+void consread(void)
+{
+	int c;
+	if (0) print("consrad\n");
+	c = getchar();
+	if (c >= 0) {
+		if (0) print("WROTE '%c'\n", c);
+		void kbdputsc(int data, int _);
+		kbdputsc(c, 0);
+	}
+}
+
+static int lastvno;
+/*
+ *  All traps come here.  It is slower to have all traps call trap()
+ *  rather than directly vectoring the handler.  However, this avoids a
+ *  lot of code duplication and possible bugs.  The only exception is
+ *  VectorSYSCALL.
+ *  Trap is called with interrupts disabled via interrupt-gates.
+ */
+void
+trap(Ureg *ureg)
+{
+	int clockintr, vno, user, interrupt;
+	// cache the previous vno to see what might be causing
+	// trouble
+	vno = ureg->cause & ~InterruptMask;
+	interrupt = !! (ureg->cause & InterruptMask);
+	//print("T 0x%llx", ureg->cause);
+	Mach *m =machp();
+	//if (sce > scx) iprint("====================");
+	lastvno = vno;
+	if (m < (Mach *)(1ULL<<63))
+		die("bogus mach");
+	Proc *up = externup();
+	char buf[ERRMAX];
+	Vctl *ctl, *v;
+
+	machp()->perf.intrts = perfticks();
+	user = userureg(ureg);
+	if(user && (machp()->NIX.nixtype == NIXTC)){
+		if (0)print("call cycles\n");
+		up->dbgreg = ureg;
+		cycles(&up->kentry);
+		if (0)print("done\n");
+	}
+
+	clockintr = interrupt && vno == SupervisorTimer;
+	if (0 && clockintr) print("C");
+	//print("clockintr %d\n", clockintr);
+
+	//_pmcupdate(machp());
+
+	if (!interrupt){
+		if (0) print("trap_handler\n");
+		trap_handler(ureg);
+	} else {
+	write_csr(sip, 0);
+
+	//print("check vno %d\n", vno);
+	if(ctl = vctl[vno]){
+		if(ctl->isintr){
+			machp()->intr++;
+			machp()->lastintr = ctl->Vkey.irq;
+		}else
+			if(up)
+				up->nqtrap++;
+
+		if(ctl->isr){
+			ctl->isr(vno);
+			if(islo())print("trap %d: isr %p enabled interrupts\n", vno, ctl->isr);
+		}
+		for(v = ctl; v != nil; v = v->next){
+			if(v->f){
+				if (0) print("F");
+				v->f(ureg, v->a);
+				if(islo())print("trap %d: ctlf %p enabled interrupts\n", vno, v->f);
+			}
+		}
+		if(ctl->eoi){
+			ctl->eoi(vno);
+			if(islo())print("trap %d: eoi %p enabled interrupts\n", vno, ctl->eoi);
+		}
+
+		intrtime(vno);
+		if(ctl->isintr){
+			if (clockintr)
+				oprof_alarm_handler(ureg);
+
+			if(up && !clockintr)
+				preempted();
+		}
+	} else if(vno < nelem(excname) && user){
+panic("OOR\n");
+		spllo();
+		snprint(buf, sizeof buf, "sys: trap: %s", excname[vno]);
+		postnote(up, 1, buf, NDebug);
+	} else if ((interrupt && vno > LastInterrupt) || (vno > LastTrap)) {
+panic("UNK\n");
+		/*
+		 * An unknown interrupt.
+		 */
+
+		iprint("cpu%d: spurious interrupt %d, last %d\n",
+			machp()->machno, vno, machp()->lastintr);
+		intrtime(vno);
+		if(user)
+			kexit(ureg);
+		return;
+	} else {
+#if 0
+		if(vno == VectorNMI){
+			nmienable();
+			if(machp()->machno != 0){
+				iprint("cpu%d: PC %#llx\n",
+					machp()->machno, ureg->ip);
+				for(;;);
+			}
+		}
+#endif
+		if (0) dumpregs(ureg);
+		if(!user){
+			ureg->sp = PTR2UINT(&ureg->sp);
+			dumpstackwithureg(ureg);
+		}
+		if(vno < nelem(excname))
+			panic("%s", excname[vno]);
+		panic("unknown trap/intr: %d\n", vno);
+	}
+	}
+	splhi();
+
+	/* delaysched set because we held a lock or because our quantum ended */
+	if(up && up->delaysched && clockintr){
+#if 0
+		if(0)
+		if(user && up->ac == nil && up->nqtrap == 0 && up->nqsyscall == 0){
+			if(!waserror()){
+				up->ac = getac(up, -1);
+				poperror();
+				runacore();
+				return;
+			}
+		}
+#endif
+		sched();
+		splhi();
+	}
+//print("DUN\n");
+
+	if(user){
+		if(up && up->procctl || up->nnote)
+			notify(ureg);
+		print("K");
+		kexit(ureg);
+	}
+//print("ALL DONE TRAP\n");
+}
+
+/*
+ * Dump general registers.
+ */
+void
+dumpgpr(Ureg* ureg)
+{
+	Proc *up = externup();
+	if(up != nil)
+		print("cpu%d: registers for %s %d\n",
+			machp()->machno, up->text, up->pid);
+	else
+		print("cpu%d: registers for kernel\n", machp()->machno);
+
+	print("ip %#llx\n", ureg->ip);
+	print("sp %#llx\n", ureg->sp);
+	print("gp %#llx\n", ureg->gp);
+	print("tp %#llx\n", ureg->tp);
+	print("t0 %#llx\n", ureg->t0);
+	print("t1 %#llx\n", ureg->t1);
+	print("t2 %#llx\n", ureg->t2);
+	print("s0 %#llx\n", ureg->s0);
+	print("s1 %#llx\n", ureg->s1);
+	print("a0 %#llx\n", ureg->a0);
+	print("a1 %#llx\n", ureg->a1);
+	print("a2 %#llx\n", ureg->a2);
+	print("a3 %#llx\n", ureg->a3);
+	print("a4 %#llx\n", ureg->a4);
+	print("a5 %#llx\n", ureg->a5);
+	print("a6 %#llx\n", ureg->a6);
+	print("a7 %#llx\n", ureg->a7);
+	print("s2 %#llx\n", ureg->s2);
+	print("s3 %#llx\n", ureg->s3);
+	print("s4 %#llx\n", ureg->s4);
+	print("s5 %#llx\n", ureg->s5);
+	print("s6 %#llx\n", ureg->s6);
+	print("s7 %#llx\n", ureg->s7);
+	print("s8 %#llx\n", ureg->s8);
+	print("s9 %#llx\n", ureg->s9);
+	print("s10 %#llx\n", ureg->s10);
+	print("s11 %#llx\n", ureg->s11);
+	print("t3 %#llx\n", ureg->t3);
+	print("t4 %#llx\n", ureg->t4);
+	print("t5 %#llx\n", ureg->t5);
+	print("t6 %#llx\n", ureg->t6);
+	print("status %#llx\n", ureg->status);
+	print("epc %#llx\n", ureg->epc);
+	print("badaddr %#llx\n", ureg->badaddr);
+	print("cause %#llx\n", ureg->cause);
+	print("insnn %#llx\n", ureg->insnn);
+	print("bp %#llx\n", ureg->bp);
+	print("ftype %#llx\n", ureg->ftype);
+
+	print("m\t%#16.16p\nup\t%#16.16p\n", machp(), up);
+}
+
+void
+dumpregs(Ureg* ureg)
+{
+	dumpgpr(ureg);
+}
+
+/*
+ * Fill in enough of Ureg to get a stack trace, and call a function.
+ * Used by debugging interface rdb.
+ */
+void
+callwithureg(void (*fn)(Ureg*))
+{
+	Ureg ureg;
+	ureg.ip = getcallerpc();
+	ureg.sp = PTR2UINT(&fn);
+	fn(&ureg);
+}
+
+static void
+dumpstackwithureg(Ureg* ureg)
+{
+	Proc *up = externup();
+	uintptr_t l, v, i, estack;
+//	extern char etext;
+	int x;
+
+	if (0) { //if((s = getconf("*nodumpstack")) != nil && atoi(s) != 0){
+		iprint("dumpstack disabled\n");
+		return;
+	}
+	iprint("dumpstack\n");
+
+	x = 0;
+	//x += iprint("ktrace 9%s %#p %#p\n", strrchr(conffile, '/')+1, ureg->ip, ureg->sp);
+	i = 0;
+	if(up != nil
+//	&& (uintptr)&l >= (uintptr)up->kstack
+	&& (uintptr_t)&l <= (uintptr_t)up->kstack+KSTACK)
+		estack = (uintptr_t)up->kstack+KSTACK;
+	else if((uintptr_t)&l >= machp()->stack && (uintptr_t)&l <= machp()->stack+MACHSTKSZ)
+		estack = machp()->stack+MACHSTKSZ;
+	else{
+		if(up != nil)
+			iprint("&up->kstack %#p &l %#p\n", up->kstack, &l);
+		else
+			iprint("&m %#p &l %#p\n", machp(), &l);
+		return;
+	}
+	x += iprint("estackx %#p\n", estack);
+
+	for(l = (uintptr_t)&l; l < estack; l += sizeof(uintptr_t)){
+		v = *(uintptr_t*)l;
+		if((KTZERO < v && v < (uintptr_t)&etext)
+		|| ((uintptr_t)&l < v && v < estack) || estack-l < 256){
+			x += iprint("%#16.16p=%#16.16p ", l, v);
+			i++;
+		}
+		if(i == 2){
+			i = 0;
+			x += iprint("\n");
+		}
+	}
+	if(i)
+		iprint("\n");
+}
+
+void
+dumpstack(void)
+{
+	callwithureg(dumpstackwithureg);
+}
+
+#if 0
+static void
+debugbpt(Ureg* ureg, void* v)
+{
+	Proc *up = externup();
+	char buf[ERRMAX];
+
+	if(up == 0)
+		panic("kernel bpt");
+	/* restore pc to instruction that caused the trap */
+	ureg->ip--;
+	sprint(buf, "sys: breakpoint");
+	postnote(up, 1, buf, NDebug);
+}
+
+static void
+doublefault(Ureg* ureg, void* v)
+{
+	iprint("badaddr %p\n", read_csr(sbadaddr));
+	panic("double fault");
+}
+
+static void
+unexpected(Ureg *ureg, void* v)
+{
+	iprint("unexpected trap %llu; ignoring\n", ureg->cause);
+}
+
+static void
+expected(Ureg* ureg, void* v)
+{
+}
+#endif
+
+/*static*/
+void
+faultarch(Ureg* ureg)
+{
+	Proc *up = externup();
+	uint64_t addr;
+	int ftype = ureg->ftype, user, insyscall;
+	char buf[ERRMAX];
+
+	addr = ureg->badaddr;
+	user = userureg(ureg);
+	if(!user && mmukmapsync(addr))
+		return;
+
+	/*
+	 * There must be a user context.
+	 * If not, the usual problem is causing a fault during
+	 * initialisation before the system is fully up.
+	 */
+	if(up == nil){
+		panic("fault with up == nil; pc %#llx addr %#llx\n",
+			ureg->ip, addr);
+	}
+
+	insyscall = up->insyscall;
+	up->insyscall = 1;
+	if (0) msg("call fault\n");
+
+	if(fault(addr, ureg->ip, ftype) < 0){
+iprint("could not %s fault %p\n", faulttypes[ftype], addr);
+		/*
+		 * It is possible to get here with !user if, for example,
+		 * a process was in a system call accessing a shared
+		 * segment but was preempted by another process which shrunk
+		 * or deallocated the shared segment; when the original
+		 * process resumes it may fault while in kernel mode.
+		 * No need to panic this case, post a note to the process
+		 * and unwind the error stack. There must be an error stack
+		 * (up->nerrlab != 0) if this is a system call, if not then
+		 * the game's a bogey.
+		 */
+		if(!user && (!insyscall || up->nerrlab == 0))
+			panic("fault: %#llx\n", addr);
+		sprint(buf, "sys: trap: fault %s addr=%#llx",
+			faulttypes[ftype], addr);
+		postnote(up, 1, buf, NDebug);
+		if(insyscall)
+			error(buf);
+	}
+	up->insyscall = insyscall;
+}
+
+/*
+ *  return the userpc the last exception happened at
+ */
+uintptr_t
+userpc(Ureg* ureg)
+{
+	Proc *up = externup();
+	if(ureg == nil)
+		ureg = up->dbgreg;
+	return ureg->ip;
+}
+
+/* This routine must save the values of registers the user is not permitted
+ * to write from devproc and then restore the saved values before returning.
+ * TODO: fix this because the segment registers are wrong for 64-bit mode.
+ */
+void
+setregisters(Ureg* ureg, char* pureg, char* uva, int n)
+{
+#if 0
+	uint64_t cs, flags, ss;
+
+	ss = ureg->ss;
+	flags = ureg->flags;
+	cs = ureg->cs;
+	memmove(pureg, uva, n);
+	ureg->cs = cs;
+	ureg->flags = (ureg->flags & 0x00ff) | (flags & 0xff00);
+	ureg->ss = ss;
+#endif
+}
+
+/* Give enough context in the ureg to produce a kernel stack for
+ * a sleeping process
+ */
+void
+setkernur(Ureg* ureg, Proc* p)
+{
+	ureg->ip = p->sched.pc;
+	ureg->sp = p->sched.sp+BY2SE;
+}
+
+uintptr_t
+dbgpc(Proc *p)
+{
+	Ureg *ureg;
+
+	ureg = p->dbgreg;
+	if(ureg == 0)
+		return 0;
+
+	return ureg->ip;
+}

+ 146 - 0
sys/src/9/riscv/trap_util.S

@@ -0,0 +1,146 @@
+/*
+ * Early initialization code for riscv
+ *
+ * Copyright 2015 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of
+ * the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
+ * GNU General Public License for more details.
+ */
+
+#define LOG_REGBYTES 3
+#define REGBYTES (1 << LOG_REGBYTES)
+
+	.macro restore_regs
+    # restore x registers
+	#if 0
+	WTF
+    LOAD  x1,1*REGBYTES(a0)
+    LOAD  x2,2*REGBYTES(a0)
+    LOAD  x3,3*REGBYTES(a0)
+    LOAD  x4,4*REGBYTES(a0)
+    LOAD  x5,5*REGBYTES(a0)
+    LOAD  x6,6*REGBYTES(a0)
+    LOAD  x7,7*REGBYTES(a0)
+    LOAD  x8,8*REGBYTES(a0)
+    LOAD  x9,9*REGBYTES(a0)
+    LOAD  x11,11*REGBYTES(a0)
+    LOAD  x12,12*REGBYTES(a0)
+    LOAD  x13,13*REGBYTES(a0)
+    LOAD  x14,14*REGBYTES(a0)
+    LOAD  x15,15*REGBYTES(a0)
+    LOAD  x16,16*REGBYTES(a0)
+    LOAD  x17,17*REGBYTES(a0)
+    LOAD  x18,18*REGBYTES(a0)
+    LOAD  x19,19*REGBYTES(a0)
+    LOAD  x20,20*REGBYTES(a0)
+    LOAD  x21,21*REGBYTES(a0)
+    LOAD  x22,22*REGBYTES(a0)
+    LOAD  x23,23*REGBYTES(a0)
+    LOAD  x24,24*REGBYTES(a0)
+    LOAD  x25,25*REGBYTES(a0)
+    LOAD  x26,26*REGBYTES(a0)
+    LOAD  x27,27*REGBYTES(a0)
+    LOAD  x28,28*REGBYTES(a0)
+    LOAD  x29,29*REGBYTES(a0)
+    LOAD  x30,30*REGBYTES(a0)
+    LOAD  x31,31*REGBYTES(a0)
+    # restore a0 last
+    LOAD  x10,10*REGBYTES(a0)
+#endif
+
+    .endm
+.macro save_tf
+  # save gprs
+	#if 0
+  STORE  x1,1*REGBYTES(x2)
+  STORE  x3,3*REGBYTES(x2)
+  STORE  x4,4*REGBYTES(x2)
+  STORE  x5,5*REGBYTES(x2)
+  STORE  x6,6*REGBYTES(x2)
+  STORE  x7,7*REGBYTES(x2)
+  STORE  x8,8*REGBYTES(x2)
+  STORE  x9,9*REGBYTES(x2)
+  STORE  x10,10*REGBYTES(x2)
+  STORE  x11,11*REGBYTES(x2)
+  STORE  x12,12*REGBYTES(x2)
+  STORE  x13,13*REGBYTES(x2)
+  STORE  x14,14*REGBYTES(x2)
+  STORE  x15,15*REGBYTES(x2)
+  STORE  x16,16*REGBYTES(x2)
+  STORE  x17,17*REGBYTES(x2)
+  STORE  x18,18*REGBYTES(x2)
+  STORE  x19,19*REGBYTES(x2)
+  STORE  x20,20*REGBYTES(x2)
+  STORE  x21,21*REGBYTES(x2)
+  STORE  x22,22*REGBYTES(x2)
+  STORE  x23,23*REGBYTES(x2)
+  STORE  x24,24*REGBYTES(x2)
+  STORE  x25,25*REGBYTES(x2)
+  STORE  x26,26*REGBYTES(x2)
+  STORE  x27,27*REGBYTES(x2)
+  STORE  x28,28*REGBYTES(x2)
+  STORE  x29,29*REGBYTES(x2)
+  STORE  x30,30*REGBYTES(x2)
+  STORE  x31,31*REGBYTES(x2)
+#endif
+  # get sr, epc, badvaddr, cause
+  csrrw  t0,sscratch,x0
+  csrr   s0,sstatus
+  csrr   t1,sepc
+  csrr   t2,sbadaddr
+  csrr   t3,scause
+	#if 0
+  STORE  t0,2*REGBYTES(x2)
+  STORE  s0,32*REGBYTES(x2)
+  STORE  t1,33*REGBYTES(x2)
+  STORE  t2,34*REGBYTES(x2)
+  STORE  t3,35*REGBYTES(x2)
+#endif
+  # get faulting insn, if it wasn't a fetch-related trap
+  li x5,-1
+//  STORE x5,36*REGBYTES(x2)
+1:
+  .endm
+
+  .text
+  .global  supervisor_trap_entry
+supervisor_trap_entry:
+    csrw mscratch, sp
+    # load in the top of the machine stack
+    li sp, 0x80FFF0 - 64
+    1:addi sp,sp,-320
+    save_tf
+    move  a0,sp
+    jal trap_handler
+    .global  trap_entry
+trap_entry:
+  csrw mscratch, sp
+
+  # SMP isn't supported yet, to avoid overwriting the same stack with different
+  # harts that handle traps at the same time.
+  # someday this gets fixed.
+  //csrr sp, mhartid
+  csrr sp, 0xf14
+.Lsmp_hang:
+  bnez sp, .Lsmp_hang
+
+  # TODO: Use the old stack pointer (plus an offset) for exceptions in machine
+  # mode, to avoid overwriting stack data.
+  li sp, 0x8000fff0
+
+  save_tf
+  move  a0,sp
+  jal trap_handler
+  .global supervisor_call_return
+supervisor_call_return:
+  csrr a0, mscratch
+  restore_regs
+  # go back into supervisor call
+  .word 0x30200073 # mret

+ 401 - 0
sys/src/9/riscv/trapentry.S

@@ -0,0 +1,401 @@
+#include "mem.h"
+/* This file was provided by the RISCV project at UC Berkeley. */
+#define PT_IP 0
+#define PT_SP 8
+#define PT_GP 16
+#define PT_TP 24
+#define PT_T0 32
+#define PT_T1 40
+#define PT_T2 48
+#define PT_S0 56
+#define PT_S1 64
+#define PT_A0 72
+#define PT_A1 80
+#define PT_A2 88
+#define PT_A3 96
+#define PT_A4 104
+#define PT_A5 112
+#define PT_A6 120
+#define PT_A7 128
+#define PT_S2 136
+#define PT_S3 144
+#define PT_S4 152
+#define PT_S5 160
+#define PT_S6 168
+#define PT_S7 176
+#define PT_S8 184
+#define PT_S9 192
+#define PT_S10 200
+#define PT_S11 208
+#define PT_T3 216
+#define PT_T4 224
+#define PT_T5 232
+#define PT_T6 240
+#define PT_SSTATUS 248
+#define PT_SEPC 256
+#define PT_SBADADDR 264
+#define PT_SCAUSE 272
+#define PT_SINSNN 280
+#define PT_SFTYPE 296
+
+	.text
+	.altmacro
+	.macro SAVE_ALL
+	LOCAL _restore_kernel_sp
+	LOCAL _save_context
+
+        addi sp, sp, -304
+	sd x1,  PT_IP(sp)
+	sd x3,  PT_GP(sp)
+	sd x5,  PT_T0(sp)
+	sd x6,  PT_T1(sp)
+	sd x7,  PT_T2(sp)
+	sd x8,  PT_S0(sp)
+	sd x9,  PT_S1(sp)
+	sd x10, PT_A0(sp)
+	sd x11, PT_A1(sp)
+	sd x12, PT_A2(sp)
+	sd x13, PT_A3(sp)
+	sd x14, PT_A4(sp)
+	sd x15, PT_A5(sp)
+	sd x16, PT_A6(sp)
+	sd x17, PT_A7(sp)
+	sd x18, PT_S2(sp)
+	sd x19, PT_S3(sp)
+	sd x20, PT_S4(sp)
+	sd x21, PT_S5(sp)
+	sd x22, PT_S6(sp)
+	sd x23, PT_S7(sp)
+	sd x24, PT_S8(sp)
+	sd x25, PT_S9(sp)
+	sd x26, PT_S10(sp)
+	sd x27, PT_S11(sp)
+	sd x28, PT_T3(sp)
+	sd x29, PT_T4(sp)
+	sd x30, PT_T5(sp)
+	sd x31, PT_T6(sp)
+
+	/* Disable FPU to detect illegal usage of
+	   floating point in kernel space */
+	//li t0, SR_FS
+
+	csrr s0, sscratch
+	csrrc s1, sstatus, t0
+	csrr s2, sepc
+	csrr s3, 0x143 /* and someday ... sbadaddr*/
+	csrr s4, 0x142 //scause
+	sd s0, PT_SP(sp)
+	sd s1, PT_SSTATUS(sp)
+	sd s2, PT_SEPC(sp)
+	sd s3, PT_SBADADDR(sp)
+	sd s4, PT_SCAUSE(sp)
+	.endm
+
+	.macro RESTORE_ALL
+	ld a0, PT_SSTATUS(sp)
+	ld a2, PT_SEPC(sp)
+	csrw sstatus, a0
+	csrw sepc, a2
+
+	ld x1,  PT_IP(sp)
+	ld x3,  PT_GP(sp)
+	ld x5,  PT_T0(sp)
+	ld x6,  PT_T1(sp)
+	ld x7,  PT_T2(sp)
+	ld x8,  PT_S0(sp)
+	ld x9,  PT_S1(sp)
+	ld x10, PT_A0(sp)
+	ld x11, PT_A1(sp)
+	ld x12, PT_A2(sp)
+	ld x13, PT_A3(sp)
+	ld x14, PT_A4(sp)
+	ld x15, PT_A5(sp)
+	ld x16, PT_A6(sp)
+	ld x17, PT_A7(sp)
+	ld x18, PT_S2(sp)
+	ld x19, PT_S3(sp)
+	ld x20, PT_S4(sp)
+	ld x21, PT_S5(sp)
+	ld x22, PT_S6(sp)
+	ld x23, PT_S7(sp)
+	ld x24, PT_S8(sp)
+	ld x25, PT_S9(sp)
+	ld x26, PT_S10(sp)
+	ld x27, PT_S11(sp)
+	ld x28, PT_T3(sp)
+	ld x29, PT_T4(sp)
+	ld x30, PT_T5(sp)
+	ld x31, PT_T6(sp)
+
+	.endm
+
+	/* we just figure out early whether it's user or not, and branch to totally different code paths.
+	 * it's just easier that way, even though there's duplication of code.
+	 * At this level, duplication is not such a problem since, once written, this kind of code never changes.
+	*/
+.globl supervisor_trap_entry
+supervisor_trap_entry:
+	/* If coming from userspace, preserve the user stack pointer and load
+	   the kernel stack pointer.  If we came from the kernel, sscratch
+	   will contain 0, and we should continue on the current stack. */
+	csrrw sp, sscratch, sp
+	/* if it is 0 we were in kernel mode. */
+	beqz sp, kernel
+	sd tp, 32(sp) // rathole.
+	mv tp, sp
+	ld sp, 16(sp) // Proc
+	ld sp, 280(sp) // kstack
+	addi sp, sp, 1024
+	addi sp, sp, 1024
+	addi sp, sp, 1024
+	addi sp, sp, 1024
+	addi sp, sp, 1024
+	addi sp, sp, 1024
+	addi sp, sp, 1024
+	SAVE_ALL
+	// tp is special, we don't want to touch it in kernel interrupts.
+	ld x1,  32(tp)
+	sd x1,  PT_TP(sp)
+
+	/* Set sscratch register to 0, so that if a recursive exception
+	   occurs, the exception vector knows it came from the kernel */
+	csrw sscratch, x0
+
+	la ra, user_ret_from_exception
+	/* MSB of cause differentiates between
+	   interrupts and exceptions */
+	bge s4, zero, 1f
+
+	/* Handle interrupts */
+	move a0, sp /* pt_regs */
+	tail _trap
+1:
+        /* Handle syscalls */
+        li t0, 8
+        beq s4, t0, user_handle_syscall
+	move a0, sp /* pt_regs */
+	tail _trap
+
+user_handle_syscall:
+	/* Advance SEPC to avoid executing the original
+	   scall instruction on sret */
+	addi s2, s2, 0x4
+	sd s2, PT_SEPC(sp)
+	/* System calls run with interrupts enabled */
+	csrs sstatus, 1/*SR_IE*/
+	move a1, sp /* pt_regs */
+	ld a0, PT_A7(sp)
+	call syscall
+user_ret_from_syscall:
+
+user_ret_from_exception:
+#define sstatus 0x100
+#define siebitno 1
+	// splhi
+	li	a0, siebitno
+	csrrs	a0, sstatus, a0
+	/* Put Mach pointer in sscratch in for user mode. */
+	csrw sscratch, tp
+user_restore_all:
+	RESTORE_ALL
+	ld x4,  PT_TP(sp)
+	ld x2,  PT_SP(sp)
+	/* FUUUUUUCCCCCKKKKK */
+	/* someday this toolchain will be done. */
+	.long 0x10200073
+	sret
+
+.globl _sysrforkret
+_sysrforkret:
+	addi	sp, sp, 32
+	sd x0, PT_A0(sp)
+	tail user_ret_from_syscall
+
+kernel:
+	csrrw sp, sscratch, sp
+	//csrr sp, sscratch
+	SAVE_ALL
+	la ra, kernel_ret_from_exception
+	/* MSB of cause differentiates between
+	   interrupts and exceptions */
+	bge s4, zero, 1f
+
+	/* Handle interrupts */
+	move a0, sp /* pt_regs */
+	tail _trap
+1:
+        /* Handle syscalls */
+        li t0, 8
+        beq s4, t0, kernel_handle_syscall
+	move a0, sp /* pt_regs */
+	tail _trap
+
+kernel_handle_syscall:
+	// TODO: panic: syscall in kernel mode.
+	j kernel_handle_syscall
+kernel_ret_from_exception:
+#define sstatus 0x100
+#define siebitno 1
+	// splhi
+	li	a0, siebitno
+	csrrc	a0, sstatus, a0
+
+kernel_restore_all:
+	RESTORE_ALL
+	addi sp, sp, 304
+
+	/* FUUUUUUCCCCCKKKKK */
+	/* someday this toolchain will be done. */
+	.long 0x10200073
+	sret
+
+#if 0
+END(handle_exception)
+
+ENTRY(ret_from_fork)
+	la ra, ret_from_exception
+	tail schedule_tail
+ENDPROC(ret_from_fork)
+
+/*
+ * Integer register context switch
+ * The callee-saved registers must be saved and restored.
+ *
+ *   a0: previous task_struct (must be preserved across the switch)
+ *   a1: next task_struct
+ */
+ENTRY(__switch_to)
+	/* Save context into prev->thread */
+	sd ra,  THREAD_RA(a0)
+	sd sp,  THREAD_SP(a0)
+	sd s0,  THREAD_S0(a0)
+	sd s1,  THREAD_S1(a0)
+	sd s2,  THREAD_S2(a0)
+	sd s3,  THREAD_S3(a0)
+	sd s4,  THREAD_S4(a0)
+	sd s5,  THREAD_S5(a0)
+	sd s6,  THREAD_S6(a0)
+	sd s7,  THREAD_S7(a0)
+	sd s8,  THREAD_S8(a0)
+	sd s9,  THREAD_S9(a0)
+	sd s10, THREAD_S10(a0)
+	sd  s11, THREAD_S11(a0)
+	/* Restore context from next->thread */
+	ld ra,  THREAD_RA(a1)
+	ld sp,  THREAD_SP(a1)
+	ld s0,  THREAD_S0(a1)
+	ld s1,  THREAD_S1(a1)
+	ld s2,  THREAD_S2(a1)
+	ld s3,  THREAD_S3(a1)
+	ld s4,  THREAD_S4(a1)
+	ld s5,  THREAD_S5(a1)
+	ld s6,  THREAD_S6(a1)
+	ld s7,  THREAD_S7(a1)
+	ld s8,  THREAD_S8(a1)
+	ld s9,  THREAD_S9(a1)
+	ld s10, THREAD_S10(a1)
+	ld s11, THREAD_S11(a1)
+	mv tp, a1 /* Next current pointer */
+	ret
+ENDPROC(__switch_to)
+
+ENTRY(__fstate_save)
+	li t1, SR_FS
+	csrs sstatus, t1
+	frcsr t0
+	fsd f0,  THREAD_F0(a0)
+	fsd f1,  THREAD_F1(a0)
+	fsd f2,  THREAD_F2(a0)
+	fsd f3,  THREAD_F3(a0)
+	fsd f4,  THREAD_F4(a0)
+	fsd f5,  THREAD_F5(a0)
+	fsd f6,  THREAD_F6(a0)
+	fsd f7,  THREAD_F7(a0)
+	fsd f8,  THREAD_F8(a0)
+	fsd f9,  THREAD_F9(a0)
+	fsd f10, THREAD_F10(a0)
+	fsd f11, THREAD_F11(a0)
+	fsd f12, THREAD_F12(a0)
+	fsd f13, THREAD_F13(a0)
+	fsd f14, THREAD_F14(a0)
+	fsd f15, THREAD_F15(a0)
+	fsd f16, THREAD_F16(a0)
+	fsd f17, THREAD_F17(a0)
+	fsd f18, THREAD_F18(a0)
+	fsd f19, THREAD_F19(a0)
+	fsd f20, THREAD_F20(a0)
+	fsd f21, THREAD_F21(a0)
+	fsd f22, THREAD_F22(a0)
+	fsd f23, THREAD_F23(a0)
+	fsd f24, THREAD_F24(a0)
+	fsd f25, THREAD_F25(a0)
+	fsd f26, THREAD_F26(a0)
+	fsd f27, THREAD_F27(a0)
+	fsd f28, THREAD_F28(a0)
+	fsd f29, THREAD_F29(a0)
+	fsd f30, THREAD_F30(a0)
+	fsd f31, THREAD_F31(a0)
+	sw t0, THREAD_FCSR(a0)
+	csrc sstatus, t1
+	ret
+ENDPROC(__fstate_save)
+
+ENTRY(__fstate_restore)
+	li t1, SR_FS
+	lw t0, THREAD_FCSR(a0)
+	csrs sstatus, t1
+	fld f0,  THREAD_F0(a0)
+	fld f1,  THREAD_F1(a0)
+	fld f2,  THREAD_F2(a0)
+	fld f3,  THREAD_F3(a0)
+	fld f4,  THREAD_F4(a0)
+	fld f5,  THREAD_F5(a0)
+	fld f6,  THREAD_F6(a0)
+	fld f7,  THREAD_F7(a0)
+	fld f8,  THREAD_F8(a0)
+	fld f9,  THREAD_F9(a0)
+	fld f10, THREAD_F10(a0)
+	fld f11, THREAD_F11(a0)
+	fld f12, THREAD_F12(a0)
+	fld f13, THREAD_F13(a0)
+	fld f14, THREAD_F14(a0)
+	fld f15, THREAD_F15(a0)
+	fld f16, THREAD_F16(a0)
+	fld f17, THREAD_F17(a0)
+	fld f18, THREAD_F18(a0)
+	fld f19, THREAD_F19(a0)
+	fld f20, THREAD_F20(a0)
+	fld f21, THREAD_F21(a0)
+	fld f22, THREAD_F22(a0)
+	fld f23, THREAD_F23(a0)
+	fld f24, THREAD_F24(a0)
+	fld f25, THREAD_F25(a0)
+	fld f26, THREAD_F26(a0)
+	fld f27, THREAD_F27(a0)
+	fld f28, THREAD_F28(a0)
+	fld f29, THREAD_F29(a0)
+	fld f30, THREAD_F30(a0)
+	fld f31, THREAD_F31(a0)
+	fscsr t0
+	csrc sstatus, t1
+	ret
+ENDPROC(__fstate_restore)
+
+
+	.section ".rodata"
+	/* Exception vector table */
+ENTRY(excp_vect_table)
+	PTR do_trap_insn_misaligned
+	PTR do_page_fault
+	PTR do_trap_insn_illegal
+	PTR do_trap_unknown
+	PTR do_trap_unknown
+	PTR do_page_fault
+	PTR do_trap_unknown
+	PTR do_page_fault
+	PTR 0 /* handle_syscall */
+	PTR do_trap_break
+excp_vect_table_end:
+END(excp_vect_table)
+
+#endif

+ 21 - 3
sys/src/9/riscv/uart.c

@@ -13,15 +13,33 @@
  * GNU General Public License for more details.
  */
 
-#include <u.h>
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
 
 uintptr_t uart_platform_base(int idx)
 {
 	return (uintptr_t) 0x40001000;
 }
 
-void testPrint(uint8_t c)
+void putchar(uint8_t c)
 {
-	uint8_t *cp = (void *)uart_platform_base(0);
+	uint8_t *cp = KADDR(uart_platform_base(0));
 	*cp = c;
 }
+
+// Get a 7-bit char. < 0 means err.
+int getchar(void)
+{
+	uint8_t *cp = KADDR(uart_platform_base(0));
+	if (cp[5] & 1) {
+		int c = cp[0];
+		if (0) print("getchar: got 0x%x\n", c);
+		return c;
+	}
+	return -1;
+}
+
+