diff options
author | Himbeer <himbeer@disroot.org> | 2024-05-23 13:48:01 +0200 |
---|---|---|
committer | Himbeer <himbeer@disroot.org> | 2024-05-23 13:48:01 +0200 |
commit | 3274a700daff545437f919041cbdce6938eede06 (patch) | |
tree | 60a4ec5ebb1406af20733027a2bb4a5d54e54908 /src/lib/interrupts.zig | |
parent | 0f61d3bed969fecb35e438bfac2fe34f588834c6 (diff) |
Drop FDT support in favor of custom HWI format
Fixes numerous parsing bugs and increases efficiency. The kernel now runs successfully on the Lichee Pi 4A.
Diffstat (limited to 'src/lib/interrupts.zig')
-rw-r--r-- | src/lib/interrupts.zig | 332 |
1 files changed, 332 insertions, 0 deletions
diff --git a/src/lib/interrupts.zig b/src/lib/interrupts.zig new file mode 100644 index 0000000..35a20d4 --- /dev/null +++ b/src/lib/interrupts.zig @@ -0,0 +1,332 @@ +// SPDX-FileCopyrightText: 2024 Himbeer <himbeer@disroot.org> +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +const std = @import("std"); + +const Console = @import("Console.zig"); +const instructions = @import("instructions.zig"); +const paging = @import("paging.zig"); +const plic = @import("plic.zig"); +const process = @import("process.zig"); +const syscall = @import("syscall.zig"); +const time = @import("sbi/time.zig"); +const trap = @import("trap.zig"); + +pub var trap_frame: trap.Frame = undefined; + +pub const SupervisorTrapVector = packed struct(usize) { + pub const Mode = enum(u2) { + direct = 0, + vectored = 1, + }; + + mode: u2, + base_addr: u62, + + pub fn fromBaseAddr(mode: Mode, base_addr: usize) SupervisorTrapVector { + return .{ + .mode = @intFromEnum(mode), + .base_addr = base_addr >> 2, + }; + } +}; + +pub const Enable = packed struct(usize) { + u_software: u1, + s_software: u1, + reserved0: u2, + u_timer: u1, + s_timer: u1, + reserved1: u2, + u_external: u1, + s_external: u1, + reserved2: u54, + + pub const all = .{ + .u_software = 1, + .s_software = 1, + .reserved0 = 0, + .u_timer = 1, + .s_timer = 1, + .reserved1 = 0, + .u_external = 1, + .s_external = 1, + .reserved2 = 0, + }; +}; + +pub const Cause = packed struct(usize) { + num: u63, + @"async": u1, + + pub fn isAsync(self: Cause) bool { + return self.@"async" == 1; + } +}; + +pub const AsyncCause = enum(u63) { + user_software = 0, + supervisor_software = 1, + user_timer = 4, + supervisor_timer = 5, + user_external = 8, + supervisor_external = 9, + _, +}; + +pub const SyncCause = enum(u63) { + instruction_address_misaligned = 0, + instruction_access_fault = 1, + illegal_instruction = 2, + breakpoint = 3, + load_access_fault = 5, + amo_address_misaligned = 6, + store_or_amo_access_fault = 7, + ecall = 8, + instruction_page_fault = 12, + load_page_fault = 13, + store_or_amo_page_fault = 15, + _, +}; + +pub const Sstatus = packed struct(usize) { + u_interrupts: u1, + s_interrupts: u1, + reserved0: u2, + u_interrupts_previous: u1, + s_interrupts_previous: u1, + reserved1: u2, + previous_privilege: u1, + reserved2: u4, + fs: u2, + xs: u2, + reserved3: u1, + sum: u1, + mxr: u1, + reserved4: u12, + u_xlen: u2, + reserved5: u29, + sd: u1, +}; + +export fn handleTrap(epc: usize, tval: usize, cause_bits: usize, hart_id: usize, status: Sstatus, frame: *trap.Frame) usize { + _ = &status; + _ = &frame; + + const console = Console.autoChoose() orelse while (true) asm volatile ("wfi"); + const w = console.writer(); + + const cause: Cause = @bitCast(cause_bits); + + if (cause.isAsync()) { + switch (@as(AsyncCause, @enumFromInt(cause.num))) { + .supervisor_software => w.print("Hart {d}: Software interrupt\r\n", .{hart_id}) catch while (true) {}, + .supervisor_timer => { + if (status.previous_privilege == 0) { + // Trapped from U-mode, update pc for next time slice. + // + // We can simply use the last node of the process list here + // because the scheduler moves a process to the end of the queue + // before returning into it. + + process.list.last.?.data.pc = epc; + process.list.last.?.data.state = .waiting; + + schedule() catch |err| { + std.debug.panic("Hart {d}: Unable to schedule next process: {any}", .{ hart_id, err }); + }; + } + + // Don't interrupt kernel code, it may never run otherwise. + }, + .supervisor_external => { + const context: u14 = @intCast(2 * hart_id + 1); + + const external_cause = plic.default.claim(context) catch |err| { + std.debug.panic("Hart {d}: Unable to claim external interrupt: {any}", .{ hart_id, err }); + }; + if (external_cause) |source| { + w.print("Hart {d}: External interrupt: 0x{x}\r\n", .{ hart_id, source }) catch while (true) {}; + handleExternal(source); + plic.default.complete(context, source) catch |err| { + std.debug.panic("Hart {d}: Unable to complete external interrupt: {any}", .{ hart_id, err }); + }; + } + }, + else => { + std.debug.panic("Hart {d}: Unhandled asynchronous interrupt: {d}", .{ hart_id, cause.num }); + }, + } + } else { + switch (@as(SyncCause, @enumFromInt(cause.num))) { + .illegal_instruction => { + std.debug.panic("Hart {d}: Illegal instruction, EPC = 0x{x:0>16}", .{ hart_id, epc }); + }, + .instruction_access_fault => { + std.debug.panic("Hart {d}: Instruction access fault: EPC = 0x{x:0>16}, TVAL = 0x{x:0>16}", .{ hart_id, epc, tval }); + }, + .load_access_fault => { + std.debug.panic("Hart {d}: Load access fault: EPC = 0x{x:0>16}, TVAL = 0x{x:0>16}", .{ hart_id, epc, tval }); + }, + .store_or_amo_access_fault => { + std.debug.panic("Hart {d}: Store/AMO access fault: EPC = 0x{x:0>16}, TVAL = 0x{x:0>16}", .{ hart_id, epc, tval }); + }, + .ecall => { + syscall.handle(frame) catch |err| switch (err) { + syscall.Error.UnknownSyscall => { + const a0 = frame.general_purpose_registers[10]; + w.print("Hart {d}: Unknown syscall, a0 = 0x{x:0>16}\r\n", .{ hart_id, a0 }) catch while (true) {}; + }, + }; + + return epc + 4; + }, + .instruction_page_fault => { + std.debug.panic("Hart {d}: Instruction page fault: EPC = 0x{x:0>16}, TVAL = 0x{x:0>16}", .{ hart_id, epc, tval }); + }, + .load_page_fault => { + std.debug.panic("Hart {d}: Load page fault: EPC = 0x{x:0>16}, TVAL = 0x{x:0>16}", .{ hart_id, epc, tval }); + }, + .store_or_amo_page_fault => { + std.debug.panic("Hart {d}: Store/AMO page fault: EPC = 0x{x:0>16}, TVAL = 0x{x:0>16}", .{ hart_id, epc, tval }); + }, + else => { + std.debug.panic("Hart {d}: Unhandled synchronous interrupt: {d}, EPC = 0x{x:0>16}", .{ hart_id, cause.num, epc }); + }, + } + } + + return epc; +} + +fn handleExternal(interrupt: ?u10) void { + _ = &interrupt; +} + +export fn supervisorTrapVector() align(4) callconv(.Naked) noreturn { + asm volatile ( + \\ csrrw t6, sscratch, t6 + \\ + \\ sd x1, 8(t6) + \\ sd x2, 16(t6) + \\ sd x3, 24(t6) + \\ sd x4, 32(t6) + \\ sd x5, 40(t6) + \\ sd x6, 48(t6) + \\ sd x7, 56(t6) + \\ sd x8, 64(t6) + \\ sd x9, 72(t6) + \\ sd x10, 80(t6) + \\ sd x11, 88(t6) + \\ sd x12, 96(t6) + \\ sd x13, 104(t6) + \\ sd x14, 112(t6) + \\ sd x15, 120(t6) + \\ sd x16, 128(t6) + \\ sd x17, 136(t6) + \\ sd x18, 144(t6) + \\ sd x19, 152(t6) + \\ sd x20, 160(t6) + \\ sd x21, 168(t6) + \\ sd x22, 176(t6) + \\ sd x23, 184(t6) + \\ sd x24, 192(t6) + \\ sd x25, 200(t6) + \\ sd x26, 208(t6) + \\ sd x27, 216(t6) + \\ sd x28, 224(t6) + \\ sd x29, 232(t6) + \\ sd x30, 240(t6) + \\ + \\ mv t5, t6 + \\ csrr t6, sscratch + \\ + \\ sd x31, 248(t5) + \\ + \\ csrw sscratch, t5 + \\ + \\ csrr a0, sepc + \\ csrr a1, stval + \\ csrr a2, scause + // \\ csrr a3, mhartid + // Use zero for the hart id until a solution is found. + \\ mv a3, zero + \\ csrr a4, sstatus + \\ mv a5, t5 + \\ la sp, _stvec_stack_end + \\ call handleTrap + \\ + \\ csrw sepc, a0 + \\ + \\ csrr t6, sscratch + \\ + \\ ld x1, 8(t6) + \\ ld x2, 16(t6) + \\ ld x3, 24(t6) + \\ ld x4, 32(t6) + \\ ld x5, 40(t6) + \\ ld x6, 48(t6) + \\ ld x7, 56(t6) + \\ ld x8, 64(t6) + \\ ld x9, 72(t6) + \\ ld x10, 80(t6) + \\ ld x11, 88(t6) + \\ ld x12, 96(t6) + \\ ld x13, 104(t6) + \\ ld x14, 112(t6) + \\ ld x15, 120(t6) + \\ ld x16, 128(t6) + \\ ld x17, 136(t6) + \\ ld x18, 144(t6) + \\ ld x19, 152(t6) + \\ ld x20, 160(t6) + \\ ld x21, 168(t6) + \\ ld x22, 176(t6) + \\ ld x23, 184(t6) + \\ ld x24, 192(t6) + \\ ld x25, 200(t6) + \\ ld x26, 208(t6) + \\ ld x27, 216(t6) + \\ ld x28, 224(t6) + \\ ld x29, 232(t6) + \\ ld x30, 240(t6) + \\ ld x31, 248(t6) + \\ + \\ sret + ); +} + +fn schedule() !noreturn { + if (process.next()) |next| { + try time.interruptInMillis(process.schedule_interval_millis); + process.switchTo(next); + } + + return process.Error.EmptySchedule; +} + +pub fn init() void { + trap_frame = .{ + .general_purpose_registers = [_]usize{0} ** 32, + .floating_point_registers = [_]usize{0} ** 32, + .satp = 0, + .stack_pointer = @ptrFromInt(instructions.stackPointer()), + .hart_id = 0, + }; + + asm volatile ( + \\ csrw sscratch, %[trapframe] + \\ + \\ la t0, supervisorTrapVector + \\ csrw stvec, t0 + \\ + \\ csrr t0, sstatus + \\ ori t0, t0, 2 + \\ csrw sstatus, t0 + : + : [trapframe] "r" (&trap_frame), + ); +} + +pub const setEnabled = instructions.setCsrFn(Enable, "sie").?; |