[PATCH v4 00/12] 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 by adding the signature node containing the capsule public key in the architecture's u-boot.dtsi file. Currently, the u-boot.dtsi file has been added for the sandbox and arm architectures. 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.
The other task is related to generation of capsules. Support is being added to generate capsules by specifying the capsule parameters in a config file. Calling the mkeficapsule tool then results in generation of the corresponding capsule files. The capsules can be generated as part of u-boot build, and this is being achieved through binman, by adding a capsule entry type. The capsules can be generated either by specifying the capsule parameters in a config file, or through specifying them as properties under the capsule entry node. If using the config file, the path to the config file is to be specified through a Kconfig symbol(CONFIG_EFI_CAPSULE_CFG_FILE).
Changes have also been made to the efi capsule update feature testing setup on the sandbox variants. Currently, the capsule files and the public key ESL file are generated after u-boot has been built. This logic has been changed so that the capsule input files along with the keys needed for capsule signing and authentication are generated prior to initiation of the u-boot build. The placement of all the files needed for generation of capsules, along with the generated capsule files is under the /tmp/capsules/ directory.
Currently, the capsule update feature is tested on the sandbox and sandbox_flattree variants in CI. 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 V3: * New patch to support passing multiple commands to the build_from_git * Put the two ifdef statements together in arm architecture's u-boot.dtsi file. * Remove the extra blank line in the Kconfig. function to build the tool. * Add support for firmware versioning, needed after rebasing on current master. * Add test cases for covering the various capsule generation scenarios. * Add function comments in the mkeficapsule bintool. * Fix the fetch method of the mkeficapsule bintool to enable building the tool. * Add more details about the capsule parameters in the documentation as well as the code. * Fix order of module imports, and addition of blank lines in the capsule.py file. * Use SetContents in the ObtainContents method. * Move the paragraph on version support under a separate subsection. * Move the description on generating capsules through config file under the section to describe capsule generation. * Add a subsection highlighting generation of capsules through binman. * Remove whitespace in the command to generate capsule keys. * Use fstrings for format specifiers. * Rebase on top of current master to work with test configuration for version support in capsule updates. * Use fstrings for format specifiers. * Add entries for generating capsules with version parameter. * Use blob nodes instead of incbin for including the binaries in FIT image. * Enable generation of capsules with versioning support.
Sughosh Ganu (12): binman: bintool: Build a tool from a list of commands nuvoton: npcm845-evb: Add a newline at the end of file capsule: authenticate: Add capsule public key in platform's dtb doc: capsule: Document the new mechanism to embed ESL file into dtb tools: mkeficapsule: Add support for parsing capsule params from config file binman: capsule: Add support for generating capsules doc: Add documentation to highlight capsule generation related updates CI: capsule: Setup the files needed for capsule update testing test: py: Setup capsule files for testing test: capsule: Remove public key embed logic from capsule update test sandbox: capsule: Add a config file for generating capsules sandbox: capsule: Generate capsule related files through binman
.azure-pipelines.yml | 26 ++ .gitlab-ci.yml | 24 ++ arch/arm/dts/nuvoton-npcm845-evb.dts | 2 +- arch/arm/dts/u-boot.dtsi | 17 + arch/sandbox/dts/u-boot.dtsi | 282 ++++++++++++++ configs/sandbox_defconfig | 3 + configs/sandbox_flattree_defconfig | 1 + configs/sandbox_spl_defconfig | 1 + doc/develop/uefi/uefi.rst | 106 +++++- lib/efi_loader/Kconfig | 10 + lib/efi_loader/Makefile | 7 + test/py/conftest.py | 89 +++++ test/py/tests/test_efi_capsule/conftest.py | 164 +------- .../test_efi_capsule/sandbox_capsule_cfg.txt | 162 ++++++++ test/py/tests/test_efi_capsule/signature.dts | 10 - .../tests/test_efi_capsule/uboot_bin_env.its | 36 -- tools/Kconfig | 16 + tools/Makefile | 1 + tools/binman/bintool.py | 19 +- tools/binman/btool/_testing.py | 3 +- tools/binman/btool/fiptool.py | 4 +- tools/binman/btool/futility.py | 4 +- tools/binman/btool/mkeficapsule.py | 158 ++++++++ tools/binman/entries.rst | 37 ++ tools/binman/etype/capsule.py | 132 +++++++ tools/binman/ftest.py | 127 +++++++ tools/binman/test/282_capsule.dts | 18 + tools/binman/test/283_capsule_signed.dts | 20 + tools/binman/test/284_capsule_conf.dts | 14 + tools/binman/test/285_capsule_missing_key.dts | 19 + .../binman/test/286_capsule_missing_index.dts | 17 + .../binman/test/287_capsule_missing_guid.dts | 17 + .../test/288_capsule_missing_payload.dts | 17 + tools/binman/test/289_capsule_missing.dts | 17 + tools/binman/test/290_capsule_version.dts | 19 + tools/binman/test/capsule_cfg.txt | 6 + tools/eficapsule.h | 115 ++++++ tools/mkeficapsule.c | 87 +++-- tools/mkeficapsule_parse.c | 352 ++++++++++++++++++ 39 files changed, 1900 insertions(+), 259 deletions(-) create mode 100644 arch/arm/dts/u-boot.dtsi create mode 100644 arch/sandbox/dts/u-boot.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 delete mode 100644 test/py/tests/test_efi_capsule/uboot_bin_env.its create mode 100644 tools/binman/btool/mkeficapsule.py create mode 100644 tools/binman/etype/capsule.py create mode 100644 tools/binman/test/282_capsule.dts create mode 100644 tools/binman/test/283_capsule_signed.dts create mode 100644 tools/binman/test/284_capsule_conf.dts create mode 100644 tools/binman/test/285_capsule_missing_key.dts create mode 100644 tools/binman/test/286_capsule_missing_index.dts create mode 100644 tools/binman/test/287_capsule_missing_guid.dts create mode 100644 tools/binman/test/288_capsule_missing_payload.dts create mode 100644 tools/binman/test/289_capsule_missing.dts create mode 100644 tools/binman/test/290_capsule_version.dts create mode 100644 tools/binman/test/capsule_cfg.txt create mode 100644 tools/mkeficapsule_parse.c

Add support to build a tool from source with a list of commands. This is useful when a tool can be built with multiple commands instead of a single command.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- Changes since V3: * New patch to support passing multiple commands to the build_from_git function to build the tool.
tools/binman/bintool.py | 19 +++++++++++-------- tools/binman/btool/_testing.py | 3 ++- tools/binman/btool/fiptool.py | 4 +++- tools/binman/btool/futility.py | 4 +++- 4 files changed, 19 insertions(+), 11 deletions(-)
diff --git a/tools/binman/bintool.py b/tools/binman/bintool.py index 81629683df..279bf2fec4 100644 --- a/tools/binman/bintool.py +++ b/tools/binman/bintool.py @@ -328,7 +328,7 @@ class Bintool: return result.stdout
@classmethod - def build_from_git(cls, git_repo, make_target, bintool_path, flags=None): + def build_from_git(cls, git_repo, make_targets, bintool_path, flags=None): """Build a bintool from a git repo
This clones the repo in a temporary directory, builds it with 'make', @@ -336,7 +336,8 @@ class Bintool:
Args: git_repo (str): URL of git repo - make_target (str): Target to pass to 'make' to build the tool + make_targets (list of str): List of targets to pass to 'make' to build + the tool bintool_path (str): Relative path of the tool in the repo, after build is complete flags (list of str): Flags or variables to pass to make, or None @@ -350,12 +351,14 @@ class Bintool: tmpdir = tempfile.mkdtemp(prefix='binmanf.') print(f"- clone git repo '{git_repo}' to '{tmpdir}'") tools.run('git', 'clone', '--depth', '1', git_repo, tmpdir) - print(f"- build target '{make_target}'") - cmd = ['make', '-C', tmpdir, '-j', f'{multiprocessing.cpu_count()}', - make_target] - if flags: - cmd += flags - tools.run(*cmd) + for target in make_targets: + print(f"- build target '{target}'") + cmd = ['make', '-C', tmpdir, '-j', f'{multiprocessing.cpu_count()}', + target] + if flags: + cmd += flags + tools.run(*cmd) + fname = os.path.join(tmpdir, bintool_path) if not os.path.exists(fname): print(f"- File '{fname}' was not produced") diff --git a/tools/binman/btool/_testing.py b/tools/binman/btool/_testing.py index 4005e8a8a5..c0109c76bf 100644 --- a/tools/binman/btool/_testing.py +++ b/tools/binman/btool/_testing.py @@ -32,5 +32,6 @@ class Bintool_testing(bintool.Bintool): return self.apt_install('package') return self.fetch_from_drive('junk') if method == bintool.FETCH_BUILD: - return self.build_from_git('url', 'target', 'pathname') + cmd = ['target'] + return self.build_from_git('url', cmd, 'pathname') return None diff --git a/tools/binman/btool/fiptool.py b/tools/binman/btool/fiptool.py index c80f8275c4..b275fee43b 100644 --- a/tools/binman/btool/fiptool.py +++ b/tools/binman/btool/fiptool.py @@ -107,8 +107,10 @@ class Bintoolfiptool(bintool.Bintool): """ if method != bintool.FETCH_BUILD: return None + + cmd = ['fiptool'] result = self.build_from_git( 'https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git', - 'fiptool', + cmd, 'tools/fiptool/fiptool') return result diff --git a/tools/binman/btool/futility.py b/tools/binman/btool/futility.py index 04c9aefe9b..4b22547368 100644 --- a/tools/binman/btool/futility.py +++ b/tools/binman/btool/futility.py @@ -168,9 +168,11 @@ class Bintoolfutility(bintool.Bintool): # # Unfortunately this requires logging in and obtaining a line for the # .gitcookies file. So use a mirror instead. + + cmd = ['all'] result = self.build_from_git( 'https://github.com/sjg20/vboot_reference.git', - 'all', + cmd, 'build/futility/futility', flags=['USE_FLASHROM=0']) return result

Hi Sughosh,
On Sat, 15 Jul 2023 at 07:46, Sughosh Ganu sughosh.ganu@linaro.org wrote:
Add support to build a tool from source with a list of commands. This is useful when a tool can be built with multiple commands instead of a single command.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
Changes since V3:
- New patch to support passing multiple commands to the build_from_git function to build the tool.
tools/binman/bintool.py | 19 +++++++++++-------- tools/binman/btool/_testing.py | 3 ++- tools/binman/btool/fiptool.py | 4 +++- tools/binman/btool/futility.py | 4 +++- 4 files changed, 19 insertions(+), 11 deletions(-)
diff --git a/tools/binman/bintool.py b/tools/binman/bintool.py index 81629683df..279bf2fec4 100644 --- a/tools/binman/bintool.py +++ b/tools/binman/bintool.py @@ -328,7 +328,7 @@ class Bintool: return result.stdout
@classmethod
- def build_from_git(cls, git_repo, make_target, bintool_path, flags=None):
def build_from_git(cls, git_repo, make_targets, bintool_path, flags=None): """Build a bintool from a git repo
This clones the repo in a temporary directory, builds it with 'make',
@@ -336,7 +336,8 @@ class Bintool:
Args: git_repo (str): URL of git repo
make_target (str): Target to pass to 'make' to build the tool
make_targets (list of str): List of targets to pass to 'make' to build
the tool bintool_path (str): Relative path of the tool in the repo, after build is complete flags (list of str): Flags or variables to pass to make, or None
@@ -350,12 +351,14 @@ class Bintool: tmpdir = tempfile.mkdtemp(prefix='binmanf.') print(f"- clone git repo '{git_repo}' to '{tmpdir}'") tools.run('git', 'clone', '--depth', '1', git_repo, tmpdir)
print(f"- build target '{make_target}'")
cmd = ['make', '-C', tmpdir, '-j', f'{multiprocessing.cpu_count()}',
make_target]
if flags:
cmd += flags
tools.run(*cmd)
for target in make_targets:
print(f"- build target '{target}'")
cmd = ['make', '-C', tmpdir, '-j', f'{multiprocessing.cpu_count()}',
target]
if flags:
cmd += flags
tools.run(*cmd)
fname = os.path.join(tmpdir, bintool_path) if not os.path.exists(fname): print(f"- File '{fname}' was not produced")
diff --git a/tools/binman/btool/_testing.py b/tools/binman/btool/_testing.py index 4005e8a8a5..c0109c76bf 100644 --- a/tools/binman/btool/_testing.py +++ b/tools/binman/btool/_testing.py @@ -32,5 +32,6 @@ class Bintool_testing(bintool.Bintool): return self.apt_install('package') return self.fetch_from_drive('junk') if method == bintool.FETCH_BUILD:
return self.build_from_git('url', 'target', 'pathname')
cmd = ['target']
return self.build_from_git('url', cmd, 'pathname')
Instead of the 'cmd' variable here, can you just put ['target'] as the function arg? Same below. It doesn't really add anything.
return None
diff --git a/tools/binman/btool/fiptool.py b/tools/binman/btool/fiptool.py index c80f8275c4..b275fee43b 100644 --- a/tools/binman/btool/fiptool.py +++ b/tools/binman/btool/fiptool.py @@ -107,8 +107,10 @@ class Bintoolfiptool(bintool.Bintool): """ if method != bintool.FETCH_BUILD: return None
cmd = ['fiptool'] result = self.build_from_git( 'https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git',
'fiptool',
cmd, 'tools/fiptool/fiptool') return result
diff --git a/tools/binman/btool/futility.py b/tools/binman/btool/futility.py index 04c9aefe9b..4b22547368 100644 --- a/tools/binman/btool/futility.py +++ b/tools/binman/btool/futility.py @@ -168,9 +168,11 @@ class Bintoolfutility(bintool.Bintool): # # Unfortunately this requires logging in and obtaining a line for the # .gitcookies file. So use a mirror instead.
cmd = ['all'] result = self.build_from_git( 'https://github.com/sjg20/vboot_reference.git',
'all',
cmd, 'build/futility/futility', flags=['USE_FLASHROM=0']) return result
-- 2.34.1
Regards, Simon

hi Simon,
On Sun, 16 Jul 2023 at 05:12, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Sat, 15 Jul 2023 at 07:46, Sughosh Ganu sughosh.ganu@linaro.org wrote:
Add support to build a tool from source with a list of commands. This is useful when a tool can be built with multiple commands instead of a single command.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
Changes since V3:
- New patch to support passing multiple commands to the build_from_git function to build the tool.
tools/binman/bintool.py | 19 +++++++++++-------- tools/binman/btool/_testing.py | 3 ++- tools/binman/btool/fiptool.py | 4 +++- tools/binman/btool/futility.py | 4 +++- 4 files changed, 19 insertions(+), 11 deletions(-)
diff --git a/tools/binman/bintool.py b/tools/binman/bintool.py index 81629683df..279bf2fec4 100644 --- a/tools/binman/bintool.py +++ b/tools/binman/bintool.py @@ -328,7 +328,7 @@ class Bintool: return result.stdout
@classmethod
- def build_from_git(cls, git_repo, make_target, bintool_path, flags=None):
def build_from_git(cls, git_repo, make_targets, bintool_path, flags=None): """Build a bintool from a git repo
This clones the repo in a temporary directory, builds it with 'make',
@@ -336,7 +336,8 @@ class Bintool:
Args: git_repo (str): URL of git repo
make_target (str): Target to pass to 'make' to build the tool
make_targets (list of str): List of targets to pass to 'make' to build
the tool bintool_path (str): Relative path of the tool in the repo, after build is complete flags (list of str): Flags or variables to pass to make, or None
@@ -350,12 +351,14 @@ class Bintool: tmpdir = tempfile.mkdtemp(prefix='binmanf.') print(f"- clone git repo '{git_repo}' to '{tmpdir}'") tools.run('git', 'clone', '--depth', '1', git_repo, tmpdir)
print(f"- build target '{make_target}'")
cmd = ['make', '-C', tmpdir, '-j', f'{multiprocessing.cpu_count()}',
make_target]
if flags:
cmd += flags
tools.run(*cmd)
for target in make_targets:
print(f"- build target '{target}'")
cmd = ['make', '-C', tmpdir, '-j', f'{multiprocessing.cpu_count()}',
target]
if flags:
cmd += flags
tools.run(*cmd)
fname = os.path.join(tmpdir, bintool_path) if not os.path.exists(fname): print(f"- File '{fname}' was not produced")
diff --git a/tools/binman/btool/_testing.py b/tools/binman/btool/_testing.py index 4005e8a8a5..c0109c76bf 100644 --- a/tools/binman/btool/_testing.py +++ b/tools/binman/btool/_testing.py @@ -32,5 +32,6 @@ class Bintool_testing(bintool.Bintool): return self.apt_install('package') return self.fetch_from_drive('junk') if method == bintool.FETCH_BUILD:
return self.build_from_git('url', 'target', 'pathname')
cmd = ['target']
return self.build_from_git('url', cmd, 'pathname')
Instead of the 'cmd' variable here, can you just put ['target'] as the function arg? Same below. It doesn't really add anything.
Okay. Will change.
-sughosh
return None
diff --git a/tools/binman/btool/fiptool.py b/tools/binman/btool/fiptool.py index c80f8275c4..b275fee43b 100644 --- a/tools/binman/btool/fiptool.py +++ b/tools/binman/btool/fiptool.py @@ -107,8 +107,10 @@ class Bintoolfiptool(bintool.Bintool): """ if method != bintool.FETCH_BUILD: return None
cmd = ['fiptool'] result = self.build_from_git( 'https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git',
'fiptool',
cmd, 'tools/fiptool/fiptool') return result
diff --git a/tools/binman/btool/futility.py b/tools/binman/btool/futility.py index 04c9aefe9b..4b22547368 100644 --- a/tools/binman/btool/futility.py +++ b/tools/binman/btool/futility.py @@ -168,9 +168,11 @@ class Bintoolfutility(bintool.Bintool): # # Unfortunately this requires logging in and obtaining a line for the # .gitcookies file. So use a mirror instead.
cmd = ['all'] result = self.build_from_git( 'https://github.com/sjg20/vboot_reference.git',
'all',
cmd, 'build/futility/futility', flags=['USE_FLASHROM=0']) return result
-- 2.34.1
Regards, Simon

Add a newline at the end of the dts, without which the build fails when including the u-boot.dtsi file.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Reviewed-by: Simon Glass sjg@chromium.org --- Changes since V3: None
arch/arm/dts/nuvoton-npcm845-evb.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/dts/nuvoton-npcm845-evb.dts b/arch/arm/dts/nuvoton-npcm845-evb.dts index 3cab7807e3..a93666cb41 100644 --- a/arch/arm/dts/nuvoton-npcm845-evb.dts +++ b/arch/arm/dts/nuvoton-npcm845-evb.dts @@ -354,4 +354,4 @@ &r1en_pins &r1oen_pins >; -}; \ No newline at end of file +};

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.
Add a signature node in the u-boot dtsi file and include the public key through the capsule-key property. This file is per architecture, and is currently being added for sandbox and arm architectures. It will have to be added for other architectures which need to enable capsule authentication support.
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 V3: * Put the two ifdef statements together in arm architecture's u-boot.dtsi file. * Remove the extra blank line in the Kconfig.
arch/arm/dts/u-boot.dtsi | 17 +++++++++++++++++ arch/sandbox/dts/u-boot.dtsi | 17 +++++++++++++++++ lib/efi_loader/Kconfig | 10 ++++++++++ lib/efi_loader/Makefile | 7 +++++++ 4 files changed, 51 insertions(+) create mode 100644 arch/arm/dts/u-boot.dtsi create mode 100644 arch/sandbox/dts/u-boot.dtsi
diff --git a/arch/arm/dts/u-boot.dtsi b/arch/arm/dts/u-boot.dtsi new file mode 100644 index 0000000000..2a9359c43c --- /dev/null +++ b/arch/arm/dts/u-boot.dtsi @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Devicetree file with miscellaneous nodes that will be included + * at build time into the DTB. Currently being used for including + * capsule related information. + * + */ + +#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT +#ifdef CONFIG_EFI_CAPSULE_AUTHENTICATE +/ { + signature { + capsule-key = /incbin/(CONFIG_EFI_CAPSULE_ESL_FILE); + }; +}; +#endif /* CONFIG_EFI_CAPSULE_AUTHENTICATE */ +#endif /* CONFIG_EFI_HAVE_CAPSULE_SUPPORT */ diff --git a/arch/sandbox/dts/u-boot.dtsi b/arch/sandbox/dts/u-boot.dtsi new file mode 100644 index 0000000000..60bd004937 --- /dev/null +++ b/arch/sandbox/dts/u-boot.dtsi @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Devicetree file with miscellaneous nodes that will be included + * at build time into the DTB. Currently being used for including + * capsule related information. + * + */ + +#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT +/ { +#ifdef CONFIG_EFI_CAPSULE_AUTHENTICATE + signature { + capsule-key = /incbin/(CONFIG_EFI_CAPSULE_ESL_FILE); + }; +#endif +}; +#endif /* CONFIG_EFI_HAVE_CAPSULE_SUPPORT */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index a22e47616f..9abb9a4db3 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -235,6 +235,16 @@ 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/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 1a8c8d7cab..c52c9d27bd 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -89,3 +89,10 @@ obj-$(CONFIG_EFI_ECPT) += efi_conformance.o
EFI_VAR_SEED_FILE := $(subst $",,$(CONFIG_EFI_VAR_SEED_FILE)) $(obj)/efi_var_seed.o: $(srctree)/$(EFI_VAR_SEED_FILE) + +ifeq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),y) +EFI_CAPSULE_KEY_PATH := $(subst $",,$(CONFIG_EFI_CAPSULE_ESL_FILE)) +ifeq ("$(wildcard $(EFI_CAPSULE_KEY_PATH))","") +$(error .esl cerificate not found. Configure your CONFIG_EFI_CAPSULE_ESL_FILE) +endif +endif

