aboutsummaryrefslogtreecommitdiff
path: root/arch/riscv64
diff options
context:
space:
mode:
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