/** @file
Implement the Firmware Volume Block (FVB) services based on SMM FVB
module and install FVB protocol.
Copyright (c) 2010 - 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 that 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 "FvbSmmDxe.h"
EFI_HANDLE mHandle = NULL;
EFI_SMM_COMMUNICATION_PROTOCOL *mSmmCommunication = NULL;
//
// Template structure used when installing FVB protocol.
//
EFI_FVB_DEVICE mFvbDeviceTemplate = {
FVB_DEVICE_SIGNATURE,
NULL,
{
FvbGetAttributes,
FvbSetAttributes,
FvbGetPhysicalAddress,
FvbGetBlockSize,
FvbRead,
FvbWrite,
FvbEraseBlocks,
NULL
},
NULL
};
FV_MEMMAP_DEVICE_PATH mFvMemmapDevicePathTemplate = {
{
{
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
}
}
};
FV_PIWG_DEVICE_PATH mFvPIWGDevicePathTemplate = {
{
{
MEDIA_DEVICE_PATH,
MEDIA_PIWG_FW_VOL_DP,
{
(UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH)),
(UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH) >> 8)
}
},
{ 0 }
},
{
END_DEVICE_PATH_TYPE,
END_ENTIRE_DEVICE_PATH_SUBTYPE,
{
END_DEVICE_PATH_LENGTH,
0
}
}
};
/**
Initialize the communicate buffer using DataSize and Function.
The communicate size is: SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE +
DataSize.
@param[out] CommunicateBuffer The communicate buffer. Caller should free it after use.
@param[out] DataPtr Points to the data in the communicate buffer. Caller should not free it.
@param[in] DataSize The payload size.
@param[in] Function The function number used to initialize the communicate header.
@retval EFI_INVALID_PARAMETER The data size is too big.
@retval EFI_SUCCESS Find the specified variable.
**/
EFI_STATUS
InitCommunicateBuffer (
OUT VOID **CommunicateBuffer,
OUT VOID **DataPtr,
IN UINTN DataSize,
IN UINTN Function
)
{
EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader;
SMM_FVB_COMMUNICATE_FUNCTION_HEADER *SmmFvbFunctionHeader;
//
// The whole buffer size: SMM_COMMUNICATE_HEADER_SIZE + SMM_FVB_COMMUNICATE_HEADER_SIZE + DataSize.
//
SmmCommunicateHeader = AllocatePool (DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_FVB_COMMUNICATE_HEADER_SIZE);
ASSERT (SmmCommunicateHeader != NULL);
//
// Prepare data buffer.
//
CopyGuid (&SmmCommunicateHeader->HeaderGuid, &gEfiSmmFirmwareVolumeBlockProtocolGuid);
SmmCommunicateHeader->MessageLength = DataSize + SMM_FVB_COMMUNICATE_HEADER_SIZE;
SmmFvbFunctionHeader = (SMM_FVB_COMMUNICATE_FUNCTION_HEADER *) SmmCommunicateHeader->Data;
SmmFvbFunctionHeader->Function = Function;
*CommunicateBuffer = SmmCommunicateHeader;
*DataPtr = SmmFvbFunctionHeader->Data;
return EFI_SUCCESS;
}
/**
Send the data in communicate buffer to SMM.
@param[out] SmmCommunicateHeader The communicate buffer.
@param[in] DataSize The payload size.
**/
EFI_STATUS
SendCommunicateBuffer (
IN EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader,
IN UINTN DataSize
)
{
EFI_STATUS Status;
UINTN CommSize;
SMM_FVB_COMMUNICATE_FUNCTION_HEADER *SmmFvbFunctionHeader;
CommSize = DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_FVB_COMMUNICATE_HEADER_SIZE;
Status = mSmmCommunication->Communicate (
mSmmCommunication,
SmmCommunicateHeader,
&CommSize
);
ASSERT_EFI_ERROR (Status);
SmmFvbFunctionHeader = (SMM_FVB_COMMUNICATE_FUNCTION_HEADER *) SmmCommunicateHeader->Data;
return SmmFvbFunctionHeader->ReturnStatus;
}
/**
This function retrieves the attributes and current settings of the block.
@param[in] This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.
@param[out] Attributes Pointer to EFI_FVB_ATTRIBUTES_2 in which the attributes
and current settings are returned. Type EFI_FVB_ATTRIBUTES_2
is defined in EFI_FIRMWARE_VOLUME_HEADER.
@retval EFI_SUCCESS The firmware volume attributes were returned.
@retval EFI_INVALID_PARAMETER Attributes is NULL.
**/
EFI_STATUS
EFIAPI
FvbGetAttributes (
IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
OUT EFI_FVB_ATTRIBUTES_2 *Attributes
)
{
EFI_STATUS Status;
UINTN PayloadSize;
EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader;
SMM_FVB_ATTRIBUTES_HEADER *SmmFvbAttributesHeader;
EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL *SmmFvb;
EFI_FVB_DEVICE *FvbDevice;
if (Attributes == NULL) {
return EFI_INVALID_PARAMETER;
}
FvbDevice = FVB_DEVICE_FROM_THIS (This);
SmmFvb = FvbDevice->SmmFvbInstance;
//
// Initialize the communicate buffer.
//
PayloadSize = sizeof (SMM_FVB_ATTRIBUTES_HEADER);
Status = InitCommunicateBuffer (
(VOID **)&SmmCommunicateHeader,
(VOID **)&SmmFvbAttributesHeader,
PayloadSize,
EFI_FUNCTION_GET_ATTRIBUTES
);
if (EFI_ERROR (Status)) {
return Status;
}
SmmFvbAttributesHeader->SmmFvb = SmmFvb;
SmmFvbAttributesHeader->Attributes = 0;
//
// Send data to SMM.
//
Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
//
// Get data from SMM.
//
*Attributes = SmmFvbAttributesHeader->Attributes;
FreePool (SmmCommunicateHeader);
return Status;
}
/**
Sets Volume attributes. No polarity translations are done.
@param[in] This Calling context.
@param[out] Attributes Output buffer which contains attributes.
@retval EFI_SUCCESS Set the Attributes successfully.
@retval EFI_INVALID_PARAMETER Attributes is NULL.
**/
EFI_STATUS
EFIAPI
FvbSetAttributes (
IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
IN OUT EFI_FVB_ATTRIBUTES_2 *Attributes
)
{
EFI_STATUS Status;
UINTN PayloadSize;
EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader;
SMM_FVB_ATTRIBUTES_HEADER *SmmFvbAttributesHeader;
EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL *SmmFvb;
EFI_FVB_DEVICE *FvbDevice;
if (Attributes == NULL) {
return EFI_INVALID_PARAMETER;
}
FvbDevice = FVB_DEVICE_FROM_THIS (This);
SmmFvb = FvbDevice->SmmFvbInstance;
//
// Initialize the communicate buffer.
//
PayloadSize = sizeof (SMM_FVB_ATTRIBUTES_HEADER);
Status = InitCommunicateBuffer (
(VOID **)&SmmCommunicateHeader,
(VOID **)&SmmFvbAttributesHeader,
PayloadSize,
EFI_FUNCTION_SET_ATTRIBUTES
);
if (EFI_ERROR (Status)) {
return Status;
}
SmmFvbAttributesHeader->SmmFvb = SmmFvb;
SmmFvbAttributesHeader->Attributes = *Attributes;
//
// Send data to SMM.
//
Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
//
// Get data from SMM.
//
*Attributes = SmmFvbAttributesHeader->Attributes;
FreePool (SmmCommunicateHeader);
return Status;
}
/**
Retrieves the physical address of the FVB instance.
@param[in] SmmFvb A pointer to EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL.
@param[out] Address Output buffer containing the address.
@retval EFI_SUCCESS Get the address successfully.
@retval Others Failed to get address.
**/
EFI_STATUS
GetPhysicalAddress (
IN EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL *SmmFvb,
OUT EFI_PHYSICAL_ADDRESS *Address
)
{
EFI_STATUS Status;
UINTN PayloadSize;
EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader;
SMM_FVB_PHYSICAL_ADDRESS_HEADER *SmmFvbPhysicalAddressHeader;
//
// Initialize the communicate buffer.
//
PayloadSize = sizeof (SMM_FVB_PHYSICAL_ADDRESS_HEADER);
Status = InitCommunicateBuffer (
(VOID **)&SmmCommunicateHeader,
(VOID **)&SmmFvbPhysicalAddressHeader,
PayloadSize,
EFI_FUNCTION_GET_PHYSICAL_ADDRESS
);
if (EFI_ERROR (Status)) {
return Status;
}
SmmFvbPhysicalAddressHeader->SmmFvb = SmmFvb;
SmmFvbPhysicalAddressHeader->Address = 0;
//
// Send data to SMM.
//
Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
//
// Get data from SMM.
//
*Address = SmmFvbPhysicalAddressHeader->Address;
FreePool (SmmCommunicateHeader);
return Status;
}
/**
Retrieves the physical address of the FVB instance.
@param[in] This A pointer to EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL.
@param[out] Address Output buffer containing the address.
@retval EFI_SUCCESS Get the address successfully.
@retval Others Failed to get the address.
**/
EFI_STATUS
EFIAPI
FvbGetPhysicalAddress (
IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
OUT EFI_PHYSICAL_ADDRESS *Address
)
{
EFI_STATUS Status;
EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL *SmmFvb;
EFI_FVB_DEVICE *FvbDevice;
if (Address == NULL) {
return EFI_INVALID_PARAMETER;
}
FvbDevice = FVB_DEVICE_FROM_THIS (This);
SmmFvb = FvbDevice->SmmFvbInstance;
Status = GetPhysicalAddress (SmmFvb, Address);
return Status;
}
/**
Retrieve the size of a logical block.
@param[in] This Calling context.
@param[in] Lba Indicates which block to return the size for.
@param[out] BlockSize A pointer to a caller allocated UINTN in which
the size of the block is returned.
@param[out] NumOfBlocks A 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.
@retval EFI_SUCCESS Get BlockSize and NumOfBlocks successfully.
@retval EFI_INVALID_PARAMETER BlockSize or NumOfBlocks are NULL.
**/
EFI_STATUS
EFIAPI
FvbGetBlockSize (
IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
IN EFI_LBA Lba,
OUT UINTN *BlockSize,
OUT UINTN *NumOfBlocks
)
{
EFI_STATUS Status;
UINTN PayloadSize;
EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader;
SMM_FVB_BLOCK_SIZE_HEADER *SmmFvbBlockSizeHeader;
EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL *SmmFvb;
EFI_FVB_DEVICE *FvbDevice;
if ((BlockSize == NULL) || (NumOfBlocks == NULL)) {
return EFI_INVALID_PARAMETER;
}
FvbDevice = FVB_DEVICE_FROM_THIS (This);
SmmFvb = FvbDevice->SmmFvbInstance;
//
// Initialize the communicate buffer.
//
PayloadSize = sizeof (SMM_FVB_BLOCK_SIZE_HEADER);
Status = InitCommunicateBuffer (
(VOID **)&SmmCommunicateHeader,
(VOID **)&SmmFvbBlockSizeHeader,
PayloadSize,
EFI_FUNCTION_GET_BLOCK_SIZE
);
if (EFI_ERROR (Status)) {
return Status;
}
SmmFvbBlockSizeHeader->SmmFvb = SmmFvb;
SmmFvbBlockSizeHeader->Lba = Lba;
//
// Send data to SMM.
//
Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
//
// Get data from SMM.
//
*BlockSize = SmmFvbBlockSizeHeader->BlockSize;
*NumOfBlocks = SmmFvbBlockSizeHeader->NumOfBlocks;
FreePool (SmmCommunicateHeader);
return Status;
}
/**
Reads data beginning at Lba:Offset from FV. The Read terminates either
when *NumBytes of data have been read, or when a block boundary is
reached. *NumBytes is updated to reflect the actual number of bytes
written. The write opertion does not include erase. This routine will
attempt to write only the specified bytes. If the writes do not stick,
it will return an error.
@param[in] This Calling context
@param[in] Lba Block in which to begin write
@param[in] Offset Offset in the block at which to begin write
@param[in,out] NumBytes On input, indicates the requested write size. On
output, indicates the actual number of bytes written
@param[in] Buffer Buffer containing source data for the write.
@retval EFI_SUCCESS The firmware volume was read successfully and
contents are in Buffer.
@retval EFI_BAD_BUFFER_SIZE Read attempted across a LBA boundary. On output,
NumBytes contains the total number of bytes returned
in Buffer.
@retval EFI_ACCESS_DENIED The firmware volume is in the ReadDisabled state
@retval EFI_DEVICE_ERROR The block device is not functioning correctly and
could not be read.
@retval EFI_INVALID_PARAMETER NumBytes or Buffer are NULL.
**/
EFI_STATUS
EFIAPI
FvbRead (
IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
IN EFI_LBA Lba,
IN UINTN Offset,
IN OUT UINTN *NumBytes,
OUT UINT8 *Buffer
)
{
EFI_STATUS Status;
UINTN PayloadSize;
EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader;
SMM_FVB_READ_WRITE_HEADER *SmmFvbReadWriteHeader;
EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL *SmmFvb;
EFI_FVB_DEVICE *FvbDevice;
if ((NumBytes == NULL) || (Buffer == NULL)) {
return EFI_INVALID_PARAMETER;
}
FvbDevice = FVB_DEVICE_FROM_THIS (This);
SmmFvb = FvbDevice->SmmFvbInstance;
//
// Initialize the communicate buffer.
//
PayloadSize = sizeof (SMM_FVB_READ_WRITE_HEADER) + *NumBytes;
Status = InitCommunicateBuffer (
(VOID **)&SmmCommunicateHeader,
(VOID **)&SmmFvbReadWriteHeader,
PayloadSize, EFI_FUNCTION_READ
);
if (EFI_ERROR (Status)) {
return Status;
}
SmmFvbReadWriteHeader->SmmFvb = SmmFvb;
SmmFvbReadWriteHeader->Lba = Lba;
SmmFvbReadWriteHeader->Offset = Offset;
SmmFvbReadWriteHeader->NumBytes = *NumBytes;
//
// Send data to SMM.
//
Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
//
// Get data from SMM.
//
*NumBytes = SmmFvbReadWriteHeader->NumBytes;
if (!EFI_ERROR (Status)) {
CopyMem (Buffer, (UINT8 *)(SmmFvbReadWriteHeader + 1), *NumBytes);
}
FreePool (SmmCommunicateHeader);
return Status;
}
/**
Writes data beginning at Lba:Offset from FV. The write terminates either
when *NumBytes of data have been written, or when a block boundary is
reached. *NumBytes is updated to reflect the actual number of bytes
written. The write opertion does not include erase. This routine will
attempt to write only the specified bytes. If the writes do not stick,
it will return an error.
@param[in] This Calling context.
@param[in] Lba Block in which to begin write.
@param[in] Offset Offset in the block at which to begin write.
@param[in,out] NumBytes On input, indicates the requested write size. On
output, indicates the actual number of bytes written.
@param[in] Buffer Buffer containing source data for the write.
@retval EFI_SUCCESS The firmware volume was written successfully.
@retval EFI_BAD_BUFFER_SIZE Write attempted across a LBA boundary. On output,
NumBytes contains the total number of bytes
actually written.
@retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state.
@retval EFI_DEVICE_ERROR The block device is not functioning correctly and
could not be written.
@retval EFI_INVALID_PARAMETER NumBytes or Buffer are NULL.
**/
EFI_STATUS
EFIAPI
FvbWrite (
IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
IN EFI_LBA Lba,
IN UINTN Offset,
IN OUT UINTN *NumBytes,
IN UINT8 *Buffer
)
{
EFI_STATUS Status;
UINTN PayloadSize;
EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader;
SMM_FVB_READ_WRITE_HEADER *SmmFvbReadWriteHeader;
EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL *SmmFvb;
EFI_FVB_DEVICE *FvbDevice;
if ((NumBytes == NULL) || (Buffer == NULL)) {
return EFI_INVALID_PARAMETER;
}
FvbDevice = FVB_DEVICE_FROM_THIS (This);
SmmFvb = FvbDevice->SmmFvbInstance;
//
// Initialize the communicate buffer.
//
PayloadSize = sizeof (SMM_FVB_READ_WRITE_HEADER) + *NumBytes;
Status = InitCommunicateBuffer (
(VOID **)&SmmCommunicateHeader,
(VOID **)&SmmFvbReadWriteHeader,
PayloadSize,
EFI_FUNCTION_WRITE
);
if (EFI_ERROR (Status)) {
return Status;
}
SmmFvbReadWriteHeader->SmmFvb = SmmFvb;
SmmFvbReadWriteHeader->Lba = Lba;
SmmFvbReadWriteHeader->Offset = Offset;
SmmFvbReadWriteHeader->NumBytes = *NumBytes;
CopyMem ((UINT8 *)(SmmFvbReadWriteHeader + 1), Buffer, *NumBytes);
//
// Send data to SMM.
//
Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
//
// Get data from SMM.
//
*NumBytes = SmmFvbReadWriteHeader->NumBytes;
FreePool (SmmCommunicateHeader);
return Status;
}
/**
The EraseBlock() function erases NumOfLba blocks started from StartingLba.
@param[in] This Calling context.
@param[in] StartingLba Starting LBA followed to erase.
@param[in] NumOfLba Number of block to erase.
@retval EFI_SUCCESS The erase request was successfully completed.
@retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state.
@retval EFI_DEVICE_ERROR The block device is not functioning correctly and
could not be written. Firmware device may have been
partially erased.
**/
EFI_STATUS
EraseBlock (
IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
IN EFI_LBA StartingLba,
IN UINTN NumOfLba
)
{
EFI_STATUS Status;
UINTN PayloadSize;
EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader;
SMM_FVB_BLOCKS_HEADER *SmmFvbBlocksHeader;
EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL *SmmFvb;
EFI_FVB_DEVICE *FvbDevice;
FvbDevice = FVB_DEVICE_FROM_THIS (This);
SmmFvb = FvbDevice->SmmFvbInstance;
//
// Initialize the communicate buffer.
//
PayloadSize = sizeof (SMM_FVB_BLOCKS_HEADER);
Status = InitCommunicateBuffer (
(VOID **)&SmmCommunicateHeader,
(VOID **)&SmmFvbBlocksHeader,
PayloadSize,
EFI_FUNCTION_ERASE_BLOCKS
);
if (EFI_ERROR (Status)) {
return Status;
}
SmmFvbBlocksHeader->SmmFvb = SmmFvb;
SmmFvbBlocksHeader->StartLba = StartingLba;
SmmFvbBlocksHeader->NumOfLba = NumOfLba;
//
// Send data to SMM.
//
Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
//
// Get data from SMM.
//
FreePool (SmmCommunicateHeader);
return Status;
}
/**
The EraseBlocks() 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.
@param[in] This Calling context/
@param[in] ... Starting LBA followed by Number of Lba to erase.
a -1 to terminate the list.
/
@retval EFI_SUCCESS The erase request was successfully completed
@retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state/
@retval EFI_DEVICE_ERROR The block device is not functioning correctly and
could not be written. Firmware device may have been
partially erased/
**/
EFI_STATUS
EFIAPI
FvbEraseBlocks (
IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
...
)
{
EFI_STATUS Status;
VA_LIST Marker;
EFI_LBA StartingLba;
UINTN NumOfLba;
Status = EFI_SUCCESS;
//
// Check the parameter.
//
VA_START (Marker, This);
do {
StartingLba = VA_ARG (Marker, EFI_LBA);
if (StartingLba == EFI_LBA_LIST_TERMINATOR ) {
break;
}
NumOfLba = VA_ARG (Marker, UINT32);
if (NumOfLba == 0) {
return EFI_INVALID_PARAMETER;
}
} while ( 1 );
VA_END (Marker);
//
// Erase the blocks.
//
VA_START (Marker, This);
do {
StartingLba = VA_ARG (Marker, EFI_LBA);
if (StartingLba == EFI_LBA_LIST_TERMINATOR ) {
break;
}
NumOfLba = VA_ARG (Marker, UINT32);
Status = EraseBlock (This, StartingLba, NumOfLba);
if (EFI_ERROR (Status)) {
break;
}
} while ( 1 );
VA_END (Marker);
return Status;
}
/**
Install the FVB protocol which based on SMM FVB protocol.
@param[in] SmmFvb The SMM FVB protocol.
**/
VOID
InstallFvb (
IN EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL *SmmFvb
)
{
EFI_STATUS Status;
EFI_HANDLE FvbHandle;
EFI_FVB_DEVICE *FvbDevice;
EFI_FIRMWARE_VOLUME_HEADER *VolumeHeader;
EFI_PHYSICAL_ADDRESS Address;
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *OldFvbInterface;
FvbDevice = AllocateRuntimeCopyPool (sizeof (EFI_FVB_DEVICE), &mFvbDeviceTemplate);
ASSERT (FvbDevice != NULL);
FvbDevice->SmmFvbInstance = SmmFvb;
Status = gBS->LocateProtocol (
&gEfiSmmCommunicationProtocolGuid,
NULL,
(VOID **) &mSmmCommunication
);
ASSERT_EFI_ERROR (Status);
Status = GetPhysicalAddress (SmmFvb, &Address);
ASSERT_EFI_ERROR (Status);
VolumeHeader = (EFI_FIRMWARE_VOLUME_HEADER *) (UINTN)Address;
//
// Set up the devicepath.
//
if (VolumeHeader->ExtHeaderOffset == 0) {
//
// FV does not contains extension header, then produce MEMMAP_DEVICE_PATH.
//
FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) AllocateRuntimeCopyPool (sizeof (FV_MEMMAP_DEVICE_PATH), &mFvMemmapDevicePathTemplate);
((FV_MEMMAP_DEVICE_PATH *) FvbDevice->DevicePath)->MemMapDevPath.StartingAddress = (UINTN)Address;
((FV_MEMMAP_DEVICE_PATH *) FvbDevice->DevicePath)->MemMapDevPath.EndingAddress = (UINTN)Address + VolumeHeader->FvLength - 1;
} else {
FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) AllocateRuntimeCopyPool (sizeof (FV_PIWG_DEVICE_PATH), &mFvPIWGDevicePathTemplate);
CopyGuid (
&((FV_PIWG_DEVICE_PATH *)FvbDevice->DevicePath)->FvDevPath.FvName,
(GUID *)(UINTN)((UINTN)Address + VolumeHeader->ExtHeaderOffset)
);
}
//
// Find a handle with a matching device path that has supports FW Block protocol.
//
Status = gBS->LocateDevicePath (
&gEfiFirmwareVolumeBlockProtocolGuid,
&FvbDevice->DevicePath,
&FvbHandle
);
if (EFI_ERROR (Status) ) {
//
// LocateDevicePath fails so install a new interface and device path.
//
FvbHandle = NULL;
Status = gBS->InstallMultipleProtocolInterfaces (
&FvbHandle,
&gEfiFirmwareVolumeBlockProtocolGuid,
&FvbDevice->FvbInstance,
&gEfiDevicePathProtocolGuid,
FvbDevice->DevicePath,
NULL
);
ASSERT_EFI_ERROR (Status);
} else if (IsDevicePathEnd (FvbDevice->DevicePath)) {
//
// Device allready exists, so reinstall the FVB protocol.
//
Status = gBS->HandleProtocol (
FvbHandle,
&gEfiFirmwareVolumeBlockProtocolGuid,
(VOID **) &OldFvbInterface
);
ASSERT_EFI_ERROR (Status);
Status = gBS->ReinstallProtocolInterface (
FvbHandle,
&gEfiFirmwareVolumeBlockProtocolGuid,
OldFvbInterface,
&FvbDevice->FvbInstance
);
ASSERT_EFI_ERROR (Status);
} else {
//
// There was a FVB protocol on an End Device Path node.
//
ASSERT (FALSE);
}
}
/**
SMM Firmware Volume Block Protocol notification event handler.
Discover NV Variable Store and install Variable Write Arch Protocol.
@param[in] Event Event whose notification function is being invoked.
@param[in] Context Pointer to the notification function's context.
**/
VOID
EFIAPI
SmmFvbReady (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
EFI_HANDLE *HandleBuffer;
UINTN HandleCount;
UINTN Index;
EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL *SmmFvb;
//
// Locate all handles of Smm Fvb protocol.
//
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiSmmFirmwareVolumeBlockProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR (Status)) {
return ;
}
//
// Install FVB protocol.
//
for (Index = 0; Index < HandleCount; Index++) {
SmmFvb = NULL;
Status = gBS->HandleProtocol (
HandleBuffer[Index],
&gEfiSmmFirmwareVolumeBlockProtocolGuid,
(VOID **) &SmmFvb
);
if (EFI_ERROR (Status)) {
break;
}
InstallFvb (SmmFvb);
}
FreePool (HandleBuffer);
}
/**
The driver entry point for Firmware Volume Block Driver.
The function does the necessary initialization work
Firmware Volume Block Driver.
@param[in] ImageHandle The firmware allocated handle for the UEFI image.
@param[in] SystemTable A pointer to the EFI system table.
@retval EFI_SUCCESS This funtion always return EFI_SUCCESS.
It will ASSERT on errors.
**/
EFI_STATUS
EFIAPI
FvbSmmDxeInitialize (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
VOID *SmmFvbRegistration;
//
// Smm FVB driver is ready.
//
EfiCreateProtocolNotifyEvent (
&gEfiSmmFirmwareVolumeBlockProtocolGuid,
TPL_CALLBACK,
SmmFvbReady,
NULL,
&SmmFvbRegistration
);
return EFI_SUCCESS;
}