| /** @file | |
| Functions in this file will program the image into flash area. | |
| Copyright (c) 2002 - 2010, 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; | |
| } |