aboutsummaryrefslogtreecommitdiff
path: root/src/lib/interrupts.zig
diff options
context:
space:
mode:
authorHimbeer <himbeer@disroot.org>2024-05-23 13:48:01 +0200
committerHimbeer <himbeer@disroot.org>2024-05-23 13:48:01 +0200
commit3274a700daff545437f919041cbdce6938eede06 (patch)
tree60a4ec5ebb1406af20733027a2bb4a5d54e54908 /src/lib/interrupts.zig
parent0f61d3bed969fecb35e438bfac2fe34f588834c6 (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.zig332
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").?;