From 7cb116229f2bde4deb9721b442d2d5d9d5202b84 Mon Sep 17 00:00:00 2001 From: Lorenzo Torres Date: Mon, 2 Feb 2026 14:54:03 +0100 Subject: [PATCH] added panic handling --- src/debug.zig | 203 ++++++++++++++++++++++++++++++++++++++++ src/main.zig | 4 + src/riscv/PageTable.zig | 2 +- 3 files changed, 208 insertions(+), 1 deletion(-) diff --git a/src/debug.zig b/src/debug.zig index 0450d49..19c0433 100644 --- a/src/debug.zig +++ b/src/debug.zig @@ -1,3 +1,4 @@ +const std = @import("std"); const Console = @import("drivers/Console.zig"); var console: Console = undefined; @@ -9,3 +10,205 @@ pub fn init(c: Console) void { pub fn print(comptime s: []const u8, args: anytype) void { console.print(s, args); } + +pub fn captureStackTrace(buffer: []usize) usize { + var count: usize = 0; + var current_fp = @frameAddress(); + + while (count < buffer.len) { + const ra_ptr: *const usize = @ptrFromInt(current_fp - 8); + const prev_fp_ptr: *const usize = @ptrFromInt(current_fp - 16); + + const ra = ra_ptr.*; + const prev_fp = prev_fp_ptr.*; + + if (ra == 0 or prev_fp == 0) break; + + buffer[count] = ra; + count += 1; + + if (prev_fp <= current_fp) break; + current_fp = prev_fp; + } + return count; +} + +fn dumpRegisters() void { + const sstatus = asm volatile ("csrr %[ret], sstatus" : [ret] "=r" (-> usize)); + const scause = asm volatile ("csrr %[ret], scause" : [ret] "=r" (-> usize)); + const sepc = asm volatile ("csrr %[ret], sepc" : [ret] "=r" (-> usize)); + const stval = asm volatile ("csrr %[ret], stval" : [ret] "=r" (-> usize)); + const satp = asm volatile ("csrr %[ret], satp" : [ret] "=r" (-> usize)); + + const sp = asm volatile ("mv %[ret], sp" : [ret] "=r" (-> usize)); + const fp = asm volatile ("mv %[ret], s0" : [ret] "=r" (-> usize)); + const ra = asm volatile ("mv %[ret], ra" : [ret] "=r" (-> usize)); + const gp = asm volatile ("mv %[ret], gp" : [ret] "=r" (-> usize)); + const tp = asm volatile ("mv %[ret], tp" : [ret] "=r" (-> usize)); + + print("\n\x1B[33m=== REGISTER DUMP ===\x1B[m\n", .{}); + print(" PC(ra): 0x{x:0>16} SP: 0x{x:0>16} FP: 0x{x:0>16}\n", .{ ra, sp, fp }); + print(" GP: 0x{x:0>16} TP: 0x{x:0>16}\n", .{ gp, tp }); + + const mode = satp >> 60; + const ppn = satp & 0xFFFFFFFFFFF; + const mode_str = switch (mode) { + 8 => "Sv39", + 9 => "Sv48", + 10 => "Sv57", + 11 => "Sv64", + 0 => "Bare", + else => "???", + }; + print(" SATP: 0x{x:0>16} (Mode: {s}, PPN: 0x{x})\n", .{ satp, mode_str, ppn }); + + print("\n\x1B[33m=== TRAP INFO ===\x1B[m\n", .{}); + print(" SSTATUS: 0x{x:0>16} SCAUSE: 0x{x:0>16}\n", .{ sstatus, scause }); + print(" SEPC: 0x{x:0>16} STVAL: 0x{x:0>16}\n", .{ sepc, stval }); + + const is_interrupt = (scause >> 63) == 1; + const code = scause & 0x7FFFFFFFFFFFFFFF; + if (is_interrupt) { + print(" Cause: Interrupt (Code {d})\n", .{code}); + } else { + print(" Cause: Exception (Code {d})\n", .{code}); + } +} + +fn printPanic(comptime fmt: []const u8, args: anytype) noreturn { + asm volatile ("csrci sstatus, 2"); + + print("\n\x1B[41;37m!!! KERNEL PANIC !!!\x1B[m\n", .{}); + print("\x1B[31mReason: \x1B[m", .{}); + print(fmt, args); + print("\n", .{}); + + dumpRegisters(); + + var address_buffer: [32]usize = undefined; + const count = captureStackTrace(&address_buffer); + + print("\n\x1B[33m=== STACK TRACE ===\x1B[m\n", .{}); + + for (address_buffer[0..count]) |addr| { + print("0x{x} ", .{addr}); + } + print("\x1B[m\n", .{}); + + for (address_buffer[0..count], 0..) |addr, i| { + print(" [{d: >2}] 0x{x:0>16}\n", .{ i, addr }); + } + + while (true) { + asm volatile ("wfi"); + } +} + +pub const KernelPanic = struct { + pub fn call(message: []const u8, addr: ?usize) noreturn { + _ = addr; + printPanic("{s}", .{message}); + } + + pub fn sentinelMismatch(expected: anytype, found: @TypeOf(expected)) noreturn { + @branchHint(.cold); + printPanic("Sentinel mismatch, expected {any}, found {any}", .{ expected, found }); + } + pub fn unwrapError(err: anyerror) noreturn { + @branchHint(.cold); + printPanic("attempt to unwrap error: {s}", .{@errorName(err)}); + } + pub fn outOfBounds(index: usize, len: usize) noreturn { + @branchHint(.cold); + printPanic("index out of bounds: index {d}, len {d}", .{ index, len }); + } + pub fn startGreaterThanEnd(start: usize, end: usize) noreturn { + @branchHint(.cold); + printPanic("start index {d} is larger than end index {d}", .{ start, end }); + } + pub fn inactiveUnionField(active: anytype, accessed: @TypeOf(active)) noreturn { + @branchHint(.cold); + printPanic("access of union field '{s}' while field '{s}' is active", .{ + @tagName(accessed), @tagName(active), + }); + } + pub fn sliceCastLenRemainder(src_len: usize) noreturn { + @branchHint(.cold); + printPanic("slice length '{d}' does not divide exactly into destination elements", .{src_len}); + } + pub fn reachedUnreachable() noreturn { + @branchHint(.cold); + printPanic("reached unreachable code", .{}); + } + pub fn unwrapNull() noreturn { + @branchHint(.cold); + printPanic("attempt to use null value", .{}); + } + pub fn castToNull() noreturn { + @branchHint(.cold); + printPanic("cast causes pointer to be null", .{}); + } + pub fn incorrectAlignment() noreturn { + @branchHint(.cold); + printPanic("incorrect alignment", .{}); + } + pub fn invalidErrorCode() noreturn { + @branchHint(.cold); + printPanic("invalid error code", .{}); + } + pub fn integerOutOfBounds() noreturn { + @branchHint(.cold); + printPanic("integer does not fit in destination type", .{}); + } + pub fn integerOverflow() noreturn { + @branchHint(.cold); + printPanic("integer overflow", .{}); + } + pub fn shlOverflow() noreturn { + @branchHint(.cold); + printPanic("left shift overflowed bits", .{}); + } + pub fn shrOverflow() noreturn { + @branchHint(.cold); + printPanic("right shift overflowed bits", .{}); + } + pub fn divideByZero() noreturn { + printPanic("division by zero", .{}); + } + pub fn exactDivisionRemainder() noreturn { + @branchHint(.cold); + printPanic("exact division produced remainder", .{}); + } + pub fn integerPartOutOfBounds() noreturn { + @branchHint(.cold); + printPanic("integer part of floating point value out of bounds", .{}); + } + pub fn corruptSwitch() noreturn { + @branchHint(.cold); + printPanic("switch on corrupt value", .{}); + } + pub fn shiftRhsTooBig() noreturn { + @branchHint(.cold); + printPanic("shift amount is greater than the type size", .{}); + } + pub fn invalidEnumValue() noreturn { + @branchHint(.cold); + printPanic("invalid enum value", .{}); + } + pub fn forLenMismatch() noreturn { + @branchHint(.cold); + printPanic("for loop over objects with non-equal lengths", .{}); + } + pub fn copyLenMismatch() noreturn { + @branchHint(.cold); + printPanic("source and destination arguments have non-equal lengths", .{}); + } + pub fn memcpyAlias() noreturn { + @branchHint(.cold); + printPanic("@memcpy arguments alias", .{}); + } + pub fn noreturnReturned() noreturn { + @branchHint(.cold); + printPanic("'noreturn' function returned", .{}); + } +}; diff --git a/src/main.zig b/src/main.zig index 8c6174e..80296da 100644 --- a/src/main.zig +++ b/src/main.zig @@ -19,8 +19,12 @@ fn print(s: []const u8) void { } } +pub const panic = debug.KernelPanic; + export fn _start() linksection(".text.init") callconv(.naked) noreturn { asm volatile ( + \\li fp, 0 + \\li ra, 0 \\li sp, 0x88000000 \\tail kmain ); diff --git a/src/riscv/PageTable.zig b/src/riscv/PageTable.zig index f539310..0b70704 100644 --- a/src/riscv/PageTable.zig +++ b/src/riscv/PageTable.zig @@ -43,7 +43,7 @@ pub fn identityMap(self: *PageTable, allocator: Allocator, memory_end: u64) !voi .user = 0, }; - var addr: u64 = 0x80000000; + var addr: u64 = 0x0; while (addr < memory_end) : (addr += 0x1000) { try self.map(allocator, addr, addr, flags); }