mirror of
				https://git.proxmox.com/git/mirror_edk2
				synced 2025-11-04 02:40:26 +00:00 
			
		
		
		
	REF:https://bugzilla.tianocore.org/show_bug.cgi?id=3479 Adds support to the UEFI variable fault tolerant write (FTW) drivers to receive FTW base and size information dynamically via the Variable Flash Information library. Cc: Jian J Wang <jian.j.wang@intel.com> Cc: Hao A Wu <hao.a.wu@intel.com> Cc: Liming Gao <gaoliming@byosoft.com.cn> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> Acked-by: Ard Biesheuvel <ardb@kernel.org> Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn> Reviewed-by: Sami Mujawar <sami.mujawar@arm.com>
		
			
				
	
	
		
			331 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			331 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  This driver installs gEdkiiFaultTolerantWriteGuid PPI to inform
 | 
						|
  the check for FTW last write data has been done.
 | 
						|
 | 
						|
Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
 | 
						|
SPDX-License-Identifier: BSD-2-Clause-Patent
 | 
						|
 | 
						|
**/
 | 
						|
 | 
						|
#include <PiPei.h>
 | 
						|
 | 
						|
#include <Guid/SystemNvDataGuid.h>
 | 
						|
#include <Guid/FaultTolerantWrite.h>
 | 
						|
#include <Library/PeiServicesLib.h>
 | 
						|
#include <Library/PcdLib.h>
 | 
						|
#include <Library/DebugLib.h>
 | 
						|
#include <Library/BaseMemoryLib.h>
 | 
						|
#include <Library/HobLib.h>
 | 
						|
#include <Library/SafeIntLib.h>
 | 
						|
#include <Library/VariableFlashInfoLib.h>
 | 
						|
 | 
						|
