Example #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;
}
Example #2
0
/**
  Communication service SMI Handler entry.

  This SMI handler provides services for the fault tolerant write wrapper driver.

  Caution: This function requires additional review when modified.
  This driver need to make sure the CommBuffer is not in the SMRAM range. 
  Also in FTW_FUNCTION_GET_LAST_WRITE case, check SmmFtwGetLastWriteHeader->Data + 
  SmmFtwGetLastWriteHeader->PrivateDataSize within communication buffer.

  @param[in]     DispatchHandle  The unique handle assigned to this handler by SmiHandlerRegister().
  @param[in]     RegisterContext Points to an optional handler context which was specified when the
                                 handler was registered.
  @param[in, out] CommBuffer     A pointer to a collection of data in memory that will be conveyed
                                 from a non-SMM environment into an SMM environment.
  @param[in, out] CommBufferSize The size of the CommBuffer.

  @retval EFI_SUCCESS                         The interrupt was handled and quiesced. No other handlers 
                                              should still be called.
  @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED  The interrupt has been quiesced but other handlers should 
                                              still be called.
  @retval EFI_WARN_INTERRUPT_SOURCE_PENDING   The interrupt is still pending and other handlers should still 
                                              be called.
  @retval EFI_INTERRUPT_PENDING               The interrupt could not be quiesced.
  
**/
EFI_STATUS
EFIAPI
SmmFaultTolerantWriteHandler (
  IN     EFI_HANDLE                                DispatchHandle,
  IN     CONST VOID                                *RegisterContext,
  IN OUT VOID                                      *CommBuffer,
  IN OUT UINTN                                     *CommBufferSize
  )
{
  EFI_STATUS                                       Status;
  SMM_FTW_COMMUNICATE_FUNCTION_HEADER              *SmmFtwFunctionHeader;
  SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER                *SmmGetMaxBlockSizeHeader;
  SMM_FTW_ALLOCATE_HEADER                          *SmmFtwAllocateHeader;
  SMM_FTW_WRITE_HEADER                             *SmmFtwWriteHeader;
  SMM_FTW_RESTART_HEADER                           *SmmFtwRestartHeader;
  SMM_FTW_GET_LAST_WRITE_HEADER                    *SmmFtwGetLastWriteHeader;
  VOID                                             *PrivateData;
  EFI_HANDLE                                       SmmFvbHandle;
  UINTN                                            InfoSize;
  UINTN                                            CommBufferPayloadSize;
  UINTN                                            PrivateDataSize;
  UINTN                                            Length;
  UINTN                                            TempCommBufferSize;

  //
  // If input is invalid, stop processing this SMI
  //
  if (CommBuffer == NULL || CommBufferSize == NULL) {
    return EFI_SUCCESS;
  }

  TempCommBufferSize = *CommBufferSize;

  if (TempCommBufferSize < SMM_FTW_COMMUNICATE_HEADER_SIZE) {
    DEBUG ((EFI_D_ERROR, "SmmFtwHandler: SMM communication buffer size invalid!\n"));
    return EFI_SUCCESS;
  }
  CommBufferPayloadSize = TempCommBufferSize - SMM_FTW_COMMUNICATE_HEADER_SIZE;

  if (!SmmIsBufferOutsideSmmValid ((UINTN)CommBuffer, TempCommBufferSize)) {
    DEBUG ((EFI_D_ERROR, "SmmFtwHandler: SMM communication buffer in SMRAM or overflow!\n"));
    return EFI_SUCCESS;
  }

  SmmFtwFunctionHeader = (SMM_FTW_COMMUNICATE_FUNCTION_HEADER *)CommBuffer;

  if (mEndOfDxe) {
    //
    // It will be not safe to expose the operations after End Of Dxe.
    //
    DEBUG ((EFI_D_ERROR, "SmmFtwHandler: Not safe to do the operation: %x after End Of Dxe, so access denied!\n", SmmFtwFunctionHeader->Function));
    SmmFtwFunctionHeader->ReturnStatus = EFI_ACCESS_DENIED;
    return EFI_SUCCESS;
  }

  switch (SmmFtwFunctionHeader->Function) {
    case FTW_FUNCTION_GET_MAX_BLOCK_SIZE:
      if (CommBufferPayloadSize < sizeof (SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER)) {
        DEBUG ((EFI_D_ERROR, "GetMaxBlockSize: SMM communication buffer size invalid!\n"));
        return EFI_SUCCESS;
      }
      SmmGetMaxBlockSizeHeader = (SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER *) SmmFtwFunctionHeader->Data;

      Status = FtwGetMaxBlockSize (
                 &mFtwDevice->FtwInstance,
                 &SmmGetMaxBlockSizeHeader->BlockSize
                 );
      break;
      
    case FTW_FUNCTION_ALLOCATE:
      if (CommBufferPayloadSize < sizeof (SMM_FTW_ALLOCATE_HEADER)) {
        DEBUG ((EFI_D_ERROR, "Allocate: SMM communication buffer size invalid!\n"));
        return EFI_SUCCESS;
      }
      SmmFtwAllocateHeader = (SMM_FTW_ALLOCATE_HEADER *) SmmFtwFunctionHeader->Data;
      Status = FtwAllocate (
                 &mFtwDevice->FtwInstance,
                 &SmmFtwAllocateHeader->CallerId,
                 SmmFtwAllocateHeader->PrivateDataSize,
                 SmmFtwAllocateHeader->NumberOfWrites
                 );
      break;
      
    case FTW_FUNCTION_WRITE:
      if (CommBufferPayloadSize < OFFSET_OF (SMM_FTW_WRITE_HEADER, Data)) {
        DEBUG ((EFI_D_ERROR, "Write: SMM communication buffer size invalid!\n"));
        return EFI_SUCCESS;
      }
      SmmFtwWriteHeader = (SMM_FTW_WRITE_HEADER *) SmmFtwFunctionHeader->Data;
      Length = SmmFtwWriteHeader->Length;
      PrivateDataSize = SmmFtwWriteHeader->PrivateDataSize;
      if (((UINTN)(~0) - Length < OFFSET_OF (SMM_FTW_WRITE_HEADER, Data)) ||
        ((UINTN)(~0) - PrivateDataSize < OFFSET_OF (SMM_FTW_WRITE_HEADER, Data) + Length)) {
        //
        // Prevent InfoSize overflow
        //
        Status = EFI_ACCESS_DENIED;
        break;
      }
      InfoSize = OFFSET_OF (SMM_FTW_WRITE_HEADER, Data) + Length + PrivateDataSize;

      //
      // SMRAM range check already covered before
      //
      if (InfoSize > CommBufferPayloadSize) {
        DEBUG ((EFI_D_ERROR, "Write: Data size exceed communication buffer size limit!\n"));
        Status = EFI_ACCESS_DENIED;
        break;
      }

      if (PrivateDataSize == 0) {
        PrivateData = NULL;
      } else {
        PrivateData = (VOID *)&SmmFtwWriteHeader->Data[Length];
      }
      Status = GetFvbByAddressAndAttribute (
                 SmmFtwWriteHeader->FvbBaseAddress, 
                 SmmFtwWriteHeader->FvbAttributes,
                 &SmmFvbHandle
                 );
      if (!EFI_ERROR (Status)) {
        Status = FtwWrite(
                   &mFtwDevice->FtwInstance,
                   SmmFtwWriteHeader->Lba,
                   SmmFtwWriteHeader->Offset,
                   Length,
                   PrivateData,
                   SmmFvbHandle,
                   SmmFtwWriteHeader->Data
                   );
      }
      break;
      
    case FTW_FUNCTION_RESTART:
      if (CommBufferPayloadSize < sizeof (SMM_FTW_RESTART_HEADER)) {
        DEBUG ((EFI_D_ERROR, "Restart: SMM communication buffer size invalid!\n"));
        return EFI_SUCCESS;
      }
      SmmFtwRestartHeader = (SMM_FTW_RESTART_HEADER *) SmmFtwFunctionHeader->Data;
      Status = GetFvbByAddressAndAttribute (
                 SmmFtwRestartHeader->FvbBaseAddress, 
                 SmmFtwRestartHeader->FvbAttributes,
                 &SmmFvbHandle
                 );      
      if (!EFI_ERROR (Status)) {
        Status = FtwRestart (&mFtwDevice->FtwInstance, SmmFvbHandle);
      }
      break;

    case FTW_FUNCTION_ABORT:
      Status = FtwAbort (&mFtwDevice->FtwInstance);
      break;
      
    case FTW_FUNCTION_GET_LAST_WRITE:
      if (CommBufferPayloadSize < OFFSET_OF (SMM_FTW_GET_LAST_WRITE_HEADER, Data)) {
        DEBUG ((EFI_D_ERROR, "GetLastWrite: SMM communication buffer size invalid!\n"));
        return EFI_SUCCESS;
      }
      SmmFtwGetLastWriteHeader = (SMM_FTW_GET_LAST_WRITE_HEADER *) SmmFtwFunctionHeader->Data;
      PrivateDataSize = SmmFtwGetLastWriteHeader->PrivateDataSize;
      if ((UINTN)(~0) - PrivateDataSize < OFFSET_OF (SMM_FTW_GET_LAST_WRITE_HEADER, Data)){
        //
        // Prevent InfoSize overflow
        //
        Status = EFI_ACCESS_DENIED;
        break;
      }
      InfoSize = OFFSET_OF (SMM_FTW_GET_LAST_WRITE_HEADER, Data) + PrivateDataSize;

      //
      // SMRAM range check already covered before
      //
      if (InfoSize > CommBufferPayloadSize) {
        DEBUG ((EFI_D_ERROR, "Data size exceed communication buffer size limit!\n"));
        Status = EFI_ACCESS_DENIED;
        break;
      }

      Status = FtwGetLastWrite (
                 &mFtwDevice->FtwInstance,
                 &SmmFtwGetLastWriteHeader->CallerId,
                 &SmmFtwGetLastWriteHeader->Lba,
                 &SmmFtwGetLastWriteHeader->Offset,
                 &SmmFtwGetLastWriteHeader->Length,
                 &PrivateDataSize,
                 (VOID *)SmmFtwGetLastWriteHeader->Data,
                 &SmmFtwGetLastWriteHeader->Complete
                 );
      SmmFtwGetLastWriteHeader->PrivateDataSize = PrivateDataSize;
      break;

    default:
      Status = EFI_UNSUPPORTED;
  }

  SmmFtwFunctionHeader->ReturnStatus = Status;

  return EFI_SUCCESS;
}
Example #3
0
/**
  Communication service SMI Handler entry.

  This SMI handler provides services for the fault tolerant write wrapper driver.

  Caution: This function requires additional review when modified.
  This driver need to make sure the CommBuffer is not in the SMRAM range. 
  Also in FTW_FUNCTION_GET_LAST_WRITE case, check SmmFtwGetLastWriteHeader->Data + 
  SmmFtwGetLastWriteHeader->PrivateDataSize within communication buffer.

  @param[in]     DispatchHandle  The unique handle assigned to this handler by SmiHandlerRegister().
  @param[in]     RegisterContext Points to an optional handler context which was specified when the
                                 handler was registered.
  @param[in, out] CommBuffer     A pointer to a collection of data in memory that will be conveyed
                                 from a non-SMM environment into an SMM environment.
  @param[in, out] CommBufferSize The size of the CommBuffer.

  @retval EFI_SUCCESS                         The interrupt was handled and quiesced. No other handlers 
                                              should still be called.
  @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED  The interrupt has been quiesced but other handlers should 
                                              still be called.
  @retval EFI_WARN_INTERRUPT_SOURCE_PENDING   The interrupt is still pending and other handlers should still 
                                              be called.
  @retval EFI_INTERRUPT_PENDING               The interrupt could not be quiesced.
  
**/
EFI_STATUS
EFIAPI
SmmFaultTolerantWriteHandler (
  IN     EFI_HANDLE                                DispatchHandle,
  IN     CONST VOID                                *RegisterContext,
  IN OUT VOID                                      *CommBuffer,
  IN OUT UINTN                                     *CommBufferSize
  )
{
  EFI_STATUS                                       Status;
  SMM_FTW_COMMUNICATE_FUNCTION_HEADER              *SmmFtwFunctionHeader;
  SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER                *SmmGetMaxBlockSizeHeader;
  SMM_FTW_ALLOCATE_HEADER                          *SmmFtwAllocateHeader;
  SMM_FTW_WRITE_HEADER                             *SmmFtwWriteHeader;
  SMM_FTW_RESTART_HEADER                           *SmmFtwRestartHeader;
  SMM_FTW_GET_LAST_WRITE_HEADER                    *SmmFtwGetLastWriteHeader;
  VOID                                             *PrivateData;
  EFI_HANDLE                                       SmmFvbHandle;
  UINTN                                            InfoSize;


  //
  // If input is invalid, stop processing this SMI
  //
  if (CommBuffer == NULL || CommBufferSize == NULL) {
    return EFI_SUCCESS;
  }

  if (*CommBufferSize < SMM_FTW_COMMUNICATE_HEADER_SIZE) {
    return EFI_SUCCESS;
  }

  if (InternalIsAddressInSmram ((EFI_PHYSICAL_ADDRESS)(UINTN)CommBuffer, *CommBufferSize)) {
    DEBUG ((EFI_D_ERROR, "SMM communication buffer size is in SMRAM!\n"));
    return EFI_SUCCESS;
  }

  SmmFtwFunctionHeader = (SMM_FTW_COMMUNICATE_FUNCTION_HEADER *)CommBuffer;
  switch (SmmFtwFunctionHeader->Function) {
    case FTW_FUNCTION_GET_MAX_BLOCK_SIZE:
      SmmGetMaxBlockSizeHeader = (SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER *) SmmFtwFunctionHeader->Data;
      InfoSize = sizeof (SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER);

      //
      // SMRAM range check already covered before
      //
      if (InfoSize > *CommBufferSize - SMM_FTW_COMMUNICATE_HEADER_SIZE) {
        DEBUG ((EFI_D_ERROR, "Data size exceed communication buffer size limit!\n"));
        Status = EFI_ACCESS_DENIED;
        break;
      }

      Status = FtwGetMaxBlockSize (
                 &mFtwDevice->FtwInstance,
                 &SmmGetMaxBlockSizeHeader->BlockSize
                 );
      break;
      
    case FTW_FUNCTION_ALLOCATE:
      SmmFtwAllocateHeader = (SMM_FTW_ALLOCATE_HEADER *) SmmFtwFunctionHeader->Data;
      Status = FtwAllocate (
                 &mFtwDevice->FtwInstance,
                 &SmmFtwAllocateHeader->CallerId,
                 SmmFtwAllocateHeader->PrivateDataSize,
                 SmmFtwAllocateHeader->NumberOfWrites
                 );
      break;
      
    case FTW_FUNCTION_WRITE:
      SmmFtwWriteHeader = (SMM_FTW_WRITE_HEADER *) SmmFtwFunctionHeader->Data;
      if (SmmFtwWriteHeader->PrivateDataSize == 0) {
        PrivateData = NULL;
      } else {
        PrivateData = (VOID *)&SmmFtwWriteHeader->Data[SmmFtwWriteHeader->Length];
      }
      Status = GetFvbByAddressAndAttribute (
                 SmmFtwWriteHeader->FvbBaseAddress, 
                 SmmFtwWriteHeader->FvbAttributes,
                 &SmmFvbHandle
                 );
      if (!EFI_ERROR (Status)) {
        Status = FtwWrite(
                   &mFtwDevice->FtwInstance,
                   SmmFtwWriteHeader->Lba,
                   SmmFtwWriteHeader->Offset,
                   SmmFtwWriteHeader->Length,
                   PrivateData,
                   SmmFvbHandle,
                   SmmFtwWriteHeader->Data
                   );
      }
      break;
      
    case FTW_FUNCTION_RESTART:
      SmmFtwRestartHeader = (SMM_FTW_RESTART_HEADER *) SmmFtwFunctionHeader->Data;
      Status = GetFvbByAddressAndAttribute (
                 SmmFtwRestartHeader->FvbBaseAddress, 
                 SmmFtwRestartHeader->FvbAttributes,
                 &SmmFvbHandle
                 );      
      if (!EFI_ERROR (Status)) {
        Status = FtwRestart (&mFtwDevice->FtwInstance, SmmFvbHandle);
      }
      break;

    case FTW_FUNCTION_ABORT:
      Status = FtwAbort (&mFtwDevice->FtwInstance);
      break;
      
    case FTW_FUNCTION_GET_LAST_WRITE:
      SmmFtwGetLastWriteHeader = (SMM_FTW_GET_LAST_WRITE_HEADER *) SmmFtwFunctionHeader->Data;
      InfoSize = OFFSET_OF (SMM_FTW_GET_LAST_WRITE_HEADER, Data) + SmmFtwGetLastWriteHeader->PrivateDataSize;

      //
      // SMRAM range check already covered before
      //
      if (InfoSize > *CommBufferSize - SMM_FTW_COMMUNICATE_HEADER_SIZE) {
        DEBUG ((EFI_D_ERROR, "Data size exceed communication buffer size limit!\n"));
        Status = EFI_ACCESS_DENIED;
        break;
      }

      Status = FtwGetLastWrite (
                 &mFtwDevice->FtwInstance,
                 &SmmFtwGetLastWriteHeader->CallerId,
                 &SmmFtwGetLastWriteHeader->Lba,
                 &SmmFtwGetLastWriteHeader->Offset,
                 &SmmFtwGetLastWriteHeader->Length,
                 &SmmFtwGetLastWriteHeader->PrivateDataSize,
                 (VOID *)SmmFtwGetLastWriteHeader->Data,
                 &SmmFtwGetLastWriteHeader->Complete
                 );
      break;

    default:
      Status = EFI_UNSUPPORTED;
  }

  SmmFtwFunctionHeader->ReturnStatus = Status;

  return EFI_SUCCESS;
}
Example #4
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;
}