REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3769 Current FvLib will hit parse issue when encountering LARGE file, then ignore latter ffs/section, thus causing required drivers not being dispatched. Therefore, need to add support for EFI_FFS_FILE_HEADER2 and EFI_COMMON_SECTION_HEADER2 in FvLib to fix this issue. Signed-off-by: Wei6 Xu <wei6.xu@intel.com> Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn>
		
			
				
	
	
		
			406 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			406 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @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;
 | 
						|
}
 |