mirror of
				https://github.com/acidanthera/audk.git
				synced 2025-10-31 11:13:53 +01:00 
			
		
		
		
	The IsValidWorkSpace() function checks if the working block header of the
workspace is valid. A mismatch detected by this function is not
necessarily an error; it can happen with an as-yet unwritten flash chip,
which is e.g. common and normal when a new ArmVirtQemu virtual machine is
booted. Therefore downgrade the message emitted by IsValidWorkSpace() from
EFI_D_ERROR to EFI_D_INFO, and change the wording from "error" to
"mismatch".
The only caller of IsValidWorkSpace(), InitFtwProtocol(), handles all of
the following cases:
(1) IsValidWorkSpace() succeeds for the working block -- this is normal
    operation,
(2) IsValidWorkSpace() fails for the working block, but succeeds for the
    spare block -- InitFtwProtocol() then restores the working block from
    the spare block,
(3) IsValidWorkSpace() fails for both the working and spare blocks --
    InitFtwProtocol() reinitializes the full workspace.
In cases (2) and (3), InitFtwProtocol() logs additional messages about the
branch taken. Their current level is EFI_D_ERROR, but the messages are
arguably informative, not necessarily error reports.
Downgrade these messages from EFI_D_ERROR to EFI_D_INFO, so that they
don't clutter the debug output when the PcdDebugPrintErrorLevel mask only
enables EFI_D_ERROR (i.e., in a "silent" build).
These messages have annoyed / confused users; see for example:
- https://bugzilla.redhat.com/show_bug.cgi?id=1270279
Cc: Star Zeng <star.zeng@intel.com>
Cc: Liming Gao <liming.gao@intel.com>
Cc: Drew Jones <drjones@redhat.com>
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Star Zeng <star.zeng@intel.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@18619 6f19259b-4bc3-4df7-8a09-765794883524
		
	
			
		
			
				
	
	
		
			620 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			620 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
| 
 | |
|    Internal functions to operate Working Block Space.
 | |
| 
 | |
| Copyright (c) 2006 - 2015, 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 "FaultTolerantWrite.h"
 | |
| 
 | |
| EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER mWorkingBlockHeader = {ZERO_GUID, 0, 0, 0, 0, {0, 0, 0}, 0};
 | |
| 
 | |
| /**
 | |
|   Initialize a local work space header.
 | |
| 
 | |
|   Since Signature and WriteQueueSize have been known, Crc can be calculated out,
 | |
|   then the work space header will be fixed.
 | |
| **/
 | |
| VOID
 | |
