Heads is a firmware distribution that replaces proprietary BIOS/UEFI with coreboot, a minimal Linux kernel, and a security-focused initramfs. It establishes a hardware root of trust, implements measured boot via TPM, and verifies the OS boot environment before handing off control.
See also: security-model.md, boot-process.md, tpm.md — detailed subsystem documentation.
External reference: deepwiki.com/linuxboot/heads — validated against code in this repository.
┌─────────────────────────────────────────────────────┐
│ SPI Flash ROM │
│ ┌──────────────┐ ┌───────────────┐ ┌──────────┐ │
│ │ coreboot │ │ Linux kernel │ │ initramfs │ │
│ │ (HW init + │→ │ (minimal, │→ │ (boot │ │
│ │ PCR 2 SRTM) │ │ no initramfs)│ │ scripts)│ │
│ └──────────────┘ └───────────────┘ └──────────┘ │
└─────────────────────────────────────────────────────┘
│ │
▼ ▼
TPM (PCR values) OS kernel via kexec
Replaces vendor firmware. Performs hardware initialization (memory training, PCIe, USB), records each firmware stage (bootblock → romstage → ramstage → payload) into TPM PCR 2 as the Static Root of Trust for Measurement (SRTM), and launches the kernel directly without a second-stage bootloader. Measured boot is transitive: each stage measures the next before executing it. The CRTM (running in bootblock) measures FMAP and the bootblock image into the preram log. Measurements taken before the TPM hardware is initialized are cached in the preram log and flushed to PCR 2 once TPM setup is complete. See tpm.md for per-board TPM init timing.
The framebuffer initialized by coreboot (libgfxinit on pre-Alder Lake, FSP GOP on Alder Lake and newer) must survive across kexec for display to work in the booted OS. Boards without a coreboot framebuffer (talos-2: kernel AST DRM, librem_l1um: BMC serial, KGPE-D16: native VGA init — server variants output to BMC serial + AST VGA; workstation variants use external discrete NVIDIA/AMD GPUs with fbwhiptail) have different display paths that do not involve sysfb handoff. See boot-process.md and kexec_handoff.md for the display handoff mechanism, per-board GPU init paths, limitations, and the TPM Disk Unlock Key workaround.
A minimal, stripped kernel compiled specifically for Heads. No initramfs — it boots directly into the Heads initramfs. Provides device drivers (TPM, USB, storage, network), filesystem support, and the platform for the boot scripts.
The root filesystem that runs at boot. Contains all Heads logic: configuration loading,
TPM operations, GPG verification, whiptail GUI, boot menu, LUKS key injection, and kexec
execution. Source lives in initrd/.
| Subsystem | Key files | Purpose |
|---|---|---|
| Init / boot flow | initrd/init, initrd/bin/gui-init.sh |
System initialization and main GUI loop |
| TPM abstraction | initrd/bin/tpmr.sh |
Unified TPM 1.2 / TPM 2.0 wrapper |
| Boot signing | initrd/bin/kexec-sign-config.sh |
GPG-sign /boot files, create checksums |
| Boot verification | initrd/bin/kexec-select-boot.sh |
Verify checksums, select and kexec the OS |
| LUKS key sealing | initrd/bin/kexec-seal-key.sh |
Seal disk encryption key to TPM |
| TOTP/HOTP | initrd/bin/seal-totp.sh, initrd/bin/seal-hotpkey.sh |
Seal attestation secrets to TPM |
| OEM reset | initrd/bin/oem-factory-reset.sh |
Full re-ownership: GPG, TPM, TOTP, checksums |
| Config GUI | initrd/bin/config-gui.sh |
Runtime configuration menus |
| Functions lib | initrd/etc/functions.sh |
Shared utilities: logging, INPUT, TPM helpers |
| GUI lib | initrd/etc/gui_functions.sh |
Whiptail wrappers, integrity report |
Three-layer hierarchy:
/etc/config— Board defaults compiled into the ROM at build time/etc/config.user— User overrides extracted from CBFS at runtime/tmp/config— Combined result, sourced during boot
combine_configs() in initrd/etc/functions.sh merges these by concatenating
/etc/config* into /tmp/config. User settings in CBFS take precedence
because they appear last in the concatenation.
Changes to user configuration are persisted by reflashing the ROM (CBFS operations).
The top-level Makefile orchestrates:
- Cross-compiler (
musl-cross-make, target:x86_64-linux-muslorpowerpc64le-linux-musl) - Modules (coreboot, Linux, busybox, GPG, cryptsetup, kexec, LVM2, …)
- Six CPIO archives assembled into the initramfs:
dev.cpio— device nodesmodules.cpio— kernel modulestools.cpio— userspace tools + configurationboard.cpio— board-specific scriptsheads.cpio— security scripts (CONFIG_HEADS=y)data.cpio— data files
- Final ROM image: coreboot ROM with Linux + initramfs payload embedded
Reproducible builds are achieved via Nix-pinned Docker images. See docker.md. The CI pipeline's workspace and cache behavior is documented in circleci.md.
| Architecture | Target triplet | Example boards |
|---|---|---|
| x86-64 | x86_64-linux-musl |
ThinkPad, Librem, Dell OptiPlex, QEMU |
| PowerPC 64-bit LE | powerpc64le-linux-musl |
Raptor Talos II |
- No network at boot — all verification is local; no certificate authorities
- Hardware root of trust — the coreboot bootblock (IBB) is the Static Core Root of Trust for Measurement (S-CRTM): the first code executed by the CPU, directly from SPI flash. Coreboot implements a transitive measurement chain: the CRTM measures FMAP and the bootblock image into the preram log, then each subsequent stage measures the next before executing it — bootblock measures romstage, romstage measures ramstage, ramstage measures the Heads payload. Measurements are taken during CBFS file loading, before decompression, and are recorded in TPM PCR 2 (SRTM) once the TPM hardware is initialized (
tpm_setup()). Measurements taken before TPM init are cached in the preram log and flushed to PCR 2 bytspi_measure_cache_to_pcr()duringtpm_setup(). The full chain — bootblock → romstage → ramstage → Heads Linux kernel + initrd — is recorded into PCR 2. PCRs 0, 1, and 3 remain zero as policy anchors. See tpm.md for TPM init timing per board. See wp-notes.md for SPI write-protection and PR0 chipset locking details. - Fail-closed — failed integrity verification drops to a recovery shell. Recovery shell authentication via GPG smartcard is enforced when GPG key backup has been configured (
CONFIG_HAVE_GPG_KEY_BACKUP=y), which is set by answering "y" to"Would you like to format an encrypted USB Thumb drive to store GPG key material? (Required to enable GPG authentication)"during OEM Factory Reset / Re-Ownership. Otherwise the recovery shell is unauthenticated. An "Ignore tampering and force a boot (Unsafe!)" option is available to override this. - Separation of duties — the public key that verifies
/bootsignatures is stored in CBFS (ROM). The private key that signs/bootstays on a USB security dongle and never leaves it. - Auditability — all source is open, builds are reproducible, ROM images are verifiable
Purism developed and upstreamed two boot enforcement modes for Heads as part
of PureBoot (their integrated stack of coreboot + TPM + Heads + USB
security dongle + LUKS). The modes are mutually exclusive and configurable
via Options → Change Configuration Settings in config-gui.sh:
| Mode | Config | Boot script | Behavior |
|---|---|---|---|
| Normal | (default) | gui-init.sh |
Full GUI, hash verification, TPM checks, HOTP/TOTP, USB dongle, unsafe boot available |
| Basic | CONFIG_BASIC=y |
gui-init-basic.sh → basic-autoboot.sh |
/init overrides CONFIG_BOOTSCRIPT. No signature checks, no dongle, no HOTP. Auto-boots first OS entry. |
| Restricted | CONFIG_RESTRICTED_BOOT=y |
gui-init.sh |
Same as Normal but: failsafe boot disabled, recovery console blocked, unsigned USB blocked. When disabled, erases TOTP/HOTP secret in TPM and reflashes. |
The SPI flash ROM is locked against writes via Intel chipset-level PR0
lockdown just before kexec. Coreboot prepares the SPI controller for
SMM-initiated locking; Heads triggers the lock via io386 SMI:
kexec-boot.sh → lock_chip.sh → io386 0xb2 0xcb → SMI → FLOCKDN
└─ PR0 locked,
flash read-only
until reset
This is x86/Intel only — QEMU (emulated), Talos II (POWER9), Librem L1UM, and AMD-based boards (KGPE-D16) lack Intel chipset locking. 29 Intel boards across all generations from Sandy Bridge to Alder Lake use this mechanism.
See wp-notes.md for the full config requirements, runtime chain details, and board-level status.
See also: wp-notes.md — per-board write-protection status and tracking.