OptiStick (S905X5M) eFuse burn and signed-firmware burn: runbook as run

Wilber Zheng · 2026-11-04 · based on the run of 2026-11-03 on one Shenzhen PVT stick (prd100046, prod root) · written so a person or an agent can repeat it

Irreversible. Stage A (root hashes) and stage B (enables) burn eFuse. After stage B the unit boots only bootloaders signed with our prod KMS keys, for life: no dbg ROM, no unsigned prd pack, no SD burn. Do not run any real (non dry-run) step without the written go of the unit's owner and Frank's OK for that unit.

The authoritative procedure is Frank's team guide in yocto_os branch main-amlinux: claude-dev/frank-analysis/secure-boot/10-team-guide.md (§3 burn, §4 is it fused, §5 fuse, §6–§8 after, recovery, traps). This page is the order we actually ran, the exact commands, what we saw, and the deviations a station without root or a prd ROM without a shell forces on you.

0. The whole run on one page

#StepIrreversibleResult on 11-03
1Get the inputs: prod-signed prd pack, fuse script + pattern at the same commit; check them offlinenochecker rc 0, 4 items at the prod root
2Park the unit in u-boot ADNL ([TPL]) and burn the pack with -r 1 on the unfused unit; prove it bootsnoboots to pairing, optisigns.sb=open
3Dry run stage A (reads only)noall fields 0, rc 0
4Park again with a -r 0 burn, pre-checks (rpmb_state=1, erase tee), stage A realyesboth root hashes written + locked, rc 0
5Cold power-cycle, boot check, boot0/boot1 == pack bootloadernoopen, green, boot0 = boot1 = pack
6Park, latch probe, dry run stage Bnohashes locked, enables 00, rc 0
7Stage B real (the two enables back to back)yesboth enables 01, read-back OK, rc 0
8First fused boot (warm + cold), post-fuse checksnofused, green, locked; refusals as designed

1. Inputs and station

1.1 What you need

1.2 Station deviations we had to make (no root on the station)

2. Park the unit (enter u-boot ADNL) without a shell

A prd ROM has no shell, so the guide's systemctl reboot update is not available. Use u-boot's power-on burn window instead (u-boot patch 178): on every power-on u-boot offers USB-A to a PC for about 3 s (enumerate) + 5 s (a burn tool must identify). The burn tool must already be waiting when the unit powers on.

  1. USB-A of the stick to the station with an A-to-A data cable. USB-C is power.
  2. Start the catch script before powering on. It waits for the unplug, then for the unit to appear as 1b8e:c004, then burns at once with -r 0 so the unit stays parked:
    #!/bin/bash
    IMG=<prod-signed prd pack>; SER=<dnl-serial>; PORT=<usb sysfs port of the unit, e.g. 1-2>; L=$1
    while [ -n "$(cat /sys/bus/usb/devices/$PORT/idVendor 2>/dev/null)" ]; do sleep 0.2; done      # unplugged
    until [ "$(cat /sys/bus/usb/devices/$PORT/idVendor 2>/dev/null):$(cat /sys/bus/usb/devices/$PORT/idProduct 2>/dev/null)" = "1b8e:c004" ]; do sleep 0.1; done
    cd ~/flash/aml-flash-tool/tools/adnl || exit 9
    timeout 900 sudo -n LD_LIBRARY_PATH=./usb_flow ./adnl_burn_pkg -p "$IMG" -s "$SER" -e 1 -b 1 -r 0 -t 0 >> "$L" 2>&1
    echo "burn rc=$?" >> "$L"
    LD_LIBRARY_PATH=./usb_flow ./adnl -s "$SER" getvar identify >> "$L" 2>&1
  3. A person unplugs USB-C, waits ~3 s, plugs it back.
  4. Expect burn successful, then getvar identify → DNL mode [TPL]. That is the park. [BL1] is BootROM DFU (same USB id): not a park, replug and retry.

