[PATCH 00/12] Sign Xilinx ZynqMP SPL/FSBL boot images using binman

From: Lukas Funke lukas.funke@weidmueller.com
This series adds two etypes to create a verified boot chain for Xilinx ZynqMP devices. The first etype 'xilinx_fsbl_auth' is used to create a bootable, signed image for ZynqMP boards using the Xilinx Bootgen tool. The second etype 'u_boot_spl_pubkey_dtb' is used to add a '/signature' node to the SPL. The public key in the signature is read from a certificate file and added using the 'fdt_add_pubkey' tool. The series also contains the corresponding btool for calling 'bootgen' and 'fdt_add_pubkey'
The following block shows an example on how to use this functionality:
spl { filename = "boot.signed.bin";
xilinx_fsbl_auth { psk-filename = "psk0.pem"; ssk-filename = "ssk0.pem"; auth-params = "ppk_select=0", "spk_id=0x00000000";
u_boot_spl_nodtb { }; u_boot_spl_pubkey_dtb { algo = "sha384,rsa4096"; required = "conf"; key-name = "dev"; }; }; };
Lukas Funke (12): binman: elf: Check for ELF_TOOLS availability and remove extra semicolon binman: mkimage: Remove extra colon binman: Don't decompress data while signing binman: blob_dtb: Add fake_size argument to ObtainContents() binman: doc: Add documentation for fdt_add_pubkey bintool binman: ftest: Add test for u_boot_spl_pubkey_dtb binman: btool: Add fdt_add_pubkey as btool binman: etype: Add u_boot_spl_pubkey_dtb etype binman: doc: Add documentation for Xilinx Bootgen bintool binman: btool: Add Xilinx Bootgen btool binman: ftest: Add test for xilinx_fsbl_auth etype binman: etype: Add xilinx_fsbl_auth etype
tools/binman/bintools.rst | 22 +++ tools/binman/btool/bootgen.py | 82 +++++++++ tools/binman/btool/fdt_add_pubkey.py | 67 +++++++ tools/binman/control.py | 2 +- tools/binman/elf.py | 10 +- tools/binman/etype/blob_dtb.py | 2 +- tools/binman/etype/mkimage.py | 2 +- tools/binman/etype/u_boot_spl_pubkey_dtb.py | 105 +++++++++++ tools/binman/etype/xilinx_fsbl_auth.py | 186 ++++++++++++++++++++ tools/binman/ftest.py | 42 ++++- tools/binman/test/280_xilinx_fsb_auth.dts | 22 +++ tools/binman/test/281_spl_pubkey_dtb.dts | 16 ++ 12 files changed, 550 insertions(+), 8 deletions(-) create mode 100644 tools/binman/btool/bootgen.py create mode 100644 tools/binman/btool/fdt_add_pubkey.py create mode 100644 tools/binman/etype/u_boot_spl_pubkey_dtb.py create mode 100644 tools/binman/etype/xilinx_fsbl_auth.py create mode 100644 tools/binman/test/280_xilinx_fsb_auth.dts create mode 100644 tools/binman/test/281_spl_pubkey_dtb.dts

From: Lukas Funke lukas.funke@weidmueller.com
Check if elf tools are available when running DecodeElf(). Also remove superfuous semicolon at line ending.
Signed-off-by: Lukas Funke lukas.funke@weidmueller.com ---
tools/binman/elf.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/tools/binman/elf.py b/tools/binman/elf.py index 5816284c32..a53f4b9c4f 100644 --- a/tools/binman/elf.py +++ b/tools/binman/elf.py @@ -438,13 +438,15 @@ def DecodeElf(data, location): Returns: ElfInfo object containing information about the decoded ELF file """ + if not ELF_TOOLS: + raise ValueError("Python: No module named 'elftools'") file_size = len(data) with io.BytesIO(data) as fd: elf = ELFFile(fd) - data_start = 0xffffffff; - data_end = 0; - mem_end = 0; - virt_to_phys = 0; + data_start = 0xffffffff + data_end = 0 + mem_end = 0 + virt_to_phys = 0
for i in range(elf.num_segments()): segment = elf.get_segment(i)

On Thu, 29 Jun 2023 at 15:59, lukas.funke-oss@weidmueller.com wrote:
From: Lukas Funke lukas.funke@weidmueller.com
Check if elf tools are available when running DecodeElf(). Also remove superfuous semicolon at line ending.
Signed-off-by: Lukas Funke lukas.funke@weidmueller.com
tools/binman/elf.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
Reviewed-by: Simon Glass sjg@chromium.org

From: Lukas Funke lukas.funke@weidmueller.com
Remove extra colon typo
Signed-off-by: Lukas Funke lukas.funke@weidmueller.com ---
tools/binman/etype/mkimage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/binman/etype/mkimage.py b/tools/binman/etype/mkimage.py index e028c44070..dd734fc779 100644 --- a/tools/binman/etype/mkimage.py +++ b/tools/binman/etype/mkimage.py @@ -24,7 +24,7 @@ class Entry_mkimage(Entry): - filename: filename of output binary generated by mkimage
The data passed to mkimage via the -d flag is collected from subnodes of the - mkimage node, e.g.:: + mkimage node, e.g.:
mkimage { filename = "imximage.bin";

Hi Lukas,
On 6/29/23 16:59, lukas.funke-oss@weidmueller.com wrote:
[You don't often get email from lukas.funke-oss@weidmueller.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
From: Lukas Funke lukas.funke@weidmueller.com
Remove extra colon typo
Signed-off-by: Lukas Funke lukas.funke@weidmueller.com
tools/binman/etype/mkimage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/binman/etype/mkimage.py b/tools/binman/etype/mkimage.py index e028c44070..dd734fc779 100644 --- a/tools/binman/etype/mkimage.py +++ b/tools/binman/etype/mkimage.py @@ -24,7 +24,7 @@ class Entry_mkimage(Entry): - filename: filename of output binary generated by mkimage
The data passed to mkimage via the -d flag is collected from subnodes of the
- mkimage node, e.g.::
- mkimage node, e.g.:
This is by far not the only occurrence in the docstrings, see:
$ ag -c :: tools/binman/**/*.py tools/binman/btool/btool_gzip.py:2 tools/binman/btool/bzip2.py:2 tools/binman/btool/lz4.py:2 tools/binman/btool/lzma_alone.py:2 tools/binman/btool/lzop.py:2 tools/binman/btool/xz.py:2 tools/binman/btool/zstd.py:2 tools/binman/cbfs_util.py:1 tools/binman/etype/atf_fip.py:3 tools/binman/etype/cbfs.py:5 tools/binman/etype/fdtmap.py:1 tools/binman/etype/fit.py:7 tools/binman/etype/mkimage.py:7 tools/binman/etype/pre_load.py:1 tools/binman/etype/section.py:1 tools/binman/etype/tee_os.py:2 tools/binman/etype/text.py:3 tools/binman/ftest.py:15 tools/binman/setup.py:3 tools/binman/state.py:1
If I'm not mistaken, we (manually) populate the docstring from the docs written in rST where `::` does actually mean something (start of a literal block, c.f. https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#lite...). I think it's fine to keep it as is (and maybe we could use the docstring directly from within sphinx instead of duplicating it, but I don't have experience with that so I don't know if it's possible, desirable and reasonably easy to implement and maintain).
Cheers, Quentin

On 29.06.2023 17:08, Quentin Schulz wrote:
Hi Lukas,
On 6/29/23 16:59, lukas.funke-oss@weidmueller.com wrote:
[You don't often get email from lukas.funke-oss@weidmueller.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
From: Lukas Funke lukas.funke@weidmueller.com
Remove extra colon typo
Signed-off-by: Lukas Funke lukas.funke@weidmueller.com
tools/binman/etype/mkimage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/binman/etype/mkimage.py b/tools/binman/etype/mkimage.py index e028c44070..dd734fc779 100644 --- a/tools/binman/etype/mkimage.py +++ b/tools/binman/etype/mkimage.py @@ -24,7 +24,7 @@ class Entry_mkimage(Entry): - filename: filename of output binary generated by mkimage
The data passed to mkimage via the -d flag is collected from subnodes of the - mkimage node, e.g.:: + mkimage node, e.g.:
This is by far not the only occurrence in the docstrings, see:
$ ag -c :: tools/binman/**/*.py tools/binman/btool/btool_gzip.py:2 tools/binman/btool/bzip2.py:2 tools/binman/btool/lz4.py:2 tools/binman/btool/lzma_alone.py:2 tools/binman/btool/lzop.py:2 tools/binman/btool/xz.py:2 tools/binman/btool/zstd.py:2 tools/binman/cbfs_util.py:1 tools/binman/etype/atf_fip.py:3 tools/binman/etype/cbfs.py:5 tools/binman/etype/fdtmap.py:1 tools/binman/etype/fit.py:7 tools/binman/etype/mkimage.py:7 tools/binman/etype/pre_load.py:1 tools/binman/etype/section.py:1 tools/binman/etype/tee_os.py:2 tools/binman/etype/text.py:3 tools/binman/ftest.py:15 tools/binman/setup.py:3 tools/binman/state.py:1
If I'm not mistaken, we (manually) populate the docstring from the docs written in rST where `::` does actually mean something (start of a literal block, c.f. https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#lite...). I think it's fine to keep it as is (and maybe we could use the docstring directly from within sphinx instead of duplicating it, but I don't have experience with that so I don't know if it's possible, desirable and reasonably easy to implement and maintain).
Cheers, Quentin
Hi Quentin,
I wasn't aware of that, thanks for pointing it out. I'll drop the patch in the next iteration.
Best regards
Lukas

From: Lukas Funke lukas.funke@weidmueller.com
While signing a fit compressed data (i.e. 'blob-ext') is decompressed, but never compressed again. When compressed data was wrapped in a section, decompression leads to an error because the outer section had the original compressed size but the inner entry has the uncompressed size now.
While singing there is no reason to decompress data. Thus, decompression should be disabled.
Furthermore, bintools should be collected before loading the data. This way bintools are available if processing is required on a node.
Signed-off-by: Lukas Funke lukas.funke@weidmueller.com ---
tools/binman/control.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/binman/control.py b/tools/binman/control.py index 68597c4e77..affc33ff3d 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -306,8 +306,8 @@ def BeforeReplace(image, allow_resize): image: Image to prepare """ state.PrepareFromLoadedData(image) - image.LoadData() image.CollectBintools() + image.LoadData(decomp=False)
# If repacking, drop the old offset/size values except for the original # ones, so we are only left with the constraints.

