Add full version FaultTolerantWrite Dxe driver.

git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@7787 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
lgao4 2009-03-04 01:05:31 +00:00
parent d0c94e5954
commit 85e923a528
9 changed files with 3841 additions and 1 deletions

View File

@ -0,0 +1,207 @@
/** @file
Fault Tolerant Write protocol provides boot-time service to do fault tolerant
write capability for block devices. The protocol provides for non-volatile
intermediate storage of the data and private information a caller would need to
recover from a critical fault, such as power failure.
Copyright (c) 2009, Intel Corporation
All rights reserved. This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#ifndef _FW_FAULT_TOLERANT_WRITE_PROTOCOL_H_
#define _FW_FAULT_TOLERANT_WRITE_PROTOCOL_H_
#define EFI_FAULT_TOLERANT_WRITE_PROTOCOL_GUID \
{ \
0x3ebd9e82, 0x2c78, 0x4de6, {0x97, 0x86, 0x8d, 0x4b, 0xfc, 0xb7, 0xc8, 0x81 } \
}
//
// Forward reference for pure ANSI compatability
//
typedef struct _EFI_FAULT_TOLERANT_WRITE_PROTOCOL EFI_FAULT_TOLERANT_WRITE_PROTOCOL;
/**
Query the largest block that may be updated in a fault tolerant manner.
@param This Indicates a pointer to the calling context.
@param BlockSize A pointer to a caller allocated UINTN that is
updated to indicate the size of the largest block
that can be updated.
@retval EFI_SUCCESS The function completed successfully
@retval EFI_ABORTED The function could not complete successfully.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_FAULT_TOLERANT_WRITE_GET_MAX_BLOCK_SIZE) (
IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL * This,
OUT UINTN *BlockSize
);
/**
Allocates space for the protocol to maintain information about writes.
Since writes must be completed in a fault tolerant manner and multiple
updates will require more resources to be successful, this function
enables the protocol to ensure that enough space exists to track
information about the upcoming writes.
@param This Indicates a pointer to the calling context.
@param CallerId The GUID identifying the write.
@param PrivateDataSize The size of the caller's private data that must be
recorded for each write.
@param NumberOfWrites The number of fault tolerant block writes that will
need to occur.
@retval EFI_SUCCESS The function completed successfully
@retval EFI_ABORTED The function could not complete successfully.
@retval EFI_ACCESS_DENIED All allocated writes have not been completed. All
writes must be completed or aborted before another
fault tolerant write can occur.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_FAULT_TOLERANT_WRITE_ALLOCATE) (
IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL * This,
IN EFI_GUID * CallerId,
IN UINTN PrivateDataSize,
IN UINTN NumberOfWrites
);
/**
Starts a target block update. This records information about the write
in fault tolerant storage and will complete the write in a recoverable
manner, ensuring at all times that either the original contents or
the modified contents are available.
@param This Calling context
@param Lba The logical block address of the target block.
@param Offset The offset within the target block to place the
data.
@param Length The number of bytes to write to the target block.
@param PrivateData A pointer to private data that the caller requires
to complete any pending writes in the event of a
fault.
@param FvBlockHandle The handle of FVB protocol that provides services
for reading, writing, and erasing the target block.
@param Buffer The data to write.
@retval EFI_SUCCESS The function completed successfully
@retval EFI_ABORTED The function could not complete successfully.
@retval EFI_BAD_BUFFER_SIZE The write would span a block boundary, which is not
a valid action.
@retval EFI_ACCESS_DENIED No writes have been allocated.
@retval EFI_NOT_READY The last write has not been completed. Restart ()
must be called to complete it.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_FAULT_TOLERANT_WRITE_WRITE) (
IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL * This,
IN EFI_LBA Lba,
IN UINTN Offset,
IN UINTN Length,
IN VOID *PrivateData,
IN EFI_HANDLE FvbHandle,
IN VOID *Buffer
);
/**
Restarts a previously interrupted write. The caller must provide the
block protocol needed to complete the interrupted write.
@param This Calling context.
@param FvBlockProtocol The handle of FVB protocol that provides services
for reading, writing, and erasing the target block.
@retval EFI_SUCCESS The function completed successfully
@retval EFI_ABORTED The function could not complete successfully.
@retval EFI_ACCESS_DENIED No pending writes exist.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_FAULT_TOLERANT_WRITE_RESTART) (
IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL * This,
IN EFI_HANDLE FvbHandle
);
/**
Aborts all previous allocated writes.
@param This Calling context
@retval EFI_SUCCESS The function completed successfully
@retval EFI_ABORTED The function could not complete successfully.
@retval EFI_NOT_FOUND No allocated writes exist.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_FAULT_TOLERANT_WRITE_ABORT) (
IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL * This
);
/**
Starts a target block update. This records information about the write
in fault tolerant storage and will complete the write in a recoverable
manner, ensuring at all times that either the original contents or
the modified contents are available.
@param This Indicates a pointer to the calling context.
@param CallerId The GUID identifying the last write.
@param Lba The logical block address of the last write.
@param Offset The offset within the block of the last write.
@param Length The length of the last write.
@param PrivateDataSize On input, the size of the PrivateData buffer. On
output, the size of the private data stored for
this write.
@param PrivateData A pointer to a buffer. The function will copy
PrivateDataSize bytes from the private data stored
for this write.
@param Complete A Boolean value with TRUE indicating that the write
was completed.
@retval EFI_SUCCESS The function completed successfully
@retval EFI_ABORTED The function could not complete successfully.
@retval EFI_NOT_FOUND No allocated writes exist.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_FAULT_TOLERANT_WRITE_GET_LAST_WRITE) (
IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL * This,
OUT EFI_GUID * CallerId,
OUT EFI_LBA *Lba,
OUT UINTN *Offset,
OUT UINTN *Length,
IN OUT UINTN *PrivateDataSize,
OUT VOID *PrivateData,
OUT BOOLEAN *Complete
);
//
// Protocol declaration
//
struct _EFI_FAULT_TOLERANT_WRITE_PROTOCOL {
EFI_FAULT_TOLERANT_WRITE_GET_MAX_BLOCK_SIZE GetMaxBlockSize;
EFI_FAULT_TOLERANT_WRITE_ALLOCATE Allocate;
EFI_FAULT_TOLERANT_WRITE_WRITE Write;
EFI_FAULT_TOLERANT_WRITE_RESTART Restart;
EFI_FAULT_TOLERANT_WRITE_ABORT Abort;
EFI_FAULT_TOLERANT_WRITE_GET_LAST_WRITE GetLastWrite;
};
extern EFI_GUID gEfiFaultTolerantWriteProtocolGuid;
#endif

View File

@ -0,0 +1,182 @@
/** @file
The EFI_SWAP_ADDRESS_RANGE_PROTOCOL is used to abstract the swap operation of boot block
and backup block of FV. This swap is especially needed when updating the boot block of FV. If any
power failure happens during updating boot block, the swapped backup block (now is the boot block)
can boot the machine with old boot block backuped in it. The swap operation is platform dependent, so
other protocols such as FTW (Fault Tolerant Write) should use this protocol instead of handling hardward directly.
Copyright (c) 2009, Intel Corporation
All rights reserved. This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#ifndef _EFI_SWAP_ADDRESS_RANGE_PROTOCOL_H_
#define _EFI_SWAP_ADDRESS_RANGE_PROTOCOL_H_
#define EFI_SWAP_ADDRESS_RANGE_PROTOCOL_GUID \
{ \
0x1259f60d, 0xb754, 0x468e, {0xa7, 0x89, 0x4d, 0xb8, 0x5d, 0x55, 0xe8, 0x7e } \
}
//
// Forward reference for pure ANSI compatability
//
typedef struct _EFI_SWAP_ADDRESS_RANGE_PROTOCOL EFI_SWAP_ADDRESS_RANGE_PROTOCOL;
#define EFI_UNSUPPORT_LOCK 0
#define EFI_SOFTWARE_LOCK 1
#define EFI_HARDWARE_LOCK 2
typedef UINT8 EFI_SWAP_LOCK_CAPABILITY;
//
// Protocl APIs
//
/**
This service gets the address range location of boot block and backup block.
The EFI_GET_RANGE_LOCATION service allows caller to get the range location of
boot block and backup block.
@param This Indicates the calling context.
@param BootBlockBase Base address of current boot block.
@param BootBlockSize Size (in bytes) of current boot block.
@param BackupBlockBase Base address of current backup block.
@param BackupBlockSize Size (in bytes) of current backup block.
@retval EFI_SUCCESS The call was successful.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_GET_RANGE_LOCATION) (
IN EFI_SWAP_ADDRESS_RANGE_PROTOCOL * This,
OUT EFI_PHYSICAL_ADDRESS * BootBlockBase,
OUT UINTN *BootBlockSize,
OUT EFI_PHYSICAL_ADDRESS * BackupBlockBase,
OUT UINTN *BackupBlockSize
);
/**
This service checks if the boot block and backup block has been swapped.
The EFI_GET_SWAP_STATE service allows caller to get current swap state of boot block and backup block.
@param This Indicates the calling context.
@param SwapState True if the boot block and backup block has been swapped.
False if the boot block and backup block has not been swapped.
@retval EFI_SUCCESS The call was successful.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_GET_SWAP_STATE) (
IN EFI_SWAP_ADDRESS_RANGE_PROTOCOL * This,
OUT BOOLEAN *SwapState
);
/**
This service swaps the boot block and backup block, or swaps them back.
The EFI_SET_SWAP_STATE service allows caller to set the swap state of boot block and backup block.
It also acquires and releases software swap lock during operation. Note the setting of new swap state
is not affected by the old swap state.
@param This Indicates the calling context.
@param NewSwapState True to swap real boot block and backup block , False to swap them back..
@retval EFI_SUCCESS The call was successful.
@retval EFI_ABORTED Set swap state error
**/
typedef
EFI_STATUS
(EFIAPI *EFI_SET_SWAP_STATE) (
IN EFI_SWAP_ADDRESS_RANGE_PROTOCOL * This,
IN BOOLEAN NewSwapState
);
/**
This service checks if a RTC (Real Time Clock) power failure happened.
The EFI_GET_RTC_POWER_STATUS service allows caller to get Real Time Clock power failure status.
If parameter RtcPowerFailed is true after function returns, the trickle current (from the main battery or trickle supply)
has been removed or failed, this means the swap status was lost in some platform (such as IA32).
So it is recommended to check RTC power status before calling GetSwapState().
@param This Indicates the calling context.
@param RtcPowerFailed True if a RTC (Real Time Clock) power failure has happened.
@retval EFI_SUCCESS The call was successful.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_GET_RTC_POWER_STATUS) (
IN EFI_SWAP_ADDRESS_RANGE_PROTOCOL * This,
OUT BOOLEAN *RtcPowerFailed
);
/**
This service returns supported lock methods for swap operation in current platform. Could be software lock, hardware lock, or unsupport lock.
The EFI_GET_SWAP_LOCK_CAPABILITY service allows caller to get supported lock method for swap operation in current platform.
Note that software and hardware lock mothod can be used simultaneously.
@param This Indicates the calling context.
@param LockCapability Current lock method for swap operation.
@retval EFI_SUCCESS The call was successful.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_GET_SWAP_LOCK_CAPABILITY) (
IN EFI_SWAP_ADDRESS_RANGE_PROTOCOL * This,
OUT EFI_SWAP_LOCK_CAPABILITY * LockCapability
);
/**
This service is used to acquire or release appointed kind of lock for Swap Address Range operation.
The EFI_GET_SWAP_LOCK_CAPABILITY service allows caller to get supported lock method for swap operation in current platform.
Note that software and hardware lock mothod can be used simultaneously.
@param This Indicates the calling context.
@param LockCapability Indicates which lock to acquire or release.
@param NewLockState True to acquire lock, False to release lock.
@retval EFI_SUCCESS The call was successful.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_SET_SWAP_LOCK) (
IN EFI_SWAP_ADDRESS_RANGE_PROTOCOL * This,
IN EFI_SWAP_LOCK_CAPABILITY LockCapability,
IN BOOLEAN NewLockState
);
struct _EFI_SWAP_ADDRESS_RANGE_PROTOCOL {
EFI_GET_RANGE_LOCATION GetRangeLocation; // has output parameters for base and length
EFI_GET_SWAP_STATE GetSwapState; // are ranges swapped or not
EFI_SET_SWAP_STATE SetSwapState; // swap or unswap ranges
EFI_GET_RTC_POWER_STATUS GetRtcPowerStatus; // checks RTC battery, or whatever...
EFI_GET_SWAP_LOCK_CAPABILITY GetSwapLockCapability; // Get TOP_SWAP lock capability,
EFI_SET_SWAP_LOCK SetSwapLock; // Set TOP_SWAP lock state
};
extern EFI_GUID gEfiSwapAddressRangeProtocolGuid;
#endif

View File

@ -209,6 +209,14 @@
## Include/Protocol/TcgPlatform.h ## Include/Protocol/TcgPlatform.h
gEfiTcgPlatformProtocolGuid = { 0x8c4c9a41, 0xbf56, 0x4627, { 0x9e, 0xa, 0xc8, 0x38, 0x6d, 0x66, 0x11, 0x5c }} gEfiTcgPlatformProtocolGuid = { 0x8c4c9a41, 0xbf56, 0x4627, { 0x9e, 0xa, 0xc8, 0x38, 0x6d, 0x66, 0x11, 0x5c }}
## Fault Tolerant Write protocol provides boot-time service to do fault tolerant write capability for block devices.
# Include/Protocol/FaultTolerantWrite.h
gEfiFaultTolerantWriteProtocolGuid = { 0x3EBD9E82, 0x2C78, 0x4DE6, { 0x97, 0x86, 0x8D, 0x4B, 0xFC, 0xB7, 0xC8, 0x81 }}
## This protocol is used to abstract the swap operation of boot block and backup block of boot FV.
# Include/Protocol/SwapAddressRange.h
gEfiSwapAddressRangeProtocolGuid = { 0x1259F60D, 0xB754, 0x468E, { 0xA7, 0x89, 0x4D, 0xB8, 0x5D, 0x55, 0xE8, 0x7E }}
[PcdsFeatureFlag] [PcdsFeatureFlag]
## Indicate whether platform can support update capsule across a system reset ## Indicate whether platform can support update capsule across a system reset
gEfiMdeModulePkgTokenSpaceGuid.PcdSupportUpdateCapsuleReset|FALSE|BOOLEAN|0x0001001d gEfiMdeModulePkgTokenSpaceGuid.PcdSupportUpdateCapsuleReset|FALSE|BOOLEAN|0x0001001d
@ -283,6 +291,11 @@
## ##
gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintEnable|TRUE|BOOLEAN|0x0001200a gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintEnable|TRUE|BOOLEAN|0x0001200a
##
# If TRUE, FULL FTW protocol services (total six APIs) will be produced.
# If FASLE, only FTW Write service is available.
gEfiMdeModulePkgTokenSpaceGuid.PcdFullFtwServiceEnable|TRUE|BOOLEAN|0x0001200a
[PcdsFixedAtBuild] [PcdsFixedAtBuild]
## Dynamic type PCD can be registered callback function for Pcd setting action. ## Dynamic type PCD can be registered callback function for Pcd setting action.
# PcdMaxPeiPcdCallBackNumberPerPcdEntry indicate maximum number of callback function # PcdMaxPeiPcdCallBackNumberPerPcdEntry indicate maximum number of callback function

View File

@ -331,7 +331,7 @@
MdeModulePkg/Application/VariableInfo/VariableInfo.inf MdeModulePkg/Application/VariableInfo/VariableInfo.inf
MdeModulePkg/Universal/Variable/Pei/VariablePei.inf MdeModulePkg/Universal/Variable/Pei/VariablePei.inf
MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf
MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf
[Components.IA32, Components.X64] [Components.IA32, Components.X64]
MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxe.inf MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxe.inf

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,672 @@
/** @file
The internal header file includes the common header files, defines
internal structure and functions used by FtwLite module.
Copyright (c) 2006 - 2008, Intel Corporation
All rights reserved. This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#ifndef _EFI_FAULT_TOLERANT_WRITE_H_
#define _EFI_FAULT_TOLERANT_WRITE_H_
#include <PiDxe.h>
#include <Guid/SystemNvDataGuid.h>
#include <Protocol/FaultTolerantWrite.h>
#include <Protocol/FirmwareVolumeBlock.h>
#include <Protocol/SwapAddressRange.h>
#include <Library/PcdLib.h>
#include <Library/DebugLib.h>
#include <Library/UefiDriverEntryPoint.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
//
// Flash erase polarity is 1
//
#define FTW_ERASE_POLARITY 1
#define FTW_VALID_STATE 0
#define FTW_INVALID_STATE 1
#define FTW_ERASED_BYTE ((UINT8) (255))
#define FTW_POLARITY_REVERT ((UINT8) (255))
//
// EFI Fault tolerant block update write queue entry
//
typedef struct {
UINT8 HeaderAllocated : 1;
UINT8 WritesAllocated : 1;
UINT8 Complete : 1;
#define HEADER_ALLOCATED 0x1
#define WRITES_ALLOCATED 0x2
#define WRITES_COMPLETED 0x4
UINT8 Reserved : 5;
EFI_GUID CallerId;
UINTN NumberOfWrites;
UINTN PrivateDataSize;
} EFI_FAULT_TOLERANT_WRITE_HEADER;
//
// EFI Fault tolerant block update write queue record
//
typedef struct {
UINT8 BootBlockUpdate : 1;
UINT8 SpareComplete : 1;
UINT8 DestinationComplete : 1;
#define BOOT_BLOCK_UPDATE 0x1
#define SPARE_COMPLETED 0x2
#define DEST_COMPLETED 0x4
UINT8 Reserved : 5;
EFI_LBA Lba;
UINTN Offset;
UINTN Length;
EFI_PHYSICAL_ADDRESS FvBaseAddress;
//
// UINT8 PrivateData[PrivateDataSize]
//
} EFI_FAULT_TOLERANT_WRITE_RECORD;
#define RECORD_SIZE(PrivateDataSize) (sizeof (EFI_FAULT_TOLERANT_WRITE_RECORD) + PrivateDataSize)
#define RECORD_TOTAL_SIZE(NumberOfWrites, PrivateDataSize) \
((NumberOfWrites) * (sizeof (EFI_FAULT_TOLERANT_WRITE_RECORD) + PrivateDataSize))
#define WRITE_TOTAL_SIZE(NumberOfWrites, PrivateDataSize) \
( \
sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER) + (NumberOfWrites) * \
(sizeof (EFI_FAULT_TOLERANT_WRITE_RECORD) + PrivateDataSize) \
)
#define FTW_DEVICE_SIGNATURE SIGNATURE_32 ('F', 'T', 'W', 'D')
//
// EFI Fault tolerant protocol private data structure
//
typedef struct {
UINTN Signature;
EFI_HANDLE Handle;
EFI_FAULT_TOLERANT_WRITE_PROTOCOL FtwInstance;
EFI_PHYSICAL_ADDRESS WorkSpaceAddress; // Base address of working space range in flash.
EFI_PHYSICAL_ADDRESS SpareAreaAddress; // Base address of spare range in flash.
UINTN WorkSpaceLength; // Size of working space range in flash.
UINTN SpareAreaLength; // Size of spare range in flash.
UINTN NumberOfSpareBlock; // Number of the blocks in spare block.
UINTN BlockSize; // Block size in bytes of the blocks in flash
EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *FtwWorkSpaceHeader;// Pointer to Working Space Header in memory buffer
EFI_FAULT_TOLERANT_WRITE_HEADER *FtwLastWriteHeader;// Pointer to last record header in memory buffer
EFI_FAULT_TOLERANT_WRITE_RECORD *FtwLastWriteRecord;// Pointer to last record in memory buffer
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FtwFvBlock; // FVB of working block
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FtwBackupFvb; // FVB of spare block
EFI_LBA FtwSpareLba; // Start LBA of spare block
EFI_LBA FtwWorkBlockLba; // Start LBA of working block that contains working space in its last block.
EFI_LBA FtwWorkSpaceLba; // Start LBA of working space
UINTN FtwWorkSpaceBase; // Offset into the FtwWorkSpaceLba block.
UINTN FtwWorkSpaceSize; // Size of working space range that stores write record.
UINT8 *FtwWorkSpace; // Point to Work Space in memory buffer
//
// Following a buffer of FtwWorkSpace[FTW_WORK_SPACE_SIZE],
// Allocated with EFI_FTW_DEVICE.
//
} EFI_FTW_DEVICE;
#define FTW_CONTEXT_FROM_THIS(a) CR (a, EFI_FTW_DEVICE, FtwInstance, FTW_DEVICE_SIGNATURE)
//
// Driver entry point
//
/**
This function is the entry point of the Fault Tolerant Write driver.
@param ImageHandle A handle for the image that is initializing this driver
@param SystemTable A pointer to the EFI system table
@return EFI_SUCCESS FTW has finished the initialization
@retval EFI_NOT_FOUND Locate FVB protocol error
@retval EFI_OUT_OF_RESOURCES Allocate memory error
@retval EFI_VOLUME_CORRUPTED Firmware volume is error
@retval EFI_ABORTED FTW initialization error
**/
EFI_STATUS
EFIAPI
InitializeFaultTolerantWrite (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
);
//
// Fault Tolerant Write Protocol API
//
/**
Query the largest block that may be updated in a fault tolerant manner.
@param This Indicates a pointer to the calling context.
@param BlockSize A pointer to a caller allocated UINTN that is updated to
indicate the size of the largest block that can be updated.
@return EFI_SUCCESS The function completed successfully
**/
EFI_STATUS
EFIAPI
FtwGetMaxBlockSize (
IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,
OUT UINTN *BlockSize
);
/**
Allocates space for the protocol to maintain information about writes.
Since writes must be completed in a fault tolerant manner and multiple
updates will require more resources to be successful, this function
enables the protocol to ensure that enough space exists to track
information about the upcoming writes.
All writes must be completed or aborted before another fault tolerant write can occur.
@param This Indicates a pointer to the calling context.
@param CallerId The GUID identifying the write.
@param PrivateDataSize The size of the caller's private data
that must be recorded for each write.
@param NumberOfWrites The number of fault tolerant block writes
that will need to occur.
@return EFI_SUCCESS The function completed successfully
@retval EFI_ABORTED The function could not complete successfully.
@retval EFI_ACCESS_DENIED All allocated writes have not been completed.
**/
EFI_STATUS
EFIAPI
FtwAllocate (
IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,
IN EFI_GUID *CallerId,
IN UINTN PrivateDataSize,
IN UINTN NumberOfWrites
);
/**
Starts a target block update. This function will record data about write
in fault tolerant storage and will complete the write in a recoverable
manner, ensuring at all times that either the original contents or
the modified contents are available.
@param This Calling context
@param Lba The logical block address of the target block.
@param Offset The offset within the target block to place the data.
@param Length The number of bytes to write to the target block.
@param PrivateData A pointer to private data that the caller requires to
complete any pending writes in the event of a fault.
@param FvBlockHandle The handle of FVB protocol that provides services for
reading, writing, and erasing the target block.
@param Buffer The data to write.
@retval EFI_SUCCESS The function completed successfully
@retval EFI_ABORTED The function could not complete successfully.
@retval EFI_BAD_BUFFER_SIZE The input data can't fit within the spare block.
Offset + *NumBytes > SpareAreaLength.
@retval EFI_ACCESS_DENIED No writes have been allocated.
@retval EFI_OUT_OF_RESOURCES Cannot allocate enough memory resource.
@retval EFI_NOT_FOUND Cannot find FVB protocol by handle.
**/
EFI_STATUS
EFIAPI
FtwWrite (
IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,
IN EFI_LBA Lba,
IN UINTN Offset,
IN UINTN Length,
IN VOID *PrivateData,
IN EFI_HANDLE FvBlockHandle,
IN VOID *Buffer
);
/**
Restarts a previously interrupted write. The caller must provide the
block protocol needed to complete the interrupted write.
@param This Calling context.
@param FvBlockHandle The handle of FVB protocol that provides services for
reading, writing, and erasing the target block.
@retval EFI_SUCCESS The function completed successfully
@retval EFI_ACCESS_DENIED No pending writes exist
@retval EFI_NOT_FOUND FVB protocol not found by the handle
@retval EFI_ABORTED The function could not complete successfully
**/
EFI_STATUS
EFIAPI
FtwRestart (
IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,
IN EFI_HANDLE FvBlockHandle
);
/**
Aborts all previous allocated writes.
@param This Calling context
@retval EFI_SUCCESS The function completed successfully
@retval EFI_ABORTED The function could not complete successfully.
@retval EFI_NOT_FOUND No allocated writes exist.
**/
EFI_STATUS
EFIAPI
FtwAbort (
IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This
);
/**
Starts a target block update. This records information about the write
in fault tolerant storage and will complete the write in a recoverable
manner, ensuring at all times that either the original contents or
the modified contents are available.
@param This Indicates a pointer to the calling context.
@param CallerId The GUID identifying the last write.
@param Lba The logical block address of the last write.
@param Offset The offset within the block of the last write.
@param Length The length of the last write.
@param PrivateDataSize bytes from the private data
stored for this write.
@param PrivateData A pointer to a buffer. The function will copy
@param Complete A Boolean value with TRUE indicating
that the write was completed.
@retval EFI_SUCCESS The function completed successfully
@retval EFI_ABORTED The function could not complete successfully
@retval EFI_NOT_FOUND No allocated writes exist
@retval EFI_BUFFER_TOO_SMALL Input buffer is not larget enough
**/
EFI_STATUS
EFIAPI
FtwGetLastWrite (
IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,
OUT EFI_GUID *CallerId,
OUT EFI_LBA *Lba,
OUT UINTN *Offset,
OUT UINTN *Length,
IN OUT UINTN *PrivateDataSize,
OUT VOID *PrivateData,
OUT BOOLEAN *Complete
);
/**
Erase spare block.
@param FtwDevice The private data of FTW driver
@retval EFI_SUCCESS The erase request was successfully completed.
@retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state.
@retval EFI_DEVICE_ERROR The block device is not functioning
correctly and could not be written.
The firmware device may have been
partially erased.
@retval EFI_INVALID_PARAMETER One or more of the LBAs listed
in the variable argument list do
not exist in the firmware volume.
**/
EFI_STATUS
FtwEraseSpareBlock (
IN EFI_FTW_DEVICE *FtwDevice
);
/**
Retrive the proper FVB protocol interface by HANDLE.
@param FvBlockHandle The handle of FVB protocol that provides services for
reading, writing, and erasing the target block.
@param FvBlock The interface of FVB protocol
@retval EFI_SUCCESS The function completed successfully
@retval EFI_ABORTED The function could not complete successfully
**/
EFI_STATUS
FtwGetFvbByHandle (
IN EFI_HANDLE FvBlockHandle,
OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock
);
/**
Is it in working block?
@param FtwDevice The private data of FTW driver
@param FvBlock Fvb protocol instance
@param Lba The block specified
@return A BOOLEAN value indicating in working block or not.
**/
BOOLEAN
IsWorkingBlock (
EFI_FTW_DEVICE *FtwDevice,
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
EFI_LBA Lba
);
/**
Is it in boot block?
@param FtwDevice The private data of FTW driver
@param FvBlock Fvb protocol instance
@param Lba The block specified
@return A BOOLEAN value indicating in boot block or not.
**/
BOOLEAN
IsBootBlock (
EFI_FTW_DEVICE *FtwDevice,
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
EFI_LBA Lba
);
/**
Copy the content of spare block to a target block. Size is FTW_BLOCK_SIZE.
Spare block is accessed by FTW backup FVB protocol interface. LBA is 1.
Target block is accessed by FvbBlock protocol interface. LBA is Lba.
@param FtwDevice The private data of FTW driver
@param FvBlock FVB Protocol interface to access target block
@param Lba Lba of the target block
@retval EFI_SUCCESS Spare block content is copied to target block
@retval EFI_INVALID_PARAMETER Input parameter error
@retval EFI_OUT_OF_RESOURCES Allocate memory error
@retval EFI_ABORTED The function could not complete successfully
**/
EFI_STATUS
FlushSpareBlockToTargetBlock (
EFI_FTW_DEVICE *FtwDevice,
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
EFI_LBA Lba
);
/**
Copy the content of spare block to working block. Size is FTW_BLOCK_SIZE.
Spare block is accessed by FTW backup FVB protocol interface. LBA is
FtwDevice->FtwSpareLba.
Working block is accessed by FTW working FVB protocol interface. LBA is
FtwDevice->FtwWorkBlockLba.
Since the working block header is important when FTW initializes, the
state of the operation should be handled carefully. The Crc value is
calculated without STATE element.
@param FtwDevice The private data of FTW driver
@retval EFI_SUCCESS Spare block content is copied to target block
@retval EFI_OUT_OF_RESOURCES Allocate memory error
@retval EFI_ABORTED The function could not complete successfully
**/
EFI_STATUS
FlushSpareBlockToWorkingBlock (
EFI_FTW_DEVICE *FtwDevice
);
/**
Copy the content of spare block to a boot block. Size is FTW_BLOCK_SIZE.
Spare block is accessed by FTW working FVB protocol interface. LBA is 1.
Target block is accessed by FvbBlock protocol interface. LBA is Lba.
FTW will do extra work on boot block update.
FTW should depend on a protocol of EFI_ADDRESS_RANGE_SWAP_PROTOCOL,
which is produced by a chipset driver.
FTW updating boot block steps may be:
1. GetRangeLocation(), if the Range is inside the boot block, FTW know
that boot block will be update. It shall add a FLAG in the working block.
2. When spare block is ready,
3. SetSwapState(EFI_SWAPPED)
4. erasing boot block,
5. programming boot block until the boot block is ok.
6. SetSwapState(UNSWAPPED)
FTW shall not allow to update boot block when battery state is error.
@param FtwDevice The private data of FTW driver
@retval EFI_SUCCESS Spare block content is copied to boot block
@retval EFI_INVALID_PARAMETER Input parameter error
@retval EFI_OUT_OF_RESOURCES Allocate memory error
@retval EFI_ABORTED The function could not complete successfully
**/
EFI_STATUS
FlushSpareBlockToBootBlock (
EFI_FTW_DEVICE *FtwDevice
);
/**
Update a bit of state on a block device. The location of the bit is
calculated by the (Lba, Offset, bit). Here bit is determined by the
the name of a certain bit.
@param FvBlock FVB Protocol interface to access SrcBlock and DestBlock
@param Lba Lba of a block
@param Offset Offset on the Lba
@param NewBit New value that will override the old value if it can be change
@retval EFI_SUCCESS A state bit has been updated successfully
@retval Others Access block device error.
Notes:
Assume all bits of State are inside the same BYTE.
@retval EFI_ABORTED Read block fail
**/
EFI_STATUS
FtwUpdateFvState (
IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
IN EFI_LBA Lba,
IN UINTN Offset,
IN UINT8 NewBit
);
/**
Get the last Write Header pointer.
The last write header is the header whose 'complete' state hasn't been set.
After all, this header may be a EMPTY header entry for next Allocate.
@param FtwWorkSpaceHeader Pointer of the working block header
@param FtwWorkSpaceSize Size of the work space
@param FtwWriteHeader Pointer to retrieve the last write header
@retval EFI_SUCCESS Get the last write record successfully
@retval EFI_ABORTED The FTW work space is damaged
**/
EFI_STATUS
FtwGetLastWriteHeader (
IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *FtwWorkSpaceHeader,
IN UINTN FtwWorkSpaceSize,
OUT EFI_FAULT_TOLERANT_WRITE_HEADER **FtwWriteHeader
);
/**
Get the last Write Record pointer. The last write Record is the Record
whose DestinationCompleted state hasn't been set. After all, this Record
may be a EMPTY record entry for next write.
@param FtwWriteHeader Pointer to the write record header
@param FtwWriteRecord Pointer to retrieve the last write record
@retval EFI_SUCCESS Get the last write record successfully
@retval EFI_ABORTED The FTW work space is damaged
**/
EFI_STATUS
FtwGetLastWriteRecord (
IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwWriteHeader,
OUT EFI_FAULT_TOLERANT_WRITE_RECORD **FtwWriteRecord
);
/**
To check if FtwRecord is the first record of FtwHeader.
@param FtwHeader Pointer to the write record header
@param FtwRecord Pointer to the write record
@retval TRUE FtwRecord is the first Record of the FtwHeader
@retval FALSE FtwRecord is not the first Record of the FtwHeader
**/
BOOLEAN
IsFirstRecordOfWrites (
IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader,
IN EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord
);
/**
To check if FtwRecord is the last record of FtwHeader. Because the
FtwHeader has NumberOfWrites & PrivateDataSize, the FtwRecord can be
determined if it is the last record of FtwHeader.
@param FtwHeader Pointer to the write record header
@param FtwRecord Pointer to the write record
@retval TRUE FtwRecord is the last Record of the FtwHeader
@retval FALSE FtwRecord is not the last Record of the FtwHeader
**/
BOOLEAN
IsLastRecordOfWrites (
IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader,
IN EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord
);
/**
To check if FtwRecord is the first record of FtwHeader.
@param FtwHeader Pointer to the write record header
@param FtwRecord Pointer to retrieve the previous write record
@retval EFI_ACCESS_DENIED Input record is the first record, no previous record is return.
@retval EFI_SUCCESS The previous write record is found.
**/
EFI_STATUS
GetPreviousRecordOfWrites (
IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader,
IN OUT EFI_FAULT_TOLERANT_WRITE_RECORD **FtwRecord
);
/**
Check whether a flash buffer is erased.
@param Buffer Buffer to check
@param BufferSize Size of the buffer
@return A BOOLEAN value indicating erased or not.
**/
BOOLEAN
IsErasedFlashBuffer (
IN UINT8 *Buffer,
IN UINTN BufferSize
);
/**
Initialize a work space when there is no work space.
@param WorkingHeader Pointer of working block header
@retval EFI_SUCCESS The function completed successfully
@retval EFI_ABORTED The function could not complete successfully.
**/
EFI_STATUS
InitWorkSpaceHeader (
IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader
);
/**
Read from working block to refresh the work space in memory.
@param FtwDevice Point to private data of FTW driver
@retval EFI_SUCCESS The function completed successfully
@retval EFI_ABORTED The function could not complete successfully.
**/
EFI_STATUS
WorkSpaceRefresh (
IN EFI_FTW_DEVICE *FtwDevice
);
/**
Check to see if it is a valid work space.
@param WorkingHeader Pointer of working block header
@retval EFI_SUCCESS The function completed successfully
@retval EFI_ABORTED The function could not complete successfully.
**/
BOOLEAN
IsValidWorkSpace (
IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader
);
/**
Reclaim the work space on the working block.
@param FtwDevice Point to private data of FTW driver
@param PreserveRecord Whether to preserve the working record is needed
@retval EFI_SUCCESS The function completed successfully
@retval EFI_OUT_OF_RESOURCES Allocate memory error
@retval EFI_ABORTED The function could not complete successfully
**/
EFI_STATUS
FtwReclaimWorkSpace (
IN EFI_FTW_DEVICE *FtwDevice,
IN BOOLEAN PreserveRecord
);
/**
Get firmware block by address.
@param Address Address specified the block
@param FvBlock The block caller wanted
@retval EFI_SUCCESS The protocol instance if found.
@retval EFI_NOT_FOUND Block not found
**/
EFI_HANDLE
GetFvbByAddress (
IN EFI_PHYSICAL_ADDRESS Address,
OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock
);
#endif

