summaryrefslogtreecommitdiff
path: root/IntelFrameworkModulePkg/Universal/FirmwareVolume/FwVolDxe/FwPadFile.c
diff options
context:
space:
mode:
Diffstat (limited to 'IntelFrameworkModulePkg/Universal/FirmwareVolume/FwVolDxe/FwPadFile.c')
-rw-r--r--IntelFrameworkModulePkg/Universal/FirmwareVolume/FwVolDxe/FwPadFile.c1032
1 files changed, 1032 insertions, 0 deletions
diff --git a/IntelFrameworkModulePkg/Universal/FirmwareVolume/FwVolDxe/FwPadFile.c b/IntelFrameworkModulePkg/Universal/FirmwareVolume/FwVolDxe/FwPadFile.c
new file mode 100644
index 0000000000..14b8563143
--- /dev/null
+++ b/IntelFrameworkModulePkg/Universal/FirmwareVolume/FwVolDxe/FwPadFile.c
@@ -0,0 +1,1032 @@
+/** @file
+ Implements functions to pad firmware file.
+
+ Copyright (c) 2006 - 2011, 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 a PAD file.
+
+ @param PadFileHeader The Pad File to be caculeted the checksum.
+
+**/
+VOID
+SetPadFileChecksum (
+ IN EFI_FFS_FILE_HEADER *PadFileHeader
+ )
+{
+ UINT32 PadFileLength;
+
+ if ((PadFileHeader->Attributes & FFS_ATTRIB_CHECKSUM) != 0) {
+
+ PadFileLength = *(UINT32 *) PadFileHeader->Size & 0x00FFFFFF;
+
+ //
+ // Calculate checksum of Pad File Data
+ //
+ PadFileHeader->IntegrityCheck.Checksum.File =
+ CalculateCheckSum8 ((UINT8 *) PadFileHeader + sizeof (EFI_FFS_FILE_HEADER), PadFileLength - sizeof (EFI_FFS_FILE_HEADER));
+
+ } else {
+
+ PadFileHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM;
+
+ }
+
+ return ;
+}
+
+/**
+ Create a PAD File in the Free Space.
+
+ @param FvDevice Firmware Volume Device.
+ @param FreeSpaceEntry Indicating in which Free Space(Cache) the Pad file will be inserted.
+ @param Size Pad file Size, not include the header.
+ @param PadFileEntry The Ffs File Entry that points to this Pad File.
+
+ @retval EFI_SUCCESS Successfully create a PAD file.
+ @retval EFI_OUT_OF_RESOURCES No enough free space to create a PAD file.
+ @retval EFI_INVALID_PARAMETER Size is not 8 byte alignment.
+ @retval EFI_DEVICE_ERROR Free space is not erased.
+**/
+EFI_STATUS
+FvCreatePadFileInFreeSpace (
+ IN FV_DEVICE *FvDevice,
+ IN FREE_SPACE_ENTRY *FreeSpaceEntry,
+ IN UINTN Size,
+ OUT FFS_FILE_LIST_ENTRY **PadFileEntry
+ )
+{
+ EFI_STATUS Status;
+ EFI_FFS_FILE_HEADER *PadFileHeader;
+ UINTN Offset;
+ UINTN NumBytesWritten;
+ UINTN StateOffset;
+ UINT8 *StartPos;
+ FFS_FILE_LIST_ENTRY *FfsFileEntry;
+
+ if (FreeSpaceEntry->Length < Size + sizeof (EFI_FFS_FILE_HEADER)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if ((Size & 0x07) != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ StartPos = FreeSpaceEntry->StartingAddress;
+
+ //
+ // First double check the space
+ //
+ if (!IsBufferErased (
+ FvDevice->ErasePolarity,
+ StartPos,
+ Size + sizeof (EFI_FFS_FILE_HEADER)
+ )) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ PadFileHeader = (EFI_FFS_FILE_HEADER *) StartPos;
+
+ //
+ // Create File Step 1
+ //
+ SetFileState (EFI_FILE_HEADER_CONSTRUCTION, PadFileHeader);
+
+ Offset = (UINTN) (StartPos - FvDevice->CachedFv);
+ StateOffset = Offset + (UINT8 *) &PadFileHeader->State - (UINT8 *) PadFileHeader;
+
+ NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);
+ Status = FvcWrite (
+ FvDevice,
+ StateOffset,
+ &NumBytesWritten,
+ &PadFileHeader->State
+ );
+ if (EFI_ERROR (Status)) {
+ SetFileState (EFI_FILE_HEADER_CONSTRUCTION, PadFileHeader);
+ return Status;
+ }
+ //
+ // Update Free Space Entry, since header is allocated
+ //
+ FreeSpaceEntry->Length -= sizeof (EFI_FFS_FILE_HEADER);
+ FreeSpaceEntry->StartingAddress += sizeof (EFI_FFS_FILE_HEADER);
+
+ //
+ // Fill File Name Guid, here we assign a NULL-GUID to Pad files
+ //
+ ZeroMem (&PadFileHeader->Name, sizeof (EFI_GUID));
+
+ //
+ // Fill File Type, checksum(0), Attributes(0), Size
+ //
+ PadFileHeader->Type = EFI_FV_FILETYPE_FFS_PAD;
+ PadFileHeader->Attributes = 0;
+ *(UINT32 *) PadFileHeader->Size &= 0xFF000000;
+ *(UINT32 *) PadFileHeader->Size |= (Size + sizeof (EFI_FFS_FILE_HEADER));
+
+ SetHeaderChecksum (PadFileHeader);
+ SetPadFileChecksum (PadFileHeader);
+
+ Offset = (UINTN) (StartPos - FvDevice->CachedFv);
+
+ NumBytesWritten = sizeof (EFI_FFS_FILE_HEADER);
+ Status = FvcWrite (
+ FvDevice,
+ Offset,
+ &NumBytesWritten,
+ (UINT8 *) PadFileHeader
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Step 2, then Mark header valid, since no data write,
+ // mark the data valid at the same time.
+ //
+ SetFileState (EFI_FILE_HEADER_VALID, PadFileHeader);
+ SetFileState (EFI_FILE_DATA_VALID, PadFileHeader);
+
+ Offset = (UINTN) (StartPos - FvDevice->CachedFv);
+ StateOffset = Offset + (UINT8 *) &PadFileHeader->State - (UINT8 *) PadFileHeader;
+
+ NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);
+ Status = FvcWrite (
+ FvDevice,
+ StateOffset,
+ &NumBytesWritten,
+ &PadFileHeader->State
+ );
+ if (EFI_ERROR (Status)) {
+ SetFileState (EFI_FILE_HEADER_VALID, PadFileHeader);
+ SetFileState (EFI_FILE_DATA_VALID, PadFileHeader);
+ return Status;
+ }
+ //
+ // Update Free Space Entry, since header is allocated
+ //
+ FreeSpaceEntry->Length -= Size;
+ FreeSpaceEntry->StartingAddress += Size;
+
+ //
+ // 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) StartPos;
+ InsertTailList (&FvDevice->FfsFileListHeader, &FfsFileEntry->Link);
+
+ *PadFileEntry = FfsFileEntry;
+ FvDevice->CurrentFfsFile = FfsFileEntry;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Fill pad file header within firmware cache.
+
+ @param PadFileHeader The start of the Pad File Buffer.
+ @param PadFileLength The length of the pad file including the header.
+
+**/
+VOID
+FvFillPadFile (
+ IN EFI_FFS_FILE_HEADER *PadFileHeader,
+ IN UINTN PadFileLength
+ )
+{
+ //
+ // Fill File Name Guid, here we assign a NULL-GUID to Pad files
+ //
+ ZeroMem (&PadFileHeader->Name, sizeof (EFI_GUID));
+
+ //
+ // Fill File Type, checksum(0), Attributes(0), Size
+ //
+ PadFileHeader->Type = EFI_FV_FILETYPE_FFS_PAD;
+ PadFileHeader->Attributes = 0;
+ *(UINT32 *) PadFileHeader->Size &= 0xFF000000;
+ *(UINT32 *) PadFileHeader->Size |= PadFileLength;
+
+ SetHeaderChecksum (PadFileHeader);
+ SetPadFileChecksum (PadFileHeader);
+
+ //
+ // Set File State to 0x00000111
+ //
+ SetFileState (EFI_FILE_HEADER_CONSTRUCTION, PadFileHeader);
+ SetFileState (EFI_FILE_HEADER_VALID, PadFileHeader);
+ SetFileState (EFI_FILE_DATA_VALID, PadFileHeader);
+
+ return ;
+}
+
+/**
+ Create entire FFS file.
+
+ @param FileHeader Starting Address of a Buffer that hold the FFS File image.
+ @param FfsFileBuffer The source buffer that contains the File Data.
+ @param BufferSize The length of FfsFileBuffer.
+ @param ActualFileSize Size of FFS file.
+ @param FileName The Guid of Ffs File.
+ @param FileType The type of the written Ffs File.
+ @param FileAttributes The attributes of the written Ffs File.
+
+ @retval EFI_INVALID_PARAMETER File type is not valid.
+ @retval EFI_SUCCESS FFS file is successfully created.
+
+**/
+EFI_STATUS
+FvFillFfsFile (
+ OUT EFI_FFS_FILE_HEADER *FileHeader,
+ 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_FFS_FILE_ATTRIBUTES TmpFileAttribute;
+ EFI_FFS_FILE_HEADER *TmpFileHeader;
+
+ //
+ // File Type value 0x0E~0xE0 are reserved
+ //
+ if ((FileType > EFI_FV_FILETYPE_SMM_CORE) && (FileType < 0xE0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ TmpFileHeader = (EFI_FFS_FILE_HEADER *) FfsFileBuffer;
+ //
+ // First fill all fields ready in FfsFileBuffer
+ //
+ CopyGuid (&TmpFileHeader->Name, FileName);
+ TmpFileHeader->Type = FileType;
+
+ //
+ // Convert the FileAttributes to FFSFileAttributes
+ //
+ FvFileAttrib2FfsFileAttrib (FileAttributes, &TmpFileAttribute);
+
+ TmpFileHeader->Attributes = TmpFileAttribute;
+
+ *(UINT32 *) TmpFileHeader->Size &= 0xFF000000;
+ *(UINT32 *) TmpFileHeader->Size |= ActualFileSize;
+
+ SetHeaderChecksum (TmpFileHeader);
+ SetFileChecksum (TmpFileHeader, ActualFileSize);
+
+ SetFileState (EFI_FILE_HEADER_CONSTRUCTION, TmpFileHeader);
+ SetFileState (EFI_FILE_HEADER_VALID, TmpFileHeader);
+ SetFileState (EFI_FILE_DATA_VALID, TmpFileHeader);
+
+ //
+ // Copy data from FfsFileBuffer to FileHeader(cache)
+ //
+ CopyMem (FileHeader, FfsFileBuffer, BufferSize);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Fill some other extra space using 0xFF(Erase Value).
+
+ @param ErasePolarity Fv erase value.
+ @param FileHeader Point to the start of FFS File.
+ @param ExtraLength The pading length.
+
+**/
+VOID
+FvAdjustFfsFile (
+ IN UINT8 ErasePolarity,
+ IN EFI_FFS_FILE_HEADER *FileHeader,
+ IN UINTN ExtraLength
+ )
+{
+ UINTN FileLength;
+ UINT8 *Ptr;
+ UINT8 PadingByte;
+
+ FileLength = *(UINT32 *) FileHeader->Size & 0x00FFFFFF;
+ Ptr = (UINT8 *) FileHeader + FileLength;
+
+ if (ErasePolarity == 0) {
+ PadingByte = 0;
+ } else {
+ PadingByte = 0xFF;
+ }
+ //
+ // Fill the non-used space with Padding Byte
+ //
+ SetMem (Ptr, ExtraLength, PadingByte);
+
+ return ;
+}
+
+/**
+ Free File List entry pointed by FileListHead.
+
+ @param FileListHeader FileListEntry Header.
+
+**/
+VOID
+FreeFileList (
+ IN LIST_ENTRY *FileListHead
+ )
+{
+ FFS_FILE_LIST_ENTRY *FfsFileEntry;
+ LIST_ENTRY *NextEntry;
+
+ FfsFileEntry = (FFS_FILE_LIST_ENTRY *) (FileListHead->ForwardLink);
+
+ //
+ // Loop the whole list entry to free resources
+ //
+ while (&FfsFileEntry->Link != FileListHead) {
+ NextEntry = (&FfsFileEntry->Link)->ForwardLink;
+ FreePool (FfsFileEntry);
+ FfsFileEntry = (FFS_FILE_LIST_ENTRY *) NextEntry;
+ }
+
+ return ;
+}
+
+/**
+ Create a new file within a PAD file area.
+
+ @param FvDevice Firmware Volume Device.
+ @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 Successfully create a new file within the found PAD file area.
+ @retval EFI_OUT_OF_RESOURCES No suitable PAD file is found.
+ @retval other errors New file is created failed.
+
+**/
+EFI_STATUS
+FvCreateNewFileInsidePadFile (
+ 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
+ )
+{
+ UINTN RequiredAlignment;
+ FFS_FILE_LIST_ENTRY *PadFileEntry;
+ EFI_STATUS Status;
+ UINTN PadAreaLength;
+ UINTN PadSize;
+ EFI_FFS_FILE_HEADER *FileHeader;
+ EFI_FFS_FILE_HEADER *OldPadFileHeader;
+ EFI_FFS_FILE_HEADER *PadFileHeader;
+ EFI_FFS_FILE_HEADER *TailPadFileHeader;
+ UINTN StateOffset;
+ UINTN Offset;
+ UINTN NumBytesWritten;
+ UINT8 *StartPos;
+ LIST_ENTRY NewFileList;
+ FFS_FILE_LIST_ENTRY *NewFileListEntry;
+ FFS_FILE_LIST_ENTRY *FfsEntry;
+ FFS_FILE_LIST_ENTRY *NextFfsEntry;
+
+ //
+ // First get the required alignment from the File Attributes
+ //
+ RequiredAlignment = GetRequiredAlignment (FileAttributes);
+
+ //
+ // Find a suitable PAD File
+ //
+ Status = FvLocatePadFile (
+ FvDevice,
+ BufferSize,
+ RequiredAlignment,
+ &PadSize,
+ &PadFileEntry
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ OldPadFileHeader = (EFI_FFS_FILE_HEADER *) PadFileEntry->FfsHeader;
+
+ //
+ // Step 1: Update Pad File Header
+ //
+ SetFileState (EFI_FILE_MARKED_FOR_UPDATE, OldPadFileHeader);
+
+ StartPos = PadFileEntry->FfsHeader;
+
+ Offset = (UINTN) (StartPos - FvDevice->CachedFv);
+ StateOffset = Offset + (UINT8 *) &OldPadFileHeader->State - (UINT8 *) OldPadFileHeader;
+
+ NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);
+ Status = FvcWrite (
+ FvDevice,
+ StateOffset,
+ &NumBytesWritten,
+ &OldPadFileHeader->State
+ );
+ if (EFI_ERROR (Status)) {
+ SetFileState (EFI_FILE_HEADER_CONSTRUCTION, OldPadFileHeader);
+ return Status;
+ }
+
+ //
+ // Step 2: Update Pad area
+ //
+ InitializeListHead (&NewFileList);
+
+ PadAreaLength = (*(UINT32 *) OldPadFileHeader->Size & 0x00FFFFFF) - sizeof (EFI_FFS_FILE_HEADER);
+
+ PadFileHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) OldPadFileHeader + sizeof (EFI_FFS_FILE_HEADER));
+
+ if (RequiredAlignment != 8) {
+ //
+ // Insert a PAD file before to achieve required alignment
+ //
+ FvFillPadFile (PadFileHeader, PadSize);
+ NewFileListEntry = AllocatePool (sizeof (FFS_FILE_LIST_ENTRY));
+ ASSERT (NewFileListEntry != NULL);
+ NewFileListEntry->FfsHeader = (UINT8 *) PadFileHeader;
+ InsertTailList (&NewFileList, &NewFileListEntry->Link);
+ }
+
+ FileHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) PadFileHeader + PadSize);
+
+ Status = FvFillFfsFile (
+ FileHeader,
+ FfsFileBuffer,
+ BufferSize,
+ ActualFileSize,
+ FileName,
+ FileType,
+ FileAttributes
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ NewFileListEntry = AllocatePool (sizeof (FFS_FILE_LIST_ENTRY));
+ ASSERT (NewFileListEntry != NULL);
+
+ NewFileListEntry->FfsHeader = (UINT8 *) FileHeader;
+ InsertTailList (&NewFileList, &NewFileListEntry->Link);
+
+ FvDevice->CurrentFfsFile = NewFileListEntry;
+
+ if (PadAreaLength > (BufferSize + PadSize)) {
+ if ((PadAreaLength - BufferSize - PadSize) >= sizeof (EFI_FFS_FILE_HEADER)) {
+ //
+ // we can insert another PAD file
+ //
+ TailPadFileHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FileHeader + BufferSize);
+ FvFillPadFile (TailPadFileHeader, PadAreaLength - BufferSize - PadSize);
+
+ NewFileListEntry = AllocatePool (sizeof (FFS_FILE_LIST_ENTRY));
+ ASSERT (NewFileListEntry != NULL);
+
+ NewFileListEntry->FfsHeader = (UINT8 *) TailPadFileHeader;
+ InsertTailList (&NewFileList, &NewFileListEntry->Link);
+ } else {
+ //
+ // because left size cannot hold another PAD file header,
+ // adjust the writing file size (just in cache)
+ //
+ FvAdjustFfsFile (
+ FvDevice->ErasePolarity,
+ FileHeader,
+ PadAreaLength - BufferSize - PadSize
+ );
+ }
+ }
+ //
+ // Start writing to FV
+ //
+ StartPos = (UINT8 *) OldPadFileHeader + sizeof (EFI_FFS_FILE_HEADER);
+
+ Offset = (UINTN) (StartPos - FvDevice->CachedFv);
+
+ NumBytesWritten = PadAreaLength;
+ Status = FvcWrite (
+ FvDevice,
+ Offset,
+ &NumBytesWritten,
+ StartPos
+ );
+ if (EFI_ERROR (Status)) {
+ FreeFileList (&NewFileList);
+ return Status;
+ }
+
+ //
+ // Step 3: Mark Pad file header as EFI_FILE_HEADER_INVALID
+ //
+ SetFileState (EFI_FILE_HEADER_INVALID, OldPadFileHeader);
+
+ StartPos = PadFileEntry->FfsHeader;
+
+ Offset = (UINTN) (StartPos - FvDevice->CachedFv);
+ StateOffset = Offset + (UINT8 *) &OldPadFileHeader->State - (UINT8 *) OldPadFileHeader;
+
+ NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);
+ Status = FvcWrite (
+ FvDevice,
+ StateOffset,
+ &NumBytesWritten,
+ &OldPadFileHeader->State
+ );
+ if (EFI_ERROR (Status)) {
+ SetFileState (EFI_FILE_HEADER_INVALID, OldPadFileHeader);
+ return Status;
+ }
+
+ //
+ // If all successfully, update FFS_FILE_LIST
+ //
+
+ //
+ // Delete old pad file entry
+ //
+ FfsEntry = (FFS_FILE_LIST_ENTRY *) PadFileEntry->Link.BackLink;
+ NextFfsEntry = (FFS_FILE_LIST_ENTRY *) PadFileEntry->Link.ForwardLink;
+
+ FreePool (PadFileEntry);
+
+ FfsEntry->Link.ForwardLink = NewFileList.ForwardLink;
+ (NewFileList.ForwardLink)->BackLink = &FfsEntry->Link;
+ NextFfsEntry->Link.BackLink = NewFileList.BackLink;
+ (NewFileList.BackLink)->ForwardLink = &NextFfsEntry->Link;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Free all FfsBuffer.
+
+ @param NumOfFiles Number of FfsBuffer.
+ @param FfsBuffer An array of pointer to an FFS File Buffer
+
+**/
+VOID
+FreeFfsBuffer (
+ IN UINTN NumOfFiles,
+ IN UINT8 **FfsBuffer
+ )
+{
+ UINTN Index;
+ for (Index = 0; Index < NumOfFiles; Index++) {
+ if (FfsBuffer[Index] != NULL) {
+ FreePool (FfsBuffer[Index]);
+ }
+ }
+}
+
+/**
+ Create multiple files within a PAD File area.
+
+ @param FvDevice Firmware Volume Device.
+ @param PadFileEntry The pad file entry to be written in.
+ @param NumOfFiles Total File number to be written.
+ @param BufferSize The array of buffer size of each FfsBuffer.
+ @param ActualFileSize The array of actual file size.
+ @param PadSize The array of leading pad file size for each FFS File
+ @param FfsBuffer The array of Ffs Buffer pointer.
+ @param FileData The array of EFI_FV_WRITE_FILE_DATA structure,
+ used to get name, attributes, type, etc.
+
+ @retval EFI_SUCCESS Add the input multiple files into PAD file area.
+ @retval EFI_OUT_OF_RESOURCES No enough memory is allocated.
+ @retval other error Files can't be added into PAD file area.
+
+**/
+EFI_STATUS
+FvCreateMultipleFilesInsidePadFile (
+ IN FV_DEVICE *FvDevice,
+ IN FFS_FILE_LIST_ENTRY *PadFileEntry,
+ IN UINTN NumOfFiles,
+ IN UINTN *BufferSize,
+ IN UINTN *ActualFileSize,
+ IN UINTN *PadSize,
+ IN UINT8 **FfsBuffer,
+ IN EFI_FV_WRITE_FILE_DATA *FileData
+ )
+{
+ EFI_STATUS Status;
+ EFI_FFS_FILE_HEADER *OldPadFileHeader;
+ UINTN Index;
+ EFI_FFS_FILE_HEADER *PadFileHeader;
+ EFI_FFS_FILE_HEADER *FileHeader;
+ EFI_FFS_FILE_HEADER *TailPadFileHeader;
+ UINTN TotalSize;
+ UINTN PadAreaLength;
+ LIST_ENTRY NewFileList;
+ FFS_FILE_LIST_ENTRY *NewFileListEntry;
+ UINTN Offset;
+ UINTN NumBytesWritten;
+ UINT8 *StartPos;
+ FFS_FILE_LIST_ENTRY *FfsEntry;
+ FFS_FILE_LIST_ENTRY *NextFfsEntry;
+
+ InitializeListHead (&NewFileList);
+
+ NewFileListEntry = NULL;
+
+ OldPadFileHeader = (EFI_FFS_FILE_HEADER *) PadFileEntry->FfsHeader;
+ PadAreaLength = (*(UINT32 *) OldPadFileHeader->Size & 0x00FFFFFF) - sizeof (EFI_FFS_FILE_HEADER);
+
+ Status = UpdateHeaderBit (
+ FvDevice,
+ OldPadFileHeader,
+ EFI_FILE_MARKED_FOR_UPDATE
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // Update PAD area
+ //
+ TotalSize = 0;
+ PadFileHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) OldPadFileHeader + sizeof (EFI_FFS_FILE_HEADER));
+ FileHeader = PadFileHeader;
+
+ for (Index = 0; Index < NumOfFiles; Index++) {
+ if (PadSize[Index] != 0) {
+ FvFillPadFile (PadFileHeader, PadSize[Index]);
+ NewFileListEntry = AllocatePool (sizeof (FFS_FILE_LIST_ENTRY));
+ if (NewFileListEntry == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ NewFileListEntry->FfsHeader = (UINT8 *) PadFileHeader;
+ InsertTailList (&NewFileList, &NewFileListEntry->Link);
+ }
+
+ FileHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) PadFileHeader + PadSize[Index]);
+ Status = FvFillFfsFile (
+ FileHeader,
+ FfsBuffer[Index],
+ BufferSize[Index],
+ ActualFileSize[Index],
+ FileData[Index].NameGuid,
+ FileData[Index].Type,
+ FileData[Index].FileAttributes
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ NewFileListEntry = AllocatePool (sizeof (FFS_FILE_LIST_ENTRY));
+ if (NewFileListEntry == NULL) {
+ FreeFileList (&NewFileList);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ NewFileListEntry->FfsHeader = (UINT8 *) FileHeader;
+ InsertTailList (&NewFileList, &NewFileListEntry->Link);
+
+ PadFileHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FileHeader + BufferSize[Index]);
+ TotalSize += PadSize[Index];
+ TotalSize += BufferSize[Index];
+ }
+
+ FvDevice->CurrentFfsFile = NewFileListEntry;
+ //
+ // Maybe we need a tail pad file
+ //
+ if (PadAreaLength > TotalSize) {
+ if ((PadAreaLength - TotalSize) >= sizeof (EFI_FFS_FILE_HEADER)) {
+ //
+ // we can insert another PAD file
+ //
+ TailPadFileHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FileHeader + BufferSize[NumOfFiles - 1]);
+ FvFillPadFile (TailPadFileHeader, PadAreaLength - TotalSize);
+
+ NewFileListEntry = AllocatePool (sizeof (FFS_FILE_LIST_ENTRY));
+ if (NewFileListEntry == NULL) {
+ FreeFileList (&NewFileList);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ NewFileListEntry->FfsHeader = (UINT8 *) TailPadFileHeader;
+ InsertTailList (&NewFileList, &NewFileListEntry->Link);
+ } else {
+ //
+ // because left size cannot hold another PAD file header,
+ // adjust the writing file size (just in cache)
+ //
+ FvAdjustFfsFile (
+ FvDevice->ErasePolarity,
+ FileHeader,
+ PadAreaLength - TotalSize
+ );
+ }
+ }
+ //
+ // Start writing to FV
+ //
+ StartPos = (UINT8 *) OldPadFileHeader + sizeof (EFI_FFS_FILE_HEADER);
+
+ Offset = (UINTN) (StartPos - FvDevice->CachedFv);
+
+ NumBytesWritten = PadAreaLength;
+ Status = FvcWrite (
+ FvDevice,
+ Offset,
+ &NumBytesWritten,
+ StartPos
+ );
+ if (EFI_ERROR (Status)) {
+ FreeFileList (&NewFileList);
+ return Status;
+ }
+
+ Status = UpdateHeaderBit (
+ FvDevice,
+ OldPadFileHeader,
+ EFI_FILE_HEADER_INVALID
+ );
+ if (EFI_ERROR (Status)) {
+ FreeFileList (&NewFileList);
+ return Status;
+ }
+
+ //
+ // Update File List Link
+ //
+
+ //
+ // First delete old pad file entry
+ //
+ FfsEntry = (FFS_FILE_LIST_ENTRY *) PadFileEntry->Link.BackLink;
+ NextFfsEntry = (FFS_FILE_LIST_ENTRY *) PadFileEntry->Link.ForwardLink;
+
+ FreePool (PadFileEntry);
+
+ FfsEntry->Link.ForwardLink = NewFileList.ForwardLink;
+ (NewFileList.ForwardLink)->BackLink = &FfsEntry->Link;
+ NextFfsEntry->Link.BackLink = NewFileList.BackLink;
+ (NewFileList.BackLink)->ForwardLink = &NextFfsEntry->Link;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Write multiple files into FV in reliable method.
+
+ @param FvDevice Firmware Volume Device.
+ @param NumOfFiles Total File number to be written.
+ @param FileData The array of EFI_FV_WRITE_FILE_DATA structure,
+ used to get name, attributes, type, etc
+ @param FileOperation The array of operation for each file.
+
+ @retval EFI_SUCCESS Files are added into FV.
+ @retval EFI_OUT_OF_RESOURCES No enough free PAD files to add the input files.
+ @retval EFI_INVALID_PARAMETER File number is less than or equal to 1.
+ @retval EFI_UNSUPPORTED File number exceeds the supported max numbers of files.
+
+**/
+EFI_STATUS
+FvCreateMultipleFiles (
+ IN FV_DEVICE *FvDevice,
+ IN UINTN NumOfFiles,
+ IN EFI_FV_WRITE_FILE_DATA *FileData,
+ IN BOOLEAN *FileOperation
+ )
+{
+ EFI_STATUS Status;
+ UINT8 *FfsBuffer[MAX_FILES];
+ UINTN Index1;
+ UINTN Index2;
+ UINTN BufferSize[MAX_FILES];
+ UINTN ActualFileSize[MAX_FILES];
+ UINTN RequiredAlignment[MAX_FILES];
+ UINTN PadSize[MAX_FILES];
+ FFS_FILE_LIST_ENTRY *PadFileEntry;
+ UINTN TotalSizeNeeded;
+ FREE_SPACE_ENTRY *FreeSpaceEntry;
+ EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;
+ UINTN Key;
+ EFI_GUID FileNameGuid;
+ EFI_FV_FILETYPE OldFileType;
+ EFI_FV_FILE_ATTRIBUTES OldFileAttributes;
+ UINTN OldFileSize;
+ FFS_FILE_LIST_ENTRY *OldFfsFileEntry[MAX_FILES];
+ EFI_FFS_FILE_HEADER *OldFileHeader[MAX_FILES];
+ BOOLEAN IsCreateFile;
+
+ //
+ // To use this function, we must ensure that the NumOfFiles is great
+ // than 1
+ //
+ if (NumOfFiles <= 1) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (NumOfFiles > MAX_FILES) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Fv = &FvDevice->Fv;
+
+ SetMem (FfsBuffer, NumOfFiles, 0);
+ SetMem (RequiredAlignment, NumOfFiles, 8);
+ SetMem (PadSize, NumOfFiles, 0);
+ ZeroMem (OldFfsFileEntry, sizeof (OldFfsFileEntry));
+ ZeroMem (OldFileHeader, sizeof (OldFileHeader));
+
+ //
+ // Adjust file size
+ //
+ for (Index1 = 0; Index1 < NumOfFiles; Index1++) {
+ ActualFileSize[Index1] = FileData[Index1].BufferSize + sizeof (EFI_FFS_FILE_HEADER);
+ BufferSize[Index1] = ActualFileSize[Index1];
+
+ if (BufferSize[Index1] == sizeof (EFI_FFS_FILE_HEADER)) {
+ //
+ // clear file attributes, zero-length file does not have any attributes
+ //
+ FileData[Index1].FileAttributes = 0;
+ }
+
+ while ((BufferSize[Index1] & 0x07) != 0) {
+ BufferSize[Index1]++;
+ }
+
+ FfsBuffer[Index1] = AllocateZeroPool (BufferSize[Index1]);
+
+ //
+ // Copy File Data into FileBuffer
+ //
+ CopyMem (
+ FfsBuffer[Index1] + sizeof (EFI_FFS_FILE_HEADER),
+ FileData[Index1].Buffer,
+ FileData[Index1].BufferSize
+ );
+
+ if (FvDevice->ErasePolarity == 1) {
+ for (Index2 = 0; Index2 < sizeof (EFI_FFS_FILE_HEADER); Index2++) {
+ FfsBuffer[Index1][Index2] = (UINT8)~FfsBuffer[Index1][Index2];
+ }
+ }
+
+ if ((FileData[Index1].FileAttributes & FFS_ATTRIB_DATA_ALIGNMENT) != 0) {
+ RequiredAlignment[Index1] = GetRequiredAlignment (FileData[Index1].FileAttributes);
+ }
+ //
+ // If update file, mark the original file header to
+ // EFI_FILE_MARKED_FOR_UPDATE
+ //
+ IsCreateFile = FileOperation[Index1];
+ if (!IsCreateFile) {
+
+ Key = 0;
+ do {
+ OldFileType = 0;
+ Status = Fv->GetNextFile (
+ Fv,
+ &Key,
+ &OldFileType,
+ &FileNameGuid,
+ &OldFileAttributes,
+ &OldFileSize
+ );
+ if (EFI_ERROR (Status)) {
+ FreeFfsBuffer (NumOfFiles, FfsBuffer);
+ return Status;
+ }
+ } while (!CompareGuid (&FileNameGuid, FileData[Index1].NameGuid));
+
+ //
+ // Get FfsFileEntry from the search key
+ //
+ OldFfsFileEntry[Index1] = (FFS_FILE_LIST_ENTRY *) Key;
+ OldFileHeader[Index1] = (EFI_FFS_FILE_HEADER *) OldFfsFileEntry[Index1]->FfsHeader;
+ Status = UpdateHeaderBit (
+ FvDevice,
+ OldFileHeader[Index1],
+ EFI_FILE_MARKED_FOR_UPDATE
+ );
+ if (EFI_ERROR (Status)) {
+ FreeFfsBuffer (NumOfFiles, FfsBuffer);
+ return Status;
+ }
+ }
+ }
+ //
+ // First to search a suitable pad file that can hold so
+ // many files
+ //
+ Status = FvSearchSuitablePadFile (
+ FvDevice,
+ NumOfFiles,
+ BufferSize,
+ RequiredAlignment,
+ PadSize,
+ &TotalSizeNeeded,
+ &PadFileEntry
+ );
+
+ if (Status == EFI_NOT_FOUND) {
+ //
+ // Try to find a free space that can hold these files
+ // and create a suitable PAD file in this free space
+ //
+ Status = FvSearchSuitableFreeSpace (
+ FvDevice,
+ NumOfFiles,
+ BufferSize,
+ RequiredAlignment,
+ PadSize,
+ &TotalSizeNeeded,
+ &FreeSpaceEntry
+ );
+ if (EFI_ERROR (Status)) {
+ FreeFfsBuffer (NumOfFiles, FfsBuffer);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // Create a PAD file in that space
+ //
+ Status = FvCreatePadFileInFreeSpace (
+ FvDevice,
+ FreeSpaceEntry,
+ TotalSizeNeeded,
+ &PadFileEntry
+ );
+
+ if (EFI_ERROR (Status)) {
+ FreeFfsBuffer (NumOfFiles, FfsBuffer);
+ return Status;
+ }
+ }
+ //
+ // Create multiple files inside such a pad file
+ // to achieve lock-step update
+ //
+ Status = FvCreateMultipleFilesInsidePadFile (
+ FvDevice,
+ PadFileEntry,
+ NumOfFiles,
+ BufferSize,
+ ActualFileSize,
+ PadSize,
+ FfsBuffer,
+ FileData
+ );
+
+ FreeFfsBuffer (NumOfFiles, FfsBuffer);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // Delete those updated files
+ //
+ for (Index1 = 0; Index1 < NumOfFiles; Index1++) {
+ IsCreateFile = FileOperation[Index1];
+ if (!IsCreateFile && OldFfsFileEntry[Index1] != NULL) {
+ (OldFfsFileEntry[Index1]->Link.BackLink)->ForwardLink = OldFfsFileEntry[Index1]->Link.ForwardLink;
+ (OldFfsFileEntry[Index1]->Link.ForwardLink)->BackLink = OldFfsFileEntry[Index1]->Link.BackLink;
+ FreePool (OldFfsFileEntry[Index1]);
+ }
+ }
+ //
+ // Set those files' state to EFI_FILE_DELETED
+ //
+ for (Index1 = 0; Index1 < NumOfFiles; Index1++) {
+ IsCreateFile = FileOperation[Index1];
+ if (!IsCreateFile && OldFileHeader[Index1] != NULL) {
+ Status = UpdateHeaderBit (FvDevice, OldFileHeader[Index1], EFI_FILE_DELETED);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+ }
+
+ return EFI_SUCCESS;
+}