3 Internal generic functions to operate flash block.
5 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
10 #include "FaultTolerantWrite.h"
14 Check whether a flash buffer is erased.
16 @param Buffer Buffer to check
17 @param BufferSize Size of the buffer
19 @return A BOOLEAN value indicating erased or not.
34 for (Index
= 0; Index
< BufferSize
; Index
+= 1) {
35 if (*Ptr
++ != FTW_ERASED_BYTE
) {
45 To erase the block with specified blocks.
48 @param FtwDevice The private data of FTW driver
49 @param FvBlock FVB Protocol interface
50 @param Lba Lba of the firmware block
51 @param NumberOfBlocks The number of consecutive blocks starting with Lba
53 @retval EFI_SUCCESS Block LBA is Erased successfully
54 @retval Others Error occurs
59 IN EFI_FTW_DEVICE
*FtwDevice
,
60 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*FvBlock
,
65 return FvBlock
->EraseBlocks (
69 EFI_LBA_LIST_TERMINATOR
76 @param FtwDevice The private data of FTW driver
78 @retval EFI_SUCCESS The erase request was successfully completed.
79 @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state.
80 @retval EFI_DEVICE_ERROR The block device is not functioning
81 correctly and could not be written.
82 The firmware device may have been
84 @retval EFI_INVALID_PARAMETER One or more of the LBAs listed
85 in the variable argument list do
86 not exist in the firmware volume.
92 IN EFI_FTW_DEVICE
*FtwDevice
95 return FtwDevice
->FtwBackupFvb
->EraseBlocks (
96 FtwDevice
->FtwBackupFvb
,
97 FtwDevice
->FtwSpareLba
,
98 FtwDevice
->NumberOfSpareBlock
,
99 EFI_LBA_LIST_TERMINATOR
105 Is it in working block?
107 @param FtwDevice The private data of FTW driver
108 @param FvBlock Fvb protocol instance
109 @param Lba The block specified
111 @return A BOOLEAN value indicating in working block or not.
116 EFI_FTW_DEVICE
*FtwDevice
,
117 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*FvBlock
,
122 // If matching the following condition, the target block is in working block.
123 // 1. Target block is on the FV of working block (Using the same FVB protocol instance).
124 // 2. Lba falls into the range of working block.
128 (FvBlock
== FtwDevice
->FtwFvBlock
) &&
129 (Lba
>= FtwDevice
->FtwWorkBlockLba
) &&
130 (Lba
<= FtwDevice
->FtwWorkSpaceLba
)
136 Get firmware volume block by address.
139 @param Address Address specified the block
140 @param FvBlock The block caller wanted
142 @retval EFI_SUCCESS The protocol instance if found.
143 @retval EFI_NOT_FOUND Block not found
148 IN EFI_PHYSICAL_ADDRESS Address
,
149 OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
**FvBlock
153 EFI_HANDLE
*HandleBuffer
;
156 EFI_PHYSICAL_ADDRESS FvbBaseAddress
;
157 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*Fvb
;
158 EFI_HANDLE FvbHandle
;
160 UINTN NumberOfBlocks
;
166 // Locate all handles of Fvb protocol
168 Status
= GetFvbCountAndBuffer (&HandleCount
, &HandleBuffer
);
169 if (EFI_ERROR (Status
)) {
174 // Get the FVB to access variable store
176 for (Index
= 0; Index
< HandleCount
; Index
+= 1) {
177 Status
= FtwGetFvbByHandle (HandleBuffer
[Index
], &Fvb
);
178 if (EFI_ERROR (Status
)) {
183 // Compare the address and select the right one
185 Status
= Fvb
->GetPhysicalAddress (Fvb
, &FvbBaseAddress
);
186 if (EFI_ERROR (Status
)) {
191 // Now, one FVB has one type of BlockSize
193 Status
= Fvb
->GetBlockSize (Fvb
, 0, &BlockSize
, &NumberOfBlocks
);
194 if (EFI_ERROR (Status
)) {
198 if ((Address
>= FvbBaseAddress
) && (Address
< (FvbBaseAddress
+ BlockSize
* NumberOfBlocks
))) {
200 FvbHandle
= HandleBuffer
[Index
];
205 FreePool (HandleBuffer
);
213 @param FtwDevice The private data of FTW driver
214 @param FvBlock Fvb protocol instance
216 @return A BOOLEAN value indicating in boot block or not.
221 EFI_FTW_DEVICE
*FtwDevice
,
222 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*FvBlock
226 EFI_SWAP_ADDRESS_RANGE_PROTOCOL
*SarProtocol
;
227 EFI_PHYSICAL_ADDRESS BootBlockBase
;
229 EFI_PHYSICAL_ADDRESS BackupBlockBase
;
230 UINTN BackupBlockSize
;
231 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*BootFvb
;
233 EFI_HANDLE FvbHandle
;
235 if (!FeaturePcdGet (PcdFullFtwServiceEnable
)) {
239 Status
= FtwGetSarProtocol ((VOID
**)&SarProtocol
);
240 if (EFI_ERROR (Status
)) {
245 // Get the boot block range
247 Status
= SarProtocol
->GetRangeLocation (
254 if (EFI_ERROR (Status
)) {
258 Status
= SarProtocol
->GetSwapState (SarProtocol
, &IsSwapped
);
259 if (EFI_ERROR (Status
)) {
264 // Get FVB by address
267 FvbHandle
= GetFvbByAddress (BootBlockBase
, &BootFvb
);
269 FvbHandle
= GetFvbByAddress (BackupBlockBase
, &BootFvb
);
272 if (FvbHandle
== NULL
) {
279 return (BOOLEAN
)(FvBlock
== BootFvb
);
283 Copy the content of spare block to a boot block. Size is FTW_BLOCK_SIZE.
284 Spare block is accessed by FTW working FVB protocol interface.
285 Target block is accessed by FvBlock protocol interface.
287 FTW will do extra work on boot block update.
288 FTW should depend on a protocol of EFI_ADDRESS_RANGE_SWAP_PROTOCOL,
289 which is produced by a chipset driver.
290 FTW updating boot block steps may be:
291 1. GetRangeLocation(), if the Range is inside the boot block, FTW know
292 that boot block will be update. It shall add a FLAG in the working block.
293 2. When spare block is ready,
294 3. SetSwapState(SWAPPED)
295 4. erasing boot block,
296 5. programming boot block until the boot block is ok.
297 6. SetSwapState(UNSWAPPED)
298 FTW shall not allow to update boot block when battery state is error.
300 @param FtwDevice The private data of FTW driver
302 @retval EFI_SUCCESS Spare block content is copied to boot block
303 @retval EFI_INVALID_PARAMETER Input parameter error
304 @retval EFI_OUT_OF_RESOURCES Allocate memory error
305 @retval EFI_ABORTED The function could not complete successfully
309 FlushSpareBlockToBootBlock (
310 EFI_FTW_DEVICE
*FtwDevice
320 EFI_SWAP_ADDRESS_RANGE_PROTOCOL
*SarProtocol
;
321 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*BootFvb
;
324 if (!FeaturePcdGet (PcdFullFtwServiceEnable
)) {
325 return EFI_UNSUPPORTED
;
329 // Locate swap address range protocol
331 Status
= FtwGetSarProtocol ((VOID
**)&SarProtocol
);
332 if (EFI_ERROR (Status
)) {
337 // Allocate a memory buffer
339 Length
= FtwDevice
->SpareAreaLength
;
340 Buffer
= AllocatePool (Length
);
341 if (Buffer
== NULL
) {
342 return EFI_OUT_OF_RESOURCES
;
346 // Get TopSwap bit state
348 Status
= SarProtocol
->GetSwapState (SarProtocol
, &TopSwap
);
349 if (EFI_ERROR (Status
)) {
350 DEBUG ((DEBUG_ERROR
, "Ftw: Get Top Swapped status - %r\n", Status
));
357 // Get FVB of current boot block
359 if (GetFvbByAddress (FtwDevice
->SpareAreaAddress
+ FtwDevice
->SpareAreaLength
, &BootFvb
) == NULL
) {
365 // Read data from current boot block
369 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
370 Count
= FtwDevice
->SpareBlockSize
;
371 Status
= BootFvb
->Read (
378 if (EFI_ERROR (Status
)) {
387 // Read data from spare block
390 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
391 Count
= FtwDevice
->SpareBlockSize
;
392 Status
= FtwDevice
->FtwBackupFvb
->Read (
393 FtwDevice
->FtwBackupFvb
,
394 FtwDevice
->FtwSpareLba
+ Index
,
399 if (EFI_ERROR (Status
)) {
410 Status
= SarProtocol
->SetSwapState (SarProtocol
, TRUE
);
411 if (EFI_ERROR (Status
)) {
418 // Erase current spare block
419 // Because TopSwap is set, this actually erase the top block (boot block)!
421 Status
= FtwEraseSpareBlock (FtwDevice
);
422 if (EFI_ERROR (Status
)) {
428 // Write memory buffer to current spare block. Still top block.
431 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
432 Count
= FtwDevice
->SpareBlockSize
;
433 Status
= FtwDevice
->FtwBackupFvb
->Write (
434 FtwDevice
->FtwBackupFvb
,
435 FtwDevice
->FtwSpareLba
+ Index
,
440 if (EFI_ERROR (Status
)) {
441 DEBUG ((DEBUG_ERROR
, "Ftw: FVB Write boot block - %r\n", Status
));
454 Status
= SarProtocol
->SetSwapState (SarProtocol
, FALSE
);
460 Copy the content of spare block to a target block.
461 Spare block is accessed by FTW backup FVB protocol interface.
462 Target block is accessed by FvBlock protocol interface.
465 @param FtwDevice The private data of FTW driver
466 @param FvBlock FVB Protocol interface to access target block
467 @param Lba Lba of the target block
468 @param BlockSize The size of the block
469 @param NumberOfBlocks The number of consecutive blocks starting with Lba
471 @retval EFI_SUCCESS Spare block content is copied to target block
472 @retval EFI_INVALID_PARAMETER Input parameter error
473 @retval EFI_OUT_OF_RESOURCES Allocate memory error
474 @retval EFI_ABORTED The function could not complete successfully
478 FlushSpareBlockToTargetBlock (
479 EFI_FTW_DEVICE
*FtwDevice
,
480 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*FvBlock
,
493 if ((FtwDevice
== NULL
) || (FvBlock
== NULL
)) {
494 return EFI_INVALID_PARAMETER
;
498 // Allocate a memory buffer
500 Length
= FtwDevice
->SpareAreaLength
;
501 Buffer
= AllocatePool (Length
);
502 if (Buffer
== NULL
) {
503 return EFI_OUT_OF_RESOURCES
;
507 // Read all content of spare block to memory buffer
510 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
511 Count
= FtwDevice
->SpareBlockSize
;
512 Status
= FtwDevice
->FtwBackupFvb
->Read (
513 FtwDevice
->FtwBackupFvb
,
514 FtwDevice
->FtwSpareLba
+ Index
,
519 if (EFI_ERROR (Status
)) {
528 // Erase the target block
530 Status
= FtwEraseBlock (FtwDevice
, FvBlock
, Lba
, NumberOfBlocks
);
531 if (EFI_ERROR (Status
)) {
537 // Write memory buffer to block, using the FvBlock protocol interface
540 for (Index
= 0; Index
< NumberOfBlocks
; Index
+= 1) {
542 Status
= FvBlock
->Write (FvBlock
, Lba
+ Index
, 0, &Count
, Ptr
);
543 if (EFI_ERROR (Status
)) {
544 DEBUG ((DEBUG_ERROR
, "Ftw: FVB Write block - %r\n", Status
));
558 Copy the content of spare block to working block. Size is FTW_BLOCK_SIZE.
559 Spare block is accessed by FTW backup FVB protocol interface. LBA is
560 FtwDevice->FtwSpareLba.
561 Working block is accessed by FTW working FVB protocol interface. LBA is
562 FtwDevice->FtwWorkBlockLba.
564 Since the working block header is important when FTW initializes, the
565 state of the operation should be handled carefully. The Crc value is
566 calculated without STATE element.
568 @param FtwDevice The private data of FTW driver
570 @retval EFI_SUCCESS Spare block content is copied to target block
571 @retval EFI_OUT_OF_RESOURCES Allocate memory error
572 @retval EFI_ABORTED The function could not complete successfully
576 FlushSpareBlockToWorkingBlock (
577 EFI_FTW_DEVICE
*FtwDevice
583 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*WorkingBlockHeader
;
589 // Allocate a memory buffer
591 Length
= FtwDevice
->SpareAreaLength
;
592 Buffer
= AllocatePool (Length
);
593 if (Buffer
== NULL
) {
594 return EFI_OUT_OF_RESOURCES
;
598 // To guarantee that the WorkingBlockValid is set on spare block
600 // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
601 // WorkingBlockValid);
602 // To skip Signature and Crc: sizeof(EFI_GUID)+sizeof(UINT32).
605 FtwDevice
->FtwBackupFvb
,
606 FtwDevice
->SpareBlockSize
,
607 FtwDevice
->FtwSpareLba
+ FtwDevice
->FtwWorkSpaceLbaInSpare
,
608 FtwDevice
->FtwWorkSpaceBaseInSpare
+ sizeof (EFI_GUID
) + sizeof (UINT32
),
612 // Read from spare block to memory buffer
615 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
616 Count
= FtwDevice
->SpareBlockSize
;
617 Status
= FtwDevice
->FtwBackupFvb
->Read (
618 FtwDevice
->FtwBackupFvb
,
619 FtwDevice
->FtwSpareLba
+ Index
,
624 if (EFI_ERROR (Status
)) {
633 // Clear the CRC and STATE, copy data from spare to working block.
635 WorkingBlockHeader
= (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*)(Buffer
+ (UINTN
)FtwDevice
->FtwWorkSpaceLbaInSpare
* FtwDevice
->SpareBlockSize
+ FtwDevice
->FtwWorkSpaceBaseInSpare
);
636 InitWorkSpaceHeader (WorkingBlockHeader
);
637 WorkingBlockHeader
->WorkingBlockValid
= FTW_ERASE_POLARITY
;
638 WorkingBlockHeader
->WorkingBlockInvalid
= FTW_ERASE_POLARITY
;
641 // target block is working block, then
642 // Set WorkingBlockInvalid in EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
643 // before erase the working block.
645 // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
646 // WorkingBlockInvalid);
647 // So hardcode offset as sizeof(EFI_GUID)+sizeof(UINT32) to
648 // skip Signature and Crc.
650 Status
= FtwUpdateFvState (
651 FtwDevice
->FtwFvBlock
,
652 FtwDevice
->WorkBlockSize
,
653 FtwDevice
->FtwWorkSpaceLba
,
654 FtwDevice
->FtwWorkSpaceBase
+ sizeof (EFI_GUID
) + sizeof (UINT32
),
655 WORKING_BLOCK_INVALID
657 if (EFI_ERROR (Status
)) {
662 FtwDevice
->FtwWorkSpaceHeader
->WorkingBlockInvalid
= FTW_VALID_STATE
;
665 // Erase the working block
667 Status
= FtwEraseBlock (FtwDevice
, FtwDevice
->FtwFvBlock
, FtwDevice
->FtwWorkBlockLba
, FtwDevice
->NumberOfWorkBlock
);
668 if (EFI_ERROR (Status
)) {
674 // Write memory buffer to working block, using the FvBlock protocol interface
677 for (Index
= 0; Index
< FtwDevice
->NumberOfWorkBlock
; Index
+= 1) {
678 Count
= FtwDevice
->WorkBlockSize
;
679 Status
= FtwDevice
->FtwFvBlock
->Write (
680 FtwDevice
->FtwFvBlock
,
681 FtwDevice
->FtwWorkBlockLba
+ Index
,
686 if (EFI_ERROR (Status
)) {
687 DEBUG ((DEBUG_ERROR
, "Ftw: FVB Write block - %r\n", Status
));
696 // Since the memory buffer will not be used, free memory Buffer.
701 // Update the VALID of the working block
703 // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER, WorkingBlockValid);
704 // So hardcode offset as sizeof(EFI_GUID)+sizeof(UINT32) to skip Signature and Crc.
706 Status
= FtwUpdateFvState (
707 FtwDevice
->FtwFvBlock
,
708 FtwDevice
->WorkBlockSize
,
709 FtwDevice
->FtwWorkSpaceLba
,
710 FtwDevice
->FtwWorkSpaceBase
+ sizeof (EFI_GUID
) + sizeof (UINT32
),
713 if (EFI_ERROR (Status
)) {
717 FtwDevice
->FtwWorkSpaceHeader
->WorkingBlockInvalid
= FTW_INVALID_STATE
;
718 FtwDevice
->FtwWorkSpaceHeader
->WorkingBlockValid
= FTW_VALID_STATE
;
724 Update a bit of state on a block device. The location of the bit is
725 calculated by the (Lba, Offset, bit). Here bit is determined by the
726 the name of a certain bit.
729 @param FvBlock FVB Protocol interface to access SrcBlock and DestBlock
730 @param BlockSize The size of the block
731 @param Lba Lba of a block
732 @param Offset Offset on the Lba
733 @param NewBit New value that will override the old value if it can be change
735 @retval EFI_SUCCESS A state bit has been updated successfully
736 @retval Others Access block device error.
738 Assume all bits of State are inside the same BYTE.
739 @retval EFI_ABORTED Read block fail
744 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*FvBlock
,
756 // Calculate the real Offset and Lba to write.
758 while (Offset
>= BlockSize
) {
764 // Read state from device, assume State is only one byte.
766 Length
= sizeof (UINT8
);
767 Status
= FvBlock
->Read (FvBlock
, Lba
, Offset
, &Length
, &State
);
768 if (EFI_ERROR (Status
)) {
772 State
^= FTW_POLARITY_REVERT
;
773 State
= (UINT8
)(State
| NewBit
);
774 State
^= FTW_POLARITY_REVERT
;
777 // Write state back to device
779 Length
= sizeof (UINT8
);
780 Status
= FvBlock
->Write (FvBlock
, Lba
, Offset
, &Length
, &State
);
786 Get the last Write Header pointer.
787 The last write header is the header whose 'complete' state hasn't been set.
788 After all, this header may be a EMPTY header entry for next Allocate.
791 @param FtwWorkSpaceHeader Pointer of the working block header
792 @param FtwWorkSpaceSize Size of the work space
793 @param FtwWriteHeader Pointer to retrieve the last write header
795 @retval EFI_SUCCESS Get the last write record successfully
796 @retval EFI_ABORTED The FTW work space is damaged
800 FtwGetLastWriteHeader (
801 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*FtwWorkSpaceHeader
,
802 IN UINTN FtwWorkSpaceSize
,
803 OUT EFI_FAULT_TOLERANT_WRITE_HEADER
**FtwWriteHeader
807 EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwHeader
;
809 *FtwWriteHeader
= NULL
;
810 FtwHeader
= (EFI_FAULT_TOLERANT_WRITE_HEADER
*)(FtwWorkSpaceHeader
+ 1);
811 Offset
= sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
);
813 while (FtwHeader
->Complete
== FTW_VALID_STATE
) {
814 Offset
+= FTW_WRITE_TOTAL_SIZE (FtwHeader
->NumberOfWrites
, FtwHeader
->PrivateDataSize
);
816 // If Offset exceed the FTW work space boudary, return error.
818 if (Offset
>= FtwWorkSpaceSize
) {
819 *FtwWriteHeader
= FtwHeader
;
823 FtwHeader
= (EFI_FAULT_TOLERANT_WRITE_HEADER
*)((UINT8
*)FtwWorkSpaceHeader
+ Offset
);
827 // Last write header is found
829 *FtwWriteHeader
= FtwHeader
;
835 Get the last Write Record pointer. The last write Record is the Record
836 whose DestinationCompleted state hasn't been set. After all, this Record
837 may be a EMPTY record entry for next write.
840 @param FtwWriteHeader Pointer to the write record header
841 @param FtwWriteRecord Pointer to retrieve the last write record
843 @retval EFI_SUCCESS Get the last write record successfully
844 @retval EFI_ABORTED The FTW work space is damaged
848 FtwGetLastWriteRecord (
849 IN EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwWriteHeader
,
850 OUT EFI_FAULT_TOLERANT_WRITE_RECORD
**FtwWriteRecord
854 EFI_FAULT_TOLERANT_WRITE_RECORD
*FtwRecord
;
856 *FtwWriteRecord
= NULL
;
857 FtwRecord
= (EFI_FAULT_TOLERANT_WRITE_RECORD
*)(FtwWriteHeader
+ 1);
860 // Try to find the last write record "that has not completed"
862 for (Index
= 0; Index
< FtwWriteHeader
->NumberOfWrites
; Index
+= 1) {
863 if (FtwRecord
->DestinationComplete
!= FTW_VALID_STATE
) {
865 // The last write record is found
867 *FtwWriteRecord
= FtwRecord
;
873 if (FtwWriteHeader
->PrivateDataSize
!= 0) {
874 FtwRecord
= (EFI_FAULT_TOLERANT_WRITE_RECORD
*)((UINTN
)FtwRecord
+ (UINTN
)FtwWriteHeader
->PrivateDataSize
);
879 // if Index == NumberOfWrites, then
880 // the last record has been written successfully,
881 // but the Header->Complete Flag has not been set.
882 // also return the last record.
884 if (Index
== FtwWriteHeader
->NumberOfWrites
) {
885 *FtwWriteRecord
= (EFI_FAULT_TOLERANT_WRITE_RECORD
*)((UINTN
)FtwRecord
- FTW_RECORD_SIZE (FtwWriteHeader
->PrivateDataSize
));
893 To check if FtwRecord is the first record of FtwHeader.
895 @param FtwHeader Pointer to the write record header
896 @param FtwRecord Pointer to the write record
898 @retval TRUE FtwRecord is the first Record of the FtwHeader
899 @retval FALSE FtwRecord is not the first Record of the FtwHeader
903 IsFirstRecordOfWrites (
904 IN EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwHeader
,
905 IN EFI_FAULT_TOLERANT_WRITE_RECORD
*FtwRecord
911 Head
= (UINT8
*)FtwHeader
;
912 Ptr
= (UINT8
*)FtwRecord
;
914 Head
+= sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER
);
915 return (BOOLEAN
)(Head
== Ptr
);
919 To check if FtwRecord is the last record of FtwHeader. Because the
920 FtwHeader has NumberOfWrites & PrivateDataSize, the FtwRecord can be
921 determined if it is the last record of FtwHeader.
923 @param FtwHeader Pointer to the write record header
924 @param FtwRecord Pointer to the write record
926 @retval TRUE FtwRecord is the last Record of the FtwHeader
927 @retval FALSE FtwRecord is not the last Record of the FtwHeader
931 IsLastRecordOfWrites (
932 IN EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwHeader
,
933 IN EFI_FAULT_TOLERANT_WRITE_RECORD
*FtwRecord
939 Head
= (UINT8
*)FtwHeader
;
940 Ptr
= (UINT8
*)FtwRecord
;
942 Head
+= FTW_WRITE_TOTAL_SIZE (FtwHeader
->NumberOfWrites
- 1, FtwHeader
->PrivateDataSize
);
943 return (BOOLEAN
)(Head
== Ptr
);
947 To check if FtwRecord is the first record of FtwHeader.
949 @param FtwHeader Pointer to the write record header
950 @param FtwRecord Pointer to retrieve the previous write record
952 @retval EFI_ACCESS_DENIED Input record is the first record, no previous record is return.
953 @retval EFI_SUCCESS The previous write record is found.
957 GetPreviousRecordOfWrites (
958 IN EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwHeader
,
959 IN OUT EFI_FAULT_TOLERANT_WRITE_RECORD
**FtwRecord
964 if (IsFirstRecordOfWrites (FtwHeader
, *FtwRecord
)) {
966 return EFI_ACCESS_DENIED
;
969 Ptr
= (UINT8
*)(*FtwRecord
);
970 Ptr
-= FTW_RECORD_SIZE (FtwHeader
->PrivateDataSize
);
971 *FtwRecord
= (EFI_FAULT_TOLERANT_WRITE_RECORD
*)Ptr
;
976 Allocate private data for FTW driver and initialize it.
978 @param[out] FtwData Pointer to the FTW device structure
980 @retval EFI_SUCCESS Initialize the FTW device successfully.
981 @retval EFI_OUT_OF_RESOURCES Allocate memory error
982 @retval EFI_INVALID_PARAMETER Workspace or Spare block does not exist
987 OUT EFI_FTW_DEVICE
**FtwData
990 EFI_FTW_DEVICE
*FtwDevice
;
993 // Allocate private data of this driver,
994 // Including the FtwWorkSpace[FTW_WORK_SPACE_SIZE].
996 FtwDevice
= AllocateZeroPool (sizeof (EFI_FTW_DEVICE
) + PcdGet32 (PcdFlashNvStorageFtwWorkingSize
));
997 if (FtwDevice
== NULL
) {
998 return EFI_OUT_OF_RESOURCES
;
1002 // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.
1004 FtwDevice
->WorkSpaceLength
= (UINTN
)PcdGet32 (PcdFlashNvStorageFtwWorkingSize
);
1005 FtwDevice
->SpareAreaLength
= (UINTN
)PcdGet32 (PcdFlashNvStorageFtwSpareSize
);
1006 if ((FtwDevice
->WorkSpaceLength
== 0) || (FtwDevice
->SpareAreaLength
== 0)) {
1007 DEBUG ((DEBUG_ERROR
, "Ftw: Workspace or Spare block does not exist!\n"));
1008 FreePool (FtwDevice
);
1009 return EFI_INVALID_PARAMETER
;
1012 FtwDevice
->Signature
= FTW_DEVICE_SIGNATURE
;
1013 FtwDevice
->FtwFvBlock
= NULL
;
1014 FtwDevice
->FtwBackupFvb
= NULL
;
1015 FtwDevice
->FtwWorkSpaceLba
= (EFI_LBA
)(-1);
1016 FtwDevice
->FtwSpareLba
= (EFI_LBA
)(-1);
1018 FtwDevice
->WorkSpaceAddress
= (EFI_PHYSICAL_ADDRESS
)PcdGet64 (PcdFlashNvStorageFtwWorkingBase64
);
1019 if (FtwDevice
->WorkSpaceAddress
== 0) {
1020 FtwDevice
->WorkSpaceAddress
= (EFI_PHYSICAL_ADDRESS
)PcdGet32 (PcdFlashNvStorageFtwWorkingBase
);
1023 FtwDevice
->SpareAreaAddress
= (EFI_PHYSICAL_ADDRESS
)PcdGet64 (PcdFlashNvStorageFtwSpareBase64
);
1024 if (FtwDevice
->SpareAreaAddress
== 0) {
1025 FtwDevice
->SpareAreaAddress
= (EFI_PHYSICAL_ADDRESS
)PcdGet32 (PcdFlashNvStorageFtwSpareBase
);
1028 *FtwData
= FtwDevice
;
1033 Find the proper Firmware Volume Block protocol for FTW operation.
1035 @param[in, out] FtwDevice Pointer to the FTW device structure
1037 @retval EFI_SUCCESS Find the FVB protocol successfully.
1038 @retval EFI_NOT_FOUND No proper FVB protocol was found.
1039 @retval EFI_ABORTED Some data can not be got or be invalid.
1044 IN OUT EFI_FTW_DEVICE
*FtwDevice
1048 EFI_HANDLE
*HandleBuffer
;
1051 EFI_PHYSICAL_ADDRESS FvbBaseAddress
;
1052 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*Fvb
;
1053 EFI_FVB_ATTRIBUTES_2 Attributes
;
1056 UINTN NumberOfBlocks
;
1058 HandleBuffer
= NULL
;
1061 // Get all FVB handle.
1063 Status
= GetFvbCountAndBuffer (&HandleCount
, &HandleBuffer
);
1064 if (EFI_ERROR (Status
)) {
1065 return EFI_NOT_FOUND
;
1069 // Get the FVB to access variable store
1072 for (Index
= 0; Index
< HandleCount
; Index
+= 1) {
1073 Status
= FtwGetFvbByHandle (HandleBuffer
[Index
], &Fvb
);
1074 if (EFI_ERROR (Status
)) {
1075 Status
= EFI_NOT_FOUND
;
1080 // Ensure this FVB protocol support Write operation.
1082 Status
= Fvb
->GetAttributes (Fvb
, &Attributes
);
1083 if (EFI_ERROR (Status
) || ((Attributes
& EFI_FVB2_WRITE_STATUS
) == 0)) {
1088 // Compare the address and select the right one
1090 Status
= Fvb
->GetPhysicalAddress (Fvb
, &FvbBaseAddress
);
1091 if (EFI_ERROR (Status
)) {
1096 // Now, one FVB has one type of BlockSize.
1098 Status
= Fvb
->GetBlockSize (Fvb
, 0, &BlockSize
, &NumberOfBlocks
);
1099 if (EFI_ERROR (Status
)) {
1103 if ((FtwDevice
->FtwFvBlock
== NULL
) && (FtwDevice
->WorkSpaceAddress
>= FvbBaseAddress
) &&
1104 ((FtwDevice
->WorkSpaceAddress
+ FtwDevice
->WorkSpaceLength
) <= (FvbBaseAddress
+ BlockSize
* NumberOfBlocks
)))
1106 FtwDevice
->FtwFvBlock
= Fvb
;
1108 // To get the LBA of work space
1110 for (LbaIndex
= 1; LbaIndex
<= NumberOfBlocks
; LbaIndex
+= 1) {
1111 if ( (FtwDevice
->WorkSpaceAddress
>= (FvbBaseAddress
+ BlockSize
* (LbaIndex
- 1)))
1112 && (FtwDevice
->WorkSpaceAddress
< (FvbBaseAddress
+ BlockSize
* LbaIndex
)))
1114 FtwDevice
->FtwWorkSpaceLba
= LbaIndex
- 1;
1116 // Get the Work space size and Base(Offset)
1118 FtwDevice
->FtwWorkSpaceSize
= FtwDevice
->WorkSpaceLength
;
1119 FtwDevice
->WorkBlockSize
= BlockSize
;
1120 FtwDevice
->FtwWorkSpaceBase
= (UINTN
)(FtwDevice
->WorkSpaceAddress
- (FvbBaseAddress
+ FtwDevice
->WorkBlockSize
* (LbaIndex
- 1)));
1121 FtwDevice
->NumberOfWorkSpaceBlock
= FTW_BLOCKS (FtwDevice
->FtwWorkSpaceBase
+ FtwDevice
->FtwWorkSpaceSize
, FtwDevice
->WorkBlockSize
);
1122 if (FtwDevice
->FtwWorkSpaceSize
>= FtwDevice
->WorkBlockSize
) {
1124 // Check the alignment of work space address and length, they should be block size aligned when work space size is larger than one block size.
1126 if (((FtwDevice
->WorkSpaceAddress
& (FtwDevice
->WorkBlockSize
- 1)) != 0) ||
1127 ((FtwDevice
->WorkSpaceLength
& (FtwDevice
->WorkBlockSize
- 1)) != 0))
1129 DEBUG ((DEBUG_ERROR
, "Ftw: Work space address or length is not block size aligned when work space size is larger than one block size\n"));
1130 FreePool (HandleBuffer
);
1134 } else if ((FtwDevice
->FtwWorkSpaceBase
+ FtwDevice
->FtwWorkSpaceSize
) > FtwDevice
->WorkBlockSize
) {
1135 DEBUG ((DEBUG_ERROR
, "Ftw: The work space range should not span blocks when work space size is less than one block size\n"));
1136 FreePool (HandleBuffer
);
1146 if ((FtwDevice
->FtwBackupFvb
== NULL
) && (FtwDevice
->SpareAreaAddress
>= FvbBaseAddress
) &&
1147 ((FtwDevice
->SpareAreaAddress
+ FtwDevice
->SpareAreaLength
) <= (FvbBaseAddress
+ BlockSize
* NumberOfBlocks
)))
1149 FtwDevice
->FtwBackupFvb
= Fvb
;
1151 // To get the LBA of spare
1153 for (LbaIndex
= 1; LbaIndex
<= NumberOfBlocks
; LbaIndex
+= 1) {
1154 if ( (FtwDevice
->SpareAreaAddress
>= (FvbBaseAddress
+ BlockSize
* (LbaIndex
- 1)))
1155 && (FtwDevice
->SpareAreaAddress
< (FvbBaseAddress
+ BlockSize
* LbaIndex
)))
1158 // Get the NumberOfSpareBlock and BlockSize
1160 FtwDevice
->FtwSpareLba
= LbaIndex
- 1;
1161 FtwDevice
->SpareBlockSize
= BlockSize
;
1162 FtwDevice
->NumberOfSpareBlock
= FtwDevice
->SpareAreaLength
/ FtwDevice
->SpareBlockSize
;
1164 // Check the range of spare area to make sure that it's in FV range
1166 if ((FtwDevice
->FtwSpareLba
+ FtwDevice
->NumberOfSpareBlock
) > NumberOfBlocks
) {
1167 DEBUG ((DEBUG_ERROR
, "Ftw: Spare area is out of FV range\n"));
1168 FreePool (HandleBuffer
);
1174 // Check the alignment of spare area address and length, they should be block size aligned
1176 if (((FtwDevice
->SpareAreaAddress
& (FtwDevice
->SpareBlockSize
- 1)) != 0) ||
1177 ((FtwDevice
->SpareAreaLength
& (FtwDevice
->SpareBlockSize
- 1)) != 0))
1179 DEBUG ((DEBUG_ERROR
, "Ftw: Spare area address or length is not block size aligned\n"));
1180 FreePool (HandleBuffer
);
1182 // Report Status Code EFI_SW_EC_ABORTED.
1184 REPORT_STATUS_CODE ((EFI_ERROR_CODE
| EFI_ERROR_UNRECOVERED
), (EFI_SOFTWARE_DXE_BS_DRIVER
| EFI_SW_EC_ABORTED
));
1195 FreePool (HandleBuffer
);
1197 if ((FtwDevice
->FtwBackupFvb
== NULL
) || (FtwDevice
->FtwFvBlock
== NULL
) ||
1198 (FtwDevice
->FtwWorkSpaceLba
== (EFI_LBA
)(-1)) || (FtwDevice
->FtwSpareLba
== (EFI_LBA
)(-1)))
1203 DEBUG ((DEBUG_INFO
, "Ftw: FtwWorkSpaceLba - 0x%lx, WorkBlockSize - 0x%x, FtwWorkSpaceBase - 0x%x\n", FtwDevice
->FtwWorkSpaceLba
, FtwDevice
->WorkBlockSize
, FtwDevice
->FtwWorkSpaceBase
));
1204 DEBUG ((DEBUG_INFO
, "Ftw: FtwSpareLba - 0x%lx, SpareBlockSize - 0x%x\n", FtwDevice
->FtwSpareLba
, FtwDevice
->SpareBlockSize
));
1210 Initialization for Fault Tolerant Write protocol.
1212 @param[in, out] FtwDevice Pointer to the FTW device structure
1214 @retval EFI_SUCCESS Initialize the FTW protocol successfully.
1215 @retval EFI_NOT_FOUND No proper FVB protocol was found.
1220 IN OUT EFI_FTW_DEVICE
*FtwDevice
1224 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*Fvb
;
1225 EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwHeader
;
1227 EFI_HANDLE FvbHandle
;
1228 EFI_LBA WorkSpaceLbaOffset
;
1231 // Find the right SMM Fvb protocol instance for FTW.
1233 Status
= FindFvbForFtw (FtwDevice
);
1234 if (EFI_ERROR (Status
)) {
1235 return EFI_NOT_FOUND
;
1239 // Calculate the start LBA of working block.
1241 if (FtwDevice
->FtwWorkSpaceSize
>= FtwDevice
->WorkBlockSize
) {
1243 // Working block is a standalone area which only contains working space.
1245 FtwDevice
->NumberOfWorkBlock
= FtwDevice
->NumberOfWorkSpaceBlock
;
1248 // Working block is an area which
1249 // contains working space in its last block and has the same size as spare
1250 // block, unless there are not enough blocks before the block that contains
1253 FtwDevice
->NumberOfWorkBlock
= (UINTN
)(FtwDevice
->FtwWorkSpaceLba
+ FtwDevice
->NumberOfWorkSpaceBlock
);
1254 while (FtwDevice
->NumberOfWorkBlock
* FtwDevice
->WorkBlockSize
> FtwDevice
->SpareAreaLength
) {
1255 FtwDevice
->NumberOfWorkBlock
--;
1259 FtwDevice
->FtwWorkBlockLba
= FtwDevice
->FtwWorkSpaceLba
+ FtwDevice
->NumberOfWorkSpaceBlock
- FtwDevice
->NumberOfWorkBlock
;
1260 DEBUG ((DEBUG_INFO
, "Ftw: NumberOfWorkBlock - 0x%x, FtwWorkBlockLba - 0x%lx\n", FtwDevice
->NumberOfWorkBlock
, FtwDevice
->FtwWorkBlockLba
));
1263 // Calcualte the LBA and base of work space in spare block.
1264 // Note: Do not assume Spare Block and Work Block have same block size.
1266 WorkSpaceLbaOffset
= FtwDevice
->FtwWorkSpaceLba
- FtwDevice
->FtwWorkBlockLba
;
1267 FtwDevice
->FtwWorkSpaceLbaInSpare
= (EFI_LBA
)(((UINTN
)WorkSpaceLbaOffset
* FtwDevice
->WorkBlockSize
+ FtwDevice
->FtwWorkSpaceBase
) / FtwDevice
->SpareBlockSize
);
1268 FtwDevice
->FtwWorkSpaceBaseInSpare
= ((UINTN
)WorkSpaceLbaOffset
* FtwDevice
->WorkBlockSize
+ FtwDevice
->FtwWorkSpaceBase
) % FtwDevice
->SpareBlockSize
;
1269 DEBUG ((DEBUG_INFO
, "Ftw: WorkSpaceLbaInSpare - 0x%lx, WorkSpaceBaseInSpare - 0x%x\n", FtwDevice
->FtwWorkSpaceLbaInSpare
, FtwDevice
->FtwWorkSpaceBaseInSpare
));
1272 // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.
1274 FtwDevice
->FtwWorkSpace
= (UINT8
*)(FtwDevice
+ 1);
1275 FtwDevice
->FtwWorkSpaceHeader
= (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*)FtwDevice
->FtwWorkSpace
;
1277 FtwDevice
->FtwLastWriteHeader
= NULL
;
1278 FtwDevice
->FtwLastWriteRecord
= NULL
;
1280 InitializeLocalWorkSpaceHeader ();
1283 // Refresh the working space data from working block
1285 Status
= WorkSpaceRefresh (FtwDevice
);
1286 ASSERT_EFI_ERROR (Status
);
1288 // If the working block workspace is not valid, try the spare block
1290 if (!IsValidWorkSpace (FtwDevice
->FtwWorkSpaceHeader
)) {
1292 // Read from spare block
1294 Status
= ReadWorkSpaceData (
1295 FtwDevice
->FtwBackupFvb
,
1296 FtwDevice
->SpareBlockSize
,
1297 FtwDevice
->FtwSpareLba
+ FtwDevice
->FtwWorkSpaceLbaInSpare
,
1298 FtwDevice
->FtwWorkSpaceBaseInSpare
,
1299 FtwDevice
->FtwWorkSpaceSize
,
1300 FtwDevice
->FtwWorkSpace
1302 ASSERT_EFI_ERROR (Status
);
1305 // If spare block is valid, then replace working block content.
1307 if (IsValidWorkSpace (FtwDevice
->FtwWorkSpaceHeader
)) {
1308 Status
= FlushSpareBlockToWorkingBlock (FtwDevice
);
1311 "Ftw: Restart working block update in %a() - %r\n",
1315 FtwAbort (&FtwDevice
->FtwInstance
);
1317 // Refresh work space.
1319 Status
= WorkSpaceRefresh (FtwDevice
);
1320 ASSERT_EFI_ERROR (Status
);
1324 "Ftw: Both working and spare blocks are invalid, init workspace\n"
1327 // If both are invalid, then initialize work space.
1330 FtwDevice
->FtwWorkSpace
,
1331 FtwDevice
->FtwWorkSpaceSize
,
1334 InitWorkSpaceHeader (FtwDevice
->FtwWorkSpaceHeader
);
1336 // Initialize the work space
1338 Status
= FtwReclaimWorkSpace (FtwDevice
, FALSE
);
1339 ASSERT_EFI_ERROR (Status
);
1344 // If the FtwDevice->FtwLastWriteRecord is 1st record of write header &&
1345 // (! SpareComplete) THEN call Abort().
1347 if ((FtwDevice
->FtwLastWriteHeader
->HeaderAllocated
== FTW_VALID_STATE
) &&
1348 (FtwDevice
->FtwLastWriteRecord
->SpareComplete
!= FTW_VALID_STATE
) &&
1349 IsFirstRecordOfWrites (FtwDevice
->FtwLastWriteHeader
, FtwDevice
->FtwLastWriteRecord
)
1352 DEBUG ((DEBUG_ERROR
, "Ftw: Init.. find first record not SpareCompleted, abort()\n"));
1353 FtwAbort (&FtwDevice
->FtwInstance
);
1357 // If Header is incompleted and the last record has completed, then
1358 // call Abort() to set the Header->Complete FLAG.
1360 if ((FtwDevice
->FtwLastWriteHeader
->Complete
!= FTW_VALID_STATE
) &&
1361 (FtwDevice
->FtwLastWriteRecord
->DestinationComplete
== FTW_VALID_STATE
) &&
1362 IsLastRecordOfWrites (FtwDevice
->FtwLastWriteHeader
, FtwDevice
->FtwLastWriteRecord
)
1365 DEBUG ((DEBUG_ERROR
, "Ftw: Init.. find last record completed but header not, abort()\n"));
1366 FtwAbort (&FtwDevice
->FtwInstance
);
1370 // To check the workspace buffer following last Write header/records is EMPTY or not.
1371 // If it's not EMPTY, FTW also need to call reclaim().
1373 FtwHeader
= FtwDevice
->FtwLastWriteHeader
;
1374 Offset
= (UINT8
*)FtwHeader
- FtwDevice
->FtwWorkSpace
;
1375 if (FtwDevice
->FtwWorkSpace
[Offset
] != FTW_ERASED_BYTE
) {
1376 Offset
+= FTW_WRITE_TOTAL_SIZE (FtwHeader
->NumberOfWrites
, FtwHeader
->PrivateDataSize
);
1379 if (!IsErasedFlashBuffer (FtwDevice
->FtwWorkSpace
+ Offset
, FtwDevice
->FtwWorkSpaceSize
- Offset
)) {
1380 Status
= FtwReclaimWorkSpace (FtwDevice
, TRUE
);
1381 ASSERT_EFI_ERROR (Status
);
1385 // Restart if it's boot block
1387 if ((FtwDevice
->FtwLastWriteHeader
->Complete
!= FTW_VALID_STATE
) &&
1388 (FtwDevice
->FtwLastWriteRecord
->SpareComplete
== FTW_VALID_STATE
)
1391 if (FtwDevice
->FtwLastWriteRecord
->BootBlockUpdate
== FTW_VALID_STATE
) {
1392 Status
= FlushSpareBlockToBootBlock (FtwDevice
);
1393 DEBUG ((DEBUG_ERROR
, "Ftw: Restart boot block update - %r\n", Status
));
1394 ASSERT_EFI_ERROR (Status
);
1395 FtwAbort (&FtwDevice
->FtwInstance
);
1398 // if (SpareCompleted) THEN Restart to fault tolerant write.
1401 FvbHandle
= GetFvbByAddress ((EFI_PHYSICAL_ADDRESS
)(UINTN
)((INT64
)FtwDevice
->SpareAreaAddress
+ FtwDevice
->FtwLastWriteRecord
->RelativeOffset
), &Fvb
);
1402 if (FvbHandle
!= NULL
) {
1403 Status
= FtwRestart (&FtwDevice
->FtwInstance
, FvbHandle
);
1404 DEBUG ((DEBUG_ERROR
, "Ftw: Restart last write - %r\n", Status
));
1405 ASSERT_EFI_ERROR (Status
);
1408 FtwAbort (&FtwDevice
->FtwInstance
);
1413 // Hook the protocol API
1415 FtwDevice
->FtwInstance
.GetMaxBlockSize
= FtwGetMaxBlockSize
;
1416 FtwDevice
->FtwInstance
.Allocate
= FtwAllocate
;
1417 FtwDevice
->FtwInstance
.Write
= FtwWrite
;
1418 FtwDevice
->FtwInstance
.Restart
= FtwRestart
;
1419 FtwDevice
->FtwInstance
.Abort
= FtwAbort
;
1420 FtwDevice
->FtwInstance
.GetLastWrite
= FtwGetLastWrite
;