From 856c7b273ef8d7f9c89d7e7c153d2547c0af7498 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Mon, 17 Mar 2025 16:01:12 +0200 Subject: [PATCH] Implement S-mode exceptions --- src/arch/riscv64/boot.zig | 4 ++ src/arch/riscv64/exception.zig | 104 +++++++++++++++++++++++++++++++++ src/arch/riscv64/regs.zig | 23 +++++++- src/arch/riscv64/vectors.S | 103 ++++++++++++++++++++++++++++++++ src/kernel.zig | 6 ++ 5 files changed, 237 insertions(+), 3 deletions(-) create mode 100644 src/arch/riscv64/exception.zig create mode 100644 src/arch/riscv64/vectors.S diff --git a/src/arch/riscv64/boot.zig b/src/arch/riscv64/boot.zig index 46abf8c..7b5ac75 100644 --- a/src/arch/riscv64/boot.zig +++ b/src/arch/riscv64/boot.zig @@ -6,6 +6,7 @@ const regs = @import("regs.zig"); const dtb = @import("../../util/dtb.zig"); const physMemory = @import("../../mem/phys.zig"); const arena = @import("../../arena.zig"); +const exception = @import("exception.zig"); const log = debug.log; const arch = kernel.arch; @@ -83,6 +84,9 @@ fn bspUpperEntry(realAddress: usize, unused: usize) callconv(.C) noreturn { rv64RelocateKernel(relOffset, relaStart, relaEnd); vmm.unmapEarly(); + // Setup exception handling + exception.init(); + debug.log.setWriteFn(&sbi.debugPrintByte); kernel.mem.PhysicalAddress.gVirtualizeBase = 0; kernel.mem.PhysicalAddress.gVirtualizeSize = vmm.virtualizeRange(); diff --git a/src/arch/riscv64/exception.zig b/src/arch/riscv64/exception.zig new file mode 100644 index 0000000..5f3714a --- /dev/null +++ b/src/arch/riscv64/exception.zig @@ -0,0 +1,104 @@ +const regs = @import("regs.zig"); +const debug = @import("../../debug.zig"); +const arch = @import("../../kernel.zig").arch; + +const log = debug.log; + +extern fn __rv64_exception_vectors() void; + +pub const ExceptionCause = enum(u64) { + misaligned_instruction = 0, + instruction_access_fault = 1, + illegal_instruction = 2, + breakpoint = 3, + load_address_misaligned = 4, + load_access_fault = 5, + store_address_misaligned = 6, + store_access_fault = 7, + ecall_umode = 8, + ecall_smode = 9, + ecall_mmode = 11, + instruction_page_fault = 12, + load_page_fault = 13, + store_page_fault = 15, + _, + + pub fn name(self: @This()) []const u8 { + return switch (self) { + .misaligned_instruction => "misaligned instruction", + .instruction_access_fault => "instruction access fault", + .illegal_instruction => "illegal instruction", + .breakpoint => "breakpoint", + .load_address_misaligned => "load address misaligned", + .load_access_fault => "load access fault", + .store_address_misaligned => "store address misaligned", + .store_access_fault => "store access fault", + .ecall_umode => "ecall umode", + .ecall_smode => "ecall smode", + .ecall_mmode => "ecall mmode", + .instruction_page_fault => "instruction page fault", + .load_page_fault => "load page fault", + .store_page_fault => "store page fault", + else => "", + }; + } +}; + +pub const ExceptionFrame = extern struct { + ra: usize, + gp: usize, + tN: [7]usize, + sN: [12]usize, + aN: [8]usize, + + pub fn dump(self: *const @This(), comptime level: log.Level) void { + log.writeln(level, " ra = 0x{x:016} gp = 0x{x:016}", .{ self.ra, self.gp }); + log.writeln(level, " t0 = 0x{x:016} t1 = 0x{x:016}", .{ self.tN[0], self.tN[1] }); + log.writeln(level, " t2 = 0x{x:016} t3 = 0x{x:016}", .{ self.tN[2], self.tN[3] }); + log.writeln(level, " t4 = 0x{x:016} t5 = 0x{x:016}", .{ self.tN[4], self.tN[5] }); + log.writeln(level, " t6 = 0x{x:016}", .{ self.tN[6] }); + log.writeln(level, " s0 = 0x{x:016} s1 = 0x{x:016}", .{ self.sN[0], self.sN[1] }); + log.writeln(level, " s2 = 0x{x:016} s1 = 0x{x:016}", .{ self.sN[2], self.sN[3] }); + log.writeln(level, " s4 = 0x{x:016} s6 = 0x{x:016}", .{ self.sN[4], self.sN[5] }); + log.writeln(level, " s6 = 0x{x:016} s7 = 0x{x:016}", .{ self.sN[6], self.sN[7] }); + log.writeln(level, " s8 = 0x{x:016} s9 = 0x{x:016}", .{ self.sN[8], self.sN[9] }); + log.writeln(level, "s10 = 0x{x:016} s11 = 0x{x:016}", .{ self.sN[10], self.sN[11] }); + } +}; + +pub fn init() void { + const base = @intFromPtr(&__rv64_exception_vectors); + + regs.STVEC.write(.{ + .MODE = .vectored, + .BASE = @intCast(base >> 2), + }); + + asm volatile ("":::"memory"); +} + +export fn rv64SmodeTrapGeneral(frame: *ExceptionFrame) callconv(.C) void { + const scause = regs.SCAUSE.read(); + if (scause.INTERRUPT) { + return rv64SmodeTrapInterrupt(frame); + } + const cause = @as(ExceptionCause, @enumFromInt(scause.CODE)); + const epc = regs.SEPC.get(); + const tval = regs.STVAL.get(); + + log.err("S-mode exception:", .{}); + log.err(" Cause: {s} (0x{x})", .{ cause.name(), scause.CODE }); + log.err(" stval = 0x{x:016} sepc = 0x{x:016}", .{ tval, epc }); + frame.dump(.err); + + @panic("Unhandled exception in S-mode"); +} + +export fn rv64SmodeTrapInterrupt(frame: *ExceptionFrame) callconv(.C) void { + _ = frame; + log.todo("rv64SmodeTrapInterrupt", .{}); +} + +comptime { + asm (@embedFile("vectors.S")); +} diff --git a/src/arch/riscv64/regs.zig b/src/arch/riscv64/regs.zig index 340e607..8283e30 100644 --- a/src/arch/riscv64/regs.zig +++ b/src/arch/riscv64/regs.zig @@ -1,5 +1,8 @@ fn makeRegister(comptime name: []const u8, comptime bits: type) type { - const repr = @typeInfo(bits).@"struct".backing_integer.?; + const repr = switch (@typeInfo(bits)) { + .@"struct" => |s| s.backing_integer.?, + else => bits, + }; return enum(repr) { pub fn set(value: repr) void { asm volatile ("csrw " ++ name ++ ", %[value]"::[value]"r"(value)); @@ -21,8 +24,6 @@ fn makeRegister(comptime name: []const u8, comptime bits: type) type { pub fn read() bits { return @bitCast(get()); } - - pub usingnamespace bits; }; } @@ -55,3 +56,19 @@ pub const SSTATUS = makeRegister("sstatus", packed struct(u64) { // 19..64 _3: u45 = 0, }); + +pub const STVEC = makeRegister("stvec", packed struct(u64) { + MODE: enum(u2) { + direct = 0, + vectored = 1, + } = .direct, + BASE: u62 = 0, +}); + +pub const SCAUSE = makeRegister("scause", packed struct(u64) { + CODE: u63 = 0, + INTERRUPT: bool = false +}); + +pub const STVAL = makeRegister("stval", u64); +pub const SEPC = makeRegister("sepc", u64); diff --git a/src/arch/riscv64/vectors.S b/src/arch/riscv64/vectors.S new file mode 100644 index 0000000..2ca96d1 --- /dev/null +++ b/src/arch/riscv64/vectors.S @@ -0,0 +1,103 @@ +.global __rv64_exception_vectors +.extern rv64SmodeTrapGeneral +.extern rv64SmodeTrapInterrupt + +// ra, gp, 7×tN, 12×sN, 8×aN +.set GP_REGS_SIZE, (2 + 7 + 12 + 8) * 8 +.set TRAP_CONTEXT_SIZE, (GP_REGS_SIZE) + +.macro SAVE_GP_REGS + sd ra, 0 * 8(sp) + sd gp, 1 * 8(sp) + + sd t0, 2 * 8(sp) + sd t1, 3 * 8(sp) + sd t2, 4 * 8(sp) + sd t3, 5 * 8(sp) + sd t4, 6 * 8(sp) + sd t5, 7 * 8(sp) + sd t6, 8 * 8(sp) + + sd s0, 9 * 8(sp) + sd s1, 10 * 8(sp) + sd s2, 11 * 8(sp) + sd s3, 12 * 8(sp) + sd s4, 13 * 8(sp) + sd s5, 14 * 8(sp) + sd s6, 15 * 8(sp) + sd s7, 16 * 8(sp) + sd s8, 17 * 8(sp) + sd s9, 18 * 8(sp) + sd s10, 19 * 8(sp) + sd s11, 20 * 8(sp) + + sd a0, 21 * 8(sp) + sd a1, 22 * 8(sp) + sd a2, 23 * 8(sp) + sd a3, 24 * 8(sp) + sd a4, 25 * 8(sp) + sd a5, 26 * 8(sp) + sd a6, 27 * 8(sp) + sd a7, 28 * 8(sp) +.endm + +.macro SMODE_TRAP n, handler +.type __rv64_smode_trap_\n, @function +__rv64_smode_trap_\n: + // TODO properly handle traps coming from U-mode + // TODO save CSRs + addi sp, sp, -(TRAP_CONTEXT_SIZE) + SAVE_GP_REGS + mv a0, sp + + call \handler + + // TODO return from exception + j . +.size __rv64_smode_trap_\n, . - __rv64_smode_trap_\n +.endm + +.pushsection .text +.option push +.option norvc + +.p2align 4 +.type __rv64_exception_vectors, %function +__rv64_exception_vectors: + j __rv64_smode_trap_0 + j __rv64_smode_trap_1 + j __rv64_smode_trap_2 + j __rv64_smode_trap_3 + j __rv64_smode_trap_4 + j __rv64_smode_trap_5 + j __rv64_smode_trap_6 + j __rv64_smode_trap_7 + j __rv64_smode_trap_8 + j __rv64_smode_trap_9 + j __rv64_smode_trap_10 + j __rv64_smode_trap_11 + j __rv64_smode_trap_12 + j __rv64_smode_trap_13 + j __rv64_smode_trap_14 + j __rv64_smode_trap_15 +.size __rv64_exception_vectors, . - __rv64_exception_vectors + +SMODE_TRAP 0, rv64SmodeTrapGeneral +SMODE_TRAP 1, rv64SmodeTrapInterrupt +SMODE_TRAP 2, rv64SmodeTrapInterrupt +SMODE_TRAP 3, rv64SmodeTrapInterrupt +SMODE_TRAP 4, rv64SmodeTrapInterrupt +SMODE_TRAP 5, rv64SmodeTrapInterrupt +SMODE_TRAP 6, rv64SmodeTrapInterrupt +SMODE_TRAP 7, rv64SmodeTrapInterrupt +SMODE_TRAP 8, rv64SmodeTrapInterrupt +SMODE_TRAP 9, rv64SmodeTrapInterrupt +SMODE_TRAP 10, rv64SmodeTrapInterrupt +SMODE_TRAP 11, rv64SmodeTrapInterrupt +SMODE_TRAP 12, rv64SmodeTrapInterrupt +SMODE_TRAP 13, rv64SmodeTrapInterrupt +SMODE_TRAP 14, rv64SmodeTrapInterrupt +SMODE_TRAP 15, rv64SmodeTrapInterrupt + +.option pop // norvc +.popsection // .text diff --git a/src/kernel.zig b/src/kernel.zig index ba0ee4d..ef82599 100644 --- a/src/kernel.zig +++ b/src/kernel.zig @@ -34,6 +34,12 @@ pub export fn kernel_main() callconv(.C) noreturn { thread.addThread(t); } + + const ptr: *const u32 = @ptrFromInt(0x1111111111111114); + const z = ptr.*; + _ = z; + while (true) {} + thread.enter(); arch.halt();