From c6e63ed2bfc30fa48f45a689d6c448cc10623743 Mon Sep 17 00:00:00 2001 From: iretq Date: Sat, 28 Mar 2026 19:14:17 +0100 Subject: [PATCH] feat(pl/ke): initial amd64 support --- build.zig | 14 +-- build/config.zig | 28 +++++- build/image.zig | 1 + build/linker-scripts/x86_64.lds | 12 +++ build/run.zig | 1 - src/kern/amd64/impl.zig | 148 ++++++++++++++++++++++++++++++++ src/kern/init.zig | 15 +++- src/kern/ipl.zig | 4 +- src/kern/panic.zig | 11 ++- src/kern/um/impl.zig | 6 +- src/main.zig | 58 ------------- src/platform/amd64/entry.zig | 3 - src/platform/amd64/impl.zig | 23 ++++- src/platform/limine/entry.zig | 31 +++++++ src/platform/root.zig | 4 + src/platform/um/entry.zig | 1 + src/platform/um/timer.zig | 4 +- src/root.zig | 2 + 18 files changed, 279 insertions(+), 87 deletions(-) delete mode 100644 src/main.zig delete mode 100644 src/platform/amd64/entry.zig create mode 100644 src/platform/limine/entry.zig diff --git a/build.zig b/build.zig index 6613c21..ec943d5 100644 --- a/build.zig +++ b/build.zig @@ -150,6 +150,7 @@ fn targetQueryForPlatform(plat: config.Platform) std.Target.Query { switch (plat.arch) { .x86_64 => { const f = std.Target.Cpu.Feature.FeatureSetFns(std.Target.x86.Feature); + q.cpu_features_add = f.featureSet(&.{.soft_float}); q.cpu_features_sub = f.featureSet(&.{ .x87, .mmx, .sse, .sse2 }); }, .aarch64 => { @@ -169,7 +170,14 @@ fn addKernel(b: *std.Build, plat: config.Platform, optimize: std.builtin.Optimiz const name = b.fmt("kernel-{s}", .{@tagName(plat.arch)}); - const root_source_file = if (plat.os == .freestanding) "src/main.zig" else "src/platform/um/entry.zig"; + const platform_name = if (plat.bootloader) |bl| + switch (bl) { + .Limine => "limine", + } + else + @tagName(plat.arch); + + const root_source_file = b.fmt("src/platform/{s}/entry.zig", .{platform_name}); const kernel = b.addExecutable(.{ .name = name, @@ -191,12 +199,8 @@ fn addKernel(b: *std.Build, plat: config.Platform, optimize: std.builtin.Optimiz if (plat.os == .freestanding) { kernel.linkage = .static; kernel.pie = false; - kernel.bundle_compiler_rt = false; - kernel.bundle_ubsan_rt = false; - kernel.root_module.link_libc = false; kernel.root_module.pic = false; - kernel.root_module.single_threaded = true; kernel.root_module.strip = false; kernel.root_module.stack_check = false; kernel.root_module.stack_protector = false; diff --git a/build/config.zig b/build/config.zig index ea25810..ae505c0 100644 --- a/build/config.zig +++ b/build/config.zig @@ -3,9 +3,15 @@ const builtin = @import("builtin"); pub const Config = std.StringHashMap([]const u8); +pub const Bootloader = enum { + Limine, +}; + pub const Platform = struct { arch: std.Target.Cpu.Arch, os: std.Target.Os.Tag, + + bootloader: ?Bootloader = null, }; pub fn parseConfig(b: *std.Build) !Config { @@ -73,11 +79,25 @@ pub fn getPlatform(config: Config) !Platform { .{ "CONFIG_ARCH_UM", Platform{ .arch = builtin.cpu.arch, .os = .linux } }, }; - inline for (keys) |entry| { - if (config.get(entry[0])) |val| { - if (std.mem.eql(u8, val, "y")) return entry[1]; + var ret = blk: { + inline for (keys) |entry| { + if (config.get(entry[0])) |val| { + if (std.mem.eql(u8, val, "y")) break :blk entry[1]; + } } + + return error.NoArchConfigured; + }; + + if (ret.arch == .x86_64 or ret.arch == .aarch64) { + ret.bootloader = blk: { + if (config.get("CONFIG_BOOT_LIMINE")) |val| { + if (std.mem.eql(u8, val, "y")) break :blk .Limine; + } + + return error.NoBootloaderConfigured; + }; } - return error.NoArchConfigured; + return ret; } diff --git a/build/image.zig b/build/image.zig index 29e21b1..80e2f69 100644 --- a/build/image.zig +++ b/build/image.zig @@ -94,6 +94,7 @@ pub fn addIso( mkiso.addArg("-o"); const iso_out = mkiso.addOutputFileArg(b.fmt("{s}.iso", .{image_name})); mkiso.step.dependOn(&prep.step); + _ = mkiso.captureStdErr(.{}); if (arch == .x86_64) { const bios_install = b.addRunArtifact(limine_tool); diff --git a/build/linker-scripts/x86_64.lds b/build/linker-scripts/x86_64.lds index 0f0da9d..b5df9b5 100644 --- a/build/linker-scripts/x86_64.lds +++ b/build/linker-scripts/x86_64.lds @@ -44,6 +44,12 @@ SECTIONS *(.rodata .rodata.*) } :rodata + .percpu_init_array : { + __init_array_percpu_start = .; + KEEP(*(.percpu_init_array)) + __init_array_percpu_end = .; + } :rodata + /* Add a .note.gnu.build-id output section in case a build ID flag is added to the */ /* linker command. */ .note.gnu.build-id : { @@ -57,6 +63,12 @@ SECTIONS *(.data .data.*) } :data + .data.percpu : { + __percpu_start = .; + *(.data.percpu) + __percpu_end = .; + } :data + /* NOTE: .bss needs to be the last thing mapped to :data, otherwise lots of */ /* unnecessary zeros will be written to the binary. */ /* If you need, for example, .init_array and .fini_array, those should be placed */ diff --git a/build/run.zig b/build/run.zig index 9cf68ab..05fedcc 100644 --- a/build/run.zig +++ b/build/run.zig @@ -44,7 +44,6 @@ pub fn addRun(b: *std.Build, kernel: *std.Build.Step.Compile, plat: config.Platf if (debug) { qemu.addArgs(&.{ - "-serial", "stdio", "-d", "int", "-D", "qemu.log", "-no-reboot", "-no-shutdown", diff --git a/src/kern/amd64/impl.zig b/src/kern/amd64/impl.zig index e69de29..014e27d 100644 --- a/src/kern/amd64/impl.zig +++ b/src/kern/amd64/impl.zig @@ -0,0 +1,148 @@ +const b = @import("base"); +const ke = b.ke; + +const std = @import("std"); + +pub const Cpu = struct { + self_ptr: *Cpu, + percpu_offset: usize, +}; + +pub const ThreadContext = struct { + rdi: u64, + rsi: u64, + rbx: u64, + rsp: u64, + rbp: u64, + r12: u64, + r13: u64, + r14: u64, + r15: u64, + + fn thread_trampoline(entry: usize, arg: usize) callconv(.c) void { + const entry_fn: *const fn (?*anyopaque) void = @ptrFromInt(entry); + entry_fn(@ptrFromInt(arg)); + // TODO: This shouldn't be reached!!! + } + + pub fn init(stack: b.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 = stack + stack_size; + { + sp -= @sizeOf(usize); + @as(*usize, @ptrFromInt(sp)).* = 0; + + sp -= @sizeOf(usize); + @as(*usize, @ptrFromInt(sp)).* = @intFromPtr(&thread_trampoline); + } + ctx.rsp = sp; + + 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), + ); + } +}; + +pub fn curcpu() *ke.Cpu { + const asm_template = std.fmt.comptimePrint("movq %%gs:{d}, %[ret]", .{@offsetOf(Cpu, "self_ptr")}); + const impl_cpu = asm volatile (asm_template + : [ret] "=r" (-> *Cpu), + ); + + return @fieldParentPtr("impl", impl_cpu); +} + +inline fn percpu_ptr_for(variable: anytype, cpu: *ke.Cpu) @TypeOf(variable) { + return @ptrFromInt(@intFromPtr(variable) +% cpu.impl.percpu_offset); +} + +pub inline fn percpu_ptr_other(variable: anytype, id: usize) @TypeOf(variable) { + return percpu_ptr_for(variable, ke.cpus[id]); +} + +pub inline fn percpu_ptr(variable: anytype) @TypeOf(variable) { + return percpu_ptr_for(variable, curcpu()); +} + +pub inline fn set_hardware_ipl(_: ke.Ipl) void {} + +pub inline fn enable_interrupts() void { + asm volatile ("sti"); +} + +pub inline fn disable_interrupts() bool { + const rflags = asm volatile ( + \\cli + \\pushfq + \\popq %[rflags] + : [rflags] "=r" (-> usize), + ); + + return rflags & (1 << 9) != 0; +} + +pub inline fn restore_interrupts(state: bool) void { + if (state) { + enable_interrupts(); + } else { + _ = disable_interrupts(); + } +} + +pub inline fn send_resched_ipi(_: *ke.Cpu) void { + @panic("TODO send_resched_ipi"); +} diff --git a/src/kern/init.zig b/src/kern/init.zig index 47d3626..2c10e6f 100644 --- a/src/kern/init.zig +++ b/src/kern/init.zig @@ -14,8 +14,17 @@ fn handler(_: ?*anyopaque) void { while (true) {} } +var temp_stack1: [8192]u8 align(16) = undefined; +var temp_stack2: [8192]u8 align(16) = undefined; +var temp_stack3: [8192]u8 align(16) = undefined; + +var stacks: []const []u8 = &.{&temp_stack1, &temp_stack2, &temp_stack3}; +var which_stack: usize = 0; + fn make_thread(entry: *const fn (?*anyopaque) void, td: *ke.Thread, arg: ?*anyopaque) void { - const _stack = std.heap.page_allocator.alloc(u8, 16384) catch @panic("wtf"); + std.debug.assert(which_stack < 3); + const _stack = stacks[which_stack]; + which_stack += 1; td.init(@intFromPtr(_stack.ptr), 16384, entry, arg); } @@ -49,8 +58,8 @@ pub fn init(boot_info: *pl.BootInfo) linksection(b.init) void { ke.ipl.lower(ipl); } else { - make_thread(&b.ex.fireworks.start, &threads[0], boot_info); - ke.sched.enqueue(&threads[0]); + // make_thread(&b.ex.fireworks.start, &threads[0], boot_info); + // ke.sched.enqueue(&threads[0]); } while (true) {} diff --git a/src/kern/ipl.zig b/src/kern/ipl.zig index d0bd87b..9ee6631 100644 --- a/src/kern/ipl.zig +++ b/src/kern/ipl.zig @@ -31,7 +31,7 @@ pub fn raise(new: Ipl) Ipl { const old = cpu.ipl; if (@intFromEnum(new) < @intFromEnum(old)) { - ke.panic("ke.ipl.raise(): Target IPL ({}) is lower than current IPL ({})\n", .{ new, old }); + std.debug.panic("ke.ipl.raise(): Target IPL ({}) is lower than current IPL ({})", .{ new, old }); } if (@intFromEnum(new) > @intFromEnum(Ipl.get_max_software())) { @@ -53,7 +53,7 @@ pub fn lower(new: Ipl) void { const old = cpu.ipl; if (@intFromEnum(new) > @intFromEnum(old)) { - ke.panic("ke.ipl.lower(): Target IPL ({}) is higher than current IPL ({})\n", .{ new, old }); + std.debug.panic("ke.ipl.lower(): Target IPL ({}) is higher than current IPL ({})", .{ new, old }); } if (@intFromEnum(new) <= @intFromEnum(Ipl.get_max_software()) and @intFromEnum(old) > @intFromEnum(Ipl.get_max_software())) { diff --git a/src/kern/panic.zig b/src/kern/panic.zig index 6cfa23b..4e06fb3 100644 --- a/src/kern/panic.zig +++ b/src/kern/panic.zig @@ -3,10 +3,13 @@ const b = @import("base"); const ke = b.ke; const ki = ke.private; -/// Panics! -pub fn panic(comptime fmt: []const u8, args: anytype) noreturn { - // FIXME: this is probably broken. - std.log.err("KERNEL PANIC: " ++ fmt, args); +pub fn panic( + msg: []const u8, + first_trace_addr: ?usize, +) noreturn { + _ = first_trace_addr; + + std.log.err("KERNEL PANIC: {s}", .{msg}); while (true) {} } diff --git a/src/kern/um/impl.zig b/src/kern/um/impl.zig index bd5661e..fda9811 100644 --- a/src/kern/um/impl.zig +++ b/src/kern/um/impl.zig @@ -20,7 +20,7 @@ fn set_signals(enabled: bool) bool { const how = if (enabled) c.SIG_UNBLOCK else c.SIG_BLOCK; if (c.pthread_sigmask(how, &newmask, &oldmask) != 0) { - ke.panic("pthread_sigmask failed", .{}); + @panic("pthread_sigmask failed"); } return c.sigismember(&oldmask, c.SIGUSR1) == 0; @@ -67,7 +67,7 @@ pub const ThreadContext = struct { var new: ThreadContext = .{ .ucontext = undefined }; if (c.getcontext(&new.ucontext) == -1) { - ke.panic("getcontext() failed", .{}); + @panic("getcontext() failed"); } new.ucontext.uc_stack.ss_sp = @ptrFromInt(stack); @@ -99,7 +99,7 @@ pub const ThreadContext = struct { thread.lock.release_no_ipl(); if (c.swapcontext(&self.ucontext, &new.ucontext) == -1) { - ke.panic("swapcontext() failed", .{}); + @panic("swapcontext() failed"); } } }; diff --git a/src/main.zig b/src/main.zig deleted file mode 100644 index 9193797..0000000 --- a/src/main.zig +++ /dev/null @@ -1,58 +0,0 @@ -const std = @import("std"); -const ksyms = @import("ksyms"); -const pl = @import("base").pl; -pub const rtl = @import("rtl"); - -const DebugWriter = struct { - pub const Error = error{}; - pub const Writer = std.io.GenericWriter(*DebugWriter, Error, write); - - fn write(self: *DebugWriter, bytes: []const u8) Error!usize { - _ = self; - for (bytes) |c| pl.debug_write(c); - return bytes.len; - } - - fn writer(self: *DebugWriter) Writer { - return .{ .context = self }; - } -}; - -var debug_writer = DebugWriter{}; - -export fn kmain() callconv(.c) void { - print("hello from kernel!\n", .{}); - - for (ksyms.ksyms) |sym| { - print(" 0x{x:0>16} {s}\n", .{ sym.addr, sym.name }); - } - - pl.first_init(); - - while (true) { - asm volatile ("hlt"); - } -} - -export fn memset(dest: [*]u8, c: u8, n: usize) [*]u8 { - for (dest[0..n]) |*b| b.* = c; - return dest; -} - -export fn memcpy(dest: [*]u8, src: [*]const u8, n: usize) [*]u8 { - for (0..n) |i| dest[i] = src[i]; - return dest; -} - -export fn memmove(dest: [*]u8, src: [*]const u8, n: usize) [*]u8 { - if (@intFromPtr(dest) < @intFromPtr(src)) { - for (0..n) |i| dest[i] = src[i]; - } else { - var i = n; - while (i > 0) { - i -= 1; - dest[i] = src[i]; - } - } - return dest; -} diff --git a/src/platform/amd64/entry.zig b/src/platform/amd64/entry.zig deleted file mode 100644 index 534a03b..0000000 --- a/src/platform/amd64/entry.zig +++ /dev/null @@ -1,3 +0,0 @@ -const b = @import("base"); - -pub fn first_init() linksection(b.init) void {} diff --git a/src/platform/amd64/impl.zig b/src/platform/amd64/impl.zig index f77b5ac..58f1316 100644 --- a/src/platform/amd64/impl.zig +++ b/src/platform/amd64/impl.zig @@ -1,6 +1,25 @@ -const entry = @import("entry.zig"); +const b = @import("base"); +const ki = b.ke.private; -pub const first_init = entry.first_init; +pub const name = "AMD64"; + +pub fn first_init() void {} + +pub fn early_init() void { + const impl_cpu = &ki.bootstrap_cpu.impl; + impl_cpu.self_ptr = impl_cpu; + impl_cpu.percpu_offset = 0; + + const impl_cpu_ptr = @intFromPtr(impl_cpu); + asm volatile ("wrmsr" + : + : [_] "{eax}" (@as(u32, @intCast(impl_cpu_ptr & 0xFFFFFFFF))), + [_] "{edx}" (@as(u32, @truncate(impl_cpu_ptr >> 32))), + [_] "{ecx}" (0xC000_0101), + ); +} + +pub fn late_init() void {} pub fn devices_init() void {} diff --git a/src/platform/limine/entry.zig b/src/platform/limine/entry.zig new file mode 100644 index 0000000..888a4b8 --- /dev/null +++ b/src/platform/limine/entry.zig @@ -0,0 +1,31 @@ +const b = @import("base"); +const ksyms = @import("ksyms"); + +const pl = b.pl; +const ke = b.ke; + +pub const std_options = b.std_options; +pub const panic = b.panic; + +pub const rtl = @import("rtl"); + +const std = @import("std"); + +var boot_info: pl.BootInfo = undefined; + +export fn kmain() callconv(.c) void { + std.log.info("hello from kernel!", .{}); + + for (ksyms.ksyms) |sym| { + std.log.info(" 0x{x:0>16} {s}", .{ sym.addr, sym.name }); + } + + boot_info.memory_map.entry_count = 0; + + var cpus: [1]*ke.Cpu = .{&ke.private.bootstrap_cpu}; + + ke.ncpus = 1; + ke.cpus = &cpus; + + ke.private.init(&boot_info); +} diff --git a/src/platform/root.zig b/src/platform/root.zig index bd24b9d..85c1cda 100644 --- a/src/platform/root.zig +++ b/src/platform/root.zig @@ -10,6 +10,10 @@ else if (@hasDecl(config, "CONFIG_ARCH_UM")) else @compileError("unsupported architecture"); +pub inline fn first_init() void { + return impl.first_init(); +} + /// Early platform-specific initialization, called before anything else. pub inline fn early_init() void { return impl.early_init(); diff --git a/src/platform/um/entry.zig b/src/platform/um/entry.zig index 6c50e1e..fec2269 100644 --- a/src/platform/um/entry.zig +++ b/src/platform/um/entry.zig @@ -1,6 +1,7 @@ const b = @import("base"); pub const std_options = b.std_options; +pub const panic = b.panic; pub const rtl = @import("rtl"); diff --git a/src/platform/um/timer.zig b/src/platform/um/timer.zig index 4abc321..5330b94 100644 --- a/src/platform/um/timer.zig +++ b/src/platform/um/timer.zig @@ -27,7 +27,7 @@ pub fn arm_timer(ns: b.Nanoseconds) void { }, .it_interval = .{ .tv_sec = 0, .tv_nsec = 0 } }; if (c.timer_settime(timerid, 0, &value, null) == -1) { - ke.panic("timer_settime failed", .{}); + @panic("timer_settime failed"); } } @@ -68,6 +68,6 @@ pub fn init_cpu() void { sev._sigev_un._tid = linux.gettid(); if (c.timer_create(c.CLOCK_MONOTONIC, &sev, &timerid) == -1) { - ke.panic("timer_create failed", .{}); + @panic("timer_create failed"); } } diff --git a/src/root.zig b/src/root.zig index 150ff8b..0ca3bc5 100644 --- a/src/root.zig +++ b/src/root.zig @@ -40,3 +40,5 @@ pub fn tib(comptime N: u32) usize { pub const std_options = std.Options{ .logFn = ke.log.log, }; + +pub const panic = std.debug.FullPanic(ke.panic);