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