audk/IntelFrameworkModulePkg/Universal/FirmwareVolume/FwVolDxe/FwPadFile.c

1222 lines
37 KiB
C

/** @file
Implements functions to pad firmware file.
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#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;
}