git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@11151 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			828 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			828 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
| 
 | |
|   These are the common Fault Tolerant Write (FTW) functions that are shared 
 | |
|   by DXE FTW driver and SMM FTW driver.
 | |
| 
 | |
| Copyright (c) 2006 - 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 "FaultTolerantWrite.h"
 | |
| 
 | |
| //
 | |
| // Fault Tolerant Write Protocol API
 | |
| //
 | |
| /**
 | |
|   Query the largest block that may be updated in a fault tolerant manner.
 | |
| 
 | |
| 
 | |
|   @param This            The pointer to this protocol instance. 
 | |
|   @param BlockSize       A pointer to a caller allocated UINTN that is updated to
 | |
|                          indicate the size of the largest block that can be updated.
 | |
| 
 | |
|   @return EFI_SUCCESS   The function completed successfully
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| FtwGetMaxBlockSize (
 | |
|   IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL    *This,
 | |
|   OUT UINTN                               *BlockSize
 | |
|   )
 | |
| {
 | |
|   EFI_FTW_DEVICE  *FtwDevice;
 | |
| 
 | |
|   if (!FeaturePcdGet(PcdFullFtwServiceEnable)) {
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   FtwDevice   = FTW_CONTEXT_FROM_THIS (This);
 | |
| 
 | |
|   *BlockSize  = FtwDevice->SpareAreaLength;
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Allocates space for the protocol to maintain information about writes.
 | |
|   Since writes must be completed in a fault tolerant manner and multiple
 | |
|   updates will require more resources to be successful, this function
 | |
|   enables the protocol to ensure that enough space exists to track
 | |
|   information about the upcoming writes.
 | |
| 
 | |
|   All writes must be completed or aborted before another fault tolerant write can occur.
 | |
| 
 | |
|   @param This            The pointer to this protocol instance. 
 | |
|   @param CallerId        The GUID identifying the write.
 | |
|   @param PrivateDataSize The size of the caller's private data
 | |
|                          that must be recorded for each write.
 | |
|   @param NumberOfWrites  The number of fault tolerant block writes
 | |
|                          that will need to occur.
 | |
| 
 | |
|   @return EFI_SUCCESS        The function completed successfully
 | |
|   @retval EFI_ABORTED        The function could not complete successfully.
 | |
|   @retval EFI_ACCESS_DENIED  All allocated writes have not been completed.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| FtwAllocate (
 | |
|   IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL    *This,
 | |
|   IN EFI_GUID                             *CallerId,
 | |
|   IN UINTN                                PrivateDataSize,
 | |
|   IN UINTN                                NumberOfWrites
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                      Status;
 | |
|   UINTN                           Length;
 | |
|   UINTN                           Offset;
 | |
|   EFI_FTW_DEVICE                  *FtwDevice;
 | |
|   EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader;
 | |
| 
 | |
|   FtwDevice = FTW_CONTEXT_FROM_THIS (This);
 | |
| 
 | |
|   Status    = WorkSpaceRefresh (FtwDevice);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
|   //
 | |
|   // Check if there is enough space for the coming allocation
 | |
|   //
 | |
|   if (WRITE_TOTAL_SIZE (NumberOfWrites, PrivateDataSize) > FtwDevice->FtwWorkSpaceHeader->WriteQueueSize) {
 | |
|     DEBUG ((EFI_D_ERROR, "Ftw: Allocate() request exceed Workspace, Caller: %g\n", CallerId));
 | |
|     return EFI_BUFFER_TOO_SMALL;
 | |
|   }
 | |
|   //
 | |
|   // Find the last write header and record.
 | |
|   // If the FtwHeader is complete, skip the completed last write header/records
 | |
|   //
 | |
|   FtwHeader = FtwDevice->FtwLastWriteHeader;
 | |
| 
 | |
|   //
 | |
|   // Previous write has not completed, access denied.
 | |
|   //
 | |
|   if ((FtwHeader->HeaderAllocated == FTW_VALID_STATE) || (FtwHeader->WritesAllocated == FTW_VALID_STATE)) {
 | |
|     return EFI_ACCESS_DENIED;
 | |
|   }
 | |
|   //
 | |
|   // If workspace is not enough, then reclaim workspace
 | |
|   //
 | |
|   Offset = (UINT8 *) FtwHeader - (UINT8 *) FtwDevice->FtwWorkSpace;
 | |
|   if (Offset + WRITE_TOTAL_SIZE (NumberOfWrites, PrivateDataSize) > FtwDevice->FtwWorkSpaceSize) {
 | |
|     Status = FtwReclaimWorkSpace (FtwDevice, TRUE);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return EFI_ABORTED;
 | |
|     }
 | |
| 
 | |
|     FtwHeader = FtwDevice->FtwLastWriteHeader;
 | |
|   }
 | |
|   //
 | |
|   // Prepare FTW write header,
 | |
|   // overwrite the buffer and write to workspace.
 | |
|   //
 | |
|   FtwHeader->WritesAllocated  = FTW_INVALID_STATE;
 | |
|   FtwHeader->Complete         = FTW_INVALID_STATE;
 | |
|   CopyMem (&FtwHeader->CallerId, CallerId, sizeof (EFI_GUID));
 | |
|   FtwHeader->NumberOfWrites   = NumberOfWrites;
 | |
|   FtwHeader->PrivateDataSize  = PrivateDataSize;
 | |
|   FtwHeader->HeaderAllocated  = FTW_VALID_STATE;
 | |
| 
 | |
|   Length                      = sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER);
 | |
|   Status = FtwDevice->FtwFvBlock->Write (
 | |
|                                     FtwDevice->FtwFvBlock,
 | |
|                                     FtwDevice->FtwWorkSpaceLba,
 | |
|                                     FtwDevice->FtwWorkSpaceBase + Offset,
 | |
|                                     &Length,
 | |
|                                     (UINT8 *) FtwHeader
 | |
|                                     );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
|   //
 | |
|   // Update Header->WriteAllocated as VALID
 | |
|   //
 | |
|   Status = FtwUpdateFvState (
 | |
|             FtwDevice->FtwFvBlock,
 | |
|             FtwDevice->FtwWorkSpaceLba,
 | |
|             FtwDevice->FtwWorkSpaceBase + Offset,
 | |
|             WRITES_ALLOCATED
 | |
|             );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
| 
 | |
|   DEBUG (
 | |
|     (EFI_D_ERROR,
 | |
|     "Ftw: Allocate() success, Caller:%g, # %d\n",
 | |
|     CallerId,
 | |
|     NumberOfWrites)
 | |
|     );
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Write a record with fault tolerant mannaer.
 | |
|   Since the content has already backuped in spare block, the write is
 | |
|   guaranteed to be completed with fault tolerant manner.
 | |
| 
 | |
|   @param This            The pointer to this protocol instance. 
 | |
|   @param Fvb             The FVB protocol that provides services for
 | |
|                          reading, writing, and erasing the target block.
 | |
| 
 | |
|   @retval  EFI_SUCCESS          The function completed successfully
 | |
|   @retval  EFI_ABORTED          The function could not complete successfully
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| FtwWriteRecord (
 | |
|   IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL     *This,
 | |
|   IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL    *Fvb
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                      Status;
 | |
|   EFI_FTW_DEVICE                  *FtwDevice;
 | |
|   EFI_FAULT_TOLERANT_WRITE_HEADER *Header;
 | |
|   EFI_FAULT_TOLERANT_WRITE_RECORD *Record;
 | |
|   UINTN                           Offset;
 | |
| 
 | |
|   FtwDevice = FTW_CONTEXT_FROM_THIS (This);
 | |
| 
 | |
|   //
 | |
|   // Spare Complete but Destination not complete,
 | |
|   // Recover the target block with the spare block.
 | |
|   //
 | |
|   Header  = FtwDevice->FtwLastWriteHeader;
 | |
|   Record  = FtwDevice->FtwLastWriteRecord;
 | |
| 
 | |
|   //
 | |
|   // IF target block is working block, THEN Flush Spare Block To Working Block;
 | |
|   // ELSE flush spare block to target block, which may be boot block after all.
 | |
|   //
 | |
|   if (IsWorkingBlock (FtwDevice, Fvb, Record->Lba)) {
 | |
|     //
 | |
|     // If target block is working block,
 | |
|     // it also need to set SPARE_COMPLETED to spare block.
 | |
|     //
 | |
|     Offset = (UINT8 *) Record - FtwDevice->FtwWorkSpace;
 | |
|     Status = FtwUpdateFvState (
 | |
|               FtwDevice->FtwBackupFvb,
 | |
|               FtwDevice->FtwWorkSpaceLba,
 | |
|               FtwDevice->FtwWorkSpaceBase + Offset,
 | |
|               SPARE_COMPLETED
 | |
|               );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return EFI_ABORTED;
 | |
|     }
 | |
| 
 | |
|     Status = FlushSpareBlockToWorkingBlock (FtwDevice);
 | |
|   } else if (IsBootBlock (FtwDevice, Fvb, Record->Lba)) {
 | |
|     //
 | |
|     // Update boot block
 | |
|     //
 | |
|     Status = FlushSpareBlockToBootBlock (FtwDevice);
 | |
|   } else {
 | |
|     //
 | |
|     // Update blocks other than working block or boot block
 | |
|     //
 | |
|     Status = FlushSpareBlockToTargetBlock (FtwDevice, Fvb, Record->Lba);
 | |
|   }
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
|   //
 | |
|   // Record the DestionationComplete in record
 | |
|   //
 | |
|   Offset = (UINT8 *) Record - FtwDevice->FtwWorkSpace;
 | |
|   Status = FtwUpdateFvState (
 | |
|             FtwDevice->FtwFvBlock,
 | |
|             FtwDevice->FtwWorkSpaceLba,
 | |
|             FtwDevice->FtwWorkSpaceBase + Offset,
 | |
|             DEST_COMPLETED
 | |
|             );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
| 
 | |
|   Record->DestinationComplete = FTW_VALID_STATE;
 | |
| 
 | |
|   //
 | |
|   // If this is the last Write in these write sequence,
 | |
|   // set the complete flag of write header.
 | |
|   //
 | |
|   if (IsLastRecordOfWrites (Header, Record)) {
 | |
|     Offset = (UINT8 *) Header - FtwDevice->FtwWorkSpace;
 | |
|     Status = FtwUpdateFvState (
 | |
|               FtwDevice->FtwFvBlock,
 | |
|               FtwDevice->FtwWorkSpaceLba,
 | |
|               FtwDevice->FtwWorkSpaceBase + Offset,
 | |
|               WRITES_COMPLETED
 | |
|               );
 | |
|     Header->Complete = FTW_VALID_STATE;
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return EFI_ABORTED;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Starts a target block update. This function will record data about write
 | |
|   in fault tolerant storage and will complete the write in a recoverable
 | |
|   manner, ensuring at all times that either the original contents or
 | |
|   the modified contents are available.
 | |
| 
 | |
|   @param This            The pointer to this protocol instance. 
 | |
|   @param Lba             The logical block address of the target block.
 | |
|   @param Offset          The offset within the target block to place the data.
 | |
|   @param Length          The number of bytes to write to the target block.
 | |
|   @param PrivateData     A pointer to private data that the caller requires to
 | |
|                          complete any pending writes in the event of a fault.
 | |
|   @param FvBlockHandle   The handle of FVB protocol that provides services for
 | |
|                          reading, writing, and erasing the target block.
 | |
|   @param Buffer          The data to write.
 | |
| 
 | |
|   @retval EFI_SUCCESS          The function completed successfully 
 | |
|   @retval EFI_ABORTED          The function could not complete successfully. 
 | |
|   @retval EFI_BAD_BUFFER_SIZE  The input data can't fit within the spare block. 
 | |
|                                Offset + *NumBytes > SpareAreaLength.
 | |
|   @retval EFI_ACCESS_DENIED    No writes have been allocated. 
 | |
|   @retval EFI_OUT_OF_RESOURCES Cannot allocate enough memory resource.
 | |
|   @retval EFI_NOT_FOUND        Cannot find FVB protocol by handle.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| FtwWrite (
 | |
|   IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL     *This,
 | |
|   IN EFI_LBA                               Lba,
 | |
|   IN UINTN                                 Offset,
 | |
|   IN UINTN                                 Length,
 | |
|   IN VOID                                  *PrivateData,
 | |
|   IN EFI_HANDLE                            FvBlockHandle,
 | |
|   IN VOID                                  *Buffer
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                          Status;
 | |
|   EFI_FTW_DEVICE                      *FtwDevice;
 | |
|   EFI_FAULT_TOLERANT_WRITE_HEADER     *Header;
 | |
|   EFI_FAULT_TOLERANT_WRITE_RECORD     *Record;
 | |
|   EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *Fvb;
 | |
|   UINTN                               MyLength;
 | |
|   UINTN                               MyOffset;
 | |
|   UINTN                               MyBufferSize;
 | |
|   UINT8                               *MyBuffer;
 | |
|   UINTN                               SpareBufferSize;
 | |
|   UINT8                               *SpareBuffer;
 | |
|   UINTN                               Index;
 | |
|   UINT8                               *Ptr;
 | |
|   EFI_PHYSICAL_ADDRESS                FvbPhysicalAddress;
 | |
| 
 | |
|   FtwDevice = FTW_CONTEXT_FROM_THIS (This);
 | |
| 
 | |
|   Status    = WorkSpaceRefresh (FtwDevice);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
| 
 | |
|   Header  = FtwDevice->FtwLastWriteHeader;
 | |
|   Record  = FtwDevice->FtwLastWriteRecord;
 | |
|   
 | |
|   if (IsErasedFlashBuffer ((UINT8 *) Header, sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER))) {
 | |
|     if (PrivateData == NULL) {
 | |
|       //
 | |
|       // Ftw Write Header is not allocated.
 | |
|       // No additional private data, the private data size is zero. Number of record can be set to 1.
 | |
|       //
 | |
|       Status = FtwAllocate (This, &gEfiCallerIdGuid, 0, 1);
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         return Status;
 | |
|       }
 | |
|     } else {
 | |
|       //
 | |
|       // Ftw Write Header is not allocated
 | |
|       // Additional private data is not NULL, the private data size can't be determined.
 | |
|       //
 | |
|       DEBUG ((EFI_D_ERROR, "Ftw: no allocates space for write record!\n"));
 | |
|       DEBUG ((EFI_D_ERROR, "Ftw: Allocate service should be called before Write service!\n"));
 | |
|       return EFI_NOT_READY;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // If Record is out of the range of Header, return access denied.
 | |
|   //
 | |
|   if (((UINTN)((UINT8 *) Record - (UINT8 *) Header)) > WRITE_TOTAL_SIZE (Header->NumberOfWrites - 1, Header->PrivateDataSize)) {
 | |
|     return EFI_ACCESS_DENIED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Check the COMPLETE flag of last write header
 | |
|   //
 | |
|   if (Header->Complete == FTW_VALID_STATE) {
 | |
|     return EFI_ACCESS_DENIED;
 | |
|   }
 | |
| 
 | |
|   if (Record->DestinationComplete == FTW_VALID_STATE) {
 | |
|     return EFI_ACCESS_DENIED;
 | |
|   }
 | |
| 
 | |
|   if ((Record->SpareComplete == FTW_VALID_STATE) && (Record->DestinationComplete != FTW_VALID_STATE)) {
 | |
|     return EFI_NOT_READY;
 | |
|   }
 | |
|   //
 | |
|   // Check if the input data can fit within the target block
 | |
|   //
 | |
|   if ((Offset + Length) > FtwDevice->SpareAreaLength) {
 | |
|     return EFI_BAD_BUFFER_SIZE;
 | |
|   }
 | |
|   //
 | |
|   // Get the FVB protocol by handle
 | |
|   //
 | |
|   Status = FtwGetFvbByHandle (FvBlockHandle, &Fvb);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_NOT_FOUND;
 | |
|   }
 | |
| 
 | |
|   Status = Fvb->GetPhysicalAddress (Fvb, &FvbPhysicalAddress);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((EFI_D_ERROR, "FtwLite: Get FVB physical address - %r\n", Status));
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Set BootBlockUpdate FLAG if it's updating boot block.
 | |
|   //
 | |
|   if (IsBootBlock (FtwDevice, Fvb, Lba)) {
 | |
|     Record->BootBlockUpdate = FTW_VALID_STATE;
 | |
|   }
 | |
|   //
 | |
|   // Write the record to the work space.
 | |
|   //
 | |
|   Record->Lba     = Lba;
 | |
|   Record->Offset  = Offset;
 | |
|   Record->Length  = Length;
 | |
|   Record->FvBaseAddress = FvbPhysicalAddress;
 | |
|   if (PrivateData != NULL) {
 | |
|     CopyMem ((Record + 1), PrivateData, Header->PrivateDataSize);
 | |
|   }
 | |
| 
 | |
|   MyOffset  = (UINT8 *) Record - FtwDevice->FtwWorkSpace;
 | |
|   MyLength  = RECORD_SIZE (Header->PrivateDataSize);
 | |
| 
 | |
|   Status = FtwDevice->FtwFvBlock->Write (
 | |
|                                     FtwDevice->FtwFvBlock,
 | |
|                                     FtwDevice->FtwWorkSpaceLba,
 | |
|                                     FtwDevice->FtwWorkSpaceBase + MyOffset,
 | |
|                                     &MyLength,
 | |
|                                     (UINT8 *) Record
 | |
|                                     );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
|   //
 | |
|   // Record has written to working block, then do the data.
 | |
|   //
 | |
|   //
 | |
|   // Allocate a memory buffer
 | |
|   //
 | |
|   MyBufferSize  = FtwDevice->SpareAreaLength;
 | |
|   MyBuffer      = AllocatePool (MyBufferSize);
 | |
|   if (MyBuffer == NULL) {
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
|   //
 | |
|   // Read all original data from target block to memory buffer
 | |
|   //
 | |
|   Ptr = MyBuffer;
 | |
|   for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
 | |
|     MyLength  = FtwDevice->BlockSize;
 | |
|     Status    = Fvb->Read (Fvb, Lba + Index, 0, &MyLength, Ptr);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       FreePool (MyBuffer);
 | |
|       return EFI_ABORTED;
 | |
|     }
 | |
| 
 | |
|     Ptr += MyLength;
 | |
|   }
 | |
|   //
 | |
|   // Overwrite the updating range data with
 | |
|   // the input buffer content
 | |
|   //
 | |
|   CopyMem (MyBuffer + Offset, Buffer, Length);
 | |
| 
 | |
|   //
 | |
|   // 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 (MyBuffer);
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
| 
 | |
|   Ptr = SpareBuffer;
 | |
|   for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
 | |
|     MyLength = FtwDevice->BlockSize;
 | |
|     Status = FtwDevice->FtwBackupFvb->Read (
 | |
|                                         FtwDevice->FtwBackupFvb,
 | |
|                                         FtwDevice->FtwSpareLba + Index,
 | |
|                                         0,
 | |
|                                         &MyLength,
 | |
|                                         Ptr
 | |
|                                         );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       FreePool (MyBuffer);
 | |
|       FreePool (SpareBuffer);
 | |
|       return EFI_ABORTED;
 | |
|     }
 | |
| 
 | |
|     Ptr += MyLength;
 | |
|   }
 | |
|   //
 | |
|   // Write the memory buffer to spare block
 | |
|   //
 | |
|   Status  = FtwEraseSpareBlock (FtwDevice);
 | |
|   Ptr     = MyBuffer;
 | |
|   for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
 | |
|     MyLength = FtwDevice->BlockSize;
 | |
|     Status = FtwDevice->FtwBackupFvb->Write (
 | |
|                                         FtwDevice->FtwBackupFvb,
 | |
|                                         FtwDevice->FtwSpareLba + Index,
 | |
|                                         0,
 | |
|                                         &MyLength,
 | |
|                                         Ptr
 | |
|                                         );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       FreePool (MyBuffer);
 | |
|       FreePool (SpareBuffer);
 | |
|       return EFI_ABORTED;
 | |
|     }
 | |
| 
 | |
|     Ptr += MyLength;
 | |
|   }
 | |
|   //
 | |
|   // Free MyBuffer
 | |
|   //
 | |
|   FreePool (MyBuffer);
 | |
| 
 | |
|   //
 | |
|   // Set the SpareComplete in the FTW record,
 | |
|   //
 | |
|   MyOffset = (UINT8 *) Record - FtwDevice->FtwWorkSpace;
 | |
|   Status = FtwUpdateFvState (
 | |
|             FtwDevice->FtwFvBlock,
 | |
|             FtwDevice->FtwWorkSpaceLba,
 | |
|             FtwDevice->FtwWorkSpaceBase + MyOffset,
 | |
|             SPARE_COMPLETED
 | |
|             );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     FreePool (SpareBuffer);
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
| 
 | |
|   Record->SpareComplete = FTW_VALID_STATE;
 | |
| 
 | |
|   //
 | |
|   //  Since the content has already backuped in spare block, the write is
 | |
|   //  guaranteed to be completed with fault tolerant manner.
 | |
|   //
 | |
|   Status = FtwWriteRecord (This, Fvb);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     FreePool (SpareBuffer);
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
|   //
 | |
|   // Restore spare backup buffer into spare block , if no failure happened during FtwWrite.
 | |
|   //
 | |
|   Status  = FtwEraseSpareBlock (FtwDevice);
 | |
|   Ptr     = SpareBuffer;
 | |
|   for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
 | |
|     MyLength = FtwDevice->BlockSize;
 | |
|     Status = FtwDevice->FtwBackupFvb->Write (
 | |
|                                         FtwDevice->FtwBackupFvb,
 | |
|                                         FtwDevice->FtwSpareLba + Index,
 | |
|                                         0,
 | |
|                                         &MyLength,
 | |
|                                         Ptr
 | |
|                                         );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       FreePool (SpareBuffer);
 | |
|       return EFI_ABORTED;
 | |
|     }
 | |
| 
 | |
|     Ptr += MyLength;
 | |
|   }
 | |
|   //
 | |
|   // All success.
 | |
|   //
 | |
|   FreePool (SpareBuffer);
 | |
| 
 | |
|   DEBUG (
 | |
|     (EFI_D_ERROR,
 | |
|     "Ftw: Write() success, (Lba:Offset)=(%lx:0x%x), Length: 0x%x\n",
 | |
|     Lba,
 | |
|     Offset,
 | |
|     Length)
 | |
|     );
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Restarts a previously interrupted write. The caller must provide the
 | |
|   block protocol needed to complete the interrupted write.
 | |
| 
 | |
|   @param This            The pointer to this protocol instance. 
 | |
|   @param FvBlockHandle   The handle of FVB protocol that provides services for
 | |
|                          reading, writing, and erasing the target block.
 | |
| 
 | |
|   @retval  EFI_SUCCESS          The function completed successfully
 | |
|   @retval  EFI_ACCESS_DENIED    No pending writes exist
 | |
|   @retval  EFI_NOT_FOUND        FVB protocol not found by the handle
 | |
|   @retval  EFI_ABORTED          The function could not complete successfully
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| FtwRestart (
 | |
|   IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL     *This,
 | |
|   IN EFI_HANDLE                            FvBlockHandle
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                          Status;
 | |
|   EFI_FTW_DEVICE                      *FtwDevice;
 | |
|   EFI_FAULT_TOLERANT_WRITE_HEADER     *Header;
 | |
|   EFI_FAULT_TOLERANT_WRITE_RECORD     *Record;
 | |
|   EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *Fvb;
 | |
| 
 | |
|   FtwDevice = FTW_CONTEXT_FROM_THIS (This);
 | |
| 
 | |
|   Status    = WorkSpaceRefresh (FtwDevice);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
| 
 | |
|   Header  = FtwDevice->FtwLastWriteHeader;
 | |
|   Record  = FtwDevice->FtwLastWriteRecord;
 | |
| 
 | |
|   //
 | |
|   // Spare Complete but Destination not complete,
 | |
|   // Recover the targt block with the spare block.
 | |
|   //
 | |
|   Status = FtwGetFvbByHandle (FvBlockHandle, &Fvb);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_NOT_FOUND;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Check the COMPLETE flag of last write header
 | |
|   //
 | |
|   if (Header->Complete == FTW_VALID_STATE) {
 | |
|     return EFI_ACCESS_DENIED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Check the flags of last write record
 | |
|   //
 | |
|   if (Record->DestinationComplete == FTW_VALID_STATE) {
 | |
|     return EFI_ACCESS_DENIED;
 | |
|   }
 | |
| 
 | |
|   if ((Record->SpareComplete != FTW_VALID_STATE)) {
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   //  Since the content has already backuped in spare block, the write is
 | |
|   //  guaranteed to be completed with fault tolerant manner.
 | |
|   //
 | |
|   Status = FtwWriteRecord (This, Fvb);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Erase Spare block
 | |
|   // This is restart, no need to keep spareblock content.
 | |
|   //
 | |
|   FtwEraseSpareBlock (FtwDevice);
 | |
| 
 | |
|   DEBUG ((EFI_D_ERROR, "Ftw: Restart() success \n"));
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Aborts all previous allocated writes.
 | |
| 
 | |
|   @param This                  The pointer to this protocol instance. 
 | |
| 
 | |
|   @retval EFI_SUCCESS          The function completed successfully
 | |
|   @retval EFI_ABORTED          The function could not complete successfully.
 | |
|   @retval EFI_NOT_FOUND        No allocated writes exist.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| FtwAbort (
 | |
|   IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL     *This
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS      Status;
 | |
|   UINTN           Offset;
 | |
|   EFI_FTW_DEVICE  *FtwDevice;
 | |
| 
 | |
|   FtwDevice = FTW_CONTEXT_FROM_THIS (This);
 | |
| 
 | |
|   Status    = WorkSpaceRefresh (FtwDevice);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
| 
 | |
|   if (FtwDevice->FtwLastWriteHeader->Complete == FTW_VALID_STATE) {
 | |
|     return EFI_NOT_FOUND;
 | |
|   }
 | |
|   //
 | |
|   // Update the complete state of the header as VALID and abort.
 | |
|   //
 | |
|   Offset = (UINT8 *) FtwDevice->FtwLastWriteHeader - FtwDevice->FtwWorkSpace;
 | |
|   Status = FtwUpdateFvState (
 | |
|             FtwDevice->FtwFvBlock,
 | |
|             FtwDevice->FtwWorkSpaceLba,
 | |
|             FtwDevice->FtwWorkSpaceBase + Offset,
 | |
|             WRITES_COMPLETED
 | |
|             );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
| 
 | |
|   FtwDevice->FtwLastWriteHeader->Complete = FTW_VALID_STATE;
 | |
| 
 | |
|   DEBUG ((EFI_D_ERROR, "Ftw: Abort() success \n"));
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Starts a target block update. This records information about the write
 | |
|   in fault tolerant storage and will complete the write in a recoverable
 | |
|   manner, ensuring at all times that either the original contents or
 | |
|   the modified contents are available.
 | |
| 
 | |
|   @param This            The pointer to this protocol instance. 
 | |
|   @param CallerId        The GUID identifying the last write.
 | |
|   @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 PrivateDataSize bytes from the private data
 | |
|                          stored for this write.
 | |
|   @param PrivateData     A pointer to a buffer. The function will copy
 | |
|   @param Complete        A Boolean value with TRUE indicating
 | |
|                          that the write was completed.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The function completed successfully
 | |
|   @retval EFI_ABORTED           The function could not complete successfully
 | |
|   @retval EFI_NOT_FOUND         No allocated writes exist
 | |
|   @retval EFI_BUFFER_TOO_SMALL  Input buffer is not larget enough
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| FtwGetLastWrite (
 | |
|   IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL     *This,
 | |
|   OUT EFI_GUID                             *CallerId,
 | |
|   OUT EFI_LBA                              *Lba,
 | |
|   OUT UINTN                                *Offset,
 | |
|   OUT UINTN                                *Length,
 | |
|   IN OUT UINTN                             *PrivateDataSize,
 | |
|   OUT VOID                                 *PrivateData,
 | |
|   OUT BOOLEAN                              *Complete
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                      Status;
 | |
|   EFI_FTW_DEVICE                  *FtwDevice;
 | |
|   EFI_FAULT_TOLERANT_WRITE_HEADER *Header;
 | |
|   EFI_FAULT_TOLERANT_WRITE_RECORD *Record;
 | |
| 
 | |
|   if (!FeaturePcdGet(PcdFullFtwServiceEnable)) {
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   FtwDevice = FTW_CONTEXT_FROM_THIS (This);
 | |
| 
 | |
|   Status    = WorkSpaceRefresh (FtwDevice);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
| 
 | |
|   Header  = FtwDevice->FtwLastWriteHeader;
 | |
|   Record  = FtwDevice->FtwLastWriteRecord;
 | |
| 
 | |
|   //
 | |
|   // If Header is incompleted and the last record has completed, then
 | |
|   // call Abort() to set the Header->Complete FLAG.
 | |
|   //
 | |
|   if ((Header->Complete != FTW_VALID_STATE) &&
 | |
|       (Record->DestinationComplete == FTW_VALID_STATE) &&
 | |
|       IsLastRecordOfWrites (Header, Record)
 | |
|         ) {
 | |
| 
 | |
|     Status    = FtwAbort (This);
 | |
|     *Complete = TRUE;
 | |
|     return EFI_NOT_FOUND;
 | |
|   }
 | |
|   //
 | |
|   // If there is no write header/record, return not found.
 | |
|   //
 | |
|   if (Header->HeaderAllocated != FTW_VALID_STATE) {
 | |
|     *Complete = TRUE;
 | |
|     return EFI_NOT_FOUND;
 | |
|   }
 | |
|   //
 | |
|   // If this record SpareComplete has not set, then it can not restart.
 | |
|   //
 | |
|   if (Record->SpareComplete != FTW_VALID_STATE) {
 | |
|     Status = GetPreviousRecordOfWrites (Header, &Record);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       FtwAbort (This);
 | |
|       *Complete = TRUE;
 | |
|       return EFI_NOT_FOUND;
 | |
|     }
 | |
|     ASSERT (Record != NULL);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Fill all the requested values
 | |
|   //
 | |
|   CopyMem (CallerId, &Header->CallerId, sizeof (EFI_GUID));
 | |
|   *Lba      = Record->Lba;
 | |
|   *Offset   = Record->Offset;
 | |
|   *Length   = Record->Length;
 | |
|   *Complete = (BOOLEAN) (Record->DestinationComplete == FTW_VALID_STATE);
 | |
| 
 | |
|   if (*PrivateDataSize < Header->PrivateDataSize) {
 | |
|     *PrivateDataSize  = Header->PrivateDataSize;
 | |
|     PrivateData       = NULL;
 | |
|     Status            = EFI_BUFFER_TOO_SMALL;
 | |
|   } else {
 | |
|     *PrivateDataSize = Header->PrivateDataSize;
 | |
|     CopyMem (PrivateData, Record + 1, *PrivateDataSize);
 | |
|     Status = EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   DEBUG ((EFI_D_ERROR, "Ftw: GetLasetWrite() success\n"));
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 |