audk/IntelFrameworkModulePkg/Universal/FirmwareVolume/FwVolDxe/Ffs.c

602 lines
14 KiB
C

/** @file
FFS file access utilities.
Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "FwVolDriver.h"
#define PHYSICAL_ADDRESS_TO_POINTER(Address) ((VOID *) ((UINTN) Address))
/**
Set File State in the FfsHeader.
@param State File state to be set into FFS header.
@param FfsHeader Points to the FFS file header
**/
VOID
SetFileState (
IN UINT8 State,
IN EFI_FFS_FILE_HEADER *FfsHeader
)
{
//
// Set File State in the FfsHeader
//
FfsHeader->State = (EFI_FFS_FILE_STATE) (FfsHeader->State ^ State);
return ;
}
/**
Get the FFS file state by checking the highest bit set in the header's state field.
@param ErasePolarity Erase polarity attribute of the firmware volume
@param FfsHeader Points to the FFS file header
@return FFS File state
**/
EFI_FFS_FILE_STATE
GetFileState (
IN UINT8 ErasePolarity,
IN EFI_FFS_FILE_HEADER *FfsHeader
)
{
EFI_FFS_FILE_STATE FileState;
UINT8 HighestBit;
FileState = FfsHeader->State;
if (ErasePolarity != 0) {
FileState = (EFI_FFS_FILE_STATE)~FileState;
}
HighestBit = 0x80;
while (HighestBit != 0 && ((HighestBit & FileState) == 0)) {
HighestBit >>= 1;
}
return (EFI_FFS_FILE_STATE) HighestBit;
}
/**
Convert the Buffer Address to LBA Entry Address.
@param FvDevice Cached FvDevice
@param BufferAddress Address of Buffer
@param LbaListEntry Pointer to the got LBA entry that contains the address.
@retval EFI_NOT_FOUND Buffer address is out of FvDevice.
@retval EFI_SUCCESS LBA entry is found for Buffer address.
**/
EFI_STATUS
Buffer2LbaEntry (
IN FV_DEVICE *FvDevice,
IN EFI_PHYSICAL_ADDRESS BufferAddress,
OUT LBA_ENTRY **LbaListEntry
)
{
LBA_ENTRY *LbaEntry;
LIST_ENTRY *Link;
Link = FvDevice->LbaHeader.ForwardLink;
LbaEntry = (LBA_ENTRY *) Link;
//
// Locate LBA which contains the address
//
while (&LbaEntry->Link != &FvDevice->LbaHeader) {
if ((EFI_PHYSICAL_ADDRESS) (UINTN) (LbaEntry->StartingAddress) > BufferAddress) {
break;
}
Link = LbaEntry->Link.ForwardLink;
LbaEntry = (LBA_ENTRY *) Link;
}
if (&LbaEntry->Link == &FvDevice->LbaHeader) {
return EFI_NOT_FOUND;
}
Link = LbaEntry->Link.BackLink;
LbaEntry = (LBA_ENTRY *) Link;
if (&LbaEntry->Link == &FvDevice->LbaHeader) {
return EFI_NOT_FOUND;
}
*LbaListEntry = LbaEntry;
return EFI_SUCCESS;
}
/**
Convert the Buffer Address to LBA Address & Offset.
@param FvDevice Cached FvDevice
@param BufferAddress Address of Buffer
@param Lba Pointer to the gob Lba value
@param Offset Pointer to the got Offset
@retval EFI_NOT_FOUND Buffer address is out of FvDevice.
@retval EFI_SUCCESS LBA and Offset is found for Buffer address.
**/
EFI_STATUS
Buffer2Lba (
IN FV_DEVICE *FvDevice,
IN EFI_PHYSICAL_ADDRESS BufferAddress,
OUT EFI_LBA *Lba,
OUT UINTN *Offset
)
{
LBA_ENTRY *LbaEntry;
EFI_STATUS Status;
LbaEntry = NULL;
Status = Buffer2LbaEntry (
FvDevice,
BufferAddress,
&LbaEntry
);
if (EFI_ERROR (Status)) {
return Status;
}
*Lba = LbaEntry->LbaIndex;
*Offset = (UINTN) BufferAddress - (UINTN) LbaEntry->StartingAddress;
return EFI_SUCCESS;
}
/**
Check if a block of buffer is erased.
@param ErasePolarity Erase polarity attribute of the firmware volume
@param Buffer The buffer to be checked
@param BufferSize Size of the buffer in bytes
@retval TRUE The block of buffer is erased
@retval FALSE The block of buffer is not erased
**/
BOOLEAN
IsBufferErased (
IN UINT8 ErasePolarity,
IN UINT8 *Buffer,
IN UINTN BufferSize
)
{
UINTN Count;
UINT8 EraseByte;
if (ErasePolarity == 1) {
EraseByte = 0xFF;
} else {
EraseByte = 0;
}
for (Count = 0; Count < BufferSize; Count++) {
if (Buffer[Count] != EraseByte) {
return FALSE;
}
}
return TRUE;
}
/**
Verify checksum of the firmware volume header.
@param FvHeader Points to the firmware volume header to be checked
@retval TRUE Checksum verification passed
@retval FALSE Checksum verification failed
**/
BOOLEAN
VerifyFvHeaderChecksum (
IN EFI_FIRMWARE_VOLUME_HEADER *FvHeader
)
{
UINT16 Checksum;
Checksum = CalculateSum16 ((UINT16 *) FvHeader, FvHeader->HeaderLength);
if (Checksum == 0) {
return TRUE;
} else {
return FALSE;
}
}
/**
Verify checksum of the FFS file header.
@param FfsHeader Points to the FFS file header to be checked
@retval TRUE Checksum verification passed
@retval FALSE Checksum verification failed
**/
BOOLEAN
VerifyHeaderChecksum (
IN EFI_FFS_FILE_HEADER *FfsHeader
)
{
UINT8 HeaderChecksum;
if (IS_FFS_FILE2 (FfsHeader)) {
HeaderChecksum = CalculateSum8 ((UINT8 *) FfsHeader, sizeof (EFI_FFS_FILE_HEADER2));
} else {
HeaderChecksum = CalculateSum8 ((UINT8 *) FfsHeader, sizeof (EFI_FFS_FILE_HEADER));
}
HeaderChecksum = (UINT8) (HeaderChecksum - FfsHeader->State - FfsHeader->IntegrityCheck.Checksum.File);
if (HeaderChecksum == 0) {
return TRUE;
} else {
return FALSE;
}
}
/**
Verify checksum of the FFS file data.
@param FfsHeader Points to the FFS file header to be checked
@retval TRUE Checksum verification passed
@retval FALSE Checksum verification failed
**/
BOOLEAN
VerifyFileChecksum (
IN EFI_FFS_FILE_HEADER *FfsHeader
)
{
UINT8 FileChecksum;
EFI_FV_FILE_ATTRIBUTES Attributes;
Attributes = FfsHeader->Attributes;
if ((Attributes & FFS_ATTRIB_CHECKSUM) != 0) {
//
// Check checksum of FFS data
//
if (IS_FFS_FILE2 (FfsHeader)) {
FileChecksum = CalculateSum8 ((UINT8 *) FfsHeader + sizeof (EFI_FFS_FILE_HEADER2), FFS_FILE2_SIZE (FfsHeader) - sizeof (EFI_FFS_FILE_HEADER2));
} else {
FileChecksum = CalculateSum8 ((UINT8 *) FfsHeader + sizeof (EFI_FFS_FILE_HEADER), FFS_FILE_SIZE (FfsHeader) - sizeof (EFI_FFS_FILE_HEADER));
}
FileChecksum = (UINT8) (FileChecksum + FfsHeader->IntegrityCheck.Checksum.File);
if (FileChecksum == 0) {
return TRUE;
} else {
return FALSE;
}
} else {
if (FfsHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM) {
return FALSE;
} else {
return TRUE;
}
}
}
/**
Check if it's a valid FFS file header.
@param ErasePolarity Erase polarity attribute of the firmware volume
@param FfsHeader Points to the FFS file header to be checked
@retval TRUE Valid FFS file header
@retval FALSE Invalid FFS file header
**/
BOOLEAN
IsValidFFSHeader (
IN UINT8 ErasePolarity,
IN EFI_FFS_FILE_HEADER *FfsHeader
)
{
EFI_FFS_FILE_STATE FileState;
//
// Check if it is a free space
//
if (IsBufferErased (
ErasePolarity,
(UINT8 *) FfsHeader,
sizeof (EFI_FFS_FILE_HEADER)
)) {
return FALSE;
}
FileState = GetFileState (ErasePolarity, FfsHeader);
switch (FileState) {
case EFI_FILE_HEADER_CONSTRUCTION:
//
// fall through
//
case EFI_FILE_HEADER_INVALID:
return FALSE;
case EFI_FILE_HEADER_VALID:
//
// fall through
//
case EFI_FILE_DATA_VALID:
//
// fall through
//
case EFI_FILE_MARKED_FOR_UPDATE:
//
// fall through
//
case EFI_FILE_DELETED:
//
// Here we need to verify header checksum
//
if (!VerifyHeaderChecksum (FfsHeader)) {
return FALSE;
}
break;
default:
//
// return
//
return FALSE;
}
return TRUE;
}
/**
Get next possible of Firmware File System Header.
@param ErasePolarity Erase polarity attribute of the firmware volume
@param FfsHeader Points to the FFS file header to be skipped.
@return Pointer to next FFS header.
**/
EFI_PHYSICAL_ADDRESS
GetNextPossibleFileHeader (
IN UINT8 ErasePolarity,
IN EFI_FFS_FILE_HEADER *FfsHeader
)
{
UINT32 FileLength;
UINT32 SkipLength;
if (!IsValidFFSHeader (ErasePolarity, FfsHeader)) {
//
// Skip this header
//
if (IS_FFS_FILE2 (FfsHeader)) {
return (EFI_PHYSICAL_ADDRESS) (UINTN) FfsHeader + sizeof (EFI_FFS_FILE_HEADER2);
} else {
return (EFI_PHYSICAL_ADDRESS) (UINTN) FfsHeader + sizeof (EFI_FFS_FILE_HEADER);
}
}
if (IS_FFS_FILE2 (FfsHeader)) {
FileLength = FFS_FILE2_SIZE (FfsHeader);
} else {
FileLength = FFS_FILE_SIZE (FfsHeader);
}
//
// Since FileLength is not multiple of 8, we need skip some bytes
// to get next possible header
//
SkipLength = FileLength;
while ((SkipLength & 0x07) != 0) {
SkipLength++;
}
return (EFI_PHYSICAL_ADDRESS) (UINTN) FfsHeader + SkipLength;
}
/**
Search FFS file with the same FFS name in FV Cache.
@param FvDevice Cached FV image.
@param FfsHeader Points to the FFS file header to be skipped.
@param StateBit FFS file state bit to be checked.
@return Pointer to next found FFS header. NULL will return if no found.
**/
EFI_FFS_FILE_HEADER *
DuplicateFileExist (
IN FV_DEVICE *FvDevice,
IN EFI_FFS_FILE_HEADER *FfsHeader,
IN EFI_FFS_FILE_STATE StateBit
)
{
UINT8 *Ptr;
EFI_FFS_FILE_HEADER *NextFfsFile;
//
// Search duplicate file, not from the beginning of FV,
// just search the next ocurrence of this file
//
NextFfsFile = FfsHeader;
do {
Ptr = (UINT8 *) PHYSICAL_ADDRESS_TO_POINTER (
GetNextPossibleFileHeader (FvDevice->ErasePolarity,
NextFfsFile)
);
NextFfsFile = (EFI_FFS_FILE_HEADER *) Ptr;
if ((UINT8 *) PHYSICAL_ADDRESS_TO_POINTER (FvDevice->CachedFv) + FvDevice->FwVolHeader->FvLength - Ptr <
sizeof (EFI_FFS_FILE_HEADER)
) {
break;
}
if (!IsValidFFSHeader (FvDevice->ErasePolarity, NextFfsFile)) {
continue;
}
if (!VerifyFileChecksum (NextFfsFile)) {
continue;
}
if (CompareGuid (&NextFfsFile->Name, &FfsHeader->Name)) {
if (GetFileState (FvDevice->ErasePolarity, NextFfsFile) == StateBit) {
return NextFfsFile;
}
}
} while (Ptr < (UINT8 *) PHYSICAL_ADDRESS_TO_POINTER (FvDevice->CachedFv) + FvDevice->FwVolHeader->FvLength);
return NULL;
}
/**
Change FFS file header state and write to FV.
@param FvDevice Cached FV image.
@param FfsHeader Points to the FFS file header to be updated.
@param State FFS file state to be set.
@retval EFI_SUCCESS File state is writen into FV.
@retval others File state can't be writen into FV.
**/
EFI_STATUS
UpdateHeaderBit (
IN FV_DEVICE *FvDevice,
IN EFI_FFS_FILE_HEADER *FfsHeader,
IN EFI_FFS_FILE_STATE State
)
{
EFI_STATUS Status;
EFI_LBA Lba;
UINTN Offset;
UINTN NumBytesWritten;
Lba = 0;
Offset = 0;
SetFileState (State, FfsHeader);
Buffer2Lba (
FvDevice,
(EFI_PHYSICAL_ADDRESS) (UINTN) (&FfsHeader->State),
&Lba,
&Offset
);
//
// Write the state byte into FV
//
NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);
Status = FvDevice->Fvb->Write (
FvDevice->Fvb,
Lba,
Offset,
&NumBytesWritten,
&FfsHeader->State
);
return Status;
}
/**
Check if it's a valid FFS file.
Here we are sure that it has a valid FFS file header since we must call IsValidFfsHeader() first.
@param FvDevice Cached FV image.
@param FfsHeader Points to the FFS file to be checked
@retval TRUE Valid FFS file
@retval FALSE Invalid FFS file
**/
BOOLEAN
IsValidFFSFile (
IN FV_DEVICE *FvDevice,
IN EFI_FFS_FILE_HEADER *FfsHeader
)
{
EFI_FFS_FILE_STATE FileState;
UINT8 ErasePolarity;
ErasePolarity = FvDevice->ErasePolarity;
FileState = GetFileState (ErasePolarity, FfsHeader);
switch (FileState) {
case EFI_FILE_DATA_VALID:
if (!VerifyFileChecksum (FfsHeader)) {
return FALSE;
}
if (FfsHeader->Type == EFI_FV_FILETYPE_FFS_PAD) {
break;
}
//
// Check if there is another duplicated file with the EFI_FILE_DATA_VALID
//
if (DuplicateFileExist (FvDevice, FfsHeader, EFI_FILE_DATA_VALID) != NULL) {
return FALSE;
}
break;
case EFI_FILE_MARKED_FOR_UPDATE:
if (!VerifyFileChecksum (FfsHeader)) {
return FALSE;
}
if (FfsHeader->Type == EFI_FV_FILETYPE_FFS_PAD) {
//
// since its data area is not unperturbed, it cannot be reclaimed,
// marked it as deleted
//
UpdateHeaderBit (FvDevice, FfsHeader, EFI_FILE_DELETED);
return TRUE;
} else if (DuplicateFileExist (FvDevice, FfsHeader, EFI_FILE_DATA_VALID) != NULL) {
//
// Here the found file is more recent than this file,
// mark it as deleted
//
UpdateHeaderBit (FvDevice, FfsHeader, EFI_FILE_DELETED);
return TRUE;
} else {
return TRUE;
}
break;
case EFI_FILE_DELETED:
if (!VerifyFileChecksum (FfsHeader)) {
return FALSE;
}
break;
default:
return FALSE;
}
return TRUE;
}