Skip to Content
axiomOSmainArchitectureBoot Flow

Boot Flow

Relevant source files

Both architectures converge on the same Rust entry symbol, kernel_main, which calls kernel::init(), mounts the ext2 root filesystem, starts /bin/init, and turns the boot CPU into an idle loop. The paths into kernel_main differ.

End-to-end sequence

x86_64: Limine handoff

limine.conf boots the /axiomos entry with the Limine protocol and kernel_path: boot():/boot/kernel, zero timeout. The kernel declares its Limine requests in kernel/src/limine.rs :

RequestUsed for
DateAtBootRequest (BOOT_TIME)Boot wall-clock seconds (BOOT_TIME_SECONDS)
ExecutableFileRequest, ExecutableAddressRequestKernel image location (backtraces, symbolization)
MemoryMapRequestPhysical allocator stage 1
HhdmRequestHigher-half direct map offset for phys_to_virt
RsdpRequestACPI tables
StackSizeRequest256 KiB (262_144) boot stack
ModuleRequestBoot modules
MpRequestApplication processors (SMP)

kernel_main first checks BASE_REVISION.is_supported(). The Limine revision and OVMF image are pinned in ci/manifests/build-inputs.env (Limine v9.6.7-binary, OVMF edk2-stable202511-r2).

AArch64: firmware entry

boot.S is entered with the DTB physical address in x0, MMU off, at EL2 or EL1. It:

  1. Saves the DTB address and parks every core except core 0.
  2. If at EL2, configures HCR_EL2 (AArch64 EL1), disables FP/SIMD traps (CPTR_EL2), grants EL1 timer access (CNTHCTL_EL2), and erets to EL1h with all interrupts masked.
  3. Sets up a 64 KiB boot stack, clears BSS, enables FP/SIMD (CPACR_EL1.FPEN), installs VBAR_EL1.
  4. Calls Rust _start(dtb_addr).

Rust _start (boot.rs ) records the DTB address, runs platform::rpi5::init() or platform::virt::init() depending on the feature, parses the device tree (a parse failure is logged and the kernel falls back to platform defaults), then jumps to kernel_main.

On AArch64 only the boot core runs; mcore::init initializes the run queues and sleep queue but does not start secondary cores. On x86_64, mcore::init uses the Limine MpRequest to hand each application processor the boot CR3 and send it to cpu_init_and_idle.

kernel::init() order

kernel/src/lib.rs  initializes subsystems in dependency order and returns a typed KernelInitError instead of panicking:

Stepx86_64AArch64
1init_boot_time (Limine date)boot time stays at epoch 0 (no RTC source)
2log::initlog::init
3mem::init, acpi::init, apic::init, hpet::initAarch64::early_init (exception vectors), Aarch64::init (mem::init, GIC + timer + RP1 GPIO IRQ, syscall)
4BPF_MANAGER createdsame
5bench::init (only with bench feature)same
6backtrace::initsame
7file::init (VFS, devfs at /dev)same
8driver::iio::initsame
9mcore::init (run queues, sleep, cleanup; SMP bring-up)mcore::init (single core)
10pci::init (PCI enumeration, VirtIO devices)virt: VirtIO MMIO init; rpi5: embedded ramdisk
11driver::iio::init_simulated_devicesame
12print AXIOM KERNEL METRICS (boot ms, heap KB, image MB)same

Memory init itself is two-staged on both architectures: physical allocator stage 1 from the boot memory map, AddressSpace init, heap init, virtual-memory manager init, then physical stage 2 (frame-state and refcount tables, which need the heap) and heap stage 2. See Memory.

After init()

On x86_64, kernel_main requires block device 0, mounts it as ext2 at /, verifies /bin/init exists, creates the init process, prints INIT_PROCESS_STARTED (with audit-diagnostics) and then always prints QEMU_BOOT_OK. The marker is deliberately emitted only after root mount and init creation so that the QEMU smoke gate proves more than kernel initialization.

On AArch64, kernel_main initializes per-CPU context for CPU 0, enables IRQs, and, if a block device exists, mounts it and starts /bin/init (printing INIT_PROCESS_STARTED, plus PI5_BOOT_OK under bench). With rpi5 it then spawns the Shrike control-link task (control_link::spawn).

Boot failure handling

Boot prerequisites map to a BootError enum with stable codes, emitted as one bounded record before halting, following ADR-0002:

BOOT_FATAL code=bootloader-revision-unsupported | boot-time-unavailable | filesystem-initialization | embedded-ramdisk-initialization | iio-simulation-initialization | root-block-device-missing | root-filesystem-invalid | root-mount-failed | init-path-invalid | init-executable-missing | init-process-creation-failed

The panic handler prints V04_PANIC kind=panic, the location and message, and an optional backtrace (backtrace feature), then halts.

Diagnostic features

FeatureEffect on boot
audit-diagnosticsOne-shot serial markers consumed by the local audit gate
bringup-diagnosticsRaw single-character writes to the Pi 5 debug UART10 at each boot stage, plus a forced scheduling probe
audit-fault-injectionRuns a physical-memory fault-injection probe after QEMU_BOOT_OK (x86_64)
benchv0.3 HIL bench instrumentation and emergency console
Last updated on