| /** @file | |
| Copyright (c) 2015 - 2021, Intel Corporation. All rights reserved.<BR> | |
| Copyright (c) 2016 - 2018, ARM Limited. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include <Library/FvLib.h> | |
| #include <Library/BaseLib.h> | |
| #include <Library/BaseMemoryLib.h> | |
| #include <Library/DebugLib.h> | |
| #define GET_OCCUPIED_SIZE(ActualSize, Alignment) \ | |
| (ActualSize) + (((Alignment) - ((ActualSize) & ((Alignment) - 1))) & ((Alignment) - 1)) | |
| /** | |
| Returns the highest bit set of the State field | |
| @param ErasePolarity Erase Polarity as defined by EFI_FVB_ERASE_POLARITY | |
| in the Attributes field. | |
| @param FfsHeader Pointer to FFS File Header. | |
| @return the highest bit in the State field | |
| **/ | |
| EFI_FFS_FILE_STATE | |
| GetFileState ( | |
| IN UINT8 ErasePolarity, | |
| IN EFI_FFS_FILE_HEADER *FfsHeader | |
| ) | |
| { | |
| EFI_FFS_FILE_STATE FileState; | |
| EFI_FFS_FILE_STATE HighestBit; | |
| FileState = FfsHeader->State; | |
| if (ErasePolarity != 0) { | |
| FileState = (EFI_FFS_FILE_STATE) ~FileState; | |
| } | |
| HighestBit = 0x80; | |
| while (HighestBit != 0 && (HighestBit & FileState) == 0) { | |
| HighestBit >>= 1; | |
| } | |
| return HighestBit; | |
| } | |
| /** | |
| Calculates the checksum of the header of a file. | |
| @param FileHeader Pointer to FFS File Header. | |
| @return Checksum of the header. | |
| **/ | |
| UINT8 | |
| CalculateHeaderChecksum ( | |
| IN EFI_FFS_FILE_HEADER *FileHeader | |
| ) | |
| { | |
| UINT8 *ptr; | |
| UINTN Index; | |
| UINT8 Sum; | |
| UINTN Size; | |
| Sum = 0; | |
| ptr = (UINT8 *)FileHeader; | |
| Size = IS_FFS_FILE2 (FileHeader) ? sizeof (EFI_FFS_FILE_HEADER2) : sizeof (EFI_FFS_FILE_HEADER); | |
| for (Index = 0; Index < Size - 3; Index += 4) { | |
| Sum = (UINT8)(Sum + ptr[Index]); | |
| Sum = (UINT8)(Sum + ptr[Index + 1]); | |
| Sum = (UINT8)(Sum + ptr[Index + 2]); | |
| Sum = (UINT8)(Sum + ptr[Index + 3]); | |
| } | |
| for ( ; Index < Size; Index++) { | |
| Sum = (UINT8)(Sum + ptr[Index]); | |
| } | |
| // | |
| // State field (since this indicates the different state of file). | |
| // | |
| Sum = (UINT8)(Sum - FileHeader->State); | |
| // | |
| // Checksum field of the file is not part of the header checksum. | |
| // | |
| Sum = (UINT8)(Sum - FileHeader->IntegrityCheck.Checksum.File); | |
| return Sum; | |
| } | |
| /** | |
| Given the input file pointer, search for the next matching file in the | |
| FFS volume as defined by SearchType. The search starts from FileHeader inside | |
| the Firmware Volume defined by FwVolHeader. | |
| @param SearchType Filter to find only files of this type. | |
| Type EFI_FV_FILETYPE_ALL causes no filtering to be done. | |
| @param FwVolHeader Pointer to the FV header of the volume to search. | |
| This parameter must point to a valid FFS volume. | |
| @param FileHeader Pointer to the current file from which to begin searching. | |
| This pointer will be updated upon return to reflect the file found. | |
| @retval EFI_NOT_FOUND No files matching the search criteria were found | |
| @retval EFI_SUCCESS | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| FfsFindNextFile ( | |
| IN EFI_FV_FILETYPE SearchType, | |
| IN EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader, | |
| IN OUT EFI_FFS_FILE_HEADER **FileHeader | |
| ) | |
| { | |
| EFI_FIRMWARE_VOLUME_EXT_HEADER *FvExtHeader; | |
| EFI_FFS_FILE_HEADER *FfsFileHeader; | |
| UINT32 FileLength; | |
| UINT32 FileOccupiedSize; | |
| UINT32 FileOffset; | |
| UINT64 FvLength; | |
| UINT8 ErasePolarity; | |
| UINT8 FileState; | |
| FvLength = FwVolHeader->FvLength; | |
| if (FwVolHeader->Attributes & EFI_FVB2_ERASE_POLARITY) { | |
| ErasePolarity = 1; | |
| } else { | |
| ErasePolarity = 0; | |
| } | |
| // | |
| // If FileHeader is not specified (NULL) start with the first file in the | |
| // firmware volume. Otherwise, start from the FileHeader. | |
| // | |
| if (*FileHeader == NULL) { | |
| if (FwVolHeader->ExtHeaderOffset != 0) { | |
| FvExtHeader = (EFI_FIRMWARE_VOLUME_EXT_HEADER *)((UINT8 *)FwVolHeader + | |
| FwVolHeader->ExtHeaderOffset); | |
| FfsFileHeader = (EFI_FFS_FILE_HEADER *)((UINT8 *)FvExtHeader + | |
| FvExtHeader->ExtHeaderSize); | |
| } else { | |
| FfsFileHeader = (EFI_FFS_FILE_HEADER *)((UINT8 *)FwVolHeader + | |
| FwVolHeader->HeaderLength); | |
| } | |
| FfsFileHeader = (EFI_FFS_FILE_HEADER *)((UINTN)FwVolHeader + | |
| ALIGN_VALUE ( | |
| (UINTN)FfsFileHeader - | |
| (UINTN)FwVolHeader, | |
| 8 | |
| )); | |
| } else { | |
| // | |
| // Length is 24 bits wide so mask upper 8 bits | |
| // FileLength is adjusted to FileOccupiedSize as it is 8 byte aligned. | |
| // | |
| FileLength = IS_FFS_FILE2 (*FileHeader) ? | |
| FFS_FILE2_SIZE (*FileHeader) : FFS_FILE_SIZE (*FileHeader); | |
| FileOccupiedSize = GET_OCCUPIED_SIZE (FileLength, 8); | |
| FfsFileHeader = (EFI_FFS_FILE_HEADER *)((UINT8 *)*FileHeader + FileOccupiedSize); | |
| } | |
| FileOffset = (UINT32)((UINT8 *)FfsFileHeader - (UINT8 *)FwVolHeader); | |
| while (FileOffset < (FvLength - sizeof (EFI_FFS_FILE_HEADER))) { | |
| // | |
| // Get FileState which is the highest bit of the State | |
| // | |
| FileState = GetFileState (ErasePolarity, FfsFileHeader); | |
| switch (FileState) { | |
| case EFI_FILE_HEADER_INVALID: | |
| if (IS_FFS_FILE2 (FfsFileHeader)) { | |
| FileOffset += sizeof (EFI_FFS_FILE_HEADER2); | |
| FfsFileHeader = (EFI_FFS_FILE_HEADER *)((UINT8 *)FfsFileHeader + sizeof (EFI_FFS_FILE_HEADER2)); | |
| } else { | |
| FileOffset += sizeof (EFI_FFS_FILE_HEADER); | |
| FfsFileHeader = (EFI_FFS_FILE_HEADER *)((UINT8 *)FfsFileHeader + sizeof (EFI_FFS_FILE_HEADER)); | |
| } | |
| break; | |
| case EFI_FILE_DATA_VALID: | |
| case EFI_FILE_MARKED_FOR_UPDATE: | |
| if (CalculateHeaderChecksum (FfsFileHeader) == 0) { | |
| FileLength = IS_FFS_FILE2 (FfsFileHeader) ? | |
| FFS_FILE2_SIZE (FfsFileHeader) : FFS_FILE_SIZE (FfsFileHeader); | |
| FileOccupiedSize = GET_OCCUPIED_SIZE (FileLength, 8); | |
| if ((SearchType == FfsFileHeader->Type) || (SearchType == EFI_FV_FILETYPE_ALL)) { | |
| *FileHeader = FfsFileHeader; | |
| return EFI_SUCCESS; | |
| } | |
| FileOffset += FileOccupiedSize; | |
| FfsFileHeader = (EFI_FFS_FILE_HEADER *)((UINT8 *)FfsFileHeader + FileOccupiedSize); | |
| } else { | |
| return EFI_NOT_FOUND; | |
| } | |
| break; | |
| case EFI_FILE_DELETED: | |
| FileLength = IS_FFS_FILE2 (FfsFileHeader) ? | |
| FFS_FILE2_SIZE (FfsFileHeader) : FFS_FILE_SIZE (FfsFileHeader); | |
| FileOccupiedSize = GET_OCCUPIED_SIZE (FileLength, 8); | |
| FileOffset += FileOccupiedSize; | |
| FfsFileHeader = (EFI_FFS_FILE_HEADER *)((UINT8 *)FfsFileHeader + FileOccupiedSize); | |
| break; | |
| default: | |
| return EFI_NOT_FOUND; | |
| } | |
| } | |
| return EFI_NOT_FOUND; | |
| } | |
| /** | |
| Locates a section within a series of sections | |
| with the specified section type. | |
| @param[in] Sections The sections to search | |
| @param[in] SizeOfSections Total size of all sections | |
| @param[in] SectionType The section type to locate | |
| @param[out] FoundSection The FFS section if found | |
| @retval EFI_SUCCESS The file and section was found | |
| @retval EFI_NOT_FOUND The file and section was not found | |
| @retval EFI_VOLUME_CORRUPTED The firmware volume was corrupted | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| FindFfsSectionInSections ( | |
| IN VOID *Sections, | |
| IN UINTN SizeOfSections, | |
| IN EFI_SECTION_TYPE SectionType, | |
| OUT EFI_COMMON_SECTION_HEADER **FoundSection | |
| ) | |
| { | |
| EFI_PHYSICAL_ADDRESS CurrentAddress; | |
| UINT32 Size; | |
| EFI_PHYSICAL_ADDRESS EndOfSections; | |
| EFI_COMMON_SECTION_HEADER *Section; | |
| EFI_PHYSICAL_ADDRESS EndOfSection; | |
| // | |
| // Loop through the FFS file sections | |
| // | |
| EndOfSection = (EFI_PHYSICAL_ADDRESS)(UINTN)Sections; | |
| EndOfSections = EndOfSection + SizeOfSections; | |
| for ( ; ;) { | |
| if (EndOfSection == EndOfSections) { | |
| break; | |
| } | |
| CurrentAddress = EndOfSection; | |
| Section = (EFI_COMMON_SECTION_HEADER *)(UINTN)CurrentAddress; | |
| Size = IS_SECTION2 (Section) ? SECTION2_SIZE (Section) : SECTION_SIZE (Section); | |
| if (Size < sizeof (*Section)) { | |
| return EFI_VOLUME_CORRUPTED; | |
| } | |
| EndOfSection = CurrentAddress + Size; | |
| if (EndOfSection > EndOfSections) { | |
| return EFI_VOLUME_CORRUPTED; | |
| } | |
| Size = GET_OCCUPIED_SIZE (Size, 4); | |
| // | |
| // Look for the requested section type | |
| // | |
| if (Section->Type == SectionType) { | |
| *FoundSection = Section; | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| return EFI_NOT_FOUND; | |
| } | |
| /** | |
| Given the input file pointer, search for the next matching section in the | |
| FFS volume. | |
| @param SearchType Filter to find only sections of this type. | |
| @param FfsFileHeader Pointer to the current file to search. | |
| @param SectionHeader Pointer to the Section matching SectionType in FfsFileHeader. | |
| NULL if section not found | |
| @retval EFI_NOT_FOUND No files matching the search criteria were found | |
| @retval EFI_SUCCESS | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| FfsFindSection ( | |
| IN EFI_SECTION_TYPE SectionType, | |
| IN EFI_FFS_FILE_HEADER *FfsFileHeader, | |
| IN OUT EFI_COMMON_SECTION_HEADER **SectionHeader | |
| ) | |
| { | |
| UINT32 FileSize; | |
| EFI_COMMON_SECTION_HEADER *Section; | |
| EFI_STATUS Status; | |
| // | |
| // Size is 24 bits wide so mask upper 8 bits. | |
| // Does not include FfsFileHeader header size | |
| // FileSize is adjusted to FileOccupiedSize as it is 8 byte aligned. | |
| // | |
| if (IS_FFS_FILE2 (FfsFileHeader)) { | |
| Section = (EFI_COMMON_SECTION_HEADER *)((EFI_FFS_FILE_HEADER2 *)FfsFileHeader + 1); | |
| FileSize = FFS_FILE2_SIZE (FfsFileHeader) - sizeof (EFI_FFS_FILE_HEADER2); | |
| } else { | |
| Section = (EFI_COMMON_SECTION_HEADER *)(FfsFileHeader + 1); | |
| FileSize = FFS_FILE_SIZE (FfsFileHeader) - sizeof (EFI_FFS_FILE_HEADER); | |
| } | |
| Status = FindFfsSectionInSections ( | |
| Section, | |
| FileSize, | |
| SectionType, | |
| SectionHeader | |
| ); | |
| return Status; | |
| } | |
| /** | |
| Given the input file pointer, search for the next matching section in the | |
| FFS volume. | |
| @param SearchType Filter to find only sections of this type. | |
| @param FfsFileHeader Pointer to the current file to search. | |
| @param SectionData Pointer to the Section matching SectionType in FfsFileHeader. | |
| NULL if section not found | |
| @param SectionDataSize The size of SectionData | |
| @retval EFI_NOT_FOUND No files matching the search criteria were found | |
| @retval EFI_SUCCESS | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| FfsFindSectionData ( | |
| IN EFI_SECTION_TYPE SectionType, | |
| IN EFI_FFS_FILE_HEADER *FfsFileHeader, | |
| IN OUT VOID **SectionData, | |
| IN OUT UINTN *SectionDataSize | |
| ) | |
| { | |
| UINT32 FileSize; | |
| EFI_COMMON_SECTION_HEADER *Section; | |
| UINT32 SectionLength; | |
| UINT32 ParsedLength; | |
| // | |
| // Size is 24 bits wide so mask upper 8 bits. | |
| // Does not include FfsFileHeader header size | |
| // FileSize is adjusted to FileOccupiedSize as it is 8 byte aligned. | |
| // | |
| if (IS_FFS_FILE2 (FfsFileHeader)) { | |
| Section = (EFI_COMMON_SECTION_HEADER *)((EFI_FFS_FILE_HEADER2 *)FfsFileHeader + 1); | |
| FileSize = FFS_FILE2_SIZE (FfsFileHeader) - sizeof (EFI_FFS_FILE_HEADER2); | |
| } else { | |
| Section = (EFI_COMMON_SECTION_HEADER *)(FfsFileHeader + 1); | |
| FileSize = FFS_FILE_SIZE (FfsFileHeader) - sizeof (EFI_FFS_FILE_HEADER); | |
| } | |
| *SectionData = NULL; | |
| ParsedLength = 0; | |
| while (ParsedLength < FileSize) { | |
| if (Section->Type == SectionType) { | |
| if (IS_SECTION2 (Section)) { | |
| *SectionData = (VOID *)((EFI_COMMON_SECTION_HEADER2 *)Section + 1); | |
| *SectionDataSize = SECTION2_SIZE (Section); | |
| } else { | |
| *SectionData = (VOID *)(Section + 1); | |
| *SectionDataSize = SECTION_SIZE (Section); | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| // | |
| // Size is 24 bits wide so mask upper 8 bits. | |
| // SectionLength is adjusted it is 4 byte aligned. | |
| // Go to the next section | |
| // | |
| SectionLength = IS_SECTION2 (Section) ? SECTION2_SIZE (Section) : SECTION_SIZE (Section); | |
| SectionLength = GET_OCCUPIED_SIZE (SectionLength, 4); | |
| ParsedLength += SectionLength; | |
| Section = (EFI_COMMON_SECTION_HEADER *)((UINT8 *)Section + SectionLength); | |
| } | |
| return EFI_NOT_FOUND; | |
| } |