mirror of https://github.com/acidanthera/audk.git
599 lines
18 KiB
C
599 lines
18 KiB
C
/*++
|
|
|
|
Copyright (c) 2006, Intel Corporation
|
|
All rights reserved. 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.
|
|
|
|
Module Name:
|
|
|
|
FwVolBlock.c
|
|
|
|
Abstract:
|
|
|
|
Firmware Volume Block protocol.. Consumes FV hobs and creates
|
|
appropriate block protocols.
|
|
|
|
Also consumes NT_NON_MM_FV envinronment variable and produces appropriate
|
|
block protocols fro them also... (this is TBD)
|
|
|
|
--*/
|
|
|
|
#include <DxeMain.h>
|
|
|
|
|
|
EFI_FW_VOL_BLOCK_DEVICE mFwVolBlock = {
|
|
FVB_DEVICE_SIGNATURE,
|
|
NULL,
|
|
{
|
|
{
|
|
{
|
|
HARDWARE_DEVICE_PATH,
|
|
HW_MEMMAP_DP,
|
|
{ (UINT8)(sizeof (MEMMAP_DEVICE_PATH)), (UINT8)(sizeof (MEMMAP_DEVICE_PATH) >> 8) }
|
|
},
|
|
EfiMemoryMappedIO,
|
|
(EFI_PHYSICAL_ADDRESS)0,
|
|
(EFI_PHYSICAL_ADDRESS)0,
|
|
},
|
|
{
|
|
END_DEVICE_PATH_TYPE,
|
|
END_ENTIRE_DEVICE_PATH_SUBTYPE,
|
|
{ END_DEVICE_PATH_LENGTH, 0 }
|
|
},
|
|
},
|
|
{
|
|
FwVolBlockGetAttributes,
|
|
(EFI_FVB_SET_ATTRIBUTES)FwVolBlockSetAttributes,
|
|
FwVolBlockGetPhysicalAddress,
|
|
FwVolBlockGetBlockSize,
|
|
FwVolBlockReadBlock,
|
|
(EFI_FVB_WRITE)FwVolBlockWriteBlock,
|
|
(EFI_FVB_ERASE_BLOCKS)FwVolBlockEraseBlock,
|
|
NULL
|
|
},
|
|
0,
|
|
NULL,
|
|
0,
|
|
0
|
|
};
|
|
|
|
|
|
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
FwVolBlockGetAttributes (
|
|
IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
|
|
OUT EFI_FVB_ATTRIBUTES *Attributes
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Retrieves Volume attributes. No polarity translations are done.
|
|
|
|
Arguments:
|
|
This - Calling context
|
|
Attributes - output buffer which contains attributes
|
|
|
|
Returns:
|
|
EFI_SUCCESS - The firmware volume attributes were returned.
|
|
|
|
--*/
|
|
{
|
|
EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;
|
|
|
|
FvbDevice = FVB_DEVICE_FROM_THIS (This);
|
|
|
|
//
|
|
// Since we are read only, it's safe to get attributes data from our in-memory copy.
|
|
//
|
|
*Attributes = FvbDevice->FvbAttributes;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
FwVolBlockSetAttributes (
|
|
IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
|
|
IN EFI_FVB_ATTRIBUTES *Attributes
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Modifies the current settings of the firmware volume according to the input parameter.
|
|
|
|
Arguments:
|
|
This - Calling context
|
|
Attributes - input buffer which contains attributes
|
|
|
|
Returns:
|
|
EFI_SUCCESS - The firmware volume attributes were returned.
|
|
EFI_INVALID_PARAMETER - The attributes requested are in conflict with the capabilities as
|
|
declared in the firmware volume header.
|
|
EFI_UNSUPPORTED - Not supported.
|
|
--*/
|
|
{
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
FwVolBlockEraseBlock (
|
|
IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
|
|
...
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
The EraseBlock() function erases one or more blocks as denoted by the
|
|
variable argument list. The entire parameter list of blocks must be verified
|
|
prior to erasing any blocks. If a block is requested that does not exist
|
|
within the associated firmware volume (it has a larger index than the last
|
|
block of the firmware volume), the EraseBlock() function must return
|
|
EFI_INVALID_PARAMETER without modifying the contents of the firmware volume.
|
|
|
|
Arguments:
|
|
This - Calling context
|
|
... - Starting LBA followed by Number of Lba to erase. a -1 to terminate
|
|
the list.
|
|
|
|
Returns:
|
|
EFI_SUCCESS - The erase request was successfully completed.
|
|
EFI_ACCESS_DENIED - The firmware volume is in the WriteDisabled state.
|
|
EFI_DEVICE_ERROR - The block device is not functioning correctly and could not be
|
|
written. The firmware device may have been partially erased.
|
|
EFI_INVALID_PARAMETER - One or more of the LBAs listed in the variable argument list do
|
|
EFI_UNSUPPORTED - Not supported.
|
|
|
|
--*/
|
|
{
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
FwVolBlockReadBlock (
|
|
IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
|
|
IN EFI_LBA Lba,
|
|
IN UINTN Offset,
|
|
IN OUT UINTN *NumBytes,
|
|
IN UINT8 *Buffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Read the specified number of bytes from the block to the input buffer.
|
|
|
|
Arguments:
|
|
This - Indicates the calling context.
|
|
Lba - The starting logical block index to read.
|
|
Offset - Offset into the block at which to begin reading.
|
|
NumBytes - Pointer to a UINT32. At entry, *NumBytes contains the
|
|
total size of the buffer. At exit, *NumBytes contains the
|
|
total number of bytes actually read.
|
|
Buffer - Pinter to a caller-allocated buffer that contains the destine
|
|
for the read.
|
|
|
|
Returns:
|
|
EFI_SUCCESS - The firmware volume was read successfully.
|
|
EFI_BAD_BUFFER_SIZE - The read was attempted across an LBA boundary.
|
|
EFI_ACCESS_DENIED - Access denied.
|
|
EFI_DEVICE_ERROR - The block device is malfunctioning and could not be read.
|
|
--*/
|
|
{
|
|
EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;
|
|
EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;
|
|
UINT8 *LbaOffset;
|
|
UINTN LbaStart;
|
|
UINTN NumOfBytesRead;
|
|
UINTN LbaIndex;
|
|
|
|
FvbDevice = FVB_DEVICE_FROM_THIS (This);
|
|
|
|
//
|
|
// Check if This FW can be read
|
|
//
|
|
if ((FvbDevice->FvbAttributes & EFI_FVB_READ_STATUS) == 0) {
|
|
return EFI_ACCESS_DENIED;
|
|
}
|
|
|
|
LbaIndex = (UINTN)Lba;
|
|
if (LbaIndex >= FvbDevice->NumBlocks) {
|
|
//
|
|
// Invalid Lba, read nothing.
|
|
//
|
|
*NumBytes = 0;
|
|
return EFI_BAD_BUFFER_SIZE;
|
|
}
|
|
|
|
if (Offset > FvbDevice->LbaCache[LbaIndex].Length) {
|
|
//
|
|
// all exceed boundry, read nothing.
|
|
//
|
|
*NumBytes = 0;
|
|
return EFI_BAD_BUFFER_SIZE;
|
|
}
|
|
|
|
NumOfBytesRead = *NumBytes;
|
|
if (Offset + NumOfBytesRead > FvbDevice->LbaCache[LbaIndex].Length) {
|
|
//
|
|
// partial exceed boundry, read data from current postion to end.
|
|
//
|
|
NumOfBytesRead = FvbDevice->LbaCache[LbaIndex].Length - Offset;
|
|
}
|
|
|
|
LbaStart = FvbDevice->LbaCache[LbaIndex].Base;
|
|
FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)((UINTN)FvbDevice->BaseAddress);
|
|
LbaOffset = (UINT8 *)FwVolHeader + LbaStart + Offset;
|
|
|
|
//
|
|
// Perform read operation
|
|
//
|
|
CopyMem (Buffer, LbaOffset, NumOfBytesRead);
|
|
|
|
if (NumOfBytesRead == *NumBytes) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
*NumBytes = NumOfBytesRead;
|
|
return EFI_BAD_BUFFER_SIZE;
|
|
}
|
|
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
FwVolBlockWriteBlock (
|
|
IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
|
|
IN EFI_LBA Lba,
|
|
IN UINTN Offset,
|
|
IN OUT UINTN *NumBytes,
|
|
IN UINT8 *Buffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Writes the specified number of bytes from the input buffer to the block.
|
|
|
|
Arguments:
|
|
This - Indicates the calling context.
|
|
Lba - The starting logical block index to write to.
|
|
Offset - Offset into the block at which to begin writing.
|
|
NumBytes - Pointer to a UINT32. At entry, *NumBytes contains the
|
|
total size of the buffer. At exit, *NumBytes contains the
|
|
total number of bytes actually written.
|
|
Buffer - Pinter to a caller-allocated buffer that contains the source
|
|
for the write.
|
|
|
|
Returns:
|
|
EFI_SUCCESS - The firmware volume was written successfully.
|
|
EFI_BAD_BUFFER_SIZE - The write was attempted across an LBA boundary. On output,
|
|
NumBytes contains the total number of bytes actually written.
|
|
EFI_ACCESS_DENIED - The firmware volume is in the WriteDisabled state.
|
|
EFI_DEVICE_ERROR - The block device is malfunctioning and could not be written.
|
|
EFI_UNSUPPORTED - Not supported.
|
|
--*/
|
|
{
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
FwVolBlockGetPhysicalAddress (
|
|
IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
|
|
OUT EFI_PHYSICAL_ADDRESS *Address
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Get Fvb's base address.
|
|
|
|
Arguments:
|
|
This - Indicates the calling context.
|
|
Address - Fvb device base address.
|
|
|
|
Returns:
|
|
EFI_SUCCESS - Successfully got Fvb's base address.
|
|
EFI_UNSUPPORTED - Not supported.
|
|
--*/
|
|
{
|
|
EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;
|
|
|
|
FvbDevice = FVB_DEVICE_FROM_THIS (This);
|
|
|
|
if (FvbDevice->FvbAttributes & EFI_FVB_MEMORY_MAPPED) {
|
|
*Address = FvbDevice->BaseAddress;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
FwVolBlockGetBlockSize (
|
|
IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
|
|
IN EFI_LBA Lba,
|
|
OUT UINTN *BlockSize,
|
|
OUT UINTN *NumberOfBlocks
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Retrieves the size in bytes of a specific block within a firmware volume.
|
|
|
|
Arguments:
|
|
This - Indicates the calling context.
|
|
Lba - Indicates the block for which to return the size.
|
|
BlockSize - Pointer to a caller-allocated UINTN in which the size of the
|
|
block is returned.
|
|
NumberOfBlocks - Pointer to a caller-allocated UINTN in which the number of
|
|
consecutive blocks starting with Lba is returned. All blocks
|
|
in this range have a size of BlockSize.
|
|
Returns:
|
|
EFI_SUCCESS - The firmware volume base address is returned.
|
|
EFI_INVALID_PARAMETER - The requested LBA is out of range.
|
|
--*/
|
|
{
|
|
UINTN TotalBlocks;
|
|
EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;
|
|
EFI_FV_BLOCK_MAP_ENTRY *PtrBlockMapEntry;
|
|
EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;
|
|
|
|
FvbDevice = FVB_DEVICE_FROM_THIS (This);
|
|
|
|
//
|
|
// Do parameter checking
|
|
//
|
|
if (Lba >= FvbDevice->NumBlocks) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)((UINTN)FvbDevice->BaseAddress);
|
|
|
|
PtrBlockMapEntry = FwVolHeader->FvBlockMap;
|
|
|
|
//
|
|
// Search the block map for the given block
|
|
//
|
|
TotalBlocks = 0;
|
|
while ((PtrBlockMapEntry->NumBlocks != 0) || (PtrBlockMapEntry->BlockLength !=0 )) {
|
|
TotalBlocks += PtrBlockMapEntry->NumBlocks;
|
|
if (Lba < TotalBlocks) {
|
|
//
|
|
// We find the range
|
|
//
|
|
break;
|
|
}
|
|
|
|
PtrBlockMapEntry++;
|
|
}
|
|
|
|
*BlockSize = PtrBlockMapEntry->BlockLength;
|
|
*NumberOfBlocks = TotalBlocks - (UINTN)Lba;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
EFI_STATUS
|
|
ProduceFVBProtocolOnBuffer (
|
|
IN EFI_PHYSICAL_ADDRESS BaseAddress,
|
|
IN UINT64 Length,
|
|
IN EFI_HANDLE ParentHandle,
|
|
OUT EFI_HANDLE *FvProtocol OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine produces a firmware volume block protocol on a given
|
|
buffer.
|
|
|
|
Arguments:
|
|
BaseAddress - base address of the firmware volume image
|
|
Length - length of the firmware volume image
|
|
ParentHandle - handle of parent firmware volume, if this
|
|
image came from an FV image file in another
|
|
firmware volume (ala capsules)
|
|
FvProtocol - Firmware volume block protocol produced.
|
|
|
|
Returns:
|
|
EFI_VOLUME_CORRUPTED - Volume corrupted.
|
|
EFI_OUT_OF_RESOURCES - No enough buffer to be allocated.
|
|
EFI_SUCCESS - Successfully produced a FVB protocol on given buffer.
|
|
|
|
--*/
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_FW_VOL_BLOCK_DEVICE *FvbDev;
|
|
EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;
|
|
UINTN BlockIndex;
|
|
UINTN BlockIndex2;
|
|
UINTN LinearOffset;
|
|
EFI_FV_BLOCK_MAP_ENTRY *PtrBlockMapEntry;
|
|
|
|
FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)BaseAddress;
|
|
//
|
|
// Validate FV Header, if not as expected, return
|
|
//
|
|
if (FwVolHeader->Signature != EFI_FVH_SIGNATURE) {
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
//
|
|
// Allocate EFI_FW_VOL_BLOCK_DEVICE
|
|
//
|
|
FvbDev = CoreAllocateCopyPool (sizeof (EFI_FW_VOL_BLOCK_DEVICE), &mFwVolBlock);
|
|
if (FvbDev == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
FvbDev->BaseAddress = BaseAddress;
|
|
FvbDev->FvbAttributes = FwVolHeader->Attributes;
|
|
FvbDev->FwVolBlockInstance.ParentHandle = ParentHandle;
|
|
|
|
//
|
|
// Init the block caching fields of the device
|
|
// First, count the number of blocks
|
|
//
|
|
FvbDev->NumBlocks = 0;
|
|
for (PtrBlockMapEntry = FwVolHeader->FvBlockMap;
|
|
PtrBlockMapEntry->NumBlocks != 0;
|
|
PtrBlockMapEntry++) {
|
|
FvbDev->NumBlocks += PtrBlockMapEntry->NumBlocks;
|
|
}
|
|
//
|
|
// Second, allocate the cache
|
|
//
|
|
FvbDev->LbaCache = CoreAllocateBootServicesPool (FvbDev->NumBlocks * sizeof (LBA_CACHE));
|
|
if (FvbDev->LbaCache == NULL) {
|
|
CoreFreePool (FvbDev);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
//
|
|
// Last, fill in the cache with the linear address of the blocks
|
|
//
|
|
BlockIndex = 0;
|
|
LinearOffset = 0;
|
|
for (PtrBlockMapEntry = FwVolHeader->FvBlockMap;
|
|
PtrBlockMapEntry->NumBlocks != 0; PtrBlockMapEntry++) {
|
|
for (BlockIndex2 = 0; BlockIndex2 < PtrBlockMapEntry->NumBlocks; BlockIndex2++) {
|
|
FvbDev->LbaCache[BlockIndex].Base = LinearOffset;
|
|
FvbDev->LbaCache[BlockIndex].Length = PtrBlockMapEntry->BlockLength;
|
|
LinearOffset += PtrBlockMapEntry->BlockLength;
|
|
BlockIndex++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set up the devicepath
|
|
//
|
|
FvbDev->DevicePath.MemMapDevPath.StartingAddress = BaseAddress;
|
|
FvbDev->DevicePath.MemMapDevPath.EndingAddress = BaseAddress + FwVolHeader->FvLength - 1;
|
|
|
|
//
|
|
//
|
|
// Attach FvVolBlock Protocol to new handle
|
|
//
|
|
Status = CoreInstallMultipleProtocolInterfaces (
|
|
&FvbDev->Handle,
|
|
&gEfiFirmwareVolumeBlockProtocolGuid, &FvbDev->FwVolBlockInstance,
|
|
&gEfiDevicePathProtocolGuid, &FvbDev->DevicePath,
|
|
&gEfiFirmwareVolumeDispatchProtocolGuid, NULL,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// If they want the handle back, set it.
|
|
//
|
|
if (FvProtocol != NULL) {
|
|
*FvProtocol = FvbDev->Handle;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
FwVolBlockDriverInit (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine is the driver initialization entry point. It initializes the
|
|
libraries, consumes FV hobs and NT_NON_MM_FV environment variable and
|
|
produces instances of FW_VOL_BLOCK_PROTOCOL as appropriate.
|
|
Arguments:
|
|
ImageHandle - The image handle.
|
|
SystemTable - The system table.
|
|
Returns:
|
|
EFI_SUCCESS - Successfully initialized firmware volume block driver.
|
|
--*/
|
|
{
|
|
EFI_PEI_HOB_POINTERS FvHob;
|
|
//
|
|
// Core Needs Firmware Volumes to function
|
|
//
|
|
FvHob.Raw = GetHobList ();
|
|
while ((FvHob.Raw = GetNextHob (EFI_HOB_TYPE_FV, FvHob.Raw)) != NULL) {
|
|
//
|
|
// Produce an FVB protocol for it
|
|
//
|
|
ProduceFVBProtocolOnBuffer (FvHob.FirmwareVolume->BaseAddress, FvHob.FirmwareVolume->Length, NULL, NULL);
|
|
FvHob.Raw = GET_NEXT_HOB (FvHob);
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
EFI_STATUS
|
|
CoreProcessFirmwareVolume (
|
|
IN VOID *FvHeader,
|
|
IN UINTN Size,
|
|
OUT EFI_HANDLE *FVProtocolHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This DXE service routine is used to process a firmware volume. In
|
|
particular, it can be called by BDS to process a single firmware
|
|
volume found in a capsule.
|
|
|
|
Arguments:
|
|
FvHeader - pointer to a firmware volume header
|
|
Size - the size of the buffer pointed to by FvHeader
|
|
FVProtocolHandle - the handle on which a firmware volume protocol
|
|
was produced for the firmware volume passed in.
|
|
|
|
Returns:
|
|
EFI_OUT_OF_RESOURCES - if an FVB could not be produced due to lack of
|
|
system resources
|
|
EFI_VOLUME_CORRUPTED - if the volume was corrupted
|
|
EFI_SUCCESS - a firmware volume protocol was produced for the
|
|
firmware volume
|
|
|
|
--*/
|
|
{
|
|
VOID *Ptr;
|
|
EFI_STATUS Status;
|
|
|
|
*FVProtocolHandle = NULL;
|
|
Status = ProduceFVBProtocolOnBuffer (
|
|
(EFI_PHYSICAL_ADDRESS) (UINTN) FvHeader,
|
|
(UINT64)Size,
|
|
NULL,
|
|
FVProtocolHandle
|
|
);
|
|
//
|
|
// Since in our implementation we use register-protocol-notify to put a
|
|
// FV protocol on the FVB protocol handle, we can't directly verify that
|
|
// the FV protocol was produced. Therefore here we will check the handle
|
|
// and make sure an FV protocol is on it. This indicates that all went
|
|
// well. Otherwise we have to assume that the volume was corrupted
|
|
// somehow.
|
|
//
|
|
if (!EFI_ERROR(Status)) {
|
|
Ptr = NULL;
|
|
Status = CoreHandleProtocol (*FVProtocolHandle, &gEfiFirmwareVolumeProtocolGuid, (VOID **)&Ptr);
|
|
if (EFI_ERROR(Status) || (Ptr == NULL)) {
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|