/** @file
  Implements write firmware file.

  Copyright (c) 2006 - 2017, 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 "FwVolDriver.h"

/**
  Calculate the checksum for the FFS header.

  @param FfsHeader   FFS File Header which needs to calculate the checksum

**/
VOID
SetHeaderChecksum (
  IN EFI_FFS_FILE_HEADER *FfsHeader
  )
{
  EFI_FFS_FILE_STATE  State;
  UINT8               FileChecksum;

  //
  // The state and the File checksum are not included
  //
  State = FfsHeader->State;
  FfsHeader->State = 0;

  FileChecksum = FfsHeader->IntegrityCheck.Checksum.File;
  FfsHeader->IntegrityCheck.Checksum.File = 0;

  FfsHeader->IntegrityCheck.Checksum.Header = 0;

  if (IS_FFS_FILE2 (FfsHeader)) {
    FfsHeader->IntegrityCheck.Checksum.Header = CalculateCheckSum8 (
      (UINT8 *) FfsHeader,
      sizeof (EFI_FFS_FILE_HEADER2)
      );
  } else {
    FfsHeader->IntegrityCheck.Checksum.Header = CalculateCheckSum8 (
      (UINT8 *) FfsHeader,
      sizeof (EFI_FFS_FILE_HEADER)
      );
  }

  FfsHeader->State                          = State;
  FfsHeader->IntegrityCheck.Checksum.File   = FileChecksum;

  return ;
}

/**
  Calculate the checksum for the FFS File.

  @param FfsHeader       FFS File Header which needs to calculate the checksum
  @param ActualFileSize  The whole Ffs File Length.

**/
VOID
SetFileChecksum (
  IN EFI_FFS_FILE_HEADER *FfsHeader,
  IN UINTN               ActualFileSize
  )
{
  if ((FfsHeader->Attributes & FFS_ATTRIB_CHECKSUM) != 0) {

    FfsHeader->IntegrityCheck.Checksum.File = 0;

    if (IS_FFS_FILE2 (FfsHeader)) {
      FfsHeader->IntegrityCheck.Checksum.File = CalculateCheckSum8 (
        (UINT8 *) FfsHeader + sizeof (EFI_FFS_FILE_HEADER2),
        ActualFileSize - sizeof (EFI_FFS_FILE_HEADER2)
        );
    } else {
      FfsHeader->IntegrityCheck.Checksum.File = CalculateCheckSum8 (
        (UINT8 *) FfsHeader + sizeof (EFI_FFS_FILE_HEADER),
        ActualFileSize - sizeof (EFI_FFS_FILE_HEADER)
        );
    }

  } else {

    FfsHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM;

  }

  return ;
}

/**
  Get the alignment value from File Attributes.

  @param FfsAttributes  FFS attribute

  @return Alignment value.

**/
UINTN
GetRequiredAlignment (
  IN EFI_FV_FILE_ATTRIBUTES FfsAttributes
  )
{
  UINTN AlignmentValue;

  AlignmentValue = FfsAttributes & EFI_FV_FILE_ATTRIB_ALIGNMENT;

  if (AlignmentValue <= 3) {
    return 0x08;
  }

  if (AlignmentValue > 16) {
    //
    // Anyway, we won't reach this code
    //
    return 0x08;
  }

  return (UINTN)1 << AlignmentValue;

}

/**
  Calculate the leading Pad file size to meet the alignment requirement.

  @param FvDevice          Cached Firmware Volume.
  @param StartAddress      The starting address to write the FFS File.
  @param BufferSize        The FFS File Buffer Size.
  @param RequiredAlignment FFS File Data alignment requirement.

  @return The required Pad File Size.

**/
UINTN
CalculatePadFileSize (
  IN FV_DEVICE            *FvDevice,
  IN EFI_PHYSICAL_ADDRESS StartAddress,
  IN UINTN                BufferSize,
  IN UINTN                RequiredAlignment
  )
{
  UINTN DataStartPos;
  UINTN RelativePos;
  UINTN PadSize;

  if (BufferSize > 0x00FFFFFF) {
    DataStartPos  = (UINTN) StartAddress + sizeof (EFI_FFS_FILE_HEADER2);
  } else {
    DataStartPos  = (UINTN) StartAddress + sizeof (EFI_FFS_FILE_HEADER);
  }
  RelativePos   = DataStartPos - (UINTN) FvDevice->CachedFv;

  PadSize       = 0;

  while ((RelativePos & (RequiredAlignment - 1)) != 0) {
    RelativePos++;
    PadSize++;
  }
  //
  // If padsize is 0, no pad file needed;
  // If padsize is great than 24, then pad file can be created
  //
  if ((PadSize == 0) || (PadSize >= sizeof (EFI_FFS_FILE_HEADER))) {
    return PadSize;
  }

  //
  // Perhaps following method can save space
  //
  RelativePos = DataStartPos - (UINTN) FvDevice->CachedFv + sizeof (EFI_FFS_FILE_HEADER);
  PadSize     = sizeof (EFI_FFS_FILE_HEADER);

  while ((RelativePos & (RequiredAlignment - 1)) != 0) {
    RelativePos++;
    PadSize++;
  }

  return PadSize;
}

