danos/sbin/init.zig

55 lines
1.7 KiB
Zig

//! /sbin/init — the first user-space program. Built as its own freestanding
//! binary (see build.zig), shipped on the boot volume at sbin/init, loaded by
//! the bootloader, and started in ring 3 by the kernel's user-ELF loader
//! (src/kernel/usermode.zig). It talks to the kernel only through the
//! `int $0x80` syscall gate.
//!
//! Today it just proves the path — say hello, exit — and grows into the real
//! init (service supervision) once processes are schedulable (M3).
const std = @import("std");
// The M1 syscall numbers (usermode.zig): 0 = exit(code), 2 = write(ptr, len).
const sys_exit = 0;
const sys_write = 2;
fn syscall2(n: u64, a: u64, b: u64) u64 {
return asm volatile ("int $0x80"
: [ret] "={rax}" (-> u64),
: [n] "{rax}" (n),
[a] "{rdi}" (a),
[b] "{rsi}" (b),
: .{ .memory = true });
}
fn write(msg: []const u8) void {
_ = syscall2(sys_write, @intFromPtr(msg.ptr), msg.len);
}
fn exit(code: u64) noreturn {
_ = syscall2(sys_exit, code, 0);
unreachable; // the kernel never returns from exit
}
/// Entry. Naked: the kernel enters with rsp 16-aligned, but a SysV function
/// expects rsp ≡ 8 (mod 16) on entry (as if reached by `call`) — so re-enter
/// the ABI with an actual call. The trap after is unreachable.
pub export fn _start() callconv(.naked) noreturn {
asm volatile (
\\call init_main
\\ud2
);
}
export fn init_main() callconv(.c) noreturn {
write("init: hello from user space\n");
exit(0);
}
/// No runtime to unwind into — report the panic as a nonzero exit code.
pub const panic = std.debug.FullPanic(struct {
fn panic(_: []const u8, _: ?usize) noreturn {
exit(127);
}
}.panic);