summaryrefslogtreecommitdiff
path: root/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe
diff options
context:
space:
mode:
authorGuo Mang <mang.guo@intel.com>2017-04-27 11:05:07 +0800
committerGuo Mang <mang.guo@intel.com>2017-04-27 11:05:07 +0800
commitc23f114d3cfbb29b8734b87213d1ec0de404197b (patch)
tree4f3612573be055139a88213559212a40b7862fee /Core/MdeModulePkg/Universal/FaultTolerantWriteDxe
parent001e57a103fce87245bfb7ae9c32ffb499a64135 (diff)
downloadedk2-platforms-c23f114d3cfbb29b8734b87213d1ec0de404197b.tar.xz
MdeModulePkg: Move to new location
Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Guo Mang <mang.guo@intel.com>
Diffstat (limited to 'Core/MdeModulePkg/Universal/FaultTolerantWriteDxe')
-rw-r--r--Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.c893
-rw-r--r--Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.h769
-rw-r--r--Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.c252
-rw-r--r--Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf91
-rw-r--r--Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.uni23
-rw-r--r--Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxeExtra.uni19
-rw-r--r--Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.c650
-rw-r--r--Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.inf98
-rw-r--r--Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmCommon.h80
-rw-r--r--Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.c562
-rw-r--r--Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.h202
-rw-r--r--Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.inf64
-rw-r--r--Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.uni24
-rw-r--r--Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxeExtra.uni19
-rw-r--r--Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FtwMisc.c1384
-rw-r--r--Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/SmmFaultTolerantWriteDxe.uni24
-rw-r--r--Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/SmmFaultTolerantWriteDxeExtra.uni19
-rw-r--r--Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/UpdateWorkingBlock.c619
18 files changed, 5792 insertions, 0 deletions
diff --git a/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.c b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.c
new file mode 100644
index 0000000000..49e747b901
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.c
@@ -0,0 +1,893 @@
+/** @file
+
+ These are the common Fault Tolerant Write (FTW) functions that are shared
+ by DXE FTW driver and SMM FTW driver.
+
+Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.<BR>
+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.
+
+**/
+
+#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;
+}
+
diff --git a/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.h b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.h
new file mode 100644
index 0000000000..be6929c654
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.h
@@ -0,0 +1,769 @@
+/** @file
+
+ The internal header file includes the common header files, defines
+ internal structure and functions used by Ftw module.
+
+Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+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.
+
+**/
+
+#ifndef _EFI_FAULT_TOLERANT_WRITE_H_
+#define _EFI_FAULT_TOLERANT_WRITE_H_
+
+#include <PiDxe.h>
+
+#include <Guid/SystemNvDataGuid.h>
+#include <Guid/ZeroGuid.h>
+#include <Protocol/FaultTolerantWrite.h>
+#include <Protocol/FirmwareVolumeBlock.h>
+#include <Protocol/SwapAddressRange.h>
+
+#include <Library/PcdLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/ReportStatusCodeLib.h>
+
+//
+// Flash erase polarity is 1
+//
+#define FTW_ERASE_POLARITY 1
+
+#define FTW_ERASED_BYTE ((UINT8) (255))
+#define FTW_POLARITY_REVERT ((UINT8) (255))
+
+#define HEADER_ALLOCATED 0x1
+#define WRITES_ALLOCATED 0x2
+#define WRITES_COMPLETED 0x4
+
+#define BOOT_BLOCK_UPDATE 0x1
+#define SPARE_COMPLETED 0x2
+#define DEST_COMPLETED 0x4
+
+#define FTW_BLOCKS(Length, BlockSize) ((UINTN) ((Length) / (BlockSize) + (((Length) & ((BlockSize) - 1)) ? 1 : 0)))
+
+#define FTW_DEVICE_SIGNATURE SIGNATURE_32 ('F', 'T', 'W', 'D')
+
+//
+// EFI Fault tolerant protocol private data structure
+//
+typedef struct {
+ UINTN Signature;
+ EFI_HANDLE Handle;
+ EFI_FAULT_TOLERANT_WRITE_PROTOCOL FtwInstance;
+ EFI_PHYSICAL_ADDRESS WorkSpaceAddress; // Base address of working space range in flash.
+ EFI_PHYSICAL_ADDRESS SpareAreaAddress; // Base address of spare range in flash.
+ UINTN WorkSpaceLength; // Size of working space range in flash.
+ UINTN NumberOfWorkSpaceBlock; // Number of the blocks in work block for work space.
+ UINTN WorkBlockSize; // Block size in bytes of the work blocks in flash
+ UINTN SpareAreaLength; // Size of spare range in flash.
+ UINTN NumberOfSpareBlock; // Number of the blocks in spare block.
+ UINTN SpareBlockSize; // Block size in bytes of the spare blocks in flash
+ EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *FtwWorkSpaceHeader;// Pointer to Working Space Header in memory buffer
+ EFI_FAULT_TOLERANT_WRITE_HEADER *FtwLastWriteHeader;// Pointer to last record header in memory buffer
+ EFI_FAULT_TOLERANT_WRITE_RECORD *FtwLastWriteRecord;// Pointer to last record in memory buffer
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FtwFvBlock; // FVB of working block
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FtwBackupFvb; // FVB of spare block
+ EFI_LBA FtwSpareLba; // Start LBA of spare block
+ EFI_LBA FtwWorkBlockLba; // Start LBA of working block that contains working space in its last block.
+ UINTN NumberOfWorkBlock; // Number of the blocks in work block.
+ EFI_LBA FtwWorkSpaceLba; // Start LBA of working space
+ UINTN FtwWorkSpaceBase; // Offset into the FtwWorkSpaceLba block.
+ UINTN FtwWorkSpaceSize; // Size of working space range that stores write record.
+ EFI_LBA FtwWorkSpaceLbaInSpare; // Start LBA of working space in spare block.
+ UINTN FtwWorkSpaceBaseInSpare;// Offset into the FtwWorkSpaceLbaInSpare block.
+ UINT8 *FtwWorkSpace; // Point to Work Space in memory buffer
+ //
+ // Following a buffer of FtwWorkSpace[FTW_WORK_SPACE_SIZE],
+ // Allocated with EFI_FTW_DEVICE.
+ //
+} EFI_FTW_DEVICE;
+
+#define FTW_CONTEXT_FROM_THIS(a) CR (a, EFI_FTW_DEVICE, FtwInstance, FTW_DEVICE_SIGNATURE)
+
+//
+// Driver entry point
+//
+/**
+ This function is the entry point of the Fault Tolerant Write driver.
+
+ @param ImageHandle A handle for the image that is initializing this driver
+ @param SystemTable A pointer to the EFI system table
+
+ @return EFI_SUCCESS FTW has finished the initialization
+ @retval EFI_NOT_FOUND Locate FVB protocol error
+ @retval EFI_OUT_OF_RESOURCES Allocate memory error
+ @retval EFI_VOLUME_CORRUPTED Firmware volume is error
+ @retval EFI_ABORTED FTW initialization error
+
+**/
+EFI_STATUS
+EFIAPI
+InitializeFaultTolerantWrite (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ );
+
+//
+// Fault Tolerant Write Protocol API
+//
+
+/**
+ Query the largest block that may be updated in a fault tolerant manner.
+
+
+ @param This Indicates a pointer to the calling context.
+ @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
+ );
+
+/**
+ 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 Indicates a pointer to the calling context.
+ @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
+ );
+
+/**
+ 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 Calling context
+ @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
+ );
+
+/**
+ Restarts a previously interrupted write. The caller must provide the
+ block protocol needed to complete the interrupted write.
+
+ @param This Calling context.
+ @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
+ );
+
+/**
+ Aborts all previous allocated writes.
+
+ @param This Calling context
+
+ @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
+ );
+
+/**
+ 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 Indicates a pointer to the calling context.
+ @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
+ );
+
+/**
+ Erase spare block.
+
+ @param FtwDevice The private data of FTW driver
+
+ @retval EFI_SUCCESS The erase request was successfully completed.
+ @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state.
+ @retval EFI_DEVICE_ERROR The block device is not functioning
+ correctly and could not be written.
+ The firmware device may have been
+ partially erased.
+ @retval EFI_INVALID_PARAMETER One or more of the LBAs listed
+ in the variable argument list do
+ not exist in the firmware volume.
+
+
+**/
+EFI_STATUS
+FtwEraseSpareBlock (
+ IN EFI_FTW_DEVICE *FtwDevice
+ );
+
+/**
+ Retrieve the proper FVB protocol interface by HANDLE.
+
+
+ @param FvBlockHandle The handle of FVB protocol that provides services for
+ reading, writing, and erasing the target block.
+ @param FvBlock The interface of FVB protocol
+
+ @retval EFI_SUCCESS The function completed successfully
+ @retval EFI_ABORTED The function could not complete successfully
+
+**/
+EFI_STATUS
+FtwGetFvbByHandle (
+ IN EFI_HANDLE FvBlockHandle,
+ OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock
+ );
+
+/**
+
+ Is it in working block?
+
+ @param FtwDevice The private data of FTW driver
+ @param FvBlock Fvb protocol instance
+ @param Lba The block specified
+
+ @return A BOOLEAN value indicating in working block or not.
+
+**/
+BOOLEAN
+IsWorkingBlock (
+ EFI_FTW_DEVICE *FtwDevice,
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
+ EFI_LBA Lba
+ );
+
+/**
+
+ Is it in boot block?
+
+ @param FtwDevice The private data of FTW driver
+ @param FvBlock Fvb protocol instance
+
+ @return A BOOLEAN value indicating in boot block or not.
+
+**/
+BOOLEAN
+IsBootBlock (
+ EFI_FTW_DEVICE *FtwDevice,
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock
+ );
+
+/**
+ Copy the content of spare block to a target block. Size is FTW_BLOCK_SIZE.
+ Spare block is accessed by FTW backup FVB protocol interface.
+ Target block is accessed by FvBlock protocol interface.
+
+
+ @param FtwDevice The private data of FTW driver
+ @param FvBlock FVB Protocol interface to access target block
+ @param Lba Lba of the target block
+ @param BlockSize The size of the block
+ @param NumberOfBlocks The number of consecutive blocks starting with Lba
+
+ @retval EFI_SUCCESS Spare block content is copied to target block
+ @retval EFI_INVALID_PARAMETER Input parameter error
+ @retval EFI_OUT_OF_RESOURCES Allocate memory error
+ @retval EFI_ABORTED The function could not complete successfully
+
+**/
+EFI_STATUS
+FlushSpareBlockToTargetBlock (
+ EFI_FTW_DEVICE *FtwDevice,
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
+ EFI_LBA Lba,
+ UINTN BlockSize,
+ UINTN NumberOfBlocks
+ );
+
+/**
+ Copy the content of spare block to working block. Size is FTW_BLOCK_SIZE.
+ Spare block is accessed by FTW backup FVB protocol interface. LBA is
+ FtwDevice->FtwSpareLba.
+ Working block is accessed by FTW working FVB protocol interface. LBA is
+ FtwDevice->FtwWorkBlockLba.
+
+ Since the working block header is important when FTW initializes, the
+ state of the operation should be handled carefully. The Crc value is
+ calculated without STATE element.
+
+ @param FtwDevice The private data of FTW driver
+
+ @retval EFI_SUCCESS Spare block content is copied to target block
+ @retval EFI_OUT_OF_RESOURCES Allocate memory error
+ @retval EFI_ABORTED The function could not complete successfully
+
+**/
+EFI_STATUS
+FlushSpareBlockToWorkingBlock (
+ EFI_FTW_DEVICE *FtwDevice
+ );
+
+/**
+ Copy the content of spare block to a boot block. Size is FTW_BLOCK_SIZE.
+ Spare block is accessed by FTW working FVB protocol interface.
+ Target block is accessed by FvBlock protocol interface.
+
+ FTW will do extra work on boot block update.
+ FTW should depend on a protocol of EFI_ADDRESS_RANGE_SWAP_PROTOCOL,
+ which is produced by a chipset driver.
+ FTW updating boot block steps may be:
+ 1. GetRangeLocation(), if the Range is inside the boot block, FTW know
+ that boot block will be update. It shall add a FLAG in the working block.
+ 2. When spare block is ready,
+ 3. SetSwapState(SWAPPED)
+ 4. erasing boot block,
+ 5. programming boot block until the boot block is ok.
+ 6. SetSwapState(UNSWAPPED)
+ FTW shall not allow to update boot block when battery state is error.
+
+ @param FtwDevice The private data of FTW driver
+
+ @retval EFI_SUCCESS Spare block content is copied to boot block
+ @retval EFI_INVALID_PARAMETER Input parameter error
+ @retval EFI_OUT_OF_RESOURCES Allocate memory error
+ @retval EFI_ABORTED The function could not complete successfully
+
+**/
+EFI_STATUS
+FlushSpareBlockToBootBlock (
+ EFI_FTW_DEVICE *FtwDevice
+ );
+
+/**
+ 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.
+
+
+ @param FvBlock FVB Protocol interface to access SrcBlock and DestBlock
+ @param BlockSize The size of the block
+ @param Lba Lba of a block
+ @param Offset Offset on the Lba
+ @param NewBit New value that will override the old value if it can be change
+
+ @retval EFI_SUCCESS A state bit has been updated successfully
+ @retval Others Access block device error.
+ Notes:
+ Assume all bits of State are inside the same BYTE.
+ @retval EFI_ABORTED Read block fail
+
+**/
+EFI_STATUS
+FtwUpdateFvState (
+ IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
+ IN UINTN BlockSize,
+ IN EFI_LBA Lba,
+ IN UINTN Offset,
+ IN UINT8 NewBit
+ );
+
+/**
+ Get the last Write Header pointer.
+ The last write header is the header whose 'complete' state hasn't been set.
+ After all, this header may be a EMPTY header entry for next Allocate.
+
+
+ @param FtwWorkSpaceHeader Pointer of the working block header
+ @param FtwWorkSpaceSize Size of the work space
+ @param FtwWriteHeader Pointer to retrieve the last write header
+
+ @retval EFI_SUCCESS Get the last write record successfully
+ @retval EFI_ABORTED The FTW work space is damaged
+
+**/
+EFI_STATUS
+FtwGetLastWriteHeader (
+ IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *FtwWorkSpaceHeader,
+ IN UINTN FtwWorkSpaceSize,
+ OUT EFI_FAULT_TOLERANT_WRITE_HEADER **FtwWriteHeader
+ );
+
+/**
+ Get the last Write Record pointer. The last write Record is the Record
+ whose DestinationCompleted state hasn't been set. After all, this Record
+ may be a EMPTY record entry for next write.
+
+
+ @param FtwWriteHeader Pointer to the write record header
+ @param FtwWriteRecord Pointer to retrieve the last write record
+
+ @retval EFI_SUCCESS Get the last write record successfully
+ @retval EFI_ABORTED The FTW work space is damaged
+
+**/
+EFI_STATUS
+FtwGetLastWriteRecord (
+ IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwWriteHeader,
+ OUT EFI_FAULT_TOLERANT_WRITE_RECORD **FtwWriteRecord
+ );
+
+/**
+ To check if FtwRecord is the first record of FtwHeader.
+
+ @param FtwHeader Pointer to the write record header
+ @param FtwRecord Pointer to the write record
+
+ @retval TRUE FtwRecord is the first Record of the FtwHeader
+ @retval FALSE FtwRecord is not the first Record of the FtwHeader
+
+**/
+BOOLEAN
+IsFirstRecordOfWrites (
+ IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader,
+ IN EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord
+ );
+
+/**
+ To check if FtwRecord is the last record of FtwHeader. Because the
+ FtwHeader has NumberOfWrites & PrivateDataSize, the FtwRecord can be
+ determined if it is the last record of FtwHeader.
+
+ @param FtwHeader Pointer to the write record header
+ @param FtwRecord Pointer to the write record
+
+ @retval TRUE FtwRecord is the last Record of the FtwHeader
+ @retval FALSE FtwRecord is not the last Record of the FtwHeader
+
+**/
+BOOLEAN
+IsLastRecordOfWrites (
+ IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader,
+ IN EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord
+ );
+
+/**
+ To check if FtwRecord is the first record of FtwHeader.
+
+ @param FtwHeader Pointer to the write record header
+ @param FtwRecord Pointer to retrieve the previous write record
+
+ @retval EFI_ACCESS_DENIED Input record is the first record, no previous record is return.
+ @retval EFI_SUCCESS The previous write record is found.
+
+**/
+EFI_STATUS
+GetPreviousRecordOfWrites (
+ IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader,
+ IN OUT EFI_FAULT_TOLERANT_WRITE_RECORD **FtwRecord
+ );
+
+/**
+
+ Check whether a flash buffer is erased.
+
+ @param Buffer Buffer to check
+ @param BufferSize Size of the buffer
+
+ @return A BOOLEAN value indicating erased or not.
+
+**/
+BOOLEAN
+IsErasedFlashBuffer (
+ IN UINT8 *Buffer,
+ IN UINTN BufferSize
+ );
+/**
+ Initialize a work space when there is no work space.
+
+ @param WorkingHeader Pointer of working block header
+
+ @retval EFI_SUCCESS The function completed successfully
+ @retval EFI_ABORTED The function could not complete successfully.
+
+**/
+EFI_STATUS
+InitWorkSpaceHeader (
+ IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader
+ );
+/**
+ Read from working block to refresh the work space in memory.
+
+ @param FtwDevice Point to private data of FTW driver
+
+ @retval EFI_SUCCESS The function completed successfully
+ @retval EFI_ABORTED The function could not complete successfully.
+
+**/
+EFI_STATUS
+WorkSpaceRefresh (
+ IN EFI_FTW_DEVICE *FtwDevice
+ );
+/**
+ Check to see if it is a valid work space.
+
+
+ @param WorkingHeader Pointer of working block header
+
+ @retval EFI_SUCCESS The function completed successfully
+ @retval EFI_ABORTED The function could not complete successfully.
+
+**/
+BOOLEAN
+IsValidWorkSpace (
+ IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader
+ );
+/**
+ Reclaim the work space on the working block.
+
+ @param FtwDevice Point to private data of FTW driver
+ @param PreserveRecord Whether to preserve the working record is needed
+
+ @retval EFI_SUCCESS The function completed successfully
+ @retval EFI_OUT_OF_RESOURCES Allocate memory error
+ @retval EFI_ABORTED The function could not complete successfully
+
+**/
+EFI_STATUS
+FtwReclaimWorkSpace (
+ IN EFI_FTW_DEVICE *FtwDevice,
+ IN BOOLEAN PreserveRecord
+ );
+
+/**
+
+ Get firmware volume block by address.
+
+
+ @param Address Address specified the block
+ @param FvBlock The block caller wanted
+
+ @retval EFI_SUCCESS The protocol instance if found.
+ @retval EFI_NOT_FOUND Block not found
+
+**/
+EFI_HANDLE
+GetFvbByAddress (
+ IN EFI_PHYSICAL_ADDRESS Address,
+ OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock
+ );
+
+/**
+ Retrieve the proper Swap Address Range protocol interface.
+
+ @param[out] SarProtocol The interface of SAR protocol
+
+ @retval EFI_SUCCESS The SAR protocol instance was found and returned in SarProtocol.
+ @retval EFI_NOT_FOUND The SAR protocol instance was not found.
+ @retval EFI_INVALID_PARAMETER SarProtocol is NULL.
+
+**/
+EFI_STATUS
+FtwGetSarProtocol (
+ OUT VOID **SarProtocol
+ );
+
+/**
+ Function returns an array of handles that support the FVB protocol
+ in a buffer allocated from pool.
+
+ @param[out] NumberHandles The number of handles returned in Buffer.
+ @param[out] Buffer A pointer to the buffer to return the requested
+ array of handles that support FVB protocol.
+
+ @retval EFI_SUCCESS The array of handles was returned in Buffer, and the number of
+ handles in Buffer was returned in NumberHandles.
+ @retval EFI_NOT_FOUND No FVB handle was found.
+ @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the matching results.
+ @retval EFI_INVALID_PARAMETER NumberHandles is NULL or Buffer is NULL.
+
+**/
+EFI_STATUS
+GetFvbCountAndBuffer (
+ OUT UINTN *NumberHandles,
+ OUT EFI_HANDLE **Buffer
+ );
+
+
+/**
+ Allocate private data for FTW driver and initialize it.
+
+ @param[out] FtwData Pointer to the FTW device structure
+
+ @retval EFI_SUCCESS Initialize the FTW device successfully.
+ @retval EFI_OUT_OF_RESOURCES Allocate memory error
+ @retval EFI_INVALID_PARAMETER Workspace or Spare block does not exist
+
+**/
+EFI_STATUS
+InitFtwDevice (
+ OUT EFI_FTW_DEVICE **FtwData
+ );
+
+
+/**
+ Initialization for Fault Tolerant Write is done in this handler.
+
+ @param[in, out] FtwDevice Pointer to the FTW device structure
+
+ @retval EFI_SUCCESS Initialize the FTW protocol successfully.
+ @retval EFI_NOT_FOUND No proper FVB protocol was found.
+
+**/
+EFI_STATUS
+InitFtwProtocol (
+ IN OUT EFI_FTW_DEVICE *FtwDevice
+ );
+
+/**
+ Initialize a local work space header.
+
+ Since Signature and WriteQueueSize have been known, Crc can be calculated out,
+ then the work space header will be fixed.
+**/
+VOID
+InitializeLocalWorkSpaceHeader (
+ VOID
+ );
+
+/**
+ Read work space data from work block or spare block.
+
+ @param FvBlock FVB Protocol interface to access the block.
+ @param BlockSize The size of the block.
+ @param Lba Lba of the block.
+ @param Offset The offset within the block.
+ @param Length The number of bytes to read from the block.
+ @param Buffer The data is read.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED The function could not complete successfully.
+
+**/
+EFI_STATUS
+ReadWorkSpaceData (
+ IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
+ IN UINTN BlockSize,
+ IN EFI_LBA Lba,
+ IN UINTN Offset,
+ IN UINTN Length,
+ OUT UINT8 *Buffer
+ );
+
+/**
+ Write data to work block.
+
+ @param FvBlock FVB Protocol interface to access the block.
+ @param BlockSize The size of the block.
+ @param Lba Lba of the block.
+ @param Offset The offset within the block to place the data.
+ @param Length The number of bytes to write to the block.
+ @param Buffer The data to write.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED The function could not complete successfully.
+
+**/
+EFI_STATUS
+WriteWorkSpaceData (
+ IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
+ IN UINTN BlockSize,
+ IN EFI_LBA Lba,
+ IN UINTN Offset,
+ IN UINTN Length,
+ IN UINT8 *Buffer
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.c b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.c
new file mode 100644
index 0000000000..3fa5431cab
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.c
@@ -0,0 +1,252 @@
+/** @file
+
+ This is a simple fault tolerant write driver.
+
+ This boot service protocol only 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.
+
+ The implementation uses an FTW (Fault Tolerant Write) Work Space.
+ This work space is a memory copy of the work space on the Working Block,
+ the size of the work space is the FTW_WORK_SPACE_SIZE bytes.
+
+ The work space stores each write record as EFI_FTW_RECORD structure.
+ The spare block stores the write buffer before write to the target block.
+
+ The write record has three states to specify the different phase of write operation.
+ 1) WRITE_ALLOCATED is that the record is allocated in write space.
+ The information of write operation is stored in write record structure.
+ 2) SPARE_COMPLETED is that the data from write buffer is writed into the spare block as the backup.
+ 3) WRITE_COMPLETED is that the data is copied from the spare block to the target block.
+
+ This driver operates the data as the whole size of spare block.
+ It first read the SpareAreaLength data from the target block into the spare memory buffer.
+ Then copy the write buffer data into the spare memory buffer.
+ Then write the spare memory buffer into the spare block.
+ Final copy the data from the spare block to the target block.
+
+ To make this drive work well, the following conditions must be satisfied:
+ 1. The write NumBytes data must be fit within Spare area.
+ Offset + NumBytes <= SpareAreaLength
+ 2. The whole flash range has the same block size.
+ 3. Working block is an area which contains working space in its last block and has the same size as spare block.
+ 4. Working Block area must be in the single one Firmware Volume Block range which FVB protocol is produced on.
+ 5. Spare area must be in the single one Firmware Volume Block range which FVB protocol is produced on.
+ 6. Any write data area (SpareAreaLength Area) which the data will be written into must be
+ in the single one Firmware Volume Block range which FVB protocol is produced on.
+ 7. If write data area (such as Variable range) is enlarged, the spare area range must be enlarged.
+ The spare area must be enough large to store the write data before write them into the target range.
+ If one of them is not satisfied, FtwWrite may fail.
+ Usually, Spare area only takes one block. That's SpareAreaLength = BlockSize, NumberOfSpareBlock = 1.
+
+Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+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.
+
+**/
+
+#include "FaultTolerantWrite.h"
+EFI_EVENT mFvbRegistration = NULL;
+
+
+/**
+ Retrieve the FVB protocol interface by HANDLE.
+
+ @param[in] FvBlockHandle The handle of FVB protocol that provides services for
+ reading, writing, and erasing the target block.
+ @param[out] FvBlock The interface of FVB protocol
+
+ @retval EFI_SUCCESS The interface information for the specified protocol was returned.
+ @retval EFI_UNSUPPORTED The device does not support the FVB protocol.
+ @retval EFI_INVALID_PARAMETER FvBlockHandle is not a valid EFI_HANDLE or FvBlock is NULL.
+
+**/
+EFI_STATUS
+FtwGetFvbByHandle (
+ IN EFI_HANDLE FvBlockHandle,
+ OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock
+ )
+{
+ //
+ // To get the FVB protocol interface on the handle
+ //
+ return gBS->HandleProtocol (
+ FvBlockHandle,
+ &gEfiFirmwareVolumeBlockProtocolGuid,
+ (VOID **) FvBlock
+ );
+}
+
+/**
+ Retrieve the Swap Address Range protocol interface.
+
+ @param[out] SarProtocol The interface of SAR protocol
+
+ @retval EFI_SUCCESS The SAR protocol instance was found and returned in SarProtocol.
+ @retval EFI_NOT_FOUND The SAR protocol instance was not found.
+ @retval EFI_INVALID_PARAMETER SarProtocol is NULL.
+
+**/
+EFI_STATUS
+FtwGetSarProtocol (
+ OUT VOID **SarProtocol
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Locate Swap Address Range protocol
+ //
+ Status = gBS->LocateProtocol (
+ &gEfiSwapAddressRangeProtocolGuid,
+ NULL,
+ SarProtocol
+ );
+ return Status;
+}
+
+/**
+ Function returns an array of handles that support the FVB protocol
+ in a buffer allocated from pool.
+
+ @param[out] NumberHandles The number of handles returned in Buffer.
+ @param[out] Buffer A pointer to the buffer to return the requested
+ array of handles that support FVB protocol.
+
+ @retval EFI_SUCCESS The array of handles was returned in Buffer, and the number of
+ handles in Buffer was returned in NumberHandles.
+ @retval EFI_NOT_FOUND No FVB handle was found.
+ @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the matching results.
+ @retval EFI_INVALID_PARAMETER NumberHandles is NULL or Buffer is NULL.
+
+**/
+EFI_STATUS
+GetFvbCountAndBuffer (
+ OUT UINTN *NumberHandles,
+ OUT EFI_HANDLE **Buffer
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Locate all handles of Fvb protocol
+ //
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiFirmwareVolumeBlockProtocolGuid,
+ NULL,
+ NumberHandles,
+ Buffer
+ );
+ return Status;
+}
+
+
+/**
+ Firmware Volume Block Protocol notification event handler.
+
+ @param[in] Event Event whose notification function is being invoked.
+ @param[in] Context Pointer to the notification function's context.
+
+**/
+VOID
+EFIAPI
+FvbNotificationEvent (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ EFI_FAULT_TOLERANT_WRITE_PROTOCOL *FtwProtocol;
+ EFI_FTW_DEVICE *FtwDevice;
+
+ //
+ // Just return to avoid installing FaultTolerantWriteProtocol again
+ // if Fault Tolerant Write protocol has been installed.
+ //
+ Status = gBS->LocateProtocol (
+ &gEfiFaultTolerantWriteProtocolGuid,
+ NULL,
+ (VOID **) &FtwProtocol
+ );
+ if (!EFI_ERROR (Status)) {
+ return ;
+ }
+
+ //
+ // Found proper FVB protocol and initialize FtwDevice for protocol installation
+ //
+ FtwDevice = (EFI_FTW_DEVICE *)Context;
+ Status = InitFtwProtocol (FtwDevice);
+ if (EFI_ERROR(Status)) {
+ return ;
+ }
+
+ //
+ // Install protocol interface
+ //
+ Status = gBS->InstallProtocolInterface (
+ &FtwDevice->Handle,
+ &gEfiFaultTolerantWriteProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &FtwDevice->FtwInstance
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ Status = gBS->CloseEvent (Event);
+ ASSERT_EFI_ERROR (Status);
+
+ return;
+}
+
+
+/**
+ This function is the entry point of the Fault Tolerant Write driver.
+
+ @param[in] ImageHandle A handle for the image that is initializing this driver
+ @param[in] SystemTable A pointer to the EFI system table
+
+ @retval EFI_SUCCESS The initialization finished successfully.
+ @retval EFI_OUT_OF_RESOURCES Allocate memory error
+ @retval EFI_INVALID_PARAMETER Workspace or Spare block does not exist
+
+**/
+EFI_STATUS
+EFIAPI
+FaultTolerantWriteInitialize (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ EFI_FTW_DEVICE *FtwDevice;
+
+ FtwDevice = NULL;
+
+ //
+ // Allocate private data structure for FTW protocol and do some initialization
+ //
+ Status = InitFtwDevice (&FtwDevice);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ //
+ // Register FvbNotificationEvent () notify function.
+ //
+ EfiCreateProtocolNotifyEvent (
+ &gEfiFirmwareVolumeBlockProtocolGuid,
+ TPL_CALLBACK,
+ FvbNotificationEvent,
+ (VOID *)FtwDevice,
+ &mFvbRegistration
+ );
+
+ return EFI_SUCCESS;
+}
diff --git a/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf
new file mode 100644
index 0000000000..5649241f71
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf
@@ -0,0 +1,91 @@
+## @file
+# Fault Tolerant Write Dxe Driver.
+#
+# This driver installs Fault Tolerant Write (FTW) protocol,
+# which provides fault tolerant write capability for block devices.
+# Its implementation depends on the full functionality FVB protocol that support read, write/erase flash access.
+#
+# Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+#
+# 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.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = FaultTolerantWriteDxe
+ MODULE_UNI_FILE = FaultTolerantWriteDxe.uni
+ FILE_GUID = FE5CEA76-4F72-49e8-986F-2CD899DFFE5D
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = FaultTolerantWriteInitialize
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources]
+ FtwMisc.c
+ UpdateWorkingBlock.c
+ FaultTolerantWrite.c
+ FaultTolerantWriteDxe.c
+ FaultTolerantWrite.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ UefiBootServicesTableLib
+ MemoryAllocationLib
+ BaseMemoryLib
+ UefiDriverEntryPoint
+ DebugLib
+ UefiLib
+ PcdLib
+ ReportStatusCodeLib
+
+[Guids]
+ #
+ # Signature in EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
+ #
+ ## CONSUMES ## GUID
+ ## PRODUCES ## GUID
+ gEdkiiWorkingBlockSignatureGuid
+
+[Protocols]
+ gEfiSwapAddressRangeProtocolGuid | gEfiMdeModulePkgTokenSpaceGuid.PcdFullFtwServiceEnable ## SOMETIMES_CONSUMES
+ ## NOTIFY
+ ## CONSUMES
+ gEfiFirmwareVolumeBlockProtocolGuid
+ gEfiFaultTolerantWriteProtocolGuid ## PRODUCES
+
+[FeaturePcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFullFtwServiceEnable ## CONSUMES
+
+[Pcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase ## SOMETIMES_CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase64 ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase ## SOMETIMES_CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase64 ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize ## CONSUMES
+
+#
+# gBS->CalculateCrc32() is consumed in EntryPoint.
+# PI spec said: When the DXE Foundation is notified that the EFI_RUNTIME_ARCH_PROTOCOL
+# has been installed, then the Boot Service CalculateCrc32() is available.
+# So add gEfiRuntimeArchProtocolGuid Depex here.
+#
+[Depex]
+ gEfiFirmwareVolumeBlockProtocolGuid AND gEfiRuntimeArchProtocolGuid
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ FaultTolerantWriteDxeExtra.uni
diff --git a/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.uni b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.uni
new file mode 100644
index 0000000000..e828c227a6
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.uni
@@ -0,0 +1,23 @@
+// /** @file
+// Fault Tolerant Write Dxe Driver.
+//
+// This driver installs Fault Tolerant Write (FTW) protocol,
+// which provides fault tolerant write capability for block devices.
+// Its implementation depends on the full functionality FVB protocol that support read, write/erase flash access.
+//
+// Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// 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.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Fault Tolerant Write Dxe Driver."
+
+#string STR_MODULE_DESCRIPTION #language en-US "Installs Fault Tolerant Write (FTW) protocol, which provides fault tolerant write capability for block devices. Its implementation depends on the full functionality FVB protocol that support read, write/erase flash access."
+
diff --git a/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxeExtra.uni b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxeExtra.uni
new file mode 100644
index 0000000000..9852e42699
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxeExtra.uni
@@ -0,0 +1,19 @@
+// /** @file
+// FaultTolerantWriteDxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// 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.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"Fault Tolerant Flash Write DXE Driver"
+
+
diff --git a/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.c b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.c
new file mode 100644
index 0000000000..1e75328f9b
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.c
@@ -0,0 +1,650 @@
+/** @file
+
+ This is a simple fault tolerant write driver that is intended to use in the SMM environment.
+
+ This boot service protocol only 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.
+
+ The implementation uses an FTW (Fault Tolerant Write) Work Space.
+ This work space is a memory copy of the work space on the Working Block,
+ the size of the work space is the FTW_WORK_SPACE_SIZE bytes.
+
+ The work space stores each write record as EFI_FTW_RECORD structure.
+ The spare block stores the write buffer before write to the target block.
+
+ The write record has three states to specify the different phase of write operation.
+ 1) WRITE_ALLOCATED is that the record is allocated in write space.
+ The information of write operation is stored in write record structure.
+ 2) SPARE_COMPLETED is that the data from write buffer is writed into the spare block as the backup.
+ 3) WRITE_COMPLETED is that the data is copied from the spare block to the target block.
+
+ This driver operates the data as the whole size of spare block.
+ It first read the SpareAreaLength data from the target block into the spare memory buffer.
+ Then copy the write buffer data into the spare memory buffer.
+ Then write the spare memory buffer into the spare block.
+ Final copy the data from the spare block to the target block.
+
+ To make this drive work well, the following conditions must be satisfied:
+ 1. The write NumBytes data must be fit within Spare area.
+ Offset + NumBytes <= SpareAreaLength
+ 2. The whole flash range has the same block size.
+ 3. Working block is an area which contains working space in its last block and has the same size as spare block.
+ 4. Working Block area must be in the single one Firmware Volume Block range which FVB protocol is produced on.
+ 5. Spare area must be in the single one Firmware Volume Block range which FVB protocol is produced on.
+ 6. Any write data area (SpareAreaLength Area) which the data will be written into must be
+ in the single one Firmware Volume Block range which FVB protocol is produced on.
+ 7. If write data area (such as Variable range) is enlarged, the spare area range must be enlarged.
+ The spare area must be enough large to store the write data before write them into the target range.
+ If one of them is not satisfied, FtwWrite may fail.
+ Usually, Spare area only takes one block. That's SpareAreaLength = BlockSize, NumberOfSpareBlock = 1.
+
+ Caution: This module requires additional review when modified.
+ This driver need to make sure the CommBuffer is not in the SMRAM range.
+
+Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.<BR>
+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.
+
+**/
+
+#include <PiSmm.h>
+#include <Library/SmmServicesTableLib.h>
+#include <Library/SmmMemLib.h>
+#include <Protocol/SmmSwapAddressRange.h>
+#include "FaultTolerantWrite.h"
+#include "FaultTolerantWriteSmmCommon.h"
+#include <Protocol/SmmEndOfDxe.h>
+
+EFI_EVENT mFvbRegistration = NULL;
+EFI_FTW_DEVICE *mFtwDevice = NULL;
+
+///
+/// The flag to indicate whether the platform has left the DXE phase of execution.
+///
+BOOLEAN mEndOfDxe = FALSE;
+
+/**
+ Retrieve the SMM FVB protocol interface by HANDLE.
+
+ @param[in] FvBlockHandle The handle of SMM FVB protocol that provides services for
+ reading, writing, and erasing the target block.
+ @param[out] FvBlock The interface of SMM FVB protocol
+
+ @retval EFI_SUCCESS The interface information for the specified protocol was returned.
+ @retval EFI_UNSUPPORTED The device does not support the SMM FVB protocol.
+ @retval EFI_INVALID_PARAMETER FvBlockHandle is not a valid EFI_HANDLE or FvBlock is NULL.
+
+**/
+EFI_STATUS
+FtwGetFvbByHandle (
+ IN EFI_HANDLE FvBlockHandle,
+ OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock
+ )
+{
+ //
+ // To get the SMM FVB protocol interface on the handle
+ //
+ return gSmst->SmmHandleProtocol (
+ FvBlockHandle,
+ &gEfiSmmFirmwareVolumeBlockProtocolGuid,
+ (VOID **) FvBlock
+ );
+}
+
+/**
+ Retrieve the SMM Swap Address Range protocol interface.
+
+ @param[out] SarProtocol The interface of SMM SAR protocol
+
+ @retval EFI_SUCCESS The SMM SAR protocol instance was found and returned in SarProtocol.
+ @retval EFI_NOT_FOUND The SMM SAR protocol instance was not found.
+ @retval EFI_INVALID_PARAMETER SarProtocol is NULL.
+
+**/
+EFI_STATUS
+FtwGetSarProtocol (
+ OUT VOID **SarProtocol
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Locate Smm Swap Address Range protocol
+ //
+ Status = gSmst->SmmLocateProtocol (
+ &gEfiSmmSwapAddressRangeProtocolGuid,
+ NULL,
+ SarProtocol
+ );
+ return Status;
+}
+
+/**
+ Function returns an array of handles that support the SMM FVB protocol
+ in a buffer allocated from pool.
+
+ @param[out] NumberHandles The number of handles returned in Buffer.
+ @param[out] Buffer A pointer to the buffer to return the requested
+ array of handles that support SMM FVB protocol.
+
+ @retval EFI_SUCCESS The array of handles was returned in Buffer, and the number of
+ handles in Buffer was returned in NumberHandles.
+ @retval EFI_NOT_FOUND No SMM FVB handle was found.
+ @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the matching results.
+ @retval EFI_INVALID_PARAMETER NumberHandles is NULL or Buffer is NULL.
+
+**/
+EFI_STATUS
+GetFvbCountAndBuffer (
+ OUT UINTN *NumberHandles,
+ OUT EFI_HANDLE **Buffer
+ )
+{
+ EFI_STATUS Status;
+ UINTN BufferSize;
+
+ if ((NumberHandles == NULL) || (Buffer == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ BufferSize = 0;
+ *NumberHandles = 0;
+ *Buffer = NULL;
+ Status = gSmst->SmmLocateHandle (
+ ByProtocol,
+ &gEfiSmmFirmwareVolumeBlockProtocolGuid,
+ NULL,
+ &BufferSize,
+ *Buffer
+ );
+ if (EFI_ERROR(Status) && Status != EFI_BUFFER_TOO_SMALL) {
+ return EFI_NOT_FOUND;
+ }
+
+ *Buffer = AllocatePool (BufferSize);
+ if (*Buffer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = gSmst->SmmLocateHandle (
+ ByProtocol,
+ &gEfiSmmFirmwareVolumeBlockProtocolGuid,
+ NULL,
+ &BufferSize,
+ *Buffer
+ );
+
+ *NumberHandles = BufferSize / sizeof(EFI_HANDLE);
+ if (EFI_ERROR(Status)) {
+ *NumberHandles = 0;
+ FreePool (*Buffer);
+ *Buffer = NULL;
+ }
+
+ return Status;
+}
+
+
+/**
+ Get the handle of the SMM FVB protocol by the FVB base address and attributes.
+
+ @param[in] Address The base address of SMM FVB protocol.
+ @param[in] Attributes The attributes of the SMM FVB protocol.
+ @param[out] SmmFvbHandle The handle of the SMM FVB protocol.
+
+ @retval EFI_SUCCESS The FVB handle is found.
+ @retval EFI_ABORTED The FVB protocol is not found.
+
+**/
+EFI_STATUS
+GetFvbByAddressAndAttribute (
+ IN EFI_PHYSICAL_ADDRESS Address,
+ IN EFI_FVB_ATTRIBUTES_2 Attributes,
+ OUT EFI_HANDLE *SmmFvbHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE *HandleBuffer;
+ UINTN HandleCount;
+ UINTN Index;
+ EFI_PHYSICAL_ADDRESS FvbBaseAddress;
+ EFI_FVB_ATTRIBUTES_2 FvbAttributes;
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
+
+ HandleBuffer = NULL;
+
+ //
+ // Locate all handles of SMM Fvb protocol.
+ //
+ Status = GetFvbCountAndBuffer (&HandleCount, &HandleBuffer);
+ if (EFI_ERROR (Status)) {
+ return EFI_ABORTED;
+ }
+
+ //
+ // Find the proper SMM Fvb handle by the address and attributes.
+ //
+ for (Index = 0; Index < HandleCount; Index++) {
+ Status = FtwGetFvbByHandle (HandleBuffer[Index], &Fvb);
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ //
+ // Compare the address.
+ //
+ Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress);
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+ if (Address != FvbBaseAddress) {
+ continue;
+ }
+
+ //
+ // Compare the attribute.
+ //
+ Status = Fvb->GetAttributes (Fvb, &FvbAttributes);
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+ if (Attributes != FvbAttributes) {
+ continue;
+ }
+
+ //
+ // Found the proper FVB handle.
+ //
+ *SmmFvbHandle = HandleBuffer[Index];
+ FreePool (HandleBuffer);
+ return EFI_SUCCESS;
+ }
+
+ FreePool (HandleBuffer);
+ return EFI_ABORTED;
+}
+
+/**
+ Communication service SMI Handler entry.
+
+ This SMI handler provides services for the fault tolerant write wrapper driver.
+
+ Caution: This function requires additional review when modified.
+ This driver need to make sure the CommBuffer is not in the SMRAM range.
+ Also in FTW_FUNCTION_GET_LAST_WRITE case, check SmmFtwGetLastWriteHeader->Data +
+ SmmFtwGetLastWriteHeader->PrivateDataSize within communication buffer.
+
+ @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
+ @param[in] RegisterContext Points to an optional handler context which was specified when the
+ handler was registered.
+ @param[in, out] CommBuffer A pointer to a collection of data in memory that will be conveyed
+ from a non-SMM environment into an SMM environment.
+ @param[in, out] CommBufferSize The size of the CommBuffer.
+
+ @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers
+ should still be called.
+ @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should
+ still be called.
+ @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still
+ be called.
+ @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced.
+
+**/
+EFI_STATUS
+EFIAPI
+SmmFaultTolerantWriteHandler (
+ IN EFI_HANDLE DispatchHandle,
+ IN CONST VOID *RegisterContext,
+ IN OUT VOID *CommBuffer,
+ IN OUT UINTN *CommBufferSize
+ )
+{
+ EFI_STATUS Status;
+ SMM_FTW_COMMUNICATE_FUNCTION_HEADER *SmmFtwFunctionHeader;
+ SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER *SmmGetMaxBlockSizeHeader;
+ SMM_FTW_ALLOCATE_HEADER *SmmFtwAllocateHeader;
+ SMM_FTW_WRITE_HEADER *SmmFtwWriteHeader;
+ SMM_FTW_RESTART_HEADER *SmmFtwRestartHeader;
+ SMM_FTW_GET_LAST_WRITE_HEADER *SmmFtwGetLastWriteHeader;
+ VOID *PrivateData;
+ EFI_HANDLE SmmFvbHandle;
+ UINTN InfoSize;
+ UINTN CommBufferPayloadSize;
+ UINTN PrivateDataSize;
+ UINTN Length;
+ UINTN TempCommBufferSize;
+
+ //
+ // If input is invalid, stop processing this SMI
+ //
+ if (CommBuffer == NULL || CommBufferSize == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ TempCommBufferSize = *CommBufferSize;
+
+ if (TempCommBufferSize < SMM_FTW_COMMUNICATE_HEADER_SIZE) {
+ DEBUG ((EFI_D_ERROR, "SmmFtwHandler: SMM communication buffer size invalid!\n"));
+ return EFI_SUCCESS;
+ }
+ CommBufferPayloadSize = TempCommBufferSize - SMM_FTW_COMMUNICATE_HEADER_SIZE;
+
+ if (!SmmIsBufferOutsideSmmValid ((UINTN)CommBuffer, TempCommBufferSize)) {
+ DEBUG ((EFI_D_ERROR, "SmmFtwHandler: SMM communication buffer in SMRAM or overflow!\n"));
+ return EFI_SUCCESS;
+ }
+
+ SmmFtwFunctionHeader = (SMM_FTW_COMMUNICATE_FUNCTION_HEADER *)CommBuffer;
+
+ if (mEndOfDxe) {
+ //
+ // It will be not safe to expose the operations after End Of Dxe.
+ //
+ DEBUG ((EFI_D_ERROR, "SmmFtwHandler: Not safe to do the operation: %x after End Of Dxe, so access denied!\n", SmmFtwFunctionHeader->Function));
+ SmmFtwFunctionHeader->ReturnStatus = EFI_ACCESS_DENIED;
+ return EFI_SUCCESS;
+ }
+
+ switch (SmmFtwFunctionHeader->Function) {
+ case FTW_FUNCTION_GET_MAX_BLOCK_SIZE:
+ if (CommBufferPayloadSize < sizeof (SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER)) {
+ DEBUG ((EFI_D_ERROR, "GetMaxBlockSize: SMM communication buffer size invalid!\n"));
+ return EFI_SUCCESS;
+ }
+ SmmGetMaxBlockSizeHeader = (SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER *) SmmFtwFunctionHeader->Data;
+
+ Status = FtwGetMaxBlockSize (
+ &mFtwDevice->FtwInstance,
+ &SmmGetMaxBlockSizeHeader->BlockSize
+ );
+ break;
+
+ case FTW_FUNCTION_ALLOCATE:
+ if (CommBufferPayloadSize < sizeof (SMM_FTW_ALLOCATE_HEADER)) {
+ DEBUG ((EFI_D_ERROR, "Allocate: SMM communication buffer size invalid!\n"));
+ return EFI_SUCCESS;
+ }
+ SmmFtwAllocateHeader = (SMM_FTW_ALLOCATE_HEADER *) SmmFtwFunctionHeader->Data;
+ Status = FtwAllocate (
+ &mFtwDevice->FtwInstance,
+ &SmmFtwAllocateHeader->CallerId,
+ SmmFtwAllocateHeader->PrivateDataSize,
+ SmmFtwAllocateHeader->NumberOfWrites
+ );
+ break;
+
+ case FTW_FUNCTION_WRITE:
+ if (CommBufferPayloadSize < OFFSET_OF (SMM_FTW_WRITE_HEADER, Data)) {
+ DEBUG ((EFI_D_ERROR, "Write: SMM communication buffer size invalid!\n"));
+ return EFI_SUCCESS;
+ }
+ SmmFtwWriteHeader = (SMM_FTW_WRITE_HEADER *) SmmFtwFunctionHeader->Data;
+ Length = SmmFtwWriteHeader->Length;
+ PrivateDataSize = SmmFtwWriteHeader->PrivateDataSize;
+ if (((UINTN)(~0) - Length < OFFSET_OF (SMM_FTW_WRITE_HEADER, Data)) ||
+ ((UINTN)(~0) - PrivateDataSize < OFFSET_OF (SMM_FTW_WRITE_HEADER, Data) + Length)) {
+ //
+ // Prevent InfoSize overflow
+ //
+ Status = EFI_ACCESS_DENIED;
+ break;
+ }
+ InfoSize = OFFSET_OF (SMM_FTW_WRITE_HEADER, Data) + Length + PrivateDataSize;
+
+ //
+ // SMRAM range check already covered before
+ //
+ if (InfoSize > CommBufferPayloadSize) {
+ DEBUG ((EFI_D_ERROR, "Write: Data size exceed communication buffer size limit!\n"));
+ Status = EFI_ACCESS_DENIED;
+ break;
+ }
+
+ if (PrivateDataSize == 0) {
+ PrivateData = NULL;
+ } else {
+ PrivateData = (VOID *)&SmmFtwWriteHeader->Data[Length];
+ }
+ Status = GetFvbByAddressAndAttribute (
+ SmmFtwWriteHeader->FvbBaseAddress,
+ SmmFtwWriteHeader->FvbAttributes,
+ &SmmFvbHandle
+ );
+ if (!EFI_ERROR (Status)) {
+ Status = FtwWrite(
+ &mFtwDevice->FtwInstance,
+ SmmFtwWriteHeader->Lba,
+ SmmFtwWriteHeader->Offset,
+ Length,
+ PrivateData,
+ SmmFvbHandle,
+ SmmFtwWriteHeader->Data
+ );
+ }
+ break;
+
+ case FTW_FUNCTION_RESTART:
+ if (CommBufferPayloadSize < sizeof (SMM_FTW_RESTART_HEADER)) {
+ DEBUG ((EFI_D_ERROR, "Restart: SMM communication buffer size invalid!\n"));
+ return EFI_SUCCESS;
+ }
+ SmmFtwRestartHeader = (SMM_FTW_RESTART_HEADER *) SmmFtwFunctionHeader->Data;
+ Status = GetFvbByAddressAndAttribute (
+ SmmFtwRestartHeader->FvbBaseAddress,
+ SmmFtwRestartHeader->FvbAttributes,
+ &SmmFvbHandle
+ );
+ if (!EFI_ERROR (Status)) {
+ Status = FtwRestart (&mFtwDevice->FtwInstance, SmmFvbHandle);
+ }
+ break;
+
+ case FTW_FUNCTION_ABORT:
+ Status = FtwAbort (&mFtwDevice->FtwInstance);
+ break;
+
+ case FTW_FUNCTION_GET_LAST_WRITE:
+ if (CommBufferPayloadSize < OFFSET_OF (SMM_FTW_GET_LAST_WRITE_HEADER, Data)) {
+ DEBUG ((EFI_D_ERROR, "GetLastWrite: SMM communication buffer size invalid!\n"));
+ return EFI_SUCCESS;
+ }
+ SmmFtwGetLastWriteHeader = (SMM_FTW_GET_LAST_WRITE_HEADER *) SmmFtwFunctionHeader->Data;
+ PrivateDataSize = SmmFtwGetLastWriteHeader->PrivateDataSize;
+ if ((UINTN)(~0) - PrivateDataSize < OFFSET_OF (SMM_FTW_GET_LAST_WRITE_HEADER, Data)){
+ //
+ // Prevent InfoSize overflow
+ //
+ Status = EFI_ACCESS_DENIED;
+ break;
+ }
+ InfoSize = OFFSET_OF (SMM_FTW_GET_LAST_WRITE_HEADER, Data) + PrivateDataSize;
+
+ //
+ // SMRAM range check already covered before
+ //
+ if (InfoSize > CommBufferPayloadSize) {
+ DEBUG ((EFI_D_ERROR, "Data size exceed communication buffer size limit!\n"));
+ Status = EFI_ACCESS_DENIED;
+ break;
+ }
+
+ Status = FtwGetLastWrite (
+ &mFtwDevice->FtwInstance,
+ &SmmFtwGetLastWriteHeader->CallerId,
+ &SmmFtwGetLastWriteHeader->Lba,
+ &SmmFtwGetLastWriteHeader->Offset,
+ &SmmFtwGetLastWriteHeader->Length,
+ &PrivateDataSize,
+ (VOID *)SmmFtwGetLastWriteHeader->Data,
+ &SmmFtwGetLastWriteHeader->Complete
+ );
+ SmmFtwGetLastWriteHeader->PrivateDataSize = PrivateDataSize;
+ break;
+
+ default:
+ Status = EFI_UNSUPPORTED;
+ }
+
+ SmmFtwFunctionHeader->ReturnStatus = Status;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ SMM Firmware Volume Block Protocol notification event handler.
+
+ @param[in] Protocol Points to the protocol's unique identifier
+ @param[in] Interface Points to the interface instance
+ @param[in] Handle The handle on which the interface was installed
+
+ @retval EFI_SUCCESS SmmEventCallback runs successfully
+
+ **/
+EFI_STATUS
+EFIAPI
+FvbNotificationEvent (
+ IN CONST EFI_GUID *Protocol,
+ IN VOID *Interface,
+ IN EFI_HANDLE Handle
+ )
+{
+ EFI_STATUS Status;
+ EFI_SMM_FAULT_TOLERANT_WRITE_PROTOCOL *FtwProtocol;
+ EFI_HANDLE SmmFtwHandle;
+ EFI_HANDLE FtwHandle;
+
+ //
+ // Just return to avoid install SMM FaultTolerantWriteProtocol again
+ // if SMM Fault Tolerant Write protocol had been installed.
+ //
+ Status = gSmst->SmmLocateProtocol (
+ &gEfiSmmFaultTolerantWriteProtocolGuid,
+ NULL,
+ (VOID **) &FtwProtocol
+ );
+ if (!EFI_ERROR (Status)) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Found proper FVB protocol and initialize FtwDevice for protocol installation
+ //
+ Status = InitFtwProtocol (mFtwDevice);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ //
+ // Install protocol interface
+ //
+ Status = gSmst->SmmInstallProtocolInterface (
+ &mFtwDevice->Handle,
+ &gEfiSmmFaultTolerantWriteProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &mFtwDevice->FtwInstance
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ ///
+ /// Register SMM FTW SMI handler
+ ///
+ Status = gSmst->SmiHandlerRegister (SmmFaultTolerantWriteHandler, &gEfiSmmFaultTolerantWriteProtocolGuid, &SmmFtwHandle);
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Notify the Ftw wrapper driver SMM Ftw is ready
+ //
+ FtwHandle = NULL;
+ Status = gBS->InstallProtocolInterface (
+ &FtwHandle,
+ &gEfiSmmFaultTolerantWriteProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ SMM END_OF_DXE protocol notification event handler.
+
+ @param Protocol Points to the protocol's unique identifier
+ @param Interface Points to the interface instance
+ @param Handle The handle on which the interface was installed
+
+ @retval EFI_SUCCESS SmmEndOfDxeCallback runs successfully
+
+**/
+EFI_STATUS
+EFIAPI
+SmmEndOfDxeCallback (
+ IN CONST EFI_GUID *Protocol,
+ IN VOID *Interface,
+ IN EFI_HANDLE Handle
+ )
+{
+ mEndOfDxe = TRUE;
+ return EFI_SUCCESS;
+}
+
+/**
+ This function is the entry point of the Fault Tolerant Write driver.
+
+ @param[in] ImageHandle A handle for the image that is initializing this driver
+ @param[in] SystemTable A pointer to the EFI system table
+
+ @retval EFI_SUCCESS The initialization finished successfully.
+ @retval EFI_OUT_OF_RESOURCES Allocate memory error
+ @retval EFI_INVALID_PARAMETER Workspace or Spare block does not exist
+
+**/
+EFI_STATUS
+EFIAPI
+SmmFaultTolerantWriteInitialize (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ VOID *SmmEndOfDxeRegistration;
+
+ //
+ // Allocate private data structure for SMM FTW protocol and do some initialization
+ //
+ Status = InitFtwDevice (&mFtwDevice);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ //
+ // Register EFI_SMM_END_OF_DXE_PROTOCOL_GUID notify function.
+ //
+ Status = gSmst->SmmRegisterProtocolNotify (
+ &gEfiSmmEndOfDxeProtocolGuid,
+ SmmEndOfDxeCallback,
+ &SmmEndOfDxeRegistration
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Register FvbNotificationEvent () notify function.
+ //
+ Status = gSmst->SmmRegisterProtocolNotify (
+ &gEfiSmmFirmwareVolumeBlockProtocolGuid,
+ FvbNotificationEvent,
+ &mFvbRegistration
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ FvbNotificationEvent (NULL, NULL, NULL);
+
+ return EFI_SUCCESS;
+}
diff --git a/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.inf b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.inf
new file mode 100644
index 0000000000..3b57c3cf64
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.inf
@@ -0,0 +1,98 @@
+## @file
+# Fault Tolerant Write Smm Driver.
+#
+# This driver installs SMM Fault Tolerant Write (FTW) protocol, which provides fault
+# tolerant write capability in SMM environment for block devices. Its implementation
+# depends on the full functionality SMM FVB protocol that support read, write/erase
+# flash access.
+#
+# Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.<BR>
+#
+# 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.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = SmmFaultTolerantWriteDxe
+ MODULE_UNI_FILE = SmmFaultTolerantWriteDxe.uni
+ FILE_GUID = 470CB248-E8AC-473c-BB4F-81069A1FE6FD
+ MODULE_TYPE = DXE_SMM_DRIVER
+ VERSION_STRING = 1.0
+ PI_SPECIFICATION_VERSION = 0x0001000A
+ ENTRY_POINT = SmmFaultTolerantWriteInitialize
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources]
+ FtwMisc.c
+ UpdateWorkingBlock.c
+ FaultTolerantWrite.c
+ FaultTolerantWriteSmm.c
+ FaultTolerantWrite.h
+ FaultTolerantWriteSmmCommon.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ SmmServicesTableLib
+ MemoryAllocationLib
+ BaseMemoryLib
+ UefiDriverEntryPoint
+ DebugLib
+ UefiLib
+ PcdLib
+ ReportStatusCodeLib
+ SmmMemLib
+
+[Guids]
+ #
+ # Signature in EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
+ #
+ ## CONSUMES ## GUID
+ ## PRODUCES ## GUID
+ gEdkiiWorkingBlockSignatureGuid
+
+[Protocols]
+ gEfiSmmSwapAddressRangeProtocolGuid | gEfiMdeModulePkgTokenSpaceGuid.PcdFullFtwServiceEnable ## SOMETIMES_CONSUMES
+ ## NOTIFY
+ ## CONSUMES
+ gEfiSmmFirmwareVolumeBlockProtocolGuid
+ ## PRODUCES
+ ## UNDEFINED # SmiHandlerRegister
+ gEfiSmmFaultTolerantWriteProtocolGuid
+ gEfiSmmEndOfDxeProtocolGuid ## CONSUMES
+
+[FeaturePcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFullFtwServiceEnable ## CONSUMES
+
+[Pcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase ## SOMETIMES_CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase64 ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase ## SOMETIMES_CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase64 ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize ## CONSUMES
+
+#
+# gBS->CalculateCrc32() is consumed in EntryPoint.
+# PI spec said: When the DXE Foundation is notified that the EFI_RUNTIME_ARCH_PROTOCOL
+# has been installed, then the Boot Service CalculateCrc32() is available.
+# So add gEfiRuntimeArchProtocolGuid Depex here.
+#
+[Depex]
+ gEfiSmmFirmwareVolumeBlockProtocolGuid AND gEfiRuntimeArchProtocolGuid
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ SmmFaultTolerantWriteDxeExtra.uni
diff --git a/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmCommon.h b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmCommon.h
new file mode 100644
index 0000000000..061673bce2
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmCommon.h
@@ -0,0 +1,80 @@
+/** @file
+
+ The common header file for SMM FTW module and SMM FTW DXE Module.
+
+Copyright (c) 2011, Intel Corporation. All rights reserved. <BR>
+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.
+
+**/
+
+#ifndef __SMM_FTW_COMMON_H__
+#define __SMM_FTW_COMMON_H__
+
+#include <Protocol/SmmFirmwareVolumeBlock.h>
+#include <Protocol/SmmFaultTolerantWrite.h>
+
+#define FTW_FUNCTION_GET_MAX_BLOCK_SIZE 1
+#define FTW_FUNCTION_ALLOCATE 2
+#define FTW_FUNCTION_WRITE 3
+#define FTW_FUNCTION_RESTART 4
+#define FTW_FUNCTION_ABORT 5
+#define FTW_FUNCTION_GET_LAST_WRITE 6
+
+typedef struct {
+ UINTN Function;
+ EFI_STATUS ReturnStatus;
+ UINT8 Data[1];
+} SMM_FTW_COMMUNICATE_FUNCTION_HEADER;
+
+///
+/// Size of SMM communicate header, without including the payload.
+///
+#define SMM_COMMUNICATE_HEADER_SIZE (OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data))
+
+///
+/// Size of SMM FTW communicate function header, without including the payload.
+///
+#define SMM_FTW_COMMUNICATE_HEADER_SIZE (OFFSET_OF (SMM_FTW_COMMUNICATE_FUNCTION_HEADER, Data))
+
+typedef struct {
+ UINTN BlockSize;
+} SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER;
+
+typedef struct {
+ EFI_GUID CallerId;
+ UINTN PrivateDataSize;
+ UINTN NumberOfWrites;
+} SMM_FTW_ALLOCATE_HEADER;
+
+typedef struct {
+ EFI_LBA Lba;
+ UINTN Offset;
+ UINTN PrivateDataSize;
+ EFI_PHYSICAL_ADDRESS FvbBaseAddress;
+ EFI_FVB_ATTRIBUTES_2 FvbAttributes;
+ UINTN Length;
+ UINT8 Data[1];
+} SMM_FTW_WRITE_HEADER;
+
+typedef struct {
+ EFI_PHYSICAL_ADDRESS FvbBaseAddress;
+ EFI_FVB_ATTRIBUTES_2 FvbAttributes;
+} SMM_FTW_RESTART_HEADER;
+
+typedef struct {
+ EFI_GUID CallerId;
+ EFI_LBA Lba;
+ UINTN Offset;
+ UINTN Length;
+ UINTN PrivateDataSize;
+ BOOLEAN Complete;
+ UINT8 Data[1];
+} SMM_FTW_GET_LAST_WRITE_HEADER;
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.c b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.c
new file mode 100644
index 0000000000..772d10dcd4
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.c
@@ -0,0 +1,562 @@
+/** @file
+
+ Implement the Fault Tolerant Write (FTW) protocol based on SMM FTW
+ module.
+
+Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved. <BR>
+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.
+
+**/
+
+#include "FaultTolerantWriteSmmDxe.h"
+
+EFI_HANDLE mHandle = NULL;
+EFI_SMM_COMMUNICATION_PROTOCOL *mSmmCommunication = NULL;
+UINTN mPrivateDataSize = 0;
+
+EFI_FAULT_TOLERANT_WRITE_PROTOCOL mFaultTolerantWriteDriver = {
+ FtwGetMaxBlockSize,
+ FtwAllocate,
+ FtwWrite,
+ FtwRestart,
+ FtwAbort,
+ FtwGetLastWrite
+};
+
+/**
+ Initialize the communicate buffer using DataSize and Function number.
+
+ @param[out] CommunicateBuffer The communicate buffer. Caller should free it after use.
+ @param[out] DataPtr Points to the data in the communicate buffer. Caller should not free it.
+ @param[in] DataSize The payload size.
+ @param[in] Function The function number used to initialize the communicate header.
+
+**/
+VOID
+InitCommunicateBuffer (
+ OUT VOID **CommunicateBuffer,
+ OUT VOID **DataPtr,
+ IN UINTN DataSize,
+ IN UINTN Function
+ )
+{
+ EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader;
+ SMM_FTW_COMMUNICATE_FUNCTION_HEADER *SmmFtwFunctionHeader;
+
+ //
+ // The whole buffer size: SMM_COMMUNICATE_HEADER_SIZE + SMM_FTW_COMMUNICATE_HEADER_SIZE + DataSize.
+ //
+ SmmCommunicateHeader = AllocateZeroPool (DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_FTW_COMMUNICATE_HEADER_SIZE);
+ ASSERT (SmmCommunicateHeader != NULL);
+
+ //
+ // Prepare data buffer.
+ //
+ CopyGuid (&SmmCommunicateHeader->HeaderGuid, &gEfiSmmFaultTolerantWriteProtocolGuid);
+ SmmCommunicateHeader->MessageLength = DataSize + SMM_FTW_COMMUNICATE_HEADER_SIZE;
+
+ SmmFtwFunctionHeader = (SMM_FTW_COMMUNICATE_FUNCTION_HEADER *) SmmCommunicateHeader->Data;
+ SmmFtwFunctionHeader->Function = Function;
+
+ *CommunicateBuffer = SmmCommunicateHeader;
+ if (DataPtr != NULL) {
+ *DataPtr = SmmFtwFunctionHeader->Data;
+ }
+}
+
+
+/**
+ Send the data in communicate buffer to SMI handler and get response.
+
+ @param[in, out] SmmCommunicateHeader The communicate buffer.
+ @param[in] DataSize The payload size.
+
+**/
+EFI_STATUS
+SendCommunicateBuffer (
+ IN OUT EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader,
+ IN UINTN DataSize
+ )
+{
+ EFI_STATUS Status;
+ UINTN CommSize;
+ SMM_FTW_COMMUNICATE_FUNCTION_HEADER *SmmFtwFunctionHeader;
+
+ CommSize = DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_FTW_COMMUNICATE_HEADER_SIZE;
+ Status = mSmmCommunication->Communicate (mSmmCommunication, SmmCommunicateHeader, &CommSize);
+ ASSERT_EFI_ERROR (Status);
+
+ SmmFtwFunctionHeader = (SMM_FTW_COMMUNICATE_FUNCTION_HEADER *) SmmCommunicateHeader->Data;
+ return SmmFtwFunctionHeader->ReturnStatus;
+}
+
+
+/**
+ Get the FvbBaseAddress and FvbAttributes from the FVB handle FvbHandle.
+
+ @param[in] FvbHandle The handle of FVB protocol that provides services.
+ @param[out] FvbBaseAddress The base address of the FVB attached with FvbHandle.
+ @param[out] FvbAttributes The attributes of the FVB attached with FvbHandle.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval Others The function could not complete successfully.
+
+**/
+EFI_STATUS
+ConvertFvbHandle (
+ IN EFI_HANDLE FvbHandle,
+ OUT EFI_PHYSICAL_ADDRESS *FvbBaseAddress,
+ OUT EFI_FVB_ATTRIBUTES_2 *FvbAttributes
+ )
+{
+ EFI_STATUS Status;
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
+
+ Status = gBS->HandleProtocol (FvbHandle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID **) &Fvb);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = Fvb->GetPhysicalAddress (Fvb, FvbBaseAddress);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = Fvb->GetAttributes (Fvb, FvbAttributes);
+ return Status;
+}
+
+
+/**
+ Get the size of the largest block that can be updated in a fault-tolerant manner.
+
+ @param[in] This Indicates a pointer to the calling context.
+ @param[out] BlockSize A pointer to a caller-allocated UINTN that is
+ updated to indicate the size of the largest block
+ that can be updated.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED The function could not complete successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+FtwGetMaxBlockSize (
+ IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,
+ OUT UINTN *BlockSize
+ )
+{
+ EFI_STATUS Status;
+ UINTN PayloadSize;
+ EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader;
+ SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER *SmmFtwBlockSizeHeader;
+
+ //
+ // Initialize the communicate buffer.
+ //
+ PayloadSize = sizeof (SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER);
+ InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, (VOID **)&SmmFtwBlockSizeHeader, PayloadSize, FTW_FUNCTION_GET_MAX_BLOCK_SIZE);
+
+ //
+ // Send data to SMM.
+ //
+ Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
+
+ //
+ // Get data from SMM
+ //
+ *BlockSize = SmmFtwBlockSizeHeader->BlockSize;
+ FreePool (SmmCommunicateHeader);
+
+ return Status;
+}
+
+
+/**
+ Allocates space for the protocol to maintain information about writes.
+ Since writes must be completed in a fault-tolerant manner and multiple
+ writes require more resources to be successful, this function
+ enables the protocol to ensure that enough space exists to track
+ information about upcoming writes.
+
+ @param[in] This A pointer to the calling context.
+ @param[in] CallerId The GUID identifying the write.
+ @param[in] PrivateDataSize The size of the caller's private data that must be
+ recorded for each write.
+ @param[in] NumberOfWrites The number of fault tolerant block writes that will
+ need to occur.
+
+ @retval EFI_SUCCESS The function completed successfully
+ @retval EFI_ABORTED The function could not complete successfully.
+ @retval EFI_ACCESS_DENIED Not all allocated writes have been completed. All
+ writes must be completed or aborted before another
+ fault tolerant write can occur.
+
+**/
+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 PayloadSize;
+ EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader;
+ SMM_FTW_ALLOCATE_HEADER *SmmFtwAllocateHeader;
+
+ //
+ // Initialize the communicate buffer.
+ //
+ PayloadSize = sizeof (SMM_FTW_ALLOCATE_HEADER);
+ InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, (VOID **)&SmmFtwAllocateHeader, PayloadSize, FTW_FUNCTION_ALLOCATE);
+ CopyGuid (&SmmFtwAllocateHeader->CallerId, CallerId);
+ SmmFtwAllocateHeader->PrivateDataSize = PrivateDataSize;
+ SmmFtwAllocateHeader->NumberOfWrites = NumberOfWrites;
+
+ //
+ // Send data to SMM.
+ //
+ Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
+ if (!EFI_ERROR( Status)) {
+ mPrivateDataSize = PrivateDataSize;
+ }
+
+ FreePool (SmmCommunicateHeader);
+ return Status;
+}
+
+
+/**
+ 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[in] This The calling context.
+ @param[in] Lba The logical block address of the target block.
+ @param[in] Offset The offset within the target block to place the
+ data.
+ @param[in] Length The number of bytes to write to the target block.
+ @param[in] PrivateData A pointer to private data that the caller requires
+ to complete any pending writes in the event of a
+ fault.
+ @param[in] FvBlockHandle The handle of FVB protocol that provides services
+ for reading, writing, and erasing the target block.
+ @param[in] 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 write would span a block boundary, which is not
+ a valid action.
+ @retval EFI_ACCESS_DENIED No writes have been allocated.
+ @retval EFI_NOT_READY The last write has not been completed. Restart()
+ must be called to complete it.
+
+**/
+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;
+ UINTN PayloadSize;
+ EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader;
+ SMM_FTW_WRITE_HEADER *SmmFtwWriteHeader;
+
+ //
+ // Initialize the communicate buffer.
+ //
+ PayloadSize = OFFSET_OF (SMM_FTW_WRITE_HEADER, Data) + Length;
+ if (PrivateData != NULL) {
+ //
+ // The private data buffer size should be the same one in FtwAllocate API.
+ //
+ PayloadSize += mPrivateDataSize;
+ }
+ InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, (VOID **)&SmmFtwWriteHeader, PayloadSize, FTW_FUNCTION_WRITE);
+
+ //
+ // FvBlockHandle can not be used in SMM environment. Here we get the FVB protocol first, then get FVB base address
+ // and its attribute. Send these information to SMM handler, the SMM handler will find the proper FVB to write data.
+ //
+ Status = ConvertFvbHandle (FvBlockHandle, &SmmFtwWriteHeader->FvbBaseAddress, &SmmFtwWriteHeader->FvbAttributes);
+ if (EFI_ERROR (Status)) {
+ FreePool (SmmCommunicateHeader);
+ return EFI_ABORTED;
+ }
+
+ SmmFtwWriteHeader->Lba = Lba;
+ SmmFtwWriteHeader->Offset = Offset;
+ SmmFtwWriteHeader->Length = Length;
+ CopyMem (SmmFtwWriteHeader->Data, Buffer, Length);
+ if (PrivateData == NULL) {
+ SmmFtwWriteHeader->PrivateDataSize = 0;
+ } else {
+ SmmFtwWriteHeader->PrivateDataSize = mPrivateDataSize;
+ CopyMem (&SmmFtwWriteHeader->Data[Length], PrivateData, mPrivateDataSize);
+ }
+
+ //
+ // Send data to SMM.
+ //
+ Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
+ FreePool (SmmCommunicateHeader);
+ return Status;
+}
+
+
+/**
+ Restarts a previously interrupted write. The caller must provide the
+ block protocol needed to complete the interrupted write.
+
+ @param[in] This The calling context.
+ @param[in] FvBlockHandle The handle of FVB protocol that provides services.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED The function could not complete successfully.
+ @retval EFI_ACCESS_DENIED No pending writes exist.
+
+**/
+EFI_STATUS
+EFIAPI
+FtwRestart (
+ IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,
+ IN EFI_HANDLE FvBlockHandle
+ )
+{
+ EFI_STATUS Status;
+ UINTN PayloadSize;
+ EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader;
+ SMM_FTW_RESTART_HEADER *SmmFtwRestartHeader;
+
+ //
+ // Initialize the communicate buffer.
+ //
+ PayloadSize = sizeof (SMM_FTW_RESTART_HEADER);
+ InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, (VOID **)&SmmFtwRestartHeader, PayloadSize, FTW_FUNCTION_RESTART);
+
+ //
+ // FvBlockHandle can not be used in SMM environment. Here we get the FVB protocol first, then get FVB base address
+ // and its attribute. Send these information to SMM handler, the SMM handler will find the proper FVB to write data.
+ //
+ Status = ConvertFvbHandle (FvBlockHandle, &SmmFtwRestartHeader->FvbBaseAddress, &SmmFtwRestartHeader->FvbAttributes);
+ if (EFI_ERROR (Status)) {
+ FreePool (SmmCommunicateHeader);
+ return EFI_ABORTED;
+ }
+
+ //
+ // Send data to SMM.
+ //
+ Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
+ FreePool (SmmCommunicateHeader);
+ return Status;
+}
+
+
+/**
+ Aborts all previously allocated writes.
+
+ @param[in] This The calling context.
+
+ @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;
+ EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader;
+
+ //
+ // Initialize the communicate buffer.
+ //
+ InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, NULL, 0, FTW_FUNCTION_ABORT);
+
+ //
+ // Send data to SMM.
+ //
+ Status = SendCommunicateBuffer (SmmCommunicateHeader, 0);
+
+ FreePool (SmmCommunicateHeader);
+ return Status;
+}
+
+
+/**
+ Starts a target block update. This function records information about the write
+ in fault-tolerant storage and completes the write in a recoverable
+ manner, ensuring at all times that either the original contents or
+ the modified contents are available.
+
+ @param[in] This Indicates a pointer to the calling context.
+ @param[out] CallerId The GUID identifying the last write.
+ @param[out] Lba The logical block address of the last write.
+ @param[out] Offset The offset within the block of the last write.
+ @param[out] Length The length of the last write.
+ @param[in, out] PrivateDataSize On input, the size of the PrivateData buffer. On
+ output, the size of the private data stored for
+ this write.
+ @param[out] PrivateData A pointer to a buffer. The function will copy
+ PrivateDataSize bytes from the private data stored
+ for this write.
+ @param[out] 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.
+
+**/
+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;
+ UINTN PayloadSize;
+ EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader;
+ SMM_FTW_GET_LAST_WRITE_HEADER *SmmFtwGetLastWriteHeader;
+
+ //
+ // Initialize the communicate buffer.
+ //
+ PayloadSize = OFFSET_OF (SMM_FTW_GET_LAST_WRITE_HEADER, Data) + *PrivateDataSize;
+ InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, (VOID **)&SmmFtwGetLastWriteHeader, PayloadSize, FTW_FUNCTION_GET_LAST_WRITE);
+ SmmFtwGetLastWriteHeader->PrivateDataSize = *PrivateDataSize;
+
+ //
+ // Send data to SMM.
+ //
+ Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
+
+ //
+ // Get data from SMM
+ //
+ *PrivateDataSize = SmmFtwGetLastWriteHeader->PrivateDataSize;
+ if (Status == EFI_SUCCESS || Status == EFI_BUFFER_TOO_SMALL) {
+ *Lba = SmmFtwGetLastWriteHeader->Lba;
+ *Offset = SmmFtwGetLastWriteHeader->Offset;
+ *Length = SmmFtwGetLastWriteHeader->Length;
+ *Complete = SmmFtwGetLastWriteHeader->Complete;
+ CopyGuid (CallerId, &SmmFtwGetLastWriteHeader->CallerId);
+ if (Status == EFI_SUCCESS) {
+ CopyMem (PrivateData, SmmFtwGetLastWriteHeader->Data, *PrivateDataSize);
+ }
+ } else if (Status == EFI_NOT_FOUND) {
+ *Complete = SmmFtwGetLastWriteHeader->Complete;
+ }
+
+ FreePool (SmmCommunicateHeader);
+ return Status;
+}
+
+/**
+ SMM Fault Tolerant Write Protocol notification event handler.
+
+ Install Fault Tolerant Write Protocol.
+
+ @param[in] Event Event whose notification function is being invoked.
+ @param[in] Context Pointer to the notification function's context.
+**/
+VOID
+EFIAPI
+SmmFtwReady (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ EFI_FAULT_TOLERANT_WRITE_PROTOCOL *FtwProtocol;
+
+ //
+ // Just return to avoid install SMM FaultTolerantWriteProtocol again
+ // if Fault Tolerant Write protocol had been installed.
+ //
+ Status = gBS->LocateProtocol (&gEfiFaultTolerantWriteProtocolGuid, NULL, (VOID **)&FtwProtocol);
+ if (!EFI_ERROR (Status)) {
+ return;
+ }
+
+ Status = gBS->LocateProtocol (&gEfiSmmCommunicationProtocolGuid, NULL, (VOID **) &mSmmCommunication);
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Install protocol interface
+ //
+ Status = gBS->InstallProtocolInterface (
+ &mHandle,
+ &gEfiFaultTolerantWriteProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &mFaultTolerantWriteDriver
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ Status = gBS->CloseEvent (Event);
+ ASSERT_EFI_ERROR (Status);
+}
+
+
+/**
+ The driver entry point for Fault Tolerant Write driver.
+
+ The function does the necessary initialization work.
+
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.
+ @param[in] SystemTable A pointer to the EFI system table.
+
+ @retval EFI_SUCCESS This funtion always return EFI_SUCCESS.
+
+**/
+EFI_STATUS
+EFIAPI
+FaultTolerantWriteSmmInitialize (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ VOID *SmmFtwRegistration;
+
+ //
+ // Smm FTW driver is ready
+ //
+ EfiCreateProtocolNotifyEvent (
+ &gEfiSmmFaultTolerantWriteProtocolGuid,
+ TPL_CALLBACK,
+ SmmFtwReady,
+ NULL,
+ &SmmFtwRegistration
+ );
+
+ return EFI_SUCCESS;
+}
+
diff --git a/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.h b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.h
new file mode 100644
index 0000000000..b4c20aee0f
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.h
@@ -0,0 +1,202 @@
+/** @file
+
+ The internal header file includes the common header files, defines
+ internal structure and functions used by FTW module.
+
+Copyright (c) 2011, Intel Corporation. All rights reserved. <BR>
+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.
+
+**/
+
+#ifndef __SMM_FTW_DXE_H__
+#define __SMM_FTW_DXE_H__
+
+#include <PiDxe.h>
+
+#include <Protocol/SmmCommunication.h>
+
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+#include <Guid/EventGroup.h>
+
+#include "FaultTolerantWriteSmmCommon.h"
+
+/**
+ Get the size of the largest block that can be updated in a fault-tolerant manner.
+
+ @param[in] This Indicates a pointer to the calling context.
+ @param[out] BlockSize A pointer to a caller-allocated UINTN that is
+ updated to indicate the size of the largest block
+ that can be updated.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED The function could not complete successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+FtwGetMaxBlockSize (
+ IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,
+ OUT UINTN *BlockSize
+ );
+
+
+/**
+ Allocates space for the protocol to maintain information about writes.
+ Since writes must be completed in a fault-tolerant manner and multiple
+ writes require more resources to be successful, this function
+ enables the protocol to ensure that enough space exists to track
+ information about upcoming writes.
+
+ @param[in] This A pointer to the calling context.
+ @param[in] CallerId The GUID identifying the write.
+ @param[in] PrivateDataSize The size of the caller's private data that must be
+ recorded for each write.
+ @param[in] NumberOfWrites The number of fault tolerant block writes that will
+ need to occur.
+
+ @retval EFI_SUCCESS The function completed successfully
+ @retval EFI_ABORTED The function could not complete successfully.
+ @retval EFI_ACCESS_DENIED Not all allocated writes have been completed. All
+ writes must be completed or aborted before another
+ fault tolerant write can occur.
+
+**/
+EFI_STATUS
+EFIAPI
+FtwAllocate (
+ IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,
+ IN EFI_GUID *CallerId,
+ IN UINTN PrivateDataSize,
+ IN UINTN NumberOfWrites
+ );
+
+
+/**
+ 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[in] This The calling context.
+ @param[in] Lba The logical block address of the target block.
+ @param[in] Offset The offset within the target block to place the
+ data.
+ @param[in] Length The number of bytes to write to the target block.
+ @param[in] PrivateData A pointer to private data that the caller requires
+ to complete any pending writes in the event of a
+ fault.
+ @param[in] FvBlockHandle The handle of FVB protocol that provides services
+ for reading, writing, and erasing the target block.
+ @param[in] 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 write would span a block boundary, which is not
+ a valid action.
+ @retval EFI_ACCESS_DENIED No writes have been allocated.
+ @retval EFI_NOT_READY The last write has not been completed. Restart()
+ must be called to complete it.
+
+**/
+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
+ );
+
+
+/**
+ Restarts a previously interrupted write. The caller must provide the
+ block protocol needed to complete the interrupted write.
+
+ @param[in] This The calling context.
+ @param[in] FvBlockHandle The handle of FVB protocol that provides services.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED The function could not complete successfully.
+ @retval EFI_ACCESS_DENIED No pending writes exist.
+
+**/
+EFI_STATUS
+EFIAPI
+FtwRestart (
+ IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,
+ IN EFI_HANDLE FvBlockHandle
+ );
+
+
+/**
+ Aborts all previously allocated writes.
+
+ @param This The calling context.
+
+ @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
+ );
+
+
+/**
+ Starts a target block update. This function records information about the write
+ in fault-tolerant storage and completes the write in a recoverable
+ manner, ensuring at all times that either the original contents or
+ the modified contents are available.
+
+ @param[in] This Indicates a pointer to the calling context.
+ @param[out] CallerId The GUID identifying the last write.
+ @param[out] Lba The logical block address of the last write.
+ @param[out] Offset The offset within the block of the last write.
+ @param[out] Length The length of the last write.
+ @param[in, out] PrivateDataSize On input, the size of the PrivateData buffer. On
+ output, the size of the private data stored for
+ this write.
+ @param[out] PrivateData A pointer to a buffer. The function will copy
+ PrivateDataSize bytes from the private data stored
+ for this write.
+ @param[out] 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.
+
+**/
+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
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.inf b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.inf
new file mode 100644
index 0000000000..82dd36c320
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.inf
@@ -0,0 +1,64 @@
+## @file
+# This module is the Runtime DXE part corresponding to SMM Fault Tolerant Write (FTW) module.
+#
+# It installs FTW protocol and works with SMM FTW module together.
+# The FTW protocol will not work after End Of Dxe because it will be not safe to expose
+# the related operations in SMM handler in SMM FTW module. You can use the FTW protocol
+# before End Of Dxe or use FaultTolerantWriteDxe module instead if you really want to.
+#
+# Copyright (c) 2011 - 2014, Intel Corporation. All rights reserved.<BR>
+#
+# 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.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = FaultTolerantWriteSmmDxe
+ MODULE_UNI_FILE = FaultTolerantWriteSmmDxe.uni
+ FILE_GUID = 98948C4A-70F2-4035-8E9F-5927493CFC07
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = FaultTolerantWriteSmmInitialize
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources]
+ FaultTolerantWriteSmmDxe.c
+ FaultTolerantWriteSmmDxe.h
+ FaultTolerantWriteSmmCommon.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ BaseLib
+ UefiBootServicesTableLib
+ DebugLib
+ DxeServicesTableLib
+ UefiDriverEntryPoint
+
+[Protocols]
+ gEfiFaultTolerantWriteProtocolGuid ## PRODUCES
+ gEfiSmmCommunicationProtocolGuid ## CONSUMES
+ ## NOTIFY
+ ## UNDEFINED # Used to do smm communication
+ ## CONSUMES
+ gEfiSmmFaultTolerantWriteProtocolGuid
+ gEfiFirmwareVolumeBlockProtocolGuid ## CONSUMES
+
+[Depex]
+ gEfiSmmCommunicationProtocolGuid
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ FaultTolerantWriteSmmDxeExtra.uni
diff --git a/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.uni b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.uni
new file mode 100644
index 0000000000..d991eeab69
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.uni
@@ -0,0 +1,24 @@
+// /** @file
+// This module is the Runtime DXE part corresponding to SMM Fault Tolerant Write (FTW) module.
+//
+// It installs FTW protocol and works with SMM FTW module together.
+// The FTW protocol will not work after End Of Dxe because it will be not safe to expose
+// the related operations in SMM handler in SMM FTW module. You can use the FTW protocol
+// before End Of Dxe or use FaultTolerantWriteDxe module instead if you really want to.
+//
+// Copyright (c) 2011 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// 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.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "The Runtime DXE part corresponding to the SMM Fault Tolerant Write (FTW) module"
+
+#string STR_MODULE_DESCRIPTION #language en-US "It installs FTW protocol and works with SMM FTW module together."
+
diff --git a/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxeExtra.uni b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxeExtra.uni
new file mode 100644
index 0000000000..8abf874098
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxeExtra.uni
@@ -0,0 +1,19 @@
+// /** @file
+// FaultTolerantWriteSmmDxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// 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.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"Fault Tolerant Flash Write SMM/DXE Bridge Driver"
+
+
diff --git a/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FtwMisc.c b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FtwMisc.c
new file mode 100644
index 0000000000..96044693cc
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/FtwMisc.c
@@ -0,0 +1,1384 @@
+/** @file
+
+ Internal generic functions to operate flash block.
+
+Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+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.
+
+**/
+
+#include "FaultTolerantWrite.h"
+
+/**
+
+ Check whether a flash buffer is erased.
+
+ @param Buffer Buffer to check
+ @param BufferSize Size of the buffer
+
+ @return A BOOLEAN value indicating erased or not.
+
+**/
+BOOLEAN
+IsErasedFlashBuffer (
+ IN UINT8 *Buffer,
+ IN UINTN BufferSize
+ )
+{
+ BOOLEAN IsEmpty;
+ UINT8 *Ptr;
+ UINTN Index;
+
+ Ptr = Buffer;
+ IsEmpty = TRUE;
+ for (Index = 0; Index < BufferSize; Index += 1) {
+ if (*Ptr++ != FTW_ERASED_BYTE) {
+ IsEmpty = FALSE;
+ break;
+ }
+ }
+
+ return IsEmpty;
+}
+
+/**
+ To erase the block with specified blocks.
+
+
+ @param FtwDevice The private data of FTW driver
+ @param FvBlock FVB Protocol interface
+ @param Lba Lba of the firmware block
+ @param NumberOfBlocks The number of consecutive blocks starting with Lba
+
+ @retval EFI_SUCCESS Block LBA is Erased successfully
+ @retval Others Error occurs
+
+**/
+EFI_STATUS
+FtwEraseBlock (
+ IN EFI_FTW_DEVICE *FtwDevice,
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
+ EFI_LBA Lba,
+ UINTN NumberOfBlocks
+ )
+{
+ return FvBlock->EraseBlocks (
+ FvBlock,
+ Lba,
+ NumberOfBlocks,
+ EFI_LBA_LIST_TERMINATOR
+ );
+}
+
+/**
+ Erase spare block.
+
+ @param FtwDevice The private data of FTW driver
+
+ @retval EFI_SUCCESS The erase request was successfully completed.
+ @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state.
+ @retval EFI_DEVICE_ERROR The block device is not functioning
+ correctly and could not be written.
+ The firmware device may have been
+ partially erased.
+ @retval EFI_INVALID_PARAMETER One or more of the LBAs listed
+ in the variable argument list do
+ not exist in the firmware volume.
+
+
+**/
+EFI_STATUS
+FtwEraseSpareBlock (
+ IN EFI_FTW_DEVICE *FtwDevice
+ )
+{
+ return FtwDevice->FtwBackupFvb->EraseBlocks (
+ FtwDevice->FtwBackupFvb,
+ FtwDevice->FtwSpareLba,
+ FtwDevice->NumberOfSpareBlock,
+ EFI_LBA_LIST_TERMINATOR
+ );
+}
+
+/**
+
+ Is it in working block?
+
+ @param FtwDevice The private data of FTW driver
+ @param FvBlock Fvb protocol instance
+ @param Lba The block specified
+
+ @return A BOOLEAN value indicating in working block or not.
+
+**/
+BOOLEAN
+IsWorkingBlock (
+ EFI_FTW_DEVICE *FtwDevice,
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
+ EFI_LBA Lba
+ )
+{
+ //
+ // If matching the following condition, the target block is in working block.
+ // 1. Target block is on the FV of working block (Using the same FVB protocol instance).
+ // 2. Lba falls into the range of working block.
+ //
+ return (BOOLEAN)
+ (
+ (FvBlock == FtwDevice->FtwFvBlock) &&
+ (Lba >= FtwDevice->FtwWorkBlockLba) &&
+ (Lba <= FtwDevice->FtwWorkSpaceLba)
+ );
+}
+
+/**
+
+ Get firmware volume block by address.
+
+
+ @param Address Address specified the block
+ @param FvBlock The block caller wanted
+
+ @retval EFI_SUCCESS The protocol instance if found.
+ @retval EFI_NOT_FOUND Block not found
+
+**/
+EFI_HANDLE
+GetFvbByAddress (
+ IN EFI_PHYSICAL_ADDRESS Address,
+ OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE *HandleBuffer;
+ UINTN HandleCount;
+ UINTN Index;
+ EFI_PHYSICAL_ADDRESS FvbBaseAddress;
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
+ EFI_HANDLE FvbHandle;
+ UINTN BlockSize;
+ UINTN NumberOfBlocks;
+
+ *FvBlock = NULL;
+ FvbHandle = NULL;
+ HandleBuffer = NULL;
+ //
+ // Locate all handles of Fvb protocol
+ //
+ Status = GetFvbCountAndBuffer (&HandleCount, &HandleBuffer);
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+ //
+ // Get the FVB to access variable store
+ //
+ for (Index = 0; Index < HandleCount; Index += 1) {
+ Status = FtwGetFvbByHandle (HandleBuffer[Index], &Fvb);
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ //
+ // Compare the address and select the right one
+ //
+ Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress);
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
+ //
+ // Now, one FVB has one type of BlockSize
+ //
+ Status = Fvb->GetBlockSize (Fvb, 0, &BlockSize, &NumberOfBlocks);
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
+ if ((Address >= FvbBaseAddress) && (Address < (FvbBaseAddress + BlockSize * NumberOfBlocks))) {
+ *FvBlock = Fvb;
+ FvbHandle = HandleBuffer[Index];
+ break;
+ }
+ }
+
+ FreePool (HandleBuffer);
+ return FvbHandle;
+}
+
+/**
+
+ Is it in boot block?
+
+ @param FtwDevice The private data of FTW driver
+ @param FvBlock Fvb protocol instance
+
+ @return A BOOLEAN value indicating in boot block or not.
+
+**/
+BOOLEAN
+IsBootBlock (
+ EFI_FTW_DEVICE *FtwDevice,
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock
+ )
+{
+ EFI_STATUS Status;
+ EFI_SWAP_ADDRESS_RANGE_PROTOCOL *SarProtocol;
+ EFI_PHYSICAL_ADDRESS BootBlockBase;
+ UINTN BootBlockSize;
+ EFI_PHYSICAL_ADDRESS BackupBlockBase;
+ UINTN BackupBlockSize;
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *BootFvb;
+ BOOLEAN IsSwapped;
+ EFI_HANDLE FvbHandle;
+
+ if (!FeaturePcdGet(PcdFullFtwServiceEnable)) {
+ return FALSE;
+ }
+
+ Status = FtwGetSarProtocol ((VOID **) &SarProtocol);
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+ //
+ // Get the boot block range
+ //
+ Status = SarProtocol->GetRangeLocation (
+ SarProtocol,
+ &BootBlockBase,
+ &BootBlockSize,
+ &BackupBlockBase,
+ &BackupBlockSize
+ );
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+
+ Status = SarProtocol->GetSwapState (SarProtocol, &IsSwapped);
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+ //
+ // Get FVB by address
+ //
+ if (!IsSwapped) {
+ FvbHandle = GetFvbByAddress (BootBlockBase, &BootFvb);
+ } else {
+ FvbHandle = GetFvbByAddress (BackupBlockBase, &BootFvb);
+ }
+
+ if (FvbHandle == NULL) {
+ return FALSE;
+ }
+ //
+ // Compare the Fvb
+ //
+ return (BOOLEAN) (FvBlock == BootFvb);
+}
+
+/**
+ Copy the content of spare block to a boot block. Size is FTW_BLOCK_SIZE.
+ Spare block is accessed by FTW working FVB protocol interface.
+ Target block is accessed by FvBlock protocol interface.
+
+ FTW will do extra work on boot block update.
+ FTW should depend on a protocol of EFI_ADDRESS_RANGE_SWAP_PROTOCOL,
+ which is produced by a chipset driver.
+ FTW updating boot block steps may be:
+ 1. GetRangeLocation(), if the Range is inside the boot block, FTW know
+ that boot block will be update. It shall add a FLAG in the working block.
+ 2. When spare block is ready,
+ 3. SetSwapState(SWAPPED)
+ 4. erasing boot block,
+ 5. programming boot block until the boot block is ok.
+ 6. SetSwapState(UNSWAPPED)
+ FTW shall not allow to update boot block when battery state is error.
+
+ @param FtwDevice The private data of FTW driver
+
+ @retval EFI_SUCCESS Spare block content is copied to boot block
+ @retval EFI_INVALID_PARAMETER Input parameter error
+ @retval EFI_OUT_OF_RESOURCES Allocate memory error
+ @retval EFI_ABORTED The function could not complete successfully
+
+**/
+EFI_STATUS
+FlushSpareBlockToBootBlock (
+ EFI_FTW_DEVICE *FtwDevice
+ )
+{
+ EFI_STATUS Status;
+ UINTN Length;
+ UINT8 *Buffer;
+ UINTN Count;
+ UINT8 *Ptr;
+ UINTN Index;
+ BOOLEAN TopSwap;
+ EFI_SWAP_ADDRESS_RANGE_PROTOCOL *SarProtocol;
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *BootFvb;
+ EFI_LBA BootLba;
+
+ if (!FeaturePcdGet(PcdFullFtwServiceEnable)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Locate swap address range protocol
+ //
+ Status = FtwGetSarProtocol ((VOID **) &SarProtocol);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // Allocate a memory buffer
+ //
+ Length = FtwDevice->SpareAreaLength;
+ Buffer = AllocatePool (Length);
+ if (Buffer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // Get TopSwap bit state
+ //
+ Status = SarProtocol->GetSwapState (SarProtocol, &TopSwap);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Ftw: Get Top Swapped status - %r\n", Status));
+ FreePool (Buffer);
+ return EFI_ABORTED;
+ }
+
+ if (TopSwap) {
+ //
+ // Get FVB of current boot block
+ //
+ if (GetFvbByAddress (FtwDevice->SpareAreaAddress + FtwDevice->SpareAreaLength, &BootFvb) == NULL) {
+ FreePool (Buffer);
+ return EFI_ABORTED;
+ }
+ //
+ // Read data from current boot block
+ //
+ BootLba = 0;
+ Ptr = Buffer;
+ for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
+ Count = FtwDevice->SpareBlockSize;
+ Status = BootFvb->Read (
+ BootFvb,
+ BootLba + Index,
+ 0,
+ &Count,
+ Ptr
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (Buffer);
+ return Status;
+ }
+
+ Ptr += Count;
+ }
+ } else {
+ //
+ // Read data from spare block
+ //
+ Ptr = Buffer;
+ for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
+ Count = FtwDevice->SpareBlockSize;
+ Status = FtwDevice->FtwBackupFvb->Read (
+ FtwDevice->FtwBackupFvb,
+ FtwDevice->FtwSpareLba + Index,
+ 0,
+ &Count,
+ Ptr
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (Buffer);
+ return Status;
+ }
+
+ Ptr += Count;
+ }
+ //
+ // Set TopSwap bit
+ //
+ Status = SarProtocol->SetSwapState (SarProtocol, TRUE);
+ if (EFI_ERROR (Status)) {
+ FreePool (Buffer);
+ return Status;
+ }
+ }
+ //
+ // Erase current spare block
+ // Because TopSwap is set, this actually erase the top block (boot block)!
+ //
+ Status = FtwEraseSpareBlock (FtwDevice);
+ if (EFI_ERROR (Status)) {
+ FreePool (Buffer);
+ return EFI_ABORTED;
+ }
+ //
+ // Write memory buffer to current spare block. Still top block.
+ //
+ Ptr = Buffer;
+ for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
+ Count = FtwDevice->SpareBlockSize;
+ Status = FtwDevice->FtwBackupFvb->Write (
+ FtwDevice->FtwBackupFvb,
+ FtwDevice->FtwSpareLba + Index,
+ 0,
+ &Count,
+ Ptr
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Ftw: FVB Write boot block - %r\n", Status));
+ FreePool (Buffer);
+ return Status;
+ }
+
+ Ptr += Count;
+ }
+
+ FreePool (Buffer);
+
+ //
+ // Clear TopSwap bit
+ //
+ Status = SarProtocol->SetSwapState (SarProtocol, FALSE);
+
+ return Status;
+}
+
+/**
+ Copy the content of spare block to a target block.
+ Spare block is accessed by FTW backup FVB protocol interface.
+ Target block is accessed by FvBlock protocol interface.
+
+
+ @param FtwDevice The private data of FTW driver
+ @param FvBlock FVB Protocol interface to access target block
+ @param Lba Lba of the target block
+ @param BlockSize The size of the block
+ @param NumberOfBlocks The number of consecutive blocks starting with Lba
+
+ @retval EFI_SUCCESS Spare block content is copied to target block
+ @retval EFI_INVALID_PARAMETER Input parameter error
+ @retval EFI_OUT_OF_RESOURCES Allocate memory error
+ @retval EFI_ABORTED The function could not complete successfully
+
+**/
+EFI_STATUS
+FlushSpareBlockToTargetBlock (
+ EFI_FTW_DEVICE *FtwDevice,
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
+ EFI_LBA Lba,
+ UINTN BlockSize,
+ UINTN NumberOfBlocks
+ )
+{
+ EFI_STATUS Status;
+ UINTN Length;
+ UINT8 *Buffer;
+ UINTN Count;
+ UINT8 *Ptr;
+ UINTN Index;
+
+ if ((FtwDevice == NULL) || (FvBlock == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // Allocate a memory buffer
+ //
+ Length = FtwDevice->SpareAreaLength;
+ Buffer = AllocatePool (Length);
+ if (Buffer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // Read all content of spare block to memory buffer
+ //
+ Ptr = Buffer;
+ for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
+ Count = FtwDevice->SpareBlockSize;
+ Status = FtwDevice->FtwBackupFvb->Read (
+ FtwDevice->FtwBackupFvb,
+ FtwDevice->FtwSpareLba + Index,
+ 0,
+ &Count,
+ Ptr
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (Buffer);
+ return Status;
+ }
+
+ Ptr += Count;
+ }
+ //
+ // Erase the target block
+ //
+ Status = FtwEraseBlock (FtwDevice, FvBlock, Lba, NumberOfBlocks);
+ if (EFI_ERROR (Status)) {
+ FreePool (Buffer);
+ return EFI_ABORTED;
+ }
+ //
+ // Write memory buffer to block, using the FvBlock protocol interface
+ //
+ Ptr = Buffer;
+ for (Index = 0; Index < NumberOfBlocks; Index += 1) {
+ Count = BlockSize;
+ Status = FvBlock->Write (FvBlock, Lba + Index, 0, &Count, Ptr);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Ftw: FVB Write block - %r\n", Status));
+ FreePool (Buffer);
+ return Status;
+ }
+
+ Ptr += Count;
+ }
+
+ FreePool (Buffer);
+
+ return Status;
+}
+
+/**
+ Copy the content of spare block to working block. Size is FTW_BLOCK_SIZE.
+ Spare block is accessed by FTW backup FVB protocol interface. LBA is
+ FtwDevice->FtwSpareLba.
+ Working block is accessed by FTW working FVB protocol interface. LBA is
+ FtwDevice->FtwWorkBlockLba.
+
+ Since the working block header is important when FTW initializes, the
+ state of the operation should be handled carefully. The Crc value is
+ calculated without STATE element.
+
+ @param FtwDevice The private data of FTW driver
+
+ @retval EFI_SUCCESS Spare block content is copied to target block
+ @retval EFI_OUT_OF_RESOURCES Allocate memory error
+ @retval EFI_ABORTED The function could not complete successfully
+
+**/
+EFI_STATUS
+FlushSpareBlockToWorkingBlock (
+ EFI_FTW_DEVICE *FtwDevice
+ )
+{
+ EFI_STATUS Status;
+ UINTN Length;
+ UINT8 *Buffer;
+ EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader;
+ UINTN Count;
+ UINT8 *Ptr;
+ UINTN Index;
+
+ //
+ // Allocate a memory buffer
+ //
+ Length = FtwDevice->SpareAreaLength;
+ Buffer = AllocatePool (Length);
+ if (Buffer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // To guarantee that the WorkingBlockValid is set on spare block
+ //
+ // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
+ // WorkingBlockValid);
+ // To skip Signature and Crc: sizeof(EFI_GUID)+sizeof(UINT32).
+ //
+ FtwUpdateFvState (
+ FtwDevice->FtwBackupFvb,
+ FtwDevice->SpareBlockSize,
+ FtwDevice->FtwSpareLba + FtwDevice->FtwWorkSpaceLbaInSpare,
+ FtwDevice->FtwWorkSpaceBaseInSpare + sizeof (EFI_GUID) + sizeof (UINT32),
+ WORKING_BLOCK_VALID
+ );
+ //
+ // Read from spare block to memory buffer
+ //
+ Ptr = Buffer;
+ for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
+ Count = FtwDevice->SpareBlockSize;
+ Status = FtwDevice->FtwBackupFvb->Read (
+ FtwDevice->FtwBackupFvb,
+ FtwDevice->FtwSpareLba + Index,
+ 0,
+ &Count,
+ Ptr
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (Buffer);
+ return Status;
+ }
+
+ Ptr += Count;
+ }
+ //
+ // Clear the CRC and STATE, copy data from spare to working block.
+ //
+ WorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (Buffer + (UINTN) FtwDevice->FtwWorkSpaceLbaInSpare * FtwDevice->SpareBlockSize + FtwDevice->FtwWorkSpaceBaseInSpare);
+ InitWorkSpaceHeader (WorkingBlockHeader);
+ WorkingBlockHeader->WorkingBlockValid = FTW_ERASE_POLARITY;
+ WorkingBlockHeader->WorkingBlockInvalid = FTW_ERASE_POLARITY;
+
+ //
+ // target block is working block, then
+ // Set WorkingBlockInvalid in EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
+ // before erase the working block.
+ //
+ // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
+ // WorkingBlockInvalid);
+ // So hardcode offset as sizeof(EFI_GUID)+sizeof(UINT32) to
+ // skip Signature and Crc.
+ //
+ Status = FtwUpdateFvState (
+ FtwDevice->FtwFvBlock,
+ FtwDevice->WorkBlockSize,
+ FtwDevice->FtwWorkSpaceLba,
+ FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),
+ WORKING_BLOCK_INVALID
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (Buffer);
+ return EFI_ABORTED;
+ }
+
+ FtwDevice->FtwWorkSpaceHeader->WorkingBlockInvalid = FTW_VALID_STATE;
+
+ //
+ // Erase the working block
+ //
+ Status = FtwEraseBlock (FtwDevice, FtwDevice->FtwFvBlock, FtwDevice->FtwWorkBlockLba, FtwDevice->NumberOfWorkBlock);
+ if (EFI_ERROR (Status)) {
+ FreePool (Buffer);
+ return EFI_ABORTED;
+ }
+ //
+ // Write memory buffer to working block, using the FvBlock protocol interface
+ //
+ Ptr = Buffer;
+ for (Index = 0; Index < FtwDevice->NumberOfWorkBlock; Index += 1) {
+ Count = FtwDevice->WorkBlockSize;
+ Status = FtwDevice->FtwFvBlock->Write (
+ FtwDevice->FtwFvBlock,
+ FtwDevice->FtwWorkBlockLba + Index,
+ 0,
+ &Count,
+ Ptr
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Ftw: FVB Write block - %r\n", Status));
+ FreePool (Buffer);
+ return Status;
+ }
+
+ Ptr += Count;
+ }
+ //
+ // Since the memory buffer will not be used, free memory Buffer.
+ //
+ FreePool (Buffer);
+
+ //
+ // Update the VALID of the working block
+ //
+ // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER, WorkingBlockValid);
+ // So hardcode offset as sizeof(EFI_GUID)+sizeof(UINT32) to skip Signature and Crc.
+ //
+ Status = FtwUpdateFvState (
+ FtwDevice->FtwFvBlock,
+ FtwDevice->WorkBlockSize,
+ FtwDevice->FtwWorkSpaceLba,
+ FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),
+ WORKING_BLOCK_VALID
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_ABORTED;
+ }
+
+ FtwDevice->FtwWorkSpaceHeader->WorkingBlockInvalid = FTW_INVALID_STATE;
+ FtwDevice->FtwWorkSpaceHeader->WorkingBlockValid = FTW_VALID_STATE;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ 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.
+
+
+ @param FvBlock FVB Protocol interface to access SrcBlock and DestBlock
+ @param BlockSize The size of the block
+ @param Lba Lba of a block
+ @param Offset Offset on the Lba
+ @param NewBit New value that will override the old value if it can be change
+
+ @retval EFI_SUCCESS A state bit has been updated successfully
+ @retval Others Access block device error.
+ Notes:
+ Assume all bits of State are inside the same BYTE.
+ @retval EFI_ABORTED Read block fail
+
+**/
+EFI_STATUS
+FtwUpdateFvState (
+ IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
+ IN UINTN BlockSize,
+ IN EFI_LBA Lba,
+ IN UINTN Offset,
+ IN UINT8 NewBit
+ )
+{
+ EFI_STATUS Status;
+ UINT8 State;
+ UINTN Length;
+
+ //
+ // Calculate the real Offset and Lba to write.
+ //
+ while (Offset >= BlockSize) {
+ Offset -= BlockSize;
+ Lba++;
+ }
+
+ //
+ // 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;
+}
+
+/**
+ Get the last Write Header pointer.
+ The last write header is the header whose 'complete' state hasn't been set.
+ After all, this header may be a EMPTY header entry for next Allocate.
+
+
+ @param FtwWorkSpaceHeader Pointer of the working block header
+ @param FtwWorkSpaceSize Size of the work space
+ @param FtwWriteHeader Pointer to retrieve the last write header
+
+ @retval EFI_SUCCESS Get the last write record successfully
+ @retval EFI_ABORTED The FTW work space is damaged
+
+**/
+EFI_STATUS
+FtwGetLastWriteHeader (
+ IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *FtwWorkSpaceHeader,
+ IN UINTN FtwWorkSpaceSize,
+ OUT EFI_FAULT_TOLERANT_WRITE_HEADER **FtwWriteHeader
+ )
+{
+ UINTN Offset;
+ EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader;
+
+ *FtwWriteHeader = NULL;
+ FtwHeader = (EFI_FAULT_TOLERANT_WRITE_HEADER *) (FtwWorkSpaceHeader + 1);
+ Offset = sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER);
+
+ while (FtwHeader->Complete == FTW_VALID_STATE) {
+ Offset += FTW_WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites, FtwHeader->PrivateDataSize);
+ //
+ // If Offset exceed the FTW work space boudary, return error.
+ //
+ if (Offset >= FtwWorkSpaceSize) {
+ *FtwWriteHeader = FtwHeader;
+ return EFI_ABORTED;
+ }
+
+ FtwHeader = (EFI_FAULT_TOLERANT_WRITE_HEADER *) ((UINT8 *) FtwWorkSpaceHeader + Offset);
+ }
+ //
+ // Last write header is found
+ //
+ *FtwWriteHeader = FtwHeader;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Get the last Write Record pointer. The last write Record is the Record
+ whose DestinationCompleted state hasn't been set. After all, this Record
+ may be a EMPTY record entry for next write.
+
+
+ @param FtwWriteHeader Pointer to the write record header
+ @param FtwWriteRecord Pointer to retrieve the last write record
+
+ @retval EFI_SUCCESS Get the last write record successfully
+ @retval EFI_ABORTED The FTW work space is damaged
+
+**/
+EFI_STATUS
+FtwGetLastWriteRecord (
+ IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwWriteHeader,
+ OUT EFI_FAULT_TOLERANT_WRITE_RECORD **FtwWriteRecord
+ )
+{
+ UINTN Index;
+ EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord;
+
+ *FtwWriteRecord = NULL;
+ FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) (FtwWriteHeader + 1);
+
+ //
+ // Try to find the last write record "that has not completed"
+ //
+ for (Index = 0; Index < FtwWriteHeader->NumberOfWrites; Index += 1) {
+ if (FtwRecord->DestinationComplete != FTW_VALID_STATE) {
+ //
+ // The last write record is found
+ //
+ *FtwWriteRecord = FtwRecord;
+ return EFI_SUCCESS;
+ }
+
+ FtwRecord++;
+
+ if (FtwWriteHeader->PrivateDataSize != 0) {
+ FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) ((UINTN) FtwRecord + (UINTN) FtwWriteHeader->PrivateDataSize);
+ }
+ }
+ //
+ // if Index == NumberOfWrites, then
+ // the last record has been written successfully,
+ // but the Header->Complete Flag has not been set.
+ // also return the last record.
+ //
+ if (Index == FtwWriteHeader->NumberOfWrites) {
+ *FtwWriteRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) ((UINTN) FtwRecord - FTW_RECORD_SIZE (FtwWriteHeader->PrivateDataSize));
+ return EFI_SUCCESS;
+ }
+
+ return EFI_ABORTED;
+}
+
+/**
+ To check if FtwRecord is the first record of FtwHeader.
+
+ @param FtwHeader Pointer to the write record header
+ @param FtwRecord Pointer to the write record
+
+ @retval TRUE FtwRecord is the first Record of the FtwHeader
+ @retval FALSE FtwRecord is not the first Record of the FtwHeader
+
+**/
+BOOLEAN
+IsFirstRecordOfWrites (
+ IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader,
+ IN EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord
+ )
+{
+ UINT8 *Head;
+ UINT8 *Ptr;
+
+ Head = (UINT8 *) FtwHeader;
+ Ptr = (UINT8 *) FtwRecord;
+
+ Head += sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER);
+ return (BOOLEAN) (Head == Ptr);
+}
+
+/**
+ To check if FtwRecord is the last record of FtwHeader. Because the
+ FtwHeader has NumberOfWrites & PrivateDataSize, the FtwRecord can be
+ determined if it is the last record of FtwHeader.
+
+ @param FtwHeader Pointer to the write record header
+ @param FtwRecord Pointer to the write record
+
+ @retval TRUE FtwRecord is the last Record of the FtwHeader
+ @retval FALSE FtwRecord is not the last Record of the FtwHeader
+
+**/
+BOOLEAN
+IsLastRecordOfWrites (
+ IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader,
+ IN EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord
+ )
+{
+ UINT8 *Head;
+ UINT8 *Ptr;
+
+ Head = (UINT8 *) FtwHeader;
+ Ptr = (UINT8 *) FtwRecord;
+
+ Head += FTW_WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites - 1, FtwHeader->PrivateDataSize);
+ return (BOOLEAN) (Head == Ptr);
+}
+
+/**
+ To check if FtwRecord is the first record of FtwHeader.
+
+ @param FtwHeader Pointer to the write record header
+ @param FtwRecord Pointer to retrieve the previous write record
+
+ @retval EFI_ACCESS_DENIED Input record is the first record, no previous record is return.
+ @retval EFI_SUCCESS The previous write record is found.
+
+**/
+EFI_STATUS
+GetPreviousRecordOfWrites (
+ IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader,
+ IN OUT EFI_FAULT_TOLERANT_WRITE_RECORD **FtwRecord
+ )
+{
+ UINT8 *Ptr;
+
+ if (IsFirstRecordOfWrites (FtwHeader, *FtwRecord)) {
+ *FtwRecord = NULL;
+ return EFI_ACCESS_DENIED;
+ }
+
+ Ptr = (UINT8 *) (*FtwRecord);
+ Ptr -= FTW_RECORD_SIZE (FtwHeader->PrivateDataSize);
+ *FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) Ptr;
+ return EFI_SUCCESS;
+}
+
+/**
+ Allocate private data for FTW driver and initialize it.
+
+ @param[out] FtwData Pointer to the FTW device structure
+
+ @retval EFI_SUCCESS Initialize the FTW device successfully.
+ @retval EFI_OUT_OF_RESOURCES Allocate memory error
+ @retval EFI_INVALID_PARAMETER Workspace or Spare block does not exist
+
+**/
+EFI_STATUS
+InitFtwDevice (
+ OUT EFI_FTW_DEVICE **FtwData
+ )
+{
+ EFI_FTW_DEVICE *FtwDevice;
+
+ //
+ // Allocate private data of this driver,
+ // Including the FtwWorkSpace[FTW_WORK_SPACE_SIZE].
+ //
+ FtwDevice = AllocateZeroPool (sizeof (EFI_FTW_DEVICE) + PcdGet32 (PcdFlashNvStorageFtwWorkingSize));
+ if (FtwDevice == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.
+ //
+ FtwDevice->WorkSpaceLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwWorkingSize);
+ FtwDevice->SpareAreaLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwSpareSize);
+ if ((FtwDevice->WorkSpaceLength == 0) || (FtwDevice->SpareAreaLength == 0)) {
+ DEBUG ((EFI_D_ERROR, "Ftw: Workspace or Spare block does not exist!\n"));
+ FreePool (FtwDevice);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FtwDevice->Signature = FTW_DEVICE_SIGNATURE;
+ FtwDevice->FtwFvBlock = NULL;
+ FtwDevice->FtwBackupFvb = NULL;
+ FtwDevice->FtwWorkSpaceLba = (EFI_LBA) (-1);
+ FtwDevice->FtwSpareLba = (EFI_LBA) (-1);
+
+ FtwDevice->WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageFtwWorkingBase64);
+ if (FtwDevice->WorkSpaceAddress == 0) {
+ FtwDevice->WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwWorkingBase);
+ }
+
+ FtwDevice->SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageFtwSpareBase64);
+ if (FtwDevice->SpareAreaAddress == 0) {
+ FtwDevice->SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwSpareBase);
+ }
+
+ *FtwData = FtwDevice;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Find the proper Firmware Volume Block protocol for FTW operation.
+
+ @param[in, out] FtwDevice Pointer to the FTW device structure
+
+ @retval EFI_SUCCESS Find the FVB protocol successfully.
+ @retval EFI_NOT_FOUND No proper FVB protocol was found.
+ @retval EFI_ABORTED Some data can not be got or be invalid.
+
+**/
+EFI_STATUS
+FindFvbForFtw (
+ IN OUT EFI_FTW_DEVICE *FtwDevice
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE *HandleBuffer;
+ UINTN HandleCount;
+ UINTN Index;
+ EFI_PHYSICAL_ADDRESS FvbBaseAddress;
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
+ EFI_FVB_ATTRIBUTES_2 Attributes;
+ UINT32 LbaIndex;
+ UINTN BlockSize;
+ UINTN NumberOfBlocks;
+
+ HandleBuffer = NULL;
+
+ //
+ // Get all FVB handle.
+ //
+ Status = GetFvbCountAndBuffer (&HandleCount, &HandleBuffer);
+ if (EFI_ERROR (Status)) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Get the FVB to access variable store
+ //
+ Fvb = NULL;
+ for (Index = 0; Index < HandleCount; Index += 1) {
+ Status = FtwGetFvbByHandle (HandleBuffer[Index], &Fvb);
+ if (EFI_ERROR (Status)) {
+ Status = EFI_NOT_FOUND;
+ break;
+ }
+
+ //
+ // Ensure this FVB protocol support Write operation.
+ //
+ Status = Fvb->GetAttributes (Fvb, &Attributes);
+ if (EFI_ERROR (Status) || ((Attributes & EFI_FVB2_WRITE_STATUS) == 0)) {
+ continue;
+ }
+ //
+ // Compare the address and select the right one
+ //
+ Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress);
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
+ //
+ // Now, one FVB has one type of BlockSize.
+ //
+ Status = Fvb->GetBlockSize (Fvb, 0, &BlockSize, &NumberOfBlocks);
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
+ if ((FtwDevice->FtwFvBlock == NULL) && (FtwDevice->WorkSpaceAddress >= FvbBaseAddress) &&
+ ((FtwDevice->WorkSpaceAddress + FtwDevice->WorkSpaceLength) <= (FvbBaseAddress + BlockSize * NumberOfBlocks))) {
+ FtwDevice->FtwFvBlock = Fvb;
+ //
+ // To get the LBA of work space
+ //
+ for (LbaIndex = 1; LbaIndex <= NumberOfBlocks; LbaIndex += 1) {
+ if ((FtwDevice->WorkSpaceAddress >= (FvbBaseAddress + BlockSize * (LbaIndex - 1)))
+ && (FtwDevice->WorkSpaceAddress < (FvbBaseAddress + BlockSize * LbaIndex))) {
+ FtwDevice->FtwWorkSpaceLba = LbaIndex - 1;
+ //
+ // Get the Work space size and Base(Offset)
+ //
+ FtwDevice->FtwWorkSpaceSize = FtwDevice->WorkSpaceLength;
+ FtwDevice->WorkBlockSize = BlockSize;
+ FtwDevice->FtwWorkSpaceBase = (UINTN) (FtwDevice->WorkSpaceAddress - (FvbBaseAddress + FtwDevice->WorkBlockSize * (LbaIndex - 1)));
+ FtwDevice->NumberOfWorkSpaceBlock = FTW_BLOCKS (FtwDevice->FtwWorkSpaceBase + FtwDevice->FtwWorkSpaceSize, FtwDevice->WorkBlockSize);
+ if (FtwDevice->FtwWorkSpaceSize >= FtwDevice->WorkBlockSize) {
+ //
+ // Check the alignment of work space address and length, they should be block size aligned when work space size is larger than one block size.
+ //
+ if (((FtwDevice->WorkSpaceAddress & (FtwDevice->WorkBlockSize - 1)) != 0) ||
+ ((FtwDevice->WorkSpaceLength & (FtwDevice->WorkBlockSize - 1)) != 0)) {
+ DEBUG ((EFI_D_ERROR, "Ftw: Work space address or length is not block size aligned when work space size is larger than one block size\n"));
+ FreePool (HandleBuffer);
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+ } else if ((FtwDevice->FtwWorkSpaceBase + FtwDevice->FtwWorkSpaceSize) > FtwDevice->WorkBlockSize) {
+ DEBUG ((EFI_D_ERROR, "Ftw: The work space range should not span blocks when work space size is less than one block size\n"));
+ FreePool (HandleBuffer);
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+ break;
+ }
+ }
+ }
+
+ if ((FtwDevice->FtwBackupFvb == NULL) && (FtwDevice->SpareAreaAddress >= FvbBaseAddress) &&
+ ((FtwDevice->SpareAreaAddress + FtwDevice->SpareAreaLength) <= (FvbBaseAddress + BlockSize * NumberOfBlocks))) {
+ FtwDevice->FtwBackupFvb = Fvb;
+ //
+ // To get the LBA of spare
+ //
+ for (LbaIndex = 1; LbaIndex <= NumberOfBlocks; LbaIndex += 1) {
+ if ((FtwDevice->SpareAreaAddress >= (FvbBaseAddress + BlockSize * (LbaIndex - 1)))
+ && (FtwDevice->SpareAreaAddress < (FvbBaseAddress + BlockSize * LbaIndex))) {
+ //
+ // Get the NumberOfSpareBlock and BlockSize
+ //
+ FtwDevice->FtwSpareLba = LbaIndex - 1;
+ FtwDevice->SpareBlockSize = BlockSize;
+ FtwDevice->NumberOfSpareBlock = FtwDevice->SpareAreaLength / FtwDevice->SpareBlockSize;
+ //
+ // Check the range of spare area to make sure that it's in FV range
+ //
+ if ((FtwDevice->FtwSpareLba + FtwDevice->NumberOfSpareBlock) > NumberOfBlocks) {
+ DEBUG ((EFI_D_ERROR, "Ftw: Spare area is out of FV range\n"));
+ FreePool (HandleBuffer);
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+ //
+ // Check the alignment of spare area address and length, they should be block size aligned
+ //
+ if (((FtwDevice->SpareAreaAddress & (FtwDevice->SpareBlockSize - 1)) != 0) ||
+ ((FtwDevice->SpareAreaLength & (FtwDevice->SpareBlockSize - 1)) != 0)) {
+ DEBUG ((EFI_D_ERROR, "Ftw: Spare area address or length is not block size aligned\n"));
+ FreePool (HandleBuffer);
+ //
+ // Report Status Code EFI_SW_EC_ABORTED.
+ //
+ REPORT_STATUS_CODE ((EFI_ERROR_CODE | EFI_ERROR_UNRECOVERED), (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_EC_ABORTED));
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+ break;
+ }
+ }
+ }
+ }
+ FreePool (HandleBuffer);
+
+ if ((FtwDevice->FtwBackupFvb == NULL) || (FtwDevice->FtwFvBlock == NULL) ||
+ (FtwDevice->FtwWorkSpaceLba == (EFI_LBA) (-1)) || (FtwDevice->FtwSpareLba == (EFI_LBA) (-1))) {
+ return EFI_ABORTED;
+ }
+ DEBUG ((EFI_D_INFO, "Ftw: FtwWorkSpaceLba - 0x%lx, WorkBlockSize - 0x%x, FtwWorkSpaceBase - 0x%x\n", FtwDevice->FtwWorkSpaceLba, FtwDevice->WorkBlockSize, FtwDevice->FtwWorkSpaceBase));
+ DEBUG ((EFI_D_INFO, "Ftw: FtwSpareLba - 0x%lx, SpareBlockSize - 0x%x\n", FtwDevice->FtwSpareLba, FtwDevice->SpareBlockSize));
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Initialization for Fault Tolerant Write protocol.
+
+ @param[in, out] FtwDevice Pointer to the FTW device structure
+
+ @retval EFI_SUCCESS Initialize the FTW protocol successfully.
+ @retval EFI_NOT_FOUND No proper FVB protocol was found.
+
+**/
+EFI_STATUS
+InitFtwProtocol (
+ IN OUT EFI_FTW_DEVICE *FtwDevice
+ )
+{
+ EFI_STATUS Status;
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
+ EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader;
+ UINTN Offset;
+ EFI_HANDLE FvbHandle;
+ EFI_LBA WorkSpaceLbaOffset;
+
+ //
+ // Find the right SMM Fvb protocol instance for FTW.
+ //
+ Status = FindFvbForFtw (FtwDevice);
+ if (EFI_ERROR (Status)) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Calculate the start LBA of working block.
+ //
+ if (FtwDevice->FtwWorkSpaceSize >= FtwDevice->WorkBlockSize) {
+ //
+ // Working block is a standalone area which only contains working space.
+ //
+ FtwDevice->NumberOfWorkBlock = FtwDevice->NumberOfWorkSpaceBlock;
+ } else {
+ //
+ // 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.
+ //
+ FtwDevice->NumberOfWorkBlock = (UINTN) (FtwDevice->FtwWorkSpaceLba + FtwDevice->NumberOfWorkSpaceBlock);
+ while (FtwDevice->NumberOfWorkBlock * FtwDevice->WorkBlockSize > FtwDevice->SpareAreaLength) {
+ FtwDevice->NumberOfWorkBlock--;
+ }
+ }
+ FtwDevice->FtwWorkBlockLba = FtwDevice->FtwWorkSpaceLba + FtwDevice->NumberOfWorkSpaceBlock - FtwDevice->NumberOfWorkBlock;
+ DEBUG ((EFI_D_INFO, "Ftw: NumberOfWorkBlock - 0x%x, FtwWorkBlockLba - 0x%lx\n", FtwDevice->NumberOfWorkBlock, FtwDevice->FtwWorkBlockLba));
+
+ //
+ // Calcualte the LBA and base of work space in spare block.
+ // Note: Do not assume Spare Block and Work Block have same block size.
+ //
+ WorkSpaceLbaOffset = FtwDevice->FtwWorkSpaceLba - FtwDevice->FtwWorkBlockLba;
+ FtwDevice->FtwWorkSpaceLbaInSpare = (EFI_LBA) (((UINTN) WorkSpaceLbaOffset * FtwDevice->WorkBlockSize + FtwDevice->FtwWorkSpaceBase) / FtwDevice->SpareBlockSize);
+ FtwDevice->FtwWorkSpaceBaseInSpare = ((UINTN) WorkSpaceLbaOffset * FtwDevice->WorkBlockSize + FtwDevice->FtwWorkSpaceBase) % FtwDevice->SpareBlockSize;
+ DEBUG ((EFI_D_INFO, "Ftw: WorkSpaceLbaInSpare - 0x%lx, WorkSpaceBaseInSpare - 0x%x\n", FtwDevice->FtwWorkSpaceLbaInSpare, FtwDevice->FtwWorkSpaceBaseInSpare));
+
+ //
+ // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.
+ //
+ FtwDevice->FtwWorkSpace = (UINT8 *) (FtwDevice + 1);
+ FtwDevice->FtwWorkSpaceHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) FtwDevice->FtwWorkSpace;
+
+ FtwDevice->FtwLastWriteHeader = NULL;
+ FtwDevice->FtwLastWriteRecord = NULL;
+
+ InitializeLocalWorkSpaceHeader ();
+
+ //
+ // Refresh the working space data from working block
+ //
+ Status = WorkSpaceRefresh (FtwDevice);
+ ASSERT_EFI_ERROR (Status);
+ //
+ // If the working block workspace is not valid, try the spare block
+ //
+ if (!IsValidWorkSpace (FtwDevice->FtwWorkSpaceHeader)) {
+ //
+ // Read from spare block
+ //
+ Status = ReadWorkSpaceData (
+ FtwDevice->FtwBackupFvb,
+ FtwDevice->SpareBlockSize,
+ FtwDevice->FtwSpareLba + FtwDevice->FtwWorkSpaceLbaInSpare,
+ FtwDevice->FtwWorkSpaceBaseInSpare,
+ FtwDevice->FtwWorkSpaceSize,
+ FtwDevice->FtwWorkSpace
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // If spare block is valid, then replace working block content.
+ //
+ if (IsValidWorkSpace (FtwDevice->FtwWorkSpaceHeader)) {
+ Status = FlushSpareBlockToWorkingBlock (FtwDevice);
+ DEBUG ((EFI_D_INFO, "Ftw: Restart working block update in %a() - %r\n",
+ __FUNCTION__, Status));
+ FtwAbort (&FtwDevice->FtwInstance);
+ //
+ // Refresh work space.
+ //
+ Status = WorkSpaceRefresh (FtwDevice);
+ ASSERT_EFI_ERROR (Status);
+ } else {
+ DEBUG ((EFI_D_INFO,
+ "Ftw: Both working and spare blocks are invalid, init workspace\n"));
+ //
+ // If both are invalid, then initialize work space.
+ //
+ SetMem (
+ FtwDevice->FtwWorkSpace,
+ FtwDevice->FtwWorkSpaceSize,
+ FTW_ERASED_BYTE
+ );
+ InitWorkSpaceHeader (FtwDevice->FtwWorkSpaceHeader);
+ //
+ // Initialize the work space
+ //
+ Status = FtwReclaimWorkSpace (FtwDevice, FALSE);
+ ASSERT_EFI_ERROR (Status);
+ }
+ }
+ //
+ // If the FtwDevice->FtwLastWriteRecord is 1st record of write header &&
+ // (! SpareComplete) THEN call Abort().
+ //
+ if ((FtwDevice->FtwLastWriteHeader->HeaderAllocated == FTW_VALID_STATE) &&
+ (FtwDevice->FtwLastWriteRecord->SpareComplete != FTW_VALID_STATE) &&
+ IsFirstRecordOfWrites (FtwDevice->FtwLastWriteHeader, FtwDevice->FtwLastWriteRecord)
+ ) {
+ DEBUG ((EFI_D_ERROR, "Ftw: Init.. find first record not SpareCompleted, abort()\n"));
+ FtwAbort (&FtwDevice->FtwInstance);
+ }
+ //
+ // If Header is incompleted and the last record has completed, then
+ // call Abort() to set the Header->Complete FLAG.
+ //
+ if ((FtwDevice->FtwLastWriteHeader->Complete != FTW_VALID_STATE) &&
+ (FtwDevice->FtwLastWriteRecord->DestinationComplete == FTW_VALID_STATE) &&
+ IsLastRecordOfWrites (FtwDevice->FtwLastWriteHeader, FtwDevice->FtwLastWriteRecord)
+ ) {
+ DEBUG ((EFI_D_ERROR, "Ftw: Init.. find last record completed but header not, abort()\n"));
+ FtwAbort (&FtwDevice->FtwInstance);
+ }
+ //
+ // To check the workspace buffer following last Write header/records is EMPTY or not.
+ // If it's not EMPTY, FTW also need to call reclaim().
+ //
+ FtwHeader = FtwDevice->FtwLastWriteHeader;
+ Offset = (UINT8 *) FtwHeader - FtwDevice->FtwWorkSpace;
+ if (FtwDevice->FtwWorkSpace[Offset] != FTW_ERASED_BYTE) {
+ Offset += FTW_WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites, FtwHeader->PrivateDataSize);
+ }
+
+ if (!IsErasedFlashBuffer (FtwDevice->FtwWorkSpace + Offset, FtwDevice->FtwWorkSpaceSize - Offset)) {
+ Status = FtwReclaimWorkSpace (FtwDevice, TRUE);
+ ASSERT_EFI_ERROR (Status);
+ }
+
+ //
+ // Restart if it's boot block
+ //
+ if ((FtwDevice->FtwLastWriteHeader->Complete != FTW_VALID_STATE) &&
+ (FtwDevice->FtwLastWriteRecord->SpareComplete == FTW_VALID_STATE)
+ ) {
+ if (FtwDevice->FtwLastWriteRecord->BootBlockUpdate == FTW_VALID_STATE) {
+ Status = FlushSpareBlockToBootBlock (FtwDevice);
+ DEBUG ((EFI_D_ERROR, "Ftw: Restart boot block update - %r\n", Status));
+ ASSERT_EFI_ERROR (Status);
+ FtwAbort (&FtwDevice->FtwInstance);
+ } else {
+ //
+ // if (SpareCompleted) THEN Restart to fault tolerant write.
+ //
+ FvbHandle = NULL;
+ FvbHandle = GetFvbByAddress ((EFI_PHYSICAL_ADDRESS) (UINTN) ((INT64) FtwDevice->SpareAreaAddress + FtwDevice->FtwLastWriteRecord->RelativeOffset), &Fvb);
+ if (FvbHandle != NULL) {
+ Status = FtwRestart (&FtwDevice->FtwInstance, FvbHandle);
+ DEBUG ((EFI_D_ERROR, "Ftw: Restart last write - %r\n", Status));
+ ASSERT_EFI_ERROR (Status);
+ }
+ FtwAbort (&FtwDevice->FtwInstance);
+ }
+ }
+ //
+ // Hook the protocol API
+ //
+ FtwDevice->FtwInstance.GetMaxBlockSize = FtwGetMaxBlockSize;
+ FtwDevice->FtwInstance.Allocate = FtwAllocate;
+ FtwDevice->FtwInstance.Write = FtwWrite;
+ FtwDevice->FtwInstance.Restart = FtwRestart;
+ FtwDevice->FtwInstance.Abort = FtwAbort;
+ FtwDevice->FtwInstance.GetLastWrite = FtwGetLastWrite;
+
+ return EFI_SUCCESS;
+}
+
diff --git a/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/SmmFaultTolerantWriteDxe.uni b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/SmmFaultTolerantWriteDxe.uni
new file mode 100644
index 0000000000..b91a6594a7
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/SmmFaultTolerantWriteDxe.uni
@@ -0,0 +1,24 @@
+// /** @file
+// Fault Tolerant Write Smm Driver.
+//
+// This driver installs SMM Fault Tolerant Write (FTW) protocol, which provides fault
+// tolerant write capability in SMM environment for block devices. Its implementation
+// depends on the full functionality SMM FVB protocol that support read, write/erase
+// flash access.
+//
+// Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// 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.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Fault Tolerant Write Smm Driver."
+
+#string STR_MODULE_DESCRIPTION #language en-US "Installs SMM Fault Tolerant Write (FTW) protocol, which provides fault tolerant write capability in SMM environment for block devices. Its implementation depends on the full functionality SMM FVB protocol that support read, write/erase flash access."
+
diff --git a/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/SmmFaultTolerantWriteDxeExtra.uni b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/SmmFaultTolerantWriteDxeExtra.uni
new file mode 100644
index 0000000000..ad1fcc0e70
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/SmmFaultTolerantWriteDxeExtra.uni
@@ -0,0 +1,19 @@
+// /** @file
+// SmmFaultTolerantWriteDxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// 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.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"SMM Fault Tolerant Flash Write Driver"
+
+
diff --git a/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/UpdateWorkingBlock.c b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/UpdateWorkingBlock.c
new file mode 100644
index 0000000000..b4327b5619
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/FaultTolerantWriteDxe/UpdateWorkingBlock.c
@@ -0,0 +1,619 @@
+/** @file
+
+ Internal functions to operate Working Block Space.
+
+Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.<BR>
+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.
+
+**/
+
+
+#include "FaultTolerantWrite.h"
+
+EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER mWorkingBlockHeader = {ZERO_GUID, 0, 0, 0, 0, {0, 0, 0}, 0};
+
+/**
+ Initialize a local work space header.
+
+ Since Signature and WriteQueueSize have been known, Crc can be calculated out,
+ then the work space header will be fixed.
+**/
+VOID
+InitializeLocalWorkSpaceHeader (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Check signature with gEdkiiWorkingBlockSignatureGuid.
+ //
+ if (CompareGuid (&gEdkiiWorkingBlockSignatureGuid, &mWorkingBlockHeader.Signature)) {
+ //
+ // The local work space header has been initialized.
+ //
+ return;
+ }
+
+ SetMem (
+ &mWorkingBlockHeader,
+ sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
+ FTW_ERASED_BYTE
+ );
+
+ //
+ // Here using gEdkiiWorkingBlockSignatureGuid as the signature.
+ //
+ CopyMem (
+ &mWorkingBlockHeader.Signature,
+ &gEdkiiWorkingBlockSignatureGuid,
+ sizeof (EFI_GUID)
+ );
+ mWorkingBlockHeader.WriteQueueSize = PcdGet32 (PcdFlashNvStorageFtwWorkingSize) - sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER);
+
+ //
+ // Crc is calculated with all the fields except Crc and STATE, so leave them as FTW_ERASED_BYTE.
+ //
+
+ //
+ // Calculate the Crc of woking block header
+ //
+ Status = gBS->CalculateCrc32 (
+ &mWorkingBlockHeader,
+ sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
+ &mWorkingBlockHeader.Crc
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ mWorkingBlockHeader.WorkingBlockValid = FTW_VALID_STATE;
+ mWorkingBlockHeader.WorkingBlockInvalid = FTW_INVALID_STATE;
+}
+
+/**
+ Check to see if it is a valid work space.
+
+
+ @param WorkingHeader Pointer of working block header
+
+ @retval TRUE The work space is valid.
+ @retval FALSE The work space is invalid.
+
+**/
+BOOLEAN
+IsValidWorkSpace (
+ IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader
+ )
+{
+ if (WorkingHeader == NULL) {
+ return FALSE;
+ }
+
+ if (CompareMem (WorkingHeader, &mWorkingBlockHeader, sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)) == 0) {
+ return TRUE;
+ }
+
+ DEBUG ((EFI_D_INFO, "Ftw: Work block header check mismatch\n"));
+ return FALSE;
+}
+
+/**
+ Initialize a work space when there is no work space.
+
+ @param WorkingHeader Pointer of working block header
+
+ @retval EFI_SUCCESS The function completed successfully
+ @retval EFI_ABORTED The function could not complete successfully.
+
+**/
+EFI_STATUS
+InitWorkSpaceHeader (
+ IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader
+ )
+{
+ if (WorkingHeader == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CopyMem (WorkingHeader, &mWorkingBlockHeader, sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER));
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Read work space data from work block or spare block.
+
+ @param FvBlock FVB Protocol interface to access the block.
+ @param BlockSize The size of the block.
+ @param Lba Lba of the block.
+ @param Offset The offset within the block.
+ @param Length The number of bytes to read from the block.
+ @param Buffer The data is read.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED The function could not complete successfully.
+
+**/
+EFI_STATUS
+ReadWorkSpaceData (
+ IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
+ IN UINTN BlockSize,
+ IN EFI_LBA Lba,
+ IN UINTN Offset,
+ IN UINTN Length,
+ OUT UINT8 *Buffer
+ )
+{
+ EFI_STATUS Status;
+ UINT8 *Ptr;
+ UINTN MyLength;
+
+ //
+ // Calculate the real Offset and Lba to write.
+ //
+ while (Offset >= BlockSize) {
+ Offset -= BlockSize;
+ Lba++;
+ }
+
+ Ptr = Buffer;
+ while (Length > 0) {
+ if ((Offset + Length) > BlockSize) {
+ MyLength = BlockSize - Offset;
+ } else {
+ MyLength = Length;
+ }
+
+ Status = FvBlock->Read (
+ FvBlock,
+ Lba,
+ Offset,
+ &MyLength,
+ Ptr
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_ABORTED;
+ }
+ Offset = 0;
+ Length -= MyLength;
+ Ptr += MyLength;
+ Lba++;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Write work space data to work block.
+
+ @param FvBlock FVB Protocol interface to access the block.
+ @param BlockSize The size of the block.
+ @param Lba Lba of the block.
+ @param Offset The offset within the block to place the data.
+ @param Length The number of bytes to write to the block.
+ @param Buffer The data to write.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED The function could not complete successfully.
+
+**/
+EFI_STATUS
+WriteWorkSpaceData (
+ IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
+ IN UINTN BlockSize,
+ IN EFI_LBA Lba,
+ IN UINTN Offset,
+ IN UINTN Length,
+ IN UINT8 *Buffer
+ )
+{
+ EFI_STATUS Status;
+ UINT8 *Ptr;
+ UINTN MyLength;
+
+ //
+ // Calculate the real Offset and Lba to write.
+ //
+ while (Offset >= BlockSize) {
+ Offset -= BlockSize;
+ Lba++;
+ }
+
+ Ptr = Buffer;
+ while (Length > 0) {
+ if ((Offset + Length) > BlockSize) {
+ MyLength = BlockSize - Offset;
+ } else {
+ MyLength = Length;
+ }
+
+ Status = FvBlock->Write (
+ FvBlock,
+ Lba,
+ Offset,
+ &MyLength,
+ Ptr
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_ABORTED;
+ }
+ Offset = 0;
+ Length -= MyLength;
+ Ptr += MyLength;
+ Lba++;
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ Read from working block to refresh the work space in memory.
+
+ @param FtwDevice Point to private data of FTW driver
+
+ @retval EFI_SUCCESS The function completed successfully
+ @retval EFI_ABORTED The function could not complete successfully.
+
+**/
+EFI_STATUS
+WorkSpaceRefresh (
+ IN EFI_FTW_DEVICE *FtwDevice
+ )
+{
+ EFI_STATUS Status;
+ UINTN RemainingSpaceSize;
+
+ //
+ // Initialize WorkSpace as FTW_ERASED_BYTE
+ //
+ SetMem (
+ FtwDevice->FtwWorkSpace,
+ FtwDevice->FtwWorkSpaceSize,
+ FTW_ERASED_BYTE
+ );
+
+ //
+ // Read from working block
+ //
+ Status = ReadWorkSpaceData (
+ FtwDevice->FtwFvBlock,
+ FtwDevice->WorkBlockSize,
+ FtwDevice->FtwWorkSpaceLba,
+ FtwDevice->FtwWorkSpaceBase,
+ FtwDevice->FtwWorkSpaceSize,
+ FtwDevice->FtwWorkSpace
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_ABORTED;
+ }
+ //
+ // Refresh the FtwLastWriteHeader
+ //
+ Status = FtwGetLastWriteHeader (
+ FtwDevice->FtwWorkSpaceHeader,
+ FtwDevice->FtwWorkSpaceSize,
+ &FtwDevice->FtwLastWriteHeader
+ );
+ RemainingSpaceSize = FtwDevice->FtwWorkSpaceSize - ((UINTN) FtwDevice->FtwLastWriteHeader - (UINTN) FtwDevice->FtwWorkSpace);
+ DEBUG ((EFI_D_INFO, "Ftw: Remaining work space size - %x\n", RemainingSpaceSize));
+ //
+ // If FtwGetLastWriteHeader() returns error, or the remaining space size is even not enough to contain
+ // one EFI_FAULT_TOLERANT_WRITE_HEADER + one EFI_FAULT_TOLERANT_WRITE_RECORD(It will cause that the header
+ // pointed by FtwDevice->FtwLastWriteHeader or record pointed by FtwDevice->FtwLastWriteRecord may contain invalid data),
+ // it needs to reclaim work space.
+ //
+ if (EFI_ERROR (Status) || RemainingSpaceSize < sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER) + sizeof (EFI_FAULT_TOLERANT_WRITE_RECORD)) {
+ //
+ // reclaim work space in working block.
+ //
+ Status = FtwReclaimWorkSpace (FtwDevice, TRUE);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Ftw: Reclaim workspace - %r\n", Status));
+ return EFI_ABORTED;
+ }
+ //
+ // Read from working block again
+ //
+ Status = ReadWorkSpaceData (
+ FtwDevice->FtwFvBlock,
+ FtwDevice->WorkBlockSize,
+ FtwDevice->FtwWorkSpaceLba,
+ FtwDevice->FtwWorkSpaceBase,
+ FtwDevice->FtwWorkSpaceSize,
+ FtwDevice->FtwWorkSpace
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_ABORTED;
+ }
+
+ Status = FtwGetLastWriteHeader (
+ FtwDevice->FtwWorkSpaceHeader,
+ FtwDevice->FtwWorkSpaceSize,
+ &FtwDevice->FtwLastWriteHeader
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_ABORTED;
+ }
+ }
+ //
+ // Refresh the FtwLastWriteRecord
+ //
+ Status = FtwGetLastWriteRecord (
+ FtwDevice->FtwLastWriteHeader,
+ &FtwDevice->FtwLastWriteRecord
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_ABORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Reclaim the work space on the working block.
+
+ @param FtwDevice Point to private data of FTW driver
+ @param PreserveRecord Whether to preserve the working record is needed
+
+ @retval EFI_SUCCESS The function completed successfully
+ @retval EFI_OUT_OF_RESOURCES Allocate memory error
+ @retval EFI_ABORTED The function could not complete successfully
+
+**/
+EFI_STATUS
+FtwReclaimWorkSpace (
+ IN EFI_FTW_DEVICE *FtwDevice,
+ IN BOOLEAN PreserveRecord
+ )
+{
+ EFI_STATUS Status;
+ UINTN Length;
+ EFI_FAULT_TOLERANT_WRITE_HEADER *Header;
+ UINT8 *TempBuffer;
+ UINTN TempBufferSize;
+ UINTN SpareBufferSize;
+ UINT8 *SpareBuffer;
+ EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader;
+ UINTN Index;
+ UINT8 *Ptr;
+ EFI_LBA WorkSpaceLbaOffset;
+
+ DEBUG ((EFI_D_INFO, "Ftw: start to reclaim work space\n"));
+
+ WorkSpaceLbaOffset = FtwDevice->FtwWorkSpaceLba - FtwDevice->FtwWorkBlockLba;
+
+ //
+ // Read all original data from working block to a memory buffer
+ //
+ TempBufferSize = FtwDevice->NumberOfWorkBlock * FtwDevice->WorkBlockSize;
+ TempBuffer = AllocateZeroPool (TempBufferSize);
+ if (TempBuffer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Ptr = TempBuffer;
+ for (Index = 0; Index < FtwDevice->NumberOfWorkBlock; Index += 1) {
+ Length = FtwDevice->WorkBlockSize;
+ Status = FtwDevice->FtwFvBlock->Read (
+ FtwDevice->FtwFvBlock,
+ FtwDevice->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) WorkSpaceLbaOffset * FtwDevice->WorkBlockSize +
+ FtwDevice->FtwWorkSpaceBase;
+
+ //
+ // Clear the content of buffer that will save the new work space data
+ //
+ SetMem (Ptr, FtwDevice->FtwWorkSpaceSize, FTW_ERASED_BYTE);
+
+ //
+ // Copy EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER to buffer
+ //
+ CopyMem (
+ Ptr,
+ FtwDevice->FtwWorkSpaceHeader,
+ sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)
+ );
+ if (PreserveRecord) {
+ //
+ // Get the last record following the header,
+ //
+ Status = FtwGetLastWriteHeader (
+ FtwDevice->FtwWorkSpaceHeader,
+ FtwDevice->FtwWorkSpaceSize,
+ &FtwDevice->FtwLastWriteHeader
+ );
+ Header = FtwDevice->FtwLastWriteHeader;
+ if (!EFI_ERROR (Status) && (Header != NULL) && (Header->Complete != FTW_VALID_STATE) && (Header->HeaderAllocated == FTW_VALID_STATE)) {
+ CopyMem (
+ Ptr + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
+ FtwDevice->FtwLastWriteHeader,
+ FTW_WRITE_TOTAL_SIZE (Header->NumberOfWrites, Header->PrivateDataSize)
+ );
+ }
+ }
+
+ CopyMem (
+ FtwDevice->FtwWorkSpace,
+ Ptr,
+ FtwDevice->FtwWorkSpaceSize
+ );
+
+ FtwGetLastWriteHeader (
+ FtwDevice->FtwWorkSpaceHeader,
+ FtwDevice->FtwWorkSpaceSize,
+ &FtwDevice->FtwLastWriteHeader
+ );
+
+ FtwGetLastWriteRecord (
+ FtwDevice->FtwLastWriteHeader,
+ &FtwDevice->FtwLastWriteRecord
+ );
+
+ //
+ // Set the WorkingBlockValid and WorkingBlockInvalid as INVALID
+ //
+ WorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (TempBuffer +
+ (UINTN) WorkSpaceLbaOffset * FtwDevice->WorkBlockSize +
+ FtwDevice->FtwWorkSpaceBase);
+ 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 = FtwDevice->SpareAreaLength;
+ SpareBuffer = AllocatePool (SpareBufferSize);
+ if (SpareBuffer == NULL) {
+ FreePool (TempBuffer);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Ptr = SpareBuffer;
+ for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
+ Length = FtwDevice->SpareBlockSize;
+ Status = FtwDevice->FtwBackupFvb->Read (
+ FtwDevice->FtwBackupFvb,
+ FtwDevice->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 (FtwDevice);
+ if (EFI_ERROR (Status)) {
+ FreePool (TempBuffer);
+ FreePool (SpareBuffer);
+ return EFI_ABORTED;
+ }
+ Ptr = TempBuffer;
+ for (Index = 0; TempBufferSize > 0; Index += 1) {
+ if (TempBufferSize > FtwDevice->SpareBlockSize) {
+ Length = FtwDevice->SpareBlockSize;
+ } else {
+ Length = TempBufferSize;
+ }
+ Status = FtwDevice->FtwBackupFvb->Write (
+ FtwDevice->FtwBackupFvb,
+ FtwDevice->FtwSpareLba + Index,
+ 0,
+ &Length,
+ Ptr
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (TempBuffer);
+ FreePool (SpareBuffer);
+ return EFI_ABORTED;
+ }
+
+ Ptr += Length;
+ TempBufferSize -= Length;
+ }
+ //
+ // Free TempBuffer
+ //
+ FreePool (TempBuffer);
+
+ //
+ // Set the WorkingBlockValid in spare block
+ //
+ Status = FtwUpdateFvState (
+ FtwDevice->FtwBackupFvb,
+ FtwDevice->SpareBlockSize,
+ FtwDevice->FtwSpareLba + FtwDevice->FtwWorkSpaceLbaInSpare,
+ FtwDevice->FtwWorkSpaceBaseInSpare + sizeof (EFI_GUID) + sizeof (UINT32),
+ WORKING_BLOCK_VALID
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (SpareBuffer);
+ return EFI_ABORTED;
+ }
+ //
+ // Before erase the working block, set WorkingBlockInvalid in working block.
+ //
+ // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
+ // WorkingBlockInvalid);
+ //
+ Status = FtwUpdateFvState (
+ FtwDevice->FtwFvBlock,
+ FtwDevice->WorkBlockSize,
+ FtwDevice->FtwWorkSpaceLba,
+ FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),
+ WORKING_BLOCK_INVALID
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (SpareBuffer);
+ return EFI_ABORTED;
+ }
+
+ FtwDevice->FtwWorkSpaceHeader->WorkingBlockInvalid = FTW_VALID_STATE;
+
+ //
+ // Write the spare block to working block
+ //
+ Status = FlushSpareBlockToWorkingBlock (FtwDevice);
+ if (EFI_ERROR (Status)) {
+ FreePool (SpareBuffer);
+ return Status;
+ }
+ //
+ // 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) {
+ Length = FtwDevice->SpareBlockSize;
+ Status = FtwDevice->FtwBackupFvb->Write (
+ FtwDevice->FtwBackupFvb,
+ FtwDevice->FtwSpareLba + Index,
+ 0,
+ &Length,
+ Ptr
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (SpareBuffer);
+ return EFI_ABORTED;
+ }
+
+ Ptr += Length;
+ }
+
+ FreePool (SpareBuffer);
+
+ DEBUG ((EFI_D_INFO, "Ftw: reclaim work space successfully\n"));
+
+ return EFI_SUCCESS;
+}