/**
  Convert EFI_FV_FILE_ATTRIBUTES to FFS_FILE_ATTRIBUTES.

  @param FvFileAttrib    The value of EFI_FV_FILE_ATTRIBUTES
  @param FfsFileAttrib   Pointer to the got FFS_FILE_ATTRIBUTES value.

**/
VOID
FvFileAttrib2FfsFileAttrib (
  IN     EFI_FV_FILE_ATTRIBUTES  FvFileAttrib,
  OUT UINT8                      *FfsFileAttrib
  )
{
  UINT8 FvFileAlignment;
  UINT8 FfsFileAlignment;
  UINT8 FfsFileAlignment2;

  FvFileAlignment   = (UINT8) (FvFileAttrib & EFI_FV_FILE_ATTRIB_ALIGNMENT);
  FfsFileAlignment  = 0;
  FfsFileAlignment2 = 0;

  switch (FvFileAlignment) {
  case 0:
    //
    // fall through
    //
  case 1:
    //
    // fall through
    //
  case 2:
    //
    // fall through
    //
  case 3:
    //
    // fall through
    //
    FfsFileAlignment = 0;
    break;

  case 4:
    //
    // fall through
    //
  case 5:
    //
    // fall through
    //
  case 6:
    //
    // fall through
    //
    FfsFileAlignment = 1;
    break;

  case 7:
    //
    // fall through
    //
  case 8:
    //
    // fall through
    //
    FfsFileAlignment = 2;
    break;

  case 9:
    FfsFileAlignment = 3;
    break;

  case 10:
    //
    // fall through
    //
  case 11:
    //
    // fall through
    //
    FfsFileAlignment = 4;
    break;

  case 12:
    //
    // fall through
    //
  case 13:
    //
    // fall through
    //
  case 14:
    //
    // fall through
    //
    FfsFileAlignment = 5;
    break;

  case 15:
    FfsFileAlignment = 6;
    break;

  case 16:
    FfsFileAlignment = 7;
    break;

  case 17:
    FfsFileAlignment = 0;
    FfsFileAlignment2 = 1;
    break;
  case 18:
    FfsFileAlignment = 1;
    FfsFileAlignment2 = 1;
    break;
  case 19:
    FfsFileAlignment = 2;
    FfsFileAlignment2 = 1;
    break;
  case 20:
    FfsFileAlignment = 3;
    FfsFileAlignment2 = 1;
    break;
  case 21:
    FfsFileAlignment = 4;
    FfsFileAlignment2 = 1;
    break;
  case 22:
    FfsFileAlignment = 5;
    FfsFileAlignment2 = 1;
    break;
  case 23:
    FfsFileAlignment = 6;
    FfsFileAlignment2 = 1;
    break;
  case 24:
    FfsFileAlignment = 7;
    FfsFileAlignment2 = 1;
    break;
  }

  *FfsFileAttrib = (UINT8) ((FfsFileAlignment << 3) | (FfsFileAlignment2 << 1));

  return ;
}

/**
  Locate a free space entry that can hold this FFS file.

  @param FvDevice          Cached Firmware Volume.
  @param Size              The FFS file size.
  @param RequiredAlignment FFS File Data alignment requirement.
  @param PadSize           Pointer to the size of leading Pad File.
  @param FreeSpaceEntry    Pointer to the Free Space Entry that meets the requirement.

  @retval EFI_SUCCESS     The free space entry is found.
  @retval EFI_NOT_FOUND   The free space entry can't be found.

**/
EFI_STATUS
FvLocateFreeSpaceEntry (
  IN  FV_DEVICE             *FvDevice,
  IN  UINTN                 Size,
  IN  UINTN                 RequiredAlignment,
  OUT UINTN                 *PadSize,
  OUT FREE_SPACE_ENTRY      **FreeSpaceEntry
  )
{
  FREE_SPACE_ENTRY  *FreeSpaceListEntry;
  LIST_ENTRY        *Link;
  UINTN             PadFileSize;

  Link                = FvDevice->FreeSpaceHeader.ForwardLink;
  FreeSpaceListEntry  = (FREE_SPACE_ENTRY *) Link;

  //
  // Loop the free space entry list to find one that can hold the
  // required the file size
  //
  while ((LIST_ENTRY *) FreeSpaceListEntry != &FvDevice->FreeSpaceHeader) {
    PadFileSize = CalculatePadFileSize (
                    FvDevice,
                    (EFI_PHYSICAL_ADDRESS) (UINTN) FreeSpaceListEntry->StartingAddress,
                    Size,
                    RequiredAlignment
                    );
    if (FreeSpaceListEntry->Length >= Size + PadFileSize) {
      *FreeSpaceEntry = FreeSpaceListEntry;
      *PadSize        = PadFileSize;
      return EFI_SUCCESS;
    }

    FreeSpaceListEntry = (FREE_SPACE_ENTRY *) FreeSpaceListEntry->Link.ForwardLink;
  }

  return EFI_NOT_FOUND;

}

/**
  Locate Pad File for writing, this is got from FV Cache.

  @param FvDevice           Cached Firmware Volume.
  @param Size               The required FFS file size.
  @param RequiredAlignment  FFS File Data alignment requirement.
  @param PadSize            Pointer to the size of leading Pad File.
  @param PadFileEntry       Pointer to the Pad File Entry that meets the requirement.

  @retval EFI_SUCCESS     The required pad file is found.
  @retval EFI_NOT_FOUND   The required pad file can't be found.

**/
EFI_STATUS
FvLocatePadFile (
  IN  FV_DEVICE           *FvDevice,
  IN  UINTN               Size,
  IN  UINTN               RequiredAlignment,
  OUT UINTN               *PadSize,
  OUT FFS_FILE_LIST_ENTRY **PadFileEntry
  )
{
  FFS_FILE_LIST_ENTRY *FileEntry;
  EFI_FFS_FILE_STATE  FileState;
  EFI_FFS_FILE_HEADER *FileHeader;
  UINTN               PadAreaLength;
  UINTN               PadFileSize;
  UINTN               HeaderSize;

  FileEntry = (FFS_FILE_LIST_ENTRY *) FvDevice->FfsFileListHeader.ForwardLink;

  //
  // travel through the whole file list to get the pad file entry
  //
  while (FileEntry != (FFS_FILE_LIST_ENTRY *) &FvDevice->FfsFileListHeader) {

    FileHeader  = (EFI_FFS_FILE_HEADER *) FileEntry->FfsHeader;
    FileState   = GetFileState (FvDevice->ErasePolarity, FileHeader);

    if ((FileHeader->Type == EFI_FV_FILETYPE_FFS_PAD) && (FileState == EFI_FILE_DATA_VALID)) {
      //
      // we find one valid pad file, check its free area length
      //
      if (IS_FFS_FILE2 (FileHeader)) {
        HeaderSize = sizeof (EFI_FFS_FILE_HEADER2);
        PadAreaLength = FFS_FILE2_SIZE (FileHeader) - HeaderSize;
      } else {
        HeaderSize = sizeof (EFI_FFS_FILE_HEADER);
        PadAreaLength = FFS_FILE_SIZE (FileHeader) - HeaderSize;
      }

      PadFileSize = CalculatePadFileSize (
                      FvDevice,
                      (EFI_PHYSICAL_ADDRESS) (UINTN) FileHeader + HeaderSize,
                      Size,
                      RequiredAlignment
                      );
      if (PadAreaLength >= (Size + PadFileSize)) {
        *PadSize      = PadFileSize;
        *PadFileEntry = FileEntry;
        return EFI_SUCCESS;
      }
    }

    FileEntry = (FFS_FILE_LIST_ENTRY *) (FileEntry->Link.ForwardLink);
  }

  return EFI_NOT_FOUND;
}

