aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile3
-rw-r--r--doc/develop/qconfig.rst14
-rw-r--r--doc/develop/spl.rst2
-rw-r--r--scripts/basic/fixdep.c2
-rw-r--r--tools/binman/binman.rst4
-rw-r--r--tools/binman/bintools.rst69
-rw-r--r--tools/binman/btool/fdt_add_pubkey.py4
-rw-r--r--tools/binman/btool/fdtgrep.py137
-rw-r--r--tools/binman/control.py77
-rw-r--r--tools/binman/elf.py2
-rw-r--r--tools/binman/entries.rst94
-rw-r--r--tools/binman/entry.py17
-rw-r--r--tools/binman/etype/alternates_fdt.py132
-rw-r--r--tools/binman/etype/blob_dtb.py8
-rw-r--r--tools/binman/etype/fit.py93
-rw-r--r--tools/binman/etype/u_boot_spl_nodtb.py2
-rw-r--r--tools/binman/etype/u_boot_tpl_nodtb.py2
-rw-r--r--tools/binman/etype/u_boot_vpl.py3
-rw-r--r--tools/binman/etype/u_boot_vpl_nodtb.py4
-rw-r--r--tools/binman/ftest.py255
-rw-r--r--tools/binman/image.py24
-rwxr-xr-xtools/binman/main.py2
-rw-r--r--tools/binman/test/328_alternates_fdt.dts28
-rw-r--r--tools/binman/test/329_alternates_fdtgrep.dts29
-rw-r--r--tools/binman/test/330_alternates_vpl.dts29
-rw-r--r--tools/binman/test/331_alternates_spl.dts29
-rw-r--r--tools/binman/test/332_alternates_inval.dts29
-rw-r--r--tools/binman/test/333_fit_fdt_dir.dts58
-rw-r--r--tools/binman/test/334_fit_fdt_compat.dts60
-rw-r--r--tools/binman/test/335_fit_fdt_phase.dts61
-rw-r--r--tools/binman/test/alt_dts/model1.dts24
-rw-r--r--tools/binman/test/alt_dts/model2.dts24
-rwxr-xr-xtools/qconfig.py29
33 files changed, 1243 insertions, 107 deletions
diff --git a/Makefile b/Makefile
index 750ce61..ea562c2 100644
--- a/Makefile
+++ b/Makefile
@@ -1388,9 +1388,12 @@ cmd_binman = $(srctree)/tools/binman/binman $(if $(BINMAN_DEBUG),-D) \
-a rockchip-tpl-path=$(ROCKCHIP_TPL) \
-a spl-bss-pad=$(if $(CONFIG_SPL_SEPARATE_BSS),,1) \
-a tpl-bss-pad=$(if $(CONFIG_TPL_SEPARATE_BSS),,1) \
+ -a vpl-bss-pad=$(if $(CONFIG_VPL_SEPARATE_BSS),,1) \
-a spl-dtb=$(CONFIG_SPL_OF_REAL) \
-a tpl-dtb=$(CONFIG_TPL_OF_REAL) \
+ -a vpl-dtb=$(CONFIG_VPL_OF_REAL) \
-a pre-load-key-path=${PRE_LOAD_KEY_PATH} \
+ -a of-spl-remove-props=$(CONFIG_OF_SPL_REMOVE_PROPS) \
$(BINMAN_$(@F))
OBJCOPYFLAGS_u-boot.ldr.hex := -I binary -O ihex
diff --git a/doc/develop/qconfig.rst b/doc/develop/qconfig.rst
index 8efb1eb..123779e 100644
--- a/doc/develop/qconfig.rst
+++ b/doc/develop/qconfig.rst
@@ -85,6 +85,20 @@ example, to find boards which enabled CONFIG_SCSI but not CONFIG_BLK::
3 matches
pg_wcom_seli8_defconfig highbank_defconfig pg_wcom_expu1_defconfig
+It is also possible to search for particular values. For example, this finds all
+boards with an empty string for `CONFIG_DEFAULT_FDT_FILE`::
+
+ ./tools/qconfig.py -f DEFAULT_FDT_FILE=\"\"
+ 1092 matches
+ ...
+
+This finds boards which have a value for SYS_MAXARGS other than 64::
+
+ ./tools/qconfig.py -f ~SYS_MAXARGS=64
+ cfg CONFIG_SYS_MAXARGS
+ 281 matches
+ ...
+
Finding implied CONFIGs
-----------------------
diff --git a/doc/develop/spl.rst b/doc/develop/spl.rst
index 0a3e572..4bb48e6 100644
--- a/doc/develop/spl.rst
+++ b/doc/develop/spl.rst
@@ -121,6 +121,8 @@ Use `spl_phase()` to find the current U-Boot phase, e.g. `PHASE_SPL`. You can
also find the previous and next phase and get the phase name.
+.. _fdtgrep_filter:
+
Device tree
-----------
The U-Boot device tree is filtered by the fdtgrep tools during the build
diff --git a/scripts/basic/fixdep.c b/scripts/basic/fixdep.c
index 5ced0f6..3d40bd7 100644
--- a/scripts/basic/fixdep.c
+++ b/scripts/basic/fixdep.c
@@ -421,6 +421,8 @@ int main(int argc, char *argv[])
strcpy(tmp_buf, "SPL_");
else if (!strncmp(target, "tpl/", 4))
strcpy(tmp_buf, "TPL_");
+ else if (!strncmp(target, "vpl/", 4))
+ strcpy(tmp_buf, "VPL_");
/* end U-Boot hack */
xprintf("cmd_%s := %s\n\n", target, cmdline);
diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst
index 872e974..0cafc36 100644
--- a/tools/binman/binman.rst
+++ b/tools/binman/binman.rst
@@ -1211,7 +1211,7 @@ Templates provide a simple way to handle this::
spi-image {
filename = "image-spi.bin";
- insert-template = <&fit>;
+ insert-template = <&common_part>;
/* things specific to SPI follow */
footer {
@@ -1224,7 +1224,7 @@ Templates provide a simple way to handle this::
mmc-image {
filename = "image-mmc.bin";
- insert-template = <&fit>;
+ insert-template = <&common_part>;
/* things specific to MMC follow */
footer {
diff --git a/tools/binman/bintools.rst b/tools/binman/bintools.rst
index 1336f4d..cd05ad8 100644
--- a/tools/binman/bintools.rst
+++ b/tools/binman/bintools.rst
@@ -10,6 +10,15 @@ binaries. It is fairly easy to create new bintools. Just add a new file to the
+Bintool: bootgen: Generate bootable fsbl image for zynq/zynqmp
+--------------------------------------------------------------
+
+This bintools supports running Xilinx "bootgen" in order
+to generate a bootable, authenticated image form an SPL.
+
+
+
+
Bintool: bzip2: Compression/decompression using the bzip2 algorithm
-------------------------------------------------------------------
@@ -37,6 +46,33 @@ Documentation about CBFS is at https://www.coreboot.org/CBFS
+Bintool: cst: Image generation for U-Boot
+-----------------------------------------
+
+This bintool supports running `cst` with some basic parameters as
+needed by binman.
+
+
+
+Bintool: fdt_add_pubkey: Add public key to control dtb (spl or u-boot proper)
+-----------------------------------------------------------------------------
+
+This bintool supports running `fdt_add_pubkey`.
+
+Normally mkimage adds signature information to the control dtb. However
+binman images are built independent from each other. Thus it is required
+to add the public key separately from mkimage.
+
+
+
+Bintool: fdtgrep: Handles the 'fdtgrep' tool
+--------------------------------------------
+
+This bintool supports running `fdtgrep` with parameters suitable for
+producing SPL devicetrees from the main one.
+
+
+
Bintool: fiptool: Image generation for ARM Trusted Firmware
-----------------------------------------------------------
@@ -143,11 +179,20 @@ Documentation is available via::
+Bintool: mkeficapsule: Handles the 'mkeficapsule' tool
+------------------------------------------------------
+
+This bintool is used for generating the EFI capsules. The
+capsule generation parameters can either be specified through
+commandline, or through a config file.
+
+
+
Bintool: mkimage: Image generation for U-Boot
---------------------------------------------
This bintool supports running `mkimage` with some basic parameters as
-neeed by binman.
+needed by binman.
Normally binman uses the mkimage built by U-Boot. But when run outside the
U-Boot build system, binman can use the version installed in your system.
@@ -194,25 +239,3 @@ Documentation is available via::
-Bintool: fdt_add_pubkey: Add public key to device tree
-------------------------------------------------------
-
-This bintool supports running `fdt_add_pubkey` in order to add a public
-key coming from a certificate to a device-tree.
-
-Normally signing is done using `mkimage` in context of `binman sign`. However,
-in this process the public key is not added to the stage before u-boot proper.
-Using `fdt_add_pubkey` the key can be injected to the SPL independent of
-`mkimage`
-
-
-
-Bintool: bootgen: Sign ZynqMP FSBL image
-----------------------------------------
-
-This bintool supports running `bootgen` in order to sign a SPL for ZynqMP
-devices.
-
-The bintool automatically creates an appropriate input image file (.bif) for
-bootgen based on the passed arguments. The output is a bootable,
-authenticated `boot.bin` file.
diff --git a/tools/binman/btool/fdt_add_pubkey.py b/tools/binman/btool/fdt_add_pubkey.py
index a507742..75a9716 100644
--- a/tools/binman/btool/fdt_add_pubkey.py
+++ b/tools/binman/btool/fdt_add_pubkey.py
@@ -32,6 +32,10 @@ class Bintoolfdt_add_pubkey(bintool.Bintool):
verified for the image / configuration to be considered valid.
algo (str): Cryptographic algorithm. Optional parameter,
default value: sha1,rsa2048
+
+ Returns:
+ CommandResult: Resulting output from the bintool, or None if the
+ tool is not present
"""
args = []
if algo:
diff --git a/tools/binman/btool/fdtgrep.py b/tools/binman/btool/fdtgrep.py
new file mode 100644
index 0000000..da1f8c7
--- /dev/null
+++ b/tools/binman/btool/fdtgrep.py
@@ -0,0 +1,137 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright 2022 Google LLC
+#
+"""Bintool implementation for fdtgrep
+
+fdtgrepprovides a way to grep devicetree-binary files to extract or remove
+certain elements.
+
+Usage: fdtgrep - extract portions from device tree
+
+Usage:
+ fdtgrep <options> <dt file>|-
+
+Output formats are:
+ dts - device tree soure text
+ dtb - device tree blob (sets -Hmt automatically)
+ bin - device tree fragment (may not be a valid .dtb)
+
+Options: -[haAc:b:C:defg:G:HIlLmn:N:o:O:p:P:rRsStTvhV]
+ -a, --show-address Display address
+ -A, --colour Show all nodes/tags, colour those that match
+ -b, --include-node-with-prop <arg> Include contains containing property
+ -c, --include-compat <arg> Compatible nodes to include in grep
+ -C, --exclude-compat <arg> Compatible nodes to exclude in grep
+ -d, --diff Diff: Mark matching nodes with +, others with -
+ -e, --enter-node Enter direct subnode names of matching nodes
+ -f, --show-offset Display offset
+ -g, --include-match <arg> Node/property/compatible string to include in grep
+ -G, --exclude-match <arg> Node/property/compatible string to exclude in grep
+ -H, --show-header Output a header
+ -I, --show-version Put "/dts-v1/;" on first line of dts output
+ -l, --list-regions Output a region list
+ -L, --list-strings List strings in string table
+ -m, --include-mem Include mem_rsvmap section in binary output
+ -n, --include-node <arg> Node to include in grep
+ -N, --exclude-node <arg> Node to exclude in grep
+ -p, --include-prop <arg> Property to include in grep
+ -P, --exclude-prop <arg> Property to exclude in grep
+ -r, --remove-strings Remove unused strings from string table
+ -R, --include-root Include root node and all properties
+ -s, --show-subnodes Show all subnodes matching nodes
+ -S, --skip-supernodes Don't include supernodes of matching nodes
+ -t, --show-stringtab Include string table in binary output
+ -T, --show-aliases Include matching aliases in output
+ -o, --out <arg> -o <output file>
+ -O, --out-format <arg> -O <output format>
+ -v, --invert-match Invert the sense of matching (select non-matching lines)
+ -h, --help Print this help and exit
+ -V, --version Print version and exit
+"""
+
+import tempfile
+
+from u_boot_pylib import tools
+from binman import bintool
+
+class Bintoolfdtgrep(bintool.Bintool):
+ """Handles the 'fdtgrep' tool
+
+ This bintool supports running `fdtgrep` with parameters suitable for
+ producing SPL devicetrees from the main one.
+ """
+ def __init__(self, name):
+ super().__init__(name, 'Grep devicetree files')
+
+ def create_for_phase(self, infile, phase, outfile, remove_props):
+ """Create the FDT for a particular phase
+
+ Args:
+ infile (str): Input filename containing the full FDT contents (with
+ all nodes and properties)
+ phase (str): Phase to generate for ('tpl', 'vpl', 'spl')
+ outfile (str): Output filename to write the grepped FDT contents to
+ (with only neceesary nodes and properties)
+
+ Returns:
+ CommandResult: Resulting output from the bintool, or None if the
+ tool is not present
+ """
+ if phase == 'tpl':
+ tag = 'bootph-pre-sram'
+ elif phase == 'vpl':
+ tag = 'bootph-verify'
+ elif phase == 'spl':
+ tag = 'bootph-pre-ram'
+ else:
+ raise ValueError(f"Invalid U-Boot phase '{phase}': Use tpl/vpl/spl")
+
+ # These args mirror those in cmd_fdtgrep in scripts/Makefile.lib
+ # First do the first stage
+ with tempfile.NamedTemporaryFile(prefix='fdtgrep.tmp',
+ dir=tools.get_output_dir()) as tmp:
+ args = [
+ infile,
+ '-o', tmp.name,
+ '-b', 'bootph-all',
+ '-b', tag,
+ '-u',
+ '-RT',
+ '-n', '/chosen',
+ '-n', '/config',
+ '-O', 'dtb',
+ ]
+ self.run_cmd(*args)
+ args = [
+ tmp.name,
+ '-o', outfile,
+ '-r',
+ '-O', 'dtb',
+ '-P', 'bootph-all',
+ '-P', 'bootph-pre-ram',
+ '-P', 'bootph-pre-sram',
+ '-P', 'bootph-verify',
+ ]
+ for prop_name in remove_props:
+ args += ['-P', prop_name]
+ return self.run_cmd(*args)
+
+ def fetch(self, method):
+ """Fetch handler for fdtgrep
+
+ This installs fdtgrep using the apt utility, which assumes that it is
+ packaged in u-boot tools, which it is not.
+
+ Args:
+ method (FETCH_...): Method to use
+
+ Returns:
+ True if the file was fetched and now installed, None if a method
+ other than FETCH_BIN was requested
+
+ Raises:
+ Valuerror: Fetching could not be completed
+ """
+ if method != bintool.FETCH_BIN:
+ return None
+ return self.apt_install('u-boot-tools')
diff --git a/tools/binman/control.py b/tools/binman/control.py
index 2f00279..542c2b4 100644
--- a/tools/binman/control.py
+++ b/tools/binman/control.py
@@ -617,6 +617,50 @@ def PrepareImagesAndDtbs(dtb_fname, select_images, update_fdt, use_expanded):
dtb_item.Flush()
return images
+def CheckForProblems(image):
+ """Check for problems with image generation
+
+ Shows warning about missing, faked or optional external blobs, as well as
+ missing bintools.
+
+ Args:
+ image (Image): Image to process
+
+ Returns:
+ bool: True if there are any problems which result in a non-functional
+ image
+ """
+ missing_list = []
+ image.CheckMissing(missing_list)
+ if missing_list:
+ tout.error("Image '%s' is missing external blobs and is non-functional: %s\n" %
+ (image.name, ' '.join([e.name for e in missing_list])))
+ _ShowHelpForMissingBlobs(tout.ERROR, missing_list)
+
+ faked_list = []
+ image.CheckFakedBlobs(faked_list)
+ if faked_list:
+ tout.warning(
+ "Image '%s' has faked external blobs and is non-functional: %s\n" %
+ (image.name, ' '.join([os.path.basename(e.GetDefaultFilename())
+ for e in faked_list])))
+
+ optional_list = []
+ image.CheckOptional(optional_list)
+ if optional_list:
+ tout.warning(
+ "Image '%s' is missing optional external blobs but is still functional: %s\n" %
+ (image.name, ' '.join([e.name for e in optional_list])))
+ _ShowHelpForMissingBlobs(tout.WARNING, optional_list)
+
+ missing_bintool_list = []
+ image.check_missing_bintools(missing_bintool_list)
+ if missing_bintool_list:
+ tout.warning(
+ "Image '%s' has missing bintools and is non-functional: %s\n" %
+ (image.name, ' '.join([os.path.basename(bintool.name)
+ for bintool in missing_bintool_list])))
+ return any([missing_list, faked_list, missing_bintool_list])
def ProcessImage(image, update_fdt, write_map, get_contents=True,
allow_resize=True, allow_missing=False,
@@ -689,38 +733,11 @@ def ProcessImage(image, update_fdt, write_map, get_contents=True,
if write_map:
image.WriteMap()
- missing_list = []
- image.CheckMissing(missing_list)
- if missing_list:
- tout.error("Image '%s' is missing external blobs and is non-functional: %s\n" %
- (image.name, ' '.join([e.name for e in missing_list])))
- _ShowHelpForMissingBlobs(tout.ERROR, missing_list)
-
- faked_list = []
- image.CheckFakedBlobs(faked_list)
- if faked_list:
- tout.warning(
- "Image '%s' has faked external blobs and is non-functional: %s\n" %
- (image.name, ' '.join([os.path.basename(e.GetDefaultFilename())
- for e in faked_list])))
+ has_problems = CheckForProblems(image)
- optional_list = []
- image.CheckOptional(optional_list)
- if optional_list:
- tout.warning(
- "Image '%s' is missing optional external blobs but is still functional: %s\n" %
- (image.name, ' '.join([e.name for e in optional_list])))
- _ShowHelpForMissingBlobs(tout.WARNING, optional_list)
-
- missing_bintool_list = []
- image.check_missing_bintools(missing_bintool_list)
- if missing_bintool_list:
- tout.warning(
- "Image '%s' has missing bintools and is non-functional: %s\n" %
- (image.name, ' '.join([os.path.basename(bintool.name)
- for bintool in missing_bintool_list])))
- return any([missing_list, faked_list, missing_bintool_list])
+ image.WriteAlternates()
+ return has_problems
def Binman(args):
"""The main control code for binman
diff --git a/tools/binman/elf.py b/tools/binman/elf.py
index 2ecc95f..a469405 100644
--- a/tools/binman/elf.py
+++ b/tools/binman/elf.py
@@ -275,7 +275,7 @@ def LookupAndWriteSymbols(elf_fname, entry, section, is_elf=False,
return 0
base = syms.get(base_sym)
if not base and not is_elf:
- tout.debug('LookupAndWriteSymbols: no base')
+ tout.debug(f'LookupAndWriteSymbols: no base: elf_fname={elf_fname}, base_sym={base_sym}, is_elf={is_elf}')
return 0
base_addr = 0 if is_elf else base.address
count = 0
diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst
index bdda1ef..1248270 100644
--- a/tools/binman/entries.rst
+++ b/tools/binman/entries.rst
@@ -11,6 +11,48 @@ features to produce new behaviours.
+.. _etype_alternates_fdt:
+
+Entry: alternates-fdt: Entry that generates alternative sections for each devicetree provided
+---------------------------------------------------------------------------------------------
+
+When creating an image designed to boot on multiple models, each model
+requires its own devicetree. This entry deals with selecting the correct
+devicetree from a directory containing them. Each one is read in turn, then
+used to produce section contents which are written to a file. This results
+in a number of images, one for each model.
+
+For example this produces images for each .dtb file in the 'dtb' directory::
+
+ alternates-fdt {
+ fdt-list-dir = "dtb";
+ filename-pattern = "NAME.bin";
+ fdt-phase = "tpl";
+
+ section {
+ u-boot-tpl {
+ };
+ };
+ };
+
+Each output file is named based on its input file, so an input file of
+`model1.dtb` results in an output file of `model1.bin` (i.e. the `NAME` in
+the `filename-pattern` property is replaced with the .dtb basename).
+
+Note that this entry type still produces contents for the 'main' image, in
+that case using the normal dtb provided to Binman, e.g. `u-boot-tpl.dtb`.
+But that image is unlikely to be useful, since it relates to whatever dtb
+happened to be the default when U-Boot builds
+(i.e. `CONFIG_DEFAULT_DEVICE_TREE`). However, Binman ensures that the size
+of each of the alternates is the same as the 'default' one, so they can in
+principle be 'slotted in' to the appropriate place in the main image.
+
+The optional `fdt-phase` property indicates the phase to build. In this
+case, it etype runs fdtgrep to obtain the devicetree subset for that phase,
+respecting the `bootph-xxx` tags in the devicetree.
+
+
+
.. _etype_atf_bl31:
Entry: atf-bl31: ARM Trusted Firmware (ATF) BL31 blob
@@ -815,6 +857,13 @@ The top-level 'fit' node supports the following special properties:
fit,fdt-list-val = "dtb1", "dtb2";
+ fit,fdt-list-dir
+ As an alternative to fit,fdt-list the list of device tree files
+ can be provided as a directory. Each .dtb file in the directory is
+ processed, , e.g.::
+
+ fit,fdt-list-dir = "arch/arm/dts
+
Substitutions
~~~~~~~~~~~~~
@@ -890,6 +939,7 @@ You can create config nodes in a similar way::
firmware = "atf";
loadables = "uboot";
fdt = "fdt-SEQ";
+ fit,compatible; // optional
};
};
@@ -899,6 +949,39 @@ for each of your two files.
Note that if no devicetree files are provided (with '-a of-list' as above)
then no nodes will be generated.
+The 'fit,compatible' property (if present) is replaced with the compatible
+string from the root node of the devicetree, so that things work correctly
+with FIT's configuration-matching algortihm.
+
+Dealing with phases
+~~~~~~~~~~~~~~~~~~~
+
+FIT can be used to load firmware. In this case it may be necessary to run
+the devicetree for each model through fdtgrep to remove unwanted properties.
+The 'fit,fdt-phase' property can be provided to indicate the phase for which
+the devicetree is intended.
+
+For example this indicates that the FDT should be processed for VPL::
+
+ images {
+ @fdt-SEQ {
+ description = "fdt-NAME";
+ type = "flat_dt";
+ compression = "none";
+ fit,fdt-phase = "vpl";
+ };
+ };
+
+Using this mechanism, it is possible to generate a FIT which can provide VPL
+images for multiple models, with TPL selecting the correct model to use. The
+same approach can of course be used for SPL images.
+
+Note that the `of-spl-remove-props` entryarg can be used to indicate
+additional properties to remove. It is often used to remove properties like
+`clock-names` and `pinctrl-names` which are not needed in SPL builds.
+
+See :ref:`fdtgrep_filter` for more information.
+
Generating nodes from an ELF file (split-elf)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2290,8 +2373,6 @@ u-boot-spl-dtb
SPL can access binman symbols at runtime. See :ref:`binman_fdt`.
-in the binman README for more information.
-
The ELF file 'spl/u-boot-spl' must also be available for this to work, since
binman uses that to look up symbols to write into the SPL binary.
@@ -2480,8 +2561,6 @@ u-boot-tpl-dtb
TPL can access binman symbols at runtime. See :ref:`binman_fdt`.
-in the binman README for more information.
-
The ELF file 'tpl/u-boot-tpl' must also be available for this to work, since
binman uses that to look up symbols to write into the TPL binary.
@@ -2571,6 +2650,9 @@ in the binman README for more information.
The ELF file 'vpl/u-boot-vpl' must also be available for this to work, since
binman uses that to look up symbols to write into the VPL binary.
+Note that this entry is automatically replaced with u-boot-vpl-expanded
+unless --no-expanded is used or the node has a 'no-expanded' property.
+
.. _etype_u_boot_vpl_bss_pad:
@@ -2659,8 +2741,8 @@ Properties / Entry arguments:
This is the U-Boot VPL binary, It does not include a device tree blob at
the end of it so may not be able to work without it, assuming VPL needs
-a device tree to operate on your platform. You can add a u_boot_vpl_dtb
-entry after this one, or use a u_boot_vpl entry instead, which normally
+a device tree to operate on your platform. You can add a u-boot-vpl-dtb
+entry after this one, or use a u-boot-vpl entry instead, which normally
expands to a section containing u-boot-vpl-dtb, u-boot-vpl-bss-pad and
u-boot-vpl-dtb
diff --git a/tools/binman/entry.py b/tools/binman/entry.py
index 219d5dc..6d2f378 100644
--- a/tools/binman/entry.py
+++ b/tools/binman/entry.py
@@ -1199,6 +1199,9 @@ features to produce new behaviours.
self.uncomp_size = len(indata)
if self.comp_bintool.is_present():
data = self.comp_bintool.compress(indata)
+ uniq = self.GetUniqueName()
+ fname = tools.get_output_filename(f'comp.{uniq}')
+ tools.write_file(fname, data)
else:
self.record_missing_bintool(self.comp_bintool)
data = tools.get_bytes(0, 1024)
@@ -1383,3 +1386,17 @@ features to produce new behaviours.
def UpdateSignatures(self, privatekey_fname, algo, input_fname):
self.Raise('Updating signatures is not supported with this entry type')
+
+ def FdtContents(self, fdt_etype):
+ """Get the contents of an FDT for a particular phase
+
+ Args:
+ fdt_etype (str): Filename of the phase of the FDT to return, e.g.
+ 'u-boot-tpl-dtb'
+
+ Returns:
+ tuple:
+ fname (str): Filename of .dtb
+ bytes: Contents of FDT (possibly run through fdtgrep)
+ """
+ return self.section.FdtContents(fdt_etype)
diff --git a/tools/binman/etype/alternates_fdt.py b/tools/binman/etype/alternates_fdt.py
new file mode 100644
index 0000000..808f535
--- /dev/null
+++ b/tools/binman/etype/alternates_fdt.py
@@ -0,0 +1,132 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright 2024 Google LLC
+# Written by Simon Glass <sjg@chromium.org>
+
+"""Entry-type module for producing multiple alternate sections"""
+
+import glob
+import os
+
+from binman.entry import EntryArg
+from binman.etype.section import Entry_section
+from dtoc import fdt_util
+from u_boot_pylib import tools
+
+class Entry_alternates_fdt(Entry_section):
+ """Entry that generates alternative sections for each devicetree provided
+
+ When creating an image designed to boot on multiple models, each model
+ requires its own devicetree. This entry deals with selecting the correct
+ devicetree from a directory containing them. Each one is read in turn, then
+ used to produce section contents which are written to a file. This results
+ in a number of images, one for each model.
+
+ For example this produces images for each .dtb file in the 'dtb' directory::
+
+ alternates-fdt {
+ fdt-list-dir = "dtb";
+ filename-pattern = "NAME.bin";
+ fdt-phase = "tpl";
+
+ section {
+ u-boot-tpl {
+ };
+ };
+ };
+
+ Each output file is named based on its input file, so an input file of
+ `model1.dtb` results in an output file of `model1.bin` (i.e. the `NAME` in
+ the `filename-pattern` property is replaced with the .dtb basename).
+
+ Note that this entry type still produces contents for the 'main' image, in
+ that case using the normal dtb provided to Binman, e.g. `u-boot-tpl.dtb`.
+ But that image is unlikely to be useful, since it relates to whatever dtb
+ happened to be the default when U-Boot builds
+ (i.e. `CONFIG_DEFAULT_DEVICE_TREE`). However, Binman ensures that the size
+ of each of the alternates is the same as the 'default' one, so they can in
+ principle be 'slotted in' to the appropriate place in the main image.
+
+ The optional `fdt-phase` property indicates the phase to build. In this
+ case, it etype runs fdtgrep to obtain the devicetree subset for that phase,
+ respecting the `bootph-xxx` tags in the devicetree.
+ """
+ def __init__(self, section, etype, node):
+ super().__init__(section, etype, node)
+ self.fdt_list_dir = None
+ self.filename_pattern = None
+ self.required_props = ['fdt-list-dir']
+ self._cur_fdt = None
+ self._fdt_phase = None
+ self.fdtgrep = None
+ self._fdt_dir = None
+ self._fdts = None
+ self._fname_pattern = None
+ self._remove_props = None
+ self.alternates = None
+
+ def ReadNode(self):
+ """Read properties from the node"""
+ super().ReadNode()
+ self._fdt_dir = fdt_util.GetString(self._node, 'fdt-list-dir')
+ fname = tools.get_input_filename(self._fdt_dir)
+ fdts = glob.glob('*.dtb', root_dir=fname)
+ self._fdts = [os.path.splitext(f)[0] for f in fdts]
+
+ self._fdt_phase = fdt_util.GetString(self._node, 'fdt-phase')
+
+ # This is used by Image.WriteAlternates()
+ self.alternates = self._fdts
+
+ self._fname_pattern = fdt_util.GetString(self._node, 'filename-pattern')
+
+ self._remove_props = []
+ props, = self.GetEntryArgsOrProps(
+ [EntryArg('of-spl-remove-props', str)], required=False)
+ if props:
+ self._remove_props = props.split()
+
+ def FdtContents(self, fdt_etype):
+ # If there is no current FDT, just use the normal one
+ if not self._cur_fdt:
+ return self.section.FdtContents(fdt_etype)
+
+ # Find the file to use
+ fname = os.path.join(self._fdt_dir, f'{self._cur_fdt}.dtb')
+ infile = tools.get_input_filename(fname)
+
+ # Run fdtgrep if needed, to remove unwanted nodes and properties
+ if self._fdt_phase:
+ uniq = self.GetUniqueName()
+ outfile = tools.get_output_filename(
+ f'{uniq}.{self._cur_fdt}-{self._fdt_phase}.dtb')
+ self.fdtgrep.create_for_phase(infile, self._fdt_phase, outfile,
+ self._remove_props)
+ return outfile, tools.read_file(outfile)
+ return fname, tools.read_file(infile)
+
+ def ProcessWithFdt(self, alt):
+ """Produce the contents of this entry, using a particular FDT blob
+
+ Args:
+ alt (str): Name of the alternate
+
+ Returns:
+ tuple:
+ str: Filename to use for the alternate's .bin file
+ bytes: Contents of this entry's section, using the selected FDT
+ """
+ pattern = self._fname_pattern or 'NAME.bin'
+ fname = pattern.replace('NAME', alt)
+
+ data = b''
+ try:
+ self._cur_fdt = alt
+ self.ProcessContents()
+ data = self.GetPaddedData()
+ finally:
+ self._cur_fdt = None
+ return fname, data
+
+ def AddBintools(self, btools):
+ super().AddBintools(btools)
+ self.fdtgrep = self.AddBintool(btools, 'fdtgrep')
diff --git a/tools/binman/etype/blob_dtb.py b/tools/binman/etype/blob_dtb.py
index d543de9..b234323 100644
--- a/tools/binman/etype/blob_dtb.py
+++ b/tools/binman/etype/blob_dtb.py
@@ -41,12 +41,12 @@ class Entry_blob_dtb(Entry_blob):
def ObtainContents(self, fake_size=0):
"""Get the device-tree from the list held by the 'state' module"""
self._filename = self.GetDefaultFilename()
- self._pathname, _ = state.GetFdtContents(self.GetFdtEtype())
+ self._pathname, _ = self.FdtContents(self.GetFdtEtype())
return super().ReadBlobContents()
def ProcessContents(self):
"""Re-read the DTB contents so that we get any calculated properties"""
- _, indata = state.GetFdtContents(self.GetFdtEtype())
+ _, indata = self.FdtContents(self.GetFdtEtype())
if self.compress == 'zstd' and self.prepend != 'length':
self.Raise('The zstd compression requires a length header')
@@ -57,7 +57,9 @@ class Entry_blob_dtb(Entry_blob):
def GetFdtEtype(self):
"""Get the entry type of this device tree
- This can be 'u-boot-dtb', 'u-boot-spl-dtb' or 'u-boot-tpl-dtb'
+ This can be 'u-boot-dtb', 'u-boot-spl-dtb', 'u-boot-tpl-dtb' or
+ 'u-boot-vpl-dtb'
+
Returns:
Entry type if any, e.g. 'u-boot-dtb'
"""
diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py
index 2c14b15..ee44e5a 100644
--- a/tools/binman/etype/fit.py
+++ b/tools/binman/etype/fit.py
@@ -5,7 +5,9 @@
"""Entry-type module for producing a FIT"""
+import glob
import libfdt
+import os
from binman.entry import Entry, EntryArg
from binman.etype.section import Entry_section
@@ -87,6 +89,13 @@ class Entry_fit(Entry_section):
fit,fdt-list-val = "dtb1", "dtb2";
+ fit,fdt-list-dir
+ As an alternative to fit,fdt-list the list of device tree files
+ can be provided as a directory. Each .dtb file in the directory is
+ processed, , e.g.::
+
+ fit,fdt-list-dir = "arch/arm/dts
+
Substitutions
~~~~~~~~~~~~~
@@ -162,6 +171,7 @@ class Entry_fit(Entry_section):
firmware = "atf";
loadables = "uboot";
fdt = "fdt-SEQ";
+ fit,compatible; // optional
};
};
@@ -171,6 +181,40 @@ class Entry_fit(Entry_section):
Note that if no devicetree files are provided (with '-a of-list' as above)
then no nodes will be generated.
+ The 'fit,compatible' property (if present) is replaced with the compatible
+ string from the root node of the devicetree, so that things work correctly
+ with FIT's configuration-matching algortihm.
+
+ Dealing with phases
+ ~~~~~~~~~~~~~~~~~~~
+
+ FIT can be used to load firmware. In this case it may be necessary to run
+ the devicetree for each model through fdtgrep to remove unwanted properties.
+ The 'fit,fdt-phase' property can be provided to indicate the phase for which
+ the devicetree is intended.
+
+ For example this indicates that the FDT should be processed for VPL::
+
+ images {
+ @fdt-SEQ {
+ description = "fdt-NAME";
+ type = "flat_dt";
+ compression = "none";
+ fit,fdt-phase = "vpl";
+ };
+ };
+
+ Using this mechanism, it is possible to generate a FIT which can provide VPL
+ images for multiple models, with TPL selecting the correct model to use. The
+ same approach can of course be used for SPL images.
+
+ Note that the `of-spl-remove-props` entryarg can be used to indicate
+ additional properties to remove. It is often used to remove properties like
+ `clock-names` and `pinctrl-names` which are not needed in SPL builds. This
+ value is automatically passed to binman by the U-Boot build.
+
+ See :ref:`fdtgrep_filter` for more information.
+
Generating nodes from an ELF file (split-elf)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -352,9 +396,16 @@ class Entry_fit(Entry_section):
self._fit = None
self._fit_props = {}
self._fdts = None
+ self._fdt_dir = None
self.mkimage = None
+ self.fdtgrep = None
self._priv_entries = {}
self._loadables = []
+ self._remove_props = []
+ props, = self.GetEntryArgsOrProps(
+ [EntryArg('of-spl-remove-props', str)], required=False)
+ if props:
+ self._remove_props = props.split()
def ReadNode(self):
super().ReadNode()
@@ -368,7 +419,14 @@ class Entry_fit(Entry_section):
if fdts is not None:
self._fdts = fdts.split()
else:
- self._fdts = fdt_util.GetStringList(self._node, 'fit,fdt-list-val')
+ self._fdt_dir = fdt_util.GetString(self._node, 'fit,fdt-list-dir')
+ if self._fdt_dir:
+ indir = tools.get_input_filename(self._fdt_dir)
+ fdts = glob.glob('*.dtb', root_dir=indir)
+ self._fdts = [os.path.splitext(f)[0] for f in sorted(fdts)]
+ else:
+ self._fdts = fdt_util.GetStringList(self._node,
+ 'fit,fdt-list-val')
self._fit_default_dt = self.GetEntryArgsOrProps([EntryArg('default-dt',
str)])[0]
@@ -483,6 +541,19 @@ class Entry_fit(Entry_section):
rel_path = node.path[len(self._node.path) + 1:]
self.Raise(f"subnode '{rel_path}': {msg}")
+ def _run_fdtgrep(self, infile, phase, outfile):
+ """Run fdtgrep to create the dtb for a phase
+
+ Args:
+ infile (str): Input filename containing the full FDT contents (with
+ all nodes and properties)
+ phase (str): Phase to generate for ('tpl', 'vpl', 'spl')
+ outfile (str): Output filename to write the grepped FDT contents to
+ (with only neceesary nodes and properties)
+ """
+ return self.fdtgrep.create_for_phase(infile, phase, outfile,
+ self._remove_props)
+
def _build_input(self):
"""Finish the FIT by adding the 'data' properties to it
@@ -584,6 +655,7 @@ class Entry_fit(Entry_section):
for seq, fdt_fname in enumerate(self._fdts):
node_name = node.name[1:].replace('SEQ', str(seq + 1))
fname = tools.get_input_filename(fdt_fname + '.dtb')
+ fdt_phase = None
with fsw.add_node(node_name):
for pname, prop in node.props.items():
if pname == 'fit,firmware':
@@ -594,6 +666,15 @@ class Entry_fit(Entry_section):
fsw.property('loadables', val.encode('utf-8'))
elif pname == 'fit,operation':
pass
+ elif pname == 'fit,compatible':
+ fdt_phase = fdt_util.GetString(node, pname)
+ data = tools.read_file(fname)
+ fdt = Fdt.FromData(data)
+ fdt.Scan()
+ prop = fdt.GetRoot().props['compatible']
+ fsw.property('compatible', prop.bytes)
+ elif pname == 'fit,fdt-phase':
+ fdt_phase = fdt_util.GetString(node, pname)
elif pname.startswith('fit,'):
self._raise_subnode(
node, f"Unknown directive '{pname}'")
@@ -606,7 +687,14 @@ class Entry_fit(Entry_section):
# Add data for 'images' nodes (but not 'config')
if depth == 1 and in_images:
- fsw.property('data', tools.read_file(fname))
+ if fdt_phase:
+ phase_fname = tools.get_output_filename(
+ f'{fdt_fname}-{fdt_phase}.dtb')
+ self._run_fdtgrep(fname, fdt_phase, phase_fname)
+ data = tools.read_file(phase_fname)
+ else:
+ data = tools.read_file(fname)
+ fsw.property('data', data)
for subnode in node.subnodes:
with fsw.add_node(subnode.name):
@@ -834,6 +922,7 @@ class Entry_fit(Entry_section):
def AddBintools(self, btools):
super().AddBintools(btools)
self.mkimage = self.AddBintool(btools, 'mkimage')
+ self.fdtgrep = self.AddBintool(btools, 'fdtgrep')
def CheckMissing(self, missing_list):
# We must use our private entry list for this since generator nodes
diff --git a/tools/binman/etype/u_boot_spl_nodtb.py b/tools/binman/etype/u_boot_spl_nodtb.py
index e7ec329..0e172ae 100644
--- a/tools/binman/etype/u_boot_spl_nodtb.py
+++ b/tools/binman/etype/u_boot_spl_nodtb.py
@@ -23,8 +23,6 @@ class Entry_u_boot_spl_nodtb(Entry_blob):
SPL can access binman symbols at runtime. See :ref:`binman_fdt`.
- in the binman README for more information.
-
The ELF file 'spl/u-boot-spl' must also be available for this to work, since
binman uses that to look up symbols to write into the SPL binary.
"""
diff --git a/tools/binman/etype/u_boot_tpl_nodtb.py b/tools/binman/etype/u_boot_tpl_nodtb.py
index 9bb2b5d..e0c8a55 100644
--- a/tools/binman/etype/u_boot_tpl_nodtb.py
+++ b/tools/binman/etype/u_boot_tpl_nodtb.py
@@ -23,8 +23,6 @@ class Entry_u_boot_tpl_nodtb(Entry_blob):
TPL can access binman symbols at runtime. See :ref:`binman_fdt`.
- in the binman README for more information.
-
The ELF file 'tpl/u-boot-tpl' must also be available for this to work, since
binman uses that to look up symbols to write into the TPL binary.
"""
diff --git a/tools/binman/etype/u_boot_vpl.py b/tools/binman/etype/u_boot_vpl.py
index 31d7e83..0797831 100644
--- a/tools/binman/etype/u_boot_vpl.py
+++ b/tools/binman/etype/u_boot_vpl.py
@@ -27,6 +27,9 @@ class Entry_u_boot_vpl(Entry_blob):
The ELF file 'vpl/u-boot-vpl' must also be available for this to work, since
binman uses that to look up symbols to write into the VPL binary.
+
+ Note that this entry is automatically replaced with u-boot-vpl-expanded
+ unless --no-expanded is used or the node has a 'no-expanded' property.
"""
def __init__(self, section, etype, node):
super().__init__(section, etype, node, auto_write_symbols=True)
diff --git a/tools/binman/etype/u_boot_vpl_nodtb.py b/tools/binman/etype/u_boot_vpl_nodtb.py
index 64c2767..765cf53 100644
--- a/tools/binman/etype/u_boot_vpl_nodtb.py
+++ b/tools/binman/etype/u_boot_vpl_nodtb.py
@@ -16,8 +16,8 @@ class Entry_u_boot_vpl_nodtb(Entry_blob):
This is the U-Boot VPL binary, It does not include a device tree blob at
the end of it so may not be able to work without it, assuming VPL needs
- a device tree to operate on your platform. You can add a u_boot_vpl_dtb
- entry after this one, or use a u_boot_vpl entry instead, which normally
+ a device tree to operate on your platform. You can add a u-boot-vpl-dtb
+ entry after this one, or use a u-boot-vpl entry instead, which normally
expands to a section containing u-boot-vpl-dtb, u-boot-vpl-bss-pad and
u-boot-vpl-dtb
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
index e4da040..93f3d22 100644
--- a/tools/binman/ftest.py
+++ b/tools/binman/ftest.py
@@ -7,6 +7,7 @@
# python -m unittest func_test.TestFunctional.testHelp
import collections
+import glob
import gzip
import hashlib
from optparse import OptionParser
@@ -546,7 +547,7 @@ class TestFunctional(unittest.TestCase):
dtb_data = self._SetupDtb(fname)
# For testing purposes, make a copy of the DT for SPL and TPL. Add
- # a node indicating which it is, so aid verification.
+ # a node indicating which it is, to aid verification.
for name in ['spl', 'tpl', 'vpl']:
dtb_fname = '%s/u-boot-%s.dtb' % (name, name)
outfile = os.path.join(self._indir, dtb_fname)
@@ -4180,8 +4181,8 @@ class TestFunctional(unittest.TestCase):
data = self._DoReadFile('172_scp.dts')
self.assertEqual(SCP_DATA, data[:len(SCP_DATA)])
- def testFitFdt(self):
- """Test an image with an FIT with multiple FDT images"""
+ def CheckFitFdt(self, dts='170_fit_fdt.dts', use_fdt_list=True):
+ """Check an image with an FIT with multiple FDT images"""
def _CheckFdt(seq, expected_data):
"""Check the FDT nodes
@@ -4220,11 +4221,12 @@ class TestFunctional(unittest.TestCase):
self.assertEqual('fdt-%d' % seq, fnode.props['fdt'].value)
entry_args = {
- 'of-list': 'test-fdt1 test-fdt2',
'default-dt': 'test-fdt2',
}
+ if use_fdt_list:
+ entry_args['of-list'] = 'test-fdt1 test-fdt2'
data = self._DoReadFileDtb(
- '170_fit_fdt.dts',
+ 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):])
@@ -4243,6 +4245,10 @@ class TestFunctional(unittest.TestCase):
_CheckConfig(1, TEST_FDT1_DATA)
_CheckConfig(2, TEST_FDT2_DATA)
+ def testFitFdt(self):
+ """Test an image with an FIT with multiple FDT images"""
+ self.CheckFitFdt()
+
def testFitFdtMissingList(self):
"""Test handling of a missing 'of-list' entry arg"""
with self.assertRaises(ValueError) as e:
@@ -7175,27 +7181,24 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
def testSplPubkeyDtb(self):
- """Test u_boot_spl_pubkey_dtb etype"""
- data = tools.read_file(self.TestFile("key.pem"))
- self._MakeInputFile("key.crt", data)
- self._DoReadFileRealDtb('306_spl_pubkey_dtb.dts')
- image = control.images['image']
- entries = image.GetEntries()
- dtb_entry = entries['u-boot-spl-pubkey-dtb']
- dtb_data = dtb_entry.GetData()
- dtb = fdt.Fdt.FromData(dtb_data)
- dtb.Scan()
-
- signature_node = dtb.GetNode('/signature')
- self.assertIsNotNone(signature_node)
- key_node = signature_node.FindNode("key-key")
- self.assertIsNotNone(key_node)
- self.assertEqual(fdt_util.GetString(key_node, "required"),
- "conf")
- self.assertEqual(fdt_util.GetString(key_node, "algo"),
- "sha384,rsa4096")
- self.assertEqual(fdt_util.GetString(key_node, "key-name-hint"),
- "key")
+ """Test u_boot_spl_pubkey_dtb etype"""
+ data = tools.read_file(self.TestFile("key.pem"))
+ self._MakeInputFile("key.crt", data)
+ self._DoReadFileRealDtb('306_spl_pubkey_dtb.dts')
+ image = control.images['image']
+ entries = image.GetEntries()
+ dtb_entry = entries['u-boot-spl-pubkey-dtb']
+ dtb_data = dtb_entry.GetData()
+ dtb = fdt.Fdt.FromData(dtb_data)
+ dtb.Scan()
+
+ signature_node = dtb.GetNode('/signature')
+ self.assertIsNotNone(signature_node)
+ key_node = signature_node.FindNode("key-key")
+ self.assertIsNotNone(key_node)
+ self.assertEqual(fdt_util.GetString(key_node, "required"), "conf")
+ self.assertEqual(fdt_util.GetString(key_node, "algo"), "sha384,rsa4096")
+ self.assertEqual(fdt_util.GetString(key_node, "key-name-hint"), "key")
def testXilinxBootgenSigning(self):
"""Test xilinx-bootgen etype"""
@@ -7487,6 +7490,206 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
err,
"Image '.*' is missing external blobs and is non-functional: .*")
+ def SetupAlternateDts(self):
+ """Compile the .dts test files for alternative-fdt
+
+ Returns:
+ tuple:
+ str: Test directory created
+ list of str: '.bin' files which we expect Binman to create
+ """
+ testdir = TestFunctional._MakeInputDir('dtb')
+ dtb_list = []
+ for fname in glob.glob(f'{self.TestFile("alt_dts")}/*.dts'):
+ tmp_fname = fdt_util.EnsureCompiled(fname, testdir)
+ base = os.path.splitext(os.path.basename(fname))[0]
+ dtb_list.append(base + '.bin')
+ shutil.move(tmp_fname, os.path.join(testdir, base + '.dtb'))
+
+ return testdir, dtb_list
+
+ def CheckAlternates(self, dts, phase, xpl_data):
+ """Run the test for the alterative-fdt etype
+
+ Args:
+ dts (str): Devicetree file to process
+ phase (str): Phase to process ('spl', 'tpl' or 'vpl')
+ xpl_data (bytes): Expected data for the phase's binary
+
+ Returns:
+ dict of .dtb files produced
+ key: str filename
+ value: Fdt object
+ """
+ dtb_list = self.SetupAlternateDts()[1]
+
+ entry_args = {
+ f'{phase}-dtb': '1',
+ f'{phase}-bss-pad': 'y',
+ 'of-spl-remove-props': 'prop-to-remove another-prop-to-get-rid-of',
+ }
+ data = self._DoReadFileDtb(dts, use_real_dtb=True, update_dtb=True,
+ use_expanded=True, entry_args=entry_args)[0]
+ self.assertEqual(xpl_data, data[:len(xpl_data)])
+ rest = data[len(xpl_data):]
+ pad_len = 10
+ self.assertEqual(tools.get_bytes(0, pad_len), rest[:pad_len])
+
+ # Check the dtb is using the test file
+ dtb_data = rest[pad_len:]
+ dtb = fdt.Fdt.FromData(dtb_data)
+ dtb.Scan()
+ fdt_size = dtb.GetFdtObj().totalsize()
+ self.assertEqual('model-not-set',
+ fdt_util.GetString(dtb.GetRoot(), 'compatible'))
+
+ pad_len = 10
+
+ # Check the other output files
+ dtbs = {}
+ for fname in dtb_list:
+ pathname = tools.get_output_filename(fname)
+ self.assertTrue(os.path.exists(pathname))
+
+ data = tools.read_file(pathname)
+ self.assertEqual(xpl_data, data[:len(xpl_data)])
+ rest = data[len(xpl_data):]
+
+ self.assertEqual(tools.get_bytes(0, pad_len), rest[:pad_len])
+ rest = rest[pad_len:]
+
+ dtb = fdt.Fdt.FromData(rest)
+ dtb.Scan()
+ dtbs[fname] = dtb
+
+ expected = 'one' if '1' in fname else 'two'
+ self.assertEqual(f'u-boot,model-{expected}',
+ fdt_util.GetString(dtb.GetRoot(), 'compatible'))
+
+ # Make sure the FDT is the same size as the 'main' one
+ rest = rest[fdt_size:]
+
+ self.assertEqual(b'', rest)
+ return dtbs
+
+ def testAlternatesFdt(self):
+ """Test handling of alternates-fdt etype"""
+ self._SetupTplElf()
+ dtbs = self.CheckAlternates('328_alternates_fdt.dts', 'tpl',
+ U_BOOT_TPL_NODTB_DATA)
+ for dtb in dtbs.values():
+ # Check for the node with the tag
+ node = dtb.GetNode('/node')
+ self.assertIsNotNone(node)
+ self.assertEqual(5, len(node.props.keys()))
+
+ # Make sure the other node is still there
+ self.assertIsNotNone(dtb.GetNode('/node/other-node'))
+
+ def testAlternatesFdtgrep(self):
+ """Test handling of alternates-fdt etype using fdtgrep"""
+ self._SetupTplElf()
+ dtbs = self.CheckAlternates('329_alternates_fdtgrep.dts', 'tpl',
+ U_BOOT_TPL_NODTB_DATA)
+ for dtb in dtbs.values():
+ # Check for the node with the tag
+ node = dtb.GetNode('/node')
+ self.assertIsNotNone(node)
+ self.assertEqual({'some-prop', 'not-a-prop-to-remove'},
+ node.props.keys())
+
+ # Make sure the other node is gone
+ self.assertIsNone(dtb.GetNode('/node/other-node'))
+
+ def testAlternatesFdtgrepVpl(self):
+ """Test handling of alternates-fdt etype using fdtgrep with vpl"""
+ self._SetupVplElf()
+ dtbs = self.CheckAlternates('330_alternates_vpl.dts', 'vpl',
+ U_BOOT_VPL_NODTB_DATA)
+
+ def testAlternatesFdtgrepSpl(self):
+ """Test handling of alternates-fdt etype using fdtgrep with spl"""
+ self._SetupSplElf()
+ dtbs = self.CheckAlternates('331_alternates_spl.dts', 'spl',
+ U_BOOT_SPL_NODTB_DATA)
+
+ def testAlternatesFdtgrepInval(self):
+ """Test alternates-fdt etype using fdtgrep with invalid phase"""
+ self._SetupSplElf()
+ with self.assertRaises(ValueError) as e:
+ dtbs = self.CheckAlternates('332_alternates_inval.dts', 'spl',
+ U_BOOT_SPL_NODTB_DATA)
+ self.assertIn("Invalid U-Boot phase 'bad-phase': Use tpl/vpl/spl",
+ str(e.exception))
+
+ def testFitFdtListDir(self):
+ """Test an image with an FIT with FDT images using fit,fdt-list-dir"""
+ self.CheckFitFdt('333_fit_fdt_dir.dts', False)
+
+ def testFitFdtCompat(self):
+ """Test an image with an FIT with compatible in the config nodes"""
+ entry_args = {
+ 'of-list': 'model1 model2',
+ 'default-dt': 'model2',
+ }
+ testdir, dtb_list = self.SetupAlternateDts()
+ data = self._DoReadFileDtb(
+ '334_fit_fdt_compat.dts', use_real_dtb=True, update_dtb=True,
+ entry_args=entry_args, extra_indirs=[testdir])[0]
+
+ fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
+
+ fit = fdt.Fdt.FromData(fit_data)
+ fit.Scan()
+
+ cnode = fit.GetNode('/configurations')
+ self.assertIn('default', cnode.props)
+ self.assertEqual('config-2', cnode.props['default'].value)
+
+ for seq in range(1, 2):
+ name = f'config-{seq}'
+ fnode = fit.GetNode('/configurations/%s' % name)
+ self.assertIsNotNone(fnode)
+ self.assertIn('compatible', fnode.props.keys())
+ expected = 'one' if seq == 1 else 'two'
+ self.assertEqual(f'u-boot,model-{expected}',
+ fnode.props['compatible'].value)
+
+ def testFitFdtPhase(self):
+ """Test an image with an FIT with fdt-phase in the fdt nodes"""
+ phase = 'tpl'
+ entry_args = {
+ f'{phase}-dtb': '1',
+ f'{phase}-bss-pad': 'y',
+ 'of-spl-remove-props': 'prop-to-remove another-prop-to-get-rid-of',
+ 'of-list': 'model1 model2',
+ 'default-dt': 'model2',
+ }
+ testdir, dtb_list = self.SetupAlternateDts()
+ data = self._DoReadFileDtb(
+ '335_fit_fdt_phase.dts', use_real_dtb=True, update_dtb=True,
+ entry_args=entry_args, extra_indirs=[testdir])[0]
+ fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
+ fit = fdt.Fdt.FromData(fit_data)
+ fit.Scan()
+
+ # Check that each FDT has only the expected properties for the phase
+ for seq in range(1, 2):
+ fnode = fit.GetNode(f'/images/fdt-{seq}')
+ self.assertIsNotNone(fnode)
+ dtb = fdt.Fdt.FromData(fnode.props['data'].bytes)
+ dtb.Scan()
+
+ # Make sure that the 'bootph-pre-sram' tag in /node protects it from
+ # removal
+ node = dtb.GetNode('/node')
+ self.assertIsNotNone(node)
+ self.assertEqual({'some-prop', 'not-a-prop-to-remove'},
+ node.props.keys())
+
+ # Make sure the other node is gone
+ self.assertIsNone(dtb.GetNode('/node/other-node'))
+
if __name__ == "__main__":
unittest.main()
diff --git a/tools/binman/image.py b/tools/binman/image.py
index e77b5d0..702c905 100644
--- a/tools/binman/image.py
+++ b/tools/binman/image.py
@@ -21,6 +21,9 @@ from dtoc import fdt_util
from u_boot_pylib import tools
from u_boot_pylib import tout
+# This is imported if needed
+state = None
+
class Image(section.Entry_section):
"""A Image, representing an output from binman
@@ -75,6 +78,10 @@ class Image(section.Entry_section):
def __init__(self, name, node, copy_to_orig=True, test=False,
ignore_missing=False, use_expanded=False, missing_etype=False,
generate=True):
+ # Put this here to allow entry-docs and help to work without libfdt
+ global state
+ from binman import state
+
super().__init__(None, 'section', node, test=test)
self.copy_to_orig = copy_to_orig
self.name = name
@@ -186,6 +193,19 @@ class Image(section.Entry_section):
os.remove(sname)
os.symlink(fname, sname)
+ def WriteAlternates(self):
+ """Write out alternative devicetree blobs, each in its own file"""
+ alt_entry = self.FindEntryType('alternates-fdt')
+ if not alt_entry:
+ return
+
+ for alt in alt_entry.alternates:
+ fname, data = alt_entry.ProcessWithFdt(alt)
+ pathname = tools.get_output_filename(fname)
+ tout.info(f"Writing alternate '{alt}' to '{pathname}'")
+ tools.write_file(pathname, data)
+ tout.info("Wrote %#x bytes" % len(data))
+
def WriteMap(self):
"""Write a map of the image to a .map file
@@ -418,3 +438,7 @@ class Image(section.Entry_section):
super().AddBintools(bintools)
self.bintools = bintools
return bintools
+
+ def FdtContents(self, fdt_etype):
+ """This base-class implementation simply calls the state function"""
+ return state.GetFdtContents(fdt_etype)
diff --git a/tools/binman/main.py b/tools/binman/main.py
index 92d2431..dc817dd 100755
--- a/tools/binman/main.py
+++ b/tools/binman/main.py
@@ -122,6 +122,8 @@ def RunBinman(args):
ret_code = RunTests(args.debug, args.verbosity, args.processes,
args.test_preserve_dirs, args.tests,
args.toolpath)
+ if args.debug and not test_util.use_concurrent:
+ print('Tests can run in parallel: pip install concurrencytest')
elif args.cmd == 'bintool-docs':
control.write_bintool_docs(bintool.Bintool.get_tool_list())
diff --git a/tools/binman/test/328_alternates_fdt.dts b/tools/binman/test/328_alternates_fdt.dts
new file mode 100644
index 0000000..c913c8e
--- /dev/null
+++ b/tools/binman/test/328_alternates_fdt.dts
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2024 Google LLC
+// Written by Simon Glass <sjg@chromium.org>
+
+/dts-v1/;
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ compatible = "model-not-set";
+
+ binman {
+ alternates-fdt {
+ fdt-list-dir = "dtb";
+ filename-pattern = "NAME.bin";
+
+ section {
+ u-boot-tpl {
+ };
+ };
+ };
+
+ blob {
+ filename = "blobfile";
+ };
+ };
+};
diff --git a/tools/binman/test/329_alternates_fdtgrep.dts b/tools/binman/test/329_alternates_fdtgrep.dts
new file mode 100644
index 0000000..4169528
--- /dev/null
+++ b/tools/binman/test/329_alternates_fdtgrep.dts
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2024 Google LLC
+// Written by Simon Glass <sjg@chromium.org>
+
+/dts-v1/;
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ compatible = "model-not-set";
+
+ binman {
+ alternates-fdt {
+ fdt-list-dir = "dtb";
+ filename-pattern = "NAME.bin";
+ fdt-phase = "tpl";
+
+ section {
+ u-boot-tpl {
+ };
+ };
+ };
+
+ blob {
+ filename = "blobfile";
+ };
+ };
+};
diff --git a/tools/binman/test/330_alternates_vpl.dts b/tools/binman/test/330_alternates_vpl.dts
new file mode 100644
index 0000000..5b57069
--- /dev/null
+++ b/tools/binman/test/330_alternates_vpl.dts
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2024 Google LLC
+// Written by Simon Glass <sjg@chromium.org>
+
+/dts-v1/;
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ compatible = "model-not-set";
+
+ binman {
+ alternates-fdt {
+ fdt-list-dir = "dtb";
+ filename-pattern = "NAME.bin";
+ fdt-phase = "vpl";
+
+ section {
+ u-boot-vpl {
+ };
+ };
+ };
+
+ blob {
+ filename = "blobfile";
+ };
+ };
+};
diff --git a/tools/binman/test/331_alternates_spl.dts b/tools/binman/test/331_alternates_spl.dts
new file mode 100644
index 0000000..882fefc
--- /dev/null
+++ b/tools/binman/test/331_alternates_spl.dts
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2024 Google LLC
+// Written by Simon Glass <sjg@chromium.org>
+
+/dts-v1/;
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ compatible = "model-not-set";
+
+ binman {
+ alternates-fdt {
+ fdt-list-dir = "dtb";
+ filename-pattern = "NAME.bin";
+ fdt-phase = "spl";
+
+ section {
+ u-boot-spl {
+ };
+ };
+ };
+
+ blob {
+ filename = "blobfile";
+ };
+ };
+};
diff --git a/tools/binman/test/332_alternates_inval.dts b/tools/binman/test/332_alternates_inval.dts
new file mode 100644
index 0000000..8c145dd
--- /dev/null
+++ b/tools/binman/test/332_alternates_inval.dts
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2024 Google LLC
+// Written by Simon Glass <sjg@chromium.org>
+
+/dts-v1/;
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ compatible = "model-not-set";
+
+ binman {
+ alternates-fdt {
+ fdt-list-dir = "dtb";
+ filename-pattern = "NAME.bin";
+ fdt-phase = "bad-phase";
+
+ section {
+ u-boot-spl {
+ };
+ };
+ };
+
+ blob {
+ filename = "blobfile";
+ };
+ };
+};
diff --git a/tools/binman/test/333_fit_fdt_dir.dts b/tools/binman/test/333_fit_fdt_dir.dts
new file mode 100644
index 0000000..aa77845
--- /dev/null
+++ b/tools/binman/test/333_fit_fdt_dir.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-dir = "fdts";
+
+ 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/334_fit_fdt_compat.dts b/tools/binman/test/334_fit_fdt_compat.dts
new file mode 100644
index 0000000..3bf45c7
--- /dev/null
+++ b/tools/binman/test/334_fit_fdt_compat.dts
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ binman {
+ u-boot {
+ };
+ fit {
+ description = "test-desc";
+ #address-cells = <1>;
+ fit,fdt-list = "of-list";
+
+ images {
+ kernel {
+ description = "Vanilla Linux kernel";
+ type = "kernel";
+ arch = "ppc";
+ os = "linux";
+ compression = "gzip";
+ load = <00000000>;
+ entry = <00000000>;
+ hash-1 {
+ algo = "crc32";
+ };
+ hash-2 {
+ algo = "sha1";
+ };
+ u-boot {
+ };
+ };
+ @fdt-SEQ {
+ description = "fdt-NAME.dtb";
+ type = "flat_dt";
+ compression = "none";
+ hash {
+ algo = "sha256";
+ };
+ };
+ };
+
+ configurations {
+ default = "@config-DEFAULT-SEQ";
+ @config-SEQ {
+ description = "conf-NAME.dtb";
+ firmware = "uboot";
+ loadables = "atf";
+ fdt = "fdt-SEQ";
+ fit,firmware = "vpl";
+ fit,compatible;
+ };
+ };
+ };
+ u-boot-nodtb {
+ };
+ };
+};
diff --git a/tools/binman/test/335_fit_fdt_phase.dts b/tools/binman/test/335_fit_fdt_phase.dts
new file mode 100644
index 0000000..f8d0740
--- /dev/null
+++ b/tools/binman/test/335_fit_fdt_phase.dts
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ binman {
+ u-boot {
+ };
+ fit {
+ description = "test-desc";
+ #address-cells = <1>;
+ fit,fdt-list = "of-list";
+
+ images {
+ kernel {
+ description = "Vanilla Linux kernel";
+ type = "kernel";
+ arch = "ppc";
+ os = "linux";
+ compression = "gzip";
+ load = <00000000>;
+ entry = <00000000>;
+ hash-1 {
+ algo = "crc32";
+ };
+ hash-2 {
+ algo = "sha1";
+ };
+ u-boot {
+ };
+ };
+ @fdt-SEQ {
+ description = "fdt-NAME.dtb";
+ type = "flat_dt";
+ compression = "none";
+ fit,fdt-phase = "tpl";
+ hash {
+ algo = "sha256";
+ };
+ };
+ };
+
+ configurations {
+ default = "@config-DEFAULT-SEQ";
+ @config-SEQ {
+ description = "conf-NAME.dtb";
+ firmware = "uboot";
+ loadables = "atf";
+ fdt = "fdt-SEQ";
+ fit,firmware = "tpl";
+ fit,compatible;
+ };
+ };
+ };
+ u-boot-nodtb {
+ };
+ };
+};
diff --git a/tools/binman/test/alt_dts/model1.dts b/tools/binman/test/alt_dts/model1.dts
new file mode 100644
index 0000000..01e95e8
--- /dev/null
+++ b/tools/binman/test/alt_dts/model1.dts
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2024 Google LLC
+// Written by Simon Glass <sjg@chromium.org>
+
+/dts-v1/;
+
+/ {
+ model = "Model One";
+ compatible = "u-boot,model-one";
+
+ /* this node remains due to bootph-pre-sram tag */
+ node {
+ some-prop;
+ prop-to-remove;
+ another-prop-to-get-rid-of;
+ not-a-prop-to-remove;
+ bootph-pre-sram;
+
+ /* this node get removed by fdtgrep */
+ other-node {
+ another-prop;
+ };
+ };
+};
diff --git a/tools/binman/test/alt_dts/model2.dts b/tools/binman/test/alt_dts/model2.dts
new file mode 100644
index 0000000..7829c51
--- /dev/null
+++ b/tools/binman/test/alt_dts/model2.dts
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2024 Google LLC
+// Written by Simon Glass <sjg@chromium.org>
+
+/dts-v1/;
+
+/ {
+ model = "Model Two";
+ compatible = "u-boot,model-two";
+
+ /* this node remains due to bootph-pre-sram tag */
+ node {
+ some-prop;
+ prop-to-remove;
+ another-prop-to-get-rid-of;
+ not-a-prop-to-remove;
+ bootph-pre-sram;
+
+ /* this node get removed by fdtgrep */
+ other-node {
+ another-prop;
+ };
+ };
+};
diff --git a/tools/qconfig.py b/tools/qconfig.py
index 4088079..7b868c7 100755
--- a/tools/qconfig.py
+++ b/tools/qconfig.py
@@ -1079,7 +1079,7 @@ def do_imply_config(config_list, add_imply, imply_flags, skip_added,
for linenum in sorted(linenums, reverse=True):
add_imply_rule(config[CONFIG_LEN:], fname, linenum)
-def defconfig_matches(configs, re_match):
+def defconfig_matches(configs, re_match, re_val):
"""Check if any CONFIG option matches a regex
The match must be complete, i.e. from the start to end of the CONFIG option.
@@ -1089,16 +1089,18 @@ def defconfig_matches(configs, re_match):
key: CONFIG option
value: Value of option
re_match (re.Pattern): Match to check
+ re_val (re.Pattern): Regular expression to check against value (or None)
Returns:
bool: True if any CONFIG matches the regex
"""
- for cfg in configs:
+ for cfg, val in configs.items():
if re_match.fullmatch(cfg):
- return True
+ if not re_val or re_val.fullmatch(val):
+ return True
return False
-def do_find_config(config_list):
+def do_find_config(config_list, list_format):
"""Find boards with a given combination of CONFIGs
Args:
@@ -1106,6 +1108,8 @@ def do_find_config(config_list):
consisting of a config option, with or without a CONFIG_ prefix. If
an option is preceded by a tilde (~) then it must be false,
otherwise it must be true)
+ list_format (bool): True to write in 'list' format, one board name per
+ line
Returns:
int: exit code (0 for success)
@@ -1123,6 +1127,11 @@ def do_find_config(config_list):
if cfg[0] == '~':
want = False
cfg = cfg[1:]
+ val = None
+ re_val = None
+ if '=' in cfg:
+ cfg, val = cfg.split('=', maxsplit=1)
+ re_val = re.compile(val)
# Search everything that is still in the running. If it has a config
# that we want, or doesn't have one that we don't, add it into the
@@ -1131,11 +1140,13 @@ def do_find_config(config_list):
out = set()
re_match = re.compile(cfg)
for defc in in_list:
- has_cfg = defconfig_matches(config_db[defc], re_match)
+ has_cfg = defconfig_matches(config_db[defc], re_match, re_val)
if has_cfg == want:
out.add(defc)
- print(f'{len(out)} matches')
- print(' '.join(item.split('_defconfig')[0] for item in out))
+ if not list_format:
+ print(f'{len(out)} matches')
+ sep = '\n' if list_format else ' '
+ print(sep.join(item.split('_defconfig')[0] for item in sorted(list(out))))
return 0
@@ -1528,6 +1539,8 @@ doc/develop/moveconfig.rst for documentation.'''
help='Find boards with a given config combination')
parser.add_argument('-i', '--imply', action='store_true', default=False,
help='find options which imply others')
+ parser.add_argument('-l', '--list', action='store_true', default=False,
+ help='Show a sorted list of board names, one per line')
parser.add_argument('-I', '--imply-flags', type=str, default='',
help="control the -i option ('help' for help")
parser.add_argument('-j', '--jobs', type=int, default=cpu_count,
@@ -1681,7 +1694,7 @@ def main():
sys.exit(1)
return 0
if args.find:
- return do_find_config(args.configs)
+ return do_find_config(args.configs, args.list)
config_db, progress = move_config(args)