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