Skip to content

Commit 5dea38b

Browse files
dkulpclaude
andcommitted
fix(bb64): validate and repair the PocketBeagle2 EEPROM before flashing eMMC
Some industrial boards leave the factory with the identity EEPROM only partly programmed. u-boot's SPL applies the 1GB DDRSS timings only when the EEPROM identifies the board as the industrial variant, so those boards come up as the 512MB base board -- and flashing one produced a finished-looking unit that was wrong in a way nothing downstream would notice. The eMMC is the tell: only the industrial has it populated, and mmcblk0 is the only target this profile ever flashes. So flash_storage.sh's bb64 preflight, previously a no-op, now checks the EEPROM and repairs it where it can, before anything is written. A preflight die already runs under the same EXIT trap as every other phase, so an abort ends with all user LEDs blinking rather than with a board that looks done. The detect/repair/verify logic moves out of BB64-AutoFlash.sh into a shared check_pb2_eeprom.sh so the boot-time flasher and the interactive one cannot drift. BB64-AutoFlash.sh's fail() moves above its first use: the EEPROM check is now the first thing that can fail, and sh only resolves a function once it has read the definition. An EEPROM that cannot be repaired now stops the flash instead of warning and continuing. There is no EEPROM part on this board -- an MSPM0L1105 emulates a 24c32 at 0x50 -- so when its firmware was never flashed, 0x50 does not ACK, at24's probe read fails, and the driver never binds. Nothing can be written to a silent device, so the only honest answer is to refuse. The same MSPM0 also emulates the ADC at 0x20, which separates three faults that need different people to look at them: neither bound (MSPM0 not running), ADC bound but 0x50 silent (MSPM0 running, firmware fault), and no i2c client at all (device tree, not the board). Its own address at 0x48 is useless for this -- it does not ACK there even on a healthy board. Also fixed, all of which reported success on boards that were not repaired: - The repair is now re-read and re-verified. A sysfs write to a write protected or wedged part looks exactly like a successful dd. - A read that fails with EIO yielded an empty header, which read as "blank" and sent the board down the repair path to abort later complaining about the write. Short reads are now rejected up front. - A base-board ID programmed on hardware with eMMC now aborts. The fix scripts merge only over 0xFF bytes, so they cannot correct an EEPROM that is fully but wrongly programmed; running one would have changed nothing and said it worked. - [ $OLDNUMHEX != ... ] was unquoted, so an empty read was a syntax error rather than a branch. The two sysfs paths in the fix scripts and the four in the checker are overridable so the failure modes can be exercised against fixtures; nothing in FPP sets them. Verified on a PocketBeagle2 Industrial: fixtures byte-identical to real PB2I and PB20 dumps, and the driver-unbind controls reproduce the real sysfs shape for each MSPM0 fault. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent b55176e commit 5dea38b

5 files changed

Lines changed: 318 additions & 101 deletions

File tree

SD/BB64-AutoFlash.sh

Lines changed: 25 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -20,100 +20,14 @@ DEVICE=/dev/mmcblk0
2020
mount -t vfat /dev/mmcblk1p1 /boot/firmware
2121
mount -t tmpfs /tmp
2222

