Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# Flashrom-based EEPROM update for Raspberry Pi 4
# Usage: pieeprom-flashrom.sh [firmware_image]
#
# This script handles the low-level SPI flashrom update.
# It is called by pieeprom-update.sh when self-update is not available,
# or can be invoked directly.

set -o errexit

# shellcheck disable=SC1091
. /usr/libexec/os-helpers-logging

NEW_IMG="${1:-pieeprom.upd}"
CURR_IMG=pieeprom-current.bin.tmp
CURR_IMG_PATH=/tmp
NEW_IMG_PATH=/mnt/boot
SPI_SPEED=16000

# Unbind a device from a driver
# Usage: device=$(driver_unbind <driver_path> <device_pattern>)
# Returns: device name on stdout if successful, empty string if not found
# Exits: using fail function if multiple devices found or unbind operation fails
driver_unbind() {
driver_path="${1}"
device_pattern="${2}"

if [ ! -d "${driver_path}" ]; then
echo ""
return
fi

devices="$(find "${driver_path}" -maxdepth 1 -type l -name "${device_pattern}" 2>/dev/null)"
device_count="$(echo "${devices}" | grep -c . || true)"
if [ "${device_count}" -gt 1 ]; then
fail "Multiple devices matching ${device_pattern} found in ${driver_path}, expected exactly one"
fi
device="$(basename "$(echo "${devices}" | head -n1)" 2>/dev/null)"
if [ -z "${device}" ]; then
echo ""
return
fi
if ! echo "${device}" > "${driver_path}/unbind" 2>/dev/null; then
fail "Failed to unbind ${device} from ${driver_path}"
fi
echo "${device}"
}

# Bind a device to a driver
# Usage: driver_bind <driver_path> <device>
# Returns: 0 on success, 1 if device is empty or driver path not found, 2 if bind fails
driver_bind() {
driver_path="${1}"
device="${2}"

if [ -z "${device}" ]; then
warn "Device is empty, cannot bind to ${driver_path}"
return 1
fi

if [ ! -d "${driver_path}" ]; then
warn "Driver path ${driver_path} not found, skipping bind"
return 1
fi

if ! echo "${device}" > "${driver_path}/bind" 2>/dev/null; then
warn "Failed to bind ${device} to ${driver_path}"
return 2
fi
}

# SPI driver helpers (required - script fails if SPI device not found)
SPI_DRIVER_PATH="/sys/bus/platform/drivers/spi-bcm2835"
spi_unbind() {
driver_unbind "${SPI_DRIVER_PATH}" "*.spi"
}
spi_bind() {
driver_bind "${SPI_DRIVER_PATH}" "${1}"
}

# VCHIQ driver helpers (optional - unbind releases GPIO pins used by audio
# to avoid pinctrl conflicts with SPI on GPIO 40-45)
# WARNING: VCHIQ cannot be rebound at runtime due to firmware limitations,
# audio/camera will be unavailable until reboot if unbound.
VCHIQ_DRIVER_PATH="/sys/bus/platform/drivers/bcm2835_vchiq"
vchiq_unbind() {
driver_unbind "${VCHIQ_DRIVER_PATH}" "*.mailbox"
}

SPI_DEVICE=$(spi_unbind)
if [ -z "${SPI_DEVICE}" ]; then
fail "SPI device not found or failed to unbind, cannot proceed with EEPROM update"
fi

/usr/bin/vcmailbox 0x00030056 4 4 0 > /dev/null || true
/usr/bin/dtparam -d /mnt/boot/overlays/ audio=off
/usr/bin/dtoverlay -d /mnt/boot/overlays/ spi-gpio40-45

# Try binding SPI; if it fails (pinctrl conflict with VCHIQ holding GPIO 40-45),
# unbind VCHIQ and retry. VCHIQ cannot be rebound at runtime, so audio/camera
# will be unavailable until reboot.
VCHIQ_UNBOUND=0
if ! spi_bind "$SPI_DEVICE"; then
warn "Unbinding VCHIQ to release GPIO 40-45 (audio/camera will be unavailable until reboot)"
vchiq_unbind
VCHIQ_UNBOUND=1
if ! spi_bind "$SPI_DEVICE"; then
fail "SPI bind failed even after VCHIQ unbind, cannot proceed"
fi
fi

