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:
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
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
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
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
appear in the map
optional (bool): True if this entry contains an optional external blob
overlap (bool): True if this entry overlaps with others
"""
fake_dir = None
@ -142,6 +143,7 @@ class Entry(object):
self.auto_write_symbols = auto_write_symbols
self.absent = False
self.optional = False
self.overlap = False
@staticmethod
def FindEntryClass(etype, expanded):
@ -294,6 +296,9 @@ class Entry(object):
self.extend_size = fdt_util.GetBool(self._node, 'extend-size')
self.missing_msg = fdt_util.GetString(self._node, 'missing-msg')
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
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:
return None
entry_data_final = entry_data
if entry_data is None:
pad_byte = (entry._pad_byte if isinstance(entry, Entry_section)
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
pad = (entry.offset or 0) - self._skip_at_start - len(section_data)
if pad > 0:
section_data += tools.get_bytes(self._pad_byte, pad)
# 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
self.Detail('GetData: %d entries, total size %#x' %
@ -467,10 +479,11 @@ class Entry_section(Entry):
(entry.offset, entry.offset, entry.size, entry.size,
self._node.path, self._skip_at_start,
self._skip_at_start, max_size, max_size))
if not entry.overlap:
if entry.offset < offset and entry.size:
entry.Raise("Offset %#x (%d) overlaps with previous entry '%s' "
"ending at %#x (%d)" %
(entry.offset, entry.offset, prev_name, offset, offset))
entry.Raise("Offset %#x (%d) overlaps with previous entry '%s' ending at %#x (%d)" %
(entry.offset, entry.offset, prev_name, offset,
offset))
offset = entry.offset + entry.size
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')
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__":
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;
};
};
};