mirror of
https://github.com/rdmsr/zag.git
synced 2026-10-30 06:48:22 +00:00
feat: fireworks!
This commit is contained in:
+41
-74
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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: {
|
||||
|
||||
Reference in New Issue
Block a user