Add full version FaultTolerantWrite Dxe driver.
authorlgao4 <lgao4@6f19259b-4bc3-4df7-8a09-765794883524>
Wed, 4 Mar 2009 01:05:31 +0000 (01:05 +0000)
committerlgao4 <lgao4@6f19259b-4bc3-4df7-8a09-765794883524>
Wed, 4 Mar 2009 01:05:31 +0000 (01:05 +0000)
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@7787 6f19259b-4bc3-4df7-8a09-765794883524

MdeModulePkg/Include/Protocol/FaultTolerantWrite.h [new file with mode: 0644]
MdeModulePkg/Include/Protocol/SwapAddressRange.h [new file with mode: 0644]
MdeModulePkg/MdeModulePkg.dec
MdeModulePkg/MdeModulePkg.dsc
MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.c [new file with mode: 0644]
MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.h [new file with mode: 0644]
MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf [new file with mode: 0644]
MdeModulePkg/Universal/FaultTolerantWriteDxe/FtwMisc.c [new file with mode: 0644]
MdeModulePkg/Universal/FaultTolerantWriteDxe/UpdateWorkingBlock.c [new file with mode: 0644]

diff --git a/MdeModulePkg/Include/Protocol/FaultTolerantWrite.h b/MdeModulePkg/Include/Protocol/FaultTolerantWrite.h
new file mode 100644 (file)
index 0000000..d673627
--- /dev/null
@@ -0,0 +1,207 @@
+/** @file\r
+Fault Tolerant Write protocol provides boot-time service to do fault tolerant \r
+write capability for block devices.  The protocol provides for non-volatile \r
+intermediate storage of the data and private information a caller would need to \r
+recover from a critical fault, such as power failure.   \r
+\r
+Copyright (c) 2009, Intel Corporation                                                         \r
+All rights reserved. This program and the accompanying materials                          \r
+are licensed and made available under the terms and conditions of the BSD License         \r
+which accompanies this distribution.  The full text of the license may be found at        \r
+http://opensource.org/licenses/bsd-license.php                                            \r
+                                                                                          \r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     \r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
+\r
+**/\r
+\r
+#ifndef _FW_FAULT_TOLERANT_WRITE_PROTOCOL_H_\r
+#define _FW_FAULT_TOLERANT_WRITE_PROTOCOL_H_\r
+\r
+#define EFI_FAULT_TOLERANT_WRITE_PROTOCOL_GUID \\r
+  { \\r
+    0x3ebd9e82, 0x2c78, 0x4de6, {0x97, 0x86, 0x8d, 0x4b, 0xfc, 0xb7, 0xc8, 0x81 } \\r
+  }\r
+\r
+//\r
+// Forward reference for pure ANSI compatability\r
+//\r
+typedef struct _EFI_FAULT_TOLERANT_WRITE_PROTOCOL  EFI_FAULT_TOLERANT_WRITE_PROTOCOL;\r
+\r
+/**\r
+  Query the largest block that may be updated in a fault tolerant manner.\r
+\r
+  @param  This                 Indicates a pointer to the calling context.\r
+  @param  BlockSize            A pointer to a caller allocated UINTN that is\r
+                               updated to  indicate the size of the largest block\r
+                               that can be updated.\r
+\r
+  @retval EFI_SUCCESS          The function completed successfully\r
+  @retval EFI_ABORTED          The function could not complete successfully.\r
+\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(EFIAPI *EFI_FAULT_TOLERANT_WRITE_GET_MAX_BLOCK_SIZE) (\r
+  IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL    * This,\r
+  OUT UINTN                               *BlockSize\r
+  );\r
+\r
+/**\r
+  Allocates space for the protocol to maintain information about writes.\r
+  Since writes must be completed in a fault tolerant manner and multiple\r
+  updates will require more resources to be successful, this function\r
+  enables the protocol to ensure that enough space exists to track\r
+  information about the upcoming writes.\r
+\r
+  @param  This                 Indicates a pointer to the calling context.\r
+  @param  CallerId             The GUID identifying the write.\r
+  @param  PrivateDataSize      The size of the caller's private data  that must be\r
+                               recorded for each write.\r
+  @param  NumberOfWrites       The number of fault tolerant block writes  that will\r
+                               need to occur.\r
+\r
+  @retval EFI_SUCCESS          The function completed successfully\r
+  @retval EFI_ABORTED          The function could not complete successfully.\r
+  @retval EFI_ACCESS_DENIED    All allocated writes have not been completed.   All\r
+                               writes must be completed or aborted before  another\r
+                               fault tolerant write can occur.\r
+\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(EFIAPI *EFI_FAULT_TOLERANT_WRITE_ALLOCATE) (\r
+  IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL    * This,\r
+  IN EFI_GUID                             * CallerId,\r
+  IN UINTN                                PrivateDataSize,\r
+  IN UINTN                                NumberOfWrites\r
+  );\r
+\r
+/**\r
+  Starts a target block update. This records information about the write\r
+  in fault tolerant storage and will complete the write in a recoverable\r
+  manner, ensuring at all times that either the original contents or\r
+  the modified contents are available.\r
+\r
+  @param  This                 Calling context\r
+  @param  Lba                  The logical block address of the target block.\r
+  @param  Offset               The offset within the target block to place the\r
+                               data.\r
+  @param  Length               The number of bytes to write to the target block.\r
+  @param  PrivateData          A pointer to private data that the caller requires\r
+                               to  complete any pending writes in the event of a\r
+                               fault.\r
+  @param  FvBlockHandle        The handle of FVB protocol that provides services\r
+                               for  reading, writing, and erasing the target block.\r
+  @param  Buffer               The data to write.\r
+\r
+  @retval EFI_SUCCESS          The function completed successfully\r
+  @retval EFI_ABORTED          The function could not complete successfully.\r
+  @retval EFI_BAD_BUFFER_SIZE  The write would span a block boundary,  which is not\r
+                               a valid action.\r
+  @retval EFI_ACCESS_DENIED    No writes have been allocated.\r
+  @retval EFI_NOT_READY        The last write has not been completed.   Restart ()\r
+                               must be called to complete it.\r
+\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(EFIAPI *EFI_FAULT_TOLERANT_WRITE_WRITE) (\r
+  IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL     * This,\r
+  IN EFI_LBA                               Lba,\r
+  IN UINTN                                 Offset,\r
+  IN UINTN                                 Length,\r
+  IN VOID                                  *PrivateData,\r
+  IN EFI_HANDLE                            FvbHandle,\r
+  IN VOID                                  *Buffer\r
+  );\r
+\r
+/**\r
+  Restarts a previously interrupted write. The caller must provide the\r
+  block protocol needed to complete the interrupted write.\r
+\r
+  @param  This                 Calling context.\r
+  @param  FvBlockProtocol      The handle of FVB protocol that provides services\r
+                               for  reading, writing, and erasing the target block.\r
+\r
+  @retval EFI_SUCCESS          The function completed successfully\r
+  @retval EFI_ABORTED          The function could not complete successfully.\r
+  @retval EFI_ACCESS_DENIED    No pending writes exist.\r
+\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(EFIAPI *EFI_FAULT_TOLERANT_WRITE_RESTART) (\r
+  IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL     * This,\r
+  IN EFI_HANDLE                            FvbHandle\r
+  );\r
+\r
+/**\r
+  Aborts all previous allocated writes.\r
+\r
+  @param  This                 Calling context\r
+\r
+  @retval EFI_SUCCESS          The function completed successfully\r
+  @retval EFI_ABORTED          The function could not complete successfully.\r
+  @retval EFI_NOT_FOUND        No allocated writes exist.\r
+\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(EFIAPI *EFI_FAULT_TOLERANT_WRITE_ABORT) (\r
+  IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL     * This\r
+  );\r
+\r
+/**\r
+  Starts a target block update. This records information about the write\r
+  in fault tolerant storage and will complete the write in a recoverable\r
+  manner, ensuring at all times that either the original contents or\r
+  the modified contents are available.\r
+\r
+  @param  This                 Indicates a pointer to the calling context.\r
+  @param  CallerId             The GUID identifying the last write.\r
+  @param  Lba                  The logical block address of the last write.\r
+  @param  Offset               The offset within the block of the last write.\r
+  @param  Length               The length of the last write.\r
+  @param  PrivateDataSize      On input, the size of the PrivateData buffer.   On\r
+                               output, the size of the private data stored  for\r
+                               this write.\r
+  @param  PrivateData          A pointer to a buffer. The function will copy\r
+                               PrivateDataSize bytes from the private data  stored\r
+                               for this write.\r
+  @param  Complete             A Boolean value with TRUE indicating  that the write\r
+                               was completed.\r
+\r
+  @retval EFI_SUCCESS          The function completed successfully\r
+  @retval EFI_ABORTED          The function could not complete successfully.\r
+  @retval EFI_NOT_FOUND        No allocated writes exist.\r
+\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(EFIAPI *EFI_FAULT_TOLERANT_WRITE_GET_LAST_WRITE) (\r
+  IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL     * This,\r
+  OUT EFI_GUID                             * CallerId,\r
+  OUT EFI_LBA                              *Lba,\r
+  OUT UINTN                                *Offset,\r
+  OUT UINTN                                *Length,\r
+  IN OUT UINTN                             *PrivateDataSize,\r
+  OUT VOID                                 *PrivateData,\r
+  OUT BOOLEAN                              *Complete\r
+  );\r
+\r
+//\r
+// Protocol declaration\r
+//\r
+struct _EFI_FAULT_TOLERANT_WRITE_PROTOCOL {\r
+  EFI_FAULT_TOLERANT_WRITE_GET_MAX_BLOCK_SIZE GetMaxBlockSize;\r
+  EFI_FAULT_TOLERANT_WRITE_ALLOCATE           Allocate;\r
+  EFI_FAULT_TOLERANT_WRITE_WRITE              Write;\r
+  EFI_FAULT_TOLERANT_WRITE_RESTART            Restart;\r
+  EFI_FAULT_TOLERANT_WRITE_ABORT              Abort;\r
+  EFI_FAULT_TOLERANT_WRITE_GET_LAST_WRITE     GetLastWrite;\r
+};\r
+\r
+extern EFI_GUID gEfiFaultTolerantWriteProtocolGuid;\r
+\r
+#endif\r
diff --git a/MdeModulePkg/Include/Protocol/SwapAddressRange.h b/MdeModulePkg/Include/Protocol/SwapAddressRange.h
new file mode 100644 (file)
index 0000000..0aff583
--- /dev/null
@@ -0,0 +1,182 @@
+/** @file\r
+The EFI_SWAP_ADDRESS_RANGE_PROTOCOL is used to abstract the swap operation of boot block \r
+and backup block of FV. This swap is especially needed when updating the boot block of FV. If any \r
+power failure happens during updating boot block, the swapped backup block (now is the boot block) \r
+can boot the machine with old boot block backuped in it. The swap operation is platform dependent, so \r
+other protocols such as FTW (Fault Tolerant Write) should use this protocol instead of handling hardward directly.\r
+\r
+Copyright (c) 2009, Intel Corporation                                                         \r
+All rights reserved. This program and the accompanying materials                          \r
+are licensed and made available under the terms and conditions of the BSD License         \r
+which accompanies this distribution.  The full text of the license may be found at        \r
+http://opensource.org/licenses/bsd-license.php                                            \r
+                                                                                          \r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     \r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
+\r
+**/\r
+\r
+#ifndef _EFI_SWAP_ADDRESS_RANGE_PROTOCOL_H_\r
+#define _EFI_SWAP_ADDRESS_RANGE_PROTOCOL_H_\r
+\r
+#define EFI_SWAP_ADDRESS_RANGE_PROTOCOL_GUID \\r
+  { \\r
+    0x1259f60d, 0xb754, 0x468e, {0xa7, 0x89, 0x4d, 0xb8, 0x5d, 0x55, 0xe8, 0x7e } \\r
+  }\r
+\r
+//\r
+// Forward reference for pure ANSI compatability\r
+//\r
+typedef struct _EFI_SWAP_ADDRESS_RANGE_PROTOCOL  EFI_SWAP_ADDRESS_RANGE_PROTOCOL;\r
+\r
+#define EFI_UNSUPPORT_LOCK  0\r
+#define EFI_SOFTWARE_LOCK   1\r
+#define EFI_HARDWARE_LOCK   2\r
+\r
+typedef UINT8 EFI_SWAP_LOCK_CAPABILITY;\r
+\r
+//\r
+// Protocl APIs\r
+//\r
+\r
+/**\r
+  This service gets the address range location of boot block and backup block.\r
+  The EFI_GET_RANGE_LOCATION service allows caller to get the range location of \r
+  boot block and backup block. \r
+\r
+  @param This          Indicates the calling context.  \r
+  @param BootBlockBase         Base address of current boot block.\r
+  @param BootBlockSize         Size (in bytes) of current boot block.\r
+  @param BackupBlockBase               Base address of current backup block.\r
+  @param BackupBlockSize               Size (in bytes) of current backup block.\r
+\r
+  @retval EFI_SUCCESS  The call was successful.\r
+    \r
+**/\r
+typedef\r
+EFI_STATUS\r
+(EFIAPI *EFI_GET_RANGE_LOCATION) (\r
+  IN EFI_SWAP_ADDRESS_RANGE_PROTOCOL            * This,\r
+  OUT EFI_PHYSICAL_ADDRESS                      * BootBlockBase,\r
+  OUT UINTN                                     *BootBlockSize,\r
+  OUT EFI_PHYSICAL_ADDRESS                      * BackupBlockBase,\r
+  OUT UINTN                                     *BackupBlockSize\r
+  );\r
+\r
+/**\r
+  This service checks if the boot block and backup block has been swapped.\r
+\r
+  The EFI_GET_SWAP_STATE service allows caller to get current swap state of boot block and backup block.\r
+\r
+  @param This          Indicates the calling context.  \r
+  @param SwapState             True if the boot block and backup block has been swapped. \r
+                                   False if the boot block and backup block has not been swapped.\r
+\r
+  @retval EFI_SUCCESS  The call was successful.\r
+    \r
+**/\r
+typedef\r
+EFI_STATUS\r
+(EFIAPI *EFI_GET_SWAP_STATE) (\r
+  IN EFI_SWAP_ADDRESS_RANGE_PROTOCOL            * This,\r
+  OUT BOOLEAN                                   *SwapState\r
+  );\r
+\r
+/**\r
+  This service swaps the boot block and backup block, or swaps them back.\r
+\r
+  The EFI_SET_SWAP_STATE service allows caller to set the swap state of boot block and backup block. \r
+  It also acquires and releases software swap lock during operation. Note the setting of new swap state \r
+  is not affected by the old swap state.\r
+\r
+  @param This          Indicates the calling context.  \r
+  @param NewSwapState          True to swap real boot block and backup block , False to swap them back..\r
+\r
+  @retval EFI_SUCCESS  The call was successful.\r
+  @retval EFI_ABORTED  Set swap state error\r
+    \r
+**/\r
+typedef\r
+EFI_STATUS\r
+(EFIAPI *EFI_SET_SWAP_STATE) (\r
+  IN EFI_SWAP_ADDRESS_RANGE_PROTOCOL            * This,\r
+  IN BOOLEAN                                    NewSwapState\r
+  );\r
+\r
+\r
+\r
+/**\r
+  This service checks if a RTC (Real Time Clock) power failure happened.\r
+\r
+  The EFI_GET_RTC_POWER_STATUS service allows caller to get Real Time Clock power failure status.  \r
+  If parameter RtcPowerFailed is true after function returns, the trickle current (from the main battery or trickle supply) \r
+  has been removed or failed, this means the swap status was lost in some platform (such as IA32). \r
+  So it is recommended to check RTC power status before calling GetSwapState().\r
+\r
+  @param This   Indicates the calling context.  \r
+  @param RtcPowerFailed   True if a RTC (Real Time Clock) power failure has happened.\r
+\r
+  @retval EFI_SUCCESS The call was successful.\r
+    \r
+**/\r
+typedef\r
+EFI_STATUS\r
+(EFIAPI *EFI_GET_RTC_POWER_STATUS) (\r
+  IN EFI_SWAP_ADDRESS_RANGE_PROTOCOL            * This,\r
+  OUT BOOLEAN                                   *RtcPowerFailed\r
+  );\r
+\r
+/**\r
+  This service returns supported lock methods for swap operation in current platform. Could be software lock, hardware lock, or unsupport lock.\r
+\r
+  The EFI_GET_SWAP_LOCK_CAPABILITY service allows caller to get supported lock method for swap operation in current platform. \r
+  Note that software and hardware lock mothod can be used simultaneously.\r
+\r
+  @param This   Indicates the calling context.\r
+  @param LockCapability                Current lock method for swap operation. \r
+\r
+  @retval EFI_SUCCESS The call was successful.\r
+    \r
+**/\r
+typedef\r
+EFI_STATUS\r
+(EFIAPI *EFI_GET_SWAP_LOCK_CAPABILITY) (\r
+  IN EFI_SWAP_ADDRESS_RANGE_PROTOCOL            * This,\r
+  OUT EFI_SWAP_LOCK_CAPABILITY                  * LockCapability\r
+  );\r
+\r
+\r
+\r
+/**\r
+  This service is used to acquire or release appointed kind of lock for Swap Address Range operation.\r
+\r
+  The EFI_GET_SWAP_LOCK_CAPABILITY service allows caller to get supported lock method for swap operation in current platform. \r
+  Note that software and hardware lock mothod can be used simultaneously.\r
+\r
+  @param This    Indicates the calling context.\r
+  @param LockCapability    Indicates which lock to acquire or release.\r
+  @param NewLockState            True to acquire lock, False to release lock.\r
+\r
+  @retval EFI_SUCCESS The call was successful.\r
+    \r
+**/\r
+typedef\r
+EFI_STATUS\r
+(EFIAPI *EFI_SET_SWAP_LOCK) (\r
+  IN EFI_SWAP_ADDRESS_RANGE_PROTOCOL            * This,\r
+  IN EFI_SWAP_LOCK_CAPABILITY                   LockCapability,\r
+  IN BOOLEAN                                    NewLockState\r
+  );\r
+\r
+struct _EFI_SWAP_ADDRESS_RANGE_PROTOCOL {\r
+  EFI_GET_RANGE_LOCATION        GetRangeLocation;       // has output parameters for base and length\r
+  EFI_GET_SWAP_STATE            GetSwapState;           // are ranges swapped or not\r
+  EFI_SET_SWAP_STATE            SetSwapState;           // swap or unswap ranges\r
+  EFI_GET_RTC_POWER_STATUS      GetRtcPowerStatus;      // checks RTC battery, or whatever...\r
+  EFI_GET_SWAP_LOCK_CAPABILITY  GetSwapLockCapability;  // Get TOP_SWAP lock capability,\r
+  EFI_SET_SWAP_LOCK             SetSwapLock;            // Set TOP_SWAP lock state\r
+};\r
+\r
+extern EFI_GUID gEfiSwapAddressRangeProtocolGuid;\r
+\r
+#endif\r
index 32bab8062942b94f03215a03c3be2433942820ef..c8ecd0e3565fc5f7d19d638de7f6888e7fd8dcc9 100644 (file)
   ## Tcg addtional services to measure PeImage and ActionString.\r
   ## Include/Protocol/TcgPlatform.h\r
   gEfiTcgPlatformProtocolGuid    = { 0x8c4c9a41, 0xbf56, 0x4627, { 0x9e, 0xa, 0xc8, 0x38, 0x6d, 0x66, 0x11, 0x5c }}\r
