From 943bf78a48ac22ce0277697976fef4f89457b446 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 23 Nov 2021 21:09:50 -0700 Subject: binman: Allow extracting a file in an alternative format In some cases entries encapsulate other data and it is useful to access the data within. An example is the fdtmap which consists of a 16-byte header, followed by a devicetree. Provide an option to specify an alternative format when extracting files. In the case of fdtmap, this is 'fdt', which produces an FDT file which can be viewed with fdtdump. Signed-off-by: Simon Glass --- tools/binman/test/213_fdtmap_alt_format.dts | 15 +++++++++++++++ tools/binman/test/214_no_alt_format.dts | 13 +++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 tools/binman/test/213_fdtmap_alt_format.dts create mode 100644 tools/binman/test/214_no_alt_format.dts (limited to 'tools/binman/test') diff --git a/tools/binman/test/213_fdtmap_alt_format.dts b/tools/binman/test/213_fdtmap_alt_format.dts new file mode 100644 index 00000000000..d9aef04bcf6 --- /dev/null +++ b/tools/binman/test/213_fdtmap_alt_format.dts @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + fdtmap { + }; + }; +}; diff --git a/tools/binman/test/214_no_alt_format.dts b/tools/binman/test/214_no_alt_format.dts new file mode 100644 index 00000000000..f00bcdd5764 --- /dev/null +++ b/tools/binman/test/214_no_alt_format.dts @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + }; +}; -- cgit v1.3.1 From cc2c50042690151b1b31d9b6d0f1a9dc5831ee5f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 23 Nov 2021 21:09:52 -0700 Subject: binman: Support lists of external blobs Sometimes it is useful to have a list of related external blobs in a single entry. An example is the DDR binaries used by meson. There are 9 files in total. Add support for this, so we don't have to have a separate entry for each. Signed-off-by: Simon Glass --- tools/binman/entries.rst | 14 ++++++ tools/binman/etype/blob.py | 16 +++++-- tools/binman/etype/blob_ext_list.py | 58 +++++++++++++++++++++++++ tools/binman/ftest.py | 20 +++++++++ tools/binman/test/215_blob_ext_list.dts | 14 ++++++ tools/binman/test/216_blob_ext_list_missing.dts | 14 ++++++ 6 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 tools/binman/etype/blob_ext_list.py create mode 100644 tools/binman/test/215_blob_ext_list.dts create mode 100644 tools/binman/test/216_blob_ext_list_missing.dts (limited to 'tools/binman/test') diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index 2ebac517ce6..d5aa3b0f4a4 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -69,6 +69,20 @@ See 'blob' for Properties / Entry arguments. +Entry: blob-ext-list: List of externally built binary blobs +----------------------------------------------------------- + +This is like blob-ext except that a number of blobs can be provided, +typically with some sort of relationship, e.g. all are DDC parameters. + +If any of the external files needed by this llist is missing, binman can +optionally ignore it and produce a broken image with a warning. + +Args: + filenames: List of filenames to read and include + + + Entry: blob-named-by-arg: A blob entry which gets its filename property from its subclass ----------------------------------------------------------------------------------------- diff --git a/tools/binman/etype/blob.py b/tools/binman/etype/blob.py index fae86ca3ec0..8c1b809e8da 100644 --- a/tools/binman/etype/blob.py +++ b/tools/binman/etype/blob.py @@ -48,10 +48,10 @@ class Entry_blob(Entry): self.ReadBlobContents() return True - def ReadBlobContents(self): + def ReadFileContents(self, pathname): """Read blob contents into memory - This function compresses the data before storing if needed. + This function compresses the data before returning if needed. We assume the data is small enough to fit into memory. If this is used for large filesystem image that might not be true. @@ -59,13 +59,23 @@ class Entry_blob(Entry): new Entry method which can read in chunks. Then we could copy the data in chunks and avoid reading it all at once. For now this seems like an unnecessary complication. + + Args: + pathname (str): Pathname to read from + + Returns: + bytes: Data read """ state.TimingStart('read') - indata = tools.ReadFile(self._pathname) + indata = tools.ReadFile(pathname) state.TimingAccum('read') state.TimingStart('compress') data = self.CompressData(indata) state.TimingAccum('compress') + return data + + def ReadBlobContents(self): + data = self.ReadFileContents(self._pathname) self.SetContents(data) return True diff --git a/tools/binman/etype/blob_ext_list.py b/tools/binman/etype/blob_ext_list.py new file mode 100644 index 00000000000..136ae819946 --- /dev/null +++ b/tools/binman/etype/blob_ext_list.py @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass +# +# Entry-type module for a list of external blobs, not built by U-Boot +# + +import os + +from binman.etype.blob import Entry_blob +from dtoc import fdt_util +from patman import tools +from patman import tout + +class Entry_blob_ext_list(Entry_blob): + """List of externally built binary blobs + + This is like blob-ext except that a number of blobs can be provided, + typically with some sort of relationship, e.g. all are DDC parameters. + + If any of the external files needed by this llist is missing, binman can + optionally ignore it and produce a broken image with a warning. + + Args: + filenames: List of filenames to read and include + """ + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) + self.external = True + + def ReadNode(self): + super().ReadNode() + self._filenames = fdt_util.GetStringList(self._node, 'filenames') + self._pathnames = [] + + def ObtainContents(self): + missing = False + pathnames = [] + for fname in self._filenames: + pathname = tools.GetInputFilename( + fname, self.external and self.section.GetAllowMissing()) + # Allow the file to be missing + if not pathname: + missing = True + pathnames.append(pathname) + self._pathnames = pathnames + + if missing: + self.SetContents(b'') + self.missing = True + return True + + data = bytearray() + for pathname in pathnames: + data += self.ReadFileContents(pathname) + + self.SetContents(data) + return True diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index d3a6cbf71df..f5ceb9fb17d 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -4715,6 +4715,26 @@ fdt fdtmap Extract the devicetree blob from the fdtmap finally: shutil.rmtree(tmpdir) + def testExtblobList(self): + """Test an image with an external blob list""" + data = self._DoReadFile('215_blob_ext_list.dts') + self.assertEqual(REFCODE_DATA + FSP_M_DATA, data) + + def testExtblobListMissing(self): + """Test an image with a missing external blob""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('216_blob_ext_list_missing.dts') + self.assertIn("Filename 'missing-file' not found in input path", + str(e.exception)) + + def testExtblobListMissingOk(self): + """Test an image with an missing external blob that is allowed""" + with test_util.capture_sys_output() as (stdout, stderr): + self._DoTestFile('216_blob_ext_list_missing.dts', + allow_missing=True) + err = stderr.getvalue() + self.assertRegex(err, "Image 'main-section'.*missing.*: blob-ext") + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/215_blob_ext_list.dts b/tools/binman/test/215_blob_ext_list.dts new file mode 100644 index 00000000000..aad2f0300d3 --- /dev/null +++ b/tools/binman/test/215_blob_ext_list.dts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + blob-ext-list { + filenames = "refcode.bin", "fsp_m.bin"; + }; + }; +}; diff --git a/tools/binman/test/216_blob_ext_list_missing.dts b/tools/binman/test/216_blob_ext_list_missing.dts new file mode 100644 index 00000000000..c02c335c760 --- /dev/null +++ b/tools/binman/test/216_blob_ext_list_missing.dts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + blob-ext-list { + filenames = "refcode.bin", "missing-file"; + }; + }; +}; -- cgit v1.3.1 From 75989727601b8fe6244314f90bc6f9ef3503b590 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 23 Nov 2021 21:08:59 -0700 Subject: binman: Add support for ATF FIP This format is used in firmware binaries so we may as well supported it. With this patch binman supports creating, listing and updating FIPs, as well as extracting files from one, provided that an FDTMAP is also present somewhere in the image. Signed-off-by: Simon Glass --- tools/binman/entries.rst | 154 +++++++++++++++++ tools/binman/etype/atf_fip.py | 273 +++++++++++++++++++++++++++++++ tools/binman/ftest.py | 217 ++++++++++++++++++++++++ tools/binman/test/203_fip.dts | 21 +++ tools/binman/test/204_fip_other.dts | 22 +++ tools/binman/test/205_fip_no_type.dts | 15 ++ tools/binman/test/206_fip_uuid.dts | 22 +++ tools/binman/test/207_fip_ls.dts | 25 +++ tools/binman/test/208_fip_replace.dts | 33 ++++ tools/binman/test/209_fip_missing.dts | 19 +++ tools/binman/test/210_fip_size.dts | 19 +++ tools/binman/test/211_fip_bad_align.dts | 18 ++ tools/binman/test/212_fip_collection.dts | 24 +++ 13 files changed, 862 insertions(+) create mode 100644 tools/binman/etype/atf_fip.py create mode 100644 tools/binman/test/203_fip.dts create mode 100644 tools/binman/test/204_fip_other.dts create mode 100644 tools/binman/test/205_fip_no_type.dts create mode 100644 tools/binman/test/206_fip_uuid.dts create mode 100644 tools/binman/test/207_fip_ls.dts create mode 100644 tools/binman/test/208_fip_replace.dts create mode 100644 tools/binman/test/209_fip_missing.dts create mode 100644 tools/binman/test/210_fip_size.dts create mode 100644 tools/binman/test/211_fip_bad_align.dts create mode 100644 tools/binman/test/212_fip_collection.dts (limited to 'tools/binman/test') diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index d5aa3b0f4a4..c47f7df0980 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -25,6 +25,160 @@ about ATF. +Entry: atf-fip: ARM Trusted Firmware's Firmware Image Package (FIP) +------------------------------------------------------------------- + +A FIP_ provides a way to group binaries in a firmware image, used by ARM's +Trusted Firmware A (TF-A) code. It is a simple format consisting of a +table of contents with information about the type, offset and size of the +binaries in the FIP. It is quite similar to FMAP, with the major difference +that it uses UUIDs to indicate the type of each entry. + +Note: It is recommended to always add an fdtmap to every image, as well as +any FIPs so that binman and other tools can access the entire image +correctly. + +The UUIDs correspond to useful names in `fiptool`, provided by ATF to +operate on FIPs. Binman uses these names to make it easier to understand +what is going on, although it is possible to provide a UUID if needed. + +The contents of the FIP are defined by subnodes of the atf-fip entry, e.g.:: + + atf-fip { + soc-fw { + filename = "bl31.bin"; + }; + + scp-fwu-cfg { + filename = "bl2u.bin"; + }; + + u-boot { + fip-type = "nt-fw"; + }; + }; + +This describes a FIP with three entries: soc-fw, scp-fwu-cfg and nt-fw. +You can use normal (non-external) binaries like U-Boot simply by adding a +FIP type, with the `fip-type` property, as above. + +Since FIP exists to bring blobs together, Binman assumes that all FIP +entries are external binaries. If a binary may not exist, you can use the +`--allow-missing` flag to Binman, in which case the image is still created, +even though it will not actually work. + +The size of the FIP depends on the size of the binaries. There is currently +no way to specify a fixed size. If the `atf-fip` node has a `size` entry, +this affects the space taken up by the `atf-fip` entry, but the FIP itself +does not expand to use that space. + +Some other FIP features are available with Binman. The header and the +entries have 64-bit flag works. The flag flags do not seem to be defined +anywhere, but you can use `fip-hdr-flags` and fip-flags` to set the values +of the header and entries respectively. + +FIP entries can be aligned to a particular power-of-two boundary. Use +fip-align for this. + +Binman only understands the entry types that are included in its +implementation. It is possible to specify a 16-byte UUID instead, using the +fip-uuid property. In this case Binman doesn't know what its type is, so +just uses the UUID. See the `u-boot` node in this example:: + + binman { + atf-fip { + fip-hdr-flags = /bits/ 64 <0x123>; + fip-align = <16>; + soc-fw { + fip-flags = /bits/ 64 <0x456>; + filename = "bl31.bin"; + }; + + scp-fwu-cfg { + filename = "bl2u.bin"; + }; + + u-boot { + fip-uuid = [fc 65 13 92 4a 5b 11 ec + 94 35 ff 2d 1c fc 79 9c]; + }; + }; + fdtmap { + }; + }; + +Binman allows reading and updating FIP entries after the image is created, +provided that an FDPMAP is present too. Updates which change the size of a +FIP entry will cause it to be expanded or contracted as needed. + +Properties for top-level atf-fip node +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +fip-hdr-flags (64 bits) + Sets the flags for the FIP header. + +Properties for subnodes +~~~~~~~~~~~~~~~~~~~~~~~ + +fip-type (str) + FIP type to use for this entry. This is needed if the entry + name is not a valid type. Value types are defined in `fip_util.py`. + The FIP type defines the UUID that is used (they map 1:1). + +fip-uuid (16 bytes) + If there is no FIP-type name defined, or it is not supported by Binman, + this property sets the UUID. It should be a 16-byte value, following the + hex digits of the UUID. + +fip-flags (64 bits) + Set the flags for a FIP entry. Use in one of the subnodes of the + 7atf-fip entry. + +fip-align + Set the alignment for a FIP entry, FIP entries can be aligned to a + particular power-of-two boundary. The default is 1. + +Adding new FIP-entry types +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When new FIP entries are defined by TF-A they appear in the +`TF-A source tree`_. You can use `fip_util.py` to update Binman to support +new types, then `send a patch`_ to the U-Boot mailing list. There are two +source files that the tool examples: + +- `include/tools_share/firmware_image_package.h` has the UUIDs +- `tools/fiptool/tbbr_config.c` has the name and descripion for each UUID + +To run the tool:: + + $ tools/binman/fip_util.py -s /path/to/arm-trusted-firmware + Warning: UUID 'UUID_NON_TRUSTED_WORLD_KEY_CERT' is not mentioned in tbbr_config.c file + Existing code in 'tools/binman/fip_util.py' is up-to-date + +If it shows there is an update, it writes a new version of `fip_util.py` +to `fip_util.py.out`. You can change the output file using the `-i` flag. +If you have a problem, use `-D` to enable traceback debugging. + +FIP commentary +~~~~~~~~~~~~~~ + +As a side effect of use of UUIDs, FIP does not support multiple +entries of the same type, such as might be used to store fonts or graphics +icons, for example. For verified boot it could be used for each part of the +image (e.g. separate FIPs for A and B) but cannot describe the whole +firmware image. As with FMAP there is no hierarchy defined, although FMAP +works around this by having 'section' areas which encompass others. A +similar workaround would be possible with FIP but is not currently defined. + +It is recommended to always add an fdtmap to every image, as well as any +FIPs so that binman and other tools can access the entire image correctly. + +.. _FIP: https://trustedfirmware-a.readthedocs.io/en/latest/design/firmware-design.html#firmware-image-package-fip +.. _`TF-A source tree`: https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git +.. _`send a patch`: https://www.denx.de/wiki/U-Boot/Patches + + + Entry: blob: Arbitrary binary blob ---------------------------------- diff --git a/tools/binman/etype/atf_fip.py b/tools/binman/etype/atf_fip.py new file mode 100644 index 00000000000..07e6c645b06 --- /dev/null +++ b/tools/binman/etype/atf_fip.py @@ -0,0 +1,273 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2019 Google LLC +# Written by Simon Glass +# +# Entry-type module for the ARM Trusted Firmware's Firmware Image Package (FIP) +# format + +from collections import OrderedDict + +from binman.entry import Entry +from binman.etype.section import Entry_section +from binman.fip_util import FIP_TYPES, FipReader, FipWriter, UUID_LEN +from dtoc import fdt_util +from patman import tools + +class Entry_atf_fip(Entry_section): + """ARM Trusted Firmware's Firmware Image Package (FIP) + + A FIP_ provides a way to group binaries in a firmware image, used by ARM's + Trusted Firmware A (TF-A) code. It is a simple format consisting of a + table of contents with information about the type, offset and size of the + binaries in the FIP. It is quite similar to FMAP, with the major difference + that it uses UUIDs to indicate the type of each entry. + + Note: It is recommended to always add an fdtmap to every image, as well as + any FIPs so that binman and other tools can access the entire image + correctly. + + The UUIDs correspond to useful names in `fiptool`, provided by ATF to + operate on FIPs. Binman uses these names to make it easier to understand + what is going on, although it is possible to provide a UUID if needed. + + The contents of the FIP are defined by subnodes of the atf-fip entry, e.g.:: + + atf-fip { + soc-fw { + filename = "bl31.bin"; + }; + + scp-fwu-cfg { + filename = "bl2u.bin"; + }; + + u-boot { + fip-type = "nt-fw"; + }; + }; + + This describes a FIP with three entries: soc-fw, scp-fwu-cfg and nt-fw. + You can use normal (non-external) binaries like U-Boot simply by adding a + FIP type, with the `fip-type` property, as above. + + Since FIP exists to bring blobs together, Binman assumes that all FIP + entries are external binaries. If a binary may not exist, you can use the + `--allow-missing` flag to Binman, in which case the image is still created, + even though it will not actually work. + + The size of the FIP depends on the size of the binaries. There is currently + no way to specify a fixed size. If the `atf-fip` node has a `size` entry, + this affects the space taken up by the `atf-fip` entry, but the FIP itself + does not expand to use that space. + + Some other FIP features are available with Binman. The header and the + entries have 64-bit flag works. The flag flags do not seem to be defined + anywhere, but you can use `fip-hdr-flags` and fip-flags` to set the values + of the header and entries respectively. + + FIP entries can be aligned to a particular power-of-two boundary. Use + fip-align for this. + + Binman only understands the entry types that are included in its + implementation. It is possible to specify a 16-byte UUID instead, using the + fip-uuid property. In this case Binman doesn't know what its type is, so + just uses the UUID. See the `u-boot` node in this example:: + + binman { + atf-fip { + fip-hdr-flags = /bits/ 64 <0x123>; + fip-align = <16>; + soc-fw { + fip-flags = /bits/ 64 <0x456>; + filename = "bl31.bin"; + }; + + scp-fwu-cfg { + filename = "bl2u.bin"; + }; + + u-boot { + fip-uuid = [fc 65 13 92 4a 5b 11 ec + 94 35 ff 2d 1c fc 79 9c]; + }; + }; + fdtmap { + }; + }; + + Binman allows reading and updating FIP entries after the image is created, + provided that an FDPMAP is present too. Updates which change the size of a + FIP entry will cause it to be expanded or contracted as needed. + + Properties for top-level atf-fip node + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + fip-hdr-flags (64 bits) + Sets the flags for the FIP header. + + Properties for subnodes + ~~~~~~~~~~~~~~~~~~~~~~~ + + fip-type (str) + FIP type to use for this entry. This is needed if the entry + name is not a valid type. Value types are defined in `fip_util.py`. + The FIP type defines the UUID that is used (they map 1:1). + + fip-uuid (16 bytes) + If there is no FIP-type name defined, or it is not supported by Binman, + this property sets the UUID. It should be a 16-byte value, following the + hex digits of the UUID. + + fip-flags (64 bits) + Set the flags for a FIP entry. Use in one of the subnodes of the + 7atf-fip entry. + + fip-align + Set the alignment for a FIP entry, FIP entries can be aligned to a + particular power-of-two boundary. The default is 1. + + Adding new FIP-entry types + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + + When new FIP entries are defined by TF-A they appear in the + `TF-A source tree`_. You can use `fip_util.py` to update Binman to support + new types, then `send a patch`_ to the U-Boot mailing list. There are two + source files that the tool examples: + + - `include/tools_share/firmware_image_package.h` has the UUIDs + - `tools/fiptool/tbbr_config.c` has the name and descripion for each UUID + + To run the tool:: + + $ tools/binman/fip_util.py -s /path/to/arm-trusted-firmware + Warning: UUID 'UUID_NON_TRUSTED_WORLD_KEY_CERT' is not mentioned in tbbr_config.c file + Existing code in 'tools/binman/fip_util.py' is up-to-date + + If it shows there is an update, it writes a new version of `fip_util.py` + to `fip_util.py.out`. You can change the output file using the `-i` flag. + If you have a problem, use `-D` to enable traceback debugging. + + FIP commentary + ~~~~~~~~~~~~~~ + + As a side effect of use of UUIDs, FIP does not support multiple + entries of the same type, such as might be used to store fonts or graphics + icons, for example. For verified boot it could be used for each part of the + image (e.g. separate FIPs for A and B) but cannot describe the whole + firmware image. As with FMAP there is no hierarchy defined, although FMAP + works around this by having 'section' areas which encompass others. A + similar workaround would be possible with FIP but is not currently defined. + + It is recommended to always add an fdtmap to every image, as well as any + FIPs so that binman and other tools can access the entire image correctly. + + .. _FIP: https://trustedfirmware-a.readthedocs.io/en/latest/design/firmware-design.html#firmware-image-package-fip + .. _`TF-A source tree`: https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git + .. _`send a patch`: https://www.denx.de/wiki/U-Boot/Patches + """ + def __init__(self, section, etype, node): + # Put this here to allow entry-docs and help to work without libfdt + global state + from binman import state + + super().__init__(section, etype, node) + self.align_default = None + self._entries = OrderedDict() + self.reader = None + + def ReadNode(self): + """Read properties from the atf-fip node""" + super().ReadNode() + self._pad_byte = fdt_util.GetInt(self._node, 'pad-byte', 0) + self._fip_flags = fdt_util.GetInt64(self._node, 'fip-hdr-flags', 0) + self._fip_align = fdt_util.GetInt(self._node, 'fip-align', 1) + if tools.NotPowerOfTwo(self._fip_align): + raise ValueError("Node '%s': FIP alignment %s must be a power of two" % + (self._node.path, self._fip_align)) + self.ReadEntries() + + def ReadEntries(self): + """Read the subnodes to find out what should go in this FIP""" + for node in self._node.subnodes: + fip_type = None + etype = None + if node.name in FIP_TYPES: + fip_type = node.name + etype = 'blob-ext' + + entry = Entry.Create(self, node, etype) + entry._fip_uuid = fdt_util.GetBytes(node, 'fip-uuid', UUID_LEN) + if not fip_type and not entry._fip_uuid: + fip_type = fdt_util.GetString(node, 'fip-type') + if not fip_type: + self.Raise("Must provide a fip-type (node name '%s' is not a known FIP type)" % + node.name) + + entry._fip_type = fip_type + entry._fip_flags = fdt_util.GetInt64(node, 'fip-flags', 0) + entry.ReadNode() + entry._fip_name = node.name + self._entries[entry._fip_name] = entry + + def BuildSectionData(self, required): + """Override this function to create a custom format for the entries + + Arguments: + required (bool): True if the data must be valid, False if it may + be missing (entry.GetData() returns None + + Returns: + bytes: Data obtained, or None if None + """ + fip = FipWriter(self._fip_flags, self._fip_align) + for entry in self._entries.values(): + # First get the input data and put it in an entry. If not available, + # try later. + entry_data = entry.GetData(required) + if not required and entry_data is None: + return None + fent = fip.add_entry(entry._fip_type or entry._fip_uuid, entry_data, + entry._fip_flags) + if fent: + entry._fip_entry = fent + data = fip.get_data() + return data + + def SetImagePos(self, image_pos): + """Override this function to set all the entry properties from FIP + + We can only do this once image_pos is known + + Args: + image_pos: Position of this entry in the image + """ + super().SetImagePos(image_pos) + + # Now update the entries with info from the FIP entries + for entry in self._entries.values(): + fent = entry._fip_entry + entry.size = fent.size + entry.offset = fent.offset + entry.image_pos = self.image_pos + entry.offset + + def ReadChildData(self, child, decomp=True, alt_format=None): + if not self.reader: + self.fip_data = super().ReadData(True) + self.reader = FipReader(self.fip_data) + reader = self.reader + + # It is tricky to obtain the data from a FIP entry since it is indexed + # by its UUID. + fent = reader.get_entry(child._fip_type or child._fip_uuid) + return fent.data + + # Note: + # It is also possible to extract it using the offsets directly, but this + # seems less FIP_friendly: + # return self.fip_data[child.offset:child.offset + child.size] + + def WriteChildData(self, child): + # Recreate the data structure, leaving the data for this child alone, + # so that child.data is used to pack into the FIP. + self.ObtainContents(skip_entry=child) + return True diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index f5ceb9fb17d..2f3ec69709b 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -23,6 +23,7 @@ from binman import cmdline from binman import control from binman import elf from binman import elf_test +from binman import fip_util from binman import fmap_util from binman import state from dtoc import fdt @@ -76,6 +77,7 @@ FSP_M_DATA = b'fsp_m' FSP_S_DATA = b'fsp_s' FSP_T_DATA = b'fsp_t' ATF_BL31_DATA = b'bl31' +ATF_BL2U_DATA = b'bl2u' OPENSBI_DATA = b'opensbi' SCP_DATA = b'scp' TEST_FDT1_DATA = b'fdt1' @@ -179,6 +181,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('bl2u.bin', ATF_BL2U_DATA) TestFunctional._MakeInputFile('fw_dynamic.bin', OPENSBI_DATA) TestFunctional._MakeInputFile('scp.bin', SCP_DATA) @@ -4735,6 +4738,220 @@ fdt fdtmap Extract the devicetree blob from the fdtmap err = stderr.getvalue() self.assertRegex(err, "Image 'main-section'.*missing.*: blob-ext") + def testFip(self): + """Basic test of generation of an ARM Firmware Image Package (FIP)""" + data = self._DoReadFile('203_fip.dts') + hdr, fents = fip_util.decode_fip(data) + self.assertEqual(fip_util.HEADER_MAGIC, hdr.name) + self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial) + self.assertEqual(0x123, hdr.flags) + + self.assertEqual(2, len(fents)) + + fent = fents[0] + self.assertEqual( + bytes([0x47, 0xd4, 0x08, 0x6d, 0x4c, 0xfe, 0x98, 0x46, + 0x9b, 0x95, 0x29, 0x50, 0xcb, 0xbd, 0x5a, 0x0]), fent.uuid) + self.assertEqual('soc-fw', fent.fip_type) + self.assertEqual(0x88, fent.offset) + self.assertEqual(len(ATF_BL31_DATA), fent.size) + self.assertEqual(0x123456789abcdef, fent.flags) + self.assertEqual(ATF_BL31_DATA, fent.data) + self.assertEqual(True, fent.valid) + + fent = fents[1] + self.assertEqual( + bytes([0x65, 0x92, 0x27, 0x03, 0x2f, 0x74, 0xe6, 0x44, + 0x8d, 0xff, 0x57, 0x9a, 0xc1, 0xff, 0x06, 0x10]), fent.uuid) + self.assertEqual('scp-fwu-cfg', fent.fip_type) + self.assertEqual(0x8c, fent.offset) + self.assertEqual(len(ATF_BL31_DATA), fent.size) + self.assertEqual(0, fent.flags) + self.assertEqual(ATF_BL2U_DATA, fent.data) + self.assertEqual(True, fent.valid) + + def testFipOther(self): + """Basic FIP with something that isn't a external blob""" + data = self._DoReadFile('204_fip_other.dts') + hdr, fents = fip_util.decode_fip(data) + + self.assertEqual(2, len(fents)) + fent = fents[1] + self.assertEqual('rot-cert', fent.fip_type) + self.assertEqual(b'aa', fent.data) + + def testFipOther(self): + """Basic FIP with something that isn't a external blob""" + data = self._DoReadFile('204_fip_other.dts') + hdr, fents = fip_util.decode_fip(data) + + self.assertEqual(2, len(fents)) + fent = fents[1] + self.assertEqual('rot-cert', fent.fip_type) + self.assertEqual(b'aa', fent.data) + + def testFipNoType(self): + """FIP with an entry of an unknown type""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('205_fip_no_type.dts') + self.assertIn("Must provide a fip-type (node name 'u-boot' is not a known FIP type)", + str(e.exception)) + + def testFipUuid(self): + """Basic FIP with a manual uuid""" + data = self._DoReadFile('206_fip_uuid.dts') + hdr, fents = fip_util.decode_fip(data) + + self.assertEqual(2, len(fents)) + fent = fents[1] + self.assertEqual(None, fent.fip_type) + self.assertEqual( + bytes([0xfc, 0x65, 0x13, 0x92, 0x4a, 0x5b, 0x11, 0xec, + 0x94, 0x35, 0xff, 0x2d, 0x1c, 0xfc, 0x79, 0x9c]), + fent.uuid) + self.assertEqual(U_BOOT_DATA, fent.data) + + def testFipLs(self): + """Test listing a FIP""" + data = self._DoReadFileRealDtb('207_fip_ls.dts') + hdr, fents = fip_util.decode_fip(data) + + try: + tmpdir, updated_fname = self._SetupImageInTmpdir() + with test_util.capture_sys_output() as (stdout, stderr): + self._DoBinman('ls', '-i', updated_fname) + finally: + shutil.rmtree(tmpdir) + lines = stdout.getvalue().splitlines() + expected = [ +'Name Image-pos Size Entry-type Offset Uncomp-size', +'----------------------------------------------------------------', +'main-section 0 2d3 section 0', +' atf-fip 0 90 atf-fip 0', +' soc-fw 88 4 blob-ext 88', +' u-boot 8c 4 u-boot 8c', +' fdtmap 90 243 fdtmap 90', +] + self.assertEqual(expected, lines) + + image = control.images['image'] + entries = image.GetEntries() + fdtmap = entries['fdtmap'] + + fdtmap_data = data[fdtmap.image_pos:fdtmap.image_pos + fdtmap.size] + magic = fdtmap_data[:8] + self.assertEqual(b'_FDTMAP_', magic) + self.assertEqual(tools.GetBytes(0, 8), fdtmap_data[8:16]) + + fdt_data = fdtmap_data[16:] + dtb = fdt.Fdt.FromData(fdt_data) + dtb.Scan() + props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/') + self.assertEqual({ + 'atf-fip/soc-fw:image-pos': 136, + 'atf-fip/soc-fw:offset': 136, + 'atf-fip/soc-fw:size': 4, + 'atf-fip/u-boot:image-pos': 140, + 'atf-fip/u-boot:offset': 140, + 'atf-fip/u-boot:size': 4, + 'atf-fip:image-pos': 0, + 'atf-fip:offset': 0, + 'atf-fip:size': 144, + 'image-pos': 0, + 'offset': 0, + 'fdtmap:image-pos': fdtmap.image_pos, + 'fdtmap:offset': fdtmap.offset, + 'fdtmap:size': len(fdtmap_data), + 'size': len(data), + }, props) + + def testFipExtractOneEntry(self): + """Test extracting a single entry fron an FIP""" + self._DoReadFileRealDtb('207_fip_ls.dts') + image_fname = tools.GetOutputFilename('image.bin') + fname = os.path.join(self._indir, 'output.extact') + control.ExtractEntries(image_fname, fname, None, ['atf-fip/u-boot']) + data = tools.ReadFile(fname) + self.assertEqual(U_BOOT_DATA, data) + + def testFipReplace(self): + """Test replacing a single file in a FIP""" + expected = U_BOOT_DATA + tools.GetBytes(0x78, 50) + data = self._DoReadFileRealDtb('208_fip_replace.dts') + updated_fname = tools.GetOutputFilename('image-updated.bin') + tools.WriteFile(updated_fname, data) + entry_name = 'atf-fip/u-boot' + control.WriteEntry(updated_fname, entry_name, expected, + allow_resize=True) + actual = control.ReadEntry(updated_fname, entry_name) + self.assertEqual(expected, actual) + + new_data = tools.ReadFile(updated_fname) + hdr, fents = fip_util.decode_fip(new_data) + + self.assertEqual(2, len(fents)) + + # Check that the FIP entry is updated + fent = fents[1] + self.assertEqual(0x8c, fent.offset) + self.assertEqual(len(expected), fent.size) + self.assertEqual(0, fent.flags) + self.assertEqual(expected, fent.data) + self.assertEqual(True, fent.valid) + + def testFipMissing(self): + with test_util.capture_sys_output() as (stdout, stderr): + self._DoTestFile('209_fip_missing.dts', allow_missing=True) + err = stderr.getvalue() + self.assertRegex(err, "Image 'main-section'.*missing.*: rmm-fw") + + def testFipSize(self): + """Test a FIP with a size property""" + data = self._DoReadFile('210_fip_size.dts') + self.assertEqual(0x100 + len(U_BOOT_DATA), len(data)) + hdr, fents = fip_util.decode_fip(data) + self.assertEqual(fip_util.HEADER_MAGIC, hdr.name) + self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial) + + self.assertEqual(1, len(fents)) + + fent = fents[0] + self.assertEqual('soc-fw', fent.fip_type) + self.assertEqual(0x60, fent.offset) + self.assertEqual(len(ATF_BL31_DATA), fent.size) + self.assertEqual(ATF_BL31_DATA, fent.data) + self.assertEqual(True, fent.valid) + + rest = data[0x60 + len(ATF_BL31_DATA):0x100] + self.assertEqual(tools.GetBytes(0xff, len(rest)), rest) + + def testFipBadAlign(self): + """Test that an invalid alignment value in a FIP is detected""" + with self.assertRaises(ValueError) as e: + self._DoTestFile('211_fip_bad_align.dts') + self.assertIn( + "Node \'/binman/atf-fip\': FIP alignment 31 must be a power of two", + str(e.exception)) + + def testFipCollection(self): + """Test using a FIP in a collection""" + data = self._DoReadFile('212_fip_collection.dts') + entry1 = control.images['image'].GetEntries()['collection'] + data1 = data[:entry1.size] + hdr1, fents2 = fip_util.decode_fip(data1) + + entry2 = control.images['image'].GetEntries()['atf-fip'] + data2 = data[entry2.offset:entry2.offset + entry2.size] + hdr1, fents2 = fip_util.decode_fip(data2) + + # The 'collection' entry should have U-Boot included at the end + self.assertEqual(entry1.size - len(U_BOOT_DATA), entry2.size) + self.assertEqual(data1, data2 + U_BOOT_DATA) + self.assertEqual(U_BOOT_DATA, data1[-4:]) + + # There should be a U-Boot after the final FIP + self.assertEqual(U_BOOT_DATA, data[-4:]) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/203_fip.dts b/tools/binman/test/203_fip.dts new file mode 100644 index 00000000000..08973373240 --- /dev/null +++ b/tools/binman/test/203_fip.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + atf-fip { + fip-hdr-flags = /bits/ 64 <0x123>; + soc-fw { + fip-flags = /bits/ 64 <0x123456789abcdef>; + filename = "bl31.bin"; + }; + + scp-fwu-cfg { + filename = "bl2u.bin"; + }; + }; + }; +}; diff --git a/tools/binman/test/204_fip_other.dts b/tools/binman/test/204_fip_other.dts new file mode 100644 index 00000000000..65039410986 --- /dev/null +++ b/tools/binman/test/204_fip_other.dts @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + atf-fip { + fip-hdr-flags = /bits/ 64 <0x123>; + soc-fw { + fip-flags = /bits/ 64 <0x123456789abcdef>; + filename = "bl31.bin"; + }; + + _testing { + fip-type = "rot-cert"; + return-contents-later; + }; + }; + }; +}; diff --git a/tools/binman/test/205_fip_no_type.dts b/tools/binman/test/205_fip_no_type.dts new file mode 100644 index 00000000000..23c8c3bc37e --- /dev/null +++ b/tools/binman/test/205_fip_no_type.dts @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + atf-fip { + fip-hdr-flags = /bits/ 64 <0x123>; + u-boot { + }; + }; + }; +}; diff --git a/tools/binman/test/206_fip_uuid.dts b/tools/binman/test/206_fip_uuid.dts new file mode 100644 index 00000000000..c9bd44f9c31 --- /dev/null +++ b/tools/binman/test/206_fip_uuid.dts @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + 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]; + }; + }; + }; +}; diff --git a/tools/binman/test/207_fip_ls.dts b/tools/binman/test/207_fip_ls.dts new file mode 100644 index 00000000000..630fca15024 --- /dev/null +++ b/tools/binman/test/207_fip_ls.dts @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + 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]; + }; + }; + + fdtmap { + }; + }; +}; diff --git a/tools/binman/test/208_fip_replace.dts b/tools/binman/test/208_fip_replace.dts new file mode 100644 index 00000000000..432c12474df --- /dev/null +++ b/tools/binman/test/208_fip_replace.dts @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + allow-repack; + 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]; + }; + + }; + + u-boot { + }; + + u-boot-dtb { + }; + + fdtmap { + }; + }; +}; diff --git a/tools/binman/test/209_fip_missing.dts b/tools/binman/test/209_fip_missing.dts new file mode 100644 index 00000000000..43bb600d047 --- /dev/null +++ b/tools/binman/test/209_fip_missing.dts @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + atf-fip { + soc-fw { + filename = "bl31.bin"; + }; + + rmm-fw { + filename = "rmm.bin"; + }; + }; + }; +}; diff --git a/tools/binman/test/210_fip_size.dts b/tools/binman/test/210_fip_size.dts new file mode 100644 index 00000000000..9dfee796459 --- /dev/null +++ b/tools/binman/test/210_fip_size.dts @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + atf-fip { + size = <0x100>; + pad-byte = <0xff>; + soc-fw { + filename = "bl31.bin"; + }; + }; + u-boot { + }; + }; +}; diff --git a/tools/binman/test/211_fip_bad_align.dts b/tools/binman/test/211_fip_bad_align.dts new file mode 100644 index 00000000000..a0901496d80 --- /dev/null +++ b/tools/binman/test/211_fip_bad_align.dts @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + atf-fip { + fip-align = <31>; + size = <0x100>; + pad-byte = <0xff>; + soc-fw { + filename = "bl31.bin"; + }; + }; + }; +}; diff --git a/tools/binman/test/212_fip_collection.dts b/tools/binman/test/212_fip_collection.dts new file mode 100644 index 00000000000..332c023af87 --- /dev/null +++ b/tools/binman/test/212_fip_collection.dts @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + collection { + content = <&fip &u_boot>; + }; + fip: atf-fip { + soc-fw { + filename = "bl31.bin"; + }; + + scp-fwu-cfg { + filename = "bl2u.bin"; + }; + }; + u_boot: u-boot { + }; + }; +}; -- cgit v1.3.1