qemu: full OTA apply scenario — write rootfs_b, flip AvbABData, boot it

Closes the loop every prior harness stopped short of, entirely
off-hardware: the real flared (WARDEN_HARD_RESET-gated build) pulls a
real signed tier-1 .wfw whose payload is a BOOTABLE rootfs stamped
0.0.2, verifies it, writes /dev/block/by-name/rootfs_b inside disk.img,
and flips the AvbABData (slot B: priority 15, 3 tries, unsuccessful —
the exact pre-first-boot arming state, round-tripped through a portal
check-in). The harness then boots slot _b and asserts the applied
version + marker are what runs. OTA-APPLY-PASS verified end to end.

- mkimage: the misc partition now carries REAL provisioned AvbABData
  (bytes mirror flare-edge's provisioning defaults) — slotctl fail-closes
  on bad AB magic before writing, which a zeroed misc tripped.
- run.sh --allow-apply / cmdline warden.fwapply: per-boot opt-in that
  makes stage-2 init export WARDEN_FW_ALLOW_APPLY=1; never the default.
- stage-2 init also exports WARDEN_HARD_RESET=0 (the CRU poke is fatal on
  virt, same class as the HPMCU probe); the harness performs the reboot.
- ADR-0006 boundary documented in the scenario and README: BCB slot
  CHOICE and the physical reset remain emulated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HUayid7W5w7jBdb9Rrj1K
This commit is contained in:
BFE Engineering
2026-08-30 10:40:08 -06:00
co-authored by Claude Fable 5
parent ff62991e8d
commit d63c2117c1
5 changed files with 214 additions and 2 deletions
+25 -1
View File
@@ -96,10 +96,15 @@ place_partition() {
rootfs_a|rootfs_b) stage="$ROOT" ;;
userdata) stage="$UDATA" ;;
oem_a|oem_b) stage="$SCRATCH/empty" ;;
# misc carries REAL AvbABData (byte 2048): flared's slotctl fail-closes
# on a bad magic before any OTA write, so a zeroed misc blocks apply
# scenarios. Bytes mirror flare-edge tools/mk-misc.py provisioning
# defaults (A: prio 15 successful, B: prio 14 successful, CRC32-BE).
misc) stage="__misc__" ;;
# Boot-chain partitions the VM never reads: present at the right offsets,
# left zeroed. Enumerated (not a wildcard) so a typo'd name in
# blkdevparts.conf fails HERE, not as a confusing mount error at boot.
env|idblock|uboot|misc|boot_a|boot_b|recovery) stage="" ;;
env|idblock|uboot|boot_a|boot_b|recovery) stage="" ;;
*) echo "FATAL: unknown partition name '$name' in blkdevparts.conf" >&2; exit 1 ;;
esac
# dd in 4K blocks — every offset in the canonical layout is 4K-aligned;
@@ -112,6 +117,25 @@ place_partition() {
[ $((off + size)) -gt "$DISK_END" ] && DISK_END=$((off + size))
[ -z "$stage" ] && return 0
local img="$SCRATCH/$name.img"
if [ "$stage" = "__misc__" ]; then
python3 - "$img" "$size" <<'PYMISC'
import struct, sys, zlib
img, size = sys.argv[1], int(sys.argv[2])
s = bytearray(28)
s[0:4] = b"\0AB0" # AB_MAGIC
s[4] = 1 # major
s[8:12] = bytes([15, 0, 1, 0]) # slot A: priority, tries, successful
s[12:16] = bytes([14, 0, 1, 0]) # slot B
meta = bytes(s) + struct.pack(">I", zlib.crc32(bytes(s)) & 0xFFFFFFFF)
buf = bytearray(size)
buf[2048:2048 + len(meta)] = meta
open(img, "wb").write(buf)
PYMISC
dd if="$img" of="$DISK" bs=4096 seek=$((off / 4096)) \
conv=notrunc,sparse status=none
qemu_log " $name: AvbABData provisioned @ +2048"
return 0
fi
mkfs_part "$stage" "$size" "$img"
dd if="$img" of="$DISK" bs=4096 seek=$((off / 4096)) \
conv=notrunc,sparse status=none