Hi,
On Thu, 29 Jun 2023 at 15:59, lukas.funke-oss@weidmueller.com wrote:
From: Lukas Funke lukas.funke@weidmueller.com
While signing a fit compressed data (i.e. 'blob-ext') is decompressed, but never compressed again. When compressed data was wrapped in a section, decompression leads to an error because the outer section had the original compressed size but the inner entry has the uncompressed size now.
While singing there is no reason to decompress data. Thus, decompression
signing ?
should be disabled.
Furthermore, bintools should be collected before loading the data. This way bintools are available if processing is required on a node.
Signed-off-by: Lukas Funke lukas.funke@weidmueller.com
tools/binman/control.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Reviewed-by: Simon Glass sjg@chromium.org
diff --git a/tools/binman/control.py b/tools/binman/control.py index 68597c4e77..affc33ff3d 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -306,8 +306,8 @@ def BeforeReplace(image, allow_resize): image: Image to prepare """ state.PrepareFromLoadedData(image)
- image.LoadData() image.CollectBintools()
image.LoadData(decomp=False)
# If repacking, drop the old offset/size values except for the original # ones, so we are only left with the constraints.
-- 2.30.2

From: Lukas Funke lukas.funke@weidmueller.com
The method 'connect_contents_to_file()' calls ObtainsContents() with 'fake_size' argument. Without providing the argument in the blob_dtb we are not able to call this method without error.
Signed-off-by: Lukas Funke lukas.funke@weidmueller.com ---
tools/binman/etype/blob_dtb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/binman/etype/blob_dtb.py b/tools/binman/etype/blob_dtb.py index 6a3fbc4775..d543de9f75 100644 --- a/tools/binman/etype/blob_dtb.py +++ b/tools/binman/etype/blob_dtb.py @@ -38,7 +38,7 @@ class Entry_blob_dtb(Entry_blob): self.Raise("Invalid prepend in '%s': '%s'" % (self._node.name, self.prepend))
- def ObtainContents(self): + def ObtainContents(self, fake_size=0): """Get the device-tree from the list held by the 'state' module""" self._filename = self.GetDefaultFilename() self._pathname, _ = state.GetFdtContents(self.GetFdtEtype())

On Thu, 29 Jun 2023 at 15:59, lukas.funke-oss@weidmueller.com wrote:
From: Lukas Funke lukas.funke@weidmueller.com
The method 'connect_contents_to_file()' calls ObtainsContents() with 'fake_size' argument. Without providing the argument in the blob_dtb we are not able to call this method without error.
Signed-off-by: Lukas Funke lukas.funke@weidmueller.com
tools/binman/etype/blob_dtb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Reviewed-by: Simon Glass sjg@chromium.org

From: Lukas Funke lukas.funke@weidmueller.com
Add documentation for btool which calls 'fdt_add_pubkey'
Signed-off-by: Lukas Funke lukas.funke@weidmueller.com ---
tools/binman/bintools.rst | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/tools/binman/bintools.rst b/tools/binman/bintools.rst index c30e7eb9ff..88221adbe1 100644 --- a/tools/binman/bintools.rst +++ b/tools/binman/bintools.rst @@ -183,3 +183,13 @@ Documentation is available via::
+Bintool: fdt_add_pubkey: Add public key to device tree +--------------------------------------------- + +This bintool supports running `fdt_add_pubkey` in order to add a public +key coming from a certificate to a device-tree. + +Normally signing is done using `mkimage` in context of `binman sign`. However, +in this process the public key is not added to the stage before u-boot proper. +Using `fdt_add_pubkey` the key can be injected to the SPL independent of +`mkimage`

On Thu, 29 Jun 2023 at 15:59, lukas.funke-oss@weidmueller.com wrote:
From: Lukas Funke lukas.funke@weidmueller.com
Add documentation for btool which calls 'fdt_add_pubkey'
Signed-off-by: Lukas Funke lukas.funke@weidmueller.com
tools/binman/bintools.rst | 10 ++++++++++ 1 file changed, 10 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org

From: Lukas Funke lukas.funke@weidmueller.com
Add test for u_boot_spl_pubkey_dtb. The test adds a public key to the dtb and checks if the required nodes will be added to the images dtb.
Signed-off-by: Lukas Funke lukas.funke@weidmueller.com ---
tools/binman/ftest.py | 32 ++++++++++++++++++++++++ tools/binman/test/281_spl_pubkey_dtb.dts | 16 ++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 tools/binman/test/281_spl_pubkey_dtb.dts
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 43b4f850a6..3bd09d3fea 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -638,6 +638,16 @@ class TestFunctional(unittest.TestCase): TestFunctional._MakeInputFile('vpl/u-boot-vpl', tools.read_file(cls.ElfTestFile(src_fname)))
+ @classmethod + def _SetupPmuFwlElf(cls, src_fname='bss_data'): + """Set up an ELF file with a '_dt_ucode_base_size' symbol + + Args: + Filename of ELF file to use as VPL + """ + TestFunctional._MakeInputFile('pmu-firmware.elf', + tools.read_file(cls.ElfTestFile(src_fname))) + @classmethod def _SetupDescriptor(cls): with open(cls.TestFile('descriptor.bin'), 'rb') as fd: @@ -6677,5 +6687,27 @@ fdt fdtmap Extract the devicetree blob from the fdtmap self.assertIn("Node '/fit': Missing tool: 'mkimage'", str(e.exception))
+ def testSplPubkeyDtb(self): + """Test u_boot_spl_pubkey_dtb etype""" + data = tools.read_file(self.TestFile("key.pem")) + self._MakeInputFile("key.crt", data) + self._DoReadFileRealDtb('281_spl_pubkey_dtb.dts') + image = control.images['image'] + entries = image.GetEntries() + dtb_entry = entries['u_boot_spl_pubkey_dtb'] + dtb_data = dtb_entry.GetData() + dtb = fdt.Fdt.FromData(dtb_data) + dtb.Scan() + + signature_node = dtb.GetNode('/signature') + self.assertIsNotNone(signature_node) + key_node = signature_node.FindNode("key-key") + self.assertIsNotNone(key_node) + self.assertEqual(fdt_util.GetString(key_node, "required"), + "conf") + self.assertEqual(fdt_util.GetString(key_node, "algo"), + "sha384,rsa4096") + self.assertEqual(fdt_util.GetString(key_node, "key-name-hint"), + "key") if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/281_spl_pubkey_dtb.dts b/tools/binman/test/281_spl_pubkey_dtb.dts new file mode 100644 index 0000000000..5a2952ed7d --- /dev/null +++ b/tools/binman/test/281_spl_pubkey_dtb.dts @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u_boot_spl_pubkey_dtb { + algo = "sha384,rsa4096"; + required = "conf"; + key-name = "key"; + }; + }; +};

On Thu, 29 Jun 2023 at 15:59, lukas.funke-oss@weidmueller.com wrote:
From: Lukas Funke lukas.funke@weidmueller.com
Add test for u_boot_spl_pubkey_dtb. The test adds a public key to the dtb and checks if the required nodes will be added to the images dtb.
Signed-off-by: Lukas Funke lukas.funke@weidmueller.com
tools/binman/ftest.py | 32 ++++++++++++++++++++++++ tools/binman/test/281_spl_pubkey_dtb.dts | 16 ++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 tools/binman/test/281_spl_pubkey_dtb.dts
Reviewed-by: Simon Glass sjg@chromium.org
nit below
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 43b4f850a6..3bd09d3fea 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -638,6 +638,16 @@ class TestFunctional(unittest.TestCase): TestFunctional._MakeInputFile('vpl/u-boot-vpl', tools.read_file(cls.ElfTestFile(src_fname)))
- @classmethod
- def _SetupPmuFwlElf(cls, src_fname='bss_data'):
"""Set up an ELF file with a '_dt_ucode_base_size' symbol
Args:
Filename of ELF file to use as VPL
"""
TestFunctional._MakeInputFile('pmu-firmware.elf',
tools.read_file(cls.ElfTestFile(src_fname)))
- @classmethod def _SetupDescriptor(cls): with open(cls.TestFile('descriptor.bin'), 'rb') as fd:
@@ -6677,5 +6687,27 @@ fdt fdtmap Extract the devicetree blob from the fdtmap self.assertIn("Node '/fit': Missing tool: 'mkimage'", str(e.exception))
- def testSplPubkeyDtb(self):
"""Test u_boot_spl_pubkey_dtb etype"""
data = tools.read_file(self.TestFile("key.pem"))
self._MakeInputFile("key.crt", data)
self._DoReadFileRealDtb('281_spl_pubkey_dtb.dts')
image = control.images['image']
entries = image.GetEntries()
dtb_entry = entries['u_boot_spl_pubkey_dtb']
dtb_data = dtb_entry.GetData()
dtb = fdt.Fdt.FromData(dtb_data)
dtb.Scan()
signature_node = dtb.GetNode('/signature')
self.assertIsNotNone(signature_node)
key_node = signature_node.FindNode("key-key")
self.assertIsNotNone(key_node)
self.assertEqual(fdt_util.GetString(key_node, "required"),
"conf")
self.assertEqual(fdt_util.GetString(key_node, "algo"),
"sha384,rsa4096")
self.assertEqual(fdt_util.GetString(key_node, "key-name-hint"),
"key")
if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/281_spl_pubkey_dtb.dts b/tools/binman/test/281_spl_pubkey_dtb.dts new file mode 100644 index 0000000000..5a2952ed7d --- /dev/null +++ b/tools/binman/test/281_spl_pubkey_dtb.dts @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
u_boot_spl_pubkey_dtb {
please use - instead of _
algo = "sha384,rsa4096";
required = "conf";
key-name = "key";
};
};
+};
2.30.2

From: Lukas Funke lukas.funke@weidmueller.com
Add btool which calls 'fdt_add_pubkey'
Signed-off-by: Lukas Funke lukas.funke@weidmueller.com ---
tools/binman/btool/fdt_add_pubkey.py | 67 ++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 tools/binman/btool/fdt_add_pubkey.py
diff --git a/tools/binman/btool/fdt_add_pubkey.py b/tools/binman/btool/fdt_add_pubkey.py new file mode 100644 index 0000000000..a50774200c --- /dev/null +++ b/tools/binman/btool/fdt_add_pubkey.py @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (C) 2023 Weidmüller Interface GmbH & Co. KG +# Lukas Funke lukas.funke@weidmueller.com +# +"""Bintool implementation for fdt_add_pubkey""" + +from binman import bintool + +class Bintoolfdt_add_pubkey(bintool.Bintool): + """Add public key to control dtb (spl or u-boot proper) + + This bintool supports running `fdt_add_pubkey`. + + Normally mkimage adds signature information to the control dtb. However + binman images are built independent from each other. Thus it is required + to add the public key separately from mkimage. + """ + def __init__(self, name): + super().__init__(name, 'Generate image for U-Boot') + + # pylint: disable=R0913 + def run(self, input_fname, keydir, keyname, required, algo): + """Run fdt_add_pubkey + + Args: + input_fname (str): dtb file to sign + keydir (str): Directory with public key. Optional parameter, + default value: '.' (current directory) + keyname (str): Public key name. Optional parameter, + default value: key + required (str): If present this indicates that the key must be + verified for the image / configuration to be considered valid. + algo (str): Cryptographic algorithm. Optional parameter, + default value: sha1,rsa2048 + """ + args = [] + if algo: + args += ['-a', algo] + if keydir: + args += ['-k', keydir] + if keyname: + args += ['-n', keyname] + if required: + args += ['-r', required] + + args += [ input_fname ] + + return self.run_cmd(*args) + + def fetch(self, method): + """Fetch handler for fdt_add_pubkey + + This installs fdt_add_pubkey using the apt utility. + + Args: + method (FETCH_...): Method to use + + Returns: + True if the file was fetched and now installed, None if a method + other than FETCH_BIN was requested + + Raises: + Valuerror: Fetching could not be completed + """ + if method != bintool.FETCH_BIN: + return None + return self.apt_install('u-boot-tools')

On Thu, 29 Jun 2023 at 15:59, lukas.funke-oss@weidmueller.com wrote:
From: Lukas Funke lukas.funke@weidmueller.com
Add btool which calls 'fdt_add_pubkey'
Signed-off-by: Lukas Funke lukas.funke@weidmueller.com
tools/binman/btool/fdt_add_pubkey.py | 67 ++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 tools/binman/btool/fdt_add_pubkey.py
Reviewed-by: Simon Glass sjg@chromium.org

From: Lukas Funke lukas.funke@weidmueller.com
This adds a new etype 'u_boot_spl_pubkey_dtb'. The etype adds the public key from a certificate to the dtb. This creates a '/signature' node which is turn contains the fields which make up the public key. Usually this is done by 'mkimage -K'. However, 'binman sign' does not add the public key to the SPL. This is why the pubkey is added using this etype.
The etype calls the underlying 'fdt_add_pubkey' tool.
Signed-off-by: Lukas Funke lukas.funke@weidmueller.com ---
tools/binman/etype/u_boot_spl_pubkey_dtb.py | 105 ++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 tools/binman/etype/u_boot_spl_pubkey_dtb.py
diff --git a/tools/binman/etype/u_boot_spl_pubkey_dtb.py b/tools/binman/etype/u_boot_spl_pubkey_dtb.py new file mode 100644 index 0000000000..25aa817975 --- /dev/null +++ b/tools/binman/etype/u_boot_spl_pubkey_dtb.py @@ -0,0 +1,105 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2023 Weidmueller GmbH +# Written by Lukas Funke lukas.funke@weidmueller.com +# +# Entry-type module for 'u-boot-spl-pubkey.dtb' +# + +import tempfile +import os + +from binman.etype.blob_dtb import Entry_blob_dtb + +from dtoc import fdt_util + +from u_boot_pylib import tools + +# pylint: disable=C0103 +class Entry_u_boot_spl_pubkey_dtb(Entry_blob_dtb): + """U-Boot SPL device tree including public key + + Properties / Entry arguments: + - key-name: Public key name without extension (e.g. .crt). Default is + determined by underlying bintool (fdt_add_pubkey), + usually 'key' + - algo: (Optional) Algorithm used for signing. Default is determined by + underlying bintool (fdt_add_pubkey), usually 'sha1,rsa2048' + - required: (Optional) If present this indicates that the key must be + verified for the image / configuration to be + considered valid + + The following example shows an image containing an SPL which + is packed together with the dtb. Binman will add a signature + node to the dtb: + + image { + ... + spl { + filename = "spl.bin" + + u_boot_spl_nodtb { + }; + u_boot_spl_pubkey_dtb { + algo = "sha384,rsa4096"; + required = "conf"; + key-name = "dev"; + }; + }; + ... + } + """ + + 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.required_props = ['key-name'] + self.fdt_add_pubkey = None + self._algo = fdt_util.GetString(self._node, 'algo') + self._required = fdt_util.GetString(self._node, 'required') + self._keyname = fdt_util.GetString(self._node, 'key-name') + + def ObtainContents(self, fake_size=0): + """ Add public key which is pointed out by + 'key-name' to node 'signature' in the spl-dtb + + This is equivalent to the '-K' option of 'mkimage' + + Args: + fake_size (int): unused + """ + + # We don't pass fake_size and skip_entry upwards + # because this is currently not support by the blob type + super().ObtainContents() + + with tempfile.NamedTemporaryFile(prefix=os.path.basename( + self.GetFdtEtype()), + dir=tools.get_output_dir())\ + as pubkey_tdb: + tools.write_file(pubkey_tdb.name, self.GetData()) + keyname = tools.get_input_filename(self._keyname + ".crt") + self.fdt_add_pubkey.run(pubkey_tdb.name, + os.path.dirname(keyname), + self._keyname, + self._required, self._algo) + dtb = tools.read_file(pubkey_tdb.name) + self.SetContents(dtb) + state.UpdateFdtContents(self.GetFdtEtype(), dtb) + + return True + + # pylint: disable=R0201,C0116 + def GetDefaultFilename(self): + return 'spl/u-boot-spl-pubkey.dtb' + + # pylint: disable=R0201,C0116 + def GetFdtEtype(self): + return 'u-boot-spl-dtb' + + # pylint: disable=R0201,C0116 + def AddBintools(self, btools): + super().AddBintools(btools) + self.fdt_add_pubkey = self.AddBintool(btools, 'fdt_add_pubkey')

On Thu, 29 Jun 2023 at 15:59, lukas.funke-oss@weidmueller.com wrote:
From: Lukas Funke lukas.funke@weidmueller.com
This adds a new etype 'u_boot_spl_pubkey_dtb'. The etype adds the public key from a certificate to the dtb. This creates a '/signature' node which is turn contains the fields which make up the public key. Usually this is done by 'mkimage -K'. However, 'binman sign' does not add the public key to the SPL. This is why the pubkey is added using this etype.
The etype calls the underlying 'fdt_add_pubkey' tool.
Signed-off-by: Lukas Funke lukas.funke@weidmueller.com
tools/binman/etype/u_boot_spl_pubkey_dtb.py | 105 ++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 tools/binman/etype/u_boot_spl_pubkey_dtb.py
Please can you use 'binman entry-docs >tools/binman/entries.rst' and add to patch?
diff --git a/tools/binman/etype/u_boot_spl_pubkey_dtb.py b/tools/binman/etype/u_boot_spl_pubkey_dtb.py new file mode 100644 index 0000000000..25aa817975 --- /dev/null +++ b/tools/binman/etype/u_boot_spl_pubkey_dtb.py @@ -0,0 +1,105 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2023 Weidmueller GmbH +# Written by Lukas Funke lukas.funke@weidmueller.com +# +# Entry-type module for 'u-boot-spl-pubkey.dtb' +#
+import tempfile +import os
+from binman.etype.blob_dtb import Entry_blob_dtb
+from dtoc import fdt_util
+from u_boot_pylib import tools
+# pylint: disable=C0103 +class Entry_u_boot_spl_pubkey_dtb(Entry_blob_dtb):
- """U-Boot SPL device tree including public key
- Properties / Entry arguments:
- key-name: Public key name without extension (e.g. .crt). Default is
determined by underlying bintool (fdt_add_pubkey),
usually 'key'
- algo: (Optional) Algorithm used for signing. Default is determined by
underlying bintool (fdt_add_pubkey), usually 'sha1,rsa2048'
- required: (Optional) If present this indicates that the key must be
verified for the image / configuration to be
considered valid
- The following example shows an image containing an SPL which
- is packed together with the dtb. Binman will add a signature
- node to the dtb:
image {
...
spl {
filename = "spl.bin"
u_boot_spl_nodtb {
};
u_boot_spl_pubkey_dtb {
algo = "sha384,rsa4096";
required = "conf";
key-name = "dev";
};
};
...
}
- """
- 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.required_props = ['key-name']
self.fdt_add_pubkey = None
self._algo = fdt_util.GetString(self._node, 'algo')
self._required = fdt_util.GetString(self._node, 'required')
self._keyname = fdt_util.GetString(self._node, 'key-name')
- def ObtainContents(self, fake_size=0):
""" Add public key which is pointed out by
Please check comment style. The first line should a summary, then a blank line, then more info
'key-name' to node 'signature' in the spl-dtb
This is equivalent to the '-K' option of 'mkimage'
Args:
fake_size (int): unused
"""
# We don't pass fake_size and skip_entry upwards
# because this is currently not support by the blob type
supported
super().ObtainContents()
with tempfile.NamedTemporaryFile(prefix=os.path.basename(
self.GetFdtEtype()),
dir=tools.get_output_dir())\
as pubkey_tdb:
tools.write_file(pubkey_tdb.name, self.GetData())
keyname = tools.get_input_filename(self._keyname + ".crt")
self.fdt_add_pubkey.run(pubkey_tdb.name,
os.path.dirname(keyname),
self._keyname,
self._required, self._algo)
dtb = tools.read_file(pubkey_tdb.name)
self.SetContents(dtb)
state.UpdateFdtContents(self.GetFdtEtype(), dtb)
return True
- # pylint: disable=R0201,C0116
- def GetDefaultFilename(self):
return 'spl/u-boot-spl-pubkey.dtb'
- # pylint: disable=R0201,C0116
- def GetFdtEtype(self):
return 'u-boot-spl-dtb'
- # pylint: disable=R0201,C0116
- def AddBintools(self, btools):
super().AddBintools(btools)
self.fdt_add_pubkey = self.AddBintool(btools, 'fdt_add_pubkey')
-- 2.30.2
Reviewed-by: Simon Glass sjg@chromium.org
Regards, Simon

From: Lukas Funke lukas.funke@weidmueller.com
Add documentation for the 'bootgen' bintool
Signed-off-by: Lukas Funke lukas.funke@weidmueller.com ---
tools/binman/bintools.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+)
diff --git a/tools/binman/bintools.rst b/tools/binman/bintools.rst index 88221adbe1..c8d69f7177 100644 --- a/tools/binman/bintools.rst +++ b/tools/binman/bintools.rst @@ -193,3 +193,15 @@ Normally signing is done using `mkimage` in context of `binman sign`. However, in this process the public key is not added to the stage before u-boot proper. Using `fdt_add_pubkey` the key can be injected to the SPL independent of `mkimage` + + + +Bintool: bootgen: Sign ZynqMP FSBL image +--------------------------------------------- + +This bintool supports running `bootgen` in order to sign a SPL for ZynqMP +devices. + +The bintool automatically creates an appropriate input image file (.bif) for +bootgen based on the passed arguments. The output is a bootable, +authenticated `boot.bin` file.

On Thu, 29 Jun 2023 at 15:59, lukas.funke-oss@weidmueller.com wrote:
From: Lukas Funke lukas.funke@weidmueller.com
Add documentation for the 'bootgen' bintool
Signed-off-by: Lukas Funke lukas.funke@weidmueller.com
tools/binman/bintools.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org

From: Lukas Funke lukas.funke@weidmueller.com
Add the Xilinx Bootgen as bintool. Xilinx Bootgen is used to create bootable SPL (FSBL in Xilinx terms) images for Zynq/ZynqMP devices. The btool creates a signed version of the SPL.
Signed-off-by: Lukas Funke lukas.funke@weidmueller.com ---
tools/binman/btool/bootgen.py | 82 +++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 tools/binman/btool/bootgen.py
diff --git a/tools/binman/btool/bootgen.py b/tools/binman/btool/bootgen.py new file mode 100644 index 0000000000..8bc727a54f --- /dev/null +++ b/tools/binman/btool/bootgen.py @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (C) 2023 Weidmüller Interface GmbH & Co. KG +# Lukas Funke lukas.funke@weidmueller.com +# +"""Bintool implementation for bootgen + +bootgen allows creating bootable SPL for Zynq(MP) + +Documentation is available via:: +https://www.xilinx.com/support/documents/sw_manuals/xilinx2022_1/ug1283-boot... + +Source code is available at: + +https://github.com/Xilinx/bootgen + +""" +import tempfile + +from binman import bintool +from u_boot_pylib import tools + +# pylint: disable=C0103 +class Bintoolbootgen(bintool.Bintool): + """Generate bootable fsbl image for zynq/zynqmp + + This bintools supports running Xilinx "bootgen" in order + to generate a bootable, authenticated image form an SPL. + + """ + def __init__(self, name): + super().__init__(name, 'Xilinx Bootgen', + version_regex=r'^****** Xilinx Bootgen', + version_args='') + + # pylint: disable=R0913 + def sign(self, arch, spl_elf_fname, pmufw_elf_fname, + psk_fname, ssk_fname, fsbl_config, auth_params, output_fname): + """ Sign FSBL(SPL) elf file and bundle it with pmu firmware + to a bootable image + + Args: + arch (str): Xilinx SoC architecture + spl_elf_fname (str): Filename of FSBL ELF file + pmufw_elf_fname (str): Filename pmu firmware + psk_fname (str): Filename of the primary secret key + ssk_fname (str): Filename of the secondary secret key + fsbl_config (str): FSBL config options + auth_params (str): Authentication parameter + output_fname (str): Filename where bootgen should write the result + """ + + _fsbl_config = f"[fsbl_config] {fsbl_config}" if fsbl_config else "" + _auth_params = f"[auth_params] {auth_params}" if auth_params else "" + + bif_template = f"""u_boot_spl_aes_rsa: {{ + [pskfile] {psk_fname} + [sskfile] {ssk_fname} + {_fsbl_config} + {_auth_params} + [ bootloader, + authentication = rsa, + destination_cpu=a53-0] {spl_elf_fname} + [pmufw_image] {pmufw_elf_fname} + }}""" + args = ["-arch", arch] + + with tempfile.NamedTemporaryFile(suffix=".bif", + dir=tools.get_output_dir()) as bif: + tools.write_file(bif.name, bif_template, binary=False) + args += ["-image", bif.name, '-w', '-o', output_fname] + self.run_cmd(*args) + + def fetch(self, method): + """Fetch bootgen from git""" + if method != bintool.FETCH_BUILD: + return None + + result = self.build_from_git( + 'https://github.com/Xilinx/bootgen', + 'all', + 'build/bootgen/bootgen') + return result

