Files
Zag/src/kern/ipl.zig
T
2026-08-07 15:41:12 -04:00

132 lines
3.4 KiB
Zig

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;
}