mirror of
https://github.com/rdmsr/zag.git
synced 2026-10-27 13:58:20 +00:00
everywhere: nuke UM
This commit is contained in:
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 +0,0 @@
|
||||
pub const name = "um";
|
||||
+1
-4
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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"),
|
||||
};
|
||||
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
@@ -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"),
|
||||
};
|
||||
|
||||
|
||||
@@ -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 {}
|
||||
};
|
||||
@@ -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"),
|
||||
};
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user