Conversation
9a7271f to
6d736e6
Compare
FIT signature verification, landed in the previous change, is enforced only on the FIT path. The environment lives in the unsigned, writable uboot-env partition (CONFIG_ENV_IS_IN_MMC=y in the vendor defconfig), and neither CONFIG_CMD_BOOTI nor CONFIG_LEGACY_IMAGE_FORMAT was disabled, so anyone with offline write access to the card could rewrite bootcmd to boot a raw unsigned kernel and skip verification without touching the FIT at all. That is the threat this work's own threat model names, so the capability was left undeclared until it was closed. The permit list ships as a U-Boot patch rather than a config fragment because it has to. CONFIG_ENV_WRITEABLE_LIST is a Kconfig symbol, but the list it consults is CFG_ENV_FLAGS_LIST_STATIC, a C define in a board config header that no fragment can set. Enabling the symbol alone would leave the list at its empty default, which rejects every variable arriving from the saved environment - avocado_boot_slot included - and would silently stop A/B updates taking effect. The two ride the same recipe gate for that reason. The nine permitted variables are the union of what actually writes this board's environment: rootdisk.conf's uboot_setenv calls across the complete and upgrade tasks, and the activate map in the stone manifest that avocadoctl's SlotAction::UbootEnv iterates. The per-slot version and uuid stamps are deliberately absent - nothing sets them, the upgrade tasks only unset them, and the boot script never reads them. Keyed autoboot rather than CONFIG_BOOTDELAY=-1, which would close the serial path outright. Every documented recovery for this board goes through the U-Boot prompt, ums being the flashing path, and the bench board's SRK fuse is already permanently burned, so removing recovery would leave it one failure from unrecoverable. Keying also fixes a real availability bug: this board's serial line intermittently injects stray bytes that interrupt autoboot with no keypress. The residual exposure is recorded in a debt marker rather than left implied, and the capability declaration that depends on this is gated on hardware verification. Signed-off-by: Javier Tia <javier@peridio.com>
093f504 pinned PREFERRED_VERSION_u-boot-imx to 2025.04 in avocado-imx.inc for every i.MX machine, because 2026.04's stock defconfigs enable EFI capsule authentication and USB DFU and do_compile fails on boards carrying neither fix. That is the right default. It is the wrong answer for this board, which already turns both off at source via no-efi-capsule-auth.cfg and disable-unused-vendor-features.cfg, so override it back here. 2026.04 is a requirement on this machine rather than a preference. The compiled-in environment selects its source with CONFIG_ENV_SOURCE_FILE, resolved against board/$(SYS_VENDOR)/$(SYS_BOARD): board/nxp/imx93_frdm on 2026.04, board/freescale/imx93_frdm on 2025.04. u-boot-imx_%.bbappend refuses to build on the older layout rather than emit a bootloader with no Avocado boot flow compiled into it, so under the layer pin this machine does not build at all. The downgrade is independently destructive: 2025.04 carries CONFIG_SYS_BOOTM_LEN=0x4000000 against 2026.04's 0x8000000, halving the bootm budget for a FIT that currently uses about 60% of it. A bootloader flash under the old pin produced a board that ran SPL and never reached U-Boot, recoverable only over uuu serial download. Placed after the requires rather than beside the other machine variables, since avocado-imx.inc is pulled in at line 10 and an assignment above it would be overwritten by the layer-wide value instead of overriding it. Signed-off-by: Javier Tia <javier@peridio.com>
The patch was generated against the 2025.04 header, which the pin removal in the previous commit no longer selects. On 2026.04 the same hunk still applies, but at an 11-line offset with fuzz 1, and Yocto's patch-fuzz QA fails the task rather than accepting a patch that no longer matches its context. Regenerated against the pristine 2026.04 source. The permitted set is unchanged - the nine variables that actually write this board's environment - and only the surrounding context moved. Note 2026.04 spells the MMC env device CONFIG_ENV_MMC_DEVICE_INDEX where 2025.04 used CONFIG_SYS_MMC_ENV_DEV, which is what shifted the anchor.
The env permit list added alongside FIT verification does not merely harden this board, it stops it booting. The whole Avocado boot flow - bootcmd, avocado_boot_init, load_image, avocado_boot and the variables they read - reached U-Boot only as the saved environment: u-boot-env.inc runs mkenvimage over env/avocado-imx93-frdm.txt and fwup writes the result to the uboot-env partition. env/mmc.c imports that copy with H_EXTERNAL, and env/flags.c drops every H_EXTERNAL variable missing the 'w' access flag, so all thirteen were rejected while the compiled-in default supplied only console and a stock bootcmd. The board would have come up running U-Boot's own boot command with no Avocado boot path at all. Move the flow to board/nxp/imx93_frdm/avocado.env, selected by CONFIG_ENV_SOURCE_FILE. Compiled-in variables arrive with H_DEFAULT, which env_flags_validate returns early for, so the flow is immune to the permit list by construction rather than by being listed on it - listing bootcmd would have reopened the offline bypass the list exists to close. Delivered unconditionally for this machine rather than under the verified-boot gate, matching fit.cfg. A build without the feature still imports the saved environment and it still wins, so those builds are unaffected; what the unconditional form buys is that both build modes run one authored copy of the flow instead of diverging. env/ avocado-imx93-frdm.txt therefore drops to provisioning state only, because a second copy would be inert under verified-boot and authoritative without it. devnum and mmcblk join the permit list. They are the one pair that cannot come from the compiled-in default: stone-provision-uuu-emmc.sh rewrites them with fw_setenv directly in the built image, after mkenvimage has already run, so an eMMC-provisioned device would otherwise fall back to the SD defaults and look for its boot partition on the wrong medium. They select the boot device, not what runs from it, and the FIT signature check still gates execution. avocado.env includes the vendor board .env rather than replacing it. That file was already in effect through the Makefile ENV_FILE_BOARD wildcard and supplies initrd_high plus the fastboot and manufacturing helpers uuu recovery depends on; dropping it would have broken the recovery path this board has already needed once. CONFIG_ENV_SOURCE_FILE is set explicitly instead of relying on the board-name default, because the default resolves through a $(wildcard): a path that stops resolving yields an empty environment silently, while the explicit form fails the build. For the file carrying the boot command, the loud failure is the one worth having. Signed-off-by: Javier Tia <javier@peridio.com>
Drop CONFIG_AUTOBOOT_KEYED and its stop string on this machine. This is an ergonomics decision, not a defect report, and the distinction matters because the first version of this change argued the opposite. The stop string was never shown to be broken. A day went into "it does not work" - sent through a confirmed 2.001-second window at 137 chars/sec and again at 44 chars/sec, plus hand-typed pastes, all failing - and the cause was the sender, not the board. The host script paced itself on a read timeout, and tio's read returns the instant bytes are waiting rather than sleeping out its timeout, so during a boot flood it spun and pushed thousands of bytes per second. That overran the LPUART FIFO, so passwd_abort_key(), which drains one character per 10ms udelay, never saw a clean sequence. Throttled to ~8 chars/sec an unkeyed keypress broke in first try, with nothing about the board or the Kconfig changed. What the keying actually cost was a day of working around it, and the reason to drop it now is that every documented recovery for this hardware goes through the U-Boot prompt - "ums 0 mmc 1" is the flashing path - and reaching it should not depend on getting a host-side send rate right. The line-noise exposure is accepted knowingly rather than dismissed: unkeyed, the stray bytes this board's serial line injects can halt an unattended boot, which is why the debt marker names unattended operation as the upgrade trigger. Re-enabling the keying when the board joins a HIL runner needs no new investigation - the string works, and the only thing that ever defeated it is now understood.
The offline-card bypass this capability names is now closed and proven on hardware, so the declaration deferred out of imx93-fit-verified-boot task 7.1 can finally be made. Until now kas/feature/verified-boot.yml appended the token to DISTRO_FEATURES and every consumer gated on it independently, so asking for it on a machine with no FIT signing key wired produced an ordinary unsigned build and no diagnostic - the silent-success failure this class exists to remove. Declared unconditionally rather than derived from DISTRO_FEATURES. The token states what the MACHINE can deliver: the signing wiring lives in this conf and in u-boot-imx_%.bbappend, so it is a property of the board's configuration and true whether or not a given build asks for it. Deriving it from the request would make the refusal tautological, for the same reason already spelled out for tpm2 one paragraph above it. The declaration names its residuals in the conf rather than implying they do not exist, because an over-claim here is exactly what 7.1 was deferred to avoid. Serial console access is one, and it is WIDER than the plan assumed: the design called for a keyed autoboot stop phrase, so the residual was to have been "anyone who reads the phrase from the source". That keying was removed for ergonomics, so it is now plain physical serial access. booti is the other - CONFIG_CMD_BOOTI cannot be disabled, since boot/Kconfig:446 selects it from a promptless BOOT_DEFAULTS_CMDS the bootstd stack needs, so the command exists and is merely unreachable from the saved environment. Both directions were checked rather than assumed. Requesting verified-boot on avocado-imx93-frdm parses clean; requesting it on avocado-qemux86-64, which declares "encrypted-var tpm2", now fails with a message naming the machine, the feature and the fix. Without the AVOCADO_SECURITY_FEATURES entry that second build would have succeeded and shipped an unsigned image that had been asked to be signed. Signed-off-by: Javier Tia <javier@peridio.com>
…heckable This change's own bypass-closed check was verified by hand: a person power- cycled the board, typed at the U-Boot prompt, and read the result. That is real evidence and it is also unreadable by anything downstream, so the task carried a prose verify: line, landed at assertion class against a test-class floor, and blocked the change it was verifying. Nothing was wrong with the work; the evidence just could not be re-run. The board became scriptable once power control and a console profile were both available, so the check is now a program. It asserts the two properties that matter and that pull against each other: a saved bootcmd must NOT survive an import, or the offline environment bypass is open, and a saved avocado_boot_slot MUST survive it, or the permit list closed the hole by breaking OTA instead. Recording the verdict rather than driving the board from verify: is deliberate. A run power-cycles hardware and takes minutes, well past the 120s per-command timeout a verification pass allows, so the harness records and check-result.sh reads. The record names the commit it ran against and is rejected unless that commit is an ancestor of HEAD, so a stale pass cannot vouch for a tree it never saw. The unimplemented modes exit non-zero instead of doing nothing. A stub that succeeds is worse than a missing one - it turns an absent check into a passing verify: line, and the task it gates then reads as verified. Two failure modes are commented in the script because both were paid for on this board. Sends are paced with msleep and never on a read timeout: tio's read returns as soon as bytes are waiting, so a loop paced on it floods a console that drains far slower, which produced three separate and wrong conclusions that the hardware was broken. And boot markers are latched as they stream rather than matched in the tail at the end, because a boot is far larger than any bounded buffer and an early marker is long gone by the time a late one arrives - that one was written, run, and observed failing here before it was fixed. Signed-off-by: Javier Tia <javier@peridio.com>
bcbafde to
86d5a7b
Compare
|
Stacked this locally with #316 (FIT boot on imx8mp-evk) to test the i.MX branches together; both parse and imx8mp-evk boots the combined tree. One interaction to fix here before merge:
What I applied locally: move the symbol into d.appendVar('SRC_URI', ' file://fit-verify.cfg')
if d.getVar('MACHINE') == 'avocado-imx93-frdm':
d.appendVar('SRC_URI', ' file://env-writeable-list.cfg file://env-writeable-list.patch')A board then adopts the lockdown together with its compiled-in env and permit list. Also |
|
Merged into wrynose via #316 (rebased, commits preserved). Closing as included. |
Closes the offline environment bypass on
avocado-imx93-frdm: with FITsignature verification on, a saved U-Boot environment could still redirect the
boot, so the signature guarded the payload while the thing choosing the payload
stayed writable by anyone who could reach the card.
The bypass, and why the obvious fix was not enough
CONFIG_ENV_WRITEABLE_LISTplus a permit list is the mechanism. But turning iton alone would have bricked the board, and that is worth stating because it was
found by reading rather than by flashing.
This machine's entire boot flow reached U-Boot only as the saved
environment -
u-boot-env.incruns mkenvimage overenv/${MACHINE}.txtand fwupwrites the result.
CONFIG_ENV_WRITEABLE_LISTrejects everyH_EXTERNALvariable missing from the list (
env/flags.c:561-563), and the saved copy isimported
H_EXTERNAL(env/mmc.c:547). All 13 flow variables would have beendropped while the compiled-in default supplied 2, leaving the board on NXP's
stock
bootcmd.So the flow is now compiled in via
CONFIG_ENV_SOURCE_FILE. Compiled-invariables arrive
H_DEFAULT, whichenv_flags_validatereturns early for, sothe boot flow is immune by construction rather than by being listed - listing
bootcmdwould have reopened the hole it closes.env/${MACHINE}.txtdeliberately keeps its own full copy.env_load()seeds thebuilt-in default before the storage driver only under
CONFIG_ENV_WRITEABLE_LIST; with the feature off the table is built from thesaved environment alone, so a reduced
.txtwould leave those builds with nobootcmd. There is a debt marker on the duplication.The permit list
devnumandmmcblkare:dw, not:sw, and that distinction is the securitycontent.
_env_flags_validate_type's string case is a barebreak, so:swaccepts anything:
mmcblk="1p6 init=/bin/sh x"flows intorootdev, then intobootargs, which the FIT signature does not cover - a root shell on a verifiedkernel.
devnumthe same way, intoloadargument injection. Decimal rejectstrailing non-digits and still admits 0 and 1.
u-boot-imx stays on 2026.04 here
093f504 pinned
PREFERRED_VERSION_u-boot-imx = "2025.04"inavocado-imx.incfor every i.MX machine, because 2026.04's stock defconfigs enable EFI capsule
authentication and USB DFU and
do_compilefails on boards carrying neitherfix. Right default; wrong answer for this board, which turns both off at source
already. Overridden back per-machine, after the
requireso it actually wins.2026.04 is a requirement rather than a preference here:
CONFIG_ENV_SOURCE_FILEresolves againstboard/$(SYS_VENDOR)/$(SYS_BOARD)-board/nxp/imx93_frdmon 2026.04,board/freescale/imx93_frdmon 2025.04. Thebbappend refuses to build on the older layout rather than emit a bootloader with
no Avocado boot flow in it. The downgrade is independently destructive:
CONFIG_SYS_BOOTM_LENhalves from0x8000000to0x4000000, and one flashunder the old pin produced a board that ran SPL and never reached U-Boot.
Autoboot is interruptible by any key
The series originally added
CONFIG_AUTOBOOT_KEYEDwith a stop string. It isremoved - as an ergonomics decision, not a defect report. An earlier
revision of this PR argued the stop string was broken; that was wrong and is
corrected here.
A day went into "the stop string does not work" - sent through a confirmed
2.001-second window at 137 chars/sec and again at 44 chars/sec, plus hand-typed
pastes, all failing. The cause was the sender, not the board. The host script
paced itself on a read timeout, and
tio's read returns the instant bytes arewaiting rather than sleeping out its timeout, so during a boot flood it spun and
pushed thousands of bytes per second. That overran the LPUART FIFO, and
passwd_abort_key()- which drains one character perudelay(10000)- neversaw a clean sequence. Throttled to ~8 chars/sec, an unkeyed keypress broke in on
the first try with nothing about the board or the Kconfig changed.
So the trade is convenience against unattended safety. Convenience wins for now
because every documented recovery for this hardware goes through the U-Boot
prompt (
ums 0 mmc 1is the flashing path), and reaching it should not dependon getting a host-side send rate right.
The cost is accepted knowingly and carries a debt marker: unkeyed, this board's
serial line can halt an unattended boot on stray bytes, which is fine for a
bench unit with a human watching and is not fine for a HIL runner. Re-enabling
the keying then needs no new investigation - the string works, and what defeated
it is now understood.
Verified on hardware
Flashed and booted on FRDM-IMX93. Confirmed on the board:
bootcmdis refused:env set bootcmd ...+saveenvaccepted andwritten, and after reset the compiled-in command still runs.
avocado_boot_slotis still honoured, so A/B switching survives.Verifying Hash Integrity ... sha256,rsa2048:FIT+ OK.bootiis present but unreachable from the saved environment.Hit any key to stop autoboot: 2and any key reaches theprompt.
do_configureselects 2026.04 under the layer-wide 2025.04 pin, confirmed bywhich work directory it touched.
A/B upgrade verified
The
-t upgradecycle has now been run on hardware, over UMS from the U-Bootprompt:
fwup: Upgrading boot slot B- correctly selectedupgrade.bfrom therunning slot A.
environment (
CONFIG_ENV_OFFSET=0x400000,CONFIG_ENV_OFFSET_REDUND=0x440000),with the live copy identified by the higher flags byte (primary 15 >
redundant 14).
avocado_boot_slotbbb.avocado_platformb.avocado_architectureBoth
b.stamps were undefined before the upgrade. The asymmetry is why bothhalves have to be read: the stale copy alone reads as a partial write.
Booting B,Verifying Hash Integrity ... sha256,rsa2048:FIT+ OKthree times, and reached userspace - so the slot switch, the signature
enforcement and the upgraded slot all hold together.