View File

@ -0,0 +1,67 @@
#/** @file
# This driver installs Fault Tolerant Write (FTW) protocol,
# which provides fault tolerant write capability for block devices.
# Its implementation depends on the full functionality FVB protocol that support read, write/erase flash access.
#
# Copyright (c) 2006 - 2009, Intel Corporation
#
# All rights reserved. This program and the accompanying materials
# are licensed and made available under the terms and conditions of the BSD License
# which accompanies this distribution. The full text of the license may be found at
# http://opensource.org/licenses/bsd-license.php
# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#
#**/
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = FaultTolerantWriteDxe
FILE_GUID = FE5CEA76-4F72-49e8-986F-2CD899DFFE5D
MODULE_TYPE = DXE_DRIVER
VERSION_STRING = 1.0
ENTRY_POINT = InitializeFaultTolerantWrite
#
# The following information is for reference only and not required by the build tools.
#
# VALID_ARCHITECTURES = IA32 X64 IPF EBC
#
[Sources.common]
FtwMisc.c
UpdateWorkingBlock.c
FaultTolerantWrite.c
FaultTolerantWrite.h
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
[LibraryClasses]
UefiBootServicesTableLib
MemoryAllocationLib
BaseMemoryLib
UefiDriverEntryPoint
DebugLib
[Guids]
gEfiSystemNvDataFvGuid ## CONSUMES ## FV Signature of Working Space Header
[Protocols]
gEfiSwapAddressRangeProtocolGuid | PcdFullFtwServiceEnable ## CONSUMES
gEfiFirmwareVolumeBlockProtocolGuid ## CONSUMES
gEfiFaultTolerantWriteProtocolGuid ## PRODUCES
[FeaturePcd]
gEfiMdeModulePkgTokenSpaceGuid.PcdFullFtwServiceEnable
[Pcd]
gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase
gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize
gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase
gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize
[Depex]
gEfiFirmwareVolumeBlockProtocolGuid AND gEfiAlternateFvBlockGuid ## gEfiAlternateFvBlockGuid specifies FVB protocol with read, write/erase flash access.

