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
)) {
173 // Get the FVB to access variable store
175 for (Index
= 0; Index
< HandleCount
; Index
+= 1) {
176 Status
= FtwGetFvbByHandle (HandleBuffer
[Index
], &Fvb
);
177 if (EFI_ERROR (Status
)) {
181 // Compare the address and select the right one
183 Status
= Fvb
->GetPhysicalAddress (Fvb
, &FvbBaseAddress
);
184 if (EFI_ERROR (Status
)) {
189 // Now, one FVB has one type of BlockSize
191 Status
= Fvb
->GetBlockSize (Fvb
, 0, &BlockSize
, &NumberOfBlocks
);
192 if (EFI_ERROR (Status
)) {
196 if ((Address
>= FvbBaseAddress
) && (Address
< (FvbBaseAddress
+ BlockSize
* NumberOfBlocks
))) {
198 FvbHandle
= HandleBuffer
[Index
];
203 FreePool (HandleBuffer
);
211 @param FtwDevice The private data of FTW driver
212 @param FvBlock Fvb protocol instance
214 @return A BOOLEAN value indicating in boot block or not.
219 EFI_FTW_DEVICE
*FtwDevice
,
220 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*FvBlock
224 EFI_SWAP_ADDRESS_RANGE_PROTOCOL
*SarProtocol
;
225 EFI_PHYSICAL_ADDRESS BootBlockBase
;
227 EFI_PHYSICAL_ADDRESS BackupBlockBase
;
228 UINTN BackupBlockSize
;
229 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*BootFvb
;
231 EFI_HANDLE FvbHandle
;
233 if (!FeaturePcdGet(PcdFullFtwServiceEnable
)) {
237 Status
= FtwGetSarProtocol ((VOID
**) &SarProtocol
);
238 if (EFI_ERROR (Status
)) {
242 // Get the boot block range
244 Status
= SarProtocol
->GetRangeLocation (
251 if (EFI_ERROR (Status
)) {
255 Status
= SarProtocol
->GetSwapState (SarProtocol
, &IsSwapped
);
256 if (EFI_ERROR (Status
)) {
260 // Get FVB by address
263 FvbHandle
= GetFvbByAddress (BootBlockBase
, &BootFvb
);
265 FvbHandle
= GetFvbByAddress (BackupBlockBase
, &BootFvb
);
268 if (FvbHandle
== NULL
) {
274 return (BOOLEAN
) (FvBlock
== BootFvb
);
278 Copy the content of spare block to a boot block. Size is FTW_BLOCK_SIZE.
279 Spare block is accessed by FTW working FVB protocol interface.
280 Target block is accessed by FvBlock protocol interface.
282 FTW will do extra work on boot block update.
283 FTW should depend on a protocol of EFI_ADDRESS_RANGE_SWAP_PROTOCOL,
284 which is produced by a chipset driver.
285 FTW updating boot block steps may be:
286 1. GetRangeLocation(), if the Range is inside the boot block, FTW know
287 that boot block will be update. It shall add a FLAG in the working block.
288 2. When spare block is ready,
289 3. SetSwapState(SWAPPED)
290 4. erasing boot block,
291 5. programming boot block until the boot block is ok.
292 6. SetSwapState(UNSWAPPED)
293 FTW shall not allow to update boot block when battery state is error.
295 @param FtwDevice The private data of FTW driver
297 @retval EFI_SUCCESS Spare block content is copied to boot block
298 @retval EFI_INVALID_PARAMETER Input parameter error
299 @retval EFI_OUT_OF_RESOURCES Allocate memory error
300 @retval EFI_ABORTED The function could not complete successfully
304 FlushSpareBlockToBootBlock (
305 EFI_FTW_DEVICE
*FtwDevice
315 EFI_SWAP_ADDRESS_RANGE_PROTOCOL
*SarProtocol
;
316 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*BootFvb
;
319 if (!FeaturePcdGet(PcdFullFtwServiceEnable
)) {
320 return EFI_UNSUPPORTED
;
324 // Locate swap address range protocol
326 Status
= FtwGetSarProtocol ((VOID
**) &SarProtocol
);
327 if (EFI_ERROR (Status
)) {
331 // Allocate a memory buffer
333 Length
= FtwDevice
->SpareAreaLength
;
334 Buffer
= AllocatePool (Length
);
335 if (Buffer
== NULL
) {
336 return EFI_OUT_OF_RESOURCES
;
339 // Get TopSwap bit state
341 Status
= SarProtocol
->GetSwapState (SarProtocol
, &TopSwap
);
342 if (EFI_ERROR (Status
)) {
343 DEBUG ((EFI_D_ERROR
, "Ftw: Get Top Swapped status - %r\n", Status
));
350 // Get FVB of current boot block
352 if (GetFvbByAddress (FtwDevice
->SpareAreaAddress
+ FtwDevice
->SpareAreaLength
, &BootFvb
) == NULL
) {
357 // Read data from current boot block
361 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
362 Count
= FtwDevice
->SpareBlockSize
;
363 Status
= BootFvb
->Read (
370 if (EFI_ERROR (Status
)) {
379 // Read data from spare block
382 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
383 Count
= FtwDevice
->SpareBlockSize
;
384 Status
= FtwDevice
->FtwBackupFvb
->Read (
385 FtwDevice
->FtwBackupFvb
,
386 FtwDevice
->FtwSpareLba
+ Index
,
391 if (EFI_ERROR (Status
)) {
401 Status
= SarProtocol
->SetSwapState (SarProtocol
, TRUE
);
402 if (EFI_ERROR (Status
)) {
408 // Erase current spare block
409 // Because TopSwap is set, this actually erase the top block (boot block)!
411 Status
= FtwEraseSpareBlock (FtwDevice
);
412 if (EFI_ERROR (Status
)) {
417 // Write memory buffer to current spare block. Still top block.
420 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
421 Count
= FtwDevice
->SpareBlockSize
;
422 Status
= FtwDevice
->FtwBackupFvb
->Write (
423 FtwDevice
->FtwBackupFvb
,
424 FtwDevice
->FtwSpareLba
+ Index
,
429 if (EFI_ERROR (Status
)) {
430 DEBUG ((EFI_D_ERROR
, "Ftw: FVB Write boot block - %r\n", Status
));
443 Status
= SarProtocol
->SetSwapState (SarProtocol
, FALSE
);
449 Copy the content of spare block to a target block.
450 Spare block is accessed by FTW backup FVB protocol interface.
451 Target block is accessed by FvBlock protocol interface.
454 @param FtwDevice The private data of FTW driver
455 @param FvBlock FVB Protocol interface to access target block
456 @param Lba Lba of the target block
457 @param BlockSize The size of the block
458 @param NumberOfBlocks The number of consecutive blocks starting with Lba
460 @retval EFI_SUCCESS Spare block content is copied to target block
461 @retval EFI_INVALID_PARAMETER Input parameter error
462 @retval EFI_OUT_OF_RESOURCES Allocate memory error
463 @retval EFI_ABORTED The function could not complete successfully
467 FlushSpareBlockToTargetBlock (
468 EFI_FTW_DEVICE
*FtwDevice
,
469 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*FvBlock
,
482 if ((FtwDevice
== NULL
) || (FvBlock
== NULL
)) {
483 return EFI_INVALID_PARAMETER
;
486 // Allocate a memory buffer
488 Length
= FtwDevice
->SpareAreaLength
;
489 Buffer
= AllocatePool (Length
);
490 if (Buffer
== NULL
) {
491 return EFI_OUT_OF_RESOURCES
;
494 // Read all content of spare block to memory buffer
497 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
498 Count
= FtwDevice
->SpareBlockSize
;
499 Status
= FtwDevice
->FtwBackupFvb
->Read (
500 FtwDevice
->FtwBackupFvb
,
501 FtwDevice
->FtwSpareLba
+ Index
,
506 if (EFI_ERROR (Status
)) {
514 // Erase the target block
516 Status
= FtwEraseBlock (FtwDevice
, FvBlock
, Lba
, NumberOfBlocks
);
517 if (EFI_ERROR (Status
)) {
522 // Write memory buffer to block, using the FvBlock protocol interface
525 for (Index
= 0; Index
< NumberOfBlocks
; Index
+= 1) {
527 Status
= FvBlock
->Write (FvBlock
, Lba
+ Index
, 0, &Count
, Ptr
);
528 if (EFI_ERROR (Status
)) {
529 DEBUG ((EFI_D_ERROR
, "Ftw: FVB Write block - %r\n", Status
));
543 Copy the content of spare block to working block. Size is FTW_BLOCK_SIZE.
544 Spare block is accessed by FTW backup FVB protocol interface. LBA is
545 FtwDevice->FtwSpareLba.
546 Working block is accessed by FTW working FVB protocol interface. LBA is
547 FtwDevice->FtwWorkBlockLba.
549 Since the working block header is important when FTW initializes, the
550 state of the operation should be handled carefully. The Crc value is
551 calculated without STATE element.
553 @param FtwDevice The private data of FTW driver
555 @retval EFI_SUCCESS Spare block content is copied to target block
556 @retval EFI_OUT_OF_RESOURCES Allocate memory error
557 @retval EFI_ABORTED The function could not complete successfully
561 FlushSpareBlockToWorkingBlock (
562 EFI_FTW_DEVICE
*FtwDevice
568 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*WorkingBlockHeader
;
574 // Allocate a memory buffer
576 Length
= FtwDevice
->SpareAreaLength
;
577 Buffer
= AllocatePool (Length
);
578 if (Buffer
== NULL
) {
579 return EFI_OUT_OF_RESOURCES
;
583 // To guarantee that the WorkingBlockValid is set on spare block
585 // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
586 // WorkingBlockValid);
587 // To skip Signature and Crc: sizeof(EFI_GUID)+sizeof(UINT32).
590 FtwDevice
->FtwBackupFvb
,
591 FtwDevice
->SpareBlockSize
,
592 FtwDevice
->FtwSpareLba
+ FtwDevice
->FtwWorkSpaceLbaInSpare
,
593 FtwDevice
->FtwWorkSpaceBaseInSpare
+ sizeof (EFI_GUID
) + sizeof (UINT32
),
597 // Read from spare block to memory buffer
600 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
601 Count
= FtwDevice
->SpareBlockSize
;
602 Status
= FtwDevice
->FtwBackupFvb
->Read (
603 FtwDevice
->FtwBackupFvb
,
604 FtwDevice
->FtwSpareLba
+ Index
,
609 if (EFI_ERROR (Status
)) {
617 // Clear the CRC and STATE, copy data from spare to working block.
619 WorkingBlockHeader
= (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*) (Buffer
+ (UINTN
) FtwDevice
->FtwWorkSpaceLbaInSpare
* FtwDevice
->SpareBlockSize
+ FtwDevice
->FtwWorkSpaceBaseInSpare
);
620 InitWorkSpaceHeader (WorkingBlockHeader
);
621 WorkingBlockHeader
->WorkingBlockValid
= FTW_ERASE_POLARITY
;
622 WorkingBlockHeader
->WorkingBlockInvalid
= FTW_ERASE_POLARITY
;
625 // target block is working block, then
626 // Set WorkingBlockInvalid in EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
627 // before erase the working block.
629 // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
630 // WorkingBlockInvalid);
631 // So hardcode offset as sizeof(EFI_GUID)+sizeof(UINT32) to
632 // skip Signature and Crc.
634 Status
= FtwUpdateFvState (
635 FtwDevice
->FtwFvBlock
,
636 FtwDevice
->WorkBlockSize
,
637 FtwDevice
->FtwWorkSpaceLba
,
638 FtwDevice
->FtwWorkSpaceBase
+ sizeof (EFI_GUID
) + sizeof (UINT32
),
639 WORKING_BLOCK_INVALID
641 if (EFI_ERROR (Status
)) {
646 FtwDevice
->FtwWorkSpaceHeader
->WorkingBlockInvalid
= FTW_VALID_STATE
;
649 // Erase the working block
651 Status
= FtwEraseBlock (FtwDevice
, FtwDevice
->FtwFvBlock
, FtwDevice
->FtwWorkBlockLba
, FtwDevice
->NumberOfWorkBlock
);
652 if (EFI_ERROR (Status
)) {
657 // Write memory buffer to working block, using the FvBlock protocol interface
660 for (Index
= 0; Index
< FtwDevice
->NumberOfWorkBlock
; Index
+= 1) {
661 Count
= FtwDevice
->WorkBlockSize
;
662 Status
= FtwDevice
->FtwFvBlock
->Write (
663 FtwDevice
->FtwFvBlock
,
664 FtwDevice
->FtwWorkBlockLba
+ Index
,
669 if (EFI_ERROR (Status
)) {
670 DEBUG ((EFI_D_ERROR
, "Ftw: FVB Write block - %r\n", Status
));
678 // Since the memory buffer will not be used, free memory Buffer.
683 // Update the VALID of the working block
685 // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER, WorkingBlockValid);
686 // So hardcode offset as sizeof(EFI_GUID)+sizeof(UINT32) to skip Signature and Crc.
688 Status
= FtwUpdateFvState (
689 FtwDevice
->FtwFvBlock
,
690 FtwDevice
->WorkBlockSize
,
691 FtwDevice
->FtwWorkSpaceLba
,
692 FtwDevice
->FtwWorkSpaceBase
+ sizeof (EFI_GUID
) + sizeof (UINT32
),
695 if (EFI_ERROR (Status
)) {
699 FtwDevice
->FtwWorkSpaceHeader
->WorkingBlockInvalid
= FTW_INVALID_STATE
;
700 FtwDevice
->FtwWorkSpaceHeader
->WorkingBlockValid
= FTW_VALID_STATE
;
706 Update a bit of state on a block device. The location of the bit is
707 calculated by the (Lba, Offset, bit). Here bit is determined by the
708 the name of a certain bit.
711 @param FvBlock FVB Protocol interface to access SrcBlock and DestBlock
712 @param BlockSize The size of the block
713 @param Lba Lba of a block
714 @param Offset Offset on the Lba
715 @param NewBit New value that will override the old value if it can be change
717 @retval EFI_SUCCESS A state bit has been updated successfully
718 @retval Others Access block device error.
720 Assume all bits of State are inside the same BYTE.
721 @retval EFI_ABORTED Read block fail
726 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*FvBlock
,
738 // Calculate the real Offset and Lba to write.
740 while (Offset
>= BlockSize
) {
746 // Read state from device, assume State is only one byte.
748 Length
= sizeof (UINT8
);
749 Status
= FvBlock
->Read (FvBlock
, Lba
, Offset
, &Length
, &State
);
750 if (EFI_ERROR (Status
)) {
754 State
^= FTW_POLARITY_REVERT
;
755 State
= (UINT8
) (State
| NewBit
);
756 State
^= FTW_POLARITY_REVERT
;
759 // Write state back to device
761 Length
= sizeof (UINT8
);
762 Status
= FvBlock
->Write (FvBlock
, Lba
, Offset
, &Length
, &State
);
768 Get the last Write Header pointer.
769 The last write header is the header whose 'complete' state hasn't been set.
770 After all, this header may be a EMPTY header entry for next Allocate.
773 @param FtwWorkSpaceHeader Pointer of the working block header
774 @param FtwWorkSpaceSize Size of the work space
775 @param FtwWriteHeader Pointer to retrieve the last write header
777 @retval EFI_SUCCESS Get the last write record successfully
778 @retval EFI_ABORTED The FTW work space is damaged
782 FtwGetLastWriteHeader (
783 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*FtwWorkSpaceHeader
,
784 IN UINTN FtwWorkSpaceSize
,
785 OUT EFI_FAULT_TOLERANT_WRITE_HEADER
**FtwWriteHeader
789 EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwHeader
;
791 *FtwWriteHeader
= NULL
;
792 FtwHeader
= (EFI_FAULT_TOLERANT_WRITE_HEADER
*) (FtwWorkSpaceHeader
+ 1);
793 Offset
= sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
);
795 while (FtwHeader
->Complete
== FTW_VALID_STATE
) {
796 Offset
+= FTW_WRITE_TOTAL_SIZE (FtwHeader
->NumberOfWrites
, FtwHeader
->PrivateDataSize
);
798 // If Offset exceed the FTW work space boudary, return error.
800 if (Offset
>= FtwWorkSpaceSize
) {
801 *FtwWriteHeader
= FtwHeader
;
805 FtwHeader
= (EFI_FAULT_TOLERANT_WRITE_HEADER
*) ((UINT8
*) FtwWorkSpaceHeader
+ Offset
);
808 // Last write header is found
810 *FtwWriteHeader
= FtwHeader
;
816 Get the last Write Record pointer. The last write Record is the Record
817 whose DestinationCompleted state hasn't been set. After all, this Record
818 may be a EMPTY record entry for next write.
821 @param FtwWriteHeader Pointer to the write record header
822 @param FtwWriteRecord Pointer to retrieve the last write record
824 @retval EFI_SUCCESS Get the last write record successfully
825 @retval EFI_ABORTED The FTW work space is damaged
829 FtwGetLastWriteRecord (
830 IN EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwWriteHeader
,
831 OUT EFI_FAULT_TOLERANT_WRITE_RECORD
**FtwWriteRecord
835 EFI_FAULT_TOLERANT_WRITE_RECORD
*FtwRecord
;
837 *FtwWriteRecord
= NULL
;
838 FtwRecord
= (EFI_FAULT_TOLERANT_WRITE_RECORD
*) (FtwWriteHeader
+ 1);
841 // Try to find the last write record "that has not completed"
843 for (Index
= 0; Index
< FtwWriteHeader
->NumberOfWrites
; Index
+= 1) {
844 if (FtwRecord
->DestinationComplete
!= FTW_VALID_STATE
) {
846 // The last write record is found
848 *FtwWriteRecord
= FtwRecord
;
854 if (FtwWriteHeader
->PrivateDataSize
!= 0) {
855 FtwRecord
= (EFI_FAULT_TOLERANT_WRITE_RECORD
*) ((UINTN
) FtwRecord
+ (UINTN
) FtwWriteHeader
->PrivateDataSize
);
859 // if Index == NumberOfWrites, then
860 // the last record has been written successfully,
861 // but the Header->Complete Flag has not been set.
862 // also return the last record.
864 if (Index
== FtwWriteHeader
->NumberOfWrites
) {
865 *FtwWriteRecord
= (EFI_FAULT_TOLERANT_WRITE_RECORD
*) ((UINTN
) FtwRecord
- FTW_RECORD_SIZE (FtwWriteHeader
->PrivateDataSize
));
873 To check if FtwRecord is the first record of FtwHeader.
875 @param FtwHeader Pointer to the write record header
876 @param FtwRecord Pointer to the write record
878 @retval TRUE FtwRecord is the first Record of the FtwHeader
879 @retval FALSE FtwRecord is not the first Record of the FtwHeader
883 IsFirstRecordOfWrites (
884 IN EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwHeader
,
885 IN EFI_FAULT_TOLERANT_WRITE_RECORD
*FtwRecord
891 Head
= (UINT8
*) FtwHeader
;
892 Ptr
= (UINT8
*) FtwRecord
;
894 Head
+= sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER
);
895 return (BOOLEAN
) (Head
== Ptr
);
899 To check if FtwRecord is the last record of FtwHeader. Because the
900 FtwHeader has NumberOfWrites & PrivateDataSize, the FtwRecord can be
901 determined if it is the last record of FtwHeader.
903 @param FtwHeader Pointer to the write record header
904 @param FtwRecord Pointer to the write record
906 @retval TRUE FtwRecord is the last Record of the FtwHeader
907 @retval FALSE FtwRecord is not the last Record of the FtwHeader
911 IsLastRecordOfWrites (
912 IN EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwHeader
,
913 IN EFI_FAULT_TOLERANT_WRITE_RECORD
*FtwRecord
919 Head
= (UINT8
*) FtwHeader
;
920 Ptr
= (UINT8
*) FtwRecord
;
922 Head
+= FTW_WRITE_TOTAL_SIZE (FtwHeader
->NumberOfWrites
- 1, FtwHeader
->PrivateDataSize
);
923 return (BOOLEAN
) (Head
== Ptr
);
927 To check if FtwRecord is the first record of FtwHeader.
929 @param FtwHeader Pointer to the write record header
930 @param FtwRecord Pointer to retrieve the previous write record
932 @retval EFI_ACCESS_DENIED Input record is the first record, no previous record is return.
933 @retval EFI_SUCCESS The previous write record is found.
937 GetPreviousRecordOfWrites (
938 IN EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwHeader
,
939 IN OUT EFI_FAULT_TOLERANT_WRITE_RECORD
**FtwRecord
944 if (IsFirstRecordOfWrites (FtwHeader
, *FtwRecord
)) {
946 return EFI_ACCESS_DENIED
;
949 Ptr
= (UINT8
*) (*FtwRecord
);
950 Ptr
-= FTW_RECORD_SIZE (FtwHeader
->PrivateDataSize
);
951 *FtwRecord
= (EFI_FAULT_TOLERANT_WRITE_RECORD
*) Ptr
;
956 Allocate private data for FTW driver and initialize it.
958 @param[out] FtwData Pointer to the FTW device structure
960 @retval EFI_SUCCESS Initialize the FTW device successfully.
961 @retval EFI_OUT_OF_RESOURCES Allocate memory error
962 @retval EFI_INVALID_PARAMETER Workspace or Spare block does not exist
967 OUT EFI_FTW_DEVICE
**FtwData
970 EFI_FTW_DEVICE
*FtwDevice
;
973 // Allocate private data of this driver,
974 // Including the FtwWorkSpace[FTW_WORK_SPACE_SIZE].
976 FtwDevice
= AllocateZeroPool (sizeof (EFI_FTW_DEVICE
) + PcdGet32 (PcdFlashNvStorageFtwWorkingSize
));
977 if (FtwDevice
== NULL
) {
978 return EFI_OUT_OF_RESOURCES
;
982 // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.
984 FtwDevice
->WorkSpaceLength
= (UINTN
) PcdGet32 (PcdFlashNvStorageFtwWorkingSize
);
985 FtwDevice
->SpareAreaLength
= (UINTN
) PcdGet32 (PcdFlashNvStorageFtwSpareSize
);
986 if ((FtwDevice
->WorkSpaceLength
== 0) || (FtwDevice
->SpareAreaLength
== 0)) {
987 DEBUG ((EFI_D_ERROR
, "Ftw: Workspace or Spare block does not exist!\n"));
988 FreePool (FtwDevice
);
989 return EFI_INVALID_PARAMETER
;
992 FtwDevice
->Signature
= FTW_DEVICE_SIGNATURE
;
993 FtwDevice
->FtwFvBlock
= NULL
;
994 FtwDevice
->FtwBackupFvb
= NULL
;
995 FtwDevice
->FtwWorkSpaceLba
= (EFI_LBA
) (-1);
996 FtwDevice
->FtwSpareLba
= (EFI_LBA
) (-1);
998 FtwDevice
->WorkSpaceAddress
= (EFI_PHYSICAL_ADDRESS
) PcdGet64 (PcdFlashNvStorageFtwWorkingBase64
);
999 if (FtwDevice
->WorkSpaceAddress
== 0) {
1000 FtwDevice
->WorkSpaceAddress
= (EFI_PHYSICAL_ADDRESS
) PcdGet32 (PcdFlashNvStorageFtwWorkingBase
);
1003 FtwDevice
->SpareAreaAddress
= (EFI_PHYSICAL_ADDRESS
) PcdGet64 (PcdFlashNvStorageFtwSpareBase64
);
1004 if (FtwDevice
->SpareAreaAddress
== 0) {
1005 FtwDevice
->SpareAreaAddress
= (EFI_PHYSICAL_ADDRESS
) PcdGet32 (PcdFlashNvStorageFtwSpareBase
);
1008 *FtwData
= FtwDevice
;
1014 Find the proper Firmware Volume Block protocol for FTW operation.
1016 @param[in, out] FtwDevice Pointer to the FTW device structure
1018 @retval EFI_SUCCESS Find the FVB protocol successfully.
1019 @retval EFI_NOT_FOUND No proper FVB protocol was found.
1020 @retval EFI_ABORTED Some data can not be got or be invalid.
1025 IN OUT EFI_FTW_DEVICE
*FtwDevice
1029 EFI_HANDLE
*HandleBuffer
;
1032 EFI_PHYSICAL_ADDRESS FvbBaseAddress
;
1033 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*Fvb
;
1034 EFI_FVB_ATTRIBUTES_2 Attributes
;
1037 UINTN NumberOfBlocks
;
1039 HandleBuffer
= NULL
;
1042 // Get all FVB handle.
1044 Status
= GetFvbCountAndBuffer (&HandleCount
, &HandleBuffer
);
1045 if (EFI_ERROR (Status
)) {
1046 return EFI_NOT_FOUND
;
1050 // Get the FVB to access variable store
1053 for (Index
= 0; Index
< HandleCount
; Index
+= 1) {
1054 Status
= FtwGetFvbByHandle (HandleBuffer
[Index
], &Fvb
);
1055 if (EFI_ERROR (Status
)) {
1056 Status
= EFI_NOT_FOUND
;
1061 // Ensure this FVB protocol support Write operation.
1063 Status
= Fvb
->GetAttributes (Fvb
, &Attributes
);
1064 if (EFI_ERROR (Status
) || ((Attributes
& EFI_FVB2_WRITE_STATUS
) == 0)) {
1068 // Compare the address and select the right one
1070 Status
= Fvb
->GetPhysicalAddress (Fvb
, &FvbBaseAddress
);
1071 if (EFI_ERROR (Status
)) {
1076 // Now, one FVB has one type of BlockSize.
1078 Status
= Fvb
->GetBlockSize (Fvb
, 0, &BlockSize
, &NumberOfBlocks
);
1079 if (EFI_ERROR (Status
)) {
1083 if ((FtwDevice
->FtwFvBlock
== NULL
) && (FtwDevice
->WorkSpaceAddress
>= FvbBaseAddress
) &&
1084 ((FtwDevice
->WorkSpaceAddress
+ FtwDevice
->WorkSpaceLength
) <= (FvbBaseAddress
+ BlockSize
* NumberOfBlocks
))) {
1085 FtwDevice
->FtwFvBlock
= Fvb
;
1087 // To get the LBA of work space
1089 for (LbaIndex
= 1; LbaIndex
<= NumberOfBlocks
; LbaIndex
+= 1) {
1090 if ((FtwDevice
->WorkSpaceAddress
>= (FvbBaseAddress
+ BlockSize
* (LbaIndex
- 1)))
1091 && (FtwDevice
->WorkSpaceAddress
< (FvbBaseAddress
+ BlockSize
* LbaIndex
))) {
1092 FtwDevice
->FtwWorkSpaceLba
= LbaIndex
- 1;
1094 // Get the Work space size and Base(Offset)
1096 FtwDevice
->FtwWorkSpaceSize
= FtwDevice
->WorkSpaceLength
;
1097 FtwDevice
->WorkBlockSize
= BlockSize
;
1098 FtwDevice
->FtwWorkSpaceBase
= (UINTN
) (FtwDevice
->WorkSpaceAddress
- (FvbBaseAddress
+ FtwDevice
->WorkBlockSize
* (LbaIndex
- 1)));
1099 FtwDevice
->NumberOfWorkSpaceBlock
= FTW_BLOCKS (FtwDevice
->FtwWorkSpaceBase
+ FtwDevice
->FtwWorkSpaceSize
, FtwDevice
->WorkBlockSize
);
1100 if (FtwDevice
->FtwWorkSpaceSize
>= FtwDevice
->WorkBlockSize
) {
1102 // 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.
1104 if (((FtwDevice
->WorkSpaceAddress
& (FtwDevice
->WorkBlockSize
- 1)) != 0) ||
1105 ((FtwDevice
->WorkSpaceLength
& (FtwDevice
->WorkBlockSize
- 1)) != 0)) {
1106 DEBUG ((EFI_D_ERROR
, "Ftw: Work space address or length is not block size aligned when work space size is larger than one block size\n"));
1107 FreePool (HandleBuffer
);
1111 } else if ((FtwDevice
->FtwWorkSpaceBase
+ FtwDevice
->FtwWorkSpaceSize
) > FtwDevice
->WorkBlockSize
) {
1112 DEBUG ((EFI_D_ERROR
, "Ftw: The work space range should not span blocks when work space size is less than one block size\n"));
1113 FreePool (HandleBuffer
);
1122 if ((FtwDevice
->FtwBackupFvb
== NULL
) && (FtwDevice
->SpareAreaAddress
>= FvbBaseAddress
) &&
1123 ((FtwDevice
->SpareAreaAddress
+ FtwDevice
->SpareAreaLength
) <= (FvbBaseAddress
+ BlockSize
* NumberOfBlocks
))) {
1124 FtwDevice
->FtwBackupFvb
= Fvb
;
1126 // To get the LBA of spare
1128 for (LbaIndex
= 1; LbaIndex
<= NumberOfBlocks
; LbaIndex
+= 1) {
1129 if ((FtwDevice
->SpareAreaAddress
>= (FvbBaseAddress
+ BlockSize
* (LbaIndex
- 1)))
1130 && (FtwDevice
->SpareAreaAddress
< (FvbBaseAddress
+ BlockSize
* LbaIndex
))) {
1132 // Get the NumberOfSpareBlock and BlockSize
1134 FtwDevice
->FtwSpareLba
= LbaIndex
- 1;
1135 FtwDevice
->SpareBlockSize
= BlockSize
;
1136 FtwDevice
->NumberOfSpareBlock
= FtwDevice
->SpareAreaLength
/ FtwDevice
->SpareBlockSize
;
1138 // Check the range of spare area to make sure that it's in FV range
1140 if ((FtwDevice
->FtwSpareLba
+ FtwDevice
->NumberOfSpareBlock
) > NumberOfBlocks
) {
1141 DEBUG ((EFI_D_ERROR
, "Ftw: Spare area is out of FV range\n"));
1142 FreePool (HandleBuffer
);
1147 // Check the alignment of spare area address and length, they should be block size aligned
1149 if (((FtwDevice
->SpareAreaAddress
& (FtwDevice
->SpareBlockSize
- 1)) != 0) ||
1150 ((FtwDevice
->SpareAreaLength
& (FtwDevice
->SpareBlockSize
- 1)) != 0)) {
1151 DEBUG ((EFI_D_ERROR
, "Ftw: Spare area address or length is not block size aligned\n"));
1152 FreePool (HandleBuffer
);
1154 // Report Status Code EFI_SW_EC_ABORTED.
1156 REPORT_STATUS_CODE ((EFI_ERROR_CODE
| EFI_ERROR_UNRECOVERED
), (EFI_SOFTWARE_DXE_BS_DRIVER
| EFI_SW_EC_ABORTED
));
1165 FreePool (HandleBuffer
);
1167 if ((FtwDevice
->FtwBackupFvb
== NULL
) || (FtwDevice
->FtwFvBlock
== NULL
) ||
1168 (FtwDevice
->FtwWorkSpaceLba
== (EFI_LBA
) (-1)) || (FtwDevice
->FtwSpareLba
== (EFI_LBA
) (-1))) {
1171 DEBUG ((EFI_D_INFO
, "Ftw: FtwWorkSpaceLba - 0x%lx, WorkBlockSize - 0x%x, FtwWorkSpaceBase - 0x%x\n", FtwDevice
->FtwWorkSpaceLba
, FtwDevice
->WorkBlockSize
, FtwDevice
->FtwWorkSpaceBase
));
1172 DEBUG ((EFI_D_INFO
, "Ftw: FtwSpareLba - 0x%lx, SpareBlockSize - 0x%x\n", FtwDevice
->FtwSpareLba
, FtwDevice
->SpareBlockSize
));
1179 Initialization for Fault Tolerant Write protocol.
1181 @param[in, out] FtwDevice Pointer to the FTW device structure
1183 @retval EFI_SUCCESS Initialize the FTW protocol successfully.
1184 @retval EFI_NOT_FOUND No proper FVB protocol was found.
1189 IN OUT EFI_FTW_DEVICE
*FtwDevice
1193 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*Fvb
;
1194 EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwHeader
;
1196 EFI_HANDLE FvbHandle
;
1197 EFI_LBA WorkSpaceLbaOffset
;
1200 // Find the right SMM Fvb protocol instance for FTW.
1202 Status
= FindFvbForFtw (FtwDevice
);
1203 if (EFI_ERROR (Status
)) {
1204 return EFI_NOT_FOUND
;
1208 // Calculate the start LBA of working block.
1210 if (FtwDevice
->FtwWorkSpaceSize
>= FtwDevice
->WorkBlockSize
) {
1212 // Working block is a standalone area which only contains working space.
1214 FtwDevice
->NumberOfWorkBlock
= FtwDevice
->NumberOfWorkSpaceBlock
;
1217 // Working block is an area which
1218 // contains working space in its last block and has the same size as spare
1219 // block, unless there are not enough blocks before the block that contains
1222 FtwDevice
->NumberOfWorkBlock
= (UINTN
) (FtwDevice
->FtwWorkSpaceLba
+ FtwDevice
->NumberOfWorkSpaceBlock
);
1223 while (FtwDevice
->NumberOfWorkBlock
* FtwDevice
->WorkBlockSize
> FtwDevice
->SpareAreaLength
) {
1224 FtwDevice
->NumberOfWorkBlock
--;
1227 FtwDevice
->FtwWorkBlockLba
= FtwDevice
->FtwWorkSpaceLba
+ FtwDevice
->NumberOfWorkSpaceBlock
- FtwDevice
->NumberOfWorkBlock
;
1228 DEBUG ((EFI_D_INFO
, "Ftw: NumberOfWorkBlock - 0x%x, FtwWorkBlockLba - 0x%lx\n", FtwDevice
->NumberOfWorkBlock
, FtwDevice
->FtwWorkBlockLba
));
1231 // Calcualte the LBA and base of work space in spare block.
1232 // Note: Do not assume Spare Block and Work Block have same block size.
1234 WorkSpaceLbaOffset
= FtwDevice
->FtwWorkSpaceLba
- FtwDevice
->FtwWorkBlockLba
;
1235 FtwDevice
->FtwWorkSpaceLbaInSpare
= (EFI_LBA
) (((UINTN
) WorkSpaceLbaOffset
* FtwDevice
->WorkBlockSize
+ FtwDevice
->FtwWorkSpaceBase
) / FtwDevice
->SpareBlockSize
);
1236 FtwDevice
->FtwWorkSpaceBaseInSpare
= ((UINTN
) WorkSpaceLbaOffset
* FtwDevice
->WorkBlockSize
+ FtwDevice
->FtwWorkSpaceBase
) % FtwDevice
->SpareBlockSize
;
1237 DEBUG ((EFI_D_INFO
, "Ftw: WorkSpaceLbaInSpare - 0x%lx, WorkSpaceBaseInSpare - 0x%x\n", FtwDevice
->FtwWorkSpaceLbaInSpare
, FtwDevice
->FtwWorkSpaceBaseInSpare
));
1240 // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.
1242 FtwDevice
->FtwWorkSpace
= (UINT8
*) (FtwDevice
+ 1);
1243 FtwDevice
->FtwWorkSpaceHeader
= (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*) FtwDevice
->FtwWorkSpace
;
1245 FtwDevice
->FtwLastWriteHeader
= NULL
;
1246 FtwDevice
->FtwLastWriteRecord
= NULL
;
1248 InitializeLocalWorkSpaceHeader ();
1251 // Refresh the working space data from working block
1253 Status
= WorkSpaceRefresh (FtwDevice
);
1254 ASSERT_EFI_ERROR (Status
);
1256 // If the working block workspace is not valid, try the spare block
1258 if (!IsValidWorkSpace (FtwDevice
->FtwWorkSpaceHeader
)) {
1260 // Read from spare block
1262 Status
= ReadWorkSpaceData (
1263 FtwDevice
->FtwBackupFvb
,
1264 FtwDevice
->SpareBlockSize
,
1265 FtwDevice
->FtwSpareLba
+ FtwDevice
->FtwWorkSpaceLbaInSpare
,
1266 FtwDevice
->FtwWorkSpaceBaseInSpare
,
1267 FtwDevice
->FtwWorkSpaceSize
,
1268 FtwDevice
->FtwWorkSpace
1270 ASSERT_EFI_ERROR (Status
);
1273 // If spare block is valid, then replace working block content.
1275 if (IsValidWorkSpace (FtwDevice
->FtwWorkSpaceHeader
)) {
1276 Status
= FlushSpareBlockToWorkingBlock (FtwDevice
);
1277 DEBUG ((EFI_D_INFO
, "Ftw: Restart working block update in %a() - %r\n",
1278 __FUNCTION__
, Status
));
1279 FtwAbort (&FtwDevice
->FtwInstance
);
1281 // Refresh work space.
1283 Status
= WorkSpaceRefresh (FtwDevice
);
1284 ASSERT_EFI_ERROR (Status
);
1287 "Ftw: Both working and spare blocks are invalid, init workspace\n"));
1289 // If both are invalid, then initialize work space.
1292 FtwDevice
->FtwWorkSpace
,
1293 FtwDevice
->FtwWorkSpaceSize
,
1296 InitWorkSpaceHeader (FtwDevice
->FtwWorkSpaceHeader
);
1298 // Initialize the work space
1300 Status
= FtwReclaimWorkSpace (FtwDevice
, FALSE
);
1301 ASSERT_EFI_ERROR (Status
);
1305 // If the FtwDevice->FtwLastWriteRecord is 1st record of write header &&
1306 // (! SpareComplete) THEN call Abort().
1308 if ((FtwDevice
->FtwLastWriteHeader
->HeaderAllocated
== FTW_VALID_STATE
) &&
1309 (FtwDevice
->FtwLastWriteRecord
->SpareComplete
!= FTW_VALID_STATE
) &&
1310 IsFirstRecordOfWrites (FtwDevice
->FtwLastWriteHeader
, FtwDevice
->FtwLastWriteRecord
)
1312 DEBUG ((EFI_D_ERROR
, "Ftw: Init.. find first record not SpareCompleted, abort()\n"));
1313 FtwAbort (&FtwDevice
->FtwInstance
);
1316 // If Header is incompleted and the last record has completed, then
1317 // call Abort() to set the Header->Complete FLAG.
1319 if ((FtwDevice
->FtwLastWriteHeader
->Complete
!= FTW_VALID_STATE
) &&
1320 (FtwDevice
->FtwLastWriteRecord
->DestinationComplete
== FTW_VALID_STATE
) &&
1321 IsLastRecordOfWrites (FtwDevice
->FtwLastWriteHeader
, FtwDevice
->FtwLastWriteRecord
)
1323 DEBUG ((EFI_D_ERROR
, "Ftw: Init.. find last record completed but header not, abort()\n"));
1324 FtwAbort (&FtwDevice
->FtwInstance
);
1327 // To check the workspace buffer following last Write header/records is EMPTY or not.
1328 // If it's not EMPTY, FTW also need to call reclaim().
1330 FtwHeader
= FtwDevice
->FtwLastWriteHeader
;
1331 Offset
= (UINT8
*) FtwHeader
- FtwDevice
->FtwWorkSpace
;
1332 if (FtwDevice
->FtwWorkSpace
[Offset
] != FTW_ERASED_BYTE
) {
1333 Offset
+= FTW_WRITE_TOTAL_SIZE (FtwHeader
->NumberOfWrites
, FtwHeader
->PrivateDataSize
);
1336 if (!IsErasedFlashBuffer (FtwDevice
->FtwWorkSpace
+ Offset
, FtwDevice
->FtwWorkSpaceSize
- Offset
)) {
1337 Status
= FtwReclaimWorkSpace (FtwDevice
, TRUE
);
1338 ASSERT_EFI_ERROR (Status
);
1342 // Restart if it's boot block
1344 if ((FtwDevice
->FtwLastWriteHeader
->Complete
!= FTW_VALID_STATE
) &&
1345 (FtwDevice
->FtwLastWriteRecord
->SpareComplete
== FTW_VALID_STATE
)
1347 if (FtwDevice
->FtwLastWriteRecord
->BootBlockUpdate
== FTW_VALID_STATE
) {
1348 Status
= FlushSpareBlockToBootBlock (FtwDevice
);
1349 DEBUG ((EFI_D_ERROR
, "Ftw: Restart boot block update - %r\n", Status
));
1350 ASSERT_EFI_ERROR (Status
);
1351 FtwAbort (&FtwDevice
->FtwInstance
);
1354 // if (SpareCompleted) THEN Restart to fault tolerant write.
1357 FvbHandle
= GetFvbByAddress ((EFI_PHYSICAL_ADDRESS
) (UINTN
) ((INT64
) FtwDevice
->SpareAreaAddress
+ FtwDevice
->FtwLastWriteRecord
->RelativeOffset
), &Fvb
);
1358 if (FvbHandle
!= NULL
) {
1359 Status
= FtwRestart (&FtwDevice
->FtwInstance
, FvbHandle
);
1360 DEBUG ((EFI_D_ERROR
, "Ftw: Restart last write - %r\n", Status
));
1361 ASSERT_EFI_ERROR (Status
);
1363 FtwAbort (&FtwDevice
->FtwInstance
);
1367 // Hook the protocol API
1369 FtwDevice
->FtwInstance
.GetMaxBlockSize
= FtwGetMaxBlockSize
;
1370 FtwDevice
->FtwInstance
.Allocate
= FtwAllocate
;
1371 FtwDevice
->FtwInstance
.Write
= FtwWrite
;
1372 FtwDevice
->FtwInstance
.Restart
= FtwRestart
;
1373 FtwDevice
->FtwInstance
.Abort
= FtwAbort
;
1374 FtwDevice
->FtwInstance
.GetLastWrite
= FtwGetLastWrite
;