/** @file Esrt management implementation. Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "EsrtImpl.h" /** Find Esrt Entry stored in ESRT repository. @param[in] FwClass Firmware class guid in Esrt entry @param[in] Attribute Esrt from Non FMP or FMP instance @param[out] Entry Esrt entry returned @retval EFI_SUCCESS Successfully find an Esrt entry @retval EF_NOT_FOUND No Esrt entry found **/ EFI_STATUS GetEsrtEntry ( IN EFI_GUID *FwClass, IN UINTN Attribute, OUT EFI_SYSTEM_RESOURCE_ENTRY *Entry ) { EFI_STATUS Status; CHAR16 *VariableName; EFI_SYSTEM_RESOURCE_ENTRY *EsrtRepository; UINTN RepositorySize; UINTN Index; UINTN EsrtNum; EsrtRepository = NULL; // // Get Esrt index buffer // if (Attribute == ESRT_FROM_FMP) { VariableName = EFI_ESRT_FMP_VARIABLE_NAME; } else { VariableName = EFI_ESRT_NONFMP_VARIABLE_NAME; } Status = GetVariable2 ( VariableName, &gEfiCallerIdGuid, (VOID **) &EsrtRepository, &RepositorySize ); if (EFI_ERROR(Status)) { goto EXIT; } if (RepositorySize % sizeof(EFI_SYSTEM_RESOURCE_ENTRY) != 0) { DEBUG((EFI_D_ERROR, "Repository Corrupt. Need to rebuild Repository.\n")); Status = EFI_ABORTED; goto EXIT; } Status = EFI_NOT_FOUND; EsrtNum = RepositorySize/sizeof(EFI_SYSTEM_RESOURCE_ENTRY); for (Index = 0; Index < EsrtNum; Index++) { if (CompareGuid(FwClass, &EsrtRepository[Index].FwClass)) { CopyMem(Entry, &EsrtRepository[Index], sizeof(EFI_SYSTEM_RESOURCE_ENTRY)); Status = EFI_SUCCESS; break; } } EXIT: if (EsrtRepository != NULL) { FreePool(EsrtRepository); } return Status; } /** Insert a new ESRT entry into ESRT Cache repository. @param[in] Entry Esrt entry to be set @param[in] Attribute Esrt from Esrt private protocol or FMP instance @retval EFI_SUCCESS Successfully set a variable. **/ EFI_STATUS InsertEsrtEntry( IN EFI_SYSTEM_RESOURCE_ENTRY *Entry, UINTN Attribute ) { EFI_STATUS Status; CHAR16 *VariableName; EFI_SYSTEM_RESOURCE_ENTRY *EsrtRepository; UINTN RepositorySize; EFI_SYSTEM_RESOURCE_ENTRY *EsrtRepositoryNew; EsrtRepository = NULL; EsrtRepositoryNew = NULL; // // Get Esrt index buffer // if (Attribute == ESRT_FROM_FMP) { VariableName = EFI_ESRT_FMP_VARIABLE_NAME; } else { VariableName = EFI_ESRT_NONFMP_VARIABLE_NAME; } Status = GetVariable2 ( VariableName, &gEfiCallerIdGuid, (VOID **) &EsrtRepository, &RepositorySize ); if (Status == EFI_NOT_FOUND) { // // If not exist, create new Esrt cache repository // Status = gRT->SetVariable( VariableName, &gEfiCallerIdGuid, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, sizeof(EFI_SYSTEM_RESOURCE_ENTRY), Entry ); return Status; } else if (Status == EFI_SUCCESS) { // // if exist, update Esrt cache repository // if (RepositorySize % sizeof(EFI_SYSTEM_RESOURCE_ENTRY) != 0) { DEBUG((EFI_D_ERROR, "Repository Corrupt. Need to rebuild Repository.\n")); // // Repository is corrupt. Clear Repository before insert new entry // Status = gRT->SetVariable( VariableName, &gEfiCallerIdGuid, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, 0, EsrtRepository ); FreePool(EsrtRepository); RepositorySize = 0; EsrtRepository = NULL; } // // Check Repository size constraint // if ((Attribute == ESRT_FROM_FMP && RepositorySize >= PcdGet32(PcdMaxFmpEsrtCacheNum) * sizeof(EFI_SYSTEM_RESOURCE_ENTRY)) ||(Attribute == ESRT_FROM_NONFMP && RepositorySize >= PcdGet32(PcdMaxNonFmpEsrtCacheNum) * sizeof(EFI_SYSTEM_RESOURCE_ENTRY)) ) { Status = EFI_OUT_OF_RESOURCES; goto EXIT; } EsrtRepositoryNew = AllocatePool(RepositorySize + sizeof(EFI_SYSTEM_RESOURCE_ENTRY)); if (EsrtRepositoryNew == NULL) { Status = EFI_OUT_OF_RESOURCES; goto EXIT; } if (RepositorySize != 0 && EsrtRepository != NULL) { CopyMem(EsrtRepositoryNew, EsrtRepository, RepositorySize); } CopyMem((UINT8 *)EsrtRepositoryNew + RepositorySize, Entry, sizeof(EFI_SYSTEM_RESOURCE_ENTRY)); Status = gRT->SetVariable( VariableName, &gEfiCallerIdGuid, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, RepositorySize + sizeof(EFI_SYSTEM_RESOURCE_ENTRY), EsrtRepositoryNew ); } EXIT: if (EsrtRepository != NULL) { FreePool(EsrtRepository); } if (EsrtRepositoryNew != NULL) { FreePool(EsrtRepositoryNew); } return Status; } /** Delete ESRT Entry from ESRT repository. @param[in] FwClass FwClass of Esrt entry to delete @param[in] Attribute Esrt from Esrt private protocol or FMP instance @retval EFI_SUCCESS Insert all entries Successfully @retval EFI_NOT_FOUND ESRT entry with FwClass doesn't exsit **/ EFI_STATUS DeleteEsrtEntry( IN EFI_GUID *FwClass, IN UINTN Attribute ) { EFI_STATUS Status; CHAR16 *VariableName; EFI_SYSTEM_RESOURCE_ENTRY *EsrtRepository; UINTN RepositorySize; UINTN Index; UINTN EsrtNum; EsrtRepository = NULL; // // Get Esrt index buffer // if (Attribute == ESRT_FROM_FMP) { VariableName = EFI_ESRT_FMP_VARIABLE_NAME; } else { VariableName = EFI_ESRT_NONFMP_VARIABLE_NAME; } Status = GetVariable2 ( VariableName, &gEfiCallerIdGuid, (VOID **) &EsrtRepository, &RepositorySize ); if (EFI_ERROR(Status)) { goto EXIT; } if ((RepositorySize % sizeof(EFI_SYSTEM_RESOURCE_ENTRY)) != 0) { DEBUG((EFI_D_ERROR, "Repository Corrupt. Need to rebuild Repository.\n")); // // Repository is corrupt. Clear Repository before insert new entry // Status = gRT->SetVariable( VariableName, &gEfiCallerIdGuid, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, 0, EsrtRepository ); goto EXIT; } Status = EFI_NOT_FOUND; EsrtNum = RepositorySize/sizeof(EFI_SYSTEM_RESOURCE_ENTRY); for (Index = 0; Index < EsrtNum; Index++) { // // Delete Esrt entry if it is found in repository // if (CompareGuid(FwClass, &EsrtRepository[Index].FwClass)) { // // If delete Esrt entry is not at the rail // if (Index < EsrtNum - 1) { CopyMem(&EsrtRepository[Index], &EsrtRepository[Index + 1], (EsrtNum - Index - 1) * sizeof(EFI_SYSTEM_RESOURCE_ENTRY)); } // // Update New Repository // Status = gRT->SetVariable( VariableName, &gEfiCallerIdGuid, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, (EsrtNum - 1) * sizeof(EFI_SYSTEM_RESOURCE_ENTRY), EsrtRepository ); break; } } EXIT: if (EsrtRepository != NULL) { FreePool(EsrtRepository); } return Status; } /** Update one ESRT entry in ESRT repository @param[in] Entry Esrt entry to be set @param[in] Attribute Esrt from Non Esrt or FMP instance @retval EFI_SUCCESS Successfully Update a variable. @retval EFI_NOT_FOUND The Esrt enry doesn't exist **/ EFI_STATUS UpdateEsrtEntry( IN EFI_SYSTEM_RESOURCE_ENTRY *Entry, UINTN Attribute ) { EFI_STATUS Status; CHAR16 *VariableName; EFI_SYSTEM_RESOURCE_ENTRY *EsrtRepository; UINTN RepositorySize; UINTN Index; UINTN EsrtNum; EsrtRepository = NULL; // // Get Esrt index buffer // if (Attribute == ESRT_FROM_FMP) { VariableName = EFI_ESRT_FMP_VARIABLE_NAME; } else { VariableName = EFI_ESRT_NONFMP_VARIABLE_NAME; } Status = GetVariable2 ( VariableName, &gEfiCallerIdGuid, (VOID **) &EsrtRepository, &RepositorySize ); if (!EFI_ERROR(Status)) { // // if exist, update Esrt cache repository // if (RepositorySize % sizeof(EFI_SYSTEM_RESOURCE_ENTRY) != 0) { DEBUG((EFI_D_ERROR, "Repository Corrupt. Need to rebuild Repository.\n")); // // Repository is corrupt. Clear Repository before insert new entry // Status = gRT->SetVariable( VariableName, &gEfiCallerIdGuid, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, 0, EsrtRepository ); Status = EFI_NOT_FOUND; goto EXIT; } Status = EFI_NOT_FOUND; EsrtNum = RepositorySize/sizeof(EFI_SYSTEM_RESOURCE_ENTRY); for (Index = 0; Index < EsrtNum; Index++) { // // Update Esrt entry if it is found in repository // if (CompareGuid(&Entry->FwClass, &EsrtRepository[Index].FwClass)) { CopyMem(&EsrtRepository[Index], Entry, sizeof(EFI_SYSTEM_RESOURCE_ENTRY)); // // Update New Repository // Status = gRT->SetVariable( VariableName, &gEfiCallerIdGuid, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, RepositorySize, EsrtRepository ); break; } } } EXIT: if (EsrtRepository != NULL) { FreePool(EsrtRepository); } 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; } /** Init one ESRT entry according to input FmpImageInfo (V1, V2, V3) . @param[in, out] EsrtEntry Esrt entry to be Init @param[in] FmpImageInfo FMP image info descriptor @param[in] DescriptorVersion FMP Image info descriptor version **/ VOID SetEsrtEntryFromFmpInfo ( IN OUT EFI_SYSTEM_RESOURCE_ENTRY *EsrtEntry, IN EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfo, IN UINT32 DescriptorVersion ) { EsrtEntry->FwVersion = FmpImageInfo->Version; EsrtEntry->FwClass = FmpImageInfo->ImageTypeId; if (IsSystemFmp(FmpImageInfo)) { EsrtEntry->FwType = ESRT_FW_TYPE_SYSTEMFIRMWARE; } else { EsrtEntry->FwType = ESRT_FW_TYPE_DEVICEFIRMWARE; } EsrtEntry->LowestSupportedFwVersion = 0; EsrtEntry->CapsuleFlags = 0; EsrtEntry->LastAttemptVersion = 0; EsrtEntry->LastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS; if (DescriptorVersion >= 2) { // // LowestSupportedImageVersion only available in FMP V2 or higher // EsrtEntry->LowestSupportedFwVersion = FmpImageInfo->LowestSupportedImageVersion; } if (DescriptorVersion >= 3) { // // LastAttemptVersion & LastAttemptStatus only available in FMP V3 or higher // EsrtEntry->LastAttemptVersion = FmpImageInfo->LastAttemptVersion; EsrtEntry->LastAttemptStatus = FmpImageInfo->LastAttemptStatus; } // // Set capsule customized flag // if ((FmpImageInfo->AttributesSupported & IMAGE_ATTRIBUTE_RESET_REQUIRED) != 0 && (FmpImageInfo->AttributesSetting & IMAGE_ATTRIBUTE_RESET_REQUIRED) != 0) { EsrtEntry->CapsuleFlags = PcdGet16(PcdSystemRebootAfterCapsuleProcessFlag); } }