]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.c
1. Fix some convert issue.
[mirror_edk2.git] / MdeModulePkg / Universal / FaultTolerantWriteDxe / FaultTolerantWrite.c
CommitLineData
85e923a5
LG
1/** @file\r
2\r
3 This is a simple fault tolerant write driver.\r
4\r
5 This boot service protocol only provides fault tolerant write capability for \r
6 block devices. The protocol has internal non-volatile intermediate storage \r
7 of the data and private information. It should be able to recover \r
8 automatically from a critical fault, such as power failure. \r
9\r
10 The implementation uses an FTW (Fault Tolerant Write) Work Space. \r
11 This work space is a memory copy of the work space on the Working Block,\r
12 the size of the work space is the FTW_WORK_SPACE_SIZE bytes.\r
13 \r
14 The work space stores each write record as EFI_FTW_RECORD structure.\r
15 The spare block stores the write buffer before write to the target block.\r
16 \r
17 The write record has three states to specify the different phase of write operation.\r
18 1) WRITE_ALLOCATED is that the record is allocated in write space.\r
19 The information of write operation is stored in write record structure.\r
20 2) SPARE_COMPLETED is that the data from write buffer is writed into the spare block as the backup.\r
21 3) WRITE_COMPLETED is that the data is copied from the spare block to the target block.\r
22\r
23 This driver operates the data as the whole size of spare block.\r
24 It first read the SpareAreaLength data from the target block into the spare memory buffer.\r
25 Then copy the write buffer data into the spare memory buffer.\r
26 Then write the spare memory buffer into the spare block.\r
27 Final copy the data from the spare block to the target block.\r
28\r
29 To make this drive work well, the following conditions must be satisfied:\r
30 1. The write NumBytes data must be fit within Spare area. \r
31 Offset + NumBytes <= SpareAreaLength\r
32 2. The whole flash range has the same block size.\r
33 3. Working block is an area which contains working space in its last block and has the same size as spare block.\r
34 4. Working Block area must be in the single one Firmware Volume Block range which FVB protocol is produced on. \r
35 5. Spare area must be in the single one Firmware Volume Block range which FVB protocol is produced on.\r
36 6. Any write data area (SpareAreaLength Area) which the data will be written into must be \r
37 in the single one Firmware Volume Block range which FVB protocol is produced on.\r
38 7. If write data area (such as Variable range) is enlarged, the spare area range must be enlarged.\r
39 The spare area must be enough large to store the write data before write them into the target range.\r
40 If one of them is not satisfied, FtwWrite may fail.\r
41 Usually, Spare area only takes one block. That's SpareAreaLength = BlockSize, NumberOfSpareBlock = 1.\r
42\r
43Copyright (c) 2006 - 2009, Intel Corporation \r
44All rights reserved. This program and the accompanying materials \r
45are licensed and made available under the terms and conditions of the BSD License \r
46which accompanies this distribution. The full text of the license may be found at \r
47http://opensource.org/licenses/bsd-license.php \r
48 \r
49THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
50WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
51\r
52**/\r
53\r
54#include "FaultTolerantWrite.h"\r
55\r
56\r
57//\r
58// Fault Tolerant Write Protocol API\r
59//\r
60/**\r
61 Query the largest block that may be updated in a fault tolerant manner.\r
62\r
63\r
64 @param This The pointer to this protocol instance. \r
65 @param BlockSize A pointer to a caller allocated UINTN that is updated to\r
66 indicate the size of the largest block that can be updated.\r
67\r
68 @return EFI_SUCCESS The function completed successfully\r
69\r
70**/\r
71EFI_STATUS\r
72EFIAPI\r
73FtwGetMaxBlockSize (\r
74 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,\r
75 OUT UINTN *BlockSize\r
76 )\r
77{\r
78 EFI_FTW_DEVICE *FtwDevice;\r
79\r
80 if (!FeaturePcdGet(PcdFullFtwServiceEnable)) {\r
81 return EFI_UNSUPPORTED;\r
82 }\r
83\r
84 FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
85\r
86 *BlockSize = FtwDevice->SpareAreaLength;\r
87\r
88 return EFI_SUCCESS;\r
89}\r
90\r
91/**\r
92 Allocates space for the protocol to maintain information about writes.\r
93 Since writes must be completed in a fault tolerant manner and multiple\r
94 updates will require more resources to be successful, this function\r
95 enables the protocol to ensure that enough space exists to track\r
96 information about the upcoming writes.\r
97\r
98 All writes must be completed or aborted before another fault tolerant write can occur.\r
99\r
100 @param This The pointer to this protocol instance. \r
101 @param CallerId The GUID identifying the write.\r
102 @param PrivateDataSize The size of the caller's private data\r
103 that must be recorded for each write.\r
104 @param NumberOfWrites The number of fault tolerant block writes\r
105 that will need to occur.\r
106\r
107 @return EFI_SUCCESS The function completed successfully\r
108 @retval EFI_ABORTED The function could not complete successfully.\r
109 @retval EFI_ACCESS_DENIED All allocated writes have not been completed.\r
110\r
111**/\r
112EFI_STATUS\r
113EFIAPI\r
114FtwAllocate (\r
115 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,\r
116 IN EFI_GUID *CallerId,\r
117 IN UINTN PrivateDataSize,\r
118 IN UINTN NumberOfWrites\r
119 )\r
120{\r
121 EFI_STATUS Status;\r
122 UINTN Length;\r
123 UINTN Offset;\r
124 EFI_FTW_DEVICE *FtwDevice;\r
125 EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader;\r
126\r
127 FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
128\r
129 Status = WorkSpaceRefresh (FtwDevice);\r
130 if (EFI_ERROR (Status)) {\r
131 return EFI_ABORTED;\r
132 }\r
133 //\r
134 // Check if there is enough space for the coming allocation\r
135 //\r
136 if (WRITE_TOTAL_SIZE (NumberOfWrites, PrivateDataSize) > FtwDevice->FtwWorkSpaceHeader->WriteQueueSize) {\r
137 DEBUG ((EFI_D_ERROR, "Ftw: Allocate() request exceed Workspace, Caller: %g\n", CallerId));\r
138 return EFI_BUFFER_TOO_SMALL;\r
139 }\r
140 //\r
141 // Find the last write header and record.\r
142 // If the FtwHeader is complete, skip the completed last write header/records\r
143 //\r
144 FtwHeader = FtwDevice->FtwLastWriteHeader;\r
145\r
146 //\r
147 // Previous write has not completed, access denied.\r
148 //\r
149 if ((FtwHeader->HeaderAllocated == FTW_VALID_STATE) || (FtwHeader->WritesAllocated == FTW_VALID_STATE)) {\r
150 return EFI_ACCESS_DENIED;\r
151 }\r
152 //\r
153 // If workspace is not enough, then reclaim workspace\r
154 //\r
155 Offset = (UINT8 *) FtwHeader - (UINT8 *) FtwDevice->FtwWorkSpace;\r
156 if (Offset + WRITE_TOTAL_SIZE (NumberOfWrites, PrivateDataSize) > FtwDevice->FtwWorkSpaceSize) {\r
157 Status = FtwReclaimWorkSpace (FtwDevice, TRUE);\r
158 if (EFI_ERROR (Status)) {\r
159 return EFI_ABORTED;\r
160 }\r
161\r
162 FtwHeader = FtwDevice->FtwLastWriteHeader;\r
163 }\r
164 //\r
165 // Prepare FTW write header,\r
166 // overwrite the buffer and write to workspace.\r
167 //\r
168 FtwHeader->WritesAllocated = FTW_INVALID_STATE;\r
169 FtwHeader->Complete = FTW_INVALID_STATE;\r
170 CopyMem (&FtwHeader->CallerId, CallerId, sizeof (EFI_GUID));\r
171 FtwHeader->NumberOfWrites = NumberOfWrites;\r
172 FtwHeader->PrivateDataSize = PrivateDataSize;\r
173 FtwHeader->HeaderAllocated = FTW_VALID_STATE;\r
174\r
175 Length = sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER);\r
176 Status = FtwDevice->FtwFvBlock->Write (\r
177 FtwDevice->FtwFvBlock,\r
178 FtwDevice->FtwWorkSpaceLba,\r
179 FtwDevice->FtwWorkSpaceBase + Offset,\r
180 &Length,\r
181 (UINT8 *) FtwHeader\r
182 );\r
183 if (EFI_ERROR (Status)) {\r
184 return EFI_ABORTED;\r
185 }\r
186 //\r
187 // Update Header->WriteAllocated as VALID\r
188 //\r
189 Status = FtwUpdateFvState (\r
190 FtwDevice->FtwFvBlock,\r
191 FtwDevice->FtwWorkSpaceLba,\r
192 FtwDevice->FtwWorkSpaceBase + Offset,\r
193 WRITES_ALLOCATED\r
194 );\r
195 if (EFI_ERROR (Status)) {\r
196 return EFI_ABORTED;\r
197 }\r
198\r
199 DEBUG (\r
200 (EFI_D_ERROR,\r
201 "Ftw: Allocate() success, Caller:%g, # %d\n",\r
202 CallerId,\r
203 NumberOfWrites)\r
204 );\r
205\r
206 return EFI_SUCCESS;\r
207}\r
208\r
209\r
210/**\r
211 Write a record with fault tolerant mannaer.\r
212 Since the content has already backuped in spare block, the write is\r
213 guaranteed to be completed with fault tolerant manner.\r
214\r
215 @param This The pointer to this protocol instance. \r
216 @param Fvb The FVB protocol that provides services for\r
217 reading, writing, and erasing the target block.\r
218\r
219 @retval EFI_SUCCESS The function completed successfully\r
220 @retval EFI_ABORTED The function could not complete successfully\r
221\r
222**/\r
223EFI_STATUS\r
224FtwWriteRecord (\r
225 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,\r
226 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb\r
227 )\r
228{\r
229 EFI_STATUS Status;\r
230 EFI_FTW_DEVICE *FtwDevice;\r
231 EFI_FAULT_TOLERANT_WRITE_HEADER *Header;\r
232 EFI_FAULT_TOLERANT_WRITE_RECORD *Record;\r
233 UINTN Offset;\r
234\r
235 FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
236\r
237 //\r
238 // Spare Complete but Destination not complete,\r
239 // Recover the targt block with the spare block.\r
240 //\r
241 Header = FtwDevice->FtwLastWriteHeader;\r
242 Record = FtwDevice->FtwLastWriteRecord;\r
243\r
244 //\r
245 // IF target block is working block, THEN Flush Spare Block To Working Block;\r
246 // ELSE flush spare block to target block, which may be boot block after all.\r
247 //\r
248 if (IsWorkingBlock (FtwDevice, Fvb, Record->Lba)) {\r
249 //\r
250 // If target block is working block,\r
251 // it also need to set SPARE_COMPLETED to spare block.\r
252 //\r
253 Offset = (UINT8 *) Record - FtwDevice->FtwWorkSpace;\r
254 Status = FtwUpdateFvState (\r
255 FtwDevice->FtwBackupFvb,\r
256 FtwDevice->FtwWorkSpaceLba,\r
257 FtwDevice->FtwWorkSpaceBase + Offset,\r
258 SPARE_COMPLETED\r
259 );\r
260 if (EFI_ERROR (Status)) {\r
261 return EFI_ABORTED;\r
262 }\r
263\r
264 Status = FlushSpareBlockToWorkingBlock (FtwDevice);\r
265 } else if (IsBootBlock (FtwDevice, Fvb, Record->Lba)) {\r
266 //\r
267 // Update boot block\r
268 //\r
269 Status = FlushSpareBlockToBootBlock (FtwDevice);\r
270 } else {\r
271 //\r
272 // Update blocks other than working block or boot block\r
273 //\r
274 Status = FlushSpareBlockToTargetBlock (FtwDevice, Fvb, Record->Lba);\r
275 }\r
276\r
277 if (EFI_ERROR (Status)) {\r
278 return EFI_ABORTED;\r
279 }\r
280 //\r
281 // Record the DestionationComplete in record\r
282 //\r
283 Offset = (UINT8 *) Record - FtwDevice->FtwWorkSpace;\r
284 Status = FtwUpdateFvState (\r
285 FtwDevice->FtwFvBlock,\r
286 FtwDevice->FtwWorkSpaceLba,\r
287 FtwDevice->FtwWorkSpaceBase + Offset,\r
288 DEST_COMPLETED\r
289 );\r
290 if (EFI_ERROR (Status)) {\r
291 return EFI_ABORTED;\r
292 }\r
293\r
294 Record->DestinationComplete = FTW_VALID_STATE;\r
295\r
296 //\r
297 // If this is the last Write in these write sequence,\r
298 // set the complete flag of write header.\r
299 //\r
300 if (IsLastRecordOfWrites (Header, Record)) {\r
301 Offset = (UINT8 *) Header - FtwDevice->FtwWorkSpace;\r
302 Status = FtwUpdateFvState (\r
303 FtwDevice->FtwFvBlock,\r
304 FtwDevice->FtwWorkSpaceLba,\r
305 FtwDevice->FtwWorkSpaceBase + Offset,\r
306 WRITES_COMPLETED\r
307 );\r
308 Header->Complete = FTW_VALID_STATE;\r
309 if (EFI_ERROR (Status)) {\r
310 return EFI_ABORTED;\r
311 }\r
312 }\r
313\r
314 return EFI_SUCCESS;\r
315}\r
316\r
317/**\r
318 Starts a target block update. This function will record data about write\r
319 in fault tolerant storage and will complete the write in a recoverable\r
320 manner, ensuring at all times that either the original contents or\r
321 the modified contents are available.\r
322\r
323 @param This The pointer to this protocol instance. \r
324 @param Lba The logical block address of the target block.\r
325 @param Offset The offset within the target block to place the data.\r
326 @param Length The number of bytes to write to the target block.\r
327 @param PrivateData A pointer to private data that the caller requires to\r
328 complete any pending writes in the event of a fault.\r
329 @param FvBlockHandle The handle of FVB protocol that provides services for\r
330 reading, writing, and erasing the target block.\r
331 @param Buffer The data to write.\r
332\r
333 @retval EFI_SUCCESS The function completed successfully \r
334 @retval EFI_ABORTED The function could not complete successfully. \r
335 @retval EFI_BAD_BUFFER_SIZE The input data can't fit within the spare block. \r
336 Offset + *NumBytes > SpareAreaLength.\r
337 @retval EFI_ACCESS_DENIED No writes have been allocated. \r
338 @retval EFI_OUT_OF_RESOURCES Cannot allocate enough memory resource.\r
339 @retval EFI_NOT_FOUND Cannot find FVB protocol by handle.\r
340\r
341**/\r
342EFI_STATUS\r
343EFIAPI\r
344FtwWrite (\r
345 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,\r
346 IN EFI_LBA Lba,\r
347 IN UINTN Offset,\r
348 IN UINTN Length,\r
349 IN VOID *PrivateData,\r
350 IN EFI_HANDLE FvBlockHandle,\r
351 IN VOID *Buffer\r
352 )\r
353{\r
354 EFI_STATUS Status;\r
355 EFI_FTW_DEVICE *FtwDevice;\r
356 EFI_FAULT_TOLERANT_WRITE_HEADER *Header;\r
357 EFI_FAULT_TOLERANT_WRITE_RECORD *Record;\r
358 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;\r
359 UINTN MyLength;\r
360 UINTN MyOffset;\r
361 UINTN MyBufferSize;\r
362 UINT8 *MyBuffer;\r
363 UINTN SpareBufferSize;\r
364 UINT8 *SpareBuffer;\r
365 UINTN Index;\r
366 UINT8 *Ptr;\r
367 EFI_PHYSICAL_ADDRESS FvbPhysicalAddress;\r
368\r
369 FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
370\r
371 Status = WorkSpaceRefresh (FtwDevice);\r
372 if (EFI_ERROR (Status)) {\r
373 return EFI_ABORTED;\r
374 }\r
375\r
376 Header = FtwDevice->FtwLastWriteHeader;\r
377 Record = FtwDevice->FtwLastWriteRecord;\r
378 \r
379 if (IsErasedFlashBuffer ((UINT8 *) Header, sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER))) {\r
380 if (PrivateData == NULL) {\r
381 //\r
382 // Ftw Write Header is not allocated.\r
383 // No additional private data, the private data size is zero. Number of record can be set to 1.\r
384 //\r
385 Status = FtwAllocate (This, &gEfiCallerIdGuid, 0, 1);\r
386 if (EFI_ERROR (Status)) {\r
387 return Status;\r
388 }\r
389 } else {\r
390 //\r
391 // Ftw Write Header is not allocated\r
392 // Additional private data is not NULL, the private data size can't be determined.\r
393 //\r
394 DEBUG ((EFI_D_ERROR, "Ftw: no allocates space for write record!\n"));\r
395 DEBUG ((EFI_D_ERROR, "Ftw: Allocate service should be called before Write service!\n"));\r
396 return EFI_NOT_READY;\r
397 }\r
398 }\r
399\r
400 //\r
401 // If Record is out of the range of Header, return access denied.\r
402 //\r
403 if (((UINTN)((UINT8 *) Record - (UINT8 *) Header)) > WRITE_TOTAL_SIZE (Header->NumberOfWrites - 1, Header->PrivateDataSize)) {\r
404 return EFI_ACCESS_DENIED;\r
405 }\r
406\r
407 //\r
408 // Check the COMPLETE flag of last write header\r
409 //\r
410 if (Header->Complete == FTW_VALID_STATE) {\r
411 return EFI_ACCESS_DENIED;\r
412 }\r
413\r
414 if (Record->DestinationComplete == FTW_VALID_STATE) {\r
415 return EFI_ACCESS_DENIED;\r
416 }\r
417\r
418 if ((Record->SpareComplete == FTW_VALID_STATE) && (Record->DestinationComplete != FTW_VALID_STATE)) {\r
419 return EFI_NOT_READY;\r
420 }\r
421 //\r
422 // Check if the input data can fit within the target block\r
423 //\r
424 if ((Offset + Length) > FtwDevice->SpareAreaLength) {\r
425 return EFI_BAD_BUFFER_SIZE;\r
426 }\r
427 //\r
428 // Get the FVB protocol by handle\r
429 //\r
430 Status = FtwGetFvbByHandle (FvBlockHandle, &Fvb);\r
431 if (EFI_ERROR (Status)) {\r
432 return EFI_NOT_FOUND;\r
433 }\r
434\r
435 Status = Fvb->GetPhysicalAddress (Fvb, &FvbPhysicalAddress);\r
436 if (EFI_ERROR (Status)) {\r
437 DEBUG ((EFI_D_ERROR, "FtwLite: Get FVB physical address - %r\n", Status));\r
438 return EFI_ABORTED;\r
439 }\r
440\r
441 //\r
442 // Set BootBlockUpdate FLAG if it's updating boot block.\r
443 //\r
444 if (IsBootBlock (FtwDevice, Fvb, Lba)) {\r
445 Record->BootBlockUpdate = FTW_VALID_STATE;\r
446 }\r
447 //\r
448 // Write the record to the work space.\r
449 //\r
450 Record->Lba = Lba;\r
451 Record->Offset = Offset;\r
452 Record->Length = Length;\r
453 Record->FvBaseAddress = FvbPhysicalAddress;\r
f0480ecf
LG
454 if (PrivateData != NULL) {\r
455 CopyMem ((Record + 1), PrivateData, Header->PrivateDataSize);\r
456 }\r
85e923a5
LG
457\r
458 MyOffset = (UINT8 *) Record - FtwDevice->FtwWorkSpace;\r
459 MyLength = RECORD_SIZE (Header->PrivateDataSize);\r
460\r
461 Status = FtwDevice->FtwFvBlock->Write (\r
462 FtwDevice->FtwFvBlock,\r
463 FtwDevice->FtwWorkSpaceLba,\r
464 FtwDevice->FtwWorkSpaceBase + MyOffset,\r
465 &MyLength,\r
466 (UINT8 *) Record\r
467 );\r
468 if (EFI_ERROR (Status)) {\r
469 return EFI_ABORTED;\r
470 }\r
471 //\r
472 // Record has written to working block, then do the data.\r
473 //\r
474 //\r
475 // Allocate a memory buffer\r
476 //\r
477 MyBufferSize = FtwDevice->SpareAreaLength;\r
478 MyBuffer = AllocatePool (MyBufferSize);\r
479 if (MyBuffer == NULL) {\r
480 return EFI_OUT_OF_RESOURCES;\r
481 }\r
482 //\r
483 // Read all original data from target block to memory buffer\r
484 //\r
485 Ptr = MyBuffer;\r
486 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
487 MyLength = FtwDevice->BlockSize;\r
488 Status = Fvb->Read (Fvb, Lba + Index, 0, &MyLength, Ptr);\r
489 if (EFI_ERROR (Status)) {\r
490 FreePool (MyBuffer);\r
491 return EFI_ABORTED;\r
492 }\r
493\r
494 Ptr += MyLength;\r
495 }\r
496 //\r
497 // Overwrite the updating range data with\r
498 // the input buffer content\r
499 //\r
500 CopyMem (MyBuffer + Offset, Buffer, Length);\r
501\r
502 //\r
503 // Try to keep the content of spare block\r
504 // Save spare block into a spare backup memory buffer (Sparebuffer)\r
505 //\r
506 SpareBufferSize = FtwDevice->SpareAreaLength;\r
507 SpareBuffer = AllocatePool (SpareBufferSize);\r
508 if (SpareBuffer == NULL) {\r
509 FreePool (MyBuffer);\r
510 return EFI_OUT_OF_RESOURCES;\r
511 }\r
512\r
513 Ptr = SpareBuffer;\r
514 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
515 MyLength = FtwDevice->BlockSize;\r
516 Status = FtwDevice->FtwBackupFvb->Read (\r
517 FtwDevice->FtwBackupFvb,\r
518 FtwDevice->FtwSpareLba + Index,\r
519 0,\r
520 &MyLength,\r
521 Ptr\r
522 );\r
523 if (EFI_ERROR (Status)) {\r
524 FreePool (MyBuffer);\r
525 FreePool (SpareBuffer);\r
526 return EFI_ABORTED;\r
527 }\r
528\r
529 Ptr += MyLength;\r
530 }\r
531 //\r
532 // Write the memory buffer to spare block\r
533 //\r
534 Status = FtwEraseSpareBlock (FtwDevice);\r
535 Ptr = MyBuffer;\r
536 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
537 MyLength = FtwDevice->BlockSize;\r
538 Status = FtwDevice->FtwBackupFvb->Write (\r
539 FtwDevice->FtwBackupFvb,\r
540 FtwDevice->FtwSpareLba + Index,\r
541 0,\r
542 &MyLength,\r
543 Ptr\r
544 );\r
545 if (EFI_ERROR (Status)) {\r
546 FreePool (MyBuffer);\r
547 FreePool (SpareBuffer);\r
548 return EFI_ABORTED;\r
549 }\r
550\r
551 Ptr += MyLength;\r
552 }\r
553 //\r
554 // Free MyBuffer\r
555 //\r
556 FreePool (MyBuffer);\r
557\r
558 //\r
559 // Set the SpareComplete in the FTW record,\r
560 //\r
561 MyOffset = (UINT8 *) Record - FtwDevice->FtwWorkSpace;\r
562 Status = FtwUpdateFvState (\r
563 FtwDevice->FtwFvBlock,\r
564 FtwDevice->FtwWorkSpaceLba,\r
565 FtwDevice->FtwWorkSpaceBase + MyOffset,\r
566 SPARE_COMPLETED\r
567 );\r
568 if (EFI_ERROR (Status)) {\r
569 FreePool (SpareBuffer);\r
570 return EFI_ABORTED;\r
571 }\r
572\r
573 Record->SpareComplete = FTW_VALID_STATE;\r
574\r
575 //\r
576 // Since the content has already backuped in spare block, the write is\r
577 // guaranteed to be completed with fault tolerant manner.\r
578 //\r
579 Status = FtwWriteRecord (This, Fvb);\r
580 if (EFI_ERROR (Status)) {\r
581 FreePool (SpareBuffer);\r
582 return EFI_ABORTED;\r
583 }\r
584 //\r
585 // Restore spare backup buffer into spare block , if no failure happened during FtwWrite.\r
586 //\r
587 Status = FtwEraseSpareBlock (FtwDevice);\r
588 Ptr = SpareBuffer;\r
589 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
590 MyLength = FtwDevice->BlockSize;\r
591 Status = FtwDevice->FtwBackupFvb->Write (\r
592 FtwDevice->FtwBackupFvb,\r
593 FtwDevice->FtwSpareLba + Index,\r
594 0,\r
595 &MyLength,\r
596 Ptr\r
597 );\r
598 if (EFI_ERROR (Status)) {\r
599 FreePool (SpareBuffer);\r
600 return EFI_ABORTED;\r
601 }\r
602\r
603 Ptr += MyLength;\r
604 }\r
605 //\r
606 // All success.\r
607 //\r
608 FreePool (SpareBuffer);\r
609\r
610 DEBUG (\r
611 (EFI_D_ERROR,\r
612 "Ftw: Write() success, (Lba:Offset)=(%lx:0x%x), Length: 0x%x\n",\r
613 Lba,\r
614 Offset,\r
615 Length)\r
616 );\r
617\r
618 return EFI_SUCCESS;\r
619}\r
620\r
621/**\r
622 Restarts a previously interrupted write. The caller must provide the\r
623 block protocol needed to complete the interrupted write.\r
624\r
625 @param This The pointer to this protocol instance. \r
626 @param FvBlockHandle The handle of FVB protocol that provides services for\r
627 reading, writing, and erasing the target block.\r
628\r
629 @retval EFI_SUCCESS The function completed successfully\r
630 @retval EFI_ACCESS_DENIED No pending writes exist\r
631 @retval EFI_NOT_FOUND FVB protocol not found by the handle\r
632 @retval EFI_ABORTED The function could not complete successfully\r
633\r
634**/\r
635EFI_STATUS\r
636EFIAPI\r
637FtwRestart (\r
638 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,\r
639 IN EFI_HANDLE FvBlockHandle\r
640 )\r
641{\r
642 EFI_STATUS Status;\r
643 EFI_FTW_DEVICE *FtwDevice;\r
644 EFI_FAULT_TOLERANT_WRITE_HEADER *Header;\r
645 EFI_FAULT_TOLERANT_WRITE_RECORD *Record;\r
646 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;\r
647\r
648 FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
649\r
650 Status = WorkSpaceRefresh (FtwDevice);\r
651 if (EFI_ERROR (Status)) {\r
652 return EFI_ABORTED;\r
653 }\r
654\r
655 Header = FtwDevice->FtwLastWriteHeader;\r
656 Record = FtwDevice->FtwLastWriteRecord;\r
657\r
658 //\r
659 // Spare Complete but Destination not complete,\r
660 // Recover the targt block with the spare block.\r
661 //\r
662 Status = FtwGetFvbByHandle (FvBlockHandle, &Fvb);\r
663 if (EFI_ERROR (Status)) {\r
664 return EFI_NOT_FOUND;\r
665 }\r
666\r
667 //\r
668 // Check the COMPLETE flag of last write header\r
669 //\r
670 if (Header->Complete == FTW_VALID_STATE) {\r
671 return EFI_ACCESS_DENIED;\r
672 }\r
673\r
674 //\r
675 // Check the flags of last write record\r
676 //\r
677 if (Record->DestinationComplete == FTW_VALID_STATE) {\r
678 return EFI_ACCESS_DENIED;\r
679 }\r
680\r
681 if ((Record->SpareComplete != FTW_VALID_STATE)) {\r
682 return EFI_ABORTED;\r
683 }\r
684\r
685 //\r
686 // Since the content has already backuped in spare block, the write is\r
687 // guaranteed to be completed with fault tolerant manner.\r
688 //\r
689 Status = FtwWriteRecord (This, Fvb);\r
690 if (EFI_ERROR (Status)) {\r
691 return EFI_ABORTED;\r
692 }\r
693\r
694 //\r
695 // Erase Spare block\r
696 // This is restart, no need to keep spareblock content.\r
697 //\r
698 FtwEraseSpareBlock (FtwDevice);\r
699\r
700 DEBUG ((EFI_D_ERROR, "Ftw: Restart() success \n"));\r
701 return EFI_SUCCESS;\r
702}\r
703\r
704/**\r
705 Aborts all previous allocated writes.\r
706\r
707 @param This The pointer to this protocol instance. \r
708\r
709 @retval EFI_SUCCESS The function completed successfully\r
710 @retval EFI_ABORTED The function could not complete successfully.\r
711 @retval EFI_NOT_FOUND No allocated writes exist.\r
712\r
713**/\r
714EFI_STATUS\r
715EFIAPI\r
716FtwAbort (\r
717 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This\r
718 )\r
719{\r
720 EFI_STATUS Status;\r
721 UINTN Offset;\r
722 EFI_FTW_DEVICE *FtwDevice;\r
723\r
724 FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
725\r
726 Status = WorkSpaceRefresh (FtwDevice);\r
727 if (EFI_ERROR (Status)) {\r
728 return EFI_ABORTED;\r
729 }\r
730\r
731 if (FtwDevice->FtwLastWriteHeader->Complete == FTW_VALID_STATE) {\r
732 return EFI_NOT_FOUND;\r
733 }\r
734 //\r
735 // Update the complete state of the header as VALID and abort.\r
736 //\r
737 Offset = (UINT8 *) FtwDevice->FtwLastWriteHeader - FtwDevice->FtwWorkSpace;\r
738 Status = FtwUpdateFvState (\r
739 FtwDevice->FtwFvBlock,\r
740 FtwDevice->FtwWorkSpaceLba,\r
741 FtwDevice->FtwWorkSpaceBase + Offset,\r
742 WRITES_COMPLETED\r
743 );\r
744 if (EFI_ERROR (Status)) {\r
745 return EFI_ABORTED;\r
746 }\r
747\r
748 FtwDevice->FtwLastWriteHeader->Complete = FTW_VALID_STATE;\r
749\r
750 DEBUG ((EFI_D_ERROR, "Ftw: Abort() success \n"));\r
751 return EFI_SUCCESS;\r
752}\r
753\r
754/**\r
755 Starts a target block update. This records information about the write\r
756 in fault tolerant storage and will complete the write in a recoverable\r
757 manner, ensuring at all times that either the original contents or\r
758 the modified contents are available.\r
759\r
760 @param This The pointer to this protocol instance. \r
761 @param CallerId The GUID identifying the last write.\r
762 @param Lba The logical block address of the last write.\r
763 @param Offset The offset within the block of the last write.\r
764 @param Length The length of the last write.\r
765 @param PrivateDataSize bytes from the private data\r
766 stored for this write.\r
767 @param PrivateData A pointer to a buffer. The function will copy\r
768 @param Complete A Boolean value with TRUE indicating\r
769 that the write was completed.\r
770\r
771 @retval EFI_SUCCESS The function completed successfully\r
772 @retval EFI_ABORTED The function could not complete successfully\r
773 @retval EFI_NOT_FOUND No allocated writes exist\r
774 @retval EFI_BUFFER_TOO_SMALL Input buffer is not larget enough\r
775\r
776**/\r
777EFI_STATUS\r
778EFIAPI\r
779FtwGetLastWrite (\r
780 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,\r
781 OUT EFI_GUID *CallerId,\r
782 OUT EFI_LBA *Lba,\r
783 OUT UINTN *Offset,\r
784 OUT UINTN *Length,\r
785 IN OUT UINTN *PrivateDataSize,\r
786 OUT VOID *PrivateData,\r
787 OUT BOOLEAN *Complete\r
788 )\r
789{\r
790 EFI_STATUS Status;\r
791 EFI_FTW_DEVICE *FtwDevice;\r
792 EFI_FAULT_TOLERANT_WRITE_HEADER *Header;\r
793 EFI_FAULT_TOLERANT_WRITE_RECORD *Record;\r
794\r
795 if (!FeaturePcdGet(PcdFullFtwServiceEnable)) {\r
796 return EFI_UNSUPPORTED;\r
797 }\r
798\r
799 FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
800\r
801 Status = WorkSpaceRefresh (FtwDevice);\r
802 if (EFI_ERROR (Status)) {\r
803 return EFI_ABORTED;\r
804 }\r
805\r
806 Header = FtwDevice->FtwLastWriteHeader;\r
807 Record = FtwDevice->FtwLastWriteRecord;\r
808\r
809 //\r
810 // If Header is incompleted and the last record has completed, then\r
811 // call Abort() to set the Header->Complete FLAG.\r
812 //\r
813 if ((Header->Complete != FTW_VALID_STATE) &&\r
814 (Record->DestinationComplete == FTW_VALID_STATE) &&\r
815 IsLastRecordOfWrites (Header, Record)\r
816 ) {\r
817\r
818 Status = FtwAbort (This);\r
819 *Complete = TRUE;\r
820 return EFI_NOT_FOUND;\r
821 }\r
822 //\r
823 // If there is no write header/record, return not found.\r
824 //\r
825 if (Header->HeaderAllocated != FTW_VALID_STATE) {\r
826 *Complete = TRUE;\r
827 return EFI_NOT_FOUND;\r
828 }\r
829 //\r
830 // If this record SpareComplete has not set, then it can not restart.\r
831 //\r
832 if (Record->SpareComplete != FTW_VALID_STATE) {\r
f0480ecf
LG
833 Status = GetPreviousRecordOfWrites (Header, &Record);\r
834 if (EFI_ERROR (Status)) {\r
85e923a5 835 FtwAbort (This);\r
85e923a5
LG
836 *Complete = TRUE;\r
837 return EFI_NOT_FOUND;\r
85e923a5
LG
838 }\r
839 }\r
f0480ecf 840\r
85e923a5
LG
841 //\r
842 // Fill all the requested values\r
843 //\r
844 CopyMem (CallerId, &Header->CallerId, sizeof (EFI_GUID));\r
845 *Lba = Record->Lba;\r
846 *Offset = Record->Offset;\r
847 *Length = Record->Length;\r
848 *Complete = (BOOLEAN) (Record->DestinationComplete == FTW_VALID_STATE);\r
849\r
850 if (*PrivateDataSize < Header->PrivateDataSize) {\r
851 *PrivateDataSize = Header->PrivateDataSize;\r
852 PrivateData = NULL;\r
853 Status = EFI_BUFFER_TOO_SMALL;\r
854 } else {\r
855 *PrivateDataSize = Header->PrivateDataSize;\r
856 CopyMem (PrivateData, Record + 1, *PrivateDataSize);\r
857 Status = EFI_SUCCESS;\r
858 }\r
859\r
860 DEBUG ((EFI_D_ERROR, "Ftw: GetLasetWrite() success\n"));\r
861\r
862 return Status;\r
863}\r
864\r
865/**\r
866 This function is the entry point of the Fault Tolerant Write driver.\r
867\r
868 @param ImageHandle A handle for the image that is initializing this driver\r
869 @param SystemTable A pointer to the EFI system table\r
870\r
871 @return EFI_SUCCESS FTW has finished the initialization\r
872 @retval EFI_NOT_FOUND Locate FVB protocol error\r
873 @retval EFI_OUT_OF_RESOURCES Allocate memory error\r
874 @retval EFI_VOLUME_CORRUPTED Firmware volume is error\r
875 @retval EFI_ABORTED FTW initialization error\r
876\r
877**/\r
878EFI_STATUS\r
879EFIAPI\r
880InitializeFaultTolerantWrite (\r
881 IN EFI_HANDLE ImageHandle,\r
882 IN EFI_SYSTEM_TABLE *SystemTable\r
883 )\r
884{\r
885 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;\r
886 UINTN Index;\r
887 EFI_HANDLE *HandleBuffer;\r
888 UINTN HandleCount;\r
889 EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;\r
890 EFI_PHYSICAL_ADDRESS BaseAddress;\r
891 EFI_FTW_DEVICE *FtwDevice;\r
892 EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader;\r
893 UINTN Length;\r
894 EFI_STATUS Status;\r
895 UINTN Offset;\r
896 EFI_FV_BLOCK_MAP_ENTRY *FvbMapEntry;\r
897 UINT32 LbaIndex;\r
898 EFI_HANDLE FvbHandle;\r
899\r
900 //\r
901 // Allocate Private data of this driver,\r
902 // INCLUDING THE FtwWorkSpace[FTW_WORK_SPACE_SIZE].\r
903 //\r
904 FvbHandle = NULL;\r
905 FtwDevice = NULL;\r
906 FtwDevice = AllocatePool (sizeof (EFI_FTW_DEVICE) + PcdGet32 (PcdFlashNvStorageFtwWorkingSize));\r
907 if (FtwDevice == NULL) {\r
908 return EFI_OUT_OF_RESOURCES;\r
909 }\r
910\r
911 ZeroMem (FtwDevice, sizeof (EFI_FTW_DEVICE));\r
912 FtwDevice->Signature = FTW_DEVICE_SIGNATURE;\r
913\r
914 //\r
915 // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.\r
916 //\r
917\r
918 FtwDevice->WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwWorkingBase);\r
919 FtwDevice->WorkSpaceLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwWorkingSize);\r
920\r
921 FtwDevice->SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwSpareBase);\r
922 FtwDevice->SpareAreaLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwSpareSize);\r
923\r
924 if ((FtwDevice->WorkSpaceLength == 0) || (FtwDevice->SpareAreaLength == 0)) {\r
925 DEBUG ((EFI_D_ERROR, "Ftw: Workspace or Spare block does not exist!\n"));\r
926 FreePool (FtwDevice);\r
927 return EFI_OUT_OF_RESOURCES;\r
928 }\r
929 //\r
930 // Locate FVB protocol by handle\r
931 //\r
932 Status = gBS->LocateHandleBuffer (\r
933 ByProtocol,\r
934 &gEfiFirmwareVolumeBlockProtocolGuid,\r
935 NULL,\r
936 &HandleCount,\r
937 &HandleBuffer\r
938 );\r
939 if (EFI_ERROR (Status)) {\r
940 FreePool (FtwDevice);\r
941 return EFI_NOT_FOUND;\r
942 }\r
943\r
944 if (HandleCount <= 0) {\r
945 FreePool (FtwDevice);\r
946 return EFI_NOT_FOUND;\r
947 }\r
948\r
949 Fvb = NULL;\r
950 FtwDevice->FtwFvBlock = NULL;\r
951 FtwDevice->FtwBackupFvb = NULL;\r
952 FtwDevice->FtwWorkSpaceLba = (EFI_LBA) (-1);\r
953 FtwDevice->FtwSpareLba = (EFI_LBA) (-1);\r
954 for (Index = 0; Index < HandleCount; Index += 1) {\r
955 Status = gBS->HandleProtocol (\r
956 HandleBuffer[Index],\r
957 &gEfiFirmwareVolumeBlockProtocolGuid,\r
958 (VOID **) &Fvb\r
959 );\r
960 if (EFI_ERROR (Status)) {\r
961 FreePool (FtwDevice);\r
962 return Status;\r
963 }\r
964\r
965 Status = Fvb->GetPhysicalAddress (Fvb, &BaseAddress);\r
966 if (EFI_ERROR (Status)) {\r
967 continue;\r
968 }\r
969\r
970 FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) BaseAddress);\r
971\r
972 if ((FtwDevice->WorkSpaceAddress >= BaseAddress) &&\r
973 ((FtwDevice->WorkSpaceAddress + FtwDevice->WorkSpaceLength) <= (BaseAddress + FwVolHeader->FvLength))\r
974 ) {\r
975 FtwDevice->FtwFvBlock = Fvb;\r
976 //\r
977 // To get the LBA of work space\r
978 //\r
979 if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) {\r
980 //\r
981 // Now, one FV has one type of BlockLength\r
982 //\r
983 FvbMapEntry = &FwVolHeader->BlockMap[0];\r
984 for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) {\r
985 if ((FtwDevice->WorkSpaceAddress >= (BaseAddress + FvbMapEntry->Length * (LbaIndex - 1)))\r
986 && (FtwDevice->WorkSpaceAddress < (BaseAddress + FvbMapEntry->Length * LbaIndex))) {\r
987 FtwDevice->FtwWorkSpaceLba = LbaIndex - 1;\r
988 //\r
989 // Get the Work space size and Base(Offset)\r
990 //\r
991 FtwDevice->FtwWorkSpaceSize = FtwDevice->WorkSpaceLength;\r
992 FtwDevice->FtwWorkSpaceBase = (UINTN) (FtwDevice->WorkSpaceAddress - (BaseAddress + FvbMapEntry->Length * (LbaIndex - 1)));\r
993 break;\r
994 }\r
995 }\r
996 }\r
997 }\r
998\r
999 if ((FtwDevice->SpareAreaAddress >= BaseAddress) &&\r
1000 ((FtwDevice->SpareAreaAddress + FtwDevice->SpareAreaLength) <= (BaseAddress + FwVolHeader->FvLength))\r
1001 ) {\r
1002 FtwDevice->FtwBackupFvb = Fvb;\r
1003 //\r
1004 // To get the LBA of spare\r
1005 //\r
1006 if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) {\r
1007 //\r
1008 // Now, one FV has one type of BlockLength\r
1009 //\r
1010 FvbMapEntry = &FwVolHeader->BlockMap[0];\r
1011 for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) {\r
1012 if ((FtwDevice->SpareAreaAddress >= (BaseAddress + FvbMapEntry->Length * (LbaIndex - 1)))\r
1013 && (FtwDevice->SpareAreaAddress < (BaseAddress + FvbMapEntry->Length * LbaIndex))) {\r
1014 //\r
1015 // Get the NumberOfSpareBlock and BlockSize\r
1016 //\r
1017 FtwDevice->FtwSpareLba = LbaIndex - 1;\r
1018 FtwDevice->BlockSize = FvbMapEntry->Length;\r
1019 FtwDevice->NumberOfSpareBlock = FtwDevice->SpareAreaLength / FtwDevice->BlockSize;\r
1020 //\r
1021 // Check the range of spare area to make sure that it's in FV range\r
1022 //\r
1023 if ((FtwDevice->FtwSpareLba + FtwDevice->NumberOfSpareBlock) > FvbMapEntry->NumBlocks) {\r
1024 DEBUG ((EFI_D_ERROR, "Ftw: Spare area is out of FV range\n"));\r
1025 FreePool (FtwDevice);\r
1026 return EFI_ABORTED;\r
1027 }\r
1028 break;\r
1029 }\r
1030 }\r
1031 }\r
1032 }\r
1033 }\r
1034\r
1035 //\r
1036 // Calculate the start LBA of working block. Working block is an area which\r
1037 // contains working space in its last block and has the same size as spare\r
1038 // block, unless there are not enough blocks before the block that contains\r
1039 // working space.\r
1040 //\r
1041 FtwDevice->FtwWorkBlockLba = FtwDevice->FtwWorkSpaceLba - FtwDevice->NumberOfSpareBlock + 1;\r
1042 if ((INT64) (FtwDevice->FtwWorkBlockLba) < 0) {\r
1043 DEBUG ((EFI_D_ERROR, "Ftw: The spare block range is too large than the working block range!\n"));\r
1044 FreePool (FtwDevice);\r
1045 return EFI_ABORTED;\r
1046 }\r
1047\r
1048 if ((FtwDevice->FtwFvBlock == NULL) ||\r
1049 (FtwDevice->FtwBackupFvb == NULL) ||\r
1050 (FtwDevice->FtwWorkSpaceLba == (EFI_LBA) (-1)) ||\r
1051 (FtwDevice->FtwSpareLba == (EFI_LBA) (-1))\r
1052 ) {\r
1053 DEBUG ((EFI_D_ERROR, "Ftw: Working or spare FVB not ready\n"));\r
1054 FreePool (FtwDevice);\r
1055 return EFI_ABORTED;\r
1056 }\r
1057 //\r
1058 // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.\r
1059 //\r
1060 FtwDevice->FtwWorkSpace = (UINT8 *) (FtwDevice + 1);\r
1061 FtwDevice->FtwWorkSpaceHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) FtwDevice->FtwWorkSpace;\r
1062\r
1063 FtwDevice->FtwLastWriteHeader = NULL;\r
1064 FtwDevice->FtwLastWriteRecord = NULL;\r
1065\r
1066 //\r
1067 // Refresh the working space data from working block\r
1068 //\r
1069 Status = WorkSpaceRefresh (FtwDevice);\r
1070 if (EFI_ERROR (Status)) {\r
1071 goto Recovery;\r
1072 }\r
1073 //\r
1074 // If the working block workspace is not valid, try the spare block\r
1075 //\r
1076 if (!IsValidWorkSpace (FtwDevice->FtwWorkSpaceHeader)) {\r
1077 //\r
1078 // Read from spare block\r
1079 //\r
1080 Length = FtwDevice->FtwWorkSpaceSize;\r
1081 Status = FtwDevice->FtwBackupFvb->Read (\r
1082 FtwDevice->FtwBackupFvb,\r
1083 FtwDevice->FtwSpareLba,\r
1084 FtwDevice->FtwWorkSpaceBase,\r
1085 &Length,\r
1086 FtwDevice->FtwWorkSpace\r
1087 );\r
1088 if (EFI_ERROR (Status)) {\r
1089 goto Recovery;\r
1090 }\r
1091 //\r
1092 // If spare block is valid, then replace working block content.\r
1093 //\r
1094 if (IsValidWorkSpace (FtwDevice->FtwWorkSpaceHeader)) {\r
1095 Status = FlushSpareBlockToWorkingBlock (FtwDevice);\r
1096 DEBUG ((EFI_D_ERROR, "Ftw: Restart working block update in Init() - %r\n", Status));\r
1097 FtwAbort (&FtwDevice->FtwInstance);\r
1098 //\r
1099 // Refresh work space.\r
1100 //\r
1101 Status = WorkSpaceRefresh (FtwDevice);\r
1102 if (EFI_ERROR (Status)) {\r
1103 goto Recovery;\r
1104 }\r
1105 } else {\r
1106 DEBUG ((EFI_D_ERROR, "Ftw: Both are invalid, init workspace\n"));\r
1107 //\r
1108 // If both are invalid, then initialize work space.\r
1109 //\r
1110 SetMem (\r
1111 FtwDevice->FtwWorkSpace,\r
1112 FtwDevice->FtwWorkSpaceSize,\r
1113 FTW_ERASED_BYTE\r
1114 );\r
1115 InitWorkSpaceHeader (FtwDevice->FtwWorkSpaceHeader);\r
1116 //\r
1117 // Initialize the work space\r
1118 //\r
1119 Status = FtwReclaimWorkSpace (FtwDevice, FALSE);\r
1120 if (EFI_ERROR (Status)) {\r
1121 goto Recovery;\r
1122 }\r
1123 }\r
1124 }\r
1125\r
1126 //\r
1127 // If the FtwDevice->FtwLastWriteRecord is 1st record of write header &&\r
1128 // (! SpareComplete) THEN call Abort().\r
1129 //\r
1130 if ((FtwDevice->FtwLastWriteHeader->HeaderAllocated == FTW_VALID_STATE) &&\r
1131 (FtwDevice->FtwLastWriteRecord->SpareComplete != FTW_VALID_STATE) &&\r
1132 IsFirstRecordOfWrites (FtwDevice->FtwLastWriteHeader, FtwDevice->FtwLastWriteRecord)\r
1133 ) {\r
1134 DEBUG ((EFI_D_ERROR, "Ftw: Init.. find first record not SpareCompleted, abort()\n"));\r
1135 FtwAbort (&FtwDevice->FtwInstance);\r
1136 }\r
1137 //\r
1138 // If Header is incompleted and the last record has completed, then\r
1139 // call Abort() to set the Header->Complete FLAG.\r
1140 //\r
1141 if ((FtwDevice->FtwLastWriteHeader->Complete != FTW_VALID_STATE) &&\r
1142 (FtwDevice->FtwLastWriteRecord->DestinationComplete == FTW_VALID_STATE) &&\r
1143 IsLastRecordOfWrites (FtwDevice->FtwLastWriteHeader, FtwDevice->FtwLastWriteRecord)\r
1144 ) {\r
1145 DEBUG ((EFI_D_ERROR, "Ftw: Init.. find last record completed but header not, abort()\n"));\r
1146 FtwAbort (&FtwDevice->FtwInstance);\r
1147 }\r
1148 //\r
1149 // To check the workspace buffer following last Write header/records is EMPTY or not.\r
1150 // If it's not EMPTY, FTW also need to call reclaim().\r
1151 //\r
1152 FtwHeader = FtwDevice->FtwLastWriteHeader;\r
1153 Offset = (UINT8 *) FtwHeader - FtwDevice->FtwWorkSpace;\r
1154 if (FtwDevice->FtwWorkSpace[Offset] != FTW_ERASED_BYTE) {\r
1155 Offset += WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites, FtwHeader->PrivateDataSize);\r
1156 }\r
1157\r
1158 if (!IsErasedFlashBuffer (\r
1159 FtwDevice->FtwWorkSpace + Offset,\r
1160 FtwDevice->FtwWorkSpaceSize - Offset\r
1161 )) {\r
1162 Status = FtwReclaimWorkSpace (FtwDevice, TRUE);\r
1163 if (EFI_ERROR (Status)) {\r
1164 goto Recovery;\r
1165 }\r
1166 }\r
1167 //\r
1168 // Restart if it's boot block\r
1169 //\r
1170 if ((FtwDevice->FtwLastWriteHeader->Complete != FTW_VALID_STATE) &&\r
1171 (FtwDevice->FtwLastWriteRecord->SpareComplete == FTW_VALID_STATE)\r
1172 ) {\r
1173 if (FtwDevice->FtwLastWriteRecord->BootBlockUpdate == FTW_VALID_STATE) {\r
1174 Status = FlushSpareBlockToBootBlock (FtwDevice);\r
1175 DEBUG ((EFI_D_ERROR, "Ftw: Restart boot block update - %r\n", Status));\r
1176 if (EFI_ERROR (Status)) {\r
1177 goto Recovery;\r
1178 }\r
1179 \r
1180 FtwAbort (&FtwDevice->FtwInstance);\r
1181 } else {\r
1182 //\r
1183 // if (SpareCompleted) THEN Restart to fault tolerant write.\r
1184 //\r
1185 FvbHandle = GetFvbByAddress (FtwDevice->FtwLastWriteRecord->FvBaseAddress, &Fvb);\r
1186 if (FvbHandle != NULL) {\r
1187 Status = FtwRestart (&FtwDevice->FtwInstance, FvbHandle);\r
1188 DEBUG ((EFI_D_ERROR, "FtwLite: Restart last write - %r\n", Status));\r
1189 if (EFI_ERROR (Status)) {\r
1190 goto Recovery;\r
1191 }\r
1192 }\r
1193 FtwAbort (&FtwDevice->FtwInstance);\r
1194 }\r
1195 }\r
1196\r
1197 //\r
1198 // Hook the protocol API\r
1199 //\r
1200 FtwDevice->FtwInstance.GetMaxBlockSize = FtwGetMaxBlockSize;\r
1201 FtwDevice->FtwInstance.Allocate = FtwAllocate;\r
1202 FtwDevice->FtwInstance.Write = FtwWrite;\r
1203 FtwDevice->FtwInstance.Restart = FtwRestart;\r
1204 FtwDevice->FtwInstance.Abort = FtwAbort;\r
1205 FtwDevice->FtwInstance.GetLastWrite = FtwGetLastWrite;\r
1206\r
1207 //\r
1208 // Install protocol interface\r
1209 //\r
1210 Status = gBS->InstallProtocolInterface (\r
1211 &FtwDevice->Handle,\r
1212 &gEfiFaultTolerantWriteProtocolGuid,\r
1213 EFI_NATIVE_INTERFACE,\r
1214 &FtwDevice->FtwInstance\r
1215 );\r
1216 if (EFI_ERROR (Status)) {\r
1217 goto Recovery;\r
1218 }\r
1219\r
1220 return EFI_SUCCESS;\r
1221\r
1222Recovery:\r
1223\r
1224 if (FtwDevice != NULL) {\r
1225 FreePool (FtwDevice);\r
1226 }\r
1227\r
1228 DEBUG ((EFI_D_ERROR, "Ftw: Severe Error occurs, need to recovery\n"));\r
1229\r
1230 return EFI_VOLUME_CORRUPTED;\r
1231}\r