Hi Sughosh,
On Sat, 15 Jul 2023 at 07:46, Sughosh Ganu sughosh.ganu@linaro.org wrote:
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.
Add a signature node in the u-boot dtsi file and include the public key through the capsule-key property. This file is per architecture, and is currently being added for sandbox and arm architectures. It will have to be added for other architectures which need to enable capsule authentication support.
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 V3:
- Put the two ifdef statements together in arm architecture's u-boot.dtsi file.
- Remove the extra blank line in the Kconfig.
arch/arm/dts/u-boot.dtsi | 17 +++++++++++++++++ arch/sandbox/dts/u-boot.dtsi | 17 +++++++++++++++++ lib/efi_loader/Kconfig | 10 ++++++++++ lib/efi_loader/Makefile | 7 +++++++ 4 files changed, 51 insertions(+) create mode 100644 arch/arm/dts/u-boot.dtsi create mode 100644 arch/sandbox/dts/u-boot.dtsi
This approach seems OK to me for now. It is a bit strange to specify a CONFIG option to add something to the DT, but we can always adjust it later if needed.
diff --git a/arch/arm/dts/u-boot.dtsi b/arch/arm/dts/u-boot.dtsi new file mode 100644 index 0000000000..2a9359c43c --- /dev/null +++ b/arch/arm/dts/u-boot.dtsi @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
/**
for multi-line comments
- Devicetree file with miscellaneous nodes that will be included
- at build time into the DTB. Currently being used for including
- capsule related information.
drop blank line
- */
+#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT +#ifdef CONFIG_EFI_CAPSULE_AUTHENTICATE
Can you combine these, or can you omit the first one?
+/ {
signature {
capsule-key = /incbin/(CONFIG_EFI_CAPSULE_ESL_FILE);
};
+}; +#endif /* CONFIG_EFI_CAPSULE_AUTHENTICATE */ +#endif /* CONFIG_EFI_HAVE_CAPSULE_SUPPORT */ diff --git a/arch/sandbox/dts/u-boot.dtsi b/arch/sandbox/dts/u-boot.dtsi new file mode 100644 index 0000000000..60bd004937 --- /dev/null +++ b/arch/sandbox/dts/u-boot.dtsi @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Devicetree file with miscellaneous nodes that will be included
- at build time into the DTB. Currently being used for including
- capsule related information.
- */
+#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT +/ { +#ifdef CONFIG_EFI_CAPSULE_AUTHENTICATE
signature {
capsule-key = /incbin/(CONFIG_EFI_CAPSULE_ESL_FILE);
};
+#endif +}; +#endif /* CONFIG_EFI_HAVE_CAPSULE_SUPPORT */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index a22e47616f..9abb9a4db3 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -235,6 +235,16 @@ 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.
Can you wrap to 72 chars or so?
config EFI_DEVICE_PATH_TO_TEXT bool "Device path to text protocol" default y diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 1a8c8d7cab..c52c9d27bd 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -89,3 +89,10 @@ obj-$(CONFIG_EFI_ECPT) += efi_conformance.o
EFI_VAR_SEED_FILE := $(subst $",,$(CONFIG_EFI_VAR_SEED_FILE)) $(obj)/efi_var_seed.o: $(srctree)/$(EFI_VAR_SEED_FILE)
+ifeq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),y) +EFI_CAPSULE_KEY_PATH := $(subst $",,$(CONFIG_EFI_CAPSULE_ESL_FILE)) +ifeq ("$(wildcard $(EFI_CAPSULE_KEY_PATH))","") +$(error .esl cerificate not found. Configure your CONFIG_EFI_CAPSULE_ESL_FILE) +endif
+endif
2.34.1
REgards, Simon

hi Simon,
On Sun, 16 Jul 2023 at 05:12, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Sat, 15 Jul 2023 at 07:46, Sughosh Ganu sughosh.ganu@linaro.org wrote:
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.
Add a signature node in the u-boot dtsi file and include the public key through the capsule-key property. This file is per architecture, and is currently being added for sandbox and arm architectures. It will have to be added for other architectures which need to enable capsule authentication support.
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 V3:
- Put the two ifdef statements together in arm architecture's u-boot.dtsi file.
- Remove the extra blank line in the Kconfig.
arch/arm/dts/u-boot.dtsi | 17 +++++++++++++++++ arch/sandbox/dts/u-boot.dtsi | 17 +++++++++++++++++ lib/efi_loader/Kconfig | 10 ++++++++++ lib/efi_loader/Makefile | 7 +++++++ 4 files changed, 51 insertions(+) create mode 100644 arch/arm/dts/u-boot.dtsi create mode 100644 arch/sandbox/dts/u-boot.dtsi
This approach seems OK to me for now. It is a bit strange to specify a CONFIG option to add something to the DT, but we can always adjust it later if needed.
diff --git a/arch/arm/dts/u-boot.dtsi b/arch/arm/dts/u-boot.dtsi new file mode 100644 index 0000000000..2a9359c43c --- /dev/null +++ b/arch/arm/dts/u-boot.dtsi @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
/**
for multi-line comments
Okay
- Devicetree file with miscellaneous nodes that will be included
- at build time into the DTB. Currently being used for including
- capsule related information.
drop blank line
Okay
- */
+#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT +#ifdef CONFIG_EFI_CAPSULE_AUTHENTICATE
Can you combine these, or can you omit the first one?
I will drop the first line. Should build for all platforms I believe.
+/ {
signature {
capsule-key = /incbin/(CONFIG_EFI_CAPSULE_ESL_FILE);
};
+}; +#endif /* CONFIG_EFI_CAPSULE_AUTHENTICATE */ +#endif /* CONFIG_EFI_HAVE_CAPSULE_SUPPORT */ diff --git a/arch/sandbox/dts/u-boot.dtsi b/arch/sandbox/dts/u-boot.dtsi new file mode 100644 index 0000000000..60bd004937 --- /dev/null +++ b/arch/sandbox/dts/u-boot.dtsi @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Devicetree file with miscellaneous nodes that will be included
- at build time into the DTB. Currently being used for including
- capsule related information.
- */
+#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT +/ { +#ifdef CONFIG_EFI_CAPSULE_AUTHENTICATE
signature {
capsule-key = /incbin/(CONFIG_EFI_CAPSULE_ESL_FILE);
};
+#endif +}; +#endif /* CONFIG_EFI_HAVE_CAPSULE_SUPPORT */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index a22e47616f..9abb9a4db3 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -235,6 +235,16 @@ 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.
Can you wrap to 72 chars or so?
Okay
-sughosh
config EFI_DEVICE_PATH_TO_TEXT bool "Device path to text protocol" default y diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 1a8c8d7cab..c52c9d27bd 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -89,3 +89,10 @@ obj-$(CONFIG_EFI_ECPT) += efi_conformance.o
EFI_VAR_SEED_FILE := $(subst $",,$(CONFIG_EFI_VAR_SEED_FILE)) $(obj)/efi_var_seed.o: $(srctree)/$(EFI_VAR_SEED_FILE)
+ifeq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),y) +EFI_CAPSULE_KEY_PATH := $(subst $",,$(CONFIG_EFI_CAPSULE_ESL_FILE)) +ifeq ("$(wildcard $(EFI_CAPSULE_KEY_PATH))","") +$(error .esl cerificate not found. Configure your CONFIG_EFI_CAPSULE_ESL_FILE) +endif
+endif
2.34.1
REgards, Simon

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 Reviewed-by: Simon Glass sjg@chromium.org --- Changes since V3: None
doc/develop/uefi/uefi.rst | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-)
diff --git a/doc/develop/uefi/uefi.rst b/doc/develop/uefi/uefi.rst index 6626ceec52..3d07aa33b8 100644 --- a/doc/develop/uefi/uefi.rst +++ b/doc/develop/uefi/uefi.rst @@ -522,20 +522,16 @@ and used by the steps highlighted below. ... }
-You can do step-4 manually with +You can perform step-4 by defining the Kconfig symbol +CONFIG_EFI_CAPSULE_ESL_FILE. Once this has been done, the signature +node can be added to the u-boot.dtsi file. For reference, check the +u-boot.dtsi file for the sandbox architecture. If this node has not +been added to the architecture's u-boot.dtsi file, this needs to be +done. The node has currently been added for the sandbox and arm +architectures' in the u-boot.dtsi file. Once the u-boot.dtsi file has +been added with the signature node, the esl file will automatically +get embedded into the platform's dtb as part of u-boot build.
-.. 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"); - }; - };
Anti-rollback Protection ************************

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 V3: * Add support for firmware versioning, needed after rebasing on current master.
tools/Kconfig | 16 ++ tools/Makefile | 1 + tools/eficapsule.h | 115 ++++++++++++ tools/mkeficapsule.c | 87 +++++---- tools/mkeficapsule_parse.c | 352 +++++++++++++++++++++++++++++++++++++ 5 files changed, 540 insertions(+), 31 deletions(-) create mode 100644 tools/mkeficapsule_parse.c
diff --git a/tools/Kconfig b/tools/Kconfig index 6e23f44d55..88ea3567d0 100644 --- a/tools/Kconfig +++ b/tools/Kconfig @@ -98,6 +98,22 @@ 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. + +config EFI_USE_CAPSULE_CFG_FILE + bool "Use the config file for generating capsules" + help + Boolean option used to specify if the EFI capsules are to + be generated through parameters specified via the config + file or through command line. + menuconfig FSPI_CONF_HEADER bool "FlexSPI Header Configuration" help diff --git a/tools/Makefile b/tools/Makefile index 3d0c4b0dd6..eb129e3bb2 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
mkfwumdata-objs := mkfwumdata.o generated/lib/crc32.o HOSTLDLIBS_mkfwumdata += -luuid diff --git a/tools/eficapsule.h b/tools/eficapsule.h index 2099a2e9b8..d455ac1d6f 100644 --- a/tools/eficapsule.h +++ b/tools/eficapsule.h @@ -52,6 +52,12 @@ typedef struct { /* flags */ #define CAPSULE_FLAGS_PERSIST_ACROSS_RESET 0x00010000
+enum capsule_type { + CAPSULE_NORMAL_BLOB = 0, + CAPSULE_ACCEPT, + CAPSULE_REVERT, +}; + struct efi_capsule_header { efi_guid_t capsule_guid; uint32_t header_size; @@ -113,6 +119,7 @@ struct efi_firmware_image_authentication { struct win_certificate_uefi_guid auth_info; } __packed;
+ /* fmp payload header */ #define SIGNATURE_16(A, B) ((A) | ((B) << 8)) #define SIGNATURE_32(A, B, C, D) \ @@ -143,4 +150,112 @@ struct fmp_payload_header_params { uint32_t fw_version; };
+/** + * 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 + * @fmp: FMP payload header used for storing firmware version + * @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; + struct fmp_payload_header_params fmp; + uint64_t monotonic_count; + char *privkey_file; + char *cert_file; + char *input_file; + char *capsule_file; + unsigned long oemflags; + enum capsule_type capsule; +}; + +/** + * 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 + * @fmp: FMP header params + * @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, + struct fmp_payload_header_params *fmp_ph_params, + 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 52be1f122e..4058813c98 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'}, @@ -49,11 +43,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" @@ -69,6 +73,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); } @@ -388,6 +393,7 @@ static void free_sig_data(struct auth_context *ctx) * @guid: GUID of related FMP driver * @index: Index number in capsule * @instance: Instance number in capsule + * @fmp: FMP header params * @mcount: Monotonic count in authentication information * @private_file: Path to a private key file * @cert_file: Path to a certificate file @@ -402,11 +408,11 @@ 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, - struct fmp_payload_header_params *fmp_ph_params, - 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, + struct fmp_payload_header_params *fmp_ph_params, + uint64_t mcount, char *privkey_file, char *cert_file, + uint16_t oemflags) { struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule; @@ -604,7 +610,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; @@ -667,6 +687,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; struct fmp_payload_header_params fmp_ph_params = { 0 };
@@ -676,8 +698,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); @@ -731,20 +754,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); @@ -754,6 +777,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); @@ -761,21 +788,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); } @@ -785,6 +812,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..0b010706d5 --- /dev/null +++ b/tools/mkeficapsule_parse.c @@ -0,0 +1,352 @@ +// 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 + fw-version: 2 + 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 + fw-version: 10 + 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", "fw-version" }; + +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; + case 10: + param->fmp.fw_version = strtoul(val, NULL, 0); + param->fmp.have_header = true; + 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, + ¶ms->fmp, + 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)); + } +}

Add support in binman for generating capsules. The capsule parameters can be specified either through a config file or through the capsule binman entry. Also add test cases in binman for capsule generation, and enable this testing on the sandbox_spl variant.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- Changes since V3: * Add test cases for covering the various capsule generation scenarios. * Add function comments in the mkeficapsule bintool. * Fix the fetch method of the mkeficapsule bintool to enable building the tool. * Add more details about the capsule parameters in the documentation as well as the code. * Fix order of module imports, and addition of blank lines in the capsule.py file. * Use SetContents in the ObtainContents method.
configs/sandbox_spl_defconfig | 1 + tools/binman/btool/mkeficapsule.py | 158 ++++++++++++++++++ tools/binman/entries.rst | 37 ++++ tools/binman/etype/capsule.py | 132 +++++++++++++++ tools/binman/ftest.py | 127 ++++++++++++++ tools/binman/test/282_capsule.dts | 18 ++ tools/binman/test/283_capsule_signed.dts | 20 +++ tools/binman/test/284_capsule_conf.dts | 14 ++ tools/binman/test/285_capsule_missing_key.dts | 19 +++ .../binman/test/286_capsule_missing_index.dts | 17 ++ .../binman/test/287_capsule_missing_guid.dts | 17 ++ .../test/288_capsule_missing_payload.dts | 17 ++ tools/binman/test/289_capsule_missing.dts | 17 ++ tools/binman/test/290_capsule_version.dts | 19 +++ tools/binman/test/capsule_cfg.txt | 6 + 15 files changed, 619 insertions(+) create mode 100644 tools/binman/btool/mkeficapsule.py create mode 100644 tools/binman/etype/capsule.py create mode 100644 tools/binman/test/282_capsule.dts create mode 100644 tools/binman/test/283_capsule_signed.dts create mode 100644 tools/binman/test/284_capsule_conf.dts create mode 100644 tools/binman/test/285_capsule_missing_key.dts create mode 100644 tools/binman/test/286_capsule_missing_index.dts create mode 100644 tools/binman/test/287_capsule_missing_guid.dts create mode 100644 tools/binman/test/288_capsule_missing_payload.dts create mode 100644 tools/binman/test/289_capsule_missing.dts create mode 100644 tools/binman/test/290_capsule_version.dts create mode 100644 tools/binman/test/capsule_cfg.txt
diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig index dd848c57c6..2fcc789347 100644 --- a/configs/sandbox_spl_defconfig +++ b/configs/sandbox_spl_defconfig @@ -248,3 +248,4 @@ CONFIG_UNIT_TEST=y CONFIG_SPL_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_TOOLS_MKEFICAPSULE=y diff --git a/tools/binman/btool/mkeficapsule.py b/tools/binman/btool/mkeficapsule.py new file mode 100644 index 0000000000..ba6b666714 --- /dev/null +++ b/tools/binman/btool/mkeficapsule.py @@ -0,0 +1,158 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2023 Linaro Limited +# +"""Bintool implementation for mkeficapsule tool + +mkeficapsule is a tool used for generating EFI capsules. + +The following are the command-line options to be provided +to the tool +Usage: mkeficapsule [options] <image blob> <output file> +Options: + -g, --guid <guid string> guid for image blob type + -i, --index <index> update image index + -I, --instance <instance> update hardware instance + -v, --fw-version <version> firmware version + -p, --private-key <privkey file> private key file + -c, --certificate <cert file> signer's certificate file + -m, --monotonic-count <count> monotonic count + -d, --dump_sig dump signature (*.p7) + -A, --fw-accept firmware accept capsule, requires GUID, no image blob + -R, --fw-revert firmware revert capsule, takes no GUID, no image blob + -o, --capoemflag Capsule OEM Flag, an integer between 0x0000 and 0xffff + -f, --cfg-file <config file> config file with capsule parameters + -h, --help print a help message + +""" + +from binman import bintool + +class Bintoolmkeficapsule(bintool.Bintool): + """Handles the 'mkeficapsule' tool + + This bintool is used for generating the EFI capsules. The + capsule generation parameters can either be specified through + command-line, or through a config file. + + """ + def __init__(self, name): + super().__init__(name, 'mkeficapsule tool for generating capsules') + + def capsule_cfg_file(self, cfg_file): + """Generate a capsule reading parameters from config file + + Args: + cfg_file (str): Path to the config file + + Returns: + str: Tool output + """ + + args = [ + f'--cfg-file={cfg_file}' + ] + return self.run_cmd(*args) + + def cmdline_capsule(self, image_index, image_guid, hardware_instance, + payload, output_fname, version=0): + """Generate a capsule through commandline provided parameters + + Args: + image_index (int): Unique number for identifying payload image + image_guid (str): GUID used for identifying the image + hardware_instance (int): Optional unique hardware instance of + a device in the system. 0 if not being used + payload (str): Path to the input payload image + output_fname (str): Path to the output capsule file + version (int): Image version (Optional) + + Returns: + str: Tool output + """ + + if version: + args = [ + f'--index={image_index}', + f'--fw-version={version}', + f'--guid={image_guid}', + f'--instance={hardware_instance}', + payload, + output_fname + ] + else: + args = [ + f'--index={image_index}', + f'--guid={image_guid}', + f'--instance={hardware_instance}', + payload, + output_fname + ] + + return self.run_cmd(*args) + + def cmdline_auth_capsule(self, image_index, image_guid, hardware_instance, + monotonic_count, priv_key, pub_key, + payload, output_fname, version=0): + """Generate a signed capsule through commandline provided parameters + + Args: + image_index (int): Unique number for identifying payload image + image_guid (str): GUID used for identifying the image + hardware_instance (int): Optional unique hardware instance of + a device in the system. 0 if not being used + monotonic_count (int): Count used when signing an image + priv_key (str): Path to the private key + pub_key(str): Path to the public key + payload (str): Path to the input payload image + output_fname (str): Path to the output capsule file + version (int): Image version (Optional) + + Returns: + str: Tool output + """ + + if version: + args = [ + f'--index={image_index}', + f'--guid={image_guid}', + f'--instance={hardware_instance}', + f'--monotonic-count={monotonic_count}', + f'--private-key={priv_key}', + f'--certificate={pub_key}', + f'--fw-version={version}', + payload, + output_fname + ] + else: + args = [ + f'--index={image_index}', + f'--guid={image_guid}', + f'--instance={hardware_instance}', + f'--monotonic-count={monotonic_count}', + f'--private-key={priv_key}', + f'--certificate={pub_key}', + payload, + output_fname + ] + + return self.run_cmd(*args) + + def fetch(self, method): + """Fetch handler for mkeficapsule + + 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 + + cmd = ['tools-only_defconfig', 'tools'] + result = self.build_from_git( + 'https://source.denx.de/u-boot/u-boot.git', + cmd, + 'tools/mkeficapsule') + return result diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index b71af801fd..523c8222f5 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -283,6 +283,43 @@ entry; similarly for SPL.
+.. _etype_capsule: + +Entry: capsule: Entry for generating EFI Capsule files +------------------------------------------------------ + +This is an entry for generating EFI capsules. + +The parameters needed for generation of the capsules can either be +provided separately, or through a config file. + +Properties / Entry arguments: + - cfg-file: Config file for providing capsule + parameters. These are parameters needed for generating the + capsules. The parameters can be listed by running the + './tools/mkeficapsule -h' command. + - image-index: Unique number for identifying corresponding + payload image. Number between 1 and descriptor count, i.e. + the total number of firmware images that can be updated. + - image-type-id: Image GUID which will be used for identifying the + updatable image on the board. + - hardware-instance: Optional number for identifying unique + hardware instance of a device in the system. Default value of 0 + for images where value is not to be used. + - fw-version: Optional value of image version that can be put on + the capsule through the Firmware Management Protocol(FMP) header. + - monotomic-count: Count used when signing an image. + - private-key: Path to PEM formatted .key private key file. + - pub-key-cert: Path to PEM formatted .crt public key certificate + file. + - filename: Path to the input(payload) file. File can be any + format, a binary or an elf, platform specific. + - capsule: Path to the output capsule file. A capsule is a + continous set of data as defined by the EFI specification. Refer + to the specification for more details. + + + .. _etype_cbfs:
Entry: cbfs: Coreboot Filesystem (CBFS) diff --git a/tools/binman/etype/capsule.py b/tools/binman/etype/capsule.py new file mode 100644 index 0000000000..46e5507704 --- /dev/null +++ b/tools/binman/etype/capsule.py @@ -0,0 +1,132 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2023 Linaro Limited +# +# Entry-type module for producing a capsule +# + +import os + +from binman.entry import Entry +from dtoc import fdt_util +from u_boot_pylib import tools + +class Entry_capsule(Entry): + """Entry for generating EFI capsules + + This is an entry for generating EFI capsules. + + The parameters needed for generation of the capsules can + either be provided separately, or through a config file. + + Properties / Entry arguments: + - cfg-file: Config file for providing capsule + parameters. These are parameters needed for generating the + capsules. The parameters can be listed by running the + './tools/mkeficapsule -h' command. + - image-index: Unique number for identifying corresponding + payload image. Number between 1 and descriptor count, i.e. + the total number of firmware images that can be updated. + - image-type-id: Image GUID which will be used for identifying the + updatable image on the board. + - hardware-instance: Optional number for identifying unique + hardware instance of a device in the system. Default value of 0 + for images where value is not to be used. + - fw-version: Optional value of image version that can be put on + the capsule through the Firmware Management Protocol(FMP) header. + - monotomic-count: Count used when signing an image. + - private-key: Path to PEM formatted .key private key file. + - pub-key-cert: Path to PEM formatted .crt public key certificate + file. + - filename: Path to the input(payload) file. File can be any + format, a binary or an elf, platform specific. + - capsule: Path to the output capsule file. A capsule is a + continous set of data as defined by the EFI specification. Refer + to the specification for more details. + + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + self.image_index = 0 + self.image_guid = "" + self.hardware_instance = 0 + self.monotonic_count = 0 + self.fw_version = 0 + self.private_key = "" + self.pub_key_cert = "" + self.auth = 0 + self.payload = "" + self.capsule_fname = "" + + def ReadNode(self): + super().ReadNode() + + self.cfg_file = fdt_util.GetString(self._node, 'cfg-file') + if not self.cfg_file: + self.image_index = fdt_util.GetInt(self._node, 'image-index') + if not self.image_index: + self.Raise('mkeficapsule must be provided an Image Index') + + self.image_guid = fdt_util.GetString(self._node, 'image-type-id') + if not self.image_guid: + self.Raise('mkeficapsule must be provided an Image GUID') + + self.fw_version = fdt_util.GetInt(self._node, 'fw-version') + self.hardware_instance = fdt_util.GetInt(self._node, 'hardware-instance') + self.monotonic_count = fdt_util.GetInt(self._node, 'monotonic-count') + + self.private_key = fdt_util.GetString(self._node, 'private-key') + self.pub_key_cert = fdt_util.GetString(self._node, 'pub-key-cert') + + if ((self.private_key and not self.pub_key_cert) or (self.pub_key_cert and not self.private_key)): + self.Raise('Both private-key and public key Certificate need to be provided') + elif not (self.private_key and self.pub_key_cert): + self.auth = 0 + else: + self.auth = 1 + + self.payload = fdt_util.GetString(self._node, 'filename') + if not self.payload: + self.Raise('mkeficapsule must be provided an input filename(payload)') + + if not os.path.isabs(self.payload): + self.payload_path = tools.get_input_filename(self.payload) + if not os.path.exists(self.payload_path): + self.Raise('Cannot resolve path to the input filename(payload)') + else: + self.payload = self.payload_path + + self.capsule_fname = fdt_util.GetString(self._node, 'capsule') + if not self.capsule_fname: + self.Raise('Specify the output capsule file') + + if not os.path.isabs(self.capsule_fname): + self.capsule_path = tools.get_output_filename(self.capsule_fname) + self.capsule_fname = self.capsule_path + + def _GenCapsule(self): + if self.cfg_file: + return self.mkeficapsule.capsule_cfg_file(self.cfg_file) + elif self.auth: + return self.mkeficapsule.cmdline_auth_capsule(self.image_index, + self.image_guid, + self.hardware_instance, + self.monotonic_count, + self.private_key, + self.pub_key_cert, + self.payload, + self.capsule_fname, + self.fw_version) + else: + return self.mkeficapsule.cmdline_capsule(self.image_index, + self.image_guid, + self.hardware_instance, + self.payload, + self.capsule_fname, + self.fw_version) + + def ObtainContents(self): + self.SetContents(tools.to_bytes(self._GenCapsule())) + return True + + def AddBintools(self, btools): + self.mkeficapsule = self.AddBintool(btools, 'mkeficapsule') diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 43b4f850a6..f5dd62d028 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -6676,6 +6676,133 @@ fdt fdtmap Extract the devicetree blob from the fdtmap ['fit']) self.assertIn("Node '/fit': Missing tool: 'mkimage'", str(e.exception))
+ def _CheckCapsule(self, signed_capsule=False, version_check=False): + # Firmware Management Protocol GUID used in capsule header + self.capsule_guid = "edd5cb6d2de8444cbda17194199ad92a" + # Image GUID specified in the DTS + self.image_guid = "52cfd7092007104791d108469b7fe9c8" + self.fmp_signature = "4d535331" + self.fmp_size = "10" + self.fmp_fw_version = "02" + + self.capsule_data = tools.read_file(self.capsule_fname) + + self.assertEqual(self.capsule_guid, self.capsule_data.hex()[:32]) + self.assertEqual(self.image_guid, self.capsule_data.hex()[96:128]) + + if version_check: + self.assertEqual(self.fmp_signature, self.capsule_data.hex()[184:192]) + self.assertEqual(self.fmp_size, self.capsule_data.hex()[192:194]) + self.assertEqual(self.fmp_fw_version, self.capsule_data.hex()[200:202]) + + if signed_capsule: + self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[4770:4778]) + elif version_check: + self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[216:224]) + else: + self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[184:192]) + + def _GenCapsuleCfgPayload(self, fname, contents): + capsule_dir = '/tmp/capsules/' + pathname = os.path.join(capsule_dir, fname) + dirname = os.path.dirname(pathname) + if dirname and not os.path.exists(dirname): + os.makedirs(dirname) + + with open(pathname, 'wb') as fd: + fd.write(contents) + + def testCapsuleGen(self): + """Test generation of EFI capsule""" + self.payload_data = tools.to_bytes(TEXT_DATA) + + TestFunctional._MakeInputFile('payload.txt', self.payload_data) + + self._DoReadFile('282_capsule.dts') + + self.capsule_fname = tools.get_output_filename('test.capsule') + self.assertTrue(os.path.exists(self.capsule_fname)) + + self._CheckCapsule() + + def testSignedCapsuleGen(self): + """Test generation of EFI capsule""" + self.payload_data = tools.to_bytes(TEXT_DATA) + + TestFunctional._MakeInputFile('payload.txt', self.payload_data) + + self._DoReadFile('283_capsule_signed.dts') + + self.capsule_fname = tools.get_output_filename('test.capsule') + self.assertTrue(os.path.exists(self.capsule_fname)) + + self._CheckCapsule(signed_capsule=True) + + def testCapsuleGenCfgFile(self): + """Test generation of EFI capsule through config file""" + self.payload_data = tools.to_bytes(TEXT_DATA) + + self._GenCapsuleCfgPayload('payload.txt', self.payload_data) + + self._DoReadFile('284_capsule_conf.dts') + + self.capsule_fname = '/tmp/capsules/test.capsule' + self.assertTrue(os.path.exists(self.capsule_fname)) + + self._CheckCapsule() + + def testCapsuleGenVersionSupport(self): + """Test generation of EFI capsule with version support""" + self.payload_data = tools.to_bytes(TEXT_DATA)
+ TestFunctional._MakeInputFile('payload.txt', self.payload_data) + + self._DoReadFile('290_capsule_version.dts') + + self.capsule_fname = tools.get_output_filename('test.capsule') + self.assertTrue(os.path.exists(self.capsule_fname)) + + self._CheckCapsule(version_check=True) + + def testCapsuleGenKeyMissing(self): + """Test that binman errors out on missing key""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('285_capsule_missing_key.dts') + + self.assertIn("Both private-key and public key Certificate need to be provided", + str(e.exception)) + + def testCapsuleGenIndexMissing(self): + """Test that binman errors out on missing image index""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('286_capsule_missing_index.dts') + + self.assertIn("mkeficapsule must be provided an Image Index", + str(e.exception)) + + def testCapsuleGenGuidMissing(self): + """Test that binman errors out on missing image GUID""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('287_capsule_missing_guid.dts') + + self.assertIn("mkeficapsule must be provided an Image GUID", + str(e.exception)) + + def testCapsuleGenPayloadMissing(self): + """Test that binman errors out on missing input(payload)image""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('288_capsule_missing_payload.dts') + + self.assertIn("mkeficapsule must be provided an input filename(payload)", + str(e.exception)) + + def testCapsuleGenCapsuleFileMissing(self): + """Test that binman errors out on missing output capsule file""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('289_capsule_missing.dts') + + self.assertIn("Specify the output capsule file", + str(e.exception)) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/282_capsule.dts b/tools/binman/test/282_capsule.dts new file mode 100644 index 0000000000..0e7fcdf8ab --- /dev/null +++ b/tools/binman/test/282_capsule.dts @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + capsule { + image-index = <0x1>; + image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8"; + hardware-instance = <0x0>; + filename = "payload.txt"; + capsule = "test.capsule"; + }; + }; +}; diff --git a/tools/binman/test/283_capsule_signed.dts b/tools/binman/test/283_capsule_signed.dts new file mode 100644 index 0000000000..70a6690eef --- /dev/null +++ b/tools/binman/test/283_capsule_signed.dts @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + capsule { + image-index = <0x1>; + image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8"; + hardware-instance = <0x0>; + private-key = "tools/binman/test/key.key"; + pub-key-cert = "tools/binman/test/key.pem"; + filename = "payload.txt"; + capsule = "test.capsule"; + }; + }; +}; diff --git a/tools/binman/test/284_capsule_conf.dts b/tools/binman/test/284_capsule_conf.dts new file mode 100644 index 0000000000..5d458a1a5a --- /dev/null +++ b/tools/binman/test/284_capsule_conf.dts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + capsule { + cfg-file = "tools/binman/test/capsule_cfg.txt"; + }; + }; +}; diff --git a/tools/binman/test/285_capsule_missing_key.dts b/tools/binman/test/285_capsule_missing_key.dts new file mode 100644 index 0000000000..7a09bbbf7b --- /dev/null +++ b/tools/binman/test/285_capsule_missing_key.dts @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + capsule { + image-index = <0x1>; + image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8"; + hardware-instance = <0x0>; + private-key = "tools/binman/test/key.key"; + filename = "payload.txt"; + capsule = "test.capsule"; + }; + }; +}; diff --git a/tools/binman/test/286_capsule_missing_index.dts b/tools/binman/test/286_capsule_missing_index.dts new file mode 100644 index 0000000000..dad521c0b5 --- /dev/null +++ b/tools/binman/test/286_capsule_missing_index.dts @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + capsule { + image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8"; + hardware-instance = <0x0>; + filename = "payload.txt"; + capsule = "test.capsule"; + }; + }; +}; diff --git a/tools/binman/test/287_capsule_missing_guid.dts b/tools/binman/test/287_capsule_missing_guid.dts new file mode 100644 index 0000000000..b81aa3ecd2 --- /dev/null +++ b/tools/binman/test/287_capsule_missing_guid.dts @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + capsule { + image-index = <0x1>; + hardware-instance = <0x0>; + filename = "payload.txt"; + capsule = "test.capsule"; + }; + }; +}; diff --git a/tools/binman/test/288_capsule_missing_payload.dts b/tools/binman/test/288_capsule_missing_payload.dts new file mode 100644 index 0000000000..e702ac1a1d --- /dev/null +++ b/tools/binman/test/288_capsule_missing_payload.dts @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + capsule { + image-index = <0x1>; + image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8"; + hardware-instance = <0x0>; + capsule = "test.capsule"; + }; + }; +}; diff --git a/tools/binman/test/289_capsule_missing.dts b/tools/binman/test/289_capsule_missing.dts new file mode 100644 index 0000000000..63b3d83370 --- /dev/null +++ b/tools/binman/test/289_capsule_missing.dts @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + capsule { + image-index = <0x1>; + image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8"; + hardware-instance = <0x0>; + filename = "payload.txt"; + }; + }; +}; diff --git a/tools/binman/test/290_capsule_version.dts b/tools/binman/test/290_capsule_version.dts new file mode 100644 index 0000000000..b829d730f1 --- /dev/null +++ b/tools/binman/test/290_capsule_version.dts @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + capsule { + image-index = <0x1>; + fw-version = <0x2>; + image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8"; + hardware-instance = <0x0>; + filename = "payload.txt"; + capsule = "test.capsule"; + }; + }; +}; diff --git a/tools/binman/test/capsule_cfg.txt b/tools/binman/test/capsule_cfg.txt new file mode 100644 index 0000000000..bf44a431b9 --- /dev/null +++ b/tools/binman/test/capsule_cfg.txt @@ -0,0 +1,6 @@ +{ + image-index: 1 + image-guid: 09D7CF52-0720-4710-91D1-08469B7FE9C8 + payload: /tmp/capsules/payload.txt + capsule: /tmp/capsules/test.capsule +}

Hi Sughosh,
On Sat, 15 Jul 2023 at 07:46, Sughosh Ganu sughosh.ganu@linaro.org wrote:
Add support in binman for generating capsules. The capsule parameters can be specified either through a config file or through the capsule binman entry. Also add test cases in binman for capsule generation, and enable this testing on the sandbox_spl variant.
Can you use sandbox instead, or perhaps sandbox_spl? SPL is really for SPL testing.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
Changes since V3:
- Add test cases for covering the various capsule generation scenarios.
- Add function comments in the mkeficapsule bintool.
- Fix the fetch method of the mkeficapsule bintool to enable building the tool.
- Add more details about the capsule parameters in the documentation as well as the code.
- Fix order of module imports, and addition of blank lines in the capsule.py file.
- Use SetContents in the ObtainContents method.
configs/sandbox_spl_defconfig | 1 + tools/binman/btool/mkeficapsule.py | 158 ++++++++++++++++++ tools/binman/entries.rst | 37 ++++ tools/binman/etype/capsule.py | 132 +++++++++++++++ tools/binman/ftest.py | 127 ++++++++++++++ tools/binman/test/282_capsule.dts | 18 ++ tools/binman/test/283_capsule_signed.dts | 20 +++ tools/binman/test/284_capsule_conf.dts | 14 ++ tools/binman/test/285_capsule_missing_key.dts | 19 +++ .../binman/test/286_capsule_missing_index.dts | 17 ++ .../binman/test/287_capsule_missing_guid.dts | 17 ++ .../test/288_capsule_missing_payload.dts | 17 ++ tools/binman/test/289_capsule_missing.dts | 17 ++ tools/binman/test/290_capsule_version.dts | 19 +++ tools/binman/test/capsule_cfg.txt | 6 + 15 files changed, 619 insertions(+) create mode 100644 tools/binman/btool/mkeficapsule.py create mode 100644 tools/binman/etype/capsule.py create mode 100644 tools/binman/test/282_capsule.dts create mode 100644 tools/binman/test/283_capsule_signed.dts create mode 100644 tools/binman/test/284_capsule_conf.dts create mode 100644 tools/binman/test/285_capsule_missing_key.dts create mode 100644 tools/binman/test/286_capsule_missing_index.dts create mode 100644 tools/binman/test/287_capsule_missing_guid.dts create mode 100644 tools/binman/test/288_capsule_missing_payload.dts create mode 100644 tools/binman/test/289_capsule_missing.dts create mode 100644 tools/binman/test/290_capsule_version.dts create mode 100644 tools/binman/test/capsule_cfg.txt
This looks pretty good to me. Some nits below
diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig index dd848c57c6..2fcc789347 100644 --- a/configs/sandbox_spl_defconfig +++ b/configs/sandbox_spl_defconfig @@ -248,3 +248,4 @@ CONFIG_UNIT_TEST=y CONFIG_SPL_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_TOOLS_MKEFICAPSULE=y
Why enabling this here? I don't think it is needed in sandbox_spl, but in any case it should be in a different patch if needed.
diff --git a/tools/binman/btool/mkeficapsule.py b/tools/binman/btool/mkeficapsule.py new file mode 100644 index 0000000000..ba6b666714 --- /dev/null +++ b/tools/binman/btool/mkeficapsule.py @@ -0,0 +1,158 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2023 Linaro Limited +# +"""Bintool implementation for mkeficapsule tool
+mkeficapsule is a tool used for generating EFI capsules.
+The following are the command-line options to be provided +to the tool +Usage: mkeficapsule [options] <image blob> <output file> +Options:
-g, --guid <guid string> guid for image blob type
-i, --index <index> update image index
-I, --instance <instance> update hardware instance
-v, --fw-version <version> firmware version
-p, --private-key <privkey file> private key file
-c, --certificate <cert file> signer's certificate file
-m, --monotonic-count <count> monotonic count
-d, --dump_sig dump signature (*.p7)
-A, --fw-accept firmware accept capsule, requires GUID, no image blob
-R, --fw-revert firmware revert capsule, takes no GUID, no image blob
-o, --capoemflag Capsule OEM Flag, an integer between 0x0000 and 0xffff
-f, --cfg-file <config file> config file with capsule parameters
-h, --help print a help message
+"""
+from binman import bintool
+class Bintoolmkeficapsule(bintool.Bintool):
- """Handles the 'mkeficapsule' tool
- This bintool is used for generating the EFI capsules. The
- capsule generation parameters can either be specified through
- command-line, or through a config file.
- """
- def __init__(self, name):
super().__init__(name, 'mkeficapsule tool for generating capsules')
- def capsule_cfg_file(self, cfg_file):
"""Generate a capsule reading parameters from config file
Args:
cfg_file (str): Path to the config file
Returns:
str: Tool output
"""
nit: drop blank lines after """ function comment (please fix throughout)
args = [
f'--cfg-file={cfg_file}'
]
return self.run_cmd(*args)
- def cmdline_capsule(self, image_index, image_guid, hardware_instance,
payload, output_fname, version=0):
"""Generate a capsule through commandline provided parameters
Args:
image_index (int): Unique number for identifying payload image
image_guid (str): GUID used for identifying the image
hardware_instance (int): Optional unique hardware instance of
a device in the system. 0 if not being used
payload (str): Path to the input payload image
output_fname (str): Path to the output capsule file
version (int): Image version (Optional)
Returns:
str: Tool output
"""
if version:
args = [
f'--index={image_index}',
f'--fw-version={version}',
f'--guid={image_guid}',
f'--instance={hardware_instance}',
payload,
output_fname
]
else:
args = [
f'--index={image_index}',
f'--guid={image_guid}',
f'--instance={hardware_instance}',
payload,
output_fname
]
return self.run_cmd(*args)
- def cmdline_auth_capsule(self, image_index, image_guid, hardware_instance,
monotonic_count, priv_key, pub_key,
payload, output_fname, version=0):
"""Generate a signed capsule through commandline provided parameters
Args:
image_index (int): Unique number for identifying payload image
image_guid (str): GUID used for identifying the image
hardware_instance (int): Optional unique hardware instance of
a device in the system. 0 if not being used
monotonic_count (int): Count used when signing an image
priv_key (str): Path to the private key
pub_key(str): Path to the public key
payload (str): Path to the input payload image
output_fname (str): Path to the output capsule file
version (int): Image version (Optional)
Returns:
str: Tool output
"""
if version:
args = [
f'--index={image_index}',
f'--guid={image_guid}',
f'--instance={hardware_instance}',
f'--monotonic-count={monotonic_count}',
f'--private-key={priv_key}',
f'--certificate={pub_key}',
f'--fw-version={version}',
payload,
output_fname
]
else:
args = [
f'--index={image_index}',
f'--guid={image_guid}',
f'--instance={hardware_instance}',
f'--monotonic-count={monotonic_count}',
f'--private-key={priv_key}',
f'--certificate={pub_key}',
payload,
output_fname
]
return self.run_cmd(*args)
- def fetch(self, method):
"""Fetch handler for mkeficapsule
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
cmd = ['tools-only_defconfig', 'tools']
result = self.build_from_git(
'https://source.denx.de/u-boot/u-boot.git',
cmd,
'tools/mkeficapsule')
return result
diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index b71af801fd..523c8222f5 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -283,6 +283,43 @@ entry; similarly for SPL.
+.. _etype_capsule:
+Entry: capsule: Entry for generating EFI Capsule files +------------------------------------------------------
+This is an entry for generating EFI capsules.
+The parameters needed for generation of the capsules can either be +provided separately, or through a config file.
+Properties / Entry arguments:
- cfg-file: Config file for providing capsule
parameters. These are parameters needed for generating the
capsules. The parameters can be listed by running the
'./tools/mkeficapsule -h' command.
- image-index: Unique number for identifying corresponding
payload image. Number between 1 and descriptor count, i.e.
the total number of firmware images that can be updated.
- image-type-id: Image GUID which will be used for identifying the
updatable image on the board.
- hardware-instance: Optional number for identifying unique
hardware instance of a device in the system. Default value of 0
for images where value is not to be used.
- fw-version: Optional value of image version that can be put on
the capsule through the Firmware Management Protocol(FMP) header.
- monotomic-count: Count used when signing an image.
- private-key: Path to PEM formatted .key private key file.
- pub-key-cert: Path to PEM formatted .crt public key certificate
file.
- filename: Path to the input(payload) file. File can be any
format, a binary or an elf, platform specific.
- capsule: Path to the output capsule file. A capsule is a
continous set of data as defined by the EFI specification. Refer
to the specification for more details.
.. _etype_cbfs:
Entry: cbfs: Coreboot Filesystem (CBFS) diff --git a/tools/binman/etype/capsule.py b/tools/binman/etype/capsule.py new file mode 100644 index 0000000000..46e5507704 --- /dev/null +++ b/tools/binman/etype/capsule.py @@ -0,0 +1,132 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2023 Linaro Limited +# +# Entry-type module for producing a capsule +#
+import os
+from binman.entry import Entry +from dtoc import fdt_util +from u_boot_pylib import tools
+class Entry_capsule(Entry):
- """Entry for generating EFI capsules
- This is an entry for generating EFI capsules.
- The parameters needed for generation of the capsules can
- either be provided separately, or through a config file.
- Properties / Entry arguments:
- cfg-file: Config file for providing capsule
parameters. These are parameters needed for generating the
capsules. The parameters can be listed by running the
'./tools/mkeficapsule -h' command.
- image-index: Unique number for identifying corresponding
payload image. Number between 1 and descriptor count, i.e.
the total number of firmware images that can be updated.
- image-type-id: Image GUID which will be used for identifying the
updatable image on the board.
- hardware-instance: Optional number for identifying unique
hardware instance of a device in the system. Default value of 0
for images where value is not to be used.
- fw-version: Optional value of image version that can be put on
the capsule through the Firmware Management Protocol(FMP) header.
- monotomic-count: Count used when signing an image.
monotonic
- private-key: Path to PEM formatted .key private key file.
- pub-key-cert: Path to PEM formatted .crt public key certificate
file.
- filename: Path to the input(payload) file. File can be any
format, a binary or an elf, platform specific.
- capsule: Path to the output capsule file. A capsule is a
continous set of data as defined by the EFI specification. Refer
continuous
Can you add a link to EFI spec so it appears in the docs here?
to the specification for more details.
- """
- def __init__(self, section, etype, node):
super().__init__(section, etype, node)
self.image_index = 0
self.image_guid = ""
self.hardware_instance = 0
self.monotonic_count = 0
self.fw_version = 0
self.private_key = ""
self.pub_key_cert = ""
self.auth = 0
self.payload = ""
self.capsule_fname = ""
Please remember to use single quotes
- def ReadNode(self):
super().ReadNode()
self.cfg_file = fdt_util.GetString(self._node, 'cfg-file')
if not self.cfg_file:
self.image_index = fdt_util.GetInt(self._node, 'image-index')
if not self.image_index:
self.Raise('mkeficapsule must be provided an Image Index')
self.image_guid = fdt_util.GetString(self._node, 'image-type-id')
if not self.image_guid:
self.Raise('mkeficapsule must be provided an Image GUID')
Use self.required_props = ['image-type-id', ...] in your __init__(). Then this is automatic
self.fw_version = fdt_util.GetInt(self._node, 'fw-version')
self.hardware_instance = fdt_util.GetInt(self._node, 'hardware-instance')
self.monotonic_count = fdt_util.GetInt(self._node, 'monotonic-count')
self.private_key = fdt_util.GetString(self._node, 'private-key')
self.pub_key_cert = fdt_util.GetString(self._node, 'pub-key-cert')
if ((self.private_key and not self.pub_key_cert) or (self.pub_key_cert and not self.private_key)):
self.Raise('Both private-key and public key Certificate need to be provided')
elif not (self.private_key and self.pub_key_cert):
self.auth = 0
else:
self.auth = 1
self.payload = fdt_util.GetString(self._node, 'filename')
if not self.payload:
self.Raise('mkeficapsule must be provided an input filename(payload)')
if not os.path.isabs(self.payload):
self.payload_path = tools.get_input_filename(self.payload)
if not os.path.exists(self.payload_path):
self.Raise('Cannot resolve path to the input filename(payload)')
else:
self.payload = self.payload_path
self.capsule_fname = fdt_util.GetString(self._node, 'capsule')
if not self.capsule_fname:
self.Raise('Specify the output capsule file')
if not os.path.isabs(self.capsule_fname):
self.capsule_path = tools.get_output_filename(self.capsule_fname)
self.capsule_fname = self.capsule_path
- def _GenCapsule(self):
What if some of the inputs are missing? Does this handle missing blobs?
if self.cfg_file:
return self.mkeficapsule.capsule_cfg_file(self.cfg_file)
elif self.auth:
return self.mkeficapsule.cmdline_auth_capsule(self.image_index,
self.image_guid,
self.hardware_instance,
self.monotonic_count,
self.private_key,
self.pub_key_cert,
self.payload,
self.capsule_fname,
self.fw_version)
else:
return self.mkeficapsule.cmdline_capsule(self.image_index,
self.image_guid,
self.hardware_instance,
self.payload,
self.capsule_fname,
self.fw_version)
- def ObtainContents(self):
self.SetContents(tools.to_bytes(self._GenCapsule()))
return True
- def AddBintools(self, btools):
self.mkeficapsule = self.AddBintool(btools, 'mkeficapsule')
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 43b4f850a6..f5dd62d028 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -6676,6 +6676,133 @@ fdt fdtmap Extract the devicetree blob from the fdtmap ['fit']) self.assertIn("Node '/fit': Missing tool: 'mkimage'", str(e.exception))
- def _CheckCapsule(self, signed_capsule=False, version_check=False):
# Firmware Management Protocol GUID used in capsule header
self.capsule_guid = "edd5cb6d2de8444cbda17194199ad92a"
Please add a constant FW_MGMT_GUID = '' at the top of the file
We really should not have GUIDs in the code...they are a mess.
# Image GUID specified in the DTS
self.image_guid = "52cfd7092007104791d108469b7fe9c8"
self.fmp_signature = "4d535331"
self.fmp_size = "10"
self.fmp_fw_version = "02"
These should really be local vars, not members.
self.capsule_data = tools.read_file(self.capsule_fname)
Pass the data in here and then you don't need to read the file
self.assertEqual(self.capsule_guid, self.capsule_data.hex()[:32])
self.assertEqual(self.image_guid, self.capsule_data.hex()[96:128])
if version_check:
self.assertEqual(self.fmp_signature, self.capsule_data.hex()[184:192])
self.assertEqual(self.fmp_size, self.capsule_data.hex()[192:194])
self.assertEqual(self.fmp_fw_version, self.capsule_data.hex()[200:202])
if signed_capsule:
self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[4770:4778])
Where do these integer offsets come from? Please add a comment
elif version_check:
self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[216:224])
else:
self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[184:192])
- def _GenCapsuleCfgPayload(self, fname, contents):
capsule_dir = '/tmp/capsules/'
You can't write to /tmp
Please use self._indir for input files - see how other tests do it
pathname = os.path.join(capsule_dir, fname)
dirname = os.path.dirname(pathname)
if dirname and not os.path.exists(dirname):
os.makedirs(dirname)
with open(pathname, 'wb') as fd:
fd.write(contents)
tools.write_file(pathname, contents)
- def testCapsuleGen(self):
"""Test generation of EFI capsule"""
self.payload_data = tools.to_bytes(TEXT_DATA)
Please can you use your own test data, like EFI_DATA ? Also if you declare it as a binary string you can drop the call.
For example:
EFI_DATA = b'efi'
TestFunctional._MakeInputFile('payload.txt', self.payload_data)
self._DoReadFile('282_capsule.dts')
data = self...
self.capsule_fname = tools.get_output_filename('test.capsule')
self.assertTrue(os.path.exists(self.capsule_fname))
You can drop that line
self._CheckCapsule()
self._CheckCapsule(data)
- def testSignedCapsuleGen(self):
"""Test generation of EFI capsule"""
self.payload_data = tools.to_bytes(TEXT_DATA)
TestFunctional._MakeInputFile('payload.txt', self.payload_data)
self._DoReadFile('283_capsule_signed.dts')
data = self...
That is the actual data, so you don't need to read it below.
self.capsule_fname = tools.get_output_filename('test.capsule')
self.assertTrue(os.path.exists(self.capsule_fname))
self._CheckCapsule(signed_capsule=True)
def testCapsuleGenCfgFile(self):
"""Test generation of EFI capsule through config file"""
self.payload_data = tools.to_bytes(TEXT_DATA)
self._GenCapsuleCfgPayload('payload.txt', self.payload_data)
self._DoReadFile('284_capsule_conf.dts')
self.capsule_fname = '/tmp/capsules/test.capsule'
self.assertTrue(os.path.exists(self.capsule_fname))
self._CheckCapsule()
def testCapsuleGenVersionSupport(self):
"""Test generation of EFI capsule with version support"""
self.payload_data = tools.to_bytes(TEXT_DATA)
TestFunctional._MakeInputFile('payload.txt', self.payload_data)
self._DoReadFile('290_capsule_version.dts')
self.capsule_fname = tools.get_output_filename('test.capsule')
self.assertTrue(os.path.exists(self.capsule_fname))
self._CheckCapsule(version_check=True)
def testCapsuleGenKeyMissing(self):
"""Test that binman errors out on missing key"""
with self.assertRaises(ValueError) as e:
self._DoReadFile('285_capsule_missing_key.dts')
self.assertIn("Both private-key and public key Certificate need to be provided",
private key certificate
str(e.exception))
- def testCapsuleGenIndexMissing(self):
"""Test that binman errors out on missing image index"""
with self.assertRaises(ValueError) as e:
self._DoReadFile('286_capsule_missing_index.dts')
self.assertIn("mkeficapsule must be provided an Image Index",
str(e.exception))
- def testCapsuleGenGuidMissing(self):
"""Test that binman errors out on missing image GUID"""
with self.assertRaises(ValueError) as e:
self._DoReadFile('287_capsule_missing_guid.dts')
self.assertIn("mkeficapsule must be provided an Image GUID",
str(e.exception))
- def testCapsuleGenPayloadMissing(self):
"""Test that binman errors out on missing input(payload)image"""
with self.assertRaises(ValueError) as e:
self._DoReadFile('288_capsule_missing_payload.dts')
self.assertIn("mkeficapsule must be provided an input filename(payload)",
str(e.exception))
- def testCapsuleGenCapsuleFileMissing(self):
"""Test that binman errors out on missing output capsule file"""
with self.assertRaises(ValueError) as e:
self._DoReadFile('289_capsule_missing.dts')
self.assertIn("Specify the output capsule file",
str(e.exception))
This looks good. It is a pain that you need to cover each missing arg. I'm not sure I can think of a better way.
if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/282_capsule.dts b/tools/binman/test/282_capsule.dts new file mode 100644 index 0000000000..0e7fcdf8ab --- /dev/null +++ b/tools/binman/test/282_capsule.dts @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
capsule {
image-index = <0x1>;
image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8";
Is there a #define somewhere for this? Perhaps you can add a #define, or a comment as to what this is.
Also, please use lower case.
hardware-instance = <0x0>;
filename = "payload.txt";
capsule = "test.capsule";
};
};
+};
Regards, Simon

hi Simon,
On Sun, 16 Jul 2023 at 05:12, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Sat, 15 Jul 2023 at 07:46, Sughosh Ganu sughosh.ganu@linaro.org wrote:
Add support in binman for generating capsules. The capsule parameters can be specified either through a config file or through the capsule binman entry. Also add test cases in binman for capsule generation, and enable this testing on the sandbox_spl variant.
Can you use sandbox instead, or perhaps sandbox_spl? SPL is really for SPL testing.
Er, I am actually using the sandbox_spl variant.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
Changes since V3:
- Add test cases for covering the various capsule generation scenarios.
- Add function comments in the mkeficapsule bintool.
- Fix the fetch method of the mkeficapsule bintool to enable building the tool.
- Add more details about the capsule parameters in the documentation as well as the code.
- Fix order of module imports, and addition of blank lines in the capsule.py file.
- Use SetContents in the ObtainContents method.
configs/sandbox_spl_defconfig | 1 + tools/binman/btool/mkeficapsule.py | 158 ++++++++++++++++++ tools/binman/entries.rst | 37 ++++ tools/binman/etype/capsule.py | 132 +++++++++++++++ tools/binman/ftest.py | 127 ++++++++++++++ tools/binman/test/282_capsule.dts | 18 ++ tools/binman/test/283_capsule_signed.dts | 20 +++ tools/binman/test/284_capsule_conf.dts | 14 ++ tools/binman/test/285_capsule_missing_key.dts | 19 +++ .../binman/test/286_capsule_missing_index.dts | 17 ++ .../binman/test/287_capsule_missing_guid.dts | 17 ++ .../test/288_capsule_missing_payload.dts | 17 ++ tools/binman/test/289_capsule_missing.dts | 17 ++ tools/binman/test/290_capsule_version.dts | 19 +++ tools/binman/test/capsule_cfg.txt | 6 + 15 files changed, 619 insertions(+) create mode 100644 tools/binman/btool/mkeficapsule.py create mode 100644 tools/binman/etype/capsule.py create mode 100644 tools/binman/test/282_capsule.dts create mode 100644 tools/binman/test/283_capsule_signed.dts create mode 100644 tools/binman/test/284_capsule_conf.dts create mode 100644 tools/binman/test/285_capsule_missing_key.dts create mode 100644 tools/binman/test/286_capsule_missing_index.dts create mode 100644 tools/binman/test/287_capsule_missing_guid.dts create mode 100644 tools/binman/test/288_capsule_missing_payload.dts create mode 100644 tools/binman/test/289_capsule_missing.dts create mode 100644 tools/binman/test/290_capsule_version.dts create mode 100644 tools/binman/test/capsule_cfg.txt
This looks pretty good to me. Some nits below
diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig index dd848c57c6..2fcc789347 100644 --- a/configs/sandbox_spl_defconfig +++ b/configs/sandbox_spl_defconfig @@ -248,3 +248,4 @@ CONFIG_UNIT_TEST=y CONFIG_SPL_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_TOOLS_MKEFICAPSULE=y
Why enabling this here? I don't think it is needed in sandbox_spl, but in any case it should be in a different patch if needed.
The binman tests run on the sandbox_spl variant. When running the capsule generation tests, the mkeficapsule tool should be present on the board variant no?
diff --git a/tools/binman/btool/mkeficapsule.py b/tools/binman/btool/mkeficapsule.py new file mode 100644 index 0000000000..ba6b666714 --- /dev/null +++ b/tools/binman/btool/mkeficapsule.py @@ -0,0 +1,158 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2023 Linaro Limited +# +"""Bintool implementation for mkeficapsule tool
+mkeficapsule is a tool used for generating EFI capsules.
+The following are the command-line options to be provided +to the tool +Usage: mkeficapsule [options] <image blob> <output file> +Options:
-g, --guid <guid string> guid for image blob type
-i, --index <index> update image index
-I, --instance <instance> update hardware instance
-v, --fw-version <version> firmware version
-p, --private-key <privkey file> private key file
-c, --certificate <cert file> signer's certificate file
-m, --monotonic-count <count> monotonic count
-d, --dump_sig dump signature (*.p7)
-A, --fw-accept firmware accept capsule, requires GUID, no image blob
-R, --fw-revert firmware revert capsule, takes no GUID, no image blob
-o, --capoemflag Capsule OEM Flag, an integer between 0x0000 and 0xffff
-f, --cfg-file <config file> config file with capsule parameters
-h, --help print a help message
+"""
+from binman import bintool
+class Bintoolmkeficapsule(bintool.Bintool):
- """Handles the 'mkeficapsule' tool
- This bintool is used for generating the EFI capsules. The
- capsule generation parameters can either be specified through
- command-line, or through a config file.
- """
- def __init__(self, name):
super().__init__(name, 'mkeficapsule tool for generating capsules')
- def capsule_cfg_file(self, cfg_file):
"""Generate a capsule reading parameters from config file
Args:
cfg_file (str): Path to the config file
Returns:
str: Tool output
"""
nit: drop blank lines after """ function comment (please fix throughout)
Okay
args = [
f'--cfg-file={cfg_file}'
]
return self.run_cmd(*args)
- def cmdline_capsule(self, image_index, image_guid, hardware_instance,
payload, output_fname, version=0):
"""Generate a capsule through commandline provided parameters
Args:
image_index (int): Unique number for identifying payload image
image_guid (str): GUID used for identifying the image
hardware_instance (int): Optional unique hardware instance of
a device in the system. 0 if not being used
payload (str): Path to the input payload image
output_fname (str): Path to the output capsule file
version (int): Image version (Optional)
Returns:
str: Tool output
"""
if version:
args = [
f'--index={image_index}',
f'--fw-version={version}',
f'--guid={image_guid}',
f'--instance={hardware_instance}',
payload,
output_fname
]
else:
args = [
f'--index={image_index}',
f'--guid={image_guid}',
f'--instance={hardware_instance}',
payload,
output_fname
]
return self.run_cmd(*args)
- def cmdline_auth_capsule(self, image_index, image_guid, hardware_instance,
monotonic_count, priv_key, pub_key,
payload, output_fname, version=0):
"""Generate a signed capsule through commandline provided parameters
Args:
image_index (int): Unique number for identifying payload image
image_guid (str): GUID used for identifying the image
hardware_instance (int): Optional unique hardware instance of
a device in the system. 0 if not being used
monotonic_count (int): Count used when signing an image
priv_key (str): Path to the private key
pub_key(str): Path to the public key
payload (str): Path to the input payload image
output_fname (str): Path to the output capsule file
version (int): Image version (Optional)
Returns:
str: Tool output
"""
if version:
args = [
f'--index={image_index}',
f'--guid={image_guid}',
f'--instance={hardware_instance}',
f'--monotonic-count={monotonic_count}',
f'--private-key={priv_key}',
f'--certificate={pub_key}',
f'--fw-version={version}',
payload,
output_fname
]
else:
args = [
f'--index={image_index}',
f'--guid={image_guid}',
f'--instance={hardware_instance}',
f'--monotonic-count={monotonic_count}',
f'--private-key={priv_key}',
f'--certificate={pub_key}',
payload,
output_fname
]
return self.run_cmd(*args)
- def fetch(self, method):
"""Fetch handler for mkeficapsule
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
cmd = ['tools-only_defconfig', 'tools']
result = self.build_from_git(
'https://source.denx.de/u-boot/u-boot.git',
cmd,
'tools/mkeficapsule')
return result
diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index b71af801fd..523c8222f5 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -283,6 +283,43 @@ entry; similarly for SPL.
+.. _etype_capsule:
+Entry: capsule: Entry for generating EFI Capsule files +------------------------------------------------------
+This is an entry for generating EFI capsules.
+The parameters needed for generation of the capsules can either be +provided separately, or through a config file.
+Properties / Entry arguments:
- cfg-file: Config file for providing capsule
parameters. These are parameters needed for generating the
capsules. The parameters can be listed by running the
'./tools/mkeficapsule -h' command.
- image-index: Unique number for identifying corresponding
payload image. Number between 1 and descriptor count, i.e.
the total number of firmware images that can be updated.
- image-type-id: Image GUID which will be used for identifying the
updatable image on the board.
- hardware-instance: Optional number for identifying unique
hardware instance of a device in the system. Default value of 0
for images where value is not to be used.
- fw-version: Optional value of image version that can be put on
the capsule through the Firmware Management Protocol(FMP) header.
- monotomic-count: Count used when signing an image.
- private-key: Path to PEM formatted .key private key file.
- pub-key-cert: Path to PEM formatted .crt public key certificate
file.
- filename: Path to the input(payload) file. File can be any
format, a binary or an elf, platform specific.
- capsule: Path to the output capsule file. A capsule is a
continous set of data as defined by the EFI specification. Refer
to the specification for more details.
.. _etype_cbfs:
Entry: cbfs: Coreboot Filesystem (CBFS) diff --git a/tools/binman/etype/capsule.py b/tools/binman/etype/capsule.py new file mode 100644 index 0000000000..46e5507704 --- /dev/null +++ b/tools/binman/etype/capsule.py @@ -0,0 +1,132 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2023 Linaro Limited +# +# Entry-type module for producing a capsule +#
+import os
+from binman.entry import Entry +from dtoc import fdt_util +from u_boot_pylib import tools
+class Entry_capsule(Entry):
- """Entry for generating EFI capsules
- This is an entry for generating EFI capsules.
- The parameters needed for generation of the capsules can
- either be provided separately, or through a config file.
- Properties / Entry arguments:
- cfg-file: Config file for providing capsule
parameters. These are parameters needed for generating the
capsules. The parameters can be listed by running the
'./tools/mkeficapsule -h' command.
- image-index: Unique number for identifying corresponding
payload image. Number between 1 and descriptor count, i.e.
the total number of firmware images that can be updated.
- image-type-id: Image GUID which will be used for identifying the
updatable image on the board.
- hardware-instance: Optional number for identifying unique
hardware instance of a device in the system. Default value of 0
for images where value is not to be used.
- fw-version: Optional value of image version that can be put on
the capsule through the Firmware Management Protocol(FMP) header.
- monotomic-count: Count used when signing an image.
monotonic
- private-key: Path to PEM formatted .key private key file.
- pub-key-cert: Path to PEM formatted .crt public key certificate
file.
- filename: Path to the input(payload) file. File can be any
format, a binary or an elf, platform specific.
- capsule: Path to the output capsule file. A capsule is a
continous set of data as defined by the EFI specification. Refer
continuous
Can you add a link to EFI spec so it appears in the docs here?
Will fix the above typos and add the link.
to the specification for more details.
- """
- def __init__(self, section, etype, node):
super().__init__(section, etype, node)
self.image_index = 0
self.image_guid = ""
self.hardware_instance = 0
self.monotonic_count = 0
self.fw_version = 0
self.private_key = ""
self.pub_key_cert = ""
self.auth = 0
self.payload = ""
self.capsule_fname = ""
Please remember to use single quotes
Okay
- def ReadNode(self):
super().ReadNode()
self.cfg_file = fdt_util.GetString(self._node, 'cfg-file')
if not self.cfg_file:
self.image_index = fdt_util.GetInt(self._node, 'image-index')
if not self.image_index:
self.Raise('mkeficapsule must be provided an Image Index')
self.image_guid = fdt_util.GetString(self._node, 'image-type-id')
if not self.image_guid:
self.Raise('mkeficapsule must be provided an Image GUID')
Use self.required_props = ['image-type-id', ...] in your __init__(). Then this is automatic
I should have clarified this during the earlier version itself. So these parameters are mandatory only when not using the config file. In the scenario of generating the capsules through config files, all these parameters are provided through the config file. Hence these explicit checks.
self.fw_version = fdt_util.GetInt(self._node, 'fw-version')
self.hardware_instance = fdt_util.GetInt(self._node, 'hardware-instance')
self.monotonic_count = fdt_util.GetInt(self._node, 'monotonic-count')
self.private_key = fdt_util.GetString(self._node, 'private-key')
self.pub_key_cert = fdt_util.GetString(self._node, 'pub-key-cert')
if ((self.private_key and not self.pub_key_cert) or (self.pub_key_cert and not self.private_key)):
self.Raise('Both private-key and public key Certificate need to be provided')
elif not (self.private_key and self.pub_key_cert):
self.auth = 0
else:
self.auth = 1
self.payload = fdt_util.GetString(self._node, 'filename')
if not self.payload:
self.Raise('mkeficapsule must be provided an input filename(payload)')
if not os.path.isabs(self.payload):
self.payload_path = tools.get_input_filename(self.payload)
if not os.path.exists(self.payload_path):
self.Raise('Cannot resolve path to the input filename(payload)')
else:
self.payload = self.payload_path
self.capsule_fname = fdt_util.GetString(self._node, 'capsule')
if not self.capsule_fname:
self.Raise('Specify the output capsule file')
if not os.path.isabs(self.capsule_fname):
self.capsule_path = tools.get_output_filename(self.capsule_fname)
self.capsule_fname = self.capsule_path
- def _GenCapsule(self):
What if some of the inputs are missing? Does this handle missing blobs?
Any missing input parameters are checked earlier itself.
if self.cfg_file:
return self.mkeficapsule.capsule_cfg_file(self.cfg_file)
elif self.auth:
return self.mkeficapsule.cmdline_auth_capsule(self.image_index,
self.image_guid,
self.hardware_instance,
self.monotonic_count,
self.private_key,
self.pub_key_cert,
self.payload,
self.capsule_fname,
self.fw_version)
else:
return self.mkeficapsule.cmdline_capsule(self.image_index,
self.image_guid,
self.hardware_instance,
self.payload,
self.capsule_fname,
self.fw_version)
- def ObtainContents(self):
self.SetContents(tools.to_bytes(self._GenCapsule()))
return True
- def AddBintools(self, btools):
self.mkeficapsule = self.AddBintool(btools, 'mkeficapsule')
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 43b4f850a6..f5dd62d028 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -6676,6 +6676,133 @@ fdt fdtmap Extract the devicetree blob from the fdtmap ['fit']) self.assertIn("Node '/fit': Missing tool: 'mkimage'", str(e.exception))
- def _CheckCapsule(self, signed_capsule=False, version_check=False):
# Firmware Management Protocol GUID used in capsule header
self.capsule_guid = "edd5cb6d2de8444cbda17194199ad92a"
Please add a constant FW_MGMT_GUID = '' at the top of the file
Okay. Will add for the capsule_guid and image_guid values.
We really should not have GUIDs in the code...they are a mess.
You want the UEFI capsule generation to happen through binman, and not mention GUIDs. That ain't happening :)
# Image GUID specified in the DTS
self.image_guid = "52cfd7092007104791d108469b7fe9c8"
self.fmp_signature = "4d535331"
self.fmp_size = "10"
self.fmp_fw_version = "02"
These should really be local vars, not members.
Okay
self.capsule_data = tools.read_file(self.capsule_fname)
Pass the data in here and then you don't need to read the file
So the file needs to be read here since the actual capsule generation tool(tools/mkeficapsule) does not return any capsule data. Instead, the data gets written to the capsule file, and the tool just returns a pass/fail status.
self.assertEqual(self.capsule_guid, self.capsule_data.hex()[:32])
self.assertEqual(self.image_guid, self.capsule_data.hex()[96:128])
if version_check:
self.assertEqual(self.fmp_signature, self.capsule_data.hex()[184:192])
self.assertEqual(self.fmp_size, self.capsule_data.hex()[192:194])
self.assertEqual(self.fmp_fw_version, self.capsule_data.hex()[200:202])
if signed_capsule:
self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[4770:4778])
Where do these integer offsets come from? Please add a comment
So, these are simply offsets in the output capsule file, which get impacted based on the contents being put in the capsule, like presence/absence of optional headers. I don't think putting a comment really wll add any value, because these offsets are variable.
elif version_check:
self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[216:224])
else:
self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[184:192])
- def _GenCapsuleCfgPayload(self, fname, contents):
capsule_dir = '/tmp/capsules/'
You can't write to /tmp
Please use self._indir for input files - see how other tests do it
For all other tests, I am indeed using _indir and outdir. But for generation of capsules through a config file, we need to specify the location where the output capsule file will be written to. Which is the reason for the /tmp/capsules/. We are using this directory for collating all capsule related files.
pathname = os.path.join(capsule_dir, fname)
dirname = os.path.dirname(pathname)
if dirname and not os.path.exists(dirname):
os.makedirs(dirname)
with open(pathname, 'wb') as fd:
fd.write(contents)
tools.write_file(pathname, contents)
Okay
- def testCapsuleGen(self):
"""Test generation of EFI capsule"""
self.payload_data = tools.to_bytes(TEXT_DATA)
Please can you use your own test data, like EFI_DATA ? Also if you declare it as a binary string you can drop the call.
For example:
EFI_DATA = b'efi'
I don't know why text_data cannot be used, but I will add the EFI_DATA.
TestFunctional._MakeInputFile('payload.txt', self.payload_data)
self._DoReadFile('282_capsule.dts')
data = self...
Please see above. We need to read the capsule file. This applies for all the related comments about using the data = self._DoReadFile...
self.capsule_fname = tools.get_output_filename('test.capsule')
self.assertTrue(os.path.exists(self.capsule_fname))
You can drop that line
self._CheckCapsule()
self._CheckCapsule(data)
- def testSignedCapsuleGen(self):
"""Test generation of EFI capsule"""
self.payload_data = tools.to_bytes(TEXT_DATA)
TestFunctional._MakeInputFile('payload.txt', self.payload_data)
self._DoReadFile('283_capsule_signed.dts')
data = self...
That is the actual data, so you don't need to read it below.
self.capsule_fname = tools.get_output_filename('test.capsule')
self.assertTrue(os.path.exists(self.capsule_fname))
self._CheckCapsule(signed_capsule=True)
def testCapsuleGenCfgFile(self):
"""Test generation of EFI capsule through config file"""
self.payload_data = tools.to_bytes(TEXT_DATA)
self._GenCapsuleCfgPayload('payload.txt', self.payload_data)
self._DoReadFile('284_capsule_conf.dts')
self.capsule_fname = '/tmp/capsules/test.capsule'
self.assertTrue(os.path.exists(self.capsule_fname))
self._CheckCapsule()
def testCapsuleGenVersionSupport(self):
"""Test generation of EFI capsule with version support"""
self.payload_data = tools.to_bytes(TEXT_DATA)
TestFunctional._MakeInputFile('payload.txt', self.payload_data)
self._DoReadFile('290_capsule_version.dts')
self.capsule_fname = tools.get_output_filename('test.capsule')
self.assertTrue(os.path.exists(self.capsule_fname))
self._CheckCapsule(version_check=True)
def testCapsuleGenKeyMissing(self):
"""Test that binman errors out on missing key"""
with self.assertRaises(ValueError) as e:
self._DoReadFile('285_capsule_missing_key.dts')
self.assertIn("Both private-key and public key Certificate need to be provided",
private key certificate
Okay
str(e.exception))
- def testCapsuleGenIndexMissing(self):
"""Test that binman errors out on missing image index"""
with self.assertRaises(ValueError) as e:
self._DoReadFile('286_capsule_missing_index.dts')
self.assertIn("mkeficapsule must be provided an Image Index",
str(e.exception))
- def testCapsuleGenGuidMissing(self):
"""Test that binman errors out on missing image GUID"""
with self.assertRaises(ValueError) as e:
self._DoReadFile('287_capsule_missing_guid.dts')
self.assertIn("mkeficapsule must be provided an Image GUID",
str(e.exception))
- def testCapsuleGenPayloadMissing(self):
"""Test that binman errors out on missing input(payload)image"""
with self.assertRaises(ValueError) as e:
self._DoReadFile('288_capsule_missing_payload.dts')
self.assertIn("mkeficapsule must be provided an input filename(payload)",
str(e.exception))
- def testCapsuleGenCapsuleFileMissing(self):
"""Test that binman errors out on missing output capsule file"""
with self.assertRaises(ValueError) as e:
self._DoReadFile('289_capsule_missing.dts')
self.assertIn("Specify the output capsule file",
str(e.exception))
This looks good. It is a pain that you need to cover each missing arg. I'm not sure I can think of a better way.
if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/282_capsule.dts b/tools/binman/test/282_capsule.dts new file mode 100644 index 0000000000..0e7fcdf8ab --- /dev/null +++ b/tools/binman/test/282_capsule.dts @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
capsule {
image-index = <0x1>;
image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8";
Is there a #define somewhere for this? Perhaps you can add a #define, or a comment as to what this is.
I will put a comment.
Also, please use lower case.
Okay
-sughosh
hardware-instance = <0x0>;
filename = "payload.txt";
capsule = "test.capsule";
};
};
+};
Regards, Simon

Hi Sughosh,
On Mon, 17 Jul 2023 at 04:44, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Simon,
On Sun, 16 Jul 2023 at 05:12, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Sat, 15 Jul 2023 at 07:46, Sughosh Ganu sughosh.ganu@linaro.org wrote:
Add support in binman for generating capsules. The capsule parameters can be specified either through a config file or through the capsule binman entry. Also add test cases in binman for capsule generation, and enable this testing on the sandbox_spl variant.
Can you use sandbox instead, or perhaps sandbox_spl? SPL is really for SPL testing.
Er, I am actually using the sandbox_spl variant.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
Changes since V3:
- Add test cases for covering the various capsule generation scenarios.
- Add function comments in the mkeficapsule bintool.
- Fix the fetch method of the mkeficapsule bintool to enable building the tool.
- Add more details about the capsule parameters in the documentation as well as the code.
- Fix order of module imports, and addition of blank lines in the capsule.py file.
- Use SetContents in the ObtainContents method.
configs/sandbox_spl_defconfig | 1 + tools/binman/btool/mkeficapsule.py | 158 ++++++++++++++++++ tools/binman/entries.rst | 37 ++++ tools/binman/etype/capsule.py | 132 +++++++++++++++ tools/binman/ftest.py | 127 ++++++++++++++ tools/binman/test/282_capsule.dts | 18 ++ tools/binman/test/283_capsule_signed.dts | 20 +++ tools/binman/test/284_capsule_conf.dts | 14 ++ tools/binman/test/285_capsule_missing_key.dts | 19 +++ .../binman/test/286_capsule_missing_index.dts | 17 ++ .../binman/test/287_capsule_missing_guid.dts | 17 ++ .../test/288_capsule_missing_payload.dts | 17 ++ tools/binman/test/289_capsule_missing.dts | 17 ++ tools/binman/test/290_capsule_version.dts | 19 +++ tools/binman/test/capsule_cfg.txt | 6 + 15 files changed, 619 insertions(+) create mode 100644 tools/binman/btool/mkeficapsule.py create mode 100644 tools/binman/etype/capsule.py create mode 100644 tools/binman/test/282_capsule.dts create mode 100644 tools/binman/test/283_capsule_signed.dts create mode 100644 tools/binman/test/284_capsule_conf.dts create mode 100644 tools/binman/test/285_capsule_missing_key.dts create mode 100644 tools/binman/test/286_capsule_missing_index.dts create mode 100644 tools/binman/test/287_capsule_missing_guid.dts create mode 100644 tools/binman/test/288_capsule_missing_payload.dts create mode 100644 tools/binman/test/289_capsule_missing.dts create mode 100644 tools/binman/test/290_capsule_version.dts create mode 100644 tools/binman/test/capsule_cfg.txt
This looks pretty good to me. Some nits below
diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig index dd848c57c6..2fcc789347 100644 --- a/configs/sandbox_spl_defconfig +++ b/configs/sandbox_spl_defconfig @@ -248,3 +248,4 @@ CONFIG_UNIT_TEST=y CONFIG_SPL_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_TOOLS_MKEFICAPSULE=y
Why enabling this here? I don't think it is needed in sandbox_spl, but in any case it should be in a different patch if needed.
The binman tests run on the sandbox_spl variant. When running the capsule generation tests, the mkeficapsule tool should be present on the board variant no?
Can we run this on the 'sandbox' variant instead?
[..]
- def ReadNode(self):
super().ReadNode()
self.cfg_file = fdt_util.GetString(self._node, 'cfg-file')
if not self.cfg_file:
self.image_index = fdt_util.GetInt(self._node, 'image-index')
if not self.image_index:
self.Raise('mkeficapsule must be provided an Image Index')
self.image_guid = fdt_util.GetString(self._node, 'image-type-id')
if not self.image_guid:
self.Raise('mkeficapsule must be provided an Image GUID')
Use self.required_props = ['image-type-id', ...] in your __init__(). Then this is automatic
I should have clarified this during the earlier version itself. So these parameters are mandatory only when not using the config file. In the scenario of generating the capsules through config files, all these parameters are provided through the config file. Hence these explicit checks.
Hmm, I think we should consider having two different etypes, then. It seems in fact that your entry type is doing 2-3 different things?
[..]
What if some of the inputs are missing? Does this handle missing blobs?
Any missing input parameters are checked earlier itself.
if self.cfg_file:
return self.mkeficapsule.capsule_cfg_file(self.cfg_file)
elif self.auth:
return self.mkeficapsule.cmdline_auth_capsule(self.image_index,
self.image_guid,
self.hardware_instance,
self.monotonic_count,
self.private_key,
self.pub_key_cert,
self.payload,
self.capsule_fname,
self.fw_version)
else:
return self.mkeficapsule.cmdline_capsule(self.image_index,
self.image_guid,
self.hardware_instance,
self.payload,
self.capsule_fname,
self.fw_version)
Here is where I wonder whether you are putting too much in a single etype. Here there are three different cases. Should we have 3 etypes?
[..]
We really should not have GUIDs in the code...they are a mess.
You want the UEFI capsule generation to happen through binman, and not mention GUIDs. That ain't happening :)
I just don't want them open-coded. They are meaningless gibberish that no one can understand. Use #define or some other way to give them a name.
If I wrote:
writel(0x09812374, 0x8723728)
you would have the same comment.
# Image GUID specified in the DTS
self.image_guid = "52cfd7092007104791d108469b7fe9c8"
self.fmp_signature = "4d535331"
self.fmp_size = "10"
self.fmp_fw_version = "02"
These should really be local vars, not members.
Okay
self.capsule_data = tools.read_file(self.capsule_fname)
Pass the data in here and then you don't need to read the file
So the file needs to be read here since the actual capsule generation tool(tools/mkeficapsule) does not return any capsule data. Instead, the data gets written to the capsule file, and the tool just returns a pass/fail status.
Sure, but you can read that data in the caller to this functoin.
self.assertEqual(self.capsule_guid, self.capsule_data.hex()[:32])
self.assertEqual(self.image_guid, self.capsule_data.hex()[96:128])
if version_check:
self.assertEqual(self.fmp_signature, self.capsule_data.hex()[184:192])
self.assertEqual(self.fmp_size, self.capsule_data.hex()[192:194])
self.assertEqual(self.fmp_fw_version, self.capsule_data.hex()[200:202])
if signed_capsule:
self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[4770:4778])
Where do these integer offsets come from? Please add a comment
So, these are simply offsets in the output capsule file, which get impacted based on the contents being put in the capsule, like presence/absence of optional headers. I don't think putting a comment really wll add any value, because these offsets are variable.
OK then please add a comment to that effect, as well as how to figure them out when things change.
elif version_check:
self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[216:224])
else:
self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[184:192])
- def _GenCapsuleCfgPayload(self, fname, contents):
capsule_dir = '/tmp/capsules/'
You can't write to /tmp
Please use self._indir for input files - see how other tests do it
For all other tests, I am indeed using _indir and outdir. But for generation of capsules through a config file, we need to specify the location where the output capsule file will be written to. Which is the reason for the /tmp/capsules/. We are using this directory for collating all capsule related files.
Well, sorry, you can't do that. I think you should provide a relative path, rather than absolute...that should solve the problem
[..]
Please can you use your own test data, like EFI_DATA ? Also if you declare it as a binary string you can drop the call.
For example:
EFI_DATA = b'efi'
I don't know why text_data cannot be used, but I will add the EFI_DATA.
Well firstly it is not a 'bytes' string. Secondly you may as well have your own as we have done with other etypes.
TestFunctional._MakeInputFile('payload.txt', self.payload_data)
self._DoReadFile('282_capsule.dts')
data = self...
Please see above. We need to read the capsule file. This applies for all the related comments about using the data = self._DoReadFile...
That needs to be fixed, since the output file should be the capsule. Why would the output file be anything else??
[..]
Regards, Simon

hi Simon,
On Wed, 19 Jul 2023 at 06:41, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Mon, 17 Jul 2023 at 04:44, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Simon,
On Sun, 16 Jul 2023 at 05:12, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Sat, 15 Jul 2023 at 07:46, Sughosh Ganu sughosh.ganu@linaro.org wrote:
Add support in binman for generating capsules. The capsule parameters can be specified either through a config file or through the capsule binman entry. Also add test cases in binman for capsule generation, and enable this testing on the sandbox_spl variant.
Can you use sandbox instead, or perhaps sandbox_spl? SPL is really for SPL testing.
Er, I am actually using the sandbox_spl variant.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
Changes since V3:
- Add test cases for covering the various capsule generation scenarios.
- Add function comments in the mkeficapsule bintool.
- Fix the fetch method of the mkeficapsule bintool to enable building the tool.
- Add more details about the capsule parameters in the documentation as well as the code.
- Fix order of module imports, and addition of blank lines in the capsule.py file.
- Use SetContents in the ObtainContents method.
configs/sandbox_spl_defconfig | 1 + tools/binman/btool/mkeficapsule.py | 158 ++++++++++++++++++ tools/binman/entries.rst | 37 ++++ tools/binman/etype/capsule.py | 132 +++++++++++++++ tools/binman/ftest.py | 127 ++++++++++++++ tools/binman/test/282_capsule.dts | 18 ++ tools/binman/test/283_capsule_signed.dts | 20 +++ tools/binman/test/284_capsule_conf.dts | 14 ++ tools/binman/test/285_capsule_missing_key.dts | 19 +++ .../binman/test/286_capsule_missing_index.dts | 17 ++ .../binman/test/287_capsule_missing_guid.dts | 17 ++ .../test/288_capsule_missing_payload.dts | 17 ++ tools/binman/test/289_capsule_missing.dts | 17 ++ tools/binman/test/290_capsule_version.dts | 19 +++ tools/binman/test/capsule_cfg.txt | 6 + 15 files changed, 619 insertions(+) create mode 100644 tools/binman/btool/mkeficapsule.py create mode 100644 tools/binman/etype/capsule.py create mode 100644 tools/binman/test/282_capsule.dts create mode 100644 tools/binman/test/283_capsule_signed.dts create mode 100644 tools/binman/test/284_capsule_conf.dts create mode 100644 tools/binman/test/285_capsule_missing_key.dts create mode 100644 tools/binman/test/286_capsule_missing_index.dts create mode 100644 tools/binman/test/287_capsule_missing_guid.dts create mode 100644 tools/binman/test/288_capsule_missing_payload.dts create mode 100644 tools/binman/test/289_capsule_missing.dts create mode 100644 tools/binman/test/290_capsule_version.dts create mode 100644 tools/binman/test/capsule_cfg.txt
This looks pretty good to me. Some nits below
diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig index dd848c57c6..2fcc789347 100644 --- a/configs/sandbox_spl_defconfig +++ b/configs/sandbox_spl_defconfig @@ -248,3 +248,4 @@ CONFIG_UNIT_TEST=y CONFIG_SPL_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_TOOLS_MKEFICAPSULE=y
Why enabling this here? I don't think it is needed in sandbox_spl, but in any case it should be in a different patch if needed.
The binman tests run on the sandbox_spl variant. When running the capsule generation tests, the mkeficapsule tool should be present on the board variant no?
Can we run this on the 'sandbox' variant instead?
The capsule tests can be run on sandbox. But the change in the sandbox_spl_defconfig is for adding support for the capsule tests which are run as part of the binman test suite in CI. So it would be a question of whether the rest of the binman tests in CI can be run on the sandbox variant. Or are there any specific set of tests which need to be run on the sandbox_spl variant?
[..]
- def ReadNode(self):
super().ReadNode()
self.cfg_file = fdt_util.GetString(self._node, 'cfg-file')
if not self.cfg_file:
self.image_index = fdt_util.GetInt(self._node, 'image-index')
if not self.image_index:
self.Raise('mkeficapsule must be provided an Image Index')
self.image_guid = fdt_util.GetString(self._node, 'image-type-id')
if not self.image_guid:
self.Raise('mkeficapsule must be provided an Image GUID')
Use self.required_props = ['image-type-id', ...] in your __init__(). Then this is automatic
I should have clarified this during the earlier version itself. So these parameters are mandatory only when not using the config file. In the scenario of generating the capsules through config files, all these parameters are provided through the config file. Hence these explicit checks.
Hmm, I think we should consider having two different etypes, then. It seems in fact that your entry type is doing 2-3 different things?
I am wondering if having two different etypes might get confusing for the users.
[..]
What if some of the inputs are missing? Does this handle missing blobs?
Any missing input parameters are checked earlier itself.
if self.cfg_file:
return self.mkeficapsule.capsule_cfg_file(self.cfg_file)
elif self.auth:
return self.mkeficapsule.cmdline_auth_capsule(self.image_index,
self.image_guid,
self.hardware_instance,
self.monotonic_count,
self.private_key,
self.pub_key_cert,
self.payload,
self.capsule_fname,
self.fw_version)
else:
return self.mkeficapsule.cmdline_capsule(self.image_index,
self.image_guid,
self.hardware_instance,
self.payload,
self.capsule_fname,
self.fw_version)
Here is where I wonder whether you are putting too much in a single etype. Here there are three different cases. Should we have 3 etypes?
Basically the result in all the three cases is the same -- generation of a capsule file. Just that the input parameters being passed for generation are slightly different. There can be multiple entry types for these, but like I mentioned above, would having multiple entry types not be confusing for the users?
[..]
We really should not have GUIDs in the code...they are a mess.
You want the UEFI capsule generation to happen through binman, and not mention GUIDs. That ain't happening :)
I just don't want them open-coded. They are meaningless gibberish that no one can understand. Use #define or some other way to give them a name.
Yes, I will move the GUIDS under a variable.
If I wrote:
writel(0x09812374, 0x8723728)
you would have the same comment.
# Image GUID specified in the DTS
self.image_guid = "52cfd7092007104791d108469b7fe9c8"
self.fmp_signature = "4d535331"
self.fmp_size = "10"
self.fmp_fw_version = "02"
These should really be local vars, not members.
Okay
self.capsule_data = tools.read_file(self.capsule_fname)
Pass the data in here and then you don't need to read the file
So the file needs to be read here since the actual capsule generation tool(tools/mkeficapsule) does not return any capsule data. Instead, the data gets written to the capsule file, and the tool just returns a pass/fail status.
Sure, but you can read that data in the caller to this functoin.
Yes, the data can be read in the caller, but that would mean duplicating the read in four functions. Instead the file read is happening in one place where the data is being used.
self.assertEqual(self.capsule_guid, self.capsule_data.hex()[:32])
self.assertEqual(self.image_guid, self.capsule_data.hex()[96:128])
if version_check:
self.assertEqual(self.fmp_signature, self.capsule_data.hex()[184:192])
self.assertEqual(self.fmp_size, self.capsule_data.hex()[192:194])
self.assertEqual(self.fmp_fw_version, self.capsule_data.hex()[200:202])
if signed_capsule:
self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[4770:4778])
Where do these integer offsets come from? Please add a comment
So, these are simply offsets in the output capsule file, which get impacted based on the contents being put in the capsule, like presence/absence of optional headers. I don't think putting a comment really wll add any value, because these offsets are variable.
OK then please add a comment to that effect, as well as how to figure them out when things change.
Okay
elif version_check:
self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[216:224])
else:
self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[184:192])
- def _GenCapsuleCfgPayload(self, fname, contents):
capsule_dir = '/tmp/capsules/'
You can't write to /tmp
Please use self._indir for input files - see how other tests do it
For all other tests, I am indeed using _indir and outdir. But for generation of capsules through a config file, we need to specify the location where the output capsule file will be written to. Which is the reason for the /tmp/capsules/. We are using this directory for collating all capsule related files.
Well, sorry, you can't do that. I think you should provide a relative path, rather than absolute...that should solve the problem
Using absolute paths is only being done in the case of testing capsule generation through config file -- all the rest of the test cases are using relative paths for the input file and the capsule file. For the generation of capsules through the config file, the paths either need to be absolute, or relative to the directory from which the mkeficapsule tool is being invoked. And I believe that the binman test is creating temporary directories at runtime for input and output files.
What is the issue you see in using the /tmp/capsules/ path for generation of capsules. I see directories being created with relevant files under /opt for other tests. If you would want the capsules directory to be in some other location, I can change that.
[..]
Please can you use your own test data, like EFI_DATA ? Also if you declare it as a binary string you can drop the call.
For example:
EFI_DATA = b'efi'
I don't know why text_data cannot be used, but I will add the EFI_DATA.
Well firstly it is not a 'bytes' string. Secondly you may as well have your own as we have done with other etypes.
Okay
TestFunctional._MakeInputFile('payload.txt', self.payload_data)
self._DoReadFile('282_capsule.dts')
data = self...
Please see above. We need to read the capsule file. This applies for all the related comments about using the data = self._DoReadFile...
That needs to be fixed, since the output file should be the capsule. Why would the output file be anything else??
The output file is indeed a capsule, but that is being generated by the capsule generation tool which gets called from the bintool. So the mkeficapsule tool that the bintool calls results in generation of the capsule file. In that way, this does not map directly to the concept of an image in binman. And I was wondering if I should introduce a new property like 'dummy-image', so that the resultant <image>.bin and <image>.map files are not created -- they really are not needed in the case of capsule generation. Would you be fine with an introduction of such a property?
-sughosh

Hi Sughosh,
On Wed, 19 Jul 2023 at 02:42, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Simon,
On Wed, 19 Jul 2023 at 06:41, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Mon, 17 Jul 2023 at 04:44, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Simon,
On Sun, 16 Jul 2023 at 05:12, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Sat, 15 Jul 2023 at 07:46, Sughosh Ganu sughosh.ganu@linaro.org wrote:
Add support in binman for generating capsules. The capsule parameters can be specified either through a config file or through the capsule binman entry. Also add test cases in binman for capsule generation, and enable this testing on the sandbox_spl variant.
Can you use sandbox instead, or perhaps sandbox_spl? SPL is really for SPL testing.
Er, I am actually using the sandbox_spl variant.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
Changes since V3:
- Add test cases for covering the various capsule generation scenarios.
- Add function comments in the mkeficapsule bintool.
- Fix the fetch method of the mkeficapsule bintool to enable building the tool.
- Add more details about the capsule parameters in the documentation as well as the code.
- Fix order of module imports, and addition of blank lines in the capsule.py file.
- Use SetContents in the ObtainContents method.
configs/sandbox_spl_defconfig | 1 + tools/binman/btool/mkeficapsule.py | 158 ++++++++++++++++++ tools/binman/entries.rst | 37 ++++ tools/binman/etype/capsule.py | 132 +++++++++++++++ tools/binman/ftest.py | 127 ++++++++++++++ tools/binman/test/282_capsule.dts | 18 ++ tools/binman/test/283_capsule_signed.dts | 20 +++ tools/binman/test/284_capsule_conf.dts | 14 ++ tools/binman/test/285_capsule_missing_key.dts | 19 +++ .../binman/test/286_capsule_missing_index.dts | 17 ++ .../binman/test/287_capsule_missing_guid.dts | 17 ++ .../test/288_capsule_missing_payload.dts | 17 ++ tools/binman/test/289_capsule_missing.dts | 17 ++ tools/binman/test/290_capsule_version.dts | 19 +++ tools/binman/test/capsule_cfg.txt | 6 + 15 files changed, 619 insertions(+) create mode 100644 tools/binman/btool/mkeficapsule.py create mode 100644 tools/binman/etype/capsule.py create mode 100644 tools/binman/test/282_capsule.dts create mode 100644 tools/binman/test/283_capsule_signed.dts create mode 100644 tools/binman/test/284_capsule_conf.dts create mode 100644 tools/binman/test/285_capsule_missing_key.dts create mode 100644 tools/binman/test/286_capsule_missing_index.dts create mode 100644 tools/binman/test/287_capsule_missing_guid.dts create mode 100644 tools/binman/test/288_capsule_missing_payload.dts create mode 100644 tools/binman/test/289_capsule_missing.dts create mode 100644 tools/binman/test/290_capsule_version.dts create mode 100644 tools/binman/test/capsule_cfg.txt
This looks pretty good to me. Some nits below
diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig index dd848c57c6..2fcc789347 100644 --- a/configs/sandbox_spl_defconfig +++ b/configs/sandbox_spl_defconfig @@ -248,3 +248,4 @@ CONFIG_UNIT_TEST=y CONFIG_SPL_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_TOOLS_MKEFICAPSULE=y
Why enabling this here? I don't think it is needed in sandbox_spl, but in any case it should be in a different patch if needed.
The binman tests run on the sandbox_spl variant. When running the capsule generation tests, the mkeficapsule tool should be present on the board variant no?
Can we run this on the 'sandbox' variant instead?
The capsule tests can be run on sandbox. But the change in the sandbox_spl_defconfig is for adding support for the capsule tests which are run as part of the binman test suite in CI. So it would be a
The binman tests are run separately, in 'Run binman, buildman, dtoc, Kconfig and patman testsuites' in gitlab.
question of whether the rest of the binman tests in CI can be run on the sandbox variant. Or are there any specific set of tests which need to be run on the sandbox_spl variant?
Just the ones that need SPL. If you type 'make qcheck' you will see most/all of the tests, including the sandbox_spl ones.
[..]
- def ReadNode(self):
super().ReadNode()
self.cfg_file = fdt_util.GetString(self._node, 'cfg-file')
if not self.cfg_file:
self.image_index = fdt_util.GetInt(self._node, 'image-index')
if not self.image_index:
self.Raise('mkeficapsule must be provided an Image Index')
self.image_guid = fdt_util.GetString(self._node, 'image-type-id')
if not self.image_guid:
self.Raise('mkeficapsule must be provided an Image GUID')
Use self.required_props = ['image-type-id', ...] in your __init__(). Then this is automatic
I should have clarified this during the earlier version itself. So these parameters are mandatory only when not using the config file. In the scenario of generating the capsules through config files, all these parameters are provided through the config file. Hence these explicit checks.
Hmm, I think we should consider having two different etypes, then. It seems in fact that your entry type is doing 2-3 different things?
I am wondering if having two different etypes might get confusing for the users.
Maybe, but I'm already confused.
[..]
What if some of the inputs are missing? Does this handle missing blobs?
Any missing input parameters are checked earlier itself.
if self.cfg_file:
return self.mkeficapsule.capsule_cfg_file(self.cfg_file)
elif self.auth:
return self.mkeficapsule.cmdline_auth_capsule(self.image_index,
self.image_guid,
self.hardware_instance,
self.monotonic_count,
self.private_key,
self.pub_key_cert,
self.payload,
self.capsule_fname,
self.fw_version)
else:
return self.mkeficapsule.cmdline_capsule(self.image_index,
self.image_guid,
self.hardware_instance,
self.payload,
self.capsule_fname,
self.fw_version)
Here is where I wonder whether you are putting too much in a single etype. Here there are three different cases. Should we have 3 etypes?
Basically the result in all the three cases is the same -- generation of a capsule file. Just that the input parameters being passed for generation are slightly different. There can be multiple entry types for these, but like I mentioned above, would having multiple entry types not be confusing for the users?
So can we drop the use of a cfg file? What is the difference between the second two? If you had added comments I would not have had to ask.
[..]
We really should not have GUIDs in the code...they are a mess.
You want the UEFI capsule generation to happen through binman, and not mention GUIDs. That ain't happening :)
I just don't want them open-coded. They are meaningless gibberish that no one can understand. Use #define or some other way to give them a name.
Yes, I will move the GUIDS under a variable.
If I wrote:
writel(0x09812374, 0x8723728)
you would have the same comment.
# Image GUID specified in the DTS
self.image_guid = "52cfd7092007104791d108469b7fe9c8"
self.fmp_signature = "4d535331"
self.fmp_size = "10"
self.fmp_fw_version = "02"
These should really be local vars, not members.
Okay
self.capsule_data = tools.read_file(self.capsule_fname)
Pass the data in here and then you don't need to read the file
So the file needs to be read here since the actual capsule generation tool(tools/mkeficapsule) does not return any capsule data. Instead, the data gets written to the capsule file, and the tool just returns a pass/fail status.
Sure, but you can read that data in the caller to this functoin.
Yes, the data can be read in the caller, but that would mean duplicating the read in four functions. Instead the file read is happening in one place where the data is being used.
What do you mean? The data is returned from the call to self._DoReadFile(), the so pattern is:
data = self._DoReadFile(...)
You should be able to see that throughout ftest.py
What exactly is the confusion here?
self.assertEqual(self.capsule_guid, self.capsule_data.hex()[:32])
self.assertEqual(self.image_guid, self.capsule_data.hex()[96:128])
if version_check:
self.assertEqual(self.fmp_signature, self.capsule_data.hex()[184:192])
self.assertEqual(self.fmp_size, self.capsule_data.hex()[192:194])
self.assertEqual(self.fmp_fw_version, self.capsule_data.hex()[200:202])
if signed_capsule:
self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[4770:4778])
Where do these integer offsets come from? Please add a comment
So, these are simply offsets in the output capsule file, which get impacted based on the contents being put in the capsule, like presence/absence of optional headers. I don't think putting a comment really wll add any value, because these offsets are variable.
OK then please add a comment to that effect, as well as how to figure them out when things change.
Okay
elif version_check:
self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[216:224])
else:
self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[184:192])
- def _GenCapsuleCfgPayload(self, fname, contents):
capsule_dir = '/tmp/capsules/'
You can't write to /tmp
Please use self._indir for input files - see how other tests do it
For all other tests, I am indeed using _indir and outdir. But for generation of capsules through a config file, we need to specify the location where the output capsule file will be written to. Which is the reason for the /tmp/capsules/. We are using this directory for collating all capsule related files.
Well, sorry, you can't do that. I think you should provide a relative path, rather than absolute...that should solve the problem
Using absolute paths is only being done in the case of testing capsule generation through config file -- all the rest of the test cases are using relative paths for the input file and the capsule file. For the generation of capsules through the config file, the paths either need to be absolute, or relative to the directory from which the mkeficapsule tool is being invoked. And I believe that the binman test is creating temporary directories at runtime for input and output files.
OK so let's drop the cfg file stuff. It doesn't seem to be needed.
What is the issue you see in using the /tmp/capsules/ path for generation of capsules. I see directories being created with relevant files under /opt for other tests. If you would want the capsules directory to be in some other location, I can change that.
Binman tests should use the input and output directories, so please figure out how to do this. If you drop the cfg file then it should be easy.
[..]
Please can you use your own test data, like EFI_DATA ? Also if you declare it as a binary string you can drop the call.
For example:
EFI_DATA = b'efi'
I don't know why text_data cannot be used, but I will add the EFI_DATA.
Well firstly it is not a 'bytes' string. Secondly you may as well have your own as we have done with other etypes.
Okay
TestFunctional._MakeInputFile('payload.txt', self.payload_data)
self._DoReadFile('282_capsule.dts')
data = self...
Please see above. We need to read the capsule file. This applies for all the related comments about using the data = self._DoReadFile...
That needs to be fixed, since the output file should be the capsule. Why would the output file be anything else??
The output file is indeed a capsule, but that is being generated by the capsule generation tool which gets called from the bintool. So the mkeficapsule tool that the bintool calls results in generation of the capsule file. In that way, this does not map directly to the concept of an image in binman. And I was wondering if I should introduce a new property like 'dummy-image', so that the resultant <image>.bin and <image>.map files are not created -- they really are not needed in the case of capsule generation. Would you be fine with an introduction of such a property?
Eek, no.
I thought the output file from binman (image.bin in most tests) was an EFI capsule. Is that not the case? If not, what exactly is it?
Regards, Simon

hi Simon,
On Thu, 20 Jul 2023 at 00:41, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Wed, 19 Jul 2023 at 02:42, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Simon,
On Wed, 19 Jul 2023 at 06:41, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Mon, 17 Jul 2023 at 04:44, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Simon,
On Sun, 16 Jul 2023 at 05:12, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Sat, 15 Jul 2023 at 07:46, Sughosh Ganu sughosh.ganu@linaro.org wrote:
Add support in binman for generating capsules. The capsule parameters can be specified either through a config file or through the capsule binman entry. Also add test cases in binman for capsule generation, and enable this testing on the sandbox_spl variant.
Can you use sandbox instead, or perhaps sandbox_spl? SPL is really for SPL testing.
Er, I am actually using the sandbox_spl variant.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
Changes since V3:
- Add test cases for covering the various capsule generation scenarios.
- Add function comments in the mkeficapsule bintool.
- Fix the fetch method of the mkeficapsule bintool to enable building the tool.
- Add more details about the capsule parameters in the documentation as well as the code.
- Fix order of module imports, and addition of blank lines in the capsule.py file.
- Use SetContents in the ObtainContents method.
configs/sandbox_spl_defconfig | 1 + tools/binman/btool/mkeficapsule.py | 158 ++++++++++++++++++ tools/binman/entries.rst | 37 ++++ tools/binman/etype/capsule.py | 132 +++++++++++++++ tools/binman/ftest.py | 127 ++++++++++++++ tools/binman/test/282_capsule.dts | 18 ++ tools/binman/test/283_capsule_signed.dts | 20 +++ tools/binman/test/284_capsule_conf.dts | 14 ++ tools/binman/test/285_capsule_missing_key.dts | 19 +++ .../binman/test/286_capsule_missing_index.dts | 17 ++ .../binman/test/287_capsule_missing_guid.dts | 17 ++ .../test/288_capsule_missing_payload.dts | 17 ++ tools/binman/test/289_capsule_missing.dts | 17 ++ tools/binman/test/290_capsule_version.dts | 19 +++ tools/binman/test/capsule_cfg.txt | 6 + 15 files changed, 619 insertions(+) create mode 100644 tools/binman/btool/mkeficapsule.py create mode 100644 tools/binman/etype/capsule.py create mode 100644 tools/binman/test/282_capsule.dts create mode 100644 tools/binman/test/283_capsule_signed.dts create mode 100644 tools/binman/test/284_capsule_conf.dts create mode 100644 tools/binman/test/285_capsule_missing_key.dts create mode 100644 tools/binman/test/286_capsule_missing_index.dts create mode 100644 tools/binman/test/287_capsule_missing_guid.dts create mode 100644 tools/binman/test/288_capsule_missing_payload.dts create mode 100644 tools/binman/test/289_capsule_missing.dts create mode 100644 tools/binman/test/290_capsule_version.dts create mode 100644 tools/binman/test/capsule_cfg.txt
This looks pretty good to me. Some nits below
diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig index dd848c57c6..2fcc789347 100644 --- a/configs/sandbox_spl_defconfig +++ b/configs/sandbox_spl_defconfig @@ -248,3 +248,4 @@ CONFIG_UNIT_TEST=y CONFIG_SPL_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_TOOLS_MKEFICAPSULE=y
Why enabling this here? I don't think it is needed in sandbox_spl, but in any case it should be in a different patch if needed.
The binman tests run on the sandbox_spl variant. When running the capsule generation tests, the mkeficapsule tool should be present on the board variant no?
Can we run this on the 'sandbox' variant instead?
The capsule tests can be run on sandbox. But the change in the sandbox_spl_defconfig is for adding support for the capsule tests which are run as part of the binman test suite in CI. So it would be a
The binman tests are run separately, in 'Run binman, buildman, dtoc, Kconfig and patman testsuites' in gitlab.
Yes, and that uses the sandbox_spl variant build for running the tests. Which is why I added building the mkeficapsule tool for the variant.
question of whether the rest of the binman tests in CI can be run on the sandbox variant. Or are there any specific set of tests which need to be run on the sandbox_spl variant?
Just the ones that need SPL. If you type 'make qcheck' you will see most/all of the tests, including the sandbox_spl ones.
Can we not just build the mkeficapsule tool in the sandbox_spl variant. That keeps things simple.
[..]
- def ReadNode(self):
super().ReadNode()
self.cfg_file = fdt_util.GetString(self._node, 'cfg-file')
if not self.cfg_file:
self.image_index = fdt_util.GetInt(self._node, 'image-index')
if not self.image_index:
self.Raise('mkeficapsule must be provided an Image Index')
self.image_guid = fdt_util.GetString(self._node, 'image-type-id')
if not self.image_guid:
self.Raise('mkeficapsule must be provided an Image GUID')
Use self.required_props = ['image-type-id', ...] in your __init__(). Then this is automatic
I should have clarified this during the earlier version itself. So these parameters are mandatory only when not using the config file. In the scenario of generating the capsules through config files, all these parameters are provided through the config file. Hence these explicit checks.
Hmm, I think we should consider having two different etypes, then. It seems in fact that your entry type is doing 2-3 different things?
I am wondering if having two different etypes might get confusing for the users.
Maybe, but I'm already confused.
[..]
What if some of the inputs are missing? Does this handle missing blobs?
Any missing input parameters are checked earlier itself.
if self.cfg_file:
return self.mkeficapsule.capsule_cfg_file(self.cfg_file)
elif self.auth:
return self.mkeficapsule.cmdline_auth_capsule(self.image_index,
self.image_guid,
self.hardware_instance,
self.monotonic_count,
self.private_key,
self.pub_key_cert,
self.payload,
self.capsule_fname,
self.fw_version)
else:
return self.mkeficapsule.cmdline_capsule(self.image_index,
self.image_guid,
self.hardware_instance,
self.payload,
self.capsule_fname,
self.fw_version)
Here is where I wonder whether you are putting too much in a single etype. Here there are three different cases. Should we have 3 etypes?
Basically the result in all the three cases is the same -- generation of a capsule file. Just that the input parameters being passed for generation are slightly different. There can be multiple entry types for these, but like I mentioned above, would having multiple entry types not be confusing for the users?
So can we drop the use of a cfg file? What is the difference between the second two? If you had added comments I would not have had to ask.
Using a config file to specify all the parameters needed for building capsule(s) is a good feature to have. It also results in generation of all the needed capsules through a single invocation of the mkeficapsule command, instead of multiple calls per capsule file.
[..]
We really should not have GUIDs in the code...they are a mess.
You want the UEFI capsule generation to happen through binman, and not mention GUIDs. That ain't happening :)
I just don't want them open-coded. They are meaningless gibberish that no one can understand. Use #define or some other way to give them a name.
Yes, I will move the GUIDS under a variable.
If I wrote:
writel(0x09812374, 0x8723728)
you would have the same comment.
# Image GUID specified in the DTS
self.image_guid = "52cfd7092007104791d108469b7fe9c8"
self.fmp_signature = "4d535331"
self.fmp_size = "10"
self.fmp_fw_version = "02"
These should really be local vars, not members.
Okay
self.capsule_data = tools.read_file(self.capsule_fname)
Pass the data in here and then you don't need to read the file
So the file needs to be read here since the actual capsule generation tool(tools/mkeficapsule) does not return any capsule data. Instead, the data gets written to the capsule file, and the tool just returns a pass/fail status.
Sure, but you can read that data in the caller to this functoin.
Yes, the data can be read in the caller, but that would mean duplicating the read in four functions. Instead the file read is happening in one place where the data is being used.
What do you mean? The data is returned from the call to self._DoReadFile(), the so pattern is:
data = self._DoReadFile(...)
You should be able to see that throughout ftest.py
What exactly is the confusion here?
No confusion. Again, this does not work when generating capsules through a config file.
self.assertEqual(self.capsule_guid, self.capsule_data.hex()[:32])
self.assertEqual(self.image_guid, self.capsule_data.hex()[96:128])
if version_check:
self.assertEqual(self.fmp_signature, self.capsule_data.hex()[184:192])
self.assertEqual(self.fmp_size, self.capsule_data.hex()[192:194])
self.assertEqual(self.fmp_fw_version, self.capsule_data.hex()[200:202])
if signed_capsule:
self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[4770:4778])
Where do these integer offsets come from? Please add a comment
So, these are simply offsets in the output capsule file, which get impacted based on the contents being put in the capsule, like presence/absence of optional headers. I don't think putting a comment really wll add any value, because these offsets are variable.
OK then please add a comment to that effect, as well as how to figure them out when things change.
Okay
elif version_check:
self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[216:224])
else:
self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[184:192])
- def _GenCapsuleCfgPayload(self, fname, contents):
capsule_dir = '/tmp/capsules/'
You can't write to /tmp
Please use self._indir for input files - see how other tests do it
For all other tests, I am indeed using _indir and outdir. But for generation of capsules through a config file, we need to specify the location where the output capsule file will be written to. Which is the reason for the /tmp/capsules/. We are using this directory for collating all capsule related files.
Well, sorry, you can't do that. I think you should provide a relative path, rather than absolute...that should solve the problem
Using absolute paths is only being done in the case of testing capsule generation through config file -- all the rest of the test cases are using relative paths for the input file and the capsule file. For the generation of capsules through the config file, the paths either need to be absolute, or relative to the directory from which the mkeficapsule tool is being invoked. And I believe that the binman test is creating temporary directories at runtime for input and output files.
OK so let's drop the cfg file stuff. It doesn't seem to be needed.
Instead of dropping support for generating capsules through a cfg file in binman, I would say drop test coverage in binman for that case. We can test this feature, that is not an issue, but you insist on using the binman input and output directories which get created at runtime. We cannot test the capsule generation with a cfg file with that restriction.
What is the issue you see in using the /tmp/capsules/ path for generation of capsules. I see directories being created with relevant files under /opt for other tests. If you would want the capsules directory to be in some other location, I can change that.
Binman tests should use the input and output directories, so please figure out how to do this. If you drop the cfg file then it should be easy.
If I could do it, I would not spend this time discussing with you. Like I said above, if you insist on not using a known location for the input and output files, maybe we can drop the test for cfg file -- this functionality does get tested as part of the capsule update feature testing. I would argue that generating capsules using a cfg file is beneficial and be retained.
[..]
Please can you use your own test data, like EFI_DATA ? Also if you declare it as a binary string you can drop the call.
For example:
EFI_DATA = b'efi'
I don't know why text_data cannot be used, but I will add the EFI_DATA.
Well firstly it is not a 'bytes' string. Secondly you may as well have your own as we have done with other etypes.
Okay
TestFunctional._MakeInputFile('payload.txt', self.payload_data)
self._DoReadFile('282_capsule.dts')
data = self...
Please see above. We need to read the capsule file. This applies for all the related comments about using the data = self._DoReadFile...
That needs to be fixed, since the output file should be the capsule. Why would the output file be anything else??
The output file is indeed a capsule, but that is being generated by the capsule generation tool which gets called from the bintool. So the mkeficapsule tool that the bintool calls results in generation of the capsule file. In that way, this does not map directly to the concept of an image in binman. And I was wondering if I should introduce a new property like 'dummy-image', so that the resultant <image>.bin and <image>.map files are not created -- they really are not needed in the case of capsule generation. Would you be fine with an introduction of such a property?
Eek, no.
I thought the output file from binman (image.bin in most tests) was an EFI capsule. Is that not the case? If not, what exactly is it?
In this case, it is not so. Which is why I proposed the above property. What happens is that the bintool calls the actual capsule generation tool, and that tool writes the capsule contents to a file which is provided to the tool as a parameter. Binman does not have to create the image.bin file in this case, since the mkeficapsule tool has already created the capsule file -- creating the image.bin and image.map is superfluous here.
-sughosh

Hi Sughosh,
On Thu, 20 Jul 2023 at 03:20, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Simon,
On Thu, 20 Jul 2023 at 00:41, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Wed, 19 Jul 2023 at 02:42, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Simon,
On Wed, 19 Jul 2023 at 06:41, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Mon, 17 Jul 2023 at 04:44, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Simon,
On Sun, 16 Jul 2023 at 05:12, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Sat, 15 Jul 2023 at 07:46, Sughosh Ganu sughosh.ganu@linaro.org wrote: > > Add support in binman for generating capsules. The capsule parameters > can be specified either through a config file or through the capsule > binman entry. Also add test cases in binman for capsule generation, > and enable this testing on the sandbox_spl variant.
Can you use sandbox instead, or perhaps sandbox_spl? SPL is really for SPL testing.
Er, I am actually using the sandbox_spl variant.
> > Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org > --- > Changes since V3: > * Add test cases for covering the various capsule generation > scenarios. > * Add function comments in the mkeficapsule bintool. > * Fix the fetch method of the mkeficapsule bintool to enable building > the tool. > * Add more details about the capsule parameters in the documentation > as well as the code. > * Fix order of module imports, and addition of blank lines in the > capsule.py file. > * Use SetContents in the ObtainContents method. > > configs/sandbox_spl_defconfig | 1 + > tools/binman/btool/mkeficapsule.py | 158 ++++++++++++++++++ > tools/binman/entries.rst | 37 ++++ > tools/binman/etype/capsule.py | 132 +++++++++++++++ > tools/binman/ftest.py | 127 ++++++++++++++ > tools/binman/test/282_capsule.dts | 18 ++ > tools/binman/test/283_capsule_signed.dts | 20 +++ > tools/binman/test/284_capsule_conf.dts | 14 ++ > tools/binman/test/285_capsule_missing_key.dts | 19 +++ > .../binman/test/286_capsule_missing_index.dts | 17 ++ > .../binman/test/287_capsule_missing_guid.dts | 17 ++ > .../test/288_capsule_missing_payload.dts | 17 ++ > tools/binman/test/289_capsule_missing.dts | 17 ++ > tools/binman/test/290_capsule_version.dts | 19 +++ > tools/binman/test/capsule_cfg.txt | 6 + > 15 files changed, 619 insertions(+) > create mode 100644 tools/binman/btool/mkeficapsule.py > create mode 100644 tools/binman/etype/capsule.py > create mode 100644 tools/binman/test/282_capsule.dts > create mode 100644 tools/binman/test/283_capsule_signed.dts > create mode 100644 tools/binman/test/284_capsule_conf.dts > create mode 100644 tools/binman/test/285_capsule_missing_key.dts > create mode 100644 tools/binman/test/286_capsule_missing_index.dts > create mode 100644 tools/binman/test/287_capsule_missing_guid.dts > create mode 100644 tools/binman/test/288_capsule_missing_payload.dts > create mode 100644 tools/binman/test/289_capsule_missing.dts > create mode 100644 tools/binman/test/290_capsule_version.dts > create mode 100644 tools/binman/test/capsule_cfg.txt
This looks pretty good to me. Some nits below
> > diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig > index dd848c57c6..2fcc789347 100644 > --- a/configs/sandbox_spl_defconfig > +++ b/configs/sandbox_spl_defconfig > @@ -248,3 +248,4 @@ CONFIG_UNIT_TEST=y > CONFIG_SPL_UNIT_TEST=y > CONFIG_UT_TIME=y > CONFIG_UT_DM=y > +CONFIG_TOOLS_MKEFICAPSULE=y
Why enabling this here? I don't think it is needed in sandbox_spl, but in any case it should be in a different patch if needed.
The binman tests run on the sandbox_spl variant. When running the capsule generation tests, the mkeficapsule tool should be present on the board variant no?
Can we run this on the 'sandbox' variant instead?
The capsule tests can be run on sandbox. But the change in the sandbox_spl_defconfig is for adding support for the capsule tests which are run as part of the binman test suite in CI. So it would be a
The binman tests are run separately, in 'Run binman, buildman, dtoc, Kconfig and patman testsuites' in gitlab.
Yes, and that uses the sandbox_spl variant build for running the tests. Which is why I added building the mkeficapsule tool for the variant.
Not really. It uses sandbox_spl to obtain the pylibfdt library, that's all.
The tests should use sandbox. By enabling CONFIG_BINMAN you will get pylibfdt anyway.
question of whether the rest of the binman tests in CI can be run on the sandbox variant. Or are there any specific set of tests which need to be run on the sandbox_spl variant?
Just the ones that need SPL. If you type 'make qcheck' you will see most/all of the tests, including the sandbox_spl ones.
Can we not just build the mkeficapsule tool in the sandbox_spl variant. That keeps things simple.
Why is it simple? It is actually a pain, because the vast majority of tests only need sandbox. We use sandbox_spl for tests that do something in SPL and need that phase of U-Boot to perform some sort of test. So far as I can tell, the EFI capsule thing works entirely in U-Boot proper.
[..]
> + > + def ReadNode(self): > + super().ReadNode() > + > + self.cfg_file = fdt_util.GetString(self._node, 'cfg-file') > + if not self.cfg_file: > + self.image_index = fdt_util.GetInt(self._node, 'image-index') > + if not self.image_index: > + self.Raise('mkeficapsule must be provided an Image Index') > + > + self.image_guid = fdt_util.GetString(self._node, 'image-type-id') > + if not self.image_guid: > + self.Raise('mkeficapsule must be provided an Image GUID')
Use self.required_props = ['image-type-id', ...] in your __init__(). Then this is automatic
I should have clarified this during the earlier version itself. So these parameters are mandatory only when not using the config file. In the scenario of generating the capsules through config files, all these parameters are provided through the config file. Hence these explicit checks.
Hmm, I think we should consider having two different etypes, then. It seems in fact that your entry type is doing 2-3 different things?
I am wondering if having two different etypes might get confusing for the users.
Maybe, but I'm already confused.
[..]
What if some of the inputs are missing? Does this handle missing blobs?
Any missing input parameters are checked earlier itself.
> + if self.cfg_file: > + return self.mkeficapsule.capsule_cfg_file(self.cfg_file) > + elif self.auth: > + return self.mkeficapsule.cmdline_auth_capsule(self.image_index, > + self.image_guid, > + self.hardware_instance, > + self.monotonic_count, > + self.private_key, > + self.pub_key_cert, > + self.payload, > + self.capsule_fname, > + self.fw_version) > + else: > + return self.mkeficapsule.cmdline_capsule(self.image_index, > + self.image_guid, > + self.hardware_instance, > + self.payload, > + self.capsule_fname, > + self.fw_version)
Here is where I wonder whether you are putting too much in a single etype. Here there are three different cases. Should we have 3 etypes?
Basically the result in all the three cases is the same -- generation of a capsule file. Just that the input parameters being passed for generation are slightly different. There can be multiple entry types for these, but like I mentioned above, would having multiple entry types not be confusing for the users?
So can we drop the use of a cfg file? What is the difference between the second two? If you had added comments I would not have had to ask.
Using a config file to specify all the parameters needed for building capsule(s) is a good feature to have. It also results in generation of all the needed capsules through a single invocation of the mkeficapsule command, instead of multiple calls per capsule file.
Does that matter? I don't really have an objection to it, except in that it makes the entry type confusing. Do we need to support this mode of operation in binman? Perhaps EFI capsule can do what ever other enrty type does and specify the params in the Binman node? You can still use the config file for other uses, but I don't see any use for it in binman.
If you want multiple capsules, wouldn't that be done with multiple capsule nodes in the image definition?
[..]
We really should not have GUIDs in the code...they are a mess.
You want the UEFI capsule generation to happen through binman, and not mention GUIDs. That ain't happening :)
I just don't want them open-coded. They are meaningless gibberish that no one can understand. Use #define or some other way to give them a name.
Yes, I will move the GUIDS under a variable.
If I wrote:
writel(0x09812374, 0x8723728)
you would have the same comment.
> + # Image GUID specified in the DTS > + self.image_guid = "52cfd7092007104791d108469b7fe9c8" > + self.fmp_signature = "4d535331" > + self.fmp_size = "10" > + self.fmp_fw_version = "02"
These should really be local vars, not members.
Okay
> + > + self.capsule_data = tools.read_file(self.capsule_fname)
Pass the data in here and then you don't need to read the file
So the file needs to be read here since the actual capsule generation tool(tools/mkeficapsule) does not return any capsule data. Instead, the data gets written to the capsule file, and the tool just returns a pass/fail status.
Sure, but you can read that data in the caller to this functoin.
Yes, the data can be read in the caller, but that would mean duplicating the read in four functions. Instead the file read is happening in one place where the data is being used.
What do you mean? The data is returned from the call to self._DoReadFile(), the so pattern is:
data = self._DoReadFile(...)
You should be able to see that throughout ftest.py
What exactly is the confusion here?
No confusion. Again, this does not work when generating capsules through a config file.
OK let's drop the config file from binman. We don't need it and I'm tired of the back and forth.
> + > + self.assertEqual(self.capsule_guid, self.capsule_data.hex()[:32]) > + self.assertEqual(self.image_guid, self.capsule_data.hex()[96:128]) > + > + if version_check: > + self.assertEqual(self.fmp_signature, self.capsule_data.hex()[184:192]) > + self.assertEqual(self.fmp_size, self.capsule_data.hex()[192:194]) > + self.assertEqual(self.fmp_fw_version, self.capsule_data.hex()[200:202]) > + > + if signed_capsule: > + self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[4770:4778])
Where do these integer offsets come from? Please add a comment
So, these are simply offsets in the output capsule file, which get impacted based on the contents being put in the capsule, like presence/absence of optional headers. I don't think putting a comment really wll add any value, because these offsets are variable.
OK then please add a comment to that effect, as well as how to figure them out when things change.
Okay
> + elif version_check: > + self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[216:224]) > + else: > + self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[184:192]) > + > + def _GenCapsuleCfgPayload(self, fname, contents): > + capsule_dir = '/tmp/capsules/'
You can't write to /tmp
Please use self._indir for input files - see how other tests do it
For all other tests, I am indeed using _indir and outdir. But for generation of capsules through a config file, we need to specify the location where the output capsule file will be written to. Which is the reason for the /tmp/capsules/. We are using this directory for collating all capsule related files.
Well, sorry, you can't do that. I think you should provide a relative path, rather than absolute...that should solve the problem
Using absolute paths is only being done in the case of testing capsule generation through config file -- all the rest of the test cases are using relative paths for the input file and the capsule file. For the generation of capsules through the config file, the paths either need to be absolute, or relative to the directory from which the mkeficapsule tool is being invoked. And I believe that the binman test is creating temporary directories at runtime for input and output files.
OK so let's drop the cfg file stuff. It doesn't seem to be needed.
Instead of dropping support for generating capsules through a cfg file in binman, I would say drop test coverage in binman for that case. We can test this feature, that is not an issue, but you insist on using the binman input and output directories which get created at runtime. We cannot test the capsule generation with a cfg file with that restriction.
No.
What is the issue you see in using the /tmp/capsules/ path for generation of capsules. I see directories being created with relevant files under /opt for other tests. If you would want the capsules directory to be in some other location, I can change that.
Binman tests should use the input and output directories, so please figure out how to do this. If you drop the cfg file then it should be easy.
If I could do it, I would not spend this time discussing with you. Like I said above, if you insist on not using a known location for the input and output files, maybe we can drop the test for cfg file -- this functionality does get tested as part of the capsule update feature testing. I would argue that generating capsules using a cfg file is beneficial and be retained.
As above.
[..]
Please can you use your own test data, like EFI_DATA ? Also if you declare it as a binary string you can drop the call.
For example:
EFI_DATA = b'efi'
I don't know why text_data cannot be used, but I will add the EFI_DATA.
Well firstly it is not a 'bytes' string. Secondly you may as well have your own as we have done with other etypes.
Okay
> + > + TestFunctional._MakeInputFile('payload.txt', self.payload_data) > + > + self._DoReadFile('282_capsule.dts')
data = self...
Please see above. We need to read the capsule file. This applies for all the related comments about using the data = self._DoReadFile...
That needs to be fixed, since the output file should be the capsule. Why would the output file be anything else??
The output file is indeed a capsule, but that is being generated by the capsule generation tool which gets called from the bintool. So the mkeficapsule tool that the bintool calls results in generation of the capsule file. In that way, this does not map directly to the concept of an image in binman. And I was wondering if I should introduce a new property like 'dummy-image', so that the resultant <image>.bin and <image>.map files are not created -- they really are not needed in the case of capsule generation. Would you be fine with an introduction of such a property?
Eek, no.
I thought the output file from binman (image.bin in most tests) was an EFI capsule. Is that not the case? If not, what exactly is it?
In this case, it is not so. Which is why I proposed the above property. What happens is that the bintool calls the actual capsule generation tool, and that tool writes the capsule contents to a file which is provided to the tool as a parameter. Binman does not have to create the image.bin file in this case, since the mkeficapsule tool has already created the capsule file -- creating the image.bin and image.map is superfluous here.
I think you are missing something here. The tools that binman uses are there to generate things that the binman description wants. The EFI capsule is just another one of those.
We should rename 'capsule' to 'efi-capsule', I think, since there might be other types of capsule.
In theory I could say:
binman { u-boot-spl { }; efi-capsule { // things we want in the capsule u-boot { } }
and it should produce an image with SPL at the start followed by the capsule. The capsule is allowed to generate data, not create the whole image. Perhaps that is the blocker here?
I see now that your capsule entry type is wonky. It needs to accept properties (not filenames!) and use those to package up the contents, which should be subnodes of itself.
Probably you should look at how mkimage does it...but you essentially call self.collect_contents_to_file() to pick up the data. Your capsule should probably subclass entry_section, rather than Entry, since entry_Section handles some other things for you, like missing blobs.
Looking at your last patch I think this is the confusion, and why this has been so hard for me to wrap my head around.
Regards, Simon

hi Simon,
On Wed, 26 Jul 2023 at 04:06, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Thu, 20 Jul 2023 at 03:20, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Simon,
On Thu, 20 Jul 2023 at 00:41, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Wed, 19 Jul 2023 at 02:42, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Simon,
On Wed, 19 Jul 2023 at 06:41, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Mon, 17 Jul 2023 at 04:44, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Simon,
On Sun, 16 Jul 2023 at 05:12, Simon Glass sjg@chromium.org wrote: > > Hi Sughosh, > > On Sat, 15 Jul 2023 at 07:46, Sughosh Ganu sughosh.ganu@linaro.org wrote: > > > > Add support in binman for generating capsules. The capsule parameters > > can be specified either through a config file or through the capsule > > binman entry. Also add test cases in binman for capsule generation, > > and enable this testing on the sandbox_spl variant. > > Can you use sandbox instead, or perhaps sandbox_spl? SPL is really for > SPL testing.
Er, I am actually using the sandbox_spl variant.
> > > > > Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org > > --- > > Changes since V3: > > * Add test cases for covering the various capsule generation > > scenarios. > > * Add function comments in the mkeficapsule bintool. > > * Fix the fetch method of the mkeficapsule bintool to enable building > > the tool. > > * Add more details about the capsule parameters in the documentation > > as well as the code. > > * Fix order of module imports, and addition of blank lines in the > > capsule.py file. > > * Use SetContents in the ObtainContents method. > > > > configs/sandbox_spl_defconfig | 1 + > > tools/binman/btool/mkeficapsule.py | 158 ++++++++++++++++++ > > tools/binman/entries.rst | 37 ++++ > > tools/binman/etype/capsule.py | 132 +++++++++++++++ > > tools/binman/ftest.py | 127 ++++++++++++++ > > tools/binman/test/282_capsule.dts | 18 ++ > > tools/binman/test/283_capsule_signed.dts | 20 +++ > > tools/binman/test/284_capsule_conf.dts | 14 ++ > > tools/binman/test/285_capsule_missing_key.dts | 19 +++ > > .../binman/test/286_capsule_missing_index.dts | 17 ++ > > .../binman/test/287_capsule_missing_guid.dts | 17 ++ > > .../test/288_capsule_missing_payload.dts | 17 ++ > > tools/binman/test/289_capsule_missing.dts | 17 ++ > > tools/binman/test/290_capsule_version.dts | 19 +++ > > tools/binman/test/capsule_cfg.txt | 6 + > > 15 files changed, 619 insertions(+) > > create mode 100644 tools/binman/btool/mkeficapsule.py > > create mode 100644 tools/binman/etype/capsule.py > > create mode 100644 tools/binman/test/282_capsule.dts > > create mode 100644 tools/binman/test/283_capsule_signed.dts > > create mode 100644 tools/binman/test/284_capsule_conf.dts > > create mode 100644 tools/binman/test/285_capsule_missing_key.dts > > create mode 100644 tools/binman/test/286_capsule_missing_index.dts > > create mode 100644 tools/binman/test/287_capsule_missing_guid.dts > > create mode 100644 tools/binman/test/288_capsule_missing_payload.dts > > create mode 100644 tools/binman/test/289_capsule_missing.dts > > create mode 100644 tools/binman/test/290_capsule_version.dts > > create mode 100644 tools/binman/test/capsule_cfg.txt > > This looks pretty good to me. Some nits below > > > > > diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig > > index dd848c57c6..2fcc789347 100644 > > --- a/configs/sandbox_spl_defconfig > > +++ b/configs/sandbox_spl_defconfig > > @@ -248,3 +248,4 @@ CONFIG_UNIT_TEST=y > > CONFIG_SPL_UNIT_TEST=y > > CONFIG_UT_TIME=y > > CONFIG_UT_DM=y > > +CONFIG_TOOLS_MKEFICAPSULE=y > > Why enabling this here? I don't think it is needed in sandbox_spl, but > in any case it should be in a different patch if needed.
The binman tests run on the sandbox_spl variant. When running the capsule generation tests, the mkeficapsule tool should be present on the board variant no?
Can we run this on the 'sandbox' variant instead?
The capsule tests can be run on sandbox. But the change in the sandbox_spl_defconfig is for adding support for the capsule tests which are run as part of the binman test suite in CI. So it would be a
The binman tests are run separately, in 'Run binman, buildman, dtoc, Kconfig and patman testsuites' in gitlab.
Yes, and that uses the sandbox_spl variant build for running the tests. Which is why I added building the mkeficapsule tool for the variant.
Not really. It uses sandbox_spl to obtain the pylibfdt library, that's all.
The tests should use sandbox. By enabling CONFIG_BINMAN you will get pylibfdt anyway.
I will check on this point.
question of whether the rest of the binman tests in CI can be run on the sandbox variant. Or are there any specific set of tests which need to be run on the sandbox_spl variant?
Just the ones that need SPL. If you type 'make qcheck' you will see most/all of the tests, including the sandbox_spl ones.
Can we not just build the mkeficapsule tool in the sandbox_spl variant. That keeps things simple.
Why is it simple? It is actually a pain, because the vast majority of tests only need sandbox. We use sandbox_spl for tests that do something in SPL and need that phase of U-Boot to perform some sort of test. So far as I can tell, the EFI capsule thing works entirely in U-Boot proper.
[..]
> > > + > > + def ReadNode(self): > > + super().ReadNode() > > + > > + self.cfg_file = fdt_util.GetString(self._node, 'cfg-file') > > + if not self.cfg_file: > > + self.image_index = fdt_util.GetInt(self._node, 'image-index') > > + if not self.image_index: > > + self.Raise('mkeficapsule must be provided an Image Index') > > + > > + self.image_guid = fdt_util.GetString(self._node, 'image-type-id') > > + if not self.image_guid: > > + self.Raise('mkeficapsule must be provided an Image GUID') > > Use self.required_props = ['image-type-id', ...] in your __init__(). > Then this is automatic
I should have clarified this during the earlier version itself. So these parameters are mandatory only when not using the config file. In the scenario of generating the capsules through config files, all these parameters are provided through the config file. Hence these explicit checks.
Hmm, I think we should consider having two different etypes, then. It seems in fact that your entry type is doing 2-3 different things?
I am wondering if having two different etypes might get confusing for the users.
Maybe, but I'm already confused.
[..]
> What if some of the inputs are missing? Does this handle missing blobs?
Any missing input parameters are checked earlier itself.
> > + if self.cfg_file: > > + return self.mkeficapsule.capsule_cfg_file(self.cfg_file) > > + elif self.auth: > > + return self.mkeficapsule.cmdline_auth_capsule(self.image_index, > > + self.image_guid, > > + self.hardware_instance, > > + self.monotonic_count, > > + self.private_key, > > + self.pub_key_cert, > > + self.payload, > > + self.capsule_fname, > > + self.fw_version) > > + else: > > + return self.mkeficapsule.cmdline_capsule(self.image_index, > > + self.image_guid, > > + self.hardware_instance, > > + self.payload, > > + self.capsule_fname, > > + self.fw_version)
Here is where I wonder whether you are putting too much in a single etype. Here there are three different cases. Should we have 3 etypes?
Basically the result in all the three cases is the same -- generation of a capsule file. Just that the input parameters being passed for generation are slightly different. There can be multiple entry types for these, but like I mentioned above, would having multiple entry types not be confusing for the users?
So can we drop the use of a cfg file? What is the difference between the second two? If you had added comments I would not have had to ask.
Using a config file to specify all the parameters needed for building capsule(s) is a good feature to have. It also results in generation of all the needed capsules through a single invocation of the mkeficapsule command, instead of multiple calls per capsule file.
Does that matter? I don't really have an objection to it, except in that it makes the entry type confusing. Do we need to support this mode of operation in binman? Perhaps EFI capsule can do what ever other enrty type does and specify the params in the Binman node? You can still use the config file for other uses, but I don't see any use for it in binman.
If you want multiple capsules, wouldn't that be done with multiple capsule nodes in the image definition?
[..]
> > We really should not have GUIDs in the code...they are a mess.
You want the UEFI capsule generation to happen through binman, and not mention GUIDs. That ain't happening :)
I just don't want them open-coded. They are meaningless gibberish that no one can understand. Use #define or some other way to give them a name.
Yes, I will move the GUIDS under a variable.
If I wrote:
writel(0x09812374, 0x8723728)
you would have the same comment.
> > > + # Image GUID specified in the DTS > > + self.image_guid = "52cfd7092007104791d108469b7fe9c8" > > + self.fmp_signature = "4d535331" > > + self.fmp_size = "10" > > + self.fmp_fw_version = "02" > > These should really be local vars, not members.
Okay
> > > + > > + self.capsule_data = tools.read_file(self.capsule_fname) > > Pass the data in here and then you don't need to read the file
So the file needs to be read here since the actual capsule generation tool(tools/mkeficapsule) does not return any capsule data. Instead, the data gets written to the capsule file, and the tool just returns a pass/fail status.
Sure, but you can read that data in the caller to this functoin.
Yes, the data can be read in the caller, but that would mean duplicating the read in four functions. Instead the file read is happening in one place where the data is being used.
What do you mean? The data is returned from the call to self._DoReadFile(), the so pattern is:
data = self._DoReadFile(...)
You should be able to see that throughout ftest.py
What exactly is the confusion here?
No confusion. Again, this does not work when generating capsules through a config file.
OK let's drop the config file from binman. We don't need it and I'm tired of the back and forth.
Sorry about that, but like I mentioned in my other reply, we would be needing support for generating capsules through the config file. Can we not work around the testing in binman for this feature. Like I mentioned earlier, this will be getting tested as part of the larger efi capsule update feature testing.
> > > + > > + self.assertEqual(self.capsule_guid, self.capsule_data.hex()[:32]) > > + self.assertEqual(self.image_guid, self.capsule_data.hex()[96:128]) > > + > > + if version_check: > > + self.assertEqual(self.fmp_signature, self.capsule_data.hex()[184:192]) > > + self.assertEqual(self.fmp_size, self.capsule_data.hex()[192:194]) > > + self.assertEqual(self.fmp_fw_version, self.capsule_data.hex()[200:202]) > > + > > + if signed_capsule: > > + self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[4770:4778]) > > Where do these integer offsets come from? Please add a comment
So, these are simply offsets in the output capsule file, which get impacted based on the contents being put in the capsule, like presence/absence of optional headers. I don't think putting a comment really wll add any value, because these offsets are variable.
OK then please add a comment to that effect, as well as how to figure them out when things change.
Okay
> > > + elif version_check: > > + self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[216:224]) > > + else: > > + self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[184:192]) > > + > > + def _GenCapsuleCfgPayload(self, fname, contents): > > + capsule_dir = '/tmp/capsules/' > > You can't write to /tmp > > Please use self._indir for input files - see how other tests do it
For all other tests, I am indeed using _indir and outdir. But for generation of capsules through a config file, we need to specify the location where the output capsule file will be written to. Which is the reason for the /tmp/capsules/. We are using this directory for collating all capsule related files.
Well, sorry, you can't do that. I think you should provide a relative path, rather than absolute...that should solve the problem
Using absolute paths is only being done in the case of testing capsule generation through config file -- all the rest of the test cases are using relative paths for the input file and the capsule file. For the generation of capsules through the config file, the paths either need to be absolute, or relative to the directory from which the mkeficapsule tool is being invoked. And I believe that the binman test is creating temporary directories at runtime for input and output files.
OK so let's drop the cfg file stuff. It doesn't seem to be needed.
Instead of dropping support for generating capsules through a cfg file in binman, I would say drop test coverage in binman for that case. We can test this feature, that is not an issue, but you insist on using the binman input and output directories which get created at runtime. We cannot test the capsule generation with a cfg file with that restriction.
No.
What is the issue you see in using the /tmp/capsules/ path for generation of capsules. I see directories being created with relevant files under /opt for other tests. If you would want the capsules directory to be in some other location, I can change that.
Binman tests should use the input and output directories, so please figure out how to do this. If you drop the cfg file then it should be easy.
If I could do it, I would not spend this time discussing with you. Like I said above, if you insist on not using a known location for the input and output files, maybe we can drop the test for cfg file -- this functionality does get tested as part of the capsule update feature testing. I would argue that generating capsules using a cfg file is beneficial and be retained.
As above.
[..]
> > Please can you use your own test data, like EFI_DATA ? Also if you > declare it as a binary string you can drop the call. > > For example: > > EFI_DATA = b'efi'
I don't know why text_data cannot be used, but I will add the EFI_DATA.
Well firstly it is not a 'bytes' string. Secondly you may as well have your own as we have done with other etypes.
Okay
> > > + > > + TestFunctional._MakeInputFile('payload.txt', self.payload_data) > > + > > + self._DoReadFile('282_capsule.dts') > > data = self...
Please see above. We need to read the capsule file. This applies for all the related comments about using the data = self._DoReadFile...
That needs to be fixed, since the output file should be the capsule. Why would the output file be anything else??
The output file is indeed a capsule, but that is being generated by the capsule generation tool which gets called from the bintool. So the mkeficapsule tool that the bintool calls results in generation of the capsule file. In that way, this does not map directly to the concept of an image in binman. And I was wondering if I should introduce a new property like 'dummy-image', so that the resultant <image>.bin and <image>.map files are not created -- they really are not needed in the case of capsule generation. Would you be fine with an introduction of such a property?
Eek, no.
I thought the output file from binman (image.bin in most tests) was an EFI capsule. Is that not the case? If not, what exactly is it?
In this case, it is not so. Which is why I proposed the above property. What happens is that the bintool calls the actual capsule generation tool, and that tool writes the capsule contents to a file which is provided to the tool as a parameter. Binman does not have to create the image.bin file in this case, since the mkeficapsule tool has already created the capsule file -- creating the image.bin and image.map is superfluous here.
I think you are missing something here. The tools that binman uses are there to generate things that the binman description wants. The EFI capsule is just another one of those.
We should rename 'capsule' to 'efi-capsule', I think, since there might be other types of capsule.
In theory I could say:
binman { u-boot-spl { }; efi-capsule { // things we want in the capsule u-boot { } }
and it should produce an image with SPL at the start followed by the capsule. The capsule is allowed to generate data, not create the whole image. Perhaps that is the blocker here?
I see now that your capsule entry type is wonky. It needs to accept properties (not filenames!) and use those to package up the contents, which should be subnodes of itself.
Okay. I will take a look at how mkimage is called. I had checked the intel_ifwi entry type for reference, and I did see use of the filename property there.
Probably you should look at how mkimage does it...but you essentially call self.collect_contents_to_file() to pick up the data. Your capsule should probably subclass entry_section, rather than Entry, since entry_Section handles some other things for you, like missing blobs.
Okay, I will check.
-sughosh
Looking at your last patch I think this is the confusion, and why this has been so hard for me to wrap my head around.
Regards, Simon

Hi Sughosh,
On Wed, 26 Jul 2023 at 03:36, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Simon,
On Wed, 26 Jul 2023 at 04:06, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Thu, 20 Jul 2023 at 03:20, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Simon,
On Thu, 20 Jul 2023 at 00:41, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Wed, 19 Jul 2023 at 02:42, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Simon,
On Wed, 19 Jul 2023 at 06:41, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Mon, 17 Jul 2023 at 04:44, Sughosh Ganu sughosh.ganu@linaro.org wrote: > > hi Simon, > > On Sun, 16 Jul 2023 at 05:12, Simon Glass sjg@chromium.org wrote: > > > > Hi Sughosh, > > > > On Sat, 15 Jul 2023 at 07:46, Sughosh Ganu sughosh.ganu@linaro.org wrote: > > > > > > Add support in binman for generating capsules. The capsule parameters > > > can be specified either through a config file or through the capsule > > > binman entry. Also add test cases in binman for capsule generation, > > > and enable this testing on the sandbox_spl variant. > > > > Can you use sandbox instead, or perhaps sandbox_spl? SPL is really for > > SPL testing. > > Er, I am actually using the sandbox_spl variant. > > > > > > > > > Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org > > > --- > > > Changes since V3: > > > * Add test cases for covering the various capsule generation > > > scenarios. > > > * Add function comments in the mkeficapsule bintool. > > > * Fix the fetch method of the mkeficapsule bintool to enable building > > > the tool. > > > * Add more details about the capsule parameters in the documentation > > > as well as the code. > > > * Fix order of module imports, and addition of blank lines in the > > > capsule.py file. > > > * Use SetContents in the ObtainContents method. > > > > > > configs/sandbox_spl_defconfig | 1 + > > > tools/binman/btool/mkeficapsule.py | 158 ++++++++++++++++++ > > > tools/binman/entries.rst | 37 ++++ > > > tools/binman/etype/capsule.py | 132 +++++++++++++++ > > > tools/binman/ftest.py | 127 ++++++++++++++ > > > tools/binman/test/282_capsule.dts | 18 ++ > > > tools/binman/test/283_capsule_signed.dts | 20 +++ > > > tools/binman/test/284_capsule_conf.dts | 14 ++ > > > tools/binman/test/285_capsule_missing_key.dts | 19 +++ > > > .../binman/test/286_capsule_missing_index.dts | 17 ++ > > > .../binman/test/287_capsule_missing_guid.dts | 17 ++ > > > .../test/288_capsule_missing_payload.dts | 17 ++ > > > tools/binman/test/289_capsule_missing.dts | 17 ++ > > > tools/binman/test/290_capsule_version.dts | 19 +++ > > > tools/binman/test/capsule_cfg.txt | 6 + > > > 15 files changed, 619 insertions(+) > > > create mode 100644 tools/binman/btool/mkeficapsule.py > > > create mode 100644 tools/binman/etype/capsule.py > > > create mode 100644 tools/binman/test/282_capsule.dts > > > create mode 100644 tools/binman/test/283_capsule_signed.dts > > > create mode 100644 tools/binman/test/284_capsule_conf.dts > > > create mode 100644 tools/binman/test/285_capsule_missing_key.dts > > > create mode 100644 tools/binman/test/286_capsule_missing_index.dts > > > create mode 100644 tools/binman/test/287_capsule_missing_guid.dts > > > create mode 100644 tools/binman/test/288_capsule_missing_payload.dts > > > create mode 100644 tools/binman/test/289_capsule_missing.dts > > > create mode 100644 tools/binman/test/290_capsule_version.dts > > > create mode 100644 tools/binman/test/capsule_cfg.txt > > > > This looks pretty good to me. Some nits below > > > > > > > > diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig > > > index dd848c57c6..2fcc789347 100644 > > > --- a/configs/sandbox_spl_defconfig > > > +++ b/configs/sandbox_spl_defconfig > > > @@ -248,3 +248,4 @@ CONFIG_UNIT_TEST=y > > > CONFIG_SPL_UNIT_TEST=y > > > CONFIG_UT_TIME=y > > > CONFIG_UT_DM=y > > > +CONFIG_TOOLS_MKEFICAPSULE=y > > > > Why enabling this here? I don't think it is needed in sandbox_spl, but > > in any case it should be in a different patch if needed. > > The binman tests run on the sandbox_spl variant. When running the > capsule generation tests, the mkeficapsule tool should be present on > the board variant no?
Can we run this on the 'sandbox' variant instead?
The capsule tests can be run on sandbox. But the change in the sandbox_spl_defconfig is for adding support for the capsule tests which are run as part of the binman test suite in CI. So it would be a
The binman tests are run separately, in 'Run binman, buildman, dtoc, Kconfig and patman testsuites' in gitlab.
Yes, and that uses the sandbox_spl variant build for running the tests. Which is why I added building the mkeficapsule tool for the variant.
Not really. It uses sandbox_spl to obtain the pylibfdt library, that's all.
The tests should use sandbox. By enabling CONFIG_BINMAN you will get pylibfdt anyway.
I will check on this point.
question of whether the rest of the binman tests in CI can be run on the sandbox variant. Or are there any specific set of tests which need to be run on the sandbox_spl variant?
Just the ones that need SPL. If you type 'make qcheck' you will see most/all of the tests, including the sandbox_spl ones.
Can we not just build the mkeficapsule tool in the sandbox_spl variant. That keeps things simple.
Why is it simple? It is actually a pain, because the vast majority of tests only need sandbox. We use sandbox_spl for tests that do something in SPL and need that phase of U-Boot to perform some sort of test. So far as I can tell, the EFI capsule thing works entirely in U-Boot proper.
[..]
> > > > > + > > > + def ReadNode(self): > > > + super().ReadNode() > > > + > > > + self.cfg_file = fdt_util.GetString(self._node, 'cfg-file') > > > + if not self.cfg_file: > > > + self.image_index = fdt_util.GetInt(self._node, 'image-index') > > > + if not self.image_index: > > > + self.Raise('mkeficapsule must be provided an Image Index') > > > + > > > + self.image_guid = fdt_util.GetString(self._node, 'image-type-id') > > > + if not self.image_guid: > > > + self.Raise('mkeficapsule must be provided an Image GUID') > > > > Use self.required_props = ['image-type-id', ...] in your __init__(). > > Then this is automatic > > I should have clarified this during the earlier version itself. So > these parameters are mandatory only when not using the config file. In > the scenario of generating the capsules through config files, all > these parameters are provided through the config file. Hence these > explicit checks.
Hmm, I think we should consider having two different etypes, then. It seems in fact that your entry type is doing 2-3 different things?
I am wondering if having two different etypes might get confusing for the users.
Maybe, but I'm already confused.
[..]
> > What if some of the inputs are missing? Does this handle missing blobs? > > Any missing input parameters are checked earlier itself. > > > > + if self.cfg_file: > > > + return self.mkeficapsule.capsule_cfg_file(self.cfg_file) > > > + elif self.auth: > > > + return self.mkeficapsule.cmdline_auth_capsule(self.image_index, > > > + self.image_guid, > > > + self.hardware_instance, > > > + self.monotonic_count, > > > + self.private_key, > > > + self.pub_key_cert, > > > + self.payload, > > > + self.capsule_fname, > > > + self.fw_version) > > > + else: > > > + return self.mkeficapsule.cmdline_capsule(self.image_index, > > > + self.image_guid, > > > + self.hardware_instance, > > > + self.payload, > > > + self.capsule_fname, > > > + self.fw_version)
Here is where I wonder whether you are putting too much in a single etype. Here there are three different cases. Should we have 3 etypes?
Basically the result in all the three cases is the same -- generation of a capsule file. Just that the input parameters being passed for generation are slightly different. There can be multiple entry types for these, but like I mentioned above, would having multiple entry types not be confusing for the users?
So can we drop the use of a cfg file? What is the difference between the second two? If you had added comments I would not have had to ask.
Using a config file to specify all the parameters needed for building capsule(s) is a good feature to have. It also results in generation of all the needed capsules through a single invocation of the mkeficapsule command, instead of multiple calls per capsule file.
Does that matter? I don't really have an objection to it, except in that it makes the entry type confusing. Do we need to support this mode of operation in binman? Perhaps EFI capsule can do what ever other enrty type does and specify the params in the Binman node? You can still use the config file for other uses, but I don't see any use for it in binman.
If you want multiple capsules, wouldn't that be done with multiple capsule nodes in the image definition?
[..]
> > > > We really should not have GUIDs in the code...they are a mess. > > You want the UEFI capsule generation to happen through binman, and not > mention GUIDs. That ain't happening :)
I just don't want them open-coded. They are meaningless gibberish that no one can understand. Use #define or some other way to give them a name.
Yes, I will move the GUIDS under a variable.
If I wrote:
writel(0x09812374, 0x8723728)
you would have the same comment.
> > > > > > + # Image GUID specified in the DTS > > > + self.image_guid = "52cfd7092007104791d108469b7fe9c8" > > > + self.fmp_signature = "4d535331" > > > + self.fmp_size = "10" > > > + self.fmp_fw_version = "02" > > > > These should really be local vars, not members. > > Okay > > > > > > + > > > + self.capsule_data = tools.read_file(self.capsule_fname) > > > > Pass the data in here and then you don't need to read the file > > So the file needs to be read here since the actual capsule generation > tool(tools/mkeficapsule) does not return any capsule data. Instead, > the data gets written to the capsule file, and the tool just returns a > pass/fail status.
Sure, but you can read that data in the caller to this functoin.
Yes, the data can be read in the caller, but that would mean duplicating the read in four functions. Instead the file read is happening in one place where the data is being used.
What do you mean? The data is returned from the call to self._DoReadFile(), the so pattern is:
data = self._DoReadFile(...)
You should be able to see that throughout ftest.py
What exactly is the confusion here?
No confusion. Again, this does not work when generating capsules through a config file.
OK let's drop the config file from binman. We don't need it and I'm tired of the back and forth.
Sorry about that, but like I mentioned in my other reply, we would be needing support for generating capsules through the config file. Can we not work around the testing in binman for this feature. Like I mentioned earlier, this will be getting tested as part of the larger efi capsule update feature testing.
We can worry about it later when all the other issues are resolved and this series is applied. Ideally that would be this week, since we have already hit rc1.
> > > > > > + > > > + self.assertEqual(self.capsule_guid, self.capsule_data.hex()[:32]) > > > + self.assertEqual(self.image_guid, self.capsule_data.hex()[96:128]) > > > + > > > + if version_check: > > > + self.assertEqual(self.fmp_signature, self.capsule_data.hex()[184:192]) > > > + self.assertEqual(self.fmp_size, self.capsule_data.hex()[192:194]) > > > + self.assertEqual(self.fmp_fw_version, self.capsule_data.hex()[200:202]) > > > + > > > + if signed_capsule: > > > + self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[4770:4778]) > > > > Where do these integer offsets come from? Please add a comment > > So, these are simply offsets in the output capsule file, which get > impacted based on the contents being put in the capsule, like > presence/absence of optional headers. I don't think putting a comment > really wll add any value, because these offsets are variable.
OK then please add a comment to that effect, as well as how to figure them out when things change.
Okay
> > > > > > + elif version_check: > > > + self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[216:224]) > > > + else: > > > + self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[184:192]) > > > + > > > + def _GenCapsuleCfgPayload(self, fname, contents): > > > + capsule_dir = '/tmp/capsules/' > > > > You can't write to /tmp > > > > Please use self._indir for input files - see how other tests do it > > For all other tests, I am indeed using _indir and outdir. But for > generation of capsules through a config file, we need to specify the > location where the output capsule file will be written to. Which is > the reason for the /tmp/capsules/. We are using this directory for > collating all capsule related files.
Well, sorry, you can't do that. I think you should provide a relative path, rather than absolute...that should solve the problem
Using absolute paths is only being done in the case of testing capsule generation through config file -- all the rest of the test cases are using relative paths for the input file and the capsule file. For the generation of capsules through the config file, the paths either need to be absolute, or relative to the directory from which the mkeficapsule tool is being invoked. And I believe that the binman test is creating temporary directories at runtime for input and output files.
OK so let's drop the cfg file stuff. It doesn't seem to be needed.
Instead of dropping support for generating capsules through a cfg file in binman, I would say drop test coverage in binman for that case. We can test this feature, that is not an issue, but you insist on using the binman input and output directories which get created at runtime. We cannot test the capsule generation with a cfg file with that restriction.
No.
What is the issue you see in using the /tmp/capsules/ path for generation of capsules. I see directories being created with relevant files under /opt for other tests. If you would want the capsules directory to be in some other location, I can change that.
Binman tests should use the input and output directories, so please figure out how to do this. If you drop the cfg file then it should be easy.
If I could do it, I would not spend this time discussing with you. Like I said above, if you insist on not using a known location for the input and output files, maybe we can drop the test for cfg file -- this functionality does get tested as part of the capsule update feature testing. I would argue that generating capsules using a cfg file is beneficial and be retained.
As above.
[..]
> > > > Please can you use your own test data, like EFI_DATA ? Also if you > > declare it as a binary string you can drop the call. > > > > For example: > > > > EFI_DATA = b'efi' > > I don't know why text_data cannot be used, but I will add the EFI_DATA.
Well firstly it is not a 'bytes' string. Secondly you may as well have your own as we have done with other etypes.
Okay
> > > > > > + > > > + TestFunctional._MakeInputFile('payload.txt', self.payload_data) > > > + > > > + self._DoReadFile('282_capsule.dts') > > > > data = self... > > Please see above. We need to read the capsule file. This applies for > all the related comments about using the data = self._DoReadFile...
That needs to be fixed, since the output file should be the capsule. Why would the output file be anything else??
The output file is indeed a capsule, but that is being generated by the capsule generation tool which gets called from the bintool. So the mkeficapsule tool that the bintool calls results in generation of the capsule file. In that way, this does not map directly to the concept of an image in binman. And I was wondering if I should introduce a new property like 'dummy-image', so that the resultant <image>.bin and <image>.map files are not created -- they really are not needed in the case of capsule generation. Would you be fine with an introduction of such a property?
Eek, no.
I thought the output file from binman (image.bin in most tests) was an EFI capsule. Is that not the case? If not, what exactly is it?
In this case, it is not so. Which is why I proposed the above property. What happens is that the bintool calls the actual capsule generation tool, and that tool writes the capsule contents to a file which is provided to the tool as a parameter. Binman does not have to create the image.bin file in this case, since the mkeficapsule tool has already created the capsule file -- creating the image.bin and image.map is superfluous here.
I think you are missing something here. The tools that binman uses are there to generate things that the binman description wants. The EFI capsule is just another one of those.
We should rename 'capsule' to 'efi-capsule', I think, since there might be other types of capsule.
In theory I could say:
binman { u-boot-spl { }; efi-capsule { // things we want in the capsule u-boot { } }
and it should produce an image with SPL at the start followed by the capsule. The capsule is allowed to generate data, not create the whole image. Perhaps that is the blocker here?
I see now that your capsule entry type is wonky. It needs to accept properties (not filenames!) and use those to package up the contents, which should be subnodes of itself.
Okay. I will take a look at how mkimage is called. I had checked the intel_ifwi entry type for reference, and I did see use of the filename property there.
Yes sections can have filenames iwc the content is written to the filename. But it is still handled internally by binman.
There might be a misunderstanding of how binman works...did you look through all the docs?
Probably you should look at how mkimage does it...but you essentially call self.collect_contents_to_file() to pick up the data. Your capsule should probably subclass entry_section, rather than Entry, since entry_Section handles some other things for you, like missing blobs.
Okay, I will check.
-sughosh
Looking at your last patch I think this is the confusion, and why this has been so hard for me to wrap my head around.
Regards, Simon

hi Simon,
On Wed, 26 Jul 2023 at 04:06, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Thu, 20 Jul 2023 at 03:20, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Simon,
On Thu, 20 Jul 2023 at 00:41, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Wed, 19 Jul 2023 at 02:42, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Simon,
On Wed, 19 Jul 2023 at 06:41, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Mon, 17 Jul 2023 at 04:44, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Simon,
On Sun, 16 Jul 2023 at 05:12, Simon Glass sjg@chromium.org wrote: > > Hi Sughosh, > > On Sat, 15 Jul 2023 at 07:46, Sughosh Ganu sughosh.ganu@linaro.org wrote: > > > > Add support in binman for generating capsules. The capsule parameters > > can be specified either through a config file or through the capsule > > binman entry. Also add test cases in binman for capsule generation, > > and enable this testing on the sandbox_spl variant. > > Can you use sandbox instead, or perhaps sandbox_spl? SPL is really for > SPL testing.
Er, I am actually using the sandbox_spl variant.
> > > > > Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org > > --- > > Changes since V3: > > * Add test cases for covering the various capsule generation > > scenarios. > > * Add function comments in the mkeficapsule bintool. > > * Fix the fetch method of the mkeficapsule bintool to enable building > > the tool. > > * Add more details about the capsule parameters in the documentation > > as well as the code. > > * Fix order of module imports, and addition of blank lines in the > > capsule.py file. > > * Use SetContents in the ObtainContents method. > > > > configs/sandbox_spl_defconfig | 1 + > > tools/binman/btool/mkeficapsule.py | 158 ++++++++++++++++++ > > tools/binman/entries.rst | 37 ++++ > > tools/binman/etype/capsule.py | 132 +++++++++++++++ > > tools/binman/ftest.py | 127 ++++++++++++++ > > tools/binman/test/282_capsule.dts | 18 ++ > > tools/binman/test/283_capsule_signed.dts | 20 +++ > > tools/binman/test/284_capsule_conf.dts | 14 ++ > > tools/binman/test/285_capsule_missing_key.dts | 19 +++ > > .../binman/test/286_capsule_missing_index.dts | 17 ++ > > .../binman/test/287_capsule_missing_guid.dts | 17 ++ > > .../test/288_capsule_missing_payload.dts | 17 ++ > > tools/binman/test/289_capsule_missing.dts | 17 ++ > > tools/binman/test/290_capsule_version.dts | 19 +++ > > tools/binman/test/capsule_cfg.txt | 6 + > > 15 files changed, 619 insertions(+) > > create mode 100644 tools/binman/btool/mkeficapsule.py > > create mode 100644 tools/binman/etype/capsule.py > > create mode 100644 tools/binman/test/282_capsule.dts > > create mode 100644 tools/binman/test/283_capsule_signed.dts > > create mode 100644 tools/binman/test/284_capsule_conf.dts > > create mode 100644 tools/binman/test/285_capsule_missing_key.dts > > create mode 100644 tools/binman/test/286_capsule_missing_index.dts > > create mode 100644 tools/binman/test/287_capsule_missing_guid.dts > > create mode 100644 tools/binman/test/288_capsule_missing_payload.dts > > create mode 100644 tools/binman/test/289_capsule_missing.dts > > create mode 100644 tools/binman/test/290_capsule_version.dts > > create mode 100644 tools/binman/test/capsule_cfg.txt > > This looks pretty good to me. Some nits below > > > > > diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig > > index dd848c57c6..2fcc789347 100644 > > --- a/configs/sandbox_spl_defconfig > > +++ b/configs/sandbox_spl_defconfig > > @@ -248,3 +248,4 @@ CONFIG_UNIT_TEST=y > > CONFIG_SPL_UNIT_TEST=y > > CONFIG_UT_TIME=y > > CONFIG_UT_DM=y > > +CONFIG_TOOLS_MKEFICAPSULE=y > > Why enabling this here? I don't think it is needed in sandbox_spl, but > in any case it should be in a different patch if needed.
The binman tests run on the sandbox_spl variant. When running the capsule generation tests, the mkeficapsule tool should be present on the board variant no?
Can we run this on the 'sandbox' variant instead?
The capsule tests can be run on sandbox. But the change in the sandbox_spl_defconfig is for adding support for the capsule tests which are run as part of the binman test suite in CI. So it would be a
The binman tests are run separately, in 'Run binman, buildman, dtoc, Kconfig and patman testsuites' in gitlab.
Yes, and that uses the sandbox_spl variant build for running the tests. Which is why I added building the mkeficapsule tool for the variant.
Not really. It uses sandbox_spl to obtain the pylibfdt library, that's all.
The tests should use sandbox. By enabling CONFIG_BINMAN you will get pylibfdt anyway.
I am not sure if I am missing something. But if I do not build the mkeficapsule tool as part of the sandbox_spl variant, I get errors in CI on the binman tests. And I believe that is because binman is being run with the '--toolpath /tmp/sandbox_spl/tools', which is built for the sandbox_spl variant. So if the mkeficapsule tool is not built for the sandbox_spl variant, this results in the binman capsule tests to fail in CI. I see this at least with the azure pipeline.
-sughosh
question of whether the rest of the binman tests in CI can be run on the sandbox variant. Or are there any specific set of tests which need to be run on the sandbox_spl variant?
Just the ones that need SPL. If you type 'make qcheck' you will see most/all of the tests, including the sandbox_spl ones.
Can we not just build the mkeficapsule tool in the sandbox_spl variant. That keeps things simple.
Why is it simple? It is actually a pain, because the vast majority of tests only need sandbox. We use sandbox_spl for tests that do something in SPL and need that phase of U-Boot to perform some sort of test. So far as I can tell, the EFI capsule thing works entirely in U-Boot proper.
[..]
> > > + > > + def ReadNode(self): > > + super().ReadNode() > > + > > + self.cfg_file = fdt_util.GetString(self._node, 'cfg-file') > > + if not self.cfg_file: > > + self.image_index = fdt_util.GetInt(self._node, 'image-index') > > + if not self.image_index: > > + self.Raise('mkeficapsule must be provided an Image Index') > > + > > + self.image_guid = fdt_util.GetString(self._node, 'image-type-id') > > + if not self.image_guid: > > + self.Raise('mkeficapsule must be provided an Image GUID') > > Use self.required_props = ['image-type-id', ...] in your __init__(). > Then this is automatic
I should have clarified this during the earlier version itself. So these parameters are mandatory only when not using the config file. In the scenario of generating the capsules through config files, all these parameters are provided through the config file. Hence these explicit checks.
Hmm, I think we should consider having two different etypes, then. It seems in fact that your entry type is doing 2-3 different things?
I am wondering if having two different etypes might get confusing for the users.
Maybe, but I'm already confused.
[..]
> What if some of the inputs are missing? Does this handle missing blobs?
Any missing input parameters are checked earlier itself.
> > + if self.cfg_file: > > + return self.mkeficapsule.capsule_cfg_file(self.cfg_file) > > + elif self.auth: > > + return self.mkeficapsule.cmdline_auth_capsule(self.image_index, > > + self.image_guid, > > + self.hardware_instance, > > + self.monotonic_count, > > + self.private_key, > > + self.pub_key_cert, > > + self.payload, > > + self.capsule_fname, > > + self.fw_version) > > + else: > > + return self.mkeficapsule.cmdline_capsule(self.image_index, > > + self.image_guid, > > + self.hardware_instance, > > + self.payload, > > + self.capsule_fname, > > + self.fw_version)
Here is where I wonder whether you are putting too much in a single etype. Here there are three different cases. Should we have 3 etypes?
Basically the result in all the three cases is the same -- generation of a capsule file. Just that the input parameters being passed for generation are slightly different. There can be multiple entry types for these, but like I mentioned above, would having multiple entry types not be confusing for the users?
So can we drop the use of a cfg file? What is the difference between the second two? If you had added comments I would not have had to ask.
Using a config file to specify all the parameters needed for building capsule(s) is a good feature to have. It also results in generation of all the needed capsules through a single invocation of the mkeficapsule command, instead of multiple calls per capsule file.
Does that matter? I don't really have an objection to it, except in that it makes the entry type confusing. Do we need to support this mode of operation in binman? Perhaps EFI capsule can do what ever other enrty type does and specify the params in the Binman node? You can still use the config file for other uses, but I don't see any use for it in binman.
If you want multiple capsules, wouldn't that be done with multiple capsule nodes in the image definition?
[..]
> > We really should not have GUIDs in the code...they are a mess.
You want the UEFI capsule generation to happen through binman, and not mention GUIDs. That ain't happening :)
I just don't want them open-coded. They are meaningless gibberish that no one can understand. Use #define or some other way to give them a name.
Yes, I will move the GUIDS under a variable.
If I wrote:
writel(0x09812374, 0x8723728)
you would have the same comment.
> > > + # Image GUID specified in the DTS > > + self.image_guid = "52cfd7092007104791d108469b7fe9c8" > > + self.fmp_signature = "4d535331" > > + self.fmp_size = "10" > > + self.fmp_fw_version = "02" > > These should really be local vars, not members.
Okay
> > > + > > + self.capsule_data = tools.read_file(self.capsule_fname) > > Pass the data in here and then you don't need to read the file
So the file needs to be read here since the actual capsule generation tool(tools/mkeficapsule) does not return any capsule data. Instead, the data gets written to the capsule file, and the tool just returns a pass/fail status.
Sure, but you can read that data in the caller to this functoin.
Yes, the data can be read in the caller, but that would mean duplicating the read in four functions. Instead the file read is happening in one place where the data is being used.
What do you mean? The data is returned from the call to self._DoReadFile(), the so pattern is:
data = self._DoReadFile(...)
You should be able to see that throughout ftest.py
What exactly is the confusion here?
No confusion. Again, this does not work when generating capsules through a config file.
OK let's drop the config file from binman. We don't need it and I'm tired of the back and forth.
> > > + > > + self.assertEqual(self.capsule_guid, self.capsule_data.hex()[:32]) > > + self.assertEqual(self.image_guid, self.capsule_data.hex()[96:128]) > > + > > + if version_check: > > + self.assertEqual(self.fmp_signature, self.capsule_data.hex()[184:192]) > > + self.assertEqual(self.fmp_size, self.capsule_data.hex()[192:194]) > > + self.assertEqual(self.fmp_fw_version, self.capsule_data.hex()[200:202]) > > + > > + if signed_capsule: > > + self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[4770:4778]) > > Where do these integer offsets come from? Please add a comment
So, these are simply offsets in the output capsule file, which get impacted based on the contents being put in the capsule, like presence/absence of optional headers. I don't think putting a comment really wll add any value, because these offsets are variable.
OK then please add a comment to that effect, as well as how to figure them out when things change.
Okay
> > > + elif version_check: > > + self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[216:224]) > > + else: > > + self.assertEqual(self.payload_data.hex(), self.capsule_data.hex()[184:192]) > > + > > + def _GenCapsuleCfgPayload(self, fname, contents): > > + capsule_dir = '/tmp/capsules/' > > You can't write to /tmp > > Please use self._indir for input files - see how other tests do it
For all other tests, I am indeed using _indir and outdir. But for generation of capsules through a config file, we need to specify the location where the output capsule file will be written to. Which is the reason for the /tmp/capsules/. We are using this directory for collating all capsule related files.
Well, sorry, you can't do that. I think you should provide a relative path, rather than absolute...that should solve the problem
Using absolute paths is only being done in the case of testing capsule generation through config file -- all the rest of the test cases are using relative paths for the input file and the capsule file. For the generation of capsules through the config file, the paths either need to be absolute, or relative to the directory from which the mkeficapsule tool is being invoked. And I believe that the binman test is creating temporary directories at runtime for input and output files.
OK so let's drop the cfg file stuff. It doesn't seem to be needed.
Instead of dropping support for generating capsules through a cfg file in binman, I would say drop test coverage in binman for that case. We can test this feature, that is not an issue, but you insist on using the binman input and output directories which get created at runtime. We cannot test the capsule generation with a cfg file with that restriction.
No.
What is the issue you see in using the /tmp/capsules/ path for generation of capsules. I see directories being created with relevant files under /opt for other tests. If you would want the capsules directory to be in some other location, I can change that.
Binman tests should use the input and output directories, so please figure out how to do this. If you drop the cfg file then it should be easy.
If I could do it, I would not spend this time discussing with you. Like I said above, if you insist on not using a known location for the input and output files, maybe we can drop the test for cfg file -- this functionality does get tested as part of the capsule update feature testing. I would argue that generating capsules using a cfg file is beneficial and be retained.
As above.
[..]
> > Please can you use your own test data, like EFI_DATA ? Also if you > declare it as a binary string you can drop the call. > > For example: > > EFI_DATA = b'efi'
I don't know why text_data cannot be used, but I will add the EFI_DATA.
Well firstly it is not a 'bytes' string. Secondly you may as well have your own as we have done with other etypes.
Okay
> > > + > > + TestFunctional._MakeInputFile('payload.txt', self.payload_data) > > + > > + self._DoReadFile('282_capsule.dts') > > data = self...
Please see above. We need to read the capsule file. This applies for all the related comments about using the data = self._DoReadFile...
That needs to be fixed, since the output file should be the capsule. Why would the output file be anything else??
The output file is indeed a capsule, but that is being generated by the capsule generation tool which gets called from the bintool. So the mkeficapsule tool that the bintool calls results in generation of the capsule file. In that way, this does not map directly to the concept of an image in binman. And I was wondering if I should introduce a new property like 'dummy-image', so that the resultant <image>.bin and <image>.map files are not created -- they really are not needed in the case of capsule generation. Would you be fine with an introduction of such a property?
Eek, no.
I thought the output file from binman (image.bin in most tests) was an EFI capsule. Is that not the case? If not, what exactly is it?
In this case, it is not so. Which is why I proposed the above property. What happens is that the bintool calls the actual capsule generation tool, and that tool writes the capsule contents to a file which is provided to the tool as a parameter. Binman does not have to create the image.bin file in this case, since the mkeficapsule tool has already created the capsule file -- creating the image.bin and image.map is superfluous here.
I think you are missing something here. The tools that binman uses are there to generate things that the binman description wants. The EFI capsule is just another one of those.
We should rename 'capsule' to 'efi-capsule', I think, since there might be other types of capsule.
In theory I could say:
binman { u-boot-spl { }; efi-capsule { // things we want in the capsule u-boot { } }
and it should produce an image with SPL at the start followed by the capsule. The capsule is allowed to generate data, not create the whole image. Perhaps that is the blocker here?
I see now that your capsule entry type is wonky. It needs to accept properties (not filenames!) and use those to package up the contents, which should be subnodes of itself.
Probably you should look at how mkimage does it...but you essentially call self.collect_contents_to_file() to pick up the data. Your capsule should probably subclass entry_section, rather than Entry, since entry_Section handles some other things for you, like missing blobs.
Looking at your last patch I think this is the confusion, and why this has been so hard for me to wrap my head around.
Regards, Simon

Hi Sughosh,
On Tue, 1 Aug 2023 at 06:29, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Simon,
On Wed, 26 Jul 2023 at 04:06, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Thu, 20 Jul 2023 at 03:20, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Simon,
On Thu, 20 Jul 2023 at 00:41, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Wed, 19 Jul 2023 at 02:42, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Simon,
On Wed, 19 Jul 2023 at 06:41, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Mon, 17 Jul 2023 at 04:44, Sughosh Ganu sughosh.ganu@linaro.org wrote: > > hi Simon, > > On Sun, 16 Jul 2023 at 05:12, Simon Glass sjg@chromium.org wrote: > > > > Hi Sughosh, > > > > On Sat, 15 Jul 2023 at 07:46, Sughosh Ganu sughosh.ganu@linaro.org wrote: > > > > > > Add support in binman for generating capsules. The capsule parameters > > > can be specified either through a config file or through the capsule > > > binman entry. Also add test cases in binman for capsule generation, > > > and enable this testing on the sandbox_spl variant. > > > > Can you use sandbox instead, or perhaps sandbox_spl? SPL is really for > > SPL testing. > > Er, I am actually using the sandbox_spl variant. > > > > > > > > > Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org > > > --- > > > Changes since V3: > > > * Add test cases for covering the various capsule generation > > > scenarios. > > > * Add function comments in the mkeficapsule bintool. > > > * Fix the fetch method of the mkeficapsule bintool to enable building > > > the tool. > > > * Add more details about the capsule parameters in the documentation > > > as well as the code. > > > * Fix order of module imports, and addition of blank lines in the > > > capsule.py file. > > > * Use SetContents in the ObtainContents method. > > > > > > configs/sandbox_spl_defconfig | 1 + > > > tools/binman/btool/mkeficapsule.py | 158 ++++++++++++++++++ > > > tools/binman/entries.rst | 37 ++++ > > > tools/binman/etype/capsule.py | 132 +++++++++++++++ > > > tools/binman/ftest.py | 127 ++++++++++++++ > > > tools/binman/test/282_capsule.dts | 18 ++ > > > tools/binman/test/283_capsule_signed.dts | 20 +++ > > > tools/binman/test/284_capsule_conf.dts | 14 ++ > > > tools/binman/test/285_capsule_missing_key.dts | 19 +++ > > > .../binman/test/286_capsule_missing_index.dts | 17 ++ > > > .../binman/test/287_capsule_missing_guid.dts | 17 ++ > > > .../test/288_capsule_missing_payload.dts | 17 ++ > > > tools/binman/test/289_capsule_missing.dts | 17 ++ > > > tools/binman/test/290_capsule_version.dts | 19 +++ > > > tools/binman/test/capsule_cfg.txt | 6 + > > > 15 files changed, 619 insertions(+) > > > create mode 100644 tools/binman/btool/mkeficapsule.py > > > create mode 100644 tools/binman/etype/capsule.py > > > create mode 100644 tools/binman/test/282_capsule.dts > > > create mode 100644 tools/binman/test/283_capsule_signed.dts > > > create mode 100644 tools/binman/test/284_capsule_conf.dts > > > create mode 100644 tools/binman/test/285_capsule_missing_key.dts > > > create mode 100644 tools/binman/test/286_capsule_missing_index.dts > > > create mode 100644 tools/binman/test/287_capsule_missing_guid.dts > > > create mode 100644 tools/binman/test/288_capsule_missing_payload.dts > > > create mode 100644 tools/binman/test/289_capsule_missing.dts > > > create mode 100644 tools/binman/test/290_capsule_version.dts > > > create mode 100644 tools/binman/test/capsule_cfg.txt > > > > This looks pretty good to me. Some nits below > > > > > > > > diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig > > > index dd848c57c6..2fcc789347 100644 > > > --- a/configs/sandbox_spl_defconfig > > > +++ b/configs/sandbox_spl_defconfig > > > @@ -248,3 +248,4 @@ CONFIG_UNIT_TEST=y > > > CONFIG_SPL_UNIT_TEST=y > > > CONFIG_UT_TIME=y > > > CONFIG_UT_DM=y > > > +CONFIG_TOOLS_MKEFICAPSULE=y > > > > Why enabling this here? I don't think it is needed in sandbox_spl, but > > in any case it should be in a different patch if needed. > > The binman tests run on the sandbox_spl variant. When running the > capsule generation tests, the mkeficapsule tool should be present on > the board variant no?
Can we run this on the 'sandbox' variant instead?
The capsule tests can be run on sandbox. But the change in the sandbox_spl_defconfig is for adding support for the capsule tests which are run as part of the binman test suite in CI. So it would be a
The binman tests are run separately, in 'Run binman, buildman, dtoc, Kconfig and patman testsuites' in gitlab.
Yes, and that uses the sandbox_spl variant build for running the tests. Which is why I added building the mkeficapsule tool for the variant.
Not really. It uses sandbox_spl to obtain the pylibfdt library, that's all.
The tests should use sandbox. By enabling CONFIG_BINMAN you will get pylibfdt anyway.
I am not sure if I am missing something. But if I do not build the mkeficapsule tool as part of the sandbox_spl variant, I get errors in CI on the binman tests. And I believe that is because binman is being run with the '--toolpath /tmp/sandbox_spl/tools', which is built for the sandbox_spl variant. So if the mkeficapsule tool is not built for the sandbox_spl variant, this results in the binman capsule tests to fail in CI. I see this at least with the azure pipeline.
Yes, alll sandbox variants should build all tools, I believe. The mkeficapsule tool should really be built by all boards, right? It also needs to appear in the u-boot-tools debian package too, for example.
[..]
Regards, Simon

The UEFI capsule can now be generate by specifying the capsule parameters through a config file. Additionally, the capsules can be generated as part of u-boot build, through binman. Highlight these changes in the documentation.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- Changes since V3: * Move the paragraph on version support under a separate subsection. * Move the description on generating capsules through config file under the section to describe capsule generation. * Add a subsection highlighting generation of capsules through binman.
doc/develop/uefi/uefi.rst | 84 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+)
diff --git a/doc/develop/uefi/uefi.rst b/doc/develop/uefi/uefi.rst index 3d07aa33b8..251eaa13fd 100644 --- a/doc/develop/uefi/uefi.rst +++ b/doc/develop/uefi/uefi.rst @@ -318,6 +318,79 @@ Run the following command --guid <image GUID> \ <capsule_file_name>
+Alternatively, the capsules can be generated through a config +file. When generating the capsules through a config file, 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 + fw-version: 2 + 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 + fw-version: 7 + 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 + fw-version: Image version + 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) + +When generating capsules through a config file, the command would look +like + +.. code-block:: console + + $ mkeficapsule --cfg-file </path/to/the/config/file> + + +Capsule with firmware version +***************************** + The UEFI specification does not define the firmware versioning mechanism. EDK II reference implementation inserts the FMP Payload Header right before the payload. It coutains the fw_version and lowest supported version, @@ -345,6 +418,17 @@ add --fw-version option in mkeficapsule tool. If the --fw-version option is not set, FMP Payload Header is not inserted and fw_version is set as 0.
+ +Capsule Generation through binman +********************************* + +Support has also been added to generate capsules during u-boot build +through binman. This requires the platform's DTB to be populated with +the capsule entry nodes for binman. The capsules then can be generated +by specifying the capsule parameters either through a config file, or +by specifying them as properties in the capsule entry node. + + Performing the update *********************

Support has being added through earlier commits to build capsules and embed the public key needed for capsule authentication as part of u-boot build.
From the testing point-of-view, this means the input files needed for
generating the above have to be setup before invoking the build. Set this up in the CI configuration files for testing the capsule update feature.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- Changes since V3: * Remove whitespace in the command to generate capsule keys.
.azure-pipelines.yml | 24 ++++++++++++++++++++++++ .gitlab-ci.yml | 22 ++++++++++++++++++++++ 2 files changed, 46 insertions(+)
diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index 06c46b681c..d732ba443d 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -398,6 +398,17 @@ stages: wget -O - https://github.com/riscv-software-src/opensbi/releases/download/v1.2/opensbi... | tar -C /tmp -xJ; export OPENSBI=/tmp/opensbi-1.2-rv-bin/share/opensbi/lp64/generic/firmware/fw_dynamic.bin; fi + mkdir -p /tmp/capsules/; + echo -n "u-boot:Old" >/tmp/capsules/u-boot.bin.old; + echo -n "u-boot:New" >/tmp/capsules/u-boot.bin.new; + echo -n "u-boot-env:Old" >/tmp/capsules/u-boot.env.old; + echo -n "u-boot-env:New" >/tmp/capsules/u-boot.env.new; + if [[ "${TEST_PY_BD}" == "sandbox" ]] || [[ "${TEST_PY_BD}" == "sandbox_flattree" ]]; then + 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 + # the below corresponds to .gitlab-ci.yml "script" cd ${WORK_DIR} export UBOOT_TRAVIS_BUILD_DIR=/tmp/${TEST_PY_BD}; @@ -582,6 +593,19 @@ 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/; + echo -n "u-boot:Old" >/tmp/capsules/u-boot.bin.old; + echo -n "u-boot:New" >/tmp/capsules/u-boot.bin.new; + echo -n "u-boot-env:Old" >/tmp/capsules/u-boot.env.old; + echo -n "u-boot-env:New" >/tmp/capsules/u-boot.env.new; + + 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 cfd58513c3..aec6ffaf1c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -37,6 +37,17 @@ stages: export OPENSBI=/tmp/opensbi-1.2-rv-bin/share/opensbi/lp64/generic/firmware/fw_dynamic.bin; fi
+ - mkdir -p /tmp/capsules/; + - echo -n "u-boot:Old" >/tmp/capsules/u-boot.bin.old; + - echo -n "u-boot:New" >/tmp/capsules/u-boot.bin.new; + - echo -n "u-boot-env:Old" >/tmp/capsules/u-boot.env.old; + - echo -n "u-boot-env:New" >/tmp/capsules/u-boot.env.new; + - if [[ "${TEST_PY_BD}" == "sandbox" ]] || [[ "${TEST_PY_BD}" == "sandbox_flattree" ]]; then + 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 + after_script: - cp -v /tmp/${TEST_PY_BD}/*.{html,css} . - rm -rf /tmp/uboot-test-hooks /tmp/venv @@ -131,6 +142,17 @@ build all other platforms: stage: world build script: - ret=0; + if [ ! -d "/tmp/capsules/" ]; then + mkdir -p /tmp/capsules/; + echo -n "u-boot:Old" >/tmp/capsules/u-boot.bin.old; + echo -n "u-boot:New" >/tmp/capsules/u-boot.bin.new; + echo -n "u-boot-env:Old" >/tmp/capsules/u-boot.env.old; + echo -n "u-boot-env:New" >/tmp/capsules/u-boot.env.new; + + 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 git config --global --add safe.directory "${CI_PROJECT_DIR}"; ./tools/buildman/buildman -o /tmp -PEWM -x arm,powerpc || ret=$?; if [[ $ret -ne 0 ]]; then

Hi,
On Sat, 15 Jul 2023 at 07:46, Sughosh Ganu sughosh.ganu@linaro.org wrote:
Support has being added through earlier commits to build capsules and embed the public key needed for capsule authentication as part of u-boot build.
From the testing point-of-view, this means the input files needed for generating the above have to be setup before invoking the build. Set this up in the CI configuration files for testing the capsule update feature.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
Changes since V3:
- Remove whitespace in the command to generate capsule keys.
.azure-pipelines.yml | 24 ++++++++++++++++++++++++ .gitlab-ci.yml | 22 ++++++++++++++++++++++
Can you add this to the Dockerfile instead? It looks like this will run on each build.
2 files changed, 46 insertions(+)
diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index 06c46b681c..d732ba443d 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -398,6 +398,17 @@ stages: wget -O - https://github.com/riscv-software-src/opensbi/releases/download/v1.2/opensbi... | tar -C /tmp -xJ; export OPENSBI=/tmp/opensbi-1.2-rv-bin/share/opensbi/lp64/generic/firmware/fw_dynamic.bin; fi
mkdir -p /tmp/capsules/;
echo -n "u-boot:Old" >/tmp/capsules/u-boot.bin.old;
echo -n "u-boot:New" >/tmp/capsules/u-boot.bin.new;
echo -n "u-boot-env:Old" >/tmp/capsules/u-boot.env.old;
echo -n "u-boot-env:New" >/tmp/capsules/u-boot.env.new;
if [[ "${TEST_PY_BD}" == "sandbox" ]] || [[ "${TEST_PY_BD}" == "sandbox_flattree" ]]; then
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
# the below corresponds to .gitlab-ci.yml "script" cd ${WORK_DIR} export UBOOT_TRAVIS_BUILD_DIR=/tmp/${TEST_PY_BD};
@@ -582,6 +593,19 @@ 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/;
echo -n "u-boot:Old" >/tmp/capsules/u-boot.bin.old;
echo -n "u-boot:New" >/tmp/capsules/u-boot.bin.new;
echo -n "u-boot-env:Old" >/tmp/capsules/u-boot.env.old;
echo -n "u-boot-env:New" >/tmp/capsules/u-boot.env.new;
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 cfd58513c3..aec6ffaf1c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -37,6 +37,17 @@ stages: export OPENSBI=/tmp/opensbi-1.2-rv-bin/share/opensbi/lp64/generic/firmware/fw_dynamic.bin; fi
- mkdir -p /tmp/capsules/;
- echo -n "u-boot:Old" >/tmp/capsules/u-boot.bin.old;
- echo -n "u-boot:New" >/tmp/capsules/u-boot.bin.new;
- echo -n "u-boot-env:Old" >/tmp/capsules/u-boot.env.old;
- echo -n "u-boot-env:New" >/tmp/capsules/u-boot.env.new;
- if [[ "${TEST_PY_BD}" == "sandbox" ]] || [[ "${TEST_PY_BD}" == "sandbox_flattree" ]]; then
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
- after_script:
- cp -v /tmp/${TEST_PY_BD}/*.{html,css} .
- rm -rf /tmp/uboot-test-hooks /tmp/venv
@@ -131,6 +142,17 @@ build all other platforms: stage: world build script: - ret=0;
if [ ! -d "/tmp/capsules/" ]; then
mkdir -p /tmp/capsules/;
echo -n "u-boot:Old" >/tmp/capsules/u-boot.bin.old;
echo -n "u-boot:New" >/tmp/capsules/u-boot.bin.new;
echo -n "u-boot-env:Old" >/tmp/capsules/u-boot.env.old;
echo -n "u-boot-env:New" >/tmp/capsules/u-boot.env.new;
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 git config --global --add safe.directory "${CI_PROJECT_DIR}"; ./tools/buildman/buildman -o /tmp -PEWM -x arm,powerpc || ret=$?; if [[ $ret -ne 0 ]]; then
-- 2.34.1
Regards, Simon

hi Simon,
On Sun, 16 Jul 2023 at 05:12, Simon Glass sjg@chromium.org wrote:
Hi,
On Sat, 15 Jul 2023 at 07:46, Sughosh Ganu sughosh.ganu@linaro.org wrote:
Support has being added through earlier commits to build capsules and embed the public key needed for capsule authentication as part of u-boot build.
From the testing point-of-view, this means the input files needed for generating the above have to be setup before invoking the build. Set this up in the CI configuration files for testing the capsule update feature.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
Changes since V3:
- Remove whitespace in the command to generate capsule keys.
.azure-pipelines.yml | 24 ++++++++++++++++++++++++ .gitlab-ci.yml | 22 ++++++++++++++++++++++
Can you add this to the Dockerfile instead? It looks like this will run on each build.
Okay. Let me try this out.
-sughosh
2 files changed, 46 insertions(+)
diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index 06c46b681c..d732ba443d 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -398,6 +398,17 @@ stages: wget -O - https://github.com/riscv-software-src/opensbi/releases/download/v1.2/opensbi... | tar -C /tmp -xJ; export OPENSBI=/tmp/opensbi-1.2-rv-bin/share/opensbi/lp64/generic/firmware/fw_dynamic.bin; fi
mkdir -p /tmp/capsules/;
echo -n "u-boot:Old" >/tmp/capsules/u-boot.bin.old;
echo -n "u-boot:New" >/tmp/capsules/u-boot.bin.new;
echo -n "u-boot-env:Old" >/tmp/capsules/u-boot.env.old;
echo -n "u-boot-env:New" >/tmp/capsules/u-boot.env.new;
if [[ "${TEST_PY_BD}" == "sandbox" ]] || [[ "${TEST_PY_BD}" == "sandbox_flattree" ]]; then
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
# the below corresponds to .gitlab-ci.yml "script" cd ${WORK_DIR} export UBOOT_TRAVIS_BUILD_DIR=/tmp/${TEST_PY_BD};
@@ -582,6 +593,19 @@ 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/;
echo -n "u-boot:Old" >/tmp/capsules/u-boot.bin.old;
echo -n "u-boot:New" >/tmp/capsules/u-boot.bin.new;
echo -n "u-boot-env:Old" >/tmp/capsules/u-boot.env.old;
echo -n "u-boot-env:New" >/tmp/capsules/u-boot.env.new;
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 cfd58513c3..aec6ffaf1c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -37,6 +37,17 @@ stages: export OPENSBI=/tmp/opensbi-1.2-rv-bin/share/opensbi/lp64/generic/firmware/fw_dynamic.bin; fi
- mkdir -p /tmp/capsules/;
- echo -n "u-boot:Old" >/tmp/capsules/u-boot.bin.old;
- echo -n "u-boot:New" >/tmp/capsules/u-boot.bin.new;
- echo -n "u-boot-env:Old" >/tmp/capsules/u-boot.env.old;
- echo -n "u-boot-env:New" >/tmp/capsules/u-boot.env.new;
- if [[ "${TEST_PY_BD}" == "sandbox" ]] || [[ "${TEST_PY_BD}" == "sandbox_flattree" ]]; then
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
- after_script:
- cp -v /tmp/${TEST_PY_BD}/*.{html,css} .
- rm -rf /tmp/uboot-test-hooks /tmp/venv
@@ -131,6 +142,17 @@ build all other platforms: stage: world build script: - ret=0;
if [ ! -d "/tmp/capsules/" ]; then
mkdir -p /tmp/capsules/;
echo -n "u-boot:Old" >/tmp/capsules/u-boot.bin.old;
echo -n "u-boot:New" >/tmp/capsules/u-boot.bin.new;
echo -n "u-boot-env:Old" >/tmp/capsules/u-boot.env.old;
echo -n "u-boot-env:New" >/tmp/capsules/u-boot.env.new;
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 git config --global --add safe.directory "${CI_PROJECT_DIR}"; ./tools/buildman/buildman -o /tmp -PEWM -x arm,powerpc || ret=$?; if [[ $ret -ne 0 ]]; then
-- 2.34.1
Regards, Simon

Support has being added through earlier commits to build capsules and embed the public key needed for capsule authentication as part of u-boot build.
From the testing point-of-view, this means the input files needed for
the above have to be setup before invoking the build. Set this up in the pytest configuration file for testing the capsule update feature.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Reviewed-by: Simon Glass sjg@chromium.org --- Changes since V3: * Use fstrings for format specifiers.
test/py/conftest.py | 84 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+)
diff --git a/test/py/conftest.py b/test/py/conftest.py index fc9dd3a83f..1092cb713b 100644 --- a/test/py/conftest.py +++ b/test/py/conftest.py @@ -80,6 +80,86 @@ 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_build(source_dir, build_dir, board_type, log): + """Setup the platform's build for testing capsule updates + + This generates the payload/input files needed for testing the + capsule update functionality, along with the keys for signing + the capsules. An EFI Signature List(ESL) file, which houses the + public key for capsule authentication is generated as + well. + + 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. + + All the generated files are placed under the /tmp/capsules/ + directory. + + 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') + + def gen_capsule_payloads(capsule_dir): + fname = f'{capsule_dir}u-boot.bin.old' + with open(fname, 'w') as fd: + fd.write('u-boot:Old') + + fname = f'{capsule_dir}u-boot.bin.new' + with open(fname, 'w') as fd: + fd.write('u-boot:New') + + fname = f'{capsule_dir}u-boot.env.old' + with open(fname, 'w') as fd: + fd.write('u-boot-env:Old') + + fname = f'{capsule_dir}u-boot.env.new' + with open(fname, 'w') as fd: + fd.write('u-boot-env:New') + + 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 ' + f'{capsule_sig_dir}{sig_name}.key ' + f'-out {capsule_sig_dir}{sig_name}.crt -nodes -days 365' ) + run_command(name, cmd, source_dir) + + name = 'cert-to-efi-sig-list' + cmd = ( f'cert-to-efi-sig-list {capsule_sig_dir}{sig_name}.crt ' + f'{capsule_sig_dir}{sig_name}.esl' ) + run_command(name, cmd, source_dir) + + sig_name = 'SIGNER2' + name = 'openssl' + cmd = ( 'openssl req -x509 -sha256 -newkey rsa:2048 ' + '-subj /CN=TEST_SIGNER/ -keyout ' + f'{capsule_sig_dir}{sig_name}.key ' + f'-out {capsule_sig_dir}{sig_name}.crt -nodes -days 365' ) + run_command(name, cmd, source_dir) + + gen_capsule_payloads(capsule_sig_dir) + def run_build(config, source_dir, build_dir, board_type, log): """run_build: Build U-Boot
@@ -90,6 +170,10 @@ def run_build(config, source_dir, build_dir, board_type, log): board_type (str): board_type parameter (e.g. 'sandbox') log (Logfile): Log file to use """ + capsule_boards = ( 'sandbox', 'sandbox64', 'sandbox_flattree' ) + if board_type in capsule_boards: + setup_capsule_build(source_dir, build_dir, board_type, log) + if config.getoption('buildman'): if build_dir != source_dir: dest_args = ['-o', build_dir, '-w']

The embedding of the public key EFI Signature List(ESL) file into the platform's DTB is now done at the time of u-boot build. Remove this logic from the capsule update test' configuration.
Include the public key for the sandbox and sandbox_flattree variant as part of the build.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Reviewed-by: Simon Glass sjg@chromium.org --- Changes since V3: * Rebase on top of current master to work with test configuration for version support in capsule updates.
configs/sandbox_defconfig | 1 + configs/sandbox_flattree_defconfig | 1 + test/py/tests/test_efi_capsule/conftest.py | 37 ++++---------------- test/py/tests/test_efi_capsule/signature.dts | 10 ------ 4 files changed, 9 insertions(+), 40 deletions(-) delete mode 100644 test/py/tests/test_efi_capsule/signature.dts
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 4cef6c5153..560f3317d9 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -340,6 +340,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/tests/test_efi_capsule/conftest.py b/test/py/tests/test_efi_capsule/conftest.py index 054be1ee97..99b502902e 100644 --- a/test/py/tests/test_efi_capsule/conftest.py +++ b/test/py/tests/test_efi_capsule/conftest.py @@ -25,48 +25,25 @@ def efi_capsule_data(request, u_boot_config): image_path = u_boot_config.persistent_data_dir + '/test_efi_capsule.img'
try: + capsules_path_dir = '/tmp/capsules/' # Create a target device check_call('dd if=/dev/zero of=./spi.bin bs=1MiB count=16', shell=True)
check_call('rm -rf %s' % mnt_point, shell=True) 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) + check_call('cp %s/* %s ' % (capsules_path_dir, data_dir), shell=True)
# Update dtb to add the version information check_call('cd %s; ' 'cp %s/test/py/tests/test_efi_capsule/version.dts .' % (data_dir, u_boot_config.source_dir), shell=True) + + capsule_auth_enabled = u_boot_config.buildconfig.get( + 'config_efi_capsule_authenticate') if capsule_auth_enabled: + check_call('cp %s/arch/sandbox/dts/test.dtb %s/test_sig.dtb' % + (u_boot_config.build_dir, data_dir), shell=True) check_call('cd %s; ' 'dtc -@ -I dts -O dtb -o version.dtbo version.dts; ' 'fdtoverlay -i test_sig.dtb ' 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"); - }; -};

Support has been added to the mkeficapsule tool to generate capsules by parsing the capsule parameters through a config file. Add a config file for generating capsules. These capsules will be used for testing the capsule update feature on sandbox platform.
Enable generation of capsules through the config file on the sandbox variant.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- Changes since V3: * Use fstrings for format specifiers. * Add entries for generating capsules with version parameter.
.azure-pipelines.yml | 2 + .gitlab-ci.yml | 2 + configs/sandbox_defconfig | 2 + test/py/conftest.py | 5 + .../test_efi_capsule/sandbox_capsule_cfg.txt | 162 ++++++++++++++++++ 5 files changed, 173 insertions(+) create mode 100644 test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt
diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index d732ba443d..240ee4f692 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -403,6 +403,7 @@ stages: echo -n "u-boot:New" >/tmp/capsules/u-boot.bin.new; echo -n "u-boot-env:Old" >/tmp/capsules/u-boot.env.old; echo -n "u-boot-env:New" >/tmp/capsules/u-boot.env.new; + cp test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt /tmp/capsules/; if [[ "${TEST_PY_BD}" == "sandbox" ]] || [[ "${TEST_PY_BD}" == "sandbox_flattree" ]]; then 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; @@ -600,6 +601,7 @@ stages: echo -n "u-boot:New" >/tmp/capsules/u-boot.bin.new; echo -n "u-boot-env:Old" >/tmp/capsules/u-boot.env.old; echo -n "u-boot-env:New" >/tmp/capsules/u-boot.env.new; + cp test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt /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; diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index aec6ffaf1c..42456e5f3f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -42,6 +42,7 @@ stages: - echo -n "u-boot:New" >/tmp/capsules/u-boot.bin.new; - echo -n "u-boot-env:Old" >/tmp/capsules/u-boot.env.old; - echo -n "u-boot-env:New" >/tmp/capsules/u-boot.env.new; + - cp test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt /tmp/capsules/; - if [[ "${TEST_PY_BD}" == "sandbox" ]] || [[ "${TEST_PY_BD}" == "sandbox_flattree" ]]; then 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; @@ -148,6 +149,7 @@ build all other platforms: echo -n "u-boot:New" >/tmp/capsules/u-boot.bin.new; echo -n "u-boot-env:Old" >/tmp/capsules/u-boot.env.old; echo -n "u-boot-env:New" >/tmp/capsules/u-boot.env.new; + cp test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt /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; diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 560f3317d9..f3c09f845a 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -341,6 +341,8 @@ 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_USE_CAPSULE_CFG_FILE=y CONFIG_EFI_SECURE_BOOT=y CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y diff --git a/test/py/conftest.py b/test/py/conftest.py index 1092cb713b..20b8dc1913 100644 --- a/test/py/conftest.py +++ b/test/py/conftest.py @@ -158,6 +158,11 @@ def setup_capsule_build(source_dir, build_dir, board_type, log): f'-out {capsule_sig_dir}{sig_name}.crt -nodes -days 365' ) run_command(name, cmd, source_dir)
+ capsule_cfg_file = 'test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt' + name = 'cp' + cmd = ( f'cp {capsule_cfg_file} {capsule_sig_dir}' ) + run_command(name, cmd, source_dir) + gen_capsule_payloads(capsule_sig_dir)
def run_build(config, source_dir, build_dir, board_type, log): 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..82d538dfb5 --- /dev/null +++ b/test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt @@ -0,0 +1,162 @@ +{ + 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 +} +{ + image-index: 1 + fw-version: 5 + image-guid: 09D7CF52-0720-4710-91D1-08469B7FE9C8 + payload: /tmp/capsules/u-boot.bin.new + capsule: /tmp/capsules/Test101 +} +{ + image-index: 2 + fw-version: 10 + image-guid: 5A7021F5-FEF2-48B4-AABA-832E777418C0 + payload: /tmp/capsules/u-boot.env.new + capsule: /tmp/capsules/Test102 +} +{ + image-index: 1 + fw-version: 2 + image-guid: 09D7CF52-0720-4710-91D1-08469B7FE9C8 + payload: /tmp/capsules/u-boot.bin.new + capsule: /tmp/capsules/Test103 + +} +{ + image-index: 1 + fw-version: 5 + image-guid: 3673B45D-6A7C-46F3-9E60-ADABB03F7937 + payload: /tmp/capsules/uboot_bin_env.itb + capsule: /tmp/capsules/Test104 +} +{ + image-index: 1 + fw-version: 2 + image-guid: 3673B45D-6A7C-46F3-9E60-ADABB03F7937 + payload: /tmp/capsules/uboot_bin_env.itb + capsule: /tmp/capsules/Test105 + +} +{ + image-index: 1 + monotonic-count: 1 + fw-version: 5 + 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/Test111 +} +{ + image-index: 2 + monotonic-count: 1 + fw-version: 10 + private-key: /tmp/capsules/SIGNER.key + pub-key-cert: /tmp/capsules/SIGNER.crt + image-guid: 5A7021F5-FEF2-48B4-AABA-832E777418C0 + payload: /tmp/capsules/u-boot.env.new + capsule: /tmp/capsules/Test112 +} +{ + image-index: 1 + monotonic-count: 1 + fw-version: 2 + 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/Test113 +} +{ + image-index: 1 + fw-version: 5 + 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/Test114 +} +{ + image-index: 1 + fw-version: 2 + 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/Test115 +}

Hi,
On Sat, 15 Jul 2023 at 07:46, Sughosh Ganu sughosh.ganu@linaro.org wrote:
Support has been added to the mkeficapsule tool to generate capsules by parsing the capsule parameters through a config file. Add a config file for generating capsules. These capsules will be used for testing the capsule update feature on sandbox platform.
Enable generation of capsules through the config file on the sandbox variant.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
Changes since V3:
- Use fstrings for format specifiers.
- Add entries for generating capsules with version parameter.
.azure-pipelines.yml | 2 + .gitlab-ci.yml | 2 + configs/sandbox_defconfig | 2 + test/py/conftest.py | 5 + .../test_efi_capsule/sandbox_capsule_cfg.txt | 162 ++++++++++++++++++ 5 files changed, 173 insertions(+) create mode 100644 test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt
diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index d732ba443d..240ee4f692 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -403,6 +403,7 @@ stages: echo -n "u-boot:New" >/tmp/capsules/u-boot.bin.new; echo -n "u-boot-env:Old" >/tmp/capsules/u-boot.env.old; echo -n "u-boot-env:New" >/tmp/capsules/u-boot.env.new;
cp test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt /tmp/capsules/; if [[ "${TEST_PY_BD}" == "sandbox" ]] || [[ "${TEST_PY_BD}" == "sandbox_flattree" ]]; then 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;
@@ -600,6 +601,7 @@ stages: echo -n "u-boot:New" >/tmp/capsules/u-boot.bin.new; echo -n "u-boot-env:Old" >/tmp/capsules/u-boot.env.old; echo -n "u-boot-env:New" >/tmp/capsules/u-boot.env.new;
cp test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt /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;
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index aec6ffaf1c..42456e5f3f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -42,6 +42,7 @@ stages: - echo -n "u-boot:New" >/tmp/capsules/u-boot.bin.new; - echo -n "u-boot-env:Old" >/tmp/capsules/u-boot.env.old; - echo -n "u-boot-env:New" >/tmp/capsules/u-boot.env.new;
- cp test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt /tmp/capsules/;
- if [[ "${TEST_PY_BD}" == "sandbox" ]] || [[ "${TEST_PY_BD}" == "sandbox_flattree" ]]; then 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;
@@ -148,6 +149,7 @@ build all other platforms: echo -n "u-boot:New" >/tmp/capsules/u-boot.bin.new; echo -n "u-boot-env:Old" >/tmp/capsules/u-boot.env.old; echo -n "u-boot-env:New" >/tmp/capsules/u-boot.env.new;
cp test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt /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;
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 560f3317d9..f3c09f845a 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -341,6 +341,8 @@ 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_USE_CAPSULE_CFG_FILE=y CONFIG_EFI_SECURE_BOOT=y CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y diff --git a/test/py/conftest.py b/test/py/conftest.py index 1092cb713b..20b8dc1913 100644 --- a/test/py/conftest.py +++ b/test/py/conftest.py @@ -158,6 +158,11 @@ def setup_capsule_build(source_dir, build_dir, board_type, log): f'-out {capsule_sig_dir}{sig_name}.crt -nodes -days 365' ) run_command(name, cmd, source_dir)
- capsule_cfg_file = 'test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt'
You can use cons.config.build_dir as your working directory.
- name = 'cp'
- cmd = ( f'cp {capsule_cfg_file} {capsule_sig_dir}' )
- run_command(name, cmd, source_dir)
- gen_capsule_payloads(capsule_sig_dir)
def run_build(config, source_dir, build_dir, board_type, log): 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..82d538dfb5 --- /dev/null +++ b/test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt @@ -0,0 +1,162 @@ +{
image-index: 1
image-guid: 09D7CF52-0720-4710-91D1-08469B7FE9C8
What are these? Can you at least given them a name and a description? We don't want to have GUIDs in the source code open-coded like this as they have no useful meaning.
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
+} +{
image-index: 1
fw-version: 5
image-guid: 09D7CF52-0720-4710-91D1-08469B7FE9C8
payload: /tmp/capsules/u-boot.bin.new
capsule: /tmp/capsules/Test101
+} +{
image-index: 2
fw-version: 10
image-guid: 5A7021F5-FEF2-48B4-AABA-832E777418C0
payload: /tmp/capsules/u-boot.env.new
capsule: /tmp/capsules/Test102
+} +{
image-index: 1
fw-version: 2
image-guid: 09D7CF52-0720-4710-91D1-08469B7FE9C8
payload: /tmp/capsules/u-boot.bin.new
capsule: /tmp/capsules/Test103
+} +{
image-index: 1
fw-version: 5
image-guid: 3673B45D-6A7C-46F3-9E60-ADABB03F7937
payload: /tmp/capsules/uboot_bin_env.itb
capsule: /tmp/capsules/Test104
+} +{
image-index: 1
fw-version: 2
image-guid: 3673B45D-6A7C-46F3-9E60-ADABB03F7937
payload: /tmp/capsules/uboot_bin_env.itb
capsule: /tmp/capsules/Test105
+} +{
image-index: 1
monotonic-count: 1
fw-version: 5
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/Test111
+} +{
image-index: 2
monotonic-count: 1
fw-version: 10
private-key: /tmp/capsules/SIGNER.key
pub-key-cert: /tmp/capsules/SIGNER.crt
image-guid: 5A7021F5-FEF2-48B4-AABA-832E777418C0
payload: /tmp/capsules/u-boot.env.new
capsule: /tmp/capsules/Test112
+} +{
image-index: 1
monotonic-count: 1
fw-version: 2
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/Test113
+} +{
image-index: 1
fw-version: 5
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/Test114
+} +{
image-index: 1
fw-version: 2
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/Test115
+}
2.34.1
Regards, Simon

hi Simon,
On Sun, 16 Jul 2023 at 05:12, Simon Glass sjg@chromium.org wrote:
Hi,
On Sat, 15 Jul 2023 at 07:46, Sughosh Ganu sughosh.ganu@linaro.org wrote:
Support has been added to the mkeficapsule tool to generate capsules by parsing the capsule parameters through a config file. Add a config file for generating capsules. These capsules will be used for testing the capsule update feature on sandbox platform.
Enable generation of capsules through the config file on the sandbox variant.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
Changes since V3:
- Use fstrings for format specifiers.
- Add entries for generating capsules with version parameter.
.azure-pipelines.yml | 2 + .gitlab-ci.yml | 2 + configs/sandbox_defconfig | 2 + test/py/conftest.py | 5 + .../test_efi_capsule/sandbox_capsule_cfg.txt | 162 ++++++++++++++++++ 5 files changed, 173 insertions(+) create mode 100644 test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt
diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index d732ba443d..240ee4f692 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -403,6 +403,7 @@ stages: echo -n "u-boot:New" >/tmp/capsules/u-boot.bin.new; echo -n "u-boot-env:Old" >/tmp/capsules/u-boot.env.old; echo -n "u-boot-env:New" >/tmp/capsules/u-boot.env.new;
cp test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt /tmp/capsules/; if [[ "${TEST_PY_BD}" == "sandbox" ]] || [[ "${TEST_PY_BD}" == "sandbox_flattree" ]]; then 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;
@@ -600,6 +601,7 @@ stages: echo -n "u-boot:New" >/tmp/capsules/u-boot.bin.new; echo -n "u-boot-env:Old" >/tmp/capsules/u-boot.env.old; echo -n "u-boot-env:New" >/tmp/capsules/u-boot.env.new;
cp test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt /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;
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index aec6ffaf1c..42456e5f3f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -42,6 +42,7 @@ stages: - echo -n "u-boot:New" >/tmp/capsules/u-boot.bin.new; - echo -n "u-boot-env:Old" >/tmp/capsules/u-boot.env.old; - echo -n "u-boot-env:New" >/tmp/capsules/u-boot.env.new;
- cp test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt /tmp/capsules/;
- if [[ "${TEST_PY_BD}" == "sandbox" ]] || [[ "${TEST_PY_BD}" == "sandbox_flattree" ]]; then 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;
@@ -148,6 +149,7 @@ build all other platforms: echo -n "u-boot:New" >/tmp/capsules/u-boot.bin.new; echo -n "u-boot-env:Old" >/tmp/capsules/u-boot.env.old; echo -n "u-boot-env:New" >/tmp/capsules/u-boot.env.new;
cp test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt /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;
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 560f3317d9..f3c09f845a 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -341,6 +341,8 @@ 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_USE_CAPSULE_CFG_FILE=y CONFIG_EFI_SECURE_BOOT=y CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y diff --git a/test/py/conftest.py b/test/py/conftest.py index 1092cb713b..20b8dc1913 100644 --- a/test/py/conftest.py +++ b/test/py/conftest.py @@ -158,6 +158,11 @@ def setup_capsule_build(source_dir, build_dir, board_type, log): f'-out {capsule_sig_dir}{sig_name}.crt -nodes -days 365' ) run_command(name, cmd, source_dir)
- capsule_cfg_file = 'test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt'
You can use cons.config.build_dir as your working directory.
Sorry, I did not get this comment. This file is in the source directory, and does not get reflected in the build_dir. Which is why this needs to be copied to a known location(/tmp/capsules/).
- name = 'cp'
- cmd = ( f'cp {capsule_cfg_file} {capsule_sig_dir}' )
- run_command(name, cmd, source_dir)
- gen_capsule_payloads(capsule_sig_dir)
def run_build(config, source_dir, build_dir, board_type, log): 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..82d538dfb5 --- /dev/null +++ b/test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt @@ -0,0 +1,162 @@ +{
image-index: 1
image-guid: 09D7CF52-0720-4710-91D1-08469B7FE9C8
What are these? Can you at least given them a name and a description? We don't want to have GUIDs in the source code open-coded like this as they have no useful meaning.
I will add a comment against the GUID values.
-sughosh
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
+} +{
image-index: 1
fw-version: 5
image-guid: 09D7CF52-0720-4710-91D1-08469B7FE9C8
payload: /tmp/capsules/u-boot.bin.new
capsule: /tmp/capsules/Test101
+} +{
image-index: 2
fw-version: 10
image-guid: 5A7021F5-FEF2-48B4-AABA-832E777418C0
payload: /tmp/capsules/u-boot.env.new
capsule: /tmp/capsules/Test102
+} +{
image-index: 1
fw-version: 2
image-guid: 09D7CF52-0720-4710-91D1-08469B7FE9C8
payload: /tmp/capsules/u-boot.bin.new
capsule: /tmp/capsules/Test103
+} +{
image-index: 1
fw-version: 5
image-guid: 3673B45D-6A7C-46F3-9E60-ADABB03F7937
payload: /tmp/capsules/uboot_bin_env.itb
capsule: /tmp/capsules/Test104
+} +{
image-index: 1
fw-version: 2
image-guid: 3673B45D-6A7C-46F3-9E60-ADABB03F7937
payload: /tmp/capsules/uboot_bin_env.itb
capsule: /tmp/capsules/Test105
+} +{
image-index: 1
monotonic-count: 1
fw-version: 5
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/Test111
+} +{
image-index: 2
monotonic-count: 1
fw-version: 10
private-key: /tmp/capsules/SIGNER.key
pub-key-cert: /tmp/capsules/SIGNER.crt
image-guid: 5A7021F5-FEF2-48B4-AABA-832E777418C0
payload: /tmp/capsules/u-boot.env.new
capsule: /tmp/capsules/Test112
+} +{
image-index: 1
monotonic-count: 1
fw-version: 2
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/Test113
+} +{
image-index: 1
fw-version: 5
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/Test114
+} +{
image-index: 1
fw-version: 2
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/Test115
+}
2.34.1
Regards, Simon

The EFI capsule files can now be generated as part of u-boot build. This is done through binman. Add capsule entry nodes in the u-boot.dtsi for the sandbox architecture for generating the capsules. Remove the corresponding generation of capsules from the capsule update conftest file.
The capsules are generated through the config file for the sandbox variant, and through explicit parameters for the sandbox_flattree variant.
Also generate the FIT image used for testing the capsule update feature on the sandbox_flattree variant through binman. Remove the now superfluous its file which was used for generating this FIT image.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- Changes since V3: * Use blob nodes instead of incbin for including the binaries in FIT image. * Enable generation of capsules with versioning support.
arch/sandbox/dts/u-boot.dtsi | 265 ++++++++++++++++++ test/py/tests/test_efi_capsule/conftest.py | 127 --------- .../tests/test_efi_capsule/uboot_bin_env.its | 36 --- 3 files changed, 265 insertions(+), 163 deletions(-) delete mode 100644 test/py/tests/test_efi_capsule/uboot_bin_env.its
diff --git a/arch/sandbox/dts/u-boot.dtsi b/arch/sandbox/dts/u-boot.dtsi index 60bd004937..7b0250ac81 100644 --- a/arch/sandbox/dts/u-boot.dtsi +++ b/arch/sandbox/dts/u-boot.dtsi @@ -13,5 +13,270 @@ capsule-key = /incbin/(CONFIG_EFI_CAPSULE_ESL_FILE); }; #endif + + binman: binman { + multiple-images; + }; +}; + +&binman { + itb { + filename = "/tmp/capsules/uboot_bin_env.itb"; + + fit { + description = "Automatic U-Boot environment update"; + #address-cells = <2>; + + images { + u-boot-bin { + description = "U-Boot binary on SPI Flash"; + compression = "none"; + type = "firmware"; + arch = "sandbox"; + load = <0>; + blob { + filename = "/tmp/capsules/u-boot.bin.new"; + }; + + hash-1 { + algo = "sha1"; + }; + }; + u-boot-env { + description = "U-Boot environment on SPI Flash"; + compression = "none"; + type = "firmware"; + arch = "sandbox"; + load = <0>; + blob { + filename = "/tmp/capsules/u-boot.env.new"; + }; + + hash-1 { + algo = "sha1"; + }; + }; + }; + }; + }; + +#ifdef CONFIG_EFI_USE_CAPSULE_CFG_FILE + capsule1 { + capsule { + cfg-file = CONFIG_EFI_CAPSULE_CFG_FILE; + }; + }; +#else + capsule2 { + capsule { + image-index = <0x1>; + image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8"; + filename = "/tmp/capsules/u-boot.bin.new"; + capsule = "/tmp/capsules/Test01"; + }; + }; + + capsule3 { + capsule { + image-index = <0x2>; + image-type-id = "5A7021F5-FEF2-48B4-AABA-832E777418C0"; + filename = "/tmp/capsules/u-boot.env.new"; + capsule = "/tmp/capsules/Test02"; + }; + }; + + capsule4 { + capsule { + image-index = <0x1>; + image-type-id = "058B7D83-50D5-4C47-A195-60D86AD341C4"; + filename = "/tmp/capsules/u-boot.bin.new"; + capsule = "/tmp/capsules/Test03"; + }; + }; + + capsule5 { + capsule { + image-index = <0x1>; + image-type-id = "3673B45D-6A7C-46F3-9E60-ADABB03F7937"; + filename = "/tmp/capsules/uboot_bin_env.itb"; + capsule = "/tmp/capsules/Test04"; + }; + }; + + capsule6 { + capsule { + image-index = <0x1>; + image-type-id = "058B7D83-50D5-4C47-A195-60D86AD341C4"; + filename = "/tmp/capsules/uboot_bin_env.itb"; + capsule = "/tmp/capsules/Test05"; + }; + }; + + capsule7 { + capsule { + image-index = <0x1>; + fw-version = <0x5>; + image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8"; + filename = "/tmp/capsules/u-boot.bin.new"; + capsule = "/tmp/capsules/Test101"; + }; + }; + + capsule8 { + capsule { + image-index = <0x2>; + fw-version = <0xa>; + image-type-id = "5A7021F5-FEF2-48B4-AABA-832E777418C0"; + filename = "/tmp/capsules/u-boot.env.new"; + capsule = "/tmp/capsules/Test102"; + }; + }; + + capsule9 { + capsule { + image-index = <0x1>; + fw-version = <0x2>; + image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8"; + filename = "/tmp/capsules/u-boot.bin.new"; + capsule = "/tmp/capsules/Test103"; + }; + }; + + capsule10 { + capsule { + image-index = <0x1>; + fw-version = <0x5>; + image-type-id = "3673B45D-6A7C-46F3-9E60-ADABB03F7937"; + filename = "/tmp/capsules/uboot_bin_env.itb"; + capsule = "/tmp/capsules/Test104"; + }; + }; + + capsule11 { + capsule { + image-index = <0x1>; + fw-version = <0x2>; + image-type-id = "3673B45D-6A7C-46F3-9E60-ADABB03F7937"; + filename = "/tmp/capsules/uboot_bin_env.itb"; + capsule = "/tmp/capsules/Test105"; + }; + }; + +#ifdef CONFIG_EFI_CAPSULE_AUTHENTICATE + capsule12 { + capsule { + image-index = <0x1>; + image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8"; + private-key = "/tmp/capsules/SIGNER.key"; + pub-key-cert = "/tmp/capsules/SIGNER.crt"; + monotonic-count = <0x1>; + filename = "/tmp/capsules/u-boot.bin.new"; + capsule = "/tmp/capsules/Test11"; + }; + }; + + capsule13 { + capsule { + image-index = <0x1>; + image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8"; + private-key = "/tmp/capsules/SIGNER2.key"; + pub-key-cert = "/tmp/capsules/SIGNER2.crt"; + monotonic-count = <0x1>; + filename = "/tmp/capsules/u-boot.bin.new"; + capsule = "/tmp/capsules/Test12"; + }; + }; + + capsule14 { + capsule { + image-index = <0x1>; + image-type-id = "3673B45D-6A7C-46F3-9E60-ADABB03F7937"; + private-key = "/tmp/capsules/SIGNER.key"; + pub-key-cert = "/tmp/capsules/SIGNER.crt"; + monotonic-count = <0x1>; + filename = "/tmp/capsules/uboot_bin_env.itb"; + capsule = "/tmp/capsules/Test13"; + }; + }; + + capsule15 { + capsule { + image-index = <0x1>; + image-type-id = "3673B45D-6A7C-46F3-9E60-ADABB03F7937"; + private-key = "/tmp/capsules/SIGNER2.key"; + pub-key-cert = "/tmp/capsules/SIGNER2.crt"; + monotonic-count = <0x1>; + filename = "/tmp/capsules/uboot_bin_env.itb"; + capsule = "/tmp/capsules/Test14"; + }; + }; + + capsule16 { + capsule { + image-index = <0x1>; + fw-version = <0x5>; + image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8"; + private-key = "/tmp/capsules/SIGNER.key"; + pub-key-cert = "/tmp/capsules/SIGNER.crt"; + monotonic-count = <0x1>; + filename = "/tmp/capsules/u-boot.bin.new"; + capsule = "/tmp/capsules/Test111"; + }; + }; + + capsule17 { + capsule { + image-index = <0x2>; + fw-version = <0xa>; + image-type-id = "5A7021F5-FEF2-48B4-AABA-832E777418C0"; + private-key = "/tmp/capsules/SIGNER.key"; + pub-key-cert = "/tmp/capsules/SIGNER.crt"; + monotonic-count = <0x1>; + filename = "/tmp/capsules/u-boot.env.new"; + capsule = "/tmp/capsules/Test112"; + }; + }; + + capsule18 { + capsule { + image-index = <0x1>; + fw-version = <0x2>; + image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8"; + private-key = "/tmp/capsules/SIGNER.key"; + pub-key-cert = "/tmp/capsules/SIGNER.crt"; + monotonic-count = <0x1>; + filename = "/tmp/capsules/u-boot.bin.new"; + capsule = "/tmp/capsules/Test113"; + }; + }; + + capsule19 { + capsule { + image-index = <0x1>; + fw-version = <0x5>; + image-type-id = "3673B45D-6A7C-46F3-9E60-ADABB03F7937"; + private-key = "/tmp/capsules/SIGNER.key"; + pub-key-cert = "/tmp/capsules/SIGNER.crt"; + monotonic-count = <0x1>; + filename = "/tmp/capsules/uboot_bin_env.itb"; + capsule = "/tmp/capsules/Test114"; + }; + }; + + capsule20 { + capsule { + image-index = <0x1>; + fw-version = <0x2>; + image-type-id = "3673B45D-6A7C-46F3-9E60-ADABB03F7937"; + private-key = "/tmp/capsules/SIGNER.key"; + pub-key-cert = "/tmp/capsules/SIGNER.crt"; + monotonic-count = <0x1>; + filename = "/tmp/capsules/uboot_bin_env.itb"; + capsule = "/tmp/capsules/Test115"; + }; + }; + +#endif /* CONFIG_EFI_CAPSULE_AUTHENTICATE */ +#endif /* CONFIG_EFI_USE_CAPSULE_CFG_FILE */ }; #endif /* CONFIG_EFI_HAVE_CAPSULE_SUPPORT */ diff --git a/test/py/tests/test_efi_capsule/conftest.py b/test/py/tests/test_efi_capsule/conftest.py index 99b502902e..ca44249a38 100644 --- a/test/py/tests/test_efi_capsule/conftest.py +++ b/test/py/tests/test_efi_capsule/conftest.py @@ -56,133 +56,6 @@ def efi_capsule_data(request, u_boot_config): '-o test_ver.dtb version.dtbo' % (data_dir, u_boot_config.build_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, - shell=True) - check_call('sed -e "s?BINFILE1?u-boot.bin.new?" -e "s?BINFILE2?u-boot.env.new?" %s/test/py/tests/test_efi_capsule/uboot_bin_env.its > %s/uboot_bin_env.its' % - (u_boot_config.source_dir, data_dir), - shell=True) - 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) - check_call('cd %s; %s/tools/mkeficapsule --index 1 --fw-version 5 ' - '--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot.bin.new Test101' % - (data_dir, u_boot_config.build_dir), - shell=True) - check_call('cd %s; %s/tools/mkeficapsule --index 2 --fw-version 10 ' - '--guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 u-boot.env.new Test102' % - (data_dir, u_boot_config.build_dir), - shell=True) - check_call('cd %s; %s/tools/mkeficapsule --index 1 --fw-version 2 ' - '--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot.bin.new Test103' % - (data_dir, u_boot_config.build_dir), - shell=True) - check_call('cd %s; %s/tools/mkeficapsule --index 1 --fw-version 5 ' - '--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 uboot_bin_env.itb Test104' % - (data_dir, u_boot_config.build_dir), - shell=True) - check_call('cd %s; %s/tools/mkeficapsule --index 1 --fw-version 2 ' - '--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 uboot_bin_env.itb Test105' % - (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) - # raw firmware signed with proper key with version information - check_call('cd %s; ' - '%s/tools/mkeficapsule --index 1 --monotonic-count 1 ' - '--fw-version 5 ' - '--private-key SIGNER.key --certificate SIGNER.crt ' - '--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 ' - 'u-boot.bin.new Test111' - % (data_dir, u_boot_config.build_dir), - shell=True) - # raw firmware signed with proper key with version information - check_call('cd %s; ' - '%s/tools/mkeficapsule --index 2 --monotonic-count 1 ' - '--fw-version 10 ' - '--private-key SIGNER.key --certificate SIGNER.crt ' - '--guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 ' - 'u-boot.env.new Test112' - % (data_dir, u_boot_config.build_dir), - shell=True) - # raw firmware signed with proper key with lower version information - check_call('cd %s; ' - '%s/tools/mkeficapsule --index 1 --monotonic-count 1 ' - '--fw-version 2 ' - '--private-key SIGNER.key --certificate SIGNER.crt ' - '--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 ' - 'u-boot.bin.new Test113' - % (data_dir, u_boot_config.build_dir), - shell=True) - # FIT firmware signed with proper key with version information - check_call('cd %s; ' - '%s/tools/mkeficapsule --index 1 --monotonic-count 1 ' - '--fw-version 5 ' - '--private-key SIGNER.key --certificate SIGNER.crt ' - '--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 ' - 'uboot_bin_env.itb Test114' - % (data_dir, u_boot_config.build_dir), - shell=True) - # FIT firmware signed with proper key with lower version information - check_call('cd %s; ' - '%s/tools/mkeficapsule --index 1 --monotonic-count 1 ' - '--fw-version 2 ' - '--private-key SIGNER.key --certificate SIGNER.crt ' - '--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 ' - 'uboot_bin_env.itb Test115' - % (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' % (mnt_point, image_path), shell=True) diff --git a/test/py/tests/test_efi_capsule/uboot_bin_env.its b/test/py/tests/test_efi_capsule/uboot_bin_env.its deleted file mode 100644 index fc65907481..0000000000 --- a/test/py/tests/test_efi_capsule/uboot_bin_env.its +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Automatic software update for U-Boot - * Make sure the flashing addresses ('load' prop) is correct for your board! - */ - -/dts-v1/; - -/ { - description = "Automatic U-Boot environment update"; - #address-cells = <2>; - - images { - u-boot-bin { - description = "U-Boot binary on SPI Flash"; - data = /incbin/("BINFILE1"); - compression = "none"; - type = "firmware"; - arch = "sandbox"; - load = <0>; - hash-1 { - algo = "sha1"; - }; - }; - u-boot-env { - description = "U-Boot environment on SPI Flash"; - data = /incbin/("BINFILE2"); - compression = "none"; - type = "firmware"; - arch = "sandbox"; - load = <0>; - hash-1 { - algo = "sha1"; - }; - }; - }; -};

Hi Sughosh,
On Sat, 15 Jul 2023 at 07:46, Sughosh Ganu sughosh.ganu@linaro.org wrote:
The EFI capsule files can now be generated as part of u-boot build. This is done through binman. Add capsule entry nodes in the u-boot.dtsi for the sandbox architecture for generating the capsules. Remove the corresponding generation of capsules from the capsule update conftest file.
The capsules are generated through the config file for the sandbox variant, and through explicit parameters for the sandbox_flattree variant.
Also generate the FIT image used for testing the capsule update feature on the sandbox_flattree variant through binman. Remove the now superfluous its file which was used for generating this FIT image.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
Changes since V3:
- Use blob nodes instead of incbin for including the binaries in FIT image.
- Enable generation of capsules with versioning support.
arch/sandbox/dts/u-boot.dtsi | 265 ++++++++++++++++++ test/py/tests/test_efi_capsule/conftest.py | 127 --------- .../tests/test_efi_capsule/uboot_bin_env.its | 36 --- 3 files changed, 265 insertions(+), 163 deletions(-) delete mode 100644 test/py/tests/test_efi_capsule/uboot_bin_env.its
diff --git a/arch/sandbox/dts/u-boot.dtsi b/arch/sandbox/dts/u-boot.dtsi index 60bd004937..7b0250ac81 100644 --- a/arch/sandbox/dts/u-boot.dtsi +++ b/arch/sandbox/dts/u-boot.dtsi @@ -13,5 +13,270 @@ capsule-key = /incbin/(CONFIG_EFI_CAPSULE_ESL_FILE); }; #endif
binman: binman {
multiple-images;
};
+};
+&binman {
itb {
filename = "/tmp/capsules/uboot_bin_env.itb";
fit {
description = "Automatic U-Boot environment update";
#address-cells = <2>;
images {
u-boot-bin {
description = "U-Boot binary on SPI Flash";
compression = "none";
type = "firmware";
arch = "sandbox";
load = <0>;
blob {
filename = "/tmp/capsules/u-boot.bin.new";
};
hash-1 {
algo = "sha1";
};
};
u-boot-env {
description = "U-Boot environment on SPI Flash";
compression = "none";
type = "firmware";
arch = "sandbox";
load = <0>;
blob {
filename = "/tmp/capsules/u-boot.env.new";
};
hash-1 {
algo = "sha1";
};
};
};
};
};
+#ifdef CONFIG_EFI_USE_CAPSULE_CFG_FILE
capsule1 {
capsule {
cfg-file = CONFIG_EFI_CAPSULE_CFG_FILE;
};
};
+#else
capsule2 {
capsule {
image-index = <0x1>;
image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8";
We seem to have a persistent problem with these appearing in the source code.
Perhaps you could add them to a header file and use GUID_MEANINGFUL_NAME here instead (also below).
In general, GUIDs should not be open-coded.
filename = "/tmp/capsules/u-boot.bin.new";
capsule = "/tmp/capsules/Test01";
There is something odd here. You should not need to specify an absolute pathname and should not use /tmp
};
};
capsule3 {
capsule {
image-index = <0x2>;
image-type-id = "5A7021F5-FEF2-48B4-AABA-832E777418C0";
filename = "/tmp/capsules/u-boot.env.new";
capsule = "/tmp/capsules/Test02";
};
};
capsule4 {
capsule {
image-index = <0x1>;
image-type-id = "058B7D83-50D5-4C47-A195-60D86AD341C4";
filename = "/tmp/capsules/u-boot.bin.new";
capsule = "/tmp/capsules/Test03";
};
};
capsule5 {
capsule {
image-index = <0x1>;
image-type-id = "3673B45D-6A7C-46F3-9E60-ADABB03F7937";
filename = "/tmp/capsules/uboot_bin_env.itb";
capsule = "/tmp/capsules/Test04";
};
};
capsule6 {
capsule {
image-index = <0x1>;
image-type-id = "058B7D83-50D5-4C47-A195-60D86AD341C4";
filename = "/tmp/capsules/uboot_bin_env.itb";
capsule = "/tmp/capsules/Test05";
};
};
capsule7 {
capsule {
image-index = <0x1>;
fw-version = <0x5>;
image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8";
filename = "/tmp/capsules/u-boot.bin.new";
capsule = "/tmp/capsules/Test101";
};
};
capsule8 {
capsule {
image-index = <0x2>;
fw-version = <0xa>;
image-type-id = "5A7021F5-FEF2-48B4-AABA-832E777418C0";
filename = "/tmp/capsules/u-boot.env.new";
capsule = "/tmp/capsules/Test102";
};
};
capsule9 {
capsule {
image-index = <0x1>;
fw-version = <0x2>;
image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8";
filename = "/tmp/capsules/u-boot.bin.new";
capsule = "/tmp/capsules/Test103";
};
};
capsule10 {
capsule {
image-index = <0x1>;
fw-version = <0x5>;
image-type-id = "3673B45D-6A7C-46F3-9E60-ADABB03F7937";
filename = "/tmp/capsules/uboot_bin_env.itb";
capsule = "/tmp/capsules/Test104";
};
};
capsule11 {
capsule {
image-index = <0x1>;
fw-version = <0x2>;
image-type-id = "3673B45D-6A7C-46F3-9E60-ADABB03F7937";
filename = "/tmp/capsules/uboot_bin_env.itb";
capsule = "/tmp/capsules/Test105";
};
};
+#ifdef CONFIG_EFI_CAPSULE_AUTHENTICATE
capsule12 {
capsule {
image-index = <0x1>;
image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8";
private-key = "/tmp/capsules/SIGNER.key";
pub-key-cert = "/tmp/capsules/SIGNER.crt";
monotonic-count = <0x1>;
filename = "/tmp/capsules/u-boot.bin.new";
capsule = "/tmp/capsules/Test11";
};
};
capsule13 {
capsule {
image-index = <0x1>;
image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8";
private-key = "/tmp/capsules/SIGNER2.key";
pub-key-cert = "/tmp/capsules/SIGNER2.crt";
monotonic-count = <0x1>;
filename = "/tmp/capsules/u-boot.bin.new";
capsule = "/tmp/capsules/Test12";
};
};
capsule14 {
capsule {
image-index = <0x1>;
image-type-id = "3673B45D-6A7C-46F3-9E60-ADABB03F7937";
private-key = "/tmp/capsules/SIGNER.key";
pub-key-cert = "/tmp/capsules/SIGNER.crt";
monotonic-count = <0x1>;
filename = "/tmp/capsules/uboot_bin_env.itb";
capsule = "/tmp/capsules/Test13";
};
};
capsule15 {
capsule {
image-index = <0x1>;
image-type-id = "3673B45D-6A7C-46F3-9E60-ADABB03F7937";
private-key = "/tmp/capsules/SIGNER2.key";
pub-key-cert = "/tmp/capsules/SIGNER2.crt";
monotonic-count = <0x1>;
filename = "/tmp/capsules/uboot_bin_env.itb";
capsule = "/tmp/capsules/Test14";
};
};
capsule16 {
capsule {
image-index = <0x1>;
fw-version = <0x5>;
image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8";
private-key = "/tmp/capsules/SIGNER.key";
pub-key-cert = "/tmp/capsules/SIGNER.crt";
monotonic-count = <0x1>;
filename = "/tmp/capsules/u-boot.bin.new";
capsule = "/tmp/capsules/Test111";
};
};
capsule17 {
capsule {
image-index = <0x2>;
fw-version = <0xa>;
image-type-id = "5A7021F5-FEF2-48B4-AABA-832E777418C0";
private-key = "/tmp/capsules/SIGNER.key";
pub-key-cert = "/tmp/capsules/SIGNER.crt";
monotonic-count = <0x1>;
filename = "/tmp/capsules/u-boot.env.new";
capsule = "/tmp/capsules/Test112";
};
};
capsule18 {
capsule {
image-index = <0x1>;
fw-version = <0x2>;
image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8";
private-key = "/tmp/capsules/SIGNER.key";
pub-key-cert = "/tmp/capsules/SIGNER.crt";
monotonic-count = <0x1>;
filename = "/tmp/capsules/u-boot.bin.new";
capsule = "/tmp/capsules/Test113";
};
};
capsule19 {
capsule {
image-index = <0x1>;
fw-version = <0x5>;
image-type-id = "3673B45D-6A7C-46F3-9E60-ADABB03F7937";
private-key = "/tmp/capsules/SIGNER.key";
pub-key-cert = "/tmp/capsules/SIGNER.crt";
monotonic-count = <0x1>;
filename = "/tmp/capsules/uboot_bin_env.itb";
capsule = "/tmp/capsules/Test114";
};
};
capsule20 {
capsule {
image-index = <0x1>;
fw-version = <0x2>;
image-type-id = "3673B45D-6A7C-46F3-9E60-ADABB03F7937";
private-key = "/tmp/capsules/SIGNER.key";
pub-key-cert = "/tmp/capsules/SIGNER.crt";
monotonic-count = <0x1>;
filename = "/tmp/capsules/uboot_bin_env.itb";
capsule = "/tmp/capsules/Test115";
};
};
+#endif /* CONFIG_EFI_CAPSULE_AUTHENTICATE */ +#endif /* CONFIG_EFI_USE_CAPSULE_CFG_FILE */ }; #endif /* CONFIG_EFI_HAVE_CAPSULE_SUPPORT */ diff --git a/test/py/tests/test_efi_capsule/conftest.py b/test/py/tests/test_efi_capsule/conftest.py index 99b502902e..ca44249a38 100644 --- a/test/py/tests/test_efi_capsule/conftest.py +++ b/test/py/tests/test_efi_capsule/conftest.py @@ -56,133 +56,6 @@ def efi_capsule_data(request, u_boot_config): '-o test_ver.dtb version.dtbo' % (data_dir, u_boot_config.build_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,
shell=True)
check_call('sed -e \"s?BINFILE1?u-boot.bin.new?\" -e \"s?BINFILE2?u-boot.env.new?\" %s/test/py/tests/test_efi_capsule/uboot_bin_env.its > %s/uboot_bin_env.its' %
(u_boot_config.source_dir, data_dir),
shell=True)
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)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --fw-version 5 '
'--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot.bin.new Test101' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 2 --fw-version 10 '
'--guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 u-boot.env.new Test102' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --fw-version 2 '
'--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot.bin.new Test103' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --fw-version 5 '
'--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 uboot_bin_env.itb Test104' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --fw-version 2 '
'--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 uboot_bin_env.itb Test105' %
(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)
# raw firmware signed with proper key with version information
check_call('cd %s; '
'%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
'--fw-version 5 '
'--private-key SIGNER.key --certificate SIGNER.crt '
'--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 '
'u-boot.bin.new Test111'
% (data_dir, u_boot_config.build_dir),
shell=True)
# raw firmware signed with proper key with version information
check_call('cd %s; '
'%s/tools/mkeficapsule --index 2 --monotonic-count 1 '
'--fw-version 10 '
'--private-key SIGNER.key --certificate SIGNER.crt '
'--guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 '
'u-boot.env.new Test112'
% (data_dir, u_boot_config.build_dir),
shell=True)
# raw firmware signed with proper key with lower version information
check_call('cd %s; '
'%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
'--fw-version 2 '
'--private-key SIGNER.key --certificate SIGNER.crt '
'--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 '
'u-boot.bin.new Test113'
% (data_dir, u_boot_config.build_dir),
shell=True)
# FIT firmware signed with proper key with version information
check_call('cd %s; '
'%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
'--fw-version 5 '
'--private-key SIGNER.key --certificate SIGNER.crt '
'--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 '
'uboot_bin_env.itb Test114'
% (data_dir, u_boot_config.build_dir),
shell=True)
# FIT firmware signed with proper key with lower version information
check_call('cd %s; '
'%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
'--fw-version 2 '
'--private-key SIGNER.key --certificate SIGNER.crt '
'--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 '
'uboot_bin_env.itb Test115'
% (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' % (mnt_point, image_path), shell=True)
diff --git a/test/py/tests/test_efi_capsule/uboot_bin_env.its b/test/py/tests/test_efi_capsule/uboot_bin_env.its deleted file mode 100644 index fc65907481..0000000000 --- a/test/py/tests/test_efi_capsule/uboot_bin_env.its +++ /dev/null @@ -1,36 +0,0 @@ -/*
- Automatic software update for U-Boot
- Make sure the flashing addresses ('load' prop) is correct for your board!
- */
-/dts-v1/;
-/ {
description = "Automatic U-Boot environment update";
#address-cells = <2>;
images {
u-boot-bin {
description = "U-Boot binary on SPI Flash";
data = /incbin/("BINFILE1");
compression = "none";
type = "firmware";
arch = "sandbox";
load = <0>;
hash-1 {
algo = "sha1";
};
};
u-boot-env {
description = "U-Boot environment on SPI Flash";
data = /incbin/("BINFILE2");
compression = "none";
type = "firmware";
arch = "sandbox";
load = <0>;
hash-1 {
algo = "sha1";
};
};
};
-};
2.34.1
REgards, Simon

hi Simon,
On Sun, 16 Jul 2023 at 05:12, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Sat, 15 Jul 2023 at 07:46, Sughosh Ganu sughosh.ganu@linaro.org wrote:
The EFI capsule files can now be generated as part of u-boot build. This is done through binman. Add capsule entry nodes in the u-boot.dtsi for the sandbox architecture for generating the capsules. Remove the corresponding generation of capsules from the capsule update conftest file.
The capsules are generated through the config file for the sandbox variant, and through explicit parameters for the sandbox_flattree variant.
Also generate the FIT image used for testing the capsule update feature on the sandbox_flattree variant through binman. Remove the now superfluous its file which was used for generating this FIT image.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
Changes since V3:
- Use blob nodes instead of incbin for including the binaries in FIT image.
- Enable generation of capsules with versioning support.
arch/sandbox/dts/u-boot.dtsi | 265 ++++++++++++++++++ test/py/tests/test_efi_capsule/conftest.py | 127 --------- .../tests/test_efi_capsule/uboot_bin_env.its | 36 --- 3 files changed, 265 insertions(+), 163 deletions(-) delete mode 100644 test/py/tests/test_efi_capsule/uboot_bin_env.its
diff --git a/arch/sandbox/dts/u-boot.dtsi b/arch/sandbox/dts/u-boot.dtsi index 60bd004937..7b0250ac81 100644 --- a/arch/sandbox/dts/u-boot.dtsi +++ b/arch/sandbox/dts/u-boot.dtsi @@ -13,5 +13,270 @@ capsule-key = /incbin/(CONFIG_EFI_CAPSULE_ESL_FILE); }; #endif
binman: binman {
multiple-images;
};
+};
+&binman {
itb {
filename = "/tmp/capsules/uboot_bin_env.itb";
fit {
description = "Automatic U-Boot environment update";
#address-cells = <2>;
images {
u-boot-bin {
description = "U-Boot binary on SPI Flash";
compression = "none";
type = "firmware";
arch = "sandbox";
load = <0>;
blob {
filename = "/tmp/capsules/u-boot.bin.new";
};
hash-1 {
algo = "sha1";
};
};
u-boot-env {
description = "U-Boot environment on SPI Flash";
compression = "none";
type = "firmware";
arch = "sandbox";
load = <0>;
blob {
filename = "/tmp/capsules/u-boot.env.new";
};
hash-1 {
algo = "sha1";
};
};
};
};
};
+#ifdef CONFIG_EFI_USE_CAPSULE_CFG_FILE
capsule1 {
capsule {
cfg-file = CONFIG_EFI_CAPSULE_CFG_FILE;
};
};
+#else
capsule2 {
capsule {
image-index = <0x1>;
image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8";
We seem to have a persistent problem with these appearing in the source code.
Perhaps you could add them to a header file and use GUID_MEANINGFUL_NAME here instead (also below).
In general, GUIDs should not be open-coded.
Okay. Will it be okay if I add these to a sandbox_capule.h. Earlier, I had similar GUID macros in the sandbox config header, and you had asked me to move them to the board file.
filename = "/tmp/capsules/u-boot.bin.new";
capsule = "/tmp/capsules/Test01";
There is something odd here. You should not need to specify an absolute pathname and should not use /tmp
The /tmp/capsules/ directory is being used for collating all the capsule testing related files. Both the input files as well as the output capsule files are being put under this directory. Do you see any issue with using this directory for the capsule files?
-sughosh
};
};
capsule3 {
capsule {
image-index = <0x2>;
image-type-id = "5A7021F5-FEF2-48B4-AABA-832E777418C0";
filename = "/tmp/capsules/u-boot.env.new";
capsule = "/tmp/capsules/Test02";
};
};
capsule4 {
capsule {
image-index = <0x1>;
image-type-id = "058B7D83-50D5-4C47-A195-60D86AD341C4";
filename = "/tmp/capsules/u-boot.bin.new";
capsule = "/tmp/capsules/Test03";
};
};
capsule5 {
capsule {
image-index = <0x1>;
image-type-id = "3673B45D-6A7C-46F3-9E60-ADABB03F7937";
filename = "/tmp/capsules/uboot_bin_env.itb";
capsule = "/tmp/capsules/Test04";
};
};
capsule6 {
capsule {
image-index = <0x1>;
image-type-id = "058B7D83-50D5-4C47-A195-60D86AD341C4";
filename = "/tmp/capsules/uboot_bin_env.itb";
capsule = "/tmp/capsules/Test05";
};
};
capsule7 {
capsule {
image-index = <0x1>;
fw-version = <0x5>;
image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8";
filename = "/tmp/capsules/u-boot.bin.new";
capsule = "/tmp/capsules/Test101";
};
};
capsule8 {
capsule {
image-index = <0x2>;
fw-version = <0xa>;
image-type-id = "5A7021F5-FEF2-48B4-AABA-832E777418C0";
filename = "/tmp/capsules/u-boot.env.new";
capsule = "/tmp/capsules/Test102";
};
};
capsule9 {
capsule {
image-index = <0x1>;
fw-version = <0x2>;
image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8";
filename = "/tmp/capsules/u-boot.bin.new";
capsule = "/tmp/capsules/Test103";
};
};
capsule10 {
capsule {
image-index = <0x1>;
fw-version = <0x5>;
image-type-id = "3673B45D-6A7C-46F3-9E60-ADABB03F7937";
filename = "/tmp/capsules/uboot_bin_env.itb";
capsule = "/tmp/capsules/Test104";
};
};
capsule11 {
capsule {
image-index = <0x1>;
fw-version = <0x2>;
image-type-id = "3673B45D-6A7C-46F3-9E60-ADABB03F7937";
filename = "/tmp/capsules/uboot_bin_env.itb";
capsule = "/tmp/capsules/Test105";
};
};
+#ifdef CONFIG_EFI_CAPSULE_AUTHENTICATE
capsule12 {
capsule {
image-index = <0x1>;
image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8";
private-key = "/tmp/capsules/SIGNER.key";
pub-key-cert = "/tmp/capsules/SIGNER.crt";
monotonic-count = <0x1>;
filename = "/tmp/capsules/u-boot.bin.new";
capsule = "/tmp/capsules/Test11";
};
};
capsule13 {
capsule {
image-index = <0x1>;
image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8";
private-key = "/tmp/capsules/SIGNER2.key";
pub-key-cert = "/tmp/capsules/SIGNER2.crt";
monotonic-count = <0x1>;
filename = "/tmp/capsules/u-boot.bin.new";
capsule = "/tmp/capsules/Test12";
};
};
capsule14 {
capsule {
image-index = <0x1>;
image-type-id = "3673B45D-6A7C-46F3-9E60-ADABB03F7937";
private-key = "/tmp/capsules/SIGNER.key";
pub-key-cert = "/tmp/capsules/SIGNER.crt";
monotonic-count = <0x1>;
filename = "/tmp/capsules/uboot_bin_env.itb";
capsule = "/tmp/capsules/Test13";
};
};
capsule15 {
capsule {
image-index = <0x1>;
image-type-id = "3673B45D-6A7C-46F3-9E60-ADABB03F7937";
private-key = "/tmp/capsules/SIGNER2.key";
pub-key-cert = "/tmp/capsules/SIGNER2.crt";
monotonic-count = <0x1>;
filename = "/tmp/capsules/uboot_bin_env.itb";
capsule = "/tmp/capsules/Test14";
};
};
capsule16 {
capsule {
image-index = <0x1>;
fw-version = <0x5>;
image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8";
private-key = "/tmp/capsules/SIGNER.key";
pub-key-cert = "/tmp/capsules/SIGNER.crt";
monotonic-count = <0x1>;
filename = "/tmp/capsules/u-boot.bin.new";
capsule = "/tmp/capsules/Test111";
};
};
capsule17 {
capsule {
image-index = <0x2>;
fw-version = <0xa>;
image-type-id = "5A7021F5-FEF2-48B4-AABA-832E777418C0";
private-key = "/tmp/capsules/SIGNER.key";
pub-key-cert = "/tmp/capsules/SIGNER.crt";
monotonic-count = <0x1>;
filename = "/tmp/capsules/u-boot.env.new";
capsule = "/tmp/capsules/Test112";
};
};
capsule18 {
capsule {
image-index = <0x1>;
fw-version = <0x2>;
image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8";
private-key = "/tmp/capsules/SIGNER.key";
pub-key-cert = "/tmp/capsules/SIGNER.crt";
monotonic-count = <0x1>;
filename = "/tmp/capsules/u-boot.bin.new";
capsule = "/tmp/capsules/Test113";
};
};
capsule19 {
capsule {
image-index = <0x1>;
fw-version = <0x5>;
image-type-id = "3673B45D-6A7C-46F3-9E60-ADABB03F7937";
private-key = "/tmp/capsules/SIGNER.key";
pub-key-cert = "/tmp/capsules/SIGNER.crt";
monotonic-count = <0x1>;
filename = "/tmp/capsules/uboot_bin_env.itb";
capsule = "/tmp/capsules/Test114";
};
};
capsule20 {
capsule {
image-index = <0x1>;
fw-version = <0x2>;
image-type-id = "3673B45D-6A7C-46F3-9E60-ADABB03F7937";
private-key = "/tmp/capsules/SIGNER.key";
pub-key-cert = "/tmp/capsules/SIGNER.crt";
monotonic-count = <0x1>;
filename = "/tmp/capsules/uboot_bin_env.itb";
capsule = "/tmp/capsules/Test115";
};
};
+#endif /* CONFIG_EFI_CAPSULE_AUTHENTICATE */ +#endif /* CONFIG_EFI_USE_CAPSULE_CFG_FILE */ }; #endif /* CONFIG_EFI_HAVE_CAPSULE_SUPPORT */ diff --git a/test/py/tests/test_efi_capsule/conftest.py b/test/py/tests/test_efi_capsule/conftest.py index 99b502902e..ca44249a38 100644 --- a/test/py/tests/test_efi_capsule/conftest.py +++ b/test/py/tests/test_efi_capsule/conftest.py @@ -56,133 +56,6 @@ def efi_capsule_data(request, u_boot_config): '-o test_ver.dtb version.dtbo' % (data_dir, u_boot_config.build_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,
shell=True)
check_call('sed -e \"s?BINFILE1?u-boot.bin.new?\" -e \"s?BINFILE2?u-boot.env.new?\" %s/test/py/tests/test_efi_capsule/uboot_bin_env.its > %s/uboot_bin_env.its' %
(u_boot_config.source_dir, data_dir),
shell=True)
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)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --fw-version 5 '
'--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot.bin.new Test101' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 2 --fw-version 10 '
'--guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 u-boot.env.new Test102' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --fw-version 2 '
'--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot.bin.new Test103' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --fw-version 5 '
'--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 uboot_bin_env.itb Test104' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --fw-version 2 '
'--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 uboot_bin_env.itb Test105' %
(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)
# raw firmware signed with proper key with version information
check_call('cd %s; '
'%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
'--fw-version 5 '
'--private-key SIGNER.key --certificate SIGNER.crt '
'--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 '
'u-boot.bin.new Test111'
% (data_dir, u_boot_config.build_dir),
shell=True)
# raw firmware signed with proper key with version information
check_call('cd %s; '
'%s/tools/mkeficapsule --index 2 --monotonic-count 1 '
'--fw-version 10 '
'--private-key SIGNER.key --certificate SIGNER.crt '
'--guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 '
'u-boot.env.new Test112'
% (data_dir, u_boot_config.build_dir),
shell=True)
# raw firmware signed with proper key with lower version information
check_call('cd %s; '
'%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
'--fw-version 2 '
'--private-key SIGNER.key --certificate SIGNER.crt '
'--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 '
'u-boot.bin.new Test113'
% (data_dir, u_boot_config.build_dir),
shell=True)
# FIT firmware signed with proper key with version information
check_call('cd %s; '
'%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
'--fw-version 5 '
'--private-key SIGNER.key --certificate SIGNER.crt '
'--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 '
'uboot_bin_env.itb Test114'
% (data_dir, u_boot_config.build_dir),
shell=True)
# FIT firmware signed with proper key with lower version information
check_call('cd %s; '
'%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
'--fw-version 2 '
'--private-key SIGNER.key --certificate SIGNER.crt '
'--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 '
'uboot_bin_env.itb Test115'
% (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' % (mnt_point, image_path), shell=True)
diff --git a/test/py/tests/test_efi_capsule/uboot_bin_env.its b/test/py/tests/test_efi_capsule/uboot_bin_env.its deleted file mode 100644 index fc65907481..0000000000 --- a/test/py/tests/test_efi_capsule/uboot_bin_env.its +++ /dev/null @@ -1,36 +0,0 @@ -/*
- Automatic software update for U-Boot
- Make sure the flashing addresses ('load' prop) is correct for your board!
- */
-/dts-v1/;
-/ {
description = "Automatic U-Boot environment update";
#address-cells = <2>;
images {
u-boot-bin {
description = "U-Boot binary on SPI Flash";
data = /incbin/("BINFILE1");
compression = "none";
type = "firmware";
arch = "sandbox";
load = <0>;
hash-1 {
algo = "sha1";
};
};
u-boot-env {
description = "U-Boot environment on SPI Flash";
data = /incbin/("BINFILE2");
compression = "none";
type = "firmware";
arch = "sandbox";
load = <0>;
hash-1 {
algo = "sha1";
};
};
};
-};
2.34.1
REgards, Simon

Hi Sughosh,
On Mon, 17 Jul 2023 at 05:18, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Simon,
On Sun, 16 Jul 2023 at 05:12, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Sat, 15 Jul 2023 at 07:46, Sughosh Ganu sughosh.ganu@linaro.org wrote:
The EFI capsule files can now be generated as part of u-boot build. This is done through binman. Add capsule entry nodes in the u-boot.dtsi for the sandbox architecture for generating the capsules. Remove the corresponding generation of capsules from the capsule update conftest file.
The capsules are generated through the config file for the sandbox variant, and through explicit parameters for the sandbox_flattree variant.
Also generate the FIT image used for testing the capsule update feature on the sandbox_flattree variant through binman. Remove the now superfluous its file which was used for generating this FIT image.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
Changes since V3:
- Use blob nodes instead of incbin for including the binaries in FIT image.
- Enable generation of capsules with versioning support.
arch/sandbox/dts/u-boot.dtsi | 265 ++++++++++++++++++ test/py/tests/test_efi_capsule/conftest.py | 127 --------- .../tests/test_efi_capsule/uboot_bin_env.its | 36 --- 3 files changed, 265 insertions(+), 163 deletions(-) delete mode 100644 test/py/tests/test_efi_capsule/uboot_bin_env.its
diff --git a/arch/sandbox/dts/u-boot.dtsi b/arch/sandbox/dts/u-boot.dtsi index 60bd004937..7b0250ac81 100644 --- a/arch/sandbox/dts/u-boot.dtsi +++ b/arch/sandbox/dts/u-boot.dtsi @@ -13,5 +13,270 @@ capsule-key = /incbin/(CONFIG_EFI_CAPSULE_ESL_FILE); }; #endif
binman: binman {
multiple-images;
};
+};
+&binman {
itb {
filename = "/tmp/capsules/uboot_bin_env.itb";
fit {
description = "Automatic U-Boot environment update";
#address-cells = <2>;
images {
u-boot-bin {
description = "U-Boot binary on SPI Flash";
compression = "none";
type = "firmware";
arch = "sandbox";
load = <0>;
blob {
filename = "/tmp/capsules/u-boot.bin.new";
};
hash-1 {
algo = "sha1";
};
};
u-boot-env {
description = "U-Boot environment on SPI Flash";
compression = "none";
type = "firmware";
arch = "sandbox";
load = <0>;
blob {
filename = "/tmp/capsules/u-boot.env.new";
};
hash-1 {
algo = "sha1";
};
};
};
};
};
+#ifdef CONFIG_EFI_USE_CAPSULE_CFG_FILE
capsule1 {
capsule {
cfg-file = CONFIG_EFI_CAPSULE_CFG_FILE;
};
};
+#else
capsule2 {
capsule {
image-index = <0x1>;
image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8";
We seem to have a persistent problem with these appearing in the source code.
Perhaps you could add them to a header file and use GUID_MEANINGFUL_NAME here instead (also below).
In general, GUIDs should not be open-coded.
Okay. Will it be okay if I add these to a sandbox_capule.h. Earlier, I had similar GUID macros in the sandbox config header, and you had asked me to move them to the board file.
These are littered all over the place. Perhaps a new 'efi_guid.h' header file that has all of them? We should also make sure they are in lib/uuid.c so they can be printed out.
filename = "/tmp/capsules/u-boot.bin.new";
capsule = "/tmp/capsules/Test01";
There is something odd here. You should not need to specify an absolute pathname and should not use /tmp
The /tmp/capsules/ directory is being used for collating all the capsule testing related files. Both the input files as well as the output capsule files are being put under this directory. Do you see any issue with using this directory for the capsule files?
Yes, the binman tests need to run in the input and output directories provided, like all the other tests do. People need to be able to inspect those dirs to see what happened.
Please also check test failures:
testtools.testresult.real._StringException: Traceback (most recent call last): File "/scratch/sglass/cosarm/src/third_party/u-boot/files/tools/binman/ftest.py", line 7012, in testCapsuleGenCapsuleFileMissing self.assertIn("Specify the output capsule file", File "/usr/lib/python3.10/unittest/case.py", line 1112, in assertIn self.fail(self._formatMessage(msg, standardMsg)) File "/usr/lib/python3.10/unittest/case.py", line 675, in fail raise self.failureException(msg) AssertionError: 'Specify the output capsule file' not found in "Filename 'payload.txt' not found in input path (/tmp/binmant.yjusbvaz) (cwd='/scratch/sglass/cosarm/src/third_party/u-boot/files')"
====================================================================== FAIL: binman.ftest.TestFunctional.testCapsuleGenCfgFile (subunit.RemotedTestCase) binman.ftest.TestFunctional.testCapsuleGenCfgFile ---------------------------------------------------------------------- testtools.testresult.real._StringException: Traceback (most recent call last): File "/scratch/sglass/cosarm/src/third_party/u-boot/files/tools/binman/ftest.py", line 6958, in testCapsuleGenCfgFile self.assertTrue(os.path.exists(self.capsule_fname)) File "/usr/lib/python3.10/unittest/case.py", line 687, in assertTrue raise self.failureException(msg) AssertionError: False is not true
---------------------------------------------------------------------- Ran 522 tests in 1.881s
Regards, Simon

hi Simon,
On Wed, 19 Jul 2023 at 06:41, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Mon, 17 Jul 2023 at 05:18, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Simon,
On Sun, 16 Jul 2023 at 05:12, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Sat, 15 Jul 2023 at 07:46, Sughosh Ganu sughosh.ganu@linaro.org wrote:
The EFI capsule files can now be generated as part of u-boot build. This is done through binman. Add capsule entry nodes in the u-boot.dtsi for the sandbox architecture for generating the capsules. Remove the corresponding generation of capsules from the capsule update conftest file.
The capsules are generated through the config file for the sandbox variant, and through explicit parameters for the sandbox_flattree variant.
Also generate the FIT image used for testing the capsule update feature on the sandbox_flattree variant through binman. Remove the now superfluous its file which was used for generating this FIT image.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
Changes since V3:
- Use blob nodes instead of incbin for including the binaries in FIT image.
- Enable generation of capsules with versioning support.
arch/sandbox/dts/u-boot.dtsi | 265 ++++++++++++++++++ test/py/tests/test_efi_capsule/conftest.py | 127 --------- .../tests/test_efi_capsule/uboot_bin_env.its | 36 --- 3 files changed, 265 insertions(+), 163 deletions(-) delete mode 100644 test/py/tests/test_efi_capsule/uboot_bin_env.its
diff --git a/arch/sandbox/dts/u-boot.dtsi b/arch/sandbox/dts/u-boot.dtsi index 60bd004937..7b0250ac81 100644 --- a/arch/sandbox/dts/u-boot.dtsi +++ b/arch/sandbox/dts/u-boot.dtsi @@ -13,5 +13,270 @@ capsule-key = /incbin/(CONFIG_EFI_CAPSULE_ESL_FILE); }; #endif
binman: binman {
multiple-images;
};
+};
+&binman {
itb {
filename = "/tmp/capsules/uboot_bin_env.itb";
fit {
description = "Automatic U-Boot environment update";
#address-cells = <2>;
images {
u-boot-bin {
description = "U-Boot binary on SPI Flash";
compression = "none";
type = "firmware";
arch = "sandbox";
load = <0>;
blob {
filename = "/tmp/capsules/u-boot.bin.new";
};
hash-1 {
algo = "sha1";
};
};
u-boot-env {
description = "U-Boot environment on SPI Flash";
compression = "none";
type = "firmware";
arch = "sandbox";
load = <0>;
blob {
filename = "/tmp/capsules/u-boot.env.new";
};
hash-1 {
algo = "sha1";
};
};
};
};
};
+#ifdef CONFIG_EFI_USE_CAPSULE_CFG_FILE
capsule1 {
capsule {
cfg-file = CONFIG_EFI_CAPSULE_CFG_FILE;
};
};
+#else
capsule2 {
capsule {
image-index = <0x1>;
image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8";
We seem to have a persistent problem with these appearing in the source code.
Perhaps you could add them to a header file and use GUID_MEANINGFUL_NAME here instead (also below).
In general, GUIDs should not be open-coded.
Okay. Will it be okay if I add these to a sandbox_capule.h. Earlier, I had similar GUID macros in the sandbox config header, and you had asked me to move them to the board file.
These are littered all over the place. Perhaps a new 'efi_guid.h'
Okay, I will add these under the efi_guid.h file.
header file that has all of them? We should also make sure they are in lib/uuid.c so they can be printed out.
Please note that these are GUID strings being used here in the generation of the capsule. What you refer to in lib/uuid.c are the actual variables of type efi_guid_t.
filename = "/tmp/capsules/u-boot.bin.new";
capsule = "/tmp/capsules/Test01";
There is something odd here. You should not need to specify an absolute pathname and should not use /tmp
The /tmp/capsules/ directory is being used for collating all the capsule testing related files. Both the input files as well as the output capsule files are being put under this directory. Do you see any issue with using this directory for the capsule files?
Yes, the binman tests need to run in the input and output directories provided, like all the other tests do. People need to be able to inspect those dirs to see what happened.
Please also check test failures:
testtools.testresult.real._StringException: Traceback (most recent call last): File "/scratch/sglass/cosarm/src/third_party/u-boot/files/tools/binman/ftest.py", line 7012, in testCapsuleGenCapsuleFileMissing self.assertIn("Specify the output capsule file", File "/usr/lib/python3.10/unittest/case.py", line 1112, in assertIn self.fail(self._formatMessage(msg, standardMsg)) File "/usr/lib/python3.10/unittest/case.py", line 675, in fail raise self.failureException(msg) AssertionError: 'Specify the output capsule file' not found in "Filename 'payload.txt' not found in input path (/tmp/binmant.yjusbvaz) (cwd='/scratch/sglass/cosarm/src/third_party/u-boot/files')"
====================================================================== FAIL: binman.ftest.TestFunctional.testCapsuleGenCfgFile (subunit.RemotedTestCase) binman.ftest.TestFunctional.testCapsuleGenCfgFile
testtools.testresult.real._StringException: Traceback (most recent call last): File "/scratch/sglass/cosarm/src/third_party/u-boot/files/tools/binman/ftest.py", line 6958, in testCapsuleGenCfgFile self.assertTrue(os.path.exists(self.capsule_fname)) File "/usr/lib/python3.10/unittest/case.py", line 687, in assertTrue raise self.failureException(msg) AssertionError: False is not true
I had run my patches through CI before submission, so they work fine in the CI setup. I believe this could be because of non-existence of the input files under the /tmp/capsules/ directory. And like I mentioned in my other reply, this issue would just be with the testCapsuleGenCfgFile function. The rest of the tests do use the input and output directories that binman creates for testing.
-sughosh
Ran 522 tests in 1.881s
Regards, Simon
participants (2)
-
Simon Glass
-
Sughosh Ganu