binman: etype: Add xilinx-bootgen etype

This adds a new etype 'xilinx-bootgen'. By using this etype it is
possible to created an signed 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 secondary
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-bootgen {
            pmufw-filename = "pmu-firmware.elf";
            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-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>
Reviewed-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Lukas Funke
2023-08-05 11:33:18 -06:00
committed by Simon Glass
parent 7f51fe5c6d
commit 7fcfa9d930
2 changed files with 300 additions and 0 deletions
+75
View File
@@ -2667,3 +2667,78 @@ may be used instead.
.. _etype_xilinx_bootgen:
Entry: xilinx-bootgen: 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
+225
View File
@@ -0,0 +1,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')