FvbDev->LbaCache must be freed on error path before freeing FvbDev. Cc: Jian J Wang <jian.j.wang@intel.com> Cc: Dandan Bi <dandan.bi@intel.com> Cc: Liming Gao <gaoliming@byosoft.com.cn> Signed-off-by: Mike Maslenkin <mike.maslenkin@gmail.com> Reviewed-by: Michael D Kinney <michael.d.kinney@intel.com> Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn>
		
			
				
	
	
		
			714 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			714 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  Implementations for Firmware Volume Block protocol.
 | 
						|
 | 
						|
  It consumes FV HOBs and creates read-only Firmare Volume Block protocol
 | 
						|
  instances for each of them.
 | 
						|
 | 
						|
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
 | 
						|
SPDX-License-Identifier: BSD-2-Clause-Patent
 | 
						|
 | 
						|
**/
 | 
						|
 | 
						|
#include "DxeMain.h"
 | 
						|
#include "FwVolBlock.h"
 | 
						|
 | 
						|
FV_MEMMAP_DEVICE_PATH  mFvMemmapDevicePathTemplate = {
 | 
						|
  {
 | 
						|
    {
 | 
						|
      HARDWARE_DEVICE_PATH,
 | 
						|
      HW_MEMMAP_DP,
 | 
						|
      {
 | 
						|
        (UINT8)(sizeof (MEMMAP_DEVICE_PATH)),
 | 
						|
        (UINT8)(sizeof (MEMMAP_DEVICE_PATH) >> 8)
 | 
						|
      }
 | 
						|
    },
 | 
						|
    EfiMemoryMappedIO,
 | 
						|
    (EFI_PHYSICAL_ADDRESS)0,
 | 
						|
    (EFI_PHYSICAL_ADDRESS)0,
 | 
						|
  },
 | 
						|
  {
 | 
						|
    END_DEVICE_PATH_TYPE,
 | 
						|
    END_ENTIRE_DEVICE_PATH_SUBTYPE,
 | 
						|
    {
 | 
						|
      END_DEVICE_PATH_LENGTH,
 | 
						|
      0
 | 
						|
    }
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
FV_PIWG_DEVICE_PATH  mFvPIWGDevicePathTemplate = {
 | 
						|
  {
 | 
						|
    {
 | 
						|
      MEDIA_DEVICE_PATH,
 | 
						|
      MEDIA_PIWG_FW_VOL_DP,
 | 
						|
      {
 | 
						|
        (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH)),
 | 
						|
        (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH) >> 8)
 | 
						|
      }
 | 
						|
    },
 | 
						|
    { 0 }
 | 
						|
  },
 | 
						|
  {
 | 
						|
    END_DEVICE_PATH_TYPE,
 | 
						|
    END_ENTIRE_DEVICE_PATH_SUBTYPE,
 | 
						|
    {
 | 
						|
      END_DEVICE_PATH_LENGTH,
 | 
						|
      0
 | 
						|
    }
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
EFI_FW_VOL_BLOCK_DEVICE  mFwVolBlock = {
 | 
						|
  FVB_DEVICE_SIGNATURE,
 | 
						|
  NULL,
 | 
						|
  NULL,
 | 
						|
  {
 | 
						|
    FwVolBlockGetAttributes,
 | 
						|
    (EFI_FVB_SET_ATTRIBUTES)FwVolBlockSetAttributes,
 | 
						|
    FwVolBlockGetPhysicalAddress,
 | 
						|
    FwVolBlockGetBlockSize,
 | 
						|
    FwVolBlockReadBlock,
 | 
						|
    (EFI_FVB_WRITE)FwVolBlockWriteBlock,
 | 
						|
    (EFI_FVB_ERASE_BLOCKS)FwVolBlockEraseBlock,
 | 
						|
    NULL
 | 
						|
  },
 | 
						|
  0,
 | 
						|
  NULL,
 | 
						|
  0,
 | 
						|
  0,
 | 
						|
  0
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
  Retrieves Volume attributes.  No polarity translations are done.
 | 
						|
 | 
						|
  @param  This                   Calling context
 | 
						|
  @param  Attributes             output buffer which contains attributes
 | 
						|
 | 
						|
  @retval EFI_SUCCESS            The firmware volume attributes were returned.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
FwVolBlockGetAttributes (
 | 
						|
  IN CONST  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
 | 
						|
  OUT       EFI_FVB_ATTRIBUTES_2                *Attributes
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
 | 
						|
 | 
						|
  FvbDevice = FVB_DEVICE_FROM_THIS (This);
 | 
						|
 | 
						|
  //
 | 
						|
  // Since we are read only, it's safe to get attributes data from our in-memory copy.
 | 
						|
  //
 | 
						|
  *Attributes = FvbDevice->FvbAttributes & ~EFI_FVB2_WRITE_STATUS;
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Modifies the current settings of the firmware volume according to the input parameter.
 | 
						|
 | 
						|
  @param  This                   Calling context
 | 
						|
  @param  Attributes             input buffer which contains attributes
 | 
						|
 | 
						|
  @retval EFI_SUCCESS            The firmware volume attributes were returned.
 | 
						|
  @retval EFI_INVALID_PARAMETER  The attributes requested are in conflict with
 | 
						|
                                 the capabilities as declared in the firmware
 | 
						|
                                 volume header.
 | 
						|
  @retval EFI_UNSUPPORTED        Not supported.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
FwVolBlockSetAttributes (
 | 
						|
  IN CONST  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
 | 
						|
  IN CONST  EFI_FVB_ATTRIBUTES_2                *Attributes
 | 
						|
  )
 | 
						|
{
 | 
						|
  return EFI_UNSUPPORTED;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  The EraseBlock() function erases one or more blocks as denoted by the
 | 
						|
  variable argument list. The entire parameter list of blocks must be verified
 | 
						|
  prior to erasing any blocks.  If a block is requested that does not exist
 | 
						|
  within the associated firmware volume (it has a larger index than the last
 | 
						|
  block of the firmware volume), the EraseBlock() function must return
 | 
						|
  EFI_INVALID_PARAMETER without modifying the contents of the firmware volume.
 | 
						|
 | 
						|
  @param  This                   Calling context
 | 
						|
  @param  ...                    Starting LBA followed by Number of Lba to erase.
 | 
						|
                                 a -1 to terminate the list.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS            The erase request was successfully completed.
 | 
						|
  @retval EFI_ACCESS_DENIED      The firmware volume is in the WriteDisabled
 | 
						|
                                 state.
 | 
						|
  @retval EFI_DEVICE_ERROR       The block device is not functioning correctly
 | 
						|
                                 and could not be written. The firmware device
 | 
						|
                                 may have been partially erased.
 | 
						|
  @retval EFI_INVALID_PARAMETER  One or more of the LBAs listed in the variable
 | 
						|
                                 argument list do
 | 
						|
  @retval EFI_UNSUPPORTED        Not supported.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
FwVolBlockEraseBlock (
 | 
						|
  IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
 | 
						|
  ...
 | 
						|
  )
 | 
						|
{
 | 
						|
  return EFI_UNSUPPORTED;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Read the specified number of bytes from the block to the input buffer.
 | 
						|
 | 
						|
  @param  This                   Indicates the calling context.
 | 
						|
  @param  Lba                    The starting logical block index to read.
 | 
						|
  @param  Offset                 Offset into the block at which to begin reading.
 | 
						|
  @param  NumBytes               Pointer to a UINT32. At entry, *NumBytes
 | 
						|
                                 contains the total size of the buffer. At exit,
 | 
						|
                                 *NumBytes contains the total number of bytes
 | 
						|
                                 actually read.
 | 
						|
  @param  Buffer                 Pinter to a caller-allocated buffer that
 | 
						|
                                 contains the destine for the read.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS            The firmware volume was read successfully.
 | 
						|
  @retval EFI_BAD_BUFFER_SIZE    The read was attempted across an LBA boundary.
 | 
						|
  @retval EFI_ACCESS_DENIED      Access denied.
 | 
						|
  @retval EFI_DEVICE_ERROR       The block device is malfunctioning and could not
 | 
						|
                                 be read.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
FwVolBlockReadBlock (
 | 
						|
  IN CONST  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
 | 
						|
  IN CONST  EFI_LBA                             Lba,
 | 
						|
  IN CONST  UINTN                               Offset,
 | 
						|
  IN OUT    UINTN                               *NumBytes,
 | 
						|
  IN OUT    UINT8                               *Buffer
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_FW_VOL_BLOCK_DEVICE     *FvbDevice;
 | 
						|
  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader;
 | 
						|
  UINT8                       *LbaOffset;
 | 
						|
  UINTN                       LbaStart;
 | 
						|
  UINTN                       NumOfBytesRead;
 | 
						|
  UINTN                       LbaIndex;
 | 
						|
 | 
						|
  FvbDevice = FVB_DEVICE_FROM_THIS (This);
 | 
						|
 | 
						|
  //
 | 
						|
  // Check if This FW can be read
 | 
						|
  //
 | 
						|
  if ((FvbDevice->FvbAttributes & EFI_FVB2_READ_STATUS) == 0) {
 | 
						|
    return EFI_ACCESS_DENIED;
 | 
						|
  }
 | 
						|
 | 
						|
  LbaIndex = (UINTN)Lba;
 | 
						|
  if (LbaIndex >= FvbDevice->NumBlocks) {
 | 
						|
    //
 | 
						|
    // Invalid Lba, read nothing.
 | 
						|
    //
 | 
						|
    *NumBytes = 0;
 | 
						|
    return EFI_BAD_BUFFER_SIZE;
 | 
						|
  }
 | 
						|
 | 
						|
  if (Offset > FvbDevice->LbaCache[LbaIndex].Length) {
 | 
						|
    //
 | 
						|
    // all exceed boundary, read nothing.
 | 
						|
    //
 | 
						|
    *NumBytes = 0;
 | 
						|
    return EFI_BAD_BUFFER_SIZE;
 | 
						|
  }
 | 
						|
 | 
						|
  NumOfBytesRead = *NumBytes;
 | 
						|
  if (Offset + NumOfBytesRead > FvbDevice->LbaCache[LbaIndex].Length) {
 | 
						|
    //
 | 
						|
    // partial exceed boundary, read data from current postion to end.
 | 
						|
    //
 | 
						|
    NumOfBytesRead = FvbDevice->LbaCache[LbaIndex].Length - Offset;
 | 
						|
  }
 | 
						|
 | 
						|
  LbaStart    = FvbDevice->LbaCache[LbaIndex].Base;
 | 
						|
  FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)((UINTN)FvbDevice->BaseAddress);
 | 
						|
  LbaOffset   = (UINT8 *)FwVolHeader + LbaStart + Offset;
 | 
						|
 | 
						|
  //
 | 
						|
  // Perform read operation
 | 
						|
  //
 | 
						|
  CopyMem (Buffer, LbaOffset, NumOfBytesRead);
 | 
						|
 | 
						|
  if (NumOfBytesRead == *NumBytes) {
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  *NumBytes = NumOfBytesRead;
 | 
						|
  return EFI_BAD_BUFFER_SIZE;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Writes the specified number of bytes from the input buffer to the block.
 | 
						|
 | 
						|
  @param  This                   Indicates the calling context.
 | 
						|
  @param  Lba                    The starting logical block index to write to.
 | 
						|
  @param  Offset                 Offset into the block at which to begin writing.
 | 
						|
  @param  NumBytes               Pointer to a UINT32. At entry, *NumBytes
 | 
						|
                                 contains the total size of the buffer. At exit,
 | 
						|
                                 *NumBytes contains the total number of bytes
 | 
						|
                                 actually written.
 | 
						|
  @param  Buffer                 Pinter to a caller-allocated buffer that
 | 
						|
                                 contains the source for the write.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS            The firmware volume was written successfully.
 | 
						|
  @retval EFI_BAD_BUFFER_SIZE    The write was attempted across an LBA boundary.
 | 
						|
                                 On output, NumBytes contains the total number of
 | 
						|
                                 bytes actually written.
 | 
						|
  @retval EFI_ACCESS_DENIED      The firmware volume is in the WriteDisabled
 | 
						|
                                 state.
 | 
						|
  @retval EFI_DEVICE_ERROR       The block device is malfunctioning and could not
 | 
						|
                                 be written.
 | 
						|
  @retval EFI_UNSUPPORTED        Not supported.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
FwVolBlockWriteBlock (
 | 
						|
  IN     EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
 | 
						|
  IN     EFI_LBA                             Lba,
 | 
						|
  IN     UINTN                               Offset,
 | 
						|
  IN OUT UINTN                               *NumBytes,
 | 
						|
  IN     UINT8                               *Buffer
 | 
						|
  )
 | 
						|
{
 | 
						|
  return EFI_UNSUPPORTED;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Get Fvb's base address.
 | 
						|
 | 
						|
  @param  This                   Indicates the calling context.
 | 
						|
  @param  Address                Fvb device base address.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS            Successfully got Fvb's base address.
 | 
						|
  @retval EFI_UNSUPPORTED        Not supported.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
FwVolBlockGetPhysicalAddress (
 | 
						|
  IN CONST  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
 | 
						|
  OUT       EFI_PHYSICAL_ADDRESS                *Address
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
 | 
						|
 | 
						|
  FvbDevice = FVB_DEVICE_FROM_THIS (This);
 | 
						|
 | 
						|
  if ((FvbDevice->FvbAttributes & EFI_FVB2_MEMORY_MAPPED) != 0) {
 | 
						|
    *Address = FvbDevice->BaseAddress;
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  return EFI_UNSUPPORTED;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Retrieves the size in bytes of a specific block within a firmware volume.
 | 
						|
 | 
						|
  @param  This                   Indicates the calling context.
 | 
						|
  @param  Lba                    Indicates the block for which to return the
 | 
						|
                                 size.
 | 
						|
  @param  BlockSize              Pointer to a caller-allocated UINTN in which the
 | 
						|
                                 size of the block is returned.
 | 
						|
  @param  NumberOfBlocks         Pointer to a caller-allocated UINTN in which the
 | 
						|
                                 number of consecutive blocks starting with Lba
 | 
						|
                                 is returned. All blocks in this range have a
 | 
						|
                                 size of BlockSize.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS            The firmware volume base address is returned.
 | 
						|
  @retval EFI_INVALID_PARAMETER  The requested LBA is out of range.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
FwVolBlockGetBlockSize (
 | 
						|
  IN CONST  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
 | 
						|
  IN CONST  EFI_LBA                             Lba,
 | 
						|
  IN OUT    UINTN                               *BlockSize,
 | 
						|
  IN OUT    UINTN                               *NumberOfBlocks
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN                       TotalBlocks;
 | 
						|
  EFI_FW_VOL_BLOCK_DEVICE     *FvbDevice;
 | 
						|
  EFI_FV_BLOCK_MAP_ENTRY      *PtrBlockMapEntry;
 | 
						|
  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader;
 | 
						|
 | 
						|
  FvbDevice = FVB_DEVICE_FROM_THIS (This);
 | 
						|
 | 
						|
  //
 | 
						|
  // Do parameter checking
 | 
						|
  //
 | 
						|
  if (Lba >= FvbDevice->NumBlocks) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)((UINTN)FvbDevice->BaseAddress);
 | 
						|
 | 
						|
  PtrBlockMapEntry = FwVolHeader->BlockMap;
 | 
						|
 | 
						|
  //
 | 
						|
  // Search the block map for the given block
 | 
						|
  //
 | 
						|
  TotalBlocks = 0;
 | 
						|
  while ((PtrBlockMapEntry->NumBlocks != 0) || (PtrBlockMapEntry->Length != 0)) {
 | 
						|
    TotalBlocks += PtrBlockMapEntry->NumBlocks;
 | 
						|
    if (Lba < TotalBlocks) {
 | 
						|
      //
 | 
						|
      // We find the range
 | 
						|
      //
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    PtrBlockMapEntry++;
 | 
						|
  }
 | 
						|
 | 
						|
  *BlockSize      = PtrBlockMapEntry->Length;
 | 
						|
  *NumberOfBlocks = TotalBlocks - (UINTN)Lba;
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 | 
						|
  Get FVB authentication status
 | 
						|
 | 
						|
  @param FvbProtocol    FVB protocol.
 | 
						|
 | 
						|
  @return Authentication status.
 | 
						|
 | 
						|
**/
 | 
						|
UINT32
 | 
						|
GetFvbAuthenticationStatus (
 | 
						|
  IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *FvbProtocol
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
 | 
						|
  UINT32                   AuthenticationStatus;
 | 
						|
 | 
						|
  AuthenticationStatus = 0;
 | 
						|
  FvbDevice            = BASE_CR (FvbProtocol, EFI_FW_VOL_BLOCK_DEVICE, FwVolBlockInstance);
 | 
						|
  if (FvbDevice->Signature == FVB_DEVICE_SIGNATURE) {
 | 
						|
    AuthenticationStatus = FvbDevice->AuthenticationStatus;
 | 
						|
  }
 | 
						|
 | 
						|
  return AuthenticationStatus;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This routine produces a firmware volume block protocol on a given
 | 
						|
  buffer.
 | 
						|
 | 
						|
  @param  BaseAddress            base address of the firmware volume image
 | 
						|
  @param  Length                 length of the firmware volume image
 | 
						|
  @param  ParentHandle           handle of parent firmware volume, if this image
 | 
						|
                                 came from an FV image file and section in another firmware
 | 
						|
                                 volume (ala capsules)
 | 
						|
  @param  AuthenticationStatus   Authentication status inherited, if this image
 | 
						|
                                 came from an FV image file and section in another firmware volume.
 | 
						|
  @param  FvProtocol             Firmware volume block protocol produced.
 | 
						|
 | 
						|
  @retval EFI_VOLUME_CORRUPTED   Volume corrupted.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES   No enough buffer to be allocated.
 | 
						|
  @retval EFI_SUCCESS            Successfully produced a FVB protocol on given
 | 
						|
                                 buffer.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
ProduceFVBProtocolOnBuffer (
 | 
						|
  IN EFI_PHYSICAL_ADDRESS  BaseAddress,
 | 
						|
  IN UINT64                Length,
 | 
						|
  IN EFI_HANDLE            ParentHandle,
 | 
						|
  IN UINT32                AuthenticationStatus,
 | 
						|
  OUT EFI_HANDLE           *FvProtocol  OPTIONAL
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                  Status;
 | 
						|
  EFI_FW_VOL_BLOCK_DEVICE     *FvbDev;
 | 
						|
  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader;
 | 
						|
  UINTN                       BlockIndex;
 | 
						|
  UINTN                       BlockIndex2;
 | 
						|
  UINTN                       LinearOffset;
 | 
						|
  UINT32                      FvAlignment;
 | 
						|
  EFI_FV_BLOCK_MAP_ENTRY      *PtrBlockMapEntry;
 | 
						|
 | 
						|
  FvAlignment = 0;
 | 
						|
  FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)BaseAddress;
 | 
						|
  //
 | 
						|
  // Validate FV Header, if not as expected, return
 | 
						|
  //
 | 
						|
  if (FwVolHeader->Signature != EFI_FVH_SIGNATURE) {
 | 
						|
    return EFI_VOLUME_CORRUPTED;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // If EFI_FVB2_WEAK_ALIGNMENT is set in the volume header then the first byte of the volume
 | 
						|
  // can be aligned on any power-of-two boundary. A weakly aligned volume can not be moved from
 | 
						|
  // its initial linked location and maintain its alignment.
 | 
						|
  //
 | 
						|
  if ((FwVolHeader->Attributes & EFI_FVB2_WEAK_ALIGNMENT) != EFI_FVB2_WEAK_ALIGNMENT) {
 | 
						|
    //
 | 
						|
    // Get FvHeader alignment
 | 
						|
    //
 | 
						|
    FvAlignment = 1 << ((FwVolHeader->Attributes & EFI_FVB2_ALIGNMENT) >> 16);
 | 
						|
    //
 | 
						|
    // FvAlignment must be greater than or equal to 8 bytes of the minimum FFS alignment value.
 | 
						|
    //
 | 
						|
    if (FvAlignment < 8) {
 | 
						|
      FvAlignment = 8;
 | 
						|
    }
 | 
						|
 | 
						|
    if ((UINTN)BaseAddress % FvAlignment != 0) {
 | 
						|
      //
 | 
						|
      // FvImage buffer is not at its required alignment.
 | 
						|
      //
 | 
						|
      DEBUG ((
 | 
						|
        DEBUG_ERROR,
 | 
						|
        "Unaligned FvImage found at 0x%lx:0x%lx, the required alignment is 0x%x\n",
 | 
						|
        BaseAddress,
 | 
						|
        Length,
 | 
						|
        FvAlignment
 | 
						|
        ));
 | 
						|
      return EFI_VOLUME_CORRUPTED;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Allocate EFI_FW_VOL_BLOCK_DEVICE
 | 
						|
  //
 | 
						|
  FvbDev = AllocateCopyPool (sizeof (EFI_FW_VOL_BLOCK_DEVICE), &mFwVolBlock);
 | 
						|
  if (FvbDev == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  FvbDev->BaseAddress                     = BaseAddress;
 | 
						|
  FvbDev->FvbAttributes                   = FwVolHeader->Attributes;
 | 
						|
  FvbDev->FwVolBlockInstance.ParentHandle = ParentHandle;
 | 
						|
  FvbDev->AuthenticationStatus            = AuthenticationStatus;
 | 
						|
 | 
						|
  //
 | 
						|
  // Init the block caching fields of the device
 | 
						|
  // First, count the number of blocks
 | 
						|
  //
 | 
						|
  FvbDev->NumBlocks = 0;
 | 
						|
  for (PtrBlockMapEntry = FwVolHeader->BlockMap;
 | 
						|
       PtrBlockMapEntry->NumBlocks != 0;
 | 
						|
       PtrBlockMapEntry++)
 | 
						|
  {
 | 
						|
    FvbDev->NumBlocks += PtrBlockMapEntry->NumBlocks;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Second, allocate the cache
 | 
						|
  //
 | 
						|
  if (FvbDev->NumBlocks >= (MAX_ADDRESS / sizeof (LBA_CACHE))) {
 | 
						|
    CoreFreePool (FvbDev);
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  FvbDev->LbaCache = AllocatePool (FvbDev->NumBlocks * sizeof (LBA_CACHE));
 | 
						|
  if (FvbDev->LbaCache == NULL) {
 | 
						|
    CoreFreePool (FvbDev);
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Last, fill in the cache with the linear address of the blocks
 | 
						|
  //
 | 
						|
  BlockIndex   = 0;
 | 
						|
  LinearOffset = 0;
 | 
						|
  for (PtrBlockMapEntry = FwVolHeader->BlockMap;
 | 
						|
       PtrBlockMapEntry->NumBlocks != 0; PtrBlockMapEntry++)
 | 
						|
  {
 | 
						|
    for (BlockIndex2 = 0; BlockIndex2 < PtrBlockMapEntry->NumBlocks; BlockIndex2++) {
 | 
						|
      FvbDev->LbaCache[BlockIndex].Base   = LinearOffset;
 | 
						|
      FvbDev->LbaCache[BlockIndex].Length = PtrBlockMapEntry->Length;
 | 
						|
      LinearOffset                       += PtrBlockMapEntry->Length;
 | 
						|
      BlockIndex++;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Judget whether FV name guid is produced in Fv extension header
 | 
						|
  //
 | 
						|
  if (FwVolHeader->ExtHeaderOffset == 0) {
 | 
						|
    //
 | 
						|
    // FV does not contains extension header, then produce MEMMAP_DEVICE_PATH
 | 
						|
    //
 | 
						|
    FvbDev->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)AllocateCopyPool (sizeof (FV_MEMMAP_DEVICE_PATH), &mFvMemmapDevicePathTemplate);
 | 
						|
    if (FvbDev->DevicePath == NULL) {
 | 
						|
      FreePool (FvbDev->LbaCache);
 | 
						|
      FreePool (FvbDev);
 | 
						|
      return EFI_OUT_OF_RESOURCES;
 | 
						|
    }
 | 
						|
 | 
						|
    ((FV_MEMMAP_DEVICE_PATH *)FvbDev->DevicePath)->MemMapDevPath.StartingAddress = BaseAddress;
 | 
						|
    ((FV_MEMMAP_DEVICE_PATH *)FvbDev->DevicePath)->MemMapDevPath.EndingAddress   = BaseAddress + FwVolHeader->FvLength - 1;
 | 
						|
  } else {
 | 
						|
    //
 | 
						|
    // FV contains extension header, then produce MEDIA_FW_VOL_DEVICE_PATH
 | 
						|
    //
 | 
						|
    FvbDev->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)AllocateCopyPool (sizeof (FV_PIWG_DEVICE_PATH), &mFvPIWGDevicePathTemplate);
 | 
						|
    if (FvbDev->DevicePath == NULL) {
 | 
						|
      FreePool (FvbDev->LbaCache);
 | 
						|
      FreePool (FvbDev);
 | 
						|
      return EFI_OUT_OF_RESOURCES;
 | 
						|
    }
 | 
						|
 | 
						|
    CopyGuid (
 | 
						|
      &((FV_PIWG_DEVICE_PATH *)FvbDev->DevicePath)->FvDevPath.FvName,
 | 
						|
      (GUID *)(UINTN)(BaseAddress + FwVolHeader->ExtHeaderOffset)
 | 
						|
      );
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  //
 | 
						|
  // Attach FvVolBlock Protocol to new handle
 | 
						|
  //
 | 
						|
  Status = CoreInstallMultipleProtocolInterfaces (
 | 
						|
             &FvbDev->Handle,
 | 
						|
             &gEfiFirmwareVolumeBlockProtocolGuid,
 | 
						|
             &FvbDev->FwVolBlockInstance,
 | 
						|
             &gEfiDevicePathProtocolGuid,
 | 
						|
             FvbDev->DevicePath,
 | 
						|
             NULL
 | 
						|
             );
 | 
						|
 | 
						|
  //
 | 
						|
  // If they want the handle back, set it.
 | 
						|
  //
 | 
						|
  if (FvProtocol != NULL) {
 | 
						|
    *FvProtocol = FvbDev->Handle;
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This routine consumes FV hobs and produces instances of FW_VOL_BLOCK_PROTOCOL as appropriate.
 | 
						|
 | 
						|
  @param  ImageHandle            The image handle.
 | 
						|
  @param  SystemTable            The system table.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS            Successfully initialized firmware volume block
 | 
						|
                                 driver.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
FwVolBlockDriverInit (
 | 
						|
  IN EFI_HANDLE        ImageHandle,
 | 
						|
  IN EFI_SYSTEM_TABLE  *SystemTable
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_PEI_HOB_POINTERS  FvHob;
 | 
						|
  EFI_PEI_HOB_POINTERS  Fv3Hob;
 | 
						|
  UINT32                AuthenticationStatus;
 | 
						|
 | 
						|
  //
 | 
						|
  // Core Needs Firmware Volumes to function
 | 
						|
  //
 | 
						|
  FvHob.Raw = GetHobList ();
 | 
						|
  while ((FvHob.Raw = GetNextHob (EFI_HOB_TYPE_FV, FvHob.Raw)) != NULL) {
 | 
						|
    AuthenticationStatus = 0;
 | 
						|
    //
 | 
						|
    // Get the authentication status propagated from PEI-phase to DXE.
 | 
						|
    //
 | 
						|
    Fv3Hob.Raw = GetHobList ();
 | 
						|
    while ((Fv3Hob.Raw = GetNextHob (EFI_HOB_TYPE_FV3, Fv3Hob.Raw)) != NULL) {
 | 
						|
      if ((Fv3Hob.FirmwareVolume3->BaseAddress == FvHob.FirmwareVolume->BaseAddress) &&
 | 
						|
          (Fv3Hob.FirmwareVolume3->Length == FvHob.FirmwareVolume->Length))
 | 
						|
      {
 | 
						|
        AuthenticationStatus = Fv3Hob.FirmwareVolume3->AuthenticationStatus;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      Fv3Hob.Raw = GET_NEXT_HOB (Fv3Hob);
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Produce an FVB protocol for it
 | 
						|
    //
 | 
						|
    ProduceFVBProtocolOnBuffer (FvHob.FirmwareVolume->BaseAddress, FvHob.FirmwareVolume->Length, NULL, AuthenticationStatus, NULL);
 | 
						|
    FvHob.Raw = GET_NEXT_HOB (FvHob);
 | 
						|
  }
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This DXE service routine is used to process a firmware volume. In
 | 
						|
  particular, it can be called by BDS to process a single firmware
 | 
						|
  volume found in a capsule.
 | 
						|
 | 
						|
  Caution: The caller need validate the input firmware volume to follow
 | 
						|
  PI specification.
 | 
						|
  DxeCore will trust the input data and process firmware volume directly.
 | 
						|
 | 
						|
  @param  FvHeader               pointer to a firmware volume header
 | 
						|
  @param  Size                   the size of the buffer pointed to by FvHeader
 | 
						|
  @param  FVProtocolHandle       the handle on which a firmware volume protocol
 | 
						|
                                 was produced for the firmware volume passed in.
 | 
						|
 | 
						|
  @retval EFI_OUT_OF_RESOURCES   if an FVB could not be produced due to lack of
 | 
						|
                                 system resources
 | 
						|
  @retval EFI_VOLUME_CORRUPTED   if the volume was corrupted
 | 
						|
  @retval EFI_SUCCESS            a firmware volume protocol was produced for the
 | 
						|
                                 firmware volume
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
CoreProcessFirmwareVolume (
 | 
						|
  IN VOID         *FvHeader,
 | 
						|
  IN UINTN        Size,
 | 
						|
  OUT EFI_HANDLE  *FVProtocolHandle
 | 
						|
  )
 | 
						|
{
 | 
						|
  VOID        *Ptr;
 | 
						|
  EFI_STATUS  Status;
 | 
						|
 | 
						|
  *FVProtocolHandle = NULL;
 | 
						|
  Status            = ProduceFVBProtocolOnBuffer (
 | 
						|
                        (EFI_PHYSICAL_ADDRESS)(UINTN)FvHeader,
 | 
						|
                        (UINT64)Size,
 | 
						|
                        NULL,
 | 
						|
                        0,
 | 
						|
                        FVProtocolHandle
 | 
						|
                        );
 | 
						|
  //
 | 
						|
  // Since in our implementation we use register-protocol-notify to put a
 | 
						|
  // FV protocol on the FVB protocol handle, we can't directly verify that
 | 
						|
  // the FV protocol was produced. Therefore here we will check the handle
 | 
						|
  // and make sure an FV protocol is on it. This indicates that all went
 | 
						|
  // well. Otherwise we have to assume that the volume was corrupted
 | 
						|
  // somehow.
 | 
						|
  //
 | 
						|
  if (!EFI_ERROR (Status)) {
 | 
						|
    ASSERT (*FVProtocolHandle != NULL);
 | 
						|
    Ptr    = NULL;
 | 
						|
    Status = CoreHandleProtocol (*FVProtocolHandle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&Ptr);
 | 
						|
    if (EFI_ERROR (Status) || (Ptr == NULL)) {
 | 
						|
      return EFI_VOLUME_CORRUPTED;
 | 
						|
    }
 | 
						|
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 |