ex25-kernel.c (6466B)
1 // Copyright 2022, Brian Swetland <swetland@frotz.net> 2 // Licensed under the Apache License, Version 2.0 3 4 #include <hw/riscv.h> 5 #include <hw/context.h> 6 #include <hw/debug.h> 7 #include <hw/intrinsics.h> 8 9 #include <hw/platform.h> 10 #include <hw/litex.h> 11 12 #include <string.h> 13 14 #include "ex25-syscalls.h" 15 16 #define PAGE_SIZE 4096 17 18 typedef uint32_t paddr_t; 19 typedef uint32_t vaddr_t; 20 21 // physical address to kernel virtual address 22 static inline void* pa_to_kva(paddr_t pa) { 23 return (void*) (pa + (0xC0000000 - 0x40000000)); 24 } 25 static inline paddr_t kva_to_pa(void *kva) { 26 return ((paddr_t) kva) - (0xC0000000 - 0x40000000); 27 } 28 29 // trivial physical page allocator 30 paddr_t ppage_next = 0; 31 32 paddr_t alloc_ppage(void) { 33 ppage_next -= PAGE_SIZE; 34 memset(pa_to_kva(ppage_next), 0, PAGE_SIZE); 35 return ppage_next; 36 } 37 38 #define PTE_PA(pte) (((pte) & 0xFFFFFC00) << 2) 39 40 uint32_t* pdir = 0; 41 42 #define USER_RW (PTE_D|PTE_A|PTE_U|PTE_R|PTE_W|PTE_V) 43 #define USER_RX (PTE_D|PTE_A|PTE_U|PTE_R|PTE_X|PTE_V) 44 #define USER_RWX (PTE_D|PTE_A|PTE_U|PTE_R|PTE_W|PTE_X|PTE_V) 45 46 #define KERNEL_RO (PTE_D|PTE_A|PTE_R|PTE_V) 47 #define KERNEL_RW (PTE_D|PTE_A|PTE_R|PTE_W|PTE_V) 48 #define KERNEL_RX (PTE_D|PTE_A|PTE_R|PTE_X|PTE_V) 49 50 int map_page(vaddr_t va, paddr_t pa, uint32_t flags) { 51 uint32_t idx1 = va >> 22; 52 uint32_t idx0 = (va >> 12) & 0x3FF; 53 uint32_t *ptbl; 54 55 //xprintf("map page va=0x%08x(%d,%d) -> pa=0x%08x (4K)\n", va, idx1, idx0, pa); 56 uint32_t pte = pdir[idx1]; 57 if (pte & PTE_V) { // valid pgdir entry 58 if (pte & (PTE_X|PTE_W|PTE_R)) { 59 return -1; // 4MB megapage already here 60 } 61 ptbl = pa_to_kva(PTE_PA(pte)); 62 //xprintf("old ptbl pa=0x%08x va=%p\n", PTE_PA(pte), ptbl); 63 } else { // no entry, allocate a pagetable 64 uint32_t ptbl_pa = alloc_ppage(); 65 ptbl = pa_to_kva(ptbl_pa); 66 //xprintf("new ptbl pa=0x%08x va=%p\n", ptbl_pa, ptbl); 67 pdir[idx1] = (ptbl_pa >> 2) | PTE_V; 68 } 69 70 pte = ptbl[idx0]; 71 if (pte & PTE_V) { 72 return -1; // 4KB page already here 73 } 74 75 ptbl[idx0] = ((pa & 0xFFFFF000) >> 2) | (flags & 0x3FF); 76 77 tlb_flush_all(); 78 79 return 0; 80 } 81 82 int map_page_4m(vaddr_t va, paddr_t pa, uint32_t flags) { 83 uint32_t idx1 = va >> 22; 84 85 //xprintf("map page va=0x%08x(%d) -> pa=0x%08x (4M)\n", va, idx1, pa); 86 uint32_t pte = pdir[idx1]; 87 if (pte & PTE_V) { // valid pgdir entry 88 return -1; 89 } 90 pdir[idx1] = ((pa & 0xFFFFF000) >> 2) | (flags & 0x3FF); 91 92 tlb_flush_all(); 93 return 0; 94 } 95 96 // load general registers from eframe 97 // save ef + sizeof(eframe_t) as kernel sp for next trap 98 // enter user mode at ef->pc 99 void user_mode_entry(eframe_t* ef); 100 101 extern char __extra_start[]; 102 extern char __extra_end[]; 103 104 void start_user_program(void) { 105 vaddr_t user_start = 0x10000000; 106 vaddr_t user_stack = 0x10400000; 107 108 xprintf("user.bin %u bytes\n", (__extra_end - __extra_start)); 109 110 // allocate 16KB (4 pages) for user text/data 111 map_page(user_start + 0*PAGE_SIZE, alloc_ppage(), USER_RWX); 112 map_page(user_start + 1*PAGE_SIZE, alloc_ppage(), USER_RWX); 113 map_page(user_start + 2*PAGE_SIZE, alloc_ppage(), USER_RWX); 114 map_page(user_start + 3*PAGE_SIZE, alloc_ppage(), USER_RWX); 115 116 // allocate a 4KB (1 page) for user stack 117 map_page(user_stack - 1*PAGE_SIZE, alloc_ppage(), USER_RW); 118 119 // allow S-MODE writes to U-MODE pages 120 csr_set(CSR_SSTATUS, SSTATUS_SUM); 121 122 // copy embedded user.bin into user memory 123 memcpy((void*) user_start, __extra_start, __extra_end - __extra_start); 124 125 csr_clr(CSR_SSTATUS, SSTATUS_SUM); 126 127 // allocate a kernel stack page 128 // setup an eframe with the initial user register state 129 eframe_t* ef = pa_to_kva(alloc_ppage() + PAGE_SIZE - sizeof(eframe_t)); 130 ef->pc = user_start; 131 ef->sp = user_stack; 132 133 // set previous privilege mode to user (0) 134 csr_clr(CSR_SSTATUS, SSTATUS_SPP); 135 136 xprintf("\n[ starting user mode program ]\n\n"); 137 138 user_mode_entry(ef); 139 // does not return 140 } 141 142 extern char __text_start[]; 143 extern char __rodata_start[]; 144 extern char __data_start[]; 145 extern char __bss_start[]; 146 extern char __bss_end[]; 147 extern char __image_end[]; 148 extern char __memory_top[]; 149 150 // On entry: 151 // SP = __memory_top (top of ram) 152 // SATP points at __memory_top - 8K 153 154 void start(void) { 155 xprintf("Example 25 - kernel\n\n"); 156 157 xprintf("text %p %u\n", __text_start, __rodata_start - __text_start); 158 xprintf("rodata %p %u\n", __rodata_start, __data_start - __rodata_start); 159 xprintf("data %p %u\n", __data_start, __bss_start - __data_start); 160 xprintf("bss %p %u\n", __bss_start, __bss_end - __bss_start); 161 xprintf("free %p %u\n", __image_end, __memory_top - __image_end); 162 xprintf("memtop %p\n", __memory_top); 163 164 // set trap vector to trap_entry() in trap-entry-single.S 165 // it will call exception_handler() or interrupt_handler() 166 csr_write(CSR_STVEC, (uintptr_t) trap_entry); 167 168 // the topmost page is the boot stack 169 // the second topmost page is the boot page directory 170 171 // setup page allocator to start grabbing pages from the top of ram 172 ppage_next = kva_to_pa(__memory_top) - 2 * PAGE_SIZE; 173 174 #if 0 175 // use the boot page table 176 pdir = (void*) __memory_top - 2 * PAGE_SIZE; 177 #else 178 // build a more correct page table 179 pdir = pa_to_kva(alloc_ppage()); 180 181 char *va = __text_start; 182 // map kernel text RX 183 while (va < __rodata_start) { 184 map_page((vaddr_t) va, kva_to_pa(va), KERNEL_RX); 185 va += PAGE_SIZE; 186 } 187 // map kernel rodata RO 188 while (va < __data_start) { 189 map_page((vaddr_t) va, kva_to_pa(va), KERNEL_RO); 190 va += PAGE_SIZE; 191 } 192 // map kernel data and the rest of ram RW 193 char *end = (void*) ((((uint32_t) __image_end) + 0x003FFFFF) & 0xFFC00000); 194 while (va < end) { 195 map_page((vaddr_t) va, kva_to_pa(va), KERNEL_RW); 196 va += PAGE_SIZE; 197 } 198 // map as much as possible as 4MB megapages 199 while (va < __memory_top) { 200 map_page_4m((vaddr_t) va, kva_to_pa(va), KERNEL_RW); 201 va += 4*1024*1024; 202 } 203 // map mmio region 204 map_page_4m(0xF0000000, 0xF0000000, KERNEL_RW); 205 206 csr_write(CSR_SATP, SATP_MODE_SV32 | (kva_to_pa(pdir) >> 12)); 207 tlb_flush_all(); 208 #endif 209 210 start_user_program(); 211 } 212 213 void interrupt_handler(void) { 214 } 215 216 // if an exception occurs, dump register state and halt 217 void exception_handler(eframe_t *ef) { 218 uint32_t cause = csr_read(CSR_SCAUSE); 219 220 if (cause == EXCn_ECALL_UMODE) { 221 switch (ef->t0) { // syscall number 222 case SYS_EXIT: 223 xprintf("\n[ user exit (status %d) ]\n", ef->a0); 224 for (;;) ; 225 case SYS_XPUTC: 226 xputc(ef->a0); 227 break; 228 default: 229 xprintf("\n[ invalid syscall %u ]\n", ef->t0); 230 for (;;) ; 231 } 232 ef->pc += 4; 233 return; 234 } 235 236 xprintf("\n** SUPERVISOR EXCEPTION **\n"); 237 xprint_s_exception(ef); 238 xprintf("\nHALT\n"); 239 for (;;) ; 240 } 241