From 9966b49db468de0168c7a455dfb5b4c66c413c4d Mon Sep 17 00:00:00 2001 From: Kimplul Date: Sun, 19 Dec 2021 17:10:36 +0200 Subject: Massive changes to jump to userspace --- arch/riscv/kernel/irq.c | 4 + arch/riscv/kernel/main.c | 324 ++++++++++++++++++++++++++++++++++++++++++----- arch/riscv/kernel/proc.c | 15 +++ arch/riscv/kernel/vmem.c | 52 ++++++++ 4 files changed, 360 insertions(+), 35 deletions(-) create mode 100644 arch/riscv/kernel/irq.c create mode 100644 arch/riscv/kernel/proc.c create mode 100644 arch/riscv/kernel/vmem.c (limited to 'arch/riscv/kernel') diff --git a/arch/riscv/kernel/irq.c b/arch/riscv/kernel/irq.c new file mode 100644 index 0000000..f015411 --- /dev/null +++ b/arch/riscv/kernel/irq.c @@ -0,0 +1,4 @@ +void irq_handler() +{ + +} diff --git a/arch/riscv/kernel/main.c b/arch/riscv/kernel/main.c index 3f524bc..c7a1290 100644 --- a/arch/riscv/kernel/main.c +++ b/arch/riscv/kernel/main.c @@ -1,51 +1,305 @@ #include -#include #include +#include +#include #include #include +#include +#include +#include +#include +#include +#include +#include +#include + +struct pm_layout_t { + pm_t base; + pm_t top; +}; + +struct pm_orders_t { + size_t max_order; + size_t bits[10]; + size_t page_shift; +}; #ifdef DEBUG -static void kernel_dbg(void *fdt) +static void init_dbg(void *fdt) { struct dbg_info_t dbg = dbg_from_fdt(fdt); - map_vmem(ROOT_PTE, (pm_t)dbg.dbg_ptr, (vm_t)dbg.dbg_ptr, - VM_R | VM_W | VM_V, MM_KPAGE); dbg_init(dbg.dbg_ptr, dbg.dev); } #else -#define kernel_dbg(...) +#define init_dbg(...) #endif -static void map_fdt(struct vm_branch_t *branch, vm_t fdt_base, vm_t fdt_top) -{ - /* TODO: fix this shit */ - map_vregion(branch, fdt_base, fdt_base, fdt_top - fdt_base, VM_R | VM_W |VM_V); -} - -void __main main(struct init_data_t d) -{ - init_pmap((void *)d.pmap_base); - init_mem(d.max_order, d.bits, d.page_shift); - init_vmem(ROOT_PTE, d.tmp_pte); - /* TODO: move pmap into vmem somewhere */ - /* TODO: mark kernel area as used */ - map_fdt(ROOT_PTE, d.fdt_base, d.fdt_top); - kernel_dbg((void *)d.fdt_base); - dbg("test\n"); - /* TODO: approximate order of business: - * setup debugging in vmem (requires mapping fdt) - * free unnecessary init - * setup interrupts (should this be done in init?) - * load init from initrd - * setup init environment (thread control blocks etc.) - * jumpstart init (free stack init setup) - */ - - /* functionality that should be implemented: - * figure out best continuous run of memory (pmap etc) - * arbitrary user and kernel mapping (memory) - * vm to pm - */ +static struct pm_layout_t get_memlayout(void *fdt) +{ + struct cell_info_t ci = get_reginfo(fdt, "/memory"); + int mem_offset = fdt_path_offset(fdt, "/memory"); + uint8_t *mem_reg = + (uint8_t *) fdt_getprop(fdt, mem_offset, "reg", NULL); + + /* if riscv128 comes around we will probably see addr_cells == 4, but + * I'm not too concerned about it at the moment */ + pm_t base = (pm_t)fdt_load_int_ptr(ci.addr_cells, mem_reg); + + if(ci.addr_cells == 2) + mem_reg += sizeof(fdt64_t); + else + mem_reg += sizeof(fdt32_t); + + /* -1 because base is a legitimate memory address */ + pm_t top = (pm_t)fdt_load_int_ptr(ci.size_cells, mem_reg) + base - 1; + return (struct pm_layout_t){base, top}; +} + +static pm_t get_kerneltop() +{ + /* interesting, for some reason if I define these to be just char + * pointers I get some wacky values. Not sure why that would be, but + * this works. */ + extern char __kernel_end; + return (pm_t)&__kernel_end; +} + +static pm_t get_initrdtop(void *fdt) +{ + int chosen_offset = fdt_path_offset(fdt, "/chosen"); + struct cell_info_t ci = get_cellinfo(fdt, chosen_offset); + + void *initrd_end_ptr = (void *)fdt_getprop(fdt, chosen_offset, + "linux,initrd-end", NULL); + + return (pm_t)fdt_load_int_ptr(ci.addr_cells, initrd_end_ptr); +} + +static pm_t get_initrdbase(void *fdt) +{ + int chosen_offset = fdt_path_offset(fdt, "/chosen"); + struct cell_info_t ci = get_cellinfo(fdt, chosen_offset); + + void *initrd_base_ptr = (void *)fdt_getprop(fdt, chosen_offset, + "linux,initrd-start", NULL); + + return (pm_t)fdt_load_int_ptr(ci.addr_cells, initrd_base_ptr); +} + +static pm_t get_fdttop(void *fdt) +{ + const char *b = (const char *)fdt; + return (pm_t)(b + fdt_totalsize(fdt)); +} + +static pm_t get_fdtbase(void *fdt) +{ + /* lol */ + return (pm_t)fdt; +} + + +static void mark_area_used(pm_t base, pm_t top) +{ + size_t area_left = top - base; + /* TODO: add in a method to make sure that we use as large mappings as + * possible. */ + while(area_left >= MM_KPAGE_SIZE){ + mark_used(base, MM_KPAGE); + area_left -= MM_KPAGE_SIZE; + base += MM_KPAGE_SIZE; + } + + if(area_left != 0) + mark_used(base, MM_KPAGE_SIZE); +} + +static void mark_reserved_mem(void *fdt) +{ + int rmem_offset = fdt_path_offset(fdt, "/reserved-memory/mmode_resv0"); + struct cell_info_t ci = get_reginfo(fdt, "/reserved-memory/mmode_resv0"); + uint8_t *rmem_reg = (uint8_t *)fdt_getprop(fdt, rmem_offset, "reg", NULL); + + pm_t base = (pm_t)fdt_load_int_ptr(ci.addr_cells, rmem_reg); + + if(ci.addr_cells == 2) + rmem_reg += sizeof(fdt64_t); + else + rmem_reg += sizeof(fdt32_t); + + pm_t top = (pm_t)fdt_load_int_ptr(ci.size_cells, rmem_reg) + base - 1; + mark_area_used(base, top); +} + +static struct pm_layout_t setup_pmem(void *fdt) +{ + struct pm_layout_t pmem = get_memlayout(fdt); + + pm_t initrd_top = get_initrdtop(fdt); + pm_t kernel_top = get_kerneltop(); + pm_t fdt_top = get_fdttop(fdt); + + pm_t top = MAX3(kernel_top, initrd_top, fdt_top); + dbg("initrd_top:\t%#lx\n", initrd_top); + dbg("kernel_top:\t%#lx\n", kernel_top); + dbg("fdt_top:\t%#lx\n", fdt_top); + + /* TODO: check that pmap placement doesn't overwrite anything, such as + * stack or go over top address of memory */ + size_t probe_size = probe_pmap(pmem.base, pmem.top - pmem.base); + /* riscv handles two byte boundaries better than one byte, so align + * upwards */ + pm_t pmap_base = align_up(top + 1, 2); + size_t actual_size = populate_pmap(pmem.base, pmem.top - pmem.base, + pmap_base); + + /* TODO: not entirely sure what to do about this, probably give up trying to + * boot? */ + if(probe_size != actual_size){ + dbg("BUG! probe_size (%#lx) != actual_size (%#lx)\n", + probe_size, actual_size); + } + + /* mark init stack, at the moment always mapped to 2M */ + mark_used(PM_STACK_BASE, MM_MPAGE); + + /* mark kernel, at the moment it is always mapped to a 2M partition */ + mark_used(PM_KERN, MM_MPAGE); + + /* mark fdt and initrd */ + mark_area_used(get_initrdbase(fdt), initrd_top); + mark_area_used(get_fdtbase(fdt), fdt_top); + + /* mark pmap */ + mark_area_used(pmap_base, pmap_base + actual_size); + + /* mark reserved mem */ + mark_reserved_mem(fdt); + + return (struct pm_layout_t){.base = pmap_base, .top = actual_size + pmap_base}; +} + +static struct pm_orders_t init_pmem(void *fdt) +{ + enum mm_mode_t mmode = get_mmode(fdt); + + size_t max_order = 0; + size_t order_bits = 9; + switch(mmode){ + case Sv32: + max_order = 1; + order_bits = 10; + break; + + case Sv39: + max_order = 2; + break; + + case Sv48: + max_order = 3; + break; + }; + + size_t bits[10] = {0}; + for(size_t i = 0; i <= max_order; ++i) + bits[i] = order_bits; + + init_mem(max_order, bits, 12); + + struct pm_orders_t ret = {max_order, {0}, 12}; + for(size_t i = 0; i <= __mm_max_order; ++i) + ret.bits[i] = bits[i]; + + return ret; +} + +static void populate_root_branch(struct vm_branch_t *b, size_t mul) +{ + for(size_t i = 0; i <= (MM_KPAGE_SIZE / sizeof(size_t))/2; ++i) + b->leaf[i] = (struct vm_branch_t *)(mul * i | VM_V | VM_R | VM_W | VM_X | VM_G); +} + +static void start_vmem(struct vm_branch_t *branch, enum mm_mode_t m) +{ + /* TODO: get ASID from CPU id */ + + 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))); + + __asm__ ("sfence.vma" : : : "memory"); + /* Sv57 && Sv64 in the future? */ +} + +static struct vm_branch_t *init_vmem(void *fdt) +{ + struct pm_orders_t o = init_pmem(fdt); + setup_pmem(fdt); + + struct vm_branch_t *b = (struct vm_branch_t *)alloc_page(MM_KPAGE, 0); + memset(b, 0, sizeof(struct vm_branch_t)); + + enum mm_mode_t mm = Sv48; + size_t mul = 0; + switch(o.max_order){ + case 3: /* Sv48 */ + mm = Sv48; + mul = 1UL << 37; + break; + + case 2: /* Sv39 */ + mm = Sv39; + mul = 1UL << 28; + break; + + case 1: /* Sv32 */ + mm = Sv32; + mul = 1UL << 20; + break; + } + + set_uvmem_size(mul); + populate_root_branch(b, uvmem_size()); + /* jump to vmem */ + start_vmem(b, mm); + return b; +} + +static void init_irq(void *fdt) +{ +} + +static void init_proc(void *fdt, struct vm_branch_t *b) +{ + struct tcb *t = (struct tcb *)alloc_page(MM_KPAGE, 0); + t->b_r = b; + t->pid = 0; + t->tid = 0; + threads_insert(t); + sp_mem_init(&t->sp_r, uvmem_size()); + + /* binary itself */ + size_t sz = align_up(get_init_size(fdt), BASE_PAGE_SIZE); + t->bin = alloc_uvmem(t, sz, VM_V | VM_X); + /* stack */ + t->stack = alloc_uvmem(t, SZ_2M, VM_V | VM_R | VM_W); + /* if it needs heap, it'll ask for it */ + + move_init(fdt, (void *)t->bin, sz); + jump_to_userspace(t, 0, 0); +} + +void __main main(void *fdt) +{ + init_dbg(fdt); + struct vm_branch_t *b = init_vmem(fdt); + + init_mem_blocks(); + init_irq(fdt); + init_proc(fdt, b); } diff --git a/arch/riscv/kernel/proc.c b/arch/riscv/kernel/proc.c new file mode 100644 index 0000000..3a39dd3 --- /dev/null +++ b/arch/riscv/kernel/proc.c @@ -0,0 +1,15 @@ +#include +#include +#include + +void jump_to_userspace(struct tcb *t, char **argv, int argc) +{ + csr_write(CSR_SEPC, bin_entry(t->bin)); + __asm__("mv sp, %0\n" : "=r" (t->stack) :: "memory"); + __asm__("sret\n" ::: "memory"); +} + +void return_to_userspace(struct tcb *t) +{ + /* lol */ +} diff --git a/arch/riscv/kernel/vmem.c b/arch/riscv/kernel/vmem.c new file mode 100644 index 0000000..ffe8731 --- /dev/null +++ b/arch/riscv/kernel/vmem.c @@ -0,0 +1,52 @@ +#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) (pnum_to_paddr(pte_ppn(pte))) +#define vm_to_index(a, o) (pm_to_index(a, o)) + +void map_vmem(struct vm_branch_t *branch, + pm_t paddr, vm_t vaddr, uint8_t flags, enum mm_order_t order) +{ + enum mm_order_t top = __mm_max_order; + while (top != order) { + size_t idx = vm_to_index(vaddr, top); + + if (!branch->leaf[idx]) { + pm_t new_leaf = alloc_page(MM_KPAGE, 0); + branch->leaf[idx] = + (struct vm_branch_t *)to_pte(new_leaf, VM_V); + + void *leaf_ptr = (void *)new_leaf; + memset(leaf_ptr, 0, sizeof(struct vm_branch_t)); + } + + pm_t pte = (pm_t)branch->leaf[idx]; + pm_t branch_pptr = (pm_t)pte_addr(pte); + branch = (struct vm_branch_t *)branch_pptr; + top--; + } + + size_t idx = vm_to_index(vaddr, top); + branch->leaf[idx] = (struct vm_branch_t *)to_pte(paddr, flags); +} + +void unmap_vmem(struct vm_branch_t *branch, vm_t vaddr, enum mm_order_t order) +{ + while (order) { + size_t idx = pm_to_index(vaddr, order); + branch = (struct vm_branch_t *)pte_addr(branch->leaf[idx]); + } + + size_t idx = pm_to_index(vaddr, order); + if (branch->leaf[idx]) + free_page(order, pte_addr(branch->leaf[idx])); + + branch->leaf[idx] = 0; +} -- cgit v1.3