/usr/sbin/flashrom -p "linux_spi:dev=/dev/spidev0.0,spispeed=${SPI_SPEED}" --read "${CURR_IMG_PATH}/${CURR_IMG}"

curr_eeprom_md5sum=$(md5sum "${CURR_IMG_PATH}/${CURR_IMG}" | awk '{print $1}')
new_eeprom_md5sum=$(md5sum "${NEW_IMG_PATH}/${NEW_IMG}" | awk '{print $1}')

if [ "$curr_eeprom_md5sum" = "$new_eeprom_md5sum" ]; then
info "SPI EEPROM fw update is not necessary"
else
info "Performing SPI EEPROM fw update..."
/usr/sbin/flashrom -p "linux_spi:dev=/dev/spidev0.0,spispeed=${SPI_SPEED}" --write "${NEW_IMG_PATH}/${NEW_IMG}"
info "SPI EEPROM fw update successful"
fi

rm "${CURR_IMG_PATH}/${CURR_IMG}"

SPI_DEVICE=$(spi_unbind)
if [ -z "${SPI_DEVICE}" ]; then
fail "SPI device not found during cleanup, cannot restore system state"
fi

/usr/bin/dtparam -d /mnt/boot/overlays/ -R spi-gpio40-45
/usr/bin/dtparam -d /mnt/boot/overlays/ audio=on
/usr/bin/vcmailbox 0x00030056 4 4 1 > /dev/null || true

if ! spi_bind "$SPI_DEVICE"; then
fail "Failed to restore SPI driver during cleanup"
fi

# In an ideal world we would restore the mailbox, but unfortunately this is impossible
# https://github.com/raspberrypi/rpi-eeprom/issues/801#issuecomment-3859373151
if [ "${VCHIQ_UNBOUND}" = "1" ]; then
warn "VCHIQ was unbound during EEPROM update and cannot be restored at runtime."
warn "Audio, camera and other VideoCore services will not work until the next reboot."
fi
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that with this change resin-init-flasher-board no longer flashroms the EEPROM during provisioning. Need to check how that affects the secure boot provisioning flow.

Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Update procedure is based on the rpi-eeprom-update script available at
# https://github.com/raspberrypi/rpi-eeprom/blob/master/rpi-eeprom-update
# EEPROM update orchestrator for Raspberry Pi 4
# Decides between self-update (bootloader handles it on reboot) and
# flashrom (direct SPI write via pieeprom-flashrom.sh).
#
# Based on https://github.com/raspberrypi/rpi-eeprom/blob/master/rpi-eeprom-update

set -o errexit

Expand All @@ -13,31 +16,11 @@ if type is_secured >/dev/null 2>&1 && is_secured; then
exit 0
fi

NEW_IMG="${1:-pieeprom-latest-stable.bin}"
CURR_IMG=pieeprom-current.bin.tmp
CURR_IMG_PATH=/tmp
NEW_IMG_PATH=/mnt/boot
SPI_SPEED=16000

spi_bind() {
command="${1}"
case $command in
"unbind")
SPI_BCM2835_SYSPATH="/sys/bus/platform/drivers/spi-bcm2835"
SPI_DEVICE="$(basename "$(find "${SPI_BCM2835_SYSPATH}" -type l -name "*.spi" | head -n1)")"
if [ -n "${SPI_DEVICE}" ]; then
echo "${SPI_DEVICE}" > "${SPI_BCM2835_SYSPATH}/${command}" || true
fi
;;
"bind")
if [ -n "${SPI_DEVICE}" ]; then
echo "${SPI_DEVICE}" > "${SPI_BCM2835_SYSPATH}/${command}" || true
SPI_DEVICE=""
fi
;;
*) fatal "$command not supported";;
esac
}
NEW_IMG="${1:-pieeprom.upd}"
DT_BOOTLOADER_TS="/proc/device-tree/chosen/bootloader/build-timestamp"
# Minimum bootloader version that supports self-update (2022-04-26)
# See https://github.com/raspberrypi/rpi-eeprom/blob/master/rpi-eeprom-update
SELF_UPDATE_MIN_VER=1650968668

