aboutsummaryrefslogtreecommitdiff
path: root/src/lib/plic.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/plic.zig')
-rw-r--r--src/lib/plic.zig101
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));
+ }
+ }
+};