| /** @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 | |
| ) | |
| { | |
| if ((PadFileHeader->Attributes & FFS_ATTRIB_CHECKSUM) != 0) { | |
| if (IS_FFS_FILE2 (PadFileHeader)) { | |
| // | |
| // Calculate checksum of Pad File Data | |
| // | |
| PadFileHeader->IntegrityCheck.Checksum.File = | |
| CalculateCheckSum8 ((UINT8 *) PadFileHeader + sizeof (EFI_FFS_FILE_HEADER2), FFS_FILE2_SIZE (PadFileHeader) - sizeof (EFI_FFS_FILE_HEADER2)); | |
| } else { | |
| // | |
| // Calculate checksum of Pad File Data | |
| // | |
| PadFileHeader->IntegrityCheck.Checksum.File = | |
| CalculateCheckSum8 ((UINT8 *) PadFileHeader + sizeof (EFI_FFS_FILE_HEADER), FFS_FILE_SIZE (PadFileHeader) - 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; | |
| UINTN HeaderSize; | |
| UINTN FileSize; | |
| HeaderSize = sizeof (EFI_FFS_FILE_HEADER); | |
| FileSize = Size + HeaderSize; | |
| if (FileSize > 0x00FFFFFF) { | |
| HeaderSize = sizeof (EFI_FFS_FILE_HEADER2); | |
| FileSize = Size + HeaderSize; | |
| } | |
| if (FreeSpaceEntry->Length < FileSize) { | |
| 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, | |
| FileSize | |
| )) { | |
| 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 -= HeaderSize; | |
| FreeSpaceEntry->StartingAddress += HeaderSize; | |
| // | |
| // 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; | |
| if ((FileSize) > 0x00FFFFFF) { | |
| ((EFI_FFS_FILE_HEADER2 *) PadFileHeader)->ExtendedSize = (UINT32) FileSize; | |
| *(UINT32 *) PadFileHeader->Size &= 0xFF000000; | |
| PadFileHeader->Attributes |= FFS_ATTRIB_LARGE_FILE; | |
| } else { | |
| *(UINT32 *) PadFileHeader->Size &= 0xFF000000; | |
| *(UINT32 *) PadFileHeader->Size |= FileSize; | |
| } | |
| SetHeaderChecksum (PadFileHeader); | |
| SetPadFileChecksum (PadFileHeader); | |
| Offset = (UINTN) (StartPos - FvDevice->CachedFv); | |
| NumBytesWritten = HeaderSize; | |
| 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; | |
| if (PadFileLength > 0x00FFFFFF) { | |
| ((EFI_FFS_FILE_HEADER2 *) PadFileHeader)->ExtendedSize = (UINT32) PadFileLength; | |
| *(UINT32 *) PadFileHeader->Size &= 0xFF000000; | |
| PadFileHeader->Attributes |= FFS_ATTRIB_LARGE_FILE; | |
| } else { | |
| *(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; | |
| 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 (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 | |
| ) | |
| { | |
| UINT8 *Ptr; | |
| UINT8 PadingByte; | |
| if (IS_FFS_FILE2 (FileHeader)) { | |
| Ptr = (UINT8 *) FileHeader + FFS_FILE2_SIZE (FileHeader); | |
| } else { | |
| Ptr = (UINT8 *) FileHeader + FFS_FILE_SIZE (FileHeader); | |
| } | |
| 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); | |
| if (IS_FFS_FILE2 (OldPadFileHeader)) { | |
| PadAreaLength = FFS_FILE2_SIZE (OldPadFileHeader) - sizeof (EFI_FFS_FILE_HEADER); | |
| PadFileHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) OldPadFileHeader + sizeof (EFI_FFS_FILE_HEADER2)); | |
| } else { | |
| PadAreaLength = FFS_FILE_SIZE (OldPadFileHeader) - sizeof (EFI_FFS_FILE_HEADER); | |
| PadFileHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) OldPadFileHeader + sizeof (EFI_FFS_FILE_HEADER)); | |
| } | |
| if (PadSize != 0) { | |
| // | |
| // 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)) { | |
| FreeFileList (&NewFileList); | |
| 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 | |
| // | |
| if (IS_FFS_FILE2 (OldPadFileHeader)) { | |
| StartPos = (UINT8 *) OldPadFileHeader + sizeof (EFI_FFS_FILE_HEADER2); | |
| } else { | |
| 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); | |
| FvDevice->CurrentFfsFile = NULL; | |
| 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); | |
| FreeFileList (&NewFileList); | |
| FvDevice->CurrentFfsFile = NULL; | |
| 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; | |
| if (IS_FFS_FILE2 (OldPadFileHeader)) { | |
| PadAreaLength = FFS_FILE2_SIZE (OldPadFileHeader) - sizeof (EFI_FFS_FILE_HEADER2); | |
| } else { | |
| PadAreaLength = FFS_FILE_SIZE (OldPadFileHeader) - 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; | |
| if (IS_FFS_FILE2 (OldPadFileHeader)) { | |
| PadFileHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) OldPadFileHeader + sizeof (EFI_FFS_FILE_HEADER2)); | |
| } else { | |
| 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) { | |
| FreeFileList (&NewFileList); | |
| 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)) { | |
| FreeFileList (&NewFileList); | |
| 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); | |
| FvDevice->CurrentFfsFile = NULL; | |
| 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 | |
| // | |
| if (IS_FFS_FILE2 (OldPadFileHeader)) { | |
| StartPos = (UINT8 *) OldPadFileHeader + sizeof (EFI_FFS_FILE_HEADER2); | |
| } else { | |
| 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); | |
| FvDevice->CurrentFfsFile = NULL; | |
| return Status; | |
| } | |
| Status = UpdateHeaderBit ( | |
| FvDevice, | |
| OldPadFileHeader, | |
| EFI_FILE_HEADER_INVALID | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| FreeFileList (&NewFileList); | |
| FvDevice->CurrentFfsFile = NULL; | |
| 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; | |
| } | |
| /** | |
| Create multiple files within the Free Space. | |
| @param FvDevice Firmware Volume Device. | |
| @param FreeSpaceEntry Indicating in which Free Space(Cache) the multiple files will be inserted. | |
| @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 | |
| FvCreateMultipleFilesInsideFreeSpace ( | |
| IN FV_DEVICE *FvDevice, | |
| IN FREE_SPACE_ENTRY *FreeSpaceEntry, | |
| 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; | |
| UINTN Index; | |
| EFI_FFS_FILE_HEADER *PadFileHeader; | |
| EFI_FFS_FILE_HEADER *FileHeader; | |
| UINTN TotalSize; | |
| LIST_ENTRY NewFileList; | |
| FFS_FILE_LIST_ENTRY *NewFileListEntry; | |
| UINTN Offset; | |
| UINTN NumBytesWritten; | |
| UINT8 *StartPos; | |
| InitializeListHead (&NewFileList); | |
| NewFileListEntry = NULL; | |
| TotalSize = 0; | |
| StartPos = FreeSpaceEntry->StartingAddress; | |
| PadFileHeader = (EFI_FFS_FILE_HEADER *) StartPos; | |
| 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) { | |
| FreeFileList (&NewFileList); | |
| 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)) { | |
| FreeFileList (&NewFileList); | |
| 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]; | |
| } | |
| if (FreeSpaceEntry->Length < TotalSize) { | |
| FreeFileList (&NewFileList); | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| FvDevice->CurrentFfsFile = NewFileListEntry; | |
| // | |
| // Start writing to FV | |
| // | |
| Offset = (UINTN) (StartPos - FvDevice->CachedFv); | |
| NumBytesWritten = TotalSize; | |
| Status = FvcWrite ( | |
| FvDevice, | |
| Offset, | |
| &NumBytesWritten, | |
| StartPos | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| FreeFileList (&NewFileList); | |
| FvDevice->CurrentFfsFile = NULL; | |
| return Status; | |
| } | |
| FreeSpaceEntry->Length -= TotalSize; | |
| FreeSpaceEntry->StartingAddress += TotalSize; | |
| NewFileListEntry = (FFS_FILE_LIST_ENTRY *) (NewFileList.ForwardLink); | |
| while (NewFileListEntry != (FFS_FILE_LIST_ENTRY *) &NewFileList) { | |
| InsertTailList (&FvDevice->FfsFileListHeader, &NewFileListEntry->Link); | |
| NewFileListEntry = (FFS_FILE_LIST_ENTRY *) (NewFileListEntry->Link.ForwardLink); | |
| } | |
| 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; | |
| UINTN HeaderSize; | |
| // | |
| // 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++) { | |
| HeaderSize = sizeof (EFI_FFS_FILE_HEADER); | |
| ActualFileSize[Index1] = FileData[Index1].BufferSize + HeaderSize; | |
| if (ActualFileSize[Index1] > 0x00FFFFFF) { | |
| HeaderSize = sizeof (EFI_FFS_FILE_HEADER2); | |
| ActualFileSize[Index1] = FileData[Index1].BufferSize + HeaderSize; | |
| } | |
| BufferSize[Index1] = ActualFileSize[Index1]; | |
| if (BufferSize[Index1] == HeaderSize) { | |
| // | |
| // 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] + HeaderSize, | |
| FileData[Index1].Buffer, | |
| FileData[Index1].BufferSize | |
| ); | |
| if (FvDevice->ErasePolarity == 1) { | |
| for (Index2 = 0; Index2 < HeaderSize; Index2++) { | |
| FfsBuffer[Index1][Index2] = (UINT8)~FfsBuffer[Index1][Index2]; | |
| } | |
| } | |
| if ((FileData[Index1].FileAttributes & EFI_FV_FILE_ATTRIB_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 | |
| // | |
| Status = FvSearchSuitableFreeSpace ( | |
| FvDevice, | |
| NumOfFiles, | |
| BufferSize, | |
| RequiredAlignment, | |
| PadSize, | |
| &TotalSizeNeeded, | |
| &FreeSpaceEntry | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| FreeFfsBuffer (NumOfFiles, FfsBuffer); | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| Status = FvCreateMultipleFilesInsideFreeSpace ( | |
| FvDevice, | |
| FreeSpaceEntry, | |
| NumOfFiles, | |
| BufferSize, | |
| ActualFileSize, | |
| PadSize, | |
| FfsBuffer, | |
| FileData | |
| ); | |
| } else { | |
| // | |
| // 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; | |
| } |