To park without burning (read-only checks), the same catch with adnl getvar identify in place of the burn holds the unit in the window. We used this for the boot0/boot1 check.

From the park until the step ends, do not touch the cable or power. On a cable-powered stick, pulling USB is a power cut.

3. Burn the signed pack on the unfused unit and prove it

Same catch, with -r 1. Then check on the unit (needs a shell: USB debug toggled on screen + the device-agent cert, or SecureLink):

grep -o 'optisigns.sb=[a-z]*' /proc/cmdline                  # optisigns.sb=open
grep -o 'androidboot.verifiedbootstate=[a-z]*' /proc/cmdline # green
cat /etc/os-release | grep -E 'IMAGE_VERSION|BUILD_ID'       # prd100046 …5103dea85

Guide §5.1 also wants a prd .swu OTA commit on the unfused unit; we did not have one (see §8).

4. Stage A: the two root hashes

4.1 Dry run (reads only)

cd <wt>
SB_ADNL=<adnl wrapper> bash amlogic/flash-host/sb-fuse-usb.sh --serial <dnl-serial> --stage A \
  --expect-hash ota-trust/efuse/prod/keys/root/rsa/s905x5m/roothash/hash-device-rootcert-rsa.bin \
  --pattern ota-trust/efuse/prod/pattern/stage-a.txt --dry-run --log-dir <dir> --foreground

Expect rc 0, every field zero / lock 00. AUDIO_VENDOR_ID lock 00 = PVT, 01 = EVT; both fine.

4.2 Park with a fresh -r 0 burn, then pre-checks

adnl -s <ser> oem "rpmb_state"
adnl -s <ser> oem "env_get rpmb_state"      # response:1   (0 = STOP, never fuse)
adnl -s <ser> oem "amlmmc erase tee"        # OKAY

4.3 Record the go, then the real run

Write one line before running: who gave the go, UTC, unit, ROM md5, script commit + sha256, dry-run rc. Then the same command without --dry-run --foreground. It detaches and prints its log path; wait for <log>.done (check at ≥ 60 s), read the log once. Expect rc=0; it ends with adnl reboot.

11-03: both hashes = abe6fe5d…, lock 01; every other field 0; both enables 00 (the unit is still open).

4.4 After stage A: cold boot + boot0/boot1 check

Cold power-cycle; Linux must show optisigns.sb=open, green, the right serial. Then check that eMMC boot0 and boot1 hold exactly the pack's bootloader. Our ROM's localuser cannot read /dev/mmcblk0boot*, so we did it from the ADNL park (no burn) with u-boot commands. u-boot has no sha256, so we used crc32:

# on the host: unpack the pack, take the bootloader item size and its crc32
~/flash/khadas-utils/aml_image_v2_packer -d <img> <dir>          # bootloader.PARTITION
SECT = size/512 (hex)   CRC = crc32(bootloader.PARTITION)            # never assume the size

# on the parked unit (each line: adnl -s <ser> oem "…")
mmc dev 1 1 ; mmc read 0x1080000 1 <SECT>        # boot0, from block 1
mmc dev 1 2 ; mmc read 0x1800000 1 <SECT>        # boot1
mmc dev 1 0
crc32 0x1080000 <size> 0x3100000                 # crc of boot0 stored at 0x3100000
crc32 0x1800000 <size> 0x3100010                 # crc of boot1
mw.l 0x3200000 <CRC>   ; mw.l 0x3200010 <CRC byte-swapped>   ; mw.l 0x3200020 0x12345678   # control
cmp.l 0x3100000 0x3200000 1 ; cmp.l 0x3100000 0x3200010 1 ; cmp.l 0x3100000 0x3200020 1
cmp.l 0x3100010 0x3200000 1 ; cmp.l 0x3100010 0x3200010 1 ; cmp.l 0x3100010 0x3200020 1
cmp.b 0x1080000 0x1800000 <size>                 # boot0 == boot1, byte for byte