View File

@ -0,0 +1,971 @@
/** @file
Internal generic functions to operate flash block.
Copyright (c) 2006 - 2008, Intel Corporation
All rights reserved. This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "FaultTolerantWrite.h"
/**
Check whether a flash buffer is erased.
@param Buffer Buffer to check
@param BufferSize Size of the buffer
@return A BOOLEAN value indicating erased or not.
**/
BOOLEAN
IsErasedFlashBuffer (
IN UINT8 *Buffer,
IN UINTN BufferSize
)
{
BOOLEAN IsEmpty;
UINT8 *Ptr;
UINTN Index;
Ptr = Buffer;
IsEmpty = TRUE;
for (Index = 0; Index < BufferSize; Index += 1) {
if (*Ptr++ != FTW_ERASED_BYTE) {
IsEmpty = FALSE;
break;
}
}
return IsEmpty;
}
/**
To erase the block with the spare block size.
@param FtwDevice The private data of FTW driver
@param FvBlock FVB Protocol interface
@param Lba Lba of the firmware block
@retval EFI_SUCCESS Block LBA is Erased successfully
@retval Others Error occurs
**/
EFI_STATUS
FtwEraseBlock (
IN EFI_FTW_DEVICE *FtwDevice,
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
EFI_LBA Lba
)
{
return FvBlock->EraseBlocks (
FvBlock,
Lba,
FtwDevice->NumberOfSpareBlock,
EFI_LBA_LIST_TERMINATOR
);
}
/**
Erase spare block.
@param FtwDevice The private data of FTW driver
@retval EFI_SUCCESS The erase request was successfully completed.
@retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state.
@retval EFI_DEVICE_ERROR The block device is not functioning
correctly and could not be written.
The firmware device may have been
partially erased.
@retval EFI_INVALID_PARAMETER One or more of the LBAs listed
in the variable argument list do
not exist in the firmware volume.
**/
EFI_STATUS
FtwEraseSpareBlock (
IN EFI_FTW_DEVICE *FtwDevice
)
{
return FtwDevice->FtwBackupFvb->EraseBlocks (
FtwDevice->FtwBackupFvb,
FtwDevice->FtwSpareLba,
FtwDevice->NumberOfSpareBlock,
EFI_LBA_LIST_TERMINATOR
);
}
/**
Retrive the proper FVB protocol interface by HANDLE.
@param FvBlockHandle The handle of FVB protocol that provides services for
reading, writing, and erasing the target block.
@param FvBlock The interface of FVB protocol
@retval EFI_SUCCESS The function completed successfully
@retval EFI_ABORTED The function could not complete successfully
**/
EFI_STATUS
FtwGetFvbByHandle (
IN EFI_HANDLE FvBlockHandle,
OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock
)
{
//
// To get the FVB protocol interface on the handle
//
return gBS->HandleProtocol (
FvBlockHandle,
&gEfiFirmwareVolumeBlockProtocolGuid,
(VOID **) FvBlock
);
}
/**
Is it in working block?
@param FtwDevice The private data of FTW driver
@param FvBlock Fvb protocol instance
@param Lba The block specified
@return A BOOLEAN value indicating in working block or not.
**/
BOOLEAN
IsWorkingBlock (
EFI_FTW_DEVICE *FtwDevice,
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
EFI_LBA Lba
)
{
//
// If matching the following condition, the target block is in working block.
// 1. Target block is on the FV of working block (Using the same FVB protocol instance).
// 2. Lba falls into the range of working block.
//
return (BOOLEAN)
(
(FvBlock == FtwDevice->FtwFvBlock) &&
(Lba >= FtwDevice->FtwWorkBlockLba) &&
(Lba <= FtwDevice->FtwWorkSpaceLba)
);
}
/**
Get firmware block by address.
@param Address Address specified the block
@param FvBlock The block caller wanted
@retval EFI_SUCCESS The protocol instance if found.
@retval EFI_NOT_FOUND Block not found
**/
EFI_HANDLE
GetFvbByAddress (
IN EFI_PHYSICAL_ADDRESS Address,
OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock
)
{
EFI_STATUS Status;
EFI_HANDLE *HandleBuffer;
UINTN HandleCount;
UINTN Index;
EFI_PHYSICAL_ADDRESS FvbBaseAddress;
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;
EFI_HANDLE FvbHandle;
*FvBlock = NULL;
FvbHandle = NULL;
//
// Locate all handles of Fvb protocol
//
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiFirmwareVolumeBlockProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR (Status)) {
return NULL;
}
//
// Get the FVB to access variable store
//
for (Index = 0; Index < HandleCount; Index += 1) {
Status = gBS->HandleProtocol (
HandleBuffer[Index],
&gEfiFirmwareVolumeBlockProtocolGuid,
(VOID **) &Fvb
);
if (EFI_ERROR (Status)) {
break;
}
//
// Compare the address and select the right one
//
Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress);
if (EFI_ERROR (Status)) {
continue;
}
FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) FvbBaseAddress);
if ((Address >= FvbBaseAddress) && (Address <= (FvbBaseAddress + (FwVolHeader->FvLength - 1)))) {
*FvBlock = Fvb;
FvbHandle = HandleBuffer[Index];
break;
}
}
FreePool (HandleBuffer);
return FvbHandle;
}
/**
Is it in boot block?
@param FtwDevice The private data of FTW driver
@param FvBlock Fvb protocol instance
@param Lba The block specified
@return A BOOLEAN value indicating in boot block or not.
**/
BOOLEAN
IsBootBlock (
EFI_FTW_DEVICE *FtwDevice,
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
EFI_LBA Lba
)
{
EFI_STATUS Status;
EFI_SWAP_ADDRESS_RANGE_PROTOCOL *SarProtocol;
EFI_PHYSICAL_ADDRESS BootBlockBase;
UINTN BootBlockSize;
EFI_PHYSICAL_ADDRESS BackupBlockBase;
UINTN BackupBlockSize;
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *BootFvb;
BOOLEAN IsSwapped;
EFI_HANDLE FvbHandle;
if (!FeaturePcdGet(PcdFullFtwServiceEnable)) {
return FALSE;
}
Status = gBS->LocateProtocol (&gEfiSwapAddressRangeProtocolGuid, NULL, (VOID **) &SarProtocol);
if (EFI_ERROR (Status)) {
return FALSE;
}
//
// Get the boot block range
//
Status = SarProtocol->GetRangeLocation (
SarProtocol,
&BootBlockBase,
&BootBlockSize,
&BackupBlockBase,
&BackupBlockSize
);
if (EFI_ERROR (Status)) {
return FALSE;
}
Status = SarProtocol->GetSwapState (SarProtocol, &IsSwapped);
if (EFI_ERROR (Status)) {
return FALSE;
}
//
// Get FVB by address
//
if (!IsSwapped) {
FvbHandle = GetFvbByAddress (BootBlockBase, &BootFvb);
} else {
FvbHandle = GetFvbByAddress (BackupBlockBase, &BootFvb);
}
if (FvbHandle == NULL) {
return FALSE;
}
//
// Compare the Fvb
//
return (BOOLEAN) (FvBlock == BootFvb);
}
/**
Copy the content of spare block to a boot block. Size is FTW_BLOCK_SIZE.
Spare block is accessed by FTW working FVB protocol interface. LBA is 1.
Target block is accessed by FvbBlock protocol interface. LBA is Lba.
FTW will do extra work on boot block update.
FTW should depend on a protocol of EFI_ADDRESS_RANGE_SWAP_PROTOCOL,
which is produced by a chipset driver.
FTW updating boot block steps may be:
1. GetRangeLocation(), if the Range is inside the boot block, FTW know
that boot block will be update. It shall add a FLAG in the working block.
2. When spare block is ready,
3. SetSwapState(EFI_SWAPPED)
4. erasing boot block,
5. programming boot block until the boot block is ok.
6. SetSwapState(UNSWAPPED)
FTW shall not allow to update boot block when battery state is error.
@param FtwDevice The private data of FTW driver
@retval EFI_SUCCESS Spare block content is copied to boot block
@retval EFI_INVALID_PARAMETER Input parameter error
@retval EFI_OUT_OF_RESOURCES Allocate memory error
@retval EFI_ABORTED The function could not complete successfully
**/
EFI_STATUS
FlushSpareBlockToBootBlock (
EFI_FTW_DEVICE *FtwDevice
)
{
EFI_STATUS Status;
UINTN Length;
UINT8 *Buffer;
UINTN Count;
UINT8 *Ptr;
UINTN Index;
BOOLEAN TopSwap;
EFI_SWAP_ADDRESS_RANGE_PROTOCOL *SarProtocol;
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *BootFvb;
EFI_LBA BootLba;
if (!FeaturePcdGet(PcdFullFtwServiceEnable)) {
return EFI_UNSUPPORTED;
}
//
// Locate swap address range protocol
//
Status = gBS->LocateProtocol (&gEfiSwapAddressRangeProtocolGuid, NULL, (VOID **) &SarProtocol);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Allocate a memory buffer
//
Length = FtwDevice->SpareAreaLength;
Buffer = AllocatePool (Length);
if (Buffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Get TopSwap bit state
//
Status = SarProtocol->GetSwapState (SarProtocol, &TopSwap);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Ftw: Get Top Swapped status - %r\n", Status));
FreePool (Buffer);
return EFI_ABORTED;
}
if (TopSwap) {
//
// Get FVB of current boot block
//
if (GetFvbByAddress (FtwDevice->SpareAreaAddress + FtwDevice->SpareAreaLength, &BootFvb) == NULL) {
FreePool (Buffer);
return EFI_ABORTED;
}
//
// Read data from current boot block
//
BootLba = 0;
Ptr = Buffer;
for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
Count = FtwDevice->BlockSize;
Status = BootFvb->Read (
BootFvb,
BootLba + Index,
0,
&Count,
Ptr
);
if (EFI_ERROR (Status)) {
FreePool (Buffer);
return Status;
}
Ptr += Count;
}
} else {
//
// Read data from spare block
//
Ptr = Buffer;
for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
Count = FtwDevice->BlockSize;
Status = FtwDevice->FtwBackupFvb->Read (
FtwDevice->FtwBackupFvb,
FtwDevice->FtwSpareLba + Index,
0,
&Count,
Ptr
);
if (EFI_ERROR (Status)) {
FreePool (Buffer);
return Status;
}
Ptr += Count;
}
//
// Set TopSwap bit
//
Status = SarProtocol->SetSwapState (SarProtocol, TRUE);
if (EFI_ERROR (Status)) {
FreePool (Buffer);
return Status;
}
}
//
// Erase current spare block
// Because TopSwap is set, this actually erase the top block (boot block)!
//
Status = FtwEraseSpareBlock (FtwDevice);
if (EFI_ERROR (Status)) {
FreePool (Buffer);
return EFI_ABORTED;
}
//
// Write memory buffer currenet spare block. Still top block.
//
Ptr = Buffer;
for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
Count = FtwDevice->BlockSize;
Status = FtwDevice->FtwBackupFvb->Write (
FtwDevice->FtwBackupFvb,
FtwDevice->FtwSpareLba + Index,
0,
&Count,
Ptr
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Ftw: FVB Write boot block - %r\n", Status));
FreePool (Buffer);
return Status;
}
Ptr += Count;
}
FreePool (Buffer);
//
// Clear TopSwap bit
//
Status = SarProtocol->SetSwapState (SarProtocol, FALSE);
return Status;
}
/**
Copy the content of spare block to a target block. Size is FTW_BLOCK_SIZE.
Spare block is accessed by FTW backup FVB protocol interface. LBA is 1.
Target block is accessed by FvbBlock protocol interface. LBA is Lba.
@param FtwDevice The private data of FTW driver
@param FvBlock FVB Protocol interface to access target block
@param Lba Lba of the target block
@retval EFI_SUCCESS Spare block content is copied to target block
@retval EFI_INVALID_PARAMETER Input parameter error
@retval EFI_OUT_OF_RESOURCES Allocate memory error
@retval EFI_ABORTED The function could not complete successfully
**/
EFI_STATUS
FlushSpareBlockToTargetBlock (
EFI_FTW_DEVICE *FtwDevice,
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
EFI_LBA Lba
)
{
EFI_STATUS Status;
UINTN Length;
UINT8 *Buffer;
UINTN Count;
UINT8 *Ptr;
UINTN Index;
if ((FtwDevice == NULL) || (FvBlock == NULL)) {
return EFI_INVALID_PARAMETER;
}
//
// Allocate a memory buffer
//
Length = FtwDevice->SpareAreaLength;
Buffer = AllocatePool (Length);
if (Buffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Read all content of spare block to memory buffer
//
Ptr = Buffer;
for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
Count = FtwDevice->BlockSize;
Status = FtwDevice->FtwBackupFvb->Read (
FtwDevice->FtwBackupFvb,
FtwDevice->FtwSpareLba + Index,
0,
&Count,
Ptr
);
if (EFI_ERROR (Status)) {
FreePool (Buffer);
return Status;
}
Ptr += Count;
}
//
// Erase the target block
//
Status = FtwEraseBlock (FtwDevice, FvBlock, Lba);
if (EFI_ERROR (Status)) {
FreePool (Buffer);
return EFI_ABORTED;
}
//
// Write memory buffer to block, using the FvbBlock protocol interface
//
Ptr = Buffer;
for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
Count = FtwDevice->BlockSize;
Status = FvBlock->Write (FvBlock, Lba + Index, 0, &Count, Ptr);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Ftw: FVB Write block - %r\n", Status));
FreePool (Buffer);
return Status;
}
Ptr += Count;
}
FreePool (Buffer);
return Status;
}
/**
Copy the content of spare block to working block. Size is FTW_BLOCK_SIZE.
Spare block is accessed by FTW backup FVB protocol interface. LBA is
FtwDevice->FtwSpareLba.
Working block is accessed by FTW working FVB protocol interface. LBA is
FtwDevice->FtwWorkBlockLba.
Since the working block header is important when FTW initializes, the
state of the operation should be handled carefully. The Crc value is
calculated without STATE element.
@param FtwDevice The private data of FTW driver
@retval EFI_SUCCESS Spare block content is copied to target block
@retval EFI_OUT_OF_RESOURCES Allocate memory error
@retval EFI_ABORTED The function could not complete successfully
**/
EFI_STATUS
FlushSpareBlockToWorkingBlock (
EFI_FTW_DEVICE *FtwDevice
)
{
EFI_STATUS Status;
UINTN Length;
UINT8 *Buffer;
EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader;
UINTN Count;
UINT8 *Ptr;
UINTN Index;
EFI_LBA WorkSpaceLbaOffset;
//
// Allocate a memory buffer
//
Length = FtwDevice->SpareAreaLength;
Buffer = AllocatePool (Length);
if (Buffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// To guarantee that the WorkingBlockValid is set on spare block
//
// Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
// WorkingBlockValid);
// To skip Signature and Crc: sizeof(EFI_GUID)+sizeof(UINT32).
//
FtwUpdateFvState (
FtwDevice->FtwBackupFvb,
FtwDevice->FtwWorkSpaceLba,
FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),
WORKING_BLOCK_VALID
);
//
// Read from spare block to memory buffer
//
Ptr = Buffer;
for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
Count = FtwDevice->BlockSize;
Status = FtwDevice->FtwBackupFvb->Read (
FtwDevice->FtwBackupFvb,
FtwDevice->FtwSpareLba + Index,
0,
&Count,
Ptr
);
if (EFI_ERROR (Status)) {
FreePool (Buffer);
return Status;
}
Ptr += Count;
}
//
// Clear the CRC and STATE, copy data from spare to working block.
//
WorkSpaceLbaOffset = FtwDevice->FtwWorkSpaceLba - FtwDevice->FtwWorkBlockLba;
WorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (Buffer + (UINTN) WorkSpaceLbaOffset * FtwDevice->BlockSize + FtwDevice->FtwWorkSpaceBase);
InitWorkSpaceHeader (WorkingBlockHeader);
WorkingBlockHeader->WorkingBlockValid = FTW_ERASE_POLARITY;
WorkingBlockHeader->WorkingBlockInvalid = FTW_ERASE_POLARITY;
//
// target block is working block, then
// Set WorkingBlockInvalid in EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
// before erase the working block.
//
// Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
// WorkingBlockInvalid);
// So hardcode offset as sizeof(EFI_GUID)+sizeof(UINT32) to
// skip Signature and Crc.
//
Status = FtwUpdateFvState (
FtwDevice->FtwFvBlock,
FtwDevice->FtwWorkSpaceLba,
FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),
WORKING_BLOCK_INVALID
);
if (EFI_ERROR (Status)) {
FreePool (Buffer);
return EFI_ABORTED;
}
FtwDevice->FtwWorkSpaceHeader->WorkingBlockInvalid = FTW_VALID_STATE;
//
// Erase the working block
//
Status = FtwEraseBlock (FtwDevice, FtwDevice->FtwFvBlock, FtwDevice->FtwWorkBlockLba);
if (EFI_ERROR (Status)) {
FreePool (Buffer);
return EFI_ABORTED;
}
//
// Write memory buffer to working block, using the FvbBlock protocol interface
//
Ptr = Buffer;
for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
Count = FtwDevice->BlockSize;
Status = FtwDevice->FtwFvBlock->Write (
FtwDevice->FtwFvBlock,
FtwDevice->FtwWorkBlockLba + Index,
0,
&Count,
Ptr
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Ftw: FVB Write block - %r\n", Status));
FreePool (Buffer);
return Status;
}
Ptr += Count;
}
//
// Since the memory buffer will not be used, free memory Buffer.
//
FreePool (Buffer);
//
// Update the VALID of the working block
//
// Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER, WorkingBlockValid);
// So hardcode offset as sizeof(EFI_GUID)+sizeof(UINT32) to skip Signature and Crc.
//
Status = FtwUpdateFvState (
FtwDevice->FtwFvBlock,
FtwDevice->FtwWorkSpaceLba,
FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),
WORKING_BLOCK_VALID
);
if (EFI_ERROR (Status)) {
return EFI_ABORTED;
}
FtwDevice->FtwWorkSpaceHeader->WorkingBlockValid = FTW_VALID_STATE;
return EFI_SUCCESS;
}
/**
Update a bit of state on a block device. The location of the bit is
calculated by the (Lba, Offset, bit). Here bit is determined by the
the name of a certain bit.
@param FvBlock FVB Protocol interface to access SrcBlock and DestBlock
@param Lba Lba of a block
@param Offset Offset on the Lba
@param NewBit New value that will override the old value if it can be change
@retval EFI_SUCCESS A state bit has been updated successfully
@retval Others Access block device error.
Notes:
Assume all bits of State are inside the same BYTE.
@retval EFI_ABORTED Read block fail
**/
EFI_STATUS
FtwUpdateFvState (
IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
IN EFI_LBA Lba,
IN UINTN Offset,
IN UINT8 NewBit
)
{
EFI_STATUS Status;
UINT8 State;
UINTN Length;
//
// Read state from device, assume State is only one byte.
//
Length = sizeof (UINT8);
Status = FvBlock->Read (FvBlock, Lba, Offset, &Length, &State);
if (EFI_ERROR (Status)) {
return EFI_ABORTED;
}
State ^= FTW_POLARITY_REVERT;
State = (UINT8) (State | NewBit);
State ^= FTW_POLARITY_REVERT;
//
// Write state back to device
//
Length = sizeof (UINT8);
Status = FvBlock->Write (FvBlock, Lba, Offset, &Length, &State);
return Status;
}
/**
Get the last Write Header pointer.
The last write header is the header whose 'complete' state hasn't been set.
After all, this header may be a EMPTY header entry for next Allocate.
@param FtwWorkSpaceHeader Pointer of the working block header
@param FtwWorkSpaceSize Size of the work space
@param FtwWriteHeader Pointer to retrieve the last write header
@retval EFI_SUCCESS Get the last write record successfully
@retval EFI_ABORTED The FTW work space is damaged
**/
EFI_STATUS
FtwGetLastWriteHeader (
IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *FtwWorkSpaceHeader,
IN UINTN FtwWorkSpaceSize,
OUT EFI_FAULT_TOLERANT_WRITE_HEADER **FtwWriteHeader
)
{
UINTN Offset;
EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader;
*FtwWriteHeader = NULL;
FtwHeader = (EFI_FAULT_TOLERANT_WRITE_HEADER *) (FtwWorkSpaceHeader + 1);
Offset = sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER);
while (FtwHeader->Complete == FTW_VALID_STATE) {
Offset += WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites, FtwHeader->PrivateDataSize);
//
// If Offset exceed the FTW work space boudary, return error.
//
if (Offset > FtwWorkSpaceSize) {
*FtwWriteHeader = FtwHeader;
return EFI_ABORTED;
}
FtwHeader = (EFI_FAULT_TOLERANT_WRITE_HEADER *) ((UINT8 *) FtwWorkSpaceHeader + Offset);
}
//
// Last write header is found
//
*FtwWriteHeader = FtwHeader;
return EFI_SUCCESS;
}
/**
Get the last Write Record pointer. The last write Record is the Record
whose DestinationCompleted state hasn't been set. After all, this Record
may be a EMPTY record entry for next write.
@param FtwWriteHeader Pointer to the write record header
@param FtwWriteRecord Pointer to retrieve the last write record
@retval EFI_SUCCESS Get the last write record successfully
@retval EFI_ABORTED The FTW work space is damaged
**/
EFI_STATUS
FtwGetLastWriteRecord (
IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwWriteHeader,
OUT EFI_FAULT_TOLERANT_WRITE_RECORD **FtwWriteRecord
)
{
UINTN Index;
EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord;
*FtwWriteRecord = NULL;
FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) (FtwWriteHeader + 1);
//
// Try to find the last write record "that has not completed"
//
for (Index = 0; Index < FtwWriteHeader->NumberOfWrites; Index += 1) {
if (FtwRecord->DestinationComplete != FTW_VALID_STATE) {
//
// The last write record is found
//
*FtwWriteRecord = FtwRecord;
return EFI_SUCCESS;
}
FtwRecord++;
if (FtwWriteHeader->PrivateDataSize != 0) {
FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) ((UINTN) FtwRecord + FtwWriteHeader->PrivateDataSize);
}
}
//
// if Index == NumberOfWrites, then
// the last record has been written successfully,
// but the Header->Complete Flag has not been set.
// also return the last record.
//
if (Index == FtwWriteHeader->NumberOfWrites) {
*FtwWriteRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) ((UINTN) FtwRecord - RECORD_SIZE (FtwWriteHeader->PrivateDataSize));
return EFI_SUCCESS;
}
return EFI_ABORTED;
}
/**
To check if FtwRecord is the first record of FtwHeader.
@param FtwHeader Pointer to the write record header
@param FtwRecord Pointer to the write record
@retval TRUE FtwRecord is the first Record of the FtwHeader
@retval FALSE FtwRecord is not the first Record of the FtwHeader
**/
BOOLEAN
IsFirstRecordOfWrites (
IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader,
IN EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord
)
{
UINT8 *Head;
UINT8 *Ptr;
Head = (UINT8 *) FtwHeader;
Ptr = (UINT8 *) FtwRecord;
Head += sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER);
return (BOOLEAN) (Head == Ptr);
}
/**
To check if FtwRecord is the last record of FtwHeader. Because the
FtwHeader has NumberOfWrites & PrivateDataSize, the FtwRecord can be
determined if it is the last record of FtwHeader.
@param FtwHeader Pointer to the write record header
@param FtwRecord Pointer to the write record
@retval TRUE FtwRecord is the last Record of the FtwHeader
@retval FALSE FtwRecord is not the last Record of the FtwHeader
**/
BOOLEAN
IsLastRecordOfWrites (
IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader,
IN EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord
)
{
UINT8 *Head;
UINT8 *Ptr;
Head = (UINT8 *) FtwHeader;
Ptr = (UINT8 *) FtwRecord;
Head += WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites - 1, FtwHeader->PrivateDataSize);
return (BOOLEAN) (Head == Ptr);
}
/**
To check if FtwRecord is the first record of FtwHeader.
@param FtwHeader Pointer to the write record header
@param FtwRecord Pointer to retrieve the previous write record
@retval EFI_ACCESS_DENIED Input record is the first record, no previous record is return.
@retval EFI_SUCCESS The previous write record is found.
**/
EFI_STATUS
GetPreviousRecordOfWrites (
IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader,
IN OUT EFI_FAULT_TOLERANT_WRITE_RECORD **FtwRecord
)
{
UINT8 *Ptr;
if (IsFirstRecordOfWrites (FtwHeader, *FtwRecord)) {
*FtwRecord = NULL;
return EFI_ACCESS_DENIED;
}
Ptr = (UINT8 *) (*FtwRecord);
Ptr -= RECORD_SIZE (FtwHeader->PrivateDataSize);
*FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) Ptr;
return EFI_SUCCESS;
}

