diff --git a/src/arch.zig b/src/arch.zig index 5778ba2..b02792b 100644 --- a/src/arch.zig +++ b/src/arch.zig @@ -23,7 +23,7 @@ pub inline fn interruptMask() bool { /// Modifies the interrupt mask to either allow or block IRQs from being delivered to the CPU. /// Returns the old IRQ mask. pub inline fn setInterruptMask(masked: bool) bool { - impl.setInterruptMask(masked); + return impl.setInterruptMask(masked); } /// Suspends the CPU until an interrupt is signalled. diff --git a/src/arch/aarch64.zig b/src/arch/aarch64.zig index dd2b547..12bdf62 100644 --- a/src/arch/aarch64.zig +++ b/src/arch/aarch64.zig @@ -1,5 +1,7 @@ const std = @import("std"); + const boot = @import("aarch64/boot.zig"); +const regs = @import("aarch64/regs.zig"); export const _ = boot.aa64BspLowerEntry; @@ -26,6 +28,20 @@ pub const Context = struct { } }; +pub fn setInterruptMask(masked: bool) bool { + const old = interruptMask(); + if (masked) { + regs.DAIF.modify(.{ .I = true }, .{}); + } else { + regs.DAIF.modify(.{}, .{ .I = true }); + } + return old; +} + +pub fn interruptMask() bool { + return regs.DAIF.read().I; +} + pub fn halt() noreturn { while (true) {} } diff --git a/src/arch/aarch64/regs.zig b/src/arch/aarch64/regs.zig index 70cfacf..48e8e2f 100644 --- a/src/arch/aarch64/regs.zig +++ b/src/arch/aarch64/regs.zig @@ -39,6 +39,20 @@ pub const TTBR1_EL1 = Register("ttbr1_el1", u64); pub const VBAR_EL1 = Register("vbar_el1", u64); pub const ELR_EL1 = Register("elr_el1", u64); pub const FAR_EL1 = Register("far_el1", u64); +pub const DAIF = Register("daif", packed struct(u64) { + // 0..6 + _0: u6 = 0, + // 6 + F: bool = false, + // 7 + I: bool = false, + // 8 + A: bool = false, + // 9 + D: bool = false, + // 10..64 + _1: u54 = 0, +}); pub const ESR_EL1 = Register("esr_el1", packed struct(u64) { // 0..25 diff --git a/src/mem/phys.zig b/src/mem/phys.zig index 8e38fc0..91b8678 100644 --- a/src/mem/phys.zig +++ b/src/mem/phys.zig @@ -9,7 +9,7 @@ const vmm = @import("vmm.zig"); const sync = @import("../sync.zig"); const Range = @import("../util/range.zig").Range; -const Spinlock = sync.IrqSafeSpinlock; +const Spinlock = sync.Spinlock; /// Represents a single region of physical memory (reserved or available). pub const MemoryRegion = struct { @@ -262,8 +262,8 @@ fn trace_free(page: mem.PhysicalAddress) void { /// Allocates a single 4KiB physical memory page. pub fn alloc_page() ?mem.PhysicalAddress { - gPhysicalMemoryLock.lock(); - defer gPhysicalMemoryLock.release(); + const guard = gPhysicalMemoryLock.lock_irqsave(); + defer guard.release(); const page = gPhysicalMemory.alloc_page(); if (comptime kernel.TRACE_PHYSICAL_ALLOCATOR) { trace_allocation(1, page); @@ -273,8 +273,8 @@ pub fn alloc_page() ?mem.PhysicalAddress { /// Allocates a set of `count` contiguous 4KiB pages. pub fn alloc_pages(count: usize) ?mem.PhysicalAddress { - gPhysicalMemoryLock.lock(); - defer gPhysicalMemoryLock.release(); + const guard = gPhysicalMemoryLock.lock_irqsave(); + defer guard.release(); const pages = gPhysicalMemory.alloc_pages(count); if (comptime kernel.TRACE_PHYSICAL_ALLOCATOR) { trace_allocation(count, pages); @@ -293,8 +293,8 @@ pub fn free_page(page: mem.PhysicalAddress) void { if (comptime kernel.TRACE_PHYSICAL_ALLOCATOR) { trace_free(page); } - gPhysicalMemoryLock.lock(); - defer gPhysicalMemoryLock.release(); + const guard = gPhysicalMemoryLock.lock_irqsave(); + defer guard.release(); gPhysicalMemory.free_page(page); } diff --git a/src/sync.zig b/src/sync.zig index 7f52edb..9c96309 100644 --- a/src/sync.zig +++ b/src/sync.zig @@ -4,10 +4,21 @@ const std = @import("std"); const arch = @import("kernel.zig").arch; /// Basic spinlock implementation -// TODO not actually IRQ safe, lol. -pub const IrqSafeSpinlock = struct { +pub const Spinlock = struct { state: std.atomic.Value(bool) = .{ .raw = false }, + const Guard = struct { + lock: *Spinlock, + irqMask: bool, + + /// Releases the `Guard`, restoring the previous IRQ state and releasing the lock used + /// to acquire it. + pub fn release(self: @This()) void { + self.lock.release(); + _ = arch.setInterruptMask(self.irqMask); + } + }; + /// Acquires a lock over `self`. Returns `false` if the lock is already held by someone else. pub fn tryLock(self: *@This()) bool { return self.state.cmpxchgStrong(false, true, .acquire, .monotonic) orelse false; @@ -20,6 +31,13 @@ pub const IrqSafeSpinlock = struct { } } + /// Same as `lock()`, but additionally saves current IRQ state and masks IRQs. + pub fn lock_irqsave(self: *@This()) Guard { + const irqMask = arch.setInterruptMask(true); + self.lock(); + return .{ .irqMask = irqMask, .lock = self }; + } + /// Releases a lock over `self`. pub fn release(self: *@This()) void { self.state.store(false, .release);