/**
  Locate a suitable pad file for multiple file writing.

  @param FvDevice          Cached Firmware Volume.
  @param NumOfFiles        The number of Files that needed updating
  @param BufferSize        The array of each file size.
  @param RequiredAlignment The array of of FFS File Data alignment requirement.
  @param PadSize           The array of size of each leading Pad File.
  @param TotalSizeNeeded   The totalsize that can hold these files.
  @param PadFileEntry      Pointer to the Pad File Entry that meets the requirement.

  @retval EFI_SUCCESS     The required pad file is found.
  @retval EFI_NOT_FOUND   The required pad file can't be found.

**/
EFI_STATUS
FvSearchSuitablePadFile (
  IN FV_DEVICE              *FvDevice,
  IN UINTN                  NumOfFiles,
  IN UINTN                  *BufferSize,
  IN UINTN                  *RequiredAlignment,
  OUT UINTN                 *PadSize,
  OUT UINTN                 *TotalSizeNeeded,
  OUT FFS_FILE_LIST_ENTRY   **PadFileEntry
  )
{
  FFS_FILE_LIST_ENTRY *FileEntry;
  EFI_FFS_FILE_STATE  FileState;
  EFI_FFS_FILE_HEADER *FileHeader;
  UINTN               PadAreaLength;
  UINTN               TotalSize;
  UINTN               Index;
  UINTN               HeaderSize;

  FileEntry = (FFS_FILE_LIST_ENTRY *) FvDevice->FfsFileListHeader.ForwardLink;

  //
  // travel through the whole file list to get the pad file entry
  //
  while (FileEntry != (FFS_FILE_LIST_ENTRY *) &FvDevice->FfsFileListHeader) {

    FileHeader  = (EFI_FFS_FILE_HEADER *) FileEntry->FfsHeader;
    FileState   = GetFileState (FvDevice->ErasePolarity, FileHeader);

    if ((FileHeader->Type == EFI_FV_FILETYPE_FFS_PAD) && (FileState == EFI_FILE_DATA_VALID)) {
      //
      // we find one valid pad file, check its length
      //
      if (IS_FFS_FILE2 (FileHeader)) {
        HeaderSize = sizeof (EFI_FFS_FILE_HEADER2);
        PadAreaLength = FFS_FILE2_SIZE (FileHeader) - HeaderSize;
      } else {
        HeaderSize = sizeof (EFI_FFS_FILE_HEADER);
        PadAreaLength = FFS_FILE_SIZE (FileHeader) - HeaderSize;
      }
      TotalSize     = 0;

      for (Index = 0; Index < NumOfFiles; Index++) {
        PadSize[Index] = CalculatePadFileSize (
                      FvDevice,
                      (EFI_PHYSICAL_ADDRESS) (UINTN) FileHeader + HeaderSize + TotalSize,
                      BufferSize[Index],
                      RequiredAlignment[Index]
                      );
        TotalSize += PadSize[Index];
        TotalSize += BufferSize[Index];

        if (TotalSize > PadAreaLength) {
          break;
        }
      }

      if (PadAreaLength >= TotalSize) {
        *PadFileEntry     = FileEntry;
        *TotalSizeNeeded  = TotalSize;
        return EFI_SUCCESS;
      }
    }

    FileEntry = (FFS_FILE_LIST_ENTRY *) (FileEntry->Link.ForwardLink);
  }

  return EFI_NOT_FOUND;
}

/**
  Locate a Free Space entry which can hold these files, including
  meeting the alignment requirements.

  @param FvDevice          Cached Firmware Volume.
  @param NumOfFiles        The number of Files that needed updating
  @param BufferSize        The array of each file size.
  @param RequiredAlignment The array of of FFS File Data alignment requirement.
  @param PadSize           The array of size of each leading Pad File.
  @param TotalSizeNeeded   The got total size that can hold these files.
  @param FreeSpaceEntry    The Free Space Entry that can hold these files.

  @retval EFI_SUCCESS     The free space entry is found.
  @retval EFI_NOT_FOUND   The free space entry can't be found.

**/
EFI_STATUS
FvSearchSuitableFreeSpace (
  IN FV_DEVICE              *FvDevice,
  IN UINTN                  NumOfFiles,
  IN UINTN                  *BufferSize,
  IN UINTN                  *RequiredAlignment,
  OUT UINTN                 *PadSize,
  OUT UINTN                 *TotalSizeNeeded,
  OUT FREE_SPACE_ENTRY      **FreeSpaceEntry
  )
{
  FREE_SPACE_ENTRY  *FreeSpaceListEntry;
  LIST_ENTRY        *Link;
  UINTN             TotalSize;
  UINTN             Index;
  UINT8             *StartAddr;

  Link                = FvDevice->FreeSpaceHeader.ForwardLink;

  FreeSpaceListEntry  = (FREE_SPACE_ENTRY *) Link;

  while ((LIST_ENTRY *) FreeSpaceListEntry != &FvDevice->FreeSpaceHeader) {
    TotalSize = 0;
    StartAddr = FreeSpaceListEntry->StartingAddress;

    //
    // Calculate the totalsize we need
    //
    for (Index = 0; Index < NumOfFiles; Index++) {
      //
      // Perhaps we don't need an EFI_FFS_FILE_HEADER, the first file
      // have had its leading pad file.
      //
      PadSize[Index] = CalculatePadFileSize (
                    FvDevice,
                    (EFI_PHYSICAL_ADDRESS) (UINTN) StartAddr + TotalSize,
                    BufferSize[Index],
                    RequiredAlignment[Index]
                    );

      TotalSize += PadSize[Index];
      TotalSize += BufferSize[Index];

      if (TotalSize > FreeSpaceListEntry->Length) {
        break;
      }
    }

    if (FreeSpaceListEntry->Length >= TotalSize) {
      *FreeSpaceEntry   = FreeSpaceListEntry;
      *TotalSizeNeeded  = TotalSize;
      return EFI_SUCCESS;
    }

    FreeSpaceListEntry = (FREE_SPACE_ENTRY *) FreeSpaceListEntry->Link.ForwardLink;
  }

  return EFI_NOT_FOUND;
}