| InitializeLocalWorkSpaceHeader (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                              Status;
 | |
| 
 | |
|   //
 | |
|   // Check signature with gEdkiiWorkingBlockSignatureGuid.
 | |
|   //
 | |
|   if (CompareGuid (&gEdkiiWorkingBlockSignatureGuid, &mWorkingBlockHeader.Signature)) {
 | |
|     //
 | |
|     // The local work space header has been initialized.
 | |
|     //
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   SetMem (
 | |
|     &mWorkingBlockHeader,
 | |
|     sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
 | |
|     FTW_ERASED_BYTE
 | |
|     );
 | |
| 
 | |
|   //
 | |
|   // Here using gEdkiiWorkingBlockSignatureGuid as the signature.
 | |
|   //
 | |
|   CopyMem (
 | |
|     &mWorkingBlockHeader.Signature,
 | |
|     &gEdkiiWorkingBlockSignatureGuid,
 | |
|     sizeof (EFI_GUID)
 | |
|     );
 | |
|   mWorkingBlockHeader.WriteQueueSize = (UINT64) (PcdGet32 (PcdFlashNvStorageFtwWorkingSize) - sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER));
 | |
| 
 | |
|   //
 | |
|   // Crc is calculated with all the fields except Crc and STATE, so leave them as FTW_ERASED_BYTE.
 | |
|   //
 | |
| 
 | |
|   //
 | |
|   // Calculate the Crc of woking block header
 | |
|   //
 | |
|   Status = gBS->CalculateCrc32 (
 | |
|                   &mWorkingBlockHeader,
 | |
|                   sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
 | |
|                   &mWorkingBlockHeader.Crc
 | |
|                   );
 | |
|   ASSERT_EFI_ERROR (Status);
 | |
| 
 | |
|   mWorkingBlockHeader.WorkingBlockValid    = FTW_VALID_STATE;
 | |
|   mWorkingBlockHeader.WorkingBlockInvalid  = FTW_INVALID_STATE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Check to see if it is a valid work space.
 | |
| 
 | |
| 
 | |
|   @param WorkingHeader   Pointer of working block header
 | |
| 
 | |
|   @retval TRUE          The work space is valid.
 | |
|   @retval FALSE         The work space is invalid.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| IsValidWorkSpace (
 | |
|   IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader
 | |
|   )
 | |
| {
 | |
|   if (WorkingHeader == NULL) {
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   if (CompareMem (WorkingHeader, &mWorkingBlockHeader, sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)) == 0) {
 | |
|     return TRUE;
 | |
|   }
 | |
| 
 | |
|   DEBUG ((EFI_D_INFO, "Ftw: Work block header check mismatch\n"));
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Initialize a work space when there is no work space.
 | |
| 
 | |
|   @param WorkingHeader   Pointer of working block header
 | |
| 
 | |
|   @retval  EFI_SUCCESS    The function completed successfully
 | |
|   @retval  EFI_ABORTED    The function could not complete successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| InitWorkSpaceHeader (
 | |
|   IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader
 | |
|   )
 | |
| {
 | |
|   if (WorkingHeader == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   CopyMem (WorkingHeader, &mWorkingBlockHeader, sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER));
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Read work space data from work block or spare block.
 | |
| 
 | |
|   @param FvBlock        FVB Protocol interface to access the block.
 | |
|   @param BlockSize      The size of the block.
 | |
|   @param Lba            Lba of the block.
 | |
|   @param Offset         The offset within the block.
 | |
|   @param Length         The number of bytes to read from the block.
 | |
|   @param Buffer         The data is read.
 | |
| 
 | |
|   @retval EFI_SUCCESS   The function completed successfully.
 | |
|   @retval EFI_ABORTED   The function could not complete successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| ReadWorkSpaceData (
 | |
|   IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
 | |
|   IN UINTN                              BlockSize,
 | |
|   IN EFI_LBA                            Lba,
 | |
|   IN UINTN                              Offset,
 | |
|   IN UINTN                              Length,
 | |
|   OUT UINT8                             *Buffer
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS            Status;
 | |
|   UINT8                 *Ptr;
 | |
|   UINTN                 MyLength;
 | |
| 
 | |
|   //
 | |
|   // Calculate the real Offset and Lba to write.
 | |
|   //
 | |
|   while (Offset >= BlockSize) {
 | |
|     Offset -= BlockSize;
 | |
|     Lba++;
 | |
|   }
 | |
| 
 | |
|   Ptr = Buffer;
 | |
|   while (Length > 0) {
 | |
|     if ((Offset + Length) > BlockSize) {
 | |
|       MyLength = BlockSize - Offset;
 | |
|     } else {
 | |
|       MyLength = Length;
 | |
|     }
 | |
| 
 | |
|     Status = FvBlock->Read (
 | |
|                         FvBlock,
 | |
|                         Lba,
 | |
|                         Offset,
 | |
|                         &MyLength,
 | |
|                         Ptr
 | |
|                         );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return EFI_ABORTED;
 | |
|     }
 | |
|     Offset = 0;
 | |
|     Length -= MyLength;
 | |
|     Ptr += MyLength;
 | |
|     Lba++;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Write work space data to work block.
 | |
| 
 | |
|   @param FvBlock        FVB Protocol interface to access the block.
 | |
|   @param BlockSize      The size of the block.
 | |
|   @param Lba            Lba of the block.
 | |
|   @param Offset         The offset within the block to place the data.
 | |
|   @param Length         The number of bytes to write to the block.
 | |
|   @param Buffer         The data to write.
 | |
| 
 | |
|   @retval EFI_SUCCESS   The function completed successfully.
 | |
|   @retval EFI_ABORTED   The function could not complete successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| WriteWorkSpaceData (
 | |
|   IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
 | |
|   IN UINTN                              BlockSize,
 | |
|   IN EFI_LBA                            Lba,
 | |
|   IN UINTN                              Offset,
 | |
|   IN UINTN                              Length,
 | |
|   IN UINT8                              *Buffer
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS            Status;
 | |
|   UINT8                 *Ptr;
 | |
|   UINTN                 MyLength;
 | |
| 
 | |
|   //
 | |
|   // Calculate the real Offset and Lba to write.
 | |
|   //
 | |
|   while (Offset >= BlockSize) {
 | |
|     Offset -= BlockSize;
 | |
|     Lba++;
 | |
|   }
 | |
| 
 | |
|   Ptr = Buffer;
 | |
|   while (Length > 0) {
 | |
|     if ((Offset + Length) > BlockSize) {
 | |
|       MyLength = BlockSize - Offset;
 | |
|     } else {
 | |
|       MyLength = Length;
 | |
|     }
 | |
| 
 | |
|     Status = FvBlock->Write (
 | |
|                         FvBlock,
 | |
|                         Lba,
 | |
|                         Offset,
 | |
|                         &MyLength,
 | |
|                         Ptr
 | |
|                         );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return EFI_ABORTED;
 | |
|     }
 | |
|     Offset = 0;
 | |
|     Length -= MyLength;
 | |
|     Ptr += MyLength;
 | |
|     Lba++;
 | |
|   }
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Read from working block to refresh the work space in memory.
 | |
| 
 | |
|   @param FtwDevice   Point to private data of FTW driver
 | |
| 
 | |
|   @retval  EFI_SUCCESS    The function completed successfully
 | |
|   @retval  EFI_ABORTED    The function could not complete successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| WorkSpaceRefresh (
 | |
|   IN EFI_FTW_DEVICE  *FtwDevice
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                      Status;
 | |
|   UINTN                           RemainingSpaceSize;
 | |
| 
 | |
|   //
 | |
|   // Initialize WorkSpace as FTW_ERASED_BYTE
 | |
|   //
 | |
|   SetMem (
 | |
|     FtwDevice->FtwWorkSpace,
 | |
|     FtwDevice->FtwWorkSpaceSize,
 | |
|     FTW_ERASED_BYTE
 | |
|     );
 | |
| 
 | |
|   //
 | |
|   // Read from working block
 | |
|   //
 | |
|   Status = ReadWorkSpaceData (
 | |
|              FtwDevice->FtwFvBlock,
 | |
|              FtwDevice->WorkBlockSize,
 | |
|              FtwDevice->FtwWorkSpaceLba,
 | |
|              FtwDevice->FtwWorkSpaceBase,
 | |
|              FtwDevice->FtwWorkSpaceSize,
 | |
|              FtwDevice->FtwWorkSpace
 | |
|              );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
|   //
 | |
|   // Refresh the FtwLastWriteHeader
 | |
|   //
 | |
|   Status = FtwGetLastWriteHeader (
 | |
|             FtwDevice->FtwWorkSpaceHeader,
 | |
|             FtwDevice->FtwWorkSpaceSize,
 | |
|             &FtwDevice->FtwLastWriteHeader
 | |
|             );
 | |
|   RemainingSpaceSize = FtwDevice->FtwWorkSpaceSize - ((UINTN) FtwDevice->FtwLastWriteHeader - (UINTN) FtwDevice->FtwWorkSpace);
 | |
|   DEBUG ((EFI_D_INFO, "Ftw: Remaining work space size - %x\n", RemainingSpaceSize));
 | |
|   //
 | |
|   // If FtwGetLastWriteHeader() returns error, or the remaining space size is even not enough to contain
 | |
|   // one EFI_FAULT_TOLERANT_WRITE_HEADER + one EFI_FAULT_TOLERANT_WRITE_RECORD(It will cause that the header
 | |
|   // pointed by FtwDevice->FtwLastWriteHeader or record pointed by FtwDevice->FtwLastWriteRecord may contain invalid data),
 | |
|   // it needs to reclaim work space.
 | |
|   //
 | |
|   if (EFI_ERROR (Status) || RemainingSpaceSize < sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER) + sizeof (EFI_FAULT_TOLERANT_WRITE_RECORD)) {
 | |
|     //
 | |
|     // reclaim work space in working block.
 | |
|     //
 | |
|     Status = FtwReclaimWorkSpace (FtwDevice, TRUE);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       DEBUG ((EFI_D_ERROR, "Ftw: Reclaim workspace - %r\n", Status));
 | |
|       return EFI_ABORTED;
 | |
|     }
 | |
|     //
 | |
|     // Read from working block again
 | |
|     //
 | |
|     Status = ReadWorkSpaceData (
 | |
|                FtwDevice->FtwFvBlock,
 | |
|                FtwDevice->WorkBlockSize,
 | |
|                FtwDevice->FtwWorkSpaceLba,
 | |
|                FtwDevice->FtwWorkSpaceBase,
 | |
|                FtwDevice->FtwWorkSpaceSize,
 | |
|                FtwDevice->FtwWorkSpace
 | |
|                );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return EFI_ABORTED;
 | |
|     }
 | |
| 
 | |
|     Status = FtwGetLastWriteHeader (
 | |
|               FtwDevice->FtwWorkSpaceHeader,
 | |
|               FtwDevice->FtwWorkSpaceSize,
 | |
|               &FtwDevice->FtwLastWriteHeader
 | |
|               );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return EFI_ABORTED;
 | |
|     }
 | |
|   }
 | |
|   //
 | |
|   // Refresh the FtwLastWriteRecord
 | |
|   //
 | |
|   Status = FtwGetLastWriteRecord (
 | |
|             FtwDevice->FtwLastWriteHeader,
 | |
|             &FtwDevice->FtwLastWriteRecord
 | |
|             );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Reclaim the work space on the working block.
 | |
| 
 | |
|   @param FtwDevice       Point to private data of FTW driver
 | |
|   @param PreserveRecord  Whether to preserve the working record is needed
 | |
| 
 | |
|   @retval EFI_SUCCESS            The function completed successfully
 | |
|   @retval EFI_OUT_OF_RESOURCES   Allocate memory error
 | |
|   @retval EFI_ABORTED            The function could not complete successfully
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| FtwReclaimWorkSpace (
 | |
|   IN EFI_FTW_DEVICE  *FtwDevice,
 | |
|   IN BOOLEAN         PreserveRecord
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                              Status;
 | |
|   UINTN                                   Length;
 | |
|   EFI_FAULT_TOLERANT_WRITE_HEADER         *Header;
 | |
|   UINT8                                   *TempBuffer;
 | |
|   UINTN                                   TempBufferSize;
 | |
|   UINTN                                   SpareBufferSize;
 | |
|   UINT8                                   *SpareBuffer;
 | |
|   EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader;
 | |
|   UINTN                                   Index;
 | |
|   UINT8                                   *Ptr;
 | |
|   EFI_LBA                                 WorkSpaceLbaOffset;
 | |
| 
 | |
|   DEBUG ((EFI_D_INFO, "Ftw: start to reclaim work space\n"));
 | |
| 
 | |
|   WorkSpaceLbaOffset = FtwDevice->FtwWorkSpaceLba - FtwDevice->FtwWorkBlockLba;
 | |
| 
 | |
|   //
 | |
|   // Read all original data from working block to a memory buffer
 | |
|   //
 | |
|   TempBufferSize = FtwDevice->NumberOfWorkBlock * FtwDevice->WorkBlockSize;
 | |
|   TempBuffer     = AllocateZeroPool (TempBufferSize);
 | |
|   if (TempBuffer == NULL) {
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
| 
 | |
|   Ptr = TempBuffer;
 | |
|   for (Index = 0; Index < FtwDevice->NumberOfWorkBlock; Index += 1) {
 | |
|     Length = FtwDevice->WorkBlockSize;
 | |
|     Status = FtwDevice->FtwFvBlock->Read (
 | |
|                                           FtwDevice->FtwFvBlock,
 | |
|                                           FtwDevice->FtwWorkBlockLba + Index,
 | |
|                                           0,
 | |
|                                           &Length,
 | |
|                                           Ptr
 | |
|                                           );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       FreePool (TempBuffer);
 | |
|       return EFI_ABORTED;
 | |
|     }
 | |
| 
 | |
|     Ptr += Length;
 | |
|   }
 | |
|   //
 | |
|   // Clean up the workspace, remove all the completed records.
 | |
|   //
 | |
|   Ptr = TempBuffer +
 | |
|         (UINTN) WorkSpaceLbaOffset * FtwDevice->WorkBlockSize +
 | |
|         FtwDevice->FtwWorkSpaceBase;
 | |
| 
 | |
|   //
 | |
|   // Clear the content of buffer that will save the new work space data
 | |
|   //
 | |
|   SetMem (Ptr, FtwDevice->FtwWorkSpaceSize, FTW_ERASED_BYTE);
 | |
| 
 | |
|   //
 | |
|   // Copy EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER to buffer
 | |
|   //
 | |
|   CopyMem (
 | |
|     Ptr,
 | |
|     FtwDevice->FtwWorkSpaceHeader,
 | |
|     sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)
 | |
|     );
 | |
|   if (PreserveRecord) {
 | |
|     //
 | |
|     // Get the last record following the header,
 | |
|     //
 | |
|     Status = FtwGetLastWriteHeader (
 | |
|                FtwDevice->FtwWorkSpaceHeader,
 | |
|                FtwDevice->FtwWorkSpaceSize,
 | |
|                &FtwDevice->FtwLastWriteHeader
 | |
|                );
 | |
|     Header = FtwDevice->FtwLastWriteHeader;
 | |
|     if (!EFI_ERROR (Status) && (Header != NULL) && (Header->Complete != FTW_VALID_STATE) && (Header->HeaderAllocated == FTW_VALID_STATE)) {
 | |
|       CopyMem (
 | |
|         Ptr + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
 | |
|         FtwDevice->FtwLastWriteHeader,
 | |
|         FTW_WRITE_TOTAL_SIZE (Header->NumberOfWrites, Header->PrivateDataSize)
 | |
|         );
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   CopyMem (
 | |
|     FtwDevice->FtwWorkSpace,
 | |
|     Ptr,
 | |
|     FtwDevice->FtwWorkSpaceSize
 | |
|     );
 | |
| 
 | |
|   FtwGetLastWriteHeader (
 | |
|     FtwDevice->FtwWorkSpaceHeader,
 | |
|     FtwDevice->FtwWorkSpaceSize,
 | |
|     &FtwDevice->FtwLastWriteHeader
 | |
|     );
 | |
| 
 | |
|   FtwGetLastWriteRecord (
 | |
|     FtwDevice->FtwLastWriteHeader,
 | |
|     &FtwDevice->FtwLastWriteRecord
 | |
|     );
 | |
| 
 | |
|   //
 | |
|   // Set the WorkingBlockValid and WorkingBlockInvalid as INVALID
 | |
|   //
 | |
|   WorkingBlockHeader                      = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (TempBuffer +
 | |
|                                             (UINTN) WorkSpaceLbaOffset * FtwDevice->WorkBlockSize +
 | |
|                                             FtwDevice->FtwWorkSpaceBase);
 | |
|   WorkingBlockHeader->WorkingBlockValid   = FTW_INVALID_STATE;
 | |
|   WorkingBlockHeader->WorkingBlockInvalid = FTW_INVALID_STATE;
 | |
| 
 | |
|   //
 | |
|   // Try to keep the content of spare block
 | |
|   // Save spare block into a spare backup memory buffer (Sparebuffer)
 | |
|   //
 | |
|   SpareBufferSize = FtwDevice->SpareAreaLength;
 | |
|   SpareBuffer     = AllocatePool (SpareBufferSize);
 | |
|   if (SpareBuffer == NULL) {
 | |
|     FreePool (TempBuffer);
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
| 
 | |
|   Ptr = SpareBuffer;
 | |
|   for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
 | |
|     Length = FtwDevice->SpareBlockSize;
 | |
|     Status = FtwDevice->FtwBackupFvb->Read (
 | |
|                                         FtwDevice->FtwBackupFvb,
 | |
|                                         FtwDevice->FtwSpareLba + Index,
 | |
|                                         0,
 | |
|                                         &Length,
 | |
|                                         Ptr
 | |
|                                         );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       FreePool (TempBuffer);
 | |
|       FreePool (SpareBuffer);
 | |
|       return EFI_ABORTED;
 | |
|     }
 | |
| 
 | |
|     Ptr += Length;
 | |
|   }
 | |
|   //
 | |
|   // Write the memory buffer to spare block
 | |
|   //
 | |
|   Status  = FtwEraseSpareBlock (FtwDevice);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     FreePool (TempBuffer);
 | |
|     FreePool (SpareBuffer);
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
|   Ptr     = TempBuffer;
 | |
|   for (Index = 0; TempBufferSize > 0; Index += 1) {
 | |
|     if (TempBufferSize > FtwDevice->SpareBlockSize) {
 | |
|       Length = FtwDevice->SpareBlockSize;
 | |
|     } else {
 | |
|       Length = TempBufferSize;
 | |
|     }
 | |
|     Status = FtwDevice->FtwBackupFvb->Write (
 | |
|                                             FtwDevice->FtwBackupFvb,
 | |
|                                             FtwDevice->FtwSpareLba + Index,
 | |
|                                             0,
 | |
|                                             &Length,
 | |
|                                             Ptr
 | |
|                                             );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       FreePool (TempBuffer);
 | |
|       FreePool (SpareBuffer);
 | |
|       return EFI_ABORTED;
 | |
|     }
 | |
| 
 | |
|     Ptr += Length;
 | |
|     TempBufferSize -= Length;
 | |
|   }
 | |
|   //
 | |
|   // Free TempBuffer
 | |
|   //
 | |
|   FreePool (TempBuffer);
 | |
| 
 | |
|   //
 | |
|   // Set the WorkingBlockValid in spare block
 | |
|   //
 | |
|   Status = FtwUpdateFvState (
 | |
|             FtwDevice->FtwBackupFvb,
 | |
|             FtwDevice->SpareBlockSize,
 | |
|             FtwDevice->FtwSpareLba + FtwDevice->FtwWorkSpaceLbaInSpare,
 | |
|             FtwDevice->FtwWorkSpaceBaseInSpare + sizeof (EFI_GUID) + sizeof (UINT32),
 | |
|             WORKING_BLOCK_VALID
 | |
|             );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     FreePool (SpareBuffer);
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
|   //
 | |
|   // Before erase the working block, set WorkingBlockInvalid in working block.
 | |
|   //
 | |
|   // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
 | |
|   //                          WorkingBlockInvalid);
 | |
|   //
 | |
|   Status = FtwUpdateFvState (
 | |
|             FtwDevice->FtwFvBlock,
 | |
|             FtwDevice->WorkBlockSize,
 | |
|             FtwDevice->FtwWorkSpaceLba,
 | |
|             FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),
 | |
|             WORKING_BLOCK_INVALID
 | |
|             );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     FreePool (SpareBuffer);
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
| 
 | |
