/** @file FFS file access utilities. Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
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; }