mirror of https://github.com/acidanthera/audk.git
776 lines
22 KiB
C
776 lines
22 KiB
C
/** @file
|
|
Firmware File System driver that produce Firmware Volume protocol.
|
|
Layers on top of Firmware Block protocol to produce a file abstraction
|
|
of FV based files.
|
|
|
|
Copyright (c) 2006 - 2014, 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 "DxeMain.h"
|
|
#include "FwVolDriver.h"
|
|
|
|
|
|
//
|
|
// Protocol notify related globals
|
|
//
|
|
VOID *gEfiFwVolBlockNotifyReg;
|
|
EFI_EVENT gEfiFwVolBlockEvent;
|
|
|
|
FV_DEVICE mFvDevice = {
|
|
FV2_DEVICE_SIGNATURE,
|
|
NULL,
|
|
NULL,
|
|
{
|
|
FvGetVolumeAttributes,
|
|
FvSetVolumeAttributes,
|
|
FvReadFile,
|
|
FvReadFileSection,
|
|
FvWriteFile,
|
|
FvGetNextFile,
|
|
sizeof (UINTN),
|
|
NULL,
|
|
FvGetVolumeInfo,
|
|
FvSetVolumeInfo
|
|
},
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
{ NULL, NULL },
|
|
0,
|
|
0,
|
|
FALSE,
|
|
FALSE
|
|
};
|
|
|
|
|
|
//
|
|
// FFS helper functions
|
|
//
|
|
/**
|
|
Read data from Firmware Block by FVB protocol Read.
|
|
The data may cross the multi block ranges.
|
|
|
|
@param Fvb The FW_VOL_BLOCK_PROTOCOL instance from which to read data.
|
|
@param StartLba Pointer to StartLba.
|
|
On input, the start logical block index from which to read.
|
|
On output,the end logical block index after reading.
|
|
@param Offset Pointer to Offset
|
|
On input, offset into the block at which to begin reading.
|
|
On output, offset into the end block after reading.
|
|
@param DataSize Size of data to be read.
|
|
@param Data Pointer to Buffer that the data will be read into.
|
|
|
|
@retval EFI_SUCCESS Successfully read data from firmware block.
|
|
@retval others
|
|
**/
|
|
EFI_STATUS
|
|
ReadFvbData (
|
|
IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb,
|
|
IN OUT EFI_LBA *StartLba,
|
|
IN OUT UINTN *Offset,
|
|
IN UINTN DataSize,
|
|
OUT UINT8 *Data
|
|
)
|
|
{
|
|
UINTN BlockSize;
|
|
UINTN NumberOfBlocks;
|
|
UINTN BlockIndex;
|
|
UINTN ReadDataSize;
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// Try read data in current block
|
|
//
|
|
BlockIndex = 0;
|
|
ReadDataSize = DataSize;
|
|
Status = Fvb->Read (Fvb, *StartLba, *Offset, &ReadDataSize, Data);
|
|
if (Status == EFI_SUCCESS) {
|
|
*Offset += DataSize;
|
|
return EFI_SUCCESS;
|
|
} else if (Status != EFI_BAD_BUFFER_SIZE) {
|
|
//
|
|
// other error will direct return
|
|
//
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Data crosses the blocks, read data from next block
|
|
//
|
|
DataSize -= ReadDataSize;
|
|
Data += ReadDataSize;
|
|
*StartLba = *StartLba + 1;
|
|
while (DataSize > 0) {
|
|
Status = Fvb->GetBlockSize (Fvb, *StartLba, &BlockSize, &NumberOfBlocks);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Read data from the crossing blocks
|
|
//
|
|
BlockIndex = 0;
|
|
while (BlockIndex < NumberOfBlocks && DataSize >= BlockSize) {
|
|
Status = Fvb->Read (Fvb, *StartLba + BlockIndex, 0, &BlockSize, Data);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
Data += BlockSize;
|
|
DataSize -= BlockSize;
|
|
BlockIndex ++;
|
|
}
|
|
|
|
//
|
|
// Data doesn't exceed the current block range.
|
|
//
|
|
if (DataSize < BlockSize) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Data must be got from the next block range.
|
|
//
|
|
*StartLba += NumberOfBlocks;
|
|
}
|
|
|
|
//
|
|
// read the remaining data
|
|
//
|
|
if (DataSize > 0) {
|
|
Status = Fvb->Read (Fvb, *StartLba + BlockIndex, 0, &DataSize, Data);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update Lba and Offset used by the following read.
|
|
//
|
|
*StartLba += BlockIndex;
|
|
*Offset = DataSize;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Given the supplied FW_VOL_BLOCK_PROTOCOL, allocate a buffer for output and
|
|
copy the real length volume header into it.
|
|
|
|
@param Fvb The FW_VOL_BLOCK_PROTOCOL instance from which to
|
|
read the volume header
|
|
@param FwVolHeader Pointer to pointer to allocated buffer in which
|
|
the volume header is returned.
|
|
|
|
@retval EFI_OUT_OF_RESOURCES No enough buffer could be allocated.
|
|
@retval EFI_SUCCESS Successfully read volume header to the allocated
|
|
buffer.
|
|
@retval EFI_INVALID_PARAMETER The FV Header signature is not as expected or
|
|
the file system could not be understood.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
GetFwVolHeader (
|
|
IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb,
|
|
OUT EFI_FIRMWARE_VOLUME_HEADER **FwVolHeader
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_FIRMWARE_VOLUME_HEADER TempFvh;
|
|
UINTN FvhLength;
|
|
EFI_LBA StartLba;
|
|
UINTN Offset;
|
|
UINT8 *Buffer;
|
|
|
|
//
|
|
// Read the standard FV header
|
|
//
|
|
StartLba = 0;
|
|
Offset = 0;
|
|
FvhLength = sizeof (EFI_FIRMWARE_VOLUME_HEADER);
|
|
Status = ReadFvbData (Fvb, &StartLba, &Offset, FvhLength, (UINT8 *)&TempFvh);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Validate FV Header signature, if not as expected, continue.
|
|
//
|
|
if (TempFvh.Signature != EFI_FVH_SIGNATURE) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Check to see that the file system is indeed formatted in a way we can
|
|
// understand it...
|
|
//
|
|
if ((!CompareGuid (&TempFvh.FileSystemGuid, &gEfiFirmwareFileSystem2Guid)) &&
|
|
(!CompareGuid (&TempFvh.FileSystemGuid, &gEfiFirmwareFileSystem3Guid))) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Allocate a buffer for the caller
|
|
//
|
|
*FwVolHeader = AllocatePool (TempFvh.HeaderLength);
|
|
if (*FwVolHeader == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Copy the standard header into the buffer
|
|
//
|
|
CopyMem (*FwVolHeader, &TempFvh, sizeof (EFI_FIRMWARE_VOLUME_HEADER));
|
|
|
|
//
|
|
// Read the rest of the header
|
|
//
|
|
FvhLength = TempFvh.HeaderLength - sizeof (EFI_FIRMWARE_VOLUME_HEADER);
|
|
Buffer = (UINT8 *)*FwVolHeader + sizeof (EFI_FIRMWARE_VOLUME_HEADER);
|
|
Status = ReadFvbData (Fvb, &StartLba, &Offset, FvhLength, Buffer);
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// Read failed so free buffer
|
|
//
|
|
CoreFreePool (*FwVolHeader);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
Free FvDevice resource when error happens
|
|
|
|
@param FvDevice pointer to the FvDevice to be freed.
|
|
|
|
**/
|
|
VOID
|
|
FreeFvDeviceResource (
|
|
IN FV_DEVICE *FvDevice
|
|
)
|
|
{
|
|
FFS_FILE_LIST_ENTRY *FfsFileEntry;
|
|
LIST_ENTRY *NextEntry;
|
|
|
|
//
|
|
// Free File List Entry
|
|
//
|
|
FfsFileEntry = (FFS_FILE_LIST_ENTRY *)FvDevice->FfsFileListHeader.ForwardLink;
|
|
while (&FfsFileEntry->Link != &FvDevice->FfsFileListHeader) {
|
|
NextEntry = (&FfsFileEntry->Link)->ForwardLink;
|
|
|
|
if (FfsFileEntry->StreamHandle != 0) {
|
|
//
|
|
// Close stream and free resources from SEP
|
|
//
|
|
CloseSectionStream (FfsFileEntry->StreamHandle, FALSE);
|
|
}
|
|
|
|
if (FfsFileEntry->FileCached) {
|
|
//
|
|
// Free the cached file buffer.
|
|
//
|
|
CoreFreePool (FfsFileEntry->FfsHeader);
|
|
}
|
|
|
|
CoreFreePool (FfsFileEntry);
|
|
|
|
FfsFileEntry = (FFS_FILE_LIST_ENTRY *) NextEntry;
|
|
}
|
|
|
|
if (!FvDevice->IsMemoryMapped) {
|
|
//
|
|
// Free the cached FV buffer.
|
|
//
|
|
CoreFreePool (FvDevice->CachedFv);
|
|
}
|
|
|
|
//
|
|
// Free Volume Header
|
|
//
|
|
CoreFreePool (FvDevice->FwVolHeader);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
Check if an FV is consistent and allocate cache for it.
|
|
|
|
@param FvDevice A pointer to the FvDevice to be checked.
|
|
|
|
@retval EFI_OUT_OF_RESOURCES No enough buffer could be allocated.
|
|
@retval EFI_SUCCESS FV is consistent and cache is allocated.
|
|
@retval EFI_VOLUME_CORRUPTED File system is corrupted.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
FvCheck (
|
|
IN OUT FV_DEVICE *FvDevice
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
|
|
EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;
|
|
EFI_FIRMWARE_VOLUME_EXT_HEADER *FwVolExtHeader;
|
|
EFI_FVB_ATTRIBUTES_2 FvbAttributes;
|
|
EFI_FV_BLOCK_MAP_ENTRY *BlockMap;
|
|
FFS_FILE_LIST_ENTRY *FfsFileEntry;
|
|
EFI_FFS_FILE_HEADER *FfsHeader;
|
|
UINT8 *CacheLocation;
|
|
UINTN LbaOffset;
|
|
UINTN HeaderSize;
|
|
UINTN Index;
|
|
EFI_LBA LbaIndex;
|
|
UINTN Size;
|
|
EFI_FFS_FILE_STATE FileState;
|
|
UINT8 *TopFvAddress;
|
|
UINTN TestLength;
|
|
EFI_PHYSICAL_ADDRESS PhysicalAddress;
|
|
BOOLEAN FileCached;
|
|
UINTN WholeFileSize;
|
|
EFI_FFS_FILE_HEADER *CacheFfsHeader;
|
|
|
|
FileCached = FALSE;
|
|
CacheFfsHeader = NULL;
|
|
|
|
Fvb = FvDevice->Fvb;
|
|
FwVolHeader = FvDevice->FwVolHeader;
|
|
|
|
Status = Fvb->GetAttributes (Fvb, &FvbAttributes);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Size is the size of the FV minus the head. We have already allocated
|
|
// the header to check to make sure the volume is valid
|
|
//
|
|
Size = (UINTN)(FwVolHeader->FvLength - FwVolHeader->HeaderLength);
|
|
if ((FvbAttributes & EFI_FVB2_MEMORY_MAPPED) != 0) {
|
|
FvDevice->IsMemoryMapped = TRUE;
|
|
|
|
Status = Fvb->GetPhysicalAddress (Fvb, &PhysicalAddress);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Don't cache memory mapped FV really.
|
|
//
|
|
FvDevice->CachedFv = (UINT8 *) (UINTN) (PhysicalAddress + FwVolHeader->HeaderLength);
|
|
} else {
|
|
FvDevice->IsMemoryMapped = FALSE;
|
|
FvDevice->CachedFv = AllocatePool (Size);
|
|
|
|
if (FvDevice->CachedFv == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remember a pointer to the end fo the CachedFv
|
|
//
|
|
FvDevice->EndOfCachedFv = FvDevice->CachedFv + Size;
|
|
|
|
if (!FvDevice->IsMemoryMapped) {
|
|
//
|
|
// Copy FV minus header into memory using the block map we have all ready
|
|
// read into memory.
|
|
//
|
|
BlockMap = FwVolHeader->BlockMap;
|
|
CacheLocation = FvDevice->CachedFv;
|
|
LbaIndex = 0;
|
|
LbaOffset = 0;
|
|
HeaderSize = FwVolHeader->HeaderLength;
|
|
while ((BlockMap->NumBlocks != 0) || (BlockMap->Length != 0)) {
|
|
Index = 0;
|
|
Size = BlockMap->Length;
|
|
if (HeaderSize > 0) {
|
|
//
|
|
// Skip header size
|
|
//
|
|
for (; Index < BlockMap->NumBlocks && HeaderSize >= BlockMap->Length; Index ++) {
|
|
HeaderSize -= BlockMap->Length;
|
|
LbaIndex ++;
|
|
}
|
|
|
|
//
|
|
// Check whether FvHeader is crossing the multi block range.
|
|
//
|
|
if (Index >= BlockMap->NumBlocks) {
|
|
BlockMap++;
|
|
continue;
|
|
} else if (HeaderSize > 0) {
|
|
LbaOffset = HeaderSize;
|
|
Size = BlockMap->Length - HeaderSize;
|
|
HeaderSize = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// read the FV data
|
|
//
|
|
for (; Index < BlockMap->NumBlocks; Index ++) {
|
|
Status = Fvb->Read (Fvb,
|
|
LbaIndex,
|
|
LbaOffset,
|
|
&Size,
|
|
CacheLocation
|
|
);
|
|
|
|
//
|
|
// Not check EFI_BAD_BUFFER_SIZE, for Size = BlockMap->Length
|
|
//
|
|
if (EFI_ERROR (Status)) {
|
|
goto Done;
|
|
}
|
|
|
|
LbaIndex++;
|
|
CacheLocation += Size;
|
|
|
|
//
|
|
// After we skip Fv Header always read from start of block
|
|
//
|
|
LbaOffset = 0;
|
|
Size = BlockMap->Length;
|
|
}
|
|
|
|
BlockMap++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Scan to check the free space & File list
|
|
//
|
|
if ((FvbAttributes & EFI_FVB2_ERASE_POLARITY) != 0) {
|
|
FvDevice->ErasePolarity = 1;
|
|
} else {
|
|
FvDevice->ErasePolarity = 0;
|
|
}
|
|
|
|
|
|
//
|
|
// go through the whole FV cache, check the consistence of the FV.
|
|
// Make a linked list of all the Ffs file headers
|
|
//
|
|
Status = EFI_SUCCESS;
|
|
InitializeListHead (&FvDevice->FfsFileListHeader);
|
|
|
|
//
|
|
// Build FFS list
|
|
//
|
|
if (FwVolHeader->ExtHeaderOffset != 0) {
|
|
//
|
|
// Searching for files starts on an 8 byte aligned boundary after the end of the Extended Header if it exists.
|
|
//
|
|
FwVolExtHeader = (EFI_FIRMWARE_VOLUME_EXT_HEADER *) (FvDevice->CachedFv + (FwVolHeader->ExtHeaderOffset - FwVolHeader->HeaderLength));
|
|
FfsHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FwVolExtHeader + FwVolExtHeader->ExtHeaderSize);
|
|
FfsHeader = (EFI_FFS_FILE_HEADER *) ALIGN_POINTER (FfsHeader, 8);
|
|
} else {
|
|
FfsHeader = (EFI_FFS_FILE_HEADER *) (FvDevice->CachedFv);
|
|
}
|
|
TopFvAddress = FvDevice->EndOfCachedFv;
|
|
while (((UINTN) FfsHeader >= (UINTN) FvDevice->CachedFv) && ((UINTN) FfsHeader <= (UINTN) ((UINTN) TopFvAddress - sizeof (EFI_FFS_FILE_HEADER)))) {
|
|
|
|
if (FileCached) {
|
|
CoreFreePool (CacheFfsHeader);
|
|
FileCached = FALSE;
|
|
}
|
|
|
|
TestLength = TopFvAddress - ((UINT8 *) FfsHeader);
|
|
if (TestLength > sizeof (EFI_FFS_FILE_HEADER)) {
|
|
TestLength = sizeof (EFI_FFS_FILE_HEADER);
|
|
}
|
|
|
|
if (IsBufferErased (FvDevice->ErasePolarity, FfsHeader, TestLength)) {
|
|
//
|
|
// We have found the free space so we are done!
|
|
//
|
|
goto Done;
|
|
}
|
|
|
|
if (!IsValidFfsHeader (FvDevice->ErasePolarity, FfsHeader, &FileState)) {
|
|
if ((FileState == EFI_FILE_HEADER_INVALID) ||
|
|
(FileState == EFI_FILE_HEADER_CONSTRUCTION)) {
|
|
if (IS_FFS_FILE2 (FfsHeader)) {
|
|
if (!FvDevice->IsFfs3Fv) {
|
|
DEBUG ((EFI_D_ERROR, "Found a FFS3 formatted file: %g in a non-FFS3 formatted FV.\n", &FfsHeader->Name));
|
|
}
|
|
FfsHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FfsHeader + sizeof (EFI_FFS_FILE_HEADER2));
|
|
} else {
|
|
FfsHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FfsHeader + sizeof (EFI_FFS_FILE_HEADER));
|
|
}
|
|
continue;
|
|
} else {
|
|
//
|
|
// File system is corrputed
|
|
//
|
|
Status = EFI_VOLUME_CORRUPTED;
|
|
goto Done;
|
|
}
|
|
}
|
|
|
|
CacheFfsHeader = FfsHeader;
|
|
if ((CacheFfsHeader->Attributes & FFS_ATTRIB_CHECKSUM) == FFS_ATTRIB_CHECKSUM) {
|
|
if (FvDevice->IsMemoryMapped) {
|
|
//
|
|
// Memory mapped FV has not been cached.
|
|
// Here is to cache FFS file to memory buffer for following checksum calculating.
|
|
// And then, the cached file buffer can be also used for FvReadFile.
|
|
//
|
|
WholeFileSize = IS_FFS_FILE2 (CacheFfsHeader) ? FFS_FILE2_SIZE (CacheFfsHeader): FFS_FILE_SIZE (CacheFfsHeader);
|
|
CacheFfsHeader = AllocateCopyPool (WholeFileSize, CacheFfsHeader);
|
|
if (CacheFfsHeader == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Done;
|
|
}
|
|
FileCached = TRUE;
|
|
}
|
|
}
|
|
|
|
if (!IsValidFfsFile (FvDevice->ErasePolarity, CacheFfsHeader)) {
|
|
//
|
|
// File system is corrupted
|
|
//
|
|
Status = EFI_VOLUME_CORRUPTED;
|
|
goto Done;
|
|
}
|
|
|
|
if (IS_FFS_FILE2 (CacheFfsHeader)) {
|
|
ASSERT (FFS_FILE2_SIZE (CacheFfsHeader) > 0x00FFFFFF);
|
|
if (!FvDevice->IsFfs3Fv) {
|
|
DEBUG ((EFI_D_ERROR, "Found a FFS3 formatted file: %g in a non-FFS3 formatted FV.\n", &CacheFfsHeader->Name));
|
|
FfsHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FfsHeader + FFS_FILE2_SIZE (CacheFfsHeader));
|
|
//
|
|
// Adjust pointer to the next 8-byte aligned boundry.
|
|
//
|
|
FfsHeader = (EFI_FFS_FILE_HEADER *) (((UINTN) FfsHeader + 7) & ~0x07);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
FileState = GetFileState (FvDevice->ErasePolarity, CacheFfsHeader);
|
|
|
|
//
|
|
// check for non-deleted file
|
|
//
|
|
if (FileState != EFI_FILE_DELETED) {
|
|
//
|
|
// Create a FFS list entry for each non-deleted file
|
|
//
|
|
FfsFileEntry = AllocateZeroPool (sizeof (FFS_FILE_LIST_ENTRY));
|
|
if (FfsFileEntry == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Done;
|
|
}
|
|
|
|
FfsFileEntry->FfsHeader = CacheFfsHeader;
|
|
FfsFileEntry->FileCached = FileCached;
|
|
FileCached = FALSE;
|
|
InsertTailList (&FvDevice->FfsFileListHeader, &FfsFileEntry->Link);
|
|
}
|
|
|
|
if (IS_FFS_FILE2 (CacheFfsHeader)) {
|
|
FfsHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FfsHeader + FFS_FILE2_SIZE (CacheFfsHeader));
|
|
} else {
|
|
FfsHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FfsHeader + FFS_FILE_SIZE (CacheFfsHeader));
|
|
}
|
|
|
|
//
|
|
// Adjust pointer to the next 8-byte aligned boundry.
|
|
//
|
|
FfsHeader = (EFI_FFS_FILE_HEADER *)(((UINTN)FfsHeader + 7) & ~0x07);
|
|
|
|
}
|
|
|
|
Done:
|
|
if (EFI_ERROR (Status)) {
|
|
if (FileCached) {
|
|
CoreFreePool (CacheFfsHeader);
|
|
FileCached = FALSE;
|
|
}
|
|
FreeFvDeviceResource (FvDevice);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
This notification function is invoked when an instance of the
|
|
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL is produced. It layers an instance of the
|
|
EFI_FIRMWARE_VOLUME2_PROTOCOL on the same handle. This is the function where
|
|
the actual initialization of the EFI_FIRMWARE_VOLUME2_PROTOCOL is done.
|
|
|
|
@param Event The event that occured
|
|
@param Context For EFI compatiblity. Not used.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
NotifyFwVolBlock (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
EFI_HANDLE Handle;
|
|
EFI_STATUS Status;
|
|
UINTN BufferSize;
|
|
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
|
|
EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;
|
|
FV_DEVICE *FvDevice;
|
|
EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;
|
|
//
|
|
// Examine all new handles
|
|
//
|
|
for (;;) {
|
|
//
|
|
// Get the next handle
|
|
//
|
|
BufferSize = sizeof (Handle);
|
|
Status = CoreLocateHandle (
|
|
ByRegisterNotify,
|
|
NULL,
|
|
gEfiFwVolBlockNotifyReg,
|
|
&BufferSize,
|
|
&Handle
|
|
);
|
|
|
|
//
|
|
// If not found, we're done
|
|
//
|
|
if (EFI_NOT_FOUND == Status) {
|
|
break;
|
|
}
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Get the FirmwareVolumeBlock protocol on that handle
|
|
//
|
|
Status = CoreHandleProtocol (Handle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID **)&Fvb);
|
|
ASSERT_EFI_ERROR (Status);
|
|
ASSERT (Fvb != NULL);
|
|
|
|
//
|
|
// Make sure the Fv Header is O.K.
|
|
//
|
|
Status = GetFwVolHeader (Fvb, &FwVolHeader);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
ASSERT (FwVolHeader != NULL);
|
|
|
|
if (!VerifyFvHeaderChecksum (FwVolHeader)) {
|
|
CoreFreePool (FwVolHeader);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check if there is an FV protocol already installed in that handle
|
|
//
|
|
Status = CoreHandleProtocol (Handle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&Fv);
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// Update Fv to use a new Fvb
|
|
//
|
|
FvDevice = BASE_CR (Fv, FV_DEVICE, Fv);
|
|
if (FvDevice->Signature == FV2_DEVICE_SIGNATURE) {
|
|
//
|
|
// Only write into our device structure if it's our device structure
|
|
//
|
|
FvDevice->Fvb = Fvb;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// No FwVol protocol on the handle so create a new one
|
|
//
|
|
FvDevice = AllocateCopyPool (sizeof (FV_DEVICE), &mFvDevice);
|
|
if (FvDevice == NULL) {
|
|
return;
|
|
}
|
|
|
|
FvDevice->Fvb = Fvb;
|
|
FvDevice->Handle = Handle;
|
|
FvDevice->FwVolHeader = FwVolHeader;
|
|
FvDevice->IsFfs3Fv = CompareGuid (&FwVolHeader->FileSystemGuid, &gEfiFirmwareFileSystem3Guid);
|
|
FvDevice->Fv.ParentHandle = Fvb->ParentHandle;
|
|
|
|
if (Fvb->ParentHandle != NULL) {
|
|
//
|
|
// Inherit the authentication status from FVB.
|
|
//
|
|
FvDevice->AuthenticationStatus = GetFvbAuthenticationStatus (Fvb);
|
|
}
|
|
|
|
if (!EFI_ERROR (FvCheck (FvDevice))) {
|
|
//
|
|
// Install an New FV protocol on the existing handle
|
|
//
|
|
Status = CoreInstallProtocolInterface (
|
|
&Handle,
|
|
&gEfiFirmwareVolume2ProtocolGuid,
|
|
EFI_NATIVE_INTERFACE,
|
|
&FvDevice->Fv
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
} else {
|
|
//
|
|
// Free FvDevice Buffer for the corrupt FV image.
|
|
//
|
|
CoreFreePool (FvDevice);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
This routine is the driver initialization entry point. It registers
|
|
a notification function. This notification function are responsible
|
|
for building the FV stack dynamically.
|
|
|
|
@param ImageHandle The image handle.
|
|
@param SystemTable The system table.
|
|
|
|
@retval EFI_SUCCESS Function successfully returned.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
FwVolDriverInit (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
gEfiFwVolBlockEvent = EfiCreateProtocolNotifyEvent (
|
|
&gEfiFirmwareVolumeBlockProtocolGuid,
|
|
TPL_CALLBACK,
|
|
NotifyFwVolBlock,
|
|
NULL,
|
|
&gEfiFwVolBlockNotifyReg
|
|
);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|