--- /dev/null
+/*++\r
+\r
+Copyright (c) 2006 - 2007, Intel Corporation \r
+All rights reserved. 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
+Module Name:\r
+\r
+ FtwLite.c\r
+\r
+Abstract:\r
+\r
+ This is a simple fault tolerant write driver, based on PlatformFd library.\r
+ And it only supports write BufferSize <= SpareAreaLength.\r
+\r
+ This boot service only protocol provides fault tolerant write capability for \r
+ block devices. The protocol has internal non-volatile intermediate storage \r
+ of the data and private information. It should be able to recover \r
+ automatically from a critical fault, such as power failure. \r
+\r
+Notes:\r
+\r
+ The implementation uses an FTW Lite (Fault Tolerant Write) Work Space. \r
+ This work space is a memory copy of the work space on the Woring Block,\r
+ the size of the work space is the FTW_WORK_SPACE_SIZE bytes.\r
+\r
+--*/\r
+\r
+#include <FtwLite.h>\r
+\r
+//\r
+// In write function, we should check the target range to prevent the user\r
+// from writing Spare block and Working space directly.\r
+//\r
+//\r
+// Fault Tolerant Write Protocol API\r
+//\r
+EFI_STATUS\r
+EFIAPI\r
+FtwLiteWrite (\r
+ IN EFI_FTW_LITE_PROTOCOL *This,\r
+ IN EFI_HANDLE FvbHandle,\r
+ IN EFI_LBA Lba,\r
+ IN UINTN Offset,\r
+ IN OUT UINTN *NumBytes,\r
+ IN VOID *Buffer\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+ Starts a target block update. This function will record data about write \r
+ in fault tolerant storage and will complete the write in a recoverable \r
+ manner, ensuring at all times that either the original contents or \r
+ the modified contents are available.\r
+\r
+Arguments:\r
+ This - Calling context\r
+ FvbHandle - The handle of FVB protocol that provides services for \r
+ reading, writing, and erasing the target block.\r
+ Lba - The logical block address of the target block. \r
+ Offset - The offset within the target block to place the data.\r
+ NumBytes - The number of bytes to write to the target block.\r
+ Buffer - The data to write.\r
+\r
+Returns:\r
+ EFI_SUCCESS - The function completed successfully\r
+ EFI_BAD_BUFFER_SIZE - The write would span a target block, which is not \r
+ a valid action.\r
+ EFI_ACCESS_DENIED - No writes have been allocated.\r
+ EFI_NOT_FOUND - Cannot find FVB by handle.\r
+ EFI_OUT_OF_RESOURCES - Cannot allocate memory.\r
+ EFI_ABORTED - The function could not complete successfully.\r
+\r
+--*/\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_FTW_LITE_DEVICE *FtwLiteDevice;\r
+ EFI_FTW_LITE_RECORD *Record;\r
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;\r
+ EFI_PHYSICAL_ADDRESS FvbPhysicalAddress;\r
+ UINTN MyLength;\r
+ UINTN MyOffset;\r
+ UINTN MyBufferSize;\r
+ UINT8 *MyBuffer;\r
+ UINTN SpareBufferSize;\r
+ UINT8 *SpareBuffer;\r
+ UINTN Index;\r
+ UINT8 *Ptr;\r
+ EFI_DEV_PATH_PTR DevPtr;\r
+\r
+ //\r
+ // Refresh work space and get last record\r
+ //\r
+ FtwLiteDevice = FTW_LITE_CONTEXT_FROM_THIS (This);\r
+ Status = WorkSpaceRefresh (FtwLiteDevice);\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ Record = FtwLiteDevice->FtwLastRecord;\r
+\r
+ //\r
+ // Check the flags of last write record\r
+ //\r
+ if ((Record->WriteAllocated == FTW_VALID_STATE) || (Record->SpareCompleted == FTW_VALID_STATE)) {\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+ //\r
+ // IF former record has completed, THEN use next record\r
+ //\r
+ if (Record->WriteCompleted == FTW_VALID_STATE) {\r
+ Record++;\r
+ FtwLiteDevice->FtwLastRecord = Record;\r
+ }\r
+\r
+ MyOffset = (UINT8 *) Record - FtwLiteDevice->FtwWorkSpace;\r
+\r
+ //\r
+ // Check if the input data can fit within the target block\r
+ //\r
+ if ((Offset +*NumBytes) > FtwLiteDevice->SpareAreaLength) {\r
+ return EFI_BAD_BUFFER_SIZE;\r
+ }\r
+ //\r
+ // Check if there is enough free space for allocate a record\r
+ //\r
+ if ((MyOffset + WRITE_TOTAL_SIZE) > FtwLiteDevice->FtwWorkSpaceSize) {\r
+ Status = FtwReclaimWorkSpace (FtwLiteDevice);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "FtwLite: Reclaim work space - %r", Status));\r
+ return EFI_ABORTED;\r
+ }\r
+ }\r
+ //\r
+ // Get the FVB protocol by handle\r
+ //\r
+ Status = FtwGetFvbByHandle (FvbHandle, &Fvb);\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ //\r
+ // Allocate a write record in workspace.\r
+ // Update Header->WriteAllocated as VALID\r
+ //\r
+ Status = FtwUpdateFvState (\r
+ FtwLiteDevice->FtwFvBlock,\r
+ FtwLiteDevice->FtwWorkSpaceLba,\r
+ FtwLiteDevice->FtwWorkSpaceBase + MyOffset,\r
+ WRITE_ALLOCATED\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_FTW_LITE, "FtwLite: Allocate record - %r\n", Status));\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ Record->WriteAllocated = FTW_VALID_STATE;\r
+\r
+ //\r
+ // Prepare data of write record, filling DevPath with memory mapped address.\r
+ //\r
+ DevPtr.MemMap = (MEMMAP_DEVICE_PATH *) &Record->DevPath;\r
+ DevPtr.MemMap->Header.Type = HARDWARE_DEVICE_PATH;\r
+ DevPtr.MemMap->Header.SubType = HW_MEMMAP_DP;\r
+ SetDevicePathNodeLength (&DevPtr.MemMap->Header, sizeof (MEMMAP_DEVICE_PATH));\r
+\r
+ Status = Fvb->GetPhysicalAddress (Fvb, &FvbPhysicalAddress);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_FTW_LITE, "FtwLite: Get FVB physical address - %r\n", Status));\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ DevPtr.MemMap->MemoryType = EfiMemoryMappedIO;\r
+ DevPtr.MemMap->StartingAddress = FvbPhysicalAddress;\r
+ DevPtr.MemMap->EndingAddress = FvbPhysicalAddress +*NumBytes;\r
+ //\r
+ // ignored!\r
+ //\r
+ Record->Lba = Lba;\r
+ Record->Offset = Offset;\r
+ Record->NumBytes = *NumBytes;\r
+\r
+ //\r
+ // Write the record to the work space.\r
+ //\r
+ MyOffset = (UINT8 *) Record - FtwLiteDevice->FtwWorkSpace;\r
+ MyLength = FTW_LITE_RECORD_SIZE;\r
+\r
+ Status = FtwLiteDevice->FtwFvBlock->Write (\r
+ FtwLiteDevice->FtwFvBlock,\r
+ FtwLiteDevice->FtwWorkSpaceLba,\r
+ FtwLiteDevice->FtwWorkSpaceBase + MyOffset,\r
+ &MyLength,\r
+ (UINT8 *) Record\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_ABORTED;\r
+ }\r
+ //\r
+ // Record has been written to working block, then write data.\r
+ //\r
+ //\r
+ // Allocate a memory buffer\r
+ //\r
+ MyBufferSize = FtwLiteDevice->SpareAreaLength;\r
+ MyBuffer = AllocatePool (MyBufferSize);\r
+ if (MyBuffer == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ //\r
+ // Starting at Lba, if the number of the rest blocks on Fvb is less\r
+ // than NumberOfSpareBlock.\r
+ //\r
+ //\r
+ // Read all original data from target block to memory buffer\r
+ //\r
+ if (IsInWorkingBlock (FtwLiteDevice, Fvb, Lba)) {\r
+ //\r
+ // If target block falls into working block, we must follow the process of\r
+ // updating working block.\r
+ //\r
+ Ptr = MyBuffer;\r
+ for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {\r
+ MyLength = FtwLiteDevice->SizeOfSpareBlock;\r
+ Status = FtwLiteDevice->FtwFvBlock->Read (\r
+ FtwLiteDevice->FtwFvBlock,\r
+ FtwLiteDevice->FtwWorkBlockLba + Index,\r
+ 0,\r
+ &MyLength,\r
+ Ptr\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (MyBuffer);\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ Ptr += MyLength;\r
+ }\r
+ //\r
+ // Update Offset by adding the offset from the start LBA of working block to\r
+ // the target LBA. The target block can not span working block!\r
+ //\r
+ Offset = (((UINTN) (Lba - FtwLiteDevice->FtwWorkBlockLba)) * FtwLiteDevice->SizeOfSpareBlock + Offset);\r
+ ASSERT ((Offset +*NumBytes) <= FtwLiteDevice->SpareAreaLength);\r
+\r
+ } else {\r
+\r
+ Ptr = MyBuffer;\r
+ for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {\r
+ MyLength = FtwLiteDevice->SizeOfSpareBlock;\r
+ Status = Fvb->Read (Fvb, Lba + Index, 0, &MyLength, Ptr);\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (MyBuffer);\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ Ptr += MyLength;\r
+ }\r
+ }\r
+ //\r
+ // Overwrite the updating range data with\r
+ // the input buffer content\r
+ //\r
+ CopyMem (MyBuffer + Offset, Buffer, *NumBytes);\r
+\r
+ //\r
+ // Try to keep the content of spare block\r
+ // Save spare block into a spare backup memory buffer (Sparebuffer)\r
+ //\r
+ SpareBufferSize = FtwLiteDevice->SpareAreaLength;\r
+ SpareBuffer = AllocatePool (SpareBufferSize);\r
+ if (SpareBuffer == NULL) {\r
+ FreePool (MyBuffer);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Ptr = SpareBuffer;\r
+ for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {\r
+ MyLength = FtwLiteDevice->SizeOfSpareBlock;\r
+ Status = FtwLiteDevice->FtwBackupFvb->Read (\r
+ FtwLiteDevice->FtwBackupFvb,\r
+ FtwLiteDevice->FtwSpareLba + Index,\r
+ 0,\r
+ &MyLength,\r
+ Ptr\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (MyBuffer);\r
+ FreePool (SpareBuffer);\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ Ptr += MyLength;\r
+ }\r
+ //\r
+ // Write the memory buffer to spare block\r
+ // Don't forget to erase Flash first.\r
+ //\r
+ Status = FtwEraseSpareBlock (FtwLiteDevice);\r
+ Ptr = MyBuffer;\r
+ for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {\r
+ MyLength = FtwLiteDevice->SizeOfSpareBlock;\r
+ Status = FtwLiteDevice->FtwBackupFvb->Write (\r
+ FtwLiteDevice->FtwBackupFvb,\r
+ FtwLiteDevice->FtwSpareLba + Index,\r
+ 0,\r
+ &MyLength,\r
+ Ptr\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (MyBuffer);\r
+ FreePool (SpareBuffer);\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ Ptr += MyLength;\r
+ }\r
+ //\r
+ // Free MyBuffer\r
+ //\r
+ FreePool (MyBuffer);\r
+\r
+ //\r
+ // Set the SpareCompleteD in the FTW record,\r
+ //\r
+ MyOffset = (UINT8 *) Record - FtwLiteDevice->FtwWorkSpace;\r
+ Status = FtwUpdateFvState (\r
+ FtwLiteDevice->FtwFvBlock,\r
+ FtwLiteDevice->FtwWorkSpaceLba,\r
+ FtwLiteDevice->FtwWorkSpaceBase + MyOffset,\r
+ SPARE_COMPLETED\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (SpareBuffer);\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ Record->SpareCompleted = FTW_VALID_STATE;\r
+\r
+ //\r
+ // Since the content has already backuped in spare block, the write is\r
+ // guaranteed to be completed with fault tolerant manner.\r
+ //\r
+ Status = FtwWriteRecord (FtwLiteDevice, Fvb);\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (SpareBuffer);\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ Record++;\r
+ FtwLiteDevice->FtwLastRecord = Record;\r
+\r
+ //\r
+ // Restore spare backup buffer into spare block , if no failure happened during FtwWrite.\r
+ //\r
+ Status = FtwEraseSpareBlock (FtwLiteDevice);\r
+ Ptr = SpareBuffer;\r
+ for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {\r
+ MyLength = FtwLiteDevice->SizeOfSpareBlock;\r
+ Status = FtwLiteDevice->FtwBackupFvb->Write (\r
+ FtwLiteDevice->FtwBackupFvb,\r
+ FtwLiteDevice->FtwSpareLba + Index,\r
+ 0,\r
+ &MyLength,\r
+ Ptr\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (SpareBuffer);\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ Ptr += MyLength;\r
+ }\r
+ //\r
+ // All success.\r
+ //\r
+ FreePool (SpareBuffer);\r
+\r
+ DEBUG (\r
+ (EFI_D_FTW_LITE,\r
+ "FtwLite: Write() success, (Lba:Offset)=(%lx:0x%x), NumBytes: 0x%x\n",\r
+ Lba,\r
+ Offset,\r
+ *NumBytes)\r
+ );\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+EFI_STATUS\r
+FtwWriteRecord (\r
+ IN EFI_FTW_LITE_DEVICE *FtwLiteDevice,\r
+ IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+ Write a record with fault tolerant mannaer.\r
+ Since the content has already backuped in spare block, the write is \r
+ guaranteed to be completed with fault tolerant manner.\r
+ \r
+Arguments:\r
+ FtwLiteDevice - The private data of FTW_LITE driver\r
+ Fvb - The FVB protocol that provides services for \r
+ reading, writing, and erasing the target block.\r
+\r
+Returns:\r
+ EFI_SUCCESS - The function completed successfully\r
+ EFI_ABORTED - The function could not complete successfully\r
+\r
+--*/\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_FTW_LITE_RECORD *Record;\r
+ EFI_LBA WorkSpaceLbaOffset; \r
+ UINTN Offset;\r
+\r
+ //\r
+ // Spare Complete but Destination not complete,\r
+ // Recover the targt block with the spare block.\r
+ //\r
+ Record = FtwLiteDevice->FtwLastRecord;\r
+\r
+ //\r
+ // IF target block is working block, THEN Flush Spare Block To Working Block;\r
+ // ELSE IF target block is boot block, THEN Flush Spare Block To boot Block;\r
+ // ELSE flush spare block to normal target block.ENDIF\r
+ //\r
+ if (IsInWorkingBlock (FtwLiteDevice, Fvb, Record->Lba)) {\r
+ //\r
+ // If target block is working block, Attention:\r
+ // it's required to set SPARE_COMPLETED to spare block.\r
+ //\r
+ WorkSpaceLbaOffset = FtwLiteDevice->FtwWorkSpaceLba - FtwLiteDevice->FtwWorkBlockLba;\r
+ Offset = (UINT8 *) Record - FtwLiteDevice->FtwWorkSpace;\r
+ Status = FtwUpdateFvState (\r
+ FtwLiteDevice->FtwBackupFvb,\r
+ FtwLiteDevice->FtwSpareLba + WorkSpaceLbaOffset,\r
+ FtwLiteDevice->FtwWorkSpaceBase + Offset,\r
+ SPARE_COMPLETED\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ Status = FlushSpareBlockToWorkingBlock (FtwLiteDevice);\r
+ } else if (IsBootBlock (FtwLiteDevice, Fvb, Record->Lba)) {\r
+ //\r
+ // Update boot block\r
+ //\r
+ Status = FlushSpareBlockToBootBlock (FtwLiteDevice);\r
+ } else {\r
+ //\r
+ // Update blocks other than working block or boot block\r
+ //\r
+ Status = FlushSpareBlockToTargetBlock (FtwLiteDevice, Fvb, Record->Lba);\r
+ }\r
+\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ //\r
+ // Set WriteCompleted flag in record\r
+ //\r
+ Offset = (UINT8 *) Record - FtwLiteDevice->FtwWorkSpace;\r
+ Status = FtwUpdateFvState (\r
+ FtwLiteDevice->FtwFvBlock,\r
+ FtwLiteDevice->FtwWorkSpaceLba,\r
+ FtwLiteDevice->FtwWorkSpaceBase + Offset,\r
+ WRITE_COMPLETED\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ Record->WriteCompleted = FTW_VALID_STATE;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+EFI_STATUS\r
+FtwRestart (\r
+ IN EFI_FTW_LITE_DEVICE *FtwLiteDevice\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+ Restarts a previously interrupted write. The caller must provide the \r
+ block protocol needed to complete the interrupted write.\r
+ \r
+Arguments:\r
+ FtwLiteDevice - The private data of FTW_LITE driver\r
+ FvbHandle - The handle of FVB protocol that provides services for \r
+ reading, writing, and erasing the target block.\r
+\r
+Returns:\r
+ EFI_SUCCESS - The function completed successfully\r
+ EFI_ACCESS_DENIED - No pending writes exist\r
+ EFI_NOT_FOUND - FVB protocol not found by the handle\r
+ EFI_ABORTED - The function could not complete successfully\r
+\r
+--*/\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_FTW_LITE_RECORD *Record;\r
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;\r
+ EFI_DEV_PATH_PTR DevPathPtr;\r
+\r
+ //\r
+ // Spare Completed but Destination not complete,\r
+ // Recover the targt block with the spare block.\r
+ //\r
+ Record = FtwLiteDevice->FtwLastRecord;\r
+\r
+ //\r
+ // Only support memory mapped FVB device path by now.\r
+ //\r
+ DevPathPtr.MemMap = (MEMMAP_DEVICE_PATH *) &Record->DevPath;\r
+ if (!((DevPathPtr.MemMap->Header.Type == HARDWARE_DEVICE_PATH) && (DevPathPtr.MemMap->Header.SubType == HW_MEMMAP_DP))\r
+ ) {\r
+ DEBUG ((EFI_D_FTW_LITE, "FtwLite: FVB Device Path is not memory mapped\n"));\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ Status = GetFvbByAddress (DevPathPtr.MemMap->StartingAddress, &Fvb);\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ //\r
+ // Since the content has already backuped in spare block, the write is\r
+ // guaranteed to be completed with fault tolerant manner.\r
+ //\r
+ Status = FtwWriteRecord (FtwLiteDevice, Fvb);\r
+ DEBUG ((EFI_D_FTW_INFO, "FtwLite: Restart() - %r\n", Status));\r
+\r
+ Record++;\r
+ FtwLiteDevice->FtwLastRecord = Record;\r
+\r
+ //\r
+ // Erase Spare block\r
+ // This is restart, no need to keep spareblock content.\r
+ //\r
+ FtwEraseSpareBlock (FtwLiteDevice);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+EFI_STATUS\r
+FtwAbort (\r
+ IN EFI_FTW_LITE_DEVICE *FtwLiteDevice\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+ Aborts all previous allocated writes.\r
+\r
+Arguments:\r
+ FtwLiteDevice - The private data of FTW_LITE driver\r
+\r
+Returns:\r
+ EFI_SUCCESS - The function completed successfully\r
+ EFI_ABORTED - The function could not complete successfully.\r
+ EFI_NOT_FOUND - No allocated writes exist.\r
+\r
+--*/\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN Offset;\r
+\r
+ if (FtwLiteDevice->FtwLastRecord->WriteCompleted == FTW_VALID_STATE) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ //\r
+ // Update the complete state of the header as VALID and abort.\r
+ //\r
+ Offset = (UINT8 *) FtwLiteDevice->FtwLastRecord - FtwLiteDevice->FtwWorkSpace;\r
+ Status = FtwUpdateFvState (\r
+ FtwLiteDevice->FtwFvBlock,\r
+ FtwLiteDevice->FtwWorkSpaceLba,\r
+ FtwLiteDevice->FtwWorkSpaceBase + Offset,\r
+ WRITE_COMPLETED\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ FtwLiteDevice->FtwLastRecord->WriteCompleted = FTW_VALID_STATE;\r
+\r
+ Status = FtwGetLastRecord (FtwLiteDevice, &FtwLiteDevice->FtwLastRecord);\r
+\r
+ //\r
+ // Erase the spare block\r
+ //\r
+ Status = FtwEraseSpareBlock (FtwLiteDevice);\r
+\r
+ DEBUG ((EFI_D_FTW_INFO, "FtwLite: Abort() success \n"));\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+InitializeFtwLite (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+/*++\r
+ Routine Description: \r
+ This function is the entry point of the Fault Tolerant Write driver.\r
+ \r
+ Arguments: \r
+ ImageHandle - EFI_HANDLE: A handle for the image that is initializing \r
+ this driver\r
+ SystemTable - EFI_SYSTEM_TABLE: A pointer to the EFI system table\r
+ \r
+ Returns: \r
+ EFI_SUCCESS - FTW has finished the initialization\r
+ EFI_ABORTED - FTW initialization error\r
+\r
+--*/\r
+{\r
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;\r
+ UINTN Index;\r
+ EFI_HANDLE *HandleBuffer;\r
+ UINTN HandleCount;\r
+ EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;\r
+ EFI_PHYSICAL_ADDRESS BaseAddress;\r
+ EFI_FTW_LITE_DEVICE *FtwLiteDevice;\r
+ EFI_FTW_LITE_RECORD *Record;\r
+ UINTN Length;\r
+ EFI_STATUS Status;\r
+ UINTN Offset;\r
+ EFI_FV_BLOCK_MAP_ENTRY *FvbMapEntry;\r
+ UINT32 LbaIndex;\r
+\r
+ //\r
+ // Allocate Private data of this driver,\r
+ // INCLUDING THE FtwWorkSpace[FTW_WORK_SPACE_SIZE].\r
+ //\r
+ FtwLiteDevice = NULL;\r
+ FtwLiteDevice = AllocatePool (sizeof (EFI_FTW_LITE_DEVICE) + FTW_WORK_SPACE_SIZE);\r
+ if (FtwLiteDevice != NULL) {\r
+ Status = EFI_SUCCESS;\r
+ } else {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ ZeroMem (FtwLiteDevice, sizeof (EFI_FTW_LITE_DEVICE));\r
+ FtwLiteDevice->Signature = FTW_LITE_DEVICE_SIGNATURE;\r
+\r
+ //\r
+ // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.\r
+ //\r
+ FtwLiteDevice->FtwWorkSpace = (UINT8 *) (FtwLiteDevice + 1);\r
+ FtwLiteDevice->FtwWorkSpaceSize = FTW_WORK_SPACE_SIZE;\r
+ SetMem (\r
+ FtwLiteDevice->FtwWorkSpace,\r
+ FtwLiteDevice->FtwWorkSpaceSize,\r
+ FTW_ERASED_BYTE\r
+ );\r
+ FtwLiteDevice->FtwWorkSpaceHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) FtwLiteDevice->FtwWorkSpace;\r
+\r
+ FtwLiteDevice->FtwLastRecord = NULL;\r
+\r
+ FtwLiteDevice->WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwWorkingBase);\r
+ FtwLiteDevice->WorkSpaceLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwWorkingSize);\r
+\r
+ FtwLiteDevice->SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwSpareBase);\r
+ FtwLiteDevice->SpareAreaLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwSpareSize);\r
+\r
+ ASSERT ((FtwLiteDevice->WorkSpaceLength != 0) && (FtwLiteDevice->SpareAreaLength != 0));\r
+\r
+ //\r
+ // Locate FVB protocol\r
+ //\r
+ Status = gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gEfiFirmwareVolumeBlockProtocolGuid,\r
+ NULL,\r
+ &HandleCount,\r
+ &HandleBuffer\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ ASSERT (HandleCount > 0);\r
+\r
+ FtwLiteDevice->FtwFvBlock = NULL;\r
+ FtwLiteDevice->FtwBackupFvb = NULL;\r
+ FtwLiteDevice->FtwWorkSpaceLba = (EFI_LBA) (-1);\r
+ FtwLiteDevice->FtwSpareLba = (EFI_LBA) (-1);\r
+ for (Index = 0; Index < HandleCount; Index += 1) {\r
+ Status = gBS->HandleProtocol (\r
+ HandleBuffer[Index],\r
+ &gEfiFirmwareVolumeBlockProtocolGuid,\r
+ (VOID **) &Fvb\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ Status = Fvb->GetPhysicalAddress (Fvb, &BaseAddress);\r
+ if (EFI_ERROR (Status)) {\r
+ continue;\r
+ }\r
+\r
+ FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) BaseAddress);\r
+\r
+ if ((FtwLiteDevice->WorkSpaceAddress >= BaseAddress) &&\r
+ (FtwLiteDevice->WorkSpaceAddress <= (BaseAddress + FwVolHeader->FvLength))\r
+ ) {\r
+ FtwLiteDevice->FtwFvBlock = Fvb;\r
+ //\r
+ // To get the LBA of work space\r
+ //\r
+ if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) {\r
+ //\r
+ // FV may have multiple types of BlockLength\r
+ //\r
+ FvbMapEntry = &FwVolHeader->BlockMap[0];\r
+ while (!((FvbMapEntry->NumBlocks == 0) && (FvbMapEntry->Length == 0))) {\r
+ for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) {\r
+ if (FtwLiteDevice->WorkSpaceAddress < (BaseAddress + FvbMapEntry->Length * LbaIndex)) {\r
+ FtwLiteDevice->FtwWorkSpaceLba = LbaIndex - 1;\r
+ //\r
+ // Get the Work space size and Base(Offset)\r
+ //\r
+ FtwLiteDevice->FtwWorkSpaceSize = FtwLiteDevice->WorkSpaceLength;\r
+ FtwLiteDevice->FtwWorkSpaceBase = (UINTN) (FtwLiteDevice->WorkSpaceAddress - (BaseAddress + FvbMapEntry->Length * (LbaIndex - 1)));\r
+ break;\r
+ }\r
+ }\r
+ //\r
+ // end for\r
+ //\r
+ FvbMapEntry++;\r
+ }\r
+ //\r
+ // end while\r
+ //\r
+ }\r
+ }\r
+\r
+ if ((FtwLiteDevice->SpareAreaAddress >= BaseAddress) &&\r
+ (FtwLiteDevice->SpareAreaAddress <= (BaseAddress + FwVolHeader->FvLength))\r
+ ) {\r
+ FtwLiteDevice->FtwBackupFvb = Fvb;\r
+ //\r
+ // To get the LBA of spare\r
+ //\r
+ if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) {\r
+ //\r
+ // FV may have multiple types of BlockLength\r
+ //\r
+ FvbMapEntry = &FwVolHeader->BlockMap[0];\r
+ while (!((FvbMapEntry->NumBlocks == 0) && (FvbMapEntry->Length == 0))) {\r
+ for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) {\r
+ if (FtwLiteDevice->SpareAreaAddress < (BaseAddress + FvbMapEntry->Length * LbaIndex)) {\r
+ //\r
+ // Get the NumberOfSpareBlock and SizeOfSpareBlock\r
+ //\r
+ FtwLiteDevice->FtwSpareLba = LbaIndex - 1;\r
+ FtwLiteDevice->SizeOfSpareBlock = FvbMapEntry->Length;\r
+ FtwLiteDevice->NumberOfSpareBlock = FtwLiteDevice->SpareAreaLength / FtwLiteDevice->SizeOfSpareBlock;\r
+ //\r
+ // Check the range of spare area to make sure that it's in FV range\r
+ //\r
+ ASSERT ((FtwLiteDevice->FtwSpareLba + FtwLiteDevice->NumberOfSpareBlock) <= FvbMapEntry->NumBlocks);\r
+ break;\r
+ }\r
+ }\r
+\r
+ FvbMapEntry++;\r
+ }\r
+ //\r
+ // end while\r
+ //\r
+ }\r
+ }\r
+ }\r
+ //\r
+ // Calculate the start LBA of working block. Working block is an area which\r
+ // contains working space in its last block and has the same size as spare\r
+ // block, unless there are not enough blocks before the block that contains\r
+ // working space.\r
+ //\r
+ FtwLiteDevice->FtwWorkBlockLba = FtwLiteDevice->FtwWorkSpaceLba - FtwLiteDevice->NumberOfSpareBlock + 1;\r
+ if ((INT64) (FtwLiteDevice->FtwWorkBlockLba) < 0) {\r
+ FtwLiteDevice->FtwWorkBlockLba = 0;\r
+ }\r
+\r
+ if ((FtwLiteDevice->FtwFvBlock == NULL) ||\r
+ (FtwLiteDevice->FtwBackupFvb == NULL) ||\r
+ (FtwLiteDevice->FtwWorkSpaceLba == (EFI_LBA) (-1)) ||\r
+ (FtwLiteDevice->FtwSpareLba == (EFI_LBA) (-1))\r
+ ) {\r
+ DEBUG ((EFI_D_ERROR, "FtwLite: Working or spare FVB not ready\n"));\r
+ ASSERT_EFI_ERROR (Status);\r
+ }\r
+ //\r
+ // Refresh workspace data from working block\r
+ //\r
+ Status = WorkSpaceRefresh (FtwLiteDevice);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ //\r
+ // If the working block workspace is not valid, try the spare block\r
+ //\r
+ if (!IsValidWorkSpace (FtwLiteDevice->FtwWorkSpaceHeader)) {\r
+ DEBUG ((EFI_D_FTW_LITE, "FtwLite: Workspace invalid, read from backup\n"));\r
+ //\r
+ // Read from spare block\r
+ //\r
+ Length = FtwLiteDevice->FtwWorkSpaceSize;\r
+ Status = FtwLiteDevice->FtwBackupFvb->Read (\r
+ FtwLiteDevice->FtwBackupFvb,\r
+ FtwLiteDevice->FtwSpareLba,\r
+ FtwLiteDevice->FtwWorkSpaceBase,\r
+ &Length,\r
+ FtwLiteDevice->FtwWorkSpace\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ //\r
+ // If spare block is valid, then replace working block content.\r
+ //\r
+ if (IsValidWorkSpace (FtwLiteDevice->FtwWorkSpaceHeader)) {\r
+ Status = FlushSpareBlockToWorkingBlock (FtwLiteDevice);\r
+ DEBUG ((EFI_D_FTW_LITE, "FtwLite: Restart working block in Init() - %r\n", Status));\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ FtwAbort (FtwLiteDevice);\r
+ //\r
+ // Refresh work space.\r
+ //\r
+ Status = WorkSpaceRefresh (FtwLiteDevice);\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_ABORTED;\r
+ }\r
+ } else {\r
+ DEBUG ((EFI_D_FTW_LITE, "FtwLite: Both are invalid, init workspace\n"));\r
+ //\r
+ // If both are invalid, then initialize work space.\r
+ //\r
+ SetMem (\r
+ FtwLiteDevice->FtwWorkSpace,\r
+ FtwLiteDevice->FtwWorkSpaceSize,\r
+ FTW_ERASED_BYTE\r
+ );\r
+ InitWorkSpaceHeader (FtwLiteDevice->FtwWorkSpaceHeader);\r
+ //\r
+ // Write to work space on the working block\r
+ //\r
+ Length = FtwLiteDevice->FtwWorkSpaceSize;\r
+ Status = FtwLiteDevice->FtwFvBlock->Write (\r
+ FtwLiteDevice->FtwFvBlock,\r
+ FtwLiteDevice->FtwWorkSpaceLba,\r
+ FtwLiteDevice->FtwWorkSpaceBase,\r
+ &Length,\r
+ FtwLiteDevice->FtwWorkSpace\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_ABORTED;\r
+ }\r
+ }\r
+ }\r
+ //\r
+ // Hook the protocol API\r
+ //\r
+ FtwLiteDevice->FtwLiteInstance.Write = FtwLiteWrite;\r
+\r
+ //\r
+ // Install protocol interface\r
+ //\r
+ Status = gBS->InstallProtocolInterface (\r
+ &FtwLiteDevice->Handle,\r
+ &gEfiFaultTolerantWriteLiteProtocolGuid,\r
+ EFI_NATIVE_INTERFACE,\r
+ &FtwLiteDevice->FtwLiteInstance\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_ABORTED;\r
+ }\r
+ //\r
+ // If (!SpareCompleted) THEN Abort to rollback.\r
+ //\r
+ if ((FtwLiteDevice->FtwLastRecord->WriteAllocated == FTW_VALID_STATE) &&\r
+ (FtwLiteDevice->FtwLastRecord->SpareCompleted != FTW_VALID_STATE)\r
+ ) {\r
+ DEBUG ((EFI_D_FTW_LITE, "FtwLite: Init.. record not SpareCompleted, abort()\n"));\r
+ FtwAbort (FtwLiteDevice);\r
+ }\r
+ //\r
+ // if (SpareCompleted) THEN Restart to fault tolerant write.\r
+ //\r
+ if ((FtwLiteDevice->FtwLastRecord->SpareCompleted == FTW_VALID_STATE) &&\r
+ (FtwLiteDevice->FtwLastRecord->WriteCompleted != FTW_VALID_STATE)\r
+ ) {\r
+\r
+ Status = FtwRestart (FtwLiteDevice);\r
+ DEBUG ((EFI_D_FTW_LITE, "FtwLite: Restart last write - %r\n", Status));\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+ //\r
+ // To check the workspace buffer behind last records is EMPTY or not.\r
+ // If it's not EMPTY, FTW_LITE also need to call reclaim().\r
+ //\r
+ Record = FtwLiteDevice->FtwLastRecord;\r
+ Offset = (UINT8 *) Record - FtwLiteDevice->FtwWorkSpace;\r
+ if (FtwLiteDevice->FtwWorkSpace[Offset] != FTW_ERASED_BYTE) {\r
+ Offset += WRITE_TOTAL_SIZE;\r
+ }\r
+\r
+ if (!IsErasedFlashBuffer (\r
+ FTW_ERASE_POLARITY,\r
+ FtwLiteDevice->FtwWorkSpace + Offset,\r
+ FtwLiteDevice->FtwWorkSpaceSize - Offset\r
+ )) {\r
+ DEBUG ((EFI_D_FTW_LITE, "FtwLite: Workspace is dirty, call reclaim...\n"));\r
+ Status = FtwReclaimWorkSpace (FtwLiteDevice);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_FTW_LITE, "FtwLite: Workspace reclaim - %r\n", Status));\r
+ return EFI_ABORTED;\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r