diff --git a/src/arch.zig b/src/arch.zig index 928e1ba..9c830e7 100644 --- a/src/arch.zig +++ b/src/arch.zig @@ -20,6 +20,19 @@ pub const impl = switch (cpu) { pub const vmm = impl.vmm; +pub const IrqGuard = struct { + state: bool, + + pub fn acquire() @This() { + const state = set_interrupt_mask(true); + return .{ .state = state }; + } + + pub fn release(self: @This()) void { + set_interrupt_mask(self.state); + } +}; + /// Halts the CPU execution indefinitely, without ever returning. pub inline fn halt() noreturn { impl.halt(); diff --git a/src/kernel.zig b/src/kernel.zig index df5d777..7d105cc 100644 --- a/src/kernel.zig +++ b/src/kernel.zig @@ -18,7 +18,6 @@ const std = @import("std"); fn f0(arg: usize) callconv(.C) void { log.info("Argument is {}", .{arg}); - thread.yield(); } noinline fn f1(arg: usize, c: usize) void { @@ -37,8 +36,6 @@ pub export fn kernel_main() callconv(.C) noreturn { var a = arena.Arena.init(256 * 0x1000) orelse @panic("Could not setup kernel arena"); thread.Queue.init_this_cpu(&a); - const t0 = thread.Thread.create_kernel(&a, &f0, 1234); - const code = switch (comptime arch.cpu) { .riscv64 => &[_]u8{ 0x93, 0x02, 0xB0, 0x07, // li t0, 123 @@ -55,7 +52,8 @@ pub export fn kernel_main() callconv(.C) noreturn { }; const t1 = thread.test_create_user_from_code(&a, code) catch @panic("Could not create test thread"); - thread.enqueue(t0); + const t = thread.Thread.create_kernel(&a, &f0, 1234); + thread.enqueue(t); thread.enqueue(t1); log.info("Test", .{}); diff --git a/src/thread.zig b/src/thread.zig index a906cdf..2cecc79 100644 --- a/src/thread.zig +++ b/src/thread.zig @@ -6,6 +6,7 @@ const arena = @import("arena.zig"); const arch = @import("kernel.zig").arch; const log = @import("debug.zig").log; const mem = @import("mem.zig"); +const sync = @import("sync.zig"); const ProcessAddressSpace = mem.vmm.ProcessAddressSpace; @@ -15,7 +16,7 @@ const ProcessAddressSpace = mem.vmm.ProcessAddressSpace; pub const KernelThreadFn = fn (usize) callconv(.C) void; pub fn kernel_return() callconv(.C) noreturn { - @panic("TODO: kernel thread exit"); + Thread.exit_current(); } /// Task to run when there are no real threads in the queue @@ -34,6 +35,8 @@ pub const Queue = struct { current: ?*Thread = null, /// Thread queue head pointer. head: ?*Thread = null, + /// Queue's lock + lock: sync.Spinlock = .{}, /// Pointer to this CPU's thread queue. pub threadlocal var t_this_cpu: ?*Queue = null; @@ -59,6 +62,7 @@ pub const Queue = struct { /// Yields CPU to the next available task. pub fn yield(self: *@This()) void { + // TODO locking here if (self.current) |curr| { // Switching from thread if (curr.next) |next| { @@ -67,6 +71,12 @@ pub const Queue = struct { self.current = next; next.switch_from(curr); } + } else if (self.head) |h| { + // ... to thread (head) + if (h != curr) { + self.current = h; + h.switch_from(curr); + } } else { // ... to idle self.current = null; @@ -86,6 +96,11 @@ pub const Queue = struct { /// Adds an available task to this queue. pub fn enqueue(self: *@This(), t: *Thread) void { + var guard = self.lock.lock_irqsave(); + defer guard.release(); + + t.queue = self; + if (self.head) |gt| { t.next = gt; t.prev = gt.prev; @@ -97,6 +112,44 @@ pub const Queue = struct { t.prev = t; } } + + /// # Invariants + /// + /// `t` must be a thread within the `self` queue. + fn dequeue(self: *@This(), t: *Thread) void { + var guard = self.lock.lock_irqsave(); + defer guard.release(); + + t.queue = null; + + if (t == self.head) { + if (t.next == t) { + self.head = null; + t.next = null; + t.prev = null; + return; + } + + if (t.next) |tn| { + tn.prev = t.prev; + } + if (t.prev) |tp| { + tp.next = t.next; + } + + self.head = t.next; + } else { + if (t.next) |tn| { + tn.prev = t.prev; + } + if (t.prev) |tp| { + tp.next = t.next; + } + } + + t.next = null; + t.prev = null; + } }; /// Represents a single execution thread. @@ -106,6 +159,8 @@ pub const Thread = struct { /// Architecture-specific task context. arch_context: arch.Context, + /// Queue to which this thread belongs + queue: ?*Queue = null, /// Next thread in the queue. next: ?*Thread = null, /// Previous thread in the queue. @@ -151,6 +206,30 @@ pub const Thread = struct { pub fn switch_from(self: *@This(), from: *@This()) void { self.arch_context.switch_from(&from.arch_context); } + + pub fn dequeue(self: *@This()) void { + // TODO queueing information should be put under a lock for SMP to work properly + if (self.queue) |q| { + q.dequeue(self); + } + } + + pub fn current() *@This() { + return Queue.t_this_cpu.?.current.?; + } + + pub fn exit_current() noreturn { + // Mask IRQs so they don't break current thread's state + const mask = arch.IrqGuard.acquire(); + defer mask.release(); + + const curr = Thread.current(); + curr.dequeue(); + + yield(); + + @panic("This code should not be reachable"); + } }; /// Helper data structure to represent kernel stacks in task contexts.