+\r
+  ## Fault Tolerant Write protocol provides boot-time service to do fault tolerant write capability for block devices.\r
+  #  Include/Protocol/FaultTolerantWrite.h\r
+  gEfiFaultTolerantWriteProtocolGuid = { 0x3EBD9E82, 0x2C78, 0x4DE6, { 0x97, 0x86, 0x8D, 0x4B, 0xFC, 0xB7, 0xC8, 0x81 }}\r
+  \r
+  ## This protocol is used to abstract the swap operation of boot block and backup block of boot FV.\r
+  #  Include/Protocol/SwapAddressRange.h\r
+  gEfiSwapAddressRangeProtocolGuid = { 0x1259F60D, 0xB754, 0x468E, { 0xA7, 0x89, 0x4D, 0xB8, 0x5D, 0x55, 0xE8, 0x7E }}\r
   \r
 [PcdsFeatureFlag]\r
   ## Indicate whether platform can support update capsule across a system reset\r
   ##\r
   gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintEnable|TRUE|BOOLEAN|0x0001200a\r
 \r
+  ##\r
+  #  If TRUE, FULL FTW protocol services (total six APIs) will be produced.\r
+  #  If FASLE, only FTW Write service is available.\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFullFtwServiceEnable|TRUE|BOOLEAN|0x0001200a\r
+\r
 [PcdsFixedAtBuild]\r
   ## Dynamic type PCD can be registered callback function for Pcd setting action.\r
   #  PcdMaxPeiPcdCallBackNumberPerPcdEntry indicate maximum number of callback function\r
index eecc51095b59295fb0a70b2ab8b10e1aa00d4281..affd59ac24ac9446473f329802d9598d335264d4 100644 (file)
   MdeModulePkg/Application/VariableInfo/VariableInfo.inf\r
   MdeModulePkg/Universal/Variable/Pei/VariablePei.inf\r
   MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf\r
-\r
+  MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf\r
 \r
 [Components.IA32, Components.X64]\r
   MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxe.inf\r