|   FtwDevice->FtwWorkSpaceHeader->WorkingBlockInvalid = FTW_VALID_STATE;
 | |
| 
 | |
|   //
 | |
|   // Write the spare block to working block
 | |
|   //
 | |
|   Status = FlushSpareBlockToWorkingBlock (FtwDevice);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     FreePool (SpareBuffer);
 | |
|     return Status;
 | |
|   }
 | |
|   //
 | |
|   // Restore spare backup buffer into spare block , if no failure happened during FtwWrite.
 | |
|   //
 | |
|   Status  = FtwEraseSpareBlock (FtwDevice);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     FreePool (SpareBuffer);
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
|   Ptr     = SpareBuffer;
 | |
|   for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
 | |
|     Length = FtwDevice->SpareBlockSize;
 | |
|     Status = FtwDevice->FtwBackupFvb->Write (
 | |
|                                         FtwDevice->FtwBackupFvb,
 | |
|                                         FtwDevice->FtwSpareLba + Index,
 | |
|                                         0,
 | |
|                                         &Length,
 | |
|                                         Ptr
 | |
|                                         );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       FreePool (SpareBuffer);
 | |
|       return EFI_ABORTED;
 | |
|     }
 | |
| 
 | |
|     Ptr += Length;
 | |
|   }
 | |
| 
 | |
|   FreePool (SpareBuffer);
 | |
| 
 | |
|   DEBUG ((EFI_D_INFO, "Ftw: reclaim work space successfully\n"));
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 |