[PATCH v2 0/8] Integrate EFI capsule tasks into u-boot's build flow

This patchset aims to bring two capsule related tasks under the u-boot build flow.
One is the embedding of the public key into the platform's dtb. The public key is in the form of an EFI Signature List(ESL) file and is used for capsule authentication. This is being achieved through binman, with an entry type added for raw images, and properties added to the binman fit entry type to enable embedding the ESL into all the DTB's that get packaged into the FIT. The path to the ESL file is being provided through a Kconfig symbol(CONFIG_EFI_CAPSULE_ESL_FILE).
Changes have also been made to the test flow so that the keys used for signing the capsule, and the ESL file, are generated prior to invoking the u-boot's build, which enables embedding the ESL file into the dtb as part of the u-boot build flow.
The other task is to add a make target for generating capsules. This is being achieved by adding support for parsing a config file to get the capsule generation parameters. Multiple payloads can be specified, resulting in generation of multiple capsules with a single invocation of the command. The path to the config file is to be specified through a Kconfig symbol(CONFIG_EFI_CAPSULE_CFG_FILE).
Changes have been made to the efi capsule test setup, whereby, with the above config symbol having been populated, the capsule files are generated through the make capsule command. The requisite config file has been placed under the test/py/tests/test_efi_capsule/ directory, which results in generation of the same set of capsule files.
Currently, the capsule authentication feature is tested on the sandbox and sandbox_flattree variants. The capsule generation through config file is enabled for the sandbox variant, with the sandbox_flattree variant generating capsules through the command-line parameters.
The document has been updated to reflect the above changes.
Changes since V1:
At a broad level, this version takes a stab at using binman for embedding the ESL file into the DTB's. This is being done for both raw images and FIT images. I had mentioned the issue of dependency when using binman for generating capsules[1], which is why I have stuck with using the make target for generating capsules.
[1] - https://lists.denx.de/pipermail/u-boot/2023-June/520814.html
* New patch * Use fdt_add_pubkey tool for adding the ESL into the dtb instead of using the shell script used in the earlier version. * Achieve the embedding of the ESL into the DTB through binman * Add an entry type fdt-esl-embed for embedding the ESL for raw images. * Add logic in binman's fit entry type for embedding the ESL into all the DTB's which are part of the FIT image. * Add corresponding documentation entries in binman for the above changes. * Add the logic to generate the keys in the yml files which get used in the CI setup. * Add a fdt-esl-embed node in sandbox's binman node with capsule authentication enabled. * Add a cfg-file parameter to pass the config file to the mkeficapsule tool. This results in generation of the same tool image irrespective of using command-line parameters or config file. * Call the mkeficapsule utility with the cfg-file parameter when building capsules via the config file.
Sughosh Ganu (8): fdt_add_pubkey: Add support for adding ESL public key under signature node capsule: authenticate: Embed capsule public key in platform's dtb test: py: Change capsule authenticate test flow doc: capsule: Document the new mechanism to embed ESL file into dtb tools: mkeficapsule: Add support for parsing capsule params from config file Makefile: Add a target for building capsules test: efi_capsule: Test capsule generation from config file doc: Add documentation to describe capsule config file format
.azure-pipelines.yml | 17 + .gitlab-ci.yml | 15 + Makefile | 9 + arch/sandbox/dts/sandbox.dts | 4 + arch/sandbox/dts/sandbox_capsule.dtsi | 12 + arch/sandbox/dts/test.dts | 4 + configs/sandbox_defconfig | 2 + configs/sandbox_flattree_defconfig | 1 + doc/develop/uefi/uefi.rst | 83 ++++- lib/efi_loader/Kconfig | 11 + test/py/conftest.py | 64 ++++ test/py/tests/test_efi_capsule/conftest.py | 142 ++++--- .../test_efi_capsule/sandbox_capsule_cfg.txt | 75 ++++ test/py/tests/test_efi_capsule/signature.dts | 10 - tools/Kconfig | 9 + tools/Makefile | 3 +- tools/binman/btool/fdt_add_pubkey.py | 73 ++++ tools/binman/entries.rst | 49 +++ tools/binman/etype/fdt_esl_embed.py | 80 ++++ tools/binman/etype/fit.py | 31 ++ tools/eficapsule.h | 110 ++++++ tools/fdt_add_pubkey.c | 16 +- tools/fdt_add_pubkey_esl.c | 98 +++++ tools/mkeficapsule.c | 84 +++-- tools/mkeficapsule_parse.c | 345 ++++++++++++++++++ 25 files changed, 1216 insertions(+), 131 deletions(-) create mode 100644 arch/sandbox/dts/sandbox_capsule.dtsi create mode 100644 test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt delete mode 100644 test/py/tests/test_efi_capsule/signature.dts create mode 100644 tools/binman/btool/fdt_add_pubkey.py create mode 100644 tools/binman/etype/fdt_esl_embed.py create mode 100644 tools/fdt_add_pubkey_esl.c create mode 100644 tools/mkeficapsule_parse.c

