SYSTEMD-STUB(7)
๐ NAME ๐
okay so bestie, welcome to the systemd-stub(7) manual page ๐ this is all about systemd-stub, aka sd-stub, aka the files linuxx64.efi.stub, linuxia32.efi.stub and linuxaa64.efi.stub โ it's a simple UEFI kernel boot stub, and no cap, it's kind of a big deal ๐
โจ SYNOPSIS โจ
/usr/lib/systemd/boot/efi/linuxx64.efi.stub
/usr/lib/systemd/boot/efi/linuxia32.efi.stub
/usr/lib/systemd/boot/efi/linuxaa64.efi.stub
ESP/.../foo.efi.extra.d/*.addon.efi
ESP/.../foo.efi.extra.d/*.cred
ESP/.../foo.efi.extra.d/*.raw
ESP/.../foo.efi.extra.d/*.sysext.raw
ESP/.../foo.efi.extra.d/*.confext.raw
ESP/loader/addons/*.addon.efi
ESP/loader/credentials/*.cred
ESP/loader/extensions/*.raw
ESP/loader/extensions/*.sysext.raw
ESP/loader/extensions/*.confext.raw
๐ DESCRIPTION ๐
systemd-stub (which lives on disk in the per-architecture files linuxx64.efi.stub, linuxia32.efi.stub and linuxaa64.efi.stub) is a simple UEFI boot stub, fr fr. So what even is a UEFI boot stub, you ask? ๐ฆ It's a little chunk of code that gets attached to a Linux kernel binary image, and it runs inside the UEFI firmware environment before we transition into the Linux kernel environment. Its whole job is making sure a Linux kernel can be executed as a regular UEFI binary, and it can do a bunch of prep work before it switches the system over into the Linux world โจ
The UEFI boot stub goes looking for all the various resources it needs for the kernel invocation inside the UEFI PE binary itself. Which is iconic, because it means you can smoosh all your resources into one single PE binary image โ a "Unified Kernel Image", or "UKI" for short ๐ โ and then sign that whole thing via UEFI SecureBoot as one unit, covering every individual resource at once. Obsessed. Specifically, it may include the following PE sections ๐
- A
.linuxsection with the ELF Linux kernel image. This one is required, bestie, you literally cannot skip it. - An optional
.osrelsection with OS release information, i.e. the contents of theos-release(5)file of the OS the kernel belongs to. - An optional
.cmdlinesection with the kernel command line to pass to the invoked kernel. - An optional
.initrdsection with the initrd. - An optional
.ucodesection with an initrd containing microcode, to be handed to the kernel before any other initrd. Heads up: this initrd must not be compressed ๐ซถ - An optional
.splashsection with an image (in the Windows.BMPformat) to show on screen before invoking the kernel. - An optional
.dtbsection with a compiled binary DeviceTree. - Zero or more
.dtbautosections.systemd-stubwill always use the first matching one. The match is performed by taking the first DeviceTree's"compatible"string supplied by the firmware in configuration tables and comparing it with the first"compatible"string from each of the.dtbautosections. If the firmware does not provide a DeviceTree, the match is done using the.hwidssection instead. After selecting the matching entry in the.hwidssection (see the description below), the"compatible"string from that entry will be used to perform the same matching procedure. If a match is found, that.dtbautosection will be loaded and will override.dtbif present. - Zero or more
.efifwsections for the firmware image. It works in many ways similar to.dtbautosections.systemd-stubwill always use the first matching one. The match is performed by first selecting the most appropriate entry in the.hwidssection based on the hardware IDs supplied by SMBIOS (see below). If a suitable entry is found, the"fwid"string from that entry will be used to perform the matching procedure for firmware blobs in.efifwsection. The first matching firmware will be loaded. - An optional
.hwidssection with hardware IDs of the machines to match DeviceTrees.systemd-stubwill use the SMBIOS data to calculate hardware IDs of the machine (as per specification[1]), and then it will try to find any of them in the.hwidssection. The first matching entry will be used. - An optional
.unamesection with the kernel version information, i.e. the output ofuname -rfor the kernel included in the.linuxsection. - An optional
.sbatsection with SBAT[2] revocation metadata. - An optional
.pcrsigsection with a set of cryptographic signatures for the expected TPM2 PCR values after the kernel has been booted, in JSON format. This is useful for implementing TPM2 policies that bind disk encryption and similar to kernels that are signed by a specific key. - An optional
.pcrpkeysection with a public key in the PEM format matching the signature data in the.pcrsigsection.
In a basic UKI, the sections listed above appear at most once, with the exception of .dtbauto and .efifw sections. In a multi-profile UKI though? Multiple sets of these sections are present in a single file and they form "profiles", one of which can be selected at boot ๐ For this, the PE section .profile is defined to be used as the separator between sets of sections. The .profile section itself may contain meta-information about the section, and follows a similar structure as the contents of the .osrel section. For further details about multi-profile UKIs, see below.
โ ๏ธ okay so this part is important. If UEFI SecureBoot is enabled and the .cmdline section is present in the executed image, then any attempts to override the kernel command line by passing one as invocation parameters to the EFI binary are ignored. So if you do wanna allow overriding the kernel command line, either disable UEFI SecureBoot, or just don't include a kernel command line PE section in the kernel image file. And if a command line is accepted via EFI invocation parameters to the EFI binary, it gets measured into TPM PCR 12 (if a TPM is present).
If a DeviceTree is embedded in the .dtb section, it replaces an existing DeviceTree in the corresponding EFI configuration table. systemd-stub will ask the firmware via the "EFI_DT_FIXUP_PROTOCOL" for hardware specific fixups to the DeviceTree ๐ซ
The contents of 11 of these 12 sections are measured into TPM PCR 11. It is otherwise not used and thus the result can be pre-calculated without too much effort. The .pcrsig section is not included in this PCR measurement, since it is supposed to contain signatures for the output of the measurement operation, and thus cannot also be input to it. If a UKI contains multiple profiles, only the PE sections of the selected profile (and those of the base profile, except if overridden) are measured.
If non-zero, the selected numeric profile is measured into PCR 12.
When .pcrsig and/or .pcrpkey sections are present in a unified kernel image, their contents are passed to the booted kernel in a synthetic initrd cpio archive that places them in the /.extra/tpm2-pcr-signature.json and /.extra/tpm2-pcr-public-key.pem files ๐ Typically, a tmpfiles.d(5) line then ensures they are copied into /run/systemd/tpm2-pcr-signature.json and /run/systemd/tpm2-pcr-public-key.pem, where they remain accessible even after the system transitions out of the initrd environment into the host file system. Tools such as systemd-cryptsetup@.service(8), systemd-cryptenroll(1) and systemd-creds(1) will automatically use files present under these paths to unlock protected resources (encrypted storage or credentials) or bind encryption to booted kernels ๐โจ
For further details about the UKI concept, see the UAPI.5 UKI specification[3].
๐ COMPANION FILES ๐
okay bestie, this is the part that makes people go "wait, you can do that??" โ the UEFI boot stub lowkey collects three types of auxiliary companion files that you optionally drop into drop-in directories on the same partition as the EFI binary, dynamically generates cpio initrd archives out of them, and hands them to the kernel. here's the whole tea, file by file:
- the
.credones โ if your kernel binary is calledfoo.efi, it looks for files with the.credsuffix in a directory namedfoo.efi.extra.d/sitting right next to it. If the kernel binary uses a counter for the purpose of Automatic Boot Assessment[4], that counter gets ignored. so like,foo+3-0.efiwill look in the directoryfoo.efi.extra.d/. a cpio archive gets generated from everything found that way, and they're placed in the/.extra/credentials/directory of the initrd file hierarchy. the main initrd can then access them in that directory. this is meant for storing auxiliary, encrypted, authenticated credentials for use withLoadCredentialEncryptedin the UEFI System Partition. peepsystemd.exec(5)andsystemd-creds(1)for the deets on encrypted credentials. the generated cpio archive is measured into TPM PCR 12 (if a TPM is present). - the
.sysext.rawones โ similarly, filesfoo.efi.extra.d/*.sysext.rawget packed up in a cpio archive and placed in the/.extra/sysext/directory in the initrd file hierarchy. this is supposed to pass additional UKI-specific system extension images to the initrd. seesystemd-sysext(8)for the details on system extension images. the generated cpio archive containing these system extension images is measured into TPM PCR 13 (if a TPM is present). - the global sysext ones โ similarly, files
/loader/extensions/*.sysext.rawget packed up in a cpio archive and placed in the/.extra/global_sysext/directory in the initrd file hierarchy. this is supposed to pass additional global system extension images to the initrd. seesystemd-sysext(8)for details on system extension images. the generated cpio archive containing these system extension images is measured into TPM PCR 13 (if a TPM is present). - the
.confext.rawones โ similarly, filesfoo.efi.extra.d/*.confext.rawget packed up in a cpio archive and placed in the/.extra/confext/directory in the initrd file hierarchy. this is supposed to pass additional UKI-specific configuration extension images to the initrd. seesystemd-confext(8)for details on configuration extension images. the generated cpio archive containing these configuration extension images is measured into TPM PCR 12 (if a TPM is present). - the global confext ones โ similarly, files
/loader/extensions/*.confext.rawget packed up in a cpio archive and placed in the/.extra/global_confext/directory in the initrd file hierarchy. this is supposed to pass additional global configuration extension images to the initrd. seesystemd-confext(8)for details on configuration extension images. the generated cpio archive containing these configuration extension images is measured into TPM PCR 12 (if a TPM is present). - the
.addon.efiones (the UKI-specific girlies) โ similarly, filesfoo.efi.extra.d/*.addon.efiget loaded and verified as PE binaries, and specific sections are loaded from them. addons are used to pass additional kernel command line parameters (the.cmdlinesection), or DeviceTree blobs (the.dtbsection), additional initrds (the.initrdsection), and microcode updates (the.ucodesection). addons let all those resources be passed regardless of the kernel version being booted, which is how platform vendors get to ship platform-specific configuration. iconic tbh.
in case Secure Boot is enabled, these files will be validated using keys in UEFI DB, Shim's DB or Shim's MOK, and only loaded if the check passes. additionally, if both the addon and the UKI contain a .uname section, the addon will be rejected if they do not match exactly. it is recommended to always add a .sbat section to all signed addons, so that they may be revoked with a SBAT policy update, without requiring blocklisting via DBX/MOKX. the ukify(1) tool will add a SBAT policy by default if none is passed when building addons. for more information on SBAT see Shim documentation[2].
addon files are sorted, loaded, and measured into TPM PCR 12 (if a TPM is present) and appended to the kernel command line. UKI command line options are listed first, then options from addons in /loader/addons/*.addon.efi, and finally UKI-specific addons. Device tree blobs are loaded and measured following the same algorithm. Microcode addons are passed to the kernel in inverse order (UKI specific addons, global addons, UKI embedded section). this is because the microcode update driver stops on the first matching filename. addons are always loaded in the same order based on the filename, so that, given the same set of addons, the same set of measurements can be expected in PCR12.
okay so here's the part where you gotta be careful, bestie ๐ญ โ the filename is not protected by the PE signature, and as such an attacker with write access to the ESP could potentially rename these files to change the order in which they are loaded, in a way that could alter the functionality of the kernel, as some options might be order-dependent. if you sign such addons, you should pay attention to the PCR12 values and make use of an attestation service so that improper use of your signed addons can be detected and dealt with using one of the aforementioned revocation mechanisms.
- the global credential ones โ files
/loader/credentials/*.credget packed up in a cpio archive and placed in the/.extra/global_credentials/directory of the initrd file hierarchy. this is supposed to pass additional credentials to the initrd, regardless of the kernel version being booted. the generated cpio archive is measured into TPM PCR 12 (if a TPM is present). - the global addon ones โ additionally, files
/loader/addons/*.addon.efiget loaded and verified as PE binaries, and the.cmdline,.dtb,.initrd, and.ucodesections are parsed from them. this is supposed to pass additional command line parameters, DeviceTree blobs, initrds, and microcode updates to the kernel, regardless of the kernel version being booted.
so why do we care?? because these mechanisms let you parameterize and extend trusted (i.e. signed), immutable initrd images in a reasonably safe way: all the data they contain is measured into TPM PCRs. on access they should be further validated โ in the credentials case by encrypting/authenticating them via TPM, as exposed by systemd-creds encrypt -T (see systemd-creds(1) for details); in the system extension images case by using signed Verity images. โจ
and psst, one more thing: earlier components of the boot process might register additional initrds, and thus additional "companion" resources such as system extensions, configuration extensions and credentials for consumption by the kernel and OS eventually booted. for example, systemd-boot(7) does this for resources configured in UAPI.1 Type #1 "extra" lines. systemd-stub will combine any resources provided that way with the companion file resources it acquires itself. ๐ซถ
โจ MULTI-PROFILE UKIS โจ
okay so bestie, here's the tea โ โ sometimes you want one single UKI to be able to boot in a bunch of different modes (we call them "profiles") without messing up the cryptographic integrity, the measurements, any of that sacred boot-process stuff. like, imagine one UKI serving three whole vibes:
- a regular normal boot profile
- one that kicks off a "factory reset" operation
- one that boots into a storage target mode (as in
systemd-storagetm.service(8))
each of those profiles would then share the exact same .linux and .initrd sections, but rock its own separate .cmdline section. so like, those last two profiles would just extend the regular kernel command line with systemd.unit=factory-reset.target or rd.systemd.unit=storagetm.target. easy peasy ๐
so how do you even do that? with the special .profile PE section, bestie. this section is literally the separator between the PE sections of the individual profiles. meaning:
.profilePE sections can show up multiple times in a single UKI ๐ซถ- the other PE sections listed above can also appear multiple times โ but only if
.profileis being used - they can appear once before the first
.profile, once between each subsequent pair, and once after the last appearance of.profile
everything listed before the first .profile counts as the "base" profile of the UKI. then each .profile section introduces a new profile, and those get numbered starting from zero (iconic behavior, we love a 0-indexed queen ๐). the PE sections that follow each .profile belong specifically to that profile.
when you boot into a specific profile, the base section's profiles get used in combination with that specific profile's sections. and if the same section is defined in both? the per-profile section overrides the base profile's version. if it's not defined in the profile, the base profile's section gets used. simple as that ๐
and for the record: a UKI that contains no .profile is considered equivalent to one that just contains a single .profile, i.e. one that only has a single profile @0. no cap.
here's a cute little example of a multi-profile UKI's sections, inspired by the setup we just described:
Table 1. Multi-Profile UKI Example
.linuxโ Base profile.osrelโ Base profile.cmdlineโ Base profile.initrdโ Base profile.profileโ Profile @0.profileโ Profile @1.cmdlineโ Profile @1.profileโ Profile @2.cmdlineโ Profile @2
so that section list up there would define three profiles. the first four sections make up the base profile. then a .profile section introduces profile @0 โ but it doesn't override any sections (or add any) from the base section, which is exactly why it's immediately followed by another .profile section that introduces section @1. that profile overrides the kernel command line. and finally, the last two sections define section @2, again overriding the command line. ๐
side note bestie: in this example the first.cmdlinecould also be moved behind the first.profilewith equivalent effect. but honestly? to keep things nicely extensible, it's probably a good idea to keep the generic command line in the base section instead of profile 0, just in case later-added profiles might want to reuse it. foresight is a vibe โจ
so which profile actually boots? you control that through the UKI's own command line:
- if the first argument starts with
@, followed by a positive integer number in decimal, that selects the profile to boot into ๐ - if the first argument is not specified like that, the UKI will automatically boot into profile 0
and a .profile section can carry meta-information about the profile too! it follows a similar format as .osrel (i.e. an environment-variable-assignment-block-like list of newline separated strings). right now, two fields are defined:
ID=โ supposed to carry a short identifying string that identifies the profile (e.g.ID=factory-reset)TITLE=โ should contain a human-readable string that may appear in the boot menu entry for this profile (e.g.TITLE='Factory Reset this Device')
๐ก๏ธ TPM PCR NOTES ๐ก๏ธ
okay this part is the serious one, so lock in ๐ โ when a unified kernel using systemd-stub is invoked, the firmware will measure it as a whole to TPM PCR 4, covering all embedded resources, like:
- the stub code itself
- the core kernel
- the embedded initrd
- the kernel command line (see above for a full list)
โฆand yes, that includes all UKI profiles. every last one.
also note: when systemd-stub measures a PE section, it measures the amount of bytes that the section takes up in memory (VirtualSize) and not the amount of bytes it takes up on disk (SizeOfRawData). ๐ญ so if the size in memory is larger than the size on disk, systemd-stub will end up measuring extra zeroes. to avoid this happening, it is recommended to make sure the size in memory of each section measured by systemd-stub is always smaller than or equal to the size on disk.ukifyautomatically makes sure this is the case when building UKIs or addons, so she's got you ๐ซถ
and one more thing โ the Linux kernel will measure all initrds it receives into TPM PCR 9. which means every type of initrd (of the selected UKI profile) will possibly be measured two or three times:
- the initrds embedded in the kernel image get measured to PCR 4, PCR 9 and PCR 11
- the initrd synthesized from credentials (and the one synthesized from configuration extensions) get measured to both PCR 9 and PCR 12
- the initrd synthesized from system extensions gets measured to both PCR 4 and PCR 9
let's summarize the OS resources and the PCRs they're measured to:
Table 2. OS Resource PCR Summary
- systemd-stub code (the entry point of the unified PE binary) โ 4
- Core kernel code (embedded in unified PE binary) โ 4 + 11
- OS release information (embedded in the unified PE binary) โ 4 + 11
- Main initrd (embedded in unified PE binary) โ 4 + 9 + 11
- Microcode initrd (embedded in unified PE binary) โ 4 + 9 + 11
- Default kernel command line (embedded in unified PE binary) โ 4 + 11
- Overridden kernel command line โ 12
- Boot splash (embedded in the unified PE binary) โ 4 + 11
- TPM2 PCR signature JSON (embedded in unified PE binary, synthesized into initrd) โ 4 + 9
- TPM2 PCR PEM public key (embedded in unified PE binary, synthesized into initrd) โ 4 + 9 + 11
- Credentials (synthesized initrd from companion files) โ 9 + 12
- System Extensions (synthesized initrd from companion files) โ 9 + 13
- Configuration Extensions (synthesized initrd from companion files) โ 9 + 12
- Selected profile unless zero โ 12
โจ EFI VARIABLES โจ
ok so bestie, systemd-stub talks to the OS through a bunch of EFI variables, and they all live under this one vendor UUID, no cap:
4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
that's the group chat everyone's posting in. here's who says what ๐
LoaderDevicePartUUIDโ holds the partition UUID of the partition the boot loader was started from on the current boot (usually an EFI System Partition, iykyk). if the boot loader already set this, systemd-stub will not touch it. but if it's not set yet, stub sets it to the partition UUID of the partition the unified kernel is started from, so that systems which boot straight into a unified kernel image โ skipping the boot loader entirely, main character behavior โ still get support.systemd-gpt-auto-generator(8)uses this info to figure out which disk you booted from, so it can auto-discover all the other partitions on that same disk. iconic.
Added in version 224.
LoaderFirmwareInfo,LoaderFirmwareTypeโ brief firmware info. usebootctl(1)to go look at this data. ๐ฆ
Added in version 250.
LoaderTpm2ActivePcrBanksโ a hexadecimal string representation of a bitmask, with values defined by the TCG EFI Protocol Specification for TPM 2.0 asEFI_TCG2_BOOT_HASH_ALG_*. if no TPM2 support or no active banks were detected, this gets set to0. set by the boot loader. usesystemd-analyze(1)to view this data.
Added in version 258.
LoaderImageIdentifierโ the file system path to the EFI executable of the boot loader for the current boot, relative to the partition's root directory (so, relative to the partition thatLoaderDevicePartUUIDpoints at, see above ๐). if it's not set yet, this gets set to the file system path of the EFI executable of the booted unified kernel, again to support systems that boot directly into a UKI and bypass any boot loader. usebootctl(1)to view this data.
Added in version 237.
StubDevicePartUUID,StubImageIdentifierโ basicallyLoaderDevicePartUUIDandLoaderImageIdentifier's cousins, but they point at the location of the unified kernel image EFI binary instead of the boot loader binary โ and they do this regardless of whether you booted via a boot loader or not. periodt.
Added in version 257.
StubDeviceURLโ if the kernel image got invoked via network booting, this variable holds the originating URL. you can use it to automatically grab extra resources from the same source. ๐ง
Added in version 258.
StubInfoโ brief stub info. usebootctl(1)to view this data.
Added in version 250.
StubPcrKernelImageโ the PCR register index that the kernel image, initrd image, boot splash, devicetree database, and the embedded command line get measured into, formatted as a decimal ASCII string (like"11"). this variable gets set if a measurement was successfully completed, and stays unset otherwise.
Added in version 252.
StubPcrKernelParametersโ the PCR register index that the kernel command line and credentials get measured into, formatted as a decimal ASCII string (like"12"). set only if a measurement successfully completed, otherwise it stays unset.
Added in version 252.
StubPcrInitRDSysExtsโ the PCR register index for the system extensions for the initrd, which get picked up from the file system the kernel image lives on. formatted as a decimal ASCII string (like"13"). set if a measurement was successfully completed, unset otherwise.
Added in version 252.
StubPcrInitRDConfExtsโ the PCR register index for the configuration extensions for the initrd, which get picked up from the file system the kernel image lives on. formatted as a decimal ASCII string (like"12"). set if a measurement was successfully completed, unset otherwise.
Added in version 255.
StubProfileโ the numeric index of the selected profile, without the@, formatted as a decimal string. it gets set on both single-profile and multi-profile UKIs. (in the single-profile case this variable is unconditionally set to"0".)
Added in version 257.
LoaderPcrSMBIOSโ the PCR register index that select SMBIOS structures get measured into โ type 1 (system information, with the volatile"Wake-up Type"field zeroed out), type 2 (baseboard information) and type 11 (OEM strings). formatted as a decimal ASCII string (like"1"). set if a measurement was successfully completed, unset otherwise. systemd-stub only performs this measurement if it hasn't already been done in the same boot (say, by the boot loader) โ and it knows that because this variable would already be set. ๐
Added in version 261.
LoaderBootSecretโ a non-volatile EFI variable that's only accessible from the pre-boot environment (i.e. the OS is not allowed to access it) and it holds a per-system secret. systemd-stub sets it automatically if it's not present already. a secret derived from the value of this EFI variable gets passed to the OS in/.extra/boot-secret, see below ๐
Added in version 261.
heads up bestie: some of those variables up there might also be set by the boot loader. the stub will only set them if they're not already set. also, some of these variables are defined by the Boot Loader Interface[5].
๐ธ INITRD RESOURCES ๐ธ
these next ones get passed as initrd cpio archives to the booted kernel, which is literally how the initial file system hierarchy in the initrd execution environment gets built. here's the whole lineup:
/โ the main initrd, coming from the".initrd"PE section of the unified kernel image.
Added in version 252.
/.extra/credentials/*.credโ credential files (suffix".cred") placed next to the unified kernel image (as described above) get copied into the/.extra/credentials/directory in the initrd execution environment. ๐
Added in version 252.
/.extra/global_credentials/*.credโ same energy: credential files in the/loader/credentials/directory, in the file system the unified kernel image is placed in, get copied into the/.extra/global_credentials/directory in the initrd execution environment.
Added in version 252.
/.extra/sysext/*.sysext.rawโ system extension image files (suffix".sysext.raw") placed next to the unified kernel image (as described above) get copied into the/.extra/sysext/directory in the initrd execution environment.
Added in version 252.
/.extra/global_sysext/*.sysext.rawโ and similarly, system extension image files (suffix".sysext.raw") placed in the/loader/extensions/directory, in the file system the unified kernel image is placed in, get copied into the/.extra/global_sysext/directory in the initrd execution environment.
Added in version 258.
/.extra/confext/*.confext.rawโ configuration extension image files (suffix".confext.raw") placed next to the unified kernel image (as described above) get copied into the/.extra/confext/directory in the initrd execution environment.
Added in version 255.
/.extra/global_confext/*.confext.rawโ similarly, configuration extension image files (suffix".confext.raw") placed in the/loader/extensions/directory, in the file system the unified kernel image is placed in, get copied into the/.extra/global_confext/directory in the initrd execution environment.
Added in version 258.
/.extra/tpm2-pcr-signature.jsonโ the TPM2 PCR signature JSON object included in the".pcrsig"PE section of the unified kernel image gets copied into the/.extra/tpm2-pcr-signature.jsonfile in the initrd execution environment. ๐ฅ
Added in version 252.
/.extra/tpm2-pcr-public-key.pemโ the PEM public key included in the".pcrpkey"PE section of the unified kernel image gets copied into the/.extra/tpm2-pcr-public-key.pemfile in the initrd execution environment.
Added in version 252.
/.extra/profile,/.extra/os-releaseโ the contents of the".profile"and".osrel"sections of the selected profile, if there is one.
Added in version 257.
/.extra/boot-secretโ a 32 byte per-system secret, derived from a 32 byte secret stored in an EFI variable (LoaderBootSecret, see above), which itself is only accessible to the pre-boot environment. this can be used for various early-boot cryptographic purposes, and OS file system access to it is restricted to root. theIMAGE_ID=/ID=data from the".osrel"gets hashed into the secret, to make sure different images get a distinct secret passed. and on top of that, a randomized 32 byte value stored in the ESP in the"/loader/boot-secret-mixin"file gets hashed in as well, so that distinct disks end up with different boot secrets. ๐ฆ
real talk though: this boot secret is ultimately protected only by firmware-enforced access controls on the EFI variable. that's generally a much weaker protection than TPM-based approaches give you, so it is strongly recommended to use the TPM on systems that actually have one. the boot secret is primarily meant to be a lower-security fallback for when a TPM isn't available. ๐
also: applications should never protect resources directly with this secret. instead, derive your own secret from it (by hashing it together with some application ID, in HMAC mode for example), so you don't accidentally leak the primary boot secret.
and one more: the boot secret is only available if the pre-boot environment had a suitable RNG source at the current boot or an earlier one. that source can be an initialized on-disk random seed, or EFI RNG support, or both.
Added in version 261.
last thing, bestie: all of these files live in thetmpfsfile system the kernel sets up for the initrd file hierarchy, which means they get lost when the system transitions from the initrd execution environment into the host file system. ๐ญ if you want these resources to stick around past that transition, you have to copy them somewhere that survives it first โ for example via a suitabletmpfiles.d(5)line. by default, that's already done for the TPM2 PCR signature and public key files. ๐ซถ
โจ SMBIOS TYPE 11 STRINGS โจ
okay so plot twist โ systemd-stub can also get configured via SMBIOS Type 11 strings?? like, the firmware-level strings?? yeah bestie, that's a whole thing ๐
The strings that matter are shaped like a name, then =, then the value. So <italic>name</italic>=<italic>value</italic>, super simple.
But here's the tea โ โ unless systemd-stub detects it's running inside a confidential computing environment, it will go search the table for a string with a specific name, and if it finds one, it uses its value. (iykyk โ confidential computing gets different treatment, that's just how it is ๐)
These are the strings it actually reads:
io.systemd.stub.kernel-cmdline-extraโ if this one is set, its value gets added to the list of kernel command line arguments, and those get measured in PCR12 and passed straight to the kernel. Added in version 254.io.systemd.boot.loglevelโ if this one's set, its value is used as the log level. Valid values, from most critical to least critical, are"emerg","alert","crit","err","warning","notice","info", and"debug". Added in version 261.
๐ ๏ธ ASSEMBLING KERNEL IMAGES ๐ ๏ธ
so you wanna actually build a bootable Unified Kernel Image out of all the various components described above?? ๐
bestie, that's literally what ukify(1) is for. go use it. go off โจ
๐ SEE ALSO ๐
the extended universe, no cap:
systemd-boot(7)systemd.exec(5)systemd-creds(1)systemd-sysext(8)- UAPI.1 Boot Loader Specification[6]
- Boot Loader Interface[5]
ukify(1)systemd-measure(1)- TPM2 PCR Measurements Made by systemd[7]
๐ NOTES ๐
the receipts, all of them, verbatim ๐
- specification โ https://learn.microsoft.com/en-us/windows-hardware/drivers/install/specifying-hardware-ids-for-a-computer
- SBAT โ https://github.com/rhboot/shim/blob/main/SBAT.md
- UAPI.5 UKI specification โ https://uapi-group.org/specifications/specs/unified_kernel_image/
- Automatic Boot Assessment โ https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT
- Boot Loader Interface โ https://systemd.io/BOOT_LOADER_INTERFACE
- UAPI.1 Boot Loader Specification โ https://uapi-group.org/specifications/specs/boot_loader_specification
- TPM2 PCR Measurements Made by systemd โ https://systemd.io/TPM2_PCR_MEASUREMENTS
and that's the whole page bestie, we made it to the end ๐ this little masterpiece is systemd 262.1, page SYSTEMD-STUB(7) โ screenshot it, cite it, cite me, mwah ๐ ๐ฆโจ
๐ฆ where this page comes from
- arch rolling: systemd 262.1-1 ยท LGPL-2.1-or-later AND CC0-1.0 AND GPL-2.0-or-later AND MIT-0
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms