All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/7] Integrate EFI capsule tasks into u-boot's build flow
@ 2023-06-13 10:37 Sughosh Ganu
  2023-06-13 10:38 ` [PATCH 1/7] capsule: authenticate: Embed capsule public key in platform's dtb Sughosh Ganu
                   ` (6 more replies)
  0 siblings, 7 replies; 50+ messages in thread
From: Sughosh Ganu @ 2023-06-13 10:37 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass, Michal Simek,
	Takahiro Akashi


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 as part
of dtb' build. The public key is in the form of an EFI Signature
List(ESL) file and is used for capsule authentication. This is
achieved at the time of the dtb generation, with the path to the ESL
file being provided through a Kconfig
symbol(CONFIG_EFI_CAPSULE_ESL_FILE).

Changes have also been made to the test flow so that the keys used for
signing the capsule, and the ESL file, are generated prior to invoking
the u-boot's build, which enables embedding the ESL file into the dtb
as part of the u-boot build flow.

The other task is to add a make target for generating capsules. This
is being achieved by adding support for parsing a config file to get
the capsule generation parameters. Multiple payloads can be specified,
resulting in generation of multiple capsules with a single invocation
of the command. The path to the config file is to be specified through
a Kconfig symbol(CONFIG_EFI_CAPSULE_CFG_FILE).

Changes have also been made to the efi capsule test setup, whereby,
with the above config symbol having been populated, the capsule files
are generated through the make capsule command. The requisite config
file has been placed under the test/py/tests/test_efi_capsule/
directory, which results in generation of the same set of capsule
files.

Currently, the capsule authentication feature is tested on the sandbox
and sandbox_flattree variants. The capsule generation through config
file is enabled for the sandbox variant, with the sandbox_flattree
variant generating capsules through the command-line parameters.

The document has been updated to reflect the above changes.

Sughosh Ganu (7):
  capsule: authenticate: Embed capsule public key in platform's dtb
  test: py: Generate capsule keys prior to building u-boot
  doc: capsule: Document the new mechanism to embed ESL file into dtb
  tools: mkeficapsule: Add support for parsing capsule params from
    config file
  Makefile: Add a target for building capsules
  test: efi_capsule: Test capsule generation from config file
  doc: Add documentation to describe capsule config file format

 Makefile                                      |   9 +
 configs/sandbox_defconfig                     |   2 +
 configs/sandbox_flattree_defconfig            |   1 +
 doc/develop/uefi/uefi.rst                     |  83 ++++-
 lib/efi_loader/Kconfig                        |  11 +
 scripts/Makefile.lib                          |   8 +
 scripts/embed_capsule_key.sh                  |  25 ++
 test/py/conftest.py                           |  64 ++++
 test/py/tests/test_efi_capsule/conftest.py    | 144 ++++----
 .../test_efi_capsule/sandbox_capsule_cfg.txt  |  75 ++++
 test/py/tests/test_efi_capsule/signature.dts  |  10 -
 tools/Kconfig                                 |   9 +
 tools/Makefile                                |   1 +
 tools/eficapsule.h                            | 110 ++++++
 tools/mkeficapsule.c                          | 106 ++++--
 tools/mkeficapsule_parse.c                    | 345 ++++++++++++++++++
 16 files changed, 866 insertions(+), 137 deletions(-)
 create mode 100755 scripts/embed_capsule_key.sh
 create mode 100644 test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt
 delete mode 100644 test/py/tests/test_efi_capsule/signature.dts
 create mode 100644 tools/mkeficapsule_parse.c

-- 
2.34.1



^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH 1/7] capsule: authenticate: Embed capsule public key in platform's dtb
  2023-06-13 10:37 [PATCH 0/7] Integrate EFI capsule tasks into u-boot's build flow Sughosh Ganu
@ 2023-06-13 10:38 ` Sughosh Ganu
  2023-06-15  9:14   ` Simon Glass
  2023-06-13 10:38 ` [PATCH 2/7] test: py: Generate capsule keys prior to building u-boot Sughosh Ganu
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 50+ messages in thread
From: Sughosh Ganu @ 2023-06-13 10:38 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass, Michal Simek,
	Takahiro Akashi, Sughosh Ganu

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 script for embedding the ESL used for capsule authentication in
the platform's dtb, and call this as part of building the dtb(s). This
brings the embedding of the ESL in the dtb into the u-boot build flow.

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>
---
 lib/efi_loader/Kconfig       | 11 +++++++++++
 scripts/Makefile.lib         |  8 ++++++++
 scripts/embed_capsule_key.sh | 25 +++++++++++++++++++++++++
 3 files changed, 44 insertions(+)
 create mode 100755 scripts/embed_capsule_key.sh

diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index c5835e6ef6..1326a1d109 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -234,6 +234,17 @@ config EFI_CAPSULE_MAX
 	  Select the max capsule index value used for capsule report
 	  variables. This value is used to create CapsuleMax variable.
 
+config EFI_CAPSULE_ESL_FILE
+	string "Path to the EFI Signature List File"
+	default ""
+	depends on EFI_CAPSULE_AUTHENTICATE
+	help
+	  Provides the absolute path to the EFI Signature List
+	  file which will be embedded in the platform's device
+	  tree and used for capsule authentication at the time
+	  of capsule update.
+
+
 config EFI_DEVICE_PATH_TO_TEXT
 	bool "Device path to text protocol"
 	default y
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 7b27224b5d..a4083d0a26 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -192,6 +192,8 @@ dtc_cpp_flags  = -Wp,-MD,$(depfile).pre.tmp -nostdinc                    \
 		 -D__ASSEMBLY__                                          \
 		 -undef -D__DTS__
 
+export dtc_cpp_flags
+
 # Finds the multi-part object the current object will be linked into
 modname-multi = $(sort $(foreach m,$(multi-used),\
 		$(if $(filter $(subst $(obj)/,,$*.o), $($(m:.o=-objs)) $($(m:.o=-y))),$(m:.o=))))
@@ -315,6 +317,9 @@ ifeq ($(CONFIG_OF_LIBFDT_OVERLAY),y)
 DTC_FLAGS += -@
 endif
 
+quiet_cmd_embedcapsulekey = EMBEDCAPSULEKEY $@
+cmd_embedcapsulekey = $(srctree)/scripts/embed_capsule_key.sh $@
+
 quiet_cmd_dtc = DTC     $@
 # Modified for U-Boot
 # Bring in any U-Boot-specific include at the end of the file
@@ -333,6 +338,9 @@ cmd_dtc = mkdir -p $(dir ${dtc-tmp}) ; \
 
 $(obj)/%.dtb: $(src)/%.dts FORCE
 	$(call if_changed_dep,dtc)
+ifeq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),y)
+	$(call cmd,embedcapsulekey,$@)
+endif
 
 pre-tmp = $(subst $(comma),_,$(dot-target).pre.tmp)
 dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp)
diff --git a/scripts/embed_capsule_key.sh b/scripts/embed_capsule_key.sh
new file mode 100755
index 0000000000..1c2e45f758
--- /dev/null
+++ b/scripts/embed_capsule_key.sh
@@ -0,0 +1,25 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2023, Linaro Limited
+#
+
+gen_capsule_signature_file() {
+cat >> $1 << EOF
+/dts-v1/;
+/plugin/;
+
+&{/} {
+	signature {
+		capsule-key = /incbin/(CONFIG_EFI_CAPSULE_ESL_FILE);
+	};
+};
+EOF
+}
+
+gen_capsule_signature_file signature.$$.dts > /dev/null 2>&1
+$CPP $dtc_cpp_flags -x assembler-with-cpp -o signature.$$.tmp signature.$$.dts > /dev/null 2>&1
+dtc -@ -O dtb -o signature.$$.dtbo signature.$$.tmp > /dev/null 2>&1
+fdtoverlay -i $1 -o temp.$$.dtb -v signature.$$.dtbo > /dev/null 2>&1
+mv temp.$$.dtb $1 > /dev/null 2>&1
+rm -f signature.$$.* > /dev/null 2>&1
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 2/7] test: py: Generate capsule keys prior to building u-boot
  2023-06-13 10:37 [PATCH 0/7] Integrate EFI capsule tasks into u-boot's build flow Sughosh Ganu
  2023-06-13 10:38 ` [PATCH 1/7] capsule: authenticate: Embed capsule public key in platform's dtb Sughosh Ganu
@ 2023-06-13 10:38 ` Sughosh Ganu
  2023-06-13 10:38 ` [PATCH 3/7] doc: capsule: Document the new mechanism to embed ESL file into dtb Sughosh Ganu
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 50+ messages in thread
From: Sughosh Ganu @ 2023-06-13 10:38 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass, Michal Simek,
	Takahiro Akashi, Sughosh Ganu

Currently, the keys and the EFI Signature List(ESL) file used for
capsule authentication can be generated after the u-boot image has
been built. The ESL file is then manually embedded into the platform's
dtb for capsule authentication.

This flow has been changed through an earlier commit, which embeds the
ESL file into the platform's dtb(s) as part of the u-boot build. This
requires generating the keys and the ESL file prior to invoking the
u-boot build.

Bring about the same sequence of generating these files prior to
invoking the u-boot build while testing.

Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
---
 configs/sandbox_defconfig                    |  1 +
 configs/sandbox_flattree_defconfig           |  1 +
 test/py/conftest.py                          | 64 ++++++++++++++++++++
 test/py/tests/test_efi_capsule/conftest.py   | 38 +++---------
 test/py/tests/test_efi_capsule/signature.dts | 10 ---
 5 files changed, 74 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 1ec44d5b33..d8a2386bb0 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -339,6 +339,7 @@ CONFIG_EFI_RUNTIME_UPDATE_CAPSULE=y
 CONFIG_EFI_CAPSULE_ON_DISK=y
 CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y
 CONFIG_EFI_CAPSULE_AUTHENTICATE=y
+CONFIG_EFI_CAPSULE_ESL_FILE="/tmp/capsules/SIGNER.esl"
 CONFIG_EFI_SECURE_BOOT=y
 CONFIG_TEST_FDTDEC=y
 CONFIG_UNIT_TEST=y
diff --git a/configs/sandbox_flattree_defconfig b/configs/sandbox_flattree_defconfig
index e7657d40dc..8d60744771 100644
--- a/configs/sandbox_flattree_defconfig
+++ b/configs/sandbox_flattree_defconfig
@@ -226,6 +226,7 @@ CONFIG_EFI_RUNTIME_UPDATE_CAPSULE=y
 CONFIG_EFI_CAPSULE_ON_DISK=y
 CONFIG_EFI_CAPSULE_FIRMWARE_FIT=y
 CONFIG_EFI_CAPSULE_AUTHENTICATE=y
+CONFIG_EFI_CAPSULE_ESL_FILE="/tmp/capsules/SIGNER.esl"
 CONFIG_UNIT_TEST=y
 CONFIG_UT_TIME=y
 CONFIG_UT_DM=y
diff --git a/test/py/conftest.py b/test/py/conftest.py
index fc9dd3a83f..b7166508d4 100644
--- a/test/py/conftest.py
+++ b/test/py/conftest.py
@@ -80,6 +80,65 @@ def pytest_addoption(parser):
         help='Run sandbox under gdbserver. The argument is the channel '+
         'over which gdbserver should communicate, e.g. localhost:1234')
 
+def setup_capsule_auth_build(source_dir, build_dir, board_type, log):
+    """Setup the platform's build for capsule authenticate
+
+    This generates the signatures needed for signing the capsules along
+    with the EFI Signature List(ESL) file, with the capsule
+    authentication feature enabled.
+
+    The ESL file is subsequently embedded into the platform's
+    dtb during the u-boot build, to be used for capsule
+    authentication.
+
+    Two sets of signatures are generated, namely SIGNER and SIGNER2.
+    The SIGNER2 key pair is used as a malicious key for testing the
+    the capsule authentication functionality.
+
+    Args:
+        soruce_dir (str): Directory containing source code
+        build_dir (str): Directory to build in
+        board_type (str): board_type parameter (e.g. 'sandbox')
+        log (Logfile): Log file to use
+
+    Returns:
+        Nothing.
+    """
+    def run_command(name, cmd, source_dir):
+        with log.section(name):
+            if isinstance(cmd, str):
+                cmd = cmd.split()
+                runner = log.get_runner(name, None)
+                runner.run(cmd, cwd=source_dir)
+                runner.close()
+                log.status_pass('OK')
+
+    capsule_sig_dir = '/tmp/capsules/'
+    sig_name = 'SIGNER'
+    mkdir_p(capsule_sig_dir)
+    name = 'openssl'
+    cmd = ( 'openssl req -x509 -sha256 -newkey rsa:2048 '
+            '-subj /CN=TEST_SIGNER/ -keyout %s%s.key '
+            '-out %s%s.crt -nodes -days 365'
+            % (capsule_sig_dir, sig_name, capsule_sig_dir, sig_name)
+           )
+    run_command(name, cmd, source_dir)
+
+    name = 'cert-to-efi-sig-list'
+    cmd = ( 'cert-to-efi-sig-list %s%s.crt %s%s.esl'
+            % (capsule_sig_dir, sig_name, capsule_sig_dir, sig_name)
+           )
+    run_command(name, cmd, source_dir)
+
+    sig_name = 'SIGNER2'
+    name = 'openssl'
+    cmd = ( 'openssl req -x509 -sha256 -newkey rsa:2048 '
+            '-subj /CN=TEST_SIGNER/ -keyout %s%s.key '
+            '-out %s%s.crt -nodes -days 365'
+            % (capsule_sig_dir, sig_name, capsule_sig_dir, sig_name)
+           )
+    run_command(name, cmd, source_dir)
+
 def run_build(config, source_dir, build_dir, board_type, log):
     """run_build: Build U-Boot
 
@@ -102,6 +161,11 @@ def run_build(config, source_dir, build_dir, board_type, log):
             o_opt = 'O=%s' % build_dir
         else:
             o_opt = ''
+
+        capsule_auth_boards = ( 'sandbox', 'sandbox_flattree' )
+        if board_type in capsule_auth_boards:
+            setup_capsule_auth_build(source_dir, build_dir, board_type, log)
+
         cmds = (
             ['make', o_opt, '-s', board_type + '_defconfig'],
             ['make', o_opt, '-s', '-j{}'.format(os.cpu_count())],
diff --git a/test/py/tests/test_efi_capsule/conftest.py b/test/py/tests/test_efi_capsule/conftest.py
index a337e62936..4269c41a74 100644
--- a/test/py/tests/test_efi_capsule/conftest.py
+++ b/test/py/tests/test_efi_capsule/conftest.py
@@ -32,36 +32,6 @@ def efi_capsule_data(request, u_boot_config):
         check_call('mkdir -p %s' % data_dir, shell=True)
         check_call('mkdir -p %s' % install_dir, shell=True)
 
-        capsule_auth_enabled = u_boot_config.buildconfig.get(
-                    'config_efi_capsule_authenticate')
-        if capsule_auth_enabled:
-            # Create private key (SIGNER.key) and certificate (SIGNER.crt)
-            check_call('cd %s; '
-                       'openssl req -x509 -sha256 -newkey rsa:2048 '
-                            '-subj /CN=TEST_SIGNER/ -keyout SIGNER.key '
-                            '-out SIGNER.crt -nodes -days 365'
-                       % data_dir, shell=True)
-            check_call('cd %s; %scert-to-efi-sig-list SIGNER.crt SIGNER.esl'
-                       % (data_dir, EFITOOLS_PATH), shell=True)
-
-            # Update dtb adding capsule certificate
-            check_call('cd %s; '
-                       'cp %s/test/py/tests/test_efi_capsule/signature.dts .'
-                       % (data_dir, u_boot_config.source_dir), shell=True)
-            check_call('cd %s; '
-                       'dtc -@ -I dts -O dtb -o signature.dtbo signature.dts; '
-                       'fdtoverlay -i %s/arch/sandbox/dts/test.dtb '
-                            '-o test_sig.dtb signature.dtbo'
-                       % (data_dir, u_boot_config.build_dir), shell=True)
-
-            # Create *malicious* private key (SIGNER2.key) and certificate
-            # (SIGNER2.crt)
-            check_call('cd %s; '
-                       'openssl req -x509 -sha256 -newkey rsa:2048 '
-                            '-subj /CN=TEST_SIGNER/ -keyout SIGNER2.key '
-                            '-out SIGNER2.crt -nodes -days 365'
-                       % data_dir, shell=True)
-
         # Create capsule files
         # two regions: one for u-boot.bin and the other for u-boot.env
         check_call('cd %s; echo -n u-boot:Old > u-boot.bin.old; echo -n u-boot:New > u-boot.bin.new; echo -n u-boot-env:Old > u-boot.env.old; echo -n u-boot-env:New > u-boot.env.new' % data_dir,
@@ -88,7 +58,14 @@ def efi_capsule_data(request, u_boot_config):
                    (data_dir, u_boot_config.build_dir),
                    shell=True)
 
+        capsule_auth_enabled = u_boot_config.buildconfig.get(
+                    'config_efi_capsule_authenticate')
         if capsule_auth_enabled:
+            capsules_path_dir = '/tmp/capsules/'
+            check_call('mv %s/* %s ' %(capsules_path_dir, data_dir), shell=True)
+            check_call('cp %s/arch/sandbox/dts/test.dtb %s/test_sig.dtb' %
+                       (u_boot_config.build_dir, data_dir), shell=True)
+
             # raw firmware signed with proper key
             check_call('cd %s; '
                        '%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
@@ -138,4 +115,5 @@ def efi_capsule_data(request, u_boot_config):
     finally:
         call('rm -rf %s' % mnt_point, shell=True)
         call('rm -f %s' % image_path, shell=True)
+        call('rm -rf %s' % capsules_path_dir, shell=True)
         call('rm -f ./spi.bin', shell=True)
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");
-	};
-};
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 3/7] doc: capsule: Document the new mechanism to embed ESL file into dtb
  2023-06-13 10:37 [PATCH 0/7] Integrate EFI capsule tasks into u-boot's build flow Sughosh Ganu
  2023-06-13 10:38 ` [PATCH 1/7] capsule: authenticate: Embed capsule public key in platform's dtb Sughosh Ganu
  2023-06-13 10:38 ` [PATCH 2/7] test: py: Generate capsule keys prior to building u-boot Sughosh Ganu
@ 2023-06-13 10:38 ` Sughosh Ganu
  2023-06-15  9:14   ` Simon Glass
  2023-06-13 10:38 ` [PATCH 4/7] tools: mkeficapsule: Add support for parsing capsule params from config file Sughosh Ganu
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 50+ messages in thread
From: Sughosh Ganu @ 2023-06-13 10:38 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass, Michal Simek,
	Takahiro Akashi, Sughosh Ganu

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>
---
 doc/develop/uefi/uefi.rst | 19 +++++--------------
 1 file changed, 5 insertions(+), 14 deletions(-)

diff --git a/doc/develop/uefi/uefi.rst b/doc/develop/uefi/uefi.rst
index ffe25ca231..f96762af39 100644
--- a/doc/develop/uefi/uefi.rst
+++ b/doc/develop/uefi/uefi.rst
@@ -495,20 +495,11 @@ and used by the steps highlighted below.
             ...
     }
 
-You can do step-4 manually with
-
-.. code-block:: console
-
-    $ dtc -@ -I dts -O dtb -o signature.dtbo signature.dts
-    $ fdtoverlay -i orig.dtb -o new.dtb -v signature.dtbo
-
-where signature.dts looks like::
-
-    &{/} {
-            signature {
-                    capsule-key = /incbin/("CRT.esl");
-            };
-    };
+You can perform step-4 by defining the Kconfig symbol
+CONFIG_EFI_CAPSULE_ESL_FILE. This symbol defines the path to the esl
+file generated in step-2. Once the symbol has been populated with the
+path to the esl file, the esl file will automatically get embedded
+into the platform's dtb as part of u-boot build.
 
 Executing the boot manager
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 4/7] tools: mkeficapsule: Add support for parsing capsule params from config file
  2023-06-13 10:37 [PATCH 0/7] Integrate EFI capsule tasks into u-boot's build flow Sughosh Ganu
                   ` (2 preceding siblings ...)
  2023-06-13 10:38 ` [PATCH 3/7] doc: capsule: Document the new mechanism to embed ESL file into dtb Sughosh Ganu
@ 2023-06-13 10:38 ` Sughosh Ganu
  2023-06-14  3:39   ` Takahiro Akashi
  2023-06-13 10:38 ` [PATCH 5/7] Makefile: Add a target for building capsules Sughosh Ganu
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 50+ messages in thread
From: Sughosh Ganu @ 2023-06-13 10:38 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass, Michal Simek,
	Takahiro Akashi, Sughosh Ganu

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>
---
 tools/Kconfig              |   9 +
 tools/Makefile             |   1 +
 tools/eficapsule.h         | 110 ++++++++++++
 tools/mkeficapsule.c       | 106 +++++++-----
 tools/mkeficapsule_parse.c | 345 +++++++++++++++++++++++++++++++++++++
 5 files changed, 531 insertions(+), 40 deletions(-)
 create mode 100644 tools/mkeficapsule_parse.c

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


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 5/7] Makefile: Add a target for building capsules
  2023-06-13 10:37 [PATCH 0/7] Integrate EFI capsule tasks into u-boot's build flow Sughosh Ganu
                   ` (3 preceding siblings ...)
  2023-06-13 10:38 ` [PATCH 4/7] tools: mkeficapsule: Add support for parsing capsule params from config file Sughosh Ganu
@ 2023-06-13 10:38 ` Sughosh Ganu
  2023-06-15  9:14   ` Simon Glass
  2023-06-13 10:38 ` [PATCH 6/7] test: efi_capsule: Test capsule generation from config file Sughosh Ganu
  2023-06-13 10:38 ` [PATCH 7/7] doc: Add documentation to describe capsule config file format Sughosh Ganu
  6 siblings, 1 reply; 50+ messages in thread
From: Sughosh Ganu @ 2023-06-13 10:38 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass, Michal Simek,
	Takahiro Akashi, Sughosh Ganu

Add a target for building EFI capsules. The capsule parameters are
specified through a config file, and the path to the config file is
specified through CONFIG_EFI_CAPSULE_CFG_FILE. When the config file is
not specified, the command only builds tools.

Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
---
 Makefile | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/Makefile b/Makefile
index 10bfaa52ad..96db29aa77 100644
--- a/Makefile
+++ b/Makefile
@@ -1151,6 +1151,15 @@ dtbs: dts/dt.dtb
 dts/dt.dtb: u-boot
 	$(Q)$(MAKE) $(build)=dts dtbs
 
+quiet_cmd_mkeficapsule = MKEFICAPSULE $@
+cmd_mkeficapsule = $(objtree)/tools/mkeficapsule $@
+
+PHONY += capsule
+capsule: tools
+ifneq ($(CONFIG_EFI_CAPSULE_CFG_FILE),"")
+	$(call cmd,mkeficapsule)
+endif
+
 quiet_cmd_copy = COPY    $@
       cmd_copy = cp $< $@
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 6/7] test: efi_capsule: Test capsule generation from config file
  2023-06-13 10:37 [PATCH 0/7] Integrate EFI capsule tasks into u-boot's build flow Sughosh Ganu
                   ` (4 preceding siblings ...)
  2023-06-13 10:38 ` [PATCH 5/7] Makefile: Add a target for building capsules Sughosh Ganu
@ 2023-06-13 10:38 ` Sughosh Ganu
  2023-06-15  9:14   ` Simon Glass
  2023-06-13 10:38 ` [PATCH 7/7] doc: Add documentation to describe capsule config file format Sughosh Ganu
  6 siblings, 1 reply; 50+ messages in thread
From: Sughosh Ganu @ 2023-06-13 10:38 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass, Michal Simek,
	Takahiro Akashi, Sughosh Ganu

Support has been added to generate capsules through parameters
specified in the config file. To bring this under the testing ambit,
make changes in the EFI capsule test logic to generate the capsule
files by parsing the config file, when the path to the config file is
specified.

Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
---
 configs/sandbox_defconfig                     |   1 +
 test/py/tests/test_efi_capsule/conftest.py    | 106 +++++++++++-------
 .../test_efi_capsule/sandbox_capsule_cfg.txt  |  75 +++++++++++++
 3 files changed, 139 insertions(+), 43 deletions(-)
 create mode 100644 test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt

diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index d8a2386bb0..ba26816898 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -340,6 +340,7 @@ CONFIG_EFI_CAPSULE_ON_DISK=y
 CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y
 CONFIG_EFI_CAPSULE_AUTHENTICATE=y
 CONFIG_EFI_CAPSULE_ESL_FILE="/tmp/capsules/SIGNER.esl"
+CONFIG_EFI_CAPSULE_CFG_FILE="/tmp/capsules/sandbox_capsule_cfg.txt"
 CONFIG_EFI_SECURE_BOOT=y
 CONFIG_TEST_FDTDEC=y
 CONFIG_UNIT_TEST=y
diff --git a/test/py/tests/test_efi_capsule/conftest.py b/test/py/tests/test_efi_capsule/conftest.py
index 4269c41a74..4eb54c1a61 100644
--- a/test/py/tests/test_efi_capsule/conftest.py
+++ b/test/py/tests/test_efi_capsule/conftest.py
@@ -42,21 +42,6 @@ def efi_capsule_data(request, u_boot_config):
         check_call('cd %s; %s/tools/mkimage -f uboot_bin_env.its uboot_bin_env.itb' %
                    (data_dir, u_boot_config.build_dir),
                    shell=True)
-        check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot.bin.new Test01' %
-                   (data_dir, u_boot_config.build_dir),
-                   shell=True)
-        check_call('cd %s; %s/tools/mkeficapsule --index 2 --guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 u-boot.env.new Test02' %
-                   (data_dir, u_boot_config.build_dir),
-                   shell=True)
-        check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 058B7D83-50D5-4C47-A195-60D86AD341C4 u-boot.bin.new Test03' %
-                   (data_dir, u_boot_config.build_dir),
-                   shell=True)
-        check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 uboot_bin_env.itb Test04' %
-                   (data_dir, u_boot_config.build_dir),
-                   shell=True)
-        check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid  058B7D83-50D5-4C47-A195-60D86AD341C4 uboot_bin_env.itb Test05' %
-                   (data_dir, u_boot_config.build_dir),
-                   shell=True)
 
         capsule_auth_enabled = u_boot_config.buildconfig.get(
                     'config_efi_capsule_authenticate')
@@ -66,40 +51,75 @@ def efi_capsule_data(request, u_boot_config):
             check_call('cp %s/arch/sandbox/dts/test.dtb %s/test_sig.dtb' %
                        (u_boot_config.build_dir, data_dir), shell=True)
 
-            # raw firmware signed with proper key
-            check_call('cd %s; '
-                       '%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
-                            '--private-key SIGNER.key --certificate SIGNER.crt '
-                            '--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 '
-                            'u-boot.bin.new Test11'
-                       % (data_dir, u_boot_config.build_dir),
+        cfg_file = u_boot_config.buildconfig.get(
+            'config_efi_capsule_cfg_file')[1:-1]
+        if cfg_file:
+            capsules_path_dir = '/tmp/capsules/'
+            check_call('mkdir -p %s ;'
+                       'cp -a %s/* %s/' % (capsules_path_dir, data_dir, capsules_path_dir),
                        shell=True)
-            # raw firmware signed with *mal* key
-            check_call('cd %s; '
-                       '%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
-                            '--private-key SIGNER2.key '
-                            '--certificate SIGNER2.crt '
-                            '--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 '
-                            'u-boot.bin.new Test12'
-                       % (data_dir, u_boot_config.build_dir),
+            check_call(' cp %s/test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt %s'
+                       % (u_boot_config.source_dir, capsules_path_dir),
                        shell=True)
-            # FIT firmware signed with proper key
             check_call('cd %s; '
-                       '%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
-                            '--private-key SIGNER.key --certificate SIGNER.crt '
-                            '--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 '
-                            'uboot_bin_env.itb Test13'
-                       % (data_dir, u_boot_config.build_dir),
+                       'make capsule O=%s' % (u_boot_config.source_dir, u_boot_config.build_dir),
                        shell=True)
-            # FIT firmware signed with *mal* key
             check_call('cd %s; '
-                       '%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
-                            '--private-key SIGNER2.key '
-                            '--certificate SIGNER2.crt '
-                            '--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 '
-                            'uboot_bin_env.itb Test14'
-                       % (data_dir, u_boot_config.build_dir),
+                       'mv Test* %s'
+                       % (capsules_path_dir, data_dir), shell=True)
+            check_call('rm -rf %s' % capsules_path_dir, shell=True)
+        else:
+            check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot.bin.new Test01' %
+                       (data_dir, u_boot_config.build_dir),
+                       shell=True)
+            check_call('cd %s; %s/tools/mkeficapsule --index 2 --guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 u-boot.env.new Test02' %
+                       (data_dir, u_boot_config.build_dir),
                        shell=True)
+            check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 058B7D83-50D5-4C47-A195-60D86AD341C4 u-boot.bin.new Test03' %
+                       (data_dir, u_boot_config.build_dir),
+                       shell=True)
+            check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 uboot_bin_env.itb Test04' %
+                       (data_dir, u_boot_config.build_dir),
+                       shell=True)
+            check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid  058B7D83-50D5-4C47-A195-60D86AD341C4 uboot_bin_env.itb Test05' %
+                       (data_dir, u_boot_config.build_dir),
+                       shell=True)
+
+            if capsule_auth_enabled:
+                # raw firmware signed with proper key
+                check_call('cd %s; '
+                           '%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
+                           '--private-key SIGNER.key --certificate SIGNER.crt '
+                           '--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 '
+                           'u-boot.bin.new Test11'
+                           % (data_dir, u_boot_config.build_dir),
+                           shell=True)
+                # raw firmware signed with *mal* key
+                check_call('cd %s; '
+                           '%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
+                           '--private-key SIGNER2.key '
+                           '--certificate SIGNER2.crt '
+                           '--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 '
+                           'u-boot.bin.new Test12'
+                           % (data_dir, u_boot_config.build_dir),
+                           shell=True)
+                # FIT firmware signed with proper key
+                check_call('cd %s; '
+                           '%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
+                           '--private-key SIGNER.key --certificate SIGNER.crt '
+                           '--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 '
+                           'uboot_bin_env.itb Test13'
+                           % (data_dir, u_boot_config.build_dir),
+                           shell=True)
+                # FIT firmware signed with *mal* key
+                check_call('cd %s; '
+                           '%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
+                           '--private-key SIGNER2.key '
+                           '--certificate SIGNER2.crt '
+                           '--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 '
+                           'uboot_bin_env.itb Test14'
+                           % (data_dir, u_boot_config.build_dir),
+                           shell=True)
 
         # Create a disk image with EFI system partition
         check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat %s %s' %
diff --git a/test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt b/test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt
new file mode 100644
index 0000000000..4e5065d538
--- /dev/null
+++ b/test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt
@@ -0,0 +1,75 @@
+{
+	image-index: 1
+	image-guid: 09D7CF52-0720-4710-91D1-08469B7FE9C8
+	payload: /tmp/capsules/u-boot.bin.new
+	capsule: /tmp/capsules/Test01
+}
+{
+	image-index: 2
+	image-guid: 5A7021F5-FEF2-48B4-AABA-832E777418C0
+	payload: /tmp/capsules/u-boot.env.new
+	capsule: /tmp/capsules/Test02
+}
+{
+	image-index: 1
+	image-guid: 058B7D83-50D5-4C47-A195-60D86AD341C4
+	payload: /tmp/capsules/u-boot.bin.new
+	capsule: /tmp/capsules/Test03
+
+}
+{
+	image-index: 1
+	image-guid: 3673B45D-6A7C-46F3-9E60-ADABB03F7937
+	payload: /tmp/capsules/uboot_bin_env.itb
+	capsule: /tmp/capsules/Test04
+
+}
+{
+	image-index: 1
+	image-guid: 058B7D83-50D5-4C47-A195-60D86AD341C4
+	payload: /tmp/capsules/uboot_bin_env.itb
+	capsule: /tmp/capsules/Test05
+
+}
+{
+	image-index: 1
+	image-guid: 058B7D83-50D5-4C47-A195-60D86AD341C4
+	payload: /tmp/capsules/uboot_bin_env.itb
+	capsule: /tmp/capsules/Test05
+}
+{
+	image-index: 1
+	monotonic-count: 1
+	private-key: /tmp/capsules/SIGNER.key
+	pub-key-cert: /tmp/capsules/SIGNER.crt
+	image-guid: 09D7CF52-0720-4710-91D1-08469B7FE9C8
+	payload: /tmp/capsules/u-boot.bin.new
+	capsule: /tmp/capsules/Test11
+}
+{
+	image-index: 1
+	monotonic-count: 1
+	private-key: /tmp/capsules/SIGNER2.key
+	pub-key-cert: /tmp/capsules/SIGNER2.crt
+	image-guid: 09D7CF52-0720-4710-91D1-08469B7FE9C8
+	payload: /tmp/capsules/u-boot.bin.new
+	capsule: /tmp/capsules/Test12
+}
+{
+	image-index: 1
+	monotonic-count: 1
+	private-key: /tmp/capsules/SIGNER.key
+	pub-key-cert: /tmp/capsules/SIGNER.crt
+	image-guid: 3673B45D-6A7C-46F3-9E60-ADABB03F7937
+	payload: /tmp/capsules/uboot_bin_env.itb
+	capsule: /tmp/capsules/Test13
+}
+{
+	image-index: 1
+	monotonic-count: 1
+	private-key: /tmp/capsules/SIGNER2.key
+	pub-key-cert: /tmp/capsules/SIGNER2.crt
+	image-guid: 3673B45D-6A7C-46F3-9E60-ADABB03F7937
+	payload: /tmp/capsules/uboot_bin_env.itb
+	capsule: /tmp/capsules/Test14
+}
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 7/7] doc: Add documentation to describe capsule config file format
  2023-06-13 10:37 [PATCH 0/7] Integrate EFI capsule tasks into u-boot's build flow Sughosh Ganu
                   ` (5 preceding siblings ...)
  2023-06-13 10:38 ` [PATCH 6/7] test: efi_capsule: Test capsule generation from config file Sughosh Ganu
@ 2023-06-13 10:38 ` Sughosh Ganu
  6 siblings, 0 replies; 50+ messages in thread
From: Sughosh Ganu @ 2023-06-13 10:38 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass, Michal Simek,
	Takahiro Akashi, Sughosh Ganu

The UEFI capsule can be generated either through command-line
parameters, or, by specifying those in a config file. Add
documentation to describe the format of the config file.

Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
---
 doc/develop/uefi/uefi.rst | 64 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 64 insertions(+)

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


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* Re: [PATCH 4/7] tools: mkeficapsule: Add support for parsing capsule params from config file
  2023-06-13 10:38 ` [PATCH 4/7] tools: mkeficapsule: Add support for parsing capsule params from config file Sughosh Ganu
@ 2023-06-14  3:39   ` Takahiro Akashi
  2023-06-14  5:26     ` Sughosh Ganu
  0 siblings, 1 reply; 50+ messages in thread
From: Takahiro Akashi @ 2023-06-14  3:39 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Simon Glass, Michal Simek

Hi Sughosh,

I think this is a good extension to mkeficapsule, but

On Tue, Jun 13, 2023 at 04:08:03PM +0530, Sughosh Ganu wrote:
> Add support for specifying the parameters needed for capsule
> generation through a config file, instead of passing them through
> command-line. Parameters for more than a single capsule file can be
> specified, resulting in generation of multiple capsules through a
> single invocation of the command.
> 
> This path is to be used for generating capsules through a make target,
> with the parameters being parsed from the config file.
> 
> Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> ---
>  tools/Kconfig              |   9 +
>  tools/Makefile             |   1 +
>  tools/eficapsule.h         | 110 ++++++++++++
>  tools/mkeficapsule.c       | 106 +++++++-----
>  tools/mkeficapsule_parse.c | 345 +++++++++++++++++++++++++++++++++++++
>  5 files changed, 531 insertions(+), 40 deletions(-)
>  create mode 100644 tools/mkeficapsule_parse.c
> 
> diff --git a/tools/Kconfig b/tools/Kconfig
> index 539708f277..95f27b7c45 100644
> --- a/tools/Kconfig
> +++ b/tools/Kconfig
> @@ -98,6 +98,15 @@ config TOOLS_MKEFICAPSULE
>  	  optionally sign that file. If you want to enable UEFI capsule
>  	  update feature on your target, you certainly need this.
>  
> +config EFI_CAPSULE_CFG_FILE
> +	string "Path to the EFI Capsule Config File"
> +	default ""
> +	help
> +	  Path to the EFI capsule config file which provides the
> +	  parameters needed to build capsule(s). Parameters can be
> +	  provided for multiple payloads resulting in corresponding
> +	  capsule images being generated.
> +
>  menuconfig FSPI_CONF_HEADER
>  	bool "FlexSPI Header Configuration"
>  	help
> diff --git a/tools/Makefile b/tools/Makefile
> index d793cf3bec..ef366f3d61 100644
> --- a/tools/Makefile
> +++ b/tools/Makefile
> @@ -250,6 +250,7 @@ HOSTLDLIBS_mkeficapsule += \
>  HOSTLDLIBS_mkeficapsule += \
>  	$(shell pkg-config --libs uuid 2> /dev/null || echo "-luuid")
>  hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule
> +mkeficapsule-objs := mkeficapsule.o mkeficapsule_parse.o
>  
>  # We build some files with extra pedantic flags to try to minimize things
>  # that won't build on some weird host compiler -- though there are lots of
> diff --git a/tools/eficapsule.h b/tools/eficapsule.h
> index 072a4b5598..42e66c6d6a 100644
> --- a/tools/eficapsule.h
> +++ b/tools/eficapsule.h
> @@ -52,6 +52,38 @@ typedef struct {
>  /* flags */
>  #define CAPSULE_FLAGS_PERSIST_ACROSS_RESET      0x00010000
>  
> +enum capsule_type {
> +	CAPSULE_NORMAL_BLOB = 0,
> +	CAPSULE_ACCEPT,
> +	CAPSULE_REVERT,
> +};
> +
> +/**
> + * struct efi_capsule_params - Capsule parameters
> + * @image_guid: Guid value of the payload input image
> + * @image_index: Image index value
> + * @hardware_instance: Hardware instance to be used for the image
> + * @monotonic_count: Monotonic count value to be used for signed capsule
> + * @privkey_file: Path to private key used in capsule signing
> + * @cert_file: Path to public key certificate used in capsule signing
> + * @input_file: Path to payload input image
> + * @capsule_file: Path to the output capsule file
> + * @oemflags: Oemflags to be populated in the capsule header
> + * @capsule: Capsule Type, normal or accept or revert
> + */
> +struct efi_capsule_params {
> +	efi_guid_t *image_guid;
> +	unsigned long image_index;
> +	unsigned long hardware_instance;
> +	uint64_t monotonic_count;
> +	char *privkey_file;
> +	char *cert_file;
> +	char *input_file;
> +	char *capsule_file;
> +	unsigned long oemflags;
> +	enum capsule_type capsule;
> +};
> +
>  struct efi_capsule_header {
>  	efi_guid_t capsule_guid;
>  	uint32_t header_size;
> @@ -113,4 +145,82 @@ struct efi_firmware_image_authentication {
>  	struct win_certificate_uefi_guid auth_info;
>  } __packed;
>  
> +/**
> + * capsule_with_cfg_file() - Generate capsule from config file
> + * @cfg_file: Path to the config file
> + *
> + * Parse the capsule parameters from the config file and use the
> + * parameters for generating one or more capsules.
> + *
> + * Return: None
> + *
> + */
> +void capsule_with_cfg_file(const char *cfg_file);
> +
> +/**
> + * convert_uuid_to_guid() - convert UUID to GUID
> + * @buf:	UUID binary
> + *
> + * UUID and GUID have the same data structure, but their binary
> + * formats are different due to the endianness. See lib/uuid.c.
> + * Since uuid_parse() can handle only UUID, this function must
> + * be called to get correct data for GUID when parsing a string.
> + *
> + * The correct data will be returned in @buf.
> + */
> +void convert_uuid_to_guid(unsigned char *buf);
> +
> +/**
> + * create_empty_capsule() - Generate an empty capsule
> + * @path: Path to the empty capsule file to be generated
> + * @guid: Guid value of the image for which empty capsule is generated
> + * @fw_accept: Flag to specify whether to generate accept or revert capsule
> + *
> + * Generate an empty capsule, either an accept or a revert capsule to be
> + * used to flag acceptance or rejection of an earlier executed firmware
> + * update operation. Being used in the FWU Multi Bank firmware update
> + * feature.
> + *
> + * Return: 0 if OK, -ve on error
> + *
> + */
> +int create_empty_capsule(char *path, efi_guid_t *guid, bool fw_accept);
> +
> +/**
> + * create_fwbin - create an uefi capsule file
> + * @path:	Path to a created capsule file
> + * @bin:	Path to a firmware binary to encapsulate
> + * @guid:	GUID of related FMP driver
> + * @index:	Index number in capsule
> + * @instance:	Instance number in capsule
> + * @mcount:	Monotonic count in authentication information
> + * @private_file:	Path to a private key file
> + * @cert_file:	Path to a certificate file
> + * @oemflags:  Capsule OEM Flags, bits 0-15
> + *
> + * This function actually does the job of creating an uefi capsule file.
> + * All the arguments must be supplied.
> + * If either @private_file ror @cert_file is NULL, the capsule file
> + * won't be signed.
> + *
> + * Return:
> + * * 0  - on success
> + * * -1 - on failure
> + */
> +int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> +		 unsigned long index, unsigned long instance,
> +		 uint64_t mcount, char *privkey_file, char *cert_file,
> +		 uint16_t oemflags);
> +
> +/**
> + * print_usage() - Print the command usage string
> + *
> + * Prints the standard command usage string. Called in the case
> + * of incorrect parameters being passed to the tool.
> + *
> + * Return: None
> + *
> + */
> +void print_usage(void);
> +
>  #endif /* _EFI_CAPSULE_H */
> diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> index b71537beee..711adf0439 100644
> --- a/tools/mkeficapsule.c
> +++ b/tools/mkeficapsule.c
> @@ -31,12 +31,6 @@ 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 struct option options[] = {
>  	{"guid", required_argument, NULL, 'g'},
>  	{"index", required_argument, NULL, 'i'},
> @@ -52,7 +46,16 @@ static struct option options[] = {
>  	{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"
> @@ -400,10 +403,10 @@ static void free_sig_data(struct auth_context *ctx)
>   * * 0  - on success
>   * * -1 - on failure
>   */
> -static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> -			unsigned long index, unsigned long instance,
> -			uint64_t mcount, char *privkey_file, char *cert_file,
> -			uint16_t oemflags)
> +int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> +		 unsigned long index, unsigned long instance,
> +		 uint64_t mcount, char *privkey_file, char *cert_file,
> +		 uint16_t oemflags)
>  {
>  	struct efi_capsule_header header;
>  	struct efi_firmware_management_capsule_header capsule;
> @@ -580,7 +583,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;
> @@ -623,19 +640,7 @@ err:
>  	return ret;
>  }
>  
> -/**
> - * main - main entry function of mkeficapsule
> - * @argc:	Number of arguments
> - * @argv:	Array of pointers to arguments
> - *
> - * Create an uefi capsule file, optionally signing it.
> - * Parse all the arguments and pass them on to create_fwbin().
> - *
> - * Return:
> - * * 0  - on success
> - * * -1 - on failure
> - */
> -int main(int argc, char **argv)
> +static void capsule_with_cmdline_params(int argc, char **argv)
>  {
>  	efi_guid_t *guid;
>  	unsigned char uuid_buf[16];
> @@ -643,6 +648,7 @@ int main(int argc, char **argv)
>  	uint64_t mcount;
>  	unsigned long oemflags;
>  	char *privkey_file, *cert_file;
> +	enum capsule_type capsule;
>  	int c, idx;
>  
>  	guid = NULL;
> @@ -652,7 +658,7 @@ int main(int argc, char **argv)
>  	privkey_file = NULL;
>  	cert_file = NULL;
>  	dump_sig = 0;
> -	capsule_type = CAPSULE_NORMAL_BLOB;
> +	capsule = CAPSULE_NORMAL_BLOB;
>  	oemflags = 0;
>  	for (;;) {
>  		c = getopt_long(argc, argv, opts_short, options, &idx);
> @@ -702,20 +708,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);
> @@ -732,21 +738,21 @@ int main(int argc, char **argv)
>  	}
>  
>  	/* check necessary parameters */
> -	if ((capsule_type == CAPSULE_NORMAL_BLOB &&
> -	    ((argc != optind + 2) || !guid ||
> -	     ((privkey_file && !cert_file) ||
> -	      (!privkey_file && cert_file)))) ||
> -	    (capsule_type != CAPSULE_NORMAL_BLOB &&
> -	    ((argc != optind + 1) ||
> -	     ((capsule_type == CAPSULE_ACCEPT) && !guid) ||
> -	     ((capsule_type == CAPSULE_REVERT) && guid)))) {
> +	if ((capsule == CAPSULE_NORMAL_BLOB &&
> +	     ((argc != optind + 2) || !guid ||
> +	      ((privkey_file && !cert_file) ||
> +	       (!privkey_file && cert_file)))) ||
> +	    (capsule != CAPSULE_NORMAL_BLOB &&
> +	     ((argc != optind + 1) ||
> +	      (capsule == CAPSULE_ACCEPT && !guid) ||
> +	      (capsule == CAPSULE_REVERT && guid)))) {
>  		print_usage();
>  		exit(EXIT_FAILURE);
>  	}
>  
> -	if (capsule_type != CAPSULE_NORMAL_BLOB) {
> +	if (capsule != CAPSULE_NORMAL_BLOB) {
>  		if (create_empty_capsule(argv[argc - 1], guid,
> -					 capsule_type == CAPSULE_ACCEPT) < 0) {
> +					 capsule == CAPSULE_ACCEPT) < 0) {
>  			fprintf(stderr, "Creating empty capsule failed\n");
>  			exit(EXIT_FAILURE);
>  		}
> @@ -756,6 +762,26 @@ int main(int argc, char **argv)
>  		fprintf(stderr, "Creating firmware capsule failed\n");
>  		exit(EXIT_FAILURE);
>  	}
> +}
> +
> +/**
> + * main - main entry function of mkeficapsule
> + * @argc:	Number of arguments
> + * @argv:	Array of pointers to arguments
> + *
> + * Create an uefi capsule file, optionally signing it.
> + * Parse all the arguments and pass them on to create_fwbin().
> + *
> + * Return:
> + * * 0  - on success
> + * * -1 - on failure
> + */
> +int main(int argc, char **argv)
> +{
> +	if (!strcmp(CONFIG_EFI_CAPSULE_CFG_FILE, ""))
> +		capsule_with_cmdline_params(argc, argv);
> +	else
> +		capsule_with_cfg_file(CONFIG_EFI_CAPSULE_CFG_FILE);

I don't know where the macro, CONFIG_EFI_CAPSULE_CFG_FILE, comes from.
Anyhow, as a general rule, any host tool must be as generic as it should not
depend on a target's config.
(I was told so before.)

So I would suggest that you add another command line, say "--config-file <file>",
to make the command generic.

-Takahiro Akashi


>  
>  	exit(EXIT_SUCCESS);
>  }
> diff --git a/tools/mkeficapsule_parse.c b/tools/mkeficapsule_parse.c
> new file mode 100644
> index 0000000000..ef4f3f6705
> --- /dev/null
> +++ b/tools/mkeficapsule_parse.c
> @@ -0,0 +1,345 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2023 Linaro Limited
> + */
> +
> +/*
> + * The code in this file adds parsing ability to the mkeficapsule
> + * tool. This allows specifying parameters needed to build the capsule
> + * through the config file instead of specifying them on the command-line.
> + * Parameters can be specified for more than one payload, generating the
> + * corresponding capsule files.
> + *
> + * The parameters are specified in a "key:value" pair. All the parameters
> + * that are currently supported by the mkeficapsule tool can be specified
> + * in the config file.
> + *
> + * The example below shows four payloads. The first payload is an example
> + * of generating a signed capsule. The second payload is an example of
> + * generating an unsigned capsule. The third payload is an accept empty
> + * capsule, while the fourth payload is the revert empty capsule, used
> + * for the multi-bank firmware update feature.
> + *
> + * This functionality can be easily extended to generate a single capsule
> + * comprising multiple payloads.
> +
> +	{
> +	    image-guid: 02f4d760-cfd5-43bd-8e2d-a42acb33c660
> +	    hardware-instance: 0
> +	    monotonic-count: 1
> +	    payload: u-boot.bin
> +	    image-index: 1
> +	    private-key: /path/to/priv/key
> +	    pub-key-cert: /path/to/pub/key
> +	    capsule: u-boot.capsule
> +	}
> +	{
> +	    image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e
> +	    hardware-instance: 0
> +	    payload: u-boot.itb
> +	    image-index: 2
> +	    oemflags: 0x8000
> +	    capsule: fit.capsule
> +	}
> +	{
> +	    capsule-type: accept
> +	    image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e
> +	    capsule: accept.capsule
> +	}
> +	{
> +	    capsule-type: revert
> +	    capsule: revert.capsule
> +	}
> +*/
> +
> +#include <ctype.h>
> +#include <limits.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include <uuid/uuid.h>
> +
> +#include "eficapsule.h"
> +
> +#define PARAMS_START	"{"
> +#define PARAMS_END	"}"
> +
> +#define PSTART		2
> +#define PEND		3
> +
> +#define MALLOC_FAIL_STR		"Unable to allocate memory\n"
> +
> +#define ARRAY_SIZE(x)		(sizeof(x) / sizeof((x)[0]))
> +
> +const char *capsule_params[] = {
> +	"image-guid", "image-index", "private-key",
> +	"pub-key-cert", "payload", "capsule",
> +	"hardware-instance", "monotonic-count",
> +	"capsule-type",	"oemflags" };
> +
> +static unsigned char params_start;
> +static unsigned char params_end;
> +
> +static void print_and_exit(const char *str)
> +{
> +	fprintf(stderr, "%s", str);
> +	exit(EXIT_FAILURE);
> +}
> +
> +static int param_delim_checks(char *line, unsigned char *token)
> +{
> +	if (!strcmp(line, PARAMS_START)) {
> +		if (params_start || !params_end) {
> +			fprintf(stderr, "Earlier params processing still in progress. ");
> +			fprintf(stderr, "Can't start processing a new params.\n");
> +			exit(EXIT_FAILURE);
> +		} else {
> +			params_start = 1;
> +			params_end = 0;
> +			*token = PSTART;
> +			return 1;
> +		}
> +	} else if (!strcmp(line, PARAMS_END)) {
> +		if (!params_start) {
> +			fprintf(stderr, "Cannot put end braces without start braces. ");
> +			fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> +			exit(EXIT_FAILURE);
> +		} else {
> +			params_start = 0;
> +			params_end = 1;
> +			*token = PEND;
> +			return 1;
> +		}
> +	} else if (!params_start) {
> +		fprintf(stderr, "Params should be passed within braces. ");
> +		fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	return 0;
> +}
> +
> +static void add_guid(efi_guid_t **guid_param, char *guid)
> +{
> +	unsigned char uuid_buf[16];
> +
> +	*guid_param = malloc(sizeof(efi_guid_t));
> +	if (!*guid_param)
> +		print_and_exit(MALLOC_FAIL_STR);
> +
> +	if (uuid_parse(guid, uuid_buf))
> +		print_and_exit("Wrong guid format\n");
> +
> +	convert_uuid_to_guid(uuid_buf);
> +	memcpy(*guid_param, uuid_buf, sizeof(efi_guid_t));
> +}
> +
> +static void add_string(char **dst, char *val)
> +{
> +	*dst = strdup(val);
> +	if (!*dst)
> +		print_and_exit(MALLOC_FAIL_STR);
> +}
> +
> +static void match_and_populate_param(char *key, char *val,
> +				     struct efi_capsule_params *param)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(capsule_params); i++) {
> +		if (!strcmp(key, capsule_params[i])) {
> +			switch (i) {
> +			case 0:
> +				add_guid(&param->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(&param->privkey_file, val);
> +				return;
> +			case 3:
> +				add_string(&param->cert_file, val);
> +				return;
> +			case 4:
> +				add_string(&param->input_file, val);
> +				return;
> +			case 5:
> +				add_string(&param->capsule_file, val);
> +				return;
> +			case 6:
> +				param->hardware_instance = strtoul(val, NULL, 0);
> +				if (param->hardware_instance == ULONG_MAX)
> +					print_and_exit("Enter a valid hardware instance value");
> +				return;
> +			case 7:
> +				param->monotonic_count = strtoull(val, NULL, 0);
> +				if (param->monotonic_count == ULLONG_MAX)
> +					print_and_exit("Enter a valid monotonic count value");
> +				return;
> +			case 8:
> +				if (!strcmp(val, "normal"))
> +					param->capsule = CAPSULE_NORMAL_BLOB;
> +				else if (!strcmp(val, "accept"))
> +					param->capsule = CAPSULE_ACCEPT;
> +				else if (!strcmp(val, "revert"))
> +					param->capsule = CAPSULE_REVERT;
> +				else
> +					print_and_exit("Invalid type of capsule");
> +
> +				return;
> +			case 9:
> +				param->oemflags = strtoul(val, NULL, 0);
> +				if (param->oemflags > 0xffff)
> +					print_and_exit("OemFlags must be between 0x0 and 0xffff\n");
> +				return;
> +			}
> +		}
> +	}
> +
> +	fprintf(stderr, "Undefined param %s specified. ", key);
> +	fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> +	exit(EXIT_FAILURE);
> +}
> +
> +static int get_capsule_params(char *line, struct efi_capsule_params *params)
> +{
> +	char *key = NULL;
> +	char *val = NULL;
> +	unsigned char token;
> +
> +	if (param_delim_checks(line, &token))
> +		return token;
> +
> +	key = strtok(line, ":");
> +	if (key)
> +		val = strtok(NULL, "\0");
> +	else
> +		print_and_exit("Expect the params in a key:value pair\n");
> +
> +	match_and_populate_param(key, val, params);
> +
> +	return 0;
> +}
> +
> +static char *skip_whitespace(char *line)
> +{
> +	char *ptr, *newline;
> +
> +	ptr = malloc(strlen(line) + 1);
> +	if (!ptr)
> +		print_and_exit(MALLOC_FAIL_STR);
> +
> +	for (newline = ptr; *line; line++)
> +		if (!isblank(*line))
> +			*ptr++ = *line;
> +	*ptr = '\0';
> +	return newline;
> +}
> +
> +static int parse_capsule_payload_params(FILE *fp, struct efi_capsule_params *params)
> +{
> +	char *line = NULL;
> +	char *newline;
> +	size_t n = 0;
> +	ssize_t len;
> +
> +	while ((len = getline(&line, &n, fp)) != -1) {
> +		if (len == 1 && line[len - 1] == '\n')
> +			continue;
> +
> +		line[len - 1] = '\0';
> +
> +		newline = skip_whitespace(line);
> +
> +		if (newline[0] == '#')
> +			continue;
> +
> +		if (get_capsule_params(newline, params) == PEND)
> +			return 0;
> +	}
> +
> +	if (errno == EINVAL || errno == ENOMEM) {
> +		fprintf(stderr, "getline() returned an error %s reading the line\n",
> +			strerror(errno));
> +		exit(EXIT_FAILURE);
> +	} else if (params_start == 1 || params_end == 0) {
> +		fprintf(stderr, "Params should be passed within braces. ");
> +		fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> +		exit(EXIT_FAILURE);
> +	} else {
> +		return -1;
> +	}
> +}
> +
> +static void params_dependency_check(struct efi_capsule_params *params)
> +{
> +	/* check necessary parameters */
> +	if ((params->capsule == CAPSULE_NORMAL_BLOB &&
> +	     ((!params->input_file || !params->capsule_file ||
> +	       !params->image_guid) ||
> +	      ((params->privkey_file && !params->cert_file) ||
> +	       (!params->privkey_file && params->cert_file)))) ||
> +	    (params->capsule != CAPSULE_NORMAL_BLOB &&
> +	     (!params->capsule_file ||
> +	      (params->capsule == CAPSULE_ACCEPT && !params->image_guid) ||
> +	      (params->capsule == CAPSULE_REVERT && params->image_guid)))) {
> +		print_usage();
> +		exit(EXIT_FAILURE);
> +	}
> +}
> +
> +static void generate_capsule(struct efi_capsule_params *params)
> +{
> +	if (params->capsule != CAPSULE_NORMAL_BLOB) {
> +		if (create_empty_capsule(params->capsule_file,
> +					 params->image_guid,
> +					 params->capsule ==
> +					 CAPSULE_ACCEPT) < 0)
> +			print_and_exit("Creating empty capsule failed\n");
> +	} else if (create_fwbin(params->capsule_file, params->input_file,
> +			      params->image_guid, params->image_index,
> +			      params->hardware_instance,
> +			      params->monotonic_count,
> +			      params->privkey_file,
> +			      params->cert_file,
> +			      (uint16_t)params->oemflags) < 0) {
> +		print_and_exit("Creating firmware capsule failed\n");
> +	}
> +}
> +
> +/**
> + * capsule_with_cfg_file() - Generate capsule from config file
> + * @cfg_file: Path to the config file
> + *
> + * Parse the capsule parameters from the config file and use the
> + * parameters for generating one or more capsules.
> + *
> + * Return: None
> + *
> + */
> +void capsule_with_cfg_file(const char *cfg_file)
> +{
> +	FILE *fp;
> +	struct efi_capsule_params params = { 0 };
> +
> +	fp = fopen(cfg_file, "r");
> +	if (!fp) {
> +		fprintf(stderr, "Unable to open the capsule config file %s\n",
> +			cfg_file);
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	params_start = 0;
> +	params_end = 1;
> +
> +	while (parse_capsule_payload_params(fp, &params) != -1) {
> +		params_dependency_check(&params);
> +		generate_capsule(&params);
> +
> +		memset(&params, 0, sizeof(struct efi_capsule_params));
> +	}
> +}
> -- 
> 2.34.1
> 

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 4/7] tools: mkeficapsule: Add support for parsing capsule params from config file
  2023-06-14  3:39   ` Takahiro Akashi
@ 2023-06-14  5:26     ` Sughosh Ganu
  2023-06-14  5:53       ` Takahiro Akashi
  0 siblings, 1 reply; 50+ messages in thread
From: Sughosh Ganu @ 2023-06-14  5:26 UTC (permalink / raw)
  To: Takahiro Akashi, Sughosh Ganu, u-boot, Heinrich Schuchardt,
	Ilias Apalodimas, Simon Glass, Michal Simek

hi Takahiro,

On Wed, 14 Jun 2023 at 09:09, Takahiro Akashi
<takahiro.akashi@linaro.org> wrote:
>
> Hi Sughosh,
>
> I think this is a good extension to mkeficapsule, but
>
> On Tue, Jun 13, 2023 at 04:08:03PM +0530, Sughosh Ganu wrote:
> > Add support for specifying the parameters needed for capsule
> > generation through a config file, instead of passing them through
> > command-line. Parameters for more than a single capsule file can be
> > specified, resulting in generation of multiple capsules through a
> > single invocation of the command.
> >
> > This path is to be used for generating capsules through a make target,
> > with the parameters being parsed from the config file.
> >
> > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > ---
> >  tools/Kconfig              |   9 +
> >  tools/Makefile             |   1 +
> >  tools/eficapsule.h         | 110 ++++++++++++
> >  tools/mkeficapsule.c       | 106 +++++++-----
> >  tools/mkeficapsule_parse.c | 345 +++++++++++++++++++++++++++++++++++++
> >  5 files changed, 531 insertions(+), 40 deletions(-)
> >  create mode 100644 tools/mkeficapsule_parse.c
> >
> > diff --git a/tools/Kconfig b/tools/Kconfig
> > index 539708f277..95f27b7c45 100644
> > --- a/tools/Kconfig
> > +++ b/tools/Kconfig
> > @@ -98,6 +98,15 @@ config TOOLS_MKEFICAPSULE
> >         optionally sign that file. If you want to enable UEFI capsule
> >         update feature on your target, you certainly need this.
> >
> > +config EFI_CAPSULE_CFG_FILE
> > +     string "Path to the EFI Capsule Config File"
> > +     default ""
> > +     help
> > +       Path to the EFI capsule config file which provides the
> > +       parameters needed to build capsule(s). Parameters can be
> > +       provided for multiple payloads resulting in corresponding
> > +       capsule images being generated.
> > +
> >  menuconfig FSPI_CONF_HEADER
> >       bool "FlexSPI Header Configuration"
> >       help
> > diff --git a/tools/Makefile b/tools/Makefile
> > index d793cf3bec..ef366f3d61 100644
> > --- a/tools/Makefile
> > +++ b/tools/Makefile
> > @@ -250,6 +250,7 @@ HOSTLDLIBS_mkeficapsule += \
> >  HOSTLDLIBS_mkeficapsule += \
> >       $(shell pkg-config --libs uuid 2> /dev/null || echo "-luuid")
> >  hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule
> > +mkeficapsule-objs := mkeficapsule.o mkeficapsule_parse.o
> >
> >  # We build some files with extra pedantic flags to try to minimize things
> >  # that won't build on some weird host compiler -- though there are lots of
> > diff --git a/tools/eficapsule.h b/tools/eficapsule.h
> > index 072a4b5598..42e66c6d6a 100644
> > --- a/tools/eficapsule.h
> > +++ b/tools/eficapsule.h
> > @@ -52,6 +52,38 @@ typedef struct {
> >  /* flags */
> >  #define CAPSULE_FLAGS_PERSIST_ACROSS_RESET      0x00010000
> >
> > +enum capsule_type {
> > +     CAPSULE_NORMAL_BLOB = 0,
> > +     CAPSULE_ACCEPT,
> > +     CAPSULE_REVERT,
> > +};
> > +
> > +/**
> > + * struct efi_capsule_params - Capsule parameters
> > + * @image_guid: Guid value of the payload input image
> > + * @image_index: Image index value
> > + * @hardware_instance: Hardware instance to be used for the image
> > + * @monotonic_count: Monotonic count value to be used for signed capsule
> > + * @privkey_file: Path to private key used in capsule signing
> > + * @cert_file: Path to public key certificate used in capsule signing
> > + * @input_file: Path to payload input image
> > + * @capsule_file: Path to the output capsule file
> > + * @oemflags: Oemflags to be populated in the capsule header
> > + * @capsule: Capsule Type, normal or accept or revert
> > + */
> > +struct efi_capsule_params {
> > +     efi_guid_t *image_guid;
> > +     unsigned long image_index;
> > +     unsigned long hardware_instance;
> > +     uint64_t monotonic_count;
> > +     char *privkey_file;
> > +     char *cert_file;
> > +     char *input_file;
> > +     char *capsule_file;
> > +     unsigned long oemflags;
> > +     enum capsule_type capsule;
> > +};
> > +
> >  struct efi_capsule_header {
> >       efi_guid_t capsule_guid;
> >       uint32_t header_size;
> > @@ -113,4 +145,82 @@ struct efi_firmware_image_authentication {
> >       struct win_certificate_uefi_guid auth_info;
> >  } __packed;
> >
> > +/**
> > + * capsule_with_cfg_file() - Generate capsule from config file
> > + * @cfg_file: Path to the config file
> > + *
> > + * Parse the capsule parameters from the config file and use the
> > + * parameters for generating one or more capsules.
> > + *
> > + * Return: None
> > + *
> > + */
> > +void capsule_with_cfg_file(const char *cfg_file);
> > +
> > +/**
> > + * convert_uuid_to_guid() - convert UUID to GUID
> > + * @buf:     UUID binary
> > + *
> > + * UUID and GUID have the same data structure, but their binary
> > + * formats are different due to the endianness. See lib/uuid.c.
> > + * Since uuid_parse() can handle only UUID, this function must
> > + * be called to get correct data for GUID when parsing a string.
> > + *
> > + * The correct data will be returned in @buf.
> > + */
> > +void convert_uuid_to_guid(unsigned char *buf);
> > +
> > +/**
> > + * create_empty_capsule() - Generate an empty capsule
> > + * @path: Path to the empty capsule file to be generated
> > + * @guid: Guid value of the image for which empty capsule is generated
> > + * @fw_accept: Flag to specify whether to generate accept or revert capsule
> > + *
> > + * Generate an empty capsule, either an accept or a revert capsule to be
> > + * used to flag acceptance or rejection of an earlier executed firmware
> > + * update operation. Being used in the FWU Multi Bank firmware update
> > + * feature.
> > + *
> > + * Return: 0 if OK, -ve on error
> > + *
> > + */
> > +int create_empty_capsule(char *path, efi_guid_t *guid, bool fw_accept);
> > +
> > +/**
> > + * create_fwbin - create an uefi capsule file
> > + * @path:    Path to a created capsule file
> > + * @bin:     Path to a firmware binary to encapsulate
> > + * @guid:    GUID of related FMP driver
> > + * @index:   Index number in capsule
> > + * @instance:        Instance number in capsule
> > + * @mcount:  Monotonic count in authentication information
> > + * @private_file:    Path to a private key file
> > + * @cert_file:       Path to a certificate file
> > + * @oemflags:  Capsule OEM Flags, bits 0-15
> > + *
> > + * This function actually does the job of creating an uefi capsule file.
> > + * All the arguments must be supplied.
> > + * If either @private_file ror @cert_file is NULL, the capsule file
> > + * won't be signed.
> > + *
> > + * Return:
> > + * * 0  - on success
> > + * * -1 - on failure
> > + */
> > +int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > +              unsigned long index, unsigned long instance,
> > +              uint64_t mcount, char *privkey_file, char *cert_file,
> > +              uint16_t oemflags);
> > +
> > +/**
> > + * print_usage() - Print the command usage string
> > + *
> > + * Prints the standard command usage string. Called in the case
> > + * of incorrect parameters being passed to the tool.
> > + *
> > + * Return: None
> > + *
> > + */
> > +void print_usage(void);
> > +
> >  #endif /* _EFI_CAPSULE_H */
> > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> > index b71537beee..711adf0439 100644
> > --- a/tools/mkeficapsule.c
> > +++ b/tools/mkeficapsule.c
> > @@ -31,12 +31,6 @@ 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 struct option options[] = {
> >       {"guid", required_argument, NULL, 'g'},
> >       {"index", required_argument, NULL, 'i'},
> > @@ -52,7 +46,16 @@ static struct option options[] = {
> >       {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"
> > @@ -400,10 +403,10 @@ static void free_sig_data(struct auth_context *ctx)
> >   * * 0  - on success
> >   * * -1 - on failure
> >   */
> > -static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > -                     unsigned long index, unsigned long instance,
> > -                     uint64_t mcount, char *privkey_file, char *cert_file,
> > -                     uint16_t oemflags)
> > +int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > +              unsigned long index, unsigned long instance,
> > +              uint64_t mcount, char *privkey_file, char *cert_file,
> > +              uint16_t oemflags)
> >  {
> >       struct efi_capsule_header header;
> >       struct efi_firmware_management_capsule_header capsule;
> > @@ -580,7 +583,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;
> > @@ -623,19 +640,7 @@ err:
> >       return ret;
> >  }
> >
> > -/**
> > - * main - main entry function of mkeficapsule
> > - * @argc:    Number of arguments
> > - * @argv:    Array of pointers to arguments
> > - *
> > - * Create an uefi capsule file, optionally signing it.
> > - * Parse all the arguments and pass them on to create_fwbin().
> > - *
> > - * Return:
> > - * * 0  - on success
> > - * * -1 - on failure
> > - */
> > -int main(int argc, char **argv)
> > +static void capsule_with_cmdline_params(int argc, char **argv)
> >  {
> >       efi_guid_t *guid;
> >       unsigned char uuid_buf[16];
> > @@ -643,6 +648,7 @@ int main(int argc, char **argv)
> >       uint64_t mcount;
> >       unsigned long oemflags;
> >       char *privkey_file, *cert_file;
> > +     enum capsule_type capsule;
> >       int c, idx;
> >
> >       guid = NULL;
> > @@ -652,7 +658,7 @@ int main(int argc, char **argv)
> >       privkey_file = NULL;
> >       cert_file = NULL;
> >       dump_sig = 0;
> > -     capsule_type = CAPSULE_NORMAL_BLOB;
> > +     capsule = CAPSULE_NORMAL_BLOB;
> >       oemflags = 0;
> >       for (;;) {
> >               c = getopt_long(argc, argv, opts_short, options, &idx);
> > @@ -702,20 +708,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);
> > @@ -732,21 +738,21 @@ int main(int argc, char **argv)
> >       }
> >
> >       /* check necessary parameters */
> > -     if ((capsule_type == CAPSULE_NORMAL_BLOB &&
> > -         ((argc != optind + 2) || !guid ||
> > -          ((privkey_file && !cert_file) ||
> > -           (!privkey_file && cert_file)))) ||
> > -         (capsule_type != CAPSULE_NORMAL_BLOB &&
> > -         ((argc != optind + 1) ||
> > -          ((capsule_type == CAPSULE_ACCEPT) && !guid) ||
> > -          ((capsule_type == CAPSULE_REVERT) && guid)))) {
> > +     if ((capsule == CAPSULE_NORMAL_BLOB &&
> > +          ((argc != optind + 2) || !guid ||
> > +           ((privkey_file && !cert_file) ||
> > +            (!privkey_file && cert_file)))) ||
> > +         (capsule != CAPSULE_NORMAL_BLOB &&
> > +          ((argc != optind + 1) ||
> > +           (capsule == CAPSULE_ACCEPT && !guid) ||
> > +           (capsule == CAPSULE_REVERT && guid)))) {
> >               print_usage();
> >               exit(EXIT_FAILURE);
> >       }
> >
> > -     if (capsule_type != CAPSULE_NORMAL_BLOB) {
> > +     if (capsule != CAPSULE_NORMAL_BLOB) {
> >               if (create_empty_capsule(argv[argc - 1], guid,
> > -                                      capsule_type == CAPSULE_ACCEPT) < 0) {
> > +                                      capsule == CAPSULE_ACCEPT) < 0) {
> >                       fprintf(stderr, "Creating empty capsule failed\n");
> >                       exit(EXIT_FAILURE);
> >               }
> > @@ -756,6 +762,26 @@ int main(int argc, char **argv)
> >               fprintf(stderr, "Creating firmware capsule failed\n");
> >               exit(EXIT_FAILURE);
> >       }
> > +}
> > +
> > +/**
> > + * main - main entry function of mkeficapsule
> > + * @argc:    Number of arguments
> > + * @argv:    Array of pointers to arguments
> > + *
> > + * Create an uefi capsule file, optionally signing it.
> > + * Parse all the arguments and pass them on to create_fwbin().
> > + *
> > + * Return:
> > + * * 0  - on success
> > + * * -1 - on failure
> > + */
> > +int main(int argc, char **argv)
> > +{
> > +     if (!strcmp(CONFIG_EFI_CAPSULE_CFG_FILE, ""))
> > +             capsule_with_cmdline_params(argc, argv);
> > +     else
> > +             capsule_with_cfg_file(CONFIG_EFI_CAPSULE_CFG_FILE);
>
> I don't know where the macro, CONFIG_EFI_CAPSULE_CFG_FILE, comes from.
> Anyhow, as a general rule, any host tool must be as generic as it should not
> depend on a target's config.
> (I was told so before.)
>
> So I would suggest that you add another command line, say "--config-file <file>",
> to make the command generic.

Yes, that would be something followed by most of the tools. The reason
I did not add a command-line option for the confile file is because I
want the capsule generation added as a make target. With the path to
the config file specified through the Kconfig symbol, we can invoke
'make capsule', and it would build the capsules by parsing the
parameters from the config file, taken from the Kconfig symbol. I know
there are ways of specifying options when using a make command, but I
don't think that is a clean way of doing things. Given the use case of
a make target, I hope we can use the Kconfig symbol for specifying the
config file path.

-sughosh

>
> -Takahiro Akashi
>
>
> >
> >       exit(EXIT_SUCCESS);
> >  }
> > diff --git a/tools/mkeficapsule_parse.c b/tools/mkeficapsule_parse.c
> > new file mode 100644
> > index 0000000000..ef4f3f6705
> > --- /dev/null
> > +++ b/tools/mkeficapsule_parse.c
> > @@ -0,0 +1,345 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright 2023 Linaro Limited
> > + */
> > +
> > +/*
> > + * The code in this file adds parsing ability to the mkeficapsule
> > + * tool. This allows specifying parameters needed to build the capsule
> > + * through the config file instead of specifying them on the command-line.
> > + * Parameters can be specified for more than one payload, generating the
> > + * corresponding capsule files.
> > + *
> > + * The parameters are specified in a "key:value" pair. All the parameters
> > + * that are currently supported by the mkeficapsule tool can be specified
> > + * in the config file.
> > + *
> > + * The example below shows four payloads. The first payload is an example
> > + * of generating a signed capsule. The second payload is an example of
> > + * generating an unsigned capsule. The third payload is an accept empty
> > + * capsule, while the fourth payload is the revert empty capsule, used
> > + * for the multi-bank firmware update feature.
> > + *
> > + * This functionality can be easily extended to generate a single capsule
> > + * comprising multiple payloads.
> > +
> > +     {
> > +         image-guid: 02f4d760-cfd5-43bd-8e2d-a42acb33c660
> > +         hardware-instance: 0
> > +         monotonic-count: 1
> > +         payload: u-boot.bin
> > +         image-index: 1
> > +         private-key: /path/to/priv/key
> > +         pub-key-cert: /path/to/pub/key
> > +         capsule: u-boot.capsule
> > +     }
> > +     {
> > +         image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e
> > +         hardware-instance: 0
> > +         payload: u-boot.itb
> > +         image-index: 2
> > +         oemflags: 0x8000
> > +         capsule: fit.capsule
> > +     }
> > +     {
> > +         capsule-type: accept
> > +         image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e
> > +         capsule: accept.capsule
> > +     }
> > +     {
> > +         capsule-type: revert
> > +         capsule: revert.capsule
> > +     }
> > +*/
> > +
> > +#include <ctype.h>
> > +#include <limits.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <string.h>
> > +
> > +#include <uuid/uuid.h>
> > +
> > +#include "eficapsule.h"
> > +
> > +#define PARAMS_START "{"
> > +#define PARAMS_END   "}"
> > +
> > +#define PSTART               2
> > +#define PEND         3
> > +
> > +#define MALLOC_FAIL_STR              "Unable to allocate memory\n"
> > +
> > +#define ARRAY_SIZE(x)                (sizeof(x) / sizeof((x)[0]))
> > +
> > +const char *capsule_params[] = {
> > +     "image-guid", "image-index", "private-key",
> > +     "pub-key-cert", "payload", "capsule",
> > +     "hardware-instance", "monotonic-count",
> > +     "capsule-type", "oemflags" };
> > +
> > +static unsigned char params_start;
> > +static unsigned char params_end;
> > +
> > +static void print_and_exit(const char *str)
> > +{
> > +     fprintf(stderr, "%s", str);
> > +     exit(EXIT_FAILURE);
> > +}
> > +
> > +static int param_delim_checks(char *line, unsigned char *token)
> > +{
> > +     if (!strcmp(line, PARAMS_START)) {
> > +             if (params_start || !params_end) {
> > +                     fprintf(stderr, "Earlier params processing still in progress. ");
> > +                     fprintf(stderr, "Can't start processing a new params.\n");
> > +                     exit(EXIT_FAILURE);
> > +             } else {
> > +                     params_start = 1;
> > +                     params_end = 0;
> > +                     *token = PSTART;
> > +                     return 1;
> > +             }
> > +     } else if (!strcmp(line, PARAMS_END)) {
> > +             if (!params_start) {
> > +                     fprintf(stderr, "Cannot put end braces without start braces. ");
> > +                     fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > +                     exit(EXIT_FAILURE);
> > +             } else {
> > +                     params_start = 0;
> > +                     params_end = 1;
> > +                     *token = PEND;
> > +                     return 1;
> > +             }
> > +     } else if (!params_start) {
> > +             fprintf(stderr, "Params should be passed within braces. ");
> > +             fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > +             exit(EXIT_FAILURE);
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static void add_guid(efi_guid_t **guid_param, char *guid)
> > +{
> > +     unsigned char uuid_buf[16];
> > +
> > +     *guid_param = malloc(sizeof(efi_guid_t));
> > +     if (!*guid_param)
> > +             print_and_exit(MALLOC_FAIL_STR);
> > +
> > +     if (uuid_parse(guid, uuid_buf))
> > +             print_and_exit("Wrong guid format\n");
> > +
> > +     convert_uuid_to_guid(uuid_buf);
> > +     memcpy(*guid_param, uuid_buf, sizeof(efi_guid_t));
> > +}
> > +
> > +static void add_string(char **dst, char *val)
> > +{
> > +     *dst = strdup(val);
> > +     if (!*dst)
> > +             print_and_exit(MALLOC_FAIL_STR);
> > +}
> > +
> > +static void match_and_populate_param(char *key, char *val,
> > +                                  struct efi_capsule_params *param)
> > +{
> > +     int i;
> > +
> > +     for (i = 0; i < ARRAY_SIZE(capsule_params); i++) {
> > +             if (!strcmp(key, capsule_params[i])) {
> > +                     switch (i) {
> > +                     case 0:
> > +                             add_guid(&param->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(&param->privkey_file, val);
> > +                             return;
> > +                     case 3:
> > +                             add_string(&param->cert_file, val);
> > +                             return;
> > +                     case 4:
> > +                             add_string(&param->input_file, val);
> > +                             return;
> > +                     case 5:
> > +                             add_string(&param->capsule_file, val);
> > +                             return;
> > +                     case 6:
> > +                             param->hardware_instance = strtoul(val, NULL, 0);
> > +                             if (param->hardware_instance == ULONG_MAX)
> > +                                     print_and_exit("Enter a valid hardware instance value");
> > +                             return;
> > +                     case 7:
> > +                             param->monotonic_count = strtoull(val, NULL, 0);
> > +                             if (param->monotonic_count == ULLONG_MAX)
> > +                                     print_and_exit("Enter a valid monotonic count value");
> > +                             return;
> > +                     case 8:
> > +                             if (!strcmp(val, "normal"))
> > +                                     param->capsule = CAPSULE_NORMAL_BLOB;
> > +                             else if (!strcmp(val, "accept"))
> > +                                     param->capsule = CAPSULE_ACCEPT;
> > +                             else if (!strcmp(val, "revert"))
> > +                                     param->capsule = CAPSULE_REVERT;
> > +                             else
> > +                                     print_and_exit("Invalid type of capsule");
> > +
> > +                             return;
> > +                     case 9:
> > +                             param->oemflags = strtoul(val, NULL, 0);
> > +                             if (param->oemflags > 0xffff)
> > +                                     print_and_exit("OemFlags must be between 0x0 and 0xffff\n");
> > +                             return;
> > +                     }
> > +             }
> > +     }
> > +
> > +     fprintf(stderr, "Undefined param %s specified. ", key);
> > +     fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > +     exit(EXIT_FAILURE);
> > +}
> > +
> > +static int get_capsule_params(char *line, struct efi_capsule_params *params)
> > +{
> > +     char *key = NULL;
> > +     char *val = NULL;
> > +     unsigned char token;
> > +
> > +     if (param_delim_checks(line, &token))
> > +             return token;
> > +
> > +     key = strtok(line, ":");
> > +     if (key)
> > +             val = strtok(NULL, "\0");
> > +     else
> > +             print_and_exit("Expect the params in a key:value pair\n");
> > +
> > +     match_and_populate_param(key, val, params);
> > +
> > +     return 0;
> > +}
> > +
> > +static char *skip_whitespace(char *line)
> > +{
> > +     char *ptr, *newline;
> > +
> > +     ptr = malloc(strlen(line) + 1);
> > +     if (!ptr)
> > +             print_and_exit(MALLOC_FAIL_STR);
> > +
> > +     for (newline = ptr; *line; line++)
> > +             if (!isblank(*line))
> > +                     *ptr++ = *line;
> > +     *ptr = '\0';
> > +     return newline;
> > +}
> > +
> > +static int parse_capsule_payload_params(FILE *fp, struct efi_capsule_params *params)
> > +{
> > +     char *line = NULL;
> > +     char *newline;
> > +     size_t n = 0;
> > +     ssize_t len;
> > +
> > +     while ((len = getline(&line, &n, fp)) != -1) {
> > +             if (len == 1 && line[len - 1] == '\n')
> > +                     continue;
> > +
> > +             line[len - 1] = '\0';
> > +
> > +             newline = skip_whitespace(line);
> > +
> > +             if (newline[0] == '#')
> > +                     continue;
> > +
> > +             if (get_capsule_params(newline, params) == PEND)
> > +                     return 0;
> > +     }
> > +
> > +     if (errno == EINVAL || errno == ENOMEM) {
> > +             fprintf(stderr, "getline() returned an error %s reading the line\n",
> > +                     strerror(errno));
> > +             exit(EXIT_FAILURE);
> > +     } else if (params_start == 1 || params_end == 0) {
> > +             fprintf(stderr, "Params should be passed within braces. ");
> > +             fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > +             exit(EXIT_FAILURE);
> > +     } else {
> > +             return -1;
> > +     }
> > +}
> > +
> > +static void params_dependency_check(struct efi_capsule_params *params)
> > +{
> > +     /* check necessary parameters */
> > +     if ((params->capsule == CAPSULE_NORMAL_BLOB &&
> > +          ((!params->input_file || !params->capsule_file ||
> > +            !params->image_guid) ||
> > +           ((params->privkey_file && !params->cert_file) ||
> > +            (!params->privkey_file && params->cert_file)))) ||
> > +         (params->capsule != CAPSULE_NORMAL_BLOB &&
> > +          (!params->capsule_file ||
> > +           (params->capsule == CAPSULE_ACCEPT && !params->image_guid) ||
> > +           (params->capsule == CAPSULE_REVERT && params->image_guid)))) {
> > +             print_usage();
> > +             exit(EXIT_FAILURE);
> > +     }
> > +}
> > +
> > +static void generate_capsule(struct efi_capsule_params *params)
> > +{
> > +     if (params->capsule != CAPSULE_NORMAL_BLOB) {
> > +             if (create_empty_capsule(params->capsule_file,
> > +                                      params->image_guid,
> > +                                      params->capsule ==
> > +                                      CAPSULE_ACCEPT) < 0)
> > +                     print_and_exit("Creating empty capsule failed\n");
> > +     } else if (create_fwbin(params->capsule_file, params->input_file,
> > +                           params->image_guid, params->image_index,
> > +                           params->hardware_instance,
> > +                           params->monotonic_count,
> > +                           params->privkey_file,
> > +                           params->cert_file,
> > +                           (uint16_t)params->oemflags) < 0) {
> > +             print_and_exit("Creating firmware capsule failed\n");
> > +     }
> > +}
> > +
> > +/**
> > + * capsule_with_cfg_file() - Generate capsule from config file
> > + * @cfg_file: Path to the config file
> > + *
> > + * Parse the capsule parameters from the config file and use the
> > + * parameters for generating one or more capsules.
> > + *
> > + * Return: None
> > + *
> > + */
> > +void capsule_with_cfg_file(const char *cfg_file)
> > +{
> > +     FILE *fp;
> > +     struct efi_capsule_params params = { 0 };
> > +
> > +     fp = fopen(cfg_file, "r");
> > +     if (!fp) {
> > +             fprintf(stderr, "Unable to open the capsule config file %s\n",
> > +                     cfg_file);
> > +             exit(EXIT_FAILURE);
> > +     }
> > +
> > +     params_start = 0;
> > +     params_end = 1;
> > +
> > +     while (parse_capsule_payload_params(fp, &params) != -1) {
> > +             params_dependency_check(&params);
> > +             generate_capsule(&params);
> > +
> > +             memset(&params, 0, sizeof(struct efi_capsule_params));
> > +     }
> > +}
> > --
> > 2.34.1
> >

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 4/7] tools: mkeficapsule: Add support for parsing capsule params from config file
  2023-06-14  5:26     ` Sughosh Ganu
@ 2023-06-14  5:53       ` Takahiro Akashi
  2023-06-15  4:39         ` Sughosh Ganu
  0 siblings, 1 reply; 50+ messages in thread
From: Takahiro Akashi @ 2023-06-14  5:53 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Simon Glass, Michal Simek

On Wed, Jun 14, 2023 at 10:56:23AM +0530, Sughosh Ganu wrote:
> hi Takahiro,
> 
> On Wed, 14 Jun 2023 at 09:09, Takahiro Akashi
> <takahiro.akashi@linaro.org> wrote:
> >
> > Hi Sughosh,
> >
> > I think this is a good extension to mkeficapsule, but
> >
> > On Tue, Jun 13, 2023 at 04:08:03PM +0530, Sughosh Ganu wrote:
> > > Add support for specifying the parameters needed for capsule
> > > generation through a config file, instead of passing them through
> > > command-line. Parameters for more than a single capsule file can be
> > > specified, resulting in generation of multiple capsules through a
> > > single invocation of the command.
> > >
> > > This path is to be used for generating capsules through a make target,
> > > with the parameters being parsed from the config file.
> > >
> > > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > > ---
> > >  tools/Kconfig              |   9 +
> > >  tools/Makefile             |   1 +
> > >  tools/eficapsule.h         | 110 ++++++++++++
> > >  tools/mkeficapsule.c       | 106 +++++++-----
> > >  tools/mkeficapsule_parse.c | 345 +++++++++++++++++++++++++++++++++++++
> > >  5 files changed, 531 insertions(+), 40 deletions(-)
> > >  create mode 100644 tools/mkeficapsule_parse.c
> > >
> > > diff --git a/tools/Kconfig b/tools/Kconfig
> > > index 539708f277..95f27b7c45 100644
> > > --- a/tools/Kconfig
> > > +++ b/tools/Kconfig
> > > @@ -98,6 +98,15 @@ config TOOLS_MKEFICAPSULE
> > >         optionally sign that file. If you want to enable UEFI capsule
> > >         update feature on your target, you certainly need this.
> > >
> > > +config EFI_CAPSULE_CFG_FILE
> > > +     string "Path to the EFI Capsule Config File"
> > > +     default ""
> > > +     help
> > > +       Path to the EFI capsule config file which provides the
> > > +       parameters needed to build capsule(s). Parameters can be
> > > +       provided for multiple payloads resulting in corresponding
> > > +       capsule images being generated.
> > > +
> > >  menuconfig FSPI_CONF_HEADER
> > >       bool "FlexSPI Header Configuration"
> > >       help
> > > diff --git a/tools/Makefile b/tools/Makefile
> > > index d793cf3bec..ef366f3d61 100644
> > > --- a/tools/Makefile
> > > +++ b/tools/Makefile
> > > @@ -250,6 +250,7 @@ HOSTLDLIBS_mkeficapsule += \
> > >  HOSTLDLIBS_mkeficapsule += \
> > >       $(shell pkg-config --libs uuid 2> /dev/null || echo "-luuid")
> > >  hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule
> > > +mkeficapsule-objs := mkeficapsule.o mkeficapsule_parse.o
> > >
> > >  # We build some files with extra pedantic flags to try to minimize things
> > >  # that won't build on some weird host compiler -- though there are lots of
> > > diff --git a/tools/eficapsule.h b/tools/eficapsule.h
> > > index 072a4b5598..42e66c6d6a 100644
> > > --- a/tools/eficapsule.h
> > > +++ b/tools/eficapsule.h
> > > @@ -52,6 +52,38 @@ typedef struct {
> > >  /* flags */
> > >  #define CAPSULE_FLAGS_PERSIST_ACROSS_RESET      0x00010000
> > >
> > > +enum capsule_type {
> > > +     CAPSULE_NORMAL_BLOB = 0,
> > > +     CAPSULE_ACCEPT,
> > > +     CAPSULE_REVERT,
> > > +};
> > > +
> > > +/**
> > > + * struct efi_capsule_params - Capsule parameters
> > > + * @image_guid: Guid value of the payload input image
> > > + * @image_index: Image index value
> > > + * @hardware_instance: Hardware instance to be used for the image
> > > + * @monotonic_count: Monotonic count value to be used for signed capsule
> > > + * @privkey_file: Path to private key used in capsule signing
> > > + * @cert_file: Path to public key certificate used in capsule signing
> > > + * @input_file: Path to payload input image
> > > + * @capsule_file: Path to the output capsule file
> > > + * @oemflags: Oemflags to be populated in the capsule header
> > > + * @capsule: Capsule Type, normal or accept or revert
> > > + */
> > > +struct efi_capsule_params {
> > > +     efi_guid_t *image_guid;
> > > +     unsigned long image_index;
> > > +     unsigned long hardware_instance;
> > > +     uint64_t monotonic_count;
> > > +     char *privkey_file;
> > > +     char *cert_file;
> > > +     char *input_file;
> > > +     char *capsule_file;
> > > +     unsigned long oemflags;
> > > +     enum capsule_type capsule;
> > > +};
> > > +
> > >  struct efi_capsule_header {
> > >       efi_guid_t capsule_guid;
> > >       uint32_t header_size;
> > > @@ -113,4 +145,82 @@ struct efi_firmware_image_authentication {
> > >       struct win_certificate_uefi_guid auth_info;
> > >  } __packed;
> > >
> > > +/**
> > > + * capsule_with_cfg_file() - Generate capsule from config file
> > > + * @cfg_file: Path to the config file
> > > + *
> > > + * Parse the capsule parameters from the config file and use the
> > > + * parameters for generating one or more capsules.
> > > + *
> > > + * Return: None
> > > + *
> > > + */
> > > +void capsule_with_cfg_file(const char *cfg_file);
> > > +
> > > +/**
> > > + * convert_uuid_to_guid() - convert UUID to GUID
> > > + * @buf:     UUID binary
> > > + *
> > > + * UUID and GUID have the same data structure, but their binary
> > > + * formats are different due to the endianness. See lib/uuid.c.
> > > + * Since uuid_parse() can handle only UUID, this function must
> > > + * be called to get correct data for GUID when parsing a string.
> > > + *
> > > + * The correct data will be returned in @buf.
> > > + */
> > > +void convert_uuid_to_guid(unsigned char *buf);
> > > +
> > > +/**
> > > + * create_empty_capsule() - Generate an empty capsule
> > > + * @path: Path to the empty capsule file to be generated
> > > + * @guid: Guid value of the image for which empty capsule is generated
> > > + * @fw_accept: Flag to specify whether to generate accept or revert capsule
> > > + *
> > > + * Generate an empty capsule, either an accept or a revert capsule to be
> > > + * used to flag acceptance or rejection of an earlier executed firmware
> > > + * update operation. Being used in the FWU Multi Bank firmware update
> > > + * feature.
> > > + *
> > > + * Return: 0 if OK, -ve on error
> > > + *
> > > + */
> > > +int create_empty_capsule(char *path, efi_guid_t *guid, bool fw_accept);
> > > +
> > > +/**
> > > + * create_fwbin - create an uefi capsule file
> > > + * @path:    Path to a created capsule file
> > > + * @bin:     Path to a firmware binary to encapsulate
> > > + * @guid:    GUID of related FMP driver
> > > + * @index:   Index number in capsule
> > > + * @instance:        Instance number in capsule
> > > + * @mcount:  Monotonic count in authentication information
> > > + * @private_file:    Path to a private key file
> > > + * @cert_file:       Path to a certificate file
> > > + * @oemflags:  Capsule OEM Flags, bits 0-15
> > > + *
> > > + * This function actually does the job of creating an uefi capsule file.
> > > + * All the arguments must be supplied.
> > > + * If either @private_file ror @cert_file is NULL, the capsule file
> > > + * won't be signed.
> > > + *
> > > + * Return:
> > > + * * 0  - on success
> > > + * * -1 - on failure
> > > + */
> > > +int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > +              unsigned long index, unsigned long instance,
> > > +              uint64_t mcount, char *privkey_file, char *cert_file,
> > > +              uint16_t oemflags);
> > > +
> > > +/**
> > > + * print_usage() - Print the command usage string
> > > + *
> > > + * Prints the standard command usage string. Called in the case
> > > + * of incorrect parameters being passed to the tool.
> > > + *
> > > + * Return: None
> > > + *
> > > + */
> > > +void print_usage(void);
> > > +
> > >  #endif /* _EFI_CAPSULE_H */
> > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> > > index b71537beee..711adf0439 100644
> > > --- a/tools/mkeficapsule.c
> > > +++ b/tools/mkeficapsule.c
> > > @@ -31,12 +31,6 @@ 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 struct option options[] = {
> > >       {"guid", required_argument, NULL, 'g'},
> > >       {"index", required_argument, NULL, 'i'},
> > > @@ -52,7 +46,16 @@ static struct option options[] = {
> > >       {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"
> > > @@ -400,10 +403,10 @@ static void free_sig_data(struct auth_context *ctx)
> > >   * * 0  - on success
> > >   * * -1 - on failure
> > >   */
> > > -static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > -                     unsigned long index, unsigned long instance,
> > > -                     uint64_t mcount, char *privkey_file, char *cert_file,
> > > -                     uint16_t oemflags)
> > > +int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > +              unsigned long index, unsigned long instance,
> > > +              uint64_t mcount, char *privkey_file, char *cert_file,
> > > +              uint16_t oemflags)
> > >  {
> > >       struct efi_capsule_header header;
> > >       struct efi_firmware_management_capsule_header capsule;
> > > @@ -580,7 +583,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;
> > > @@ -623,19 +640,7 @@ err:
> > >       return ret;
> > >  }
> > >
> > > -/**
> > > - * main - main entry function of mkeficapsule
> > > - * @argc:    Number of arguments
> > > - * @argv:    Array of pointers to arguments
> > > - *
> > > - * Create an uefi capsule file, optionally signing it.
> > > - * Parse all the arguments and pass them on to create_fwbin().
> > > - *
> > > - * Return:
> > > - * * 0  - on success
> > > - * * -1 - on failure
> > > - */
> > > -int main(int argc, char **argv)
> > > +static void capsule_with_cmdline_params(int argc, char **argv)
> > >  {
> > >       efi_guid_t *guid;
> > >       unsigned char uuid_buf[16];
> > > @@ -643,6 +648,7 @@ int main(int argc, char **argv)
> > >       uint64_t mcount;
> > >       unsigned long oemflags;
> > >       char *privkey_file, *cert_file;
> > > +     enum capsule_type capsule;
> > >       int c, idx;
> > >
> > >       guid = NULL;
> > > @@ -652,7 +658,7 @@ int main(int argc, char **argv)
> > >       privkey_file = NULL;
> > >       cert_file = NULL;
> > >       dump_sig = 0;
> > > -     capsule_type = CAPSULE_NORMAL_BLOB;
> > > +     capsule = CAPSULE_NORMAL_BLOB;
> > >       oemflags = 0;
> > >       for (;;) {
> > >               c = getopt_long(argc, argv, opts_short, options, &idx);
> > > @@ -702,20 +708,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);
> > > @@ -732,21 +738,21 @@ int main(int argc, char **argv)
> > >       }
> > >
> > >       /* check necessary parameters */
> > > -     if ((capsule_type == CAPSULE_NORMAL_BLOB &&
> > > -         ((argc != optind + 2) || !guid ||
> > > -          ((privkey_file && !cert_file) ||
> > > -           (!privkey_file && cert_file)))) ||
> > > -         (capsule_type != CAPSULE_NORMAL_BLOB &&
> > > -         ((argc != optind + 1) ||
> > > -          ((capsule_type == CAPSULE_ACCEPT) && !guid) ||
> > > -          ((capsule_type == CAPSULE_REVERT) && guid)))) {
> > > +     if ((capsule == CAPSULE_NORMAL_BLOB &&
> > > +          ((argc != optind + 2) || !guid ||
> > > +           ((privkey_file && !cert_file) ||
> > > +            (!privkey_file && cert_file)))) ||
> > > +         (capsule != CAPSULE_NORMAL_BLOB &&
> > > +          ((argc != optind + 1) ||
> > > +           (capsule == CAPSULE_ACCEPT && !guid) ||
> > > +           (capsule == CAPSULE_REVERT && guid)))) {
> > >               print_usage();
> > >               exit(EXIT_FAILURE);
> > >       }
> > >
> > > -     if (capsule_type != CAPSULE_NORMAL_BLOB) {
> > > +     if (capsule != CAPSULE_NORMAL_BLOB) {
> > >               if (create_empty_capsule(argv[argc - 1], guid,
> > > -                                      capsule_type == CAPSULE_ACCEPT) < 0) {
> > > +                                      capsule == CAPSULE_ACCEPT) < 0) {
> > >                       fprintf(stderr, "Creating empty capsule failed\n");
> > >                       exit(EXIT_FAILURE);
> > >               }
> > > @@ -756,6 +762,26 @@ int main(int argc, char **argv)
> > >               fprintf(stderr, "Creating firmware capsule failed\n");
> > >               exit(EXIT_FAILURE);
> > >       }
> > > +}
> > > +
> > > +/**
> > > + * main - main entry function of mkeficapsule
> > > + * @argc:    Number of arguments
> > > + * @argv:    Array of pointers to arguments
> > > + *
> > > + * Create an uefi capsule file, optionally signing it.
> > > + * Parse all the arguments and pass them on to create_fwbin().
> > > + *
> > > + * Return:
> > > + * * 0  - on success
> > > + * * -1 - on failure
> > > + */
> > > +int main(int argc, char **argv)
> > > +{
> > > +     if (!strcmp(CONFIG_EFI_CAPSULE_CFG_FILE, ""))
> > > +             capsule_with_cmdline_params(argc, argv);
> > > +     else
> > > +             capsule_with_cfg_file(CONFIG_EFI_CAPSULE_CFG_FILE);
> >
> > I don't know where the macro, CONFIG_EFI_CAPSULE_CFG_FILE, comes from.
> > Anyhow, as a general rule, any host tool must be as generic as it should not
> > depend on a target's config.
> > (I was told so before.)
> >
> > So I would suggest that you add another command line, say "--config-file <file>",
> > to make the command generic.
> 
> Yes, that would be something followed by most of the tools. The reason
> I did not add a command-line option for the confile file is because I
> want the capsule generation added as a make target. With the path to
> the config file specified through the Kconfig symbol, we can invoke
> 'make capsule', and it would build the capsules by parsing the
> parameters from the config file, taken from the Kconfig symbol. I know
> there are ways of specifying options when using a make command, but I
> don't think that is a clean way of doing things.

Not sure, but in your [5/7],
cmd_mkeficapsule = $(objtree)/tools/mkeficapsule --config-file $(CONFIG_EFI_CAPSULE_CFG_FILE)

Doesn't this change work?

-Takahiro Akashi


> Given the use case of
> a make target, I hope we can use the Kconfig symbol for specifying the
> config file path.
> 
> -sughosh
> 
> >
> > -Takahiro Akashi
> >
> >
> > >
> > >       exit(EXIT_SUCCESS);
> > >  }
> > > diff --git a/tools/mkeficapsule_parse.c b/tools/mkeficapsule_parse.c
> > > new file mode 100644
> > > index 0000000000..ef4f3f6705
> > > --- /dev/null
> > > +++ b/tools/mkeficapsule_parse.c
> > > @@ -0,0 +1,345 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/*
> > > + * Copyright 2023 Linaro Limited
> > > + */
> > > +
> > > +/*
> > > + * The code in this file adds parsing ability to the mkeficapsule
> > > + * tool. This allows specifying parameters needed to build the capsule
> > > + * through the config file instead of specifying them on the command-line.
> > > + * Parameters can be specified for more than one payload, generating the
> > > + * corresponding capsule files.
> > > + *
> > > + * The parameters are specified in a "key:value" pair. All the parameters
> > > + * that are currently supported by the mkeficapsule tool can be specified
> > > + * in the config file.
> > > + *
> > > + * The example below shows four payloads. The first payload is an example
> > > + * of generating a signed capsule. The second payload is an example of
> > > + * generating an unsigned capsule. The third payload is an accept empty
> > > + * capsule, while the fourth payload is the revert empty capsule, used
> > > + * for the multi-bank firmware update feature.
> > > + *
> > > + * This functionality can be easily extended to generate a single capsule
> > > + * comprising multiple payloads.
> > > +
> > > +     {
> > > +         image-guid: 02f4d760-cfd5-43bd-8e2d-a42acb33c660
> > > +         hardware-instance: 0
> > > +         monotonic-count: 1
> > > +         payload: u-boot.bin
> > > +         image-index: 1
> > > +         private-key: /path/to/priv/key
> > > +         pub-key-cert: /path/to/pub/key
> > > +         capsule: u-boot.capsule
> > > +     }
> > > +     {
> > > +         image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e
> > > +         hardware-instance: 0
> > > +         payload: u-boot.itb
> > > +         image-index: 2
> > > +         oemflags: 0x8000
> > > +         capsule: fit.capsule
> > > +     }
> > > +     {
> > > +         capsule-type: accept
> > > +         image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e
> > > +         capsule: accept.capsule
> > > +     }
> > > +     {
> > > +         capsule-type: revert
> > > +         capsule: revert.capsule
> > > +     }
> > > +*/
> > > +
> > > +#include <ctype.h>
> > > +#include <limits.h>
> > > +#include <stdio.h>
> > > +#include <stdlib.h>
> > > +#include <string.h>
> > > +
> > > +#include <uuid/uuid.h>
> > > +
> > > +#include "eficapsule.h"
> > > +
> > > +#define PARAMS_START "{"
> > > +#define PARAMS_END   "}"
> > > +
> > > +#define PSTART               2
> > > +#define PEND         3
> > > +
> > > +#define MALLOC_FAIL_STR              "Unable to allocate memory\n"
> > > +
> > > +#define ARRAY_SIZE(x)                (sizeof(x) / sizeof((x)[0]))
> > > +
> > > +const char *capsule_params[] = {
> > > +     "image-guid", "image-index", "private-key",
> > > +     "pub-key-cert", "payload", "capsule",
> > > +     "hardware-instance", "monotonic-count",
> > > +     "capsule-type", "oemflags" };
> > > +
> > > +static unsigned char params_start;
> > > +static unsigned char params_end;
> > > +
> > > +static void print_and_exit(const char *str)
> > > +{
> > > +     fprintf(stderr, "%s", str);
> > > +     exit(EXIT_FAILURE);
> > > +}
> > > +
> > > +static int param_delim_checks(char *line, unsigned char *token)
> > > +{
> > > +     if (!strcmp(line, PARAMS_START)) {
> > > +             if (params_start || !params_end) {
> > > +                     fprintf(stderr, "Earlier params processing still in progress. ");
> > > +                     fprintf(stderr, "Can't start processing a new params.\n");
> > > +                     exit(EXIT_FAILURE);
> > > +             } else {
> > > +                     params_start = 1;
> > > +                     params_end = 0;
> > > +                     *token = PSTART;
> > > +                     return 1;
> > > +             }
> > > +     } else if (!strcmp(line, PARAMS_END)) {
> > > +             if (!params_start) {
> > > +                     fprintf(stderr, "Cannot put end braces without start braces. ");
> > > +                     fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > +                     exit(EXIT_FAILURE);
> > > +             } else {
> > > +                     params_start = 0;
> > > +                     params_end = 1;
> > > +                     *token = PEND;
> > > +                     return 1;
> > > +             }
> > > +     } else if (!params_start) {
> > > +             fprintf(stderr, "Params should be passed within braces. ");
> > > +             fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > +             exit(EXIT_FAILURE);
> > > +     }
> > > +
> > > +     return 0;
> > > +}
> > > +
> > > +static void add_guid(efi_guid_t **guid_param, char *guid)
> > > +{
> > > +     unsigned char uuid_buf[16];
> > > +
> > > +     *guid_param = malloc(sizeof(efi_guid_t));
> > > +     if (!*guid_param)
> > > +             print_and_exit(MALLOC_FAIL_STR);
> > > +
> > > +     if (uuid_parse(guid, uuid_buf))
> > > +             print_and_exit("Wrong guid format\n");
> > > +
> > > +     convert_uuid_to_guid(uuid_buf);
> > > +     memcpy(*guid_param, uuid_buf, sizeof(efi_guid_t));
> > > +}
> > > +
> > > +static void add_string(char **dst, char *val)
> > > +{
> > > +     *dst = strdup(val);
> > > +     if (!*dst)
> > > +             print_and_exit(MALLOC_FAIL_STR);
> > > +}
> > > +
> > > +static void match_and_populate_param(char *key, char *val,
> > > +                                  struct efi_capsule_params *param)
> > > +{
> > > +     int i;
> > > +
> > > +     for (i = 0; i < ARRAY_SIZE(capsule_params); i++) {
> > > +             if (!strcmp(key, capsule_params[i])) {
> > > +                     switch (i) {
> > > +                     case 0:
> > > +                             add_guid(&param->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(&param->privkey_file, val);
> > > +                             return;
> > > +                     case 3:
> > > +                             add_string(&param->cert_file, val);
> > > +                             return;
> > > +                     case 4:
> > > +                             add_string(&param->input_file, val);
> > > +                             return;
> > > +                     case 5:
> > > +                             add_string(&param->capsule_file, val);
> > > +                             return;
> > > +                     case 6:
> > > +                             param->hardware_instance = strtoul(val, NULL, 0);
> > > +                             if (param->hardware_instance == ULONG_MAX)
> > > +                                     print_and_exit("Enter a valid hardware instance value");
> > > +                             return;
> > > +                     case 7:
> > > +                             param->monotonic_count = strtoull(val, NULL, 0);
> > > +                             if (param->monotonic_count == ULLONG_MAX)
> > > +                                     print_and_exit("Enter a valid monotonic count value");
> > > +                             return;
> > > +                     case 8:
> > > +                             if (!strcmp(val, "normal"))
> > > +                                     param->capsule = CAPSULE_NORMAL_BLOB;
> > > +                             else if (!strcmp(val, "accept"))
> > > +                                     param->capsule = CAPSULE_ACCEPT;
> > > +                             else if (!strcmp(val, "revert"))
> > > +                                     param->capsule = CAPSULE_REVERT;
> > > +                             else
> > > +                                     print_and_exit("Invalid type of capsule");
> > > +
> > > +                             return;
> > > +                     case 9:
> > > +                             param->oemflags = strtoul(val, NULL, 0);
> > > +                             if (param->oemflags > 0xffff)
> > > +                                     print_and_exit("OemFlags must be between 0x0 and 0xffff\n");
> > > +                             return;
> > > +                     }
> > > +             }
> > > +     }
> > > +
> > > +     fprintf(stderr, "Undefined param %s specified. ", key);
> > > +     fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > +     exit(EXIT_FAILURE);
> > > +}
> > > +
> > > +static int get_capsule_params(char *line, struct efi_capsule_params *params)
> > > +{
> > > +     char *key = NULL;
> > > +     char *val = NULL;
> > > +     unsigned char token;
> > > +
> > > +     if (param_delim_checks(line, &token))
> > > +             return token;
> > > +
> > > +     key = strtok(line, ":");
> > > +     if (key)
> > > +             val = strtok(NULL, "\0");
> > > +     else
> > > +             print_and_exit("Expect the params in a key:value pair\n");
> > > +
> > > +     match_and_populate_param(key, val, params);
> > > +
> > > +     return 0;
> > > +}
> > > +
> > > +static char *skip_whitespace(char *line)
> > > +{
> > > +     char *ptr, *newline;
> > > +
> > > +     ptr = malloc(strlen(line) + 1);
> > > +     if (!ptr)
> > > +             print_and_exit(MALLOC_FAIL_STR);
> > > +
> > > +     for (newline = ptr; *line; line++)
> > > +             if (!isblank(*line))
> > > +                     *ptr++ = *line;
> > > +     *ptr = '\0';
> > > +     return newline;
> > > +}
> > > +
> > > +static int parse_capsule_payload_params(FILE *fp, struct efi_capsule_params *params)
> > > +{
> > > +     char *line = NULL;
> > > +     char *newline;
> > > +     size_t n = 0;
> > > +     ssize_t len;
> > > +
> > > +     while ((len = getline(&line, &n, fp)) != -1) {
> > > +             if (len == 1 && line[len - 1] == '\n')
> > > +                     continue;
> > > +
> > > +             line[len - 1] = '\0';
> > > +
> > > +             newline = skip_whitespace(line);
> > > +
> > > +             if (newline[0] == '#')
> > > +                     continue;
> > > +
> > > +             if (get_capsule_params(newline, params) == PEND)
> > > +                     return 0;
> > > +     }
> > > +
> > > +     if (errno == EINVAL || errno == ENOMEM) {
> > > +             fprintf(stderr, "getline() returned an error %s reading the line\n",
> > > +                     strerror(errno));
> > > +             exit(EXIT_FAILURE);
> > > +     } else if (params_start == 1 || params_end == 0) {
> > > +             fprintf(stderr, "Params should be passed within braces. ");
> > > +             fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > +             exit(EXIT_FAILURE);
> > > +     } else {
> > > +             return -1;
> > > +     }
> > > +}
> > > +
> > > +static void params_dependency_check(struct efi_capsule_params *params)
> > > +{
> > > +     /* check necessary parameters */
> > > +     if ((params->capsule == CAPSULE_NORMAL_BLOB &&
> > > +          ((!params->input_file || !params->capsule_file ||
> > > +            !params->image_guid) ||
> > > +           ((params->privkey_file && !params->cert_file) ||
> > > +            (!params->privkey_file && params->cert_file)))) ||
> > > +         (params->capsule != CAPSULE_NORMAL_BLOB &&
> > > +          (!params->capsule_file ||
> > > +           (params->capsule == CAPSULE_ACCEPT && !params->image_guid) ||
> > > +           (params->capsule == CAPSULE_REVERT && params->image_guid)))) {
> > > +             print_usage();
> > > +             exit(EXIT_FAILURE);
> > > +     }
> > > +}
> > > +
> > > +static void generate_capsule(struct efi_capsule_params *params)
> > > +{
> > > +     if (params->capsule != CAPSULE_NORMAL_BLOB) {
> > > +             if (create_empty_capsule(params->capsule_file,
> > > +                                      params->image_guid,
> > > +                                      params->capsule ==
> > > +                                      CAPSULE_ACCEPT) < 0)
> > > +                     print_and_exit("Creating empty capsule failed\n");
> > > +     } else if (create_fwbin(params->capsule_file, params->input_file,
> > > +                           params->image_guid, params->image_index,
> > > +                           params->hardware_instance,
> > > +                           params->monotonic_count,
> > > +                           params->privkey_file,
> > > +                           params->cert_file,
> > > +                           (uint16_t)params->oemflags) < 0) {
> > > +             print_and_exit("Creating firmware capsule failed\n");
> > > +     }
> > > +}
> > > +
> > > +/**
> > > + * capsule_with_cfg_file() - Generate capsule from config file
> > > + * @cfg_file: Path to the config file
> > > + *
> > > + * Parse the capsule parameters from the config file and use the
> > > + * parameters for generating one or more capsules.
> > > + *
> > > + * Return: None
> > > + *
> > > + */
> > > +void capsule_with_cfg_file(const char *cfg_file)
> > > +{
> > > +     FILE *fp;
> > > +     struct efi_capsule_params params = { 0 };
> > > +
> > > +     fp = fopen(cfg_file, "r");
> > > +     if (!fp) {
> > > +             fprintf(stderr, "Unable to open the capsule config file %s\n",
> > > +                     cfg_file);
> > > +             exit(EXIT_FAILURE);
> > > +     }
> > > +
> > > +     params_start = 0;
> > > +     params_end = 1;
> > > +
> > > +     while (parse_capsule_payload_params(fp, &params) != -1) {
> > > +             params_dependency_check(&params);
> > > +             generate_capsule(&params);
> > > +
> > > +             memset(&params, 0, sizeof(struct efi_capsule_params));
> > > +     }
> > > +}
> > > --
> > > 2.34.1
> > >

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 4/7] tools: mkeficapsule: Add support for parsing capsule params from config file
  2023-06-14  5:53       ` Takahiro Akashi
@ 2023-06-15  4:39         ` Sughosh Ganu
  2023-06-15  5:49           ` Takahiro Akashi
  0 siblings, 1 reply; 50+ messages in thread
From: Sughosh Ganu @ 2023-06-15  4:39 UTC (permalink / raw)
  To: Takahiro Akashi, Sughosh Ganu, u-boot, Heinrich Schuchardt,
	Ilias Apalodimas, Simon Glass, Michal Simek

On Wed, 14 Jun 2023 at 11:23, Takahiro Akashi
<takahiro.akashi@linaro.org> wrote:
>
> On Wed, Jun 14, 2023 at 10:56:23AM +0530, Sughosh Ganu wrote:
> > hi Takahiro,
> >
> > On Wed, 14 Jun 2023 at 09:09, Takahiro Akashi
> > <takahiro.akashi@linaro.org> wrote:
> > >
> > > Hi Sughosh,
> > >
> > > I think this is a good extension to mkeficapsule, but
> > >
> > > On Tue, Jun 13, 2023 at 04:08:03PM +0530, Sughosh Ganu wrote:
> > > > Add support for specifying the parameters needed for capsule
> > > > generation through a config file, instead of passing them through
> > > > command-line. Parameters for more than a single capsule file can be
> > > > specified, resulting in generation of multiple capsules through a
> > > > single invocation of the command.
> > > >
> > > > This path is to be used for generating capsules through a make target,
> > > > with the parameters being parsed from the config file.
> > > >
> > > > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > > > ---
> > > >  tools/Kconfig              |   9 +
> > > >  tools/Makefile             |   1 +
> > > >  tools/eficapsule.h         | 110 ++++++++++++
> > > >  tools/mkeficapsule.c       | 106 +++++++-----
> > > >  tools/mkeficapsule_parse.c | 345 +++++++++++++++++++++++++++++++++++++
> > > >  5 files changed, 531 insertions(+), 40 deletions(-)
> > > >  create mode 100644 tools/mkeficapsule_parse.c
> > > >
> > > > diff --git a/tools/Kconfig b/tools/Kconfig
> > > > index 539708f277..95f27b7c45 100644
> > > > --- a/tools/Kconfig
> > > > +++ b/tools/Kconfig
> > > > @@ -98,6 +98,15 @@ config TOOLS_MKEFICAPSULE
> > > >         optionally sign that file. If you want to enable UEFI capsule
> > > >         update feature on your target, you certainly need this.
> > > >
> > > > +config EFI_CAPSULE_CFG_FILE
> > > > +     string "Path to the EFI Capsule Config File"
> > > > +     default ""
> > > > +     help
> > > > +       Path to the EFI capsule config file which provides the
> > > > +       parameters needed to build capsule(s). Parameters can be
> > > > +       provided for multiple payloads resulting in corresponding
> > > > +       capsule images being generated.
> > > > +
> > > >  menuconfig FSPI_CONF_HEADER
> > > >       bool "FlexSPI Header Configuration"
> > > >       help
> > > > diff --git a/tools/Makefile b/tools/Makefile
> > > > index d793cf3bec..ef366f3d61 100644
> > > > --- a/tools/Makefile
> > > > +++ b/tools/Makefile
> > > > @@ -250,6 +250,7 @@ HOSTLDLIBS_mkeficapsule += \
> > > >  HOSTLDLIBS_mkeficapsule += \
> > > >       $(shell pkg-config --libs uuid 2> /dev/null || echo "-luuid")
> > > >  hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule
> > > > +mkeficapsule-objs := mkeficapsule.o mkeficapsule_parse.o
> > > >
> > > >  # We build some files with extra pedantic flags to try to minimize things
> > > >  # that won't build on some weird host compiler -- though there are lots of
> > > > diff --git a/tools/eficapsule.h b/tools/eficapsule.h
> > > > index 072a4b5598..42e66c6d6a 100644
> > > > --- a/tools/eficapsule.h
> > > > +++ b/tools/eficapsule.h
> > > > @@ -52,6 +52,38 @@ typedef struct {
> > > >  /* flags */
> > > >  #define CAPSULE_FLAGS_PERSIST_ACROSS_RESET      0x00010000
> > > >
> > > > +enum capsule_type {
> > > > +     CAPSULE_NORMAL_BLOB = 0,
> > > > +     CAPSULE_ACCEPT,
> > > > +     CAPSULE_REVERT,
> > > > +};
> > > > +
> > > > +/**
> > > > + * struct efi_capsule_params - Capsule parameters
> > > > + * @image_guid: Guid value of the payload input image
> > > > + * @image_index: Image index value
> > > > + * @hardware_instance: Hardware instance to be used for the image
> > > > + * @monotonic_count: Monotonic count value to be used for signed capsule
> > > > + * @privkey_file: Path to private key used in capsule signing
> > > > + * @cert_file: Path to public key certificate used in capsule signing
> > > > + * @input_file: Path to payload input image
> > > > + * @capsule_file: Path to the output capsule file
> > > > + * @oemflags: Oemflags to be populated in the capsule header
> > > > + * @capsule: Capsule Type, normal or accept or revert
> > > > + */
> > > > +struct efi_capsule_params {
> > > > +     efi_guid_t *image_guid;
> > > > +     unsigned long image_index;
> > > > +     unsigned long hardware_instance;
> > > > +     uint64_t monotonic_count;
> > > > +     char *privkey_file;
> > > > +     char *cert_file;
> > > > +     char *input_file;
> > > > +     char *capsule_file;
> > > > +     unsigned long oemflags;
> > > > +     enum capsule_type capsule;
> > > > +};
> > > > +
> > > >  struct efi_capsule_header {
> > > >       efi_guid_t capsule_guid;
> > > >       uint32_t header_size;
> > > > @@ -113,4 +145,82 @@ struct efi_firmware_image_authentication {
> > > >       struct win_certificate_uefi_guid auth_info;
> > > >  } __packed;
> > > >
> > > > +/**
> > > > + * capsule_with_cfg_file() - Generate capsule from config file
> > > > + * @cfg_file: Path to the config file
> > > > + *
> > > > + * Parse the capsule parameters from the config file and use the
> > > > + * parameters for generating one or more capsules.
> > > > + *
> > > > + * Return: None
> > > > + *
> > > > + */
> > > > +void capsule_with_cfg_file(const char *cfg_file);
> > > > +
> > > > +/**
> > > > + * convert_uuid_to_guid() - convert UUID to GUID
> > > > + * @buf:     UUID binary
> > > > + *
> > > > + * UUID and GUID have the same data structure, but their binary
> > > > + * formats are different due to the endianness. See lib/uuid.c.
> > > > + * Since uuid_parse() can handle only UUID, this function must
> > > > + * be called to get correct data for GUID when parsing a string.
> > > > + *
> > > > + * The correct data will be returned in @buf.
> > > > + */
> > > > +void convert_uuid_to_guid(unsigned char *buf);
> > > > +
> > > > +/**
> > > > + * create_empty_capsule() - Generate an empty capsule
> > > > + * @path: Path to the empty capsule file to be generated
> > > > + * @guid: Guid value of the image for which empty capsule is generated
> > > > + * @fw_accept: Flag to specify whether to generate accept or revert capsule
> > > > + *
> > > > + * Generate an empty capsule, either an accept or a revert capsule to be
> > > > + * used to flag acceptance or rejection of an earlier executed firmware
> > > > + * update operation. Being used in the FWU Multi Bank firmware update
> > > > + * feature.
> > > > + *
> > > > + * Return: 0 if OK, -ve on error
> > > > + *
> > > > + */
> > > > +int create_empty_capsule(char *path, efi_guid_t *guid, bool fw_accept);
> > > > +
> > > > +/**
> > > > + * create_fwbin - create an uefi capsule file
> > > > + * @path:    Path to a created capsule file
> > > > + * @bin:     Path to a firmware binary to encapsulate
> > > > + * @guid:    GUID of related FMP driver
> > > > + * @index:   Index number in capsule
> > > > + * @instance:        Instance number in capsule
> > > > + * @mcount:  Monotonic count in authentication information
> > > > + * @private_file:    Path to a private key file
> > > > + * @cert_file:       Path to a certificate file
> > > > + * @oemflags:  Capsule OEM Flags, bits 0-15
> > > > + *
> > > > + * This function actually does the job of creating an uefi capsule file.
> > > > + * All the arguments must be supplied.
> > > > + * If either @private_file ror @cert_file is NULL, the capsule file
> > > > + * won't be signed.
> > > > + *
> > > > + * Return:
> > > > + * * 0  - on success
> > > > + * * -1 - on failure
> > > > + */
> > > > +int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > +              unsigned long index, unsigned long instance,
> > > > +              uint64_t mcount, char *privkey_file, char *cert_file,
> > > > +              uint16_t oemflags);
> > > > +
> > > > +/**
> > > > + * print_usage() - Print the command usage string
> > > > + *
> > > > + * Prints the standard command usage string. Called in the case
> > > > + * of incorrect parameters being passed to the tool.
> > > > + *
> > > > + * Return: None
> > > > + *
> > > > + */
> > > > +void print_usage(void);
> > > > +
> > > >  #endif /* _EFI_CAPSULE_H */
> > > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> > > > index b71537beee..711adf0439 100644
> > > > --- a/tools/mkeficapsule.c
> > > > +++ b/tools/mkeficapsule.c
> > > > @@ -31,12 +31,6 @@ 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 struct option options[] = {
> > > >       {"guid", required_argument, NULL, 'g'},
> > > >       {"index", required_argument, NULL, 'i'},
> > > > @@ -52,7 +46,16 @@ static struct option options[] = {
> > > >       {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"
> > > > @@ -400,10 +403,10 @@ static void free_sig_data(struct auth_context *ctx)
> > > >   * * 0  - on success
> > > >   * * -1 - on failure
> > > >   */
> > > > -static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > -                     unsigned long index, unsigned long instance,
> > > > -                     uint64_t mcount, char *privkey_file, char *cert_file,
> > > > -                     uint16_t oemflags)
> > > > +int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > +              unsigned long index, unsigned long instance,
> > > > +              uint64_t mcount, char *privkey_file, char *cert_file,
> > > > +              uint16_t oemflags)
> > > >  {
> > > >       struct efi_capsule_header header;
> > > >       struct efi_firmware_management_capsule_header capsule;
> > > > @@ -580,7 +583,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;
> > > > @@ -623,19 +640,7 @@ err:
> > > >       return ret;
> > > >  }
> > > >
> > > > -/**
> > > > - * main - main entry function of mkeficapsule
> > > > - * @argc:    Number of arguments
> > > > - * @argv:    Array of pointers to arguments
> > > > - *
> > > > - * Create an uefi capsule file, optionally signing it.
> > > > - * Parse all the arguments and pass them on to create_fwbin().
> > > > - *
> > > > - * Return:
> > > > - * * 0  - on success
> > > > - * * -1 - on failure
> > > > - */
> > > > -int main(int argc, char **argv)
> > > > +static void capsule_with_cmdline_params(int argc, char **argv)
> > > >  {
> > > >       efi_guid_t *guid;
> > > >       unsigned char uuid_buf[16];
> > > > @@ -643,6 +648,7 @@ int main(int argc, char **argv)
> > > >       uint64_t mcount;
> > > >       unsigned long oemflags;
> > > >       char *privkey_file, *cert_file;
> > > > +     enum capsule_type capsule;
> > > >       int c, idx;
> > > >
> > > >       guid = NULL;
> > > > @@ -652,7 +658,7 @@ int main(int argc, char **argv)
> > > >       privkey_file = NULL;
> > > >       cert_file = NULL;
> > > >       dump_sig = 0;
> > > > -     capsule_type = CAPSULE_NORMAL_BLOB;
> > > > +     capsule = CAPSULE_NORMAL_BLOB;
> > > >       oemflags = 0;
> > > >       for (;;) {
> > > >               c = getopt_long(argc, argv, opts_short, options, &idx);
> > > > @@ -702,20 +708,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);
> > > > @@ -732,21 +738,21 @@ int main(int argc, char **argv)
> > > >       }
> > > >
> > > >       /* check necessary parameters */
> > > > -     if ((capsule_type == CAPSULE_NORMAL_BLOB &&
> > > > -         ((argc != optind + 2) || !guid ||
> > > > -          ((privkey_file && !cert_file) ||
> > > > -           (!privkey_file && cert_file)))) ||
> > > > -         (capsule_type != CAPSULE_NORMAL_BLOB &&
> > > > -         ((argc != optind + 1) ||
> > > > -          ((capsule_type == CAPSULE_ACCEPT) && !guid) ||
> > > > -          ((capsule_type == CAPSULE_REVERT) && guid)))) {
> > > > +     if ((capsule == CAPSULE_NORMAL_BLOB &&
> > > > +          ((argc != optind + 2) || !guid ||
> > > > +           ((privkey_file && !cert_file) ||
> > > > +            (!privkey_file && cert_file)))) ||
> > > > +         (capsule != CAPSULE_NORMAL_BLOB &&
> > > > +          ((argc != optind + 1) ||
> > > > +           (capsule == CAPSULE_ACCEPT && !guid) ||
> > > > +           (capsule == CAPSULE_REVERT && guid)))) {
> > > >               print_usage();
> > > >               exit(EXIT_FAILURE);
> > > >       }
> > > >
> > > > -     if (capsule_type != CAPSULE_NORMAL_BLOB) {
> > > > +     if (capsule != CAPSULE_NORMAL_BLOB) {
> > > >               if (create_empty_capsule(argv[argc - 1], guid,
> > > > -                                      capsule_type == CAPSULE_ACCEPT) < 0) {
> > > > +                                      capsule == CAPSULE_ACCEPT) < 0) {
> > > >                       fprintf(stderr, "Creating empty capsule failed\n");
> > > >                       exit(EXIT_FAILURE);
> > > >               }
> > > > @@ -756,6 +762,26 @@ int main(int argc, char **argv)
> > > >               fprintf(stderr, "Creating firmware capsule failed\n");
> > > >               exit(EXIT_FAILURE);
> > > >       }
> > > > +}
> > > > +
> > > > +/**
> > > > + * main - main entry function of mkeficapsule
> > > > + * @argc:    Number of arguments
> > > > + * @argv:    Array of pointers to arguments
> > > > + *
> > > > + * Create an uefi capsule file, optionally signing it.
> > > > + * Parse all the arguments and pass them on to create_fwbin().
> > > > + *
> > > > + * Return:
> > > > + * * 0  - on success
> > > > + * * -1 - on failure
> > > > + */
> > > > +int main(int argc, char **argv)
> > > > +{
> > > > +     if (!strcmp(CONFIG_EFI_CAPSULE_CFG_FILE, ""))
> > > > +             capsule_with_cmdline_params(argc, argv);
> > > > +     else
> > > > +             capsule_with_cfg_file(CONFIG_EFI_CAPSULE_CFG_FILE);
> > >
> > > I don't know where the macro, CONFIG_EFI_CAPSULE_CFG_FILE, comes from.
> > > Anyhow, as a general rule, any host tool must be as generic as it should not
> > > depend on a target's config.
> > > (I was told so before.)
> > >
> > > So I would suggest that you add another command line, say "--config-file <file>",
> > > to make the command generic.
> >
> > Yes, that would be something followed by most of the tools. The reason
> > I did not add a command-line option for the confile file is because I
> > want the capsule generation added as a make target. With the path to
> > the config file specified through the Kconfig symbol, we can invoke
> > 'make capsule', and it would build the capsules by parsing the
> > parameters from the config file, taken from the Kconfig symbol. I know
> > there are ways of specifying options when using a make command, but I
> > don't think that is a clean way of doing things.
>
> Not sure, but in your [5/7],
> cmd_mkeficapsule = $(objtree)/tools/mkeficapsule --config-file $(CONFIG_EFI_CAPSULE_CFG_FILE)
>
> Doesn't this change work?

So, I tried the above suggested change. But trying to run a make
'target' does not work without the .config file being present. FWIW,
the same is the case for building tools as well. I think that is the
reason for the tools-only_defconfig.

-sughosh

>
> -Takahiro Akashi
>
>
> > Given the use case of
> > a make target, I hope we can use the Kconfig symbol for specifying the
> > config file path.
> >
> > -sughosh
> >
> > >
> > > -Takahiro Akashi
> > >
> > >
> > > >
> > > >       exit(EXIT_SUCCESS);
> > > >  }
> > > > diff --git a/tools/mkeficapsule_parse.c b/tools/mkeficapsule_parse.c
> > > > new file mode 100644
> > > > index 0000000000..ef4f3f6705
> > > > --- /dev/null
> > > > +++ b/tools/mkeficapsule_parse.c
> > > > @@ -0,0 +1,345 @@
> > > > +// SPDX-License-Identifier: GPL-2.0
> > > > +/*
> > > > + * Copyright 2023 Linaro Limited
> > > > + */
> > > > +
> > > > +/*
> > > > + * The code in this file adds parsing ability to the mkeficapsule
> > > > + * tool. This allows specifying parameters needed to build the capsule
> > > > + * through the config file instead of specifying them on the command-line.
> > > > + * Parameters can be specified for more than one payload, generating the
> > > > + * corresponding capsule files.
> > > > + *
> > > > + * The parameters are specified in a "key:value" pair. All the parameters
> > > > + * that are currently supported by the mkeficapsule tool can be specified
> > > > + * in the config file.
> > > > + *
> > > > + * The example below shows four payloads. The first payload is an example
> > > > + * of generating a signed capsule. The second payload is an example of
> > > > + * generating an unsigned capsule. The third payload is an accept empty
> > > > + * capsule, while the fourth payload is the revert empty capsule, used
> > > > + * for the multi-bank firmware update feature.
> > > > + *
> > > > + * This functionality can be easily extended to generate a single capsule
> > > > + * comprising multiple payloads.
> > > > +
> > > > +     {
> > > > +         image-guid: 02f4d760-cfd5-43bd-8e2d-a42acb33c660
> > > > +         hardware-instance: 0
> > > > +         monotonic-count: 1
> > > > +         payload: u-boot.bin
> > > > +         image-index: 1
> > > > +         private-key: /path/to/priv/key
> > > > +         pub-key-cert: /path/to/pub/key
> > > > +         capsule: u-boot.capsule
> > > > +     }
> > > > +     {
> > > > +         image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e
> > > > +         hardware-instance: 0
> > > > +         payload: u-boot.itb
> > > > +         image-index: 2
> > > > +         oemflags: 0x8000
> > > > +         capsule: fit.capsule
> > > > +     }
> > > > +     {
> > > > +         capsule-type: accept
> > > > +         image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e
> > > > +         capsule: accept.capsule
> > > > +     }
> > > > +     {
> > > > +         capsule-type: revert
> > > > +         capsule: revert.capsule
> > > > +     }
> > > > +*/
> > > > +
> > > > +#include <ctype.h>
> > > > +#include <limits.h>
> > > > +#include <stdio.h>
> > > > +#include <stdlib.h>
> > > > +#include <string.h>
> > > > +
> > > > +#include <uuid/uuid.h>
> > > > +
> > > > +#include "eficapsule.h"
> > > > +
> > > > +#define PARAMS_START "{"
> > > > +#define PARAMS_END   "}"
> > > > +
> > > > +#define PSTART               2
> > > > +#define PEND         3
> > > > +
> > > > +#define MALLOC_FAIL_STR              "Unable to allocate memory\n"
> > > > +
> > > > +#define ARRAY_SIZE(x)                (sizeof(x) / sizeof((x)[0]))
> > > > +
> > > > +const char *capsule_params[] = {
> > > > +     "image-guid", "image-index", "private-key",
> > > > +     "pub-key-cert", "payload", "capsule",
> > > > +     "hardware-instance", "monotonic-count",
> > > > +     "capsule-type", "oemflags" };
> > > > +
> > > > +static unsigned char params_start;
> > > > +static unsigned char params_end;
> > > > +
> > > > +static void print_and_exit(const char *str)
> > > > +{
> > > > +     fprintf(stderr, "%s", str);
> > > > +     exit(EXIT_FAILURE);
> > > > +}
> > > > +
> > > > +static int param_delim_checks(char *line, unsigned char *token)
> > > > +{
> > > > +     if (!strcmp(line, PARAMS_START)) {
> > > > +             if (params_start || !params_end) {
> > > > +                     fprintf(stderr, "Earlier params processing still in progress. ");
> > > > +                     fprintf(stderr, "Can't start processing a new params.\n");
> > > > +                     exit(EXIT_FAILURE);
> > > > +             } else {
> > > > +                     params_start = 1;
> > > > +                     params_end = 0;
> > > > +                     *token = PSTART;
> > > > +                     return 1;
> > > > +             }
> > > > +     } else if (!strcmp(line, PARAMS_END)) {
> > > > +             if (!params_start) {
> > > > +                     fprintf(stderr, "Cannot put end braces without start braces. ");
> > > > +                     fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > +                     exit(EXIT_FAILURE);
> > > > +             } else {
> > > > +                     params_start = 0;
> > > > +                     params_end = 1;
> > > > +                     *token = PEND;
> > > > +                     return 1;
> > > > +             }
> > > > +     } else if (!params_start) {
> > > > +             fprintf(stderr, "Params should be passed within braces. ");
> > > > +             fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > +             exit(EXIT_FAILURE);
> > > > +     }
> > > > +
> > > > +     return 0;
> > > > +}
> > > > +
> > > > +static void add_guid(efi_guid_t **guid_param, char *guid)
> > > > +{
> > > > +     unsigned char uuid_buf[16];
> > > > +
> > > > +     *guid_param = malloc(sizeof(efi_guid_t));
> > > > +     if (!*guid_param)
> > > > +             print_and_exit(MALLOC_FAIL_STR);
> > > > +
> > > > +     if (uuid_parse(guid, uuid_buf))
> > > > +             print_and_exit("Wrong guid format\n");
> > > > +
> > > > +     convert_uuid_to_guid(uuid_buf);
> > > > +     memcpy(*guid_param, uuid_buf, sizeof(efi_guid_t));
> > > > +}
> > > > +
> > > > +static void add_string(char **dst, char *val)
> > > > +{
> > > > +     *dst = strdup(val);
> > > > +     if (!*dst)
> > > > +             print_and_exit(MALLOC_FAIL_STR);
> > > > +}
> > > > +
> > > > +static void match_and_populate_param(char *key, char *val,
> > > > +                                  struct efi_capsule_params *param)
> > > > +{
> > > > +     int i;
> > > > +
> > > > +     for (i = 0; i < ARRAY_SIZE(capsule_params); i++) {
> > > > +             if (!strcmp(key, capsule_params[i])) {
> > > > +                     switch (i) {
> > > > +                     case 0:
> > > > +                             add_guid(&param->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(&param->privkey_file, val);
> > > > +                             return;
> > > > +                     case 3:
> > > > +                             add_string(&param->cert_file, val);
> > > > +                             return;
> > > > +                     case 4:
> > > > +                             add_string(&param->input_file, val);
> > > > +                             return;
> > > > +                     case 5:
> > > > +                             add_string(&param->capsule_file, val);
> > > > +                             return;
> > > > +                     case 6:
> > > > +                             param->hardware_instance = strtoul(val, NULL, 0);
> > > > +                             if (param->hardware_instance == ULONG_MAX)
> > > > +                                     print_and_exit("Enter a valid hardware instance value");
> > > > +                             return;
> > > > +                     case 7:
> > > > +                             param->monotonic_count = strtoull(val, NULL, 0);
> > > > +                             if (param->monotonic_count == ULLONG_MAX)
> > > > +                                     print_and_exit("Enter a valid monotonic count value");
> > > > +                             return;
> > > > +                     case 8:
> > > > +                             if (!strcmp(val, "normal"))
> > > > +                                     param->capsule = CAPSULE_NORMAL_BLOB;
> > > > +                             else if (!strcmp(val, "accept"))
> > > > +                                     param->capsule = CAPSULE_ACCEPT;
> > > > +                             else if (!strcmp(val, "revert"))
> > > > +                                     param->capsule = CAPSULE_REVERT;
> > > > +                             else
> > > > +                                     print_and_exit("Invalid type of capsule");
> > > > +
> > > > +                             return;
> > > > +                     case 9:
> > > > +                             param->oemflags = strtoul(val, NULL, 0);
> > > > +                             if (param->oemflags > 0xffff)
> > > > +                                     print_and_exit("OemFlags must be between 0x0 and 0xffff\n");
> > > > +                             return;
> > > > +                     }
> > > > +             }
> > > > +     }
> > > > +
> > > > +     fprintf(stderr, "Undefined param %s specified. ", key);
> > > > +     fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > +     exit(EXIT_FAILURE);
> > > > +}
> > > > +
> > > > +static int get_capsule_params(char *line, struct efi_capsule_params *params)
> > > > +{
> > > > +     char *key = NULL;
> > > > +     char *val = NULL;
> > > > +     unsigned char token;
> > > > +
> > > > +     if (param_delim_checks(line, &token))
> > > > +             return token;
> > > > +
> > > > +     key = strtok(line, ":");
> > > > +     if (key)
> > > > +             val = strtok(NULL, "\0");
> > > > +     else
> > > > +             print_and_exit("Expect the params in a key:value pair\n");
> > > > +
> > > > +     match_and_populate_param(key, val, params);
> > > > +
> > > > +     return 0;
> > > > +}
> > > > +
> > > > +static char *skip_whitespace(char *line)
> > > > +{
> > > > +     char *ptr, *newline;
> > > > +
> > > > +     ptr = malloc(strlen(line) + 1);
> > > > +     if (!ptr)
> > > > +             print_and_exit(MALLOC_FAIL_STR);
> > > > +
> > > > +     for (newline = ptr; *line; line++)
> > > > +             if (!isblank(*line))
> > > > +                     *ptr++ = *line;
> > > > +     *ptr = '\0';
> > > > +     return newline;
> > > > +}
> > > > +
> > > > +static int parse_capsule_payload_params(FILE *fp, struct efi_capsule_params *params)
> > > > +{
> > > > +     char *line = NULL;
> > > > +     char *newline;
> > > > +     size_t n = 0;
> > > > +     ssize_t len;
> > > > +
> > > > +     while ((len = getline(&line, &n, fp)) != -1) {
> > > > +             if (len == 1 && line[len - 1] == '\n')
> > > > +                     continue;
> > > > +
> > > > +             line[len - 1] = '\0';
> > > > +
> > > > +             newline = skip_whitespace(line);
> > > > +
> > > > +             if (newline[0] == '#')
> > > > +                     continue;
> > > > +
> > > > +             if (get_capsule_params(newline, params) == PEND)
> > > > +                     return 0;
> > > > +     }
> > > > +
> > > > +     if (errno == EINVAL || errno == ENOMEM) {
> > > > +             fprintf(stderr, "getline() returned an error %s reading the line\n",
> > > > +                     strerror(errno));
> > > > +             exit(EXIT_FAILURE);
> > > > +     } else if (params_start == 1 || params_end == 0) {
> > > > +             fprintf(stderr, "Params should be passed within braces. ");
> > > > +             fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > +             exit(EXIT_FAILURE);
> > > > +     } else {
> > > > +             return -1;
> > > > +     }
> > > > +}
> > > > +
> > > > +static void params_dependency_check(struct efi_capsule_params *params)
> > > > +{
> > > > +     /* check necessary parameters */
> > > > +     if ((params->capsule == CAPSULE_NORMAL_BLOB &&
> > > > +          ((!params->input_file || !params->capsule_file ||
> > > > +            !params->image_guid) ||
> > > > +           ((params->privkey_file && !params->cert_file) ||
> > > > +            (!params->privkey_file && params->cert_file)))) ||
> > > > +         (params->capsule != CAPSULE_NORMAL_BLOB &&
> > > > +          (!params->capsule_file ||
> > > > +           (params->capsule == CAPSULE_ACCEPT && !params->image_guid) ||
> > > > +           (params->capsule == CAPSULE_REVERT && params->image_guid)))) {
> > > > +             print_usage();
> > > > +             exit(EXIT_FAILURE);
> > > > +     }
> > > > +}
> > > > +
> > > > +static void generate_capsule(struct efi_capsule_params *params)
> > > > +{
> > > > +     if (params->capsule != CAPSULE_NORMAL_BLOB) {
> > > > +             if (create_empty_capsule(params->capsule_file,
> > > > +                                      params->image_guid,
> > > > +                                      params->capsule ==
> > > > +                                      CAPSULE_ACCEPT) < 0)
> > > > +                     print_and_exit("Creating empty capsule failed\n");
> > > > +     } else if (create_fwbin(params->capsule_file, params->input_file,
> > > > +                           params->image_guid, params->image_index,
> > > > +                           params->hardware_instance,
> > > > +                           params->monotonic_count,
> > > > +                           params->privkey_file,
> > > > +                           params->cert_file,
> > > > +                           (uint16_t)params->oemflags) < 0) {
> > > > +             print_and_exit("Creating firmware capsule failed\n");
> > > > +     }
> > > > +}
> > > > +
> > > > +/**
> > > > + * capsule_with_cfg_file() - Generate capsule from config file
> > > > + * @cfg_file: Path to the config file
> > > > + *
> > > > + * Parse the capsule parameters from the config file and use the
> > > > + * parameters for generating one or more capsules.
> > > > + *
> > > > + * Return: None
> > > > + *
> > > > + */
> > > > +void capsule_with_cfg_file(const char *cfg_file)
> > > > +{
> > > > +     FILE *fp;
> > > > +     struct efi_capsule_params params = { 0 };
> > > > +
> > > > +     fp = fopen(cfg_file, "r");
> > > > +     if (!fp) {
> > > > +             fprintf(stderr, "Unable to open the capsule config file %s\n",
> > > > +                     cfg_file);
> > > > +             exit(EXIT_FAILURE);
> > > > +     }
> > > > +
> > > > +     params_start = 0;
> > > > +     params_end = 1;
> > > > +
> > > > +     while (parse_capsule_payload_params(fp, &params) != -1) {
> > > > +             params_dependency_check(&params);
> > > > +             generate_capsule(&params);
> > > > +
> > > > +             memset(&params, 0, sizeof(struct efi_capsule_params));
> > > > +     }
> > > > +}
> > > > --
> > > > 2.34.1
> > > >

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 4/7] tools: mkeficapsule: Add support for parsing capsule params from config file
  2023-06-15  4:39         ` Sughosh Ganu
@ 2023-06-15  5:49           ` Takahiro Akashi
  2023-06-16  4:26             ` Sughosh Ganu
  0 siblings, 1 reply; 50+ messages in thread
From: Takahiro Akashi @ 2023-06-15  5:49 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Simon Glass, Michal Simek

On Thu, Jun 15, 2023 at 10:09:06AM +0530, Sughosh Ganu wrote:
> On Wed, 14 Jun 2023 at 11:23, Takahiro Akashi
> <takahiro.akashi@linaro.org> wrote:
> >
> > On Wed, Jun 14, 2023 at 10:56:23AM +0530, Sughosh Ganu wrote:
> > > hi Takahiro,
> > >
> > > On Wed, 14 Jun 2023 at 09:09, Takahiro Akashi
> > > <takahiro.akashi@linaro.org> wrote:
> > > >
> > > > Hi Sughosh,
> > > >
> > > > I think this is a good extension to mkeficapsule, but
> > > >
> > > > On Tue, Jun 13, 2023 at 04:08:03PM +0530, Sughosh Ganu wrote:
> > > > > Add support for specifying the parameters needed for capsule
> > > > > generation through a config file, instead of passing them through
> > > > > command-line. Parameters for more than a single capsule file can be
> > > > > specified, resulting in generation of multiple capsules through a
> > > > > single invocation of the command.
> > > > >
> > > > > This path is to be used for generating capsules through a make target,
> > > > > with the parameters being parsed from the config file.
> > > > >
> > > > > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > > > > ---
> > > > >  tools/Kconfig              |   9 +
> > > > >  tools/Makefile             |   1 +
> > > > >  tools/eficapsule.h         | 110 ++++++++++++
> > > > >  tools/mkeficapsule.c       | 106 +++++++-----
> > > > >  tools/mkeficapsule_parse.c | 345 +++++++++++++++++++++++++++++++++++++
> > > > >  5 files changed, 531 insertions(+), 40 deletions(-)
> > > > >  create mode 100644 tools/mkeficapsule_parse.c
> > > > >
> > > > > diff --git a/tools/Kconfig b/tools/Kconfig
> > > > > index 539708f277..95f27b7c45 100644
> > > > > --- a/tools/Kconfig
> > > > > +++ b/tools/Kconfig
> > > > > @@ -98,6 +98,15 @@ config TOOLS_MKEFICAPSULE
> > > > >         optionally sign that file. If you want to enable UEFI capsule
> > > > >         update feature on your target, you certainly need this.
> > > > >
> > > > > +config EFI_CAPSULE_CFG_FILE
> > > > > +     string "Path to the EFI Capsule Config File"
> > > > > +     default ""
> > > > > +     help
> > > > > +       Path to the EFI capsule config file which provides the
> > > > > +       parameters needed to build capsule(s). Parameters can be
> > > > > +       provided for multiple payloads resulting in corresponding
> > > > > +       capsule images being generated.
> > > > > +
> > > > >  menuconfig FSPI_CONF_HEADER
> > > > >       bool "FlexSPI Header Configuration"
> > > > >       help
> > > > > diff --git a/tools/Makefile b/tools/Makefile
> > > > > index d793cf3bec..ef366f3d61 100644
> > > > > --- a/tools/Makefile
> > > > > +++ b/tools/Makefile
> > > > > @@ -250,6 +250,7 @@ HOSTLDLIBS_mkeficapsule += \
> > > > >  HOSTLDLIBS_mkeficapsule += \
> > > > >       $(shell pkg-config --libs uuid 2> /dev/null || echo "-luuid")
> > > > >  hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule
> > > > > +mkeficapsule-objs := mkeficapsule.o mkeficapsule_parse.o
> > > > >
> > > > >  # We build some files with extra pedantic flags to try to minimize things
> > > > >  # that won't build on some weird host compiler -- though there are lots of
> > > > > diff --git a/tools/eficapsule.h b/tools/eficapsule.h
> > > > > index 072a4b5598..42e66c6d6a 100644
> > > > > --- a/tools/eficapsule.h
> > > > > +++ b/tools/eficapsule.h
> > > > > @@ -52,6 +52,38 @@ typedef struct {
> > > > >  /* flags */
> > > > >  #define CAPSULE_FLAGS_PERSIST_ACROSS_RESET      0x00010000
> > > > >
> > > > > +enum capsule_type {
> > > > > +     CAPSULE_NORMAL_BLOB = 0,
> > > > > +     CAPSULE_ACCEPT,
> > > > > +     CAPSULE_REVERT,
> > > > > +};
> > > > > +
> > > > > +/**
> > > > > + * struct efi_capsule_params - Capsule parameters
> > > > > + * @image_guid: Guid value of the payload input image
> > > > > + * @image_index: Image index value
> > > > > + * @hardware_instance: Hardware instance to be used for the image
> > > > > + * @monotonic_count: Monotonic count value to be used for signed capsule
> > > > > + * @privkey_file: Path to private key used in capsule signing
> > > > > + * @cert_file: Path to public key certificate used in capsule signing
> > > > > + * @input_file: Path to payload input image
> > > > > + * @capsule_file: Path to the output capsule file
> > > > > + * @oemflags: Oemflags to be populated in the capsule header
> > > > > + * @capsule: Capsule Type, normal or accept or revert
> > > > > + */
> > > > > +struct efi_capsule_params {
> > > > > +     efi_guid_t *image_guid;
> > > > > +     unsigned long image_index;
> > > > > +     unsigned long hardware_instance;
> > > > > +     uint64_t monotonic_count;
> > > > > +     char *privkey_file;
> > > > > +     char *cert_file;
> > > > > +     char *input_file;
> > > > > +     char *capsule_file;
> > > > > +     unsigned long oemflags;
> > > > > +     enum capsule_type capsule;
> > > > > +};
> > > > > +
> > > > >  struct efi_capsule_header {
> > > > >       efi_guid_t capsule_guid;
> > > > >       uint32_t header_size;
> > > > > @@ -113,4 +145,82 @@ struct efi_firmware_image_authentication {
> > > > >       struct win_certificate_uefi_guid auth_info;
> > > > >  } __packed;
> > > > >
> > > > > +/**
> > > > > + * capsule_with_cfg_file() - Generate capsule from config file
> > > > > + * @cfg_file: Path to the config file
> > > > > + *
> > > > > + * Parse the capsule parameters from the config file and use the
> > > > > + * parameters for generating one or more capsules.
> > > > > + *
> > > > > + * Return: None
> > > > > + *
> > > > > + */
> > > > > +void capsule_with_cfg_file(const char *cfg_file);
> > > > > +
> > > > > +/**
> > > > > + * convert_uuid_to_guid() - convert UUID to GUID
> > > > > + * @buf:     UUID binary
> > > > > + *
> > > > > + * UUID and GUID have the same data structure, but their binary
> > > > > + * formats are different due to the endianness. See lib/uuid.c.
> > > > > + * Since uuid_parse() can handle only UUID, this function must
> > > > > + * be called to get correct data for GUID when parsing a string.
> > > > > + *
> > > > > + * The correct data will be returned in @buf.
> > > > > + */
> > > > > +void convert_uuid_to_guid(unsigned char *buf);
> > > > > +
> > > > > +/**
> > > > > + * create_empty_capsule() - Generate an empty capsule
> > > > > + * @path: Path to the empty capsule file to be generated
> > > > > + * @guid: Guid value of the image for which empty capsule is generated
> > > > > + * @fw_accept: Flag to specify whether to generate accept or revert capsule
> > > > > + *
> > > > > + * Generate an empty capsule, either an accept or a revert capsule to be
> > > > > + * used to flag acceptance or rejection of an earlier executed firmware
> > > > > + * update operation. Being used in the FWU Multi Bank firmware update
> > > > > + * feature.
> > > > > + *
> > > > > + * Return: 0 if OK, -ve on error
> > > > > + *
> > > > > + */
> > > > > +int create_empty_capsule(char *path, efi_guid_t *guid, bool fw_accept);
> > > > > +
> > > > > +/**
> > > > > + * create_fwbin - create an uefi capsule file
> > > > > + * @path:    Path to a created capsule file
> > > > > + * @bin:     Path to a firmware binary to encapsulate
> > > > > + * @guid:    GUID of related FMP driver
> > > > > + * @index:   Index number in capsule
> > > > > + * @instance:        Instance number in capsule
> > > > > + * @mcount:  Monotonic count in authentication information
> > > > > + * @private_file:    Path to a private key file
> > > > > + * @cert_file:       Path to a certificate file
> > > > > + * @oemflags:  Capsule OEM Flags, bits 0-15
> > > > > + *
> > > > > + * This function actually does the job of creating an uefi capsule file.
> > > > > + * All the arguments must be supplied.
> > > > > + * If either @private_file ror @cert_file is NULL, the capsule file
> > > > > + * won't be signed.
> > > > > + *
> > > > > + * Return:
> > > > > + * * 0  - on success
> > > > > + * * -1 - on failure
> > > > > + */
> > > > > +int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > > +              unsigned long index, unsigned long instance,
> > > > > +              uint64_t mcount, char *privkey_file, char *cert_file,
> > > > > +              uint16_t oemflags);
> > > > > +
> > > > > +/**
> > > > > + * print_usage() - Print the command usage string
> > > > > + *
> > > > > + * Prints the standard command usage string. Called in the case
> > > > > + * of incorrect parameters being passed to the tool.
> > > > > + *
> > > > > + * Return: None
> > > > > + *
> > > > > + */
> > > > > +void print_usage(void);
> > > > > +
> > > > >  #endif /* _EFI_CAPSULE_H */
> > > > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> > > > > index b71537beee..711adf0439 100644
> > > > > --- a/tools/mkeficapsule.c
> > > > > +++ b/tools/mkeficapsule.c
> > > > > @@ -31,12 +31,6 @@ 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 struct option options[] = {
> > > > >       {"guid", required_argument, NULL, 'g'},
> > > > >       {"index", required_argument, NULL, 'i'},
> > > > > @@ -52,7 +46,16 @@ static struct option options[] = {
> > > > >       {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"
> > > > > @@ -400,10 +403,10 @@ static void free_sig_data(struct auth_context *ctx)
> > > > >   * * 0  - on success
> > > > >   * * -1 - on failure
> > > > >   */
> > > > > -static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > > -                     unsigned long index, unsigned long instance,
> > > > > -                     uint64_t mcount, char *privkey_file, char *cert_file,
> > > > > -                     uint16_t oemflags)
> > > > > +int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > > +              unsigned long index, unsigned long instance,
> > > > > +              uint64_t mcount, char *privkey_file, char *cert_file,
> > > > > +              uint16_t oemflags)
> > > > >  {
> > > > >       struct efi_capsule_header header;
> > > > >       struct efi_firmware_management_capsule_header capsule;
> > > > > @@ -580,7 +583,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;
> > > > > @@ -623,19 +640,7 @@ err:
> > > > >       return ret;
> > > > >  }
> > > > >
> > > > > -/**
> > > > > - * main - main entry function of mkeficapsule
> > > > > - * @argc:    Number of arguments
> > > > > - * @argv:    Array of pointers to arguments
> > > > > - *
> > > > > - * Create an uefi capsule file, optionally signing it.
> > > > > - * Parse all the arguments and pass them on to create_fwbin().
> > > > > - *
> > > > > - * Return:
> > > > > - * * 0  - on success
> > > > > - * * -1 - on failure
> > > > > - */
> > > > > -int main(int argc, char **argv)
> > > > > +static void capsule_with_cmdline_params(int argc, char **argv)
> > > > >  {
> > > > >       efi_guid_t *guid;
> > > > >       unsigned char uuid_buf[16];
> > > > > @@ -643,6 +648,7 @@ int main(int argc, char **argv)
> > > > >       uint64_t mcount;
> > > > >       unsigned long oemflags;
> > > > >       char *privkey_file, *cert_file;
> > > > > +     enum capsule_type capsule;
> > > > >       int c, idx;
> > > > >
> > > > >       guid = NULL;
> > > > > @@ -652,7 +658,7 @@ int main(int argc, char **argv)
> > > > >       privkey_file = NULL;
> > > > >       cert_file = NULL;
> > > > >       dump_sig = 0;
> > > > > -     capsule_type = CAPSULE_NORMAL_BLOB;
> > > > > +     capsule = CAPSULE_NORMAL_BLOB;
> > > > >       oemflags = 0;
> > > > >       for (;;) {
> > > > >               c = getopt_long(argc, argv, opts_short, options, &idx);
> > > > > @@ -702,20 +708,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);
> > > > > @@ -732,21 +738,21 @@ int main(int argc, char **argv)
> > > > >       }
> > > > >
> > > > >       /* check necessary parameters */
> > > > > -     if ((capsule_type == CAPSULE_NORMAL_BLOB &&
> > > > > -         ((argc != optind + 2) || !guid ||
> > > > > -          ((privkey_file && !cert_file) ||
> > > > > -           (!privkey_file && cert_file)))) ||
> > > > > -         (capsule_type != CAPSULE_NORMAL_BLOB &&
> > > > > -         ((argc != optind + 1) ||
> > > > > -          ((capsule_type == CAPSULE_ACCEPT) && !guid) ||
> > > > > -          ((capsule_type == CAPSULE_REVERT) && guid)))) {
> > > > > +     if ((capsule == CAPSULE_NORMAL_BLOB &&
> > > > > +          ((argc != optind + 2) || !guid ||
> > > > > +           ((privkey_file && !cert_file) ||
> > > > > +            (!privkey_file && cert_file)))) ||
> > > > > +         (capsule != CAPSULE_NORMAL_BLOB &&
> > > > > +          ((argc != optind + 1) ||
> > > > > +           (capsule == CAPSULE_ACCEPT && !guid) ||
> > > > > +           (capsule == CAPSULE_REVERT && guid)))) {
> > > > >               print_usage();
> > > > >               exit(EXIT_FAILURE);
> > > > >       }
> > > > >
> > > > > -     if (capsule_type != CAPSULE_NORMAL_BLOB) {
> > > > > +     if (capsule != CAPSULE_NORMAL_BLOB) {
> > > > >               if (create_empty_capsule(argv[argc - 1], guid,
> > > > > -                                      capsule_type == CAPSULE_ACCEPT) < 0) {
> > > > > +                                      capsule == CAPSULE_ACCEPT) < 0) {
> > > > >                       fprintf(stderr, "Creating empty capsule failed\n");
> > > > >                       exit(EXIT_FAILURE);
> > > > >               }
> > > > > @@ -756,6 +762,26 @@ int main(int argc, char **argv)
> > > > >               fprintf(stderr, "Creating firmware capsule failed\n");
> > > > >               exit(EXIT_FAILURE);
> > > > >       }
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * main - main entry function of mkeficapsule
> > > > > + * @argc:    Number of arguments
> > > > > + * @argv:    Array of pointers to arguments
> > > > > + *
> > > > > + * Create an uefi capsule file, optionally signing it.
> > > > > + * Parse all the arguments and pass them on to create_fwbin().
> > > > > + *
> > > > > + * Return:
> > > > > + * * 0  - on success
> > > > > + * * -1 - on failure
> > > > > + */
> > > > > +int main(int argc, char **argv)
> > > > > +{
> > > > > +     if (!strcmp(CONFIG_EFI_CAPSULE_CFG_FILE, ""))
> > > > > +             capsule_with_cmdline_params(argc, argv);
> > > > > +     else
> > > > > +             capsule_with_cfg_file(CONFIG_EFI_CAPSULE_CFG_FILE);
> > > >
> > > > I don't know where the macro, CONFIG_EFI_CAPSULE_CFG_FILE, comes from.
> > > > Anyhow, as a general rule, any host tool must be as generic as it should not
> > > > depend on a target's config.
> > > > (I was told so before.)
> > > >
> > > > So I would suggest that you add another command line, say "--config-file <file>",
> > > > to make the command generic.
> > >
> > > Yes, that would be something followed by most of the tools. The reason
> > > I did not add a command-line option for the confile file is because I
> > > want the capsule generation added as a make target. With the path to
> > > the config file specified through the Kconfig symbol, we can invoke
> > > 'make capsule', and it would build the capsules by parsing the
> > > parameters from the config file, taken from the Kconfig symbol. I know
> > > there are ways of specifying options when using a make command, but I
> > > don't think that is a clean way of doing things.
> >
> > Not sure, but in your [5/7],
> > cmd_mkeficapsule = $(objtree)/tools/mkeficapsule --config-file $(CONFIG_EFI_CAPSULE_CFG_FILE)
> >
> > Doesn't this change work?
> 
> So, I tried the above suggested change. But trying to run a make
> 'target' does not work without the .config file being present.

Not sure what you meant to say here.
Why don't you have .config when building U-Boot (or rather 'target')?

-Takahiro Akashi

> FWIW,
> the same is the case for building tools as well. I think that is the
> reason for the tools-only_defconfig.
> 
> -sughosh
> 
> >
> > -Takahiro Akashi
> >
> >
> > > Given the use case of
> > > a make target, I hope we can use the Kconfig symbol for specifying the
> > > config file path.
> > >
> > > -sughosh
> > >
> > > >
> > > > -Takahiro Akashi
> > > >
> > > >
> > > > >
> > > > >       exit(EXIT_SUCCESS);
> > > > >  }
> > > > > diff --git a/tools/mkeficapsule_parse.c b/tools/mkeficapsule_parse.c
> > > > > new file mode 100644
> > > > > index 0000000000..ef4f3f6705
> > > > > --- /dev/null
> > > > > +++ b/tools/mkeficapsule_parse.c
> > > > > @@ -0,0 +1,345 @@
> > > > > +// SPDX-License-Identifier: GPL-2.0
> > > > > +/*
> > > > > + * Copyright 2023 Linaro Limited
> > > > > + */
> > > > > +
> > > > > +/*
> > > > > + * The code in this file adds parsing ability to the mkeficapsule
> > > > > + * tool. This allows specifying parameters needed to build the capsule
> > > > > + * through the config file instead of specifying them on the command-line.
> > > > > + * Parameters can be specified for more than one payload, generating the
> > > > > + * corresponding capsule files.
> > > > > + *
> > > > > + * The parameters are specified in a "key:value" pair. All the parameters
> > > > > + * that are currently supported by the mkeficapsule tool can be specified
> > > > > + * in the config file.
> > > > > + *
> > > > > + * The example below shows four payloads. The first payload is an example
> > > > > + * of generating a signed capsule. The second payload is an example of
> > > > > + * generating an unsigned capsule. The third payload is an accept empty
> > > > > + * capsule, while the fourth payload is the revert empty capsule, used
> > > > > + * for the multi-bank firmware update feature.
> > > > > + *
> > > > > + * This functionality can be easily extended to generate a single capsule
> > > > > + * comprising multiple payloads.
> > > > > +
> > > > > +     {
> > > > > +         image-guid: 02f4d760-cfd5-43bd-8e2d-a42acb33c660
> > > > > +         hardware-instance: 0
> > > > > +         monotonic-count: 1
> > > > > +         payload: u-boot.bin
> > > > > +         image-index: 1
> > > > > +         private-key: /path/to/priv/key
> > > > > +         pub-key-cert: /path/to/pub/key
> > > > > +         capsule: u-boot.capsule
> > > > > +     }
> > > > > +     {
> > > > > +         image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e
> > > > > +         hardware-instance: 0
> > > > > +         payload: u-boot.itb
> > > > > +         image-index: 2
> > > > > +         oemflags: 0x8000
> > > > > +         capsule: fit.capsule
> > > > > +     }
> > > > > +     {
> > > > > +         capsule-type: accept
> > > > > +         image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e
> > > > > +         capsule: accept.capsule
> > > > > +     }
> > > > > +     {
> > > > > +         capsule-type: revert
> > > > > +         capsule: revert.capsule
> > > > > +     }
> > > > > +*/
> > > > > +
> > > > > +#include <ctype.h>
> > > > > +#include <limits.h>
> > > > > +#include <stdio.h>
> > > > > +#include <stdlib.h>
> > > > > +#include <string.h>
> > > > > +
> > > > > +#include <uuid/uuid.h>
> > > > > +
> > > > > +#include "eficapsule.h"
> > > > > +
> > > > > +#define PARAMS_START "{"
> > > > > +#define PARAMS_END   "}"
> > > > > +
> > > > > +#define PSTART               2
> > > > > +#define PEND         3
> > > > > +
> > > > > +#define MALLOC_FAIL_STR              "Unable to allocate memory\n"
> > > > > +
> > > > > +#define ARRAY_SIZE(x)                (sizeof(x) / sizeof((x)[0]))
> > > > > +
> > > > > +const char *capsule_params[] = {
> > > > > +     "image-guid", "image-index", "private-key",
> > > > > +     "pub-key-cert", "payload", "capsule",
> > > > > +     "hardware-instance", "monotonic-count",
> > > > > +     "capsule-type", "oemflags" };
> > > > > +
> > > > > +static unsigned char params_start;
> > > > > +static unsigned char params_end;
> > > > > +
> > > > > +static void print_and_exit(const char *str)
> > > > > +{
> > > > > +     fprintf(stderr, "%s", str);
> > > > > +     exit(EXIT_FAILURE);
> > > > > +}
> > > > > +
> > > > > +static int param_delim_checks(char *line, unsigned char *token)
> > > > > +{
> > > > > +     if (!strcmp(line, PARAMS_START)) {
> > > > > +             if (params_start || !params_end) {
> > > > > +                     fprintf(stderr, "Earlier params processing still in progress. ");
> > > > > +                     fprintf(stderr, "Can't start processing a new params.\n");
> > > > > +                     exit(EXIT_FAILURE);
> > > > > +             } else {
> > > > > +                     params_start = 1;
> > > > > +                     params_end = 0;
> > > > > +                     *token = PSTART;
> > > > > +                     return 1;
> > > > > +             }
> > > > > +     } else if (!strcmp(line, PARAMS_END)) {
> > > > > +             if (!params_start) {
> > > > > +                     fprintf(stderr, "Cannot put end braces without start braces. ");
> > > > > +                     fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > > +                     exit(EXIT_FAILURE);
> > > > > +             } else {
> > > > > +                     params_start = 0;
> > > > > +                     params_end = 1;
> > > > > +                     *token = PEND;
> > > > > +                     return 1;
> > > > > +             }
> > > > > +     } else if (!params_start) {
> > > > > +             fprintf(stderr, "Params should be passed within braces. ");
> > > > > +             fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > > +             exit(EXIT_FAILURE);
> > > > > +     }
> > > > > +
> > > > > +     return 0;
> > > > > +}
> > > > > +
> > > > > +static void add_guid(efi_guid_t **guid_param, char *guid)
> > > > > +{
> > > > > +     unsigned char uuid_buf[16];
> > > > > +
> > > > > +     *guid_param = malloc(sizeof(efi_guid_t));
> > > > > +     if (!*guid_param)
> > > > > +             print_and_exit(MALLOC_FAIL_STR);
> > > > > +
> > > > > +     if (uuid_parse(guid, uuid_buf))
> > > > > +             print_and_exit("Wrong guid format\n");
> > > > > +
> > > > > +     convert_uuid_to_guid(uuid_buf);
> > > > > +     memcpy(*guid_param, uuid_buf, sizeof(efi_guid_t));
> > > > > +}
> > > > > +
> > > > > +static void add_string(char **dst, char *val)
> > > > > +{
> > > > > +     *dst = strdup(val);
> > > > > +     if (!*dst)
> > > > > +             print_and_exit(MALLOC_FAIL_STR);
> > > > > +}
> > > > > +
> > > > > +static void match_and_populate_param(char *key, char *val,
> > > > > +                                  struct efi_capsule_params *param)
> > > > > +{
> > > > > +     int i;
> > > > > +
> > > > > +     for (i = 0; i < ARRAY_SIZE(capsule_params); i++) {
> > > > > +             if (!strcmp(key, capsule_params[i])) {
> > > > > +                     switch (i) {
> > > > > +                     case 0:
> > > > > +                             add_guid(&param->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(&param->privkey_file, val);
> > > > > +                             return;
> > > > > +                     case 3:
> > > > > +                             add_string(&param->cert_file, val);
> > > > > +                             return;
> > > > > +                     case 4:
> > > > > +                             add_string(&param->input_file, val);
> > > > > +                             return;
> > > > > +                     case 5:
> > > > > +                             add_string(&param->capsule_file, val);
> > > > > +                             return;
> > > > > +                     case 6:
> > > > > +                             param->hardware_instance = strtoul(val, NULL, 0);
> > > > > +                             if (param->hardware_instance == ULONG_MAX)
> > > > > +                                     print_and_exit("Enter a valid hardware instance value");
> > > > > +                             return;
> > > > > +                     case 7:
> > > > > +                             param->monotonic_count = strtoull(val, NULL, 0);
> > > > > +                             if (param->monotonic_count == ULLONG_MAX)
> > > > > +                                     print_and_exit("Enter a valid monotonic count value");
> > > > > +                             return;
> > > > > +                     case 8:
> > > > > +                             if (!strcmp(val, "normal"))
> > > > > +                                     param->capsule = CAPSULE_NORMAL_BLOB;
> > > > > +                             else if (!strcmp(val, "accept"))
> > > > > +                                     param->capsule = CAPSULE_ACCEPT;
> > > > > +                             else if (!strcmp(val, "revert"))
> > > > > +                                     param->capsule = CAPSULE_REVERT;
> > > > > +                             else
> > > > > +                                     print_and_exit("Invalid type of capsule");
> > > > > +
> > > > > +                             return;
> > > > > +                     case 9:
> > > > > +                             param->oemflags = strtoul(val, NULL, 0);
> > > > > +                             if (param->oemflags > 0xffff)
> > > > > +                                     print_and_exit("OemFlags must be between 0x0 and 0xffff\n");
> > > > > +                             return;
> > > > > +                     }
> > > > > +             }
> > > > > +     }
> > > > > +
> > > > > +     fprintf(stderr, "Undefined param %s specified. ", key);
> > > > > +     fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > > +     exit(EXIT_FAILURE);
> > > > > +}
> > > > > +
> > > > > +static int get_capsule_params(char *line, struct efi_capsule_params *params)
> > > > > +{
> > > > > +     char *key = NULL;
> > > > > +     char *val = NULL;
> > > > > +     unsigned char token;
> > > > > +
> > > > > +     if (param_delim_checks(line, &token))
> > > > > +             return token;
> > > > > +
> > > > > +     key = strtok(line, ":");
> > > > > +     if (key)
> > > > > +             val = strtok(NULL, "\0");
> > > > > +     else
> > > > > +             print_and_exit("Expect the params in a key:value pair\n");
> > > > > +
> > > > > +     match_and_populate_param(key, val, params);
> > > > > +
> > > > > +     return 0;
> > > > > +}
> > > > > +
> > > > > +static char *skip_whitespace(char *line)
> > > > > +{
> > > > > +     char *ptr, *newline;
> > > > > +
> > > > > +     ptr = malloc(strlen(line) + 1);
> > > > > +     if (!ptr)
> > > > > +             print_and_exit(MALLOC_FAIL_STR);
> > > > > +
> > > > > +     for (newline = ptr; *line; line++)
> > > > > +             if (!isblank(*line))
> > > > > +                     *ptr++ = *line;
> > > > > +     *ptr = '\0';
> > > > > +     return newline;
> > > > > +}
> > > > > +
> > > > > +static int parse_capsule_payload_params(FILE *fp, struct efi_capsule_params *params)
> > > > > +{
> > > > > +     char *line = NULL;
> > > > > +     char *newline;
> > > > > +     size_t n = 0;
> > > > > +     ssize_t len;
> > > > > +
> > > > > +     while ((len = getline(&line, &n, fp)) != -1) {
> > > > > +             if (len == 1 && line[len - 1] == '\n')
> > > > > +                     continue;
> > > > > +
> > > > > +             line[len - 1] = '\0';
> > > > > +
> > > > > +             newline = skip_whitespace(line);
> > > > > +
> > > > > +             if (newline[0] == '#')
> > > > > +                     continue;
> > > > > +
> > > > > +             if (get_capsule_params(newline, params) == PEND)
> > > > > +                     return 0;
> > > > > +     }
> > > > > +
> > > > > +     if (errno == EINVAL || errno == ENOMEM) {
> > > > > +             fprintf(stderr, "getline() returned an error %s reading the line\n",
> > > > > +                     strerror(errno));
> > > > > +             exit(EXIT_FAILURE);
> > > > > +     } else if (params_start == 1 || params_end == 0) {
> > > > > +             fprintf(stderr, "Params should be passed within braces. ");
> > > > > +             fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > > +             exit(EXIT_FAILURE);
> > > > > +     } else {
> > > > > +             return -1;
> > > > > +     }
> > > > > +}
> > > > > +
> > > > > +static void params_dependency_check(struct efi_capsule_params *params)
> > > > > +{
> > > > > +     /* check necessary parameters */
> > > > > +     if ((params->capsule == CAPSULE_NORMAL_BLOB &&
> > > > > +          ((!params->input_file || !params->capsule_file ||
> > > > > +            !params->image_guid) ||
> > > > > +           ((params->privkey_file && !params->cert_file) ||
> > > > > +            (!params->privkey_file && params->cert_file)))) ||
> > > > > +         (params->capsule != CAPSULE_NORMAL_BLOB &&
> > > > > +          (!params->capsule_file ||
> > > > > +           (params->capsule == CAPSULE_ACCEPT && !params->image_guid) ||
> > > > > +           (params->capsule == CAPSULE_REVERT && params->image_guid)))) {
> > > > > +             print_usage();
> > > > > +             exit(EXIT_FAILURE);
> > > > > +     }
> > > > > +}
> > > > > +
> > > > > +static void generate_capsule(struct efi_capsule_params *params)
> > > > > +{
> > > > > +     if (params->capsule != CAPSULE_NORMAL_BLOB) {
> > > > > +             if (create_empty_capsule(params->capsule_file,
> > > > > +                                      params->image_guid,
> > > > > +                                      params->capsule ==
> > > > > +                                      CAPSULE_ACCEPT) < 0)
> > > > > +                     print_and_exit("Creating empty capsule failed\n");
> > > > > +     } else if (create_fwbin(params->capsule_file, params->input_file,
> > > > > +                           params->image_guid, params->image_index,
> > > > > +                           params->hardware_instance,
> > > > > +                           params->monotonic_count,
> > > > > +                           params->privkey_file,
> > > > > +                           params->cert_file,
> > > > > +                           (uint16_t)params->oemflags) < 0) {
> > > > > +             print_and_exit("Creating firmware capsule failed\n");
> > > > > +     }
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * capsule_with_cfg_file() - Generate capsule from config file
> > > > > + * @cfg_file: Path to the config file
> > > > > + *
> > > > > + * Parse the capsule parameters from the config file and use the
> > > > > + * parameters for generating one or more capsules.
> > > > > + *
> > > > > + * Return: None
> > > > > + *
> > > > > + */
> > > > > +void capsule_with_cfg_file(const char *cfg_file)
> > > > > +{
> > > > > +     FILE *fp;
> > > > > +     struct efi_capsule_params params = { 0 };
> > > > > +
> > > > > +     fp = fopen(cfg_file, "r");
> > > > > +     if (!fp) {
> > > > > +             fprintf(stderr, "Unable to open the capsule config file %s\n",
> > > > > +                     cfg_file);
> > > > > +             exit(EXIT_FAILURE);
> > > > > +     }
> > > > > +
> > > > > +     params_start = 0;
> > > > > +     params_end = 1;
> > > > > +
> > > > > +     while (parse_capsule_payload_params(fp, &params) != -1) {
> > > > > +             params_dependency_check(&params);
> > > > > +             generate_capsule(&params);
> > > > > +
> > > > > +             memset(&params, 0, sizeof(struct efi_capsule_params));
> > > > > +     }
> > > > > +}
> > > > > --
> > > > > 2.34.1
> > > > >

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 1/7] capsule: authenticate: Embed capsule public key in platform's dtb
  2023-06-13 10:38 ` [PATCH 1/7] capsule: authenticate: Embed capsule public key in platform's dtb Sughosh Ganu
@ 2023-06-15  9:14   ` Simon Glass
  2023-06-15 16:11     ` Sughosh Ganu
  0 siblings, 1 reply; 50+ messages in thread
From: Simon Glass @ 2023-06-15  9:14 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Michal Simek,
	Takahiro Akashi

Hi Sughosh,

On Tue, 13 Jun 2023 at 11:41, 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 script for embedding the ESL used for capsule authentication in
> the platform's dtb, and call this as part of building the dtb(s). This
> brings the embedding of the ESL in the dtb into the u-boot build flow.
>
> 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>
> ---
>  lib/efi_loader/Kconfig       | 11 +++++++++++
>  scripts/Makefile.lib         |  8 ++++++++
>  scripts/embed_capsule_key.sh | 25 +++++++++++++++++++++++++
>  3 files changed, 44 insertions(+)
>  create mode 100755 scripts/embed_capsule_key.sh
>
> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> index c5835e6ef6..1326a1d109 100644
> --- a/lib/efi_loader/Kconfig
> +++ b/lib/efi_loader/Kconfig
> @@ -234,6 +234,17 @@ config EFI_CAPSULE_MAX
>           Select the max capsule index value used for capsule report
>           variables. This value is used to create CapsuleMax variable.
>
> +config EFI_CAPSULE_ESL_FILE
> +       string "Path to the EFI Signature List File"
> +       default ""
> +       depends on EFI_CAPSULE_AUTHENTICATE
> +       help
> +         Provides the absolute path to the EFI Signature List
> +         file which will be embedded in the platform's device
> +         tree and used for capsule authentication at the time
> +         of capsule update.
> +
> +
>  config EFI_DEVICE_PATH_TO_TEXT
>         bool "Device path to text protocol"
>         default y
> diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
> index 7b27224b5d..a4083d0a26 100644
> --- a/scripts/Makefile.lib
> +++ b/scripts/Makefile.lib
> @@ -192,6 +192,8 @@ dtc_cpp_flags  = -Wp,-MD,$(depfile).pre.tmp -nostdinc                    \
>                  -D__ASSEMBLY__                                          \
>                  -undef -D__DTS__
>
> +export dtc_cpp_flags
> +
>  # Finds the multi-part object the current object will be linked into
>  modname-multi = $(sort $(foreach m,$(multi-used),\
>                 $(if $(filter $(subst $(obj)/,,$*.o), $($(m:.o=-objs)) $($(m:.o=-y))),$(m:.o=))))
> @@ -315,6 +317,9 @@ ifeq ($(CONFIG_OF_LIBFDT_OVERLAY),y)
>  DTC_FLAGS += -@
>  endif
>
> +quiet_cmd_embedcapsulekey = EMBEDCAPSULEKEY $@
> +cmd_embedcapsulekey = $(srctree)/scripts/embed_capsule_key.sh $@
> +
>  quiet_cmd_dtc = DTC     $@
>  # Modified for U-Boot
>  # Bring in any U-Boot-specific include at the end of the file
> @@ -333,6 +338,9 @@ cmd_dtc = mkdir -p $(dir ${dtc-tmp}) ; \
>
>  $(obj)/%.dtb: $(src)/%.dts FORCE
>         $(call if_changed_dep,dtc)
> +ifeq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),y)
> +       $(call cmd,embedcapsulekey,$@)
> +endif
>
>  pre-tmp = $(subst $(comma),_,$(dot-target).pre.tmp)
>  dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp)
> diff --git a/scripts/embed_capsule_key.sh b/scripts/embed_capsule_key.sh
> new file mode 100755
> index 0000000000..1c2e45f758
> --- /dev/null
> +++ b/scripts/embed_capsule_key.sh
> @@ -0,0 +1,25 @@
> +#! /bin/bash
> +# SPDX-License-Identifier: GPL-2.0+
> +#
> +# Copyright (C) 2023, Linaro Limited
> +#
> +
> +gen_capsule_signature_file() {
> +cat >> $1 << EOF
> +/dts-v1/;
> +/plugin/;
> +
> +&{/} {
> +       signature {
> +               capsule-key = /incbin/(CONFIG_EFI_CAPSULE_ESL_FILE);
> +       };
> +};
> +EOF
> +}
> +
> +gen_capsule_signature_file signature.$$.dts > /dev/null 2>&1
> +$CPP $dtc_cpp_flags -x assembler-with-cpp -o signature.$$.tmp signature.$$.dts > /dev/null 2>&1
> +dtc -@ -O dtb -o signature.$$.dtbo signature.$$.tmp > /dev/null 2>&1
> +fdtoverlay -i $1 -o temp.$$.dtb -v signature.$$.dtbo > /dev/null 2>&1
> +mv temp.$$.dtb $1 > /dev/null 2>&1
> +rm -f signature.$$.* > /dev/null 2>&1
> --
> 2.34.1
>

Can you please add this to binman instead?

Regards,
Simon

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 6/7] test: efi_capsule: Test capsule generation from config file
  2023-06-13 10:38 ` [PATCH 6/7] test: efi_capsule: Test capsule generation from config file Sughosh Ganu
@ 2023-06-15  9:14   ` Simon Glass
  0 siblings, 0 replies; 50+ messages in thread
From: Simon Glass @ 2023-06-15  9:14 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Michal Simek,
	Takahiro Akashi

Hi Sughosh,

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

These tests should really be in binman.

Regards,
Simon

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/7] Makefile: Add a target for building capsules
  2023-06-13 10:38 ` [PATCH 5/7] Makefile: Add a target for building capsules Sughosh Ganu
@ 2023-06-15  9:14   ` Simon Glass
  2023-06-15 16:25     ` Sughosh Ganu
  0 siblings, 1 reply; 50+ messages in thread
From: Simon Glass @ 2023-06-15  9:14 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Michal Simek,
	Takahiro Akashi

Hi Sughosh,

On Tue, 13 Jun 2023 at 11:39, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
>
> Add a target for building EFI capsules. The capsule parameters are
> specified through a config file, and the path to the config file is
> specified through CONFIG_EFI_CAPSULE_CFG_FILE. When the config file is
> not specified, the command only builds tools.
>
> Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> ---
>  Makefile | 9 +++++++++
>  1 file changed, 9 insertions(+)
>
> diff --git a/Makefile b/Makefile
> index 10bfaa52ad..96db29aa77 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -1151,6 +1151,15 @@ dtbs: dts/dt.dtb
>  dts/dt.dtb: u-boot
>         $(Q)$(MAKE) $(build)=dts dtbs
>
> +quiet_cmd_mkeficapsule = MKEFICAPSULE $@
> +cmd_mkeficapsule = $(objtree)/tools/mkeficapsule $@
> +
> +PHONY += capsule
> +capsule: tools
> +ifneq ($(CONFIG_EFI_CAPSULE_CFG_FILE),"")
> +       $(call cmd,mkeficapsule)
> +endif
> +
>  quiet_cmd_copy = COPY    $@
>        cmd_copy = cp $< $@
>
> --
> 2.34.1
>

We should be using binman to build images...you seem to be building
something in parallel with that. Can you please take a look at binman?

Regards,
Simon

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 3/7] doc: capsule: Document the new mechanism to embed ESL file into dtb
  2023-06-13 10:38 ` [PATCH 3/7] doc: capsule: Document the new mechanism to embed ESL file into dtb Sughosh Ganu
@ 2023-06-15  9:14   ` Simon Glass
  0 siblings, 0 replies; 50+ messages in thread
From: Simon Glass @ 2023-06-15  9:14 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Michal Simek,
	Takahiro Akashi

Hi Sughosh,

On Tue, 13 Jun 2023 at 11:38, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
>
> 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>
> ---
>  doc/develop/uefi/uefi.rst | 19 +++++--------------
>  1 file changed, 5 insertions(+), 14 deletions(-)
>
> diff --git a/doc/develop/uefi/uefi.rst b/doc/develop/uefi/uefi.rst
> index ffe25ca231..f96762af39 100644
> --- a/doc/develop/uefi/uefi.rst
> +++ b/doc/develop/uefi/uefi.rst
> @@ -495,20 +495,11 @@ and used by the steps highlighted below.
>              ...
>      }
>
> -You can do step-4 manually with
> -
> -.. code-block:: console
> -
> -    $ dtc -@ -I dts -O dtb -o signature.dtbo signature.dts
> -    $ fdtoverlay -i orig.dtb -o new.dtb -v signature.dtbo
> -
> -where signature.dts looks like::
> -
> -    &{/} {
> -            signature {
> -                    capsule-key = /incbin/("CRT.esl");
> -            };
> -    };
> +You can perform step-4 by defining the Kconfig symbol
> +CONFIG_EFI_CAPSULE_ESL_FILE. This symbol defines the path to the esl
> +file generated in step-2. Once the symbol has been populated with the
> +path to the esl file, the esl file will automatically get embedded
> +into the platform's dtb as part of u-boot build.
>
>  Executing the boot manager
>  ~~~~~~~~~~~~~~~~~~~~~~~~~~
> --
> 2.34.1
>

This should be handled as part of the binman build.

Regards,
Simon

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 1/7] capsule: authenticate: Embed capsule public key in platform's dtb
  2023-06-15  9:14   ` Simon Glass
@ 2023-06-15 16:11     ` Sughosh Ganu
  2023-06-19 12:36       ` Simon Glass
  0 siblings, 1 reply; 50+ messages in thread
From: Sughosh Ganu @ 2023-06-15 16:11 UTC (permalink / raw)
  To: Simon Glass
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Michal Simek,
	Takahiro Akashi

hi Simon,

On Thu, 15 Jun 2023 at 14:44, Simon Glass <sjg@chromium.org> wrote:
>
> Hi Sughosh,
>
> On Tue, 13 Jun 2023 at 11:41, 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 script for embedding the ESL used for capsule authentication in
> > the platform's dtb, and call this as part of building the dtb(s). This
> > brings the embedding of the ESL in the dtb into the u-boot build flow.
> >
> > 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>
> > ---
> >  lib/efi_loader/Kconfig       | 11 +++++++++++
> >  scripts/Makefile.lib         |  8 ++++++++
> >  scripts/embed_capsule_key.sh | 25 +++++++++++++++++++++++++
> >  3 files changed, 44 insertions(+)
> >  create mode 100755 scripts/embed_capsule_key.sh
> >
> > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> > index c5835e6ef6..1326a1d109 100644
> > --- a/lib/efi_loader/Kconfig
> > +++ b/lib/efi_loader/Kconfig
> > @@ -234,6 +234,17 @@ config EFI_CAPSULE_MAX
> >           Select the max capsule index value used for capsule report
> >           variables. This value is used to create CapsuleMax variable.
> >
> > +config EFI_CAPSULE_ESL_FILE
> > +       string "Path to the EFI Signature List File"
> > +       default ""
> > +       depends on EFI_CAPSULE_AUTHENTICATE
> > +       help
> > +         Provides the absolute path to the EFI Signature List
> > +         file which will be embedded in the platform's device
> > +         tree and used for capsule authentication at the time
> > +         of capsule update.
> > +
> > +
> >  config EFI_DEVICE_PATH_TO_TEXT
> >         bool "Device path to text protocol"
> >         default y
> > diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
> > index 7b27224b5d..a4083d0a26 100644
> > --- a/scripts/Makefile.lib
> > +++ b/scripts/Makefile.lib
> > @@ -192,6 +192,8 @@ dtc_cpp_flags  = -Wp,-MD,$(depfile).pre.tmp -nostdinc                    \
> >                  -D__ASSEMBLY__                                          \
> >                  -undef -D__DTS__
> >
> > +export dtc_cpp_flags
> > +
> >  # Finds the multi-part object the current object will be linked into
> >  modname-multi = $(sort $(foreach m,$(multi-used),\
> >                 $(if $(filter $(subst $(obj)/,,$*.o), $($(m:.o=-objs)) $($(m:.o=-y))),$(m:.o=))))
> > @@ -315,6 +317,9 @@ ifeq ($(CONFIG_OF_LIBFDT_OVERLAY),y)
> >  DTC_FLAGS += -@
> >  endif
> >
> > +quiet_cmd_embedcapsulekey = EMBEDCAPSULEKEY $@
> > +cmd_embedcapsulekey = $(srctree)/scripts/embed_capsule_key.sh $@
> > +
> >  quiet_cmd_dtc = DTC     $@
> >  # Modified for U-Boot
> >  # Bring in any U-Boot-specific include at the end of the file
> > @@ -333,6 +338,9 @@ cmd_dtc = mkdir -p $(dir ${dtc-tmp}) ; \
> >
> >  $(obj)/%.dtb: $(src)/%.dts FORCE
> >         $(call if_changed_dep,dtc)
> > +ifeq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),y)
> > +       $(call cmd,embedcapsulekey,$@)
> > +endif
> >
> >  pre-tmp = $(subst $(comma),_,$(dot-target).pre.tmp)
> >  dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp)
> > diff --git a/scripts/embed_capsule_key.sh b/scripts/embed_capsule_key.sh
> > new file mode 100755
> > index 0000000000..1c2e45f758
> > --- /dev/null
> > +++ b/scripts/embed_capsule_key.sh
> > @@ -0,0 +1,25 @@
> > +#! /bin/bash
> > +# SPDX-License-Identifier: GPL-2.0+
> > +#
> > +# Copyright (C) 2023, Linaro Limited
> > +#
> > +
> > +gen_capsule_signature_file() {
> > +cat >> $1 << EOF
> > +/dts-v1/;
> > +/plugin/;
> > +
> > +&{/} {
> > +       signature {
> > +               capsule-key = /incbin/(CONFIG_EFI_CAPSULE_ESL_FILE);
> > +       };
> > +};
> > +EOF
> > +}
> > +
> > +gen_capsule_signature_file signature.$$.dts > /dev/null 2>&1
> > +$CPP $dtc_cpp_flags -x assembler-with-cpp -o signature.$$.tmp signature.$$.dts > /dev/null 2>&1
> > +dtc -@ -O dtb -o signature.$$.dtbo signature.$$.tmp > /dev/null 2>&1
> > +fdtoverlay -i $1 -o temp.$$.dtb -v signature.$$.dtbo > /dev/null 2>&1
> > +mv temp.$$.dtb $1 > /dev/null 2>&1
> > +rm -f signature.$$.* > /dev/null 2>&1
> > --
> > 2.34.1
> >
>
> Can you please add this to binman instead?

I had looked at using binman for this work earlier because I very much
expected this comment from you :). Having said that, I am very much
open to using binman instead if it turns out to be the better way of
achieving this. What this patch does is that, with capsule
authentication enabled, it embeds the public key esl file into the
dtb's as they get built. As per my understanding, binman gets called
at the end of the u-boot build, once the constituent images( e..g
u-boot.bin = u-boot-no-dtb.bin + dtb) have been generated. So, if we
call binman _after_ the requisite image(s) have been generated, we
would need to 1) identify the dtb's in which the esl needs to be
embedded, and then 2) generate the final image all over again. Don't
you think this is non optimal? Or is there a way of generating the
constituent images(including the dtb's) through binman instead?

My understanding of binman is that it is a tool of packaging
constituent images together. But the constituent images are still
being built through make targets.

-sughosh

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/7] Makefile: Add a target for building capsules
  2023-06-15  9:14   ` Simon Glass
@ 2023-06-15 16:25     ` Sughosh Ganu
  2023-06-19 12:37       ` Simon Glass
  0 siblings, 1 reply; 50+ messages in thread
From: Sughosh Ganu @ 2023-06-15 16:25 UTC (permalink / raw)
  To: Simon Glass
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Michal Simek,
	Takahiro Akashi

hi Simon,

On Thu, 15 Jun 2023 at 14:44, Simon Glass <sjg@chromium.org> wrote:
>
> Hi Sughosh,
>
> On Tue, 13 Jun 2023 at 11:39, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> >
> > Add a target for building EFI capsules. The capsule parameters are
> > specified through a config file, and the path to the config file is
> > specified through CONFIG_EFI_CAPSULE_CFG_FILE. When the config file is
> > not specified, the command only builds tools.
> >
> > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > ---
> >  Makefile | 9 +++++++++
> >  1 file changed, 9 insertions(+)
> >
> > diff --git a/Makefile b/Makefile
> > index 10bfaa52ad..96db29aa77 100644
> > --- a/Makefile
> > +++ b/Makefile
> > @@ -1151,6 +1151,15 @@ dtbs: dts/dt.dtb
> >  dts/dt.dtb: u-boot
> >         $(Q)$(MAKE) $(build)=dts dtbs
> >
> > +quiet_cmd_mkeficapsule = MKEFICAPSULE $@
> > +cmd_mkeficapsule = $(objtree)/tools/mkeficapsule $@
> > +
> > +PHONY += capsule
> > +capsule: tools
> > +ifneq ($(CONFIG_EFI_CAPSULE_CFG_FILE),"")
> > +       $(call cmd,mkeficapsule)
> > +endif
> > +
> >  quiet_cmd_copy = COPY    $@
> >        cmd_copy = cp $< $@
> >
> > --
> > 2.34.1
> >
>
> We should be using binman to build images...you seem to be building
> something in parallel with that. Can you please take a look at binman?

Again, I had explored using binman for this task. The one issue where
I find the above flow better is that I can simply build my payload
image(s) followed by 'make capsule' to generate the capsules for
earlier generated images. In it's current form, I don't see an easy
way to enforce this dependency in binman when I want to build the
payload followed by generation of capsules. I did see the mention of
encapsulating an entry within another dependent entry, but I think
that makes the implementation more complex than it ought to be.

I think it is much easier to use the make flow to generate the images
followed by capsules, instead of tweaking the binman node to first
generate the payload images, followed by enabling the capsule node to
build the capsules. If there is an easy way of enforcing this
dependency, please let me know. Thanks

-sughosh

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 4/7] tools: mkeficapsule: Add support for parsing capsule params from config file
  2023-06-15  5:49           ` Takahiro Akashi
@ 2023-06-16  4:26             ` Sughosh Ganu
  2023-06-16  4:46               ` Takahiro Akashi
  0 siblings, 1 reply; 50+ messages in thread
From: Sughosh Ganu @ 2023-06-16  4:26 UTC (permalink / raw)
  To: Takahiro Akashi, Sughosh Ganu, u-boot, Heinrich Schuchardt,
	Ilias Apalodimas, Simon Glass, Michal Simek

On Thu, 15 Jun 2023 at 11:19, Takahiro Akashi
<takahiro.akashi@linaro.org> wrote:
>
> On Thu, Jun 15, 2023 at 10:09:06AM +0530, Sughosh Ganu wrote:
> > On Wed, 14 Jun 2023 at 11:23, Takahiro Akashi
> > <takahiro.akashi@linaro.org> wrote:
> > >
> > > On Wed, Jun 14, 2023 at 10:56:23AM +0530, Sughosh Ganu wrote:
> > > > hi Takahiro,
> > > >
> > > > On Wed, 14 Jun 2023 at 09:09, Takahiro Akashi
> > > > <takahiro.akashi@linaro.org> wrote:
> > > > >
> > > > > Hi Sughosh,
> > > > >
> > > > > I think this is a good extension to mkeficapsule, but
> > > > >
> > > > > On Tue, Jun 13, 2023 at 04:08:03PM +0530, Sughosh Ganu wrote:
> > > > > > Add support for specifying the parameters needed for capsule
> > > > > > generation through a config file, instead of passing them through
> > > > > > command-line. Parameters for more than a single capsule file can be
> > > > > > specified, resulting in generation of multiple capsules through a
> > > > > > single invocation of the command.
> > > > > >
> > > > > > This path is to be used for generating capsules through a make target,
> > > > > > with the parameters being parsed from the config file.
> > > > > >
> > > > > > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > > > > > ---
> > > > > >  tools/Kconfig              |   9 +
> > > > > >  tools/Makefile             |   1 +
> > > > > >  tools/eficapsule.h         | 110 ++++++++++++
> > > > > >  tools/mkeficapsule.c       | 106 +++++++-----
> > > > > >  tools/mkeficapsule_parse.c | 345 +++++++++++++++++++++++++++++++++++++
> > > > > >  5 files changed, 531 insertions(+), 40 deletions(-)
> > > > > >  create mode 100644 tools/mkeficapsule_parse.c
> > > > > >
> > > > > > diff --git a/tools/Kconfig b/tools/Kconfig
> > > > > > index 539708f277..95f27b7c45 100644
> > > > > > --- a/tools/Kconfig
> > > > > > +++ b/tools/Kconfig
> > > > > > @@ -98,6 +98,15 @@ config TOOLS_MKEFICAPSULE
> > > > > >         optionally sign that file. If you want to enable UEFI capsule
> > > > > >         update feature on your target, you certainly need this.
> > > > > >
> > > > > > +config EFI_CAPSULE_CFG_FILE
> > > > > > +     string "Path to the EFI Capsule Config File"
> > > > > > +     default ""
> > > > > > +     help
> > > > > > +       Path to the EFI capsule config file which provides the
> > > > > > +       parameters needed to build capsule(s). Parameters can be
> > > > > > +       provided for multiple payloads resulting in corresponding
> > > > > > +       capsule images being generated.
> > > > > > +
> > > > > >  menuconfig FSPI_CONF_HEADER
> > > > > >       bool "FlexSPI Header Configuration"
> > > > > >       help
> > > > > > diff --git a/tools/Makefile b/tools/Makefile
> > > > > > index d793cf3bec..ef366f3d61 100644
> > > > > > --- a/tools/Makefile
> > > > > > +++ b/tools/Makefile
> > > > > > @@ -250,6 +250,7 @@ HOSTLDLIBS_mkeficapsule += \
> > > > > >  HOSTLDLIBS_mkeficapsule += \
> > > > > >       $(shell pkg-config --libs uuid 2> /dev/null || echo "-luuid")
> > > > > >  hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule
> > > > > > +mkeficapsule-objs := mkeficapsule.o mkeficapsule_parse.o
> > > > > >
> > > > > >  # We build some files with extra pedantic flags to try to minimize things
> > > > > >  # that won't build on some weird host compiler -- though there are lots of
> > > > > > diff --git a/tools/eficapsule.h b/tools/eficapsule.h
> > > > > > index 072a4b5598..42e66c6d6a 100644
> > > > > > --- a/tools/eficapsule.h
> > > > > > +++ b/tools/eficapsule.h
> > > > > > @@ -52,6 +52,38 @@ typedef struct {
> > > > > >  /* flags */
> > > > > >  #define CAPSULE_FLAGS_PERSIST_ACROSS_RESET      0x00010000
> > > > > >
> > > > > > +enum capsule_type {
> > > > > > +     CAPSULE_NORMAL_BLOB = 0,
> > > > > > +     CAPSULE_ACCEPT,
> > > > > > +     CAPSULE_REVERT,
> > > > > > +};
> > > > > > +
> > > > > > +/**
> > > > > > + * struct efi_capsule_params - Capsule parameters
> > > > > > + * @image_guid: Guid value of the payload input image
> > > > > > + * @image_index: Image index value
> > > > > > + * @hardware_instance: Hardware instance to be used for the image
> > > > > > + * @monotonic_count: Monotonic count value to be used for signed capsule
> > > > > > + * @privkey_file: Path to private key used in capsule signing
> > > > > > + * @cert_file: Path to public key certificate used in capsule signing
> > > > > > + * @input_file: Path to payload input image
> > > > > > + * @capsule_file: Path to the output capsule file
> > > > > > + * @oemflags: Oemflags to be populated in the capsule header
> > > > > > + * @capsule: Capsule Type, normal or accept or revert
> > > > > > + */
> > > > > > +struct efi_capsule_params {
> > > > > > +     efi_guid_t *image_guid;
> > > > > > +     unsigned long image_index;
> > > > > > +     unsigned long hardware_instance;
> > > > > > +     uint64_t monotonic_count;
> > > > > > +     char *privkey_file;
> > > > > > +     char *cert_file;
> > > > > > +     char *input_file;
> > > > > > +     char *capsule_file;
> > > > > > +     unsigned long oemflags;
> > > > > > +     enum capsule_type capsule;
> > > > > > +};
> > > > > > +
> > > > > >  struct efi_capsule_header {
> > > > > >       efi_guid_t capsule_guid;
> > > > > >       uint32_t header_size;
> > > > > > @@ -113,4 +145,82 @@ struct efi_firmware_image_authentication {
> > > > > >       struct win_certificate_uefi_guid auth_info;
> > > > > >  } __packed;
> > > > > >
> > > > > > +/**
> > > > > > + * capsule_with_cfg_file() - Generate capsule from config file
> > > > > > + * @cfg_file: Path to the config file
> > > > > > + *
> > > > > > + * Parse the capsule parameters from the config file and use the
> > > > > > + * parameters for generating one or more capsules.
> > > > > > + *
> > > > > > + * Return: None
> > > > > > + *
> > > > > > + */
> > > > > > +void capsule_with_cfg_file(const char *cfg_file);
> > > > > > +
> > > > > > +/**
> > > > > > + * convert_uuid_to_guid() - convert UUID to GUID
> > > > > > + * @buf:     UUID binary
> > > > > > + *
> > > > > > + * UUID and GUID have the same data structure, but their binary
> > > > > > + * formats are different due to the endianness. See lib/uuid.c.
> > > > > > + * Since uuid_parse() can handle only UUID, this function must
> > > > > > + * be called to get correct data for GUID when parsing a string.
> > > > > > + *
> > > > > > + * The correct data will be returned in @buf.
> > > > > > + */
> > > > > > +void convert_uuid_to_guid(unsigned char *buf);
> > > > > > +
> > > > > > +/**
> > > > > > + * create_empty_capsule() - Generate an empty capsule
> > > > > > + * @path: Path to the empty capsule file to be generated
> > > > > > + * @guid: Guid value of the image for which empty capsule is generated
> > > > > > + * @fw_accept: Flag to specify whether to generate accept or revert capsule
> > > > > > + *
> > > > > > + * Generate an empty capsule, either an accept or a revert capsule to be
> > > > > > + * used to flag acceptance or rejection of an earlier executed firmware
> > > > > > + * update operation. Being used in the FWU Multi Bank firmware update
> > > > > > + * feature.
> > > > > > + *
> > > > > > + * Return: 0 if OK, -ve on error
> > > > > > + *
> > > > > > + */
> > > > > > +int create_empty_capsule(char *path, efi_guid_t *guid, bool fw_accept);
> > > > > > +
> > > > > > +/**
> > > > > > + * create_fwbin - create an uefi capsule file
> > > > > > + * @path:    Path to a created capsule file
> > > > > > + * @bin:     Path to a firmware binary to encapsulate
> > > > > > + * @guid:    GUID of related FMP driver
> > > > > > + * @index:   Index number in capsule
> > > > > > + * @instance:        Instance number in capsule
> > > > > > + * @mcount:  Monotonic count in authentication information
> > > > > > + * @private_file:    Path to a private key file
> > > > > > + * @cert_file:       Path to a certificate file
> > > > > > + * @oemflags:  Capsule OEM Flags, bits 0-15
> > > > > > + *
> > > > > > + * This function actually does the job of creating an uefi capsule file.
> > > > > > + * All the arguments must be supplied.
> > > > > > + * If either @private_file ror @cert_file is NULL, the capsule file
> > > > > > + * won't be signed.
> > > > > > + *
> > > > > > + * Return:
> > > > > > + * * 0  - on success
> > > > > > + * * -1 - on failure
> > > > > > + */
> > > > > > +int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > > > +              unsigned long index, unsigned long instance,
> > > > > > +              uint64_t mcount, char *privkey_file, char *cert_file,
> > > > > > +              uint16_t oemflags);
> > > > > > +
> > > > > > +/**
> > > > > > + * print_usage() - Print the command usage string
> > > > > > + *
> > > > > > + * Prints the standard command usage string. Called in the case
> > > > > > + * of incorrect parameters being passed to the tool.
> > > > > > + *
> > > > > > + * Return: None
> > > > > > + *
> > > > > > + */
> > > > > > +void print_usage(void);
> > > > > > +
> > > > > >  #endif /* _EFI_CAPSULE_H */
> > > > > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> > > > > > index b71537beee..711adf0439 100644
> > > > > > --- a/tools/mkeficapsule.c
> > > > > > +++ b/tools/mkeficapsule.c
> > > > > > @@ -31,12 +31,6 @@ 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 struct option options[] = {
> > > > > >       {"guid", required_argument, NULL, 'g'},
> > > > > >       {"index", required_argument, NULL, 'i'},
> > > > > > @@ -52,7 +46,16 @@ static struct option options[] = {
> > > > > >       {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"
> > > > > > @@ -400,10 +403,10 @@ static void free_sig_data(struct auth_context *ctx)
> > > > > >   * * 0  - on success
> > > > > >   * * -1 - on failure
> > > > > >   */
> > > > > > -static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > > > -                     unsigned long index, unsigned long instance,
> > > > > > -                     uint64_t mcount, char *privkey_file, char *cert_file,
> > > > > > -                     uint16_t oemflags)
> > > > > > +int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > > > +              unsigned long index, unsigned long instance,
> > > > > > +              uint64_t mcount, char *privkey_file, char *cert_file,
> > > > > > +              uint16_t oemflags)
> > > > > >  {
> > > > > >       struct efi_capsule_header header;
> > > > > >       struct efi_firmware_management_capsule_header capsule;
> > > > > > @@ -580,7 +583,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;
> > > > > > @@ -623,19 +640,7 @@ err:
> > > > > >       return ret;
> > > > > >  }
> > > > > >
> > > > > > -/**
> > > > > > - * main - main entry function of mkeficapsule
> > > > > > - * @argc:    Number of arguments
> > > > > > - * @argv:    Array of pointers to arguments
> > > > > > - *
> > > > > > - * Create an uefi capsule file, optionally signing it.
> > > > > > - * Parse all the arguments and pass them on to create_fwbin().
> > > > > > - *
> > > > > > - * Return:
> > > > > > - * * 0  - on success
> > > > > > - * * -1 - on failure
> > > > > > - */
> > > > > > -int main(int argc, char **argv)
> > > > > > +static void capsule_with_cmdline_params(int argc, char **argv)
> > > > > >  {
> > > > > >       efi_guid_t *guid;
> > > > > >       unsigned char uuid_buf[16];
> > > > > > @@ -643,6 +648,7 @@ int main(int argc, char **argv)
> > > > > >       uint64_t mcount;
> > > > > >       unsigned long oemflags;
> > > > > >       char *privkey_file, *cert_file;
> > > > > > +     enum capsule_type capsule;
> > > > > >       int c, idx;
> > > > > >
> > > > > >       guid = NULL;
> > > > > > @@ -652,7 +658,7 @@ int main(int argc, char **argv)
> > > > > >       privkey_file = NULL;
> > > > > >       cert_file = NULL;
> > > > > >       dump_sig = 0;
> > > > > > -     capsule_type = CAPSULE_NORMAL_BLOB;
> > > > > > +     capsule = CAPSULE_NORMAL_BLOB;
> > > > > >       oemflags = 0;
> > > > > >       for (;;) {
> > > > > >               c = getopt_long(argc, argv, opts_short, options, &idx);
> > > > > > @@ -702,20 +708,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);
> > > > > > @@ -732,21 +738,21 @@ int main(int argc, char **argv)
> > > > > >       }
> > > > > >
> > > > > >       /* check necessary parameters */
> > > > > > -     if ((capsule_type == CAPSULE_NORMAL_BLOB &&
> > > > > > -         ((argc != optind + 2) || !guid ||
> > > > > > -          ((privkey_file && !cert_file) ||
> > > > > > -           (!privkey_file && cert_file)))) ||
> > > > > > -         (capsule_type != CAPSULE_NORMAL_BLOB &&
> > > > > > -         ((argc != optind + 1) ||
> > > > > > -          ((capsule_type == CAPSULE_ACCEPT) && !guid) ||
> > > > > > -          ((capsule_type == CAPSULE_REVERT) && guid)))) {
> > > > > > +     if ((capsule == CAPSULE_NORMAL_BLOB &&
> > > > > > +          ((argc != optind + 2) || !guid ||
> > > > > > +           ((privkey_file && !cert_file) ||
> > > > > > +            (!privkey_file && cert_file)))) ||
> > > > > > +         (capsule != CAPSULE_NORMAL_BLOB &&
> > > > > > +          ((argc != optind + 1) ||
> > > > > > +           (capsule == CAPSULE_ACCEPT && !guid) ||
> > > > > > +           (capsule == CAPSULE_REVERT && guid)))) {
> > > > > >               print_usage();
> > > > > >               exit(EXIT_FAILURE);
> > > > > >       }
> > > > > >
> > > > > > -     if (capsule_type != CAPSULE_NORMAL_BLOB) {
> > > > > > +     if (capsule != CAPSULE_NORMAL_BLOB) {
> > > > > >               if (create_empty_capsule(argv[argc - 1], guid,
> > > > > > -                                      capsule_type == CAPSULE_ACCEPT) < 0) {
> > > > > > +                                      capsule == CAPSULE_ACCEPT) < 0) {
> > > > > >                       fprintf(stderr, "Creating empty capsule failed\n");
> > > > > >                       exit(EXIT_FAILURE);
> > > > > >               }
> > > > > > @@ -756,6 +762,26 @@ int main(int argc, char **argv)
> > > > > >               fprintf(stderr, "Creating firmware capsule failed\n");
> > > > > >               exit(EXIT_FAILURE);
> > > > > >       }
> > > > > > +}
> > > > > > +
> > > > > > +/**
> > > > > > + * main - main entry function of mkeficapsule
> > > > > > + * @argc:    Number of arguments
> > > > > > + * @argv:    Array of pointers to arguments
> > > > > > + *
> > > > > > + * Create an uefi capsule file, optionally signing it.
> > > > > > + * Parse all the arguments and pass them on to create_fwbin().
> > > > > > + *
> > > > > > + * Return:
> > > > > > + * * 0  - on success
> > > > > > + * * -1 - on failure
> > > > > > + */
> > > > > > +int main(int argc, char **argv)
> > > > > > +{
> > > > > > +     if (!strcmp(CONFIG_EFI_CAPSULE_CFG_FILE, ""))
> > > > > > +             capsule_with_cmdline_params(argc, argv);
> > > > > > +     else
> > > > > > +             capsule_with_cfg_file(CONFIG_EFI_CAPSULE_CFG_FILE);
> > > > >
> > > > > I don't know where the macro, CONFIG_EFI_CAPSULE_CFG_FILE, comes from.
> > > > > Anyhow, as a general rule, any host tool must be as generic as it should not
> > > > > depend on a target's config.
> > > > > (I was told so before.)
> > > > >
> > > > > So I would suggest that you add another command line, say "--config-file <file>",
> > > > > to make the command generic.
> > > >
> > > > Yes, that would be something followed by most of the tools. The reason
> > > > I did not add a command-line option for the confile file is because I
> > > > want the capsule generation added as a make target. With the path to
> > > > the config file specified through the Kconfig symbol, we can invoke
> > > > 'make capsule', and it would build the capsules by parsing the
> > > > parameters from the config file, taken from the Kconfig symbol. I know
> > > > there are ways of specifying options when using a make command, but I
> > > > don't think that is a clean way of doing things.
> > >
> > > Not sure, but in your [5/7],
> > > cmd_mkeficapsule = $(objtree)/tools/mkeficapsule --config-file $(CONFIG_EFI_CAPSULE_CFG_FILE)
> > >
> > > Doesn't this change work?
> >
> > So, I tried the above suggested change. But trying to run a make
> > 'target' does not work without the .config file being present.
>
> Not sure what you meant to say here.
> Why don't you have .config when building U-Boot (or rather 'target')?

Maybe I misunderstood your earlier comment, but I thought you were
looking to build capsules without relying on a target config. Which I
believe cannot be done for a make target.

-sughosh

>
> -Takahiro Akashi
>
> > FWIW,
> > the same is the case for building tools as well. I think that is the
> > reason for the tools-only_defconfig.
> >
> > -sughosh
> >
> > >
> > > -Takahiro Akashi
> > >
> > >
> > > > Given the use case of
> > > > a make target, I hope we can use the Kconfig symbol for specifying the
> > > > config file path.
> > > >
> > > > -sughosh
> > > >
> > > > >
> > > > > -Takahiro Akashi
> > > > >
> > > > >
> > > > > >
> > > > > >       exit(EXIT_SUCCESS);
> > > > > >  }
> > > > > > diff --git a/tools/mkeficapsule_parse.c b/tools/mkeficapsule_parse.c
> > > > > > new file mode 100644
> > > > > > index 0000000000..ef4f3f6705
> > > > > > --- /dev/null
> > > > > > +++ b/tools/mkeficapsule_parse.c
> > > > > > @@ -0,0 +1,345 @@
> > > > > > +// SPDX-License-Identifier: GPL-2.0
> > > > > > +/*
> > > > > > + * Copyright 2023 Linaro Limited
> > > > > > + */
> > > > > > +
> > > > > > +/*
> > > > > > + * The code in this file adds parsing ability to the mkeficapsule
> > > > > > + * tool. This allows specifying parameters needed to build the capsule
> > > > > > + * through the config file instead of specifying them on the command-line.
> > > > > > + * Parameters can be specified for more than one payload, generating the
> > > > > > + * corresponding capsule files.
> > > > > > + *
> > > > > > + * The parameters are specified in a "key:value" pair. All the parameters
> > > > > > + * that are currently supported by the mkeficapsule tool can be specified
> > > > > > + * in the config file.
> > > > > > + *
> > > > > > + * The example below shows four payloads. The first payload is an example
> > > > > > + * of generating a signed capsule. The second payload is an example of
> > > > > > + * generating an unsigned capsule. The third payload is an accept empty
> > > > > > + * capsule, while the fourth payload is the revert empty capsule, used
> > > > > > + * for the multi-bank firmware update feature.
> > > > > > + *
> > > > > > + * This functionality can be easily extended to generate a single capsule
> > > > > > + * comprising multiple payloads.
> > > > > > +
> > > > > > +     {
> > > > > > +         image-guid: 02f4d760-cfd5-43bd-8e2d-a42acb33c660
> > > > > > +         hardware-instance: 0
> > > > > > +         monotonic-count: 1
> > > > > > +         payload: u-boot.bin
> > > > > > +         image-index: 1
> > > > > > +         private-key: /path/to/priv/key
> > > > > > +         pub-key-cert: /path/to/pub/key
> > > > > > +         capsule: u-boot.capsule
> > > > > > +     }
> > > > > > +     {
> > > > > > +         image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e
> > > > > > +         hardware-instance: 0
> > > > > > +         payload: u-boot.itb
> > > > > > +         image-index: 2
> > > > > > +         oemflags: 0x8000
> > > > > > +         capsule: fit.capsule
> > > > > > +     }
> > > > > > +     {
> > > > > > +         capsule-type: accept
> > > > > > +         image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e
> > > > > > +         capsule: accept.capsule
> > > > > > +     }
> > > > > > +     {
> > > > > > +         capsule-type: revert
> > > > > > +         capsule: revert.capsule
> > > > > > +     }
> > > > > > +*/
> > > > > > +
> > > > > > +#include <ctype.h>
> > > > > > +#include <limits.h>
> > > > > > +#include <stdio.h>
> > > > > > +#include <stdlib.h>
> > > > > > +#include <string.h>
> > > > > > +
> > > > > > +#include <uuid/uuid.h>
> > > > > > +
> > > > > > +#include "eficapsule.h"
> > > > > > +
> > > > > > +#define PARAMS_START "{"
> > > > > > +#define PARAMS_END   "}"
> > > > > > +
> > > > > > +#define PSTART               2
> > > > > > +#define PEND         3
> > > > > > +
> > > > > > +#define MALLOC_FAIL_STR              "Unable to allocate memory\n"
> > > > > > +
> > > > > > +#define ARRAY_SIZE(x)                (sizeof(x) / sizeof((x)[0]))
> > > > > > +
> > > > > > +const char *capsule_params[] = {
> > > > > > +     "image-guid", "image-index", "private-key",
> > > > > > +     "pub-key-cert", "payload", "capsule",
> > > > > > +     "hardware-instance", "monotonic-count",
> > > > > > +     "capsule-type", "oemflags" };
> > > > > > +
> > > > > > +static unsigned char params_start;
> > > > > > +static unsigned char params_end;
> > > > > > +
> > > > > > +static void print_and_exit(const char *str)
> > > > > > +{
> > > > > > +     fprintf(stderr, "%s", str);
> > > > > > +     exit(EXIT_FAILURE);
> > > > > > +}
> > > > > > +
> > > > > > +static int param_delim_checks(char *line, unsigned char *token)
> > > > > > +{
> > > > > > +     if (!strcmp(line, PARAMS_START)) {
> > > > > > +             if (params_start || !params_end) {
> > > > > > +                     fprintf(stderr, "Earlier params processing still in progress. ");
> > > > > > +                     fprintf(stderr, "Can't start processing a new params.\n");
> > > > > > +                     exit(EXIT_FAILURE);
> > > > > > +             } else {
> > > > > > +                     params_start = 1;
> > > > > > +                     params_end = 0;
> > > > > > +                     *token = PSTART;
> > > > > > +                     return 1;
> > > > > > +             }
> > > > > > +     } else if (!strcmp(line, PARAMS_END)) {
> > > > > > +             if (!params_start) {
> > > > > > +                     fprintf(stderr, "Cannot put end braces without start braces. ");
> > > > > > +                     fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > > > +                     exit(EXIT_FAILURE);
> > > > > > +             } else {
> > > > > > +                     params_start = 0;
> > > > > > +                     params_end = 1;
> > > > > > +                     *token = PEND;
> > > > > > +                     return 1;
> > > > > > +             }
> > > > > > +     } else if (!params_start) {
> > > > > > +             fprintf(stderr, "Params should be passed within braces. ");
> > > > > > +             fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > > > +             exit(EXIT_FAILURE);
> > > > > > +     }
> > > > > > +
> > > > > > +     return 0;
> > > > > > +}
> > > > > > +
> > > > > > +static void add_guid(efi_guid_t **guid_param, char *guid)
> > > > > > +{
> > > > > > +     unsigned char uuid_buf[16];
> > > > > > +
> > > > > > +     *guid_param = malloc(sizeof(efi_guid_t));
> > > > > > +     if (!*guid_param)
> > > > > > +             print_and_exit(MALLOC_FAIL_STR);
> > > > > > +
> > > > > > +     if (uuid_parse(guid, uuid_buf))
> > > > > > +             print_and_exit("Wrong guid format\n");
> > > > > > +
> > > > > > +     convert_uuid_to_guid(uuid_buf);
> > > > > > +     memcpy(*guid_param, uuid_buf, sizeof(efi_guid_t));
> > > > > > +}
> > > > > > +
> > > > > > +static void add_string(char **dst, char *val)
> > > > > > +{
> > > > > > +     *dst = strdup(val);
> > > > > > +     if (!*dst)
> > > > > > +             print_and_exit(MALLOC_FAIL_STR);
> > > > > > +}
> > > > > > +
> > > > > > +static void match_and_populate_param(char *key, char *val,
> > > > > > +                                  struct efi_capsule_params *param)
> > > > > > +{
> > > > > > +     int i;
> > > > > > +
> > > > > > +     for (i = 0; i < ARRAY_SIZE(capsule_params); i++) {
> > > > > > +             if (!strcmp(key, capsule_params[i])) {
> > > > > > +                     switch (i) {
> > > > > > +                     case 0:
> > > > > > +                             add_guid(&param->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(&param->privkey_file, val);
> > > > > > +                             return;
> > > > > > +                     case 3:
> > > > > > +                             add_string(&param->cert_file, val);
> > > > > > +                             return;
> > > > > > +                     case 4:
> > > > > > +                             add_string(&param->input_file, val);
> > > > > > +                             return;
> > > > > > +                     case 5:
> > > > > > +                             add_string(&param->capsule_file, val);
> > > > > > +                             return;
> > > > > > +                     case 6:
> > > > > > +                             param->hardware_instance = strtoul(val, NULL, 0);
> > > > > > +                             if (param->hardware_instance == ULONG_MAX)
> > > > > > +                                     print_and_exit("Enter a valid hardware instance value");
> > > > > > +                             return;
> > > > > > +                     case 7:
> > > > > > +                             param->monotonic_count = strtoull(val, NULL, 0);
> > > > > > +                             if (param->monotonic_count == ULLONG_MAX)
> > > > > > +                                     print_and_exit("Enter a valid monotonic count value");
> > > > > > +                             return;
> > > > > > +                     case 8:
> > > > > > +                             if (!strcmp(val, "normal"))
> > > > > > +                                     param->capsule = CAPSULE_NORMAL_BLOB;
> > > > > > +                             else if (!strcmp(val, "accept"))
> > > > > > +                                     param->capsule = CAPSULE_ACCEPT;
> > > > > > +                             else if (!strcmp(val, "revert"))
> > > > > > +                                     param->capsule = CAPSULE_REVERT;
> > > > > > +                             else
> > > > > > +                                     print_and_exit("Invalid type of capsule");
> > > > > > +
> > > > > > +                             return;
> > > > > > +                     case 9:
> > > > > > +                             param->oemflags = strtoul(val, NULL, 0);
> > > > > > +                             if (param->oemflags > 0xffff)
> > > > > > +                                     print_and_exit("OemFlags must be between 0x0 and 0xffff\n");
> > > > > > +                             return;
> > > > > > +                     }
> > > > > > +             }
> > > > > > +     }
> > > > > > +
> > > > > > +     fprintf(stderr, "Undefined param %s specified. ", key);
> > > > > > +     fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > > > +     exit(EXIT_FAILURE);
> > > > > > +}
> > > > > > +
> > > > > > +static int get_capsule_params(char *line, struct efi_capsule_params *params)
> > > > > > +{
> > > > > > +     char *key = NULL;
> > > > > > +     char *val = NULL;
> > > > > > +     unsigned char token;
> > > > > > +
> > > > > > +     if (param_delim_checks(line, &token))
> > > > > > +             return token;
> > > > > > +
> > > > > > +     key = strtok(line, ":");
> > > > > > +     if (key)
> > > > > > +             val = strtok(NULL, "\0");
> > > > > > +     else
> > > > > > +             print_and_exit("Expect the params in a key:value pair\n");
> > > > > > +
> > > > > > +     match_and_populate_param(key, val, params);
> > > > > > +
> > > > > > +     return 0;
> > > > > > +}
> > > > > > +
> > > > > > +static char *skip_whitespace(char *line)
> > > > > > +{
> > > > > > +     char *ptr, *newline;
> > > > > > +
> > > > > > +     ptr = malloc(strlen(line) + 1);
> > > > > > +     if (!ptr)
> > > > > > +             print_and_exit(MALLOC_FAIL_STR);
> > > > > > +
> > > > > > +     for (newline = ptr; *line; line++)
> > > > > > +             if (!isblank(*line))
> > > > > > +                     *ptr++ = *line;
> > > > > > +     *ptr = '\0';
> > > > > > +     return newline;
> > > > > > +}
> > > > > > +
> > > > > > +static int parse_capsule_payload_params(FILE *fp, struct efi_capsule_params *params)
> > > > > > +{
> > > > > > +     char *line = NULL;
> > > > > > +     char *newline;
> > > > > > +     size_t n = 0;
> > > > > > +     ssize_t len;
> > > > > > +
> > > > > > +     while ((len = getline(&line, &n, fp)) != -1) {
> > > > > > +             if (len == 1 && line[len - 1] == '\n')
> > > > > > +                     continue;
> > > > > > +
> > > > > > +             line[len - 1] = '\0';
> > > > > > +
> > > > > > +             newline = skip_whitespace(line);
> > > > > > +
> > > > > > +             if (newline[0] == '#')
> > > > > > +                     continue;
> > > > > > +
> > > > > > +             if (get_capsule_params(newline, params) == PEND)
> > > > > > +                     return 0;
> > > > > > +     }
> > > > > > +
> > > > > > +     if (errno == EINVAL || errno == ENOMEM) {
> > > > > > +             fprintf(stderr, "getline() returned an error %s reading the line\n",
> > > > > > +                     strerror(errno));
> > > > > > +             exit(EXIT_FAILURE);
> > > > > > +     } else if (params_start == 1 || params_end == 0) {
> > > > > > +             fprintf(stderr, "Params should be passed within braces. ");
> > > > > > +             fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > > > +             exit(EXIT_FAILURE);
> > > > > > +     } else {
> > > > > > +             return -1;
> > > > > > +     }
> > > > > > +}
> > > > > > +
> > > > > > +static void params_dependency_check(struct efi_capsule_params *params)
> > > > > > +{
> > > > > > +     /* check necessary parameters */
> > > > > > +     if ((params->capsule == CAPSULE_NORMAL_BLOB &&
> > > > > > +          ((!params->input_file || !params->capsule_file ||
> > > > > > +            !params->image_guid) ||
> > > > > > +           ((params->privkey_file && !params->cert_file) ||
> > > > > > +            (!params->privkey_file && params->cert_file)))) ||
> > > > > > +         (params->capsule != CAPSULE_NORMAL_BLOB &&
> > > > > > +          (!params->capsule_file ||
> > > > > > +           (params->capsule == CAPSULE_ACCEPT && !params->image_guid) ||
> > > > > > +           (params->capsule == CAPSULE_REVERT && params->image_guid)))) {
> > > > > > +             print_usage();
> > > > > > +             exit(EXIT_FAILURE);
> > > > > > +     }
> > > > > > +}
> > > > > > +
> > > > > > +static void generate_capsule(struct efi_capsule_params *params)
> > > > > > +{
> > > > > > +     if (params->capsule != CAPSULE_NORMAL_BLOB) {
> > > > > > +             if (create_empty_capsule(params->capsule_file,
> > > > > > +                                      params->image_guid,
> > > > > > +                                      params->capsule ==
> > > > > > +                                      CAPSULE_ACCEPT) < 0)
> > > > > > +                     print_and_exit("Creating empty capsule failed\n");
> > > > > > +     } else if (create_fwbin(params->capsule_file, params->input_file,
> > > > > > +                           params->image_guid, params->image_index,
> > > > > > +                           params->hardware_instance,
> > > > > > +                           params->monotonic_count,
> > > > > > +                           params->privkey_file,
> > > > > > +                           params->cert_file,
> > > > > > +                           (uint16_t)params->oemflags) < 0) {
> > > > > > +             print_and_exit("Creating firmware capsule failed\n");
> > > > > > +     }
> > > > > > +}
> > > > > > +
> > > > > > +/**
> > > > > > + * capsule_with_cfg_file() - Generate capsule from config file
> > > > > > + * @cfg_file: Path to the config file
> > > > > > + *
> > > > > > + * Parse the capsule parameters from the config file and use the
> > > > > > + * parameters for generating one or more capsules.
> > > > > > + *
> > > > > > + * Return: None
> > > > > > + *
> > > > > > + */
> > > > > > +void capsule_with_cfg_file(const char *cfg_file)
> > > > > > +{
> > > > > > +     FILE *fp;
> > > > > > +     struct efi_capsule_params params = { 0 };
> > > > > > +
> > > > > > +     fp = fopen(cfg_file, "r");
> > > > > > +     if (!fp) {
> > > > > > +             fprintf(stderr, "Unable to open the capsule config file %s\n",
> > > > > > +                     cfg_file);
> > > > > > +             exit(EXIT_FAILURE);
> > > > > > +     }
> > > > > > +
> > > > > > +     params_start = 0;
> > > > > > +     params_end = 1;
> > > > > > +
> > > > > > +     while (parse_capsule_payload_params(fp, &params) != -1) {
> > > > > > +             params_dependency_check(&params);
> > > > > > +             generate_capsule(&params);
> > > > > > +
> > > > > > +             memset(&params, 0, sizeof(struct efi_capsule_params));
> > > > > > +     }
> > > > > > +}
> > > > > > --
> > > > > > 2.34.1
> > > > > >

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 4/7] tools: mkeficapsule: Add support for parsing capsule params from config file
  2023-06-16  4:26             ` Sughosh Ganu
@ 2023-06-16  4:46               ` Takahiro Akashi
  2023-06-16  5:07                 ` Sughosh Ganu
  0 siblings, 1 reply; 50+ messages in thread
From: Takahiro Akashi @ 2023-06-16  4:46 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Simon Glass, Michal Simek

Hi Sughosh,

On Fri, Jun 16, 2023 at 09:56:33AM +0530, Sughosh Ganu wrote:
> On Thu, 15 Jun 2023 at 11:19, Takahiro Akashi
> <takahiro.akashi@linaro.org> wrote:
> >
> > On Thu, Jun 15, 2023 at 10:09:06AM +0530, Sughosh Ganu wrote:
> > > On Wed, 14 Jun 2023 at 11:23, Takahiro Akashi
> > > <takahiro.akashi@linaro.org> wrote:
> > > >
> > > > On Wed, Jun 14, 2023 at 10:56:23AM +0530, Sughosh Ganu wrote:
> > > > > hi Takahiro,
> > > > >
> > > > > On Wed, 14 Jun 2023 at 09:09, Takahiro Akashi
> > > > > <takahiro.akashi@linaro.org> wrote:
> > > > > >
> > > > > > Hi Sughosh,
> > > > > >
> > > > > > I think this is a good extension to mkeficapsule, but
> > > > > >
> > > > > > On Tue, Jun 13, 2023 at 04:08:03PM +0530, Sughosh Ganu wrote:
> > > > > > > Add support for specifying the parameters needed for capsule
> > > > > > > generation through a config file, instead of passing them through
> > > > > > > command-line. Parameters for more than a single capsule file can be
> > > > > > > specified, resulting in generation of multiple capsules through a
> > > > > > > single invocation of the command.
> > > > > > >
> > > > > > > This path is to be used for generating capsules through a make target,
> > > > > > > with the parameters being parsed from the config file.
> > > > > > >
> > > > > > > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > > > > > > ---
> > > > > > >  tools/Kconfig              |   9 +
> > > > > > >  tools/Makefile             |   1 +
> > > > > > >  tools/eficapsule.h         | 110 ++++++++++++
> > > > > > >  tools/mkeficapsule.c       | 106 +++++++-----
> > > > > > >  tools/mkeficapsule_parse.c | 345 +++++++++++++++++++++++++++++++++++++
> > > > > > >  5 files changed, 531 insertions(+), 40 deletions(-)
> > > > > > >  create mode 100644 tools/mkeficapsule_parse.c
> > > > > > >
> > > > > > > diff --git a/tools/Kconfig b/tools/Kconfig
> > > > > > > index 539708f277..95f27b7c45 100644
> > > > > > > --- a/tools/Kconfig
> > > > > > > +++ b/tools/Kconfig
> > > > > > > @@ -98,6 +98,15 @@ config TOOLS_MKEFICAPSULE
> > > > > > >         optionally sign that file. If you want to enable UEFI capsule
> > > > > > >         update feature on your target, you certainly need this.
> > > > > > >
> > > > > > > +config EFI_CAPSULE_CFG_FILE
> > > > > > > +     string "Path to the EFI Capsule Config File"
> > > > > > > +     default ""
> > > > > > > +     help
> > > > > > > +       Path to the EFI capsule config file which provides the
> > > > > > > +       parameters needed to build capsule(s). Parameters can be
> > > > > > > +       provided for multiple payloads resulting in corresponding
> > > > > > > +       capsule images being generated.
> > > > > > > +
> > > > > > >  menuconfig FSPI_CONF_HEADER
> > > > > > >       bool "FlexSPI Header Configuration"
> > > > > > >       help
> > > > > > > diff --git a/tools/Makefile b/tools/Makefile
> > > > > > > index d793cf3bec..ef366f3d61 100644
> > > > > > > --- a/tools/Makefile
> > > > > > > +++ b/tools/Makefile
> > > > > > > @@ -250,6 +250,7 @@ HOSTLDLIBS_mkeficapsule += \
> > > > > > >  HOSTLDLIBS_mkeficapsule += \
> > > > > > >       $(shell pkg-config --libs uuid 2> /dev/null || echo "-luuid")
> > > > > > >  hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule
> > > > > > > +mkeficapsule-objs := mkeficapsule.o mkeficapsule_parse.o
> > > > > > >
> > > > > > >  # We build some files with extra pedantic flags to try to minimize things
> > > > > > >  # that won't build on some weird host compiler -- though there are lots of
> > > > > > > diff --git a/tools/eficapsule.h b/tools/eficapsule.h
> > > > > > > index 072a4b5598..42e66c6d6a 100644
> > > > > > > --- a/tools/eficapsule.h
> > > > > > > +++ b/tools/eficapsule.h
> > > > > > > @@ -52,6 +52,38 @@ typedef struct {
> > > > > > >  /* flags */
> > > > > > >  #define CAPSULE_FLAGS_PERSIST_ACROSS_RESET      0x00010000
> > > > > > >
> > > > > > > +enum capsule_type {
> > > > > > > +     CAPSULE_NORMAL_BLOB = 0,
> > > > > > > +     CAPSULE_ACCEPT,
> > > > > > > +     CAPSULE_REVERT,
> > > > > > > +};
> > > > > > > +
> > > > > > > +/**
> > > > > > > + * struct efi_capsule_params - Capsule parameters
> > > > > > > + * @image_guid: Guid value of the payload input image
> > > > > > > + * @image_index: Image index value
> > > > > > > + * @hardware_instance: Hardware instance to be used for the image
> > > > > > > + * @monotonic_count: Monotonic count value to be used for signed capsule
> > > > > > > + * @privkey_file: Path to private key used in capsule signing
> > > > > > > + * @cert_file: Path to public key certificate used in capsule signing
> > > > > > > + * @input_file: Path to payload input image
> > > > > > > + * @capsule_file: Path to the output capsule file
> > > > > > > + * @oemflags: Oemflags to be populated in the capsule header
> > > > > > > + * @capsule: Capsule Type, normal or accept or revert
> > > > > > > + */
> > > > > > > +struct efi_capsule_params {
> > > > > > > +     efi_guid_t *image_guid;
> > > > > > > +     unsigned long image_index;
> > > > > > > +     unsigned long hardware_instance;
> > > > > > > +     uint64_t monotonic_count;
> > > > > > > +     char *privkey_file;
> > > > > > > +     char *cert_file;
> > > > > > > +     char *input_file;
> > > > > > > +     char *capsule_file;
> > > > > > > +     unsigned long oemflags;
> > > > > > > +     enum capsule_type capsule;
> > > > > > > +};
> > > > > > > +
> > > > > > >  struct efi_capsule_header {
> > > > > > >       efi_guid_t capsule_guid;
> > > > > > >       uint32_t header_size;
> > > > > > > @@ -113,4 +145,82 @@ struct efi_firmware_image_authentication {
> > > > > > >       struct win_certificate_uefi_guid auth_info;
> > > > > > >  } __packed;
> > > > > > >
> > > > > > > +/**
> > > > > > > + * capsule_with_cfg_file() - Generate capsule from config file
> > > > > > > + * @cfg_file: Path to the config file
> > > > > > > + *
> > > > > > > + * Parse the capsule parameters from the config file and use the
> > > > > > > + * parameters for generating one or more capsules.
> > > > > > > + *
> > > > > > > + * Return: None
> > > > > > > + *
> > > > > > > + */
> > > > > > > +void capsule_with_cfg_file(const char *cfg_file);
> > > > > > > +
> > > > > > > +/**
> > > > > > > + * convert_uuid_to_guid() - convert UUID to GUID
> > > > > > > + * @buf:     UUID binary
> > > > > > > + *
> > > > > > > + * UUID and GUID have the same data structure, but their binary
> > > > > > > + * formats are different due to the endianness. See lib/uuid.c.
> > > > > > > + * Since uuid_parse() can handle only UUID, this function must
> > > > > > > + * be called to get correct data for GUID when parsing a string.
> > > > > > > + *
> > > > > > > + * The correct data will be returned in @buf.
> > > > > > > + */
> > > > > > > +void convert_uuid_to_guid(unsigned char *buf);
> > > > > > > +
> > > > > > > +/**
> > > > > > > + * create_empty_capsule() - Generate an empty capsule
> > > > > > > + * @path: Path to the empty capsule file to be generated
> > > > > > > + * @guid: Guid value of the image for which empty capsule is generated
> > > > > > > + * @fw_accept: Flag to specify whether to generate accept or revert capsule
> > > > > > > + *
> > > > > > > + * Generate an empty capsule, either an accept or a revert capsule to be
> > > > > > > + * used to flag acceptance or rejection of an earlier executed firmware
> > > > > > > + * update operation. Being used in the FWU Multi Bank firmware update
> > > > > > > + * feature.
> > > > > > > + *
> > > > > > > + * Return: 0 if OK, -ve on error
> > > > > > > + *
> > > > > > > + */
> > > > > > > +int create_empty_capsule(char *path, efi_guid_t *guid, bool fw_accept);
> > > > > > > +
> > > > > > > +/**
> > > > > > > + * create_fwbin - create an uefi capsule file
> > > > > > > + * @path:    Path to a created capsule file
> > > > > > > + * @bin:     Path to a firmware binary to encapsulate
> > > > > > > + * @guid:    GUID of related FMP driver
> > > > > > > + * @index:   Index number in capsule
> > > > > > > + * @instance:        Instance number in capsule
> > > > > > > + * @mcount:  Monotonic count in authentication information
> > > > > > > + * @private_file:    Path to a private key file
> > > > > > > + * @cert_file:       Path to a certificate file
> > > > > > > + * @oemflags:  Capsule OEM Flags, bits 0-15
> > > > > > > + *
> > > > > > > + * This function actually does the job of creating an uefi capsule file.
> > > > > > > + * All the arguments must be supplied.
> > > > > > > + * If either @private_file ror @cert_file is NULL, the capsule file
> > > > > > > + * won't be signed.
> > > > > > > + *
> > > > > > > + * Return:
> > > > > > > + * * 0  - on success
> > > > > > > + * * -1 - on failure
> > > > > > > + */
> > > > > > > +int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > > > > +              unsigned long index, unsigned long instance,
> > > > > > > +              uint64_t mcount, char *privkey_file, char *cert_file,
> > > > > > > +              uint16_t oemflags);
> > > > > > > +
> > > > > > > +/**
> > > > > > > + * print_usage() - Print the command usage string
> > > > > > > + *
> > > > > > > + * Prints the standard command usage string. Called in the case
> > > > > > > + * of incorrect parameters being passed to the tool.
> > > > > > > + *
> > > > > > > + * Return: None
> > > > > > > + *
> > > > > > > + */
> > > > > > > +void print_usage(void);
> > > > > > > +
> > > > > > >  #endif /* _EFI_CAPSULE_H */
> > > > > > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> > > > > > > index b71537beee..711adf0439 100644
> > > > > > > --- a/tools/mkeficapsule.c
> > > > > > > +++ b/tools/mkeficapsule.c
> > > > > > > @@ -31,12 +31,6 @@ 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 struct option options[] = {
> > > > > > >       {"guid", required_argument, NULL, 'g'},
> > > > > > >       {"index", required_argument, NULL, 'i'},
> > > > > > > @@ -52,7 +46,16 @@ static struct option options[] = {
> > > > > > >       {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"
> > > > > > > @@ -400,10 +403,10 @@ static void free_sig_data(struct auth_context *ctx)
> > > > > > >   * * 0  - on success
> > > > > > >   * * -1 - on failure
> > > > > > >   */
> > > > > > > -static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > > > > -                     unsigned long index, unsigned long instance,
> > > > > > > -                     uint64_t mcount, char *privkey_file, char *cert_file,
> > > > > > > -                     uint16_t oemflags)
> > > > > > > +int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > > > > +              unsigned long index, unsigned long instance,
> > > > > > > +              uint64_t mcount, char *privkey_file, char *cert_file,
> > > > > > > +              uint16_t oemflags)
> > > > > > >  {
> > > > > > >       struct efi_capsule_header header;
> > > > > > >       struct efi_firmware_management_capsule_header capsule;
> > > > > > > @@ -580,7 +583,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;
> > > > > > > @@ -623,19 +640,7 @@ err:
> > > > > > >       return ret;
> > > > > > >  }
> > > > > > >
> > > > > > > -/**
> > > > > > > - * main - main entry function of mkeficapsule
> > > > > > > - * @argc:    Number of arguments
> > > > > > > - * @argv:    Array of pointers to arguments
> > > > > > > - *
> > > > > > > - * Create an uefi capsule file, optionally signing it.
> > > > > > > - * Parse all the arguments and pass them on to create_fwbin().
> > > > > > > - *
> > > > > > > - * Return:
> > > > > > > - * * 0  - on success
> > > > > > > - * * -1 - on failure
> > > > > > > - */
> > > > > > > -int main(int argc, char **argv)
> > > > > > > +static void capsule_with_cmdline_params(int argc, char **argv)
> > > > > > >  {
> > > > > > >       efi_guid_t *guid;
> > > > > > >       unsigned char uuid_buf[16];
> > > > > > > @@ -643,6 +648,7 @@ int main(int argc, char **argv)
> > > > > > >       uint64_t mcount;
> > > > > > >       unsigned long oemflags;
> > > > > > >       char *privkey_file, *cert_file;
> > > > > > > +     enum capsule_type capsule;
> > > > > > >       int c, idx;
> > > > > > >
> > > > > > >       guid = NULL;
> > > > > > > @@ -652,7 +658,7 @@ int main(int argc, char **argv)
> > > > > > >       privkey_file = NULL;
> > > > > > >       cert_file = NULL;
> > > > > > >       dump_sig = 0;
> > > > > > > -     capsule_type = CAPSULE_NORMAL_BLOB;
> > > > > > > +     capsule = CAPSULE_NORMAL_BLOB;
> > > > > > >       oemflags = 0;
> > > > > > >       for (;;) {
> > > > > > >               c = getopt_long(argc, argv, opts_short, options, &idx);
> > > > > > > @@ -702,20 +708,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);
> > > > > > > @@ -732,21 +738,21 @@ int main(int argc, char **argv)
> > > > > > >       }
> > > > > > >
> > > > > > >       /* check necessary parameters */
> > > > > > > -     if ((capsule_type == CAPSULE_NORMAL_BLOB &&
> > > > > > > -         ((argc != optind + 2) || !guid ||
> > > > > > > -          ((privkey_file && !cert_file) ||
> > > > > > > -           (!privkey_file && cert_file)))) ||
> > > > > > > -         (capsule_type != CAPSULE_NORMAL_BLOB &&
> > > > > > > -         ((argc != optind + 1) ||
> > > > > > > -          ((capsule_type == CAPSULE_ACCEPT) && !guid) ||
> > > > > > > -          ((capsule_type == CAPSULE_REVERT) && guid)))) {
> > > > > > > +     if ((capsule == CAPSULE_NORMAL_BLOB &&
> > > > > > > +          ((argc != optind + 2) || !guid ||
> > > > > > > +           ((privkey_file && !cert_file) ||
> > > > > > > +            (!privkey_file && cert_file)))) ||
> > > > > > > +         (capsule != CAPSULE_NORMAL_BLOB &&
> > > > > > > +          ((argc != optind + 1) ||
> > > > > > > +           (capsule == CAPSULE_ACCEPT && !guid) ||
> > > > > > > +           (capsule == CAPSULE_REVERT && guid)))) {
> > > > > > >               print_usage();
> > > > > > >               exit(EXIT_FAILURE);
> > > > > > >       }
> > > > > > >
> > > > > > > -     if (capsule_type != CAPSULE_NORMAL_BLOB) {
> > > > > > > +     if (capsule != CAPSULE_NORMAL_BLOB) {
> > > > > > >               if (create_empty_capsule(argv[argc - 1], guid,
> > > > > > > -                                      capsule_type == CAPSULE_ACCEPT) < 0) {
> > > > > > > +                                      capsule == CAPSULE_ACCEPT) < 0) {
> > > > > > >                       fprintf(stderr, "Creating empty capsule failed\n");
> > > > > > >                       exit(EXIT_FAILURE);
> > > > > > >               }
> > > > > > > @@ -756,6 +762,26 @@ int main(int argc, char **argv)
> > > > > > >               fprintf(stderr, "Creating firmware capsule failed\n");
> > > > > > >               exit(EXIT_FAILURE);
> > > > > > >       }
> > > > > > > +}
> > > > > > > +
> > > > > > > +/**
> > > > > > > + * main - main entry function of mkeficapsule
> > > > > > > + * @argc:    Number of arguments
> > > > > > > + * @argv:    Array of pointers to arguments
> > > > > > > + *
> > > > > > > + * Create an uefi capsule file, optionally signing it.
> > > > > > > + * Parse all the arguments and pass them on to create_fwbin().
> > > > > > > + *
> > > > > > > + * Return:
> > > > > > > + * * 0  - on success
> > > > > > > + * * -1 - on failure
> > > > > > > + */
> > > > > > > +int main(int argc, char **argv)
> > > > > > > +{
> > > > > > > +     if (!strcmp(CONFIG_EFI_CAPSULE_CFG_FILE, ""))
> > > > > > > +             capsule_with_cmdline_params(argc, argv);
> > > > > > > +     else
> > > > > > > +             capsule_with_cfg_file(CONFIG_EFI_CAPSULE_CFG_FILE);
> > > > > >
> > > > > > I don't know where the macro, CONFIG_EFI_CAPSULE_CFG_FILE, comes from.
> > > > > > Anyhow, as a general rule, any host tool must be as generic as it should not
> > > > > > depend on a target's config.
> > > > > > (I was told so before.)
> > > > > >
> > > > > > So I would suggest that you add another command line, say "--config-file <file>",
> > > > > > to make the command generic.
> > > > >
> > > > > Yes, that would be something followed by most of the tools. The reason
> > > > > I did not add a command-line option for the confile file is because I
> > > > > want the capsule generation added as a make target. With the path to
> > > > > the config file specified through the Kconfig symbol, we can invoke
> > > > > 'make capsule', and it would build the capsules by parsing the
> > > > > parameters from the config file, taken from the Kconfig symbol. I know
> > > > > there are ways of specifying options when using a make command, but I
> > > > > don't think that is a clean way of doing things.
> > > >
> > > > Not sure, but in your [5/7],
> > > > cmd_mkeficapsule = $(objtree)/tools/mkeficapsule --config-file $(CONFIG_EFI_CAPSULE_CFG_FILE)
> > > >
> > > > Doesn't this change work?
> > >
> > > So, I tried the above suggested change. But trying to run a make
> > > 'target' does not work without the .config file being present.
> >
> > Not sure what you meant to say here.
> > Why don't you have .config when building U-Boot (or rather 'target')?
> 
> Maybe I misunderstood your earlier comment, but I thought you were
> looking to build capsules without relying on a target config.

Not exactly.
The basic requirement, I believe, is that the exact same binary (with
the same set of functionalities) should be generated for any host tool
whatever a target's config, including tools-only_defconfig, is.

Is it clear now?

-Takahiro Akashi

> Which I
> believe cannot be done for a make target.
> 
> -sughosh
> 
> >
> > -Takahiro Akashi
> >
> > > FWIW,
> > > the same is the case for building tools as well. I think that is the
> > > reason for the tools-only_defconfig.
> > >
> > > -sughosh
> > >
> > > >
> > > > -Takahiro Akashi
> > > >
> > > >
> > > > > Given the use case of
> > > > > a make target, I hope we can use the Kconfig symbol for specifying the
> > > > > config file path.
> > > > >
> > > > > -sughosh
> > > > >
> > > > > >
> > > > > > -Takahiro Akashi
> > > > > >
> > > > > >
> > > > > > >
> > > > > > >       exit(EXIT_SUCCESS);
> > > > > > >  }
> > > > > > > diff --git a/tools/mkeficapsule_parse.c b/tools/mkeficapsule_parse.c
> > > > > > > new file mode 100644
> > > > > > > index 0000000000..ef4f3f6705
> > > > > > > --- /dev/null
> > > > > > > +++ b/tools/mkeficapsule_parse.c
> > > > > > > @@ -0,0 +1,345 @@
> > > > > > > +// SPDX-License-Identifier: GPL-2.0
> > > > > > > +/*
> > > > > > > + * Copyright 2023 Linaro Limited
> > > > > > > + */
> > > > > > > +
> > > > > > > +/*
> > > > > > > + * The code in this file adds parsing ability to the mkeficapsule
> > > > > > > + * tool. This allows specifying parameters needed to build the capsule
> > > > > > > + * through the config file instead of specifying them on the command-line.
> > > > > > > + * Parameters can be specified for more than one payload, generating the
> > > > > > > + * corresponding capsule files.
> > > > > > > + *
> > > > > > > + * The parameters are specified in a "key:value" pair. All the parameters
> > > > > > > + * that are currently supported by the mkeficapsule tool can be specified
> > > > > > > + * in the config file.
> > > > > > > + *
> > > > > > > + * The example below shows four payloads. The first payload is an example
> > > > > > > + * of generating a signed capsule. The second payload is an example of
> > > > > > > + * generating an unsigned capsule. The third payload is an accept empty
> > > > > > > + * capsule, while the fourth payload is the revert empty capsule, used
> > > > > > > + * for the multi-bank firmware update feature.
> > > > > > > + *
> > > > > > > + * This functionality can be easily extended to generate a single capsule
> > > > > > > + * comprising multiple payloads.
> > > > > > > +
> > > > > > > +     {
> > > > > > > +         image-guid: 02f4d760-cfd5-43bd-8e2d-a42acb33c660
> > > > > > > +         hardware-instance: 0
> > > > > > > +         monotonic-count: 1
> > > > > > > +         payload: u-boot.bin
> > > > > > > +         image-index: 1
> > > > > > > +         private-key: /path/to/priv/key
> > > > > > > +         pub-key-cert: /path/to/pub/key
> > > > > > > +         capsule: u-boot.capsule
> > > > > > > +     }
> > > > > > > +     {
> > > > > > > +         image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e
> > > > > > > +         hardware-instance: 0
> > > > > > > +         payload: u-boot.itb
> > > > > > > +         image-index: 2
> > > > > > > +         oemflags: 0x8000
> > > > > > > +         capsule: fit.capsule
> > > > > > > +     }
> > > > > > > +     {
> > > > > > > +         capsule-type: accept
> > > > > > > +         image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e
> > > > > > > +         capsule: accept.capsule
> > > > > > > +     }
> > > > > > > +     {
> > > > > > > +         capsule-type: revert
> > > > > > > +         capsule: revert.capsule
> > > > > > > +     }
> > > > > > > +*/
> > > > > > > +
> > > > > > > +#include <ctype.h>
> > > > > > > +#include <limits.h>
> > > > > > > +#include <stdio.h>
> > > > > > > +#include <stdlib.h>
> > > > > > > +#include <string.h>
> > > > > > > +
> > > > > > > +#include <uuid/uuid.h>
> > > > > > > +
> > > > > > > +#include "eficapsule.h"
> > > > > > > +
> > > > > > > +#define PARAMS_START "{"
> > > > > > > +#define PARAMS_END   "}"
> > > > > > > +
> > > > > > > +#define PSTART               2
> > > > > > > +#define PEND         3
> > > > > > > +
> > > > > > > +#define MALLOC_FAIL_STR              "Unable to allocate memory\n"
> > > > > > > +
> > > > > > > +#define ARRAY_SIZE(x)                (sizeof(x) / sizeof((x)[0]))
> > > > > > > +
> > > > > > > +const char *capsule_params[] = {
> > > > > > > +     "image-guid", "image-index", "private-key",
> > > > > > > +     "pub-key-cert", "payload", "capsule",
> > > > > > > +     "hardware-instance", "monotonic-count",
> > > > > > > +     "capsule-type", "oemflags" };
> > > > > > > +
> > > > > > > +static unsigned char params_start;
> > > > > > > +static unsigned char params_end;
> > > > > > > +
> > > > > > > +static void print_and_exit(const char *str)
> > > > > > > +{
> > > > > > > +     fprintf(stderr, "%s", str);
> > > > > > > +     exit(EXIT_FAILURE);
> > > > > > > +}
> > > > > > > +
> > > > > > > +static int param_delim_checks(char *line, unsigned char *token)
> > > > > > > +{
> > > > > > > +     if (!strcmp(line, PARAMS_START)) {
> > > > > > > +             if (params_start || !params_end) {
> > > > > > > +                     fprintf(stderr, "Earlier params processing still in progress. ");
> > > > > > > +                     fprintf(stderr, "Can't start processing a new params.\n");
> > > > > > > +                     exit(EXIT_FAILURE);
> > > > > > > +             } else {
> > > > > > > +                     params_start = 1;
> > > > > > > +                     params_end = 0;
> > > > > > > +                     *token = PSTART;
> > > > > > > +                     return 1;
> > > > > > > +             }
> > > > > > > +     } else if (!strcmp(line, PARAMS_END)) {
> > > > > > > +             if (!params_start) {
> > > > > > > +                     fprintf(stderr, "Cannot put end braces without start braces. ");
> > > > > > > +                     fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > > > > +                     exit(EXIT_FAILURE);
> > > > > > > +             } else {
> > > > > > > +                     params_start = 0;
> > > > > > > +                     params_end = 1;
> > > > > > > +                     *token = PEND;
> > > > > > > +                     return 1;
> > > > > > > +             }
> > > > > > > +     } else if (!params_start) {
> > > > > > > +             fprintf(stderr, "Params should be passed within braces. ");
> > > > > > > +             fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > > > > +             exit(EXIT_FAILURE);
> > > > > > > +     }
> > > > > > > +
> > > > > > > +     return 0;
> > > > > > > +}
> > > > > > > +
> > > > > > > +static void add_guid(efi_guid_t **guid_param, char *guid)
> > > > > > > +{
> > > > > > > +     unsigned char uuid_buf[16];
> > > > > > > +
> > > > > > > +     *guid_param = malloc(sizeof(efi_guid_t));
> > > > > > > +     if (!*guid_param)
> > > > > > > +             print_and_exit(MALLOC_FAIL_STR);
> > > > > > > +
> > > > > > > +     if (uuid_parse(guid, uuid_buf))
> > > > > > > +             print_and_exit("Wrong guid format\n");
> > > > > > > +
> > > > > > > +     convert_uuid_to_guid(uuid_buf);
> > > > > > > +     memcpy(*guid_param, uuid_buf, sizeof(efi_guid_t));
> > > > > > > +}
> > > > > > > +
> > > > > > > +static void add_string(char **dst, char *val)
> > > > > > > +{
> > > > > > > +     *dst = strdup(val);
> > > > > > > +     if (!*dst)
> > > > > > > +             print_and_exit(MALLOC_FAIL_STR);
> > > > > > > +}
> > > > > > > +
> > > > > > > +static void match_and_populate_param(char *key, char *val,
> > > > > > > +                                  struct efi_capsule_params *param)
> > > > > > > +{
> > > > > > > +     int i;
> > > > > > > +
> > > > > > > +     for (i = 0; i < ARRAY_SIZE(capsule_params); i++) {
> > > > > > > +             if (!strcmp(key, capsule_params[i])) {
> > > > > > > +                     switch (i) {
> > > > > > > +                     case 0:
> > > > > > > +                             add_guid(&param->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(&param->privkey_file, val);
> > > > > > > +                             return;
> > > > > > > +                     case 3:
> > > > > > > +                             add_string(&param->cert_file, val);
> > > > > > > +                             return;
> > > > > > > +                     case 4:
> > > > > > > +                             add_string(&param->input_file, val);
> > > > > > > +                             return;
> > > > > > > +                     case 5:
> > > > > > > +                             add_string(&param->capsule_file, val);
> > > > > > > +                             return;
> > > > > > > +                     case 6:
> > > > > > > +                             param->hardware_instance = strtoul(val, NULL, 0);
> > > > > > > +                             if (param->hardware_instance == ULONG_MAX)
> > > > > > > +                                     print_and_exit("Enter a valid hardware instance value");
> > > > > > > +                             return;
> > > > > > > +                     case 7:
> > > > > > > +                             param->monotonic_count = strtoull(val, NULL, 0);
> > > > > > > +                             if (param->monotonic_count == ULLONG_MAX)
> > > > > > > +                                     print_and_exit("Enter a valid monotonic count value");
> > > > > > > +                             return;
> > > > > > > +                     case 8:
> > > > > > > +                             if (!strcmp(val, "normal"))
> > > > > > > +                                     param->capsule = CAPSULE_NORMAL_BLOB;
> > > > > > > +                             else if (!strcmp(val, "accept"))
> > > > > > > +                                     param->capsule = CAPSULE_ACCEPT;
> > > > > > > +                             else if (!strcmp(val, "revert"))
> > > > > > > +                                     param->capsule = CAPSULE_REVERT;
> > > > > > > +                             else
> > > > > > > +                                     print_and_exit("Invalid type of capsule");
> > > > > > > +
> > > > > > > +                             return;
> > > > > > > +                     case 9:
> > > > > > > +                             param->oemflags = strtoul(val, NULL, 0);
> > > > > > > +                             if (param->oemflags > 0xffff)
> > > > > > > +                                     print_and_exit("OemFlags must be between 0x0 and 0xffff\n");
> > > > > > > +                             return;
> > > > > > > +                     }
> > > > > > > +             }
> > > > > > > +     }
> > > > > > > +
> > > > > > > +     fprintf(stderr, "Undefined param %s specified. ", key);
> > > > > > > +     fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > > > > +     exit(EXIT_FAILURE);
> > > > > > > +}
> > > > > > > +
> > > > > > > +static int get_capsule_params(char *line, struct efi_capsule_params *params)
> > > > > > > +{
> > > > > > > +     char *key = NULL;
> > > > > > > +     char *val = NULL;
> > > > > > > +     unsigned char token;
> > > > > > > +
> > > > > > > +     if (param_delim_checks(line, &token))
> > > > > > > +             return token;
> > > > > > > +
> > > > > > > +     key = strtok(line, ":");
> > > > > > > +     if (key)
> > > > > > > +             val = strtok(NULL, "\0");
> > > > > > > +     else
> > > > > > > +             print_and_exit("Expect the params in a key:value pair\n");
> > > > > > > +
> > > > > > > +     match_and_populate_param(key, val, params);
> > > > > > > +
> > > > > > > +     return 0;
> > > > > > > +}
> > > > > > > +
> > > > > > > +static char *skip_whitespace(char *line)
> > > > > > > +{
> > > > > > > +     char *ptr, *newline;
> > > > > > > +
> > > > > > > +     ptr = malloc(strlen(line) + 1);
> > > > > > > +     if (!ptr)
> > > > > > > +             print_and_exit(MALLOC_FAIL_STR);
> > > > > > > +
> > > > > > > +     for (newline = ptr; *line; line++)
> > > > > > > +             if (!isblank(*line))
> > > > > > > +                     *ptr++ = *line;
> > > > > > > +     *ptr = '\0';
> > > > > > > +     return newline;
> > > > > > > +}
> > > > > > > +
> > > > > > > +static int parse_capsule_payload_params(FILE *fp, struct efi_capsule_params *params)
> > > > > > > +{
> > > > > > > +     char *line = NULL;
> > > > > > > +     char *newline;
> > > > > > > +     size_t n = 0;
> > > > > > > +     ssize_t len;
> > > > > > > +
> > > > > > > +     while ((len = getline(&line, &n, fp)) != -1) {
> > > > > > > +             if (len == 1 && line[len - 1] == '\n')
> > > > > > > +                     continue;
> > > > > > > +
> > > > > > > +             line[len - 1] = '\0';
> > > > > > > +
> > > > > > > +             newline = skip_whitespace(line);
> > > > > > > +
> > > > > > > +             if (newline[0] == '#')
> > > > > > > +                     continue;
> > > > > > > +
> > > > > > > +             if (get_capsule_params(newline, params) == PEND)
> > > > > > > +                     return 0;
> > > > > > > +     }
> > > > > > > +
> > > > > > > +     if (errno == EINVAL || errno == ENOMEM) {
> > > > > > > +             fprintf(stderr, "getline() returned an error %s reading the line\n",
> > > > > > > +                     strerror(errno));
> > > > > > > +             exit(EXIT_FAILURE);
> > > > > > > +     } else if (params_start == 1 || params_end == 0) {
> > > > > > > +             fprintf(stderr, "Params should be passed within braces. ");
> > > > > > > +             fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > > > > +             exit(EXIT_FAILURE);
> > > > > > > +     } else {
> > > > > > > +             return -1;
> > > > > > > +     }
> > > > > > > +}
> > > > > > > +
> > > > > > > +static void params_dependency_check(struct efi_capsule_params *params)
> > > > > > > +{
> > > > > > > +     /* check necessary parameters */
> > > > > > > +     if ((params->capsule == CAPSULE_NORMAL_BLOB &&
> > > > > > > +          ((!params->input_file || !params->capsule_file ||
> > > > > > > +            !params->image_guid) ||
> > > > > > > +           ((params->privkey_file && !params->cert_file) ||
> > > > > > > +            (!params->privkey_file && params->cert_file)))) ||
> > > > > > > +         (params->capsule != CAPSULE_NORMAL_BLOB &&
> > > > > > > +          (!params->capsule_file ||
> > > > > > > +           (params->capsule == CAPSULE_ACCEPT && !params->image_guid) ||
> > > > > > > +           (params->capsule == CAPSULE_REVERT && params->image_guid)))) {
> > > > > > > +             print_usage();
> > > > > > > +             exit(EXIT_FAILURE);
> > > > > > > +     }
> > > > > > > +}
> > > > > > > +
> > > > > > > +static void generate_capsule(struct efi_capsule_params *params)
> > > > > > > +{
> > > > > > > +     if (params->capsule != CAPSULE_NORMAL_BLOB) {
> > > > > > > +             if (create_empty_capsule(params->capsule_file,
> > > > > > > +                                      params->image_guid,
> > > > > > > +                                      params->capsule ==
> > > > > > > +                                      CAPSULE_ACCEPT) < 0)
> > > > > > > +                     print_and_exit("Creating empty capsule failed\n");
> > > > > > > +     } else if (create_fwbin(params->capsule_file, params->input_file,
> > > > > > > +                           params->image_guid, params->image_index,
> > > > > > > +                           params->hardware_instance,
> > > > > > > +                           params->monotonic_count,
> > > > > > > +                           params->privkey_file,
> > > > > > > +                           params->cert_file,
> > > > > > > +                           (uint16_t)params->oemflags) < 0) {
> > > > > > > +             print_and_exit("Creating firmware capsule failed\n");
> > > > > > > +     }
> > > > > > > +}
> > > > > > > +
> > > > > > > +/**
> > > > > > > + * capsule_with_cfg_file() - Generate capsule from config file
> > > > > > > + * @cfg_file: Path to the config file
> > > > > > > + *
> > > > > > > + * Parse the capsule parameters from the config file and use the
> > > > > > > + * parameters for generating one or more capsules.
> > > > > > > + *
> > > > > > > + * Return: None
> > > > > > > + *
> > > > > > > + */
> > > > > > > +void capsule_with_cfg_file(const char *cfg_file)
> > > > > > > +{
> > > > > > > +     FILE *fp;
> > > > > > > +     struct efi_capsule_params params = { 0 };
> > > > > > > +
> > > > > > > +     fp = fopen(cfg_file, "r");
> > > > > > > +     if (!fp) {
> > > > > > > +             fprintf(stderr, "Unable to open the capsule config file %s\n",
> > > > > > > +                     cfg_file);
> > > > > > > +             exit(EXIT_FAILURE);
> > > > > > > +     }
> > > > > > > +
> > > > > > > +     params_start = 0;
> > > > > > > +     params_end = 1;
> > > > > > > +
> > > > > > > +     while (parse_capsule_payload_params(fp, &params) != -1) {
> > > > > > > +             params_dependency_check(&params);
> > > > > > > +             generate_capsule(&params);
> > > > > > > +
> > > > > > > +             memset(&params, 0, sizeof(struct efi_capsule_params));
> > > > > > > +     }
> > > > > > > +}
> > > > > > > --
> > > > > > > 2.34.1
> > > > > > >

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 4/7] tools: mkeficapsule: Add support for parsing capsule params from config file
  2023-06-16  4:46               ` Takahiro Akashi
@ 2023-06-16  5:07                 ` Sughosh Ganu
  2023-06-16  5:18                   ` Takahiro Akashi
  0 siblings, 1 reply; 50+ messages in thread
From: Sughosh Ganu @ 2023-06-16  5:07 UTC (permalink / raw)
  To: Takahiro Akashi, Sughosh Ganu, u-boot, Heinrich Schuchardt,
	Ilias Apalodimas, Simon Glass, Michal Simek

hi Takahiro,

On Fri, 16 Jun 2023 at 10:16, Takahiro Akashi
<takahiro.akashi@linaro.org> wrote:
>
> Hi Sughosh,
>
> On Fri, Jun 16, 2023 at 09:56:33AM +0530, Sughosh Ganu wrote:
> > On Thu, 15 Jun 2023 at 11:19, Takahiro Akashi
> > <takahiro.akashi@linaro.org> wrote:
> > >
> > > On Thu, Jun 15, 2023 at 10:09:06AM +0530, Sughosh Ganu wrote:
> > > > On Wed, 14 Jun 2023 at 11:23, Takahiro Akashi
> > > > <takahiro.akashi@linaro.org> wrote:
> > > > >
> > > > > On Wed, Jun 14, 2023 at 10:56:23AM +0530, Sughosh Ganu wrote:
> > > > > > hi Takahiro,
> > > > > >
> > > > > > On Wed, 14 Jun 2023 at 09:09, Takahiro Akashi
> > > > > > <takahiro.akashi@linaro.org> wrote:
> > > > > > >
> > > > > > > Hi Sughosh,
> > > > > > >
> > > > > > > I think this is a good extension to mkeficapsule, but
> > > > > > >
> > > > > > > On Tue, Jun 13, 2023 at 04:08:03PM +0530, Sughosh Ganu wrote:
> > > > > > > > Add support for specifying the parameters needed for capsule
> > > > > > > > generation through a config file, instead of passing them through
> > > > > > > > command-line. Parameters for more than a single capsule file can be
> > > > > > > > specified, resulting in generation of multiple capsules through a
> > > > > > > > single invocation of the command.
> > > > > > > >
> > > > > > > > This path is to be used for generating capsules through a make target,
> > > > > > > > with the parameters being parsed from the config file.
> > > > > > > >
> > > > > > > > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > > > > > > > ---
> > > > > > > >  tools/Kconfig              |   9 +
> > > > > > > >  tools/Makefile             |   1 +
> > > > > > > >  tools/eficapsule.h         | 110 ++++++++++++
> > > > > > > >  tools/mkeficapsule.c       | 106 +++++++-----
> > > > > > > >  tools/mkeficapsule_parse.c | 345 +++++++++++++++++++++++++++++++++++++
> > > > > > > >  5 files changed, 531 insertions(+), 40 deletions(-)
> > > > > > > >  create mode 100644 tools/mkeficapsule_parse.c
> > > > > > > >
> > > > > > > > diff --git a/tools/Kconfig b/tools/Kconfig
> > > > > > > > index 539708f277..95f27b7c45 100644
> > > > > > > > --- a/tools/Kconfig
> > > > > > > > +++ b/tools/Kconfig
> > > > > > > > @@ -98,6 +98,15 @@ config TOOLS_MKEFICAPSULE
> > > > > > > >         optionally sign that file. If you want to enable UEFI capsule
> > > > > > > >         update feature on your target, you certainly need this.
> > > > > > > >
> > > > > > > > +config EFI_CAPSULE_CFG_FILE
> > > > > > > > +     string "Path to the EFI Capsule Config File"
> > > > > > > > +     default ""
> > > > > > > > +     help
> > > > > > > > +       Path to the EFI capsule config file which provides the
> > > > > > > > +       parameters needed to build capsule(s). Parameters can be
> > > > > > > > +       provided for multiple payloads resulting in corresponding
> > > > > > > > +       capsule images being generated.
> > > > > > > > +
> > > > > > > >  menuconfig FSPI_CONF_HEADER
> > > > > > > >       bool "FlexSPI Header Configuration"
> > > > > > > >       help
> > > > > > > > diff --git a/tools/Makefile b/tools/Makefile
> > > > > > > > index d793cf3bec..ef366f3d61 100644
> > > > > > > > --- a/tools/Makefile
> > > > > > > > +++ b/tools/Makefile
> > > > > > > > @@ -250,6 +250,7 @@ HOSTLDLIBS_mkeficapsule += \
> > > > > > > >  HOSTLDLIBS_mkeficapsule += \
> > > > > > > >       $(shell pkg-config --libs uuid 2> /dev/null || echo "-luuid")
> > > > > > > >  hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule
> > > > > > > > +mkeficapsule-objs := mkeficapsule.o mkeficapsule_parse.o
> > > > > > > >
> > > > > > > >  # We build some files with extra pedantic flags to try to minimize things
> > > > > > > >  # that won't build on some weird host compiler -- though there are lots of
> > > > > > > > diff --git a/tools/eficapsule.h b/tools/eficapsule.h
> > > > > > > > index 072a4b5598..42e66c6d6a 100644
> > > > > > > > --- a/tools/eficapsule.h
> > > > > > > > +++ b/tools/eficapsule.h
> > > > > > > > @@ -52,6 +52,38 @@ typedef struct {
> > > > > > > >  /* flags */
> > > > > > > >  #define CAPSULE_FLAGS_PERSIST_ACROSS_RESET      0x00010000
> > > > > > > >
> > > > > > > > +enum capsule_type {
> > > > > > > > +     CAPSULE_NORMAL_BLOB = 0,
> > > > > > > > +     CAPSULE_ACCEPT,
> > > > > > > > +     CAPSULE_REVERT,
> > > > > > > > +};
> > > > > > > > +
> > > > > > > > +/**
> > > > > > > > + * struct efi_capsule_params - Capsule parameters
> > > > > > > > + * @image_guid: Guid value of the payload input image
> > > > > > > > + * @image_index: Image index value
> > > > > > > > + * @hardware_instance: Hardware instance to be used for the image
> > > > > > > > + * @monotonic_count: Monotonic count value to be used for signed capsule
> > > > > > > > + * @privkey_file: Path to private key used in capsule signing
> > > > > > > > + * @cert_file: Path to public key certificate used in capsule signing
> > > > > > > > + * @input_file: Path to payload input image
> > > > > > > > + * @capsule_file: Path to the output capsule file
> > > > > > > > + * @oemflags: Oemflags to be populated in the capsule header
> > > > > > > > + * @capsule: Capsule Type, normal or accept or revert
> > > > > > > > + */
> > > > > > > > +struct efi_capsule_params {
> > > > > > > > +     efi_guid_t *image_guid;
> > > > > > > > +     unsigned long image_index;
> > > > > > > > +     unsigned long hardware_instance;
> > > > > > > > +     uint64_t monotonic_count;
> > > > > > > > +     char *privkey_file;
> > > > > > > > +     char *cert_file;
> > > > > > > > +     char *input_file;
> > > > > > > > +     char *capsule_file;
> > > > > > > > +     unsigned long oemflags;
> > > > > > > > +     enum capsule_type capsule;
> > > > > > > > +};
> > > > > > > > +
> > > > > > > >  struct efi_capsule_header {
> > > > > > > >       efi_guid_t capsule_guid;
> > > > > > > >       uint32_t header_size;
> > > > > > > > @@ -113,4 +145,82 @@ struct efi_firmware_image_authentication {
> > > > > > > >       struct win_certificate_uefi_guid auth_info;
> > > > > > > >  } __packed;
> > > > > > > >
> > > > > > > > +/**
> > > > > > > > + * capsule_with_cfg_file() - Generate capsule from config file
> > > > > > > > + * @cfg_file: Path to the config file
> > > > > > > > + *
> > > > > > > > + * Parse the capsule parameters from the config file and use the
> > > > > > > > + * parameters for generating one or more capsules.
> > > > > > > > + *
> > > > > > > > + * Return: None
> > > > > > > > + *
> > > > > > > > + */
> > > > > > > > +void capsule_with_cfg_file(const char *cfg_file);
> > > > > > > > +
> > > > > > > > +/**
> > > > > > > > + * convert_uuid_to_guid() - convert UUID to GUID
> > > > > > > > + * @buf:     UUID binary
> > > > > > > > + *
> > > > > > > > + * UUID and GUID have the same data structure, but their binary
> > > > > > > > + * formats are different due to the endianness. See lib/uuid.c.
> > > > > > > > + * Since uuid_parse() can handle only UUID, this function must
> > > > > > > > + * be called to get correct data for GUID when parsing a string.
> > > > > > > > + *
> > > > > > > > + * The correct data will be returned in @buf.
> > > > > > > > + */
> > > > > > > > +void convert_uuid_to_guid(unsigned char *buf);
> > > > > > > > +
> > > > > > > > +/**
> > > > > > > > + * create_empty_capsule() - Generate an empty capsule
> > > > > > > > + * @path: Path to the empty capsule file to be generated
> > > > > > > > + * @guid: Guid value of the image for which empty capsule is generated
> > > > > > > > + * @fw_accept: Flag to specify whether to generate accept or revert capsule
> > > > > > > > + *
> > > > > > > > + * Generate an empty capsule, either an accept or a revert capsule to be
> > > > > > > > + * used to flag acceptance or rejection of an earlier executed firmware
> > > > > > > > + * update operation. Being used in the FWU Multi Bank firmware update
> > > > > > > > + * feature.
> > > > > > > > + *
> > > > > > > > + * Return: 0 if OK, -ve on error
> > > > > > > > + *
> > > > > > > > + */
> > > > > > > > +int create_empty_capsule(char *path, efi_guid_t *guid, bool fw_accept);
> > > > > > > > +
> > > > > > > > +/**
> > > > > > > > + * create_fwbin - create an uefi capsule file
> > > > > > > > + * @path:    Path to a created capsule file
> > > > > > > > + * @bin:     Path to a firmware binary to encapsulate
> > > > > > > > + * @guid:    GUID of related FMP driver
> > > > > > > > + * @index:   Index number in capsule
> > > > > > > > + * @instance:        Instance number in capsule
> > > > > > > > + * @mcount:  Monotonic count in authentication information
> > > > > > > > + * @private_file:    Path to a private key file
> > > > > > > > + * @cert_file:       Path to a certificate file
> > > > > > > > + * @oemflags:  Capsule OEM Flags, bits 0-15
> > > > > > > > + *
> > > > > > > > + * This function actually does the job of creating an uefi capsule file.
> > > > > > > > + * All the arguments must be supplied.
> > > > > > > > + * If either @private_file ror @cert_file is NULL, the capsule file
> > > > > > > > + * won't be signed.
> > > > > > > > + *
> > > > > > > > + * Return:
> > > > > > > > + * * 0  - on success
> > > > > > > > + * * -1 - on failure
> > > > > > > > + */
> > > > > > > > +int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > > > > > +              unsigned long index, unsigned long instance,
> > > > > > > > +              uint64_t mcount, char *privkey_file, char *cert_file,
> > > > > > > > +              uint16_t oemflags);
> > > > > > > > +
> > > > > > > > +/**
> > > > > > > > + * print_usage() - Print the command usage string
> > > > > > > > + *
> > > > > > > > + * Prints the standard command usage string. Called in the case
> > > > > > > > + * of incorrect parameters being passed to the tool.
> > > > > > > > + *
> > > > > > > > + * Return: None
> > > > > > > > + *
> > > > > > > > + */
> > > > > > > > +void print_usage(void);
> > > > > > > > +
> > > > > > > >  #endif /* _EFI_CAPSULE_H */
> > > > > > > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> > > > > > > > index b71537beee..711adf0439 100644
> > > > > > > > --- a/tools/mkeficapsule.c
> > > > > > > > +++ b/tools/mkeficapsule.c
> > > > > > > > @@ -31,12 +31,6 @@ 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 struct option options[] = {
> > > > > > > >       {"guid", required_argument, NULL, 'g'},
> > > > > > > >       {"index", required_argument, NULL, 'i'},
> > > > > > > > @@ -52,7 +46,16 @@ static struct option options[] = {
> > > > > > > >       {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"
> > > > > > > > @@ -400,10 +403,10 @@ static void free_sig_data(struct auth_context *ctx)
> > > > > > > >   * * 0  - on success
> > > > > > > >   * * -1 - on failure
> > > > > > > >   */
> > > > > > > > -static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > > > > > -                     unsigned long index, unsigned long instance,
> > > > > > > > -                     uint64_t mcount, char *privkey_file, char *cert_file,
> > > > > > > > -                     uint16_t oemflags)
> > > > > > > > +int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > > > > > +              unsigned long index, unsigned long instance,
> > > > > > > > +              uint64_t mcount, char *privkey_file, char *cert_file,
> > > > > > > > +              uint16_t oemflags)
> > > > > > > >  {
> > > > > > > >       struct efi_capsule_header header;
> > > > > > > >       struct efi_firmware_management_capsule_header capsule;
> > > > > > > > @@ -580,7 +583,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;
> > > > > > > > @@ -623,19 +640,7 @@ err:
> > > > > > > >       return ret;
> > > > > > > >  }
> > > > > > > >
> > > > > > > > -/**
> > > > > > > > - * main - main entry function of mkeficapsule
> > > > > > > > - * @argc:    Number of arguments
> > > > > > > > - * @argv:    Array of pointers to arguments
> > > > > > > > - *
> > > > > > > > - * Create an uefi capsule file, optionally signing it.
> > > > > > > > - * Parse all the arguments and pass them on to create_fwbin().
> > > > > > > > - *
> > > > > > > > - * Return:
> > > > > > > > - * * 0  - on success
> > > > > > > > - * * -1 - on failure
> > > > > > > > - */
> > > > > > > > -int main(int argc, char **argv)
> > > > > > > > +static void capsule_with_cmdline_params(int argc, char **argv)
> > > > > > > >  {
> > > > > > > >       efi_guid_t *guid;
> > > > > > > >       unsigned char uuid_buf[16];
> > > > > > > > @@ -643,6 +648,7 @@ int main(int argc, char **argv)
> > > > > > > >       uint64_t mcount;
> > > > > > > >       unsigned long oemflags;
> > > > > > > >       char *privkey_file, *cert_file;
> > > > > > > > +     enum capsule_type capsule;
> > > > > > > >       int c, idx;
> > > > > > > >
> > > > > > > >       guid = NULL;
> > > > > > > > @@ -652,7 +658,7 @@ int main(int argc, char **argv)
> > > > > > > >       privkey_file = NULL;
> > > > > > > >       cert_file = NULL;
> > > > > > > >       dump_sig = 0;
> > > > > > > > -     capsule_type = CAPSULE_NORMAL_BLOB;
> > > > > > > > +     capsule = CAPSULE_NORMAL_BLOB;
> > > > > > > >       oemflags = 0;
> > > > > > > >       for (;;) {
> > > > > > > >               c = getopt_long(argc, argv, opts_short, options, &idx);
> > > > > > > > @@ -702,20 +708,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);
> > > > > > > > @@ -732,21 +738,21 @@ int main(int argc, char **argv)
> > > > > > > >       }
> > > > > > > >
> > > > > > > >       /* check necessary parameters */
> > > > > > > > -     if ((capsule_type == CAPSULE_NORMAL_BLOB &&
> > > > > > > > -         ((argc != optind + 2) || !guid ||
> > > > > > > > -          ((privkey_file && !cert_file) ||
> > > > > > > > -           (!privkey_file && cert_file)))) ||
> > > > > > > > -         (capsule_type != CAPSULE_NORMAL_BLOB &&
> > > > > > > > -         ((argc != optind + 1) ||
> > > > > > > > -          ((capsule_type == CAPSULE_ACCEPT) && !guid) ||
> > > > > > > > -          ((capsule_type == CAPSULE_REVERT) && guid)))) {
> > > > > > > > +     if ((capsule == CAPSULE_NORMAL_BLOB &&
> > > > > > > > +          ((argc != optind + 2) || !guid ||
> > > > > > > > +           ((privkey_file && !cert_file) ||
> > > > > > > > +            (!privkey_file && cert_file)))) ||
> > > > > > > > +         (capsule != CAPSULE_NORMAL_BLOB &&
> > > > > > > > +          ((argc != optind + 1) ||
> > > > > > > > +           (capsule == CAPSULE_ACCEPT && !guid) ||
> > > > > > > > +           (capsule == CAPSULE_REVERT && guid)))) {
> > > > > > > >               print_usage();
> > > > > > > >               exit(EXIT_FAILURE);
> > > > > > > >       }
> > > > > > > >
> > > > > > > > -     if (capsule_type != CAPSULE_NORMAL_BLOB) {
> > > > > > > > +     if (capsule != CAPSULE_NORMAL_BLOB) {
> > > > > > > >               if (create_empty_capsule(argv[argc - 1], guid,
> > > > > > > > -                                      capsule_type == CAPSULE_ACCEPT) < 0) {
> > > > > > > > +                                      capsule == CAPSULE_ACCEPT) < 0) {
> > > > > > > >                       fprintf(stderr, "Creating empty capsule failed\n");
> > > > > > > >                       exit(EXIT_FAILURE);
> > > > > > > >               }
> > > > > > > > @@ -756,6 +762,26 @@ int main(int argc, char **argv)
> > > > > > > >               fprintf(stderr, "Creating firmware capsule failed\n");
> > > > > > > >               exit(EXIT_FAILURE);
> > > > > > > >       }
> > > > > > > > +}
> > > > > > > > +
> > > > > > > > +/**
> > > > > > > > + * main - main entry function of mkeficapsule
> > > > > > > > + * @argc:    Number of arguments
> > > > > > > > + * @argv:    Array of pointers to arguments
> > > > > > > > + *
> > > > > > > > + * Create an uefi capsule file, optionally signing it.
> > > > > > > > + * Parse all the arguments and pass them on to create_fwbin().
> > > > > > > > + *
> > > > > > > > + * Return:
> > > > > > > > + * * 0  - on success
> > > > > > > > + * * -1 - on failure
> > > > > > > > + */
> > > > > > > > +int main(int argc, char **argv)
> > > > > > > > +{
> > > > > > > > +     if (!strcmp(CONFIG_EFI_CAPSULE_CFG_FILE, ""))
> > > > > > > > +             capsule_with_cmdline_params(argc, argv);
> > > > > > > > +     else
> > > > > > > > +             capsule_with_cfg_file(CONFIG_EFI_CAPSULE_CFG_FILE);
> > > > > > >
> > > > > > > I don't know where the macro, CONFIG_EFI_CAPSULE_CFG_FILE, comes from.
> > > > > > > Anyhow, as a general rule, any host tool must be as generic as it should not
> > > > > > > depend on a target's config.
> > > > > > > (I was told so before.)
> > > > > > >
> > > > > > > So I would suggest that you add another command line, say "--config-file <file>",
> > > > > > > to make the command generic.
> > > > > >
> > > > > > Yes, that would be something followed by most of the tools. The reason
> > > > > > I did not add a command-line option for the confile file is because I
> > > > > > want the capsule generation added as a make target. With the path to
> > > > > > the config file specified through the Kconfig symbol, we can invoke
> > > > > > 'make capsule', and it would build the capsules by parsing the
> > > > > > parameters from the config file, taken from the Kconfig symbol. I know
> > > > > > there are ways of specifying options when using a make command, but I
> > > > > > don't think that is a clean way of doing things.
> > > > >
> > > > > Not sure, but in your [5/7],
> > > > > cmd_mkeficapsule = $(objtree)/tools/mkeficapsule --config-file $(CONFIG_EFI_CAPSULE_CFG_FILE)
> > > > >
> > > > > Doesn't this change work?
> > > >
> > > > So, I tried the above suggested change. But trying to run a make
> > > > 'target' does not work without the .config file being present.
> > >
> > > Not sure what you meant to say here.
> > > Why don't you have .config when building U-Boot (or rather 'target')?
> >
> > Maybe I misunderstood your earlier comment, but I thought you were
> > looking to build capsules without relying on a target config.
>
> Not exactly.
> The basic requirement, I believe, is that the exact same binary (with
> the same set of functionalities) should be generated for any host tool
> whatever a target's config, including tools-only_defconfig, is.

Okay. I think I now understand what you are looking for. However, I
believe if you want the same binary for both scenarios, the only way
might be to drop the make target to generate capsules, and do it
through the --config-file command-line option. I will check if we can
pass the config file as a parameter when building capsules as a
target. I could not get it working when I tried it earlier though. If
this is indeed not possible, do you have a strong opinion on having
the same binary for both scenarios?

-sughosh

>
> Is it clear now?
>
> -Takahiro Akashi
>
> > Which I
> > believe cannot be done for a make target.
> >
> > -sughosh
> >
> > >
> > > -Takahiro Akashi
> > >
> > > > FWIW,
> > > > the same is the case for building tools as well. I think that is the
> > > > reason for the tools-only_defconfig.
> > > >
> > > > -sughosh
> > > >
> > > > >
> > > > > -Takahiro Akashi
> > > > >
> > > > >
> > > > > > Given the use case of
> > > > > > a make target, I hope we can use the Kconfig symbol for specifying the
> > > > > > config file path.
> > > > > >
> > > > > > -sughosh
> > > > > >
> > > > > > >
> > > > > > > -Takahiro Akashi
> > > > > > >
> > > > > > >
> > > > > > > >
> > > > > > > >       exit(EXIT_SUCCESS);
> > > > > > > >  }
> > > > > > > > diff --git a/tools/mkeficapsule_parse.c b/tools/mkeficapsule_parse.c
> > > > > > > > new file mode 100644
> > > > > > > > index 0000000000..ef4f3f6705
> > > > > > > > --- /dev/null
> > > > > > > > +++ b/tools/mkeficapsule_parse.c
> > > > > > > > @@ -0,0 +1,345 @@
> > > > > > > > +// SPDX-License-Identifier: GPL-2.0
> > > > > > > > +/*
> > > > > > > > + * Copyright 2023 Linaro Limited
> > > > > > > > + */
> > > > > > > > +
> > > > > > > > +/*
> > > > > > > > + * The code in this file adds parsing ability to the mkeficapsule
> > > > > > > > + * tool. This allows specifying parameters needed to build the capsule
> > > > > > > > + * through the config file instead of specifying them on the command-line.
> > > > > > > > + * Parameters can be specified for more than one payload, generating the
> > > > > > > > + * corresponding capsule files.
> > > > > > > > + *
> > > > > > > > + * The parameters are specified in a "key:value" pair. All the parameters
> > > > > > > > + * that are currently supported by the mkeficapsule tool can be specified
> > > > > > > > + * in the config file.
> > > > > > > > + *
> > > > > > > > + * The example below shows four payloads. The first payload is an example
> > > > > > > > + * of generating a signed capsule. The second payload is an example of
> > > > > > > > + * generating an unsigned capsule. The third payload is an accept empty
> > > > > > > > + * capsule, while the fourth payload is the revert empty capsule, used
> > > > > > > > + * for the multi-bank firmware update feature.
> > > > > > > > + *
> > > > > > > > + * This functionality can be easily extended to generate a single capsule
> > > > > > > > + * comprising multiple payloads.
> > > > > > > > +
> > > > > > > > +     {
> > > > > > > > +         image-guid: 02f4d760-cfd5-43bd-8e2d-a42acb33c660
> > > > > > > > +         hardware-instance: 0
> > > > > > > > +         monotonic-count: 1
> > > > > > > > +         payload: u-boot.bin
> > > > > > > > +         image-index: 1
> > > > > > > > +         private-key: /path/to/priv/key
> > > > > > > > +         pub-key-cert: /path/to/pub/key
> > > > > > > > +         capsule: u-boot.capsule
> > > > > > > > +     }
> > > > > > > > +     {
> > > > > > > > +         image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e
> > > > > > > > +         hardware-instance: 0
> > > > > > > > +         payload: u-boot.itb
> > > > > > > > +         image-index: 2
> > > > > > > > +         oemflags: 0x8000
> > > > > > > > +         capsule: fit.capsule
> > > > > > > > +     }
> > > > > > > > +     {
> > > > > > > > +         capsule-type: accept
> > > > > > > > +         image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e
> > > > > > > > +         capsule: accept.capsule
> > > > > > > > +     }
> > > > > > > > +     {
> > > > > > > > +         capsule-type: revert
> > > > > > > > +         capsule: revert.capsule
> > > > > > > > +     }
> > > > > > > > +*/
> > > > > > > > +
> > > > > > > > +#include <ctype.h>
> > > > > > > > +#include <limits.h>
> > > > > > > > +#include <stdio.h>
> > > > > > > > +#include <stdlib.h>
> > > > > > > > +#include <string.h>
> > > > > > > > +
> > > > > > > > +#include <uuid/uuid.h>
> > > > > > > > +
> > > > > > > > +#include "eficapsule.h"
> > > > > > > > +
> > > > > > > > +#define PARAMS_START "{"
> > > > > > > > +#define PARAMS_END   "}"
> > > > > > > > +
> > > > > > > > +#define PSTART               2
> > > > > > > > +#define PEND         3
> > > > > > > > +
> > > > > > > > +#define MALLOC_FAIL_STR              "Unable to allocate memory\n"
> > > > > > > > +
> > > > > > > > +#define ARRAY_SIZE(x)                (sizeof(x) / sizeof((x)[0]))
> > > > > > > > +
> > > > > > > > +const char *capsule_params[] = {
> > > > > > > > +     "image-guid", "image-index", "private-key",
> > > > > > > > +     "pub-key-cert", "payload", "capsule",
> > > > > > > > +     "hardware-instance", "monotonic-count",
> > > > > > > > +     "capsule-type", "oemflags" };
> > > > > > > > +
> > > > > > > > +static unsigned char params_start;
> > > > > > > > +static unsigned char params_end;
> > > > > > > > +
> > > > > > > > +static void print_and_exit(const char *str)
> > > > > > > > +{
> > > > > > > > +     fprintf(stderr, "%s", str);
> > > > > > > > +     exit(EXIT_FAILURE);
> > > > > > > > +}
> > > > > > > > +
> > > > > > > > +static int param_delim_checks(char *line, unsigned char *token)
> > > > > > > > +{
> > > > > > > > +     if (!strcmp(line, PARAMS_START)) {
> > > > > > > > +             if (params_start || !params_end) {
> > > > > > > > +                     fprintf(stderr, "Earlier params processing still in progress. ");
> > > > > > > > +                     fprintf(stderr, "Can't start processing a new params.\n");
> > > > > > > > +                     exit(EXIT_FAILURE);
> > > > > > > > +             } else {
> > > > > > > > +                     params_start = 1;
> > > > > > > > +                     params_end = 0;
> > > > > > > > +                     *token = PSTART;
> > > > > > > > +                     return 1;
> > > > > > > > +             }
> > > > > > > > +     } else if (!strcmp(line, PARAMS_END)) {
> > > > > > > > +             if (!params_start) {
> > > > > > > > +                     fprintf(stderr, "Cannot put end braces without start braces. ");
> > > > > > > > +                     fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > > > > > +                     exit(EXIT_FAILURE);
> > > > > > > > +             } else {
> > > > > > > > +                     params_start = 0;
> > > > > > > > +                     params_end = 1;
> > > > > > > > +                     *token = PEND;
> > > > > > > > +                     return 1;
> > > > > > > > +             }
> > > > > > > > +     } else if (!params_start) {
> > > > > > > > +             fprintf(stderr, "Params should be passed within braces. ");
> > > > > > > > +             fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > > > > > +             exit(EXIT_FAILURE);
> > > > > > > > +     }
> > > > > > > > +
> > > > > > > > +     return 0;
> > > > > > > > +}
> > > > > > > > +
> > > > > > > > +static void add_guid(efi_guid_t **guid_param, char *guid)
> > > > > > > > +{
> > > > > > > > +     unsigned char uuid_buf[16];
> > > > > > > > +
> > > > > > > > +     *guid_param = malloc(sizeof(efi_guid_t));
> > > > > > > > +     if (!*guid_param)
> > > > > > > > +             print_and_exit(MALLOC_FAIL_STR);
> > > > > > > > +
> > > > > > > > +     if (uuid_parse(guid, uuid_buf))
> > > > > > > > +             print_and_exit("Wrong guid format\n");
> > > > > > > > +
> > > > > > > > +     convert_uuid_to_guid(uuid_buf);
> > > > > > > > +     memcpy(*guid_param, uuid_buf, sizeof(efi_guid_t));
> > > > > > > > +}
> > > > > > > > +
> > > > > > > > +static void add_string(char **dst, char *val)
> > > > > > > > +{
> > > > > > > > +     *dst = strdup(val);
> > > > > > > > +     if (!*dst)
> > > > > > > > +             print_and_exit(MALLOC_FAIL_STR);
> > > > > > > > +}
> > > > > > > > +
> > > > > > > > +static void match_and_populate_param(char *key, char *val,
> > > > > > > > +                                  struct efi_capsule_params *param)
> > > > > > > > +{
> > > > > > > > +     int i;
> > > > > > > > +
> > > > > > > > +     for (i = 0; i < ARRAY_SIZE(capsule_params); i++) {
> > > > > > > > +             if (!strcmp(key, capsule_params[i])) {
> > > > > > > > +                     switch (i) {
> > > > > > > > +                     case 0:
> > > > > > > > +                             add_guid(&param->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(&param->privkey_file, val);
> > > > > > > > +                             return;
> > > > > > > > +                     case 3:
> > > > > > > > +                             add_string(&param->cert_file, val);
> > > > > > > > +                             return;
> > > > > > > > +                     case 4:
> > > > > > > > +                             add_string(&param->input_file, val);
> > > > > > > > +                             return;
> > > > > > > > +                     case 5:
> > > > > > > > +                             add_string(&param->capsule_file, val);
> > > > > > > > +                             return;
> > > > > > > > +                     case 6:
> > > > > > > > +                             param->hardware_instance = strtoul(val, NULL, 0);
> > > > > > > > +                             if (param->hardware_instance == ULONG_MAX)
> > > > > > > > +                                     print_and_exit("Enter a valid hardware instance value");
> > > > > > > > +                             return;
> > > > > > > > +                     case 7:
> > > > > > > > +                             param->monotonic_count = strtoull(val, NULL, 0);
> > > > > > > > +                             if (param->monotonic_count == ULLONG_MAX)
> > > > > > > > +                                     print_and_exit("Enter a valid monotonic count value");
> > > > > > > > +                             return;
> > > > > > > > +                     case 8:
> > > > > > > > +                             if (!strcmp(val, "normal"))
> > > > > > > > +                                     param->capsule = CAPSULE_NORMAL_BLOB;
> > > > > > > > +                             else if (!strcmp(val, "accept"))
> > > > > > > > +                                     param->capsule = CAPSULE_ACCEPT;
> > > > > > > > +                             else if (!strcmp(val, "revert"))
> > > > > > > > +                                     param->capsule = CAPSULE_REVERT;
> > > > > > > > +                             else
> > > > > > > > +                                     print_and_exit("Invalid type of capsule");
> > > > > > > > +
> > > > > > > > +                             return;
> > > > > > > > +                     case 9:
> > > > > > > > +                             param->oemflags = strtoul(val, NULL, 0);
> > > > > > > > +                             if (param->oemflags > 0xffff)
> > > > > > > > +                                     print_and_exit("OemFlags must be between 0x0 and 0xffff\n");
> > > > > > > > +                             return;
> > > > > > > > +                     }
> > > > > > > > +             }
> > > > > > > > +     }
> > > > > > > > +
> > > > > > > > +     fprintf(stderr, "Undefined param %s specified. ", key);
> > > > > > > > +     fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > > > > > +     exit(EXIT_FAILURE);
> > > > > > > > +}
> > > > > > > > +
> > > > > > > > +static int get_capsule_params(char *line, struct efi_capsule_params *params)
> > > > > > > > +{
> > > > > > > > +     char *key = NULL;
> > > > > > > > +     char *val = NULL;
> > > > > > > > +     unsigned char token;
> > > > > > > > +
> > > > > > > > +     if (param_delim_checks(line, &token))
> > > > > > > > +             return token;
> > > > > > > > +
> > > > > > > > +     key = strtok(line, ":");
> > > > > > > > +     if (key)
> > > > > > > > +             val = strtok(NULL, "\0");
> > > > > > > > +     else
> > > > > > > > +             print_and_exit("Expect the params in a key:value pair\n");
> > > > > > > > +
> > > > > > > > +     match_and_populate_param(key, val, params);
> > > > > > > > +
> > > > > > > > +     return 0;
> > > > > > > > +}
> > > > > > > > +
> > > > > > > > +static char *skip_whitespace(char *line)
> > > > > > > > +{
> > > > > > > > +     char *ptr, *newline;
> > > > > > > > +
> > > > > > > > +     ptr = malloc(strlen(line) + 1);
> > > > > > > > +     if (!ptr)
> > > > > > > > +             print_and_exit(MALLOC_FAIL_STR);
> > > > > > > > +
> > > > > > > > +     for (newline = ptr; *line; line++)
> > > > > > > > +             if (!isblank(*line))
> > > > > > > > +                     *ptr++ = *line;
> > > > > > > > +     *ptr = '\0';
> > > > > > > > +     return newline;
> > > > > > > > +}
> > > > > > > > +
> > > > > > > > +static int parse_capsule_payload_params(FILE *fp, struct efi_capsule_params *params)
> > > > > > > > +{
> > > > > > > > +     char *line = NULL;
> > > > > > > > +     char *newline;
> > > > > > > > +     size_t n = 0;
> > > > > > > > +     ssize_t len;
> > > > > > > > +
> > > > > > > > +     while ((len = getline(&line, &n, fp)) != -1) {
> > > > > > > > +             if (len == 1 && line[len - 1] == '\n')
> > > > > > > > +                     continue;
> > > > > > > > +
> > > > > > > > +             line[len - 1] = '\0';
> > > > > > > > +
> > > > > > > > +             newline = skip_whitespace(line);
> > > > > > > > +
> > > > > > > > +             if (newline[0] == '#')
> > > > > > > > +                     continue;
> > > > > > > > +
> > > > > > > > +             if (get_capsule_params(newline, params) == PEND)
> > > > > > > > +                     return 0;
> > > > > > > > +     }
> > > > > > > > +
> > > > > > > > +     if (errno == EINVAL || errno == ENOMEM) {
> > > > > > > > +             fprintf(stderr, "getline() returned an error %s reading the line\n",
> > > > > > > > +                     strerror(errno));
> > > > > > > > +             exit(EXIT_FAILURE);
> > > > > > > > +     } else if (params_start == 1 || params_end == 0) {
> > > > > > > > +             fprintf(stderr, "Params should be passed within braces. ");
> > > > > > > > +             fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > > > > > +             exit(EXIT_FAILURE);
> > > > > > > > +     } else {
> > > > > > > > +             return -1;
> > > > > > > > +     }
> > > > > > > > +}
> > > > > > > > +
> > > > > > > > +static void params_dependency_check(struct efi_capsule_params *params)
> > > > > > > > +{
> > > > > > > > +     /* check necessary parameters */
> > > > > > > > +     if ((params->capsule == CAPSULE_NORMAL_BLOB &&
> > > > > > > > +          ((!params->input_file || !params->capsule_file ||
> > > > > > > > +            !params->image_guid) ||
> > > > > > > > +           ((params->privkey_file && !params->cert_file) ||
> > > > > > > > +            (!params->privkey_file && params->cert_file)))) ||
> > > > > > > > +         (params->capsule != CAPSULE_NORMAL_BLOB &&
> > > > > > > > +          (!params->capsule_file ||
> > > > > > > > +           (params->capsule == CAPSULE_ACCEPT && !params->image_guid) ||
> > > > > > > > +           (params->capsule == CAPSULE_REVERT && params->image_guid)))) {
> > > > > > > > +             print_usage();
> > > > > > > > +             exit(EXIT_FAILURE);
> > > > > > > > +     }
> > > > > > > > +}
> > > > > > > > +
> > > > > > > > +static void generate_capsule(struct efi_capsule_params *params)
> > > > > > > > +{
> > > > > > > > +     if (params->capsule != CAPSULE_NORMAL_BLOB) {
> > > > > > > > +             if (create_empty_capsule(params->capsule_file,
> > > > > > > > +                                      params->image_guid,
> > > > > > > > +                                      params->capsule ==
> > > > > > > > +                                      CAPSULE_ACCEPT) < 0)
> > > > > > > > +                     print_and_exit("Creating empty capsule failed\n");
> > > > > > > > +     } else if (create_fwbin(params->capsule_file, params->input_file,
> > > > > > > > +                           params->image_guid, params->image_index,
> > > > > > > > +                           params->hardware_instance,
> > > > > > > > +                           params->monotonic_count,
> > > > > > > > +                           params->privkey_file,
> > > > > > > > +                           params->cert_file,
> > > > > > > > +                           (uint16_t)params->oemflags) < 0) {
> > > > > > > > +             print_and_exit("Creating firmware capsule failed\n");
> > > > > > > > +     }
> > > > > > > > +}
> > > > > > > > +
> > > > > > > > +/**
> > > > > > > > + * capsule_with_cfg_file() - Generate capsule from config file
> > > > > > > > + * @cfg_file: Path to the config file
> > > > > > > > + *
> > > > > > > > + * Parse the capsule parameters from the config file and use the
> > > > > > > > + * parameters for generating one or more capsules.
> > > > > > > > + *
> > > > > > > > + * Return: None
> > > > > > > > + *
> > > > > > > > + */
> > > > > > > > +void capsule_with_cfg_file(const char *cfg_file)
> > > > > > > > +{
> > > > > > > > +     FILE *fp;
> > > > > > > > +     struct efi_capsule_params params = { 0 };
> > > > > > > > +
> > > > > > > > +     fp = fopen(cfg_file, "r");
> > > > > > > > +     if (!fp) {
> > > > > > > > +             fprintf(stderr, "Unable to open the capsule config file %s\n",
> > > > > > > > +                     cfg_file);
> > > > > > > > +             exit(EXIT_FAILURE);
> > > > > > > > +     }
> > > > > > > > +
> > > > > > > > +     params_start = 0;
> > > > > > > > +     params_end = 1;
> > > > > > > > +
> > > > > > > > +     while (parse_capsule_payload_params(fp, &params) != -1) {
> > > > > > > > +             params_dependency_check(&params);
> > > > > > > > +             generate_capsule(&params);
> > > > > > > > +
> > > > > > > > +             memset(&params, 0, sizeof(struct efi_capsule_params));
> > > > > > > > +     }
> > > > > > > > +}
> > > > > > > > --
> > > > > > > > 2.34.1
> > > > > > > >

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 4/7] tools: mkeficapsule: Add support for parsing capsule params from config file
  2023-06-16  5:07                 ` Sughosh Ganu
@ 2023-06-16  5:18                   ` Takahiro Akashi
  2023-06-16  6:35                     ` Sughosh Ganu
  0 siblings, 1 reply; 50+ messages in thread
From: Takahiro Akashi @ 2023-06-16  5:18 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Simon Glass, Michal Simek

On Fri, Jun 16, 2023 at 10:37:01AM +0530, Sughosh Ganu wrote:
> hi Takahiro,
> 
> On Fri, 16 Jun 2023 at 10:16, Takahiro Akashi
> <takahiro.akashi@linaro.org> wrote:
> >
> > Hi Sughosh,
> >
> > On Fri, Jun 16, 2023 at 09:56:33AM +0530, Sughosh Ganu wrote:
> > > On Thu, 15 Jun 2023 at 11:19, Takahiro Akashi
> > > <takahiro.akashi@linaro.org> wrote:
> > > >
> > > > On Thu, Jun 15, 2023 at 10:09:06AM +0530, Sughosh Ganu wrote:
> > > > > On Wed, 14 Jun 2023 at 11:23, Takahiro Akashi
> > > > > <takahiro.akashi@linaro.org> wrote:
> > > > > >
> > > > > > On Wed, Jun 14, 2023 at 10:56:23AM +0530, Sughosh Ganu wrote:
> > > > > > > hi Takahiro,
> > > > > > >
> > > > > > > On Wed, 14 Jun 2023 at 09:09, Takahiro Akashi
> > > > > > > <takahiro.akashi@linaro.org> wrote:
> > > > > > > >
> > > > > > > > Hi Sughosh,
> > > > > > > >
> > > > > > > > I think this is a good extension to mkeficapsule, but
> > > > > > > >
> > > > > > > > On Tue, Jun 13, 2023 at 04:08:03PM +0530, Sughosh Ganu wrote:
> > > > > > > > > Add support for specifying the parameters needed for capsule
> > > > > > > > > generation through a config file, instead of passing them through
> > > > > > > > > command-line. Parameters for more than a single capsule file can be
> > > > > > > > > specified, resulting in generation of multiple capsules through a
> > > > > > > > > single invocation of the command.
> > > > > > > > >
> > > > > > > > > This path is to be used for generating capsules through a make target,
> > > > > > > > > with the parameters being parsed from the config file.
> > > > > > > > >
> > > > > > > > > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > > > > > > > > ---
> > > > > > > > >  tools/Kconfig              |   9 +
> > > > > > > > >  tools/Makefile             |   1 +
> > > > > > > > >  tools/eficapsule.h         | 110 ++++++++++++
> > > > > > > > >  tools/mkeficapsule.c       | 106 +++++++-----
> > > > > > > > >  tools/mkeficapsule_parse.c | 345 +++++++++++++++++++++++++++++++++++++
> > > > > > > > >  5 files changed, 531 insertions(+), 40 deletions(-)
> > > > > > > > >  create mode 100644 tools/mkeficapsule_parse.c
> > > > > > > > >
> > > > > > > > > diff --git a/tools/Kconfig b/tools/Kconfig
> > > > > > > > > index 539708f277..95f27b7c45 100644
> > > > > > > > > --- a/tools/Kconfig
> > > > > > > > > +++ b/tools/Kconfig
> > > > > > > > > @@ -98,6 +98,15 @@ config TOOLS_MKEFICAPSULE
> > > > > > > > >         optionally sign that file. If you want to enable UEFI capsule
> > > > > > > > >         update feature on your target, you certainly need this.
> > > > > > > > >
> > > > > > > > > +config EFI_CAPSULE_CFG_FILE
> > > > > > > > > +     string "Path to the EFI Capsule Config File"
> > > > > > > > > +     default ""
> > > > > > > > > +     help
> > > > > > > > > +       Path to the EFI capsule config file which provides the
> > > > > > > > > +       parameters needed to build capsule(s). Parameters can be
> > > > > > > > > +       provided for multiple payloads resulting in corresponding
> > > > > > > > > +       capsule images being generated.
> > > > > > > > > +
> > > > > > > > >  menuconfig FSPI_CONF_HEADER
> > > > > > > > >       bool "FlexSPI Header Configuration"
> > > > > > > > >       help
> > > > > > > > > diff --git a/tools/Makefile b/tools/Makefile
> > > > > > > > > index d793cf3bec..ef366f3d61 100644
> > > > > > > > > --- a/tools/Makefile
> > > > > > > > > +++ b/tools/Makefile
> > > > > > > > > @@ -250,6 +250,7 @@ HOSTLDLIBS_mkeficapsule += \
> > > > > > > > >  HOSTLDLIBS_mkeficapsule += \
> > > > > > > > >       $(shell pkg-config --libs uuid 2> /dev/null || echo "-luuid")
> > > > > > > > >  hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule
> > > > > > > > > +mkeficapsule-objs := mkeficapsule.o mkeficapsule_parse.o
> > > > > > > > >
> > > > > > > > >  # We build some files with extra pedantic flags to try to minimize things
> > > > > > > > >  # that won't build on some weird host compiler -- though there are lots of
> > > > > > > > > diff --git a/tools/eficapsule.h b/tools/eficapsule.h
> > > > > > > > > index 072a4b5598..42e66c6d6a 100644
> > > > > > > > > --- a/tools/eficapsule.h
> > > > > > > > > +++ b/tools/eficapsule.h
> > > > > > > > > @@ -52,6 +52,38 @@ typedef struct {
> > > > > > > > >  /* flags */
> > > > > > > > >  #define CAPSULE_FLAGS_PERSIST_ACROSS_RESET      0x00010000
> > > > > > > > >
> > > > > > > > > +enum capsule_type {
> > > > > > > > > +     CAPSULE_NORMAL_BLOB = 0,
> > > > > > > > > +     CAPSULE_ACCEPT,
> > > > > > > > > +     CAPSULE_REVERT,
> > > > > > > > > +};
> > > > > > > > > +
> > > > > > > > > +/**
> > > > > > > > > + * struct efi_capsule_params - Capsule parameters
> > > > > > > > > + * @image_guid: Guid value of the payload input image
> > > > > > > > > + * @image_index: Image index value
> > > > > > > > > + * @hardware_instance: Hardware instance to be used for the image
> > > > > > > > > + * @monotonic_count: Monotonic count value to be used for signed capsule
> > > > > > > > > + * @privkey_file: Path to private key used in capsule signing
> > > > > > > > > + * @cert_file: Path to public key certificate used in capsule signing
> > > > > > > > > + * @input_file: Path to payload input image
> > > > > > > > > + * @capsule_file: Path to the output capsule file
> > > > > > > > > + * @oemflags: Oemflags to be populated in the capsule header
> > > > > > > > > + * @capsule: Capsule Type, normal or accept or revert
> > > > > > > > > + */
> > > > > > > > > +struct efi_capsule_params {
> > > > > > > > > +     efi_guid_t *image_guid;
> > > > > > > > > +     unsigned long image_index;
> > > > > > > > > +     unsigned long hardware_instance;
> > > > > > > > > +     uint64_t monotonic_count;
> > > > > > > > > +     char *privkey_file;
> > > > > > > > > +     char *cert_file;
> > > > > > > > > +     char *input_file;
> > > > > > > > > +     char *capsule_file;
> > > > > > > > > +     unsigned long oemflags;
> > > > > > > > > +     enum capsule_type capsule;
> > > > > > > > > +};
> > > > > > > > > +
> > > > > > > > >  struct efi_capsule_header {
> > > > > > > > >       efi_guid_t capsule_guid;
> > > > > > > > >       uint32_t header_size;
> > > > > > > > > @@ -113,4 +145,82 @@ struct efi_firmware_image_authentication {
> > > > > > > > >       struct win_certificate_uefi_guid auth_info;
> > > > > > > > >  } __packed;
> > > > > > > > >
> > > > > > > > > +/**
> > > > > > > > > + * capsule_with_cfg_file() - Generate capsule from config file
> > > > > > > > > + * @cfg_file: Path to the config file
> > > > > > > > > + *
> > > > > > > > > + * Parse the capsule parameters from the config file and use the
> > > > > > > > > + * parameters for generating one or more capsules.
> > > > > > > > > + *
> > > > > > > > > + * Return: None
> > > > > > > > > + *
> > > > > > > > > + */
> > > > > > > > > +void capsule_with_cfg_file(const char *cfg_file);
> > > > > > > > > +
> > > > > > > > > +/**
> > > > > > > > > + * convert_uuid_to_guid() - convert UUID to GUID
> > > > > > > > > + * @buf:     UUID binary
> > > > > > > > > + *
> > > > > > > > > + * UUID and GUID have the same data structure, but their binary
> > > > > > > > > + * formats are different due to the endianness. See lib/uuid.c.
> > > > > > > > > + * Since uuid_parse() can handle only UUID, this function must
> > > > > > > > > + * be called to get correct data for GUID when parsing a string.
> > > > > > > > > + *
> > > > > > > > > + * The correct data will be returned in @buf.
> > > > > > > > > + */
> > > > > > > > > +void convert_uuid_to_guid(unsigned char *buf);
> > > > > > > > > +
> > > > > > > > > +/**
> > > > > > > > > + * create_empty_capsule() - Generate an empty capsule
> > > > > > > > > + * @path: Path to the empty capsule file to be generated
> > > > > > > > > + * @guid: Guid value of the image for which empty capsule is generated
> > > > > > > > > + * @fw_accept: Flag to specify whether to generate accept or revert capsule
> > > > > > > > > + *
> > > > > > > > > + * Generate an empty capsule, either an accept or a revert capsule to be
> > > > > > > > > + * used to flag acceptance or rejection of an earlier executed firmware
> > > > > > > > > + * update operation. Being used in the FWU Multi Bank firmware update
> > > > > > > > > + * feature.
> > > > > > > > > + *
> > > > > > > > > + * Return: 0 if OK, -ve on error
> > > > > > > > > + *
> > > > > > > > > + */
> > > > > > > > > +int create_empty_capsule(char *path, efi_guid_t *guid, bool fw_accept);
> > > > > > > > > +
> > > > > > > > > +/**
> > > > > > > > > + * create_fwbin - create an uefi capsule file
> > > > > > > > > + * @path:    Path to a created capsule file
> > > > > > > > > + * @bin:     Path to a firmware binary to encapsulate
> > > > > > > > > + * @guid:    GUID of related FMP driver
> > > > > > > > > + * @index:   Index number in capsule
> > > > > > > > > + * @instance:        Instance number in capsule
> > > > > > > > > + * @mcount:  Monotonic count in authentication information
> > > > > > > > > + * @private_file:    Path to a private key file
> > > > > > > > > + * @cert_file:       Path to a certificate file
> > > > > > > > > + * @oemflags:  Capsule OEM Flags, bits 0-15
> > > > > > > > > + *
> > > > > > > > > + * This function actually does the job of creating an uefi capsule file.
> > > > > > > > > + * All the arguments must be supplied.
> > > > > > > > > + * If either @private_file ror @cert_file is NULL, the capsule file
> > > > > > > > > + * won't be signed.
> > > > > > > > > + *
> > > > > > > > > + * Return:
> > > > > > > > > + * * 0  - on success
> > > > > > > > > + * * -1 - on failure
> > > > > > > > > + */
> > > > > > > > > +int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > > > > > > +              unsigned long index, unsigned long instance,
> > > > > > > > > +              uint64_t mcount, char *privkey_file, char *cert_file,
> > > > > > > > > +              uint16_t oemflags);
> > > > > > > > > +
> > > > > > > > > +/**
> > > > > > > > > + * print_usage() - Print the command usage string
> > > > > > > > > + *
> > > > > > > > > + * Prints the standard command usage string. Called in the case
> > > > > > > > > + * of incorrect parameters being passed to the tool.
> > > > > > > > > + *
> > > > > > > > > + * Return: None
> > > > > > > > > + *
> > > > > > > > > + */
> > > > > > > > > +void print_usage(void);
> > > > > > > > > +
> > > > > > > > >  #endif /* _EFI_CAPSULE_H */
> > > > > > > > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> > > > > > > > > index b71537beee..711adf0439 100644
> > > > > > > > > --- a/tools/mkeficapsule.c
> > > > > > > > > +++ b/tools/mkeficapsule.c
> > > > > > > > > @@ -31,12 +31,6 @@ 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 struct option options[] = {
> > > > > > > > >       {"guid", required_argument, NULL, 'g'},
> > > > > > > > >       {"index", required_argument, NULL, 'i'},
> > > > > > > > > @@ -52,7 +46,16 @@ static struct option options[] = {
> > > > > > > > >       {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"
> > > > > > > > > @@ -400,10 +403,10 @@ static void free_sig_data(struct auth_context *ctx)
> > > > > > > > >   * * 0  - on success
> > > > > > > > >   * * -1 - on failure
> > > > > > > > >   */
> > > > > > > > > -static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > > > > > > -                     unsigned long index, unsigned long instance,
> > > > > > > > > -                     uint64_t mcount, char *privkey_file, char *cert_file,
> > > > > > > > > -                     uint16_t oemflags)
> > > > > > > > > +int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > > > > > > +              unsigned long index, unsigned long instance,
> > > > > > > > > +              uint64_t mcount, char *privkey_file, char *cert_file,
> > > > > > > > > +              uint16_t oemflags)
> > > > > > > > >  {
> > > > > > > > >       struct efi_capsule_header header;
> > > > > > > > >       struct efi_firmware_management_capsule_header capsule;
> > > > > > > > > @@ -580,7 +583,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;
> > > > > > > > > @@ -623,19 +640,7 @@ err:
> > > > > > > > >       return ret;
> > > > > > > > >  }
> > > > > > > > >
> > > > > > > > > -/**
> > > > > > > > > - * main - main entry function of mkeficapsule
> > > > > > > > > - * @argc:    Number of arguments
> > > > > > > > > - * @argv:    Array of pointers to arguments
> > > > > > > > > - *
> > > > > > > > > - * Create an uefi capsule file, optionally signing it.
> > > > > > > > > - * Parse all the arguments and pass them on to create_fwbin().
> > > > > > > > > - *
> > > > > > > > > - * Return:
> > > > > > > > > - * * 0  - on success
> > > > > > > > > - * * -1 - on failure
> > > > > > > > > - */
> > > > > > > > > -int main(int argc, char **argv)
> > > > > > > > > +static void capsule_with_cmdline_params(int argc, char **argv)
> > > > > > > > >  {
> > > > > > > > >       efi_guid_t *guid;
> > > > > > > > >       unsigned char uuid_buf[16];
> > > > > > > > > @@ -643,6 +648,7 @@ int main(int argc, char **argv)
> > > > > > > > >       uint64_t mcount;
> > > > > > > > >       unsigned long oemflags;
> > > > > > > > >       char *privkey_file, *cert_file;
> > > > > > > > > +     enum capsule_type capsule;
> > > > > > > > >       int c, idx;
> > > > > > > > >
> > > > > > > > >       guid = NULL;
> > > > > > > > > @@ -652,7 +658,7 @@ int main(int argc, char **argv)
> > > > > > > > >       privkey_file = NULL;
> > > > > > > > >       cert_file = NULL;
> > > > > > > > >       dump_sig = 0;
> > > > > > > > > -     capsule_type = CAPSULE_NORMAL_BLOB;
> > > > > > > > > +     capsule = CAPSULE_NORMAL_BLOB;
> > > > > > > > >       oemflags = 0;
> > > > > > > > >       for (;;) {
> > > > > > > > >               c = getopt_long(argc, argv, opts_short, options, &idx);
> > > > > > > > > @@ -702,20 +708,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);
> > > > > > > > > @@ -732,21 +738,21 @@ int main(int argc, char **argv)
> > > > > > > > >       }
> > > > > > > > >
> > > > > > > > >       /* check necessary parameters */
> > > > > > > > > -     if ((capsule_type == CAPSULE_NORMAL_BLOB &&
> > > > > > > > > -         ((argc != optind + 2) || !guid ||
> > > > > > > > > -          ((privkey_file && !cert_file) ||
> > > > > > > > > -           (!privkey_file && cert_file)))) ||
> > > > > > > > > -         (capsule_type != CAPSULE_NORMAL_BLOB &&
> > > > > > > > > -         ((argc != optind + 1) ||
> > > > > > > > > -          ((capsule_type == CAPSULE_ACCEPT) && !guid) ||
> > > > > > > > > -          ((capsule_type == CAPSULE_REVERT) && guid)))) {
> > > > > > > > > +     if ((capsule == CAPSULE_NORMAL_BLOB &&
> > > > > > > > > +          ((argc != optind + 2) || !guid ||
> > > > > > > > > +           ((privkey_file && !cert_file) ||
> > > > > > > > > +            (!privkey_file && cert_file)))) ||
> > > > > > > > > +         (capsule != CAPSULE_NORMAL_BLOB &&
> > > > > > > > > +          ((argc != optind + 1) ||
> > > > > > > > > +           (capsule == CAPSULE_ACCEPT && !guid) ||
> > > > > > > > > +           (capsule == CAPSULE_REVERT && guid)))) {
> > > > > > > > >               print_usage();
> > > > > > > > >               exit(EXIT_FAILURE);
> > > > > > > > >       }
> > > > > > > > >
> > > > > > > > > -     if (capsule_type != CAPSULE_NORMAL_BLOB) {
> > > > > > > > > +     if (capsule != CAPSULE_NORMAL_BLOB) {
> > > > > > > > >               if (create_empty_capsule(argv[argc - 1], guid,
> > > > > > > > > -                                      capsule_type == CAPSULE_ACCEPT) < 0) {
> > > > > > > > > +                                      capsule == CAPSULE_ACCEPT) < 0) {
> > > > > > > > >                       fprintf(stderr, "Creating empty capsule failed\n");
> > > > > > > > >                       exit(EXIT_FAILURE);
> > > > > > > > >               }
> > > > > > > > > @@ -756,6 +762,26 @@ int main(int argc, char **argv)
> > > > > > > > >               fprintf(stderr, "Creating firmware capsule failed\n");
> > > > > > > > >               exit(EXIT_FAILURE);
> > > > > > > > >       }
> > > > > > > > > +}
> > > > > > > > > +
> > > > > > > > > +/**
> > > > > > > > > + * main - main entry function of mkeficapsule
> > > > > > > > > + * @argc:    Number of arguments
> > > > > > > > > + * @argv:    Array of pointers to arguments
> > > > > > > > > + *
> > > > > > > > > + * Create an uefi capsule file, optionally signing it.
> > > > > > > > > + * Parse all the arguments and pass them on to create_fwbin().
> > > > > > > > > + *
> > > > > > > > > + * Return:
> > > > > > > > > + * * 0  - on success
> > > > > > > > > + * * -1 - on failure
> > > > > > > > > + */
> > > > > > > > > +int main(int argc, char **argv)
> > > > > > > > > +{
> > > > > > > > > +     if (!strcmp(CONFIG_EFI_CAPSULE_CFG_FILE, ""))
> > > > > > > > > +             capsule_with_cmdline_params(argc, argv);
> > > > > > > > > +     else
> > > > > > > > > +             capsule_with_cfg_file(CONFIG_EFI_CAPSULE_CFG_FILE);
> > > > > > > >
> > > > > > > > I don't know where the macro, CONFIG_EFI_CAPSULE_CFG_FILE, comes from.
> > > > > > > > Anyhow, as a general rule, any host tool must be as generic as it should not
> > > > > > > > depend on a target's config.
> > > > > > > > (I was told so before.)
> > > > > > > >
> > > > > > > > So I would suggest that you add another command line, say "--config-file <file>",
> > > > > > > > to make the command generic.
> > > > > > >
> > > > > > > Yes, that would be something followed by most of the tools. The reason
> > > > > > > I did not add a command-line option for the confile file is because I
> > > > > > > want the capsule generation added as a make target. With the path to
> > > > > > > the config file specified through the Kconfig symbol, we can invoke
> > > > > > > 'make capsule', and it would build the capsules by parsing the
> > > > > > > parameters from the config file, taken from the Kconfig symbol. I know
> > > > > > > there are ways of specifying options when using a make command, but I
> > > > > > > don't think that is a clean way of doing things.
> > > > > >
> > > > > > Not sure, but in your [5/7],
> > > > > > cmd_mkeficapsule = $(objtree)/tools/mkeficapsule --config-file $(CONFIG_EFI_CAPSULE_CFG_FILE)
> > > > > >
> > > > > > Doesn't this change work?
> > > > >
> > > > > So, I tried the above suggested change. But trying to run a make
> > > > > 'target' does not work without the .config file being present.
> > > >
> > > > Not sure what you meant to say here.
> > > > Why don't you have .config when building U-Boot (or rather 'target')?
> > >
> > > Maybe I misunderstood your earlier comment, but I thought you were
> > > looking to build capsules without relying on a target config.
> >
> > Not exactly.
> > The basic requirement, I believe, is that the exact same binary (with
> > the same set of functionalities) should be generated for any host tool
> > whatever a target's config, including tools-only_defconfig, is.
> 
> Okay. I think I now understand what you are looking for. However, I
> believe if you want the same binary for both scenarios, the only way
> might be to drop the make target to generate capsules, and do it
> through the --config-file command-line option.

Again not sure what you're trying to do.

> I will check if we can
> pass the config file as a parameter when building capsules as a
> target. I could not get it working when I tried it earlier though. If
> this is indeed not possible, do you have a strong opinion on having
> the same binary for both scenarios?

 I have no reason why you can't.
 If you see any failure, please give me more details about how
 you are going to manage so that I can help you.

 -Takahiro Akashi

> -sughosh
> 
> >
> > Is it clear now?
> >
> > -Takahiro Akashi
> >
> > > Which I
> > > believe cannot be done for a make target.
> > >
> > > -sughosh
> > >
> > > >
> > > > -Takahiro Akashi
> > > >
> > > > > FWIW,
> > > > > the same is the case for building tools as well. I think that is the
> > > > > reason for the tools-only_defconfig.
> > > > >
> > > > > -sughosh
> > > > >
> > > > > >
> > > > > > -Takahiro Akashi
> > > > > >
> > > > > >
> > > > > > > Given the use case of
> > > > > > > a make target, I hope we can use the Kconfig symbol for specifying the
> > > > > > > config file path.
> > > > > > >
> > > > > > > -sughosh
> > > > > > >
> > > > > > > >
> > > > > > > > -Takahiro Akashi
> > > > > > > >
> > > > > > > >
> > > > > > > > >
> > > > > > > > >       exit(EXIT_SUCCESS);
> > > > > > > > >  }
> > > > > > > > > diff --git a/tools/mkeficapsule_parse.c b/tools/mkeficapsule_parse.c
> > > > > > > > > new file mode 100644
> > > > > > > > > index 0000000000..ef4f3f6705
> > > > > > > > > --- /dev/null
> > > > > > > > > +++ b/tools/mkeficapsule_parse.c
> > > > > > > > > @@ -0,0 +1,345 @@
> > > > > > > > > +// SPDX-License-Identifier: GPL-2.0
> > > > > > > > > +/*
> > > > > > > > > + * Copyright 2023 Linaro Limited
> > > > > > > > > + */
> > > > > > > > > +
> > > > > > > > > +/*
> > > > > > > > > + * The code in this file adds parsing ability to the mkeficapsule
> > > > > > > > > + * tool. This allows specifying parameters needed to build the capsule
> > > > > > > > > + * through the config file instead of specifying them on the command-line.
> > > > > > > > > + * Parameters can be specified for more than one payload, generating the
> > > > > > > > > + * corresponding capsule files.
> > > > > > > > > + *
> > > > > > > > > + * The parameters are specified in a "key:value" pair. All the parameters
> > > > > > > > > + * that are currently supported by the mkeficapsule tool can be specified
> > > > > > > > > + * in the config file.
> > > > > > > > > + *
> > > > > > > > > + * The example below shows four payloads. The first payload is an example
> > > > > > > > > + * of generating a signed capsule. The second payload is an example of
> > > > > > > > > + * generating an unsigned capsule. The third payload is an accept empty
> > > > > > > > > + * capsule, while the fourth payload is the revert empty capsule, used
> > > > > > > > > + * for the multi-bank firmware update feature.
> > > > > > > > > + *
> > > > > > > > > + * This functionality can be easily extended to generate a single capsule
> > > > > > > > > + * comprising multiple payloads.
> > > > > > > > > +
> > > > > > > > > +     {
> > > > > > > > > +         image-guid: 02f4d760-cfd5-43bd-8e2d-a42acb33c660
> > > > > > > > > +         hardware-instance: 0
> > > > > > > > > +         monotonic-count: 1
> > > > > > > > > +         payload: u-boot.bin
> > > > > > > > > +         image-index: 1
> > > > > > > > > +         private-key: /path/to/priv/key
> > > > > > > > > +         pub-key-cert: /path/to/pub/key
> > > > > > > > > +         capsule: u-boot.capsule
> > > > > > > > > +     }
> > > > > > > > > +     {
> > > > > > > > > +         image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e
> > > > > > > > > +         hardware-instance: 0
> > > > > > > > > +         payload: u-boot.itb
> > > > > > > > > +         image-index: 2
> > > > > > > > > +         oemflags: 0x8000
> > > > > > > > > +         capsule: fit.capsule
> > > > > > > > > +     }
> > > > > > > > > +     {
> > > > > > > > > +         capsule-type: accept
> > > > > > > > > +         image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e
> > > > > > > > > +         capsule: accept.capsule
> > > > > > > > > +     }
> > > > > > > > > +     {
> > > > > > > > > +         capsule-type: revert
> > > > > > > > > +         capsule: revert.capsule
> > > > > > > > > +     }
> > > > > > > > > +*/
> > > > > > > > > +
> > > > > > > > > +#include <ctype.h>
> > > > > > > > > +#include <limits.h>
> > > > > > > > > +#include <stdio.h>
> > > > > > > > > +#include <stdlib.h>
> > > > > > > > > +#include <string.h>
> > > > > > > > > +
> > > > > > > > > +#include <uuid/uuid.h>
> > > > > > > > > +
> > > > > > > > > +#include "eficapsule.h"
> > > > > > > > > +
> > > > > > > > > +#define PARAMS_START "{"
> > > > > > > > > +#define PARAMS_END   "}"
> > > > > > > > > +
> > > > > > > > > +#define PSTART               2
> > > > > > > > > +#define PEND         3
> > > > > > > > > +
> > > > > > > > > +#define MALLOC_FAIL_STR              "Unable to allocate memory\n"
> > > > > > > > > +
> > > > > > > > > +#define ARRAY_SIZE(x)                (sizeof(x) / sizeof((x)[0]))
> > > > > > > > > +
> > > > > > > > > +const char *capsule_params[] = {
> > > > > > > > > +     "image-guid", "image-index", "private-key",
> > > > > > > > > +     "pub-key-cert", "payload", "capsule",
> > > > > > > > > +     "hardware-instance", "monotonic-count",
> > > > > > > > > +     "capsule-type", "oemflags" };
> > > > > > > > > +
> > > > > > > > > +static unsigned char params_start;
> > > > > > > > > +static unsigned char params_end;
> > > > > > > > > +
> > > > > > > > > +static void print_and_exit(const char *str)
> > > > > > > > > +{
> > > > > > > > > +     fprintf(stderr, "%s", str);
> > > > > > > > > +     exit(EXIT_FAILURE);
> > > > > > > > > +}
> > > > > > > > > +
> > > > > > > > > +static int param_delim_checks(char *line, unsigned char *token)
> > > > > > > > > +{
> > > > > > > > > +     if (!strcmp(line, PARAMS_START)) {
> > > > > > > > > +             if (params_start || !params_end) {
> > > > > > > > > +                     fprintf(stderr, "Earlier params processing still in progress. ");
> > > > > > > > > +                     fprintf(stderr, "Can't start processing a new params.\n");
> > > > > > > > > +                     exit(EXIT_FAILURE);
> > > > > > > > > +             } else {
> > > > > > > > > +                     params_start = 1;
> > > > > > > > > +                     params_end = 0;
> > > > > > > > > +                     *token = PSTART;
> > > > > > > > > +                     return 1;
> > > > > > > > > +             }
> > > > > > > > > +     } else if (!strcmp(line, PARAMS_END)) {
> > > > > > > > > +             if (!params_start) {
> > > > > > > > > +                     fprintf(stderr, "Cannot put end braces without start braces. ");
> > > > > > > > > +                     fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > > > > > > +                     exit(EXIT_FAILURE);
> > > > > > > > > +             } else {
> > > > > > > > > +                     params_start = 0;
> > > > > > > > > +                     params_end = 1;
> > > > > > > > > +                     *token = PEND;
> > > > > > > > > +                     return 1;
> > > > > > > > > +             }
> > > > > > > > > +     } else if (!params_start) {
> > > > > > > > > +             fprintf(stderr, "Params should be passed within braces. ");
> > > > > > > > > +             fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > > > > > > +             exit(EXIT_FAILURE);
> > > > > > > > > +     }
> > > > > > > > > +
> > > > > > > > > +     return 0;
> > > > > > > > > +}
> > > > > > > > > +
> > > > > > > > > +static void add_guid(efi_guid_t **guid_param, char *guid)
> > > > > > > > > +{
> > > > > > > > > +     unsigned char uuid_buf[16];
> > > > > > > > > +
> > > > > > > > > +     *guid_param = malloc(sizeof(efi_guid_t));
> > > > > > > > > +     if (!*guid_param)
> > > > > > > > > +             print_and_exit(MALLOC_FAIL_STR);
> > > > > > > > > +
> > > > > > > > > +     if (uuid_parse(guid, uuid_buf))
> > > > > > > > > +             print_and_exit("Wrong guid format\n");
> > > > > > > > > +
> > > > > > > > > +     convert_uuid_to_guid(uuid_buf);
> > > > > > > > > +     memcpy(*guid_param, uuid_buf, sizeof(efi_guid_t));
> > > > > > > > > +}
> > > > > > > > > +
> > > > > > > > > +static void add_string(char **dst, char *val)
> > > > > > > > > +{
> > > > > > > > > +     *dst = strdup(val);
> > > > > > > > > +     if (!*dst)
> > > > > > > > > +             print_and_exit(MALLOC_FAIL_STR);
> > > > > > > > > +}
> > > > > > > > > +
> > > > > > > > > +static void match_and_populate_param(char *key, char *val,
> > > > > > > > > +                                  struct efi_capsule_params *param)
> > > > > > > > > +{
> > > > > > > > > +     int i;
> > > > > > > > > +
> > > > > > > > > +     for (i = 0; i < ARRAY_SIZE(capsule_params); i++) {
> > > > > > > > > +             if (!strcmp(key, capsule_params[i])) {
> > > > > > > > > +                     switch (i) {
> > > > > > > > > +                     case 0:
> > > > > > > > > +                             add_guid(&param->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(&param->privkey_file, val);
> > > > > > > > > +                             return;
> > > > > > > > > +                     case 3:
> > > > > > > > > +                             add_string(&param->cert_file, val);
> > > > > > > > > +                             return;
> > > > > > > > > +                     case 4:
> > > > > > > > > +                             add_string(&param->input_file, val);
> > > > > > > > > +                             return;
> > > > > > > > > +                     case 5:
> > > > > > > > > +                             add_string(&param->capsule_file, val);
> > > > > > > > > +                             return;
> > > > > > > > > +                     case 6:
> > > > > > > > > +                             param->hardware_instance = strtoul(val, NULL, 0);
> > > > > > > > > +                             if (param->hardware_instance == ULONG_MAX)
> > > > > > > > > +                                     print_and_exit("Enter a valid hardware instance value");
> > > > > > > > > +                             return;
> > > > > > > > > +                     case 7:
> > > > > > > > > +                             param->monotonic_count = strtoull(val, NULL, 0);
> > > > > > > > > +                             if (param->monotonic_count == ULLONG_MAX)
> > > > > > > > > +                                     print_and_exit("Enter a valid monotonic count value");
> > > > > > > > > +                             return;
> > > > > > > > > +                     case 8:
> > > > > > > > > +                             if (!strcmp(val, "normal"))
> > > > > > > > > +                                     param->capsule = CAPSULE_NORMAL_BLOB;
> > > > > > > > > +                             else if (!strcmp(val, "accept"))
> > > > > > > > > +                                     param->capsule = CAPSULE_ACCEPT;
> > > > > > > > > +                             else if (!strcmp(val, "revert"))
> > > > > > > > > +                                     param->capsule = CAPSULE_REVERT;
> > > > > > > > > +                             else
> > > > > > > > > +                                     print_and_exit("Invalid type of capsule");
> > > > > > > > > +
> > > > > > > > > +                             return;
> > > > > > > > > +                     case 9:
> > > > > > > > > +                             param->oemflags = strtoul(val, NULL, 0);
> > > > > > > > > +                             if (param->oemflags > 0xffff)
> > > > > > > > > +                                     print_and_exit("OemFlags must be between 0x0 and 0xffff\n");
> > > > > > > > > +                             return;
> > > > > > > > > +                     }
> > > > > > > > > +             }
> > > > > > > > > +     }
> > > > > > > > > +
> > > > > > > > > +     fprintf(stderr, "Undefined param %s specified. ", key);
> > > > > > > > > +     fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > > > > > > +     exit(EXIT_FAILURE);
> > > > > > > > > +}
> > > > > > > > > +
> > > > > > > > > +static int get_capsule_params(char *line, struct efi_capsule_params *params)
> > > > > > > > > +{
> > > > > > > > > +     char *key = NULL;
> > > > > > > > > +     char *val = NULL;
> > > > > > > > > +     unsigned char token;
> > > > > > > > > +
> > > > > > > > > +     if (param_delim_checks(line, &token))
> > > > > > > > > +             return token;
> > > > > > > > > +
> > > > > > > > > +     key = strtok(line, ":");
> > > > > > > > > +     if (key)
> > > > > > > > > +             val = strtok(NULL, "\0");
> > > > > > > > > +     else
> > > > > > > > > +             print_and_exit("Expect the params in a key:value pair\n");
> > > > > > > > > +
> > > > > > > > > +     match_and_populate_param(key, val, params);
> > > > > > > > > +
> > > > > > > > > +     return 0;
> > > > > > > > > +}
> > > > > > > > > +
> > > > > > > > > +static char *skip_whitespace(char *line)
> > > > > > > > > +{
> > > > > > > > > +     char *ptr, *newline;
> > > > > > > > > +
> > > > > > > > > +     ptr = malloc(strlen(line) + 1);
> > > > > > > > > +     if (!ptr)
> > > > > > > > > +             print_and_exit(MALLOC_FAIL_STR);
> > > > > > > > > +
> > > > > > > > > +     for (newline = ptr; *line; line++)
> > > > > > > > > +             if (!isblank(*line))
> > > > > > > > > +                     *ptr++ = *line;
> > > > > > > > > +     *ptr = '\0';
> > > > > > > > > +     return newline;
> > > > > > > > > +}
> > > > > > > > > +
> > > > > > > > > +static int parse_capsule_payload_params(FILE *fp, struct efi_capsule_params *params)
> > > > > > > > > +{
> > > > > > > > > +     char *line = NULL;
> > > > > > > > > +     char *newline;
> > > > > > > > > +     size_t n = 0;
> > > > > > > > > +     ssize_t len;
> > > > > > > > > +
> > > > > > > > > +     while ((len = getline(&line, &n, fp)) != -1) {
> > > > > > > > > +             if (len == 1 && line[len - 1] == '\n')
> > > > > > > > > +                     continue;
> > > > > > > > > +
> > > > > > > > > +             line[len - 1] = '\0';
> > > > > > > > > +
> > > > > > > > > +             newline = skip_whitespace(line);
> > > > > > > > > +
> > > > > > > > > +             if (newline[0] == '#')
> > > > > > > > > +                     continue;
> > > > > > > > > +
> > > > > > > > > +             if (get_capsule_params(newline, params) == PEND)
> > > > > > > > > +                     return 0;
> > > > > > > > > +     }
> > > > > > > > > +
> > > > > > > > > +     if (errno == EINVAL || errno == ENOMEM) {
> > > > > > > > > +             fprintf(stderr, "getline() returned an error %s reading the line\n",
> > > > > > > > > +                     strerror(errno));
> > > > > > > > > +             exit(EXIT_FAILURE);
> > > > > > > > > +     } else if (params_start == 1 || params_end == 0) {
> > > > > > > > > +             fprintf(stderr, "Params should be passed within braces. ");
> > > > > > > > > +             fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > > > > > > +             exit(EXIT_FAILURE);
> > > > > > > > > +     } else {
> > > > > > > > > +             return -1;
> > > > > > > > > +     }
> > > > > > > > > +}
> > > > > > > > > +
> > > > > > > > > +static void params_dependency_check(struct efi_capsule_params *params)
> > > > > > > > > +{
> > > > > > > > > +     /* check necessary parameters */
> > > > > > > > > +     if ((params->capsule == CAPSULE_NORMAL_BLOB &&
> > > > > > > > > +          ((!params->input_file || !params->capsule_file ||
> > > > > > > > > +            !params->image_guid) ||
> > > > > > > > > +           ((params->privkey_file && !params->cert_file) ||
> > > > > > > > > +            (!params->privkey_file && params->cert_file)))) ||
> > > > > > > > > +         (params->capsule != CAPSULE_NORMAL_BLOB &&
> > > > > > > > > +          (!params->capsule_file ||
> > > > > > > > > +           (params->capsule == CAPSULE_ACCEPT && !params->image_guid) ||
> > > > > > > > > +           (params->capsule == CAPSULE_REVERT && params->image_guid)))) {
> > > > > > > > > +             print_usage();
> > > > > > > > > +             exit(EXIT_FAILURE);
> > > > > > > > > +     }
> > > > > > > > > +}
> > > > > > > > > +
> > > > > > > > > +static void generate_capsule(struct efi_capsule_params *params)
> > > > > > > > > +{
> > > > > > > > > +     if (params->capsule != CAPSULE_NORMAL_BLOB) {
> > > > > > > > > +             if (create_empty_capsule(params->capsule_file,
> > > > > > > > > +                                      params->image_guid,
> > > > > > > > > +                                      params->capsule ==
> > > > > > > > > +                                      CAPSULE_ACCEPT) < 0)
> > > > > > > > > +                     print_and_exit("Creating empty capsule failed\n");
> > > > > > > > > +     } else if (create_fwbin(params->capsule_file, params->input_file,
> > > > > > > > > +                           params->image_guid, params->image_index,
> > > > > > > > > +                           params->hardware_instance,
> > > > > > > > > +                           params->monotonic_count,
> > > > > > > > > +                           params->privkey_file,
> > > > > > > > > +                           params->cert_file,
> > > > > > > > > +                           (uint16_t)params->oemflags) < 0) {
> > > > > > > > > +             print_and_exit("Creating firmware capsule failed\n");
> > > > > > > > > +     }
> > > > > > > > > +}
> > > > > > > > > +
> > > > > > > > > +/**
> > > > > > > > > + * capsule_with_cfg_file() - Generate capsule from config file
> > > > > > > > > + * @cfg_file: Path to the config file
> > > > > > > > > + *
> > > > > > > > > + * Parse the capsule parameters from the config file and use the
> > > > > > > > > + * parameters for generating one or more capsules.
> > > > > > > > > + *
> > > > > > > > > + * Return: None
> > > > > > > > > + *
> > > > > > > > > + */
> > > > > > > > > +void capsule_with_cfg_file(const char *cfg_file)
> > > > > > > > > +{
> > > > > > > > > +     FILE *fp;
> > > > > > > > > +     struct efi_capsule_params params = { 0 };
> > > > > > > > > +
> > > > > > > > > +     fp = fopen(cfg_file, "r");
> > > > > > > > > +     if (!fp) {
> > > > > > > > > +             fprintf(stderr, "Unable to open the capsule config file %s\n",
> > > > > > > > > +                     cfg_file);
> > > > > > > > > +             exit(EXIT_FAILURE);
> > > > > > > > > +     }
> > > > > > > > > +
> > > > > > > > > +     params_start = 0;
> > > > > > > > > +     params_end = 1;
> > > > > > > > > +
> > > > > > > > > +     while (parse_capsule_payload_params(fp, &params) != -1) {
> > > > > > > > > +             params_dependency_check(&params);
> > > > > > > > > +             generate_capsule(&params);
> > > > > > > > > +
> > > > > > > > > +             memset(&params, 0, sizeof(struct efi_capsule_params));
> > > > > > > > > +     }
> > > > > > > > > +}
> > > > > > > > > --
> > > > > > > > > 2.34.1
> > > > > > > > >

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 4/7] tools: mkeficapsule: Add support for parsing capsule params from config file
  2023-06-16  5:18                   ` Takahiro Akashi
@ 2023-06-16  6:35                     ` Sughosh Ganu
  2023-06-16 13:12                       ` Schmidt, Malte
  0 siblings, 1 reply; 50+ messages in thread
From: Sughosh Ganu @ 2023-06-16  6:35 UTC (permalink / raw)
  To: Takahiro Akashi, Sughosh Ganu, u-boot, Heinrich Schuchardt,
	Ilias Apalodimas, Simon Glass, Michal Simek

On Fri, 16 Jun 2023 at 10:48, Takahiro Akashi
<takahiro.akashi@linaro.org> wrote:
>
> On Fri, Jun 16, 2023 at 10:37:01AM +0530, Sughosh Ganu wrote:
> > hi Takahiro,
> >
> > On Fri, 16 Jun 2023 at 10:16, Takahiro Akashi
> > <takahiro.akashi@linaro.org> wrote:
> > >
> > > Hi Sughosh,
> > >
> > > On Fri, Jun 16, 2023 at 09:56:33AM +0530, Sughosh Ganu wrote:
> > > > On Thu, 15 Jun 2023 at 11:19, Takahiro Akashi
> > > > <takahiro.akashi@linaro.org> wrote:
> > > > >
> > > > > On Thu, Jun 15, 2023 at 10:09:06AM +0530, Sughosh Ganu wrote:
> > > > > > On Wed, 14 Jun 2023 at 11:23, Takahiro Akashi
> > > > > > <takahiro.akashi@linaro.org> wrote:
> > > > > > >
> > > > > > > On Wed, Jun 14, 2023 at 10:56:23AM +0530, Sughosh Ganu wrote:
> > > > > > > > hi Takahiro,
> > > > > > > >
> > > > > > > > On Wed, 14 Jun 2023 at 09:09, Takahiro Akashi
> > > > > > > > <takahiro.akashi@linaro.org> wrote:
> > > > > > > > >
> > > > > > > > > Hi Sughosh,
> > > > > > > > >
> > > > > > > > > I think this is a good extension to mkeficapsule, but
> > > > > > > > >
> > > > > > > > > On Tue, Jun 13, 2023 at 04:08:03PM +0530, Sughosh Ganu wrote:
> > > > > > > > > > Add support for specifying the parameters needed for capsule
> > > > > > > > > > generation through a config file, instead of passing them through
> > > > > > > > > > command-line. Parameters for more than a single capsule file can be
> > > > > > > > > > specified, resulting in generation of multiple capsules through a
> > > > > > > > > > single invocation of the command.
> > > > > > > > > >
> > > > > > > > > > This path is to be used for generating capsules through a make target,
> > > > > > > > > > with the parameters being parsed from the config file.
> > > > > > > > > >
> > > > > > > > > > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > > > > > > > > > ---
> > > > > > > > > >  tools/Kconfig              |   9 +
> > > > > > > > > >  tools/Makefile             |   1 +
> > > > > > > > > >  tools/eficapsule.h         | 110 ++++++++++++
> > > > > > > > > >  tools/mkeficapsule.c       | 106 +++++++-----
> > > > > > > > > >  tools/mkeficapsule_parse.c | 345 +++++++++++++++++++++++++++++++++++++
> > > > > > > > > >  5 files changed, 531 insertions(+), 40 deletions(-)
> > > > > > > > > >  create mode 100644 tools/mkeficapsule_parse.c
> > > > > > > > > >
> > > > > > > > > > diff --git a/tools/Kconfig b/tools/Kconfig
> > > > > > > > > > index 539708f277..95f27b7c45 100644
> > > > > > > > > > --- a/tools/Kconfig
> > > > > > > > > > +++ b/tools/Kconfig
> > > > > > > > > > @@ -98,6 +98,15 @@ config TOOLS_MKEFICAPSULE
> > > > > > > > > >         optionally sign that file. If you want to enable UEFI capsule
> > > > > > > > > >         update feature on your target, you certainly need this.
> > > > > > > > > >
> > > > > > > > > > +config EFI_CAPSULE_CFG_FILE
> > > > > > > > > > +     string "Path to the EFI Capsule Config File"
> > > > > > > > > > +     default ""
> > > > > > > > > > +     help
> > > > > > > > > > +       Path to the EFI capsule config file which provides the
> > > > > > > > > > +       parameters needed to build capsule(s). Parameters can be
> > > > > > > > > > +       provided for multiple payloads resulting in corresponding
> > > > > > > > > > +       capsule images being generated.
> > > > > > > > > > +
> > > > > > > > > >  menuconfig FSPI_CONF_HEADER
> > > > > > > > > >       bool "FlexSPI Header Configuration"
> > > > > > > > > >       help
> > > > > > > > > > diff --git a/tools/Makefile b/tools/Makefile
> > > > > > > > > > index d793cf3bec..ef366f3d61 100644
> > > > > > > > > > --- a/tools/Makefile
> > > > > > > > > > +++ b/tools/Makefile
> > > > > > > > > > @@ -250,6 +250,7 @@ HOSTLDLIBS_mkeficapsule += \
> > > > > > > > > >  HOSTLDLIBS_mkeficapsule += \
> > > > > > > > > >       $(shell pkg-config --libs uuid 2> /dev/null || echo "-luuid")
> > > > > > > > > >  hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule
> > > > > > > > > > +mkeficapsule-objs := mkeficapsule.o mkeficapsule_parse.o
> > > > > > > > > >
> > > > > > > > > >  # We build some files with extra pedantic flags to try to minimize things
> > > > > > > > > >  # that won't build on some weird host compiler -- though there are lots of
> > > > > > > > > > diff --git a/tools/eficapsule.h b/tools/eficapsule.h
> > > > > > > > > > index 072a4b5598..42e66c6d6a 100644
> > > > > > > > > > --- a/tools/eficapsule.h
> > > > > > > > > > +++ b/tools/eficapsule.h
> > > > > > > > > > @@ -52,6 +52,38 @@ typedef struct {
> > > > > > > > > >  /* flags */
> > > > > > > > > >  #define CAPSULE_FLAGS_PERSIST_ACROSS_RESET      0x00010000
> > > > > > > > > >
> > > > > > > > > > +enum capsule_type {
> > > > > > > > > > +     CAPSULE_NORMAL_BLOB = 0,
> > > > > > > > > > +     CAPSULE_ACCEPT,
> > > > > > > > > > +     CAPSULE_REVERT,
> > > > > > > > > > +};
> > > > > > > > > > +
> > > > > > > > > > +/**
> > > > > > > > > > + * struct efi_capsule_params - Capsule parameters
> > > > > > > > > > + * @image_guid: Guid value of the payload input image
> > > > > > > > > > + * @image_index: Image index value
> > > > > > > > > > + * @hardware_instance: Hardware instance to be used for the image
> > > > > > > > > > + * @monotonic_count: Monotonic count value to be used for signed capsule
> > > > > > > > > > + * @privkey_file: Path to private key used in capsule signing
> > > > > > > > > > + * @cert_file: Path to public key certificate used in capsule signing
> > > > > > > > > > + * @input_file: Path to payload input image
> > > > > > > > > > + * @capsule_file: Path to the output capsule file
> > > > > > > > > > + * @oemflags: Oemflags to be populated in the capsule header
> > > > > > > > > > + * @capsule: Capsule Type, normal or accept or revert
> > > > > > > > > > + */
> > > > > > > > > > +struct efi_capsule_params {
> > > > > > > > > > +     efi_guid_t *image_guid;
> > > > > > > > > > +     unsigned long image_index;
> > > > > > > > > > +     unsigned long hardware_instance;
> > > > > > > > > > +     uint64_t monotonic_count;
> > > > > > > > > > +     char *privkey_file;
> > > > > > > > > > +     char *cert_file;
> > > > > > > > > > +     char *input_file;
> > > > > > > > > > +     char *capsule_file;
> > > > > > > > > > +     unsigned long oemflags;
> > > > > > > > > > +     enum capsule_type capsule;
> > > > > > > > > > +};
> > > > > > > > > > +
> > > > > > > > > >  struct efi_capsule_header {
> > > > > > > > > >       efi_guid_t capsule_guid;
> > > > > > > > > >       uint32_t header_size;
> > > > > > > > > > @@ -113,4 +145,82 @@ struct efi_firmware_image_authentication {
> > > > > > > > > >       struct win_certificate_uefi_guid auth_info;
> > > > > > > > > >  } __packed;
> > > > > > > > > >
> > > > > > > > > > +/**
> > > > > > > > > > + * capsule_with_cfg_file() - Generate capsule from config file
> > > > > > > > > > + * @cfg_file: Path to the config file
> > > > > > > > > > + *
> > > > > > > > > > + * Parse the capsule parameters from the config file and use the
> > > > > > > > > > + * parameters for generating one or more capsules.
> > > > > > > > > > + *
> > > > > > > > > > + * Return: None
> > > > > > > > > > + *
> > > > > > > > > > + */
> > > > > > > > > > +void capsule_with_cfg_file(const char *cfg_file);
> > > > > > > > > > +
> > > > > > > > > > +/**
> > > > > > > > > > + * convert_uuid_to_guid() - convert UUID to GUID
> > > > > > > > > > + * @buf:     UUID binary
> > > > > > > > > > + *
> > > > > > > > > > + * UUID and GUID have the same data structure, but their binary
> > > > > > > > > > + * formats are different due to the endianness. See lib/uuid.c.
> > > > > > > > > > + * Since uuid_parse() can handle only UUID, this function must
> > > > > > > > > > + * be called to get correct data for GUID when parsing a string.
> > > > > > > > > > + *
> > > > > > > > > > + * The correct data will be returned in @buf.
> > > > > > > > > > + */
> > > > > > > > > > +void convert_uuid_to_guid(unsigned char *buf);
> > > > > > > > > > +
> > > > > > > > > > +/**
> > > > > > > > > > + * create_empty_capsule() - Generate an empty capsule
> > > > > > > > > > + * @path: Path to the empty capsule file to be generated
> > > > > > > > > > + * @guid: Guid value of the image for which empty capsule is generated
> > > > > > > > > > + * @fw_accept: Flag to specify whether to generate accept or revert capsule
> > > > > > > > > > + *
> > > > > > > > > > + * Generate an empty capsule, either an accept or a revert capsule to be
> > > > > > > > > > + * used to flag acceptance or rejection of an earlier executed firmware
> > > > > > > > > > + * update operation. Being used in the FWU Multi Bank firmware update
> > > > > > > > > > + * feature.
> > > > > > > > > > + *
> > > > > > > > > > + * Return: 0 if OK, -ve on error
> > > > > > > > > > + *
> > > > > > > > > > + */
> > > > > > > > > > +int create_empty_capsule(char *path, efi_guid_t *guid, bool fw_accept);
> > > > > > > > > > +
> > > > > > > > > > +/**
> > > > > > > > > > + * create_fwbin - create an uefi capsule file
> > > > > > > > > > + * @path:    Path to a created capsule file
> > > > > > > > > > + * @bin:     Path to a firmware binary to encapsulate
> > > > > > > > > > + * @guid:    GUID of related FMP driver
> > > > > > > > > > + * @index:   Index number in capsule
> > > > > > > > > > + * @instance:        Instance number in capsule
> > > > > > > > > > + * @mcount:  Monotonic count in authentication information
> > > > > > > > > > + * @private_file:    Path to a private key file
> > > > > > > > > > + * @cert_file:       Path to a certificate file
> > > > > > > > > > + * @oemflags:  Capsule OEM Flags, bits 0-15
> > > > > > > > > > + *
> > > > > > > > > > + * This function actually does the job of creating an uefi capsule file.
> > > > > > > > > > + * All the arguments must be supplied.
> > > > > > > > > > + * If either @private_file ror @cert_file is NULL, the capsule file
> > > > > > > > > > + * won't be signed.
> > > > > > > > > > + *
> > > > > > > > > > + * Return:
> > > > > > > > > > + * * 0  - on success
> > > > > > > > > > + * * -1 - on failure
> > > > > > > > > > + */
> > > > > > > > > > +int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > > > > > > > +              unsigned long index, unsigned long instance,
> > > > > > > > > > +              uint64_t mcount, char *privkey_file, char *cert_file,
> > > > > > > > > > +              uint16_t oemflags);
> > > > > > > > > > +
> > > > > > > > > > +/**
> > > > > > > > > > + * print_usage() - Print the command usage string
> > > > > > > > > > + *
> > > > > > > > > > + * Prints the standard command usage string. Called in the case
> > > > > > > > > > + * of incorrect parameters being passed to the tool.
> > > > > > > > > > + *
> > > > > > > > > > + * Return: None
> > > > > > > > > > + *
> > > > > > > > > > + */
> > > > > > > > > > +void print_usage(void);
> > > > > > > > > > +
> > > > > > > > > >  #endif /* _EFI_CAPSULE_H */
> > > > > > > > > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> > > > > > > > > > index b71537beee..711adf0439 100644
> > > > > > > > > > --- a/tools/mkeficapsule.c
> > > > > > > > > > +++ b/tools/mkeficapsule.c
> > > > > > > > > > @@ -31,12 +31,6 @@ 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 struct option options[] = {
> > > > > > > > > >       {"guid", required_argument, NULL, 'g'},
> > > > > > > > > >       {"index", required_argument, NULL, 'i'},
> > > > > > > > > > @@ -52,7 +46,16 @@ static struct option options[] = {
> > > > > > > > > >       {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"
> > > > > > > > > > @@ -400,10 +403,10 @@ static void free_sig_data(struct auth_context *ctx)
> > > > > > > > > >   * * 0  - on success
> > > > > > > > > >   * * -1 - on failure
> > > > > > > > > >   */
> > > > > > > > > > -static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > > > > > > > -                     unsigned long index, unsigned long instance,
> > > > > > > > > > -                     uint64_t mcount, char *privkey_file, char *cert_file,
> > > > > > > > > > -                     uint16_t oemflags)
> > > > > > > > > > +int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > > > > > > > +              unsigned long index, unsigned long instance,
> > > > > > > > > > +              uint64_t mcount, char *privkey_file, char *cert_file,
> > > > > > > > > > +              uint16_t oemflags)
> > > > > > > > > >  {
> > > > > > > > > >       struct efi_capsule_header header;
> > > > > > > > > >       struct efi_firmware_management_capsule_header capsule;
> > > > > > > > > > @@ -580,7 +583,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;
> > > > > > > > > > @@ -623,19 +640,7 @@ err:
> > > > > > > > > >       return ret;
> > > > > > > > > >  }
> > > > > > > > > >
> > > > > > > > > > -/**
> > > > > > > > > > - * main - main entry function of mkeficapsule
> > > > > > > > > > - * @argc:    Number of arguments
> > > > > > > > > > - * @argv:    Array of pointers to arguments
> > > > > > > > > > - *
> > > > > > > > > > - * Create an uefi capsule file, optionally signing it.
> > > > > > > > > > - * Parse all the arguments and pass them on to create_fwbin().
> > > > > > > > > > - *
> > > > > > > > > > - * Return:
> > > > > > > > > > - * * 0  - on success
> > > > > > > > > > - * * -1 - on failure
> > > > > > > > > > - */
> > > > > > > > > > -int main(int argc, char **argv)
> > > > > > > > > > +static void capsule_with_cmdline_params(int argc, char **argv)
> > > > > > > > > >  {
> > > > > > > > > >       efi_guid_t *guid;
> > > > > > > > > >       unsigned char uuid_buf[16];
> > > > > > > > > > @@ -643,6 +648,7 @@ int main(int argc, char **argv)
> > > > > > > > > >       uint64_t mcount;
> > > > > > > > > >       unsigned long oemflags;
> > > > > > > > > >       char *privkey_file, *cert_file;
> > > > > > > > > > +     enum capsule_type capsule;
> > > > > > > > > >       int c, idx;
> > > > > > > > > >
> > > > > > > > > >       guid = NULL;
> > > > > > > > > > @@ -652,7 +658,7 @@ int main(int argc, char **argv)
> > > > > > > > > >       privkey_file = NULL;
> > > > > > > > > >       cert_file = NULL;
> > > > > > > > > >       dump_sig = 0;
> > > > > > > > > > -     capsule_type = CAPSULE_NORMAL_BLOB;
> > > > > > > > > > +     capsule = CAPSULE_NORMAL_BLOB;
> > > > > > > > > >       oemflags = 0;
> > > > > > > > > >       for (;;) {
> > > > > > > > > >               c = getopt_long(argc, argv, opts_short, options, &idx);
> > > > > > > > > > @@ -702,20 +708,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);
> > > > > > > > > > @@ -732,21 +738,21 @@ int main(int argc, char **argv)
> > > > > > > > > >       }
> > > > > > > > > >
> > > > > > > > > >       /* check necessary parameters */
> > > > > > > > > > -     if ((capsule_type == CAPSULE_NORMAL_BLOB &&
> > > > > > > > > > -         ((argc != optind + 2) || !guid ||
> > > > > > > > > > -          ((privkey_file && !cert_file) ||
> > > > > > > > > > -           (!privkey_file && cert_file)))) ||
> > > > > > > > > > -         (capsule_type != CAPSULE_NORMAL_BLOB &&
> > > > > > > > > > -         ((argc != optind + 1) ||
> > > > > > > > > > -          ((capsule_type == CAPSULE_ACCEPT) && !guid) ||
> > > > > > > > > > -          ((capsule_type == CAPSULE_REVERT) && guid)))) {
> > > > > > > > > > +     if ((capsule == CAPSULE_NORMAL_BLOB &&
> > > > > > > > > > +          ((argc != optind + 2) || !guid ||
> > > > > > > > > > +           ((privkey_file && !cert_file) ||
> > > > > > > > > > +            (!privkey_file && cert_file)))) ||
> > > > > > > > > > +         (capsule != CAPSULE_NORMAL_BLOB &&
> > > > > > > > > > +          ((argc != optind + 1) ||
> > > > > > > > > > +           (capsule == CAPSULE_ACCEPT && !guid) ||
> > > > > > > > > > +           (capsule == CAPSULE_REVERT && guid)))) {
> > > > > > > > > >               print_usage();
> > > > > > > > > >               exit(EXIT_FAILURE);
> > > > > > > > > >       }
> > > > > > > > > >
> > > > > > > > > > -     if (capsule_type != CAPSULE_NORMAL_BLOB) {
> > > > > > > > > > +     if (capsule != CAPSULE_NORMAL_BLOB) {
> > > > > > > > > >               if (create_empty_capsule(argv[argc - 1], guid,
> > > > > > > > > > -                                      capsule_type == CAPSULE_ACCEPT) < 0) {
> > > > > > > > > > +                                      capsule == CAPSULE_ACCEPT) < 0) {
> > > > > > > > > >                       fprintf(stderr, "Creating empty capsule failed\n");
> > > > > > > > > >                       exit(EXIT_FAILURE);
> > > > > > > > > >               }
> > > > > > > > > > @@ -756,6 +762,26 @@ int main(int argc, char **argv)
> > > > > > > > > >               fprintf(stderr, "Creating firmware capsule failed\n");
> > > > > > > > > >               exit(EXIT_FAILURE);
> > > > > > > > > >       }
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +/**
> > > > > > > > > > + * main - main entry function of mkeficapsule
> > > > > > > > > > + * @argc:    Number of arguments
> > > > > > > > > > + * @argv:    Array of pointers to arguments
> > > > > > > > > > + *
> > > > > > > > > > + * Create an uefi capsule file, optionally signing it.
> > > > > > > > > > + * Parse all the arguments and pass them on to create_fwbin().
> > > > > > > > > > + *
> > > > > > > > > > + * Return:
> > > > > > > > > > + * * 0  - on success
> > > > > > > > > > + * * -1 - on failure
> > > > > > > > > > + */
> > > > > > > > > > +int main(int argc, char **argv)
> > > > > > > > > > +{
> > > > > > > > > > +     if (!strcmp(CONFIG_EFI_CAPSULE_CFG_FILE, ""))
> > > > > > > > > > +             capsule_with_cmdline_params(argc, argv);
> > > > > > > > > > +     else
> > > > > > > > > > +             capsule_with_cfg_file(CONFIG_EFI_CAPSULE_CFG_FILE);
> > > > > > > > >
> > > > > > > > > I don't know where the macro, CONFIG_EFI_CAPSULE_CFG_FILE, comes from.
> > > > > > > > > Anyhow, as a general rule, any host tool must be as generic as it should not
> > > > > > > > > depend on a target's config.
> > > > > > > > > (I was told so before.)
> > > > > > > > >
> > > > > > > > > So I would suggest that you add another command line, say "--config-file <file>",
> > > > > > > > > to make the command generic.
> > > > > > > >
> > > > > > > > Yes, that would be something followed by most of the tools. The reason
> > > > > > > > I did not add a command-line option for the confile file is because I
> > > > > > > > want the capsule generation added as a make target. With the path to
> > > > > > > > the config file specified through the Kconfig symbol, we can invoke
> > > > > > > > 'make capsule', and it would build the capsules by parsing the
> > > > > > > > parameters from the config file, taken from the Kconfig symbol. I know
> > > > > > > > there are ways of specifying options when using a make command, but I
> > > > > > > > don't think that is a clean way of doing things.
> > > > > > >
> > > > > > > Not sure, but in your [5/7],
> > > > > > > cmd_mkeficapsule = $(objtree)/tools/mkeficapsule --config-file $(CONFIG_EFI_CAPSULE_CFG_FILE)
> > > > > > >
> > > > > > > Doesn't this change work?
> > > > > >
> > > > > > So, I tried the above suggested change. But trying to run a make
> > > > > > 'target' does not work without the .config file being present.
> > > > >
> > > > > Not sure what you meant to say here.
> > > > > Why don't you have .config when building U-Boot (or rather 'target')?
> > > >
> > > > Maybe I misunderstood your earlier comment, but I thought you were
> > > > looking to build capsules without relying on a target config.
> > >
> > > Not exactly.
> > > The basic requirement, I believe, is that the exact same binary (with
> > > the same set of functionalities) should be generated for any host tool
> > > whatever a target's config, including tools-only_defconfig, is.
> >
> > Okay. I think I now understand what you are looking for. However, I
> > believe if you want the same binary for both scenarios, the only way
> > might be to drop the make target to generate capsules, and do it
> > through the --config-file command-line option.
>
> Again not sure what you're trying to do.

Never mind. It works with the cfg-file being passed as a command-line
parameter. I will make the change for the next version.

-sughosh

>
> > I will check if we can
> > pass the config file as a parameter when building capsules as a
> > target. I could not get it working when I tried it earlier though. If
> > this is indeed not possible, do you have a strong opinion on having
> > the same binary for both scenarios?
>
>  I have no reason why you can't.
>  If you see any failure, please give me more details about how
>  you are going to manage so that I can help you.
>
>  -Takahiro Akashi
>
> > -sughosh
> >
> > >
> > > Is it clear now?
> > >
> > > -Takahiro Akashi
> > >
> > > > Which I
> > > > believe cannot be done for a make target.
> > > >
> > > > -sughosh
> > > >
> > > > >
> > > > > -Takahiro Akashi
> > > > >
> > > > > > FWIW,
> > > > > > the same is the case for building tools as well. I think that is the
> > > > > > reason for the tools-only_defconfig.
> > > > > >
> > > > > > -sughosh
> > > > > >
> > > > > > >
> > > > > > > -Takahiro Akashi
> > > > > > >
> > > > > > >
> > > > > > > > Given the use case of
> > > > > > > > a make target, I hope we can use the Kconfig symbol for specifying the
> > > > > > > > config file path.
> > > > > > > >
> > > > > > > > -sughosh
> > > > > > > >
> > > > > > > > >
> > > > > > > > > -Takahiro Akashi
> > > > > > > > >
> > > > > > > > >
> > > > > > > > > >
> > > > > > > > > >       exit(EXIT_SUCCESS);
> > > > > > > > > >  }
> > > > > > > > > > diff --git a/tools/mkeficapsule_parse.c b/tools/mkeficapsule_parse.c
> > > > > > > > > > new file mode 100644
> > > > > > > > > > index 0000000000..ef4f3f6705
> > > > > > > > > > --- /dev/null
> > > > > > > > > > +++ b/tools/mkeficapsule_parse.c
> > > > > > > > > > @@ -0,0 +1,345 @@
> > > > > > > > > > +// SPDX-License-Identifier: GPL-2.0
> > > > > > > > > > +/*
> > > > > > > > > > + * Copyright 2023 Linaro Limited
> > > > > > > > > > + */
> > > > > > > > > > +
> > > > > > > > > > +/*
> > > > > > > > > > + * The code in this file adds parsing ability to the mkeficapsule
> > > > > > > > > > + * tool. This allows specifying parameters needed to build the capsule
> > > > > > > > > > + * through the config file instead of specifying them on the command-line.
> > > > > > > > > > + * Parameters can be specified for more than one payload, generating the
> > > > > > > > > > + * corresponding capsule files.
> > > > > > > > > > + *
> > > > > > > > > > + * The parameters are specified in a "key:value" pair. All the parameters
> > > > > > > > > > + * that are currently supported by the mkeficapsule tool can be specified
> > > > > > > > > > + * in the config file.
> > > > > > > > > > + *
> > > > > > > > > > + * The example below shows four payloads. The first payload is an example
> > > > > > > > > > + * of generating a signed capsule. The second payload is an example of
> > > > > > > > > > + * generating an unsigned capsule. The third payload is an accept empty
> > > > > > > > > > + * capsule, while the fourth payload is the revert empty capsule, used
> > > > > > > > > > + * for the multi-bank firmware update feature.
> > > > > > > > > > + *
> > > > > > > > > > + * This functionality can be easily extended to generate a single capsule
> > > > > > > > > > + * comprising multiple payloads.
> > > > > > > > > > +
> > > > > > > > > > +     {
> > > > > > > > > > +         image-guid: 02f4d760-cfd5-43bd-8e2d-a42acb33c660
> > > > > > > > > > +         hardware-instance: 0
> > > > > > > > > > +         monotonic-count: 1
> > > > > > > > > > +         payload: u-boot.bin
> > > > > > > > > > +         image-index: 1
> > > > > > > > > > +         private-key: /path/to/priv/key
> > > > > > > > > > +         pub-key-cert: /path/to/pub/key
> > > > > > > > > > +         capsule: u-boot.capsule
> > > > > > > > > > +     }
> > > > > > > > > > +     {
> > > > > > > > > > +         image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e
> > > > > > > > > > +         hardware-instance: 0
> > > > > > > > > > +         payload: u-boot.itb
> > > > > > > > > > +         image-index: 2
> > > > > > > > > > +         oemflags: 0x8000
> > > > > > > > > > +         capsule: fit.capsule
> > > > > > > > > > +     }
> > > > > > > > > > +     {
> > > > > > > > > > +         capsule-type: accept
> > > > > > > > > > +         image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e
> > > > > > > > > > +         capsule: accept.capsule
> > > > > > > > > > +     }
> > > > > > > > > > +     {
> > > > > > > > > > +         capsule-type: revert
> > > > > > > > > > +         capsule: revert.capsule
> > > > > > > > > > +     }
> > > > > > > > > > +*/
> > > > > > > > > > +
> > > > > > > > > > +#include <ctype.h>
> > > > > > > > > > +#include <limits.h>
> > > > > > > > > > +#include <stdio.h>
> > > > > > > > > > +#include <stdlib.h>
> > > > > > > > > > +#include <string.h>
> > > > > > > > > > +
> > > > > > > > > > +#include <uuid/uuid.h>
> > > > > > > > > > +
> > > > > > > > > > +#include "eficapsule.h"
> > > > > > > > > > +
> > > > > > > > > > +#define PARAMS_START "{"
> > > > > > > > > > +#define PARAMS_END   "}"
> > > > > > > > > > +
> > > > > > > > > > +#define PSTART               2
> > > > > > > > > > +#define PEND         3
> > > > > > > > > > +
> > > > > > > > > > +#define MALLOC_FAIL_STR              "Unable to allocate memory\n"
> > > > > > > > > > +
> > > > > > > > > > +#define ARRAY_SIZE(x)                (sizeof(x) / sizeof((x)[0]))
> > > > > > > > > > +
> > > > > > > > > > +const char *capsule_params[] = {
> > > > > > > > > > +     "image-guid", "image-index", "private-key",
> > > > > > > > > > +     "pub-key-cert", "payload", "capsule",
> > > > > > > > > > +     "hardware-instance", "monotonic-count",
> > > > > > > > > > +     "capsule-type", "oemflags" };
> > > > > > > > > > +
> > > > > > > > > > +static unsigned char params_start;
> > > > > > > > > > +static unsigned char params_end;
> > > > > > > > > > +
> > > > > > > > > > +static void print_and_exit(const char *str)
> > > > > > > > > > +{
> > > > > > > > > > +     fprintf(stderr, "%s", str);
> > > > > > > > > > +     exit(EXIT_FAILURE);
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static int param_delim_checks(char *line, unsigned char *token)
> > > > > > > > > > +{
> > > > > > > > > > +     if (!strcmp(line, PARAMS_START)) {
> > > > > > > > > > +             if (params_start || !params_end) {
> > > > > > > > > > +                     fprintf(stderr, "Earlier params processing still in progress. ");
> > > > > > > > > > +                     fprintf(stderr, "Can't start processing a new params.\n");
> > > > > > > > > > +                     exit(EXIT_FAILURE);
> > > > > > > > > > +             } else {
> > > > > > > > > > +                     params_start = 1;
> > > > > > > > > > +                     params_end = 0;
> > > > > > > > > > +                     *token = PSTART;
> > > > > > > > > > +                     return 1;
> > > > > > > > > > +             }
> > > > > > > > > > +     } else if (!strcmp(line, PARAMS_END)) {
> > > > > > > > > > +             if (!params_start) {
> > > > > > > > > > +                     fprintf(stderr, "Cannot put end braces without start braces. ");
> > > > > > > > > > +                     fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > > > > > > > +                     exit(EXIT_FAILURE);
> > > > > > > > > > +             } else {
> > > > > > > > > > +                     params_start = 0;
> > > > > > > > > > +                     params_end = 1;
> > > > > > > > > > +                     *token = PEND;
> > > > > > > > > > +                     return 1;
> > > > > > > > > > +             }
> > > > > > > > > > +     } else if (!params_start) {
> > > > > > > > > > +             fprintf(stderr, "Params should be passed within braces. ");
> > > > > > > > > > +             fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > > > > > > > +             exit(EXIT_FAILURE);
> > > > > > > > > > +     }
> > > > > > > > > > +
> > > > > > > > > > +     return 0;
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static void add_guid(efi_guid_t **guid_param, char *guid)
> > > > > > > > > > +{
> > > > > > > > > > +     unsigned char uuid_buf[16];
> > > > > > > > > > +
> > > > > > > > > > +     *guid_param = malloc(sizeof(efi_guid_t));
> > > > > > > > > > +     if (!*guid_param)
> > > > > > > > > > +             print_and_exit(MALLOC_FAIL_STR);
> > > > > > > > > > +
> > > > > > > > > > +     if (uuid_parse(guid, uuid_buf))
> > > > > > > > > > +             print_and_exit("Wrong guid format\n");
> > > > > > > > > > +
> > > > > > > > > > +     convert_uuid_to_guid(uuid_buf);
> > > > > > > > > > +     memcpy(*guid_param, uuid_buf, sizeof(efi_guid_t));
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static void add_string(char **dst, char *val)
> > > > > > > > > > +{
> > > > > > > > > > +     *dst = strdup(val);
> > > > > > > > > > +     if (!*dst)
> > > > > > > > > > +             print_and_exit(MALLOC_FAIL_STR);
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static void match_and_populate_param(char *key, char *val,
> > > > > > > > > > +                                  struct efi_capsule_params *param)
> > > > > > > > > > +{
> > > > > > > > > > +     int i;
> > > > > > > > > > +
> > > > > > > > > > +     for (i = 0; i < ARRAY_SIZE(capsule_params); i++) {
> > > > > > > > > > +             if (!strcmp(key, capsule_params[i])) {
> > > > > > > > > > +                     switch (i) {
> > > > > > > > > > +                     case 0:
> > > > > > > > > > +                             add_guid(&param->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(&param->privkey_file, val);
> > > > > > > > > > +                             return;
> > > > > > > > > > +                     case 3:
> > > > > > > > > > +                             add_string(&param->cert_file, val);
> > > > > > > > > > +                             return;
> > > > > > > > > > +                     case 4:
> > > > > > > > > > +                             add_string(&param->input_file, val);
> > > > > > > > > > +                             return;
> > > > > > > > > > +                     case 5:
> > > > > > > > > > +                             add_string(&param->capsule_file, val);
> > > > > > > > > > +                             return;
> > > > > > > > > > +                     case 6:
> > > > > > > > > > +                             param->hardware_instance = strtoul(val, NULL, 0);
> > > > > > > > > > +                             if (param->hardware_instance == ULONG_MAX)
> > > > > > > > > > +                                     print_and_exit("Enter a valid hardware instance value");
> > > > > > > > > > +                             return;
> > > > > > > > > > +                     case 7:
> > > > > > > > > > +                             param->monotonic_count = strtoull(val, NULL, 0);
> > > > > > > > > > +                             if (param->monotonic_count == ULLONG_MAX)
> > > > > > > > > > +                                     print_and_exit("Enter a valid monotonic count value");
> > > > > > > > > > +                             return;
> > > > > > > > > > +                     case 8:
> > > > > > > > > > +                             if (!strcmp(val, "normal"))
> > > > > > > > > > +                                     param->capsule = CAPSULE_NORMAL_BLOB;
> > > > > > > > > > +                             else if (!strcmp(val, "accept"))
> > > > > > > > > > +                                     param->capsule = CAPSULE_ACCEPT;
> > > > > > > > > > +                             else if (!strcmp(val, "revert"))
> > > > > > > > > > +                                     param->capsule = CAPSULE_REVERT;
> > > > > > > > > > +                             else
> > > > > > > > > > +                                     print_and_exit("Invalid type of capsule");
> > > > > > > > > > +
> > > > > > > > > > +                             return;
> > > > > > > > > > +                     case 9:
> > > > > > > > > > +                             param->oemflags = strtoul(val, NULL, 0);
> > > > > > > > > > +                             if (param->oemflags > 0xffff)
> > > > > > > > > > +                                     print_and_exit("OemFlags must be between 0x0 and 0xffff\n");
> > > > > > > > > > +                             return;
> > > > > > > > > > +                     }
> > > > > > > > > > +             }
> > > > > > > > > > +     }
> > > > > > > > > > +
> > > > > > > > > > +     fprintf(stderr, "Undefined param %s specified. ", key);
> > > > > > > > > > +     fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > > > > > > > +     exit(EXIT_FAILURE);
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static int get_capsule_params(char *line, struct efi_capsule_params *params)
> > > > > > > > > > +{
> > > > > > > > > > +     char *key = NULL;
> > > > > > > > > > +     char *val = NULL;
> > > > > > > > > > +     unsigned char token;
> > > > > > > > > > +
> > > > > > > > > > +     if (param_delim_checks(line, &token))
> > > > > > > > > > +             return token;
> > > > > > > > > > +
> > > > > > > > > > +     key = strtok(line, ":");
> > > > > > > > > > +     if (key)
> > > > > > > > > > +             val = strtok(NULL, "\0");
> > > > > > > > > > +     else
> > > > > > > > > > +             print_and_exit("Expect the params in a key:value pair\n");
> > > > > > > > > > +
> > > > > > > > > > +     match_and_populate_param(key, val, params);
> > > > > > > > > > +
> > > > > > > > > > +     return 0;
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static char *skip_whitespace(char *line)
> > > > > > > > > > +{
> > > > > > > > > > +     char *ptr, *newline;
> > > > > > > > > > +
> > > > > > > > > > +     ptr = malloc(strlen(line) + 1);
> > > > > > > > > > +     if (!ptr)
> > > > > > > > > > +             print_and_exit(MALLOC_FAIL_STR);
> > > > > > > > > > +
> > > > > > > > > > +     for (newline = ptr; *line; line++)
> > > > > > > > > > +             if (!isblank(*line))
> > > > > > > > > > +                     *ptr++ = *line;
> > > > > > > > > > +     *ptr = '\0';
> > > > > > > > > > +     return newline;
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static int parse_capsule_payload_params(FILE *fp, struct efi_capsule_params *params)
> > > > > > > > > > +{
> > > > > > > > > > +     char *line = NULL;
> > > > > > > > > > +     char *newline;
> > > > > > > > > > +     size_t n = 0;
> > > > > > > > > > +     ssize_t len;
> > > > > > > > > > +
> > > > > > > > > > +     while ((len = getline(&line, &n, fp)) != -1) {
> > > > > > > > > > +             if (len == 1 && line[len - 1] == '\n')
> > > > > > > > > > +                     continue;
> > > > > > > > > > +
> > > > > > > > > > +             line[len - 1] = '\0';
> > > > > > > > > > +
> > > > > > > > > > +             newline = skip_whitespace(line);
> > > > > > > > > > +
> > > > > > > > > > +             if (newline[0] == '#')
> > > > > > > > > > +                     continue;
> > > > > > > > > > +
> > > > > > > > > > +             if (get_capsule_params(newline, params) == PEND)
> > > > > > > > > > +                     return 0;
> > > > > > > > > > +     }
> > > > > > > > > > +
> > > > > > > > > > +     if (errno == EINVAL || errno == ENOMEM) {
> > > > > > > > > > +             fprintf(stderr, "getline() returned an error %s reading the line\n",
> > > > > > > > > > +                     strerror(errno));
> > > > > > > > > > +             exit(EXIT_FAILURE);
> > > > > > > > > > +     } else if (params_start == 1 || params_end == 0) {
> > > > > > > > > > +             fprintf(stderr, "Params should be passed within braces. ");
> > > > > > > > > > +             fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> > > > > > > > > > +             exit(EXIT_FAILURE);
> > > > > > > > > > +     } else {
> > > > > > > > > > +             return -1;
> > > > > > > > > > +     }
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static void params_dependency_check(struct efi_capsule_params *params)
> > > > > > > > > > +{
> > > > > > > > > > +     /* check necessary parameters */
> > > > > > > > > > +     if ((params->capsule == CAPSULE_NORMAL_BLOB &&
> > > > > > > > > > +          ((!params->input_file || !params->capsule_file ||
> > > > > > > > > > +            !params->image_guid) ||
> > > > > > > > > > +           ((params->privkey_file && !params->cert_file) ||
> > > > > > > > > > +            (!params->privkey_file && params->cert_file)))) ||
> > > > > > > > > > +         (params->capsule != CAPSULE_NORMAL_BLOB &&
> > > > > > > > > > +          (!params->capsule_file ||
> > > > > > > > > > +           (params->capsule == CAPSULE_ACCEPT && !params->image_guid) ||
> > > > > > > > > > +           (params->capsule == CAPSULE_REVERT && params->image_guid)))) {
> > > > > > > > > > +             print_usage();
> > > > > > > > > > +             exit(EXIT_FAILURE);
> > > > > > > > > > +     }
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static void generate_capsule(struct efi_capsule_params *params)
> > > > > > > > > > +{
> > > > > > > > > > +     if (params->capsule != CAPSULE_NORMAL_BLOB) {
> > > > > > > > > > +             if (create_empty_capsule(params->capsule_file,
> > > > > > > > > > +                                      params->image_guid,
> > > > > > > > > > +                                      params->capsule ==
> > > > > > > > > > +                                      CAPSULE_ACCEPT) < 0)
> > > > > > > > > > +                     print_and_exit("Creating empty capsule failed\n");
> > > > > > > > > > +     } else if (create_fwbin(params->capsule_file, params->input_file,
> > > > > > > > > > +                           params->image_guid, params->image_index,
> > > > > > > > > > +                           params->hardware_instance,
> > > > > > > > > > +                           params->monotonic_count,
> > > > > > > > > > +                           params->privkey_file,
> > > > > > > > > > +                           params->cert_file,
> > > > > > > > > > +                           (uint16_t)params->oemflags) < 0) {
> > > > > > > > > > +             print_and_exit("Creating firmware capsule failed\n");
> > > > > > > > > > +     }
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +/**
> > > > > > > > > > + * capsule_with_cfg_file() - Generate capsule from config file
> > > > > > > > > > + * @cfg_file: Path to the config file
> > > > > > > > > > + *
> > > > > > > > > > + * Parse the capsule parameters from the config file and use the
> > > > > > > > > > + * parameters for generating one or more capsules.
> > > > > > > > > > + *
> > > > > > > > > > + * Return: None
> > > > > > > > > > + *
> > > > > > > > > > + */
> > > > > > > > > > +void capsule_with_cfg_file(const char *cfg_file)
> > > > > > > > > > +{
> > > > > > > > > > +     FILE *fp;
> > > > > > > > > > +     struct efi_capsule_params params = { 0 };
> > > > > > > > > > +
> > > > > > > > > > +     fp = fopen(cfg_file, "r");
> > > > > > > > > > +     if (!fp) {
> > > > > > > > > > +             fprintf(stderr, "Unable to open the capsule config file %s\n",
> > > > > > > > > > +                     cfg_file);
> > > > > > > > > > +             exit(EXIT_FAILURE);
> > > > > > > > > > +     }
> > > > > > > > > > +
> > > > > > > > > > +     params_start = 0;
> > > > > > > > > > +     params_end = 1;
> > > > > > > > > > +
> > > > > > > > > > +     while (parse_capsule_payload_params(fp, &params) != -1) {
> > > > > > > > > > +             params_dependency_check(&params);
> > > > > > > > > > +             generate_capsule(&params);
> > > > > > > > > > +
> > > > > > > > > > +             memset(&params, 0, sizeof(struct efi_capsule_params));
> > > > > > > > > > +     }
> > > > > > > > > > +}
> > > > > > > > > > --
> > > > > > > > > > 2.34.1
> > > > > > > > > >

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 4/7] tools: mkeficapsule: Add support for parsing capsule params from config file
  2023-06-16  6:35                     ` Sughosh Ganu
@ 2023-06-16 13:12                       ` Schmidt, Malte
  2023-06-19  7:27                         ` Sughosh Ganu
  0 siblings, 1 reply; 50+ messages in thread
From: Schmidt, Malte @ 2023-06-16 13:12 UTC (permalink / raw)
  To: Sughosh Ganu, Takahiro Akashi, u-boot, Heinrich Schuchardt,
	Ilias Apalodimas, Simon Glass, Michal Simek

Hi sughosh,

Am 16.06.2023 um 08:35 schrieb Sughosh Ganu:
> On Fri, 16 Jun 2023 at 10:48, Takahiro Akashi
> <takahiro.akashi@linaro.org>  wrote:
>> On Fri, Jun 16, 2023 at 10:37:01AM +0530, Sughosh Ganu wrote:
>>> hi Takahiro,
>>>
>>> On Fri, 16 Jun 2023 at 10:16, Takahiro Akashi
>>> <takahiro.akashi@linaro.org>  wrote:
>>>> Hi Sughosh,
>>>>
>>>> On Fri, Jun 16, 2023 at 09:56:33AM +0530, Sughosh Ganu wrote:
>>>>> On Thu, 15 Jun 2023 at 11:19, Takahiro Akashi
>>>>> <takahiro.akashi@linaro.org>  wrote:
>>>>>> On Thu, Jun 15, 2023 at 10:09:06AM +0530, Sughosh Ganu wrote:
>>>>>>> On Wed, 14 Jun 2023 at 11:23, Takahiro Akashi
>>>>>>> <takahiro.akashi@linaro.org>  wrote:
>>>>>>>> On Wed, Jun 14, 2023 at 10:56:23AM +0530, Sughosh Ganu wrote:
>>>>>>>>> hi Takahiro,
>>>>>>>>>
>>>>>>>>> On Wed, 14 Jun 2023 at 09:09, Takahiro Akashi
>>>>>>>>> <takahiro.akashi@linaro.org>  wrote:
>>>>>>>>>> Hi Sughosh,
>>>>>>>>>>
>>>>>>>>>> I think this is a good extension to mkeficapsule, but
>>>>>>>>>>
>>>>>>>>>> On Tue, Jun 13, 2023 at 04:08:03PM +0530, Sughosh Ganu wrote:
>>>>>>>>>>> Add support for specifying the parameters needed for capsule
>>>>>>>>>>> generation through a config file, instead of passing them through
>>>>>>>>>>> command-line. Parameters for more than a single capsule file can be
>>>>>>>>>>> specified, resulting in generation of multiple capsules through a
>>>>>>>>>>> single invocation of the command.
>>>>>>>>>>>
>>>>>>>>>>> This path is to be used for generating capsules through a make target,
>>>>>>>>>>> with the parameters being parsed from the config file.
>>>>>>>>>>>
>>>>>>>>>>> Signed-off-by: Sughosh Ganu<sughosh.ganu@linaro.org>
>>>>>>>>>>> ---
>>>>>>>>>>>   tools/Kconfig              |   9 +
>>>>>>>>>>>   tools/Makefile             |   1 +
>>>>>>>>>>>   tools/eficapsule.h         | 110 ++++++++++++
>>>>>>>>>>>   tools/mkeficapsule.c       | 106 +++++++-----
>>>>>>>>>>>   tools/mkeficapsule_parse.c | 345 +++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>   5 files changed, 531 insertions(+), 40 deletions(-)
>>>>>>>>>>>   create mode 100644 tools/mkeficapsule_parse.c
>>>>>>>>>>>
>>>>>>>>>>> diff --git a/tools/Kconfig b/tools/Kconfig
>>>>>>>>>>> index 539708f277..95f27b7c45 100644
>>>>>>>>>>> --- a/tools/Kconfig
>>>>>>>>>>> +++ b/tools/Kconfig
>>>>>>>>>>> @@ -98,6 +98,15 @@ config TOOLS_MKEFICAPSULE
>>>>>>>>>>>          optionally sign that file. If you want to enable UEFI capsule
>>>>>>>>>>>          update feature on your target, you certainly need this.
>>>>>>>>>>>
>>>>>>>>>>> +config EFI_CAPSULE_CFG_FILE
>>>>>>>>>>> +     string "Path to the EFI Capsule Config File"
>>>>>>>>>>> +     default ""
>>>>>>>>>>> +     help
>>>>>>>>>>> +       Path to the EFI capsule config file which provides the
>>>>>>>>>>> +       parameters needed to build capsule(s). Parameters can be
>>>>>>>>>>> +       provided for multiple payloads resulting in corresponding
>>>>>>>>>>> +       capsule images being generated.
>>>>>>>>>>> +
>>>>>>>>>>>   menuconfig FSPI_CONF_HEADER
>>>>>>>>>>>        bool "FlexSPI Header Configuration"
>>>>>>>>>>>        help
>>>>>>>>>>> diff --git a/tools/Makefile b/tools/Makefile
>>>>>>>>>>> index d793cf3bec..ef366f3d61 100644
>>>>>>>>>>> --- a/tools/Makefile
>>>>>>>>>>> +++ b/tools/Makefile
>>>>>>>>>>> @@ -250,6 +250,7 @@ HOSTLDLIBS_mkeficapsule += \
>>>>>>>>>>>   HOSTLDLIBS_mkeficapsule += \
>>>>>>>>>>>        $(shell pkg-config --libs uuid 2> /dev/null || echo "-luuid")
>>>>>>>>>>>   hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule
>>>>>>>>>>> +mkeficapsule-objs := mkeficapsule.o mkeficapsule_parse.o
>>>>>>>>>>>
>>>>>>>>>>>   # We build some files with extra pedantic flags to try to minimize things
>>>>>>>>>>>   # that won't build on some weird host compiler -- though there are lots of
>>>>>>>>>>> diff --git a/tools/eficapsule.h b/tools/eficapsule.h
>>>>>>>>>>> index 072a4b5598..42e66c6d6a 100644
>>>>>>>>>>> --- a/tools/eficapsule.h
>>>>>>>>>>> +++ b/tools/eficapsule.h
>>>>>>>>>>> @@ -52,6 +52,38 @@ typedef struct {
>>>>>>>>>>>   /* flags */
>>>>>>>>>>>   #define CAPSULE_FLAGS_PERSIST_ACROSS_RESET      0x00010000
>>>>>>>>>>>
>>>>>>>>>>> +enum capsule_type {
>>>>>>>>>>> +     CAPSULE_NORMAL_BLOB = 0,
>>>>>>>>>>> +     CAPSULE_ACCEPT,
>>>>>>>>>>> +     CAPSULE_REVERT,
>>>>>>>>>>> +};
>>>>>>>>>>> +
>>>>>>>>>>> +/**
>>>>>>>>>>> + * struct efi_capsule_params - Capsule parameters
>>>>>>>>>>> + * @image_guid: Guid value of the payload input image
>>>>>>>>>>> + * @image_index: Image index value
>>>>>>>>>>> + * @hardware_instance: Hardware instance to be used for the image
>>>>>>>>>>> + * @monotonic_count: Monotonic count value to be used for signed capsule
>>>>>>>>>>> + * @privkey_file: Path to private key used in capsule signing
>>>>>>>>>>> + * @cert_file: Path to public key certificate used in capsule signing
>>>>>>>>>>> + * @input_file: Path to payload input image
>>>>>>>>>>> + * @capsule_file: Path to the output capsule file
>>>>>>>>>>> + * @oemflags: Oemflags to be populated in the capsule header
>>>>>>>>>>> + * @capsule: Capsule Type, normal or accept or revert
>>>>>>>>>>> + */
>>>>>>>>>>> +struct efi_capsule_params {
>>>>>>>>>>> +     efi_guid_t *image_guid;
>>>>>>>>>>> +     unsigned long image_index;
>>>>>>>>>>> +     unsigned long hardware_instance;
>>>>>>>>>>> +     uint64_t monotonic_count;
>>>>>>>>>>> +     char *privkey_file;
>>>>>>>>>>> +     char *cert_file;
>>>>>>>>>>> +     char *input_file;
>>>>>>>>>>> +     char *capsule_file;
>>>>>>>>>>> +     unsigned long oemflags;
>>>>>>>>>>> +     enum capsule_type capsule;
>>>>>>>>>>> +};
>>>>>>>>>>> +
>>>>>>>>>>>   struct efi_capsule_header {
>>>>>>>>>>>        efi_guid_t capsule_guid;
>>>>>>>>>>>        uint32_t header_size;
>>>>>>>>>>> @@ -113,4 +145,82 @@ struct efi_firmware_image_authentication {
>>>>>>>>>>>        struct win_certificate_uefi_guid auth_info;
>>>>>>>>>>>   } __packed;
>>>>>>>>>>>
>>>>>>>>>>> +/**
>>>>>>>>>>> + * capsule_with_cfg_file() - Generate capsule from config file
>>>>>>>>>>> + * @cfg_file: Path to the config file
>>>>>>>>>>> + *
>>>>>>>>>>> + * Parse the capsule parameters from the config file and use the
>>>>>>>>>>> + * parameters for generating one or more capsules.
>>>>>>>>>>> + *
>>>>>>>>>>> + * Return: None
>>>>>>>>>>> + *
>>>>>>>>>>> + */
>>>>>>>>>>> +void capsule_with_cfg_file(const char *cfg_file);
>>>>>>>>>>> +
>>>>>>>>>>> +/**
>>>>>>>>>>> + * convert_uuid_to_guid() - convert UUID to GUID
>>>>>>>>>>> + * @buf:     UUID binary
>>>>>>>>>>> + *
>>>>>>>>>>> + * UUID and GUID have the same data structure, but their binary
>>>>>>>>>>> + * formats are different due to the endianness. See lib/uuid.c.
>>>>>>>>>>> + * Since uuid_parse() can handle only UUID, this function must
>>>>>>>>>>> + * be called to get correct data for GUID when parsing a string.
>>>>>>>>>>> + *
>>>>>>>>>>> + * The correct data will be returned in @buf.
>>>>>>>>>>> + */
>>>>>>>>>>> +void convert_uuid_to_guid(unsigned char *buf);
>>>>>>>>>>> +
>>>>>>>>>>> +/**
>>>>>>>>>>> + * create_empty_capsule() - Generate an empty capsule
>>>>>>>>>>> + * @path: Path to the empty capsule file to be generated
>>>>>>>>>>> + * @guid: Guid value of the image for which empty capsule is generated
>>>>>>>>>>> + * @fw_accept: Flag to specify whether to generate accept or revert capsule
>>>>>>>>>>> + *
>>>>>>>>>>> + * Generate an empty capsule, either an accept or a revert capsule to be
>>>>>>>>>>> + * used to flag acceptance or rejection of an earlier executed firmware
>>>>>>>>>>> + * update operation. Being used in the FWU Multi Bank firmware update
>>>>>>>>>>> + * feature.
>>>>>>>>>>> + *
>>>>>>>>>>> + * Return: 0 if OK, -ve on error
>>>>>>>>>>> + *
>>>>>>>>>>> + */
>>>>>>>>>>> +int create_empty_capsule(char *path, efi_guid_t *guid, bool fw_accept);
>>>>>>>>>>> +
>>>>>>>>>>> +/**
>>>>>>>>>>> + * create_fwbin - create an uefi capsule file
>>>>>>>>>>> + * @path:    Path to a created capsule file
>>>>>>>>>>> + * @bin:     Path to a firmware binary to encapsulate
>>>>>>>>>>> + * @guid:    GUID of related FMP driver
>>>>>>>>>>> + * @index:   Index number in capsule
>>>>>>>>>>> + * @instance:        Instance number in capsule
>>>>>>>>>>> + * @mcount:  Monotonic count in authentication information
>>>>>>>>>>> + * @private_file:    Path to a private key file
>>>>>>>>>>> + * @cert_file:       Path to a certificate file
>>>>>>>>>>> + * @oemflags:  Capsule OEM Flags, bits 0-15
>>>>>>>>>>> + *
>>>>>>>>>>> + * This function actually does the job of creating an uefi capsule file.
>>>>>>>>>>> + * All the arguments must be supplied.
>>>>>>>>>>> + * If either @private_file ror @cert_file is NULL, the capsule file
>>>>>>>>>>> + * won't be signed.
>>>>>>>>>>> + *
>>>>>>>>>>> + * Return:
>>>>>>>>>>> + * * 0  - on success
>>>>>>>>>>> + * * -1 - on failure
>>>>>>>>>>> + */
>>>>>>>>>>> +int create_fwbin(char *path, char *bin, efi_guid_t *guid,
>>>>>>>>>>> +              unsigned long index, unsigned long instance,
>>>>>>>>>>> +              uint64_t mcount, char *privkey_file, char *cert_file,
>>>>>>>>>>> +              uint16_t oemflags);
>>>>>>>>>>> +
>>>>>>>>>>> +/**
>>>>>>>>>>> + * print_usage() - Print the command usage string
>>>>>>>>>>> + *
>>>>>>>>>>> + * Prints the standard command usage string. Called in the case
>>>>>>>>>>> + * of incorrect parameters being passed to the tool.
>>>>>>>>>>> + *
>>>>>>>>>>> + * Return: None
>>>>>>>>>>> + *
>>>>>>>>>>> + */
>>>>>>>>>>> +void print_usage(void);
>>>>>>>>>>> +
>>>>>>>>>>>   #endif /* _EFI_CAPSULE_H */
>>>>>>>>>>> diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
>>>>>>>>>>> index b71537beee..711adf0439 100644
>>>>>>>>>>> --- a/tools/mkeficapsule.c
>>>>>>>>>>> +++ b/tools/mkeficapsule.c
>>>>>>>>>>> @@ -31,12 +31,6 @@ 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 struct option options[] = {
>>>>>>>>>>>        {"guid", required_argument, NULL, 'g'},
>>>>>>>>>>>        {"index", required_argument, NULL, 'i'},
>>>>>>>>>>> @@ -52,7 +46,16 @@ static struct option options[] = {
>>>>>>>>>>>        {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"
>>>>>>>>>>> @@ -400,10 +403,10 @@ static void free_sig_data(struct auth_context *ctx)
>>>>>>>>>>>    * * 0  - on success
>>>>>>>>>>>    * * -1 - on failure
>>>>>>>>>>>    */
>>>>>>>>>>> -static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
>>>>>>>>>>> -                     unsigned long index, unsigned long instance,
>>>>>>>>>>> -                     uint64_t mcount, char *privkey_file, char *cert_file,
>>>>>>>>>>> -                     uint16_t oemflags)
>>>>>>>>>>> +int create_fwbin(char *path, char *bin, efi_guid_t *guid,
>>>>>>>>>>> +              unsigned long index, unsigned long instance,
>>>>>>>>>>> +              uint64_t mcount, char *privkey_file, char *cert_file,
>>>>>>>>>>> +              uint16_t oemflags)
>>>>>>>>>>>   {
>>>>>>>>>>>        struct efi_capsule_header header;
>>>>>>>>>>>        struct efi_firmware_management_capsule_header capsule;
>>>>>>>>>>> @@ -580,7 +583,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;
>>>>>>>>>>> @@ -623,19 +640,7 @@ err:
>>>>>>>>>>>        return ret;
>>>>>>>>>>>   }
>>>>>>>>>>>
>>>>>>>>>>> -/**
>>>>>>>>>>> - * main - main entry function of mkeficapsule
>>>>>>>>>>> - * @argc:    Number of arguments
>>>>>>>>>>> - * @argv:    Array of pointers to arguments
>>>>>>>>>>> - *
>>>>>>>>>>> - * Create an uefi capsule file, optionally signing it.
>>>>>>>>>>> - * Parse all the arguments and pass them on to create_fwbin().
>>>>>>>>>>> - *
>>>>>>>>>>> - * Return:
>>>>>>>>>>> - * * 0  - on success
>>>>>>>>>>> - * * -1 - on failure
>>>>>>>>>>> - */
>>>>>>>>>>> -int main(int argc, char **argv)
>>>>>>>>>>> +static void capsule_with_cmdline_params(int argc, char **argv)
>>>>>>>>>>>   {
>>>>>>>>>>>        efi_guid_t *guid;
>>>>>>>>>>>        unsigned char uuid_buf[16];
>>>>>>>>>>> @@ -643,6 +648,7 @@ int main(int argc, char **argv)
>>>>>>>>>>>        uint64_t mcount;
>>>>>>>>>>>        unsigned long oemflags;
>>>>>>>>>>>        char *privkey_file, *cert_file;
>>>>>>>>>>> +     enum capsule_type capsule;
>>>>>>>>>>>        int c, idx;
>>>>>>>>>>>
>>>>>>>>>>>        guid = NULL;
>>>>>>>>>>> @@ -652,7 +658,7 @@ int main(int argc, char **argv)
>>>>>>>>>>>        privkey_file = NULL;
>>>>>>>>>>>        cert_file = NULL;
>>>>>>>>>>>        dump_sig = 0;
>>>>>>>>>>> -     capsule_type = CAPSULE_NORMAL_BLOB;
>>>>>>>>>>> +     capsule = CAPSULE_NORMAL_BLOB;
>>>>>>>>>>>        oemflags = 0;
>>>>>>>>>>>        for (;;) {
>>>>>>>>>>>                c = getopt_long(argc, argv, opts_short, options, &idx);
>>>>>>>>>>> @@ -702,20 +708,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);
>>>>>>>>>>> @@ -732,21 +738,21 @@ int main(int argc, char **argv)
>>>>>>>>>>>        }
>>>>>>>>>>>
>>>>>>>>>>>        /* check necessary parameters */
>>>>>>>>>>> -     if ((capsule_type == CAPSULE_NORMAL_BLOB &&
>>>>>>>>>>> -         ((argc != optind + 2) || !guid ||
>>>>>>>>>>> -          ((privkey_file && !cert_file) ||
>>>>>>>>>>> -           (!privkey_file && cert_file)))) ||
>>>>>>>>>>> -         (capsule_type != CAPSULE_NORMAL_BLOB &&
>>>>>>>>>>> -         ((argc != optind + 1) ||
>>>>>>>>>>> -          ((capsule_type == CAPSULE_ACCEPT) && !guid) ||
>>>>>>>>>>> -          ((capsule_type == CAPSULE_REVERT) && guid)))) {
>>>>>>>>>>> +     if ((capsule == CAPSULE_NORMAL_BLOB &&
>>>>>>>>>>> +          ((argc != optind + 2) || !guid ||
>>>>>>>>>>> +           ((privkey_file && !cert_file) ||
>>>>>>>>>>> +            (!privkey_file && cert_file)))) ||
>>>>>>>>>>> +         (capsule != CAPSULE_NORMAL_BLOB &&
>>>>>>>>>>> +          ((argc != optind + 1) ||
>>>>>>>>>>> +           (capsule == CAPSULE_ACCEPT && !guid) ||
>>>>>>>>>>> +           (capsule == CAPSULE_REVERT && guid)))) {
>>>>>>>>>>>                print_usage();
>>>>>>>>>>>                exit(EXIT_FAILURE);
>>>>>>>>>>>        }
>>>>>>>>>>>
>>>>>>>>>>> -     if (capsule_type != CAPSULE_NORMAL_BLOB) {
>>>>>>>>>>> +     if (capsule != CAPSULE_NORMAL_BLOB) {
>>>>>>>>>>>                if (create_empty_capsule(argv[argc - 1], guid,
>>>>>>>>>>> -                                      capsule_type == CAPSULE_ACCEPT) < 0) {
>>>>>>>>>>> +                                      capsule == CAPSULE_ACCEPT) < 0) {
>>>>>>>>>>>                        fprintf(stderr, "Creating empty capsule failed\n");
>>>>>>>>>>>                        exit(EXIT_FAILURE);
>>>>>>>>>>>                }
>>>>>>>>>>> @@ -756,6 +762,26 @@ int main(int argc, char **argv)
>>>>>>>>>>>                fprintf(stderr, "Creating firmware capsule failed\n");
>>>>>>>>>>>                exit(EXIT_FAILURE);
>>>>>>>>>>>        }
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>> +/**
>>>>>>>>>>> + * main - main entry function of mkeficapsule
>>>>>>>>>>> + * @argc:    Number of arguments
>>>>>>>>>>> + * @argv:    Array of pointers to arguments
>>>>>>>>>>> + *
>>>>>>>>>>> + * Create an uefi capsule file, optionally signing it.
>>>>>>>>>>> + * Parse all the arguments and pass them on to create_fwbin().
>>>>>>>>>>> + *
>>>>>>>>>>> + * Return:
>>>>>>>>>>> + * * 0  - on success
>>>>>>>>>>> + * * -1 - on failure
>>>>>>>>>>> + */
>>>>>>>>>>> +int main(int argc, char **argv)
>>>>>>>>>>> +{
>>>>>>>>>>> +     if (!strcmp(CONFIG_EFI_CAPSULE_CFG_FILE, ""))
>>>>>>>>>>> +             capsule_with_cmdline_params(argc, argv);
>>>>>>>>>>> +     else
>>>>>>>>>>> +             capsule_with_cfg_file(CONFIG_EFI_CAPSULE_CFG_FILE);
>>>>>>>>>> I don't know where the macro, CONFIG_EFI_CAPSULE_CFG_FILE, comes from.
>>>>>>>>>> Anyhow, as a general rule, any host tool must be as generic as it should not
>>>>>>>>>> depend on a target's config.
>>>>>>>>>> (I was told so before.)
>>>>>>>>>>
>>>>>>>>>> So I would suggest that you add another command line, say "--config-file <file>",
>>>>>>>>>> to make the command generic.
>>>>>>>>> Yes, that would be something followed by most of the tools. The reason
>>>>>>>>> I did not add a command-line option for the confile file is because I
>>>>>>>>> want the capsule generation added as a make target. With the path to
>>>>>>>>> the config file specified through the Kconfig symbol, we can invoke
>>>>>>>>> 'make capsule', and it would build the capsules by parsing the
>>>>>>>>> parameters from the config file, taken from the Kconfig symbol. I know
>>>>>>>>> there are ways of specifying options when using a make command, but I
>>>>>>>>> don't think that is a clean way of doing things.
>>>>>>>> Not sure, but in your [5/7],
>>>>>>>> cmd_mkeficapsule = $(objtree)/tools/mkeficapsule --config-file $(CONFIG_EFI_CAPSULE_CFG_FILE)
>>>>>>>>
>>>>>>>> Doesn't this change work?
>>>>>>> So, I tried the above suggested change. But trying to run a make
>>>>>>> 'target' does not work without the .config file being present.
>>>>>> Not sure what you meant to say here.
>>>>>> Why don't you have .config when building U-Boot (or rather 'target')?
>>>>> Maybe I misunderstood your earlier comment, but I thought you were
>>>>> looking to build capsules without relying on a target config.
>>>> Not exactly.
>>>> The basic requirement, I believe, is that the exact same binary (with
>>>> the same set of functionalities) should be generated for any host tool
>>>> whatever a target's config, including tools-only_defconfig, is.
>>> Okay. I think I now understand what you are looking for. However, I
>>> believe if you want the same binary for both scenarios, the only way
>>> might be to drop the make target to generate capsules, and do it
>>> through the --config-file command-line option.
>> Again not sure what you're trying to do.
> Never mind. It works with the cfg-file being passed as a command-line
> parameter. I will make the change for the next version.
>
> -sughosh
>
>>> I will check if we can
>>> pass the config file as a parameter when building capsules as a
>>> target. I could not get it working when I tried it earlier though. If
>>> this is indeed not possible, do you have a strong opinion on having
>>> the same binary for both scenarios?
>>   I have no reason why you can't.
>>   If you see any failure, please give me more details about how
>>   you are going to manage so that I can help you.
>>
>>   -Takahiro Akashi
>>
>>> -sughosh
>>>
>>>> Is it clear now?
>>>>
>>>> -Takahiro Akashi
>>>>
>>>>> Which I
>>>>> believe cannot be done for a make target.
>>>>>
>>>>> -sughosh
>>>>>
>>>>>> -Takahiro Akashi
>>>>>>
>>>>>>> FWIW,
>>>>>>> the same is the case for building tools as well. I think that is the
>>>>>>> reason for the tools-only_defconfig.
>>>>>>>
>>>>>>> -sughosh
>>>>>>>
>>>>>>>> -Takahiro Akashi
>>>>>>>>
>>>>>>>>
>>>>>>>>> Given the use case of
>>>>>>>>> a make target, I hope we can use the Kconfig symbol for specifying the
>>>>>>>>> config file path.
>>>>>>>>>
>>>>>>>>> -sughosh
>>>>>>>>>
>>>>>>>>>> -Takahiro Akashi
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>>        exit(EXIT_SUCCESS);
>>>>>>>>>>>   }
>>>>>>>>>>> diff --git a/tools/mkeficapsule_parse.c b/tools/mkeficapsule_parse.c
>>>>>>>>>>> new file mode 100644
>>>>>>>>>>> index 0000000000..ef4f3f6705
>>>>>>>>>>> --- /dev/null
>>>>>>>>>>> +++ b/tools/mkeficapsule_parse.c
>>>>>>>>>>> @@ -0,0 +1,345 @@
>>>>>>>>>>> +// SPDX-License-Identifier: GPL-2.0
>>>>>>>>>>> +/*
>>>>>>>>>>> + * Copyright 2023 Linaro Limited
>>>>>>>>>>> + */
>>>>>>>>>>> +
>>>>>>>>>>> +/*
>>>>>>>>>>> + * The code in this file adds parsing ability to the mkeficapsule
>>>>>>>>>>> + * tool. This allows specifying parameters needed to build the capsule
>>>>>>>>>>> + * through the config file instead of specifying them on the command-line.
>>>>>>>>>>> + * Parameters can be specified for more than one payload, generating the
>>>>>>>>>>> + * corresponding capsule files.
>>>>>>>>>>> + *
>>>>>>>>>>> + * The parameters are specified in a "key:value" pair. All the parameters
>>>>>>>>>>> + * that are currently supported by the mkeficapsule tool can be specified
>>>>>>>>>>> + * in the config file.
>>>>>>>>>>> + *
>>>>>>>>>>> + * The example below shows four payloads. The first payload is an example
>>>>>>>>>>> + * of generating a signed capsule. The second payload is an example of
>>>>>>>>>>> + * generating an unsigned capsule. The third payload is an accept empty
>>>>>>>>>>> + * capsule, while the fourth payload is the revert empty capsule, used
>>>>>>>>>>> + * for the multi-bank firmware update feature.
>>>>>>>>>>> + *
>>>>>>>>>>> + * This functionality can be easily extended to generate a single capsule
>>>>>>>>>>> + * comprising multiple payloads.
>>>>>>>>>>> +
>>>>>>>>>>> +     {
>>>>>>>>>>> +         image-guid: 02f4d760-cfd5-43bd-8e2d-a42acb33c660
>>>>>>>>>>> +         hardware-instance: 0
>>>>>>>>>>> +         monotonic-count: 1
>>>>>>>>>>> +         payload: u-boot.bin
>>>>>>>>>>> +         image-index: 1
>>>>>>>>>>> +         private-key: /path/to/priv/key
>>>>>>>>>>> +         pub-key-cert: /path/to/pub/key
>>>>>>>>>>> +         capsule: u-boot.capsule
>>>>>>>>>>> +     }
>>>>>>>>>>> +     {
>>>>>>>>>>> +         image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e
>>>>>>>>>>> +         hardware-instance: 0
>>>>>>>>>>> +         payload: u-boot.itb
>>>>>>>>>>> +         image-index: 2
>>>>>>>>>>> +         oemflags: 0x8000
>>>>>>>>>>> +         capsule: fit.capsule
>>>>>>>>>>> +     }
>>>>>>>>>>> +     {
>>>>>>>>>>> +         capsule-type: accept
>>>>>>>>>>> +         image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e
>>>>>>>>>>> +         capsule: accept.capsule
>>>>>>>>>>> +     }
>>>>>>>>>>> +     {
>>>>>>>>>>> +         capsule-type: revert
>>>>>>>>>>> +         capsule: revert.capsule
>>>>>>>>>>> +     }
>>>>>>>>>>> +*/
>>>>>>>>>>> +
If i understand it correctly the EDK2 GenerateCapsule tool allows for 
multiple
payloads inside one capsule by specifying a list of payloads in the 
JSON-file.
I think something similar should be done here to support multiple payloads
inside one capsule. What about something like this:

{

   content: [{
     image-guid: 02f4d760-cfd5-43bd-8e2d-a42acb33c660
     hardware-instance: 0
     monotonic-count: 1
     payload: u-boot.bin
     image-index: 1
   },{

     image-guid: 02f4d760-cfd5-43bd-8e2d-a42acb33c660
     hardware-instance: 1
     monotonic-count: 1
     payload: boot.bin
     image-index: 2
   }],

   private-key: /path/to/priv/key
   pub-key-cert: /path/to/pub/key
   capsule: u-boot.capsule

}

?

Best Regards
Malte
>>>>>>>>>>> +#include <ctype.h>
>>>>>>>>>>> +#include <limits.h>
>>>>>>>>>>> +#include <stdio.h>
>>>>>>>>>>> +#include <stdlib.h>
>>>>>>>>>>> +#include <string.h>
>>>>>>>>>>> +
>>>>>>>>>>> +#include <uuid/uuid.h>
>>>>>>>>>>> +
>>>>>>>>>>> +#include "eficapsule.h"
>>>>>>>>>>> +
>>>>>>>>>>> +#define PARAMS_START "{"
>>>>>>>>>>> +#define PARAMS_END   "}"
>>>>>>>>>>> +
>>>>>>>>>>> +#define PSTART               2
>>>>>>>>>>> +#define PEND         3
>>>>>>>>>>> +
>>>>>>>>>>> +#define MALLOC_FAIL_STR              "Unable to allocate memory\n"
>>>>>>>>>>> +
>>>>>>>>>>> +#define ARRAY_SIZE(x)                (sizeof(x) / sizeof((x)[0]))
>>>>>>>>>>> +
>>>>>>>>>>> +const char *capsule_params[] = {
>>>>>>>>>>> +     "image-guid", "image-index", "private-key",
>>>>>>>>>>> +     "pub-key-cert", "payload", "capsule",
>>>>>>>>>>> +     "hardware-instance", "monotonic-count",
>>>>>>>>>>> +     "capsule-type", "oemflags" };
>>>>>>>>>>> +
>>>>>>>>>>> +static unsigned char params_start;
>>>>>>>>>>> +static unsigned char params_end;
>>>>>>>>>>> +
>>>>>>>>>>> +static void print_and_exit(const char *str)
>>>>>>>>>>> +{
>>>>>>>>>>> +     fprintf(stderr, "%s", str);
>>>>>>>>>>> +     exit(EXIT_FAILURE);
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>> +static int param_delim_checks(char *line, unsigned char *token)
>>>>>>>>>>> +{
>>>>>>>>>>> +     if (!strcmp(line, PARAMS_START)) {
>>>>>>>>>>> +             if (params_start || !params_end) {
>>>>>>>>>>> +                     fprintf(stderr, "Earlier params processing still in progress. ");
>>>>>>>>>>> +                     fprintf(stderr, "Can't start processing a new params.\n");
>>>>>>>>>>> +                     exit(EXIT_FAILURE);
>>>>>>>>>>> +             } else {
>>>>>>>>>>> +                     params_start = 1;
>>>>>>>>>>> +                     params_end = 0;
>>>>>>>>>>> +                     *token = PSTART;
>>>>>>>>>>> +                     return 1;
>>>>>>>>>>> +             }
>>>>>>>>>>> +     } else if (!strcmp(line, PARAMS_END)) {
>>>>>>>>>>> +             if (!params_start) {
>>>>>>>>>>> +                     fprintf(stderr, "Cannot put end braces without start braces. ");
>>>>>>>>>>> +                     fprintf(stderr, "Please check the documentation for reference config file syntax\n");
>>>>>>>>>>> +                     exit(EXIT_FAILURE);
>>>>>>>>>>> +             } else {
>>>>>>>>>>> +                     params_start = 0;
>>>>>>>>>>> +                     params_end = 1;
>>>>>>>>>>> +                     *token = PEND;
>>>>>>>>>>> +                     return 1;
>>>>>>>>>>> +             }
>>>>>>>>>>> +     } else if (!params_start) {
>>>>>>>>>>> +             fprintf(stderr, "Params should be passed within braces. ");
>>>>>>>>>>> +             fprintf(stderr, "Please check the documentation for reference config file syntax\n");
>>>>>>>>>>> +             exit(EXIT_FAILURE);
>>>>>>>>>>> +     }
>>>>>>>>>>> +
>>>>>>>>>>> +     return 0;
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>> +static void add_guid(efi_guid_t **guid_param, char *guid)
>>>>>>>>>>> +{
>>>>>>>>>>> +     unsigned char uuid_buf[16];
>>>>>>>>>>> +
>>>>>>>>>>> +     *guid_param = malloc(sizeof(efi_guid_t));
>>>>>>>>>>> +     if (!*guid_param)
>>>>>>>>>>> +             print_and_exit(MALLOC_FAIL_STR);
>>>>>>>>>>> +
>>>>>>>>>>> +     if (uuid_parse(guid, uuid_buf))
>>>>>>>>>>> +             print_and_exit("Wrong guid format\n");
>>>>>>>>>>> +
>>>>>>>>>>> +     convert_uuid_to_guid(uuid_buf);
>>>>>>>>>>> +     memcpy(*guid_param, uuid_buf, sizeof(efi_guid_t));
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>> +static void add_string(char **dst, char *val)
>>>>>>>>>>> +{
>>>>>>>>>>> +     *dst = strdup(val);
>>>>>>>>>>> +     if (!*dst)
>>>>>>>>>>> +             print_and_exit(MALLOC_FAIL_STR);
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>> +static void match_and_populate_param(char *key, char *val,
>>>>>>>>>>> +                                  struct efi_capsule_params *param)
>>>>>>>>>>> +{
>>>>>>>>>>> +     int i;
>>>>>>>>>>> +
>>>>>>>>>>> +     for (i = 0; i < ARRAY_SIZE(capsule_params); i++) {
>>>>>>>>>>> +             if (!strcmp(key, capsule_params[i])) {
>>>>>>>>>>> +                     switch (i) {
>>>>>>>>>>> +                     case 0:
>>>>>>>>>>> +                             add_guid(&param->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(&param->privkey_file, val);
>>>>>>>>>>> +                             return;
>>>>>>>>>>> +                     case 3:
>>>>>>>>>>> +                             add_string(&param->cert_file, val);
>>>>>>>>>>> +                             return;
>>>>>>>>>>> +                     case 4:
>>>>>>>>>>> +                             add_string(&param->input_file, val);
>>>>>>>>>>> +                             return;
>>>>>>>>>>> +                     case 5:
>>>>>>>>>>> +                             add_string(&param->capsule_file, val);
>>>>>>>>>>> +                             return;
>>>>>>>>>>> +                     case 6:
>>>>>>>>>>> +                             param->hardware_instance = strtoul(val, NULL, 0);
>>>>>>>>>>> +                             if (param->hardware_instance == ULONG_MAX)
>>>>>>>>>>> +                                     print_and_exit("Enter a valid hardware instance value");
>>>>>>>>>>> +                             return;
>>>>>>>>>>> +                     case 7:
>>>>>>>>>>> +                             param->monotonic_count = strtoull(val, NULL, 0);
>>>>>>>>>>> +                             if (param->monotonic_count == ULLONG_MAX)
>>>>>>>>>>> +                                     print_and_exit("Enter a valid monotonic count value");
>>>>>>>>>>> +                             return;
>>>>>>>>>>> +                     case 8:
>>>>>>>>>>> +                             if (!strcmp(val, "normal"))
>>>>>>>>>>> +                                     param->capsule = CAPSULE_NORMAL_BLOB;
>>>>>>>>>>> +                             else if (!strcmp(val, "accept"))
>>>>>>>>>>> +                                     param->capsule = CAPSULE_ACCEPT;
>>>>>>>>>>> +                             else if (!strcmp(val, "revert"))
>>>>>>>>>>> +                                     param->capsule = CAPSULE_REVERT;
>>>>>>>>>>> +                             else
>>>>>>>>>>> +                                     print_and_exit("Invalid type of capsule");
>>>>>>>>>>> +
>>>>>>>>>>> +                             return;
>>>>>>>>>>> +                     case 9:
>>>>>>>>>>> +                             param->oemflags = strtoul(val, NULL, 0);
>>>>>>>>>>> +                             if (param->oemflags > 0xffff)
>>>>>>>>>>> +                                     print_and_exit("OemFlags must be between 0x0 and 0xffff\n");
>>>>>>>>>>> +                             return;
>>>>>>>>>>> +                     }
>>>>>>>>>>> +             }
>>>>>>>>>>> +     }
>>>>>>>>>>> +
>>>>>>>>>>> +     fprintf(stderr, "Undefined param %s specified. ", key);
>>>>>>>>>>> +     fprintf(stderr, "Please check the documentation for reference config file syntax\n");
>>>>>>>>>>> +     exit(EXIT_FAILURE);
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>> +static int get_capsule_params(char *line, struct efi_capsule_params *params)
>>>>>>>>>>> +{
>>>>>>>>>>> +     char *key = NULL;
>>>>>>>>>>> +     char *val = NULL;
>>>>>>>>>>> +     unsigned char token;
>>>>>>>>>>> +
>>>>>>>>>>> +     if (param_delim_checks(line, &token))
>>>>>>>>>>> +             return token;
>>>>>>>>>>> +
>>>>>>>>>>> +     key = strtok(line, ":");
>>>>>>>>>>> +     if (key)
>>>>>>>>>>> +             val = strtok(NULL, "\0");
>>>>>>>>>>> +     else
>>>>>>>>>>> +             print_and_exit("Expect the params in a key:value pair\n");
>>>>>>>>>>> +
>>>>>>>>>>> +     match_and_populate_param(key, val, params);
>>>>>>>>>>> +
>>>>>>>>>>> +     return 0;
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>> +static char *skip_whitespace(char *line)
>>>>>>>>>>> +{
>>>>>>>>>>> +     char *ptr, *newline;
>>>>>>>>>>> +
>>>>>>>>>>> +     ptr = malloc(strlen(line) + 1);
>>>>>>>>>>> +     if (!ptr)
>>>>>>>>>>> +             print_and_exit(MALLOC_FAIL_STR);
>>>>>>>>>>> +
>>>>>>>>>>> +     for (newline = ptr; *line; line++)
>>>>>>>>>>> +             if (!isblank(*line))
>>>>>>>>>>> +                     *ptr++ = *line;
>>>>>>>>>>> +     *ptr = '\0';
>>>>>>>>>>> +     return newline;
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>> +static int parse_capsule_payload_params(FILE *fp, struct efi_capsule_params *params)
>>>>>>>>>>> +{
>>>>>>>>>>> +     char *line = NULL;
>>>>>>>>>>> +     char *newline;
>>>>>>>>>>> +     size_t n = 0;
>>>>>>>>>>> +     ssize_t len;
>>>>>>>>>>> +
>>>>>>>>>>> +     while ((len = getline(&line, &n, fp)) != -1) {
>>>>>>>>>>> +             if (len == 1 && line[len - 1] == '\n')
>>>>>>>>>>> +                     continue;
>>>>>>>>>>> +
>>>>>>>>>>> +             line[len - 1] = '\0';
>>>>>>>>>>> +
>>>>>>>>>>> +             newline = skip_whitespace(line);
>>>>>>>>>>> +
>>>>>>>>>>> +             if (newline[0] == '#')
>>>>>>>>>>> +                     continue;
>>>>>>>>>>> +
>>>>>>>>>>> +             if (get_capsule_params(newline, params) == PEND)
>>>>>>>>>>> +                     return 0;
>>>>>>>>>>> +     }
>>>>>>>>>>> +
>>>>>>>>>>> +     if (errno == EINVAL || errno == ENOMEM) {
>>>>>>>>>>> +             fprintf(stderr, "getline() returned an error %s reading the line\n",
>>>>>>>>>>> +                     strerror(errno));
>>>>>>>>>>> +             exit(EXIT_FAILURE);
>>>>>>>>>>> +     } else if (params_start == 1 || params_end == 0) {
>>>>>>>>>>> +             fprintf(stderr, "Params should be passed within braces. ");
>>>>>>>>>>> +             fprintf(stderr, "Please check the documentation for reference config file syntax\n");
>>>>>>>>>>> +             exit(EXIT_FAILURE);
>>>>>>>>>>> +     } else {
>>>>>>>>>>> +             return -1;
>>>>>>>>>>> +     }
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>> +static void params_dependency_check(struct efi_capsule_params *params)
>>>>>>>>>>> +{
>>>>>>>>>>> +     /* check necessary parameters */
>>>>>>>>>>> +     if ((params->capsule == CAPSULE_NORMAL_BLOB &&
>>>>>>>>>>> +          ((!params->input_file || !params->capsule_file ||
>>>>>>>>>>> +            !params->image_guid) ||
>>>>>>>>>>> +           ((params->privkey_file && !params->cert_file) ||
>>>>>>>>>>> +            (!params->privkey_file && params->cert_file)))) ||
>>>>>>>>>>> +         (params->capsule != CAPSULE_NORMAL_BLOB &&
>>>>>>>>>>> +          (!params->capsule_file ||
>>>>>>>>>>> +           (params->capsule == CAPSULE_ACCEPT && !params->image_guid) ||
>>>>>>>>>>> +           (params->capsule == CAPSULE_REVERT && params->image_guid)))) {
>>>>>>>>>>> +             print_usage();
>>>>>>>>>>> +             exit(EXIT_FAILURE);
>>>>>>>>>>> +     }
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>> +static void generate_capsule(struct efi_capsule_params *params)
>>>>>>>>>>> +{
>>>>>>>>>>> +     if (params->capsule != CAPSULE_NORMAL_BLOB) {
>>>>>>>>>>> +             if (create_empty_capsule(params->capsule_file,
>>>>>>>>>>> +                                      params->image_guid,
>>>>>>>>>>> +                                      params->capsule ==
>>>>>>>>>>> +                                      CAPSULE_ACCEPT) < 0)
>>>>>>>>>>> +                     print_and_exit("Creating empty capsule failed\n");
>>>>>>>>>>> +     } else if (create_fwbin(params->capsule_file, params->input_file,
>>>>>>>>>>> +                           params->image_guid, params->image_index,
>>>>>>>>>>> +                           params->hardware_instance,
>>>>>>>>>>> +                           params->monotonic_count,
>>>>>>>>>>> +                           params->privkey_file,
>>>>>>>>>>> +                           params->cert_file,
>>>>>>>>>>> +                           (uint16_t)params->oemflags) < 0) {
>>>>>>>>>>> +             print_and_exit("Creating firmware capsule failed\n");
>>>>>>>>>>> +     }
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>> +/**
>>>>>>>>>>> + * capsule_with_cfg_file() - Generate capsule from config file
>>>>>>>>>>> + * @cfg_file: Path to the config file
>>>>>>>>>>> + *
>>>>>>>>>>> + * Parse the capsule parameters from the config file and use the
>>>>>>>>>>> + * parameters for generating one or more capsules.
>>>>>>>>>>> + *
>>>>>>>>>>> + * Return: None
>>>>>>>>>>> + *
>>>>>>>>>>> + */
>>>>>>>>>>> +void capsule_with_cfg_file(const char *cfg_file)
>>>>>>>>>>> +{
>>>>>>>>>>> +     FILE *fp;
>>>>>>>>>>> +     struct efi_capsule_params params = { 0 };
>>>>>>>>>>> +
>>>>>>>>>>> +     fp = fopen(cfg_file, "r");
>>>>>>>>>>> +     if (!fp) {
>>>>>>>>>>> +             fprintf(stderr, "Unable to open the capsule config file %s\n",
>>>>>>>>>>> +                     cfg_file);
>>>>>>>>>>> +             exit(EXIT_FAILURE);
>>>>>>>>>>> +     }
>>>>>>>>>>> +
>>>>>>>>>>> +     params_start = 0;
>>>>>>>>>>> +     params_end = 1;
>>>>>>>>>>> +
>>>>>>>>>>> +     while (parse_capsule_payload_params(fp, &params) != -1) {
>>>>>>>>>>> +             params_dependency_check(&params);
>>>>>>>>>>> +             generate_capsule(&params);
>>>>>>>>>>> +
>>>>>>>>>>> +             memset(&params, 0, sizeof(struct efi_capsule_params));
>>>>>>>>>>> +     }
>>>>>>>>>>> +}
>>>>>>>>>>> --
>>>>>>>>>>> 2.34.1
>>>>>>>>>>>

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 4/7] tools: mkeficapsule: Add support for parsing capsule params from config file
  2023-06-16 13:12                       ` Schmidt, Malte
@ 2023-06-19  7:27                         ` Sughosh Ganu
  2023-06-19  9:42                           ` Schmidt, Malte
  0 siblings, 1 reply; 50+ messages in thread
From: Sughosh Ganu @ 2023-06-19  7:27 UTC (permalink / raw)
  To: Schmidt, Malte
  Cc: Takahiro Akashi, u-boot, Heinrich Schuchardt, Ilias Apalodimas,
	Simon Glass, Michal Simek

hi Malte,

On Fri, 16 Jun 2023 at 18:42, Schmidt, Malte
<malte.schmidt-oss@weidmueller.com> wrote:
>
> Hi sughosh,
>
> Am 16.06.2023 um 08:35 schrieb Sughosh Ganu:
>
> On Fri, 16 Jun 2023 at 10:48, Takahiro Akashi
> <takahiro.akashi@linaro.org> wrote:
>
> On Fri, Jun 16, 2023 at 10:37:01AM +0530, Sughosh Ganu wrote:
>
> hi Takahiro,
>
> On Fri, 16 Jun 2023 at 10:16, Takahiro Akashi
> <takahiro.akashi@linaro.org> wrote:
>
> Hi Sughosh,
>
> On Fri, Jun 16, 2023 at 09:56:33AM +0530, Sughosh Ganu wrote:
>
> On Thu, 15 Jun 2023 at 11:19, Takahiro Akashi
> <takahiro.akashi@linaro.org> wrote:
>
> On Thu, Jun 15, 2023 at 10:09:06AM +0530, Sughosh Ganu wrote:
>
> On Wed, 14 Jun 2023 at 11:23, Takahiro Akashi
> <takahiro.akashi@linaro.org> wrote:
>
> On Wed, Jun 14, 2023 at 10:56:23AM +0530, Sughosh Ganu wrote:
>
> hi Takahiro,
>
> On Wed, 14 Jun 2023 at 09:09, Takahiro Akashi
> <takahiro.akashi@linaro.org> wrote:
>
> Hi Sughosh,
>
> I think this is a good extension to mkeficapsule, but
>
> On Tue, Jun 13, 2023 at 04:08:03PM +0530, Sughosh Ganu wrote:
>
> Add support for specifying the parameters needed for capsule
> generation through a config file, instead of passing them through
> command-line. Parameters for more than a single capsule file can be
> specified, resulting in generation of multiple capsules through a
> single invocation of the command.
>
> This path is to be used for generating capsules through a make target,
> with the parameters being parsed from the config file.
>
> Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> ---
>  tools/Kconfig              |   9 +
>  tools/Makefile             |   1 +
>  tools/eficapsule.h         | 110 ++++++++++++
>  tools/mkeficapsule.c       | 106 +++++++-----
>  tools/mkeficapsule_parse.c | 345 +++++++++++++++++++++++++++++++++++++
>  5 files changed, 531 insertions(+), 40 deletions(-)
>  create mode 100644 tools/mkeficapsule_parse.c
>

<snip>

> diff --git a/tools/mkeficapsule_parse.c b/tools/mkeficapsule_parse.c
> new file mode 100644
> index 0000000000..ef4f3f6705
> --- /dev/null
> +++ b/tools/mkeficapsule_parse.c
> @@ -0,0 +1,345 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2023 Linaro Limited
> + */
> +
> +/*
> + * The code in this file adds parsing ability to the mkeficapsule
> + * tool. This allows specifying parameters needed to build the capsule
> + * through the config file instead of specifying them on the command-line.
> + * Parameters can be specified for more than one payload, generating the
> + * corresponding capsule files.
> + *
> + * The parameters are specified in a "key:value" pair. All the parameters
> + * that are currently supported by the mkeficapsule tool can be specified
> + * in the config file.
> + *
> + * The example below shows four payloads. The first payload is an example
> + * of generating a signed capsule. The second payload is an example of
> + * generating an unsigned capsule. The third payload is an accept empty
> + * capsule, while the fourth payload is the revert empty capsule, used
> + * for the multi-bank firmware update feature.
> + *
> + * This functionality can be easily extended to generate a single capsule
> + * comprising multiple payloads.
> +
> +     {
> +         image-guid: 02f4d760-cfd5-43bd-8e2d-a42acb33c660
> +         hardware-instance: 0
> +         monotonic-count: 1
> +         payload: u-boot.bin
> +         image-index: 1
> +         private-key: /path/to/priv/key
> +         pub-key-cert: /path/to/pub/key
> +         capsule: u-boot.capsule
> +     }
> +     {
> +         image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e
> +         hardware-instance: 0
> +         payload: u-boot.itb
> +         image-index: 2
> +         oemflags: 0x8000
> +         capsule: fit.capsule
> +     }
> +     {
> +         capsule-type: accept
> +         image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e
> +         capsule: accept.capsule
> +     }
> +     {
> +         capsule-type: revert
> +         capsule: revert.capsule
> +     }
> +*/
> +
>
> If i understand it correctly the EDK2 GenerateCapsule tool allows for multiple
> payloads inside one capsule by specifying a list of payloads in the JSON-file.
> I think something similar should be done here to support multiple payloads
> inside one capsule. What about something like this:
>
> {
>
>   content: [{
>     image-guid: 02f4d760-cfd5-43bd-8e2d-a42acb33c660
>     hardware-instance: 0
>     monotonic-count: 1
>     payload: u-boot.bin
>     image-index: 1
>   },{
>
>     image-guid: 02f4d760-cfd5-43bd-8e2d-a42acb33c660
>     hardware-instance: 1
>     monotonic-count: 1
>     payload: boot.bin
>     image-index: 2
>   }],
>
>   private-key: /path/to/priv/key
>   pub-key-cert: /path/to/pub/key
>   capsule: u-boot.capsule
>
> }
>
> ?

I am aware of these additional brackets and the "Payloads" keyword
that is used in the EDK2 json file. Adding this should not be a big
effort. However, the reason I did not add this is that I did not see
any value in adding this. I believe the EDK2 json file also is used
for providing information about additional files, like optional driver
images. But we don't support that with the u-boot tool. So will we be
adding any value by putting these additional brackets.

The other question is, in case of a single capsule file consisting of
multiple payloads, how do we pass the name of the output capsule file.
I see two options, one is having a field like "capsule" outside of all
the payloads. The other is passing this through the command line. In
any case, changes for extending this for supporting a single capsule
should be sent along with your change of adding support for a single
capsule file. I can work on adding this, but it would be good to get
this in as part of your patches.

-sughosh

>
> Best Regards
> Malte
>
> +#include <ctype.h>
> +#include <limits.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include <uuid/uuid.h>
> +
> +#include "eficapsule.h"
> +
> +#define PARAMS_START "{"
> +#define PARAMS_END   "}"
> +
> +#define PSTART               2
> +#define PEND         3
> +
> +#define MALLOC_FAIL_STR              "Unable to allocate memory\n"
> +
> +#define ARRAY_SIZE(x)                (sizeof(x) / sizeof((x)[0]))
> +
> +const char *capsule_params[] = {
> +     "image-guid", "image-index", "private-key",
> +     "pub-key-cert", "payload", "capsule",
> +     "hardware-instance", "monotonic-count",
> +     "capsule-type", "oemflags" };
> +
> +static unsigned char params_start;
> +static unsigned char params_end;
> +
> +static void print_and_exit(const char *str)
> +{
> +     fprintf(stderr, "%s", str);
> +     exit(EXIT_FAILURE);
> +}
> +
> +static int param_delim_checks(char *line, unsigned char *token)
> +{
> +     if (!strcmp(line, PARAMS_START)) {
> +             if (params_start || !params_end) {
> +                     fprintf(stderr, "Earlier params processing still in progress. ");
> +                     fprintf(stderr, "Can't start processing a new params.\n");
> +                     exit(EXIT_FAILURE);
> +             } else {
> +                     params_start = 1;
> +                     params_end = 0;
> +                     *token = PSTART;
> +                     return 1;
> +             }
> +     } else if (!strcmp(line, PARAMS_END)) {
> +             if (!params_start) {
> +                     fprintf(stderr, "Cannot put end braces without start braces. ");
> +                     fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> +                     exit(EXIT_FAILURE);
> +             } else {
> +                     params_start = 0;
> +                     params_end = 1;
> +                     *token = PEND;
> +                     return 1;
> +             }
> +     } else if (!params_start) {
> +             fprintf(stderr, "Params should be passed within braces. ");
> +             fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> +             exit(EXIT_FAILURE);
> +     }
> +
> +     return 0;
> +}
> +
> +static void add_guid(efi_guid_t **guid_param, char *guid)
> +{
> +     unsigned char uuid_buf[16];
> +
> +     *guid_param = malloc(sizeof(efi_guid_t));
> +     if (!*guid_param)
> +             print_and_exit(MALLOC_FAIL_STR);
> +
> +     if (uuid_parse(guid, uuid_buf))
> +             print_and_exit("Wrong guid format\n");
> +
> +     convert_uuid_to_guid(uuid_buf);
> +     memcpy(*guid_param, uuid_buf, sizeof(efi_guid_t));
> +}
> +
> +static void add_string(char **dst, char *val)
> +{
> +     *dst = strdup(val);
> +     if (!*dst)
> +             print_and_exit(MALLOC_FAIL_STR);
> +}
> +
> +static void match_and_populate_param(char *key, char *val,
> +                                  struct efi_capsule_params *param)
> +{
> +     int i;
> +
> +     for (i = 0; i < ARRAY_SIZE(capsule_params); i++) {
> +             if (!strcmp(key, capsule_params[i])) {
> +                     switch (i) {
> +                     case 0:
> +                             add_guid(&param->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(&param->privkey_file, val);
> +                             return;
> +                     case 3:
> +                             add_string(&param->cert_file, val);
> +                             return;
> +                     case 4:
> +                             add_string(&param->input_file, val);
> +                             return;
> +                     case 5:
> +                             add_string(&param->capsule_file, val);
> +                             return;
> +                     case 6:
> +                             param->hardware_instance = strtoul(val, NULL, 0);
> +                             if (param->hardware_instance == ULONG_MAX)
> +                                     print_and_exit("Enter a valid hardware instance value");
> +                             return;
> +                     case 7:
> +                             param->monotonic_count = strtoull(val, NULL, 0);
> +                             if (param->monotonic_count == ULLONG_MAX)
> +                                     print_and_exit("Enter a valid monotonic count value");
> +                             return;
> +                     case 8:
> +                             if (!strcmp(val, "normal"))
> +                                     param->capsule = CAPSULE_NORMAL_BLOB;
> +                             else if (!strcmp(val, "accept"))
> +                                     param->capsule = CAPSULE_ACCEPT;
> +                             else if (!strcmp(val, "revert"))
> +                                     param->capsule = CAPSULE_REVERT;
> +                             else
> +                                     print_and_exit("Invalid type of capsule");
> +
> +                             return;
> +                     case 9:
> +                             param->oemflags = strtoul(val, NULL, 0);
> +                             if (param->oemflags > 0xffff)
> +                                     print_and_exit("OemFlags must be between 0x0 and 0xffff\n");
> +                             return;
> +                     }
> +             }
> +     }
> +
> +     fprintf(stderr, "Undefined param %s specified. ", key);
> +     fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> +     exit(EXIT_FAILURE);
> +}
> +
> +static int get_capsule_params(char *line, struct efi_capsule_params *params)
> +{
> +     char *key = NULL;
> +     char *val = NULL;
> +     unsigned char token;
> +
> +     if (param_delim_checks(line, &token))
> +             return token;
> +
> +     key = strtok(line, ":");
> +     if (key)
> +             val = strtok(NULL, "\0");
> +     else
> +             print_and_exit("Expect the params in a key:value pair\n");
> +
> +     match_and_populate_param(key, val, params);
> +
> +     return 0;
> +}
> +
> +static char *skip_whitespace(char *line)
> +{
> +     char *ptr, *newline;
> +
> +     ptr = malloc(strlen(line) + 1);
> +     if (!ptr)
> +             print_and_exit(MALLOC_FAIL_STR);
> +
> +     for (newline = ptr; *line; line++)
> +             if (!isblank(*line))
> +                     *ptr++ = *line;
> +     *ptr = '\0';
> +     return newline;
> +}
> +
> +static int parse_capsule_payload_params(FILE *fp, struct efi_capsule_params *params)
> +{
> +     char *line = NULL;
> +     char *newline;
> +     size_t n = 0;
> +     ssize_t len;
> +
> +     while ((len = getline(&line, &n, fp)) != -1) {
> +             if (len == 1 && line[len - 1] == '\n')
> +                     continue;
> +
> +             line[len - 1] = '\0';
> +
> +             newline = skip_whitespace(line);
> +
> +             if (newline[0] == '#')
> +                     continue;
> +
> +             if (get_capsule_params(newline, params) == PEND)
> +                     return 0;
> +     }
> +
> +     if (errno == EINVAL || errno == ENOMEM) {
> +             fprintf(stderr, "getline() returned an error %s reading the line\n",
> +                     strerror(errno));
> +             exit(EXIT_FAILURE);
> +     } else if (params_start == 1 || params_end == 0) {
> +             fprintf(stderr, "Params should be passed within braces. ");
> +             fprintf(stderr, "Please check the documentation for reference config file syntax\n");
> +             exit(EXIT_FAILURE);
> +     } else {
> +             return -1;
> +     }
> +}
> +
> +static void params_dependency_check(struct efi_capsule_params *params)
> +{
> +     /* check necessary parameters */
> +     if ((params->capsule == CAPSULE_NORMAL_BLOB &&
> +          ((!params->input_file || !params->capsule_file ||
> +            !params->image_guid) ||
> +           ((params->privkey_file && !params->cert_file) ||
> +            (!params->privkey_file && params->cert_file)))) ||
> +         (params->capsule != CAPSULE_NORMAL_BLOB &&
> +          (!params->capsule_file ||
> +           (params->capsule == CAPSULE_ACCEPT && !params->image_guid) ||
> +           (params->capsule == CAPSULE_REVERT && params->image_guid)))) {
> +             print_usage();
> +             exit(EXIT_FAILURE);
> +     }
> +}
> +
> +static void generate_capsule(struct efi_capsule_params *params)
> +{
> +     if (params->capsule != CAPSULE_NORMAL_BLOB) {
> +             if (create_empty_capsule(params->capsule_file,
> +                                      params->image_guid,
> +                                      params->capsule ==
> +                                      CAPSULE_ACCEPT) < 0)
> +                     print_and_exit("Creating empty capsule failed\n");
> +     } else if (create_fwbin(params->capsule_file, params->input_file,
> +                           params->image_guid, params->image_index,
> +                           params->hardware_instance,
> +                           params->monotonic_count,
> +                           params->privkey_file,
> +                           params->cert_file,
> +                           (uint16_t)params->oemflags) < 0) {
> +             print_and_exit("Creating firmware capsule failed\n");
> +     }
> +}
> +
> +/**
> + * capsule_with_cfg_file() - Generate capsule from config file
> + * @cfg_file: Path to the config file
> + *
> + * Parse the capsule parameters from the config file and use the
> + * parameters for generating one or more capsules.
> + *
> + * Return: None
> + *
> + */
> +void capsule_with_cfg_file(const char *cfg_file)
> +{
> +     FILE *fp;
> +     struct efi_capsule_params params = { 0 };
> +
> +     fp = fopen(cfg_file, "r");
> +     if (!fp) {
> +             fprintf(stderr, "Unable to open the capsule config file %s\n",
> +                     cfg_file);
> +             exit(EXIT_FAILURE);
> +     }
> +
> +     params_start = 0;
> +     params_end = 1;
> +
> +     while (parse_capsule_payload_params(fp, &params) != -1) {
> +             params_dependency_check(&params);
> +             generate_capsule(&params);
> +
> +             memset(&params, 0, sizeof(struct efi_capsule_params));
> +     }
> +}
> --
> 2.34.1
>
>

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 4/7] tools: mkeficapsule: Add support for parsing capsule params from config file
  2023-06-19  7:27                         ` Sughosh Ganu
@ 2023-06-19  9:42                           ` Schmidt, Malte
  0 siblings, 0 replies; 50+ messages in thread
From: Schmidt, Malte @ 2023-06-19  9:42 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: Takahiro Akashi, u-boot, Heinrich Schuchardt, Ilias Apalodimas,
	Simon Glass, Michal Simek

Hi sughosh,


Am 19.06.2023 um 09:27 schrieb Sughosh Ganu:
> hi Malte,
>
> On Fri, 16 Jun 2023 at 18:42, Schmidt, Malte
> <malte.schmidt-oss@weidmueller.com> wrote:
>> Hi sughosh,
>>
>> Am 16.06.2023 um 08:35 schrieb Sughosh Ganu:
>>
>> On Fri, 16 Jun 2023 at 10:48, Takahiro Akashi
>> <takahiro.akashi@linaro.org> wrote:
>>
>> On Fri, Jun 16, 2023 at 10:37:01AM +0530, Sughosh Ganu wrote:
>>
>> hi Takahiro,
>>
>> On Fri, 16 Jun 2023 at 10:16, Takahiro Akashi
>> <takahiro.akashi@linaro.org> wrote:
>>
>> Hi Sughosh,
>>
>> On Fri, Jun 16, 2023 at 09:56:33AM +0530, Sughosh Ganu wrote:
>>
>> On Thu, 15 Jun 2023 at 11:19, Takahiro Akashi
>> <takahiro.akashi@linaro.org> wrote:
>>
>> On Thu, Jun 15, 2023 at 10:09:06AM +0530, Sughosh Ganu wrote:
>>
>> On Wed, 14 Jun 2023 at 11:23, Takahiro Akashi
>> <takahiro.akashi@linaro.org> wrote:
>>
>> On Wed, Jun 14, 2023 at 10:56:23AM +0530, Sughosh Ganu wrote:
>>
>> hi Takahiro,
>>
>> On Wed, 14 Jun 2023 at 09:09, Takahiro Akashi
>> <takahiro.akashi@linaro.org> wrote:
>>
>> Hi Sughosh,
>>
>> I think this is a good extension to mkeficapsule, but
>>
>> On Tue, Jun 13, 2023 at 04:08:03PM +0530, Sughosh Ganu wrote:
>>
>> Add support for specifying the parameters needed for capsule
>> generation through a config file, instead of passing them through
>> command-line. Parameters for more than a single capsule file can be
>> specified, resulting in generation of multiple capsules through a
>> single invocation of the command.
>>
>> This path is to be used for generating capsules through a make target,
>> with the parameters being parsed from the config file.
>>
>> Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
>> ---
>>   tools/Kconfig              |   9 +
>>   tools/Makefile             |   1 +
>>   tools/eficapsule.h         | 110 ++++++++++++
>>   tools/mkeficapsule.c       | 106 +++++++-----
>>   tools/mkeficapsule_parse.c | 345 +++++++++++++++++++++++++++++++++++++
>>   5 files changed, 531 insertions(+), 40 deletions(-)
>>   create mode 100644 tools/mkeficapsule_parse.c
>>
> <snip>
>
>> diff --git a/tools/mkeficapsule_parse.c b/tools/mkeficapsule_parse.c
>> new file mode 100644
>> index 0000000000..ef4f3f6705
>> --- /dev/null
>> +++ b/tools/mkeficapsule_parse.c
>> @@ -0,0 +1,345 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright 2023 Linaro Limited
>> + */
>> +
>> +/*
>> + * The code in this file adds parsing ability to the mkeficapsule
>> + * tool. This allows specifying parameters needed to build the capsule
>> + * through the config file instead of specifying them on the command-line.
>> + * Parameters can be specified for more than one payload, generating the
>> + * corresponding capsule files.
>> + *
>> + * The parameters are specified in a "key:value" pair. All the parameters
>> + * that are currently supported by the mkeficapsule tool can be specified
>> + * in the config file.
>> + *
>> + * The example below shows four payloads. The first payload is an example
>> + * of generating a signed capsule. The second payload is an example of
>> + * generating an unsigned capsule. The third payload is an accept empty
>> + * capsule, while the fourth payload is the revert empty capsule, used
>> + * for the multi-bank firmware update feature.
>> + *
>> + * This functionality can be easily extended to generate a single capsule
>> + * comprising multiple payloads.
>> +
>> +     {
>> +         image-guid: 02f4d760-cfd5-43bd-8e2d-a42acb33c660
>> +         hardware-instance: 0
>> +         monotonic-count: 1
>> +         payload: u-boot.bin
>> +         image-index: 1
>> +         private-key: /path/to/priv/key
>> +         pub-key-cert: /path/to/pub/key
>> +         capsule: u-boot.capsule
>> +     }
>> +     {
>> +         image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e
>> +         hardware-instance: 0
>> +         payload: u-boot.itb
>> +         image-index: 2
>> +         oemflags: 0x8000
>> +         capsule: fit.capsule
>> +     }
>> +     {
>> +         capsule-type: accept
>> +         image-guid: 4ce292da-1dd8-428d-a1c2-77743ef8b96e
>> +         capsule: accept.capsule
>> +     }
>> +     {
>> +         capsule-type: revert
>> +         capsule: revert.capsule
>> +     }
>> +*/
>> +
>>
>> If i understand it correctly the EDK2 GenerateCapsule tool allows for multiple
>> payloads inside one capsule by specifying a list of payloads in the JSON-file.
>> I think something similar should be done here to support multiple payloads
>> inside one capsule. What about something like this:
>>
>> {
>>
>>    content: [{
>>      image-guid: 02f4d760-cfd5-43bd-8e2d-a42acb33c660
>>      hardware-instance: 0
>>      monotonic-count: 1
>>      payload: u-boot.bin
>>      image-index: 1
>>    },{
>>
>>      image-guid: 02f4d760-cfd5-43bd-8e2d-a42acb33c660
>>      hardware-instance: 1
>>      monotonic-count: 1
>>      payload: boot.bin
>>      image-index: 2
>>    }],
>>
>>    private-key: /path/to/priv/key
>>    pub-key-cert: /path/to/pub/key
>>    capsule: u-boot.capsule
>>
>> }
>>
>> ?
> I am aware of these additional brackets and the "Payloads" keyword
> that is used in the EDK2 json file. Adding this should not be a big
> effort. However, the reason I did not add this is that I did not see
> any value in adding this. I believe the EDK2 json file also is used
> for providing information about additional files, like optional driver
> images. But we don't support that with the u-boot tool. So will we be
> adding any value by putting these additional brackets.
>
> The other question is, in case of a single capsule file consisting of
> multiple payloads, how do we pass the name of the output capsule file.
> I see two options, one is having a field like "capsule" outside of all
> the payloads. The other is passing this through the command line. In
> any case, changes for extending this for supporting a single capsule
> should be sent along with your change of adding support for a single
> capsule file. I can work on adding this, but it would be good to get
> this in as part of your patches.
>
> -sughosh
Yes I agree. It is probably a good idea to sent it along with patches
extending the mkeficapusle tool. I will see how the discussion about my
suggested mkeficapsule extension goes and pick it up from there.

Best Regards
Malte
>
>> Best Regards
>> Malte
>>
>> +#include <ctype.h>
>> +#include <limits.h>
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +#include <string.h>
>> +
>> +#include <uuid/uuid.h>
>> +
>> +#include "eficapsule.h"
>> +
>> +#define PARAMS_START "{"
>> +#define PARAMS_END   "}"
>> +
>> +#define PSTART               2
>> +#define PEND         3
>> +
>> +#define MALLOC_FAIL_STR              "Unable to allocate memory\n"
>> +
>> +#define ARRAY_SIZE(x)                (sizeof(x) / sizeof((x)[0]))
>> +
>> +const char *capsule_params[] = {
>> +     "image-guid", "image-index", "private-key",
>> +     "pub-key-cert", "payload", "capsule",
>> +     "hardware-instance", "monotonic-count",
>> +     "capsule-type", "oemflags" };
>> +
>> +static unsigned char params_start;
>> +static unsigned char params_end;
>> +
>> +static void print_and_exit(const char *str)
>> +{
>> +     fprintf(stderr, "%s", str);
>> +     exit(EXIT_FAILURE);
>> +}
>> +
>> +static int param_delim_checks(char *line, unsigned char *token)
>> +{
>> +     if (!strcmp(line, PARAMS_START)) {
>> +             if (params_start || !params_end) {
>> +                     fprintf(stderr, "Earlier params processing still in progress. ");
>> +                     fprintf(stderr, "Can't start processing a new params.\n");
>> +                     exit(EXIT_FAILURE);
>> +             } else {
>> +                     params_start = 1;
>> +                     params_end = 0;
>> +                     *token = PSTART;
>> +                     return 1;
>> +             }
>> +     } else if (!strcmp(line, PARAMS_END)) {
>> +             if (!params_start) {
>> +                     fprintf(stderr, "Cannot put end braces without start braces. ");
>> +                     fprintf(stderr, "Please check the documentation for reference config file syntax\n");
>> +                     exit(EXIT_FAILURE);
>> +             } else {
>> +                     params_start = 0;
>> +                     params_end = 1;
>> +                     *token = PEND;
>> +                     return 1;
>> +             }
>> +     } else if (!params_start) {
>> +             fprintf(stderr, "Params should be passed within braces. ");
>> +             fprintf(stderr, "Please check the documentation for reference config file syntax\n");
>> +             exit(EXIT_FAILURE);
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static void add_guid(efi_guid_t **guid_param, char *guid)
>> +{
>> +     unsigned char uuid_buf[16];
>> +
>> +     *guid_param = malloc(sizeof(efi_guid_t));
>> +     if (!*guid_param)
>> +             print_and_exit(MALLOC_FAIL_STR);
>> +
>> +     if (uuid_parse(guid, uuid_buf))
>> +             print_and_exit("Wrong guid format\n");
>> +
>> +     convert_uuid_to_guid(uuid_buf);
>> +     memcpy(*guid_param, uuid_buf, sizeof(efi_guid_t));
>> +}
>> +
>> +static void add_string(char **dst, char *val)
>> +{
>> +     *dst = strdup(val);
>> +     if (!*dst)
>> +             print_and_exit(MALLOC_FAIL_STR);
>> +}
>> +
>> +static void match_and_populate_param(char *key, char *val,
>> +                                  struct efi_capsule_params *param)
>> +{
>> +     int i;
>> +
>> +     for (i = 0; i < ARRAY_SIZE(capsule_params); i++) {
>> +             if (!strcmp(key, capsule_params[i])) {
>> +                     switch (i) {
>> +                     case 0:
>> +                             add_guid(&param->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(&param->privkey_file, val);
>> +                             return;
>> +                     case 3:
>> +                             add_string(&param->cert_file, val);
>> +                             return;
>> +                     case 4:
>> +                             add_string(&param->input_file, val);
>> +                             return;
>> +                     case 5:
>> +                             add_string(&param->capsule_file, val);
>> +                             return;
>> +                     case 6:
>> +                             param->hardware_instance = strtoul(val, NULL, 0);
>> +                             if (param->hardware_instance == ULONG_MAX)
>> +                                     print_and_exit("Enter a valid hardware instance value");
>> +                             return;
>> +                     case 7:
>> +                             param->monotonic_count = strtoull(val, NULL, 0);
>> +                             if (param->monotonic_count == ULLONG_MAX)
>> +                                     print_and_exit("Enter a valid monotonic count value");
>> +                             return;
>> +                     case 8:
>> +                             if (!strcmp(val, "normal"))
>> +                                     param->capsule = CAPSULE_NORMAL_BLOB;
>> +                             else if (!strcmp(val, "accept"))
>> +                                     param->capsule = CAPSULE_ACCEPT;
>> +                             else if (!strcmp(val, "revert"))
>> +                                     param->capsule = CAPSULE_REVERT;
>> +                             else
>> +                                     print_and_exit("Invalid type of capsule");
>> +
>> +                             return;
>> +                     case 9:
>> +                             param->oemflags = strtoul(val, NULL, 0);
>> +                             if (param->oemflags > 0xffff)
>> +                                     print_and_exit("OemFlags must be between 0x0 and 0xffff\n");
>> +                             return;
>> +                     }
>> +             }
>> +     }
>> +
>> +     fprintf(stderr, "Undefined param %s specified. ", key);
>> +     fprintf(stderr, "Please check the documentation for reference config file syntax\n");
>> +     exit(EXIT_FAILURE);
>> +}
>> +
>> +static int get_capsule_params(char *line, struct efi_capsule_params *params)
>> +{
>> +     char *key = NULL;
>> +     char *val = NULL;
>> +     unsigned char token;
>> +
>> +     if (param_delim_checks(line, &token))
>> +             return token;
>> +
>> +     key = strtok(line, ":");
>> +     if (key)
>> +             val = strtok(NULL, "\0");
>> +     else
>> +             print_and_exit("Expect the params in a key:value pair\n");
>> +
>> +     match_and_populate_param(key, val, params);
>> +
>> +     return 0;
>> +}
>> +
>> +static char *skip_whitespace(char *line)
>> +{
>> +     char *ptr, *newline;
>> +
>> +     ptr = malloc(strlen(line) + 1);
>> +     if (!ptr)
>> +             print_and_exit(MALLOC_FAIL_STR);
>> +
>> +     for (newline = ptr; *line; line++)
>> +             if (!isblank(*line))
>> +                     *ptr++ = *line;
>> +     *ptr = '\0';
>> +     return newline;
>> +}
>> +
>> +static int parse_capsule_payload_params(FILE *fp, struct efi_capsule_params *params)
>> +{
>> +     char *line = NULL;
>> +     char *newline;
>> +     size_t n = 0;
>> +     ssize_t len;
>> +
>> +     while ((len = getline(&line, &n, fp)) != -1) {
>> +             if (len == 1 && line[len - 1] == '\n')
>> +                     continue;
>> +
>> +             line[len - 1] = '\0';
>> +
>> +             newline = skip_whitespace(line);
>> +
>> +             if (newline[0] == '#')
>> +                     continue;
>> +
>> +             if (get_capsule_params(newline, params) == PEND)
>> +                     return 0;
>> +     }
>> +
>> +     if (errno == EINVAL || errno == ENOMEM) {
>> +             fprintf(stderr, "getline() returned an error %s reading the line\n",
>> +                     strerror(errno));
>> +             exit(EXIT_FAILURE);
>> +     } else if (params_start == 1 || params_end == 0) {
>> +             fprintf(stderr, "Params should be passed within braces. ");
>> +             fprintf(stderr, "Please check the documentation for reference config file syntax\n");
>> +             exit(EXIT_FAILURE);
>> +     } else {
>> +             return -1;
>> +     }
>> +}
>> +
>> +static void params_dependency_check(struct efi_capsule_params *params)
>> +{
>> +     /* check necessary parameters */
>> +     if ((params->capsule == CAPSULE_NORMAL_BLOB &&
>> +          ((!params->input_file || !params->capsule_file ||
>> +            !params->image_guid) ||
>> +           ((params->privkey_file && !params->cert_file) ||
>> +            (!params->privkey_file && params->cert_file)))) ||
>> +         (params->capsule != CAPSULE_NORMAL_BLOB &&
>> +          (!params->capsule_file ||
>> +           (params->capsule == CAPSULE_ACCEPT && !params->image_guid) ||
>> +           (params->capsule == CAPSULE_REVERT && params->image_guid)))) {
>> +             print_usage();
>> +             exit(EXIT_FAILURE);
>> +     }
>> +}
>> +
>> +static void generate_capsule(struct efi_capsule_params *params)
>> +{
>> +     if (params->capsule != CAPSULE_NORMAL_BLOB) {
>> +             if (create_empty_capsule(params->capsule_file,
>> +                                      params->image_guid,
>> +                                      params->capsule ==
>> +                                      CAPSULE_ACCEPT) < 0)
>> +                     print_and_exit("Creating empty capsule failed\n");
>> +     } else if (create_fwbin(params->capsule_file, params->input_file,
>> +                           params->image_guid, params->image_index,
>> +                           params->hardware_instance,
>> +                           params->monotonic_count,
>> +                           params->privkey_file,
>> +                           params->cert_file,
>> +                           (uint16_t)params->oemflags) < 0) {
>> +             print_and_exit("Creating firmware capsule failed\n");
>> +     }
>> +}
>> +
>> +/**
>> + * capsule_with_cfg_file() - Generate capsule from config file
>> + * @cfg_file: Path to the config file
>> + *
>> + * Parse the capsule parameters from the config file and use the
>> + * parameters for generating one or more capsules.
>> + *
>> + * Return: None
>> + *
>> + */
>> +void capsule_with_cfg_file(const char *cfg_file)
>> +{
>> +     FILE *fp;
>> +     struct efi_capsule_params params = { 0 };
>> +
>> +     fp = fopen(cfg_file, "r");
>> +     if (!fp) {
>> +             fprintf(stderr, "Unable to open the capsule config file %s\n",
>> +                     cfg_file);
>> +             exit(EXIT_FAILURE);
>> +     }
>> +
>> +     params_start = 0;
>> +     params_end = 1;
>> +
>> +     while (parse_capsule_payload_params(fp, &params) != -1) {
>> +             params_dependency_check(&params);
>> +             generate_capsule(&params);
>> +
>> +             memset(&params, 0, sizeof(struct efi_capsule_params));
>> +     }
>> +}
>> --
>> 2.34.1
>>
>>


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 1/7] capsule: authenticate: Embed capsule public key in platform's dtb
  2023-06-15 16:11     ` Sughosh Ganu
@ 2023-06-19 12:36       ` Simon Glass
  2023-06-21  4:20         ` Sughosh Ganu
  0 siblings, 1 reply; 50+ messages in thread
From: Simon Glass @ 2023-06-19 12:36 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Michal Simek,
	Takahiro Akashi

Hi Sughosh,

On Thu, 15 Jun 2023 at 17:11, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
>
> hi Simon,
>
> On Thu, 15 Jun 2023 at 14:44, Simon Glass <sjg@chromium.org> wrote:
> >
> > Hi Sughosh,
> >
> > On Tue, 13 Jun 2023 at 11:41, 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 script for embedding the ESL used for capsule authentication in
> > > the platform's dtb, and call this as part of building the dtb(s). This
> > > brings the embedding of the ESL in the dtb into the u-boot build flow.
> > >
> > > 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>
> > > ---
> > >  lib/efi_loader/Kconfig       | 11 +++++++++++
> > >  scripts/Makefile.lib         |  8 ++++++++
> > >  scripts/embed_capsule_key.sh | 25 +++++++++++++++++++++++++
> > >  3 files changed, 44 insertions(+)
> > >  create mode 100755 scripts/embed_capsule_key.sh
> > >
> > > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> > > index c5835e6ef6..1326a1d109 100644
> > > --- a/lib/efi_loader/Kconfig
> > > +++ b/lib/efi_loader/Kconfig
> > > @@ -234,6 +234,17 @@ config EFI_CAPSULE_MAX
> > >           Select the max capsule index value used for capsule report
> > >           variables. This value is used to create CapsuleMax variable.
> > >
> > > +config EFI_CAPSULE_ESL_FILE
> > > +       string "Path to the EFI Signature List File"
> > > +       default ""
> > > +       depends on EFI_CAPSULE_AUTHENTICATE
> > > +       help
> > > +         Provides the absolute path to the EFI Signature List
> > > +         file which will be embedded in the platform's device
> > > +         tree and used for capsule authentication at the time
> > > +         of capsule update.
> > > +
> > > +
> > >  config EFI_DEVICE_PATH_TO_TEXT
> > >         bool "Device path to text protocol"
> > >         default y
> > > diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
> > > index 7b27224b5d..a4083d0a26 100644
> > > --- a/scripts/Makefile.lib
> > > +++ b/scripts/Makefile.lib
> > > @@ -192,6 +192,8 @@ dtc_cpp_flags  = -Wp,-MD,$(depfile).pre.tmp -nostdinc                    \
> > >                  -D__ASSEMBLY__                                          \
> > >                  -undef -D__DTS__
> > >
> > > +export dtc_cpp_flags
> > > +
> > >  # Finds the multi-part object the current object will be linked into
> > >  modname-multi = $(sort $(foreach m,$(multi-used),\
> > >                 $(if $(filter $(subst $(obj)/,,$*.o), $($(m:.o=-objs)) $($(m:.o=-y))),$(m:.o=))))
> > > @@ -315,6 +317,9 @@ ifeq ($(CONFIG_OF_LIBFDT_OVERLAY),y)
> > >  DTC_FLAGS += -@
> > >  endif
> > >
> > > +quiet_cmd_embedcapsulekey = EMBEDCAPSULEKEY $@
> > > +cmd_embedcapsulekey = $(srctree)/scripts/embed_capsule_key.sh $@
> > > +
> > >  quiet_cmd_dtc = DTC     $@
> > >  # Modified for U-Boot
> > >  # Bring in any U-Boot-specific include at the end of the file
> > > @@ -333,6 +338,9 @@ cmd_dtc = mkdir -p $(dir ${dtc-tmp}) ; \
> > >
> > >  $(obj)/%.dtb: $(src)/%.dts FORCE
> > >         $(call if_changed_dep,dtc)
> > > +ifeq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),y)
> > > +       $(call cmd,embedcapsulekey,$@)
> > > +endif
> > >
> > >  pre-tmp = $(subst $(comma),_,$(dot-target).pre.tmp)
> > >  dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp)
> > > diff --git a/scripts/embed_capsule_key.sh b/scripts/embed_capsule_key.sh
> > > new file mode 100755
> > > index 0000000000..1c2e45f758
> > > --- /dev/null
> > > +++ b/scripts/embed_capsule_key.sh
> > > @@ -0,0 +1,25 @@
> > > +#! /bin/bash
> > > +# SPDX-License-Identifier: GPL-2.0+
> > > +#
> > > +# Copyright (C) 2023, Linaro Limited
> > > +#
> > > +
> > > +gen_capsule_signature_file() {
> > > +cat >> $1 << EOF
> > > +/dts-v1/;
> > > +/plugin/;
> > > +
> > > +&{/} {
> > > +       signature {
> > > +               capsule-key = /incbin/(CONFIG_EFI_CAPSULE_ESL_FILE);
> > > +       };
> > > +};
> > > +EOF
> > > +}
> > > +
> > > +gen_capsule_signature_file signature.$$.dts > /dev/null 2>&1
> > > +$CPP $dtc_cpp_flags -x assembler-with-cpp -o signature.$$.tmp signature.$$.dts > /dev/null 2>&1
> > > +dtc -@ -O dtb -o signature.$$.dtbo signature.$$.tmp > /dev/null 2>&1
> > > +fdtoverlay -i $1 -o temp.$$.dtb -v signature.$$.dtbo > /dev/null 2>&1
> > > +mv temp.$$.dtb $1 > /dev/null 2>&1
> > > +rm -f signature.$$.* > /dev/null 2>&1
> > > --
> > > 2.34.1
> > >
> >
> > Can you please add this to binman instead?
>
> I had looked at using binman for this work earlier because I very much
> expected this comment from you :). Having said that, I am very much
> open to using binman instead if it turns out to be the better way of
> achieving this. What this patch does is that, with capsule
> authentication enabled, it embeds the public key esl file into the
> dtb's as they get built. As per my understanding, binman gets called
> at the end of the u-boot build, once the constituent images( e..g
> u-boot.bin = u-boot-no-dtb.bin + dtb) have been generated. So, if we
> call binman _after_ the requisite image(s) have been generated, we
> would need to 1) identify the dtb's in which the esl needs to be
> embedded, and then 2) generate the final image all over again. Don't
> you think this is non optimal? Or is there a way of generating the
> constituent images(including the dtb's) through binman instead?

The best way to do that IMO is to generate a second file, .e.g.
u-boot-capsule.bin

I don't think it is a good idea to add other junk to u-boot.bin. It
should just be U-Boot + dtb.

>
> My understanding of binman is that it is a tool of packaging
> constituent images together. But the constituent images are still
> being built through make targets.

In binman terminology, it collects a set of 'binaries' to produce
firmware 'images'.

Regards,
Simon

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/7] Makefile: Add a target for building capsules
  2023-06-15 16:25     ` Sughosh Ganu
@ 2023-06-19 12:37       ` Simon Glass
  2023-06-21  4:26         ` Sughosh Ganu
  0 siblings, 1 reply; 50+ messages in thread
From: Simon Glass @ 2023-06-19 12:37 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Michal Simek,
	Takahiro Akashi

Hi Sughosh,

On Thu, 15 Jun 2023 at 17:25, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
>
> hi Simon,
>
> On Thu, 15 Jun 2023 at 14:44, Simon Glass <sjg@chromium.org> wrote:
> >
> > Hi Sughosh,
> >
> > On Tue, 13 Jun 2023 at 11:39, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > >
> > > Add a target for building EFI capsules. The capsule parameters are
> > > specified through a config file, and the path to the config file is
> > > specified through CONFIG_EFI_CAPSULE_CFG_FILE. When the config file is
> > > not specified, the command only builds tools.
> > >
> > > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > > ---
> > >  Makefile | 9 +++++++++
> > >  1 file changed, 9 insertions(+)
> > >
> > > diff --git a/Makefile b/Makefile
> > > index 10bfaa52ad..96db29aa77 100644
> > > --- a/Makefile
> > > +++ b/Makefile
> > > @@ -1151,6 +1151,15 @@ dtbs: dts/dt.dtb
> > >  dts/dt.dtb: u-boot
> > >         $(Q)$(MAKE) $(build)=dts dtbs
> > >
> > > +quiet_cmd_mkeficapsule = MKEFICAPSULE $@
> > > +cmd_mkeficapsule = $(objtree)/tools/mkeficapsule $@
> > > +
> > > +PHONY += capsule
> > > +capsule: tools
> > > +ifneq ($(CONFIG_EFI_CAPSULE_CFG_FILE),"")
> > > +       $(call cmd,mkeficapsule)
> > > +endif
> > > +
> > >  quiet_cmd_copy = COPY    $@
> > >        cmd_copy = cp $< $@
> > >
> > > --
> > > 2.34.1
> > >
> >
> > We should be using binman to build images...you seem to be building
> > something in parallel with that. Can you please take a look at binman?
>
> Again, I had explored using binman for this task. The one issue where
> I find the above flow better is that I can simply build my payload
> image(s) followed by 'make capsule' to generate the capsules for
> earlier generated images. In it's current form, I don't see an easy
> way to enforce this dependency in binman when I want to build the
> payload followed by generation of capsules. I did see the mention of
> encapsulating an entry within another dependent entry, but I think
> that makes the implementation more complex than it ought to be.
>
> I think it is much easier to use the make flow to generate the images
> followed by capsules, instead of tweaking the binman node to first
> generate the payload images, followed by enabling the capsule node to
> build the capsules. If there is an easy way of enforcing this
> dependency, please let me know. Thanks

Can you share your explorations? I think the capsule should be created
as part of the build, if enabled. Rather than changing the input
files, binman should produce new output files.

We are trying to remove most of the output logic in Makefile. It
should just be producing input files for binman.

Regards,
Simon

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 1/7] capsule: authenticate: Embed capsule public key in platform's dtb
  2023-06-19 12:36       ` Simon Glass
@ 2023-06-21  4:20         ` Sughosh Ganu
  2023-06-26  9:07           ` Simon Glass
  0 siblings, 1 reply; 50+ messages in thread
From: Sughosh Ganu @ 2023-06-21  4:20 UTC (permalink / raw)
  To: Simon Glass
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Michal Simek,
	Takahiro Akashi

hi Simon,

On Mon, 19 Jun 2023 at 18:07, Simon Glass <sjg@chromium.org> wrote:
>
> Hi Sughosh,
>
> On Thu, 15 Jun 2023 at 17:11, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> >
> > hi Simon,
> >
> > On Thu, 15 Jun 2023 at 14:44, Simon Glass <sjg@chromium.org> wrote:
> > >
> > > Hi Sughosh,
> > >
> > > On Tue, 13 Jun 2023 at 11:41, 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 script for embedding the ESL used for capsule authentication in
> > > > the platform's dtb, and call this as part of building the dtb(s). This
> > > > brings the embedding of the ESL in the dtb into the u-boot build flow.
> > > >
> > > > 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>
> > > > ---
> > > >  lib/efi_loader/Kconfig       | 11 +++++++++++
> > > >  scripts/Makefile.lib         |  8 ++++++++
> > > >  scripts/embed_capsule_key.sh | 25 +++++++++++++++++++++++++
> > > >  3 files changed, 44 insertions(+)
> > > >  create mode 100755 scripts/embed_capsule_key.sh
> > > >
> > > > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> > > > index c5835e6ef6..1326a1d109 100644
> > > > --- a/lib/efi_loader/Kconfig
> > > > +++ b/lib/efi_loader/Kconfig
> > > > @@ -234,6 +234,17 @@ config EFI_CAPSULE_MAX
> > > >           Select the max capsule index value used for capsule report
> > > >           variables. This value is used to create CapsuleMax variable.
> > > >
> > > > +config EFI_CAPSULE_ESL_FILE
> > > > +       string "Path to the EFI Signature List File"
> > > > +       default ""
> > > > +       depends on EFI_CAPSULE_AUTHENTICATE
> > > > +       help
> > > > +         Provides the absolute path to the EFI Signature List
> > > > +         file which will be embedded in the platform's device
> > > > +         tree and used for capsule authentication at the time
> > > > +         of capsule update.
> > > > +
> > > > +
> > > >  config EFI_DEVICE_PATH_TO_TEXT
> > > >         bool "Device path to text protocol"
> > > >         default y
> > > > diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
> > > > index 7b27224b5d..a4083d0a26 100644
> > > > --- a/scripts/Makefile.lib
> > > > +++ b/scripts/Makefile.lib
> > > > @@ -192,6 +192,8 @@ dtc_cpp_flags  = -Wp,-MD,$(depfile).pre.tmp -nostdinc                    \
> > > >                  -D__ASSEMBLY__                                          \
> > > >                  -undef -D__DTS__
> > > >
> > > > +export dtc_cpp_flags
> > > > +
> > > >  # Finds the multi-part object the current object will be linked into
> > > >  modname-multi = $(sort $(foreach m,$(multi-used),\
> > > >                 $(if $(filter $(subst $(obj)/,,$*.o), $($(m:.o=-objs)) $($(m:.o=-y))),$(m:.o=))))
> > > > @@ -315,6 +317,9 @@ ifeq ($(CONFIG_OF_LIBFDT_OVERLAY),y)
> > > >  DTC_FLAGS += -@
> > > >  endif
> > > >
> > > > +quiet_cmd_embedcapsulekey = EMBEDCAPSULEKEY $@
> > > > +cmd_embedcapsulekey = $(srctree)/scripts/embed_capsule_key.sh $@
> > > > +
> > > >  quiet_cmd_dtc = DTC     $@
> > > >  # Modified for U-Boot
> > > >  # Bring in any U-Boot-specific include at the end of the file
> > > > @@ -333,6 +338,9 @@ cmd_dtc = mkdir -p $(dir ${dtc-tmp}) ; \
> > > >
> > > >  $(obj)/%.dtb: $(src)/%.dts FORCE
> > > >         $(call if_changed_dep,dtc)
> > > > +ifeq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),y)
> > > > +       $(call cmd,embedcapsulekey,$@)
> > > > +endif
> > > >
> > > >  pre-tmp = $(subst $(comma),_,$(dot-target).pre.tmp)
> > > >  dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp)
> > > > diff --git a/scripts/embed_capsule_key.sh b/scripts/embed_capsule_key.sh
> > > > new file mode 100755
> > > > index 0000000000..1c2e45f758
> > > > --- /dev/null
> > > > +++ b/scripts/embed_capsule_key.sh
> > > > @@ -0,0 +1,25 @@
> > > > +#! /bin/bash
> > > > +# SPDX-License-Identifier: GPL-2.0+
> > > > +#
> > > > +# Copyright (C) 2023, Linaro Limited
> > > > +#
> > > > +
> > > > +gen_capsule_signature_file() {
> > > > +cat >> $1 << EOF
> > > > +/dts-v1/;
> > > > +/plugin/;
> > > > +
> > > > +&{/} {
> > > > +       signature {
> > > > +               capsule-key = /incbin/(CONFIG_EFI_CAPSULE_ESL_FILE);
> > > > +       };
> > > > +};
> > > > +EOF
> > > > +}
> > > > +
> > > > +gen_capsule_signature_file signature.$$.dts > /dev/null 2>&1
> > > > +$CPP $dtc_cpp_flags -x assembler-with-cpp -o signature.$$.tmp signature.$$.dts > /dev/null 2>&1
> > > > +dtc -@ -O dtb -o signature.$$.dtbo signature.$$.tmp > /dev/null 2>&1
> > > > +fdtoverlay -i $1 -o temp.$$.dtb -v signature.$$.dtbo > /dev/null 2>&1
> > > > +mv temp.$$.dtb $1 > /dev/null 2>&1
> > > > +rm -f signature.$$.* > /dev/null 2>&1
> > > > --
> > > > 2.34.1
> > > >
> > >
> > > Can you please add this to binman instead?
> >
> > I had looked at using binman for this work earlier because I very much
> > expected this comment from you :). Having said that, I am very much
> > open to using binman instead if it turns out to be the better way of
> > achieving this. What this patch does is that, with capsule
> > authentication enabled, it embeds the public key esl file into the
> > dtb's as they get built. As per my understanding, binman gets called
> > at the end of the u-boot build, once the constituent images( e..g
> > u-boot.bin = u-boot-no-dtb.bin + dtb) have been generated. So, if we
> > call binman _after_ the requisite image(s) have been generated, we
> > would need to 1) identify the dtb's in which the esl needs to be
> > embedded, and then 2) generate the final image all over again. Don't
> > you think this is non optimal? Or is there a way of generating the
> > constituent images(including the dtb's) through binman instead?
>
> The best way to do that IMO is to generate a second file, .e.g.
> u-boot-capsule.bin

That would break the scripts for platforms which might be using
u-boot.bin as the image to boot from. I know that the ST platform
which does enable capsule updates uses the u-boot-nodtb.bin as the
BL33 image and the u-boot.dtb as BL33_CFG. Hence my question, if we
have to use binman, is there a way to 1) modify the u-boot.dtb and
then 2) regenerate u-boot.bin image.

I know this is software, and everything can be done in a hacky way.
But I was exploring using the u-boot node as a section entry, so that
the u-boot.dtb can be modified and then binman would
repackage/regenerate the u-boot.bin. But this is not working.

>
> I don't think it is a good idea to add other junk to u-boot.bin. It
> should just be U-Boot + dtb.

No junk is being added to u-boot.bin. Just that, as the platform
builds dtb's, the ESL file gets embedded into them as a property under
the signature node. There is no additional image being added to the
the u-boot.bin.

-sughosh

>
> >
> > My understanding of binman is that it is a tool of packaging
> > constituent images together. But the constituent images are still
> > being built through make targets.
>
> In binman terminology, it collects a set of 'binaries' to produce
> firmware 'images'.
>
> Regards,
> Simon

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/7] Makefile: Add a target for building capsules
  2023-06-19 12:37       ` Simon Glass
@ 2023-06-21  4:26         ` Sughosh Ganu
  2023-06-26  9:07           ` Simon Glass
  0 siblings, 1 reply; 50+ messages in thread
From: Sughosh Ganu @ 2023-06-21  4:26 UTC (permalink / raw)
  To: Simon Glass
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Michal Simek,
	Takahiro Akashi

hi Simon,

On Mon, 19 Jun 2023 at 18:07, Simon Glass <sjg@chromium.org> wrote:
>
> Hi Sughosh,
>
> On Thu, 15 Jun 2023 at 17:25, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> >
> > hi Simon,
> >
> > On Thu, 15 Jun 2023 at 14:44, Simon Glass <sjg@chromium.org> wrote:
> > >
> > > Hi Sughosh,
> > >
> > > On Tue, 13 Jun 2023 at 11:39, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > >
> > > > Add a target for building EFI capsules. The capsule parameters are
> > > > specified through a config file, and the path to the config file is
> > > > specified through CONFIG_EFI_CAPSULE_CFG_FILE. When the config file is
> > > > not specified, the command only builds tools.
> > > >
> > > > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > > > ---
> > > >  Makefile | 9 +++++++++
> > > >  1 file changed, 9 insertions(+)
> > > >
> > > > diff --git a/Makefile b/Makefile
> > > > index 10bfaa52ad..96db29aa77 100644
> > > > --- a/Makefile
> > > > +++ b/Makefile
> > > > @@ -1151,6 +1151,15 @@ dtbs: dts/dt.dtb
> > > >  dts/dt.dtb: u-boot
> > > >         $(Q)$(MAKE) $(build)=dts dtbs
> > > >
> > > > +quiet_cmd_mkeficapsule = MKEFICAPSULE $@
> > > > +cmd_mkeficapsule = $(objtree)/tools/mkeficapsule $@
> > > > +
> > > > +PHONY += capsule
> > > > +capsule: tools
> > > > +ifneq ($(CONFIG_EFI_CAPSULE_CFG_FILE),"")
> > > > +       $(call cmd,mkeficapsule)
> > > > +endif
> > > > +
> > > >  quiet_cmd_copy = COPY    $@
> > > >        cmd_copy = cp $< $@
> > > >
> > > > --
> > > > 2.34.1
> > > >
> > >
> > > We should be using binman to build images...you seem to be building
> > > something in parallel with that. Can you please take a look at binman?
> >
> > Again, I had explored using binman for this task. The one issue where
> > I find the above flow better is that I can simply build my payload
> > image(s) followed by 'make capsule' to generate the capsules for
> > earlier generated images. In it's current form, I don't see an easy
> > way to enforce this dependency in binman when I want to build the
> > payload followed by generation of capsules. I did see the mention of
> > encapsulating an entry within another dependent entry, but I think
> > that makes the implementation more complex than it ought to be.
> >
> > I think it is much easier to use the make flow to generate the images
> > followed by capsules, instead of tweaking the binman node to first
> > generate the payload images, followed by enabling the capsule node to
> > build the capsules. If there is an easy way of enforcing this
> > dependency, please let me know. Thanks
>
> Can you share your explorations? I think the capsule should be created
> as part of the build, if enabled. Rather than changing the input
> files, binman should produce new output files.

This is an issue of handling dependencies in binman, and not changing
input files. We do not have support for telling binman "build/generate
this particular image first before you proceed to build the capsules
using the earlier built images". I am not sure if this can be done in
a generic manner in binman, so that irrespective of the image being
generated, it can be specified to build capsules once the capsule
input images have been generated.

>
> We are trying to remove most of the output logic in Makefile. It
> should just be producing input files for binman.

I understand. However, like I mentioned above, as of now, we don't
have a way of handling dependencies in binman, at least in a generic
manner. Once this support gets added, I know that it would be trivial
to add support for building capsules in binman.

-sughosh

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 1/7] capsule: authenticate: Embed capsule public key in platform's dtb
  2023-06-21  4:20         ` Sughosh Ganu
@ 2023-06-26  9:07           ` Simon Glass
  2023-06-26  9:53             ` Ilias Apalodimas
  0 siblings, 1 reply; 50+ messages in thread
From: Simon Glass @ 2023-06-26  9:07 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Michal Simek,
	Takahiro Akashi

Hi Sughosh,

On Wed, 21 Jun 2023 at 05:20, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
>
> hi Simon,
>
> On Mon, 19 Jun 2023 at 18:07, Simon Glass <sjg@chromium.org> wrote:
> >
> > Hi Sughosh,
> >
> > On Thu, 15 Jun 2023 at 17:11, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > >
> > > hi Simon,
> > >
> > > On Thu, 15 Jun 2023 at 14:44, Simon Glass <sjg@chromium.org> wrote:
> > > >
> > > > Hi Sughosh,
> > > >
> > > > On Tue, 13 Jun 2023 at 11:41, 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 script for embedding the ESL used for capsule authentication in
> > > > > the platform's dtb, and call this as part of building the dtb(s). This
> > > > > brings the embedding of the ESL in the dtb into the u-boot build flow.
> > > > >
> > > > > 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>
> > > > > ---
> > > > >  lib/efi_loader/Kconfig       | 11 +++++++++++
> > > > >  scripts/Makefile.lib         |  8 ++++++++
> > > > >  scripts/embed_capsule_key.sh | 25 +++++++++++++++++++++++++
> > > > >  3 files changed, 44 insertions(+)
> > > > >  create mode 100755 scripts/embed_capsule_key.sh
> > > > >
> > > > > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> > > > > index c5835e6ef6..1326a1d109 100644
> > > > > --- a/lib/efi_loader/Kconfig
> > > > > +++ b/lib/efi_loader/Kconfig
> > > > > @@ -234,6 +234,17 @@ config EFI_CAPSULE_MAX
> > > > >           Select the max capsule index value used for capsule report
> > > > >           variables. This value is used to create CapsuleMax variable.
> > > > >
> > > > > +config EFI_CAPSULE_ESL_FILE
> > > > > +       string "Path to the EFI Signature List File"
> > > > > +       default ""
> > > > > +       depends on EFI_CAPSULE_AUTHENTICATE
> > > > > +       help
> > > > > +         Provides the absolute path to the EFI Signature List
> > > > > +         file which will be embedded in the platform's device
> > > > > +         tree and used for capsule authentication at the time
> > > > > +         of capsule update.
> > > > > +
> > > > > +
> > > > >  config EFI_DEVICE_PATH_TO_TEXT
> > > > >         bool "Device path to text protocol"
> > > > >         default y
> > > > > diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
> > > > > index 7b27224b5d..a4083d0a26 100644
> > > > > --- a/scripts/Makefile.lib
> > > > > +++ b/scripts/Makefile.lib
> > > > > @@ -192,6 +192,8 @@ dtc_cpp_flags  = -Wp,-MD,$(depfile).pre.tmp -nostdinc                    \
> > > > >                  -D__ASSEMBLY__                                          \
> > > > >                  -undef -D__DTS__
> > > > >
> > > > > +export dtc_cpp_flags
> > > > > +
> > > > >  # Finds the multi-part object the current object will be linked into
> > > > >  modname-multi = $(sort $(foreach m,$(multi-used),\
> > > > >                 $(if $(filter $(subst $(obj)/,,$*.o), $($(m:.o=-objs)) $($(m:.o=-y))),$(m:.o=))))
> > > > > @@ -315,6 +317,9 @@ ifeq ($(CONFIG_OF_LIBFDT_OVERLAY),y)
> > > > >  DTC_FLAGS += -@
> > > > >  endif
> > > > >
> > > > > +quiet_cmd_embedcapsulekey = EMBEDCAPSULEKEY $@
> > > > > +cmd_embedcapsulekey = $(srctree)/scripts/embed_capsule_key.sh $@
> > > > > +
> > > > >  quiet_cmd_dtc = DTC     $@
> > > > >  # Modified for U-Boot
> > > > >  # Bring in any U-Boot-specific include at the end of the file
> > > > > @@ -333,6 +338,9 @@ cmd_dtc = mkdir -p $(dir ${dtc-tmp}) ; \
> > > > >
> > > > >  $(obj)/%.dtb: $(src)/%.dts FORCE
> > > > >         $(call if_changed_dep,dtc)
> > > > > +ifeq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),y)
> > > > > +       $(call cmd,embedcapsulekey,$@)
> > > > > +endif
> > > > >
> > > > >  pre-tmp = $(subst $(comma),_,$(dot-target).pre.tmp)
> > > > >  dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp)
> > > > > diff --git a/scripts/embed_capsule_key.sh b/scripts/embed_capsule_key.sh
> > > > > new file mode 100755
> > > > > index 0000000000..1c2e45f758
> > > > > --- /dev/null
> > > > > +++ b/scripts/embed_capsule_key.sh
> > > > > @@ -0,0 +1,25 @@
> > > > > +#! /bin/bash
> > > > > +# SPDX-License-Identifier: GPL-2.0+
> > > > > +#
> > > > > +# Copyright (C) 2023, Linaro Limited
> > > > > +#
> > > > > +
> > > > > +gen_capsule_signature_file() {
> > > > > +cat >> $1 << EOF
> > > > > +/dts-v1/;
> > > > > +/plugin/;
> > > > > +
> > > > > +&{/} {
> > > > > +       signature {
> > > > > +               capsule-key = /incbin/(CONFIG_EFI_CAPSULE_ESL_FILE);
> > > > > +       };
> > > > > +};
> > > > > +EOF
> > > > > +}
> > > > > +
> > > > > +gen_capsule_signature_file signature.$$.dts > /dev/null 2>&1
> > > > > +$CPP $dtc_cpp_flags -x assembler-with-cpp -o signature.$$.tmp signature.$$.dts > /dev/null 2>&1
> > > > > +dtc -@ -O dtb -o signature.$$.dtbo signature.$$.tmp > /dev/null 2>&1
> > > > > +fdtoverlay -i $1 -o temp.$$.dtb -v signature.$$.dtbo > /dev/null 2>&1
> > > > > +mv temp.$$.dtb $1 > /dev/null 2>&1
> > > > > +rm -f signature.$$.* > /dev/null 2>&1
> > > > > --
> > > > > 2.34.1
> > > > >
> > > >
> > > > Can you please add this to binman instead?
> > >
> > > I had looked at using binman for this work earlier because I very much
> > > expected this comment from you :). Having said that, I am very much
> > > open to using binman instead if it turns out to be the better way of
> > > achieving this. What this patch does is that, with capsule
> > > authentication enabled, it embeds the public key esl file into the
> > > dtb's as they get built. As per my understanding, binman gets called
> > > at the end of the u-boot build, once the constituent images( e..g
> > > u-boot.bin = u-boot-no-dtb.bin + dtb) have been generated. So, if we
> > > call binman _after_ the requisite image(s) have been generated, we
> > > would need to 1) identify the dtb's in which the esl needs to be
> > > embedded, and then 2) generate the final image all over again. Don't
> > > you think this is non optimal? Or is there a way of generating the
> > > constituent images(including the dtb's) through binman instead?
> >
> > The best way to do that IMO is to generate a second file, .e.g.
> > u-boot-capsule.bin
>
> That would break the scripts for platforms which might be using
> u-boot.bin as the image to boot from. I know that the ST platform
> which does enable capsule updates uses the u-boot-nodtb.bin as the
> BL33 image and the u-boot.dtb as BL33_CFG. Hence my question, if we
> have to use binman, is there a way to 1) modify the u-boot.dtb and
> then 2) regenerate u-boot.bin image.
>
> I know this is software, and everything can be done in a hacky way.
> But I was exploring using the u-boot node as a section entry, so that
> the u-boot.dtb can be modified and then binman would
> repackage/regenerate the u-boot.bin. But this is not working.

NO, please do not do that.  You should create a new file, not modify
u-boot.bin or u-boot.dtb. Please just don't mess around with this, it
will lead to all sorts of confusion.

I thought we already had this discussion a while back?

>
> >
> > I don't think it is a good idea to add other junk to u-boot.bin. It
> > should just be U-Boot + dtb.
>
> No junk is being added to u-boot.bin. Just that, as the platform
> builds dtb's, the ESL file gets embedded into them as a property under
> the signature node. There is no additional image being added to the
> the u-boot.bin.

This needs to be done in a separte file, as above.

Regards,
Simon

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/7] Makefile: Add a target for building capsules
  2023-06-21  4:26         ` Sughosh Ganu
@ 2023-06-26  9:07           ` Simon Glass
  2023-06-26 12:13             ` Sughosh Ganu
  0 siblings, 1 reply; 50+ messages in thread
From: Simon Glass @ 2023-06-26  9:07 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Michal Simek,
	Takahiro Akashi

Hi Sughosh,

On Wed, 21 Jun 2023 at 05:26, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
>
> hi Simon,
>
> On Mon, 19 Jun 2023 at 18:07, Simon Glass <sjg@chromium.org> wrote:
> >
> > Hi Sughosh,
> >
> > On Thu, 15 Jun 2023 at 17:25, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > >
> > > hi Simon,
> > >
> > > On Thu, 15 Jun 2023 at 14:44, Simon Glass <sjg@chromium.org> wrote:
> > > >
> > > > Hi Sughosh,
> > > >
> > > > On Tue, 13 Jun 2023 at 11:39, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > >
> > > > > Add a target for building EFI capsules. The capsule parameters are
> > > > > specified through a config file, and the path to the config file is
> > > > > specified through CONFIG_EFI_CAPSULE_CFG_FILE. When the config file is
> > > > > not specified, the command only builds tools.
> > > > >
> > > > > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > > > > ---
> > > > >  Makefile | 9 +++++++++
> > > > >  1 file changed, 9 insertions(+)
> > > > >
> > > > > diff --git a/Makefile b/Makefile
> > > > > index 10bfaa52ad..96db29aa77 100644
> > > > > --- a/Makefile
> > > > > +++ b/Makefile
> > > > > @@ -1151,6 +1151,15 @@ dtbs: dts/dt.dtb
> > > > >  dts/dt.dtb: u-boot
> > > > >         $(Q)$(MAKE) $(build)=dts dtbs
> > > > >
> > > > > +quiet_cmd_mkeficapsule = MKEFICAPSULE $@
> > > > > +cmd_mkeficapsule = $(objtree)/tools/mkeficapsule $@
> > > > > +
> > > > > +PHONY += capsule
> > > > > +capsule: tools
> > > > > +ifneq ($(CONFIG_EFI_CAPSULE_CFG_FILE),"")
> > > > > +       $(call cmd,mkeficapsule)
> > > > > +endif
> > > > > +
> > > > >  quiet_cmd_copy = COPY    $@
> > > > >        cmd_copy = cp $< $@
> > > > >
> > > > > --
> > > > > 2.34.1
> > > > >
> > > >
> > > > We should be using binman to build images...you seem to be building
> > > > something in parallel with that. Can you please take a look at binman?
> > >
> > > Again, I had explored using binman for this task. The one issue where
> > > I find the above flow better is that I can simply build my payload
> > > image(s) followed by 'make capsule' to generate the capsules for
> > > earlier generated images. In it's current form, I don't see an easy
> > > way to enforce this dependency in binman when I want to build the
> > > payload followed by generation of capsules. I did see the mention of
> > > encapsulating an entry within another dependent entry, but I think
> > > that makes the implementation more complex than it ought to be.
> > >
> > > I think it is much easier to use the make flow to generate the images
> > > followed by capsules, instead of tweaking the binman node to first
> > > generate the payload images, followed by enabling the capsule node to
> > > build the capsules. If there is an easy way of enforcing this
> > > dependency, please let me know. Thanks
> >
> > Can you share your explorations? I think the capsule should be created
> > as part of the build, if enabled. Rather than changing the input
> > files, binman should produce new output files.
>
> This is an issue of handling dependencies in binman, and not changing
> input files. We do not have support for telling binman "build/generate
> this particular image first before you proceed to build the capsules
> using the earlier built images". I am not sure if this can be done in
> a generic manner in binman, so that irrespective of the image being
> generated, it can be specified to build capsules once the capsule
> input images have been generated.

I'm just not sure what you are getting out here.

See INPUTS-y for the input files to binman. Then binman uses these to
generate output files. It does not mess with the input files, nor
should it. Please read the top part of the Binman motivation to
understand how all this works.

At the risk of repeating myself, we want the Makefile to focus on
building U-Boot, with Binman handling the laterprocessing of those
files. Binman may run as part of the U-Boot build, and/or can be run
later, with the input files.

>
> >
> > We are trying to remove most of the output logic in Makefile. It
> > should just be producing input files for binman.
>
> I understand. However, like I mentioned above, as of now, we don't
> have a way of handling dependencies in binman, at least in a generic
> manner. Once this support gets added, I know that it would be trivial
> to add support for building capsules in binman.

What dependencies do you need? Please describe it in a simple manner
so I can understand. It cannot involve change the binman input files.

Regards,
Simon

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 1/7] capsule: authenticate: Embed capsule public key in platform's dtb
  2023-06-26  9:07           ` Simon Glass
@ 2023-06-26  9:53             ` Ilias Apalodimas
  2023-06-26 11:19               ` Simon Glass
  0 siblings, 1 reply; 50+ messages in thread
From: Ilias Apalodimas @ 2023-06-26  9:53 UTC (permalink / raw)
  To: Simon Glass
  Cc: Sughosh Ganu, u-boot, Heinrich Schuchardt, Michal Simek, Takahiro Akashi

Hi Simon,

[...]

> > > > > > +
> > > > > > +gen_capsule_signature_file signature.$$.dts > /dev/null 2>&1
> > > > > > +$CPP $dtc_cpp_flags -x assembler-with-cpp -o signature.$$.tmp signature.$$.dts > /dev/null 2>&1
> > > > > > +dtc -@ -O dtb -o signature.$$.dtbo signature.$$.tmp > /dev/null 2>&1
> > > > > > +fdtoverlay -i $1 -o temp.$$.dtb -v signature.$$.dtbo > /dev/null 2>&1
> > > > > > +mv temp.$$.dtb $1 > /dev/null 2>&1
> > > > > > +rm -f signature.$$.* > /dev/null 2>&1
> > > > > > --
> > > > > > 2.34.1
> > > > > >
> > > > >
> > > > > Can you please add this to binman instead?
> > > >
> > > > I had looked at using binman for this work earlier because I very much
> > > > expected this comment from you :). Having said that, I am very much
> > > > open to using binman instead if it turns out to be the better way of
> > > > achieving this. What this patch does is that, with capsule
> > > > authentication enabled, it embeds the public key esl file into the
> > > > dtb's as they get built. As per my understanding, binman gets called
> > > > at the end of the u-boot build, once the constituent images( e..g
> > > > u-boot.bin = u-boot-no-dtb.bin + dtb) have been generated. So, if we
> > > > call binman _after_ the requisite image(s) have been generated, we
> > > > would need to 1) identify the dtb's in which the esl needs to be
> > > > embedded, and then 2) generate the final image all over again. Don't
> > > > you think this is non optimal? Or is there a way of generating the
> > > > constituent images(including the dtb's) through binman instead?
> > >
> > > The best way to do that IMO is to generate a second file, .e.g.
> > > u-boot-capsule.bin
> >

This make no sense to me whatsoever.  Do we have an example in u-boot
generating multiple dtb versions for other reasons/subsystems?

> > That would break the scripts for platforms which might be using
> > u-boot.bin as the image to boot from. I know that the ST platform
> > which does enable capsule updates uses the u-boot-nodtb.bin as the
> > BL33 image and the u-boot.dtb as BL33_CFG. Hence my question, if we
> > have to use binman, is there a way to 1) modify the u-boot.dtb and
> > then 2) regenerate u-boot.bin image.
> >
> > I know this is software, and everything can be done in a hacky way.
> > But I was exploring using the u-boot node as a section entry, so that
> > the u-boot.dtb can be modified and then binman would
> > repackage/regenerate the u-boot.bin. But this is not working.
>
> NO, please do not do that.  You should create a new file, not modify
> u-boot.bin or u-boot.dtb. Please just don't mess around with this, it
> will lead to all sorts of confusion.
>
> I thought we already had this discussion a while back?

No we haven't.  In fact I am struggling to see the confusion part.  It's
fine for the u-boot dtb to include all the internal nodes DM needs, but
suddenly having the capsule signature is problematic?

In the past the .esl file was part of the U-Boot binary and things were
working perfectly fine.  In fact you could update/downgrade u-boot and the
signatures naturally followed along instead of having to update u-boot
*and* the dtb, which we have to do today. You could also build a capsule
way easier without injecting/removing signatures to the dtb.
You were the one that insisted on reverting that and instead adding it on
the dtb.  We explained most of the downsides back then, along with some
security concerns.  We also mentioned that the signature in the dtb makes
little sense since it's difference *per class of boards* and it's not
something we could include in static dtb files, but that lead nowhere...

As Sughosh already said there are platforms that use the generated u-boot
dtb and the raw binary to assemble a FIP image.  So why exactly adding the
capsule signature to the default dtb is wrong?  Why should we add an
*extra* .dtb with one additional node -- which is very much needed by the
design you proposed.  This will only lead to extra confusion.


Thanks
/Ilias
>
> >
> > >
> > > I don't think it is a good idea to add other junk to u-boot.bin. It
> > > should just be U-Boot + dtb.
> >
> > No junk is being added to u-boot.bin. Just that, as the platform
> > builds dtb's, the ESL file gets embedded into them as a property under
> > the signature node. There is no additional image being added to the
> > the u-boot.bin.
>
> This needs to be done in a separte file, as above.
>
> Regards,
> Simon

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 1/7] capsule: authenticate: Embed capsule public key in platform's dtb
  2023-06-26  9:53             ` Ilias Apalodimas
@ 2023-06-26 11:19               ` Simon Glass
  2023-06-27  9:54                 ` Ilias Apalodimas
  0 siblings, 1 reply; 50+ messages in thread
From: Simon Glass @ 2023-06-26 11:19 UTC (permalink / raw)
  To: Ilias Apalodimas
  Cc: Sughosh Ganu, u-boot, Heinrich Schuchardt, Michal Simek, Takahiro Akashi

Hi Ilias,

On Mon, 26 Jun 2023 at 10:53, Ilias Apalodimas
<ilias.apalodimas@linaro.org> wrote:
>
> Hi Simon,
>
> [...]
>
> > > > > > > +
> > > > > > > +gen_capsule_signature_file signature.$$.dts > /dev/null 2>&1
> > > > > > > +$CPP $dtc_cpp_flags -x assembler-with-cpp -o signature.$$.tmp signature.$$.dts > /dev/null 2>&1
> > > > > > > +dtc -@ -O dtb -o signature.$$.dtbo signature.$$.tmp > /dev/null 2>&1
> > > > > > > +fdtoverlay -i $1 -o temp.$$.dtb -v signature.$$.dtbo > /dev/null 2>&1
> > > > > > > +mv temp.$$.dtb $1 > /dev/null 2>&1
> > > > > > > +rm -f signature.$$.* > /dev/null 2>&1
> > > > > > > --
> > > > > > > 2.34.1
> > > > > > >
> > > > > >
> > > > > > Can you please add this to binman instead?
> > > > >
> > > > > I had looked at using binman for this work earlier because I very much
> > > > > expected this comment from you :). Having said that, I am very much
> > > > > open to using binman instead if it turns out to be the better way of
> > > > > achieving this. What this patch does is that, with capsule
> > > > > authentication enabled, it embeds the public key esl file into the
> > > > > dtb's as they get built. As per my understanding, binman gets called
> > > > > at the end of the u-boot build, once the constituent images( e..g
> > > > > u-boot.bin = u-boot-no-dtb.bin + dtb) have been generated. So, if we
> > > > > call binman _after_ the requisite image(s) have been generated, we
> > > > > would need to 1) identify the dtb's in which the esl needs to be
> > > > > embedded, and then 2) generate the final image all over again. Don't
> > > > > you think this is non optimal? Or is there a way of generating the
> > > > > constituent images(including the dtb's) through binman instead?
> > > >
> > > > The best way to do that IMO is to generate a second file, .e.g.
> > > > u-boot-capsule.bin
> > >
>
> This make no sense to me whatsoever.  Do we have an example in u-boot
> generating multiple dtb versions for other reasons/subsystems?
>
> > > That would break the scripts for platforms which might be using
> > > u-boot.bin as the image to boot from. I know that the ST platform
> > > which does enable capsule updates uses the u-boot-nodtb.bin as the
> > > BL33 image and the u-boot.dtb as BL33_CFG. Hence my question, if we
> > > have to use binman, is there a way to 1) modify the u-boot.dtb and
> > > then 2) regenerate u-boot.bin image.
> > >
> > > I know this is software, and everything can be done in a hacky way.
> > > But I was exploring using the u-boot node as a section entry, so that
> > > the u-boot.dtb can be modified and then binman would
> > > repackage/regenerate the u-boot.bin. But this is not working.
> >
> > NO, please do not do that.  You should create a new file, not modify
> > u-boot.bin or u-boot.dtb. Please just don't mess around with this, it
> > will lead to all sorts of confusion.
> >
> > I thought we already had this discussion a while back?
>
> No we haven't.  In fact I am struggling to see the confusion part.  It's
> fine for the u-boot dtb to include all the internal nodes DM needs, but
> suddenly having the capsule signature is problematic?
>
> In the past the .esl file was part of the U-Boot binary and things were
> working perfectly fine.  In fact you could update/downgrade u-boot and the
> signatures naturally followed along instead of having to update u-boot
> *and* the dtb, which we have to do today. You could also build a capsule
> way easier without injecting/removing signatures to the dtb.
> You were the one that insisted on reverting that and instead adding it on
> the dtb.  We explained most of the downsides back then, along with some
> security concerns.  We also mentioned that the signature in the dtb makes
> little sense since it's difference *per class of boards* and it's not
> something we could include in static dtb files, but that lead nowhere...
>
> As Sughosh already said there are platforms that use the generated u-boot
> dtb and the raw binary to assemble a FIP image.  So why exactly adding the
> capsule signature to the default dtb is wrong?  Why should we add an
> *extra* .dtb with one additional node -- which is very much needed by the
> design you proposed.  This will only lead to extra confusion.

1. I thought a capsule update was going to be a separate file, e.g.
u-boot-capture.bin ?

2. You can't put the signature into U-Boot. It needs to be in the
capsule update so that U-Boot can check it. If you are talking about
the public key, then yes that needs to be in U-Boot

3. Where does the public key come from? With the normal verified boot
flow it is created (and added to the dtb) as a separate signing step
after U-Boot is built.

4. Off topic, but re FIP, can someone just kill that off and use FIT?
It is such a shame that a new format was invented...

Regards,
Simon

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/7] Makefile: Add a target for building capsules
  2023-06-26  9:07           ` Simon Glass
@ 2023-06-26 12:13             ` Sughosh Ganu
  2023-06-27  4:57               ` Sughosh Ganu
  0 siblings, 1 reply; 50+ messages in thread
From: Sughosh Ganu @ 2023-06-26 12:13 UTC (permalink / raw)
  To: Simon Glass
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Michal Simek,
	Takahiro Akashi

hi Simon,

On Mon, 26 Jun 2023 at 14:38, Simon Glass <sjg@chromium.org> wrote:
>
> Hi Sughosh,
>
> On Wed, 21 Jun 2023 at 05:26, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> >
> > hi Simon,
> >
> > On Mon, 19 Jun 2023 at 18:07, Simon Glass <sjg@chromium.org> wrote:
> > >
> > > Hi Sughosh,
> > >
> > > On Thu, 15 Jun 2023 at 17:25, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > >
> > > > hi Simon,
> > > >
> > > > On Thu, 15 Jun 2023 at 14:44, Simon Glass <sjg@chromium.org> wrote:
> > > > >
> > > > > Hi Sughosh,
> > > > >
> > > > > On Tue, 13 Jun 2023 at 11:39, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > >
> > > > > > Add a target for building EFI capsules. The capsule parameters are
> > > > > > specified through a config file, and the path to the config file is
> > > > > > specified through CONFIG_EFI_CAPSULE_CFG_FILE. When the config file is
> > > > > > not specified, the command only builds tools.
> > > > > >
> > > > > > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > > > > > ---
> > > > > >  Makefile | 9 +++++++++
> > > > > >  1 file changed, 9 insertions(+)
> > > > > >
> > > > > > diff --git a/Makefile b/Makefile
> > > > > > index 10bfaa52ad..96db29aa77 100644
> > > > > > --- a/Makefile
> > > > > > +++ b/Makefile
> > > > > > @@ -1151,6 +1151,15 @@ dtbs: dts/dt.dtb
> > > > > >  dts/dt.dtb: u-boot
> > > > > >         $(Q)$(MAKE) $(build)=dts dtbs
> > > > > >
> > > > > > +quiet_cmd_mkeficapsule = MKEFICAPSULE $@
> > > > > > +cmd_mkeficapsule = $(objtree)/tools/mkeficapsule $@
> > > > > > +
> > > > > > +PHONY += capsule
> > > > > > +capsule: tools
> > > > > > +ifneq ($(CONFIG_EFI_CAPSULE_CFG_FILE),"")
> > > > > > +       $(call cmd,mkeficapsule)
> > > > > > +endif
> > > > > > +
> > > > > >  quiet_cmd_copy = COPY    $@
> > > > > >        cmd_copy = cp $< $@
> > > > > >
> > > > > > --
> > > > > > 2.34.1
> > > > > >
> > > > >
> > > > > We should be using binman to build images...you seem to be building
> > > > > something in parallel with that. Can you please take a look at binman?
> > > >
> > > > Again, I had explored using binman for this task. The one issue where
> > > > I find the above flow better is that I can simply build my payload
> > > > image(s) followed by 'make capsule' to generate the capsules for
> > > > earlier generated images. In it's current form, I don't see an easy
> > > > way to enforce this dependency in binman when I want to build the
> > > > payload followed by generation of capsules. I did see the mention of
> > > > encapsulating an entry within another dependent entry, but I think
> > > > that makes the implementation more complex than it ought to be.
> > > >
> > > > I think it is much easier to use the make flow to generate the images
> > > > followed by capsules, instead of tweaking the binman node to first
> > > > generate the payload images, followed by enabling the capsule node to
> > > > build the capsules. If there is an easy way of enforcing this
> > > > dependency, please let me know. Thanks
> > >
> > > Can you share your explorations? I think the capsule should be created
> > > as part of the build, if enabled. Rather than changing the input
> > > files, binman should produce new output files.
> >
> > This is an issue of handling dependencies in binman, and not changing
> > input files. We do not have support for telling binman "build/generate
> > this particular image first before you proceed to build the capsules
> > using the earlier built images". I am not sure if this can be done in
> > a generic manner in binman, so that irrespective of the image being
> > generated, it can be specified to build capsules once the capsule
> > input images have been generated.
>
> I'm just not sure what you are getting out here.
>
> See INPUTS-y for the input files to binman. Then binman uses these to
> generate output files. It does not mess with the input files, nor
> should it. Please read the top part of the Binman motivation to
> understand how all this works.
>
> At the risk of repeating myself, we want the Makefile to focus on
> building U-Boot, with Binman handling the laterprocessing of those
> files. Binman may run as part of the U-Boot build, and/or can be run
> later, with the input files.
>
> >
> > >
> > > We are trying to remove most of the output logic in Makefile. It
> > > should just be producing input files for binman.
> >
> > I understand. However, like I mentioned above, as of now, we don't
> > have a way of handling dependencies in binman, at least in a generic
> > manner. Once this support gets added, I know that it would be trivial
> > to add support for building capsules in binman.
>
> What dependencies do you need? Please describe it in a simple manner
> so I can understand. It cannot involve change the binman input files.

Consider the following scenarios.

One board, say Board A uses fip.bin as the input file(payload) for
generating the capsule file. The fip.bin is being generated through
binman. A binman entry is also added for generating the capsule(say
fip.capule). Now, binman has to generate fip.bin *and subsequently*
fip.capsule, as the capsule file will contain fip.bin as it's
payload(input).

Second Board B, uses u-boot.itb, which is a FIT image, as the input
file for generating the capsule. The u-boot.itb is being generated
through binman, and so is the capsule. But binman needs to build the
u-boot.itb *before* it generates the corresponding capsule file, which
uses u-boot.itb as the capsule payload.

There can be multiple such examples of input files being generated by
binman, followed by the capsule getting generated by binman. How do I
specify this dependency in binman -- build/generate the input file
first, and then use that files in generating the capsule.

-sughosh

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/7] Makefile: Add a target for building capsules
  2023-06-26 12:13             ` Sughosh Ganu
@ 2023-06-27  4:57               ` Sughosh Ganu
  2023-06-27 11:20                 ` Simon Glass
  0 siblings, 1 reply; 50+ messages in thread
From: Sughosh Ganu @ 2023-06-27  4:57 UTC (permalink / raw)
  To: Simon Glass
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Michal Simek,
	Takahiro Akashi

hi Simon,

On Mon, 26 Jun 2023 at 17:43, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
>
> hi Simon,
>
> On Mon, 26 Jun 2023 at 14:38, Simon Glass <sjg@chromium.org> wrote:
> >
> > Hi Sughosh,
> >
> > On Wed, 21 Jun 2023 at 05:26, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > >
> > > hi Simon,
> > >
> > > On Mon, 19 Jun 2023 at 18:07, Simon Glass <sjg@chromium.org> wrote:
> > > >
> > > > Hi Sughosh,
> > > >
> > > > On Thu, 15 Jun 2023 at 17:25, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > >
> > > > > hi Simon,
> > > > >
> > > > > On Thu, 15 Jun 2023 at 14:44, Simon Glass <sjg@chromium.org> wrote:
> > > > > >
> > > > > > Hi Sughosh,
> > > > > >
> > > > > > On Tue, 13 Jun 2023 at 11:39, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > >
> > > > > > > Add a target for building EFI capsules. The capsule parameters are
> > > > > > > specified through a config file, and the path to the config file is
> > > > > > > specified through CONFIG_EFI_CAPSULE_CFG_FILE. When the config file is
> > > > > > > not specified, the command only builds tools.
> > > > > > >
> > > > > > > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > > > > > > ---
> > > > > > >  Makefile | 9 +++++++++
> > > > > > >  1 file changed, 9 insertions(+)
> > > > > > >
> > > > > > > diff --git a/Makefile b/Makefile
> > > > > > > index 10bfaa52ad..96db29aa77 100644
> > > > > > > --- a/Makefile
> > > > > > > +++ b/Makefile
> > > > > > > @@ -1151,6 +1151,15 @@ dtbs: dts/dt.dtb
> > > > > > >  dts/dt.dtb: u-boot
> > > > > > >         $(Q)$(MAKE) $(build)=dts dtbs
> > > > > > >
> > > > > > > +quiet_cmd_mkeficapsule = MKEFICAPSULE $@
> > > > > > > +cmd_mkeficapsule = $(objtree)/tools/mkeficapsule $@
> > > > > > > +
> > > > > > > +PHONY += capsule
> > > > > > > +capsule: tools
> > > > > > > +ifneq ($(CONFIG_EFI_CAPSULE_CFG_FILE),"")
> > > > > > > +       $(call cmd,mkeficapsule)
> > > > > > > +endif
> > > > > > > +
> > > > > > >  quiet_cmd_copy = COPY    $@
> > > > > > >        cmd_copy = cp $< $@
> > > > > > >
> > > > > > > --
> > > > > > > 2.34.1
> > > > > > >
> > > > > >
> > > > > > We should be using binman to build images...you seem to be building
> > > > > > something in parallel with that. Can you please take a look at binman?
> > > > >
> > > > > Again, I had explored using binman for this task. The one issue where
> > > > > I find the above flow better is that I can simply build my payload
> > > > > image(s) followed by 'make capsule' to generate the capsules for
> > > > > earlier generated images. In it's current form, I don't see an easy
> > > > > way to enforce this dependency in binman when I want to build the
> > > > > payload followed by generation of capsules. I did see the mention of
> > > > > encapsulating an entry within another dependent entry, but I think
> > > > > that makes the implementation more complex than it ought to be.
> > > > >
> > > > > I think it is much easier to use the make flow to generate the images
> > > > > followed by capsules, instead of tweaking the binman node to first
> > > > > generate the payload images, followed by enabling the capsule node to
> > > > > build the capsules. If there is an easy way of enforcing this
> > > > > dependency, please let me know. Thanks
> > > >
> > > > Can you share your explorations? I think the capsule should be created
> > > > as part of the build, if enabled. Rather than changing the input
> > > > files, binman should produce new output files.
> > >
> > > This is an issue of handling dependencies in binman, and not changing
> > > input files. We do not have support for telling binman "build/generate
> > > this particular image first before you proceed to build the capsules
> > > using the earlier built images". I am not sure if this can be done in
> > > a generic manner in binman, so that irrespective of the image being
> > > generated, it can be specified to build capsules once the capsule
> > > input images have been generated.
> >
> > I'm just not sure what you are getting out here.
> >
> > See INPUTS-y for the input files to binman. Then binman uses these to
> > generate output files. It does not mess with the input files, nor
> > should it. Please read the top part of the Binman motivation to
> > understand how all this works.
> >
> > At the risk of repeating myself, we want the Makefile to focus on
> > building U-Boot, with Binman handling the laterprocessing of those
> > files. Binman may run as part of the U-Boot build, and/or can be run
> > later, with the input files.
> >
> > >
> > > >
> > > > We are trying to remove most of the output logic in Makefile. It
> > > > should just be producing input files for binman.
> > >
> > > I understand. However, like I mentioned above, as of now, we don't
> > > have a way of handling dependencies in binman, at least in a generic
> > > manner. Once this support gets added, I know that it would be trivial
> > > to add support for building capsules in binman.
> >
> > What dependencies do you need? Please describe it in a simple manner
> > so I can understand. It cannot involve change the binman input files.
>
> Consider the following scenarios.
>
> One board, say Board A uses fip.bin as the input file(payload) for
> generating the capsule file. The fip.bin is being generated through
> binman. A binman entry is also added for generating the capsule(say
> fip.capule). Now, binman has to generate fip.bin *and subsequently*
> fip.capsule, as the capsule file will contain fip.bin as it's
> payload(input).
>
> Second Board B, uses u-boot.itb, which is a FIT image, as the input
> file for generating the capsule. The u-boot.itb is being generated
> through binman, and so is the capsule. But binman needs to build the
> u-boot.itb *before* it generates the corresponding capsule file, which
> uses u-boot.itb as the capsule payload.
>
> There can be multiple such examples of input files being generated by
> binman, followed by the capsule getting generated by binman. How do I
> specify this dependency in binman -- build/generate the input file
> first, and then use that files in generating the capsule.

Can you confirm if the above dependencies can be handled in binman
currently. If not, I'd suggest you remove your Nak for patch 6 of this
series [1]. Like I mentioned earlier, if there is a means of
specifying dependencies for generating images in binman, moving the
capsule generation to binman will not be a difficult task.

Also, can you go through the other set of patches in V2, specifically
the ones where putting the public key ESL into the dtb is being done
through binman.

-sughosh

[1] - https://lists.denx.de/pipermail/u-boot/2023-June/521103.html

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 1/7] capsule: authenticate: Embed capsule public key in platform's dtb
  2023-06-26 11:19               ` Simon Glass
@ 2023-06-27  9:54                 ` Ilias Apalodimas
  2023-06-27 10:13                   ` Simon Glass
  0 siblings, 1 reply; 50+ messages in thread
From: Ilias Apalodimas @ 2023-06-27  9:54 UTC (permalink / raw)
  To: Simon Glass
  Cc: Sughosh Ganu, u-boot, Heinrich Schuchardt, Michal Simek, Takahiro Akashi

Hi Simon,

On Mon, 26 Jun 2023 at 14:19, Simon Glass <sjg@chromium.org> wrote:
>
> Hi Ilias,
>
> On Mon, 26 Jun 2023 at 10:53, Ilias Apalodimas
> <ilias.apalodimas@linaro.org> wrote:
> >
> > Hi Simon,
> >
> > [...]
> >
> > > > > > > > +
> > > > > > > > +gen_capsule_signature_file signature.$$.dts > /dev/null 2>&1
> > > > > > > > +$CPP $dtc_cpp_flags -x assembler-with-cpp -o signature.$$.tmp signature.$$.dts > /dev/null 2>&1
> > > > > > > > +dtc -@ -O dtb -o signature.$$.dtbo signature.$$.tmp > /dev/null 2>&1
> > > > > > > > +fdtoverlay -i $1 -o temp.$$.dtb -v signature.$$.dtbo > /dev/null 2>&1
> > > > > > > > +mv temp.$$.dtb $1 > /dev/null 2>&1
> > > > > > > > +rm -f signature.$$.* > /dev/null 2>&1
> > > > > > > > --
> > > > > > > > 2.34.1
> > > > > > > >
> > > > > > >
> > > > > > > Can you please add this to binman instead?
> > > > > >
> > > > > > I had looked at using binman for this work earlier because I very much
> > > > > > expected this comment from you :). Having said that, I am very much
> > > > > > open to using binman instead if it turns out to be the better way of
> > > > > > achieving this. What this patch does is that, with capsule
> > > > > > authentication enabled, it embeds the public key esl file into the
> > > > > > dtb's as they get built. As per my understanding, binman gets called
> > > > > > at the end of the u-boot build, once the constituent images( e..g
> > > > > > u-boot.bin = u-boot-no-dtb.bin + dtb) have been generated. So, if we
> > > > > > call binman _after_ the requisite image(s) have been generated, we
> > > > > > would need to 1) identify the dtb's in which the esl needs to be
> > > > > > embedded, and then 2) generate the final image all over again. Don't
> > > > > > you think this is non optimal? Or is there a way of generating the
> > > > > > constituent images(including the dtb's) through binman instead?
> > > > >
> > > > > The best way to do that IMO is to generate a second file, .e.g.
> > > > > u-boot-capsule.bin
> > > >
> >
> > This make no sense to me whatsoever.  Do we have an example in u-boot
> > generating multiple dtb versions for other reasons/subsystems?
> >
> > > > That would break the scripts for platforms which might be using
> > > > u-boot.bin as the image to boot from. I know that the ST platform
> > > > which does enable capsule updates uses the u-boot-nodtb.bin as the
> > > > BL33 image and the u-boot.dtb as BL33_CFG. Hence my question, if we
> > > > have to use binman, is there a way to 1) modify the u-boot.dtb and
> > > > then 2) regenerate u-boot.bin image.
> > > >
> > > > I know this is software, and everything can be done in a hacky way.
> > > > But I was exploring using the u-boot node as a section entry, so that
> > > > the u-boot.dtb can be modified and then binman would
> > > > repackage/regenerate the u-boot.bin. But this is not working.
> > >
> > > NO, please do not do that.  You should create a new file, not modify
> > > u-boot.bin or u-boot.dtb. Please just don't mess around with this, it
> > > will lead to all sorts of confusion.
> > >
> > > I thought we already had this discussion a while back?
> >
> > No we haven't.  In fact I am struggling to see the confusion part.  It's
> > fine for the u-boot dtb to include all the internal nodes DM needs, but
> > suddenly having the capsule signature is problematic?
> >
> > In the past the .esl file was part of the U-Boot binary and things were
> > working perfectly fine.  In fact you could update/downgrade u-boot and the
> > signatures naturally followed along instead of having to update u-boot
> > *and* the dtb, which we have to do today. You could also build a capsule
> > way easier without injecting/removing signatures to the dtb.
> > You were the one that insisted on reverting that and instead adding it on
> > the dtb.  We explained most of the downsides back then, along with some
> > security concerns.  We also mentioned that the signature in the dtb makes
> > little sense since it's difference *per class of boards* and it's not
> > something we could include in static dtb files, but that lead nowhere...
> >
> > As Sughosh already said there are platforms that use the generated u-boot
> > dtb and the raw binary to assemble a FIP image.  So why exactly adding the
> > capsule signature to the default dtb is wrong?  Why should we add an
> > *extra* .dtb with one additional node -- which is very much needed by the
> > design you proposed.  This will only lead to extra confusion.
>
> 1. I thought a capsule update was going to be a separate file, e.g.
> u-boot-capture.bin ?

Yes the capsule itself is a different file and I dont think there's
any disagreement on how to generate that.
On the u-boot.bin you need to flash on the board though, you need to
include the public key you authenticate the incoming capsule against.
That's what Sughosh wants to inject.

>
> 2. You can't put the signature into U-Boot. It needs to be in the
> capsule update so that U-Boot can check it. If you are talking about
> the public key, then yes that needs to be in U-Boot

See above,

>
> 3. Where does the public key come from? With the normal verified boot
> flow it is created (and added to the dtb) as a separate signing step
> after U-Boot is built.

It's per class of hardware or whatever the vendor decides it should be.

>
> 4. Off topic, but re FIP, can someone just kill that off and use FIT?
> It is such a shame that a new format was invented...

You'd have to ask the TrustedFirmware-A code owners, but I doubt it.
FIP is what TF-A uses for the first stage bootloader packaging and
authentication of subsequent boot stages.

Thanks
/Ilias
>
> Regards,
> Simon

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 1/7] capsule: authenticate: Embed capsule public key in platform's dtb
  2023-06-27  9:54                 ` Ilias Apalodimas
@ 2023-06-27 10:13                   ` Simon Glass
  2023-06-27 10:20                     ` Sughosh Ganu
  0 siblings, 1 reply; 50+ messages in thread
From: Simon Glass @ 2023-06-27 10:13 UTC (permalink / raw)
  To: Ilias Apalodimas
  Cc: Sughosh Ganu, u-boot, Heinrich Schuchardt, Michal Simek, Takahiro Akashi

Hi,

On Tue, 27 Jun 2023 at 10:55, Ilias Apalodimas
<ilias.apalodimas@linaro.org> wrote:
>
> Hi Simon,
>
> On Mon, 26 Jun 2023 at 14:19, Simon Glass <sjg@chromium.org> wrote:
> >
> > Hi Ilias,
> >
> > On Mon, 26 Jun 2023 at 10:53, Ilias Apalodimas
> > <ilias.apalodimas@linaro.org> wrote:
> > >
> > > Hi Simon,
> > >
> > > [...]
> > >
> > > > > > > > > +
> > > > > > > > > +gen_capsule_signature_file signature.$$.dts > /dev/null 2>&1
> > > > > > > > > +$CPP $dtc_cpp_flags -x assembler-with-cpp -o signature.$$.tmp signature.$$.dts > /dev/null 2>&1
> > > > > > > > > +dtc -@ -O dtb -o signature.$$.dtbo signature.$$.tmp > /dev/null 2>&1
> > > > > > > > > +fdtoverlay -i $1 -o temp.$$.dtb -v signature.$$.dtbo > /dev/null 2>&1
> > > > > > > > > +mv temp.$$.dtb $1 > /dev/null 2>&1
> > > > > > > > > +rm -f signature.$$.* > /dev/null 2>&1
> > > > > > > > > --
> > > > > > > > > 2.34.1
> > > > > > > > >
> > > > > > > >
> > > > > > > > Can you please add this to binman instead?
> > > > > > >
> > > > > > > I had looked at using binman for this work earlier because I very much
> > > > > > > expected this comment from you :). Having said that, I am very much
> > > > > > > open to using binman instead if it turns out to be the better way of
> > > > > > > achieving this. What this patch does is that, with capsule
> > > > > > > authentication enabled, it embeds the public key esl file into the
> > > > > > > dtb's as they get built. As per my understanding, binman gets called
> > > > > > > at the end of the u-boot build, once the constituent images( e..g
> > > > > > > u-boot.bin = u-boot-no-dtb.bin + dtb) have been generated. So, if we
> > > > > > > call binman _after_ the requisite image(s) have been generated, we
> > > > > > > would need to 1) identify the dtb's in which the esl needs to be
> > > > > > > embedded, and then 2) generate the final image all over again. Don't
> > > > > > > you think this is non optimal? Or is there a way of generating the
> > > > > > > constituent images(including the dtb's) through binman instead?
> > > > > >
> > > > > > The best way to do that IMO is to generate a second file, .e.g.
> > > > > > u-boot-capsule.bin
> > > > >
> > >
> > > This make no sense to me whatsoever.  Do we have an example in u-boot
> > > generating multiple dtb versions for other reasons/subsystems?
> > >
> > > > > That would break the scripts for platforms which might be using
> > > > > u-boot.bin as the image to boot from. I know that the ST platform
> > > > > which does enable capsule updates uses the u-boot-nodtb.bin as the
> > > > > BL33 image and the u-boot.dtb as BL33_CFG. Hence my question, if we
> > > > > have to use binman, is there a way to 1) modify the u-boot.dtb and
> > > > > then 2) regenerate u-boot.bin image.
> > > > >
> > > > > I know this is software, and everything can be done in a hacky way.
> > > > > But I was exploring using the u-boot node as a section entry, so that
> > > > > the u-boot.dtb can be modified and then binman would
> > > > > repackage/regenerate the u-boot.bin. But this is not working.
> > > >
> > > > NO, please do not do that.  You should create a new file, not modify
> > > > u-boot.bin or u-boot.dtb. Please just don't mess around with this, it
> > > > will lead to all sorts of confusion.
> > > >
> > > > I thought we already had this discussion a while back?
> > >
> > > No we haven't.  In fact I am struggling to see the confusion part.  It's
> > > fine for the u-boot dtb to include all the internal nodes DM needs, but
> > > suddenly having the capsule signature is problematic?
> > >
> > > In the past the .esl file was part of the U-Boot binary and things were
> > > working perfectly fine.  In fact you could update/downgrade u-boot and the
> > > signatures naturally followed along instead of having to update u-boot
> > > *and* the dtb, which we have to do today. You could also build a capsule
> > > way easier without injecting/removing signatures to the dtb.
> > > You were the one that insisted on reverting that and instead adding it on
> > > the dtb.  We explained most of the downsides back then, along with some
> > > security concerns.  We also mentioned that the signature in the dtb makes
> > > little sense since it's difference *per class of boards* and it's not
> > > something we could include in static dtb files, but that lead nowhere...
> > >
> > > As Sughosh already said there are platforms that use the generated u-boot
> > > dtb and the raw binary to assemble a FIP image.  So why exactly adding the
> > > capsule signature to the default dtb is wrong?  Why should we add an
> > > *extra* .dtb with one additional node -- which is very much needed by the
> > > design you proposed.  This will only lead to extra confusion.
> >
> > 1. I thought a capsule update was going to be a separate file, e.g.
> > u-boot-capture.bin ?
>
> Yes the capsule itself is a different file and I dont think there's
> any disagreement on how to generate that.
> On the u-boot.bin you need to flash on the board though, you need to
> include the public key you authenticate the incoming capsule against.
> That's what Sughosh wants to inject.
>
> >
> > 2. You can't put the signature into U-Boot. It needs to be in the
> > capsule update so that U-Boot can check it. If you are talking about
> > the public key, then yes that needs to be in U-Boot
>
> See above,
>
> >
> > 3. Where does the public key come from? With the normal verified boot
> > flow it is created (and added to the dtb) as a separate signing step
> > after U-Boot is built.
>
> It's per class of hardware or whatever the vendor decides it should be.
>
> >
> > 4. Off topic, but re FIP, can someone just kill that off and use FIT?
> > It is such a shame that a new format was invented...
>
> You'd have to ask the TrustedFirmware-A code owners, but I doubt it.
> FIP is what TF-A uses for the first stage bootloader packaging and
> authentication of subsequent boot stages.

Sughosh do you happen to be at EOSS so we could talk this through?
Otherwise I'll reply later on.

Regards,
Simon

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 1/7] capsule: authenticate: Embed capsule public key in platform's dtb
  2023-06-27 10:13                   ` Simon Glass
@ 2023-06-27 10:20                     ` Sughosh Ganu
  0 siblings, 0 replies; 50+ messages in thread
From: Sughosh Ganu @ 2023-06-27 10:20 UTC (permalink / raw)
  To: Simon Glass
  Cc: Ilias Apalodimas, u-boot, Heinrich Schuchardt, Michal Simek,
	Takahiro Akashi

hi Simon,

On Tue, 27 Jun 2023 at 15:44, Simon Glass <sjg@chromium.org> wrote:
>
> Hi,
>
> On Tue, 27 Jun 2023 at 10:55, Ilias Apalodimas
> <ilias.apalodimas@linaro.org> wrote:
> >
> > Hi Simon,
> >
> > On Mon, 26 Jun 2023 at 14:19, Simon Glass <sjg@chromium.org> wrote:
> > >
> > > Hi Ilias,
> > >
> > > On Mon, 26 Jun 2023 at 10:53, Ilias Apalodimas
> > > <ilias.apalodimas@linaro.org> wrote:
> > > >
> > > > Hi Simon,
> > > >
> > > > [...]
> > > >
> > > > > > > > > > +
> > > > > > > > > > +gen_capsule_signature_file signature.$$.dts > /dev/null 2>&1
> > > > > > > > > > +$CPP $dtc_cpp_flags -x assembler-with-cpp -o signature.$$.tmp signature.$$.dts > /dev/null 2>&1
> > > > > > > > > > +dtc -@ -O dtb -o signature.$$.dtbo signature.$$.tmp > /dev/null 2>&1
> > > > > > > > > > +fdtoverlay -i $1 -o temp.$$.dtb -v signature.$$.dtbo > /dev/null 2>&1
> > > > > > > > > > +mv temp.$$.dtb $1 > /dev/null 2>&1
> > > > > > > > > > +rm -f signature.$$.* > /dev/null 2>&1
> > > > > > > > > > --
> > > > > > > > > > 2.34.1
> > > > > > > > > >
> > > > > > > > >
> > > > > > > > > Can you please add this to binman instead?
> > > > > > > >
> > > > > > > > I had looked at using binman for this work earlier because I very much
> > > > > > > > expected this comment from you :). Having said that, I am very much
> > > > > > > > open to using binman instead if it turns out to be the better way of
> > > > > > > > achieving this. What this patch does is that, with capsule
> > > > > > > > authentication enabled, it embeds the public key esl file into the
> > > > > > > > dtb's as they get built. As per my understanding, binman gets called
> > > > > > > > at the end of the u-boot build, once the constituent images( e..g
> > > > > > > > u-boot.bin = u-boot-no-dtb.bin + dtb) have been generated. So, if we
> > > > > > > > call binman _after_ the requisite image(s) have been generated, we
> > > > > > > > would need to 1) identify the dtb's in which the esl needs to be
> > > > > > > > embedded, and then 2) generate the final image all over again. Don't
> > > > > > > > you think this is non optimal? Or is there a way of generating the
> > > > > > > > constituent images(including the dtb's) through binman instead?
> > > > > > >
> > > > > > > The best way to do that IMO is to generate a second file, .e.g.
> > > > > > > u-boot-capsule.bin
> > > > > >
> > > >
> > > > This make no sense to me whatsoever.  Do we have an example in u-boot
> > > > generating multiple dtb versions for other reasons/subsystems?
> > > >
> > > > > > That would break the scripts for platforms which might be using
> > > > > > u-boot.bin as the image to boot from. I know that the ST platform
> > > > > > which does enable capsule updates uses the u-boot-nodtb.bin as the
> > > > > > BL33 image and the u-boot.dtb as BL33_CFG. Hence my question, if we
> > > > > > have to use binman, is there a way to 1) modify the u-boot.dtb and
> > > > > > then 2) regenerate u-boot.bin image.
> > > > > >
> > > > > > I know this is software, and everything can be done in a hacky way.
> > > > > > But I was exploring using the u-boot node as a section entry, so that
> > > > > > the u-boot.dtb can be modified and then binman would
> > > > > > repackage/regenerate the u-boot.bin. But this is not working.
> > > > >
> > > > > NO, please do not do that.  You should create a new file, not modify
> > > > > u-boot.bin or u-boot.dtb. Please just don't mess around with this, it
> > > > > will lead to all sorts of confusion.
> > > > >
> > > > > I thought we already had this discussion a while back?
> > > >
> > > > No we haven't.  In fact I am struggling to see the confusion part.  It's
> > > > fine for the u-boot dtb to include all the internal nodes DM needs, but
> > > > suddenly having the capsule signature is problematic?
> > > >
> > > > In the past the .esl file was part of the U-Boot binary and things were
> > > > working perfectly fine.  In fact you could update/downgrade u-boot and the
> > > > signatures naturally followed along instead of having to update u-boot
> > > > *and* the dtb, which we have to do today. You could also build a capsule
> > > > way easier without injecting/removing signatures to the dtb.
> > > > You were the one that insisted on reverting that and instead adding it on
> > > > the dtb.  We explained most of the downsides back then, along with some
> > > > security concerns.  We also mentioned that the signature in the dtb makes
> > > > little sense since it's difference *per class of boards* and it's not
> > > > something we could include in static dtb files, but that lead nowhere...
> > > >
> > > > As Sughosh already said there are platforms that use the generated u-boot
> > > > dtb and the raw binary to assemble a FIP image.  So why exactly adding the
> > > > capsule signature to the default dtb is wrong?  Why should we add an
> > > > *extra* .dtb with one additional node -- which is very much needed by the
> > > > design you proposed.  This will only lead to extra confusion.
> > >
> > > 1. I thought a capsule update was going to be a separate file, e.g.
> > > u-boot-capture.bin ?
> >
> > Yes the capsule itself is a different file and I dont think there's
> > any disagreement on how to generate that.
> > On the u-boot.bin you need to flash on the board though, you need to
> > include the public key you authenticate the incoming capsule against.
> > That's what Sughosh wants to inject.
> >
> > >
> > > 2. You can't put the signature into U-Boot. It needs to be in the
> > > capsule update so that U-Boot can check it. If you are talking about
> > > the public key, then yes that needs to be in U-Boot
> >
> > See above,
> >
> > >
> > > 3. Where does the public key come from? With the normal verified boot
> > > flow it is created (and added to the dtb) as a separate signing step
> > > after U-Boot is built.
> >
> > It's per class of hardware or whatever the vendor decides it should be.
> >
> > >
> > > 4. Off topic, but re FIP, can someone just kill that off and use FIT?
> > > It is such a shame that a new format was invented...
> >
> > You'd have to ask the TrustedFirmware-A code owners, but I doubt it.
> > FIP is what TF-A uses for the first stage bootloader packaging and
> > authentication of subsequent boot stages.
>
> Sughosh do you happen to be at EOSS so we could talk this through?
> Otherwise I'll reply later on.

I will not be at EOSS. You can reply to my question in your free time.
Basically I wanted your feedback about addressing dependencies in
binman [1].

-sughosh

[1] - https://lists.denx.de/pipermail/u-boot/2023-June/521148.html

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/7] Makefile: Add a target for building capsules
  2023-06-27  4:57               ` Sughosh Ganu
@ 2023-06-27 11:20                 ` Simon Glass
  2023-06-27 12:07                   ` Sughosh Ganu
  0 siblings, 1 reply; 50+ messages in thread
From: Simon Glass @ 2023-06-27 11:20 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Michal Simek,
	Takahiro Akashi

Hi Sughosh,

On Tue, 27 Jun 2023 at 05:57, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
>
> hi Simon,
>
> On Mon, 26 Jun 2023 at 17:43, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> >
> > hi Simon,
> >
> > On Mon, 26 Jun 2023 at 14:38, Simon Glass <sjg@chromium.org> wrote:
> > >
> > > Hi Sughosh,
> > >
> > > On Wed, 21 Jun 2023 at 05:26, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > >
> > > > hi Simon,
> > > >
> > > > On Mon, 19 Jun 2023 at 18:07, Simon Glass <sjg@chromium.org> wrote:
> > > > >
> > > > > Hi Sughosh,
> > > > >
> > > > > On Thu, 15 Jun 2023 at 17:25, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > >
> > > > > > hi Simon,
> > > > > >
> > > > > > On Thu, 15 Jun 2023 at 14:44, Simon Glass <sjg@chromium.org> wrote:
> > > > > > >
> > > > > > > Hi Sughosh,
> > > > > > >
> > > > > > > On Tue, 13 Jun 2023 at 11:39, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > > >
> > > > > > > > Add a target for building EFI capsules. The capsule parameters are
> > > > > > > > specified through a config file, and the path to the config file is
> > > > > > > > specified through CONFIG_EFI_CAPSULE_CFG_FILE. When the config file is
> > > > > > > > not specified, the command only builds tools.
> > > > > > > >
> > > > > > > > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > > > > > > > ---
> > > > > > > >  Makefile | 9 +++++++++
> > > > > > > >  1 file changed, 9 insertions(+)
> > > > > > > >
> > > > > > > > diff --git a/Makefile b/Makefile
> > > > > > > > index 10bfaa52ad..96db29aa77 100644
> > > > > > > > --- a/Makefile
> > > > > > > > +++ b/Makefile
> > > > > > > > @@ -1151,6 +1151,15 @@ dtbs: dts/dt.dtb
> > > > > > > >  dts/dt.dtb: u-boot
> > > > > > > >         $(Q)$(MAKE) $(build)=dts dtbs
> > > > > > > >
> > > > > > > > +quiet_cmd_mkeficapsule = MKEFICAPSULE $@
> > > > > > > > +cmd_mkeficapsule = $(objtree)/tools/mkeficapsule $@
> > > > > > > > +
> > > > > > > > +PHONY += capsule
> > > > > > > > +capsule: tools
> > > > > > > > +ifneq ($(CONFIG_EFI_CAPSULE_CFG_FILE),"")
> > > > > > > > +       $(call cmd,mkeficapsule)
> > > > > > > > +endif
> > > > > > > > +
> > > > > > > >  quiet_cmd_copy = COPY    $@
> > > > > > > >        cmd_copy = cp $< $@
> > > > > > > >
> > > > > > > > --
> > > > > > > > 2.34.1
> > > > > > > >
> > > > > > >
> > > > > > > We should be using binman to build images...you seem to be building
> > > > > > > something in parallel with that. Can you please take a look at binman?
> > > > > >
> > > > > > Again, I had explored using binman for this task. The one issue where
> > > > > > I find the above flow better is that I can simply build my payload
> > > > > > image(s) followed by 'make capsule' to generate the capsules for
> > > > > > earlier generated images. In it's current form, I don't see an easy
> > > > > > way to enforce this dependency in binman when I want to build the
> > > > > > payload followed by generation of capsules. I did see the mention of
> > > > > > encapsulating an entry within another dependent entry, but I think
> > > > > > that makes the implementation more complex than it ought to be.
> > > > > >
> > > > > > I think it is much easier to use the make flow to generate the images
> > > > > > followed by capsules, instead of tweaking the binman node to first
> > > > > > generate the payload images, followed by enabling the capsule node to
> > > > > > build the capsules. If there is an easy way of enforcing this
> > > > > > dependency, please let me know. Thanks
> > > > >
> > > > > Can you share your explorations? I think the capsule should be created
> > > > > as part of the build, if enabled. Rather than changing the input
> > > > > files, binman should produce new output files.
> > > >
> > > > This is an issue of handling dependencies in binman, and not changing
> > > > input files. We do not have support for telling binman "build/generate
> > > > this particular image first before you proceed to build the capsules
> > > > using the earlier built images". I am not sure if this can be done in
> > > > a generic manner in binman, so that irrespective of the image being
> > > > generated, it can be specified to build capsules once the capsule
> > > > input images have been generated.
> > >
> > > I'm just not sure what you are getting out here.
> > >
> > > See INPUTS-y for the input files to binman. Then binman uses these to
> > > generate output files. It does not mess with the input files, nor
> > > should it. Please read the top part of the Binman motivation to
> > > understand how all this works.
> > >
> > > At the risk of repeating myself, we want the Makefile to focus on
> > > building U-Boot, with Binman handling the laterprocessing of those
> > > files. Binman may run as part of the U-Boot build, and/or can be run
> > > later, with the input files.
> > >
> > > >
> > > > >
> > > > > We are trying to remove most of the output logic in Makefile. It
> > > > > should just be producing input files for binman.
> > > >
> > > > I understand. However, like I mentioned above, as of now, we don't
> > > > have a way of handling dependencies in binman, at least in a generic
> > > > manner. Once this support gets added, I know that it would be trivial
> > > > to add support for building capsules in binman.
> > >
> > > What dependencies do you need? Please describe it in a simple manner
> > > so I can understand. It cannot involve change the binman input files.
> >
> > Consider the following scenarios.
> >
> > One board, say Board A uses fip.bin as the input file(payload) for
> > generating the capsule file. The fip.bin is being generated through
> > binman. A binman entry is also added for generating the capsule(say
> > fip.capule). Now, binman has to generate fip.bin *and subsequently*
> > fip.capsule, as the capsule file will contain fip.bin as it's
> > payload(input).
> >
> > Second Board B, uses u-boot.itb, which is a FIT image, as the input
> > file for generating the capsule. The u-boot.itb is being generated
> > through binman, and so is the capsule. But binman needs to build the
> > u-boot.itb *before* it generates the corresponding capsule file, which
> > uses u-boot.itb as the capsule payload.
> >
> > There can be multiple such examples of input files being generated by
> > binman, followed by the capsule getting generated by binman. How do I
> > specify this dependency in binman -- build/generate the input file
> > first, and then use that files in generating the capsule.

At present you can do this by ordering the images correctly, i.e. put
the first image first and the dependent image after it. For the
dependent image you can have a blob which is the entire first image.

If you are trying to do a second binman operation later, then perhaps
something like 'binman sign' would be useful. Then people can provide
their own key and sign the images in a separate binman operation. This
is likely needed anyway for things to work on a signing server.

>
> Can you confirm if the above dependencies can be handled in binman
> currently. If not, I'd suggest you remove your Nak for patch 6 of this
> series [1]. Like I mentioned earlier, if there is a means of
> specifying dependencies for generating images in binman, moving the
> capsule generation to binman will not be a difficult task.
>
> Also, can you go through the other set of patches in V2, specifically
> the ones where putting the public key ESL into the dtb is being done
> through binman.

The order of operation is supposed to be:

1. Various projects used to build their ouputs (e.g. TF-A)
2. Makefile used to build U-Boot:
2a. The build produces a set of files which serve as inputs to binman (INPUTS-y)
2b. Binman runs on INPUTS-y, picking up all the bits and creating the
final firmware image(s)
3. If necessary, separate from the U-Boot build, binman can be used
separately to do signing or whatever is needed on the final image(s)

I understand that the public key is available in a CONFIG, so it
should be possible to embed it in the build as input, either as a
.dtsi build using 2a, or as a binary file pulled in by binman in 2b.

The patches I reviewed are modifying the input files (INPUTS-y) which
is not allowed. Nor does it seem to be necessary.

As to having 'real' dependencies between images in Binman, I believe
that should be possible. At the moment images are entirely
independent, but I am looking at implementing a simple 'template'
scheme, where you can include some or all of an image description in
another image. See [1] common-part in case you have thoughts on that.

Regards,
Simon

[1] https://patchwork.ozlabs.org/project/uboot/patch/9e905abd369f1d8b182e6fc6e3c9ef4b1526bf76.1685975993.git.jan.kiszka@siemens.com/

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/7] Makefile: Add a target for building capsules
  2023-06-27 11:20                 ` Simon Glass
@ 2023-06-27 12:07                   ` Sughosh Ganu
  2023-06-27 12:20                     ` Simon Glass
  0 siblings, 1 reply; 50+ messages in thread
From: Sughosh Ganu @ 2023-06-27 12:07 UTC (permalink / raw)
  To: Simon Glass
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Michal Simek,
	Takahiro Akashi

hi Simon,

On Tue, 27 Jun 2023 at 16:50, Simon Glass <sjg@chromium.org> wrote:
>
> Hi Sughosh,
>
> On Tue, 27 Jun 2023 at 05:57, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> >
> > hi Simon,
> >
> > On Mon, 26 Jun 2023 at 17:43, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > >
> > > hi Simon,
> > >
> > > On Mon, 26 Jun 2023 at 14:38, Simon Glass <sjg@chromium.org> wrote:
> > > >
> > > > Hi Sughosh,
> > > >
> > > > On Wed, 21 Jun 2023 at 05:26, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > >
> > > > > hi Simon,
> > > > >
> > > > > On Mon, 19 Jun 2023 at 18:07, Simon Glass <sjg@chromium.org> wrote:
> > > > > >
> > > > > > Hi Sughosh,
> > > > > >
> > > > > > On Thu, 15 Jun 2023 at 17:25, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > >
> > > > > > > hi Simon,
> > > > > > >
> > > > > > > On Thu, 15 Jun 2023 at 14:44, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > >
> > > > > > > > Hi Sughosh,
> > > > > > > >
> > > > > > > > On Tue, 13 Jun 2023 at 11:39, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > > > >
> > > > > > > > > Add a target for building EFI capsules. The capsule parameters are
> > > > > > > > > specified through a config file, and the path to the config file is
> > > > > > > > > specified through CONFIG_EFI_CAPSULE_CFG_FILE. When the config file is
> > > > > > > > > not specified, the command only builds tools.
> > > > > > > > >
> > > > > > > > > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > > > > > > > > ---
> > > > > > > > >  Makefile | 9 +++++++++
> > > > > > > > >  1 file changed, 9 insertions(+)
> > > > > > > > >
> > > > > > > > > diff --git a/Makefile b/Makefile
> > > > > > > > > index 10bfaa52ad..96db29aa77 100644
> > > > > > > > > --- a/Makefile
> > > > > > > > > +++ b/Makefile
> > > > > > > > > @@ -1151,6 +1151,15 @@ dtbs: dts/dt.dtb
> > > > > > > > >  dts/dt.dtb: u-boot
> > > > > > > > >         $(Q)$(MAKE) $(build)=dts dtbs
> > > > > > > > >
> > > > > > > > > +quiet_cmd_mkeficapsule = MKEFICAPSULE $@
> > > > > > > > > +cmd_mkeficapsule = $(objtree)/tools/mkeficapsule $@
> > > > > > > > > +
> > > > > > > > > +PHONY += capsule
> > > > > > > > > +capsule: tools
> > > > > > > > > +ifneq ($(CONFIG_EFI_CAPSULE_CFG_FILE),"")
> > > > > > > > > +       $(call cmd,mkeficapsule)
> > > > > > > > > +endif
> > > > > > > > > +
> > > > > > > > >  quiet_cmd_copy = COPY    $@
> > > > > > > > >        cmd_copy = cp $< $@
> > > > > > > > >
> > > > > > > > > --
> > > > > > > > > 2.34.1
> > > > > > > > >
> > > > > > > >
> > > > > > > > We should be using binman to build images...you seem to be building
> > > > > > > > something in parallel with that. Can you please take a look at binman?
> > > > > > >
> > > > > > > Again, I had explored using binman for this task. The one issue where
> > > > > > > I find the above flow better is that I can simply build my payload
> > > > > > > image(s) followed by 'make capsule' to generate the capsules for
> > > > > > > earlier generated images. In it's current form, I don't see an easy
> > > > > > > way to enforce this dependency in binman when I want to build the
> > > > > > > payload followed by generation of capsules. I did see the mention of
> > > > > > > encapsulating an entry within another dependent entry, but I think
> > > > > > > that makes the implementation more complex than it ought to be.
> > > > > > >
> > > > > > > I think it is much easier to use the make flow to generate the images
> > > > > > > followed by capsules, instead of tweaking the binman node to first
> > > > > > > generate the payload images, followed by enabling the capsule node to
> > > > > > > build the capsules. If there is an easy way of enforcing this
> > > > > > > dependency, please let me know. Thanks
> > > > > >
> > > > > > Can you share your explorations? I think the capsule should be created
> > > > > > as part of the build, if enabled. Rather than changing the input
> > > > > > files, binman should produce new output files.
> > > > >
> > > > > This is an issue of handling dependencies in binman, and not changing
> > > > > input files. We do not have support for telling binman "build/generate
> > > > > this particular image first before you proceed to build the capsules
> > > > > using the earlier built images". I am not sure if this can be done in
> > > > > a generic manner in binman, so that irrespective of the image being
> > > > > generated, it can be specified to build capsules once the capsule
> > > > > input images have been generated.
> > > >
> > > > I'm just not sure what you are getting out here.
> > > >
> > > > See INPUTS-y for the input files to binman. Then binman uses these to
> > > > generate output files. It does not mess with the input files, nor
> > > > should it. Please read the top part of the Binman motivation to
> > > > understand how all this works.
> > > >
> > > > At the risk of repeating myself, we want the Makefile to focus on
> > > > building U-Boot, with Binman handling the laterprocessing of those
> > > > files. Binman may run as part of the U-Boot build, and/or can be run
> > > > later, with the input files.
> > > >
> > > > >
> > > > > >
> > > > > > We are trying to remove most of the output logic in Makefile. It
> > > > > > should just be producing input files for binman.
> > > > >
> > > > > I understand. However, like I mentioned above, as of now, we don't
> > > > > have a way of handling dependencies in binman, at least in a generic
> > > > > manner. Once this support gets added, I know that it would be trivial
> > > > > to add support for building capsules in binman.
> > > >
> > > > What dependencies do you need? Please describe it in a simple manner
> > > > so I can understand. It cannot involve change the binman input files.
> > >
> > > Consider the following scenarios.
> > >
> > > One board, say Board A uses fip.bin as the input file(payload) for
> > > generating the capsule file. The fip.bin is being generated through
> > > binman. A binman entry is also added for generating the capsule(say
> > > fip.capule). Now, binman has to generate fip.bin *and subsequently*
> > > fip.capsule, as the capsule file will contain fip.bin as it's
> > > payload(input).
> > >
> > > Second Board B, uses u-boot.itb, which is a FIT image, as the input
> > > file for generating the capsule. The u-boot.itb is being generated
> > > through binman, and so is the capsule. But binman needs to build the
> > > u-boot.itb *before* it generates the corresponding capsule file, which
> > > uses u-boot.itb as the capsule payload.
> > >
> > > There can be multiple such examples of input files being generated by
> > > binman, followed by the capsule getting generated by binman. How do I
> > > specify this dependency in binman -- build/generate the input file
> > > first, and then use that files in generating the capsule.
>
> At present you can do this by ordering the images correctly, i.e. put
> the first image first and the dependent image after it. For the
> dependent image you can have a blob which is the entire first image.

If putting the image entries in a certain order under the binman node
*guarantees* the generation of the first image prior to the generation
of the second image, I think that should work for my use case.
However, when I look at the binman.rst document, I see this mentioned
under the 'Image dependencies' section.

<quote>
Binman does not currently support images that depend on each other. For example,
if one image creates `fred.bin` and then the next uses this `fred.bin` to
produce a final `image.bin`, then the behaviour is undefined. It may work, or it
may produce an error about `fred.bin` being missing, or it may use a version of
`fred.bin` from a previous run.
</quote>

I believe the above is precisely what my use case is. One image
generating fip.bin(for e.g.), and the next image using this fip.bin to
produce the final fip.capsule. Or is this a case of the document not
reflecting the actual code?

>
> If you are trying to do a second binman operation later, then perhaps
> something like 'binman sign' would be useful. Then people can provide
> their own key and sign the images in a separate binman operation. This
> is likely needed anyway for things to work on a signing server.
>
> >
> > Can you confirm if the above dependencies can be handled in binman
> > currently. If not, I'd suggest you remove your Nak for patch 6 of this
> > series [1]. Like I mentioned earlier, if there is a means of
> > specifying dependencies for generating images in binman, moving the
> > capsule generation to binman will not be a difficult task.
> >
> > Also, can you go through the other set of patches in V2, specifically
> > the ones where putting the public key ESL into the dtb is being done
> > through binman.
>
> The order of operation is supposed to be:
>
> 1. Various projects used to build their ouputs (e.g. TF-A)
> 2. Makefile used to build U-Boot:
> 2a. The build produces a set of files which serve as inputs to binman (INPUTS-y)
> 2b. Binman runs on INPUTS-y, picking up all the bits and creating the
> final firmware image(s)
> 3. If necessary, separate from the U-Boot build, binman can be used
> separately to do signing or whatever is needed on the final image(s)
>
> I understand that the public key is available in a CONFIG, so it
> should be possible to embed it in the build as input, either as a
> .dtsi build using 2a, or as a binary file pulled in by binman in 2b.

Using a dtsi would mean that every platform which wants to enable
capsule authentication would need to add a signature node to it's
dtsi. Instead, is it not simpler to just generate a dtbo and merge it
with the dtb being generated. That is what was being done in V1 [1].
For your suggestion to pull it in as a binary file in binman, that
still does not fix the issue of not changing INPUTS-y.

If you ask me, the embedding of the public-key into the dtb is not a
task suitable for binman. Why? Because this task is actually changing
one of the INPUTS-y file that feeds into binman. And yes, we can
generate a different set of files, like u-boot-capsule.dtb and
u-boot-capsule.bin -- implementing that is not at all difficult. But
like I had highlighted earlier, and also explained by Ilias, we
already have platforms that use capsule updates and which use
u-boot.dtb and u-boot.bin. Also, platforms would not want a separate
set of files, one for normal boot, one when using capsules. So I think
it is imperative that we generate the same set of files irrespective
of whether a platform enables capsule updates. So a proper design
would be to add/embed the public key into the dtb as the dtb is
getting built. Again, this is what was being done in V1.


>
> The patches I reviewed are modifying the input files (INPUTS-y) which
> is not allowed. Nor does it seem to be necessary.

The necessity was explained by me and Ilias earlier, and above.

-sughosh

[1] - https://lists.denx.de/pipermail/u-boot/2023-June/520121.html




>
> As to having 'real' dependencies between images in Binman, I believe
> that should be possible. At the moment images are entirely
> independent, but I am looking at implementing a simple 'template'
> scheme, where you can include some or all of an image description in
> another image. See [1] common-part in case you have thoughts on that.
>
> Regards,
> Simon
>
> [1] https://patchwork.ozlabs.org/project/uboot/patch/9e905abd369f1d8b182e6fc6e3c9ef4b1526bf76.1685975993.git.jan.kiszka@siemens.com/

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/7] Makefile: Add a target for building capsules
  2023-06-27 12:07                   ` Sughosh Ganu
@ 2023-06-27 12:20                     ` Simon Glass
  2023-06-27 17:42                       ` Sughosh Ganu
  0 siblings, 1 reply; 50+ messages in thread
From: Simon Glass @ 2023-06-27 12:20 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Michal Simek,
	Takahiro Akashi

Hi Sughosh,

On Tue, 27 Jun 2023 at 13:08, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
>
> hi Simon,
>
> On Tue, 27 Jun 2023 at 16:50, Simon Glass <sjg@chromium.org> wrote:
> >
> > Hi Sughosh,
> >
> > On Tue, 27 Jun 2023 at 05:57, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > >
> > > hi Simon,
> > >
> > > On Mon, 26 Jun 2023 at 17:43, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > >
> > > > hi Simon,
> > > >
> > > > On Mon, 26 Jun 2023 at 14:38, Simon Glass <sjg@chromium.org> wrote:
> > > > >
> > > > > Hi Sughosh,
> > > > >
> > > > > On Wed, 21 Jun 2023 at 05:26, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > >
> > > > > > hi Simon,
> > > > > >
> > > > > > On Mon, 19 Jun 2023 at 18:07, Simon Glass <sjg@chromium.org> wrote:
> > > > > > >
> > > > > > > Hi Sughosh,
> > > > > > >
> > > > > > > On Thu, 15 Jun 2023 at 17:25, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > > >
> > > > > > > > hi Simon,
> > > > > > > >
> > > > > > > > On Thu, 15 Jun 2023 at 14:44, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > >
> > > > > > > > > Hi Sughosh,
> > > > > > > > >
> > > > > > > > > On Tue, 13 Jun 2023 at 11:39, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > > > > >
> > > > > > > > > > Add a target for building EFI capsules. The capsule parameters are
> > > > > > > > > > specified through a config file, and the path to the config file is
> > > > > > > > > > specified through CONFIG_EFI_CAPSULE_CFG_FILE. When the config file is
> > > > > > > > > > not specified, the command only builds tools.
> > > > > > > > > >
> > > > > > > > > > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > > > > > > > > > ---
> > > > > > > > > >  Makefile | 9 +++++++++
> > > > > > > > > >  1 file changed, 9 insertions(+)
> > > > > > > > > >
> > > > > > > > > > diff --git a/Makefile b/Makefile
> > > > > > > > > > index 10bfaa52ad..96db29aa77 100644
> > > > > > > > > > --- a/Makefile
> > > > > > > > > > +++ b/Makefile
> > > > > > > > > > @@ -1151,6 +1151,15 @@ dtbs: dts/dt.dtb
> > > > > > > > > >  dts/dt.dtb: u-boot
> > > > > > > > > >         $(Q)$(MAKE) $(build)=dts dtbs
> > > > > > > > > >
> > > > > > > > > > +quiet_cmd_mkeficapsule = MKEFICAPSULE $@
> > > > > > > > > > +cmd_mkeficapsule = $(objtree)/tools/mkeficapsule $@
> > > > > > > > > > +
> > > > > > > > > > +PHONY += capsule
> > > > > > > > > > +capsule: tools
> > > > > > > > > > +ifneq ($(CONFIG_EFI_CAPSULE_CFG_FILE),"")
> > > > > > > > > > +       $(call cmd,mkeficapsule)
> > > > > > > > > > +endif
> > > > > > > > > > +
> > > > > > > > > >  quiet_cmd_copy = COPY    $@
> > > > > > > > > >        cmd_copy = cp $< $@
> > > > > > > > > >
> > > > > > > > > > --
> > > > > > > > > > 2.34.1
> > > > > > > > > >
> > > > > > > > >
> > > > > > > > > We should be using binman to build images...you seem to be building
> > > > > > > > > something in parallel with that. Can you please take a look at binman?
> > > > > > > >
> > > > > > > > Again, I had explored using binman for this task. The one issue where
> > > > > > > > I find the above flow better is that I can simply build my payload
> > > > > > > > image(s) followed by 'make capsule' to generate the capsules for
> > > > > > > > earlier generated images. In it's current form, I don't see an easy
> > > > > > > > way to enforce this dependency in binman when I want to build the
> > > > > > > > payload followed by generation of capsules. I did see the mention of
> > > > > > > > encapsulating an entry within another dependent entry, but I think
> > > > > > > > that makes the implementation more complex than it ought to be.
> > > > > > > >
> > > > > > > > I think it is much easier to use the make flow to generate the images
> > > > > > > > followed by capsules, instead of tweaking the binman node to first
> > > > > > > > generate the payload images, followed by enabling the capsule node to
> > > > > > > > build the capsules. If there is an easy way of enforcing this
> > > > > > > > dependency, please let me know. Thanks
> > > > > > >
> > > > > > > Can you share your explorations? I think the capsule should be created
> > > > > > > as part of the build, if enabled. Rather than changing the input
> > > > > > > files, binman should produce new output files.
> > > > > >
> > > > > > This is an issue of handling dependencies in binman, and not changing
> > > > > > input files. We do not have support for telling binman "build/generate
> > > > > > this particular image first before you proceed to build the capsules
> > > > > > using the earlier built images". I am not sure if this can be done in
> > > > > > a generic manner in binman, so that irrespective of the image being
> > > > > > generated, it can be specified to build capsules once the capsule
> > > > > > input images have been generated.
> > > > >
> > > > > I'm just not sure what you are getting out here.
> > > > >
> > > > > See INPUTS-y for the input files to binman. Then binman uses these to
> > > > > generate output files. It does not mess with the input files, nor
> > > > > should it. Please read the top part of the Binman motivation to
> > > > > understand how all this works.
> > > > >
> > > > > At the risk of repeating myself, we want the Makefile to focus on
> > > > > building U-Boot, with Binman handling the laterprocessing of those
> > > > > files. Binman may run as part of the U-Boot build, and/or can be run
> > > > > later, with the input files.
> > > > >
> > > > > >
> > > > > > >
> > > > > > > We are trying to remove most of the output logic in Makefile. It
> > > > > > > should just be producing input files for binman.
> > > > > >
> > > > > > I understand. However, like I mentioned above, as of now, we don't
> > > > > > have a way of handling dependencies in binman, at least in a generic
> > > > > > manner. Once this support gets added, I know that it would be trivial
> > > > > > to add support for building capsules in binman.
> > > > >
> > > > > What dependencies do you need? Please describe it in a simple manner
> > > > > so I can understand. It cannot involve change the binman input files.
> > > >
> > > > Consider the following scenarios.
> > > >
> > > > One board, say Board A uses fip.bin as the input file(payload) for
> > > > generating the capsule file. The fip.bin is being generated through
> > > > binman. A binman entry is also added for generating the capsule(say
> > > > fip.capule). Now, binman has to generate fip.bin *and subsequently*
> > > > fip.capsule, as the capsule file will contain fip.bin as it's
> > > > payload(input).
> > > >
> > > > Second Board B, uses u-boot.itb, which is a FIT image, as the input
> > > > file for generating the capsule. The u-boot.itb is being generated
> > > > through binman, and so is the capsule. But binman needs to build the
> > > > u-boot.itb *before* it generates the corresponding capsule file, which
> > > > uses u-boot.itb as the capsule payload.
> > > >
> > > > There can be multiple such examples of input files being generated by
> > > > binman, followed by the capsule getting generated by binman. How do I
> > > > specify this dependency in binman -- build/generate the input file
> > > > first, and then use that files in generating the capsule.
> >
> > At present you can do this by ordering the images correctly, i.e. put
> > the first image first and the dependent image after it. For the
> > dependent image you can have a blob which is the entire first image.
>
> If putting the image entries in a certain order under the binman node
> *guarantees* the generation of the first image prior to the generation
> of the second image, I think that should work for my use case.
> However, when I look at the binman.rst document, I see this mentioned
> under the 'Image dependencies' section.
>
> <quote>
> Binman does not currently support images that depend on each other. For example,
> if one image creates `fred.bin` and then the next uses this `fred.bin` to
> produce a final `image.bin`, then the behaviour is undefined. It may work, or it
> may produce an error about `fred.bin` being missing, or it may use a version of
> `fred.bin` from a previous run.
> </quote>
>
> I believe the above is precisely what my use case is. One image
> generating fip.bin(for e.g.), and the next image using this fip.bin to
> produce the final fip.capsule. Or is this a case of the document not
> reflecting the actual code?

So long as the images are in the correct order in the resulting .dtb,
then it will work. Quite a few boards rely on this.

This comment is there because I planned to implement concurrent image
generation (as is done for sections). But for now this is not
implemented. Also I plan to implement templates before parallel
images, so we can handle dependencies in a more general way.

So if that is the only blocker, I am sorry for the docs being too
conservative. I will send a patch.

>
> >
> > If you are trying to do a second binman operation later, then perhaps
> > something like 'binman sign' would be useful. Then people can provide
> > their own key and sign the images in a separate binman operation. This
> > is likely needed anyway for things to work on a signing server.
> >
> > >
> > > Can you confirm if the above dependencies can be handled in binman
> > > currently. If not, I'd suggest you remove your Nak for patch 6 of this
> > > series [1]. Like I mentioned earlier, if there is a means of
> > > specifying dependencies for generating images in binman, moving the
> > > capsule generation to binman will not be a difficult task.
> > >
> > > Also, can you go through the other set of patches in V2, specifically
> > > the ones where putting the public key ESL into the dtb is being done
> > > through binman.
> >
> > The order of operation is supposed to be:
> >
> > 1. Various projects used to build their ouputs (e.g. TF-A)
> > 2. Makefile used to build U-Boot:
> > 2a. The build produces a set of files which serve as inputs to binman (INPUTS-y)
> > 2b. Binman runs on INPUTS-y, picking up all the bits and creating the
> > final firmware image(s)
> > 3. If necessary, separate from the U-Boot build, binman can be used
> > separately to do signing or whatever is needed on the final image(s)
> >
> > I understand that the public key is available in a CONFIG, so it
> > should be possible to embed it in the build as input, either as a
> > .dtsi build using 2a, or as a binary file pulled in by binman in 2b.
>
> Using a dtsi would mean that every platform which wants to enable
> capsule authentication would need to add a signature node to it's
> dtsi. Instead, is it not simpler to just generate a dtbo and merge it
> with the dtb being generated. That is what was being done in V1 [1].

Why is it simpler? The .dtsi is where we are supposed to put
devicetree properties. It seems a lot harder (to me) to add it later.
It could be a #include, or even just put it in the .dtsi if it is the
same for all boards?

> For your suggestion to pull it in as a binary file in binman, that
> still does not fix the issue of not changing INPUTS-y.
>
> If you ask me, the embedding of the public-key into the dtb is not a
> task suitable for binman. Why? Because this task is actually changing
> one of the INPUTS-y file that feeds into binman. And yes, we can
> generate a different set of files, like u-boot-capsule.dtb and
> u-boot-capsule.bin -- implementing that is not at all difficult. But
> like I had highlighted earlier, and also explained by Ilias, we
> already have platforms that use capsule updates and which use
> u-boot.dtb and u-boot.bin. Also, platforms would not want a separate

Those platforms should change, IMO. But how can this be, when this
functionality has not yet been added to U-Boot?

> set of files, one for normal boot, one when using capsules. So I think
> it is imperative that we generate the same set of files irrespective
> of whether a platform enables capsule updates. So a proper design
> would be to add/embed the public key into the dtb as the dtb is
> getting built. Again, this is what was being done in V1.

I completely disagree with this. A capsule update is not the same as
the vanilla board build / binary. Please can you just give up on this
idea? Many platforms generate their output in separate files, e.g. see
u-boot-rockchip.bin - it just does not make sense to change the built
binary after it is built.

>
>
> >
> > The patches I reviewed are modifying the input files (INPUTS-y) which
> > is not allowed. Nor does it seem to be necessary.
>
> The necessity was explained by me and Ilias earlier, and above.

OK, but it still is not correct.

Regards,
Simon

>
> -sughosh
>
> [1] - https://lists.denx.de/pipermail/u-boot/2023-June/520121.html
>
>
>
>
> >
> > As to having 'real' dependencies between images in Binman, I believe
> > that should be possible. At the moment images are entirely
> > independent, but I am looking at implementing a simple 'template'
> > scheme, where you can include some or all of an image description in
> > another image. See [1] common-part in case you have thoughts on that.
> >
> > Regards,
> > Simon
> >
> > [1] https://patchwork.ozlabs.org/project/uboot/patch/9e905abd369f1d8b182e6fc6e3c9ef4b1526bf76.1685975993.git.jan.kiszka@siemens.com/

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/7] Makefile: Add a target for building capsules
  2023-06-27 12:20                     ` Simon Glass
@ 2023-06-27 17:42                       ` Sughosh Ganu
  2023-06-28  7:42                         ` Simon Glass
  0 siblings, 1 reply; 50+ messages in thread
From: Sughosh Ganu @ 2023-06-27 17:42 UTC (permalink / raw)
  To: Simon Glass
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Michal Simek,
	Takahiro Akashi

hi Simon,

On Tue, 27 Jun 2023 at 17:51, Simon Glass <sjg@chromium.org> wrote:
>
> Hi Sughosh,
>
> On Tue, 27 Jun 2023 at 13:08, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> >
> > hi Simon,
> >
> > On Tue, 27 Jun 2023 at 16:50, Simon Glass <sjg@chromium.org> wrote:
> > >
> > > Hi Sughosh,
> > >
> > > On Tue, 27 Jun 2023 at 05:57, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > >
> > > > hi Simon,
> > > >
> > > > On Mon, 26 Jun 2023 at 17:43, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > >
> > > > > hi Simon,
> > > > >
> > > > > On Mon, 26 Jun 2023 at 14:38, Simon Glass <sjg@chromium.org> wrote:
> > > > > >
> > > > > > Hi Sughosh,
> > > > > >
> > > > > > On Wed, 21 Jun 2023 at 05:26, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > >
> > > > > > > hi Simon,
> > > > > > >
> > > > > > > On Mon, 19 Jun 2023 at 18:07, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > >
> > > > > > > > Hi Sughosh,
> > > > > > > >
> > > > > > > > On Thu, 15 Jun 2023 at 17:25, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > > > >
> > > > > > > > > hi Simon,
> > > > > > > > >
> > > > > > > > > On Thu, 15 Jun 2023 at 14:44, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > >
> > > > > > > > > > Hi Sughosh,
> > > > > > > > > >
> > > > > > > > > > On Tue, 13 Jun 2023 at 11:39, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > > > > > >
> > > > > > > > > > > Add a target for building EFI capsules. The capsule parameters are
> > > > > > > > > > > specified through a config file, and the path to the config file is
> > > > > > > > > > > specified through CONFIG_EFI_CAPSULE_CFG_FILE. When the config file is
> > > > > > > > > > > not specified, the command only builds tools.
> > > > > > > > > > >
> > > > > > > > > > > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > > > > > > > > > > ---
> > > > > > > > > > >  Makefile | 9 +++++++++
> > > > > > > > > > >  1 file changed, 9 insertions(+)
> > > > > > > > > > >
> > > > > > > > > > > diff --git a/Makefile b/Makefile
> > > > > > > > > > > index 10bfaa52ad..96db29aa77 100644
> > > > > > > > > > > --- a/Makefile
> > > > > > > > > > > +++ b/Makefile
> > > > > > > > > > > @@ -1151,6 +1151,15 @@ dtbs: dts/dt.dtb
> > > > > > > > > > >  dts/dt.dtb: u-boot
> > > > > > > > > > >         $(Q)$(MAKE) $(build)=dts dtbs
> > > > > > > > > > >
> > > > > > > > > > > +quiet_cmd_mkeficapsule = MKEFICAPSULE $@
> > > > > > > > > > > +cmd_mkeficapsule = $(objtree)/tools/mkeficapsule $@
> > > > > > > > > > > +
> > > > > > > > > > > +PHONY += capsule
> > > > > > > > > > > +capsule: tools
> > > > > > > > > > > +ifneq ($(CONFIG_EFI_CAPSULE_CFG_FILE),"")
> > > > > > > > > > > +       $(call cmd,mkeficapsule)
> > > > > > > > > > > +endif
> > > > > > > > > > > +
> > > > > > > > > > >  quiet_cmd_copy = COPY    $@
> > > > > > > > > > >        cmd_copy = cp $< $@
> > > > > > > > > > >
> > > > > > > > > > > --
> > > > > > > > > > > 2.34.1
> > > > > > > > > > >
> > > > > > > > > >
> > > > > > > > > > We should be using binman to build images...you seem to be building
> > > > > > > > > > something in parallel with that. Can you please take a look at binman?
> > > > > > > > >
> > > > > > > > > Again, I had explored using binman for this task. The one issue where
> > > > > > > > > I find the above flow better is that I can simply build my payload
> > > > > > > > > image(s) followed by 'make capsule' to generate the capsules for
> > > > > > > > > earlier generated images. In it's current form, I don't see an easy
> > > > > > > > > way to enforce this dependency in binman when I want to build the
> > > > > > > > > payload followed by generation of capsules. I did see the mention of
> > > > > > > > > encapsulating an entry within another dependent entry, but I think
> > > > > > > > > that makes the implementation more complex than it ought to be.
> > > > > > > > >
> > > > > > > > > I think it is much easier to use the make flow to generate the images
> > > > > > > > > followed by capsules, instead of tweaking the binman node to first
> > > > > > > > > generate the payload images, followed by enabling the capsule node to
> > > > > > > > > build the capsules. If there is an easy way of enforcing this
> > > > > > > > > dependency, please let me know. Thanks
> > > > > > > >
> > > > > > > > Can you share your explorations? I think the capsule should be created
> > > > > > > > as part of the build, if enabled. Rather than changing the input
> > > > > > > > files, binman should produce new output files.
> > > > > > >
> > > > > > > This is an issue of handling dependencies in binman, and not changing
> > > > > > > input files. We do not have support for telling binman "build/generate
> > > > > > > this particular image first before you proceed to build the capsules
> > > > > > > using the earlier built images". I am not sure if this can be done in
> > > > > > > a generic manner in binman, so that irrespective of the image being
> > > > > > > generated, it can be specified to build capsules once the capsule
> > > > > > > input images have been generated.
> > > > > >
> > > > > > I'm just not sure what you are getting out here.
> > > > > >
> > > > > > See INPUTS-y for the input files to binman. Then binman uses these to
> > > > > > generate output files. It does not mess with the input files, nor
> > > > > > should it. Please read the top part of the Binman motivation to
> > > > > > understand how all this works.
> > > > > >
> > > > > > At the risk of repeating myself, we want the Makefile to focus on
> > > > > > building U-Boot, with Binman handling the laterprocessing of those
> > > > > > files. Binman may run as part of the U-Boot build, and/or can be run
> > > > > > later, with the input files.
> > > > > >
> > > > > > >
> > > > > > > >
> > > > > > > > We are trying to remove most of the output logic in Makefile. It
> > > > > > > > should just be producing input files for binman.
> > > > > > >
> > > > > > > I understand. However, like I mentioned above, as of now, we don't
> > > > > > > have a way of handling dependencies in binman, at least in a generic
> > > > > > > manner. Once this support gets added, I know that it would be trivial
> > > > > > > to add support for building capsules in binman.
> > > > > >
> > > > > > What dependencies do you need? Please describe it in a simple manner
> > > > > > so I can understand. It cannot involve change the binman input files.
> > > > >
> > > > > Consider the following scenarios.
> > > > >
> > > > > One board, say Board A uses fip.bin as the input file(payload) for
> > > > > generating the capsule file. The fip.bin is being generated through
> > > > > binman. A binman entry is also added for generating the capsule(say
> > > > > fip.capule). Now, binman has to generate fip.bin *and subsequently*
> > > > > fip.capsule, as the capsule file will contain fip.bin as it's
> > > > > payload(input).
> > > > >
> > > > > Second Board B, uses u-boot.itb, which is a FIT image, as the input
> > > > > file for generating the capsule. The u-boot.itb is being generated
> > > > > through binman, and so is the capsule. But binman needs to build the
> > > > > u-boot.itb *before* it generates the corresponding capsule file, which
> > > > > uses u-boot.itb as the capsule payload.
> > > > >
> > > > > There can be multiple such examples of input files being generated by
> > > > > binman, followed by the capsule getting generated by binman. How do I
> > > > > specify this dependency in binman -- build/generate the input file
> > > > > first, and then use that files in generating the capsule.
> > >
> > > At present you can do this by ordering the images correctly, i.e. put
> > > the first image first and the dependent image after it. For the
> > > dependent image you can have a blob which is the entire first image.
> >
> > If putting the image entries in a certain order under the binman node
> > *guarantees* the generation of the first image prior to the generation
> > of the second image, I think that should work for my use case.
> > However, when I look at the binman.rst document, I see this mentioned
> > under the 'Image dependencies' section.
> >
> > <quote>
> > Binman does not currently support images that depend on each other. For example,
> > if one image creates `fred.bin` and then the next uses this `fred.bin` to
> > produce a final `image.bin`, then the behaviour is undefined. It may work, or it
> > may produce an error about `fred.bin` being missing, or it may use a version of
> > `fred.bin` from a previous run.
> > </quote>
> >
> > I believe the above is precisely what my use case is. One image
> > generating fip.bin(for e.g.), and the next image using this fip.bin to
> > produce the final fip.capsule. Or is this a case of the document not
> > reflecting the actual code?
>
> So long as the images are in the correct order in the resulting .dtb,
> then it will work. Quite a few boards rely on this.
>
> This comment is there because I planned to implement concurrent image
> generation (as is done for sections). But for now this is not
> implemented. Also I plan to implement templates before parallel
> images, so we can handle dependencies in a more general way.
>
> So if that is the only blocker, I am sorry for the docs being too
> conservative. I will send a patch.
>
> >
> > >
> > > If you are trying to do a second binman operation later, then perhaps
> > > something like 'binman sign' would be useful. Then people can provide
> > > their own key and sign the images in a separate binman operation. This
> > > is likely needed anyway for things to work on a signing server.
> > >
> > > >
> > > > Can you confirm if the above dependencies can be handled in binman
> > > > currently. If not, I'd suggest you remove your Nak for patch 6 of this
> > > > series [1]. Like I mentioned earlier, if there is a means of
> > > > specifying dependencies for generating images in binman, moving the
> > > > capsule generation to binman will not be a difficult task.
> > > >
> > > > Also, can you go through the other set of patches in V2, specifically
> > > > the ones where putting the public key ESL into the dtb is being done
> > > > through binman.
> > >
> > > The order of operation is supposed to be:
> > >
> > > 1. Various projects used to build their ouputs (e.g. TF-A)
> > > 2. Makefile used to build U-Boot:
> > > 2a. The build produces a set of files which serve as inputs to binman (INPUTS-y)
> > > 2b. Binman runs on INPUTS-y, picking up all the bits and creating the
> > > final firmware image(s)
> > > 3. If necessary, separate from the U-Boot build, binman can be used
> > > separately to do signing or whatever is needed on the final image(s)
> > >
> > > I understand that the public key is available in a CONFIG, so it
> > > should be possible to embed it in the build as input, either as a
> > > .dtsi build using 2a, or as a binary file pulled in by binman in 2b.
> >
> > Using a dtsi would mean that every platform which wants to enable
> > capsule authentication would need to add a signature node to it's
> > dtsi. Instead, is it not simpler to just generate a dtbo and merge it
> > with the dtb being generated. That is what was being done in V1 [1].
>
> Why is it simpler? The .dtsi is where we are supposed to put
> devicetree properties. It seems a lot harder (to me) to add it later.
> It could be a #include, or even just put it in the .dtsi if it is the
> same for all boards?

I was referring to the solution in V1 of the patch series [1]. All
that was needed there was the path to the public key ESL file, which
was being provided through the CONFIG_EFI_CAPSULE_ESL_FILE. The
embed_capsule_key.sh would take care of doing what you are suggesting
to be done via dtsi. When adding a node to the dtsi, the user will
still have to add a node to the dtsi and include the contents of the
ESL file. All that was being automated through the script in V1.

>
> > For your suggestion to pull it in as a binary file in binman, that
> > still does not fix the issue of not changing INPUTS-y.
> >
> > If you ask me, the embedding of the public-key into the dtb is not a
> > task suitable for binman. Why? Because this task is actually changing
> > one of the INPUTS-y file that feeds into binman. And yes, we can
> > generate a different set of files, like u-boot-capsule.dtb and
> > u-boot-capsule.bin -- implementing that is not at all difficult. But
> > like I had highlighted earlier, and also explained by Ilias, we
> > already have platforms that use capsule updates and which use
> > u-boot.dtb and u-boot.bin. Also, platforms would not want a separate
>
> Those platforms should change, IMO. But how can this be, when this
> functionality has not yet been added to U-Boot?
>
> > set of files, one for normal boot, one when using capsules. So I think
> > it is imperative that we generate the same set of files irrespective
> > of whether a platform enables capsule updates. So a proper design
> > would be to add/embed the public key into the dtb as the dtb is
> > getting built. Again, this is what was being done in V1.
>
> I completely disagree with this. A capsule update is not the same as
> the vanilla board build / binary. Please can you just give up on this
> idea? Many platforms generate their output in separate files, e.g. see
> u-boot-rockchip.bin - it just does not make sense to change the built
> binary after it is built.

I completely agree with your last statement. Which is why I said that
if we want to use the same files which are otherwise
generated(u-boot.dtb, u-boot.bin), binman is not the right way to add
the public key to the platform's dtb -- that should be done when the
dtb is being built.

-sughosh

[1] - https://lists.denx.de/pipermail/u-boot/2023-June/520121.html

>
> >
> >
> > >
> > > The patches I reviewed are modifying the input files (INPUTS-y) which
> > > is not allowed. Nor does it seem to be necessary.
> >
> > The necessity was explained by me and Ilias earlier, and above.
>
> OK, but it still is not correct.
>
> Regards,
> Simon
>
> >
> > -sughosh
> >
> > [1] - https://lists.denx.de/pipermail/u-boot/2023-June/520121.html
> >
> >
> >
> >
> > >
> > > As to having 'real' dependencies between images in Binman, I believe
> > > that should be possible. At the moment images are entirely
> > > independent, but I am looking at implementing a simple 'template'
> > > scheme, where you can include some or all of an image description in
> > > another image. See [1] common-part in case you have thoughts on that.
> > >
> > > Regards,
> > > Simon
> > >
> > > [1] https://patchwork.ozlabs.org/project/uboot/patch/9e905abd369f1d8b182e6fc6e3c9ef4b1526bf76.1685975993.git.jan.kiszka@siemens.com/

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/7] Makefile: Add a target for building capsules
  2023-06-27 17:42                       ` Sughosh Ganu
@ 2023-06-28  7:42                         ` Simon Glass
  2023-06-28  8:42                           ` Ilias Apalodimas
  2023-06-28 10:00                           ` Sughosh Ganu
  0 siblings, 2 replies; 50+ messages in thread
From: Simon Glass @ 2023-06-28  7:42 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Michal Simek,
	Takahiro Akashi

Hi Sughosh,

On Tue, 27 Jun 2023 at 18:42, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
>
> hi Simon,
>
> On Tue, 27 Jun 2023 at 17:51, Simon Glass <sjg@chromium.org> wrote:
> >
> > Hi Sughosh,
> >
> > On Tue, 27 Jun 2023 at 13:08, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > >
> > > hi Simon,
> > >
> > > On Tue, 27 Jun 2023 at 16:50, Simon Glass <sjg@chromium.org> wrote:
> > > >
> > > > Hi Sughosh,
> > > >
> > > > On Tue, 27 Jun 2023 at 05:57, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > >
> > > > > hi Simon,
> > > > >
> > > > > On Mon, 26 Jun 2023 at 17:43, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > >
> > > > > > hi Simon,
> > > > > >
> > > > > > On Mon, 26 Jun 2023 at 14:38, Simon Glass <sjg@chromium.org> wrote:
> > > > > > >
> > > > > > > Hi Sughosh,
> > > > > > >
> > > > > > > On Wed, 21 Jun 2023 at 05:26, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > > >
> > > > > > > > hi Simon,
> > > > > > > >
> > > > > > > > On Mon, 19 Jun 2023 at 18:07, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > >
> > > > > > > > > Hi Sughosh,
> > > > > > > > >
> > > > > > > > > On Thu, 15 Jun 2023 at 17:25, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > > > > >
> > > > > > > > > > hi Simon,
> > > > > > > > > >
> > > > > > > > > > On Thu, 15 Jun 2023 at 14:44, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > > >
> > > > > > > > > > > Hi Sughosh,
> > > > > > > > > > >
> > > > > > > > > > > On Tue, 13 Jun 2023 at 11:39, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > > > > > > >
> > > > > > > > > > > > Add a target for building EFI capsules. The capsule parameters are
> > > > > > > > > > > > specified through a config file, and the path to the config file is
> > > > > > > > > > > > specified through CONFIG_EFI_CAPSULE_CFG_FILE. When the config file is
> > > > > > > > > > > > not specified, the command only builds tools.
> > > > > > > > > > > >
> > > > > > > > > > > > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > > > > > > > > > > > ---
> > > > > > > > > > > >  Makefile | 9 +++++++++
> > > > > > > > > > > >  1 file changed, 9 insertions(+)
> > > > > > > > > > > >
> > > > > > > > > > > > diff --git a/Makefile b/Makefile
> > > > > > > > > > > > index 10bfaa52ad..96db29aa77 100644
> > > > > > > > > > > > --- a/Makefile
> > > > > > > > > > > > +++ b/Makefile
> > > > > > > > > > > > @@ -1151,6 +1151,15 @@ dtbs: dts/dt.dtb
> > > > > > > > > > > >  dts/dt.dtb: u-boot
> > > > > > > > > > > >         $(Q)$(MAKE) $(build)=dts dtbs
> > > > > > > > > > > >
> > > > > > > > > > > > +quiet_cmd_mkeficapsule = MKEFICAPSULE $@
> > > > > > > > > > > > +cmd_mkeficapsule = $(objtree)/tools/mkeficapsule $@
> > > > > > > > > > > > +
> > > > > > > > > > > > +PHONY += capsule
> > > > > > > > > > > > +capsule: tools
> > > > > > > > > > > > +ifneq ($(CONFIG_EFI_CAPSULE_CFG_FILE),"")
> > > > > > > > > > > > +       $(call cmd,mkeficapsule)
> > > > > > > > > > > > +endif
> > > > > > > > > > > > +
> > > > > > > > > > > >  quiet_cmd_copy = COPY    $@
> > > > > > > > > > > >        cmd_copy = cp $< $@
> > > > > > > > > > > >
> > > > > > > > > > > > --
> > > > > > > > > > > > 2.34.1
> > > > > > > > > > > >
> > > > > > > > > > >
> > > > > > > > > > > We should be using binman to build images...you seem to be building
> > > > > > > > > > > something in parallel with that. Can you please take a look at binman?
> > > > > > > > > >
> > > > > > > > > > Again, I had explored using binman for this task. The one issue where
> > > > > > > > > > I find the above flow better is that I can simply build my payload
> > > > > > > > > > image(s) followed by 'make capsule' to generate the capsules for
> > > > > > > > > > earlier generated images. In it's current form, I don't see an easy
> > > > > > > > > > way to enforce this dependency in binman when I want to build the
> > > > > > > > > > payload followed by generation of capsules. I did see the mention of
> > > > > > > > > > encapsulating an entry within another dependent entry, but I think
> > > > > > > > > > that makes the implementation more complex than it ought to be.
> > > > > > > > > >
> > > > > > > > > > I think it is much easier to use the make flow to generate the images
> > > > > > > > > > followed by capsules, instead of tweaking the binman node to first
> > > > > > > > > > generate the payload images, followed by enabling the capsule node to
> > > > > > > > > > build the capsules. If there is an easy way of enforcing this
> > > > > > > > > > dependency, please let me know. Thanks
> > > > > > > > >
> > > > > > > > > Can you share your explorations? I think the capsule should be created
> > > > > > > > > as part of the build, if enabled. Rather than changing the input
> > > > > > > > > files, binman should produce new output files.
> > > > > > > >
> > > > > > > > This is an issue of handling dependencies in binman, and not changing
> > > > > > > > input files. We do not have support for telling binman "build/generate
> > > > > > > > this particular image first before you proceed to build the capsules
> > > > > > > > using the earlier built images". I am not sure if this can be done in
> > > > > > > > a generic manner in binman, so that irrespective of the image being
> > > > > > > > generated, it can be specified to build capsules once the capsule
> > > > > > > > input images have been generated.
> > > > > > >
> > > > > > > I'm just not sure what you are getting out here.
> > > > > > >
> > > > > > > See INPUTS-y for the input files to binman. Then binman uses these to
> > > > > > > generate output files. It does not mess with the input files, nor
> > > > > > > should it. Please read the top part of the Binman motivation to
> > > > > > > understand how all this works.
> > > > > > >
> > > > > > > At the risk of repeating myself, we want the Makefile to focus on
> > > > > > > building U-Boot, with Binman handling the laterprocessing of those
> > > > > > > files. Binman may run as part of the U-Boot build, and/or can be run
> > > > > > > later, with the input files.
> > > > > > >
> > > > > > > >
> > > > > > > > >
> > > > > > > > > We are trying to remove most of the output logic in Makefile. It
> > > > > > > > > should just be producing input files for binman.
> > > > > > > >
> > > > > > > > I understand. However, like I mentioned above, as of now, we don't
> > > > > > > > have a way of handling dependencies in binman, at least in a generic
> > > > > > > > manner. Once this support gets added, I know that it would be trivial
> > > > > > > > to add support for building capsules in binman.
> > > > > > >
> > > > > > > What dependencies do you need? Please describe it in a simple manner
> > > > > > > so I can understand. It cannot involve change the binman input files.
> > > > > >
> > > > > > Consider the following scenarios.
> > > > > >
> > > > > > One board, say Board A uses fip.bin as the input file(payload) for
> > > > > > generating the capsule file. The fip.bin is being generated through
> > > > > > binman. A binman entry is also added for generating the capsule(say
> > > > > > fip.capule). Now, binman has to generate fip.bin *and subsequently*
> > > > > > fip.capsule, as the capsule file will contain fip.bin as it's
> > > > > > payload(input).
> > > > > >
> > > > > > Second Board B, uses u-boot.itb, which is a FIT image, as the input
> > > > > > file for generating the capsule. The u-boot.itb is being generated
> > > > > > through binman, and so is the capsule. But binman needs to build the
> > > > > > u-boot.itb *before* it generates the corresponding capsule file, which
> > > > > > uses u-boot.itb as the capsule payload.
> > > > > >
> > > > > > There can be multiple such examples of input files being generated by
> > > > > > binman, followed by the capsule getting generated by binman. How do I
> > > > > > specify this dependency in binman -- build/generate the input file
> > > > > > first, and then use that files in generating the capsule.
> > > >
> > > > At present you can do this by ordering the images correctly, i.e. put
> > > > the first image first and the dependent image after it. For the
> > > > dependent image you can have a blob which is the entire first image.
> > >
> > > If putting the image entries in a certain order under the binman node
> > > *guarantees* the generation of the first image prior to the generation
> > > of the second image, I think that should work for my use case.
> > > However, when I look at the binman.rst document, I see this mentioned
> > > under the 'Image dependencies' section.
> > >
> > > <quote>
> > > Binman does not currently support images that depend on each other. For example,
> > > if one image creates `fred.bin` and then the next uses this `fred.bin` to
> > > produce a final `image.bin`, then the behaviour is undefined. It may work, or it
> > > may produce an error about `fred.bin` being missing, or it may use a version of
> > > `fred.bin` from a previous run.
> > > </quote>
> > >
> > > I believe the above is precisely what my use case is. One image
> > > generating fip.bin(for e.g.), and the next image using this fip.bin to
> > > produce the final fip.capsule. Or is this a case of the document not
> > > reflecting the actual code?
> >
> > So long as the images are in the correct order in the resulting .dtb,
> > then it will work. Quite a few boards rely on this.
> >
> > This comment is there because I planned to implement concurrent image
> > generation (as is done for sections). But for now this is not
> > implemented. Also I plan to implement templates before parallel
> > images, so we can handle dependencies in a more general way.
> >
> > So if that is the only blocker, I am sorry for the docs being too
> > conservative. I will send a patch.
> >
> > >
> > > >
> > > > If you are trying to do a second binman operation later, then perhaps
> > > > something like 'binman sign' would be useful. Then people can provide
> > > > their own key and sign the images in a separate binman operation. This
> > > > is likely needed anyway for things to work on a signing server.
> > > >
> > > > >
> > > > > Can you confirm if the above dependencies can be handled in binman
> > > > > currently. If not, I'd suggest you remove your Nak for patch 6 of this
> > > > > series [1]. Like I mentioned earlier, if there is a means of
> > > > > specifying dependencies for generating images in binman, moving the
> > > > > capsule generation to binman will not be a difficult task.
> > > > >
> > > > > Also, can you go through the other set of patches in V2, specifically
> > > > > the ones where putting the public key ESL into the dtb is being done
> > > > > through binman.
> > > >
> > > > The order of operation is supposed to be:
> > > >
> > > > 1. Various projects used to build their ouputs (e.g. TF-A)
> > > > 2. Makefile used to build U-Boot:
> > > > 2a. The build produces a set of files which serve as inputs to binman (INPUTS-y)
> > > > 2b. Binman runs on INPUTS-y, picking up all the bits and creating the
> > > > final firmware image(s)
> > > > 3. If necessary, separate from the U-Boot build, binman can be used
> > > > separately to do signing or whatever is needed on the final image(s)
> > > >
> > > > I understand that the public key is available in a CONFIG, so it
> > > > should be possible to embed it in the build as input, either as a
> > > > .dtsi build using 2a, or as a binary file pulled in by binman in 2b.
> > >
> > > Using a dtsi would mean that every platform which wants to enable
> > > capsule authentication would need to add a signature node to it's
> > > dtsi. Instead, is it not simpler to just generate a dtbo and merge it
> > > with the dtb being generated. That is what was being done in V1 [1].
> >
> > Why is it simpler? The .dtsi is where we are supposed to put
> > devicetree properties. It seems a lot harder (to me) to add it later.
> > It could be a #include, or even just put it in the .dtsi if it is the
> > same for all boards?
>
> I was referring to the solution in V1 of the patch series [1]. All
> that was needed there was the path to the public key ESL file, which
> was being provided through the CONFIG_EFI_CAPSULE_ESL_FILE. The
> embed_capsule_key.sh would take care of doing what you are suggesting
> to be done via dtsi. When adding a node to the dtsi, the user will
> still have to add a node to the dtsi and include the contents of the
> ESL file. All that was being automated through the script in V1.

Instead of all that complexity, can you check the irc where Kwiboo
describes how to add a .dtsi fragment containing that CONFIG

BTW the format you are using in the dtb looks to be binary. Have you
thought about using real properties and values like the existing
public key mechanism? Or is that binary format required by EFI?

>
> >
> > > For your suggestion to pull it in as a binary file in binman, that
> > > still does not fix the issue of not changing INPUTS-y.
> > >
> > > If you ask me, the embedding of the public-key into the dtb is not a
> > > task suitable for binman. Why? Because this task is actually changing
> > > one of the INPUTS-y file that feeds into binman. And yes, we can
> > > generate a different set of files, like u-boot-capsule.dtb and
> > > u-boot-capsule.bin -- implementing that is not at all difficult. But
> > > like I had highlighted earlier, and also explained by Ilias, we
> > > already have platforms that use capsule updates and which use
> > > u-boot.dtb and u-boot.bin. Also, platforms would not want a separate
> >
> > Those platforms should change, IMO. But how can this be, when this
> > functionality has not yet been added to U-Boot?
> >
> > > set of files, one for normal boot, one when using capsules. So I think
> > > it is imperative that we generate the same set of files irrespective
> > > of whether a platform enables capsule updates. So a proper design
> > > would be to add/embed the public key into the dtb as the dtb is
> > > getting built. Again, this is what was being done in V1.
> >
> > I completely disagree with this. A capsule update is not the same as
> > the vanilla board build / binary. Please can you just give up on this
> > idea? Many platforms generate their output in separate files, e.g. see
> > u-boot-rockchip.bin - it just does not make sense to change the built
> > binary after it is built.
>
> I completely agree with your last statement. Which is why I said that
> if we want to use the same files which are otherwise
> generated(u-boot.dtb, u-boot.bin), binman is not the right way to add
> the public key to the platform's dtb -- that should be done when the
> dtb is being built.

OK, so perhaps that is some progress.

My second point (perhaps lost above) is that the capsule file should
be called u-boot-capsule.bin or something like that, not u-boot.bin,
since that is the output from the build system.

Regards,
Simon

>
> -sughosh
>
> [1] - https://lists.denx.de/pipermail/u-boot/2023-June/520121.html
>
> >
> > >
> > >
> > > >
> > > > The patches I reviewed are modifying the input files (INPUTS-y) which
> > > > is not allowed. Nor does it seem to be necessary.
> > >
> > > The necessity was explained by me and Ilias earlier, and above.
> >
> > OK, but it still is not correct.
> >
> > Regards,
> > Simon
> >
> > >
> > > -sughosh
> > >
> > > [1] - https://lists.denx.de/pipermail/u-boot/2023-June/520121.html
> > >
> > >
> > >
> > >
> > > >
> > > > As to having 'real' dependencies between images in Binman, I believe
> > > > that should be possible. At the moment images are entirely
> > > > independent, but I am looking at implementing a simple 'template'
> > > > scheme, where you can include some or all of an image description in
> > > > another image. See [1] common-part in case you have thoughts on that.
> > > >
> > > > Regards,
> > > > Simon
> > > >
> > > > [1] https://patchwork.ozlabs.org/project/uboot/patch/9e905abd369f1d8b182e6fc6e3c9ef4b1526bf76.1685975993.git.jan.kiszka@siemens.com/

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/7] Makefile: Add a target for building capsules
  2023-06-28  7:42                         ` Simon Glass
@ 2023-06-28  8:42                           ` Ilias Apalodimas
  2023-06-28 10:00                           ` Sughosh Ganu
  1 sibling, 0 replies; 50+ messages in thread
From: Ilias Apalodimas @ 2023-06-28  8:42 UTC (permalink / raw)
  To: Simon Glass, Jonas Karlman
  Cc: Sughosh Ganu, u-boot, Heinrich Schuchardt, Michal Simek, Takahiro Akashi

[...]

> > > > > The order of operation is supposed to be:
> > > > >
> > > > > 1. Various projects used to build their ouputs (e.g. TF-A)
> > > > > 2. Makefile used to build U-Boot:
> > > > > 2a. The build produces a set of files which serve as inputs to binman (INPUTS-y)
> > > > > 2b. Binman runs on INPUTS-y, picking up all the bits and creating the
> > > > > final firmware image(s)
> > > > > 3. If necessary, separate from the U-Boot build, binman can be used
> > > > > separately to do signing or whatever is needed on the final image(s)
> > > > >
> > > > > I understand that the public key is available in a CONFIG, so it
> > > > > should be possible to embed it in the build as input, either as a
> > > > > .dtsi build using 2a, or as a binary file pulled in by binman in 2b.
> > > >
> > > > Using a dtsi would mean that every platform which wants to enable
> > > > capsule authentication would need to add a signature node to it's
> > > > dtsi. Instead, is it not simpler to just generate a dtbo and merge it
> > > > with the dtb being generated. That is what was being done in V1 [1].
> > >
> > > Why is it simpler? The .dtsi is where we are supposed to put
> > > devicetree properties. It seems a lot harder (to me) to add it later.
> > > It could be a #include, or even just put it in the .dtsi if it is the
> > > same for all boards?
> >
> > I was referring to the solution in V1 of the patch series [1]. All
> > that was needed there was the path to the public key ESL file, which
> > was being provided through the CONFIG_EFI_CAPSULE_ESL_FILE. The
> > embed_capsule_key.sh would take care of doing what you are suggesting
> > to be done via dtsi. When adding a node to the dtsi, the user will
> > still have to add a node to the dtsi and include the contents of the
> > ESL file. All that was being automated through the script in V1.
>
> Instead of all that complexity, can you check the irc where Kwiboo
> describes how to add a .dtsi fragment containing that CONFIG
>
> BTW the format you are using in the dtb looks to be binary. Have you
> thought about using real properties and values like the existing
> public key mechanism? Or is that binary format required by EFI?
>
> >
> > >
> > > > For your suggestion to pull it in as a binary file in binman, that
> > > > still does not fix the issue of not changing INPUTS-y.
> > > >
> > > > If you ask me, the embedding of the public-key into the dtb is not a
> > > > task suitable for binman. Why? Because this task is actually changing
> > > > one of the INPUTS-y file that feeds into binman. And yes, we can
> > > > generate a different set of files, like u-boot-capsule.dtb and
> > > > u-boot-capsule.bin -- implementing that is not at all difficult. But
> > > > like I had highlighted earlier, and also explained by Ilias, we
> > > > already have platforms that use capsule updates and which use
> > > > u-boot.dtb and u-boot.bin. Also, platforms would not want a separate
> > >
> > > Those platforms should change, IMO. But how can this be, when this
> > > functionality has not yet been added to U-Boot?
> > >
> > > > set of files, one for normal boot, one when using capsules. So I think
> > > > it is imperative that we generate the same set of files irrespective
> > > > of whether a platform enables capsule updates. So a proper design
> > > > would be to add/embed the public key into the dtb as the dtb is
> > > > getting built. Again, this is what was being done in V1.
> > >
> > > I completely disagree with this. A capsule update is not the same as
> > > the vanilla board build / binary. Please can you just give up on this
> > > idea? Many platforms generate their output in separate files, e.g. see
> > > u-boot-rockchip.bin - it just does not make sense to change the built
> > > binary after it is built.
> >
> > I completely agree with your last statement. Which is why I said that
> > if we want to use the same files which are otherwise
> > generated(u-boot.dtb, u-boot.bin), binman is not the right way to add
> > the public key to the platform's dtb -- that should be done when the
> > dtb is being built.
>
> OK, so perhaps that is some progress.
>
> My second point (perhaps lost above) is that the capsule file should
> be called u-boot-capsule.bin or something like that, not u-boot.bin,
> since that is the output from the build system.

+CC Jonas who proposed the idea.

So one reasonable way to plug this was what Jonas and I discussed over IRC.
Instead of having a custom script injecting the 'signature' node in
the .dts we could add
- u-boot-cap-key.dtsi (or similar)
- if authenticated capsule updates are selected then
CONFIG_DEVICE_TREE_INCLUDES can be automatically selected to include
the .dts that contains the public portion of the key

That would take care of the public key inclusion in the final binary,
without going through binman since this is already working in
makefiles.   We need to add a few sanity checks.  E.g if the .esl file
is empty we need to pop a build error, but that's all sanely doable.

For the last part of the capsule generation, you can use binman.  You
can include the private key you need to sign the capsule in a config
file that's fed into it.  That tool would then call mkeficapsule and
generate the signed capsule update files.  You'll be missing a
makefile target 'make capsules', but given the fact you need to build
u-boot to include it in the capsule eventually, I don't think this is
a big deal.

Regards
/Ilias



>
> Regards,
> Simon
>
> >
> > -sughosh
> >
> > [1] - https://lists.denx.de/pipermail/u-boot/2023-June/520121.html
> >
> > >
> > > >
> > > >
> > > > >
> > > > > The patches I reviewed are modifying the input files (INPUTS-y) which
> > > > > is not allowed. Nor does it seem to be necessary.
> > > >
> > > > The necessity was explained by me and Ilias earlier, and above.
> > >
> > > OK, but it still is not correct.
> > >
> > > Regards,
> > > Simon
> > >
> > > >
> > > > -sughosh
> > > >
> > > > [1] - https://lists.denx.de/pipermail/u-boot/2023-June/520121.html
> > > >
> > > >
> > > >
> > > >
> > > > >
> > > > > As to having 'real' dependencies between images in Binman, I believe
> > > > > that should be possible. At the moment images are entirely
> > > > > independent, but I am looking at implementing a simple 'template'
> > > > > scheme, where you can include some or all of an image description in
> > > > > another image. See [1] common-part in case you have thoughts on that.
> > > > >
> > > > > Regards,
> > > > > Simon
> > > > >
> > > > > [1] https://patchwork.ozlabs.org/project/uboot/patch/9e905abd369f1d8b182e6fc6e3c9ef4b1526bf76.1685975993.git.jan.kiszka@siemens.com/

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/7] Makefile: Add a target for building capsules
  2023-06-28  7:42                         ` Simon Glass
  2023-06-28  8:42                           ` Ilias Apalodimas
@ 2023-06-28 10:00                           ` Sughosh Ganu
  2023-06-28 10:19                             ` Simon Glass
  1 sibling, 1 reply; 50+ messages in thread
From: Sughosh Ganu @ 2023-06-28 10:00 UTC (permalink / raw)
  To: Simon Glass
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Michal Simek,
	Takahiro Akashi

hi Simon,

On Wed, 28 Jun 2023 at 13:12, Simon Glass <sjg@chromium.org> wrote:
>
> Hi Sughosh,
>
> On Tue, 27 Jun 2023 at 18:42, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> >
> > hi Simon,
> >
> > On Tue, 27 Jun 2023 at 17:51, Simon Glass <sjg@chromium.org> wrote:
> > >
> > > Hi Sughosh,
> > >
> > > On Tue, 27 Jun 2023 at 13:08, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > >
> > > > hi Simon,
> > > >
> > > > On Tue, 27 Jun 2023 at 16:50, Simon Glass <sjg@chromium.org> wrote:
> > > > >
> > > > > Hi Sughosh,
> > > > >
> > > > > On Tue, 27 Jun 2023 at 05:57, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > >
> > > > > > hi Simon,
> > > > > >
> > > > > > On Mon, 26 Jun 2023 at 17:43, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > >
> > > > > > > hi Simon,
> > > > > > >
> > > > > > > On Mon, 26 Jun 2023 at 14:38, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > >
> > > > > > > > Hi Sughosh,
> > > > > > > >
> > > > > > > > On Wed, 21 Jun 2023 at 05:26, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > > > >
> > > > > > > > > hi Simon,
> > > > > > > > >
> > > > > > > > > On Mon, 19 Jun 2023 at 18:07, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > >
> > > > > > > > > > Hi Sughosh,
> > > > > > > > > >
> > > > > > > > > > On Thu, 15 Jun 2023 at 17:25, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > > > > > >
> > > > > > > > > > > hi Simon,
> > > > > > > > > > >
> > > > > > > > > > > On Thu, 15 Jun 2023 at 14:44, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > > > >
> > > > > > > > > > > > Hi Sughosh,
> > > > > > > > > > > >
> > > > > > > > > > > > On Tue, 13 Jun 2023 at 11:39, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > > > > > > > >
> > > > > > > > > > > > > Add a target for building EFI capsules. The capsule parameters are
> > > > > > > > > > > > > specified through a config file, and the path to the config file is
> > > > > > > > > > > > > specified through CONFIG_EFI_CAPSULE_CFG_FILE. When the config file is
> > > > > > > > > > > > > not specified, the command only builds tools.
> > > > > > > > > > > > >
> > > > > > > > > > > > > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > > > > > > > > > > > > ---
> > > > > > > > > > > > >  Makefile | 9 +++++++++
> > > > > > > > > > > > >  1 file changed, 9 insertions(+)
> > > > > > > > > > > > >
> > > > > > > > > > > > > diff --git a/Makefile b/Makefile
> > > > > > > > > > > > > index 10bfaa52ad..96db29aa77 100644
> > > > > > > > > > > > > --- a/Makefile
> > > > > > > > > > > > > +++ b/Makefile
> > > > > > > > > > > > > @@ -1151,6 +1151,15 @@ dtbs: dts/dt.dtb
> > > > > > > > > > > > >  dts/dt.dtb: u-boot
> > > > > > > > > > > > >         $(Q)$(MAKE) $(build)=dts dtbs
> > > > > > > > > > > > >
> > > > > > > > > > > > > +quiet_cmd_mkeficapsule = MKEFICAPSULE $@
> > > > > > > > > > > > > +cmd_mkeficapsule = $(objtree)/tools/mkeficapsule $@
> > > > > > > > > > > > > +
> > > > > > > > > > > > > +PHONY += capsule
> > > > > > > > > > > > > +capsule: tools
> > > > > > > > > > > > > +ifneq ($(CONFIG_EFI_CAPSULE_CFG_FILE),"")
> > > > > > > > > > > > > +       $(call cmd,mkeficapsule)
> > > > > > > > > > > > > +endif
> > > > > > > > > > > > > +
> > > > > > > > > > > > >  quiet_cmd_copy = COPY    $@
> > > > > > > > > > > > >        cmd_copy = cp $< $@
> > > > > > > > > > > > >
> > > > > > > > > > > > > --
> > > > > > > > > > > > > 2.34.1
> > > > > > > > > > > > >
> > > > > > > > > > > >
> > > > > > > > > > > > We should be using binman to build images...you seem to be building
> > > > > > > > > > > > something in parallel with that. Can you please take a look at binman?
> > > > > > > > > > >
> > > > > > > > > > > Again, I had explored using binman for this task. The one issue where
> > > > > > > > > > > I find the above flow better is that I can simply build my payload
> > > > > > > > > > > image(s) followed by 'make capsule' to generate the capsules for
> > > > > > > > > > > earlier generated images. In it's current form, I don't see an easy
> > > > > > > > > > > way to enforce this dependency in binman when I want to build the
> > > > > > > > > > > payload followed by generation of capsules. I did see the mention of
> > > > > > > > > > > encapsulating an entry within another dependent entry, but I think
> > > > > > > > > > > that makes the implementation more complex than it ought to be.
> > > > > > > > > > >
> > > > > > > > > > > I think it is much easier to use the make flow to generate the images
> > > > > > > > > > > followed by capsules, instead of tweaking the binman node to first
> > > > > > > > > > > generate the payload images, followed by enabling the capsule node to
> > > > > > > > > > > build the capsules. If there is an easy way of enforcing this
> > > > > > > > > > > dependency, please let me know. Thanks
> > > > > > > > > >
> > > > > > > > > > Can you share your explorations? I think the capsule should be created
> > > > > > > > > > as part of the build, if enabled. Rather than changing the input
> > > > > > > > > > files, binman should produce new output files.
> > > > > > > > >
> > > > > > > > > This is an issue of handling dependencies in binman, and not changing
> > > > > > > > > input files. We do not have support for telling binman "build/generate
> > > > > > > > > this particular image first before you proceed to build the capsules
> > > > > > > > > using the earlier built images". I am not sure if this can be done in
> > > > > > > > > a generic manner in binman, so that irrespective of the image being
> > > > > > > > > generated, it can be specified to build capsules once the capsule
> > > > > > > > > input images have been generated.
> > > > > > > >
> > > > > > > > I'm just not sure what you are getting out here.
> > > > > > > >
> > > > > > > > See INPUTS-y for the input files to binman. Then binman uses these to
> > > > > > > > generate output files. It does not mess with the input files, nor
> > > > > > > > should it. Please read the top part of the Binman motivation to
> > > > > > > > understand how all this works.
> > > > > > > >
> > > > > > > > At the risk of repeating myself, we want the Makefile to focus on
> > > > > > > > building U-Boot, with Binman handling the laterprocessing of those
> > > > > > > > files. Binman may run as part of the U-Boot build, and/or can be run
> > > > > > > > later, with the input files.
> > > > > > > >
> > > > > > > > >
> > > > > > > > > >
> > > > > > > > > > We are trying to remove most of the output logic in Makefile. It
> > > > > > > > > > should just be producing input files for binman.
> > > > > > > > >
> > > > > > > > > I understand. However, like I mentioned above, as of now, we don't
> > > > > > > > > have a way of handling dependencies in binman, at least in a generic
> > > > > > > > > manner. Once this support gets added, I know that it would be trivial
> > > > > > > > > to add support for building capsules in binman.
> > > > > > > >
> > > > > > > > What dependencies do you need? Please describe it in a simple manner
> > > > > > > > so I can understand. It cannot involve change the binman input files.
> > > > > > >
> > > > > > > Consider the following scenarios.
> > > > > > >
> > > > > > > One board, say Board A uses fip.bin as the input file(payload) for
> > > > > > > generating the capsule file. The fip.bin is being generated through
> > > > > > > binman. A binman entry is also added for generating the capsule(say
> > > > > > > fip.capule). Now, binman has to generate fip.bin *and subsequently*
> > > > > > > fip.capsule, as the capsule file will contain fip.bin as it's
> > > > > > > payload(input).
> > > > > > >
> > > > > > > Second Board B, uses u-boot.itb, which is a FIT image, as the input
> > > > > > > file for generating the capsule. The u-boot.itb is being generated
> > > > > > > through binman, and so is the capsule. But binman needs to build the
> > > > > > > u-boot.itb *before* it generates the corresponding capsule file, which
> > > > > > > uses u-boot.itb as the capsule payload.
> > > > > > >
> > > > > > > There can be multiple such examples of input files being generated by
> > > > > > > binman, followed by the capsule getting generated by binman. How do I
> > > > > > > specify this dependency in binman -- build/generate the input file
> > > > > > > first, and then use that files in generating the capsule.
> > > > >
> > > > > At present you can do this by ordering the images correctly, i.e. put
> > > > > the first image first and the dependent image after it. For the
> > > > > dependent image you can have a blob which is the entire first image.
> > > >
> > > > If putting the image entries in a certain order under the binman node
> > > > *guarantees* the generation of the first image prior to the generation
> > > > of the second image, I think that should work for my use case.
> > > > However, when I look at the binman.rst document, I see this mentioned
> > > > under the 'Image dependencies' section.
> > > >
> > > > <quote>
> > > > Binman does not currently support images that depend on each other. For example,
> > > > if one image creates `fred.bin` and then the next uses this `fred.bin` to
> > > > produce a final `image.bin`, then the behaviour is undefined. It may work, or it
> > > > may produce an error about `fred.bin` being missing, or it may use a version of
> > > > `fred.bin` from a previous run.
> > > > </quote>
> > > >
> > > > I believe the above is precisely what my use case is. One image
> > > > generating fip.bin(for e.g.), and the next image using this fip.bin to
> > > > produce the final fip.capsule. Or is this a case of the document not
> > > > reflecting the actual code?
> > >
> > > So long as the images are in the correct order in the resulting .dtb,
> > > then it will work. Quite a few boards rely on this.
> > >
> > > This comment is there because I planned to implement concurrent image
> > > generation (as is done for sections). But for now this is not
> > > implemented. Also I plan to implement templates before parallel
> > > images, so we can handle dependencies in a more general way.
> > >
> > > So if that is the only blocker, I am sorry for the docs being too
> > > conservative. I will send a patch.
> > >
> > > >
> > > > >
> > > > > If you are trying to do a second binman operation later, then perhaps
> > > > > something like 'binman sign' would be useful. Then people can provide
> > > > > their own key and sign the images in a separate binman operation. This
> > > > > is likely needed anyway for things to work on a signing server.
> > > > >
> > > > > >
> > > > > > Can you confirm if the above dependencies can be handled in binman
> > > > > > currently. If not, I'd suggest you remove your Nak for patch 6 of this
> > > > > > series [1]. Like I mentioned earlier, if there is a means of
> > > > > > specifying dependencies for generating images in binman, moving the
> > > > > > capsule generation to binman will not be a difficult task.
> > > > > >
> > > > > > Also, can you go through the other set of patches in V2, specifically
> > > > > > the ones where putting the public key ESL into the dtb is being done
> > > > > > through binman.
> > > > >
> > > > > The order of operation is supposed to be:
> > > > >
> > > > > 1. Various projects used to build their ouputs (e.g. TF-A)
> > > > > 2. Makefile used to build U-Boot:
> > > > > 2a. The build produces a set of files which serve as inputs to binman (INPUTS-y)
> > > > > 2b. Binman runs on INPUTS-y, picking up all the bits and creating the
> > > > > final firmware image(s)
> > > > > 3. If necessary, separate from the U-Boot build, binman can be used
> > > > > separately to do signing or whatever is needed on the final image(s)
> > > > >
> > > > > I understand that the public key is available in a CONFIG, so it
> > > > > should be possible to embed it in the build as input, either as a
> > > > > .dtsi build using 2a, or as a binary file pulled in by binman in 2b.
> > > >
> > > > Using a dtsi would mean that every platform which wants to enable
> > > > capsule authentication would need to add a signature node to it's
> > > > dtsi. Instead, is it not simpler to just generate a dtbo and merge it
> > > > with the dtb being generated. That is what was being done in V1 [1].
> > >
> > > Why is it simpler? The .dtsi is where we are supposed to put
> > > devicetree properties. It seems a lot harder (to me) to add it later.
> > > It could be a #include, or even just put it in the .dtsi if it is the
> > > same for all boards?
> >
> > I was referring to the solution in V1 of the patch series [1]. All
> > that was needed there was the path to the public key ESL file, which
> > was being provided through the CONFIG_EFI_CAPSULE_ESL_FILE. The
> > embed_capsule_key.sh would take care of doing what you are suggesting
> > to be done via dtsi. When adding a node to the dtsi, the user will
> > still have to add a node to the dtsi and include the contents of the
> > ESL file. All that was being automated through the script in V1.
>
> Instead of all that complexity, can you check the irc where Kwiboo
> describes how to add a .dtsi fragment containing that CONFIG

Not sure what is so complex about the script. But yes, I can add a
dtsi file fragment. Only thing is, it will have to be added under all
the arch/$ARCH/dts/ directories which would want to support capsule
updates with authentication. The current logic in Makefile.lib looks
for the *u-boot.dtsi files under the same dir location as the dts
being built. But since you prefer having the dtsi file, I will add
these for the sandbox and arm arch's for now.

>
> BTW the format you are using in the dtb looks to be binary. Have you
> thought about using real properties and values like the existing
> public key mechanism? Or is that binary format required by EFI?

Yes, the UEFI spec defines the EFI Signature List(ESL) structure, and
expects the public key certificate to be in the ESL format. The same
structure is used for variable authentication as well -- the capsule
authentication logic basically uses variable authentication
functionality for signature verification.

>
> >
> > >
> > > > For your suggestion to pull it in as a binary file in binman, that
> > > > still does not fix the issue of not changing INPUTS-y.
> > > >
> > > > If you ask me, the embedding of the public-key into the dtb is not a
> > > > task suitable for binman. Why? Because this task is actually changing
> > > > one of the INPUTS-y file that feeds into binman. And yes, we can
> > > > generate a different set of files, like u-boot-capsule.dtb and
> > > > u-boot-capsule.bin -- implementing that is not at all difficult. But
> > > > like I had highlighted earlier, and also explained by Ilias, we
> > > > already have platforms that use capsule updates and which use
> > > > u-boot.dtb and u-boot.bin. Also, platforms would not want a separate
> > >
> > > Those platforms should change, IMO. But how can this be, when this
> > > functionality has not yet been added to U-Boot?
> > >
> > > > set of files, one for normal boot, one when using capsules. So I think
> > > > it is imperative that we generate the same set of files irrespective
> > > > of whether a platform enables capsule updates. So a proper design
> > > > would be to add/embed the public key into the dtb as the dtb is
> > > > getting built. Again, this is what was being done in V1.
> > >
> > > I completely disagree with this. A capsule update is not the same as
> > > the vanilla board build / binary. Please can you just give up on this
> > > idea? Many platforms generate their output in separate files, e.g. see
> > > u-boot-rockchip.bin - it just does not make sense to change the built
> > > binary after it is built.
> >
> > I completely agree with your last statement. Which is why I said that
> > if we want to use the same files which are otherwise
> > generated(u-boot.dtb, u-boot.bin), binman is not the right way to add
> > the public key to the platform's dtb -- that should be done when the
> > dtb is being built.
>
> OK, so perhaps that is some progress.
>
> My second point (perhaps lost above) is that the capsule file should
> be called u-boot-capsule.bin or something like that, not u-boot.bin,
> since that is the output from the build system.

I think this point is moot with the public key getting embedded
through the dtsi file.

-sughosh

>
> Regards,
> Simon
>
> >
> > -sughosh
> >
> > [1] - https://lists.denx.de/pipermail/u-boot/2023-June/520121.html
> >
> > >
> > > >
> > > >
> > > > >
> > > > > The patches I reviewed are modifying the input files (INPUTS-y) which
> > > > > is not allowed. Nor does it seem to be necessary.
> > > >
> > > > The necessity was explained by me and Ilias earlier, and above.
> > >
> > > OK, but it still is not correct.
> > >
> > > Regards,
> > > Simon
> > >
> > > >
> > > > -sughosh
> > > >
> > > > [1] - https://lists.denx.de/pipermail/u-boot/2023-June/520121.html
> > > >
> > > >
> > > >
> > > >
> > > > >
> > > > > As to having 'real' dependencies between images in Binman, I believe
> > > > > that should be possible. At the moment images are entirely
> > > > > independent, but I am looking at implementing a simple 'template'
> > > > > scheme, where you can include some or all of an image description in
> > > > > another image. See [1] common-part in case you have thoughts on that.
> > > > >
> > > > > Regards,
> > > > > Simon
> > > > >
> > > > > [1] https://patchwork.ozlabs.org/project/uboot/patch/9e905abd369f1d8b182e6fc6e3c9ef4b1526bf76.1685975993.git.jan.kiszka@siemens.com/

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/7] Makefile: Add a target for building capsules
  2023-06-28 10:00                           ` Sughosh Ganu
@ 2023-06-28 10:19                             ` Simon Glass
  2023-06-28 12:25                               ` Sughosh Ganu
  0 siblings, 1 reply; 50+ messages in thread
From: Simon Glass @ 2023-06-28 10:19 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Michal Simek,
	Takahiro Akashi

Hi Sughosh,

On Wed, 28 Jun 2023 at 11:00, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
>
> hi Simon,
>
> On Wed, 28 Jun 2023 at 13:12, Simon Glass <sjg@chromium.org> wrote:
> >
> > Hi Sughosh,
> >
> > On Tue, 27 Jun 2023 at 18:42, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > >
> > > hi Simon,
> > >
> > > On Tue, 27 Jun 2023 at 17:51, Simon Glass <sjg@chromium.org> wrote:
> > > >
> > > > Hi Sughosh,
> > > >
> > > > On Tue, 27 Jun 2023 at 13:08, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > >
> > > > > hi Simon,
> > > > >
> > > > > On Tue, 27 Jun 2023 at 16:50, Simon Glass <sjg@chromium.org> wrote:
> > > > > >
> > > > > > Hi Sughosh,
> > > > > >
> > > > > > On Tue, 27 Jun 2023 at 05:57, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > >
> > > > > > > hi Simon,
> > > > > > >
> > > > > > > On Mon, 26 Jun 2023 at 17:43, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > > >
> > > > > > > > hi Simon,
> > > > > > > >
> > > > > > > > On Mon, 26 Jun 2023 at 14:38, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > >
> > > > > > > > > Hi Sughosh,
> > > > > > > > >
> > > > > > > > > On Wed, 21 Jun 2023 at 05:26, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > > > > >
> > > > > > > > > > hi Simon,
> > > > > > > > > >
> > > > > > > > > > On Mon, 19 Jun 2023 at 18:07, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > > >
> > > > > > > > > > > Hi Sughosh,
> > > > > > > > > > >
> > > > > > > > > > > On Thu, 15 Jun 2023 at 17:25, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > > > > > > >
> > > > > > > > > > > > hi Simon,
> > > > > > > > > > > >
> > > > > > > > > > > > On Thu, 15 Jun 2023 at 14:44, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > > > > >
> > > > > > > > > > > > > Hi Sughosh,
> > > > > > > > > > > > >
> > > > > > > > > > > > > On Tue, 13 Jun 2023 at 11:39, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > Add a target for building EFI capsules. The capsule parameters are
> > > > > > > > > > > > > > specified through a config file, and the path to the config file is
> > > > > > > > > > > > > > specified through CONFIG_EFI_CAPSULE_CFG_FILE. When the config file is
> > > > > > > > > > > > > > not specified, the command only builds tools.
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > > > > > > > > > > > > > ---
> > > > > > > > > > > > > >  Makefile | 9 +++++++++
> > > > > > > > > > > > > >  1 file changed, 9 insertions(+)
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > diff --git a/Makefile b/Makefile
> > > > > > > > > > > > > > index 10bfaa52ad..96db29aa77 100644
> > > > > > > > > > > > > > --- a/Makefile
> > > > > > > > > > > > > > +++ b/Makefile
> > > > > > > > > > > > > > @@ -1151,6 +1151,15 @@ dtbs: dts/dt.dtb
> > > > > > > > > > > > > >  dts/dt.dtb: u-boot
> > > > > > > > > > > > > >         $(Q)$(MAKE) $(build)=dts dtbs
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > +quiet_cmd_mkeficapsule = MKEFICAPSULE $@
> > > > > > > > > > > > > > +cmd_mkeficapsule = $(objtree)/tools/mkeficapsule $@
> > > > > > > > > > > > > > +
> > > > > > > > > > > > > > +PHONY += capsule
> > > > > > > > > > > > > > +capsule: tools
> > > > > > > > > > > > > > +ifneq ($(CONFIG_EFI_CAPSULE_CFG_FILE),"")
> > > > > > > > > > > > > > +       $(call cmd,mkeficapsule)
> > > > > > > > > > > > > > +endif
> > > > > > > > > > > > > > +
> > > > > > > > > > > > > >  quiet_cmd_copy = COPY    $@
> > > > > > > > > > > > > >        cmd_copy = cp $< $@
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > --
> > > > > > > > > > > > > > 2.34.1
> > > > > > > > > > > > > >
> > > > > > > > > > > > >
> > > > > > > > > > > > > We should be using binman to build images...you seem to be building
> > > > > > > > > > > > > something in parallel with that. Can you please take a look at binman?
> > > > > > > > > > > >
> > > > > > > > > > > > Again, I had explored using binman for this task. The one issue where
> > > > > > > > > > > > I find the above flow better is that I can simply build my payload
> > > > > > > > > > > > image(s) followed by 'make capsule' to generate the capsules for
> > > > > > > > > > > > earlier generated images. In it's current form, I don't see an easy
> > > > > > > > > > > > way to enforce this dependency in binman when I want to build the
> > > > > > > > > > > > payload followed by generation of capsules. I did see the mention of
> > > > > > > > > > > > encapsulating an entry within another dependent entry, but I think
> > > > > > > > > > > > that makes the implementation more complex than it ought to be.
> > > > > > > > > > > >
> > > > > > > > > > > > I think it is much easier to use the make flow to generate the images
> > > > > > > > > > > > followed by capsules, instead of tweaking the binman node to first
> > > > > > > > > > > > generate the payload images, followed by enabling the capsule node to
> > > > > > > > > > > > build the capsules. If there is an easy way of enforcing this
> > > > > > > > > > > > dependency, please let me know. Thanks
> > > > > > > > > > >
> > > > > > > > > > > Can you share your explorations? I think the capsule should be created
> > > > > > > > > > > as part of the build, if enabled. Rather than changing the input
> > > > > > > > > > > files, binman should produce new output files.
> > > > > > > > > >
> > > > > > > > > > This is an issue of handling dependencies in binman, and not changing
> > > > > > > > > > input files. We do not have support for telling binman "build/generate
> > > > > > > > > > this particular image first before you proceed to build the capsules
> > > > > > > > > > using the earlier built images". I am not sure if this can be done in
> > > > > > > > > > a generic manner in binman, so that irrespective of the image being
> > > > > > > > > > generated, it can be specified to build capsules once the capsule
> > > > > > > > > > input images have been generated.
> > > > > > > > >
> > > > > > > > > I'm just not sure what you are getting out here.
> > > > > > > > >
> > > > > > > > > See INPUTS-y for the input files to binman. Then binman uses these to
> > > > > > > > > generate output files. It does not mess with the input files, nor
> > > > > > > > > should it. Please read the top part of the Binman motivation to
> > > > > > > > > understand how all this works.
> > > > > > > > >
> > > > > > > > > At the risk of repeating myself, we want the Makefile to focus on
> > > > > > > > > building U-Boot, with Binman handling the laterprocessing of those
> > > > > > > > > files. Binman may run as part of the U-Boot build, and/or can be run
> > > > > > > > > later, with the input files.
> > > > > > > > >
> > > > > > > > > >
> > > > > > > > > > >
> > > > > > > > > > > We are trying to remove most of the output logic in Makefile. It
> > > > > > > > > > > should just be producing input files for binman.
> > > > > > > > > >
> > > > > > > > > > I understand. However, like I mentioned above, as of now, we don't
> > > > > > > > > > have a way of handling dependencies in binman, at least in a generic
> > > > > > > > > > manner. Once this support gets added, I know that it would be trivial
> > > > > > > > > > to add support for building capsules in binman.
> > > > > > > > >
> > > > > > > > > What dependencies do you need? Please describe it in a simple manner
> > > > > > > > > so I can understand. It cannot involve change the binman input files.
> > > > > > > >
> > > > > > > > Consider the following scenarios.
> > > > > > > >
> > > > > > > > One board, say Board A uses fip.bin as the input file(payload) for
> > > > > > > > generating the capsule file. The fip.bin is being generated through
> > > > > > > > binman. A binman entry is also added for generating the capsule(say
> > > > > > > > fip.capule). Now, binman has to generate fip.bin *and subsequently*
> > > > > > > > fip.capsule, as the capsule file will contain fip.bin as it's
> > > > > > > > payload(input).
> > > > > > > >
> > > > > > > > Second Board B, uses u-boot.itb, which is a FIT image, as the input
> > > > > > > > file for generating the capsule. The u-boot.itb is being generated
> > > > > > > > through binman, and so is the capsule. But binman needs to build the
> > > > > > > > u-boot.itb *before* it generates the corresponding capsule file, which
> > > > > > > > uses u-boot.itb as the capsule payload.
> > > > > > > >
> > > > > > > > There can be multiple such examples of input files being generated by
> > > > > > > > binman, followed by the capsule getting generated by binman. How do I
> > > > > > > > specify this dependency in binman -- build/generate the input file
> > > > > > > > first, and then use that files in generating the capsule.
> > > > > >
> > > > > > At present you can do this by ordering the images correctly, i.e. put
> > > > > > the first image first and the dependent image after it. For the
> > > > > > dependent image you can have a blob which is the entire first image.
> > > > >
> > > > > If putting the image entries in a certain order under the binman node
> > > > > *guarantees* the generation of the first image prior to the generation
> > > > > of the second image, I think that should work for my use case.
> > > > > However, when I look at the binman.rst document, I see this mentioned
> > > > > under the 'Image dependencies' section.
> > > > >
> > > > > <quote>
> > > > > Binman does not currently support images that depend on each other. For example,
> > > > > if one image creates `fred.bin` and then the next uses this `fred.bin` to
> > > > > produce a final `image.bin`, then the behaviour is undefined. It may work, or it
> > > > > may produce an error about `fred.bin` being missing, or it may use a version of
> > > > > `fred.bin` from a previous run.
> > > > > </quote>
> > > > >
> > > > > I believe the above is precisely what my use case is. One image
> > > > > generating fip.bin(for e.g.), and the next image using this fip.bin to
> > > > > produce the final fip.capsule. Or is this a case of the document not
> > > > > reflecting the actual code?
> > > >
> > > > So long as the images are in the correct order in the resulting .dtb,
> > > > then it will work. Quite a few boards rely on this.
> > > >
> > > > This comment is there because I planned to implement concurrent image
> > > > generation (as is done for sections). But for now this is not
> > > > implemented. Also I plan to implement templates before parallel
> > > > images, so we can handle dependencies in a more general way.
> > > >
> > > > So if that is the only blocker, I am sorry for the docs being too
> > > > conservative. I will send a patch.
> > > >
> > > > >
> > > > > >
> > > > > > If you are trying to do a second binman operation later, then perhaps
> > > > > > something like 'binman sign' would be useful. Then people can provide
> > > > > > their own key and sign the images in a separate binman operation. This
> > > > > > is likely needed anyway for things to work on a signing server.
> > > > > >
> > > > > > >
> > > > > > > Can you confirm if the above dependencies can be handled in binman
> > > > > > > currently. If not, I'd suggest you remove your Nak for patch 6 of this
> > > > > > > series [1]. Like I mentioned earlier, if there is a means of
> > > > > > > specifying dependencies for generating images in binman, moving the
> > > > > > > capsule generation to binman will not be a difficult task.
> > > > > > >
> > > > > > > Also, can you go through the other set of patches in V2, specifically
> > > > > > > the ones where putting the public key ESL into the dtb is being done
> > > > > > > through binman.
> > > > > >
> > > > > > The order of operation is supposed to be:
> > > > > >
> > > > > > 1. Various projects used to build their ouputs (e.g. TF-A)
> > > > > > 2. Makefile used to build U-Boot:
> > > > > > 2a. The build produces a set of files which serve as inputs to binman (INPUTS-y)
> > > > > > 2b. Binman runs on INPUTS-y, picking up all the bits and creating the
> > > > > > final firmware image(s)
> > > > > > 3. If necessary, separate from the U-Boot build, binman can be used
> > > > > > separately to do signing or whatever is needed on the final image(s)
> > > > > >
> > > > > > I understand that the public key is available in a CONFIG, so it
> > > > > > should be possible to embed it in the build as input, either as a
> > > > > > .dtsi build using 2a, or as a binary file pulled in by binman in 2b.
> > > > >
> > > > > Using a dtsi would mean that every platform which wants to enable
> > > > > capsule authentication would need to add a signature node to it's
> > > > > dtsi. Instead, is it not simpler to just generate a dtbo and merge it
> > > > > with the dtb being generated. That is what was being done in V1 [1].
> > > >
> > > > Why is it simpler? The .dtsi is where we are supposed to put
> > > > devicetree properties. It seems a lot harder (to me) to add it later.
> > > > It could be a #include, or even just put it in the .dtsi if it is the
> > > > same for all boards?
> > >
> > > I was referring to the solution in V1 of the patch series [1]. All
> > > that was needed there was the path to the public key ESL file, which
> > > was being provided through the CONFIG_EFI_CAPSULE_ESL_FILE. The
> > > embed_capsule_key.sh would take care of doing what you are suggesting
> > > to be done via dtsi. When adding a node to the dtsi, the user will
> > > still have to add a node to the dtsi and include the contents of the
> > > ESL file. All that was being automated through the script in V1.
> >
> > Instead of all that complexity, can you check the irc where Kwiboo
> > describes how to add a .dtsi fragment containing that CONFIG
>
> Not sure what is so complex about the script. But yes, I can add a
> dtsi file fragment. Only thing is, it will have to be added under all
> the arch/$ARCH/dts/ directories which would want to support capsule
> updates with authentication. The current logic in Makefile.lib looks
> for the *u-boot.dtsi files under the same dir location as the dts
> being built. But since you prefer having the dtsi file, I will add
> these for the sandbox and arm arch's for now.

OK good.

(the script is presumably not complex, but not having it at all is better)
(yes the dtsi includes are on a per-arch basis)

>
> >
> > BTW the format you are using in the dtb looks to be binary. Have you
> > thought about using real properties and values like the existing
> > public key mechanism? Or is that binary format required by EFI?
>
> Yes, the UEFI spec defines the EFI Signature List(ESL) structure, and
> expects the public key certificate to be in the ESL format. The same
> structure is used for variable authentication as well -- the capsule
> authentication logic basically uses variable authentication
> functionality for signature verification.

It does frustrate me that EFI insists on using ad-hoc binary formats
for things. It seems quite old-fashioned. It would be better to put
things in a node with real properties, etc. That way users can look at
it easily and we don't need so much special parsing code.

>
> >
> > >
> > > >
> > > > > For your suggestion to pull it in as a binary file in binman, that
> > > > > still does not fix the issue of not changing INPUTS-y.
> > > > >
> > > > > If you ask me, the embedding of the public-key into the dtb is not a
> > > > > task suitable for binman. Why? Because this task is actually changing
> > > > > one of the INPUTS-y file that feeds into binman. And yes, we can
> > > > > generate a different set of files, like u-boot-capsule.dtb and
> > > > > u-boot-capsule.bin -- implementing that is not at all difficult. But
> > > > > like I had highlighted earlier, and also explained by Ilias, we
> > > > > already have platforms that use capsule updates and which use
> > > > > u-boot.dtb and u-boot.bin. Also, platforms would not want a separate
> > > >
> > > > Those platforms should change, IMO. But how can this be, when this
> > > > functionality has not yet been added to U-Boot?
> > > >
> > > > > set of files, one for normal boot, one when using capsules. So I think
> > > > > it is imperative that we generate the same set of files irrespective
> > > > > of whether a platform enables capsule updates. So a proper design
> > > > > would be to add/embed the public key into the dtb as the dtb is
> > > > > getting built. Again, this is what was being done in V1.
> > > >
> > > > I completely disagree with this. A capsule update is not the same as
> > > > the vanilla board build / binary. Please can you just give up on this
> > > > idea? Many platforms generate their output in separate files, e.g. see
> > > > u-boot-rockchip.bin - it just does not make sense to change the built
> > > > binary after it is built.
> > >
> > > I completely agree with your last statement. Which is why I said that
> > > if we want to use the same files which are otherwise
> > > generated(u-boot.dtb, u-boot.bin), binman is not the right way to add
> > > the public key to the platform's dtb -- that should be done when the
> > > dtb is being built.
> >
> > OK, so perhaps that is some progress.
> >
> > My second point (perhaps lost above) is that the capsule file should
> > be called u-boot-capsule.bin or something like that, not u-boot.bin,
> > since that is the output from the build system.
>
> I think this point is moot with the public key getting embedded
> through the dtsi file.

Yes for u-boot-dtb since the public key will already be there.

But for the capsule update, it should not modify u-boot.bin but
instead create a new file like u-boot-capsule.bin - right?

Regards,
Simon
[..]

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/7] Makefile: Add a target for building capsules
  2023-06-28 10:19                             ` Simon Glass
@ 2023-06-28 12:25                               ` Sughosh Ganu
  2023-06-28 13:02                                 ` Simon Glass
  0 siblings, 1 reply; 50+ messages in thread
From: Sughosh Ganu @ 2023-06-28 12:25 UTC (permalink / raw)
  To: Simon Glass
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Michal Simek,
	Takahiro Akashi

hi Simon,

On Wed, 28 Jun 2023 at 15:49, Simon Glass <sjg@chromium.org> wrote:
>
> Hi Sughosh,
>
> On Wed, 28 Jun 2023 at 11:00, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> >
> > hi Simon,
> >
> > On Wed, 28 Jun 2023 at 13:12, Simon Glass <sjg@chromium.org> wrote:
> > >
> > > Hi Sughosh,
> > >
> > > On Tue, 27 Jun 2023 at 18:42, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > >
> > > > hi Simon,
> > > >
> > > > On Tue, 27 Jun 2023 at 17:51, Simon Glass <sjg@chromium.org> wrote:
> > > > >
> > > > > Hi Sughosh,
> > > > >
> > > > > On Tue, 27 Jun 2023 at 13:08, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > >
> > > > > > hi Simon,
> > > > > >
> > > > > > On Tue, 27 Jun 2023 at 16:50, Simon Glass <sjg@chromium.org> wrote:
> > > > > > >
> > > > > > > Hi Sughosh,
> > > > > > >
> > > > > > > On Tue, 27 Jun 2023 at 05:57, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > > >
> > > > > > > > hi Simon,
> > > > > > > >
> > > > > > > > On Mon, 26 Jun 2023 at 17:43, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > > > >
> > > > > > > > > hi Simon,
> > > > > > > > >
> > > > > > > > > On Mon, 26 Jun 2023 at 14:38, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > >
> > > > > > > > > > Hi Sughosh,
> > > > > > > > > >
> > > > > > > > > > On Wed, 21 Jun 2023 at 05:26, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > > > > > >
> > > > > > > > > > > hi Simon,
> > > > > > > > > > >
> > > > > > > > > > > On Mon, 19 Jun 2023 at 18:07, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > > > >
> > > > > > > > > > > > Hi Sughosh,
> > > > > > > > > > > >
> > > > > > > > > > > > On Thu, 15 Jun 2023 at 17:25, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > > > > > > > >
> > > > > > > > > > > > > hi Simon,
> > > > > > > > > > > > >
> > > > > > > > > > > > > On Thu, 15 Jun 2023 at 14:44, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > Hi Sughosh,
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > On Tue, 13 Jun 2023 at 11:39, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > Add a target for building EFI capsules. The capsule parameters are
> > > > > > > > > > > > > > > specified through a config file, and the path to the config file is
> > > > > > > > > > > > > > > specified through CONFIG_EFI_CAPSULE_CFG_FILE. When the config file is
> > > > > > > > > > > > > > > not specified, the command only builds tools.
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > > > > > > > > > > > > > > ---
> > > > > > > > > > > > > > >  Makefile | 9 +++++++++
> > > > > > > > > > > > > > >  1 file changed, 9 insertions(+)
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > diff --git a/Makefile b/Makefile
> > > > > > > > > > > > > > > index 10bfaa52ad..96db29aa77 100644
> > > > > > > > > > > > > > > --- a/Makefile
> > > > > > > > > > > > > > > +++ b/Makefile
> > > > > > > > > > > > > > > @@ -1151,6 +1151,15 @@ dtbs: dts/dt.dtb
> > > > > > > > > > > > > > >  dts/dt.dtb: u-boot
> > > > > > > > > > > > > > >         $(Q)$(MAKE) $(build)=dts dtbs
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > +quiet_cmd_mkeficapsule = MKEFICAPSULE $@
> > > > > > > > > > > > > > > +cmd_mkeficapsule = $(objtree)/tools/mkeficapsule $@
> > > > > > > > > > > > > > > +
> > > > > > > > > > > > > > > +PHONY += capsule
> > > > > > > > > > > > > > > +capsule: tools
> > > > > > > > > > > > > > > +ifneq ($(CONFIG_EFI_CAPSULE_CFG_FILE),"")
> > > > > > > > > > > > > > > +       $(call cmd,mkeficapsule)
> > > > > > > > > > > > > > > +endif
> > > > > > > > > > > > > > > +
> > > > > > > > > > > > > > >  quiet_cmd_copy = COPY    $@
> > > > > > > > > > > > > > >        cmd_copy = cp $< $@
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > --
> > > > > > > > > > > > > > > 2.34.1
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > We should be using binman to build images...you seem to be building
> > > > > > > > > > > > > > something in parallel with that. Can you please take a look at binman?
> > > > > > > > > > > > >
> > > > > > > > > > > > > Again, I had explored using binman for this task. The one issue where
> > > > > > > > > > > > > I find the above flow better is that I can simply build my payload
> > > > > > > > > > > > > image(s) followed by 'make capsule' to generate the capsules for
> > > > > > > > > > > > > earlier generated images. In it's current form, I don't see an easy
> > > > > > > > > > > > > way to enforce this dependency in binman when I want to build the
> > > > > > > > > > > > > payload followed by generation of capsules. I did see the mention of
> > > > > > > > > > > > > encapsulating an entry within another dependent entry, but I think
> > > > > > > > > > > > > that makes the implementation more complex than it ought to be.
> > > > > > > > > > > > >
> > > > > > > > > > > > > I think it is much easier to use the make flow to generate the images
> > > > > > > > > > > > > followed by capsules, instead of tweaking the binman node to first
> > > > > > > > > > > > > generate the payload images, followed by enabling the capsule node to
> > > > > > > > > > > > > build the capsules. If there is an easy way of enforcing this
> > > > > > > > > > > > > dependency, please let me know. Thanks
> > > > > > > > > > > >
> > > > > > > > > > > > Can you share your explorations? I think the capsule should be created
> > > > > > > > > > > > as part of the build, if enabled. Rather than changing the input
> > > > > > > > > > > > files, binman should produce new output files.
> > > > > > > > > > >
> > > > > > > > > > > This is an issue of handling dependencies in binman, and not changing
> > > > > > > > > > > input files. We do not have support for telling binman "build/generate
> > > > > > > > > > > this particular image first before you proceed to build the capsules
> > > > > > > > > > > using the earlier built images". I am not sure if this can be done in
> > > > > > > > > > > a generic manner in binman, so that irrespective of the image being
> > > > > > > > > > > generated, it can be specified to build capsules once the capsule
> > > > > > > > > > > input images have been generated.
> > > > > > > > > >
> > > > > > > > > > I'm just not sure what you are getting out here.
> > > > > > > > > >
> > > > > > > > > > See INPUTS-y for the input files to binman. Then binman uses these to
> > > > > > > > > > generate output files. It does not mess with the input files, nor
> > > > > > > > > > should it. Please read the top part of the Binman motivation to
> > > > > > > > > > understand how all this works.
> > > > > > > > > >
> > > > > > > > > > At the risk of repeating myself, we want the Makefile to focus on
> > > > > > > > > > building U-Boot, with Binman handling the laterprocessing of those
> > > > > > > > > > files. Binman may run as part of the U-Boot build, and/or can be run
> > > > > > > > > > later, with the input files.
> > > > > > > > > >
> > > > > > > > > > >
> > > > > > > > > > > >
> > > > > > > > > > > > We are trying to remove most of the output logic in Makefile. It
> > > > > > > > > > > > should just be producing input files for binman.
> > > > > > > > > > >
> > > > > > > > > > > I understand. However, like I mentioned above, as of now, we don't
> > > > > > > > > > > have a way of handling dependencies in binman, at least in a generic
> > > > > > > > > > > manner. Once this support gets added, I know that it would be trivial
> > > > > > > > > > > to add support for building capsules in binman.
> > > > > > > > > >
> > > > > > > > > > What dependencies do you need? Please describe it in a simple manner
> > > > > > > > > > so I can understand. It cannot involve change the binman input files.
> > > > > > > > >
> > > > > > > > > Consider the following scenarios.
> > > > > > > > >
> > > > > > > > > One board, say Board A uses fip.bin as the input file(payload) for
> > > > > > > > > generating the capsule file. The fip.bin is being generated through
> > > > > > > > > binman. A binman entry is also added for generating the capsule(say
> > > > > > > > > fip.capule). Now, binman has to generate fip.bin *and subsequently*
> > > > > > > > > fip.capsule, as the capsule file will contain fip.bin as it's
> > > > > > > > > payload(input).
> > > > > > > > >
> > > > > > > > > Second Board B, uses u-boot.itb, which is a FIT image, as the input
> > > > > > > > > file for generating the capsule. The u-boot.itb is being generated
> > > > > > > > > through binman, and so is the capsule. But binman needs to build the
> > > > > > > > > u-boot.itb *before* it generates the corresponding capsule file, which
> > > > > > > > > uses u-boot.itb as the capsule payload.
> > > > > > > > >
> > > > > > > > > There can be multiple such examples of input files being generated by
> > > > > > > > > binman, followed by the capsule getting generated by binman. How do I
> > > > > > > > > specify this dependency in binman -- build/generate the input file
> > > > > > > > > first, and then use that files in generating the capsule.
> > > > > > >
> > > > > > > At present you can do this by ordering the images correctly, i.e. put
> > > > > > > the first image first and the dependent image after it. For the
> > > > > > > dependent image you can have a blob which is the entire first image.
> > > > > >
> > > > > > If putting the image entries in a certain order under the binman node
> > > > > > *guarantees* the generation of the first image prior to the generation
> > > > > > of the second image, I think that should work for my use case.
> > > > > > However, when I look at the binman.rst document, I see this mentioned
> > > > > > under the 'Image dependencies' section.
> > > > > >
> > > > > > <quote>
> > > > > > Binman does not currently support images that depend on each other. For example,
> > > > > > if one image creates `fred.bin` and then the next uses this `fred.bin` to
> > > > > > produce a final `image.bin`, then the behaviour is undefined. It may work, or it
> > > > > > may produce an error about `fred.bin` being missing, or it may use a version of
> > > > > > `fred.bin` from a previous run.
> > > > > > </quote>
> > > > > >
> > > > > > I believe the above is precisely what my use case is. One image
> > > > > > generating fip.bin(for e.g.), and the next image using this fip.bin to
> > > > > > produce the final fip.capsule. Or is this a case of the document not
> > > > > > reflecting the actual code?
> > > > >
> > > > > So long as the images are in the correct order in the resulting .dtb,
> > > > > then it will work. Quite a few boards rely on this.
> > > > >
> > > > > This comment is there because I planned to implement concurrent image
> > > > > generation (as is done for sections). But for now this is not
> > > > > implemented. Also I plan to implement templates before parallel
> > > > > images, so we can handle dependencies in a more general way.
> > > > >
> > > > > So if that is the only blocker, I am sorry for the docs being too
> > > > > conservative. I will send a patch.
> > > > >
> > > > > >
> > > > > > >
> > > > > > > If you are trying to do a second binman operation later, then perhaps
> > > > > > > something like 'binman sign' would be useful. Then people can provide
> > > > > > > their own key and sign the images in a separate binman operation. This
> > > > > > > is likely needed anyway for things to work on a signing server.
> > > > > > >
> > > > > > > >
> > > > > > > > Can you confirm if the above dependencies can be handled in binman
> > > > > > > > currently. If not, I'd suggest you remove your Nak for patch 6 of this
> > > > > > > > series [1]. Like I mentioned earlier, if there is a means of
> > > > > > > > specifying dependencies for generating images in binman, moving the
> > > > > > > > capsule generation to binman will not be a difficult task.
> > > > > > > >
> > > > > > > > Also, can you go through the other set of patches in V2, specifically
> > > > > > > > the ones where putting the public key ESL into the dtb is being done
> > > > > > > > through binman.
> > > > > > >
> > > > > > > The order of operation is supposed to be:
> > > > > > >
> > > > > > > 1. Various projects used to build their ouputs (e.g. TF-A)
> > > > > > > 2. Makefile used to build U-Boot:
> > > > > > > 2a. The build produces a set of files which serve as inputs to binman (INPUTS-y)
> > > > > > > 2b. Binman runs on INPUTS-y, picking up all the bits and creating the
> > > > > > > final firmware image(s)
> > > > > > > 3. If necessary, separate from the U-Boot build, binman can be used
> > > > > > > separately to do signing or whatever is needed on the final image(s)
> > > > > > >
> > > > > > > I understand that the public key is available in a CONFIG, so it
> > > > > > > should be possible to embed it in the build as input, either as a
> > > > > > > .dtsi build using 2a, or as a binary file pulled in by binman in 2b.
> > > > > >
> > > > > > Using a dtsi would mean that every platform which wants to enable
> > > > > > capsule authentication would need to add a signature node to it's
> > > > > > dtsi. Instead, is it not simpler to just generate a dtbo and merge it
> > > > > > with the dtb being generated. That is what was being done in V1 [1].
> > > > >
> > > > > Why is it simpler? The .dtsi is where we are supposed to put
> > > > > devicetree properties. It seems a lot harder (to me) to add it later.
> > > > > It could be a #include, or even just put it in the .dtsi if it is the
> > > > > same for all boards?
> > > >
> > > > I was referring to the solution in V1 of the patch series [1]. All
> > > > that was needed there was the path to the public key ESL file, which
> > > > was being provided through the CONFIG_EFI_CAPSULE_ESL_FILE. The
> > > > embed_capsule_key.sh would take care of doing what you are suggesting
> > > > to be done via dtsi. When adding a node to the dtsi, the user will
> > > > still have to add a node to the dtsi and include the contents of the
> > > > ESL file. All that was being automated through the script in V1.
> > >
> > > Instead of all that complexity, can you check the irc where Kwiboo
> > > describes how to add a .dtsi fragment containing that CONFIG
> >
> > Not sure what is so complex about the script. But yes, I can add a
> > dtsi file fragment. Only thing is, it will have to be added under all
> > the arch/$ARCH/dts/ directories which would want to support capsule
> > updates with authentication. The current logic in Makefile.lib looks
> > for the *u-boot.dtsi files under the same dir location as the dts
> > being built. But since you prefer having the dtsi file, I will add
> > these for the sandbox and arm arch's for now.
>
> OK good.
>
> (the script is presumably not complex, but not having it at all is better)
> (yes the dtsi includes are on a per-arch basis)
>
> >
> > >
> > > BTW the format you are using in the dtb looks to be binary. Have you
> > > thought about using real properties and values like the existing
> > > public key mechanism? Or is that binary format required by EFI?
> >
> > Yes, the UEFI spec defines the EFI Signature List(ESL) structure, and
> > expects the public key certificate to be in the ESL format. The same
> > structure is used for variable authentication as well -- the capsule
> > authentication logic basically uses variable authentication
> > functionality for signature verification.
>
> It does frustrate me that EFI insists on using ad-hoc binary formats
> for things. It seems quite old-fashioned. It would be better to put
> things in a node with real properties, etc. That way users can look at
> it easily and we don't need so much special parsing code.
>
> >
> > >
> > > >
> > > > >
> > > > > > For your suggestion to pull it in as a binary file in binman, that
> > > > > > still does not fix the issue of not changing INPUTS-y.
> > > > > >
> > > > > > If you ask me, the embedding of the public-key into the dtb is not a
> > > > > > task suitable for binman. Why? Because this task is actually changing
> > > > > > one of the INPUTS-y file that feeds into binman. And yes, we can
> > > > > > generate a different set of files, like u-boot-capsule.dtb and
> > > > > > u-boot-capsule.bin -- implementing that is not at all difficult. But
> > > > > > like I had highlighted earlier, and also explained by Ilias, we
> > > > > > already have platforms that use capsule updates and which use
> > > > > > u-boot.dtb and u-boot.bin. Also, platforms would not want a separate
> > > > >
> > > > > Those platforms should change, IMO. But how can this be, when this
> > > > > functionality has not yet been added to U-Boot?
> > > > >
> > > > > > set of files, one for normal boot, one when using capsules. So I think
> > > > > > it is imperative that we generate the same set of files irrespective
> > > > > > of whether a platform enables capsule updates. So a proper design
> > > > > > would be to add/embed the public key into the dtb as the dtb is
> > > > > > getting built. Again, this is what was being done in V1.
> > > > >
> > > > > I completely disagree with this. A capsule update is not the same as
> > > > > the vanilla board build / binary. Please can you just give up on this
> > > > > idea? Many platforms generate their output in separate files, e.g. see
> > > > > u-boot-rockchip.bin - it just does not make sense to change the built
> > > > > binary after it is built.
> > > >
> > > > I completely agree with your last statement. Which is why I said that
> > > > if we want to use the same files which are otherwise
> > > > generated(u-boot.dtb, u-boot.bin), binman is not the right way to add
> > > > the public key to the platform's dtb -- that should be done when the
> > > > dtb is being built.
> > >
> > > OK, so perhaps that is some progress.
> > >
> > > My second point (perhaps lost above) is that the capsule file should
> > > be called u-boot-capsule.bin or something like that, not u-boot.bin,
> > > since that is the output from the build system.
> >
> > I think this point is moot with the public key getting embedded
> > through the dtsi file.
>
> Yes for u-boot-dtb since the public key will already be there.
>
> But for the capsule update, it should not modify u-boot.bin but
> instead create a new file like u-boot-capsule.bin - right?

For the record, as discussed over irc, the capsule file is not part of
u-boot.bin. It is placed separately on the EFI System Partition(ESP).
I will move the generation of the capsules to binman and the embedding
of the public key ESL to u-boot.dtsi in the next version. Thanks.

-sughosh

>
> Regards,
> Simon
> [..]

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/7] Makefile: Add a target for building capsules
  2023-06-28 12:25                               ` Sughosh Ganu
@ 2023-06-28 13:02                                 ` Simon Glass
  0 siblings, 0 replies; 50+ messages in thread
From: Simon Glass @ 2023-06-28 13:02 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Michal Simek,
	Takahiro Akashi

Hi Sughosh,

On Wed, 28 Jun 2023 at 13:25, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
>
> hi Simon,
>
> On Wed, 28 Jun 2023 at 15:49, Simon Glass <sjg@chromium.org> wrote:
> >
> > Hi Sughosh,
> >
> > On Wed, 28 Jun 2023 at 11:00, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > >
> > > hi Simon,
> > >
> > > On Wed, 28 Jun 2023 at 13:12, Simon Glass <sjg@chromium.org> wrote:
> > > >
> > > > Hi Sughosh,
> > > >
> > > > On Tue, 27 Jun 2023 at 18:42, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > >
> > > > > hi Simon,
> > > > >
> > > > > On Tue, 27 Jun 2023 at 17:51, Simon Glass <sjg@chromium.org> wrote:
> > > > > >
> > > > > > Hi Sughosh,
> > > > > >
> > > > > > On Tue, 27 Jun 2023 at 13:08, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > >
> > > > > > > hi Simon,
> > > > > > >
> > > > > > > On Tue, 27 Jun 2023 at 16:50, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > >
> > > > > > > > Hi Sughosh,
> > > > > > > >
> > > > > > > > On Tue, 27 Jun 2023 at 05:57, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > > > >
> > > > > > > > > hi Simon,
> > > > > > > > >
> > > > > > > > > On Mon, 26 Jun 2023 at 17:43, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > > > > >
> > > > > > > > > > hi Simon,
> > > > > > > > > >
> > > > > > > > > > On Mon, 26 Jun 2023 at 14:38, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > > >
> > > > > > > > > > > Hi Sughosh,
> > > > > > > > > > >
> > > > > > > > > > > On Wed, 21 Jun 2023 at 05:26, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > > > > > > >
> > > > > > > > > > > > hi Simon,
> > > > > > > > > > > >
> > > > > > > > > > > > On Mon, 19 Jun 2023 at 18:07, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > > > > >
> > > > > > > > > > > > > Hi Sughosh,
> > > > > > > > > > > > >
> > > > > > > > > > > > > On Thu, 15 Jun 2023 at 17:25, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > hi Simon,
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > On Thu, 15 Jun 2023 at 14:44, Simon Glass <sjg@chromium.org> wrote:
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > Hi Sughosh,
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > On Tue, 13 Jun 2023 at 11:39, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > Add a target for building EFI capsules. The capsule parameters are
> > > > > > > > > > > > > > > > specified through a config file, and the path to the config file is
> > > > > > > > > > > > > > > > specified through CONFIG_EFI_CAPSULE_CFG_FILE. When the config file is
> > > > > > > > > > > > > > > > not specified, the command only builds tools.
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > > > > > > > > > > > > > > > ---
> > > > > > > > > > > > > > > >  Makefile | 9 +++++++++
> > > > > > > > > > > > > > > >  1 file changed, 9 insertions(+)
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > diff --git a/Makefile b/Makefile
> > > > > > > > > > > > > > > > index 10bfaa52ad..96db29aa77 100644
> > > > > > > > > > > > > > > > --- a/Makefile
> > > > > > > > > > > > > > > > +++ b/Makefile
> > > > > > > > > > > > > > > > @@ -1151,6 +1151,15 @@ dtbs: dts/dt.dtb
> > > > > > > > > > > > > > > >  dts/dt.dtb: u-boot
> > > > > > > > > > > > > > > >         $(Q)$(MAKE) $(build)=dts dtbs
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > +quiet_cmd_mkeficapsule = MKEFICAPSULE $@
> > > > > > > > > > > > > > > > +cmd_mkeficapsule = $(objtree)/tools/mkeficapsule $@
> > > > > > > > > > > > > > > > +
> > > > > > > > > > > > > > > > +PHONY += capsule
> > > > > > > > > > > > > > > > +capsule: tools
> > > > > > > > > > > > > > > > +ifneq ($(CONFIG_EFI_CAPSULE_CFG_FILE),"")
> > > > > > > > > > > > > > > > +       $(call cmd,mkeficapsule)
> > > > > > > > > > > > > > > > +endif
> > > > > > > > > > > > > > > > +
> > > > > > > > > > > > > > > >  quiet_cmd_copy = COPY    $@
> > > > > > > > > > > > > > > >        cmd_copy = cp $< $@
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > --
> > > > > > > > > > > > > > > > 2.34.1
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > We should be using binman to build images...you seem to be building
> > > > > > > > > > > > > > > something in parallel with that. Can you please take a look at binman?
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > Again, I had explored using binman for this task. The one issue where
> > > > > > > > > > > > > > I find the above flow better is that I can simply build my payload
> > > > > > > > > > > > > > image(s) followed by 'make capsule' to generate the capsules for
> > > > > > > > > > > > > > earlier generated images. In it's current form, I don't see an easy
> > > > > > > > > > > > > > way to enforce this dependency in binman when I want to build the
> > > > > > > > > > > > > > payload followed by generation of capsules. I did see the mention of
> > > > > > > > > > > > > > encapsulating an entry within another dependent entry, but I think
> > > > > > > > > > > > > > that makes the implementation more complex than it ought to be.
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > I think it is much easier to use the make flow to generate the images
> > > > > > > > > > > > > > followed by capsules, instead of tweaking the binman node to first
> > > > > > > > > > > > > > generate the payload images, followed by enabling the capsule node to
> > > > > > > > > > > > > > build the capsules. If there is an easy way of enforcing this
> > > > > > > > > > > > > > dependency, please let me know. Thanks
> > > > > > > > > > > > >
> > > > > > > > > > > > > Can you share your explorations? I think the capsule should be created
> > > > > > > > > > > > > as part of the build, if enabled. Rather than changing the input
> > > > > > > > > > > > > files, binman should produce new output files.
> > > > > > > > > > > >
> > > > > > > > > > > > This is an issue of handling dependencies in binman, and not changing
> > > > > > > > > > > > input files. We do not have support for telling binman "build/generate
> > > > > > > > > > > > this particular image first before you proceed to build the capsules
> > > > > > > > > > > > using the earlier built images". I am not sure if this can be done in
> > > > > > > > > > > > a generic manner in binman, so that irrespective of the image being
> > > > > > > > > > > > generated, it can be specified to build capsules once the capsule
> > > > > > > > > > > > input images have been generated.
> > > > > > > > > > >
> > > > > > > > > > > I'm just not sure what you are getting out here.
> > > > > > > > > > >
> > > > > > > > > > > See INPUTS-y for the input files to binman. Then binman uses these to
> > > > > > > > > > > generate output files. It does not mess with the input files, nor
> > > > > > > > > > > should it. Please read the top part of the Binman motivation to
> > > > > > > > > > > understand how all this works.
> > > > > > > > > > >
> > > > > > > > > > > At the risk of repeating myself, we want the Makefile to focus on
> > > > > > > > > > > building U-Boot, with Binman handling the laterprocessing of those
> > > > > > > > > > > files. Binman may run as part of the U-Boot build, and/or can be run
> > > > > > > > > > > later, with the input files.
> > > > > > > > > > >
> > > > > > > > > > > >
> > > > > > > > > > > > >
> > > > > > > > > > > > > We are trying to remove most of the output logic in Makefile. It
> > > > > > > > > > > > > should just be producing input files for binman.
> > > > > > > > > > > >
> > > > > > > > > > > > I understand. However, like I mentioned above, as of now, we don't
> > > > > > > > > > > > have a way of handling dependencies in binman, at least in a generic
> > > > > > > > > > > > manner. Once this support gets added, I know that it would be trivial
> > > > > > > > > > > > to add support for building capsules in binman.
> > > > > > > > > > >
> > > > > > > > > > > What dependencies do you need? Please describe it in a simple manner
> > > > > > > > > > > so I can understand. It cannot involve change the binman input files.
> > > > > > > > > >
> > > > > > > > > > Consider the following scenarios.
> > > > > > > > > >
> > > > > > > > > > One board, say Board A uses fip.bin as the input file(payload) for
> > > > > > > > > > generating the capsule file. The fip.bin is being generated through
> > > > > > > > > > binman. A binman entry is also added for generating the capsule(say
> > > > > > > > > > fip.capule). Now, binman has to generate fip.bin *and subsequently*
> > > > > > > > > > fip.capsule, as the capsule file will contain fip.bin as it's
> > > > > > > > > > payload(input).
> > > > > > > > > >
> > > > > > > > > > Second Board B, uses u-boot.itb, which is a FIT image, as the input
> > > > > > > > > > file for generating the capsule. The u-boot.itb is being generated
> > > > > > > > > > through binman, and so is the capsule. But binman needs to build the
> > > > > > > > > > u-boot.itb *before* it generates the corresponding capsule file, which
> > > > > > > > > > uses u-boot.itb as the capsule payload.
> > > > > > > > > >
> > > > > > > > > > There can be multiple such examples of input files being generated by
> > > > > > > > > > binman, followed by the capsule getting generated by binman. How do I
> > > > > > > > > > specify this dependency in binman -- build/generate the input file
> > > > > > > > > > first, and then use that files in generating the capsule.
> > > > > > > >
> > > > > > > > At present you can do this by ordering the images correctly, i.e. put
> > > > > > > > the first image first and the dependent image after it. For the
> > > > > > > > dependent image you can have a blob which is the entire first image.
> > > > > > >
> > > > > > > If putting the image entries in a certain order under the binman node
> > > > > > > *guarantees* the generation of the first image prior to the generation
> > > > > > > of the second image, I think that should work for my use case.
> > > > > > > However, when I look at the binman.rst document, I see this mentioned
> > > > > > > under the 'Image dependencies' section.
> > > > > > >
> > > > > > > <quote>
> > > > > > > Binman does not currently support images that depend on each other. For example,
> > > > > > > if one image creates `fred.bin` and then the next uses this `fred.bin` to
> > > > > > > produce a final `image.bin`, then the behaviour is undefined. It may work, or it
> > > > > > > may produce an error about `fred.bin` being missing, or it may use a version of
> > > > > > > `fred.bin` from a previous run.
> > > > > > > </quote>
> > > > > > >
> > > > > > > I believe the above is precisely what my use case is. One image
> > > > > > > generating fip.bin(for e.g.), and the next image using this fip.bin to
> > > > > > > produce the final fip.capsule. Or is this a case of the document not
> > > > > > > reflecting the actual code?
> > > > > >
> > > > > > So long as the images are in the correct order in the resulting .dtb,
> > > > > > then it will work. Quite a few boards rely on this.
> > > > > >
> > > > > > This comment is there because I planned to implement concurrent image
> > > > > > generation (as is done for sections). But for now this is not
> > > > > > implemented. Also I plan to implement templates before parallel
> > > > > > images, so we can handle dependencies in a more general way.
> > > > > >
> > > > > > So if that is the only blocker, I am sorry for the docs being too
> > > > > > conservative. I will send a patch.
> > > > > >
> > > > > > >
> > > > > > > >
> > > > > > > > If you are trying to do a second binman operation later, then perhaps
> > > > > > > > something like 'binman sign' would be useful. Then people can provide
> > > > > > > > their own key and sign the images in a separate binman operation. This
> > > > > > > > is likely needed anyway for things to work on a signing server.
> > > > > > > >
> > > > > > > > >
> > > > > > > > > Can you confirm if the above dependencies can be handled in binman
> > > > > > > > > currently. If not, I'd suggest you remove your Nak for patch 6 of this
> > > > > > > > > series [1]. Like I mentioned earlier, if there is a means of
> > > > > > > > > specifying dependencies for generating images in binman, moving the
> > > > > > > > > capsule generation to binman will not be a difficult task.
> > > > > > > > >
> > > > > > > > > Also, can you go through the other set of patches in V2, specifically
> > > > > > > > > the ones where putting the public key ESL into the dtb is being done
> > > > > > > > > through binman.
> > > > > > > >
> > > > > > > > The order of operation is supposed to be:
> > > > > > > >
> > > > > > > > 1. Various projects used to build their ouputs (e.g. TF-A)
> > > > > > > > 2. Makefile used to build U-Boot:
> > > > > > > > 2a. The build produces a set of files which serve as inputs to binman (INPUTS-y)
> > > > > > > > 2b. Binman runs on INPUTS-y, picking up all the bits and creating the
> > > > > > > > final firmware image(s)
> > > > > > > > 3. If necessary, separate from the U-Boot build, binman can be used
> > > > > > > > separately to do signing or whatever is needed on the final image(s)
> > > > > > > >
> > > > > > > > I understand that the public key is available in a CONFIG, so it
> > > > > > > > should be possible to embed it in the build as input, either as a
> > > > > > > > .dtsi build using 2a, or as a binary file pulled in by binman in 2b.
> > > > > > >
> > > > > > > Using a dtsi would mean that every platform which wants to enable
> > > > > > > capsule authentication would need to add a signature node to it's
> > > > > > > dtsi. Instead, is it not simpler to just generate a dtbo and merge it
> > > > > > > with the dtb being generated. That is what was being done in V1 [1].
> > > > > >
> > > > > > Why is it simpler? The .dtsi is where we are supposed to put
> > > > > > devicetree properties. It seems a lot harder (to me) to add it later.
> > > > > > It could be a #include, or even just put it in the .dtsi if it is the
> > > > > > same for all boards?
> > > > >
> > > > > I was referring to the solution in V1 of the patch series [1]. All
> > > > > that was needed there was the path to the public key ESL file, which
> > > > > was being provided through the CONFIG_EFI_CAPSULE_ESL_FILE. The
> > > > > embed_capsule_key.sh would take care of doing what you are suggesting
> > > > > to be done via dtsi. When adding a node to the dtsi, the user will
> > > > > still have to add a node to the dtsi and include the contents of the
> > > > > ESL file. All that was being automated through the script in V1.
> > > >
> > > > Instead of all that complexity, can you check the irc where Kwiboo
> > > > describes how to add a .dtsi fragment containing that CONFIG
> > >
> > > Not sure what is so complex about the script. But yes, I can add a
> > > dtsi file fragment. Only thing is, it will have to be added under all
> > > the arch/$ARCH/dts/ directories which would want to support capsule
> > > updates with authentication. The current logic in Makefile.lib looks
> > > for the *u-boot.dtsi files under the same dir location as the dts
> > > being built. But since you prefer having the dtsi file, I will add
> > > these for the sandbox and arm arch's for now.
> >
> > OK good.
> >
> > (the script is presumably not complex, but not having it at all is better)
> > (yes the dtsi includes are on a per-arch basis)
> >
> > >
> > > >
> > > > BTW the format you are using in the dtb looks to be binary. Have you
> > > > thought about using real properties and values like the existing
> > > > public key mechanism? Or is that binary format required by EFI?
> > >
> > > Yes, the UEFI spec defines the EFI Signature List(ESL) structure, and
> > > expects the public key certificate to be in the ESL format. The same
> > > structure is used for variable authentication as well -- the capsule
> > > authentication logic basically uses variable authentication
> > > functionality for signature verification.
> >
> > It does frustrate me that EFI insists on using ad-hoc binary formats
> > for things. It seems quite old-fashioned. It would be better to put
> > things in a node with real properties, etc. That way users can look at
> > it easily and we don't need so much special parsing code.
> >
> > >
> > > >
> > > > >
> > > > > >
> > > > > > > For your suggestion to pull it in as a binary file in binman, that
> > > > > > > still does not fix the issue of not changing INPUTS-y.
> > > > > > >
> > > > > > > If you ask me, the embedding of the public-key into the dtb is not a
> > > > > > > task suitable for binman. Why? Because this task is actually changing
> > > > > > > one of the INPUTS-y file that feeds into binman. And yes, we can
> > > > > > > generate a different set of files, like u-boot-capsule.dtb and
> > > > > > > u-boot-capsule.bin -- implementing that is not at all difficult. But
> > > > > > > like I had highlighted earlier, and also explained by Ilias, we
> > > > > > > already have platforms that use capsule updates and which use
> > > > > > > u-boot.dtb and u-boot.bin. Also, platforms would not want a separate
> > > > > >
> > > > > > Those platforms should change, IMO. But how can this be, when this
> > > > > > functionality has not yet been added to U-Boot?
> > > > > >
> > > > > > > set of files, one for normal boot, one when using capsules. So I think
> > > > > > > it is imperative that we generate the same set of files irrespective
> > > > > > > of whether a platform enables capsule updates. So a proper design
> > > > > > > would be to add/embed the public key into the dtb as the dtb is
> > > > > > > getting built. Again, this is what was being done in V1.
> > > > > >
> > > > > > I completely disagree with this. A capsule update is not the same as
> > > > > > the vanilla board build / binary. Please can you just give up on this
> > > > > > idea? Many platforms generate their output in separate files, e.g. see
> > > > > > u-boot-rockchip.bin - it just does not make sense to change the built
> > > > > > binary after it is built.
> > > > >
> > > > > I completely agree with your last statement. Which is why I said that
> > > > > if we want to use the same files which are otherwise
> > > > > generated(u-boot.dtb, u-boot.bin), binman is not the right way to add
> > > > > the public key to the platform's dtb -- that should be done when the
> > > > > dtb is being built.
> > > >
> > > > OK, so perhaps that is some progress.
> > > >
> > > > My second point (perhaps lost above) is that the capsule file should
> > > > be called u-boot-capsule.bin or something like that, not u-boot.bin,
> > > > since that is the output from the build system.
> > >
> > > I think this point is moot with the public key getting embedded
> > > through the dtsi file.
> >
> > Yes for u-boot-dtb since the public key will already be there.
> >
> > But for the capsule update, it should not modify u-boot.bin but
> > instead create a new file like u-boot-capsule.bin - right?
>
> For the record, as discussed over irc, the capsule file is not part of
> u-boot.bin. It is placed separately on the EFI System Partition(ESP).
> I will move the generation of the capsules to binman and the embedding
> of the public key ESL to u-boot.dtsi in the next version. Thanks.

Yes that sounds right, thanks.

Regards,
Simon

^ permalink raw reply	[flat|nested] 50+ messages in thread

end of thread, other threads:[~2023-06-28 13:02 UTC | newest]

Thread overview: 50+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-06-13 10:37 [PATCH 0/7] Integrate EFI capsule tasks into u-boot's build flow Sughosh Ganu
2023-06-13 10:38 ` [PATCH 1/7] capsule: authenticate: Embed capsule public key in platform's dtb Sughosh Ganu
2023-06-15  9:14   ` Simon Glass
2023-06-15 16:11     ` Sughosh Ganu
2023-06-19 12:36       ` Simon Glass
2023-06-21  4:20         ` Sughosh Ganu
2023-06-26  9:07           ` Simon Glass
2023-06-26  9:53             ` Ilias Apalodimas
2023-06-26 11:19               ` Simon Glass
2023-06-27  9:54                 ` Ilias Apalodimas
2023-06-27 10:13                   ` Simon Glass
2023-06-27 10:20                     ` Sughosh Ganu
2023-06-13 10:38 ` [PATCH 2/7] test: py: Generate capsule keys prior to building u-boot Sughosh Ganu
2023-06-13 10:38 ` [PATCH 3/7] doc: capsule: Document the new mechanism to embed ESL file into dtb Sughosh Ganu
2023-06-15  9:14   ` Simon Glass
2023-06-13 10:38 ` [PATCH 4/7] tools: mkeficapsule: Add support for parsing capsule params from config file Sughosh Ganu
2023-06-14  3:39   ` Takahiro Akashi
2023-06-14  5:26     ` Sughosh Ganu
2023-06-14  5:53       ` Takahiro Akashi
2023-06-15  4:39         ` Sughosh Ganu
2023-06-15  5:49           ` Takahiro Akashi
2023-06-16  4:26             ` Sughosh Ganu
2023-06-16  4:46               ` Takahiro Akashi
2023-06-16  5:07                 ` Sughosh Ganu
2023-06-16  5:18                   ` Takahiro Akashi
2023-06-16  6:35                     ` Sughosh Ganu
2023-06-16 13:12                       ` Schmidt, Malte
2023-06-19  7:27                         ` Sughosh Ganu
2023-06-19  9:42                           ` Schmidt, Malte
2023-06-13 10:38 ` [PATCH 5/7] Makefile: Add a target for building capsules Sughosh Ganu
2023-06-15  9:14   ` Simon Glass
2023-06-15 16:25     ` Sughosh Ganu
2023-06-19 12:37       ` Simon Glass
2023-06-21  4:26         ` Sughosh Ganu
2023-06-26  9:07           ` Simon Glass
2023-06-26 12:13             ` Sughosh Ganu
2023-06-27  4:57               ` Sughosh Ganu
2023-06-27 11:20                 ` Simon Glass
2023-06-27 12:07                   ` Sughosh Ganu
2023-06-27 12:20                     ` Simon Glass
2023-06-27 17:42                       ` Sughosh Ganu
2023-06-28  7:42                         ` Simon Glass
2023-06-28  8:42                           ` Ilias Apalodimas
2023-06-28 10:00                           ` Sughosh Ganu
2023-06-28 10:19                             ` Simon Glass
2023-06-28 12:25                               ` Sughosh Ganu
2023-06-28 13:02                                 ` Simon Glass
2023-06-13 10:38 ` [PATCH 6/7] test: efi_capsule: Test capsule generation from config file Sughosh Ganu
2023-06-15  9:14   ` Simon Glass
2023-06-13 10:38 ` [PATCH 7/7] doc: Add documentation to describe capsule config file format Sughosh Ganu

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.