everywhere: nuke UM

This commit is contained in:
Loïc Grégoire
2026-07-02 20:01:12 -04:00
parent efe5df1567
commit 4a03a10d0c
17 changed files with 49 additions and 783 deletions
+43 -62
View File
@@ -19,7 +19,7 @@ pub fn build(b: *std.Build) void {
return;
};
// Get the target platform (might be linux or freestanding)
// Get the target platform
const plat = config.getPlatform(cfg) catch {
b.default_step.dependOn(
&b.addFail("Invalid platform (this should not happen!)").step,
@@ -48,10 +48,10 @@ pub fn build(b: *std.Build) void {
rtl_module.addImport("rtl", rtl_module);
const arch = if (plat.os == .freestanding) switch (plat.arch) {
const arch = switch (plat.arch) {
.x86_64 => "amd64",
else => @tagName(plat.arch),
} else "um";
};
const arch_module = b.createModule(.{
.root_source_file = b.path(b.fmt("src/arch/{s}/root.zig", .{arch})),
@@ -132,24 +132,22 @@ fn targetQueryForPlatform(plat: config.Platform) std.Target.Query {
var q: std.Target.Query = .{
.cpu_arch = plat.arch,
.os_tag = plat.os,
.abi = if (plat.os == .freestanding) .none else .gnu,
.abi = .none,
.cpu_model = .baseline,
};
if (plat.os == .freestanding) {
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 => {
const f = std.Target.Cpu.Feature.FeatureSetFns(std.Target.aarch64.Feature);
q.cpu_features_sub = f.featureSet(&.{ .fp_armv8, .neon });
},
.riscv64 => {},
else => {},
}
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 => {
const f = std.Target.Cpu.Feature.FeatureSetFns(std.Target.aarch64.Feature);
q.cpu_features_sub = f.featureSet(&.{ .fp_armv8, .neon });
},
.riscv64 => {},
else => {},
}
return q;
@@ -181,59 +179,42 @@ fn addKernel(b: *std.Build, plat: config.Platform, optimize: std.builtin.Optimiz
const modules: []const *std.Build.Module = &.{ config_module, ksyms_module, rtl, arch, kernel.root_module };
if (plat.os == .freestanding) {
kernel.linkage = .static;
kernel.pie = false;
kernel.linkage = .static;
kernel.pie = false;
for (modules) |m| {
m.pic = false;
m.strip = false;
m.stack_check = false;
m.stack_protector = false;
m.unwind_tables = .none;
m.omit_frame_pointer = false;
for (modules) |m| {
m.pic = false;
m.strip = false;
m.stack_check = false;
m.stack_protector = false;
m.unwind_tables = .none;
m.omit_frame_pointer = false;
m.code_model = switch (plat.arch) {
.x86_64 => .kernel,
.riscv64 => .medium,
else => .large,
};
switch (plat.arch) {
.x86_64 => {
m.red_zone = false;
},
else => {},
}
}
kernel.entry = .{ .symbol_name = "kmain" };
m.code_model = switch (plat.arch) {
.x86_64 => .kernel,
.riscv64 => .medium,
else => .large,
};
switch (plat.arch) {
.x86_64 => {
kernel.root_module.addAssemblyFile(b.path("src/kern/amd64/locore.s"));
kernel.root_module.addAssemblyFile(b.path("src/platform/pc/ap.s"));
m.red_zone = false;
},
else => {},
}
kernel.linker_script = b.path(b.fmt("build/linker-scripts/{s}.lds", .{@tagName(plat.arch)}));
} else {
const translate_c = b.addTranslateC(.{
.root_source_file = b.path("src/platform/um/c.h"),
.target = target,
.optimize = optimize,
});
translate_c.addIncludePath(.{ .cwd_relative = "/usr/include" });
kernel.root_module.addImport("c", translate_c.createModule());
kernel.linker_script = b.path("build/linker-scripts/uml.lds");
kernel.root_module.link_libc = true;
kernel.linker_allow_shlib_undefined = true;
kernel.root_module.linkSystemLibrary("sdl2-compat", .{ .use_pkg_config = .force });
kernel.root_module.addLibraryPath(.{ .cwd_relative = "/usr/lib64" });
}
kernel.entry = .{ .symbol_name = "kmain" };
switch (plat.arch) {
.x86_64 => {
kernel.root_module.addAssemblyFile(b.path("src/kern/amd64/locore.s"));
kernel.root_module.addAssemblyFile(b.path("src/platform/pc/ap.s"));
},
else => {},
}
kernel.linker_script = b.path(b.fmt("build/linker-scripts/{s}.lds", .{@tagName(plat.arch)}));
return kernel;
}
+1 -3
View File
@@ -16,7 +16,6 @@ pub const Platform = struct {
const ArchEnum = enum {
amd64,
aarch64,
um,
};
const BootloaderEnum = enum {
@@ -38,9 +37,8 @@ pub fn getPlatform(config: Config) !Platform {
.arch = switch (config.arch) {
.amd64 => .x86_64,
.aarch64 => .aarch64,
.um => builtin.cpu.arch,
},
.os = if (config.arch == .um) .linux else .freestanding,
.os = .freestanding,
};
if (ret.os == .freestanding and (ret.arch == .x86_64 or ret.arch == .aarch64)) {
+3 -3
View File
@@ -47,6 +47,8 @@ pub fn addRun(b: *std.Build, kernel: *std.Build.Step.Compile, plat: config.Platf
qemu.addArgs(&.{
"-d",
"int",
"-s",
"-S",
"-D",
"qemu.log",
"-no-reboot",
@@ -56,7 +58,7 @@ pub fn addRun(b: *std.Build, kernel: *std.Build.Step.Compile, plat: config.Platf
switch (plat.arch) {
.x86_64 => {
qemu.addArgs(&.{ "-serial", "stdio", "-m", "1G", "-smp", "4" });
qemu.addArgs(&.{ "-serial", "stdio", "-m", "32M", "-smp", "4" });
if (!debug and builtin.target.os.tag == .linux and builtin.cpu.arch == .x86_64) qemu.addArgs(&.{ "-enable-kvm", "-cpu", "host,+invtsc" });
},
@@ -66,7 +68,5 @@ pub fn addRun(b: *std.Build, kernel: *std.Build.Step.Compile, plat: config.Platf
}
run_step.dependOn(&qemu.step);
} else {
run_step.dependOn(&b.addRunArtifact(kernel).step);
}
}
-11
View File
@@ -9,7 +9,6 @@
.options = .{
.{ .value = "amd64" },
.{ .value = "aarch64" },
.{ .value = "um", .label = "Usermode (Linux)" },
},
},
.{
@@ -20,11 +19,6 @@
.options = .{
.{ .value = "limine", .label = "Limine" },
},
.depends_on = .{
.not = .{
.key_eq = .{ .key = "arch", .value = "um" },
},
},
},
.{
.kind = .menu,
@@ -37,7 +31,6 @@
.default = .{ .int = 10 },
.help = "This option sets the default time slice (in milliseconds) used by the scheduler.",
},
.{
.kind = .int,
.key = "sched_balance_interval",
@@ -45,7 +38,6 @@
.default = .{ .int = 1000 },
.help = "This option sets the rate (in milliseconds) at which thread load balancing is performed.",
},
.{
.kind = .int,
.key = "ncpus",
@@ -54,7 +46,6 @@
.range = .{ .min = 1, .max = 16384 },
.help = "This option sets the maximum number of CPUs the kernel can support.",
},
.{
.kind = .int,
.key = "log_buffer_shift",
@@ -62,11 +53,9 @@
.default = .{ .int = 14 },
.help = "This option sets the size of the kernel log buffer as a power of two. The actual size is calculated as 2 raised to the power of this value (i.e., 2^LOG_BUFFER_SHIFT bytes).",
},
.{
.kind = .menu,
.label = "Memory Management Options",
.entries = .{
.{
.kind = .menu,
-1
View File
@@ -1 +0,0 @@
pub const name = "um";
+1 -4
View File
@@ -87,11 +87,8 @@ pub fn panic_with_frame(
pub fn panic(
msg: []const u8,
first_trace_addr: ?usize,
_: ?usize,
) noreturn {
if (config.arch == .um) {
std.debug.defaultPanic(msg, first_trace_addr);
}
panic_lock.acquire_no_ipl();
panic_with_frame(msg, @frameAddress());
}
-1
View File
@@ -5,7 +5,6 @@ const rtl = @import("rtl");
pub const impl = switch (config.arch) {
.amd64 => @import("amd64/impl.zig"),
.um => @import("um/impl.zig"),
else => @compileError("unsupported architecture"),
};
-135
View File
@@ -1,135 +0,0 @@
const r = @import("root");
const c = r.pl.impl.c;
const ke = r.ke;
const ki = ke.private;
fn set_signals(enabled: bool) bool {
var newmask: c.sigset_t = undefined;
var oldmask: c.sigset_t = undefined;
_ = c.sigemptyset(&newmask);
_ = c.sigaddset(&newmask, c.SIGUSR1);
_ = c.sigaddset(&newmask, c.SIGUSR2);
_ = c.sigaddset(&newmask, c.SIGALRM);
const how = if (enabled) c.SIG_UNBLOCK else c.SIG_BLOCK;
if (c.pthread_sigmask(how, &newmask, &oldmask) != 0) {
@panic("pthread_sigmask failed");
}
return c.sigismember(&oldmask, c.SIGUSR1) == 0;
}
threadlocal var hardware_ipl: ke.Ipl = .Passive;
threadlocal var interrupts_enabled: bool = true;
pub fn set_hardware_ipl(ipl: ke.Ipl) void {
hardware_ipl = ipl;
update_signals();
}
pub fn disable_interrupts() bool {
const old = interrupts_enabled;
interrupts_enabled = false;
update_signals();
return old;
}
pub fn enable_interrupts() bool {
const old = interrupts_enabled;
interrupts_enabled = true;
update_signals();
return old;
}
pub fn restore_interrupts(val: bool) void {
interrupts_enabled = val;
update_signals();
}
fn update_signals() void {
const should_enable = interrupts_enabled and
@intFromEnum(hardware_ipl) <= @intFromEnum(ke.Ipl.get_max_software());
_ = set_signals(should_enable);
}
pub fn send_resched_ipi(cpu: u32) void {
_ = c.pthread_kill(r.pl.impl.percpu.remote(cpu).pthread, c.SIGUSR1);
}
pub const ThreadContext = struct {
ucontext: c.ucontext_t,
old_lock: ?*ke.SpinLock,
fn real_entry(entry_int: c_ulong, arg_int: c_ulong) callconv(.c) void {
if (ki.sched.percpu.local().current_thread.?.context.old_lock) |lock| {
lock.release_no_ipl();
ki.sched.percpu.local().current_thread.?.context.old_lock = null;
}
const entry: *const fn (?*anyopaque) void = @ptrFromInt(entry_int);
const arg: ?*anyopaque = @ptrFromInt(arg_int);
ke.ipl.lower(.Passive);
entry(arg);
}
pub fn init(stack: r.VAddr, stack_size: usize, entry: *const fn (?*anyopaque) void, arg: ?*anyopaque) ThreadContext {
var new: ThreadContext = .{ .ucontext = undefined, .old_lock = null };
if (c.getcontext(&new.ucontext) == -1) {
@panic("getcontext() failed");
}
new.ucontext.uc_stack.ss_sp = @ptrFromInt(stack);
new.ucontext.uc_stack.ss_size = stack_size;
// No successor
new.ucontext.uc_link = null;
const entry_int: usize = @intFromPtr(entry);
const arg_int: usize = @intFromPtr(arg);
c.makecontext(
&new.ucontext,
@ptrCast(&real_entry),
2,
@as(c_ulong, entry_int),
@as(c_ulong, arg_int),
);
return new;
}
pub fn load(self: *ThreadContext) void {
_ = c.setcontext(&self.ucontext);
}
pub fn switch_to(self: *ThreadContext, new: *ThreadContext) void {
const thread: *ke.Thread = @fieldParentPtr("context", self);
new.old_lock = &thread.lock;
if (c.swapcontext(&self.ucontext, &new.ucontext) == -1) {
@panic("swapcontext() failed");
}
if (self.old_lock) |lock| {
self.old_lock = null;
lock.release_no_ipl();
}
}
};
const bootstrap_offsets = [1]usize{0};
pub fn early_init() void {
// Ensure we can use per-cpu data.
r.pl.impl.cpu_offsets = @constCast(&bootstrap_offsets);
}
pub inline fn percpu_ptr_other(variable: anytype, cpu_id: usize) @TypeOf(variable) {
return @ptrFromInt(@intFromPtr(variable) +% r.pl.impl.cpu_offsets[cpu_id]);
}
pub inline fn percpu_ptr(variable: anytype) @TypeOf(variable) {
return @ptrFromInt(@intFromPtr(variable) +% r.pl.impl.cpu_offsets[r.pl.impl.my_cpu_id]);
}
+1 -3
View File
@@ -103,9 +103,7 @@ fn map_kernel(boot_info: *pl.BootInfo) void {
pub fn init(boot_info: *pl.BootInfo) linksection(r.init) void {
mi.phys.init(boot_info);
if (config.arch != .um) {
map_kernel(boot_info);
}
map_kernel(boot_info);
mi.kernel_space.pmap.activate();
mi.phys.init_pfndb();
mi.zone.early_init();
-1
View File
@@ -29,7 +29,6 @@ pub const PMapLevel = struct {
pub const impl = switch (config.arch) {
.amd64 => @import("amd64/impl.zig"),
.um => @import("um/impl.zig"),
else => @compileError("unsupported architecture"),
};
-80
View File
@@ -1,80 +0,0 @@
const r = @import("root");
const std = @import("std");
const linux = std.os.linux;
pub var kernel_heap_base: usize = 0;
pub var pfndb_base: usize = 0;
pub const hhdm_minimum_max_address: usize = 0;
var page_table: std.AutoHashMap(usize, usize) = .init(std.heap.page_allocator);
pub fn init_kernel() void {
kernel_heap_base = linux.mmap(
null,
r.gib(8),
.{ .READ = true, .WRITE = true },
.{ .ANONYMOUS = true, .TYPE = .PRIVATE },
-1,
0,
);
r.pl.impl.check_function("mmap", kernel_heap_base);
pfndb_base = linux.mmap(
null,
r.gib(1),
.{ .READ = true, .WRITE = true },
.{ .ANONYMOUS = true, .TYPE = .PRIVATE },
-1,
0,
);
r.pl.impl.check_function("mmap", pfndb_base);
}
pub fn phys_to_virt(pa: usize) usize {
return r.pl.impl.global_state.phys_base + pa;
}
pub fn virt_to_phys(va: usize) usize {
return va - r.pl.impl.global_state.phys_base;
}
pub const PMap = struct {
const Self = @This();
pub fn map_from(_: *Self, va: r.VAddr, size: usize, source: anytype) void {
var remain = size;
while (remain > 0) {
const item = source.next(va);
var posix_flags: std.c.PROT = .{};
posix_flags.READ = item.flags.read;
posix_flags.WRITE = item.flags.write;
posix_flags.EXEC = item.flags.execute;
_ = std.c.mmap(
@ptrFromInt(va),
size,
posix_flags,
.{ .FIXED = true, .TYPE = .PRIVATE },
r.pl.impl.global_state.phys_memory_memfd,
@bitCast(item.pa),
);
page_table.put(va & ~@as(usize, 0xFFF), item.pa) catch unreachable;
remain -|= item.len;
}
}
pub fn query(_: *Self, va: r.VAddr) ?r.PAddr {
const page = va & ~@as(usize, 0xFFF);
const pa = page_table.get(page) orelse return null;
return pa;
}
pub fn activate(_: *Self) void {}
};
-1
View File
@@ -5,7 +5,6 @@ const config = @import("config");
pub const impl = switch (config.arch) {
.amd64 => @import("pc/impl.zig"),
.um => @import("um/impl.zig"),
else => @compileError("unsupported architecture"),
};
-9
View File
@@ -1,9 +0,0 @@
#define _GNU_SOURCE 1
#include <SDL2/SDL.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <ucontext.h>
#include <unistd.h>
-186
View File
@@ -1,186 +0,0 @@
const r = @import("root");
pub const std_options = r.std_options;
pub const panic = r.panic;
const c = @import("c");
const std = @import("std");
const ke = r.ke;
const pl = r.pl;
const default_mem_size = r.gib(1);
fn parse_mem_size(str: []const u8) !usize {
if (str.len == 0) return default_mem_size;
const last = str[str.len - 1];
const mul: usize = switch (std.ascii.toLower(last)) {
'k' => r.kib(1),
'm' => r.mib(1),
'g' => r.gib(1),
else => 1,
};
const digits = if (mul == 1) str else str[0 .. str.len - 1];
const size = try std.fmt.parseInt(usize, digits, 10);
return size * mul;
}
var gui_started = std.atomic.Value(bool).init(false);
fn gui_thread() void {
if (c.SDL_Init(c.SDL_INIT_VIDEO) != 0) {
std.debug.panic("Failed to initialize SDL: {s}\n", .{c.SDL_GetError()});
}
const window = c.SDL_CreateWindow(
"Zag (UM)",
c.SDL_WINDOWPOS_UNDEFINED,
c.SDL_WINDOWPOS_UNDEFINED,
800,
600,
c.SDL_WINDOW_SHOWN,
);
if (window == null) {
std.debug.panic("Failed to create window: {s}\n", .{c.SDL_GetError()});
}
const renderer = c.SDL_CreateRenderer(window.?, -1, c.SDL_RENDERER_ACCELERATED);
if (renderer == null) {
std.debug.panic("Failed to create renderer: {s}\n", .{c.SDL_GetError()});
}
const fb_surface: ?*c.SDL_Surface = c.SDL_CreateRGBSurface(0, 800, 600, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0);
if (fb_surface == null) {
std.debug.panic("Failed to create surface: {s}\n", .{c
.SDL_GetError()});
}
const surface = fb_surface.?;
const texture = c.SDL_CreateTexture(
renderer.?,
c.SDL_PIXELFORMAT_RGB888,
c.SDL_TEXTUREACCESS_STREAMING,
800,
600,
);
pl.impl.global_state.boot_info.framebuffer = .{
.address = @intFromPtr(surface.pixels),
.width = 800,
.height = 600,
.pitch = @intCast(surface.pitch),
.bpp = 32,
};
gui_started.store(true, .release);
// Mask all signals.
var sigset: c.sigset_t = undefined;
_ = c.sigfillset(&sigset);
_ = c.pthread_sigmask(c.SIG_BLOCK, &sigset, null);
var event: c.SDL_Event = undefined;
var quit = false;
while (!quit) {
const frame_start = c.SDL_GetTicks();
while (c.SDL_PollEvent(&event) != 0) {
if (event.type == c.SDL_QUIT) {
quit = true;
}
}
// Copy the framebuffer (surface) to a texture and render it.
_ = c.SDL_UpdateTexture(texture, null, surface.pixels, surface.pitch);
_ = c.SDL_RenderClear(renderer.?);
_ = c.SDL_RenderCopy(renderer.?, texture, null, null);
_ = c.SDL_RenderPresent(renderer.?);
const frame_time = c.SDL_GetTicks() - frame_start;
if (frame_time < 16) {
c.SDL_Delay(16 - frame_time);
}
}
_ = c.SDL_DestroyTexture(texture);
// Exit.
c.SDL_Quit();
std.process.exit(0);
}
fn start_gui() void {
_ = std.Thread.spawn(.{ .allocator = std.heap.page_allocator }, gui_thread, .{}) catch unreachable;
}
pub fn main(init: std.process.Init) !void {
var args = try init.minimal.args.iterateAllocator(init.gpa);
defer args.deinit();
const exe = args.next().?;
var mem_size: usize = default_mem_size;
var cmdline: ?[]const u8 = null;
var ncpus: usize = 1;
var gui = false;
while (args.next()) |arg| {
if (std.mem.eql(u8, arg, "-m") or std.mem.eql(u8, arg, "--mem-size")) {
const val = args.next() orelse fatal("--mem-size requires a value", .{});
mem_size = parse_mem_size(val) catch fatal("invalid mem-size", .{});
} else if (std.mem.eql(u8, arg, "-c") or std.mem.eql(u8, arg, "--cmdline")) {
cmdline = args.next() orelse fatal("--cmdline requires a value", .{});
} else if (std.mem.eql(u8, arg, "-t") or std.mem.eql(u8, arg, "--threads")) {
const val = args.next() orelse fatal("--threads requires a value", .{});
ncpus = std.fmt.parseInt(usize, val, 10) catch fatal("invalid thread count", .{});
} else if (std.mem.eql(u8, arg, "-g") or std.mem.eql(u8, arg, "--gui")) {
gui = true;
} else if (std.mem.eql(u8, arg, "--help") or std.mem.eql(u8, arg, "-h")) {
var stderr = std.Io.File.stderr().writerStreaming(init.io, &.{});
var w = &stderr.interface;
try w.print(
\\Usage: {s} [options]
\\ -m, --mem-size SIZE Set the memory size
\\ -c, --cmdline STR Set the kernel cmdline
\\ -t, --threads COUNT Set the number of threads
\\ -g, --gui, Enable graphics mode using SDL2
\\
, .{exe});
std.process.exit(0);
} else {
fatal("unknown argument: {s}", .{arg});
}
}
pl.impl.global_state.params =
.{ .ncpus = ncpus, .cmdline = cmdline, .mem_size = mem_size };
pl.impl.global_state.boot_info.kernel_address.physical_base = 0;
pl.impl.global_state.boot_info.kernel_address.virtual_base = 0;
pl.impl.global_state.boot_info.rsdp = 0;
pl.impl.global_state.boot_info.cmdline = cmdline;
if (gui) {
start_gui();
while (gui_started.load(.monotonic) != true) {
std.atomic.spinLoopHint();
}
}
ke.ncpus = ncpus;
ke.private.init(&pl.impl.global_state.boot_info);
}
fn fatal(comptime fmt: []const u8, args: anytype) noreturn {
std.debug.print(fmt ++ "\n", args);
std.process.exit(1);
}
-206
View File
@@ -1,206 +0,0 @@
const std = @import("std");
const linux = std.os.linux;
const r = @import("root");
const pl = r.pl;
const ke = r.ke;
const ki = r.ke.private;
const posix = std.posix;
pub const entry = @import("entry.zig");
const timer = @import("timer.zig");
pub const c = @import("c");
pub const name = "User-Mode";
pub const GlobalState = struct {
const Params = struct {
mem_size: usize,
cmdline: ?[]const u8,
ncpus: usize,
};
params: Params,
boot_info: pl.BootInfo,
phys_memory_memfd: i32,
phys_base: usize,
};
pub const CpuState = struct {
pthread: c.pthread_t,
td: *ke.Thread,
};
pub var global_state: GlobalState = undefined;
pub threadlocal var my_cpu_id: usize = 0;
pub const percpu = ke.CpuLocal(CpuState, undefined);
pub fn debug_write(char: u8) void {
_ = linux.write(linux.STDERR_FILENO, @ptrCast(&char), 1);
}
pub const arm_timer = timer.arm_timer;
pub fn check_function(fn_name: []const u8, ret: usize) void {
const real_ret: isize = @bitCast(ret);
if (real_ret < 0) {
const errno_val = linux.errno(ret);
std.debug.print("error: {s}: {s}\n", .{ fn_name, @tagName(errno_val) });
std.process.exit(1);
}
}
pub var cpu_offsets: []usize = undefined;
pub fn early_init(_: *pl.BootInfo) linksection(r.init) void {
percpu.local().pthread = c.pthread_self();
my_cpu_id = 0;
const memfd: i32 = @intCast(linux.memfd_create("phys_memory", 0));
check_function("ftruncate", linux.ftruncate(memfd, @intCast(global_state.params.mem_size)));
const phys_base = linux.mmap(null, global_state.params.mem_size, .{ .READ = true, .WRITE = true }, .{ .TYPE = .SHARED }, memfd, 0);
check_function("mmap", phys_base);
// Build memory map
global_state.boot_info.memory_map.entry_count = 1;
global_state.boot_info.memory_map.entries[0].base = 0;
global_state.boot_info.memory_map.entries[0].size = global_state.params.mem_size;
global_state.boot_info.memory_map.entries[0].type = .Free;
global_state.phys_memory_memfd = memfd;
global_state.phys_base = phys_base;
const pfndb_base = linux.mmap(null, r.tib(1), .{}, .{ .ANONYMOUS = true, .TYPE = .PRIVATE }, -1, 0);
check_function("mmap", pfndb_base);
const heap_base = linux.mmap(null, r.gib(8), .{}, .{ .ANONYMOUS = true, .TYPE = .PRIVATE }, -1, 0);
check_function("mmap", heap_base);
r.kernel_pfndb_base = pfndb_base;
r.kernel_heap_base = heap_base;
std.log.info("um: kernel heap lives at {X}", .{heap_base});
}
fn sigusr1_handler(_: posix.SIG, _: *const posix.siginfo_t, _: ?*anyopaque) callconv(.c) void {
if (@intFromEnum(ki.ipl.current()) < @intFromEnum(ke.Ipl.Dispatch) and ki.ipl.is_softint_pending(.Dispatch)) {
ki.dpc.dispatch(ke.cpu.current());
}
}
fn make_thread(entrypoint: *const fn (?*anyopaque) void, td: *ke.Thread) void {
const stack = std.heap.page_allocator.alloc(u8, 16384) catch @panic("wtf");
td.init(@intFromPtr(stack.ptr), 16384, entrypoint, null);
td.base_priority = ke.Thread.Priority.idle;
td.priority = td.base_priority;
td.pinned = true;
}
var cpus_up = std.atomic.Value(usize).init(0);
fn other_cpu_entry(_: ?*anyopaque) callconv(.c) ?*anyopaque {
my_cpu_id = cpus_up.fetchAdd(1, .monotonic) + 1;
percpu.local().pthread = c.pthread_self();
ki.cpu.init_cpu(@intCast(my_cpu_id));
ki.sched.percpu.local().current_thread = percpu.local().td;
ki.sched.percpu.local().idle_thread = percpu.local().td;
timer.init_cpu();
while (true) {}
return null;
}
var cpu: ke.Cpu linksection(r.percpu) = undefined;
fn idle(_: ?*anyopaque) void {
while (true) {
std.atomic.spinLoopHint();
}
}
extern var __percpu_start: u8;
extern var __percpu_end: u8;
pub fn late_init(_: *pl.BootInfo) linksection(r.init) void {
timer.init();
timer.init_cpu();
var sa: posix.Sigaction = std.mem.zeroes(posix.Sigaction);
sa.handler = .{
.sigaction = sigusr1_handler,
};
sa.flags = posix.SA.SIGINFO;
sa.mask = posix.sigemptyset();
_ = posix.sigaction(posix.SIG.USR1, &sa, null);
std.log.info("um: starting {} thread{s}", .{
ke.ncpus - 1,
if ((ke.ncpus - 1) > 1) "s" else "",
});
const other_count = ke.ncpus - 1;
const allocator = std.heap.page_allocator;
var other_threads =
allocator.alloc(c.pthread_t, other_count) catch @panic("oom");
cpu_offsets = allocator.alloc(usize, ke.ncpus) catch @panic("oom");
cpu_offsets[0] = 0;
const size = @intFromPtr(&__percpu_end) - @intFromPtr(&__percpu_start);
for (1..ke.ncpus) |i| {
const block = allocator.alloc(u8, size) catch @panic("oom");
cpu_offsets[i] = @intFromPtr(block.ptr) -% @intFromPtr(&__percpu_start);
@memcpy(block, @as([*]u8, @ptrCast(&__percpu_start))[0..size]);
const td = allocator.create(ke.Thread) catch @panic("oom");
make_thread(idle, td);
percpu.remote(@intCast(i)).td = td;
var attr: c.pthread_attr_t = undefined;
_ = c.pthread_attr_init(&attr);
var cpuset: c.cpu_set_t = std.mem.zeroes(c.cpu_set_t);
cpuset.__bits[i / 64] |= @as(c_ulong, 1) << @intCast(i % 64);
_ = c.pthread_attr_setaffinity_np(&attr, @sizeOf(c.cpu_set_t), &cpuset);
const ret = c.pthread_create(
&other_threads[i - 1],
&attr,
other_cpu_entry,
null,
);
if (ret != 0) {
@panic("pthread_create() failed");
}
}
if (ke.ncpus > 1) {
while (cpus_up.load(.monotonic) < @as(u32, @intCast(ke.ncpus - 1))) {
std.atomic.spinLoopHint();
}
}
}
-75
View File
@@ -1,75 +0,0 @@
const r = @import("root");
const std = @import("std");
const posix = std.posix;
const linux = std.os.linux;
const c = r.pl.impl.c;
const ke = r.ke;
const ki = ke.private;
threadlocal var timerid: c.timer_t = undefined;
fn timer_handler(_: posix.SIG, _: *const posix.siginfo_t, _: ?*anyopaque) callconv(.c) void {
const old_ipl = ke.ipl.set_hardware(.Device);
ki.timer.clock();
_ = ke.ipl.set_hardware(old_ipl);
if (@intFromEnum(old_ipl) < @intFromEnum(ke.Ipl.Dispatch) and ki.ipl.is_softint_pending(.Dispatch)) {
ki.dpc.dispatch(ke.cpu.current());
}
}
pub fn arm_timer(ns: r.Nanoseconds) void {
const value: c.struct_itimerspec = .{ .it_value = .{
.tv_sec = @intCast(ns / std.time.ns_per_s),
.tv_nsec = @intCast(ns % std.time.ns_per_s),
}, .it_interval = .{ .tv_sec = 0, .tv_nsec = 0 } };
if (c.timer_settime(timerid, 0, &value, null) == -1) {
@panic("timer_settime failed");
}
}
fn tc_read() u64 {
var ts: std.c.timespec = undefined;
_ = linux.clock_gettime(linux.CLOCK.MONOTONIC, &ts);
return @intCast(ts.sec * std.time.ns_per_s + ts.nsec);
}
var tc: ke.TimeCounter = .{
.read_count = tc_read,
.frequency = std.time.ns_per_s,
.name = "CLOCK_MONOTONIC",
.quality = 1000,
.mask = std.math.maxInt(u64),
.p = 0,
.n = 0,
};
pub fn init() void {
var act = std.mem.zeroes(posix.Sigaction);
act.handler.sigaction = timer_handler;
act.flags = posix.SA.RESTART | posix.SA.SIGINFO;
act.mask = posix.sigfillset();
posix.sigaction(posix.SIG.ALRM, &act, null);
ke.time.register_source(&tc);
}
pub fn init_cpu() void {
var sev: c.sigevent = std.mem.zeroes(c.sigevent);
sev.sigev_notify = c.SIGEV_THREAD_ID;
sev.sigev_signo = c.SIGALRM;
sev._sigev_un._tid = linux.gettid();
if (c.timer_create(c.CLOCK_MONOTONIC, &sev, &timerid) == -1) {
@panic("timer_create failed");
}
}
-2
View File
@@ -56,8 +56,6 @@ pub const std_options = std.Options{
pub const panic = std.debug.FullPanic(ke.panic);
pub const main = if (config.arch == .um) pl.impl.entry.main else undefined;
comptime {
// Ensure the entry point is linked in.
_ = @import("boot/limine/entry.zig");