aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--arch/x86/dts/u-boot.dtsi2
-rw-r--r--common/spl/Kconfig24
-rw-r--r--common/spl/spl.c12
-rw-r--r--drivers/phy/phy-uclass.c20
-rw-r--r--include/spl.h4
-rw-r--r--scripts/dtc/libfdt/fdt_ro.c8
-rw-r--r--tools/binman/binman.rst98
-rw-r--r--tools/binman/elf.py37
-rw-r--r--tools/binman/elf_test.py31
-rw-r--r--tools/binman/entries.rst124
-rw-r--r--tools/binman/entry.py51
-rw-r--r--tools/binman/etype/fit.py323
-rw-r--r--tools/binman/etype/mkimage.py27
-rw-r--r--tools/binman/etype/tee_os.py22
-rw-r--r--tools/binman/ftest.py164
-rw-r--r--tools/binman/missing-blob-help4
-rw-r--r--tools/binman/state.py2
-rw-r--r--tools/binman/test/221_fit_subentry_hash.dts52
-rw-r--r--tools/binman/test/222_tee_os.dts14
-rw-r--r--tools/binman/test/223_fit_fdt_oper.dts56
-rw-r--r--tools/binman/test/224_fit_bad_oper.dts27
-rw-r--r--tools/binman/test/Makefile6
-rw-r--r--tools/binman/test/elf_sections.c20
-rw-r--r--tools/binman/test/elf_sections.lds31
-rw-r--r--tools/dtoc/fdt.py37
-rw-r--r--tools/dtoc/fdt_util.py12
-rw-r--r--tools/dtoc/test/dtoc_test_simple.dts1
-rwxr-xr-xtools/dtoc/test_fdt.py43
-rwxr-xr-xtools/moveconfig.py39
30 files changed, 1140 insertions, 152 deletions
diff --git a/Makefile b/Makefile
index 4b15224..697cc51 100644
--- a/Makefile
+++ b/Makefile
@@ -1327,6 +1327,7 @@ cmd_binman = $(srctree)/tools/binman/binman $(if $(BINMAN_DEBUG),-D) \
-I arch/$(ARCH)/dts -a of-list=$(CONFIG_OF_LIST) \
$(foreach f,$(BINMAN_INDIRS),-I $(f)) \
-a atf-bl31-path=${BL31} \
+ -a tee-os-path=${TEE} \
-a opensbi-path=${OPENSBI} \
-a default-dt=$(default_dt) \
-a scp-path=$(SCP) \
diff --git a/arch/x86/dts/u-boot.dtsi b/arch/x86/dts/u-boot.dtsi
index ca84d18..24e692f 100644
--- a/arch/x86/dts/u-boot.dtsi
+++ b/arch/x86/dts/u-boot.dtsi
@@ -37,7 +37,7 @@
u-boot-tpl-dtb {
};
#endif
- spl {
+ u-boot-spl {
type = "u-boot-spl";
offset = <CONFIG_X86_OFFSET_SPL>;
};
diff --git a/common/spl/Kconfig b/common/spl/Kconfig
index e0d0a6f..9418d37 100644
--- a/common/spl/Kconfig
+++ b/common/spl/Kconfig
@@ -101,6 +101,18 @@ config SPL_SHOW_ERRORS
This adds a small amount to SPL code size, perhaps 100 bytes.
+config SPL_BINMAN_SYMBOLS
+ bool "Declare binman symbols in SPL"
+ depends on SPL_FRAMEWORK && BINMAN
+ default y
+ help
+ This enables use of symbols in SPL which refer to U-Boot, enabling SPL
+ to obtain the location of U-Boot simply by calling spl_get_image_pos()
+ and spl_get_image_size().
+
+ For this to work, you must have a U-Boot image in the binman image, so
+ binman can update SPL with the location of it.
+
menu "PowerPC and LayerScape SPL Boot options"
config SPL_NAND_BOOT
@@ -1321,6 +1333,18 @@ config TPL_SIZE_LIMIT
Specifies the maximum length of the U-Boot TPL image.
If this value is zero, it is ignored.
+config TPL_BINMAN_SYMBOLS
+ bool "Declare binman symbols in SPL"
+ depends on SPL_FRAMEWORK && BINMAN
+ default y
+ help
+ This enables use of symbols in TPL which refer to U-Boot, enabling SPL
+ to obtain the location of U-Boot simply by calling spl_get_image_pos()
+ and spl_get_image_size().
+
+ For this to work, you must have a U-Boot image in the binman image, so
+ binman can update SPL with the location of it.
+
config TPL_FRAMEWORK
bool "Support TPL based upon the common SPL framework"
default y if SPL_FRAMEWORK
diff --git a/common/spl/spl.c b/common/spl/spl.c
index 884102b..b452d4f 100644
--- a/common/spl/spl.c
+++ b/common/spl/spl.c
@@ -49,13 +49,15 @@ DECLARE_GLOBAL_DATA_PTR;
u32 *boot_params_ptr = NULL;
+#if CONFIG_IS_ENABLED(BINMAN_SYMBOLS)
/* See spl.h for information about this */
binman_sym_declare(ulong, u_boot_any, image_pos);
binman_sym_declare(ulong, u_boot_any, size);
+#endif
#ifdef CONFIG_TPL
-binman_sym_declare(ulong, spl, image_pos);
-binman_sym_declare(ulong, spl, size);
+binman_sym_declare(ulong, u_boot_spl, image_pos);
+binman_sym_declare(ulong, u_boot_spl, size);
#endif
/* Define board data structure */
@@ -140,19 +142,21 @@ void spl_fixup_fdt(void *fdt_blob)
#endif
}
+#if CONFIG_IS_ENABLED(BINMAN_SYMBOLS)
ulong spl_get_image_pos(void)
{
return spl_phase() == PHASE_TPL ?
- binman_sym(ulong, spl, image_pos) :
+ binman_sym(ulong, u_boot_spl, image_pos) :
binman_sym(ulong, u_boot_any, image_pos);
}
ulong spl_get_image_size(void)
{
return spl_phase() == PHASE_TPL ?
- binman_sym(ulong, spl, size) :
+ binman_sym(ulong, u_boot_spl, size) :
binman_sym(ulong, u_boot_any, size);
}
+#endif /* BINMAN_SYMBOLS */
ulong spl_get_image_text_base(void)
{
diff --git a/drivers/phy/phy-uclass.c b/drivers/phy/phy-uclass.c
index 49e2ec2..8b84da3 100644
--- a/drivers/phy/phy-uclass.c
+++ b/drivers/phy/phy-uclass.c
@@ -354,23 +354,31 @@ int generic_phy_configure(struct phy *phy, void *params)
int generic_phy_get_bulk(struct udevice *dev, struct phy_bulk *bulk)
{
int i, ret, count;
+ struct udevice *phydev = dev;
bulk->count = 0;
/* Return if no phy declared */
- if (!dev_read_prop(dev, "phys", NULL))
- return 0;
+ if (!dev_read_prop(dev, "phys", NULL)) {
+ phydev = dev->parent;
+ if (!dev_read_prop(phydev, "phys", NULL)) {
+ pr_err("%s : no phys property\n", __func__);
+ return 0;
+ }
+ }
- count = dev_count_phandle_with_args(dev, "phys", "#phy-cells", 0);
- if (count < 1)
+ count = dev_count_phandle_with_args(phydev, "phys", "#phy-cells", 0);
+ if (count < 1) {
+ pr_err("%s : no phys found %d\n", __func__, count);
return count;
+ }
- bulk->phys = devm_kcalloc(dev, count, sizeof(struct phy), GFP_KERNEL);
+ bulk->phys = devm_kcalloc(phydev, count, sizeof(struct phy), GFP_KERNEL);
if (!bulk->phys)
return -ENOMEM;
for (i = 0; i < count; i++) {
- ret = generic_phy_get_by_index(dev, i, &bulk->phys[i]);
+ ret = generic_phy_get_by_index(phydev, i, &bulk->phys[i]);
if (ret) {
pr_err("Failed to get PHY%d for %s\n", i, dev->name);
return ret;
diff --git a/include/spl.h b/include/spl.h
index bb92bc6..8ceb3c0 100644
--- a/include/spl.h
+++ b/include/spl.h
@@ -269,8 +269,8 @@ struct spl_load_info {
*/
binman_sym_extern(ulong, u_boot_any, image_pos);
binman_sym_extern(ulong, u_boot_any, size);
-binman_sym_extern(ulong, spl, image_pos);
-binman_sym_extern(ulong, spl, size);
+binman_sym_extern(ulong, u_boot_spl, image_pos);
+binman_sym_extern(ulong, u_boot_spl, size);
/**
* spl_get_image_pos() - get the image position of the next phase
diff --git a/scripts/dtc/libfdt/fdt_ro.c b/scripts/dtc/libfdt/fdt_ro.c
index efe7efe..63eaf57 100644
--- a/scripts/dtc/libfdt/fdt_ro.c
+++ b/scripts/dtc/libfdt/fdt_ro.c
@@ -937,4 +937,10 @@ int fdt_check_full(const void *fdt, size_t bufsize)
}
}
}
-#endif
+#else
+int fdt_check_full(const void __always_unused *fdt,
+ size_t __always_unused bufsize)
+{
+ return 0;
+}
+#endif /* #if !defined(FDT_ASSUME_MASK) || FDT_ASSUME_MASK != 0xff */
diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst
index ab5a5e0..7716453 100644
--- a/tools/binman/binman.rst
+++ b/tools/binman/binman.rst
@@ -163,8 +163,8 @@ Consider sunxi. It has the following steps:
Binman is intended to replace the last step. The U-Boot build system builds
u-boot.bin and sunxi-spl.bin. Binman can then take over creation of
-sunxi-spl.bin (by calling mksunxiboot, or hopefully one day mkimage). In any
-case, it would then create the image from the component parts.
+sunxi-spl.bin by calling mksunxiboot or mkimage. In any case, it would then
+create the image from the component parts.
This simplifies the U-Boot Makefile somewhat, since various pieces of logic
can be replaced by a call to binman.
@@ -1358,6 +1358,92 @@ development, since dealing with exceptions and problems in threads is more
difficult. This avoids any use of ThreadPoolExecutor.
+Collecting data for an entry type
+---------------------------------
+
+Some entry types deal with data obtained from others. For example,
+`Entry_mkimage` calls the `mkimage` tool with data from its subnodes::
+
+ mkimage {
+ args = "-n test -T script";
+
+ u-boot-spl {
+ };
+
+ u-boot {
+ };
+ };
+
+This shows mkimage being passed a file consisting of SPL and U-Boot proper. It
+is create by calling `Entry.collect_contents_to_file()`. Note that in this case,
+the data is passed to mkimage for processing but does not appear separately in
+the image. It may not appear at all, depending on what mkimage does. The
+contents of the `mkimage` entry are entirely dependent on the processing done
+by the entry, with the provided subnodes (`u-boot-spl` and `u-boot`) simply
+providing the input data for that processing.
+
+Note that `Entry.collect_contents_to_file()` simply concatenates the data from
+the different entries together, with no control over alignment, etc. Another
+approach is to subclass `Entry_section` so that those features become available,
+such as `size` and `pad-byte`. Then the contents of the entry can be obtained by
+calling `BuildSectionData()`.
+
+There are other ways to obtain data also, depending on the situation. If the
+entry type is simply signing data which exists elsewhere in the image, then
+you can use `Entry_collection` as a base class. It lets you use a property
+called `content` which lists the entries containing data to be processed. This
+is used by `Entry_vblock`, for example::
+
+ u_boot: u-boot {
+ };
+ vblock {
+ content = <&u_boot &dtb>;
+ keyblock = "firmware.keyblock";
+ signprivate = "firmware_data_key.vbprivk";
+ version = <1>;
+ kernelkey = "kernel_subkey.vbpubk";
+ preamble-flags = <1>;
+ };
+
+ dtb: u-boot-dtb {
+ };
+
+which shows an image containing `u-boot` and `u-boot-dtb`, with the `vblock`
+image collecting their contents to produce input for its signing process,
+without affecting those entries, which still appear in the final image
+untouched.
+
+Another example is where an entry type needs several independent pieces of input
+to function. For example, `Entry_fip` allows a number of different binary blobs
+to be placed in their own individual places in a custom data structure in the
+output image. To make that work you can add subnodes for each of them and call
+`Entry.Create()` on each subnode, as `Entry_fip` does. Then the data for each
+blob can come from any suitable place, such as an `Entry_u_boot` or an
+`Entry_blob` or anything else::
+
+ atf-fip {
+ fip-hdr-flags = /bits/ 64 <0x123>;
+ soc-fw {
+ fip-flags = /bits/ 64 <0x123456789abcdef>;
+ filename = "bl31.bin";
+ };
+
+ u-boot {
+ fip-uuid = [fc 65 13 92 4a 5b 11 ec
+ 94 35 ff 2d 1c fc 79 9c];
+ };
+ };
+
+The `soc-fw` node is a `blob-ext` (i.e. it reads in a named binary file) whereas
+`u-boot` is a normal entry type. This works because `Entry_fip` selects the
+`blob-ext` entry type if the node name (here `soc-fw`) is recognised as being
+a known blob type.
+
+When adding new entry types you are encouraged to use subnodes to provide the
+data for processing, unless the `content` approach is more suitable. Ad-hoc
+properties and other methods of obtaining data are discouraged, since it adds to
+confusion for users.
+
History / Credits
-----------------
@@ -1402,6 +1488,14 @@ Some ideas:
- Detect invalid properties in nodes
- Sort the fdtmap by offset
- Output temporary files to a different directory
+- Rationalise the fdt, fdt_util and pylibfdt modules which currently have some
+ overlapping and confusing functionality
+- Update the fdt library to use a better format for Prop.value (the current one
+ is useful for dtoc but not much else)
+- Figure out how to make Fdt support changing the node order, so that
+ Node.AddSubnode() can support adding a node before another, existing node.
+ Perhaps it should completely regenerate the flat tree?
+
--
Simon Glass <sjg@chromium.org>
diff --git a/tools/binman/elf.py b/tools/binman/elf.py
index bc4966e..5e7d6ae 100644
--- a/tools/binman/elf.py
+++ b/tools/binman/elf.py
@@ -20,6 +20,7 @@ from patman import tout
ELF_TOOLS = True
try:
from elftools.elf.elffile import ELFFile
+ from elftools.elf.elffile import ELFError
from elftools.elf.sections import SymbolTableSection
except: # pragma: no cover
ELF_TOOLS = False
@@ -369,3 +370,39 @@ def UpdateFile(infile, outfile, start_sym, end_sym, insert):
newdata += data[syms[end_sym].offset:]
tools.write_file(outfile, newdata)
tout.info('Written to offset %#x' % syms[start_sym].offset)
+
+def read_segments(data):
+ """Read segments from an ELF file
+
+ Args:
+ data (bytes): Contents of file
+
+ Returns:
+ tuple:
+ list of segments, each:
+ int: Segment number (0 = first)
+ int: Start address of segment in memory
+ bytes: Contents of segment
+ int: entry address for image
+
+ Raises:
+ ValueError: elftools is not available
+ """
+ if not ELF_TOOLS:
+ raise ValueError('Python elftools package is not available')
+ with io.BytesIO(data) as inf:
+ try:
+ elf = ELFFile(inf)
+ except ELFError as err:
+ raise ValueError(err)
+ entry = elf.header['e_entry']
+ segments = []
+ for i in range(elf.num_segments()):
+ segment = elf.get_segment(i)
+ if segment['p_type'] != 'PT_LOAD' or not segment['p_memsz']:
+ skipped = 1 # To make code-coverage see this line
+ continue
+ start = segment['p_offset']
+ rend = start + segment['p_filesz']
+ segments.append((i, segment['p_paddr'], data[start:rend]))
+ return segments, entry
diff --git a/tools/binman/elf_test.py b/tools/binman/elf_test.py
index 47ebfba..a67915b 100644
--- a/tools/binman/elf_test.py
+++ b/tools/binman/elf_test.py
@@ -56,8 +56,8 @@ class FakeSection:
def BuildElfTestFiles(target_dir):
"""Build ELF files used for testing in binman
- This compiles and links the test files into the specified directory. It the
- Makefile and source files in the binman test/ directory.
+ This compiles and links the test files into the specified directory. It uses
+ the Makefile and source files in the binman test/ directory.
Args:
target_dir: Directory to put the files into
@@ -258,6 +258,33 @@ class TestElf(unittest.TestCase):
offset = elf.GetSymbolFileOffset(fname, ['missing_sym'])
self.assertEqual({}, offset)
+ def test_read_segments(self):
+ """Test for read_segments()"""
+ if not elf.ELF_TOOLS:
+ self.skipTest('Python elftools not available')
+ fname = self.ElfTestFile('embed_data')
+ segments, entry = elf.read_segments(tools.read_file(fname))
+
+ def test_read_segments_fail(self):
+ """Test for read_segments() without elftools"""
+ try:
+ old_val = elf.ELF_TOOLS
+ elf.ELF_TOOLS = False
+ fname = self.ElfTestFile('embed_data')
+ with self.assertRaises(ValueError) as e:
+ elf.read_segments(tools.read_file(fname))
+ self.assertIn('Python elftools package is not available',
+ str(e.exception))
+ finally:
+ elf.ELF_TOOLS = old_val
+
+ def test_read_segments_bad_data(self):
+ """Test for read_segments() with an invalid ELF file"""
+ fname = self.ElfTestFile('embed_data')
+ with self.assertRaises(ValueError) as e:
+ elf.read_segments(tools.get_bytes(100, 100))
+ self.assertIn('Magic number does not match', str(e.exception))
+
if __name__ == '__main__':
unittest.main()
diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst
index c47f7df..484cde5 100644
--- a/tools/binman/entries.rst
+++ b/tools/binman/entries.rst
@@ -553,11 +553,73 @@ For example, this creates an image containing a FIT with U-Boot SPL::
};
};
+More complex setups can be created, with generated nodes, as described
+below.
+
+Properties (in the 'fit' node itself)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Special properties have a `fit,` prefix, indicating that they should be
+processed but not included in the final FIT.
+
+The top-level 'fit' node supports the following special properties:
+
+ fit,external-offset
+ Indicates that the contents of the FIT are external and provides the
+ external offset. This is passed to mkimage via the -E and -p flags.
+
+ fit,fdt-list
+ Indicates the entry argument which provides the list of device tree
+ files for the gen-fdt-nodes operation (as below). This is often
+ `of-list` meaning that `-a of-list="dtb1 dtb2..."` should be passed
+ to binman.
+
+Substitutions
+~~~~~~~~~~~~~
+
+Node names and property values support a basic string-substitution feature.
+Available substitutions for '@' nodes (and property values) are:
+
+SEQ:
+ Sequence number of the generated fdt (1, 2, ...)
+NAME
+ Name of the dtb as provided (i.e. without adding '.dtb')
+
+The `default` property, if present, will be automatically set to the name
+if of configuration whose devicetree matches the `default-dt` entry
+argument, e.g. with `-a default-dt=sun50i-a64-pine64-lts`.
+
+Available substitutions for property values in these nodes are:
+
+DEFAULT-SEQ:
+ Sequence number of the default fdt, as provided by the 'default-dt'
+ entry argument
+
+Available operations
+~~~~~~~~~~~~~~~~~~~~
+
+You can add an operation to an '@' node to indicate which operation is
+required::
+
+ @fdt-SEQ {
+ fit,operation = "gen-fdt-nodes";
+ ...
+ };
+
+Available operations are:
+
+gen-fdt-nodes
+ Generate FDT nodes as above. This is the default if there is no
+ `fit,operation` property.
+
+Generating nodes from an FDT list (gen-fdt-nodes)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
U-Boot supports creating fdt and config nodes automatically. To do this,
-pass an of-list property (e.g. -a of-list=file1 file2). This tells binman
-that you want to generates nodes for two files: file1.dtb and file2.dtb
-The fit,fdt-list property (see above) indicates that of-list should be used.
-If the property is missing you will get an error.
+pass an `of-list` property (e.g. `-a of-list=file1 file2`). This tells
+binman that you want to generates nodes for two files: `file1.dtb` and
+`file2.dtb`. The `fit,fdt-list` property (see above) indicates that
+`of-list` should be used. If the property is missing you will get an error.
Then add a 'generator node', a node with a name starting with '@'::
@@ -569,10 +631,11 @@ Then add a 'generator node', a node with a name starting with '@'::
};
};
-This tells binman to create nodes fdt-1 and fdt-2 for each of your two
+This tells binman to create nodes `fdt-1` and `fdt-2` for each of your two
files. All the properties you specify will be included in the node. This
node acts like a template to generate the nodes. The generator node itself
does not appear in the output - it is replaced with what binman generates.
+A 'data' property is created with the contents of the FDT file.
You can create config nodes in a similar way::
@@ -586,35 +649,12 @@ You can create config nodes in a similar way::
};
};
-This tells binman to create nodes config-1 and config-2, i.e. a config for
-each of your two files.
-
-Available substitutions for '@' nodes are:
-
-SEQ:
- Sequence number of the generated fdt (1, 2, ...)
-NAME
- Name of the dtb as provided (i.e. without adding '.dtb')
+This tells binman to create nodes `config-1` and `config-2`, i.e. a config
+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 'default' property, if present, will be automatically set to the name
-if of configuration whose devicetree matches the 'default-dt' entry
-argument, e.g. with '-a default-dt=sun50i-a64-pine64-lts'.
-
-Available substitutions for '@' property values are
-
-DEFAULT-SEQ:
- Sequence number of the default fdt,as provided by the 'default-dt' entry
- argument
-
-Properties (in the 'fit' node itself):
- fit,external-offset: Indicates that the contents of the FIT are external
- and provides the external offset. This is passsed to mkimage via
- the -E and -p flags.
-
-
Entry: fmap: An entry which contains an Fmap section
@@ -931,6 +971,17 @@ This calls mkimage to create an imximage with u-boot-spl.bin as the input
file. The output from mkimage then becomes part of the image produced by
binman.
+To use CONFIG options in the arguments, use a string list instead, as in
+this example which also produces four arguments::
+
+ mkimage {
+ args = "-n", CONFIG_SYS_SOC, "-T imximage";
+
+ u-boot-spl {
+ };
+ };
+
+
Entry: opensbi: RISC-V OpenSBI fw_dynamic blob
@@ -1103,6 +1154,19 @@ available. This is set by the `SetAllowMissing()` method, if
+Entry: tee-os: Entry containing an OP-TEE Trusted OS (TEE) blob
+---------------------------------------------------------------
+
+Properties / Entry arguments:
+ - tee-os-path: Filename of file to read into entry. This is typically
+ called tee-pager.bin
+
+This entry holds the run-time firmware, typically started by U-Boot SPL.
+See the U-Boot README for your architecture or board for how to use it. See
+https://github.com/OP-TEE/optee_os for more information about OP-TEE.
+
+
+
Entry: text: An entry which contains text
-----------------------------------------
diff --git a/tools/binman/entry.py b/tools/binman/entry.py
index dc26f8f..bf68a85 100644
--- a/tools/binman/entry.py
+++ b/tools/binman/entry.py
@@ -78,6 +78,8 @@ class Entry(object):
external: True if this entry contains an external binary blob
bintools: Bintools used by this entry (only populated for Image)
missing_bintools: List of missing bintools for this entry
+ update_hash: True if this entry's "hash" subnode should be
+ updated with a hash of the entry contents
"""
def __init__(self, section, etype, node, name_prefix=''):
# Put this here to allow entry-docs and help to work without libfdt
@@ -111,6 +113,7 @@ class Entry(object):
self.allow_fake = False
self.bintools = {}
self.missing_bintools = []
+ self.update_hash = True
@staticmethod
def FindEntryClass(etype, expanded):
@@ -315,9 +318,11 @@ class Entry(object):
if self.compress != 'none':
state.AddZeroProp(self._node, 'uncomp-size')
- err = state.CheckAddHashProp(self._node)
- if err:
- self.Raise(err)
+
+ if self.update_hash:
+ err = state.CheckAddHashProp(self._node)
+ if err:
+ self.Raise(err)
def SetCalculatedProperties(self):
"""Set the value of device-tree properties calculated by binman"""
@@ -333,7 +338,9 @@ class Entry(object):
state.SetInt(self._node, 'orig-size', self.orig_size, True)
if self.uncomp_size is not None:
state.SetInt(self._node, 'uncomp-size', self.uncomp_size)
- state.CheckSetHashValue(self._node, self.GetData)
+
+ if self.update_hash:
+ state.CheckSetHashValue(self._node, self.GetData)
def ProcessFdt(self, fdt):
"""Allow entries to adjust the device tree
@@ -1108,3 +1115,39 @@ features to produce new behaviours.
btool = bintool.Bintool.create(name)
tools[name] = btool
return btool
+
+ def SetUpdateHash(self, update_hash):
+ """Set whether this entry's "hash" subnode should be updated
+
+ Args:
+ update_hash: True if hash should be updated, False if not
+ """
+ self.update_hash = update_hash
+
+ def collect_contents_to_file(self, entries, prefix):
+ """Put the contents of a list of entries into a file
+
+ Args:
+ entries (list of Entry): Entries to collect
+ prefix (str): Filename prefix of file to write to
+
+ If any entry does not have contents yet, this function returns False
+ for the data.
+
+ Returns:
+ Tuple:
+ bytes: Concatenated data from all the entries (or False)
+ str: Filename of file written (or False if no data)
+ str: Unique portion of filename (or False if no data)
+ """
+ data = b''
+ for entry in entries:
+ # First get the input data and put it in a file. If not available,
+ # try later.
+ if not entry.ObtainContents():
+ return False, False, False
+ data += entry.GetData()
+ uniq = self.GetUniqueName()
+ fname = tools.get_output_filename(f'{prefix}.{uniq}')
+ tools.write_file(fname, data)
+ return data, fname, uniq
diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py
index a56b056..2d4c5f6 100644
--- a/tools/binman/etype/fit.py
+++ b/tools/binman/etype/fit.py
@@ -14,7 +14,14 @@ from dtoc import fdt_util
from dtoc.fdt import Fdt
from patman import tools
+# Supported operations, with the fit,operation property
+OP_GEN_FDT_NODES = range(1)
+OPERATIONS = {
+ 'gen-fdt-nodes': OP_GEN_FDT_NODES,
+ }
+
class Entry_fit(Entry_section):
+
"""Flat Image Tree (FIT)
This calls mkimage to create a FIT (U-Boot Flat Image Tree) based on the
@@ -47,11 +54,73 @@ class Entry_fit(Entry_section):
};
};
+ More complex setups can be created, with generated nodes, as described
+ below.
+
+ Properties (in the 'fit' node itself)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Special properties have a `fit,` prefix, indicating that they should be
+ processed but not included in the final FIT.
+
+ The top-level 'fit' node supports the following special properties:
+
+ fit,external-offset
+ Indicates that the contents of the FIT are external and provides the
+ external offset. This is passed to mkimage via the -E and -p flags.
+
+ fit,fdt-list
+ Indicates the entry argument which provides the list of device tree
+ files for the gen-fdt-nodes operation (as below). This is often
+ `of-list` meaning that `-a of-list="dtb1 dtb2..."` should be passed
+ to binman.
+
+ Substitutions
+ ~~~~~~~~~~~~~
+
+ Node names and property values support a basic string-substitution feature.
+ Available substitutions for '@' nodes (and property values) are:
+
+ SEQ:
+ Sequence number of the generated fdt (1, 2, ...)
+ NAME
+ Name of the dtb as provided (i.e. without adding '.dtb')
+
+ The `default` property, if present, will be automatically set to the name
+ if of configuration whose devicetree matches the `default-dt` entry
+ argument, e.g. with `-a default-dt=sun50i-a64-pine64-lts`.
+
+ Available substitutions for property values in these nodes are:
+
+ DEFAULT-SEQ:
+ Sequence number of the default fdt, as provided by the 'default-dt'
+ entry argument
+
+ Available operations
+ ~~~~~~~~~~~~~~~~~~~~
+
+ You can add an operation to an '@' node to indicate which operation is
+ required::
+
+ @fdt-SEQ {
+ fit,operation = "gen-fdt-nodes";
+ ...
+ };
+
+ Available operations are:
+
+ gen-fdt-nodes
+ Generate FDT nodes as above. This is the default if there is no
+ `fit,operation` property.
+
+ Generating nodes from an FDT list (gen-fdt-nodes)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
U-Boot supports creating fdt and config nodes automatically. To do this,
- pass an of-list property (e.g. -a of-list=file1 file2). This tells binman
- that you want to generates nodes for two files: file1.dtb and file2.dtb
- The fit,fdt-list property (see above) indicates that of-list should be used.
- If the property is missing you will get an error.
+ pass an `of-list` property (e.g. `-a of-list=file1 file2`). This tells
+ binman that you want to generates nodes for two files: `file1.dtb` and
+ `file2.dtb`. The `fit,fdt-list` property (see above) indicates that
+ `of-list` should be used. If the property is missing you will get an error.
Then add a 'generator node', a node with a name starting with '@'::
@@ -63,10 +132,11 @@ class Entry_fit(Entry_section):
};
};
- This tells binman to create nodes fdt-1 and fdt-2 for each of your two
+ This tells binman to create nodes `fdt-1` and `fdt-2` for each of your two
files. All the properties you specify will be included in the node. This
node acts like a template to generate the nodes. The generator node itself
does not appear in the output - it is replaced with what binman generates.
+ A 'data' property is created with the contents of the FDT file.
You can create config nodes in a similar way::
@@ -80,34 +150,11 @@ class Entry_fit(Entry_section):
};
};
- This tells binman to create nodes config-1 and config-2, i.e. a config for
- each of your two files.
-
- Available substitutions for '@' nodes are:
-
- SEQ:
- Sequence number of the generated fdt (1, 2, ...)
- NAME
- Name of the dtb as provided (i.e. without adding '.dtb')
+ This tells binman to create nodes `config-1` and `config-2`, i.e. a config
+ 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 'default' property, if present, will be automatically set to the name
- if of configuration whose devicetree matches the 'default-dt' entry
- argument, e.g. with '-a default-dt=sun50i-a64-pine64-lts'.
-
- Available substitutions for '@' property values are
-
- DEFAULT-SEQ:
- Sequence number of the default fdt,as provided by the 'default-dt' entry
- argument
-
- Properties (in the 'fit' node itself):
- fit,external-offset: Indicates that the contents of the FIT are external
- and provides the external offset. This is passsed to mkimage via
- the -E and -p flags.
-
"""
def __init__(self, section, etype, node):
"""
@@ -141,13 +188,120 @@ class Entry_fit(Entry_section):
self.ReadEntries()
super().ReadNode()
+ def _get_operation(self, subnode):
+ """Get the operation referenced by a subnode
+
+ Args:
+ subnode (Node): Subnode (of the FIT) to check
+
+ Returns:
+ int: Operation to perform
+
+ Raises:
+ ValueError: Invalid operation name
+ """
+ oper_name = subnode.props.get('fit,operation')
+ if not oper_name:
+ return OP_GEN_FDT_NODES
+ oper = OPERATIONS.get(oper_name.value)
+ if not oper:
+ self.Raise(f"Unknown operation '{oper_name.value}'")
+ return oper
+
def ReadEntries(self):
+ def _process_prop(pname, prop):
+ """Process special properties
+
+ Handles properties with generated values. At present the only
+ supported property is 'default', i.e. the default device tree in
+ the configurations node.
+
+ Args:
+ pname (str): Name of property
+ prop (Prop): Property to process
+ """
+ if pname == 'default':
+ val = prop.value
+ # Handle the 'default' property
+ if val.startswith('@'):
+ if not self._fdts:
+ return
+ if not self._fit_default_dt:
+ self.Raise("Generated 'default' node requires default-dt entry argument")
+ if self._fit_default_dt not in self._fdts:
+ self.Raise("default-dt entry argument '%s' not found in fdt list: %s" %
+ (self._fit_default_dt,
+ ', '.join(self._fdts)))
+ seq = self._fdts.index(self._fit_default_dt)
+ val = val[1:].replace('DEFAULT-SEQ', str(seq + 1))
+ fsw.property_string(pname, val)
+ return
+ fsw.property(pname, prop.bytes)
+
+ def _scan_gen_fdt_nodes(subnode, depth, in_images):
+ """Generate FDT nodes
+
+ This creates one node for each member of self._fdts using the
+ provided template. If a property value contains 'NAME' it is
+ replaced with the filename of the FDT. If a property value contains
+ SEQ it is replaced with the node sequence number, where 1 is the
+ first.
+
+ Args:
+ subnode (None): Generator node to process
+ depth: Current node depth (0 is the base 'fit' node)
+ in_images: True if this is inside the 'images' node, so that
+ 'data' properties should be generated
+ """
+ if self._fdts:
+ # Generate nodes for each FDT
+ for seq, fdt_fname in enumerate(self._fdts):
+ node_name = subnode.name[1:].replace('SEQ', str(seq + 1))
+ fname = tools.get_input_filename(fdt_fname + '.dtb')
+ with fsw.add_node(node_name):
+ for pname, prop in subnode.props.items():
+ val = prop.bytes.replace(
+ b'NAME', tools.to_bytes(fdt_fname))
+ val = val.replace(
+ b'SEQ', tools.to_bytes(str(seq + 1)))
+ fsw.property(pname, val)
+
+ # Add data for 'images' nodes (but not 'config')
+ if depth == 1 and in_images:
+ fsw.property('data', tools.read_file(fname))
+ else:
+ if self._fdts is None:
+ if self._fit_list_prop:
+ self.Raise("Generator node requires '%s' entry argument" %
+ self._fit_list_prop.value)
+ else:
+ self.Raise("Generator node requires 'fit,fdt-list' property")
+
+ def _scan_node(subnode, depth, in_images):
+ """Generate nodes from a template
+
+ This creates one node for each member of self._fdts using the
+ provided template. If a property value contains 'NAME' it is
+ replaced with the filename of the FDT. If a property value contains
+ SEQ it is replaced with the node sequence number, where 1 is the
+ first.
+
+ Args:
+ subnode (None): Generator node to process
+ depth: Current node depth (0 is the base 'fit' node)
+ in_images: True if this is inside the 'images' node, so that
+ 'data' properties should be generated
+ """
+ oper = self._get_operation(subnode)
+ if oper == OP_GEN_FDT_NODES:
+ _scan_gen_fdt_nodes(subnode, depth, in_images)
+
def _AddNode(base_node, depth, node):
"""Add a node to the FIT
Args:
base_node: Base Node of the FIT (with 'description' property)
- depth: Current node depth (0 is the base node)
+ depth: Current node depth (0 is the base 'fit' node)
node: Current node to process
There are two cases to deal with:
@@ -157,23 +311,7 @@ class Entry_fit(Entry_section):
"""
for pname, prop in node.props.items():
if not pname.startswith('fit,'):
- if pname == 'default':
- val = prop.value
- # Handle the 'default' property
- if val.startswith('@'):
- if not self._fdts:
- continue
- if not self._fit_default_dt:
- self.Raise("Generated 'default' node requires default-dt entry argument")
- if self._fit_default_dt not in self._fdts:
- self.Raise("default-dt entry argument '%s' not found in fdt list: %s" %
- (self._fit_default_dt,
- ', '.join(self._fdts)))
- seq = self._fdts.index(self._fit_default_dt)
- val = val[1:].replace('DEFAULT-SEQ', str(seq + 1))
- fsw.property_string(pname, val)
- continue
- fsw.property(pname, prop.bytes)
+ _process_prop(pname, prop)
rel_path = node.path[len(base_node.path):]
in_images = rel_path.startswith('/images')
@@ -186,6 +324,8 @@ class Entry_fit(Entry_section):
# 'data' property later.
entry = Entry.Create(self.section, node, etype='section')
entry.ReadNode()
+ # The hash subnodes here are for mkimage, not binman.
+ entry.SetUpdateHash(False)
self._entries[rel_path] = entry
for subnode in node.subnodes:
@@ -196,31 +336,7 @@ class Entry_fit(Entry_section):
# fsw.add_node() or _AddNode() for it.
pass
elif self.GetImage().generate and subnode.name.startswith('@'):
- if self._fdts:
- # Generate notes for each FDT
- for seq, fdt_fname in enumerate(self._fdts):
- node_name = subnode.name[1:].replace('SEQ',
- str(seq + 1))
- fname = tools.get_input_filename(fdt_fname + '.dtb')
- with fsw.add_node(node_name):
- for pname, prop in subnode.props.items():
- val = prop.bytes.replace(
- b'NAME', tools.to_bytes(fdt_fname))
- val = val.replace(
- b'SEQ', tools.to_bytes(str(seq + 1)))
- fsw.property(pname, val)
-
- # Add data for 'fdt' nodes (but not 'config')
- if depth == 1 and in_images:
- fsw.property('data',
- tools.read_file(fname))
- else:
- if self._fdts is None:
- if self._fit_list_prop:
- self.Raise("Generator node requires '%s' entry argument" %
- self._fit_list_prop.value)
- else:
- self.Raise("Generator node requires 'fit,fdt-list' property")
+ _scan_node(subnode, depth, in_images)
else:
with fsw.add_node(subnode.name):
_AddNode(base_node, depth + 1, subnode)
@@ -291,16 +407,57 @@ class Entry_fit(Entry_section):
data = fdt.GetContents()
return data
+ def SetImagePos(self, image_pos):
+ """Set the position in the image
+
+ This sets each subentry's offsets, sizes and positions-in-image
+ according to where they ended up in the packed FIT file.
+
+ Args:
+ image_pos: Position of this entry in the image
+ """
+ super().SetImagePos(image_pos)
+
+ # If mkimage is missing we'll have empty data,
+ # which will cause a FDT_ERR_BADMAGIC error
+ if self.mkimage in self.missing_bintools:
+ return
+
+ fdt = Fdt.FromData(self.GetData())
+ fdt.Scan()
+
+ for path, section in self._entries.items():
+ node = fdt.GetNode(path)
+
+ data_prop = node.props.get("data")
+ data_pos = fdt_util.GetInt(node, "data-position")
+ data_offset = fdt_util.GetInt(node, "data-offset")
+ data_size = fdt_util.GetInt(node, "data-size")
+
+ # Contents are inside the FIT
+ if data_prop is not None:
+ # GetOffset() returns offset of a fdt_property struct,
+ # which has 3 fdt32_t members before the actual data.
+ offset = data_prop.GetOffset() + 12
+ size = len(data_prop.bytes)
+
+ # External offset from the base of the FIT
+ elif data_pos is not None:
+ offset = data_pos
+ size = data_size
+
+ # External offset from the end of the FIT, not used in binman
+ elif data_offset is not None: # pragma: no cover
+ offset = fdt.GetFdtObj().totalsize() + data_offset
+ size = data_size
+
+ # This should never happen
+ else: # pragma: no cover
+ self.Raise("%s: missing data properties" % (path))
+
+ section.SetOffsetSize(offset, size)
+ section.SetImagePos(self.image_pos)
+
def AddBintools(self, tools):
super().AddBintools(tools)
self.mkimage = self.AddBintool(tools, 'mkimage')
-
- def AddMissingProperties(self, have_image_pos):
- # We don't want to interfere with any hash properties in the FIT, so
- # disable this for now.
- pass
-
- def SetCalculatedProperties(self):
- # We don't want to interfere with any hash properties in the FIT, so
- # disable this for now.
- pass
diff --git a/tools/binman/etype/mkimage.py b/tools/binman/etype/mkimage.py
index baa16f3..8e74ebb 100644
--- a/tools/binman/etype/mkimage.py
+++ b/tools/binman/etype/mkimage.py
@@ -31,25 +31,30 @@ class Entry_mkimage(Entry):
This calls mkimage to create an imximage with u-boot-spl.bin as the input
file. The output from mkimage then becomes part of the image produced by
binman.
+
+ To use CONFIG options in the arguments, use a string list instead, as in
+ this example which also produces four arguments::
+
+ mkimage {
+ args = "-n", CONFIG_SYS_SOC, "-T imximage";
+
+ u-boot-spl {
+ };
+ };
+
"""
def __init__(self, section, etype, node):
super().__init__(section, etype, node)
- self._args = fdt_util.GetString(self._node, 'args').split(' ')
+ self._args = fdt_util.GetArgs(self._node, 'args')
self._mkimage_entries = OrderedDict()
self.align_default = None
self.ReadEntries()
def ObtainContents(self):
- data = b''
- for entry in self._mkimage_entries.values():
- # First get the input data and put it in a file. If not available,
- # try later.
- if not entry.ObtainContents():
- return False
- data += entry.GetData()
- uniq = self.GetUniqueName()
- input_fname = tools.get_output_filename('mkimage.%s' % uniq)
- tools.write_file(input_fname, data)
+ data, input_fname, uniq = self.collect_contents_to_file(
+ self._mkimage_entries.values(), 'mkimage')
+ if data is False:
+ return False
output_fname = tools.get_output_filename('mkimage-out.%s' % uniq)
if self.mkimage.run_cmd('-d', input_fname, *self._args,
output_fname) is not None:
diff --git a/tools/binman/etype/tee_os.py b/tools/binman/etype/tee_os.py
new file mode 100644
index 0000000..6ce4b67
--- /dev/null
+++ b/tools/binman/etype/tee_os.py
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (C) 2022 Texas Instruments Incorporated - https://www.ti.com/
+#
+# Entry-type module for OP-TEE Trusted OS firmware blob
+#
+
+from binman.etype.blob_named_by_arg import Entry_blob_named_by_arg
+
+class Entry_tee_os(Entry_blob_named_by_arg):
+ """Entry containing an OP-TEE Trusted OS (TEE) blob
+
+ Properties / Entry arguments:
+ - tee-os-path: Filename of file to read into entry. This is typically
+ called tee-pager.bin
+
+ This entry holds the run-time firmware, typically started by U-Boot SPL.
+ See the U-Boot README for your architecture or board for how to use it. See
+ https://github.com/OP-TEE/optee_os for more information about OP-TEE.
+ """
+ def __init__(self, section, etype, node):
+ super().__init__(section, etype, node, 'tee-os')
+ self.external = True
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
index 59b6d52..8f00db6 100644
--- a/tools/binman/ftest.py
+++ b/tools/binman/ftest.py
@@ -84,6 +84,7 @@ FSP_M_DATA = b'fsp_m'
FSP_S_DATA = b'fsp_s'
FSP_T_DATA = b'fsp_t'
ATF_BL31_DATA = b'bl31'
+TEE_OS_DATA = b'this is some tee OS data'
ATF_BL2U_DATA = b'bl2u'
OPENSBI_DATA = b'opensbi'
SCP_DATA = b'scp'
@@ -188,6 +189,7 @@ class TestFunctional(unittest.TestCase):
TestFunctional._MakeInputFile('compress', COMPRESS_DATA)
TestFunctional._MakeInputFile('compress_big', COMPRESS_DATA_BIG)
TestFunctional._MakeInputFile('bl31.bin', ATF_BL31_DATA)
+ TestFunctional._MakeInputFile('tee-pager.bin', TEE_OS_DATA)
TestFunctional._MakeInputFile('bl2u.bin', ATF_BL2U_DATA)
TestFunctional._MakeInputFile('fw_dynamic.bin', OPENSBI_DATA)
TestFunctional._MakeInputFile('scp.bin', SCP_DATA)
@@ -2076,7 +2078,7 @@ class TestFunctional(unittest.TestCase):
def testHashBadAlgo(self):
with self.assertRaises(ValueError) as e:
self._DoReadFileDtb('092_hash_bad_algo.dts', update_dtb=True)
- self.assertIn("Node '/binman/u-boot': Unknown hash algorithm",
+ self.assertIn("Node '/binman/u-boot': Unknown hash algorithm 'invalid'",
str(e.exception))
def testHashSection(self):
@@ -3770,6 +3772,62 @@ class TestFunctional(unittest.TestCase):
self._CheckSimpleFitData(fit_data, U_BOOT_EXP_DATA, U_BOOT_SPL_DTB_DATA)
+ def testSimpleFitImagePos(self):
+ """Test that we have correct image-pos for FIT subentries"""
+ data, _, _, out_dtb_fname = self._DoReadFileDtb('161_fit.dts',
+ update_dtb=True)
+ dtb = fdt.Fdt(out_dtb_fname)
+ dtb.Scan()
+ props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
+
+ self.assertEqual({
+ 'image-pos': 0,
+ 'offset': 0,
+ 'size': 1890,
+
+ 'u-boot:image-pos': 0,
+ 'u-boot:offset': 0,
+ 'u-boot:size': 4,
+
+ 'fit:image-pos': 4,
+ 'fit:offset': 4,
+ 'fit:size': 1840,
+
+ 'fit/images/kernel:image-pos': 160,
+ 'fit/images/kernel:offset': 156,
+ 'fit/images/kernel:size': 4,
+
+ 'fit/images/kernel/u-boot:image-pos': 160,
+ 'fit/images/kernel/u-boot:offset': 0,
+ 'fit/images/kernel/u-boot:size': 4,
+
+ 'fit/images/fdt-1:image-pos': 456,
+ 'fit/images/fdt-1:offset': 452,
+ 'fit/images/fdt-1:size': 6,
+
+ 'fit/images/fdt-1/u-boot-spl-dtb:image-pos': 456,
+ 'fit/images/fdt-1/u-boot-spl-dtb:offset': 0,
+ 'fit/images/fdt-1/u-boot-spl-dtb:size': 6,
+
+ 'u-boot-nodtb:image-pos': 1844,
+ 'u-boot-nodtb:offset': 1844,
+ 'u-boot-nodtb:size': 46,
+ }, props)
+
+ # Actually check the data is where we think it is
+ for node, expected in [
+ ("u-boot", U_BOOT_DATA),
+ ("fit/images/kernel", U_BOOT_DATA),
+ ("fit/images/kernel/u-boot", U_BOOT_DATA),
+ ("fit/images/fdt-1", U_BOOT_SPL_DTB_DATA),
+ ("fit/images/fdt-1/u-boot-spl-dtb", U_BOOT_SPL_DTB_DATA),
+ ("u-boot-nodtb", U_BOOT_NODTB_DATA),
+ ]:
+ image_pos = props[f"{node}:image-pos"]
+ size = props[f"{node}:size"]
+ self.assertEqual(len(expected), size)
+ self.assertEqual(expected, data[image_pos:image_pos+size])
+
def testFitExternal(self):
"""Test an image with an FIT with external images"""
data = self._DoReadFile('162_fit_external.dts')
@@ -3798,6 +3856,62 @@ class TestFunctional(unittest.TestCase):
self.assertEqual(U_BOOT_DATA + b'aa',
data[actual_pos:actual_pos + external_data_size])
+ def testFitExternalImagePos(self):
+ """Test that we have correct image-pos for external FIT subentries"""
+ data, _, _, out_dtb_fname = self._DoReadFileDtb('162_fit_external.dts',
+ update_dtb=True)
+ dtb = fdt.Fdt(out_dtb_fname)
+ dtb.Scan()
+ props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
+
+ self.assertEqual({
+ 'image-pos': 0,
+ 'offset': 0,
+ 'size': 1082,
+
+ 'u-boot:image-pos': 0,
+ 'u-boot:offset': 0,
+ 'u-boot:size': 4,
+
+ 'fit:size': 1032,
+ 'fit:offset': 4,
+ 'fit:image-pos': 4,
+
+ 'fit/images/kernel:size': 4,
+ 'fit/images/kernel:offset': 1024,
+ 'fit/images/kernel:image-pos': 1028,
+
+ 'fit/images/kernel/u-boot:size': 4,
+ 'fit/images/kernel/u-boot:offset': 0,
+ 'fit/images/kernel/u-boot:image-pos': 1028,
+
+ 'fit/images/fdt-1:size': 2,
+ 'fit/images/fdt-1:offset': 1028,
+ 'fit/images/fdt-1:image-pos': 1032,
+
+ 'fit/images/fdt-1/_testing:size': 2,
+ 'fit/images/fdt-1/_testing:offset': 0,
+ 'fit/images/fdt-1/_testing:image-pos': 1032,
+
+ 'u-boot-nodtb:image-pos': 1036,
+ 'u-boot-nodtb:offset': 1036,
+ 'u-boot-nodtb:size': 46,
+ }, props)
+
+ # Actually check the data is where we think it is
+ for node, expected in [
+ ("u-boot", U_BOOT_DATA),
+ ("fit/images/kernel", U_BOOT_DATA),
+ ("fit/images/kernel/u-boot", U_BOOT_DATA),
+ ("fit/images/fdt-1", b'aa'),
+ ("fit/images/fdt-1/_testing", b'aa'),
+ ("u-boot-nodtb", U_BOOT_NODTB_DATA),
+ ]:
+ image_pos = props[f"{node}:image-pos"]
+ size = props[f"{node}:size"]
+ self.assertEqual(len(expected), size)
+ self.assertEqual(expected, data[image_pos:image_pos+size])
+
def testFitMissing(self):
"""Test that binman still produces a FIT image if mkimage is missing"""
with test_util.capture_sys_output() as (_, stderr):
@@ -5160,5 +5274,53 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
self.assertRegex(err,
"Image 'main-section'.*missing bintools.*: futility")
+ def testFitSubentryHashSubnode(self):
+ """Test an image with a FIT inside"""
+ data, _, _, out_dtb_name = self._DoReadFileDtb(
+ '221_fit_subentry_hash.dts', use_real_dtb=True, update_dtb=True)
+
+ mkimage_dtb = fdt.Fdt.FromData(data)
+ mkimage_dtb.Scan()
+ binman_dtb = fdt.Fdt(out_dtb_name)
+ binman_dtb.Scan()
+
+ # Check that binman didn't add hash values
+ fnode = binman_dtb.GetNode('/binman/fit/images/kernel/hash')
+ self.assertNotIn('value', fnode.props)
+
+ fnode = binman_dtb.GetNode('/binman/fit/images/fdt-1/hash')
+ self.assertNotIn('value', fnode.props)
+
+ # Check that mkimage added hash values
+ fnode = mkimage_dtb.GetNode('/images/kernel/hash')
+ self.assertIn('value', fnode.props)
+
+ fnode = mkimage_dtb.GetNode('/images/fdt-1/hash')
+ self.assertIn('value', fnode.props)
+
+ def testPackTeeOs(self):
+ """Test that an image with an TEE binary can be created"""
+ data = self._DoReadFile('222_tee_os.dts')
+ self.assertEqual(TEE_OS_DATA, data[:len(TEE_OS_DATA)])
+
+ def testFitFdtOper(self):
+ """Check handling of a specified FIT operation"""
+ entry_args = {
+ 'of-list': 'test-fdt1 test-fdt2',
+ 'default-dt': 'test-fdt2',
+ }
+ self._DoReadFileDtb(
+ '223_fit_fdt_oper.dts',
+ entry_args=entry_args,
+ extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
+
+ def testFitFdtBadOper(self):
+ """Check handling of an FDT map when the section cannot be found"""
+ with self.assertRaises(ValueError) as exc:
+ self._DoReadFileDtb('224_fit_bad_oper.dts')
+ self.assertIn("Node '/binman/fit': Unknown operation 'unknown'",
+ str(exc.exception))
+
+
if __name__ == "__main__":
unittest.main()
diff --git a/tools/binman/missing-blob-help b/tools/binman/missing-blob-help
index 551ca87..c61ca02 100644
--- a/tools/binman/missing-blob-help
+++ b/tools/binman/missing-blob-help
@@ -33,3 +33,7 @@ k3-rti-wdt-firmware:
If CONFIG_WDT_K3_RTI_LOAD_FW is enabled, a firmware image is needed for
the R5F core(s) to trigger the system reset. One possible source is
https://github.com/siemens/k3-rti-wdt.
+
+tee-os:
+See the documentation for your board. You may need to build Open Portable
+Trusted Execution Environment (OP-TEE) with TEE=/path/to/tee.bin
diff --git a/tools/binman/state.py b/tools/binman/state.py
index 8cd8a48..a302e1f 100644
--- a/tools/binman/state.py
+++ b/tools/binman/state.py
@@ -397,7 +397,7 @@ def CheckAddHashProp(node):
if algo.value == 'sha256':
size = 32
else:
- return "Unknown hash algorithm '%s'" % algo
+ return "Unknown hash algorithm '%s'" % algo.value
for n in GetUpdateNodes(hash_node):
n.AddEmptyProp('value', size)
diff --git a/tools/binman/test/221_fit_subentry_hash.dts b/tools/binman/test/221_fit_subentry_hash.dts
new file mode 100644
index 0000000..2cb04f9
--- /dev/null
+++ b/tools/binman/test/221_fit_subentry_hash.dts
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ binman {
+ fit {
+ description = "test-desc";
+ #address-cells = <1>;
+
+ images {
+ kernel {
+ description = "Vanilla Linux kernel";
+ type = "kernel";
+ arch = "ppc";
+ os = "linux";
+ compression = "gzip";
+ load = <00000000>;
+ entry = <00000000>;
+ hash {
+ algo = "sha1";
+ };
+ u-boot {
+ };
+ };
+ fdt-1 {
+ description = "Flattened Device Tree blob";
+ type = "flat_dt";
+ arch = "ppc";
+ compression = "none";
+ hash {
+ algo = "crc32";
+ };
+ u-boot-spl-dtb {
+ };
+ };
+ };
+
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ description = "Boot Linux kernel with FDT blob";
+ kernel = "kernel";
+ fdt = "fdt-1";
+ };
+ };
+ };
+ };
+};
diff --git a/tools/binman/test/222_tee_os.dts b/tools/binman/test/222_tee_os.dts
new file mode 100644
index 0000000..6885497
--- /dev/null
+++ b/tools/binman/test/222_tee_os.dts
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ binman {
+ tee-os {
+ filename = "tee-pager.bin";
+ };
+ };
+};
diff --git a/tools/binman/test/223_fit_fdt_oper.dts b/tools/binman/test/223_fit_fdt_oper.dts
new file mode 100644
index 0000000..e630165
--- /dev/null
+++ b/tools/binman/test/223_fit_fdt_oper.dts
@@ -0,0 +1,56 @@
+// 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 {
+ fit,operation = "gen-fdt-nodes";
+ description = "fdt-NAME.dtb";
+ type = "flat_dt";
+ compression = "none";
+ };
+ };
+
+ 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/224_fit_bad_oper.dts b/tools/binman/test/224_fit_bad_oper.dts
new file mode 100644
index 0000000..cee801e
--- /dev/null
+++ b/tools/binman/test/224_fit_bad_oper.dts
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ binman {
+ fit {
+ description = "test-desc";
+ #address-cells = <1>;
+ fit,fdt-list = "of-list";
+
+ images {
+ @fdt-SEQ {
+ fit,operation = "unknown";
+ description = "fdt-NAME.dtb";
+ type = "flat_dt";
+ compression = "none";
+ };
+ };
+ };
+ fdtmap {
+ };
+ };
+};
diff --git a/tools/binman/test/Makefile b/tools/binman/test/Makefile
index 387ba16..57057e2 100644
--- a/tools/binman/test/Makefile
+++ b/tools/binman/test/Makefile
@@ -29,11 +29,12 @@ LDS_BINMAN := -T $(SRC)u_boot_binman_syms.lds
LDS_BINMAN_BAD := -T $(SRC)u_boot_binman_syms_bad.lds
LDS_BINMAN_X86 := -T $(SRC)u_boot_binman_syms_x86.lds
LDS_BINMAN_EMBED := -T $(SRC)u_boot_binman_embed.lds
+LDS_EFL_SECTIONS := -T $(SRC)elf_sections.lds
TARGETS = u_boot_ucode_ptr u_boot_no_ucode_ptr bss_data \
u_boot_binman_syms u_boot_binman_syms.bin u_boot_binman_syms_bad \
u_boot_binman_syms_size u_boot_binman_syms_x86 embed_data \
- u_boot_binman_embed u_boot_binman_embed_sm
+ u_boot_binman_embed u_boot_binman_embed_sm elf_sections
all: $(TARGETS)
@@ -70,6 +71,9 @@ u_boot_binman_embed: u_boot_binman_embed.c
u_boot_binman_embed_sm: CFLAGS += $(LDS_BINMAN_EMBED)
u_boot_binman_embed_sm: u_boot_binman_embed_sm.c
+elf_sections: CFLAGS += $(LDS_EFL_SECTIONS)
+elf_sections: elf_sections.c
+
clean:
rm -f $(TARGETS)
diff --git a/tools/binman/test/elf_sections.c b/tools/binman/test/elf_sections.c
new file mode 100644
index 0000000..9bcce9a
--- /dev/null
+++ b/tools/binman/test/elf_sections.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2022 Google LLC
+ *
+ * Program containing two text sections
+ */
+
+int __attribute__((section(".sram_data"))) data[29];
+
+int __attribute__((section(".sram_code"))) calculate(int x)
+{
+ data[0] = x;
+
+ return x * x;
+}
+
+int main(void)
+{
+ return calculate(123);
+}
diff --git a/tools/binman/test/elf_sections.lds b/tools/binman/test/elf_sections.lds
new file mode 100644
index 0000000..7b6e932
--- /dev/null
+++ b/tools/binman/test/elf_sections.lds
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) 2016 Google, Inc
+ */
+
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+ENTRY(_start)
+
+SECTIONS
+{
+ . = 0x00000010;
+ _start = .;
+
+ . = ALIGN(4);
+ .text :
+ {
+ *(.text*)
+ }
+
+ . = 0x00001000;
+ .sram :
+ {
+ *(.sram*)
+ }
+
+ /DISCARD/ : {
+ *(.comment)
+ *(.dyn*)
+ }
+}
diff --git a/tools/dtoc/fdt.py b/tools/dtoc/fdt.py
index 7e13757..c16909a 100644
--- a/tools/dtoc/fdt.py
+++ b/tools/dtoc/fdt.py
@@ -356,6 +356,8 @@ class Node:
offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND)
for subnode in self.subnodes:
+ if subnode._offset is None:
+ continue
if subnode.name != fdt_obj.get_name(offset):
raise ValueError('Internal error, node name mismatch %s != %s' %
(subnode.name, fdt_obj.get_name(offset)))
@@ -501,6 +503,24 @@ class Node:
val = bytes(val, 'utf-8')
return self.AddData(prop_name, val + b'\0')
+ def AddStringList(self, prop_name, val):
+ """Add a new string-list property to a node
+
+ The device tree is marked dirty so that the value will be written to
+ the blob on the next sync.
+
+ Args:
+ prop_name: Name of property to add
+ val (list of str): List of strings to add
+
+ Returns:
+ Prop added
+ """
+ out = b''
+ for string in val:
+ out += bytes(string, 'utf-8') + b'\0'
+ return self.AddData(prop_name, out)
+
def AddInt(self, prop_name, val):
"""Add a new integer property to a node
@@ -530,6 +550,23 @@ class Node:
self.subnodes.append(subnode)
return subnode
+ def Delete(self):
+ """Delete a node
+
+ The node is deleted and the offset cache is invalidated.
+
+ Args:
+ node (Node): Node to delete
+
+ Raises:
+ ValueError if the node does not exist
+ """
+ CheckErr(self._fdt._fdt_obj.del_node(self.Offset()),
+ "Node '%s': delete" % self.path)
+ parent = self.parent
+ self._fdt.Invalidate()
+ parent.subnodes.remove(self)
+
def Sync(self, auto_resize=False):
"""Sync node changes back to the device tree
diff --git a/tools/dtoc/fdt_util.py b/tools/dtoc/fdt_util.py
index d59ea2f..c82e774 100644
--- a/tools/dtoc/fdt_util.py
+++ b/tools/dtoc/fdt_util.py
@@ -184,6 +184,18 @@ def GetStringList(node, propname, default=None):
return [strval]
return value
+def GetArgs(node, propname):
+ prop = node.props.get(propname)
+ if not prop:
+ raise ValueError(f"Node '{node.path}': Expected property '{propname}'")
+ if prop.bytes:
+ value = GetStringList(node, propname)
+ else:
+ value = []
+ lists = [v.split() for v in value]
+ args = [x for l in lists for x in l]
+ return args
+
def GetBool(node, propname, default=False):
"""Get an boolean from a property
diff --git a/tools/dtoc/test/dtoc_test_simple.dts b/tools/dtoc/test/dtoc_test_simple.dts
index 4c2c70a..2d321fb 100644
--- a/tools/dtoc/test/dtoc_test_simple.dts
+++ b/tools/dtoc/test/dtoc_test_simple.dts
@@ -62,5 +62,6 @@
orig-node {
orig = <1 23 4>;
+ args = "-n first", "second", "-p", "123,456", "-x";
};
};
diff --git a/tools/dtoc/test_fdt.py b/tools/dtoc/test_fdt.py
index c789822..576d65b 100755
--- a/tools/dtoc/test_fdt.py
+++ b/tools/dtoc/test_fdt.py
@@ -272,6 +272,17 @@ class TestNode(unittest.TestCase):
self.dtb.Sync(auto_resize=True)
+ def testAddOneNode(self):
+ """Testing deleting and adding a subnode before syncing"""
+ subnode = self.node.AddSubnode('subnode')
+ self.node.AddSubnode('subnode2')
+ self.dtb.Sync(auto_resize=True)
+
+ # Delete a node and add a new one
+ subnode.Delete()
+ self.node.AddSubnode('subnode3')
+ self.dtb.Sync()
+
def testRefreshNameMismatch(self):
"""Test name mismatch when syncing nodes and properties"""
prop = self.node.AddInt('integer-a', 12)
@@ -531,6 +542,23 @@ class TestProp(unittest.TestCase):
self.node.AddData('data', tools.get_bytes(65, 20000))
self.dtb.Sync(auto_resize=True)
+ def test_string_list(self):
+ """Test adding string-list property to a node"""
+ val = ['123', '456']
+ self.node.AddStringList('stringlist', val)
+ self.dtb.Sync(auto_resize=True)
+ data = self.fdt.getprop(self.node.Offset(), 'stringlist')
+ self.assertEqual(b'123\x00456\0', data)
+
+ def test_delete_node(self):
+ """Test deleting a node"""
+ old_offset = self.fdt.path_offset('/spl-test')
+ self.assertGreater(old_offset, 0)
+ self.node.Delete()
+ self.dtb.Sync()
+ new_offset = self.fdt.path_offset('/spl-test', libfdt.QUIET_NOTFOUND)
+ self.assertEqual(-libfdt.NOTFOUND, new_offset)
+
def testFromData(self):
dtb2 = fdt.Fdt.FromData(self.dtb.GetContents())
self.assertEqual(dtb2.GetContents(), self.dtb.GetContents())
@@ -624,6 +652,21 @@ class TestFdtUtil(unittest.TestCase):
self.assertEqual(['test'],
fdt_util.GetStringList(self.node, 'missing', ['test']))
+ def testGetArgs(self):
+ node = self.dtb.GetNode('/orig-node')
+ self.assertEqual(['message'], fdt_util.GetArgs(self.node, 'stringval'))
+ self.assertEqual(
+ ['multi-word', 'message'],
+ fdt_util.GetArgs(self.node, 'stringarray'))
+ self.assertEqual([], fdt_util.GetArgs(self.node, 'boolval'))
+ self.assertEqual(['-n', 'first', 'second', '-p', '123,456', '-x'],
+ fdt_util.GetArgs(node, 'args'))
+ with self.assertRaises(ValueError) as exc:
+ fdt_util.GetArgs(self.node, 'missing')
+ self.assertIn(
+ "Node '/spl-test': Expected property 'missing'",
+ str(exc.exception))
+
def testGetBool(self):
self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
diff --git a/tools/moveconfig.py b/tools/moveconfig.py
index 1bcf58c..cff1e30 100755
--- a/tools/moveconfig.py
+++ b/tools/moveconfig.py
@@ -91,7 +91,20 @@ SIZES = {
'SZ_4G': 0x100000000
}
+RE_REMOVE_DEFCONFIG = re.compile(r'(.*)_defconfig')
+
### helper functions ###
+def remove_defconfig(defc):
+ """Drop the _defconfig suffix on a string
+
+ Args:
+ defc (str): String to convert
+
+ Returns:
+ str: string with the '_defconfig' suffix removed
+ """
+ return RE_REMOVE_DEFCONFIG.match(defc)[1]
+
def check_top_directory():
"""Exit if we are not at the top of source directory."""
for fname in 'README', 'Licenses':
@@ -1593,12 +1606,31 @@ 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):
+ """Check if any CONFIG option matches a regex
+
+ The match must be complete, i.e. from the start to end of the CONFIG option.
+
+ Args:
+ configs (dict): Dict of CONFIG options:
+ key: CONFIG option
+ value: Value of option
+ re_match (re.Pattern): Match to check
+
+ Returns:
+ bool: True if any CONFIG matches the regex
+ """
+ for cfg in configs:
+ m_cfg = re_match.match(cfg)
+ if m_cfg and m_cfg.span()[1] == len(cfg):
+ return True
+ return False
def do_find_config(config_list):
"""Find boards with a given combination of CONFIGs
Params:
- config_list: List of CONFIG options to check (each a string consisting
+ config_list: List of CONFIG options to check (each a regex 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)
@@ -1630,15 +1662,16 @@ def do_find_config(config_list):
# running for the next stage
in_list = out
out = set()
+ re_match = re.compile(cfg)
for defc in in_list:
- has_cfg = cfg in config_db[defc]
+ has_cfg = defconfig_matches(config_db[defc], re_match)
if has_cfg == want:
out.add(defc)
if adhoc:
print(f"Error: Not in Kconfig: %s" % ' '.join(adhoc))
else:
print(f'{len(out)} matches')
- print(' '.join(out))
+ print(' '.join([remove_defconfig(item) for item in out]))
def prefix_config(cfg):