aboutsummaryrefslogtreecommitdiff
path: root/arch/riscv/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/riscv/kernel')
-rw-r--r--arch/riscv/kernel/irq.c4
-rw-r--r--arch/riscv/kernel/main.c314
-rw-r--r--arch/riscv/kernel/proc.c15
-rw-r--r--arch/riscv/kernel/vmem.c52
4 files changed, 355 insertions, 30 deletions
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 <apos/debug.h>
-#include <apos/init.h>
#include <apos/vmem.h>
+#include <apos/pmem.h>
+#include <apos/types.h>
#include <apos/mem.h>
#include <apos/lock.h>
+#include <apos/tcb.h>
+#include <apos/string.h>
+#include <apos/proc.h>
+#include <apos/mem_nodes.h>
+#include <apos/elf.h>
+#include <apos/initrd.h>
+#include <libfdt.h>
+#include <csr.h>
+
+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)
+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)
{
- /* TODO: fix this shit */
- map_vregion(branch, fdt_base, fdt_base, fdt_top - fdt_base, VM_R | VM_W |VM_V);
+ 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(struct init_data_t d)
+void __main main(void *fdt)
{
- 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)
- */
+ init_dbg(fdt);
+ struct vm_branch_t *b = init_vmem(fdt);
- /* functionality that should be implemented:
- * figure out best continuous run of memory (pmap etc)
- * arbitrary user and kernel mapping (memory)
- * vm to pm
- */
+ 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 <apos/tcb.h>
+#include <apos/elf.h>
+#include <csr.h>
+
+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 <apos/string.h>
+#include <apos/pmem.h>
+#include <apos/vmem.h>
+#include <apos/mem.h>
+#include <apos/debug.h>
+#include <pages.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) (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;
+}