qconfig minor improvement
binman support for alternate entries
This commit is contained in:
Tom Rini
2024-07-29 10:22:42 -06:00
33 changed files with 1242 additions and 106 deletions

View File

@@ -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

View File

@@ -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
-----------------------

View File

@@ -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

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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.

View File

@@ -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:

View File

@@ -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')

View File

@@ -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)
has_problems = CheckForProblems(image)
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])
image.WriteAlternates()
return has_problems
def Binman(args):
"""The main control code for binman

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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')

View File

@@ -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'
"""

View File

@@ -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

View File

@@ -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.
"""

View File

@@ -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.
"""

View File

@@ -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)

View File

@@ -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

View File

@@ -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()
"""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")
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()

View File

@@ -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)

View File

@@ -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())

View File

@@ -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";
};
};
};

View File

@@ -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";
};
};
};

View File

@@ -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";
};
};
};

View File

@@ -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";
};
};
};

View File

@@ -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";
};
};
};

View File

@@ -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 {
};
};
};

View File

@@ -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 {
};
};
};

View File

@@ -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 {
};
};
};

View File

@@ -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;
};
};
};

View File

@@ -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;
};
};
};

View File

@@ -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)