Download zig/kernel/trap.zig from Snapkitty/phaser-ags: direct link, hf CLI and curl.
- Browser
- Download file 12.5 kB
-
https://huggingface.co/Snapkitty/phaser-ags/resolve/main/zig/kernel/trap.zig
- Command line
-
hf download hf://Snapkitty/phaser-ags/zig/kernel/trap.zig
-
curl -L -o trap.zig https://huggingface.co/Snapkitty/phaser-ags/resolve/main/zig/kernel/trap.zig
12.5 kB
| //! SPDX-License-Identifier: AGPL-3.0-or-later | |
| //! Copyright 2025 Ahmad Ali Parr / SnapKitty οΏ½ https://github.com/SNAPKITTYWEST/phaser-ags | |
| //! βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| //! PHASER AGS β Trap framework (complete, no stubs) | |
| //! | |
| //! Full context save/restore dispatch. Integrates with process.zig | |
| //! for preemptive context switching on timer tick. | |
| //! βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| const std = @import("std"); | |
| const driver = @import("driver.zig"); | |
| // βββ Trap frame layout (matches start.S offsets exactly) βββββββββ | |
| pub const TrapFrame = extern struct { | |
| prev_ctx: u64, // 0(sp) β old sp before csrrw swap | |
| ra: u64, // 8 x1 | |
| t0: u64, // 16 x5 | |
| t1: u64, // 24 x6 | |
| t2: u64, // 32 x7 | |
| a0: u64, // 40 x10 | |
| a1: u64, // 48 x11 | |
| a2: u64, // 56 x12 | |
| a3: u64, // 64 x13 | |
| a4: u64, // 72 x14 | |
| a5: u64, // 80 x15 | |
| a6: u64, // 88 x16 | |
| a7: u64, // 96 x17 | |
| s0: u64, // 104 x8 | |
| s1: u64, // 112 x9 | |
| s2: u64, // 120 x18 | |
| s3: u64, // 128 x19 | |
| s4: u64, // 136 x20 | |
| s5: u64, // 144 x21 | |
| s6: u64, // 152 x22 | |
| s7: u64, // 160 x23 | |
| s8: u64, // 168 x24 | |
| s9: u64, // 176 x25 | |
| s10: u64, // 184 x26 | |
| s11: u64, // 192 x27 | |
| t3: u64, // 200 x28 | |
| t4: u64, // 208 x29 | |
| t5: u64, // 216 x30 | |
| t6: u64, // 224 x31 | |
| mepc: u64, // 232 | |
| mstatus: u64, // 240 | |
| mcause: u64, // 248 | |
| mtval: u64, // 256 | |
| }; | |
| comptime { | |
| std.debug.assert(@sizeOf(TrapFrame) == 264); | |
| } | |
| // βββ Exception and interrupt codes βββββββββββββββββββββββββββββββ | |
| pub const Exception = enum(u64) { | |
| instr_misaligned = 0, | |
| instr_access = 1, | |
| illegal_instr = 2, | |
| breakpoint = 3, | |
| load_misaligned = 4, | |
| load_access = 5, | |
| store_misaligned = 6, | |
| store_access = 7, | |
| ecall_u = 8, | |
| ecall_s = 9, | |
| ecall_m = 11, | |
| instr_page_fault = 12, | |
| load_page_fault = 13, | |
| store_page_fault = 15, | |
| _, | |
| }; | |
| pub const Interrupt = enum(u64) { | |
| s_software = 1, | |
| m_software = 3, | |
| s_timer = 5, | |
| m_timer = 7, | |
| s_external = 9, | |
| m_external = 11, | |
| _, | |
| }; | |
| // βββ Handler registration ββββββββββββββββββββββββββββββββββββββββ | |
| const ExceptionHandler = *const fn (*TrapFrame, u64) void; | |
| const InterruptHandler = *const fn (*TrapFrame, u64) void; | |
| var exc_handlers: [16]?ExceptionHandler = .{null} ** 16; | |
| var irq_handlers: [12]?InterruptHandler = .{null} ** 12; | |
| pub fn registerException(code: u64, handler: ExceptionHandler) void { | |
| if (code < 16) exc_handlers[code] = handler; | |
| } | |
| pub fn registerInterrupt(code: u64, handler: InterruptHandler) void { | |
| if (code > 0 and code < 12) irq_handlers[code] = handler; | |
| } | |
| // βββ Reschedule flag (set by timer, checked by dispatch) βββββββββ | |
| var need_reschedule: bool = false; | |
| pub fn requestReschedule() void { | |
| need_reschedule = true; | |
| } | |
| // βββ Main dispatch β called from start.S βββββββββββββββββββββββββ | |
| export fn trapDispatch(frame: *TrapFrame) *TrapFrame { | |
| const cause = frame.mcause; | |
| const is_irq = (cause >> 63) != 0; | |
| const code = cause & 0x7FFF_FFFF_FFFF_FFFF; | |
| if (is_irq) { | |
| dispatchInterrupt(frame, code); | |
| } else { | |
| dispatchException(frame, code); | |
| } | |
| // ββ Preemptive context switch ββ | |
| if (need_reschedule) { | |
| need_reschedule = false; | |
| return doContextSwitch(frame); | |
| } | |
| return frame; | |
| } | |
| // βββ Interrupt dispatch ββββββββββββββββββββββββββββββββββββββββββ | |
| fn dispatchInterrupt(frame: *TrapFrame, code: u64) void { | |
| if (code < 12 and irq_handlers[code] != null) { | |
| irq_handlers[code].?(frame, code); | |
| return; | |
| } | |
| switch (@as(Interrupt, @enumFromInt(code))) { | |
| .m_timer => defaultTimerHandler(frame), | |
| .m_software => defaultSoftwareHandler(frame), | |
| .m_external => defaultExternalHandler(frame), | |
| else => { | |
| driver.Uart.puts("UNHANDLED IRQ "); | |
| driver.Uart.putHex(@intCast(code)); | |
| driver.Uart.putc('\n'); | |
| }, | |
| } | |
| } | |
| // βββ Exception dispatch ββββββββββββββββββββββββββββββββββββββββββ | |
| fn dispatchException(frame: *TrapFrame, code: u64) void { | |
| if (code < 16 and exc_handlers[code] != null) { | |
| exc_handlers[code].?(frame, code); | |
| return; | |
| } | |
| switch (@as(Exception, @enumFromInt(code))) { | |
| .ecall_m => handleEcall(frame), | |
| .ecall_u => handleEcall(frame), | |
| .illegal_instr => handleIllegal(frame), | |
| .instr_page_fault, | |
| .load_page_fault, | |
| .store_page_fault => handlePageFault(frame), | |
| .instr_misaligned, | |
| .load_misaligned, | |
| .store_misaligned => handleMisalign(frame), | |
| .instr_access, | |
| .load_access, | |
| .store_access => handleAccessFault(frame), | |
| .breakpoint => handleBreakpoint(frame), | |
| else => { | |
| driver.Uart.puts("UNHANDLED EXC "); | |
| driver.Uart.putHex(@intCast(code)); | |
| driver.Uart.puts(" at "); | |
| driver.Uart.putHex(@intCast(frame.mepc)); | |
| driver.Uart.putc('\n'); | |
| frame.mepc += 4; | |
| }, | |
| } | |
| } | |
| // βββ Default interrupt handlers ββββββββββββββββββββββββββββββββββ | |
| fn defaultTimerHandler(frame: *TrapFrame) void { | |
| // Acknowledge: CLINT mtimecmp already set by timer.zig | |
| // Deactivate in MIP by clearing MTIP (done by writing mtimecmp) | |
| _ = frame; | |
| // Increment tick count | |
| const timer_mod = @import("timer.zig"); | |
| timer_mod.ackTick(); | |
| } | |
| fn defaultSoftwareHandler(frame: *TrapFrame) void { | |
| _ = frame; | |
| // Clear MSIP in CLINT | |
| driver.regWrite(u32, 0x02000000, 0); | |
| } | |
| fn defaultExternalHandler(frame: *TrapFrame) void { | |
| _ = frame; | |
| // Claim interrupt from PLIC | |
| const plic = @import("driver.zig").Plic; | |
| const irq_id = plic.claim(0); | |
| if (irq_id != 0) { | |
| // Dispatch to registered external IRQ handlers | |
| driver.dispatchExternalIrq(irq_id); | |
| plic.complete(0, irq_id); | |
| } | |
| } | |
| // βββ Exception handlers ββββββββββββββββββββββββββββββββββββββββββ | |
| fn handleEcall(frame: *TrapFrame) void { | |
| const syscall_num: u64 = frame.a7; | |
| const result = @import("syscall.zig").dispatch(syscall_num, frame); | |
| frame.a0 = result; | |
| frame.mepc += 4; // Skip the ecall instruction | |
| } | |
| fn handleIllegal(frame: *TrapFrame) void { | |
| driver.Uart.puts("ILLEGAL INSTR at "); | |
| driver.Uart.putHex(@intCast(frame.mepc)); | |
| driver.Uart.puts(" mtval="); | |
| driver.Uart.putHex(@intCast(frame.mtval)); | |
| driver.Uart.putc('\n'); | |
| frame.mepc += 4; | |
| } | |
| fn handlePageFault(frame: *TrapFrame) void { | |
| driver.Uart.puts("PAGE FAULT at "); | |
| driver.Uart.putHex(@intCast(frame.mepc)); | |
| driver.Uart.puts(" addr="); | |
| driver.Uart.putHex(@intCast(frame.mtval)); | |
| driver.Uart.putc('\n'); | |
| // Kill the offending process if one exists | |
| const proc = @import("process.zig"); | |
| if (proc.getCurrent()) |p| { | |
| driver.Uart.puts("Killing proc "); | |
| driver.Uart.putHex(p.pid); | |
| driver.Uart.putc('\n'); | |
| proc.kill(p.pid); | |
| requestReschedule(); | |
| } else { | |
| frame.mepc += 4; | |
| } | |
| } | |
| fn handleMisalign(frame: *TrapFrame) void { | |
| driver.Uart.puts("MISALIGNED at "); | |
| driver.Uart.putHex(@intCast(frame.mepc)); | |
| driver.Uart.puts(" addr="); | |
| driver.Uart.putHex(@intCast(frame.mtval)); | |
| driver.Uart.putc('\n'); | |
| frame.mepc += 4; | |
| } | |
| fn handleAccessFault(frame: *TrapFrame) void { | |
| driver.Uart.puts("ACCESS FAULT at "); | |
| driver.Uart.putHex(@intCast(frame.mepc)); | |
| driver.Uart.puts(" addr="); | |
| driver.Uart.putHex(@intCast(frame.mtval)); | |
| driver.Uart.putc('\n'); | |
| const proc = @import("process.zig"); | |
| if (proc.getCurrent()) |p| { | |
| proc.kill(p.pid); | |
| requestReschedule(); | |
| } else { | |
| frame.mepc += 4; | |
| } | |
| } | |
| fn handleBreakpoint(frame: *TrapFrame) void { | |
| driver.Uart.puts("BREAKPOINT at "); | |
| driver.Uart.putHex(@intCast(frame.mepc)); | |
| driver.Uart.putc('\n'); | |
| frame.mepc += 4; | |
| } | |
| // βββ Context switch ββββββββββββββββββββββββββββββββββββββββββββββ | |
| fn doContextSwitch(frame: *TrapFrame) *TrapFrame { | |
| const proc = @import("process.zig"); | |
| // Save current process state | |
| if (proc.getCurrent()) |cur| { | |
| cur.ksp = @intFromPtr(frame); | |
| if (cur.state == .running) { | |
| cur.state = .runnable; | |
| } | |
| } | |
| // Pick next process | |
| const next = proc.schedule() orelse { | |
| // Nothing to run β return current frame (idle) | |
| return frame; | |
| }; | |
| // Switch address space if page tables differ | |
| const cur = proc.getCurrent(); | |
| if (cur == null or cur.?.page_root != next.page_root) { | |
| @import("memory.zig").switchToPageTable(next.page_root); | |
| } | |
| next.state = .running; | |
| proc.setCurrentPid(next.pid); | |
| // Return the new process's saved trap frame | |
| return @ptrFromInt(next.ksp); | |
| } | |
| // βββ Install default handlers at boot ββββββββββββββββββββββββββββ | |
| pub fn initDefaults() void { | |
| // Register all exception handlers | |
| registerException(@intFromEnum(Exception.ecall_m), handleEcall); | |
| registerException(@intFromEnum(Exception.ecall_u), handleEcall); | |
| registerException(@intFromEnum(Exception.illegal_instr), handleIllegal); | |
| registerException(@intFromEnum(Exception.instr_page_fault), handlePageFault); | |
| registerException(@intFromEnum(Exception.load_page_fault), handlePageFault); | |
| registerException(@intFromEnum(Exception.store_page_fault), handlePageFault); | |
| registerException(@intFromEnum(Exception.instr_misaligned), handleMisalign); | |
| registerException(@intFromEnum(Exception.load_misaligned), handleMisalign); | |
| registerException(@intFromEnum(Exception.store_misaligned), handleMisalign); | |
| registerException(@intFromEnum(Exception.instr_access), handleAccessFault); | |
| registerException(@intFromEnum(Exception.load_access), handleAccessFault); | |
| registerException(@intFromEnum(Exception.store_access), handleAccessFault); | |
| registerException(@intFromEnum(Exception.breakpoint), handleBreakpoint); | |
| // Timer interrupt handler | |
| registerInterrupt(@intFromEnum(Interrupt.m_timer), handleTimerIrq); | |
| registerInterrupt(@intFromEnum(Interrupt.m_software), handleSoftwareIrq); | |
| registerInterrupt(@intFromEnum(Interrupt.m_external), handleExternalIrq); | |
| } | |
| fn handleTimerIrq(frame: *TrapFrame, code: u64) void { | |
| _ = code; | |
| const timer_mod = @import("timer.zig"); | |
| timer_mod.ackTick(); | |
| timer_mod.advanceCompare(); | |
| // Request reschedule on every tick | |
| requestReschedule(); | |
| } | |
| fn handleSoftwareIrq(frame: *TrapFrame, code: u64) void { | |
| _ = frame; | |
| _ = code; | |
| // Clear MSIP | |
| driver.regWrite(u32, 0x02000000, 0); | |
| } | |
| fn handleExternalIrq(frame: *TrapFrame, code: u64) void { | |
| _ = frame; | |
| _ = code; | |
| const plic = driver.Plic; | |
| const irq_id = plic.claim(0); | |
| if (irq_id != 0) { | |
| driver.dispatchExternalIrq(irq_id); | |
| plic.complete(0, irq_id); | |
| } | |
| } | |