feat: fireworks!

This commit is contained in:
Loïc Grégoire
2026-04-18 20:44:52 -04:00
parent 712a8012af
commit 8fc133cc3c
18 changed files with 186 additions and 127 deletions
+41 -74
View File
@@ -11,92 +11,54 @@ pub const early_init = init.early_init;
pub export var cpu_self_offset: usize linksection(r.percpu) = 0;
pub export var cpu_offsets: [*]usize = undefined;
pub const ThreadContext = struct {
rdi: u64,
rsi: u64,
rbx: u64,
rsp: u64,
rbp: u64,
r12: u64,
r13: u64,
r14: u64,
r15: u64,
const ThreadFrame = extern struct {
rbp: u64 align(1),
rbx: u64 align(1),
r12: u64 align(1),
r13: u64 align(1),
r14: u64 align(1),
r15: u64 align(1),
rip: u64 align(1),
};
fn thread_trampoline(entry: usize, arg: usize) callconv(.c) void {
extern fn asm_thread_entry() void;
extern fn do_context_switch(old: *ThreadContext, new: *ThreadContext, lock: *u8) callconv(.c) void;
pub const ThreadContext = extern struct {
rsp: u64 align(1),
frame: ThreadFrame,
export fn thread_entry(entry: usize, arg: usize) callconv(.c) void {
const entry_fn: *const fn (?*anyopaque) void = @ptrFromInt(entry);
ke.ipl.lower(.Passive);
entry_fn(@ptrFromInt(arg));
// TODO: This shouldn't be reached!!!
}
pub fn init(stack: r.VAddr, stack_size: usize, entry: *const fn (?*anyopaque) void, arg: ?*anyopaque) @This() {
var ctx: @This() = undefined;
ctx.rdi = @intFromPtr(entry);
ctx.rsi = @intFromPtr(arg);
var sp: usize = stack + stack_size;
var sp = stack + stack_size;
{
sp -= @sizeOf(usize);
@as(*usize, @ptrFromInt(sp)).* = 0;
sp -= @sizeOf(usize);
@as(*usize, @ptrFromInt(sp)).* = @intFromPtr(&thread_trampoline);
if (sp % 16 != 0) {
sp -= (sp % 16);
}
ctx.rsp = sp;
const frame: *ThreadFrame = @ptrFromInt(sp - @sizeOf(ThreadFrame));
frame.* = std.mem.zeroes(ThreadFrame);
ctx.rsp = @intFromPtr(frame);
frame.rip = @intFromPtr(&asm_thread_entry);
frame.r12 = @intFromPtr(entry);
frame.r13 = @intFromPtr(arg);
return ctx;
}
export fn do_switch_to() callconv(.naked) void {
const asm_template = std.fmt.comptimePrint(
\\movq %%rbx, {d}(%%rdi)
\\movq %%rsp, {d}(%%rdi)
\\movq %%rbp, {d}(%%rdi)
\\movq %%r12, {d}(%%rdi)
\\movq %%r13, {d}(%%rdi)
\\movq %%r14, {d}(%%rdi)
\\movq %%r15, {d}(%%rdi)
\\
\\movq {d}(%%rsi), %%rbx
\\movq {d}(%%rsi), %%rsp
\\movq {d}(%%rsi), %%rbp
\\movq {d}(%%rsi), %%r12
\\movq {d}(%%rsi), %%r13
\\movq {d}(%%rsi), %%r14
\\movq {d}(%%rsi), %%r15
\\movq {d}(%%rsi), %%rdi
\\movq {d}(%%rsi), %%rsi
\\
\\ret
, .{
@offsetOf(ThreadContext, "rbx"),
@offsetOf(ThreadContext, "rsp"),
@offsetOf(ThreadContext, "rbp"),
@offsetOf(ThreadContext, "r12"),
@offsetOf(ThreadContext, "r13"),
@offsetOf(ThreadContext, "r14"),
@offsetOf(ThreadContext, "r15"),
@offsetOf(ThreadContext, "rbx"),
@offsetOf(ThreadContext, "rsp"),
@offsetOf(ThreadContext, "rbp"),
@offsetOf(ThreadContext, "r12"),
@offsetOf(ThreadContext, "r13"),
@offsetOf(ThreadContext, "r14"),
@offsetOf(ThreadContext, "r15"),
@offsetOf(ThreadContext, "rdi"),
@offsetOf(ThreadContext, "rsi"),
});
asm volatile (asm_template);
}
pub fn switch_to(self: *ThreadContext, new: *ThreadContext) callconv(.c) void {
asm volatile (
\\call do_switch_to
:
: [_] "{rdi}" (self),
[_] "{rsi}" (new),
);
const thread: *ke.Thread = @alignCast(@fieldParentPtr("context", self));
do_context_switch(self, new, &thread.lock.locked);
}
};
@@ -118,9 +80,14 @@ pub inline fn percpu_ptr(variable: anytype) @TypeOf(variable) {
}
pub inline fn set_hardware_ipl(ipl: ke.Ipl) void {
const cr8_value: u64 = switch (ipl) {
.Passive, .Dispatch => 0,
.Device => 13,
.High => 15,
};
asm volatile ("mov %[ipl], %%cr8"
:
: [ipl] "r" (@as(u64, @intFromEnum(ipl))),
: [ipl] "r" (cr8_value),
);
}
@@ -144,6 +111,6 @@ pub inline fn restore_interrupts(state: bool) void {
}
}
pub inline fn send_resched_ipi(_: u32) void {
@panic("TODO send_resched_ipi");
pub inline fn send_resched_ipi(target: u32) void {
pl.impl.send_resched_ipi(target);
}
+5
View File
@@ -86,11 +86,16 @@ fn early_cpu_init() linksection(r.init) void {
}
pub fn ap_entry(cpu_id: u32, booted: *std.atomic.Value(usize)) noreturn {
amd64.write_msr(.GsBase, ki.impl.cpu_offsets[cpu_id]);
early_cpu_init();
ki.cpu.init_cpu(cpu_id);
pl.impl.init_ap();
ki.sched.percpu.local().current_thread = pl.impl.smp.start_thread.local().*;
_ = booted.fetchAdd(1, .monotonic);
ke.ipl.lower(.Passive);
while (true) {
std.atomic.spinLoopHint();
+1 -1
View File
@@ -87,7 +87,7 @@ export fn isr_handler_main(frame: *const amd64.IrqFrame) callconv(.{ .x86_64_sys
pl.impl.apic.eoi();
}
ki.impl.set_hardware_ipl(ipl);
_ = ke.ipl.set_hardware(ipl);
if (@intFromEnum(ipl) < @intFromEnum(ke.Ipl.get_max_software()) and ki.ipl.is_softint_pending(.Dispatch)) {
ki.dpc.dispatch(ke.cpu.current());
+39
View File
@@ -131,3 +131,42 @@ __interrupt_vectors:
.rept 256
.quad isr\()\+
.endr
.global do_context_switch
do_context_switch:
/* Save callee-saved registers. */
push %r15
push %r14
push %r13
push %r12
push %rbx
push %rbp
/* Save stack pointer. */
mov %rsp, (%rdi)
/* Load new stack pointer. */
mov (%rsi), %rsp
/* Unlock passed lock. */
xorl %ecx, %ecx
xchgb %cl, (%rdx)
/* Restore callee-saved registers. */
pop %rbp
pop %rbx
pop %r12
pop %r13
pop %r14
pop %r15
ret
.extern thread_entry
.global asm_thread_entry
asm_thread_entry:
mov %r12, %rdi
mov %r13, %rsi
call thread_entry
ud2
+4 -2
View File
@@ -7,7 +7,7 @@ const ki = ke.private;
const ex = r.ex;
var thread0: ke.Thread = undefined;
var stack: [8192]u8 align(16) = undefined;
var stack: [r.kib(16)]u8 align(16) = undefined;
pub fn init(boot_info: *pl.BootInfo) linksection(r.init) void {
ki.impl.early_init();
@@ -15,10 +15,12 @@ pub fn init(boot_info: *pl.BootInfo) linksection(r.init) void {
pl.early_init(boot_info);
ki.cpu.init_cpu(0);
thread0.init(@intFromPtr(&stack), 8192, ki.sched.idle, null);
thread0.init(@intFromPtr(&stack), r.kib(16), ki.sched.idle, null);
thread0.priority = 0;
thread0.priority_class = .Idle;
ki.sched.percpu.local().current_thread = &thread0;
std.log.info("Zag for {s} ({s}), cmdline is \"{?s}\"", .{ pl.name, arch.name, boot_info.cmdline });
ex.private.init(boot_info);
+6 -6
View File
@@ -795,26 +795,26 @@ fn yield(cpu: u32) void {
sched_cpu.queues_lock.acquire_no_ipl();
const cur = cpu.current_thread;
var next = cpu.next_thread;
const cur = sched_cpu.current_thread;
var next = sched_cpu.next_thread;
if (next != null) {
cpu.next_thread = null;
sched_cpu.next_thread = null;
} else {
// Pick a new thread to run.
next = select_thread(null, cpu, false);
next = select_thread(null, sched_cpu, false);
}
if (next == null) {
// Nothing to run, go idle.
next = cpu.idle_thread;
next = sched_cpu.idle_thread;
sched_cpu.steal_work = true;
}
sched_cpu.queues_lock.release_no_ipl();
// Switch into the thread.
do_switch(cpu, cur.?, next.?);
do_switch(sched_cpu, cur.?, next.?);
// cur lock dropped
}
+11 -8
View File
@@ -172,13 +172,16 @@ fn handle_expiry(_: ?*anyopaque) void {
return;
}
if (timer.state.cmpxchgStrong(.Pending, .Running, .acquire, .monotonic) != null) {
// Timer must've been canceled.
cpu.lock.release_no_ipl();
continue;
}
_ = cpu.timers.pop();
cpu.lock.release_no_ipl();
if (timer.state.cmpxchgStrong(.Pending, .Running, .acquire, .monotonic) != null) {
// Timer must've been canceled.
continue;
}
const maybe_dpc = timer.dpc;
timer.hdr.lock.acquire_no_ipl();
@@ -186,12 +189,12 @@ fn handle_expiry(_: ?*anyopaque) void {
timer.hdr.signaled = 1;
timer.cpu = null;
// Wake whomever was waiting on the timer.
ki.wait.satisfy_wait(&timer.hdr);
timer.state.store(.Stopped, .release);
if (timer.dpc) |dpc| ke.dpc.enqueue(dpc, null);
// Wake whomever was waiting on the timer.
ki.wait.satisfy_wait(&timer.hdr);
timer.hdr.lock.release_no_ipl();
if (maybe_dpc) |dpc| ke.dpc.enqueue(dpc, null);
}
}
+2 -1
View File
@@ -5,6 +5,7 @@ const rtl = @import("rtl");
const std = @import("std");
const r = @import("root");
const ke = r.ke;
const ki = ke.private;
/// Header for waitable objects.
/// This must be added to any structured which is considered waitable.
@@ -86,7 +87,7 @@ pub fn wait_one(object: *DispatchHeader, timeout: ?r.Nanoseconds) !usize {
pub fn wait_any(objects: []*DispatchHeader, timeout: ?r.Nanoseconds, waitblocks: ?[*]WaitBlock) !usize {
const ipl = ke.ipl.raise(.Dispatch);
defer ke.ipl.lower(ipl);
const curtd = ke.curcpu().current_thread.?;
const curtd = ki.sched.percpu.local().current_thread.?;
const obj_count = objects.len;
const has_timeout = timeout != null;
const blocks = waitblocks orelse blk: {