Signed-off-by: jljusten Reviewed-by: rsun3 Reviewed-by: lgao4 git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@12257 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			1219 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1219 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @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;
 | 
						|
}
 |