const std = @import("std"); const r = @import("root"); const ke = r.ke; const ki = ke.private; /// Interrupt priority level (IPL) pub const Ipl = enum(u8) { /// All interrupts enabled. Passive = 0, /// Preemption disabled. Dispatch = 1, /// Device interrupts disabled. Device = 13, /// All interrupts disabled. High = 15, pub fn get_max_software() Ipl { return .Dispatch; } }; const PerCpu = struct { /// Bitmask of pending software interrupts on this CPU. pending_softints: std.atomic.Value(u8), }; /// Current IPL on this CPU. pub const cpu_ipl = ke.ExportedCpuLocal( Ipl, .Passive, "cpu_ipl", ); pub const percpu = ke.CpuLocal(PerCpu, .{ .pending_softints = .init(0), }); pub fn current() Ipl { return cpu_ipl.local().*; } /// Raise IPL to `new`. /// Panics if the current IPL is higher than requested. pub fn raise(new: Ipl) Ipl { var old: ke.Ipl = undefined; if (ki.impl.raise_software_ipl(new, &old)) { @branchHint(.unlikely); std.debug.panic( "ke.ipl.raise(): Target IPL ({}) is lower than current IPL ({})", .{ new, old }, ); } if (@intFromEnum(new) > @intFromEnum(Ipl.get_max_software())) { // Moving between hardware levels is rare. @branchHint(.unlikely); ki.impl.set_hardware_ipl(new); } return old; } /// Lower IPL to `new`. /// Panics if the current IPL is lower than requested. pub fn lower(new: Ipl) void { var old: ke.Ipl = undefined; if (ki.impl.lower_software_ipl(new, &old)) { @branchHint(.unlikely); std.debug.panic( "ke.ipl.lower(): Target IPL ({}) is higher than current IPL ({})", .{ new, old }, ); } if (@intFromEnum(new) <= @intFromEnum(Ipl.get_max_software()) and @intFromEnum(old) > @intFromEnum(Ipl.get_max_software())) { @branchHint(.unlikely); ki.impl.set_hardware_ipl(.Passive); } if (@intFromEnum(new) < @intFromEnum(Ipl.Dispatch) and is_softint_pending(.Dispatch)) { // Dispatch DPCs if necessary. ki.dpc.dispatch(); } } /// Set the hardware IPL to `new`. pub fn set_hardware(new: Ipl) Ipl { const ints = ki.impl.disable_interrupts(); const cpu = cpu_ipl.local(); const old = cpu.*; cpu.* = new; ki.impl.set_hardware_ipl(new); ki.impl.restore_interrupts(ints); return old; } /// Mark a software interrupt of IPL `ipl` on `cpu` as pending. pub fn set_softint_pending(cpu: u32, ipl: Ipl) void { std.debug.assert(@intFromEnum(ipl) <= @intFromEnum(Ipl.Dispatch)); _ = percpu.remote(cpu).pending_softints.bitSet( @intCast(@intFromEnum(ipl)), .monotonic, ); } /// Mark a software interrupt of IPL `ipl` on `cpu` as handled. pub fn clear_softint_pending(cpu: u32, ipl: Ipl) void { std.debug.assert(@intFromEnum(ipl) <= @intFromEnum(Ipl.Dispatch)); _ = percpu.remote(cpu).pending_softints.bitReset( @intCast(@intFromEnum(ipl)), .monotonic, ); } /// Check whether a software interrupt of IPL `ipl` /// is pending on the current CPU. pub fn is_softint_pending(ipl: Ipl) bool { std.debug.assert(@intFromEnum(ipl) <= @intFromEnum(Ipl.Dispatch)); const shift: u3 = @intCast(@intFromEnum(ipl)); const bit: u8 = @as(u8, 1) << shift; return percpu.local().pending_softints.load(.monotonic) & bit != 0; }