Merge pull request #1 from timg236/initial_revision

Initial revision
This commit is contained in:
timg
2019-09-03 09:59:49 +01:00
committed by GitHub
8 changed files with 564 additions and 0 deletions

67
LICENSE Normal file
View File

@@ -0,0 +1,67 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: rpi-eeprom
Source: https://github.com/raspberrypi/rpi-eeprom
Files: *
Copyright: 2019, Raspberry Pi (Trading) Ltd.
License: BSD-3
Files: firmware/*
Copyright: 2019, Raspberry Pi (Trading) Ltd.
License: custom
License: BSD-3
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
.
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the
distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT
HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
License: custom
All rights reserved.
.
Redistribution. Redistribution and use in binary form, without
modification, are permitted provided that the following conditions are
met:
.
* This software may only be used for the purposes of developing for,
running or using a Raspberry Pi device.
* Redistributions must reproduce the above copyright notice and the
following disclaimer in the documentation and/or other materials
provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
.
DISCLAIMER. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
recovery.bin Normal file

Binary file not shown.

94
rpi-eeprom-config Executable file
View File

@@ -0,0 +1,94 @@
#!/usr/bin/python
# rpi-eeprom-config
# Utility for reading and writing the configuration file in the
# Raspberry Pi4 bootloader EEPROM image.
import argparse
import struct
import sys
IMAGE_SIZE = 512 * 1024
# Each section starts with a magic number followed by a 32 bit offset to the
# next section (big-endian).
# The number, order and size of the sections depends on the bootloader version
# but the following mask can be used to test for section headers and skip
# unknown data.
MAGIC_MASK = 0x55aaf00f
FILE_MAGIC = 0x55aaf11f # id for modifiable file, currently only bootconf.txt
FILE_HDR_LEN = 20
FILENAME_LEN = 12
class BootloaderImage(object):
def __init__(self, filename, output):
self._filename = filename
self._bytes = bytearray(open(filename, 'rb').read())
self._out = None
if output is not None:
self._out = open(output, 'wb')
if len(self._bytes) != IMAGE_SIZE:
raise Exception("%s: Expected size %d bytes actual size %d bytes" %
(filename, IMAGE_SIZE, len(self._bytes)))
def find_config(self):
offset = 0
magic = 0
while offset < IMAGE_SIZE:
magic, length = struct.unpack_from('>LL', self._bytes, offset)
if (magic & MAGIC_MASK) != MAGIC_MASK:
raise Exception('EEPROM is corrupted')
if magic == FILE_MAGIC: # Found a file
name = self._bytes[offset + 8: offset + FILE_HDR_LEN]
if name.decode('utf-8') == 'bootconf.txt':
return (offset, length)
offset += 4 + length
offset += 8 - (offset % 8) # Pad
raise Exception('Bootloader config not found')
def write(self, new_config):
hdr_offset, length = self.find_config()
new_config_bytes = open(new_config, 'rb').read()
new_len = len(new_config_bytes) + FILENAME_LEN + 4
if new_len > length and new_len > 1024:
raise Exception('Config is too large')
if hdr_offset + len(new_config_bytes) + FILE_HDR_LEN > IMAGE_SIZE:
raise Exception('EEPROM image size exceeded')
struct.pack_into('>L', self._bytes, hdr_offset + 4, new_len)
struct.pack_into(("%ds" % len(new_config_bytes)), self._bytes, hdr_offset + 4 + FILE_HDR_LEN, new_config_bytes)
if self._out is not None:
self._out.write(self._bytes)
self._out.close()
else:
sys.stdout.write(self._bytes)
def read(self):
hdr_offset, length = self.find_config()
offset = hdr_offset + 4 + FILE_HDR_LEN
config_bytes = self._bytes[offset:offset+length-FILENAME_LEN-4]
if self._out is not None:
self._out.write(config_bytes)
self._out.close()
else:
sys.stdout.write(config_bytes)
def main():
parser = argparse.ArgumentParser('RPI EEPROM config tool')
parser.add_argument('--config', help='Filename of new bootloader config')
parser.add_argument('--out', help='Filename for the EEPROM image with updated config')
parser.add_argument('eeprom', help='EEPROM filename (pieeprom.bin)')
args = parser.parse_args()
image = BootloaderImage(args.eeprom, args.out)
if args.config is not None:
image.write(args.config)
else:
image.read()
if __name__ == '__main__':
main()

396
rpi-eeprom-update Executable file
View File

@@ -0,0 +1,396 @@
#!/bin/sh
# Raspberry Pi4 boot EEPROM updater.
set -e
script_dir=$(cd "$(dirname "$0")" && pwd)
if [ -f /etc/default/rpi-eeprom-update ]; then
. /etc/default/rpi-eeprom-update
fi
FIRMWARE_ROOT=${FIRMWARE_ROOT:-/lib/firmware/raspberrypi/bootloader}
# May be used to select beta releases instead of the default critical
# updates.
FIRMWARE_RELEASE_STATUS=${FIRMWARE_RELEASE_STATUS:-critical}
FIRMWARE_IMAGE_DIR=${FIRMWARE_IMAGE_DIR:-${FIRMWARE_ROOT}/${FIRMWARE_RELEASE_STATUS}}
FIRMWARE_BACKUP_DIR=${FIRMWARE_BACKUP_DIR:-/var/lib/raspberrypi/bootloader/backup}
USE_FLASHROM=${USE_FLASHROM:-0}
RECOVERY_BIN=${RECOVERY_BIN:-${FIRMWARE_ROOT}/recovery.bin}
BOOTFS=${BOOTFS:-/boot}
EXIT_SUCCESS=0
EXIT_UPDATE_REQUIRED=1
EXIT_FAILED=2
EXIT_EEPROM_FROZEN=3
# Reserved
# EXIT_PREVIOUS_UPDATE_FAILED=4
OVERWRITE_CONFIG=0
# Maximum safe SPI speed for EEPROM access 16000, slower is ok.
SPI_SPEED=16000
# Timestamp for first release which doesn't have a timestamp field
FIRST_VERSION=1557513636
EEPROM_SIZE=524288
# Simple bootloader which is able to load start.elf in the event of a power
# cut. This runs SDRAM at low speed and may have reduced functionality but
# should be enough to run flashrom again.
TMP_EEPROM_IMAGE=""
TMP_BOOTFS_MNT=""
cleanup() {
if [ -f "${TMP_EEPROM_IMAGE}" ]; then
rm -f "${TMP_EEPROM_IMAGE}"
fi
if [ -f "${TMP_EEPROM_CONFIG}" ]; then
rm -f "${TMP_EEPROM_CONFIG}"
fi
if [ -d "${TMP_BOOTFS_MNT}" ]; then
umount "${TMP_BOOTFS_MNT}"
rmdir "${TMP_BOOTFS_MNT}"
fi
TMP_BOOTFS_MNT=
TMP_EEPROM_IMAGE=
TMP_EEPROM_CONFIG=
}
trap cleanup EXIT
die() {
echo "$@" >&2
exit ${EXIT_FAILED}
}
prepareImage()
{
eeprom_image="$1"
[ -f "${eeprom_image}" ] || die "EEPROM image \'${eeprom_image}\' not found"
TMP_EEPROM_IMAGE="$(mktemp)"
TMP_EEPROM_CONFIG="$(mktemp)"
mkdir -p "${FIRMWARE_BACKUP_DIR}"
# Backup the configuration of the currently loaded bootloader
vcgencmd bootloader_config > "${TMP_EEPROM_CONFIG}"
backup="${FIRMWARE_BACKUP_DIR}/pieeprom-backup-$(date +%Y%m%d-%H%M%S).conf"
cp -f "${TMP_EEPROM_CONFIG}" "${backup}"
if [ "$(wc -l "${TMP_EEPROM_CONFIG}" | awk '{print $1}')" -lt 3 ]; then
# Don't propagate empty EEPROM config files and also prevent the initial
# bootloader config with WAKE_ON_GPIO=0 propgating to newer versions by
# accident.
OVERWRITE_CONFIG=1
fi
cp -f "${eeprom_image}" "${TMP_EEPROM_IMAGE}"
if [ "${OVERWRITE_CONFIG}" = 0 ]; then
"${script_dir}/rpi-eeprom-config" \
--out "${TMP_EEPROM_IMAGE}" \
--config "${TMP_EEPROM_CONFIG}" "${eeprom_image}"
fi
}
applyRecoveryUpdate()
{
eeprom_image="$1"
[ -f "${eeprom_image}" ] || die "${eeprom_image} not found"
TMP_EEPROM_IMAGE="$(mktemp)"
findBootFS
prepareImage "${eeprom_image}"
# If recovery.bin encounters pieeprom.upd then it will select it in
# preference to pieeprom.bin. The .upd file also causes recovery.bin
# to rename itself to recovery.000 and reboot if the update is successful.
# The rename causes the ROM to ignore this file and use the newly flashed
# EEPROM image instead.
cp -f "${TMP_EEPROM_IMAGE}" "${BOOTFS}/pieeprom.upd" \
|| die "Failed to copy ${TMP_EEPROM_IMAGE} to ${BOOTFS}"
cp -f "${RECOVERY_BIN}" "${BOOTFS}/recovery.bin" \
|| die "Failed to copy ${RECOVERY_BIN} to ${BOOTFS}"
}
applyUpdate() {
eeprom_image="$1"
[ "$(id -u)" = "0" ] || die "* Must be run as root - try 'sudo rpi-eeprom-update'"
if [ "${USE_FLASHROM}" = 0 ]; then
applyRecoveryUpdate "${eeprom_image}"
return
fi
# Bootloader EEPROM chip-select is muxed with audio pin so disable audio
# LDO first to avoid sending noise to analog audio.
/opt/vc/bin/vcmailbox 0x00030056 4 4 0 > /dev/null || true
dtparam audio=off
# Switch the SPI pins to boot EEPROM
dtoverlay spi-gpio40-45
modprobe spidev
modprobe spi-bcm2835
prepareImage "${eeprom_image}"
echo "Applying update ${eeprom_image}"
flashrom -p "linux_spi:dev=/dev/spidev0.0,spispeed=${SPI_SPEED}" -w "${TMP_EEPROM_IMAGE}" || die "flashrom EEPROM update failed"
dtparam -R spi-gpio40-45
dtparam audio=on
/opt/vc/bin/vcmailbox 0x00030056 4 4 1 > /dev/null || true
}
# Use the version reported by the loaded EEPROM instead of attempting to retrieve
# this via flashrom to avoid unnecessary audio glitches.
CURRENT_VERSION=
getCurrentVersion() {
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
else
# 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.
if [ -z "${CURRENT_VERSION}" ]; then
CURRENT_VERSION="${FIRST_VERSION}"
fi
}
# Set to the latest critical firmware version
CRITICAL_UPDATE_IMAGE=""
CRITICAL_UPDATE_VERSION=0
getLatestCriticalUpdate() {
CRITICAL_UPDATE_VERSION=0
match=".*/pieeprom-[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9].bin"
latest="$(find "${FIRMWARE_IMAGE_DIR}" -maxdepth 1 -type f -size "${EEPROM_SIZE}c" -regex "${match}" | sort -r | head -n1)"
if [ -f "${latest}" ]; then
CRITICAL_UPDATE_VERSION=$(strings "${latest}" | grep BUILD_TIMESTAMP | sed 's/.*=//g')
CRITICAL_UPDATE_IMAGE="${latest}"
fi
}
checkDependencies() {
if [ ! -d "${FIRMWARE_IMAGE_DIR}" ]; then
die "Bootloader critical updates directory ${FIRMWARE_IMAGE_DIR} not found."
fi
if vcgencmd bootloader_config | grep -qi "Command not registered"; then
die "vcgencmd: bootloader_config. not supported. Please update VC firmware"
fi
if ! flashrom --version > /dev/null 2>&1; then
[ "${USE_FLASHROM}" = 0 ] || die "flashrom not found."
fi
if [ "${USE_FLASHROM}" = 0 ]; then
[ -f "${RECOVERY_BIN}" ] || die "${RECOVERY_BIN} not found"
fi
}
usage() {
cat <<EOF
rpi-eeprom-update [options]... [FILE]
Checks whether there Raspberry Pi bootloader EEPROM is up to date and
optionally updates the EEPROM.
A backup of the current EEPROM config file is written to ${FIRMWARE_BACKUP_DIR}
before applying the update.
-a Install the latest critical update if necessary.
-d Use the default bootloader config instead of migrating the current settings.
-f Install the given file instead of the latest critical update.
Ignores the FREEZE_VERSION flag in bootloader and is intended for manual
firmware updates.
WARNING: This command should only be run from console mode in order to
avoid conflicts/deadlock with dtoverlay/dtparam settings.
-h Display help text and exit
-j Write status information using JSON notation
-m Write status information to the given file when run without -a or -f
To extract the configuration file from an EEPROM image.
rpi-eeprom-config pieeprom.bin --out bootconf.txt
To update the configuration file in an EEPROM image.
rpi-eeprom-config pieeprom.bin --config bootconf.txt --out pieeprom-new.bin
To flash the new image
sudo rpi-eeprom-update -d -f ./pieeprom-new.bin
The syntax is the same as config.txt but section filters etc are not supported. See
online documentation for the list of paramters.
The official documentation for the Raspberry Pi bootloader EEPROM is available here:-
https://www.raspberrypi.org/documentation/hardware/raspberrypi/booteeprom.md
EOF
exit ${EXIT_SUCCESS}
}
printVersions()
{
cur="$1"
new="$2"
echo "CURRENT: $(date -u "-d@${cur}") (${cur})"
echo " LATEST: $(date -u "-d@${new}") (${new})"
}
findBootFS()
{
# recovery.bin is loaded by the ROM from the boot partition, this is normally
# ${BOOTFS} but on NOOBS this is /dev/mmcblk0p1 with volume label RECOVERY
# If ${BOOTFS} is not writable OR is not on /dev/mmcblk0 then error because the ROM
# can only load recovery.bin from the on-board SD-CARD slot or the EEPROM.
# To skip installing the safe mode recovery.bin use the -s option
if blkid | grep -qE "/dev/mmcblk0p1.*LABEL_FATBOOT.*RECOVERY.*TYPE.*vfat"; then
TMP_BOOTFS_MNT="$(mktemp -d)"
mount /dev/mmcblk0p1 "${TMP_BOOTFS_MNT}"
BOOTFS="${TMP_BOOTFS_MNT}"
fi
# If BOOTFS is not a directory or doesn't contain any .elf files then
# it's probably not the boot partition so assume that it cannot be used for a
# safe mode recovery
[ -d "${BOOTFS}" ] || die "BOOTFS: \"${BOOTFS}\" is not a directory"
[ "$(find "${BOOTFS}/" -name "*.elf" | wc -l)" -gt 0 ] || die "BOOTFS: \"${BOOTFS}\" contains no .elf files"
}
checkAndApply()
{
getCurrentVersion
getLatestCriticalUpdate
if [ "${CRITICAL_UPDATE_VERSION}" -gt "${CURRENT_VERSION}" ]; then
printVersions "${CURRENT_VERSION}" "${CRITICAL_UPDATE_VERSION}"
echo "*** INSTALLING REQUIRED UPDATE ***"
applyUpdate "${CRITICAL_UPDATE_IMAGE}"
echo "Bootloader EEPROM update pending. Please reboot to apply the update."
else
echo "Bootloader EEPROM is up to date. $(date -d@${CURRENT_VERSION})"
fi
}
fileUpdate()
{
echo "*** INSTALLING ${1} ***"
[ -f "${1}" ] || die "\"${1}\" not found"
applyUpdate "$1"
echo "Bootloader EEPROM update complete. Please reboot."
}
removePreviousUpdates()
{
if [ "$(id -u)" = "0" ]; then
findBootFS
# Remove any stale recovery.bin files or EEPROM images
# N.B. recovery.bin is normally ignored by the ROM if is not a valid
# executable but it's best to not have the file at all.
rm -f "${BOOTFS}/recovery.bin"
rm -f "${BOOTFS}/pieeprom.bin" "${BOOTFS}/pieeprom.upd"
# Case insensitive for FAT bootfs
find "${BOOTFS}" -maxdepth 1 -type f -iname "recovery.*" -regex ".*\.[0-9][0-9][0-9]$" -exec rm -f {} \;
fi
}
checkVersion()
{
getCurrentVersion
getLatestCriticalUpdate
if [ "${CRITICAL_UPDATE_VERSION}" -gt "${CURRENT_VERSION}" ]; then
echo "*** UPDATE REQUIRED ***"
printVersions "${CURRENT_VERSION}" "${CRITICAL_UPDATE_VERSION}"
write_status_info EXIT_UPDATE_REQUIRED "${CURRENT_VERSION}" "${CRITICAL_UPDATE_VERSION}"
exit ${EXIT_UPDATE_REQUIRED}
else
echo "Bootloader EEPROM is up to date"
printVersions "${CURRENT_VERSION}" "${CRITICAL_UPDATE_VERSION}"
write_status_info EXIT_SUCCESS "${CURRENT_VERSION}" "${CRITICAL_UPDATE_VERSION}"
exit ${EXIT_SUCCESS}
fi
}
write_status_info()
{
[ -z "${MACHINE_OUTPUT}" ] && return 0
exit_code="${1}"
cur=${2}
new=${3}
if [ "${JSON_OUTPUT}" = "no" ]; then
cat > "${MACHINE_OUTPUT}" <<EOF
EXITCODE="${exit_code}"
CURRENT_TS=${cur}
LATEST_TS=${new}
EOF
else
cat > "${MACHINE_OUTPUT}" <<EOF
{
"EXITCODE": "${exit_code}",
"CURRENT_TS": ${cur},
"LATEST_TS": ${new}
}
EOF
fi
return 0
}
AUTO_UPDATE=""
FILE_UPDATE=""
MACHINE_OUTPUT=""
JSON_OUTPUT="no"
CPU_VER="$(grep Revision /proc/cpuinfo | awk '{print $3}' | cut -c3)"
if [ "${CPU_VER}" != "3" ]; then
# Not a BCM2711, no EEPROMs to update.
exit 0
fi
checkDependencies
while getopts adhf:m:j option; do
case "${option}" in
a) AUTO_UPDATE=1
;;
d) OVERWRITE_CONFIG=1
;;
f) FILE_UPDATE="${OPTARG}"
;;
j) JSON_OUTPUT="yes"
;;
m) MACHINE_OUTPUT="${OPTARG}"
;;
h) usage
;;
*) echo "Unknown argument \"${option}\""
usage
;;
esac
done
removePreviousUpdates
if [ -n "${AUTO_UPDATE}" ]; then
if vcgencmd bootloader_config | grep FREEZE_VERSION=1; then
echo "EEPROM version is frozen. Skipping update"
exit ${EXIT_EEPROM_FROZEN}
else
checkAndApply
fi
elif [ -n "${FILE_UPDATE}" ]; then
fileUpdate "${FILE_UPDATE}"
else
checkVersion
fi

View File

@@ -0,0 +1,7 @@
FIRMWARE_ROOT=/lib/firmware/raspberrypi/bootloader
FIRMWARE_RELEASE_STATUS="critical"
FIRMWARE_IMAGE_DIR="${FIRMWARE_ROOT}/${FIRMWARE_RELEASE_STATUS}"
FIRMWARE_BACKUP_DIR="/var/lib/raspberrypi/bootloader/backup"
BOOTFS=/boot
USE_FLASHROM=0