binman: Move stage header into a CBFS attribute

cbfsutil completely changed the way that stages are formatted in CBFS.
Adjust the binman implementation to do the same.

This mirrors commit 81dc20e744 in coreboot.

Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Simon Glass 2023-10-14 14:40:30 -06:00 committed by Tom Rini
parent fe35c2f011
commit ce0e9e3990
2 changed files with 42 additions and 29 deletions

View file

@ -44,10 +44,10 @@ FILE_HEADER_LEN = 0x18
FILE_MAGIC = b'LARCHIVE' FILE_MAGIC = b'LARCHIVE'
ATTRIBUTE_ALIGN = 4 # All attribute sizes must be divisible by this ATTRIBUTE_ALIGN = 4 # All attribute sizes must be divisible by this
# A stage header containing information about 'stage' files # A stage-header attribute containing information about 'stage' files
# Yes this is correct: this header is in litte-endian format # Yes this is correct: this header is in litte-endian format
STAGE_FORMAT = '<IQQII' ATTR_STAGE_FORMAT = '>IIQII'
STAGE_LEN = 0x1c ATTR_STAGE_LEN = 0x18
# An attribute describring the compression used in a file # An attribute describring the compression used in a file
ATTR_COMPRESSION_FORMAT = '>IIII' ATTR_COMPRESSION_FORMAT = '>IIII'
@ -59,6 +59,7 @@ FILE_ATTR_TAG_HASH = 0x68736148
FILE_ATTR_TAG_POSITION = 0x42435350 # PSCB FILE_ATTR_TAG_POSITION = 0x42435350 # PSCB
FILE_ATTR_TAG_ALIGNMENT = 0x42434c41 # ALCB FILE_ATTR_TAG_ALIGNMENT = 0x42434c41 # ALCB
FILE_ATTR_TAG_PADDING = 0x47444150 # PDNG FILE_ATTR_TAG_PADDING = 0x47444150 # PDNG
FILE_ATTR_TAG_STAGEHEADER = 0x53746748 # StgH
# This is 'the size of bootblock reserved in firmware image (cbfs.txt)' # This is 'the size of bootblock reserved in firmware image (cbfs.txt)'
# Not much more info is available, but we set it to 4, due to this comment in # Not much more info is available, but we set it to 4, due to this comment in
@ -97,6 +98,7 @@ ARCH_NAMES = {
# File types. Only supported ones are included here # File types. Only supported ones are included here
TYPE_CBFSHEADER = 0x02 # Master header, HEADER_FORMAT TYPE_CBFSHEADER = 0x02 # Master header, HEADER_FORMAT
TYPE_LEGACY_STAGE = 0x10 # Stage, holding an executable TYPE_LEGACY_STAGE = 0x10 # Stage, holding an executable
TYPE_STAGE = 0x11 # New-type stage with ATTR_STAGE_FORMAT
TYPE_RAW = 0x50 # Raw file, possibly compressed TYPE_RAW = 0x50 # Raw file, possibly compressed
TYPE_EMPTY = 0xffffffff # Empty data TYPE_EMPTY = 0xffffffff # Empty data
@ -265,7 +267,7 @@ class CbfsFile(object):
Returns: Returns:
CbfsFile object containing the file information CbfsFile object containing the file information
""" """
cfile = CbfsFile(name, TYPE_LEGACY_STAGE, data, cbfs_offset) cfile = CbfsFile(name, TYPE_STAGE, data, cbfs_offset)
cfile.base_address = base_address cfile.base_address = base_address
return cfile return cfile
@ -326,8 +328,8 @@ class CbfsFile(object):
""" """
name = _pack_string(self.name) name = _pack_string(self.name)
hdr_len = len(name) + FILE_HEADER_LEN hdr_len = len(name) + FILE_HEADER_LEN
if self.ftype == TYPE_LEGACY_STAGE: if self.ftype == TYPE_STAGE:
pass hdr_len += ATTR_STAGE_LEN
elif self.ftype == TYPE_RAW: elif self.ftype == TYPE_RAW:
if self.compress: if self.compress:
hdr_len += ATTR_COMPRESSION_LEN hdr_len += ATTR_COMPRESSION_LEN
@ -354,11 +356,11 @@ class CbfsFile(object):
attr = b'' attr = b''
pad = b'' pad = b''
data = self.data data = self.data
if self.ftype == TYPE_LEGACY_STAGE: if self.ftype == TYPE_STAGE:
elf_data = elf.DecodeElf(data, self.base_address) elf_data = elf.DecodeElf(data, self.base_address)
content = struct.pack(STAGE_FORMAT, self.compress, attr = struct.pack(ATTR_STAGE_FORMAT, FILE_ATTR_TAG_STAGEHEADER,
elf_data.entry, elf_data.load, ATTR_STAGE_LEN, elf_data.load,
len(elf_data.data), elf_data.memsize) elf_data.entry - elf_data.load, elf_data.memsize)
data = elf_data.data data = elf_data.data
elif self.ftype == TYPE_RAW: elif self.ftype == TYPE_RAW:
orig_data = data orig_data = data
@ -639,7 +641,7 @@ class CbfsReader(object):
files: Ordered list of CbfsFile objects files: Ordered list of CbfsFile objects
align: Alignment to use for files, typically ENTRT_ALIGN align: Alignment to use for files, typically ENTRT_ALIGN
stage_base_address: Base address to use when mapping ELF files into the stage_base_address: Base address to use when mapping ELF files into the
CBFS for TYPE_LEGACY_STAGE files. If this is larger than the code address CBFS for TYPE_STAGE files. If this is larger than the code address
of the ELF file, then data at the start of the ELF file will not of the ELF file, then data at the start of the ELF file will not
appear in the CBFS. Currently there are no tests for behaviour as appear in the CBFS. Currently there are no tests for behaviour as
documentation is sparse documentation is sparse
@ -740,26 +742,28 @@ class CbfsReader(object):
print('name', name) print('name', name)
# If there are attribute headers present, read those # If there are attribute headers present, read those
compress = self._read_attr(fd, file_pos, attr, offset) attrs = self._read_attr(fd, file_pos, attr, offset)
if compress is None: if attrs is None:
return False return False
# Create the correct CbfsFile object depending on the type # Create the correct CbfsFile object depending on the type
cfile = None cfile = None
cbfs_offset = file_pos + offset cbfs_offset = file_pos + offset
fd.seek(cbfs_offset, io.SEEK_SET) fd.seek(cbfs_offset, io.SEEK_SET)
if DEBUG:
print(f'ftype {ftype:x}')
if ftype == TYPE_CBFSHEADER: if ftype == TYPE_CBFSHEADER:
self._read_header(fd) self._read_header(fd)
elif ftype == TYPE_LEGACY_STAGE: elif ftype == TYPE_STAGE:
data = fd.read(STAGE_LEN)
cfile = CbfsFile.stage(self.stage_base_address, name, b'', cfile = CbfsFile.stage(self.stage_base_address, name, b'',
cbfs_offset) cbfs_offset)
(cfile.compress, cfile.entry, cfile.load, cfile.data_len, cfile.load, entry_offset, cfile.memlen = attrs
cfile.memlen) = struct.unpack(STAGE_FORMAT, data) cfile.entry = cfile.load + entry_offset
cfile.data = fd.read(cfile.data_len) cfile.data = fd.read(cfile.memlen)
cfile.data_len = cfile.memlen
elif ftype == TYPE_RAW: elif ftype == TYPE_RAW:
data = fd.read(size) data = fd.read(size)
cfile = CbfsFile.raw(name, data, cbfs_offset, compress) cfile = CbfsFile.raw(name, data, cbfs_offset, attrs)
cfile.decompress() cfile.decompress()
if DEBUG: if DEBUG:
print('data', data) print('data', data)
@ -783,8 +787,8 @@ class CbfsReader(object):
"""Read attributes from the file """Read attributes from the file
CBFS files can have attributes which are things that cannot fit into the CBFS files can have attributes which are things that cannot fit into the
header. The only attributes currently supported are compression and the header. The only attributes currently supported are compression, stage
unused tag. header and the unused tag
Args: Args:
fd: File to read from fd: File to read from
@ -794,11 +798,16 @@ class CbfsReader(object):
attributes) attributes)
Returns: Returns:
Either:
Compression to use for the file (COMPRESS_...) Compression to use for the file (COMPRESS_...)
tuple containing stage info:
load address
entry offset
memory size
""" """
compress = COMPRESS_NONE attrs = None
if not attr: if not attr:
return compress return COMPRESS_NONE
attr_size = offset - attr attr_size = offset - attr
fd.seek(file_pos + attr, io.SEEK_SET) fd.seek(file_pos + attr, io.SEEK_SET)
while attr_size: while attr_size:
@ -813,10 +822,15 @@ class CbfsReader(object):
# We don't currently use this information # We don't currently use this information
atag, alen, compress, _decomp_size = struct.unpack( atag, alen, compress, _decomp_size = struct.unpack(
ATTR_COMPRESSION_FORMAT, data) ATTR_COMPRESSION_FORMAT, data)
attrs = compress
elif atag == FILE_ATTR_TAG_STAGEHEADER:
atag, alen, load, entry_offset, memsize = struct.unpack(
ATTR_STAGE_FORMAT, data)
attrs = (load, entry_offset, memsize)
else: else:
print('Unknown attribute tag %x' % atag) print('Unknown attribute tag %x' % atag)
attr_size -= len(data) attr_size -= len(data)
return compress return attrs
def _read_header(self, fd): def _read_header(self, fd):
"""Read the master header """Read the master header

