CloverBootloader/MdeModulePkg/Universal/EsrtDxe/EsrtDxe.c
2019-09-03 12:58:42 +03:00

662 lines
18 KiB
C

/** @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;
}