Skip to content

Commit 6b14e84

Browse files
authored
Merge pull request #492 from timg236/raspberrypi5-use-flashrom
rpi-eeprom-update: Add the option to use flashrom for updates on Raspberry Pi 5
2 parents aded082 + db154d4 commit 6b14e84

File tree

3 files changed

+136
-17
lines changed

3 files changed

+136
-17
lines changed

rpi-eeprom-config

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -109,17 +109,22 @@ def exit_error(msg):
109109
sys.stderr.write("ERROR: %s\n" % msg)
110110
sys.exit(1)
111111

112-
def shell_cmd(args):
112+
def shell_cmd(args, timeout=5, echo=False):
113113
"""
114114
Executes a shell command waits for completion returning STDOUT. If an
115115
error occurs then exit and output the subprocess stdout, stderr messages
116116
for debug.
117117
"""
118118
start = time.time()
119119
arg_str = ' '.join(args)
120-
result = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
121-
122-
while time.time() - start < 5:
120+
bufsize = 0 if echo else -1
121+
result = subprocess.Popen(args, bufsize=bufsize, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
122+
123+
while time.time() - start < timeout:
124+
if echo:
125+
s = result.stdout.read(80).decode('utf-8')
126+
if s != "":
127+
sys.stdout.write(s)
123128
if result.poll() is not None:
124129
break
125130

@@ -128,8 +133,8 @@ def shell_cmd(args):
128133

129134
if result.returncode != 0:
130135
exit_error("%s failed: %d\n %s\n %s\n" %
131-
(arg_str, result.returncode, result.stdout.read(), result.stderr.read()))
132-
else:
136+
(arg_str, result.returncode, result.stdout.read().decode('utf-8'), result.stderr.read().decode('utf-8')))
137+
elif not echo:
133138
return result.stdout.read().decode('utf-8')
134139

135140
def get_latest_eeprom():
@@ -170,8 +175,10 @@ def apply_update(config, eeprom=None, config_src=None):
170175
# with EEPROMs with configs delivered outside of APT.
171176
# The checksums are really just a safety check for automatic updates.
172177
args = ['rpi-eeprom-update', '-d', '-i', '-f', tmp_update]
173-
resp = shell_cmd(args)
174-
sys.stdout.write(resp)
178+
179+
# If flashrom is used then the command will not return until the EEPROM
180+
# has been updated so use a larger timeout.
181+
shell_cmd(args, timeout=20, echo=True)
175182

176183
def edit_config(eeprom=None):
177184
"""
@@ -377,6 +384,15 @@ class BootloaderImage(object):
377384
% (src_filename, len(src_bytes), MAX_FILE_SIZE))
378385
self.update(src_bytes, dst_filename)
379386

387+
def set_timestamp(self, timestamp):
388+
"""
389+
Sets the self-update timestamp in an EEPROM image file. This is useful when
390+
using flashrom to write to SPI flash instead of using the bootloader self-update mode.
391+
"""
392+
ts = int(timestamp)
393+
struct.pack_into('<L', self._bytes, len(self._bytes) - 4, ts)
394+
struct.pack_into('<L', self._bytes, len(self._bytes) - 8, ~ts & 0xffffffff)
395+
380396
def write(self):
381397
"""
382398
Writes the updated EEPROM image to stdout or the specified output file.
@@ -498,6 +514,7 @@ See 'rpi-eeprom-update -h' for more information about the available EEPROM image
498514
parser.add_argument('-d', '--digest', help='Signed boot only. The name of the .sig file generated by rpi-eeprom-dgst for config.txt ', required=False)
499515
parser.add_argument('-p', '--pubkey', help='Signed boot only. The name of the RSA public key file to store in the EEPROM', required=False)
500516
parser.add_argument('-x', '--extract', action='store_true', default=False, help='Extract the modifiable files (boot.conf, pubkey, signature)', required=False)
517+
parser.add_argument('-t', '--timestamp', help='Set the timestamp in the EEPROM image file', required=False)
501518
parser.add_argument('eeprom', nargs='?', help='Name of EEPROM file to use as input')
502519
args = parser.parse_args()
503520

