Esempio n. 1
0
/**
  Initialization for Fault Tolerant Write protocol.

  @param[in, out] FtwDevice     Pointer to the FTW device structure

  @retval EFI_SUCCESS           Initialize the FTW protocol successfully.
  @retval EFI_NOT_FOUND         No proper FVB protocol was found.
  
**/
EFI_STATUS
InitFtwProtocol (
  IN OUT EFI_FTW_DEVICE               *FtwDevice
  )
{
  EFI_STATUS                          Status;
  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *Fvb;
  UINTN                               Length;
  EFI_FAULT_TOLERANT_WRITE_HEADER     *FtwHeader;
  UINTN                               Offset;
  EFI_HANDLE                          FvbHandle;

  //
  // Find the right SMM Fvb protocol instance for FTW.
  //
  Status = FindFvbForFtw (FtwDevice);
  if (EFI_ERROR (Status)) {
    return EFI_NOT_FOUND;
  }  
  
  //
  // Calculate the start LBA of working block. Working block is an area which
  // contains working space in its last block and has the same size as spare
  // block, unless there are not enough blocks before the block that contains
  // working space.
  //
  FtwDevice->FtwWorkBlockLba = FtwDevice->FtwWorkSpaceLba - FtwDevice->NumberOfSpareBlock + 1;
  ASSERT ((INT64) (FtwDevice->FtwWorkBlockLba) >= 0); 

  //
  // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.
  //
  FtwDevice->FtwWorkSpace = (UINT8 *) (FtwDevice + 1);
  FtwDevice->FtwWorkSpaceHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) FtwDevice->FtwWorkSpace;

  FtwDevice->FtwLastWriteHeader = NULL;
  FtwDevice->FtwLastWriteRecord = NULL;

  //
  // Refresh the working space data from working block
  //
  Status = WorkSpaceRefresh (FtwDevice);
  ASSERT_EFI_ERROR (Status);
  //
  // If the working block workspace is not valid, try the spare block
  //
  if (!IsValidWorkSpace (FtwDevice->FtwWorkSpaceHeader)) {
    //
    // Read from spare block
    //
    Length = FtwDevice->FtwWorkSpaceSize;
    Status = FtwDevice->FtwBackupFvb->Read (
                    FtwDevice->FtwBackupFvb,
                    FtwDevice->FtwSpareLba,
                    FtwDevice->FtwWorkSpaceBase,
                    &Length,
                    FtwDevice->FtwWorkSpace
                    );
    ASSERT_EFI_ERROR (Status);

    //
    // If spare block is valid, then replace working block content.
    //
    if (IsValidWorkSpace (FtwDevice->FtwWorkSpaceHeader)) {
      Status = FlushSpareBlockToWorkingBlock (FtwDevice);
      DEBUG ((EFI_D_ERROR, "Ftw: Restart working block update in InitFtwProtocol() - %r\n", Status));
      FtwAbort (&FtwDevice->FtwInstance);
      //
      // Refresh work space.
      //
      Status = WorkSpaceRefresh (FtwDevice);
      ASSERT_EFI_ERROR (Status);
    } else {
      DEBUG ((EFI_D_ERROR, "Ftw: Both are invalid, init workspace\n"));
      //
      // If both are invalid, then initialize work space.
      //
      SetMem (
        FtwDevice->FtwWorkSpace,
        FtwDevice->FtwWorkSpaceSize,
        FTW_ERASED_BYTE
        );
      InitWorkSpaceHeader (FtwDevice->FtwWorkSpaceHeader);
      //
      // Initialize the work space
      //
      Status = FtwReclaimWorkSpace (FtwDevice, FALSE);
      ASSERT_EFI_ERROR (Status);
    }
  }
  //
  // If the FtwDevice->FtwLastWriteRecord is 1st record of write header &&
  // (! SpareComplete) THEN call Abort().
  //
  if ((FtwDevice->FtwLastWriteHeader->HeaderAllocated == FTW_VALID_STATE) &&
    (FtwDevice->FtwLastWriteRecord->SpareComplete != FTW_VALID_STATE) &&
    IsFirstRecordOfWrites (FtwDevice->FtwLastWriteHeader, FtwDevice->FtwLastWriteRecord)
    ) {
    DEBUG ((EFI_D_ERROR, "Ftw: Init.. find first record not SpareCompleted, abort()\n"));
    FtwAbort (&FtwDevice->FtwInstance);
  }
  //
  // If Header is incompleted and the last record has completed, then
  // call Abort() to set the Header->Complete FLAG.
  //
  if ((FtwDevice->FtwLastWriteHeader->Complete != FTW_VALID_STATE) &&
    (FtwDevice->FtwLastWriteRecord->DestinationComplete == FTW_VALID_STATE) &&
    IsLastRecordOfWrites (FtwDevice->FtwLastWriteHeader, FtwDevice->FtwLastWriteRecord)
    ) {
    DEBUG ((EFI_D_ERROR, "Ftw: Init.. find last record completed but header not, abort()\n"));
    FtwAbort (&FtwDevice->FtwInstance);
  }
  //
  // To check the workspace buffer following last Write header/records is EMPTY or not.
  // If it's not EMPTY, FTW also need to call reclaim().
  //
  FtwHeader = FtwDevice->FtwLastWriteHeader;
  Offset    = (UINT8 *) FtwHeader - FtwDevice->FtwWorkSpace;
  if (FtwDevice->FtwWorkSpace[Offset] != FTW_ERASED_BYTE) {
    Offset += WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites, FtwHeader->PrivateDataSize);
  }
  
  if (!IsErasedFlashBuffer (FtwDevice->FtwWorkSpace + Offset, FtwDevice->FtwWorkSpaceSize - Offset)) {
    Status = FtwReclaimWorkSpace (FtwDevice, TRUE);
    ASSERT_EFI_ERROR (Status);
  }

  //
  // Restart if it's boot block
  //
  if ((FtwDevice->FtwLastWriteHeader->Complete != FTW_VALID_STATE) &&
    (FtwDevice->FtwLastWriteRecord->SpareComplete == FTW_VALID_STATE)
    ) {
    if (FtwDevice->FtwLastWriteRecord->BootBlockUpdate == FTW_VALID_STATE) {
      Status = FlushSpareBlockToBootBlock (FtwDevice);
      DEBUG ((EFI_D_ERROR, "Ftw: Restart boot block update - %r\n", Status));
      ASSERT_EFI_ERROR (Status);
      FtwAbort (&FtwDevice->FtwInstance);
    } else {
      //
      // if (SpareCompleted) THEN  Restart to fault tolerant write.
      //
      FvbHandle = NULL;
      FvbHandle = GetFvbByAddress (FtwDevice->FtwLastWriteRecord->FvBaseAddress, &Fvb);
      if (FvbHandle != NULL) {
        Status = FtwRestart (&FtwDevice->FtwInstance, FvbHandle);
        DEBUG ((EFI_D_ERROR, "FtwLite: Restart last write - %r\n", Status));
        ASSERT_EFI_ERROR (Status);
      }
      FtwAbort (&FtwDevice->FtwInstance);
    }
  }
  //
  // Hook the protocol API
  //
  FtwDevice->FtwInstance.GetMaxBlockSize = FtwGetMaxBlockSize;
  FtwDevice->FtwInstance.Allocate        = FtwAllocate;
  FtwDevice->FtwInstance.Write           = FtwWrite;
  FtwDevice->FtwInstance.Restart         = FtwRestart;
  FtwDevice->FtwInstance.Abort           = FtwAbort;
  FtwDevice->FtwInstance.GetLastWrite    = FtwGetLastWrite;
    
  return EFI_SUCCESS;
}
Esempio n. 2
0
/**
  Firmware Volume Block Protocol notification event handler.

  Initialization for Fault Tolerant Write is done in this handler.

  @param[in] Event    Event whose notification function is being invoked.
  @param[in] Context  Pointer to the notification function's context.
**/
VOID
EFIAPI
FvbNotificationEvent (
  IN  EFI_EVENT       Event,
  IN  VOID            *Context
  )
{
  EFI_STATUS                          Status;
  EFI_HANDLE                          *HandleBuffer;
  UINTN                               HandleCount;
  UINTN                               Index;
  EFI_PHYSICAL_ADDRESS                FvbBaseAddress;
  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *Fvb;
  EFI_FIRMWARE_VOLUME_HEADER          *FwVolHeader;
  EFI_FVB_ATTRIBUTES_2                Attributes;
  EFI_FTW_DEVICE                      *FtwDevice;
  EFI_FV_BLOCK_MAP_ENTRY              *FvbMapEntry;
  UINT32                              LbaIndex;
  UINTN                               Length;
  EFI_FAULT_TOLERANT_WRITE_HEADER     *FtwHeader;
  UINTN                               Offset;
  EFI_HANDLE                          FvbHandle;

  FtwDevice = (EFI_FTW_DEVICE *)Context;
  FvbHandle = NULL;
  Fvb       = NULL;

  FtwDevice->WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwWorkingBase);
  FtwDevice->SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwSpareBase);

  //
  // Locate all handles of Fvb protocol
  //
  Status = gBS->LocateHandleBuffer (
                ByProtocol,
                &gEfiFirmwareVolumeBlockProtocolGuid,
                NULL,
                &HandleCount,
                &HandleBuffer
                );
  if (EFI_ERROR (Status)) {
    return;
  }

  //
  // Get the FVB to access variable store
  //
  for (Index = 0; Index < HandleCount; Index += 1) {
    Status = gBS->HandleProtocol (
                  HandleBuffer[Index],
                  &gEfiFirmwareVolumeBlockProtocolGuid,
                  (VOID **) &Fvb
                  );
    if (EFI_ERROR (Status)) {
      Status = EFI_NOT_FOUND;
      break;
    }

    //
    // Ensure this FVB protocol supported Write operation.
    //
    Status = Fvb->GetAttributes (Fvb, &Attributes);
    if (EFI_ERROR (Status) || ((Attributes & EFI_FVB2_WRITE_STATUS) == 0)) {
      continue;     
    }
    //
    // Compare the address and select the right one
    //
    Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress);
    if (EFI_ERROR (Status)) {
      continue;
    }

    FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) FvbBaseAddress);
    if ((FtwDevice->FtwFvBlock == NULL) && (FtwDevice->WorkSpaceAddress >= FvbBaseAddress) &&
      ((FtwDevice->WorkSpaceAddress + FtwDevice->WorkSpaceLength) <= (FvbBaseAddress + FwVolHeader->FvLength))
      ) {
      FtwDevice->FtwFvBlock = Fvb;
      //
      // To get the LBA of work space
      //
      if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) {
        //
        // Now, one FV has one type of BlockLength
        //
        FvbMapEntry = &FwVolHeader->BlockMap[0];
        for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) {
          if ((FtwDevice->WorkSpaceAddress >= (FvbBaseAddress + FvbMapEntry->Length * (LbaIndex - 1)))
              && (FtwDevice->WorkSpaceAddress < (FvbBaseAddress + FvbMapEntry->Length * LbaIndex))) {
            FtwDevice->FtwWorkSpaceLba = LbaIndex - 1;
            //
            // Get the Work space size and Base(Offset)
            //
            FtwDevice->FtwWorkSpaceSize = FtwDevice->WorkSpaceLength;
            FtwDevice->FtwWorkSpaceBase = (UINTN) (FtwDevice->WorkSpaceAddress - (FvbBaseAddress + FvbMapEntry->Length * (LbaIndex - 1)));
            break;
          }
        }
      }
    }
    
    if ((FtwDevice->FtwBackupFvb == NULL) && (FtwDevice->SpareAreaAddress >= FvbBaseAddress) &&
      ((FtwDevice->SpareAreaAddress + FtwDevice->SpareAreaLength) <= (FvbBaseAddress + FwVolHeader->FvLength))
      ) {
      FtwDevice->FtwBackupFvb = Fvb;
      //
      // To get the LBA of spare
      //
      if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) {
        //
        // Now, one FV has one type of BlockLength
        //
        FvbMapEntry = &FwVolHeader->BlockMap[0];
        for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) {
          if ((FtwDevice->SpareAreaAddress >= (FvbBaseAddress + FvbMapEntry->Length * (LbaIndex - 1)))
              && (FtwDevice->SpareAreaAddress < (FvbBaseAddress + FvbMapEntry->Length * LbaIndex))) {
            //
            // Get the NumberOfSpareBlock and BlockSize
            //
            FtwDevice->FtwSpareLba   = LbaIndex - 1;
            FtwDevice->BlockSize     = FvbMapEntry->Length;
            FtwDevice->NumberOfSpareBlock = FtwDevice->SpareAreaLength / FtwDevice->BlockSize;
            //
            // Check the range of spare area to make sure that it's in FV range
            //
            if ((FtwDevice->FtwSpareLba + FtwDevice->NumberOfSpareBlock) > FvbMapEntry->NumBlocks) {
              DEBUG ((EFI_D_ERROR, "Ftw: Spare area is out of FV range\n"));
              ASSERT (FALSE);
              return;
            }
            break;
          }
        }
      }
    }
  }

  if ((FtwDevice->FtwBackupFvb == NULL) || (FtwDevice->FtwFvBlock == NULL) ||
    (FtwDevice->FtwWorkSpaceLba == (EFI_LBA) (-1)) || (FtwDevice->FtwSpareLba == (EFI_LBA) (-1))) {
    return;
  }

  DEBUG ((EFI_D_INFO, "Ftw: Working and spare FVB is ready\n"));
  //
  // Calculate the start LBA of working block. Working block is an area which
  // contains working space in its last block and has the same size as spare
  // block, unless there are not enough blocks before the block that contains
  // working space.
  //
  FtwDevice->FtwWorkBlockLba = FtwDevice->FtwWorkSpaceLba - FtwDevice->NumberOfSpareBlock + 1;
  ASSERT ((INT64) (FtwDevice->FtwWorkBlockLba) >= 0); 

  //
  // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.
  //
  FtwDevice->FtwWorkSpace = (UINT8 *) (FtwDevice + 1);
  FtwDevice->FtwWorkSpaceHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) FtwDevice->FtwWorkSpace;

  FtwDevice->FtwLastWriteHeader = NULL;
  FtwDevice->FtwLastWriteRecord = NULL;

  //
  // Refresh the working space data from working block
  //
  Status = WorkSpaceRefresh (FtwDevice);
  ASSERT_EFI_ERROR (Status);
  //
  // If the working block workspace is not valid, try the spare block
  //
  if (!IsValidWorkSpace (FtwDevice->FtwWorkSpaceHeader)) {
    //
    // Read from spare block
    //
    Length = FtwDevice->FtwWorkSpaceSize;
    Status = FtwDevice->FtwBackupFvb->Read (
                    FtwDevice->FtwBackupFvb,
                    FtwDevice->FtwSpareLba,
                    FtwDevice->FtwWorkSpaceBase,
                    &Length,
                    FtwDevice->FtwWorkSpace
                    );
    ASSERT_EFI_ERROR (Status);

    //
    // If spare block is valid, then replace working block content.
    //
    if (IsValidWorkSpace (FtwDevice->FtwWorkSpaceHeader)) {
      Status = FlushSpareBlockToWorkingBlock (FtwDevice);
      DEBUG ((EFI_D_ERROR, "Ftw: Restart working block update in Init() - %r\n", Status));
      FtwAbort (&FtwDevice->FtwInstance);
      //
      // Refresh work space.
      //
      Status = WorkSpaceRefresh (FtwDevice);
      ASSERT_EFI_ERROR (Status);
    } else {
      DEBUG ((EFI_D_ERROR, "Ftw: Both are invalid, init workspace\n"));
      //
      // If both are invalid, then initialize work space.
      //
      SetMem (
        FtwDevice->FtwWorkSpace,
        FtwDevice->FtwWorkSpaceSize,
        FTW_ERASED_BYTE
        );
      InitWorkSpaceHeader (FtwDevice->FtwWorkSpaceHeader);
      //
      // Initialize the work space
      //
      Status = FtwReclaimWorkSpace (FtwDevice, FALSE);
      ASSERT_EFI_ERROR (Status);
    }
  }
  //
  // If the FtwDevice->FtwLastWriteRecord is 1st record of write header &&
  // (! SpareComplete) THEN call Abort().
  //
  if ((FtwDevice->FtwLastWriteHeader->HeaderAllocated == FTW_VALID_STATE) &&
    (FtwDevice->FtwLastWriteRecord->SpareComplete != FTW_VALID_STATE) &&
    IsFirstRecordOfWrites (FtwDevice->FtwLastWriteHeader, FtwDevice->FtwLastWriteRecord)
    ) {
    DEBUG ((EFI_D_ERROR, "Ftw: Init.. find first record not SpareCompleted, abort()\n"));
    FtwAbort (&FtwDevice->FtwInstance);
  }
  //
  // If Header is incompleted and the last record has completed, then
  // call Abort() to set the Header->Complete FLAG.
  //
  if ((FtwDevice->FtwLastWriteHeader->Complete != FTW_VALID_STATE) &&
    (FtwDevice->FtwLastWriteRecord->DestinationComplete == FTW_VALID_STATE) &&
    IsLastRecordOfWrites (FtwDevice->FtwLastWriteHeader, FtwDevice->FtwLastWriteRecord)
    ) {
    DEBUG ((EFI_D_ERROR, "Ftw: Init.. find last record completed but header not, abort()\n"));
    FtwAbort (&FtwDevice->FtwInstance);
  }
  //
  // To check the workspace buffer following last Write header/records is EMPTY or not.
  // If it's not EMPTY, FTW also need to call reclaim().
  //
  FtwHeader = FtwDevice->FtwLastWriteHeader;
  Offset    = (UINT8 *) FtwHeader - FtwDevice->FtwWorkSpace;
  if (FtwDevice->FtwWorkSpace[Offset] != FTW_ERASED_BYTE) {
    Offset += WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites, FtwHeader->PrivateDataSize);
  }
  
  if (!IsErasedFlashBuffer (FtwDevice->FtwWorkSpace + Offset, FtwDevice->FtwWorkSpaceSize - Offset)) {
    Status = FtwReclaimWorkSpace (FtwDevice, TRUE);
    ASSERT_EFI_ERROR (Status);
  }

  //
  // Restart if it's boot block
  //
  if ((FtwDevice->FtwLastWriteHeader->Complete != FTW_VALID_STATE) &&
    (FtwDevice->FtwLastWriteRecord->SpareComplete == FTW_VALID_STATE)
    ) {
    if (FtwDevice->FtwLastWriteRecord->BootBlockUpdate == FTW_VALID_STATE) {
      Status = FlushSpareBlockToBootBlock (FtwDevice);
      DEBUG ((EFI_D_ERROR, "Ftw: Restart boot block update - %r\n", Status));
      ASSERT_EFI_ERROR (Status);
      FtwAbort (&FtwDevice->FtwInstance);
    } else {
      //
      // if (SpareCompleted) THEN  Restart to fault tolerant write.
      //
      FvbHandle = GetFvbByAddress (FtwDevice->FtwLastWriteRecord->FvBaseAddress, &Fvb);
      if (FvbHandle != NULL) {
        Status = FtwRestart (&FtwDevice->FtwInstance, FvbHandle);
        DEBUG ((EFI_D_ERROR, "FtwLite: Restart last write - %r\n", Status));
        ASSERT_EFI_ERROR (Status);
      }
      FtwAbort (&FtwDevice->FtwInstance);
    }
  }
  //
  // Hook the protocol API
  //
  FtwDevice->FtwInstance.GetMaxBlockSize = FtwGetMaxBlockSize;
  FtwDevice->FtwInstance.Allocate        = FtwAllocate;
  FtwDevice->FtwInstance.Write           = FtwWrite;
  FtwDevice->FtwInstance.Restart         = FtwRestart;
  FtwDevice->FtwInstance.Abort           = FtwAbort;
  FtwDevice->FtwInstance.GetLastWrite    = FtwGetLastWrite;
  
  //
  // Install protocol interface
  //
  Status = gBS->InstallProtocolInterface (
            &FtwDevice->Handle,
            &gEfiFaultTolerantWriteProtocolGuid,
            EFI_NATIVE_INTERFACE,
            &FtwDevice->FtwInstance
            );

  ASSERT_EFI_ERROR (Status);
  
  //
  // Close the notify event to avoid install FaultTolerantWriteProtocol again.
  //
  Status = gBS->CloseEvent (Event);	
  ASSERT_EFI_ERROR (Status);
  
  return;
}
Esempio n. 3
0
/**
  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;
}
Esempio n. 4
0
/**
  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 targt 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;
}
Esempio n. 5
0
EFI_STATUS
FtwReclaimWorkSpace (
  IN EFI_FTW_LITE_DEVICE  *FtwLiteDevice,
  IN BOOLEAN              PreserveRecord
  )
/*++

Routine Description:
    Reclaim the work space on the working block.

Arguments:
    FtwLiteDevice     - Point to private data of FTW driver
    PreserveRecord    - Whether to preserve the working record is needed

Returns:
    EFI_SUCCESS           - The function completed successfully
    EFI_OUT_OF_RESOURCES  - Allocate memory error
    EFI_ABORTED           - The function could not complete successfully

--*/
{
  EFI_STATUS                              Status;
  UINT8                                   *TempBuffer;
  UINTN                                   TempBufferSize;
  UINT8                                   *Ptr;
  UINTN                                   Length;
  UINTN                                   Index;
  UINTN                                   SpareBufferSize;
  UINT8                                   *SpareBuffer;
  EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader;
  EFI_FTW_LITE_RECORD                     *Record;

  DEBUG ((EFI_D_FTW_LITE, "FtwLite: start to reclaim work space\n"));

  //
  // Read all original data from working block to a memory buffer
  //
  TempBufferSize = FtwLiteDevice->SpareAreaLength;
  Status = gBS->AllocatePool (
                  EfiBootServicesData,
                  TempBufferSize,
                  &TempBuffer
                  );
  if (EFI_ERROR (Status)) {
    return EFI_OUT_OF_RESOURCES;
  }

  EfiZeroMem (TempBuffer, TempBufferSize);

  Ptr = TempBuffer;
  for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
    Length = FtwLiteDevice->SizeOfSpareBlock;
    Status = FtwLiteDevice->FtwFvBlock->Read (
                                          FtwLiteDevice->FtwFvBlock,
                                          FtwLiteDevice->FtwWorkBlockLba + Index,
                                          0,
                                          &Length,
                                          Ptr
                                          );
    if (EFI_ERROR (Status)) {
      gBS->FreePool (TempBuffer);
      return EFI_ABORTED;
    }

    Ptr += Length;
  }
  //
  // Clean up the workspace, remove all the completed records.
  //
  Ptr = TempBuffer +
    ((UINTN) (FtwLiteDevice->FtwWorkSpaceLba - FtwLiteDevice->FtwWorkBlockLba)) *
    FtwLiteDevice->SizeOfSpareBlock +
    FtwLiteDevice->FtwWorkSpaceBase;

  //
  // Clear the content of buffer that will save the new work space data
  //
  EfiSetMem (Ptr, FtwLiteDevice->FtwWorkSpaceSize, FTW_ERASED_BYTE);

  //
  // Copy EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER to buffer
  //
  EfiCopyMem (
    Ptr,
    FtwLiteDevice->FtwWorkSpaceHeader,
    sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)
    );

  if (PreserveRecord) {
    //
    // Get the last record
    //
    Status = FtwGetLastRecord (FtwLiteDevice, &FtwLiteDevice->FtwLastRecord);
    Record = FtwLiteDevice->FtwLastRecord;
    if (!EFI_ERROR (Status)                       &&
        Record                 != NULL            &&
        Record->WriteAllocated == FTW_VALID_STATE &&
        Record->WriteCompleted != FTW_VALID_STATE) {
      EfiCopyMem (
        (UINT8 *) Ptr + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
        Record,
        WRITE_TOTAL_SIZE
        );
    }
  }
  
  EfiCopyMem (
    FtwLiteDevice->FtwWorkSpace,
    Ptr,
    FtwLiteDevice->FtwWorkSpaceSize
    );

  Status = FtwGetLastRecord (FtwLiteDevice, &FtwLiteDevice->FtwLastRecord);

  //
  // Set the WorkingBlockValid and WorkingBlockInvalid as INVALID
  //
  WorkingBlockHeader                      = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) Ptr;
  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 = FtwLiteDevice->SpareAreaLength;
  SpareBuffer     = EfiLibAllocatePool (SpareBufferSize);
  if (SpareBuffer == NULL) {
    gBS->FreePool (TempBuffer);
    return EFI_OUT_OF_RESOURCES;
  }

  Ptr = SpareBuffer;
  for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
    Length = FtwLiteDevice->SizeOfSpareBlock;
    Status = FtwLiteDevice->FtwBackupFvb->Read (
                                            FtwLiteDevice->FtwBackupFvb,
                                            FtwLiteDevice->FtwSpareLba + Index,
                                            0,
                                            &Length,
                                            Ptr
                                            );
    if (EFI_ERROR (Status)) {
      gBS->FreePool (TempBuffer);
      gBS->FreePool (SpareBuffer);
      return EFI_ABORTED;
    }

    Ptr += Length;
  }
  //
  // Write the memory buffer to spare block
  //
  Status  = FtwEraseSpareBlock (FtwLiteDevice);
  Ptr     = TempBuffer;
  for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
    Length = FtwLiteDevice->SizeOfSpareBlock;
    Status = FtwLiteDevice->FtwBackupFvb->Write (
                                            FtwLiteDevice->FtwBackupFvb,
                                            FtwLiteDevice->FtwSpareLba + Index,
                                            0,
                                            &Length,
                                            Ptr
                                            );
    if (EFI_ERROR (Status)) {
      gBS->FreePool (TempBuffer);
      gBS->FreePool (SpareBuffer);
      return EFI_ABORTED;
    }

    Ptr += Length;
  }
  //
  // Free TempBuffer
  //
  gBS->FreePool (TempBuffer);

  //
  // Write the spare block to working block
  //
  Status = FlushSpareBlockToWorkingBlock (FtwLiteDevice);
  if (EFI_ERROR (Status)) {
    gBS->FreePool (SpareBuffer);
    return Status;
  }
  //
  // Restore spare backup buffer into spare block , if no failure happened during FtwWrite.
  //
  Status  = FtwEraseSpareBlock (FtwLiteDevice);
  Ptr     = SpareBuffer;
  for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
    Length = FtwLiteDevice->SizeOfSpareBlock;
    Status = FtwLiteDevice->FtwBackupFvb->Write (
                                            FtwLiteDevice->FtwBackupFvb,
                                            FtwLiteDevice->FtwSpareLba + Index,
                                            0,
                                            &Length,
                                            Ptr
                                            );
    if (EFI_ERROR (Status)) {
      gBS->FreePool (SpareBuffer);
      return EFI_ABORTED;
    }

    Ptr += Length;
  }

  gBS->FreePool (SpareBuffer);

  DEBUG ((EFI_D_FTW_LITE, "FtwLite: reclaim work space success\n"));

  return EFI_SUCCESS;
}