uboot/lib/efi_loader/efi_esrt.c
<<
>>
Prefs
   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
  14const efi_guid_t efi_esrt_guid = EFI_SYSTEM_RESOURCE_TABLE_GUID;
  15
  16static 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 */
  36static efi_status_t
  37efi_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 */
  91static
  92inline 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 */
 110static
 111efi_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 %u entries (%u bytes)\n",
 124                          num_entries, size);
 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_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 */
 165static
 166struct 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 %u\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 %u\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 */
 221static
 222efi_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_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
 300out:
 301        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 */
 315efi_status_t efi_esrt_populate(void)
 316{
 317        efi_handle_t *base_handle = NULL;
 318        efi_handle_t *it_handle;
 319        efi_uintn_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 (%zd) 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 (%u)\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_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_free_pool(img_info);
 409                        goto out;
 410                }
 411
 412                num_entries += desc_count;
 413
 414                efi_free_pool(img_info);
 415        }
 416
 417        EFI_PRINT("ESRT create table with %u 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_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 (%u)\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
 449out:
 450
 451        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 */
 460static 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 */
 479efi_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_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}
 511