os-workshop

same materials and sample source for RV32 OS projects
git clone http://frotz.net/git/os-workshop.git
Log | Files | Refs

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