mirror of
https://github.com/raspberrypi/rpi-eeprom.git
synced 2026-01-20 21:13:36 +08:00
Update the rpi-eeprom-config tool to accept config files of up to 2024 byte. The config section has a 24byte header so the section is always <= 2KiB. This allows a reasonably large user-data section in the config file accessible via 'vcgencmd bootloader_config' as an alternative to customer OTP data. N.B. The vcgencmd uses a single VCHIQ message which is limited to 4092 bytes. Setting a 2KiB limit here gives room for user-data plus some spare space for future config expansion before an VCHIQ bulk message or an extra EEPROM 4KiB page is required.
108 lines
3.8 KiB
Python
Executable File
108 lines
3.8 KiB
Python
Executable File
#!/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
|
|
|
|
MAX_BOOTCONF_SIZE = 2024
|
|
|
|
# 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 = 0x55aaf00f
|
|
MAGIC_MASK = 0xfffff00f
|
|
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:
|
|
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 len(new_config_bytes) > MAX_BOOTCONF_SIZE:
|
|
raise Exception("Config is too large (%d bytes). The maximum size is %d bytes."
|
|
% (len(new_config_bytes), MAX_BOOTCONF_SIZE))
|
|
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 the new config is smaller than the old config then set any old
|
|
# data which is now unused to all ones (erase value)
|
|
pad_start = hdr_offset + 4 + FILE_HDR_LEN + len(new_config_bytes)
|
|
pad = 0
|
|
while pad < (length - len(new_config_bytes)):
|
|
struct.pack_into('B', self._bytes, pad_start + pad, 0xff)
|
|
pad = pad + 1
|
|
|
|
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()
|