View File

@ -0,0 +1,491 @@
/** @file
Internal functions to operate Working Block Space.
Copyright (c) 2006 - 2008, Intel Corporation
All rights reserved. This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "FaultTolerantWrite.h"
/**
Check to see if it is a valid work space.
@param WorkingHeader Pointer of working block header
@retval EFI_SUCCESS The function completed successfully
@retval EFI_ABORTED The function could not complete successfully.
**/
BOOLEAN
IsValidWorkSpace (
IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader
)
{
EFI_STATUS Status;
EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER WorkingBlockHeader;
if (WorkingHeader == NULL) {
return FALSE;
}
if (WorkingHeader->WorkingBlockValid != FTW_VALID_STATE) {
DEBUG ((EFI_D_ERROR, "Ftw: Work block header valid bit check error\n"));
return FALSE;
}
//
// Check signature with gEfiSystemNvDataFvGuid
//
if (!CompareGuid (&gEfiSystemNvDataFvGuid, &WorkingHeader->Signature)) {
DEBUG ((EFI_D_ERROR, "Ftw: Work block header signature check error\n"));
return FALSE;
}
//
// Check the CRC of header
//
CopyMem (
&WorkingBlockHeader,
WorkingHeader,
sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)
);
//
// Filter out the Crc and State fields
//
SetMem (
&WorkingBlockHeader.Crc,
sizeof (UINT32),
FTW_ERASED_BYTE
);
WorkingBlockHeader.WorkingBlockValid = FTW_ERASE_POLARITY;
WorkingBlockHeader.WorkingBlockInvalid = FTW_ERASE_POLARITY;
//
// Calculate the Crc of woking block header
//
Status = gBS->CalculateCrc32 (
(UINT8 *) &WorkingBlockHeader,
sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
&WorkingBlockHeader.Crc
);
if (EFI_ERROR (Status)) {
return FALSE;
}
if (WorkingBlockHeader.Crc != WorkingHeader->Crc) {
DEBUG ((EFI_D_ERROR, "Ftw: Work block header CRC check error\n"));
return FALSE;
}
return TRUE;
}
/**
Initialize a work space when there is no work space.
@param WorkingHeader Pointer of working block header
@retval EFI_SUCCESS The function completed successfully
@retval EFI_ABORTED The function could not complete successfully.
**/
EFI_STATUS
InitWorkSpaceHeader (
IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader
)
{
EFI_STATUS Status;
if (WorkingHeader == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Here using gEfiSystemNvDataFvGuid as the signature.
//
CopyMem (
&WorkingHeader->Signature,
&gEfiSystemNvDataFvGuid,
sizeof (EFI_GUID)
);
WorkingHeader->WriteQueueSize = (UINT64) (PcdGet32 (PcdFlashNvStorageFtwWorkingSize) - sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER));
//
// Crc is calculated with all the fields except Crc and STATE
//
WorkingHeader->WorkingBlockValid = FTW_ERASE_POLARITY;
WorkingHeader->WorkingBlockInvalid = FTW_ERASE_POLARITY;
SetMem (
&WorkingHeader->Crc,
sizeof (UINT32),
FTW_ERASED_BYTE
);
//
// Calculate the CRC value
//
Status = gBS->CalculateCrc32 (
(UINT8 *) WorkingHeader,
sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
&WorkingHeader->Crc
);
if (EFI_ERROR (Status)) {
return EFI_ABORTED;
}
//
// Restore the WorkingBlockValid flag to VALID state
//
WorkingHeader->WorkingBlockValid = FTW_VALID_STATE;
WorkingHeader->WorkingBlockInvalid = FTW_INVALID_STATE;
return EFI_SUCCESS;
}
/**
Read from working block to refresh the work space in memory.
@param FtwDevice Point to private data of FTW driver
@retval EFI_SUCCESS The function completed successfully
@retval EFI_ABORTED The function could not complete successfully.
**/
EFI_STATUS
WorkSpaceRefresh (
IN EFI_FTW_DEVICE *FtwDevice
)
{
EFI_STATUS Status;
UINTN Length;
UINTN Offset;
EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader;
//
// Initialize WorkSpace as FTW_ERASED_BYTE
//
SetMem (
FtwDevice->FtwWorkSpace,
FtwDevice->FtwWorkSpaceSize,
FTW_ERASED_BYTE
);
//
// Read from working block
//
Length = FtwDevice->FtwWorkSpaceSize;
Status = FtwDevice->FtwFvBlock->Read (
FtwDevice->FtwFvBlock,
FtwDevice->FtwWorkSpaceLba,
FtwDevice->FtwWorkSpaceBase,
&Length,
FtwDevice->FtwWorkSpace
);
if (EFI_ERROR (Status)) {
return EFI_ABORTED;
}
//
// Refresh the FtwLastWriteHeader
//
Status = FtwGetLastWriteHeader (
FtwDevice->FtwWorkSpaceHeader,
FtwDevice->FtwWorkSpaceSize,
&FtwDevice->FtwLastWriteHeader
);
FtwHeader = FtwDevice->FtwLastWriteHeader;
Offset = (UINTN) (UINT8 *) FtwHeader - (UINTN) FtwDevice->FtwWorkSpace;
//
// if the Header is out of the workspace limit, call reclaim.
//
if (EFI_ERROR (Status) && (Offset >= FtwDevice->FtwWorkSpaceSize)) {
//
// reclaim work space in working block.
//
Status = FtwReclaimWorkSpace (FtwDevice, TRUE);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Ftw: Reclaim workspace - %r\n", Status));
return EFI_ABORTED;
}
//
// Read from working block again
//
Length = FtwDevice->FtwWorkSpaceSize;
Status = FtwDevice->FtwFvBlock->Read (
FtwDevice->FtwFvBlock,
FtwDevice->FtwWorkSpaceLba,
FtwDevice->FtwWorkSpaceBase,
&Length,
FtwDevice->FtwWorkSpace
);
if (EFI_ERROR (Status)) {
return EFI_ABORTED;
}
Status = FtwGetLastWriteHeader (
FtwDevice->FtwWorkSpaceHeader,
FtwDevice->FtwWorkSpaceSize,
&FtwDevice->FtwLastWriteHeader
);
}
//
// Refresh the FtwLastWriteRecord
//
Status = FtwGetLastWriteRecord (
FtwDevice->FtwLastWriteHeader,
&FtwDevice->FtwLastWriteRecord
);
if (EFI_ERROR (Status)) {
return EFI_ABORTED;
}
return EFI_SUCCESS;
}
/**
Reclaim the work space on the working block.
@param FtwDevice Point to private data of FTW driver
@param PreserveRecord Whether to preserve the working record is needed
@retval EFI_SUCCESS The function completed successfully
@retval EFI_OUT_OF_RESOURCES Allocate memory error
@retval EFI_ABORTED The function could not complete successfully
**/
EFI_STATUS
FtwReclaimWorkSpace (
IN EFI_FTW_DEVICE *FtwDevice,
IN BOOLEAN PreserveRecord
)
{
EFI_STATUS Status;
UINTN Length;
EFI_FAULT_TOLERANT_WRITE_HEADER *Header;
UINT8 *TempBuffer;
UINTN TempBufferSize;
UINTN SpareBufferSize;
UINT8 *SpareBuffer;
EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader;
UINTN Index;
UINT8 *Ptr;
DEBUG ((EFI_D_ERROR, "Ftw: start to reclaim work space\n"));
//
// Read all original data from working block to a memory buffer
//
TempBufferSize = FtwDevice->SpareAreaLength;
TempBuffer = AllocateZeroPool (TempBufferSize);
if (TempBuffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Ptr = TempBuffer;
for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
Length = FtwDevice->BlockSize;
Status = FtwDevice->FtwFvBlock->Read (
FtwDevice->FtwFvBlock,
FtwDevice->FtwWorkBlockLba + Index,
0,
&Length,
Ptr
);
if (EFI_ERROR (Status)) {
FreePool (TempBuffer);
return EFI_ABORTED;
}
Ptr += Length;
}
//
// Clean up the workspace, remove all the completed records.
//
Ptr = TempBuffer +
((UINTN) (FtwDevice->FtwWorkSpaceLba - FtwDevice->FtwWorkBlockLba)) * FtwDevice->BlockSize +
FtwDevice->FtwWorkSpaceBase;
//
// Clear the content of buffer that will save the new work space data
//
SetMem (Ptr, FtwDevice->FtwWorkSpaceSize, FTW_ERASED_BYTE);
//
// Copy EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER to buffer
//
CopyMem (
Ptr,
FtwDevice->FtwWorkSpaceHeader,
sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)
);
if (PreserveRecord) {
//
// Get the last record following the header,
//
Status = FtwGetLastWriteHeader (
FtwDevice->FtwWorkSpaceHeader,
FtwDevice->FtwWorkSpaceSize,
&FtwDevice->FtwLastWriteHeader
);
Header = FtwDevice->FtwLastWriteHeader;
if (!EFI_ERROR (Status) && (Header != NULL) && (Header->Complete != FTW_VALID_STATE)) {
CopyMem (
Ptr + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
FtwDevice->FtwLastWriteHeader,
WRITE_TOTAL_SIZE (Header->NumberOfWrites, Header->PrivateDataSize)
);
}
}
CopyMem (
FtwDevice->FtwWorkSpace,
Ptr,
FtwDevice->FtwWorkSpaceSize
);
FtwGetLastWriteHeader (
FtwDevice->FtwWorkSpaceHeader,
FtwDevice->FtwWorkSpaceSize,
&FtwDevice->FtwLastWriteHeader
);
//
// Set the WorkingBlockValid and WorkingBlockInvalid as INVALID
//
WorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (TempBuffer + FtwDevice->FtwWorkSpaceBase);
WorkingBlockHeader->WorkingBlockValid = FTW_INVALID_STATE;
WorkingBlockHeader->WorkingBlockInvalid = FTW_INVALID_STATE;
//
// Try to keep the content of spare block
// Save spare block into a spare backup memory buffer (Sparebuffer)
//
SpareBufferSize = FtwDevice->SpareAreaLength;
SpareBuffer = AllocatePool (SpareBufferSize);
if (SpareBuffer == NULL) {
FreePool (TempBuffer);
return EFI_OUT_OF_RESOURCES;
}
Ptr = SpareBuffer;
for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
Length = FtwDevice->BlockSize;
Status = FtwDevice->FtwBackupFvb->Read (
FtwDevice->FtwBackupFvb,
FtwDevice->FtwSpareLba + Index,
0,
&Length,
Ptr
);
if (EFI_ERROR (Status)) {
FreePool (TempBuffer);
FreePool (SpareBuffer);
return EFI_ABORTED;
}
Ptr += Length;
}
//
// Write the memory buffer to spare block
//
Status = FtwEraseSpareBlock (FtwDevice);
Ptr = TempBuffer;
for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
Length = FtwDevice->BlockSize;
Status = FtwDevice->FtwBackupFvb->Write (
FtwDevice->FtwBackupFvb,
FtwDevice->FtwSpareLba + Index,
0,
&Length,
Ptr
);
if (EFI_ERROR (Status)) {
FreePool (TempBuffer);
FreePool (SpareBuffer);
return EFI_ABORTED;
}
Ptr += Length;
}
//
// Free TempBuffer
//
FreePool (TempBuffer);
//
// Set the WorkingBlockValid in spare block
//
Status = FtwUpdateFvState (
FtwDevice->FtwBackupFvb,
FtwDevice->FtwWorkSpaceLba,
FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),
WORKING_BLOCK_VALID
);
if (EFI_ERROR (Status)) {
FreePool (SpareBuffer);
return EFI_ABORTED;
}
//
// Before erase the working block, set WorkingBlockInvalid in working block.
//
// Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
// WorkingBlockInvalid);
//
Status = FtwUpdateFvState (
FtwDevice->FtwFvBlock,
FtwDevice->FtwWorkSpaceLba,
FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),
WORKING_BLOCK_INVALID
);
if (EFI_ERROR (Status)) {
FreePool (SpareBuffer);
return EFI_ABORTED;
}
FtwDevice->FtwWorkSpaceHeader->WorkingBlockInvalid = FTW_VALID_STATE;
//
// Write the spare block to working block
//
Status = FlushSpareBlockToWorkingBlock (FtwDevice);
if (EFI_ERROR (Status)) {
FreePool (SpareBuffer);
return Status;
}
//
// Restore spare backup buffer into spare block , if no failure happened during FtwWrite.
//
Status = FtwEraseSpareBlock (FtwDevice);
Ptr = SpareBuffer;
for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
Length = FtwDevice->BlockSize;
Status = FtwDevice->FtwBackupFvb->Write (
FtwDevice->FtwBackupFvb,
FtwDevice->FtwSpareLba + Index,
0,
&Length,
Ptr
);
if (EFI_ERROR (Status)) {
FreePool (SpareBuffer);
return EFI_ABORTED;
}
Ptr += Length;
}
FreePool (SpareBuffer);
DEBUG ((EFI_D_ERROR, "Ftw: reclaim work space successfully\n"));
return EFI_SUCCESS;
}