/** @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;
}
