summaryrefslogtreecommitdiff
path: root/Vlv2TbltDevicePkg/UpdateDriverDxe/FlashUpdate.c
diff options
context:
space:
mode:
Diffstat (limited to 'Vlv2TbltDevicePkg/UpdateDriverDxe/FlashUpdate.c')
-rw-r--r--Vlv2TbltDevicePkg/UpdateDriverDxe/FlashUpdate.c1217
1 files changed, 1217 insertions, 0 deletions
diff --git a/Vlv2TbltDevicePkg/UpdateDriverDxe/FlashUpdate.c b/Vlv2TbltDevicePkg/UpdateDriverDxe/FlashUpdate.c
new file mode 100644
index 0000000000..95216c9d24
--- /dev/null
+++ b/Vlv2TbltDevicePkg/UpdateDriverDxe/FlashUpdate.c
@@ -0,0 +1,1217 @@
+/** @file
+ Functions in this file will program the image into flash area.
+
+ Copyright (c) 2004 - 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 "UpdateDriver.h"
+
+/**
+ Write a block size data into flash.
+
+ @param FvbProtocol Pointer to FVB protocol.
+ @param Lba Logic block index to be updated.
+ @param BlockSize Block size
+ @param Buffer Buffer data to be written.
+
+ @retval EFI_SUCCESS Write data successfully.
+ @retval other errors Write data failed.
+
+**/
+EFI_STATUS
+UpdateOneBlock (
+ IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvbProtocol,
+ IN EFI_LBA Lba,
+ IN UINTN BlockSize,
+ IN UINT8 *Buffer
+ )
+{
+ EFI_STATUS Status;
+ UINTN Size;
+
+ //
+ // First erase the block
+ //
+ Status = FvbProtocol->EraseBlocks (
+ FvbProtocol,
+ Lba, // Lba
+ 1, // NumOfBlocks
+ EFI_LBA_LIST_TERMINATOR
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Write the block
+ //
+ Size = BlockSize;
+ Status = FvbProtocol->Write (
+ FvbProtocol,
+ Lba, // Lba
+ 0, // Offset
+ &Size, // Size
+ Buffer // Buffer
+ );
+ if ((EFI_ERROR (Status)) || (Size != BlockSize)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Write buffer data in a flash block.
+
+ @param FvbProtocol Pointer to FVB protocol.
+ @param Lba Logic block index to be updated.
+ @param Offset The offset within the block.
+ @param Length Size of buffer to be updated.
+ @param BlockSize Block size.
+ @param Buffer Buffer data to be updated.
+
+ @retval EFI_SUCCESS Write data successfully.
+ @retval other errors Write data failed.
+
+**/
+EFI_STATUS
+UpdateBufferInOneBlock (
+ IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvbProtocol,
+ IN EFI_LBA Lba,
+ IN UINTN Offset,
+ IN UINTN Length,
+ IN UINTN BlockSize,
+ IN UINT8 *Buffer
+ )
+{
+ EFI_STATUS Status;
+ UINTN Size;
+ UINT8 *ReservedBuffer;
+
+ //
+ // If we are going to update a whole block
+ //
+ if ((Offset == 0) && (Length == BlockSize)) {
+ Status = UpdateOneBlock (
+ FvbProtocol,
+ Lba,
+ BlockSize,
+ Buffer
+ );
+ return Status;
+ }
+
+ //
+ // If it is not a full block update, we need to coalesce data in
+ // the block that is not going to be updated and new data together.
+ //
+
+ //
+ // Allocate a reserved buffer to make up the final buffer for update
+ //
+ ReservedBuffer = NULL;
+ ReservedBuffer = AllocatePool (BlockSize);
+ if (ReservedBuffer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // First get the original content of the block
+ //
+ Size = BlockSize;
+ Status = FvbProtocol->Read (
+ FvbProtocol,
+ Lba,
+ 0,
+ &Size,
+ ReservedBuffer
+ );
+ if ((EFI_ERROR (Status)) || (Size != BlockSize)) {
+ FreePool (ReservedBuffer);
+ return Status;
+ }
+
+ //
+ // Overwrite the reserved buffer with new content
+ //
+ CopyMem (ReservedBuffer + Offset, Buffer, Length);
+
+ Status = UpdateOneBlock (
+ FvbProtocol,
+ Lba,
+ BlockSize,
+ ReservedBuffer
+ );
+
+ FreePool (ReservedBuffer);
+
+ return Status;
+}
+
+/**
+ Get the last write log, and check the status of last write.
+ If not complete, restart will be taken.
+
+ @param FvbHandle Handle of FVB protocol.
+ @param FtwProtocol FTW protocol instance.
+ @param ConfigData Config data on updating driver.
+ @param PrivateDataSize bytes from the private data
+ stored for this write.
+ @param PrivateData A pointer to a buffer. The function will copy.
+ @param Lba The logical block address of the last write.
+ @param Offset The offset within the block of the last write.
+ @param Length The length of the last write.
+ @param Pending A Boolean value with TRUE indicating
+ that the write was completed.
+
+ @retval EFI_OUT_OF_RESOURCES No enough memory is allocated.
+ @retval EFI_ABORTED The FTW work space is damaged.
+ @retval EFI_NOT_FOUND The last write is not done by this driver.
+ @retval EFI_SUCCESS Last write log is got.
+
+**/
+EFI_STATUS
+RetrieveLastWrite (
+ IN EFI_HANDLE FvbHandle,
+ IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *FtwProtocol,
+ IN UPDATE_CONFIG_DATA *ConfigData,
+ IN UINTN PrivateDataSize,
+ IN OUT UPDATE_PRIVATE_DATA *PrivateData,
+ IN OUT EFI_LBA *Lba,
+ IN OUT UINTN *Offset,
+ IN OUT UINTN *Length,
+ IN OUT BOOLEAN *Pending
+ )
+{
+ EFI_STATUS Status;
+ EFI_GUID CallerId;
+ UINTN PrivateBufferSize;
+ BOOLEAN Complete;
+ VOID *PrivateDataBuffer;
+
+ //
+ // Get the last write
+ //
+ *Pending = FALSE;
+ PrivateBufferSize = PrivateDataSize;
+ PrivateDataBuffer = NULL;
+ Status = FtwProtocol->GetLastWrite (
+ FtwProtocol,
+ &CallerId,
+ Lba,
+ Offset,
+ Length,
+ &PrivateBufferSize,
+ PrivateData,
+ &Complete
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // If there is no incompleted record, return success.
+ //
+ if ((Status == EFI_NOT_FOUND) && Complete) {
+ return EFI_SUCCESS;
+ } else if (Status == EFI_BUFFER_TOO_SMALL) {
+ //
+ // If buffer too small, reallocate buffer and call getlastwrite again
+ //
+ PrivateDataBuffer = AllocatePool (PrivateBufferSize);
+
+ if (PrivateDataBuffer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = FtwProtocol->GetLastWrite (
+ FtwProtocol,
+ &CallerId,
+ Lba,
+ Offset,
+ Length,
+ &PrivateBufferSize,
+ PrivateDataBuffer,
+ &Complete
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool ( PrivateDataBuffer);
+ return EFI_ABORTED;
+ } else {
+ CopyMem (PrivateData, PrivateDataBuffer, PrivateDataSize);
+ FreePool (PrivateDataBuffer);
+ PrivateDataBuffer = NULL;
+ }
+ } else {
+ return EFI_ABORTED;
+ }
+ }
+
+ *Pending = TRUE;
+
+ //
+ // If the caller is not the update driver, then return.
+ // The update driver cannot continue to perform the update
+ //
+ if (CompareMem (&CallerId, &gEfiCallerIdGuid, sizeof (EFI_GUID)) != 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Check the private data and see if it is the one I need.
+ //
+ if (CompareMem (&(PrivateData->FileGuid), &(ConfigData->FileGuid), sizeof(EFI_GUID)) != 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // If the caller is the update driver and complete is not true, then restart().
+ //
+ if (!Complete) {
+ //
+ // Re-start the update
+ //
+ Status = FtwProtocol->Restart (
+ FtwProtocol,
+ FvbHandle
+ );
+ //
+ // If restart() error, then abort().
+ //
+ if (EFI_ERROR (Status)) {
+ FtwProtocol->Abort (FtwProtocol);
+ //
+ // Now set Pending as FALSE as this record has been cleared
+ //
+ *Pending = FALSE;
+ return EFI_SUCCESS;
+ }
+
+ }
+
+ return Status;
+}
+
+/**
+ Update the whole FV image in fault tolerant write method.
+
+ @param FvbHandle Handle of FVB protocol for the updated flash range.
+ @param FvbProtocol FVB protocol.
+ @param BlockMap Block array to specify flash area.
+ @param ConfigData Config data on updating driver.
+ @param ImageBuffer Image buffer to be updated.
+ @param ImageSize Image size.
+
+ @retval EFI_SUCCESS FV image is writed into flash.
+ @retval EFI_INVALID_PARAMETER Config data is not valid.
+ @retval EFI_NOT_FOUND FTW protocol doesn't exist.
+ @retval EFI_OUT_OF_RESOURCES No enough backup space.
+ @retval EFI_ABORTED Error happen when update FV.
+
+**/
+EFI_STATUS
+FaultTolerantUpdateOnWholeFv (
+ IN EFI_HANDLE FvbHandle,
+ IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvbProtocol,
+ IN EFI_FV_BLOCK_MAP_ENTRY *BlockMap,
+ IN UPDATE_CONFIG_DATA *ConfigData,
+ IN UINT8 *ImageBuffer,
+ IN UINTN ImageSize
+ )
+{
+ EFI_STATUS Status;
+ EFI_FAULT_TOLERANT_WRITE_PROTOCOL *FtwProtocol;
+ UINTN MaxBlockSize;
+ UINTN FtwMaxBlockSize;
+ BOOLEAN Pending;
+ UPDATE_PRIVATE_DATA PrivateData;
+ EFI_LBA PendingLba;
+ EFI_LBA Lba;
+ UINTN PendingOffset;
+ UINTN Offset;
+ UINTN PendingLength;
+ UINTN Length;
+ EFI_FV_BLOCK_MAP_ENTRY *PtrMap;
+ UINTN NumOfBlocks;
+ UINTN Index;
+ UINT8 *UpdateBuffer;
+
+ if ((ConfigData->UpdateType != UpdateWholeFV)
+ || (!ConfigData->FaultTolerant)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Get the FTW protocol
+ //
+ Status = gBS->LocateProtocol (
+ &gEfiFaultTolerantWriteProtocolGuid,
+ NULL,
+ (VOID **) &FtwProtocol
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Get the maximum block size of the FV, and number of blocks
+ // NumOfBlocks will be the NumOfUdpates.
+ //
+ MaxBlockSize = 0;
+ NumOfBlocks = 0;
+ PtrMap = BlockMap;
+ while (TRUE) {
+ if ((PtrMap->NumBlocks == 0) || (PtrMap->Length == 0)) {
+ break;
+ }
+ if (MaxBlockSize < PtrMap->Length) {
+ MaxBlockSize = PtrMap->Length;
+ }
+ NumOfBlocks = NumOfBlocks + PtrMap->NumBlocks;
+ PtrMap++;
+ }
+
+ FtwProtocol->GetMaxBlockSize (FtwProtocol, &FtwMaxBlockSize);
+ //
+ // Not enough backup space. return directly
+ //
+ if (FtwMaxBlockSize < MaxBlockSize) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ PendingLba = 0;
+ PendingOffset = 0;
+ PendingLength = 0;
+ Pending = FALSE;
+
+ //
+ // Fault Tolerant Write can only support actual fault tolerance if the write
+ // is a reclaim operation, which means the data buffer (new and old) are
+ // acutally both stored in flash. But for component update write, the data
+ // are now in memory. So we cannot actually recover the data after power
+ // failure.
+ //
+ Status = RetrieveLastWrite (
+ FvbHandle,
+ FtwProtocol,
+ ConfigData,
+ sizeof (UPDATE_PRIVATE_DATA),
+ &PrivateData,
+ &PendingLba,
+ &PendingOffset,
+ &PendingLength,
+ &Pending
+ );
+
+ if (Pending && (Status == EFI_NOT_FOUND)) {
+ //
+ // Cannot continue with the write operation
+ //
+ return EFI_ABORTED;
+ }
+
+ if (EFI_ERROR(Status)) {
+ return EFI_ABORTED;
+ }
+
+ //
+ // Currently we start from the pending write if there is any. But as we
+ // are going to update a whole FV, we can just abort last write and start
+ // from the very begining.
+ //
+ if (!Pending) {
+ //
+ // Now allocte the update private data in FTW. If there is pending
+ // write, it has already been allocated and no need to allocate here.
+ //
+ Status = FtwProtocol->Allocate (
+ FtwProtocol,
+ &gEfiCallerIdGuid,
+ sizeof (UPDATE_PRIVATE_DATA),
+ NumOfBlocks
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ //
+ // Perform the update now. If there are pending writes, we need to
+ // start from the pending write instead of the very beginning.
+ //
+ PtrMap = BlockMap;
+ Lba = 0;
+ Offset = 0;
+ UpdateBuffer = ImageBuffer;
+ CopyMem (
+ (VOID *) &PrivateData.FileGuid,
+ (VOID *) &ConfigData->FileGuid,
+ sizeof (EFI_GUID)
+ );
+
+ while (TRUE) {
+ if ((PtrMap->NumBlocks == 0) || (PtrMap->Length == 0)) {
+ break;
+ }
+ Length = (UINTN)PtrMap->Length;
+ for (Index = 0; Index < PtrMap->NumBlocks; Index++) {
+
+ //
+ // Add an extra check here to see if the pending record is correct
+ //
+ if (Pending && (Lba == PendingLba)) {
+ if ((PendingOffset != Offset) || (PendingLength != Length)) {
+ //
+ // Error.
+ //
+ Status = EFI_ABORTED;
+ break;
+ }
+ }
+
+ if ((!Pending) || (Lba >= PendingLba)) {
+ Status = FtwProtocol->Write (
+ FtwProtocol,
+ Lba, // Lba
+ Offset, // Offset
+ Length, // Size
+ &PrivateData, // Private Data
+ FvbHandle, // FVB handle
+ UpdateBuffer // Buffer
+ );
+ }
+
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ Lba++;
+ UpdateBuffer = (UINT8 *) ((UINTN)UpdateBuffer + Length);
+ }
+
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ PtrMap++;
+ }
+
+ return Status;
+
+}
+
+/**
+ Directly update the whole FV image without fault tolerant write method.
+
+ @param FvbHandle Handle of FVB protocol for the updated flash range.
+ @param FvbProtocol FVB protocol.
+ @param BlockMap Block array to specify flash area.
+ @param ConfigData Config data on updating driver.
+ @param ImageBuffer Image buffer to be updated.
+ @param ImageSize Image size.
+
+ @retval EFI_SUCCESS FV image is writed into flash.
+ @retval EFI_INVALID_PARAMETER Config data is not valid.
+ @retval EFI_ABORTED Error happen when update FV.
+
+**/
+EFI_STATUS
+NonFaultTolerantUpdateOnWholeFv (
+ IN EFI_HANDLE FvbHandle,
+ IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvbProtocol,
+ IN EFI_FV_BLOCK_MAP_ENTRY *BlockMap,
+ IN UPDATE_CONFIG_DATA *ConfigData,
+ IN UINT8 *ImageBuffer,
+ IN UINTN ImageSize
+ )
+{
+ EFI_STATUS Status;
+ EFI_FV_BLOCK_MAP_ENTRY *PtrMap;
+ UINTN Index;
+ EFI_LBA UpdateLba;
+ UINT8 *UpdateBuffer;
+ UINTN UpdateSize;
+
+ if ((ConfigData->UpdateType != UpdateWholeFV )
+ || (ConfigData->FaultTolerant)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = EFI_SUCCESS;
+ PtrMap = BlockMap;
+ UpdateLba = 0;
+ UpdateBuffer = ImageBuffer;
+
+ //
+ // Perform the update now
+ //
+ while (TRUE) {
+ if ((PtrMap->NumBlocks == 0) || (PtrMap->Length == 0)) {
+ break;
+ }
+ UpdateSize = (UINTN)PtrMap->Length;
+ for (Index = 0; Index < PtrMap->NumBlocks; Index++) {
+ Status = UpdateOneBlock (
+ FvbProtocol,
+ UpdateLba,
+ UpdateSize,
+ UpdateBuffer
+ );
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ UpdateLba++;
+ UpdateBuffer = (UINT8 *) ((UINTN)UpdateBuffer + UpdateSize);
+ }
+
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ PtrMap++;
+ }
+
+ return Status;
+}
+
+/**
+ Update the whole FV image, and reinsall FVB protocol for the updated FV image.
+
+ @param FvbHandle Handle of FVB protocol for the updated flash range.
+ @param FvbProtocol FVB protocol.
+ @param ConfigData Config data on updating driver.
+ @param ImageBuffer Image buffer to be updated.
+ @param ImageSize Image size.
+
+ @retval EFI_INVALID_PARAMETER Update type is not UpdateWholeFV.
+ Or Image size is not same to the size of whole FV.
+ @retval EFI_OUT_OF_RESOURCES No enoug memory is allocated.
+ @retval EFI_SUCCESS FV image is updated, and its FVB protocol is reinstalled.
+
+**/
+EFI_STATUS
+PerformUpdateOnWholeFv (
+ IN EFI_HANDLE FvbHandle,
+ IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvbProtocol,
+ IN UPDATE_CONFIG_DATA *ConfigData,
+ IN UINT8 *ImageBuffer,
+ IN UINTN ImageSize
+)
+{
+ EFI_STATUS Status;
+ EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;
+ EFI_FV_BLOCK_MAP_ENTRY *BlockMap;
+ CHAR16 *TmpStr;
+
+ if (ConfigData->UpdateType != UpdateWholeFV) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Get the header of the firmware volume
+ //
+ FwVolHeader = NULL;
+ FwVolHeader = AllocatePool (((EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) (ConfigData->BaseAddress)))->HeaderLength);
+ if (FwVolHeader == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ CopyMem (
+ FwVolHeader,
+ (VOID *) ((UINTN) (ConfigData->BaseAddress)),
+ ((EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) (ConfigData->BaseAddress)))->HeaderLength
+ );
+
+ //
+ // Check if ImageSize is the same as the size of the whole FV
+ //
+ if ((UINT64)ImageSize != FwVolHeader->FvLength) {
+ FreePool (FwVolHeader);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Print on screen
+ //
+ TmpStr = HiiGetString (gHiiHandle, STRING_TOKEN(UPDATE_FIRMWARE_VOLUME), NULL);
+ if (TmpStr != NULL) {
+ Print (TmpStr, ConfigData->BaseAddress, (FwVolHeader->FvLength + ConfigData->BaseAddress));
+ FreePool (TmpStr);
+ }
+
+ DEBUG ((EFI_D_UPDATE, "UpdateDriver: updating whole FV from %08LX to %08LX\n",
+ ConfigData->BaseAddress, (FwVolHeader->FvLength + ConfigData->BaseAddress)));
+
+ //
+ // Get the block map of the firmware volume
+ //
+ BlockMap = &(FwVolHeader->BlockMap[0]);
+
+ //
+ // It is about the same if we are going to fault tolerantly update
+ // a certain FV in our current design. But we divide non-fault tolerant
+ // and fault tolerant udpate here for better maintenance as fault
+ // tolerance may change and may be done more wisely if we have space.
+ //
+ if (ConfigData->FaultTolerant) {
+ Status = FaultTolerantUpdateOnWholeFv (
+ FvbHandle,
+ FvbProtocol,
+ BlockMap,
+ ConfigData,
+ ImageBuffer,
+ ImageSize
+ );
+ } else {
+ Status = NonFaultTolerantUpdateOnWholeFv (
+ FvbHandle,
+ FvbProtocol,
+ BlockMap,
+ ConfigData,
+ ImageBuffer,
+ ImageSize
+ );
+ }
+
+ FreePool (FwVolHeader);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // As the whole FV has been replaced, the FV driver shall re-parse the
+ // firmware volume. So re-install FVB protocol here
+ //
+ Status = gBS->ReinstallProtocolInterface (
+ FvbHandle,
+ &gEfiFirmwareVolumeBlockProtocolGuid,
+ FvbProtocol,
+ FvbProtocol
+ );
+
+ return Status;
+}
+
+/**
+ Update certain file in the FV.
+
+ @param FvbHandle Handle of FVB protocol for the updated flash range.
+ @param FvbProtocol FVB protocol.
+ @param ConfigData Config data on updating driver.
+ @param ImageBuffer Image buffer to be updated.
+ @param ImageSize Image size.
+ @param FileType FFS file type.
+ @param FileAttributes FFS file attribute
+
+ @retval EFI_INVALID_PARAMETER Update type is not UpdateFvFile.
+ Or Image size is not same to the size of whole FV.
+ @retval EFI_UNSUPPORTED PEIM FFS is unsupported to be updated.
+ @retval EFI_SUCCESS The FFS file is added into FV.
+
+**/
+EFI_STATUS
+PerformUpdateOnFvFile (
+ IN EFI_HANDLE FvbHandle,
+ IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvbProtocol,
+ IN UPDATE_CONFIG_DATA *ConfigData,
+ IN UINT8 *ImageBuffer,
+ IN UINTN ImageSize,
+ IN EFI_FV_FILETYPE FileType,
+ IN EFI_FV_FILE_ATTRIBUTES FileAttributes
+ )
+{
+ EFI_STATUS Status;
+ EFI_FIRMWARE_VOLUME2_PROTOCOL *FwVolProtocol;
+ EFI_FV_WRITE_FILE_DATA FileData;
+ CHAR16 *TmpStr;
+
+ if (ConfigData->UpdateType != UpdateFvFile) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Print on screen
+ //
+ TmpStr = HiiGetString (gHiiHandle, STRING_TOKEN(UPDATE_FIRMWARE_VOLUME_FILE), NULL);
+ if (TmpStr != NULL) {
+ Print (TmpStr, &(ConfigData->FileGuid));
+ FreePool (TmpStr);
+ }
+
+ DEBUG ((EFI_D_UPDATE, "UpdateDriver: updating file: %g\n",
+ &(ConfigData->FileGuid)));
+
+ //
+ // Get Firmware volume protocol on this FVB protocol
+ //
+ Status = gBS->HandleProtocol (
+ FvbHandle,
+ &gEfiFirmwareVolume2ProtocolGuid,
+ (VOID **) &FwVolProtocol
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // If it is a PEIM, we need first to rebase it before committing
+ // the write to target
+ //
+ if ((FileType == EFI_FV_FILETYPE_PEI_CORE) || (FileType == EFI_FV_FILETYPE_PEIM )
+ || (FileType == EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ FileData.NameGuid = &(ConfigData->FileGuid);
+ FileData.Type = FileType;
+ FileData.FileAttributes = FileAttributes;
+ FileData.Buffer = ImageBuffer;
+ FileData.BufferSize = (UINT32) ImageSize;
+
+ Status = FwVolProtocol->WriteFile (
+ FwVolProtocol,
+ 1, // NumberOfFiles
+ (EFI_FV_WRITE_POLICY)ConfigData->FaultTolerant,
+ &FileData
+ );
+ return Status;
+}
+
+/**
+ Update the buffer into flash area in fault tolerant write method.
+
+ @param ImageBuffer Image buffer to be updated.
+ @param SizeLeft Size of the image buffer.
+ @param UpdatedSize Size of the updated buffer.
+ @param ConfigData Config data on updating driver.
+ @param FlashAddress Flash address to be updated as start address.
+ @param FvbProtocol FVB protocol.
+ @param FvbHandle Handle of FVB protocol for the updated flash range.
+
+ @retval EFI_SUCCESS Buffer data is updated into flash.
+ @retval EFI_INVALID_PARAMETER Base flash address is not in FVB flash area.
+ @retval EFI_NOT_FOUND FTW protocol doesn't exist.
+ @retval EFI_OUT_OF_RESOURCES No enough backup space.
+ @retval EFI_ABORTED Error happen when update flash area.
+
+**/
+EFI_STATUS
+FaultTolerantUpdateOnPartFv (
+ IN UINT8 *ImageBuffer,
+ IN UINTN SizeLeft,
+ IN OUT UINTN *UpdatedSize,
+ IN UPDATE_CONFIG_DATA *ConfigData,
+ IN EFI_PHYSICAL_ADDRESS FlashAddress,
+ IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvbProtocol,
+ IN EFI_HANDLE FvbHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;
+ EFI_FIRMWARE_VOLUME_HEADER *FwVolHeaderTmp;
+ EFI_PHYSICAL_ADDRESS BaseAddress;
+ EFI_PHYSICAL_ADDRESS FvBase;
+ EFI_PHYSICAL_ADDRESS NextBlock;
+ EFI_FV_BLOCK_MAP_ENTRY *BlockMap;
+ EFI_FV_BLOCK_MAP_ENTRY *PtrMap;
+ UINTN NumOfUpdates;
+ UINTN TotalSize;
+ EFI_PHYSICAL_ADDRESS StartAddress;
+ EFI_FAULT_TOLERANT_WRITE_PROTOCOL *FtwProtocol;
+ UINTN MaxBlockSize;
+ UINTN FtwMaxBlockSize;
+ BOOLEAN Pending;
+ UPDATE_PRIVATE_DATA PrivateData;
+ EFI_LBA PendingLba;
+ EFI_LBA Lba;
+ UINTN BlockSize;
+ UINTN PendingOffset;
+ UINTN Offset;
+ UINTN PendingLength;
+ UINTN Length;
+ UINTN Index;
+ UINT8 *Image;
+
+ //
+ // Get the block map to update the block one by one
+ //
+ Status = FvbProtocol->GetPhysicalAddress (
+ FvbProtocol,
+ &FvBase
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ FwVolHeaderTmp = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)FvBase;
+ if ((FlashAddress < FvBase) || (FlashAddress > (FvBase + FwVolHeaderTmp->FvLength))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)AllocateCopyPool (
+ FwVolHeaderTmp->HeaderLength,
+ FwVolHeaderTmp
+ );
+ if (FwVolHeader == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // For fault tolerant write, we have to know how many blocks we need to
+ // update. So we will calculate number of updates and max block size first
+ //
+ NumOfUpdates = 0;
+ MaxBlockSize = 0;
+ TotalSize = SizeLeft;
+ StartAddress = FlashAddress;
+ BaseAddress = FvBase;
+ BlockMap = &(FwVolHeader->BlockMap[0]);
+ PtrMap = BlockMap;
+
+ while (TotalSize > 0) {
+ if ((PtrMap->NumBlocks == 0) || (PtrMap->Length == 0)) {
+ break;
+ }
+
+ BlockSize = PtrMap->Length;
+ for (Index = 0; Index < PtrMap->NumBlocks; Index++) {
+ NextBlock = BaseAddress + BlockSize;
+ //
+ // Check if this block need to be updated
+ //
+ if ((StartAddress >= BaseAddress) && (StartAddress < NextBlock)) {
+ //
+ // Get the maximum block size
+ //
+ if (MaxBlockSize < BlockSize) {
+ MaxBlockSize = BlockSize;
+ }
+
+ //
+ // This block shall be udpated. So increment number of updates
+ //
+ NumOfUpdates++;
+ Offset = (UINTN) (StartAddress - BaseAddress);
+ Length = TotalSize;
+ if ((Length + Offset ) > BlockSize) {
+ Length = BlockSize - Offset;
+ }
+
+ StartAddress = StartAddress + Length;
+ TotalSize = TotalSize - Length;
+ if (TotalSize <= 0) {
+ break;
+ }
+ }
+ BaseAddress = NextBlock;
+ }
+ PtrMap++;
+ }
+
+ //
+ // Get the FTW protocol
+ //
+ Status = gBS->LocateProtocol (
+ &gEfiFaultTolerantWriteProtocolGuid,
+ NULL,
+ (VOID **) &FtwProtocol
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (FwVolHeader);
+ return EFI_NOT_FOUND;
+ }
+
+ FtwProtocol->GetMaxBlockSize (FtwProtocol, &FtwMaxBlockSize);
+
+ //
+ // Not enough backup space. return directly
+ //
+ if (FtwMaxBlockSize < MaxBlockSize) {
+ FreePool (FwVolHeader);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ PendingLba = 0;
+ PendingOffset = 0;
+ PendingLength = 0;
+ Pending = FALSE;
+
+ //
+ // Fault Tolerant Write can only support actual fault tolerance if the write
+ // is a reclaim operation, which means the data buffer (new and old) are
+ // acutally both stored in flash. But for component update write, the data
+ // are now in memory. So we cannot actually recover the data after power
+ // failure.
+ //
+ Status = RetrieveLastWrite (
+ FvbHandle,
+ FtwProtocol,
+ ConfigData,
+ sizeof (UPDATE_PRIVATE_DATA),
+ &PrivateData,
+ &PendingLba,
+ &PendingOffset,
+ &PendingLength,
+ &Pending
+ );
+ if (Pending && (Status == EFI_NOT_FOUND)) {
+ //
+ // I'm not the owner of the pending fault tolerant write record
+ // Cannot continue with the write operation
+ //
+ FreePool (FwVolHeader);
+ return EFI_ABORTED;
+ }
+
+ if (EFI_ERROR(Status)) {
+ FreePool (FwVolHeader);
+ return EFI_ABORTED;
+ }
+
+ //
+ // Currently we start from the pending write if there is any. But if the
+ // caller is exactly the same, and the new data is already a in memory, (it
+ // cannot be stored in flash in last write,) we can just abort last write
+ // and start from the very begining.
+ //
+ if (!Pending) {
+ //
+ // Now allocte the update private data in FTW. If there is pending
+ // write, it has already been allocated and no need to allocate here.
+ //
+ Status = FtwProtocol->Allocate (
+ FtwProtocol,
+ &gEfiCallerIdGuid,
+ sizeof (UPDATE_PRIVATE_DATA),
+ NumOfUpdates
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (FwVolHeader);
+ return Status;
+ }
+ }
+
+ //
+ // Perform the update now. If there are pending writes, we need to
+ // start from the pending write instead of the very beginning.
+ //
+ TotalSize = SizeLeft;
+ Lba = 0;
+ StartAddress = FlashAddress;
+ BaseAddress = FvBase;
+ PtrMap = BlockMap;
+ Image = ImageBuffer;
+ CopyMem (
+ (VOID *) &PrivateData.FileGuid,
+ (VOID *) &ConfigData->FileGuid,
+ sizeof (EFI_GUID)
+ );
+
+ while (TotalSize > 0) {
+ if ((PtrMap->NumBlocks == 0) || (PtrMap->Length == 0)) {
+ break;
+ }
+
+ BlockSize = (UINTN)PtrMap->Length;
+ for (Index = 0; Index < PtrMap->NumBlocks; Index++) {
+ NextBlock = BaseAddress + BlockSize;
+ if ((StartAddress >= BaseAddress) && (StartAddress < NextBlock)) {
+ //
+ // So we need to update this block
+ //
+ Offset = (UINTN) (StartAddress - BaseAddress);
+ Length = TotalSize;
+ if ((Length + Offset ) > BlockSize) {
+ Length = BlockSize - Offset;
+ }
+
+ //
+ // Add an extra check here to see if the pending record is correct
+ //
+ if (Pending && (Lba == PendingLba)) {
+ if ((PendingOffset != Offset) || (PendingLength != Length)) {
+ //
+ // Error.
+ //
+ Status = EFI_ABORTED;
+ break;
+ }
+ }
+
+ if ((!Pending) || (Lba >= PendingLba)) {
+ DEBUG ((EFI_D_UPDATE, "Update Flash area from %08LX to %08LX\n", StartAddress, (UINT64)StartAddress + Length));
+ Status = FtwProtocol->Write (
+ FtwProtocol,
+ Lba, // Lba
+ Offset, // Offset
+ Length, // Size
+ &PrivateData, // Private Data
+ FvbHandle, // FVB handle
+ Image // Buffer
+ );
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ }
+
+ //
+ // Now increment StartAddress, ImageBuffer and decrease the
+ // left size to prepare for the next block update.
+ //
+ StartAddress = StartAddress + Length;
+ Image = Image + Length;
+ TotalSize = TotalSize - Length;
+ if (TotalSize <= 0) {
+ break;
+ }
+ }
+ BaseAddress = NextBlock;
+ Lba++;
+ }
+
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ PtrMap++;
+ }
+
+ FreePool (FwVolHeader);
+
+ *UpdatedSize = SizeLeft - TotalSize;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Directly update the buffer into flash area without fault tolerant write method.
+
+ @param ImageBuffer Image buffer to be updated.
+ @param SizeLeft Size of the image buffer.
+ @param UpdatedSize Size of the updated buffer.
+ @param FlashAddress Flash address to be updated as start address.
+ @param FvbProtocol FVB protocol.
+ @param FvbHandle Handle of FVB protocol for the updated flash range.
+
+ @retval EFI_SUCCESS Buffer data is updated into flash.
+ @retval EFI_INVALID_PARAMETER Base flash address is not in FVB flash area.
+ @retval EFI_OUT_OF_RESOURCES No enough backup space.
+
+**/
+EFI_STATUS
+NonFaultTolerantUpdateOnPartFv (
+ IN UINT8 *ImageBuffer,
+ IN UINTN SizeLeft,
+ IN OUT UINTN *UpdatedSize,
+ IN EFI_PHYSICAL_ADDRESS FlashAddress,
+ IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvbProtocol,
+ IN EFI_HANDLE FvbHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;
+ EFI_FIRMWARE_VOLUME_HEADER *FwVolHeaderTmp;
+ EFI_PHYSICAL_ADDRESS BaseAddress;
+ EFI_PHYSICAL_ADDRESS NextBlock;
+ EFI_FV_BLOCK_MAP_ENTRY *BlockMap;
+ UINTN Index;
+ UINTN TotalSize;
+ UINTN BlockSize;
+ EFI_LBA Lba;
+ UINTN Offset;
+ UINTN Length;
+ UINT8 *Image;
+
+ //
+ // Get the block map to update the block one by one
+ //
+ Status = FvbProtocol->GetPhysicalAddress (
+ FvbProtocol,
+ &BaseAddress
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ FwVolHeaderTmp = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)BaseAddress;
+ if ((FlashAddress < BaseAddress) || (FlashAddress > ( BaseAddress + FwVolHeaderTmp->FvLength ))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)AllocateCopyPool (
+ FwVolHeaderTmp->HeaderLength,
+ FwVolHeaderTmp
+ );
+ if (FwVolHeader == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Image = ImageBuffer;
+ TotalSize = SizeLeft;
+ BlockMap = &(FwVolHeader->BlockMap[0]);
+ Lba = 0;
+
+ while (TotalSize > 0) {
+ if ((BlockMap->NumBlocks == 0) || (BlockMap->Length == 0)) {
+ break;
+ }
+
+ BlockSize = BlockMap->Length;
+ for (Index = 0 ; Index < BlockMap->NumBlocks ; Index++) {
+ NextBlock = BaseAddress + BlockSize;
+ if ((FlashAddress >= BaseAddress) && (FlashAddress < NextBlock)) {
+ //
+ // So we need to update this block
+ //
+ Offset = (UINTN) FlashAddress - (UINTN) BaseAddress;
+ Length = TotalSize;
+ if ((Length + Offset ) > BlockSize) {
+ Length = BlockSize - Offset;
+ }
+
+ DEBUG ((EFI_D_UPDATE, "Update Flash area from %08LX to %08LX\n", FlashAddress, (UINT64)FlashAddress + Length));
+ //
+ // Update the block
+ //
+ Status = UpdateBufferInOneBlock (
+ FvbProtocol,
+ Lba,
+ Offset,
+ Length,
+ BlockSize,
+ Image
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (FwVolHeader);
+ return Status;
+ }
+
+ //
+ // Now increment FlashAddress, ImageBuffer and decrease the
+ // left size to prepare for the next block update.
+ //
+ FlashAddress = FlashAddress + Length;
+ Image = Image + Length;
+ TotalSize = TotalSize - Length;
+ if (TotalSize <= 0) {
+ break;
+ }
+ }
+ BaseAddress = NextBlock;
+ Lba++;
+ }
+
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ BlockMap++;
+ }
+
+ FreePool (FwVolHeader);
+
+ *UpdatedSize = SizeLeft - TotalSize;
+
+ return EFI_SUCCESS;
+}