The traversing of a Memory Mapped FV can overflow the 4GB limit on a 32bit system during the setting up a Linked List of FFS file inside the FV. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Star Zeng <star.zeng@intel.com> Reviewed-by: Liming Gao <liming.gao@intel.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16527 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			776 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			776 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  Firmware File System driver that produce Firmware Volume protocol.
 | 
						|
  Layers on top of Firmware Block protocol to produce a file abstraction
 | 
						|
  of FV based files.
 | 
						|
 | 
						|
Copyright (c) 2006 - 2014, 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 "DxeMain.h"
 | 
						|
#include "FwVolDriver.h"
 | 
						|
 | 
						|
 | 
						|
//
 | 
						|
// Protocol notify related globals
 | 
						|
//
 | 
						|
VOID          *gEfiFwVolBlockNotifyReg;
 | 
						|
EFI_EVENT     gEfiFwVolBlockEvent;
 | 
						|
 | 
						|
FV_DEVICE mFvDevice = {
 | 
						|
  FV2_DEVICE_SIGNATURE,
 | 
						|
  NULL,
 | 
						|
  NULL,
 | 
						|
  {
 | 
						|
    FvGetVolumeAttributes,
 | 
						|
    FvSetVolumeAttributes,
 | 
						|
    FvReadFile,
 | 
						|
    FvReadFileSection,
 | 
						|
    FvWriteFile,
 | 
						|
    FvGetNextFile,   
 | 
						|
	sizeof (UINTN),
 | 
						|
    NULL,
 | 
						|
    FvGetVolumeInfo,
 | 
						|
    FvSetVolumeInfo
 | 
						|
  },
 | 
						|
  NULL,
 | 
						|
  NULL,
 | 
						|
  NULL,
 | 
						|
  NULL,
 | 
						|
  { NULL, NULL },
 | 
						|
  0,
 | 
						|
  0,
 | 
						|
  FALSE,
 | 
						|
  FALSE
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
//
 | 
						|
// FFS helper functions
 | 
						|
//
 | 
						|
/**
 | 
						|
  Read data from Firmware Block by FVB protocol Read. 
 | 
						|
  The data may cross the multi block ranges.
 | 
						|
 | 
						|
  @param  Fvb                   The FW_VOL_BLOCK_PROTOCOL instance from which to read data.
 | 
						|
  @param  StartLba              Pointer to StartLba.
 | 
						|
                                On input, the start logical block index from which to read.
 | 
						|
                                On output,the end logical block index after reading.
 | 
						|
  @param  Offset                Pointer to Offset
 | 
						|
                                On input, offset into the block at which to begin reading.
 | 
						|
                                On output, offset into the end block after reading.
 | 
						|
  @param  DataSize              Size of data to be read.
 | 
						|
  @param  Data                  Pointer to Buffer that the data will be read into.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           Successfully read data from firmware block.
 | 
						|
  @retval others
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
ReadFvbData (
 | 
						|
  IN     EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL     *Fvb,
 | 
						|
  IN OUT EFI_LBA                                *StartLba,
 | 
						|
  IN OUT UINTN                                  *Offset,
 | 
						|
  IN     UINTN                                  DataSize,
 | 
						|
  OUT    UINT8                                  *Data
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN                       BlockSize;
 | 
						|
  UINTN                       NumberOfBlocks;
 | 
						|
  UINTN                       BlockIndex;
 | 
						|
  UINTN                       ReadDataSize;
 | 
						|
  EFI_STATUS                  Status;
 | 
						|
  
 | 
						|
  //
 | 
						|
  // Try read data in current block
 | 
						|
  //
 | 
						|
  BlockIndex   = 0;
 | 
						|
  ReadDataSize = DataSize;
 | 
						|
  Status = Fvb->Read (Fvb, *StartLba, *Offset, &ReadDataSize, Data);
 | 
						|
  if (Status == EFI_SUCCESS) {
 | 
						|
    *Offset  += DataSize;
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  } else if (Status != EFI_BAD_BUFFER_SIZE) {
 | 
						|
    //
 | 
						|
    // other error will direct return
 | 
						|
    //
 | 
						|
    return Status;
 | 
						|
  }
 | 
						|
  
 | 
						|
  //
 | 
						|
  // Data crosses the blocks, read data from next block
 | 
						|
  //
 | 
						|
  DataSize -= ReadDataSize;
 | 
						|
  Data     += ReadDataSize;
 | 
						|
  *StartLba = *StartLba + 1;
 | 
						|
  while (DataSize > 0) {
 | 
						|
    Status = Fvb->GetBlockSize (Fvb, *StartLba, &BlockSize, &NumberOfBlocks);
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      return Status;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Read data from the crossing blocks
 | 
						|
    //
 | 
						|
    BlockIndex = 0; 
 | 
						|
    while (BlockIndex < NumberOfBlocks && DataSize >= BlockSize) {
 | 
						|
      Status = Fvb->Read (Fvb, *StartLba + BlockIndex, 0, &BlockSize, Data);
 | 
						|
      if (EFI_ERROR (Status)) {
 | 
						|
        return Status;
 | 
						|
      }
 | 
						|
      Data += BlockSize;
 | 
						|
      DataSize -= BlockSize;
 | 
						|
      BlockIndex ++;
 | 
						|
    }
 | 
						|
    
 | 
						|
    //
 | 
						|
    // Data doesn't exceed the current block range.
 | 
						|
    //
 | 
						|
    if (DataSize < BlockSize) {
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    
 | 
						|
    //
 | 
						|
    // Data must be got from the next block range.
 | 
						|
    //
 | 
						|
    *StartLba += NumberOfBlocks;
 | 
						|
  }
 | 
						|
  
 | 
						|
  //
 | 
						|
  // read the remaining data
 | 
						|
  //
 | 
						|
  if (DataSize > 0) {
 | 
						|
    Status = Fvb->Read (Fvb, *StartLba + BlockIndex, 0, &DataSize, Data);
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      return Status;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  
 | 
						|
  //
 | 
						|
  // Update Lba and Offset used by the following read.
 | 
						|
  //
 | 
						|
  *StartLba += BlockIndex;
 | 
						|
  *Offset   = DataSize;
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Given the supplied FW_VOL_BLOCK_PROTOCOL, allocate a buffer for output and
 | 
						|
  copy the real length volume header into it.
 | 
						|
 | 
						|
  @param  Fvb                   The FW_VOL_BLOCK_PROTOCOL instance from which to
 | 
						|
                                read the volume header
 | 
						|
  @param  FwVolHeader           Pointer to pointer to allocated buffer in which
 | 
						|
                                the volume header is returned.
 | 
						|
 | 
						|
  @retval EFI_OUT_OF_RESOURCES  No enough buffer could be allocated.
 | 
						|
  @retval EFI_SUCCESS           Successfully read volume header to the allocated
 | 
						|
                                buffer.
 | 
						|
  @retval EFI_INVALID_PARAMETER The FV Header signature is not as expected or
 | 
						|
                                the file system could not be understood.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
GetFwVolHeader (
 | 
						|
  IN     EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL     *Fvb,
 | 
						|
  OUT    EFI_FIRMWARE_VOLUME_HEADER             **FwVolHeader
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                  Status;
 | 
						|
  EFI_FIRMWARE_VOLUME_HEADER  TempFvh;
 | 
						|
  UINTN                       FvhLength;
 | 
						|
  EFI_LBA                     StartLba;
 | 
						|
  UINTN                       Offset;
 | 
						|
  UINT8                       *Buffer;
 | 
						|
  
 | 
						|
  //
 | 
						|
  // Read the standard FV header
 | 
						|
  //
 | 
						|
  StartLba = 0;
 | 
						|
  Offset   = 0;
 | 
						|
  FvhLength = sizeof (EFI_FIRMWARE_VOLUME_HEADER);
 | 
						|
  Status = ReadFvbData (Fvb, &StartLba, &Offset, FvhLength, (UINT8 *)&TempFvh);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return Status;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Validate FV Header signature, if not as expected, continue.
 | 
						|
  //
 | 
						|
  if (TempFvh.Signature != EFI_FVH_SIGNATURE) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Check to see that the file system is indeed formatted in a way we can
 | 
						|
  // understand it...
 | 
						|
  //
 | 
						|
  if ((!CompareGuid (&TempFvh.FileSystemGuid, &gEfiFirmwareFileSystem2Guid)) &&
 | 
						|
      (!CompareGuid (&TempFvh.FileSystemGuid, &gEfiFirmwareFileSystem3Guid))) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Allocate a buffer for the caller
 | 
						|
  //
 | 
						|
  *FwVolHeader = AllocatePool (TempFvh.HeaderLength);
 | 
						|
  if (*FwVolHeader == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Copy the standard header into the buffer
 | 
						|
  //
 | 
						|
  CopyMem (*FwVolHeader, &TempFvh, sizeof (EFI_FIRMWARE_VOLUME_HEADER));
 | 
						|
 | 
						|
  //
 | 
						|
  // Read the rest of the header
 | 
						|
  //
 | 
						|
  FvhLength = TempFvh.HeaderLength - sizeof (EFI_FIRMWARE_VOLUME_HEADER);
 | 
						|
  Buffer = (UINT8 *)*FwVolHeader + sizeof (EFI_FIRMWARE_VOLUME_HEADER);
 | 
						|
  Status = ReadFvbData (Fvb, &StartLba, &Offset, FvhLength, Buffer);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    //
 | 
						|
    // Read failed so free buffer
 | 
						|
    //
 | 
						|
    CoreFreePool (*FwVolHeader);
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Free FvDevice resource when error happens
 | 
						|
 | 
						|
  @param  FvDevice              pointer to the FvDevice to be freed.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
FreeFvDeviceResource (
 | 
						|
  IN FV_DEVICE  *FvDevice
 | 
						|
  )
 | 
						|
{
 | 
						|
  FFS_FILE_LIST_ENTRY         *FfsFileEntry;
 | 
						|
  LIST_ENTRY                  *NextEntry;
 | 
						|
 | 
						|
  //
 | 
						|
  // Free File List Entry
 | 
						|
  //
 | 
						|
  FfsFileEntry = (FFS_FILE_LIST_ENTRY *)FvDevice->FfsFileListHeader.ForwardLink;
 | 
						|
  while (&FfsFileEntry->Link != &FvDevice->FfsFileListHeader) {
 | 
						|
    NextEntry = (&FfsFileEntry->Link)->ForwardLink;
 | 
						|
 | 
						|
    if (FfsFileEntry->StreamHandle != 0) {
 | 
						|
      //
 | 
						|
      // Close stream and free resources from SEP
 | 
						|
      //
 | 
						|
      CloseSectionStream (FfsFileEntry->StreamHandle, FALSE);
 | 
						|
    }
 | 
						|
 | 
						|
    if (FfsFileEntry->FileCached) {
 | 
						|
      //
 | 
						|
      // Free the cached file buffer.
 | 
						|
      //
 | 
						|
      CoreFreePool (FfsFileEntry->FfsHeader);
 | 
						|
    }
 | 
						|
 | 
						|
    CoreFreePool (FfsFileEntry);
 | 
						|
 | 
						|
    FfsFileEntry = (FFS_FILE_LIST_ENTRY *) NextEntry;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!FvDevice->IsMemoryMapped) {
 | 
						|
    //
 | 
						|
    // Free the cached FV buffer.
 | 
						|
    //
 | 
						|
    CoreFreePool (FvDevice->CachedFv);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Free Volume Header
 | 
						|
  //
 | 
						|
  CoreFreePool (FvDevice->FwVolHeader);
 | 
						|
 | 
						|
  return;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Check if an FV is consistent and allocate cache for it.
 | 
						|
 | 
						|
  @param  FvDevice              A pointer to the FvDevice to be checked.
 | 
						|
 | 
						|
  @retval EFI_OUT_OF_RESOURCES  No enough buffer could be allocated.
 | 
						|
  @retval EFI_SUCCESS           FV is consistent and cache is allocated.
 | 
						|
  @retval EFI_VOLUME_CORRUPTED  File system is corrupted.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
FvCheck (
 | 
						|
  IN OUT FV_DEVICE  *FvDevice
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                            Status;
 | 
						|
  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL    *Fvb;
 | 
						|
  EFI_FIRMWARE_VOLUME_HEADER            *FwVolHeader;
 | 
						|
  EFI_FIRMWARE_VOLUME_EXT_HEADER        *FwVolExtHeader;
 | 
						|
  EFI_FVB_ATTRIBUTES_2                  FvbAttributes;
 | 
						|
  EFI_FV_BLOCK_MAP_ENTRY                *BlockMap;
 | 
						|
  FFS_FILE_LIST_ENTRY                   *FfsFileEntry;
 | 
						|
  EFI_FFS_FILE_HEADER                   *FfsHeader;
 | 
						|
  UINT8                                 *CacheLocation;
 | 
						|
  UINTN                                 LbaOffset;
 | 
						|
  UINTN                                 HeaderSize;
 | 
						|
  UINTN                                 Index;
 | 
						|
  EFI_LBA                               LbaIndex;
 | 
						|
  UINTN                                 Size;
 | 
						|
  EFI_FFS_FILE_STATE                    FileState;
 | 
						|
  UINT8                                 *TopFvAddress;
 | 
						|
  UINTN                                 TestLength;
 | 
						|
  EFI_PHYSICAL_ADDRESS                  PhysicalAddress;
 | 
						|
  BOOLEAN                               FileCached;
 | 
						|
  UINTN                                 WholeFileSize;
 | 
						|
  EFI_FFS_FILE_HEADER                   *CacheFfsHeader;
 | 
						|
 | 
						|
  FileCached = FALSE;
 | 
						|
  CacheFfsHeader = NULL;
 | 
						|
 | 
						|
  Fvb = FvDevice->Fvb;
 | 
						|
  FwVolHeader = FvDevice->FwVolHeader;
 | 
						|
 | 
						|
  Status = Fvb->GetAttributes (Fvb, &FvbAttributes);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return Status;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Size is the size of the FV minus the head. We have already allocated
 | 
						|
  // the header to check to make sure the volume is valid
 | 
						|
  //
 | 
						|
  Size = (UINTN)(FwVolHeader->FvLength - FwVolHeader->HeaderLength);
 | 
						|
  if ((FvbAttributes & EFI_FVB2_MEMORY_MAPPED) != 0) {
 | 
						|
    FvDevice->IsMemoryMapped = TRUE;
 | 
						|
 | 
						|
    Status = Fvb->GetPhysicalAddress (Fvb, &PhysicalAddress);
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      return Status;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Don't cache memory mapped FV really.
 | 
						|
    //
 | 
						|
    FvDevice->CachedFv = (UINT8 *) (UINTN) (PhysicalAddress + FwVolHeader->HeaderLength);
 | 
						|
  } else {
 | 
						|
    FvDevice->IsMemoryMapped = FALSE;
 | 
						|
    FvDevice->CachedFv = AllocatePool (Size);
 | 
						|
 | 
						|
    if (FvDevice->CachedFv == NULL) {
 | 
						|
      return EFI_OUT_OF_RESOURCES;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Remember a pointer to the end fo the CachedFv
 | 
						|
  //
 | 
						|
  FvDevice->EndOfCachedFv = FvDevice->CachedFv + Size;
 | 
						|
 | 
						|
  if (!FvDevice->IsMemoryMapped) {
 | 
						|
    //
 | 
						|
    // Copy FV minus header into memory using the block map we have all ready
 | 
						|
    // read into memory.
 | 
						|
    //
 | 
						|
    BlockMap = FwVolHeader->BlockMap;
 | 
						|
    CacheLocation = FvDevice->CachedFv;
 | 
						|
    LbaIndex = 0;
 | 
						|
    LbaOffset = 0;
 | 
						|
    HeaderSize = FwVolHeader->HeaderLength;
 | 
						|
    while ((BlockMap->NumBlocks != 0) || (BlockMap->Length != 0)) {
 | 
						|
      Index = 0;
 | 
						|
      Size  = BlockMap->Length;
 | 
						|
      if (HeaderSize > 0) {
 | 
						|
        //
 | 
						|
        // Skip header size
 | 
						|
        //
 | 
						|
        for (; Index < BlockMap->NumBlocks && HeaderSize >= BlockMap->Length; Index ++) {
 | 
						|
          HeaderSize -= BlockMap->Length;
 | 
						|
          LbaIndex ++;
 | 
						|
        }
 | 
						|
 | 
						|
        //
 | 
						|
        // Check whether FvHeader is crossing the multi block range.
 | 
						|
        //
 | 
						|
        if (Index >= BlockMap->NumBlocks) {
 | 
						|
          BlockMap++;
 | 
						|
          continue;
 | 
						|
        } else if (HeaderSize > 0) {
 | 
						|
          LbaOffset = HeaderSize;
 | 
						|
          Size = BlockMap->Length - HeaderSize;
 | 
						|
          HeaderSize = 0;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    
 | 
						|
      //
 | 
						|
      // read the FV data  
 | 
						|
      //
 | 
						|
      for (; Index < BlockMap->NumBlocks; Index ++) {
 | 
						|
        Status = Fvb->Read (Fvb,
 | 
						|
                        LbaIndex,
 | 
						|
                        LbaOffset,
 | 
						|
                        &Size,
 | 
						|
                        CacheLocation
 | 
						|
                        );
 | 
						|
 | 
						|
        //
 | 
						|
        // Not check EFI_BAD_BUFFER_SIZE, for Size = BlockMap->Length
 | 
						|
        //
 | 
						|
        if (EFI_ERROR (Status)) {
 | 
						|
          goto Done;
 | 
						|
        }
 | 
						|
 | 
						|
        LbaIndex++;
 | 
						|
        CacheLocation += Size;
 | 
						|
 | 
						|
        //
 | 
						|
        // After we skip Fv Header always read from start of block
 | 
						|
        //
 | 
						|
        LbaOffset = 0;
 | 
						|
        Size  = BlockMap->Length;
 | 
						|
      }
 | 
						|
 | 
						|
      BlockMap++;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Scan to check the free space & File list
 | 
						|
  //
 | 
						|
  if ((FvbAttributes & EFI_FVB2_ERASE_POLARITY) != 0) {
 | 
						|
    FvDevice->ErasePolarity = 1;
 | 
						|
  } else {
 | 
						|
    FvDevice->ErasePolarity = 0;
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  //
 | 
						|
  // go through the whole FV cache, check the consistence of the FV.
 | 
						|
  // Make a linked list of all the Ffs file headers
 | 
						|
  //
 | 
						|
  Status = EFI_SUCCESS;
 | 
						|
  InitializeListHead (&FvDevice->FfsFileListHeader);
 | 
						|
 | 
						|
  //
 | 
						|
  // Build FFS list
 | 
						|
  //
 | 
						|
  if (FwVolHeader->ExtHeaderOffset != 0) {
 | 
						|
    //
 | 
						|
    // Searching for files starts on an 8 byte aligned boundary after the end of the Extended Header if it exists.
 | 
						|
    //
 | 
						|
    FwVolExtHeader = (EFI_FIRMWARE_VOLUME_EXT_HEADER *) (FvDevice->CachedFv + (FwVolHeader->ExtHeaderOffset - FwVolHeader->HeaderLength));
 | 
						|
    FfsHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FwVolExtHeader + FwVolExtHeader->ExtHeaderSize);
 | 
						|
    FfsHeader = (EFI_FFS_FILE_HEADER *) ALIGN_POINTER (FfsHeader, 8);
 | 
						|
  } else {
 | 
						|
    FfsHeader = (EFI_FFS_FILE_HEADER *) (FvDevice->CachedFv);
 | 
						|
  }
 | 
						|
  TopFvAddress = FvDevice->EndOfCachedFv;
 | 
						|
  while (((UINTN) FfsHeader >= (UINTN) FvDevice->CachedFv) && ((UINTN) FfsHeader <= (UINTN) ((UINTN) TopFvAddress - sizeof (EFI_FFS_FILE_HEADER)))) {
 | 
						|
 | 
						|
    if (FileCached) {
 | 
						|
      CoreFreePool (CacheFfsHeader);
 | 
						|
      FileCached = FALSE;
 | 
						|
    }
 | 
						|
 | 
						|
    TestLength = TopFvAddress - ((UINT8 *) FfsHeader);
 | 
						|
    if (TestLength > sizeof (EFI_FFS_FILE_HEADER)) {
 | 
						|
      TestLength = sizeof (EFI_FFS_FILE_HEADER);
 | 
						|
    }
 | 
						|
 | 
						|
    if (IsBufferErased (FvDevice->ErasePolarity, FfsHeader, TestLength)) {
 | 
						|
      //
 | 
						|
      // We have found the free space so we are done!
 | 
						|
      //
 | 
						|
      goto Done;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!IsValidFfsHeader (FvDevice->ErasePolarity, FfsHeader, &FileState)) {
 | 
						|
      if ((FileState == EFI_FILE_HEADER_INVALID) ||
 | 
						|
          (FileState == EFI_FILE_HEADER_CONSTRUCTION)) {
 | 
						|
        if (IS_FFS_FILE2 (FfsHeader)) {
 | 
						|
          if (!FvDevice->IsFfs3Fv) {
 | 
						|
            DEBUG ((EFI_D_ERROR, "Found a FFS3 formatted file: %g in a non-FFS3 formatted FV.\n", &FfsHeader->Name));
 | 
						|
          }
 | 
						|
          FfsHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FfsHeader + sizeof (EFI_FFS_FILE_HEADER2));
 | 
						|
        } else {
 | 
						|
          FfsHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FfsHeader + sizeof (EFI_FFS_FILE_HEADER));
 | 
						|
        }
 | 
						|
        continue;
 | 
						|
      } else {
 | 
						|
        //
 | 
						|
        // File system is corrputed
 | 
						|
        //
 | 
						|
        Status = EFI_VOLUME_CORRUPTED;
 | 
						|
        goto Done;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    CacheFfsHeader = FfsHeader;
 | 
						|
    if ((CacheFfsHeader->Attributes & FFS_ATTRIB_CHECKSUM) == FFS_ATTRIB_CHECKSUM) {
 | 
						|
      if (FvDevice->IsMemoryMapped) {
 | 
						|
        //
 | 
						|
        // Memory mapped FV has not been cached.
 | 
						|
        // Here is to cache FFS file to memory buffer for following checksum calculating.
 | 
						|
        // And then, the cached file buffer can be also used for FvReadFile.
 | 
						|
        //
 | 
						|
        WholeFileSize = IS_FFS_FILE2 (CacheFfsHeader) ? FFS_FILE2_SIZE (CacheFfsHeader): FFS_FILE_SIZE (CacheFfsHeader);
 | 
						|
        CacheFfsHeader = AllocateCopyPool (WholeFileSize, CacheFfsHeader);
 | 
						|
        if (CacheFfsHeader == NULL) {
 | 
						|
          Status = EFI_OUT_OF_RESOURCES;
 | 
						|
          goto Done;
 | 
						|
        }
 | 
						|
        FileCached = TRUE;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    if (!IsValidFfsFile (FvDevice->ErasePolarity, CacheFfsHeader)) {
 | 
						|
      //
 | 
						|
      // File system is corrupted
 | 
						|
      //
 | 
						|
      Status = EFI_VOLUME_CORRUPTED;
 | 
						|
      goto Done;
 | 
						|
    }
 | 
						|
 | 
						|
    if (IS_FFS_FILE2 (CacheFfsHeader)) {
 | 
						|
      ASSERT (FFS_FILE2_SIZE (CacheFfsHeader) > 0x00FFFFFF);
 | 
						|
      if (!FvDevice->IsFfs3Fv) {
 | 
						|
        DEBUG ((EFI_D_ERROR, "Found a FFS3 formatted file: %g in a non-FFS3 formatted FV.\n", &CacheFfsHeader->Name));
 | 
						|
        FfsHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FfsHeader + FFS_FILE2_SIZE (CacheFfsHeader));
 | 
						|
        //
 | 
						|
        // Adjust pointer to the next 8-byte aligned boundry.
 | 
						|
        //
 | 
						|
        FfsHeader = (EFI_FFS_FILE_HEADER *) (((UINTN) FfsHeader + 7) & ~0x07);
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    FileState = GetFileState (FvDevice->ErasePolarity, CacheFfsHeader);
 | 
						|
 | 
						|
    //
 | 
						|
    // check for non-deleted file
 | 
						|
    //
 | 
						|
    if (FileState != EFI_FILE_DELETED) {
 | 
						|
      //
 | 
						|
      // Create a FFS list entry for each non-deleted file
 | 
						|
      //
 | 
						|
      FfsFileEntry = AllocateZeroPool (sizeof (FFS_FILE_LIST_ENTRY));
 | 
						|
      if (FfsFileEntry == NULL) {
 | 
						|
        Status = EFI_OUT_OF_RESOURCES;
 | 
						|
        goto Done;
 | 
						|
      }
 | 
						|
 | 
						|
      FfsFileEntry->FfsHeader = CacheFfsHeader;
 | 
						|
      FfsFileEntry->FileCached = FileCached;
 | 
						|
      FileCached = FALSE;
 | 
						|
      InsertTailList (&FvDevice->FfsFileListHeader, &FfsFileEntry->Link);
 | 
						|
    }
 | 
						|
 | 
						|
    if (IS_FFS_FILE2 (CacheFfsHeader)) {
 | 
						|
      FfsHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FfsHeader + FFS_FILE2_SIZE (CacheFfsHeader));
 | 
						|
    } else {
 | 
						|
      FfsHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FfsHeader + FFS_FILE_SIZE (CacheFfsHeader));
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Adjust pointer to the next 8-byte aligned boundry.
 | 
						|
    //
 | 
						|
    FfsHeader = (EFI_FFS_FILE_HEADER *)(((UINTN)FfsHeader + 7) & ~0x07);
 | 
						|
 | 
						|
  }
 | 
						|
 | 
						|
Done:
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    if (FileCached) {
 | 
						|
      CoreFreePool (CacheFfsHeader);
 | 
						|
      FileCached = FALSE;
 | 
						|
    }
 | 
						|
    FreeFvDeviceResource (FvDevice);
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  This notification function is invoked when an instance of the
 | 
						|
  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL is produced.  It layers an instance of the
 | 
						|
  EFI_FIRMWARE_VOLUME2_PROTOCOL on the same handle.  This is the function where
 | 
						|
  the actual initialization of the EFI_FIRMWARE_VOLUME2_PROTOCOL is done.
 | 
						|
 | 
						|
  @param  Event                 The event that occured
 | 
						|
  @param  Context               For EFI compatiblity.  Not used.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
EFIAPI
 | 
						|
NotifyFwVolBlock (
 | 
						|
  IN  EFI_EVENT Event,
 | 
						|
  IN  VOID      *Context
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_HANDLE                            Handle;
 | 
						|
  EFI_STATUS                            Status;
 | 
						|
  UINTN                                 BufferSize;
 | 
						|
  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL    *Fvb;
 | 
						|
  EFI_FIRMWARE_VOLUME2_PROTOCOL         *Fv;
 | 
						|
  FV_DEVICE                             *FvDevice;
 | 
						|
  EFI_FIRMWARE_VOLUME_HEADER            *FwVolHeader;
 | 
						|
  //
 | 
						|
  // Examine all new handles
 | 
						|
  //
 | 
						|
  for (;;) {
 | 
						|
    //
 | 
						|
    // Get the next handle
 | 
						|
    //
 | 
						|
    BufferSize = sizeof (Handle);
 | 
						|
    Status = CoreLocateHandle (
 | 
						|
              ByRegisterNotify,
 | 
						|
              NULL,
 | 
						|
              gEfiFwVolBlockNotifyReg,
 | 
						|
              &BufferSize,
 | 
						|
              &Handle
 | 
						|
              );
 | 
						|
 | 
						|
    //
 | 
						|
    // If not found, we're done
 | 
						|
    //
 | 
						|
    if (EFI_NOT_FOUND == Status) {
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Get the FirmwareVolumeBlock protocol on that handle
 | 
						|
    //
 | 
						|
    Status = CoreHandleProtocol (Handle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID **)&Fvb);
 | 
						|
    ASSERT_EFI_ERROR (Status);
 | 
						|
    ASSERT (Fvb != NULL);
 | 
						|
 | 
						|
    //
 | 
						|
    // Make sure the Fv Header is O.K.
 | 
						|
    //
 | 
						|
    Status = GetFwVolHeader (Fvb, &FwVolHeader);
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    ASSERT (FwVolHeader != NULL);
 | 
						|
 | 
						|
    if (!VerifyFvHeaderChecksum (FwVolHeader)) {
 | 
						|
      CoreFreePool (FwVolHeader);
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Check if there is an FV protocol already installed in that handle
 | 
						|
    //
 | 
						|
    Status = CoreHandleProtocol (Handle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&Fv);
 | 
						|
    if (!EFI_ERROR (Status)) {
 | 
						|
      //
 | 
						|
      // Update Fv to use a new Fvb
 | 
						|
      //
 | 
						|
      FvDevice = BASE_CR (Fv, FV_DEVICE, Fv);
 | 
						|
      if (FvDevice->Signature == FV2_DEVICE_SIGNATURE) {
 | 
						|
        //
 | 
						|
        // Only write into our device structure if it's our device structure
 | 
						|
        //
 | 
						|
        FvDevice->Fvb = Fvb;
 | 
						|
      }
 | 
						|
 | 
						|
    } else {
 | 
						|
      //
 | 
						|
      // No FwVol protocol on the handle so create a new one
 | 
						|
      //
 | 
						|
      FvDevice = AllocateCopyPool (sizeof (FV_DEVICE), &mFvDevice);
 | 
						|
      if (FvDevice == NULL) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      FvDevice->Fvb             = Fvb;
 | 
						|
      FvDevice->Handle          = Handle;
 | 
						|
      FvDevice->FwVolHeader     = FwVolHeader;
 | 
						|
      FvDevice->IsFfs3Fv        = CompareGuid (&FwVolHeader->FileSystemGuid, &gEfiFirmwareFileSystem3Guid);
 | 
						|
      FvDevice->Fv.ParentHandle = Fvb->ParentHandle;
 | 
						|
 | 
						|
      if (Fvb->ParentHandle != NULL) {
 | 
						|
        //
 | 
						|
        // Inherit the authentication status from FVB.
 | 
						|
        //
 | 
						|
        FvDevice->AuthenticationStatus = GetFvbAuthenticationStatus (Fvb);
 | 
						|
      }
 | 
						|
      
 | 
						|
      if (!EFI_ERROR (FvCheck (FvDevice))) {
 | 
						|
        //
 | 
						|
        // Install an New FV protocol on the existing handle
 | 
						|
        //
 | 
						|
        Status = CoreInstallProtocolInterface (
 | 
						|
                    &Handle,
 | 
						|
                    &gEfiFirmwareVolume2ProtocolGuid,
 | 
						|
                    EFI_NATIVE_INTERFACE,
 | 
						|
                    &FvDevice->Fv
 | 
						|
                    );
 | 
						|
        ASSERT_EFI_ERROR (Status);
 | 
						|
      } else {
 | 
						|
        //
 | 
						|
        // Free FvDevice Buffer for the corrupt FV image.
 | 
						|
        //
 | 
						|
        CoreFreePool (FvDevice);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  This routine is the driver initialization entry point.  It registers
 | 
						|
  a notification function.  This notification function are responsible
 | 
						|
  for building the FV stack dynamically.
 | 
						|
 | 
						|
  @param  ImageHandle           The image handle.
 | 
						|
  @param  SystemTable           The system table.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           Function successfully returned.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
FwVolDriverInit (
 | 
						|
  IN EFI_HANDLE                   ImageHandle,
 | 
						|
  IN EFI_SYSTEM_TABLE             *SystemTable
 | 
						|
  )
 | 
						|
{
 | 
						|
  gEfiFwVolBlockEvent = EfiCreateProtocolNotifyEvent (
 | 
						|
                          &gEfiFirmwareVolumeBlockProtocolGuid,
 | 
						|
                          TPL_CALLBACK,
 | 
						|
                          NotifyFwVolBlock,
 | 
						|
                          NULL,
 | 
						|
                          &gEfiFwVolBlockNotifyReg
 | 
						|
                          );
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
 |