diff options
Diffstat (limited to 'arch/riscv64')
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 Binary files differnew file mode 100755 index 0000000..f05254f --- /dev/null +++ b/arch/riscv64/conf/init 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 Binary files differnew file mode 100644 index 0000000..90344c1 --- /dev/null +++ b/arch/riscv64/conf/initrd 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 |
