binman: capsule: Add support for generating EFI empty capsules

Add support in binman for generating EFI empty capsules. These
capsules are used in the FWU A/B update feature. Also add test cases
in binman for the corresponding code coverage.

Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
Reviewed-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Sughosh Ganu 2023-10-10 14:40:59 +05:30 committed by Simon Glass
parent f1c8fc5e67
commit 74aae507bc
8 changed files with 247 additions and 0 deletions

View file

@ -532,6 +532,50 @@ payload using the blob-ext subnode.
.. _etype_efi_empty_capsule:
Entry: efi-empty-capsule: Entry for generating EFI Empty Capsule files
----------------------------------------------------------------------
The parameters needed for generation of the empty capsules can
be provided as properties in the entry.
Properties / Entry arguments:
- image-guid: Image GUID which will be used for identifying the
updatable image on the board. Mandatory for accept capsule.
- capsule-type - String to indicate type of capsule to generate. Valid
values are 'accept' and 'revert'.
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`_. For more information on the empty capsule, refer the
sections 2.3.2 and 2.3.3 in the `Dependable Boot specification`_.
A typical accept empty capsule entry node would then look something
like this::
empty-capsule {
type = "efi-empty-capsule";
/* GUID of the image being accepted */
image-type-id = SANDBOX_UBOOT_IMAGE_GUID;
capsule-type = "accept";
};
A typical revert empty capsule entry node would then look something
like this::
empty-capsule {
type = "efi-empty-capsule";
capsule-type = "revert";
};
The empty capsules do not have any input payload image.
.. _`UEFI specification`: https://uefi.org/sites/default/files/resources/UEFI_Spec_2_10_Aug29.pdf
.. _`Dependable Boot specification`: https://git.codelinaro.org/linaro/dependable-boot/mbfw/uploads/6f7ddfe3be24e18d4319e108a758d02e/mbfw.pdf
.. _etype_encrypted:
Entry: encrypted: Externally built encrypted binary blob

View file

@ -0,0 +1,86 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2023 Linaro Limited
#
# Entry-type module for producing an empty EFI capsule
#
import os
from binman.entry import Entry
from binman.etype.efi_capsule import get_binman_test_guid
from binman.etype.section import Entry_section
from dtoc import fdt_util
from u_boot_pylib import tools
class Entry_efi_empty_capsule(Entry_section):
"""Generate EFI empty capsules
The parameters needed for generation of the empty capsules can
be provided as properties in the entry.
Properties / Entry arguments:
- image-guid: Image GUID which will be used for identifying the
updatable image on the board. Mandatory for accept capsule.
- capsule-type - String to indicate type of capsule to generate. Valid
values are 'accept' and 'revert'.
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`_. For more information on the empty capsule, refer the
sections 2.3.2 and 2.3.3 in the `Dependable Boot specification`_.
A typical accept empty capsule entry node would then look something like this
empty-capsule {
type = "efi-empty-capsule";
/* GUID of image being accepted */
image-type-id = SANDBOX_UBOOT_IMAGE_GUID;
capsule-type = "accept";
};
A typical revert empty capsule entry node would then look something like this
empty-capsule {
type = "efi-empty-capsule";
capsule-type = "revert";
};
The empty capsules do not have any input payload image.
.. _`UEFI specification`: https://uefi.org/sites/default/files/resources/UEFI_Spec_2_10_Aug29.pdf
.. _`Dependable Boot specification`: https://git.codelinaro.org/linaro/dependable-boot/mbfw/uploads/6f7ddfe3be24e18d4319e108a758d02e/mbfw.pdf
"""
def __init__(self, section, etype, node):
super().__init__(section, etype, node)
self.required_props = ['capsule-type']
self.accept = 0
self.revert = 0
def ReadNode(self):
super().ReadNode()
self.image_guid = fdt_util.GetString(self._node, 'image-guid')
self.capsule_type = fdt_util.GetString(self._node, 'capsule-type')
if self.capsule_type != 'accept' and self.capsule_type != 'revert':
self.Raise('capsule-type should be either \'accept\' or \'revert\'')
if self.capsule_type == 'accept' and not self.image_guid:
self.Raise('Image GUID needed for generating accept capsule')
def BuildSectionData(self, required):
uniq = self.GetUniqueName()
outfile = self._filename if self._filename else 'capsule.%s' % uniq
capsule_fname = tools.get_output_filename(outfile)
accept = True if self.capsule_type == 'accept' else False
guid = self.image_guid
if self.image_guid == "binman-test":
guid = get_binman_test_guid('binman-test')
ret = self.mkeficapsule.generate_empty_capsule(guid, capsule_fname,
accept)
if ret is not None:
return tools.read_file(capsule_fname)
def AddBintools(self, btools):
self.mkeficapsule = self.AddBintool(btools, 'mkeficapsule')

View file

@ -126,6 +126,9 @@ FW_MGMT_GUID = '6dcbd5ed-e82d-4c44-bda1-7194199ad92a'
CAPSULE_IMAGE_GUID = '09d7cf52-0720-4710-91d1-08469b7fe9c8'
# Windows cert GUID
WIN_CERT_TYPE_EFI_GUID = '4aafd29d-68df-49ee-8aa9-347d375665a7'
# Empty capsule GUIDs
EMPTY_CAPSULE_ACCEPT_GUID = '0c996046-bcc0-4d04-85ec-e1fcedf1c6f8'
EMPTY_CAPSULE_REVERT_GUID = 'acd58b4b-c0e8-475f-99b5-6b3f7e07aaf0'
class TestFunctional(unittest.TestCase):
"""Functional tests for binman
@ -7293,6 +7296,27 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
self.assertEqual(payload_data_len, int(hdr['Payload Image Size']))
def _CheckEmptyCapsule(self, data, accept_capsule=False):
if accept_capsule:
capsule_hdr_guid = EMPTY_CAPSULE_ACCEPT_GUID
else:
capsule_hdr_guid = EMPTY_CAPSULE_REVERT_GUID
hdr = self._GetCapsuleHeaders(data)
self.assertEqual(capsule_hdr_guid.upper(),
hdr['EFI_CAPSULE_HDR.CAPSULE_GUID'])
if accept_capsule:
capsule_size = "0000002C"
else:
capsule_size = "0000001C"
self.assertEqual(capsule_size,
hdr['EFI_CAPSULE_HDR.CAPSULE_IMAGE_SIZE'])
if accept_capsule:
self.assertEqual(CAPSULE_IMAGE_GUID.upper(), hdr['ACCEPT_IMAGE_GUID'])
def testCapsuleGen(self):
"""Test generation of EFI capsule"""
data = self._DoReadFile('311_capsule.dts')
@ -7357,5 +7381,38 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
self.assertIn("entry is missing properties: image-guid",
str(e.exception))
def testCapsuleGenAcceptCapsule(self):
"""Test generationg of accept EFI capsule"""
data = self._DoReadFile('319_capsule_accept.dts')
self._CheckEmptyCapsule(data, accept_capsule=True)
def testCapsuleGenRevertCapsule(self):
"""Test generationg of revert EFI capsule"""
data = self._DoReadFile('320_capsule_revert.dts')
self._CheckEmptyCapsule(data)
def testCapsuleGenAcceptGuidMissing(self):
"""Test that binman errors out on missing image GUID for accept capsule"""
with self.assertRaises(ValueError) as e:
self._DoReadFile('321_capsule_accept_missing_guid.dts')
self.assertIn("Image GUID needed for generating accept capsule",
str(e.exception))
def testCapsuleGenEmptyCapsuleTypeMissing(self):
"""Test that capsule-type is specified"""
with self.assertRaises(ValueError) as e:
self._DoReadFile('322_empty_capsule_type_missing.dts')
self.assertIn("entry is missing properties: capsule-type",
str(e.exception))
def testCapsuleGenAcceptOrRevertMissing(self):
"""Test that both accept and revert capsule are not specified"""
with self.assertRaises(ValueError) as e:
self._DoReadFile('323_capsule_accept_revert_missing.dts')
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1,13 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
efi-empty-capsule {
/* Image GUID for testing capsule update */
image-guid = "binman-test";
capsule-type = "accept";
};
};
};

View file

@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
efi-empty-capsule {
capsule-type = "revert";
};
};
};

View file

@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
efi-empty-capsule {
capsule-type = "accept";
};
};
};

View file

@ -0,0 +1,12 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
efi-empty-capsule {
/* Image GUID for testing capsule update */
image-guid = "binman-test";
};
};
};

View file

@ -0,0 +1,13 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
efi-empty-capsule {
/* Image GUID for testing capsule update */
image-guid = "binman-test";
capsule-type = "foo";
};
};
};