@@ -518,6 +535,8 @@ See 'rpi-eeprom-update -h' for more information about the available EEPROM image
518535
apply_update(args.apply, args.eeprom, args.apply)
519536
elif args.eeprom is not None:
520537
image = BootloaderImage(args.eeprom, args.out)
538+
if args.timestamp is not None:
539+
image.set_timestamp(args.timestamp)
521540
if args.config is not None:
522541
if not os.path.exists(args.config):
523542
exit_error("config file '%s' not found" % args.config)
@@ -527,6 +546,8 @@ See 'rpi-eeprom-update -h' for more information about the available EEPROM image
527546
if args.pubkey is not None:
528547
image.update_key(args.pubkey, PUBKEY_BIN)
529548
image.write()
549+
elif args.config is None and args.timestamp is not None:
550+
image.write()
530551
else:
531552
image.read()
532553
elif args.config is None and args.eeprom is None:

rpi-eeprom-update

Lines changed: 104 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ cleanup() {
8989
if [ -f "${NEW_EEPROM_CONFIG}" ]; then
9090
rm -f "${NEW_EEPROM_CONFIG}"
9191
fi
92+
if [ -f "${FLASHROM_LOG}" ]; then
93+
rm -f "${FLASHROM_LOG}"
94+
fi
9295
if [ -d "${TMP_BOOTFS_MNT}" ]; then
9396
umount "${TMP_BOOTFS_MNT}"
9497
rmdir "${TMP_BOOTFS_MNT}"
@@ -97,6 +100,7 @@ cleanup() {
97100
TMP_EEPROM_IMAGE=
98101
TMP_EEPROM_CONFIG=
99102
NEW_EEPROM_CONFIG=
103+
FLASHROM_LOG=
100104
}
101105
trap cleanup EXIT
102106

@@ -169,7 +173,14 @@ prepareImage()
169173
if [ "${OVERWRITE_CONFIG}" = 0 ]; then
170174
"${script_dir}/rpi-eeprom-config" \
171175
--out "${TMP_EEPROM_IMAGE}" \
172-
--config "${NEW_EEPROM_CONFIG}" "${BOOTLOADER_UPDATE_IMAGE}"
176+
--config "${NEW_EEPROM_CONFIG}" \
177+
--timestamp "$(date -u +%s)" \
178+
"${BOOTLOADER_UPDATE_IMAGE}"
179+
else
180+
"${script_dir}/rpi-eeprom-config" \
181+
--out "${TMP_EEPROM_IMAGE}" \
182+
--timestamp "$(date -u +%s)" \
183+
"${BOOTLOADER_UPDATE_IMAGE}"
173184
fi
174185
}
175186

@@ -200,7 +211,7 @@ applyRecoveryUpdate()
200211
# and the current timestamp.
201212
rpi-eeprom-digest -i "${TMP_EEPROM_IMAGE}" -o "${BOOTFS}/pieeprom.sig"
202213

203-
cp -f "${TMP_EEPROM_IMAGE}" "${BOOTFS}/pieeprom.upd" \
214+
cp -fv "${TMP_EEPROM_IMAGE}" "${BOOTFS}/pieeprom.upd" \
204215
|| die "Failed to copy ${TMP_EEPROM_IMAGE} to ${BOOTFS}"
205216

206217
# For NFS mounts ensure that the files are readable to the TFTP user
@@ -225,7 +236,7 @@ applyRecoveryUpdate()
225236
RPI_EEPROM_SELF_UPDATE=0
226237
fi
227238

228-
# Setting bootlaoder_update=0 was really intended for use with network-boot with shared
239+
# Setting bootloader_update=0 was really intended for use with network-boot with shared
229240
# config.txt files. However, if it looks as though self-update has been disabled then
230241
# assume recovery.bin is required.
231242
config_txt="${BOOTFS}/config.txt"
@@ -237,8 +248,33 @@ applyRecoveryUpdate()
237248

238249
[ "${BOOTLOADER_CURRENT_VERSION}" -ge "${RPI_EEPROM_SELF_UPDATE_MIN_VER}" ] || RPI_EEPROM_SELF_UPDATE=0
239250

