binman: Add length header attribute to dtb entry

Add an optional length header attribute to the device tree blob entry
class based on the compressed data header from the utilities to compress
and decompress data.

If needed the header could be enabled with the following
attribute beside the compress attribute:
  prepend = "length";

The header was introduced as part of commit eb0f4a4cb4 ("binman:
Support replacing data in a cbfs") to allow device tree entries to be
larger than the compressed contents. Regarding the commit "this is
necessary to cope with a compressed device tree being updated in such a
way that it shrinks after the entry size is already set (an obscure
case)". This case need to be fixed without influence any compressed data
by itself.

Signed-off-by: Stefan Herbrechtsmeier <stefan.herbrechtsmeier@weidmueller.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Stefan Herbrechtsmeier 2022-08-19 16:25:25 +02:00 committed by Simon Glass
parent 204a27bbb2
commit 6aa8000e74
5 changed files with 105 additions and 0 deletions

View File

@ -216,6 +216,9 @@ This is a blob containing a device tree. The contents of the blob are
obtained from the list of available device-tree files, managed by the
'state' module.
Additional Properties / Entry arguments:
- prepend: Header type to use:
length: 32-bit length header
.. _etype_blob_ext:

View File

@ -7,6 +7,8 @@
from binman.entry import Entry
from binman.etype.blob import Entry_blob
from dtoc import fdt_util
import struct
# This is imported if needed
state = None
@ -17,6 +19,9 @@ class Entry_blob_dtb(Entry_blob):
This is a blob containing a device tree. The contents of the blob are
obtained from the list of available device-tree files, managed by the
'state' module.
Additional attributes:
prepend: Header used (e.g. 'length')
"""
def __init__(self, section, etype, node):
# Put this here to allow entry-docs and help to work without libfdt
@ -24,6 +29,14 @@ class Entry_blob_dtb(Entry_blob):
from binman import state
super().__init__(section, etype, node)
self.prepend = None
def ReadNode(self):
super().ReadNode()
self.prepend = fdt_util.GetString(self._node, 'prepend')
if self.prepend and self.prepend not in ['length']:
self.Raise("Invalid prepend in '%s': '%s'" %
(self._node.name, self.prepend))
def ObtainContents(self):
"""Get the device-tree from the list held by the 'state' module"""
@ -58,3 +71,17 @@ class Entry_blob_dtb(Entry_blob):
# will still return the old contents
state.UpdateFdtContents(self.GetFdtEtype(), data)
return ok
def CompressData(self, indata):
data = super().CompressData(indata)
if self.prepend == 'length':
hdr = struct.pack('<I', len(data))
data = hdr + data
return data
def DecompressData(self, indata):
if self.prepend == 'length':
data_len = struct.unpack('<I', indata[:4])[0]
indata = indata[4:4 + data_len]
data = super().DecompressData(indata)
return data

View File

@ -5807,6 +5807,45 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
expect = U_BOOT_SPL_DATA + U_BOOT_DATA
self.assertEqual(expect, data[:len(expect)])
def testCompressDtbPrependInvalid(self):
"""Test that invalid header is detected"""
with self.assertRaises(ValueError) as e:
self._DoReadFileDtb('235_compress_dtb_prepend_invalid.dts')
self.assertIn("Node '/binman/u-boot-dtb': Invalid prepend in "
"'u-boot-dtb': 'invalid'", str(e.exception))
def testCompressDtbPrependLength(self):
"""Test that compress with length header works as expected"""
data = self._DoReadFileRealDtb('236_compress_dtb_prepend_length.dts')
image = control.images['image']
entries = image.GetEntries()
self.assertIn('u-boot-dtb', entries)
u_boot_dtb = entries['u-boot-dtb']
self.assertIn('fdtmap', entries)
fdtmap = entries['fdtmap']
image_fname = tools.get_output_filename('image.bin')
orig = control.ReadEntry(image_fname, 'u-boot-dtb')
dtb = fdt.Fdt.FromData(orig)
dtb.Scan()
props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
expected = {
'u-boot:size': len(U_BOOT_DATA),
'u-boot-dtb:uncomp-size': len(orig),
'u-boot-dtb:size': u_boot_dtb.size,
'fdtmap:size': fdtmap.size,
'size': len(data),
}
self.assertEqual(expected, props)
# Check implementation
self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
rest = data[len(U_BOOT_DATA):]
comp_data_len = struct.unpack('<I', rest[:4])[0]
comp_data = rest[4:4 + comp_data_len]
orig2 = self._decompress(comp_data)
self.assertEqual(orig, orig2)
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,17 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
u-boot {
};
u-boot-dtb {
compress = "lz4";
prepend = "invalid";
};
};
};

View File

@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
u-boot {
};
u-boot-dtb {
compress = "lz4";
prepend = "length";
};
fdtmap {
};
};
};