1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
|
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2023 Linaro Limited
#
# Entry-type module for producing a EFI capsule
#
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
def get_binman_test_guid(type_str):
"""Get the test image GUID for binman
Based on the string passed to the function, return
the corresponding GUID.
Args:
type_str: Key value of the type of GUID to look for
Returns:
The actual GUID value (str)
"""
TYPE_TO_GUID = {
'binman-test' : '985f2937-7c2e-5e9a-8a5e-8e063312964b'
}
return TYPE_TO_GUID[type_str]
class Entry_efi_capsule(Entry_section):
"""Generate EFI capsules
The parameters needed for generation of the capsules can
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. Mandatory
property.
- image-guid: Image GUID which will be used for identifying the
updatable image on the board. Mandatory property.
- 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: 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. Mandatory
property for generating signed capsules.
- public-key-cert: Path to PEM formatted .crt public key certificate
file. Mandatory property for generating signed capsules.
- oem-flags - OEM flags to be passed through capsule header.
- dump-signature: Optional boolean (default: false). Instruct
mkeficapsule to write signature data to a separate file. The
filename will be <capsule file>.p7. It might be used to verify
capsule authentication with external tools.
Since this is a subclass of Entry_section, all properties of the parent
class also apply here. Except for the properties stated as mandatory, the
rest of the properties are optional.
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`_.
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-guid = SANDBOX_UBOOT_IMAGE_GUID;
hardware-instance = <0x0>;
private-key = "path/to/the/private/key";
public-key-cert = "path/to/the/public-key-cert";
oem-flags = <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.
.. _`UEFI specification`: https://uefi.org/sites/default/files/resources/UEFI_Spec_2_10_Aug29.pdf
"""
def __init__(self, section, etype, node):
super().__init__(section, etype, node)
self.required_props = ['image-index', 'image-guid']
self.image_index = 0
self.image_guid = ''
self.hardware_instance = 0
self.monotonic_count = 0
self.fw_version = 0
self.oem_flags = 0
self.private_key = ''
self.public_key_cert = ''
self.auth = 0
self.dump_signature = False
def ReadNode(self):
super().ReadNode()
self.image_index = fdt_util.GetInt(self._node, 'image-index')
self.image_guid = fdt_util.GetString(self._node, 'image-guid')
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.oem_flags = fdt_util.GetInt(self._node, 'oem-flags')
self.dump_signature = fdt_util.GetBool(self._node, 'dump-signature')
self.private_key = fdt_util.GetString(self._node, 'private-key')
self.public_key_cert = fdt_util.GetString(self._node, 'public-key-cert')
if ((self.private_key and not self.public_key_cert) or (self.public_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.public_key_cert):
self.auth = 0
else:
self.auth = 1
def BuildSectionData(self, required):
private_key = ''
public_key_cert = ''
if self.auth:
if not os.path.isabs(self.private_key) and not 'pkcs11:' in self.private_key:
private_key = tools.get_input_filename(self.private_key)
if not os.path.isabs(self.public_key_cert) and not 'pkcs11:' in self.public_key_cert:
public_key_cert = tools.get_input_filename(self.public_key_cert)
if 'pkcs11:' in self.private_key:
private_key = self.private_key
if 'pkcs11:' in self.public_key_cert:
public_key_cert = self.public_key_cert
data, payload, uniq = self.collect_contents_to_file(
self._entries.values(), 'capsule_in')
outfile = self._filename if self._filename else 'capsule.%s' % uniq
capsule_fname = tools.get_output_filename(outfile)
guid = self.image_guid
if self.image_guid == "binman-test":
guid = get_binman_test_guid('binman-test')
ret = self.mkeficapsule.generate_capsule(self.image_index,
guid,
self.hardware_instance,
payload,
capsule_fname,
private_key,
public_key_cert,
self.monotonic_count,
self.fw_version,
self.oem_flags,
self.dump_signature)
if ret is not None:
return tools.read_file(capsule_fname)
else:
# Bintool is missing; just use the input data as the output
if not self.GetAllowMissing():
self.Raise("Missing tool: 'mkeficapsule'")
self.record_missing_bintool(self.mkeficapsule)
return data
def AddBintools(self, btools):
self.mkeficapsule = self.AddBintool(btools, 'mkeficapsule')
|