/** @file Publishes ESRT table from Firmware Management Protocol instances Copyright (c) 2016, Microsoft Corporation Copyright (c) 2018, Intel Corporation. All rights reserved.
All rights reserved. SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #include #include /** Print ESRT to debug console. @param[in] Table Pointer to the ESRT table. **/ VOID EFIAPI PrintTable ( IN EFI_SYSTEM_RESOURCE_TABLE *Table ); // // Number of ESRT entries to grow by each time we run out of room // #define GROWTH_STEP 10 /** 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 given a FMP descriptor. If the guid is already in the ESRT it will be ignored. The ESRT will grow if it does not have enough room. @param[in, out] Table On input, pointer to the pointer to the ESRT. On output, same as input or pointer to the pointer to new enlarged ESRT. @param[in] FmpImageInfoBuf Pointer to the EFI_FIRMWARE_IMAGE_DESCRIPTOR. @param[in] FmpVersion FMP Version. @return Status code. **/ EFI_STATUS CreateEsrtEntry ( IN OUT EFI_SYSTEM_RESOURCE_TABLE **Table, IN EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf, IN UINT32 FmpVersion ) { UINTN Index; EFI_SYSTEM_RESOURCE_ENTRY *Entry; UINTN NewSize; EFI_SYSTEM_RESOURCE_TABLE *NewTable; Index = 0; Entry = NULL; Entry = (EFI_SYSTEM_RESOURCE_ENTRY *)((*Table) + 1); // // Make sure Guid isn't already in the list // for (Index = 0; Index < (*Table)->FwResourceCount; Index++) { if (CompareGuid (&Entry->FwClass, &FmpImageInfoBuf->ImageTypeId)) { DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: ESRT Entry already exists for FMP Instance with GUID %g\n", &Entry->FwClass)); return EFI_INVALID_PARAMETER; } Entry++; } // // Grow table if needed // if ((*Table)->FwResourceCount >= (*Table)->FwResourceCountMax) { NewSize = (((*Table)->FwResourceCountMax + GROWTH_STEP) * sizeof (EFI_SYSTEM_RESOURCE_ENTRY)) + sizeof (EFI_SYSTEM_RESOURCE_TABLE); NewTable = AllocateZeroPool (NewSize); if (NewTable == NULL) { DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to allocate memory larger table for ESRT. \n")); return EFI_OUT_OF_RESOURCES; } // // Copy the whole old table into new table buffer // CopyMem ( NewTable, (*Table), (((*Table)->FwResourceCountMax) * sizeof (EFI_SYSTEM_RESOURCE_ENTRY)) + sizeof (EFI_SYSTEM_RESOURCE_TABLE) ); // // Update max // NewTable->FwResourceCountMax = NewTable->FwResourceCountMax + GROWTH_STEP; // // Free old table // FreePool ((*Table)); // // Reassign pointer to new table. // (*Table) = NewTable; } // // ESRT table has enough room for the new entry so add new entry // Entry = (EFI_SYSTEM_RESOURCE_ENTRY *)(((UINT8 *)(*Table)) + sizeof (EFI_SYSTEM_RESOURCE_TABLE)); // // Move to the location of new entry // Entry = Entry + (*Table)->FwResourceCount; // // Increment resource count // (*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; } return EFI_SUCCESS; } /** 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; EFI_SYSTEM_RESOURCE_TABLE *Table; UINTN NoProtocols; VOID **Buffer; UINTN Index; EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp; UINTN DescriptorSize; EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf; EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBufOrg; UINT8 FmpImageInfoCount; UINT32 FmpImageInfoDescriptorVer; UINTN ImageInfoSize; UINT32 PackageVersion; CHAR16 *PackageVersionName; Status = EFI_SUCCESS; Table = NULL; NoProtocols = 0; Buffer = NULL; PackageVersionName = NULL; FmpImageInfoBuf = NULL; FmpImageInfoBufOrg = NULL; Fmp = NULL; Status = EfiLocateProtocolBuffer ( &gEfiFirmwareManagementProtocolGuid, &NoProtocols, &Buffer ); if (EFI_ERROR(Status) || (Buffer == NULL)) { return NULL; } // // Allocate Memory for table // Table = AllocateZeroPool ( (GROWTH_STEP * sizeof (EFI_SYSTEM_RESOURCE_ENTRY)) + sizeof (EFI_SYSTEM_RESOURCE_TABLE) ); if (Table == NULL) { DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to allocate memory for ESRT.\n")); gBS->FreePool (Buffer); return NULL; } Table->FwResourceCount = 0; Table->FwResourceCountMax = GROWTH_STEP; Table->FwResourceVersion = EFI_SYSTEM_RESOURCE_TABLE_FIRMWARE_RESOURCE_VERSION; for (Index = 0; Index < NoProtocols; Index++) { Fmp = (EFI_FIRMWARE_MANAGEMENT_PROTOCOL *) Buffer[Index]; 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)); continue; } FmpImageInfoBuf = AllocateZeroPool (ImageInfoSize); if (FmpImageInfoBuf == NULL) { DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to get memory for descriptors.\n")); continue; } FmpImageInfoBufOrg = FmpImageInfoBuf; PackageVersionName = NULL; Status = Fmp->GetImageInfo ( Fmp, &ImageInfoSize, // ImageInfoSize FmpImageInfoBuf, // ImageInfo &FmpImageInfoDescriptorVer, // DescriptorVersion &FmpImageInfoCount, // DescriptorCount &DescriptorSize, // DescriptorSize &PackageVersion, // PackageVersion &PackageVersionName // PackageVersionName ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failure in GetImageInfo. Status = %r\n", Status)); FreePool (FmpImageInfoBufOrg); FmpImageInfoBufOrg = NULL; continue; } // // Check each descriptor and read from the one specified // 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, FmpImageInfoBuf, FmpImageInfoDescriptorVer); } FmpImageInfoCount--; // // Increment the buffer pointer ahead by the size of the descriptor // FmpImageInfoBuf = (EFI_FIRMWARE_IMAGE_DESCRIPTOR *)(((UINT8 *)FmpImageInfoBuf) + DescriptorSize); } if (PackageVersionName != NULL) { FreePool (PackageVersionName); PackageVersionName = NULL; } FreePool (FmpImageInfoBufOrg); FmpImageInfoBufOrg = NULL; } gBS->FreePool (Buffer); 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; }