aboutsummaryrefslogtreecommitdiff
path: root/arch/riscv64
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2022-01-06 23:39:12 +0200
committerKimplul <kimi.h.kuparinen@gmail.com>2022-01-07 14:05:06 +0200
commit9cc125fb3cfdc2c8ae7153b5ae0fe705980835fb (patch)
treea1974f9513458acab0bd8d09758259b24b6d7d8c /arch/riscv64
parentf0eba93472e0afecc57180fc0d638eeef8e6f3c6 (diff)
downloadkmi-9cc125fb3cfdc2c8ae7153b5ae0fe705980835fb.tar.gz
kmi-9cc125fb3cfdc2c8ae7153b5ae0fe705980835fb.zip
Allow building with clang
+ Release mode is sort of broken, as lto doesn't work with linker relaxation and turning the relaxation off causes issues with the jump from init to kernel (as I've currently implemented it, could probably be fixed with a manual jump from assembly, I'll add it to my TODO list)
Diffstat (limited to 'arch/riscv64')
-rw-r--r--arch/riscv64/conf/apos.its48
-rw-r--r--arch/riscv64/conf/boot.cmd4
-rwxr-xr-xarch/riscv64/conf/initbin0 -> 968 bytes
-rw-r--r--arch/riscv64/conf/init-link.S31
-rw-r--r--arch/riscv64/conf/initrdbin0 -> 1536 bytes
-rw-r--r--arch/riscv64/conf/kernel-link.S26
-rwxr-xr-xarch/riscv64/conf/mkimage.sh31
-rwxr-xr-xarch/riscv64/conf/rmimage.sh9
-rw-r--r--arch/riscv64/conf/rootfs.fd9
-rw-r--r--arch/riscv64/conf/s.S4
-rw-r--r--arch/riscv64/config.h34
-rw-r--r--arch/riscv64/include/csr.h63
-rw-r--r--arch/riscv64/include/lock.h1
-rw-r--r--arch/riscv64/include/pages.h18
-rw-r--r--arch/riscv64/include/sbi.h12
-rw-r--r--arch/riscv64/include/tcb.h9
-rw-r--r--arch/riscv64/include/vmem.h27
-rw-r--r--arch/riscv64/init/init.c72
-rw-r--r--arch/riscv64/init/start.S12
-rw-r--r--arch/riscv64/ipc.txt7
-rw-r--r--arch/riscv64/kernel/cpu.c6
-rw-r--r--arch/riscv64/kernel/irq.c30
-rw-r--r--arch/riscv64/kernel/main.c11
-rw-r--r--arch/riscv64/kernel/pmem.c15
-rw-r--r--arch/riscv64/kernel/proc.c15
-rw-r--r--arch/riscv64/kernel/vmem.c182
-rw-r--r--arch/riscv64/mm.txt93
-rw-r--r--arch/riscv64/proc.txt3
-rw-r--r--arch/riscv64/source.mk17
29 files changed, 789 insertions, 0 deletions
diff --git a/arch/riscv64/conf/apos.its b/arch/riscv64/conf/apos.its
new file mode 100644
index 0000000..da48860
--- /dev/null
+++ b/arch/riscv64/conf/apos.its
@@ -0,0 +1,48 @@
+/dts-v1/;
+
+/ {
+ description = "FUCK";
+#address-cells = <1>;
+
+ images {
+ kernel {
+ description = "Garbaggio";
+ data = /incbin/("../../../apos.bin");
+ type = "kernel";
+ arch = "riscv";
+ os = "apos";
+ compression = "none";
+ load = <0x80080000>;
+ entry = <0x80080000>;
+ hash-1 {
+ algo = "sha1";
+ };
+ };
+
+ initrd {
+ description = "Gargabbio initrd";
+ data = /incbin/("initrd");
+ type = "ramdisk";
+ arch = "riscv";
+ os = "apos";
+ compression = "none";
+ load = <0x88300000>;
+ hash-1 {
+ algo = "sha1";
+ };
+ };
+ };
+
+ configurations {
+ default = "config";
+
+ config {
+ description = "Default configuration";
+ kernel = "kernel";
+ ramdisk = "initrd";
+ hash-1 {
+ algo = "sha1";
+ };
+ };
+ };
+};
diff --git a/arch/riscv64/conf/boot.cmd b/arch/riscv64/conf/boot.cmd
new file mode 100644
index 0000000..a7517fd
--- /dev/null
+++ b/arch/riscv64/conf/boot.cmd
@@ -0,0 +1,4 @@
+fdt move ${fdt_addr} ${fdt_addr_r}
+fdt addr ${fdt_addr_r}
+load ${devtype} ${devnum} ${kernel_addr_r} apos.itb
+bootm ${kernel_addr_r} ${kernel_addr_r} ${fdt_addr_r}
diff --git a/arch/riscv64/conf/init b/arch/riscv64/conf/init
new file mode 100755
index 0000000..f05254f
--- /dev/null
+++ b/arch/riscv64/conf/init
Binary files differ
diff --git a/arch/riscv64/conf/init-link.S b/arch/riscv64/conf/init-link.S
new file mode 100644
index 0000000..baabcc6
--- /dev/null
+++ b/arch/riscv64/conf/init-link.S
@@ -0,0 +1,31 @@
+OUTPUT_ARCH(riscv)
+ENTRY(_start)
+
+SECTIONS {
+ . = ABSOLUTE(PM_KERN_BASE);
+ __init_start = .;
+
+ /* objcopy only copies these three sections (as far as I'm aware) into the
+ produces binary, so __init_end should point to the correct location in the
+ final binary
+ */
+ .text ALIGN(4K) : AT(0) {
+ *(.init.start)
+ *(.text*);
+ }
+
+ .rodata : {
+ *(.rodata*)
+ }
+
+ .data : {
+ *(.data*)
+ }
+
+ .bss : {
+ *(.sbss*) *(.bss*) *(COMMON)
+ }
+
+ __init_end = . ;
+ __kernel_size = <KERNEL_SIZE>;
+}
diff --git a/arch/riscv64/conf/initrd b/arch/riscv64/conf/initrd
new file mode 100644
index 0000000..90344c1
--- /dev/null
+++ b/arch/riscv64/conf/initrd
Binary files differ
diff --git a/arch/riscv64/conf/kernel-link.S b/arch/riscv64/conf/kernel-link.S
new file mode 100644
index 0000000..c6a910a
--- /dev/null
+++ b/arch/riscv64/conf/kernel-link.S
@@ -0,0 +1,26 @@
+OUTPUT_ARCH(riscv)
+ENTRY(main)
+
+SECTIONS {
+ . = ABSOLUTE(VM_KERN);
+ __kernel_start = .;
+
+ .text ALIGN(4K) : AT(0) {
+ *(.kernel.start);
+ *(.text*);
+ }
+
+ .rodata : {
+ *(.rodata*)
+ }
+
+ .data : {
+ *(.data*)
+ }
+
+ .bss : {
+ *(.sbss*) *(.bss*) *(COMMON)
+ }
+
+ __kernel_end = .;
+}
diff --git a/arch/riscv64/conf/mkimage.sh b/arch/riscv64/conf/mkimage.sh
new file mode 100755
index 0000000..d097248
--- /dev/null
+++ b/arch/riscv64/conf/mkimage.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+if [ ! -e fs ]; then
+ mkdir fs
+fi
+
+if [ ! -e rootfs.img ]; then
+ virt-make-fs --partition=mbr --type=vfat --size=10M fs rootfs.img
+fi
+
+
+SSIZE=$(fdisk -l rootfs.img | awk '$1=="Units:" {print $8}')
+OFFSET=$(fdisk -l rootfs.img | awk '$1=="rootfs.img1" {print $2}')
+mount -o loop,offset=$((${SSIZE}*${OFFSET})) rootfs.img fs
+
+# not entirely pleased with this solution, although eventually I should probably
+# move `run` out of the kernel repo and into some `aposos` repo with a runtime
+# and proper initrd etc. so this is good enough for now
+../u-boot-apos/tools/mkimage -f arch/riscv64/conf/apos.its fs/apos.itb
+../u-boot-apos/tools/mkimage -A riscv -O apos -T script \
+ -d arch/riscv64/conf/boot.cmd fs/boot.scr
+
+umount -l fs
+
+qemu-system-riscv64 -machine virt \
+ -kernel ../u-boot-apos/u-boot \
+ -bios /usr/lib/riscv64-linux-gnu/opensbi/generic/fw_jump.elf \
+ -device virtio-blk-device,drive=hd0 \
+ -drive file=rootfs.img,format=raw,id=hd0 \
+ -S -s -m 2G \
+ -serial stdio
diff --git a/arch/riscv64/conf/rmimage.sh b/arch/riscv64/conf/rmimage.sh
new file mode 100755
index 0000000..8d6457f
--- /dev/null
+++ b/arch/riscv64/conf/rmimage.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+if [ -e fs ]; then
+ rm -r fs
+fi
+
+if [ -e rootfs.img ]; then
+ rm rootfs.img
+fi
diff --git a/arch/riscv64/conf/rootfs.fd b/arch/riscv64/conf/rootfs.fd
new file mode 100644
index 0000000..59fb5f8
--- /dev/null
+++ b/arch/riscv64/conf/rootfs.fd
@@ -0,0 +1,9 @@
+label: gpt
+label-id: DBD1F99F-BFA3-D448-828F-0B505DF81F17
+device: rootfs.img
+unit: sectors
+first-lba: 2048
+last-lba: 20446
+sector-size: 512
+
+rootfs.img1 : start= 2048, size= 18399, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=B129DB0D-F225-5C48-AA6D-DD8C049FA72C
diff --git a/arch/riscv64/conf/s.S b/arch/riscv64/conf/s.S
new file mode 100644
index 0000000..80a3a11
--- /dev/null
+++ b/arch/riscv64/conf/s.S
@@ -0,0 +1,4 @@
+.text
+.global _start
+_start:
+ecall
diff --git a/arch/riscv64/config.h b/arch/riscv64/config.h
new file mode 100644
index 0000000..0ef22fa
--- /dev/null
+++ b/arch/riscv64/config.h
@@ -0,0 +1,34 @@
+#include <apos/sizes.h>
+
+/* --- START ARCH USER CONFIG VALUES --- */
+/* physical address to which the kernel will be loaded */
+#define RAM_BASE 0x80000000
+#define PM_KERN_BASE (RAM_BASE + SZ_512K)
+#define PM_KERN_SIZE (SZ_256K)
+#define PM_KERN_TOP (PM_KERN_BASE + PM_KERN_SIZE)
+/* --- END ARCH USER CONFIG VALUES --- */
+
+/* don't touch >:( */
+
+#define PM_STACK_BASE (PM_KERN_BASE + SZ_256K)
+#define PM_STACK_SIZE (SZ_256K)
+#define PM_STACK_TOP (PM_STACK_BASE + PM_STACK_SIZE)
+
+#define VM_DMAP (0xffffffc000000000) /* testing for now */
+#define VM_KERN (VM_DMAP + SZ_256K)
+#define TMP_PTE (-SZ_2G)
+#define ROOT_PTE (0)
+#define ROOT_REGION (SZ_4K)
+
+#define IO_PAGE 511
+#define KSTART_PAGE 256
+#define CSTACK_PAGE 255
+
+/* assume Sv39, probably wouldn't be too difficult to use runtime parameters
+ * instead. First 4K is reserved for NULL, but I suppose it could be mapped
+ * later if *absolutely* necessary. */
+#define UVMEM_START (SZ_4K)
+#define UVMEM_END (SZ_256G - SZ_1G)
+
+#define PROC_STACK_TOP (SZ_256G)
+#define PROC_STACK_BASE (SZ_256G - SZ_1G)
diff --git a/arch/riscv64/include/csr.h b/arch/riscv64/include/csr.h
new file mode 100644
index 0000000..52db9c1
--- /dev/null
+++ b/arch/riscv64/include/csr.h
@@ -0,0 +1,63 @@
+#ifndef APOS_CSR_H
+#define APOS_CSR_H
+
+#include <apos/utils.h>
+
+#define SATP_MODE_Sv32 0x80000000
+#define SATP_MODE_Sv39 0x8000000000000000
+#define SATP_MODE_Sv48 0x9000000000000000
+
+
+/* supervisor CSR registers */
+#define CSR_SSTATUS 0x100
+#define CSR_SIE 0x104
+#define CSR_STVEC 0x105
+#define CSR_SCOUNTEREN 0x106
+
+#define CSR_SENVCFG 0x10A
+
+#define CSR_SSCRATCH 0x140
+#define CSR_SEPC 0x141
+#define CSR_SCAUSE 0x142
+#define CSR_STVAL 0x143
+#define CSR_SIP 0x144
+
+#define CSR_SATP 0x180
+
+#define CSR_SCONTEXT 0x5A8
+
+/* Exception causes */
+#define EXC_INST_MISALIGNED 0
+#define EXC_INST_ACCESS 1
+#define EXC_BREAKPOINT 3
+#define EXC_LOAD_ACCESS 5
+#define EXC_STORE_ACCESS 7
+#define EXC_SYSCALL 8
+#define EXC_INST_PAGE_FAULT 12
+#define EXC_LOAD_PAGE_FAULT 13
+#define EXC_STORE_PAGE_FAULT 15
+
+/* status CSR bits */
+#define SSTATUS_SUM (1 << 18)
+#define SSTATUS_SPP (1 << 8)
+
+/* directly lifted from Linux:/arch/riscv/include/asm/asm.h:9-13 */
+#ifdef __ASSEMBLY__
+#define __ASM_STR(x) x
+#else
+#define __ASM_STR(x) #x
+#endif
+
+#define csr_read(csr, res)\
+ __asm__ volatile ("csrr %0, " __ASM_STR(csr) : "=r" (res) : : "memory")
+
+#define csr_write(csr, val)\
+ __asm__ volatile ("csrw " __ASM_STR(csr) ", %0" : : "r" (val) : "memory")
+
+#define csr_set(csr, val)\
+ __asm__ volatile ("csrs " __ASM_STR(csr) ", %0" : : "r" (val) : "memory")
+
+#define csr_clear(csr, val)\
+ __asm__ volatile ("csrc " __ASM_STR(csr) ", %0" : : "r" (val) : "memory")
+
+#endif /* APOS_CSR_H */
diff --git a/arch/riscv64/include/lock.h b/arch/riscv64/include/lock.h
new file mode 100644
index 0000000..4f71344
--- /dev/null
+++ b/arch/riscv64/include/lock.h
@@ -0,0 +1 @@
+#define optional_pause()
diff --git a/arch/riscv64/include/pages.h b/arch/riscv64/include/pages.h
new file mode 100644
index 0000000..362499a
--- /dev/null
+++ b/arch/riscv64/include/pages.h
@@ -0,0 +1,18 @@
+#ifndef APOS_RISCV_PAGES_H
+#define APOS_RISCV_PAGES_H
+
+#include <apos/types.h>
+
+#define MM_KPAGE MM_O0
+#define MM_MPAGE MM_O1
+#define MM_GPAGE MM_O2
+#define MM_TPAGE MM_O3
+
+#define MM_KPAGE_SIZE SZ_4K
+#define MM_MPAGE_SIZE SZ_2M
+#define MM_GPAGE_SIZE SZ_1G
+#define MM_TPAGE_SIZE SZ_512G
+
+typedef uint64_t pm_t;
+
+#endif /* APOS_RISCV_PAGES_H */
diff --git a/arch/riscv64/include/sbi.h b/arch/riscv64/include/sbi.h
new file mode 100644
index 0000000..609849d
--- /dev/null
+++ b/arch/riscv64/include/sbi.h
@@ -0,0 +1,12 @@
+#ifndef APOS_RISCV_SBI_H
+
+struct sbiret {
+ long error;
+ long value;
+};
+
+struct sbiret sbi_ecall(int ext, int fid,
+ unsigned long arg0, unsigned long arg1, unsigned long arg2,
+ unsigned long arg3, unsigned long arg4, unsigned long arg5);
+
+#endif /* APOS_RISCV_SBI_H */
diff --git a/arch/riscv64/include/tcb.h b/arch/riscv64/include/tcb.h
new file mode 100644
index 0000000..d1e0717
--- /dev/null
+++ b/arch/riscv64/include/tcb.h
@@ -0,0 +1,9 @@
+#ifndef ARCH_RISCV_TCB_H
+#define ARCH_RISCV_TCB_H
+
+struct arch_tcbd {
+ /* empty for now, but should probably be filled with stuff like register
+ * saving of something */
+};
+
+#endif /* ARCH_RISCV_TCB_H */
diff --git a/arch/riscv64/include/vmem.h b/arch/riscv64/include/vmem.h
new file mode 100644
index 0000000..228b24b
--- /dev/null
+++ b/arch/riscv64/include/vmem.h
@@ -0,0 +1,27 @@
+#ifndef APOS_RISCV_VMAP_H
+#define APOS_RISCV_VMAP_H
+
+#include <apos/types.h>
+#include <apos/attrs.h>
+
+#define VM_V (1 << 0)
+#define VM_R (1 << 1)
+#define VM_W (1 << 2)
+#define VM_X (1 << 3)
+#define VM_U (1 << 4)
+#define VM_G (1 << 5)
+#define VM_A (1 << 6)
+#define VM_D (1 << 7)
+
+enum mm_mode {
+ Sv48,
+ Sv39,
+ Sv32,
+};
+
+typedef size_t vm_t;
+struct __packed vm_branch {
+ struct vm_branch *leaf[512];
+};
+
+#endif /* APOS_RISCV_VMAP_H */
diff --git a/arch/riscv64/init/init.c b/arch/riscv64/init/init.c
new file mode 100644
index 0000000..7aecfb5
--- /dev/null
+++ b/arch/riscv64/init/init.c
@@ -0,0 +1,72 @@
+#include <apos/types.h>
+#include <apos/attrs.h>
+#include <apos/utils.h>
+#include <apos/vmem.h>
+#include <vmem.h>
+#include <csr.h>
+
+/* assume 64 bit for now */
+struct __packed init_vmem {
+ int *leaf[512];
+};
+
+struct init_vmem *root_branch;
+
+#define to_pte(a, f) (((a) >> 12) << 10 | (f))
+
+/* assume Sv39 for now */
+void init_bootmem()
+{
+ size_t flags = VM_V | VM_X | VM_R | VM_W;
+
+ extern char *__init_start;
+ root_branch = (struct init_vmem *)align_down((size_t)&__init_start - SZ_4K, SZ_4K);
+
+ /* direct mapping (temp) */
+ for(size_t i = 0; i < CSTACK_PAGE; ++i)
+ root_branch->leaf[i] = (int*)to_pte(SZ_1G * i, flags);
+
+ /* kernel (also sort of direct mapping) */
+ flags |= VM_G;
+ for(size_t i = KSTART_PAGE; i < IO_PAGE; ++i)
+ root_branch->leaf[i] = (int *)to_pte(RAM_BASE + SZ_1G * (i - 256), flags);
+
+ /* kernel IO, map to 0 for now, will be updated in the future */
+ root_branch->leaf[IO_PAGE] = (int *)to_pte(0, flags);
+
+ csr_write(CSR_SATP, SATP_MODE_Sv39 | ((size_t)root_branch >> 12));
+}
+
+void move_kernel()
+{
+ extern char *__init_end;
+ extern char *__kernel_size;
+
+ size_t sz = (size_t)&__kernel_size;
+ char *src = (char *)&__init_end;
+ char *dst = (char *)VM_KERN;
+ for(size_t i = 0; i < sz; ++i)
+ dst[i] = src[i];
+}
+
+#define __va_reg(reg)\
+{\
+ vm_t reg = 0;\
+ __asm__("mv %0, " QUOTE(reg) : "=r" (reg) :: );\
+ reg = (vm_t)__va(reg);\
+ __asm__("mv " QUOTE(reg) ", %0" :: "rK" (reg) : );\
+}
+
+void init(void *fdt)
+{
+ extern char *__init_end;
+ extern void jump_to_kernel(void *k, void *fdt);
+ void (*kernel_main)(void *fdt) = (void (*)(void *))(VM_KERN);
+ init_bootmem();
+ move_kernel();
+ __va_reg(sp);
+ __va_reg(fp);
+ __va_reg(gp);
+ jump_to_kernel((void *)VM_KERN, __va(fdt));
+ //kernel_main(__va(fdt));
+}
diff --git a/arch/riscv64/init/start.S b/arch/riscv64/init/start.S
new file mode 100644
index 0000000..7842946
--- /dev/null
+++ b/arch/riscv64/init/start.S
@@ -0,0 +1,12 @@
+.section .init.start
+.global _start
+_start:
+li sp, PM_STACK_TOP
+call init
+
+.section .text
+.global jump_to_kernel
+jump_to_kernel:
+mv a2, a0 // store kernel addr
+mv a0, a1 // move fdt pointer to first argument
+jr a2 // jump to kernel
diff --git a/arch/riscv64/ipc.txt b/arch/riscv64/ipc.txt
new file mode 100644
index 0000000..21d758e
--- /dev/null
+++ b/arch/riscv64/ipc.txt
@@ -0,0 +1,7 @@
+Current idea: Have a bunch of servers that allow IPC requests. Each IPC request
+to a server temporarily maps the server's address space etc. into the calling
+functions address space, except for the callstack. Callstack should be a bunch
+of 4K pages, so they can easily be mapped to be unreachable by deeper nested
+server calls, whereas the normal stack can be mapped to higher order pages to
+save a little bit of memory access latency when the client is operating
+normally.
diff --git a/arch/riscv64/kernel/cpu.c b/arch/riscv64/kernel/cpu.c
new file mode 100644
index 0000000..6ae4ee7
--- /dev/null
+++ b/arch/riscv64/kernel/cpu.c
@@ -0,0 +1,6 @@
+#include <apos/cpu.h>
+
+id_t cpu_id()
+{
+ return 0; /* VERY MUCH TEMP */
+}
diff --git a/arch/riscv64/kernel/irq.c b/arch/riscv64/kernel/irq.c
new file mode 100644
index 0000000..f844dd1
--- /dev/null
+++ b/arch/riscv64/kernel/irq.c
@@ -0,0 +1,30 @@
+#include <apos/irq.h>
+#include <apos/attrs.h>
+#include <apos/debug.h>
+#include <csr.h>
+
+void init_irq(void *fdt)
+{
+ UNUSED(fdt);
+ csr_write(CSR_STVEC, &handle_irq);
+
+ long s = 0;
+ csr_read(CSR_SIE, s);
+ info("CSR_SIE: %lx\n", s);
+}
+
+__aligned(4) void handle_irq()
+{
+ while(1);
+}
+
+/* very simple for now */
+void enable_irq()
+{
+ csr_set(CSR_SSTATUS, CSR_SIE);
+}
+
+void disable_irq()
+{
+ csr_clear(CSR_SSTATUS, CSR_SIE);
+}
diff --git a/arch/riscv64/kernel/main.c b/arch/riscv64/kernel/main.c
new file mode 100644
index 0000000..9ce256e
--- /dev/null
+++ b/arch/riscv64/kernel/main.c
@@ -0,0 +1,11 @@
+#include <apos/utils.h>
+#include <csr.h>
+
+void arch_setup(void *fdt)
+{
+ UNUSED(fdt);
+ /* allow supervisor code to touch user pages */
+ csr_set(CSR_SSTATUS, SSTATUS_SUM);
+ /* mark that we want to eventually jump to userspace */
+ csr_clear(CSR_SSTATUS, SSTATUS_SPP);
+}
diff --git a/arch/riscv64/kernel/pmem.c b/arch/riscv64/kernel/pmem.c
new file mode 100644
index 0000000..45b8eeb
--- /dev/null
+++ b/arch/riscv64/kernel/pmem.c
@@ -0,0 +1,15 @@
+#include <apos/pmem.h>
+
+int arch_pmem_conf(void *fdt, size_t *max_order, size_t *base_bits, size_t bits[ORDERS_NUM])
+{
+ UNUSED(fdt);
+
+ /* Sv39 for now */
+ *max_order = 2;
+ *base_bits = 12;
+ /* we can assume bits[] is zeroed out beforehand */
+ for(size_t i = 0; i <= *max_order; ++i)
+ bits[i] = 9;
+
+ return 0;
+}
diff --git a/arch/riscv64/kernel/proc.c b/arch/riscv64/kernel/proc.c
new file mode 100644
index 0000000..b696912
--- /dev/null
+++ b/arch/riscv64/kernel/proc.c
@@ -0,0 +1,15 @@
+#include <apos/tcb.h>
+#include <apos/elf.h>
+#include <csr.h>
+
+void jump_to_userspace(struct tcb *t, int argc, char **argv)
+{
+ csr_write(CSR_SEPC, t->entry);
+ __asm__ volatile ("mv sp, %0\n" : "=r" (t->proc_stack) :: "memory");
+ __asm__ volatile ("sret\n" ::: "memory");
+}
+
+void return_to_userspace(struct tcb *t)
+{
+ /* lol */
+}
diff --git a/arch/riscv64/kernel/vmem.c b/arch/riscv64/kernel/vmem.c
new file mode 100644
index 0000000..bc1a74e
--- /dev/null
+++ b/arch/riscv64/kernel/vmem.c
@@ -0,0 +1,182 @@
+#include <apos/string.h>
+#include <apos/pmem.h>
+#include <apos/vmem.h>
+#include <apos/mem.h>
+#include <apos/debug.h>
+#include <apos/cpu.h>
+#include <pages.h>
+#include <vmem.h>
+#include <csr.h>
+
+#define pte_ppn(pte) (((pm_t)(pte)) >> 10)
+#define pte_flags(pte) (((pm_t)(pte)) & 0xff)
+#define to_pte(p, f) ((pm_to_pnum(p) << 10) | (f))
+#define pte_addr(pte) __va(pnum_to_paddr(pte_ppn(pte)))
+#define pte_paddr(pte) (pnum_to_paddr(pte_ppn(pte)))
+#define vm_to_index(a, o) (pm_to_index(a, o))
+#define is_active(pte) (pte_flags(pte) & VM_V)
+#define is_leaf(pte) (is_active(pte) && (pte_flags(pte) & ~VM_V))
+#define is_branch(pte) (is_active(pte) && !(pte_flags(pte) & ~VM_V))
+
+static pm_t *__find_vmem(struct vm_branch *b, vm_t v, enum mm_order *o)
+{
+ enum mm_order top = __mm_max_order;
+ if(o)
+ *o = MM_O0;
+ do {
+ size_t idx = vm_to_index(v, top);
+ pm_t pte = (pm_t)b->leaf[idx];
+
+ if(!pte)
+ return 0;
+
+ if(is_leaf(pte)){
+ if(o)
+ *o = top;
+
+ return (pm_t *)&b->leaf[idx];
+ }
+
+ b = (struct vm_branch *)pte_addr(pte);
+ } while (top--);
+
+ return 0;
+}
+
+int mod_vmem(struct vm_branch *branch, vm_t vaddr, pm_t paddr, uint8_t flags)
+{
+ pm_t *pte = __find_vmem(branch, vaddr, 0);
+ if(pte){
+ *pte = to_pte((pm_t)__pa(paddr), flags);
+ return 0;
+ }
+
+ return -1;
+}
+
+/* huh, should probably add status flags etc. to all my API functions. Damn, I'm
+ * lazy. */
+int stat_vmem(struct vm_branch *branch, vm_t vaddr, pm_t *paddr,
+ enum mm_order *order, uint8_t *flags)
+{
+ pm_t *pte = __find_vmem(branch, vaddr, order);
+ if(pte){
+ if(paddr)
+ *paddr = (pm_t)pte_addr(*pte);
+
+ if(flags)
+ *flags = pte_flags(*pte);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+static struct vm_branch *__create_leaf()
+{
+ pm_t new_leaf = alloc_page(MM_KPAGE, 0);
+ memset((void *)new_leaf, 0, sizeof(struct vm_branch));
+ return (struct vm_branch *)to_pte((pm_t)__pa(new_leaf), VM_V);
+}
+
+static void __destroy_branch(struct vm_branch *b)
+{
+ if(!b)
+ return;
+
+ for(size_t i = 0; i < BASE_PAGE_SIZE / sizeof(pm_t); ++i){
+ if(is_branch(b->leaf[i]))
+ __destroy_branch((struct vm_branch *)pte_addr(b->leaf[i]));
+
+ free_page(MM_KPAGE, (pm_t)pte_addr(b->leaf[i]));
+ }
+}
+
+void map_vmem(struct vm_branch *branch,
+ pm_t paddr, vm_t vaddr, uint8_t flags, enum mm_order order)
+{
+ enum mm_order top = __mm_max_order;
+ while (top != order) {
+ size_t idx = vm_to_index(vaddr, top);
+
+ if (!branch->leaf[idx])
+ branch->leaf[idx] = __create_leaf();
+
+
+ branch = (struct vm_branch *)pte_addr(branch->leaf[idx]);
+ top--;
+ }
+
+ size_t idx = vm_to_index(vaddr, top);
+ if (is_branch(branch->leaf[idx])) /* something has gone terribly wrong */
+ __destroy_branch(branch->leaf[idx]);
+
+ branch->leaf[idx] = (struct vm_branch *)to_pte((pm_t)__pa(paddr), flags);
+}
+
+void unmap_vmem(struct vm_branch *branch, vm_t vaddr)
+{
+
+ pm_t *pte = __find_vmem(branch, vaddr, 0);
+ if(pte)
+ *pte = 0;
+}
+
+void flush_tlb()
+{
+ __asm__ volatile ("sfence.vma %0\n" :: "r" (cpu_id()) : "memory");
+}
+
+void flush_tlb_all()
+{
+ __asm__ volatile ("sfence.vma\n" ::: "memory");
+}
+
+static void start_vmem(struct vm_branch *branch, enum mm_mode m)
+{
+ branch = (struct vm_branch *)__pa(branch);
+
+ if(m == Sv32)
+ csr_write(CSR_SATP, SATP_MODE_Sv32 | pm_to_pnum((pm_t)(branch)));
+ else if (m == Sv39)
+ csr_write(CSR_SATP, SATP_MODE_Sv39 | pm_to_pnum((pm_t)(branch)));
+ else
+ csr_write(CSR_SATP, SATP_MODE_Sv48 | pm_to_pnum((pm_t)(branch)));
+
+ flush_tlb_all();
+ /* Sv57 && Sv64 in the future? */
+}
+
+struct vm_branch *init_vmem(void *fdt)
+{
+ UNUSED(fdt);
+ struct vm_branch *b = (struct vm_branch *)alloc_page(MM_KPAGE, 0);
+ memset(b, 0, MM_KPAGE_SIZE);
+
+ populate_root_branch(b);
+ /* update which memory branch to use */
+ start_vmem(b, Sv39);
+ return b;
+}
+
+void populate_root_branch(struct vm_branch *b)
+{
+ size_t flags = VM_V | VM_R | VM_W | VM_X | VM_G;
+ for(size_t i = KSTART_PAGE; i < IO_PAGE; ++i)
+ b->leaf[i] = (struct vm_branch *)to_pte(RAM_BASE + SZ_1G * (i - KSTART_PAGE), flags);
+
+ /* map kernel IO to zero for now, this will be overridden later
+ * (if at all) */
+ b->leaf[IO_PAGE] = (struct vm_branch *)to_pte(0, flags);
+}
+
+#ifdef DEBUG
+int setup_kernel_io(struct vm_branch *b, vm_t paddr)
+{
+ /* assume Sv39 for now */
+ pm_t gigapage = paddr / MM_GPAGE_SIZE;
+ b->leaf[IO_PAGE] = (struct vm_branch *)to_pte(gigapage, VM_V | VM_R | VM_W);
+ return -SZ_1G + paddr - (gigapage * MM_GPAGE_SIZE);
+}
+#endif
diff --git a/arch/riscv64/mm.txt b/arch/riscv64/mm.txt
new file mode 100644
index 0000000..e5c52cb
--- /dev/null
+++ b/arch/riscv64/mm.txt
@@ -0,0 +1,93 @@
+Ideas for actually working virtual mappings:
+
+1. Define sensible memory areas, to start with for Sv39 and then that can
+probably be extended to Sv48 etc.
+
+Something like this for Sv39:
+
+Offset | Size | Purpose
+------------------------------------------------
+-242 GiB | 256 GiB | Userspace
+------------------------------------------------
+-246 GiB | 4 GiB | Direct mapping
+-254 GiB | 8 GiB | vmemmap
+-255 GiB | 1 GiB | Kernel IO
+-256 GiB | 1 GiB | Kernel
+
+(alternate scheme: Kernel vmem is itself a direct mapping?)
+(first 256KiB page reserved for whatever, next 256KiB reserved for kernel vma,
+ next 512KiB reserved for kernel pma?)
+
+Sv39, final answer:
+
+First 255 pages are userspace (uvmem), page 255 is reserved for callstack.
+Following 255 pages are reserved for kernel 'direct mapping', and the 512th page
+is reserved for kernel IO.
+
+Sv48:
+
+Offset | Size | Purpose
+------------------------------------------------
+-58368 GiB | 65536 GiB | Userspace
+------------------------------------------------
+-60416 GiB | 2048 GiB | Direct mapping
+-64512 GiB | 4096 GiB | vmemmap
+-65024 GiB | 512 GiB | Kernel IO
+-65536 GiB | 512 GiB | Kernel
+
+Note that tera/giga/etc pages have to be aligned to their corresponding size.
+This means that these areas will probably have to be mapped with 2M pages, which
+is fine but would've been cool to use top-level PTE's to keep context switch
+latencies down.
+
+(Note: Direct mapping can be something like
+ inline static __va(pm_t pa)
+ {
+ if (__dma_bottom >= pa || __dma_top <= pa)
+ remap_dma(pa);
+ return pa + __dma_bottom;
+ }
+ ...)
+
+PTE's can be kept in vmemmap, and the Direct mapping can overlap.
+
+2. Come up with a somewhat sensible virtual memory management system. Linux uses
+rb_trees, which might be a good idea, although I should figure out how to handle
+both acuiring and freeing them quickly.
+
+Maybe keep two separate trees, one for free and one for mapped regions? Free
+regions should be ordered by size, to quickly find the best suitable size.
+Used/mapped regions should be ordered by address, to quickly find the correct
+region.
+
+struct rb_free_node {
+ union {
+ size_t size;
+ vm_t address;
+ };
+
+ struct rb_free_node *pair;
+ struct rb_free_node *left;
+ struct rb_free_node *right;
+};
+
+struct rb_used_node {
+ vm_t address;
+ size_t size;
+
+ struct rb_used_node *left;
+ struct rb_used_node *right;
+};
+
+My idea: Have two trees made of rb_free_node, one ordered by size and one
+ordered by address, with the *pair pointer pointing to the corresponding node in
+the other tree.
+
+When allocating, search up the most suitable free area in the tree ordered by
+size, and do the required node modifications.
+(remove node completely, split area one or twice, mirror in other tree)
+
+When freeing, search up the address in the used area, after which insert it into
+the free trees and remove it from the used tree. After that, merge adjacent
+memory areas in the free trees? Could be done reasonably efficiently, should
+start up a test implementation, like with pmem.
diff --git a/arch/riscv64/proc.txt b/arch/riscv64/proc.txt
new file mode 100644
index 0000000..29b2e92
--- /dev/null
+++ b/arch/riscv64/proc.txt
@@ -0,0 +1,3 @@
+Each thread should obviously have a thread control block (tcb) which lives in
+kernelspace and has pointers to virtual memory allocations, along with PID and
+other required data. That's about what I can come up with at the moment.
diff --git a/arch/riscv64/source.mk b/arch/riscv64/source.mk
new file mode 100644
index 0000000..d1986d4
--- /dev/null
+++ b/arch/riscv64/source.mk
@@ -0,0 +1,17 @@
+KERNEL_LOCAL != echo arch/riscv64/kernel/*.[cS]
+KERNEL_SOURCES += $(KERNEL_LOCAL)
+INIT_SOURCES += arch/riscv64/init/*.[cS]
+
+CLEANUP_CMD := ./arch/riscv64/conf/rmimage.sh
+
+ARCH_FLAGS := -mcmodel=medany
+
+# llvm compilation doesn't seem to work, either bfd reads the object files wrong
+# and outputs incorrect symbols or lld handles relocations in a dumb way. Only
+# GCC compiles the kernel correctly at the moment (on RISCV, at least). I'll
+# look into this later, but for now I'll just use GCC. The code does _compile_
+# with clang, it just won't link.
+#LINKFLAGS := -fuse-ld=bfd
+
+run:
+ ./arch/riscv64/conf/mkimage.sh