binman: Support overlapping entries

In some cases it is useful to have an entry overlap with another in a
section, either to update the contents within a blob, or to add an entry
to the fdtmap that covers only part of the blob.

Add support for this.

Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Simon Glass 2023-01-11 16:10:16 -07:00
parent 97fb8081ec
commit 9766f69c98
8 changed files with 183 additions and 10 deletions

View file

@ -792,6 +792,12 @@ align-default:
symlink: symlink:
Adds a symlink to the image with string given in the symlink property. Adds a symlink to the image with string given in the symlink property.
overlap:
Indicates that this entry overlaps with others in the same section. These
entries should appear at the end of the section. Overlapping entries are not
packed with other entries, but their contents are written over other entries
in the section. Overlapping entries must have an explicit offset and size.
Examples of the above options can be found in the tests. See the Examples of the above options can be found in the tests. See the
tools/binman/test directory. tools/binman/test directory.
@ -1720,7 +1726,8 @@ implementation of Pack() is usually sufficient.
Note: for sections, this also checks that the entries do not overlap, nor extend Note: for sections, this also checks that the entries do not overlap, nor extend
outside the section. If the section does not have a defined size, the size is outside the section. If the section does not have a defined size, the size is
set large enough to hold all the entries. set large enough to hold all the entries. For entries that are explicitly marked
as overlapping, this check is skipped.
6. SetImagePos() - sets the image position of every entry. This is the absolute 6. SetImagePos() - sets the image position of every entry. This is the absolute
position 'image-pos', as opposed to 'offset' which is relative to the containing position 'image-pos', as opposed to 'offset' which is relative to the containing

View file

@ -98,6 +98,7 @@ class Entry(object):
An absent entry is removed during processing so that it does not An absent entry is removed during processing so that it does not
appear in the map appear in the map
optional (bool): True if this entry contains an optional external blob optional (bool): True if this entry contains an optional external blob
overlap (bool): True if this entry overlaps with others
""" """
fake_dir = None fake_dir = None
@ -142,6 +143,7 @@ class Entry(object):
self.auto_write_symbols = auto_write_symbols self.auto_write_symbols = auto_write_symbols
self.absent = False self.absent = False
self.optional = False self.optional = False
self.overlap = False
@staticmethod @staticmethod
def FindEntryClass(etype, expanded): def FindEntryClass(etype, expanded):
@ -294,6 +296,9 @@ class Entry(object):
self.extend_size = fdt_util.GetBool(self._node, 'extend-size') self.extend_size = fdt_util.GetBool(self._node, 'extend-size')
self.missing_msg = fdt_util.GetString(self._node, 'missing-msg') self.missing_msg = fdt_util.GetString(self._node, 'missing-msg')
self.optional = fdt_util.GetBool(self._node, 'optional') self.optional = fdt_util.GetBool(self._node, 'optional')
self.overlap = fdt_util.GetBool(self._node, 'overlap')
if self.overlap:
self.required_props += ['offset', 'size']
# This is only supported by blobs and sections at present # This is only supported by blobs and sections at present
self.compress = fdt_util.GetString(self._node, 'compress', 'none') self.compress = fdt_util.GetString(self._node, 'compress', 'none')

View file

@ -332,18 +332,30 @@ class Entry_section(Entry):
if not required and entry_data is None: if not required and entry_data is None:
return None return None
entry_data_final = entry_data
if entry_data is None: if entry_data is None:
pad_byte = (entry._pad_byte if isinstance(entry, Entry_section) pad_byte = (entry._pad_byte if isinstance(entry, Entry_section)
else self._pad_byte) else self._pad_byte)
entry_data = tools.get_bytes(self._pad_byte, entry.size) entry_data_final = tools.get_bytes(self._pad_byte, entry.size)
data = self.GetPaddedDataForEntry(entry, entry_data) data = self.GetPaddedDataForEntry(entry, entry_data_final)
# Handle empty space before the entry # Handle empty space before the entry
pad = (entry.offset or 0) - self._skip_at_start - len(section_data) pad = (entry.offset or 0) - self._skip_at_start - len(section_data)
if pad > 0: if pad > 0:
section_data += tools.get_bytes(self._pad_byte, pad) section_data += tools.get_bytes(self._pad_byte, pad)
# Add in the actual entry data # Add in the actual entry data
if entry.overlap:
end_offset = entry.offset + entry.size
if end_offset > len(section_data):
entry.Raise("Offset %#x (%d) ending at %#x (%d) must overlap with existing entries" %
(entry.offset, entry.offset, end_offset,
end_offset))
# Don't write anything for null entries'
if entry_data is not None:
section_data = (section_data[:entry.offset] + data +
section_data[entry.offset + entry.size:])
else:
section_data += data section_data += data
self.Detail('GetData: %d entries, total size %#x' % self.Detail('GetData: %d entries, total size %#x' %
@ -467,10 +479,11 @@ class Entry_section(Entry):
(entry.offset, entry.offset, entry.size, entry.size, (entry.offset, entry.offset, entry.size, entry.size,
self._node.path, self._skip_at_start, self._node.path, self._skip_at_start,
self._skip_at_start, max_size, max_size)) self._skip_at_start, max_size, max_size))
if not entry.overlap:
if entry.offset < offset and entry.size: if entry.offset < offset and entry.size:
entry.Raise("Offset %#x (%d) overlaps with previous entry '%s' " entry.Raise("Offset %#x (%d) overlaps with previous entry '%s' ending at %#x (%d)" %
"ending at %#x (%d)" % (entry.offset, entry.offset, prev_name, offset,
(entry.offset, entry.offset, prev_name, offset, offset)) offset))
offset = entry.offset + entry.size offset = entry.offset + entry.size
prev_name = entry.GetPath() prev_name = entry.GetPath()

