3 Internal generic functions to operate flash block.
5 Copyright (c) 2006 - 2014, 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 specified blocks.
54 @param FtwDevice The private data of FTW driver
55 @param FvBlock FVB Protocol interface
56 @param Lba Lba of the firmware block
57 @param NumberOfBlocks The number of consecutive blocks starting with Lba
59 @retval EFI_SUCCESS Block LBA is Erased successfully
60 @retval Others Error occurs
65 IN EFI_FTW_DEVICE
*FtwDevice
,
66 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*FvBlock
,
71 return FvBlock
->EraseBlocks (
75 EFI_LBA_LIST_TERMINATOR
82 @param FtwDevice The private data of FTW driver
84 @retval EFI_SUCCESS The erase request was successfully completed.
85 @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state.
86 @retval EFI_DEVICE_ERROR The block device is not functioning
87 correctly and could not be written.
88 The firmware device may have been
90 @retval EFI_INVALID_PARAMETER One or more of the LBAs listed
91 in the variable argument list do
92 not exist in the firmware volume.
98 IN EFI_FTW_DEVICE
*FtwDevice
101 return FtwDevice
->FtwBackupFvb
->EraseBlocks (
102 FtwDevice
->FtwBackupFvb
,
103 FtwDevice
->FtwSpareLba
,
104 FtwDevice
->NumberOfSpareBlock
,
105 EFI_LBA_LIST_TERMINATOR
111 Is it in working block?
113 @param FtwDevice The private data of FTW driver
114 @param FvBlock Fvb protocol instance
115 @param Lba The block specified
117 @return A BOOLEAN value indicating in working block or not.
122 EFI_FTW_DEVICE
*FtwDevice
,
123 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*FvBlock
,
128 // If matching the following condition, the target block is in working block.
129 // 1. Target block is on the FV of working block (Using the same FVB protocol instance).
130 // 2. Lba falls into the range of working block.
134 (FvBlock
== FtwDevice
->FtwFvBlock
) &&
135 (Lba
>= FtwDevice
->FtwWorkBlockLba
) &&
136 (Lba
<= FtwDevice
->FtwWorkSpaceLba
)
142 Get firmware volume block by address.
145 @param Address Address specified the block
146 @param FvBlock The block caller wanted
148 @retval EFI_SUCCESS The protocol instance if found.
149 @retval EFI_NOT_FOUND Block not found
154 IN EFI_PHYSICAL_ADDRESS Address
,
155 OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
**FvBlock
159 EFI_HANDLE
*HandleBuffer
;
162 EFI_PHYSICAL_ADDRESS FvbBaseAddress
;
163 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*Fvb
;
164 EFI_HANDLE FvbHandle
;
166 UINTN NumberOfBlocks
;
172 // Locate all handles of Fvb protocol
174 Status
= GetFvbCountAndBuffer (&HandleCount
, &HandleBuffer
);
175 if (EFI_ERROR (Status
)) {
179 // Get the FVB to access variable store
181 for (Index
= 0; Index
< HandleCount
; Index
+= 1) {
182 Status
= FtwGetFvbByHandle (HandleBuffer
[Index
], &Fvb
);
183 if (EFI_ERROR (Status
)) {
187 // Compare the address and select the right one
189 Status
= Fvb
->GetPhysicalAddress (Fvb
, &FvbBaseAddress
);
190 if (EFI_ERROR (Status
)) {
195 // Now, one FVB has one type of BlockSize
197 Status
= Fvb
->GetBlockSize (Fvb
, 0, &BlockSize
, &NumberOfBlocks
);
198 if (EFI_ERROR (Status
)) {
202 if ((Address
>= FvbBaseAddress
) && (Address
< (FvbBaseAddress
+ BlockSize
* NumberOfBlocks
))) {
204 FvbHandle
= HandleBuffer
[Index
];
209 FreePool (HandleBuffer
);
217 @param FtwDevice The private data of FTW driver
218 @param FvBlock Fvb protocol instance
220 @return A BOOLEAN value indicating in boot block or not.
225 EFI_FTW_DEVICE
*FtwDevice
,
226 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*FvBlock
230 EFI_SWAP_ADDRESS_RANGE_PROTOCOL
*SarProtocol
;
231 EFI_PHYSICAL_ADDRESS BootBlockBase
;
233 EFI_PHYSICAL_ADDRESS BackupBlockBase
;
234 UINTN BackupBlockSize
;
235 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*BootFvb
;
237 EFI_HANDLE FvbHandle
;
239 if (!FeaturePcdGet(PcdFullFtwServiceEnable
)) {
243 Status
= FtwGetSarProtocol ((VOID
**) &SarProtocol
);
244 if (EFI_ERROR (Status
)) {
248 // Get the boot block range
250 Status
= SarProtocol
->GetRangeLocation (
257 if (EFI_ERROR (Status
)) {
261 Status
= SarProtocol
->GetSwapState (SarProtocol
, &IsSwapped
);
262 if (EFI_ERROR (Status
)) {
266 // Get FVB by address
269 FvbHandle
= GetFvbByAddress (BootBlockBase
, &BootFvb
);
271 FvbHandle
= GetFvbByAddress (BackupBlockBase
, &BootFvb
);
274 if (FvbHandle
== NULL
) {
280 return (BOOLEAN
) (FvBlock
== BootFvb
);
284 Copy the content of spare block to a boot block. Size is FTW_BLOCK_SIZE.
285 Spare block is accessed by FTW working FVB protocol interface.
286 Target block is accessed by FvBlock protocol interface.
288 FTW will do extra work on boot block update.
289 FTW should depend on a protocol of EFI_ADDRESS_RANGE_SWAP_PROTOCOL,
290 which is produced by a chipset driver.
291 FTW updating boot block steps may be:
292 1. GetRangeLocation(), if the Range is inside the boot block, FTW know
293 that boot block will be update. It shall add a FLAG in the working block.
294 2. When spare block is ready,
295 3. SetSwapState(SWAPPED)
296 4. erasing boot block,
297 5. programming boot block until the boot block is ok.
298 6. SetSwapState(UNSWAPPED)
299 FTW shall not allow to update boot block when battery state is error.
301 @param FtwDevice The private data of FTW driver
303 @retval EFI_SUCCESS Spare block content is copied to boot block
304 @retval EFI_INVALID_PARAMETER Input parameter error
305 @retval EFI_OUT_OF_RESOURCES Allocate memory error
306 @retval EFI_ABORTED The function could not complete successfully
310 FlushSpareBlockToBootBlock (
311 EFI_FTW_DEVICE
*FtwDevice
321 EFI_SWAP_ADDRESS_RANGE_PROTOCOL
*SarProtocol
;
322 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*BootFvb
;
325 if (!FeaturePcdGet(PcdFullFtwServiceEnable
)) {
326 return EFI_UNSUPPORTED
;
330 // Locate swap address range protocol
332 Status
= FtwGetSarProtocol ((VOID
**) &SarProtocol
);
333 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
;
345 // Get TopSwap bit state
347 Status
= SarProtocol
->GetSwapState (SarProtocol
, &TopSwap
);
348 if (EFI_ERROR (Status
)) {
349 DEBUG ((EFI_D_ERROR
, "Ftw: Get Top Swapped status - %r\n", Status
));
356 // Get FVB of current boot block
358 if (GetFvbByAddress (FtwDevice
->SpareAreaAddress
+ FtwDevice
->SpareAreaLength
, &BootFvb
) == NULL
) {
363 // Read data from current boot block
367 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
368 Count
= FtwDevice
->SpareBlockSize
;
369 Status
= BootFvb
->Read (
376 if (EFI_ERROR (Status
)) {
385 // Read data from spare block
388 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
389 Count
= FtwDevice
->SpareBlockSize
;
390 Status
= FtwDevice
->FtwBackupFvb
->Read (
391 FtwDevice
->FtwBackupFvb
,
392 FtwDevice
->FtwSpareLba
+ Index
,
397 if (EFI_ERROR (Status
)) {
407 Status
= SarProtocol
->SetSwapState (SarProtocol
, TRUE
);
408 if (EFI_ERROR (Status
)) {
414 // Erase current spare block
415 // Because TopSwap is set, this actually erase the top block (boot block)!
417 Status
= FtwEraseSpareBlock (FtwDevice
);
418 if (EFI_ERROR (Status
)) {
423 // Write memory buffer to current spare block. Still top block.
426 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
427 Count
= FtwDevice
->SpareBlockSize
;
428 Status
= FtwDevice
->FtwBackupFvb
->Write (
429 FtwDevice
->FtwBackupFvb
,
430 FtwDevice
->FtwSpareLba
+ Index
,
435 if (EFI_ERROR (Status
)) {
436 DEBUG ((EFI_D_ERROR
, "Ftw: FVB Write boot block - %r\n", Status
));
449 Status
= SarProtocol
->SetSwapState (SarProtocol
, FALSE
);
455 Copy the content of spare block to a target block.
456 Spare block is accessed by FTW backup FVB protocol interface.
457 Target block is accessed by FvBlock protocol interface.
460 @param FtwDevice The private data of FTW driver
461 @param FvBlock FVB Protocol interface to access target block
462 @param Lba Lba of the target block
463 @param BlockSize The size of the block
464 @param NumberOfBlocks The number of consecutive blocks starting with Lba
466 @retval EFI_SUCCESS Spare block content is copied to target block
467 @retval EFI_INVALID_PARAMETER Input parameter error
468 @retval EFI_OUT_OF_RESOURCES Allocate memory error
469 @retval EFI_ABORTED The function could not complete successfully
473 FlushSpareBlockToTargetBlock (
474 EFI_FTW_DEVICE
*FtwDevice
,
475 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*FvBlock
,
488 if ((FtwDevice
== NULL
) || (FvBlock
== NULL
)) {
489 return EFI_INVALID_PARAMETER
;
492 // Allocate a memory buffer
494 Length
= FtwDevice
->SpareAreaLength
;
495 Buffer
= AllocatePool (Length
);
496 if (Buffer
== NULL
) {
497 return EFI_OUT_OF_RESOURCES
;
500 // Read all content of spare block to memory buffer
503 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
504 Count
= FtwDevice
->SpareBlockSize
;
505 Status
= FtwDevice
->FtwBackupFvb
->Read (
506 FtwDevice
->FtwBackupFvb
,
507 FtwDevice
->FtwSpareLba
+ Index
,
512 if (EFI_ERROR (Status
)) {
520 // Erase the target block
522 Status
= FtwEraseBlock (FtwDevice
, FvBlock
, Lba
, NumberOfBlocks
);
523 if (EFI_ERROR (Status
)) {
528 // Write memory buffer to block, using the FvBlock protocol interface
531 for (Index
= 0; Index
< NumberOfBlocks
; Index
+= 1) {
533 Status
= FvBlock
->Write (FvBlock
, Lba
+ Index
, 0, &Count
, Ptr
);
534 if (EFI_ERROR (Status
)) {
535 DEBUG ((EFI_D_ERROR
, "Ftw: FVB Write block - %r\n", Status
));
549 Copy the content of spare block to working block. Size is FTW_BLOCK_SIZE.
550 Spare block is accessed by FTW backup FVB protocol interface. LBA is
551 FtwDevice->FtwSpareLba.
552 Working block is accessed by FTW working FVB protocol interface. LBA is
553 FtwDevice->FtwWorkBlockLba.
555 Since the working block header is important when FTW initializes, the
556 state of the operation should be handled carefully. The Crc value is
557 calculated without STATE element.
559 @param FtwDevice The private data of FTW driver
561 @retval EFI_SUCCESS Spare block content is copied to target block
562 @retval EFI_OUT_OF_RESOURCES Allocate memory error
563 @retval EFI_ABORTED The function could not complete successfully
567 FlushSpareBlockToWorkingBlock (
568 EFI_FTW_DEVICE
*FtwDevice
574 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*WorkingBlockHeader
;
580 // Allocate a memory buffer
582 Length
= FtwDevice
->SpareAreaLength
;
583 Buffer
= AllocatePool (Length
);
584 if (Buffer
== NULL
) {
585 return EFI_OUT_OF_RESOURCES
;
589 // To guarantee that the WorkingBlockValid is set on spare block
591 // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
592 // WorkingBlockValid);
593 // To skip Signature and Crc: sizeof(EFI_GUID)+sizeof(UINT32).
596 FtwDevice
->FtwBackupFvb
,
597 FtwDevice
->SpareBlockSize
,
598 FtwDevice
->FtwSpareLba
+ FtwDevice
->FtwWorkSpaceLbaInSpare
,
599 FtwDevice
->FtwWorkSpaceBaseInSpare
+ sizeof (EFI_GUID
) + sizeof (UINT32
),
603 // Read from spare block to memory buffer
606 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
607 Count
= FtwDevice
->SpareBlockSize
;
608 Status
= FtwDevice
->FtwBackupFvb
->Read (
609 FtwDevice
->FtwBackupFvb
,
610 FtwDevice
->FtwSpareLba
+ Index
,
615 if (EFI_ERROR (Status
)) {
623 // Clear the CRC and STATE, copy data from spare to working block.
625 WorkingBlockHeader
= (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*) (Buffer
+ (UINTN
) FtwDevice
->FtwWorkSpaceLbaInSpare
* FtwDevice
->SpareBlockSize
+ FtwDevice
->FtwWorkSpaceBaseInSpare
);
626 InitWorkSpaceHeader (WorkingBlockHeader
);
627 WorkingBlockHeader
->WorkingBlockValid
= FTW_ERASE_POLARITY
;
628 WorkingBlockHeader
->WorkingBlockInvalid
= FTW_ERASE_POLARITY
;
631 // target block is working block, then
632 // Set WorkingBlockInvalid in EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
633 // before erase the working block.
635 // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
636 // WorkingBlockInvalid);
637 // So hardcode offset as sizeof(EFI_GUID)+sizeof(UINT32) to
638 // skip Signature and Crc.
640 Status
= FtwUpdateFvState (
641 FtwDevice
->FtwFvBlock
,
642 FtwDevice
->WorkBlockSize
,
643 FtwDevice
->FtwWorkSpaceLba
,
644 FtwDevice
->FtwWorkSpaceBase
+ sizeof (EFI_GUID
) + sizeof (UINT32
),
645 WORKING_BLOCK_INVALID
647 if (EFI_ERROR (Status
)) {
652 FtwDevice
->FtwWorkSpaceHeader
->WorkingBlockInvalid
= FTW_VALID_STATE
;
655 // Erase the working block
657 Status
= FtwEraseBlock (FtwDevice
, FtwDevice
->FtwFvBlock
, FtwDevice
->FtwWorkBlockLba
, FtwDevice
->NumberOfWorkBlock
);
658 if (EFI_ERROR (Status
)) {
663 // Write memory buffer to working block, using the FvBlock protocol interface
666 for (Index
= 0; Index
< FtwDevice
->NumberOfWorkBlock
; Index
+= 1) {
667 Count
= FtwDevice
->WorkBlockSize
;
668 Status
= FtwDevice
->FtwFvBlock
->Write (
669 FtwDevice
->FtwFvBlock
,
670 FtwDevice
->FtwWorkBlockLba
+ Index
,
675 if (EFI_ERROR (Status
)) {
676 DEBUG ((EFI_D_ERROR
, "Ftw: FVB Write block - %r\n", Status
));
684 // Since the memory buffer will not be used, free memory Buffer.
689 // Update the VALID of the working block
691 // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER, WorkingBlockValid);
692 // So hardcode offset as sizeof(EFI_GUID)+sizeof(UINT32) to skip Signature and Crc.
694 Status
= FtwUpdateFvState (
695 FtwDevice
->FtwFvBlock
,
696 FtwDevice
->WorkBlockSize
,
697 FtwDevice
->FtwWorkSpaceLba
,
698 FtwDevice
->FtwWorkSpaceBase
+ sizeof (EFI_GUID
) + sizeof (UINT32
),
701 if (EFI_ERROR (Status
)) {
705 FtwDevice
->FtwWorkSpaceHeader
->WorkingBlockInvalid
= FTW_INVALID_STATE
;
706 FtwDevice
->FtwWorkSpaceHeader
->WorkingBlockValid
= FTW_VALID_STATE
;
712 Update a bit of state on a block device. The location of the bit is
713 calculated by the (Lba, Offset, bit). Here bit is determined by the
714 the name of a certain bit.
717 @param FvBlock FVB Protocol interface to access SrcBlock and DestBlock
718 @param BlockSize The size of the block
719 @param Lba Lba of a block
720 @param Offset Offset on the Lba
721 @param NewBit New value that will override the old value if it can be change
723 @retval EFI_SUCCESS A state bit has been updated successfully
724 @retval Others Access block device error.
726 Assume all bits of State are inside the same BYTE.
727 @retval EFI_ABORTED Read block fail
732 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*FvBlock
,
744 // Calculate the real Offset and Lba to write.
746 while (Offset
>= BlockSize
) {
752 // Read state from device, assume State is only one byte.
754 Length
= sizeof (UINT8
);
755 Status
= FvBlock
->Read (FvBlock
, Lba
, Offset
, &Length
, &State
);
756 if (EFI_ERROR (Status
)) {
760 State
^= FTW_POLARITY_REVERT
;
761 State
= (UINT8
) (State
| NewBit
);
762 State
^= FTW_POLARITY_REVERT
;
765 // Write state back to device
767 Length
= sizeof (UINT8
);
768 Status
= FvBlock
->Write (FvBlock
, Lba
, Offset
, &Length
, &State
);
774 Get the last Write Header pointer.
775 The last write header is the header whose 'complete' state hasn't been set.
776 After all, this header may be a EMPTY header entry for next Allocate.
779 @param FtwWorkSpaceHeader Pointer of the working block header
780 @param FtwWorkSpaceSize Size of the work space
781 @param FtwWriteHeader Pointer to retrieve the last write header
783 @retval EFI_SUCCESS Get the last write record successfully
784 @retval EFI_ABORTED The FTW work space is damaged
788 FtwGetLastWriteHeader (
789 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*FtwWorkSpaceHeader
,
790 IN UINTN FtwWorkSpaceSize
,
791 OUT EFI_FAULT_TOLERANT_WRITE_HEADER
**FtwWriteHeader
795 EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwHeader
;
797 *FtwWriteHeader
= NULL
;
798 FtwHeader
= (EFI_FAULT_TOLERANT_WRITE_HEADER
*) (FtwWorkSpaceHeader
+ 1);
799 Offset
= sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
);
801 while (FtwHeader
->Complete
== FTW_VALID_STATE
) {
802 Offset
+= FTW_WRITE_TOTAL_SIZE (FtwHeader
->NumberOfWrites
, FtwHeader
->PrivateDataSize
);
804 // If Offset exceed the FTW work space boudary, return error.
806 if (Offset
>= FtwWorkSpaceSize
) {
807 *FtwWriteHeader
= FtwHeader
;
811 FtwHeader
= (EFI_FAULT_TOLERANT_WRITE_HEADER
*) ((UINT8
*) FtwWorkSpaceHeader
+ Offset
);
814 // Last write header is found
816 *FtwWriteHeader
= FtwHeader
;
822 Get the last Write Record pointer. The last write Record is the Record
823 whose DestinationCompleted state hasn't been set. After all, this Record
824 may be a EMPTY record entry for next write.
827 @param FtwWriteHeader Pointer to the write record header
828 @param FtwWriteRecord Pointer to retrieve the last write record
830 @retval EFI_SUCCESS Get the last write record successfully
831 @retval EFI_ABORTED The FTW work space is damaged
835 FtwGetLastWriteRecord (
836 IN EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwWriteHeader
,
837 OUT EFI_FAULT_TOLERANT_WRITE_RECORD
**FtwWriteRecord
841 EFI_FAULT_TOLERANT_WRITE_RECORD
*FtwRecord
;
843 *FtwWriteRecord
= NULL
;
844 FtwRecord
= (EFI_FAULT_TOLERANT_WRITE_RECORD
*) (FtwWriteHeader
+ 1);
847 // Try to find the last write record "that has not completed"
849 for (Index
= 0; Index
< FtwWriteHeader
->NumberOfWrites
; Index
+= 1) {
850 if (FtwRecord
->DestinationComplete
!= FTW_VALID_STATE
) {
852 // The last write record is found
854 *FtwWriteRecord
= FtwRecord
;
860 if (FtwWriteHeader
->PrivateDataSize
!= 0) {
861 FtwRecord
= (EFI_FAULT_TOLERANT_WRITE_RECORD
*) ((UINTN
) FtwRecord
+ (UINTN
) FtwWriteHeader
->PrivateDataSize
);
865 // if Index == NumberOfWrites, then
866 // the last record has been written successfully,
867 // but the Header->Complete Flag has not been set.
868 // also return the last record.
870 if (Index
== FtwWriteHeader
->NumberOfWrites
) {
871 *FtwWriteRecord
= (EFI_FAULT_TOLERANT_WRITE_RECORD
*) ((UINTN
) FtwRecord
- FTW_RECORD_SIZE (FtwWriteHeader
->PrivateDataSize
));
879 To check if FtwRecord is the first record of FtwHeader.
881 @param FtwHeader Pointer to the write record header
882 @param FtwRecord Pointer to the write record
884 @retval TRUE FtwRecord is the first Record of the FtwHeader
885 @retval FALSE FtwRecord is not the first Record of the FtwHeader
889 IsFirstRecordOfWrites (
890 IN EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwHeader
,
891 IN EFI_FAULT_TOLERANT_WRITE_RECORD
*FtwRecord
897 Head
= (UINT8
*) FtwHeader
;
898 Ptr
= (UINT8
*) FtwRecord
;
900 Head
+= sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER
);
901 return (BOOLEAN
) (Head
== Ptr
);
905 To check if FtwRecord is the last record of FtwHeader. Because the
906 FtwHeader has NumberOfWrites & PrivateDataSize, the FtwRecord can be
907 determined if it is the last record of FtwHeader.
909 @param FtwHeader Pointer to the write record header
910 @param FtwRecord Pointer to the write record
912 @retval TRUE FtwRecord is the last Record of the FtwHeader
913 @retval FALSE FtwRecord is not the last Record of the FtwHeader
917 IsLastRecordOfWrites (
918 IN EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwHeader
,
919 IN EFI_FAULT_TOLERANT_WRITE_RECORD
*FtwRecord
925 Head
= (UINT8
*) FtwHeader
;
926 Ptr
= (UINT8
*) FtwRecord
;
928 Head
+= FTW_WRITE_TOTAL_SIZE (FtwHeader
->NumberOfWrites
- 1, FtwHeader
->PrivateDataSize
);
929 return (BOOLEAN
) (Head
== Ptr
);
933 To check if FtwRecord is the first record of FtwHeader.
935 @param FtwHeader Pointer to the write record header
936 @param FtwRecord Pointer to retrieve the previous write record
938 @retval EFI_ACCESS_DENIED Input record is the first record, no previous record is return.
939 @retval EFI_SUCCESS The previous write record is found.
943 GetPreviousRecordOfWrites (
944 IN EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwHeader
,
945 IN OUT EFI_FAULT_TOLERANT_WRITE_RECORD
**FtwRecord
950 if (IsFirstRecordOfWrites (FtwHeader
, *FtwRecord
)) {
952 return EFI_ACCESS_DENIED
;
955 Ptr
= (UINT8
*) (*FtwRecord
);
956 Ptr
-= FTW_RECORD_SIZE (FtwHeader
->PrivateDataSize
);
957 *FtwRecord
= (EFI_FAULT_TOLERANT_WRITE_RECORD
*) Ptr
;
962 Allocate private data for FTW driver and initialize it.
964 @param[out] FtwData Pointer to the FTW device structure
966 @retval EFI_SUCCESS Initialize the FTW device successfully.
967 @retval EFI_OUT_OF_RESOURCES Allocate memory error
968 @retval EFI_INVALID_PARAMETER Workspace or Spare block does not exist
973 OUT EFI_FTW_DEVICE
**FtwData
976 EFI_FTW_DEVICE
*FtwDevice
;
979 // Allocate private data of this driver,
980 // Including the FtwWorkSpace[FTW_WORK_SPACE_SIZE].
982 FtwDevice
= AllocateZeroPool (sizeof (EFI_FTW_DEVICE
) + PcdGet32 (PcdFlashNvStorageFtwWorkingSize
));
983 if (FtwDevice
== NULL
) {
984 return EFI_OUT_OF_RESOURCES
;
988 // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.
990 FtwDevice
->WorkSpaceLength
= (UINTN
) PcdGet32 (PcdFlashNvStorageFtwWorkingSize
);
991 FtwDevice
->SpareAreaLength
= (UINTN
) PcdGet32 (PcdFlashNvStorageFtwSpareSize
);
992 if ((FtwDevice
->WorkSpaceLength
== 0) || (FtwDevice
->SpareAreaLength
== 0)) {
993 DEBUG ((EFI_D_ERROR
, "Ftw: Workspace or Spare block does not exist!\n"));
994 FreePool (FtwDevice
);
995 return EFI_INVALID_PARAMETER
;
998 FtwDevice
->Signature
= FTW_DEVICE_SIGNATURE
;
999 FtwDevice
->FtwFvBlock
= NULL
;
1000 FtwDevice
->FtwBackupFvb
= NULL
;
1001 FtwDevice
->FtwWorkSpaceLba
= (EFI_LBA
) (-1);
1002 FtwDevice
->FtwSpareLba
= (EFI_LBA
) (-1);
1004 FtwDevice
->WorkSpaceAddress
= (EFI_PHYSICAL_ADDRESS
) PcdGet64 (PcdFlashNvStorageFtwWorkingBase64
);
1005 if (FtwDevice
->WorkSpaceAddress
== 0) {
1006 FtwDevice
->WorkSpaceAddress
= (EFI_PHYSICAL_ADDRESS
) PcdGet32 (PcdFlashNvStorageFtwWorkingBase
);
1009 FtwDevice
->SpareAreaAddress
= (EFI_PHYSICAL_ADDRESS
) PcdGet64 (PcdFlashNvStorageFtwSpareBase64
);
1010 if (FtwDevice
->SpareAreaAddress
== 0) {
1011 FtwDevice
->SpareAreaAddress
= (EFI_PHYSICAL_ADDRESS
) PcdGet32 (PcdFlashNvStorageFtwSpareBase
);
1014 *FtwData
= FtwDevice
;
1020 Find the proper Firmware Volume Block protocol for FTW operation.
1022 @param[in, out] FtwDevice Pointer to the FTW device structure
1024 @retval EFI_SUCCESS Find the FVB protocol successfully.
1025 @retval EFI_NOT_FOUND No proper FVB protocol was found.
1026 @retval EFI_ABORTED Some data can not be got or be invalid.
1031 IN OUT EFI_FTW_DEVICE
*FtwDevice
1035 EFI_HANDLE
*HandleBuffer
;
1038 EFI_PHYSICAL_ADDRESS FvbBaseAddress
;
1039 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*Fvb
;
1040 EFI_FVB_ATTRIBUTES_2 Attributes
;
1043 UINTN NumberOfBlocks
;
1045 HandleBuffer
= NULL
;
1048 // Get all FVB handle.
1050 Status
= GetFvbCountAndBuffer (&HandleCount
, &HandleBuffer
);
1051 if (EFI_ERROR (Status
)) {
1052 return EFI_NOT_FOUND
;
1056 // Get the FVB to access variable store
1059 for (Index
= 0; Index
< HandleCount
; Index
+= 1) {
1060 Status
= FtwGetFvbByHandle (HandleBuffer
[Index
], &Fvb
);
1061 if (EFI_ERROR (Status
)) {
1062 Status
= EFI_NOT_FOUND
;
1067 // Ensure this FVB protocol support Write operation.
1069 Status
= Fvb
->GetAttributes (Fvb
, &Attributes
);
1070 if (EFI_ERROR (Status
) || ((Attributes
& EFI_FVB2_WRITE_STATUS
) == 0)) {
1074 // Compare the address and select the right one
1076 Status
= Fvb
->GetPhysicalAddress (Fvb
, &FvbBaseAddress
);
1077 if (EFI_ERROR (Status
)) {
1082 // Now, one FVB has one type of BlockSize.
1084 Status
= Fvb
->GetBlockSize (Fvb
, 0, &BlockSize
, &NumberOfBlocks
);
1085 if (EFI_ERROR (Status
)) {
1089 if ((FtwDevice
->FtwFvBlock
== NULL
) && (FtwDevice
->WorkSpaceAddress
>= FvbBaseAddress
) &&
1090 ((FtwDevice
->WorkSpaceAddress
+ FtwDevice
->WorkSpaceLength
) <= (FvbBaseAddress
+ BlockSize
* NumberOfBlocks
))) {
1091 FtwDevice
->FtwFvBlock
= Fvb
;
1093 // To get the LBA of work space
1095 for (LbaIndex
= 1; LbaIndex
<= NumberOfBlocks
; LbaIndex
+= 1) {
1096 if ((FtwDevice
->WorkSpaceAddress
>= (FvbBaseAddress
+ BlockSize
* (LbaIndex
- 1)))
1097 && (FtwDevice
->WorkSpaceAddress
< (FvbBaseAddress
+ BlockSize
* LbaIndex
))) {
1098 FtwDevice
->FtwWorkSpaceLba
= LbaIndex
- 1;
1100 // Get the Work space size and Base(Offset)
1102 FtwDevice
->FtwWorkSpaceSize
= FtwDevice
->WorkSpaceLength
;
1103 FtwDevice
->WorkBlockSize
= BlockSize
;
1104 FtwDevice
->FtwWorkSpaceBase
= (UINTN
) (FtwDevice
->WorkSpaceAddress
- (FvbBaseAddress
+ FtwDevice
->WorkBlockSize
* (LbaIndex
- 1)));
1105 FtwDevice
->NumberOfWorkSpaceBlock
= FTW_BLOCKS (FtwDevice
->FtwWorkSpaceBase
+ FtwDevice
->FtwWorkSpaceSize
, FtwDevice
->WorkBlockSize
);
1106 if (FtwDevice
->FtwWorkSpaceSize
>= FtwDevice
->WorkBlockSize
) {
1108 // 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.
1110 if (((FtwDevice
->WorkSpaceAddress
& (FtwDevice
->WorkBlockSize
- 1)) != 0) ||
1111 ((FtwDevice
->WorkSpaceLength
& (FtwDevice
->WorkBlockSize
- 1)) != 0)) {
1112 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"));
1113 FreePool (HandleBuffer
);
1117 } else if ((FtwDevice
->FtwWorkSpaceBase
+ FtwDevice
->FtwWorkSpaceSize
) > FtwDevice
->WorkBlockSize
) {
1118 DEBUG ((EFI_D_ERROR
, "Ftw: The work space range should not span blocks when work space size is less than one block size\n"));
1119 FreePool (HandleBuffer
);
1128 if ((FtwDevice
->FtwBackupFvb
== NULL
) && (FtwDevice
->SpareAreaAddress
>= FvbBaseAddress
) &&
1129 ((FtwDevice
->SpareAreaAddress
+ FtwDevice
->SpareAreaLength
) <= (FvbBaseAddress
+ BlockSize
* NumberOfBlocks
))) {
1130 FtwDevice
->FtwBackupFvb
= Fvb
;
1132 // To get the LBA of spare
1134 for (LbaIndex
= 1; LbaIndex
<= NumberOfBlocks
; LbaIndex
+= 1) {
1135 if ((FtwDevice
->SpareAreaAddress
>= (FvbBaseAddress
+ BlockSize
* (LbaIndex
- 1)))
1136 && (FtwDevice
->SpareAreaAddress
< (FvbBaseAddress
+ BlockSize
* LbaIndex
))) {
1138 // Get the NumberOfSpareBlock and BlockSize
1140 FtwDevice
->FtwSpareLba
= LbaIndex
- 1;
1141 FtwDevice
->SpareBlockSize
= BlockSize
;
1142 FtwDevice
->NumberOfSpareBlock
= FtwDevice
->SpareAreaLength
/ FtwDevice
->SpareBlockSize
;
1144 // Check the range of spare area to make sure that it's in FV range
1146 if ((FtwDevice
->FtwSpareLba
+ FtwDevice
->NumberOfSpareBlock
) > NumberOfBlocks
) {
1147 DEBUG ((EFI_D_ERROR
, "Ftw: Spare area is out of FV range\n"));
1148 FreePool (HandleBuffer
);
1153 // Check the alignment of spare area address and length, they should be block size aligned
1155 if (((FtwDevice
->SpareAreaAddress
& (FtwDevice
->SpareBlockSize
- 1)) != 0) ||
1156 ((FtwDevice
->SpareAreaLength
& (FtwDevice
->SpareBlockSize
- 1)) != 0)) {
1157 DEBUG ((EFI_D_ERROR
, "Ftw: Spare area address or length is not block size aligned\n"));
1158 FreePool (HandleBuffer
);
1160 // Report Status Code EFI_SW_EC_ABORTED.
1162 REPORT_STATUS_CODE ((EFI_ERROR_CODE
| EFI_ERROR_UNRECOVERED
), (EFI_SOFTWARE_DXE_BS_DRIVER
| EFI_SW_EC_ABORTED
));
1171 FreePool (HandleBuffer
);
1173 if ((FtwDevice
->FtwBackupFvb
== NULL
) || (FtwDevice
->FtwFvBlock
== NULL
) ||
1174 (FtwDevice
->FtwWorkSpaceLba
== (EFI_LBA
) (-1)) || (FtwDevice
->FtwSpareLba
== (EFI_LBA
) (-1))) {
1177 DEBUG ((EFI_D_INFO
, "Ftw: FtwWorkSpaceLba - 0x%lx, WorkBlockSize - 0x%x, FtwWorkSpaceBase - 0x%x\n", FtwDevice
->FtwWorkSpaceLba
, FtwDevice
->WorkBlockSize
, FtwDevice
->FtwWorkSpaceBase
));
1178 DEBUG ((EFI_D_INFO
, "Ftw: FtwSpareLba - 0x%lx, SpareBlockSize - 0x%x\n", FtwDevice
->FtwSpareLba
, FtwDevice
->SpareBlockSize
));
1185 Initialization for Fault Tolerant Write protocol.
1187 @param[in, out] FtwDevice Pointer to the FTW device structure
1189 @retval EFI_SUCCESS Initialize the FTW protocol successfully.
1190 @retval EFI_NOT_FOUND No proper FVB protocol was found.
1195 IN OUT EFI_FTW_DEVICE
*FtwDevice
1199 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*Fvb
;
1200 EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwHeader
;
1202 EFI_HANDLE FvbHandle
;
1203 EFI_LBA WorkSpaceLbaOffset
;
1206 // Find the right SMM Fvb protocol instance for FTW.
1208 Status
= FindFvbForFtw (FtwDevice
);
1209 if (EFI_ERROR (Status
)) {
1210 return EFI_NOT_FOUND
;
1214 // Calculate the start LBA of working block.
1216 if (FtwDevice
->FtwWorkSpaceSize
>= FtwDevice
->WorkBlockSize
) {
1218 // Working block is a standalone area which only contains working space.
1220 FtwDevice
->NumberOfWorkBlock
= FtwDevice
->NumberOfWorkSpaceBlock
;
1223 // Working block is an area which
1224 // contains working space in its last block and has the same size as spare
1225 // block, unless there are not enough blocks before the block that contains
1228 FtwDevice
->NumberOfWorkBlock
= (UINTN
) (FtwDevice
->FtwWorkSpaceLba
+ FtwDevice
->NumberOfWorkSpaceBlock
);
1229 while (FtwDevice
->NumberOfWorkBlock
* FtwDevice
->WorkBlockSize
> FtwDevice
->SpareAreaLength
) {
1230 FtwDevice
->NumberOfWorkBlock
--;
1233 FtwDevice
->FtwWorkBlockLba
= FtwDevice
->FtwWorkSpaceLba
+ FtwDevice
->NumberOfWorkSpaceBlock
- FtwDevice
->NumberOfWorkBlock
;
1234 DEBUG ((EFI_D_INFO
, "Ftw: NumberOfWorkBlock - 0x%x, FtwWorkBlockLba - 0x%lx\n", FtwDevice
->NumberOfWorkBlock
, FtwDevice
->FtwWorkBlockLba
));
1237 // Calcualte the LBA and base of work space in spare block.
1238 // Note: Do not assume Spare Block and Work Block have same block size.
1240 WorkSpaceLbaOffset
= FtwDevice
->FtwWorkSpaceLba
- FtwDevice
->FtwWorkBlockLba
;
1241 FtwDevice
->FtwWorkSpaceLbaInSpare
= (EFI_LBA
) (((UINTN
) WorkSpaceLbaOffset
* FtwDevice
->WorkBlockSize
+ FtwDevice
->FtwWorkSpaceBase
) / FtwDevice
->SpareBlockSize
);
1242 FtwDevice
->FtwWorkSpaceBaseInSpare
= ((UINTN
) WorkSpaceLbaOffset
* FtwDevice
->WorkBlockSize
+ FtwDevice
->FtwWorkSpaceBase
) % FtwDevice
->SpareBlockSize
;
1243 DEBUG ((EFI_D_INFO
, "Ftw: WorkSpaceLbaInSpare - 0x%lx, WorkSpaceBaseInSpare - 0x%x\n", FtwDevice
->FtwWorkSpaceLbaInSpare
, FtwDevice
->FtwWorkSpaceBaseInSpare
));
1246 // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.
1248 FtwDevice
->FtwWorkSpace
= (UINT8
*) (FtwDevice
+ 1);
1249 FtwDevice
->FtwWorkSpaceHeader
= (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*) FtwDevice
->FtwWorkSpace
;
1251 FtwDevice
->FtwLastWriteHeader
= NULL
;
1252 FtwDevice
->FtwLastWriteRecord
= NULL
;
1254 InitializeLocalWorkSpaceHeader ();
1257 // Refresh the working space data from working block
1259 Status
= WorkSpaceRefresh (FtwDevice
);
1260 ASSERT_EFI_ERROR (Status
);
1262 // If the working block workspace is not valid, try the spare block
1264 if (!IsValidWorkSpace (FtwDevice
->FtwWorkSpaceHeader
)) {
1266 // Read from spare block
1268 Status
= ReadWorkSpaceData (
1269 FtwDevice
->FtwBackupFvb
,
1270 FtwDevice
->SpareBlockSize
,
1271 FtwDevice
->FtwSpareLba
+ FtwDevice
->FtwWorkSpaceLbaInSpare
,
1272 FtwDevice
->FtwWorkSpaceBaseInSpare
,
1273 FtwDevice
->FtwWorkSpaceSize
,
1274 FtwDevice
->FtwWorkSpace
1276 ASSERT_EFI_ERROR (Status
);
1279 // If spare block is valid, then replace working block content.
1281 if (IsValidWorkSpace (FtwDevice
->FtwWorkSpaceHeader
)) {
1282 Status
= FlushSpareBlockToWorkingBlock (FtwDevice
);
1283 DEBUG ((EFI_D_INFO
, "Ftw: Restart working block update in %a() - %r\n",
1284 __FUNCTION__
, Status
));
1285 FtwAbort (&FtwDevice
->FtwInstance
);
1287 // Refresh work space.
1289 Status
= WorkSpaceRefresh (FtwDevice
);
1290 ASSERT_EFI_ERROR (Status
);
1293 "Ftw: Both working and spare blocks are invalid, init workspace\n"));
1295 // If both are invalid, then initialize work space.
1298 FtwDevice
->FtwWorkSpace
,
1299 FtwDevice
->FtwWorkSpaceSize
,
1302 InitWorkSpaceHeader (FtwDevice
->FtwWorkSpaceHeader
);
1304 // Initialize the work space
1306 Status
= FtwReclaimWorkSpace (FtwDevice
, FALSE
);
1307 ASSERT_EFI_ERROR (Status
);
1311 // If the FtwDevice->FtwLastWriteRecord is 1st record of write header &&
1312 // (! SpareComplete) THEN call Abort().
1314 if ((FtwDevice
->FtwLastWriteHeader
->HeaderAllocated
== FTW_VALID_STATE
) &&
1315 (FtwDevice
->FtwLastWriteRecord
->SpareComplete
!= FTW_VALID_STATE
) &&
1316 IsFirstRecordOfWrites (FtwDevice
->FtwLastWriteHeader
, FtwDevice
->FtwLastWriteRecord
)
1318 DEBUG ((EFI_D_ERROR
, "Ftw: Init.. find first record not SpareCompleted, abort()\n"));
1319 FtwAbort (&FtwDevice
->FtwInstance
);
1322 // If Header is incompleted and the last record has completed, then
1323 // call Abort() to set the Header->Complete FLAG.
1325 if ((FtwDevice
->FtwLastWriteHeader
->Complete
!= FTW_VALID_STATE
) &&
1326 (FtwDevice
->FtwLastWriteRecord
->DestinationComplete
== FTW_VALID_STATE
) &&
1327 IsLastRecordOfWrites (FtwDevice
->FtwLastWriteHeader
, FtwDevice
->FtwLastWriteRecord
)
1329 DEBUG ((EFI_D_ERROR
, "Ftw: Init.. find last record completed but header not, abort()\n"));
1330 FtwAbort (&FtwDevice
->FtwInstance
);
1333 // To check the workspace buffer following last Write header/records is EMPTY or not.
1334 // If it's not EMPTY, FTW also need to call reclaim().
1336 FtwHeader
= FtwDevice
->FtwLastWriteHeader
;
1337 Offset
= (UINT8
*) FtwHeader
- FtwDevice
->FtwWorkSpace
;
1338 if (FtwDevice
->FtwWorkSpace
[Offset
] != FTW_ERASED_BYTE
) {
1339 Offset
+= FTW_WRITE_TOTAL_SIZE (FtwHeader
->NumberOfWrites
, FtwHeader
->PrivateDataSize
);
1342 if (!IsErasedFlashBuffer (FtwDevice
->FtwWorkSpace
+ Offset
, FtwDevice
->FtwWorkSpaceSize
- Offset
)) {
1343 Status
= FtwReclaimWorkSpace (FtwDevice
, TRUE
);
1344 ASSERT_EFI_ERROR (Status
);
1348 // Restart if it's boot block
1350 if ((FtwDevice
->FtwLastWriteHeader
->Complete
!= FTW_VALID_STATE
) &&
1351 (FtwDevice
->FtwLastWriteRecord
->SpareComplete
== FTW_VALID_STATE
)
1353 if (FtwDevice
->FtwLastWriteRecord
->BootBlockUpdate
== FTW_VALID_STATE
) {
1354 Status
= FlushSpareBlockToBootBlock (FtwDevice
);
1355 DEBUG ((EFI_D_ERROR
, "Ftw: Restart boot block update - %r\n", Status
));
1356 ASSERT_EFI_ERROR (Status
);
1357 FtwAbort (&FtwDevice
->FtwInstance
);
1360 // if (SpareCompleted) THEN Restart to fault tolerant write.
1363 FvbHandle
= GetFvbByAddress ((EFI_PHYSICAL_ADDRESS
) (UINTN
) ((INT64
) FtwDevice
->SpareAreaAddress
+ FtwDevice
->FtwLastWriteRecord
->RelativeOffset
), &Fvb
);
1364 if (FvbHandle
!= NULL
) {
1365 Status
= FtwRestart (&FtwDevice
->FtwInstance
, FvbHandle
);
1366 DEBUG ((EFI_D_ERROR
, "Ftw: Restart last write - %r\n", Status
));
1367 ASSERT_EFI_ERROR (Status
);
1369 FtwAbort (&FtwDevice
->FtwInstance
);
1373 // Hook the protocol API
1375 FtwDevice
->FtwInstance
.GetMaxBlockSize
= FtwGetMaxBlockSize
;
1376 FtwDevice
->FtwInstance
.Allocate
= FtwAllocate
;
1377 FtwDevice
->FtwInstance
.Write
= FtwWrite
;
1378 FtwDevice
->FtwInstance
.Restart
= FtwRestart
;
1379 FtwDevice
->FtwInstance
.Abort
= FtwAbort
;
1380 FtwDevice
->FtwInstance
.GetLastWrite
= FtwGetLastWrite
;