commit f6fa58cce08278734144b46f83c95cab99ee4f42
parent 059eaa4cce639f1a77e5412c6c24040422ae7c9a
Author: Brian Swetland <swetland@frotz.net>
Date: Sun, 24 Apr 2022 12:25:09 -0700
bios: tiny bios to handle startup in machine mode
- install a trap handler to dump state on exceptions
- switch to supervisor mode before starting the app/kernel
- bios at 0x80000000 (start of ram, up to 32k)
- app/kernel at 0x80004000 (just above the bios)
- adjust link scripts, qemu arguments, etc
- todo: delegate user mode faults to supervisor mode
Diffstat:
5 files changed, 175 insertions(+), 2 deletions(-)
diff --git a/Makefile b/Makefile
@@ -10,8 +10,9 @@ V := @
# defaults for Ubuntu, can override with local.mk
XTOOLCHAIN ?= /usr/bin/riscv64-unknown-elf-
QEMU ?= /usr/bin/qemu-system-riscv32
+QEMUBIOS := out/bios.elf
-QFLAGS := -machine virt -bios none -nographic
+QFLAGS := -machine virt -bios $(QEMUBIOS) -nographic
QFLAGS.GDB := $(QFLAGS) -gdb tcp::7777 -S
ifeq ($(wildcard $(XTOOLCHAIN)gcc),)
diff --git a/bios/bios.c b/bios/bios.c
@@ -0,0 +1,104 @@
+// Copyright 2022, Brian Swetland <swetland@frotz.net>
+// Licensed under the Apache License, Version 2.0
+
+#include <hw/riscv.h>
+#include <hw/debug.h>
+
+#define SVC_ENTRY 0x80004000
+
+extern void mach_exception_entry(void);
+
+const char* cause(uint32_t n) {
+ if (n & 0x80000000U) {
+ return "Interrupt";
+ } else {
+ switch(n & 0x7FFFFFFFU) {
+ case 0: return "Instruction Address Misaligned";
+ case 1: return "Instruction Address Fault";
+ case 2: return "Illegal Instruction";
+ case 3: return "Breakpoint";
+ case 4: return "Load Address Misaligned";
+ case 5: return "Load Address Fault";
+ case 6: return "Store Address Misaligned";
+ case 7: return "Store Address Fault";
+ case 8: return "User Mode ECALL";
+ case 9: return "Supervisor Mode ECALL";
+ case 10: return "Machine Mode ECALL";
+ case 12: return "Instruction Page Fault";
+ case 13: return "Load Page Fault";
+ case 14: return "Store Page Fault";
+ }
+ }
+ return "Unknown";
+}
+
+const char* mode(uint32_t n) {
+ switch (n) {
+ case 0: return "User";
+ case 1: return "Supervisor";
+ case 3: return "Machine Mode";
+ default: return "???";
+ }
+}
+
+void mach_exception_handler(uint32_t regs[32]) {
+ uint32_t n = csr_read(CSR_MCAUSE);
+ xprintf("\n** MACHINE EXCEPTION %08x %08x\n", n, csr_read(CSR_MTVAL));
+ xprintf("** %s\n", cause(n));
+ n = csr_read(CSR_MSTATUS);
+ xprintf("** in %s mode\n\n", mode((n >> MSTATUS_MPP_SHIFT) & 3));
+
+ xprintf("pc %08x ra %08x sp %08x gp %08x\n",
+ regs[0], regs[1], regs[2], regs[3]);
+ xprintf("tp %08x t0 %08x t1 %08x t2 %08x\n",
+ regs[4], regs[5], regs[6], regs[7]);
+ xprintf("fp %08x s1 %08x a0 %08x a1 %08x\n",
+ regs[8], regs[9], regs[10], regs[11]);
+ xprintf("a2 %08x a3 %08x a4 %08x a5 %08x\n",
+ regs[12], regs[13], regs[14], regs[15]);
+ xprintf("\n** HALT\n");
+ for (;;) ;
+}
+
+void enter_mode_u(uint32_t a0, uint32_t a1, uint32_t pc, uint32_t sp);
+void enter_mode_s(uint32_t a0, uint32_t a1, uint32_t pc, uint32_t sp);
+
+#if 0
+void user_start(uint32_t user_pc, uint32_t user_sp) {
+ xprintf("Hello, User Mode %08x %08x\n", user_pc, user_sp);
+
+ // illegal write from user mode
+ // csr_write(CSR_MEPC, 0x42);
+
+ // syscall
+ asm volatile ("ecall");
+
+ for (;;) ;
+}
+#endif
+
+#define INT_LIST (INTb_SVC_SW|INTb_SVC_TIMER|INTb_SVC_EXTERN)
+#define EXC_LIST (EXCb_ECALL_UMODE)
+
+void start(uint32_t hartid, uint32_t fdt) {
+ xprintf("** Frobozz Magic BIOS v0.1 **\n");
+
+ // delegate interrupts and exceptions
+ //xprintf("MIDELEG %08x MEDELEG %08x\n",
+ // csr_read(CSR_MIDELEG), csr_read(CSR_MEDELEG));
+ csr_set(CSR_MIDELEG, INT_LIST);
+ csr_set(CSR_MEDELEG, EXC_LIST);
+ //xprintf("MIDELEG %08x MEDELEG %08x\n\n",
+ // csr_read(CSR_MIDELEG), csr_read(CSR_MEDELEG));
+
+ // set previous status to S_MODE, previous interrupt status ENABLED
+ csr_write(CSR_MSTATUS, (PRIV_S << MSTATUS_MPP_SHIFT) | MSTATUS_MPIE);
+ //xprintf("MSTATUS %08x\n", csr_read(CSR_MSTATUS));
+
+ // set mach exception vector and stack pointer
+ csr_write(CSR_MTVEC, (uintptr_t) mach_exception_entry);
+ csr_write(CSR_MSCRATCH, SVC_ENTRY);
+
+ enter_mode_s(hartid, fdt, SVC_ENTRY, 0);
+}
+
diff --git a/bios/entry.S b/bios/entry.S
@@ -0,0 +1,68 @@
+// Copyright 2022, Brian Swetland <swetland@frotz.net>
+// Licensed under the Apache License, Version 2.0
+
+#include <hw/riscv.h>
+
+.globl mach_exception_entry
+mach_exception_entry:
+ // swap active sp with value in MSCRATCH (exception stack)
+ // interupts have been disabled on exception entry
+ csrrw sp, CSR_MSCRATCH, sp
+
+ // save previous registers to stack
+ addi sp, sp, - 32 * 4
+ sw x1, 0x04(sp)
+ sw x3, 0x0C(sp)
+ sw x4, 0x10(sp)
+ sw x5, 0x14(sp)
+ sw x6, 0x18(sp)
+ sw x7, 0x1C(sp)
+ sw x8, 0x20(sp)
+ sw x9, 0x24(sp)
+ sw x10, 0x28(sp)
+ sw x11, 0x2C(sp)
+ sw x12, 0x30(sp)
+ sw x13, 0x34(sp)
+ sw x14, 0x38(sp)
+ sw x15, 0x3C(sp)
+ sw x16, 0x40(sp)
+ sw x17, 0x44(sp)
+ sw x18, 0x48(sp)
+ sw x19, 0x4C(sp)
+ sw x20, 0x50(sp)
+ sw x21, 0x54(sp)
+ sw x22, 0x58(sp)
+ sw x23, 0x5C(sp)
+ sw x24, 0x60(sp)
+ sw x25, 0x64(sp)
+ sw x26, 0x68(sp)
+ sw x27, 0x6C(sp)
+ sw x28, 0x70(sp)
+ sw x29, 0x74(sp)
+ sw x30, 0x78(sp)
+ sw x31, 0x7C(sp)
+ mv a0, sp
+
+ // save previous pc (hw stashed it in MEPC)
+ csrr t0, CSR_MEPC
+ sw t0, 0x00(sp)
+
+ // save previous sp (we stashed it in MSCRATCH)
+ csrr t0, CSR_MSCRATCH
+ sw t0, 0x08(sp)
+
+ j mach_exception_handler
+ j .
+
+.globl enter_mode_u
+enter_mode_u: // (a0, a1, user_pc, user_sp)
+ csrw CSR_SEPC, a2
+ mv sp, a3
+ sret
+
+.globl enter_mode_s
+enter_mode_s: // (a0, a1, svc_pc, svc_sp)
+ csrw CSR_MEPC, a2
+ mv sp, a3
+ mret
+
diff --git a/hw/simple.ld b/hw/bios.ld
diff --git a/hw/simple.ld b/hw/simple.ld
@@ -3,7 +3,7 @@ ENTRY(_start)
SECTIONS
{
- . = 0x80000000;
+ . = 0x80004000;
.start : { *(.start) }
.text : { *(.text*) }
.rodata : { *(.rodata*) }