diff --git a/build/mk-bootimg.sh b/build/mk-bootimg.sh index e23cd76..77d1071 100755 --- a/build/mk-bootimg.sh +++ b/build/mk-bootimg.sh @@ -130,38 +130,59 @@ cat > "$WORKDIR/boot.its" <<'ITS' }; ITS -# - SUB-IMAGE ALIGNMENT (`-B 0x800`). `-p` only places the FIRST payload; the -# rest are packed contiguously, and U-Boot reads each one by dividing its -# data-position by the 512-byte block size with a TRUNCATING divide. A -# payload that is not block-aligned is therefore read from the wrong offset: -# the RESC loader's sha256 fails and logo.bmp silently does not load -# (observed 2026-09-01, flare-edge 9387cff). +# - SUB-IMAGE ALIGNMENT (`-B 0x200`). `-p` places only the FIRST payload; the +# rest are packed contiguously. U-Boot reads each with +# `blk_off = (FIT_ALIGN(fdt_totalsize) + offset) / blksz` (fit.c:331), a +# truncating divide by the 512-byte eMMC block, where FIT_ALIGN rounds to +# IMAGE_ALIGN_SIZE = 512 (include/image.h:955-958). So TWO things must hold: +# the metadata size must be a multiple of 512, or FIT_ALIGN(T) != T and +# EVERY payload is read late by the difference; and each data-position must +# be a multiple of 512, or the divide drops the remainder. # -# MEASURED, both directions. Without -B, this script produced kernel at -# 0x9A94 (data-position % 512 = 148) -- misaligned, exactly the failing -# shape. The vendor images that actually boot this board are aligned to -# 0x800, not merely to 512: boot.img has fdt/kernel/resource at 0x800, -# 0x12800, 0x3A7800 and recovery.img at 0x800, 0x12800, 0x3F8800 -- every -# one a multiple of 2048. +# Measured: without -B this script emitted the kernel at data-position +# 0x9A94 (% 512 = 148) with metadata 1064 (% 512 = 40) -- both faults at +# once. flare-edge 9387cff hit exactly this on hardware: "resource: sha256 +# Bad hash" then "No resource file: logo.bmp", fixed by -B 0x200 and +# confirmed by a clean boot. # -# 9387cff recorded `-B 0x200` from that failure. It is right but weaker than -# it needed to be: it was inferred from noticing the proven image was -# "512-aligned" without checking for a larger factor. 0x800 satisfies -# everything 0x200 does (every 0x800 multiple is a 0x200 multiple) and -# additionally reproduces the vendor layout exactly, for at most 2 KiB of -# padding. When the two disagree, match the image that is known to boot. -echo "== FIT (external data, -E -p 0x800 -B 0x800)" -( cd "$WORKDIR" && mkimage -f boot.its -E -p 0x800 -B 0x800 boot.img >/dev/null ) +# 512, NOT 2048. An earlier revision of this comment claimed the vendor +# aligns to 0x800 and gated on it. That was drawn from two payloads in one +# build. Across the vendor's own release images on this machine, every +# data-position is a multiple of 512 and only some are multiples of 2048 +# (four of eight RELEASE_TEST boot.img files sit at % 2048 = 1536), so a +# 2048 gate rejects images the vendor shipped and U-Boot boots. 0x800 is the +# vendor's -p value -- the absolute position of the first payload -- not an +# alignment. +# +# The flag is feature-detected because the SDK vendors mkimage 2017.09, +# which has no -B at all and dies with "invalid option -- 'B'". Its packer +# already 512-aligns, so omitting the flag there is correct rather than a +# fallback. project/build.sh prepends the SDK tool dir to PATH, so that +# binary IS what a build inside the SDK environment resolves. +if mkimage 2>&1 | grep -q -- '-B '; then + _align_flag="-B 0x200" +else + _align_flag="" + echo "== mkimage has no -B (vendor 2017.09); it 512-aligns natively" +fi +echo "== FIT (external data, -E -p 0x800 ${_align_flag:-no -B})" +( cd "$WORKDIR" && mkimage -f boot.its -E -p 0x800 ${_align_flag} boot.img >/dev/null ) -# Every payload must land on a 0x800 boundary. This is the check that would have -# caught the unaligned build before it reached a panel, so it runs on every -# build rather than living in a separate tool nobody remembers to invoke. +# Assert what U-Boot actually requires, on every build: the failure is silent -- +# a misread offset does not fail the build, it fails on a panel, and sometimes +# only as a missing logo. +_meta="$(od -An -tu4 -j4 -N4 --endian=big "$WORKDIR/boot.img" | tr -d ' ')" +if [ $(( _meta % 512 )) -ne 0 ]; then + echo "FATAL: FIT metadata is $_meta bytes, not a multiple of 512;" >&2 + echo " FIT_ALIGN would round it up and every payload reads late" >&2 + exit 1 +fi for _n in fdt kernel resource; do _pos="$(fdtget -t u "$WORKDIR/boot.img" "/images/$_n" data-position 2>/dev/null || true)" [ -n "$_pos" ] || continue - if [ $(( _pos % 2048 )) -ne 0 ]; then - echo "FATAL: /images/$_n data-position $_pos is not 0x800-aligned;" >&2 - echo " U-Boot's block divide would read it from the wrong offset" >&2 + if [ $(( _pos % 512 )) -ne 0 ]; then + echo "FATAL: /images/$_n data-position $_pos is not 512-aligned;" >&2 + echo " U-Boot's truncating block divide would read the wrong offset" >&2 exit 1 fi done @@ -171,7 +192,7 @@ done # a panel that will not come back. meta="$(od -An -tu4 -j4 -N4 --endian=big "$WORKDIR/boot.img" | tr -d ' ')" total="$(stat -c %s "$WORKDIR/boot.img")" -if [ "${meta:-0}" -gt 65536 ] || [ "${meta:-0}" -ge "$total" ]; then +if [ "${meta:-0}" -ge 4096 ] || [ "${meta:-0}" -ge "$total" ]; then echo "FATAL: FIT metadata is ${meta} bytes of a ${total}-byte image: that is an" >&2 echo " embedded-data FIT and U-Boot will report 'No fit blob'." >&2 exit 1