Heads modules (tools included in the initrd)

June 26, 2026 · View on GitHub

Tools available in the Heads initrd are defined by CONFIG_* flags in board configs (boards/*/*.config) and compiled by the top-level Makefile. Each bin_modules-$(CONFIG_*) += <name> line adds a package to tools.cpio (one of six CPIO archives assembled into the initrd).

Not all tools are BusyBox applets — many are standalone binaries compiled as separate packages.

Module list (from the bin_modules-$(CONFIG_* ) block in the Makefile)

Makefile lineConfig flagPackageType
718CONFIG_KEXECkexecStandalone
719CONFIG_TPMTOTPtpmtotpStandalone
720CONFIG_PCIUTILSpciutilsStandalone
721CONFIG_FLASHROMflashromStandalone
722CONFIG_FLASHPROGflashprogStandalone
723CONFIG_CRYPTSETUPcryptsetupStandalone
724CONFIG_CRYPTSETUP2cryptsetup2Standalone
725CONFIG_GPGgpgStandalone
726CONFIG_GPG2gpg2Standalone
727CONFIG_PINENTRYpinentryStandalone
728CONFIG_LVM2lvm2Standalone
729CONFIG_DROPBEARdropbearStandalone
730CONFIG_FLASHTOOLSflashtoolsStandalone
731CONFIG_NEWTnewtStandalone
732CONFIG_CAIROcairoStandalone
733CONFIG_FBWHIPTAILfbwhiptailStandalone
734CONFIG_HOTPKEYhotp-verificationStandalone
735CONFIG_MSRTOOLSmsrtoolsStandalone
736CONFIG_NKSTORECLInkstorecliStandalone
737CONFIG_UTIL_LINUXutil-linuxStandalone
738CONFIG_OPENSSLopensslStandalone
739CONFIG_TPM2_TOOLStpm2-toolsStandalone
740CONFIG_BASHbashStandalone
741CONFIG_POWERPC_UTILSpowerpc-utilsStandalone
742CONFIG_IO386io386Standalone
743CONFIG_IOPORTioportStandalone
744CONFIG_KBDkbdStandalone
745CONFIG_ZSTDzstdStandalone
746CONFIG_E2FSPROGSe2fsprogsStandalone

BusyBox applets (always available)

BusyBox v1.36.1 provides the following applets relevant to Heads scripts. These are always available regardless of board config.

[, [[, arch, arp, ascii, ash, awk, base32, basename, blkid, blockdev,
bunzip2, bzcat, bzip2, cat, chattr, chmod, chroot, clear, cmp, cp,
cpio, crc32, cttyhack, cut, date, dc, dd, devmem, df, diff, dirname,
dmesg, du, echo, env, expr, factor, fallocate, false, fdisk, find,
fold, fsck, fsfreeze, getopt, grep, groups, gunzip, gzip, hd, head,
hexdump, hexedit, hostid, hwclock, i2cdetect, i2cdump, i2cget, i2cset,
id, ifconfig, insmod, install, ip, kill, killall, killall5, less, link,
ln, loadkmap, losetup, ls, lsattr, lsmod, lsof, lsscsi, lsusb, lzcat,
lzma, md5sum, mkdir, mkdosfs, mkfifo, mkfs.vfat, mknod, mktemp,
modinfo, more, mount, mv, nc, nl, nproc, nslookup, ntpd, partprobe,
paste, patch, pgrep, pidof, ping, pkill, printf, ps, pwd, readlink,
realpath, reboot, reset, resume, rm, rmdir, route, sed, seedrng, seq,
setfattr, setpriv, setserial, setsid, sh, sha1sum, sha256sum, sha3sum,
sha512sum, shred, sleep, sort, ssl_client, stat, strings, stty, sync,
sysctl, tail, tar, tee, test, tftp, time, top, touch, tr, tree, true,
truncate, tsort, tty, udhcpc, umount, uname, uniq, unlzma, unxz, unzip,
usleep, vconfig, vi, wc, wget, which, xargs, xxd, xz, xzcat, zcat

How the build system includes modules

Three files interact to determine what goes into tools.cpio (the initrd):

FileLineRole
boards/<board>/<board>.configCONFIG_FOO=yBoard-specific Make variable (e.g. CONFIG_GPG2=y enables GPG)
modules/<name>CONFIG_FOO ?= yModule default — only sets the variable if the board config did not
Makefileinclude modules/*Loads all module files into the Make namespace
Makefilebin_modules-$(CONFIG_FOO) += fooConditionally builds and adds the module to tools.cpio

The inclusion decision tree for any board:

  1. include $(CONFIG) loads the board config — any CONFIG_FOO=y (no export needed) becomes a Make variable.
  2. include modules/* loads every module file. Each module can set a default with ?= which only applies if the board config didn't already set the variable.
  3. bin_modules-$(CONFIG_FOO) += foo conditionally adds the module to tools.cpio — when CONFIG_FOO is y, the module is built and included; when n or unset, it is skipped.
  4. modules-$(CONFIG_FOO) += foo (in the module file) adds the module to the build graph so its compile targets run.

export in board configs is unrelated to module inclusion. export places the variable into the initrd's /etc/config at build time, where config-gui.sh can modulate it further with user overrides from CBFS /etc/config.user. Module inclusion is purely based on Make variable state.

Auto-included modules

These modules use CONFIG_FOO ?= y in their modules/<name> file, so they are included in every build unless a board explicitly sets CONFIG_FOO=n:

ModuleFileDefault
zstdmodules/zstdCONFIG_ZSTD ?= y — provides zstd-decompress
bashMakefileCONFIG_BASH ?= y — interactive shell
kbdMakefileCONFIG_KBD ?= y — keymaps and loadkeys
headsMakefileCONFIG_HEADS ?= y — Heads base

Some auto-included defaults are set in the Makefile itself (before include modules/*), others in the module .mk files. The effect is the same: ?= only sets the variable if the board config did not already override it.

Board-enabled modules

These modules default to n and must be explicitly enabled in the board config with CONFIG_FOO=y (no export needed for inclusion):

# boards/qemu-coreboot-fbwhiptail-tpm2/qemu-coreboot-fbwhiptail-tpm2.config
CONFIG_GPG2=y          # enables gpg2 module
CONFIG_TPM2_TOOLS=y    # enables tpm2-tools module

Listing auto-included modules

grep -r 'CONFIG_.*?= y' modules/ Makefile | grep -v '\.git'

Available targets

Module targets

Each modules/<name> file generates a Make target. Build a single package and its dependencies:

nix develop --command make BOARD=$BOARD kexec     # kexec-tools
nix develop --command make BOARD=$BOARD linux      # Linux kernel
nix develop --command make BOARD=$BOARD coreboot   # coreboot ROM

Full ROM build (all modules + initrd + ROM assembly):

nix develop --command make BOARD=$BOARD

Maintenance targets

TargetWhat it does
real.cleanRemove all build artifacts
real.gitcleangit clean — remove all untracked files
real.gitclean_keep_packagesgit clean but keep downloaded tarballs in packages/
real.remove_canary_files-extract_patch_rebuild_what_changedRemove all .canary sentinels, clear install + coreboot/board build caches, then rebuild. Use this after changing patches.
real.gitclean_keep_packages_and_buildKeep packages + clean + full rebuild

All run under nix develop (local) or ./docker_repro.sh (Docker):

nix develop --command make BOARD=$BOARD real.clean
nix develop --command make BOARD=$BOARD real.remove_canary_files-extract_patch_rebuild_what_changed

Module-level helpers

Some packages define their own helpers in modules/<name>. Common ones (run with nix develop --command make BOARD=$BOARD <target>):

TargetDefined inWhat it does
coreboot.save_in_defconfig_format_in_placemodules/corebootNormalize to defconfig (minimal, sorted)
coreboot.save_in_oldconfig_format_in_placemodules/corebootNormalize to full .config
coreboot.save_in_defconfig_format_backupmodules/corebootSame as defconfig but saves as _defconfig backup
coreboot.modify_defconfig_in_placemodules/corebootRun menuconfig, save as defconfig
coreboot.modify_and_save_oldconfig_in_placemodules/corebootRun menuconfig, save as full .config
linux.save_in_defconfig_format_in_placemodules/linuxNormalize kernel config to defconfig
linux.save_in_olddefconfig_format_in_placemodules/linuxNormalize to olddefconfig format
linux.save_in_versioned_defconfig_formatmodules/linuxSave defconfig with version stamp
linux.save_in_versioned_oldconfigmodules/linuxSave full .config with version stamp
linux.modify_and_save_defconfig_in_placemodules/linuxRun menuconfig, save as defconfig
linux.modify_and_save_oldconfig_in_placemodules/linuxRun menuconfig, save as full .config
linux.prompt_for_new_config_options_for_kernel_version_bumpmodules/linuxPrompt for new kernel Kconfig options on version bump
linuxboot.runmodules/linuxbootRun Heads under LinuxBoot
u-root.cleanmodules/u-rootClean u-root build artifacts

These are used after manually editing config/coreboot-BOARD.config or config/linux-BOARD.config to normalize the file back to the convention expected by the build system.

Build lifecycle

Each module (whether tarball or git-sourced) goes through the same stages controlled by sentinel files in build/$ARCH/PACKAGE-DIR/:

tarball / git clone → .canary → .configured → .build → binary → initrd
  • .canary — package extracted/cloned and patches applied. Depends only on the tarball or git repo HEAD, NOT on patch files.
  • .configured./configure run (or equivalent setup).
  • .buildmake / make install run. Depends on .configured and on all dependency packages' .build files.

The binary lands in build/$ARCH/PACKAGE-DIR/$output and is copied into the initrd by bin_modules-$(CONFIG_FOO).

Rebuilding after changing a patch

The .canary sentinel does NOT depend on patch files. Modifying a patch in patches/PACKAGE-VERSION/ leaves .canary up-to-date and the old binary is used. To force re-extraction and re-patching:

Per-package (fastest, one package only):

rm build/$ARCH/PACKAGE-DIR/.canary
rm build/$ARCH/PACKAGE-DIR/.configured
rm build/$ARCH/PACKAGE-DIR/.build
nix develop --command sh -c "make BOARD=$BOARD $PACKAGE"

Example: after changing patches/kexec-2.0.26/0003-screen_info-normalize-for-VLFB.patch:

rm build/x86/kexec-tools-2.0.26/.canary
rm build/x86/kexec-tools-2.0.26/.configured
rm build/x86/kexec-tools-2.0.26/.build
nix develop --command sh -c "make BOARD=novacustom-nv4x_adl kexec"

Full rebuild (all packages, use the helper target):

nix develop --command make BOARD=$BOARD \
  real.remove_canary_files-extract_patch_rebuild_what_changed
nix develop --command make BOARD=$BOARD

Module file format

Defined in modules/<name>. See modules/kexec for a complete example. Key variables:

modules-$(CONFIG_KEXEC) += kexec      # add to build graph
kexec_dir := kexec-tools-$(kexec_version)
kexec_tar := kexec-tools-$(kexec_version).tar.gz
kexec_hash := sha256...
kexec_output := build/sbin/kexec       # installed into initrd

The define_module function in Makefile expands these into the .canary.configured.build chain above. The package name is the Make target: make BOARD=... kexec builds just that package.