From 9cc125fb3cfdc2c8ae7153b5ae0fe705980835fb Mon Sep 17 00:00:00 2001 From: Kimplul Date: Thu, 6 Jan 2022 23:39:12 +0200 Subject: 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) --- arch/riscv64/kernel/vmem.c | 182 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 arch/riscv64/kernel/vmem.c (limited to 'arch/riscv64/kernel/vmem.c') 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 -- cgit v1.3