The fdt_add_pubkey tool is used for adding a public key to the devicetree, which is then used for verifying the FIT signatures. Add a function for embedding the public key in the form of an EFI Signature List(ESL) file as a property under the signature node of the device tree.
Unlike the public key added for FIT signature verification, the ESL file contents are added as a whole, as a property under the signature node in the DTB.
The public key in the ESL form is used by the capsule authentication feature for authenticating the capsules, prior to update.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- Changes since V1: * New patch * Use fdt_add_pubkey tool for adding the ESL into the dtb instead of using the shell script used in the earlier version.
tools/Makefile | 2 +- tools/fdt_add_pubkey.c | 16 +++++-- tools/fdt_add_pubkey_esl.c | 98 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 tools/fdt_add_pubkey_esl.c
diff --git a/tools/Makefile b/tools/Makefile index d793cf3bec..a5558eeb4d 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -152,7 +152,7 @@ dumpimage-objs := $(dumpimage-mkimage-objs) dumpimage.o mkimage-objs := $(dumpimage-mkimage-objs) mkimage.o fit_info-objs := $(dumpimage-mkimage-objs) fit_info.o fit_check_sign-objs := $(dumpimage-mkimage-objs) fit_check_sign.o -fdt_add_pubkey-objs := $(dumpimage-mkimage-objs) fdt_add_pubkey.o +fdt_add_pubkey-objs := $(dumpimage-mkimage-objs) fdt_add_pubkey.o fdt_add_pubkey_esl.o file2include-objs := file2include.o
ifneq ($(CONFIG_MX23)$(CONFIG_MX28)$(CONFIG_TOOLS_LIBCRYPTO),) diff --git a/tools/fdt_add_pubkey.c b/tools/fdt_add_pubkey.c index 5582d7a8ef..f536ab543b 100644 --- a/tools/fdt_add_pubkey.c +++ b/tools/fdt_add_pubkey.c @@ -2,18 +2,21 @@ #include <image.h> #include "fit_common.h"
+extern int fdt_embed_esl(const char *esl_file, void *keydest); + static const char *cmdname;
static const char *algo_name = "sha1,rsa2048"; /* -a <algo> */ static const char *keydir = "."; /* -k <keydir> */ static const char *keyname = "key"; /* -n <keyname> */ static const char *require_keys; /* -r <conf|image> */ +static const char *esl_file; /* -e <esl file> */ static const char *keydest; /* argv[n] */
static void __attribute__((__noreturn__)) print_usage(const char *msg) { fprintf(stderr, "Error: %s\n", msg); - fprintf(stderr, "Usage: %s [-a <algo>] [-k <keydir>] [-n <keyname>] [-r <conf|image>]" + fprintf(stderr, "Usage: %s [-a <algo>] [-e <esl file>] [-k <keydir>] [-n <keyname>] [-r <conf|image>]" " <fdt blob>\n", cmdname); fprintf(stderr, "Help information: %s [-h]\n", cmdname); exit(EXIT_FAILURE); @@ -23,6 +26,7 @@ static void __attribute__((__noreturn__)) print_help(void) { fprintf(stderr, "Options:\n" "\t-a <algo> Cryptographic algorithm. Optional parameter, default value: sha1,rsa2048\n" + "\t-e <esl file> EFI Signature List(ESL) file to embed into the FDT\n" "\t-k <keydir> Directory with public key. Optional parameter, default value: .\n" "\t-n <keyname> Public key name. Optional parameter, default value: key\n" "\t-r <conf|image> Required: If present this indicates that the key must be verified for the image / configuration to be considered valid.\n" @@ -34,7 +38,7 @@ static void process_args(int argc, char *argv[]) { int opt;
- while ((opt = getopt(argc, argv, "a:k:n:r:h")) != -1) { + while ((opt = getopt(argc, argv, "a:e:k:n:r:h")) != -1) { switch (opt) { case 'k': keydir = optarg; @@ -48,6 +52,9 @@ static void process_args(int argc, char *argv[]) case 'r': require_keys = optarg; break; + case 'e': + esl_file = optarg; + break; case 'h': print_help(); default: @@ -106,7 +113,10 @@ static int add_pubkey(struct image_sign_info *info) if (destfd < 0) exit(EXIT_FAILURE);
- ret = info->crypto->add_verify_data(info, dest_blob); + + ret = esl_file ? fdt_embed_esl(esl_file, dest_blob) : + info->crypto->add_verify_data(info, dest_blob); + if (ret == -ENOSPC) continue; else if (ret < 0) diff --git a/tools/fdt_add_pubkey_esl.c b/tools/fdt_add_pubkey_esl.c new file mode 100644 index 0000000000..de6ee41535 --- /dev/null +++ b/tools/fdt_add_pubkey_esl.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2023 Linaro Limited + * + */ + +#include <fcntl.h> +#include <stdio.h> +#include <unistd.h> + +#include <linux/libfdt.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "mkimage.h" + +#define ESL_SIG_NODENAME "signature" + +static int get_esl_pub_key(const char *esl_file, void **key_ptr, int *key_fd, + off_t *key_size) +{ + int ret; + struct stat pub_key; + + debug("%s: esl file => %s\n", __func__, esl_file); + *key_fd = open(esl_file, O_RDONLY); + if (*key_fd == -1) { + fprintf(stderr, "Unable to open %s: %s\n", + esl_file, strerror(errno)); + return -EACCES; + } + + ret = fstat(*key_fd, &pub_key); + if (ret == -1) { + fprintf(stderr, "Can't stat %s: %s\n", + esl_file, strerror(errno)); + ret = errno; + goto err; + } + *key_size = pub_key.st_size; + + /* mmap the public key esl file */ + *key_ptr = mmap(0, *key_size, PROT_READ, MAP_SHARED, *key_fd, 0); + if ((*key_ptr == MAP_FAILED) || (errno != 0)) { + fprintf(stderr, "Failed to mmap %s:%s\n", + esl_file, strerror(errno)); + ret = errno; + goto err; + } + + return 0; +err: + close(*key_fd); + + return ret; +} + +int fdt_embed_esl(const char *esl_file, void *keydest) +{ + int ret, key_fd; + off_t key_size = 0; + void *key_ptr = NULL; + int parent; + + ret = get_esl_pub_key(esl_file, &key_ptr, &key_fd, &key_size); + if (ret) { + debug("Unable to open the public key esl file\n"); + goto out; + } + + parent = fdt_subnode_offset(keydest, 0, ESL_SIG_NODENAME); + if (parent == -FDT_ERR_NOTFOUND) { + parent = fdt_add_subnode(keydest, 0, ESL_SIG_NODENAME); + if (parent < 0) { + ret = parent; + if (ret != -FDT_ERR_NOSPACE) { + fprintf(stderr, "Couldn't create signature node: %s\n", + fdt_strerror(parent)); + } + } + } + + if (ret) + goto out; + + ret = fdt_setprop(keydest, parent, "capsule-key", + key_ptr, key_size); + +out: + close(key_fd); + munmap(key_ptr, key_size); + + if (ret) + return ret == -FDT_ERR_NOSPACE ? -ENOSPC : -EIO; + + return ret; +}

The EFI capsule authentication logic in u-boot expects the public key in the form of an EFI Signature List(ESL) to be provided as part of the platform's dtb. Currently, the embedding of the ESL file into the dtb needs to be done manually. Use the fdt_add_pubkey tool from binman for embedding the ESL.
The capsule update feature is supported for both raw and FIT images. Embedding of the ESL for raw images needs to be done through a binman entry, fdt-esl-embed. For the FIT image, a couple of properties have been added to the fit node which facilitate embedding the ESL into all the DTB's that are packaged into the FIT image.
The path to the ESL file is specified through the CONFIG_EFI_CAPSULE_ESL_FILE symbol.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- Changes since V1: * Achieve the embedding of the ESL into the DTB through binman * Add an entry type fdt-esl-embed for embedding the ESL for raw images. * Add logic in binman's fit entry type for embedding the ESL into all the DTB's which are part of the FIT image. * Add corresponding documentation entries in binman for the above changes.
lib/efi_loader/Kconfig | 11 ++++ tools/binman/btool/fdt_add_pubkey.py | 73 +++++++++++++++++++++++++ tools/binman/entries.rst | 49 +++++++++++++++++ tools/binman/etype/fdt_esl_embed.py | 80 ++++++++++++++++++++++++++++ tools/binman/etype/fit.py | 31 +++++++++++ 5 files changed, 244 insertions(+) create mode 100644 tools/binman/btool/fdt_add_pubkey.py create mode 100644 tools/binman/etype/fdt_esl_embed.py
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..1326a1d109 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -234,6 +234,17 @@ config EFI_CAPSULE_MAX Select the max capsule index value used for capsule report variables. This value is used to create CapsuleMax variable.
+config EFI_CAPSULE_ESL_FILE + string "Path to the EFI Signature List File" + default "" + depends on EFI_CAPSULE_AUTHENTICATE + help + Provides the absolute path to the EFI Signature List + file which will be embedded in the platform's device + tree and used for capsule authentication at the time + of capsule update. + + config EFI_DEVICE_PATH_TO_TEXT bool "Device path to text protocol" default y diff --git a/tools/binman/btool/fdt_add_pubkey.py b/tools/binman/btool/fdt_add_pubkey.py new file mode 100644 index 0000000000..071ad166ff --- /dev/null +++ b/tools/binman/btool/fdt_add_pubkey.py @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2023 Linaro Limited +# +"""Bintool implementation for fdt_add_pubkey tool + +fdt_add_pubkey is a tool used for embedding a public key +into the DTB file. + +Currently, this is being used for embedding the EFI Signature +List(ESL) file into the DTB provided. The contents of the ESL +file get added as a property, capsule-key under the signature +node. + +The following are the command line options to be provided to the tool. +Options: + -a <algo> Cryptographic algorithm. Optional parameter, default value: sha1,rsa2048 + -e <esl file> EFI Signature List(ESL) file to embed into the FDT + -k <keydir> Directory with public key. Optional parameter, default value: . + -n <keyname> Public key name. Optional parameter, default value: key + -r <conf|image> Required: If present this indicates that the key must be verified for the image / configuration to be considered valid. + <fdt blob> FDT blob file for adding of the public key. Required parameter. + +""" + +from binman import bintool + +class Bintoolfdt_add_pubkey(bintool.Bintool): + """Handles the 'fdt_add_pubkey' tool + + This bintool supports running the fdt_add_pubkey tool for + embedding the public key into the dtb file provided. + + Currently, this is being used for embedding the EFI Signature + List(ESL) file into the DTB provided. + """ + def __init__(self, name): + super().__init__(name, 'Tool for generating adding pubkey to platform dtb') + + def add_esl(self, esl_fname, dtb_fname): + """Add an ESL public key into the DTB + + Args: + esl_fname: Path to the ESL file + dtb_name: Path to the DTB file + + Returns: + None + """ + args = [ + f'-e', + esl_fname, + dtb_fname + ] + + self.run_cmd(*args) + + def fetch(self, method): + """Fetch handler for fdt_add_pubkey + + This builds the tool from source + + Returns: + tuple: + str: Filename of fetched file to copy to a suitable directory + str: Name of temp directory to remove, or None + """ + if method != bintool.FETCH_BUILD: + return None + result = self.build_from_git( + 'https://source.denx.de/u-boot/u-boot.git', + 'tools', + 'tools/fdt_add_pubkey') + return result diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index b71af801fd..b89bb2cc1d 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -518,6 +518,38 @@ without the fdtmap header, so it can be viewed with `fdtdump`.
+.. _etype_fdt_esl_embed: + +Entry: fdt_esl_embed: An entry for embedding ESL contents into the DTB +---------------------------------------------------------------------- + +Properties / Entry Arguments: + - esl-file: Path to the ESL file. Usually specified + through the CONFIG_EFI_CAPSULE_ESL_FILE Kconfig + symbol. Mandatory property. + - dtb: A list of DTB file names into which the ESL + needs to be embedded. Provided as a string list, + similar to the compatible property. At least one + DTB file needs to be specified. Mandatory property. + - concat: An optional property which specifies the + files which need to be concatenated, along with + the third file into which the result needs to be + put into. + E.g. concat = "./u-boot-nodtb.bin", "./u-boot.dtb", "./u-boot.bin"; + The above concatenates u-boot-nodtb.bin and the + u-boot.dtb files, and puts the result into + u-boot.bin. + +This entry is used for embedding a public key in the form of an +EFI Signature List(ESL) file into the DTB. The public key is +used for authenticating capsules prior to capsule update. + +Once the dtb file has been updated by embedding the ESL, the concat +property allows regenerating the u-boot.bin comprising of the +u-boot-nodtb.bin and the updated dtb. + + + .. _etype_files:
Entry: files: A set of files arranged in a section @@ -615,6 +647,23 @@ The top-level 'fit' node supports the following special properties: `of-list` meaning that `-a of-list="dtb1 dtb2..."` should be passed to binman.
+ In addition to the above properties, a couple of properties exist for embedding + the EFI Signature List(ESL) public key file into the DTB's that are put into + the FIT image. + + The capsule update functionality is supported for the FIT and raw image types. + For the FIT image with capsule updates enabled along with capsule + authentication, it requires the the devicetree with the ESL public key. + Embedding the ESL into the DTBs is being done through a couple of additional + properties + + embed-esl + A boolean property which specifies that the ESL needs to be embedded + into all the DTB's that are put on the FIT image. + esl-file + Path to the ESL file that is to be embedded into the DTB's. + + Substitutions ~~~~~~~~~~~~~
diff --git a/tools/binman/etype/fdt_esl_embed.py b/tools/binman/etype/fdt_esl_embed.py new file mode 100644 index 0000000000..708ab138fd --- /dev/null +++ b/tools/binman/etype/fdt_esl_embed.py @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2023 Linaro Limited +# +# Entry-type module for Embedding ESL into FDTs +# + +from binman.entry import Entry +from dtoc import fdt_util +from u_boot_pylib import tools +from u_boot_pylib import tout + +class Entry_fdt_esl_embed(Entry): + """Entry for embedding the ESL file into devicetree(s) + + This is an entry for embedding the EFI Signature List(ESL) + file into one or multiple devicetrees. + + The ESL is a public key which is inserted as a property + under the signature node in the DTB. One or more dtb files + can be specified under the fdt-esl-embed node to embed the + ESL file. + + Properties / Entry arguments: + - esl-file: Path to the ESL file. Usually specified + through the CONFIG_EFI_CAPSULE_ESL_FILE Kconfig + symbol. Mandatory property. + - dtb: A list of DTB file names into which the ESL + needs to be embedded. Provided as a string list, + similar to the compatible property. At least one + DTB file needs to be specified. Mandatory property. + - concat: An optional property which specifies the + files which need to be concatenated, along with + the third file into which the result needs to be + put into. + E.g. concat = "./u-boot-nodtb.bin", "./u-boot.dtb", "./u-boot.bin"; + The above concatenates u-boot-nodtb.bin and the + u-boot.dtb files, and puts the result into + u-boot.bin. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + self.dtbs = [] + + def ReadNode(self): + super().ReadNode() + + self.esl = fdt_util.GetString(self._node, 'esl-file') + self.dtbs = fdt_util.GetStringList(self._node, 'dtb') + self.concat = fdt_util.GetStringList(self._node, 'concat') + + if not self.esl: + self.Raise('Path to the ESL file needs to be provided') + if not self.dtbs: + self.Raise('At least one DTB needs to be provided') + + def dtb_embed_esl(self): + for dtb in self.dtbs: + self.fdt_add_pubkey.add_esl(self.esl, + tools.get_input_filename(dtb)) + + def concat_binaries(self): + bins = self.concat + bin0_fname = tools.get_input_filename(bins[0]) + bin1_fname = tools.get_input_filename(bins[1]) + bin2_fname = tools.get_input_filename(bins[2]) + with open(bin0_fname, 'rb') as fd1, open(bin1_fname, 'rb') as fd2, open(bin2_fname, 'wb') as fd3: + bin1 = fd1.read() + bin2 = fd2.read() + bin1 += bin2 + + fd3.write(bin1) + + def ObtainContents(self): + self.dtb_embed_esl() + + if self.concat: + self.concat_binaries() + + def AddBintools(self, btools): + self.fdt_add_pubkey = self.AddBintool(btools, 'fdt_add_pubkey') diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py index c395706ece..a745d83b6f 100644 --- a/tools/binman/etype/fit.py +++ b/tools/binman/etype/fit.py @@ -20,6 +20,7 @@ OPERATIONS = { 'gen-fdt-nodes': OP_GEN_FDT_NODES, 'split-elf': OP_SPLIT_ELF, } +esl_embed_done = False
class Entry_fit(Entry_section):
@@ -81,6 +82,22 @@ class Entry_fit(Entry_section): `of-list` meaning that `-a of-list="dtb1 dtb2..."` should be passed to binman.
+ In addition to the above properties, a couple of properties exist for embedding + the EFI Signature List(ESL) public key file into the DTB's that are put into + the FIT image. + + The capsule update functionality is supported for the FIT and raw image types. + For the FIT image with capsule updates enabled along with capsule + authentication, it requires the the devicetree with the ESL public key. + Embedding the ESL into the DTBs is being done through a couple of additional + properties + + embed-esl + A boolean property which specifies that the ESL needs to be embedded + into all the DTB's that are put on the FIT image. + esl-file + Path to the ESL file that is to be embedded into the DTB's. + Substitutions ~~~~~~~~~~~~~
@@ -363,6 +380,9 @@ class Entry_fit(Entry_section): self._fdts = fdts.split() self._fit_default_dt = self.GetEntryArgsOrProps([EntryArg('default-dt', str)])[0] + self.embed_esl = fdt_util.GetBool(self._node, 'embed-esl') + if self.embed_esl: + self.esl = fdt_util.GetString(self._node, 'esl-file')
def _get_operation(self, base_node, node): """Get the operation referenced by a subnode @@ -434,6 +454,15 @@ class Entry_fit(Entry_section): Returns: bytes: Contents of the section """ + + global esl_embed_done + + if self.embed_esl and not esl_embed_done: + for dtb in self._fdts: + self.fdt_add_pubkey.add_esl(self.esl, + tools.get_input_filename(dtb + '.dtb')) + esl_embed_done = True + data = self._build_input() uniq = self.GetUniqueName() input_fname = tools.get_output_filename(f'{uniq}.itb') @@ -825,6 +854,8 @@ class Entry_fit(Entry_section): def AddBintools(self, btools): super().AddBintools(btools) self.mkimage = self.AddBintool(btools, 'mkimage') + if self.embed_esl: + self.fdt_add_pubkey = self.AddBintool(btools, 'fdt_add_pubkey')
def CheckMissing(self, missing_list): # We must use our private entry list for this since generator nodes

Currently, the keys and the EFI Signature List(ESL) file used for testing capsule authentication functionality are being generated after the u-boot image has been built. The ESL file is then embedded into the platform's DTB for capsule authentication.
This flow has been changed through an earlier commit, which embeds the ESL file into the platform's dtb(s) as part of the u-boot build. This requires generating the keys and the ESL file prior to invoking the u-boot build.
Bring about the same sequence of generating these files prior to invoking the u-boot build while testing.
For testing the EFI capsule functionality through manual invocation of pytest, changes have been made to the configuration to generate the openssl keys and the ESL file prior to u-boot build. Similar changes have been made in the CI test environment to generate the keys and the ESL prior to the u-boot build.
The binman node needed for invoking the corresponding entry module for embedding the ESL has been added for sandbox to facilitate testing.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- Changes since V1: * Add the logic to generate the keys in the yml files which get used in the CI setup. * Add a fdt-esl-embed node in sandbox's binman node with capsule authentication enabled.
.azure-pipelines.yml | 17 ++++++ .gitlab-ci.yml | 15 +++++ arch/sandbox/dts/sandbox.dts | 4 ++ arch/sandbox/dts/sandbox_capsule.dtsi | 12 ++++ arch/sandbox/dts/test.dts | 4 ++ configs/sandbox_defconfig | 1 + configs/sandbox_flattree_defconfig | 1 + test/py/conftest.py | 64 ++++++++++++++++++++ test/py/tests/test_efi_capsule/conftest.py | 37 +++-------- test/py/tests/test_efi_capsule/signature.dts | 10 --- 10 files changed, 125 insertions(+), 40 deletions(-) create mode 100644 arch/sandbox/dts/sandbox_capsule.dtsi delete mode 100644 test/py/tests/test_efi_capsule/signature.dts
diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index 3c1846a5bc..7681fabdac 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -398,6 +398,14 @@ stages: wget -O - https://github.com/riscv/opensbi/releases/download/v0.9/opensbi-0.9-rv-bin.t... | tar -C /tmp -xJ; export OPENSBI=/tmp/opensbi-0.9-rv-bin/share/opensbi/lp64/generic/firmware/fw_dynamic.bin; fi + if [[ "${TEST_PY_BD}" == "sandbox" ]] || [[ "${TEST_PY_BD}" == "sandbox_flattree" ]]; then + if [ ! -d "/tmp/capsules/" ]; then + mkdir -p /tmp/capsules/; + openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_SIGNER/ -keyout /tmp/capsules/SIGNER.key -out /tmp/capsules/SIGNER.crt -nodes -days 365; + openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_SIGNER/ -keyout /tmp/capsules/SIGNER2.key -out /tmp/capsules/SIGNER2.crt -nodes -days 365; + cert-to-efi-sig-list /tmp/capsules/SIGNER.crt /tmp/capsules/SIGNER.esl; + fi + fi # the below corresponds to .gitlab-ci.yml "script" cd ${WORK_DIR} export UBOOT_TRAVIS_BUILD_DIR=/tmp/${TEST_PY_BD}; @@ -580,6 +588,15 @@ stages: cd ${WORK_DIR} # make environment variables available as tests are running inside a container export BUILDMAN="${BUILDMAN}" + if [[ "${BUILDMAN}" == "sandbox" ]] || [[ "${BUILDMAN}" == "sandbox x86" ]]; then + if [ ! -d "/tmp/capsules/" ]; then + mkdir -p /tmp/capsules/; + openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_SIGNER/ -keyout /tmp/capsules/SIGNER.key -out /tmp/capsules/SIGNER.crt -nodes -days 365; + openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_SIGNER/ -keyout /tmp/capsules/SIGNER2.key -out /tmp/capsules/SIGNER2.crt -nodes -days 365; + cert-to-efi-sig-list /tmp/capsules/SIGNER.crt /tmp/capsules/SIGNER.esl; + fi + fi + git config --global --add safe.directory ${WORK_DIR} EOF cat << "EOF" >> build.sh diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e6c6ab3586..caf2db9476 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -37,6 +37,14 @@ stages: export OPENSBI=/tmp/opensbi-0.9-rv-bin/share/opensbi/lp64/generic/firmware/fw_dynamic.bin; fi
+ - if [[ "${TEST_PY_BD}" == "sandbox" ]] || [[ "${TEST_PY_BD}" == "sandbox_flattree" ]]; then + if [ ! -d "/tmp/capsules/" ]; then + mkdir -p /tmp/capsules/; + openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_SIGNER/ -keyout /tmp/capsules/SIGNER.key -out /tmp/capsules/SIGNER.crt -nodes -days 365; + openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_SIGNER/ -keyout /tmp/capsules/SIGNER2.key -out /tmp/capsules/SIGNER2.crt -nodes -days 365; + cert-to-efi-sig-list /tmp/capsules/SIGNER.crt /tmp/capsules/SIGNER.esl; + fi + fi after_script: - cp -v /tmp/${TEST_PY_BD}/*.{html,css} . - rm -rf /tmp/uboot-test-hooks /tmp/venv @@ -132,6 +140,13 @@ build all other platforms: script: - ret=0; git config --global --add safe.directory "${CI_PROJECT_DIR}"; + if [ ! -d "/tmp/capsules/" ]; then + mkdir -p /tmp/capsules/; + openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_SIGNER/ -keyout /tmp/capsules/SIGNER.key -out /tmp/capsules/SIGNER.crt -nodes -days 365; + openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_SIGNER/ -keyout /tmp/capsules/SIGNER2.key -out /tmp/capsules/SIGNER2.crt -nodes -days 365; + cert-to-efi-sig-list /tmp/capsules/SIGNER.crt /tmp/capsules/SIGNER.esl; + fi + ./tools/buildman/buildman -o /tmp -PEWM -x arm,powerpc || ret=$?; if [[ $ret -ne 0 ]]; then ./tools/buildman/buildman -o /tmp -seP; diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts index 12d3eff5fa..81a865eac8 100644 --- a/arch/sandbox/dts/sandbox.dts +++ b/arch/sandbox/dts/sandbox.dts @@ -106,3 +106,7 @@ #if IS_ENABLED(CONFIG_SUPPORT_VPL) #include "sandbox_vpl.dtsi" #endif + +#ifdef CONFIG_EFI_CAPSULE_AUTHENTICATE +#include "sandbox_capsule.dtsi" +#endif diff --git a/arch/sandbox/dts/sandbox_capsule.dtsi b/arch/sandbox/dts/sandbox_capsule.dtsi new file mode 100644 index 0000000000..59ef8d121b --- /dev/null +++ b/arch/sandbox/dts/sandbox_capsule.dtsi @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Devicetree file for embedding ESL into test DTB + */ + +&binman { + fdt-esl-embed { + esl-file = CONFIG_EFI_CAPSULE_ESL_FILE; + dtb = "test.dtb", "u-boot.dtb"; + concat = "./u-boot-nodtb.bin", "./u-boot.dtb", "./u-boot.bin"; + }; +}; diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index ff9f9222e6..06e1092400 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1828,3 +1828,7 @@ #ifdef CONFIG_SANDBOX_VPL #include "sandbox_vpl.dtsi" #endif + +#ifdef CONFIG_EFI_CAPSULE_AUTHENTICATE +#include "sandbox_capsule.dtsi" +#endif diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 1ec44d5b33..d8a2386bb0 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -339,6 +339,7 @@ CONFIG_EFI_RUNTIME_UPDATE_CAPSULE=y CONFIG_EFI_CAPSULE_ON_DISK=y CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y CONFIG_EFI_CAPSULE_AUTHENTICATE=y +CONFIG_EFI_CAPSULE_ESL_FILE="/tmp/capsules/SIGNER.esl" CONFIG_EFI_SECURE_BOOT=y CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y diff --git a/configs/sandbox_flattree_defconfig b/configs/sandbox_flattree_defconfig index e7657d40dc..8d60744771 100644 --- a/configs/sandbox_flattree_defconfig +++ b/configs/sandbox_flattree_defconfig @@ -226,6 +226,7 @@ CONFIG_EFI_RUNTIME_UPDATE_CAPSULE=y CONFIG_EFI_CAPSULE_ON_DISK=y CONFIG_EFI_CAPSULE_FIRMWARE_FIT=y CONFIG_EFI_CAPSULE_AUTHENTICATE=y +CONFIG_EFI_CAPSULE_ESL_FILE="/tmp/capsules/SIGNER.esl" CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y diff --git a/test/py/conftest.py b/test/py/conftest.py index fc9dd3a83f..45bd1749ee 100644 --- a/test/py/conftest.py +++ b/test/py/conftest.py @@ -80,6 +80,65 @@ def pytest_addoption(parser): help='Run sandbox under gdbserver. The argument is the channel '+ 'over which gdbserver should communicate, e.g. localhost:1234')
+def setup_capsule_auth_build(source_dir, build_dir, board_type, log): + """Setup the platform's build for capsule authenticate + + This generates the keys needed for signing the capsules along + with the EFI Signature List(ESL) file, with the capsule + authentication feature enabled. + + The ESL file is subsequently embedded into the platform's + dtb during the u-boot build, to be used for capsule + authentication. + + Two sets of keys are generated, namely SIGNER and SIGNER2. + The SIGNER2 key pair is used as a malicious key for testing the + the capsule authentication functionality. + + Args: + soruce_dir (str): Directory containing source code + build_dir (str): Directory to build in + board_type (str): board_type parameter (e.g. 'sandbox') + log (Logfile): Log file to use + + Returns: + Nothing. + """ + def run_command(name, cmd, source_dir): + with log.section(name): + if isinstance(cmd, str): + cmd = cmd.split() + runner = log.get_runner(name, None) + runner.run(cmd, cwd=source_dir) + runner.close() + log.status_pass('OK') + + capsule_sig_dir = '/tmp/capsules/' + sig_name = 'SIGNER' + mkdir_p(capsule_sig_dir) + name = 'openssl' + cmd = ( 'openssl req -x509 -sha256 -newkey rsa:2048 ' + '-subj /CN=TEST_SIGNER/ -keyout %s%s.key ' + '-out %s%s.crt -nodes -days 365' + % (capsule_sig_dir, sig_name, capsule_sig_dir, sig_name) + ) + run_command(name, cmd, source_dir) + + name = 'cert-to-efi-sig-list' + cmd = ( 'cert-to-efi-sig-list %s%s.crt %s%s.esl' + % (capsule_sig_dir, sig_name, capsule_sig_dir, sig_name) + ) + run_command(name, cmd, source_dir) + + sig_name = 'SIGNER2' + name = 'openssl' + cmd = ( 'openssl req -x509 -sha256 -newkey rsa:2048 ' + '-subj /CN=TEST_SIGNER/ -keyout %s%s.key ' + '-out %s%s.crt -nodes -days 365' + % (capsule_sig_dir, sig_name, capsule_sig_dir, sig_name) + ) + run_command(name, cmd, source_dir) + def run_build(config, source_dir, build_dir, board_type, log): """run_build: Build U-Boot
@@ -102,6 +161,11 @@ def run_build(config, source_dir, build_dir, board_type, log): o_opt = 'O=%s' % build_dir else: o_opt = '' + + capsule_auth_boards = ( 'sandbox', 'sandbox_flattree' ) + if board_type in capsule_auth_boards: + setup_capsule_auth_build(source_dir, build_dir, board_type, log) + cmds = ( ['make', o_opt, '-s', board_type + '_defconfig'], ['make', o_opt, '-s', '-j{}'.format(os.cpu_count())], diff --git a/test/py/tests/test_efi_capsule/conftest.py b/test/py/tests/test_efi_capsule/conftest.py index a337e62936..cec733942f 100644 --- a/test/py/tests/test_efi_capsule/conftest.py +++ b/test/py/tests/test_efi_capsule/conftest.py @@ -32,36 +32,6 @@ def efi_capsule_data(request, u_boot_config): check_call('mkdir -p %s' % data_dir, shell=True) check_call('mkdir -p %s' % install_dir, shell=True)
- capsule_auth_enabled = u_boot_config.buildconfig.get( - 'config_efi_capsule_authenticate') - if capsule_auth_enabled: - # Create private key (SIGNER.key) and certificate (SIGNER.crt) - check_call('cd %s; ' - 'openssl req -x509 -sha256 -newkey rsa:2048 ' - '-subj /CN=TEST_SIGNER/ -keyout SIGNER.key ' - '-out SIGNER.crt -nodes -days 365' - % data_dir, shell=True) - check_call('cd %s; %scert-to-efi-sig-list SIGNER.crt SIGNER.esl' - % (data_dir, EFITOOLS_PATH), shell=True) - - # Update dtb adding capsule certificate - check_call('cd %s; ' - 'cp %s/test/py/tests/test_efi_capsule/signature.dts .' - % (data_dir, u_boot_config.source_dir), shell=True) - check_call('cd %s; ' - 'dtc -@ -I dts -O dtb -o signature.dtbo signature.dts; ' - 'fdtoverlay -i %s/arch/sandbox/dts/test.dtb ' - '-o test_sig.dtb signature.dtbo' - % (data_dir, u_boot_config.build_dir), shell=True) - - # Create *malicious* private key (SIGNER2.key) and certificate - # (SIGNER2.crt) - check_call('cd %s; ' - 'openssl req -x509 -sha256 -newkey rsa:2048 ' - '-subj /CN=TEST_SIGNER/ -keyout SIGNER2.key ' - '-out SIGNER2.crt -nodes -days 365' - % data_dir, shell=True) - # Create capsule files # two regions: one for u-boot.bin and the other for u-boot.env check_call('cd %s; echo -n u-boot:Old > u-boot.bin.old; echo -n u-boot:New > u-boot.bin.new; echo -n u-boot-env:Old > u-boot.env.old; echo -n u-boot-env:New > u-boot.env.new' % data_dir, @@ -88,7 +58,14 @@ def efi_capsule_data(request, u_boot_config): (data_dir, u_boot_config.build_dir), shell=True)
+ capsule_auth_enabled = u_boot_config.buildconfig.get( + 'config_efi_capsule_authenticate') if capsule_auth_enabled: + capsules_path_dir = '/tmp/capsules/' + check_call('mv %s/* %s ' %(capsules_path_dir, data_dir), shell=True) + check_call('cp %s/arch/sandbox/dts/test.dtb %s/test_sig.dtb' % + (u_boot_config.build_dir, data_dir), shell=True) + # raw firmware signed with proper key check_call('cd %s; ' '%s/tools/mkeficapsule --index 1 --monotonic-count 1 ' diff --git a/test/py/tests/test_efi_capsule/signature.dts b/test/py/tests/test_efi_capsule/signature.dts deleted file mode 100644 index 078cfc76c9..0000000000 --- a/test/py/tests/test_efi_capsule/signature.dts +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -/dts-v1/; -/plugin/; - -&{/} { - signature { - capsule-key = /incbin/("SIGNER.esl"); - }; -};

Update the document to specify how the EFI Signature List(ESL) file can be embedded into the platform's dtb as part of the u-boot build.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- Changes since V1: None
doc/develop/uefi/uefi.rst | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-)
diff --git a/doc/develop/uefi/uefi.rst b/doc/develop/uefi/uefi.rst index ffe25ca231..f96762af39 100644 --- a/doc/develop/uefi/uefi.rst +++ b/doc/develop/uefi/uefi.rst @@ -495,20 +495,11 @@ and used by the steps highlighted below. ... }
-You can do step-4 manually with - -.. code-block:: console - - $ dtc -@ -I dts -O dtb -o signature.dtbo signature.dts - $ fdtoverlay -i orig.dtb -o new.dtb -v signature.dtbo - -where signature.dts looks like:: - - &{/} { - signature { - capsule-key = /incbin/("CRT.esl"); - }; - }; +You can perform step-4 by defining the Kconfig symbol +CONFIG_EFI_CAPSULE_ESL_FILE. This symbol defines the path to the esl +file generated in step-2. Once the symbol has been populated with the +path to the esl file, the esl file will automatically get embedded +into the platform's dtb as part of u-boot build.
Executing the boot manager ~~~~~~~~~~~~~~~~~~~~~~~~~~

Add support for specifying the parameters needed for capsule generation through a config file, instead of passing them through command-line. Parameters for more than a single capsule file can be specified, resulting in generation of multiple capsules through a single invocation of the command.
This path is to be used for generating capsules through a make target, with the parameters being parsed from the config file.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- Changes since V1: * Add a cfg-file parameter to pass the config file to the mkeficapsule tool. This results in generation of the same tool image irrespective of using command-line parameters or config file.
tools/Kconfig | 9 + tools/Makefile | 1 + tools/eficapsule.h | 110 ++++++++++++ tools/mkeficapsule.c | 84 +++++---- tools/mkeficapsule_parse.c | 345 +++++++++++++++++++++++++++++++++++++ 5 files changed, 519 insertions(+), 30 deletions(-) create mode 100644 tools/mkeficapsule_parse.c
diff --git a/tools/Kconfig b/tools/Kconfig index 539708f277..95f27b7c45 100644 --- a/tools/Kconfig +++ b/tools/Kconfig @@ -98,6 +98,15 @@ config TOOLS_MKEFICAPSULE optionally sign that file. If you want to enable UEFI capsule update feature on your target, you certainly need this.
+config EFI_CAPSULE_CFG_FILE + string "Path to the EFI Capsule Config File" + default "" + help + Path to the EFI capsule config file which provides the + parameters needed to build capsule(s). Parameters can be + provided for multiple payloads resulting in corresponding + capsule images being generated. + menuconfig FSPI_CONF_HEADER bool "FlexSPI Header Configuration" help diff --git a/tools/Makefile b/tools/Makefile index a5558eeb4d..833d8292e3 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -250,6 +250,7 @@ HOSTLDLIBS_mkeficapsule += \ HOSTLDLIBS_mkeficapsule += \ $(shell pkg-config --libs uuid 2> /dev/null || echo "-luuid") hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule +mkeficapsule-objs := mkeficapsule.o mkeficapsule_parse.o
# We build some files with extra pedantic flags to try to minimize things # that won't build on some weird host compiler -- though there are lots of diff --git a/tools/eficapsule.h b/tools/eficapsule.h index 072a4b5598..42e66c6d6a 100644 --- a/tools/eficapsule.h +++ b/tools/eficapsule.h @@ -52,6 +52,38 @@ typedef struct { /* flags */ #define CAPSULE_FLAGS_PERSIST_ACROSS_RESET 0x00010000
+enum capsule_type { + CAPSULE_NORMAL_BLOB = 0, + CAPSULE_ACCEPT, + CAPSULE_REVERT, +}; + +/** + * struct efi_capsule_params - Capsule parameters + * @image_guid: Guid value of the payload input image + * @image_index: Image index value + * @hardware_instance: Hardware instance to be used for the image + * @monotonic_count: Monotonic count value to be used for signed capsule + * @privkey_file: Path to private key used in capsule signing + * @cert_file: Path to public key certificate used in capsule signing + * @input_file: Path to payload input image + * @capsule_file: Path to the output capsule file + * @oemflags: Oemflags to be populated in the capsule header + * @capsule: Capsule Type, normal or accept or revert + */ +struct efi_capsule_params { + efi_guid_t *image_guid; + unsigned long image_index; + unsigned long hardware_instance; + uint64_t monotonic_count; + char *privkey_file; + char *cert_file; + char *input_file; + char *capsule_file; + unsigned long oemflags; + enum capsule_type capsule; +}; + struct efi_capsule_header { efi_guid_t capsule_guid; uint32_t header_size; @@ -113,4 +145,82 @@ struct efi_firmware_image_authentication { struct win_certificate_uefi_guid auth_info; } __packed;
+/** + * capsule_with_cfg_file() - Generate capsule from config file + * @cfg_file: Path to the config file + * + * Parse the capsule parameters from the config file and use the + * parameters for generating one or more capsules. + * + * Return: None + * + */ +void capsule_with_cfg_file(const char *cfg_file); + +/** + * convert_uuid_to_guid() - convert UUID to GUID + * @buf: UUID binary + * + * UUID and GUID have the same data structure, but their binary + * formats are different due to the endianness. See lib/uuid.c. + * Since uuid_parse() can handle only UUID, this function must + * be called to get correct data for GUID when parsing a string. + * + * The correct data will be returned in @buf. + */ +void convert_uuid_to_guid(unsigned char *buf); + +/** + * create_empty_capsule() - Generate an empty capsule + * @path: Path to the empty capsule file to be generated + * @guid: Guid value of the image for which empty capsule is generated + * @fw_accept: Flag to specify whether to generate accept or revert capsule + * + * Generate an empty capsule, either an accept or a revert capsule to be + * used to flag acceptance or rejection of an earlier executed firmware + * update operation. Being used in the FWU Multi Bank firmware update + * feature. + * + * Return: 0 if OK, -ve on error + * + */ +int create_empty_capsule(char *path, efi_guid_t *guid, bool fw_accept); + +/** + * create_fwbin - create an uefi capsule file + * @path: Path to a created capsule file + * @bin: Path to a firmware binary to encapsulate + * @guid: GUID of related FMP driver + * @index: Index number in capsule + * @instance: Instance number in capsule + * @mcount: Monotonic count in authentication information + * @private_file: Path to a private key file + * @cert_file: Path to a certificate file + * @oemflags: Capsule OEM Flags, bits 0-15 + * + * This function actually does the job of creating an uefi capsule file. + * All the arguments must be supplied. + * If either @private_file ror @cert_file is NULL, the capsule file + * won't be signed. + * + * Return: + * * 0 - on success + * * -1 - on failure + */ +int create_fwbin(char *path, char *bin, efi_guid_t *guid, + unsigned long index, unsigned long instance, + uint64_t mcount, char *privkey_file, char *cert_file, + uint16_t oemflags); + +/** + * print_usage() - Print the command usage string + * + * Prints the standard command usage string. Called in the case + * of incorrect parameters being passed to the tool. + * + * Return: None + * + */ +void print_usage(void); + #endif /* _EFI_CAPSULE_H */ diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index b71537beee..522dc4b474 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,13 +29,7 @@ static const char *tool_name = "mkeficapsule"; efi_guid_t efi_guid_fm_capsule = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID; efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
-static const char *opts_short = "g:i:I:v:p:c:m:o:dhAR"; - -enum { - CAPSULE_NORMAL_BLOB = 0, - CAPSULE_ACCEPT, - CAPSULE_REVERT, -} capsule_type; +static const char *opts_short = "g:i:I:v:p:c:m:o:f:dhAR";
static struct option options[] = { {"guid", required_argument, NULL, 'g'}, @@ -48,11 +42,21 @@ static struct option options[] = { {"fw-accept", no_argument, NULL, 'A'}, {"fw-revert", no_argument, NULL, 'R'}, {"capoemflag", required_argument, NULL, 'o'}, + {"cfg-file", required_argument, NULL, 'f'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0}, };
-static void print_usage(void) +/** + * print_usage() - Print the command usage string + * + * Prints the standard command usage string. Called in the case + * of incorrect parameters being passed to the tool. + * + * Return: None + * + */ +void print_usage(void) { fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n" "Options:\n" @@ -67,6 +71,7 @@ static void print_usage(void) "\t-A, --fw-accept firmware accept capsule, requires GUID, no image blob\n" "\t-R, --fw-revert firmware revert capsule, takes no GUID, no image blob\n" "\t-o, --capoemflag Capsule OEM Flag, an integer between 0x0000 and 0xffff\n" + "\t-f, --cfg-file <config file> config file with capsule parameters\n" "\t-h, --help print a help message\n", tool_name); } @@ -400,10 +405,10 @@ static void free_sig_data(struct auth_context *ctx) * * 0 - on success * * -1 - on failure */ -static int create_fwbin(char *path, char *bin, efi_guid_t *guid, - unsigned long index, unsigned long instance, - uint64_t mcount, char *privkey_file, char *cert_file, - uint16_t oemflags) +int create_fwbin(char *path, char *bin, efi_guid_t *guid, + unsigned long index, unsigned long instance, + uint64_t mcount, char *privkey_file, char *cert_file, + uint16_t oemflags) { struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule; @@ -580,7 +585,21 @@ void convert_uuid_to_guid(unsigned char *buf) buf[7] = c; }
-static int create_empty_capsule(char *path, efi_guid_t *guid, bool fw_accept) +/** + * create_empty_capsule() - Generate an empty capsule + * @path: Path to the empty capsule file to be generated + * @guid: Guid value of the image for which empty capsule is generated + * @fw_accept: Flag to specify whether to generate accept or revert capsule + * + * Generate an empty capsule, either an accept or a revert capsule to be + * used to flag acceptance or rejection of an earlier executed firmware + * update operation. Being used in the FWU Multi Bank firmware update + * feature. + * + * Return: 0 if OK, -ve on error + * + */ +int create_empty_capsule(char *path, efi_guid_t *guid, bool fw_accept) { struct efi_capsule_header header = { 0 }; FILE *f = NULL; @@ -643,6 +662,8 @@ int main(int argc, char **argv) uint64_t mcount; unsigned long oemflags; char *privkey_file, *cert_file; + char *cfg_file; + enum capsule_type capsule; int c, idx;
guid = NULL; @@ -651,8 +672,9 @@ int main(int argc, char **argv) mcount = 0; privkey_file = NULL; cert_file = NULL; + cfg_file = NULL; dump_sig = 0; - capsule_type = CAPSULE_NORMAL_BLOB; + capsule = CAPSULE_NORMAL_BLOB; oemflags = 0; for (;;) { c = getopt_long(argc, argv, opts_short, options, &idx); @@ -702,20 +724,20 @@ int main(int argc, char **argv) dump_sig = 1; break; case 'A': - if (capsule_type) { + if (capsule) { fprintf(stderr, "Select either of Accept or Revert capsule generation\n"); exit(1); } - capsule_type = CAPSULE_ACCEPT; + capsule = CAPSULE_ACCEPT; break; case 'R': - if (capsule_type) { + if (capsule) { fprintf(stderr, "Select either of Accept or Revert capsule generation\n"); exit(1); } - capsule_type = CAPSULE_REVERT; + capsule = CAPSULE_REVERT; break; case 'o': oemflags = strtoul(optarg, NULL, 0); @@ -725,6 +747,10 @@ int main(int argc, char **argv) exit(1); } break; + case 'f': + cfg_file = optarg; + capsule_with_cfg_file(cfg_file); + exit(EXIT_SUCCESS); default: print_usage(); exit(EXIT_SUCCESS); @@ -732,21 +758,21 @@ int main(int argc, char **argv) }
/* check necessary parameters */ - if ((capsule_type == CAPSULE_NORMAL_BLOB && - ((argc != optind + 2) || !guid || - ((privkey_file && !cert_file) || - (!privkey_file && cert_file)))) || - (capsule_type != CAPSULE_NORMAL_BLOB && - ((argc != optind + 1) || - ((capsule_type == CAPSULE_ACCEPT) && !guid) || - ((capsule_type == CAPSULE_REVERT) && guid)))) { + if ((capsule == CAPSULE_NORMAL_BLOB && + ((argc != optind + 2) || !guid || + ((privkey_file && !cert_file) || + (!privkey_file && cert_file)))) || + (capsule != CAPSULE_NORMAL_BLOB && + ((argc != optind + 1) || + (capsule == CAPSULE_ACCEPT && !guid) || + (capsule == CAPSULE_REVERT && guid)))) { print_usage(); exit(EXIT_FAILURE); }
- if (capsule_type != CAPSULE_NORMAL_BLOB) { + if (capsule != CAPSULE_NORMAL_BLOB) { if (create_empty_capsule(argv[argc - 1], guid, - capsule_type == CAPSULE_ACCEPT) < 0) { + capsule == CAPSULE_ACCEPT) < 0) { fprintf(stderr, "Creating empty capsule failed\n"); exit(EXIT_FAILURE); } @@ -756,6 +782,4 @@ int main(int argc, char **argv) fprintf(stderr, "Creating firmware capsule failed\n"); exit(EXIT_FAILURE); } - - exit(EXIT_SUCCESS); } diff --git a/tools/mkeficapsule_parse.c b/tools/mkeficapsule_parse.c new file mode 100644 index 0000000000..ef4f3f6705 --- /dev/null +++ b/tools/mkeficapsule_parse.c @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2023 Linaro Limited + */ + +/* + * The code in this file adds parsing ability to the mkeficapsule + * tool. This allows specifying parameters needed to build the capsule + * through the config file instead of specifying them on the command-line. + * Parameters can be specified for more than one payload, generating the + * corresponding capsule files. + * + * The parameters are specified in a "key:value" pair. All the parameters + * that are currently supported by the mkeficapsule tool can be specified + * in the config file. + * + * The example below shows four payloads. The first payload is an example + * of generating a signed capsule. The second payload is an example of + * generating an unsigned capsule. The third payload is an accept empty + * capsule, while the fourth payload is the revert empty capsule, used + * for the multi-bank firmware update feature. + * + * This functionality can be easily extended to generate a single capsule + * comprising multiple payloads. + + { + image-guid: 02f4d760-cfd5-43bd-8e2d-a42acb33c660 + hardware-instance: 0 + monotonic-count: 1 + payload: u-boot.bin + image-index: 1 + private-key: /path/to/priv/key + pub-key-cert: /path/to/pub/key + capsule: u-boot.capsule + } + { + image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e + hardware-instance: 0 + payload: u-boot.itb + image-index: 2 + oemflags: 0x8000 + capsule: fit.capsule + } + { + capsule-type: accept + image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e + capsule: accept.capsule + } + { + capsule-type: revert + capsule: revert.capsule + } +*/ + +#include <ctype.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <uuid/uuid.h> + +#include "eficapsule.h" + +#define PARAMS_START "{" +#define PARAMS_END "}" + +#define PSTART 2 +#define PEND 3 + +#define MALLOC_FAIL_STR "Unable to allocate memory\n" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +const char *capsule_params[] = { + "image-guid", "image-index", "private-key", + "pub-key-cert", "payload", "capsule", + "hardware-instance", "monotonic-count", + "capsule-type", "oemflags" }; + +static unsigned char params_start; +static unsigned char params_end; + +static void print_and_exit(const char *str) +{ + fprintf(stderr, "%s", str); + exit(EXIT_FAILURE); +} + +static int param_delim_checks(char *line, unsigned char *token) +{ + if (!strcmp(line, PARAMS_START)) { + if (params_start || !params_end) { + fprintf(stderr, "Earlier params processing still in progress. "); + fprintf(stderr, "Can't start processing a new params.\n"); + exit(EXIT_FAILURE); + } else { + params_start = 1; + params_end = 0; + *token = PSTART; + return 1; + } + } else if (!strcmp(line, PARAMS_END)) { + if (!params_start) { + fprintf(stderr, "Cannot put end braces without start braces. "); + fprintf(stderr, "Please check the documentation for reference config file syntax\n"); + exit(EXIT_FAILURE); + } else { + params_start = 0; + params_end = 1; + *token = PEND; + return 1; + } + } else if (!params_start) { + fprintf(stderr, "Params should be passed within braces. "); + fprintf(stderr, "Please check the documentation for reference config file syntax\n"); + exit(EXIT_FAILURE); + } + + return 0; +} + +static void add_guid(efi_guid_t **guid_param, char *guid) +{ + unsigned char uuid_buf[16]; + + *guid_param = malloc(sizeof(efi_guid_t)); + if (!*guid_param) + print_and_exit(MALLOC_FAIL_STR); + + if (uuid_parse(guid, uuid_buf)) + print_and_exit("Wrong guid format\n"); + + convert_uuid_to_guid(uuid_buf); + memcpy(*guid_param, uuid_buf, sizeof(efi_guid_t)); +} + +static void add_string(char **dst, char *val) +{ + *dst = strdup(val); + if (!*dst) + print_and_exit(MALLOC_FAIL_STR); +} + +static void match_and_populate_param(char *key, char *val, + struct efi_capsule_params *param) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(capsule_params); i++) { + if (!strcmp(key, capsule_params[i])) { + switch (i) { + case 0: + add_guid(¶m->image_guid, val); + return; + case 1: + param->image_index = strtoul(val, NULL, 0); + if (param->image_index == ULONG_MAX) + print_and_exit("Enter a valid value of index bewtween 1-255"); + return; + case 2: + add_string(¶m->privkey_file, val); + return; + case 3: + add_string(¶m->cert_file, val); + return; + case 4: + add_string(¶m->input_file, val); + return; + case 5: + add_string(¶m->capsule_file, val); + return; + case 6: + param->hardware_instance = strtoul(val, NULL, 0); + if (param->hardware_instance == ULONG_MAX) + print_and_exit("Enter a valid hardware instance value"); + return; + case 7: + param->monotonic_count = strtoull(val, NULL, 0); + if (param->monotonic_count == ULLONG_MAX) + print_and_exit("Enter a valid monotonic count value"); + return; + case 8: + if (!strcmp(val, "normal")) + param->capsule = CAPSULE_NORMAL_BLOB; + else if (!strcmp(val, "accept")) + param->capsule = CAPSULE_ACCEPT; + else if (!strcmp(val, "revert")) + param->capsule = CAPSULE_REVERT; + else + print_and_exit("Invalid type of capsule"); + + return; + case 9: + param->oemflags = strtoul(val, NULL, 0); + if (param->oemflags > 0xffff) + print_and_exit("OemFlags must be between 0x0 and 0xffff\n"); + return; + } + } + } + + fprintf(stderr, "Undefined param %s specified. ", key); + fprintf(stderr, "Please check the documentation for reference config file syntax\n"); + exit(EXIT_FAILURE); +} + +static int get_capsule_params(char *line, struct efi_capsule_params *params) +{ + char *key = NULL; + char *val = NULL; + unsigned char token; + + if (param_delim_checks(line, &token)) + return token; + + key = strtok(line, ":"); + if (key) + val = strtok(NULL, "\0"); + else + print_and_exit("Expect the params in a key:value pair\n"); + + match_and_populate_param(key, val, params); + + return 0; +} + +static char *skip_whitespace(char *line) +{ + char *ptr, *newline; + + ptr = malloc(strlen(line) + 1); + if (!ptr) + print_and_exit(MALLOC_FAIL_STR); + + for (newline = ptr; *line; line++) + if (!isblank(*line)) + *ptr++ = *line; + *ptr = '\0'; + return newline; +} + +static int parse_capsule_payload_params(FILE *fp, struct efi_capsule_params *params) +{ + char *line = NULL; + char *newline; + size_t n = 0; + ssize_t len; + + while ((len = getline(&line, &n, fp)) != -1) { + if (len == 1 && line[len - 1] == '\n') + continue; + + line[len - 1] = '\0'; + + newline = skip_whitespace(line); + + if (newline[0] == '#') + continue; + + if (get_capsule_params(newline, params) == PEND) + return 0; + } + + if (errno == EINVAL || errno == ENOMEM) { + fprintf(stderr, "getline() returned an error %s reading the line\n", + strerror(errno)); + exit(EXIT_FAILURE); + } else if (params_start == 1 || params_end == 0) { + fprintf(stderr, "Params should be passed within braces. "); + fprintf(stderr, "Please check the documentation for reference config file syntax\n"); + exit(EXIT_FAILURE); + } else { + return -1; + } +} + +static void params_dependency_check(struct efi_capsule_params *params) +{ + /* check necessary parameters */ + if ((params->capsule == CAPSULE_NORMAL_BLOB && + ((!params->input_file || !params->capsule_file || + !params->image_guid) || + ((params->privkey_file && !params->cert_file) || + (!params->privkey_file && params->cert_file)))) || + (params->capsule != CAPSULE_NORMAL_BLOB && + (!params->capsule_file || + (params->capsule == CAPSULE_ACCEPT && !params->image_guid) || + (params->capsule == CAPSULE_REVERT && params->image_guid)))) { + print_usage(); + exit(EXIT_FAILURE); + } +} + +static void generate_capsule(struct efi_capsule_params *params) +{ + if (params->capsule != CAPSULE_NORMAL_BLOB) { + if (create_empty_capsule(params->capsule_file, + params->image_guid, + params->capsule == + CAPSULE_ACCEPT) < 0) + print_and_exit("Creating empty capsule failed\n"); + } else if (create_fwbin(params->capsule_file, params->input_file, + params->image_guid, params->image_index, + params->hardware_instance, + params->monotonic_count, + params->privkey_file, + params->cert_file, + (uint16_t)params->oemflags) < 0) { + print_and_exit("Creating firmware capsule failed\n"); + } +} + +/** + * capsule_with_cfg_file() - Generate capsule from config file + * @cfg_file: Path to the config file + * + * Parse the capsule parameters from the config file and use the + * parameters for generating one or more capsules. + * + * Return: None + * + */ +void capsule_with_cfg_file(const char *cfg_file) +{ + FILE *fp; + struct efi_capsule_params params = { 0 }; + + fp = fopen(cfg_file, "r"); + if (!fp) { + fprintf(stderr, "Unable to open the capsule config file %s\n", + cfg_file); + exit(EXIT_FAILURE); + } + + params_start = 0; + params_end = 1; + + while (parse_capsule_payload_params(fp, ¶ms) != -1) { + params_dependency_check(¶ms); + generate_capsule(¶ms); + + memset(¶ms, 0, sizeof(struct efi_capsule_params)); + } +}

On 6/24/23 15:41, Sughosh Ganu wrote:
Add support for specifying the parameters needed for capsule generation through a config file, instead of passing them through command-line. Parameters for more than a single capsule file can be specified, resulting in generation of multiple capsules through a single invocation of the command.
This path is to be used for generating capsules through a make target, with the parameters being parsed from the config file.
Can you please also add support for adding comment to config file? Pretty much just ignore lines which starts with /* or so.
Thanks, Michal

On Fri, 14 Jul 2023 at 16:14, Michal Simek michal.simek@amd.com wrote:
On 6/24/23 15:41, Sughosh Ganu wrote:
Add support for specifying the parameters needed for capsule generation through a config file, instead of passing them through command-line. Parameters for more than a single capsule file can be specified, resulting in generation of multiple capsules through a single invocation of the command.
This path is to be used for generating capsules through a make target, with the parameters being parsed from the config file.
Can you please also add support for adding comment to config file? Pretty much just ignore lines which starts with /* or so.
Comments are currently supported with the '#' character, so that lines starting with # are considered as comments.
-sughosh

On 7/15/23 14:30, Sughosh Ganu wrote:
On Fri, 14 Jul 2023 at 16:14, Michal Simek michal.simek@amd.com wrote:
On 6/24/23 15:41, Sughosh Ganu wrote:
Add support for specifying the parameters needed for capsule generation through a config file, instead of passing them through command-line. Parameters for more than a single capsule file can be specified, resulting in generation of multiple capsules through a single invocation of the command.
This path is to be used for generating capsules through a make target, with the parameters being parsed from the config file.
Can you please also add support for adding comment to config file? Pretty much just ignore lines which starts with /* or so.
Comments are currently supported with the '#' character, so that lines starting with # are considered as comments.
Good thanks. M

Add a target for building EFI capsules. The capsule parameters are specified through a config file, and the path to the config file is specified through CONFIG_EFI_CAPSULE_CFG_FILE. When the config file is not specified, the command only builds tools.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- Changes since V1: * Call the mkeficapsule utility with the cfg-file parameter when building capsules via the config file.
Makefile | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/Makefile b/Makefile index 444baaefd0..7d22427699 100644 --- a/Makefile +++ b/Makefile @@ -1151,6 +1151,15 @@ dtbs: dts/dt.dtb dts/dt.dtb: u-boot $(Q)$(MAKE) $(build)=dts dtbs
+quiet_cmd_mkeficapsule = MKEFICAPSULE +cmd_mkeficapsule = $(objtree)/tools/mkeficapsule --cfg-file=$(CONFIG_EFI_CAPSULE_CFG_FILE) + +PHONY += capsule +capsule: tools +ifneq ($(CONFIG_EFI_CAPSULE_CFG_FILE),"") + $(call cmd,mkeficapsule) +endif + quiet_cmd_copy = COPY $@ cmd_copy = cp $< $@

On Sat, 24 Jun 2023 at 14:42, Sughosh Ganu sughosh.ganu@linaro.org wrote:
Add a target for building EFI capsules. The capsule parameters are specified through a config file, and the path to the config file is specified through CONFIG_EFI_CAPSULE_CFG_FILE. When the config file is not specified, the command only builds tools.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
Changes since V1:
- Call the mkeficapsule utility with the cfg-file parameter when building capsules via the config file.
Makefile | 9 +++++++++ 1 file changed, 9 insertions(+)
NAK, please do not add new output-file rules to Makefile

Support has been added to generate capsules through parameters specified in the config file. To bring this under the testing ambit, make changes in the EFI capsule test logic to generate the capsule files by parsing the config file, when the path to the config file is specified.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- Changes since V1: None
configs/sandbox_defconfig | 1 + test/py/tests/test_efi_capsule/conftest.py | 107 +++++++++++------- .../test_efi_capsule/sandbox_capsule_cfg.txt | 75 ++++++++++++ 3 files changed, 139 insertions(+), 44 deletions(-) create mode 100644 test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index d8a2386bb0..ba26816898 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -340,6 +340,7 @@ CONFIG_EFI_CAPSULE_ON_DISK=y CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y CONFIG_EFI_CAPSULE_AUTHENTICATE=y CONFIG_EFI_CAPSULE_ESL_FILE="/tmp/capsules/SIGNER.esl" +CONFIG_EFI_CAPSULE_CFG_FILE="/tmp/capsules/sandbox_capsule_cfg.txt" CONFIG_EFI_SECURE_BOOT=y CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y diff --git a/test/py/tests/test_efi_capsule/conftest.py b/test/py/tests/test_efi_capsule/conftest.py index cec733942f..5d7f05eef8 100644 --- a/test/py/tests/test_efi_capsule/conftest.py +++ b/test/py/tests/test_efi_capsule/conftest.py @@ -42,64 +42,83 @@ def efi_capsule_data(request, u_boot_config): check_call('cd %s; %s/tools/mkimage -f uboot_bin_env.its uboot_bin_env.itb' % (data_dir, u_boot_config.build_dir), shell=True) - check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot.bin.new Test01' % - (data_dir, u_boot_config.build_dir), - shell=True) - check_call('cd %s; %s/tools/mkeficapsule --index 2 --guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 u-boot.env.new Test02' % - (data_dir, u_boot_config.build_dir), - shell=True) - check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 058B7D83-50D5-4C47-A195-60D86AD341C4 u-boot.bin.new Test03' % - (data_dir, u_boot_config.build_dir), - shell=True) - check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 uboot_bin_env.itb Test04' % - (data_dir, u_boot_config.build_dir), - shell=True) - check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 058B7D83-50D5-4C47-A195-60D86AD341C4 uboot_bin_env.itb Test05' % - (data_dir, u_boot_config.build_dir), - shell=True)
capsule_auth_enabled = u_boot_config.buildconfig.get( 'config_efi_capsule_authenticate') if capsule_auth_enabled: capsules_path_dir = '/tmp/capsules/' - check_call('mv %s/* %s ' %(capsules_path_dir, data_dir), shell=True) + check_call('mv %s* %s/ ' %(capsules_path_dir, data_dir), shell=True) check_call('cp %s/arch/sandbox/dts/test.dtb %s/test_sig.dtb' % (u_boot_config.build_dir, data_dir), shell=True)
- # raw firmware signed with proper key - check_call('cd %s; ' - '%s/tools/mkeficapsule --index 1 --monotonic-count 1 ' - '--private-key SIGNER.key --certificate SIGNER.crt ' - '--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 ' - 'u-boot.bin.new Test11' - % (data_dir, u_boot_config.build_dir), + cfg_file = u_boot_config.buildconfig.get( + 'config_efi_capsule_cfg_file')[1:-1] + if cfg_file: + capsules_path_dir = '/tmp/capsules/' + check_call('mkdir -p %s ;' + 'cp -a %s/* %s/' % (capsules_path_dir, data_dir, capsules_path_dir), shell=True) - # raw firmware signed with *mal* key - check_call('cd %s; ' - '%s/tools/mkeficapsule --index 1 --monotonic-count 1 ' - '--private-key SIGNER2.key ' - '--certificate SIGNER2.crt ' - '--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 ' - 'u-boot.bin.new Test12' - % (data_dir, u_boot_config.build_dir), + check_call(' cp %s/test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt %s' + % (u_boot_config.source_dir, capsules_path_dir), shell=True) - # FIT firmware signed with proper key check_call('cd %s; ' - '%s/tools/mkeficapsule --index 1 --monotonic-count 1 ' - '--private-key SIGNER.key --certificate SIGNER.crt ' - '--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 ' - 'uboot_bin_env.itb Test13' - % (data_dir, u_boot_config.build_dir), + 'make capsule O=%s' % (u_boot_config.source_dir, u_boot_config.build_dir), shell=True) - # FIT firmware signed with *mal* key check_call('cd %s; ' - '%s/tools/mkeficapsule --index 1 --monotonic-count 1 ' - '--private-key SIGNER2.key ' - '--certificate SIGNER2.crt ' - '--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 ' - 'uboot_bin_env.itb Test14' - % (data_dir, u_boot_config.build_dir), + 'mv Test* %s/' + % (capsules_path_dir, data_dir), shell=True) + else: + check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot.bin.new Test01' % + (data_dir, u_boot_config.build_dir), + shell=True) + check_call('cd %s; %s/tools/mkeficapsule --index 2 --guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 u-boot.env.new Test02' % + (data_dir, u_boot_config.build_dir), shell=True) + check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 058B7D83-50D5-4C47-A195-60D86AD341C4 u-boot.bin.new Test03' % + (data_dir, u_boot_config.build_dir), + shell=True) + check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 uboot_bin_env.itb Test04' % + (data_dir, u_boot_config.build_dir), + shell=True) + check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 058B7D83-50D5-4C47-A195-60D86AD341C4 uboot_bin_env.itb Test05' % + (data_dir, u_boot_config.build_dir), + shell=True) + + if capsule_auth_enabled: + # raw firmware signed with proper key + check_call('cd %s; ' + '%s/tools/mkeficapsule --index 1 --monotonic-count 1 ' + '--private-key SIGNER.key --certificate SIGNER.crt ' + '--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 ' + 'u-boot.bin.new Test11' + % (data_dir, u_boot_config.build_dir), + shell=True) + # raw firmware signed with *mal* key + check_call('cd %s; ' + '%s/tools/mkeficapsule --index 1 --monotonic-count 1 ' + '--private-key SIGNER2.key ' + '--certificate SIGNER2.crt ' + '--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 ' + 'u-boot.bin.new Test12' + % (data_dir, u_boot_config.build_dir), + shell=True) + # FIT firmware signed with proper key + check_call('cd %s; ' + '%s/tools/mkeficapsule --index 1 --monotonic-count 1 ' + '--private-key SIGNER.key --certificate SIGNER.crt ' + '--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 ' + 'uboot_bin_env.itb Test13' + % (data_dir, u_boot_config.build_dir), + shell=True) + # FIT firmware signed with *mal* key + check_call('cd %s; ' + '%s/tools/mkeficapsule --index 1 --monotonic-count 1 ' + '--private-key SIGNER2.key ' + '--certificate SIGNER2.crt ' + '--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 ' + 'uboot_bin_env.itb Test14' + % (data_dir, u_boot_config.build_dir), + shell=True)
# Create a disk image with EFI system partition check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat %s %s' % diff --git a/test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt b/test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt new file mode 100644 index 0000000000..4e5065d538 --- /dev/null +++ b/test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt @@ -0,0 +1,75 @@ +{ + image-index: 1 + image-guid: 09D7CF52-0720-4710-91D1-08469B7FE9C8 + payload: /tmp/capsules/u-boot.bin.new + capsule: /tmp/capsules/Test01 +} +{ + image-index: 2 + image-guid: 5A7021F5-FEF2-48B4-AABA-832E777418C0 + payload: /tmp/capsules/u-boot.env.new + capsule: /tmp/capsules/Test02 +} +{ + image-index: 1 + image-guid: 058B7D83-50D5-4C47-A195-60D86AD341C4 + payload: /tmp/capsules/u-boot.bin.new + capsule: /tmp/capsules/Test03 + +} +{ + image-index: 1 + image-guid: 3673B45D-6A7C-46F3-9E60-ADABB03F7937 + payload: /tmp/capsules/uboot_bin_env.itb + capsule: /tmp/capsules/Test04 + +} +{ + image-index: 1 + image-guid: 058B7D83-50D5-4C47-A195-60D86AD341C4 + payload: /tmp/capsules/uboot_bin_env.itb + capsule: /tmp/capsules/Test05 + +} +{ + image-index: 1 + image-guid: 058B7D83-50D5-4C47-A195-60D86AD341C4 + payload: /tmp/capsules/uboot_bin_env.itb + capsule: /tmp/capsules/Test05 +} +{ + image-index: 1 + monotonic-count: 1 + private-key: /tmp/capsules/SIGNER.key + pub-key-cert: /tmp/capsules/SIGNER.crt + image-guid: 09D7CF52-0720-4710-91D1-08469B7FE9C8 + payload: /tmp/capsules/u-boot.bin.new + capsule: /tmp/capsules/Test11 +} +{ + image-index: 1 + monotonic-count: 1 + private-key: /tmp/capsules/SIGNER2.key + pub-key-cert: /tmp/capsules/SIGNER2.crt + image-guid: 09D7CF52-0720-4710-91D1-08469B7FE9C8 + payload: /tmp/capsules/u-boot.bin.new + capsule: /tmp/capsules/Test12 +} +{ + image-index: 1 + monotonic-count: 1 + private-key: /tmp/capsules/SIGNER.key + pub-key-cert: /tmp/capsules/SIGNER.crt + image-guid: 3673B45D-6A7C-46F3-9E60-ADABB03F7937 + payload: /tmp/capsules/uboot_bin_env.itb + capsule: /tmp/capsules/Test13 +} +{ + image-index: 1 + monotonic-count: 1 + private-key: /tmp/capsules/SIGNER2.key + pub-key-cert: /tmp/capsules/SIGNER2.crt + image-guid: 3673B45D-6A7C-46F3-9E60-ADABB03F7937 + payload: /tmp/capsules/uboot_bin_env.itb + capsule: /tmp/capsules/Test14 +}

The UEFI capsule can be generated either through command-line parameters, or, by specifying those in a config file. Add documentation to describe the format of the config file.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- Changes since V1: None
doc/develop/uefi/uefi.rst | 64 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+)
diff --git a/doc/develop/uefi/uefi.rst b/doc/develop/uefi/uefi.rst index f96762af39..09b32c9921 100644 --- a/doc/develop/uefi/uefi.rst +++ b/doc/develop/uefi/uefi.rst @@ -442,6 +442,70 @@ following command can be issued --guid c1b629f1-ce0e-4894-82bf-f0a38387e630 \ optee.bin optee.capsule
+Or alternatively, the capsule can be generated through a make target + +.. code-block:: bash + + $ make capsule + +Issuing the above make command requires specifying the capsule +parameters through a config file instead. The Kconfig symbol +CONFIG_EFI_CAPSULE_CFG_FILE is to be used for specifying the path to +the config file. + +The config file describes the parameters that are used for generating +one or more capsules. The parameters for a given capsule file are +specified within curly braces, in the form of "key:value" pairs. All +the parameters that are currently supported by the mkeficapsule tool +can be specified through the config file. + +The following are some example payload parameters specified through +the config file. + +.. code-block:: none + + { + image-guid: 02f4d760-cfd5-43bd-8e2d-a42acb33c660 + hardware-instance: 0 + monotonic-count: 1 + payload: u-boot.bin + image-index: 1 + private-key: /path/to/priv/key + pub-key-cert: /path/to/pub/key + capsule: u-boot.capsule + } + { + image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e + hardware-instance: 0 + payload: u-boot.itb + image-index: 2 + oemflags: 0x8000 + capsule: fit.capsule + } + { + capsule-type: accept + image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e + capsule: accept.capsule + } + { + capsule-type: revert + capsule: revert.capsule + } + +The following are the keys that specify the capsule parameters + +..code-block:: none + + image-guid: Image GUID + image-index: Image index value + private-key: Path to the private key file used for capsule signing + pub-key-cert: Path to the public key crt file used for capsule signing + payload: Path to the capsule payload file + capsule: Path to the output capsule file that is generated + hardware-instance: Hardware Instance value + monotonic-count: Monotonic count value + capsule-type: Specifies capsule type. normal(default), accept or revert + oemflags: 16bit Oemflags value to be used(populated in capsule header)
Enabling Capsule Authentication *******************************
participants (3)
-
Michal Simek
-
Simon Glass
-
Sughosh Ganu