Flash 75 lines
// PID 1 ELF root.
//
// Built as a standalone aarch64-freestanding ET_EXEC (`pid1.elf`),
// staged into the initramfs at `/sbin/init`. The kernel's
// `kernel_process` locates that entry and hands its bytes to
// `prepare_move_to_user_elf` — the same ELF path the exec-elf /
// stackbomb / flibc test payloads already travel.
//
// The loader honours `e_entry` + `p_vaddr`, so section placement is the
// linker script's job (`tools/pid1_linker.ld`, single PT_LOAD at
// 0x100000) — no manual section pinning.
use "kernel_tests" as tests
use build_options
const OK = "[ \x1b[32mOK\x1b[0m ] "
const PID1_MSG [*:0]u8 = OK ++ "Reached target Userspace\n"
const LOGIN_PATH [*:0]u8 = "/bin/login"
// CI credential script. When the `ci-login-seed` build flag is set,
// PID-1 injects these bytes into the console RX ring before exec'ing
// /bin/login, so the real login path authenticates unattended under the boot
// watchdog (no interactive typist on QEMU, which feeds `</dev/null`). Must
// match an /etc/passwd + /etc/shadow account (see tools/gen_shadow.zig);
// `flash`/`flash` exercises the privilege drop to uid 1000. The flag is OFF
// by default and `zig build deploy` omits it, so a hardware boot does NOT
// seed — it stops at the `login:` prompt and demands a real password.
const LOGIN_SCRIPT []u8 = "flash\nflash\n"
// ELF entry. Naked: the loader zeroes x0..x30, sets SP = STACK_TOP
// (top stack page eagerly mapped) and jumps here. `bl pid1_main`
// gives `pid1_main` a normal prologue against that stack; the
// trailing `mov x8,#2 ; svc #0` (SYS_EXIT) is the fallback for an
// unexpected `pid1_main` return.
export fn _start() callconv(.naked) noreturn {
asm volatile (
\\bl pid1_main
\\mov x8, #2
\\svc #0
)
}
export fn pid1_main() noreturn {
tests.sys_writeConsole(PID1_MSG)
// The in-kernel [TEST] harness is the boot-as-test path the QEMU
// watchdog asserts (28 scenarios + 32 free-page checkpoints). Gated so
// deploy/run boot clean straight to login; -Dboot-selftest=true
// (CI/validation) runs it, the default skips it.
if build_options.boot_selftest {
result := tests.run_all()
tests.print_tally(result.passed, result.total)
}
// Hand PID 1 to /bin/login. login authenticates against
// /etc/shadow, drops privilege per /etc/passwd, then execs the user's
// shell — fsh prints its homescreen line at REPL entry (the stable
// `type 'help' for commands` tail), the boot-success marker the watchdog
// waits for. To keep the unattended
// QEMU boot reaching that marker (no interactive typist), inject the CI
// credentials into the console RX ring first via SYS_CONSOLE_INJECT so
// login's fd-0 reads drain them; inject-before-exec closes the empty-ring
// race the way the retired in-harness fsh capstone did. Neither login nor
// fsh emits sys_dump_free, so the free-page checkpoint count stays
// deterministic. Gated on the ci-login-seed flag: a hardware deploy omits
// it and the boot stops at the real `login:` prompt. sys_execve only
// returns on failure — fall through to sys_exit then.
if build_options.ci_login_seed {
for b in LOGIN_SCRIPT {
tests.sys_console_inject(b)
}
}
const argv = [_:null]?[*:0]u8{ LOGIN_PATH }
_ = tests.sys_execve(#intFromPtr(LOGIN_PATH), #intFromPtr(&argv))
tests.sys_exit()
}