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:
Tim Gover
2023-10-24 12:33:38 +01:00
parent aded0825e3
commit db154d4710
3 changed files with 135 additions and 16 deletions

View File

@@ -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: