[PATCH 5/7] binman: Support generating FITs with multiple dtbs
Simon Glass
sjg at chromium.org
Sat Aug 22 04:36:56 CEST 2020
In some cases it is useful to generate a FIT which has a number of DTB
images, selectable by configuration. Add support for this in binman,
using a simple iterator and string substitution.
Signed-off-by: Simon Glass <sjg at chromium.org>
---
tools/binman/README.entries | 42 +++++++++++++++-
tools/binman/etype/fit.py | 76 ++++++++++++++++++++++++++---
tools/binman/ftest.py | 80 +++++++++++++++++++++++++++++--
tools/binman/test/166_fit_fdt.dts | 55 +++++++++++++++++++++
4 files changed, 242 insertions(+), 11 deletions(-)
create mode 100644 tools/binman/test/166_fit_fdt.dts
diff --git a/tools/binman/README.entries b/tools/binman/README.entries
index 85b8ec9fa73..2bf7655b7f0 100644
--- a/tools/binman/README.entries
+++ b/tools/binman/README.entries
@@ -335,6 +335,7 @@ For example, this creates an image containing a FIT with U-Boot SPL:
binman {
fit {
description = "Test FIT";
+ fit,fdt-list = "of-list";
images {
kernel at 1 {
@@ -353,7 +354,46 @@ For example, this creates an image containing a FIT with U-Boot SPL:
};
};
-Properties:
+U-Boot supports creating fdt and config nodes automatically. To do this,
+pass an of-list property (e.g. -a of-list=file1 file2). This tells binman
+that you want to generates nodes for two files: file1.dtb and file2.dtb
+The fit,fdt-list property (see above) indicates that of-list should be used.
+
+Then add a 'generator node', a node with a name starting with '@':
+
+ @fdt-SEQ {
+ description = "fdt-NAME";
+ type = "flat_dt";
+ compression = "none";
+ };
+
+This tells binman to create nodes fdt-1 and fdt-2 for each of your two
+files. All the properties you specify will be included in the node. This
+node acts like a template to generate the nodes. The generator node itself
+does not appear in the output - it is replaced with what binman generates.
+
+You can create config nodes in a similar way:
+
+ @config-SEQ {
+ description = "NAME";
+ firmware = "uboot";
+ loadables = "atf";
+ fdt = "fdt-SEQ";
+ };
+
+This tells binman to create nodes config-1 and config-2, i.e. a config for
+each of your two files.
+
+Available substitutions for '@' nodes are:
+
+ SEQ Sequence number of the generated fdt (1, 2, ...)
+ NAME Name of the dtb as provided (i.e. without adding '.dtb')
+
+Note that if no device tree files are provided (with '-a of-list' as above)
+then no nodes will be generated.
+
+
+Properties (in the 'fit' node itself):
fit,external-offset: Indicates that the contents of the FIT are external
and provides the external offset. This is passsed to mkimage via
the -E and -p flags.
diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py
index 75712f44092..e45a99dfd73 100644
--- a/tools/binman/etype/fit.py
+++ b/tools/binman/etype/fit.py
@@ -8,7 +8,7 @@
from collections import defaultdict, OrderedDict
import libfdt
-from binman.entry import Entry
+from binman.entry import Entry, EntryArg
from dtoc import fdt_util
from dtoc.fdt import Fdt
from patman import tools
@@ -27,6 +27,7 @@ class Entry_fit(Entry):
binman {
fit {
description = "Test FIT";
+ fit,fdt-list = "of-list";
images {
kernel at 1 {
@@ -45,7 +46,46 @@ class Entry_fit(Entry):
};
};
- Properties:
+ U-Boot supports creating fdt and config nodes automatically. To do this,
+ pass an of-list property (e.g. -a of-list=file1 file2). This tells binman
+ that you want to generates nodes for two files: file1.dtb and file2.dtb
+ The fit,fdt-list property (see above) indicates that of-list should be used.
+
+ Then add a 'generator node', a node with a name starting with '@':
+
+ @fdt-SEQ {
+ description = "fdt-NAME";
+ type = "flat_dt";
+ compression = "none";
+ };
+
+ This tells binman to create nodes fdt-1 and fdt-2 for each of your two
+ files. All the properties you specify will be included in the node. This
+ node acts like a template to generate the nodes. The generator node itself
+ does not appear in the output - it is replaced with what binman generates.
+
+ You can create config nodes in a similar way:
+
+ @config-SEQ {
+ description = "NAME";
+ firmware = "uboot";
+ loadables = "atf";
+ fdt = "fdt-SEQ";
+ };
+
+ This tells binman to create nodes config-1 and config-2, i.e. a config for
+ each of your two files.
+
+ Available substitutions for '@' nodes are:
+
+ SEQ Sequence number of the generated fdt (1, 2, ...)
+ NAME Name of the dtb as provided (i.e. without adding '.dtb')
+
+ Note that if no device tree files are provided (with '-a of-list' as above)
+ then no nodes will be generated.
+
+
+ Properties (in the 'fit' node itself):
fit,external-offset: Indicates that the contents of the FIT are external
and provides the external offset. This is passsed to mkimage via
the -E and -p flags.
@@ -64,6 +104,15 @@ class Entry_fit(Entry):
self._fit = None
self._fit_content = defaultdict(list)
self._fit_props = {}
+ for pname, prop in self._node.props.items():
+ if pname.startswith('fit,'):
+ self._fit_props[pname] = prop
+
+ self._fdts = []
+ list_prop = self._fit_props.get('fit,fdt-list')
+ if list_prop:
+ fdts, = self.GetEntryArgsOrProps([EntryArg(list_prop.value, str)])
+ self._fdts = fdts and fdts.split() or []
def ReadNode(self):
self._ReadSubnodes()
@@ -84,13 +133,12 @@ class Entry_fit(Entry):
image
"""
for pname, prop in node.props.items():
- if pname.startswith('fit,'):
- self._fit_props[pname] = prop
- else:
+ if not pname.startswith('fit,'):
fsw.property(pname, prop.bytes)
rel_path = node.path[len(base_node.path):]
- has_images = depth == 2 and rel_path.startswith('/images/')
+ in_images = rel_path.startswith('/images')
+ has_images = depth == 2 and in_images
for subnode in node.subnodes:
if has_images and not (subnode.name.startswith('hash') or
subnode.name.startswith('signature')):
@@ -100,6 +148,22 @@ class Entry_fit(Entry):
entry = Entry.Create(self.section, subnode)
entry.ReadNode()
self._fit_content[rel_path].append(entry)
+ elif subnode.name.startswith('@'):
+ for seq, fdt_fname in enumerate(self._fdts):
+ node_name = subnode.name[1:].replace('SEQ',
+ str(seq + 1))
+ fname = tools.GetInputFilename(fdt_fname + '.dtb')
+ with fsw.add_node(node_name):
+ for pname, prop in subnode.props.items():
+ val = prop.bytes.replace(
+ b'NAME', tools.ToBytes(fdt_fname))
+ val = val.replace(b'SEQ',
+ tools.ToBytes(str(seq + 1)))
+ fsw.property(pname, val)
+
+ # Add data for 'fdt' nodes (but not 'config')
+ if depth == 1 and in_images:
+ fsw.property('data', tools.ReadFile(fname))
else:
with fsw.add_node(subnode.name):
_AddNode(base_node, depth + 1, subnode)
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
index 30c9c764327..67b86276724 100644
--- a/tools/binman/ftest.py
+++ b/tools/binman/ftest.py
@@ -76,6 +76,11 @@ FSP_M_DATA = b'fsp_m'
FSP_S_DATA = b'fsp_s'
FSP_T_DATA = b'fsp_t'
ATF_BL31_DATA = b'bl31'
+TEST_FDT1_DATA = b'fdt1'
+TEST_FDT2_DATA = b'test-fdt2'
+
+# Subdirectory of the input dir to use to put test FDTs
+TEST_FDT_SUBDIR = 'fdts'
# The expected size for the device tree in some tests
EXTRACT_DTB_SIZE = 0x3c9
@@ -171,6 +176,12 @@ class TestFunctional(unittest.TestCase):
TestFunctional._MakeInputFile('compress', COMPRESS_DATA)
TestFunctional._MakeInputFile('bl31.bin', ATF_BL31_DATA)
+ # Add a few .dtb files for testing
+ TestFunctional._MakeInputFile('%s/test-fdt1.dtb' % TEST_FDT_SUBDIR,
+ TEST_FDT1_DATA)
+ TestFunctional._MakeInputFile('%s/test-fdt2.dtb' % TEST_FDT_SUBDIR,
+ TEST_FDT2_DATA)
+
# Travis-CI may have an old lz4
cls.have_lz4 = True
try:
@@ -288,7 +299,7 @@ class TestFunctional(unittest.TestCase):
def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False,
entry_args=None, images=None, use_real_dtb=False,
- verbosity=None, allow_missing=False):
+ verbosity=None, allow_missing=False, extra_indirs=None):
"""Run binman with a given test file
Args:
@@ -308,6 +319,7 @@ class TestFunctional(unittest.TestCase):
verbosity: Verbosity level to use (0-3, None=don't set it)
allow_missing: Set the '--allow-missing' flag so that missing
external binaries just produce a warning instead of an error
+ extra_indirs: Extra input directories to add using -I
"""
args = []
if debug:
@@ -334,6 +346,9 @@ class TestFunctional(unittest.TestCase):
if images:
for image in images:
args += ['-i', image]
+ if extra_indirs:
+ for indir in extra_indirs:
+ args += ['-I', indir]
return self._DoBinman(*args)
def _SetupDtb(self, fname, outfile='u-boot.dtb'):
@@ -383,7 +398,8 @@ class TestFunctional(unittest.TestCase):
return dtb.GetContents()
def _DoReadFileDtb(self, fname, use_real_dtb=False, map=False,
- update_dtb=False, entry_args=None, reset_dtbs=True):
+ update_dtb=False, entry_args=None, reset_dtbs=True,
+ extra_indirs=None):
"""Run binman and return the resulting image
This runs binman with a given test file and then reads the resulting
@@ -407,6 +423,7 @@ class TestFunctional(unittest.TestCase):
reset_dtbs: With use_real_dtb the test dtb is overwritten by this
function. If reset_dtbs is True, then the original test dtb
is written back before this function finishes
+ extra_indirs: Extra input directories to add using -I
Returns:
Tuple:
@@ -430,7 +447,8 @@ class TestFunctional(unittest.TestCase):
try:
retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
- entry_args=entry_args, use_real_dtb=use_real_dtb)
+ entry_args=entry_args, use_real_dtb=use_real_dtb,
+ extra_indirs=extra_indirs)
self.assertEqual(0, retcode)
out_dtb_fname = tools.GetOutputFilename('u-boot.dtb.out')
@@ -3512,7 +3530,61 @@ class TestFunctional(unittest.TestCase):
data = self._DoReadFile('165_atf_bl31.dts')
self.assertEqual(ATF_BL31_DATA, data[:len(ATF_BL31_DATA)])
-ATF_BL31_DATA
+ def testFitFdt(self):
+ """Test an image with an FIT with multiple FDT images"""
+ def _CheckFdt(seq, expected_data):
+ """Check the FDT nodes
+
+ Args:
+ seq: Sequence number to check (0 or 1)
+ expected_data: Expected contents of 'data' property
+ """
+ name = 'fdt-%d' % seq
+ fnode = dtb.GetNode('/images/%s' % name)
+ self.assertIsNotNone(fnode)
+ self.assertEqual({'description','type', 'compression', 'data'},
+ set(fnode.props.keys()))
+ self.assertEqual(expected_data, fnode.props['data'].bytes)
+ self.assertEqual('fdt-test-fdt%d.dtb' % seq,
+ fnode.props['description'].value)
+ def _CheckConfig(seq, expected_data):
+ """Check the configuration nodes
+
+ Args:
+ seq: Sequence number to check (0 or 1)
+ expected_data: Expected contents of 'data' property
+ """
+ name = 'config-%d' % seq
+ fnode = dtb.GetNode('/configurations/%s' % name)
+ self.assertIsNotNone(fnode)
+ self.assertEqual({'description','firmware', 'loadables', 'fdt'},
+ set(fnode.props.keys()))
+ self.assertEqual('conf-test-fdt%d.dtb' % seq,
+ fnode.props['description'].value)
+ self.assertEqual('fdt-%d' % seq, fnode.props['fdt'].value)
+
+ entry_args = {
+ 'of-list': 'test-fdt1 test-fdt2',
+ }
+ data = self._DoReadFileDtb(
+ '166_fit_fdt.dts',
+ entry_args=entry_args,
+ extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
+ self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
+ fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
+
+ dtb = fdt.Fdt.FromData(fit_data)
+ dtb.Scan()
+ fnode = dtb.GetNode('/images/kernel')
+ self.assertIn('data', fnode.props)
+
+ # Check all the properties in fdt-1 and fdt-2
+ _CheckFdt(1, TEST_FDT1_DATA)
+ _CheckFdt(2, TEST_FDT2_DATA)
+
+ # Check configurations
+ _CheckConfig(1, TEST_FDT1_DATA)
+ _CheckConfig(2, TEST_FDT2_DATA)
if __name__ == "__main__":
unittest.main()
diff --git a/tools/binman/test/166_fit_fdt.dts b/tools/binman/test/166_fit_fdt.dts
new file mode 100644
index 00000000000..89142e91017
--- /dev/null
+++ b/tools/binman/test/166_fit_fdt.dts
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ binman {
+ u-boot {
+ };
+ fit {
+ description = "test-desc";
+ #address-cells = <1>;
+ fit,fdt-list = "of-list";
+
+ images {
+ kernel {
+ description = "Vanilla Linux kernel";
+ type = "kernel";
+ arch = "ppc";
+ os = "linux";
+ compression = "gzip";
+ load = <00000000>;
+ entry = <00000000>;
+ hash-1 {
+ algo = "crc32";
+ };
+ hash-2 {
+ algo = "sha1";
+ };
+ u-boot {
+ };
+ };
+ @fdt-SEQ {
+ description = "fdt-NAME.dtb";
+ type = "flat_dt";
+ compression = "none";
+ };
+ };
+
+ configurations {
+ default = "config-1";
+ @config-SEQ {
+ description = "conf-NAME.dtb";
+ firmware = "uboot";
+ loadables = "atf";
+ fdt = "fdt-SEQ";
+ };
+ };
+ };
+ u-boot-nodtb {
+ };
+ };
+};
--
2.28.0.297.g1956fa8f8d-goog
More information about the U-Boot
mailing list