This document describes the complete boot flow from power-on to OS handoff.
See also: architecture.md for component overview, tpm.md for TPM PCR details, security-model.md for the trust model.
Power-on
│
▼
coreboot (SPI flash)
│ hardware init, SRTM measurement into PCR 2
▼
Linux kernel (coreboot payload, no initramfs)
│
▼
/init ← first userspace process
│ mount filesystems, load config, combine user overrides
▼
/bin/gui-init ← main interactive boot loop
│ TPM preflight, GPG key check, TOTP/HOTP attestation
▼
kexec-select-boot
│ verify /boot hashes + GPG signature, rollback counter
▼
kexec ← hands off to OS kernel
/init is the first userspace process. It:
- Mounts virtual filesystems (
/dev,/proc,/sys). - Loads board defaults from
/etc/configand the functions library. - Runs
cbfs-initto extract user configuration from CBFS into/etc/config.user. - Calls
combine_configs()to merge all/etc/config*files into/tmp/config, then sources/tmp/configso all subsequent scripts see the merged settings. - Checks for a quick
rkeypress (100 ms timeout) to drop to a recovery shell before any GUI starts. - Execs
cttyhack $CONFIG_BOOTSCRIPT(default:/bin/gui-init), which sets up a controlling TTY and hands off to the boot script.
/etc/config (ROM, board defaults)
/etc/config.user (CBFS, user overrides)
│
└─► combine_configs() ─► /tmp/config (runtime, sourced by all scripts)
User settings appear last in the concatenation and therefore override board defaults. Changes are persisted by reflashing CBFS.
gui-init is the main interactive boot agent. It runs as an infinite loop and
handles all user interaction until the OS is handed off via kexec.
On startup, gui-init detects the controlling TTY (set by cttyhack in /init)
and exports it as HEADS_TTY and GPG_TTY. This ensures that all interactive
prompts and GPG operations reach the correct terminal regardless of stdout/stderr
redirections.
Before showing any menu, gui-init verifies that the TPM rollback counter is
consistent with /boot/kexec_rollback.txt. An inconsistency indicates either a
TPM reset (expected: user must re-seal secrets) or an unexpected state (possible
tampering). On failure, the main menu background is set to error color and the
user is offered recovery options.
gui-init counts the keys in the GPG keyring. An empty keyring means no /boot
signature can be verified. The user must add a key or perform OEM Factory Reset
before booting.
unseal-totp retrieves the TOTP secret from TPM NVRAM and generates the current
30-second code. If the unseal fails (PCR mismatch, TPM reset, tampered firmware),
INTEGRITY_GATE_REQUIRED is set to y, which blocks all subsequent TPM secret
sealing until an integrity check passes. See security-model.md.
If a hardware HOTP token is present (/bin/hotp_verification), gui-init obtains
the HOTP secret (unsealed from TPM on boards with a TPM; derived from a ROM hash on
boards without one) and asks the token to verify the current code. Result codes:
0 = success, 4 = wrong code, 7 = not a valid HOTP value.
See security-model.md
for the no-TPM path.
If HOTP succeeded and CONFIG_AUTO_BOOT_TIMEOUT is set, a countdown starts and
the default boot entry is selected automatically if the user does not intervene.
show_main_menu displays the current date, TOTP code, and HOTP status in the
menu title bar. The background color reflects the current integrity state
(normal / warning / error). Options: default boot, refresh TOTP/HOTP, options
menu, system info, power off.
Called from the boot menu. Responsible for final verification and OS handoff.
For TPM2 systems, verifies the SHA-256 hash of the TPM2 primary key handle
against /boot/kexec_primhdl_hash.txt (if the file exists). A mismatch means
the TPM2 primary key was regenerated without updating the stored hash.
verify_checksums checks the SHA-256 of every /boot file against
kexec_hashes.txt, then verifies kexec.sig with gpgv. A hash mismatch or
invalid signature causes die — there is no "boot anyway" path.
Optionally, root partition hashes are also checked if CONFIG_ROOT_CHECK_AT_BOOT=y.
The TPM monotonic counter index is read from /boot/kexec_rollback.txt and the
counter is read from the TPM. The SHA-256 of the counter file is then checked
against the hash stored in kexec_rollback.txt. Any discrepancy aborts the boot.
If a TPM-sealed LUKS Disk Unlock Key (DUK) is configured, kexec-insert-key
unseals the DUK and injects it into a minimal initrd prepended to the OS initrd.
The OS kernel then finds the key and unlocks LUKS without prompting the user.
kexec-boot performs the final kexec system call to hand off to the OS kernel.
The recovery shell is an authenticated environment. Entering it extends TPM
PCR 4 with "recovery", permanently invalidating TOTP/HOTP/LUKS unseal for
the rest of the boot session. See tpm.md.