diff options
Diffstat (limited to 'src/lib/plic.zig')
-rw-r--r-- | src/lib/plic.zig | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/src/lib/plic.zig b/src/lib/plic.zig new file mode 100644 index 0000000..227b8b8 --- /dev/null +++ b/src/lib/plic.zig @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: 2024 Himbeer <himbeer@disroot.org> +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +const std = @import("std"); + +const hwinfo = @import("hwinfo.zig"); + +pub var default: Plic = undefined; + +pub const Error = error{ + NoPlic, + PlicIncompatible, + NoPlicReg, + InterruptOutOfRange, + ContextOutOfRange, +}; + +pub const Context = packed struct { + priority_threshold: u32, + claim_or_complete: u32, +}; + +pub const Plic = struct { + mmio_register: hwinfo.Reg, + + const priority_offset = 0x0; + const enable_offset = 0x2000; + const context_offset_zero = 0x200000; + const context_offset_nonzero = 0x201000; + + pub const num_contexts = 15872; + + // A value greater than or equal to num_contexts for context is an error. + // A value of 0 for interrupt results in an error. + pub fn setEnabled(self: Plic, context: u14, interrupt: u10, enable: bool) !void { + if (context >= num_contexts) return Error.ContextOutOfRange; + if (interrupt == 0) return Error.InterruptOutOfRange; + + const mmio_slice = self.mmioSlice(); + const enable_ptr: *volatile [num_contexts][32]u32 = @alignCast(@ptrCast(&mmio_slice[enable_offset])); + + const register = interrupt / 32; + const bit = @as(u32, 1) << @intCast(interrupt & 0x1f); + + if (enable) { + enable_ptr[context][register] |= bit; + } else { + enable_ptr[context][register] &= ~bit; + } + } + + // A value of 0 for interrupt results in an error. + pub fn setPriority(self: Plic, interrupt: u10, priority: u3) !void { + if (interrupt == 0) return Error.InterruptOutOfRange; + + const mmio_slice = self.mmioSlice(); + const priority_ptr: *volatile [1024]u32 = @alignCast(@ptrCast(&mmio_slice[priority_offset])); + + priority_ptr[interrupt] = @intCast(priority); + } + + // A value greater than or equal to num_contexts for context is an error. + pub fn setPriorityThreshold(self: Plic, context: u14, threshold: u3) !void { + const context_ptr = try self.contextPtr(context); + context_ptr.priority_threshold = threshold; + } + + // A value greater than or equal to num_contexts for context is an error. + // Non-null interrupts are guaranteed to be non-zero. + pub fn claim(self: Plic, context: u14) !?u10 { + const context_ptr = try self.contextPtr(context); + const interrupt = context_ptr.claim_or_complete; + + if (interrupt != 0) return @intCast(interrupt) else return null; + } + + // A value greater than or equal to num_contexts for context is an error. + // A value of 0 for interrupt results in an error. + pub fn complete(self: Plic, context: u14, interrupt: u10) !void { + if (interrupt == 0) return Error.InterruptOutOfRange; + + const context_ptr = try self.contextPtr(context); + context_ptr.claim_or_complete = interrupt; + } + + fn contextPtr(self: Plic, context: u14) !*volatile Context { + if (context >= num_contexts) return Error.ContextOutOfRange; + + const mmio_slice = self.mmio_register.slice(u8); + + if (context == 0) { + return @alignCast(@ptrCast(&mmio_slice[context_offset_zero])); + } else { + const context_offset: usize = context - 1; + const ptr_offset = context_offset * (@sizeOf(u32) + @sizeOf(Context)); + const context_ptr = &mmio_slice[context_offset_nonzero + ptr_offset]; + return @alignCast(@ptrCast(context_ptr)); + } + } +}; |