/** @file Implements functions to pad firmware file. Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
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; }