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

1615 lines
45 KiB
C

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