]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.c
Add full version FaultTolerantWrite Dxe driver.
[mirror_edk2.git] / MdeModulePkg / Universal / FaultTolerantWriteDxe / FaultTolerantWrite.c
1 /** @file
2
3 This is a simple fault tolerant write driver.
4
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.
9
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.
13
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.
16
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.
22
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.
28
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.
42
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
48
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.
51
52 **/
53
54 #include "FaultTolerantWrite.h"
55
56
57 //
58 // Fault Tolerant Write Protocol API
59 //
60 /**
61 Query the largest block that may be updated in a fault tolerant manner.
62
63
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.
67
68 @return EFI_SUCCESS The function completed successfully
69
70 **/
71 EFI_STATUS
72 EFIAPI
73 FtwGetMaxBlockSize (
74 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,
75 OUT UINTN *BlockSize
76 )
77 {
78 EFI_FTW_DEVICE *FtwDevice;
79
80 if (!FeaturePcdGet(PcdFullFtwServiceEnable)) {
81 return EFI_UNSUPPORTED;
82 }
83
84 FtwDevice = FTW_CONTEXT_FROM_THIS (This);
85
86 *BlockSize = FtwDevice->SpareAreaLength;
87
88 return EFI_SUCCESS;
89 }
90
91 /**
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.
97
98 All writes must be completed or aborted before another fault tolerant write can occur.
99
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.
106
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.
110
111 **/
112 EFI_STATUS
113 EFIAPI
114 FtwAllocate (
115 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,
116 IN EFI_GUID *CallerId,
117 IN UINTN PrivateDataSize,
118 IN UINTN NumberOfWrites
119 )
120 {
121 EFI_STATUS Status;
122 UINTN Length;
123 UINTN Offset;
124 EFI_FTW_DEVICE *FtwDevice;
125 EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader;
126
127 FtwDevice = FTW_CONTEXT_FROM_THIS (This);
128
129 Status = WorkSpaceRefresh (FtwDevice);
130 if (EFI_ERROR (Status)) {
131 return EFI_ABORTED;
132 }
133 //
134 // Check if there is enough space for the coming allocation
135 //
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;
139 }
140 //
141 // Find the last write header and record.
142 // If the FtwHeader is complete, skip the completed last write header/records
143 //
144 FtwHeader = FtwDevice->FtwLastWriteHeader;
145
146 //
147 // Previous write has not completed, access denied.
148 //
149 if ((FtwHeader->HeaderAllocated == FTW_VALID_STATE) || (FtwHeader->WritesAllocated == FTW_VALID_STATE)) {
150 return EFI_ACCESS_DENIED;
151 }
152 //
153 // If workspace is not enough, then reclaim workspace
154 //
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)) {
159 return EFI_ABORTED;
160 }
161
162 FtwHeader = FtwDevice->FtwLastWriteHeader;
163 }
164 //
165 // Prepare FTW write header,
166 // overwrite the buffer and write to workspace.
167 //
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;
174
175 Length = sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER);
176 Status = FtwDevice->FtwFvBlock->Write (
177 FtwDevice->FtwFvBlock,
178 FtwDevice->FtwWorkSpaceLba,
179 FtwDevice->FtwWorkSpaceBase + Offset,
180 &Length,
181 (UINT8 *) FtwHeader
182 );
183 if (EFI_ERROR (Status)) {
184 return EFI_ABORTED;
185 }
186 //
187 // Update Header->WriteAllocated as VALID
188 //
189 Status = FtwUpdateFvState (
190 FtwDevice->FtwFvBlock,
191 FtwDevice->FtwWorkSpaceLba,
192 FtwDevice->FtwWorkSpaceBase + Offset,
193 WRITES_ALLOCATED
194 );
195 if (EFI_ERROR (Status)) {
196 return EFI_ABORTED;
197 }
198
199 DEBUG (
200 (EFI_D_ERROR,
201 "Ftw: Allocate() success, Caller:%g, # %d\n",
202 CallerId,
203 NumberOfWrites)
204 );
205
206 return EFI_SUCCESS;
207 }
208
209
210 /**
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.
214
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.
218
219 @retval EFI_SUCCESS The function completed successfully
220 @retval EFI_ABORTED The function could not complete successfully
221
222 **/
223 EFI_STATUS
224 FtwWriteRecord (
225 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,
226 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb
227 )
228 {
229 EFI_STATUS Status;
230 EFI_FTW_DEVICE *FtwDevice;
231 EFI_FAULT_TOLERANT_WRITE_HEADER *Header;
232 EFI_FAULT_TOLERANT_WRITE_RECORD *Record;
233 UINTN Offset;
234
235 FtwDevice = FTW_CONTEXT_FROM_THIS (This);
236
237 //
238 // Spare Complete but Destination not complete,
239 // Recover the targt block with the spare block.
240 //
241 Header = FtwDevice->FtwLastWriteHeader;
242 Record = FtwDevice->FtwLastWriteRecord;
243
244 //
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.
247 //
248 if (IsWorkingBlock (FtwDevice, Fvb, Record->Lba)) {
249 //
250 // If target block is working block,
251 // it also need to set SPARE_COMPLETED to spare block.
252 //
253 Offset = (UINT8 *) Record - FtwDevice->FtwWorkSpace;
254 Status = FtwUpdateFvState (
255 FtwDevice->FtwBackupFvb,
256 FtwDevice->FtwWorkSpaceLba,
257 FtwDevice->FtwWorkSpaceBase + Offset,
258 SPARE_COMPLETED
259 );
260 if (EFI_ERROR (Status)) {
261 return EFI_ABORTED;
262 }
263
264 Status = FlushSpareBlockToWorkingBlock (FtwDevice);
265 } else if (IsBootBlock (FtwDevice, Fvb, Record->Lba)) {
266 //
267 // Update boot block
268 //
269 Status = FlushSpareBlockToBootBlock (FtwDevice);
270 } else {
271 //
272 // Update blocks other than working block or boot block
273 //
274 Status = FlushSpareBlockToTargetBlock (FtwDevice, Fvb, Record->Lba);
275 }
276
277 if (EFI_ERROR (Status)) {
278 return EFI_ABORTED;
279 }
280 //
281 // Record the DestionationComplete in record
282 //
283 Offset = (UINT8 *) Record - FtwDevice->FtwWorkSpace;
284 Status = FtwUpdateFvState (
285 FtwDevice->FtwFvBlock,
286 FtwDevice->FtwWorkSpaceLba,
287 FtwDevice->FtwWorkSpaceBase + Offset,
288 DEST_COMPLETED
289 );
290 if (EFI_ERROR (Status)) {
291 return EFI_ABORTED;
292 }
293
294 Record->DestinationComplete = FTW_VALID_STATE;
295
296 //
297 // If this is the last Write in these write sequence,
298 // set the complete flag of write header.
299 //
300 if (IsLastRecordOfWrites (Header, Record)) {
301 Offset = (UINT8 *) Header - FtwDevice->FtwWorkSpace;
302 Status = FtwUpdateFvState (
303 FtwDevice->FtwFvBlock,
304 FtwDevice->FtwWorkSpaceLba,
305 FtwDevice->FtwWorkSpaceBase + Offset,
306 WRITES_COMPLETED
307 );
308 Header->Complete = FTW_VALID_STATE;
309 if (EFI_ERROR (Status)) {
310 return EFI_ABORTED;
311 }
312 }
313
314 return EFI_SUCCESS;
315 }
316
317 /**
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.
322
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.
332
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.
340
341 **/
342 EFI_STATUS
343 EFIAPI
344 FtwWrite (
345 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,
346 IN EFI_LBA Lba,
347 IN UINTN Offset,
348 IN UINTN Length,
349 IN VOID *PrivateData,
350 IN EFI_HANDLE FvBlockHandle,
351 IN VOID *Buffer
352 )
353 {
354 EFI_STATUS Status;
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;
359 UINTN MyLength;
360 UINTN MyOffset;
361 UINTN MyBufferSize;
362 UINT8 *MyBuffer;
363 UINTN SpareBufferSize;
364 UINT8 *SpareBuffer;
365 UINTN Index;
366 UINT8 *Ptr;
367 EFI_PHYSICAL_ADDRESS FvbPhysicalAddress;
368
369 FtwDevice = FTW_CONTEXT_FROM_THIS (This);
370
371 Status = WorkSpaceRefresh (FtwDevice);
372 if (EFI_ERROR (Status)) {
373 return EFI_ABORTED;
374 }
375
376 Header = FtwDevice->FtwLastWriteHeader;
377 Record = FtwDevice->FtwLastWriteRecord;
378
379 if (IsErasedFlashBuffer ((UINT8 *) Header, sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER))) {
380 if (PrivateData == NULL) {
381 //
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.
384 //
385 Status = FtwAllocate (This, &gEfiCallerIdGuid, 0, 1);
386 if (EFI_ERROR (Status)) {
387 return Status;
388 }
389 } else {
390 //
391 // Ftw Write Header is not allocated
392 // Additional private data is not NULL, the private data size can't be determined.
393 //
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;
397 }
398 }
399
400 //
401 // If Record is out of the range of Header, return access denied.
402 //
403 if (((UINTN)((UINT8 *) Record - (UINT8 *) Header)) > WRITE_TOTAL_SIZE (Header->NumberOfWrites - 1, Header->PrivateDataSize)) {
404 return EFI_ACCESS_DENIED;
405 }
406
407 //
408 // Check the COMPLETE flag of last write header
409 //
410 if (Header->Complete == FTW_VALID_STATE) {
411 return EFI_ACCESS_DENIED;
412 }
413
414 if (Record->DestinationComplete == FTW_VALID_STATE) {
415 return EFI_ACCESS_DENIED;
416 }
417
418 if ((Record->SpareComplete == FTW_VALID_STATE) && (Record->DestinationComplete != FTW_VALID_STATE)) {
419 return EFI_NOT_READY;
420 }
421 //
422 // Check if the input data can fit within the target block
423 //
424 if ((Offset + Length) > FtwDevice->SpareAreaLength) {
425 return EFI_BAD_BUFFER_SIZE;
426 }
427 //
428 // Get the FVB protocol by handle
429 //
430 Status = FtwGetFvbByHandle (FvBlockHandle, &Fvb);
431 if (EFI_ERROR (Status)) {
432 return EFI_NOT_FOUND;
433 }
434
435 Status = Fvb->GetPhysicalAddress (Fvb, &FvbPhysicalAddress);
436 if (EFI_ERROR (Status)) {
437 DEBUG ((EFI_D_ERROR, "FtwLite: Get FVB physical address - %r\n", Status));
438 return EFI_ABORTED;
439 }
440
441 //
442 // Set BootBlockUpdate FLAG if it's updating boot block.
443 //
444 if (IsBootBlock (FtwDevice, Fvb, Lba)) {
445 Record->BootBlockUpdate = FTW_VALID_STATE;
446 }
447 //
448 // Write the record to the work space.
449 //
450 Record->Lba = Lba;
451 Record->Offset = Offset;
452 Record->Length = Length;
453 Record->FvBaseAddress = FvbPhysicalAddress;
454 CopyMem ((Record + 1), PrivateData, Header->PrivateDataSize);
455
456 MyOffset = (UINT8 *) Record - FtwDevice->FtwWorkSpace;
457 MyLength = RECORD_SIZE (Header->PrivateDataSize);
458
459 Status = FtwDevice->FtwFvBlock->Write (
460 FtwDevice->FtwFvBlock,
461 FtwDevice->FtwWorkSpaceLba,
462 FtwDevice->FtwWorkSpaceBase + MyOffset,
463 &MyLength,
464 (UINT8 *) Record
465 );
466 if (EFI_ERROR (Status)) {
467 return EFI_ABORTED;
468 }
469 //
470 // Record has written to working block, then do the data.
471 //
472 //
473 // Allocate a memory buffer
474 //
475 MyBufferSize = FtwDevice->SpareAreaLength;
476 MyBuffer = AllocatePool (MyBufferSize);
477 if (MyBuffer == NULL) {
478 return EFI_OUT_OF_RESOURCES;
479 }
480 //
481 // Read all original data from target block to memory buffer
482 //
483 Ptr = MyBuffer;
484 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
485 MyLength = FtwDevice->BlockSize;
486 Status = Fvb->Read (Fvb, Lba + Index, 0, &MyLength, Ptr);
487 if (EFI_ERROR (Status)) {
488 FreePool (MyBuffer);
489 return EFI_ABORTED;
490 }
491
492 Ptr += MyLength;
493 }
494 //
495 // Overwrite the updating range data with
496 // the input buffer content
497 //
498 CopyMem (MyBuffer + Offset, Buffer, Length);
499
500 //
501 // Try to keep the content of spare block
502 // Save spare block into a spare backup memory buffer (Sparebuffer)
503 //
504 SpareBufferSize = FtwDevice->SpareAreaLength;
505 SpareBuffer = AllocatePool (SpareBufferSize);
506 if (SpareBuffer == NULL) {
507 FreePool (MyBuffer);
508 return EFI_OUT_OF_RESOURCES;
509 }
510
511 Ptr = SpareBuffer;
512 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
513 MyLength = FtwDevice->BlockSize;
514 Status = FtwDevice->FtwBackupFvb->Read (
515 FtwDevice->FtwBackupFvb,
516 FtwDevice->FtwSpareLba + Index,
517 0,
518 &MyLength,
519 Ptr
520 );
521 if (EFI_ERROR (Status)) {
522 FreePool (MyBuffer);
523 FreePool (SpareBuffer);
524 return EFI_ABORTED;
525 }
526
527 Ptr += MyLength;
528 }
529 //
530 // Write the memory buffer to spare block
531 //
532 Status = FtwEraseSpareBlock (FtwDevice);
533 Ptr = MyBuffer;
534 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
535 MyLength = FtwDevice->BlockSize;
536 Status = FtwDevice->FtwBackupFvb->Write (
537 FtwDevice->FtwBackupFvb,
538 FtwDevice->FtwSpareLba + Index,
539 0,
540 &MyLength,
541 Ptr
542 );
543 if (EFI_ERROR (Status)) {
544 FreePool (MyBuffer);
545 FreePool (SpareBuffer);
546 return EFI_ABORTED;
547 }
548
549 Ptr += MyLength;
550 }
551 //
552 // Free MyBuffer
553 //
554 FreePool (MyBuffer);
555
556 //
557 // Set the SpareComplete in the FTW record,
558 //
559 MyOffset = (UINT8 *) Record - FtwDevice->FtwWorkSpace;
560 Status = FtwUpdateFvState (
561 FtwDevice->FtwFvBlock,
562 FtwDevice->FtwWorkSpaceLba,
563 FtwDevice->FtwWorkSpaceBase + MyOffset,
564 SPARE_COMPLETED
565 );
566 if (EFI_ERROR (Status)) {
567 FreePool (SpareBuffer);
568 return EFI_ABORTED;
569 }
570
571 Record->SpareComplete = FTW_VALID_STATE;
572
573 //
574 // Since the content has already backuped in spare block, the write is
575 // guaranteed to be completed with fault tolerant manner.
576 //
577 Status = FtwWriteRecord (This, Fvb);
578 if (EFI_ERROR (Status)) {
579 FreePool (SpareBuffer);
580 return EFI_ABORTED;
581 }
582 //
583 // Restore spare backup buffer into spare block , if no failure happened during FtwWrite.
584 //
585 Status = FtwEraseSpareBlock (FtwDevice);
586 Ptr = SpareBuffer;
587 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
588 MyLength = FtwDevice->BlockSize;
589 Status = FtwDevice->FtwBackupFvb->Write (
590 FtwDevice->FtwBackupFvb,
591 FtwDevice->FtwSpareLba + Index,
592 0,
593 &MyLength,
594 Ptr
595 );
596 if (EFI_ERROR (Status)) {
597 FreePool (SpareBuffer);
598 return EFI_ABORTED;
599 }
600
601 Ptr += MyLength;
602 }
603 //
604 // All success.
605 //
606 FreePool (SpareBuffer);
607
608 DEBUG (
609 (EFI_D_ERROR,
610 "Ftw: Write() success, (Lba:Offset)=(%lx:0x%x), Length: 0x%x\n",
611 Lba,
612 Offset,
613 Length)
614 );
615
616 return EFI_SUCCESS;
617 }
618
619 /**
620 Restarts a previously interrupted write. The caller must provide the
621 block protocol needed to complete the interrupted write.
622
623 @param This The pointer to this protocol instance.
624 @param FvBlockHandle The handle of FVB protocol that provides services for
625 reading, writing, and erasing the target block.
626
627 @retval EFI_SUCCESS The function completed successfully
628 @retval EFI_ACCESS_DENIED No pending writes exist
629 @retval EFI_NOT_FOUND FVB protocol not found by the handle
630 @retval EFI_ABORTED The function could not complete successfully
631
632 **/
633 EFI_STATUS
634 EFIAPI
635 FtwRestart (
636 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,
637 IN EFI_HANDLE FvBlockHandle
638 )
639 {
640 EFI_STATUS Status;
641 EFI_FTW_DEVICE *FtwDevice;
642 EFI_FAULT_TOLERANT_WRITE_HEADER *Header;
643 EFI_FAULT_TOLERANT_WRITE_RECORD *Record;
644 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
645
646 FtwDevice = FTW_CONTEXT_FROM_THIS (This);
647
648 Status = WorkSpaceRefresh (FtwDevice);
649 if (EFI_ERROR (Status)) {
650 return EFI_ABORTED;
651 }
652
653 Header = FtwDevice->FtwLastWriteHeader;
654 Record = FtwDevice->FtwLastWriteRecord;
655
656 //
657 // Spare Complete but Destination not complete,
658 // Recover the targt block with the spare block.
659 //
660 Status = FtwGetFvbByHandle (FvBlockHandle, &Fvb);
661 if (EFI_ERROR (Status)) {
662 return EFI_NOT_FOUND;
663 }
664
665 //
666 // Check the COMPLETE flag of last write header
667 //
668 if (Header->Complete == FTW_VALID_STATE) {
669 return EFI_ACCESS_DENIED;
670 }
671
672 //
673 // Check the flags of last write record
674 //
675 if (Record->DestinationComplete == FTW_VALID_STATE) {
676 return EFI_ACCESS_DENIED;
677 }
678
679 if ((Record->SpareComplete != FTW_VALID_STATE)) {
680 return EFI_ABORTED;
681 }
682
683 //
684 // Since the content has already backuped in spare block, the write is
685 // guaranteed to be completed with fault tolerant manner.
686 //
687 Status = FtwWriteRecord (This, Fvb);
688 if (EFI_ERROR (Status)) {
689 return EFI_ABORTED;
690 }
691
692 //
693 // Erase Spare block
694 // This is restart, no need to keep spareblock content.
695 //
696 FtwEraseSpareBlock (FtwDevice);
697
698 DEBUG ((EFI_D_ERROR, "Ftw: Restart() success \n"));
699 return EFI_SUCCESS;
700 }
701
702 /**
703 Aborts all previous allocated writes.
704
705 @param This The pointer to this protocol instance.
706
707 @retval EFI_SUCCESS The function completed successfully
708 @retval EFI_ABORTED The function could not complete successfully.
709 @retval EFI_NOT_FOUND No allocated writes exist.
710
711 **/
712 EFI_STATUS
713 EFIAPI
714 FtwAbort (
715 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This
716 )
717 {
718 EFI_STATUS Status;
719 UINTN Offset;
720 EFI_FTW_DEVICE *FtwDevice;
721
722 FtwDevice = FTW_CONTEXT_FROM_THIS (This);
723
724 Status = WorkSpaceRefresh (FtwDevice);
725 if (EFI_ERROR (Status)) {
726 return EFI_ABORTED;
727 }
728
729 if (FtwDevice->FtwLastWriteHeader->Complete == FTW_VALID_STATE) {
730 return EFI_NOT_FOUND;
731 }
732 //
733 // Update the complete state of the header as VALID and abort.
734 //
735 Offset = (UINT8 *) FtwDevice->FtwLastWriteHeader - FtwDevice->FtwWorkSpace;
736 Status = FtwUpdateFvState (
737 FtwDevice->FtwFvBlock,
738 FtwDevice->FtwWorkSpaceLba,
739 FtwDevice->FtwWorkSpaceBase + Offset,
740 WRITES_COMPLETED
741 );
742 if (EFI_ERROR (Status)) {
743 return EFI_ABORTED;
744 }
745
746 FtwDevice->FtwLastWriteHeader->Complete = FTW_VALID_STATE;
747
748 DEBUG ((EFI_D_ERROR, "Ftw: Abort() success \n"));
749 return EFI_SUCCESS;
750 }
751
752 /**
753 Starts a target block update. This records information about the write
754 in fault tolerant storage and will complete the write in a recoverable
755 manner, ensuring at all times that either the original contents or
756 the modified contents are available.
757
758 @param This The pointer to this protocol instance.
759 @param CallerId The GUID identifying the last write.
760 @param Lba The logical block address of the last write.
761 @param Offset The offset within the block of the last write.
762 @param Length The length of the last write.
763 @param PrivateDataSize bytes from the private data
764 stored for this write.
765 @param PrivateData A pointer to a buffer. The function will copy
766 @param Complete A Boolean value with TRUE indicating
767 that the write was completed.
768
769 @retval EFI_SUCCESS The function completed successfully
770 @retval EFI_ABORTED The function could not complete successfully
771 @retval EFI_NOT_FOUND No allocated writes exist
772 @retval EFI_BUFFER_TOO_SMALL Input buffer is not larget enough
773
774 **/
775 EFI_STATUS
776 EFIAPI
777 FtwGetLastWrite (
778 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,
779 OUT EFI_GUID *CallerId,
780 OUT EFI_LBA *Lba,
781 OUT UINTN *Offset,
782 OUT UINTN *Length,
783 IN OUT UINTN *PrivateDataSize,
784 OUT VOID *PrivateData,
785 OUT BOOLEAN *Complete
786 )
787 {
788 EFI_STATUS Status;
789 EFI_FTW_DEVICE *FtwDevice;
790 EFI_FAULT_TOLERANT_WRITE_HEADER *Header;
791 EFI_FAULT_TOLERANT_WRITE_RECORD *Record;
792
793 if (!FeaturePcdGet(PcdFullFtwServiceEnable)) {
794 return EFI_UNSUPPORTED;
795 }
796
797 FtwDevice = FTW_CONTEXT_FROM_THIS (This);
798
799 Status = WorkSpaceRefresh (FtwDevice);
800 if (EFI_ERROR (Status)) {
801 return EFI_ABORTED;
802 }
803
804 Header = FtwDevice->FtwLastWriteHeader;
805 Record = FtwDevice->FtwLastWriteRecord;
806
807 //
808 // If Header is incompleted and the last record has completed, then
809 // call Abort() to set the Header->Complete FLAG.
810 //
811 if ((Header->Complete != FTW_VALID_STATE) &&
812 (Record->DestinationComplete == FTW_VALID_STATE) &&
813 IsLastRecordOfWrites (Header, Record)
814 ) {
815
816 Status = FtwAbort (This);
817 *Complete = TRUE;
818 return EFI_NOT_FOUND;
819 }
820 //
821 // If there is no write header/record, return not found.
822 //
823 if (Header->HeaderAllocated != FTW_VALID_STATE) {
824 *Complete = TRUE;
825 return EFI_NOT_FOUND;
826 }
827 //
828 // If this record SpareComplete has not set, then it can not restart.
829 //
830 if (Record->SpareComplete != FTW_VALID_STATE) {
831 if (IsFirstRecordOfWrites (Header, Record)) {
832 //
833 // The First record cannot be restart and target is still healthy,
834 // so abort() is a safe solution.
835 //
836 FtwAbort (This);
837
838 *Complete = TRUE;
839 return EFI_NOT_FOUND;
840 } else {
841 //
842 // Step back to the previous record
843 //
844 GetPreviousRecordOfWrites (Header, &Record);
845 }
846 }
847 //
848 // Fill all the requested values
849 //
850 CopyMem (CallerId, &Header->CallerId, sizeof (EFI_GUID));
851 *Lba = Record->Lba;
852 *Offset = Record->Offset;
853 *Length = Record->Length;
854 *Complete = (BOOLEAN) (Record->DestinationComplete == FTW_VALID_STATE);
855
856 if (*PrivateDataSize < Header->PrivateDataSize) {
857 *PrivateDataSize = Header->PrivateDataSize;
858 PrivateData = NULL;
859 Status = EFI_BUFFER_TOO_SMALL;
860 } else {
861 *PrivateDataSize = Header->PrivateDataSize;
862 CopyMem (PrivateData, Record + 1, *PrivateDataSize);
863 Status = EFI_SUCCESS;
864 }
865
866 DEBUG ((EFI_D_ERROR, "Ftw: GetLasetWrite() success\n"));
867
868 return Status;
869 }
870
871 /**
872 This function is the entry point of the Fault Tolerant Write driver.
873
874 @param ImageHandle A handle for the image that is initializing this driver
875 @param SystemTable A pointer to the EFI system table
876
877 @return EFI_SUCCESS FTW has finished the initialization
878 @retval EFI_NOT_FOUND Locate FVB protocol error
879 @retval EFI_OUT_OF_RESOURCES Allocate memory error
880 @retval EFI_VOLUME_CORRUPTED Firmware volume is error
881 @retval EFI_ABORTED FTW initialization error
882
883 **/
884 EFI_STATUS
885 EFIAPI
886 InitializeFaultTolerantWrite (
887 IN EFI_HANDLE ImageHandle,
888 IN EFI_SYSTEM_TABLE *SystemTable
889 )
890 {
891 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
892 UINTN Index;
893 EFI_HANDLE *HandleBuffer;
894 UINTN HandleCount;
895 EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;
896 EFI_PHYSICAL_ADDRESS BaseAddress;
897 EFI_FTW_DEVICE *FtwDevice;
898 EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader;
899 UINTN Length;
900 EFI_STATUS Status;
901 UINTN Offset;
902 EFI_FV_BLOCK_MAP_ENTRY *FvbMapEntry;
903 UINT32 LbaIndex;
904 EFI_HANDLE FvbHandle;
905
906 //
907 // Allocate Private data of this driver,
908 // INCLUDING THE FtwWorkSpace[FTW_WORK_SPACE_SIZE].
909 //
910 FvbHandle = NULL;
911 FtwDevice = NULL;
912 FtwDevice = AllocatePool (sizeof (EFI_FTW_DEVICE) + PcdGet32 (PcdFlashNvStorageFtwWorkingSize));
913 if (FtwDevice == NULL) {
914 return EFI_OUT_OF_RESOURCES;
915 }
916
917 ZeroMem (FtwDevice, sizeof (EFI_FTW_DEVICE));
918 FtwDevice->Signature = FTW_DEVICE_SIGNATURE;
919
920 //
921 // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.
922 //
923
924 FtwDevice->WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwWorkingBase);
925 FtwDevice->WorkSpaceLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwWorkingSize);
926
927 FtwDevice->SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwSpareBase);
928 FtwDevice->SpareAreaLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwSpareSize);
929
930 if ((FtwDevice->WorkSpaceLength == 0) || (FtwDevice->SpareAreaLength == 0)) {
931 DEBUG ((EFI_D_ERROR, "Ftw: Workspace or Spare block does not exist!\n"));
932 FreePool (FtwDevice);
933 return EFI_OUT_OF_RESOURCES;
934 }
935 //
936 // Locate FVB protocol by handle
937 //
938 Status = gBS->LocateHandleBuffer (
939 ByProtocol,
940 &gEfiFirmwareVolumeBlockProtocolGuid,
941 NULL,
942 &HandleCount,
943 &HandleBuffer
944 );
945 if (EFI_ERROR (Status)) {
946 FreePool (FtwDevice);
947 return EFI_NOT_FOUND;
948 }
949
950 if (HandleCount <= 0) {
951 FreePool (FtwDevice);
952 return EFI_NOT_FOUND;
953 }
954
955 Fvb = NULL;
956 FtwDevice->FtwFvBlock = NULL;
957 FtwDevice->FtwBackupFvb = NULL;
958 FtwDevice->FtwWorkSpaceLba = (EFI_LBA) (-1);
959 FtwDevice->FtwSpareLba = (EFI_LBA) (-1);
960 for (Index = 0; Index < HandleCount; Index += 1) {
961 Status = gBS->HandleProtocol (
962 HandleBuffer[Index],
963 &gEfiFirmwareVolumeBlockProtocolGuid,
964 (VOID **) &Fvb
965 );
966 if (EFI_ERROR (Status)) {
967 FreePool (FtwDevice);
968 return Status;
969 }
970
971 Status = Fvb->GetPhysicalAddress (Fvb, &BaseAddress);
972 if (EFI_ERROR (Status)) {
973 continue;
974 }
975
976 FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) BaseAddress);
977
978 if ((FtwDevice->WorkSpaceAddress >= BaseAddress) &&
979 ((FtwDevice->WorkSpaceAddress + FtwDevice->WorkSpaceLength) <= (BaseAddress + FwVolHeader->FvLength))
980 ) {
981 FtwDevice->FtwFvBlock = Fvb;
982 //
983 // To get the LBA of work space
984 //
985 if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) {
986 //
987 // Now, one FV has one type of BlockLength
988 //
989 FvbMapEntry = &FwVolHeader->BlockMap[0];
990 for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) {
991 if ((FtwDevice->WorkSpaceAddress >= (BaseAddress + FvbMapEntry->Length * (LbaIndex - 1)))
992 && (FtwDevice->WorkSpaceAddress < (BaseAddress + FvbMapEntry->Length * LbaIndex))) {
993 FtwDevice->FtwWorkSpaceLba = LbaIndex - 1;
994 //
995 // Get the Work space size and Base(Offset)
996 //
997 FtwDevice->FtwWorkSpaceSize = FtwDevice->WorkSpaceLength;
998 FtwDevice->FtwWorkSpaceBase = (UINTN) (FtwDevice->WorkSpaceAddress - (BaseAddress + FvbMapEntry->Length * (LbaIndex - 1)));
999 break;
1000 }
1001 }
1002 }
1003 }
1004
1005 if ((FtwDevice->SpareAreaAddress >= BaseAddress) &&
1006 ((FtwDevice->SpareAreaAddress + FtwDevice->SpareAreaLength) <= (BaseAddress + FwVolHeader->FvLength))
1007 ) {
1008 FtwDevice->FtwBackupFvb = Fvb;
1009 //
1010 // To get the LBA of spare
1011 //
1012 if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) {
1013 //
1014 // Now, one FV has one type of BlockLength
1015 //
1016 FvbMapEntry = &FwVolHeader->BlockMap[0];
1017 for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) {
1018 if ((FtwDevice->SpareAreaAddress >= (BaseAddress + FvbMapEntry->Length * (LbaIndex - 1)))
1019 && (FtwDevice->SpareAreaAddress < (BaseAddress + FvbMapEntry->Length * LbaIndex))) {
1020 //
1021 // Get the NumberOfSpareBlock and BlockSize
1022 //
1023 FtwDevice->FtwSpareLba = LbaIndex - 1;
1024 FtwDevice->BlockSize = FvbMapEntry->Length;
1025 FtwDevice->NumberOfSpareBlock = FtwDevice->SpareAreaLength / FtwDevice->BlockSize;
1026 //
1027 // Check the range of spare area to make sure that it's in FV range
1028 //
1029 if ((FtwDevice->FtwSpareLba + FtwDevice->NumberOfSpareBlock) > FvbMapEntry->NumBlocks) {
1030 DEBUG ((EFI_D_ERROR, "Ftw: Spare area is out of FV range\n"));
1031 FreePool (FtwDevice);
1032 return EFI_ABORTED;
1033 }
1034 break;
1035 }
1036 }
1037 }
1038 }
1039 }
1040
1041 //
1042 // Calculate the start LBA of working block. Working block is an area which
1043 // contains working space in its last block and has the same size as spare
1044 // block, unless there are not enough blocks before the block that contains
1045 // working space.
1046 //
1047 FtwDevice->FtwWorkBlockLba = FtwDevice->FtwWorkSpaceLba - FtwDevice->NumberOfSpareBlock + 1;
1048 if ((INT64) (FtwDevice->FtwWorkBlockLba) < 0) {
1049 DEBUG ((EFI_D_ERROR, "Ftw: The spare block range is too large than the working block range!\n"));
1050 FreePool (FtwDevice);
1051 return EFI_ABORTED;
1052 }
1053
1054 if ((FtwDevice->FtwFvBlock == NULL) ||
1055 (FtwDevice->FtwBackupFvb == NULL) ||
1056 (FtwDevice->FtwWorkSpaceLba == (EFI_LBA) (-1)) ||
1057 (FtwDevice->FtwSpareLba == (EFI_LBA) (-1))
1058 ) {
1059 DEBUG ((EFI_D_ERROR, "Ftw: Working or spare FVB not ready\n"));
1060 FreePool (FtwDevice);
1061 return EFI_ABORTED;
1062 }
1063 //
1064 // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.
1065 //
1066 FtwDevice->FtwWorkSpace = (UINT8 *) (FtwDevice + 1);
1067 FtwDevice->FtwWorkSpaceHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) FtwDevice->FtwWorkSpace;
1068
1069 FtwDevice->FtwLastWriteHeader = NULL;
1070 FtwDevice->FtwLastWriteRecord = NULL;
1071
1072 //
1073 // Refresh the working space data from working block
1074 //
1075 Status = WorkSpaceRefresh (FtwDevice);
1076 if (EFI_ERROR (Status)) {
1077 goto Recovery;
1078 }
1079 //
1080 // If the working block workspace is not valid, try the spare block
1081 //
1082 if (!IsValidWorkSpace (FtwDevice->FtwWorkSpaceHeader)) {
1083 //
1084 // Read from spare block
1085 //
1086 Length = FtwDevice->FtwWorkSpaceSize;
1087 Status = FtwDevice->FtwBackupFvb->Read (
1088 FtwDevice->FtwBackupFvb,
1089 FtwDevice->FtwSpareLba,
1090 FtwDevice->FtwWorkSpaceBase,
1091 &Length,
1092 FtwDevice->FtwWorkSpace
1093 );
1094 if (EFI_ERROR (Status)) {
1095 goto Recovery;
1096 }
1097 //
1098 // If spare block is valid, then replace working block content.
1099 //
1100 if (IsValidWorkSpace (FtwDevice->FtwWorkSpaceHeader)) {
1101 Status = FlushSpareBlockToWorkingBlock (FtwDevice);
1102 DEBUG ((EFI_D_ERROR, "Ftw: Restart working block update in Init() - %r\n", Status));
1103 FtwAbort (&FtwDevice->FtwInstance);
1104 //
1105 // Refresh work space.
1106 //
1107 Status = WorkSpaceRefresh (FtwDevice);
1108 if (EFI_ERROR (Status)) {
1109 goto Recovery;
1110 }
1111 } else {
1112 DEBUG ((EFI_D_ERROR, "Ftw: Both are invalid, init workspace\n"));
1113 //
1114 // If both are invalid, then initialize work space.
1115 //
1116 SetMem (
1117 FtwDevice->FtwWorkSpace,
1118 FtwDevice->FtwWorkSpaceSize,
1119 FTW_ERASED_BYTE
1120 );
1121 InitWorkSpaceHeader (FtwDevice->FtwWorkSpaceHeader);
1122 //
1123 // Initialize the work space
1124 //
1125 Status = FtwReclaimWorkSpace (FtwDevice, FALSE);
1126 if (EFI_ERROR (Status)) {
1127 goto Recovery;
1128 }
1129 }
1130 }
1131
1132 //
1133 // If the FtwDevice->FtwLastWriteRecord is 1st record of write header &&
1134 // (! SpareComplete) THEN call Abort().
1135 //
1136 if ((FtwDevice->FtwLastWriteHeader->HeaderAllocated == FTW_VALID_STATE) &&
1137 (FtwDevice->FtwLastWriteRecord->SpareComplete != FTW_VALID_STATE) &&
1138 IsFirstRecordOfWrites (FtwDevice->FtwLastWriteHeader, FtwDevice->FtwLastWriteRecord)
1139 ) {
1140 DEBUG ((EFI_D_ERROR, "Ftw: Init.. find first record not SpareCompleted, abort()\n"));
1141 FtwAbort (&FtwDevice->FtwInstance);
1142 }
1143 //
1144 // If Header is incompleted and the last record has completed, then
1145 // call Abort() to set the Header->Complete FLAG.
1146 //
1147 if ((FtwDevice->FtwLastWriteHeader->Complete != FTW_VALID_STATE) &&
1148 (FtwDevice->FtwLastWriteRecord->DestinationComplete == FTW_VALID_STATE) &&
1149 IsLastRecordOfWrites (FtwDevice->FtwLastWriteHeader, FtwDevice->FtwLastWriteRecord)
1150 ) {
1151 DEBUG ((EFI_D_ERROR, "Ftw: Init.. find last record completed but header not, abort()\n"));
1152 FtwAbort (&FtwDevice->FtwInstance);
1153 }
1154 //
1155 // To check the workspace buffer following last Write header/records is EMPTY or not.
1156 // If it's not EMPTY, FTW also need to call reclaim().
1157 //
1158 FtwHeader = FtwDevice->FtwLastWriteHeader;
1159 Offset = (UINT8 *) FtwHeader - FtwDevice->FtwWorkSpace;
1160 if (FtwDevice->FtwWorkSpace[Offset] != FTW_ERASED_BYTE) {
1161 Offset += WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites, FtwHeader->PrivateDataSize);
1162 }
1163
1164 if (!IsErasedFlashBuffer (
1165 FtwDevice->FtwWorkSpace + Offset,
1166 FtwDevice->FtwWorkSpaceSize - Offset
1167 )) {
1168 Status = FtwReclaimWorkSpace (FtwDevice, TRUE);
1169 if (EFI_ERROR (Status)) {
1170 goto Recovery;
1171 }
1172 }
1173 //
1174 // Restart if it's boot block
1175 //
1176 if ((FtwDevice->FtwLastWriteHeader->Complete != FTW_VALID_STATE) &&
1177 (FtwDevice->FtwLastWriteRecord->SpareComplete == FTW_VALID_STATE)
1178 ) {
1179 if (FtwDevice->FtwLastWriteRecord->BootBlockUpdate == FTW_VALID_STATE) {
1180 Status = FlushSpareBlockToBootBlock (FtwDevice);
1181 DEBUG ((EFI_D_ERROR, "Ftw: Restart boot block update - %r\n", Status));
1182 if (EFI_ERROR (Status)) {
1183 goto Recovery;
1184 }
1185
1186 FtwAbort (&FtwDevice->FtwInstance);
1187 } else {
1188 //
1189 // if (SpareCompleted) THEN Restart to fault tolerant write.
1190 //
1191 FvbHandle = GetFvbByAddress (FtwDevice->FtwLastWriteRecord->FvBaseAddress, &Fvb);
1192 if (FvbHandle != NULL) {
1193 Status = FtwRestart (&FtwDevice->FtwInstance, FvbHandle);
1194 DEBUG ((EFI_D_ERROR, "FtwLite: Restart last write - %r\n", Status));
1195 if (EFI_ERROR (Status)) {
1196 goto Recovery;
1197 }
1198 }
1199 FtwAbort (&FtwDevice->FtwInstance);
1200 }
1201 }
1202
1203 //
1204 // Hook the protocol API
1205 //
1206 FtwDevice->FtwInstance.GetMaxBlockSize = FtwGetMaxBlockSize;
1207 FtwDevice->FtwInstance.Allocate = FtwAllocate;
1208 FtwDevice->FtwInstance.Write = FtwWrite;
1209 FtwDevice->FtwInstance.Restart = FtwRestart;
1210 FtwDevice->FtwInstance.Abort = FtwAbort;
1211 FtwDevice->FtwInstance.GetLastWrite = FtwGetLastWrite;
1212
1213 //
1214 // Install protocol interface
1215 //
1216 Status = gBS->InstallProtocolInterface (
1217 &FtwDevice->Handle,
1218 &gEfiFaultTolerantWriteProtocolGuid,
1219 EFI_NATIVE_INTERFACE,
1220 &FtwDevice->FtwInstance
1221 );
1222 if (EFI_ERROR (Status)) {
1223 goto Recovery;
1224 }
1225
1226 return EFI_SUCCESS;
1227
1228 Recovery:
1229
1230 if (FtwDevice != NULL) {
1231 FreePool (FtwDevice);
1232 }
1233
1234 DEBUG ((EFI_D_ERROR, "Ftw: Severe Error occurs, need to recovery\n"));
1235
1236 return EFI_VOLUME_CORRUPTED;
1237 }