From patchwork Tue Aug 1 17:40:15 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sughosh Ganu X-Patchwork-Id: 708743 Delivered-To: patch@linaro.org Received: by 2002:a5d:55c5:0:b0:317:2194:b2bc with SMTP id i5csp473162wrw; Tue, 1 Aug 2023 10:41:47 -0700 (PDT) X-Google-Smtp-Source: APBJJlFd/uil7EZRmcegVDD5akmB8I+8pgIrpbzDD3yj6GoEdpGdTRU+tcCttOQXNySkN9F1p12q X-Received: by 2002:adf:f7cd:0:b0:313:f7a1:3d92 with SMTP id a13-20020adff7cd000000b00313f7a13d92mr2927082wrq.66.1690911707027; Tue, 01 Aug 2023 10:41:47 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1690911707; cv=none; d=google.com; s=arc-20160816; b=Lk3CwqI22Kz/fGOV2bvoEZmGPdhrf0t0BE/5V6Bu1sqk/eUfjkznVy/NgM6w6mgiUz gedVsP09XCPiL0mCq5o5sEkDh2U1N1gm3fT1V57fAYwgZcZpdKL8ghEIOB0+ENG5wlmo g5NDtL/V/erFXUhKYh2ItDXN+yXna/kL+O7DwXCXFJFtMcxZngQ69ivaN09Fgm8HdZxL ivKxd/U1xx8xOFNEAEeQIywYj/46LuQcOY8MIxHc+p4RwL38be2Nxd+hilIuOETibqGm fiIymbpzXCTd5JRhpxWpMYyL59/QcB3XpkScqaQB6TCXQ5Tyq8m0uN0y8H2F602wbFAP ga1A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:content-transfer-encoding :mime-version:references:in-reply-to:message-id:date:subject:cc:to :from; bh=n6KdIL2BC1y02aEjnF6d2OyxyIQCCvs/hhBqnA+fRy0=; fh=pvWoYJ8DnC9IyTD46q2s71hlWpPSBdE/YYp0/eyIYcc=; b=eP+E+cakglRZl9Lld4PrhlepC14mIdcQjzYqTzEAqLRizVoIaV7OUdPBy+fQ3sa5p/ 64shUHhU78LSbQ9lt2BFAZy1opeYoFor4+ZbFqfP+fn7jT3PjxZ//jCKQx85tjtfGUDN HBrjmcVVYdnLdBO37qm9oSI79e0lSuCJVq+ECgu2tiwi4vU5DEjMmIGpvH62yZaz30BD AlW6sN4asITONqOWzQ9i205wH/FYtANRWkJihmfF2RuCiaAAi4ekJ89hpe3zHpdh4NXp 1Oa2klPGMSDfGE2P9+44x/oPH/jy9YnSWUmiReFDDV39ZIDbAcyfxgfg9xK1f5xXrBZt Jqyg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of u-boot-bounces@lists.denx.de designates 85.214.62.61 as permitted sender) smtp.mailfrom=u-boot-bounces@lists.denx.de; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from phobos.denx.de (phobos.denx.de. [85.214.62.61]) by mx.google.com with ESMTPS id f2-20020a7bc8c2000000b003fe13fe4e73si4694061wml.201.2023.08.01.10.41.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 01 Aug 2023 10:41:46 -0700 (PDT) Received-SPF: pass (google.com: domain of u-boot-bounces@lists.denx.de designates 85.214.62.61 as permitted sender) client-ip=85.214.62.61; Authentication-Results: mx.google.com; spf=pass (google.com: domain of u-boot-bounces@lists.denx.de designates 85.214.62.61 as permitted sender) smtp.mailfrom=u-boot-bounces@lists.denx.de; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id AE55886CBD; Tue, 1 Aug 2023 19:41:03 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=fail (p=none dis=none) header.from=linaro.org Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Received: by phobos.denx.de (Postfix, from userid 109) id 5946C86C96; Tue, 1 Aug 2023 19:41:02 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-1.2 required=5.0 tests=BAYES_00,SPF_HELO_NONE, SPF_SOFTFAIL,T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=3.4.2 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by phobos.denx.de (Postfix) with ESMTP id 611A086C9A for ; Tue, 1 Aug 2023 19:40:58 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=fail (p=none dis=none) header.from=linaro.org Authentication-Results: phobos.denx.de; spf=fail smtp.mailfrom=sughosh.ganu@linaro.org Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 67D36D75; Tue, 1 Aug 2023 10:41:41 -0700 (PDT) Received: from a076522.blr.arm.com (unknown [10.162.46.7]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id D173B3F59C; Tue, 1 Aug 2023 10:40:54 -0700 (PDT) From: Sughosh Ganu To: u-boot@lists.denx.de Cc: Heinrich Schuchardt , Ilias Apalodimas , Simon Glass , Takahiro Akashi , Malte Schmidt , Michal Simek , Tom Rini , Sughosh Ganu Subject: [PATCH v6 6/9] binman: capsule: Add support for generating EFI capsules Date: Tue, 1 Aug 2023 23:10:15 +0530 Message-Id: <20230801174018.1342555-7-sughosh.ganu@linaro.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230801174018.1342555-1-sughosh.ganu@linaro.org> References: <20230801174018.1342555-1-sughosh.ganu@linaro.org> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.8 at phobos.denx.de X-Virus-Status: Clean Add support in binman for generating EFI capsules. The capsule parameters can be specified through the capsule binman entry. Also add test cases in binman for testing capsule generation. Signed-off-by: Sughosh Ganu --- Changes since V5: * Add support for the oemflag parameter used in FWU A/B updates. This was missed in the earlier version. * Use a single function, generate_capsule in the mkeficapsule bintool, instead of the multiple functions in earlier version. * Remove the logic for generating capsules from config file as suggested by Simon. * Use required_props for image index and GUID parameters. * Use a subnode for the capsule payload instead of using a filename for the payload, as suggested by Simon. * Add a capsule generation test with oemflag parameter being passed. configs/sandbox_spl_defconfig | 1 + tools/binman/btool/mkeficapsule.py | 101 +++++++++++ tools/binman/entries.rst | 64 +++++++ tools/binman/etype/efi_capsule.py | 160 ++++++++++++++++++ tools/binman/ftest.py | 122 +++++++++++++ tools/binman/test/307_capsule.dts | 21 +++ tools/binman/test/308_capsule_signed.dts | 23 +++ tools/binman/test/309_capsule_version.dts | 22 +++ tools/binman/test/310_capsule_signed_ver.dts | 24 +++ tools/binman/test/311_capsule_oemflags.dts | 22 +++ tools/binman/test/312_capsule_missing_key.dts | 22 +++ .../binman/test/313_capsule_missing_index.dts | 20 +++ .../binman/test/314_capsule_missing_guid.dts | 19 +++ .../test/315_capsule_missing_payload.dts | 17 ++ 14 files changed, 638 insertions(+) create mode 100644 tools/binman/btool/mkeficapsule.py create mode 100644 tools/binman/etype/efi_capsule.py create mode 100644 tools/binman/test/307_capsule.dts create mode 100644 tools/binman/test/308_capsule_signed.dts create mode 100644 tools/binman/test/309_capsule_version.dts create mode 100644 tools/binman/test/310_capsule_signed_ver.dts create mode 100644 tools/binman/test/311_capsule_oemflags.dts create mode 100644 tools/binman/test/312_capsule_missing_key.dts create mode 100644 tools/binman/test/313_capsule_missing_index.dts create mode 100644 tools/binman/test/314_capsule_missing_guid.dts create mode 100644 tools/binman/test/315_capsule_missing_payload.dts diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig index 8d50162b27..65223475ab 100644 --- a/configs/sandbox_spl_defconfig +++ b/configs/sandbox_spl_defconfig @@ -249,3 +249,4 @@ CONFIG_UNIT_TEST=y CONFIG_SPL_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_TOOLS_MKEFICAPSULE=y diff --git a/tools/binman/btool/mkeficapsule.py b/tools/binman/btool/mkeficapsule.py new file mode 100644 index 0000000000..da1d5de873 --- /dev/null +++ b/tools/binman/btool/mkeficapsule.py @@ -0,0 +1,101 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2023 Linaro Limited +# +"""Bintool implementation for mkeficapsule tool + +mkeficapsule is a tool used for generating EFI capsules. + +The following are the command-line options to be provided +to the tool +Usage: mkeficapsule [options] +Options: + -g, --guid guid for image blob type + -i, --index update image index + -I, --instance update hardware instance + -v, --fw-version firmware version + -p, --private-key private key file + -c, --certificate signer's certificate file + -m, --monotonic-count monotonic count + -d, --dump_sig dump signature (*.p7) + -A, --fw-accept firmware accept capsule, requires GUID, no image blob + -R, --fw-revert firmware revert capsule, takes no GUID, no image blob + -o, --capoemflag Capsule OEM Flag, an integer between 0x0000 and 0xffff + -h, --help print a help message +""" + +from binman import bintool + +class Bintoolmkeficapsule(bintool.Bintool): + """Handles the 'mkeficapsule' tool + + This bintool is used for generating the EFI capsules. The + capsule generation parameters can either be specified through + command-line, or through a config file. + """ + def __init__(self, name): + super().__init__(name, 'mkeficapsule tool for generating capsules') + + def generate_capsule(self, image_index, image_guid, hardware_instance, + payload, output_fname, priv_key, pub_key, + monotonic_count=0, version=0, oemflags=0): + """Generate a capsule through commandline provided parameters + + Args: + image_index (int): Unique number for identifying payload image + image_guid (str): GUID used for identifying the image + hardware_instance (int): Optional unique hardware instance of + a device in the system. 0 if not being used + payload (str): Path to the input payload image + output_fname (str): Path to the output capsule file + priv_key (str): Path to the private key + pub_key(str): Path to the public key + monotonic_count (int): Count used when signing an image + version (int): Image version (Optional) + oemflags (int): Optional 16 bit OEM flags + + Returns: + str: Tool output + """ + args = [ + f'--index={image_index}', + f'--guid={image_guid}', + f'--instance={hardware_instance}' + ] + + if version: + args += [f'--fw-version={version}'] + if oemflags: + args += [f'--capoemflag={oemflags}'] + if priv_key and pub_key: + args += [ + f'--monotonic-count={monotonic_count}', + f'--private-key={priv_key}', + f'--certificate={pub_key}' + ] + + args += [ + payload, + output_fname + ] + + return self.run_cmd(*args) + + def fetch(self, method): + """Fetch handler for mkeficapsule + + This builds the tool from source + + Returns: + tuple: + str: Filename of fetched file to copy to a suitable directory + str: Name of temp directory to remove, or None + """ + if method != bintool.FETCH_BUILD: + return None + + cmd = ['tools-only_defconfig', 'tools'] + result = self.build_from_git( + 'https://source.denx.de/u-boot/u-boot.git', + cmd, + 'tools/mkeficapsule') + return result diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index f2376932be..cfe699361c 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -468,6 +468,70 @@ updating the EC on startup via software sync. +.. _etype_efi_capsule: + +Entry: capsule: Entry for generating EFI Capsule files +------------------------------------------------------ + +This is an entry for generating EFI capsules. + + This is an entry for generating EFI capsules. + + The parameters needed for generation of the capsules can + either be provided as properties in the entry. + +Properties / Entry arguments: + - image-index: Unique number for identifying corresponding + payload image. Number between 1 and descriptor count, i.e. + the total number of firmware images that can be updated. + - image-type-id: Image GUID which will be used for identifying the + updatable image on the board. + - hardware-instance: Optional number for identifying unique + hardware instance of a device in the system. Default value of 0 + for images where value is not to be used. + - fw-version: Optional value of image version that can be put on + the capsule through the Firmware Management Protocol(FMP) header. + - monotonic-count: Count used when signing an image. + - private-key: Path to PEM formatted .key private key file. + - pub-key-cert: Path to PEM formatted .crt public key certificate + file. + - capoemflags - Optional OEM flags to be passed through capsule header. + - filename: Optional path to the output capsule file. A capsule is a + continuous set of data as defined by the EFI specification. Refer + to the specification for more details. + + For more details on the description of the capsule format, and the capsule + update functionality, refer Section 8.5 and Chapter 23 in the UEFI + specification. + https://uefi.org/sites/default/files/resources/UEFI_Spec_2_10_Aug29.pdf + + The capsule parameters like image index and image GUID are passed as + properties in the entry. The payload to be used in the capsule is to be + provided as a subnode of the capsule entry. + +A typical capsule entry node would then look something like this:: + + capsule { + type = "efi-capsule"; + image-index = <0x1>; + /* Image GUID for testing capsule update */ + image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8"; + hardware-instance = <0x0>; + private-key = "tools/binman/test/key.key"; + pub-key-cert = "tools/binman/test/key.pem"; + filename = "test.capsule"; + + u-boot { + }; + }; + +In the above example, the capsule payload is the u-boot image. The +capsule entry would read the contents of the payload and put them +into the capsule. Any external file can also be specified as the +payload using the blob-ext subnode. + + + .. _etype_encrypted: Entry: encrypted: Externally built encrypted binary blob diff --git a/tools/binman/etype/efi_capsule.py b/tools/binman/etype/efi_capsule.py new file mode 100644 index 0000000000..b8a269e247 --- /dev/null +++ b/tools/binman/etype/efi_capsule.py @@ -0,0 +1,160 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2023 Linaro Limited +# +# Entry-type module for producing a EFI capsule +# + +from collections import OrderedDict +import os + +from binman.entry import Entry +from binman.etype.section import Entry_section +from dtoc import fdt_util +from u_boot_pylib import tools + +class Entry_efi_capsule(Entry_section): + """Entry for generating EFI capsules + + This is an entry for generating EFI capsules. + + The parameters needed for generation of the capsules can + either be provided as properties in the entry. + + Properties / Entry arguments: + - image-index: Unique number for identifying corresponding + payload image. Number between 1 and descriptor count, i.e. + the total number of firmware images that can be updated. + - image-type-id: Image GUID which will be used for identifying the + updatable image on the board. + - hardware-instance: Optional number for identifying unique + hardware instance of a device in the system. Default value of 0 + for images where value is not to be used. + - fw-version: Optional value of image version that can be put on + the capsule through the Firmware Management Protocol(FMP) header. + - monotonic-count: Count used when signing an image. + - private-key: Path to PEM formatted .key private key file. + - pub-key-cert: Path to PEM formatted .crt public key certificate + file. + - capoemflags - Optional OEM flags to be passed through capsule header. + - filename: Optional path to the output capsule file. A capsule is a + continuous set of data as defined by the EFI specification. Refer + to the specification for more details. + + For more details on the description of the capsule format, and the capsule + update functionality, refer Section 8.5 and Chapter 23 in the UEFI + specification. + https://uefi.org/sites/default/files/resources/UEFI_Spec_2_10_Aug29.pdf + + The capsule parameters like image index and image GUID are passed as + properties in the entry. The payload to be used in the capsule is to be + provided as a subnode of the capsule entry. + + A typical capsule entry node would then look something like this + + capsule { + type = "efi-capsule"; + image-index = <0x1>; + /* Image GUID for testing capsule update */ + image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8"; + hardware-instance = <0x0>; + private-key = "tools/binman/test/key.key"; + pub-key-cert = "tools/binman/test/key.pem"; + capoemflags = <0x8000>; + + u-boot { + }; + }; + + In the above example, the capsule payload is the u-boot image. The + capsule entry would read the contents of the payload and put them + into the capsule. Any external file can also be specified as the + payload using the blob-ext subnode. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + self.required_props = ['image-index', 'image-type-id'] + self.image_index = 0 + self.image_guid = '' + self.hardware_instance = 0 + self.monotonic_count = 0 + self.fw_version = 0 + self.capoemflags = 0 + self.private_key = '' + self.pub_key_cert = '' + self.auth = 0 + self.capsule_fname = '' + self._entries = OrderedDict() + + def ReadNode(self): + self.ReadEntries() + super().ReadNode() + + self.image_index = fdt_util.GetInt(self._node, 'image-index') + self.image_guid = fdt_util.GetString(self._node, 'image-type-id') + self.fw_version = fdt_util.GetInt(self._node, 'fw-version') + self.hardware_instance = fdt_util.GetInt(self._node, 'hardware-instance') + self.monotonic_count = fdt_util.GetInt(self._node, 'monotonic-count') + self.capoemflags = fdt_util.GetInt(self._node, 'capoemflags') + + self.private_key = fdt_util.GetString(self._node, 'private-key') + self.pub_key_cert = fdt_util.GetString(self._node, 'pub-key-cert') + if ((self.private_key and not self.pub_key_cert) or (self.pub_key_cert and not self.private_key)): + self.Raise('Both private key and public key certificate need to be provided') + elif not (self.private_key and self.pub_key_cert): + self.auth = 0 + else: + self.auth = 1 + + def ReadEntries(self): + """Read the subnode to get the payload for this capsule""" + # We can have a single payload per capsule + if len(self._node.subnodes) != 1: + self.Raise('The capsule entry expects a single subnode for payload') + + for node in self._node.subnodes: + entry = Entry.Create(self, node) + entry.ReadNode() + self._entries[entry.name] = entry + + def _GenCapsule(self): + return self.mkeficapsule.generate_capsule(self.image_index, + self.image_guid, + self.hardware_instance, + self.payload, + self.capsule_fname, + self.private_key, + self.pub_key_cert, + self.monotonic_count, + self.fw_version, + self.capoemflags) + + def BuildSectionData(self, required): + if self.auth: + if not os.path.isabs(self.private_key): + self.private_key = tools.get_input_filename(self.private_key) + if not os.path.isabs(self.pub_key_cert): + self.pub_key_cert = tools.get_input_filename(self.pub_key_cert) + data, self.payload, _ = self.collect_contents_to_file( + self._entries.values(), 'capsule_payload') + outfile = self._filename if self._filename else self._node.name + self.capsule_fname = tools.get_output_filename(outfile) + if self._GenCapsule() is not None: + os.remove(self.payload) + return tools.read_file(self.capsule_fname) + + def ProcessContents(self): + ok = super().ProcessContents() + data = self.BuildSectionData(True) + ok2 = self.ProcessContentsUpdate(data) + return ok and ok2 + + def SetImagePos(self, image_pos): + upto = 0 + for entry in self.GetEntries().values(): + entry.SetOffsetSize(upto, None) + upto += entry.size + + super().SetImagePos(image_pos) + + def AddBintools(self, btools): + self.mkeficapsule = self.AddBintool(btools, 'mkeficapsule') diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 1cfa349d38..7e7926b607 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -48,6 +48,7 @@ U_BOOT_VPL_DATA = b'vpl76543210fedcbazywxyz_' BLOB_DATA = b'89' ME_DATA = b'0abcd' VGA_DATA = b'vga' +EFI_CAPSULE_DATA = b'efi' U_BOOT_DTB_DATA = b'udtb' U_BOOT_SPL_DTB_DATA = b'spldtb' U_BOOT_TPL_DTB_DATA = b'tpldtb' @@ -119,6 +120,11 @@ COMP_BINTOOLS = ['bzip2', 'gzip', 'lz4', 'lzma_alone', 'lzop', 'xz', 'zstd'] TEE_ADDR = 0x5678 +# Firmware Management Protocol(FMP) GUID +FW_MGMT_GUID = 'edd5cb6d2de8444cbda17194199ad92a' +# Image GUID specified in the DTS +CAPSULE_IMAGE_GUID = '52cfd7092007104791d108469b7fe9c8' + class TestFunctional(unittest.TestCase): """Functional tests for binman @@ -215,6 +221,7 @@ class TestFunctional(unittest.TestCase): TestFunctional._MakeInputFile('scp.bin', SCP_DATA) TestFunctional._MakeInputFile('rockchip-tpl.bin', ROCKCHIP_TPL_DATA) TestFunctional._MakeInputFile('ti_unsecure.bin', TI_UNSECURE_DATA) + TestFunctional._MakeInputFile('efi_capsule_payload.bin', EFI_CAPSULE_DATA) # Add a few .dtb files for testing TestFunctional._MakeInputFile('%s/test-fdt1.dtb' % TEST_FDT_SUBDIR, @@ -7087,5 +7094,120 @@ fdt fdtmap Extract the devicetree blob from the fdtmap self.assertEqual(fdt_util.GetString(key_node, "key-name-hint"), "key") + def _CheckCapsule(self, data, signed_capsule=False, version_check=False, + capoemflags=False): + fmp_signature = "4d535331" # 'M', 'S', 'S', '1' + fmp_size = "10" + fmp_fw_version = "02" + oemflag = "0080" + + payload_data = EFI_CAPSULE_DATA + + # Firmware Management Protocol(FMP) GUID - offset(0 - 32) + self.assertEqual(FW_MGMT_GUID, data.hex()[:32]) + # Image GUID - offset(96 - 128) + self.assertEqual(CAPSULE_IMAGE_GUID, data.hex()[96:128]) + + if capoemflags: + # OEM Flags - offset(40 - 44) + self.assertEqual(oemflag, data.hex()[40:44]) + if signed_capsule and version_check: + # FMP header signature - offset(4770 - 4778) + self.assertEqual(fmp_signature, data.hex()[4770:4778]) + # FMP header size - offset(4778 - 4780) + self.assertEqual(fmp_size, data.hex()[4778:4780]) + # firmware version - offset(4786 - 4788) + self.assertEqual(fmp_fw_version, data.hex()[4786:4788]) + # payload offset signed capsule(4802 - 4808) + self.assertEqual(payload_data.hex(), data.hex()[4802:4808]) + elif signed_capsule: + # payload offset signed capsule(4770 - 4776) + self.assertEqual(payload_data.hex(), data.hex()[4770:4776]) + elif version_check: + # FMP header signature - offset(184 - 192) + self.assertEqual(fmp_signature, data.hex()[184:192]) + # FMP header size - offset(192 - 194) + self.assertEqual(fmp_size, data.hex()[192:194]) + # firmware version - offset(200 - 202) + self.assertEqual(fmp_fw_version, data.hex()[200:202]) + # payload offset for non-signed capsule with version header(216 - 222) + self.assertEqual(payload_data.hex(), data.hex()[216:222]) + else: + # payload offset for non-signed capsule with no version header(184 - 190) + self.assertEqual(payload_data.hex(), data.hex()[184:190]) + + def testCapsuleGen(self): + """Test generation of EFI capsule""" + data = self._DoReadFile('307_capsule.dts') + + self._CheckCapsule(data) + + def testSignedCapsuleGen(self): + """Test generation of EFI capsule""" + data = tools.read_file(self.TestFile("key.key")) + self._MakeInputFile("key.key", data) + data = tools.read_file(self.TestFile("key.pem")) + self._MakeInputFile("key.crt", data) + + data = self._DoReadFile('308_capsule_signed.dts') + + self._CheckCapsule(data, signed_capsule=True) + + def testCapsuleGenVersionSupport(self): + """Test generation of EFI capsule with version support""" + data = self._DoReadFile('309_capsule_version.dts') + + self._CheckCapsule(data, version_check=True) + + def testCapsuleGenSignedVer(self): + """Test generation of signed EFI capsule with version information""" + data = tools.read_file(self.TestFile("key.key")) + self._MakeInputFile("key.key", data) + data = tools.read_file(self.TestFile("key.pem")) + self._MakeInputFile("key.crt", data) + + data = self._DoReadFile('310_capsule_signed_ver.dts') + + self._CheckCapsule(data, signed_capsule=True, version_check=True) + + def testCapsuleGenCapOemFlags(self): + """Test generation of EFI capsule with OEM Flags set""" + data = self._DoReadFile('311_capsule_oemflags.dts') + + self._CheckCapsule(data, capoemflags=True) + + + def testCapsuleGenKeyMissing(self): + """Test that binman errors out on missing key""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('312_capsule_missing_key.dts') + + self.assertIn("Both private key and public key certificate need to be provided", + str(e.exception)) + + def testCapsuleGenIndexMissing(self): + """Test that binman errors out on missing image index""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('313_capsule_missing_index.dts') + + self.assertIn("entry is missing properties: image-index", + str(e.exception)) + + def testCapsuleGenGuidMissing(self): + """Test that binman errors out on missing image GUID""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('314_capsule_missing_guid.dts') + + self.assertIn("entry is missing properties: image-type-id", + str(e.exception)) + + def testCapsuleGenPayloadMissing(self): + """Test that binman errors out on missing input(payload)image""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('315_capsule_missing_payload.dts') + + self.assertIn("The capsule entry expects a single subnode for payload", + str(e.exception)) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/307_capsule.dts b/tools/binman/test/307_capsule.dts new file mode 100644 index 0000000000..dd5b6f1c11 --- /dev/null +++ b/tools/binman/test/307_capsule.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + efi-capsule { + image-index = <0x1>; + /* Image GUID for testing capsule update */ + image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8"; + hardware-instance = <0x0>; + + blob-ext { + filename = "efi_capsule_payload.bin"; + }; + }; + }; +}; diff --git a/tools/binman/test/308_capsule_signed.dts b/tools/binman/test/308_capsule_signed.dts new file mode 100644 index 0000000000..2765dd4140 --- /dev/null +++ b/tools/binman/test/308_capsule_signed.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + efi-capsule { + image-index = <0x1>; + /* Image GUID for testing capsule update */ + image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8"; + hardware-instance = <0x0>; + private-key = "key.key"; + pub-key-cert = "key.crt"; + + blob-ext { + filename = "efi_capsule_payload.bin"; + }; + }; + }; +}; diff --git a/tools/binman/test/309_capsule_version.dts b/tools/binman/test/309_capsule_version.dts new file mode 100644 index 0000000000..21d6e2bd26 --- /dev/null +++ b/tools/binman/test/309_capsule_version.dts @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + efi-capsule { + image-index = <0x1>; + fw-version = <0x2>; + /* Image GUID for testing capsule update */ + image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8"; + hardware-instance = <0x0>; + + blob-ext { + filename = "efi_capsule_payload.bin"; + }; + }; + }; +}; diff --git a/tools/binman/test/310_capsule_signed_ver.dts b/tools/binman/test/310_capsule_signed_ver.dts new file mode 100644 index 0000000000..f3606e6520 --- /dev/null +++ b/tools/binman/test/310_capsule_signed_ver.dts @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + efi-capsule { + image-index = <0x1>; + fw-version = <0x2>; + /* Image GUID for testing capsule update */ + image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8"; + hardware-instance = <0x0>; + private-key = "key.key"; + pub-key-cert = "key.crt"; + + blob-ext { + filename = "efi_capsule_payload.bin"; + }; + }; + }; +}; diff --git a/tools/binman/test/311_capsule_oemflags.dts b/tools/binman/test/311_capsule_oemflags.dts new file mode 100644 index 0000000000..9f535f8712 --- /dev/null +++ b/tools/binman/test/311_capsule_oemflags.dts @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + efi-capsule { + image-index = <0x1>; + /* Image GUID for testing capsule update */ + image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8"; + hardware-instance = <0x0>; + capoemflags = <0x8000>; + + blob-ext { + filename = "efi_capsule_payload.bin"; + }; + }; + }; +}; diff --git a/tools/binman/test/312_capsule_missing_key.dts b/tools/binman/test/312_capsule_missing_key.dts new file mode 100644 index 0000000000..6a6fc59b6d --- /dev/null +++ b/tools/binman/test/312_capsule_missing_key.dts @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + efi-capsule { + image-index = <0x1>; + /* Image GUID for testing capsule update */ + image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8"; + hardware-instance = <0x0>; + private-key = "tools/binman/test/key.key"; + + blob-ext { + filename = "efi_capsule_payload.bin"; + }; + }; + }; +}; diff --git a/tools/binman/test/313_capsule_missing_index.dts b/tools/binman/test/313_capsule_missing_index.dts new file mode 100644 index 0000000000..7806521135 --- /dev/null +++ b/tools/binman/test/313_capsule_missing_index.dts @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + efi-capsule { + /* Image GUID for testing capsule update */ + image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8"; + hardware-instance = <0x0>; + + blob-ext { + filename = "efi_capsule_payload.bin"; + }; + }; + }; +}; diff --git a/tools/binman/test/314_capsule_missing_guid.dts b/tools/binman/test/314_capsule_missing_guid.dts new file mode 100644 index 0000000000..599562bef6 --- /dev/null +++ b/tools/binman/test/314_capsule_missing_guid.dts @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + efi-capsule { + image-index = <0x1>; + hardware-instance = <0x0>; + + blob-ext { + filename = "efi_capsule_payload.bin"; + }; + }; + }; +}; diff --git a/tools/binman/test/315_capsule_missing_payload.dts b/tools/binman/test/315_capsule_missing_payload.dts new file mode 100644 index 0000000000..86da9cd309 --- /dev/null +++ b/tools/binman/test/315_capsule_missing_payload.dts @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + efi-capsule { + image-index = <0x1>; + /* Image GUID for testing capsule update */ + image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8"; + hardware-instance = <0x0>; + }; + }; +};