Pass = boot0 and boot1 each match one of the two byte orders (ours: the swapped form), the 0x12345678 control fails, and cmp.b is equal over the full size. Mismatch: stop, no stage B. Do this before stage B: after the enables, u-boot refuses mmc and printenv over ADNL.

5. Stage B: the two enables

  1. Re-park (§2, read-only park is enough; a fresh -r 0 burn is what the guide asks) and repeat the §4.2 pre-checks.
  2. Latch probe: adnl -s <ser> oem "setenv bootdelay -1" → OKAY. FAILED … H1b here = stop, call Frank.
  3. Dry run B:
    SB_ADNL=<adnl wrapper> bash amlogic/flash-host/sb-fuse-usb.sh --serial <ser> --stage B \
      --expect-hash ota-trust/efuse/prod/keys/root/rsa/s905x5m/roothash/hash-device-rootcert-rsa.bin \
      --pattern ota-trust/efuse/prod/pattern/pattern.txt --dry-run --log-dir <dir> --foreground
    rc 0; hashes = prod root, lock 01; enables 00; it prints would write: FEAT_ENABLE_DEVICE_VENDOR_SIG FEAT_ENABLE_DEVICE_SCS_SIG.
  4. Record the go, real run (no --dry-run --foreground). It writes FEAT_ENABLE_DEVICE_VENDOR_SIG 01 then FEAT_ENABLE_DEVICE_SCS_SIG 01 back to back, reads all back, ends with adnl reboot. Nobody touches cable or power until <log>.done shows rc=.
The two enables are one atomic step. A power loss between them leaves a unit that can never be completed: stop, quarantine, escalate to Frank. Never type efuse_obj set by hand.

6. After the fuse: checks we ran

CheckHowExpected / seen 11-03
First boot (warm)after the script's adnl rebootpairing screen in ~40 s
Cold bootpull + replug USB-C; shell via USB debugoptisigns.sb=fused, verifiedbootstate=green, vbmeta.device_state=locked, slot _a, prd100046
u-boot hardening (H1b)park; oem "printenv bootcmd", oem "mmc dev 1 1"FAILED … OptiSigns H1b: … not allowed when fused
eFuse read still allowedoem "efuse_obj get FEAT_ENABLE_DEVICE_SCS_SIG"01
Unsigned loader refusedadnl reboot romusb → [BL1]; tools/secure-boot/loader-only/sb-loader-only.sh --loader <DDR.USB of a dbg pack> --loader-sha256 <sha> --serial <ser> --expect refusePASS: Fail in first sect, unit stays [BL1]
Signed burn still works (positive control)from [BL1]: adnl_burn_pkg -p <prod-signed pack> -e 1 -b 1 -r 1 -t 0burn successful, BL1 → BL2 → TPL, boots fused/green
Recovery on a fused unithold the button at power-on → recovery menu → Factory Resetrecovery image verified; system/boot/vbmeta restored from golden; new pairing code; boots fused/green

On this station sb-loader-only.sh needed a burn wrapper as SB_LO_BURN because sudoers forbids sudo env; it passes the shim's variables to sudo explicitly:

#!/bin/bash
cd "$HOME/flash/aml-flash-tool/tools/adnl" || exit 2
V=(); for n in LUASHIM_REPLACE_DIR LUASHIM_DUMP_DIR LUASHIM_REQUIRE; do [ -n "${!n:-}" ] && V+=("$n=${!n}"); done
exec sudo -n "${V[@]}" LD_PRELOAD="$LUASHIM_SO" LD_LIBRARY_PATH=./usb_flow ./adnl_burn_pkg "$@"

7. Stop rules (from the guide, the ones that matter on the day)

8. Not covered on 11-03

9. Evidence to keep per unit

One record file per unit (template: guide §5.7) plus raw logs: catch/park logs, the pre-check log, each dry-run and real-run log with its .done, the boot0/boot1 crc log, post-fuse logs, and screenshots of each boot. Keep the unit's serials in that private record only, never in shared pages.