View file

@ -6199,6 +6199,69 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
data = self._DoReadFile('268_null.dts') data = self._DoReadFile('268_null.dts')
self.assertEqual(U_BOOT_DATA + b'\xff\xff\xff\xff' + U_BOOT_IMG_DATA, data) self.assertEqual(U_BOOT_DATA + b'\xff\xff\xff\xff' + U_BOOT_IMG_DATA, data)
def testOverlap(self):
"""Test an image with a overlapping entry"""
data = self._DoReadFile('269_overlap.dts')
self.assertEqual(U_BOOT_DATA[:1] + b'aa' + U_BOOT_DATA[3:], data)
image = control.images['image']
entries = image.GetEntries()
self.assertIn('inset', entries)
inset = entries['inset']
self.assertEqual(1, inset.offset);
self.assertEqual(1, inset.image_pos);
self.assertEqual(2, inset.size);
def testOverlapNull(self):
"""Test an image with a null overlap"""
data = self._DoReadFile('270_overlap_null.dts')
self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
# Check the FMAP
fhdr, fentries = fmap_util.DecodeFmap(data[len(U_BOOT_DATA):])
self.assertEqual(4, fhdr.nareas)
fiter = iter(fentries)
fentry = next(fiter)
self.assertEqual(b'SECTION', fentry.name)
self.assertEqual(0, fentry.offset)
self.assertEqual(len(U_BOOT_DATA), fentry.size)
self.assertEqual(0, fentry.flags)
fentry = next(fiter)
self.assertEqual(b'U_BOOT', fentry.name)
self.assertEqual(0, fentry.offset)
self.assertEqual(len(U_BOOT_DATA), fentry.size)
self.assertEqual(0, fentry.flags)
# Make sure that the NULL entry appears in the FMAP
fentry = next(fiter)
self.assertEqual(b'NULL', fentry.name)
self.assertEqual(1, fentry.offset)
self.assertEqual(2, fentry.size)
self.assertEqual(0, fentry.flags)
fentry = next(fiter)
self.assertEqual(b'FMAP', fentry.name)
self.assertEqual(len(U_BOOT_DATA), fentry.offset)
def testOverlapBad(self):
"""Test an image with a bad overlapping entry"""
with self.assertRaises(ValueError) as exc:
self._DoReadFile('271_overlap_bad.dts')
self.assertIn(
"Node '/binman/inset': Offset 0x10 (16) ending at 0x12 (18) must overlap with existing entries",
str(exc.exception))
def testOverlapNoOffset(self):
"""Test an image with a bad overlapping entry"""
with self.assertRaises(ValueError) as exc:
self._DoReadFile('272_overlap_no_size.dts')
self.assertIn(
"Node '/binman/inset': 'fill' entry is missing properties: size",
str(exc.exception))
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View file

@ -0,0 +1,21 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
u-boot {
};
inset {
type = "fill";
fill-byte = [61];
offset = <1>;
size = <2>;
overlap;
};
};
};

View file

@ -0,0 +1,24 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
section {
u-boot {
};
null {
offset = <1>;
size = <2>;
overlap;
};
};
fmap {
};
};
};

View file

@ -0,0 +1,21 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
u-boot {
};
inset {
type = "fill";
fill-byte = [61];
offset = <0x10>;
size = <2>;
overlap;
};
};
};

View file

@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
u-boot {
};
inset {
type = "fill";
fill-byte = [61];
overlap;
};
};
};