aboutsummaryrefslogtreecommitdiff
path: root/src/lib/interrupts.zig
diff options
context:
space:
mode:
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").?;