diff options
author | Tom Rini <trini@konsulko.com> | 2023-07-20 21:31:31 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2023-07-20 21:31:31 -0400 |
commit | e896279ac39ebb97f23e6132bf7668a61e1cd86b (patch) | |
tree | 24e035ebd13dbd272da4b3845a1dfcac66e4a86f | |
parent | 7fe5accb4516144b7abb8f183640cdf50423121e (diff) | |
parent | 24142ead21ed5e4d2d6f39dd410d91d815ea1ae2 (diff) | |
download | u-boot-e896279ac39ebb97f23e6132bf7668a61e1cd86b.zip u-boot-e896279ac39ebb97f23e6132bf7668a61e1cd86b.tar.gz u-boot-e896279ac39ebb97f23e6132bf7668a61e1cd86b.tar.bz2 |
Merge tag 'dm-pull-20jul23' of https://source.denx.de/u-boot/custodians/u-boot-dmWIP/20Jul2023
binman mkimage and template enhancements
misc fixes
44 files changed, 1238 insertions, 153 deletions
diff --git a/arch/arm/dts/stm32mp15-u-boot.dtsi b/arch/arm/dts/stm32mp15-u-boot.dtsi index d872c6f..573dd4d 100644 --- a/arch/arm/dts/stm32mp15-u-boot.dtsi +++ b/arch/arm/dts/stm32mp15-u-boot.dtsi @@ -226,6 +226,7 @@ mkimage { args = "-T stm32image -a 0x2ffc2500 -e 0x2ffc2500"; u-boot-spl { + no-write-symbols; }; }; }; diff --git a/arch/sandbox/include/asm/sdl.h b/arch/sandbox/include/asm/sdl.h index 56dcb84..ee4991f 100644 --- a/arch/sandbox/include/asm/sdl.h +++ b/arch/sandbox/include/asm/sdl.h @@ -7,6 +7,7 @@ #define __SANDBOX_SDL_H #include <errno.h> +#include <video.h> #ifdef CONFIG_SANDBOX_SDL @@ -87,6 +88,22 @@ int sandbox_sdl_sound_stop(void); */ int sandbox_sdl_sound_init(int rate, int channels); +/** + * sandbox_sdl_set_bpp() - Set the depth of the sandbox display + * + * The device must not be active when this function is called. It activiates it + * before returning. + * + * This updates the depth value and adjusts a few other settings accordingly. + * It must be called before the display is probed. + * + * @dev: Device to adjust + * @l2bpp: depth to set + * Return: 0 if the device was already active, other error if it fails to probe + * after the change + */ +int sandbox_sdl_set_bpp(struct udevice *dev, enum video_log2_bpp l2bpp); + #else static inline int sandbox_sdl_init_display(int width, int height, int log2_bpp, bool double_size) @@ -134,6 +151,12 @@ static inline int sandbox_sdl_sound_init(int rate, int channels) return -ENODEV; } +static inline int sandbox_sdl_set_bpp(struct udevice *dev, + enum video_log2_bpp l2bpp) +{ + return -ENOSYS; +} + #endif #endif diff --git a/arch/sandbox/include/asm/test.h b/arch/sandbox/include/asm/test.h index e482271..17159f8 100644 --- a/arch/sandbox/include/asm/test.h +++ b/arch/sandbox/include/asm/test.h @@ -8,7 +8,6 @@ #ifndef __ASM_TEST_H #define __ASM_TEST_H -#include <video.h> #include <pci_ids.h> struct unit_test_state; @@ -300,30 +299,6 @@ void sandbox_cros_ec_set_test_flags(struct udevice *dev, uint flags); */ int sandbox_cros_ec_get_pwm_duty(struct udevice *dev, uint index, uint *duty); -#if IS_ENABLED(CONFIG_SANDBOX_SDL) -/** - * sandbox_sdl_set_bpp() - Set the depth of the sandbox display - * - * The device must not be active when this function is called. It activiates it - * before returning. - * - * This updates the depth value and adjusts a few other settings accordingly. - * It must be called before the display is probed. - * - * @dev: Device to adjust - * @l2bpp: depth to set - * Return: 0 if the device was already active, other error if it fails to probe - * after the change - */ -int sandbox_sdl_set_bpp(struct udevice *dev, enum video_log2_bpp l2bpp); -#else -static inline int sandbox_sdl_set_bpp(struct udevice *dev, - enum video_log2_bpp l2bpp) -{ - return -ENOSYS; -} -#endif - /** * sandbox_set_fake_efi_mgr_dev() - Control EFI bootmgr producing valid bootflow * diff --git a/boot/bootmeth_script.c b/boot/bootmeth_script.c index 225eb18..a4050c3 100644 --- a/boot/bootmeth_script.c +++ b/boot/bootmeth_script.c @@ -190,7 +190,10 @@ static int script_boot(struct udevice *dev, struct bootflow *bflow) ulong addr; int ret; - ret = env_set("devtype", blk_get_devtype(bflow->blk)); + if (desc->uclass_id == UCLASS_USB) + ret = env_set("devtype", "usb"); + else + ret = env_set("devtype", blk_get_devtype(bflow->blk)); if (!ret) ret = env_set_hex("devnum", desc->devnum); if (!ret) @@ -181,13 +181,17 @@ static ulong load_serial(long offset) } else #endif { + void *dst; + ret = lmb_reserve(&lmb, store_addr, binlen); if (ret) { printf("\nCannot overwrite reserved area (%08lx..%08lx)\n", store_addr, store_addr + binlen); return ret; } - memcpy((char *)(store_addr), binbuf, binlen); + dst = map_sysmem(store_addr, binlen); + memcpy(dst, binbuf, binlen); + unmap_sysmem(dst); lmb_free(&lmb, store_addr, binlen); } if ((store_addr) < start_addr) @@ -350,15 +354,19 @@ static int save_serial(ulong address, ulong count) if(write_record(SREC3_START)) /* write the header */ return (-1); do { - if(count) { /* collect hex data in the buffer */ - c = *(volatile uchar*)(address + reclen); /* get one byte */ - checksum += c; /* accumulate checksum */ + volatile uchar *src; + + src = map_sysmem(address, count); + if (count) { /* collect hex data in the buffer */ + c = src[reclen]; /* get one byte */ + checksum += c; /* accumulate checksum */ data[2*reclen] = hex[(c>>4)&0x0f]; data[2*reclen+1] = hex[c & 0x0f]; data[2*reclen+2] = '\0'; ++reclen; --count; } + unmap_sysmem((void *)src); if(reclen == SREC_BYTES_PER_RECORD || count == 0) { /* enough data collected for one record: dump it */ if(reclen) { /* build & write a data record: */ diff --git a/drivers/core/of_access.c b/drivers/core/of_access.c index 81a3079..57f1044 100644 --- a/drivers/core/of_access.c +++ b/drivers/core/of_access.c @@ -593,11 +593,14 @@ int of_read_u64(const struct device_node *np, const char *propname, u64 *outp) int of_property_match_string(const struct device_node *np, const char *propname, const char *string) { - const struct property *prop = of_find_property(np, propname, NULL); + int len = 0; + const struct property *prop = of_find_property(np, propname, &len); size_t l; int i; const char *p, *end; + if (!prop && len == -FDT_ERR_NOTFOUND) + return -ENOENT; if (!prop) return -EINVAL; if (!prop->value) diff --git a/drivers/core/read.c b/drivers/core/read.c index 0289a2e..5749473 100644 --- a/drivers/core/read.c +++ b/drivers/core/read.c @@ -211,10 +211,9 @@ void *dev_remap_addr(const struct udevice *dev) return dev_remap_addr_index(dev, 0); } -fdt_addr_t dev_read_addr_size(const struct udevice *dev, const char *property, - fdt_size_t *sizep) +fdt_addr_t dev_read_addr_size(const struct udevice *dev, fdt_size_t *sizep) { - return ofnode_get_addr_size(dev_ofnode(dev), property, sizep); + return dev_read_addr_size_index(dev, 0, sizep); } const char *dev_read_name(const struct udevice *dev) diff --git a/drivers/reset/reset-rockchip.c b/drivers/reset/reset-rockchip.c index 2ebe338..6cabaa1 100644 --- a/drivers/reset/reset-rockchip.c +++ b/drivers/reset/reset-rockchip.c @@ -97,7 +97,7 @@ static int rockchip_reset_probe(struct udevice *dev) fdt_addr_t addr; fdt_size_t size; - addr = dev_read_addr_size(dev, "reg", &size); + addr = dev_read_addr_size(dev, &size); if (addr == FDT_ADDR_T_NONE) return -EINVAL; diff --git a/include/dm/read.h b/include/dm/read.h index 56ac076..137f2a5 100644 --- a/include/dm/read.h +++ b/include/dm/read.h @@ -347,18 +347,13 @@ fdt_addr_t dev_read_addr_pci(const struct udevice *dev); void *dev_remap_addr(const struct udevice *dev); /** - * dev_read_addr_size() - get address and size from a device property - * - * This does no address translation. It simply reads an property that contains - * an address and a size value, one after the other. + * dev_read_addr_size() - Get the reg property of a device * * @dev: Device to read from - * @propname: property to read * @sizep: place to put size value (on success) * Return: address value, or FDT_ADDR_T_NONE on error */ -fdt_addr_t dev_read_addr_size(const struct udevice *dev, const char *propname, - fdt_size_t *sizep); +fdt_addr_t dev_read_addr_size(const struct udevice *dev, fdt_size_t *sizep); /** * dev_read_name() - get the name of a device's node @@ -1002,10 +997,9 @@ static inline void *dev_remap_addr_name(const struct udevice *dev, } static inline fdt_addr_t dev_read_addr_size(const struct udevice *dev, - const char *propname, fdt_size_t *sizep) { - return ofnode_get_addr_size(dev_ofnode(dev), propname, sizep); + return dev_read_addr_size_index(dev, 0, sizep); } static inline const char *dev_read_name(const struct udevice *dev) diff --git a/test/dm/video.c b/test/dm/video.c index 0534ee9..d907f68 100644 --- a/test/dm/video.c +++ b/test/dm/video.c @@ -15,6 +15,7 @@ #include <video.h> #include <video_console.h> #include <asm/test.h> +#include <asm/sdl.h> #include <dm/test.h> #include <dm/uclass-internal.h> #include <test/test.h> diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst index 23cbb99..8f57b6c 100644 --- a/tools/binman/binman.rst +++ b/tools/binman/binman.rst @@ -727,6 +727,13 @@ optional: Note that missing, optional blobs do not produce a non-zero exit code from binman, although it does show a warning about the missing external blob. +insert-template: + This is not strictly speaking an entry property, since it is processed early + in Binman before the entries are read. It is a list of phandles of nodes to + include in the current (target) node. For each node, its subnodes and their + properties are brought into the target node. See Templates_ below for + more information. + The attributes supported for images and sections are described below. Several are similar to those for entries. @@ -831,6 +838,13 @@ write-symbols: binman. This is automatic for certain entry types, e.g. `u-boot-spl`. See binman_syms_ for more information. +no-write-symbols: + Disables symbol writing for this entry. This can be used in entry types + where symbol writing is automatic. For example, if `u-boot-spl` refers to + the `u_boot_any_image_pos` symbol but U-Boot is not available in the image + containing SPL, this can be used to disable the writing. Quite likely this + indicates a bug in your setup. + elf-filename: Sets the file name of a blob's associated ELF file. For example, if the blob is `zephyr.bin` then the ELF file may be `zephyr.elf`. This allows @@ -1165,6 +1179,86 @@ If you are having trouble figuring out what is going on, you can use arch/arm/dts/u-boot.dtsi ... found: "arch/arm/dts/juno-r2-u-boot.dtsi" +Templates +========= + +Sometimes multiple images need to be created which have all have a common +part. For example, a board may generate SPI and eMMC images which both include +a FIT. Since the FIT includes many entries, it is tedious to repeat them twice +in the image description. + +Templates provide a simple way to handle this:: + + binman { + multiple-images; + common_part: template-1 { + some-property; + fit { + ... lots of entries in here + }; + + text { + text = "base image"; + }; + }; + + spi-image { + filename = "image-spi.bin"; + insert-template = <&fit>; + + /* things specific to SPI follow */ + footer { + ]; + + text { + text = "SPI image"; + }; + }; + + mmc-image { + filename = "image-mmc.bin"; + insert-template = <&fit>; + + /* things specific to MMC follow */ + footer { + ]; + + text { + text = "MMC image"; + }; + }; + }; + +The template node name must start with 'template', so it is not considered to be +an image itself. + +The mechanism is very simple. For each phandle in the 'insert-templates' +property, the source node is looked up. Then the subnodes of that source node +are copied into the target node, i.e. the one containing the `insert-template` +property. + +If the target node has a node with the same name as a template, its properties +override corresponding properties in the template. This allows the template to +be uses as a base, with the node providing updates to the properties as needed. +The overriding happens recursively. + +Template nodes appear first in each node that they are inserted into and +ordering of template nodes is preserved. Other nodes come afterwards. If a +template node also appears in the target node, then the template node sets the +order. Thus the template can be used to set the ordering, even if the target +node provides all the properties. In the above example, `fit` and `text` appear +first in the `spi-image` and `mmc-image` images, followed by `footer`. + +Where there are multiple template nodes, they are inserted in that order. so +the first template node appears first, then the second. + +Properties in the template node are inserted into the destination node if they +do not exist there. In the example above, `some-property` is added to each of +`spi-image` and `mmc-image`. + +Note that template nodes are not removed from the binman description at present. + + Updating an ELF file ==================== diff --git a/tools/binman/bintool.py b/tools/binman/bintool.py index 8162968..0b0f56d 100644 --- a/tools/binman/bintool.py +++ b/tools/binman/bintool.py @@ -288,7 +288,7 @@ class Bintool: name = os.path.expanduser(self.name) # Expand paths containing ~ all_args = (name,) + args env = tools.get_env_with_path() - tout.detail(f"bintool: {' '.join(all_args)}") + tout.debug(f"bintool: {' '.join(all_args)}") result = command.run_pipe( [all_args], capture=True, capture_stderr=True, env=env, raise_on_error=False, binary=binary) diff --git a/tools/binman/control.py b/tools/binman/control.py index 7e2dd35..25e6681 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -22,6 +22,7 @@ from binman import bintool from binman import cbfs_util from binman import elf from binman import entry +from dtoc import fdt_util from u_boot_pylib import command from u_boot_pylib import tools from u_boot_pylib import tout @@ -56,8 +57,9 @@ def _ReadImageDesc(binman_node, use_expanded): images = OrderedDict() if 'multiple-images' in binman_node.props: for node in binman_node.subnodes: - images[node.name] = Image(node.name, node, - use_expanded=use_expanded) + if 'template' not in node.name: + images[node.name] = Image(node.name, node, + use_expanded=use_expanded) else: images['image'] = Image('image', binman_node, use_expanded=use_expanded) return images @@ -478,6 +480,30 @@ def SignEntries(image_fname, input_fname, privatekey_fname, algo, entry_paths, AfterReplace(image, allow_resize=True, write_map=write_map) +def _ProcessTemplates(parent): + """Handle any templates in the binman description + + Args: + parent: Binman node to process (typically /binman) + + Search though each target node looking for those with an 'insert-template' + property. Use that as a list of references to template nodes to use to + adjust the target node. + + Processing involves copying each subnode of the template node into the + target node. + + This is done recursively, so templates can be at any level of the binman + image, e.g. inside a section. + + See 'Templates' in the Binman documnentation for details. + """ + for node in parent.subnodes: + tmpl = fdt_util.GetPhandleList(node, 'insert-template') + if tmpl: + node.copy_subnodes_from_phandles(tmpl) + _ProcessTemplates(node) + def PrepareImagesAndDtbs(dtb_fname, select_images, update_fdt, use_expanded): """Prepare the images to be processed and select the device tree @@ -520,6 +546,8 @@ def PrepareImagesAndDtbs(dtb_fname, select_images, update_fdt, use_expanded): raise ValueError("Device tree '%s' does not have a 'binman' " "node" % dtb_fname) + _ProcessTemplates(node) + images = _ReadImageDesc(node, use_expanded) if select_images: diff --git a/tools/binman/elf.py b/tools/binman/elf.py index 5816284..4219001 100644 --- a/tools/binman/elf.py +++ b/tools/binman/elf.py @@ -248,6 +248,9 @@ def LookupAndWriteSymbols(elf_fname, entry, section, is_elf=False, entry: Entry to process section: Section which can be used to lookup symbol values base_sym: Base symbol marking the start of the image + + Returns: + int: Number of symbols written """ if not base_sym: base_sym = '__image_copy_start' @@ -269,12 +272,13 @@ def LookupAndWriteSymbols(elf_fname, entry, section, is_elf=False, if not syms: tout.debug('LookupAndWriteSymbols: no syms') - return + return 0 base = syms.get(base_sym) if not base and not is_elf: tout.debug('LookupAndWriteSymbols: no base') - return + return 0 base_addr = 0 if is_elf else base.address + count = 0 for name, sym in syms.items(): if name.startswith('_binman'): msg = ("Section '%s': Symbol '%s'\n in entry '%s'" % @@ -307,6 +311,11 @@ def LookupAndWriteSymbols(elf_fname, entry, section, is_elf=False, (msg, name, offset, value, len(value_bytes))) entry.data = (entry.data[:offset] + value_bytes + entry.data[offset + sym.size:]) + count += 1 + if count: + tout.detail( + f"Section '{section.GetPath()}': entry '{entry.GetPath()}' : {count} symbols") + return count def GetSymbolValue(sym, data, msg): """Get the value of a symbol diff --git a/tools/binman/elf_test.py b/tools/binman/elf_test.py index c980839..cc95b42 100644 --- a/tools/binman/elf_test.py +++ b/tools/binman/elf_test.py @@ -141,7 +141,8 @@ class TestElf(unittest.TestCase): entry = FakeEntry(10) section = FakeSection() elf_fname = self.ElfTestFile('u_boot_binman_syms_bad') - elf.LookupAndWriteSymbols(elf_fname, entry, section) + count = elf.LookupAndWriteSymbols(elf_fname, entry, section) + self.assertEqual(0, count) def testBadSymbolSize(self): """Test that an attempt to use an 8-bit symbol are detected @@ -162,7 +163,7 @@ class TestElf(unittest.TestCase): def testNoValue(self): """Test the case where we have no value for the symbol - This should produce -1 values for all thress symbols, taking up the + This should produce -1 values for all three symbols, taking up the first 16 bytes of the image. """ if not elf.ELF_TOOLS: @@ -170,7 +171,8 @@ class TestElf(unittest.TestCase): entry = FakeEntry(28) section = FakeSection(sym_value=None) elf_fname = self.ElfTestFile('u_boot_binman_syms') - elf.LookupAndWriteSymbols(elf_fname, entry, section) + count = elf.LookupAndWriteSymbols(elf_fname, entry, section) + self.assertEqual(5, count) expected = (struct.pack('<L', elf.BINMAN_SYM_MAGIC_VALUE) + tools.get_bytes(255, 20) + tools.get_bytes(ord('a'), 4)) @@ -369,6 +371,11 @@ class TestElf(unittest.TestCase): elf.GetSymbolOffset(fname, 'embed') self.assertIn('__image_copy_start', str(e.exception)) + def test_get_symbol_address(self): + fname = self.ElfTestFile('embed_data') + addr = elf.GetSymbolAddress(fname, 'region_size') + self.assertEqual(0, addr) + if __name__ == '__main__': unittest.main() diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index b71af80..b55f424 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -615,6 +615,12 @@ The top-level 'fit' node supports the following special properties: `of-list` meaning that `-a of-list="dtb1 dtb2..."` should be passed to binman. + fit,fdt-list-val + As an alternative to fit,fdt-list the list of device tree files + can be provided in this property as a string list, e.g.:: + + fit,fdt-list-val = "dtb1", "dtb2"; + Substitutions ~~~~~~~~~~~~~ diff --git a/tools/binman/entry.py b/tools/binman/entry.py index 3945690..42e0b7b 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -158,6 +158,7 @@ class Entry(object): self.offset_from_elf = None self.preserve = False self.build_done = False + self.no_write_symbols = False @staticmethod def FindEntryClass(etype, expanded): @@ -321,6 +322,7 @@ class Entry(object): 'offset-from-elf') self.preserve = fdt_util.GetBool(self._node, 'preserve') + self.no_write_symbols = fdt_util.GetBool(self._node, 'no-write-symbols') def GetDefaultFilename(self): return None @@ -472,6 +474,9 @@ class Entry(object): def ObtainContents(self, skip_entry=None, fake_size=0): """Figure out the contents of an entry. + For missing blobs (where allow-missing is enabled), the contents are set + to b'' and self.missing is set to True. + Args: skip_entry (Entry): Entry to skip when obtaining section contents fake_size (int): Size of fake file to create if needed @@ -695,7 +700,7 @@ class Entry(object): Args: section: Section containing the entry """ - if self.auto_write_symbols: + if self.auto_write_symbols and not self.no_write_symbols: # Check if we are writing symbols into an ELF file is_elf = self.GetDefaultFilename() == self.elf_fname elf.LookupAndWriteSymbols(self.elf_fname, self, section.GetImage(), @@ -1309,10 +1314,6 @@ features to produce new behaviours. """ data = b'' for entry in entries: - # First get the input data and put it in a file. If not available, - # try later. - if not entry.ObtainContents(fake_size=fake_size): - return None, None, None data += entry.GetData() uniq = self.GetUniqueName() fname = tools.get_output_filename(f'{prefix}.{uniq}') diff --git a/tools/binman/etype/blob_phase.py b/tools/binman/etype/blob_phase.py index b937158..951d993 100644 --- a/tools/binman/etype/blob_phase.py +++ b/tools/binman/etype/blob_phase.py @@ -52,3 +52,8 @@ class Entry_blob_phase(Entry_section): # Read entries again, now that we have some self.ReadEntries() + + # Propagate the no-write-symbols property + if self.no_write_symbols: + for entry in self._entries.values(): + entry.no_write_symbols = True diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py index c395706..ef4d066 100644 --- a/tools/binman/etype/fit.py +++ b/tools/binman/etype/fit.py @@ -81,6 +81,12 @@ class Entry_fit(Entry_section): `of-list` meaning that `-a of-list="dtb1 dtb2..."` should be passed to binman. + fit,fdt-list-val + As an alternative to fit,fdt-list the list of device tree files + can be provided in this property as a string list, e.g.:: + + fit,fdt-list-val = "dtb1", "dtb2"; + Substitutions ~~~~~~~~~~~~~ @@ -361,6 +367,9 @@ class Entry_fit(Entry_section): [EntryArg(self._fit_list_prop.value, str)]) if fdts is not None: self._fdts = fdts.split() + else: + self._fdts = fdt_util.GetStringList(self._node, 'fit,fdt-list-val') + self._fit_default_dt = self.GetEntryArgsOrProps([EntryArg('default-dt', str)])[0] diff --git a/tools/binman/etype/mkimage.py b/tools/binman/etype/mkimage.py index e028c44..6ae5d0c 100644 --- a/tools/binman/etype/mkimage.py +++ b/tools/binman/etype/mkimage.py @@ -8,10 +8,11 @@ from collections import OrderedDict from binman.entry import Entry +from binman.etype.section import Entry_section from dtoc import fdt_util from u_boot_pylib import tools -class Entry_mkimage(Entry): +class Entry_mkimage(Entry_section): """Binary produced by mkimage Properties / Entry arguments: @@ -121,54 +122,67 @@ class Entry_mkimage(Entry): """ def __init__(self, section, etype, node): super().__init__(section, etype, node) - self._multiple_data_files = fdt_util.GetBool(self._node, 'multiple-data-files') - self._mkimage_entries = OrderedDict() self._imagename = None - self._filename = fdt_util.GetString(self._node, 'filename') - self.align_default = None + self._multiple_data_files = False def ReadNode(self): super().ReadNode() + self._multiple_data_files = fdt_util.GetBool(self._node, + 'multiple-data-files') self._args = fdt_util.GetArgs(self._node, 'args') self._data_to_imagename = fdt_util.GetBool(self._node, 'data-to-imagename') if self._data_to_imagename and self._node.FindNode('imagename'): self.Raise('Cannot use both imagename node and data-to-imagename') - self.ReadEntries() def ReadEntries(self): """Read the subnodes to find out what should go in this image""" for node in self._node.subnodes: - entry = Entry.Create(self, node) + if self.IsSpecialSubnode(node): + continue + entry = Entry.Create(self, node, + expanded=self.GetImage().use_expanded, + missing_etype=self.GetImage().missing_etype) entry.ReadNode() + entry.SetPrefix(self._name_prefix) if entry.name == 'imagename': self._imagename = entry else: - self._mkimage_entries[entry.name] = entry + self._entries[entry.name] = entry - def ObtainContents(self): + def BuildSectionData(self, required): + """Build mkimage entry contents + + Runs mkimage to build the entry contents + + Args: + required (bool): True if the data must be present, False if it is OK + to return None + + Returns: + bytes: Contents of the section + """ # Use a non-zero size for any fake files to keep mkimage happy # Note that testMkimageImagename() relies on this 'mkimage' parameter fake_size = 1024 if self._multiple_data_files: fnames = [] uniq = self.GetUniqueName() - for entry in self._mkimage_entries.values(): - if not entry.ObtainContents(fake_size=fake_size): - return False - if entry._pathname: - fnames.append(entry._pathname) + for entry in self._entries.values(): + # Put the contents in a temporary file + ename = f'mkimage-in-{uniq}-{entry.name}' + fname = tools.get_output_filename(ename) + data = entry.GetData(required) + tools.write_file(fname, data) + fnames.append(fname) input_fname = ":".join(fnames) + data = b'' else: data, input_fname, uniq = self.collect_contents_to_file( - self._mkimage_entries.values(), 'mkimage', fake_size) - if data is None: - return False + self._entries.values(), 'mkimage', fake_size) if self._imagename: image_data, imagename_fname, _ = self.collect_contents_to_file( [self._imagename], 'mkimage-n', 1024) - if image_data is None: - return False outfile = self._filename if self._filename else 'mkimage-out.%s' % uniq output_fname = tools.get_output_filename(outfile) @@ -176,8 +190,7 @@ class Entry_mkimage(Entry): self.CheckMissing(missing_list) self.missing = bool(missing_list) if self.missing: - self.SetContents(b'') - return self.allow_missing + return b'' args = ['-d', input_fname] if self._data_to_imagename: @@ -186,71 +199,58 @@ class Entry_mkimage(Entry): args += ['-n', imagename_fname] args += self._args + [output_fname] if self.mkimage.run_cmd(*args) is not None: - self.SetContents(tools.read_file(output_fname)) + return tools.read_file(output_fname) else: # Bintool is missing; just use the input data as the output self.record_missing_bintool(self.mkimage) - self.SetContents(data) - - return True + return data def GetEntries(self): # Make a copy so we don't change the original - entries = OrderedDict(self._mkimage_entries) + entries = OrderedDict(self._entries) if self._imagename: entries['imagename'] = self._imagename return entries - def SetAllowMissing(self, allow_missing): - """Set whether a section allows missing external blobs + def AddBintools(self, btools): + super().AddBintools(btools) + self.mkimage = self.AddBintool(btools, 'mkimage') - Args: - allow_missing: True if allowed, False if not allowed - """ - self.allow_missing = allow_missing - for entry in self._mkimage_entries.values(): - entry.SetAllowMissing(allow_missing) - if self._imagename: - self._imagename.SetAllowMissing(allow_missing) + def CheckEntries(self): + pass - def SetAllowFakeBlob(self, allow_fake): - """Set whether the sub nodes allows to create a fake blob + def ProcessContents(self): + # The blob may have changed due to WriteSymbols() + ok = super().ProcessContents() + data = self.BuildSectionData(True) + ok2 = self.ProcessContentsUpdate(data) + return ok and ok2 - Args: - allow_fake: True if allowed, False if not allowed - """ - for entry in self._mkimage_entries.values(): - entry.SetAllowFakeBlob(allow_fake) - if self._imagename: - self._imagename.SetAllowFakeBlob(allow_fake) + def SetImagePos(self, image_pos): + """Set the position in the image - def CheckMissing(self, missing_list): - """Check if any entries in this section have missing external blobs + This sets each subentry's offsets, sizes and positions-in-image + according to where they ended up in the packed mkimage file. - If there are missing (non-optional) blobs, the entries are added to the - list + NOTE: This assumes a legacy mkimage and assumes that the images are + written to the output in order. SoC-specific mkimage handling may not + conform to this, in which case these values may be wrong. Args: - missing_list: List of Entry objects to be added to + image_pos (int): Position of this entry in the image """ - for entry in self._mkimage_entries.values(): - entry.CheckMissing(missing_list) - if self._imagename: - self._imagename.CheckMissing(missing_list) + # The mkimage header consists of 0x40 bytes, following by a table of + # offsets for each file + upto = 0x40 - def CheckFakedBlobs(self, faked_blobs_list): - """Check if any entries in this section have faked external blobs + # Skip the 0-terminated list of offsets (assume a single image) + upto += 4 + 4 + for entry in self.GetEntries().values(): + entry.SetOffsetSize(upto, None) - If there are faked blobs, the entries are added to the list + # Give up if any entries lack a size + if entry.size is None: + return + upto += entry.size - Args: - faked_blobs_list: List of Entry objects to be added to - """ - for entry in self._mkimage_entries.values(): - entry.CheckFakedBlobs(faked_blobs_list) - if self._imagename: - self._imagename.CheckFakedBlobs(faked_blobs_list) - - def AddBintools(self, btools): - super().AddBintools(btools) - self.mkimage = self.AddBintool(btools, 'mkimage') + super().SetImagePos(image_pos) diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py index c36edd1..7c4d312 100644 --- a/tools/binman/etype/section.py +++ b/tools/binman/etype/section.py @@ -168,6 +168,7 @@ class Entry_section(Entry): self._end_4gb = False self._ignore_missing = False self._filename = None + self.align_default = 0 def IsSpecialSubnode(self, node): """Check if a node is a special one used by the section itself @@ -178,7 +179,8 @@ class Entry_section(Entry): Returns: bool: True if the node is a special one, else False """ - return node.name.startswith('hash') or node.name.startswith('signature') + start_list = ('hash', 'signature', 'template') + return any(node.name.startswith(name) for name in start_list) def ReadNode(self): """Read properties from the section node""" @@ -315,12 +317,15 @@ class Entry_section(Entry): This should be overridden by subclasses which want to build their own data structure for the section. + Missing entries will have be given empty (or fake) data, so are + processed normally here. + Args: required: True if the data must be present, False if it is OK to return None Returns: - Contents of the section (bytes) + Contents of the section (bytes), None if not available """ section_data = bytearray() @@ -710,6 +715,33 @@ class Entry_section(Entry): def GetEntryContents(self, skip_entry=None): """Call ObtainContents() for each entry in the section + The overall goal of this function is to read in any available data in + this entry and any subentries. This includes reading in blobs, setting + up objects which have predefined contents, etc. + + Since entry types which contain entries call ObtainContents() on all + those entries too, the result is that ObtainContents() is called + recursively for the whole tree below this one. + + Entries with subentries are generally not *themselves& processed here, + i.e. their ObtainContents() implementation simply obtains contents of + their subentries, skipping their own contents. For example, the + implementation here (for entry_Section) does not attempt to pack the + entries into a final result. That is handled later. + + Generally, calling this results in SetContents() being called for each + entry, so that the 'data' and 'contents_size; properties are set, and + subsequent calls to GetData() will return value data. + + Where 'allow_missing' is set, this can result in the 'missing' property + being set to True if there is no data. This is handled by setting the + data to b''. This function will still return success. Future calls to + GetData() for this entry will return b'', or in the case where the data + is faked, GetData() will return that fake data. + + Args: + skip_entry: (single) Entry to skip, or None to process all entries + Note that this may set entry.absent to True if the entry is not actually needed """ @@ -719,7 +751,7 @@ class Entry_section(Entry): next_todo.append(entry) return entry - todo = self._entries.values() + todo = self.GetEntries().values() for passnum in range(3): threads = state.GetThreads() next_todo = [] @@ -892,7 +924,7 @@ class Entry_section(Entry): allow_missing: True if allowed, False if not allowed """ self.allow_missing = allow_missing - for entry in self._entries.values(): + for entry in self.GetEntries().values(): entry.SetAllowMissing(allow_missing) def SetAllowFakeBlob(self, allow_fake): @@ -902,7 +934,7 @@ class Entry_section(Entry): allow_fake: True if allowed, False if not allowed """ super().SetAllowFakeBlob(allow_fake) - for entry in self._entries.values(): + for entry in self.GetEntries().values(): entry.SetAllowFakeBlob(allow_fake) def CheckMissing(self, missing_list): @@ -914,7 +946,7 @@ class Entry_section(Entry): Args: missing_list: List of Entry objects to be added to """ - for entry in self._entries.values(): + for entry in self.GetEntries().values(): entry.CheckMissing(missing_list) def CheckFakedBlobs(self, faked_blobs_list): @@ -925,7 +957,7 @@ class Entry_section(Entry): Args: faked_blobs_list: List of Entry objects to be added to """ - for entry in self._entries.values(): + for entry in self.GetEntries().values(): entry.CheckFakedBlobs(faked_blobs_list) def CheckOptional(self, optional_list): @@ -936,7 +968,7 @@ class Entry_section(Entry): Args: optional_list (list): List of Entry objects to be added to """ - for entry in self._entries.values(): + for entry in self.GetEntries().values(): entry.CheckOptional(optional_list) def check_missing_bintools(self, missing_list): @@ -948,7 +980,7 @@ class Entry_section(Entry): missing_list: List of Bintool objects to be added to """ super().check_missing_bintools(missing_list) - for entry in self._entries.values(): + for entry in self.GetEntries().values(): entry.check_missing_bintools(missing_list) def _CollectEntries(self, entries, entries_by_name, add_entry): @@ -998,12 +1030,12 @@ class Entry_section(Entry): entry.Raise(f'Missing required properties/entry args: {missing}') def CheckAltFormats(self, alt_formats): - for entry in self._entries.values(): + for entry in self.GetEntries().values(): entry.CheckAltFormats(alt_formats) def AddBintools(self, btools): super().AddBintools(btools) - for entry in self._entries.values(): + for entry in self.GetEntries().values(): entry.AddBintools(btools) def read_elf_segments(self): diff --git a/tools/binman/etype/u_boot_spl_bss_pad.py b/tools/binman/etype/u_boot_spl_bss_pad.py index 1ffeb39..4af4045 100644 --- a/tools/binman/etype/u_boot_spl_bss_pad.py +++ b/tools/binman/etype/u_boot_spl_bss_pad.py @@ -38,7 +38,7 @@ class Entry_u_boot_spl_bss_pad(Entry_blob): def ObtainContents(self): fname = tools.get_input_filename('spl/u-boot-spl') bss_size = elf.GetSymbolAddress(fname, '__bss_size') - if not bss_size: + if bss_size is None: self.Raise('Expected __bss_size symbol in spl/u-boot-spl') self.SetContents(tools.get_bytes(0, bss_size)) return True diff --git a/tools/binman/etype/u_boot_tpl_bss_pad.py b/tools/binman/etype/u_boot_tpl_bss_pad.py index 29c6a95..46d2cd5 100644 --- a/tools/binman/etype/u_boot_tpl_bss_pad.py +++ b/tools/binman/etype/u_boot_tpl_bss_pad.py @@ -38,7 +38,7 @@ class Entry_u_boot_tpl_bss_pad(Entry_blob): def ObtainContents(self): fname = tools.get_input_filename('tpl/u-boot-tpl') bss_size = elf.GetSymbolAddress(fname, '__bss_size') - if not bss_size: + if bss_size is None: self.Raise('Expected __bss_size symbol in tpl/u-boot-tpl') self.SetContents(tools.get_bytes(0, bss_size)) return True diff --git a/tools/binman/etype/u_boot_vpl_bss_pad.py b/tools/binman/etype/u_boot_vpl_bss_pad.py index bba38cc..12b286a 100644 --- a/tools/binman/etype/u_boot_vpl_bss_pad.py +++ b/tools/binman/etype/u_boot_vpl_bss_pad.py @@ -38,7 +38,7 @@ class Entry_u_boot_vpl_bss_pad(Entry_blob): def ObtainContents(self): fname = tools.get_input_filename('vpl/u-boot-vpl') bss_size = elf.GetSymbolAddress(fname, '__bss_size') - if not bss_size: + if bss_size is None: self.Raise('Expected __bss_size symbol in vpl/u-boot-vpl') self.SetContents(tools.get_bytes(0, bss_size)) return True diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 43b4f85..e53181a 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -1103,6 +1103,7 @@ class TestFunctional(unittest.TestCase): def testPackZeroOffset(self): """Test that an entry at offset 0 is not given a new offset""" + self._SetupSplElf() with self.assertRaises(ValueError) as e: self._DoTestFile('025_pack_zero_size.dts') self.assertIn("Node '/binman/u-boot-spl': Offset 0x0 (0) overlaps " @@ -1116,6 +1117,7 @@ class TestFunctional(unittest.TestCase): def testPackX86RomNoSize(self): """Test that the end-at-4gb property requires a size property""" + self._SetupSplElf() with self.assertRaises(ValueError) as e: self._DoTestFile('027_pack_4gb_no_size.dts') self.assertIn("Image '/binman': Section size must be provided when " @@ -1124,6 +1126,7 @@ class TestFunctional(unittest.TestCase): def test4gbAndSkipAtStartTogether(self): """Test that the end-at-4gb and skip-at-size property can't be used together""" + self._SetupSplElf() with self.assertRaises(ValueError) as e: self._DoTestFile('098_4gb_and_skip_at_start_together.dts') self.assertIn("Image '/binman': Provide either 'end-at-4gb' or " @@ -1131,6 +1134,7 @@ class TestFunctional(unittest.TestCase): def testPackX86RomOutside(self): """Test that the end-at-4gb property checks for offset boundaries""" + self._SetupSplElf() with self.assertRaises(ValueError) as e: self._DoTestFile('028_pack_4gb_outside.dts') self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) size 0x4 (4) " @@ -1423,6 +1427,7 @@ class TestFunctional(unittest.TestCase): def testPackUbootSplMicrocode(self): """Test that x86 microcode can be handled correctly in SPL""" + self._SetupSplElf() self._PackUbootSplMicrocode('049_x86_ucode_spl.dts') def testPackUbootSplMicrocodeReorder(self): @@ -1442,6 +1447,7 @@ class TestFunctional(unittest.TestCase): def testSplDtb(self): """Test that an image with spl/u-boot-spl.dtb can be created""" + self._SetupSplElf() data = self._DoReadFile('051_u_boot_spl_dtb.dts') self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)]) @@ -1452,7 +1458,7 @@ class TestFunctional(unittest.TestCase): self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)]) def checkSymbols(self, dts, base_data, u_boot_offset, entry_args=None, - use_expanded=False): + use_expanded=False, no_write_symbols=False): """Check the image contains the expected symbol values Args: @@ -1481,9 +1487,14 @@ class TestFunctional(unittest.TestCase): sym_values = struct.pack('<LLQLL', elf.BINMAN_SYM_MAGIC_VALUE, 0x00, u_boot_offset + len(U_BOOT_DATA), 0x10 + u_boot_offset, 0x04) - expected = (sym_values + base_data[24:] + - tools.get_bytes(0xff, 1) + U_BOOT_DATA + sym_values + - base_data[24:]) + if no_write_symbols: + expected = (base_data + + tools.get_bytes(0xff, 0x38 - len(base_data)) + + U_BOOT_DATA + base_data) + else: + expected = (sym_values + base_data[24:] + + tools.get_bytes(0xff, 1) + U_BOOT_DATA + sym_values + + base_data[24:]) self.assertEqual(expected, data) def testSymbols(self): @@ -1957,6 +1968,8 @@ class TestFunctional(unittest.TestCase): def testUpdateFdtAll(self): """Test that all device trees are updated with offset/size info""" + self._SetupSplElf() + self._SetupTplElf() data = self._DoReadFileRealDtb('082_fdt_update_all.dts') base_expected = { @@ -3279,6 +3292,8 @@ class TestFunctional(unittest.TestCase): def testUpdateFdtAllRepack(self): """Test that all device trees are updated with offset/size info""" + self._SetupSplElf() + self._SetupTplElf() data = self._DoReadFileRealDtb('134_fdt_update_all_repack.dts') SECTION_SIZE = 0x300 DTB_SIZE = 602 @@ -3732,6 +3747,7 @@ class TestFunctional(unittest.TestCase): def testMkimage(self): """Test using mkimage to build an image""" + self._SetupSplElf() data = self._DoReadFile('156_mkimage.dts') # Just check that the data appears in the file somewhere @@ -3739,6 +3755,7 @@ class TestFunctional(unittest.TestCase): def testMkimageMissing(self): """Test that binman still produces an image if mkimage is missing""" + self._SetupSplElf() with test_util.capture_sys_output() as (_, stderr): self._DoTestFile('156_mkimage.dts', force_missing_bintools='mkimage') @@ -3851,6 +3868,7 @@ class TestFunctional(unittest.TestCase): def testSimpleFit(self): """Test an image with a FIT inside""" + self._SetupSplElf() data = self._DoReadFile('161_fit.dts') self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)]) self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):]) @@ -5370,6 +5388,7 @@ fdt fdtmap Extract the devicetree blob from the fdtmap def testFitSubentryHashSubnode(self): """Test an image with a FIT inside""" + self._SetupSplElf() data, _, _, out_dtb_name = self._DoReadFileDtb( '221_fit_subentry_hash.dts', use_real_dtb=True, update_dtb=True) @@ -5888,6 +5907,7 @@ fdt fdtmap Extract the devicetree blob from the fdtmap def testMkimageImagename(self): """Test using mkimage with -n holding the data too""" + self._SetupSplElf() data = self._DoReadFile('242_mkimage_name.dts') # Check that the data appears in the file somewhere @@ -5905,6 +5925,7 @@ fdt fdtmap Extract the devicetree blob from the fdtmap def testMkimageImage(self): """Test using mkimage with -n holding the data too""" + self._SetupSplElf() data = self._DoReadFile('243_mkimage_image.dts') # Check that the data appears in the file somewhere @@ -5925,6 +5946,7 @@ fdt fdtmap Extract the devicetree blob from the fdtmap def testMkimageImageNoContent(self): """Test using mkimage with -n and no data""" + self._SetupSplElf() with self.assertRaises(ValueError) as exc: self._DoReadFile('244_mkimage_image_no_content.dts') self.assertIn('Could not complete processing of contents', @@ -5932,6 +5954,7 @@ fdt fdtmap Extract the devicetree blob from the fdtmap def testMkimageImageBad(self): """Test using mkimage with imagename node and data-to-imagename""" + self._SetupSplElf() with self.assertRaises(ValueError) as exc: self._DoReadFile('245_mkimage_image_bad.dts') self.assertIn('Cannot use both imagename node and data-to-imagename', @@ -5947,6 +5970,7 @@ fdt fdtmap Extract the devicetree blob from the fdtmap def testMkimageCollection(self): """Test using a collection referring to an entry in a mkimage entry""" + self._SetupSplElf() data = self._DoReadFile('247_mkimage_coll.dts') expect = U_BOOT_SPL_DATA + U_BOOT_DATA self.assertEqual(expect, data[:len(expect)]) @@ -6032,6 +6056,8 @@ fdt fdtmap Extract the devicetree blob from the fdtmap def testMkimageMultipleDataFiles(self): """Test passing multiple files to mkimage in a mkimage entry""" + self._SetupSplElf() + self._SetupTplElf() data = self._DoReadFile('252_mkimage_mult_data.dts') # Size of files are packed in their 4B big-endian format expect = struct.pack('>I', len(U_BOOT_TPL_DATA)) @@ -6046,8 +6072,42 @@ fdt fdtmap Extract the devicetree blob from the fdtmap expect += U_BOOT_SPL_DATA self.assertEqual(expect, data[-len(expect):]) + def testMkimageMultipleExpanded(self): + """Test passing multiple files to mkimage in a mkimage entry""" + self._SetupSplElf() + self._SetupTplElf() + entry_args = { + 'spl-bss-pad': 'y', + 'spl-dtb': 'y', + } + data = self._DoReadFileDtb('252_mkimage_mult_data.dts', + use_expanded=True, entry_args=entry_args)[0] + pad_len = 10 + tpl_expect = U_BOOT_TPL_DATA + spl_expect = U_BOOT_SPL_NODTB_DATA + tools.get_bytes(0, pad_len) + spl_expect += U_BOOT_SPL_DTB_DATA + + content = data[0x40:] + lens = struct.unpack('>III', content[:12]) + + # Size of files are packed in their 4B big-endian format + # Size info is always followed by a 4B zero value. + self.assertEqual(len(tpl_expect), lens[0]) + self.assertEqual(len(spl_expect), lens[1]) + self.assertEqual(0, lens[2]) + + rest = content[12:] + self.assertEqual(tpl_expect, rest[:len(tpl_expect)]) + + rest = rest[len(tpl_expect):] + align_pad = len(tpl_expect) % 4 + self.assertEqual(tools.get_bytes(0, align_pad), rest[:align_pad]) + rest = rest[align_pad:] + self.assertEqual(spl_expect, rest) + def testMkimageMultipleNoContent(self): """Test passing multiple data files to mkimage with one data file having no content""" + self._SetupSplElf() with self.assertRaises(ValueError) as exc: self._DoReadFile('253_mkimage_mult_no_content.dts') self.assertIn('Could not complete processing of contents', @@ -6055,6 +6115,7 @@ fdt fdtmap Extract the devicetree blob from the fdtmap def testMkimageFilename(self): """Test using mkimage to build a binary with a filename""" + self._SetupSplElf() retcode = self._DoTestFile('254_mkimage_filename.dts') self.assertEqual(0, retcode) fname = tools.get_output_filename('mkimage-test.bin') @@ -6529,6 +6590,7 @@ fdt fdtmap Extract the devicetree blob from the fdtmap def testReplaceFitSibling(self): """Test an image with a FIT inside where we replace its sibling""" + self._SetupSplElf() fname = TestFunctional._MakeInputFile('once', b'available once') self._DoReadFileRealDtb('277_replace_fit_sibling.dts') os.remove(fname) @@ -6603,7 +6665,7 @@ fdt fdtmap Extract the devicetree blob from the fdtmap Private key DTB """ - + self._SetupSplElf() data = self._DoReadFileRealDtb(dts) updated_fname = tools.get_output_filename('image-updated.bin') tools.write_file(updated_fname, data) @@ -6676,6 +6738,152 @@ fdt fdtmap Extract the devicetree blob from the fdtmap ['fit']) self.assertIn("Node '/fit': Missing tool: 'mkimage'", str(e.exception)) + def testSymbolNoWrite(self): + """Test disabling of symbol writing""" + self._SetupSplElf() + self.checkSymbols('282_symbols_disable.dts', U_BOOT_SPL_DATA, 0x1c, + no_write_symbols=True) + + def testSymbolNoWriteExpanded(self): + """Test disabling of symbol writing in expanded entries""" + entry_args = { + 'spl-dtb': '1', + } + self.checkSymbols('282_symbols_disable.dts', U_BOOT_SPL_NODTB_DATA + + U_BOOT_SPL_DTB_DATA, 0x38, + entry_args=entry_args, use_expanded=True, + no_write_symbols=True) + + def testMkimageSpecial(self): + """Test mkimage ignores special hash-1 node""" + data = self._DoReadFile('283_mkimage_special.dts') + + # Just check that the data appears in the file somewhere + self.assertIn(U_BOOT_DATA, data) + + def testFitFdtList(self): + """Test an image with an FIT with the fit,fdt-list-val option""" + entry_args = { + 'default-dt': 'test-fdt2', + } + data = self._DoReadFileDtb( + '284_fit_fdt_list.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)] + + def testSplEmptyBss(self): + """Test an expanded SPL with a zero-size BSS""" + # ELF file with a '__bss_size' symbol + self._SetupSplElf(src_fname='bss_data_zero') + + entry_args = { + 'spl-bss-pad': 'y', + 'spl-dtb': 'y', + } + data = self._DoReadFileDtb('285_spl_expand.dts', + use_expanded=True, entry_args=entry_args)[0] + + def testTemplate(self): + """Test using a template""" + TestFunctional._MakeInputFile('vga2.bin', b'#' + VGA_DATA) + data = self._DoReadFile('286_template.dts') + first = U_BOOT_DATA + VGA_DATA + U_BOOT_DTB_DATA + second = U_BOOT_DATA + b'#' + VGA_DATA + U_BOOT_DTB_DATA + self.assertEqual(U_BOOT_IMG_DATA + first + second, data) + + def testTemplateBlobMulti(self): + """Test using a template with 'multiple-images' enabled""" + TestFunctional._MakeInputFile('my-blob.bin', b'blob') + TestFunctional._MakeInputFile('my-blob2.bin', b'other') + retcode = self._DoTestFile('287_template_multi.dts') + + self.assertEqual(0, retcode) + image = control.images['image'] + image_fname = tools.get_output_filename('my-image.bin') + data = tools.read_file(image_fname) + self.assertEqual(b'blob@@@@other', data) + + def testTemplateFit(self): + """Test using a template in a FIT""" + fit_data = self._DoReadFile('288_template_fit.dts') + fname = os.path.join(self._indir, 'fit_data.fit') + tools.write_file(fname, fit_data) + out = tools.run('dumpimage', '-l', fname) + + def testTemplateSection(self): + """Test using a template in a section (not at top level)""" + TestFunctional._MakeInputFile('vga2.bin', b'#' + VGA_DATA) + data = self._DoReadFile('289_template_section.dts') + first = U_BOOT_DATA + VGA_DATA + U_BOOT_DTB_DATA + second = U_BOOT_DATA + b'#' + VGA_DATA + U_BOOT_DTB_DATA + self.assertEqual(U_BOOT_IMG_DATA + first + second + first, data) + + def testMkimageSymbols(self): + """Test using mkimage to build an image with symbols in it""" + self._SetupSplElf('u_boot_binman_syms') + data = self._DoReadFile('290_mkimage_sym.dts') + + image = control.images['image'] + entries = image.GetEntries() + self.assertIn('u-boot', entries) + u_boot = entries['u-boot'] + + mkim = entries['mkimage'] + mkim_entries = mkim.GetEntries() + self.assertIn('u-boot-spl', mkim_entries) + spl = mkim_entries['u-boot-spl'] + self.assertIn('u-boot-spl2', mkim_entries) + spl2 = mkim_entries['u-boot-spl2'] + + # skip the mkimage header and the area sizes + mk_data = data[mkim.offset + 0x40:] + size, term = struct.unpack('>LL', mk_data[:8]) + + # There should be only one image, so check that the zero terminator is + # present + self.assertEqual(0, term) + + content = mk_data[8:8 + size] + + # 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 + spl_data = content[:0x18] + content = content[0x1b:] + + # After the header is a table of offsets for each image. There should + # only be one image, then a 0 terminator, so figure out the real start + # of the image data + base = 0x40 + 8 + + # Check symbols in both u-boot-spl and u-boot-spl2 + for i in range(2): + vals = struct.unpack('<LLQLL', spl_data) + + # 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 'u_boot_binman_syms' test image + self.assertEqual(elf.BINMAN_SYM_MAGIC_VALUE, vals[0]) + self.assertEqual(base, vals[1]) + self.assertEqual(spl2.offset, vals[2]) + # figure out the internal positions of its components + self.assertEqual(0x10 + u_boot.image_pos, vals[3]) + + # Check that spl and spl2 are actually at the indicated positions + self.assertEqual( + elf.BINMAN_SYM_MAGIC_VALUE, + struct.unpack('<I', data[spl.image_pos:spl.image_pos + 4])[0]) + self.assertEqual( + elf.BINMAN_SYM_MAGIC_VALUE, + struct.unpack('<I', data[spl2.image_pos:spl2.image_pos + 4])[0]) + + self.assertEqual(len(U_BOOT_DATA), vals[4]) + + # Move to next + spl_data = content[:0x18] + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/state.py b/tools/binman/state.py index 3e78cf3..45bae40 100644 --- a/tools/binman/state.py +++ b/tools/binman/state.py @@ -385,8 +385,8 @@ def SetInt(node, prop, value, for_repack=False): for_repack: True is this property is only needed for repacking """ for n in GetUpdateNodes(node, for_repack): - tout.detail("File %s: Update node '%s' prop '%s' to %#x" % - (n.GetFdt().name, n.path, prop, value)) + tout.debug("File %s: Update node '%s' prop '%s' to %#x" % + (n.GetFdt().name, n.path, prop, value)) n.SetInt(prop, value) def CheckAddHashProp(node): diff --git a/tools/binman/test/282_symbols_disable.dts b/tools/binman/test/282_symbols_disable.dts new file mode 100644 index 0000000..6efa933 --- /dev/null +++ b/tools/binman/test/282_symbols_disable.dts @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pad-byte = <0xff>; + u-boot-spl { + no-write-symbols; + }; + + u-boot { + offset = <0x38>; + no-expanded; + }; + + u-boot-spl2 { + type = "u-boot-spl"; + no-write-symbols; + }; + }; +}; diff --git a/tools/binman/test/283_mkimage_special.dts b/tools/binman/test/283_mkimage_special.dts new file mode 100644 index 0000000..c234093 --- /dev/null +++ b/tools/binman/test/283_mkimage_special.dts @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + mkimage { + args = "-T script"; + + u-boot { + }; + + hash { + }; + + imagename { + type = "u-boot"; + }; + }; + }; +}; diff --git a/tools/binman/test/284_fit_fdt_list.dts b/tools/binman/test/284_fit_fdt_list.dts new file mode 100644 index 0000000..8885313 --- /dev/null +++ b/tools/binman/test/284_fit_fdt_list.dts @@ -0,0 +1,58 @@ +// 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-val = "test-fdt1", "test-fdt2"; + + 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"; + hash { + algo = "sha256"; + }; + }; + }; + + configurations { + default = "@config-DEFAULT-SEQ"; + @config-SEQ { + description = "conf-NAME.dtb"; + firmware = "uboot"; + loadables = "atf"; + fdt = "fdt-SEQ"; + }; + }; + }; + u-boot-nodtb { + }; + }; +}; diff --git a/tools/binman/test/285_spl_expand.dts b/tools/binman/test/285_spl_expand.dts new file mode 100644 index 0000000..9c88ccb --- /dev/null +++ b/tools/binman/test/285_spl_expand.dts @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot-spl { + }; + }; +}; diff --git a/tools/binman/test/286_template.dts b/tools/binman/test/286_template.dts new file mode 100644 index 0000000..6980dbf --- /dev/null +++ b/tools/binman/test/286_template.dts @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot-img { + }; + + common_part: template { + u-boot { + }; + + intel-vga { + filename = "vga.bin"; + }; + }; + + first { + type = "section"; + insert-template = <&common_part>; + + u-boot-dtb { + }; + }; + + second { + type = "section"; + insert-template = <&common_part>; + + u-boot-dtb { + }; + + intel-vga { + filename = "vga2.bin"; + }; + }; + }; +}; diff --git a/tools/binman/test/287_template_multi.dts b/tools/binman/test/287_template_multi.dts new file mode 100644 index 0000000..122bfcc --- /dev/null +++ b/tools/binman/test/287_template_multi.dts @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; +/ { + binman: binman { + multiple-images; + + my_template: template { + blob-ext@0 { + filename = "my-blob.bin"; + offset = <0>; + }; + blob-ext@8 { + offset = <8>; + }; + }; + + image { + pad-byte = <0x40>; + filename = "my-image.bin"; + insert-template = <&my_template>; + blob-ext@8 { + filename = "my-blob2.bin"; + }; + }; + }; +}; diff --git a/tools/binman/test/288_template_fit.dts b/tools/binman/test/288_template_fit.dts new file mode 100644 index 0000000..d84dca4 --- /dev/null +++ b/tools/binman/test/288_template_fit.dts @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + binman: binman { + multiple-images; + + my_template: template { + fit@0 { + images { + kernel-1 { + }; + kernel-2 { + }; + }; + }; + }; + + image { + filename = "image.bin"; + insert-template = <&my_template>; + + fit@0 { + description = "desc"; + configurations { + }; + images { + kernel-3 { + }; + kernel-4 { + }; + }; + }; + }; + }; +}; diff --git a/tools/binman/test/289_template_section.dts b/tools/binman/test/289_template_section.dts new file mode 100644 index 0000000..8a744a0 --- /dev/null +++ b/tools/binman/test/289_template_section.dts @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot-img { + }; + + common_part: template { + u-boot { + }; + + intel-vga { + filename = "vga.bin"; + }; + }; + + first { + type = "section"; + insert-template = <&common_part>; + + u-boot-dtb { + }; + }; + + section { + second { + type = "section"; + insert-template = <&common_part>; + + u-boot-dtb { + }; + + intel-vga { + filename = "vga2.bin"; + }; + }; + }; + + second { + type = "section"; + insert-template = <&common_part>; + + u-boot-dtb { + }; + }; + }; +}; diff --git a/tools/binman/test/290_mkimage_sym.dts b/tools/binman/test/290_mkimage_sym.dts new file mode 100644 index 0000000..2dfd286 --- /dev/null +++ b/tools/binman/test/290_mkimage_sym.dts @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot-dtb { + }; + + mkimage { + args = "-n test -T script"; + + u-boot-spl { + }; + + u-boot-spl2 { + type = "u-boot-spl"; + }; + }; + + u-boot { + }; + }; +}; diff --git a/tools/binman/test/Makefile b/tools/binman/test/Makefile index cd66a30..4d152ee 100644 --- a/tools/binman/test/Makefile +++ b/tools/binman/test/Makefile @@ -32,7 +32,7 @@ LDS_BINMAN_EMBED := -T $(SRC)u_boot_binman_embed.lds LDS_EFL_SECTIONS := -T $(SRC)elf_sections.lds LDS_BLOB := -T $(SRC)blob_syms.lds -TARGETS = u_boot_ucode_ptr u_boot_no_ucode_ptr bss_data \ +TARGETS = u_boot_ucode_ptr u_boot_no_ucode_ptr bss_data bss_data_zero \ u_boot_binman_syms u_boot_binman_syms.bin u_boot_binman_syms_bad \ u_boot_binman_syms_size u_boot_binman_syms_x86 embed_data \ u_boot_binman_embed u_boot_binman_embed_sm elf_sections blob_syms.bin @@ -48,6 +48,9 @@ u_boot_ucode_ptr: u_boot_ucode_ptr.c bss_data: CFLAGS += $(SRC)bss_data.lds bss_data: bss_data.c +bss_data_zero: CFLAGS += $(SRC)bss_data_zero.lds +bss_data_zero: bss_data_zero.c + embed_data: CFLAGS += $(SRC)embed_data.lds embed_data: embed_data.c diff --git a/tools/binman/test/bss_data.c b/tools/binman/test/bss_data.c index 4f9b64c..7047a3b 100644 --- a/tools/binman/test/bss_data.c +++ b/tools/binman/test/bss_data.c @@ -7,9 +7,8 @@ */ int bss_data[10]; -int __bss_size = sizeof(bss_data); -int main() +int main(void) { bss_data[2] = 2; diff --git a/tools/binman/test/bss_data_zero.c b/tools/binman/test/bss_data_zero.c new file mode 100644 index 0000000..7047a3b --- /dev/null +++ b/tools/binman/test/bss_data_zero.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2016 Google, Inc + * + * Simple program to create a bss_data region so the symbol can be read + * by binutils. This is used by binman tests. + */ + +int bss_data[10]; + +int main(void) +{ + bss_data[2] = 2; + + return 0; +} diff --git a/tools/binman/test/bss_data_zero.lds b/tools/binman/test/bss_data_zero.lds new file mode 100644 index 0000000..8fa0210 --- /dev/null +++ b/tools/binman/test/bss_data_zero.lds @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2016 Google, Inc + */ + +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start) + +SECTIONS +{ + . = 0xfffffdf0; + _start = .; + __bss_size = 0; +} diff --git a/tools/binman/test/embed_data.lds b/tools/binman/test/embed_data.lds index 908bf66..d416cb2 100644 --- a/tools/binman/test/embed_data.lds +++ b/tools/binman/test/embed_data.lds @@ -17,6 +17,7 @@ SECTIONS embed_start = .; *(.embed*) embed_end = .; + region_size = 0; . = ALIGN(32); *(.data*) } diff --git a/tools/dtoc/fdt.py b/tools/dtoc/fdt.py index a8e0534..fd0f3e9 100644 --- a/tools/dtoc/fdt.py +++ b/tools/dtoc/fdt.py @@ -13,6 +13,7 @@ from dtoc import fdt_util import libfdt from libfdt import QUIET_NOTFOUND from u_boot_pylib import tools +from u_boot_pylib import tout # This deals with a device tree, presenting it as an assortment of Node and # Prop objects, representing nodes and properties, respectively. This file @@ -264,6 +265,13 @@ class Prop: fdt_obj.setprop(node.Offset(), self.name, self.bytes) self.dirty = False + def purge(self): + """Set a property offset to None + + The property remains in the tree structure and will be recreated when + the FDT is synced + """ + self._offset = None class Node: """A device tree node @@ -534,8 +542,8 @@ class Node: """ return self.AddData(prop_name, struct.pack('>I', val)) - def AddSubnode(self, name): - """Add a new subnode to the node + def Subnode(self, name): + """Create new subnode for the node Args: name: name of node to add @@ -544,10 +552,72 @@ class Node: New subnode that was created """ path = self.path + '/' + name - subnode = Node(self._fdt, self, None, name, path) + return Node(self._fdt, self, None, name, path) + + def AddSubnode(self, name): + """Add a new subnode to the node, after all other subnodes + + Args: + name: name of node to add + + Returns: + New subnode that was created + """ + subnode = self.Subnode(name) self.subnodes.append(subnode) return subnode + def insert_subnode(self, name): + """Add a new subnode to the node, before all other subnodes + + This deletes other subnodes and sets their offset to None, so that they + will be recreated after this one. + + Args: + name: name of node to add + + Returns: + New subnode that was created + """ + # Deleting a node invalidates the offsets of all following nodes, so + # process in reverse order so that the offset of each node remains valid + # until deletion. + for subnode in reversed(self.subnodes): + subnode.purge(True) + subnode = self.Subnode(name) + self.subnodes.insert(0, subnode) + return subnode + + def purge(self, delete_it=False): + """Purge this node, setting offset to None and deleting from FDT""" + if self._offset is not None: + if delete_it: + CheckErr(self._fdt._fdt_obj.del_node(self.Offset()), + "Node '%s': delete" % self.path) + self._offset = None + self._fdt.Invalidate() + + for prop in self.props.values(): + prop.purge() + + for subnode in self.subnodes: + subnode.purge(False) + + def move_to_first(self): + """Move the current node to first in its parent's node list""" + parent = self.parent + if parent.subnodes and parent.subnodes[0] == self: + return + for subnode in reversed(parent.subnodes): + subnode.purge(True) + + new_subnodes = [self] + for subnode in parent.subnodes: + #subnode.purge(False) + if subnode != self: + new_subnodes.append(subnode) + parent.subnodes = new_subnodes + def Delete(self): """Delete a node @@ -635,6 +705,71 @@ class Node: prop.Sync(auto_resize) return added + def merge_props(self, src): + """Copy missing properties (except 'phandle') from another node + + Args: + src (Node): Node containing properties to copy + + Adds properties which are present in src but not in this node. Any + 'phandle' property is not copied since this might result in two nodes + with the same phandle, thus making phandle references ambiguous. + """ + for name, src_prop in src.props.items(): + if name != 'phandle' and name not in self.props: + self.props[name] = Prop(self, None, name, src_prop.bytes) + + def copy_node(self, src): + """Copy a node and all its subnodes into this node + + Args: + src (Node): Node to copy + + Returns: + Node: Resulting destination node + + This works recursively. + + The new node is put before all other nodes. If the node already + exists, just its subnodes and properties are copied, placing them before + any existing subnodes. Properties which exist in the destination node + already are not copied. + """ + dst = self.FindNode(src.name) + if dst: + dst.move_to_first() + else: + dst = self.insert_subnode(src.name) + dst.merge_props(src) + + # Process in reverse order so that they appear correctly in the result, + # since copy_node() puts the node first in the list + for node in reversed(src.subnodes): + dst.copy_node(node) + return dst + + def copy_subnodes_from_phandles(self, phandle_list): + """Copy subnodes of a list of nodes into another node + + Args: + phandle_list (list of int): List of phandles of nodes to copy + + For each node in the phandle list, its subnodes and their properties are + copied recursively. Note that it does not copy the node itself, nor its + properties. + """ + # Process in reverse order, since new nodes are inserted at the start of + # the destination's node list. We want them to appear in order of the + # phandle list + for phandle in phandle_list.__reversed__(): + parent = self.GetFdt().LookupPhandle(phandle) + tout.debug(f'adding template {parent.path} to node {self.path}') + for node in parent.subnodes.__reversed__(): + dst = self.copy_node(node) + + tout.debug(f'merge props from {parent.path} to {dst.path}') + self.merge_props(parent) + class Fdt: """Provides simple access to a flat device tree blob using libfdts. diff --git a/tools/dtoc/test/dtoc_test_copy.dts b/tools/dtoc/test/dtoc_test_copy.dts new file mode 100644 index 0000000..36faa9b --- /dev/null +++ b/tools/dtoc/test/dtoc_test_copy.dts @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + reference = <&over>; /* nake sure that the 'over' phandle exists */ + copy-list = <&another &base>; + + dest { + bootph-all; + compatible = "sandbox,spl-test"; + stringarray = "one"; + longbytearray = [09 0a 0b 0c 0d 0e 0f 10]; + maybe-empty-int = <1>; + + first@0 { + a-prop = <456>; + b-prop = <1>; + }; + + existing { + }; + + base { + second { + second3 { + }; + + second2 { + new-prop; + }; + + second1 { + new-prop; + }; + + second4 { + }; + }; + }; + }; + + base: base { + compatible = "sandbox,i2c"; + bootph-all; + #address-cells = <1>; + #size-cells = <0>; + over: over { + compatible = "sandbox,pmic"; + bootph-all; + reg = <9>; + low-power; + }; + + first@0 { + reg = <0>; + a-prop = <123>; + }; + + second: second { + second1 { + some-prop; + }; + + second2 { + some-prop; + }; + }; + }; + + another: another { + new-prop = "hello"; + earlier { + wibble = <2>; + }; + + later { + fibble = <3>; + }; + }; +}; diff --git a/tools/dtoc/test_fdt.py b/tools/dtoc/test_fdt.py index 4fe8d12..3e54694 100755 --- a/tools/dtoc/test_fdt.py +++ b/tools/dtoc/test_fdt.py @@ -306,6 +306,119 @@ class TestNode(unittest.TestCase): self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'", str(exc.exception)) + def test_copy_node(self): + """Test copy_node() function""" + def do_copy_checks(dtb, dst, expect_none): + self.assertEqual( + ['/dest/base', '/dest/first@0', '/dest/existing'], + [n.path for n in dst.subnodes]) + + chk = dtb.GetNode('/dest/base') + self.assertTrue(chk) + self.assertEqual( + {'compatible', 'bootph-all', '#address-cells', '#size-cells'}, + chk.props.keys()) + + # Check the first property + prop = chk.props['bootph-all'] + self.assertEqual('bootph-all', prop.name) + self.assertEqual(True, prop.value) + self.assertEqual(chk.path, prop._node.path) + + # Check the second property + prop2 = chk.props['compatible'] + self.assertEqual('compatible', prop2.name) + self.assertEqual('sandbox,i2c', prop2.value) + self.assertEqual(chk.path, prop2._node.path) + + base = chk.FindNode('base') + self.assertTrue(chk) + + first = dtb.GetNode('/dest/base/first@0') + self.assertTrue(first) + over = dtb.GetNode('/dest/base/over') + self.assertTrue(over) + + # Make sure that the phandle for 'over' is not copied + self.assertNotIn('phandle', over.props.keys()) + + second = dtb.GetNode('/dest/base/second') + self.assertTrue(second) + self.assertEqual([over.name, first.name, second.name], + [n.name for n in chk.subnodes]) + self.assertEqual(chk, over.parent) + self.assertEqual( + {'bootph-all', 'compatible', 'reg', 'low-power'}, + over.props.keys()) + + if expect_none: + self.assertIsNone(prop._offset) + self.assertIsNone(prop2._offset) + self.assertIsNone(over._offset) + else: + self.assertTrue(prop._offset) + self.assertTrue(prop2._offset) + self.assertTrue(over._offset) + + # Now check ordering of the subnodes + self.assertEqual( + ['second1', 'second2', 'second3', 'second4'], + [n.name for n in second.subnodes]) + + dtb = fdt.FdtScan(find_dtb_file('dtoc_test_copy.dts')) + tmpl = dtb.GetNode('/base') + dst = dtb.GetNode('/dest') + dst.copy_node(tmpl) + + do_copy_checks(dtb, dst, expect_none=True) + + dtb.Sync(auto_resize=True) + + # Now check that the FDT looks correct + new_dtb = fdt.Fdt.FromData(dtb.GetContents()) + new_dtb.Scan() + dst = new_dtb.GetNode('/dest') + do_copy_checks(new_dtb, dst, expect_none=False) + + def test_copy_subnodes_from_phandles(self): + """Test copy_node() function""" + dtb = fdt.FdtScan(find_dtb_file('dtoc_test_copy.dts')) + + orig = dtb.GetNode('/') + node_list = fdt_util.GetPhandleList(orig, 'copy-list') + + dst = dtb.GetNode('/dest') + dst.copy_subnodes_from_phandles(node_list) + + pmic = dtb.GetNode('/dest/over') + self.assertTrue(pmic) + + subn = dtb.GetNode('/dest/first@0') + self.assertTrue(subn) + self.assertEqual({'a-prop', 'b-prop', 'reg'}, subn.props.keys()) + + self.assertEqual( + ['/dest/earlier', '/dest/later', '/dest/over', '/dest/first@0', + '/dest/second', '/dest/existing', '/dest/base'], + [n.path for n in dst.subnodes]) + + # Make sure that the phandle for 'over' is not copied + over = dst.FindNode('over') + print('keys', over.props.keys()) + self.assertNotIn('phandle', over.props.keys()) + + # Check the merged properties, first the base ones in '/dest' + expect = {'bootph-all', 'compatible', 'stringarray', 'longbytearray', + 'maybe-empty-int'} + + # Properties from 'base' + expect.update({'#address-cells', '#size-cells'}) + + # Properties from 'another' + expect.add('new-prop') + + self.assertEqual(expect, set(dst.props.keys())) + class TestProp(unittest.TestCase): """Test operation of the Prop class""" diff --git a/tools/u_boot_pylib/pyproject.toml b/tools/u_boot_pylib/pyproject.toml index 3f33caf..037c5d6 100644 --- a/tools/u_boot_pylib/pyproject.toml +++ b/tools/u_boot_pylib/pyproject.toml @@ -9,7 +9,7 @@ authors = [ { name="Simon Glass", email="sjg@chromium.org" }, ] description = "U-Boot python library" -readme = "README.md" +readme = "README.rst" requires-python = ">=3.7" classifiers = [ "Programming Language :: Python :: 3", @@ -20,3 +20,7 @@ classifiers = [ [project.urls] "Homepage" = "https://u-boot.readthedocs.io" "Bug Tracker" = "https://source.denx.de/groups/u-boot/-/issues" + +[tool.setuptools.packages.find] +where = [".."] +include = ["u_boot_pylib*"] |