/** @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.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #include 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); }