kernel.c (4830B)
1 // Copyright 2022, Brian Swetland <swetland@frotz.net> 2 // Licensed under the Apache License, Version 2.0 3 4 #include "kernel.h" 5 6 #include <hw/debug.h> 7 #include <hw/platform.h> 8 #include <hw/litex.h> 9 10 #include <string.h> 11 12 // kernel page directory 13 static uint32_t *kpgdir; 14 15 // load general registers from eframe 16 // save ef + sizeof(eframe_t) as kernel sp and tp for next trap 17 // enter user mode at ef->pc 18 void user_mode_entry(eframe_t* ef); 19 20 extern char __extra_start[]; 21 extern char __extra_end[]; 22 23 // the top bits on a kernel stack 24 typedef struct { 25 eframe_t eframe; 26 thread_t thread; 27 } kstack_top_t; 28 29 #define FRAMEBUFFER_SIZE (640*480*2) 30 31 void map_framebuffer(uint32_t* pgdir) { 32 paddr_t pa = FRAMEBUFFER_BASE; 33 vaddr_t va = 0x20000000; 34 for (unsigned n = 0; n < FRAMEBUFFER_SIZE; n += PAGE_SIZE) { 35 // TODO: remove the pa from the freelist 36 vm_map_4k(pgdir, va + n, pa + n, USER_RW); 37 } 38 } 39 40 void start_user_program(void) { 41 vaddr_t user_start = 0x10000000; 42 vaddr_t user_stack = 0x10400000; 43 44 xprintf("user.bin %u bytes\n", (__extra_end - __extra_start)); 45 46 // allocate 16KB (4 pages) for user text/data 47 vm_map_4k(kpgdir, user_start + 0*PAGE_SIZE, ppage_alloc_z(), USER_RWX); 48 vm_map_4k(kpgdir, user_start + 1*PAGE_SIZE, ppage_alloc_z(), USER_RWX); 49 vm_map_4k(kpgdir, user_start + 2*PAGE_SIZE, ppage_alloc_z(), USER_RWX); 50 vm_map_4k(kpgdir, user_start + 3*PAGE_SIZE, ppage_alloc_z(), USER_RWX); 51 52 // allocate a 4KB (1 page) for user stack 53 vm_map_4k(kpgdir, user_stack - 1*PAGE_SIZE, ppage_alloc_z(), USER_RW); 54 55 map_framebuffer(kpgdir); 56 57 // allow S-MODE writes to U-MODE pages 58 csr_set(CSR_SSTATUS, SSTATUS_SUM); 59 60 // copy embedded user.bin into user memory 61 memcpy((void*) user_start, __extra_start, __extra_end - __extra_start); 62 63 csr_clr(CSR_SSTATUS, SSTATUS_SUM); 64 65 // allocate a kernel stack page 66 // setup an eframe with the initial user register state 67 kstack_top_t *kst = kpage_alloc_z() + PAGE_SIZE - sizeof(kstack_top_t); 68 kst->eframe.pc = user_start; 69 kst->eframe.sp = user_stack; 70 71 // set previous privilege mode to user (0) 72 csr_clr(CSR_SSTATUS, SSTATUS_SPP); 73 74 xprintf("\n[ starting user mode program ]\n\n"); 75 76 user_mode_entry(&kst->eframe); 77 // does not return 78 } 79 80 extern char __text_start[]; 81 extern char __rodata_start[]; 82 extern char __data_start[]; 83 extern char __bss_start[]; 84 extern char __bss_end[]; 85 extern char __image_end[]; 86 extern char __memory_top[]; 87 88 // On entry: 89 // SP = __memory_top (top of ram) 90 // SATP points at __memory_top - 8K 91 92 void start(void) { 93 xprintf("X/OS Kernel v0.1\n\n"); 94 95 xprintf("text %p %u\n", __text_start, __rodata_start - __text_start); 96 xprintf("rodata %p %u\n", __rodata_start, __data_start - __rodata_start); 97 xprintf("data %p %u\n", __data_start, __bss_start - __data_start); 98 xprintf("bss %p %u\n", __bss_start, __bss_end - __bss_start); 99 xprintf("free %p %u\n", __image_end, __memory_top - __image_end); 100 xprintf("memtop %p\n", __memory_top); 101 102 // set trap vector to trap_entry() in trap-entry-single.S 103 // it will call exception_handler() or interrupt_handler() 104 csr_write(CSR_STVEC, (uintptr_t) trap_entry); 105 106 // the topmost page is the boot stack 107 // the second topmost page is the boot page directory 108 109 // give all other pages from end-of-kernel to boot pagedir 110 // to the virtual memory manager 111 vmm_init(kva_to_pa(__image_end), kva_to_pa(__memory_top) - 2 * PAGE_SIZE); 112 113 // build a more correct page table 114 kpgdir = kpage_alloc_z(); 115 116 char *va = __text_start; 117 // map kernel text RX 118 while (va < __rodata_start) { 119 vm_map_4k(kpgdir, (vaddr_t) va, kva_to_pa(va), KERNEL_RX); 120 va += PAGE_SIZE; 121 } 122 // map kernel rodata RO 123 while (va < __data_start) { 124 vm_map_4k(kpgdir, (vaddr_t) va, kva_to_pa(va), KERNEL_RO); 125 va += PAGE_SIZE; 126 } 127 // map kernel data and the rest of ram RW 128 char *end = (void*) ((((uint32_t) __image_end) + 0x003FFFFF) & 0xFFC00000); 129 while (va < end) { 130 vm_map_4k(kpgdir, (vaddr_t) va, kva_to_pa(va), KERNEL_RW); 131 va += PAGE_SIZE; 132 } 133 // map as much as possible as 4MB megapages 134 while (va < __memory_top) { 135 vm_map_4m(kpgdir, (vaddr_t) va, kva_to_pa(va), KERNEL_RW); 136 va += 4*1024*1024; 137 } 138 // map mmio region 139 vm_map_4m(kpgdir, 0xF0000000, 0xF0000000, KERNEL_RW); 140 141 csr_write(CSR_SATP, SATP_MODE_SV32 | (kva_to_pa(kpgdir) >> 12)); 142 tlb_flush_all(); 143 144 start_user_program(); 145 } 146 147 status_t sys_thread_create(handle_t* out, void *entry, void *arg) { 148 return XOS_ERR_NOT_SUPPORTED; 149 } 150 151 void sys_xputc(unsigned ch) { 152 xputc(ch); 153 } 154 155 void sys_exit(int n) { 156 xprintf("\n[ user exit (status %d) ]\n", n); 157 for (;;) ; 158 } 159 160 void interrupt_handler(void) { 161 } 162 163 // if an exception occurs, dump register state and halt 164 void exception_handler(eframe_t *ef) { 165 xprintf("\n** SUPERVISOR EXCEPTION **\n"); 166 xprint_s_exception(ef); 167 xprintf("\nHALT\n"); 168 for (;;) ; 169 } 170 171 void panic(const char *msg) { 172 xprintf("\n** kernel panic: %s **\n", msg); 173 for (;;) ; 174 }