| /** @file | |
| Publishes ESRT table from Firmware Management Protocol instances | |
| Copyright (c) 2016, Microsoft Corporation | |
| Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR> | |
| All rights reserved. | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include <Uefi.h> | |
| #include <Library/BaseLib.h> | |
| #include <Library/BaseMemoryLib.h> | |
| #include <Library/MemoryAllocationLib.h> | |
| #include <Library/UefiBootServicesTableLib.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/PcdLib.h> | |
| #include <Library/UefiLib.h> | |
| #include <Protocol/FirmwareManagement.h> | |
| #include <Guid/EventGroup.h> | |
| #include <Guid/SystemResourceTable.h> | |
| /// | |
| /// Structure for array of unique GUID/HardwareInstance pairs from the | |
| /// current set of EFI_FIRMWARE_IMAGE_DESCRIPTORs from all FMP Protocols. | |
| /// | |
| typedef struct { | |
| /// | |
| /// A unique GUID identifying the firmware image type. | |
| /// | |
| EFI_GUID ImageTypeGuid; | |
| /// | |
| /// An optional number to identify the unique hardware instance within the | |
| /// system for devices that may have multiple instances whenever possible. | |
| /// | |
| UINT64 HardwareInstance; | |
| } GUID_HARDWAREINSTANCE_PAIR; | |
| /** | |
| Print ESRT to debug console. | |
| @param[in] Table Pointer to the ESRT table. | |
| **/ | |
| VOID | |
| EFIAPI | |
| PrintTable ( | |
| IN EFI_SYSTEM_RESOURCE_TABLE *Table | |
| ); | |
| /** | |
| Install EFI System Resource Table into the UEFI Configuration Table | |
| @param[in] Table Pointer to the ESRT. | |
| @return Status code. | |
| **/ | |
| EFI_STATUS | |
| InstallEfiSystemResourceTableInUefiConfigurationTable ( | |
| IN EFI_SYSTEM_RESOURCE_TABLE *Table | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| Status = EFI_SUCCESS; | |
| if (Table->FwResourceCount == 0) { | |
| DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Can't install ESRT table because it has zero Entries. \n")); | |
| Status = EFI_UNSUPPORTED; | |
| } else { | |
| // | |
| // Install the pointer into config table | |
| // | |
| Status = gBS->InstallConfigurationTable (&gEfiSystemResourceTableGuid, Table); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Can't install ESRT table. Status: %r. \n", Status)); | |
| } else { | |
| DEBUG ((DEBUG_INFO, "EsrtFmpDxe: Installed ESRT table. \n")); | |
| } | |
| } | |
| return Status; | |
| } | |
| /** | |
| Return if this FMP is a system FMP or a device FMP, based upon FmpImageInfo. | |
| @param[in] FmpImageInfo A pointer to EFI_FIRMWARE_IMAGE_DESCRIPTOR | |
| @return TRUE It is a system FMP. | |
| @return FALSE It is a device FMP. | |
| **/ | |
| BOOLEAN | |
| IsSystemFmp ( | |
| IN EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfo | |
| ) | |
| { | |
| GUID *Guid; | |
| UINTN Count; | |
| UINTN Index; | |
| Guid = PcdGetPtr (PcdSystemFmpCapsuleImageTypeIdGuid); | |
| Count = PcdGetSize (PcdSystemFmpCapsuleImageTypeIdGuid) / sizeof (GUID); | |
| for (Index = 0; Index < Count; Index++, Guid++) { | |
| if (CompareGuid (&FmpImageInfo->ImageTypeId, Guid)) { | |
| return TRUE; | |
| } | |
| } | |
| return FALSE; | |
| } | |
| /** | |
| Function to create a single ESRT Entry and add it to the ESRT with | |
| a given FMP descriptor. If the GUID is already in the ESRT, then the ESRT | |
| entry is updated. | |
| @param[in,out] Table Pointer to the ESRT Table. | |
| @param[in,out] HardwareInstances Pointer to the GUID_HARDWAREINSTANCE_PAIR. | |
| @param[in,out] NumberOfDescriptors The number of EFI_FIRMWARE_IMAGE_DESCRIPTORs. | |
| @param[in] FmpImageInfoBuf Pointer to the EFI_FIRMWARE_IMAGE_DESCRIPTOR. | |
| @param[in] FmpVersion FMP Version. | |
| @retval EFI_SUCCESS FmpImageInfoBuf was use to fill in a new ESRT entry | |
| in Table. | |
| @retval EFI_SUCCESS The ImageTypeId GUID in FmpImageInfoBuf matches an | |
| existing ESRT entry in Table, and the information | |
| from FmpImageInfoBuf was merged into the the existing | |
| ESRT entry. | |
| @retval EFI_UNSPOORTED The GUID/HardareInstance in FmpImageInfoBuf has is a | |
| duplicate. | |
| **/ | |
| EFI_STATUS | |
| CreateEsrtEntry ( | |
| IN OUT EFI_SYSTEM_RESOURCE_TABLE *Table, | |
| IN OUT GUID_HARDWAREINSTANCE_PAIR *HardwareInstances, | |
| IN OUT UINT32 *NumberOfDescriptors, | |
| IN EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf, | |
| IN UINT32 FmpVersion | |
| ) | |
| { | |
| UINTN Index; | |
| EFI_SYSTEM_RESOURCE_ENTRY *Entry; | |
| UINT64 FmpHardwareInstance; | |
| FmpHardwareInstance = 0; | |
| if (FmpVersion >= 3) { | |
| FmpHardwareInstance = FmpImageInfoBuf->HardwareInstance; | |
| } | |
| // | |
| // Check to see of FmpImageInfoBuf GUID/HardwareInstance is unique | |
| // | |
| for (Index = 0; Index < *NumberOfDescriptors; Index++) { | |
| if (CompareGuid (&HardwareInstances[Index].ImageTypeGuid, &FmpImageInfoBuf->ImageTypeId)) { | |
| if (HardwareInstances[Index].HardwareInstance == FmpHardwareInstance) { | |
| DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Duplicate firmware image descriptor with GUID %g HardwareInstance:0x%x\n", &FmpImageInfoBuf->ImageTypeId, FmpHardwareInstance)); | |
| ASSERT ( | |
| !CompareGuid (&HardwareInstances[Index].ImageTypeGuid, &FmpImageInfoBuf->ImageTypeId) || | |
| HardwareInstances[Index].HardwareInstance != FmpHardwareInstance | |
| ); | |
| return EFI_UNSUPPORTED; | |
| } | |
| } | |
| } | |
| // | |
| // Record new GUID/HardwareInstance pair | |
| // | |
| CopyGuid (&HardwareInstances[*NumberOfDescriptors].ImageTypeGuid, &FmpImageInfoBuf->ImageTypeId); | |
| HardwareInstances[*NumberOfDescriptors].HardwareInstance = FmpHardwareInstance; | |
| *NumberOfDescriptors = *NumberOfDescriptors + 1; | |
| DEBUG ((DEBUG_INFO, "EsrtFmpDxe: Add new image descriptor with GUID %g HardwareInstance:0x%x\n", &FmpImageInfoBuf->ImageTypeId, FmpHardwareInstance)); | |
| // | |
| // Check to see if GUID is already in the ESRT table | |
| // | |
| Entry = (EFI_SYSTEM_RESOURCE_ENTRY *)(Table + 1); | |
| for (Index = 0; Index < Table->FwResourceCount; Index++, Entry++) { | |
| if (!CompareGuid (&Entry->FwClass, &FmpImageInfoBuf->ImageTypeId)) { | |
| continue; | |
| } | |
| DEBUG ((DEBUG_INFO, "EsrtFmpDxe: ESRT Entry already exists for FMP Instance with GUID %g\n", &Entry->FwClass)); | |
| // | |
| // Set ESRT FwVersion to the smaller of the two values | |
| // | |
| Entry->FwVersion = MIN (FmpImageInfoBuf->Version, Entry->FwVersion); | |
| // | |
| // VERSION 2 has Lowest Supported | |
| // | |
| if (FmpVersion >= 2) { | |
| // | |
| // Set ESRT LowestSupportedFwVersion to the smaller of the two values | |
| // | |
| Entry->LowestSupportedFwVersion = | |
| MIN ( | |
| FmpImageInfoBuf->LowestSupportedImageVersion, | |
| Entry->LowestSupportedFwVersion | |
| ); | |
| } | |
| // | |
| // VERSION 3 supports last attempt values | |
| // | |
| if (FmpVersion >= 3) { | |
| // | |
| // Update the ESRT entry with the last attempt status and last attempt | |
| // version from the first FMP instance whose last attempt status is not | |
| // SUCCESS. If all FMP instances are SUCCESS, then set version to the | |
| // smallest value from all FMP instances. | |
| // | |
| if (Entry->LastAttemptStatus == LAST_ATTEMPT_STATUS_SUCCESS) { | |
| if (FmpImageInfoBuf->LastAttemptStatus != LAST_ATTEMPT_STATUS_SUCCESS) { | |
| Entry->LastAttemptStatus = FmpImageInfoBuf->LastAttemptStatus; | |
| Entry->LastAttemptVersion = FmpImageInfoBuf->LastAttemptVersion; | |
| } else { | |
| Entry->LastAttemptVersion = | |
| MIN ( | |
| FmpImageInfoBuf->LastAttemptVersion, | |
| Entry->LastAttemptVersion | |
| ); | |
| } | |
| } | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| // | |
| // Add a new ESRT Table Entry | |
| // | |
| Entry = (EFI_SYSTEM_RESOURCE_ENTRY *)(Table + 1) + Table->FwResourceCount; | |
| CopyGuid (&Entry->FwClass, &FmpImageInfoBuf->ImageTypeId); | |
| if (IsSystemFmp (FmpImageInfoBuf)) { | |
| DEBUG ((DEBUG_INFO, "EsrtFmpDxe: Found an ESRT entry for a System Device.\n")); | |
| Entry->FwType = (UINT32)(ESRT_FW_TYPE_SYSTEMFIRMWARE); | |
| } else { | |
| Entry->FwType = (UINT32)(ESRT_FW_TYPE_DEVICEFIRMWARE); | |
| } | |
| Entry->FwVersion = FmpImageInfoBuf->Version; | |
| Entry->LowestSupportedFwVersion = 0; | |
| Entry->CapsuleFlags = 0; | |
| Entry->LastAttemptVersion = 0; | |
| Entry->LastAttemptStatus = 0; | |
| // | |
| // VERSION 2 has Lowest Supported | |
| // | |
| if (FmpVersion >= 2) { | |
| Entry->LowestSupportedFwVersion = FmpImageInfoBuf->LowestSupportedImageVersion; | |
| } | |
| // | |
| // VERSION 3 supports last attempt values | |
| // | |
| if (FmpVersion >= 3) { | |
| Entry->LastAttemptVersion = FmpImageInfoBuf->LastAttemptVersion; | |
| Entry->LastAttemptStatus = FmpImageInfoBuf->LastAttemptStatus; | |
| } | |
| // | |
| // Increment the number of active ESRT Table Entries | |
| // | |
| Table->FwResourceCount++; | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Function to retrieve the EFI_FIRMWARE_IMAGE_DESCRIPTOR from an FMP Instance. | |
| The returned buffer is allocated using AllocatePool() and must be freed by the | |
| caller using FreePool(). | |
| @param[in] Fmp Pointer to an EFI_FIRMWARE_MANAGEMENT_PROTOCOL. | |
| @param[out] FmpImageInfoDescriptorVer Pointer to the version number associated | |
| with the returned EFI_FIRMWARE_IMAGE_DESCRIPTOR. | |
| @param[out] FmpImageInfoCount Pointer to the number of the returned | |
| EFI_FIRMWARE_IMAGE_DESCRIPTORs. | |
| @param[out] DescriptorSize Pointer to the size, in bytes, of each | |
| returned EFI_FIRMWARE_IMAGE_DESCRIPTOR. | |
| @return Pointer to the retrieved EFI_FIRMWARE_IMAGE_DESCRIPTOR. If the | |
| descriptor can not be retrieved, then NULL is returned. | |
| **/ | |
| EFI_FIRMWARE_IMAGE_DESCRIPTOR * | |
| FmpGetFirmwareImageDescriptor ( | |
| IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp, | |
| OUT UINT32 *FmpImageInfoDescriptorVer, | |
| OUT UINT8 *FmpImageInfoCount, | |
| OUT UINTN *DescriptorSize | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN ImageInfoSize; | |
| UINT32 PackageVersion; | |
| CHAR16 *PackageVersionName; | |
| EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf; | |
| ImageInfoSize = 0; | |
| Status = Fmp->GetImageInfo ( | |
| Fmp, // FMP Pointer | |
| &ImageInfoSize, // Buffer Size (in this case 0) | |
| NULL, // NULL so we can get size | |
| FmpImageInfoDescriptorVer, // DescriptorVersion | |
| FmpImageInfoCount, // DescriptorCount | |
| DescriptorSize, // DescriptorSize | |
| &PackageVersion, // PackageVersion | |
| &PackageVersionName // PackageVersionName | |
| ); | |
| if (Status != EFI_BUFFER_TOO_SMALL) { | |
| DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Unexpected Failure in GetImageInfo. Status = %r\n", Status)); | |
| return NULL; | |
| } | |
| FmpImageInfoBuf = AllocateZeroPool (ImageInfoSize); | |
| if (FmpImageInfoBuf == NULL) { | |
| DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to get memory for FMP descriptor.\n")); | |
| return NULL; | |
| } | |
| PackageVersionName = NULL; | |
| Status = Fmp->GetImageInfo ( | |
| Fmp, // FMP Pointer | |
| &ImageInfoSize, // ImageInfoSize | |
| FmpImageInfoBuf, // ImageInfo | |
| FmpImageInfoDescriptorVer, // DescriptorVersion | |
| FmpImageInfoCount, // DescriptorCount | |
| DescriptorSize, // DescriptorSize | |
| &PackageVersion, // PackageVersion | |
| &PackageVersionName // PackageVersionName | |
| ); | |
| if (PackageVersionName != NULL) { | |
| FreePool (PackageVersionName); | |
| } | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failure in GetImageInfo. Status = %r\n", Status)); | |
| FreePool (FmpImageInfoBuf); | |
| return NULL; | |
| } | |
| return FmpImageInfoBuf; | |
| } | |
| /** | |
| Function to create ESRT based on FMP Instances. | |
| Create ESRT table, get the descriptors from FMP Instance and | |
| create ESRT entries (ESRE). | |
| @return Pointer to the ESRT created. | |
| **/ | |
| EFI_SYSTEM_RESOURCE_TABLE * | |
| CreateFmpBasedEsrt ( | |
| VOID | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN NoProtocols; | |
| VOID **Buffer; | |
| UINTN Index; | |
| UINT32 FmpImageInfoDescriptorVer; | |
| UINT8 FmpImageInfoCount; | |
| UINTN DescriptorSize; | |
| UINT32 NumberOfDescriptors; | |
| EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf; | |
| EFI_FIRMWARE_IMAGE_DESCRIPTOR *OrgFmpImageInfoBuf; | |
| EFI_SYSTEM_RESOURCE_TABLE *Table; | |
| GUID_HARDWAREINSTANCE_PAIR *HardwareInstances; | |
| Status = EFI_SUCCESS; | |
| NoProtocols = 0; | |
| Buffer = NULL; | |
| FmpImageInfoBuf = NULL; | |
| OrgFmpImageInfoBuf = NULL; | |
| Table = NULL; | |
| HardwareInstances = NULL; | |
| Status = EfiLocateProtocolBuffer ( | |
| &gEfiFirmwareManagementProtocolGuid, | |
| &NoProtocols, | |
| &Buffer | |
| ); | |
| if (EFI_ERROR (Status) || (Buffer == NULL)) { | |
| return NULL; | |
| } | |
| // | |
| // Count the total number of EFI_FIRMWARE_IMAGE_DESCRIPTORs | |
| // | |
| for (Index = 0, NumberOfDescriptors = 0; Index < NoProtocols; Index++) { | |
| FmpImageInfoBuf = FmpGetFirmwareImageDescriptor ( | |
| (EFI_FIRMWARE_MANAGEMENT_PROTOCOL *)Buffer[Index], | |
| &FmpImageInfoDescriptorVer, | |
| &FmpImageInfoCount, | |
| &DescriptorSize | |
| ); | |
| if (FmpImageInfoBuf != NULL) { | |
| NumberOfDescriptors += FmpImageInfoCount; | |
| FreePool (FmpImageInfoBuf); | |
| } | |
| } | |
| // | |
| // Allocate ESRT Table and GUID/HardwareInstance table | |
| // | |
| Table = AllocateZeroPool ( | |
| (NumberOfDescriptors * sizeof (EFI_SYSTEM_RESOURCE_ENTRY)) + sizeof (EFI_SYSTEM_RESOURCE_TABLE) | |
| ); | |
| if (Table == NULL) { | |
| DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to allocate memory for ESRT.\n")); | |
| FreePool (Buffer); | |
| return NULL; | |
| } | |
| HardwareInstances = AllocateZeroPool (NumberOfDescriptors * sizeof (GUID_HARDWAREINSTANCE_PAIR)); | |
| if (HardwareInstances == NULL) { | |
| DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to allocate memory for HW Instance Table.\n")); | |
| FreePool (Table); | |
| FreePool (Buffer); | |
| return NULL; | |
| } | |
| // | |
| // Initialize ESRT Table | |
| // | |
| Table->FwResourceCount = 0; | |
| Table->FwResourceCountMax = NumberOfDescriptors; | |
| Table->FwResourceVersion = EFI_SYSTEM_RESOURCE_TABLE_FIRMWARE_RESOURCE_VERSION; | |
| NumberOfDescriptors = 0; | |
| for (Index = 0; Index < NoProtocols; Index++) { | |
| FmpImageInfoBuf = FmpGetFirmwareImageDescriptor ( | |
| (EFI_FIRMWARE_MANAGEMENT_PROTOCOL *)Buffer[Index], | |
| &FmpImageInfoDescriptorVer, | |
| &FmpImageInfoCount, | |
| &DescriptorSize | |
| ); | |
| if (FmpImageInfoBuf == NULL) { | |
| continue; | |
| } | |
| // | |
| // Check each descriptor and read from the one specified | |
| // | |
| OrgFmpImageInfoBuf = FmpImageInfoBuf; | |
| while (FmpImageInfoCount > 0) { | |
| // | |
| // If the descriptor has the IN USE bit set, create ESRT entry otherwise ignore. | |
| // | |
| if ((FmpImageInfoBuf->AttributesSetting & FmpImageInfoBuf->AttributesSupported & IMAGE_ATTRIBUTE_IN_USE) == IMAGE_ATTRIBUTE_IN_USE) { | |
| // | |
| // Create ESRT entry | |
| // | |
| CreateEsrtEntry (Table, HardwareInstances, &NumberOfDescriptors, FmpImageInfoBuf, FmpImageInfoDescriptorVer); | |
| } | |
| FmpImageInfoCount--; | |
| // | |
| // Increment the buffer pointer ahead by the size of the descriptor | |
| // | |
| FmpImageInfoBuf = (EFI_FIRMWARE_IMAGE_DESCRIPTOR *)(((UINT8 *)FmpImageInfoBuf) + DescriptorSize); | |
| } | |
| FreePool (OrgFmpImageInfoBuf); | |
| OrgFmpImageInfoBuf = NULL; | |
| } | |
| FreePool (Buffer); | |
| FreePool (HardwareInstances); | |
| return Table; | |
| } | |
| /** | |
| Notify function for event group EFI_EVENT_GROUP_READY_TO_BOOT. This is used to | |
| install the Efi System Resource Table. | |
| @param[in] Event The Event that is being processed. | |
| @param[in] Context The Event Context. | |
| **/ | |
| VOID | |
| EFIAPI | |
| EsrtReadyToBootEventNotify ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_SYSTEM_RESOURCE_TABLE *Table; | |
| Table = CreateFmpBasedEsrt (); | |
| if (Table != NULL) { | |
| // | |
| // Print table on debug builds | |
| // | |
| DEBUG_CODE_BEGIN (); | |
| PrintTable (Table); | |
| DEBUG_CODE_END (); | |
| Status = InstallEfiSystemResourceTableInUefiConfigurationTable (Table); | |
| if (EFI_ERROR (Status)) { | |
| FreePool (Table); | |
| } | |
| } else { | |
| DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Can't install ESRT table because it is NULL. \n")); | |
| } | |
| // | |
| // Close the event to prevent it be signalled again. | |
| // | |
| gBS->CloseEvent (Event); | |
| } | |
| /** | |
| The module Entry Point of the Efi System Resource Table DXE driver. | |
| @param[in] ImageHandle The firmware allocated handle for the EFI image. | |
| @param[in] SystemTable A pointer to the EFI System Table. | |
| @retval EFI_SUCCESS The entry point is executed successfully. | |
| @retval Other Some error occurs when executing this entry point. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| EsrtFmpEntryPoint ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_EVENT EsrtReadyToBootEvent; | |
| // | |
| // Register notify function to install ESRT on ReadyToBoot Event. | |
| // | |
| Status = EfiCreateEventReadyToBootEx ( | |
| TPL_CALLBACK, | |
| EsrtReadyToBootEventNotify, | |
| NULL, | |
| &EsrtReadyToBootEvent | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to register for ready to boot\n")); | |
| } | |
| return Status; | |
| } |