/**
  Calculate the length of the remaining space in FV.

  @param FvDevice        Cached Firmware Volume
  @param Offset          Current offset to FV base address.
  @param Lba             LBA number for the current offset.
  @param LOffset         Offset in block for the current offset.

  @return the length of remaining space.

**/
UINTN
CalculateRemainingLength (
  IN     FV_DEVICE                            *FvDevice,
  IN     UINTN                                Offset,
  OUT  EFI_LBA                                *Lba,
  OUT  UINTN                                  *LOffset
  )
{
  LIST_ENTRY      *Link;
  LBA_ENTRY       *LbaEntry;
  UINTN           Count;

  Count     = 0;
  *Lba      = 0;
  Link      = FvDevice->LbaHeader.ForwardLink;
  LbaEntry  = (LBA_ENTRY *) Link;

  while (&LbaEntry->Link != &FvDevice->LbaHeader) {
    if (Count > Offset) {
      break;
    }

    Count += LbaEntry->BlockLength;
    (*Lba)++;
    Link      = LbaEntry->Link.ForwardLink;
    LbaEntry  = (LBA_ENTRY *) Link;
  }

  if (Count <= Offset) {
    return 0;
  }

  Link      = LbaEntry->Link.BackLink;
  LbaEntry  = (LBA_ENTRY *) Link;

  (*Lba)--;
  *LOffset  = (UINTN) (LbaEntry->BlockLength - (Count - Offset));

  Count     = 0;
  while (&LbaEntry->Link != &FvDevice->LbaHeader) {

    Count += LbaEntry->BlockLength;

    Link      = LbaEntry->Link.ForwardLink;
    LbaEntry  = (LBA_ENTRY *) Link;
  }

  Count -= *LOffset;

  return Count;
}

