mirror of
https://github.com/rdmsr/zag.git
synced 2026-10-29 22:48:20 +00:00
initial commit
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
const b = @import("base");
|
||||
|
||||
pub fn first_init() linksection(b.init) void {}
|
||||
@@ -0,0 +1,16 @@
|
||||
const entry = @import("entry.zig");
|
||||
|
||||
pub const first_init = entry.first_init;
|
||||
|
||||
pub fn devices_init() void {}
|
||||
|
||||
pub fn debug_write(c: u8) void {
|
||||
// Write to debugcon
|
||||
asm volatile ("outb %[c], %[port]"
|
||||
:
|
||||
: [c] "{al}" (c),
|
||||
[port] "N{dx}" (@as(u16, 0xe9)),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn arm_timer(_: u64) void {}
|
||||
@@ -0,0 +1,75 @@
|
||||
//! Platform interface: each port must implement these functions and variables.
|
||||
//! To add a new platform, create a new file and implement all required functions.
|
||||
|
||||
const config = @import("config");
|
||||
|
||||
pub const impl = if (@hasDecl(config, "CONFIG_ARCH_AMD64"))
|
||||
@import("amd64/impl.zig")
|
||||
else if (@hasDecl(config, "CONFIG_ARCH_UM"))
|
||||
@import("um/impl.zig")
|
||||
else
|
||||
@compileError("unsupported architecture");
|
||||
|
||||
/// Early platform-specific initialization, called before anything else.
|
||||
pub inline fn early_init() void {
|
||||
return impl.early_init();
|
||||
}
|
||||
|
||||
/// Late platform init. Memory allocator is available at this point.
|
||||
/// SMP bringup must be done after this returns.
|
||||
pub inline fn late_init() void {
|
||||
return impl.late_init();
|
||||
}
|
||||
|
||||
/// Write a single character to the debug console.
|
||||
pub inline fn debug_write(c: u8) void {
|
||||
return impl.debug_write(c);
|
||||
}
|
||||
|
||||
/// Arm a one-shot timer to fire in `ns` nanoseconds.
|
||||
/// The timer must call `ki_timer_expiry()` when it fires.
|
||||
pub inline fn arm_timer(ns: u64) void {
|
||||
return impl.arm_timer(ns);
|
||||
}
|
||||
|
||||
/// Pretty name for the platform
|
||||
pub const name = impl.name;
|
||||
|
||||
/// A generic struct representing information passed from the bootloader.
|
||||
pub const BootInfo = struct {
|
||||
/// Represents the system memory map.
|
||||
pub const MemMap = struct {
|
||||
/// A memory map entry.
|
||||
pub const Entry = struct {
|
||||
pub const Kind = enum {
|
||||
Free,
|
||||
Reserved,
|
||||
LoaderReclaimable,
|
||||
AcpiNvs,
|
||||
AcpiReclaimable,
|
||||
Kernel,
|
||||
};
|
||||
|
||||
kind: Kind,
|
||||
base: usize,
|
||||
size: usize,
|
||||
};
|
||||
|
||||
entry_count: usize,
|
||||
entries: [256]Entry,
|
||||
};
|
||||
|
||||
pub const KernelAddress = struct { physical_base: usize, virtual_base: usize };
|
||||
|
||||
/// RSDP on ACPI machines
|
||||
rsdp: usize,
|
||||
|
||||
/// Kernel command-line arguments
|
||||
cmdline: ?[]const u8,
|
||||
|
||||
/// Memory map
|
||||
memory_map: MemMap,
|
||||
|
||||
/// Kernel address
|
||||
kernel_address: KernelAddress,
|
||||
};
|
||||
@@ -0,0 +1,81 @@
|
||||
const b = @import("base");
|
||||
|
||||
pub const std_options = b.std_options;
|
||||
|
||||
const std = @import("std");
|
||||
const ke = b.ke;
|
||||
const pl = b.pl;
|
||||
|
||||
const default_mem_size = b.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' => b.kib(1),
|
||||
'm' => b.mib(1),
|
||||
'g' => b.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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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, "--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
|
||||
\\
|
||||
, .{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;
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
const std = @import("std");
|
||||
const linux = std.os.linux;
|
||||
const b = @import("base");
|
||||
const pl = b.pl;
|
||||
const ke = b.ke;
|
||||
const ki = b.ke.private;
|
||||
const posix = std.posix;
|
||||
|
||||
const timer = @import("timer.zig");
|
||||
|
||||
pub const c = @cImport({
|
||||
@cDefine("_GNU_SOURCE", "1");
|
||||
@cInclude("pthread.h");
|
||||
@cInclude("sys/mman.h");
|
||||
@cInclude("ucontext.h");
|
||||
@cInclude("signal.h");
|
||||
@cInclude("unistd.h");
|
||||
@cInclude("sys/time.h");
|
||||
@cInclude("sched.h");
|
||||
});
|
||||
|
||||
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 var global_state: GlobalState = undefined;
|
||||
pub threadlocal var my_cpu: *ke.Cpu = undefined;
|
||||
|
||||
var cpu_id: u32 = 0;
|
||||
|
||||
pub fn devices_init() void {}
|
||||
|
||||
pub fn debug_write(char: u8) void {
|
||||
std.debug.print("{c}", .{char});
|
||||
}
|
||||
|
||||
pub const arm_timer = timer.arm_timer;
|
||||
|
||||
fn check_function(fn_name: []const u8, ret: usize) void {
|
||||
const real_ret: i64 = @intCast(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 fn early_init() linksection(b.init) void {
|
||||
// Set current CPU to the bootstrap CPU
|
||||
my_cpu = &ki.bootstrap_cpu;
|
||||
|
||||
my_cpu.impl.pthread = c.pthread_self();
|
||||
|
||||
// Allocate our ID
|
||||
my_cpu.id = 0;
|
||||
cpu_id += 1;
|
||||
|
||||
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 }, .{ .ANONYMOUS = 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 = phys_base;
|
||||
global_state.boot_info.memory_map.entries[0].size = global_state.params.mem_size;
|
||||
global_state.boot_info.memory_map.entries[0].kind = .Free;
|
||||
|
||||
global_state.phys_memory_memfd = memfd;
|
||||
global_state.phys_base = phys_base;
|
||||
|
||||
const pfndb_base = linux.mmap(null, b.tib(1), .{}, .{ .ANONYMOUS = true, .TYPE = .PRIVATE }, -1, 0);
|
||||
|
||||
check_function("mmap", pfndb_base);
|
||||
|
||||
const heap_base = linux.mmap(null, b.gib(8), .{}, .{ .ANONYMOUS = true, .TYPE = .PRIVATE }, -1, 0);
|
||||
|
||||
check_function("mmap", heap_base);
|
||||
|
||||
b.kernel_pfndb_base = pfndb_base;
|
||||
b.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(ke.curcpu().ipl) < @intFromEnum(ke.Ipl.Dispatch) and ki.ipl.is_softint_pending(ke.curcpu(), .Dispatch)) {
|
||||
ki.dpc.dispatch(ke.curcpu());
|
||||
}
|
||||
}
|
||||
|
||||
fn make_thread(entry: *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, entry, null);
|
||||
|
||||
td.priority = 0;
|
||||
td.priority_class = .Idle;
|
||||
}
|
||||
|
||||
var cpus_up = std.atomic.Value(usize).init(0);
|
||||
|
||||
fn other_cpu_entry(arg: ?*anyopaque) callconv(.c) ?*anyopaque {
|
||||
my_cpu = @ptrCast(@alignCast(arg.?));
|
||||
|
||||
my_cpu.id = @atomicRmw(u32, &cpu_id, .Add, 1, .monotonic);
|
||||
my_cpu.impl.pthread = c.pthread_self();
|
||||
|
||||
my_cpu.init(my_cpu.idle_thread.?);
|
||||
|
||||
timer.init_cpu();
|
||||
|
||||
_ = cpus_up.fetchAdd(1, .monotonic);
|
||||
|
||||
while (true) {}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
fn idle(_: ?*anyopaque) void {}
|
||||
|
||||
pub fn late_init() linksection(b.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("bum: 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");
|
||||
|
||||
ke.cpus = allocator.alloc(*ke.Cpu, ke.ncpus) catch @panic("oom");
|
||||
ke.cpus[0] = &ki.bootstrap_cpu;
|
||||
|
||||
for (1..ke.ncpus) |i| {
|
||||
ke.cpus[i] = allocator.create(ke.Cpu) catch @panic("oom");
|
||||
|
||||
ke.cpus[i].idle_thread = allocator.create(ke.Thread) catch @panic("oom");
|
||||
|
||||
make_thread(idle, ke.cpus[i].idle_thread.?);
|
||||
|
||||
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 r = c.pthread_create(
|
||||
&other_threads[i - 1],
|
||||
&attr,
|
||||
other_cpu_entry,
|
||||
ke.cpus[i],
|
||||
);
|
||||
|
||||
if (r != 0) {
|
||||
@panic("pthread_create() failed");
|
||||
}
|
||||
}
|
||||
|
||||
if (ke.ncpus > 1) {
|
||||
while (cpus_up.load(.monotonic) < @as(u32, @intCast(ke.ncpus - 1))) {
|
||||
std.atomic.spinLoopHint();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
const b = @import("base");
|
||||
const std = @import("std");
|
||||
const posix = std.posix;
|
||||
const linux = std.os.linux;
|
||||
const c = b.pl.impl.c;
|
||||
const ke = b.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);
|
||||
|
||||
ke.dpc.enqueue(&ke.curcpu().timer_dpc, null);
|
||||
|
||||
_ = ke.ipl.set_hardware(old_ipl);
|
||||
|
||||
if (@intFromEnum(old_ipl) < @intFromEnum(ke.Ipl.Dispatch) and ki.ipl.is_softint_pending(ke.curcpu(), .Dispatch)) {
|
||||
ki.dpc.dispatch(ke.curcpu());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn arm_timer(ns: b.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) {
|
||||
ke.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: posix.Sigaction = undefined;
|
||||
|
||||
act.handler.sigaction = timer_handler;
|
||||
act.flags = posix.SA.RESTART | posix.SA.SIGINFO;
|
||||
|
||||
posix.sigaction(posix.SIG.ALRM, &act, null);
|
||||
|
||||
ke.timecounter.register(&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) {
|
||||
ke.panic("timer_create failed", .{});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user