+/** @file\r
+ Implements write firmware file.\r
+\r
+ Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions\r
+ of the BSD License which accompanies this distribution. The\r
+ full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "FwVolDriver.h"\r
+\r
+/**\r
+ Caculate the checksum for the FFS header.\r
+\r
+ @param FfsHeader FFS File Header which needs to caculate the checksum\r
+\r
+**/\r
+VOID\r
+SetHeaderChecksum (\r
+ IN EFI_FFS_FILE_HEADER *FfsHeader\r
+ )\r
+{\r
+ EFI_FFS_FILE_STATE State;\r
+ UINT8 HeaderChecksum;\r
+ UINT8 FileChecksum;\r
+\r
+ //\r
+ // The state and the File checksum are not included\r
+ //\r
+ State = FfsHeader->State;\r
+ FfsHeader->State = 0;\r
+\r
+ FileChecksum = FfsHeader->IntegrityCheck.Checksum.File;\r
+ FfsHeader->IntegrityCheck.Checksum.File = 0;\r
+\r
+ FfsHeader->IntegrityCheck.Checksum.Header = 0;\r
+\r
+ HeaderChecksum = CalculateSum8 (\r
+ (UINT8 *)FfsHeader,\r
+ sizeof (EFI_FFS_FILE_HEADER)\r
+ );\r
+\r
+ FfsHeader->IntegrityCheck.Checksum.Header = (UINT8) (~HeaderChecksum + 1);\r
+\r
+ FfsHeader->State = State;\r
+ FfsHeader->IntegrityCheck.Checksum.File = FileChecksum;\r
+\r
+ return ;\r
+}\r
+\r
+/**\r
+ Caculate the checksum for the FFS File.\r
+\r
+ @param FfsHeader FFS File Header which needs to caculate the checksum\r
+ @param ActualFileSize The whole Ffs File Length.\r
+\r
+**/\r
+VOID\r
+SetFileChecksum (\r
+ IN EFI_FFS_FILE_HEADER *FfsHeader,\r
+ IN UINTN ActualFileSize\r
+ )\r
+{\r
+ EFI_FFS_FILE_STATE State;\r
+ UINT8 FileChecksum;\r
+\r
+ if ((FfsHeader->Attributes & FFS_ATTRIB_CHECKSUM) != 0) {\r
+ //\r
+ // The file state is not included\r
+ //\r
+ State = FfsHeader->State;\r
+ FfsHeader->State = 0;\r
+\r
+ FfsHeader->IntegrityCheck.Checksum.File = 0;\r
+\r
+ //\r
+ // File checksum \r
+ //\r
+ FileChecksum = CalculateSum8 (\r
+ (UINT8 *)(FfsHeader + 1),\r
+ ActualFileSize - sizeof (EFI_FFS_FILE_HEADER)\r
+ );\r
+\r
+ FfsHeader->IntegrityCheck.Checksum.File = (UINT8) (~FileChecksum + 1);\r
+\r
+ FfsHeader->State = State;\r
+\r
+ } else {\r
+\r
+ FfsHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM;\r
+\r
+ }\r
+\r
+ return ;\r
+}\r
+\r
+/**\r
+ Get the alignment value from File Attributes.\r
+\r
+ @param FfsAttributes FFS attribute\r
+\r
+ @return Alignment value.\r
+\r
+**/\r
+UINTN\r
+GetRequiredAlignment (\r
+ IN EFI_FV_FILE_ATTRIBUTES FfsAttributes\r
+ )\r
+{\r
+ UINTN AlignmentValue;\r
+\r
+ AlignmentValue = FfsAttributes & EFI_FV_FILE_ATTRIB_ALIGNMENT;\r
+\r
+ if (AlignmentValue <= 3) {\r
+ return 0x08;\r
+ }\r
+\r
+ if (AlignmentValue > 16) {\r
+ //\r
+ // Anyway, we won't reach this code\r
+ //\r
+ return 0x08;\r
+ }\r
+\r
+ return (UINTN)1 << AlignmentValue;\r
+\r
+}\r
+\r
+/**\r
+ Caculate the leading Pad file size to meet the alignment requirement.\r
+\r
+ @param FvDevice Cached Firmware Volume.\r
+ @param StartAddress The starting address to write the FFS File.\r
+ @param RequiredAlignment FFS File Data alignment requirement.\r
+\r
+ @return The required Pad File Size.\r
+\r
+**/\r
+UINTN\r
+CaculatePadFileSize (\r
+ IN FV_DEVICE *FvDevice,\r
+ IN EFI_PHYSICAL_ADDRESS StartAddress,\r
+ IN UINTN RequiredAlignment\r
+ )\r
+{\r
+ UINTN DataStartPos;\r
+ UINTN RelativePos;\r
+ UINTN PadSize;\r
+\r
+ DataStartPos = (UINTN) StartAddress + sizeof (EFI_FFS_FILE_HEADER);\r
+ RelativePos = DataStartPos - (UINTN) FvDevice->CachedFv;\r
+\r
+ PadSize = 0;\r
+\r
+ while ((RelativePos & (RequiredAlignment - 1)) != 0) {\r
+ RelativePos++;\r
+ PadSize++;\r
+ }\r
+ //\r
+ // If padsize is 0, no pad file needed;\r
+ // If padsize is great than 24, then pad file can be created\r
+ //\r
+ if ((PadSize == 0) || (PadSize >= sizeof (EFI_FFS_FILE_HEADER))) {\r
+ return PadSize;\r
+ }\r
+\r
+ //\r
+ // Perhaps following method can save space\r
+ //\r
+ RelativePos = DataStartPos - (UINTN) FvDevice->CachedFv + sizeof (EFI_FFS_FILE_HEADER);\r
+ PadSize = sizeof (EFI_FFS_FILE_HEADER);\r
+\r
+ while ((RelativePos & (RequiredAlignment - 1)) != 0) {\r
+ RelativePos++;\r
+ PadSize++;\r
+ }\r
+\r
+ return PadSize;\r
+}\r
+\r
+/**\r
+ Convert EFI_FV_FILE_ATTRIBUTES to FFS_FILE_ATTRIBUTES.\r
+\r
+ @param FvFileAttrib The value of EFI_FV_FILE_ATTRIBUTES\r
+ @param FfsFileAttrib Pointer to the got FFS_FILE_ATTRIBUTES value.\r
+\r
+**/\r
+VOID\r
+FvFileAttrib2FfsFileAttrib (\r
+ IN EFI_FV_FILE_ATTRIBUTES FvFileAttrib,\r
+ OUT UINT8 *FfsFileAttrib\r
+ )\r
+{\r
+ UINT8 FvFileAlignment;\r
+ UINT8 FfsFileAlignment;\r
+\r
+ FvFileAlignment = (UINT8) (FvFileAttrib & EFI_FV_FILE_ATTRIB_ALIGNMENT);\r
+ FfsFileAlignment = 0;\r
+\r
+ switch (FvFileAlignment) {\r
+ case 0:\r
+ //\r
+ // fall through\r
+ //\r
+ case 1:\r
+ //\r
+ // fall through\r
+ //\r
+ case 2:\r
+ //\r
+ // fall through\r
+ //\r
+ case 3:\r
+ //\r
+ // fall through\r
+ //\r
+ FfsFileAlignment = 0;\r
+ break;\r
+\r
+ case 4:\r
+ //\r
+ // fall through\r
+ //\r
+ case 5:\r
+ //\r
+ // fall through\r
+ //\r
+ case 6:\r
+ //\r
+ // fall through\r
+ //\r
+ FfsFileAlignment = 1;\r
+ break;\r
+\r
+ case 7:\r
+ //\r
+ // fall through\r
+ //\r
+ case 8:\r
+ //\r
+ // fall through\r
+ //\r
+ FfsFileAlignment = 2;\r
+ break;\r
+\r
+ case 9:\r
+ FfsFileAlignment = 3;\r
+ break;\r
+\r
+ case 10:\r
+ //\r
+ // fall through\r
+ //\r
+ case 11:\r
+ //\r
+ // fall through\r
+ //\r
+ FfsFileAlignment = 4;\r
+ break;\r
+\r
+ case 12:\r
+ //\r
+ // fall through\r
+ //\r
+ case 13:\r
+ //\r
+ // fall through\r
+ //\r
+ case 14:\r
+ //\r
+ // fall through\r
+ //\r
+ FfsFileAlignment = 5;\r
+ break;\r
+\r
+ case 15:\r
+ FfsFileAlignment = 6;\r
+ break;\r
+\r
+ case 16:\r
+ FfsFileAlignment = 7;\r
+ break;\r
+ }\r
+\r
+ *FfsFileAttrib = (UINT8) (FfsFileAlignment << 3);\r
+\r
+ return ;\r
+}\r
+\r
+/**\r
+ Locate a free space entry that can hold this FFS file.\r
+\r
+ @param FvDevice Cached Firmware Volume.\r
+ @param Size The FFS file size.\r
+ @param RequiredAlignment FFS File Data alignment requirement.\r
+ @param PadSize Pointer to the size of leading Pad File.\r
+ @param FreeSpaceEntry Pointer to the Free Space Entry that meets the requirement.\r
+\r
+ @retval EFI_SUCCESS The free space entry is found.\r
+ @retval EFI_NOT_FOUND The free space entry can't be found.\r
+\r
+**/\r
+EFI_STATUS\r
+FvLocateFreeSpaceEntry (\r
+ IN FV_DEVICE *FvDevice,\r
+ IN UINTN Size,\r
+ IN UINTN RequiredAlignment,\r
+ OUT UINTN *PadSize,\r
+ OUT FREE_SPACE_ENTRY **FreeSpaceEntry\r
+ )\r
+{\r
+ FREE_SPACE_ENTRY *FreeSpaceListEntry;\r
+ LIST_ENTRY *Link;\r
+ UINTN PadFileSize;\r
+\r
+ Link = FvDevice->FreeSpaceHeader.ForwardLink;\r
+ FreeSpaceListEntry = (FREE_SPACE_ENTRY *) Link;\r
+\r
+ //\r
+ // Loop the free space entry list to find one that can hold the\r
+ // required the file size\r
+ //\r
+ while ((LIST_ENTRY *) FreeSpaceListEntry != &FvDevice->FreeSpaceHeader) {\r
+ PadFileSize = CaculatePadFileSize (\r
+ FvDevice,\r
+ (EFI_PHYSICAL_ADDRESS) (UINTN) FreeSpaceListEntry->StartingAddress,\r
+ RequiredAlignment\r
+ );\r
+ if (FreeSpaceListEntry->Length >= Size + PadFileSize) {\r
+ *FreeSpaceEntry = FreeSpaceListEntry;\r
+ *PadSize = PadFileSize;\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ FreeSpaceListEntry = (FREE_SPACE_ENTRY *) FreeSpaceListEntry->Link.ForwardLink;\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+\r
+}\r
+\r
+/**\r
+ Locate a free space that can hold this file.\r
+\r
+ @param FvDevice Cached Firmware Volume.\r
+ @param Size On input, it is the required size.\r
+ On output, it is the actual size of free space.\r
+ @param RequiredAlignment FFS File Data alignment requirement.\r
+ @param PadSize Pointer to the size of leading Pad File.\r
+ @param StartingAddress The starting address of the Free Space Entry\r
+ that meets the requirement.\r
+\r
+ @retval EFI_SUCCESS The free space is found.\r
+ @retval EFI_NOT_FOUND The free space can't be found.\r
+\r
+**/\r
+EFI_STATUS\r
+FvLocateFreeSpace (\r
+ IN FV_DEVICE *FvDevice,\r
+ IN OUT UINTN *Size,\r
+ IN UINTN RequiredAlignment,\r
+ OUT UINTN *PadSize,\r
+ OUT EFI_PHYSICAL_ADDRESS *StartingAddress\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ FREE_SPACE_ENTRY *FreeSpaceEntry;\r
+\r
+ //\r
+ // First find the free space entry\r
+ //\r
+ Status = FvLocateFreeSpaceEntry (\r
+ FvDevice,\r
+ *Size,\r
+ RequiredAlignment,\r
+ PadSize,\r
+ &FreeSpaceEntry\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ *Size = FreeSpaceEntry->Length;\r
+ *StartingAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) FreeSpaceEntry->StartingAddress;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Locate Pad File for writing, this is got from FV Cache.\r
+\r
+ @param FvDevice Cached Firmware Volume.\r
+ @param Size The required FFS file size.\r
+ @param RequiredAlignment FFS File Data alignment requirement.\r
+ @param PadSize Pointer to the size of leading Pad File.\r
+ @param PadFileEntry Pointer to the Pad File Entry that meets the requirement.\r
+\r
+ @retval EFI_SUCCESS The required pad file is found.\r
+ @retval EFI_NOT_FOUND The required pad file can't be found.\r
+\r
+**/\r
+EFI_STATUS\r
+FvLocatePadFile (\r
+ IN FV_DEVICE *FvDevice,\r
+ IN UINTN Size,\r
+ IN UINTN RequiredAlignment,\r
+ OUT UINTN *PadSize,\r
+ OUT FFS_FILE_LIST_ENTRY **PadFileEntry\r
+ )\r
+{\r
+ FFS_FILE_LIST_ENTRY *FileEntry;\r
+ EFI_FFS_FILE_STATE FileState;\r
+ EFI_FFS_FILE_HEADER *FileHeader;\r
+ UINTN FileLength;\r
+ UINTN PadAreaLength;\r
+ UINTN PadFileSize;\r
+\r
+ FileEntry = (FFS_FILE_LIST_ENTRY *) FvDevice->FfsFileListHeader.ForwardLink;\r
+\r
+ //\r
+ // travel through the whole file list to get the pad file entry\r
+ //\r
+ while (FileEntry != (FFS_FILE_LIST_ENTRY *) &FvDevice->FfsFileListHeader) {\r
+\r
+ FileHeader = (EFI_FFS_FILE_HEADER *) FileEntry->FfsHeader;\r
+ FileState = GetFileState (FvDevice->ErasePolarity, FileHeader);\r
+\r
+ if ((FileHeader->Type == EFI_FV_FILETYPE_FFS_PAD) && (FileState == EFI_FILE_DATA_VALID)) {\r
+ //\r
+ // we find one valid pad file, check its free area length\r
+ //\r
+ FileLength = *(UINT32 *) FileHeader->Size & 0x00FFFFFF;\r
+ PadAreaLength = FileLength - sizeof (EFI_FFS_FILE_HEADER);\r
+\r
+ PadFileSize = CaculatePadFileSize (\r
+ FvDevice,\r
+ (EFI_PHYSICAL_ADDRESS) (UINTN) FileHeader + sizeof (EFI_FFS_FILE_HEADER),\r
+ RequiredAlignment\r
+ );\r
+ if (PadAreaLength >= (Size + PadFileSize)) {\r
+ *PadSize = PadFileSize;\r
+ *PadFileEntry = FileEntry;\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ FileEntry = (FFS_FILE_LIST_ENTRY *) (FileEntry->Link.ForwardLink);\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+ Locate a suitable pad file for multiple file writing.\r
+\r
+ @param FvDevice Cached Firmware Volume.\r
+ @param NumOfFiles The number of Files that needed updating\r
+ @param BufferSize The array of each file size.\r
+ @param RequiredAlignment The array of of FFS File Data alignment requirement.\r
+ @param PadSize The array of size of each leading Pad File.\r
+ @param TotalSizeNeeded The totalsize that can hold these files.\r
+ @param PadFileEntry Pointer to the Pad File Entry that meets the requirement.\r
+\r
+ @retval EFI_SUCCESS The required pad file is found.\r
+ @retval EFI_NOT_FOUND The required pad file can't be found.\r
+\r
+**/\r
+EFI_STATUS\r
+FvSearchSuitablePadFile (\r
+ IN FV_DEVICE *FvDevice,\r
+ IN UINTN NumOfFiles,\r
+ IN UINTN *BufferSize,\r
+ IN UINTN *RequiredAlignment,\r
+ OUT UINTN *PadSize,\r
+ OUT UINTN *TotalSizeNeeded,\r
+ OUT FFS_FILE_LIST_ENTRY **PadFileEntry\r
+ )\r
+{\r
+ FFS_FILE_LIST_ENTRY *FileEntry;\r
+ EFI_FFS_FILE_STATE FileState;\r
+ EFI_FFS_FILE_HEADER *FileHeader;\r
+ UINTN FileLength;\r
+ UINTN PadAreaLength;\r
+ UINTN TotalSize;\r
+ UINTN Index;\r
+\r
+ FileEntry = (FFS_FILE_LIST_ENTRY *) FvDevice->FfsFileListHeader.ForwardLink;\r
+\r
+ //\r
+ // travel through the whole file list to get the pad file entry\r
+ //\r
+ while (FileEntry != (FFS_FILE_LIST_ENTRY *) &FvDevice->FfsFileListHeader) {\r
+\r
+ FileHeader = (EFI_FFS_FILE_HEADER *) FileEntry->FfsHeader;\r
+ FileState = GetFileState (FvDevice->ErasePolarity, FileHeader);\r
+\r
+ if ((FileHeader->Type == EFI_FV_FILETYPE_FFS_PAD) && (FileState == EFI_FILE_DATA_VALID)) {\r
+ //\r
+ // we find one valid pad file, check its length\r
+ //\r
+ FileLength = *(UINT32 *) FileHeader->Size & 0x00FFFFFF;\r
+ PadAreaLength = FileLength - sizeof (EFI_FFS_FILE_HEADER);\r
+ TotalSize = 0;\r
+\r
+ for (Index = 0; Index < NumOfFiles; Index++) {\r
+ PadSize[Index] = CaculatePadFileSize (\r
+ FvDevice,\r
+ (EFI_PHYSICAL_ADDRESS) (UINTN) FileHeader + sizeof (EFI_FFS_FILE_HEADER) + TotalSize,\r
+ RequiredAlignment[Index]\r
+ );\r
+ TotalSize += PadSize[Index];\r
+ TotalSize += BufferSize[Index];\r
+\r
+ if (TotalSize > PadAreaLength) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (PadAreaLength >= TotalSize) {\r
+ *PadFileEntry = FileEntry;\r
+ *TotalSizeNeeded = TotalSize;\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ FileEntry = (FFS_FILE_LIST_ENTRY *) (FileEntry->Link.ForwardLink);\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+ Locate a Free Space entry which can hold these files, including\r
+ meeting the alignment requirements.\r
+\r
+ @param FvDevice Cached Firmware Volume.\r
+ @param NumOfFiles The number of Files that needed updating\r
+ @param BufferSize The array of each file size.\r
+ @param RequiredAlignment The array of of FFS File Data alignment requirement.\r
+ @param PadSize The array of size of each leading Pad File.\r
+ @param TotalSizeNeeded The got total size that can hold these files.\r
+ @param FreeSpaceEntry The Free Space Entry that can hold these files.\r
+\r
+ @retval EFI_SUCCESS The free space entry is found.\r
+ @retval EFI_NOT_FOUND The free space entry can't be found.\r
+\r
+**/\r
+EFI_STATUS\r
+FvSearchSuitableFreeSpace (\r
+ IN FV_DEVICE *FvDevice,\r
+ IN UINTN NumOfFiles,\r
+ IN UINTN *BufferSize,\r
+ IN UINTN *RequiredAlignment,\r
+ OUT UINTN *PadSize,\r
+ OUT UINTN *TotalSizeNeeded,\r
+ OUT FREE_SPACE_ENTRY **FreeSpaceEntry\r
+ )\r
+{\r
+ FREE_SPACE_ENTRY *FreeSpaceListEntry;\r
+ LIST_ENTRY *Link;\r
+ UINTN TotalSize;\r
+ UINTN Index;\r
+ UINT8 *StartAddr;\r
+\r
+ Link = FvDevice->FreeSpaceHeader.ForwardLink;\r
+\r
+ FreeSpaceListEntry = (FREE_SPACE_ENTRY *) Link;\r
+\r
+ while ((LIST_ENTRY *) FreeSpaceListEntry != &FvDevice->FreeSpaceHeader) {\r
+ TotalSize = 0;\r
+ StartAddr = FreeSpaceListEntry->StartingAddress;\r
+\r
+ //\r
+ // Caculate the totalsize we need\r
+ //\r
+ for (Index = 0; Index < NumOfFiles; Index++) {\r
+ //\r
+ // Perhaps we don't need an EFI_FFS_FILE_HEADER, the first file\r
+ // have had its leading pad file.\r
+ //\r
+ PadSize[Index] = CaculatePadFileSize (\r
+ FvDevice,\r
+ (EFI_PHYSICAL_ADDRESS) (UINTN) StartAddr + TotalSize,\r
+ RequiredAlignment[Index]\r
+ );\r
+\r
+ TotalSize += PadSize[Index];\r
+ TotalSize += BufferSize[Index];\r
+\r
+ if (TotalSize > FreeSpaceListEntry->Length) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (FreeSpaceListEntry->Length >= TotalSize) {\r
+ *FreeSpaceEntry = FreeSpaceListEntry;\r
+ *TotalSizeNeeded = TotalSize;\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ FreeSpaceListEntry = (FREE_SPACE_ENTRY *) FreeSpaceListEntry->Link.ForwardLink;\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+ Calculate the length of the remaining space in FV.\r
+\r
+ @param FvDevice Cached Firmware Volume\r
+ @param Offset Current offset to FV base address.\r
+ @param Lba LBA number for the current offset.\r
+ @param LOffset Offset in block for the current offset.\r
+\r
+ @return the length of remaining space.\r
+\r
+**/\r
+UINTN\r
+CalculateRemainingLength (\r
+ IN FV_DEVICE *FvDevice,\r
+ IN UINTN Offset,\r
+ OUT EFI_LBA *Lba,\r
+ OUT UINTN *LOffset\r
+ )\r
+{\r
+ LIST_ENTRY *Link;\r
+ LBA_ENTRY *LbaEntry;\r
+ UINTN Count;\r
+\r
+ Count = 0;\r
+ *Lba = 0;\r
+ Link = FvDevice->LbaHeader.ForwardLink;\r
+ LbaEntry = (LBA_ENTRY *) Link;\r
+\r
+ while (&LbaEntry->Link != &FvDevice->LbaHeader) {\r
+ if (Count > Offset) {\r
+ break;\r
+ }\r
+\r
+ Count += LbaEntry->BlockLength;\r
+ (*Lba)++;\r
+ Link = LbaEntry->Link.ForwardLink;\r
+ LbaEntry = (LBA_ENTRY *) Link;\r
+ }\r
+\r
+ if (Count <= Offset) {\r
+ return 0;\r
+ }\r
+\r
+ Link = LbaEntry->Link.BackLink;\r
+ LbaEntry = (LBA_ENTRY *) Link;\r
+\r
+ (*Lba)--;\r
+ *LOffset = (UINTN) (LbaEntry->BlockLength - (Count - Offset));\r
+\r
+ Count = 0;\r
+ while (&LbaEntry->Link != &FvDevice->LbaHeader) {\r
+\r
+ Count += LbaEntry->BlockLength;\r
+\r
+ Link = LbaEntry->Link.ForwardLink;\r
+ LbaEntry = (LBA_ENTRY *) Link;\r
+ }\r
+\r
+ Count -= *LOffset;\r
+\r
+ return Count;\r
+}\r
+\r
+/**\r
+ Writes data beginning at Lba:Offset from FV. The write terminates either\r
+ when *NumBytes of data have been written, or when the firmware end is\r
+ reached. *NumBytes is updated to reflect the actual number of bytes\r
+ written.\r
+\r
+ @param FvDevice Cached Firmware Volume\r
+ @param Offset Offset in the block at which to begin write\r
+ @param NumBytes At input, indicates the requested write size.\r
+ At output, indicates the actual number of bytes written.\r
+ @param Buffer Buffer containing source data for the write.\r
+\r
+ @retval EFI_SUCCESS Data is successfully written into FV.\r
+ @return error Data is failed written.\r
+\r
+**/\r
+EFI_STATUS\r
+FvcWrite (\r
+ IN FV_DEVICE *FvDevice,\r
+ IN UINTN Offset,\r
+ IN OUT UINTN *NumBytes,\r
+ IN UINT8 *Buffer\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;\r
+ EFI_LBA Lba;\r
+ UINTN LOffset;\r
+ EFI_FVB_ATTRIBUTES_2 FvbAttributes;\r
+ UINTN RemainingLength;\r
+ UINTN WriteLength;\r
+ UINT8 *TmpBuffer;\r
+ \r
+ LOffset = 0;\r
+ RemainingLength = CalculateRemainingLength (FvDevice, Offset, &Lba, &LOffset);\r
+ if ((UINTN) (*NumBytes) > RemainingLength) {\r
+ *NumBytes = (UINTN) RemainingLength;\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Fvb = FvDevice->Fvb;\r
+\r
+ Status = Fvb->GetAttributes (\r
+ Fvb,\r
+ &FvbAttributes\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if ((FvbAttributes & EFI_FV2_WRITE_STATUS) != 0) {\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ RemainingLength = *NumBytes;\r
+ WriteLength = RemainingLength;\r
+ TmpBuffer = Buffer;\r
+\r
+ do {\r
+ Status = Fvb->Write (\r
+ Fvb,\r
+ Lba,\r
+ LOffset,\r
+ &WriteLength,\r
+ TmpBuffer\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+\r
+ if (Status == EFI_BAD_BUFFER_SIZE) {\r
+ Lba++;\r
+ LOffset = 0;\r
+ TmpBuffer += WriteLength;\r
+ RemainingLength -= WriteLength;\r
+ WriteLength = (UINTN) RemainingLength;\r
+\r
+ continue;\r
+ } else {\r
+ return Status;\r
+ }\r
+ } while (1);\r
+\r
+Done:\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Create a new FFS file into Firmware Volume device.\r
+\r
+ @param FvDevice Cached Firmware Volume.\r
+ @param FfsFileBuffer A buffer that holds an FFS file,(it contains\r
+ a File Header which is in init state).\r
+ @param BufferSize The size of FfsFileBuffer.\r
+ @param ActualFileSize The actual file length, it may not be multiples of 8.\r
+ @param FileName The FFS File Name.\r
+ @param FileType The FFS File Type.\r
+ @param FileAttributes The Attributes of the FFS File to be created.\r
+\r
+ @retval EFI_SUCCESS FFS fle is added into FV.\r
+ @retval EFI_INVALID_PARAMETER File type is not valid.\r
+ @retval EFI_DEVICE_ERROR FV doesn't set writable attribute.\r
+ @retval EFI_NOT_FOUND FV has no enough space for the added file.\r
+\r
+**/\r
+EFI_STATUS\r
+FvCreateNewFile (\r
+ IN FV_DEVICE *FvDevice,\r
+ IN UINT8 *FfsFileBuffer,\r
+ IN UINTN BufferSize,\r
+ IN UINTN ActualFileSize,\r
+ IN EFI_GUID *FileName,\r
+ IN EFI_FV_FILETYPE FileType,\r
+ IN EFI_FV_FILE_ATTRIBUTES FileAttributes\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_FFS_FILE_HEADER *FileHeader;\r
+ EFI_PHYSICAL_ADDRESS BufferPtr;\r
+ UINTN Offset;\r
+ UINTN NumBytesWritten;\r
+ UINTN StateOffset;\r
+ FREE_SPACE_ENTRY *FreeSpaceEntry;\r
+ UINTN RequiredAlignment;\r
+ UINTN PadFileSize;\r
+ FFS_FILE_LIST_ENTRY *PadFileEntry;\r
+ EFI_FFS_FILE_ATTRIBUTES TmpFileAttribute;\r
+ FFS_FILE_LIST_ENTRY *FfsFileEntry;\r
+\r
+ //\r
+ // File Type: 0x0E~0xE0 are reserved\r
+ //\r
+ if ((FileType > EFI_FV_FILETYPE_SMM_CORE) && (FileType < 0xE0)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // First find a free space that can hold this image.\r
+ // Check alignment, FFS at least must be aligned at 8-byte boundry\r
+ //\r
+ RequiredAlignment = GetRequiredAlignment (FileAttributes);\r
+\r
+ Status = FvLocateFreeSpaceEntry (\r
+ FvDevice,\r
+ BufferSize,\r
+ RequiredAlignment,\r
+ &PadFileSize,\r
+ &FreeSpaceEntry\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Maybe we need to find a PAD file that can hold this image\r
+ //\r
+ Status = FvCreateNewFileInsidePadFile (\r
+ FvDevice,\r
+ FfsFileBuffer,\r
+ BufferSize,\r
+ ActualFileSize,\r
+ FileName,\r
+ FileType,\r
+ FileAttributes\r
+ );\r
+\r
+ return Status;\r
+ }\r
+\r
+ BufferPtr = (EFI_PHYSICAL_ADDRESS) (UINTN) FreeSpaceEntry->StartingAddress;\r
+\r
+ //\r
+ // If we need a leading PAD File, create it first.\r
+ //\r
+ if (PadFileSize != 0) {\r
+ Status = FvCreatePadFileInFreeSpace (\r
+ FvDevice,\r
+ FreeSpaceEntry,\r
+ PadFileSize - sizeof (EFI_FFS_FILE_HEADER),\r
+ &PadFileEntry\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+ //\r
+ // Maybe we create a pad file, so re-get the free space starting address\r
+ // and length\r
+ //\r
+ BufferPtr = (EFI_PHYSICAL_ADDRESS) (UINTN) FreeSpaceEntry->StartingAddress;\r
+\r
+ //\r
+ // File creation step 1: Allocate File Header,\r
+ // Mark EFI_FILE_HEADER_CONSTRUCTION bit to TRUE,\r
+ // Write Name, IntegrityCheck.Header, Type, Attributes, and Size\r
+ //\r
+ FileHeader = (EFI_FFS_FILE_HEADER *) FfsFileBuffer;\r
+ SetFileState (EFI_FILE_HEADER_CONSTRUCTION, FileHeader);\r
+\r
+ Offset = (UINTN) (BufferPtr - FvDevice->CachedFv);\r
+ StateOffset = Offset + (UINT8 *) &FileHeader->State - (UINT8 *) FileHeader;\r
+\r
+ NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);\r
+ Status = FvcWrite (\r
+ FvDevice,\r
+ StateOffset,\r
+ &NumBytesWritten,\r
+ &FileHeader->State\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ //\r
+ // update header 2 cache\r
+ //\r
+ CopyMem (\r
+ (UINT8 *) (UINTN) BufferPtr,\r
+ FileHeader,\r
+ sizeof (EFI_FFS_FILE_HEADER)\r
+ );\r
+\r
+ //\r
+ // update Free Space Entry, now need to substract the EFI_FFS_FILE_HEADER\r
+ //\r
+ FreeSpaceEntry->StartingAddress += sizeof (EFI_FFS_FILE_HEADER);\r
+ FreeSpaceEntry->Length -= sizeof (EFI_FFS_FILE_HEADER);\r
+\r
+ CopyGuid (&FileHeader->Name, FileName);\r
+ FileHeader->Type = FileType;\r
+\r
+ //\r
+ // Convert FvFileAttribute to FfsFileAttributes\r
+ //\r
+ FvFileAttrib2FfsFileAttrib (FileAttributes, &TmpFileAttribute);\r
+\r
+ FileHeader->Attributes = TmpFileAttribute;\r
+\r
+ //\r
+ // File size is including the FFS File Header.\r
+ //\r
+ *(UINT32 *) FileHeader->Size &= 0xFF000000;\r
+ *(UINT32 *) FileHeader->Size |= ActualFileSize;\r
+\r
+ SetHeaderChecksum (FileHeader);\r
+\r
+ Offset = (UINTN) (BufferPtr - FvDevice->CachedFv);\r
+\r
+ NumBytesWritten = sizeof (EFI_FFS_FILE_HEADER);\r
+ Status = FvcWrite (\r
+ FvDevice,\r
+ Offset,\r
+ &NumBytesWritten,\r
+ (UINT8 *) FileHeader\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ //\r
+ // update header 2 cache\r
+ //\r
+ CopyMem (\r
+ (UINT8 *) (UINTN) BufferPtr,\r
+ FileHeader,\r
+ sizeof (EFI_FFS_FILE_HEADER)\r
+ );\r
+\r
+ //\r
+ // end of step 1\r
+ //\r
+ // File creation step 2:\r
+ // MARK EFI_FILE_HEADER_VALID bit to TRUE,\r
+ // Write IntegrityCheck.File, File Data\r
+ //\r
+ SetFileState (EFI_FILE_HEADER_VALID, FileHeader);\r
+\r
+ Offset = (UINTN) (BufferPtr - FvDevice->CachedFv);\r
+ StateOffset = Offset + (UINT8 *) &FileHeader->State - (UINT8 *) FileHeader;\r
+\r
+ NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);\r
+ Status = FvcWrite (\r
+ FvDevice,\r
+ StateOffset,\r
+ &NumBytesWritten,\r
+ &FileHeader->State\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ //\r
+ // update header 2 cache\r
+ //\r
+ CopyMem (\r
+ (UINT8 *) (UINTN) BufferPtr,\r
+ FileHeader,\r
+ sizeof (EFI_FFS_FILE_HEADER)\r
+ );\r
+\r
+ //\r
+ // update Free Space Entry, now need to substract the file data length\r
+ //\r
+ FreeSpaceEntry->StartingAddress += (BufferSize - sizeof (EFI_FFS_FILE_HEADER));\r
+ FreeSpaceEntry->Length -= (BufferSize - sizeof (EFI_FFS_FILE_HEADER));\r
+\r
+ //\r
+ // Caculate File Checksum\r
+ //\r
+ SetFileChecksum (FileHeader, ActualFileSize);\r
+\r
+ Offset = (UINTN) (BufferPtr - FvDevice->CachedFv);\r
+\r
+ NumBytesWritten = BufferSize;\r
+ Status = FvcWrite (\r
+ FvDevice,\r
+ Offset,\r
+ &NumBytesWritten,\r
+ FfsFileBuffer\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ //\r
+ // each time write block successfully, write also to cache\r
+ //\r
+ CopyMem (\r
+ (UINT8 *) (UINTN) BufferPtr,\r
+ FfsFileBuffer,\r
+ NumBytesWritten\r
+ );\r
+\r
+ //\r
+ // Step 3: Mark EFI_FILE_DATA_VALID to TRUE\r
+ //\r
+ SetFileState (EFI_FILE_DATA_VALID, FileHeader);\r
+\r
+ Offset = (UINTN) (BufferPtr - FvDevice->CachedFv);\r
+ StateOffset = Offset + (UINT8 *) &FileHeader->State - (UINT8 *) FileHeader;\r
+\r
+ NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);\r
+ Status = FvcWrite (\r
+ FvDevice,\r
+ StateOffset,\r
+ &NumBytesWritten,\r
+ &FileHeader->State\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ //\r
+ // update header 2 cache\r
+ //\r
+ CopyMem (\r
+ (UINT8 *) (UINTN) BufferPtr,\r
+ FileHeader,\r
+ sizeof (EFI_FFS_FILE_HEADER)\r
+ );\r
+\r
+ //\r
+ // If successfully, insert an FfsFileEntry at the end of ffs file list\r
+ //\r
+\r
+ FfsFileEntry = AllocateZeroPool (sizeof (FFS_FILE_LIST_ENTRY));\r
+ ASSERT (FfsFileEntry != NULL);\r
+ FfsFileEntry->FfsHeader = (UINT8 *) (UINTN) BufferPtr;\r
+ InsertTailList (&FvDevice->FfsFileListHeader, &FfsFileEntry->Link);\r
+\r
+ //\r
+ // Set cache file to this file\r
+ //\r
+ FvDevice->CurrentFfsFile = FfsFileEntry;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Update a File, so after successful update, there are 2 files existing\r
+ in FV, one is marked for deleted, and another one is valid.\r
+\r
+ @param FvDevice Cached Firmware Volume.\r
+ @param FfsFileBuffer A buffer that holds an FFS file,(it contains\r
+ a File Header which is in init state).\r
+ @param BufferSize The size of FfsFileBuffer.\r
+ @param ActualFileSize The actual file length, it may not be multiples of 8.\r
+ @param FileName The FFS File Name.\r
+ @param NewFileType The FFS File Type.\r
+ @param NewFileAttributes The Attributes of the FFS File to be created.\r
+\r
+ @retval EFI_SUCCESS FFS fle is updated into FV.\r
+ @retval EFI_INVALID_PARAMETER File type is not valid.\r
+ @retval EFI_DEVICE_ERROR FV doesn't set writable attribute.\r
+ @retval EFI_NOT_FOUND FV has no enough space for the added file.\r
+ FFS with same file name is not found in FV.\r
+\r
+**/\r
+EFI_STATUS\r
+FvUpdateFile (\r
+ IN FV_DEVICE *FvDevice,\r
+ IN UINT8 *FfsFileBuffer,\r
+ IN UINTN BufferSize,\r
+ IN UINTN ActualFileSize,\r
+ IN EFI_GUID *FileName,\r
+ IN EFI_FV_FILETYPE NewFileType,\r
+ IN EFI_FV_FILE_ATTRIBUTES NewFileAttributes\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;\r
+ UINTN NumBytesWritten;\r
+ EFI_FV_FILETYPE OldFileType;\r
+ EFI_FV_FILE_ATTRIBUTES OldFileAttributes;\r
+ UINTN OldFileSize;\r
+ EFI_FFS_FILE_HEADER *OldFileHeader;\r
+ UINTN OldOffset;\r
+ UINTN OldStateOffset;\r
+ FFS_FILE_LIST_ENTRY *OldFfsFileEntry;\r
+ UINTN Key;\r
+ EFI_GUID FileNameGuid;\r
+\r
+ Fv = &FvDevice->Fv;\r
+\r
+ //\r
+ // Step 1, find old file,\r
+ // Mark EFI_FILE_MARKED_FOR_UPDATE to TRUE in the older header\r
+ //\r
+\r
+ //\r
+ // Check if the file was read last time.\r
+ //\r
+ OldFileHeader = NULL;\r
+ OldFfsFileEntry = FvDevice->CurrentFfsFile;\r
+\r
+ if (OldFfsFileEntry != NULL) {\r
+ OldFileHeader = (EFI_FFS_FILE_HEADER *) OldFfsFileEntry->FfsHeader;\r
+ }\r
+\r
+ if ((OldFfsFileEntry == NULL) || (!CompareGuid (&OldFileHeader->Name, FileName))) {\r
+ Key = 0;\r
+ do {\r
+ OldFileType = 0;\r
+ Status = Fv->GetNextFile (\r
+ Fv,\r
+ &Key,\r
+ &OldFileType,\r
+ &FileNameGuid,\r
+ &OldFileAttributes,\r
+ &OldFileSize\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ } while (!CompareGuid (&FileNameGuid, FileName));\r
+\r
+ //\r
+ // Get FfsFileEntry from the search key\r
+ //\r
+ OldFfsFileEntry = (FFS_FILE_LIST_ENTRY *) Key;\r
+\r
+ //\r
+ // Double check file state before being ready to be removed\r
+ //\r
+ OldFileHeader = (EFI_FFS_FILE_HEADER *) OldFfsFileEntry->FfsHeader;\r
+ } else {\r
+ //\r
+ // Mark the cache file to invalid\r
+ //\r
+ FvDevice->CurrentFfsFile = NULL;\r
+ }\r
+ //\r
+ // Update File: Mark EFI_FILE_MARKED_FOR_UPDATE to TRUE\r
+ //\r
+ SetFileState (EFI_FILE_MARKED_FOR_UPDATE, OldFileHeader);\r
+\r
+ OldOffset = (UINTN) ((EFI_PHYSICAL_ADDRESS) (UINTN) OldFileHeader - FvDevice->CachedFv);\r
+ OldStateOffset = OldOffset + (UINT8 *) &OldFileHeader->State - (UINT8 *) OldFileHeader;\r
+\r
+ NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);\r
+ Status = FvcWrite (\r
+ FvDevice,\r
+ OldStateOffset,\r
+ &NumBytesWritten,\r
+ &OldFileHeader->State\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // if failed, write the bit back in the cache, its XOR operation.\r
+ //\r
+ SetFileState (EFI_FILE_MARKED_FOR_UPDATE, OldFileHeader);\r
+\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Step 2, Create New Files\r
+ //\r
+ Status = FvCreateNewFile (\r
+ FvDevice,\r
+ FfsFileBuffer,\r
+ BufferSize,\r
+ ActualFileSize,\r
+ FileName,\r
+ NewFileType,\r
+ NewFileAttributes\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // If successfully, remove this file entry,\r
+ // although delete file may fail.\r
+ //\r
+ (OldFfsFileEntry->Link.BackLink)->ForwardLink = OldFfsFileEntry->Link.ForwardLink;\r
+ (OldFfsFileEntry->Link.ForwardLink)->BackLink = OldFfsFileEntry->Link.BackLink;\r
+ FreePool (OldFfsFileEntry);\r
+\r
+ //\r
+ // Step 3: Delete old files,\r
+ // by marking EFI_FILE_DELETED to TRUE\r
+ //\r
+ SetFileState (EFI_FILE_DELETED, OldFileHeader);\r
+\r
+ OldOffset = (UINTN) ((EFI_PHYSICAL_ADDRESS) (UINTN) OldFileHeader - FvDevice->CachedFv);\r
+ OldStateOffset = OldOffset + (UINT8 *) &OldFileHeader->State - (UINT8 *) OldFileHeader;\r
+\r
+ NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);\r
+ Status = FvcWrite (\r
+ FvDevice,\r
+ OldStateOffset,\r
+ &NumBytesWritten,\r
+ &OldFileHeader->State\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // if failed, write the bit back in the cache, its XOR operation.\r
+ //\r
+ SetFileState (EFI_FILE_DELETED, OldFileHeader);\r
+\r
+ return Status;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Deleted a given file from FV device.\r
+\r
+ @param FvDevice Cached Firmware Volume.\r
+ @param NameGuid The FFS File Name.\r
+\r
+ @retval EFI_SUCCESS FFS file with the specified FFS name is removed.\r
+ @retval EFI_NOT_FOUND FFS file with the specified FFS name is not found.\r
+\r
+**/\r
+EFI_STATUS\r
+FvDeleteFile (\r
+ IN FV_DEVICE *FvDevice,\r
+ IN EFI_GUID *NameGuid\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN Key;\r
+ EFI_GUID FileNameGuid;\r
+ EFI_FV_FILETYPE FileType;\r
+ EFI_FV_FILE_ATTRIBUTES FileAttributes;\r
+ UINTN FileSize;\r
+ EFI_FFS_FILE_HEADER *FileHeader;\r
+ FFS_FILE_LIST_ENTRY *FfsFileEntry;\r
+ EFI_FFS_FILE_STATE FileState;\r
+ EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;\r
+ UINTN Offset;\r
+ UINTN StateOffset;\r
+ UINTN NumBytesWritten;\r
+\r
+ Fv = &FvDevice->Fv;\r
+\r
+ //\r
+ // Check if the file was read last time.\r
+ //\r
+ FileHeader = NULL;\r
+ FfsFileEntry = FvDevice->CurrentFfsFile;\r
+\r
+ if (FfsFileEntry != NULL) {\r
+ FileHeader = (EFI_FFS_FILE_HEADER *) FfsFileEntry->FfsHeader;\r
+ }\r
+\r
+ if ((FfsFileEntry == NULL) || (!CompareGuid (&FileHeader->Name, NameGuid))) {\r
+ //\r
+ // Next search for the file using GetNextFile\r
+ //\r
+ Key = 0;\r
+ do {\r
+ FileType = 0;\r
+ Status = Fv->GetNextFile (\r
+ Fv,\r
+ &Key,\r
+ &FileType,\r
+ &FileNameGuid,\r
+ &FileAttributes,\r
+ &FileSize\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ } while (!CompareGuid (&FileNameGuid, NameGuid));\r
+\r
+ //\r
+ // Get FfsFileEntry from the search key\r
+ //\r
+ FfsFileEntry = (FFS_FILE_LIST_ENTRY *) Key;\r
+\r
+ //\r
+ // Double check file state before being ready to be removed\r
+ //\r
+ FileHeader = (EFI_FFS_FILE_HEADER *) FfsFileEntry->FfsHeader;\r
+ } else {\r
+ //\r
+ // Mark the cache file to NULL\r
+ //\r
+ FvDevice->CurrentFfsFile = NULL;\r
+ }\r
+\r
+ FileState = GetFileState (FvDevice->ErasePolarity, FileHeader);\r
+\r
+ if (FileState == EFI_FILE_HEADER_INVALID) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ if (FileState == EFI_FILE_DELETED) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ //\r
+ // Delete File: Mark EFI_FILE_DELETED to TRUE\r
+ //\r
+ SetFileState (EFI_FILE_DELETED, FileHeader);\r
+\r
+ Offset = (UINTN) ((EFI_PHYSICAL_ADDRESS) (UINTN) FileHeader - FvDevice->CachedFv);\r
+ StateOffset = Offset + (UINT8 *) &FileHeader->State - (UINT8 *) FileHeader;\r
+\r
+ NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);\r
+ Status = FvcWrite (\r
+ FvDevice,\r
+ StateOffset,\r
+ &NumBytesWritten,\r
+ &FileHeader->State\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // if failed, write the bit back in the cache, its XOR operation.\r
+ //\r
+ SetFileState (EFI_FILE_DELETED, FileHeader);\r
+\r
+ return Status;\r
+ }\r
+ //\r
+ // If successfully, remove this file entry\r
+ //\r
+ FvDevice->CurrentFfsFile = NULL;\r
+\r
+ (FfsFileEntry->Link.BackLink)->ForwardLink = FfsFileEntry->Link.ForwardLink;\r
+ (FfsFileEntry->Link.ForwardLink)->BackLink = FfsFileEntry->Link.BackLink;\r
+ FreePool (FfsFileEntry);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Writes one or more files to the firmware volume.\r
+\r
+ @param This Indicates the calling context.\r
+ @param NumberOfFiles Number of files.\r
+ @param WritePolicy WritePolicy indicates the level of reliability\r
+ for the write in the event of a power failure or\r
+ other system failure during the write operation.\r
+ @param FileData FileData is an pointer to an array of\r
+ EFI_FV_WRITE_DATA. Each element of array\r
+ FileData represents a file to be written.\r
+\r
+ @retval EFI_SUCCESS Files successfully written to firmware volume\r
+ @retval EFI_OUT_OF_RESOURCES Not enough buffer to be allocated.\r
+ @retval EFI_DEVICE_ERROR Device error.\r
+ @retval EFI_WRITE_PROTECTED Write protected.\r
+ @retval EFI_NOT_FOUND Not found.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+ @retval EFI_UNSUPPORTED This function not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FvWriteFile (\r
+ IN CONST EFI_FIRMWARE_VOLUME2_PROTOCOL *This,\r
+ IN UINT32 NumberOfFiles,\r
+ IN EFI_FV_WRITE_POLICY WritePolicy,\r
+ IN EFI_FV_WRITE_FILE_DATA *FileData\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN Index1;\r
+ UINTN Index2;\r
+ UINT8 *FileBuffer;\r
+ UINTN BufferSize;\r
+ UINTN ActualSize;\r
+ UINT8 ErasePolarity;\r
+ FV_DEVICE *FvDevice;\r
+ EFI_FV_FILETYPE FileType;\r
+ EFI_FV_FILE_ATTRIBUTES FileAttributes;\r
+ UINTN Size;\r
+ BOOLEAN CreateNewFile[MAX_FILES];\r
+ UINTN NumDelete;\r
+ EFI_FV_ATTRIBUTES FvAttributes;\r
+ UINT32 AuthenticationStatus;\r
+\r
+ if (NumberOfFiles > MAX_FILES) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ SetMem (CreateNewFile, NumberOfFiles, TRUE);\r
+\r
+ FvDevice = FV_DEVICE_FROM_THIS (This);\r
+\r
+ //\r
+ // First check the volume attributes.\r
+ //\r
+ Status = This->GetVolumeAttributes (\r
+ This,\r
+ &FvAttributes\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ //\r
+ // Can we have write right?\r
+ //\r
+ if ((FvAttributes & EFI_FV2_WRITE_STATUS) != 0) {\r
+ return EFI_WRITE_PROTECTED;\r
+ }\r
+\r
+ ErasePolarity = FvDevice->ErasePolarity;\r
+\r
+ //\r
+ // Loop for all files\r
+ //\r
+ NumDelete = 0;\r
+ for (Index1 = 0; Index1 < NumberOfFiles; Index1++) {\r
+ if (FileData[Index1].BufferSize == 0) {\r
+ //\r
+ // Here we will delete this file\r
+ //\r
+ Status = This->ReadFile (\r
+ This,\r
+ FileData[Index1].NameGuid,\r
+ NULL,\r
+ &Size,\r
+ &FileType,\r
+ &FileAttributes,\r
+ &AuthenticationStatus\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ NumDelete++;\r
+ } else {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ if (FileData[Index1].Type == EFI_FV_FILETYPE_FFS_PAD) {\r
+ //\r
+ // According to PI spec, on EFI_FV_FILETYPE_FFS_PAD: \r
+ // "Standard firmware file system services will not return the handle of any pad files, \r
+ // nor will they permit explicit creation of such files."\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+\r
+ if ((NumDelete != NumberOfFiles) && (NumDelete != 0)) {\r
+ //\r
+ // A delete was request with a multiple file write\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (NumDelete == NumberOfFiles) {\r
+ for (Index1 = 0; Index1 < NumberOfFiles; Index1++) {\r
+ //\r
+ // Delete Files\r
+ //\r
+ Status = FvDeleteFile (FvDevice, FileData[Index1].NameGuid);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ for (Index1 = 0; Index1 < NumberOfFiles; Index1++) {\r
+ Status = This->ReadFile (\r
+ This,\r
+ FileData[Index1].NameGuid,\r
+ NULL,\r
+ &Size,\r
+ &FileType,\r
+ &FileAttributes,\r
+ &AuthenticationStatus\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ CreateNewFile[Index1] = FALSE;\r
+ } else if (Status == EFI_NOT_FOUND) {\r
+ CreateNewFile[Index1] = TRUE;\r
+ } else {\r
+ return Status;\r
+ }\r
+ //\r
+ // Checking alignment\r
+ //\r
+ if ((FileData[Index1].FileAttributes & EFI_FV_FILE_ATTRIB_ALIGNMENT) != 0) {\r
+ UINT8 FFSAlignmentValue;\r
+ UINT8 FvAlignmentValue;\r
+\r
+ FFSAlignmentValue = (UINT8) (FileData[Index1].FileAttributes & EFI_FV_FILE_ATTRIB_ALIGNMENT);\r
+ FvAlignmentValue = (UINT8) (((UINT32) (FvAttributes & EFI_FV2_ALIGNMENT)) >> 16);\r
+\r
+ if (FFSAlignmentValue > FvAlignmentValue) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+ }\r
+\r
+ if ((WritePolicy != EFI_FV_RELIABLE_WRITE) && (WritePolicy != EFI_FV_UNRELIABLE_WRITE)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ //\r
+ // Checking the reliable write is supported by FV\r
+ //\r
+\r
+ if ((WritePolicy == EFI_FV_RELIABLE_WRITE) && (NumberOfFiles > 1)) {\r
+ //\r
+ // Only for multiple files, reliable write is meaningful\r
+ //\r
+ Status = FvCreateMultipleFiles (\r
+ FvDevice,\r
+ NumberOfFiles,\r
+ FileData,\r
+ CreateNewFile\r
+ );\r
+\r
+ return Status;\r
+ }\r
+\r
+ for (Index1 = 0; Index1 < NumberOfFiles; Index1++) {\r
+ //\r
+ // Making Buffersize QWORD boundry, and add file tail.\r
+ //\r
+ ActualSize = FileData[Index1].BufferSize + sizeof (EFI_FFS_FILE_HEADER);\r
+ BufferSize = ActualSize;\r
+\r
+ while ((BufferSize & 0x07) != 0) {\r
+ BufferSize++;\r
+ }\r
+\r
+ FileBuffer = AllocateZeroPool (BufferSize);\r
+ if (FileBuffer == NULL) {\r
+ return Status;\r
+ }\r
+ //\r
+ // Copy File Data into FileBuffer\r
+ //\r
+ CopyMem (\r
+ FileBuffer + sizeof (EFI_FFS_FILE_HEADER),\r
+ FileData[Index1].Buffer,\r
+ FileData[Index1].BufferSize\r
+ );\r
+\r
+ if (ErasePolarity == 1) {\r
+ //\r
+ // Fill the file header and padding byte with Erase Byte\r
+ //\r
+ for (Index2 = 0; Index2 < sizeof (EFI_FFS_FILE_HEADER); Index2++) {\r
+ FileBuffer[Index2] = (UINT8)~FileBuffer[Index2];\r
+ }\r
+\r
+ for (Index2 = ActualSize; Index2 < BufferSize; Index2++) {\r
+ FileBuffer[Index2] = (UINT8)~FileBuffer[Index2];\r
+ }\r
+ }\r
+\r
+ if (CreateNewFile[Index1]) {\r
+ Status = FvCreateNewFile (\r
+ FvDevice,\r
+ FileBuffer,\r
+ BufferSize,\r
+ ActualSize,\r
+ FileData[Index1].NameGuid,\r
+ FileData[Index1].Type,\r
+ FileData[Index1].FileAttributes\r
+ );\r
+ } else {\r
+ Status = FvUpdateFile (\r
+ FvDevice,\r
+ FileBuffer,\r
+ BufferSize,\r
+ ActualSize,\r
+ FileData[Index1].NameGuid,\r
+ FileData[Index1].Type,\r
+ FileData[Index1].FileAttributes\r
+ );\r
+ }\r
+\r
+ FreePool (FileBuffer);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r