/**
  Writes data beginning at Lba:Offset from FV. The write terminates either
  when *NumBytes of data have been written, or when the firmware end is
  reached.  *NumBytes is updated to reflect the actual number of bytes
  written.

  @param FvDevice        Cached Firmware Volume
  @param Offset          Offset in the block at which to begin write
  @param NumBytes        At input, indicates the requested write size.
                         At output, indicates the actual number of bytes written.
  @param Buffer          Buffer containing source data for the write.

  @retval EFI_SUCCESS  Data is successfully written into FV.
  @return error        Data is failed written.

**/
EFI_STATUS
FvcWrite (
  IN     FV_DEVICE                            *FvDevice,
  IN     UINTN                                Offset,
  IN OUT UINTN                                *NumBytes,
  IN     UINT8                                *Buffer
  )
{
  EFI_STATUS                          Status;
  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *Fvb;
  EFI_LBA                             Lba;
  UINTN                               LOffset;
  EFI_FVB_ATTRIBUTES_2                FvbAttributes;
  UINTN                               RemainingLength;
  UINTN                               WriteLength;
  UINT8                               *TmpBuffer;
  
  LOffset = 0;
  RemainingLength = CalculateRemainingLength (FvDevice, Offset, &Lba, &LOffset);
  if ((UINTN) (*NumBytes) > RemainingLength) {
    *NumBytes = (UINTN) RemainingLength;
    return EFI_INVALID_PARAMETER;
  }

  Fvb = FvDevice->Fvb;

  Status = Fvb->GetAttributes (
                  Fvb,
                  &FvbAttributes
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  if ((FvbAttributes & EFI_FV2_WRITE_STATUS) == 0) {
    return EFI_ACCESS_DENIED;
  }

  RemainingLength = *NumBytes;
  WriteLength     = RemainingLength;
  TmpBuffer       = Buffer;

  do {
    Status = Fvb->Write (
                    Fvb,
                    Lba,
                    LOffset,
                    &WriteLength,
                    TmpBuffer
                    );
    if (!EFI_ERROR (Status)) {
      goto Done;
    }

    if (Status == EFI_BAD_BUFFER_SIZE) {
      Lba++;
      LOffset = 0;
      TmpBuffer += WriteLength;
      RemainingLength -= WriteLength;
      WriteLength = (UINTN) RemainingLength;

      continue;
    } else {
      return Status;
    }
  } while (1);

Done:
  return EFI_SUCCESS;
}

/**
  Create a new FFS file into Firmware Volume device.

  @param FvDevice        Cached Firmware Volume.
  @param FfsFileBuffer   A buffer that holds an FFS file,(it contains
                         a File Header which is in init state).
  @param BufferSize      The size of FfsFileBuffer.
  @param ActualFileSize  The actual file length, it may not be multiples of 8.
  @param FileName        The FFS File Name.
  @param FileType        The FFS File Type.
  @param FileAttributes  The Attributes of the FFS File to be created.

  @retval EFI_SUCCESS           FFS fle is added into FV.
  @retval EFI_INVALID_PARAMETER File type is not valid.
  @retval EFI_DEVICE_ERROR      FV doesn't set writable attribute.
  @retval EFI_NOT_FOUND         FV has no enough space for the added file.

**/
EFI_STATUS
FvCreateNewFile (
  IN FV_DEVICE                *FvDevice,
  IN UINT8                    *FfsFileBuffer,
  IN UINTN                    BufferSize,
  IN UINTN                    ActualFileSize,
  IN EFI_GUID                 *FileName,
  IN EFI_FV_FILETYPE          FileType,
  IN EFI_FV_FILE_ATTRIBUTES   FileAttributes
  )
{
  EFI_STATUS                          Status;
  EFI_FFS_FILE_HEADER                 *FileHeader;
  EFI_PHYSICAL_ADDRESS                BufferPtr;
  UINTN                               Offset;
  UINTN                               NumBytesWritten;
  UINTN                               StateOffset;
  FREE_SPACE_ENTRY                    *FreeSpaceEntry;
  UINTN                               RequiredAlignment;
  UINTN                               PadFileSize;
  FFS_FILE_LIST_ENTRY                 *PadFileEntry;
  EFI_FFS_FILE_ATTRIBUTES             TmpFileAttribute;
  FFS_FILE_LIST_ENTRY                 *FfsFileEntry;
  UINTN                               HeaderSize;

  //
  // File Type: 0x0E~0xE0 are reserved
  //
  if ((FileType > EFI_FV_FILETYPE_SMM_CORE) && (FileType < 0xE0)) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // First find a free space that can hold this image.
  // Check alignment, FFS at least must be aligned at 8-byte boundary
  //
  RequiredAlignment = GetRequiredAlignment (FileAttributes);

  Status = FvLocateFreeSpaceEntry (
            FvDevice,
            BufferSize,
            RequiredAlignment,
            &PadFileSize,
            &FreeSpaceEntry
            );
  if (EFI_ERROR (Status)) {
    //
    // Maybe we need to find a PAD file that can hold this image
    //
    Status = FvCreateNewFileInsidePadFile (
              FvDevice,
              FfsFileBuffer,
              BufferSize,
              ActualFileSize,
              FileName,
              FileType,
              FileAttributes
              );

    return Status;
  }

  BufferPtr     = (EFI_PHYSICAL_ADDRESS) (UINTN) FreeSpaceEntry->StartingAddress;

  //
  // If we need a leading PAD File, create it first.
  //
  if (PadFileSize != 0) {
    Status = FvCreatePadFileInFreeSpace (
              FvDevice,
              FreeSpaceEntry,
              PadFileSize - sizeof (EFI_FFS_FILE_HEADER),
              &PadFileEntry
              );
    if (EFI_ERROR (Status)) {
      return Status;
    }
  }
  //
  // Maybe we create a pad file, so re-get the free space starting address
  // and length
  //
  BufferPtr     = (EFI_PHYSICAL_ADDRESS) (UINTN) FreeSpaceEntry->StartingAddress;

  //
  // File creation step 1: Allocate File Header,
  // Mark EFI_FILE_HEADER_CONSTRUCTION bit to TRUE,
  // Write Name, IntegrityCheck.Header, Type, Attributes, and Size
  //
  FileHeader = (EFI_FFS_FILE_HEADER *) FfsFileBuffer;
  if (ActualFileSize > 0x00FFFFFF) {
    HeaderSize = sizeof (EFI_FFS_FILE_HEADER2);
  } else {
    HeaderSize = sizeof (EFI_FFS_FILE_HEADER);
  }
  SetFileState (EFI_FILE_HEADER_CONSTRUCTION, FileHeader);

  Offset          = (UINTN) (BufferPtr - FvDevice->CachedFv);
  StateOffset     = Offset + (UINT8 *) &FileHeader->State - (UINT8 *) FileHeader;

  NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);
  Status = FvcWrite (
            FvDevice,
            StateOffset,
            &NumBytesWritten,
            &FileHeader->State
            );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // update header 2 cache
  //
  CopyMem (
    (UINT8 *) (UINTN) BufferPtr,
    FileHeader,
    HeaderSize
    );

  //
  // update Free Space Entry, now need to substract the file header length
  //
  FreeSpaceEntry->StartingAddress += HeaderSize;
  FreeSpaceEntry->Length -= HeaderSize;

  CopyGuid (&FileHeader->Name, FileName);
  FileHeader->Type = FileType;

  //
  // Convert FvFileAttribute to FfsFileAttributes
  //
  FvFileAttrib2FfsFileAttrib (FileAttributes, &TmpFileAttribute);

  FileHeader->Attributes = TmpFileAttribute;

  //
  // File size is including the FFS File Header.
  //
  if (ActualFileSize > 0x00FFFFFF) {
    ((EFI_FFS_FILE_HEADER2 *) FileHeader)->ExtendedSize = (UINT32) ActualFileSize;
    *(UINT32 *) FileHeader->Size &= 0xFF000000;
    FileHeader->Attributes |= FFS_ATTRIB_LARGE_FILE;
  } else {
    *(UINT32 *) FileHeader->Size &= 0xFF000000;
    *(UINT32 *) FileHeader->Size |= ActualFileSize;
  }

  SetHeaderChecksum (FileHeader);

  Offset          = (UINTN) (BufferPtr - FvDevice->CachedFv);

  NumBytesWritten = HeaderSize;
  Status = FvcWrite (
            FvDevice,
            Offset,
            &NumBytesWritten,
            (UINT8 *) FileHeader
            );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // update header 2 cache
  //
  CopyMem (
    (UINT8 *) (UINTN) BufferPtr,
    FileHeader,
    HeaderSize
    );

  //
  // end of step 1
  //
  // File creation step 2:
  // MARK EFI_FILE_HEADER_VALID bit to TRUE,
  // Write IntegrityCheck.File, File Data
  //
  SetFileState (EFI_FILE_HEADER_VALID, FileHeader);

  Offset          = (UINTN) (BufferPtr - FvDevice->CachedFv);
  StateOffset     = Offset + (UINT8 *) &FileHeader->State - (UINT8 *) FileHeader;

  NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);
  Status = FvcWrite (
            FvDevice,
            StateOffset,
            &NumBytesWritten,
            &FileHeader->State
            );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // update header 2 cache
  //
  CopyMem (
    (UINT8 *) (UINTN) BufferPtr,
    FileHeader,
    HeaderSize
    );

  //
  // update Free Space Entry, now need to substract the file data length
  //
  FreeSpaceEntry->StartingAddress += (BufferSize - HeaderSize);
  FreeSpaceEntry->Length -= (BufferSize - HeaderSize);

  //
  // Calculate File Checksum
  //
  SetFileChecksum (FileHeader, ActualFileSize);

  Offset          = (UINTN) (BufferPtr - FvDevice->CachedFv);

  NumBytesWritten = BufferSize;
  Status = FvcWrite (
            FvDevice,
            Offset,
            &NumBytesWritten,
            FfsFileBuffer
            );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // each time write block successfully, write also to cache
  //
  CopyMem (
    (UINT8 *) (UINTN) BufferPtr,
    FfsFileBuffer,
    NumBytesWritten
    );

  //
  // Step 3: Mark EFI_FILE_DATA_VALID to TRUE
  //
  SetFileState (EFI_FILE_DATA_VALID, FileHeader);

  Offset          = (UINTN) (BufferPtr - FvDevice->CachedFv);
  StateOffset     = Offset + (UINT8 *) &FileHeader->State - (UINT8 *) FileHeader;

  NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);
  Status = FvcWrite (
            FvDevice,
            StateOffset,
            &NumBytesWritten,
            &FileHeader->State
            );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // update header 2 cache
  //
  CopyMem (
    (UINT8 *) (UINTN) BufferPtr,
    FileHeader,
    HeaderSize
    );

  //
  // If successfully, insert an FfsFileEntry at the end of ffs file list
  //

  FfsFileEntry            = AllocateZeroPool (sizeof (FFS_FILE_LIST_ENTRY));
  ASSERT (FfsFileEntry   != NULL);
  FfsFileEntry->FfsHeader = (UINT8 *) (UINTN) BufferPtr;
  InsertTailList (&FvDevice->FfsFileListHeader, &FfsFileEntry->Link);

  //
  // Set cache file to this file
  //
  FvDevice->CurrentFfsFile = FfsFileEntry;

  return EFI_SUCCESS;
}

