3 Internal functions to operate Working Block Space.
5 Copyright (c) 2006 - 2015, 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 (
35 // Check signature with gEdkiiWorkingBlockSignatureGuid.
37 if (CompareGuid (&gEdkiiWorkingBlockSignatureGuid
, &mWorkingBlockHeader
.Signature
)) {
39 // The local work space header has been initialized.
46 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
),
51 // Here using gEdkiiWorkingBlockSignatureGuid as the signature.
54 &mWorkingBlockHeader
.Signature
,
55 &gEdkiiWorkingBlockSignatureGuid
,
58 mWorkingBlockHeader
.WriteQueueSize
= (UINT64
) (PcdGet32 (PcdFlashNvStorageFtwWorkingSize
) - sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
));
61 // Crc is calculated with all the fields except Crc and STATE, so leave them as FTW_ERASED_BYTE.
65 // Calculate the Crc of woking block header
67 Status
= gBS
->CalculateCrc32 (
69 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
),
70 &mWorkingBlockHeader
.Crc
72 ASSERT_EFI_ERROR (Status
);
74 mWorkingBlockHeader
.WorkingBlockValid
= FTW_VALID_STATE
;
75 mWorkingBlockHeader
.WorkingBlockInvalid
= FTW_INVALID_STATE
;
79 Check to see if it is a valid work space.
82 @param WorkingHeader Pointer of working block header
84 @retval TRUE The work space is valid.
85 @retval FALSE The work space is invalid.
90 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*WorkingHeader
93 if (WorkingHeader
== NULL
) {
97 if (CompareMem (WorkingHeader
, &mWorkingBlockHeader
, sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
)) == 0) {
101 DEBUG ((EFI_D_ERROR
, "Ftw: Work block header check error\n"));
106 Initialize a work space when there is no work space.
108 @param WorkingHeader Pointer of working block header
110 @retval EFI_SUCCESS The function completed successfully
111 @retval EFI_ABORTED The function could not complete successfully.
115 InitWorkSpaceHeader (
116 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*WorkingHeader
119 if (WorkingHeader
== NULL
) {
120 return EFI_INVALID_PARAMETER
;
123 CopyMem (WorkingHeader
, &mWorkingBlockHeader
, sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
));
129 Read work space data from work block or spare block.
131 @param FvBlock FVB Protocol interface to access the block.
132 @param BlockSize The size of the block.
133 @param Lba Lba of the block.
134 @param Offset The offset within the block.
135 @param Length The number of bytes to read from the block.
136 @param Buffer The data is read.
138 @retval EFI_SUCCESS The function completed successfully.
139 @retval EFI_ABORTED The function could not complete successfully.
144 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*FvBlock
,
157 // Calculate the real Offset and Lba to write.
159 while (Offset
>= BlockSize
) {
166 if ((Offset
+ Length
) > BlockSize
) {
167 MyLength
= BlockSize
- Offset
;
172 Status
= FvBlock
->Read (
179 if (EFI_ERROR (Status
)) {
192 Write work space data to work block.
194 @param FvBlock FVB Protocol interface to access the block.
195 @param BlockSize The size of the block.
196 @param Lba Lba of the block.
197 @param Offset The offset within the block to place the data.
198 @param Length The number of bytes to write to the block.
199 @param Buffer The data to write.
201 @retval EFI_SUCCESS The function completed successfully.
202 @retval EFI_ABORTED The function could not complete successfully.
207 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*FvBlock
,
220 // Calculate the real Offset and Lba to write.
222 while (Offset
>= BlockSize
) {
229 if ((Offset
+ Length
) > BlockSize
) {
230 MyLength
= BlockSize
- Offset
;
235 Status
= FvBlock
->Write (
242 if (EFI_ERROR (Status
)) {
254 Read from working block to refresh the work space in memory.
256 @param FtwDevice Point to private data of FTW driver
258 @retval EFI_SUCCESS The function completed successfully
259 @retval EFI_ABORTED The function could not complete successfully.
264 IN EFI_FTW_DEVICE
*FtwDevice
268 UINTN RemainingSpaceSize
;
271 // Initialize WorkSpace as FTW_ERASED_BYTE
274 FtwDevice
->FtwWorkSpace
,
275 FtwDevice
->FtwWorkSpaceSize
,
280 // Read from working block
282 Status
= ReadWorkSpaceData (
283 FtwDevice
->FtwFvBlock
,
284 FtwDevice
->WorkBlockSize
,
285 FtwDevice
->FtwWorkSpaceLba
,
286 FtwDevice
->FtwWorkSpaceBase
,
287 FtwDevice
->FtwWorkSpaceSize
,
288 FtwDevice
->FtwWorkSpace
290 if (EFI_ERROR (Status
)) {
294 // Refresh the FtwLastWriteHeader
296 Status
= FtwGetLastWriteHeader (
297 FtwDevice
->FtwWorkSpaceHeader
,
298 FtwDevice
->FtwWorkSpaceSize
,
299 &FtwDevice
->FtwLastWriteHeader
301 RemainingSpaceSize
= FtwDevice
->FtwWorkSpaceSize
- ((UINTN
) FtwDevice
->FtwLastWriteHeader
- (UINTN
) FtwDevice
->FtwWorkSpace
);
302 DEBUG ((EFI_D_INFO
, "Ftw: Remaining work space size - %x\n", RemainingSpaceSize
));
304 // If FtwGetLastWriteHeader() returns error, or the remaining space size is even not enough to contain
305 // one EFI_FAULT_TOLERANT_WRITE_HEADER + one EFI_FAULT_TOLERANT_WRITE_RECORD(It will cause that the header
306 // pointed by FtwDevice->FtwLastWriteHeader or record pointed by FtwDevice->FtwLastWriteRecord may contain invalid data),
307 // it needs to reclaim work space.
309 if (EFI_ERROR (Status
) || RemainingSpaceSize
< sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER
) + sizeof (EFI_FAULT_TOLERANT_WRITE_RECORD
)) {
311 // reclaim work space in working block.
313 Status
= FtwReclaimWorkSpace (FtwDevice
, TRUE
);
314 if (EFI_ERROR (Status
)) {
315 DEBUG ((EFI_D_ERROR
, "Ftw: Reclaim workspace - %r\n", Status
));
319 // Read from working block again
321 Status
= ReadWorkSpaceData (
322 FtwDevice
->FtwFvBlock
,
323 FtwDevice
->WorkBlockSize
,
324 FtwDevice
->FtwWorkSpaceLba
,
325 FtwDevice
->FtwWorkSpaceBase
,
326 FtwDevice
->FtwWorkSpaceSize
,
327 FtwDevice
->FtwWorkSpace
329 if (EFI_ERROR (Status
)) {
333 Status
= FtwGetLastWriteHeader (
334 FtwDevice
->FtwWorkSpaceHeader
,
335 FtwDevice
->FtwWorkSpaceSize
,
336 &FtwDevice
->FtwLastWriteHeader
338 if (EFI_ERROR (Status
)) {
343 // Refresh the FtwLastWriteRecord
345 Status
= FtwGetLastWriteRecord (
346 FtwDevice
->FtwLastWriteHeader
,
347 &FtwDevice
->FtwLastWriteRecord
349 if (EFI_ERROR (Status
)) {
357 Reclaim the work space on the working block.
359 @param FtwDevice Point to private data of FTW driver
360 @param PreserveRecord Whether to preserve the working record is needed
362 @retval EFI_SUCCESS The function completed successfully
363 @retval EFI_OUT_OF_RESOURCES Allocate memory error
364 @retval EFI_ABORTED The function could not complete successfully
368 FtwReclaimWorkSpace (
369 IN EFI_FTW_DEVICE
*FtwDevice
,
370 IN BOOLEAN PreserveRecord
375 EFI_FAULT_TOLERANT_WRITE_HEADER
*Header
;
377 UINTN TempBufferSize
;
378 UINTN SpareBufferSize
;
380 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*WorkingBlockHeader
;
383 EFI_LBA WorkSpaceLbaOffset
;
385 DEBUG ((EFI_D_INFO
, "Ftw: start to reclaim work space\n"));
387 WorkSpaceLbaOffset
= FtwDevice
->FtwWorkSpaceLba
- FtwDevice
->FtwWorkBlockLba
;
390 // Read all original data from working block to a memory buffer
392 TempBufferSize
= FtwDevice
->NumberOfWorkBlock
* FtwDevice
->WorkBlockSize
;
393 TempBuffer
= AllocateZeroPool (TempBufferSize
);
394 if (TempBuffer
== NULL
) {
395 return EFI_OUT_OF_RESOURCES
;
399 for (Index
= 0; Index
< FtwDevice
->NumberOfWorkBlock
; Index
+= 1) {
400 Length
= FtwDevice
->WorkBlockSize
;
401 Status
= FtwDevice
->FtwFvBlock
->Read (
402 FtwDevice
->FtwFvBlock
,
403 FtwDevice
->FtwWorkBlockLba
+ Index
,
408 if (EFI_ERROR (Status
)) {
409 FreePool (TempBuffer
);
416 // Clean up the workspace, remove all the completed records.
419 (UINTN
) WorkSpaceLbaOffset
* FtwDevice
->WorkBlockSize
+
420 FtwDevice
->FtwWorkSpaceBase
;
423 // Clear the content of buffer that will save the new work space data
425 SetMem (Ptr
, FtwDevice
->FtwWorkSpaceSize
, FTW_ERASED_BYTE
);
428 // Copy EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER to buffer
432 FtwDevice
->FtwWorkSpaceHeader
,
433 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
)
435 if (PreserveRecord
) {
437 // Get the last record following the header,
439 Status
= FtwGetLastWriteHeader (
440 FtwDevice
->FtwWorkSpaceHeader
,
441 FtwDevice
->FtwWorkSpaceSize
,
442 &FtwDevice
->FtwLastWriteHeader
444 Header
= FtwDevice
->FtwLastWriteHeader
;
445 if (!EFI_ERROR (Status
) && (Header
!= NULL
) && (Header
->Complete
!= FTW_VALID_STATE
) && (Header
->HeaderAllocated
== FTW_VALID_STATE
)) {
447 Ptr
+ sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
),
448 FtwDevice
->FtwLastWriteHeader
,
449 FTW_WRITE_TOTAL_SIZE (Header
->NumberOfWrites
, Header
->PrivateDataSize
)
455 FtwDevice
->FtwWorkSpace
,
457 FtwDevice
->FtwWorkSpaceSize
460 FtwGetLastWriteHeader (
461 FtwDevice
->FtwWorkSpaceHeader
,
462 FtwDevice
->FtwWorkSpaceSize
,
463 &FtwDevice
->FtwLastWriteHeader
466 FtwGetLastWriteRecord (
467 FtwDevice
->FtwLastWriteHeader
,
468 &FtwDevice
->FtwLastWriteRecord
472 // Set the WorkingBlockValid and WorkingBlockInvalid as INVALID
474 WorkingBlockHeader
= (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*) (TempBuffer
+
475 (UINTN
) WorkSpaceLbaOffset
* FtwDevice
->WorkBlockSize
+
476 FtwDevice
->FtwWorkSpaceBase
);
477 WorkingBlockHeader
->WorkingBlockValid
= FTW_INVALID_STATE
;
478 WorkingBlockHeader
->WorkingBlockInvalid
= FTW_INVALID_STATE
;
481 // Try to keep the content of spare block
482 // Save spare block into a spare backup memory buffer (Sparebuffer)
484 SpareBufferSize
= FtwDevice
->SpareAreaLength
;
485 SpareBuffer
= AllocatePool (SpareBufferSize
);
486 if (SpareBuffer
== NULL
) {
487 FreePool (TempBuffer
);
488 return EFI_OUT_OF_RESOURCES
;
492 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
493 Length
= FtwDevice
->SpareBlockSize
;
494 Status
= FtwDevice
->FtwBackupFvb
->Read (
495 FtwDevice
->FtwBackupFvb
,
496 FtwDevice
->FtwSpareLba
+ Index
,
501 if (EFI_ERROR (Status
)) {
502 FreePool (TempBuffer
);
503 FreePool (SpareBuffer
);
510 // Write the memory buffer to spare block
512 Status
= FtwEraseSpareBlock (FtwDevice
);
513 if (EFI_ERROR (Status
)) {
514 FreePool (TempBuffer
);
515 FreePool (SpareBuffer
);
519 for (Index
= 0; TempBufferSize
> 0; Index
+= 1) {
520 if (TempBufferSize
> FtwDevice
->SpareBlockSize
) {
521 Length
= FtwDevice
->SpareBlockSize
;
523 Length
= TempBufferSize
;
525 Status
= FtwDevice
->FtwBackupFvb
->Write (
526 FtwDevice
->FtwBackupFvb
,
527 FtwDevice
->FtwSpareLba
+ Index
,
532 if (EFI_ERROR (Status
)) {
533 FreePool (TempBuffer
);
534 FreePool (SpareBuffer
);
539 TempBufferSize
-= Length
;
544 FreePool (TempBuffer
);
547 // Set the WorkingBlockValid in spare block
549 Status
= FtwUpdateFvState (
550 FtwDevice
->FtwBackupFvb
,
551 FtwDevice
->SpareBlockSize
,
552 FtwDevice
->FtwSpareLba
+ FtwDevice
->FtwWorkSpaceLbaInSpare
,
553 FtwDevice
->FtwWorkSpaceBaseInSpare
+ sizeof (EFI_GUID
) + sizeof (UINT32
),
556 if (EFI_ERROR (Status
)) {
557 FreePool (SpareBuffer
);
561 // Before erase the working block, set WorkingBlockInvalid in working block.
563 // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
564 // WorkingBlockInvalid);
566 Status
= FtwUpdateFvState (
567 FtwDevice
->FtwFvBlock
,
568 FtwDevice
->WorkBlockSize
,
569 FtwDevice
->FtwWorkSpaceLba
,
570 FtwDevice
->FtwWorkSpaceBase
+ sizeof (EFI_GUID
) + sizeof (UINT32
),
571 WORKING_BLOCK_INVALID
573 if (EFI_ERROR (Status
)) {
574 FreePool (SpareBuffer
);
578 FtwDevice
->FtwWorkSpaceHeader
->WorkingBlockInvalid
= FTW_VALID_STATE
;
581 // Write the spare block to working block
583 Status
= FlushSpareBlockToWorkingBlock (FtwDevice
);
584 if (EFI_ERROR (Status
)) {
585 FreePool (SpareBuffer
);
589 // Restore spare backup buffer into spare block , if no failure happened during FtwWrite.
591 Status
= FtwEraseSpareBlock (FtwDevice
);
592 if (EFI_ERROR (Status
)) {
593 FreePool (SpareBuffer
);
597 for (Index
= 0; Index
< FtwDevice
->NumberOfSpareBlock
; Index
+= 1) {
598 Length
= FtwDevice
->SpareBlockSize
;
599 Status
= FtwDevice
->FtwBackupFvb
->Write (
600 FtwDevice
->FtwBackupFvb
,
601 FtwDevice
->FtwSpareLba
+ Index
,
606 if (EFI_ERROR (Status
)) {
607 FreePool (SpareBuffer
);
614 FreePool (SpareBuffer
);
616 DEBUG ((EFI_D_INFO
, "Ftw: reclaim work space successfully\n"));