240-
if [ "${RPI_EEPROM_SELF_UPDATE}" != "1" ]; then
241-
echo "Using recovery.bin for EEPROM update"
251+
# For immediate updates via flash the recovery.bin update is created and then discarded if the
252+
# flashrom update was successful. For SD boot (most common) this provides a rollback in the event
253+
# of power loss.
254+
if [ "${RPI_EEPROM_USE_FLASHROM}" = 1 ]; then
255+
echo
256+
echo "UPDATING bootloader."
257+
echo
258+
echo "*** WARNING: Do not disconnect the power until the update is complete ***"
259+
echo "If a problem occurs then the Raspberry Pi Imager may be used to create"
260+
echo "a bootloader rescue SD card image which restores the default bootloader image."
261+
echo
262+
FLASHROM_LOG="$(mktemp)"
263+
echo "flashrom -p linux_spi:dev=${SPIDEV},spispeed=16000 -w ${BOOTFS}/pieeprom.upd"
264+
if flashrom -p linux_spi:dev=${SPIDEV},spispeed=16000 -w "${BOOTFS}/pieeprom.upd" > "${FLASHROM_LOG}"; then
265+
# Success - remove update files from the boot partition
266+
removePreviousUpdates
267+
echo "UPDATE SUCCESSFUL"
268+
else
269+
# Leave the recovery files in case the EEPROM has been partially updated
270+
cat "${FLASHROM_LOG}"
271+
die "UPDATE FAILED"
272+
fi
273+
return
274+
elif [ "${RPI_EEPROM_SELF_UPDATE}" = "1" ]; then
275+
echo "Using self-update"
276+
else
277+
echo "Copying recovery.bin to ${BOOTFS} for EEPROM update"
242278
cp -f "${RECOVERY_BIN}" "${BOOTFS}/recovery.bin" || die "Failed to copy ${RECOVERY_BIN} to ${BOOTFS}"
243279
fi
244280

@@ -269,6 +305,25 @@ applyUpdate() {
269305
) || die "Unable to validate EEPROM image package checksums"
270306
fi
271307

308+
# Disable flashrom if the SPI device is not found
309+
if [ "${RPI_EEPROM_USE_FLASHROM}" = 1 ]; then
310+
flashrom_probe_ok=0
311+
if ! [ -e "${SPIDEV}" ]; then
312+
echo "WARNING: SPI device ${SPIDEV} not found. Setting RPI_EEPROM_USE_FLASHROM to 0"
313+
fi
314+
315+
if ! flashrom -p linux_spi:dev=${SPIDEV},spispeed=16000 > /dev/null 2>&1; then
316+
echo "WARNING: Flashrom probe of ${SPIDEV} failed"
317+
else
318+
flashrom_probe_ok=1
319+
fi
320+
if [ "${flashrom_probe_ok}" != 1 ]; then
321+
echo "Setting RPI_EEPROM_USE_FLASHROM to 0"
322+
echo
323+
export RPI_EEPROM_USE_FLASHROM=0
324+
fi
325+
fi
326+
272327
applyRecoveryUpdate
273328
}
274329