EFI_PEI_PPI_DESCRIPTOR  mPpiListVariable = {
 | 
						|
  (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
 | 
						|
  &gEdkiiFaultTolerantWriteGuid,
 | 
						|
  NULL
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
  Get the last Write Header pointer.
 | 
						|
  The last write header is the header whose 'complete' state hasn't been set.
 | 
						|
  After all, this header may be a EMPTY header entry for next Allocate.
 | 
						|
 | 
						|
 | 
						|
  @param FtwWorkSpaceHeader Pointer of the working block header
 | 
						|
  @param FtwWorkSpaceSize   Size of the work space
 | 
						|
  @param FtwWriteHeader     Pointer to retrieve the last write header
 | 
						|
 | 
						|
  @retval  EFI_SUCCESS      Get the last write record successfully
 | 
						|
  @retval  EFI_ABORTED      The FTW work space is damaged
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
FtwGetLastWriteHeader (
 | 
						|
  IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER  *FtwWorkSpaceHeader,
 | 
						|
  IN UINTN                                    FtwWorkSpaceSize,
 | 
						|
  OUT EFI_FAULT_TOLERANT_WRITE_HEADER         **FtwWriteHeader
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN                            Offset;
 | 
						|
  EFI_FAULT_TOLERANT_WRITE_HEADER  *FtwHeader;
 | 
						|
 | 
						|
  *FtwWriteHeader = NULL;
 | 
						|
  FtwHeader       = (EFI_FAULT_TOLERANT_WRITE_HEADER *)(FtwWorkSpaceHeader + 1);
 | 
						|
  Offset          = sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER);
 | 
						|
 | 
						|
  while (FtwHeader->Complete == FTW_VALID_STATE) {
 | 
						|
    Offset += FTW_WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites, FtwHeader->PrivateDataSize);
 | 
						|
    //
 | 
						|
    // If Offset exceed the FTW work space boudary, return error.
 | 
						|
    //
 | 
						|
    if (Offset >= FtwWorkSpaceSize) {
 | 
						|
      *FtwWriteHeader = FtwHeader;
 | 
						|
      return EFI_ABORTED;
 | 
						|
    }
 | 
						|
 | 
						|
    FtwHeader = (EFI_FAULT_TOLERANT_WRITE_HEADER *)((UINT8 *)FtwWorkSpaceHeader + Offset);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Last write header is found
 | 
						|
  //
 | 
						|
  *FtwWriteHeader = FtwHeader;
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Get the last Write Record pointer. The last write Record is the Record
 | 
						|
  whose DestinationCompleted state hasn't been set. After all, this Record
 | 
						|
  may be a EMPTY record entry for next write.
 | 
						|
 | 
						|
 | 
						|
  @param FtwWriteHeader  Pointer to the write record header
 | 
						|
  @param FtwWriteRecord  Pointer to retrieve the last write record
 | 
						|
 | 
						|
  @retval EFI_SUCCESS        Get the last write record successfully
 | 
						|
  @retval EFI_ABORTED        The FTW work space is damaged
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
FtwGetLastWriteRecord (
 | 
						|
  IN EFI_FAULT_TOLERANT_WRITE_HEADER   *FtwWriteHeader,
 | 
						|
  OUT EFI_FAULT_TOLERANT_WRITE_RECORD  **FtwWriteRecord
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN                            Index;
 | 
						|
  EFI_FAULT_TOLERANT_WRITE_RECORD  *FtwRecord;
 | 
						|
 | 
						|
  *FtwWriteRecord = NULL;
 | 
						|
  FtwRecord       = (EFI_FAULT_TOLERANT_WRITE_RECORD *)(FtwWriteHeader + 1);
 | 
						|
 | 
						|
  //
 | 
						|
  // Try to find the last write record "that has not completed"
 | 
						|
  //
 | 
						|
  for (Index = 0; Index < FtwWriteHeader->NumberOfWrites; Index += 1) {
 | 
						|
    if (FtwRecord->DestinationComplete != FTW_VALID_STATE) {
 | 
						|
      //
 | 
						|
      // The last write record is found
 | 
						|
      //
 | 
						|
      *FtwWriteRecord = FtwRecord;
 | 
						|
      return EFI_SUCCESS;
 | 
						|
    }
 | 
						|
 | 
						|
    FtwRecord++;
 | 
						|
 | 
						|
    if (FtwWriteHeader->PrivateDataSize != 0) {
 | 
						|
      FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *)((UINTN)FtwRecord + (UINTN)FtwWriteHeader->PrivateDataSize);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  //  if Index == NumberOfWrites, then
 | 
						|
  //  the last record has been written successfully,
 | 
						|
  //  but the Header->Complete Flag has not been set.
 | 
						|
  //  also return the last record.
 | 
						|
  //
 | 
						|
  if (Index == FtwWriteHeader->NumberOfWrites) {
 | 
						|
    *FtwWriteRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *)((UINTN)FtwRecord - FTW_RECORD_SIZE (FtwWriteHeader->PrivateDataSize));
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  return EFI_ABORTED;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Check to see if it is a valid work space.
 | 
						|
 | 
						|
 | 
						|
  @param WorkingHeader   Pointer of working block header
 | 
						|
  @param WorkingLength   Working block length
 | 
						|
 | 
						|
  @retval TRUE          The work space is valid.
 | 
						|
  @retval FALSE         The work space is invalid.
 | 
						|
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
IsValidWorkSpace (
 | 
						|
  IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER  *WorkingHeader,
 | 
						|
  IN UINTN                                    WorkingLength
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT8  Data;
 | 
						|
 | 
						|
  if (WorkingHeader == NULL) {
 | 
						|
    return FALSE;
 | 
						|
  }
 | 
						|
 | 
						|
  if ((WorkingHeader->WorkingBlockValid != FTW_VALID_STATE) || (WorkingHeader->WorkingBlockInvalid == FTW_VALID_STATE)) {
 | 
						|
    DEBUG ((DEBUG_ERROR, "FtwPei: Work block header valid bit check error\n"));
 | 
						|
    return FALSE;
 | 
						|
  }
 | 
						|
 | 
						|
  if (WorkingHeader->WriteQueueSize != (WorkingLength - sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER))) {
 | 
						|
    DEBUG ((DEBUG_ERROR, "FtwPei: Work block header WriteQueueSize check error\n"));
 | 
						|
    return FALSE;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Check signature with gEdkiiWorkingBlockSignatureGuid
 | 
						|
  //
 | 
						|
  if (!CompareGuid (&gEdkiiWorkingBlockSignatureGuid, &WorkingHeader->Signature)) {
 | 
						|
    DEBUG ((DEBUG_ERROR, "FtwPei: Work block header signature check error, it should be gEdkiiWorkingBlockSignatureGuid\n"));
 | 
						|
    //
 | 
						|
    // To be compatible with old signature gEfiSystemNvDataFvGuid.
 | 
						|
    //
 | 
						|
    if (!CompareGuid (&gEfiSystemNvDataFvGuid, &WorkingHeader->Signature)) {
 | 
						|
      return FALSE;
 | 
						|
    } else {
 | 
						|
      Data = *(UINT8 *)(WorkingHeader + 1);
 | 
						|
      if (Data != 0xff) {
 | 
						|
        DEBUG ((DEBUG_ERROR, "FtwPei: Old format FTW structure can't be handled\n"));
 | 
						|
        ASSERT (FALSE);
 | 
						|
        return FALSE;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Main entry for Fault Tolerant Write PEIM.
 | 
						|
 | 
						|
  @param[in]  FileHandle              Handle of the file being invoked.
 | 
						|
  @param[in]  PeiServices             Pointer to PEI Services table.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS  If the interface could be successfully installed
 | 
						|
  @retval Others       Returned from PeiServicesInstallPpi()
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
PeimFaultTolerantWriteInitialize (
 | 
						|
  IN       EFI_PEI_FILE_HANDLE  FileHandle,
 | 
						|
  IN CONST EFI_PEI_SERVICES     **PeiServices
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                               Status;
 | 
						|
  EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER  *FtwWorkingBlockHeader;
 | 
						|
  EFI_FAULT_TOLERANT_WRITE_HEADER          *FtwLastWriteHeader;
 | 
						|
  EFI_FAULT_TOLERANT_WRITE_RECORD          *FtwLastWriteRecord;
 | 
						|
  EFI_PHYSICAL_ADDRESS                     WorkSpaceAddress;
 | 
						|
  UINTN                                    WorkSpaceLength;
 | 
						|
  EFI_PHYSICAL_ADDRESS                     SpareAreaAddress;
 | 
						|
  UINTN                                    SpareAreaLength;
 | 
						|
  EFI_PHYSICAL_ADDRESS                     WorkSpaceInSpareArea;
 | 
						|
  UINT64                                   Size;
 | 
						|
  FAULT_TOLERANT_WRITE_LAST_WRITE_DATA     FtwLastWrite;
 | 
						|
 | 
						|
  FtwWorkingBlockHeader = NULL;
 | 
						|
  FtwLastWriteHeader    = NULL;
 | 
						|
  FtwLastWriteRecord    = NULL;
 | 
						|
 | 
						|
  SpareAreaAddress = 0;
 | 
						|
  SpareAreaLength  = 0;
 | 
						|
  WorkSpaceAddress = 0;
 | 
						|
  WorkSpaceLength  = 0;
 | 
						|
 | 
						|
  Status = GetVariableFlashFtwWorkingInfo (&WorkSpaceAddress, &Size);
 | 
						|
  ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
  Status = SafeUint64ToUintn (Size, &WorkSpaceLength);
 | 
						|
  // This driver currently assumes the size will be UINTN so assert the value is safe for now.
 | 
						|
  ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
  Status = GetVariableFlashFtwSpareInfo (&SpareAreaAddress, &Size);
 | 
						|
  ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
  Status = SafeUint64ToUintn (Size, &SpareAreaLength);
 | 
						|
  // This driver currently assumes the size will be UINTN so assert the value is safe for now.
 | 
						|
  ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
  //
 | 
						|
  // The address of FTW working base and spare base must not be 0.
 | 
						|
  //
 | 
						|
  ASSERT ((WorkSpaceAddress != 0) && (SpareAreaAddress != 0));
 | 
						|
 | 
						|
  FtwWorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *)(UINTN)WorkSpaceAddress;
 | 
						|
  if (IsValidWorkSpace (FtwWorkingBlockHeader, WorkSpaceLength)) {
 | 
						|
    Status = FtwGetLastWriteHeader (
 | 
						|
               FtwWorkingBlockHeader,
 | 
						|
               WorkSpaceLength,
 | 
						|
               &FtwLastWriteHeader
 | 
						|
               );
 | 
						|
    if (!EFI_ERROR (Status)) {
 | 
						|
      Status = FtwGetLastWriteRecord (
 | 
						|
                 FtwLastWriteHeader,
 | 
						|
                 &FtwLastWriteRecord
 | 
						|
                 );
 | 
						|
    }
 | 
						|
 | 
						|
    if (!EFI_ERROR (Status)) {
 | 
						|
      ASSERT (FtwLastWriteRecord != NULL);
 | 
						|
      if ((FtwLastWriteRecord->SpareComplete == FTW_VALID_STATE) && (FtwLastWriteRecord->DestinationComplete != FTW_VALID_STATE)) {
 | 
						|
        //
 | 
						|
        // If FTW last write was still in progress with SpareComplete set and DestinationComplete not set.
 | 
						|
        // It means the target buffer has been backed up in spare block, then target block has been erased,
 | 
						|
        // but the target buffer has not been writen in target block from spare block, we need to build
 | 
						|
        // FAULT_TOLERANT_WRITE_LAST_WRITE_DATA GUID hob to hold the FTW last write data.
 | 
						|
        //
 | 
						|
        FtwLastWrite.TargetAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)((INT64)SpareAreaAddress + FtwLastWriteRecord->RelativeOffset);
 | 
						|
        FtwLastWrite.SpareAddress  = SpareAreaAddress;
 | 
						|
        FtwLastWrite.Length        = SpareAreaLength;
 | 
						|
        DEBUG ((
 | 
						|
          DEBUG_INFO,
 | 
						|
          "FtwPei last write data: TargetAddress - 0x%x SpareAddress - 0x%x Length - 0x%x\n",
 | 
						|
          (UINTN)FtwLastWrite.TargetAddress,
 | 
						|
          (UINTN)FtwLastWrite.SpareAddress,
 | 
						|
          (UINTN)FtwLastWrite.Length
 | 
						|
          ));
 | 
						|
        BuildGuidDataHob (&gEdkiiFaultTolerantWriteGuid, (VOID *)&FtwLastWrite, sizeof (FAULT_TOLERANT_WRITE_LAST_WRITE_DATA));
 | 
						|
      }
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    FtwWorkingBlockHeader = NULL;
 | 
						|
    //
 | 
						|
    // If the working block workspace is not valid, try to find workspace in the spare block.
 | 
						|
    //
 | 
						|
    WorkSpaceInSpareArea = SpareAreaAddress + SpareAreaLength - WorkSpaceLength;
 | 
						|
    while (WorkSpaceInSpareArea >= SpareAreaAddress) {
 | 
						|
      if (CompareGuid (&gEdkiiWorkingBlockSignatureGuid, (EFI_GUID *)(UINTN)WorkSpaceInSpareArea)) {
 | 
						|
        //
 | 
						|
        // Found the workspace.
 | 
						|
        //
 | 
						|
        DEBUG ((DEBUG_INFO, "FtwPei: workspace in spare block is at 0x%x.\n", (UINTN)WorkSpaceInSpareArea));
 | 
						|
        FtwWorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *)(UINTN)WorkSpaceInSpareArea;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      WorkSpaceInSpareArea = WorkSpaceInSpareArea - sizeof (EFI_GUID);
 | 
						|
    }
 | 
						|
 | 
						|
    if ((FtwWorkingBlockHeader != NULL) && IsValidWorkSpace (FtwWorkingBlockHeader, WorkSpaceLength)) {
 | 
						|
      //
 | 
						|
      // It was workspace self reclaim, build FAULT_TOLERANT_WRITE_LAST_WRITE_DATA GUID hob for it.
 | 
						|
      //
 | 
						|
      FtwLastWrite.TargetAddress = WorkSpaceAddress - (WorkSpaceInSpareArea - SpareAreaAddress);
 | 
						|
      FtwLastWrite.SpareAddress  = SpareAreaAddress;
 | 
						|
      FtwLastWrite.Length        = SpareAreaLength;
 | 
						|
      DEBUG ((
 | 
						|
        DEBUG_INFO,
 | 
						|
        "FtwPei last write data: TargetAddress - 0x%x SpareAddress - 0x%x Length - 0x%x\n",
 | 
						|
        (UINTN)FtwLastWrite.TargetAddress,
 | 
						|
        (UINTN)FtwLastWrite.SpareAddress,
 | 
						|
        (UINTN)FtwLastWrite.Length
 | 
						|
        ));
 | 
						|
      BuildGuidDataHob (&gEdkiiFaultTolerantWriteGuid, (VOID *)&FtwLastWrite, sizeof (FAULT_TOLERANT_WRITE_LAST_WRITE_DATA));
 | 
						|
    } else {
 | 
						|
      //
 | 
						|
      // Both are invalid.
 | 
						|
      //
 | 
						|
      DEBUG ((DEBUG_ERROR, "FtwPei: Both working and spare block are invalid.\n"));
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Install gEdkiiFaultTolerantWriteGuid PPI to inform the check for FTW last write data has been done.
 | 
						|
  //
 | 
						|
  return PeiServicesInstallPpi (&mPpiListVariable);
 | 
						|
}
 |