From af1a266670d040d2f4083ff309d732d648afba2a Mon Sep 17 00:00:00 2001 From: Angelos Mouzakitis Date: Tue, 10 Oct 2023 14:33:42 +0000 Subject: Add submodule dependency files Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec --- .../FaultTolerantWriteDxe/FaultTolerantWrite.c | 887 +++++++++++++++++++++ 1 file changed, 887 insertions(+) create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.c (limited to 'roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.c') diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.c b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.c new file mode 100644 index 000000000..484763225 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.c @@ -0,0 +1,887 @@ +/** @file + + These are the common Fault Tolerant Write (FTW) functions that are shared + by DXE FTW driver and SMM FTW driver. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "FaultTolerantWrite.h" + +// +// Fault Tolerant Write Protocol API +// +/** + Query the largest block that may be updated in a fault tolerant manner. + + + @param This The pointer to this protocol instance. + @param BlockSize A pointer to a caller allocated UINTN that is updated to + indicate the size of the largest block that can be updated. + + @return EFI_SUCCESS The function completed successfully + +**/ +EFI_STATUS +EFIAPI +FtwGetMaxBlockSize ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + OUT UINTN *BlockSize + ) +{ + EFI_FTW_DEVICE *FtwDevice; + + if (!FeaturePcdGet(PcdFullFtwServiceEnable)) { + return EFI_UNSUPPORTED; + } + + FtwDevice = FTW_CONTEXT_FROM_THIS (This); + + *BlockSize = FtwDevice->SpareAreaLength; + + return EFI_SUCCESS; +} + +/** + Allocates space for the protocol to maintain information about writes. + Since writes must be completed in a fault tolerant manner and multiple + updates will require more resources to be successful, this function + enables the protocol to ensure that enough space exists to track + information about the upcoming writes. + + All writes must be completed or aborted before another fault tolerant write can occur. + + @param This The pointer to this protocol instance. + @param CallerId The GUID identifying the write. + @param PrivateDataSize The size of the caller's private data + that must be recorded for each write. + @param NumberOfWrites The number of fault tolerant block writes + that will need to occur. + + @return EFI_SUCCESS The function completed successfully + @retval EFI_ABORTED The function could not complete successfully. + @retval EFI_ACCESS_DENIED All allocated writes have not been completed. + +**/ +EFI_STATUS +EFIAPI +FtwAllocate ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + IN EFI_GUID *CallerId, + IN UINTN PrivateDataSize, + IN UINTN NumberOfWrites + ) +{ + EFI_STATUS Status; + UINTN Offset; + EFI_FTW_DEVICE *FtwDevice; + EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader; + + FtwDevice = FTW_CONTEXT_FROM_THIS (This); + + Status = WorkSpaceRefresh (FtwDevice); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + // + // Check if there is enough space for the coming allocation + // + if (FTW_WRITE_TOTAL_SIZE (NumberOfWrites, PrivateDataSize) > FtwDevice->FtwWorkSpaceHeader->WriteQueueSize) { + DEBUG ((EFI_D_ERROR, "Ftw: Allocate() request exceed Workspace, Caller: %g\n", CallerId)); + return EFI_BUFFER_TOO_SMALL; + } + // + // Find the last write header and record. + // If the FtwHeader is complete, skip the completed last write header/records + // + FtwHeader = FtwDevice->FtwLastWriteHeader; + + // + // Previous write has not completed, access denied. + // + if ((FtwHeader->HeaderAllocated == FTW_VALID_STATE) || (FtwHeader->WritesAllocated == FTW_VALID_STATE)) { + return EFI_ACCESS_DENIED; + } + // + // If workspace is not enough, then reclaim workspace + // + Offset = (UINT8 *) FtwHeader - (UINT8 *) FtwDevice->FtwWorkSpace; + if (Offset + FTW_WRITE_TOTAL_SIZE (NumberOfWrites, PrivateDataSize) > FtwDevice->FtwWorkSpaceSize) { + Status = FtwReclaimWorkSpace (FtwDevice, TRUE); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + FtwHeader = FtwDevice->FtwLastWriteHeader; + } + // + // Prepare FTW write header, + // overwrite the buffer and write to workspace. + // + FtwHeader->WritesAllocated = FTW_INVALID_STATE; + FtwHeader->Complete = FTW_INVALID_STATE; + CopyMem (&FtwHeader->CallerId, CallerId, sizeof (EFI_GUID)); + FtwHeader->NumberOfWrites = NumberOfWrites; + FtwHeader->PrivateDataSize = PrivateDataSize; + FtwHeader->HeaderAllocated = FTW_VALID_STATE; + + Status = WriteWorkSpaceData ( + FtwDevice->FtwFvBlock, + FtwDevice->WorkBlockSize, + FtwDevice->FtwWorkSpaceLba, + FtwDevice->FtwWorkSpaceBase + Offset, + sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER), + (UINT8 *) FtwHeader + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + // + // Update Header->WriteAllocated as VALID + // + Status = FtwUpdateFvState ( + FtwDevice->FtwFvBlock, + FtwDevice->WorkBlockSize, + FtwDevice->FtwWorkSpaceLba, + FtwDevice->FtwWorkSpaceBase + Offset, + WRITES_ALLOCATED + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + DEBUG ( + (EFI_D_INFO, + "Ftw: Allocate() success, Caller:%g, # %d\n", + CallerId, + NumberOfWrites) + ); + + return EFI_SUCCESS; +} + + +/** + Write a record with fault tolerant manner. + Since the content has already backuped in spare block, the write is + guaranteed to be completed with fault tolerant manner. + + @param This The pointer to this protocol instance. + @param Fvb The FVB protocol that provides services for + reading, writing, and erasing the target block. + @param BlockSize The size of the block. + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_ABORTED The function could not complete successfully + +**/ +EFI_STATUS +FtwWriteRecord ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb, + IN UINTN BlockSize + ) +{ + EFI_STATUS Status; + EFI_FTW_DEVICE *FtwDevice; + EFI_FAULT_TOLERANT_WRITE_HEADER *Header; + EFI_FAULT_TOLERANT_WRITE_RECORD *Record; + UINTN Offset; + UINTN NumberOfWriteBlocks; + + FtwDevice = FTW_CONTEXT_FROM_THIS (This); + + // + // Spare Complete but Destination not complete, + // Recover the target block with the spare block. + // + Header = FtwDevice->FtwLastWriteHeader; + Record = FtwDevice->FtwLastWriteRecord; + + // + // IF target block is working block, THEN Flush Spare Block To Working Block; + // ELSE flush spare block to target block, which may be boot block after all. + // + if (IsWorkingBlock (FtwDevice, Fvb, Record->Lba)) { + // + // If target block is working block, + // it also need to set SPARE_COMPLETED to spare block. + // + Offset = (UINT8 *) Record - FtwDevice->FtwWorkSpace; + Status = FtwUpdateFvState ( + FtwDevice->FtwBackupFvb, + FtwDevice->SpareBlockSize, + FtwDevice->FtwSpareLba + FtwDevice->FtwWorkSpaceLbaInSpare, + FtwDevice->FtwWorkSpaceBaseInSpare + Offset, + SPARE_COMPLETED + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + Status = FlushSpareBlockToWorkingBlock (FtwDevice); + } else if (IsBootBlock (FtwDevice, Fvb)) { + // + // Update boot block + // + Status = FlushSpareBlockToBootBlock (FtwDevice); + } else { + // + // Update blocks other than working block or boot block + // + NumberOfWriteBlocks = FTW_BLOCKS ((UINTN) (Record->Offset + Record->Length), BlockSize); + Status = FlushSpareBlockToTargetBlock (FtwDevice, Fvb, Record->Lba, BlockSize, NumberOfWriteBlocks); + } + + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + // + // Record the DestionationComplete in record + // + Offset = (UINT8 *) Record - FtwDevice->FtwWorkSpace; + Status = FtwUpdateFvState ( + FtwDevice->FtwFvBlock, + FtwDevice->WorkBlockSize, + FtwDevice->FtwWorkSpaceLba, + FtwDevice->FtwWorkSpaceBase + Offset, + DEST_COMPLETED + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + Record->DestinationComplete = FTW_VALID_STATE; + + // + // If this is the last Write in these write sequence, + // set the complete flag of write header. + // + if (IsLastRecordOfWrites (Header, Record)) { + Offset = (UINT8 *) Header - FtwDevice->FtwWorkSpace; + Status = FtwUpdateFvState ( + FtwDevice->FtwFvBlock, + FtwDevice->WorkBlockSize, + FtwDevice->FtwWorkSpaceLba, + FtwDevice->FtwWorkSpaceBase + Offset, + WRITES_COMPLETED + ); + Header->Complete = FTW_VALID_STATE; + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + } + + return EFI_SUCCESS; +} + +/** + 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. + + @param This The pointer to this protocol instance. + @param Lba The logical block address of the target block. + @param Offset The offset within the target block to place the data. + @param Length The number of bytes to write to the target block. + @param PrivateData A pointer to private data that the caller requires to + complete any pending writes in the event of a fault. + @param FvBlockHandle The handle of FVB protocol that provides services for + reading, writing, and erasing the target block. + @param Buffer The data to write. + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_ABORTED The function could not complete successfully. + @retval EFI_BAD_BUFFER_SIZE The input data can't fit within the spare block. + Offset + *NumBytes > SpareAreaLength. + @retval EFI_ACCESS_DENIED No writes have been allocated. + @retval EFI_OUT_OF_RESOURCES Cannot allocate enough memory resource. + @retval EFI_NOT_FOUND Cannot find FVB protocol by handle. + +**/ +EFI_STATUS +EFIAPI +FtwWrite ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + IN EFI_LBA Lba, + IN UINTN Offset, + IN UINTN Length, + IN VOID *PrivateData, + IN EFI_HANDLE FvBlockHandle, + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + EFI_FTW_DEVICE *FtwDevice; + EFI_FAULT_TOLERANT_WRITE_HEADER *Header; + EFI_FAULT_TOLERANT_WRITE_RECORD *Record; + EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; + UINTN MyLength; + UINTN MyOffset; + UINTN MyBufferSize; + UINT8 *MyBuffer; + UINTN SpareBufferSize; + UINT8 *SpareBuffer; + UINTN Index; + UINT8 *Ptr; + EFI_PHYSICAL_ADDRESS FvbPhysicalAddress; + UINTN BlockSize; + UINTN NumberOfBlocks; + UINTN NumberOfWriteBlocks; + UINTN WriteLength; + + FtwDevice = FTW_CONTEXT_FROM_THIS (This); + + Status = WorkSpaceRefresh (FtwDevice); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + Header = FtwDevice->FtwLastWriteHeader; + Record = FtwDevice->FtwLastWriteRecord; + + if (IsErasedFlashBuffer ((UINT8 *) Header, sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER))) { + if (PrivateData == NULL) { + // + // Ftw Write Header is not allocated. + // No additional private data, the private data size is zero. Number of record can be set to 1. + // + Status = FtwAllocate (This, &gEfiCallerIdGuid, 0, 1); + if (EFI_ERROR (Status)) { + return Status; + } + } else { + // + // Ftw Write Header is not allocated + // Additional private data is not NULL, the private data size can't be determined. + // + DEBUG ((EFI_D_ERROR, "Ftw: no allocates space for write record!\n")); + DEBUG ((EFI_D_ERROR, "Ftw: Allocate service should be called before Write service!\n")); + return EFI_NOT_READY; + } + } + + // + // If Record is out of the range of Header, return access denied. + // + if (((UINTN) Record - (UINTN) Header) > FTW_WRITE_TOTAL_SIZE (Header->NumberOfWrites - 1, Header->PrivateDataSize)) { + return EFI_ACCESS_DENIED; + } + + // + // Check the COMPLETE flag of last write header + // + if (Header->Complete == FTW_VALID_STATE) { + return EFI_ACCESS_DENIED; + } + + if (Record->DestinationComplete == FTW_VALID_STATE) { + return EFI_ACCESS_DENIED; + } + + if ((Record->SpareComplete == FTW_VALID_STATE) && (Record->DestinationComplete != FTW_VALID_STATE)) { + return EFI_NOT_READY; + } + + // + // Get the FVB protocol by handle + // + Status = FtwGetFvbByHandle (FvBlockHandle, &Fvb); + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + + Status = Fvb->GetPhysicalAddress (Fvb, &FvbPhysicalAddress); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Ftw: Write(), Get FVB physical address - %r\n", Status)); + return EFI_ABORTED; + } + + // + // Now, one FVB has one type of BlockSize. + // + Status = Fvb->GetBlockSize (Fvb, 0, &BlockSize, &NumberOfBlocks); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Ftw: Write(), Get block size - %r\n", Status)); + return EFI_ABORTED; + } + + NumberOfWriteBlocks = FTW_BLOCKS (Offset + Length, BlockSize); + DEBUG ((EFI_D_INFO, "Ftw: Write(), BlockSize - 0x%x, NumberOfWriteBlock - 0x%x\n", BlockSize, NumberOfWriteBlocks)); + WriteLength = NumberOfWriteBlocks * BlockSize; + + // + // Check if the input data can fit within the spare block. + // + if (WriteLength > FtwDevice->SpareAreaLength) { + return EFI_BAD_BUFFER_SIZE; + } + + // + // Set BootBlockUpdate FLAG if it's updating boot block. + // + if (IsBootBlock (FtwDevice, Fvb)) { + Record->BootBlockUpdate = FTW_VALID_STATE; + // + // Boot Block and Spare Block should have same block size and block numbers. + // + ASSERT ((BlockSize == FtwDevice->SpareBlockSize) && (NumberOfWriteBlocks == FtwDevice->NumberOfSpareBlock)); + } + // + // Write the record to the work space. + // + Record->Lba = Lba; + Record->Offset = Offset; + Record->Length = Length; + Record->RelativeOffset = (INT64) (FvbPhysicalAddress + (UINTN) Lba * BlockSize) - (INT64) FtwDevice->SpareAreaAddress; + if (PrivateData != NULL) { + CopyMem ((Record + 1), PrivateData, (UINTN) Header->PrivateDataSize); + } + + MyOffset = (UINT8 *) Record - FtwDevice->FtwWorkSpace; + MyLength = FTW_RECORD_SIZE (Header->PrivateDataSize); + + Status = WriteWorkSpaceData ( + FtwDevice->FtwFvBlock, + FtwDevice->WorkBlockSize, + FtwDevice->FtwWorkSpaceLba, + FtwDevice->FtwWorkSpaceBase + MyOffset, + MyLength, + (UINT8 *) Record + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + // + // Record has written to working block, then do the data. + // + // + // Allocate a memory buffer + // + MyBufferSize = WriteLength; + MyBuffer = AllocatePool (MyBufferSize); + if (MyBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Read all original data from target block to memory buffer + // + Ptr = MyBuffer; + for (Index = 0; Index < NumberOfWriteBlocks; Index += 1) { + MyLength = BlockSize; + Status = Fvb->Read (Fvb, Lba + Index, 0, &MyLength, Ptr); + if (EFI_ERROR (Status)) { + FreePool (MyBuffer); + return EFI_ABORTED; + } + + Ptr += MyLength; + } + // + // Overwrite the updating range data with + // the input buffer content + // + CopyMem (MyBuffer + Offset, Buffer, Length); + + // + // Try to keep the content of spare block + // Save spare block into a spare backup memory buffer (Sparebuffer) + // + SpareBufferSize = FtwDevice->SpareAreaLength; + SpareBuffer = AllocatePool (SpareBufferSize); + if (SpareBuffer == NULL) { + FreePool (MyBuffer); + return EFI_OUT_OF_RESOURCES; + } + + Ptr = SpareBuffer; + for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) { + MyLength = FtwDevice->SpareBlockSize; + Status = FtwDevice->FtwBackupFvb->Read ( + FtwDevice->FtwBackupFvb, + FtwDevice->FtwSpareLba + Index, + 0, + &MyLength, + Ptr + ); + if (EFI_ERROR (Status)) { + FreePool (MyBuffer); + FreePool (SpareBuffer); + return EFI_ABORTED; + } + + Ptr += MyLength; + } + // + // Write the memory buffer to spare block + // Do not assume Spare Block and Target Block have same block size + // + Status = FtwEraseSpareBlock (FtwDevice); + if (EFI_ERROR (Status)) { + FreePool (MyBuffer); + FreePool (SpareBuffer); + return EFI_ABORTED; + } + Ptr = MyBuffer; + for (Index = 0; MyBufferSize > 0; Index += 1) { + if (MyBufferSize > FtwDevice->SpareBlockSize) { + MyLength = FtwDevice->SpareBlockSize; + } else { + MyLength = MyBufferSize; + } + Status = FtwDevice->FtwBackupFvb->Write ( + FtwDevice->FtwBackupFvb, + FtwDevice->FtwSpareLba + Index, + 0, + &MyLength, + Ptr + ); + if (EFI_ERROR (Status)) { + FreePool (MyBuffer); + FreePool (SpareBuffer); + return EFI_ABORTED; + } + + Ptr += MyLength; + MyBufferSize -= MyLength; + } + // + // Free MyBuffer + // + FreePool (MyBuffer); + + // + // Set the SpareComplete in the FTW record, + // + MyOffset = (UINT8 *) Record - FtwDevice->FtwWorkSpace; + Status = FtwUpdateFvState ( + FtwDevice->FtwFvBlock, + FtwDevice->WorkBlockSize, + FtwDevice->FtwWorkSpaceLba, + FtwDevice->FtwWorkSpaceBase + MyOffset, + SPARE_COMPLETED + ); + if (EFI_ERROR (Status)) { + FreePool (SpareBuffer); + return EFI_ABORTED; + } + + Record->SpareComplete = 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 (This, Fvb, BlockSize); + if (EFI_ERROR (Status)) { + FreePool (SpareBuffer); + return EFI_ABORTED; + } + // + // Restore spare backup buffer into spare block , if no failure happened during FtwWrite. + // + Status = FtwEraseSpareBlock (FtwDevice); + if (EFI_ERROR (Status)) { + FreePool (SpareBuffer); + return EFI_ABORTED; + } + Ptr = SpareBuffer; + for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) { + MyLength = FtwDevice->SpareBlockSize; + Status = FtwDevice->FtwBackupFvb->Write ( + FtwDevice->FtwBackupFvb, + FtwDevice->FtwSpareLba + Index, + 0, + &MyLength, + Ptr + ); + if (EFI_ERROR (Status)) { + FreePool (SpareBuffer); + return EFI_ABORTED; + } + + Ptr += MyLength; + } + // + // All success. + // + FreePool (SpareBuffer); + + DEBUG ( + (EFI_D_INFO, + "Ftw: Write() success, (Lba:Offset)=(%lx:0x%x), Length: 0x%x\n", + Lba, + Offset, + Length) + ); + + return EFI_SUCCESS; +} + +/** + Restarts a previously interrupted write. The caller must provide the + block protocol needed to complete the interrupted write. + + @param This The pointer to this protocol instance. + @param FvBlockHandle The handle of FVB protocol that provides services for + reading, writing, and erasing the target block. + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_ACCESS_DENIED No pending writes exist + @retval EFI_NOT_FOUND FVB protocol not found by the handle + @retval EFI_ABORTED The function could not complete successfully + +**/ +EFI_STATUS +EFIAPI +FtwRestart ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + IN EFI_HANDLE FvBlockHandle + ) +{ + EFI_STATUS Status; + EFI_FTW_DEVICE *FtwDevice; + EFI_FAULT_TOLERANT_WRITE_HEADER *Header; + EFI_FAULT_TOLERANT_WRITE_RECORD *Record; + EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; + UINTN BlockSize; + UINTN NumberOfBlocks; + + FtwDevice = FTW_CONTEXT_FROM_THIS (This); + + Status = WorkSpaceRefresh (FtwDevice); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + Header = FtwDevice->FtwLastWriteHeader; + Record = FtwDevice->FtwLastWriteRecord; + + // + // Spare Complete but Destination not complete, + // Recover the targt block with the spare block. + // + Status = FtwGetFvbByHandle (FvBlockHandle, &Fvb); + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + + // + // Now, one FVB has one type of BlockSize + // + Status = Fvb->GetBlockSize (Fvb, 0, &BlockSize, &NumberOfBlocks); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Ftw: Restart(), Get block size - %r\n", Status)); + return EFI_ABORTED; + } + + // + // Check the COMPLETE flag of last write header + // + if (Header->Complete == FTW_VALID_STATE) { + return EFI_ACCESS_DENIED; + } + + // + // Check the flags of last write record + // + if (Record->DestinationComplete == FTW_VALID_STATE) { + return EFI_ACCESS_DENIED; + } + + if ((Record->SpareComplete != FTW_VALID_STATE)) { + return EFI_ABORTED; + } + + // + // Since the content has already backuped in spare block, the write is + // guaranteed to be completed with fault tolerant manner. + // + Status = FtwWriteRecord (This, Fvb, BlockSize); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + // + // Erase Spare block + // This is restart, no need to keep spareblock content. + // + Status = FtwEraseSpareBlock (FtwDevice); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + DEBUG ((EFI_D_INFO, "%a(): success\n", __FUNCTION__)); + return EFI_SUCCESS; +} + +/** + Aborts all previous allocated writes. + + @param This The pointer to this protocol instance. + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_ABORTED The function could not complete successfully. + @retval EFI_NOT_FOUND No allocated writes exist. + +**/ +EFI_STATUS +EFIAPI +FtwAbort ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This + ) +{ + EFI_STATUS Status; + UINTN Offset; + EFI_FTW_DEVICE *FtwDevice; + + FtwDevice = FTW_CONTEXT_FROM_THIS (This); + + Status = WorkSpaceRefresh (FtwDevice); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + if (FtwDevice->FtwLastWriteHeader->HeaderAllocated != FTW_VALID_STATE) { + return EFI_NOT_FOUND; + } + + if (FtwDevice->FtwLastWriteHeader->Complete == FTW_VALID_STATE) { + return EFI_NOT_FOUND; + } + // + // Update the complete state of the header as VALID and abort. + // + Offset = (UINT8 *) FtwDevice->FtwLastWriteHeader - FtwDevice->FtwWorkSpace; + Status = FtwUpdateFvState ( + FtwDevice->FtwFvBlock, + FtwDevice->WorkBlockSize, + FtwDevice->FtwWorkSpaceLba, + FtwDevice->FtwWorkSpaceBase + Offset, + WRITES_COMPLETED + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + FtwDevice->FtwLastWriteHeader->Complete = FTW_VALID_STATE; + + DEBUG ((EFI_D_INFO, "%a(): success\n", __FUNCTION__)); + return EFI_SUCCESS; +} + +/** + Starts a target block update. This records information about the 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. + + @param This The pointer to this protocol instance. + @param CallerId The GUID identifying the last write. + @param Lba The logical block address of the last write. + @param Offset The offset within the block of the last write. + @param Length The length of the last write. + @param PrivateDataSize bytes from the private data + stored for this write. + @param PrivateData A pointer to a buffer. The function will copy + @param Complete A Boolean value with TRUE indicating + that the write was completed. + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_ABORTED The function could not complete successfully + @retval EFI_NOT_FOUND No allocated writes exist + @retval EFI_BUFFER_TOO_SMALL Input buffer is not larget enough + +**/ +EFI_STATUS +EFIAPI +FtwGetLastWrite ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + OUT EFI_GUID *CallerId, + OUT EFI_LBA *Lba, + OUT UINTN *Offset, + OUT UINTN *Length, + IN OUT UINTN *PrivateDataSize, + OUT VOID *PrivateData, + OUT BOOLEAN *Complete + ) +{ + EFI_STATUS Status; + EFI_FTW_DEVICE *FtwDevice; + EFI_FAULT_TOLERANT_WRITE_HEADER *Header; + EFI_FAULT_TOLERANT_WRITE_RECORD *Record; + + if (!FeaturePcdGet(PcdFullFtwServiceEnable)) { + return EFI_UNSUPPORTED; + } + + FtwDevice = FTW_CONTEXT_FROM_THIS (This); + + Status = WorkSpaceRefresh (FtwDevice); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + Header = FtwDevice->FtwLastWriteHeader; + Record = FtwDevice->FtwLastWriteRecord; + + // + // If Header is incompleted and the last record has completed, then + // call Abort() to set the Header->Complete FLAG. + // + if ((Header->Complete != FTW_VALID_STATE) && + (Record->DestinationComplete == FTW_VALID_STATE) && + IsLastRecordOfWrites (Header, Record) + ) { + + Status = FtwAbort (This); + *Complete = TRUE; + return EFI_NOT_FOUND; + } + // + // If there is no write header/record, return not found. + // + if (Header->HeaderAllocated != FTW_VALID_STATE) { + *Complete = TRUE; + return EFI_NOT_FOUND; + } + // + // If this record SpareComplete has not set, then it can not restart. + // + if (Record->SpareComplete != FTW_VALID_STATE) { + Status = GetPreviousRecordOfWrites (Header, &Record); + if (EFI_ERROR (Status)) { + FtwAbort (This); + *Complete = TRUE; + return EFI_NOT_FOUND; + } + ASSERT (Record != NULL); + } + + // + // Fill all the requested values + // + CopyMem (CallerId, &Header->CallerId, sizeof (EFI_GUID)); + *Lba = Record->Lba; + *Offset = (UINTN) Record->Offset; + *Length = (UINTN) Record->Length; + *Complete = (BOOLEAN) (Record->DestinationComplete == FTW_VALID_STATE); + + if (*PrivateDataSize < Header->PrivateDataSize) { + *PrivateDataSize = (UINTN) Header->PrivateDataSize; + PrivateData = NULL; + Status = EFI_BUFFER_TOO_SMALL; + } else { + *PrivateDataSize = (UINTN) Header->PrivateDataSize; + CopyMem (PrivateData, Record + 1, *PrivateDataSize); + Status = EFI_SUCCESS; + } + + DEBUG ((EFI_D_INFO, "%a(): success\n", __FUNCTION__)); + + return Status; +} + -- cgit 1.2.3-korg