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:
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