+/** @file\r
+ This driver installs gEdkiiFaultTolerantWriteGuid PPI to inform\r
+ the check for FTW last write data has been done.\r
+\r
+Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials \r
+are licensed and made available under the terms and conditions of the BSD License \r
+which accompanies this distribution. The full text of the license may be found at \r
+http://opensource.org/licenses/bsd-license.php \r
+ \r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
+\r
+**/\r
+\r
+#include <PiPei.h>\r
+\r
+#include <Guid/SystemNvDataGuid.h>\r
+#include <Guid/FaultTolerantWrite.h>\r
+#include <Library/PeiServicesLib.h>\r
+#include <Library/PcdLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/HobLib.h>\r
+\r
+EFI_PEI_PPI_DESCRIPTOR mPpiListVariable = {\r
+ (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),\r
+ &gEdkiiFaultTolerantWriteGuid,\r
+ NULL\r
+};\r
+\r
+/**\r
+ Get the last Write Header pointer.\r
+ The last write header is the header whose 'complete' state hasn't been set.\r
+ After all, this header may be a EMPTY header entry for next Allocate.\r
+\r
+\r
+ @param FtwWorkSpaceHeader Pointer of the working block header\r
+ @param FtwWorkSpaceSize Size of the work space\r
+ @param FtwWriteHeader Pointer to retrieve the last write header\r
+\r
+ @retval EFI_SUCCESS Get the last write record successfully\r
+ @retval EFI_ABORTED The FTW work space is damaged\r
+\r
+**/\r
+EFI_STATUS\r
+FtwGetLastWriteHeader (\r
+ IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *FtwWorkSpaceHeader,\r
+ IN UINTN FtwWorkSpaceSize,\r
+ OUT EFI_FAULT_TOLERANT_WRITE_HEADER **FtwWriteHeader\r
+ )\r
+{\r
+ UINTN Offset;\r
+ EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader;\r
+\r
+ *FtwWriteHeader = NULL;\r
+ FtwHeader = (EFI_FAULT_TOLERANT_WRITE_HEADER *) (FtwWorkSpaceHeader + 1);\r
+ Offset = sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER);\r
+\r
+ while (FtwHeader->Complete == FTW_VALID_STATE) {\r
+ Offset += FTW_WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites, FtwHeader->PrivateDataSize);\r
+ //\r
+ // If Offset exceed the FTW work space boudary, return error.\r
+ //\r
+ if (Offset >= FtwWorkSpaceSize) {\r
+ *FtwWriteHeader = FtwHeader;\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ FtwHeader = (EFI_FAULT_TOLERANT_WRITE_HEADER *) ((UINT8 *) FtwWorkSpaceHeader + Offset);\r
+ }\r
+ //\r
+ // Last write header is found\r
+ //\r
+ *FtwWriteHeader = FtwHeader;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Get the last Write Record pointer. The last write Record is the Record\r
+ whose DestinationCompleted state hasn't been set. After all, this Record\r
+ may be a EMPTY record entry for next write.\r
+\r
+\r
+ @param FtwWriteHeader Pointer to the write record header\r
+ @param FtwWriteRecord Pointer to retrieve the last write record\r
+\r
+ @retval EFI_SUCCESS Get the last write record successfully\r
+ @retval EFI_ABORTED The FTW work space is damaged\r
+\r
+**/\r
+EFI_STATUS\r
+FtwGetLastWriteRecord (\r
+ IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwWriteHeader,\r
+ OUT EFI_FAULT_TOLERANT_WRITE_RECORD **FtwWriteRecord\r
+ )\r
+{\r
+ UINTN Index;\r
+ EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord;\r
+\r
+ *FtwWriteRecord = NULL;\r
+ FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) (FtwWriteHeader + 1);\r
+\r
+ //\r
+ // Try to find the last write record "that has not completed"\r
+ //\r
+ for (Index = 0; Index < FtwWriteHeader->NumberOfWrites; Index += 1) {\r
+ if (FtwRecord->DestinationComplete != FTW_VALID_STATE) {\r
+ //\r
+ // The last write record is found\r
+ //\r
+ *FtwWriteRecord = FtwRecord;\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ FtwRecord++;\r
+\r
+ if (FtwWriteHeader->PrivateDataSize != 0) {\r
+ FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) ((UINTN) FtwRecord + (UINTN) FtwWriteHeader->PrivateDataSize);\r
+ }\r
+ }\r
+ //\r
+ // if Index == NumberOfWrites, then\r
+ // the last record has been written successfully,\r
+ // but the Header->Complete Flag has not been set.\r
+ // also return the last record.\r
+ //\r
+ if (Index == FtwWriteHeader->NumberOfWrites) {\r
+ *FtwWriteRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) ((UINTN) FtwRecord - FTW_RECORD_SIZE (FtwWriteHeader->PrivateDataSize));\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ return EFI_ABORTED;\r
+}\r
+\r
+/**\r
+ Check to see if it is a valid work space.\r
+\r
+\r
+ @param WorkingHeader Pointer of working block header\r
+ @param WorkingLength Working block length\r
+\r
+ @retval TRUE The work space is valid.\r
+ @retval FALSE The work space is invalid.\r
+\r
+**/\r
+BOOLEAN\r
+IsValidWorkSpace (\r
+ IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader,\r
+ IN UINTN WorkingLength\r
+ )\r
+{\r
+ UINT8 Data;\r
+\r
+ if (WorkingHeader == NULL) {\r
+ return FALSE;\r
+ }\r
+\r
+ if ((WorkingHeader->WorkingBlockValid != FTW_VALID_STATE) || (WorkingHeader->WorkingBlockInvalid == FTW_VALID_STATE)) {\r
+ DEBUG ((EFI_D_ERROR, "FtwPei: Work block header valid bit check error\n"));\r
+ return FALSE;\r
+ }\r
+\r
+ if (WorkingHeader->WriteQueueSize != (WorkingLength - sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER))) {\r
+ DEBUG ((EFI_D_ERROR, "FtwPei: Work block header WriteQueueSize check error\n"));\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Check signature with gEdkiiWorkingBlockSignatureGuid\r
+ //\r
+ if (!CompareGuid (&gEdkiiWorkingBlockSignatureGuid, &WorkingHeader->Signature)) {\r
+ DEBUG ((EFI_D_ERROR, "FtwPei: Work block header signature check error, it should be gEdkiiWorkingBlockSignatureGuid\n"));\r
+ //\r
+ // To be compatible with old signature gEfiSystemNvDataFvGuid.\r
+ //\r
+ if (!CompareGuid (&gEfiSystemNvDataFvGuid, &WorkingHeader->Signature)) {\r
+ return FALSE;\r
+ } else {\r
+ Data = *(UINT8 *) (WorkingHeader + 1);\r
+ if (Data != 0xff) {\r
+ DEBUG ((EFI_D_ERROR, "FtwPei: Old format FTW structure can't be handled\n"));\r
+ ASSERT (FALSE);\r
+ return FALSE;\r
+ }\r
+ }\r
+ }\r
+\r
+ return TRUE;\r
+\r
+}\r
+\r
+/**\r
+ Main entry for Fault Tolerant Write PEIM.\r
+\r
+ @param[in] FileHandle Handle of the file being invoked.\r
+ @param[in] PeiServices Pointer to PEI Services table.\r
+\r
+ @retval EFI_SUCCESS If the interface could be successfully installed\r
+ @retval Others Returned from PeiServicesInstallPpi()\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PeimFaultTolerantWriteInitialize (\r
+ IN EFI_PEI_FILE_HANDLE FileHandle,\r
+ IN CONST EFI_PEI_SERVICES **PeiServices\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *FtwWorkingBlockHeader;\r
+ EFI_FAULT_TOLERANT_WRITE_HEADER *FtwLastWriteHeader;\r
+ EFI_FAULT_TOLERANT_WRITE_RECORD *FtwLastWriteRecord;\r
+ EFI_PHYSICAL_ADDRESS WorkSpaceAddress;\r
+ UINTN WorkSpaceLength;\r
+ EFI_PHYSICAL_ADDRESS SpareAreaAddress;\r
+ UINTN SpareAreaLength;\r
+ EFI_PHYSICAL_ADDRESS WorkSpaceInSpareArea;\r
+ FAULT_TOLERANT_WRITE_LAST_WRITE_DATA FtwLastWrite;\r
+\r
+ FtwWorkingBlockHeader = NULL;\r
+ FtwLastWriteHeader = NULL;\r
+ FtwLastWriteRecord = NULL;\r
+\r
+ WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageFtwWorkingBase64);\r
+ if (WorkSpaceAddress == 0) {\r
+ WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwWorkingBase);\r
+ }\r
+ WorkSpaceLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwWorkingSize);\r
+\r
+ SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageFtwSpareBase64);\r
+ if (SpareAreaAddress == 0) {\r
+ SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwSpareBase);\r
+ }\r
+ SpareAreaLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwSpareSize);\r
+\r
+ //\r
+ // The address of FTW working base and spare base must not be 0.\r
+ //\r
+ ASSERT ((WorkSpaceAddress != 0) && (SpareAreaAddress != 0));\r
+\r
+ FtwWorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (UINTN) WorkSpaceAddress;\r
+ if (IsValidWorkSpace (FtwWorkingBlockHeader, WorkSpaceLength)) {\r
+ Status = FtwGetLastWriteHeader (\r
+ FtwWorkingBlockHeader,\r
+ WorkSpaceLength,\r
+ &FtwLastWriteHeader\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = FtwGetLastWriteRecord (\r
+ FtwLastWriteHeader,\r
+ &FtwLastWriteRecord\r
+ );\r
+ }\r
+\r
+ if (!EFI_ERROR (Status) && ((FtwLastWriteRecord->SpareComplete == FTW_VALID_STATE) && (FtwLastWriteRecord->DestinationComplete != FTW_VALID_STATE))) {\r
+ //\r
+ // If FTW last write was still in progress with SpareComplete set and DestinationComplete not set.\r
+ // It means the target buffer has been backed up in spare block, then target block has been erased,\r
+ // but the target buffer has not been writen in target block from spare block, we need to build\r
+ // FAULT_TOLERANT_WRITE_LAST_WRITE_DATA GUID hob to hold the FTW last write data.\r
+ //\r
+ FtwLastWrite.TargetAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) ((INT64) SpareAreaAddress + FtwLastWriteRecord->RelativeOffset);\r
+ FtwLastWrite.SpareAddress = SpareAreaAddress;\r
+ FtwLastWrite.Length = SpareAreaLength;\r
+ DEBUG ((\r
+ EFI_D_INFO,\r
+ "FtwPei last write data: TargetAddress - 0x%x SpareAddress - 0x%x Length - 0x%x\n",\r
+ (UINTN) FtwLastWrite.TargetAddress,\r
+ (UINTN) FtwLastWrite.SpareAddress,\r
+ (UINTN) FtwLastWrite.Length));\r
+ BuildGuidDataHob (&gEdkiiFaultTolerantWriteGuid, (VOID *) &FtwLastWrite, sizeof (FAULT_TOLERANT_WRITE_LAST_WRITE_DATA));\r
+ }\r
+ } else {\r
+ FtwWorkingBlockHeader = NULL;\r
+ //\r
+ // If the working block workspace is not valid, try to find workspace in the spare block.\r
+ //\r
+ WorkSpaceInSpareArea = SpareAreaAddress + SpareAreaLength - WorkSpaceLength;\r
+ while (WorkSpaceInSpareArea >= SpareAreaAddress) {\r
+ if (CompareGuid (&gEdkiiWorkingBlockSignatureGuid, (EFI_GUID *) (UINTN) WorkSpaceInSpareArea)) {\r
+ //\r
+ // Found the workspace.\r
+ //\r
+ DEBUG ((EFI_D_INFO, "FtwPei: workspace in spare block is at 0x%x.\n", (UINTN) WorkSpaceInSpareArea));\r
+ FtwWorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (UINTN) WorkSpaceInSpareArea;\r
+ break;\r
+ }\r
+ WorkSpaceInSpareArea = WorkSpaceInSpareArea - sizeof (EFI_GUID);\r
+ }\r
+ if ((FtwWorkingBlockHeader != NULL) && IsValidWorkSpace (FtwWorkingBlockHeader, WorkSpaceLength)) {\r
+ //\r
+ // It was workspace self reclaim, build FAULT_TOLERANT_WRITE_LAST_WRITE_DATA GUID hob for it.\r
+ //\r
+ FtwLastWrite.TargetAddress = WorkSpaceAddress - (WorkSpaceInSpareArea - SpareAreaAddress);\r
+ FtwLastWrite.SpareAddress = SpareAreaAddress;\r
+ FtwLastWrite.Length = SpareAreaLength;\r
+ DEBUG ((\r
+ EFI_D_INFO,\r
+ "FtwPei last write data: TargetAddress - 0x%x SpareAddress - 0x%x Length - 0x%x\n",\r
+ (UINTN) FtwLastWrite.TargetAddress,\r
+ (UINTN) FtwLastWrite.SpareAddress,\r
+ (UINTN) FtwLastWrite.Length));\r
+ BuildGuidDataHob (&gEdkiiFaultTolerantWriteGuid, (VOID *) &FtwLastWrite, sizeof (FAULT_TOLERANT_WRITE_LAST_WRITE_DATA));\r
+ } else {\r
+ //\r
+ // Both are invalid.\r
+ //\r
+ DEBUG ((EFI_D_ERROR, "FtwPei: Both working and spare block are invalid.\n"));\r
+ }\r
+ }\r
+\r
+ //\r
+ // Install gEdkiiFaultTolerantWriteGuid PPI to inform the check for FTW last write data has been done.\r
+ //\r
+ return PeiServicesInstallPpi (&mPpiListVariable);\r
+}\r