View file

@ -391,7 +391,7 @@ class TestCbfs(unittest.TestCase):
cbfs_util.DEBUG = True cbfs_util.DEBUG = True
with test_util.capture_sys_output() as (stdout, _stderr): with test_util.capture_sys_output() as (stdout, _stderr):
cbfs_util.CbfsReader(data) cbfs_util.CbfsReader(data)
self.assertEqual('name u-boot\ndata %s\n' % U_BOOT_DATA, self.assertEqual('name u-boot\nftype 50\ndata %s\n' % U_BOOT_DATA,
stdout.getvalue()) stdout.getvalue())
finally: finally:
cbfs_util.DEBUG = False cbfs_util.DEBUG = False
@ -475,7 +475,7 @@ class TestCbfs(unittest.TestCase):
self._compare_expected_cbfs(data, cbfs_fname) self._compare_expected_cbfs(data, cbfs_fname)
def test_cbfs_stage(self): def test_cbfs_stage(self):
"""Tests handling of a Coreboot Filesystem (CBFS)""" """Tests handling of a CBFS stage"""
if not elf.ELF_TOOLS: if not elf.ELF_TOOLS:
self.skipTest('Python elftools not available') self.skipTest('Python elftools not available')
elf_fname = os.path.join(self._indir, 'cbfs-stage.elf') elf_fname = os.path.join(self._indir, 'cbfs-stage.elf')
@ -490,8 +490,7 @@ class TestCbfs(unittest.TestCase):
load = 0xfef20000 load = 0xfef20000
entry = load + 2 entry = load + 2
cfile = self._check_uboot(cbfs, cbfs_util.TYPE_LEGACY_STAGE, cfile = self._check_uboot(cbfs, cbfs_util.TYPE_STAGE, offset=0x38,
offset=0x20,
data=U_BOOT_DATA + U_BOOT_DTB_DATA) data=U_BOOT_DATA + U_BOOT_DTB_DATA)
self.assertEqual(entry, cfile.entry) self.assertEqual(entry, cfile.entry)