# According to documentation, custom EEPROM update scripts
# must also check for FREEZE_VERSION flag
Expand All @@ -46,32 +29,34 @@ if [ "$(/usr/bin/vcgencmd bootloader_config | grep "FREEZE_VERSION=1" || true)"
exit 0
fi

spi_bind unbind

/usr/bin/vcmailbox 0x00030056 4 4 0 > /dev/null || true
/usr/bin/dtparam -d /mnt/boot/overlays/ audio=off
/usr/bin/dtoverlay -d /mnt/boot/overlays/ spi-gpio40-45

spi_bind bind

/usr/sbin/flashrom -p "linux_spi:dev=/dev/spidev0.0,spispeed=${SPI_SPEED}" --read ${CURR_IMG_PATH}/${CURR_IMG}

curr_eeprom_md5sum=$(md5sum "${CURR_IMG_PATH}/${CURR_IMG}" | awk '{print $1}')
new_eeprom_md5sum=$(md5sum "${NEW_IMG_PATH}/${NEW_IMG}" | awk '{print $1}')
# bootloader_update=0 in config.txt disables all EEPROM updates (self-update and flashrom)
if grep -q "^bootloader_update=0" /mnt/boot/config.txt 2>/dev/null; then
warn "EEPROM updates disabled in config.txt (bootloader_update=0). Will NOT update!"
exit 0
fi

if [ "$curr_eeprom_md5sum" = "$new_eeprom_md5sum" ]; then
info "SPI EEPROM fw update is not necessary"
# Get current bootloader version (unix timestamp)
if [ -f "${DT_BOOTLOADER_TS}" ]; then
CURRENT_VERSION=$(printf "%d" "0x$(od "${DT_BOOTLOADER_TS}" -v -An -t x1 | tr -d ' ')")
else
info "Performing SPI EEPROM fw update..."
/usr/sbin/flashrom -p "linux_spi:dev=/dev/spidev0.0,spispeed=${SPI_SPEED}" --write "${NEW_IMG_PATH}/${NEW_IMG}"
CURRENT_VERSION=$(/usr/bin/vcgencmd bootloader_version | grep timestamp | awk '{print $2}')
fi
if ! [ "${CURRENT_VERSION:-0}" -gt 0 ] 2>/dev/null; then
warn "Could not determine bootloader version, falling back to flashrom"
CURRENT_VERSION=0
fi
info "Current bootloader version: ${CURRENT_VERSION}"

# Check if self-update is supported by the bootloader
if [ "${CURRENT_VERSION}" -ge "${SELF_UPDATE_MIN_VER}" ]; then
# Check if self-update has been explicitly disabled in the EEPROM config
if /usr/bin/vcgencmd bootloader_config | grep -q "ENABLE_SELF_UPDATE=0"; then
warn "Self-update is supported but disabled in EEPROM config (ENABLE_SELF_UPDATE=0)"
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should never happen as we are ourselves shipping our eeprom bootloader. However if for some reason this were to happen, then we would flashrom the firmware any way.

else
info "Bootloader supports self-update. EEPROM will be updated on next reboot if needed."
exit 0
fi
fi

rm ${CURR_IMG_PATH}/$CURR_IMG

spi_bind unbind

/usr/bin/dtparam -d /mnt/boot/overlays/ -R spi-gpio40-45
/usr/bin/dtparam -d /mnt/boot/overlays/ audio=on
/usr/bin/vcmailbox 0x00030056 4 4 1 > /dev/null || true

spi_bind bind
info "Falling back to flashrom"
/usr/libexec/pieeprom-flashrom.sh "${NEW_IMG}"
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ LIC_FILES_CHKSUM = "file://LICENSE;md5=a6c5149578a16272119f3f9c13d6549b"
SRC_URI = " \
git://github.com/raspberrypi/rpi-eeprom.git;protocol=https;branch=master \
file://pieeprom-update.sh \
file://pieeprom-flashrom.sh \
"

# EEPROM configuration default values for this version, as specified at
Expand All @@ -23,6 +24,8 @@ PV = "20250821+git${SRCPV}"
LATEST_STABLE_PIEEPROM_FW:raspberrypi4-64 = "2025-08-20"
VL805_FW_REV = "000138c0"
FIRMWARE:raspberrypi4-64 = "firmware-2711"
TGT_EEPROM_BIN = "pieeprom.bin"
TGT_EEPROM_SIG = "pieeprom.sig"

S = "${WORKDIR}/git"

Expand All @@ -37,8 +40,8 @@ RDEPENDS:${PN} = "dtc flashrom userlandtools"
# the configuration that exists in the binary
do_compile() {
src_eeprom_bin="pieeprom-${LATEST_STABLE_PIEEPROM_FW}.bin"
tgt_eeprom_bin="pieeprom-latest-stable.bin"
cp ${S}/rpi-eeprom-config "${WORKDIR}"
cp ${S}/rpi-eeprom-digest "${WORKDIR}"
cp "${S}/${FIRMWARE}/stable/${src_eeprom_bin}" "${WORKDIR}/"
boot_conf="${WORKDIR}/default-config.txt"

Expand All @@ -53,13 +56,16 @@ do_compile() {

if [ "x${SIGN_API}" = "x" ]; then
${PYTHON} "${WORKDIR}/rpi-eeprom-config" --config "${boot_conf}" \
--out "${WORKDIR}/${tgt_eeprom_bin}" "${WORKDIR}/${src_eeprom_bin}"
--out "${WORKDIR}/${TGT_EEPROM_BIN}" "${WORKDIR}/${src_eeprom_bin}"

"${WORKDIR}/rpi-eeprom-digest" -i "${WORKDIR}/${TGT_EEPROM_BIN}" -o "${WORKDIR}/${TGT_EEPROM_SIG}"
fi
}

do_install() {
install -d ${D}${libexecdir}
install -d ${D}${libexecdir}
install -m 0775 ${WORKDIR}/pieeprom-update.sh ${D}${libexecdir}/pieeprom-update.sh
install -m 0775 ${WORKDIR}/pieeprom-flashrom.sh ${D}${libexecdir}/pieeprom-flashrom.sh
}
do_install[depends] += "\
rpi-bootfiles:do_deploy \
Expand All @@ -71,10 +77,11 @@ do_deploy () {
fi
mkdir ${DEPLOY_DIR_IMAGE}/rpi-eeprom/

cp ${WORKDIR}/pieeprom-latest-stable* ${DEPLOY_DIR_IMAGE}/rpi-eeprom/
if [ -f "${S}/${FIRMWARE}/critical/vl805-${VL805_FW_REV}.bin" ]; then
cp ${S}/${FIRMWARE}/critical/vl805-${VL805_FW_REV}.bin ${DEPLOY_DIR_IMAGE}/${PN}/vl805-latest-stable.bin
fi
cp "${WORKDIR}/${TGT_EEPROM_BIN}" ${DEPLOY_DIR_IMAGE}/rpi-eeprom/
cp "${WORKDIR}/${TGT_EEPROM_SIG}" ${DEPLOY_DIR_IMAGE}/rpi-eeprom/
}

# vl805 utility is deprecated, see https://github.com/raspberrypi/rpi-eeprom/commit/fed1ca62a5752cb5a990608c8c897ce0b077600a
Expand All @@ -91,5 +98,5 @@ do_deploy[depends] += " \
rpi-bootfiles:do_deploy \
"

FILES:${PN} = "${libexecdir}/pieeprom-update.sh"
FILES:${PN} = "${libexecdir}/pieeprom-update.sh ${libexecdir}/pieeprom-flashrom.sh"
COMPATIBLE_MACHINE = "raspberrypi4-64"
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ IMAGE_INSTALL:append:raspberrypi4-superhub = " \
"

BALENA_BOOT_PARTITION_FILES:append:raspberrypi4-64 = " \
rpi-eeprom/pieeprom-latest-stable.bin:/pieeprom-latest-stable.bin \
rpi-eeprom/pieeprom.bin:/pieeprom.bin \
rpi-eeprom/pieeprom.sig:/pieeprom.sig \
rpi-eeprom/vl805-latest-stable.bin:/vl805-latest-stable.bin \
"
Loading