/**
  Update a File, so after successful update, there are 2 files existing
  in FV, one is marked for deleted, and another one is valid.

  @param FvDevice          Cached Firmware Volume.
  @param FfsFileBuffer     A buffer that holds an FFS file,(it contains
                           a File Header which is in init state).
  @param BufferSize        The size of FfsFileBuffer.
  @param ActualFileSize    The actual file length, it may not be multiples of 8.
  @param FileName          The FFS File Name.
  @param NewFileType       The FFS File Type.
  @param NewFileAttributes The Attributes of the FFS File to be created.

  @retval EFI_SUCCESS           FFS fle is updated into FV.
  @retval EFI_INVALID_PARAMETER File type is not valid.
  @retval EFI_DEVICE_ERROR      FV doesn't set writable attribute.
  @retval EFI_NOT_FOUND         FV has no enough space for the added file.
                                FFS with same file name is not found in FV.

**/
EFI_STATUS
FvUpdateFile (
  IN FV_DEVICE                *FvDevice,
  IN UINT8                    *FfsFileBuffer,
  IN UINTN                    BufferSize,
  IN UINTN                    ActualFileSize,
  IN EFI_GUID                 *FileName,
  IN EFI_FV_FILETYPE          NewFileType,
  IN EFI_FV_FILE_ATTRIBUTES   NewFileAttributes
  )
{
  EFI_STATUS                          Status;
  EFI_FIRMWARE_VOLUME2_PROTOCOL       *Fv;
  UINTN                               NumBytesWritten;
  EFI_FV_FILETYPE                     OldFileType;
  EFI_FV_FILE_ATTRIBUTES              OldFileAttributes;
  UINTN                               OldFileSize;
  EFI_FFS_FILE_HEADER                 *OldFileHeader;
  UINTN                               OldOffset;
  UINTN                               OldStateOffset;
  FFS_FILE_LIST_ENTRY                 *OldFfsFileEntry;
  UINTN                               Key;
  EFI_GUID                            FileNameGuid;

  Fv  = &FvDevice->Fv;

  //
  // Step 1, find old file,
  // Mark EFI_FILE_MARKED_FOR_UPDATE to TRUE in the older header
  //

  //
  // Check if the file was read last time.
  //
  OldFileHeader   = NULL;
  OldFfsFileEntry = FvDevice->CurrentFfsFile;

  if (OldFfsFileEntry != NULL) {
    OldFileHeader = (EFI_FFS_FILE_HEADER *) OldFfsFileEntry->FfsHeader;
  }

  if ((OldFfsFileEntry == NULL) || (!CompareGuid (&OldFileHeader->Name, FileName))) {
    Key = 0;
    do {
      OldFileType = 0;
      Status = Fv->GetNextFile (
                    Fv,
                    &Key,
                    &OldFileType,
                    &FileNameGuid,
                    &OldFileAttributes,
                    &OldFileSize
                    );
      if (EFI_ERROR (Status)) {
        return Status;
      }
    } while (!CompareGuid (&FileNameGuid, FileName));

    //
    // Get FfsFileEntry from the search key
    //
    OldFfsFileEntry = (FFS_FILE_LIST_ENTRY *) Key;

    //
    // Double check file state before being ready to be removed
    //
    OldFileHeader = (EFI_FFS_FILE_HEADER *) OldFfsFileEntry->FfsHeader;
  } else {
    //
    // Mark the cache file to invalid
    //
    FvDevice->CurrentFfsFile = NULL;
  }
  //
  // Update File: Mark EFI_FILE_MARKED_FOR_UPDATE to TRUE
  //
  SetFileState (EFI_FILE_MARKED_FOR_UPDATE, OldFileHeader);

  OldOffset       = (UINTN) ((EFI_PHYSICAL_ADDRESS) (UINTN) OldFileHeader - FvDevice->CachedFv);
  OldStateOffset  = OldOffset + (UINT8 *) &OldFileHeader->State - (UINT8 *) OldFileHeader;

  NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);
  Status = FvcWrite (
            FvDevice,
            OldStateOffset,
            &NumBytesWritten,
            &OldFileHeader->State
            );
  if (EFI_ERROR (Status)) {
    //
    // if failed, write the bit back in the cache, its XOR operation.
    //
    SetFileState (EFI_FILE_MARKED_FOR_UPDATE, OldFileHeader);

    return Status;
  }

  //
  // Step 2, Create New Files
  //
  Status = FvCreateNewFile (
            FvDevice,
            FfsFileBuffer,
            BufferSize,
            ActualFileSize,
            FileName,
            NewFileType,
            NewFileAttributes
            );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // If successfully, remove this file entry,
  // although delete file may fail.
  //
  (OldFfsFileEntry->Link.BackLink)->ForwardLink = OldFfsFileEntry->Link.ForwardLink;
  (OldFfsFileEntry->Link.ForwardLink)->BackLink = OldFfsFileEntry->Link.BackLink;
  FreePool (OldFfsFileEntry);

  //
  // Step 3: Delete old files,
  // by marking EFI_FILE_DELETED to TRUE
  //
  SetFileState (EFI_FILE_DELETED, OldFileHeader);

  OldOffset       = (UINTN) ((EFI_PHYSICAL_ADDRESS) (UINTN) OldFileHeader - FvDevice->CachedFv);
  OldStateOffset  = OldOffset + (UINT8 *) &OldFileHeader->State - (UINT8 *) OldFileHeader;

  NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);
  Status = FvcWrite (
            FvDevice,
            OldStateOffset,
            &NumBytesWritten,
            &OldFileHeader->State
            );
  if (EFI_ERROR (Status)) {
    //
    // if failed, write the bit back in the cache, its XOR operation.
    //
    SetFileState (EFI_FILE_DELETED, OldFileHeader);

    return Status;
  }

  return EFI_SUCCESS;
}

