1# SPDX-License-Identifier: GPL-2.0+
2# Copyright (C) 2023 Weidm��ller Interface GmbH & Co. KG
3# Lukas Funke <lukas.funke@weidmueller.com>
4#
5"""Bintool implementation for bootgen
6
7bootgen allows creating bootable SPL for Zynq(MP)
8
9Documentation is available via:
10https://www.xilinx.com/support/documents/sw_manuals/xilinx2022_1/ug1283-bootgen-user-guide.pdf
11
12Source code is available at:
13https://github.com/Xilinx/bootgen
14
15"""
16
17from binman import bintool
18from u_boot_pylib import tools
19
20# pylint: disable=C0103
21class Bintoolbootgen(bintool.Bintool):
22    """Generate bootable fsbl image for zynq/zynqmp
23
24    This bintools supports running Xilinx "bootgen" in order
25    to generate a bootable, authenticated image form an SPL.
26
27    """
28    def __init__(self, name):
29        super().__init__(name, 'Xilinx Bootgen',
30                         version_regex=r'^\*\*\*\*\*\* *Xilinx Bootgen *(.*)',
31                         version_args='-help')
32
33    # pylint: disable=R0913
34    def sign(self, arch, spl_elf_fname, pmufw_elf_fname,
35             psk_fname, ssk_fname, fsbl_config, auth_params, keysrc_enc,
36             output_fname):
37        """Sign SPL elf file and bundle it with PMU firmware into an image
38
39        The method bundels the SPL together with a 'Platform Management Unit'
40        (PMU)[1] firmware into a single bootable image. The image in turn is
41        signed with the provided 'secondary secret key' (ssk), which in turn is
42        signed with the 'primary secret key' (psk). In order to verify the
43        authenticity of the ppk, it's hash has to be fused into the device
44        itself.
45
46        In Xilinx terms the SPL is usually called 'FSBL'
47        (First Stage Boot Loader). The jobs of the SPL and the FSBL are mostly
48        the same: load bitstream, bootstrap u-boot.
49
50        Args:
51            arch (str): Xilinx SoC architecture. Currently only 'zynqmp' is
52                supported.
53            spl_elf_fname (str): Filename of SPL ELF file. The filename must end
54                with '.elf' in order for bootgen to recognized it as an ELF
55                file. Otherwise the start address field is missinterpreted.
56            pmufw_elf_fname (str): Filename PMU ELF firmware.
57            psk_fname (str): Filename of the primary secret key (psk). The psk
58                is a .pem file which holds the RSA private key used for signing
59                the secondary secret key.
60            ssk_fname (str): Filename of the secondary secret key. The ssk
61                is a .pem file which holds the RSA private key used for signing
62                the actual boot firmware.
63            fsbl_config (str): FSBL config options. A string list of fsbl config
64                options. Valid values according to [2] are:
65                "bh_auth_enable": Boot Header Authentication Enable: RSA
66                    authentication of the bootimage is done
67                    excluding the verification of PPK hash and SPK ID. This is
68                    useful for debugging before bricking a device.
69                "auth_only": Boot image is only RSA signed. FSBL should not be
70                    decrypted. See the
71                    Zynq UltraScale+ Device Technical Reference Manual (UG1085)
72                    for more information.
73                There are more options which relate to PUF (physical unclonable
74                functions). Please refer to Xilinx manuals for further info.
75            auth_params (str): Authentication parameter. A semicolon separated
76                list of authentication parameters. Valid values according to [3]
77                are:
78                "ppk_select=<0|1>" - Select which ppk to use
79                "spk_id=<32-bit spk id>" - Specifies which SPK can be
80                    used or revoked, default is 0x0
81                "spk_select=<spk-efuse/user-efuse>" - To differentiate spk and
82                    user efuses.
83                "auth_header" - To authenticate headers when no partition
84                    is authenticated.
85            keysrc_enc (str): This specifies the Key source for encryption.
86                Valid values according to [3] are:
87                "bbram_red_key" - RED key stored in BBRAM
88                "efuse_red_key" - RED key stored in eFUSE
89                "efuse_gry_key" - Grey (Obfuscated) Key stored in eFUSE.
90                "bh_gry_key" - Grey (Obfuscated) Key stored in boot header
91                "bh_blk_key" - Black Key stored in boot header
92                "efuse_blk_key" - Black Key stored in eFUSE
93                "kup_key" - User Key
94
95            output_fname (str): Filename where bootgen should write the result
96
97        Returns:
98            str: Bootgen output from stdout
99
100        [1] https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18841724/PMU+Firmware
101        [2] https://docs.xilinx.com/r/en-US/ug1283-bootgen-user-guide/fsbl_config
102        [3] https://docs.xilinx.com/r/en-US/ug1283-bootgen-user-guide/auth_params
103        [4] https://docs.xilinx.com/r/en-US/ug1283-bootgen-user-guide/keysrc_encryption
104        """
105
106        _fsbl_config = f"[fsbl_config] {fsbl_config}" if fsbl_config else ""
107        _auth_params = f"[auth_params] {auth_params}" if auth_params else ""
108        _keysrc_enc  = f"[keysrc_encryption] {keysrc_enc}" if keysrc_enc else ""
109
110        bif_template = f"""u_boot_spl_aes_rsa: {{
111            [pskfile] {psk_fname}
112            [sskfile] {ssk_fname}
113            {_keysrc_enc}
114            {_fsbl_config}
115            {_auth_params}
116            [ bootloader,
117              authentication = rsa,
118              destination_cpu=a53-0] {spl_elf_fname}
119            [pmufw_image] {pmufw_elf_fname}
120        }}"""
121        args = ["-arch", arch]
122
123        bif_fname = tools.get_output_filename('bootgen-in.sign.bif')
124        tools.write_file(bif_fname, bif_template, False)
125        args += ["-image", bif_fname, '-w', '-o', output_fname]
126        return self.run_cmd(*args)
127
128    def fetch(self, method):
129        """Fetch bootgen from git"""
130        if method != bintool.FETCH_BUILD:
131            return None
132
133        result = self.build_from_git(
134            'https://github.com/Xilinx/bootgen',
135            ['all'],
136            'bootgen')
137        return result
138