diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f0b911b --- /dev/null +++ b/LICENSE @@ -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 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. diff --git a/firmware/critical/pieeprom-2019-05-10.bin b/firmware/critical/pieeprom-2019-05-10.bin new file mode 100644 index 0000000..c0f6caf Binary files /dev/null and b/firmware/critical/pieeprom-2019-05-10.bin differ diff --git a/firmware/critical/pieeprom-2019-07-15.bin b/firmware/critical/pieeprom-2019-07-15.bin new file mode 100644 index 0000000..fce2112 Binary files /dev/null and b/firmware/critical/pieeprom-2019-07-15.bin differ diff --git a/firmware/critical/recovery.bin b/firmware/critical/recovery.bin new file mode 100644 index 0000000..218375c Binary files /dev/null and b/firmware/critical/recovery.bin differ diff --git a/recovery.bin b/recovery.bin new file mode 100644 index 0000000..218375c Binary files /dev/null and b/recovery.bin differ diff --git a/rpi-eeprom-config b/rpi-eeprom-config new file mode 100755 index 0000000..32cbd80 --- /dev/null +++ b/rpi-eeprom-config @@ -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() diff --git a/rpi-eeprom-update b/rpi-eeprom-update new file mode 100755 index 0000000..84b2c8b --- /dev/null +++ b/rpi-eeprom-update @@ -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 < "${MACHINE_OUTPUT}" < "${MACHINE_OUTPUT}" <