Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | # SPDX-License-Identifier: GPL-2.0+ # Copyright (c) 2023 Weidmueller GmbH # Written by Lukas Funke <lukas.funke@weidmueller.com> # # Entry-type module for Zynq(MP) boot images (boot.bin) # import tempfile from collections import OrderedDict from binman import elf from binman.etype.section import Entry_section from dtoc import fdt_util from u_boot_pylib import tools from u_boot_pylib import command # pylint: disable=C0103 class Entry_xilinx_bootgen(Entry_section): """Signed SPL boot image for 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-key-name-hint: Name of primary secret key to use for signing the secondardy public key. Format: .pem file - ssk-key-name-hint: Name of secondardy secret key to use for signing the boot image. Format: .pem file The etype is used to create a boot image for Xilinx ZynqMP devices. Information for signed images: In AMD/Xilinx SoCs, two pairs of public and secret keys are used - primary and secondary. The function of the primary public/secret key pair is to authenticate the secondary public/secret key pair. The function of the secondary key is to sign/verify the boot image. [1] AMD/Xilinx uses the following terms for private/public keys [1]: PSK = Primary Secret Key (Used to sign Secondary Public Key) PPK = Primary Public Key (Used to verify Secondary Public Key) SSK = Secondary Secret Key (Used to sign the boot image/partitions) SPK = Used to verify the actual boot image The following example builds a signed 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-bootgen { psk-key-name-hint = "psk0"; ssk-key-name-hint = "ssk0"; auth-params = "ppk_select=0", "spk_id=0x00000000"; u-boot-spl-nodtb { }; u-boot-spl-pubkey-dtb { algo = "sha384,rsa4096"; required = "conf"; key-name-hint = "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-bootgen { psk-key-name-hint = "psk0"; psk-key-name-hint = "ssk0"; ... fsbl-config = "bh_auth_enable"; ... }; [1] https://docs.xilinx.com/r/en-US/ug1283-bootgen-user-guide/Using-Authentication """ 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_key_name_hint = None self._ssk_key_name_hint = None self.align_default = None self.bootgen = None self.required_props = ['pmufw-filename', 'psk-key-name-hint', 'ssk-key-name-hint'] def ReadNode(self): """Read properties from the xilinx-bootgen 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') self._psk_key_name_hint = fdt_util.GetString(self._node, 'psk-key-name-hint') self._ssk_key_name_hint = fdt_util.GetString(self._node, 'ssk-key-name-hint') self.ReadEntries() @classmethod def _ToElf(cls, 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 = {"aarch64": ["-B", "aarch64", "-O", "elf64-littleaarch64"], # amd64 support makes no sense for the target # platform, but we include it here to enable # testing on hosts "x86_64": ["-B", "i386", "-O", "elf64-x86-64"] } 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('-') spl_elf = elf.DecodeElf(tools.read_file( tools.get_input_filename('spl/u-boot-spl')), 0) # 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[arch] 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={hex(spl_elf.entry)}", "-Ttext", hex(spl_elf.entry)] command.run(ld, *args) def BuildSectionData(self, required): """Pack node content, and create bootable, signed ZynqMP boot image 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, signed SPL image for Xilinx ZynqMP devices. """ data = super().BuildSectionData(required) 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_key_name_hint + ".pem") ssk_fname = tools.get_input_filename(self._ssk_key_name_hint + ".pem") 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') # 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 if self.bootgen.sign('zynqmp', spl_elf_fname, pmufw_elf_fname, psk_fname, ssk_fname, fsbl_config, auth_params, self._keysrc_enc, bootbin_fname) is None: # Bintool is missing; just use empty data as the output self.record_missing_bintool(self.bootgen) data = tools.get_bytes(0, 1024) else: data = tools.read_file(bootbin_fname) self.SetContents(data) return data # pylint: disable=C0116 def AddBintools(self, btools): super().AddBintools(btools) self.bootgen = self.AddBintool(btools, 'bootgen') |