/**
  Deleted a given file from FV device.

  @param FvDevice        Cached Firmware Volume.
  @param NameGuid        The FFS File Name.

  @retval EFI_SUCCESS    FFS file with the specified FFS name is removed.
  @retval EFI_NOT_FOUND  FFS file with the specified FFS name is not found.

**/
EFI_STATUS
FvDeleteFile (
  IN FV_DEVICE  *FvDevice,
  IN EFI_GUID   *NameGuid
  )
{
  EFI_STATUS                          Status;
  UINTN                               Key;
  EFI_GUID                            FileNameGuid;
  EFI_FV_FILETYPE                     FileType;
  EFI_FV_FILE_ATTRIBUTES              FileAttributes;
  UINTN                               FileSize;
  EFI_FFS_FILE_HEADER                 *FileHeader;
  FFS_FILE_LIST_ENTRY                 *FfsFileEntry;
  EFI_FFS_FILE_STATE                  FileState;
  EFI_FIRMWARE_VOLUME2_PROTOCOL        *Fv;
  UINTN                               Offset;
  UINTN                               StateOffset;
  UINTN                               NumBytesWritten;

  Fv  = &FvDevice->Fv;

  //
  // Check if the file was read last time.
  //
  FileHeader    = NULL;
  FfsFileEntry  = FvDevice->CurrentFfsFile;

  if (FfsFileEntry != NULL) {
    FileHeader = (EFI_FFS_FILE_HEADER *) FfsFileEntry->FfsHeader;
  }

  if ((FfsFileEntry == NULL) || (!CompareGuid (&FileHeader->Name, NameGuid))) {
    //
    // Next search for the file using GetNextFile
    //
    Key = 0;
    do {
      FileType = 0;
      Status = Fv->GetNextFile (
                    Fv,
                    &Key,
                    &FileType,
                    &FileNameGuid,
                    &FileAttributes,
                    &FileSize
                    );
      if (EFI_ERROR (Status)) {
        return Status;
      }
    } while (!CompareGuid (&FileNameGuid, NameGuid));

    //
    // Get FfsFileEntry from the search key
    //
    FfsFileEntry = (FFS_FILE_LIST_ENTRY *) Key;

    //
    // Double check file state before being ready to be removed
    //
    FileHeader = (EFI_FFS_FILE_HEADER *) FfsFileEntry->FfsHeader;
  } else {
    //
    // Mark the cache file to NULL
    //
    FvDevice->CurrentFfsFile = NULL;
  }

  FileState = GetFileState (FvDevice->ErasePolarity, FileHeader);

  if (FileState == EFI_FILE_HEADER_INVALID) {
    return EFI_NOT_FOUND;
  }

  if (FileState == EFI_FILE_DELETED) {
    return EFI_NOT_FOUND;
  }
  //
  // Delete File: Mark EFI_FILE_DELETED to TRUE
  //
  SetFileState (EFI_FILE_DELETED, FileHeader);

  Offset          = (UINTN) ((EFI_PHYSICAL_ADDRESS) (UINTN) FileHeader - FvDevice->CachedFv);
  StateOffset     = Offset + (UINT8 *) &FileHeader->State - (UINT8 *) FileHeader;

  NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);
  Status = FvcWrite (
            FvDevice,
            StateOffset,
            &NumBytesWritten,
            &FileHeader->State
            );
  if (EFI_ERROR (Status)) {
    //
    // if failed, write the bit back in the cache, its XOR operation.
    //
    SetFileState (EFI_FILE_DELETED, FileHeader);

    return Status;
  }
  //
  // If successfully, remove this file entry
  //
  FvDevice->CurrentFfsFile                    = NULL;

  (FfsFileEntry->Link.BackLink)->ForwardLink  = FfsFileEntry->Link.ForwardLink;
  (FfsFileEntry->Link.ForwardLink)->BackLink  = FfsFileEntry->Link.BackLink;
  FreePool (FfsFileEntry);

  return EFI_SUCCESS;
}

