summaryrefslogtreecommitdiff
path: root/IntelFrameworkModulePkg/Universal/FirmwareVolume/FwVolDxe/Ffs.c
diff options
context:
space:
mode:
Diffstat (limited to 'IntelFrameworkModulePkg/Universal/FirmwareVolume/FwVolDxe/Ffs.c')
-rw-r--r--IntelFrameworkModulePkg/Universal/FirmwareVolume/FwVolDxe/Ffs.c625
1 files changed, 625 insertions, 0 deletions
diff --git a/IntelFrameworkModulePkg/Universal/FirmwareVolume/FwVolDxe/Ffs.c b/IntelFrameworkModulePkg/Universal/FirmwareVolume/FwVolDxe/Ffs.c
new file mode 100644
index 0000000000..1e3c839e27
--- /dev/null
+++ b/IntelFrameworkModulePkg/Universal/FirmwareVolume/FwVolDxe/Ffs.c
@@ -0,0 +1,625 @@
+/** @file
+ FFS file access utilities.
+
+ Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions
+ of the BSD License which accompanies this distribution. The
+ full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#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;
+
+ 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;
+ UINT32 FileSize;
+
+ Attributes = FfsHeader->Attributes;
+
+ if ((Attributes & FFS_ATTRIB_CHECKSUM) != 0) {
+
+ FileSize = *(UINT32 *) FfsHeader->Size & 0x00FFFFFF;
+
+ //
+ // Check checksum of FFS data
+ //
+ FileChecksum = CalculateSum8 ((UINT8 *) FfsHeader + sizeof (EFI_FFS_FILE_HEADER), FileSize - 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
+ //
+ return (EFI_PHYSICAL_ADDRESS) (UINTN) FfsHeader + sizeof (EFI_FFS_FILE_HEADER);
+ }
+
+ FileLength = *(UINT32 *) FfsHeader->Size & 0x00FFFFFF;
+
+ //
+ // 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;
+}
+
+/**
+ Locate the first file in FV.
+
+ @param FvDevice Cached FV image.
+ @param FirstFile Points to the got first FFS file header.
+
+ @retval EFI_NOT_FOUND No FFS file is found in FV.
+ @retval EFI_SUCCESS The first FFS file is got.
+
+**/
+EFI_STATUS
+FvLocateFirstFile (
+ IN FV_DEVICE *FvDevice,
+ OUT EFI_FFS_FILE_HEADER **FirstFile
+ )
+{
+ FFS_FILE_LIST_ENTRY *TmpFileList;
+ LIST_ENTRY *Link;
+
+ Link = FvDevice->FfsFileListHeader.ForwardLink;
+
+ if (Link == &FvDevice->FfsFileListHeader) {
+ return EFI_NOT_FOUND;
+ }
+
+ TmpFileList = (FFS_FILE_LIST_ENTRY *) Link;
+ *FirstFile = (EFI_FFS_FILE_HEADER *) TmpFileList->FfsHeader;
+
+ return EFI_SUCCESS;
+}