Every operating system begins before it is really itself. The CPU starts in a platform-defined environment, firmware initializes enough hardware to load the first executable, and that executable prepares the machine for the kernel. Only after this chain has done its work can the operating system begin enforcing its own rules.

That early path is easy to treat as plumbing, but the boot process is part of the security model. For EriX, boot is the first place where untrusted bytes become trusted execution, and it is also the first place where machine authority is translated into explicit structure: verified images, memory maps, module descriptors, entry addresses, framebuffer metadata, ACPI pointers, and eventually kernel objects.

If this path is sloppy, the capability system starts from a lie. This post walks through the EriX boot path from firmware to kernel: what UEFI provides, what the bootloader must do, what the handoff contains, and why the kernel is entered as a higher-half executable.


Boot Starts With Firmware Authority

On current EriX targets, the boot path starts with UEFI on x86_64. UEFI is firmware, and it runs before the operating system. It provides the boot-time services that let an EFI application read files, allocate memory, inspect the platform memory map, discover configuration tables such as ACPI, query graphics output through GOP, and exit boot services before the kernel takes over.

The EriX bootloader is built as a UEFI application, so firmware loads the bootloader first and calls its UEFI entrypoint. In the implementation, that entrypoint is efi_main, using the UEFI x86_64 calling convention. At this point EriX is not yet in control of the machine; it is running inside a firmware-provided environment.

The bootloader can ask UEFI to read a file, allocate memory, inspect firmware tables, and prepare page tables, but all of that authority is temporary. UEFI boot services are not the operating system. They are scaffolding that the bootloader must use carefully, summarize into explicit facts, and then leave behind.


The Bootloader Is Trusted Code

The bootloader runs before the kernel can enforce anything, which puts it in the trusted computing base. If the bootloader loads the wrong bytes, jumps to the wrong address, accepts a tampered image, mislabels memory, or invents authority that did not come from verified input, the kernel starts from a compromised foundation. To keep that risk auditable, EriX keeps the bootloader’s job narrow and explicit:

  • locate and load boot.img
  • validate the image before trusting it
  • validate the dynamic boot store
  • relocate the dynamic kernel and its required early objects
  • build a deterministic handoff structure
  • prepare page tables and a bootstrap stack
  • exit UEFI boot services
  • jump to the kernel once

That is deliberately not a general-purpose boot environment. There is no boot menu policy in the current scope, no remote recovery path, and no attempt to continue after malformed boot-critical input. The rule is verify before execute: if the boot image cannot be parsed, verified, loaded, mapped, or described consistently, the boot attempt fails before the kernel receives control.


Loading boot.img

The current UEFI path looks for this file on the boot volume. The path is fixed for the current profile, which keeps early media handling narrow and avoids turning boot into a policy-discovery problem:

\ERIX\BOOT.IMG

The bootloader opens the boot volume through UEFI file protocols, reads the file into UEFI-allocated memory, and performs a cheap first check for the ERIXBOOT container magic before deeper parsing. After that, lib-bootimg takes over: the bootloader parses the image as a BootImage, verifies its signature and hashes, checks the manifest architecture, and only then begins extracting payloads.

This separation matters because the boot medium is untrusted. The file may be missing, truncated, malformed, or tampered with, and the bootloader does not get to treat a section descriptor as true just because it came from disk. In EriX, the boot.img parser and verifier are part of the trusted boot path, and they must reject bad structure, bad bounds, bad hashes, unsupported versions, and incompatible architecture data before any loaded byte becomes executable.

The next posts will go deeper into the boot.img format and image verification. For this post, the important point is the ordering:

  1. read bytes
  2. parse structure
  3. verify integrity
  4. check target compatibility
  5. validate and relocate dynamic boot artifacts
  6. build handoff metadata

Execution comes after validation, not before. That ordering is the difference between treating the boot image as an input and treating it as authority.


Loading the Kernel

Once boot.img has been accepted, the bootloader needs a kernel, and in the EriX boot path the kernel is loaded from the signed dynamic boot store. The bootloader validates the dynamic kernel as an ELF64 ET_DYN object, checks the EriX dynamic-link metadata, verifies dependency names and object hashes against signed metadata, loads required shared objects, applies approved relocations, enforces W^X constraints, and prepares a dynamic boot catalog for the kernel.

That sounds like a lot because it is a lot, but the security shape remains direct and reviewable:

  • data comes from a verified boot image
  • executable format parsing is explicit
  • segment ranges are checked
  • relocation side effects are constrained
  • missing or inconsistent dynamic metadata fails before kernel entry

The bootloader exists to validate and relocate the dynamic kernel, then describe the verified early object graph to the kernel. It does not become a general dynamic linker for the running system; that role belongs later, after the kernel and user-space service model exist.


