3 Internal functions to operate Working Block Space.
5 Copyright (c) 2006 - 2018, 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.
17 #include "FaultTolerantWrite.h"
19 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER mWorkingBlockHeader
= {ZERO_GUID
, 0, 0, 0, 0, {0, 0, 0}, 0};
22 Initialize a local work space header.
24 Since Signature and WriteQueueSize have been known, Crc can be calculated out,
25 then the work space header will be fixed.
28 InitializeLocalWorkSpaceHeader (
33 // Check signature with gEdkiiWorkingBlockSignatureGuid.
35 if (CompareGuid (&gEdkiiWorkingBlockSignatureGuid
, &mWorkingBlockHeader
.Signature
)) {
37 // The local work space header has been initialized.
44 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
),
49 // Here using gEdkiiWorkingBlockSignatureGuid as the signature.
52 &mWorkingBlockHeader
.Signature
,
53 &gEdkiiWorkingBlockSignatureGuid
,
56 mWorkingBlockHeader
.WriteQueueSize
= PcdGet32 (PcdFlashNvStorageFtwWorkingSize
) - sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
);
59 // Crc is calculated with all the fields except Crc and STATE, so leave them as FTW_ERASED_BYTE.
63 // Calculate the Crc of woking block header
65 mWorkingBlockHeader
.Crc
= FtwCalculateCrc32 (&mWorkingBlockHeader
,
66 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
));
68 mWorkingBlockHeader
.WorkingBlockValid
= FTW_VALID_STATE
;
69 mWorkingBlockHeader
.WorkingBlockInvalid
= FTW_INVALID_STATE
;
73 Check to see if it is a valid work space.
76 @param WorkingHeader Pointer of working block header
78 @retval TRUE The work space is valid.
79 @retval FALSE The work space is invalid.
84 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*WorkingHeader
87 if (WorkingHeader
== NULL
) {
91 if (CompareMem (WorkingHeader
, &mWorkingBlockHeader
, sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
)) == 0) {
95 DEBUG ((EFI_D_INFO
, "Ftw: Work block header check mismatch\n"));
100 Initialize a work space when there is no work space.
102 @param WorkingHeader Pointer of working block header
104 @retval EFI_SUCCESS The function completed successfully
105 @retval EFI_ABORTED The function could not complete successfully.
109 InitWorkSpaceHeader (
110 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*WorkingHeader
113 if (WorkingHeader
== NULL
) {
114 return EFI_INVALID_PARAMETER
;
117 CopyMem (WorkingHeader
, &mWorkingBlockHeader
, sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
));
123 Read work space data from work block or spare block.
125 @param FvBlock FVB Protocol interface to access the block.
126 @param BlockSize The size of the block.
127 @param Lba Lba of the block.
128 @param Offset The offset within the block.
129 @param Length The number of bytes to read from the block.
130 @param Buffer The data is read.
132 @retval EFI_SUCCESS The function completed successfully.
133 @retval EFI_ABORTED The function could not complete successfully.
138 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*FvBlock
,
151 // Calculate the real Offset and Lba to write.
153 while (Offset
>= BlockSize
) {
160 if ((Offset
+ Length
) > BlockSize
) {
161 MyLength
= BlockSize
- Offset
;
166 Status
= FvBlock
->Read (
173 if (EFI_ERROR (Status
)) {
186 Write work space data to work block.
188 @param FvBlock FVB Protocol interface to access the block.
189 @param BlockSize The size of the block.
190 @param Lba Lba of the block.
191 @param Offset The offset within the block to place the data.
192 @param Length The number of bytes to write to the block.
193 @param Buffer The data to write.
195 @retval EFI_SUCCESS The function completed successfully.
196 @retval EFI_ABORTED The function could not complete successfully.
201 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*FvBlock
,
214 // Calculate the real Offset and Lba to write.
216 while (Offset
>= BlockSize
) {
223 if ((Offset
+ Length
) > BlockSize
) {
224 MyLength
= BlockSize
- Offset
;
229 Status
= FvBlock
->Write (
236 if (EFI_ERROR (Status
)) {
248 Read from working block to refresh the work space in memory.
250 @param FtwDevice Point to private data of FTW driver
252 @retval EFI_SUCCESS The function completed successfully
253 @retval EFI_ABORTED The function could not complete successfully.
258 IN EFI_FTW_DEVICE
*FtwDevice
262 UINTN RemainingSpaceSize
;
265 // Initialize WorkSpace as FTW_ERASED_BYTE
268 FtwDevice
->FtwWorkSpace
,
269 FtwDevice
->FtwWorkSpaceSize
,
274 // Read from working block
276 Status
= ReadWorkSpaceData (
277 FtwDevice
->FtwFvBlock
,
278 FtwDevice
->WorkBlockSize
,
279 FtwDevice
->FtwWorkSpaceLba
,
280 FtwDevice
->FtwWorkSpaceBase
,
281 FtwDevice
->FtwWorkSpaceSize
,
282 FtwDevice
->FtwWorkSpace
284 if (EFI_ERROR (Status
)) {
288 // Refresh the FtwLastWriteHeader
290 Status
= FtwGetLastWriteHeader (
291 FtwDevice
->FtwWorkSpaceHeader
,
292 FtwDevice
->FtwWorkSpaceSize
,
293 &FtwDevice
->FtwLastWriteHeader
295 RemainingSpaceSize
= FtwDevice
->FtwWorkSpaceSize
- ((UINTN
) FtwDevice
->FtwLastWriteHeader
- (UINTN
) FtwDevice
->FtwWorkSpace
);
296 DEBUG ((EFI_D_INFO
, "Ftw: Remaining work space size - %x\n", RemainingSpaceSize
));
298 // If FtwGetLastWriteHeader() returns error, or the remaining space size is even not enough to contain
299 // one EFI_FAULT_TOLERANT_WRITE_HEADER + one EFI_FAULT_TOLERANT_WRITE_RECORD(It will cause that the header
300 // pointed by FtwDevice->FtwLastWriteHeader or record pointed by FtwDevice->FtwLastWriteRecord may contain invalid data),
301 // it needs to reclaim work space.
303 if (EFI_ERROR (Status
) || RemainingSpaceSize
< sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER
) + sizeof (EFI_FAULT_TOLERANT_WRITE_RECORD
)) {
305 // reclaim work space in working block.
307 Status
= FtwReclaimWorkSpace (FtwDevice
, TRUE
);
308 if (EFI_ERROR (Status
)) {
309 DEBUG ((EFI_D_ERROR
, "Ftw: Reclaim workspace - %r\n", Status
));
313 // Read from working block again
315 Status
= ReadWorkSpaceData (
316 FtwDevice
->FtwFvBlock
,
317 FtwDevice
->WorkBlockSize
,
318 FtwDevice
->FtwWorkSpaceLba
,
319 FtwDevice
->FtwWorkSpaceBase
,
320 FtwDevice
->FtwWorkSpaceSize
,
321 FtwDevice
->FtwWorkSpace
323 if (EFI_ERROR (Status
)) {
327 Status
= FtwGetLastWriteHeader (
328 FtwDevice
->FtwWorkSpaceHeader
,
329 FtwDevice
->FtwWorkSpaceSize
,
330 &FtwDevice
->FtwLastWriteHeader
332 if (EFI_ERROR (Status
)) {
337 // Refresh the FtwLastWriteRecord
339 Status
= FtwGetLastWriteRecord (
340 FtwDevice
->FtwLastWriteHeader
,
341 &FtwDevice
->FtwLastWriteRecord
343 if (EFI_ERROR (Status
)) {
351 Reclaim the work space on the working block.
353 @param FtwDevice Point to private data of FTW driver
354 @param PreserveRecord Whether to preserve the working record is needed
356 @retval EFI_SUCCESS The function completed successfully
357 @retval EFI_OUT_OF_RESOURCES Allocate memory error
358 @retval EFI_ABORTED The function could not complete successfully
362 FtwReclaimWorkSpace (
363 IN EFI_FTW_DEVICE
*FtwDevice
,
364 IN BOOLEAN PreserveRecord
369 EFI_FAULT_TOLERANT_WRITE_HEADER
*Header
;
371 UINTN TempBufferSize
;
372 UINTN SpareBufferSize
;
374 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*WorkingBlockHeader
;
377 EFI_LBA WorkSpaceLbaOffset
;
379 DEBUG ((EFI_D_INFO
, "Ftw: start to reclaim work space\n"));
381 WorkSpaceLbaOffset
= FtwDevice
->FtwWorkSpaceLba
- FtwDevice
->FtwWorkBlockLba
;
384 // Read all original data from working block to a memory buffer
386 TempBufferSize
= FtwDevice
->NumberOfWorkBlock
* FtwDevice
->WorkBlockSize
;
387 TempBuffer
= AllocateZeroPool (TempBufferSize
);
388 if (TempBuffer
== NULL
) {
389 return EFI_OUT_OF_RESOURCES
;
393 for (Index
= 0; Index
< FtwDevice
->NumberOfWorkBlock
; Index
+= 1) {
394 Length
= FtwDevice
->WorkBlockSize
;
395 Status
= FtwDevice
->FtwFvBlock
->Read (
396 FtwDevice
->FtwFvBlock
,
397 FtwDevice
->FtwWorkBlockLba
+ Index
,
402 if (EFI_ERROR (Status
)) {
403 FreePool (TempBuffer
);
410 // Clean up the workspace, remove all the completed records.
413 (UINTN
) WorkSpaceLbaOffset
* FtwDevice
->WorkBlockSize
+
414 FtwDevice
->FtwWorkSpaceBase
;
417 // Clear the content of buffer that will save the new work space data
419 SetMem (Ptr
, FtwDevice
->FtwWorkSpaceSize
, FTW_ERASED_BYTE
);
422 // Copy EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER to buffer
426 FtwDevice
->FtwWorkSpaceHeader
,
427 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
)
429 if (PreserveRecord
) {
431 // Get the last record following the header,
433 Status
= FtwGetLastWriteHeader (
434 FtwDevice
->FtwWorkSpaceHeader
,
435 FtwDevice
->FtwWorkSpaceSize
,
436 &FtwDevice
->FtwLastWriteHeader
438 Header
= FtwDevice
->FtwLastWriteHeader
;
439 if (!EFI_ERROR (Status
) && (Header
!= NULL
) && (Header
->Complete
!= FTW_VALID_STATE
) && (Header
->HeaderAllocated
== FTW_VALID_STATE
)) {
441 Ptr
+ sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
),
442 FtwDevice
->FtwLastWriteHeader
,
443 FTW_WRITE_TOTAL_SIZE (Header
->NumberOfWrites
, Header
->PrivateDataSize
)
449 FtwDevice
->FtwWorkSpace
,
451 FtwDevice
->FtwWorkSpaceSize
454 FtwGetLastWriteHeader (
455 FtwDevice
->FtwWorkSpaceHeader
,
456 FtwDevice
->FtwWorkSpaceSize
,
457 &FtwDevice
->FtwLastWriteHeader
460 FtwGetLastWriteRecord (
461 FtwDevice
->FtwLastWriteHeader
,
462 &FtwDevice
->FtwLastWriteRecord
466 // Set the WorkingBlockValid and WorkingBlockInvalid as INVALID
468 WorkingBlockHeader
= (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*) (TempBuffer
+
469 (UINTN
) WorkSpaceLbaOffset
* FtwDevice
->WorkBlockSize
+
470 FtwDevice
->FtwWorkSpaceBase
);
471 WorkingBlockHeader
->WorkingBlockValid
= FTW_INVALID_STATE
;
472 WorkingBlockHeader
->WorkingBlockInvalid
= FTW_INVALID_STATE
;
475 // Try to keep the content of spare block
476 // Save spare block into a spare backup memory buffer (Sparebuffer)
478 SpareBufferSize
= FtwDevice
->SpareAreaLength
;
479 SpareBuffer
= AllocatePool (SpareBufferSize
);
480 if (SpareBuffer
== NULL
) {
481 FreePool (TempBuffer
);
482 return EFI_OUT_OF_RESOURCES
;
486 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
487 Length
= FtwDevice
->SpareBlockSize
;
488 Status
= FtwDevice
->FtwBackupFvb
->Read (
489 FtwDevice
->FtwBackupFvb
,
490 FtwDevice
->FtwSpareLba
+ Index
,
495 if (EFI_ERROR (Status
)) {
496 FreePool (TempBuffer
);
497 FreePool (SpareBuffer
);
504 // Write the memory buffer to spare block
506 Status
= FtwEraseSpareBlock (FtwDevice
);
507 if (EFI_ERROR (Status
)) {
508 FreePool (TempBuffer
);
509 FreePool (SpareBuffer
);
513 for (Index
= 0; TempBufferSize
> 0; Index
+= 1) {
514 if (TempBufferSize
> FtwDevice
->SpareBlockSize
) {
515 Length
= FtwDevice
->SpareBlockSize
;
517 Length
= TempBufferSize
;
519 Status
= FtwDevice
->FtwBackupFvb
->Write (
520 FtwDevice
->FtwBackupFvb
,
521 FtwDevice
->FtwSpareLba
+ Index
,
526 if (EFI_ERROR (Status
)) {
527 FreePool (TempBuffer
);
528 FreePool (SpareBuffer
);
533 TempBufferSize
-= Length
;
538 FreePool (TempBuffer
);
541 // Set the WorkingBlockValid in spare block
543 Status
= FtwUpdateFvState (
544 FtwDevice
->FtwBackupFvb
,
545 FtwDevice
->SpareBlockSize
,
546 FtwDevice
->FtwSpareLba
+ FtwDevice
->FtwWorkSpaceLbaInSpare
,
547 FtwDevice
->FtwWorkSpaceBaseInSpare
+ sizeof (EFI_GUID
) + sizeof (UINT32
),
550 if (EFI_ERROR (Status
)) {
551 FreePool (SpareBuffer
);
555 // Before erase the working block, set WorkingBlockInvalid in working block.
557 // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
558 // WorkingBlockInvalid);
560 Status
= FtwUpdateFvState (
561 FtwDevice
->FtwFvBlock
,
562 FtwDevice
->WorkBlockSize
,
563 FtwDevice
->FtwWorkSpaceLba
,
564 FtwDevice
->FtwWorkSpaceBase
+ sizeof (EFI_GUID
) + sizeof (UINT32
),
565 WORKING_BLOCK_INVALID
567 if (EFI_ERROR (Status
)) {
568 FreePool (SpareBuffer
);
572 FtwDevice
->FtwWorkSpaceHeader
->WorkingBlockInvalid
= FTW_VALID_STATE
;
575 // Write the spare block to working block
577 Status
= FlushSpareBlockToWorkingBlock (FtwDevice
);
578 if (EFI_ERROR (Status
)) {
579 FreePool (SpareBuffer
);
583 // Restore spare backup buffer into spare block , if no failure happened during FtwWrite.
585 Status
= FtwEraseSpareBlock (FtwDevice
);
586 if (EFI_ERROR (Status
)) {
587 FreePool (SpareBuffer
);
591 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
592 Length
= FtwDevice
->SpareBlockSize
;
593 Status
= FtwDevice
->FtwBackupFvb
->Write (
594 FtwDevice
->FtwBackupFvb
,
595 FtwDevice
->FtwSpareLba
+ Index
,
600 if (EFI_ERROR (Status
)) {
601 FreePool (SpareBuffer
);
608 FreePool (SpareBuffer
);
610 DEBUG ((EFI_D_INFO
, "Ftw: reclaim work space successfully\n"));