os-workshop

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit 8197d4ef5e42be2f6f69512c8af8289cacc56fe1
parent d8cc5466839979bde2cb1f00db5af6ba9794a238
Author: Brian Swetland <swetland@frotz.net>
Date:   Sun, 15 May 2022 11:00:37 -0700

example: ex25-kernel

- initial mmu setup for kernel at va 0xC0000000
- mmu setup for userspace program
- userspace program start
- syscall handling (exit and putc)

Diffstat:
Aexample/ex25-kernel.c | 169+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aexample/ex25-syscalls.S | 14++++++++++++++
Aexample/ex25-syscalls.h | 4++++
Aexample/ex25-user.c | 15+++++++++++++++
Aproject/ex25-kernel.app.mk | 10++++++++++
Aproject/ex25-user.app.mk | 8++++++++
6 files changed, 220 insertions(+), 0 deletions(-)

diff --git a/example/ex25-kernel.c b/example/ex25-kernel.c @@ -0,0 +1,169 @@ +// Copyright 2022, Brian Swetland <swetland@frotz.net> +// Licensed under the Apache License, Version 2.0 + +#include <hw/riscv.h> +#include <hw/context.h> +#include <hw/debug.h> +#include <hw/intrinsics.h> + +#include <hw/platform.h> +#include <hw/litex.h> + +#include <string.h> + +#include "ex25-syscalls.h" + +#define PAGE_SIZE 4096 + +typedef uint32_t paddr_t; +typedef uint32_t vaddr_t; + +// physical address to kernel virtual address +static inline void* pa_to_kva(paddr_t pa) { + return (void*) (pa + (0xC0000000 - 0x40000000)); +} +static inline paddr_t kva_to_pa(void *kva) { + return ((paddr_t) kva) - (0xC0000000 - 0x40000000); +} + +// trivial physical page allocator +paddr_t ppage_next = 0; + +paddr_t alloc_ppage(void) { + ppage_next -= PAGE_SIZE; + memset(pa_to_kva(ppage_next), 0, PAGE_SIZE); + return ppage_next; +} + +#define PTE_PA(pte) (((pte) & 0xFFFFFC00) << 2) + +uint32_t* pdir = 0; + +#define MAP_USER_RW (PTE_D|PTE_A|PTE_U|PTE_R|PTE_W|PTE_V) +#define MAP_USER_RX (PTE_D|PTE_A|PTE_U|PTE_R|PTE_X|PTE_V) +#define MAP_USER_RWX (PTE_D|PTE_A|PTE_U|PTE_R|PTE_W|PTE_X|PTE_V) + +int map_page(vaddr_t va, paddr_t pa, uint32_t flags) { + uint32_t idx1 = va >> 22; + uint32_t idx0 = (va >> 12) & 0x3FF; + uint32_t *ptbl; + + xprintf("map page va=0x%08x(%d,%d) -> pa=0x%08x\n", va, idx1, idx0, pa); + + uint32_t pte = pdir[idx1]; + if (pte & PTE_V) { // valid pgdir entry + if (pte & (PTE_X|PTE_W|PTE_R)) { + return -1; // 4MB megapage already here + } + ptbl = pa_to_kva(PTE_PA(pte)); + xprintf("old ptbl pa=0x%08x va=%p\n", PTE_PA(pte), ptbl); + } else { // no entry, allocate a pagetable + uint32_t ptbl_pa = alloc_ppage(); + ptbl = pa_to_kva(ptbl_pa); + xprintf("new ptbl pa=0x%08x va=%p\n", ptbl_pa, ptbl); + pdir[idx1] = (ptbl_pa >> 2) | PTE_V; + } + + pte = ptbl[idx0]; + if (pte & PTE_V) { + return -1; // 4KB page already here + } + + ptbl[idx0] = ((pa & 0xFFFFF000) >> 2) | (flags & 0x3FF); + + tlb_flush_all(); + + return 0; +} + +// load general registers from eframe +// save ef + sizeof(eframe_t) as kernel sp for next trap +// enter user mode at ef->pc +void user_mode_entry(eframe_t* ef); + +extern char __extra_start[]; +extern char __extra_end[]; + +void start_user_program(void) { + vaddr_t user_start = 0x10000000; + vaddr_t user_stack = 0x10400000; + + xprintf("user.bin %u bytes\n", (__extra_end - __extra_start)); + + // allocate 16KB (4 pages) for user text/data + map_page(user_start + 0*PAGE_SIZE, alloc_ppage(), MAP_USER_RWX); + map_page(user_start + 1*PAGE_SIZE, alloc_ppage(), MAP_USER_RWX); + map_page(user_start + 2*PAGE_SIZE, alloc_ppage(), MAP_USER_RWX); + map_page(user_start + 3*PAGE_SIZE, alloc_ppage(), MAP_USER_RWX); + + // allocate a 4KB (1 page) for user stack + map_page(user_stack - 1*PAGE_SIZE, alloc_ppage(), MAP_USER_RW); + + // allow S-MODE writes to U-MODE pages + csr_set(CSR_SSTATUS, SSTATUS_SUM); + + // copy embedded user.bin into user memory + memcpy((void*) user_start, __extra_start, __extra_end - __extra_start); + + csr_clr(CSR_SSTATUS, SSTATUS_SUM); + + // allocate a kernel stack page + // setup an eframe with the initial user register state + eframe_t* ef = pa_to_kva(alloc_ppage() + PAGE_SIZE - sizeof(eframe_t)); + ef->pc = user_start; + ef->sp = user_stack; + + // set previous privilege mode to user (0) + csr_clr(CSR_SSTATUS, SSTATUS_SPP); + + xprintf("\n[ starting user mode program ]\n\n"); + + user_mode_entry(ef); + // does not return +} + +void start(void *memtop) { + xprintf("Example 25 - kernel\n\n"); + + // set trap vector to trap_entry() in trap-entry-single.S + // it will call exception_handler() or interrupt_handler() + csr_write(CSR_STVEC, (uintptr_t) trap_entry); + + // start allocating from 2 pages down from top of ram + // since our boot stack starts at top of ram + // and the initial page directory is just below that + ppage_next = kva_to_pa(memtop) - 2 * PAGE_SIZE; + pdir = (void*) (memtop - 2 * PAGE_SIZE); + + start_user_program(); +} + +void interrupt_handler(void) { +} + +// if an exception occurs, dump register state and halt +void exception_handler(eframe_t *ef) { + uint32_t cause = csr_read(CSR_SCAUSE); + + if (cause == EXCn_ECALL_UMODE) { + switch (ef->t0) { // syscall number + case SYS_EXIT: + xprintf("\n[ user exit (status %d) ]\n", ef->a0); + for (;;) ; + case SYS_XPUTC: + xputc(ef->a0); + break; + default: + xprintf("\n[ invalid syscall %u ]\n", ef->t0); + for (;;) ; + } + ef->pc += 4; + return; + } + + xprintf("\n** SUPERVISOR EXCEPTION **\n"); + xprint_s_exception(ef); + xprintf("\nHALT\n"); + for (;;) ; +} + diff --git a/example/ex25-syscalls.S b/example/ex25-syscalls.S @@ -0,0 +1,14 @@ + +#include "ex25-syscalls.h" + +.globl exit +exit: + li t0, SYS_EXIT + ecall + ret + +.globl xputc +xputc: + li t0, SYS_XPUTC + ecall + ret diff --git a/example/ex25-syscalls.h b/example/ex25-syscalls.h @@ -0,0 +1,4 @@ +#pragma once + +#define SYS_EXIT 0 +#define SYS_XPUTC 1 diff --git a/example/ex25-user.c b/example/ex25-user.c @@ -0,0 +1,15 @@ +// Copyright 2022, Brian Swetland <swetland@frotz.net> +// Licensed under the Apache License, Version 2.0 + +#include <hw/debug.h> + +void xputs(const char* s) { + while (*s) { + xputc(*s++); + } +} + +void start(void) { + xprintf("Hello, User Mode!\n"); + exit(42); +} diff --git a/project/ex25-kernel.app.mk b/project/ex25-kernel.app.mk @@ -0,0 +1,10 @@ + +MOD_NAME := ex25-kernel +MOD_SRC := hw/src/start.mmu.S hw/src/trap-entry-dual-stack.S +MOD_SRC += example/ex25-kernel.c +MOD_SRC += hw/src/context-switch.S +MOD_SRC += hw/src/debug-printf.c hw/src/debug-io.c hw/src/print-exception.c +MOD_LIB := c +MOD_EXT := out/ex25-user.bin +MOD_LDSCRIPT := make/kernel.ram.ld +include make/app.mk diff --git a/project/ex25-user.app.mk b/project/ex25-user.app.mk @@ -0,0 +1,8 @@ + +MOD_NAME := ex25-user +MOD_SRC := hw/src/start.S +MOD_SRC += example/ex25-user.c example/ex25-syscalls.S +MOD_SRC += hw/src/debug-printf.c +MOD_LIB := c +MOD_LDSCRIPT := make/user.ram.ld +include make/app.mk