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