/**
  Writes one or more files to the firmware volume.

  @param  This                   Indicates the calling context.
  @param  NumberOfFiles          Number of files.
  @param  WritePolicy            WritePolicy indicates the level of reliability
                                 for the write in the event of a power failure or
                                 other system failure during the write operation.
  @param  FileData               FileData is an pointer to an array of
                                 EFI_FV_WRITE_DATA. Each element of array
                                 FileData represents a file to be written.

  @retval EFI_SUCCESS            Files successfully written to firmware volume
  @retval EFI_OUT_OF_RESOURCES   Not enough buffer to be allocated.
  @retval EFI_DEVICE_ERROR       Device error.
  @retval EFI_WRITE_PROTECTED    Write protected.
  @retval EFI_NOT_FOUND          Not found.
  @retval EFI_INVALID_PARAMETER  Invalid parameter.
  @retval EFI_UNSUPPORTED        This function not supported.

**/
EFI_STATUS
EFIAPI
FvWriteFile (
  IN CONST EFI_FIRMWARE_VOLUME2_PROTOCOL   *This,
  IN UINT32                         NumberOfFiles,
  IN EFI_FV_WRITE_POLICY            WritePolicy,
  IN EFI_FV_WRITE_FILE_DATA         *FileData
  )
{
  EFI_STATUS                          Status;
  UINTN                               Index1;
  UINTN                               Index2;
  UINT8                               *FileBuffer;
  UINTN                               BufferSize;
  UINTN                               ActualSize;
  UINT8                               ErasePolarity;
  FV_DEVICE                           *FvDevice;
  EFI_FV_FILETYPE                     FileType;
  EFI_FV_FILE_ATTRIBUTES              FileAttributes;
  UINTN                               Size;
  BOOLEAN                             CreateNewFile[MAX_FILES];
  UINTN                               NumDelete;
  EFI_FV_ATTRIBUTES                   FvAttributes;
  UINT32                              AuthenticationStatus;
  UINTN                               HeaderSize;

  if (NumberOfFiles > MAX_FILES) {
    return EFI_UNSUPPORTED;
  }

  Status = EFI_SUCCESS;

  SetMem (CreateNewFile, NumberOfFiles, TRUE);

  FvDevice  = FV_DEVICE_FROM_THIS (This);

  //
  // First check the volume attributes.
  //
  Status = This->GetVolumeAttributes (
                  This,
                  &FvAttributes
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // Can we have write right?
  //
  if ((FvAttributes & EFI_FV2_WRITE_STATUS) == 0) {
    return EFI_WRITE_PROTECTED;
  }

  ErasePolarity = FvDevice->ErasePolarity;

  //
  // Loop for all files
  //
  NumDelete = 0;
  for (Index1 = 0; Index1 < NumberOfFiles; Index1++) {

    if ((FileData[Index1].BufferSize + sizeof (EFI_FFS_FILE_HEADER) > 0x00FFFFFF) && !FvDevice->IsFfs3Fv) {
      //
      // Found a file needs a FFS3 formatted file to store it, but it is in a non-FFS3 formatted FV.
      //
      DEBUG ((EFI_D_ERROR, "FFS3 formatted file can't be written in a non-FFS3 formatted FV.\n"));
      return EFI_INVALID_PARAMETER;
    }

    if (FileData[Index1].BufferSize == 0) {
      //
      // Here we will delete this file
      //
      Status = This->ReadFile (
                      This,
                      FileData[Index1].NameGuid,
                      NULL,
                      &Size,
                      &FileType,
                      &FileAttributes,
                      &AuthenticationStatus
                      );
      if (!EFI_ERROR (Status)) {
        NumDelete++;
      } else {
        return Status;
      }
    }

    if (FileData[Index1].Type == EFI_FV_FILETYPE_FFS_PAD) {
      //
      // According to PI spec, on EFI_FV_FILETYPE_FFS_PAD: 
      // "Standard firmware file system services will not return the handle of any pad files, 
      // nor will they permit explicit creation of such files."
      //
      return EFI_INVALID_PARAMETER;
    }
  }

  if ((NumDelete != NumberOfFiles) && (NumDelete != 0)) {
    //
    // A delete was request with a multiple file write
    //
    return EFI_INVALID_PARAMETER;
  }

  if (NumDelete == NumberOfFiles) {
    for (Index1 = 0; Index1 < NumberOfFiles; Index1++) {
      //
      // Delete Files
      //
      Status = FvDeleteFile (FvDevice, FileData[Index1].NameGuid);
      if (EFI_ERROR (Status)) {
        return Status;
      }
    }

    return EFI_SUCCESS;
  }

  for (Index1 = 0; Index1 < NumberOfFiles; Index1++) {
    Status = This->ReadFile (
                    This,
                    FileData[Index1].NameGuid,
                    NULL,
                    &Size,
                    &FileType,
                    &FileAttributes,
                    &AuthenticationStatus
                    );
    if (!EFI_ERROR (Status)) {
      CreateNewFile[Index1] = FALSE;
    } else if (Status == EFI_NOT_FOUND) {
      CreateNewFile[Index1] = TRUE;
    } else {
      return Status;
    }
    //
    // Checking alignment
    //
    if ((FileData[Index1].FileAttributes & EFI_FV_FILE_ATTRIB_ALIGNMENT) != 0) {
      UINT8 FFSAlignmentValue;
      UINT8 FvAlignmentValue;

      FFSAlignmentValue = (UINT8) (FileData[Index1].FileAttributes & EFI_FV_FILE_ATTRIB_ALIGNMENT);
      FvAlignmentValue = (UINT8) (((UINT32) (FvAttributes & EFI_FV2_ALIGNMENT)) >> 16);

      if (FFSAlignmentValue > FvAlignmentValue) {
        return EFI_INVALID_PARAMETER;
      }
    }
  }

  if ((WritePolicy != EFI_FV_RELIABLE_WRITE) && (WritePolicy != EFI_FV_UNRELIABLE_WRITE)) {
    return EFI_INVALID_PARAMETER;
  }
  //
  // Checking the reliable write is supported by FV
  //

  if ((WritePolicy == EFI_FV_RELIABLE_WRITE) && (NumberOfFiles > 1)) {
    //
    // Only for multiple files, reliable write is meaningful
    //
    Status = FvCreateMultipleFiles (
              FvDevice,
              NumberOfFiles,
              FileData,
              CreateNewFile
              );

    return Status;
  }

  for (Index1 = 0; Index1 < NumberOfFiles; Index1++) {
    //
    // Making Buffersize QWORD boundary, and add file tail.
    //
    HeaderSize = sizeof (EFI_FFS_FILE_HEADER);
    ActualSize = FileData[Index1].BufferSize + HeaderSize;
    if (ActualSize > 0x00FFFFFF) {
      HeaderSize = sizeof (EFI_FFS_FILE_HEADER2);
      ActualSize = FileData[Index1].BufferSize + HeaderSize;
    }
    BufferSize  = ActualSize;

    while ((BufferSize & 0x07) != 0) {
      BufferSize++;
    }

    FileBuffer = AllocateZeroPool (BufferSize);
    if (FileBuffer == NULL) {
      return Status;
    }
    //
    // Copy File Data into FileBuffer
    //
    CopyMem (
      FileBuffer + HeaderSize,
      FileData[Index1].Buffer,
      FileData[Index1].BufferSize
      );

    if (ErasePolarity == 1) {
      //
      // Fill the file header and padding byte with Erase Byte
      //
      for (Index2 = 0; Index2 < HeaderSize; Index2++) {
        FileBuffer[Index2] = (UINT8)~FileBuffer[Index2];
      }

      for (Index2 = ActualSize; Index2 < BufferSize; Index2++) {
        FileBuffer[Index2] = (UINT8)~FileBuffer[Index2];
      }
    }

    if (CreateNewFile[Index1]) {
      Status = FvCreateNewFile (
                FvDevice,
                FileBuffer,
                BufferSize,
                ActualSize,
                FileData[Index1].NameGuid,
                FileData[Index1].Type,
                FileData[Index1].FileAttributes
                );
    } else {
      Status = FvUpdateFile (
                FvDevice,
                FileBuffer,
                BufferSize,
                ActualSize,
                FileData[Index1].NameGuid,
                FileData[Index1].Type,
                FileData[Index1].FileAttributes
                );
    }

    FreePool (FileBuffer);

    if (EFI_ERROR (Status)) {
      return Status;
    }
  }

  return EFI_SUCCESS;
}