Preserving Boot Metadata

The dynamic kernel is not the only thing in the boot image. The bootloader also preserves required non-executable boot metadata for the kernel and early user-space system, while executable services belong to the dynamic object graph. Those executable artifacts are described by the dynamic catalog as objects, segments, and dependency edges derived from the signed store and manifest.

Other required sections are non-executable blobs. Examples include boot configuration, dynamic-link metadata stores, and console font data. These are copied into mapped memory and described as read-only modules with no entry point, because a blob is not executable just because it appears in the boot image.

That distinction is important for capability design. A boot configuration payload should be readable by the early system, but it should not be treated as code. A font blob may be needed for framebuffer continuity, but it does not need execution authority. EriX preserves this distinction in the handoff:

  • dynamic object descriptors for executable artifacts
  • dynamic segment descriptors for mapped executable and data ranges
  • dynamic dependency descriptors for early object relationships
  • SECTION_TYPE_BOOT_CONFIG for boot policy data
  • module descriptors for non-executable boot blobs

The handoff carries the difference forward so the kernel and rootd do not have to guess whether a piece of boot data is executable authority, read-only configuration, or ordinary metadata.


The Handoff Is the Contract

The bootloader does not call a kernel API, because there is no running kernel yet. Instead, the bootloader builds a handoff blob: a versioned binary contract defined by lib-handoff. In the current bootloader-to-kernel profile, it starts with the ERIXHK01 magic, major/minor version fields, total size, architecture and platform IDs, then offsets and counts for the tables that follow.

The handoff can include several classes of data that the kernel needs before it can build its own runtime view of the machine:

  • normalized memory map entries
  • loaded module descriptors
  • ACPI RSDP pointer
  • verified build ID and image hash
  • framebuffer continuity metadata
  • dynamic object descriptors
  • dynamic segment descriptors
  • dynamic dependency edges

This is the first structured transfer of authority. The firmware gave the bootloader raw facts and temporary services, and the verified boot image gave the bootloader signed payload metadata. The bootloader combines those into a deterministic description of what it loaded, where it placed it, and what the kernel may trust.

The handoff is not a hint or “best effort” metadata. It is the input from which the kernel begins constructing its own view of the machine, which is why it carries counts, entry sizes, offsets, hashes, types, ranges, and version fields. It must be possible for the kernel to reject it.


Memory Maps Need Normalization

Firmware memory maps are not automatically shaped for kernel policy. UEFI reports regions with firmware memory types, while the bootloader also knows about memory it allocated for the dynamic kernel, early object mappings, required boot blobs, bootstrap stack, handoff pages, framebuffer mappings, and the original boot image. Those views must be merged before the kernel can reason about available memory.

In EriX, the bootloader snapshots the UEFI memory map, adds explicit boot-owned regions, and normalizes the result into non-overlapping ranges with EriX memory kinds such as:

  • usable RAM
  • reserved memory
  • ACPI reclaimable memory
  • ACPI NVS memory
  • MMIO/device memory
  • bootloader-owned memory
  • boot-image-owned memory

Explicitly owned boot regions win over generic firmware classifications. That matters because the kernel must not accidentally treat the memory holding the boot image, handoff blob, dynamic object mappings, required boot blobs, or bootstrap stack as ordinary free RAM. Memory is authority in EriX, so even this early the system is careful about who is allowed to reuse which bytes.


Leaving UEFI Behind

UEFI boot services are useful, but they are temporary. Before jumping to the kernel, the bootloader calls ExitBootServices, which is a one-way transition in practice. After a successful exit, the bootloader must treat boot services pointers as invalid; it cannot keep asking firmware to allocate memory or read files after the operating system takes over.

This transition is delicate because UEFI requires the bootloader to exit using a current memory-map key. If the map changes between snapshot and exit, the call can fail and the loader must retry with a fresh map. The EriX UEFI adapter handles that retry loop in the platform layer.

The important design point is that the kernel is entered after the bootloader has finished using firmware services. The kernel should not inherit a half-open firmware dependency; it should inherit explicit data.


Building the First Page Tables

The kernel is entered with paging already active. For the current x86_64 UEFI profile, the bootloader builds minimal page tables with two kinds of mappings: an identity-mapped low-memory region for early bring-up and specific higher-half mappings for the objects the kernel will use immediately.

The current implementation maps the first 1 GiB using 2 MiB pages and also identity-maps APIC MMIO windows. It then maps higher-half virtual ranges for the kernel, loaded dynamic objects, required boot blobs, handoff blob, framebuffer, and bootstrap stack. This is enough for the kernel to start in the address space it expects.

