1# SPDX-License-Identifier: GPL-2.0+ 2# Copyright (c) 2023 Linaro Limited 3# 4# Entry-type module for producing a EFI capsule 5# 6 7import os 8 9from binman.entry import Entry 10from binman.etype.section import Entry_section 11from dtoc import fdt_util 12from u_boot_pylib import tools 13 14def get_binman_test_guid(type_str): 15 """Get the test image GUID for binman 16 17 Based on the string passed to the function, return 18 the corresponding GUID. 19 20 Args: 21 type_str: Key value of the type of GUID to look for 22 23 Returns: 24 The actual GUID value (str) 25 """ 26 TYPE_TO_GUID = { 27 'binman-test' : '985f2937-7c2e-5e9a-8a5e-8e063312964b' 28 } 29 30 return TYPE_TO_GUID[type_str] 31 32class Entry_efi_capsule(Entry_section): 33 """Generate EFI capsules 34 35 The parameters needed for generation of the capsules can 36 be provided as properties in the entry. 37 38 Properties / Entry arguments: 39 - image-index: Unique number for identifying corresponding 40 payload image. Number between 1 and descriptor count, i.e. 41 the total number of firmware images that can be updated. Mandatory 42 property. 43 - image-guid: Image GUID which will be used for identifying the 44 updatable image on the board. Mandatory property. 45 - hardware-instance: Optional number for identifying unique 46 hardware instance of a device in the system. Default value of 0 47 for images where value is not to be used. 48 - fw-version: Value of image version that can be put on the capsule 49 through the Firmware Management Protocol(FMP) header. 50 - monotonic-count: Count used when signing an image. 51 - private-key: Path to PEM formatted .key private key file. Mandatory 52 property for generating signed capsules. 53 - public-key-cert: Path to PEM formatted .crt public key certificate 54 file. Mandatory property for generating signed capsules. 55 - oem-flags - OEM flags to be passed through capsule header. 56 57 Since this is a subclass of Entry_section, all properties of the parent 58 class also apply here. Except for the properties stated as mandatory, the 59 rest of the properties are optional. 60 61 For more details on the description of the capsule format, and the capsule 62 update functionality, refer Section 8.5 and Chapter 23 in the `UEFI 63 specification`_. 64 65 The capsule parameters like image index and image GUID are passed as 66 properties in the entry. The payload to be used in the capsule is to be 67 provided as a subnode of the capsule entry. 68 69 A typical capsule entry node would then look something like this:: 70 71 capsule { 72 type = "efi-capsule"; 73 image-index = <0x1>; 74 /* Image GUID for testing capsule update */ 75 image-guid = SANDBOX_UBOOT_IMAGE_GUID; 76 hardware-instance = <0x0>; 77 private-key = "path/to/the/private/key"; 78 public-key-cert = "path/to/the/public-key-cert"; 79 oem-flags = <0x8000>; 80 81 u-boot { 82 }; 83 }; 84 85 In the above example, the capsule payload is the U-Boot image. The 86 capsule entry would read the contents of the payload and put them 87 into the capsule. Any external file can also be specified as the 88 payload using the blob-ext subnode. 89 90 .. _`UEFI specification`: https://uefi.org/sites/default/files/resources/UEFI_Spec_2_10_Aug29.pdf 91 """ 92 def __init__(self, section, etype, node): 93 super().__init__(section, etype, node) 94 self.required_props = ['image-index', 'image-guid'] 95 self.image_index = 0 96 self.image_guid = '' 97 self.hardware_instance = 0 98 self.monotonic_count = 0 99 self.fw_version = 0 100 self.oem_flags = 0 101 self.private_key = '' 102 self.public_key_cert = '' 103 self.auth = 0 104 105 def ReadNode(self): 106 super().ReadNode() 107 108 self.image_index = fdt_util.GetInt(self._node, 'image-index') 109 self.image_guid = fdt_util.GetString(self._node, 'image-guid') 110 self.fw_version = fdt_util.GetInt(self._node, 'fw-version') 111 self.hardware_instance = fdt_util.GetInt(self._node, 'hardware-instance') 112 self.monotonic_count = fdt_util.GetInt(self._node, 'monotonic-count') 113 self.oem_flags = fdt_util.GetInt(self._node, 'oem-flags') 114 115 self.private_key = fdt_util.GetString(self._node, 'private-key') 116 self.public_key_cert = fdt_util.GetString(self._node, 'public-key-cert') 117 if ((self.private_key and not self.public_key_cert) or (self.public_key_cert and not self.private_key)): 118 self.Raise('Both private key and public key certificate need to be provided') 119 elif not (self.private_key and self.public_key_cert): 120 self.auth = 0 121 else: 122 self.auth = 1 123 124 def BuildSectionData(self, required): 125 private_key = '' 126 public_key_cert = '' 127 if self.auth: 128 if not os.path.isabs(self.private_key): 129 private_key = tools.get_input_filename(self.private_key) 130 if not os.path.isabs(self.public_key_cert): 131 public_key_cert = tools.get_input_filename(self.public_key_cert) 132 data, payload, uniq = self.collect_contents_to_file( 133 self._entries.values(), 'capsule_in') 134 outfile = self._filename if self._filename else 'capsule.%s' % uniq 135 capsule_fname = tools.get_output_filename(outfile) 136 guid = self.image_guid 137 if self.image_guid == "binman-test": 138 guid = get_binman_test_guid('binman-test') 139 140 ret = self.mkeficapsule.generate_capsule(self.image_index, 141 guid, 142 self.hardware_instance, 143 payload, 144 capsule_fname, 145 private_key, 146 public_key_cert, 147 self.monotonic_count, 148 self.fw_version, 149 self.oem_flags) 150 if ret is not None: 151 return tools.read_file(capsule_fname) 152 else: 153 # Bintool is missing; just use the input data as the output 154 if not self.GetAllowMissing(): 155 self.Raise("Missing tool: 'mkeficapsule'") 156 self.record_missing_bintool(self.mkeficapsule) 157 return data 158 159 def AddBintools(self, btools): 160 self.mkeficapsule = self.AddBintool(btools, 'mkeficapsule') 161