os-workshop

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

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

glue code for more real kernel mode examples

Diffstat:
Mhw/inc/hw/context.h | 4++--
Ahw/src/start.mmu.S | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahw/src/trap-entry-dual-stack.S | 149+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 250 insertions(+), 2 deletions(-)

diff --git a/hw/inc/hw/context.h b/hw/inc/hw/context.h @@ -28,7 +28,7 @@ typedef struct cframe { // tframe - Trap Frame // contains the 16 caller-save and temporary registers -// as well as the pc +// as well as the pc (and maybe the sp) typedef struct tframe { uint32_t ra; uint32_t t0; @@ -48,7 +48,7 @@ typedef struct tframe { uint32_t a7; uint32_t pc; - uint32_t _0; + uint32_t sp; uint32_t _1; uint32_t _2; } tframe_t; diff --git a/hw/src/start.mmu.S b/hw/src/start.mmu.S @@ -0,0 +1,99 @@ +// Copyright 2022, Brian Swetland <swetland@frotz.net> +// Licensed under the Apache License, Version 2.0 + +#include <hw/riscv.h> + +// We start running at the physical start of memory (0x40008000) +// We are linked at the virtual address 0xC0000000 + +// So we must first setup an initial page table that maps +// ram to 0xC0000000, maps the first megapage of ram +// to 0x40000000 (so we don't pull the rug out from under us +// when we switch on the MMU), and maps the start of MMIO +// space at 0xF0000000 so we can still talk to the serial port + +#define PTE_BITS_RWX (PTE_A | PTE_D | PTE_X | PTE_W | PTE_R | PTE_V) +#define PTE_BITS_RW (PTE_A | PTE_D | PTE_W | PTE_R | PTE_V) + +.globl _start +_start: + // stack is at top of memory + lw sp, stack_top + + // adjust it from vaddr to paddr + li t0, (0xC0000000 - 0x40000000) + sub t3, sp, t0 + + // use the next to last page (before the stack) + // for a bootstrap MMU page directory + li t4, 4096 + sub t1, t3, t4 // t1 = end of pagetable + sub t0, t1, t4 // t0 = start of pagetable + mv t2, t0 // t2 = pagetable + +ptbl_zero_loop: + sw zero, 0(t0) + addi t0, t0, 4 + bne t0, t1, ptbl_zero_loop + + // identity map a 4MB page where we're running now + li t0, (0x40000000 >> 2) | PTE_BITS_RWX + li t1, (0x40000000 >> 20) // offset of pte + add t1, t2, t1 + sw t0, 0(t1) + + // map ram at 0xC0000000 where the kernel is linked + li t1, (0xC0000000 >> 20) // offset of pte at start of ram + srli t3, sp, 20 // offset of pte at end of ram + li t4, (0x00400000 >> 2) // 4MB ppn increment +ptbl_map_loop: + add t5, t2, t1 // t5 = pagetable + pte offset + sw t0, 0(t5) // store pte + add t1, t1, 4 // increment offset to next pte + add t0, t0, t4 // increment ppn to next megapage + bne t1, t3, ptbl_map_loop + + // identity map the first 4MB of the mmio space + li t0, (0xF0000000 >> 2) | PTE_BITS_RW + li t1, (0xF0000000 >> 20) // offset of pte + add t1, t2, t1 + sw t0, 0(t1) + + // enable paging + srli t0, t2, 12 + li t1, SATP_MODE_SV32 + or t0, t0, t1 + csrw satp, t0 + + // flush TLB + sfence.vma zero,zero + + nop + nop + nop + nop + nop + + // absolute jump to vaddr entrypoint + lw t0, _vstart_addr + jr t0 + +_vstart: + // zero BSS + la t0, __bss_start + la t1, __bss_end +zero_loop: + beq t0, t1, zero_done + sw zero, 0(t0) + add t0, t0, 4 + j zero_loop +zero_done: + + // jump to kernel entrypoint + mv a0, sp + j start + +stack_top: + .word __stack_top +_vstart_addr: + .word _vstart diff --git a/hw/src/trap-entry-dual-stack.S b/hw/src/trap-entry-dual-stack.S @@ -0,0 +1,149 @@ +// Copyright 2022, Brian Swetland <swetland@frotz.net> +// Licensed under the Apache License, Version 2.0 + +// This file provides assembly glue for handling traps +// (interrupts or exceptions) and safely saving and +// restoring the processor state of the code running +// when the trap happened. + +// It assume a dual stack setup -- when an interrupt +// or exception occurs, a kernel stack will be used to +// save the state -- whether or not the active user +// stack is valid will not matter. + +// It will call into interrupt_handler() or exception_handler() +// to process the interrupt or exception. + +// For interrupts, only the registers that must be saved +// are saved (the others are callee-save so the C code +// called into will save them if necessary). + +// For exceptions, ALL registers are saved so that the +// exception handler my inspect, display, or modify them +// as necesary. + +// hw/context.h defines structures tframe_t and eframe_t +// that correspond to how the registers are saved on the +// stack + +.global trap_entry +trap_entry: + // swap the user sp with the kernel sp + // stored in SSCRATCH + csrrw sp, sscratch, sp + + // reserve stack space for a full eframe_t + addi sp, sp, - (32 * 4) + + // save caller-save registers (tframe_t) + sw ra, 0x00(sp) + sw t0, 0x04(sp) + sw t1, 0x08(sp) + sw t2, 0x0C(sp) + sw t3, 0x10(sp) + sw t4, 0x14(sp) + sw t5, 0x18(sp) + sw t6, 0x1C(sp) + sw a0, 0x20(sp) + sw a1, 0x24(sp) + sw a2, 0x28(sp) + sw a3, 0x2C(sp) + sw a4, 0x30(sp) + sw a5, 0x34(sp) + sw a6, 0x38(sp) + sw a7, 0x3C(sp) + + // save user pc which hw stashed in SEPC + // and user sp which we stashed in SSCRATCH + csrr t0, sepc + csrr t1, sscratch + sw t0, 0x40(sp) + sw t1, 0x44(sp) + + // if the scause high bit is clear, it is an exception + csrr t0, scause + bge t0, zero, exception_entry + + // otherwise it is an interrupt, call interrupt_handler() + jal interrupt_handler + +.globl trap_exit +trap_exit: + // restore kernel sp to sscratch + addi t0, sp, (32 * 4) + csrw sscratch, t0 + + // user sp goes into sepc for sret + lw t0, 0x40(sp) + csrw sepc, t0 + + // restore the caller-save registers + lw ra, 0x00(sp) + lw t0, 0x04(sp) + lw t1, 0x08(sp) + lw t2, 0x0C(sp) + lw t3, 0x10(sp) + lw t4, 0x14(sp) + lw t5, 0x18(sp) + lw t6, 0x1C(sp) + lw a0, 0x20(sp) + lw a1, 0x24(sp) + lw a2, 0x28(sp) + lw a3, 0x2C(sp) + lw a4, 0x30(sp) + lw a5, 0x34(sp) + lw a6, 0x38(sp) + lw a7, 0x3C(sp) + + // finally restore the user sp + lw sp, 0x44(sp) + + // and return from trap + sret + +exception_entry: + // save the callee-save registers (cframe_t) + // user pc and sp already stored at 0x40(sp), 0x44(sp) + sw gp, 0x48(sp) + sw tp, 0x4C(sp) + sw s0, 0x50(sp) + sw s1, 0x54(sp) + sw s2, 0x58(sp) + sw s3, 0x5C(sp) + sw s4, 0x60(sp) + sw s5, 0x64(sp) + sw s6, 0x68(sp) + sw s7, 0x6C(sp) + sw s8, 0x70(sp) + sw s9, 0x74(sp) + sw s10, 0x78(sp) + sw s11, 0x7C(sp) + + // call exception_handler() passing eframe_t* + // as the first argument + mv a0, sp + jal exception_handler + +.globl exception_exit +exception_exit: + lw gp, 0x48(sp) + lw tp, 0x4C(sp) + lw s0, 0x50(sp) + lw s1, 0x54(sp) + lw s2, 0x58(sp) + lw s3, 0x5C(sp) + lw s4, 0x60(sp) + lw s5, 0x64(sp) + lw s6, 0x68(sp) + lw s7, 0x6C(sp) + lw s8, 0x70(sp) + lw s9, 0x74(sp) + lw s10, 0x78(sp) + lw s11, 0x7C(sp) + + j trap_exit + +.globl user_mode_entry +user_mode_entry: + mv sp, a0 + j exception_exit