audk/BaseTools/Source/C/Common/FirmwareVolumeBuffer.c

1782 lines
36 KiB
C

/** @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));
}