]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / MdeModulePkg / Universal / FaultTolerantWriteDxe / FaultTolerantWrite.c
1 /** @file
2
3 These are the common Fault Tolerant Write (FTW) functions that are shared
4 by DXE FTW driver and SMM FTW driver.
5
6 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
8
9 **/
10
11 #include "FaultTolerantWrite.h"
12
13 //
14 // Fault Tolerant Write Protocol API
15 //
16
17 /**
18 Query the largest block that may be updated in a fault tolerant manner.
19
20
21 @param This The pointer to this protocol instance.
22 @param BlockSize A pointer to a caller allocated UINTN that is updated to
23 indicate the size of the largest block that can be updated.
24
25 @return EFI_SUCCESS The function completed successfully
26
27 **/
28 EFI_STATUS
29 EFIAPI
30 FtwGetMaxBlockSize (
31 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,
32 OUT UINTN *BlockSize
33 )
34 {
35 EFI_FTW_DEVICE *FtwDevice;
36
37 if (!FeaturePcdGet (PcdFullFtwServiceEnable)) {
38 return EFI_UNSUPPORTED;
39 }
40
41 FtwDevice = FTW_CONTEXT_FROM_THIS (This);
42
43 *BlockSize = FtwDevice->SpareAreaLength;
44
45 return EFI_SUCCESS;
46 }
47
48 /**
49 Allocates space for the protocol to maintain information about writes.
50 Since writes must be completed in a fault tolerant manner and multiple
51 updates will require more resources to be successful, this function
52 enables the protocol to ensure that enough space exists to track
53 information about the upcoming writes.
54
55 All writes must be completed or aborted before another fault tolerant write can occur.
56
57 @param This The pointer to this protocol instance.
58 @param CallerId The GUID identifying the write.
59 @param PrivateDataSize The size of the caller's private data
60 that must be recorded for each write.
61 @param NumberOfWrites The number of fault tolerant block writes
62 that will need to occur.
63
64 @return EFI_SUCCESS The function completed successfully
65 @retval EFI_ABORTED The function could not complete successfully.
66 @retval EFI_ACCESS_DENIED All allocated writes have not been completed.
67
68 **/
69 EFI_STATUS
70 EFIAPI
71 FtwAllocate (
72 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,
73 IN EFI_GUID *CallerId,
74 IN UINTN PrivateDataSize,
75 IN UINTN NumberOfWrites
76 )
77 {
78 EFI_STATUS Status;
79 UINTN Offset;
80 EFI_FTW_DEVICE *FtwDevice;
81 EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader;
82
83 FtwDevice = FTW_CONTEXT_FROM_THIS (This);
84
85 Status = WorkSpaceRefresh (FtwDevice);
86 if (EFI_ERROR (Status)) {
87 return EFI_ABORTED;
88 }
89
90 //
91 // Check if there is enough space for the coming allocation
92 //
93 if (FTW_WRITE_TOTAL_SIZE (NumberOfWrites, PrivateDataSize) > FtwDevice->FtwWorkSpaceHeader->WriteQueueSize) {
94 DEBUG ((DEBUG_ERROR, "Ftw: Allocate() request exceed Workspace, Caller: %g\n", CallerId));
95 return EFI_BUFFER_TOO_SMALL;
96 }
97
98 //
99 // Find the last write header and record.
100 // If the FtwHeader is complete, skip the completed last write header/records
101 //
102 FtwHeader = FtwDevice->FtwLastWriteHeader;
103
104 //
105 // Previous write has not completed, access denied.
106 //
107 if ((FtwHeader->HeaderAllocated == FTW_VALID_STATE) || (FtwHeader->WritesAllocated == FTW_VALID_STATE)) {
108 return EFI_ACCESS_DENIED;
109 }
110
111 //
112 // If workspace is not enough, then reclaim workspace
113 //
114 Offset = (UINT8 *)FtwHeader - (UINT8 *)FtwDevice->FtwWorkSpace;
115 if (Offset + FTW_WRITE_TOTAL_SIZE (NumberOfWrites, PrivateDataSize) > FtwDevice->FtwWorkSpaceSize) {
116 Status = FtwReclaimWorkSpace (FtwDevice, TRUE);
117 if (EFI_ERROR (Status)) {
118 return EFI_ABORTED;
119 }
120
121 FtwHeader = FtwDevice->FtwLastWriteHeader;
122 }
123
124 //
125 // Prepare FTW write header,
126 // overwrite the buffer and write to workspace.
127 //
128 FtwHeader->WritesAllocated = FTW_INVALID_STATE;
129 FtwHeader->Complete = FTW_INVALID_STATE;
130 CopyMem (&FtwHeader->CallerId, CallerId, sizeof (EFI_GUID));
131 FtwHeader->NumberOfWrites = NumberOfWrites;
132 FtwHeader->PrivateDataSize = PrivateDataSize;
133 FtwHeader->HeaderAllocated = FTW_VALID_STATE;
134
135 Status = WriteWorkSpaceData (
136 FtwDevice->FtwFvBlock,
137 FtwDevice->WorkBlockSize,
138 FtwDevice->FtwWorkSpaceLba,
139 FtwDevice->FtwWorkSpaceBase + Offset,
140 sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER),
141 (UINT8 *)FtwHeader
142 );
143 if (EFI_ERROR (Status)) {
144 return EFI_ABORTED;
145 }
146
147 //
148 // Update Header->WriteAllocated as VALID
149 //
150 Status = FtwUpdateFvState (
151 FtwDevice->FtwFvBlock,
152 FtwDevice->WorkBlockSize,
153 FtwDevice->FtwWorkSpaceLba,
154 FtwDevice->FtwWorkSpaceBase + Offset,
155 WRITES_ALLOCATED
156 );
157 if (EFI_ERROR (Status)) {
158 return EFI_ABORTED;
159 }
160
161 DEBUG (
162 (DEBUG_INFO,
163 "Ftw: Allocate() success, Caller:%g, # %d\n",
164 CallerId,
165 NumberOfWrites)
166 );
167
168 return EFI_SUCCESS;
169 }
170
171 /**
172 Write a record with fault tolerant manner.
173 Since the content has already backuped in spare block, the write is
174 guaranteed to be completed with fault tolerant manner.
175
176 @param This The pointer to this protocol instance.
177 @param Fvb The FVB protocol that provides services for
178 reading, writing, and erasing the target block.
179 @param BlockSize The size of the block.
180
181 @retval EFI_SUCCESS The function completed successfully
182 @retval EFI_ABORTED The function could not complete successfully
183
184 **/
185 EFI_STATUS
186 FtwWriteRecord (
187 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,
188 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb,
189 IN UINTN BlockSize
190 )
191 {
192 EFI_STATUS Status;
193 EFI_FTW_DEVICE *FtwDevice;
194 EFI_FAULT_TOLERANT_WRITE_HEADER *Header;
195 EFI_FAULT_TOLERANT_WRITE_RECORD *Record;
196 UINTN Offset;
197 UINTN NumberOfWriteBlocks;
198
199 FtwDevice = FTW_CONTEXT_FROM_THIS (This);
200
201 //
202 // Spare Complete but Destination not complete,
203 // Recover the target block with the spare block.
204 //
205 Header = FtwDevice->FtwLastWriteHeader;
206 Record = FtwDevice->FtwLastWriteRecord;
207
208 //
209 // IF target block is working block, THEN Flush Spare Block To Working Block;
210 // ELSE flush spare block to target block, which may be boot block after all.
211 //
212 if (IsWorkingBlock (FtwDevice, Fvb, Record->Lba)) {
213 //
214 // If target block is working block,
215 // it also need to set SPARE_COMPLETED to spare block.
216 //
217 Offset = (UINT8 *)Record - FtwDevice->FtwWorkSpace;
218 Status = FtwUpdateFvState (
219 FtwDevice->FtwBackupFvb,
220 FtwDevice->SpareBlockSize,
221 FtwDevice->FtwSpareLba + FtwDevice->FtwWorkSpaceLbaInSpare,
222 FtwDevice->FtwWorkSpaceBaseInSpare + Offset,
223 SPARE_COMPLETED
224 );
225 if (EFI_ERROR (Status)) {
226 return EFI_ABORTED;
227 }
228
229 Status = FlushSpareBlockToWorkingBlock (FtwDevice);
230 } else if (IsBootBlock (FtwDevice, Fvb)) {
231 //
232 // Update boot block
233 //
234 Status = FlushSpareBlockToBootBlock (FtwDevice);
235 } else {
236 //
237 // Update blocks other than working block or boot block
238 //
239 NumberOfWriteBlocks = FTW_BLOCKS ((UINTN)(Record->Offset + Record->Length), BlockSize);
240 Status = FlushSpareBlockToTargetBlock (FtwDevice, Fvb, Record->Lba, BlockSize, NumberOfWriteBlocks);
241 }
242
243 if (EFI_ERROR (Status)) {
244 return EFI_ABORTED;
245 }
246
247 //
248 // Record the DestionationComplete in record
249 //
250 Offset = (UINT8 *)Record - FtwDevice->FtwWorkSpace;
251 Status = FtwUpdateFvState (
252 FtwDevice->FtwFvBlock,
253 FtwDevice->WorkBlockSize,
254 FtwDevice->FtwWorkSpaceLba,
255 FtwDevice->FtwWorkSpaceBase + Offset,
256 DEST_COMPLETED
257 );
258 if (EFI_ERROR (Status)) {
259 return EFI_ABORTED;
260 }
261
262 Record->DestinationComplete = FTW_VALID_STATE;
263
264 //
265 // If this is the last Write in these write sequence,
266 // set the complete flag of write header.
267 //
268 if (IsLastRecordOfWrites (Header, Record)) {
269 Offset = (UINT8 *)Header - FtwDevice->FtwWorkSpace;
270 Status = FtwUpdateFvState (
271 FtwDevice->FtwFvBlock,
272 FtwDevice->WorkBlockSize,
273 FtwDevice->FtwWorkSpaceLba,
274 FtwDevice->FtwWorkSpaceBase + Offset,
275 WRITES_COMPLETED
276 );
277 Header->Complete = FTW_VALID_STATE;
278 if (EFI_ERROR (Status)) {
279 return EFI_ABORTED;
280 }
281 }
282
283 return EFI_SUCCESS;
284 }
285
286 /**
287 Starts a target block update. This function will record data about write
288 in fault tolerant storage and will complete the write in a recoverable
289 manner, ensuring at all times that either the original contents or
290 the modified contents are available.
291
292 @param This The pointer to this protocol instance.
293 @param Lba The logical block address of the target block.
294 @param Offset The offset within the target block to place the data.
295 @param Length The number of bytes to write to the target block.
296 @param PrivateData A pointer to private data that the caller requires to
297 complete any pending writes in the event of a fault.
298 @param FvBlockHandle The handle of FVB protocol that provides services for
299 reading, writing, and erasing the target block.
300 @param Buffer The data to write.
301
302 @retval EFI_SUCCESS The function completed successfully
303 @retval EFI_ABORTED The function could not complete successfully.
304 @retval EFI_BAD_BUFFER_SIZE The input data can't fit within the spare block.
305 Offset + *NumBytes > SpareAreaLength.
306 @retval EFI_ACCESS_DENIED No writes have been allocated.
307 @retval EFI_OUT_OF_RESOURCES Cannot allocate enough memory resource.
308 @retval EFI_NOT_FOUND Cannot find FVB protocol by handle.
309
310 **/
311 EFI_STATUS
312 EFIAPI
313 FtwWrite (
314 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,
315 IN EFI_LBA Lba,
316 IN UINTN Offset,
317 IN UINTN Length,
318 IN VOID *PrivateData,
319 IN EFI_HANDLE FvBlockHandle,
320 IN VOID *Buffer
321 )
322 {
323 EFI_STATUS Status;
324 EFI_FTW_DEVICE *FtwDevice;
325 EFI_FAULT_TOLERANT_WRITE_HEADER *Header;
326 EFI_FAULT_TOLERANT_WRITE_RECORD *Record;
327 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
328 UINTN MyLength;
329 UINTN MyOffset;
330 UINTN MyBufferSize;
331 UINT8 *MyBuffer;
332 UINTN SpareBufferSize;
333 UINT8 *SpareBuffer;
334 UINTN Index;
335 UINT8 *Ptr;
336 EFI_PHYSICAL_ADDRESS FvbPhysicalAddress;
337 UINTN BlockSize;
338 UINTN NumberOfBlocks;
339 UINTN NumberOfWriteBlocks;
340 UINTN WriteLength;
341
342 FtwDevice = FTW_CONTEXT_FROM_THIS (This);
343
344 Status = WorkSpaceRefresh (FtwDevice);
345 if (EFI_ERROR (Status)) {
346 return EFI_ABORTED;
347 }
348
349 Header = FtwDevice->FtwLastWriteHeader;
350 Record = FtwDevice->FtwLastWriteRecord;
351
352 if (IsErasedFlashBuffer ((UINT8 *)Header, sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER))) {
353 if (PrivateData == NULL) {
354 //
355 // Ftw Write Header is not allocated.
356 // No additional private data, the private data size is zero. Number of record can be set to 1.
357 //
358 Status = FtwAllocate (This, &gEfiCallerIdGuid, 0, 1);
359 if (EFI_ERROR (Status)) {
360 return Status;
361 }
362 } else {
363 //
364 // Ftw Write Header is not allocated
365 // Additional private data is not NULL, the private data size can't be determined.
366 //
367 DEBUG ((DEBUG_ERROR, "Ftw: no allocates space for write record!\n"));
368 DEBUG ((DEBUG_ERROR, "Ftw: Allocate service should be called before Write service!\n"));
369 return EFI_NOT_READY;
370 }
371 }
372
373 //
374 // If Record is out of the range of Header, return access denied.
375 //
376 if (((UINTN)Record - (UINTN)Header) > FTW_WRITE_TOTAL_SIZE (Header->NumberOfWrites - 1, Header->PrivateDataSize)) {
377 return EFI_ACCESS_DENIED;
378 }
379
380 //
381 // Check the COMPLETE flag of last write header
382 //
383 if (Header->Complete == FTW_VALID_STATE) {
384 return EFI_ACCESS_DENIED;
385 }
386
387 if (Record->DestinationComplete == FTW_VALID_STATE) {
388 return EFI_ACCESS_DENIED;
389 }
390
391 if ((Record->SpareComplete == FTW_VALID_STATE) && (Record->DestinationComplete != FTW_VALID_STATE)) {
392 return EFI_NOT_READY;
393 }
394
395 //
396 // Get the FVB protocol by handle
397 //
398 Status = FtwGetFvbByHandle (FvBlockHandle, &Fvb);
399 if (EFI_ERROR (Status)) {
400 return EFI_NOT_FOUND;
401 }
402
403 Status = Fvb->GetPhysicalAddress (Fvb, &FvbPhysicalAddress);
404 if (EFI_ERROR (Status)) {
405 DEBUG ((DEBUG_ERROR, "Ftw: Write(), Get FVB physical address - %r\n", Status));
406 return EFI_ABORTED;
407 }
408
409 //
410 // Now, one FVB has one type of BlockSize.
411 //
412 Status = Fvb->GetBlockSize (Fvb, 0, &BlockSize, &NumberOfBlocks);
413 if (EFI_ERROR (Status)) {
414 DEBUG ((DEBUG_ERROR, "Ftw: Write(), Get block size - %r\n", Status));
415 return EFI_ABORTED;
416 }
417
418 NumberOfWriteBlocks = FTW_BLOCKS (Offset + Length, BlockSize);
419 DEBUG ((DEBUG_INFO, "Ftw: Write(), BlockSize - 0x%x, NumberOfWriteBlock - 0x%x\n", BlockSize, NumberOfWriteBlocks));
420 WriteLength = NumberOfWriteBlocks * BlockSize;
421
422 //
423 // Check if the input data can fit within the spare block.
424 //
425 if (WriteLength > FtwDevice->SpareAreaLength) {
426 return EFI_BAD_BUFFER_SIZE;
427 }
428
429 //
430 // Set BootBlockUpdate FLAG if it's updating boot block.
431 //
432 if (IsBootBlock (FtwDevice, Fvb)) {
433 Record->BootBlockUpdate = FTW_VALID_STATE;
434 //
435 // Boot Block and Spare Block should have same block size and block numbers.
436 //
437 ASSERT ((BlockSize == FtwDevice->SpareBlockSize) && (NumberOfWriteBlocks == FtwDevice->NumberOfSpareBlock));
438 }
439
440 //
441 // Write the record to the work space.
442 //
443 Record->Lba = Lba;
444 Record->Offset = Offset;
445 Record->Length = Length;
446 Record->RelativeOffset = (INT64)(FvbPhysicalAddress + (UINTN)Lba * BlockSize) - (INT64)FtwDevice->SpareAreaAddress;
447 if (PrivateData != NULL) {
448 CopyMem ((Record + 1), PrivateData, (UINTN)Header->PrivateDataSize);
449 }
450
451 MyOffset = (UINT8 *)Record - FtwDevice->FtwWorkSpace;
452 MyLength = FTW_RECORD_SIZE (Header->PrivateDataSize);
453
454 Status = WriteWorkSpaceData (
455 FtwDevice->FtwFvBlock,
456 FtwDevice->WorkBlockSize,
457 FtwDevice->FtwWorkSpaceLba,
458 FtwDevice->FtwWorkSpaceBase + MyOffset,
459 MyLength,
460 (UINT8 *)Record
461 );
462 if (EFI_ERROR (Status)) {
463 return EFI_ABORTED;
464 }
465
466 //
467 // Record has written to working block, then do the data.
468 //
469 //
470 // Allocate a memory buffer
471 //
472 MyBufferSize = WriteLength;
473 MyBuffer = AllocatePool (MyBufferSize);
474 if (MyBuffer == NULL) {
475 return EFI_OUT_OF_RESOURCES;
476 }
477
478 //
479 // Read all original data from target block to memory buffer
480 //
481 Ptr = MyBuffer;
482 for (Index = 0; Index < NumberOfWriteBlocks; Index += 1) {
483 MyLength = BlockSize;
484 Status = Fvb->Read (Fvb, Lba + Index, 0, &MyLength, Ptr);
485 if (EFI_ERROR (Status)) {
486 FreePool (MyBuffer);
487 return EFI_ABORTED;
488 }
489
490 Ptr += MyLength;
491 }
492
493 //
494 // Overwrite the updating range data with
495 // the input buffer content
496 //
497 CopyMem (MyBuffer + Offset, Buffer, Length);
498
499 //
500 // Try to keep the content of spare block
501 // Save spare block into a spare backup memory buffer (Sparebuffer)
502 //
503 SpareBufferSize = FtwDevice->SpareAreaLength;
504 SpareBuffer = AllocatePool (SpareBufferSize);
505 if (SpareBuffer == NULL) {
506 FreePool (MyBuffer);
507 return EFI_OUT_OF_RESOURCES;
508 }
509
510 Ptr = SpareBuffer;
511 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
512 MyLength = FtwDevice->SpareBlockSize;
513 Status = FtwDevice->FtwBackupFvb->Read (
514 FtwDevice->FtwBackupFvb,
515 FtwDevice->FtwSpareLba + Index,
516 0,
517 &MyLength,
518 Ptr
519 );
520 if (EFI_ERROR (Status)) {
521 FreePool (MyBuffer);
522 FreePool (SpareBuffer);
523 return EFI_ABORTED;
524 }
525
526 Ptr += MyLength;
527 }
528
529 //
530 // Write the memory buffer to spare block
531 // Do not assume Spare Block and Target Block have same block size
532 //
533 Status = FtwEraseSpareBlock (FtwDevice);
534 if (EFI_ERROR (Status)) {
535 FreePool (MyBuffer);
536 FreePool (SpareBuffer);
537 return EFI_ABORTED;
538 }
539
540 Ptr = MyBuffer;
541 for (Index = 0; MyBufferSize > 0; Index += 1) {
542 if (MyBufferSize > FtwDevice->SpareBlockSize) {
543 MyLength = FtwDevice->SpareBlockSize;
544 } else {
545 MyLength = MyBufferSize;
546 }
547
548 Status = FtwDevice->FtwBackupFvb->Write (
549 FtwDevice->FtwBackupFvb,
550 FtwDevice->FtwSpareLba + Index,
551 0,
552 &MyLength,
553 Ptr
554 );
555 if (EFI_ERROR (Status)) {
556 FreePool (MyBuffer);
557 FreePool (SpareBuffer);
558 return EFI_ABORTED;
559 }
560
561 Ptr += MyLength;
562 MyBufferSize -= MyLength;
563 }
564
565 //
566 // Free MyBuffer
567 //
568 FreePool (MyBuffer);
569
570 //
571 // Set the SpareComplete in the FTW record,
572 //
573 MyOffset = (UINT8 *)Record - FtwDevice->FtwWorkSpace;
574 Status = FtwUpdateFvState (
575 FtwDevice->FtwFvBlock,
576 FtwDevice->WorkBlockSize,
577 FtwDevice->FtwWorkSpaceLba,
578 FtwDevice->FtwWorkSpaceBase + MyOffset,
579 SPARE_COMPLETED
580 );
581 if (EFI_ERROR (Status)) {
582 FreePool (SpareBuffer);
583 return EFI_ABORTED;
584 }
585
586 Record->SpareComplete = FTW_VALID_STATE;
587
588 //
589 // Since the content has already backuped in spare block, the write is
590 // guaranteed to be completed with fault tolerant manner.
591 //
592 Status = FtwWriteRecord (This, Fvb, BlockSize);
593 if (EFI_ERROR (Status)) {
594 FreePool (SpareBuffer);
595 return EFI_ABORTED;
596 }
597
598 //
599 // Restore spare backup buffer into spare block , if no failure happened during FtwWrite.
600 //
601 Status = FtwEraseSpareBlock (FtwDevice);
602 if (EFI_ERROR (Status)) {
603 FreePool (SpareBuffer);
604 return EFI_ABORTED;
605 }
606
607 Ptr = SpareBuffer;
608 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
609 MyLength = FtwDevice->SpareBlockSize;
610 Status = FtwDevice->FtwBackupFvb->Write (
611 FtwDevice->FtwBackupFvb,
612 FtwDevice->FtwSpareLba + Index,
613 0,
614 &MyLength,
615 Ptr
616 );
617 if (EFI_ERROR (Status)) {
618 FreePool (SpareBuffer);
619 return EFI_ABORTED;
620 }
621
622 Ptr += MyLength;
623 }
624
625 //
626 // All success.
627 //
628 FreePool (SpareBuffer);
629
630 DEBUG (
631 (DEBUG_INFO,
632 "Ftw: Write() success, (Lba:Offset)=(%lx:0x%x), Length: 0x%x\n",
633 Lba,
634 Offset,
635 Length)
636 );
637
638 return EFI_SUCCESS;
639 }
640
641 /**
642 Restarts a previously interrupted write. The caller must provide the
643 block protocol needed to complete the interrupted write.
644
645 @param This The pointer to this protocol instance.
646 @param FvBlockHandle The handle of FVB protocol that provides services for
647 reading, writing, and erasing the target block.
648
649 @retval EFI_SUCCESS The function completed successfully
650 @retval EFI_ACCESS_DENIED No pending writes exist
651 @retval EFI_NOT_FOUND FVB protocol not found by the handle
652 @retval EFI_ABORTED The function could not complete successfully
653
654 **/
655 EFI_STATUS
656 EFIAPI
657 FtwRestart (
658 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,
659 IN EFI_HANDLE FvBlockHandle
660 )
661 {
662 EFI_STATUS Status;
663 EFI_FTW_DEVICE *FtwDevice;
664 EFI_FAULT_TOLERANT_WRITE_HEADER *Header;
665 EFI_FAULT_TOLERANT_WRITE_RECORD *Record;
666 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
667 UINTN BlockSize;
668 UINTN NumberOfBlocks;
669
670 FtwDevice = FTW_CONTEXT_FROM_THIS (This);
671
672 Status = WorkSpaceRefresh (FtwDevice);
673 if (EFI_ERROR (Status)) {
674 return EFI_ABORTED;
675 }
676
677 Header = FtwDevice->FtwLastWriteHeader;
678 Record = FtwDevice->FtwLastWriteRecord;
679
680 //
681 // Spare Complete but Destination not complete,
682 // Recover the targt block with the spare block.
683 //
684 Status = FtwGetFvbByHandle (FvBlockHandle, &Fvb);
685 if (EFI_ERROR (Status)) {
686 return EFI_NOT_FOUND;
687 }
688
689 //
690 // Now, one FVB has one type of BlockSize
691 //
692 Status = Fvb->GetBlockSize (Fvb, 0, &BlockSize, &NumberOfBlocks);
693 if (EFI_ERROR (Status)) {
694 DEBUG ((DEBUG_ERROR, "Ftw: Restart(), Get block size - %r\n", Status));
695 return EFI_ABORTED;
696 }
697
698 //
699 // Check the COMPLETE flag of last write header
700 //
701 if (Header->Complete == FTW_VALID_STATE) {
702 return EFI_ACCESS_DENIED;
703 }
704
705 //
706 // Check the flags of last write record
707 //
708 if (Record->DestinationComplete == FTW_VALID_STATE) {
709 return EFI_ACCESS_DENIED;
710 }
711
712 if ((Record->SpareComplete != FTW_VALID_STATE)) {
713 return EFI_ABORTED;
714 }
715
716 //
717 // Since the content has already backuped in spare block, the write is
718 // guaranteed to be completed with fault tolerant manner.
719 //
720 Status = FtwWriteRecord (This, Fvb, BlockSize);
721 if (EFI_ERROR (Status)) {
722 return EFI_ABORTED;
723 }
724
725 //
726 // Erase Spare block
727 // This is restart, no need to keep spareblock content.
728 //
729 Status = FtwEraseSpareBlock (FtwDevice);
730 if (EFI_ERROR (Status)) {
731 return EFI_ABORTED;
732 }
733
734 DEBUG ((DEBUG_INFO, "%a(): success\n", __FUNCTION__));
735 return EFI_SUCCESS;
736 }
737
738 /**
739 Aborts all previous allocated writes.
740
741 @param This The pointer to this protocol instance.
742
743 @retval EFI_SUCCESS The function completed successfully
744 @retval EFI_ABORTED The function could not complete successfully.
745 @retval EFI_NOT_FOUND No allocated writes exist.
746
747 **/
748 EFI_STATUS
749 EFIAPI
750 FtwAbort (
751 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This
752 )
753 {
754 EFI_STATUS Status;
755 UINTN Offset;
756 EFI_FTW_DEVICE *FtwDevice;
757
758 FtwDevice = FTW_CONTEXT_FROM_THIS (This);
759
760 Status = WorkSpaceRefresh (FtwDevice);
761 if (EFI_ERROR (Status)) {
762 return EFI_ABORTED;
763 }
764
765 if (FtwDevice->FtwLastWriteHeader->HeaderAllocated != FTW_VALID_STATE) {
766 return EFI_NOT_FOUND;
767 }
768
769 if (FtwDevice->FtwLastWriteHeader->Complete == FTW_VALID_STATE) {
770 return EFI_NOT_FOUND;
771 }
772
773 //
774 // Update the complete state of the header as VALID and abort.
775 //
776 Offset = (UINT8 *)FtwDevice->FtwLastWriteHeader - FtwDevice->FtwWorkSpace;
777 Status = FtwUpdateFvState (
778 FtwDevice->FtwFvBlock,
779 FtwDevice->WorkBlockSize,
780 FtwDevice->FtwWorkSpaceLba,
781 FtwDevice->FtwWorkSpaceBase + Offset,
782 WRITES_COMPLETED
783 );
784 if (EFI_ERROR (Status)) {
785 return EFI_ABORTED;
786 }
787
788 FtwDevice->FtwLastWriteHeader->Complete = FTW_VALID_STATE;
789
790 DEBUG ((DEBUG_INFO, "%a(): success\n", __FUNCTION__));
791 return EFI_SUCCESS;
792 }
793
794 /**
795 Starts a target block update. This records information about the write
796 in fault tolerant storage and will complete the write in a recoverable
797 manner, ensuring at all times that either the original contents or
798 the modified contents are available.
799
800 @param This The pointer to this protocol instance.
801 @param CallerId The GUID identifying the last write.
802 @param Lba The logical block address of the last write.
803 @param Offset The offset within the block of the last write.
804 @param Length The length of the last write.
805 @param PrivateDataSize bytes from the private data
806 stored for this write.
807 @param PrivateData A pointer to a buffer. The function will copy
808 @param Complete A Boolean value with TRUE indicating
809 that the write was completed.
810
811 @retval EFI_SUCCESS The function completed successfully
812 @retval EFI_ABORTED The function could not complete successfully
813 @retval EFI_NOT_FOUND No allocated writes exist
814 @retval EFI_BUFFER_TOO_SMALL Input buffer is not larget enough
815
816 **/
817 EFI_STATUS
818 EFIAPI
819 FtwGetLastWrite (
820 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,
821 OUT EFI_GUID *CallerId,
822 OUT EFI_LBA *Lba,
823 OUT UINTN *Offset,
824 OUT UINTN *Length,
825 IN OUT UINTN *PrivateDataSize,
826 OUT VOID *PrivateData,
827 OUT BOOLEAN *Complete
828 )
829 {
830 EFI_STATUS Status;
831 EFI_FTW_DEVICE *FtwDevice;
832 EFI_FAULT_TOLERANT_WRITE_HEADER *Header;
833 EFI_FAULT_TOLERANT_WRITE_RECORD *Record;
834
835 if (!FeaturePcdGet (PcdFullFtwServiceEnable)) {
836 return EFI_UNSUPPORTED;
837 }
838
839 FtwDevice = FTW_CONTEXT_FROM_THIS (This);
840
841 Status = WorkSpaceRefresh (FtwDevice);
842 if (EFI_ERROR (Status)) {
843 return EFI_ABORTED;
844 }
845
846 Header = FtwDevice->FtwLastWriteHeader;
847 Record = FtwDevice->FtwLastWriteRecord;
848
849 //
850 // If Header is incompleted and the last record has completed, then
851 // call Abort() to set the Header->Complete FLAG.
852 //
853 if ((Header->Complete != FTW_VALID_STATE) &&
854 (Record->DestinationComplete == FTW_VALID_STATE) &&
855 IsLastRecordOfWrites (Header, Record)
856 )
857 {
858 Status = FtwAbort (This);
859 *Complete = TRUE;
860 return EFI_NOT_FOUND;
861 }
862
863 //
864 // If there is no write header/record, return not found.
865 //
866 if (Header->HeaderAllocated != FTW_VALID_STATE) {
867 *Complete = TRUE;
868 return EFI_NOT_FOUND;
869 }
870
871 //
872 // If this record SpareComplete has not set, then it can not restart.
873 //
874 if (Record->SpareComplete != FTW_VALID_STATE) {
875 Status = GetPreviousRecordOfWrites (Header, &Record);
876 if (EFI_ERROR (Status)) {
877 FtwAbort (This);
878 *Complete = TRUE;
879 return EFI_NOT_FOUND;
880 }
881
882 ASSERT (Record != NULL);
883 }
884
885 //
886 // Fill all the requested values
887 //
888 CopyMem (CallerId, &Header->CallerId, sizeof (EFI_GUID));
889 *Lba = Record->Lba;
890 *Offset = (UINTN)Record->Offset;
891 *Length = (UINTN)Record->Length;
892 *Complete = (BOOLEAN)(Record->DestinationComplete == FTW_VALID_STATE);
893
894 if (*PrivateDataSize < Header->PrivateDataSize) {
895 *PrivateDataSize = (UINTN)Header->PrivateDataSize;
896 PrivateData = NULL;
897 Status = EFI_BUFFER_TOO_SMALL;
898 } else {
899 *PrivateDataSize = (UINTN)Header->PrivateDataSize;
900 CopyMem (PrivateData, Record + 1, *PrivateDataSize);
901 Status = EFI_SUCCESS;
902 }
903
904 DEBUG ((DEBUG_INFO, "%a(): success\n", __FUNCTION__));
905
906 return Status;
907 }