/** @file\r
\r
- These are the common Fault Tolerant Write (FTW) functions that are shared \r
+ These are the common Fault Tolerant Write (FTW) functions that are shared\r
by DXE FTW driver and SMM FTW driver.\r
\r
-Copyright (c) 2006 - 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
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
\r
**/\r
\r
Query the largest block that may be updated in a fault tolerant manner.\r
\r
\r
- @param This The pointer to this protocol instance. \r
+ @param This The pointer to this protocol instance.\r
@param BlockSize A pointer to a caller allocated UINTN that is updated to\r
indicate the size of the largest block that can be updated.\r
\r
\r
All writes must be completed or aborted before another fault tolerant write can occur.\r
\r
- @param This The pointer to this protocol instance. \r
+ @param This The pointer to this protocol instance.\r
@param CallerId The GUID identifying the write.\r
@param PrivateDataSize The size of the caller's private data\r
that must be recorded for each write.\r
)\r
{\r
EFI_STATUS Status;\r
- UINTN Length;\r
UINTN Offset;\r
EFI_FTW_DEVICE *FtwDevice;\r
EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader;\r
FtwHeader->PrivateDataSize = PrivateDataSize;\r
FtwHeader->HeaderAllocated = FTW_VALID_STATE;\r
\r
- Length = sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER);\r
- Status = FtwDevice->FtwFvBlock->Write (\r
- FtwDevice->FtwFvBlock,\r
- FtwDevice->FtwWorkSpaceLba,\r
- FtwDevice->FtwWorkSpaceBase + Offset,\r
- &Length,\r
- (UINT8 *) FtwHeader\r
- );\r
+ Status = WriteWorkSpaceData (\r
+ FtwDevice->FtwFvBlock,\r
+ FtwDevice->WorkBlockSize,\r
+ FtwDevice->FtwWorkSpaceLba,\r
+ FtwDevice->FtwWorkSpaceBase + Offset,\r
+ sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER),\r
+ (UINT8 *) FtwHeader\r
+ );\r
if (EFI_ERROR (Status)) {\r
return EFI_ABORTED;\r
}\r
//\r
Status = FtwUpdateFvState (\r
FtwDevice->FtwFvBlock,\r
+ FtwDevice->WorkBlockSize,\r
FtwDevice->FtwWorkSpaceLba,\r
FtwDevice->FtwWorkSpaceBase + Offset,\r
WRITES_ALLOCATED\r
}\r
\r
DEBUG (\r
- (EFI_D_ERROR,\r
+ (EFI_D_INFO,\r
"Ftw: Allocate() success, Caller:%g, # %d\n",\r
CallerId,\r
NumberOfWrites)\r
\r
\r
/**\r
- Write a record with fault tolerant mannaer.\r
+ Write a record with fault tolerant manner.\r
Since the content has already backuped in spare block, the write is\r
guaranteed to be completed with fault tolerant manner.\r
\r
- @param This The pointer to this protocol instance. \r
+ @param This The pointer to this protocol instance.\r
@param Fvb The FVB protocol that provides services for\r
reading, writing, and erasing the target block.\r
+ @param BlockSize The size of the block.\r
\r
@retval EFI_SUCCESS The function completed successfully\r
@retval EFI_ABORTED The function could not complete successfully\r
EFI_STATUS\r
FtwWriteRecord (\r
IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,\r
- IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb\r
+ IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb,\r
+ IN UINTN BlockSize\r
)\r
{\r
EFI_STATUS Status;\r
EFI_FAULT_TOLERANT_WRITE_HEADER *Header;\r
EFI_FAULT_TOLERANT_WRITE_RECORD *Record;\r
UINTN Offset;\r
- EFI_LBA WorkSpaceLbaOffset;\r
+ UINTN NumberOfWriteBlocks;\r
\r
FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
\r
- WorkSpaceLbaOffset = FtwDevice->FtwWorkSpaceLba - FtwDevice->FtwWorkBlockLba;\r
-\r
//\r
// Spare Complete but Destination not complete,\r
// Recover the target block with the spare block.\r
Offset = (UINT8 *) Record - FtwDevice->FtwWorkSpace;\r
Status = FtwUpdateFvState (\r
FtwDevice->FtwBackupFvb,\r
- FtwDevice->FtwSpareLba + WorkSpaceLbaOffset,\r
- FtwDevice->FtwWorkSpaceBase + Offset,\r
+ FtwDevice->SpareBlockSize,\r
+ FtwDevice->FtwSpareLba + FtwDevice->FtwWorkSpaceLbaInSpare,\r
+ FtwDevice->FtwWorkSpaceBaseInSpare + Offset,\r
SPARE_COMPLETED\r
);\r
if (EFI_ERROR (Status)) {\r
}\r
\r
Status = FlushSpareBlockToWorkingBlock (FtwDevice);\r
- } else if (IsBootBlock (FtwDevice, Fvb, Record->Lba)) {\r
+ } else if (IsBootBlock (FtwDevice, Fvb)) {\r
//\r
// Update boot block\r
//\r
//\r
// Update blocks other than working block or boot block\r
//\r
- Status = FlushSpareBlockToTargetBlock (FtwDevice, Fvb, Record->Lba);\r
+ NumberOfWriteBlocks = FTW_BLOCKS ((UINTN) (Record->Offset + Record->Length), BlockSize);\r
+ Status = FlushSpareBlockToTargetBlock (FtwDevice, Fvb, Record->Lba, BlockSize, NumberOfWriteBlocks);\r
}\r
\r
if (EFI_ERROR (Status)) {\r
Offset = (UINT8 *) Record - FtwDevice->FtwWorkSpace;\r
Status = FtwUpdateFvState (\r
FtwDevice->FtwFvBlock,\r
+ FtwDevice->WorkBlockSize,\r
FtwDevice->FtwWorkSpaceLba,\r
FtwDevice->FtwWorkSpaceBase + Offset,\r
DEST_COMPLETED\r
Offset = (UINT8 *) Header - FtwDevice->FtwWorkSpace;\r
Status = FtwUpdateFvState (\r
FtwDevice->FtwFvBlock,\r
+ FtwDevice->WorkBlockSize,\r
FtwDevice->FtwWorkSpaceLba,\r
FtwDevice->FtwWorkSpaceBase + Offset,\r
WRITES_COMPLETED\r
manner, ensuring at all times that either the original contents or\r
the modified contents are available.\r
\r
- @param This The pointer to this protocol instance. \r
+ @param This The pointer to this protocol instance.\r
@param Lba The logical block address of the target block.\r
@param Offset The offset within the target block to place the data.\r
@param Length The number of bytes to write to the target block.\r
reading, writing, and erasing the target block.\r
@param Buffer The data to write.\r
\r
- @retval EFI_SUCCESS The function completed successfully \r
- @retval EFI_ABORTED The function could not complete successfully. \r
- @retval EFI_BAD_BUFFER_SIZE The input data can't fit within the spare block. \r
+ @retval EFI_SUCCESS The function completed successfully\r
+ @retval EFI_ABORTED The function could not complete successfully.\r
+ @retval EFI_BAD_BUFFER_SIZE The input data can't fit within the spare block.\r
Offset + *NumBytes > SpareAreaLength.\r
- @retval EFI_ACCESS_DENIED No writes have been allocated. \r
+ @retval EFI_ACCESS_DENIED No writes have been allocated.\r
@retval EFI_OUT_OF_RESOURCES Cannot allocate enough memory resource.\r
@retval EFI_NOT_FOUND Cannot find FVB protocol by handle.\r
\r
UINTN Index;\r
UINT8 *Ptr;\r
EFI_PHYSICAL_ADDRESS FvbPhysicalAddress;\r
+ UINTN BlockSize;\r
+ UINTN NumberOfBlocks;\r
+ UINTN NumberOfWriteBlocks;\r
+ UINTN WriteLength;\r
\r
FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
\r
\r
Header = FtwDevice->FtwLastWriteHeader;\r
Record = FtwDevice->FtwLastWriteRecord;\r
- \r
+\r
if (IsErasedFlashBuffer ((UINT8 *) Header, sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER))) {\r
if (PrivateData == NULL) {\r
//\r
//\r
// If Record is out of the range of Header, return access denied.\r
//\r
- if (((UINTN)((UINT8 *) Record - (UINT8 *) Header)) > FTW_WRITE_TOTAL_SIZE (Header->NumberOfWrites - 1, Header->PrivateDataSize)) {\r
+ if (((UINTN) Record - (UINTN) Header) > FTW_WRITE_TOTAL_SIZE (Header->NumberOfWrites - 1, Header->PrivateDataSize)) {\r
return EFI_ACCESS_DENIED;\r
}\r
\r
if ((Record->SpareComplete == FTW_VALID_STATE) && (Record->DestinationComplete != FTW_VALID_STATE)) {\r
return EFI_NOT_READY;\r
}\r
- //\r
- // Check if the input data can fit within the target block\r
- //\r
- if ((Offset + Length) > FtwDevice->SpareAreaLength) {\r
- return EFI_BAD_BUFFER_SIZE;\r
- }\r
+\r
//\r
// Get the FVB protocol by handle\r
//\r
\r
Status = Fvb->GetPhysicalAddress (Fvb, &FvbPhysicalAddress);\r
if (EFI_ERROR (Status)) {\r
- DEBUG ((EFI_D_ERROR, "FtwLite: Get FVB physical address - %r\n", Status));\r
+ DEBUG ((EFI_D_ERROR, "Ftw: Write(), Get FVB physical address - %r\n", Status));\r
return EFI_ABORTED;\r
}\r
\r
+ //\r
+ // Now, one FVB has one type of BlockSize.\r
+ //\r
+ Status = Fvb->GetBlockSize (Fvb, 0, &BlockSize, &NumberOfBlocks);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "Ftw: Write(), Get block size - %r\n", Status));\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ NumberOfWriteBlocks = FTW_BLOCKS (Offset + Length, BlockSize);\r
+ DEBUG ((EFI_D_INFO, "Ftw: Write(), BlockSize - 0x%x, NumberOfWriteBlock - 0x%x\n", BlockSize, NumberOfWriteBlocks));\r
+ WriteLength = NumberOfWriteBlocks * BlockSize;\r
+\r
+ //\r
+ // Check if the input data can fit within the spare block.\r
+ //\r
+ if (WriteLength > FtwDevice->SpareAreaLength) {\r
+ return EFI_BAD_BUFFER_SIZE;\r
+ }\r
+\r
//\r
// Set BootBlockUpdate FLAG if it's updating boot block.\r
//\r
- if (IsBootBlock (FtwDevice, Fvb, Lba)) {\r
+ if (IsBootBlock (FtwDevice, Fvb)) {\r
Record->BootBlockUpdate = FTW_VALID_STATE;\r
+ //\r
+ // Boot Block and Spare Block should have same block size and block numbers.\r
+ //\r
+ ASSERT ((BlockSize == FtwDevice->SpareBlockSize) && (NumberOfWriteBlocks == FtwDevice->NumberOfSpareBlock));\r
}\r
//\r
// Write the record to the work space.\r
Record->Lba = Lba;\r
Record->Offset = Offset;\r
Record->Length = Length;\r
- Record->RelativeOffset = (INT64) (FvbPhysicalAddress + (UINTN) Lba * FtwDevice->BlockSize) - (INT64) FtwDevice->SpareAreaAddress;\r
+ Record->RelativeOffset = (INT64) (FvbPhysicalAddress + (UINTN) Lba * BlockSize) - (INT64) FtwDevice->SpareAreaAddress;\r
if (PrivateData != NULL) {\r
CopyMem ((Record + 1), PrivateData, (UINTN) Header->PrivateDataSize);\r
}\r
MyOffset = (UINT8 *) Record - FtwDevice->FtwWorkSpace;\r
MyLength = FTW_RECORD_SIZE (Header->PrivateDataSize);\r
\r
- Status = FtwDevice->FtwFvBlock->Write (\r
- FtwDevice->FtwFvBlock,\r
- FtwDevice->FtwWorkSpaceLba,\r
- FtwDevice->FtwWorkSpaceBase + MyOffset,\r
- &MyLength,\r
- (UINT8 *) Record\r
- );\r
+ Status = WriteWorkSpaceData (\r
+ FtwDevice->FtwFvBlock,\r
+ FtwDevice->WorkBlockSize,\r
+ FtwDevice->FtwWorkSpaceLba,\r
+ FtwDevice->FtwWorkSpaceBase + MyOffset,\r
+ MyLength,\r
+ (UINT8 *) Record\r
+ );\r
if (EFI_ERROR (Status)) {\r
return EFI_ABORTED;\r
}\r
//\r
// Allocate a memory buffer\r
//\r
- MyBufferSize = FtwDevice->SpareAreaLength;\r
+ MyBufferSize = WriteLength;\r
MyBuffer = AllocatePool (MyBufferSize);\r
if (MyBuffer == NULL) {\r
return EFI_OUT_OF_RESOURCES;\r
// Read all original data from target block to memory buffer\r
//\r
Ptr = MyBuffer;\r
- for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
- MyLength = FtwDevice->BlockSize;\r
+ for (Index = 0; Index < NumberOfWriteBlocks; Index += 1) {\r
+ MyLength = BlockSize;\r
Status = Fvb->Read (Fvb, Lba + Index, 0, &MyLength, Ptr);\r
if (EFI_ERROR (Status)) {\r
FreePool (MyBuffer);\r
\r
Ptr = SpareBuffer;\r
for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
- MyLength = FtwDevice->BlockSize;\r
+ MyLength = FtwDevice->SpareBlockSize;\r
Status = FtwDevice->FtwBackupFvb->Read (\r
FtwDevice->FtwBackupFvb,\r
FtwDevice->FtwSpareLba + Index,\r
}\r
//\r
// Write the memory buffer to spare block\r
+ // Do not assume Spare Block and Target Block have same block size\r
//\r
Status = FtwEraseSpareBlock (FtwDevice);\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (MyBuffer);\r
+ FreePool (SpareBuffer);\r
+ return EFI_ABORTED;\r
+ }\r
Ptr = MyBuffer;\r
- for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
- MyLength = FtwDevice->BlockSize;\r
+ for (Index = 0; MyBufferSize > 0; Index += 1) {\r
+ if (MyBufferSize > FtwDevice->SpareBlockSize) {\r
+ MyLength = FtwDevice->SpareBlockSize;\r
+ } else {\r
+ MyLength = MyBufferSize;\r
+ }\r
Status = FtwDevice->FtwBackupFvb->Write (\r
FtwDevice->FtwBackupFvb,\r
FtwDevice->FtwSpareLba + Index,\r
}\r
\r
Ptr += MyLength;\r
+ MyBufferSize -= MyLength;\r
}\r
//\r
// Free MyBuffer\r
MyOffset = (UINT8 *) Record - FtwDevice->FtwWorkSpace;\r
Status = FtwUpdateFvState (\r
FtwDevice->FtwFvBlock,\r
+ FtwDevice->WorkBlockSize,\r
FtwDevice->FtwWorkSpaceLba,\r
FtwDevice->FtwWorkSpaceBase + MyOffset,\r
SPARE_COMPLETED\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 (This, Fvb);\r
+ Status = FtwWriteRecord (This, Fvb, BlockSize);\r
if (EFI_ERROR (Status)) {\r
FreePool (SpareBuffer);\r
return EFI_ABORTED;\r
// Restore spare backup buffer into spare block , if no failure happened during FtwWrite.\r
//\r
Status = FtwEraseSpareBlock (FtwDevice);\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (SpareBuffer);\r
+ return EFI_ABORTED;\r
+ }\r
Ptr = SpareBuffer;\r
for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
- MyLength = FtwDevice->BlockSize;\r
+ MyLength = FtwDevice->SpareBlockSize;\r
Status = FtwDevice->FtwBackupFvb->Write (\r
FtwDevice->FtwBackupFvb,\r
FtwDevice->FtwSpareLba + Index,\r
FreePool (SpareBuffer);\r
\r
DEBUG (\r
- (EFI_D_ERROR,\r
+ (EFI_D_INFO,\r
"Ftw: Write() success, (Lba:Offset)=(%lx:0x%x), Length: 0x%x\n",\r
Lba,\r
Offset,\r
Restarts a previously interrupted write. The caller must provide the\r
block protocol needed to complete the interrupted write.\r
\r
- @param This The pointer to this protocol instance. \r
+ @param This The pointer to this protocol instance.\r
@param FvBlockHandle The handle of FVB protocol that provides services for\r
reading, writing, and erasing the target block.\r
\r
EFI_FAULT_TOLERANT_WRITE_HEADER *Header;\r
EFI_FAULT_TOLERANT_WRITE_RECORD *Record;\r
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;\r
+ UINTN BlockSize;\r
+ UINTN NumberOfBlocks;\r
\r
FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
\r
return EFI_NOT_FOUND;\r
}\r
\r
+ //\r
+ // Now, one FVB has one type of BlockSize\r
+ //\r
+ Status = Fvb->GetBlockSize (Fvb, 0, &BlockSize, &NumberOfBlocks);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "Ftw: Restart(), Get block size - %r\n", Status));\r
+ return EFI_ABORTED;\r
+ }\r
+\r
//\r
// Check the COMPLETE flag of last write header\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 (This, Fvb);\r
+ Status = FtwWriteRecord (This, Fvb, BlockSize);\r
if (EFI_ERROR (Status)) {\r
return EFI_ABORTED;\r
}\r
// Erase Spare block\r
// This is restart, no need to keep spareblock content.\r
//\r
- FtwEraseSpareBlock (FtwDevice);\r
+ Status = FtwEraseSpareBlock (FtwDevice);\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_ABORTED;\r
+ }\r
\r
- DEBUG ((EFI_D_ERROR, "Ftw: Restart() success \n"));\r
+ DEBUG ((EFI_D_INFO, "%a(): success\n", __FUNCTION__));\r
return EFI_SUCCESS;\r
}\r
\r
/**\r
Aborts all previous allocated writes.\r
\r
- @param This The pointer to this protocol instance. \r
+ @param This The pointer to this protocol instance.\r
\r
@retval EFI_SUCCESS The function completed successfully\r
@retval EFI_ABORTED The function could not complete successfully.\r
Offset = (UINT8 *) FtwDevice->FtwLastWriteHeader - FtwDevice->FtwWorkSpace;\r
Status = FtwUpdateFvState (\r
FtwDevice->FtwFvBlock,\r
+ FtwDevice->WorkBlockSize,\r
FtwDevice->FtwWorkSpaceLba,\r
FtwDevice->FtwWorkSpaceBase + Offset,\r
WRITES_COMPLETED\r
\r
FtwDevice->FtwLastWriteHeader->Complete = FTW_VALID_STATE;\r
\r
- DEBUG ((EFI_D_ERROR, "Ftw: Abort() success \n"));\r
+ DEBUG ((EFI_D_INFO, "%a(): success\n", __FUNCTION__));\r
return EFI_SUCCESS;\r
}\r
\r
manner, ensuring at all times that either the original contents or\r
the modified contents are available.\r
\r
- @param This The pointer to this protocol instance. \r
+ @param This The pointer to this protocol instance.\r
@param CallerId The GUID identifying the last write.\r
@param Lba The logical block address of the last write.\r
@param Offset The offset within the block of the last write.\r
Status = EFI_SUCCESS;\r
}\r
\r
- DEBUG ((EFI_D_ERROR, "Ftw: GetLasetWrite() success\n"));\r
+ DEBUG ((EFI_D_INFO, "%a(): success\n", __FUNCTION__));\r
\r
return Status;\r
}\r