/** @file | |
EFI Firmware Volume routines which work on a Fv image in buffers. | |
Copyright (c) 1999 - 2016, 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 "FirmwareVolumeBufferLib.h" | |
#include "BinderFuncs.h" | |
// | |
// Local macros | |
// | |
#define EFI_TEST_FFS_ATTRIBUTES_BIT(FvbAttributes, TestAttributes, Bit) \ | |
( \ | |
(BOOLEAN) ( \ | |
(FvbAttributes & EFI_FVB2_ERASE_POLARITY) ? (((~TestAttributes) & Bit) == Bit) : ((TestAttributes & Bit) == Bit) \ | |
) \ | |
) | |
// | |
// Local prototypes | |
// | |
STATIC | |
UINT32 | |
FvBufGetSecHdrLen( | |
IN EFI_COMMON_SECTION_HEADER *SectionHeader | |
) | |
{ | |
if (SectionHeader == NULL) { | |
return 0; | |
} | |
if (FvBufExpand3ByteSize(SectionHeader->Size) == 0xffffff) { | |
return sizeof(EFI_COMMON_SECTION_HEADER2); | |
} | |
return sizeof(EFI_COMMON_SECTION_HEADER); | |
} | |
STATIC | |
UINT32 | |
FvBufGetSecFileLen ( | |
IN EFI_COMMON_SECTION_HEADER *SectionHeader | |
) | |
{ | |
UINT32 Length; | |
if (SectionHeader == NULL) { | |
return 0; | |
} | |
Length = FvBufExpand3ByteSize(SectionHeader->Size); | |
if (Length == 0xffffff) { | |
Length = ((EFI_COMMON_SECTION_HEADER2 *)SectionHeader)->ExtendedSize; | |
} | |
return Length; | |
} | |
// | |
// Local prototypes | |
// | |
STATIC | |
UINT16 | |
FvBufCalculateChecksum16 ( | |
IN UINT16 *Buffer, | |
IN UINTN Size | |
); | |
STATIC | |
UINT8 | |
FvBufCalculateChecksum8 ( | |
IN UINT8 *Buffer, | |
IN UINTN Size | |
); | |
// | |
// Procedures start | |
// | |
EFI_STATUS | |
FvBufRemoveFileNew ( | |
IN OUT VOID *Fv, | |
IN EFI_GUID *Name | |
) | |
/*++ | |
Routine Description: | |
Clears out all files from the Fv buffer in memory | |
Arguments: | |
SourceFv - Address of the Fv in memory, this firmware volume volume will | |
be modified, if SourceFfsFile exists | |
SourceFfsFile - Input FFS file to replace | |
Returns: | |
EFI_SUCCESS | |
EFI_NOT_FOUND | |
--*/ | |
{ | |
EFI_STATUS Status; | |
EFI_FFS_FILE_HEADER* FileToRm; | |
UINTN FileToRmLength; | |
Status = FvBufFindFileByName( | |
Fv, | |
Name, | |
(VOID **)&FileToRm | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
FileToRmLength = FvBufGetFfsFileSize (FileToRm); | |
CommonLibBinderSetMem ( | |
FileToRm, | |
FileToRmLength, | |
(((EFI_FIRMWARE_VOLUME_HEADER*)Fv)->Attributes & EFI_FVB2_ERASE_POLARITY) | |
? 0xFF : 0 | |
); | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
FvBufRemoveFile ( | |
IN OUT VOID *Fv, | |
IN EFI_GUID *Name | |
) | |
/*++ | |
Routine Description: | |
Clears out all files from the Fv buffer in memory | |
Arguments: | |
SourceFv - Address of the Fv in memory, this firmware volume volume will | |
be modified, if SourceFfsFile exists | |
SourceFfsFile - Input FFS file to replace | |
Returns: | |
EFI_SUCCESS | |
EFI_NOT_FOUND | |
--*/ | |
{ | |
EFI_STATUS Status; | |
EFI_FFS_FILE_HEADER *NextFile; | |
EFI_FIRMWARE_VOLUME_HEADER *TempFv; | |
UINTN FileKey; | |
UINTN FvLength; | |
Status = FvBufFindFileByName( | |
Fv, | |
Name, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = FvBufGetSize (Fv, &FvLength); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
TempFv = NULL; | |
Status = FvBufDuplicate (Fv, (VOID **)&TempFv); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = FvBufClearAllFiles (TempFv); | |
if (EFI_ERROR (Status)) { | |
CommonLibBinderFree (TempFv); | |
return Status; | |
} | |
// TempFv has been allocated. It must now be freed | |
// before returning. | |
FileKey = 0; | |
while (TRUE) { | |
Status = FvBufFindNextFile (Fv, &FileKey, (VOID **)&NextFile); | |
if (Status == EFI_NOT_FOUND) { | |
break; | |
} else if (EFI_ERROR (Status)) { | |
CommonLibBinderFree (TempFv); | |
return Status; | |
} | |
if (CommonLibBinderCompareGuid (Name, &NextFile->Name)) { | |
continue; | |
} | |
else { | |
Status = FvBufAddFile (TempFv, NextFile); | |
if (EFI_ERROR (Status)) { | |
CommonLibBinderFree (TempFv); | |
return Status; | |
} | |
} | |
} | |
CommonLibBinderCopyMem (Fv, TempFv, FvLength); | |
CommonLibBinderFree (TempFv); | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
FvBufChecksumFile ( | |
IN OUT VOID *FfsFile | |
) | |
/*++ | |
Routine Description: | |
Clears out all files from the Fv buffer in memory | |
Arguments: | |
SourceFfsFile - Input FFS file to update the checksum for | |
Returns: | |
EFI_SUCCESS | |
EFI_NOT_FOUND | |
--*/ | |
{ | |
EFI_FFS_FILE_HEADER* File = (EFI_FFS_FILE_HEADER*)FfsFile; | |
EFI_FFS_FILE_STATE StateBackup; | |
UINT32 FileSize; | |
FileSize = FvBufGetFfsFileSize (File); | |
// | |
// Fill in checksums and state, they must be 0 for checksumming. | |
// | |
File->IntegrityCheck.Checksum.Header = 0; | |
File->IntegrityCheck.Checksum.File = 0; | |
StateBackup = File->State; | |
File->State = 0; | |
File->IntegrityCheck.Checksum.Header = | |
FvBufCalculateChecksum8 ( | |
(UINT8 *) File, | |
FvBufGetFfsHeaderSize (File) | |
); | |
if (File->Attributes & FFS_ATTRIB_CHECKSUM) { | |
File->IntegrityCheck.Checksum.File = FvBufCalculateChecksum8 ( | |
(VOID*)((UINT8 *)File + FvBufGetFfsHeaderSize (File)), | |
FileSize - FvBufGetFfsHeaderSize (File) | |
); | |
} else { | |
File->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM; | |
} | |
File->State = StateBackup; | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
FvBufChecksumHeader ( | |
IN OUT VOID *Fv | |
) | |
/*++ | |
Routine Description: | |
Clears out all files from the Fv buffer in memory | |
Arguments: | |
SourceFv - Address of the Fv in memory, this firmware volume volume will | |
be modified, if SourceFfsFile exists | |
SourceFfsFile - Input FFS file to replace | |
Returns: | |
EFI_SUCCESS | |
EFI_NOT_FOUND | |
--*/ | |
{ | |
EFI_FIRMWARE_VOLUME_HEADER* FvHeader = (EFI_FIRMWARE_VOLUME_HEADER*)Fv; | |
FvHeader->Checksum = 0; | |
FvHeader->Checksum = | |
FvBufCalculateChecksum16 ( | |
(UINT16*) FvHeader, | |
FvHeader->HeaderLength / sizeof (UINT16) | |
); | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
FvBufDuplicate ( | |
IN VOID *SourceFv, | |
IN OUT VOID **DestinationFv | |
) | |
/*++ | |
Routine Description: | |
Clears out all files from the Fv buffer in memory | |
Arguments: | |
SourceFv - Address of the Fv in memory | |
DestinationFv - Output for destination Fv | |
DestinationFv == NULL - invalid parameter | |
*DestinationFv == NULL - memory will be allocated | |
*DestinationFv != NULL - this address will be the destination | |
Returns: | |
EFI_SUCCESS | |
--*/ | |
{ | |
EFI_STATUS Status; | |
UINTN size; | |
if (DestinationFv == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Status = FvBufGetSize (SourceFv, &size); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (*DestinationFv == NULL) { | |
*DestinationFv = CommonLibBinderAllocate (size); | |
if (*DestinationFv == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
} | |
CommonLibBinderCopyMem (*DestinationFv, SourceFv, size); | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
FvBufExtend ( | |
IN VOID **Fv, | |
IN UINTN Size | |
) | |
/*++ | |
Routine Description: | |
Extends a firmware volume by the given number of bytes. | |
BUGBUG: Does not handle the case where the firmware volume has a | |
VTF (Volume Top File). The VTF will not be moved to the | |
end of the extended FV. | |
Arguments: | |
Fv - Source and destination firmware volume. | |
Note: The original firmware volume buffer is freed! | |
Size - The minimum size that the firmware volume is to be extended by. | |
The FV may be extended more than this size. | |
Returns: | |
EFI_SUCCESS | |
--*/ | |
{ | |
EFI_STATUS Status; | |
UINTN OldSize; | |
UINTN NewSize; | |
UINTN BlockCount; | |
VOID* NewFv; | |
EFI_FIRMWARE_VOLUME_HEADER* hdr; | |
EFI_FV_BLOCK_MAP_ENTRY* blk; | |
Status = FvBufGetSize (*Fv, &OldSize); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Locate the block map in the fv header | |
// | |
hdr = (EFI_FIRMWARE_VOLUME_HEADER*)*Fv; | |
blk = hdr->BlockMap; | |
// | |
// Calculate the number of blocks needed to achieve the requested | |
// size extension | |
// | |
BlockCount = ((Size + (blk->Length - 1)) / blk->Length); | |
// | |
// Calculate the new size from the number of blocks that will be added | |
// | |
NewSize = OldSize + (BlockCount * blk->Length); | |
NewFv = CommonLibBinderAllocate (NewSize); | |
if (NewFv == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
// | |
// Copy the old data | |
// | |
CommonLibBinderCopyMem (NewFv, *Fv, OldSize); | |
// | |
// Free the old fv buffer | |
// | |
CommonLibBinderFree (*Fv); | |
// | |
// Locate the block map in the new fv header | |
// | |
hdr = (EFI_FIRMWARE_VOLUME_HEADER*)NewFv; | |
hdr->FvLength = NewSize; | |
blk = hdr->BlockMap; | |
// | |
// Update the block map for the new fv | |
// | |
blk->NumBlocks += (UINT32)BlockCount; | |
// | |
// Update the FV header checksum | |
// | |
FvBufChecksumHeader (NewFv); | |
// | |
// Clear out the new area of the FV | |
// | |
CommonLibBinderSetMem ( | |
(UINT8*)NewFv + OldSize, | |
(NewSize - OldSize), | |
(hdr->Attributes & EFI_FVB2_ERASE_POLARITY) ? 0xFF : 0 | |
); | |
// | |
// Set output with new fv that was created | |
// | |
*Fv = NewFv; | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
FvBufClearAllFiles ( | |
IN OUT VOID *Fv | |
) | |
/*++ | |
Routine Description: | |
Clears out all files from the Fv buffer in memory | |
Arguments: | |
Fv - Address of the Fv in memory | |
Returns: | |
EFI_SUCCESS | |
--*/ | |
{ | |
EFI_FIRMWARE_VOLUME_HEADER *hdr = (EFI_FIRMWARE_VOLUME_HEADER*)Fv; | |
EFI_STATUS Status; | |
UINTN size = 0; | |
Status = FvBufGetSize (Fv, &size); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
CommonLibBinderSetMem( | |
(UINT8*)hdr + hdr->HeaderLength, | |
size - hdr->HeaderLength, | |
(hdr->Attributes & EFI_FVB2_ERASE_POLARITY) ? 0xFF : 0 | |
); | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
FvBufGetSize ( | |
IN VOID *Fv, | |
OUT UINTN *Size | |
) | |
/*++ | |
Routine Description: | |
Clears out all files from the Fv buffer in memory | |
Arguments: | |
Fv - Address of the Fv in memory | |
Returns: | |
EFI_SUCCESS | |
--*/ | |
{ | |
EFI_FIRMWARE_VOLUME_HEADER *hdr = (EFI_FIRMWARE_VOLUME_HEADER*)Fv; | |
EFI_FV_BLOCK_MAP_ENTRY *blk = hdr->BlockMap; | |
*Size = 0; | |
while (blk->Length != 0 || blk->NumBlocks != 0) { | |
*Size = *Size + (blk->Length * blk->NumBlocks); | |
if (*Size >= 0x40000000) { | |
// If size is greater than 1GB, then assume it is corrupted | |
return EFI_VOLUME_CORRUPTED; | |
} | |
blk++; | |
} | |
if (*Size == 0) { | |
// If size is 0, then assume the volume is corrupted | |
return EFI_VOLUME_CORRUPTED; | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
FvBufAddFile ( | |
IN OUT VOID *Fv, | |
IN VOID *File | |
) | |
/*++ | |
Routine Description: | |
Adds a new FFS file | |
Arguments: | |
Fv - Address of the Fv in memory | |
File - FFS file to add to Fv | |
Returns: | |
EFI_SUCCESS | |
--*/ | |
{ | |
EFI_FIRMWARE_VOLUME_HEADER *hdr = (EFI_FIRMWARE_VOLUME_HEADER*)Fv; | |
EFI_FFS_FILE_HEADER *fhdr = NULL; | |
EFI_FVB_ATTRIBUTES_2 FvbAttributes; | |
UINTN offset; | |
UINTN fsize; | |
UINTN newSize; | |
UINTN clearLoop; | |
EFI_STATUS Status; | |
UINTN fvSize; | |
Status = FvBufGetSize (Fv, &fvSize); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
FvbAttributes = hdr->Attributes; | |
newSize = FvBufGetFfsFileSize ((EFI_FFS_FILE_HEADER*)File); | |
for( | |
offset = (UINTN)ALIGN_POINTER (hdr->HeaderLength, 8); | |
offset + newSize <= fvSize; | |
offset = (UINTN)ALIGN_POINTER (offset, 8) | |
) { | |
fhdr = (EFI_FFS_FILE_HEADER*) ((UINT8*)hdr + offset); | |
if (EFI_TEST_FFS_ATTRIBUTES_BIT( | |
FvbAttributes, | |
fhdr->State, | |
EFI_FILE_HEADER_VALID | |
) | |
) { | |
// BUGBUG: Need to make sure that the new file does not already | |
// exist. | |
fsize = FvBufGetFfsFileSize (fhdr); | |
if (fsize == 0 || (offset + fsize > fvSize)) { | |
return EFI_VOLUME_CORRUPTED; | |
} | |
offset = offset + fsize; | |
continue; | |
} | |
clearLoop = 0; | |
while ((clearLoop < newSize) && | |
(((UINT8*)fhdr)[clearLoop] == | |
(UINT8)((hdr->Attributes & EFI_FVB2_ERASE_POLARITY) ? 0xFF : 0) | |
) | |
) { | |
clearLoop++; | |
} | |
// | |
// We found a place in the FV which is empty and big enough for | |
// the new file | |
// | |
if (clearLoop >= newSize) { | |
break; | |
} | |
offset = offset + 1; // Make some forward progress | |
} | |
if (offset + newSize > fvSize) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
CommonLibBinderCopyMem (fhdr, File, newSize); | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
FvBufAddFileWithExtend ( | |
IN OUT VOID **Fv, | |
IN VOID *File | |
) | |
/*++ | |
Routine Description: | |
Adds a new FFS file. Extends the firmware volume if needed. | |
Arguments: | |
Fv - Source and destination firmware volume. | |
Note: If the FV is extended, then the original firmware volume | |
buffer is freed! | |
Size - The minimum size that the firmware volume is to be extended by. | |
The FV may be extended more than this size. | |
Returns: | |
EFI_SUCCESS | |
--*/ | |
{ | |
EFI_STATUS Status; | |
EFI_FFS_FILE_HEADER* NewFile; | |
NewFile = (EFI_FFS_FILE_HEADER*)File; | |
// | |
// Try to add to the capsule volume | |
// | |
Status = FvBufAddFile (*Fv, NewFile); | |
if (Status == EFI_OUT_OF_RESOURCES) { | |
// | |
// Try to extend the capsule volume by the size of the file | |
// | |
Status = FvBufExtend (Fv, FvBufExpand3ByteSize (NewFile->Size)); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Now, try to add the file again | |
// | |
Status = FvBufAddFile (*Fv, NewFile); | |
} | |
return Status; | |
} | |
EFI_STATUS | |
FvBufAddVtfFile ( | |
IN OUT VOID *Fv, | |
IN VOID *File | |
) | |
/*++ | |
Routine Description: | |
Adds a new FFS VFT (Volume Top File) file. In other words, adds the | |
file to the end of the firmware volume. | |
Arguments: | |
Fv - Address of the Fv in memory | |
File - FFS file to add to Fv | |
Returns: | |
EFI_SUCCESS | |
--*/ | |
{ | |
EFI_STATUS Status; | |
EFI_FIRMWARE_VOLUME_HEADER *hdr = (EFI_FIRMWARE_VOLUME_HEADER*)Fv; | |
EFI_FFS_FILE_HEADER* NewFile; | |
UINTN NewFileSize; | |
UINT8 erasedUint8; | |
UINTN clearLoop; | |
EFI_FFS_FILE_HEADER *LastFile; | |
UINTN LastFileSize; | |
UINTN fvSize; | |
UINTN Key; | |
Status = FvBufGetSize (Fv, &fvSize); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
erasedUint8 = (UINT8)((hdr->Attributes & EFI_FVB2_ERASE_POLARITY) ? 0xFF : 0); | |
NewFileSize = FvBufGetFfsFileSize ((EFI_FFS_FILE_HEADER*)File); | |
if (NewFileSize != (UINTN)ALIGN_POINTER (NewFileSize, 8)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Find the last file in the FV | |
// | |
Key = 0; | |
LastFile = NULL; | |
LastFileSize = 0; | |
do { | |
Status = FvBufFindNextFile (Fv, &Key, (VOID **)&LastFile); | |
LastFileSize = FvBufGetFfsFileSize ((EFI_FFS_FILE_HEADER*)File); | |
} while (!EFI_ERROR (Status)); | |
// | |
// If no files were found, then we start at the beginning of the FV | |
// | |
if (LastFile == NULL) { | |
LastFile = (EFI_FFS_FILE_HEADER*)((UINT8*)hdr + hdr->HeaderLength); | |
} | |
// | |
// We want to put the new file (VTF) at the end of the FV | |
// | |
NewFile = (EFI_FFS_FILE_HEADER*)((UINT8*)hdr + (fvSize - NewFileSize)); | |
// | |
// Check to see if there is enough room for the VTF after the last file | |
// found in the FV | |
// | |
if ((UINT8*)NewFile < ((UINT8*)LastFile + LastFileSize)) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
// | |
// Loop to determine if the end of the FV is empty | |
// | |
clearLoop = 0; | |
while ((clearLoop < NewFileSize) && | |
(((UINT8*)NewFile)[clearLoop] == erasedUint8) | |
) { | |
clearLoop++; | |
} | |
// | |
// Check to see if there was not enough room for the file | |
// | |
if (clearLoop < NewFileSize) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
CommonLibBinderCopyMem (NewFile, File, NewFileSize); | |
return EFI_SUCCESS; | |
} | |
VOID | |
FvBufCompact3ByteSize ( | |
OUT VOID* SizeDest, | |
IN UINT32 Size | |
) | |
/*++ | |
Routine Description: | |
Expands the 3 byte size commonly used in Firmware Volume data structures | |
Arguments: | |
Size - Address of the 3 byte array representing the size | |
Returns: | |
UINT32 | |
--*/ | |
{ | |
((UINT8*)SizeDest)[0] = (UINT8)Size; | |
((UINT8*)SizeDest)[1] = (UINT8)(Size >> 8); | |
((UINT8*)SizeDest)[2] = (UINT8)(Size >> 16); | |
} | |
UINT32 | |
FvBufGetFfsFileSize ( | |
IN EFI_FFS_FILE_HEADER *Ffs | |
) | |
/*++ | |
Routine Description: | |
Get the FFS file size. | |
Arguments: | |
Ffs - Pointer to FFS header | |
Returns: | |
UINT32 | |
--*/ | |
{ | |
if (Ffs == NULL) { | |
return 0; | |
} | |
if (Ffs->Attributes & FFS_ATTRIB_LARGE_FILE) { | |
return (UINT32) ((EFI_FFS_FILE_HEADER2 *)Ffs)->ExtendedSize; | |
} | |
return FvBufExpand3ByteSize(Ffs->Size); | |
} | |
UINT32 | |
FvBufGetFfsHeaderSize ( | |
IN EFI_FFS_FILE_HEADER *Ffs | |
) | |
/*++ | |
Routine Description: | |
Get the FFS header size. | |
Arguments: | |
Ffs - Pointer to FFS header | |
Returns: | |
UINT32 | |
--*/ | |
{ | |
if (Ffs == NULL) { | |
return 0; | |
} | |
if (Ffs->Attributes & FFS_ATTRIB_LARGE_FILE) { | |
return sizeof(EFI_FFS_FILE_HEADER2); | |
} | |
return sizeof(EFI_FFS_FILE_HEADER); | |
} | |
UINT32 | |
FvBufExpand3ByteSize ( | |
IN VOID* Size | |
) | |
/*++ | |
Routine Description: | |
Expands the 3 byte size commonly used in Firmware Volume data structures | |
Arguments: | |
Size - Address of the 3 byte array representing the size | |
Returns: | |
UINT32 | |
--*/ | |
{ | |
return (((UINT8*)Size)[2] << 16) + | |
(((UINT8*)Size)[1] << 8) + | |
((UINT8*)Size)[0]; | |
} | |
EFI_STATUS | |
FvBufFindNextFile ( | |
IN VOID *Fv, | |
IN OUT UINTN *Key, | |
OUT VOID **File | |
) | |
/*++ | |
Routine Description: | |
Iterates through the files contained within the firmware volume | |
Arguments: | |
Fv - Address of the Fv in memory | |
Key - Should be 0 to get the first file. After that, it should be | |
passed back in without modifying it's contents to retrieve | |
subsequent files. | |
File - Output file pointer | |
File == NULL - invalid parameter | |
otherwise - *File will be update to the location of the file | |
Returns: | |
EFI_SUCCESS | |
EFI_NOT_FOUND | |
EFI_VOLUME_CORRUPTED | |
--*/ | |
{ | |
EFI_FIRMWARE_VOLUME_HEADER *hdr = (EFI_FIRMWARE_VOLUME_HEADER*)Fv; | |
EFI_FFS_FILE_HEADER *fhdr = NULL; | |
EFI_FVB_ATTRIBUTES_2 FvbAttributes; | |
UINTN fsize; | |
EFI_STATUS Status; | |
UINTN fvSize; | |
if (Fv == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Status = FvBufGetSize (Fv, &fvSize); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (*Key == 0) { | |
*Key = hdr->HeaderLength; | |
} | |
FvbAttributes = hdr->Attributes; | |
for( | |
*Key = (UINTN)ALIGN_POINTER (*Key, 8); | |
(*Key + sizeof (*fhdr)) < fvSize; | |
*Key = (UINTN)ALIGN_POINTER (*Key, 8) | |
) { | |
fhdr = (EFI_FFS_FILE_HEADER*) ((UINT8*)hdr + *Key); | |
fsize = FvBufGetFfsFileSize (fhdr); | |
if (!EFI_TEST_FFS_ATTRIBUTES_BIT( | |
FvbAttributes, | |
fhdr->State, | |
EFI_FILE_HEADER_VALID | |
) || | |
EFI_TEST_FFS_ATTRIBUTES_BIT( | |
FvbAttributes, | |
fhdr->State, | |
EFI_FILE_HEADER_INVALID | |
) | |
) { | |
*Key = *Key + 1; // Make some forward progress | |
continue; | |
} else if( | |
EFI_TEST_FFS_ATTRIBUTES_BIT( | |
FvbAttributes, | |
fhdr->State, | |
EFI_FILE_MARKED_FOR_UPDATE | |
) || | |
EFI_TEST_FFS_ATTRIBUTES_BIT( | |
FvbAttributes, | |
fhdr->State, | |
EFI_FILE_DELETED | |
) | |
) { | |
*Key = *Key + fsize; | |
continue; | |
} else if (EFI_TEST_FFS_ATTRIBUTES_BIT( | |
FvbAttributes, | |
fhdr->State, | |
EFI_FILE_DATA_VALID | |
) | |
) { | |
*File = (UINT8*)hdr + *Key; | |
*Key = *Key + fsize; | |
return EFI_SUCCESS; | |
} | |
*Key = *Key + 1; // Make some forward progress | |
} | |
return EFI_NOT_FOUND; | |
} | |
EFI_STATUS | |
FvBufFindFileByName ( | |
IN VOID *Fv, | |
IN EFI_GUID *Name, | |
OUT VOID **File | |
) | |
/*++ | |
Routine Description: | |
Searches the Fv for a file by its name | |
Arguments: | |
Fv - Address of the Fv in memory | |
Name - Guid filename to search for in the firmware volume | |
File - Output file pointer | |
File == NULL - Only determine if the file exists, based on return | |
value from the function call. | |
otherwise - *File will be update to the location of the file | |
Returns: | |
EFI_SUCCESS | |
EFI_NOT_FOUND | |
EFI_VOLUME_CORRUPTED | |
--*/ | |
{ | |
EFI_STATUS Status; | |
UINTN Key; | |
EFI_FFS_FILE_HEADER *NextFile; | |
Key = 0; | |
while (TRUE) { | |
Status = FvBufFindNextFile (Fv, &Key, (VOID **)&NextFile); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (CommonLibBinderCompareGuid (Name, &NextFile->Name)) { | |
if (File != NULL) { | |
*File = NextFile; | |
} | |
return EFI_SUCCESS; | |
} | |
} | |
return EFI_NOT_FOUND; | |
} | |
EFI_STATUS | |
FvBufFindFileByType ( | |
IN VOID *Fv, | |
IN EFI_FV_FILETYPE Type, | |
OUT VOID **File | |
) | |
/*++ | |
Routine Description: | |
Searches the Fv for a file by its type | |
Arguments: | |
Fv - Address of the Fv in memory | |
Type - FFS FILE type to search for | |
File - Output file pointer | |
(File == NULL) -> Only determine if the file exists, based on return | |
value from the function call. | |
otherwise -> *File will be update to the location of the file | |
Returns: | |
EFI_SUCCESS | |
EFI_NOT_FOUND | |
EFI_VOLUME_CORRUPTED | |
--*/ | |
{ | |
EFI_STATUS Status; | |
UINTN Key; | |
EFI_FFS_FILE_HEADER *NextFile; | |
Key = 0; | |
while (TRUE) { | |
Status = FvBufFindNextFile (Fv, &Key, (VOID **)&NextFile); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (Type == NextFile->Type) { | |
if (File != NULL) { | |
*File = NextFile; | |
} | |
return EFI_SUCCESS; | |
} | |
} | |
return EFI_NOT_FOUND; | |
} | |
EFI_STATUS | |
FvBufGetFileRawData ( | |
IN VOID* FfsFile, | |
OUT VOID** RawData, | |
OUT UINTN* RawDataSize | |
) | |
/*++ | |
Routine Description: | |
Searches the requested file for raw data. | |
This routine either returns all the payload of a EFI_FV_FILETYPE_RAW file, | |
or finds the EFI_SECTION_RAW section within the file and returns its data. | |
Arguments: | |
FfsFile - Address of the FFS file in memory | |
RawData - Pointer to the raw data within the file | |
(This is NOT allocated. It is within the file.) | |
RawDataSize - Size of the raw data within the file | |
Returns: | |
EFI_STATUS | |
--*/ | |
{ | |
EFI_STATUS Status; | |
EFI_FFS_FILE_HEADER* File; | |
EFI_RAW_SECTION* Section; | |
File = (EFI_FFS_FILE_HEADER*)FfsFile; | |
// | |
// Is the file type == EFI_FV_FILETYPE_RAW? | |
// | |
if (File->Type == EFI_FV_FILETYPE_RAW) { | |
// | |
// Raw filetypes don't have sections, so we just return the raw data | |
// | |
*RawData = (VOID*)((UINT8 *)File + FvBufGetFfsHeaderSize (File)); | |
*RawDataSize = FvBufGetFfsFileSize (File) - FvBufGetFfsHeaderSize (File); | |
return EFI_SUCCESS; | |
} | |
// | |
// Within the file, we now need to find the EFI_SECTION_RAW section. | |
// | |
Status = FvBufFindSectionByType (File, EFI_SECTION_RAW, (VOID **)&Section); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
*RawData = (VOID*)((UINT8 *)Section + FvBufGetSecHdrLen(Section)); | |
*RawDataSize = | |
FvBufGetSecFileLen (Section) - FvBufGetSecHdrLen(Section); | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
FvBufPackageFreeformRawFile ( | |
IN EFI_GUID* Filename, | |
IN VOID* RawData, | |
IN UINTN RawDataSize, | |
OUT VOID** FfsFile | |
) | |
/*++ | |
Routine Description: | |
Packages up a FFS file containing the input raw data. | |
The file created will have a type of EFI_FV_FILETYPE_FREEFORM, and will | |
contain one EFI_FV_FILETYPE_RAW section. | |
Arguments: | |
RawData - Pointer to the raw data to be packed | |
RawDataSize - Size of the raw data to be packed | |
FfsFile - Address of the packaged FFS file. | |
Note: The called must deallocate this memory! | |
Returns: | |
EFI_STATUS | |
--*/ | |
{ | |
EFI_FFS_FILE_HEADER* NewFile; | |
UINT32 NewFileSize; | |
EFI_RAW_SECTION* NewSection; | |
UINT32 NewSectionSize; | |
UINT32 FfsHdrLen; | |
UINT32 SecHdrLen; | |
// | |
// The section size is the DataSize + the size of the section header | |
// | |
NewSectionSize = (UINT32)sizeof (EFI_RAW_SECTION) + (UINT32)RawDataSize; | |
SecHdrLen = sizeof (EFI_RAW_SECTION); | |
if (NewSectionSize >= MAX_SECTION_SIZE) { | |
NewSectionSize = (UINT32)sizeof (EFI_RAW_SECTION2) + (UINT32)RawDataSize; | |
SecHdrLen = sizeof (EFI_RAW_SECTION2); | |
} | |
// | |
// The file size is the size of the file header + the section size | |
// | |
NewFileSize = sizeof (EFI_FFS_FILE_HEADER) + NewSectionSize; | |
FfsHdrLen = sizeof (EFI_FFS_FILE_HEADER); | |
if (NewFileSize >= MAX_FFS_SIZE) { | |
NewFileSize = sizeof (EFI_FFS_FILE_HEADER2) + NewSectionSize; | |
FfsHdrLen = sizeof (EFI_FFS_FILE_HEADER2); | |
} | |
// | |
// Try to allocate a buffer to build the new FFS file in | |
// | |
NewFile = CommonLibBinderAllocate (NewFileSize); | |
if (NewFile == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
CommonLibBinderSetMem (NewFile, NewFileSize, 0); | |
// | |
// The NewSection follow right after the FFS file header | |
// | |
NewSection = (EFI_RAW_SECTION*)((UINT8*)NewFile + FfsHdrLen); | |
if (NewSectionSize >= MAX_SECTION_SIZE) { | |
FvBufCompact3ByteSize (NewSection->Size, 0xffffff); | |
((EFI_RAW_SECTION2 *)NewSection)->ExtendedSize = NewSectionSize; | |
} else { | |
FvBufCompact3ByteSize (NewSection->Size, NewSectionSize); | |
} | |
NewSection->Type = EFI_SECTION_RAW; | |
// | |
// Copy the actual file data into the buffer | |
// | |
CommonLibBinderCopyMem ((UINT8 *)NewSection + SecHdrLen, RawData, RawDataSize); | |
// | |
// Initialize the FFS file header | |
// | |
CommonLibBinderCopyMem (&NewFile->Name, Filename, sizeof (EFI_GUID)); | |
NewFile->Attributes = 0; | |
if (NewFileSize >= MAX_FFS_SIZE) { | |
FvBufCompact3ByteSize (NewFile->Size, 0x0); | |
((EFI_FFS_FILE_HEADER2 *)NewFile)->ExtendedSize = NewFileSize; | |
NewFile->Attributes |= FFS_ATTRIB_LARGE_FILE; | |
} else { | |
FvBufCompact3ByteSize (NewFile->Size, NewFileSize); | |
} | |
NewFile->Type = EFI_FV_FILETYPE_FREEFORM; | |
NewFile->IntegrityCheck.Checksum.Header = | |
FvBufCalculateChecksum8 ((UINT8*)NewFile, FfsHdrLen); | |
NewFile->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM; | |
NewFile->State = (UINT8)~( EFI_FILE_HEADER_CONSTRUCTION | | |
EFI_FILE_HEADER_VALID | | |
EFI_FILE_DATA_VALID | |
); | |
*FfsFile = NewFile; | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
FvBufFindNextSection ( | |
IN VOID *SectionsStart, | |
IN UINTN TotalSectionsSize, | |
IN OUT UINTN *Key, | |
OUT VOID **Section | |
) | |
/*++ | |
Routine Description: | |
Iterates through the sections contained within a given array of sections | |
Arguments: | |
SectionsStart - Address of the start of the FFS sections array | |
TotalSectionsSize - Total size of all the sections | |
Key - Should be 0 to get the first section. After that, it should be | |
passed back in without modifying it's contents to retrieve | |
subsequent files. | |
Section - Output section pointer | |
(Section == NULL) -> invalid parameter | |
otherwise -> *Section will be update to the location of the file | |
Returns: | |
EFI_SUCCESS | |
EFI_NOT_FOUND | |
EFI_VOLUME_CORRUPTED | |
--*/ | |
{ | |
EFI_COMMON_SECTION_HEADER *sectionHdr; | |
UINTN sectionSize; | |
*Key = (UINTN)ALIGN_POINTER (*Key, 4); // Sections are DWORD aligned | |
if ((*Key + sizeof (*sectionHdr)) > TotalSectionsSize) { | |
return EFI_NOT_FOUND; | |
} | |
sectionHdr = (EFI_COMMON_SECTION_HEADER*)((UINT8*)SectionsStart + *Key); | |
sectionSize = FvBufGetSecFileLen (sectionHdr); | |
if (sectionSize < sizeof (EFI_COMMON_SECTION_HEADER)) { | |
return EFI_NOT_FOUND; | |
} | |
if ((*Key + sectionSize) > TotalSectionsSize) { | |
return EFI_NOT_FOUND; | |
} | |
*Section = (UINT8*)sectionHdr; | |
*Key = *Key + sectionSize; | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
FvBufCountSections ( | |
IN VOID* FfsFile, | |
IN UINTN* Count | |
) | |
/*++ | |
Routine Description: | |
Searches the FFS file and counts the number of sections found. | |
The sections are NOT recursed. | |
Arguments: | |
FfsFile - Address of the FFS file in memory | |
Count - The location to store the section count in | |
Returns: | |
EFI_SUCCESS | |
EFI_NOT_FOUND | |
EFI_VOLUME_CORRUPTED | |
--*/ | |
{ | |
EFI_STATUS Status; | |
UINTN Key; | |
VOID* SectionStart; | |
UINTN TotalSectionsSize; | |
EFI_COMMON_SECTION_HEADER* NextSection; | |
SectionStart = (VOID*)((UINTN)FfsFile + FvBufGetFfsHeaderSize(FfsFile)); | |
TotalSectionsSize = | |
FvBufGetFfsFileSize ((EFI_FFS_FILE_HEADER*)FfsFile) - | |
FvBufGetFfsHeaderSize(FfsFile); | |
Key = 0; | |
*Count = 0; | |
while (TRUE) { | |
Status = FvBufFindNextSection ( | |
SectionStart, | |
TotalSectionsSize, | |
&Key, | |
(VOID **)&NextSection | |
); | |
if (Status == EFI_NOT_FOUND) { | |
return EFI_SUCCESS; | |
} else if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Increment the section counter | |
// | |
*Count += 1; | |
} | |
return EFI_NOT_FOUND; | |
} | |
EFI_STATUS | |
FvBufFindSectionByType ( | |
IN VOID *FfsFile, | |
IN UINT8 Type, | |
OUT VOID **Section | |
) | |
/*++ | |
Routine Description: | |
Searches the FFS file for a section by its type | |
Arguments: | |
FfsFile - Address of the FFS file in memory | |
Type - FFS FILE section type to search for | |
Section - Output section pointer | |
(Section == NULL) -> Only determine if the section exists, based on return | |
value from the function call. | |
otherwise -> *Section will be update to the location of the file | |
Returns: | |
EFI_SUCCESS | |
EFI_NOT_FOUND | |
EFI_VOLUME_CORRUPTED | |
--*/ | |
{ | |
EFI_STATUS Status; | |
UINTN Key; | |
VOID* SectionStart; | |
UINTN TotalSectionsSize; | |
EFI_COMMON_SECTION_HEADER* NextSection; | |
SectionStart = (VOID*)((UINTN)FfsFile + FvBufGetFfsHeaderSize(FfsFile)); | |
TotalSectionsSize = | |
FvBufGetFfsFileSize ((EFI_FFS_FILE_HEADER*)FfsFile) - | |
FvBufGetFfsHeaderSize(FfsFile); | |
Key = 0; | |
while (TRUE) { | |
Status = FvBufFindNextSection ( | |
SectionStart, | |
TotalSectionsSize, | |
&Key, | |
(VOID **)&NextSection | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (Type == NextSection->Type) { | |
if (Section != NULL) { | |
*Section = NextSection; | |
} | |
return EFI_SUCCESS; | |
} | |
} | |
return EFI_NOT_FOUND; | |
} | |
EFI_STATUS | |
FvBufShrinkWrap ( | |
IN VOID *Fv | |
) | |
/*++ | |
Routine Description: | |
Shrinks a firmware volume (in place) to provide a minimal FV. | |
BUGBUG: Does not handle the case where the firmware volume has a | |
VTF (Volume Top File). The VTF will not be moved to the | |
end of the extended FV. | |
Arguments: | |
Fv - Firmware volume. | |
Returns: | |
EFI_SUCCESS | |
--*/ | |
{ | |
EFI_STATUS Status; | |
UINTN OldSize; | |
UINT32 BlockCount; | |
UINT32 NewBlockSize = 128; | |
UINTN Key; | |
EFI_FFS_FILE_HEADER* FileIt; | |
VOID* EndOfLastFile; | |
EFI_FIRMWARE_VOLUME_HEADER* FvHdr; | |
Status = FvBufGetSize (Fv, &OldSize); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = FvBufUnifyBlockSizes (Fv, NewBlockSize); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Locate the block map in the fv header | |
// | |
FvHdr = (EFI_FIRMWARE_VOLUME_HEADER*)Fv; | |
// | |
// Find the end of the last file | |
// | |
Key = 0; | |
EndOfLastFile = (UINT8*)FvHdr + FvHdr->FvLength; | |
while (!EFI_ERROR (FvBufFindNextFile (Fv, &Key, (VOID **)&FileIt))) { | |
EndOfLastFile = | |
(VOID*)((UINT8*)FileIt + FvBufGetFfsFileSize (FileIt)); | |
} | |
// | |
// Set the BlockCount to have the minimal number of blocks for the Fv. | |
// | |
BlockCount = (UINT32)((UINTN)EndOfLastFile - (UINTN)Fv); | |
BlockCount = BlockCount + NewBlockSize - 1; | |
BlockCount = BlockCount / NewBlockSize; | |
// | |
// Adjust the block count to shrink the Fv in place. | |
// | |
FvHdr->BlockMap[0].NumBlocks = BlockCount; | |
FvHdr->FvLength = BlockCount * NewBlockSize; | |
// | |
// Update the FV header checksum | |
// | |
FvBufChecksumHeader (Fv); | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
FvBufUnifyBlockSizes ( | |
IN OUT VOID *Fv, | |
IN UINTN BlockSize | |
) | |
/*++ | |
Routine Description: | |
Searches the FFS file for a section by its type | |
Arguments: | |
Fv - Address of the Fv in memory | |
BlockSize - The size of the blocks to convert the Fv to. If the total size | |
of the Fv is not evenly divisible by this size, then | |
EFI_INVALID_PARAMETER will be returned. | |
Returns: | |
EFI_SUCCESS | |
EFI_NOT_FOUND | |
EFI_VOLUME_CORRUPTED | |
--*/ | |
{ | |
EFI_FIRMWARE_VOLUME_HEADER *hdr = (EFI_FIRMWARE_VOLUME_HEADER*)Fv; | |
EFI_FV_BLOCK_MAP_ENTRY *blk = hdr->BlockMap; | |
UINT32 Size; | |
Size = 0; | |
// | |
// Scan through the block map list, performing error checking, and adding | |
// up the total Fv size. | |
// | |
while( blk->Length != 0 || | |
blk->NumBlocks != 0 | |
) { | |
Size = Size + (blk->Length * blk->NumBlocks); | |
blk++; | |
if ((UINT8*)blk > ((UINT8*)hdr + hdr->HeaderLength)) { | |
return EFI_VOLUME_CORRUPTED; | |
} | |
} | |
// | |
// Make sure that the Fv size is a multiple of the new block size. | |
// | |
if ((Size % BlockSize) != 0) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Zero out the entire block map. | |
// | |
CommonLibBinderSetMem ( | |
&hdr->BlockMap, | |
(UINTN)blk - (UINTN)&hdr->BlockMap, | |
0 | |
); | |
// | |
// Write out the single block map entry. | |
// | |
hdr->BlockMap[0].Length = (UINT32)BlockSize; | |
hdr->BlockMap[0].NumBlocks = Size / (UINT32)BlockSize; | |
return EFI_SUCCESS; | |
} | |
STATIC | |
UINT16 | |
FvBufCalculateSum16 ( | |
IN UINT16 *Buffer, | |
IN UINTN Size | |
) | |
/*++ | |
Routine Description: | |
This function calculates the UINT16 sum for the requested region. | |
Arguments: | |
Buffer Pointer to buffer containing byte data of component. | |
Size Size of the buffer | |
Returns: | |
The 16 bit checksum | |
--*/ | |
{ | |
UINTN Index; | |
UINT16 Sum; | |
Sum = 0; | |
// | |
// Perform the word sum for buffer | |
// | |
for (Index = 0; Index < Size; Index++) { | |
Sum = (UINT16) (Sum + Buffer[Index]); | |
} | |
return (UINT16) Sum; | |
} | |
STATIC | |
UINT16 | |
FvBufCalculateChecksum16 ( | |
IN UINT16 *Buffer, | |
IN UINTN Size | |
) | |
/*++ | |
Routine Description:: | |
This function calculates the value needed for a valid UINT16 checksum | |
Arguments: | |
Buffer Pointer to buffer containing byte data of component. | |
Size Size of the buffer | |
Returns: | |
The 16 bit checksum value needed. | |
--*/ | |
{ | |
return (UINT16)(0x10000 - FvBufCalculateSum16 (Buffer, Size)); | |
} | |
STATIC | |
UINT8 | |
FvBufCalculateSum8 ( | |
IN UINT8 *Buffer, | |
IN UINTN Size | |
) | |
/*++ | |
Description: | |
This function calculates the UINT8 sum for the requested region. | |
Input: | |
Buffer Pointer to buffer containing byte data of component. | |
Size Size of the buffer | |
Return: | |
The 8 bit checksum value needed. | |
--*/ | |
{ | |
UINTN Index; | |
UINT8 Sum; | |
Sum = 0; | |
// | |
// Perform the byte sum for buffer | |
// | |
for (Index = 0; Index < Size; Index++) { | |
Sum = (UINT8) (Sum + Buffer[Index]); | |
} | |
return Sum; | |
} | |
STATIC | |
UINT8 | |
FvBufCalculateChecksum8 ( | |
IN UINT8 *Buffer, | |
IN UINTN Size | |
) | |
/*++ | |
Description: | |
This function calculates the value needed for a valid UINT8 checksum | |
Input: | |
Buffer Pointer to buffer containing byte data of component. | |
Size Size of the buffer | |
Return: | |
The 8 bit checksum value needed. | |
--*/ | |
{ | |
return (UINT8)(0x100 - FvBufCalculateSum8 (Buffer, Size)); | |
} | |