The page tables are not the final virtual-memory system. They are a bridge that lets the kernel execute, validate the handoff, install early CPU state, and begin building the real kernel and user-space environment. The bridge still has to be correct: if the kernel’s entry page is not mapped, the machine faults immediately; if the handoff blob is mapped at the wrong virtual address, the kernel reads nonsense; if the stack is missing, entry fails before Rust code can do much of anything.


Why a Higher-Half Kernel?

EriX enters the kernel in the higher half of the virtual address space. That means the kernel runs at high virtual addresses instead of being linked and executed only in the low identity-mapped range. This is a common kernel design because it gives the kernel a stable virtual address region independent of where physical memory was allocated.

The higher-half layout also separates kernel virtual space from ordinary user-space ranges and lets the kernel keep its own mappings present across address-space changes later, while user-space mappings can vary per task. The address layout itself does not enforce the whole security model, but it supports the boundary by making kernel memory distinct from normal process memory.

In EriX, the bootloader is responsible for making the initial higher-half entry possible. It loads the kernel according to ELF virtual addresses, maps those virtual addresses to allocated physical pages, creates a bootstrap stack in the higher half, maps the handoff blob at a known higher-half address, loads cr3, and jumps to the kernel entry point.

At the jump, the current x86_64 contract is intentionally small and explicit. The bootloader supplies only the state the kernel needs to begin validating the handoff and installing its own early CPU state:

  • SysV ABI
  • rdi contains the handoff pointer
  • rsp points at the bootstrap stack with the expected alignment
  • paging is active
  • control does not return

That is a small ABI by design. The less implicit state the kernel entry depends on, the easier the boundary is to audit.


Entering the Kernel

On the kernel side, entry begins before the full kernel runtime exists. The dynamic kernel exposes erix_dynlink_entry, which enters the early kernel path with the handoff pointer, and the first work is defensive rather than policy-heavy.

The kernel disables interrupts, initializes early serial output, checks that the handoff pointer is not null, reads the handoff size from the header, enforces a maximum handoff size, and then validates the full blob through lib-handoff. After structural validation, the kernel applies its own policy checks:

  • architecture must be x86_64
  • platform must be UEFI
  • build ID must not be empty
  • dynamic catalog tables must be internally consistent
  • dynamic object names, hashes, segments, dependencies, and store ranges must validate before use

The bootloader already built the handoff, but the kernel still validates it. Trusted components do not get to skip contracts just because another trusted component produced the data. The whole point of a versioned handoff is that both sides can agree on exactly what was transferred.

Only after that does the kernel move deeper into early initialization: GDT setup, IDT setup, syscall path setup, optional early console initialization, and eventually bootstrap orchestration for the first root task. The kernel becomes the kernel gradually, and the first thing it does is check the ground under its feet.


Boot Is Authority Translation

It is tempting to describe boot as “load the kernel and jump”. That is technically true, but it misses the operating-system design point. For EriX, boot is authority translation: firmware authority becomes explicit boot facts, boot image bytes become verified sections, ELF files become mapped executable ranges, blobs become non-executable module descriptors, firmware memory maps become normalized memory regions, dynamic-link metadata becomes a bounded object graph, and framebuffer state becomes continuity metadata.

All of that becomes a handoff blob, and the kernel receives that blob and decides whether it is acceptable. Only then can it begin turning machine resources into kernel objects, capabilities, address spaces, endpoints, and the first user-space task. This is why the boot process belongs in a capability OS discussion: the capability model does not start after boot as an afterthought; it depends on boot not smuggling in ambient authority.

The bootloader should not merely say that it loaded some things. It should say exactly what it loaded, where it is, what it is, how it was verified, and which platform facts it observed. That is the difference between a jump and a handoff.


What EriX Keeps Out of the Bootloader

The bootloader is powerful because it runs early, and that is exactly why it should stay small. EriX does not want the bootloader to decide runtime policy: it should not decide which service owns memory policy, how processes are supervised, how drivers are managed, or how filesystems are composed.

Those decisions belong to the kernel and user-space system services. The bootloader’s job is narrower: validate the boot artifact, prepare the minimum execution environment, describe what it did, and transfer control.

That line matters for TCB size. A bootloader with more features is not automatically better, because every feature in early boot is code that runs before the kernel can isolate it. Every parser, fallback path, interactive mode, and policy exception increases the amount of trusted behavior that must be correct before the system starts.


Looking Ahead

This post treated boot.img mostly as a verified container. The next step is to open that container and look at its design directly.

The next post will explain the EriX boot.img format: why the system uses a unified image, how sections are laid out, what metadata is carried, and how the format supports reproducible, deterministic boot artifacts.