blob: 519a47267caddfa4eab25ce6c2e9c66f841bed49 [file] [log] [blame]
AKASHI Takahirof4818e62020-11-30 18:12:12 +09001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * EFI Firmware management protocol
4 *
5 * Copyright (c) 2020 Linaro Limited
6 * Author: AKASHI Takahiro
7 */
8
9#include <common.h>
10#include <charset.h>
11#include <dfu.h>
12#include <efi_loader.h>
13#include <image.h>
Sughosh Ganu7221c6e2020-12-30 19:27:05 +053014#include <signatures.h>
15
AKASHI Takahirof4818e62020-11-30 18:12:12 +090016#include <linux/list.h>
17
Sughosh Ganu7221c6e2020-12-30 19:27:05 +053018#define FMP_PAYLOAD_HDR_SIGNATURE SIGNATURE_32('M', 'S', 'S', '1')
19
20/**
21 * struct fmp_payload_header - EDK2 header for the FMP payload
22 *
23 * This structure describes the header which is preprended to the
24 * FMP payload by the edk2 capsule generation scripts.
25 *
26 * @signature: Header signature used to identify the header
27 * @header_size: Size of the structure
28 * @fw_version: Firmware versions used
29 * @lowest_supported_version: Lowest supported version
30 */
31struct fmp_payload_header {
32 u32 signature;
33 u32 header_size;
34 u32 fw_version;
35 u32 lowest_supported_version;
36};
37
AKASHI Takahiro7ff3f3c2020-11-17 09:28:00 +090038/* Place holder; not supported */
39static
40efi_status_t EFIAPI efi_firmware_get_image_unsupported(
41 struct efi_firmware_management_protocol *this,
42 u8 image_index,
43 void *image,
44 efi_uintn_t *image_size)
45{
46 EFI_ENTRY("%p %d %p %p\n", this, image_index, image, image_size);
47
48 return EFI_EXIT(EFI_UNSUPPORTED);
49}
50
51/* Place holder; not supported */
52static
53efi_status_t EFIAPI efi_firmware_check_image_unsupported(
54 struct efi_firmware_management_protocol *this,
55 u8 image_index,
56 const void *image,
57 efi_uintn_t *image_size,
58 u32 *image_updatable)
59{
60 EFI_ENTRY("%p %d %p %p %p\n", this, image_index, image, image_size,
61 image_updatable);
62
63 return EFI_EXIT(EFI_UNSUPPORTED);
64}
65
66/* Place holder; not supported */
67static
68efi_status_t EFIAPI efi_firmware_get_package_info_unsupported(
69 struct efi_firmware_management_protocol *this,
70 u32 *package_version,
71 u16 **package_version_name,
72 u32 *package_version_name_maxlen,
73 u64 *attributes_supported,
74 u64 *attributes_setting)
75{
76 EFI_ENTRY("%p %p %p %p %p %p\n", this, package_version,
77 package_version_name, package_version_name_maxlen,
78 attributes_supported, attributes_setting);
79
80 return EFI_EXIT(EFI_UNSUPPORTED);
81}
82
83/* Place holder; not supported */
84static
85efi_status_t EFIAPI efi_firmware_set_package_info_unsupported(
86 struct efi_firmware_management_protocol *this,
87 const void *image,
88 efi_uintn_t *image_size,
89 const void *vendor_code,
90 u32 package_version,
91 const u16 *package_version_name)
92{
93 EFI_ENTRY("%p %p %p %p %x %p\n", this, image, image_size, vendor_code,
94 package_version, package_version_name);
95
96 return EFI_EXIT(EFI_UNSUPPORTED);
97}
AKASHI Takahirof4818e62020-11-30 18:12:12 +090098
99/**
100 * efi_get_dfu_info - return information about the current firmware image
101 * @this: Protocol instance
102 * @image_info_size: Size of @image_info
103 * @image_info: Image information
104 * @descriptor_version: Pointer to version number
105 * @descriptor_count: Pointer to number of descriptors
106 * @descriptor_size: Pointer to descriptor size
107 * package_version: Package version
108 * package_version_name: Package version's name
109 * image_type: Image type GUID
110 *
111 * Return information bout the current firmware image in @image_info.
112 * @image_info will consist of a number of descriptors.
113 * Each descriptor will be created based on "dfu_alt_info" variable.
114 *
115 * Return status code
116 */
117static efi_status_t efi_get_dfu_info(
118 efi_uintn_t *image_info_size,
119 struct efi_firmware_image_descriptor *image_info,
120 u32 *descriptor_version,
121 u8 *descriptor_count,
122 efi_uintn_t *descriptor_size,
123 u32 *package_version,
124 u16 **package_version_name,
125 const efi_guid_t *image_type)
126{
127 struct dfu_entity *dfu;
128 size_t names_len, total_size;
129 int dfu_num, i;
130 u16 *name, *next;
Heinrich Schuchardt294efd92022-01-15 02:11:22 +0100131 int ret;
AKASHI Takahirof4818e62020-11-30 18:12:12 +0900132
Heinrich Schuchardt294efd92022-01-15 02:11:22 +0100133 ret = dfu_init_env_entities(NULL, NULL);
134 if (ret)
135 return EFI_SUCCESS;
AKASHI Takahirof4818e62020-11-30 18:12:12 +0900136
137 names_len = 0;
138 dfu_num = 0;
139 list_for_each_entry(dfu, &dfu_list, list) {
140 names_len += (utf8_utf16_strlen(dfu->name) + 1) * 2;
141 dfu_num++;
142 }
143 if (!dfu_num) {
Heinrich Schuchardt294efd92022-01-15 02:11:22 +0100144 log_warning("No entities in dfu_alt_info\n");
AKASHI Takahirof4818e62020-11-30 18:12:12 +0900145 *image_info_size = 0;
146 dfu_free_entities();
147
148 return EFI_SUCCESS;
149 }
150
151 total_size = sizeof(*image_info) * dfu_num + names_len;
152 /*
153 * we will assume that sizeof(*image_info) * dfu_name
154 * is, at least, a multiple of 2. So the start address for
155 * image_id_name would be aligned with 2 bytes.
156 */
157 if (*image_info_size < total_size) {
158 *image_info_size = total_size;
159 dfu_free_entities();
160
161 return EFI_BUFFER_TOO_SMALL;
162 }
163 *image_info_size = total_size;
164
165 *descriptor_version = EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION;
166 *descriptor_count = dfu_num;
167 *descriptor_size = sizeof(*image_info);
168 *package_version = 0xffffffff; /* not supported */
169 *package_version_name = NULL; /* not supported */
170
171 /* DFU alt number should correspond to image_index */
172 i = 0;
173 /* Name area starts just after descriptors */
174 name = (u16 *)((u8 *)image_info + sizeof(*image_info) * dfu_num);
175 next = name;
176 list_for_each_entry(dfu, &dfu_list, list) {
177 image_info[i].image_index = dfu->alt + 1;
178 image_info[i].image_type_id = *image_type;
179 image_info[i].image_id = dfu->alt;
180
181 /* copy the DFU entity name */
182 utf8_utf16_strcpy(&next, dfu->name);
183 image_info[i].image_id_name = name;
184 name = ++next;
185
186 image_info[i].version = 0; /* not supported */
187 image_info[i].version_name = NULL; /* not supported */
188 image_info[i].size = 0;
189 image_info[i].attributes_supported =
Sughosh Ganuc9a821b2020-12-30 19:27:10 +0530190 IMAGE_ATTRIBUTE_IMAGE_UPDATABLE |
191 IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED;
AKASHI Takahirof4818e62020-11-30 18:12:12 +0900192 image_info[i].attributes_setting =
193 IMAGE_ATTRIBUTE_IMAGE_UPDATABLE;
Sughosh Ganuc9a821b2020-12-30 19:27:10 +0530194
195 /* Check if the capsule authentication is enabled */
Sughosh Ganud1bd8492021-04-12 20:35:23 +0530196 if (IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE))
Sughosh Ganuc9a821b2020-12-30 19:27:10 +0530197 image_info[0].attributes_setting |=
198 IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED;
199
AKASHI Takahirof4818e62020-11-30 18:12:12 +0900200 image_info[i].lowest_supported_image_version = 0;
201 image_info[i].last_attempt_version = 0;
202 image_info[i].last_attempt_status = LAST_ATTEMPT_STATUS_SUCCESS;
203 image_info[i].hardware_instance = 1;
204 image_info[i].dependencies = NULL;
205
206 i++;
207 }
208
209 dfu_free_entities();
210
211 return EFI_SUCCESS;
212}
213
AKASHI Takahiro7ff3f3c2020-11-17 09:28:00 +0900214#ifdef CONFIG_EFI_CAPSULE_FIRMWARE_FIT
215/*
216 * This FIRMWARE_MANAGEMENT_PROTOCOL driver provides a firmware update
217 * method with existing FIT image format, and handles
218 * - multiple regions of firmware via DFU
219 * but doesn't support
220 * - versioning of firmware image
221 * - package information
222 */
223const efi_guid_t efi_firmware_image_type_uboot_fit =
224 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID;
225
AKASHI Takahirof4818e62020-11-30 18:12:12 +0900226/**
227 * efi_firmware_fit_get_image_info - return information about the current
228 * firmware image
229 * @this: Protocol instance
230 * @image_info_size: Size of @image_info
231 * @image_info: Image information
232 * @descriptor_version: Pointer to version number
233 * @descriptor_count: Pointer to number of descriptors
234 * @descriptor_size: Pointer to descriptor size
235 * package_version: Package version
236 * package_version_name: Package version's name
237 *
238 * Return information bout the current firmware image in @image_info.
239 * @image_info will consist of a number of descriptors.
240 * Each descriptor will be created based on "dfu_alt_info" variable.
241 *
242 * Return status code
243 */
244static
245efi_status_t EFIAPI efi_firmware_fit_get_image_info(
246 struct efi_firmware_management_protocol *this,
247 efi_uintn_t *image_info_size,
248 struct efi_firmware_image_descriptor *image_info,
249 u32 *descriptor_version,
250 u8 *descriptor_count,
251 efi_uintn_t *descriptor_size,
252 u32 *package_version,
253 u16 **package_version_name)
254{
255 efi_status_t ret;
256
257 EFI_ENTRY("%p %p %p %p %p %p %p %p\n", this,
258 image_info_size, image_info,
259 descriptor_version, descriptor_count, descriptor_size,
260 package_version, package_version_name);
261
262 if (!image_info_size)
263 return EFI_EXIT(EFI_INVALID_PARAMETER);
264
265 if (*image_info_size &&
266 (!image_info || !descriptor_version || !descriptor_count ||
267 !descriptor_size || !package_version || !package_version_name))
268 return EFI_EXIT(EFI_INVALID_PARAMETER);
269
270 ret = efi_get_dfu_info(image_info_size, image_info,
271 descriptor_version, descriptor_count,
272 descriptor_size,
273 package_version, package_version_name,
274 &efi_firmware_image_type_uboot_fit);
275
276 return EFI_EXIT(ret);
277}
278
AKASHI Takahirof4818e62020-11-30 18:12:12 +0900279/**
280 * efi_firmware_fit_set_image - update the firmware image
281 * @this: Protocol instance
282 * @image_index: Image index number
283 * @image: New image
284 * @image_size: Size of new image
285 * @vendor_code: Vendor-specific update policy
286 * @progress: Function to report the progress of update
287 * @abort_reason: Pointer to string of abort reason
288 *
289 * Update the firmware to new image, using dfu. The new image should
290 * have FIT image format commonly used in U-Boot.
291 * @vendor_code, @progress and @abort_reason are not supported.
292 *
293 * Return: status code
294 */
295static
296efi_status_t EFIAPI efi_firmware_fit_set_image(
297 struct efi_firmware_management_protocol *this,
298 u8 image_index,
299 const void *image,
300 efi_uintn_t image_size,
301 const void *vendor_code,
302 efi_status_t (*progress)(efi_uintn_t completion),
303 u16 **abort_reason)
304{
Simon Glassc665dbf2021-02-07 14:27:02 -0700305 EFI_ENTRY("%p %d %p %zd %p %p %p\n", this, image_index, image,
AKASHI Takahirof4818e62020-11-30 18:12:12 +0900306 image_size, vendor_code, progress, abort_reason);
307
308 if (!image || image_index != 1)
309 return EFI_EXIT(EFI_INVALID_PARAMETER);
310
311 if (fit_update(image))
312 return EFI_EXIT(EFI_DEVICE_ERROR);
313
314 return EFI_EXIT(EFI_SUCCESS);
315}
316
AKASHI Takahiro7ff3f3c2020-11-17 09:28:00 +0900317const struct efi_firmware_management_protocol efi_fmp_fit = {
318 .get_image_info = efi_firmware_fit_get_image_info,
319 .get_image = efi_firmware_get_image_unsupported,
320 .set_image = efi_firmware_fit_set_image,
321 .check_image = efi_firmware_check_image_unsupported,
322 .get_package_info = efi_firmware_get_package_info_unsupported,
323 .set_package_info = efi_firmware_set_package_info_unsupported,
324};
325#endif /* CONFIG_EFI_CAPSULE_FIRMWARE_FIT */
AKASHI Takahirof4818e62020-11-30 18:12:12 +0900326
AKASHI Takahiro7ff3f3c2020-11-17 09:28:00 +0900327#ifdef CONFIG_EFI_CAPSULE_FIRMWARE_RAW
328/*
329 * This FIRMWARE_MANAGEMENT_PROTOCOL driver provides a firmware update
330 * method with raw data.
331 */
332const efi_guid_t efi_firmware_image_type_uboot_raw =
333 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID;
AKASHI Takahirof4818e62020-11-30 18:12:12 +0900334
AKASHI Takahiro7ff3f3c2020-11-17 09:28:00 +0900335/**
336 * efi_firmware_raw_get_image_info - return information about the current
337 firmware image
338 * @this: Protocol instance
339 * @image_info_size: Size of @image_info
340 * @image_info: Image information
341 * @descriptor_version: Pointer to version number
342 * @descriptor_count: Pointer to number of descriptors
343 * @descriptor_size: Pointer to descriptor size
344 * package_version: Package version
345 * package_version_name: Package version's name
346 *
347 * Return information bout the current firmware image in @image_info.
348 * @image_info will consist of a number of descriptors.
349 * Each descriptor will be created based on "dfu_alt_info" variable.
350 *
351 * Return status code
352 */
AKASHI Takahirof4818e62020-11-30 18:12:12 +0900353static
AKASHI Takahiro7ff3f3c2020-11-17 09:28:00 +0900354efi_status_t EFIAPI efi_firmware_raw_get_image_info(
AKASHI Takahirof4818e62020-11-30 18:12:12 +0900355 struct efi_firmware_management_protocol *this,
AKASHI Takahiro7ff3f3c2020-11-17 09:28:00 +0900356 efi_uintn_t *image_info_size,
357 struct efi_firmware_image_descriptor *image_info,
358 u32 *descriptor_version,
359 u8 *descriptor_count,
360 efi_uintn_t *descriptor_size,
AKASHI Takahirof4818e62020-11-30 18:12:12 +0900361 u32 *package_version,
AKASHI Takahiro7ff3f3c2020-11-17 09:28:00 +0900362 u16 **package_version_name)
AKASHI Takahirof4818e62020-11-30 18:12:12 +0900363{
AKASHI Takahiro7ff3f3c2020-11-17 09:28:00 +0900364 efi_status_t ret = EFI_SUCCESS;
AKASHI Takahirof4818e62020-11-30 18:12:12 +0900365
AKASHI Takahiro7ff3f3c2020-11-17 09:28:00 +0900366 EFI_ENTRY("%p %p %p %p %p %p %p %p\n", this,
367 image_info_size, image_info,
368 descriptor_version, descriptor_count, descriptor_size,
369 package_version, package_version_name);
370
371 if (!image_info_size)
372 return EFI_EXIT(EFI_INVALID_PARAMETER);
373
374 if (*image_info_size &&
375 (!image_info || !descriptor_version || !descriptor_count ||
376 !descriptor_size || !package_version || !package_version_name))
377 return EFI_EXIT(EFI_INVALID_PARAMETER);
378
379 ret = efi_get_dfu_info(image_info_size, image_info,
380 descriptor_version, descriptor_count,
381 descriptor_size,
382 package_version, package_version_name,
383 &efi_firmware_image_type_uboot_raw);
384
385 return EFI_EXIT(ret);
AKASHI Takahirof4818e62020-11-30 18:12:12 +0900386}
387
AKASHI Takahiro7ff3f3c2020-11-17 09:28:00 +0900388/**
389 * efi_firmware_raw_set_image - update the firmware image
390 * @this: Protocol instance
391 * @image_index: Image index number
392 * @image: New image
393 * @image_size: Size of new image
394 * @vendor_code: Vendor-specific update policy
395 * @progress: Function to report the progress of update
396 * @abort_reason: Pointer to string of abort reason
397 *
398 * Update the firmware to new image, using dfu. The new image should
399 * be a single raw image.
400 * @vendor_code, @progress and @abort_reason are not supported.
401 *
402 * Return: status code
403 */
AKASHI Takahirof4818e62020-11-30 18:12:12 +0900404static
AKASHI Takahiro7ff3f3c2020-11-17 09:28:00 +0900405efi_status_t EFIAPI efi_firmware_raw_set_image(
AKASHI Takahirof4818e62020-11-30 18:12:12 +0900406 struct efi_firmware_management_protocol *this,
AKASHI Takahiro7ff3f3c2020-11-17 09:28:00 +0900407 u8 image_index,
AKASHI Takahirof4818e62020-11-30 18:12:12 +0900408 const void *image,
AKASHI Takahiro7ff3f3c2020-11-17 09:28:00 +0900409 efi_uintn_t image_size,
AKASHI Takahirof4818e62020-11-30 18:12:12 +0900410 const void *vendor_code,
AKASHI Takahiro7ff3f3c2020-11-17 09:28:00 +0900411 efi_status_t (*progress)(efi_uintn_t completion),
412 u16 **abort_reason)
AKASHI Takahirof4818e62020-11-30 18:12:12 +0900413{
Sughosh Ganu7221c6e2020-12-30 19:27:05 +0530414 u32 fmp_hdr_signature;
415 struct fmp_payload_header *header;
Sughosh Ganuc9a821b2020-12-30 19:27:10 +0530416 void *capsule_payload;
417 efi_status_t status;
418 efi_uintn_t capsule_payload_size;
Sughosh Ganu7221c6e2020-12-30 19:27:05 +0530419
Simon Glassc665dbf2021-02-07 14:27:02 -0700420 EFI_ENTRY("%p %d %p %zd %p %p %p\n", this, image_index, image,
AKASHI Takahiro7ff3f3c2020-11-17 09:28:00 +0900421 image_size, vendor_code, progress, abort_reason);
AKASHI Takahirof4818e62020-11-30 18:12:12 +0900422
AKASHI Takahiro7ff3f3c2020-11-17 09:28:00 +0900423 if (!image)
424 return EFI_EXIT(EFI_INVALID_PARAMETER);
425
Sughosh Ganuc9a821b2020-12-30 19:27:10 +0530426 /* Authenticate the capsule if authentication enabled */
Sughosh Ganud1bd8492021-04-12 20:35:23 +0530427 if (IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)) {
Sughosh Ganuc9a821b2020-12-30 19:27:10 +0530428 capsule_payload = NULL;
429 capsule_payload_size = 0;
430 status = efi_capsule_authenticate(image, image_size,
431 &capsule_payload,
432 &capsule_payload_size);
433
434 if (status == EFI_SECURITY_VIOLATION) {
435 printf("Capsule authentication check failed. Aborting update\n");
436 return EFI_EXIT(status);
437 } else if (status != EFI_SUCCESS) {
438 return EFI_EXIT(status);
439 }
440
441 debug("Capsule authentication successfull\n");
442 image = capsule_payload;
443 image_size = capsule_payload_size;
444 } else {
445 debug("Capsule authentication disabled. ");
446 debug("Updating capsule without authenticating.\n");
447 }
448
Sughosh Ganu7221c6e2020-12-30 19:27:05 +0530449 fmp_hdr_signature = FMP_PAYLOAD_HDR_SIGNATURE;
450 header = (void *)image;
451
452 if (!memcmp(&header->signature, &fmp_hdr_signature,
453 sizeof(fmp_hdr_signature))) {
454 /*
455 * When building the capsule with the scripts in
456 * edk2, a FMP header is inserted above the capsule
457 * payload. Compensate for this header to get the
458 * actual payload that is to be updated.
459 */
460 image += header->header_size;
461 image_size -= header->header_size;
462
463 }
464
AKASHI Takahiro7ff3f3c2020-11-17 09:28:00 +0900465 if (dfu_write_by_alt(image_index - 1, (void *)image, image_size,
466 NULL, NULL))
467 return EFI_EXIT(EFI_DEVICE_ERROR);
468
469 return EFI_EXIT(EFI_SUCCESS);
AKASHI Takahirof4818e62020-11-30 18:12:12 +0900470}
471
AKASHI Takahiro7ff3f3c2020-11-17 09:28:00 +0900472const struct efi_firmware_management_protocol efi_fmp_raw = {
473 .get_image_info = efi_firmware_raw_get_image_info,
AKASHI Takahirof4818e62020-11-30 18:12:12 +0900474 .get_image = efi_firmware_get_image_unsupported,
AKASHI Takahiro7ff3f3c2020-11-17 09:28:00 +0900475 .set_image = efi_firmware_raw_set_image,
AKASHI Takahirof4818e62020-11-30 18:12:12 +0900476 .check_image = efi_firmware_check_image_unsupported,
477 .get_package_info = efi_firmware_get_package_info_unsupported,
478 .set_package_info = efi_firmware_set_package_info_unsupported,
479};
AKASHI Takahiro7ff3f3c2020-11-17 09:28:00 +0900480#endif /* CONFIG_EFI_CAPSULE_FIRMWARE_RAW */