aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2023-07-20 21:31:31 -0400
committerTom Rini <trini@konsulko.com>2023-07-20 21:31:31 -0400
commite896279ac39ebb97f23e6132bf7668a61e1cd86b (patch)
tree24e035ebd13dbd272da4b3845a1dfcac66e4a86f
parent7fe5accb4516144b7abb8f183640cdf50423121e (diff)
parent24142ead21ed5e4d2d6f39dd410d91d815ea1ae2 (diff)
downloadu-boot-WIP/20Jul2023.zip
u-boot-WIP/20Jul2023.tar.gz
u-boot-WIP/20Jul2023.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
-rw-r--r--arch/arm/dts/stm32mp15-u-boot.dtsi1
-rw-r--r--arch/sandbox/include/asm/sdl.h23
-rw-r--r--arch/sandbox/include/asm/test.h25
-rw-r--r--boot/bootmeth_script.c5
-rw-r--r--cmd/load.c16
-rw-r--r--drivers/core/of_access.c5
-rw-r--r--drivers/core/read.c5
-rw-r--r--drivers/reset/reset-rockchip.c2
-rw-r--r--include/dm/read.h12
-rw-r--r--test/dm/video.c1
-rw-r--r--tools/binman/binman.rst94
-rw-r--r--tools/binman/bintool.py2
-rw-r--r--tools/binman/control.py32
-rw-r--r--tools/binman/elf.py13
-rw-r--r--tools/binman/elf_test.py13
-rw-r--r--tools/binman/entries.rst6
-rw-r--r--tools/binman/entry.py11
-rw-r--r--tools/binman/etype/blob_phase.py5
-rw-r--r--tools/binman/etype/fit.py9
-rw-r--r--tools/binman/etype/mkimage.py136
-rw-r--r--tools/binman/etype/section.py54
-rw-r--r--tools/binman/etype/u_boot_spl_bss_pad.py2
-rw-r--r--tools/binman/etype/u_boot_tpl_bss_pad.py2
-rw-r--r--tools/binman/etype/u_boot_vpl_bss_pad.py2
-rw-r--r--tools/binman/ftest.py218
-rw-r--r--tools/binman/state.py4
-rw-r--r--tools/binman/test/282_symbols_disable.dts25
-rw-r--r--tools/binman/test/283_mkimage_special.dts24
-rw-r--r--tools/binman/test/284_fit_fdt_list.dts58
-rw-r--r--tools/binman/test/285_spl_expand.dts13
-rw-r--r--tools/binman/test/286_template.dts42
-rw-r--r--tools/binman/test/287_template_multi.dts27
-rw-r--r--tools/binman/test/288_template_fit.dts37
-rw-r--r--tools/binman/test/289_template_section.dts52
-rw-r--r--tools/binman/test/290_mkimage_sym.dts27
-rw-r--r--tools/binman/test/Makefile5
-rw-r--r--tools/binman/test/bss_data.c3
-rw-r--r--tools/binman/test/bss_data_zero.c16
-rw-r--r--tools/binman/test/bss_data_zero.lds15
-rw-r--r--tools/binman/test/embed_data.lds1
-rw-r--r--tools/dtoc/fdt.py141
-rw-r--r--tools/dtoc/test/dtoc_test_copy.dts88
-rwxr-xr-xtools/dtoc/test_fdt.py113
-rw-r--r--tools/u_boot_pylib/pyproject.toml6
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)
diff --git a/cmd/load.c b/cmd/load.c
index 5c4f347..2715cf5 100644
--- a/cmd/load.c
+++ b/cmd/load.c
@@ -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*"]