rpi-update: Add support for updating the bootloader EEPROM (#24)

* Download the latest EEPROM release to the local
  /lib/firmware/raspberrypi/bootloader directory
* Update the rpi-eeprom tools
* Remove the old eeprom_version check
* Check bootloader commits as well as firwmware commits
* SKIP_BOOTLOADER default to 1 for now until fully tested
* Update recovery.bin if the bootloader revision has changed.
This commit is contained in:
timg236
2023-12-11 18:48:40 +00:00
committed by GitHub
parent 02d49298bd
commit 62ef428cf9

View File

@@ -15,6 +15,10 @@ REPO_URI=${REPO_URI:-"https://github.com/raspberrypi/rpi-firmware"}
REPO_API_URI=${REPO_URI/github.com/api.github.com\/repos} REPO_API_URI=${REPO_URI/github.com/api.github.com\/repos}
REPO_CONTENT_URI=${REPO_URI/github.com/raw.githubusercontent.com} REPO_CONTENT_URI=${REPO_URI/github.com/raw.githubusercontent.com}
BOOTLOADER_REPO_URI=${BOOTLOADER_REPO_URI:-"https://github.com/raspberrypi/rpi-eeprom"}
BOOTLOADER_REPO_API_URI=${BOOTLOADER_REPO_URI/github.com/api.github.com\/repos}
BOOTLOADER_REPO_CONTENT_URI=${BOOTLOADER_REPO_URI/github.com/raw.githubusercontent.com}
UPDATE_SELF=${UPDATE_SELF:-1} UPDATE_SELF=${UPDATE_SELF:-1}
UPDATE_REPO_URI="https://github.com/raspberrypi/rpi-update" UPDATE_REPO_URI="https://github.com/raspberrypi/rpi-update"
UPDATE_REPO_CONTENT_URI=${UPDATE_REPO_URI/github.com/raw.githubusercontent.com} UPDATE_REPO_CONTENT_URI=${UPDATE_REPO_URI/github.com/raw.githubusercontent.com}
@@ -55,6 +59,7 @@ else
fi fi
WORK_PATH=${WORK_PATH:-"${ROOT_PATH}/root"} WORK_PATH=${WORK_PATH:-"${ROOT_PATH}/root"}
SKIP_KERNEL=${SKIP_KERNEL:-} SKIP_KERNEL=${SKIP_KERNEL:-}
SKIP_BOOTLOADER=${SKIP_BOOTLOADER:-1}
SKIP_FIRMWARE=${SKIP_FIRMWARE:-0} SKIP_FIRMWARE=${SKIP_FIRMWARE:-0}
SKIP_SDK=${SKIP_SDK:-0} SKIP_SDK=${SKIP_SDK:-0}
SKIP_VCLIBS=${SKIP_VCLIBS:-0} SKIP_VCLIBS=${SKIP_VCLIBS:-0}
@@ -79,6 +84,8 @@ FW_MODPATH="${ROOT_PATH}/lib/modules"
FW_REV_IN=${1:-""} FW_REV_IN=${1:-""}
FW_REVFILE="${FW_PATH}/.firmware_revision" FW_REVFILE="${FW_PATH}/.firmware_revision"
SELFUPDATE_SCRIPT="${WORK_PATH}/.updateScript.sh" SELFUPDATE_SCRIPT="${WORK_PATH}/.updateScript.sh"
BOOTLOADER_REV_IN=${2:-""}
BOOTLOADER_REVFILE="${FW_PATH}/.bootloader_revision"
[ "${RPI_UPDATE_UNSUPPORTED}" -ne 0 ] && echo -e "You appear to be trying to update firmware on an incompatible distribution. To force update, run the following:\nsudo -E RPI_UPDATE_UNSUPPORTED=0 rpi-update" && exit 1 [ "${RPI_UPDATE_UNSUPPORTED}" -ne 0 ] && echo -e "You appear to be trying to update firmware on an incompatible distribution. To force update, run the following:\nsudo -E RPI_UPDATE_UNSUPPORTED=0 rpi-update" && exit 1
@@ -138,7 +145,7 @@ function update_self() {
cat > "${SELFUPDATE_SCRIPT}" << EOF cat > "${SELFUPDATE_SCRIPT}" << EOF
if mv "${_tempFileName}" "$0"; then if mv "${_tempFileName}" "$0"; then
rm -- "\$0" rm -- "\$0"
exec env UPDATE_SELF=0 /bin/bash "$0" "${FW_REV_IN}" exec env UPDATE_SELF=0 /bin/bash "$0" "${FW_REV_IN}" "${BOOTLOADER_REV_IN}"
else else
echo " !!! Failed!" echo " !!! Failed!"
fi fi
@@ -233,70 +240,91 @@ function update_sdk {
fi fi
} }
# Check if the bootloader is older than the latest critical release. An old function download_bootloader_tools {
# bootloader shouldn't block rpi-update so just inform the user that bootloader for tool in rpi-eeprom-update rpi-eeprom-config rpi-eeprom-digest; do
# is out of date. temp="$(mktemp)"
function check_eeprom_version { if ! eval curl -fs ${CURL_OPTIONS} "${BOOTLOADER_REPO_CONTENT_URI}/${BOOTLOADER_REV}/${tool}" --output "${temp}"; then
local CURRENT_VERSION="" echo " !!! Failed to download ${tool}"
local FIRST_VERSION=1557513636 rm -f "${temp}"
# MIN VERSION for Sep 10 2019 EEPROM exit 1
local MIN_VERSION=1568112110
local HAVE_BOOTLOADER_EEPROM=0
# Skip EEPROM check if vcgencmd is missing because it won't be possible to
# check the version.
if ! command -v vcgencmd > /dev/null; then
return
fi
rev=
if [ -f /proc/cpuinfo ]; then
rev="$(sed -n '/^Revision/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo)"
fi
if [ -n $rev ] && [ $(((0x$rev >> 23) & 1)) -ne 0 ] && [ $(((0x$rev >> 12) & 15)) -eq 3 ]; then
HAVE_BOOTLOADER_EEPROM=1
fi
if [ "${HAVE_BOOTLOADER_EEPROM}" != 1 ]; then
return
fi
# vcgencmd doesn't return non-zero for unknown commands.
if vcgencmd bootloader_config | grep -qi "Command not registered"; then
# Firmware is too old to return the bootloader config
return
fi
# If FREEZE_VERSION is specified then assume that the user doesn't want
# an EEPROM update so skip the check.
if vcgencmd bootloader_config | grep -q FREEZE_VERSION=1; then
return
fi
if vcgencmd bootloader_version | grep -q timestamp; then
CURRENT_VERSION=$(vcgencmd bootloader_version | grep timestamp | awk '{print $2}')
if [ "${CURRENT_VERSION}" = "0" ]; then
# If a timestamp of zero is returned then it's new firmware but an
# old bootloader. Assume bootloader v0
CURRENT_VERSION="${FIRST_VERSION}"
fi fi
elif vcgencmd bootloader_version | grep -qi "unknown"; then mv "${temp}" "/usr/bin/${tool}"
return chmod a+rx "/usr/bin/${tool}"
else done
# New bootloader / old firmware ? Try to parse the date }
CURRENT_VERSION=$(date -u +%s --date "$(vcgencmd bootloader_version | head -n1)")
fi
# Failed to parse the version. Default to the initial production release. function download_bootloader_images {
if [ -z "${CURRENT_VERSION}" ]; then bcm_chip=$1
CURRENT_VERSION="${FIRST_VERSION}" latest_eeprom=$(eval curl -fs ${CURL_OPTIONS} ${BOOTLOADER_REPO_API_URI}/git/trees/${BOOTLOADER_REV}?recursive=1 | grep "firmware-${bcm_chip}/latest/pieeprom" | sed 's/.*\(pieeprom.*\)".*/\1/' | sort -r | head -n1)
fi if [[ -n "${latest_eeprom}" ]]; then
fw_path="/lib/firmware/raspberrypi/bootloader-${bcm_chip}/latest"
if [[ ! -d "${fw_path}" ]]; then
echo "Path ${fw_path} not found. Please install the rpi-eeprom package first"
exit 1
fi
if [[ "${BOOTLOADER_REV_IN}" != "master" ]]; then
echo "Non default bootloader revision specified (${BOOTLOADER_REV_IN}) deleting local bootloader binaries in ${fw_path}"
rm -rf "${fw_path}"
mkdir -p "${fw_path}"
fi
if [[ ! -f "${fw_path}/${latest_eeprom}" ]]; then
eeprom_temp="$(mktemp)"
if ! eval curl -fs ${CURL_OPTIONS} "${BOOTLOADER_REPO_CONTENT_URI}/${BOOTLOADER_REV}/firmware-${bcm_chip}/latest/${latest_eeprom}" --output "${eeprom_temp}"; then
echo " !!! Failed to download firmware-${bcm_chip}/${latest_eeprom}"
rm -rf "${eeprom_temp}"
exit 1
fi
mv "${eeprom_temp}" "${fw_path}/${latest_eeprom}"
chmod a+r "${fw_path}/${latest_eeprom}"
fi
if [ "${CURRENT_VERSION}" -lt "${MIN_VERSION}" ]; then # Always download the latest recovery.bin if bootloader_rev has changed.
echo "A newer bootloader EEPROM version is available." recovery_temp="$(mktemp)"
echo "On Debian, try: sudo apt update; sudo apt install rpi-eeprom" if ! eval curl -fs ${CURL_OPTIONS} "${BOOTLOADER_REPO_CONTENT_URI}/${BOOTLOADER_REV}/firmware-${bcm_chip}/latest/recovery.bin" --output "${recovery_temp}"; then
echo "then reboot to install the new bootloader" echo " !!! Failed to download ${bcm_chip}/recovery.bin"
rm -rf "${recovery_temp}"
exit 1
fi
mv "${recovery_temp}" "${fw_path}/recovery.bin"
chmod a+r "${fw_path}/recovery.bin"
fi
}
function download_bootloader {
if [[ ${SKIP_BOOTLOADER} -eq 0 ]]; then
echo "Downloading bootloader tools"
download_bootloader_tools
echo "Downloading bootloader images"
if grep -q "bcm2711" /proc/device-tree/compatible; then
download_bootloader_images 2711
fi
if grep -q "bcm2712" /proc/device-tree/compatible; then
download_bootloader_images 2712
fi
fi
}
function update_bootloader {
if [[ ${SKIP_BOOTLOADER} -eq 0 ]]; then
sed /etc/default/rpi-eeprom-update -i -e "s/^FIRMWARE_RELEASE_STATUS.*/FIRMWARE_RELEASE_STATUS=\"latest\"/"
if [[ "${BOOTLOADER_REV_IN}" != "master" ]]; then
# Use latest according to rpi-eeprom git hash
echo "Using latest bootloader from rpi-eeprom ${BOOTLOADER_REV_IN}"
config_temp="$(mktemp)"
if ! rpi-eeprom-config > "${config_temp}"; then
echo "Failed to read current bootloader config"
exit 1
fi
if ! rpi-eeprom-config -a "${config_temp}"; then
echo "Failed to update bootloader - rpi-eeprom-rev ${BOOTLOADER_REV_IN}"
exit 1
fi
rm -f "${config_temp}"
else
# Update if newer
rpi-eeprom-update -a
fi
fi fi
} }
@@ -316,8 +344,8 @@ function show_notice {
if ${NOTICE_HASH_EXISTS}; then if ${NOTICE_HASH_EXISTS}; then
local NOTICE=$(echo "${FULL_NOTICE}" | tail -n+2) local NOTICE=$(echo "${FULL_NOTICE}" | tail -n+2)
local NEW_HASH=${FW_REV} local NEW_HASH=${FW_REV}
local LOCAL_lt_NOTICE=$(compare_hashes "${LOCAL_HASH}" lt "${NOTICE_HASH}") local LOCAL_lt_NOTICE=$(compare_hashes "${REPO_API_URI}" "${LOCAL_HASH}" lt "${NOTICE_HASH}")
local NEW_ge_NOTICE=$(compare_hashes "${NEW_HASH}" ge "${NOTICE_HASH}") local NEW_ge_NOTICE=$(compare_hashes "${REPO_API_URI}" "${NEW_HASH}" ge "${NOTICE_HASH}")
if ! ${LOCAL_lt_NOTICE} && ! ${NEW_ge_NOTICE}; then if ! ${LOCAL_lt_NOTICE} && ! ${NEW_ge_NOTICE}; then
return return
fi fi
@@ -455,6 +483,7 @@ function finalise {
fi fi
echo " *** Storing current firmware revision" echo " *** Storing current firmware revision"
echo "${FW_REV}" > "${FW_REVFILE}" echo "${FW_REV}" > "${FW_REVFILE}"
echo "${BOOTLOADER_REV}" > "${BOOTLOADER_REVFILE}"
} }
function do_backup { function do_backup {
@@ -509,13 +538,14 @@ function do_update {
check_partition 256 check_partition 256
fi fi
check_initramfs check_initramfs
check_eeprom_version
show_notice show_notice
download_bootloader
download_rev download_rev
if [[ -f "${FW_REPOLOCAL}/pre-install" ]]; then if [[ -f "${FW_REPOLOCAL}/pre-install" ]]; then
echo " *** Running pre-install script" echo " *** Running pre-install script"
source "${FW_REPOLOCAL}/pre-install" source "${FW_REPOLOCAL}/pre-install"
fi fi
update_bootloader
update_firmware update_firmware
update_modules update_modules
update_vc_libs update_vc_libs
@@ -624,14 +654,14 @@ function noobs_fix {
} }
function get_hash_date { function get_hash_date {
local COMMITS_URI=${REPO_API_URI}/commits/$1 local COMMITS_URI=${1}/commits/$2
eval curl ${CURL_OPTIONS_API} -s ${CURL_OPTIONS} "${COMMITS_URI}" | grep "date" | head -1 | awk -F\" '{print $4}' eval curl ${CURL_OPTIONS_API} -s ${CURL_OPTIONS} "${COMMITS_URI}" | grep "date" | head -1 | awk -F\" '{print $4}'
} }
function compare_hashes { function compare_hashes {
local DATE1=$(get_hash_date "$1") local DATE1=$(get_hash_date "${1}" "$2")
local DATE2=$(get_hash_date "$3") local DATE2=$(get_hash_date "${1}" "$4")
if [ $(date -d "${DATE1}" +%s) -$2 $(date -d "${DATE2}" +%s) ]; then if [ $(date -d "${DATE1}" +%s) -$3 $(date -d "${DATE2}" +%s) ]; then
echo true echo true
else else
echo false echo false
@@ -640,10 +670,16 @@ function compare_hashes {
function get_long_hash { function get_long_hash {
# ask github for long version hash # ask github for long version hash
local COMMITS_URI=${REPO_API_URI}/commits/$1 local COMMITS_URI=${1}/commits/$2
eval curl ${CURL_OPTIONS_API} -s ${CURL_OPTIONS} "${COMMITS_URI}" | awk 'BEGIN {hash=""} { if (hash == "" && $1 == "\"sha\":") {hash=substr($2, 2, 40);} } END {print hash}' eval curl ${CURL_OPTIONS_API} -s ${CURL_OPTIONS} "${COMMITS_URI}" | awk 'BEGIN {hash=""} { if (hash == "" && $1 == "\"sha\":") {hash=substr($2, 2, 40);} } END {print hash}'
} }
function print_diffs {
local diff_uri="$1"
SEPARATOR="======================================================"
eval curl ${CURL_OPTIONS_API} -s ${CURL_OPTIONS} "${diff_uri}" | awk -v SEPARATOR="${SEPARATOR}" -F\" ' { if ($2 == "commits") {commits=1} if (commits && $2 == "message") {print SEPARATOR "\nCommit: " $4} }' | sed 's/\\n\\n/\nCommit:\ /g'
}
if [[ ${EUID} -ne 0 ]]; then if [[ ${EUID} -ne 0 ]]; then
echo " !!! This tool must be run as root" echo " !!! This tool must be run as root"
@@ -688,15 +724,28 @@ if [[ "${FW_REV_IN}" == "" ]]; then
FW_REV_IN=${BRANCH} FW_REV_IN=${BRANCH}
fi fi
if [[ "${BOOTLOADER_REV_IN}" == "" ]]; then
BOOTLOADER_REV_IN=${BRANCH}
fi
ARTIFACT="" ARTIFACT=""
BUILD="" BUILD=""
FW_REV="" FW_REV=""
if [[ ${FW_REV_IN} != http* ]] && [[ ! -f ${FW_REV_IN} ]]; then if [[ ${FW_REV_IN} != http* ]] && [[ ! -f ${FW_REV_IN} ]]; then
FW_REV=$(get_long_hash "${FW_REV_IN}") FW_REV=$(get_long_hash "${REPO_API_URI}" "${FW_REV_IN}")
fi fi
echo FW_REV:$FW_REV echo FW_REV:$FW_REV
BOOTLOADER_REV=""
BOOTLOADER_LOCAL_HASH=""
if [[ ${SKIP_BOOTLOADER} -eq 0 ]]; then
if [[ ${BOOTLOADER_REV_IN} != http* ]] && [[ ! -f ${BOOTLOADER_REV_IN} ]]; then
BOOTLOADER_REV=$(get_long_hash "${BOOTLOADER_REPO_API_URI}" "${BOOTLOADER_REV_IN}")
fi
echo BOOTLOADER_REV:$BOOTLOADER_REV
fi
if [[ "${FW_REV}" == "" ]]; then if [[ "${FW_REV}" == "" ]]; then
if [[ ${FW_REV_IN} != http* ]] && [[ ! -f ${FW_REV_IN} ]]; then if [[ ${FW_REV_IN} != http* ]] && [[ ! -f ${FW_REV_IN} ]]; then
IFS=':' read ARTIFACT BUILD <<<${FW_REV_IN} IFS=':' read ARTIFACT BUILD <<<${FW_REV_IN}
@@ -735,23 +784,59 @@ if [[ ! -f "${FW_REVFILE}" ]]; then
else else
if [[ "${ARTIFACT}" != "" ]]; then if [[ "${ARTIFACT}" != "" ]]; then
LOCAL_HASH=$(cat "${FW_REVFILE}") LOCAL_HASH=$(cat "${FW_REVFILE}")
if [[ "${SKIP_BOOTLOADER}" -eq 0 ]]; then
BOOTLOADER_LOCAL_HASH=$(cat "${BOOTLOADER_REVFILE}")
fi
else else
LOCAL_HASH=$(get_long_hash "$(cat "${FW_REVFILE}")") LOCAL_HASH=$(get_long_hash ${REPO_API_URI} "$(cat "${FW_REVFILE}")")
if [[ "${SKIP_BOOTLOADER}" -eq 0 ]]; then
if [[ ! -f "${BOOTLOADER_REVFILE}" ]]; then
BOOTLOADER_LOCAL_HASH=0
else
BOOTLOADER_LOCAL_HASH=$(get_long_hash "${BOOTLOADER_REPO_API_URI}" "$(cat "${BOOTLOADER_REVFILE}")")
fi
fi
fi fi
if [[ "${LOCAL_HASH}" == "${FW_REV}" ]]; then
echo " *** Your firmware is already up to date (delete ${FW_REVFILE} to force an update anyway)" if [[ "${LOCAL_HASH}" == "${FW_REV}" && "${BOOTLOADER_LOCAL_HASH}" == "${BOOTLOADER_REV}" ]] ; then
echo " *** Your firmware is already up to date (delete ${FW_REVFILE} and ${BOOTLOADER_REVFILE} to force an update anyway)"
exit 0 exit 0
else
if [[ "${LOCAL_HASH}" == "${FW_REV}" ]]; then
# Skip other components if only the bootloader has changed
SKIP_FIRMWARE=1
SKIP_KERNEL=1
SKIP_VCLIBS=1
SKIP_SDK=1
SKIP_DOWNLOAD=1
ARTIFACT=""
elif [[ "${BOOTLOADER_LOCAL_HASH}" == "${BOOTLOADER_REV}" ]] ; then
SKIP_BOOTLOADER=1
fi
fi fi
if [[ ${JUST_CHECK} -ne 0 ]]; then if [[ ${JUST_CHECK} -ne 0 ]]; then
if $(compare_hashes "${LOCAL_HASH}" lt "${FW_REV}"); then if $(compare_hashes "${REPO_API_URI}" "${LOCAL_HASH}" lt "${FW_REV}"); then
echo " *** Firmware update required. New commits available:" echo " *** Firmware update required. New commits available:"
DIFF_URI=${REPO_API_URI}/compare/${LOCAL_HASH}...${FW_REV} DIFF_URI=${REPO_API_URI}/compare/${LOCAL_HASH}...${FW_REV}
else print_diffs "${DIFF_URI}"
elif $(compare_hashes "${REPO_API_URI}" "${LOCAL_HASH}" gt "${FW_REV}"); then
echo " *** Firmware downgrade requested. Commits to drop:" echo " *** Firmware downgrade requested. Commits to drop:"
DIFF_URI=${REPO_API_URI}/compare/${FW_REV}...${LOCAL_HASH} DIFF_URI=${REPO_API_URI}/compare/${FW_REV}...${LOCAL_HASH}
print_diffs "${DIFF_URI}"
fi
if [[ ${SKIP_BOOTLOADER} -eq 0 ]]; then
if $(compare_hashes "${BOOTLOADER_REPO_API_URI}" "${BOOTLOADER_LOCAL_HASH}" lt "${BOOTLOADER_REV}"); then
echo " *** Bootloader update required. New commits available:"
DIFF_URI=${BOOTLOADER_REPO_API_URI}/compare/${BOOTLOADER_LOCAL_HASH}...${BOOTLOADER_REV}
print_diffs "${DIFF_URI}"
elif $(compare_hashes "${BOOTLOADER_REPO_API_URI}" "${BOOTLOADER_LOCAL_HASH}" gt "${BOOTLOADER_REV}"); then
echo " *** Bootloader downgrade requested. Commits to drop:"
DIFF_URI=${BOOTLOADER_REPO_API_URI}/compare/${BOOTLOADER_REV}...${BOOTLOADER_LOCAL_HASH}
print_diffs "${DIFF_URI}"
fi
fi fi
SEPARATOR="======================================================"
eval curl ${CURL_OPTIONS_API} -s ${CURL_OPTIONS} "${DIFF_URI}" | awk -v SEPARATOR="${SEPARATOR}" -F\" ' { if ($2 == "commits") {commits=1} if (commits && $2 == "message") {print SEPARATOR "\nCommit: " $4} }' | sed 's/\\n\\n/\nCommit:\ /g'
exit 2 exit 2
fi fi
fi fi