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