1 /** @file
2 
3   Internal generic functions to operate flash block.
4 
5 Copyright (c) 2006 - 2014, 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
10 
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.
13 
14 **/
15 
16 #include "FaultTolerantWrite.h"
17 
18 /**
19 
20   Check whether a flash buffer is erased.
21 
22   @param Buffer          Buffer to check
23   @param BufferSize      Size of the buffer
24 
25   @return A BOOLEAN value indicating erased or not.
26 
27 **/
28 BOOLEAN
IsErasedFlashBuffer(IN UINT8 * Buffer,IN UINTN BufferSize)29 IsErasedFlashBuffer (
30   IN UINT8           *Buffer,
31   IN UINTN           BufferSize
32   )
33 {
34   BOOLEAN IsEmpty;
35   UINT8   *Ptr;
36   UINTN   Index;
37 
38   Ptr     = Buffer;
39   IsEmpty = TRUE;
40   for (Index = 0; Index < BufferSize; Index += 1) {
41     if (*Ptr++ != FTW_ERASED_BYTE) {
42       IsEmpty = FALSE;
43       break;
44     }
45   }
46 
47   return IsEmpty;
48 }
49 
50 /**
51   To erase the block with specified blocks.
52 
53 
54   @param FtwDevice       The private data of FTW driver
55   @param FvBlock         FVB Protocol interface
56   @param Lba             Lba of the firmware block
57   @param NumberOfBlocks  The number of consecutive blocks starting with Lba
58 
59   @retval  EFI_SUCCESS    Block LBA is Erased successfully
60   @retval  Others         Error occurs
61 
62 **/
63 EFI_STATUS
FtwEraseBlock(IN EFI_FTW_DEVICE * FtwDevice,EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL * FvBlock,EFI_LBA Lba,UINTN NumberOfBlocks)64 FtwEraseBlock (
65   IN EFI_FTW_DEVICE                   *FtwDevice,
66   EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *FvBlock,
67   EFI_LBA                             Lba,
68   UINTN                               NumberOfBlocks
69   )
70 {
71   return FvBlock->EraseBlocks (
72                     FvBlock,
73                     Lba,
74                     NumberOfBlocks,
75                     EFI_LBA_LIST_TERMINATOR
76                     );
77 }
78 
79 /**
80   Erase spare block.
81 
82   @param FtwDevice        The private data of FTW driver
83 
84   @retval EFI_SUCCESS           The erase request was successfully completed.
85   @retval EFI_ACCESS_DENIED     The firmware volume is in the WriteDisabled state.
86   @retval EFI_DEVICE_ERROR      The block device is not functioning
87                                 correctly and could not be written.
88                                 The firmware device may have been
89                                 partially erased.
90   @retval EFI_INVALID_PARAMETER One or more of the LBAs listed
91                                 in the variable argument list do
92                                 not exist in the firmware volume.
93 
94 
95 **/
96 EFI_STATUS
FtwEraseSpareBlock(IN EFI_FTW_DEVICE * FtwDevice)97 FtwEraseSpareBlock (
98   IN EFI_FTW_DEVICE   *FtwDevice
99   )
100 {
101   return FtwDevice->FtwBackupFvb->EraseBlocks (
102                                     FtwDevice->FtwBackupFvb,
103                                     FtwDevice->FtwSpareLba,
104                                     FtwDevice->NumberOfSpareBlock,
105                                     EFI_LBA_LIST_TERMINATOR
106                                     );
107 }
108 
109 /**
110 
111   Is it in working block?
112 
113   @param FtwDevice       The private data of FTW driver
114   @param FvBlock         Fvb protocol instance
115   @param Lba             The block specified
116 
117   @return A BOOLEAN value indicating in working block or not.
118 
119 **/
120 BOOLEAN
IsWorkingBlock(EFI_FTW_DEVICE * FtwDevice,EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL * FvBlock,EFI_LBA Lba)121 IsWorkingBlock (
122   EFI_FTW_DEVICE                      *FtwDevice,
123   EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *FvBlock,
124   EFI_LBA                             Lba
125   )
126 {
127   //
128   // If matching the following condition, the target block is in working block.
129   // 1. Target block is on the FV of working block (Using the same FVB protocol instance).
130   // 2. Lba falls into the range of working block.
131   //
132   return (BOOLEAN)
133     (
134       (FvBlock == FtwDevice->FtwFvBlock) &&
135       (Lba >= FtwDevice->FtwWorkBlockLba) &&
136       (Lba <= FtwDevice->FtwWorkSpaceLba)
137     );
138 }
139 
140 /**
141 
142   Get firmware volume block by address.
143 
144 
145   @param Address         Address specified the block
146   @param FvBlock         The block caller wanted
147 
148   @retval  EFI_SUCCESS    The protocol instance if found.
149   @retval  EFI_NOT_FOUND  Block not found
150 
151 **/
152 EFI_HANDLE
GetFvbByAddress(IN EFI_PHYSICAL_ADDRESS Address,OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL ** FvBlock)153 GetFvbByAddress (
154   IN  EFI_PHYSICAL_ADDRESS               Address,
155   OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock
156   )
157 {
158   EFI_STATUS                          Status;
159   EFI_HANDLE                          *HandleBuffer;
160   UINTN                               HandleCount;
161   UINTN                               Index;
162   EFI_PHYSICAL_ADDRESS                FvbBaseAddress;
163   EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *Fvb;
164   EFI_HANDLE                          FvbHandle;
165   UINTN                               BlockSize;
166   UINTN                               NumberOfBlocks;
167 
168   *FvBlock  = NULL;
169   FvbHandle = NULL;
170   HandleBuffer = NULL;
171   //
172   // Locate all handles of Fvb protocol
173   //
174   Status = GetFvbCountAndBuffer (&HandleCount, &HandleBuffer);
175   if (EFI_ERROR (Status)) {
176     return NULL;
177   }
178   //
179   // Get the FVB to access variable store
180   //
181   for (Index = 0; Index < HandleCount; Index += 1) {
182     Status = FtwGetFvbByHandle (HandleBuffer[Index], &Fvb);
183     if (EFI_ERROR (Status)) {
184       break;
185     }
186     //
187     // Compare the address and select the right one
188     //
189     Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress);
190     if (EFI_ERROR (Status)) {
191       continue;
192     }
193 
194     //
195     // Now, one FVB has one type of BlockSize
196     //
197     Status = Fvb->GetBlockSize (Fvb, 0, &BlockSize, &NumberOfBlocks);
198     if (EFI_ERROR (Status)) {
199       continue;
200     }
201 
202     if ((Address >= FvbBaseAddress) && (Address < (FvbBaseAddress + BlockSize * NumberOfBlocks))) {
203       *FvBlock  = Fvb;
204       FvbHandle  = HandleBuffer[Index];
205       break;
206     }
207   }
208 
209   FreePool (HandleBuffer);
210   return FvbHandle;
211 }
212 
213 /**
214 
215   Is it in boot block?
216 
217   @param FtwDevice       The private data of FTW driver
218   @param FvBlock         Fvb protocol instance
219 
220   @return A BOOLEAN value indicating in boot block or not.
221 
222 **/
223 BOOLEAN
IsBootBlock(EFI_FTW_DEVICE * FtwDevice,EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL * FvBlock)224 IsBootBlock (
225   EFI_FTW_DEVICE                      *FtwDevice,
226   EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *FvBlock
227   )
228 {
229   EFI_STATUS                          Status;
230   EFI_SWAP_ADDRESS_RANGE_PROTOCOL     *SarProtocol;
231   EFI_PHYSICAL_ADDRESS                BootBlockBase;
232   UINTN                               BootBlockSize;
233   EFI_PHYSICAL_ADDRESS                BackupBlockBase;
234   UINTN                               BackupBlockSize;
235   EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *BootFvb;
236   BOOLEAN                             IsSwapped;
237   EFI_HANDLE                          FvbHandle;
238 
239   if (!FeaturePcdGet(PcdFullFtwServiceEnable)) {
240     return FALSE;
241   }
242 
243   Status = FtwGetSarProtocol ((VOID **) &SarProtocol);
244   if (EFI_ERROR (Status)) {
245     return FALSE;
246   }
247   //
248   // Get the boot block range
249   //
250   Status = SarProtocol->GetRangeLocation (
251                           SarProtocol,
252                           &BootBlockBase,
253                           &BootBlockSize,
254                           &BackupBlockBase,
255                           &BackupBlockSize
256                           );
257   if (EFI_ERROR (Status)) {
258     return FALSE;
259   }
260 
261   Status = SarProtocol->GetSwapState (SarProtocol, &IsSwapped);
262   if (EFI_ERROR (Status)) {
263     return FALSE;
264   }
265   //
266   // Get FVB by address
267   //
268   if (!IsSwapped) {
269     FvbHandle = GetFvbByAddress (BootBlockBase, &BootFvb);
270   } else {
271     FvbHandle = GetFvbByAddress (BackupBlockBase, &BootFvb);
272   }
273 
274   if (FvbHandle == NULL) {
275     return FALSE;
276   }
277   //
278   // Compare the Fvb
279   //
280   return (BOOLEAN) (FvBlock == BootFvb);
281 }
282 
283 /**
284   Copy the content of spare block to a boot block. Size is FTW_BLOCK_SIZE.
285   Spare block is accessed by FTW working FVB protocol interface.
286   Target block is accessed by FvBlock protocol interface.
287 
288   FTW will do extra work on boot block update.
289   FTW should depend on a protocol of EFI_ADDRESS_RANGE_SWAP_PROTOCOL,
290   which is produced by a chipset driver.
291   FTW updating boot block steps may be:
292   1. GetRangeLocation(), if the Range is inside the boot block, FTW know
293   that boot block will be update. It shall add a FLAG in the working block.
294   2. When spare block is ready,
295   3. SetSwapState(SWAPPED)
296   4. erasing boot block,
297   5. programming boot block until the boot block is ok.
298   6. SetSwapState(UNSWAPPED)
299   FTW shall not allow to update boot block when battery state is error.
300 
301   @param FtwDevice       The private data of FTW driver
302 
303   @retval EFI_SUCCESS             Spare block content is copied to boot block
304   @retval EFI_INVALID_PARAMETER   Input parameter error
305   @retval EFI_OUT_OF_RESOURCES    Allocate memory error
306   @retval EFI_ABORTED             The function could not complete successfully
307 
308 **/
309 EFI_STATUS
FlushSpareBlockToBootBlock(EFI_FTW_DEVICE * FtwDevice)310 FlushSpareBlockToBootBlock (
311   EFI_FTW_DEVICE                      *FtwDevice
312   )
313 {
314   EFI_STATUS                          Status;
315   UINTN                               Length;
316   UINT8                               *Buffer;
317   UINTN                               Count;
318   UINT8                               *Ptr;
319   UINTN                               Index;
320   BOOLEAN                             TopSwap;
321   EFI_SWAP_ADDRESS_RANGE_PROTOCOL     *SarProtocol;
322   EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *BootFvb;
323   EFI_LBA                             BootLba;
324 
325   if (!FeaturePcdGet(PcdFullFtwServiceEnable)) {
326     return EFI_UNSUPPORTED;
327   }
328 
329   //
330   // Locate swap address range protocol
331   //
332   Status = FtwGetSarProtocol ((VOID **) &SarProtocol);
333   if (EFI_ERROR (Status)) {
334     return Status;
335   }
336   //
337   // Allocate a memory buffer
338   //
339   Length = FtwDevice->SpareAreaLength;
340   Buffer  = AllocatePool (Length);
341   if (Buffer == NULL) {
342     return EFI_OUT_OF_RESOURCES;
343   }
344   //
345   // Get TopSwap bit state
346   //
347   Status = SarProtocol->GetSwapState (SarProtocol, &TopSwap);
348   if (EFI_ERROR (Status)) {
349     DEBUG ((EFI_D_ERROR, "Ftw: Get Top Swapped status - %r\n", Status));
350     FreePool (Buffer);
351     return EFI_ABORTED;
352   }
353 
354   if (TopSwap) {
355     //
356     // Get FVB of current boot block
357     //
358     if (GetFvbByAddress (FtwDevice->SpareAreaAddress + FtwDevice->SpareAreaLength, &BootFvb) == NULL) {
359       FreePool (Buffer);
360       return EFI_ABORTED;
361     }
362     //
363     // Read data from current boot block
364     //
365     BootLba = 0;
366     Ptr     = Buffer;
367     for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
368       Count = FtwDevice->SpareBlockSize;
369       Status = BootFvb->Read (
370                           BootFvb,
371                           BootLba + Index,
372                           0,
373                           &Count,
374                           Ptr
375                           );
376       if (EFI_ERROR (Status)) {
377         FreePool (Buffer);
378         return Status;
379       }
380 
381       Ptr += Count;
382     }
383   } else {
384     //
385     // Read data from spare block
386     //
387     Ptr = Buffer;
388     for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
389       Count = FtwDevice->SpareBlockSize;
390       Status = FtwDevice->FtwBackupFvb->Read (
391                                           FtwDevice->FtwBackupFvb,
392                                           FtwDevice->FtwSpareLba + Index,
393                                           0,
394                                           &Count,
395                                           Ptr
396                                           );
397       if (EFI_ERROR (Status)) {
398         FreePool (Buffer);
399         return Status;
400       }
401 
402       Ptr += Count;
403     }
404     //
405     // Set TopSwap bit
406     //
407     Status = SarProtocol->SetSwapState (SarProtocol, TRUE);
408     if (EFI_ERROR (Status)) {
409       FreePool (Buffer);
410       return Status;
411     }
412   }
413   //
414   // Erase current spare block
415   // Because TopSwap is set, this actually erase the top block (boot block)!
416   //
417   Status = FtwEraseSpareBlock (FtwDevice);
418   if (EFI_ERROR (Status)) {
419     FreePool (Buffer);
420     return EFI_ABORTED;
421   }
422   //
423   // Write memory buffer to current spare block. Still top block.
424   //
425   Ptr = Buffer;
426   for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
427     Count = FtwDevice->SpareBlockSize;
428     Status = FtwDevice->FtwBackupFvb->Write (
429                                         FtwDevice->FtwBackupFvb,
430                                         FtwDevice->FtwSpareLba + Index,
431                                         0,
432                                         &Count,
433                                         Ptr
434                                         );
435     if (EFI_ERROR (Status)) {
436       DEBUG ((EFI_D_ERROR, "Ftw: FVB Write boot block - %r\n", Status));
437       FreePool (Buffer);
438       return Status;
439     }
440 
441     Ptr += Count;
442   }
443 
444   FreePool (Buffer);
445 
446   //
447   // Clear TopSwap bit
448   //
449   Status = SarProtocol->SetSwapState (SarProtocol, FALSE);
450 
451   return Status;
452 }
453 
454 /**
455   Copy the content of spare block to a target block.
456   Spare block is accessed by FTW backup FVB protocol interface.
457   Target block is accessed by FvBlock protocol interface.
458 
459 
460   @param FtwDevice       The private data of FTW driver
461   @param FvBlock         FVB Protocol interface to access target block
462   @param Lba             Lba of the target block
463   @param BlockSize       The size of the block
464   @param NumberOfBlocks  The number of consecutive blocks starting with Lba
465 
466   @retval  EFI_SUCCESS               Spare block content is copied to target block
467   @retval  EFI_INVALID_PARAMETER     Input parameter error
468   @retval  EFI_OUT_OF_RESOURCES      Allocate memory error
469   @retval  EFI_ABORTED               The function could not complete successfully
470 
471 **/
472 EFI_STATUS
FlushSpareBlockToTargetBlock(EFI_FTW_DEVICE * FtwDevice,EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL * FvBlock,EFI_LBA Lba,UINTN BlockSize,UINTN NumberOfBlocks)473 FlushSpareBlockToTargetBlock (
474   EFI_FTW_DEVICE                      *FtwDevice,
475   EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *FvBlock,
476   EFI_LBA                             Lba,
477   UINTN                               BlockSize,
478   UINTN                               NumberOfBlocks
479   )
480 {
481   EFI_STATUS  Status;
482   UINTN       Length;
483   UINT8       *Buffer;
484   UINTN       Count;
485   UINT8       *Ptr;
486   UINTN       Index;
487 
488   if ((FtwDevice == NULL) || (FvBlock == NULL)) {
489     return EFI_INVALID_PARAMETER;
490   }
491   //
492   // Allocate a memory buffer
493   //
494   Length = FtwDevice->SpareAreaLength;
495   Buffer  = AllocatePool (Length);
496   if (Buffer == NULL) {
497     return EFI_OUT_OF_RESOURCES;
498   }
499   //
500   // Read all content of spare block to memory buffer
501   //
502   Ptr = Buffer;
503   for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
504     Count = FtwDevice->SpareBlockSize;
505     Status = FtwDevice->FtwBackupFvb->Read (
506                                         FtwDevice->FtwBackupFvb,
507                                         FtwDevice->FtwSpareLba + Index,
508                                         0,
509                                         &Count,
510                                         Ptr
511                                         );
512     if (EFI_ERROR (Status)) {
513       FreePool (Buffer);
514       return Status;
515     }
516 
517     Ptr += Count;
518   }
519   //
520   // Erase the target block
521   //
522   Status = FtwEraseBlock (FtwDevice, FvBlock, Lba, NumberOfBlocks);
523   if (EFI_ERROR (Status)) {
524     FreePool (Buffer);
525     return EFI_ABORTED;
526   }
527   //
528   // Write memory buffer to block, using the FvBlock protocol interface
529   //
530   Ptr = Buffer;
531   for (Index = 0; Index < NumberOfBlocks; Index += 1) {
532     Count   = BlockSize;
533     Status  = FvBlock->Write (FvBlock, Lba + Index, 0, &Count, Ptr);
534     if (EFI_ERROR (Status)) {
535       DEBUG ((EFI_D_ERROR, "Ftw: FVB Write block - %r\n", Status));
536       FreePool (Buffer);
537       return Status;
538     }
539 
540     Ptr += Count;
541   }
542 
543   FreePool (Buffer);
544 
545   return Status;
546 }
547 
548 /**
549   Copy the content of spare block to working block. Size is FTW_BLOCK_SIZE.
550   Spare block is accessed by FTW backup FVB protocol interface. LBA is
551   FtwDevice->FtwSpareLba.
552   Working block is accessed by FTW working FVB protocol interface. LBA is
553   FtwDevice->FtwWorkBlockLba.
554 
555   Since the working block header is important when FTW initializes, the
556   state of the operation should be handled carefully. The Crc value is
557   calculated without STATE element.
558 
559   @param FtwDevice       The private data of FTW driver
560 
561   @retval  EFI_SUCCESS               Spare block content is copied to target block
562   @retval  EFI_OUT_OF_RESOURCES      Allocate memory error
563   @retval  EFI_ABORTED               The function could not complete successfully
564 
565 **/
566 EFI_STATUS
FlushSpareBlockToWorkingBlock(EFI_FTW_DEVICE * FtwDevice)567 FlushSpareBlockToWorkingBlock (
568   EFI_FTW_DEVICE                      *FtwDevice
569   )
570 {
571   EFI_STATUS                              Status;
572   UINTN                                   Length;
573   UINT8                                   *Buffer;
574   EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader;
575   UINTN                                   Count;
576   UINT8                                   *Ptr;
577   UINTN                                   Index;
578 
579   //
580   // Allocate a memory buffer
581   //
582   Length = FtwDevice->SpareAreaLength;
583   Buffer  = AllocatePool (Length);
584   if (Buffer == NULL) {
585     return EFI_OUT_OF_RESOURCES;
586   }
587 
588   //
589   // To guarantee that the WorkingBlockValid is set on spare block
590   //
591   //  Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
592   //                            WorkingBlockValid);
593   // To skip Signature and Crc: sizeof(EFI_GUID)+sizeof(UINT32).
594   //
595   FtwUpdateFvState (
596     FtwDevice->FtwBackupFvb,
597     FtwDevice->SpareBlockSize,
598     FtwDevice->FtwSpareLba + FtwDevice->FtwWorkSpaceLbaInSpare,
599     FtwDevice->FtwWorkSpaceBaseInSpare + sizeof (EFI_GUID) + sizeof (UINT32),
600     WORKING_BLOCK_VALID
601     );
602   //
603   // Read from spare block to memory buffer
604   //
605   Ptr = Buffer;
606   for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
607     Count = FtwDevice->SpareBlockSize;
608     Status = FtwDevice->FtwBackupFvb->Read (
609                                         FtwDevice->FtwBackupFvb,
610                                         FtwDevice->FtwSpareLba + Index,
611                                         0,
612                                         &Count,
613                                         Ptr
614                                         );
615     if (EFI_ERROR (Status)) {
616       FreePool (Buffer);
617       return Status;
618     }
619 
620     Ptr += Count;
621   }
622   //
623   // Clear the CRC and STATE, copy data from spare to working block.
624   //
625   WorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (Buffer + (UINTN) FtwDevice->FtwWorkSpaceLbaInSpare * FtwDevice->SpareBlockSize + FtwDevice->FtwWorkSpaceBaseInSpare);
626   InitWorkSpaceHeader (WorkingBlockHeader);
627   WorkingBlockHeader->WorkingBlockValid   = FTW_ERASE_POLARITY;
628   WorkingBlockHeader->WorkingBlockInvalid = FTW_ERASE_POLARITY;
629 
630   //
631   // target block is working block, then
632   //   Set WorkingBlockInvalid in EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
633   //   before erase the working block.
634   //
635   //  Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
636   //                            WorkingBlockInvalid);
637   // So hardcode offset as sizeof(EFI_GUID)+sizeof(UINT32) to
638   // skip Signature and Crc.
639   //
640   Status = FtwUpdateFvState (
641             FtwDevice->FtwFvBlock,
642             FtwDevice->WorkBlockSize,
643             FtwDevice->FtwWorkSpaceLba,
644             FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),
645             WORKING_BLOCK_INVALID
646             );
647   if (EFI_ERROR (Status)) {
648     FreePool (Buffer);
649     return EFI_ABORTED;
650   }
651 
652   FtwDevice->FtwWorkSpaceHeader->WorkingBlockInvalid = FTW_VALID_STATE;
653 
654   //
655   // Erase the working block
656   //
657   Status = FtwEraseBlock (FtwDevice, FtwDevice->FtwFvBlock, FtwDevice->FtwWorkBlockLba, FtwDevice->NumberOfWorkBlock);
658   if (EFI_ERROR (Status)) {
659     FreePool (Buffer);
660     return EFI_ABORTED;
661   }
662   //
663   // Write memory buffer to working block, using the FvBlock protocol interface
664   //
665   Ptr = Buffer;
666   for (Index = 0; Index < FtwDevice->NumberOfWorkBlock; Index += 1) {
667     Count = FtwDevice->WorkBlockSize;
668     Status = FtwDevice->FtwFvBlock->Write (
669                                       FtwDevice->FtwFvBlock,
670                                       FtwDevice->FtwWorkBlockLba + Index,
671                                       0,
672                                       &Count,
673                                       Ptr
674                                       );
675     if (EFI_ERROR (Status)) {
676       DEBUG ((EFI_D_ERROR, "Ftw: FVB Write block - %r\n", Status));
677       FreePool (Buffer);
678       return Status;
679     }
680 
681     Ptr += Count;
682   }
683   //
684   // Since the memory buffer will not be used, free memory Buffer.
685   //
686   FreePool (Buffer);
687 
688   //
689   // Update the VALID of the working block
690   //
691   // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER, WorkingBlockValid);
692   // So hardcode offset as sizeof(EFI_GUID)+sizeof(UINT32) to skip Signature and Crc.
693   //
694   Status = FtwUpdateFvState (
695             FtwDevice->FtwFvBlock,
696             FtwDevice->WorkBlockSize,
697             FtwDevice->FtwWorkSpaceLba,
698             FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),
699             WORKING_BLOCK_VALID
700             );
701   if (EFI_ERROR (Status)) {
702     return EFI_ABORTED;
703   }
704 
705   FtwDevice->FtwWorkSpaceHeader->WorkingBlockInvalid = FTW_INVALID_STATE;
706   FtwDevice->FtwWorkSpaceHeader->WorkingBlockValid = FTW_VALID_STATE;
707 
708   return EFI_SUCCESS;
709 }
710 
711 /**
712   Update a bit of state on a block device. The location of the bit is
713   calculated by the (Lba, Offset, bit). Here bit is determined by the
714   the name of a certain bit.
715 
716 
717   @param FvBlock         FVB Protocol interface to access SrcBlock and DestBlock
718   @param BlockSize       The size of the block
719   @param Lba             Lba of a block
720   @param Offset          Offset on the Lba
721   @param NewBit          New value that will override the old value if it can be change
722 
723   @retval  EFI_SUCCESS    A state bit has been updated successfully
724   @retval  Others         Access block device error.
725                           Notes:
726                           Assume all bits of State are inside the same BYTE.
727   @retval  EFI_ABORTED    Read block fail
728 
729 **/
730 EFI_STATUS
FtwUpdateFvState(IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL * FvBlock,IN UINTN BlockSize,IN EFI_LBA Lba,IN UINTN Offset,IN UINT8 NewBit)731 FtwUpdateFvState (
732   IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *FvBlock,
733   IN UINTN                               BlockSize,
734   IN EFI_LBA                             Lba,
735   IN UINTN                               Offset,
736   IN UINT8                               NewBit
737   )
738 {
739   EFI_STATUS  Status;
740   UINT8       State;
741   UINTN       Length;
742 
743   //
744   // Calculate the real Offset and Lba to write.
745   //
746   while (Offset >= BlockSize) {
747     Offset -= BlockSize;
748     Lba++;
749   }
750 
751   //
752   // Read state from device, assume State is only one byte.
753   //
754   Length  = sizeof (UINT8);
755   Status  = FvBlock->Read (FvBlock, Lba, Offset, &Length, &State);
756   if (EFI_ERROR (Status)) {
757     return EFI_ABORTED;
758   }
759 
760   State ^= FTW_POLARITY_REVERT;
761   State  = (UINT8) (State | NewBit);
762   State ^= FTW_POLARITY_REVERT;
763 
764   //
765   // Write state back to device
766   //
767   Length  = sizeof (UINT8);
768   Status  = FvBlock->Write (FvBlock, Lba, Offset, &Length, &State);
769 
770   return Status;
771 }
772 
773 /**
774   Get the last Write Header pointer.
775   The last write header is the header whose 'complete' state hasn't been set.
776   After all, this header may be a EMPTY header entry for next Allocate.
777 
778 
779   @param FtwWorkSpaceHeader Pointer of the working block header
780   @param FtwWorkSpaceSize   Size of the work space
781   @param FtwWriteHeader     Pointer to retrieve the last write header
782 
783   @retval  EFI_SUCCESS      Get the last write record successfully
784   @retval  EFI_ABORTED      The FTW work space is damaged
785 
786 **/
787 EFI_STATUS
FtwGetLastWriteHeader(IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER * FtwWorkSpaceHeader,IN UINTN FtwWorkSpaceSize,OUT EFI_FAULT_TOLERANT_WRITE_HEADER ** FtwWriteHeader)788 FtwGetLastWriteHeader (
789   IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER  *FtwWorkSpaceHeader,
790   IN UINTN                                    FtwWorkSpaceSize,
791   OUT EFI_FAULT_TOLERANT_WRITE_HEADER         **FtwWriteHeader
792   )
793 {
794   UINTN                           Offset;
795   EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader;
796 
797   *FtwWriteHeader = NULL;
798   FtwHeader       = (EFI_FAULT_TOLERANT_WRITE_HEADER *) (FtwWorkSpaceHeader + 1);
799   Offset          = sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER);
800 
801   while (FtwHeader->Complete == FTW_VALID_STATE) {
802     Offset += FTW_WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites, FtwHeader->PrivateDataSize);
803     //
804     // If Offset exceed the FTW work space boudary, return error.
805     //
806     if (Offset >= FtwWorkSpaceSize) {
807       *FtwWriteHeader = FtwHeader;
808       return EFI_ABORTED;
809     }
810 
811     FtwHeader = (EFI_FAULT_TOLERANT_WRITE_HEADER *) ((UINT8 *) FtwWorkSpaceHeader + Offset);
812   }
813   //
814   // Last write header is found
815   //
816   *FtwWriteHeader = FtwHeader;
817 
818   return EFI_SUCCESS;
819 }
820 
821 /**
822   Get the last Write Record pointer. The last write Record is the Record
823   whose DestinationCompleted state hasn't been set. After all, this Record
824   may be a EMPTY record entry for next write.
825 
826 
827   @param FtwWriteHeader  Pointer to the write record header
828   @param FtwWriteRecord  Pointer to retrieve the last write record
829 
830   @retval EFI_SUCCESS        Get the last write record successfully
831   @retval EFI_ABORTED        The FTW work space is damaged
832 
833 **/
834 EFI_STATUS
FtwGetLastWriteRecord(IN EFI_FAULT_TOLERANT_WRITE_HEADER * FtwWriteHeader,OUT EFI_FAULT_TOLERANT_WRITE_RECORD ** FtwWriteRecord)835 FtwGetLastWriteRecord (
836   IN EFI_FAULT_TOLERANT_WRITE_HEADER          *FtwWriteHeader,
837   OUT EFI_FAULT_TOLERANT_WRITE_RECORD         **FtwWriteRecord
838   )
839 {
840   UINTN                           Index;
841   EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord;
842 
843   *FtwWriteRecord = NULL;
844   FtwRecord       = (EFI_FAULT_TOLERANT_WRITE_RECORD *) (FtwWriteHeader + 1);
845 
846   //
847   // Try to find the last write record "that has not completed"
848   //
849   for (Index = 0; Index < FtwWriteHeader->NumberOfWrites; Index += 1) {
850     if (FtwRecord->DestinationComplete != FTW_VALID_STATE) {
851       //
852       // The last write record is found
853       //
854       *FtwWriteRecord = FtwRecord;
855       return EFI_SUCCESS;
856     }
857 
858     FtwRecord++;
859 
860     if (FtwWriteHeader->PrivateDataSize != 0) {
861       FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) ((UINTN) FtwRecord + (UINTN) FtwWriteHeader->PrivateDataSize);
862     }
863   }
864   //
865   //  if Index == NumberOfWrites, then
866   //  the last record has been written successfully,
867   //  but the Header->Complete Flag has not been set.
868   //  also return the last record.
869   //
870   if (Index == FtwWriteHeader->NumberOfWrites) {
871     *FtwWriteRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) ((UINTN) FtwRecord - FTW_RECORD_SIZE (FtwWriteHeader->PrivateDataSize));
872     return EFI_SUCCESS;
873   }
874 
875   return EFI_ABORTED;
876 }
877 
878 /**
879   To check if FtwRecord is the first record of FtwHeader.
880 
881   @param FtwHeader  Pointer to the write record header
882   @param FtwRecord  Pointer to the write record
883 
884   @retval TRUE      FtwRecord is the first Record of the FtwHeader
885   @retval FALSE     FtwRecord is not the first Record of the FtwHeader
886 
887 **/
888 BOOLEAN
IsFirstRecordOfWrites(IN EFI_FAULT_TOLERANT_WRITE_HEADER * FtwHeader,IN EFI_FAULT_TOLERANT_WRITE_RECORD * FtwRecord)889 IsFirstRecordOfWrites (
890   IN EFI_FAULT_TOLERANT_WRITE_HEADER    *FtwHeader,
891   IN EFI_FAULT_TOLERANT_WRITE_RECORD    *FtwRecord
892   )
893 {
894   UINT8 *Head;
895   UINT8 *Ptr;
896 
897   Head  = (UINT8 *) FtwHeader;
898   Ptr   = (UINT8 *) FtwRecord;
899 
900   Head += sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER);
901   return (BOOLEAN) (Head == Ptr);
902 }
903 
904 /**
905   To check if FtwRecord is the last record of FtwHeader. Because the
906   FtwHeader has NumberOfWrites & PrivateDataSize, the FtwRecord can be
907   determined if it is the last record of FtwHeader.
908 
909   @param FtwHeader  Pointer to the write record header
910   @param FtwRecord  Pointer to the write record
911 
912   @retval TRUE      FtwRecord is the last Record of the FtwHeader
913   @retval FALSE     FtwRecord is not the last Record of the FtwHeader
914 
915 **/
916 BOOLEAN
IsLastRecordOfWrites(IN EFI_FAULT_TOLERANT_WRITE_HEADER * FtwHeader,IN EFI_FAULT_TOLERANT_WRITE_RECORD * FtwRecord)917 IsLastRecordOfWrites (
918   IN EFI_FAULT_TOLERANT_WRITE_HEADER    *FtwHeader,
919   IN EFI_FAULT_TOLERANT_WRITE_RECORD    *FtwRecord
920   )
921 {
922   UINT8 *Head;
923   UINT8 *Ptr;
924 
925   Head  = (UINT8 *) FtwHeader;
926   Ptr   = (UINT8 *) FtwRecord;
927 
928   Head += FTW_WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites - 1, FtwHeader->PrivateDataSize);
929   return (BOOLEAN) (Head == Ptr);
930 }
931 
932 /**
933   To check if FtwRecord is the first record of FtwHeader.
934 
935   @param FtwHeader  Pointer to the write record header
936   @param FtwRecord  Pointer to retrieve the previous write record
937 
938   @retval EFI_ACCESS_DENIED  Input record is the first record, no previous record is return.
939   @retval EFI_SUCCESS        The previous write record is found.
940 
941 **/
942 EFI_STATUS
GetPreviousRecordOfWrites(IN EFI_FAULT_TOLERANT_WRITE_HEADER * FtwHeader,IN OUT EFI_FAULT_TOLERANT_WRITE_RECORD ** FtwRecord)943 GetPreviousRecordOfWrites (
944   IN     EFI_FAULT_TOLERANT_WRITE_HEADER    *FtwHeader,
945   IN OUT EFI_FAULT_TOLERANT_WRITE_RECORD    **FtwRecord
946   )
947 {
948   UINT8 *Ptr;
949 
950   if (IsFirstRecordOfWrites (FtwHeader, *FtwRecord)) {
951     *FtwRecord = NULL;
952     return EFI_ACCESS_DENIED;
953   }
954 
955   Ptr = (UINT8 *) (*FtwRecord);
956   Ptr -= FTW_RECORD_SIZE (FtwHeader->PrivateDataSize);
957   *FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) Ptr;
958   return EFI_SUCCESS;
959 }
960 
961 /**
962   Allocate private data for FTW driver and initialize it.
963 
964   @param[out] FtwData           Pointer to the FTW device structure
965 
966   @retval EFI_SUCCESS           Initialize the FTW device successfully.
967   @retval EFI_OUT_OF_RESOURCES  Allocate memory error
968   @retval EFI_INVALID_PARAMETER Workspace or Spare block does not exist
969 
970 **/
971 EFI_STATUS
InitFtwDevice(OUT EFI_FTW_DEVICE ** FtwData)972 InitFtwDevice (
973   OUT EFI_FTW_DEVICE               **FtwData
974   )
975 {
976   EFI_FTW_DEVICE                   *FtwDevice;
977 
978   //
979   // Allocate private data of this driver,
980   // Including the FtwWorkSpace[FTW_WORK_SPACE_SIZE].
981   //
982   FtwDevice = AllocateZeroPool (sizeof (EFI_FTW_DEVICE) + PcdGet32 (PcdFlashNvStorageFtwWorkingSize));
983   if (FtwDevice == NULL) {
984     return EFI_OUT_OF_RESOURCES;
985   }
986 
987   //
988   // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.
989   //
990   FtwDevice->WorkSpaceLength  = (UINTN) PcdGet32 (PcdFlashNvStorageFtwWorkingSize);
991   FtwDevice->SpareAreaLength  = (UINTN) PcdGet32 (PcdFlashNvStorageFtwSpareSize);
992   if ((FtwDevice->WorkSpaceLength == 0) || (FtwDevice->SpareAreaLength == 0)) {
993     DEBUG ((EFI_D_ERROR, "Ftw: Workspace or Spare block does not exist!\n"));
994     FreePool (FtwDevice);
995     return EFI_INVALID_PARAMETER;
996   }
997 
998   FtwDevice->Signature        = FTW_DEVICE_SIGNATURE;
999   FtwDevice->FtwFvBlock       = NULL;
1000   FtwDevice->FtwBackupFvb     = NULL;
1001   FtwDevice->FtwWorkSpaceLba  = (EFI_LBA) (-1);
1002   FtwDevice->FtwSpareLba      = (EFI_LBA) (-1);
1003 
1004   FtwDevice->WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageFtwWorkingBase64);
1005   if (FtwDevice->WorkSpaceAddress == 0) {
1006     FtwDevice->WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwWorkingBase);
1007   }
1008 
1009   FtwDevice->SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageFtwSpareBase64);
1010   if (FtwDevice->SpareAreaAddress == 0) {
1011     FtwDevice->SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwSpareBase);
1012   }
1013 
1014   *FtwData = FtwDevice;
1015   return EFI_SUCCESS;
1016 }
1017 
1018 
1019 /**
1020   Find the proper Firmware Volume Block protocol for FTW operation.
1021 
1022   @param[in, out] FtwDevice     Pointer to the FTW device structure
1023 
1024   @retval EFI_SUCCESS           Find the FVB protocol successfully.
1025   @retval EFI_NOT_FOUND         No proper FVB protocol was found.
1026   @retval EFI_ABORTED           Some data can not be got or be invalid.
1027 
1028 **/
1029 EFI_STATUS
FindFvbForFtw(IN OUT EFI_FTW_DEVICE * FtwDevice)1030 FindFvbForFtw (
1031   IN OUT EFI_FTW_DEVICE               *FtwDevice
1032   )
1033 {
1034   EFI_STATUS                          Status;
1035   EFI_HANDLE                          *HandleBuffer;
1036   UINTN                               HandleCount;
1037   UINTN                               Index;
1038   EFI_PHYSICAL_ADDRESS                FvbBaseAddress;
1039   EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *Fvb;
1040   EFI_FVB_ATTRIBUTES_2                Attributes;
1041   UINT32                              LbaIndex;
1042   UINTN                               BlockSize;
1043   UINTN                               NumberOfBlocks;
1044 
1045   HandleBuffer = NULL;
1046 
1047   //
1048   // Get all FVB handle.
1049   //
1050   Status = GetFvbCountAndBuffer (&HandleCount, &HandleBuffer);
1051   if (EFI_ERROR (Status)) {
1052     return EFI_NOT_FOUND;
1053   }
1054 
1055   //
1056   // Get the FVB to access variable store
1057   //
1058   Fvb = NULL;
1059   for (Index = 0; Index < HandleCount; Index += 1) {
1060     Status = FtwGetFvbByHandle (HandleBuffer[Index], &Fvb);
1061     if (EFI_ERROR (Status)) {
1062       Status = EFI_NOT_FOUND;
1063       break;
1064     }
1065 
1066     //
1067     // Ensure this FVB protocol support Write operation.
1068     //
1069     Status = Fvb->GetAttributes (Fvb, &Attributes);
1070     if (EFI_ERROR (Status) || ((Attributes & EFI_FVB2_WRITE_STATUS) == 0)) {
1071       continue;
1072     }
1073     //
1074     // Compare the address and select the right one
1075     //
1076     Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress);
1077     if (EFI_ERROR (Status)) {
1078       continue;
1079     }
1080 
1081     //
1082     // Now, one FVB has one type of BlockSize.
1083     //
1084     Status = Fvb->GetBlockSize (Fvb, 0, &BlockSize, &NumberOfBlocks);
1085     if (EFI_ERROR (Status)) {
1086       continue;
1087     }
1088 
1089     if ((FtwDevice->FtwFvBlock == NULL) && (FtwDevice->WorkSpaceAddress >= FvbBaseAddress) &&
1090         ((FtwDevice->WorkSpaceAddress + FtwDevice->WorkSpaceLength) <= (FvbBaseAddress + BlockSize * NumberOfBlocks))) {
1091       FtwDevice->FtwFvBlock = Fvb;
1092       //
1093       // To get the LBA of work space
1094       //
1095       for (LbaIndex = 1; LbaIndex <= NumberOfBlocks; LbaIndex += 1) {
1096         if ((FtwDevice->WorkSpaceAddress >= (FvbBaseAddress + BlockSize * (LbaIndex - 1)))
1097             && (FtwDevice->WorkSpaceAddress < (FvbBaseAddress + BlockSize * LbaIndex))) {
1098           FtwDevice->FtwWorkSpaceLba = LbaIndex - 1;
1099           //
1100           // Get the Work space size and Base(Offset)
1101           //
1102           FtwDevice->FtwWorkSpaceSize = FtwDevice->WorkSpaceLength;
1103           FtwDevice->WorkBlockSize    = BlockSize;
1104           FtwDevice->FtwWorkSpaceBase = (UINTN) (FtwDevice->WorkSpaceAddress - (FvbBaseAddress + FtwDevice->WorkBlockSize * (LbaIndex - 1)));
1105           FtwDevice->NumberOfWorkSpaceBlock = FTW_BLOCKS (FtwDevice->FtwWorkSpaceBase + FtwDevice->FtwWorkSpaceSize, FtwDevice->WorkBlockSize);
1106           if (FtwDevice->FtwWorkSpaceSize >= FtwDevice->WorkBlockSize) {
1107             //
1108             // Check the alignment of work space address and length, they should be block size aligned when work space size is larger than one block size.
1109             //
1110             if (((FtwDevice->WorkSpaceAddress & (FtwDevice->WorkBlockSize - 1)) != 0) ||
1111                 ((FtwDevice->WorkSpaceLength & (FtwDevice->WorkBlockSize - 1)) != 0)) {
1112               DEBUG ((EFI_D_ERROR, "Ftw: Work space address or length is not block size aligned when work space size is larger than one block size\n"));
1113               FreePool (HandleBuffer);
1114               ASSERT (FALSE);
1115               return EFI_ABORTED;
1116             }
1117           } else if ((FtwDevice->FtwWorkSpaceBase + FtwDevice->FtwWorkSpaceSize) > FtwDevice->WorkBlockSize) {
1118             DEBUG ((EFI_D_ERROR, "Ftw: The work space range should not span blocks when work space size is less than one block size\n"));
1119             FreePool (HandleBuffer);
1120             ASSERT (FALSE);
1121             return EFI_ABORTED;
1122           }
1123           break;
1124         }
1125       }
1126     }
1127 
1128     if ((FtwDevice->FtwBackupFvb == NULL) && (FtwDevice->SpareAreaAddress >= FvbBaseAddress) &&
1129         ((FtwDevice->SpareAreaAddress + FtwDevice->SpareAreaLength) <= (FvbBaseAddress + BlockSize * NumberOfBlocks))) {
1130       FtwDevice->FtwBackupFvb = Fvb;
1131       //
1132       // To get the LBA of spare
1133       //
1134       for (LbaIndex = 1; LbaIndex <= NumberOfBlocks; LbaIndex += 1) {
1135         if ((FtwDevice->SpareAreaAddress >= (FvbBaseAddress + BlockSize * (LbaIndex - 1)))
1136             && (FtwDevice->SpareAreaAddress < (FvbBaseAddress + BlockSize * LbaIndex))) {
1137           //
1138           // Get the NumberOfSpareBlock and BlockSize
1139           //
1140           FtwDevice->FtwSpareLba        = LbaIndex - 1;
1141           FtwDevice->SpareBlockSize     = BlockSize;
1142           FtwDevice->NumberOfSpareBlock = FtwDevice->SpareAreaLength / FtwDevice->SpareBlockSize;
1143           //
1144           // Check the range of spare area to make sure that it's in FV range
1145           //
1146           if ((FtwDevice->FtwSpareLba + FtwDevice->NumberOfSpareBlock) > NumberOfBlocks) {
1147             DEBUG ((EFI_D_ERROR, "Ftw: Spare area is out of FV range\n"));
1148             FreePool (HandleBuffer);
1149             ASSERT (FALSE);
1150             return EFI_ABORTED;
1151           }
1152           //
1153           // Check the alignment of spare area address and length, they should be block size aligned
1154           //
1155           if (((FtwDevice->SpareAreaAddress & (FtwDevice->SpareBlockSize - 1)) != 0) ||
1156               ((FtwDevice->SpareAreaLength & (FtwDevice->SpareBlockSize - 1)) != 0)) {
1157             DEBUG ((EFI_D_ERROR, "Ftw: Spare area address or length is not block size aligned\n"));
1158             FreePool (HandleBuffer);
1159             //
1160             // Report Status Code EFI_SW_EC_ABORTED.
1161             //
1162             REPORT_STATUS_CODE ((EFI_ERROR_CODE | EFI_ERROR_UNRECOVERED), (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_EC_ABORTED));
1163             ASSERT (FALSE);
1164             CpuDeadLoop ();
1165           }
1166           break;
1167         }
1168       }
1169     }
1170   }
1171   FreePool (HandleBuffer);
1172 
1173   if ((FtwDevice->FtwBackupFvb == NULL) || (FtwDevice->FtwFvBlock == NULL) ||
1174     (FtwDevice->FtwWorkSpaceLba == (EFI_LBA) (-1)) || (FtwDevice->FtwSpareLba == (EFI_LBA) (-1))) {
1175     return EFI_ABORTED;
1176   }
1177   DEBUG ((EFI_D_INFO, "Ftw: FtwWorkSpaceLba - 0x%lx, WorkBlockSize  - 0x%x, FtwWorkSpaceBase - 0x%x\n", FtwDevice->FtwWorkSpaceLba, FtwDevice->WorkBlockSize, FtwDevice->FtwWorkSpaceBase));
1178   DEBUG ((EFI_D_INFO, "Ftw: FtwSpareLba     - 0x%lx, SpareBlockSize - 0x%x\n", FtwDevice->FtwSpareLba, FtwDevice->SpareBlockSize));
1179 
1180   return EFI_SUCCESS;
1181 }
1182 
1183 
1184 /**
1185   Initialization for Fault Tolerant Write protocol.
1186 
1187   @param[in, out] FtwDevice     Pointer to the FTW device structure
1188 
1189   @retval EFI_SUCCESS           Initialize the FTW protocol successfully.
1190   @retval EFI_NOT_FOUND         No proper FVB protocol was found.
1191 
1192 **/
1193 EFI_STATUS
InitFtwProtocol(IN OUT EFI_FTW_DEVICE * FtwDevice)1194 InitFtwProtocol (
1195   IN OUT EFI_FTW_DEVICE               *FtwDevice
1196   )
1197 {
1198   EFI_STATUS                          Status;
1199   EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *Fvb;
1200   EFI_FAULT_TOLERANT_WRITE_HEADER     *FtwHeader;
1201   UINTN                               Offset;
1202   EFI_HANDLE                          FvbHandle;
1203   EFI_LBA                             WorkSpaceLbaOffset;
1204 
1205   //
1206   // Find the right SMM Fvb protocol instance for FTW.
1207   //
1208   Status = FindFvbForFtw (FtwDevice);
1209   if (EFI_ERROR (Status)) {
1210     return EFI_NOT_FOUND;
1211   }
1212 
1213   //
1214   // Calculate the start LBA of working block.
1215   //
1216   if (FtwDevice->FtwWorkSpaceSize >= FtwDevice->WorkBlockSize) {
1217     //
1218     // Working block is a standalone area which only contains working space.
1219     //
1220     FtwDevice->NumberOfWorkBlock = FtwDevice->NumberOfWorkSpaceBlock;
1221   } else {
1222     //
1223     // Working block is an area which
1224     // contains working space in its last block and has the same size as spare
1225     // block, unless there are not enough blocks before the block that contains
1226     // working space.
1227     //
1228     FtwDevice->NumberOfWorkBlock = (UINTN) (FtwDevice->FtwWorkSpaceLba + FtwDevice->NumberOfWorkSpaceBlock);
1229     while (FtwDevice->NumberOfWorkBlock * FtwDevice->WorkBlockSize > FtwDevice->SpareAreaLength) {
1230       FtwDevice->NumberOfWorkBlock--;
1231     }
1232   }
1233   FtwDevice->FtwWorkBlockLba = FtwDevice->FtwWorkSpaceLba + FtwDevice->NumberOfWorkSpaceBlock - FtwDevice->NumberOfWorkBlock;
1234   DEBUG ((EFI_D_INFO, "Ftw: NumberOfWorkBlock - 0x%x, FtwWorkBlockLba - 0x%lx\n", FtwDevice->NumberOfWorkBlock, FtwDevice->FtwWorkBlockLba));
1235 
1236   //
1237   // Calcualte the LBA and base of work space in spare block.
1238   // Note: Do not assume Spare Block and Work Block have same block size.
1239   //
1240   WorkSpaceLbaOffset = FtwDevice->FtwWorkSpaceLba - FtwDevice->FtwWorkBlockLba;
1241   FtwDevice->FtwWorkSpaceLbaInSpare = (EFI_LBA) (((UINTN) WorkSpaceLbaOffset * FtwDevice->WorkBlockSize + FtwDevice->FtwWorkSpaceBase) / FtwDevice->SpareBlockSize);
1242   FtwDevice->FtwWorkSpaceBaseInSpare = ((UINTN) WorkSpaceLbaOffset * FtwDevice->WorkBlockSize + FtwDevice->FtwWorkSpaceBase) % FtwDevice->SpareBlockSize;
1243   DEBUG ((EFI_D_INFO, "Ftw: WorkSpaceLbaInSpare - 0x%lx, WorkSpaceBaseInSpare - 0x%x\n", FtwDevice->FtwWorkSpaceLbaInSpare, FtwDevice->FtwWorkSpaceBaseInSpare));
1244 
1245   //
1246   // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.
1247   //
1248   FtwDevice->FtwWorkSpace = (UINT8 *) (FtwDevice + 1);
1249   FtwDevice->FtwWorkSpaceHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) FtwDevice->FtwWorkSpace;
1250 
1251   FtwDevice->FtwLastWriteHeader = NULL;
1252   FtwDevice->FtwLastWriteRecord = NULL;
1253 
1254   InitializeLocalWorkSpaceHeader ();
1255 
1256   //
1257   // Refresh the working space data from working block
1258   //
1259   Status = WorkSpaceRefresh (FtwDevice);
1260   ASSERT_EFI_ERROR (Status);
1261   //
1262   // If the working block workspace is not valid, try the spare block
1263   //
1264   if (!IsValidWorkSpace (FtwDevice->FtwWorkSpaceHeader)) {
1265     //
1266     // Read from spare block
1267     //
1268     Status = ReadWorkSpaceData (
1269                FtwDevice->FtwBackupFvb,
1270                FtwDevice->SpareBlockSize,
1271                FtwDevice->FtwSpareLba + FtwDevice->FtwWorkSpaceLbaInSpare,
1272                FtwDevice->FtwWorkSpaceBaseInSpare,
1273                FtwDevice->FtwWorkSpaceSize,
1274                FtwDevice->FtwWorkSpace
1275                );
1276     ASSERT_EFI_ERROR (Status);
1277 
1278     //
1279     // If spare block is valid, then replace working block content.
1280     //
1281     if (IsValidWorkSpace (FtwDevice->FtwWorkSpaceHeader)) {
1282       Status = FlushSpareBlockToWorkingBlock (FtwDevice);
1283       DEBUG ((EFI_D_INFO, "Ftw: Restart working block update in %a() - %r\n",
1284         __FUNCTION__, Status));
1285       FtwAbort (&FtwDevice->FtwInstance);
1286       //
1287       // Refresh work space.
1288       //
1289       Status = WorkSpaceRefresh (FtwDevice);
1290       ASSERT_EFI_ERROR (Status);
1291     } else {
1292       DEBUG ((EFI_D_INFO,
1293         "Ftw: Both working and spare blocks are invalid, init workspace\n"));
1294       //
1295       // If both are invalid, then initialize work space.
1296       //
1297       SetMem (
1298         FtwDevice->FtwWorkSpace,
1299         FtwDevice->FtwWorkSpaceSize,
1300         FTW_ERASED_BYTE
1301         );
1302       InitWorkSpaceHeader (FtwDevice->FtwWorkSpaceHeader);
1303       //
1304       // Initialize the work space
1305       //
1306       Status = FtwReclaimWorkSpace (FtwDevice, FALSE);
1307       ASSERT_EFI_ERROR (Status);
1308     }
1309   }
1310   //
1311   // If the FtwDevice->FtwLastWriteRecord is 1st record of write header &&
1312   // (! SpareComplete) THEN call Abort().
1313   //
1314   if ((FtwDevice->FtwLastWriteHeader->HeaderAllocated == FTW_VALID_STATE) &&
1315     (FtwDevice->FtwLastWriteRecord->SpareComplete != FTW_VALID_STATE) &&
1316     IsFirstRecordOfWrites (FtwDevice->FtwLastWriteHeader, FtwDevice->FtwLastWriteRecord)
1317     ) {
1318     DEBUG ((EFI_D_ERROR, "Ftw: Init.. find first record not SpareCompleted, abort()\n"));
1319     FtwAbort (&FtwDevice->FtwInstance);
1320   }
1321   //
1322   // If Header is incompleted and the last record has completed, then
1323   // call Abort() to set the Header->Complete FLAG.
1324   //
1325   if ((FtwDevice->FtwLastWriteHeader->Complete != FTW_VALID_STATE) &&
1326     (FtwDevice->FtwLastWriteRecord->DestinationComplete == FTW_VALID_STATE) &&
1327     IsLastRecordOfWrites (FtwDevice->FtwLastWriteHeader, FtwDevice->FtwLastWriteRecord)
1328     ) {
1329     DEBUG ((EFI_D_ERROR, "Ftw: Init.. find last record completed but header not, abort()\n"));
1330     FtwAbort (&FtwDevice->FtwInstance);
1331   }
1332   //
1333   // To check the workspace buffer following last Write header/records is EMPTY or not.
1334   // If it's not EMPTY, FTW also need to call reclaim().
1335   //
1336   FtwHeader = FtwDevice->FtwLastWriteHeader;
1337   Offset    = (UINT8 *) FtwHeader - FtwDevice->FtwWorkSpace;
1338   if (FtwDevice->FtwWorkSpace[Offset] != FTW_ERASED_BYTE) {
1339     Offset += FTW_WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites, FtwHeader->PrivateDataSize);
1340   }
1341 
1342   if (!IsErasedFlashBuffer (FtwDevice->FtwWorkSpace + Offset, FtwDevice->FtwWorkSpaceSize - Offset)) {
1343     Status = FtwReclaimWorkSpace (FtwDevice, TRUE);
1344     ASSERT_EFI_ERROR (Status);
1345   }
1346 
1347   //
1348   // Restart if it's boot block
1349   //
1350   if ((FtwDevice->FtwLastWriteHeader->Complete != FTW_VALID_STATE) &&
1351     (FtwDevice->FtwLastWriteRecord->SpareComplete == FTW_VALID_STATE)
1352     ) {
1353     if (FtwDevice->FtwLastWriteRecord->BootBlockUpdate == FTW_VALID_STATE) {
1354       Status = FlushSpareBlockToBootBlock (FtwDevice);
1355       DEBUG ((EFI_D_ERROR, "Ftw: Restart boot block update - %r\n", Status));
1356       ASSERT_EFI_ERROR (Status);
1357       FtwAbort (&FtwDevice->FtwInstance);
1358     } else {
1359       //
1360       // if (SpareCompleted) THEN  Restart to fault tolerant write.
1361       //
1362       FvbHandle = NULL;
1363       FvbHandle = GetFvbByAddress ((EFI_PHYSICAL_ADDRESS) (UINTN) ((INT64) FtwDevice->SpareAreaAddress + FtwDevice->FtwLastWriteRecord->RelativeOffset), &Fvb);
1364       if (FvbHandle != NULL) {
1365         Status = FtwRestart (&FtwDevice->FtwInstance, FvbHandle);
1366         DEBUG ((EFI_D_ERROR, "Ftw: Restart last write - %r\n", Status));
1367         ASSERT_EFI_ERROR (Status);
1368       }
1369       FtwAbort (&FtwDevice->FtwInstance);
1370     }
1371   }
1372   //
1373   // Hook the protocol API
1374   //
1375   FtwDevice->FtwInstance.GetMaxBlockSize = FtwGetMaxBlockSize;
1376   FtwDevice->FtwInstance.Allocate        = FtwAllocate;
1377   FtwDevice->FtwInstance.Write           = FtwWrite;
1378   FtwDevice->FtwInstance.Restart         = FtwRestart;
1379   FtwDevice->FtwInstance.Abort           = FtwAbort;
1380   FtwDevice->FtwInstance.GetLastWrite    = FtwGetLastWrite;
1381 
1382   return EFI_SUCCESS;
1383 }
1384 
1385