diff options
| author | Tom Rini <[email protected]> | 2021-03-28 20:29:39 -0400 |
|---|---|---|
| committer | Tom Rini <[email protected]> | 2021-03-28 20:29:39 -0400 |
| commit | 4906238191b90be7aec2269ba8cd6aeb161cd312 (patch) | |
| tree | 1bab411fe047542ab69342a90fba6110f4dbe124 /tools | |
| parent | 9c7335e4e68355a96bd5a411b2a5f85866823c58 (diff) | |
| parent | e5021221db3faf7e90a295d6eb045fbf5c6a908b (diff) | |
Merge tag 'dm-pull-28mar21' of git://git.denx.de/u-boot-dm into next
binman support for expanding entries, connections
misc fixes and improvements to sandbox, etc.
x86 CBFS improvements
x86 coreboot improvements
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/binman/binman.rst | 14 | ||||
| -rw-r--r-- | tools/binman/entries.rst | 21 | ||||
| -rw-r--r-- | tools/binman/entry.py | 16 | ||||
| -rw-r--r-- | tools/binman/etype/cbfs.py | 1 | ||||
| -rw-r--r-- | tools/binman/etype/collection.py | 67 | ||||
| -rw-r--r-- | tools/binman/etype/mkimage.py | 1 | ||||
| -rw-r--r-- | tools/binman/etype/section.py | 36 | ||||
| -rw-r--r-- | tools/binman/etype/u_boot.py | 2 | ||||
| -rw-r--r-- | tools/binman/etype/u_boot_spl.py | 2 | ||||
| -rw-r--r-- | tools/binman/etype/u_boot_tpl.py | 2 | ||||
| -rw-r--r-- | tools/binman/etype/vblock.py | 36 | ||||
| -rw-r--r-- | tools/binman/ftest.py | 57 | ||||
| -rw-r--r-- | tools/binman/test/196_symbols_nodtb.dts (renamed from tools/binman/test/192_symbols_nodtb.dts) | 0 | ||||
| -rw-r--r-- | tools/binman/test/197_symbols_expand.dts | 23 | ||||
| -rw-r--r-- | tools/binman/test/198_collection.dts | 27 | ||||
| -rw-r--r-- | tools/binman/test/199_collection_section.dts | 32 | ||||
| -rw-r--r-- | tools/binman/test/200_align_default.dts | 30 | ||||
| -rw-r--r-- | tools/dtoc/fdt.py | 92 | ||||
| -rw-r--r-- | tools/dtoc/test/dtoc_test_simple.dts | 4 | ||||
| -rwxr-xr-x | tools/dtoc/test_fdt.py | 76 |
20 files changed, 470 insertions, 69 deletions
diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst index 15314d19586..1aa2459d50c 100644 --- a/tools/binman/binman.rst +++ b/tools/binman/binman.rst @@ -472,6 +472,13 @@ missing-msg: information about what needs to be fixed. See missing-blob-help for the message for each tag. +no-expanded: + By default binman substitutes entries with expanded versions if available, + so that a `u-boot` entry type turns into `u-boot-expanded`, for example. The + `--no-expanded` command-line option disables this globally. The + `no-expanded` property disables this just for a single entry. Put the + `no-expanded` boolean property in the node to select this behaviour. + The attributes supported for images and sections are described below. Several are similar to those for entries. @@ -555,6 +562,13 @@ skip-at-start: 'end-at-4gb' property is not applicable where CONFIG_SYS_TEXT_BASE + Image size != 4gb. +align-default: + Specifies the default alignment for entries in this section, if they do + not specify an alignment. Note that this only applies to top-level entries + in the section (direct subentries), not any subentries of those entries. + This means that each section must specify its own default alignment, if + required. + Examples of the above options can be found in the tests. See the tools/binman/test directory. diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index f6faa15562f..a91211e93ed 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -248,6 +248,19 @@ both of size 1MB. +Entry: collection: An entry which contains a collection of other entries +------------------------------------------------------------------------ + +Properties / Entry arguments: + - content: List of phandles to entries to include + +This allows reusing the contents of other entries. The contents of the +listed entries are combined to form this entry. This serves as a useful +base class for entry types which need to process data from elsewhere in +the image, not necessarily child entries. + + + Entry: cros-ec-rw: A blob entry which contains a Chromium OS read-write EC image -------------------------------------------------------------------------------- @@ -783,6 +796,8 @@ Properties / Entry arguments: (see binman README for more information): file, since the first 16 bytes are skipped when writing. name-prefix: Adds a prefix to the name of every entry in the section when writing out the map + align_default: Default alignment for this section, if no alignment is + given in the entry Properties: allow_missing: True if this section permits external blobs to be @@ -863,7 +878,7 @@ U-Boot can access binman symbols at runtime. See: in the binman README for more information. Note that this entry is automatically replaced with u-boot-expanded unless ---no-expanded is used. +--no-expanded is used or the node has a 'no-expanded' property. @@ -984,7 +999,7 @@ The ELF file 'spl/u-boot-spl' must also be available for this to work, since binman uses that to look up symbols to write into the SPL binary. Note that this entry is automatically replaced with u-boot-spl-expanded -unless --no-expanded is used. +unless --no-expanded is used or the node has a 'no-expanded' property. @@ -1113,7 +1128,7 @@ The ELF file 'tpl/u-boot-tpl' must also be available for this to work, since binman uses that to look up symbols to write into the TPL binary. Note that this entry is automatically replaced with u-boot-tpl-expanded -unless --no-expanded is used. +unless --no-expanded is used or the node has a 'no-expanded' property. diff --git a/tools/binman/entry.py b/tools/binman/entry.py index 1cfa024a9f9..70222718ea9 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -164,7 +164,8 @@ class Entry(object): if obj and expanded: # Check whether to use the expanded entry new_etype = etype + '-expanded' - if obj.UseExpanded(node, etype, new_etype): + can_expand = not fdt_util.GetBool(node, 'no-expanded') + if can_expand and obj.UseExpanded(node, etype, new_etype): etype = new_etype else: obj = None @@ -200,6 +201,8 @@ class Entry(object): if tools.NotPowerOfTwo(self.align): raise ValueError("Node '%s': Alignment %s must be a power of two" % (self._node.path, self.align)) + if self.section and self.align is None: + self.align = self.section.align_default self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0) self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0) self.align_size = fdt_util.GetInt(self._node, 'align-size') @@ -437,6 +440,11 @@ class Entry(object): """Convenience function to raise an error referencing a node""" raise ValueError("Node '%s': %s" % (self._node.path, msg)) + def Info(self, msg): + """Convenience function to log info referencing a node""" + tag = "Info '%s'" % self._node.path + tout.Detail('%30s: %s' % (tag, msg)) + def Detail(self, msg): """Convenience function to log detail referencing a node""" tag = "Node '%s'" % self._node.path @@ -476,9 +484,13 @@ class Entry(object): """ return self._node.path - def GetData(self): + def GetData(self, required=True): """Get the contents of an entry + Args: + required: True if the data must be present, False if it is OK to + return None + Returns: bytes content of the entry, excluding any padding. If the entry is compressed, the compressed data is returned diff --git a/tools/binman/etype/cbfs.py b/tools/binman/etype/cbfs.py index 1daddeb8229..44db7b9bb20 100644 --- a/tools/binman/etype/cbfs.py +++ b/tools/binman/etype/cbfs.py @@ -169,6 +169,7 @@ class Entry_cbfs(Entry): super().__init__(section, etype, node) self._cbfs_arg = fdt_util.GetString(node, 'cbfs-arch', 'x86') + self.align_default = None self._cbfs_entries = OrderedDict() self._ReadSubnodes() self.reader = None diff --git a/tools/binman/etype/collection.py b/tools/binman/etype/collection.py new file mode 100644 index 00000000000..1625575fe98 --- /dev/null +++ b/tools/binman/etype/collection.py @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2021 Google LLC +# Written by Simon Glass <[email protected]> +# + +# Support for a collection of entries from other parts of an image + +from collections import OrderedDict +import os + +from binman.entry import Entry +from dtoc import fdt_util + +class Entry_collection(Entry): + """An entry which contains a collection of other entries + + Properties / Entry arguments: + - content: List of phandles to entries to include + + This allows reusing the contents of other entries. The contents of the + listed entries are combined to form this entry. This serves as a useful + base class for entry types which need to process data from elsewhere in + the image, not necessarily child entries. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + self.content = fdt_util.GetPhandleList(self._node, 'content') + if not self.content: + self.Raise("Collection must have a 'content' property") + + def GetContents(self, required): + """Get the contents of this entry + + Args: + required: True if the data must be present, False if it is OK to + return None + + Returns: + bytes content of the entry + """ + # Join up all the data + self.Info('Getting contents, required=%s' % required) + data = b'' + for entry_phandle in self.content: + entry_data = self.section.GetContentsByPhandle(entry_phandle, self, + required) + if not required and entry_data is None: + self.Info('Contents not available yet') + # Data not available yet + return None + data += entry_data + + self.Info('Returning contents size %x' % len(data)) + + return data + + def ObtainContents(self): + data = self.GetContents(False) + if data is None: + return False + self.SetContents(data) + return True + + def ProcessContents(self): + # The blob may have changed due to WriteSymbols() + data = self.GetContents(True) + return self.ProcessContentsUpdate(data) diff --git a/tools/binman/etype/mkimage.py b/tools/binman/etype/mkimage.py index e9f82729ab4..e49977522e3 100644 --- a/tools/binman/etype/mkimage.py +++ b/tools/binman/etype/mkimage.py @@ -36,6 +36,7 @@ class Entry_mkimage(Entry): super().__init__(section, etype, node) self._args = fdt_util.GetString(self._node, 'args').split(' ') self._mkimage_entries = OrderedDict() + self.align_default = None self._ReadSubnodes() def ObtainContents(self): diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py index cce1500b4e5..c3bac026c14 100644 --- a/tools/binman/etype/section.py +++ b/tools/binman/etype/section.py @@ -36,6 +36,8 @@ class Entry_section(Entry): file, since the first 16 bytes are skipped when writing. name-prefix: Adds a prefix to the name of every entry in the section when writing out the map + align_default: Default alignment for this section, if no alignment is + given in the entry Properties: allow_missing: True if this section permits external blobs to be @@ -76,6 +78,7 @@ class Entry_section(Entry): if self._skip_at_start is None: self._skip_at_start = 0 self._name_prefix = fdt_util.GetString(self._node, 'name-prefix') + self.align_default = fdt_util.GetInt(self._node, 'align-default', 0) filename = fdt_util.GetString(self._node, 'filename') if filename: self._filename = filename @@ -180,7 +183,7 @@ class Entry_section(Entry): return data - def _BuildSectionData(self): + def _BuildSectionData(self, required): """Build the contents of a section This places all entries at the right place, dealing with padding before @@ -188,13 +191,20 @@ class Entry_section(Entry): pad-before and pad-after properties in the section items) since that is handled by the parent section. + Args: + required: True if the data must be present, False if it is OK to + return None + Returns: Contents of the section (bytes) """ section_data = b'' for entry in self._entries.values(): - data = self.GetPaddedDataForEntry(entry, entry.GetData()) + entry_data = entry.GetData(required) + if not required and entry_data is None: + return None + data = self.GetPaddedDataForEntry(entry, entry_data) # Handle empty space before the entry pad = (entry.offset or 0) - self._skip_at_start - len(section_data) if pad > 0: @@ -226,18 +236,24 @@ class Entry_section(Entry): data = self.GetData() return section.GetPaddedDataForEntry(self, data) - def GetData(self): + def GetData(self, required=True): """Get the contents of an entry This builds the contents of the section, stores this as the contents of the section and returns it + Args: + required: True if the data must be present, False if it is OK to + return None + Returns: bytes content of the section, made up for all all of its subentries. This excludes any padding. If the section is compressed, the compressed data is returned """ - data = self._BuildSectionData() + data = self._BuildSectionData(required) + if data is None: + return None self.SetContents(data) return data @@ -263,7 +279,7 @@ class Entry_section(Entry): self._SortEntries() self._ExpandEntries() - data = self._BuildSectionData() + data = self._BuildSectionData(True) self.SetContents(data) self.CheckSize() @@ -360,16 +376,20 @@ class Entry_section(Entry): def GetEntries(self): return self._entries - def GetContentsByPhandle(self, phandle, source_entry): + def GetContentsByPhandle(self, phandle, source_entry, required): """Get the data contents of an entry specified by a phandle This uses a phandle to look up a node and and find the entry - associated with it. Then it returnst he contents of that entry. + associated with it. Then it returns the contents of that entry. + + The node must be a direct subnode of this section. Args: phandle: Phandle to look up (integer) source_entry: Entry containing that phandle (used for error reporting) + required: True if the data must be present, False if it is OK to + return None Returns: data from associated entry (as a string), or None if not found @@ -379,7 +399,7 @@ class Entry_section(Entry): source_entry.Raise("Cannot find node for phandle %d" % phandle) for entry in self._entries.values(): if entry._node == node: - return entry.GetData() + return entry.GetData(required) source_entry.Raise("Cannot find entry for node '%s'" % node.name) def LookupSymbol(self, sym_name, optional, msg, base_addr, entries=None): diff --git a/tools/binman/etype/u_boot.py b/tools/binman/etype/u_boot.py index d2eaba6d4aa..e8d180a46dd 100644 --- a/tools/binman/etype/u_boot.py +++ b/tools/binman/etype/u_boot.py @@ -25,7 +25,7 @@ class Entry_u_boot(Entry_blob): in the binman README for more information. Note that this entry is automatically replaced with u-boot-expanded unless - --no-expanded is used. + --no-expanded is used or the node has a 'no-expanded' property. """ def __init__(self, section, etype, node): super().__init__(section, etype, node) diff --git a/tools/binman/etype/u_boot_spl.py b/tools/binman/etype/u_boot_spl.py index 1c39f982519..6f79bf59f9f 100644 --- a/tools/binman/etype/u_boot_spl.py +++ b/tools/binman/etype/u_boot_spl.py @@ -32,7 +32,7 @@ class Entry_u_boot_spl(Entry_blob): binman uses that to look up symbols to write into the SPL binary. Note that this entry is automatically replaced with u-boot-spl-expanded - unless --no-expanded is used. + unless --no-expanded is used or the node has a 'no-expanded' property. """ def __init__(self, section, etype, node): super().__init__(section, etype, node) diff --git a/tools/binman/etype/u_boot_tpl.py b/tools/binman/etype/u_boot_tpl.py index 95d9bd6b20e..0c575df8cdc 100644 --- a/tools/binman/etype/u_boot_tpl.py +++ b/tools/binman/etype/u_boot_tpl.py @@ -32,7 +32,7 @@ class Entry_u_boot_tpl(Entry_blob): binman uses that to look up symbols to write into the TPL binary. Note that this entry is automatically replaced with u-boot-tpl-expanded - unless --no-expanded is used. + unless --no-expanded is used or the node has a 'no-expanded' property. """ def __init__(self, section, etype, node): super().__init__(section, etype, node) diff --git a/tools/binman/etype/vblock.py b/tools/binman/etype/vblock.py index eba5351dd52..c0a6a28c943 100644 --- a/tools/binman/etype/vblock.py +++ b/tools/binman/etype/vblock.py @@ -9,12 +9,13 @@ from collections import OrderedDict import os -from binman.entry import Entry, EntryArg +from binman.entry import EntryArg +from binman.etype.collection import Entry_collection from dtoc import fdt_util from patman import tools -class Entry_vblock(Entry): +class Entry_vblock(Entry_collection): """An entry which contains a Chromium OS verified boot block Properties / Entry arguments: @@ -37,9 +38,6 @@ class Entry_vblock(Entry): """ def __init__(self, section, etype, node): super().__init__(section, etype, node) - self.content = fdt_util.GetPhandleList(self._node, 'content') - if not self.content: - self.Raise("Vblock must have a 'content' property") (self.keydir, self.keyblock, self.signprivate, self.version, self.kernelkey, self.preamble_flags) = self.GetEntryArgsOrProps([ EntryArg('keydir', str), @@ -49,15 +47,21 @@ class Entry_vblock(Entry): EntryArg('kernelkey', str), EntryArg('preamble-flags', int)]) - def GetVblock(self): + def GetVblock(self, required): + """Get the contents of this entry + + Args: + required: True if the data must be present, False if it is OK to + return None + + Returns: + bytes content of the entry, which is the signed vblock for the + provided data + """ # Join up the data files to be signed - input_data = b'' - for entry_phandle in self.content: - data = self.section.GetContentsByPhandle(entry_phandle, self) - if data is None: - # Data not available yet - return False - input_data += data + input_data = self.GetContents(required) + if input_data is None: + return None uniq = self.GetUniqueName() output_fname = tools.GetOutputFilename('vblock.%s' % uniq) @@ -79,13 +83,13 @@ class Entry_vblock(Entry): return tools.ReadFile(output_fname) def ObtainContents(self): - data = self.GetVblock() - if data is False: + data = self.GetVblock(False) + if data is None: return False self.SetContents(data) return True def ProcessContents(self): # The blob may have changed due to WriteSymbols() - data = self.GetVblock() + data = self.GetVblock(True) return self.ProcessContentsUpdate(data) diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 81c213a908a..89fe6612e1b 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -1344,13 +1344,19 @@ class TestFunctional(unittest.TestCase): data = self._DoReadFile('052_u_boot_spl_nodtb.dts') self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)]) - def checkSymbols(self, dts, base_data, u_boot_offset): + def checkSymbols(self, dts, base_data, u_boot_offset, entry_args=None, + use_expanded=False): """Check the image contains the expected symbol values Args: dts: Device tree file to use for test base_data: Data before and after 'u-boot' section u_boot_offset: Offset of 'u-boot' section in image + entry_args: Dict of entry args to supply to binman + key: arg name + value: value of that arg + use_expanded: True to use expanded entries where available, e.g. + 'u-boot-expanded' instead of 'u-boot' """ elf_fname = self.ElfTestFile('u_boot_binman_syms') syms = elf.GetSymbols(elf_fname, ['binman', 'image']) @@ -1359,7 +1365,8 @@ class TestFunctional(unittest.TestCase): addr) self._SetupSplElf('u_boot_binman_syms') - data = self._DoReadFile(dts) + data = self._DoReadFileDtb(dts, entry_args=entry_args, + use_expanded=use_expanded)[0] # The image should contain the symbols from u_boot_binman_syms.c # Note that image_pos is adjusted by the base address of the image, # which is 0x10 in our test image @@ -1377,7 +1384,7 @@ class TestFunctional(unittest.TestCase): def testSymbolsNoDtb(self): """Test binman can assign symbols embedded in U-Boot SPL""" - self.checkSymbols('192_symbols_nodtb.dts', + self.checkSymbols('196_symbols_nodtb.dts', U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA, 0x38) @@ -1711,7 +1718,7 @@ class TestFunctional(unittest.TestCase): """Test we detect a vblock which has no content to sign""" with self.assertRaises(ValueError) as e: self._DoReadFile('075_vblock_no_content.dts') - self.assertIn("Node '/binman/vblock': Vblock must have a 'content' " + self.assertIn("Node '/binman/vblock': Collection must have a 'content' " 'property', str(e.exception)) def testVblockBadPhandle(self): @@ -4460,5 +4467,47 @@ class TestFunctional(unittest.TestCase): start += fdt_size + len(U_BOOT_TPL_DATA) self.assertEqual(len(data), start) + def testSymbolsExpanded(self): + """Test binman can assign symbols in expanded entries""" + entry_args = { + 'spl-dtb': '1', + } + self.checkSymbols('197_symbols_expand.dts', U_BOOT_SPL_NODTB_DATA + + U_BOOT_SPL_DTB_DATA, 0x38, + entry_args=entry_args, use_expanded=True) + + def testCollection(self): + """Test a collection""" + data = self._DoReadFile('198_collection.dts') + self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA + + tools.GetBytes(0xff, 2) + U_BOOT_NODTB_DATA + + tools.GetBytes(0xfe, 3) + U_BOOT_DTB_DATA, + data) + + def testCollectionSection(self): + """Test a collection where a section must be built first""" + # Sections never have their contents when GetData() is called, but when + # _BuildSectionData() is called with required=True, a section will force + # building the contents, producing an error is anything is still + # missing. + data = self._DoReadFile('199_collection_section.dts') + section = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA + self.assertEqual(section + U_BOOT_DATA + tools.GetBytes(0xff, 2) + + section + tools.GetBytes(0xfe, 3) + U_BOOT_DATA, + data) + + def testAlignDefault(self): + """Test that default alignment works on sections""" + data = self._DoReadFile('200_align_default.dts') + expected = (U_BOOT_DATA + tools.GetBytes(0, 8 - len(U_BOOT_DATA)) + + U_BOOT_DATA) + # Special alignment for section + expected += tools.GetBytes(0, 32 - len(expected)) + # No alignment within the nested section + expected += U_BOOT_DATA + U_BOOT_NODTB_DATA; + # Now the final piece, which should be default-aligned + expected += tools.GetBytes(0, 88 - len(expected)) + U_BOOT_NODTB_DATA + self.assertEqual(expected, data) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/192_symbols_nodtb.dts b/tools/binman/test/196_symbols_nodtb.dts index 5c900d60709..5c900d60709 100644 --- a/tools/binman/test/192_symbols_nodtb.dts +++ b/tools/binman/test/196_symbols_nodtb.dts diff --git a/tools/binman/test/197_symbols_expand.dts b/tools/binman/test/197_symbols_expand.dts new file mode 100644 index 00000000000..8aee76dc755 --- /dev/null +++ b/tools/binman/test/197_symbols_expand.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pad-byte = <0xff>; + u-boot-spl { + }; + + u-boot { + offset = <0x38>; + no-expanded; + }; + + u-boot-spl2 { + type = "u-boot-spl"; + }; + }; +}; diff --git a/tools/binman/test/198_collection.dts b/tools/binman/test/198_collection.dts new file mode 100644 index 00000000000..484a1b0050d --- /dev/null +++ b/tools/binman/test/198_collection.dts @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + collection { + content = <&u_boot_nodtb &dtb>; + }; + fill { + size = <2>; + fill-byte = [ff]; + }; + u_boot_nodtb: u-boot-nodtb { + }; + fill2 { + type = "fill"; + size = <3>; + fill-byte = [fe]; + }; + dtb: u-boot-dtb { + }; + }; +}; diff --git a/tools/binman/test/199_collection_section.dts b/tools/binman/test/199_collection_section.dts new file mode 100644 index 00000000000..03a73194c3f --- /dev/null +++ b/tools/binman/test/199_collection_section.dts @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + collection { + content = <§ion &u_boot>; + }; + fill { + size = <2>; + fill-byte = [ff]; + }; + section: section { + u-boot-nodtb { + }; + u-boot-dtb { + }; + }; + fill2 { + type = "fill"; + size = <3>; + fill-byte = [fe]; + }; + u_boot: u-boot { + no-expanded; + }; + }; +}; diff --git a/tools/binman/test/200_align_default.dts b/tools/binman/test/200_align_default.dts new file mode 100644 index 00000000000..1b155770d4c --- /dev/null +++ b/tools/binman/test/200_align_default.dts @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + align-default = <8>; + u-boot { + }; + + u-boot-align { + type = "u-boot"; + }; + + section { + align = <32>; + u-boot { + }; + + u-boot-nodtb { + }; + }; + + u-boot-nodtb { + }; + }; +}; diff --git a/tools/dtoc/fdt.py b/tools/dtoc/fdt.py index 25ce5136ebf..3996971e39c 100644 --- a/tools/dtoc/fdt.py +++ b/tools/dtoc/fdt.py @@ -103,6 +103,8 @@ class Prop: """A device tree property Properties: + node: Node containing this property + offset: Offset of the property (None if still to be synced) name: Property name (as per the device tree) value: Property value as a string of bytes, or a list of strings of bytes @@ -114,7 +116,7 @@ class Prop: self.name = name self.value = None self.bytes = bytes(data) - self.dirty = False + self.dirty = offset is None if not data: self.type = Type.BOOL self.value = True @@ -228,9 +230,14 @@ class Prop: Raises: FdtException if auto_resize is False and there is not enough space """ - if self._offset is None or self.dirty: + if self.dirty: node = self._node fdt_obj = node._fdt._fdt_obj + node_name = fdt_obj.get_name(node._offset) + if node_name and node_name != node.name: + raise ValueError("Internal error, node '%s' name mismatch '%s'" % + (node.path, node_name)) + if auto_resize: while fdt_obj.setprop(node.Offset(), self.name, self.bytes, (libfdt.NOSPACE,)) == -libfdt.NOSPACE: @@ -239,13 +246,15 @@ class Prop: fdt_obj.setprop(node.Offset(), self.name, self.bytes) else: fdt_obj.setprop(node.Offset(), self.name, self.bytes) + self.dirty = False class Node: """A device tree node Properties: - offset: Integer offset in the device tree + parent: Parent Node + offset: Integer offset in the device tree (None if to be synced) name: Device tree node tname path: Full path to node, along with the node name itself _fdt: Device tree object @@ -324,6 +333,11 @@ class Node: fdt_obj = self._fdt._fdt_obj if self._offset != my_offset: self._offset = my_offset + name = fdt_obj.get_name(self._offset) + if name and self.name != name: + raise ValueError("Internal error, node '%s' name mismatch '%s'" % + (self.path, name)) + offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND) for subnode in self.subnodes: if subnode.name != fdt_obj.get_name(offset): @@ -339,8 +353,8 @@ class Node: p = fdt_obj.get_property_by_offset(poffset) prop = self.props.get(p.name) if not prop: - raise ValueError("Internal error, property '%s' missing, " - 'offset %d' % (p.name, poffset)) + raise ValueError("Internal error, node '%s' property '%s' missing, " + 'offset %d' % (self.path, p.name, poffset)) prop.RefreshOffset(poffset) poffset = fdt_obj.next_property_offset(poffset, QUIET_NOTFOUND) @@ -447,8 +461,13 @@ class Node: Args: prop_name: Name of property to add val: Bytes value of property + + Returns: + Prop added """ - self.props[prop_name] = Prop(self, None, prop_name, val) + prop = Prop(self, None, prop_name, val) + self.props[prop_name] = prop + return prop def AddString(self, prop_name, val): """Add a new string property to a node @@ -459,9 +478,12 @@ class Node: Args: prop_name: Name of property to add val: String value of property + + Returns: + Prop added """ val = bytes(val, 'utf-8') - self.AddData(prop_name, val + b'\0') + return self.AddData(prop_name, val + b'\0') def AddInt(self, prop_name, val): """Add a new integer property to a node @@ -472,8 +494,11 @@ class Node: Args: prop_name: Name of property to add val: Integer value of property + + Returns: + Prop added """ - self.AddData(prop_name, struct.pack('>I', val)) + return self.AddData(prop_name, struct.pack('>I', val)) def AddSubnode(self, name): """Add a new subnode to the node @@ -499,9 +524,13 @@ class Node: auto_resize: Resize the device tree automatically if it does not have enough space for the update + Returns: + True if the node had to be added, False if it already existed + Raises: FdtException if auto_resize is False and there is not enough space """ + added = False if self._offset is None: # The subnode doesn't exist yet, so add it fdt_obj = self._fdt._fdt_obj @@ -515,23 +544,45 @@ class Node: else: offset = fdt_obj.add_subnode(self.parent._offset, self.name) self._offset = offset + added = True - # Sync subnodes in reverse so that we don't disturb node offsets for - # nodes that are earlier in the DT. This avoids an O(n^2) rescan of - # node offsets. + # Sync the existing subnodes first, so that we can rely on the offsets + # being correct. As soon as we add new subnodes, it pushes all the + # existing subnodes up. for node in reversed(self.subnodes): - node.Sync(auto_resize) + if node._offset is not None: + node.Sync(auto_resize) - # Sync properties now, whose offsets should not have been disturbed. - # We do this after subnodes, since this disturbs the offsets of these - # properties. Note that new properties will have an offset of None here, - # which Python 3 cannot sort against int. So use a large value instead - # to ensure that the new properties are added first. + # Sync subnodes in reverse so that we get the expected order. Each + # new node goes at the start of the subnode list. This avoids an O(n^2) + # rescan of node offsets. + num_added = 0 + for node in reversed(self.subnodes): + if node.Sync(auto_resize): + num_added += 1 + if num_added: + # Reorder our list of nodes to put the new ones first, since that's + # what libfdt does + old_count = len(self.subnodes) - num_added + subnodes = self.subnodes[old_count:] + self.subnodes[:old_count] + self.subnodes = subnodes + + # Sync properties now, whose offsets should not have been disturbed, + # since properties come before subnodes. This is done after all the + # subnode processing above, since updating properties can disturb the + # offsets of those subnodes. + # Properties are synced in reverse order, with new properties added + # before existing properties are synced. This ensures that the offsets + # of earlier properties are not disturbed. + # Note that new properties will have an offset of None here, which + # Python cannot sort against int. So use a large value instead so that + # new properties are added first. prop_list = sorted(self.props.values(), key=lambda prop: prop._offset or 1 << 31, reverse=True) for prop in prop_list: prop.Sync(auto_resize) + return added class Fdt: @@ -642,8 +693,9 @@ class Fdt: Raises: FdtException if auto_resize is False and there is not enough space """ + self.CheckCache() self._root.Sync(auto_resize) - self.Invalidate() + self.Refresh() def Pack(self): """Pack the device tree down to its minimum size @@ -652,7 +704,7 @@ class Fdt: build up in the device tree binary. """ CheckErr(self._fdt_obj.pack(), 'pack') - self.Invalidate() + self.Refresh() def GetContents(self): """Get the contents of the FDT @@ -704,11 +756,11 @@ class Fdt: if self._cached_offsets: return self.Refresh() - self._cached_offsets = True def Refresh(self): """Refresh the offset cache""" self._root.Refresh(0) + self._cached_offsets = True def GetStructOffset(self, offset): """Get the file offset of a given struct offset diff --git a/tools/dtoc/test/dtoc_test_simple.dts b/tools/dtoc/test/dtoc_test_simple.dts index d8ab8613ee3..b5c1274bb7c 100644 --- a/tools/dtoc/test/dtoc_test_simple.dts +++ b/tools/dtoc/test/dtoc_test_simple.dts @@ -56,4 +56,8 @@ low-power; }; }; + + orig-node { + orig = <1 23 4>; + }; }; diff --git a/tools/dtoc/test_fdt.py b/tools/dtoc/test_fdt.py index 1c3a8a2ab1e..856392b1bd9 100755 --- a/tools/dtoc/test_fdt.py +++ b/tools/dtoc/test_fdt.py @@ -154,6 +154,7 @@ class TestNode(unittest.TestCase): def setUp(self): self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts')) self.node = self.dtb.GetNode('/spl-test') + self.fdt = self.dtb.GetFdtObj() def testOffset(self): """Tests that we can obtain the offset of a node""" @@ -197,7 +198,7 @@ class TestNode(unittest.TestCase): def testRefreshExtraNode(self): """Test refreshing offsets when an expected node is missing""" # Delete it from the device tre, not our tables - self.dtb.GetFdtObj().del_node(self.node.Offset()) + self.fdt.del_node(self.node.Offset()) with self.assertRaises(ValueError) as e: self.dtb.Refresh() self.assertIn('Internal error, node name mismatch ' @@ -209,7 +210,7 @@ class TestNode(unittest.TestCase): del self.node.props['notstring'] with self.assertRaises(ValueError) as e: self.dtb.Refresh() - self.assertIn("Internal error, property 'notstring' missing, offset ", + self.assertIn("Internal error, node '/spl-test' property 'notstring' missing, offset ", str(e.exception)) def testLookupPhandle(self): @@ -220,6 +221,66 @@ class TestNode(unittest.TestCase): target = dtb.GetNode('/phandle-target') self.assertEqual(target, dtb.LookupPhandle(fdt32_to_cpu(prop.value))) + def testAddNodeSpace(self): + """Test adding a single node when out of space""" + self.fdt.pack() + self.node.AddSubnode('subnode') + with self.assertRaises(libfdt.FdtException) as e: + self.dtb.Sync(auto_resize=False) + self.assertIn('FDT_ERR_NOSPACE', str(e.exception)) + + self.dtb.Sync(auto_resize=True) + offset = self.fdt.path_offset('/spl-test/subnode') + self.assertTrue(offset > 0) + + def testAddNodes(self): + """Test adding various subnode and properies""" + node = self.dtb.GetNode('/i2c@0') + + # Add one more node next to the pmic one + sn1 = node.AddSubnode('node-one') + sn1.AddInt('integer-a', 12) + sn1.AddInt('integer-b', 23) + + # Sync so that everything is clean + self.dtb.Sync(auto_resize=True) + + # Add two subnodes next to pmic and node-one + sn2 = node.AddSubnode('node-two') + sn2.AddInt('integer-2a', 34) + sn2.AddInt('integer-2b', 45) + + sn3 = node.AddSubnode('node-three') + sn3.AddInt('integer-3', 123) + + # Add a property to the node after i2c@0 to check that this is not + # disturbed by adding a subnode to i2c@0 + orig_node = self.dtb.GetNode('/orig-node') + orig_node.AddInt('integer-4', 456) + + # Add a property to the pmic node to check that pmic properties are not + # disturbed + pmic = self.dtb.GetNode('/i2c@0/pmic@9') + pmic.AddInt('integer-5', 567) + + self.dtb.Sync(auto_resize=True) + + def testRefreshNameMismatch(self): + """Test name mismatch when syncing nodes and properties""" + prop = self.node.AddInt('integer-a', 12) + + wrong_offset = self.dtb.GetNode('/i2c@0')._offset + self.node._offset = wrong_offset + with self.assertRaises(ValueError) as e: + self.dtb.Sync() + self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'", + str(e.exception)) + + with self.assertRaises(ValueError) as e: + self.node.Refresh(wrong_offset) + self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'", + str(e.exception)) + class TestProp(unittest.TestCase): """Test operation of the Prop class""" @@ -385,17 +446,6 @@ class TestProp(unittest.TestCase): self.assertIn('FDT_ERR_NOSPACE', str(e.exception)) self.dtb.Sync(auto_resize=True) - def testAddNode(self): - self.fdt.pack() - self.node.AddSubnode('subnode') - with self.assertRaises(libfdt.FdtException) as e: - self.dtb.Sync(auto_resize=False) - self.assertIn('FDT_ERR_NOSPACE', str(e.exception)) - - self.dtb.Sync(auto_resize=True) - offset = self.fdt.path_offset('/spl-test/subnode') - self.assertTrue(offset > 0) - def testAddMore(self): """Test various other methods for adding and setting properties""" self.node.AddZeroProp('one') |
