Files
rpi-eeprom/rpi-eeprom-config
Tim Gover 1ba58068ea Set unused data to zero if size of config is reduced
If the size of bootconf.txt is reduced then set the unused data
to all ones instead of leaving garbage at the end. The EEPROM
header contains the actual file but this makes it easier to
verify the image and makes overreads more obvious.
2019-09-24 13:39:42 +01:00

105 lines
3.7 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
# 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 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 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()