@@ -334,14 +389,21 @@ checkDependencies() {
334389
BCM_CHIP=2711
335390
EEPROM_SIZE=524288
336391
BOOTLOADER_AUTO_UPDATE_MIN_VERSION="${BOOTLOADER_AUTO_UPDATE_MIN_VERSION:-1599135103}"
392+
393+
SPIDEV=/dev/spidev0.0
337394
elif [ $(((0x$BOARD_INFO >> 12) & 15)) = 4 ]; then
338395
BCM_CHIP=2712
339396
EEPROM_SIZE=2097152
340397
BOOTLOADER_AUTO_UPDATE_MIN_VERSION="${BOOTLOADER_AUTO_UPDATE_MIN_VERSION:-1697650217}"
398+
SPIDEV=/dev/spidev10.0
341399
else
342400
chipNotSupported
343401
fi
344402

403+
# Default to off - in the future Raspberry Pi 5 may default to using flashrom if
404+
# flashrom is available.
405+
[ -z "${RPI_EEPROM_USE_FLASHROM}" ] && RPI_EEPROM_USE_FLASHROM=0
406+
345407
FIRMWARE_IMAGE_DIR="${FIRMWARE_ROOT}-${BCM_CHIP}/${FIRMWARE_RELEASE_STATUS}"
346408
if ! [ -d "${FIRMWARE_IMAGE_DIR}" ]; then
347409
# Use unadorned name for backwards compatiblity
@@ -358,6 +420,18 @@ checkDependencies() {
358420
echo "The recommended method for flashing the EEPROM is rpiboot."
359421
echo "See: https://github.com/raspberrypi/usbboot/blob/master/Readme.md"
360422
echo "Run with -h for more information."
423+
echo
424+
echo "To enable flashrom programming of the EEPROM"
425+
echo "Add these the following entries to /etc/default/rpi-eeprom-update"
426+
echo "RPI_EEPROM_USE_FLASHROM=1"
427+
echo "CM4_ENABLE_RPI_EEPROM_UPDATE=1"
428+
echo
429+
echo "and these entries to config.txt and reboot"
430+
echo "[cm4]"
431+
echo "dtparam=spi=on"
432+
echo "dtoverlay=audremap"
433+
echo "dtoverlay=spi-gpio40-45"
434+
echo
361435
exit ${EXIT_SUCCESS}
362436
fi
363437

@@ -404,6 +478,10 @@ checkDependencies() {
404478
if [ "${BCM_CHIP}" = 2711 ] && [ ! -f "${RECOVERY_BIN}" ]; then
405479
die "${RECOVERY_BIN} not found."
406480
fi
481+
482+
if ! command -v flashrom > /dev/null; then
483+
RPI_EEPROM_USE_FLASHROM=0
484+
fi
407485
}
408486

409487
usage() {
@@ -542,6 +620,22 @@ N.B. If there is a power failure during SELF_UPDATE the EEPROM write may fail an
542620
usbboot must be used to flash the bootloader EEPROM. SELF_UPDATE is not recommended
543621
for updating the bootloader on remote systems.
544622
623+
FLASHROM:
624+
625+
If the RPI_EEPROM_USE_FLASHROM variable is set to 1 then flashrom is used to perform
626+
an immediate update to the SPI flash rather than installing the recovery.bin plus
627+
pieeprom.upd files. The power must not be disconnected during this update otherwise the
628+
EEPROM will need to be re-flashed using the Rasberry Pi Imager bootloader restore feature.
629+
630+
On Raspberry Pi 4, CM4, CM4-S and Pi400 flashrom updates are not enabled by default
631+
because the SPI GPIOs are shared with analog audio. To enable this add the following
632+
entries to config.txt. This moves analog audio to GPIO pins 12,13 and may not be
633+
compatible with some HATS / CM4 IO boards.
634+
635+
dtparam=spi=on
636+
dtoverlay=audremap
637+
dtoverlay=spi-gpio40-45
638+
545639
EOF
546640
exit ${EXIT_SUCCESS}
547641
}
@@ -595,7 +689,9 @@ findBootFS()
595689
elif [ -z "$BOOTFS" ]; then
596690
if ! BOOTFS=$(/usr/lib/raspberrypi-sys-mods/get_fw_loc 2> /dev/null); then
597691
for BOOTFS in /boot/firmware /boot; do
598-
if findmnt --fstab "$BOOTFS" > /dev/null; then
692+
if [ -f "${BOOTFS}/config.txt" ]; then
693+
break
694+
elif findmnt --fstab "$BOOTFS" > /dev/null; then
599695
break
600696
fi
601697
done
@@ -714,7 +810,7 @@ checkAndApply()
714810
fi
715811

716812
if [ "${ACTION_UPDATE_BOOTLOADER}" = 1 ] || [ "${ACTION_UPDATE_VL805}" = 1 ]; then
717-
echo "*** INSTALLING EEPROM UPDATES ***"
813+
echo "*** PREPARING EEPROM UPDATES ***"
718814
echo ""
719815

720816
printVersions
@@ -727,7 +823,7 @@ checkAndApply()
727823
fileUpdate()
728824
{
729825
removePreviousUpdates
730-
echo "*** INSTALLING ${BOOTLOADER_UPDATE_IMAGE} ${VL805_UPDATE_IMAGE} ***"
826+
echo "*** CREATED UPDATE ${BOOTLOADER_UPDATE_IMAGE} ${VL805_UPDATE_IMAGE} ***"
731827
echo
732828

733829
if [ -n "${BOOTLOADER_UPDATE_IMAGE}" ]; then

rpi-eeprom-update-default

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
FIRMWARE_ROOT=/lib/firmware/raspberrypi/bootloader
33
FIRMWARE_RELEASE_STATUS="default"
44
FIRMWARE_BACKUP_DIR="/var/lib/raspberrypi/bootloader/backup"
5-
USE_FLASHROM=0
65
EEPROM_CONFIG_HOOK=
76

87
# BOOTFS can be set here to override auto-detection in rpi-eeprom-update
98
#BOOTFS=/boot
9+
10+
# Use flashrom if available to update the bootloader without rebooting - Raspberry Pi 5
11+
#RPI_EEPROM_USE_FLASHROM=1

0 commit comments

Comments
 (0)