Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Shumin Qiu <shumin.qiu@intel.com> Reviewed-by: Jeff Fan <jeff.fan@intel.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@17044 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			1587 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1587 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   Implements write firmware file.
 | |
| 
 | |
|   Copyright (c) 2006 - 2015, 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;
 | |
| 
 | |
|   FvFileAlignment   = (UINT8) (FvFileAttrib & EFI_FV_FILE_ATTRIB_ALIGNMENT);
 | |
|   FfsFileAlignment  = 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;
 | |
|   }
 | |
| 
 | |
|   *FfsFileAttrib = (UINT8) (FfsFileAlignment << 3);
 | |
| 
 | |
|   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 boundry
 | |
|   //
 | |
|   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 boundry, 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;
 | |
| }
 |