diff --git a/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.c b/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.c
new file mode 100644 (file)
index 0000000..d31eaaa
--- /dev/null
@@ -0,0 +1,1237 @@
+/** @file\r
+\r
+  This is a simple fault tolerant write driver.\r
+\r
+  This boot service protocol only provides fault tolerant write capability for \r
+  block devices.  The protocol has internal non-volatile intermediate storage \r
+  of the data and private information. It should be able to recover \r
+  automatically from a critical fault, such as power failure. \r
+\r
+  The implementation uses an FTW (Fault Tolerant Write) Work Space. \r
+  This work space is a memory copy of the work space on the Working Block,\r
+  the size of the work space is the FTW_WORK_SPACE_SIZE bytes.\r
+  \r
+  The work space stores each write record as EFI_FTW_RECORD structure.\r
+  The spare block stores the write buffer before write to the target block.\r
+  \r
+  The write record has three states to specify the different phase of write operation.\r
+  1) WRITE_ALLOCATED is that the record is allocated in write space.\r
+     The information of write operation is stored in write record structure.\r
+  2) SPARE_COMPLETED is that the data from write buffer is writed into the spare block as the backup.\r
+  3) WRITE_COMPLETED is that the data is copied from the spare block to the target block.\r
+\r
+  This driver operates the data as the whole size of spare block.\r
+  It first read the SpareAreaLength data from the target block into the spare memory buffer.\r
+  Then copy the write buffer data into the spare memory buffer.\r
+  Then write the spare memory buffer into the spare block.\r
+  Final copy the data from the spare block to the target block.\r
+\r
+  To make this drive work well, the following conditions must be satisfied:\r
+  1. The write NumBytes data must be fit within Spare area. \r
+     Offset + NumBytes <= SpareAreaLength\r
+  2. The whole flash range has the same block size.\r
+  3. Working block is an area which contains working space in its last block and has the same size as spare block.\r
+  4. Working Block area must be in the single one Firmware Volume Block range which FVB protocol is produced on.  \r
+  5. Spare area must be in the single one Firmware Volume Block range which FVB protocol is produced on.\r
+  6. Any write data area (SpareAreaLength Area) which the data will be written into must be \r
+     in the single one Firmware Volume Block range which FVB protocol is produced on.\r
+  7. If write data area (such as Variable range) is enlarged, the spare area range must be enlarged.\r
+     The spare area must be enough large to store the write data before write them into the target range.\r
+  If one of them is not satisfied, FtwWrite may fail.\r
+  Usually, Spare area only takes one block. That's SpareAreaLength = BlockSize, NumberOfSpareBlock = 1.\r
+\r
+Copyright (c) 2006 - 2009, Intel Corporation                                                         \r
+All rights reserved. This program and the accompanying materials                          \r
+are licensed and made available under the terms and conditions of the BSD License         \r
+which accompanies this distribution.  The full text of the license may be found at        \r
+http://opensource.org/licenses/bsd-license.php                                            \r
+                                                                                          \r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     \r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.  \r
+\r
+**/\r
+\r
+#include "FaultTolerantWrite.h"\r
+\r
+\r
+//\r
+// Fault Tolerant Write Protocol API\r
+//\r
+/**\r
+  Query the largest block that may be updated in a fault tolerant manner.\r
+\r
+\r
+  @param This            The pointer to this protocol instance. \r
+  @param BlockSize       A pointer to a caller allocated UINTN that is updated to\r
+                         indicate the size of the largest block that can be updated.\r
+\r
+  @return EFI_SUCCESS   The function completed successfully\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FtwGetMaxBlockSize (\r
+  IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL    *This,\r
+  OUT UINTN                               *BlockSize\r
+  )\r
+{\r
+  EFI_FTW_DEVICE  *FtwDevice;\r
+\r
+  if (!FeaturePcdGet(PcdFullFtwServiceEnable)) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  FtwDevice   = FTW_CONTEXT_FROM_THIS (This);\r
+\r
+  *BlockSize  = FtwDevice->SpareAreaLength;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Allocates space for the protocol to maintain information about writes.\r
+  Since writes must be completed in a fault tolerant manner and multiple\r
+  updates will require more resources to be successful, this function\r
+  enables the protocol to ensure that enough space exists to track\r
+  information about the upcoming writes.\r
+\r
+  All writes must be completed or aborted before another fault tolerant write can occur.\r
+\r
+  @param This            The pointer to this protocol instance. \r
+  @param CallerId        The GUID identifying the write.\r
+  @param PrivateDataSize The size of the caller's private data\r
+                         that must be recorded for each write.\r
+  @param NumberOfWrites  The number of fault tolerant block writes\r
+                         that will need to occur.\r
+\r
+  @return EFI_SUCCESS        The function completed successfully\r
+  @retval EFI_ABORTED        The function could not complete successfully.\r
+  @retval EFI_ACCESS_DENIED  All allocated writes have not been completed.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FtwAllocate (\r
+  IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL    *This,\r
+  IN EFI_GUID                             *CallerId,\r
+  IN UINTN                                PrivateDataSize,\r
+  IN UINTN                                NumberOfWrites\r
+  )\r
+{\r
+  EFI_STATUS                      Status;\r
+  UINTN                           Length;\r
+  UINTN                           Offset;\r
+  EFI_FTW_DEVICE                  *FtwDevice;\r
+  EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader;\r
+\r
+  FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
+\r
+  Status    = WorkSpaceRefresh (FtwDevice);\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_ABORTED;\r
+  }\r
+  //\r
+  // Check if there is enough space for the coming allocation\r
+  //\r
+  if (WRITE_TOTAL_SIZE (NumberOfWrites, PrivateDataSize) > FtwDevice->FtwWorkSpaceHeader->WriteQueueSize) {\r
+    DEBUG ((EFI_D_ERROR, "Ftw: Allocate() request exceed Workspace, Caller: %g\n", CallerId));\r
+    return EFI_BUFFER_TOO_SMALL;\r
+  }\r
+  //\r
+  // Find the last write header and record.\r
+  // If the FtwHeader is complete, skip the completed last write header/records\r
+  //\r
+  FtwHeader = FtwDevice->FtwLastWriteHeader;\r
+\r
+  //\r
+  // Previous write has not completed, access denied.\r
+  //\r
+  if ((FtwHeader->HeaderAllocated == FTW_VALID_STATE) || (FtwHeader->WritesAllocated == FTW_VALID_STATE)) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+  //\r
+  // If workspace is not enough, then reclaim workspace\r
+  //\r
+  Offset = (UINT8 *) FtwHeader - (UINT8 *) FtwDevice->FtwWorkSpace;\r
+  if (Offset + WRITE_TOTAL_SIZE (NumberOfWrites, PrivateDataSize) > FtwDevice->FtwWorkSpaceSize) {\r
+    Status = FtwReclaimWorkSpace (FtwDevice, TRUE);\r
+    if (EFI_ERROR (Status)) {\r
+      return EFI_ABORTED;\r
+    }\r
+\r
+    FtwHeader = FtwDevice->FtwLastWriteHeader;\r
+  }\r
+  //\r
+  // Prepare FTW write header,\r
+  // overwrite the buffer and write to workspace.\r
+  //\r
+  FtwHeader->WritesAllocated  = FTW_INVALID_STATE;\r
+  FtwHeader->Complete         = FTW_INVALID_STATE;\r
+  CopyMem (&FtwHeader->CallerId, CallerId, sizeof (EFI_GUID));\r
+  FtwHeader->NumberOfWrites   = NumberOfWrites;\r
+  FtwHeader->PrivateDataSize  = PrivateDataSize;\r
+  FtwHeader->HeaderAllocated  = FTW_VALID_STATE;\r
+\r
+  Length                      = sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER);\r
+  Status = FtwDevice->FtwFvBlock->Write (\r
+                                    FtwDevice->FtwFvBlock,\r
+                                    FtwDevice->FtwWorkSpaceLba,\r
+                                    FtwDevice->FtwWorkSpaceBase + Offset,\r
+                                    &Length,\r
+                                    (UINT8 *) FtwHeader\r
+                                    );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_ABORTED;\r
+  }\r
+  //\r
+  // Update Header->WriteAllocated as VALID\r
+  //\r
+  Status = FtwUpdateFvState (\r
+            FtwDevice->FtwFvBlock,\r
+            FtwDevice->FtwWorkSpaceLba,\r
+            FtwDevice->FtwWorkSpaceBase + Offset,\r
+            WRITES_ALLOCATED\r
+            );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  DEBUG (\r
+    (EFI_D_ERROR,\r
+    "Ftw: Allocate() success, Caller:%g, # %d\n",\r
+    CallerId,\r
+    NumberOfWrites)\r
+    );\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Write a record with fault tolerant mannaer.\r
+  Since the content has already backuped in spare block, the write is\r
+  guaranteed to be completed with fault tolerant manner.\r
+\r
+  @param This            The pointer to this protocol instance. \r
+  @param Fvb             The FVB protocol that provides services for\r
+                         reading, writing, and erasing the target block.\r
+\r
+  @retval  EFI_SUCCESS          The function completed successfully\r
+  @retval  EFI_ABORTED          The function could not complete successfully\r
+\r
+**/\r
+EFI_STATUS\r
+FtwWriteRecord (\r
+  IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL     *This,\r
+  IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL    *Fvb\r
+  )\r
+{\r
+  EFI_STATUS                      Status;\r
+  EFI_FTW_DEVICE                  *FtwDevice;\r
+  EFI_FAULT_TOLERANT_WRITE_HEADER *Header;\r
+  EFI_FAULT_TOLERANT_WRITE_RECORD *Record;\r
+  UINTN                           Offset;\r
+\r
+  FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
+\r
+  //\r
+  // Spare Complete but Destination not complete,\r
+  // Recover the targt block with the spare block.\r
+  //\r
+  Header  = FtwDevice->FtwLastWriteHeader;\r
+  Record  = FtwDevice->FtwLastWriteRecord;\r
+\r
+  //\r
+  // IF target block is working block, THEN Flush Spare Block To Working Block;\r
+  // ELSE flush spare block to target block, which may be boot block after all.\r
+  //\r
+  if (IsWorkingBlock (FtwDevice, Fvb, Record->Lba)) {\r
+    //\r
+    // If target block is working block,\r
+    // it also need to set SPARE_COMPLETED to spare block.\r
+    //\r
+    Offset = (UINT8 *) Record - FtwDevice->FtwWorkSpace;\r
+    Status = FtwUpdateFvState (\r
+              FtwDevice->FtwBackupFvb,\r
+              FtwDevice->FtwWorkSpaceLba,\r
+              FtwDevice->FtwWorkSpaceBase + Offset,\r
+              SPARE_COMPLETED\r
+              );\r
+    if (EFI_ERROR (Status)) {\r
+      return EFI_ABORTED;\r
+    }\r
+\r
+    Status = FlushSpareBlockToWorkingBlock (FtwDevice);\r
+  } else if (IsBootBlock (FtwDevice, Fvb, Record->Lba)) {\r
+    //\r
+    // Update boot block\r
+    //\r
+    Status = FlushSpareBlockToBootBlock (FtwDevice);\r
+  } else {\r
+    //\r
+    // Update blocks other than working block or boot block\r
+    //\r
+    Status = FlushSpareBlockToTargetBlock (FtwDevice, Fvb, Record->Lba);\r
+  }\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_ABORTED;\r
+  }\r
+  //\r
+  // Record the DestionationComplete in record\r
+  //\r
+  Offset = (UINT8 *) Record - FtwDevice->FtwWorkSpace;\r
+  Status = FtwUpdateFvState (\r
+            FtwDevice->FtwFvBlock,\r
+            FtwDevice->FtwWorkSpaceLba,\r
+            FtwDevice->FtwWorkSpaceBase + Offset,\r
+            DEST_COMPLETED\r
+            );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  Record->DestinationComplete = FTW_VALID_STATE;\r
+\r
+  //\r
+  // If this is the last Write in these write sequence,\r
+  // set the complete flag of write header.\r
+  //\r
+  if (IsLastRecordOfWrites (Header, Record)) {\r
+    Offset = (UINT8 *) Header - FtwDevice->FtwWorkSpace;\r
+    Status = FtwUpdateFvState (\r
+              FtwDevice->FtwFvBlock,\r
+              FtwDevice->FtwWorkSpaceLba,\r
+              FtwDevice->FtwWorkSpaceBase + Offset,\r
+              WRITES_COMPLETED\r
+              );\r
+    Header->Complete = FTW_VALID_STATE;\r
+    if (EFI_ERROR (Status)) {\r
+      return EFI_ABORTED;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Starts a target block update. This function will record data about write\r
+  in fault tolerant storage and will complete the write in a recoverable\r
+  manner, ensuring at all times that either the original contents or\r
+  the modified contents are available.\r
+\r
+  @param This            The pointer to this protocol instance. \r
+  @param Lba             The logical block address of the target block.\r
+  @param Offset          The offset within the target block to place the data.\r
+  @param Length          The number of bytes to write to the target block.\r
+  @param PrivateData     A pointer to private data that the caller requires to\r
+                         complete any pending writes in the event of a fault.\r
+  @param FvBlockHandle   The handle of FVB protocol that provides services for\r
+                         reading, writing, and erasing the target block.\r
+  @param Buffer          The data to write.\r
+\r
+  @retval EFI_SUCCESS          The function completed successfully \r
+  @retval EFI_ABORTED          The function could not complete successfully. \r
+  @retval EFI_BAD_BUFFER_SIZE  The input data can't fit within the spare block. \r
+                               Offset + *NumBytes > SpareAreaLength.\r
+  @retval EFI_ACCESS_DENIED    No writes have been allocated. \r
+  @retval EFI_OUT_OF_RESOURCES Cannot allocate enough memory resource.\r
+  @retval EFI_NOT_FOUND        Cannot find FVB protocol by handle.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FtwWrite (\r
+  IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL     *This,\r
+  IN EFI_LBA                               Lba,\r
+  IN UINTN                                 Offset,\r
+  IN UINTN                                 Length,\r
+  IN VOID                                  *PrivateData,\r
+  IN EFI_HANDLE                            FvBlockHandle,\r
+  IN VOID                                  *Buffer\r
+  )\r
+{\r
+  EFI_STATUS                          Status;\r
+  EFI_FTW_DEVICE                      *FtwDevice;\r
+  EFI_FAULT_TOLERANT_WRITE_HEADER     *Header;\r
+  EFI_FAULT_TOLERANT_WRITE_RECORD     *Record;\r
+  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *Fvb;\r
+  UINTN                               MyLength;\r
+  UINTN                               MyOffset;\r
+  UINTN                               MyBufferSize;\r
+  UINT8                               *MyBuffer;\r
+  UINTN                               SpareBufferSize;\r
+  UINT8                               *SpareBuffer;\r
+  UINTN                               Index;\r
+  UINT8                               *Ptr;\r
+  EFI_PHYSICAL_ADDRESS                FvbPhysicalAddress;\r
+\r
+  FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
+\r
+  Status    = WorkSpaceRefresh (FtwDevice);\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  Header  = FtwDevice->FtwLastWriteHeader;\r
+  Record  = FtwDevice->FtwLastWriteRecord;\r
+  \r
+  if (IsErasedFlashBuffer ((UINT8 *) Header, sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER))) {\r
+    if (PrivateData == NULL) {\r
+      //\r
+      // Ftw Write Header is not allocated.\r
+      // No additional private data, the private data size is zero. Number of record can be set to 1.\r
+      //\r
+      Status = FtwAllocate (This, &gEfiCallerIdGuid, 0, 1);\r
+      if (EFI_ERROR (Status)) {\r
+        return Status;\r
+      }\r
+    } else {\r
+      //\r
+      // Ftw Write Header is not allocated\r
+      // Additional private data is not NULL, the private data size can't be determined.\r
+      //\r
+      DEBUG ((EFI_D_ERROR, "Ftw: no allocates space for write record!\n"));\r
+      DEBUG ((EFI_D_ERROR, "Ftw: Allocate service should be called before Write service!\n"));\r
+      return EFI_NOT_READY;\r
+    }\r
+  }\r
+\r
+  //\r
+  // If Record is out of the range of Header, return access denied.\r
+  //\r
+  if (((UINTN)((UINT8 *) Record - (UINT8 *) Header)) > WRITE_TOTAL_SIZE (Header->NumberOfWrites - 1, Header->PrivateDataSize)) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  //\r
+  // Check the COMPLETE flag of last write header\r
+  //\r
+  if (Header->Complete == FTW_VALID_STATE) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  if (Record->DestinationComplete == FTW_VALID_STATE) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  if ((Record->SpareComplete == FTW_VALID_STATE) && (Record->DestinationComplete != FTW_VALID_STATE)) {\r
+    return EFI_NOT_READY;\r
+  }\r
+  //\r
+  // Check if the input data can fit within the target block\r
+  //\r
+  if ((Offset + Length) > FtwDevice->SpareAreaLength) {\r
+    return EFI_BAD_BUFFER_SIZE;\r
+  }\r
+  //\r
+  // Get the FVB protocol by handle\r
+  //\r
+  Status = FtwGetFvbByHandle (FvBlockHandle, &Fvb);\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  Status = Fvb->GetPhysicalAddress (Fvb, &FvbPhysicalAddress);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_ERROR, "FtwLite: Get FVB physical address - %r\n", Status));\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  //\r
+  // Set BootBlockUpdate FLAG if it's updating boot block.\r
+  //\r
+  if (IsBootBlock (FtwDevice, Fvb, Lba)) {\r
+    Record->BootBlockUpdate = FTW_VALID_STATE;\r
+  }\r
+  //\r
+  // Write the record to the work space.\r
+  //\r
+  Record->Lba     = Lba;\r
+  Record->Offset  = Offset;\r
+  Record->Length  = Length;\r
+  Record->FvBaseAddress = FvbPhysicalAddress;\r
+  CopyMem ((Record + 1), PrivateData, Header->PrivateDataSize);\r
+\r
+  MyOffset  = (UINT8 *) Record - FtwDevice->FtwWorkSpace;\r
+  MyLength  = RECORD_SIZE (Header->PrivateDataSize);\r
+\r
+  Status = FtwDevice->FtwFvBlock->Write (\r
+                                    FtwDevice->FtwFvBlock,\r
+                                    FtwDevice->FtwWorkSpaceLba,\r
+                                    FtwDevice->FtwWorkSpaceBase + MyOffset,\r
+                                    &MyLength,\r
+                                    (UINT8 *) Record\r
+                                    );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_ABORTED;\r
+  }\r
+  //\r
+  // Record has written to working block, then do the data.\r
+  //\r
+  //\r
+  // Allocate a memory buffer\r
+  //\r
+  MyBufferSize  = FtwDevice->SpareAreaLength;\r
+  MyBuffer      = AllocatePool (MyBufferSize);\r
+  if (MyBuffer == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  //\r
+  // Read all original data from target block to memory buffer\r
+  //\r
+  Ptr = MyBuffer;\r
+  for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
+    MyLength  = FtwDevice->BlockSize;\r
+    Status    = Fvb->Read (Fvb, Lba + Index, 0, &MyLength, Ptr);\r
+    if (EFI_ERROR (Status)) {\r
+      FreePool (MyBuffer);\r
+      return EFI_ABORTED;\r
+    }\r
+\r
+    Ptr += MyLength;\r
+  }\r
+  //\r
+  // Overwrite the updating range data with\r
+  // the input buffer content\r
+  //\r
+  CopyMem (MyBuffer + Offset, Buffer, Length);\r
+\r
+  //\r
+  // Try to keep the content of spare block\r
+  // Save spare block into a spare backup memory buffer (Sparebuffer)\r
+  //\r
+  SpareBufferSize = FtwDevice->SpareAreaLength;\r
+  SpareBuffer     = AllocatePool (SpareBufferSize);\r
+  if (SpareBuffer == NULL) {\r
+    FreePool (MyBuffer);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Ptr = SpareBuffer;\r
+  for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
+    MyLength = FtwDevice->BlockSize;\r
+    Status = FtwDevice->FtwBackupFvb->Read (\r
+                                        FtwDevice->FtwBackupFvb,\r
+                                        FtwDevice->FtwSpareLba + Index,\r
+                                        0,\r
+                                        &MyLength,\r
+                                        Ptr\r
+                                        );\r
+    if (EFI_ERROR (Status)) {\r
+      FreePool (MyBuffer);\r
+      FreePool (SpareBuffer);\r
+      return EFI_ABORTED;\r
+    }\r
+\r
+    Ptr += MyLength;\r
+  }\r
+  //\r
+  // Write the memory buffer to spare block\r
+  //\r
+  Status  = FtwEraseSpareBlock (FtwDevice);\r
+  Ptr     = MyBuffer;\r
+  for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
+    MyLength = FtwDevice->BlockSize;\r
+    Status = FtwDevice->FtwBackupFvb->Write (\r
+                                        FtwDevice->FtwBackupFvb,\r
+                                        FtwDevice->FtwSpareLba + Index,\r
+                                        0,\r
+                                        &MyLength,\r
+                                        Ptr\r
+                                        );\r
+    if (EFI_ERROR (Status)) {\r
+      FreePool (MyBuffer);\r
+      FreePool (SpareBuffer);\r
+      return EFI_ABORTED;\r
+    }\r
+\r
+    Ptr += MyLength;\r
+  }\r
+  //\r
+  // Free MyBuffer\r
+  //\r
+  FreePool (MyBuffer);\r
+\r
+  //\r
+  // Set the SpareComplete in the FTW record,\r
+  //\r
+  MyOffset = (UINT8 *) Record - FtwDevice->FtwWorkSpace;\r
+  Status = FtwUpdateFvState (\r
+            FtwDevice->FtwFvBlock,\r
+            FtwDevice->FtwWorkSpaceLba,\r
+            FtwDevice->FtwWorkSpaceBase + MyOffset,\r
+            SPARE_COMPLETED\r
+            );\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (SpareBuffer);\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  Record->SpareComplete = FTW_VALID_STATE;\r
+\r
+  //\r
+  //  Since the content has already backuped in spare block, the write is\r
+  //  guaranteed to be completed with fault tolerant manner.\r
+  //\r
+  Status = FtwWriteRecord (This, Fvb);\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (SpareBuffer);\r
+    return EFI_ABORTED;\r
+  }\r
+  //\r
+  // Restore spare backup buffer into spare block , if no failure happened during FtwWrite.\r
+  //\r
+  Status  = FtwEraseSpareBlock (FtwDevice);\r
+  Ptr     = SpareBuffer;\r
+  for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
+    MyLength = FtwDevice->BlockSize;\r
+    Status = FtwDevice->FtwBackupFvb->Write (\r
+                                        FtwDevice->FtwBackupFvb,\r
+                                        FtwDevice->FtwSpareLba + Index,\r
+                                        0,\r
+                                        &MyLength,\r
+                                        Ptr\r
+                                        );\r
+    if (EFI_ERROR (Status)) {\r
+      FreePool (SpareBuffer);\r
+      return EFI_ABORTED;\r
+    }\r
+\r
+    Ptr += MyLength;\r
+  }\r
+  //\r
+  // All success.\r
+  //\r
+  FreePool (SpareBuffer);\r
+\r
+  DEBUG (\r
+    (EFI_D_ERROR,\r
+    "Ftw: Write() success, (Lba:Offset)=(%lx:0x%x), Length: 0x%x\n",\r
+    Lba,\r
+    Offset,\r
+    Length)\r
+    );\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Restarts a previously interrupted write. The caller must provide the\r
+  block protocol needed to complete the interrupted write.\r
+\r
+  @param This            The pointer to this protocol instance. \r
+  @param FvBlockHandle   The handle of FVB protocol that provides services for\r
+                         reading, writing, and erasing the target block.\r
+\r
+  @retval  EFI_SUCCESS          The function completed successfully\r
+  @retval  EFI_ACCESS_DENIED    No pending writes exist\r
+  @retval  EFI_NOT_FOUND        FVB protocol not found by the handle\r
+  @retval  EFI_ABORTED          The function could not complete successfully\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FtwRestart (\r
+  IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL     *This,\r
+  IN EFI_HANDLE                            FvBlockHandle\r
+  )\r
+{\r
+  EFI_STATUS                          Status;\r
+  EFI_FTW_DEVICE                      *FtwDevice;\r
+  EFI_FAULT_TOLERANT_WRITE_HEADER     *Header;\r
+  EFI_FAULT_TOLERANT_WRITE_RECORD     *Record;\r
+  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *Fvb;\r
+\r
+  FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
+\r
+  Status    = WorkSpaceRefresh (FtwDevice);\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  Header  = FtwDevice->FtwLastWriteHeader;\r
+  Record  = FtwDevice->FtwLastWriteRecord;\r
+\r
+  //\r
+  // Spare Complete but Destination not complete,\r
+  // Recover the targt block with the spare block.\r
+  //\r
+  Status = FtwGetFvbByHandle (FvBlockHandle, &Fvb);\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  //\r
+  // Check the COMPLETE flag of last write header\r
+  //\r
+  if (Header->Complete == FTW_VALID_STATE) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  //\r
+  // Check the flags of last write record\r
+  //\r
+  if (Record->DestinationComplete == FTW_VALID_STATE) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  if ((Record->SpareComplete != FTW_VALID_STATE)) {\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  //\r
+  //  Since the content has already backuped in spare block, the write is\r
+  //  guaranteed to be completed with fault tolerant manner.\r
+  //\r
+  Status = FtwWriteRecord (This, Fvb);\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  //\r
+  // Erase Spare block\r
+  // This is restart, no need to keep spareblock content.\r
+  //\r
+  FtwEraseSpareBlock (FtwDevice);\r
+\r
+  DEBUG ((EFI_D_ERROR, "Ftw: Restart() success \n"));\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Aborts all previous allocated writes.\r
+\r
+  @param This                  The pointer to this protocol instance. \r
+\r
+  @retval EFI_SUCCESS          The function completed successfully\r
+  @retval EFI_ABORTED          The function could not complete successfully.\r
+  @retval EFI_NOT_FOUND        No allocated writes exist.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FtwAbort (\r
+  IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL     *This\r
+  )\r
+{\r
+  EFI_STATUS      Status;\r
+  UINTN           Offset;\r
+  EFI_FTW_DEVICE  *FtwDevice;\r
+\r
+  FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
+\r
+  Status    = WorkSpaceRefresh (FtwDevice);\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  if (FtwDevice->FtwLastWriteHeader->Complete == FTW_VALID_STATE) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+  //\r
+  // Update the complete state of the header as VALID and abort.\r
+  //\r
+  Offset = (UINT8 *) FtwDevice->FtwLastWriteHeader - FtwDevice->FtwWorkSpace;\r
+  Status = FtwUpdateFvState (\r
+            FtwDevice->FtwFvBlock,\r
+            FtwDevice->FtwWorkSpaceLba,\r
+            FtwDevice->FtwWorkSpaceBase + Offset,\r
+            WRITES_COMPLETED\r
+            );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  FtwDevice->FtwLastWriteHeader->Complete = FTW_VALID_STATE;\r
+\r
+  DEBUG ((EFI_D_ERROR, "Ftw: Abort() success \n"));\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Starts a target block update. This records information about the write\r
+  in fault tolerant storage and will complete the write in a recoverable\r
+  manner, ensuring at all times that either the original contents or\r
+  the modified contents are available.\r
+\r
+  @param This            The pointer to this protocol instance. \r
+  @param CallerId        The GUID identifying the last write.\r
+  @param Lba             The logical block address of the last write.\r
+  @param Offset          The offset within the block of the last write.\r
+  @param Length          The length of the last write.\r
+  @param PrivateDataSize bytes from the private data\r
+                         stored for this write.\r
+  @param PrivateData     A pointer to a buffer. The function will copy\r
+  @param Complete        A Boolean value with TRUE indicating\r
+                         that the write was completed.\r
+\r
+  @retval EFI_SUCCESS           The function completed successfully\r
+  @retval EFI_ABORTED           The function could not complete successfully\r
+  @retval EFI_NOT_FOUND         No allocated writes exist\r
+  @retval EFI_BUFFER_TOO_SMALL  Input buffer is not larget enough\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FtwGetLastWrite (\r
+  IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL     *This,\r
+  OUT EFI_GUID                             *CallerId,\r
+  OUT EFI_LBA                              *Lba,\r
+  OUT UINTN                                *Offset,\r
+  OUT UINTN                                *Length,\r
+  IN OUT UINTN                             *PrivateDataSize,\r
+  OUT VOID                                 *PrivateData,\r
+  OUT BOOLEAN                              *Complete\r
+  )\r
+{\r
+  EFI_STATUS                      Status;\r
+  EFI_FTW_DEVICE                  *FtwDevice;\r
+  EFI_FAULT_TOLERANT_WRITE_HEADER *Header;\r
+  EFI_FAULT_TOLERANT_WRITE_RECORD *Record;\r
+\r
+  if (!FeaturePcdGet(PcdFullFtwServiceEnable)) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
+\r
+  Status    = WorkSpaceRefresh (FtwDevice);\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  Header  = FtwDevice->FtwLastWriteHeader;\r
+  Record  = FtwDevice->FtwLastWriteRecord;\r
+\r
+  //\r
+  // If Header is incompleted and the last record has completed, then\r
+  // call Abort() to set the Header->Complete FLAG.\r
+  //\r
+  if ((Header->Complete != FTW_VALID_STATE) &&\r
+      (Record->DestinationComplete == FTW_VALID_STATE) &&\r
+      IsLastRecordOfWrites (Header, Record)\r
+        ) {\r
+\r
+    Status    = FtwAbort (This);\r
+    *Complete = TRUE;\r
+    return EFI_NOT_FOUND;\r
+  }\r
+  //\r
+  // If there is no write header/record, return not found.\r
+  //\r
+  if (Header->HeaderAllocated != FTW_VALID_STATE) {\r
+    *Complete = TRUE;\r
+    return EFI_NOT_FOUND;\r
+  }\r
+  //\r
+  // If this record SpareComplete has not set, then it can not restart.\r
+  //\r
+  if (Record->SpareComplete != FTW_VALID_STATE) {\r
+    if (IsFirstRecordOfWrites (Header, Record)) {\r
+      //\r
+      // The First record cannot be restart and target is still healthy,\r
+      // so abort() is a safe solution.\r
+      //\r
+      FtwAbort (This);\r
+\r
+      *Complete = TRUE;\r
+      return EFI_NOT_FOUND;\r
+    } else {\r
+      //\r
+      // Step back to the previous record\r
+      //\r
+      GetPreviousRecordOfWrites (Header, &Record);\r
+    }\r
+  }\r
+  //\r
+  // Fill all the requested values\r
+  //\r
+  CopyMem (CallerId, &Header->CallerId, sizeof (EFI_GUID));\r
+  *Lba      = Record->Lba;\r
+  *Offset   = Record->Offset;\r
+  *Length   = Record->Length;\r
+  *Complete = (BOOLEAN) (Record->DestinationComplete == FTW_VALID_STATE);\r
+\r
+  if (*PrivateDataSize < Header->PrivateDataSize) {\r
+    *PrivateDataSize  = Header->PrivateDataSize;\r
+    PrivateData       = NULL;\r
+    Status            = EFI_BUFFER_TOO_SMALL;\r
+  } else {\r
+    *PrivateDataSize = Header->PrivateDataSize;\r
+    CopyMem (PrivateData, Record + 1, *PrivateDataSize);\r
+    Status = EFI_SUCCESS;\r
+  }\r
+\r
+  DEBUG ((EFI_D_ERROR, "Ftw: GetLasetWrite() success\n"));\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  This function is the entry point of the Fault Tolerant Write driver.\r
+\r
+  @param ImageHandle     A handle for the image that is initializing this driver\r
+  @param SystemTable     A pointer to the EFI system table\r
+\r
+  @return EFI_SUCCESS           FTW has finished the initialization\r
+  @retval EFI_NOT_FOUND         Locate FVB protocol error\r
+  @retval EFI_OUT_OF_RESOURCES  Allocate memory error\r
+  @retval EFI_VOLUME_CORRUPTED  Firmware volume is error\r
+  @retval EFI_ABORTED           FTW initialization error\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+InitializeFaultTolerantWrite (\r
+  IN EFI_HANDLE         ImageHandle,\r
+  IN EFI_SYSTEM_TABLE   *SystemTable\r
+  )\r
+{\r
+  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *Fvb;\r
+  UINTN                               Index;\r
+  EFI_HANDLE                          *HandleBuffer;\r
+  UINTN                               HandleCount;\r
+  EFI_FIRMWARE_VOLUME_HEADER          *FwVolHeader;\r
+  EFI_PHYSICAL_ADDRESS                BaseAddress;\r
+  EFI_FTW_DEVICE                      *FtwDevice;\r
+  EFI_FAULT_TOLERANT_WRITE_HEADER     *FtwHeader;\r
+  UINTN                               Length;\r
+  EFI_STATUS                          Status;\r
+  UINTN                               Offset;\r
+  EFI_FV_BLOCK_MAP_ENTRY              *FvbMapEntry;\r
+  UINT32                              LbaIndex;\r
+  EFI_HANDLE                          FvbHandle;\r
+\r
+  //\r
+  // Allocate Private data of this driver,\r
+  // INCLUDING THE FtwWorkSpace[FTW_WORK_SPACE_SIZE].\r
+  //\r
+  FvbHandle = NULL;\r
+  FtwDevice = NULL;\r
+  FtwDevice = AllocatePool (sizeof (EFI_FTW_DEVICE) + PcdGet32 (PcdFlashNvStorageFtwWorkingSize));\r
+  if (FtwDevice == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  ZeroMem (FtwDevice, sizeof (EFI_FTW_DEVICE));\r
+  FtwDevice->Signature = FTW_DEVICE_SIGNATURE;\r
+\r
+  //\r
+  // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.\r
+  //\r
+\r
+  FtwDevice->WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwWorkingBase);\r
+  FtwDevice->WorkSpaceLength  = (UINTN) PcdGet32 (PcdFlashNvStorageFtwWorkingSize);\r
+\r
+  FtwDevice->SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwSpareBase);\r
+  FtwDevice->SpareAreaLength  = (UINTN) PcdGet32 (PcdFlashNvStorageFtwSpareSize);\r
+\r
+  if ((FtwDevice->WorkSpaceLength == 0) || (FtwDevice->SpareAreaLength == 0)) {\r
+    DEBUG ((EFI_D_ERROR, "Ftw: Workspace or Spare block does not exist!\n"));\r
+    FreePool (FtwDevice);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  //\r
+  // Locate FVB protocol by handle\r
+  //\r
+  Status = gBS->LocateHandleBuffer (\r
+                  ByProtocol,\r
+                  &gEfiFirmwareVolumeBlockProtocolGuid,\r
+                  NULL,\r
+                  &HandleCount,\r
+                  &HandleBuffer\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (FtwDevice);\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  if (HandleCount <= 0) {\r
+    FreePool (FtwDevice);\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  Fvb                         = NULL;\r
+  FtwDevice->FtwFvBlock       = NULL;\r
+  FtwDevice->FtwBackupFvb     = NULL;\r
+  FtwDevice->FtwWorkSpaceLba  = (EFI_LBA) (-1);\r
+  FtwDevice->FtwSpareLba      = (EFI_LBA) (-1);\r
+  for (Index = 0; Index < HandleCount; Index += 1) {\r
+    Status = gBS->HandleProtocol (\r
+                    HandleBuffer[Index],\r
+                    &gEfiFirmwareVolumeBlockProtocolGuid,\r
+                    (VOID **) &Fvb\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      FreePool (FtwDevice);\r
+      return Status;\r
+    }\r
+\r
+    Status = Fvb->GetPhysicalAddress (Fvb, &BaseAddress);\r
+    if (EFI_ERROR (Status)) {\r
+      continue;\r
+    }\r
+\r
+    FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) BaseAddress);\r
+\r
+    if ((FtwDevice->WorkSpaceAddress >= BaseAddress) &&\r
+        ((FtwDevice->WorkSpaceAddress + FtwDevice->WorkSpaceLength) <= (BaseAddress + FwVolHeader->FvLength))\r
+        ) {\r
+      FtwDevice->FtwFvBlock = Fvb;\r
+      //\r
+      // To get the LBA of work space\r
+      //\r
+      if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) {\r
+        //\r
+        // Now, one FV has one type of BlockLength\r
+        //\r
+        FvbMapEntry = &FwVolHeader->BlockMap[0];\r
+        for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) {\r
+            if ((FtwDevice->WorkSpaceAddress >= (BaseAddress + FvbMapEntry->Length * (LbaIndex - 1)))\r
+              && (FtwDevice->WorkSpaceAddress < (BaseAddress + FvbMapEntry->Length * LbaIndex))) {\r
+            FtwDevice->FtwWorkSpaceLba = LbaIndex - 1;\r
+            //\r
+            // Get the Work space size and Base(Offset)\r
+            //\r
+            FtwDevice->FtwWorkSpaceSize = FtwDevice->WorkSpaceLength;\r
+            FtwDevice->FtwWorkSpaceBase = (UINTN) (FtwDevice->WorkSpaceAddress - (BaseAddress + FvbMapEntry->Length * (LbaIndex - 1)));\r
+            break;\r
+          }\r
+        }\r
+      }\r
+    }\r
+\r
+    if ((FtwDevice->SpareAreaAddress >= BaseAddress) &&\r
+        ((FtwDevice->SpareAreaAddress + FtwDevice->SpareAreaLength) <= (BaseAddress + FwVolHeader->FvLength))\r
+        ) {\r
+      FtwDevice->FtwBackupFvb = Fvb;\r
+      //\r
+      // To get the LBA of spare\r
+      //\r
+      if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) {\r
+        //\r
+        // Now, one FV has one type of BlockLength\r
+        //\r
+        FvbMapEntry = &FwVolHeader->BlockMap[0];\r
+        for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) {\r
+            if ((FtwDevice->SpareAreaAddress >= (BaseAddress + FvbMapEntry->Length * (LbaIndex - 1)))\r
+              && (FtwDevice->SpareAreaAddress < (BaseAddress + FvbMapEntry->Length * LbaIndex))) {\r
+            //\r
+            // Get the NumberOfSpareBlock and BlockSize\r
+            //\r
+            FtwDevice->FtwSpareLba        = LbaIndex - 1;\r
+            FtwDevice->BlockSize          = FvbMapEntry->Length;\r
+            FtwDevice->NumberOfSpareBlock = FtwDevice->SpareAreaLength / FtwDevice->BlockSize;\r
+            //\r
+            // Check the range of spare area to make sure that it's in FV range\r
+            //\r
+            if ((FtwDevice->FtwSpareLba + FtwDevice->NumberOfSpareBlock) > FvbMapEntry->NumBlocks) {\r
+              DEBUG ((EFI_D_ERROR, "Ftw: Spare area is out of FV range\n"));\r
+              FreePool (FtwDevice);\r
+              return EFI_ABORTED;\r
+            }\r
+            break;\r
+          }\r
+        }\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
+  // Calculate the start LBA of working block. Working block is an area which\r
+  // contains working space in its last block and has the same size as spare\r
+  // block, unless there are not enough blocks before the block that contains\r
+  // working space.\r
+  //\r
+  FtwDevice->FtwWorkBlockLba = FtwDevice->FtwWorkSpaceLba - FtwDevice->NumberOfSpareBlock + 1;\r
+  if ((INT64) (FtwDevice->FtwWorkBlockLba) < 0) {\r
+    DEBUG ((EFI_D_ERROR, "Ftw: The spare block range is too large than the working block range!\n"));\r
+    FreePool (FtwDevice);\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  if ((FtwDevice->FtwFvBlock == NULL) ||\r
+      (FtwDevice->FtwBackupFvb == NULL) ||\r
+      (FtwDevice->FtwWorkSpaceLba == (EFI_LBA) (-1)) ||\r
+      (FtwDevice->FtwSpareLba == (EFI_LBA) (-1))\r
+      ) {\r
+    DEBUG ((EFI_D_ERROR, "Ftw: Working or spare FVB not ready\n"));\r
+    FreePool (FtwDevice);\r
+    return EFI_ABORTED;\r
+  }\r
+  //\r
+  // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.\r
+  //\r
+  FtwDevice->FtwWorkSpace = (UINT8 *) (FtwDevice + 1);\r
+  FtwDevice->FtwWorkSpaceHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) FtwDevice->FtwWorkSpace;\r
+\r
+  FtwDevice->FtwLastWriteHeader = NULL;\r
+  FtwDevice->FtwLastWriteRecord = NULL;\r
+\r
+  //\r
+  // Refresh the working space data from working block\r
+  //\r
+  Status = WorkSpaceRefresh (FtwDevice);\r
+  if (EFI_ERROR (Status)) {\r
+    goto Recovery;\r
+  }\r
+  //\r
+  // If the working block workspace is not valid, try the spare block\r
+  //\r
+  if (!IsValidWorkSpace (FtwDevice->FtwWorkSpaceHeader)) {\r
+    //\r
+    // Read from spare block\r
+    //\r
+    Length = FtwDevice->FtwWorkSpaceSize;\r
+    Status = FtwDevice->FtwBackupFvb->Read (\r
+                                        FtwDevice->FtwBackupFvb,\r
+                                        FtwDevice->FtwSpareLba,\r
+                                        FtwDevice->FtwWorkSpaceBase,\r
+                                        &Length,\r
+                                        FtwDevice->FtwWorkSpace\r
+                                        );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Recovery;\r
+    }\r
+    //\r
+    // If spare block is valid, then replace working block content.\r
+    //\r
+    if (IsValidWorkSpace (FtwDevice->FtwWorkSpaceHeader)) {\r
+      Status = FlushSpareBlockToWorkingBlock (FtwDevice);\r
+      DEBUG ((EFI_D_ERROR, "Ftw: Restart working block update in Init() - %r\n", Status));\r
+      FtwAbort (&FtwDevice->FtwInstance);\r
+      //\r
+      // Refresh work space.\r
+      //\r
+      Status = WorkSpaceRefresh (FtwDevice);\r
+      if (EFI_ERROR (Status)) {\r
+        goto Recovery;\r
+      }\r
+    } else {\r
+      DEBUG ((EFI_D_ERROR, "Ftw: Both are invalid, init workspace\n"));\r
+      //\r
+      // If both are invalid, then initialize work space.\r
+      //\r
+      SetMem (\r
+        FtwDevice->FtwWorkSpace,\r
+        FtwDevice->FtwWorkSpaceSize,\r
+        FTW_ERASED_BYTE\r
+        );\r
+      InitWorkSpaceHeader (FtwDevice->FtwWorkSpaceHeader);\r
+      //\r
+      // Initialize the work space\r
+      //\r
+      Status = FtwReclaimWorkSpace (FtwDevice, FALSE);\r
+      if (EFI_ERROR (Status)) {\r
+        goto Recovery;\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
+  // If the FtwDevice->FtwLastWriteRecord is 1st record of write header &&\r
+  // (! SpareComplete)  THEN  call Abort().\r
+  //\r
+  if ((FtwDevice->FtwLastWriteHeader->HeaderAllocated == FTW_VALID_STATE) &&\r
+      (FtwDevice->FtwLastWriteRecord->SpareComplete != FTW_VALID_STATE) &&\r
+      IsFirstRecordOfWrites (FtwDevice->FtwLastWriteHeader, FtwDevice->FtwLastWriteRecord)\r
+        ) {\r
+    DEBUG ((EFI_D_ERROR, "Ftw: Init.. find first record not SpareCompleted, abort()\n"));\r
+    FtwAbort (&FtwDevice->FtwInstance);\r
+  }\r
+  //\r
+  // If Header is incompleted and the last record has completed, then\r
+  // call Abort() to set the Header->Complete FLAG.\r
+  //\r
+  if ((FtwDevice->FtwLastWriteHeader->Complete != FTW_VALID_STATE) &&\r
+      (FtwDevice->FtwLastWriteRecord->DestinationComplete == FTW_VALID_STATE) &&\r
+      IsLastRecordOfWrites (FtwDevice->FtwLastWriteHeader, FtwDevice->FtwLastWriteRecord)\r
+        ) {\r
+    DEBUG ((EFI_D_ERROR, "Ftw: Init.. find last record completed but header not, abort()\n"));\r
+    FtwAbort (&FtwDevice->FtwInstance);\r
+  }\r
+  //\r
+  // To check the workspace buffer following last Write header/records is EMPTY or not.\r
+  // If it's not EMPTY, FTW also need to call reclaim().\r
+  //\r
+  FtwHeader = FtwDevice->FtwLastWriteHeader;\r
+  Offset    = (UINT8 *) FtwHeader - FtwDevice->FtwWorkSpace;\r
+  if (FtwDevice->FtwWorkSpace[Offset] != FTW_ERASED_BYTE) {\r
+    Offset += WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites, FtwHeader->PrivateDataSize);\r
+  }\r
+\r
+  if (!IsErasedFlashBuffer (\r
+        FtwDevice->FtwWorkSpace + Offset,\r
+        FtwDevice->FtwWorkSpaceSize - Offset\r
+        )) {\r
+    Status = FtwReclaimWorkSpace (FtwDevice, TRUE);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Recovery;\r
+    }\r
+  }\r
+  //\r
+  // Restart if it's boot block\r
+  //\r
+  if ((FtwDevice->FtwLastWriteHeader->Complete != FTW_VALID_STATE) &&\r
+      (FtwDevice->FtwLastWriteRecord->SpareComplete == FTW_VALID_STATE)\r
+      ) {\r
+    if (FtwDevice->FtwLastWriteRecord->BootBlockUpdate == FTW_VALID_STATE) {\r
+      Status = FlushSpareBlockToBootBlock (FtwDevice);\r
+      DEBUG ((EFI_D_ERROR, "Ftw: Restart boot block update - %r\n", Status));\r
+      if (EFI_ERROR (Status)) {\r
+        goto Recovery;\r
+      }\r
+  \r
+      FtwAbort (&FtwDevice->FtwInstance);\r
+    } else {\r
+      //\r
+      // if (SpareCompleted) THEN  Restart to fault tolerant write.\r
+      //\r
+      FvbHandle = GetFvbByAddress (FtwDevice->FtwLastWriteRecord->FvBaseAddress, &Fvb);\r
+      if (FvbHandle != NULL) {\r
+        Status = FtwRestart (&FtwDevice->FtwInstance, FvbHandle);\r
+        DEBUG ((EFI_D_ERROR, "FtwLite: Restart last write - %r\n", Status));\r
+        if (EFI_ERROR (Status)) {\r
+          goto Recovery;\r
+        }\r
+      }\r
+      FtwAbort (&FtwDevice->FtwInstance);\r
+    }\r
+  }\r
+\r
+  //\r
+  // Hook the protocol API\r
+  //\r
+  FtwDevice->FtwInstance.GetMaxBlockSize  = FtwGetMaxBlockSize;\r
+  FtwDevice->FtwInstance.Allocate         = FtwAllocate;\r
+  FtwDevice->FtwInstance.Write            = FtwWrite;\r
+  FtwDevice->FtwInstance.Restart          = FtwRestart;\r
+  FtwDevice->FtwInstance.Abort            = FtwAbort;\r
+  FtwDevice->FtwInstance.GetLastWrite     = FtwGetLastWrite;\r
+\r
+  //\r
+  // Install protocol interface\r
+  //\r
+  Status = gBS->InstallProtocolInterface (\r
+                  &FtwDevice->Handle,\r
+                  &gEfiFaultTolerantWriteProtocolGuid,\r
+                  EFI_NATIVE_INTERFACE,\r
+                  &FtwDevice->FtwInstance\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto Recovery;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+Recovery:\r
+\r
+  if (FtwDevice != NULL) {\r
+    FreePool (FtwDevice);\r
+  }\r
+\r
+  DEBUG ((EFI_D_ERROR, "Ftw: Severe Error occurs, need to recovery\n"));\r
+\r
+  return EFI_VOLUME_CORRUPTED;\r
+}\r
diff --git a/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.h b/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.h
new file mode 100644 (file)
index 0000000..3cc6572
--- /dev/null
@@ -0,0 +1,672 @@
+/** @file\r
+\r
+  The internal header file includes the common header files, defines\r
+  internal structure and functions used by FtwLite module.\r
+\r
+Copyright (c) 2006 - 2008, Intel Corporation                                                         \r
+All rights reserved. This program and the accompanying materials                          \r
+are licensed and made available under the terms and conditions of the BSD License         \r
+which accompanies this distribution.  The full text of the license may be found at        \r
+http://opensource.org/licenses/bsd-license.php                                            \r
+                                                                                          \r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     \r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
+\r
+**/\r
+\r
+#ifndef _EFI_FAULT_TOLERANT_WRITE_H_\r
+#define _EFI_FAULT_TOLERANT_WRITE_H_\r
+\r
+#include <PiDxe.h>\r
+\r
+#include <Guid/SystemNvDataGuid.h>\r
+#include <Protocol/FaultTolerantWrite.h>\r
+#include <Protocol/FirmwareVolumeBlock.h>\r
+#include <Protocol/SwapAddressRange.h>\r
+\r
+#include <Library/PcdLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiDriverEntryPoint.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+\r
+//\r
+// Flash erase polarity is 1\r
+//\r
+#define FTW_ERASE_POLARITY  1\r
+\r
+#define FTW_VALID_STATE     0\r
+#define FTW_INVALID_STATE   1\r
+\r
+#define FTW_ERASED_BYTE     ((UINT8) (255))\r
+#define FTW_POLARITY_REVERT ((UINT8) (255))\r
+\r
+//\r
+// EFI Fault tolerant block update write queue entry\r
+//\r
+typedef struct {\r
+  UINT8     HeaderAllocated : 1;\r
+  UINT8     WritesAllocated : 1;\r
+  UINT8     Complete : 1;\r
+#define HEADER_ALLOCATED  0x1\r
+#define WRITES_ALLOCATED  0x2\r
+#define WRITES_COMPLETED  0x4\r
+  UINT8     Reserved : 5;\r
+  EFI_GUID  CallerId;\r
+  UINTN     NumberOfWrites;\r
+  UINTN     PrivateDataSize;\r
+} EFI_FAULT_TOLERANT_WRITE_HEADER;\r
+\r
+//\r
+// EFI Fault tolerant block update write queue record\r
+//\r
+typedef struct {\r
+  UINT8   BootBlockUpdate : 1;\r
+  UINT8   SpareComplete : 1;\r
+  UINT8   DestinationComplete : 1;\r
+#define BOOT_BLOCK_UPDATE 0x1\r
+#define SPARE_COMPLETED   0x2\r
+#define DEST_COMPLETED    0x4\r
+  UINT8   Reserved : 5;\r
+  EFI_LBA Lba;\r
+  UINTN   Offset;\r
+  UINTN   Length;\r
+  EFI_PHYSICAL_ADDRESS  FvBaseAddress;\r
+  //\r
+  // UINT8                PrivateData[PrivateDataSize]\r
+  //\r
+} EFI_FAULT_TOLERANT_WRITE_RECORD;\r
+\r
+\r
+#define RECORD_SIZE(PrivateDataSize)  (sizeof (EFI_FAULT_TOLERANT_WRITE_RECORD) + PrivateDataSize)\r
+\r
+#define RECORD_TOTAL_SIZE(NumberOfWrites, PrivateDataSize) \\r
+    ((NumberOfWrites) * (sizeof (EFI_FAULT_TOLERANT_WRITE_RECORD) + PrivateDataSize))\r
+\r
+#define WRITE_TOTAL_SIZE(NumberOfWrites, PrivateDataSize) \\r
+    ( \\r
+      sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER) + (NumberOfWrites) * \\r
+        (sizeof (EFI_FAULT_TOLERANT_WRITE_RECORD) + PrivateDataSize) \\r
+    )\r
+\r
+#define FTW_DEVICE_SIGNATURE  SIGNATURE_32 ('F', 'T', 'W', 'D')\r
+\r
+//\r
+// EFI Fault tolerant protocol private data structure\r
+//\r
+typedef struct {\r
+  UINTN                                   Signature;\r
+  EFI_HANDLE                              Handle;\r
+  EFI_FAULT_TOLERANT_WRITE_PROTOCOL       FtwInstance;\r
+  EFI_PHYSICAL_ADDRESS                    WorkSpaceAddress;   // Base address of working space range in flash.\r
+  EFI_PHYSICAL_ADDRESS                    SpareAreaAddress;   // Base address of spare range in flash.\r
+  UINTN                                   WorkSpaceLength;    // Size of working space range in flash.\r
+  UINTN                                   SpareAreaLength;    // Size of spare range in flash.\r
+  UINTN                                   NumberOfSpareBlock; // Number of the blocks in spare block.\r
+  UINTN                                   BlockSize;          // Block size in bytes of the blocks in flash\r
+  EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *FtwWorkSpaceHeader;// Pointer to Working Space Header in memory buffer\r
+  EFI_FAULT_TOLERANT_WRITE_HEADER         *FtwLastWriteHeader;// Pointer to last record header in memory buffer\r
+  EFI_FAULT_TOLERANT_WRITE_RECORD         *FtwLastWriteRecord;// Pointer to last record in memory buffer\r
+  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL      *FtwFvBlock;        // FVB of working block\r
+  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL      *FtwBackupFvb;      // FVB of spare block\r
+  EFI_LBA                                 FtwSpareLba;        // Start LBA of spare block\r
+  EFI_LBA                                 FtwWorkBlockLba;    // Start LBA of working block that contains working space in its last block.\r
+  EFI_LBA                                 FtwWorkSpaceLba;    // Start LBA of working space\r
+  UINTN                                   FtwWorkSpaceBase;   // Offset into the FtwWorkSpaceLba block.\r
+  UINTN                                   FtwWorkSpaceSize;   // Size of working space range that stores write record.\r
+  UINT8                                   *FtwWorkSpace;      // Point to Work Space in memory buffer \r
+  //\r
+  // Following a buffer of FtwWorkSpace[FTW_WORK_SPACE_SIZE],\r
+  // Allocated with EFI_FTW_DEVICE.\r
+  //\r
+} EFI_FTW_DEVICE;\r
+\r
+#define FTW_CONTEXT_FROM_THIS(a)  CR (a, EFI_FTW_DEVICE, FtwInstance, FTW_DEVICE_SIGNATURE)\r
+\r
+//\r
+// Driver entry point\r
+//\r
+/**\r
+  This function is the entry point of the Fault Tolerant Write driver.\r
+\r
+  @param ImageHandle     A handle for the image that is initializing this driver\r
+  @param SystemTable     A pointer to the EFI system table\r
+\r
+  @return EFI_SUCCESS           FTW has finished the initialization\r
+  @retval EFI_NOT_FOUND         Locate FVB protocol error\r
+  @retval EFI_OUT_OF_RESOURCES  Allocate memory error\r
+  @retval EFI_VOLUME_CORRUPTED  Firmware volume is error\r
+  @retval EFI_ABORTED           FTW initialization error\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+InitializeFaultTolerantWrite (\r
+  IN EFI_HANDLE         ImageHandle,\r
+  IN EFI_SYSTEM_TABLE   *SystemTable\r
+  );\r
+\r
+//\r
+// Fault Tolerant Write Protocol API\r
+//\r
+\r
+/**\r
+  Query the largest block that may be updated in a fault tolerant manner.\r
+\r
+\r
+  @param This            Indicates a pointer to the calling context.\r
+  @param BlockSize       A pointer to a caller allocated UINTN that is updated to\r
+                         indicate the size of the largest block that can be updated.\r
+\r
+  @return EFI_SUCCESS   The function completed successfully\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FtwGetMaxBlockSize (\r
+  IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL    *This,\r
+  OUT UINTN                               *BlockSize\r
+  );\r
+\r
+/**\r
+  Allocates space for the protocol to maintain information about writes.\r
+  Since writes must be completed in a fault tolerant manner and multiple\r
+  updates will require more resources to be successful, this function\r
+  enables the protocol to ensure that enough space exists to track\r
+  information about the upcoming writes.\r
+\r
+  All writes must be completed or aborted before another fault tolerant write can occur.\r
+\r
+  @param This            Indicates a pointer to the calling context.\r
+  @param CallerId        The GUID identifying the write.\r
+  @param PrivateDataSize The size of the caller's private data\r
+                         that must be recorded for each write.\r
+  @param NumberOfWrites  The number of fault tolerant block writes\r
+                         that will need to occur.\r
+\r
+  @return EFI_SUCCESS        The function completed successfully\r
+  @retval EFI_ABORTED        The function could not complete successfully.\r
+  @retval EFI_ACCESS_DENIED  All allocated writes have not been completed.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FtwAllocate (\r
+  IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL    *This,\r
+  IN EFI_GUID                             *CallerId,\r
+  IN UINTN                                PrivateDataSize,\r
+  IN UINTN                                NumberOfWrites\r
+  );\r
+\r
+/**\r
+  Starts a target block update. This function will record data about write\r
+  in fault tolerant storage and will complete the write in a recoverable\r
+  manner, ensuring at all times that either the original contents or\r
+  the modified contents are available.\r
+\r
+\r
+  @param This            Calling context\r
+  @param Lba             The logical block address of the target block.\r
+  @param Offset          The offset within the target block to place the data.\r
+  @param Length          The number of bytes to write to the target block.\r
+  @param PrivateData     A pointer to private data that the caller requires to\r
+                         complete any pending writes in the event of a fault.\r
+  @param FvBlockHandle   The handle of FVB protocol that provides services for\r
+                         reading, writing, and erasing the target block.\r
+  @param Buffer          The data to write.\r
+\r
+  @retval EFI_SUCCESS          The function completed successfully \r
+  @retval EFI_ABORTED          The function could not complete successfully. \r
+  @retval EFI_BAD_BUFFER_SIZE  The input data can't fit within the spare block. \r
+                               Offset + *NumBytes > SpareAreaLength.\r
+  @retval EFI_ACCESS_DENIED    No writes have been allocated. \r
+  @retval EFI_OUT_OF_RESOURCES Cannot allocate enough memory resource.\r
+  @retval EFI_NOT_FOUND        Cannot find FVB protocol by handle.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FtwWrite (\r
+  IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL     *This,\r
+  IN EFI_LBA                               Lba,\r
+  IN UINTN                                 Offset,\r
+  IN UINTN                                 Length,\r
+  IN VOID                                  *PrivateData,\r
+  IN EFI_HANDLE                            FvBlockHandle,\r
+  IN VOID                                  *Buffer\r
+  );\r
+\r
+/**\r
+  Restarts a previously interrupted write. The caller must provide the\r
+  block protocol needed to complete the interrupted write.\r
+\r
+  @param This            Calling context.\r
+  @param FvBlockHandle   The handle of FVB protocol that provides services for\r
+                         reading, writing, and erasing the target block.\r
+\r
+  @retval  EFI_SUCCESS          The function completed successfully\r
+  @retval  EFI_ACCESS_DENIED    No pending writes exist\r
+  @retval  EFI_NOT_FOUND        FVB protocol not found by the handle\r
+  @retval  EFI_ABORTED          The function could not complete successfully\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FtwRestart (\r
+  IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL     *This,\r
+  IN EFI_HANDLE                            FvBlockHandle\r
+  );\r
+\r
+/**\r
+  Aborts all previous allocated writes.\r
+\r
+  @param  This                 Calling context\r
+\r
+  @retval EFI_SUCCESS          The function completed successfully\r
+  @retval EFI_ABORTED          The function could not complete successfully.\r
+  @retval EFI_NOT_FOUND        No allocated writes exist.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FtwAbort (\r
+  IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL     *This\r
+  );\r
+\r
+/**\r
+  Starts a target block update. This records information about the write\r
+  in fault tolerant storage and will complete the write in a recoverable\r
+  manner, ensuring at all times that either the original contents or\r
+  the modified contents are available.\r
+\r
+  @param This            Indicates a pointer to the calling context.\r
+  @param CallerId        The GUID identifying the last write.\r
+  @param Lba             The logical block address of the last write.\r
+  @param Offset          The offset within the block of the last write.\r
+  @param Length          The length of the last write.\r
+  @param PrivateDataSize bytes from the private data\r
+                         stored for this write.\r
+  @param PrivateData     A pointer to a buffer. The function will copy\r
+  @param Complete        A Boolean value with TRUE indicating\r
+                         that the write was completed.\r
+\r
+  @retval EFI_SUCCESS           The function completed successfully\r
+  @retval EFI_ABORTED           The function could not complete successfully\r
+  @retval EFI_NOT_FOUND         No allocated writes exist\r
+  @retval EFI_BUFFER_TOO_SMALL  Input buffer is not larget enough\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FtwGetLastWrite (\r
+  IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL     *This,\r
+  OUT EFI_GUID                             *CallerId,\r
+  OUT EFI_LBA                              *Lba,\r
+  OUT UINTN                                *Offset,\r
+  OUT UINTN                                *Length,\r
+  IN OUT UINTN                             *PrivateDataSize,\r
+  OUT VOID                                 *PrivateData,\r
+  OUT BOOLEAN                              *Complete\r
+  );\r
+\r
+/**\r
+  Erase spare block.\r
+\r
+  @param FtwDevice        The private data of FTW driver\r
+\r
+  @retval EFI_SUCCESS           The erase request was successfully completed.\r
+  @retval EFI_ACCESS_DENIED     The firmware volume is in the WriteDisabled state.\r
+  @retval EFI_DEVICE_ERROR      The block device is not functioning\r
+                                correctly and could not be written.\r
+                                The firmware device may have been\r
+                                partially erased.\r
+  @retval EFI_INVALID_PARAMETER One or more of the LBAs listed\r
+                                in the variable argument list do\r
+                                not exist in the firmware volume.  \r
+\r
+\r
+**/\r
+EFI_STATUS\r
+FtwEraseSpareBlock (\r
+  IN EFI_FTW_DEVICE   *FtwDevice\r
+  );\r
+\r
+/**\r
+  Retrive the proper FVB protocol interface by HANDLE.\r
+\r
+\r
+  @param FvBlockHandle   The handle of FVB protocol that provides services for\r
+                         reading, writing, and erasing the target block.\r
+  @param FvBlock         The interface of FVB protocol\r
+\r
+  @retval  EFI_SUCCESS   The function completed successfully\r
+  @retval  EFI_ABORTED   The function could not complete successfully\r
+\r
+**/\r
+EFI_STATUS\r
+FtwGetFvbByHandle (\r
+  IN EFI_HANDLE                           FvBlockHandle,\r
+  OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  **FvBlock\r
+  );\r
+\r
+/**\r
+\r
+  Is it in working block?\r
+\r
+  @param FtwDevice       The private data of FTW driver\r
+  @param FvBlock         Fvb protocol instance\r
+  @param Lba             The block specified\r
+\r
+  @return A BOOLEAN value indicating in working block or not.\r
+\r
+**/\r
+BOOLEAN\r
+IsWorkingBlock (\r
+  EFI_FTW_DEVICE                      *FtwDevice,\r
+  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *FvBlock,\r
+  EFI_LBA                             Lba\r
+  );\r
+\r
+/**\r
+\r
+  Is it in boot block?\r
+\r
+  @param FtwDevice       The private data of FTW driver\r
+  @param FvBlock         Fvb protocol instance\r
+  @param Lba             The block specified\r
+\r
+  @return A BOOLEAN value indicating in boot block or not.\r
+\r
+**/\r
+BOOLEAN\r
+IsBootBlock (\r
+  EFI_FTW_DEVICE                      *FtwDevice,\r
+  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *FvBlock,\r
+  EFI_LBA                             Lba\r
+  );\r
+\r
+/**\r
+  Copy the content of spare block to a target block. Size is FTW_BLOCK_SIZE.\r
+  Spare block is accessed by FTW backup FVB protocol interface. LBA is 1.\r
+  Target block is accessed by FvbBlock protocol interface. LBA is Lba.\r
+\r
+\r
+  @param FtwDevice       The private data of FTW driver\r
+  @param FvBlock         FVB Protocol interface to access target block\r
+  @param Lba             Lba of the target block\r
+\r
+  @retval  EFI_SUCCESS               Spare block content is copied to target block\r
+  @retval  EFI_INVALID_PARAMETER     Input parameter error\r
+  @retval  EFI_OUT_OF_RESOURCES      Allocate memory error\r
+  @retval  EFI_ABORTED               The function could not complete successfully\r
+\r
+**/\r
+EFI_STATUS\r
+FlushSpareBlockToTargetBlock (\r
+  EFI_FTW_DEVICE                      *FtwDevice,\r
+  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *FvBlock,\r
+  EFI_LBA                             Lba\r
+  );\r
+\r
+/**\r
+  Copy the content of spare block to working block. Size is FTW_BLOCK_SIZE.\r
+  Spare block is accessed by FTW backup FVB protocol interface. LBA is\r
+  FtwDevice->FtwSpareLba.\r
+  Working block is accessed by FTW working FVB protocol interface. LBA is\r
+  FtwDevice->FtwWorkBlockLba.\r
+\r
+  Since the working block header is important when FTW initializes, the\r
+  state of the operation should be handled carefully. The Crc value is\r
+  calculated without STATE element.\r
+\r
+  @param FtwDevice       The private data of FTW driver\r
+\r
+  @retval  EFI_SUCCESS               Spare block content is copied to target block\r
+  @retval  EFI_OUT_OF_RESOURCES      Allocate memory error\r
+  @retval  EFI_ABORTED               The function could not complete successfully\r
+\r
+**/\r
+EFI_STATUS\r
+FlushSpareBlockToWorkingBlock (\r
+  EFI_FTW_DEVICE                      *FtwDevice\r
+  );\r
+\r
+/**\r
+  Copy the content of spare block to a boot block. Size is FTW_BLOCK_SIZE.\r
+  Spare block is accessed by FTW working FVB protocol interface. LBA is 1.\r
+  Target block is accessed by FvbBlock protocol interface. LBA is Lba.\r
+\r
+  FTW will do extra work on boot block update.\r
+  FTW should depend on a protocol of EFI_ADDRESS_RANGE_SWAP_PROTOCOL,\r
+  which is produced by a chipset driver.\r
+  FTW updating boot block steps may be:\r
+  1. GetRangeLocation(), if the Range is inside the boot block, FTW know\r
+  that boot block will be update. It shall add a FLAG in the working block.\r
+  2. When spare block is ready,\r
+  3. SetSwapState(EFI_SWAPPED)\r
+  4. erasing boot block,\r
+  5. programming boot block until the boot block is ok.\r
+  6. SetSwapState(UNSWAPPED)\r
+  FTW shall not allow to update boot block when battery state is error.\r
+\r
+  @param FtwDevice       The private data of FTW driver\r
+\r
+  @retval EFI_SUCCESS             Spare block content is copied to boot block\r
+  @retval EFI_INVALID_PARAMETER   Input parameter error\r
+  @retval EFI_OUT_OF_RESOURCES    Allocate memory error\r
+  @retval EFI_ABORTED             The function could not complete successfully\r
+\r
+**/\r
+EFI_STATUS\r
+FlushSpareBlockToBootBlock (\r
+  EFI_FTW_DEVICE                      *FtwDevice\r
+  );\r
+\r
+/**\r
+  Update a bit of state on a block device. The location of the bit is\r
+  calculated by the (Lba, Offset, bit). Here bit is determined by the\r
+  the name of a certain bit.\r
+\r
+\r
+  @param FvBlock         FVB Protocol interface to access SrcBlock and DestBlock\r
+  @param Lba             Lba of a block\r
+  @param Offset          Offset on the Lba\r
+  @param NewBit          New value that will override the old value if it can be change\r
+\r
+  @retval  EFI_SUCCESS    A state bit has been updated successfully\r
+  @retval  Others         Access block device error.\r
+                          Notes:\r
+                          Assume all bits of State are inside the same BYTE.\r
+  @retval  EFI_ABORTED    Read block fail\r
+\r
+**/\r
+EFI_STATUS\r
+FtwUpdateFvState (\r
+  IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *FvBlock,\r
+  IN EFI_LBA                             Lba,\r
+  IN UINTN                               Offset,\r
+  IN UINT8                               NewBit\r
+  );\r
+\r
+/**\r
+  Get the last Write Header pointer.\r
+  The last write header is the header whose 'complete' state hasn't been set.\r
+  After all, this header may be a EMPTY header entry for next Allocate.\r
+\r
+\r
+  @param FtwWorkSpaceHeader Pointer of the working block header\r
+  @param FtwWorkSpaceSize   Size of the work space\r
+  @param FtwWriteHeader     Pointer to retrieve the last write header\r
+\r
+  @retval  EFI_SUCCESS      Get the last write record successfully\r
+  @retval  EFI_ABORTED      The FTW work space is damaged\r
+\r
+**/\r
+EFI_STATUS\r
+FtwGetLastWriteHeader (\r
+  IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER  *FtwWorkSpaceHeader,\r
+  IN UINTN                                    FtwWorkSpaceSize,\r
+  OUT EFI_FAULT_TOLERANT_WRITE_HEADER         **FtwWriteHeader\r
+  );\r
+\r
+/**\r
+  Get the last Write Record pointer. The last write Record is the Record\r
+  whose DestinationCompleted state hasn't been set. After all, this Record\r
+  may be a EMPTY record entry for next write.\r
+\r
+\r
+  @param FtwWriteHeader  Pointer to the write record header\r
+  @param FtwWriteRecord  Pointer to retrieve the last write record\r
+\r
+  @retval EFI_SUCCESS        Get the last write record successfully\r
+  @retval EFI_ABORTED        The FTW work space is damaged\r
+\r
+**/\r
+EFI_STATUS\r
+FtwGetLastWriteRecord (\r
+  IN EFI_FAULT_TOLERANT_WRITE_HEADER          *FtwWriteHeader,\r
+  OUT EFI_FAULT_TOLERANT_WRITE_RECORD         **FtwWriteRecord\r
+  );\r
+\r
+/**\r
+  To check if FtwRecord is the first record of FtwHeader.\r
+\r
+  @param FtwHeader  Pointer to the write record header\r
+  @param FtwRecord  Pointer to the write record\r
+\r
+  @retval TRUE      FtwRecord is the first Record of the FtwHeader\r
+  @retval FALSE     FtwRecord is not the first Record of the FtwHeader\r
+\r
+**/\r
+BOOLEAN\r
+IsFirstRecordOfWrites (\r
+  IN EFI_FAULT_TOLERANT_WRITE_HEADER    *FtwHeader,\r
+  IN EFI_FAULT_TOLERANT_WRITE_RECORD    *FtwRecord\r
+  );\r
+\r
+/**\r
+  To check if FtwRecord is the last record of FtwHeader. Because the\r
+  FtwHeader has NumberOfWrites & PrivateDataSize, the FtwRecord can be\r
+  determined if it is the last record of FtwHeader.\r
+\r
+  @param FtwHeader  Pointer to the write record header\r
+  @param FtwRecord  Pointer to the write record\r
+\r
+  @retval TRUE      FtwRecord is the last Record of the FtwHeader\r
+  @retval FALSE     FtwRecord is not the last Record of the FtwHeader\r
+\r
+**/\r
+BOOLEAN\r
+IsLastRecordOfWrites (\r
+  IN EFI_FAULT_TOLERANT_WRITE_HEADER    *FtwHeader,\r
+  IN EFI_FAULT_TOLERANT_WRITE_RECORD    *FtwRecord\r
+  );\r
+\r
+/**\r
+  To check if FtwRecord is the first record of FtwHeader.\r
+\r
+  @param FtwHeader  Pointer to the write record header\r
+  @param FtwRecord  Pointer to retrieve the previous write record\r
+\r
+  @retval EFI_ACCESS_DENIED  Input record is the first record, no previous record is return.\r
+  @retval EFI_SUCCESS        The previous write record is found.\r
+\r
+**/\r
+EFI_STATUS\r
+GetPreviousRecordOfWrites (\r
+  IN     EFI_FAULT_TOLERANT_WRITE_HEADER    *FtwHeader,\r
+  IN OUT EFI_FAULT_TOLERANT_WRITE_RECORD    **FtwRecord\r
+  );\r
+\r
+/**\r
+\r
+  Check whether a flash buffer is erased.\r
+\r
+  @param Buffer          Buffer to check\r
+  @param BufferSize      Size of the buffer\r
+\r
+  @return A BOOLEAN value indicating erased or not.\r
+\r
+**/\r
+BOOLEAN\r
+IsErasedFlashBuffer (\r
+  IN UINT8           *Buffer,\r
+  IN UINTN           BufferSize\r
+  );\r
+/**\r
+  Initialize a work space when there is no work space.\r
+\r
+  @param WorkingHeader   Pointer of working block header\r
+\r
+  @retval  EFI_SUCCESS    The function completed successfully\r
+  @retval  EFI_ABORTED    The function could not complete successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+InitWorkSpaceHeader (\r
+  IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader\r
+  );\r
+/**\r
+  Read from working block to refresh the work space in memory.\r
+\r
+  @param FtwDevice   Point to private data of FTW driver\r
+\r
+  @retval  EFI_SUCCESS    The function completed successfully\r
+  @retval  EFI_ABORTED    The function could not complete successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+WorkSpaceRefresh (\r
+  IN EFI_FTW_DEVICE  *FtwDevice\r
+  );\r
+/**\r
+  Check to see if it is a valid work space.\r
+\r
+\r
+  @param WorkingHeader   Pointer of working block header\r
+\r
+  @retval  EFI_SUCCESS    The function completed successfully\r
+  @retval  EFI_ABORTED    The function could not complete successfully.\r
+\r
+**/\r
+BOOLEAN\r
+IsValidWorkSpace (\r
+  IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader\r
+  );\r
+/**\r
+  Reclaim the work space on the working block.\r
+\r
+  @param FtwDevice       Point to private data of FTW driver\r
+  @param PreserveRecord  Whether to preserve the working record is needed\r
+\r
+  @retval EFI_SUCCESS            The function completed successfully\r
+  @retval EFI_OUT_OF_RESOURCES   Allocate memory error\r
+  @retval EFI_ABORTED            The function could not complete successfully\r
+\r
+**/\r
+EFI_STATUS\r
+FtwReclaimWorkSpace (\r
+  IN EFI_FTW_DEVICE  *FtwDevice,\r
+  IN BOOLEAN         PreserveRecord\r
+  );\r
+\r
+/**\r
+\r
+  Get firmware block by address.\r
+\r
+\r
+  @param Address         Address specified the block\r
+  @param FvBlock         The block caller wanted\r
+\r
+  @retval  EFI_SUCCESS    The protocol instance if found.\r
+  @retval  EFI_NOT_FOUND  Block not found\r
+\r
+**/\r
+EFI_HANDLE\r
+GetFvbByAddress (\r
+  IN  EFI_PHYSICAL_ADDRESS               Address,\r
+  OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock\r
+  );\r
+\r
+#endif\r
diff --git a/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf b/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf
new file mode 100644 (file)
index 0000000..cf526e7
--- /dev/null
@@ -0,0 +1,67 @@
+#/** @file\r
+# This driver installs Fault Tolerant Write (FTW) protocol, \r
+# which provides fault tolerant write capability for block devices.\r
+# Its implementation depends on the full functionality FVB protocol that support read, write/erase flash access.\r
+#\r
+# Copyright (c) 2006 - 2009, Intel Corporation\r
+#\r
+#  All rights reserved. This program and the accompanying materials\r
+#  are licensed and made available under the terms and conditions of the BSD License\r
+#  which accompanies this distribution. The full text of the license may be found at\r
+#  http://opensource.org/licenses/bsd-license.php\r
+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+#**/\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = FaultTolerantWriteDxe\r
+  FILE_GUID                      = FE5CEA76-4F72-49e8-986F-2CD899DFFE5D\r
+  MODULE_TYPE                    = DXE_DRIVER\r
+  VERSION_STRING                 = 1.0\r
+  ENTRY_POINT                    = InitializeFaultTolerantWrite\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC\r
+#\r
+\r
+[Sources.common]\r
+  FtwMisc.c\r
+  UpdateWorkingBlock.c\r
+  FaultTolerantWrite.c\r
+  FaultTolerantWrite.h\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+\r
+[LibraryClasses]\r
+  UefiBootServicesTableLib\r
+  MemoryAllocationLib\r
+  BaseMemoryLib\r
+  UefiDriverEntryPoint\r
+  DebugLib\r
+\r
+[Guids]\r
+  gEfiSystemNvDataFvGuid                        ## CONSUMES ## FV Signature of Working Space Header\r
+\r
+[Protocols]\r
+  gEfiSwapAddressRangeProtocolGuid     | PcdFullFtwServiceEnable          ## CONSUMES\r
+  gEfiFirmwareVolumeBlockProtocolGuid           ## CONSUMES\r
+  gEfiFaultTolerantWriteProtocolGuid            ## PRODUCES\r
+\r
+[FeaturePcd]\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFullFtwServiceEnable\r
+\r
+[Pcd]\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize\r
+\r
+[Depex]\r
+  gEfiFirmwareVolumeBlockProtocolGuid AND gEfiAlternateFvBlockGuid  ## gEfiAlternateFvBlockGuid specifies FVB protocol with read, write/erase flash access.\r
+\r
diff --git a/MdeModulePkg/Universal/FaultTolerantWriteDxe/FtwMisc.c b/MdeModulePkg/Universal/FaultTolerantWriteDxe/FtwMisc.c
new file mode 100644 (file)
index 0000000..de768e1
--- /dev/null
@@ -0,0 +1,971 @@
+/** @file\r
+\r
+  Internal generic functions to operate flash block.\r
+\r
+Copyright (c) 2006 - 2008, Intel Corporation                                                         \r
+All rights reserved. This program and the accompanying materials                          \r
+are licensed and made available under the terms and conditions of the BSD License         \r
+which accompanies this distribution.  The full text of the license may be found at        \r
+http://opensource.org/licenses/bsd-license.php                                            \r
+                                                                                          \r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     \r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
+\r
+**/\r
+\r
+#include "FaultTolerantWrite.h"\r
+\r
+/**\r
+\r
+  Check whether a flash buffer is erased.\r
+\r
+  @param Buffer          Buffer to check\r
+  @param BufferSize      Size of the buffer\r
+\r
+  @return A BOOLEAN value indicating erased or not.\r
+\r
+**/\r
+BOOLEAN\r
+IsErasedFlashBuffer (\r
+  IN UINT8           *Buffer,\r
+  IN UINTN           BufferSize\r
+  )\r
+{\r
+  BOOLEAN IsEmpty;\r
+  UINT8   *Ptr;\r
+  UINTN   Index;\r
+\r
+  Ptr     = Buffer;\r
+  IsEmpty = TRUE;\r
+  for (Index = 0; Index < BufferSize; Index += 1) {\r
+    if (*Ptr++ != FTW_ERASED_BYTE) {\r
+      IsEmpty = FALSE;\r
+      break;\r
+    }\r
+  }\r
+\r
+  return IsEmpty;\r
+}\r
+\r
+/**\r
+  To erase the block with the spare block size.\r
+\r
+\r
+  @param FtwDevice       The private data of FTW driver\r
+  @param FvBlock         FVB Protocol interface\r
+  @param Lba             Lba of the firmware block\r
+\r
+  @retval  EFI_SUCCESS    Block LBA is Erased successfully\r
+  @retval  Others         Error occurs\r
+\r
+**/\r
+EFI_STATUS\r
+FtwEraseBlock (\r
+  IN EFI_FTW_DEVICE                   *FtwDevice,\r
+  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *FvBlock,\r
+  EFI_LBA                             Lba\r
+  )\r
+{\r
+  return FvBlock->EraseBlocks (\r
+                    FvBlock,\r
+                    Lba,\r
+                    FtwDevice->NumberOfSpareBlock,\r
+                    EFI_LBA_LIST_TERMINATOR\r
+                    );\r
+}\r
+\r
+/**\r
+  Erase spare block.\r
+\r
+  @param FtwDevice        The private data of FTW driver\r
+\r
+  @retval EFI_SUCCESS           The erase request was successfully completed.\r
+  @retval EFI_ACCESS_DENIED     The firmware volume is in the WriteDisabled state.\r
+  @retval EFI_DEVICE_ERROR      The block device is not functioning\r
+                                correctly and could not be written.\r
+                                The firmware device may have been\r
+                                partially erased.\r
+  @retval EFI_INVALID_PARAMETER One or more of the LBAs listed\r
+                                in the variable argument list do\r
+                                not exist in the firmware volume.  \r
+\r
+\r
+**/\r
+EFI_STATUS\r
+FtwEraseSpareBlock (\r
+  IN EFI_FTW_DEVICE   *FtwDevice\r
+  )\r
+{\r
+  return FtwDevice->FtwBackupFvb->EraseBlocks (\r
+                                    FtwDevice->FtwBackupFvb,\r
+                                    FtwDevice->FtwSpareLba,\r
+                                    FtwDevice->NumberOfSpareBlock,\r
+                                    EFI_LBA_LIST_TERMINATOR\r
+                                    );\r
+}\r
+\r
+/**\r
+  Retrive the proper FVB protocol interface by HANDLE.\r
+\r
+\r
+  @param FvBlockHandle   The handle of FVB protocol that provides services for\r
+                         reading, writing, and erasing the target block.\r
+  @param FvBlock         The interface of FVB protocol\r
+\r
+  @retval  EFI_SUCCESS   The function completed successfully\r
+  @retval  EFI_ABORTED   The function could not complete successfully\r
+\r
+**/\r
+EFI_STATUS\r
+FtwGetFvbByHandle (\r
+  IN EFI_HANDLE                           FvBlockHandle,\r
+  OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  **FvBlock\r
+  )\r
+{\r
+  //\r
+  // To get the FVB protocol interface on the handle\r
+  //\r
+  return gBS->HandleProtocol (\r
+                FvBlockHandle,\r
+                &gEfiFirmwareVolumeBlockProtocolGuid,\r
+                (VOID **) FvBlock\r
+                );\r
+}\r
+\r
+/**\r
+\r
+  Is it in working block?\r
+\r
+  @param FtwDevice       The private data of FTW driver\r
+  @param FvBlock         Fvb protocol instance\r
+  @param Lba             The block specified\r
+\r
+  @return A BOOLEAN value indicating in working block or not.\r
+\r
+**/\r
+BOOLEAN\r
+IsWorkingBlock (\r
+  EFI_FTW_DEVICE                      *FtwDevice,\r
+  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *FvBlock,\r
+  EFI_LBA                             Lba\r
+  )\r
+{\r
+  //\r
+  // If matching the following condition, the target block is in working block.\r
+  // 1. Target block is on the FV of working block (Using the same FVB protocol instance).\r
+  // 2. Lba falls into the range of working block.\r
+  //\r
+  return (BOOLEAN)\r
+    (\r
+      (FvBlock == FtwDevice->FtwFvBlock) &&\r
+      (Lba >= FtwDevice->FtwWorkBlockLba) &&\r
+      (Lba <= FtwDevice->FtwWorkSpaceLba)\r
+    );\r
+}\r
+\r
+/**\r
+\r
+  Get firmware block by address.\r
+\r
+\r
+  @param Address         Address specified the block\r
+  @param FvBlock         The block caller wanted\r
+\r
+  @retval  EFI_SUCCESS    The protocol instance if found.\r
+  @retval  EFI_NOT_FOUND  Block not found\r
+\r
+**/\r
+EFI_HANDLE\r
+GetFvbByAddress (\r
+  IN  EFI_PHYSICAL_ADDRESS               Address,\r
+  OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock\r
+  )\r
+{\r
+  EFI_STATUS                          Status;\r
+  EFI_HANDLE                          *HandleBuffer;\r
+  UINTN                               HandleCount;\r
+  UINTN                               Index;\r
+  EFI_PHYSICAL_ADDRESS                FvbBaseAddress;\r
+  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *Fvb;\r
+  EFI_FIRMWARE_VOLUME_HEADER          *FwVolHeader;\r
+  EFI_HANDLE                          FvbHandle;\r
+\r
+  *FvBlock  = NULL;\r
+  FvbHandle = NULL;\r
+  //\r
+  // Locate all handles of Fvb protocol\r
+  //\r
+  Status = gBS->LocateHandleBuffer (\r
+                  ByProtocol,\r
+                  &gEfiFirmwareVolumeBlockProtocolGuid,\r
+                  NULL,\r
+                  &HandleCount,\r
+                  &HandleBuffer\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return NULL;\r
+  }\r
+  //\r
+  // Get the FVB to access variable store\r
+  //\r
+  for (Index = 0; Index < HandleCount; Index += 1) {\r
+    Status = gBS->HandleProtocol (\r
+                    HandleBuffer[Index],\r
+                    &gEfiFirmwareVolumeBlockProtocolGuid,\r
+                    (VOID **) &Fvb\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      break;\r
+    }\r
+    //\r
+    // Compare the address and select the right one\r
+    //\r
+    Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress);\r
+    if (EFI_ERROR (Status)) {\r
+      continue;\r
+    }\r
+\r
+    FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) FvbBaseAddress);\r
+    if ((Address >= FvbBaseAddress) && (Address <= (FvbBaseAddress + (FwVolHeader->FvLength - 1)))) {\r
+      *FvBlock  = Fvb;\r
+      FvbHandle  = HandleBuffer[Index];\r
+      break;\r
+    }\r
+  }\r
+\r
+  FreePool (HandleBuffer);\r
+  return FvbHandle;\r
+}\r
+\r
+/**\r
+\r
+  Is it in boot block?\r
+\r
+  @param FtwDevice       The private data of FTW driver\r
+  @param FvBlock         Fvb protocol instance\r
+  @param Lba             The block specified\r
+\r
+  @return A BOOLEAN value indicating in boot block or not.\r
+\r
+**/\r
+BOOLEAN\r
+IsBootBlock (\r
+  EFI_FTW_DEVICE                      *FtwDevice,\r
+  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *FvBlock,\r
+  EFI_LBA                             Lba\r
+  )\r
+{\r
+  EFI_STATUS                          Status;\r
+  EFI_SWAP_ADDRESS_RANGE_PROTOCOL     *SarProtocol;\r
+  EFI_PHYSICAL_ADDRESS                BootBlockBase;\r
+  UINTN                               BootBlockSize;\r
+  EFI_PHYSICAL_ADDRESS                BackupBlockBase;\r
+  UINTN                               BackupBlockSize;\r
+  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *BootFvb;\r
+  BOOLEAN                             IsSwapped;\r
+  EFI_HANDLE                          FvbHandle;\r
+\r
+  if (!FeaturePcdGet(PcdFullFtwServiceEnable)) {\r
+    return FALSE;\r
+  }\r
+\r
+  Status = gBS->LocateProtocol (&gEfiSwapAddressRangeProtocolGuid, NULL, (VOID **) &SarProtocol);\r
+  if (EFI_ERROR (Status)) {\r
+    return FALSE;\r
+  }\r
+  //\r
+  // Get the boot block range\r
+  //\r
+  Status = SarProtocol->GetRangeLocation (\r
+                          SarProtocol,\r
+                          &BootBlockBase,\r
+                          &BootBlockSize,\r
+                          &BackupBlockBase,\r
+                          &BackupBlockSize\r
+                          );\r
+  if (EFI_ERROR (Status)) {\r
+    return FALSE;\r
+  }\r
+\r
+  Status = SarProtocol->GetSwapState (SarProtocol, &IsSwapped);\r
+  if (EFI_ERROR (Status)) {\r
+    return FALSE;\r
+  }\r
+  //\r
+  // Get FVB by address\r
+  //\r
+  if (!IsSwapped) {\r
+    FvbHandle = GetFvbByAddress (BootBlockBase, &BootFvb);\r
+  } else {\r
+    FvbHandle = GetFvbByAddress (BackupBlockBase, &BootFvb);\r
+  }\r
+\r
+  if (FvbHandle == NULL) {\r
+    return FALSE;\r
+  }\r
+  //\r
+  // Compare the Fvb\r
+  //\r
+  return (BOOLEAN) (FvBlock == BootFvb);\r
+}\r
+\r
+/**\r
+  Copy the content of spare block to a boot block. Size is FTW_BLOCK_SIZE.\r
+  Spare block is accessed by FTW working FVB protocol interface. LBA is 1.\r
+  Target block is accessed by FvbBlock protocol interface. LBA is Lba.\r
+\r
+  FTW will do extra work on boot block update.\r
+  FTW should depend on a protocol of EFI_ADDRESS_RANGE_SWAP_PROTOCOL,\r
+  which is produced by a chipset driver.\r
+  FTW updating boot block steps may be:\r
+  1. GetRangeLocation(), if the Range is inside the boot block, FTW know\r
+  that boot block will be update. It shall add a FLAG in the working block.\r
+  2. When spare block is ready,\r
+  3. SetSwapState(EFI_SWAPPED)\r
+  4. erasing boot block,\r
+  5. programming boot block until the boot block is ok.\r
+  6. SetSwapState(UNSWAPPED)\r
+  FTW shall not allow to update boot block when battery state is error.\r
+\r
+  @param FtwDevice       The private data of FTW driver\r
+\r
+  @retval EFI_SUCCESS             Spare block content is copied to boot block\r
+  @retval EFI_INVALID_PARAMETER   Input parameter error\r
+  @retval EFI_OUT_OF_RESOURCES    Allocate memory error\r
+  @retval EFI_ABORTED             The function could not complete successfully\r
+\r
+**/\r
+EFI_STATUS\r
+FlushSpareBlockToBootBlock (\r
+  EFI_FTW_DEVICE                      *FtwDevice\r
+  )\r
+{\r
+  EFI_STATUS                          Status;\r
+  UINTN                               Length;\r
+  UINT8                               *Buffer;\r
+  UINTN                               Count;\r
+  UINT8                               *Ptr;\r
+  UINTN                               Index;\r
+  BOOLEAN                             TopSwap;\r
+  EFI_SWAP_ADDRESS_RANGE_PROTOCOL     *SarProtocol;\r
+  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *BootFvb;\r
+  EFI_LBA                             BootLba;\r
+\r
+  if (!FeaturePcdGet(PcdFullFtwServiceEnable)) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  //\r
+  // Locate swap address range protocol\r
+  //\r
+  Status = gBS->LocateProtocol (&gEfiSwapAddressRangeProtocolGuid, NULL, (VOID **) &SarProtocol);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+  //\r
+  // Allocate a memory buffer\r
+  //\r
+  Length = FtwDevice->SpareAreaLength;\r
+  Buffer  = AllocatePool (Length);\r
+  if (Buffer == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  //\r
+  // Get TopSwap bit state\r
+  //\r
+  Status = SarProtocol->GetSwapState (SarProtocol, &TopSwap);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_ERROR, "Ftw: Get Top Swapped status - %r\n", Status));\r
+    FreePool (Buffer);\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  if (TopSwap) {\r
+    //\r
+    // Get FVB of current boot block\r
+    //\r
+    if (GetFvbByAddress (FtwDevice->SpareAreaAddress + FtwDevice->SpareAreaLength, &BootFvb) == NULL) {\r
+      FreePool (Buffer);\r
+      return EFI_ABORTED;\r
+    }\r
+    //\r
+    // Read data from current boot block\r
+    //\r
+    BootLba = 0;\r
+    Ptr     = Buffer;\r
+    for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
+      Count = FtwDevice->BlockSize;\r
+      Status = BootFvb->Read (\r
+                          BootFvb,\r
+                          BootLba + Index,\r
+                          0,\r
+                          &Count,\r
+                          Ptr\r
+                          );\r
+      if (EFI_ERROR (Status)) {\r
+        FreePool (Buffer);\r
+        return Status;\r
+      }\r
+\r
+      Ptr += Count;\r
+    }\r
+  } else {\r
+    //\r
+    // Read data from spare block\r
+    //\r
+    Ptr = Buffer;\r
+    for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
+      Count = FtwDevice->BlockSize;\r
+      Status = FtwDevice->FtwBackupFvb->Read (\r
+                                          FtwDevice->FtwBackupFvb,\r
+                                          FtwDevice->FtwSpareLba + Index,\r
+                                          0,\r
+                                          &Count,\r
+                                          Ptr\r
+                                          );\r
+      if (EFI_ERROR (Status)) {\r
+        FreePool (Buffer);\r
+        return Status;\r
+      }\r
+\r
+      Ptr += Count;\r
+    }\r
+    //\r
+    // Set TopSwap bit\r
+    //\r
+    Status = SarProtocol->SetSwapState (SarProtocol, TRUE);\r
+    if (EFI_ERROR (Status)) {\r
+      FreePool (Buffer);\r
+      return Status;\r
+    }\r
+  }\r
+  //\r
+  // Erase current spare block\r
+  // Because TopSwap is set, this actually erase the top block (boot block)!\r
+  //\r
+  Status = FtwEraseSpareBlock (FtwDevice);\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Buffer);\r
+    return EFI_ABORTED;\r
+  }\r
+  //\r
+  // Write memory buffer currenet spare block. Still top block.\r
+  //\r
+  Ptr = Buffer;\r
+  for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
+    Count = FtwDevice->BlockSize;\r
+    Status = FtwDevice->FtwBackupFvb->Write (\r
+                                        FtwDevice->FtwBackupFvb,\r
+                                        FtwDevice->FtwSpareLba + Index,\r
+                                        0,\r
+                                        &Count,\r
+                                        Ptr\r
+                                        );\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((EFI_D_ERROR, "Ftw: FVB Write boot block - %r\n", Status));\r
+      FreePool (Buffer);\r
+      return Status;\r
+    }\r
+\r
+    Ptr += Count;\r
+  }\r
+\r
+  FreePool (Buffer);\r
+\r
+  //\r
+  // Clear TopSwap bit\r
+  //\r
+  Status = SarProtocol->SetSwapState (SarProtocol, FALSE);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Copy the content of spare block to a target block. Size is FTW_BLOCK_SIZE.\r
+  Spare block is accessed by FTW backup FVB protocol interface. LBA is 1.\r
+  Target block is accessed by FvbBlock protocol interface. LBA is Lba.\r
+\r
+\r
+  @param FtwDevice       The private data of FTW driver\r
+  @param FvBlock         FVB Protocol interface to access target block\r
+  @param Lba             Lba of the target block\r
+\r
+  @retval  EFI_SUCCESS               Spare block content is copied to target block\r
+  @retval  EFI_INVALID_PARAMETER     Input parameter error\r
+  @retval  EFI_OUT_OF_RESOURCES      Allocate memory error\r
+  @retval  EFI_ABORTED               The function could not complete successfully\r
+\r
+**/\r
+EFI_STATUS\r
+FlushSpareBlockToTargetBlock (\r
+  EFI_FTW_DEVICE                      *FtwDevice,\r
+  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *FvBlock,\r
+  EFI_LBA                             Lba\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  UINTN       Length;\r
+  UINT8       *Buffer;\r
+  UINTN       Count;\r
+  UINT8       *Ptr;\r
+  UINTN       Index;\r
+\r
+  if ((FtwDevice == NULL) || (FvBlock == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Allocate a memory buffer\r
+  //\r
+  Length = FtwDevice->SpareAreaLength;\r
+  Buffer  = AllocatePool (Length);\r
+  if (Buffer == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  //\r
+  // Read all content of spare block to memory buffer\r
+  //\r
+  Ptr = Buffer;\r
+  for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
+    Count = FtwDevice->BlockSize;\r
+    Status = FtwDevice->FtwBackupFvb->Read (\r
+                                        FtwDevice->FtwBackupFvb,\r
+                                        FtwDevice->FtwSpareLba + Index,\r
+                                        0,\r
+                                        &Count,\r
+                                        Ptr\r
+                                        );\r
+    if (EFI_ERROR (Status)) {\r
+      FreePool (Buffer);\r
+      return Status;\r
+    }\r
+\r
+    Ptr += Count;\r
+  }\r
+  //\r
+  // Erase the target block\r
+  //\r
+  Status = FtwEraseBlock (FtwDevice, FvBlock, Lba);\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Buffer);\r
+    return EFI_ABORTED;\r
+  }\r
+  //\r
+  // Write memory buffer to block, using the FvbBlock protocol interface\r
+  //\r
+  Ptr = Buffer;\r
+  for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
+    Count   = FtwDevice->BlockSize;\r
+    Status  = FvBlock->Write (FvBlock, Lba + Index, 0, &Count, Ptr);\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((EFI_D_ERROR, "Ftw: FVB Write block - %r\n", Status));\r
+      FreePool (Buffer);\r
+      return Status;\r
+    }\r
+\r
+    Ptr += Count;\r
+  }\r
+\r
+  FreePool (Buffer);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Copy the content of spare block to working block. Size is FTW_BLOCK_SIZE.\r
+  Spare block is accessed by FTW backup FVB protocol interface. LBA is\r
+  FtwDevice->FtwSpareLba.\r
+  Working block is accessed by FTW working FVB protocol interface. LBA is\r
+  FtwDevice->FtwWorkBlockLba.\r
+\r
+  Since the working block header is important when FTW initializes, the\r
+  state of the operation should be handled carefully. The Crc value is\r
+  calculated without STATE element.\r
+\r
+  @param FtwDevice       The private data of FTW driver\r
+\r
+  @retval  EFI_SUCCESS               Spare block content is copied to target block\r
+  @retval  EFI_OUT_OF_RESOURCES      Allocate memory error\r
+  @retval  EFI_ABORTED               The function could not complete successfully\r
+\r
+**/\r
+EFI_STATUS\r
+FlushSpareBlockToWorkingBlock (\r
+  EFI_FTW_DEVICE                      *FtwDevice\r
+  )\r
+{\r
+  EFI_STATUS                              Status;\r
+  UINTN                                   Length;\r
+  UINT8                                   *Buffer;\r
+  EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader;\r
+  UINTN                                   Count;\r
+  UINT8                                   *Ptr;\r
+  UINTN                                   Index;\r
+  EFI_LBA                                 WorkSpaceLbaOffset;\r
+\r
+  //\r
+  // Allocate a memory buffer\r
+  //\r
+  Length = FtwDevice->SpareAreaLength;\r
+  Buffer  = AllocatePool (Length);\r
+  if (Buffer == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  //\r
+  // To guarantee that the WorkingBlockValid is set on spare block\r
+  //\r
+  //  Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,\r
+  //                            WorkingBlockValid);\r
+  // To skip Signature and Crc: sizeof(EFI_GUID)+sizeof(UINT32).\r
+  //\r
+  FtwUpdateFvState (\r
+    FtwDevice->FtwBackupFvb,\r
+    FtwDevice->FtwWorkSpaceLba,\r
+    FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),\r
+    WORKING_BLOCK_VALID\r
+    );\r
+  //\r
+  // Read from spare block to memory buffer\r
+  //\r
+  Ptr = Buffer;\r
+  for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
+    Count = FtwDevice->BlockSize;\r
+    Status = FtwDevice->FtwBackupFvb->Read (\r
+                                        FtwDevice->FtwBackupFvb,\r
+                                        FtwDevice->FtwSpareLba + Index,\r
+                                        0,\r
+                                        &Count,\r
+                                        Ptr\r
+                                        );\r
+    if (EFI_ERROR (Status)) {\r
+      FreePool (Buffer);\r
+      return Status;\r
+    }\r
+\r
+    Ptr += Count;\r
+  }\r
+  //\r
+  // Clear the CRC and STATE, copy data from spare to working block.\r
+  //\r
+  WorkSpaceLbaOffset = FtwDevice->FtwWorkSpaceLba - FtwDevice->FtwWorkBlockLba;\r
+  WorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (Buffer + (UINTN) WorkSpaceLbaOffset * FtwDevice->BlockSize + FtwDevice->FtwWorkSpaceBase);\r
+  InitWorkSpaceHeader (WorkingBlockHeader);\r
+  WorkingBlockHeader->WorkingBlockValid   = FTW_ERASE_POLARITY;\r
+  WorkingBlockHeader->WorkingBlockInvalid = FTW_ERASE_POLARITY;\r
+\r
+  //\r
+  // target block is working block, then\r
+  //   Set WorkingBlockInvalid in EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER\r
+  //   before erase the working block.\r
+  //\r
+  //  Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,\r
+  //                            WorkingBlockInvalid);\r
+  // So hardcode offset as sizeof(EFI_GUID)+sizeof(UINT32) to\r
+  // skip Signature and Crc.\r
+  //\r
+  Status = FtwUpdateFvState (\r
+            FtwDevice->FtwFvBlock,\r
+            FtwDevice->FtwWorkSpaceLba,\r
+            FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),\r
+            WORKING_BLOCK_INVALID\r
+            );\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Buffer);\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  FtwDevice->FtwWorkSpaceHeader->WorkingBlockInvalid = FTW_VALID_STATE;\r
+\r
+  //\r
+  // Erase the working block\r
+  //\r
+  Status = FtwEraseBlock (FtwDevice, FtwDevice->FtwFvBlock, FtwDevice->FtwWorkBlockLba);\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Buffer);\r
+    return EFI_ABORTED;\r
+  }\r
+  //\r
+  // Write memory buffer to working block, using the FvbBlock protocol interface\r
+  //\r
+  Ptr = Buffer;\r
+  for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
+    Count = FtwDevice->BlockSize;\r
+    Status = FtwDevice->FtwFvBlock->Write (\r
+                                      FtwDevice->FtwFvBlock,\r
+                                      FtwDevice->FtwWorkBlockLba + Index,\r
+                                      0,\r
+                                      &Count,\r
+                                      Ptr\r
+                                      );\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((EFI_D_ERROR, "Ftw: FVB Write block - %r\n", Status));\r
+      FreePool (Buffer);\r
+      return Status;\r
+    }\r
+\r
+    Ptr += Count;\r
+  }\r
+  //\r
+  // Since the memory buffer will not be used, free memory Buffer.\r
+  //\r
+  FreePool (Buffer);\r
+\r
+  //\r
+  // Update the VALID of the working block\r
+  //\r
+  // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER, WorkingBlockValid);\r
+  // So hardcode offset as sizeof(EFI_GUID)+sizeof(UINT32) to skip Signature and Crc.\r
+  //\r
+  Status = FtwUpdateFvState (\r
+            FtwDevice->FtwFvBlock,\r
+            FtwDevice->FtwWorkSpaceLba,\r
+            FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),\r
+            WORKING_BLOCK_VALID\r
+            );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  FtwDevice->FtwWorkSpaceHeader->WorkingBlockValid = FTW_VALID_STATE;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Update a bit of state on a block device. The location of the bit is\r
+  calculated by the (Lba, Offset, bit). Here bit is determined by the\r
+  the name of a certain bit.\r
+\r
+\r
+  @param FvBlock         FVB Protocol interface to access SrcBlock and DestBlock\r
+  @param Lba             Lba of a block\r
+  @param Offset          Offset on the Lba\r
+  @param NewBit          New value that will override the old value if it can be change\r
+\r
+  @retval  EFI_SUCCESS    A state bit has been updated successfully\r
+  @retval  Others         Access block device error.\r
+                          Notes:\r
+                          Assume all bits of State are inside the same BYTE.\r
+  @retval  EFI_ABORTED    Read block fail\r
+\r
+**/\r
+EFI_STATUS\r
+FtwUpdateFvState (\r
+  IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *FvBlock,\r
+  IN EFI_LBA                             Lba,\r
+  IN UINTN                               Offset,\r
+  IN UINT8                               NewBit\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  UINT8       State;\r
+  UINTN       Length;\r
+\r
+  //\r
+  // Read state from device, assume State is only one byte.\r
+  //\r
+  Length  = sizeof (UINT8);\r
+  Status  = FvBlock->Read (FvBlock, Lba, Offset, &Length, &State);\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  State ^= FTW_POLARITY_REVERT;\r
+  State  = (UINT8) (State | NewBit);\r
+  State ^= FTW_POLARITY_REVERT;\r
+\r
+  //\r
+  // Write state back to device\r
+  //\r
+  Length  = sizeof (UINT8);\r
+  Status  = FvBlock->Write (FvBlock, Lba, Offset, &Length, &State);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Get the last Write Header pointer.\r
+  The last write header is the header whose 'complete' state hasn't been set.\r
+  After all, this header may be a EMPTY header entry for next Allocate.\r
+\r
+\r
+  @param FtwWorkSpaceHeader Pointer of the working block header\r
+  @param FtwWorkSpaceSize   Size of the work space\r
+  @param FtwWriteHeader     Pointer to retrieve the last write header\r
+\r
+  @retval  EFI_SUCCESS      Get the last write record successfully\r
+  @retval  EFI_ABORTED      The FTW work space is damaged\r
+\r
+**/\r
+EFI_STATUS\r
+FtwGetLastWriteHeader (\r
+  IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER  *FtwWorkSpaceHeader,\r
+  IN UINTN                                    FtwWorkSpaceSize,\r
+  OUT EFI_FAULT_TOLERANT_WRITE_HEADER         **FtwWriteHeader\r
+  )\r
+{\r
+  UINTN                           Offset;\r
+  EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader;\r
+\r
+  *FtwWriteHeader = NULL;\r
+  FtwHeader       = (EFI_FAULT_TOLERANT_WRITE_HEADER *) (FtwWorkSpaceHeader + 1);\r
+  Offset          = sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER);\r
+\r
+  while (FtwHeader->Complete == FTW_VALID_STATE) {\r
+    Offset += WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites, FtwHeader->PrivateDataSize);\r
+    //\r
+    // If Offset exceed the FTW work space boudary, return error.\r
+    //\r
+    if (Offset > FtwWorkSpaceSize) {\r
+      *FtwWriteHeader = FtwHeader;\r
+      return EFI_ABORTED;\r
+    }\r
+\r
+    FtwHeader = (EFI_FAULT_TOLERANT_WRITE_HEADER *) ((UINT8 *) FtwWorkSpaceHeader + Offset);\r
+  }\r
+  //\r
+  // Last write header is found\r
+  //\r
+  *FtwWriteHeader = FtwHeader;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Get the last Write Record pointer. The last write Record is the Record\r
+  whose DestinationCompleted state hasn't been set. After all, this Record\r
+  may be a EMPTY record entry for next write.\r
+\r
+\r
+  @param FtwWriteHeader  Pointer to the write record header\r
+  @param FtwWriteRecord  Pointer to retrieve the last write record\r
+\r
+  @retval EFI_SUCCESS        Get the last write record successfully\r
+  @retval EFI_ABORTED        The FTW work space is damaged\r
+\r
+**/\r
+EFI_STATUS\r
+FtwGetLastWriteRecord (\r
+  IN EFI_FAULT_TOLERANT_WRITE_HEADER          *FtwWriteHeader,\r
+  OUT EFI_FAULT_TOLERANT_WRITE_RECORD         **FtwWriteRecord\r
+  )\r
+{\r
+  UINTN                           Index;\r
+  EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord;\r
+\r
+  *FtwWriteRecord = NULL;\r
+  FtwRecord       = (EFI_FAULT_TOLERANT_WRITE_RECORD *) (FtwWriteHeader + 1);\r
+\r
+  //\r
+  // Try to find the last write record "that has not completed"\r
+  //\r
+  for (Index = 0; Index < FtwWriteHeader->NumberOfWrites; Index += 1) {\r
+    if (FtwRecord->DestinationComplete != FTW_VALID_STATE) {\r
+      //\r
+      // The last write record is found\r
+      //\r
+      *FtwWriteRecord = FtwRecord;\r
+      return EFI_SUCCESS;\r
+    }\r
+\r
+    FtwRecord++;\r
+\r
+    if (FtwWriteHeader->PrivateDataSize != 0) {\r
+      FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) ((UINTN) FtwRecord + FtwWriteHeader->PrivateDataSize);\r
+    }\r
+  }\r
+  //\r
+  //  if Index == NumberOfWrites, then\r
+  //  the last record has been written successfully,\r
+  //  but the Header->Complete Flag has not been set.\r
+  //  also return the last record.\r
+  //\r
+  if (Index == FtwWriteHeader->NumberOfWrites) {\r
+    *FtwWriteRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) ((UINTN) FtwRecord - RECORD_SIZE (FtwWriteHeader->PrivateDataSize));\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  return EFI_ABORTED;\r
+}\r
+\r
+/**\r
+  To check if FtwRecord is the first record of FtwHeader.\r
+\r
+  @param FtwHeader  Pointer to the write record header\r
+  @param FtwRecord  Pointer to the write record\r
+\r
+  @retval TRUE      FtwRecord is the first Record of the FtwHeader\r
+  @retval FALSE     FtwRecord is not the first Record of the FtwHeader\r
+\r
+**/\r
+BOOLEAN\r
+IsFirstRecordOfWrites (\r
+  IN EFI_FAULT_TOLERANT_WRITE_HEADER    *FtwHeader,\r
+  IN EFI_FAULT_TOLERANT_WRITE_RECORD    *FtwRecord\r
+  )\r
+{\r
+  UINT8 *Head;\r
+  UINT8 *Ptr;\r
+\r
+  Head  = (UINT8 *) FtwHeader;\r
+  Ptr   = (UINT8 *) FtwRecord;\r
+\r
+  Head += sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER);\r
+  return (BOOLEAN) (Head == Ptr);\r
+}\r
+\r
+/**\r
+  To check if FtwRecord is the last record of FtwHeader. Because the\r
+  FtwHeader has NumberOfWrites & PrivateDataSize, the FtwRecord can be\r
+  determined if it is the last record of FtwHeader.\r
+\r
+  @param FtwHeader  Pointer to the write record header\r
+  @param FtwRecord  Pointer to the write record\r
+\r
+  @retval TRUE      FtwRecord is the last Record of the FtwHeader\r
+  @retval FALSE     FtwRecord is not the last Record of the FtwHeader\r
+\r
+**/\r
+BOOLEAN\r
+IsLastRecordOfWrites (\r
+  IN EFI_FAULT_TOLERANT_WRITE_HEADER    *FtwHeader,\r
+  IN EFI_FAULT_TOLERANT_WRITE_RECORD    *FtwRecord\r
+  )\r
+{\r
+  UINT8 *Head;\r
+  UINT8 *Ptr;\r
+\r
+  Head  = (UINT8 *) FtwHeader;\r
+  Ptr   = (UINT8 *) FtwRecord;\r
+\r
+  Head += WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites - 1, FtwHeader->PrivateDataSize);\r
+  return (BOOLEAN) (Head == Ptr);\r
+}\r
+\r
+/**\r
+  To check if FtwRecord is the first record of FtwHeader.\r
+\r
+  @param FtwHeader  Pointer to the write record header\r
+  @param FtwRecord  Pointer to retrieve the previous write record\r
+\r
+  @retval EFI_ACCESS_DENIED  Input record is the first record, no previous record is return.\r
+  @retval EFI_SUCCESS        The previous write record is found.\r
+\r
+**/\r
+EFI_STATUS\r
+GetPreviousRecordOfWrites (\r
+  IN     EFI_FAULT_TOLERANT_WRITE_HEADER    *FtwHeader,\r
+  IN OUT EFI_FAULT_TOLERANT_WRITE_RECORD    **FtwRecord\r
+  )\r
+{\r
+  UINT8 *Ptr;\r
+\r
+  if (IsFirstRecordOfWrites (FtwHeader, *FtwRecord)) {\r
+    *FtwRecord = NULL;\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  Ptr = (UINT8 *) (*FtwRecord);\r
+  Ptr -= RECORD_SIZE (FtwHeader->PrivateDataSize);\r
+  *FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) Ptr;\r
+  return EFI_SUCCESS;\r
+}\r
diff --git a/MdeModulePkg/Universal/FaultTolerantWriteDxe/UpdateWorkingBlock.c b/MdeModulePkg/Universal/FaultTolerantWriteDxe/UpdateWorkingBlock.c
new file mode 100644 (file)
index 0000000..83bb3b8
--- /dev/null
@@ -0,0 +1,491 @@
+/** @file\r
+\r
+   Internal functions to operate Working Block Space.\r
+\r
+Copyright (c) 2006 - 2008, Intel Corporation                                                         \r
+All rights reserved. This program and the accompanying materials                          \r
+are licensed and made available under the terms and conditions of the BSD License         \r
+which accompanies this distribution.  The full text of the license may be found at        \r
+http://opensource.org/licenses/bsd-license.php                                            \r
+                                                                                          \r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     \r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
+\r
+**/\r
+\r
+\r
+#include "FaultTolerantWrite.h"\r
+\r
+/**\r
+  Check to see if it is a valid work space.\r
+\r
+\r
+  @param WorkingHeader   Pointer of working block header\r
+\r
+  @retval  EFI_SUCCESS    The function completed successfully\r
+  @retval  EFI_ABORTED    The function could not complete successfully.\r
+\r
+**/\r
+BOOLEAN\r
+IsValidWorkSpace (\r
+  IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader\r
+  )\r
+{\r
+  EFI_STATUS                              Status;\r
+  EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER WorkingBlockHeader;\r
+\r
+  if (WorkingHeader == NULL) {\r
+    return FALSE;\r
+  }\r
+\r
+  if (WorkingHeader->WorkingBlockValid != FTW_VALID_STATE) {\r
+    DEBUG ((EFI_D_ERROR, "Ftw: Work block header valid bit check error\n"));\r
+    return FALSE;\r
+  }\r
+  //\r
+  // Check signature with gEfiSystemNvDataFvGuid\r
+  //\r
+  if (!CompareGuid (&gEfiSystemNvDataFvGuid, &WorkingHeader->Signature)) {\r
+    DEBUG ((EFI_D_ERROR, "Ftw: Work block header signature check error\n"));\r
+    return FALSE;\r
+  }\r
+  //\r
+  // Check the CRC of header\r
+  //\r
+  CopyMem (\r
+    &WorkingBlockHeader,\r
+    WorkingHeader,\r
+    sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)\r
+    );\r
+\r
+  //\r
+  // Filter out the Crc and State fields\r
+  //\r
+  SetMem (\r
+    &WorkingBlockHeader.Crc,\r
+    sizeof (UINT32),\r
+    FTW_ERASED_BYTE\r
+    );\r
+  WorkingBlockHeader.WorkingBlockValid    = FTW_ERASE_POLARITY;\r
+  WorkingBlockHeader.WorkingBlockInvalid  = FTW_ERASE_POLARITY;\r
+\r
+  //\r
+  // Calculate the Crc of woking block header\r
+  //\r
+  Status = gBS->CalculateCrc32 (\r
+                  (UINT8 *) &WorkingBlockHeader,\r
+                  sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),\r
+                  &WorkingBlockHeader.Crc\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return FALSE;\r
+  }\r
+\r
+  if (WorkingBlockHeader.Crc != WorkingHeader->Crc) {\r
+    DEBUG ((EFI_D_ERROR, "Ftw: Work block header CRC check error\n"));\r
+    return FALSE;\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+/**\r
+  Initialize a work space when there is no work space.\r
+\r
+  @param WorkingHeader   Pointer of working block header\r
+\r
+  @retval  EFI_SUCCESS    The function completed successfully\r
+  @retval  EFI_ABORTED    The function could not complete successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+InitWorkSpaceHeader (\r
+  IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  if (WorkingHeader == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Here using gEfiSystemNvDataFvGuid as the signature.\r
+  //\r
+  CopyMem (\r
+    &WorkingHeader->Signature,\r
+    &gEfiSystemNvDataFvGuid,\r
+    sizeof (EFI_GUID)\r
+    );\r
+  WorkingHeader->WriteQueueSize = (UINT64) (PcdGet32 (PcdFlashNvStorageFtwWorkingSize) - sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER));\r
+\r
+  //\r
+  // Crc is calculated with all the fields except Crc and STATE\r
+  //\r
+  WorkingHeader->WorkingBlockValid    = FTW_ERASE_POLARITY;\r
+  WorkingHeader->WorkingBlockInvalid  = FTW_ERASE_POLARITY;\r
+\r
+  SetMem (\r
+    &WorkingHeader->Crc,\r
+    sizeof (UINT32),\r
+    FTW_ERASED_BYTE\r
+    );\r
+\r
+  //\r
+  // Calculate the CRC value\r
+  //\r
+  Status = gBS->CalculateCrc32 (\r
+                  (UINT8 *) WorkingHeader,\r
+                  sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),\r
+                  &WorkingHeader->Crc\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_ABORTED;\r
+  }\r
+  //\r
+  // Restore the WorkingBlockValid flag to VALID state\r
+  //\r
+  WorkingHeader->WorkingBlockValid    = FTW_VALID_STATE;\r
+  WorkingHeader->WorkingBlockInvalid  = FTW_INVALID_STATE;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Read from working block to refresh the work space in memory.\r
+\r
+  @param FtwDevice   Point to private data of FTW driver\r
+\r
+  @retval  EFI_SUCCESS    The function completed successfully\r
+  @retval  EFI_ABORTED    The function could not complete successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+WorkSpaceRefresh (\r
+  IN EFI_FTW_DEVICE  *FtwDevice\r
+  )\r
+{\r
+  EFI_STATUS                      Status;\r
+  UINTN                           Length;\r
+  UINTN                           Offset;\r
+  EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader;\r
+\r
+  //\r
+  // Initialize WorkSpace as FTW_ERASED_BYTE\r
+  //\r
+  SetMem (\r
+    FtwDevice->FtwWorkSpace,\r
+    FtwDevice->FtwWorkSpaceSize,\r
+    FTW_ERASED_BYTE\r
+    );\r
+\r
+  //\r
+  // Read from working block\r
+  //\r
+  Length = FtwDevice->FtwWorkSpaceSize;\r
+  Status = FtwDevice->FtwFvBlock->Read (\r
+                                    FtwDevice->FtwFvBlock,\r
+                                    FtwDevice->FtwWorkSpaceLba,\r
+                                    FtwDevice->FtwWorkSpaceBase,\r
+                                    &Length,\r
+                                    FtwDevice->FtwWorkSpace\r
+                                    );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_ABORTED;\r
+  }\r
+  //\r
+  // Refresh the FtwLastWriteHeader\r
+  //\r
+  Status = FtwGetLastWriteHeader (\r
+            FtwDevice->FtwWorkSpaceHeader,\r
+            FtwDevice->FtwWorkSpaceSize,\r
+            &FtwDevice->FtwLastWriteHeader\r
+            );\r
+\r
+  FtwHeader = FtwDevice->FtwLastWriteHeader;\r
+  Offset    = (UINTN) (UINT8 *) FtwHeader - (UINTN) FtwDevice->FtwWorkSpace;\r
+\r
+  //\r
+  // if the Header is out of the workspace limit, call reclaim.\r
+  //\r
+  if (EFI_ERROR (Status) && (Offset >= FtwDevice->FtwWorkSpaceSize)) {\r
+    //\r
+    // reclaim work space in working block.\r
+    //\r
+    Status = FtwReclaimWorkSpace (FtwDevice, TRUE);\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((EFI_D_ERROR, "Ftw: Reclaim workspace - %r\n", Status));\r
+      return EFI_ABORTED;\r
+    }\r
+    //\r
+    // Read from working block again\r
+    //\r
+    Length = FtwDevice->FtwWorkSpaceSize;\r
+    Status = FtwDevice->FtwFvBlock->Read (\r
+                                      FtwDevice->FtwFvBlock,\r
+                                      FtwDevice->FtwWorkSpaceLba,\r
+                                      FtwDevice->FtwWorkSpaceBase,\r
+                                      &Length,\r
+                                      FtwDevice->FtwWorkSpace\r
+                                      );\r
+    if (EFI_ERROR (Status)) {\r
+      return EFI_ABORTED;\r
+    }\r
+\r
+    Status = FtwGetLastWriteHeader (\r
+              FtwDevice->FtwWorkSpaceHeader,\r
+              FtwDevice->FtwWorkSpaceSize,\r
+              &FtwDevice->FtwLastWriteHeader\r
+              );\r
+  }\r
+  //\r
+  // Refresh the FtwLastWriteRecord\r
+  //\r
+  Status = FtwGetLastWriteRecord (\r
+            FtwDevice->FtwLastWriteHeader,\r
+            &FtwDevice->FtwLastWriteRecord\r
+            );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Reclaim the work space on the working block.\r
+\r
+  @param FtwDevice       Point to private data of FTW driver\r
+  @param PreserveRecord  Whether to preserve the working record is needed\r
+\r
+  @retval EFI_SUCCESS            The function completed successfully\r
+  @retval EFI_OUT_OF_RESOURCES   Allocate memory error\r
+  @retval EFI_ABORTED            The function could not complete successfully\r
+\r
+**/\r
+EFI_STATUS\r
+FtwReclaimWorkSpace (\r
+  IN EFI_FTW_DEVICE  *FtwDevice,\r
+  IN BOOLEAN         PreserveRecord\r
+  )\r
+{\r
+  EFI_STATUS                              Status;\r
+  UINTN                                   Length;\r
+  EFI_FAULT_TOLERANT_WRITE_HEADER         *Header;\r
+  UINT8                                   *TempBuffer;\r
+  UINTN                                   TempBufferSize;\r
+  UINTN                                   SpareBufferSize;\r
+  UINT8                                   *SpareBuffer;\r
+  EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader;\r
+  UINTN                                   Index;\r
+  UINT8                                   *Ptr;\r
+\r
+  DEBUG ((EFI_D_ERROR, "Ftw: start to reclaim work space\n"));\r
+\r
+  //\r
+  // Read all original data from working block to a memory buffer\r
+  //\r
+  TempBufferSize = FtwDevice->SpareAreaLength;\r
+  TempBuffer     = AllocateZeroPool (TempBufferSize);\r
+  if (TempBuffer == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Ptr = TempBuffer;\r
+  for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
+    Length = FtwDevice->BlockSize;\r
+    Status = FtwDevice->FtwFvBlock->Read (\r
+                                          FtwDevice->FtwFvBlock,\r
+                                          FtwDevice->FtwWorkBlockLba + Index,\r
+                                          0,\r
+                                          &Length,\r
+                                          Ptr\r
+                                          );\r
+    if (EFI_ERROR (Status)) {\r
+      FreePool (TempBuffer);\r
+      return EFI_ABORTED;\r
+    }\r
+\r
+    Ptr += Length;\r
+  }\r
+  //\r
+  // Clean up the workspace, remove all the completed records.\r
+  //\r
+  Ptr = TempBuffer +\r
+        ((UINTN) (FtwDevice->FtwWorkSpaceLba - FtwDevice->FtwWorkBlockLba)) * FtwDevice->BlockSize + \r
+        FtwDevice->FtwWorkSpaceBase;\r
+\r
+  //\r
+  // Clear the content of buffer that will save the new work space data\r
+  //\r
+  SetMem (Ptr, FtwDevice->FtwWorkSpaceSize, FTW_ERASED_BYTE);\r
+\r
+  //\r
+  // Copy EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER to buffer\r
+  //\r
+  CopyMem (\r
+    Ptr,\r
+    FtwDevice->FtwWorkSpaceHeader,\r
+    sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)\r
+    );\r
+  if (PreserveRecord) {\r
+    //\r
+    // Get the last record following the header,\r
+    //\r
+    Status = FtwGetLastWriteHeader (\r
+               FtwDevice->FtwWorkSpaceHeader,\r
+               FtwDevice->FtwWorkSpaceSize,\r
+               &FtwDevice->FtwLastWriteHeader\r
+               );\r
+    Header = FtwDevice->FtwLastWriteHeader;\r
+    if (!EFI_ERROR (Status) && (Header != NULL) && (Header->Complete != FTW_VALID_STATE)) {\r
+      CopyMem (\r
+        Ptr + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),\r
+        FtwDevice->FtwLastWriteHeader,\r
+        WRITE_TOTAL_SIZE (Header->NumberOfWrites, Header->PrivateDataSize)\r
+        );\r
+    }\r
+  }\r
+\r
+  CopyMem (\r
+    FtwDevice->FtwWorkSpace,\r
+    Ptr,\r
+    FtwDevice->FtwWorkSpaceSize\r
+    );\r
+\r
+  FtwGetLastWriteHeader (\r
+    FtwDevice->FtwWorkSpaceHeader,\r
+    FtwDevice->FtwWorkSpaceSize,\r
+    &FtwDevice->FtwLastWriteHeader\r
+    );\r
+\r
+  //\r
+  // Set the WorkingBlockValid and WorkingBlockInvalid as INVALID\r
+  //\r
+  WorkingBlockHeader                      = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (TempBuffer + FtwDevice->FtwWorkSpaceBase);\r
+  WorkingBlockHeader->WorkingBlockValid   = FTW_INVALID_STATE;\r
+  WorkingBlockHeader->WorkingBlockInvalid = FTW_INVALID_STATE;\r
+\r
+  //\r
+  // Try to keep the content of spare block\r
+  // Save spare block into a spare backup memory buffer (Sparebuffer)\r
+  //\r
+  SpareBufferSize = FtwDevice->SpareAreaLength;\r
+  SpareBuffer     = AllocatePool (SpareBufferSize);\r
+  if (SpareBuffer == NULL) {\r
+    FreePool (TempBuffer);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Ptr = SpareBuffer;\r
+  for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
+    Length = FtwDevice->BlockSize;\r
+    Status = FtwDevice->FtwBackupFvb->Read (\r
+                                        FtwDevice->FtwBackupFvb,\r
+                                        FtwDevice->FtwSpareLba + Index,\r
+                                        0,\r
+                                        &Length,\r
+                                        Ptr\r
+                                        );\r
+    if (EFI_ERROR (Status)) {\r
+      FreePool (TempBuffer);\r
+      FreePool (SpareBuffer);\r
+      return EFI_ABORTED;\r
+    }\r
+\r
+    Ptr += Length;\r
+  }\r
+  //\r
+  // Write the memory buffer to spare block\r
+  //\r
+  Status  = FtwEraseSpareBlock (FtwDevice);\r
+  Ptr     = TempBuffer;\r
+  for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
+    Length = FtwDevice->BlockSize;\r
+    Status = FtwDevice->FtwBackupFvb->Write (\r
+                                            FtwDevice->FtwBackupFvb,\r
+                                            FtwDevice->FtwSpareLba + Index,\r
+                                            0,\r
+                                            &Length,\r
+                                            Ptr\r
+                                            );\r
+    if (EFI_ERROR (Status)) {\r
+      FreePool (TempBuffer);\r
+      FreePool (SpareBuffer);\r
+      return EFI_ABORTED;\r
+    }\r
+\r
+    Ptr += Length;\r
+  }\r
+  //\r
+  // Free TempBuffer\r
+  //\r
+  FreePool (TempBuffer);\r
+\r
+  //\r
+  // Set the WorkingBlockValid in spare block\r
+  //\r
+  Status = FtwUpdateFvState (\r
+            FtwDevice->FtwBackupFvb,\r
+            FtwDevice->FtwWorkSpaceLba,\r
+            FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),\r
+            WORKING_BLOCK_VALID\r
+            );\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (SpareBuffer);\r
+    return EFI_ABORTED;\r
+  }\r
+  //\r
+  // Before erase the working block, set WorkingBlockInvalid in working block.\r
+  //\r
+  // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,\r
+  //                          WorkingBlockInvalid);\r
+  //\r
+  Status = FtwUpdateFvState (\r
+            FtwDevice->FtwFvBlock,\r
+            FtwDevice->FtwWorkSpaceLba,\r
+            FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),\r
+            WORKING_BLOCK_INVALID\r
+            );\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (SpareBuffer);\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  FtwDevice->FtwWorkSpaceHeader->WorkingBlockInvalid = FTW_VALID_STATE;\r
+\r
+  //\r
+  // Write the spare block to working block\r
+  //\r
+  Status = FlushSpareBlockToWorkingBlock (FtwDevice);\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (SpareBuffer);\r
+    return Status;\r
+  }\r
+  //\r
+  // Restore spare backup buffer into spare block , if no failure happened during FtwWrite.\r
+  //\r
+  Status  = FtwEraseSpareBlock (FtwDevice);\r
+  Ptr     = SpareBuffer;\r
+  for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
+    Length = FtwDevice->BlockSize;\r
+    Status = FtwDevice->FtwBackupFvb->Write (\r
+                                        FtwDevice->FtwBackupFvb,\r
+                                        FtwDevice->FtwSpareLba + Index,\r
+                                        0,\r
+                                        &Length,\r
+                                        Ptr\r
+                                        );\r
+    if (EFI_ERROR (Status)) {\r
+      FreePool (SpareBuffer);\r
+      return EFI_ABORTED;\r
+    }\r
+\r
+    Ptr += Length;\r
+  }\r
+\r
+  FreePool (SpareBuffer);\r
+\r
+  DEBUG ((EFI_D_ERROR, "Ftw: reclaim work space successfully\n"));\r
+\r
+  return EFI_SUCCESS;\r
+}\r