@@ -55,14 +55,39 @@ python() {
"UEFI_FIRMWARE_BINARIES", \
"UEFI_CAPSULE_CONFIG_GENERATOR_SCRIPT", \
"CAPSULE_ALL_COMPONENTS", \
- "CAPSULE_SELECTED_COMPONENTS", \
- "PAYLOAD_CERTIFICATE_PATH", \
- "PAYLOAD_PRIVATE_KEY_PATH"]:
+ "CAPSULE_SELECTED_COMPONENTS"]:
if not d.getVar(var):
raise bb.parse.SkipRecipe(f"{var} not set")
}
IMAGE_CMD:uefi_capsule(){
+ signing_pem_dir="${WORKDIR}/uefi-capsule-signing-pems"
+ rm -rf "$signing_pem_dir"
+ install -d -m 0700 "$signing_pem_dir"
+
+ set -- ${CAPSULE_CERTIFICATE_PATHS}
+ signing_pem_paths=""
+ signing_pem_index=0
+ for private_key_path in ${CAPSULE_PRIVATE_KEY_PATHS}; do
+ if [ "$#" -eq 0 ]; then
+ bbfatal "CAPSULE_PRIVATE_KEY_PATHS has more entries than CAPSULE_CERTIFICATE_PATHS"
+ fi
+
+ certificate_path="$1"
+ shift
+
+ signing_pem_path="$signing_pem_dir/$signing_pem_index.pem"
+ install -m 0600 "$private_key_path" "$signing_pem_path"
+ cat "$certificate_path" >> "$signing_pem_path"
+
+ signing_pem_paths="$signing_pem_paths $signing_pem_path"
+ signing_pem_index="$(expr "$signing_pem_index" + 1)"
+ done
+
+ if [ "$#" -ne 0 ]; then
+ bbfatal "CAPSULE_CERTIFICATE_PATHS has more entries than CAPSULE_PRIVATE_KEY_PATHS"
+ fi
+
# Generates the UEFI capsule payloads JSON
${PYTHON} ${UEFI_CAPSULE_CONFIG_GENERATOR_SCRIPT} \
--selected_components ${CAPSULE_SELECTED_COMPONENTS}\
@@ -74,16 +99,13 @@ IMAGE_CMD:uefi_capsule(){
--monotonic_counts ${CAPSULE_MONOTONIC_COUNTS} \
--payloads ${UEFI_FIRMWARE_BINARIES} \
--update_image_indexes ${CAPSULE_INDEXES} \
- --private_keys ${CAPSULE_PRIVATE_KEY_PATHS} \
+ --signing_pems $signing_pem_paths \
--certificates ${CAPSULE_CERTIFICATE_PATHS} \
--output ${CAPSULE_CONFIG_FILE}
# Force the GenerateCapsule script to use python3
export PYTHON_COMMAND=${PYTHON}
- # Append the certificate to the private key to create a PEM bundle compatible with EDK2 tools
- cat ${PAYLOAD_CERTIFICATE_PATH} >> ${PAYLOAD_PRIVATE_KEY_PATH}
-
# Generate the UEFI capsule image using the EDK2 GenerateCapsule tool
${STAGING_BINDIR_NATIVE}/edk2-BaseTools/BinWrappers/PosixLike/GenerateCapsule \
-e -j ${CAPSULE_CONFIG_FILE} \
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: <text>Copyright 2025 Arm Limited and/or its
+# SPDX-FileCopyrightText: <text>Copyright 2025-2026 Arm Limited and/or its
# affiliates <open-source-office@arm.com></text>
#
# SPDX-License-Identifier: MIT
@@ -19,7 +19,7 @@ Usage:
--monotonic_counts 1 1 1 \
--payloads bl2.bin initramfs.bin tfm_s.bin \
--update_image_indexes 1 4 2 \
- --private_keys key.key key.key key.key \
+ --signing_pems signing.pem signing.pem signing.pem \
--certificates cert.crt cert.crt cert.crt \
--components bl2 initramfs tfm_s \
--selected_components bl2 \
@@ -50,6 +50,17 @@ def parse_arguments() -> argparse.Namespace:
"--output", default="capsule_payloads.json", help="Output JSON file name"
)
+ parser.add_argument(
+ "--signing_pems", "--private_keys",
+ dest="signing_pems",
+ nargs="+",
+ required=True,
+ help=(
+ "List of signing PEM file paths (private key and certificate). "
+ "--private_keys is kept as a compatibility alias."
+ ),
+ )
+
# Required arguments for each payload entry
required_args = {
"components": "List of components",
@@ -60,7 +71,6 @@ def parse_arguments() -> argparse.Namespace:
"monotonic_counts": "List of monotonic counts",
"payloads": "List of payload file paths",
"update_image_indexes": "List of update image indexes",
- "private_keys": "List of private key file paths",
"certificates": "List of certificate file paths",
}
@@ -105,7 +115,7 @@ def create_payloads(args: argparse.Namespace) -> List[dict]:
"MonotonicCount": args.monotonic_counts[i],
"Payload": args.payloads[i],
"UpdateImageIndex": args.update_image_indexes[i],
- "OpenSslSignerPrivateCertFile": args.private_keys[i],
+ "OpenSslSignerPrivateCertFile": args.signing_pems[i],
"OpenSslTrustedPublicCertFile": args.certificates[i],
"OpenSslOtherPublicCertFile": args.certificates[i],
}
GenerateCapsule expects a PEM that contains both the private key and the matching certificate. The current capsule flow appends the certificate to PAYLOAD_PRIVATE_KEY_PATH in place, which mutates the deployed key file and adds another certificate block every time do_image_uefi_capsule runs. Prepare per-component signing PEMs under WORKDIR instead and pass those files to the capsule JSON generator. This keeps the configured private key unchanged, makes repeated capsule builds idempotent, and provides GenerateCapsule with the combined key and certificate input it requires. Rename the helper's primary signing input from --private_keys to --signing_pems to reflect the actual input format, while keeping --private_keys as a compatibility alias. Generating the signing PEMs per component also allows each capsule payload to use its own private key and matching certificate instead of relying on a single mutated key file. Use expr for the signing PEM counter because the BitBake shell parser does not support arithmetic expansion. Signed-off-by: Gianluca Andreotti <gianluca.andreotti@arm.com> --- meta-arm/classes/uefi_capsule.bbclass | 36 +++++++++++++++---- .../scripts/generate_capsule_json_multiple.py | 18 +++++++--- 2 files changed, 43 insertions(+), 11 deletions(-)