23-
echo "---------------------------------------"
24-
25-
# Some PB2i boards ship with blank or improperly-programmed EEPROMs.
26-
# The factory write is two-step: (1) board template, (2) serial number.
27-
# Either or both steps can fail, so check both:
28-
# - Magic header (bytes 0-1) should be AA55
29-
# - Serial number (bytes 52-55) should not be all FF
30-
# The fix scripts use a merge strategy that only overwrites 0xFF bytes
31-
# in the existing EEPROM, so they're safe to run in either failure mode.
32-
EEPROM=/sys/bus/i2c/devices/0-0050/eeprom
33-
NEEDS_FIX=false
34-
if [ -f "$EEPROM" ]; then
35-
MAGIC=$(dd if="$EEPROM" bs=1 count=2 2>/dev/null | xxd -p)
36-
SERIAL=$(dd if="$EEPROM" bs=1 skip=52 count=4 2>/dev/null | xxd -p)
37-
if [ "$MAGIC" != "aa55" ]; then
38-
echo "EEPROM header invalid (expected aa55, got ${MAGIC:-empty})"
39-
NEEDS_FIX=true
40-
elif [ "$SERIAL" = "ffffffff" ]; then
41-
echo "EEPROM header valid but serial number is blank"
42-
NEEDS_FIX=true
43-
else
44-
echo "EEPROM valid (magic=${MAGIC}, serial=${SERIAL})"
45-
fi
46-
47-
if $NEEDS_FIX; then
48-
# Determine the board variant. The device-tree model string is NOT
49-
# usable for this: it reads "BeagleBoard.org PocketBeagle2" on the
50-
# industrial board as well as on the base one.
51-
#
52-
# The board ID at EEPROM offset 46 is "PB2I" on the industrial and
53-
# "PB20" on the base board, and it is still readable in the "valid
54-
# header, blank serial" failure mode. When the header itself is blank
55-
# there is nothing left to read, so probe the hardware instead: the
56-
# industrial always has eMMC populated, the base board ships with the
57-
# footprint empty (and is not a target for this script anyway, since
58-
# everything below flashes ${DEVICE}).
59-
#
60-
# This matters beyond cosmetics. u-boot's SPL only applies the 1GB
61-
# DDRSS timings when the EEPROM identifies the board as the industrial
62-
# variant, so writing a base-board EEPROM onto an industrial leaves it
63-
# running on half its RAM until the EEPROM is rewritten.
64-
BOARDID=""
65-
if [ "$MAGIC" = "aa55" ]; then
66-
BOARDID=$(dd if="$EEPROM" bs=1 skip=46 count=4 2>/dev/null)
67-
fi
68-
case "$BOARDID" in
69-
PB2I)
70-
INDUSTRIAL=true
71-
;;
72-
PB20)
73-
INDUSTRIAL=false
74-
;;
75-
*)
76-
# Blank header - wait briefly for the eMMC to enumerate, then
77-
# decide on its presence.
78-
INDUSTRIAL=false
79-
i=0
80-
while [ $i -lt 10 ]; do
81-
if [ "$(cat /sys/block/mmcblk0/device/type 2>/dev/null)" = "MMC" ]; then
82-
INDUSTRIAL=true
83-
break
84-
fi
85-
i=$((i + 1))
86-
sleep 1
87-
done
88-
echo "EEPROM header blank, detected industrial=${INDUSTRIAL} from eMMC presence"
89-
;;
90-
esac
91-
92-
if $INDUSTRIAL; then
93-
echo "Running fix_pb2i_eeprom.sh"
94-
/bin/bash /opt/fpp/capes/drivers/bb64/fix_pb2i_eeprom.sh
95-
else
96-
echo "Running fix_pb2_eeprom.sh"
97-
/bin/bash /opt/fpp/capes/drivers/bb64/fix_pb2_eeprom.sh
98-
fi
99-
fi
100-
else
101-
echo "WARNING: EEPROM device not found at $EEPROM"
102-
fi
103-
104-
echo "---------------------------------------"
105-
echo "Installing bootloader "
106-
echo ""
107-
108-
#install bootloader
109-
/opt/u-boot/bb-u-boot-pocketbeagle2/install-emmc.sh
110-
111-
11223
# Mark a failure the only two ways an unattended flash can be read: leave the board
11324
# powered on, and blink every user LED together. A finished board goes dark, so
11425
# neither can be mistaken for success. flash_storage.sh does this for its own
115-
# failures; this covers the steps after it. Note the PocketBeagle2 exposes
26+
# failures; this covers the steps either side of it. Note the PocketBeagle2 exposes
11627
# usr1-usr4 where the BeagleBone Black exposes usr0-usr3, hence the glob.
28+
#
29+
# Defined before the first thing that can fail: the EEPROM check below aborts boards
30+
# with no EEPROM at all, and sh resolves a function only once it has been read.
11731
fail() {
11832
echo ""
11933
echo "############################################################"
@@ -129,6 +43,27 @@ fail() {
12943
exit 1
13044
}
13145

46+
echo "---------------------------------------"
47+
48+
# Some PB2i boards ship with a blank or half-programmed EEPROM, which makes u-boot
49+
# configure the industrial board as the 512MB base one. check_pb2_eeprom.sh works
50+
# out which variant this is, repairs what can be repaired, and verifies the result.
51+
#
52+
# It exits non-zero when the EEPROM is missing entirely or is programmed with the
53+
# wrong identity. Neither can be written around, and a board flashed in that state
54+
# boots misidentified -- so stop here and blink, rather than producing something
55+
# that looks finished.
56+
/bin/bash /opt/fpp/capes/drivers/bb64/check_pb2_eeprom.sh \
57+
|| fail "The on-board EEPROM is not usable; see the message above."
58+
59+
echo "---------------------------------------"
60+
echo "Installing bootloader "
61+
echo ""
62+
63+
#install bootloader
64+
/opt/u-boot/bb-u-boot-pocketbeagle2/install-emmc.sh
65+
66+
13267
# flash_storage.sh partitions, copies, configures and then VERIFIES the eMMC.
13368
/opt/fpp/SD/flash_storage.sh -y --clone --no-reboot ${DEVICE} \
13469
|| fail "See the errors above."

SD/flash_storage.sh

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,31 @@ profile_bb64_fixup() {
610610
EOF
611611
}
612612

613-
profile_bb64_preflight() { :; }
613+
# The PocketBeagle2 Industrial is the only board this profile ever flashes: mmcblk0
614+
# is the eMMC, and only the industrial has it populated. Some of those boards leave
615+
# the factory with a blank or half-written identity EEPROM, which makes u-boot bring
616+
# them up as the 512MB base board -- so the EEPROM gets checked, and repaired if it
617+
# can be, before anything is written to the eMMC.
618+
#
619+
# A board with no EEPROM at all cannot be repaired, and check_pb2_eeprom.sh exits
620+
# non-zero for it. Dying here is what the factory flow needs: preflight runs under
621+
# the same EXIT trap as every other phase, so the abort ends with all the user LEDs
622+
# blinking rather than with a board that looks finished.
623+
profile_bb64_preflight() {
624+
local check="${BINDIR}/../capes/drivers/bb64/check_pb2_eeprom.sh"
625+
626+
if [ ! -f "${check}" ]; then
627+
info " ${check} not found; skipping the EEPROM check"
628+
return 0
629+
fi
630+
631+
/bin/bash "${check}" || die \
632+
"the on-board EEPROM is not usable. See the message above.
633+
634+
Flashing would produce a board that misreports its variant, and on the
635+
industrial that means booting with half its RAM."
636+
info " on-board EEPROM OK"
637+
}
614638

615639
profile_bb64_finish() { :; }
616640

0 commit comments

Comments
 (0)