// guide
Secure Boot
What Visor verifies, what the firmware verifies, and what neither does.
Behavior under Secure Boot
- PE images (Windows Boot Manager, UKIs, EFI-stub kernels, chainloaded loaders): if shim is present, Visor asks its
SHIM_LOCKprotocol to verify the image and refuses the boot on failure. With or without shim, the firmware's ownLoadImageverification still applies — an unsigned or untrusted image is rejected by the firmware itself. - Raw (non-PE) kernels boot through the EFI handover protocol, which bypasses firmware verification entirely — so under Secure Boot, Visor refuses them unless shim verifies them first.
- With Secure Boot off, everything boots normally.
When the firmware rejects an image, Visor logs the exact EFI status code and
says plainly that Secure Boot refused it (EFI_SECURITY_VIOLATION /
EFI_ACCESS_DENIED), with a hint to sign/enroll the image or disable
Secure Boot — no more guessing from a vague failure.
Getting a fully signed chain
Two workable models:
- Your own keys (sbctl) — enroll custom keys, then sign
visor_x64.efi, your kernels/UKIs, and any EfiFs driver. The installer offers this (install.sh --sign), andvisor signre-signs everything installed on the ESP, drivers included. - Shim — boot Visor via
shimx64.efi; shim then validates your kernels against the enrolled db/MOK certificates, and Visor consults shim for every image it launches.
sbctl sign, or disable
Secure Boot for that boot.Shim and MOK
Visor can be booted through shimx64.efi, which validates Visor
itself and every image Visor launches against the enrolled db/MOK
certificates. The full walkthrough lives in the repo's
Secure Boot via shim + MOK guide:
add the SBAT section to drivers that lack one, sign Visor and every driver,
enroll the key with mokutil --import, then reboot and choose
Enroll MOK in MokManager. Run the signing steps in a root shell, and
repeat them after every Visor update.
Builds embed an .sbat section generated from
sbat.csv.in and stamped with the Visor version, so shim does not
refuse the image for missing SBAT metadata. A Secure Boot revocation list
update can still revoke Visor through SBAT, exactly as it can revoke any other
bootloader; the fix is a newer signed build.
Windows and BitLocker
When Windows is chainloaded from Visor, shim and Visor are part of Windows'
measured boot. BitLocker can then lose its PCR 7 binding, or bind to PCR 4, and
ask for the recovery key after every Visor or shim update. Set
windows_bootnext=1 to hand Windows to the firmware's own
Windows Boot Manager
entry with BootNext plus a reset, so
Windows starts without shim or Visor in its measured path. Visor only uses a
firmware entry on the selected partition, skips the handoff when no matching
entry exists or the handoff marker cannot be stored, and falls back to a normal
chainload if the handoff did not start Windows. An entry that pins
sha256= always stays on the verified chainload path.
Scope of the guarantee — read this part
Secure Boot / shim verification covers the kernel or UKI image only.
It does not cover the initrd, the kernel command line, or
boot.conf — an attacker with write access to the ESP could alter those
without failing verification. (This is true of most boot managers;
systemd-boot has the same property for non-UKI entries.) To close that gap:
- Boot a UKI — kernel, initrd, and cmdline are one signed PE image, verified as a unit; or
- pin the kernel hash with the
sha256=entry key (boot refused on mismatch), and store the initrd as a VISORENC container — its HMAC-SHA256 check refuses tampered files.
Measured boot (TPM2)
When a EFI_TCG2_PROTOCOL is present Visor measures the on-disk boot.conf into PCR 5 and the exact kernel command line (NUL-inclusive) into PCR 12 via HashLogExtendEvent. The PCR 5 event uses EV_EVENT_TAG 0xf5bc582a so a Visor boot and an sd-boot boot land the same log entry; PCR 12 matches systemd's tpm_log_load_options layout so a LUKS2 sealed-key policy for PCR 12 stays portable.
The active PCR banks are published as volatile LoaderTpm2ActivePcrBanks and advertised in LoaderFeatures (TPM2_ACTIVE_PCR_BANKS). Configure with tpm=1 (default, alias measure) and tpm_pcr_config / tpm_pcr_cmdline (0–23, defaults 5 and 12). Set tpm=0 to skip entirely. The firmware log is checked — if the measured data would be silently ignored, Visor logs and continues rather than pretending to measure.
An unusable firmware TPM cannot hang the boot. Visor arms the
VisorTpmStage NVRAM marker before the
EFI_TCG2_PROTOCOL probe and clears it after; a boot that finds
the marker still armed concludes the previous boot died inside the probe,
skips measured boot for that boot, and leaves it off on subsequent boots
instead of risking a hang every Nth boot. Clear the variable to re-enable
measured boot. If the marker cannot be stored, the probe is skipped, and the
capability block is validated against a full struct and a sane manufacturer
ID before any bank is trusted.
Tip — combine a UKI (signed, therefore verified) with a TPM-sealed LUKS key for PCR 12 and you cover both Secure Boot (the image) and measured boot (the cmdline) without extra tooling.
Other hardening in the load path
SHIM_LOCK.Verifyis declared with the platform C ABI, notEFIAPI/ms_abi, matching 64-bit shim. Calling it through the wrong ABI could crash or corrupt the call.- Image decode budgets: PNG/BMP dimensions capped (8192 per side, 16M pixels) so a crafted image cannot exhaust memory. MP4 sample tables are size-checked before use, every sample offset and length is bounded against the file, and capture PNG row-stride and allocation arithmetic are checked for wrap.
- File replacement truncates the destination first, writes the complete buffer, verifies the byte count and flushes before the old name is removed. Self-heal also refuses to rewrite a malformed or oversized
BootOrder, and rejects a zero-size fallback binary. - GPT scanning treats an entry with a non-zero type GUID as used even when its LBA range is zero, and diagnoses that state instead of ignoring it.
- EfiFs driver downloads are pinned to a fixed upstream release and SHA-256 verified before install.
- Config and directory parsing validate lengths and structures (no fixed line limits, no truncated device paths).
- Decrypted buffers and captured passwords are wiped after use.