mirror of
https://github.com/rdmsr/zag.git
synced 2026-10-27 13:58:20 +00:00
249 lines
5.8 KiB
Zig
249 lines
5.8 KiB
Zig
//! amd64-specific kernel code.
|
|
|
|
const r = @import("root");
|
|
const ke = r.ke;
|
|
const kep = ke.private;
|
|
const pl = r.pl;
|
|
const amd64 = r.arch;
|
|
|
|
const std = @import("std");
|
|
|
|
pub const tlb_max_pages = 33;
|
|
|
|
pub const init = @import("init.zig");
|
|
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;
|
|
|
|
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),
|
|
};
|
|
|
|
const Context = ke.Thread.Context;
|
|
|
|
extern fn asm_thread_entry() void;
|
|
|
|
extern fn amd64_context_load(td: *ThreadContext) callconv(.c) void;
|
|
|
|
extern fn amd64_context_switch(
|
|
old: *Context,
|
|
new: *Context,
|
|
lock: *u8,
|
|
switching: *bool,
|
|
) callconv(.c) void;
|
|
|
|
extern fn amd64_context_switch_cont_n(
|
|
old: *Context,
|
|
new: *Context,
|
|
lock: *u8,
|
|
switching: *bool,
|
|
) callconv(.c) void;
|
|
|
|
extern fn amd64_call_continuation(
|
|
td: *ke.Thread,
|
|
ctx: *Context,
|
|
func: usize,
|
|
arg: ?*anyopaque,
|
|
) callconv(.c) noreturn;
|
|
|
|
export fn ki_free_old_stack(stack_top: usize) callconv(.c) void {
|
|
kep.thread.stack_cache_free(stack_top);
|
|
}
|
|
|
|
/// Trampoline to match the zig ABI.
|
|
export fn ki_continuation_trampoline(
|
|
func: usize,
|
|
arg: ?*anyopaque,
|
|
) noreturn {
|
|
ke.ipl.lower(.Passive);
|
|
const f: *const fn (?*anyopaque) void = @ptrFromInt(func);
|
|
f(arg);
|
|
@panic("Continuation returned");
|
|
}
|
|
|
|
pub const ThreadContext = extern struct {
|
|
rsp: u64 align(1) = 0xDEADC0D3,
|
|
|
|
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));
|
|
}
|
|
|
|
pub fn init_with_stack(
|
|
stack: r.VAddr,
|
|
) @This() {
|
|
return .{
|
|
.rsp = stack,
|
|
};
|
|
}
|
|
|
|
pub fn reset(
|
|
self: *@This(),
|
|
stack_top: r.VAddr,
|
|
entry: *const fn (?*anyopaque) void,
|
|
arg: ?*anyopaque,
|
|
) @This() {
|
|
var sp: usize = stack_top;
|
|
|
|
sp = sp & ~@as(usize, 15);
|
|
sp = sp - @sizeOf(ThreadFrame);
|
|
sp = (sp & ~@as(usize, 15)) - 8;
|
|
|
|
const frame: *ThreadFrame = @ptrFromInt(sp);
|
|
|
|
frame.* = std.mem.zeroes(ThreadFrame);
|
|
|
|
self.rsp = @intFromPtr(frame);
|
|
frame.rip = @intFromPtr(&asm_thread_entry);
|
|
frame.r12 = @intFromPtr(entry);
|
|
frame.r13 = @intFromPtr(arg);
|
|
|
|
return self.*;
|
|
}
|
|
|
|
pub fn load(self: *ThreadContext) void {
|
|
amd64_context_load(self);
|
|
}
|
|
};
|
|
|
|
pub fn switch_normal_to_normal(
|
|
old: *Context,
|
|
new: *Context,
|
|
) void {
|
|
const thread: *ke.Thread = @alignCast(@fieldParentPtr("context", old));
|
|
amd64_context_switch(
|
|
old,
|
|
new,
|
|
&thread.lock.inner.locked.raw,
|
|
&thread.switching.raw,
|
|
);
|
|
}
|
|
|
|
pub fn switch_cont_to_normal(
|
|
old: *Context,
|
|
new: *Context,
|
|
) void {
|
|
const td: *ke.Thread = @alignCast(@fieldParentPtr("context", old));
|
|
|
|
amd64_context_switch_cont_n(
|
|
old,
|
|
new,
|
|
&td.lock.inner.locked.raw,
|
|
&td.switching.raw,
|
|
);
|
|
}
|
|
|
|
pub fn call_continuation(
|
|
ctx: *Context,
|
|
continuation: ke.Continuation,
|
|
) noreturn {
|
|
const td: *ke.Thread = @alignCast(@fieldParentPtr("context", ctx));
|
|
|
|
amd64_call_continuation(
|
|
td,
|
|
ctx,
|
|
@intFromPtr(continuation.func),
|
|
continuation.arg,
|
|
);
|
|
}
|
|
|
|
inline fn percpu_ptr_for(variable: anytype, cpu: u32) @TypeOf(variable) {
|
|
return @ptrFromInt(@intFromPtr(variable) +% cpu_offsets[cpu]);
|
|
}
|
|
|
|
pub inline fn percpu_ptr_other(variable: anytype, id: u32) @TypeOf(variable) {
|
|
return percpu_ptr_for(variable, id);
|
|
}
|
|
|
|
pub inline fn halt() void {
|
|
asm volatile ("hlt");
|
|
}
|
|
|
|
pub inline fn percpu_ptr(variable: anytype) @TypeOf(variable) {
|
|
const offset = asm volatile ("mov %%gs:(%[self]), %[out]"
|
|
: [out] "=r" (-> u64),
|
|
: [self] "r" (&cpu_self_offset),
|
|
);
|
|
|
|
return @ptrFromInt(offset +% @intFromPtr(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" (cr8_value),
|
|
);
|
|
}
|
|
|
|
pub inline fn enable_interrupts() void {
|
|
amd64.sti();
|
|
}
|
|
|
|
pub inline fn disable_interrupts() bool {
|
|
const ie = amd64.rflags().interrupt_enable;
|
|
|
|
amd64.cli();
|
|
|
|
return ie;
|
|
}
|
|
|
|
pub inline fn restore_interrupts(state: bool) void {
|
|
if (state) {
|
|
enable_interrupts();
|
|
} else {
|
|
_ = disable_interrupts();
|
|
}
|
|
}
|
|
|
|
pub inline fn flush_full_tlb() void {
|
|
const prev_cr3 = amd64.read_cr(3);
|
|
// Reload CR3 to flush the entire TLB.
|
|
// This works because we assume global pages are never unmapped.
|
|
// If they are in the future, this should toggle CR4.PGE instead.
|
|
amd64.write_cr(3, prev_cr3);
|
|
}
|
|
|
|
pub inline fn flush_tlb(va: r.VAddr) void {
|
|
amd64.invlpg(va);
|
|
}
|
|
|
|
// Inline asm so the compiler has a chance to inline it.
|
|
pub inline fn raise_software_ipl(new: ke.Ipl, old: *ke.Ipl) bool {
|
|
return asm volatile (
|
|
\\movb %%gs:cpu_ipl, %%cl
|
|
\\movb %%cl, (%[old_ptr])
|
|
\\movb %[new_val], %%gs:cpu_ipl
|
|
\\cmpb %%cl, %[new_val]
|
|
\\setl %[result]
|
|
: [result] "=r" (-> bool),
|
|
: [new_val] "r" (new),
|
|
[old_ptr] "r" (old),
|
|
: .{ .rcx = true, .memory = true });
|
|
}
|
|
|
|
pub inline fn lower_software_ipl(new: ke.Ipl, old: *ke.Ipl) bool {
|
|
return asm volatile (
|
|
\\movb %%gs:cpu_ipl, %%cl
|
|
\\movb %%cl, (%[old_ptr])
|
|
\\movb %[new_val], %%gs:cpu_ipl
|
|
\\cmpb %[new_val], %%cl
|
|
\\setl %[result]
|
|
: [result] "=r" (-> bool),
|
|
: [new_val] "r" (new),
|
|
[old_ptr] "r" (old),
|
|
: .{ .rcx = true, .memory = true });
|
|
}
|