blob: 443bd999ce11395bbef8182204f269ee83b92ce3 [file] [log] [blame]
Jose Marinho64a8aae2021-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
Jose Marinho64a8aae2021-03-02 17:26:38 +00008#include <efi_loader.h>
9#include <log.h>
10#include <efi_api.h>
11#include <malloc.h>
12
13const efi_guid_t efi_esrt_guid = EFI_SYSTEM_RESOURCE_TABLE_GUID;
14
15static struct efi_system_resource_table *esrt;
16
17#define EFI_ESRT_VERSION 1
18
19/**
20 * efi_esrt_image_info_to_entry() - copy the information present in a fw image
21 * descriptor to a ESRT entry.
22 * The function ensures the ESRT entry matches the image_type_id in @img_info.
23 * In case of a mismatch we leave the entry unchanged.
24 *
25 * @img_info: the source image info descriptor
26 * @entry: pointer to the ESRT entry to be filled
27 * @desc_version: the version of the elements in img_info
28 * @image_type: the image type value to be set in the ESRT entry
29 * @flags: the capsule flags value to be set in the ESRT entry
30 *
31 * Return:
32 * - EFI_SUCCESS if the entry is correctly updated
33 * - EFI_INVALID_PARAMETER if entry does not match image_type_id in @img_info.
34 */
35static efi_status_t
36efi_esrt_image_info_to_entry(struct efi_firmware_image_descriptor *img_info,
37 struct efi_system_resource_entry *entry,
38 u32 desc_version, u32 image_type, u32 flags)
39{
40 if (guidcmp(&entry->fw_class, &img_info->image_type_id)) {
41 EFI_PRINT("ESRT entry %pUL mismatches img_type_id %pUL\n",
42 &entry->fw_class, &img_info->image_type_id);
43 return EFI_INVALID_PARAMETER;
44 }
45
46 entry->fw_version = img_info->version;
47
48 entry->fw_type = image_type;
49 entry->capsule_flags = flags;
50
51 /*
52 * The field lowest_supported_image_version is only present
53 * on image info structure of version 2 or greater.
54 * See the EFI_FIRMWARE_IMAGE_DESCRIPTOR definition in UEFI.
55 */
56 if (desc_version >= 2)
57 entry->lowest_supported_fw_version =
58 img_info->lowest_supported_image_version;
59 else
60 entry->lowest_supported_fw_version = 0;
61
62 /*
63 * The fields last_attempt_version and last_attempt_status
64 * are only present on image info structure of version 3 or
65 * greater.
66 * See the EFI_FIRMWARE_IMAGE_DESCRIPTOR definition in UEFI.
67 */
68 if (desc_version >= 3) {
69 entry->last_attempt_version =
70 img_info->last_attempt_version;
71
72 entry->last_attempt_status =
73 img_info->last_attempt_status;
74 } else {
75 entry->last_attempt_version = 0;
76 entry->last_attempt_status = LAST_ATTEMPT_STATUS_SUCCESS;
77 }
78
79 return EFI_SUCCESS;
80}
81
82/**
83 * efi_esrt_entries_to_size() - Obtain the bytes used by an ESRT
84 * datastructure with @num_entries.
85 *
86 * @num_entries: the number of entries in the ESRT.
87 *
88 * Return: the number of bytes an ESRT with @num_entries occupies in memory.
89 */
90static
91inline u32 efi_esrt_entries_to_size(u32 num_entries)
92{
93 u32 esrt_size = sizeof(struct efi_system_resource_table) +
94 num_entries * sizeof(struct efi_system_resource_entry);
95
96 return esrt_size;
97}
98
99/**
100 * efi_esrt_allocate_install() - Allocates @num_entries for the ESRT and
101 * performs basic ESRT initialization.
102 *
103 * @num_entries: the number of entries that the ESRT will hold.
104 *
105 * Return:
106 * - pointer to the ESRT if successful.
107 * - NULL otherwise.
108 */
109static
110efi_status_t efi_esrt_allocate_install(u32 num_entries)
111{
112 efi_status_t ret;
113 struct efi_system_resource_table *new_esrt;
114 u32 size = efi_esrt_entries_to_size(num_entries);
115 efi_guid_t esrt_guid = efi_esrt_guid;
116
Heinrich Schuchardtcb50dda2023-01-24 00:25:23 +0100117 /* Reserve memory for ESRT */
Jose Marinho64a8aae2021-03-02 17:26:38 +0000118 ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, size,
119 (void **)&new_esrt);
120
121 if (ret != EFI_SUCCESS) {
Heinrich Schuchardt7631c9d2021-04-08 12:15:26 +0200122 EFI_PRINT("ESRT cannot allocate memory for %u entries (%u bytes)\n",
123 num_entries, size);
Jose Marinho64a8aae2021-03-02 17:26:38 +0000124
125 return ret;
126 }
127
128 new_esrt->fw_resource_count_max = num_entries;
129 new_esrt->fw_resource_count = 0;
130 new_esrt->fw_resource_version = EFI_ESRT_VERSION;
131
132 /* Install the ESRT in the system configuration table. */
133 ret = efi_install_configuration_table(&esrt_guid, (void *)new_esrt);
134 if (ret != EFI_SUCCESS) {
135 EFI_PRINT("ESRT failed to install the ESRT in the system table\n");
136 return ret;
137 }
138
139 /* If there was a previous ESRT, deallocate its memory now. */
140 if (esrt)
Sughosh Ganu7d823742021-04-14 12:38:25 +0530141 ret = efi_free_pool(esrt);
Jose Marinho64a8aae2021-03-02 17:26:38 +0000142
143 esrt = new_esrt;
144
145 return EFI_SUCCESS;
146}
147
148/**
149 * esrt_find_entry() - Obtain the ESRT entry for the image with GUID
150 * @img_fw_class.
151 *
152 * If the img_fw_class is not yet present in the ESRT, this function
153 * reserves the tail element of the current ESRT as the entry for that fw_class.
154 * The number of elements in the ESRT is updated in that case.
155 *
156 * @img_fw_class: the GUID of the FW image which ESRT entry we want to obtain.
157 *
158 * Return:
159 * - A pointer to the ESRT entry for the image with GUID img_fw_class,
160 * - NULL if:
161 * - there is no more space in the ESRT,
162 * - ESRT is not initialized,
163 */
164static
165struct efi_system_resource_entry *esrt_find_entry(efi_guid_t *img_fw_class)
166{
167 u32 filled_entries;
168 u32 max_entries;
169 struct efi_system_resource_entry *entry;
170
171 if (!esrt) {
172 EFI_PRINT("ESRT access before initialized\n");
173 return NULL;
174 }
175
176 filled_entries = esrt->fw_resource_count;
177 entry = esrt->entries;
178
179 /* Check if the image with img_fw_class is already in the ESRT. */
180 for (u32 idx = 0; idx < filled_entries; idx++) {
181 if (!guidcmp(&entry[idx].fw_class, img_fw_class)) {
Heinrich Schuchardtce00a742022-01-16 14:15:31 +0100182 EFI_PRINT("ESRT found entry for image %pUs at index %u\n",
Jose Marinho64a8aae2021-03-02 17:26:38 +0000183 img_fw_class, idx);
184 return &entry[idx];
185 }
186 }
187
188 max_entries = esrt->fw_resource_count_max;
189 /*
190 * Since the image with img_fw_class is not present in the ESRT, check
191 * if ESRT is full before appending the new entry to it.
192 */
193 if (filled_entries == max_entries) {
194 EFI_PRINT("ESRT full, this should not happen\n");
195 return NULL;
196 }
197
198 /*
199 * This is a new entry for a fw image, increment the element
200 * number in the table and set the fw_class field.
201 */
202 esrt->fw_resource_count++;
203 entry[filled_entries].fw_class = *img_fw_class;
Heinrich Schuchardtce00a742022-01-16 14:15:31 +0100204 EFI_PRINT("ESRT allocated new entry for image %pUs at index %u\n",
Jose Marinho64a8aae2021-03-02 17:26:38 +0000205 img_fw_class, filled_entries);
206
207 return &entry[filled_entries];
208}
209
210/**
211 * efi_esrt_add_from_fmp() - Populates a sequence of ESRT entries from the FW
212 * images in the FMP.
213 *
214 * @fmp: the FMP instance from which FW images are added to the ESRT
215 *
216 * Return:
217 * - EFI_SUCCESS if all the FW images in the FMP are added to the ESRT
218 * - Error status otherwise
219 */
220static
221efi_status_t efi_esrt_add_from_fmp(struct efi_firmware_management_protocol *fmp)
222{
223 struct efi_system_resource_entry *entry = NULL;
224 size_t info_size = 0;
225 struct efi_firmware_image_descriptor *img_info = NULL;
226 u32 desc_version;
227 u8 desc_count;
228 size_t desc_size;
229 u32 package_version;
230 u16 *package_version_name;
231 efi_status_t ret = EFI_SUCCESS;
232
233 /*
234 * TODO: set the field image_type depending on the FW image type
235 * defined in a platform basis.
236 */
237 u32 image_type = ESRT_FW_TYPE_UNKNOWN;
238
239 /* TODO: set the capsule flags as a function of the FW image type. */
240 u32 flags = 0;
241
242 ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, img_info,
243 &desc_version, &desc_count,
244 &desc_size, NULL, NULL));
245
246 if (ret != EFI_BUFFER_TOO_SMALL) {
247 /*
248 * An input of info_size=0 should always lead
249 * fmp->get_image_info to return BUFFER_TO_SMALL.
250 */
251 EFI_PRINT("Erroneous FMP implementation\n");
252 return EFI_INVALID_PARAMETER;
253 }
254
Sughosh Ganu7d823742021-04-14 12:38:25 +0530255 ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, info_size,
256 (void **)&img_info);
Jose Marinho64a8aae2021-03-02 17:26:38 +0000257 if (ret != EFI_SUCCESS) {
258 EFI_PRINT("ESRT failed to allocate memory for image info.\n");
259 return ret;
260 }
261
262 ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, img_info,
263 &desc_version, &desc_count,
264 &desc_size, &package_version,
265 &package_version_name));
266 if (ret != EFI_SUCCESS) {
267 EFI_PRINT("ESRT failed to obtain the FMP image info\n");
268 goto out;
269 }
270
271 /*
272 * Iterate over all the FW images in the FMP.
273 */
274 for (u32 desc_idx = 0; desc_idx < desc_count; desc_idx++) {
275 struct efi_firmware_image_descriptor *cur_img_info =
276 (struct efi_firmware_image_descriptor *)
277 ((uintptr_t)img_info + desc_idx * desc_size);
278
279 /*
280 * Obtain the ESRT entry for the FW image with fw_class
281 * equal to cur_img_info->image_type_id.
282 */
283 entry = esrt_find_entry(&cur_img_info->image_type_id);
284
285 if (entry) {
286 ret = efi_esrt_image_info_to_entry(cur_img_info, entry,
287 desc_version,
288 image_type, flags);
289 if (ret != EFI_SUCCESS)
290 EFI_PRINT("ESRT entry mismatches image_type\n");
291
292 } else {
Heinrich Schuchardtce00a742022-01-16 14:15:31 +0100293 EFI_PRINT("ESRT failed to add entry for %pUs\n",
Jose Marinho64a8aae2021-03-02 17:26:38 +0000294 &cur_img_info->image_type_id);
295 continue;
296 }
297 }
298
299out:
Sughosh Ganu7d823742021-04-14 12:38:25 +0530300 efi_free_pool(img_info);
Jose Marinho64a8aae2021-03-02 17:26:38 +0000301 return EFI_SUCCESS;
302}
303
304/**
305 * efi_esrt_populate() - Populates the ESRT entries from the FMP instances
306 * present in the system.
307 * If an ESRT already exists, the old ESRT is replaced in the system table.
308 * The memory of the old ESRT is deallocated.
309 *
310 * Return:
311 * - EFI_SUCCESS if the ESRT is correctly created
312 * - error code otherwise.
313 */
314efi_status_t efi_esrt_populate(void)
315{
316 efi_handle_t *base_handle = NULL;
317 efi_handle_t *it_handle;
Heinrich Schuchardt8c448862021-04-08 09:15:52 +0200318 efi_uintn_t no_handles = 0;
Jose Marinho64a8aae2021-03-02 17:26:38 +0000319 struct efi_firmware_management_protocol *fmp;
320 efi_status_t ret;
321 u32 num_entries = 0;
322 struct efi_handler *handler;
323
324 /*
325 * Obtain the number of registered FMP handles.
326 */
327 ret = EFI_CALL(efi_locate_handle_buffer(BY_PROTOCOL,
328 &efi_guid_firmware_management_protocol,
329 NULL, &no_handles,
330 (efi_handle_t **)&base_handle));
331
332 if (ret != EFI_SUCCESS) {
333 EFI_PRINT("ESRT There are no FMP instances\n");
334
335 ret = efi_esrt_allocate_install(0);
336 if (ret != EFI_SUCCESS) {
337 EFI_PRINT("ESRT failed to create table with 0 entries\n");
338 return ret;
339 }
340 return EFI_SUCCESS;
341 }
342
Sughosh Ganu5c3de472021-04-08 12:30:55 +0530343 EFI_PRINT("ESRT populate esrt from (%zd) available FMP handles\n",
Jose Marinho64a8aae2021-03-02 17:26:38 +0000344 no_handles);
345
346 /*
347 * Iterate over all FMPs to determine an upper bound on the number of
348 * ESRT entries.
349 */
350 it_handle = base_handle;
351 for (u32 idx = 0; idx < no_handles; idx++, it_handle++) {
352 struct efi_firmware_image_descriptor *img_info = NULL;
353 size_t info_size = 0;
354 u32 desc_version = 0;
355 u8 desc_count = 0;
356 size_t desc_size = 0;
357 u32 package_version;
358 u16 *package_version_name;
359
360 ret = efi_search_protocol(*it_handle,
361 &efi_guid_firmware_management_protocol,
362 &handler);
363
364 if (ret != EFI_SUCCESS) {
Heinrich Schuchardt7631c9d2021-04-08 12:15:26 +0200365 EFI_PRINT("ESRT Unable to find FMP handle (%u)\n",
Jose Marinho64a8aae2021-03-02 17:26:38 +0000366 idx);
Masahisa Kojima5a0970e2023-12-18 18:57:41 +0900367 continue;
Jose Marinho64a8aae2021-03-02 17:26:38 +0000368 }
369 fmp = handler->protocol_interface;
370
371 ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, NULL,
372 &desc_version, &desc_count,
373 &desc_size, &package_version,
374 &package_version_name));
375
376 if (ret != EFI_BUFFER_TOO_SMALL) {
377 /*
378 * An input of info_size=0 should always lead
379 * fmp->get_image_info to return BUFFER_TO_SMALL.
380 */
381 EFI_PRINT("ESRT erroneous FMP implementation\n");
Masahisa Kojima5a0970e2023-12-18 18:57:41 +0900382 continue;
Jose Marinho64a8aae2021-03-02 17:26:38 +0000383 }
384
Sughosh Ganu7d823742021-04-14 12:38:25 +0530385 ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, info_size,
386 (void **)&img_info);
Jose Marinho64a8aae2021-03-02 17:26:38 +0000387 if (ret != EFI_SUCCESS) {
388 EFI_PRINT("ESRT failed to allocate memory for image info\n");
Masahisa Kojima5a0970e2023-12-18 18:57:41 +0900389 continue;
Jose Marinho64a8aae2021-03-02 17:26:38 +0000390 }
391
392 /*
393 * Calls to a FMP get_image_info method do not return the
394 * desc_count value if the return status differs from EFI_SUCCESS.
395 * We need to repeat the call to get_image_info with a properly
396 * sized buffer in order to obtain the real number of images
397 * handled by the FMP.
398 */
399 ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, img_info,
400 &desc_version, &desc_count,
401 &desc_size, &package_version,
402 &package_version_name));
403
404 if (ret != EFI_SUCCESS) {
405 EFI_PRINT("ESRT failed to obtain image info from FMP\n");
Sughosh Ganu7d823742021-04-14 12:38:25 +0530406 efi_free_pool(img_info);
Masahisa Kojima5a0970e2023-12-18 18:57:41 +0900407 continue;
Jose Marinho64a8aae2021-03-02 17:26:38 +0000408 }
409
410 num_entries += desc_count;
411
Sughosh Ganu7d823742021-04-14 12:38:25 +0530412 efi_free_pool(img_info);
Jose Marinho64a8aae2021-03-02 17:26:38 +0000413 }
414
Masahisa Kojima5a0970e2023-12-18 18:57:41 +0900415 /* error occurs in fmp->get_image_info() if num_entries is 0 here */
416 if (!num_entries) {
417 EFI_PRINT("Error occurs, num_entries should not be 0\n");
418 ret = EFI_INVALID_PARAMETER;
419 goto out;
420 }
421
Heinrich Schuchardt7631c9d2021-04-08 12:15:26 +0200422 EFI_PRINT("ESRT create table with %u entries\n", num_entries);
Jose Marinho64a8aae2021-03-02 17:26:38 +0000423 /*
424 * Allocate an ESRT with the sufficient number of entries to accommodate
425 * all the FMPs in the system.
426 */
427 ret = efi_esrt_allocate_install(num_entries);
428 if (ret != EFI_SUCCESS) {
429 EFI_PRINT("ESRT failed to initialize table\n");
430 goto out;
431 }
432
433 /*
434 * Populate the ESRT entries with all existing FMP.
435 */
436 it_handle = base_handle;
437 for (u32 idx = 0; idx < no_handles; idx++, it_handle++) {
Sughosh Ganu7d823742021-04-14 12:38:25 +0530438 ret = efi_search_protocol(*it_handle,
439 &efi_guid_firmware_management_protocol,
440 &handler);
Jose Marinho64a8aae2021-03-02 17:26:38 +0000441
442 if (ret != EFI_SUCCESS) {
Heinrich Schuchardt7631c9d2021-04-08 12:15:26 +0200443 EFI_PRINT("ESRT unable to find FMP handle (%u)\n",
Jose Marinho64a8aae2021-03-02 17:26:38 +0000444 idx);
Masahisa Kojima5a0970e2023-12-18 18:57:41 +0900445 continue;
Jose Marinho64a8aae2021-03-02 17:26:38 +0000446 }
447 fmp = handler->protocol_interface;
448
449 ret = efi_esrt_add_from_fmp(fmp);
450 if (ret != EFI_SUCCESS)
451 EFI_PRINT("ESRT failed to add FMP to the table\n");
452 }
453
454out:
455
Sughosh Ganu7d823742021-04-14 12:38:25 +0530456 efi_free_pool(base_handle);
Jose Marinho64a8aae2021-03-02 17:26:38 +0000457
458 return ret;
459}
460
461/**
462 * efi_esrt_new_fmp_notify() - Callback for the EVT_NOTIFY_SIGNAL event raised
463 * when a new FMP protocol instance is registered in the system.
464 */
465static void EFIAPI efi_esrt_new_fmp_notify(struct efi_event *event,
466 void *context)
467{
468 efi_status_t ret;
469
470 EFI_ENTRY();
471
472 ret = efi_esrt_populate();
473 if (ret != EFI_SUCCESS)
474 EFI_PRINT("ESRT failed to populate ESRT entry\n");
475
476 EFI_EXIT(ret);
477}
478
479/**
480 * efi_esrt_register() - Install the ESRT system table.
481 *
482 * Return: status code
483 */
484efi_status_t efi_esrt_register(void)
485{
486 struct efi_event *ev = NULL;
487 void *registration;
488 efi_status_t ret;
489
490 EFI_PRINT("ESRT creation start\n");
491
492 ret = efi_esrt_populate();
493 if (ret != EFI_SUCCESS) {
494 EFI_PRINT("ESRT failed to initiate the table\n");
495 return ret;
496 }
497
Sughosh Ganu7d823742021-04-14 12:38:25 +0530498 ret = efi_create_event(EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
499 efi_esrt_new_fmp_notify, NULL, NULL, &ev);
Jose Marinho64a8aae2021-03-02 17:26:38 +0000500 if (ret != EFI_SUCCESS) {
501 EFI_PRINT("ESRT failed to create event\n");
502 return ret;
503 }
504
505 ret = EFI_CALL(efi_register_protocol_notify(&efi_guid_firmware_management_protocol,
506 ev, &registration));
507 if (ret != EFI_SUCCESS) {
508 EFI_PRINT("ESRT failed to register FMP callback\n");
509 return ret;
510 }
511
512 EFI_PRINT("ESRT table created\n");
513
514 return ret;
515}