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