1 /** @file
2 
3    Internal functions to operate Working Block Space.
4 
5 Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution.  The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
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 
17 #include "FaultTolerantWrite.h"
18 
19 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER mWorkingBlockHeader = {ZERO_GUID, 0, 0, 0, 0, {0, 0, 0}, 0};
20 
21 /**
22   Initialize a local work space header.
23 
24   Since Signature and WriteQueueSize have been known, Crc can be calculated out,
25   then the work space header will be fixed.
26 **/
27 VOID
InitializeLocalWorkSpaceHeader(VOID)28 InitializeLocalWorkSpaceHeader (
29   VOID
30   )
31 {
32   EFI_STATUS                              Status;
33 
34   //
35   // Check signature with gEdkiiWorkingBlockSignatureGuid.
36   //
37   if (CompareGuid (&gEdkiiWorkingBlockSignatureGuid, &mWorkingBlockHeader.Signature)) {
38     //
39     // The local work space header has been initialized.
40     //
41     return;
42   }
43 
44   SetMem (
45     &mWorkingBlockHeader,
46     sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
47     FTW_ERASED_BYTE
48     );
49 
50   //
51   // Here using gEdkiiWorkingBlockSignatureGuid as the signature.
52   //
53   CopyMem (
54     &mWorkingBlockHeader.Signature,
55     &gEdkiiWorkingBlockSignatureGuid,
56     sizeof (EFI_GUID)
57     );
58   mWorkingBlockHeader.WriteQueueSize = (UINT64) (PcdGet32 (PcdFlashNvStorageFtwWorkingSize) - sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER));
59 
60   //
61   // Crc is calculated with all the fields except Crc and STATE, so leave them as FTW_ERASED_BYTE.
62   //
63 
64   //
65   // Calculate the Crc of woking block header
66   //
67   Status = gBS->CalculateCrc32 (
68                   &mWorkingBlockHeader,
69                   sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
70                   &mWorkingBlockHeader.Crc
71                   );
72   ASSERT_EFI_ERROR (Status);
73 
74   mWorkingBlockHeader.WorkingBlockValid    = FTW_VALID_STATE;
75   mWorkingBlockHeader.WorkingBlockInvalid  = FTW_INVALID_STATE;
76 }
77 
78 /**
79   Check to see if it is a valid work space.
80 
81 
82   @param WorkingHeader   Pointer of working block header
83 
84   @retval TRUE          The work space is valid.
85   @retval FALSE         The work space is invalid.
86 
87 **/
88 BOOLEAN
IsValidWorkSpace(IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER * WorkingHeader)89 IsValidWorkSpace (
90   IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader
91   )
92 {
93   if (WorkingHeader == NULL) {
94     return FALSE;
95   }
96 
97   if (CompareMem (WorkingHeader, &mWorkingBlockHeader, sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)) == 0) {
98     return TRUE;
99   }
100 
101   DEBUG ((EFI_D_INFO, "Ftw: Work block header check mismatch\n"));
102   return FALSE;
103 }
104 
105 /**
106   Initialize a work space when there is no work space.
107 
108   @param WorkingHeader   Pointer of working block header
109 
110   @retval  EFI_SUCCESS    The function completed successfully
111   @retval  EFI_ABORTED    The function could not complete successfully.
112 
113 **/
114 EFI_STATUS
InitWorkSpaceHeader(IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER * WorkingHeader)115 InitWorkSpaceHeader (
116   IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader
117   )
118 {
119   if (WorkingHeader == NULL) {
120     return EFI_INVALID_PARAMETER;
121   }
122 
123   CopyMem (WorkingHeader, &mWorkingBlockHeader, sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER));
124 
125   return EFI_SUCCESS;
126 }
127 
128 /**
129   Read work space data from work block or spare block.
130 
131   @param FvBlock        FVB Protocol interface to access the block.
132   @param BlockSize      The size of the block.
133   @param Lba            Lba of the block.
134   @param Offset         The offset within the block.
135   @param Length         The number of bytes to read from the block.
136   @param Buffer         The data is read.
137 
138   @retval EFI_SUCCESS   The function completed successfully.
139   @retval EFI_ABORTED   The function could not complete successfully.
140 
141 **/
142 EFI_STATUS
ReadWorkSpaceData(IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL * FvBlock,IN UINTN BlockSize,IN EFI_LBA Lba,IN UINTN Offset,IN UINTN Length,OUT UINT8 * Buffer)143 ReadWorkSpaceData (
144   IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
145   IN UINTN                              BlockSize,
146   IN EFI_LBA                            Lba,
147   IN UINTN                              Offset,
148   IN UINTN                              Length,
149   OUT UINT8                             *Buffer
150   )
151 {
152   EFI_STATUS            Status;
153   UINT8                 *Ptr;
154   UINTN                 MyLength;
155 
156   //
157   // Calculate the real Offset and Lba to write.
158   //
159   while (Offset >= BlockSize) {
160     Offset -= BlockSize;
161     Lba++;
162   }
163 
164   Ptr = Buffer;
165   while (Length > 0) {
166     if ((Offset + Length) > BlockSize) {
167       MyLength = BlockSize - Offset;
168     } else {
169       MyLength = Length;
170     }
171 
172     Status = FvBlock->Read (
173                         FvBlock,
174                         Lba,
175                         Offset,
176                         &MyLength,
177                         Ptr
178                         );
179     if (EFI_ERROR (Status)) {
180       return EFI_ABORTED;
181     }
182     Offset = 0;
183     Length -= MyLength;
184     Ptr += MyLength;
185     Lba++;
186   }
187 
188   return EFI_SUCCESS;
189 }
190 
191 /**
192   Write work space data to work block.
193 
194   @param FvBlock        FVB Protocol interface to access the block.
195   @param BlockSize      The size of the block.
196   @param Lba            Lba of the block.
197   @param Offset         The offset within the block to place the data.
198   @param Length         The number of bytes to write to the block.
199   @param Buffer         The data to write.
200 
201   @retval EFI_SUCCESS   The function completed successfully.
202   @retval EFI_ABORTED   The function could not complete successfully.
203 
204 **/
205 EFI_STATUS
WriteWorkSpaceData(IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL * FvBlock,IN UINTN BlockSize,IN EFI_LBA Lba,IN UINTN Offset,IN UINTN Length,IN UINT8 * Buffer)206 WriteWorkSpaceData (
207   IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
208   IN UINTN                              BlockSize,
209   IN EFI_LBA                            Lba,
210   IN UINTN                              Offset,
211   IN UINTN                              Length,
212   IN UINT8                              *Buffer
213   )
214 {
215   EFI_STATUS            Status;
216   UINT8                 *Ptr;
217   UINTN                 MyLength;
218 
219   //
220   // Calculate the real Offset and Lba to write.
221   //
222   while (Offset >= BlockSize) {
223     Offset -= BlockSize;
224     Lba++;
225   }
226 
227   Ptr = Buffer;
228   while (Length > 0) {
229     if ((Offset + Length) > BlockSize) {
230       MyLength = BlockSize - Offset;
231     } else {
232       MyLength = Length;
233     }
234 
235     Status = FvBlock->Write (
236                         FvBlock,
237                         Lba,
238                         Offset,
239                         &MyLength,
240                         Ptr
241                         );
242     if (EFI_ERROR (Status)) {
243       return EFI_ABORTED;
244     }
245     Offset = 0;
246     Length -= MyLength;
247     Ptr += MyLength;
248     Lba++;
249   }
250   return EFI_SUCCESS;
251 }
252 
253 /**
254   Read from working block to refresh the work space in memory.
255 
256   @param FtwDevice   Point to private data of FTW driver
257 
258   @retval  EFI_SUCCESS    The function completed successfully
259   @retval  EFI_ABORTED    The function could not complete successfully.
260 
261 **/
262 EFI_STATUS
WorkSpaceRefresh(IN EFI_FTW_DEVICE * FtwDevice)263 WorkSpaceRefresh (
264   IN EFI_FTW_DEVICE  *FtwDevice
265   )
266 {
267   EFI_STATUS                      Status;
268   UINTN                           RemainingSpaceSize;
269 
270   //
271   // Initialize WorkSpace as FTW_ERASED_BYTE
272   //
273   SetMem (
274     FtwDevice->FtwWorkSpace,
275     FtwDevice->FtwWorkSpaceSize,
276     FTW_ERASED_BYTE
277     );
278 
279   //
280   // Read from working block
281   //
282   Status = ReadWorkSpaceData (
283              FtwDevice->FtwFvBlock,
284              FtwDevice->WorkBlockSize,
285              FtwDevice->FtwWorkSpaceLba,
286              FtwDevice->FtwWorkSpaceBase,
287              FtwDevice->FtwWorkSpaceSize,
288              FtwDevice->FtwWorkSpace
289              );
290   if (EFI_ERROR (Status)) {
291     return EFI_ABORTED;
292   }
293   //
294   // Refresh the FtwLastWriteHeader
295   //
296   Status = FtwGetLastWriteHeader (
297             FtwDevice->FtwWorkSpaceHeader,
298             FtwDevice->FtwWorkSpaceSize,
299             &FtwDevice->FtwLastWriteHeader
300             );
301   RemainingSpaceSize = FtwDevice->FtwWorkSpaceSize - ((UINTN) FtwDevice->FtwLastWriteHeader - (UINTN) FtwDevice->FtwWorkSpace);
302   DEBUG ((EFI_D_INFO, "Ftw: Remaining work space size - %x\n", RemainingSpaceSize));
303   //
304   // If FtwGetLastWriteHeader() returns error, or the remaining space size is even not enough to contain
305   // one EFI_FAULT_TOLERANT_WRITE_HEADER + one EFI_FAULT_TOLERANT_WRITE_RECORD(It will cause that the header
306   // pointed by FtwDevice->FtwLastWriteHeader or record pointed by FtwDevice->FtwLastWriteRecord may contain invalid data),
307   // it needs to reclaim work space.
308   //
309   if (EFI_ERROR (Status) || RemainingSpaceSize < sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER) + sizeof (EFI_FAULT_TOLERANT_WRITE_RECORD)) {
310     //
311     // reclaim work space in working block.
312     //
313     Status = FtwReclaimWorkSpace (FtwDevice, TRUE);
314     if (EFI_ERROR (Status)) {
315       DEBUG ((EFI_D_ERROR, "Ftw: Reclaim workspace - %r\n", Status));
316       return EFI_ABORTED;
317     }
318     //
319     // Read from working block again
320     //
321     Status = ReadWorkSpaceData (
322                FtwDevice->FtwFvBlock,
323                FtwDevice->WorkBlockSize,
324                FtwDevice->FtwWorkSpaceLba,
325                FtwDevice->FtwWorkSpaceBase,
326                FtwDevice->FtwWorkSpaceSize,
327                FtwDevice->FtwWorkSpace
328                );
329     if (EFI_ERROR (Status)) {
330       return EFI_ABORTED;
331     }
332 
333     Status = FtwGetLastWriteHeader (
334               FtwDevice->FtwWorkSpaceHeader,
335               FtwDevice->FtwWorkSpaceSize,
336               &FtwDevice->FtwLastWriteHeader
337               );
338     if (EFI_ERROR (Status)) {
339       return EFI_ABORTED;
340     }
341   }
342   //
343   // Refresh the FtwLastWriteRecord
344   //
345   Status = FtwGetLastWriteRecord (
346             FtwDevice->FtwLastWriteHeader,
347             &FtwDevice->FtwLastWriteRecord
348             );
349   if (EFI_ERROR (Status)) {
350     return EFI_ABORTED;
351   }
352 
353   return EFI_SUCCESS;
354 }
355 
356 /**
357   Reclaim the work space on the working block.
358 
359   @param FtwDevice       Point to private data of FTW driver
360   @param PreserveRecord  Whether to preserve the working record is needed
361 
362   @retval EFI_SUCCESS            The function completed successfully
363   @retval EFI_OUT_OF_RESOURCES   Allocate memory error
364   @retval EFI_ABORTED            The function could not complete successfully
365 
366 **/
367 EFI_STATUS
FtwReclaimWorkSpace(IN EFI_FTW_DEVICE * FtwDevice,IN BOOLEAN PreserveRecord)368 FtwReclaimWorkSpace (
369   IN EFI_FTW_DEVICE  *FtwDevice,
370   IN BOOLEAN         PreserveRecord
371   )
372 {
373   EFI_STATUS                              Status;
374   UINTN                                   Length;
375   EFI_FAULT_TOLERANT_WRITE_HEADER         *Header;
376   UINT8                                   *TempBuffer;
377   UINTN                                   TempBufferSize;
378   UINTN                                   SpareBufferSize;
379   UINT8                                   *SpareBuffer;
380   EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader;
381   UINTN                                   Index;
382   UINT8                                   *Ptr;
383   EFI_LBA                                 WorkSpaceLbaOffset;
384 
385   DEBUG ((EFI_D_INFO, "Ftw: start to reclaim work space\n"));
386 
387   WorkSpaceLbaOffset = FtwDevice->FtwWorkSpaceLba - FtwDevice->FtwWorkBlockLba;
388 
389   //
390   // Read all original data from working block to a memory buffer
391   //
392   TempBufferSize = FtwDevice->NumberOfWorkBlock * FtwDevice->WorkBlockSize;
393   TempBuffer     = AllocateZeroPool (TempBufferSize);
394   if (TempBuffer == NULL) {
395     return EFI_OUT_OF_RESOURCES;
396   }
397 
398   Ptr = TempBuffer;
399   for (Index = 0; Index < FtwDevice->NumberOfWorkBlock; Index += 1) {
400     Length = FtwDevice->WorkBlockSize;
401     Status = FtwDevice->FtwFvBlock->Read (
402                                           FtwDevice->FtwFvBlock,
403                                           FtwDevice->FtwWorkBlockLba + Index,
404                                           0,
405                                           &Length,
406                                           Ptr
407                                           );
408     if (EFI_ERROR (Status)) {
409       FreePool (TempBuffer);
410       return EFI_ABORTED;
411     }
412 
413     Ptr += Length;
414   }
415   //
416   // Clean up the workspace, remove all the completed records.
417   //
418   Ptr = TempBuffer +
419         (UINTN) WorkSpaceLbaOffset * FtwDevice->WorkBlockSize +
420         FtwDevice->FtwWorkSpaceBase;
421 
422   //
423   // Clear the content of buffer that will save the new work space data
424   //
425   SetMem (Ptr, FtwDevice->FtwWorkSpaceSize, FTW_ERASED_BYTE);
426 
427   //
428   // Copy EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER to buffer
429   //
430   CopyMem (
431     Ptr,
432     FtwDevice->FtwWorkSpaceHeader,
433     sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)
434     );
435   if (PreserveRecord) {
436     //
437     // Get the last record following the header,
438     //
439     Status = FtwGetLastWriteHeader (
440                FtwDevice->FtwWorkSpaceHeader,
441                FtwDevice->FtwWorkSpaceSize,
442                &FtwDevice->FtwLastWriteHeader
443                );
444     Header = FtwDevice->FtwLastWriteHeader;
445     if (!EFI_ERROR (Status) && (Header != NULL) && (Header->Complete != FTW_VALID_STATE) && (Header->HeaderAllocated == FTW_VALID_STATE)) {
446       CopyMem (
447         Ptr + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
448         FtwDevice->FtwLastWriteHeader,
449         FTW_WRITE_TOTAL_SIZE (Header->NumberOfWrites, Header->PrivateDataSize)
450         );
451     }
452   }
453 
454   CopyMem (
455     FtwDevice->FtwWorkSpace,
456     Ptr,
457     FtwDevice->FtwWorkSpaceSize
458     );
459 
460   FtwGetLastWriteHeader (
461     FtwDevice->FtwWorkSpaceHeader,
462     FtwDevice->FtwWorkSpaceSize,
463     &FtwDevice->FtwLastWriteHeader
464     );
465 
466   FtwGetLastWriteRecord (
467     FtwDevice->FtwLastWriteHeader,
468     &FtwDevice->FtwLastWriteRecord
469     );
470 
471   //
472   // Set the WorkingBlockValid and WorkingBlockInvalid as INVALID
473   //
474   WorkingBlockHeader                      = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (TempBuffer +
475                                             (UINTN) WorkSpaceLbaOffset * FtwDevice->WorkBlockSize +
476                                             FtwDevice->FtwWorkSpaceBase);
477   WorkingBlockHeader->WorkingBlockValid   = FTW_INVALID_STATE;
478   WorkingBlockHeader->WorkingBlockInvalid = FTW_INVALID_STATE;
479 
480   //
481   // Try to keep the content of spare block
482   // Save spare block into a spare backup memory buffer (Sparebuffer)
483   //
484   SpareBufferSize = FtwDevice->SpareAreaLength;
485   SpareBuffer     = AllocatePool (SpareBufferSize);
486   if (SpareBuffer == NULL) {
487     FreePool (TempBuffer);
488     return EFI_OUT_OF_RESOURCES;
489   }
490 
491   Ptr = SpareBuffer;
492   for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
493     Length = FtwDevice->SpareBlockSize;
494     Status = FtwDevice->FtwBackupFvb->Read (
495                                         FtwDevice->FtwBackupFvb,
496                                         FtwDevice->FtwSpareLba + Index,
497                                         0,
498                                         &Length,
499                                         Ptr
500                                         );
501     if (EFI_ERROR (Status)) {
502       FreePool (TempBuffer);
503       FreePool (SpareBuffer);
504       return EFI_ABORTED;
505     }
506 
507     Ptr += Length;
508   }
509   //
510   // Write the memory buffer to spare block
511   //
512   Status  = FtwEraseSpareBlock (FtwDevice);
513   if (EFI_ERROR (Status)) {
514     FreePool (TempBuffer);
515     FreePool (SpareBuffer);
516     return EFI_ABORTED;
517   }
518   Ptr     = TempBuffer;
519   for (Index = 0; TempBufferSize > 0; Index += 1) {
520     if (TempBufferSize > FtwDevice->SpareBlockSize) {
521       Length = FtwDevice->SpareBlockSize;
522     } else {
523       Length = TempBufferSize;
524     }
525     Status = FtwDevice->FtwBackupFvb->Write (
526                                             FtwDevice->FtwBackupFvb,
527                                             FtwDevice->FtwSpareLba + Index,
528                                             0,
529                                             &Length,
530                                             Ptr
531                                             );
532     if (EFI_ERROR (Status)) {
533       FreePool (TempBuffer);
534       FreePool (SpareBuffer);
535       return EFI_ABORTED;
536     }
537 
538     Ptr += Length;
539     TempBufferSize -= Length;
540   }
541   //
542   // Free TempBuffer
543   //
544   FreePool (TempBuffer);
545 
546   //
547   // Set the WorkingBlockValid in spare block
548   //
549   Status = FtwUpdateFvState (
550             FtwDevice->FtwBackupFvb,
551             FtwDevice->SpareBlockSize,
552             FtwDevice->FtwSpareLba + FtwDevice->FtwWorkSpaceLbaInSpare,
553             FtwDevice->FtwWorkSpaceBaseInSpare + sizeof (EFI_GUID) + sizeof (UINT32),
554             WORKING_BLOCK_VALID
555             );
556   if (EFI_ERROR (Status)) {
557     FreePool (SpareBuffer);
558     return EFI_ABORTED;
559   }
560   //
561   // Before erase the working block, set WorkingBlockInvalid in working block.
562   //
563   // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
564   //                          WorkingBlockInvalid);
565   //
566   Status = FtwUpdateFvState (
567             FtwDevice->FtwFvBlock,
568             FtwDevice->WorkBlockSize,
569             FtwDevice->FtwWorkSpaceLba,
570             FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),
571             WORKING_BLOCK_INVALID
572             );
573   if (EFI_ERROR (Status)) {
574     FreePool (SpareBuffer);
575     return EFI_ABORTED;
576   }
577 
578   FtwDevice->FtwWorkSpaceHeader->WorkingBlockInvalid = FTW_VALID_STATE;
579 
580   //
581   // Write the spare block to working block
582   //
583   Status = FlushSpareBlockToWorkingBlock (FtwDevice);
584   if (EFI_ERROR (Status)) {
585     FreePool (SpareBuffer);
586     return Status;
587   }
588   //
589   // Restore spare backup buffer into spare block , if no failure happened during FtwWrite.
590   //
591   Status  = FtwEraseSpareBlock (FtwDevice);
592   if (EFI_ERROR (Status)) {
593     FreePool (SpareBuffer);
594     return EFI_ABORTED;
595   }
596   Ptr     = SpareBuffer;
597   for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
598     Length = FtwDevice->SpareBlockSize;
599     Status = FtwDevice->FtwBackupFvb->Write (
600                                         FtwDevice->FtwBackupFvb,
601                                         FtwDevice->FtwSpareLba + Index,
602                                         0,
603                                         &Length,
604                                         Ptr
605                                         );
606     if (EFI_ERROR (Status)) {
607       FreePool (SpareBuffer);
608       return EFI_ABORTED;
609     }
610 
611     Ptr += Length;
612   }
613 
614   FreePool (SpareBuffer);
615 
616   DEBUG ((EFI_D_INFO, "Ftw: reclaim work space successfully\n"));
617 
618   return EFI_SUCCESS;
619 }
620