blob: e235c8fe91c6f44d12fe8c874d78ca1574ffe09a [file] [log] [blame]
Jose Marinhoebb61ee2021-03-02 17:26:38 +00001// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * EFI application ESRT tables support
4 *
5 * Copyright (C) 2021 Arm Ltd.
6 */
7
Heinrich Schuchardt955a3212025-01-16 20:26:59 +01008#define LOG_CATEGORY LOGC_EFI
9
Jose Marinhoebb61ee2021-03-02 17:26:38 +000010#include <efi_loader.h>
11#include <log.h>
12#include <efi_api.h>
13#include <malloc.h>
14
15const efi_guid_t efi_esrt_guid = EFI_SYSTEM_RESOURCE_TABLE_GUID;
16
17static struct efi_system_resource_table *esrt;
18
19#define EFI_ESRT_VERSION 1
20
21/**
22 * efi_esrt_image_info_to_entry() - copy the information present in a fw image
23 * descriptor to a ESRT entry.
24 * The function ensures the ESRT entry matches the image_type_id in @img_info.
25 * In case of a mismatch we leave the entry unchanged.
26 *
27 * @img_info: the source image info descriptor
28 * @entry: pointer to the ESRT entry to be filled
29 * @desc_version: the version of the elements in img_info
30 * @image_type: the image type value to be set in the ESRT entry
31 * @flags: the capsule flags value to be set in the ESRT entry
32 *
33 * Return:
34 * - EFI_SUCCESS if the entry is correctly updated
35 * - EFI_INVALID_PARAMETER if entry does not match image_type_id in @img_info.
36 */
37static efi_status_t
38efi_esrt_image_info_to_entry(struct efi_firmware_image_descriptor *img_info,
39 struct efi_system_resource_entry *entry,
40 u32 desc_version, u32 image_type, u32 flags)
41{
42 if (guidcmp(&entry->fw_class, &img_info->image_type_id)) {
43 EFI_PRINT("ESRT entry %pUL mismatches img_type_id %pUL\n",
44 &entry->fw_class, &img_info->image_type_id);
45 return EFI_INVALID_PARAMETER;
46 }
47
48 entry->fw_version = img_info->version;
49
50 entry->fw_type = image_type;
51 entry->capsule_flags = flags;
52
53 /*
54 * The field lowest_supported_image_version is only present
55 * on image info structure of version 2 or greater.
56 * See the EFI_FIRMWARE_IMAGE_DESCRIPTOR definition in UEFI.
57 */
58 if (desc_version >= 2)
59 entry->lowest_supported_fw_version =
60 img_info->lowest_supported_image_version;
61 else
62 entry->lowest_supported_fw_version = 0;
63
64 /*
65 * The fields last_attempt_version and last_attempt_status
66 * are only present on image info structure of version 3 or
67 * greater.
68 * See the EFI_FIRMWARE_IMAGE_DESCRIPTOR definition in UEFI.
69 */
70 if (desc_version >= 3) {
71 entry->last_attempt_version =
72 img_info->last_attempt_version;
73
74 entry->last_attempt_status =
75 img_info->last_attempt_status;
76 } else {
77 entry->last_attempt_version = 0;
78 entry->last_attempt_status = LAST_ATTEMPT_STATUS_SUCCESS;
79 }
80
81 return EFI_SUCCESS;
82}
83
84/**
85 * efi_esrt_entries_to_size() - Obtain the bytes used by an ESRT
86 * datastructure with @num_entries.
87 *
88 * @num_entries: the number of entries in the ESRT.
89 *
90 * Return: the number of bytes an ESRT with @num_entries occupies in memory.
91 */
92static
93inline u32 efi_esrt_entries_to_size(u32 num_entries)
94{
95 u32 esrt_size = sizeof(struct efi_system_resource_table) +
96 num_entries * sizeof(struct efi_system_resource_entry);
97
98 return esrt_size;
99}
100
101/**
102 * efi_esrt_allocate_install() - Allocates @num_entries for the ESRT and
103 * performs basic ESRT initialization.
104 *
105 * @num_entries: the number of entries that the ESRT will hold.
106 *
107 * Return:
108 * - pointer to the ESRT if successful.
109 * - NULL otherwise.
110 */
111static
112efi_status_t efi_esrt_allocate_install(u32 num_entries)
113{
114 efi_status_t ret;
115 struct efi_system_resource_table *new_esrt;
116 u32 size = efi_esrt_entries_to_size(num_entries);
117 efi_guid_t esrt_guid = efi_esrt_guid;
118
Heinrich Schuchardt05f785b2023-01-24 00:25:23 +0100119 /* Reserve memory for ESRT */
Jose Marinhoebb61ee2021-03-02 17:26:38 +0000120 ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, size,
121 (void **)&new_esrt);
122
123 if (ret != EFI_SUCCESS) {
Heinrich Schuchardt60f44092021-04-08 12:15:26 +0200124 EFI_PRINT("ESRT cannot allocate memory for %u entries (%u bytes)\n",
125 num_entries, size);
Jose Marinhoebb61ee2021-03-02 17:26:38 +0000126
127 return ret;
128 }
129
130 new_esrt->fw_resource_count_max = num_entries;
131 new_esrt->fw_resource_count = 0;
132 new_esrt->fw_resource_version = EFI_ESRT_VERSION;
133
134 /* Install the ESRT in the system configuration table. */
135 ret = efi_install_configuration_table(&esrt_guid, (void *)new_esrt);
136 if (ret != EFI_SUCCESS) {
137 EFI_PRINT("ESRT failed to install the ESRT in the system table\n");
138 return ret;
139 }
140
141 /* If there was a previous ESRT, deallocate its memory now. */
142 if (esrt)
Sughosh Ganu15543522021-04-14 12:38:25 +0530143 ret = efi_free_pool(esrt);
Jose Marinhoebb61ee2021-03-02 17:26:38 +0000144
145 esrt = new_esrt;
146
147 return EFI_SUCCESS;
148}
149
150/**
151 * esrt_find_entry() - Obtain the ESRT entry for the image with GUID
152 * @img_fw_class.
153 *
154 * If the img_fw_class is not yet present in the ESRT, this function
155 * reserves the tail element of the current ESRT as the entry for that fw_class.
156 * The number of elements in the ESRT is updated in that case.
157 *
158 * @img_fw_class: the GUID of the FW image which ESRT entry we want to obtain.
159 *
160 * Return:
161 * - A pointer to the ESRT entry for the image with GUID img_fw_class,
162 * - NULL if:
163 * - there is no more space in the ESRT,
164 * - ESRT is not initialized,
165 */
166static
167struct efi_system_resource_entry *esrt_find_entry(efi_guid_t *img_fw_class)
168{
169 u32 filled_entries;
170 u32 max_entries;
171 struct efi_system_resource_entry *entry;
172
173 if (!esrt) {
174 EFI_PRINT("ESRT access before initialized\n");
175 return NULL;
176 }
177
178 filled_entries = esrt->fw_resource_count;
179 entry = esrt->entries;
180
181 /* Check if the image with img_fw_class is already in the ESRT. */
182 for (u32 idx = 0; idx < filled_entries; idx++) {
183 if (!guidcmp(&entry[idx].fw_class, img_fw_class)) {
Heinrich Schuchardt282249d2022-01-16 14:15:31 +0100184 EFI_PRINT("ESRT found entry for image %pUs at index %u\n",
Jose Marinhoebb61ee2021-03-02 17:26:38 +0000185 img_fw_class, idx);
186 return &entry[idx];
187 }
188 }
189
190 max_entries = esrt->fw_resource_count_max;
191 /*
192 * Since the image with img_fw_class is not present in the ESRT, check
193 * if ESRT is full before appending the new entry to it.
194 */
195 if (filled_entries == max_entries) {
196 EFI_PRINT("ESRT full, this should not happen\n");
197 return NULL;
198 }
199
200 /*
201 * This is a new entry for a fw image, increment the element
202 * number in the table and set the fw_class field.
203 */
204 esrt->fw_resource_count++;
205 entry[filled_entries].fw_class = *img_fw_class;
Heinrich Schuchardt282249d2022-01-16 14:15:31 +0100206 EFI_PRINT("ESRT allocated new entry for image %pUs at index %u\n",
Jose Marinhoebb61ee2021-03-02 17:26:38 +0000207 img_fw_class, filled_entries);
208
209 return &entry[filled_entries];
210}
211
212/**
213 * efi_esrt_add_from_fmp() - Populates a sequence of ESRT entries from the FW
214 * images in the FMP.
215 *
216 * @fmp: the FMP instance from which FW images are added to the ESRT
217 *
218 * Return:
219 * - EFI_SUCCESS if all the FW images in the FMP are added to the ESRT
220 * - Error status otherwise
221 */
222static
223efi_status_t efi_esrt_add_from_fmp(struct efi_firmware_management_protocol *fmp)
224{
225 struct efi_system_resource_entry *entry = NULL;
226 size_t info_size = 0;
227 struct efi_firmware_image_descriptor *img_info = NULL;
228 u32 desc_version;
229 u8 desc_count;
230 size_t desc_size;
231 u32 package_version;
232 u16 *package_version_name;
233 efi_status_t ret = EFI_SUCCESS;
234
235 /*
236 * TODO: set the field image_type depending on the FW image type
237 * defined in a platform basis.
238 */
239 u32 image_type = ESRT_FW_TYPE_UNKNOWN;
240
241 /* TODO: set the capsule flags as a function of the FW image type. */
242 u32 flags = 0;
243
244 ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, img_info,
245 &desc_version, &desc_count,
246 &desc_size, NULL, NULL));
247
248 if (ret != EFI_BUFFER_TOO_SMALL) {
249 /*
250 * An input of info_size=0 should always lead
251 * fmp->get_image_info to return BUFFER_TO_SMALL.
252 */
253 EFI_PRINT("Erroneous FMP implementation\n");
254 return EFI_INVALID_PARAMETER;
255 }
256
Sughosh Ganu15543522021-04-14 12:38:25 +0530257 ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, info_size,
258 (void **)&img_info);
Jose Marinhoebb61ee2021-03-02 17:26:38 +0000259 if (ret != EFI_SUCCESS) {
260 EFI_PRINT("ESRT failed to allocate memory for image info.\n");
261 return ret;
262 }
263
264 ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, img_info,
265 &desc_version, &desc_count,
266 &desc_size, &package_version,
267 &package_version_name));
268 if (ret != EFI_SUCCESS) {
269 EFI_PRINT("ESRT failed to obtain the FMP image info\n");
270 goto out;
271 }
272
273 /*
274 * Iterate over all the FW images in the FMP.
275 */
276 for (u32 desc_idx = 0; desc_idx < desc_count; desc_idx++) {
277 struct efi_firmware_image_descriptor *cur_img_info =
278 (struct efi_firmware_image_descriptor *)
279 ((uintptr_t)img_info + desc_idx * desc_size);
280
281 /*
282 * Obtain the ESRT entry for the FW image with fw_class
283 * equal to cur_img_info->image_type_id.
284 */
285 entry = esrt_find_entry(&cur_img_info->image_type_id);
286
287 if (entry) {
288 ret = efi_esrt_image_info_to_entry(cur_img_info, entry,
289 desc_version,
290 image_type, flags);
291 if (ret != EFI_SUCCESS)
292 EFI_PRINT("ESRT entry mismatches image_type\n");
293
294 } else {
Heinrich Schuchardt282249d2022-01-16 14:15:31 +0100295 EFI_PRINT("ESRT failed to add entry for %pUs\n",
Jose Marinhoebb61ee2021-03-02 17:26:38 +0000296 &cur_img_info->image_type_id);
297 continue;
298 }
299 }
300
301out:
Sughosh Ganu15543522021-04-14 12:38:25 +0530302 efi_free_pool(img_info);
Jose Marinhoebb61ee2021-03-02 17:26:38 +0000303 return EFI_SUCCESS;
304}
305
306/**
307 * efi_esrt_populate() - Populates the ESRT entries from the FMP instances
308 * present in the system.
309 * If an ESRT already exists, the old ESRT is replaced in the system table.
310 * The memory of the old ESRT is deallocated.
311 *
312 * Return:
313 * - EFI_SUCCESS if the ESRT is correctly created
314 * - error code otherwise.
315 */
316efi_status_t efi_esrt_populate(void)
317{
318 efi_handle_t *base_handle = NULL;
319 efi_handle_t *it_handle;
Heinrich Schuchardt13ddf772021-04-08 09:15:52 +0200320 efi_uintn_t no_handles = 0;
Jose Marinhoebb61ee2021-03-02 17:26:38 +0000321 struct efi_firmware_management_protocol *fmp;
322 efi_status_t ret;
323 u32 num_entries = 0;
324 struct efi_handler *handler;
325
326 /*
327 * Obtain the number of registered FMP handles.
328 */
329 ret = EFI_CALL(efi_locate_handle_buffer(BY_PROTOCOL,
330 &efi_guid_firmware_management_protocol,
331 NULL, &no_handles,
332 (efi_handle_t **)&base_handle));
333
334 if (ret != EFI_SUCCESS) {
335 EFI_PRINT("ESRT There are no FMP instances\n");
336
337 ret = efi_esrt_allocate_install(0);
338 if (ret != EFI_SUCCESS) {
339 EFI_PRINT("ESRT failed to create table with 0 entries\n");
340 return ret;
341 }
342 return EFI_SUCCESS;
343 }
344
Sughosh Ganu2fa98662021-04-08 12:30:55 +0530345 EFI_PRINT("ESRT populate esrt from (%zd) available FMP handles\n",
Jose Marinhoebb61ee2021-03-02 17:26:38 +0000346 no_handles);
347
348 /*
349 * Iterate over all FMPs to determine an upper bound on the number of
350 * ESRT entries.
351 */
352 it_handle = base_handle;
353 for (u32 idx = 0; idx < no_handles; idx++, it_handle++) {
354 struct efi_firmware_image_descriptor *img_info = NULL;
355 size_t info_size = 0;
356 u32 desc_version = 0;
357 u8 desc_count = 0;
358 size_t desc_size = 0;
359 u32 package_version;
360 u16 *package_version_name;
361
362 ret = efi_search_protocol(*it_handle,
363 &efi_guid_firmware_management_protocol,
364 &handler);
365
366 if (ret != EFI_SUCCESS) {
Heinrich Schuchardt60f44092021-04-08 12:15:26 +0200367 EFI_PRINT("ESRT Unable to find FMP handle (%u)\n",
Jose Marinhoebb61ee2021-03-02 17:26:38 +0000368 idx);
Masahisa Kojima57087602023-12-18 18:57:41 +0900369 continue;
Jose Marinhoebb61ee2021-03-02 17:26:38 +0000370 }
371 fmp = handler->protocol_interface;
372
373 ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, NULL,
374 &desc_version, &desc_count,
375 &desc_size, &package_version,
376 &package_version_name));
377
378 if (ret != EFI_BUFFER_TOO_SMALL) {
379 /*
380 * An input of info_size=0 should always lead
381 * fmp->get_image_info to return BUFFER_TO_SMALL.
382 */
383 EFI_PRINT("ESRT erroneous FMP implementation\n");
Masahisa Kojima57087602023-12-18 18:57:41 +0900384 continue;
Jose Marinhoebb61ee2021-03-02 17:26:38 +0000385 }
386
Sughosh Ganu15543522021-04-14 12:38:25 +0530387 ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, info_size,
388 (void **)&img_info);
Jose Marinhoebb61ee2021-03-02 17:26:38 +0000389 if (ret != EFI_SUCCESS) {
390 EFI_PRINT("ESRT failed to allocate memory for image info\n");
Masahisa Kojima57087602023-12-18 18:57:41 +0900391 continue;
Jose Marinhoebb61ee2021-03-02 17:26:38 +0000392 }
393
394 /*
395 * Calls to a FMP get_image_info method do not return the
396 * desc_count value if the return status differs from EFI_SUCCESS.
397 * We need to repeat the call to get_image_info with a properly
398 * sized buffer in order to obtain the real number of images
399 * handled by the FMP.
400 */
401 ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, img_info,
402 &desc_version, &desc_count,
403 &desc_size, &package_version,
404 &package_version_name));
405
406 if (ret != EFI_SUCCESS) {
407 EFI_PRINT("ESRT failed to obtain image info from FMP\n");
Sughosh Ganu15543522021-04-14 12:38:25 +0530408 efi_free_pool(img_info);
Masahisa Kojima57087602023-12-18 18:57:41 +0900409 continue;
Jose Marinhoebb61ee2021-03-02 17:26:38 +0000410 }
411
412 num_entries += desc_count;
413
Sughosh Ganu15543522021-04-14 12:38:25 +0530414 efi_free_pool(img_info);
Jose Marinhoebb61ee2021-03-02 17:26:38 +0000415 }
416
Masahisa Kojima57087602023-12-18 18:57:41 +0900417 /* error occurs in fmp->get_image_info() if num_entries is 0 here */
418 if (!num_entries) {
419 EFI_PRINT("Error occurs, num_entries should not be 0\n");
420 ret = EFI_INVALID_PARAMETER;
421 goto out;
422 }
423
Heinrich Schuchardt60f44092021-04-08 12:15:26 +0200424 EFI_PRINT("ESRT create table with %u entries\n", num_entries);
Jose Marinhoebb61ee2021-03-02 17:26:38 +0000425 /*
426 * Allocate an ESRT with the sufficient number of entries to accommodate
427 * all the FMPs in the system.
428 */
429 ret = efi_esrt_allocate_install(num_entries);
430 if (ret != EFI_SUCCESS) {
431 EFI_PRINT("ESRT failed to initialize table\n");
432 goto out;
433 }
434
435 /*
436 * Populate the ESRT entries with all existing FMP.
437 */
438 it_handle = base_handle;
439 for (u32 idx = 0; idx < no_handles; idx++, it_handle++) {
Sughosh Ganu15543522021-04-14 12:38:25 +0530440 ret = efi_search_protocol(*it_handle,
441 &efi_guid_firmware_management_protocol,
442 &handler);
Jose Marinhoebb61ee2021-03-02 17:26:38 +0000443
444 if (ret != EFI_SUCCESS) {
Heinrich Schuchardt60f44092021-04-08 12:15:26 +0200445 EFI_PRINT("ESRT unable to find FMP handle (%u)\n",
Jose Marinhoebb61ee2021-03-02 17:26:38 +0000446 idx);
Masahisa Kojima57087602023-12-18 18:57:41 +0900447 continue;
Jose Marinhoebb61ee2021-03-02 17:26:38 +0000448 }
449 fmp = handler->protocol_interface;
450
451 ret = efi_esrt_add_from_fmp(fmp);
452 if (ret != EFI_SUCCESS)
453 EFI_PRINT("ESRT failed to add FMP to the table\n");
454 }
455
456out:
457
Sughosh Ganu15543522021-04-14 12:38:25 +0530458 efi_free_pool(base_handle);
Jose Marinhoebb61ee2021-03-02 17:26:38 +0000459
460 return ret;
461}
462
463/**
464 * efi_esrt_new_fmp_notify() - Callback for the EVT_NOTIFY_SIGNAL event raised
465 * when a new FMP protocol instance is registered in the system.
466 */
467static void EFIAPI efi_esrt_new_fmp_notify(struct efi_event *event,
468 void *context)
469{
470 efi_status_t ret;
471
472 EFI_ENTRY();
473
474 ret = efi_esrt_populate();
475 if (ret != EFI_SUCCESS)
476 EFI_PRINT("ESRT failed to populate ESRT entry\n");
477
478 EFI_EXIT(ret);
479}
480
481/**
482 * efi_esrt_register() - Install the ESRT system table.
483 *
484 * Return: status code
485 */
486efi_status_t efi_esrt_register(void)
487{
488 struct efi_event *ev = NULL;
489 void *registration;
490 efi_status_t ret;
491
492 EFI_PRINT("ESRT creation start\n");
493
494 ret = efi_esrt_populate();
495 if (ret != EFI_SUCCESS) {
496 EFI_PRINT("ESRT failed to initiate the table\n");
497 return ret;
498 }
499
Sughosh Ganu15543522021-04-14 12:38:25 +0530500 ret = efi_create_event(EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
501 efi_esrt_new_fmp_notify, NULL, NULL, &ev);
Jose Marinhoebb61ee2021-03-02 17:26:38 +0000502 if (ret != EFI_SUCCESS) {
503 EFI_PRINT("ESRT failed to create event\n");
504 return ret;
505 }
506
507 ret = EFI_CALL(efi_register_protocol_notify(&efi_guid_firmware_management_protocol,
508 ev, &registration));
509 if (ret != EFI_SUCCESS) {
510 EFI_PRINT("ESRT failed to register FMP callback\n");
511 return ret;
512 }
513
514 EFI_PRINT("ESRT table created\n");
515
516 return ret;
517}