/** @file
Copyright (c) 2024, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
@par Reference(s):
- Platform Security Firmware Update for the A-profile Specification 1.0
(https://developer.arm.com/documentation/den0118/latest)
@par Glossary:
- FW - Firmware
- FWU - Firmware Update
- FWS - Firmware Storage
- PSA - Platform Security update for the A-profile specification
- IFD - Image File Data
- IF - Image File
- IMG - Image
- MM - Management Mode
- PROP - Property
- Bkup, Bkp - Backup
**/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "DiskGpt.h"
#include "FwsMetadata.h"
/**
Matching function used to find out Firmware Update Metadata GPT partition.
@param [in] PartitionEntry GPT partition.
@param [in] Data Partition Name.
@retval TRUE Found.
@retval FALSE Not found.
**/
BOOLEAN
EFIAPI
MatchedMetaDataPartition (
IN CONST EFI_PARTITION_ENTRY *PartitionEntry,
IN CONST VOID *Data
)
{
CONST CHAR16 *PartitionName = (CONST CHAR16 *)Data;
if (CompareGuid (&PartitionEntry->PartitionTypeGUID, &gPsaFwuMetadataGuid) &&
(StrCmp (PartitionEntry->PartitionName, PartitionName) == 0))
{
return TRUE;
}
return FALSE;
}
/**
Check CRC32 of the FWU metadata against the CRC32 value
present in the FWU metadata.
@param [in] FwsMetadata Firmware Update Store Metadata.
@retval EFI_SUCCESS The CRC is valid.
@retval EFI_CRC_ERROR The CRC is invalid.
@retval EFI_INVALID_PARAMETER
**/
EFI_STATUS
EFIAPI
FwsMetadataCrcCheck (
IN FWS_METADATA *FwsMetadata
)
{
PSA_MM_FWU_METADATA_COMMON_HEADER *Header;
UINT32 Crc32;
if ((FwsMetadata == NULL) || (FwsMetadata->Metadata == NULL)) {
DEBUG ((DEBUG_ERROR, "%a: Invalid Arguments...\n", __func__));
return EFI_INVALID_PARAMETER;
}
Header = (PSA_MM_FWU_METADATA_COMMON_HEADER *)FwsMetadata->Metadata;
Crc32 = CalculateCrc32 (
(VOID *)Header + sizeof (Header->Crc32),
FwsMetadata->MetadataSize - sizeof (Header->Crc32)
);
if (Crc32 != Header->Crc32) {
DEBUG ((DEBUG_ERROR, "%a: Invalid Arguments...\n", __func__));
return EFI_CRC_ERROR;
}
return EFI_SUCCESS;
}
/**
Save FWU metadata structure to GPT partition.
@param [in] FwsMetadata Firmware Update Store Metadata.
@retval EFI_SUCCESS
@retval EFI_INVALID_PARAMETER
@retval Others Failed to write metadata to GPT partition.
**/
EFI_STATUS
EFIAPI
FwsMetadataSave (
IN FWS_METADATA *FwsMetadata
)
{
EFI_STATUS Status;
PSA_MM_FWU_METADATA_COMMON_HEADER *Header;
UINT32 Crc32;
CONST UINTN MaxRetries = 3;
UINTN Retries;
if ((FwsMetadata == NULL) ||
(FwsMetadata->GptHandle == NULL) ||
(FwsMetadata->Metadata == NULL))
{
DEBUG ((DEBUG_ERROR, "%a: Invalid Arguments...\n", __func__));
return EFI_INVALID_PARAMETER;
}
Header = (PSA_MM_FWU_METADATA_COMMON_HEADER *)FwsMetadata->Metadata;
Crc32 = CalculateCrc32 (
(VOID *)Header + sizeof (Header->Crc32),
FwsMetadata->MetadataSize - sizeof (Header->Crc32)
);
Header->Crc32 = Crc32;
for (Retries = MaxRetries; Retries > 0; Retries--) {
Status = GptWritePartition (
FwsMetadata->GptHandle,
FwsMetadata->Metadata,
FwsMetadata->MetadataSize,
0,
FwsMetadata->ActiveLba
);
if (!EFI_ERROR (Status)) {
break;
}
}
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "Active metadata failed to persist! FW Store may be inoperative.\n"));
return Status;
}
for (Retries = MaxRetries; Retries > 0; Retries--) {
Status = GptWritePartition (
FwsMetadata->GptHandle,
FwsMetadata->Metadata,
FwsMetadata->MetadataSize,
0,
FwsMetadata->BackupLba
);
if (!EFI_ERROR (Status)) {
break;
}
}
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "Backup metadata failed to persist! FW Store may be inoperative.\n"));
return Status;
}
return EFI_SUCCESS;
}
/**
Get active bank index.
@param [in] FwsMetadata Firmware Update Store Metadata.
@param [out] ActiveIndex Active bank index
@retval EFI_SUCCESS
@retval EFI_INVALID_PARAMETER
**/
EFI_STATUS
EFIAPI
FwsMetadataGetActiveIndex (
IN FWS_METADATA *FwsMetadata,
UINT32 *ActiveIndex
)
{
PSA_MM_FWU_METADATA_COMMON_HEADER *Header;
if ((FwsMetadata == NULL) || (FwsMetadata->Metadata == NULL) ||
(ActiveIndex == NULL))
{
DEBUG ((DEBUG_ERROR, "%a: Invalid Arguments...\n", __func__));
return EFI_INVALID_PARAMETER;
}
Header = (PSA_MM_FWU_METADATA_COMMON_HEADER *)FwsMetadata->Metadata;
*ActiveIndex = Header->ActiveIndex;
return EFI_SUCCESS;
}
/**
Set active bank index.
@param [in] FwsMetadata Firmware Update Store Metadata.
@param [in] ActiveIndex Active bank index
@retval EFI_SUCCESS
@retval EFI_INVALID_PARAMETER
**/
EFI_STATUS
EFIAPI
FwsMetadataSetActiveIndex (
IN FWS_METADATA *FwsMetadata,
UINT32 ActiveIndex
)
{
PSA_MM_FWU_METADATA_COMMON_HEADER *Header;
UINT8 NumBanks;
if ((FwsMetadata == NULL) || (FwsMetadata->Metadata == NULL)) {
DEBUG ((DEBUG_ERROR, "%a: Invalid Arguments...\n", __func__));
return EFI_INVALID_PARAMETER;
}
FwsMetadata->FwsMetadataOps->GetNumBanks (FwsMetadata, &NumBanks);
if (ActiveIndex >= NumBanks) {
DEBUG ((DEBUG_ERROR, "%a: Invalid Arguments...\n", __func__));
return EFI_INVALID_PARAMETER;
}
Header = (PSA_MM_FWU_METADATA_COMMON_HEADER *)FwsMetadata->Metadata;
Header->ActiveIndex = ActiveIndex;
return EFI_SUCCESS;
}
/**
Get previous active bank index.
@param [in] FwsMetadata Firmware Update Store Metadata.
@param [out] PreviousActiveIndex Previous Active bank index
@retval EFI_SUCCESS
@retval EFI_INVALID_PARAMETER
**/
EFI_STATUS
EFIAPI
FwsMetadataGetPreviousActiveIndex (
IN FWS_METADATA *FwsMetadata,
UINT32 *PreviousActiveIndex
)
{
PSA_MM_FWU_METADATA_COMMON_HEADER *Header;
if ((FwsMetadata == NULL) || (FwsMetadata->Metadata == NULL) ||
(PreviousActiveIndex == NULL))
{
DEBUG ((DEBUG_ERROR, "%a: Invalid Arguments...\n", __func__));
return EFI_INVALID_PARAMETER;
}
Header = (PSA_MM_FWU_METADATA_COMMON_HEADER *)FwsMetadata->Metadata;
*PreviousActiveIndex = Header->PreviousActiveIndex;
return EFI_SUCCESS;
}
/**
Set previous active bank index.
@param [in] FwsMetadata Firmware Update Store Metadata.
@param [in] PreviousActiveIndex Previous Active bank index
@retval EFI_SUCCESS
@retval EFI_INVALID_PARAMETER
**/
EFI_STATUS
EFIAPI
FwsMetadataSetPreviousActiveIndex (
IN FWS_METADATA *FwsMetadata,
UINT32 PreviousActiveIndex
)
{
PSA_MM_FWU_METADATA_COMMON_HEADER *Header;
UINT8 NumBanks;
if ((FwsMetadata == NULL) || (FwsMetadata->Metadata == NULL)) {
DEBUG ((DEBUG_ERROR, "%a: Invalid Arguments...\n", __func__));
return EFI_INVALID_PARAMETER;
}
FwsMetadata->FwsMetadataOps->GetNumBanks (FwsMetadata, &NumBanks);
if (PreviousActiveIndex >= NumBanks) {
DEBUG ((DEBUG_ERROR, "%a: Invalid Arguments...\n", __func__));
return EFI_INVALID_PARAMETER;
}
Header = (PSA_MM_FWU_METADATA_COMMON_HEADER *)FwsMetadata->Metadata;
Header->PreviousActiveIndex = PreviousActiveIndex;
return EFI_SUCCESS;
}
/**
Initialize and Load Firmware Update Store Metadata.
@param [in] GptHandle GptHandle
@param [out] FwsMetadata Firmware Update Store Metadata.
@retval EFI_SUCCESS
@retval EFI_INVALID_PARAMETER
@retval EFI_OUT_OF_RESOURCES
@retval EFI_ABORTED Metadata is invalid.
@retval Others Failed to get metadata from gpt partition.
**/
EFI_STATUS
EFIAPI
FwsMetadataInit (
IN GPT_PARTITION_HANDLE *GptHandle,
OUT FWS_METADATA **FwsMetadata
)
{
EFI_STATUS Status;
PSA_MM_FWU_METADATA_COMMON_HEADER Header;
EFI_LBA StartLba;
FWS_METADATA_OPS *FwsMetadataOps;
if ((GptHandle == NULL) || (FwsMetadata == NULL)) {
DEBUG ((DEBUG_ERROR, "%a: Invalid Arguments...\n", __func__));
return EFI_INVALID_PARAMETER;
}
Status = GptGetMatchedPartitionStats (
GptHandle,
MatchedMetaDataPartition,
(VOID *)FWU_METADATA_PARTITION_NAME,
&StartLba,
NULL
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: Failed to get partition info: %r\n", __func__, Status));
goto ErrorHandler;
}
Status = GptReadPartition (
GptHandle,
(VOID *)&Header,
sizeof (PSA_MM_FWU_METADATA_COMMON_HEADER),
0,
StartLba
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: Failed to read metadata common header: %r\n", __func__, Status));
goto ErrorHandler;
}
switch (Header.Version) {
case FWS_METADATA_FORMAT_V2:
FwsMetadataOps = &gFwsMetadataV2Ops;
break;
default:
DEBUG ((DEBUG_ERROR, "%a: Invalid Metadata Version: %d\n", __func__, Header.Version));
return EFI_UNSUPPORTED;
}
Status = FwsMetadataOps->InitMetadata (GptHandle, FwsMetadata);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: Failed to alloc: %r\n", __func__, Status));
goto ErrorHandler;
}
return EFI_SUCCESS;
ErrorHandler:
*FwsMetadata = NULL;
return Status;
}
/**
Cleanup Loaded Firmware Update Store Metadata.
@param [in] FwsMetadata Firmware Update Store Metadata.
**/
VOID
EFIAPI
FwsMetadataExit (
IN FWS_METADATA *FwsMetadata
)
{
if (FwsMetadata == NULL) {
return;
}
FwsMetadata->FwsMetadataOps->ExitMetadata (FwsMetadata);
}
/**
Get the number of banks.
@param [in] FwsMetadata Firmware Update Store Metadata.
@param [out] NumBanks Number of banks.
@retval EFI_SUCCESS
@retval EFI_INVALID_PARAMETER
**/
EFI_STATUS
EFIAPI
FwsMetadataGetNumBanks (
IN FWS_METADATA *FwsMetadata,
OUT UINT8 *NumBanks
)
{
if ((FwsMetadata == NULL) || (FwsMetadata->Metadata == NULL) || (NumBanks == NULL)) {
DEBUG ((DEBUG_ERROR, "%a: Invalid arguments...\n", __func__));
return EFI_INVALID_PARAMETER;
}
return FwsMetadata->FwsMetadataOps->GetNumBanks (FwsMetadata, NumBanks);
}
/**
Get the number of images per bank.
@param [in] FwsMetadata Firmware Update Store Metadata.
@param [out] NumImages Number of images per bank.
@retval EFI_SUCCESS
@retval EFI_INVALID_PARAMETER
**/
EFI_STATUS
EFIAPI
FwsMetadataGetNumImages (
IN FWS_METADATA *FwsMetadata,
OUT UINT16 *NumImages
)
{
if ((FwsMetadata == NULL) || (FwsMetadata->Metadata == NULL) || (NumImages == NULL)) {
DEBUG ((DEBUG_ERROR, "%a: Invalid arguments...\n", __func__));
return EFI_INVALID_PARAMETER;
}
return FwsMetadata->FwsMetadataOps->GetNumImages (FwsMetadata, NumImages);
}
/**
Check trial state.
@param [in] FwsMetadata Firmware Update Store Metadata.
@param [out] TrialRunState If true, current is in the trial state.
@retval EFI_SUCCESS
@retval EFI_INVALID_PARAMETER
**/
EFI_STATUS
EFIAPI
FwsMetadataCheckTrialRunState (
IN FWS_METADATA *FwsMetadata,
OUT BOOLEAN *TrialRunState
)
{
if ((FwsMetadata == NULL) || (FwsMetadata->Metadata == NULL) || (TrialRunState == NULL)) {
DEBUG ((DEBUG_ERROR, "%a: Invalid arguments...\n", __func__));
return EFI_INVALID_PARAMETER;
}
return FwsMetadata->FwsMetadataOps->CheckTrialRunState (FwsMetadata, TrialRunState);
}
/**
Get all image type guids.
@param [in] FwsMetadata Firmware Update Store Metadata.
@param [out] ImageTypeGuids Image type guid array.
@param [in,out] Size Byte size of @ImageTypeGuids.
@retval EFI_SUCCESS
@retval EFI_INVALID_PARAMETER
@retval EFI_BUFFER_TOO_SMALL Size of @ImageTypeGuids is too small to
contain all image type guids manged by
firmware update store.
**/
EFI_STATUS
EFIAPI
FwsMetadataGetImageTypeGuids (
IN FWS_METADATA *FwsMetadata,
OUT EFI_GUID *ImageTypeGuids,
IN OUT UINT32 *Size
)
{
if ((FwsMetadata == NULL) || (FwsMetadata->Metadata == NULL) ||
(ImageTypeGuids == NULL) || (Size == NULL))
{
DEBUG ((DEBUG_ERROR, "%a: Invalid arguments...\n", __func__));
return EFI_INVALID_PARAMETER;
}
return FwsMetadata->FwsMetadataOps->GetImageTypeGuids (
FwsMetadata,
ImageTypeGuids,
Size
);
}
/**
Get location guid of image type guid.
@param [in] FwsMetadata Firmware Update Store Metadata.
@param [in] ImageTypeGuid Image type guid.
@param [out] LocationGuid Location Guids.
@retval EFI_SUCCESS
@retval EFI_INVALID_PARAMETER
**/
EFI_STATUS
EFIAPI
FwsMetadataGetLocationGuid (
IN FWS_METADATA *FwsMetadata,
IN CONST EFI_GUID *ImageTypeGuid,
OUT EFI_GUID *LocationGuid
)
{
if ((FwsMetadata == NULL) || (FwsMetadata->Metadata == NULL) ||
(ImageTypeGuid == NULL) || (LocationGuid == NULL))
{
DEBUG ((DEBUG_ERROR, "%a: Invalid arguments...\n", __func__));
return EFI_INVALID_PARAMETER;
}
return FwsMetadata->FwsMetadataOps->GetLocationGuid (
FwsMetadata,
ImageTypeGuid,
LocationGuid
);
}
/**
Get image guid of image type guid.
@param [in] FwsMetadata Firmware Update Store Metadata.
@param [in] ImageTypeGuid Image type guid.
@param [in] BankIdx Bank Index.
@param [out] ImageGuid Image Guids.
@retval EFI_SUCCESS
@retval EFI_INVALID_PARAMETER
**/
EFI_STATUS
EFIAPI
FwsMetadataGetImageGuid (
IN FWS_METADATA *FwsMetadata,
IN CONST EFI_GUID *ImageTypeGuid,
IN UINT32 BankIdx,
OUT EFI_GUID *ImageGuid
)
{
if ((FwsMetadata == NULL) || (FwsMetadata->Metadata == NULL) ||
(ImageTypeGuid == NULL) || (ImageGuid == NULL))
{
DEBUG ((DEBUG_ERROR, "%a: Invalid arguments...\n", __func__));
return EFI_INVALID_PARAMETER;
}
return FwsMetadata->FwsMetadataOps->GetImageGuid (
FwsMetadata,
ImageTypeGuid,
BankIdx,
ImageGuid
);
}
/**
Get accept state of image type guid.
@param [in] FwsMetadata Firmware Update Store Metadata.
@param [in] ImageTypeGuid Image type guid.
@param [in] BankIdx Bank Index
@param [out] AcceptState If true, @ImageTypeGuid in the @BankIdx
is accepted.
@retval EFI_SUCCESS
@retval EFI_INVALID_PARAMETER
**/
EFI_STATUS
EFIAPI
FwsMetadataGetAcceptState (
IN FWS_METADATA *FwsMetadata,
IN CONST EFI_GUID *ImageTypeGuid,
IN UINT32 BankIdx,
OUT BOOLEAN *AcceptState
)
{
if ((FwsMetadata == NULL) || (FwsMetadata->Metadata == NULL) ||
(ImageTypeGuid == NULL) || (AcceptState == NULL))
{
DEBUG ((DEBUG_ERROR, "%a: Invalid arguments...\n", __func__));
return EFI_INVALID_PARAMETER;
}
return FwsMetadata->FwsMetadataOps->GetAcceptState (
FwsMetadata,
ImageTypeGuid,
BankIdx,
AcceptState
);
}
/**
Set accept state of image type guid.
@param [in] FwsMetadata Firmware Update Store Metadata.
@param [in] ImageTypeGuid Image type guid.
@param [in] BankIdx Bank Index
@param [in] AcceptReq Type of request.
@retval EFI_SUCCESS
@retval EFI_INVALID_PARAMETER
**/
EFI_STATUS
EFIAPI
FwsMetadataSetAcceptState (
IN FWS_METADATA *FwsMetadata,
IN CONST EFI_GUID *ImageTypeGuid,
IN UINT32 BankIdx,
IN FWS_ACCEPT_REQ AcceptReq
)
{
if ((FwsMetadata == NULL) || (FwsMetadata->Metadata == NULL) ||
(ImageTypeGuid == NULL))
{
DEBUG ((DEBUG_ERROR, "%a: Invalid arguments...\n", __func__));
return EFI_INVALID_PARAMETER;
}
if (AcceptReq > FwsImageWriteUnAcceptReq) {
DEBUG ((DEBUG_ERROR, "%a: Invalid arguments...\n", __func__));
return EFI_INVALID_PARAMETER;
}
return FwsMetadata->FwsMetadataOps->SetAcceptState (
FwsMetadata,
ImageTypeGuid,
BankIdx,
AcceptReq
);
}
/**
Update bank state.
@param [in] FwsMetadata Firmware Update Store Metadata.
@param [in] BankIdx Bank Index
@retval EFI_SUCCESS
@retval EFI_INVALID_PARAMETER
**/
EFI_STATUS
EFIAPI
FwsMetadataUpdateBankState (
IN FWS_METADATA *FwsMetadata,
IN UINT32 BankIdx
)
{
if ((FwsMetadata == NULL) || (FwsMetadata->Metadata == NULL)) {
DEBUG ((DEBUG_ERROR, "%a: Invalid arguments...\n", __func__));
return EFI_INVALID_PARAMETER;
}
return FwsMetadata->FwsMetadataOps->UpdateBankState (FwsMetadata, BankIdx);
}
/**
Rollback metadata from @BackupIdx's one to @@TargetIdx's one
@param [in] FwsMetadata Firmware Update Store Metadata.
@param [in] BackupIdx Backup Bank Index
@param [in] TargetIdx Target Bank Index
@retval EFI_SUCCESS
@retval EFI_INVALID_PARAMETER
**/
EFI_STATUS
EFIAPI
FwsMetadataRollBack (
IN FWS_METADATA *FwsMetadata,
IN UINT32 BackupIdx,
IN UINT32 TargetIdx
)
{
if ((FwsMetadata == NULL) || (FwsMetadata->Metadata == NULL)) {
DEBUG ((DEBUG_ERROR, "%a: Invalid arguments...\n", __func__));
return EFI_INVALID_PARAMETER;
}
return FwsMetadata->FwsMetadataOps->RollBack (FwsMetadata, BackupIdx, TargetIdx);
}