Hi,
On Thu, 29 Jun 2023 at 15:59, lukas.funke-oss@weidmueller.com wrote:
From: Lukas Funke lukas.funke@weidmueller.com
Add the Xilinx Bootgen as bintool. Xilinx Bootgen is used to create bootable SPL (FSBL in Xilinx terms) images for Zynq/ZynqMP devices. The btool creates a signed version of the SPL.
Signed-off-by: Lukas Funke lukas.funke@weidmueller.com
tools/binman/btool/bootgen.py | 82 +++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 tools/binman/btool/bootgen.py
diff --git a/tools/binman/btool/bootgen.py b/tools/binman/btool/bootgen.py new file mode 100644 index 0000000000..8bc727a54f --- /dev/null +++ b/tools/binman/btool/bootgen.py @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (C) 2023 Weidmüller Interface GmbH & Co. KG +# Lukas Funke lukas.funke@weidmueller.com +# +"""Bintool implementation for bootgen
+bootgen allows creating bootable SPL for Zynq(MP)
+Documentation is available via:: +https://www.xilinx.com/support/documents/sw_manuals/xilinx2022_1/ug1283-boot...
But you need to create some info here. It is good to have that link, but we also need some about of info about it here.
Overall this whole patch needs more docs and detail.
+Source code is available at:
+https://github.com/Xilinx/bootgen
+""" +import tempfile
+from binman import bintool +from u_boot_pylib import tools
+# pylint: disable=C0103 +class Bintoolbootgen(bintool.Bintool):
- """Generate bootable fsbl image for zynq/zynqmp
- This bintools supports running Xilinx "bootgen" in order
- to generate a bootable, authenticated image form an SPL.
- """
- def __init__(self, name):
super().__init__(name, 'Xilinx Bootgen',
version_regex=r'^\*\*\*\*\*\* Xilinx Bootgen',
version_args='')
- # pylint: disable=R0913
- def sign(self, arch, spl_elf_fname, pmufw_elf_fname,
psk_fname, ssk_fname, fsbl_config, auth_params, output_fname):
""" Sign FSBL(SPL) elf file and bundle it with pmu firmware
to a bootable image
Args:
arch (str): Xilinx SoC architecture
what options are valid?
spl_elf_fname (str): Filename of FSBL ELF file
what is FSBL?
pmufw_elf_fname (str): Filename pmu firmware
what is pmu?
psk_fname (str): Filename of the primary secret key
ssk_fname (str): Filename of the secondary secret key
why are there two keys? What format is used for these keys?
fsbl_config (str): FSBL config options
what options are available? What are valid vaulues for this arg?
auth_params (str): Authentication parameter
what are valid values?
output_fname (str): Filename where bootgen should write the result
This should really be handled automatically, I think. See how the mkimage etype creates an output filename.
"""
_fsbl_config = f"[fsbl_config] {fsbl_config}" if fsbl_config else ""
_auth_params = f"[auth_params] {auth_params}" if auth_params else ""
bif_template = f"""u_boot_spl_aes_rsa: {{
[pskfile] {psk_fname}
[sskfile] {ssk_fname}
{_fsbl_config}
{_auth_params}
[ bootloader,
authentication = rsa,
destination_cpu=a53-0] {spl_elf_fname}
[pmufw_image] {pmufw_elf_fname}
}}"""
args = ["-arch", arch]
with tempfile.NamedTemporaryFile(suffix=".bif",
dir=tools.get_output_dir()) as bif:
Please use a deterministic name - see mkimage etype for an example.
tools.write_file(bif.name, bif_template, binary=False)
args += ["-image", bif.name, '-w', '-o', output_fname]
self.run_cmd(*args)
- def fetch(self, method):
"""Fetch bootgen from git"""
if method != bintool.FETCH_BUILD:
return None
result = self.build_from_git(
'https://github.com/Xilinx/bootgen',
'all',
'build/bootgen/bootgen')
return result
-- 2.30.2
Regards, Simon

On 30.06.2023 06:18, Simon Glass wrote:
Hi,
On Thu, 29 Jun 2023 at 15:59, lukas.funke-oss@weidmueller.com wrote:
From: Lukas Funke lukas.funke@weidmueller.com
Add the Xilinx Bootgen as bintool. Xilinx Bootgen is used to create bootable SPL (FSBL in Xilinx terms) images for Zynq/ZynqMP devices. The btool creates a signed version of the SPL.
Signed-off-by: Lukas Funke lukas.funke@weidmueller.com
tools/binman/btool/bootgen.py | 82 +++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 tools/binman/btool/bootgen.py
diff --git a/tools/binman/btool/bootgen.py b/tools/binman/btool/bootgen.py new file mode 100644 index 0000000000..8bc727a54f --- /dev/null +++ b/tools/binman/btool/bootgen.py @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (C) 2023 Weidmüller Interface GmbH & Co. KG +# Lukas Funke lukas.funke@weidmueller.com +# +"""Bintool implementation for bootgen
+bootgen allows creating bootable SPL for Zynq(MP)
+Documentation is available via:: +https://www.xilinx.com/support/documents/sw_manuals/xilinx2022_1/ug1283-boot...
But you need to create some info here. It is good to have that link, but we also need some about of info about it here.
Overall this whole patch needs more docs and detail.
+Source code is available at:
+https://github.com/Xilinx/bootgen
+""" +import tempfile
+from binman import bintool +from u_boot_pylib import tools
+# pylint: disable=C0103 +class Bintoolbootgen(bintool.Bintool):
- """Generate bootable fsbl image for zynq/zynqmp
- This bintools supports running Xilinx "bootgen" in order
- to generate a bootable, authenticated image form an SPL.
- """
- def __init__(self, name):
super().__init__(name, 'Xilinx Bootgen',
version_regex=r'^\*\*\*\*\*\* Xilinx Bootgen',
version_args='')
- # pylint: disable=R0913
- def sign(self, arch, spl_elf_fname, pmufw_elf_fname,
psk_fname, ssk_fname, fsbl_config, auth_params, output_fname):
""" Sign FSBL(SPL) elf file and bundle it with pmu firmware
to a bootable image
Args:
arch (str): Xilinx SoC architecture
what options are valid?
spl_elf_fname (str): Filename of FSBL ELF file
what is FSBL?
pmufw_elf_fname (str): Filename pmu firmware
what is pmu?
psk_fname (str): Filename of the primary secret key
ssk_fname (str): Filename of the secondary secret key
why are there two keys? What format is used for these keys?
fsbl_config (str): FSBL config options
what options are available? What are valid vaulues for this arg?
auth_params (str): Authentication parameter
what are valid values?
output_fname (str): Filename where bootgen should write the result
This should really be handled automatically, I think. See how the mkimage etype creates an output filename.
Simon, thanks for the review!
What to you mean by automatically? The mkimage btool also has an 'output_fname' argument as well. And the output filename is passed down from the mkimage etype to the mkimage btool. Should the bootgen btool generate the output_fname by itself and pass it back to the caller?
"""
_fsbl_config = f"[fsbl_config] {fsbl_config}" if fsbl_config else ""
_auth_params = f"[auth_params] {auth_params}" if auth_params else ""
bif_template = f"""u_boot_spl_aes_rsa: {{
[pskfile] {psk_fname}
[sskfile] {ssk_fname}
{_fsbl_config}
{_auth_params}
[ bootloader,
authentication = rsa,
destination_cpu=a53-0] {spl_elf_fname}
[pmufw_image] {pmufw_elf_fname}
}}"""
args = ["-arch", arch]
with tempfile.NamedTemporaryFile(suffix=".bif",
dir=tools.get_output_dir()) as bif:
Please use a deterministic name - see mkimage etype for an example.
The .bif file is a temporary input file passed to 'bootgen'.
My intention was to make sure that the file is deleted afterwards. Would you prefer that the intermediate files are kept and the output file is deterministically created with "uniq = self.GetUniqueName()" and "output_fname = tools.get_output_filename('foo.%s' % uniq)"?
If so, I would also change the way the ELF file is created in order to keep the intermediate results.
tools.write_file(bif.name, bif_template, binary=False)
args += ["-image", bif.name, '-w', '-o', output_fname]
self.run_cmd(*args)
- def fetch(self, method):
"""Fetch bootgen from git"""
if method != bintool.FETCH_BUILD:
return None
result = self.build_from_git(
'https://github.com/Xilinx/bootgen',
'all',
'build/bootgen/bootgen')
return result
-- 2.30.2
Regards, Simon
Regards, Lukas

Hi Lukas,
On Fri, 30 Jun 2023 at 13:28, Lukas Funke lukas.funke-oss@weidmueller.com wrote:
On 30.06.2023 06:18, Simon Glass wrote:
Hi,
On Thu, 29 Jun 2023 at 15:59, lukas.funke-oss@weidmueller.com wrote:
From: Lukas Funke lukas.funke@weidmueller.com
Add the Xilinx Bootgen as bintool. Xilinx Bootgen is used to create bootable SPL (FSBL in Xilinx terms) images for Zynq/ZynqMP devices. The btool creates a signed version of the SPL.
Signed-off-by: Lukas Funke lukas.funke@weidmueller.com
tools/binman/btool/bootgen.py | 82 +++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 tools/binman/btool/bootgen.py
diff --git a/tools/binman/btool/bootgen.py b/tools/binman/btool/bootgen.py new file mode 100644 index 0000000000..8bc727a54f --- /dev/null +++ b/tools/binman/btool/bootgen.py @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (C) 2023 Weidmüller Interface GmbH & Co. KG +# Lukas Funke lukas.funke@weidmueller.com +# +"""Bintool implementation for bootgen
+bootgen allows creating bootable SPL for Zynq(MP)
+Documentation is available via:: +https://www.xilinx.com/support/documents/sw_manuals/xilinx2022_1/ug1283-boot...
But you need to create some info here. It is good to have that link, but we also need some about of info about it here.
Overall this whole patch needs more docs and detail.
+Source code is available at:
+https://github.com/Xilinx/bootgen
+""" +import tempfile
+from binman import bintool +from u_boot_pylib import tools
+# pylint: disable=C0103 +class Bintoolbootgen(bintool.Bintool):
- """Generate bootable fsbl image for zynq/zynqmp
- This bintools supports running Xilinx "bootgen" in order
- to generate a bootable, authenticated image form an SPL.
- """
- def __init__(self, name):
super().__init__(name, 'Xilinx Bootgen',
version_regex=r'^\*\*\*\*\*\* Xilinx Bootgen',
version_args='')
- # pylint: disable=R0913
- def sign(self, arch, spl_elf_fname, pmufw_elf_fname,
psk_fname, ssk_fname, fsbl_config, auth_params, output_fname):
""" Sign FSBL(SPL) elf file and bundle it with pmu firmware
to a bootable image
Args:
arch (str): Xilinx SoC architecture
what options are valid?
spl_elf_fname (str): Filename of FSBL ELF file
what is FSBL?
pmufw_elf_fname (str): Filename pmu firmware
what is pmu?
psk_fname (str): Filename of the primary secret key
ssk_fname (str): Filename of the secondary secret key
why are there two keys? What format is used for these keys?
fsbl_config (str): FSBL config options
what options are available? What are valid vaulues for this arg?
auth_params (str): Authentication parameter
what are valid values?
output_fname (str): Filename where bootgen should write the result
This should really be handled automatically, I think. See how the mkimage etype creates an output filename.
Simon, thanks for the review!
What to you mean by automatically? The mkimage btool also has an 'output_fname' argument as well. And the output filename is passed down from the mkimage etype to the mkimage btool. Should the bootgen btool generate the output_fname by itself and pass it back to the caller?
Oh I think I was getting confused with the 'filename' property used by the mkimage entry type. What you are doing here looks fine to me.
"""
_fsbl_config = f"[fsbl_config] {fsbl_config}" if fsbl_config else ""
_auth_params = f"[auth_params] {auth_params}" if auth_params else ""
bif_template = f"""u_boot_spl_aes_rsa: {{
[pskfile] {psk_fname}
[sskfile] {ssk_fname}
{_fsbl_config}
{_auth_params}
[ bootloader,
authentication = rsa,
destination_cpu=a53-0] {spl_elf_fname}
[pmufw_image] {pmufw_elf_fname}
}}"""
args = ["-arch", arch]
with tempfile.NamedTemporaryFile(suffix=".bif",
dir=tools.get_output_dir()) as bif:
Please use a deterministic name - see mkimage etype for an example.
The .bif file is a temporary input file passed to 'bootgen'.
My intention was to make sure that the file is deleted afterwards. Would you prefer that the intermediate files are kept and the output file is deterministically created with "uniq = self.GetUniqueName()" and "output_fname = tools.get_output_filename('foo.%s' % uniq)"?
If so, I would also change the way the ELF file is created in order to keep the intermediate results.
tools.write_file(bif.name, bif_template, binary=False)
args += ["-image", bif.name, '-w', '-o', output_fname]
self.run_cmd(*args)
- def fetch(self, method):
"""Fetch bootgen from git"""
if method != bintool.FETCH_BUILD:
return None
result = self.build_from_git(
'https://github.com/Xilinx/bootgen',
'all',
'build/bootgen/bootgen')
return result
-- 2.30.2
Regards, Simon

From: Lukas Funke lukas.funke@weidmueller.com
Add test for the 'xilinx_fsbl_auth' etype
Signed-off-by: Lukas Funke lukas.funke@weidmueller.com ---
tools/binman/ftest.py | 8 ++++++++ tools/binman/test/280_xilinx_fsb_auth.dts | 22 ++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 tools/binman/test/280_xilinx_fsb_auth.dts
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 3bd09d3fea..f0a7861649 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -6686,6 +6686,14 @@ fdt fdtmap Extract the devicetree blob from the fdtmap ['fit']) self.assertIn("Node '/fit': Missing tool: 'mkimage'", str(e.exception))
+ def testXilinxFsblAuth(self): + """Test xilinx_fsbl_auth etype""" + data = tools.read_file(self.TestFile("key.key")) + self._MakeInputFile("psk.pem", data) + self._MakeInputFile("ssk.pem", data) + self._SetupPmuFwlElf() + self._SetupSplElf() + self._DoReadFileRealDtb('280_xilinx_fsb_auth.dts')
def testSplPubkeyDtb(self): """Test u_boot_spl_pubkey_dtb etype""" diff --git a/tools/binman/test/280_xilinx_fsb_auth.dts b/tools/binman/test/280_xilinx_fsb_auth.dts new file mode 100644 index 0000000000..2bfd36c22e --- /dev/null +++ b/tools/binman/test/280_xilinx_fsb_auth.dts @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + xilinx_fsbl_auth { + + psk-filename = "psk.pem"; + ssk-filename = "ssk.pem"; + auth-params = "ppk_select=0", "spk_id=0x00000000"; + + u_boot_spl_nodtb { + }; + u_boot_spl_dtb { + }; + }; + }; +};

From: Lukas Funke lukas.funke@weidmueller.com
This adds a new etype 'xilinx_fsbl_auth'. Using this etype it is possible to created an authenticated SPL (FSBL in Xilinx terms) for ZynqMP boards.
The etype uses Xilinx Bootgen tools in order to transform the SPL into a bootable image and sign the image with a given primary and seconrady public key. For more information to signing the FSBL please refer to the Xilinx Bootgen documentation.
Here is an example of the etype in use:
spl { filename = "boot.signed.bin";
xilinx_fsbl_auth { psk-filename = "psk0.pem"; ssk-filename = "ssk0.pem"; auth-params = "ppk_select=0", "spk_id=0x00000000";
u_boot_spl_nodtb { }; u_boot_spl_dtb { }; }; };
For this to work the hash of the primary public key has to be fused into the ZynqMP device and authentication (RSA_EN) has to be set.
For testing purposes: if ppk hash check should be skipped one can add the property 'fsbl_config = "bh_auth_enable";' to the etype. However, this should only be used for testing(!).
Signed-off-by: Lukas Funke lukas.funke@weidmueller.com ---
tools/binman/etype/xilinx_fsbl_auth.py | 186 +++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 tools/binman/etype/xilinx_fsbl_auth.py
diff --git a/tools/binman/etype/xilinx_fsbl_auth.py b/tools/binman/etype/xilinx_fsbl_auth.py new file mode 100644 index 0000000000..ec70db9414 --- /dev/null +++ b/tools/binman/etype/xilinx_fsbl_auth.py @@ -0,0 +1,186 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2023 Weidmueller GmbH +# Written by Lukas Funke lukas.funke@weidmueller.com +# +# Entry-type module for signed ZynqMP boot images (boot.bin) +# + +import tempfile + +from collections import OrderedDict + +from binman import elf +from binman.entry import Entry + +from dtoc import fdt_util + +from u_boot_pylib import tools +from u_boot_pylib import command + +# pylint: disable=C0103 +class Entry_xilinx_fsbl_auth(Entry): + """Authenticated SPL flat binary for booting Xilinx + ZynqMP devices + + Properties / Entry arguments: + - auth-params: (Optional) Authentication parameters passed to bootgen + - fsbl-config: (Optional) FSBL parameters passed to bootgen + - pmufw-filename: Filename of PMU firmware. Default: pmu-firmware.elf + - psk-filename: Filename of primary public key + - ssk-filename: Filename of secondary public key + + The following example builds an authenticated boot image. The fuses of + the primary public key (ppk) should be fused together with the RSA_EN flag. + + spl { + filename = "boot.signed.bin"; + + xilinx_fsbl_auth { + psk-filename = "psk0.pem"; + ssk-filename = "ssk0.pem"; + auth-params = "ppk_select=0", "spk_id=0x00000000"; + + u_boot_spl_nodtb { + }; + u_boot_spl_pubkey_dtb { + algo = "sha384,rsa4096"; + required = "conf"; + key-name = "dev"; + }; + }; + }; + + For testing purposes, e.g. if no RSA_EN should be fused, one could add + the "bh_auth_enable" flag in the fsbl-config field. This will skip the + verification of the ppk fuses and boot the image, even if ppk hash is + invalid: + + xilinx_fsbl_auth { + psk-filename = "psk0.pem"; + ssk-filename = "ssk0.pem"; + ... + fsbl-config = "bh_auth_enable"; + ... + }; + + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + self.align_default = None + self.bootgen = None + self._entries = OrderedDict() + self._filename = self.GetDefaultFilename() + self.required_props = ['psk-filename', 'ssk-filename'] + + def ReadNode(self): + """Read properties from the xilinx_fsbl_auth node""" + super().ReadNode() + self._auth_params = fdt_util.GetStringList(self._node, + 'auth-params') + self._fsbl_config = fdt_util.GetStringList(self._node, + 'fsbl-config') + self._pmufw_filename = fdt_util.GetString(self._node, + 'pmufw-filename', + 'pmu-firmware.elf') + self._psk_filename = fdt_util.GetString(self._node, 'psk-filename', + 'psk.pem') + self._ssk_filename = fdt_util.GetString(self._node, 'ssk-filename', + 'ssk.pem') + self.ReadEntries() + + def ReadEntries(self): + """Read the subnodes to find out what should go in this image""" + for node in self._node.subnodes: + entry = Entry.Create(self, node) + entry.ReadNode() + self._entries[entry.name] = entry + + @classmethod + def __ToElf(self, data, output_fname): + """ Convert SPL object file to bootable ELF file. + + Args: + data (bytearray): u-boot-spl-nodtb + u-boot-spl-pubkey-dtb obj file + data + output_fname (str): Filename of converted FSBL ELF file + """ + platform_elfflags = [] + + gcc, args = tools.get_target_compile_tool('cc') + args += ['-dumpmachine'] + stdout = command.output(gcc, *args) + # split target machine triplet (arch, vendor, os) + arch, _, _ = stdout.split('-') + + if arch == 'aarch64': + platform_elfflags = ["-B", "aarch64", "-O", "elf64-littleaarch64"] + elif arch == 'x86_64': + # amd64 support makes no sense for the target platform, but we + # include it here to enable testing on hosts + platform_elfflags = ["-B", "i386", "-O", "elf64-x86-64"] + + spl_text_base = hex(elf.GetSymbolAddress( + tools.get_input_filename('spl/u-boot-spl'), ".text")) + + # Obj file to swap data and text section (rename-section) + with tempfile.NamedTemporaryFile(prefix="u-boot-spl-pubkey-", + suffix=".o.tmp", + dir=tools.get_output_dir())\ + as tmp_obj: + input_objcopy_fname = tmp_obj.name + # Align packed content to 4 byte boundary + pad = bytearray(tools.align(len(data), 4) - len(data)) + tools.write_file(input_objcopy_fname, data + pad) + # Final output elf file which contains a valid start address + with tempfile.NamedTemporaryFile(prefix="u-boot-spl-pubkey-elf-", + suffix=".o.tmp", + dir=tools.get_output_dir())\ + as tmp_elf_obj: + input_ld_fname = tmp_elf_obj.name + objcopy, args = tools.get_target_compile_tool('objcopy') + args += ["--rename-section", ".data=.text", + "-I", "binary"] + args += platform_elfflags + args += [input_objcopy_fname, input_ld_fname] + command.run(objcopy, *args) + + ld, args = tools.get_target_compile_tool('ld') + args += [input_ld_fname, '-o', output_fname, + "--defsym", f"_start={spl_text_base}", + "-Ttext", spl_text_base] + command.run(ld, *args) + + def ObtainContents(self, skip_entry=None, fake_size=0): + """Figure out the contents of an entry.""" + pmufw_elf_fname = tools.get_input_filename(self._pmufw_filename) + psk_fname = tools.get_input_filename(self._psk_filename) + ssk_fname = tools.get_input_filename(self._ssk_filename) + fsbl_config = ";".join(self._fsbl_config) if self._fsbl_config else None + auth_params = ";".join(self._auth_params) if self._auth_params else None + + with tempfile.NamedTemporaryFile(prefix="u-boot-spl-", + suffix=".elf", + dir=tools.get_output_dir())\ + as spl_elf: + + data, _, _ = self.collect_contents_to_file( + self._entries.values(), 'spl') + self.__ToElf(bytearray(data), spl_elf.name) + + self.bootgen.sign('zynqmp', spl_elf.name, pmufw_elf_fname, + psk_fname, ssk_fname, fsbl_config, + auth_params, self._filename) + self.SetContents(tools.read_file(self._filename)) + + return True + + # pylint: disable=C0116 + def GetDefaultFilename(self): + return 'spl/boot.bin' + + # pylint: disable=C0116 + def AddBintools(self, btools): + super().AddBintools(btools) + for entry in self._entries.values(): + entry.AddBintools(btools) + self.bootgen = self.AddBintool(btools, 'bootgen')
participants (4)
-
Lukas Funke
-
lukas.funke-oss@weidmueller.com
-
Quentin Schulz
-
Simon Glass