--- /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
+Module Name:\r
+\r
+ FtwWorkSpace.c\r
+ \r
+Abstract:\r
+\r
+Revision History\r
+\r
+--*/\r
+\r
+\r
+#include <FtwLite.h>\r
+\r
+BOOLEAN\r
+IsValidWorkSpace (\r
+ IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+ Check to see if it is a valid work space.\r
+\r
+Arguments:\r
+ WorkingHeader - Pointer of working block header \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_FAULT_TOLERANT_WORKING_BLOCK_HEADER WorkingBlockHeader;\r
+\r
+ ASSERT (WorkingHeader != NULL);\r
+ if (WorkingHeader->WorkingBlockValid != FTW_VALID_STATE) {\r
+ return FALSE;\r
+ }\r
+ //\r
+ // Check signature with gEfiSystemNvDataFvGuid\r
+ //\r
+ if (!CompareGuid (&gEfiSystemNvDataFvGuid, &WorkingHeader->Signature)) {\r
+ return FALSE;\r
+ }\r
+ //\r
+ // Check the CRC of header\r
+ //\r
+ CopyMem (\r
+ &WorkingBlockHeader,\r
+ WorkingHeader,\r
+ sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)\r
+ );\r
+\r
+ //\r
+ // Filter out the Crc and State fields\r
+ //\r
+ SetMem (\r
+ &WorkingBlockHeader.Crc,\r
+ sizeof (UINT32),\r
+ FTW_ERASED_BYTE\r
+ );\r
+ WorkingBlockHeader.WorkingBlockValid = FTW_ERASE_POLARITY;\r
+ WorkingBlockHeader.WorkingBlockInvalid = FTW_ERASE_POLARITY;\r
+\r
+ //\r
+ // Calculate the Crc of woking block header\r
+ //\r
+ Status = gBS->CalculateCrc32 (\r
+ (UINT8 *) &WorkingBlockHeader,\r
+ sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),\r
+ &WorkingBlockHeader.Crc\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ if (WorkingBlockHeader.Crc != WorkingHeader->Crc) {\r
+ DEBUG ((EFI_D_FTW_LITE, "FtwLite: Work block header CRC check error\n"));\r
+ return FALSE;\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+EFI_STATUS\r
+InitWorkSpaceHeader (\r
+ IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+ Initialize a work space when there is no work space.\r
+\r
+Arguments:\r
+ WorkingHeader - Pointer of working block header \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
+\r
+ ASSERT (WorkingHeader != NULL);\r
+\r
+ //\r
+ // Here using gEfiSystemNvDataFvGuid as the signature.\r
+ //\r
+ CopyMem (\r
+ &WorkingHeader->Signature,\r
+ &gEfiSystemNvDataFvGuid,\r
+ sizeof (EFI_GUID)\r
+ );\r
+ WorkingHeader->WriteQueueSize = FTW_WORKING_QUEUE_SIZE;\r
+\r
+ //\r
+ // Crc is calculated with all the fields except Crc and STATE\r
+ //\r
+ WorkingHeader->WorkingBlockValid = FTW_ERASE_POLARITY;\r
+ WorkingHeader->WorkingBlockInvalid = FTW_ERASE_POLARITY;\r
+ SetMem (&WorkingHeader->Crc, sizeof (UINT32), FTW_ERASED_BYTE);\r
+\r
+ //\r
+ // Calculate the CRC value\r
+ //\r
+ Status = gBS->CalculateCrc32 (\r
+ (UINT8 *) WorkingHeader,\r
+ sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),\r
+ &WorkingHeader->Crc\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ //\r
+ // Restore the WorkingBlockValid flag to VALID state\r
+ //\r
+ WorkingHeader->WorkingBlockValid = FTW_VALID_STATE;\r
+ WorkingHeader->WorkingBlockInvalid = FTW_INVALID_STATE;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+FtwUpdateFvState (\r
+ IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,\r
+ IN EFI_LBA Lba,\r
+ IN UINTN Offset,\r
+ IN UINT8 NewBit\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+ Update a bit of state on a block device. The location of the bit is \r
+ calculated by the (Lba, Offset, bit). Here bit is determined by the \r
+ the name of a certain bit.\r
+\r
+Arguments:\r
+ FvBlock - FVB Protocol interface to access SrcBlock and DestBlock\r
+ Lba - Lba of a block\r
+ Offset - Offset on the Lba\r
+ NewBit - New value that will override the old value if it can be change\r
+\r
+Returns:\r
+ EFI_SUCCESS - A state bit has been updated successfully\r
+ Others - Access block device error.\r
+\r
+Notes:\r
+ Assume all bits of State are inside the same BYTE. \r
+\r
+ EFI_ABORTED - Read block fail\r
+--*/\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 State;\r
+ UINTN Length;\r
+\r
+ //\r
+ // Read state from device, assume State is only one byte.\r
+ //\r
+ Length = sizeof (UINT8);\r
+ Status = FvBlock->Read (FvBlock, Lba, Offset, &Length, &State);\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ State ^= FTW_POLARITY_REVERT;\r
+ State = (UINT8) (State | NewBit);\r
+ State ^= FTW_POLARITY_REVERT;\r
+\r
+ //\r
+ // Write state back to device\r
+ //\r
+ Length = sizeof (UINT8);\r
+ Status = FvBlock->Write (FvBlock, Lba, Offset, &Length, &State);\r
+\r
+ return Status;\r
+}\r
+\r
+EFI_STATUS\r
+FtwGetLastRecord (\r
+ IN EFI_FTW_LITE_DEVICE *FtwLiteDevice,\r
+ OUT EFI_FTW_LITE_RECORD **FtwLastRecord\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+ Get the last Write record pointer. \r
+ The last record is the record whose 'complete' state hasn't been set.\r
+ After all, this header may be a EMPTY header entry for next Allocate. \r
+\r
+Arguments:\r
+ FtwLiteDevice - Private data of this driver\r
+ FtwLastRecord - Pointer to retrieve the last write record\r
+\r
+Returns:\r
+ EFI_SUCCESS - Get the last write record successfully\r
+ EFI_ABORTED - The FTW work space is damaged\r
+\r
+--*/\r
+{\r
+ EFI_FTW_LITE_RECORD *Record;\r
+\r
+ Record = (EFI_FTW_LITE_RECORD *) (FtwLiteDevice->FtwWorkSpaceHeader + 1);\r
+ while (Record->WriteCompleted == FTW_VALID_STATE) {\r
+ //\r
+ // If Offset exceed the FTW work space boudary, return error.\r
+ //\r
+ if ((UINTN) ((UINT8 *) Record - FtwLiteDevice->FtwWorkSpace) > FtwLiteDevice->FtwWorkSpaceSize) {\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ Record++;\r
+ }\r
+ //\r
+ // Last write record is found\r
+ //\r
+ *FtwLastRecord = Record;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+WorkSpaceRefresh (\r
+ IN EFI_FTW_LITE_DEVICE *FtwLiteDevice\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+ Read from working block to refresh the work space in memory.\r
+\r
+Arguments:\r
+ FtwLiteDevice - Point to private data of FTW driver\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
+ UINTN Length;\r
+ UINTN Offset;\r
+ EFI_FTW_LITE_RECORD *Record;\r
+\r
+ //\r
+ // Initialize WorkSpace as FTW_ERASED_BYTE\r
+ //\r
+ SetMem (\r
+ FtwLiteDevice->FtwWorkSpace,\r
+ FtwLiteDevice->FtwWorkSpaceSize,\r
+ FTW_ERASED_BYTE\r
+ );\r
+\r
+ //\r
+ // Read from working block\r
+ //\r
+ Length = FtwLiteDevice->FtwWorkSpaceSize;\r
+ Status = FtwLiteDevice->FtwFvBlock->Read (\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
+ // Refresh the FtwLastRecord\r
+ //\r
+ Status = FtwGetLastRecord (FtwLiteDevice, &FtwLiteDevice->FtwLastRecord);\r
+\r
+ Record = FtwLiteDevice->FtwLastRecord;\r
+ Offset = (UINTN) (UINT8 *) Record - (UINTN) FtwLiteDevice->FtwWorkSpace;\r
+\r
+ //\r
+ // IF work space has error or Record is out of the workspace limit, THEN\r
+ // call reclaim.\r
+ //\r
+ if (EFI_ERROR (Status) || (Offset + WRITE_TOTAL_SIZE >= FtwLiteDevice->FtwWorkSpaceSize)) {\r
+ //\r
+ // reclaim work space in working block.\r
+ //\r
+ Status = FtwReclaimWorkSpace (FtwLiteDevice);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_FTW_LITE, "FtwLite: Reclaim workspace - %r\n", Status));\r
+ return EFI_ABORTED;\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+CleanupWorkSpace (\r
+ IN EFI_FTW_LITE_DEVICE *FtwLiteDevice,\r
+ IN OUT UINT8 *FtwSpaceBuffer,\r
+ IN UINTN BufferSize\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+ Reclaim the work space. Get rid of all the completed write records\r
+ and write records in the Fault Tolerant work space.\r
+\r
+Arguments:\r
+ FtwLiteDevice - Point to private data of FTW driver\r
+ FtwSpaceBuffer - Buffer to contain the reclaimed clean data\r
+ BufferSize - Size of the FtwSpaceBuffer\r
+\r
+Returns:\r
+ EFI_SUCCESS - The function completed successfully\r
+ EFI_BUFFER_TOO_SMALL - The FtwSpaceBuffer is too small\r
+ EFI_ABORTED - The function could not complete successfully.\r
+\r
+--*/\r
+{\r
+ UINTN Length;\r
+ EFI_FTW_LITE_RECORD *Record;\r
+\r
+ //\r
+ // To check if the buffer is large enough\r
+ //\r
+ Length = FtwLiteDevice->FtwWorkSpaceSize;\r
+ if (BufferSize < Length) {\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+ //\r
+ // Clear the content of buffer that will save the new work space data\r
+ //\r
+ SetMem (FtwSpaceBuffer, Length, FTW_ERASED_BYTE);\r
+\r
+ //\r
+ // Copy EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER to buffer\r
+ //\r
+ CopyMem (\r
+ FtwSpaceBuffer,\r
+ FtwLiteDevice->FtwWorkSpaceHeader,\r
+ sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)\r
+ );\r
+\r
+ //\r
+ // Get the last record\r
+ //\r
+ Record = FtwLiteDevice->FtwLastRecord;\r
+ if ((Record != NULL) && (Record->WriteAllocated == FTW_VALID_STATE) && (Record->WriteCompleted != FTW_VALID_STATE)) {\r
+ CopyMem (\r
+ (UINT8 *) FtwSpaceBuffer + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),\r
+ Record,\r
+ WRITE_TOTAL_SIZE\r
+ );\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+FtwReclaimWorkSpace (\r
+ IN EFI_FTW_LITE_DEVICE *FtwLiteDevice\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+ Reclaim the work space on the working block.\r
+\r
+Arguments:\r
+ FtwLiteDevice - Point to private data of FTW driver\r
+\r
+Returns:\r
+ EFI_SUCCESS - The function completed successfully\r
+ EFI_OUT_OF_RESOURCES - Allocate memory error\r
+ EFI_ABORTED - The function could not complete successfully\r
+\r
+--*/\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 *TempBuffer;\r
+ UINTN TempBufferSize;\r
+ UINT8 *Ptr;\r
+ UINTN Length;\r
+ UINTN Index;\r
+ UINTN SpareBufferSize;\r
+ UINT8 *SpareBuffer;\r
+ EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader;\r
+\r
+ DEBUG ((EFI_D_FTW_LITE, "FtwLite: start to reclaim work space\n"));\r
+\r
+ //\r
+ // Read all original data from working block to a memory buffer\r
+ //\r
+ TempBufferSize = FtwLiteDevice->SpareAreaLength;\r
+ TempBuffer = AllocateZeroPool (TempBufferSize);\r
+ if (TempBuffer != NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Ptr = TempBuffer;\r
+ for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {\r
+ Length = FtwLiteDevice->SizeOfSpareBlock;\r
+ Status = FtwLiteDevice->FtwFvBlock->Read (\r
+ FtwLiteDevice->FtwFvBlock,\r
+ FtwLiteDevice->FtwWorkBlockLba + Index,\r
+ 0,\r
+ &Length,\r
+ Ptr\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (TempBuffer);\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ Ptr += Length;\r
+ }\r
+ //\r
+ // Clean up the workspace, remove all the completed records.\r
+ //\r
+ Ptr = TempBuffer +\r
+ ((UINTN) (FtwLiteDevice->FtwWorkSpaceLba - FtwLiteDevice->FtwWorkBlockLba)) *\r
+ FtwLiteDevice->SizeOfSpareBlock + FtwLiteDevice->FtwWorkSpaceBase;\r
+\r
+ Status = CleanupWorkSpace (\r
+ FtwLiteDevice,\r
+ Ptr,\r
+ FtwLiteDevice->FtwWorkSpaceSize\r
+ );\r
+\r
+ CopyMem (\r
+ FtwLiteDevice->FtwWorkSpace,\r
+ Ptr,\r
+ FtwLiteDevice->FtwWorkSpaceSize\r
+ );\r
+\r
+ Status = FtwGetLastRecord (FtwLiteDevice, &FtwLiteDevice->FtwLastRecord);\r
+\r
+ //\r
+ // Set the WorkingBlockValid and WorkingBlockInvalid as INVALID\r
+ //\r
+ WorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) Ptr;\r
+ WorkingBlockHeader->WorkingBlockValid = FTW_INVALID_STATE;\r
+ WorkingBlockHeader->WorkingBlockInvalid = FTW_INVALID_STATE;\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 (TempBuffer);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Ptr = SpareBuffer;\r
+ for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {\r
+ Length = FtwLiteDevice->SizeOfSpareBlock;\r
+ Status = FtwLiteDevice->FtwBackupFvb->Read (\r
+ FtwLiteDevice->FtwBackupFvb,\r
+ FtwLiteDevice->FtwSpareLba + Index,\r
+ 0,\r
+ &Length,\r
+ Ptr\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (TempBuffer);\r
+ FreePool (SpareBuffer);\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ Ptr += Length;\r
+ }\r
+ //\r
+ // Write the memory buffer to spare block\r
+ //\r
+ Status = FtwEraseSpareBlock (FtwLiteDevice);\r
+ Ptr = TempBuffer;\r
+ for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {\r
+ Length = FtwLiteDevice->SizeOfSpareBlock;\r
+ Status = FtwLiteDevice->FtwBackupFvb->Write (\r
+ FtwLiteDevice->FtwBackupFvb,\r
+ FtwLiteDevice->FtwSpareLba + Index,\r
+ 0,\r
+ &Length,\r
+ Ptr\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (TempBuffer);\r
+ FreePool (SpareBuffer);\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ Ptr += Length;\r
+ }\r
+ //\r
+ // Free TempBuffer\r
+ //\r
+ FreePool (TempBuffer);\r
+\r
+ //\r
+ // Write the spare block to working block\r
+ //\r
+ Status = FlushSpareBlockToWorkingBlock (FtwLiteDevice);\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (SpareBuffer);\r
+ return Status;\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
+ Length = FtwLiteDevice->SizeOfSpareBlock;\r
+ Status = FtwLiteDevice->FtwBackupFvb->Write (\r
+ FtwLiteDevice->FtwBackupFvb,\r
+ FtwLiteDevice->FtwSpareLba + Index,\r
+ 0,\r
+ &Length,\r
+ Ptr\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (SpareBuffer);\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ Ptr += Length;\r
+ }\r
+\r
+ FreePool (SpareBuffer);\r
+\r
+ DEBUG ((EFI_D_FTW_LITE, "FtwLite: reclaim work space success\n"));\r
+\r
+ return EFI_SUCCESS;\r
+}\r