3 This is a simple fault tolerant write driver.
5 This boot service protocol only provides fault tolerant write capability for
6 block devices. The protocol has internal non-volatile intermediate storage
7 of the data and private information. It should be able to recover
8 automatically from a critical fault, such as power failure.
10 The implementation uses an FTW (Fault Tolerant Write) Work Space.
11 This work space is a memory copy of the work space on the Working Block,
12 the size of the work space is the FTW_WORK_SPACE_SIZE bytes.
14 The work space stores each write record as EFI_FTW_RECORD structure.
15 The spare block stores the write buffer before write to the target block.
17 The write record has three states to specify the different phase of write operation.
18 1) WRITE_ALLOCATED is that the record is allocated in write space.
19 The information of write operation is stored in write record structure.
20 2) SPARE_COMPLETED is that the data from write buffer is writed into the spare block as the backup.
21 3) WRITE_COMPLETED is that the data is copied from the spare block to the target block.
23 This driver operates the data as the whole size of spare block.
24 It first read the SpareAreaLength data from the target block into the spare memory buffer.
25 Then copy the write buffer data into the spare memory buffer.
26 Then write the spare memory buffer into the spare block.
27 Final copy the data from the spare block to the target block.
29 To make this drive work well, the following conditions must be satisfied:
30 1. The write NumBytes data must be fit within Spare area.
31 Offset + NumBytes <= SpareAreaLength
32 2. The whole flash range has the same block size.
33 3. Working block is an area which contains working space in its last block and has the same size as spare block.
34 4. Working Block area must be in the single one Firmware Volume Block range which FVB protocol is produced on.
35 5. Spare area must be in the single one Firmware Volume Block range which FVB protocol is produced on.
36 6. Any write data area (SpareAreaLength Area) which the data will be written into must be
37 in the single one Firmware Volume Block range which FVB protocol is produced on.
38 7. If write data area (such as Variable range) is enlarged, the spare area range must be enlarged.
39 The spare area must be enough large to store the write data before write them into the target range.
40 If one of them is not satisfied, FtwWrite may fail.
41 Usually, Spare area only takes one block. That's SpareAreaLength = BlockSize, NumberOfSpareBlock = 1.
43 Copyright (c) 2006 - 2009, Intel Corporation
44 All rights reserved. This program and the accompanying materials
45 are licensed and made available under the terms and conditions of the BSD License
46 which accompanies this distribution. The full text of the license may be found at
47 http://opensource.org/licenses/bsd-license.php
49 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
50 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
54 #include "FaultTolerantWrite.h"
58 // Fault Tolerant Write Protocol API
61 Query the largest block that may be updated in a fault tolerant manner.
64 @param This The pointer to this protocol instance.
65 @param BlockSize A pointer to a caller allocated UINTN that is updated to
66 indicate the size of the largest block that can be updated.
68 @return EFI_SUCCESS The function completed successfully
74 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL
*This
,
78 EFI_FTW_DEVICE
*FtwDevice
;
80 if (!FeaturePcdGet(PcdFullFtwServiceEnable
)) {
81 return EFI_UNSUPPORTED
;
84 FtwDevice
= FTW_CONTEXT_FROM_THIS (This
);
86 *BlockSize
= FtwDevice
->SpareAreaLength
;
92 Allocates space for the protocol to maintain information about writes.
93 Since writes must be completed in a fault tolerant manner and multiple
94 updates will require more resources to be successful, this function
95 enables the protocol to ensure that enough space exists to track
96 information about the upcoming writes.
98 All writes must be completed or aborted before another fault tolerant write can occur.
100 @param This The pointer to this protocol instance.
101 @param CallerId The GUID identifying the write.
102 @param PrivateDataSize The size of the caller's private data
103 that must be recorded for each write.
104 @param NumberOfWrites The number of fault tolerant block writes
105 that will need to occur.
107 @return EFI_SUCCESS The function completed successfully
108 @retval EFI_ABORTED The function could not complete successfully.
109 @retval EFI_ACCESS_DENIED All allocated writes have not been completed.
115 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL
*This
,
116 IN EFI_GUID
*CallerId
,
117 IN UINTN PrivateDataSize
,
118 IN UINTN NumberOfWrites
124 EFI_FTW_DEVICE
*FtwDevice
;
125 EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwHeader
;
127 FtwDevice
= FTW_CONTEXT_FROM_THIS (This
);
129 Status
= WorkSpaceRefresh (FtwDevice
);
130 if (EFI_ERROR (Status
)) {
134 // Check if there is enough space for the coming allocation
136 if (WRITE_TOTAL_SIZE (NumberOfWrites
, PrivateDataSize
) > FtwDevice
->FtwWorkSpaceHeader
->WriteQueueSize
) {
137 DEBUG ((EFI_D_ERROR
, "Ftw: Allocate() request exceed Workspace, Caller: %g\n", CallerId
));
138 return EFI_BUFFER_TOO_SMALL
;
141 // Find the last write header and record.
142 // If the FtwHeader is complete, skip the completed last write header/records
144 FtwHeader
= FtwDevice
->FtwLastWriteHeader
;
147 // Previous write has not completed, access denied.
149 if ((FtwHeader
->HeaderAllocated
== FTW_VALID_STATE
) || (FtwHeader
->WritesAllocated
== FTW_VALID_STATE
)) {
150 return EFI_ACCESS_DENIED
;
153 // If workspace is not enough, then reclaim workspace
155 Offset
= (UINT8
*) FtwHeader
- (UINT8
*) FtwDevice
->FtwWorkSpace
;
156 if (Offset
+ WRITE_TOTAL_SIZE (NumberOfWrites
, PrivateDataSize
) > FtwDevice
->FtwWorkSpaceSize
) {
157 Status
= FtwReclaimWorkSpace (FtwDevice
, TRUE
);
158 if (EFI_ERROR (Status
)) {
162 FtwHeader
= FtwDevice
->FtwLastWriteHeader
;
165 // Prepare FTW write header,
166 // overwrite the buffer and write to workspace.
168 FtwHeader
->WritesAllocated
= FTW_INVALID_STATE
;
169 FtwHeader
->Complete
= FTW_INVALID_STATE
;
170 CopyMem (&FtwHeader
->CallerId
, CallerId
, sizeof (EFI_GUID
));
171 FtwHeader
->NumberOfWrites
= NumberOfWrites
;
172 FtwHeader
->PrivateDataSize
= PrivateDataSize
;
173 FtwHeader
->HeaderAllocated
= FTW_VALID_STATE
;
175 Length
= sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER
);
176 Status
= FtwDevice
->FtwFvBlock
->Write (
177 FtwDevice
->FtwFvBlock
,
178 FtwDevice
->FtwWorkSpaceLba
,
179 FtwDevice
->FtwWorkSpaceBase
+ Offset
,
183 if (EFI_ERROR (Status
)) {
187 // Update Header->WriteAllocated as VALID
189 Status
= FtwUpdateFvState (
190 FtwDevice
->FtwFvBlock
,
191 FtwDevice
->FtwWorkSpaceLba
,
192 FtwDevice
->FtwWorkSpaceBase
+ Offset
,
195 if (EFI_ERROR (Status
)) {
201 "Ftw: Allocate() success, Caller:%g, # %d\n",
211 Write a record with fault tolerant mannaer.
212 Since the content has already backuped in spare block, the write is
213 guaranteed to be completed with fault tolerant manner.
215 @param This The pointer to this protocol instance.
216 @param Fvb The FVB protocol that provides services for
217 reading, writing, and erasing the target block.
219 @retval EFI_SUCCESS The function completed successfully
220 @retval EFI_ABORTED The function could not complete successfully
225 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL
*This
,
226 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*Fvb
230 EFI_FTW_DEVICE
*FtwDevice
;
231 EFI_FAULT_TOLERANT_WRITE_HEADER
*Header
;
232 EFI_FAULT_TOLERANT_WRITE_RECORD
*Record
;
235 FtwDevice
= FTW_CONTEXT_FROM_THIS (This
);
238 // Spare Complete but Destination not complete,
239 // Recover the targt block with the spare block.
241 Header
= FtwDevice
->FtwLastWriteHeader
;
242 Record
= FtwDevice
->FtwLastWriteRecord
;
245 // IF target block is working block, THEN Flush Spare Block To Working Block;
246 // ELSE flush spare block to target block, which may be boot block after all.
248 if (IsWorkingBlock (FtwDevice
, Fvb
, Record
->Lba
)) {
250 // If target block is working block,
251 // it also need to set SPARE_COMPLETED to spare block.
253 Offset
= (UINT8
*) Record
- FtwDevice
->FtwWorkSpace
;
254 Status
= FtwUpdateFvState (
255 FtwDevice
->FtwBackupFvb
,
256 FtwDevice
->FtwWorkSpaceLba
,
257 FtwDevice
->FtwWorkSpaceBase
+ Offset
,
260 if (EFI_ERROR (Status
)) {
264 Status
= FlushSpareBlockToWorkingBlock (FtwDevice
);
265 } else if (IsBootBlock (FtwDevice
, Fvb
, Record
->Lba
)) {
269 Status
= FlushSpareBlockToBootBlock (FtwDevice
);
272 // Update blocks other than working block or boot block
274 Status
= FlushSpareBlockToTargetBlock (FtwDevice
, Fvb
, Record
->Lba
);
277 if (EFI_ERROR (Status
)) {
281 // Record the DestionationComplete in record
283 Offset
= (UINT8
*) Record
- FtwDevice
->FtwWorkSpace
;
284 Status
= FtwUpdateFvState (
285 FtwDevice
->FtwFvBlock
,
286 FtwDevice
->FtwWorkSpaceLba
,
287 FtwDevice
->FtwWorkSpaceBase
+ Offset
,
290 if (EFI_ERROR (Status
)) {
294 Record
->DestinationComplete
= FTW_VALID_STATE
;
297 // If this is the last Write in these write sequence,
298 // set the complete flag of write header.
300 if (IsLastRecordOfWrites (Header
, Record
)) {
301 Offset
= (UINT8
*) Header
- FtwDevice
->FtwWorkSpace
;
302 Status
= FtwUpdateFvState (
303 FtwDevice
->FtwFvBlock
,
304 FtwDevice
->FtwWorkSpaceLba
,
305 FtwDevice
->FtwWorkSpaceBase
+ Offset
,
308 Header
->Complete
= FTW_VALID_STATE
;
309 if (EFI_ERROR (Status
)) {
318 Starts a target block update. This function will record data about write
319 in fault tolerant storage and will complete the write in a recoverable
320 manner, ensuring at all times that either the original contents or
321 the modified contents are available.
323 @param This The pointer to this protocol instance.
324 @param Lba The logical block address of the target block.
325 @param Offset The offset within the target block to place the data.
326 @param Length The number of bytes to write to the target block.
327 @param PrivateData A pointer to private data that the caller requires to
328 complete any pending writes in the event of a fault.
329 @param FvBlockHandle The handle of FVB protocol that provides services for
330 reading, writing, and erasing the target block.
331 @param Buffer The data to write.
333 @retval EFI_SUCCESS The function completed successfully
334 @retval EFI_ABORTED The function could not complete successfully.
335 @retval EFI_BAD_BUFFER_SIZE The input data can't fit within the spare block.
336 Offset + *NumBytes > SpareAreaLength.
337 @retval EFI_ACCESS_DENIED No writes have been allocated.
338 @retval EFI_OUT_OF_RESOURCES Cannot allocate enough memory resource.
339 @retval EFI_NOT_FOUND Cannot find FVB protocol by handle.
345 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL
*This
,
349 IN VOID
*PrivateData
,
350 IN EFI_HANDLE FvBlockHandle
,
355 EFI_FTW_DEVICE
*FtwDevice
;
356 EFI_FAULT_TOLERANT_WRITE_HEADER
*Header
;
357 EFI_FAULT_TOLERANT_WRITE_RECORD
*Record
;
358 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*Fvb
;
363 UINTN SpareBufferSize
;
367 EFI_PHYSICAL_ADDRESS FvbPhysicalAddress
;
369 FtwDevice
= FTW_CONTEXT_FROM_THIS (This
);
371 Status
= WorkSpaceRefresh (FtwDevice
);
372 if (EFI_ERROR (Status
)) {
376 Header
= FtwDevice
->FtwLastWriteHeader
;
377 Record
= FtwDevice
->FtwLastWriteRecord
;
379 if (IsErasedFlashBuffer ((UINT8
*) Header
, sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER
))) {
380 if (PrivateData
== NULL
) {
382 // Ftw Write Header is not allocated.
383 // No additional private data, the private data size is zero. Number of record can be set to 1.
385 Status
= FtwAllocate (This
, &gEfiCallerIdGuid
, 0, 1);
386 if (EFI_ERROR (Status
)) {
391 // Ftw Write Header is not allocated
392 // Additional private data is not NULL, the private data size can't be determined.
394 DEBUG ((EFI_D_ERROR
, "Ftw: no allocates space for write record!\n"));
395 DEBUG ((EFI_D_ERROR
, "Ftw: Allocate service should be called before Write service!\n"));
396 return EFI_NOT_READY
;
401 // If Record is out of the range of Header, return access denied.
403 if (((UINTN
)((UINT8
*) Record
- (UINT8
*) Header
)) > WRITE_TOTAL_SIZE (Header
->NumberOfWrites
- 1, Header
->PrivateDataSize
)) {
404 return EFI_ACCESS_DENIED
;
408 // Check the COMPLETE flag of last write header
410 if (Header
->Complete
== FTW_VALID_STATE
) {
411 return EFI_ACCESS_DENIED
;
414 if (Record
->DestinationComplete
== FTW_VALID_STATE
) {
415 return EFI_ACCESS_DENIED
;
418 if ((Record
->SpareComplete
== FTW_VALID_STATE
) && (Record
->DestinationComplete
!= FTW_VALID_STATE
)) {
419 return EFI_NOT_READY
;
422 // Check if the input data can fit within the target block
424 if ((Offset
+ Length
) > FtwDevice
->SpareAreaLength
) {
425 return EFI_BAD_BUFFER_SIZE
;
428 // Get the FVB protocol by handle
430 Status
= FtwGetFvbByHandle (FvBlockHandle
, &Fvb
);
431 if (EFI_ERROR (Status
)) {
432 return EFI_NOT_FOUND
;
435 Status
= Fvb
->GetPhysicalAddress (Fvb
, &FvbPhysicalAddress
);
436 if (EFI_ERROR (Status
)) {
437 DEBUG ((EFI_D_ERROR
, "FtwLite: Get FVB physical address - %r\n", Status
));
442 // Set BootBlockUpdate FLAG if it's updating boot block.
444 if (IsBootBlock (FtwDevice
, Fvb
, Lba
)) {
445 Record
->BootBlockUpdate
= FTW_VALID_STATE
;
448 // Write the record to the work space.
451 Record
->Offset
= Offset
;
452 Record
->Length
= Length
;
453 Record
->FvBaseAddress
= FvbPhysicalAddress
;
454 if (PrivateData
!= NULL
) {
455 CopyMem ((Record
+ 1), PrivateData
, Header
->PrivateDataSize
);
458 MyOffset
= (UINT8
*) Record
- FtwDevice
->FtwWorkSpace
;
459 MyLength
= RECORD_SIZE (Header
->PrivateDataSize
);
461 Status
= FtwDevice
->FtwFvBlock
->Write (
462 FtwDevice
->FtwFvBlock
,
463 FtwDevice
->FtwWorkSpaceLba
,
464 FtwDevice
->FtwWorkSpaceBase
+ MyOffset
,
468 if (EFI_ERROR (Status
)) {
472 // Record has written to working block, then do the data.
475 // Allocate a memory buffer
477 MyBufferSize
= FtwDevice
->SpareAreaLength
;
478 MyBuffer
= AllocatePool (MyBufferSize
);
479 if (MyBuffer
== NULL
) {
480 return EFI_OUT_OF_RESOURCES
;
483 // Read all original data from target block to memory buffer
486 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
487 MyLength
= FtwDevice
->BlockSize
;
488 Status
= Fvb
->Read (Fvb
, Lba
+ Index
, 0, &MyLength
, Ptr
);
489 if (EFI_ERROR (Status
)) {
497 // Overwrite the updating range data with
498 // the input buffer content
500 CopyMem (MyBuffer
+ Offset
, Buffer
, Length
);
503 // Try to keep the content of spare block
504 // Save spare block into a spare backup memory buffer (Sparebuffer)
506 SpareBufferSize
= FtwDevice
->SpareAreaLength
;
507 SpareBuffer
= AllocatePool (SpareBufferSize
);
508 if (SpareBuffer
== NULL
) {
510 return EFI_OUT_OF_RESOURCES
;
514 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
515 MyLength
= FtwDevice
->BlockSize
;
516 Status
= FtwDevice
->FtwBackupFvb
->Read (
517 FtwDevice
->FtwBackupFvb
,
518 FtwDevice
->FtwSpareLba
+ Index
,
523 if (EFI_ERROR (Status
)) {
525 FreePool (SpareBuffer
);
532 // Write the memory buffer to spare block
534 Status
= FtwEraseSpareBlock (FtwDevice
);
536 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
537 MyLength
= FtwDevice
->BlockSize
;
538 Status
= FtwDevice
->FtwBackupFvb
->Write (
539 FtwDevice
->FtwBackupFvb
,
540 FtwDevice
->FtwSpareLba
+ Index
,
545 if (EFI_ERROR (Status
)) {
547 FreePool (SpareBuffer
);
559 // Set the SpareComplete in the FTW record,
561 MyOffset
= (UINT8
*) Record
- FtwDevice
->FtwWorkSpace
;
562 Status
= FtwUpdateFvState (
563 FtwDevice
->FtwFvBlock
,
564 FtwDevice
->FtwWorkSpaceLba
,
565 FtwDevice
->FtwWorkSpaceBase
+ MyOffset
,
568 if (EFI_ERROR (Status
)) {
569 FreePool (SpareBuffer
);
573 Record
->SpareComplete
= FTW_VALID_STATE
;
576 // Since the content has already backuped in spare block, the write is
577 // guaranteed to be completed with fault tolerant manner.
579 Status
= FtwWriteRecord (This
, Fvb
);
580 if (EFI_ERROR (Status
)) {
581 FreePool (SpareBuffer
);
585 // Restore spare backup buffer into spare block , if no failure happened during FtwWrite.
587 Status
= FtwEraseSpareBlock (FtwDevice
);
589 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
590 MyLength
= FtwDevice
->BlockSize
;
591 Status
= FtwDevice
->FtwBackupFvb
->Write (
592 FtwDevice
->FtwBackupFvb
,
593 FtwDevice
->FtwSpareLba
+ Index
,
598 if (EFI_ERROR (Status
)) {
599 FreePool (SpareBuffer
);
608 FreePool (SpareBuffer
);
612 "Ftw: Write() success, (Lba:Offset)=(%lx:0x%x), Length: 0x%x\n",
622 Restarts a previously interrupted write. The caller must provide the
623 block protocol needed to complete the interrupted write.
625 @param This The pointer to this protocol instance.
626 @param FvBlockHandle The handle of FVB protocol that provides services for
627 reading, writing, and erasing the target block.
629 @retval EFI_SUCCESS The function completed successfully
630 @retval EFI_ACCESS_DENIED No pending writes exist
631 @retval EFI_NOT_FOUND FVB protocol not found by the handle
632 @retval EFI_ABORTED The function could not complete successfully
638 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL
*This
,
639 IN EFI_HANDLE FvBlockHandle
643 EFI_FTW_DEVICE
*FtwDevice
;
644 EFI_FAULT_TOLERANT_WRITE_HEADER
*Header
;
645 EFI_FAULT_TOLERANT_WRITE_RECORD
*Record
;
646 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*Fvb
;
648 FtwDevice
= FTW_CONTEXT_FROM_THIS (This
);
650 Status
= WorkSpaceRefresh (FtwDevice
);
651 if (EFI_ERROR (Status
)) {
655 Header
= FtwDevice
->FtwLastWriteHeader
;
656 Record
= FtwDevice
->FtwLastWriteRecord
;
659 // Spare Complete but Destination not complete,
660 // Recover the targt block with the spare block.
662 Status
= FtwGetFvbByHandle (FvBlockHandle
, &Fvb
);
663 if (EFI_ERROR (Status
)) {
664 return EFI_NOT_FOUND
;
668 // Check the COMPLETE flag of last write header
670 if (Header
->Complete
== FTW_VALID_STATE
) {
671 return EFI_ACCESS_DENIED
;
675 // Check the flags of last write record
677 if (Record
->DestinationComplete
== FTW_VALID_STATE
) {
678 return EFI_ACCESS_DENIED
;
681 if ((Record
->SpareComplete
!= FTW_VALID_STATE
)) {
686 // Since the content has already backuped in spare block, the write is
687 // guaranteed to be completed with fault tolerant manner.
689 Status
= FtwWriteRecord (This
, Fvb
);
690 if (EFI_ERROR (Status
)) {
696 // This is restart, no need to keep spareblock content.
698 FtwEraseSpareBlock (FtwDevice
);
700 DEBUG ((EFI_D_ERROR
, "Ftw: Restart() success \n"));
705 Aborts all previous allocated writes.
707 @param This The pointer to this protocol instance.
709 @retval EFI_SUCCESS The function completed successfully
710 @retval EFI_ABORTED The function could not complete successfully.
711 @retval EFI_NOT_FOUND No allocated writes exist.
717 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL
*This
722 EFI_FTW_DEVICE
*FtwDevice
;
724 FtwDevice
= FTW_CONTEXT_FROM_THIS (This
);
726 Status
= WorkSpaceRefresh (FtwDevice
);
727 if (EFI_ERROR (Status
)) {
731 if (FtwDevice
->FtwLastWriteHeader
->Complete
== FTW_VALID_STATE
) {
732 return EFI_NOT_FOUND
;
735 // Update the complete state of the header as VALID and abort.
737 Offset
= (UINT8
*) FtwDevice
->FtwLastWriteHeader
- FtwDevice
->FtwWorkSpace
;
738 Status
= FtwUpdateFvState (
739 FtwDevice
->FtwFvBlock
,
740 FtwDevice
->FtwWorkSpaceLba
,
741 FtwDevice
->FtwWorkSpaceBase
+ Offset
,
744 if (EFI_ERROR (Status
)) {
748 FtwDevice
->FtwLastWriteHeader
->Complete
= FTW_VALID_STATE
;
750 DEBUG ((EFI_D_ERROR
, "Ftw: Abort() success \n"));
755 Starts a target block update. This records information about the write
756 in fault tolerant storage and will complete the write in a recoverable
757 manner, ensuring at all times that either the original contents or
758 the modified contents are available.
760 @param This The pointer to this protocol instance.
761 @param CallerId The GUID identifying the last write.
762 @param Lba The logical block address of the last write.
763 @param Offset The offset within the block of the last write.
764 @param Length The length of the last write.
765 @param PrivateDataSize bytes from the private data
766 stored for this write.
767 @param PrivateData A pointer to a buffer. The function will copy
768 @param Complete A Boolean value with TRUE indicating
769 that the write was completed.
771 @retval EFI_SUCCESS The function completed successfully
772 @retval EFI_ABORTED The function could not complete successfully
773 @retval EFI_NOT_FOUND No allocated writes exist
774 @retval EFI_BUFFER_TOO_SMALL Input buffer is not larget enough
780 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL
*This
,
781 OUT EFI_GUID
*CallerId
,
785 IN OUT UINTN
*PrivateDataSize
,
786 OUT VOID
*PrivateData
,
787 OUT BOOLEAN
*Complete
791 EFI_FTW_DEVICE
*FtwDevice
;
792 EFI_FAULT_TOLERANT_WRITE_HEADER
*Header
;
793 EFI_FAULT_TOLERANT_WRITE_RECORD
*Record
;
795 if (!FeaturePcdGet(PcdFullFtwServiceEnable
)) {
796 return EFI_UNSUPPORTED
;
799 FtwDevice
= FTW_CONTEXT_FROM_THIS (This
);
801 Status
= WorkSpaceRefresh (FtwDevice
);
802 if (EFI_ERROR (Status
)) {
806 Header
= FtwDevice
->FtwLastWriteHeader
;
807 Record
= FtwDevice
->FtwLastWriteRecord
;
810 // If Header is incompleted and the last record has completed, then
811 // call Abort() to set the Header->Complete FLAG.
813 if ((Header
->Complete
!= FTW_VALID_STATE
) &&
814 (Record
->DestinationComplete
== FTW_VALID_STATE
) &&
815 IsLastRecordOfWrites (Header
, Record
)
818 Status
= FtwAbort (This
);
820 return EFI_NOT_FOUND
;
823 // If there is no write header/record, return not found.
825 if (Header
->HeaderAllocated
!= FTW_VALID_STATE
) {
827 return EFI_NOT_FOUND
;
830 // If this record SpareComplete has not set, then it can not restart.
832 if (Record
->SpareComplete
!= FTW_VALID_STATE
) {
833 Status
= GetPreviousRecordOfWrites (Header
, &Record
);
834 if (EFI_ERROR (Status
)) {
837 return EFI_NOT_FOUND
;
842 // Fill all the requested values
844 CopyMem (CallerId
, &Header
->CallerId
, sizeof (EFI_GUID
));
846 *Offset
= Record
->Offset
;
847 *Length
= Record
->Length
;
848 *Complete
= (BOOLEAN
) (Record
->DestinationComplete
== FTW_VALID_STATE
);
850 if (*PrivateDataSize
< Header
->PrivateDataSize
) {
851 *PrivateDataSize
= Header
->PrivateDataSize
;
853 Status
= EFI_BUFFER_TOO_SMALL
;
855 *PrivateDataSize
= Header
->PrivateDataSize
;
856 CopyMem (PrivateData
, Record
+ 1, *PrivateDataSize
);
857 Status
= EFI_SUCCESS
;
860 DEBUG ((EFI_D_ERROR
, "Ftw: GetLasetWrite() success\n"));
866 This function is the entry point of the Fault Tolerant Write driver.
868 @param ImageHandle A handle for the image that is initializing this driver
869 @param SystemTable A pointer to the EFI system table
871 @return EFI_SUCCESS FTW has finished the initialization
872 @retval EFI_NOT_FOUND Locate FVB protocol error
873 @retval EFI_OUT_OF_RESOURCES Allocate memory error
874 @retval EFI_VOLUME_CORRUPTED Firmware volume is error
875 @retval EFI_ABORTED FTW initialization error
880 InitializeFaultTolerantWrite (
881 IN EFI_HANDLE ImageHandle
,
882 IN EFI_SYSTEM_TABLE
*SystemTable
885 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*Fvb
;
887 EFI_HANDLE
*HandleBuffer
;
889 EFI_FIRMWARE_VOLUME_HEADER
*FwVolHeader
;
890 EFI_PHYSICAL_ADDRESS BaseAddress
;
891 EFI_FTW_DEVICE
*FtwDevice
;
892 EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwHeader
;
896 EFI_FV_BLOCK_MAP_ENTRY
*FvbMapEntry
;
898 EFI_HANDLE FvbHandle
;
901 // Allocate Private data of this driver,
902 // INCLUDING THE FtwWorkSpace[FTW_WORK_SPACE_SIZE].
906 FtwDevice
= AllocatePool (sizeof (EFI_FTW_DEVICE
) + PcdGet32 (PcdFlashNvStorageFtwWorkingSize
));
907 if (FtwDevice
== NULL
) {
908 return EFI_OUT_OF_RESOURCES
;
911 ZeroMem (FtwDevice
, sizeof (EFI_FTW_DEVICE
));
912 FtwDevice
->Signature
= FTW_DEVICE_SIGNATURE
;
915 // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.
918 FtwDevice
->WorkSpaceAddress
= (EFI_PHYSICAL_ADDRESS
) PcdGet32 (PcdFlashNvStorageFtwWorkingBase
);
919 FtwDevice
->WorkSpaceLength
= (UINTN
) PcdGet32 (PcdFlashNvStorageFtwWorkingSize
);
921 FtwDevice
->SpareAreaAddress
= (EFI_PHYSICAL_ADDRESS
) PcdGet32 (PcdFlashNvStorageFtwSpareBase
);
922 FtwDevice
->SpareAreaLength
= (UINTN
) PcdGet32 (PcdFlashNvStorageFtwSpareSize
);
924 if ((FtwDevice
->WorkSpaceLength
== 0) || (FtwDevice
->SpareAreaLength
== 0)) {
925 DEBUG ((EFI_D_ERROR
, "Ftw: Workspace or Spare block does not exist!\n"));
926 FreePool (FtwDevice
);
927 return EFI_OUT_OF_RESOURCES
;
930 // Locate FVB protocol by handle
932 Status
= gBS
->LocateHandleBuffer (
934 &gEfiFirmwareVolumeBlockProtocolGuid
,
939 if (EFI_ERROR (Status
)) {
940 FreePool (FtwDevice
);
941 return EFI_NOT_FOUND
;
944 if (HandleCount
<= 0) {
945 FreePool (FtwDevice
);
946 return EFI_NOT_FOUND
;
950 FtwDevice
->FtwFvBlock
= NULL
;
951 FtwDevice
->FtwBackupFvb
= NULL
;
952 FtwDevice
->FtwWorkSpaceLba
= (EFI_LBA
) (-1);
953 FtwDevice
->FtwSpareLba
= (EFI_LBA
) (-1);
954 for (Index
= 0; Index
< HandleCount
; Index
+= 1) {
955 Status
= gBS
->HandleProtocol (
957 &gEfiFirmwareVolumeBlockProtocolGuid
,
960 if (EFI_ERROR (Status
)) {
961 FreePool (FtwDevice
);
965 Status
= Fvb
->GetPhysicalAddress (Fvb
, &BaseAddress
);
966 if (EFI_ERROR (Status
)) {
970 FwVolHeader
= (EFI_FIRMWARE_VOLUME_HEADER
*) ((UINTN
) BaseAddress
);
972 if ((FtwDevice
->WorkSpaceAddress
>= BaseAddress
) &&
973 ((FtwDevice
->WorkSpaceAddress
+ FtwDevice
->WorkSpaceLength
) <= (BaseAddress
+ FwVolHeader
->FvLength
))
975 FtwDevice
->FtwFvBlock
= Fvb
;
977 // To get the LBA of work space
979 if ((FwVolHeader
->FvLength
) > (FwVolHeader
->HeaderLength
)) {
981 // Now, one FV has one type of BlockLength
983 FvbMapEntry
= &FwVolHeader
->BlockMap
[0];
984 for (LbaIndex
= 1; LbaIndex
<= FvbMapEntry
->NumBlocks
; LbaIndex
+= 1) {
985 if ((FtwDevice
->WorkSpaceAddress
>= (BaseAddress
+ FvbMapEntry
->Length
* (LbaIndex
- 1)))
986 && (FtwDevice
->WorkSpaceAddress
< (BaseAddress
+ FvbMapEntry
->Length
* LbaIndex
))) {
987 FtwDevice
->FtwWorkSpaceLba
= LbaIndex
- 1;
989 // Get the Work space size and Base(Offset)
991 FtwDevice
->FtwWorkSpaceSize
= FtwDevice
->WorkSpaceLength
;
992 FtwDevice
->FtwWorkSpaceBase
= (UINTN
) (FtwDevice
->WorkSpaceAddress
- (BaseAddress
+ FvbMapEntry
->Length
* (LbaIndex
- 1)));
999 if ((FtwDevice
->SpareAreaAddress
>= BaseAddress
) &&
1000 ((FtwDevice
->SpareAreaAddress
+ FtwDevice
->SpareAreaLength
) <= (BaseAddress
+ FwVolHeader
->FvLength
))
1002 FtwDevice
->FtwBackupFvb
= Fvb
;
1004 // To get the LBA of spare
1006 if ((FwVolHeader
->FvLength
) > (FwVolHeader
->HeaderLength
)) {
1008 // Now, one FV has one type of BlockLength
1010 FvbMapEntry
= &FwVolHeader
->BlockMap
[0];
1011 for (LbaIndex
= 1; LbaIndex
<= FvbMapEntry
->NumBlocks
; LbaIndex
+= 1) {
1012 if ((FtwDevice
->SpareAreaAddress
>= (BaseAddress
+ FvbMapEntry
->Length
* (LbaIndex
- 1)))
1013 && (FtwDevice
->SpareAreaAddress
< (BaseAddress
+ FvbMapEntry
->Length
* LbaIndex
))) {
1015 // Get the NumberOfSpareBlock and BlockSize
1017 FtwDevice
->FtwSpareLba
= LbaIndex
- 1;
1018 FtwDevice
->BlockSize
= FvbMapEntry
->Length
;
1019 FtwDevice
->NumberOfSpareBlock
= FtwDevice
->SpareAreaLength
/ FtwDevice
->BlockSize
;
1021 // Check the range of spare area to make sure that it's in FV range
1023 if ((FtwDevice
->FtwSpareLba
+ FtwDevice
->NumberOfSpareBlock
) > FvbMapEntry
->NumBlocks
) {
1024 DEBUG ((EFI_D_ERROR
, "Ftw: Spare area is out of FV range\n"));
1025 FreePool (FtwDevice
);
1036 // Calculate the start LBA of working block. Working block is an area which
1037 // contains working space in its last block and has the same size as spare
1038 // block, unless there are not enough blocks before the block that contains
1041 FtwDevice
->FtwWorkBlockLba
= FtwDevice
->FtwWorkSpaceLba
- FtwDevice
->NumberOfSpareBlock
+ 1;
1042 if ((INT64
) (FtwDevice
->FtwWorkBlockLba
) < 0) {
1043 DEBUG ((EFI_D_ERROR
, "Ftw: The spare block range is too large than the working block range!\n"));
1044 FreePool (FtwDevice
);
1048 if ((FtwDevice
->FtwFvBlock
== NULL
) ||
1049 (FtwDevice
->FtwBackupFvb
== NULL
) ||
1050 (FtwDevice
->FtwWorkSpaceLba
== (EFI_LBA
) (-1)) ||
1051 (FtwDevice
->FtwSpareLba
== (EFI_LBA
) (-1))
1053 DEBUG ((EFI_D_ERROR
, "Ftw: Working or spare FVB not ready\n"));
1054 FreePool (FtwDevice
);
1058 // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.
1060 FtwDevice
->FtwWorkSpace
= (UINT8
*) (FtwDevice
+ 1);
1061 FtwDevice
->FtwWorkSpaceHeader
= (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*) FtwDevice
->FtwWorkSpace
;
1063 FtwDevice
->FtwLastWriteHeader
= NULL
;
1064 FtwDevice
->FtwLastWriteRecord
= NULL
;
1067 // Refresh the working space data from working block
1069 Status
= WorkSpaceRefresh (FtwDevice
);
1070 if (EFI_ERROR (Status
)) {
1074 // If the working block workspace is not valid, try the spare block
1076 if (!IsValidWorkSpace (FtwDevice
->FtwWorkSpaceHeader
)) {
1078 // Read from spare block
1080 Length
= FtwDevice
->FtwWorkSpaceSize
;
1081 Status
= FtwDevice
->FtwBackupFvb
->Read (
1082 FtwDevice
->FtwBackupFvb
,
1083 FtwDevice
->FtwSpareLba
,
1084 FtwDevice
->FtwWorkSpaceBase
,
1086 FtwDevice
->FtwWorkSpace
1088 if (EFI_ERROR (Status
)) {
1092 // If spare block is valid, then replace working block content.
1094 if (IsValidWorkSpace (FtwDevice
->FtwWorkSpaceHeader
)) {
1095 Status
= FlushSpareBlockToWorkingBlock (FtwDevice
);
1096 DEBUG ((EFI_D_ERROR
, "Ftw: Restart working block update in Init() - %r\n", Status
));
1097 FtwAbort (&FtwDevice
->FtwInstance
);
1099 // Refresh work space.
1101 Status
= WorkSpaceRefresh (FtwDevice
);
1102 if (EFI_ERROR (Status
)) {
1106 DEBUG ((EFI_D_ERROR
, "Ftw: Both are invalid, init workspace\n"));
1108 // If both are invalid, then initialize work space.
1111 FtwDevice
->FtwWorkSpace
,
1112 FtwDevice
->FtwWorkSpaceSize
,
1115 InitWorkSpaceHeader (FtwDevice
->FtwWorkSpaceHeader
);
1117 // Initialize the work space
1119 Status
= FtwReclaimWorkSpace (FtwDevice
, FALSE
);
1120 if (EFI_ERROR (Status
)) {
1127 // If the FtwDevice->FtwLastWriteRecord is 1st record of write header &&
1128 // (! SpareComplete) THEN call Abort().
1130 if ((FtwDevice
->FtwLastWriteHeader
->HeaderAllocated
== FTW_VALID_STATE
) &&
1131 (FtwDevice
->FtwLastWriteRecord
->SpareComplete
!= FTW_VALID_STATE
) &&
1132 IsFirstRecordOfWrites (FtwDevice
->FtwLastWriteHeader
, FtwDevice
->FtwLastWriteRecord
)
1134 DEBUG ((EFI_D_ERROR
, "Ftw: Init.. find first record not SpareCompleted, abort()\n"));
1135 FtwAbort (&FtwDevice
->FtwInstance
);
1138 // If Header is incompleted and the last record has completed, then
1139 // call Abort() to set the Header->Complete FLAG.
1141 if ((FtwDevice
->FtwLastWriteHeader
->Complete
!= FTW_VALID_STATE
) &&
1142 (FtwDevice
->FtwLastWriteRecord
->DestinationComplete
== FTW_VALID_STATE
) &&
1143 IsLastRecordOfWrites (FtwDevice
->FtwLastWriteHeader
, FtwDevice
->FtwLastWriteRecord
)
1145 DEBUG ((EFI_D_ERROR
, "Ftw: Init.. find last record completed but header not, abort()\n"));
1146 FtwAbort (&FtwDevice
->FtwInstance
);
1149 // To check the workspace buffer following last Write header/records is EMPTY or not.
1150 // If it's not EMPTY, FTW also need to call reclaim().
1152 FtwHeader
= FtwDevice
->FtwLastWriteHeader
;
1153 Offset
= (UINT8
*) FtwHeader
- FtwDevice
->FtwWorkSpace
;
1154 if (FtwDevice
->FtwWorkSpace
[Offset
] != FTW_ERASED_BYTE
) {
1155 Offset
+= WRITE_TOTAL_SIZE (FtwHeader
->NumberOfWrites
, FtwHeader
->PrivateDataSize
);
1158 if (!IsErasedFlashBuffer (
1159 FtwDevice
->FtwWorkSpace
+ Offset
,
1160 FtwDevice
->FtwWorkSpaceSize
- Offset
1162 Status
= FtwReclaimWorkSpace (FtwDevice
, TRUE
);
1163 if (EFI_ERROR (Status
)) {
1168 // Restart if it's boot block
1170 if ((FtwDevice
->FtwLastWriteHeader
->Complete
!= FTW_VALID_STATE
) &&
1171 (FtwDevice
->FtwLastWriteRecord
->SpareComplete
== FTW_VALID_STATE
)
1173 if (FtwDevice
->FtwLastWriteRecord
->BootBlockUpdate
== FTW_VALID_STATE
) {
1174 Status
= FlushSpareBlockToBootBlock (FtwDevice
);
1175 DEBUG ((EFI_D_ERROR
, "Ftw: Restart boot block update - %r\n", Status
));
1176 if (EFI_ERROR (Status
)) {
1180 FtwAbort (&FtwDevice
->FtwInstance
);
1183 // if (SpareCompleted) THEN Restart to fault tolerant write.
1185 FvbHandle
= GetFvbByAddress (FtwDevice
->FtwLastWriteRecord
->FvBaseAddress
, &Fvb
);
1186 if (FvbHandle
!= NULL
) {
1187 Status
= FtwRestart (&FtwDevice
->FtwInstance
, FvbHandle
);
1188 DEBUG ((EFI_D_ERROR
, "FtwLite: Restart last write - %r\n", Status
));
1189 if (EFI_ERROR (Status
)) {
1193 FtwAbort (&FtwDevice
->FtwInstance
);
1198 // Hook the protocol API
1200 FtwDevice
->FtwInstance
.GetMaxBlockSize
= FtwGetMaxBlockSize
;
1201 FtwDevice
->FtwInstance
.Allocate
= FtwAllocate
;
1202 FtwDevice
->FtwInstance
.Write
= FtwWrite
;
1203 FtwDevice
->FtwInstance
.Restart
= FtwRestart
;
1204 FtwDevice
->FtwInstance
.Abort
= FtwAbort
;
1205 FtwDevice
->FtwInstance
.GetLastWrite
= FtwGetLastWrite
;
1208 // Install protocol interface
1210 Status
= gBS
->InstallProtocolInterface (
1212 &gEfiFaultTolerantWriteProtocolGuid
,
1213 EFI_NATIVE_INTERFACE
,
1214 &FtwDevice
->FtwInstance
1216 if (EFI_ERROR (Status
)) {
1224 if (FtwDevice
!= NULL
) {
1225 FreePool (FtwDevice
);
1228 DEBUG ((EFI_D_ERROR
, "Ftw: Severe Error occurs, need to recovery\n"));
1230 return EFI_VOLUME_CORRUPTED
;