mirror of
https://github.com/raspberrypi/rpi-eeprom.git
synced 2026-01-20 21:13:36 +08:00
rpi-eeprom-update: Add the option to use flashrom for updates on Raspberry Pi 5
On Raspberry Pi 5 there are dedicated pins for the bootloader SPI EEPROM. This makes it possible to do immediate updates via flashrom. The "current" EEPROM config is the EEPROM config at boot rather than what has just been written to the SPI flash because this is consistent with current behaviour. To use flashrom instead of recovery.bin for bootloader updates set RPI_EEPROM_USE_FLASHROM=1 in /etc/defaults/rpi-eeprom-update BCM2711 On CM4, Pi4, CM4-S, Pi400 config.txt must be modified to disable the analog audio driver which shares the GPIO pins used by the bootloader EEPROM. dtparam=spi=on dtoverlay=audremap dtoverlay=spi-gpio40-45
This commit is contained in:
@@ -109,7 +109,7 @@ def exit_error(msg):
|
||||
sys.stderr.write("ERROR: %s\n" % msg)
|
||||
sys.exit(1)
|
||||
|
||||
def shell_cmd(args):
|
||||
def shell_cmd(args, timeout=5, echo=False):
|
||||
"""
|
||||
Executes a shell command waits for completion returning STDOUT. If an
|
||||
error occurs then exit and output the subprocess stdout, stderr messages
|
||||
@@ -117,9 +117,14 @@ def shell_cmd(args):
|
||||
"""
|
||||
start = time.time()
|
||||
arg_str = ' '.join(args)
|
||||
result = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
bufsize = 0 if echo else -1
|
||||
result = subprocess.Popen(args, bufsize=bufsize, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
while time.time() - start < 5:
|
||||
while time.time() - start < timeout:
|
||||
if echo:
|
||||
s = result.stdout.read(80).decode('utf-8')
|
||||
if s != "":
|
||||
sys.stdout.write(s)
|
||||
if result.poll() is not None:
|
||||
break
|
||||
|
||||
@@ -128,8 +133,8 @@ def shell_cmd(args):
|
||||
|
||||
if result.returncode != 0:
|
||||
exit_error("%s failed: %d\n %s\n %s\n" %
|
||||
(arg_str, result.returncode, result.stdout.read(), result.stderr.read()))
|
||||
else:
|
||||
(arg_str, result.returncode, result.stdout.read().decode('utf-8'), result.stderr.read().decode('utf-8')))
|
||||
elif not echo:
|
||||
return result.stdout.read().decode('utf-8')
|
||||
|
||||
def get_latest_eeprom():
|
||||
@@ -170,8 +175,10 @@ def apply_update(config, eeprom=None, config_src=None):
|
||||
# with EEPROMs with configs delivered outside of APT.
|
||||
# The checksums are really just a safety check for automatic updates.
|
||||
args = ['rpi-eeprom-update', '-d', '-i', '-f', tmp_update]
|
||||
resp = shell_cmd(args)
|
||||
sys.stdout.write(resp)
|
||||
|
||||
# If flashrom is used then the command will not return until the EEPROM
|
||||
# has been updated so use a larger timeout.
|
||||
shell_cmd(args, timeout=20, echo=True)
|
||||
|
||||
def edit_config(eeprom=None):
|
||||
"""
|
||||
@@ -377,6 +384,15 @@ class BootloaderImage(object):
|
||||
% (src_filename, len(src_bytes), MAX_FILE_SIZE))
|
||||
self.update(src_bytes, dst_filename)
|
||||
|
||||
def set_timestamp(self, timestamp):
|
||||
"""
|
||||
Sets the self-update timestamp in an EEPROM image file. This is useful when
|
||||
using flashrom to write to SPI flash instead of using the bootloader self-update mode.
|
||||
"""
|
||||
ts = int(timestamp)
|
||||
struct.pack_into('<L', self._bytes, len(self._bytes) - 4, ts)
|
||||
struct.pack_into('<L', self._bytes, len(self._bytes) - 8, ~ts & 0xffffffff)
|
||||
|
||||
def write(self):
|
||||
"""
|
||||
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
|
||||
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)
|
||||
parser.add_argument('-p', '--pubkey', help='Signed boot only. The name of the RSA public key file to store in the EEPROM', required=False)
|
||||
parser.add_argument('-x', '--extract', action='store_true', default=False, help='Extract the modifiable files (boot.conf, pubkey, signature)', required=False)
|
||||
parser.add_argument('-t', '--timestamp', help='Set the timestamp in the EEPROM image file', required=False)
|
||||
parser.add_argument('eeprom', nargs='?', help='Name of EEPROM file to use as input')
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -518,6 +535,8 @@ See 'rpi-eeprom-update -h' for more information about the available EEPROM image
|
||||
apply_update(args.apply, args.eeprom, args.apply)
|
||||
elif args.eeprom is not None:
|
||||
image = BootloaderImage(args.eeprom, args.out)
|
||||
if args.timestamp is not None:
|
||||
image.set_timestamp(args.timestamp)
|
||||
if args.config is not None:
|
||||
if not os.path.exists(args.config):
|
||||
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
|
||||
if args.pubkey is not None:
|
||||
image.update_key(args.pubkey, PUBKEY_BIN)
|
||||
image.write()
|
||||
elif args.config is None and args.timestamp is not None:
|
||||
image.write()
|
||||
else:
|
||||
image.read()
|
||||
elif args.config is None and args.eeprom is None:
|
||||
|
||||
@@ -89,6 +89,9 @@ cleanup() {
|
||||
if [ -f "${NEW_EEPROM_CONFIG}" ]; then
|
||||
rm -f "${NEW_EEPROM_CONFIG}"
|
||||
fi
|
||||
if [ -f "${FLASHROM_LOG}" ]; then
|
||||
rm -f "${FLASHROM_LOG}"
|
||||
fi
|
||||
if [ -d "${TMP_BOOTFS_MNT}" ]; then
|
||||
umount "${TMP_BOOTFS_MNT}"
|
||||
rmdir "${TMP_BOOTFS_MNT}"
|
||||
@@ -97,6 +100,7 @@ cleanup() {
|
||||
TMP_EEPROM_IMAGE=
|
||||
TMP_EEPROM_CONFIG=
|
||||
NEW_EEPROM_CONFIG=
|
||||
FLASHROM_LOG=
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
@@ -169,7 +173,14 @@ prepareImage()
|
||||
if [ "${OVERWRITE_CONFIG}" = 0 ]; then
|
||||
"${script_dir}/rpi-eeprom-config" \
|
||||
--out "${TMP_EEPROM_IMAGE}" \
|
||||
--config "${NEW_EEPROM_CONFIG}" "${BOOTLOADER_UPDATE_IMAGE}"
|
||||
--config "${NEW_EEPROM_CONFIG}" \
|
||||
--timestamp "$(date -u +%s)" \
|
||||
"${BOOTLOADER_UPDATE_IMAGE}"
|
||||
else
|
||||
"${script_dir}/rpi-eeprom-config" \
|
||||
--out "${TMP_EEPROM_IMAGE}" \
|
||||
--timestamp "$(date -u +%s)" \
|
||||
"${BOOTLOADER_UPDATE_IMAGE}"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -200,7 +211,7 @@ applyRecoveryUpdate()
|
||||
# and the current timestamp.
|
||||
rpi-eeprom-digest -i "${TMP_EEPROM_IMAGE}" -o "${BOOTFS}/pieeprom.sig"
|
||||
|
||||
cp -f "${TMP_EEPROM_IMAGE}" "${BOOTFS}/pieeprom.upd" \
|
||||
cp -fv "${TMP_EEPROM_IMAGE}" "${BOOTFS}/pieeprom.upd" \
|
||||
|| die "Failed to copy ${TMP_EEPROM_IMAGE} to ${BOOTFS}"
|
||||
|
||||
# For NFS mounts ensure that the files are readable to the TFTP user
|
||||
@@ -225,7 +236,7 @@ applyRecoveryUpdate()
|
||||
RPI_EEPROM_SELF_UPDATE=0
|
||||
fi
|
||||
|
||||
# Setting bootlaoder_update=0 was really intended for use with network-boot with shared
|
||||
# Setting bootloader_update=0 was really intended for use with network-boot with shared
|
||||
# config.txt files. However, if it looks as though self-update has been disabled then
|
||||
# assume recovery.bin is required.
|
||||
config_txt="${BOOTFS}/config.txt"
|
||||
@@ -237,8 +248,33 @@ applyRecoveryUpdate()
|
||||
|
||||
[ "${BOOTLOADER_CURRENT_VERSION}" -ge "${RPI_EEPROM_SELF_UPDATE_MIN_VER}" ] || RPI_EEPROM_SELF_UPDATE=0
|
||||
|
||||
if [ "${RPI_EEPROM_SELF_UPDATE}" != "1" ]; then
|
||||
echo "Using recovery.bin for EEPROM update"
|
||||
# For immediate updates via flash the recovery.bin update is created and then discarded if the
|
||||
# flashrom update was successful. For SD boot (most common) this provides a rollback in the event
|
||||
# of power loss.
|
||||
if [ "${RPI_EEPROM_USE_FLASHROM}" = 1 ]; then
|
||||
echo
|
||||
echo "UPDATING bootloader."
|
||||
echo
|
||||
echo "*** WARNING: Do not disconnect the power until the update is complete ***"
|
||||
echo "If a problem occurs then the Raspberry Pi Imager may be used to create"
|
||||
echo "a bootloader rescue SD card image which restores the default bootloader image."
|
||||
echo
|
||||
FLASHROM_LOG="$(mktemp)"
|
||||
echo "flashrom -p linux_spi:dev=${SPIDEV},spispeed=16000 -w ${BOOTFS}/pieeprom.upd"
|
||||
if flashrom -p linux_spi:dev=${SPIDEV},spispeed=16000 -w "${BOOTFS}/pieeprom.upd" > "${FLASHROM_LOG}"; then
|
||||
# Success - remove update files from the boot partition
|
||||
removePreviousUpdates
|
||||
echo "UPDATE SUCCESSFUL"
|
||||
else
|
||||
# Leave the recovery files in case the EEPROM has been partially updated
|
||||
cat "${FLASHROM_LOG}"
|
||||
die "UPDATE FAILED"
|
||||
fi
|
||||
return
|
||||
elif [ "${RPI_EEPROM_SELF_UPDATE}" = "1" ]; then
|
||||
echo "Using self-update"
|
||||
else
|
||||
echo "Copying recovery.bin to ${BOOTFS} for EEPROM update"
|
||||
cp -f "${RECOVERY_BIN}" "${BOOTFS}/recovery.bin" || die "Failed to copy ${RECOVERY_BIN} to ${BOOTFS}"
|
||||
fi
|
||||
|
||||
@@ -269,6 +305,25 @@ applyUpdate() {
|
||||
) || die "Unable to validate EEPROM image package checksums"
|
||||
fi
|
||||
|
||||
# Disable flashrom if the SPI device is not found
|
||||
if [ "${RPI_EEPROM_USE_FLASHROM}" = 1 ]; then
|
||||
flashrom_probe_ok=0
|
||||
if ! [ -e "${SPIDEV}" ]; then
|
||||
echo "WARNING: SPI device ${SPIDEV} not found. Setting RPI_EEPROM_USE_FLASHROM to 0"
|
||||
fi
|
||||
|
||||
if ! flashrom -p linux_spi:dev=${SPIDEV},spispeed=16000 > /dev/null 2>&1; then
|
||||
echo "WARNING: Flashrom probe of ${SPIDEV} failed"
|
||||
else
|
||||
flashrom_probe_ok=1
|
||||
fi
|
||||
if [ "${flashrom_probe_ok}" != 1 ]; then
|
||||
echo "Setting RPI_EEPROM_USE_FLASHROM to 0"
|
||||
echo
|
||||
export RPI_EEPROM_USE_FLASHROM=0
|
||||
fi
|
||||
fi
|
||||
|
||||
applyRecoveryUpdate
|
||||
}
|
||||
|
||||
@@ -334,14 +389,21 @@ checkDependencies() {
|
||||
BCM_CHIP=2711
|
||||
EEPROM_SIZE=524288
|
||||
BOOTLOADER_AUTO_UPDATE_MIN_VERSION="${BOOTLOADER_AUTO_UPDATE_MIN_VERSION:-1599135103}"
|
||||
|
||||
SPIDEV=/dev/spidev0.0
|
||||
elif [ $(((0x$BOARD_INFO >> 12) & 15)) = 4 ]; then
|
||||
BCM_CHIP=2712
|
||||
EEPROM_SIZE=2097152
|
||||
BOOTLOADER_AUTO_UPDATE_MIN_VERSION="${BOOTLOADER_AUTO_UPDATE_MIN_VERSION:-1697650217}"
|
||||
SPIDEV=/dev/spidev10.0
|
||||
else
|
||||
chipNotSupported
|
||||
fi
|
||||
|
||||
# Default to off - in the future Raspberry Pi 5 may default to using flashrom if
|
||||
# flashrom is available.
|
||||
[ -z "${RPI_EEPROM_USE_FLASHROM}" ] && RPI_EEPROM_USE_FLASHROM=0
|
||||
|
||||
FIRMWARE_IMAGE_DIR="${FIRMWARE_ROOT}-${BCM_CHIP}/${FIRMWARE_RELEASE_STATUS}"
|
||||
if ! [ -d "${FIRMWARE_IMAGE_DIR}" ]; then
|
||||
# Use unadorned name for backwards compatiblity
|
||||
@@ -358,6 +420,18 @@ checkDependencies() {
|
||||
echo "The recommended method for flashing the EEPROM is rpiboot."
|
||||
echo "See: https://github.com/raspberrypi/usbboot/blob/master/Readme.md"
|
||||
echo "Run with -h for more information."
|
||||
echo
|
||||
echo "To enable flashrom programming of the EEPROM"
|
||||
echo "Add these the following entries to /etc/default/rpi-eeprom-update"
|
||||
echo "RPI_EEPROM_USE_FLASHROM=1"
|
||||
echo "CM4_ENABLE_RPI_EEPROM_UPDATE=1"
|
||||
echo
|
||||
echo "and these entries to config.txt and reboot"
|
||||
echo "[cm4]"
|
||||
echo "dtparam=spi=on"
|
||||
echo "dtoverlay=audremap"
|
||||
echo "dtoverlay=spi-gpio40-45"
|
||||
echo
|
||||
exit ${EXIT_SUCCESS}
|
||||
fi
|
||||
|
||||
@@ -404,6 +478,10 @@ checkDependencies() {
|
||||
if [ "${BCM_CHIP}" = 2711 ] && [ ! -f "${RECOVERY_BIN}" ]; then
|
||||
die "${RECOVERY_BIN} not found."
|
||||
fi
|
||||
|
||||
if ! command -v flashrom > /dev/null; then
|
||||
RPI_EEPROM_USE_FLASHROM=0
|
||||
fi
|
||||
}
|
||||
|
||||
usage() {
|
||||
@@ -542,6 +620,22 @@ N.B. If there is a power failure during SELF_UPDATE the EEPROM write may fail an
|
||||
usbboot must be used to flash the bootloader EEPROM. SELF_UPDATE is not recommended
|
||||
for updating the bootloader on remote systems.
|
||||
|
||||
FLASHROM:
|
||||
|
||||
If the RPI_EEPROM_USE_FLASHROM variable is set to 1 then flashrom is used to perform
|
||||
an immediate update to the SPI flash rather than installing the recovery.bin plus
|
||||
pieeprom.upd files. The power must not be disconnected during this update otherwise the
|
||||
EEPROM will need to be re-flashed using the Rasberry Pi Imager bootloader restore feature.
|
||||
|
||||
On Raspberry Pi 4, CM4, CM4-S and Pi400 flashrom updates are not enabled by default
|
||||
because the SPI GPIOs are shared with analog audio. To enable this add the following
|
||||
entries to config.txt. This moves analog audio to GPIO pins 12,13 and may not be
|
||||
compatible with some HATS / CM4 IO boards.
|
||||
|
||||
dtparam=spi=on
|
||||
dtoverlay=audremap
|
||||
dtoverlay=spi-gpio40-45
|
||||
|
||||
EOF
|
||||
exit ${EXIT_SUCCESS}
|
||||
}
|
||||
@@ -595,7 +689,9 @@ findBootFS()
|
||||
elif [ -z "$BOOTFS" ]; then
|
||||
if ! BOOTFS=$(/usr/lib/raspberrypi-sys-mods/get_fw_loc 2> /dev/null); then
|
||||
for BOOTFS in /boot/firmware /boot; do
|
||||
if findmnt --fstab "$BOOTFS" > /dev/null; then
|
||||
if [ -f "${BOOTFS}/config.txt" ]; then
|
||||
break
|
||||
elif findmnt --fstab "$BOOTFS" > /dev/null; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
@@ -714,7 +810,7 @@ checkAndApply()
|
||||
fi
|
||||
|
||||
if [ "${ACTION_UPDATE_BOOTLOADER}" = 1 ] || [ "${ACTION_UPDATE_VL805}" = 1 ]; then
|
||||
echo "*** INSTALLING EEPROM UPDATES ***"
|
||||
echo "*** PREPARING EEPROM UPDATES ***"
|
||||
echo ""
|
||||
|
||||
printVersions
|
||||
@@ -727,7 +823,7 @@ checkAndApply()
|
||||
fileUpdate()
|
||||
{
|
||||
removePreviousUpdates
|
||||
echo "*** INSTALLING ${BOOTLOADER_UPDATE_IMAGE} ${VL805_UPDATE_IMAGE} ***"
|
||||
echo "*** CREATED UPDATE ${BOOTLOADER_UPDATE_IMAGE} ${VL805_UPDATE_IMAGE} ***"
|
||||
echo
|
||||
|
||||
if [ -n "${BOOTLOADER_UPDATE_IMAGE}" ]; then
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
FIRMWARE_ROOT=/lib/firmware/raspberrypi/bootloader
|
||||
FIRMWARE_RELEASE_STATUS="default"
|
||||
FIRMWARE_BACKUP_DIR="/var/lib/raspberrypi/bootloader/backup"
|
||||
USE_FLASHROM=0
|
||||
EEPROM_CONFIG_HOOK=
|
||||
|
||||
# BOOTFS can be set here to override auto-detection in rpi-eeprom-update
|
||||
#BOOTFS=/boot
|
||||
|
||||
# Use flashrom if available to update the bootloader without rebooting - Raspberry Pi 5
|
||||
#RPI_EEPROM_USE_FLASHROM=1
|
||||
|
||||
Reference in New Issue
Block a user