3 Internal generic functions to operate flash block.
5 Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16 #include "FaultTolerantWrite.h"
20 Check whether a flash buffer is erased.
22 @param Buffer Buffer to check
23 @param BufferSize Size of the buffer
25 @return A BOOLEAN value indicating erased or not.
40 for (Index
= 0; Index
< BufferSize
; Index
+= 1) {
41 if (*Ptr
++ != FTW_ERASED_BYTE
) {
51 To erase the block with the spare block size.
54 @param FtwDevice The private data of FTW driver
55 @param FvBlock FVB Protocol interface
56 @param Lba Lba of the firmware block
58 @retval EFI_SUCCESS Block LBA is Erased successfully
59 @retval Others Error occurs
64 IN EFI_FTW_DEVICE
*FtwDevice
,
65 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*FvBlock
,
69 return FvBlock
->EraseBlocks (
72 FtwDevice
->NumberOfSpareBlock
,
73 EFI_LBA_LIST_TERMINATOR
80 @param FtwDevice The private data of FTW driver
82 @retval EFI_SUCCESS The erase request was successfully completed.
83 @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state.
84 @retval EFI_DEVICE_ERROR The block device is not functioning
85 correctly and could not be written.
86 The firmware device may have been
88 @retval EFI_INVALID_PARAMETER One or more of the LBAs listed
89 in the variable argument list do
90 not exist in the firmware volume.
96 IN EFI_FTW_DEVICE
*FtwDevice
99 return FtwDevice
->FtwBackupFvb
->EraseBlocks (
100 FtwDevice
->FtwBackupFvb
,
101 FtwDevice
->FtwSpareLba
,
102 FtwDevice
->NumberOfSpareBlock
,
103 EFI_LBA_LIST_TERMINATOR
109 Is it in working block?
111 @param FtwDevice The private data of FTW driver
112 @param FvBlock Fvb protocol instance
113 @param Lba The block specified
115 @return A BOOLEAN value indicating in working block or not.
120 EFI_FTW_DEVICE
*FtwDevice
,
121 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*FvBlock
,
126 // If matching the following condition, the target block is in working block.
127 // 1. Target block is on the FV of working block (Using the same FVB protocol instance).
128 // 2. Lba falls into the range of working block.
132 (FvBlock
== FtwDevice
->FtwFvBlock
) &&
133 (Lba
>= FtwDevice
->FtwWorkBlockLba
) &&
134 (Lba
<= FtwDevice
->FtwWorkSpaceLba
)
140 Get firmware block by address.
143 @param Address Address specified the block
144 @param FvBlock The block caller wanted
146 @retval EFI_SUCCESS The protocol instance if found.
147 @retval EFI_NOT_FOUND Block not found
152 IN EFI_PHYSICAL_ADDRESS Address
,
153 OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
**FvBlock
157 EFI_HANDLE
*HandleBuffer
;
160 EFI_PHYSICAL_ADDRESS FvbBaseAddress
;
161 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*Fvb
;
162 EFI_FIRMWARE_VOLUME_HEADER
*FwVolHeader
;
163 EFI_HANDLE FvbHandle
;
168 // Locate all handles of Fvb protocol
170 Status
= GetFvbCountAndBuffer (&HandleCount
, &HandleBuffer
);
171 if (EFI_ERROR (Status
)) {
175 // Get the FVB to access variable store
177 for (Index
= 0; Index
< HandleCount
; Index
+= 1) {
178 Status
= FtwGetFvbByHandle (HandleBuffer
[Index
], &Fvb
);
179 if (EFI_ERROR (Status
)) {
183 // Compare the address and select the right one
185 Status
= Fvb
->GetPhysicalAddress (Fvb
, &FvbBaseAddress
);
186 if (EFI_ERROR (Status
)) {
190 FwVolHeader
= (EFI_FIRMWARE_VOLUME_HEADER
*) ((UINTN
) FvbBaseAddress
);
191 if ((Address
>= FvbBaseAddress
) && (Address
<= (FvbBaseAddress
+ (FwVolHeader
->FvLength
- 1)))) {
193 FvbHandle
= HandleBuffer
[Index
];
198 FreePool (HandleBuffer
);
206 @param FtwDevice The private data of FTW driver
207 @param FvBlock Fvb protocol instance
208 @param Lba The block specified
210 @return A BOOLEAN value indicating in boot block or not.
215 EFI_FTW_DEVICE
*FtwDevice
,
216 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*FvBlock
,
221 EFI_SWAP_ADDRESS_RANGE_PROTOCOL
*SarProtocol
;
222 EFI_PHYSICAL_ADDRESS BootBlockBase
;
224 EFI_PHYSICAL_ADDRESS BackupBlockBase
;
225 UINTN BackupBlockSize
;
226 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*BootFvb
;
228 EFI_HANDLE FvbHandle
;
230 if (!FeaturePcdGet(PcdFullFtwServiceEnable
)) {
234 Status
= FtwGetSarProtocol ((VOID
**) &SarProtocol
);
235 if (EFI_ERROR (Status
)) {
239 // Get the boot block range
241 Status
= SarProtocol
->GetRangeLocation (
248 if (EFI_ERROR (Status
)) {
252 Status
= SarProtocol
->GetSwapState (SarProtocol
, &IsSwapped
);
253 if (EFI_ERROR (Status
)) {
257 // Get FVB by address
260 FvbHandle
= GetFvbByAddress (BootBlockBase
, &BootFvb
);
262 FvbHandle
= GetFvbByAddress (BackupBlockBase
, &BootFvb
);
265 if (FvbHandle
== NULL
) {
271 return (BOOLEAN
) (FvBlock
== BootFvb
);
275 Copy the content of spare block to a boot block. Size is FTW_BLOCK_SIZE.
276 Spare block is accessed by FTW working FVB protocol interface. LBA is 1.
277 Target block is accessed by FvbBlock protocol interface. LBA is Lba.
279 FTW will do extra work on boot block update.
280 FTW should depend on a protocol of EFI_ADDRESS_RANGE_SWAP_PROTOCOL,
281 which is produced by a chipset driver.
282 FTW updating boot block steps may be:
283 1. GetRangeLocation(), if the Range is inside the boot block, FTW know
284 that boot block will be update. It shall add a FLAG in the working block.
285 2. When spare block is ready,
286 3. SetSwapState(EFI_SWAPPED)
287 4. erasing boot block,
288 5. programming boot block until the boot block is ok.
289 6. SetSwapState(UNSWAPPED)
290 FTW shall not allow to update boot block when battery state is error.
292 @param FtwDevice The private data of FTW driver
294 @retval EFI_SUCCESS Spare block content is copied to boot block
295 @retval EFI_INVALID_PARAMETER Input parameter error
296 @retval EFI_OUT_OF_RESOURCES Allocate memory error
297 @retval EFI_ABORTED The function could not complete successfully
301 FlushSpareBlockToBootBlock (
302 EFI_FTW_DEVICE
*FtwDevice
312 EFI_SWAP_ADDRESS_RANGE_PROTOCOL
*SarProtocol
;
313 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*BootFvb
;
316 if (!FeaturePcdGet(PcdFullFtwServiceEnable
)) {
317 return EFI_UNSUPPORTED
;
321 // Locate swap address range protocol
323 Status
= FtwGetSarProtocol ((VOID
**) &SarProtocol
);
324 if (EFI_ERROR (Status
)) {
328 // Allocate a memory buffer
330 Length
= FtwDevice
->SpareAreaLength
;
331 Buffer
= AllocatePool (Length
);
332 if (Buffer
== NULL
) {
333 return EFI_OUT_OF_RESOURCES
;
336 // Get TopSwap bit state
338 Status
= SarProtocol
->GetSwapState (SarProtocol
, &TopSwap
);
339 if (EFI_ERROR (Status
)) {
340 DEBUG ((EFI_D_ERROR
, "Ftw: Get Top Swapped status - %r\n", Status
));
347 // Get FVB of current boot block
349 if (GetFvbByAddress (FtwDevice
->SpareAreaAddress
+ FtwDevice
->SpareAreaLength
, &BootFvb
) == NULL
) {
354 // Read data from current boot block
358 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
359 Count
= FtwDevice
->BlockSize
;
360 Status
= BootFvb
->Read (
367 if (EFI_ERROR (Status
)) {
376 // Read data from spare block
379 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
380 Count
= FtwDevice
->BlockSize
;
381 Status
= FtwDevice
->FtwBackupFvb
->Read (
382 FtwDevice
->FtwBackupFvb
,
383 FtwDevice
->FtwSpareLba
+ Index
,
388 if (EFI_ERROR (Status
)) {
398 Status
= SarProtocol
->SetSwapState (SarProtocol
, TRUE
);
399 if (EFI_ERROR (Status
)) {
405 // Erase current spare block
406 // Because TopSwap is set, this actually erase the top block (boot block)!
408 Status
= FtwEraseSpareBlock (FtwDevice
);
409 if (EFI_ERROR (Status
)) {
414 // Write memory buffer to current spare block. Still top block.
417 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
418 Count
= FtwDevice
->BlockSize
;
419 Status
= FtwDevice
->FtwBackupFvb
->Write (
420 FtwDevice
->FtwBackupFvb
,
421 FtwDevice
->FtwSpareLba
+ Index
,
426 if (EFI_ERROR (Status
)) {
427 DEBUG ((EFI_D_ERROR
, "Ftw: FVB Write boot block - %r\n", Status
));
440 Status
= SarProtocol
->SetSwapState (SarProtocol
, FALSE
);
446 Copy the content of spare block to a target block. Size is FTW_BLOCK_SIZE.
447 Spare block is accessed by FTW backup FVB protocol interface. LBA is 1.
448 Target block is accessed by FvbBlock protocol interface. LBA is Lba.
451 @param FtwDevice The private data of FTW driver
452 @param FvBlock FVB Protocol interface to access target block
453 @param Lba Lba of the target block
455 @retval EFI_SUCCESS Spare block content is copied to target block
456 @retval EFI_INVALID_PARAMETER Input parameter error
457 @retval EFI_OUT_OF_RESOURCES Allocate memory error
458 @retval EFI_ABORTED The function could not complete successfully
462 FlushSpareBlockToTargetBlock (
463 EFI_FTW_DEVICE
*FtwDevice
,
464 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*FvBlock
,
475 if ((FtwDevice
== NULL
) || (FvBlock
== NULL
)) {
476 return EFI_INVALID_PARAMETER
;
479 // Allocate a memory buffer
481 Length
= FtwDevice
->SpareAreaLength
;
482 Buffer
= AllocatePool (Length
);
483 if (Buffer
== NULL
) {
484 return EFI_OUT_OF_RESOURCES
;
487 // Read all content of spare block to memory buffer
490 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
491 Count
= FtwDevice
->BlockSize
;
492 Status
= FtwDevice
->FtwBackupFvb
->Read (
493 FtwDevice
->FtwBackupFvb
,
494 FtwDevice
->FtwSpareLba
+ Index
,
499 if (EFI_ERROR (Status
)) {
507 // Erase the target block
509 Status
= FtwEraseBlock (FtwDevice
, FvBlock
, Lba
);
510 if (EFI_ERROR (Status
)) {
515 // Write memory buffer to block, using the FvbBlock protocol interface
518 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
519 Count
= FtwDevice
->BlockSize
;
520 Status
= FvBlock
->Write (FvBlock
, Lba
+ Index
, 0, &Count
, Ptr
);
521 if (EFI_ERROR (Status
)) {
522 DEBUG ((EFI_D_ERROR
, "Ftw: FVB Write block - %r\n", Status
));
536 Copy the content of spare block to working block. Size is FTW_BLOCK_SIZE.
537 Spare block is accessed by FTW backup FVB protocol interface. LBA is
538 FtwDevice->FtwSpareLba.
539 Working block is accessed by FTW working FVB protocol interface. LBA is
540 FtwDevice->FtwWorkBlockLba.
542 Since the working block header is important when FTW initializes, the
543 state of the operation should be handled carefully. The Crc value is
544 calculated without STATE element.
546 @param FtwDevice The private data of FTW driver
548 @retval EFI_SUCCESS Spare block content is copied to target block
549 @retval EFI_OUT_OF_RESOURCES Allocate memory error
550 @retval EFI_ABORTED The function could not complete successfully
554 FlushSpareBlockToWorkingBlock (
555 EFI_FTW_DEVICE
*FtwDevice
561 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*WorkingBlockHeader
;
565 EFI_LBA WorkSpaceLbaOffset
;
568 // Allocate a memory buffer
570 Length
= FtwDevice
->SpareAreaLength
;
571 Buffer
= AllocatePool (Length
);
572 if (Buffer
== NULL
) {
573 return EFI_OUT_OF_RESOURCES
;
576 WorkSpaceLbaOffset
= FtwDevice
->FtwWorkSpaceLba
- FtwDevice
->FtwWorkBlockLba
;
579 // To guarantee that the WorkingBlockValid is set on spare block
581 // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
582 // WorkingBlockValid);
583 // To skip Signature and Crc: sizeof(EFI_GUID)+sizeof(UINT32).
586 FtwDevice
->FtwBackupFvb
,
587 FtwDevice
->FtwSpareLba
+ WorkSpaceLbaOffset
,
588 FtwDevice
->FtwWorkSpaceBase
+ sizeof (EFI_GUID
) + sizeof (UINT32
),
592 // Read from spare block to memory buffer
595 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
596 Count
= FtwDevice
->BlockSize
;
597 Status
= FtwDevice
->FtwBackupFvb
->Read (
598 FtwDevice
->FtwBackupFvb
,
599 FtwDevice
->FtwSpareLba
+ Index
,
604 if (EFI_ERROR (Status
)) {
612 // Clear the CRC and STATE, copy data from spare to working block.
614 WorkingBlockHeader
= (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*) (Buffer
+ (UINTN
) WorkSpaceLbaOffset
* FtwDevice
->BlockSize
+ FtwDevice
->FtwWorkSpaceBase
);
615 InitWorkSpaceHeader (WorkingBlockHeader
);
616 WorkingBlockHeader
->WorkingBlockValid
= FTW_ERASE_POLARITY
;
617 WorkingBlockHeader
->WorkingBlockInvalid
= FTW_ERASE_POLARITY
;
620 // target block is working block, then
621 // Set WorkingBlockInvalid in EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
622 // before erase the working block.
624 // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
625 // WorkingBlockInvalid);
626 // So hardcode offset as sizeof(EFI_GUID)+sizeof(UINT32) to
627 // skip Signature and Crc.
629 Status
= FtwUpdateFvState (
630 FtwDevice
->FtwFvBlock
,
631 FtwDevice
->FtwWorkSpaceLba
,
632 FtwDevice
->FtwWorkSpaceBase
+ sizeof (EFI_GUID
) + sizeof (UINT32
),
633 WORKING_BLOCK_INVALID
635 if (EFI_ERROR (Status
)) {
640 FtwDevice
->FtwWorkSpaceHeader
->WorkingBlockInvalid
= FTW_VALID_STATE
;
643 // Erase the working block
645 Status
= FtwEraseBlock (FtwDevice
, FtwDevice
->FtwFvBlock
, FtwDevice
->FtwWorkBlockLba
);
646 if (EFI_ERROR (Status
)) {
651 // Write memory buffer to working block, using the FvbBlock protocol interface
654 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
655 Count
= FtwDevice
->BlockSize
;
656 Status
= FtwDevice
->FtwFvBlock
->Write (
657 FtwDevice
->FtwFvBlock
,
658 FtwDevice
->FtwWorkBlockLba
+ Index
,
663 if (EFI_ERROR (Status
)) {
664 DEBUG ((EFI_D_ERROR
, "Ftw: FVB Write block - %r\n", Status
));
672 // Since the memory buffer will not be used, free memory Buffer.
677 // Update the VALID of the working block
679 // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER, WorkingBlockValid);
680 // So hardcode offset as sizeof(EFI_GUID)+sizeof(UINT32) to skip Signature and Crc.
682 Status
= FtwUpdateFvState (
683 FtwDevice
->FtwFvBlock
,
684 FtwDevice
->FtwWorkSpaceLba
,
685 FtwDevice
->FtwWorkSpaceBase
+ sizeof (EFI_GUID
) + sizeof (UINT32
),
688 if (EFI_ERROR (Status
)) {
692 FtwDevice
->FtwWorkSpaceHeader
->WorkingBlockInvalid
= FTW_INVALID_STATE
;
693 FtwDevice
->FtwWorkSpaceHeader
->WorkingBlockValid
= FTW_VALID_STATE
;
699 Update a bit of state on a block device. The location of the bit is
700 calculated by the (Lba, Offset, bit). Here bit is determined by the
701 the name of a certain bit.
704 @param FvBlock FVB Protocol interface to access SrcBlock and DestBlock
705 @param Lba Lba of a block
706 @param Offset Offset on the Lba
707 @param NewBit New value that will override the old value if it can be change
709 @retval EFI_SUCCESS A state bit has been updated successfully
710 @retval Others Access block device error.
712 Assume all bits of State are inside the same BYTE.
713 @retval EFI_ABORTED Read block fail
718 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*FvBlock
,
729 // Read state from device, assume State is only one byte.
731 Length
= sizeof (UINT8
);
732 Status
= FvBlock
->Read (FvBlock
, Lba
, Offset
, &Length
, &State
);
733 if (EFI_ERROR (Status
)) {
737 State
^= FTW_POLARITY_REVERT
;
738 State
= (UINT8
) (State
| NewBit
);
739 State
^= FTW_POLARITY_REVERT
;
742 // Write state back to device
744 Length
= sizeof (UINT8
);
745 Status
= FvBlock
->Write (FvBlock
, Lba
, Offset
, &Length
, &State
);
751 Get the last Write Header pointer.
752 The last write header is the header whose 'complete' state hasn't been set.
753 After all, this header may be a EMPTY header entry for next Allocate.
756 @param FtwWorkSpaceHeader Pointer of the working block header
757 @param FtwWorkSpaceSize Size of the work space
758 @param FtwWriteHeader Pointer to retrieve the last write header
760 @retval EFI_SUCCESS Get the last write record successfully
761 @retval EFI_ABORTED The FTW work space is damaged
765 FtwGetLastWriteHeader (
766 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*FtwWorkSpaceHeader
,
767 IN UINTN FtwWorkSpaceSize
,
768 OUT EFI_FAULT_TOLERANT_WRITE_HEADER
**FtwWriteHeader
772 EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwHeader
;
774 *FtwWriteHeader
= NULL
;
775 FtwHeader
= (EFI_FAULT_TOLERANT_WRITE_HEADER
*) (FtwWorkSpaceHeader
+ 1);
776 Offset
= sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
);
778 while (FtwHeader
->Complete
== FTW_VALID_STATE
) {
779 Offset
+= FTW_WRITE_TOTAL_SIZE (FtwHeader
->NumberOfWrites
, FtwHeader
->PrivateDataSize
);
781 // If Offset exceed the FTW work space boudary, return error.
783 if (Offset
>= FtwWorkSpaceSize
) {
784 *FtwWriteHeader
= FtwHeader
;
788 FtwHeader
= (EFI_FAULT_TOLERANT_WRITE_HEADER
*) ((UINT8
*) FtwWorkSpaceHeader
+ Offset
);
791 // Last write header is found
793 *FtwWriteHeader
= FtwHeader
;
799 Get the last Write Record pointer. The last write Record is the Record
800 whose DestinationCompleted state hasn't been set. After all, this Record
801 may be a EMPTY record entry for next write.
804 @param FtwWriteHeader Pointer to the write record header
805 @param FtwWriteRecord Pointer to retrieve the last write record
807 @retval EFI_SUCCESS Get the last write record successfully
808 @retval EFI_ABORTED The FTW work space is damaged
812 FtwGetLastWriteRecord (
813 IN EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwWriteHeader
,
814 OUT EFI_FAULT_TOLERANT_WRITE_RECORD
**FtwWriteRecord
818 EFI_FAULT_TOLERANT_WRITE_RECORD
*FtwRecord
;
820 *FtwWriteRecord
= NULL
;
821 FtwRecord
= (EFI_FAULT_TOLERANT_WRITE_RECORD
*) (FtwWriteHeader
+ 1);
824 // Try to find the last write record "that has not completed"
826 for (Index
= 0; Index
< FtwWriteHeader
->NumberOfWrites
; Index
+= 1) {
827 if (FtwRecord
->DestinationComplete
!= FTW_VALID_STATE
) {
829 // The last write record is found
831 *FtwWriteRecord
= FtwRecord
;
837 if (FtwWriteHeader
->PrivateDataSize
!= 0) {
838 FtwRecord
= (EFI_FAULT_TOLERANT_WRITE_RECORD
*) ((UINTN
) FtwRecord
+ (UINTN
) FtwWriteHeader
->PrivateDataSize
);
842 // if Index == NumberOfWrites, then
843 // the last record has been written successfully,
844 // but the Header->Complete Flag has not been set.
845 // also return the last record.
847 if (Index
== FtwWriteHeader
->NumberOfWrites
) {
848 *FtwWriteRecord
= (EFI_FAULT_TOLERANT_WRITE_RECORD
*) ((UINTN
) FtwRecord
- FTW_RECORD_SIZE (FtwWriteHeader
->PrivateDataSize
));
856 To check if FtwRecord is the first record of FtwHeader.
858 @param FtwHeader Pointer to the write record header
859 @param FtwRecord Pointer to the write record
861 @retval TRUE FtwRecord is the first Record of the FtwHeader
862 @retval FALSE FtwRecord is not the first Record of the FtwHeader
866 IsFirstRecordOfWrites (
867 IN EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwHeader
,
868 IN EFI_FAULT_TOLERANT_WRITE_RECORD
*FtwRecord
874 Head
= (UINT8
*) FtwHeader
;
875 Ptr
= (UINT8
*) FtwRecord
;
877 Head
+= sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER
);
878 return (BOOLEAN
) (Head
== Ptr
);
882 To check if FtwRecord is the last record of FtwHeader. Because the
883 FtwHeader has NumberOfWrites & PrivateDataSize, the FtwRecord can be
884 determined if it is the last record of FtwHeader.
886 @param FtwHeader Pointer to the write record header
887 @param FtwRecord Pointer to the write record
889 @retval TRUE FtwRecord is the last Record of the FtwHeader
890 @retval FALSE FtwRecord is not the last Record of the FtwHeader
894 IsLastRecordOfWrites (
895 IN EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwHeader
,
896 IN EFI_FAULT_TOLERANT_WRITE_RECORD
*FtwRecord
902 Head
= (UINT8
*) FtwHeader
;
903 Ptr
= (UINT8
*) FtwRecord
;
905 Head
+= FTW_WRITE_TOTAL_SIZE (FtwHeader
->NumberOfWrites
- 1, FtwHeader
->PrivateDataSize
);
906 return (BOOLEAN
) (Head
== Ptr
);
910 To check if FtwRecord is the first record of FtwHeader.
912 @param FtwHeader Pointer to the write record header
913 @param FtwRecord Pointer to retrieve the previous write record
915 @retval EFI_ACCESS_DENIED Input record is the first record, no previous record is return.
916 @retval EFI_SUCCESS The previous write record is found.
920 GetPreviousRecordOfWrites (
921 IN EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwHeader
,
922 IN OUT EFI_FAULT_TOLERANT_WRITE_RECORD
**FtwRecord
927 if (IsFirstRecordOfWrites (FtwHeader
, *FtwRecord
)) {
929 return EFI_ACCESS_DENIED
;
932 Ptr
= (UINT8
*) (*FtwRecord
);
933 Ptr
-= FTW_RECORD_SIZE (FtwHeader
->PrivateDataSize
);
934 *FtwRecord
= (EFI_FAULT_TOLERANT_WRITE_RECORD
*) Ptr
;
939 Allocate private data for FTW driver and initialize it.
941 @param[out] FtwData Pointer to the FTW device structure
943 @retval EFI_SUCCESS Initialize the FTW device successfully.
944 @retval EFI_OUT_OF_RESOURCES Allocate memory error
945 @retval EFI_INVALID_PARAMETER Workspace or Spare block does not exist
950 OUT EFI_FTW_DEVICE
**FtwData
953 EFI_FTW_DEVICE
*FtwDevice
;
956 // Allocate private data of this driver,
957 // Including the FtwWorkSpace[FTW_WORK_SPACE_SIZE].
959 FtwDevice
= AllocateZeroPool (sizeof (EFI_FTW_DEVICE
) + PcdGet32 (PcdFlashNvStorageFtwWorkingSize
));
960 if (FtwDevice
== NULL
) {
961 return EFI_OUT_OF_RESOURCES
;
965 // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.
967 FtwDevice
->WorkSpaceLength
= (UINTN
) PcdGet32 (PcdFlashNvStorageFtwWorkingSize
);
968 FtwDevice
->SpareAreaLength
= (UINTN
) PcdGet32 (PcdFlashNvStorageFtwSpareSize
);
969 if ((FtwDevice
->WorkSpaceLength
== 0) || (FtwDevice
->SpareAreaLength
== 0)) {
970 DEBUG ((EFI_D_ERROR
, "Ftw: Workspace or Spare block does not exist!\n"));
971 FreePool (FtwDevice
);
972 return EFI_INVALID_PARAMETER
;
975 FtwDevice
->Signature
= FTW_DEVICE_SIGNATURE
;
976 FtwDevice
->FtwFvBlock
= NULL
;
977 FtwDevice
->FtwBackupFvb
= NULL
;
978 FtwDevice
->FtwWorkSpaceLba
= (EFI_LBA
) (-1);
979 FtwDevice
->FtwSpareLba
= (EFI_LBA
) (-1);
981 FtwDevice
->WorkSpaceAddress
= (EFI_PHYSICAL_ADDRESS
) PcdGet64 (PcdFlashNvStorageFtwWorkingBase64
);
982 if (FtwDevice
->WorkSpaceAddress
== 0) {
983 FtwDevice
->WorkSpaceAddress
= (EFI_PHYSICAL_ADDRESS
) PcdGet32 (PcdFlashNvStorageFtwWorkingBase
);
986 FtwDevice
->SpareAreaAddress
= (EFI_PHYSICAL_ADDRESS
) PcdGet64 (PcdFlashNvStorageFtwSpareBase64
);
987 if (FtwDevice
->SpareAreaAddress
== 0) {
988 FtwDevice
->SpareAreaAddress
= (EFI_PHYSICAL_ADDRESS
) PcdGet32 (PcdFlashNvStorageFtwSpareBase
);
991 *FtwData
= FtwDevice
;
997 Find the proper Firmware Volume Block protocol for FTW operation.
999 @param[in, out] FtwDevice Pointer to the FTW device structure
1001 @retval EFI_SUCCESS Find the FVB protocol successfully.
1002 @retval EFI_NOT_FOUND No proper FVB protocol was found.
1003 @retval EFI_ABORTED Some data can not be got or be invalid.
1008 IN OUT EFI_FTW_DEVICE
*FtwDevice
1012 EFI_HANDLE
*HandleBuffer
;
1015 EFI_PHYSICAL_ADDRESS FvbBaseAddress
;
1016 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*Fvb
;
1017 EFI_FIRMWARE_VOLUME_HEADER
*FwVolHeader
;
1018 EFI_FVB_ATTRIBUTES_2 Attributes
;
1019 EFI_FV_BLOCK_MAP_ENTRY
*FvbMapEntry
;
1023 // Get all FVB handle.
1025 Status
= GetFvbCountAndBuffer (&HandleCount
, &HandleBuffer
);
1026 if (EFI_ERROR (Status
)) {
1027 return EFI_NOT_FOUND
;
1031 // Get the FVB to access variable store
1034 for (Index
= 0; Index
< HandleCount
; Index
+= 1) {
1035 Status
= FtwGetFvbByHandle (HandleBuffer
[Index
], &Fvb
);
1036 if (EFI_ERROR (Status
)) {
1037 Status
= EFI_NOT_FOUND
;
1042 // Ensure this FVB protocol support Write operation.
1044 Status
= Fvb
->GetAttributes (Fvb
, &Attributes
);
1045 if (EFI_ERROR (Status
) || ((Attributes
& EFI_FVB2_WRITE_STATUS
) == 0)) {
1049 // Compare the address and select the right one
1051 Status
= Fvb
->GetPhysicalAddress (Fvb
, &FvbBaseAddress
);
1052 if (EFI_ERROR (Status
)) {
1056 FwVolHeader
= (EFI_FIRMWARE_VOLUME_HEADER
*) ((UINTN
) FvbBaseAddress
);
1057 if ((FtwDevice
->FtwFvBlock
== NULL
) && (FtwDevice
->WorkSpaceAddress
>= FvbBaseAddress
) &&
1058 ((FtwDevice
->WorkSpaceAddress
+ FtwDevice
->WorkSpaceLength
) <= (FvbBaseAddress
+ FwVolHeader
->FvLength
))
1060 FtwDevice
->FtwFvBlock
= Fvb
;
1062 // To get the LBA of work space
1064 if ((FwVolHeader
->FvLength
) > (FwVolHeader
->HeaderLength
)) {
1066 // Now, one FV has one type of BlockLength
1068 FvbMapEntry
= &FwVolHeader
->BlockMap
[0];
1069 for (LbaIndex
= 1; LbaIndex
<= FvbMapEntry
->NumBlocks
; LbaIndex
+= 1) {
1070 if ((FtwDevice
->WorkSpaceAddress
>= (FvbBaseAddress
+ FvbMapEntry
->Length
* (LbaIndex
- 1)))
1071 && (FtwDevice
->WorkSpaceAddress
< (FvbBaseAddress
+ FvbMapEntry
->Length
* LbaIndex
))) {
1072 FtwDevice
->FtwWorkSpaceLba
= LbaIndex
- 1;
1074 // Get the Work space size and Base(Offset)
1076 FtwDevice
->FtwWorkSpaceSize
= FtwDevice
->WorkSpaceLength
;
1077 FtwDevice
->FtwWorkSpaceBase
= (UINTN
) (FtwDevice
->WorkSpaceAddress
- (FvbBaseAddress
+ FvbMapEntry
->Length
* (LbaIndex
- 1)));
1084 if ((FtwDevice
->FtwBackupFvb
== NULL
) && (FtwDevice
->SpareAreaAddress
>= FvbBaseAddress
) &&
1085 ((FtwDevice
->SpareAreaAddress
+ FtwDevice
->SpareAreaLength
) <= (FvbBaseAddress
+ FwVolHeader
->FvLength
))
1087 FtwDevice
->FtwBackupFvb
= Fvb
;
1089 // To get the LBA of spare
1091 if ((FwVolHeader
->FvLength
) > (FwVolHeader
->HeaderLength
)) {
1093 // Now, one FV has one type of BlockLength
1095 FvbMapEntry
= &FwVolHeader
->BlockMap
[0];
1096 for (LbaIndex
= 1; LbaIndex
<= FvbMapEntry
->NumBlocks
; LbaIndex
+= 1) {
1097 if ((FtwDevice
->SpareAreaAddress
>= (FvbBaseAddress
+ FvbMapEntry
->Length
* (LbaIndex
- 1)))
1098 && (FtwDevice
->SpareAreaAddress
< (FvbBaseAddress
+ FvbMapEntry
->Length
* LbaIndex
))) {
1100 // Get the NumberOfSpareBlock and BlockSize
1102 FtwDevice
->FtwSpareLba
= LbaIndex
- 1;
1103 FtwDevice
->BlockSize
= FvbMapEntry
->Length
;
1104 FtwDevice
->NumberOfSpareBlock
= FtwDevice
->SpareAreaLength
/ FtwDevice
->BlockSize
;
1106 // Check the range of spare area to make sure that it's in FV range
1108 if ((FtwDevice
->FtwSpareLba
+ FtwDevice
->NumberOfSpareBlock
) > FvbMapEntry
->NumBlocks
) {
1109 DEBUG ((EFI_D_ERROR
, "Ftw: Spare area is out of FV range\n"));
1110 FreePool (HandleBuffer
);
1115 // Check the alignment of spare area address and length, they should be block size aligned
1117 if (((FtwDevice
->SpareAreaAddress
& (FtwDevice
->BlockSize
- 1)) != 0) ||
1118 ((FtwDevice
->SpareAreaLength
& (FtwDevice
->BlockSize
- 1)) != 0)) {
1119 DEBUG ((EFI_D_ERROR
, "Ftw: Spare area address or length is not block size aligned\n"));
1120 FreePool (HandleBuffer
);
1122 // Report Status Code EFI_SW_EC_ABORTED.
1124 REPORT_STATUS_CODE ( (EFI_ERROR_CODE
| EFI_ERROR_UNRECOVERED
), (EFI_SOFTWARE_DXE_BS_DRIVER
| EFI_SW_EC_ABORTED
));
1134 FreePool (HandleBuffer
);
1136 if ((FtwDevice
->FtwBackupFvb
== NULL
) || (FtwDevice
->FtwFvBlock
== NULL
) ||
1137 (FtwDevice
->FtwWorkSpaceLba
== (EFI_LBA
) (-1)) || (FtwDevice
->FtwSpareLba
== (EFI_LBA
) (-1))) {
1146 Initialization for Fault Tolerant Write protocol.
1148 @param[in, out] FtwDevice Pointer to the FTW device structure
1150 @retval EFI_SUCCESS Initialize the FTW protocol successfully.
1151 @retval EFI_NOT_FOUND No proper FVB protocol was found.
1156 IN OUT EFI_FTW_DEVICE
*FtwDevice
1160 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*Fvb
;
1162 EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwHeader
;
1164 EFI_HANDLE FvbHandle
;
1165 EFI_LBA WorkSpaceLbaOffset
;
1168 // Find the right SMM Fvb protocol instance for FTW.
1170 Status
= FindFvbForFtw (FtwDevice
);
1171 if (EFI_ERROR (Status
)) {
1172 return EFI_NOT_FOUND
;
1176 // Calculate the start LBA of working block. Working block is an area which
1177 // contains working space in its last block and has the same size as spare
1178 // block, unless there are not enough blocks before the block that contains
1181 FtwDevice
->FtwWorkBlockLba
= FtwDevice
->FtwWorkSpaceLba
- FtwDevice
->NumberOfSpareBlock
+ 1;
1182 ASSERT ((INT64
) (FtwDevice
->FtwWorkBlockLba
) >= 0);
1185 // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.
1187 FtwDevice
->FtwWorkSpace
= (UINT8
*) (FtwDevice
+ 1);
1188 FtwDevice
->FtwWorkSpaceHeader
= (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*) FtwDevice
->FtwWorkSpace
;
1190 FtwDevice
->FtwLastWriteHeader
= NULL
;
1191 FtwDevice
->FtwLastWriteRecord
= NULL
;
1193 InitializeLocalWorkSpaceHeader ();
1196 // Refresh the working space data from working block
1198 Status
= WorkSpaceRefresh (FtwDevice
);
1199 ASSERT_EFI_ERROR (Status
);
1201 // If the working block workspace is not valid, try the spare block
1203 if (!IsValidWorkSpace (FtwDevice
->FtwWorkSpaceHeader
)) {
1205 // Read from spare block
1207 WorkSpaceLbaOffset
= FtwDevice
->FtwWorkSpaceLba
- FtwDevice
->FtwWorkBlockLba
;
1208 Length
= FtwDevice
->FtwWorkSpaceSize
;
1209 Status
= FtwDevice
->FtwBackupFvb
->Read (
1210 FtwDevice
->FtwBackupFvb
,
1211 FtwDevice
->FtwSpareLba
+ WorkSpaceLbaOffset
,
1212 FtwDevice
->FtwWorkSpaceBase
,
1214 FtwDevice
->FtwWorkSpace
1216 ASSERT_EFI_ERROR (Status
);
1219 // If spare block is valid, then replace working block content.
1221 if (IsValidWorkSpace (FtwDevice
->FtwWorkSpaceHeader
)) {
1222 Status
= FlushSpareBlockToWorkingBlock (FtwDevice
);
1223 DEBUG ((EFI_D_ERROR
, "Ftw: Restart working block update in InitFtwProtocol() - %r\n", Status
));
1224 FtwAbort (&FtwDevice
->FtwInstance
);
1226 // Refresh work space.
1228 Status
= WorkSpaceRefresh (FtwDevice
);
1229 ASSERT_EFI_ERROR (Status
);
1231 DEBUG ((EFI_D_ERROR
, "Ftw: Both are invalid, init workspace\n"));
1233 // If both are invalid, then initialize work space.
1236 FtwDevice
->FtwWorkSpace
,
1237 FtwDevice
->FtwWorkSpaceSize
,
1240 InitWorkSpaceHeader (FtwDevice
->FtwWorkSpaceHeader
);
1242 // Initialize the work space
1244 Status
= FtwReclaimWorkSpace (FtwDevice
, FALSE
);
1245 ASSERT_EFI_ERROR (Status
);
1249 // If the FtwDevice->FtwLastWriteRecord is 1st record of write header &&
1250 // (! SpareComplete) THEN call Abort().
1252 if ((FtwDevice
->FtwLastWriteHeader
->HeaderAllocated
== FTW_VALID_STATE
) &&
1253 (FtwDevice
->FtwLastWriteRecord
->SpareComplete
!= FTW_VALID_STATE
) &&
1254 IsFirstRecordOfWrites (FtwDevice
->FtwLastWriteHeader
, FtwDevice
->FtwLastWriteRecord
)
1256 DEBUG ((EFI_D_ERROR
, "Ftw: Init.. find first record not SpareCompleted, abort()\n"));
1257 FtwAbort (&FtwDevice
->FtwInstance
);
1260 // If Header is incompleted and the last record has completed, then
1261 // call Abort() to set the Header->Complete FLAG.
1263 if ((FtwDevice
->FtwLastWriteHeader
->Complete
!= FTW_VALID_STATE
) &&
1264 (FtwDevice
->FtwLastWriteRecord
->DestinationComplete
== FTW_VALID_STATE
) &&
1265 IsLastRecordOfWrites (FtwDevice
->FtwLastWriteHeader
, FtwDevice
->FtwLastWriteRecord
)
1267 DEBUG ((EFI_D_ERROR
, "Ftw: Init.. find last record completed but header not, abort()\n"));
1268 FtwAbort (&FtwDevice
->FtwInstance
);
1271 // To check the workspace buffer following last Write header/records is EMPTY or not.
1272 // If it's not EMPTY, FTW also need to call reclaim().
1274 FtwHeader
= FtwDevice
->FtwLastWriteHeader
;
1275 Offset
= (UINT8
*) FtwHeader
- FtwDevice
->FtwWorkSpace
;
1276 if (FtwDevice
->FtwWorkSpace
[Offset
] != FTW_ERASED_BYTE
) {
1277 Offset
+= FTW_WRITE_TOTAL_SIZE (FtwHeader
->NumberOfWrites
, FtwHeader
->PrivateDataSize
);
1280 if (!IsErasedFlashBuffer (FtwDevice
->FtwWorkSpace
+ Offset
, FtwDevice
->FtwWorkSpaceSize
- Offset
)) {
1281 Status
= FtwReclaimWorkSpace (FtwDevice
, TRUE
);
1282 ASSERT_EFI_ERROR (Status
);
1286 // Restart if it's boot block
1288 if ((FtwDevice
->FtwLastWriteHeader
->Complete
!= FTW_VALID_STATE
) &&
1289 (FtwDevice
->FtwLastWriteRecord
->SpareComplete
== FTW_VALID_STATE
)
1291 if (FtwDevice
->FtwLastWriteRecord
->BootBlockUpdate
== FTW_VALID_STATE
) {
1292 Status
= FlushSpareBlockToBootBlock (FtwDevice
);
1293 DEBUG ((EFI_D_ERROR
, "Ftw: Restart boot block update - %r\n", Status
));
1294 ASSERT_EFI_ERROR (Status
);
1295 FtwAbort (&FtwDevice
->FtwInstance
);
1298 // if (SpareCompleted) THEN Restart to fault tolerant write.
1301 FvbHandle
= GetFvbByAddress ((EFI_PHYSICAL_ADDRESS
) (UINTN
) ((INT64
) FtwDevice
->SpareAreaAddress
+ FtwDevice
->FtwLastWriteRecord
->RelativeOffset
), &Fvb
);
1302 if (FvbHandle
!= NULL
) {
1303 Status
= FtwRestart (&FtwDevice
->FtwInstance
, FvbHandle
);
1304 DEBUG ((EFI_D_ERROR
, "FtwLite: Restart last write - %r\n", Status
));
1305 ASSERT_EFI_ERROR (Status
);
1307 FtwAbort (&FtwDevice
->FtwInstance
);
1311 // Hook the protocol API
1313 FtwDevice
->FtwInstance
.GetMaxBlockSize
= FtwGetMaxBlockSize
;
1314 FtwDevice
->FtwInstance
.Allocate
= FtwAllocate
;
1315 FtwDevice
->FtwInstance
.Write
= FtwWrite
;
1316 FtwDevice
->FtwInstance
.Restart
= FtwRestart
;
1317 FtwDevice
->FtwInstance
.Abort
= FtwAbort
;
1318 FtwDevice
->FtwInstance
.GetLastWrite
= FtwGetLastWrite
;