/** @file Functions in this file will program the image into flash area. Copyright (c) 2002 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #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; }