diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/EsrtDxe/EsrtDxe.c | |
parent | Initial commit. (diff) | |
download | virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip |
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/EsrtDxe/EsrtDxe.c')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/EsrtDxe/EsrtDxe.c | 661 |
1 files changed, 661 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/EsrtDxe/EsrtDxe.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/EsrtDxe/EsrtDxe.c new file mode 100644 index 00000000..cdffc8a5 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/EsrtDxe/EsrtDxe.c @@ -0,0 +1,661 @@ +/** @file + Esrt management module. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#include "EsrtImpl.h" + + +// +// Module globals. +// + +ESRT_PRIVATE_DATA mPrivate; + +ESRT_MANAGEMENT_PROTOCOL mEsrtManagementProtocolTemplate = { + EsrtDxeGetEsrtEntry, + EsrtDxeUpdateEsrtEntry, + EsrtDxeRegisterEsrtEntry, + EsrtDxeUnRegisterEsrtEntry, + EsrtDxeSyncFmp, + EsrtDxeLockEsrtRepository + }; + +/** + Get ESRT entry from ESRT Cache by FwClass Guid + + @param[in] FwClass FwClass of Esrt entry to get + @param[in, out] Entry Esrt entry returned + + @retval EFI_SUCCESS The variable saving this Esrt Entry exists. + @retval EF_NOT_FOUND No correct variable found. + @retval EFI_WRITE_PROTECTED ESRT Cache repository is locked + +**/ +EFI_STATUS +EFIAPI +EsrtDxeGetEsrtEntry( + IN EFI_GUID *FwClass, + IN OUT EFI_SYSTEM_RESOURCE_ENTRY *Entry + ) +{ + EFI_STATUS Status; + + if (FwClass == NULL || Entry == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EfiAcquireLockOrFail (&mPrivate.NonFmpLock); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Find in Non-FMP Cached Esrt Repository + // + Status = GetEsrtEntry( + FwClass, + ESRT_FROM_NONFMP, + Entry + ); + + EfiReleaseLock(&mPrivate.NonFmpLock); + + if (EFI_ERROR(Status)) { + Status = EfiAcquireLockOrFail (&mPrivate.FmpLock); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Find in FMP Cached Esrt NV Variable + // + Status = GetEsrtEntry( + FwClass, + ESRT_FROM_FMP, + Entry + ); + + EfiReleaseLock(&mPrivate.FmpLock); + } + + return Status; +} + +/** + Update one ESRT entry in ESRT Cache. + + @param[in] Entry Esrt entry to be updated + + @retval EFI_SUCCESS Successfully update an ESRT entry in cache. + @retval EFI_INVALID_PARAMETER Entry does't exist in ESRT Cache + @retval EFI_WRITE_PROTECTED ESRT Cache repositoy is locked + +**/ +EFI_STATUS +EFIAPI +EsrtDxeUpdateEsrtEntry( + IN EFI_SYSTEM_RESOURCE_ENTRY *Entry + ) +{ + EFI_STATUS Status; + + if (Entry == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EfiAcquireLockOrFail (&mPrivate.FmpLock); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = UpdateEsrtEntry(Entry, ESRT_FROM_FMP); + + if (!EFI_ERROR(Status)) { + EfiReleaseLock(&mPrivate.FmpLock); + return Status; + } + EfiReleaseLock(&mPrivate.FmpLock); + + + Status = EfiAcquireLockOrFail (&mPrivate.NonFmpLock); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = UpdateEsrtEntry(Entry, ESRT_FROM_NONFMP); + + EfiReleaseLock(&mPrivate.NonFmpLock); + + return Status; +} + +/** + Non-FMP instance to unregister Esrt Entry from ESRT Cache. + + @param[in] FwClass FwClass of Esrt entry to Unregister + + @retval EFI_SUCCESS Insert all entries Successfully + @retval EFI_NOT_FOUND Entry of FwClass does not exsit + +**/ +EFI_STATUS +EFIAPI +EsrtDxeUnRegisterEsrtEntry( + IN EFI_GUID *FwClass + ) +{ + EFI_STATUS Status; + + if (FwClass == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EfiAcquireLockOrFail (&mPrivate.NonFmpLock); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = DeleteEsrtEntry(FwClass, ESRT_FROM_NONFMP); + + EfiReleaseLock(&mPrivate.NonFmpLock); + + return Status; +} + +/** + Non-FMP instance to register one ESRT entry into ESRT Cache. + + @param[in] Entry Esrt entry to be set + + @retval EFI_SUCCESS Successfully set a variable. + @retval EFI_INVALID_PARAMETER ESRT Entry is already exist + @retval EFI_OUT_OF_RESOURCES Non-FMP ESRT repository is full + +**/ +EFI_STATUS +EFIAPI +EsrtDxeRegisterEsrtEntry( + IN EFI_SYSTEM_RESOURCE_ENTRY *Entry + ) +{ + EFI_STATUS Status; + EFI_SYSTEM_RESOURCE_ENTRY EsrtEntryTmp; + + if (Entry == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EfiAcquireLockOrFail (&mPrivate.NonFmpLock); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = GetEsrtEntry( + &Entry->FwClass, + ESRT_FROM_NONFMP, + &EsrtEntryTmp + ); + + if (Status == EFI_NOT_FOUND) { + Status = InsertEsrtEntry(Entry, ESRT_FROM_NONFMP); + } + + EfiReleaseLock(&mPrivate.NonFmpLock); + + return Status; +} + +/** + This function syn up Cached ESRT with data from FMP instances + Function should be called after Connect All in order to locate all FMP protocols + installed. + + @retval EFI_SUCCESS Successfully sync cache repository from FMP instances + @retval EFI_NOT_FOUND No FMP Instance are found + @retval EFI_OUT_OF_RESOURCES Resource allocaton fail + +**/ +EFI_STATUS +EFIAPI +EsrtDxeSyncFmp( + VOID + ) +{ + EFI_STATUS Status; + UINTN Index1; + UINTN Index2; + UINTN Index3; + EFI_HANDLE *HandleBuffer; + EFI_FIRMWARE_MANAGEMENT_PROTOCOL **FmpBuf; + UINTN NumberOfHandles; + UINTN *DescriptorSizeBuf; + EFI_FIRMWARE_IMAGE_DESCRIPTOR **FmpImageInfoBuf; + EFI_FIRMWARE_IMAGE_DESCRIPTOR *TempFmpImageInfo; + UINT8 *FmpImageInfoCountBuf; + UINT32 *FmpImageInfoDescriptorVerBuf; + UINTN ImageInfoSize; + UINT32 PackageVersion; + CHAR16 *PackageVersionName; + EFI_SYSTEM_RESOURCE_ENTRY *EsrtRepositoryNew; + UINTN EntryNumNew; + + NumberOfHandles = 0; + EntryNumNew = 0; + FmpBuf = NULL; + HandleBuffer = NULL; + FmpImageInfoBuf = NULL; + FmpImageInfoCountBuf = NULL; + PackageVersionName = NULL; + DescriptorSizeBuf = NULL; + FmpImageInfoDescriptorVerBuf = NULL; + EsrtRepositoryNew = NULL; + + // + // Get image information from all FMP protocol + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiFirmwareManagementProtocolGuid, + NULL, + &NumberOfHandles, + &HandleBuffer + ); + + + if (Status == EFI_NOT_FOUND) { + EntryNumNew = 0; + goto UPDATE_REPOSITORY; + } else if (EFI_ERROR(Status)){ + goto END; + } + + // + // Allocate buffer to hold new FMP ESRT Cache repository + // + EsrtRepositoryNew = AllocateZeroPool(PcdGet32(PcdMaxFmpEsrtCacheNum) * sizeof(EFI_SYSTEM_RESOURCE_ENTRY)); + if (EsrtRepositoryNew == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto END; + } + + FmpBuf = AllocatePool(sizeof(EFI_FIRMWARE_MANAGEMENT_PROTOCOL *) * NumberOfHandles); + if (FmpBuf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto END; + } + + FmpImageInfoBuf = AllocateZeroPool(sizeof(EFI_FIRMWARE_IMAGE_DESCRIPTOR *) * NumberOfHandles); + if (FmpImageInfoBuf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto END; + } + + FmpImageInfoCountBuf = AllocateZeroPool(sizeof(UINT8) * NumberOfHandles); + if (FmpImageInfoCountBuf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto END; + } + + DescriptorSizeBuf = AllocateZeroPool(sizeof(UINTN) * NumberOfHandles); + if (DescriptorSizeBuf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto END; + } + + FmpImageInfoDescriptorVerBuf = AllocateZeroPool(sizeof(UINT32) * NumberOfHandles); + if (FmpImageInfoDescriptorVerBuf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto END; + } + + // + // Get all FmpImageInfo Descriptor into FmpImageInfoBuf + // + for (Index1 = 0; Index1 < NumberOfHandles; Index1++){ + Status = gBS->HandleProtocol( + HandleBuffer[Index1], + &gEfiFirmwareManagementProtocolGuid, + (VOID **)&FmpBuf[Index1] + ); + + if (EFI_ERROR(Status)) { + continue; + } + + ImageInfoSize = 0; + Status = FmpBuf[Index1]->GetImageInfo ( + FmpBuf[Index1], + &ImageInfoSize, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL + ); + + if (Status == EFI_BUFFER_TOO_SMALL) { + FmpImageInfoBuf[Index1] = AllocateZeroPool(ImageInfoSize); + if (FmpImageInfoBuf[Index1] == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto END; + } + } else { + continue; + } + + PackageVersionName = NULL; + Status = FmpBuf[Index1]->GetImageInfo ( + FmpBuf[Index1], + &ImageInfoSize, + FmpImageInfoBuf[Index1], + &FmpImageInfoDescriptorVerBuf[Index1], + &FmpImageInfoCountBuf[Index1], + &DescriptorSizeBuf[Index1], + &PackageVersion, + &PackageVersionName + ); + + // + // If FMP GetInformation interface failed, skip this resource + // + if (EFI_ERROR(Status)){ + FmpImageInfoCountBuf[Index1] = 0; + continue; + } + + if (PackageVersionName != NULL) { + FreePool(PackageVersionName); + } + } + + // + // Create new FMP cache repository based on FmpImageInfoBuf + // + for (Index2 = 0; Index2 < NumberOfHandles; Index2++){ + TempFmpImageInfo = FmpImageInfoBuf[Index2]; + for (Index3 = 0; Index3 < FmpImageInfoCountBuf[Index2]; Index3++){ + if ((TempFmpImageInfo->AttributesSupported & IMAGE_ATTRIBUTE_IN_USE) != 0 + && (TempFmpImageInfo->AttributesSetting & IMAGE_ATTRIBUTE_IN_USE) != 0){ + // + // Always put the first smallest version of Image info into ESRT cache + // + for(Index1 = 0; Index1 < EntryNumNew; Index1++) { + if (CompareGuid(&EsrtRepositoryNew[Index1].FwClass, &TempFmpImageInfo->ImageTypeId)) { + if(EsrtRepositoryNew[Index1].FwVersion > TempFmpImageInfo->Version) { + SetEsrtEntryFromFmpInfo(&EsrtRepositoryNew[Index1], TempFmpImageInfo, FmpImageInfoDescriptorVerBuf[Index2]); + } + break; + } + } + // + // New ImageTypeId can't be found in EsrtRepositoryNew. Create a new one + // + if (Index1 == EntryNumNew){ + SetEsrtEntryFromFmpInfo(&EsrtRepositoryNew[EntryNumNew], TempFmpImageInfo, FmpImageInfoDescriptorVerBuf[Index2]); + EntryNumNew++; + if (EntryNumNew >= PcdGet32(PcdMaxFmpEsrtCacheNum)) { + break; + } + } + } + + // + // Use DescriptorSize to move ImageInfo Pointer to stay compatible with different ImageInfo version + // + TempFmpImageInfo = (EFI_FIRMWARE_IMAGE_DESCRIPTOR *)((UINT8 *)TempFmpImageInfo + DescriptorSizeBuf[Index2]); + } + } + +UPDATE_REPOSITORY: + + Status = EfiAcquireLockOrFail (&mPrivate.FmpLock); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gRT->SetVariable( + EFI_ESRT_FMP_VARIABLE_NAME, + &gEfiCallerIdGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + EntryNumNew * sizeof(EFI_SYSTEM_RESOURCE_ENTRY), + EsrtRepositoryNew + ); + + EfiReleaseLock(&mPrivate.FmpLock); + +END: + if (EsrtRepositoryNew != NULL) { + FreePool(EsrtRepositoryNew); + } + + if (HandleBuffer != NULL) { + FreePool(HandleBuffer); + } + + if (FmpBuf != NULL) { + FreePool(FmpBuf); + } + + if (FmpImageInfoCountBuf != NULL) { + FreePool(FmpImageInfoCountBuf); + } + + if (DescriptorSizeBuf != NULL) { + FreePool(DescriptorSizeBuf); + } + + if (FmpImageInfoDescriptorVerBuf != NULL) { + FreePool(FmpImageInfoDescriptorVerBuf); + } + + if (FmpImageInfoBuf != NULL) { + for (Index1 = 0; Index1 < NumberOfHandles; Index1++){ + if (FmpImageInfoBuf[Index1] != NULL) { + FreePool(FmpImageInfoBuf[Index1]); + } + } + FreePool(FmpImageInfoBuf); + } + + return Status; +} + +/** + This function locks up Esrt repository to be readonly. It should be called + before gEfiEndOfDxeEventGroupGuid event signaled + + @retval EFI_SUCCESS Locks up FMP Non-FMP repository successfully + +**/ +EFI_STATUS +EFIAPI +EsrtDxeLockEsrtRepository( + VOID + ) +{ + EFI_STATUS Status; + EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock; + // + // Mark ACPI_GLOBAL_VARIABLE variable to read-only if the Variable Lock protocol exists + // + Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock); + if (!EFI_ERROR (Status)) { + Status = VariableLock->RequestToLock (VariableLock, EFI_ESRT_FMP_VARIABLE_NAME, &gEfiCallerIdGuid); + DEBUG((EFI_D_INFO, "EsrtDxe Lock EsrtFmp Variable Status 0x%x", Status)); + + Status = VariableLock->RequestToLock (VariableLock, EFI_ESRT_NONFMP_VARIABLE_NAME, &gEfiCallerIdGuid); + DEBUG((EFI_D_INFO, "EsrtDxe Lock EsrtNonFmp Variable Status 0x%x", Status)); + } + + return Status; +} + +/** + Notify function for event group EFI_EVENT_GROUP_READY_TO_BOOT. This is used to + install the Esrt Table into system configuration 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 *EsrtTable; + EFI_SYSTEM_RESOURCE_ENTRY *FmpEsrtRepository; + EFI_SYSTEM_RESOURCE_ENTRY *NonFmpEsrtRepository; + UINTN FmpRepositorySize; + UINTN NonFmpRepositorySize; + + + FmpEsrtRepository = NULL; + NonFmpEsrtRepository = NULL; + FmpRepositorySize = 0; + NonFmpRepositorySize = 0; + + Status = EfiAcquireLockOrFail (&mPrivate.NonFmpLock); + if (EFI_ERROR (Status)) { + return; + } + + Status = GetVariable2 ( + EFI_ESRT_NONFMP_VARIABLE_NAME, + &gEfiCallerIdGuid, + (VOID **) &NonFmpEsrtRepository, + &NonFmpRepositorySize + ); + + if (EFI_ERROR(Status)) { + NonFmpRepositorySize = 0; + } + + if (NonFmpRepositorySize % sizeof(EFI_SYSTEM_RESOURCE_ENTRY) != 0) { + DEBUG((EFI_D_ERROR, "NonFmp Repository Corrupt. Need to rebuild NonFmp Repository.\n")); + NonFmpRepositorySize = 0; + } + + EfiReleaseLock(&mPrivate.NonFmpLock); + + Status = EfiAcquireLockOrFail (&mPrivate.FmpLock); + Status = GetVariable2 ( + EFI_ESRT_FMP_VARIABLE_NAME, + &gEfiCallerIdGuid, + (VOID **) &FmpEsrtRepository, + &FmpRepositorySize + ); + + if (EFI_ERROR(Status)) { + FmpRepositorySize = 0; + } + + if (FmpRepositorySize % sizeof(EFI_SYSTEM_RESOURCE_ENTRY) != 0) { + DEBUG((EFI_D_ERROR, "Fmp Repository Corrupt. Need to rebuild Fmp Repository.\n")); + FmpRepositorySize = 0; + } + + EfiReleaseLock(&mPrivate.FmpLock); + + // + // Skip ESRT table publish if no ESRT entry exists + // + if (NonFmpRepositorySize + FmpRepositorySize == 0) { + goto EXIT; + } + + EsrtTable = AllocatePool(sizeof(EFI_SYSTEM_RESOURCE_TABLE) + NonFmpRepositorySize + FmpRepositorySize); + if (EsrtTable == NULL) { + DEBUG ((EFI_D_ERROR, "Esrt table memory allocation failure\n")); + goto EXIT; + } + + EsrtTable->FwResourceVersion = EFI_SYSTEM_RESOURCE_TABLE_FIRMWARE_RESOURCE_VERSION; + EsrtTable->FwResourceCount = (UINT32)((NonFmpRepositorySize + FmpRepositorySize) / sizeof(EFI_SYSTEM_RESOURCE_ENTRY)); + EsrtTable->FwResourceCountMax = PcdGet32(PcdMaxNonFmpEsrtCacheNum) + PcdGet32(PcdMaxFmpEsrtCacheNum); + + if (NonFmpRepositorySize != 0 && NonFmpEsrtRepository != NULL) { + CopyMem(EsrtTable + 1, NonFmpEsrtRepository, NonFmpRepositorySize); + } + + if (FmpRepositorySize != 0 && FmpEsrtRepository != NULL) { + CopyMem((UINT8 *)(EsrtTable + 1) + NonFmpRepositorySize, FmpEsrtRepository, FmpRepositorySize); + } + + // + // Publish Esrt to system config table + // + Status = gBS->InstallConfigurationTable (&gEfiSystemResourceTableGuid, EsrtTable); + + // + // Only one successful install + // + gBS->CloseEvent(Event); + +EXIT: + + if (FmpEsrtRepository != NULL) { + FreePool(FmpEsrtRepository); + } + + if (NonFmpEsrtRepository != NULL) { + FreePool(NonFmpEsrtRepository); + } +} + +/** + The module Entry Point of the Esrt DXE driver that manages cached ESRT repository + & publishes ESRT table + + @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 +EsrtDxeEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + EfiInitializeLock (&mPrivate.FmpLock, TPL_CALLBACK); + EfiInitializeLock (&mPrivate.NonFmpLock, TPL_CALLBACK); + + // + // Install Esrt management Protocol + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &mPrivate.Handle, + &gEsrtManagementProtocolGuid, + &mEsrtManagementProtocolTemplate, + NULL + ); + ASSERT_EFI_ERROR (Status); + + // + // Register notify function to install Esrt Table on ReadyToBoot Event. + // + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + EsrtReadyToBootEventNotify, + NULL, + &gEfiEventReadyToBootGuid, + &mPrivate.Event + ); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; +} |