Hi Lukas, On Thu, 6 Jul 2023 at 09:38, <lukas.funke-...@weidmueller.com> wrote: > > From: Lukas Funke <lukas.fu...@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.fu...@weidmueller.com> > > --- > > Changes in v2: > - Add 'keysrc-enc' property to pass down to Bootgen > - Improved documentation > - Use predictable output names for intermediated results > > tools/binman/entries.rst | 53 ++++++ > tools/binman/etype/xilinx_fsbl_auth.py | 213 +++++++++++++++++++++++++ > 2 files changed, 266 insertions(+) > create mode 100644 tools/binman/etype/xilinx_fsbl_auth.py > > diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst > index c3c5bda881..98ec3c82a5 100644 > --- a/tools/binman/entries.rst > +++ b/tools/binman/entries.rst > @@ -2462,3 +2462,56 @@ may be used instead. > > > > +.. _etype_xilinx_fsbl_auth: > + > +Entry: xilinx-fsbl-auth: Authenticated SPL for booting Xilinx ZynqMP devices > +---------------------------------------------------------------------------- > + > +Properties / Entry arguments: > + - auth-params: (Optional) Authentication parameters passed to bootgen > + - fsbl-config: (Optional) FSBL parameters passed to bootgen > + - keysrc-enc: (Optional) Key source when using decryption engine > + - 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. > + > +Example node:: > + > + 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. > + > +Example node:: > + > + xilinx-fsbl-auth { > + psk-filename = "psk0.pem"; > + ssk-filename = "ssk0.pem"; > + ... > + fsbl-config = "bh_auth_enable"; > + ... > + }; > + > + > + > diff --git a/tools/binman/etype/xilinx_fsbl_auth.py > b/tools/binman/etype/xilinx_fsbl_auth.py > new file mode 100644 > index 0000000000..72794ad2bc > --- /dev/null > +++ b/tools/binman/etype/xilinx_fsbl_auth.py > @@ -0,0 +1,213 @@ > +# SPDX-License-Identifier: GPL-2.0+ > +# Copyright (c) 2023 Weidmueller GmbH > +# Written by Lukas Funke <lukas.fu...@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 for booting Xilinx ZynqMP devices > + > + Properties / Entry arguments: > + - auth-params: (Optional) Authentication parameters passed to bootgen > + - fsbl-config: (Optional) FSBL parameters passed to bootgen > + - keysrc-enc: (Optional) Key source when using decryption engine > + - pmufw-filename: Filename of PMU firmware. Default: pmu-firmware.elf > + - psk-filename: Filename of primary public key > + - ssk-filename: Filename of secondary public key
Can you link to some docs that explains what all of these really are, e.g. format of files. > + > + 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. > + > + Example node:: > + > + 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. > + > + Example node:: > + > + 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._auth_params = None > + self._entries = OrderedDict() > + self._filename = None > + self._fsbl_config = None > + self._keysrc_enc = None > + self._pmufw_filename = None > + self._psk_filename = None > + self._ssk_filename = None > + self.align_default = None > + self.bootgen = None > + 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._filename = fdt_util.GetString(self._node, 'filename') > + self._fsbl_config = fdt_util.GetStringList(self._node, > + 'fsbl-config') > + self._keysrc_enc = fdt_util.GetString(self._node, > + 'keysrc-enc') > + 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 This looks like a section, so I suspect your class should be a subclass of entry_Section. Then you can drop this function since it is already there. > + > + @classmethod > + def __ToElf(self, data, output_fname): Single _ > + """ Convert SPL object file to bootable ELF file. Drop space before C > + > + 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): > + """ Pack node content, and create bootable, signed ZynqMP boot image Drop space before first P > + > + The method collects the content of this node (usually SPL + dtb) and > + converts them to an ELF file. The ELF file is passed to the > + Xilinx bootgen tool which packs the SPL ELF file together with > + Platform Management Unit (PMU) firmware into a bootable image > + for ZynqMP devices. The image is signed within this step. > + > + The result is a bootable, authenticated SPL image for Xilinx ZynqMP > + devices. > + > + """ > + bootbin_fname = self._filename if self._filename else \ > + tools.get_output_filename( > + f'boot.{self.GetUniqueName()}.bin') > + > + 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 > + > + spl_elf_fname = > tools.get_output_filename('u-boot-spl-pubkey.dtb.elf') > + > + # Collect node contents. This is usually the SPL concatenated > + # with the SPL dtb (device tree). > + data, _, _ = self.collect_contents_to_file( > + self._entries.values(), 'spl') data = self.collect_contents_to_file(...)[0] > + > + # We need to convert to node content (see above) into an ELF > + # file in order to be processed by bootgen. > + self.__ToElf(bytearray(data), spl_elf_fname) > + > + # Call Bootgen in order to sign the SPL > + self.bootgen.sign('zynqmp', spl_elf_fname, pmufw_elf_fname, > + psk_fname, ssk_fname, fsbl_config, > + auth_params, self._keysrc_enc, bootbin_fname) > + > + self.SetContents(tools.read_file(bootbin_fname)) > + > + return True If you convert this class to a sectoin then should you implement BuildSectionContents() instead of ObtainContents() > + > + # 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') > -- > 2.30.2 > I notice that test coverage is not 100% with these patches. Please use 'binman test -T' to see what is missing and add more test cases to the bottom of ftest.py Regards, Simon