]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/FirmwareVolume/FaultTolerantWriteDxe/FtwLite.c
1. code scrub for ConSplitterDxe.
[mirror_edk2.git] / MdeModulePkg / Universal / FirmwareVolume / FaultTolerantWriteDxe / FtwLite.c
CommitLineData
6cc9ca32 1/** @file\r
d7dec593 2\r
6cc9ca32 3 This is a simple fault tolerant write driver.\r
d7dec593 4 And it only supports write BufferSize <= SpareAreaLength.\r
5\r
6 This boot service only protocol provides fault tolerant write capability for \r
7 block devices. The protocol has internal non-volatile intermediate storage \r
8 of the data and private information. It should be able to recover \r
9 automatically from a critical fault, such as power failure. \r
10\r
d7dec593 11 The implementation uses an FTW Lite (Fault Tolerant Write) Work Space. \r
da770255 12 This work space is a memory copy of the work space on the Working Block,\r
d7dec593 13 the size of the work space is the FTW_WORK_SPACE_SIZE bytes.\r
14\r
6cc9ca32
LG
15Copyright (c) 2006 - 2008, Intel Corporation \r
16All rights reserved. This program and the accompanying materials \r
17are licensed and made available under the terms and conditions of the BSD License \r
18which accompanies this distribution. The full text of the license may be found at \r
19http://opensource.org/licenses/bsd-license.php \r
20 \r
21THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
22WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
23\r
24**/\r
d7dec593 25\r
e87a334f 26#include "FtwLite.h"\r
d7dec593 27\r
6aab8214 28/**\r
29 Starts a target block update. This function will record data about write\r
30 in fault tolerant storage and will complete the write in a recoverable\r
31 manner, ensuring at all times that either the original contents or\r
da770255 32 the modified contents are available. We should check the target\r
33 range to prevent the user from writing Spare block and Working \r
34 space directly.\r
6aab8214 35\r
36 @param This Calling context\r
37 @param FvbHandle The handle of FVB protocol that provides services for\r
38 reading, writing, and erasing the target block.\r
39 @param Lba The logical block address of the target block.\r
40 @param Offset The offset within the target block to place the data.\r
41 @param NumBytes The number of bytes to write to the target block.\r
42 @param Buffer The data to write.\r
43\r
44 @retval EFI_SUCCESS The function completed successfully\r
45 @retval EFI_BAD_BUFFER_SIZE The write would span a target block, which is not\r
46 a valid action.\r
47 @retval EFI_ACCESS_DENIED No writes have been allocated.\r
48 @retval EFI_NOT_FOUND Cannot find FVB by handle.\r
49 @retval EFI_OUT_OF_RESOURCES Cannot allocate memory.\r
50 @retval EFI_ABORTED The function could not complete successfully.\r
51\r
52**/\r
d7dec593 53EFI_STATUS\r
54EFIAPI\r
55FtwLiteWrite (\r
56 IN EFI_FTW_LITE_PROTOCOL *This,\r
57 IN EFI_HANDLE FvbHandle,\r
58 IN EFI_LBA Lba,\r
59 IN UINTN Offset,\r
60 IN OUT UINTN *NumBytes,\r
61 IN VOID *Buffer\r
62 )\r
d7dec593 63{\r
64 EFI_STATUS Status;\r
65 EFI_FTW_LITE_DEVICE *FtwLiteDevice;\r
66 EFI_FTW_LITE_RECORD *Record;\r
67 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;\r
68 EFI_PHYSICAL_ADDRESS FvbPhysicalAddress;\r
69 UINTN MyLength;\r
70 UINTN MyOffset;\r
71 UINTN MyBufferSize;\r
72 UINT8 *MyBuffer;\r
73 UINTN SpareBufferSize;\r
74 UINT8 *SpareBuffer;\r
75 UINTN Index;\r
76 UINT8 *Ptr;\r
77 EFI_DEV_PATH_PTR DevPtr;\r
78\r
79 //\r
80 // Refresh work space and get last record\r
81 //\r
82 FtwLiteDevice = FTW_LITE_CONTEXT_FROM_THIS (This);\r
83 Status = WorkSpaceRefresh (FtwLiteDevice);\r
84 if (EFI_ERROR (Status)) {\r
85 return EFI_ABORTED;\r
86 }\r
87\r
88 Record = FtwLiteDevice->FtwLastRecord;\r
89\r
90 //\r
91 // Check the flags of last write record\r
92 //\r
93 if ((Record->WriteAllocated == FTW_VALID_STATE) || (Record->SpareCompleted == FTW_VALID_STATE)) {\r
94 return EFI_ACCESS_DENIED;\r
95 }\r
96 //\r
97 // IF former record has completed, THEN use next record\r
98 //\r
99 if (Record->WriteCompleted == FTW_VALID_STATE) {\r
100 Record++;\r
101 FtwLiteDevice->FtwLastRecord = Record;\r
102 }\r
103\r
104 MyOffset = (UINT8 *) Record - FtwLiteDevice->FtwWorkSpace;\r
105\r
106 //\r
107 // Check if the input data can fit within the target block\r
108 //\r
109 if ((Offset +*NumBytes) > FtwLiteDevice->SpareAreaLength) {\r
110 return EFI_BAD_BUFFER_SIZE;\r
111 }\r
112 //\r
113 // Check if there is enough free space for allocate a record\r
114 //\r
115 if ((MyOffset + WRITE_TOTAL_SIZE) > FtwLiteDevice->FtwWorkSpaceSize) {\r
e11ae3a5 116 Status = FtwReclaimWorkSpace (FtwLiteDevice, TRUE);\r
d7dec593 117 if (EFI_ERROR (Status)) {\r
118 DEBUG ((EFI_D_ERROR, "FtwLite: Reclaim work space - %r", Status));\r
119 return EFI_ABORTED;\r
120 }\r
121 }\r
122 //\r
123 // Get the FVB protocol by handle\r
124 //\r
125 Status = FtwGetFvbByHandle (FvbHandle, &Fvb);\r
126 if (EFI_ERROR (Status)) {\r
127 return EFI_NOT_FOUND;\r
128 }\r
129 //\r
130 // Allocate a write record in workspace.\r
131 // Update Header->WriteAllocated as VALID\r
132 //\r
133 Status = FtwUpdateFvState (\r
134 FtwLiteDevice->FtwFvBlock,\r
135 FtwLiteDevice->FtwWorkSpaceLba,\r
136 FtwLiteDevice->FtwWorkSpaceBase + MyOffset,\r
137 WRITE_ALLOCATED\r
138 );\r
139\r
140 if (EFI_ERROR (Status)) {\r
141 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Allocate record - %r\n", Status));\r
142 return EFI_ABORTED;\r
143 }\r
144\r
145 Record->WriteAllocated = FTW_VALID_STATE;\r
146\r
147 //\r
148 // Prepare data of write record, filling DevPath with memory mapped address.\r
149 //\r
150 DevPtr.MemMap = (MEMMAP_DEVICE_PATH *) &Record->DevPath;\r
151 DevPtr.MemMap->Header.Type = HARDWARE_DEVICE_PATH;\r
152 DevPtr.MemMap->Header.SubType = HW_MEMMAP_DP;\r
153 SetDevicePathNodeLength (&DevPtr.MemMap->Header, sizeof (MEMMAP_DEVICE_PATH));\r
154\r
155 Status = Fvb->GetPhysicalAddress (Fvb, &FvbPhysicalAddress);\r
156 if (EFI_ERROR (Status)) {\r
157 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Get FVB physical address - %r\n", Status));\r
158 return EFI_ABORTED;\r
159 }\r
160\r
161 DevPtr.MemMap->MemoryType = EfiMemoryMappedIO;\r
162 DevPtr.MemMap->StartingAddress = FvbPhysicalAddress;\r
163 DevPtr.MemMap->EndingAddress = FvbPhysicalAddress +*NumBytes;\r
164 //\r
165 // ignored!\r
166 //\r
167 Record->Lba = Lba;\r
168 Record->Offset = Offset;\r
169 Record->NumBytes = *NumBytes;\r
170\r
171 //\r
172 // Write the record to the work space.\r
173 //\r
174 MyOffset = (UINT8 *) Record - FtwLiteDevice->FtwWorkSpace;\r
175 MyLength = FTW_LITE_RECORD_SIZE;\r
176\r
177 Status = FtwLiteDevice->FtwFvBlock->Write (\r
178 FtwLiteDevice->FtwFvBlock,\r
179 FtwLiteDevice->FtwWorkSpaceLba,\r
180 FtwLiteDevice->FtwWorkSpaceBase + MyOffset,\r
181 &MyLength,\r
182 (UINT8 *) Record\r
183 );\r
184 if (EFI_ERROR (Status)) {\r
185 return EFI_ABORTED;\r
186 }\r
187 //\r
188 // Record has been written to working block, then write data.\r
189 //\r
190 //\r
191 // Allocate a memory buffer\r
192 //\r
193 MyBufferSize = FtwLiteDevice->SpareAreaLength;\r
194 MyBuffer = AllocatePool (MyBufferSize);\r
195 if (MyBuffer == NULL) {\r
196 return EFI_OUT_OF_RESOURCES;\r
197 }\r
198 //\r
199 // Starting at Lba, if the number of the rest blocks on Fvb is less\r
200 // than NumberOfSpareBlock.\r
201 //\r
202 //\r
203 // Read all original data from target block to memory buffer\r
204 //\r
205 if (IsInWorkingBlock (FtwLiteDevice, Fvb, Lba)) {\r
206 //\r
207 // If target block falls into working block, we must follow the process of\r
208 // updating working block.\r
209 //\r
210 Ptr = MyBuffer;\r
211 for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {\r
212 MyLength = FtwLiteDevice->SizeOfSpareBlock;\r
213 Status = FtwLiteDevice->FtwFvBlock->Read (\r
214 FtwLiteDevice->FtwFvBlock,\r
215 FtwLiteDevice->FtwWorkBlockLba + Index,\r
216 0,\r
217 &MyLength,\r
218 Ptr\r
219 );\r
220 if (EFI_ERROR (Status)) {\r
221 FreePool (MyBuffer);\r
222 return EFI_ABORTED;\r
223 }\r
224\r
225 Ptr += MyLength;\r
226 }\r
227 //\r
228 // Update Offset by adding the offset from the start LBA of working block to\r
229 // the target LBA. The target block can not span working block!\r
230 //\r
231 Offset = (((UINTN) (Lba - FtwLiteDevice->FtwWorkBlockLba)) * FtwLiteDevice->SizeOfSpareBlock + Offset);\r
232 ASSERT ((Offset +*NumBytes) <= FtwLiteDevice->SpareAreaLength);\r
233\r
234 } else {\r
235\r
236 Ptr = MyBuffer;\r
237 for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {\r
238 MyLength = FtwLiteDevice->SizeOfSpareBlock;\r
239 Status = Fvb->Read (Fvb, Lba + Index, 0, &MyLength, Ptr);\r
240 if (EFI_ERROR (Status)) {\r
241 FreePool (MyBuffer);\r
242 return EFI_ABORTED;\r
243 }\r
244\r
245 Ptr += MyLength;\r
246 }\r
247 }\r
248 //\r
249 // Overwrite the updating range data with\r
250 // the input buffer content\r
251 //\r
252 CopyMem (MyBuffer + Offset, Buffer, *NumBytes);\r
253\r
254 //\r
255 // Try to keep the content of spare block\r
256 // Save spare block into a spare backup memory buffer (Sparebuffer)\r
257 //\r
258 SpareBufferSize = FtwLiteDevice->SpareAreaLength;\r
259 SpareBuffer = AllocatePool (SpareBufferSize);\r
260 if (SpareBuffer == NULL) {\r
261 FreePool (MyBuffer);\r
262 return EFI_OUT_OF_RESOURCES;\r
263 }\r
264\r
265 Ptr = SpareBuffer;\r
266 for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {\r
267 MyLength = FtwLiteDevice->SizeOfSpareBlock;\r
268 Status = FtwLiteDevice->FtwBackupFvb->Read (\r
269 FtwLiteDevice->FtwBackupFvb,\r
270 FtwLiteDevice->FtwSpareLba + Index,\r
271 0,\r
272 &MyLength,\r
273 Ptr\r
274 );\r
275 if (EFI_ERROR (Status)) {\r
276 FreePool (MyBuffer);\r
277 FreePool (SpareBuffer);\r
278 return EFI_ABORTED;\r
279 }\r
280\r
281 Ptr += MyLength;\r
282 }\r
283 //\r
284 // Write the memory buffer to spare block\r
285 // Don't forget to erase Flash first.\r
286 //\r
287 Status = FtwEraseSpareBlock (FtwLiteDevice);\r
288 Ptr = MyBuffer;\r
289 for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {\r
290 MyLength = FtwLiteDevice->SizeOfSpareBlock;\r
291 Status = FtwLiteDevice->FtwBackupFvb->Write (\r
292 FtwLiteDevice->FtwBackupFvb,\r
293 FtwLiteDevice->FtwSpareLba + Index,\r
294 0,\r
295 &MyLength,\r
296 Ptr\r
297 );\r
298 if (EFI_ERROR (Status)) {\r
299 FreePool (MyBuffer);\r
300 FreePool (SpareBuffer);\r
301 return EFI_ABORTED;\r
302 }\r
303\r
304 Ptr += MyLength;\r
305 }\r
306 //\r
307 // Free MyBuffer\r
308 //\r
309 FreePool (MyBuffer);\r
310\r
311 //\r
312 // Set the SpareCompleteD in the FTW record,\r
313 //\r
314 MyOffset = (UINT8 *) Record - FtwLiteDevice->FtwWorkSpace;\r
315 Status = FtwUpdateFvState (\r
316 FtwLiteDevice->FtwFvBlock,\r
317 FtwLiteDevice->FtwWorkSpaceLba,\r
318 FtwLiteDevice->FtwWorkSpaceBase + MyOffset,\r
319 SPARE_COMPLETED\r
320 );\r
321 if (EFI_ERROR (Status)) {\r
322 FreePool (SpareBuffer);\r
323 return EFI_ABORTED;\r
324 }\r
325\r
326 Record->SpareCompleted = FTW_VALID_STATE;\r
327\r
328 //\r
329 // Since the content has already backuped in spare block, the write is\r
330 // guaranteed to be completed with fault tolerant manner.\r
331 //\r
332 Status = FtwWriteRecord (FtwLiteDevice, Fvb);\r
333 if (EFI_ERROR (Status)) {\r
334 FreePool (SpareBuffer);\r
335 return EFI_ABORTED;\r
336 }\r
337\r
338 Record++;\r
339 FtwLiteDevice->FtwLastRecord = Record;\r
340\r
341 //\r
342 // Restore spare backup buffer into spare block , if no failure happened during FtwWrite.\r
343 //\r
344 Status = FtwEraseSpareBlock (FtwLiteDevice);\r
345 Ptr = SpareBuffer;\r
346 for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {\r
347 MyLength = FtwLiteDevice->SizeOfSpareBlock;\r
348 Status = FtwLiteDevice->FtwBackupFvb->Write (\r
349 FtwLiteDevice->FtwBackupFvb,\r
350 FtwLiteDevice->FtwSpareLba + Index,\r
351 0,\r
352 &MyLength,\r
353 Ptr\r
354 );\r
355 if (EFI_ERROR (Status)) {\r
356 FreePool (SpareBuffer);\r
357 return EFI_ABORTED;\r
358 }\r
359\r
360 Ptr += MyLength;\r
361 }\r
362 //\r
363 // All success.\r
364 //\r
365 FreePool (SpareBuffer);\r
366\r
367 DEBUG (\r
368 (EFI_D_FTW_LITE,\r
369 "FtwLite: Write() success, (Lba:Offset)=(%lx:0x%x), NumBytes: 0x%x\n",\r
370 Lba,\r
371 Offset,\r
372 *NumBytes)\r
373 );\r
374\r
375 return EFI_SUCCESS;\r
376}\r
377\r
378\r
6aab8214 379/**\r
da770255 380 Write a record with fault tolerant manner.\r
6aab8214 381 Since the content has already backuped in spare block, the write is\r
382 guaranteed to be completed with fault tolerant manner.\r
383\r
384\r
385 @param FtwLiteDevice The private data of FTW_LITE driver\r
386 @param Fvb The FVB protocol that provides services for\r
387 reading, writing, and erasing the target block.\r
388\r
389 @retval EFI_SUCCESS The function completed successfully\r
390 @retval EFI_ABORTED The function could not complete successfully\r
391\r
392**/\r
d7dec593 393EFI_STATUS\r
394FtwWriteRecord (\r
395 IN EFI_FTW_LITE_DEVICE *FtwLiteDevice,\r
396 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb\r
397 )\r
d7dec593 398{\r
399 EFI_STATUS Status;\r
400 EFI_FTW_LITE_RECORD *Record;\r
401 EFI_LBA WorkSpaceLbaOffset; \r
402 UINTN Offset;\r
403\r
404 //\r
405 // Spare Complete but Destination not complete,\r
406 // Recover the targt block with the spare block.\r
407 //\r
408 Record = FtwLiteDevice->FtwLastRecord;\r
409\r
410 //\r
411 // IF target block is working block, THEN Flush Spare Block To Working Block;\r
412 // ELSE IF target block is boot block, THEN Flush Spare Block To boot Block;\r
413 // ELSE flush spare block to normal target block.ENDIF\r
414 //\r
415 if (IsInWorkingBlock (FtwLiteDevice, Fvb, Record->Lba)) {\r
416 //\r
417 // If target block is working block, Attention:\r
418 // it's required to set SPARE_COMPLETED to spare block.\r
419 //\r
420 WorkSpaceLbaOffset = FtwLiteDevice->FtwWorkSpaceLba - FtwLiteDevice->FtwWorkBlockLba;\r
421 Offset = (UINT8 *) Record - FtwLiteDevice->FtwWorkSpace;\r
422 Status = FtwUpdateFvState (\r
423 FtwLiteDevice->FtwBackupFvb,\r
424 FtwLiteDevice->FtwSpareLba + WorkSpaceLbaOffset,\r
425 FtwLiteDevice->FtwWorkSpaceBase + Offset,\r
426 SPARE_COMPLETED\r
427 );\r
428 ASSERT_EFI_ERROR (Status);\r
429\r
430 Status = FlushSpareBlockToWorkingBlock (FtwLiteDevice);\r
431 } else if (IsBootBlock (FtwLiteDevice, Fvb, Record->Lba)) {\r
432 //\r
433 // Update boot block\r
434 //\r
435 Status = FlushSpareBlockToBootBlock (FtwLiteDevice);\r
436 } else {\r
437 //\r
438 // Update blocks other than working block or boot block\r
439 //\r
440 Status = FlushSpareBlockToTargetBlock (FtwLiteDevice, Fvb, Record->Lba);\r
441 }\r
442\r
443 ASSERT_EFI_ERROR (Status);\r
444\r
445 //\r
446 // Set WriteCompleted flag in record\r
447 //\r
448 Offset = (UINT8 *) Record - FtwLiteDevice->FtwWorkSpace;\r
449 Status = FtwUpdateFvState (\r
450 FtwLiteDevice->FtwFvBlock,\r
451 FtwLiteDevice->FtwWorkSpaceLba,\r
452 FtwLiteDevice->FtwWorkSpaceBase + Offset,\r
453 WRITE_COMPLETED\r
454 );\r
455 ASSERT_EFI_ERROR (Status);\r
456\r
457 Record->WriteCompleted = FTW_VALID_STATE;\r
458 return EFI_SUCCESS;\r
459}\r
460\r
461\r
6aab8214 462/**\r
463 Restarts a previously interrupted write. The caller must provide the\r
464 block protocol needed to complete the interrupted write.\r
465\r
466\r
467 @param FtwLiteDevice The private data of FTW_LITE driver\r
468 FvbHandle - The handle of FVB protocol that provides services for\r
469 reading, writing, and erasing the target block.\r
470\r
471 @retval EFI_SUCCESS The function completed successfully\r
472 @retval EFI_ACCESS_DENIED No pending writes exist\r
473 @retval EFI_NOT_FOUND FVB protocol not found by the handle\r
474 @retval EFI_ABORTED The function could not complete successfully\r
475\r
476**/\r
d7dec593 477EFI_STATUS\r
478FtwRestart (\r
479 IN EFI_FTW_LITE_DEVICE *FtwLiteDevice\r
480 )\r
d7dec593 481{\r
482 EFI_STATUS Status;\r
483 EFI_FTW_LITE_RECORD *Record;\r
484 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;\r
485 EFI_DEV_PATH_PTR DevPathPtr;\r
486\r
487 //\r
488 // Spare Completed but Destination not complete,\r
489 // Recover the targt block with the spare block.\r
490 //\r
491 Record = FtwLiteDevice->FtwLastRecord;\r
492\r
493 //\r
494 // Only support memory mapped FVB device path by now.\r
495 //\r
496 DevPathPtr.MemMap = (MEMMAP_DEVICE_PATH *) &Record->DevPath;\r
497 if (!((DevPathPtr.MemMap->Header.Type == HARDWARE_DEVICE_PATH) && (DevPathPtr.MemMap->Header.SubType == HW_MEMMAP_DP))\r
498 ) {\r
499 DEBUG ((EFI_D_FTW_LITE, "FtwLite: FVB Device Path is not memory mapped\n"));\r
500 return EFI_ABORTED;\r
501 }\r
502\r
503 Status = GetFvbByAddress (DevPathPtr.MemMap->StartingAddress, &Fvb);\r
504 if (EFI_ERROR (Status)) {\r
505 return EFI_NOT_FOUND;\r
506 }\r
507 //\r
508 // Since the content has already backuped in spare block, the write is\r
509 // guaranteed to be completed with fault tolerant manner.\r
510 //\r
511 Status = FtwWriteRecord (FtwLiteDevice, Fvb);\r
512 DEBUG ((EFI_D_FTW_INFO, "FtwLite: Restart() - %r\n", Status));\r
513\r
514 Record++;\r
515 FtwLiteDevice->FtwLastRecord = Record;\r
516\r
517 //\r
518 // Erase Spare block\r
519 // This is restart, no need to keep spareblock content.\r
520 //\r
521 FtwEraseSpareBlock (FtwLiteDevice);\r
522\r
523 return Status;\r
524}\r
525\r
526\r
6aab8214 527/**\r
528 Aborts all previous allocated writes.\r
d7dec593 529\r
d7dec593 530\r
6aab8214 531 @param FtwLiteDevice The private data of FTW_LITE driver\r
d7dec593 532\r
6aab8214 533 @retval EFI_SUCCESS The function completed successfully\r
534 @retval EFI_ABORTED The function could not complete successfully.\r
535 @retval EFI_NOT_FOUND No allocated writes exist.\r
d7dec593 536\r
6aab8214 537**/\r
538EFI_STATUS\r
539FtwAbort (\r
540 IN EFI_FTW_LITE_DEVICE *FtwLiteDevice\r
541 )\r
d7dec593 542{\r
543 EFI_STATUS Status;\r
544 UINTN Offset;\r
545\r
546 if (FtwLiteDevice->FtwLastRecord->WriteCompleted == FTW_VALID_STATE) {\r
547 return EFI_NOT_FOUND;\r
548 }\r
549 //\r
550 // Update the complete state of the header as VALID and abort.\r
551 //\r
552 Offset = (UINT8 *) FtwLiteDevice->FtwLastRecord - FtwLiteDevice->FtwWorkSpace;\r
553 Status = FtwUpdateFvState (\r
554 FtwLiteDevice->FtwFvBlock,\r
555 FtwLiteDevice->FtwWorkSpaceLba,\r
556 FtwLiteDevice->FtwWorkSpaceBase + Offset,\r
557 WRITE_COMPLETED\r
558 );\r
559 if (EFI_ERROR (Status)) {\r
560 return EFI_ABORTED;\r
561 }\r
562\r
563 FtwLiteDevice->FtwLastRecord->WriteCompleted = FTW_VALID_STATE;\r
564\r
565 Status = FtwGetLastRecord (FtwLiteDevice, &FtwLiteDevice->FtwLastRecord);\r
566\r
567 //\r
568 // Erase the spare block\r
569 //\r
570 Status = FtwEraseSpareBlock (FtwLiteDevice);\r
571\r
572 DEBUG ((EFI_D_FTW_INFO, "FtwLite: Abort() success \n"));\r
573 return EFI_SUCCESS;\r
574}\r
575\r
6aab8214 576/**\r
577 This function is the entry point of the Fault Tolerant Write driver.\r
578\r
da770255 579 @param ImageHandle A handle for the image that is initializing this driver\r
580 @param SystemTable A pointer to the EFI system table\r
6aab8214 581\r
582 @retval EFI_SUCCESS FTW has finished the initialization\r
583 @retval EFI_ABORTED FTW initialization error\r
584\r
585**/\r
d7dec593 586EFI_STATUS\r
587EFIAPI\r
588InitializeFtwLite (\r
589 IN EFI_HANDLE ImageHandle,\r
590 IN EFI_SYSTEM_TABLE *SystemTable\r
591 )\r
d7dec593 592{\r
593 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;\r
594 UINTN Index;\r
595 EFI_HANDLE *HandleBuffer;\r
596 UINTN HandleCount;\r
597 EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;\r
598 EFI_PHYSICAL_ADDRESS BaseAddress;\r
599 EFI_FTW_LITE_DEVICE *FtwLiteDevice;\r
600 EFI_FTW_LITE_RECORD *Record;\r
601 UINTN Length;\r
602 EFI_STATUS Status;\r
603 UINTN Offset;\r
604 EFI_FV_BLOCK_MAP_ENTRY *FvbMapEntry;\r
605 UINT32 LbaIndex;\r
276be2de 606\r
d7dec593 607 //\r
da770255 608 // Allocate Private data of this driver, including the FtwWorkSpace[FTW_WORK_SPACE_SIZE].\r
d7dec593 609 //\r
610 FtwLiteDevice = NULL;\r
611 FtwLiteDevice = AllocatePool (sizeof (EFI_FTW_LITE_DEVICE) + FTW_WORK_SPACE_SIZE);\r
612 if (FtwLiteDevice != NULL) {\r
613 Status = EFI_SUCCESS;\r
614 } else {\r
615 Status = EFI_OUT_OF_RESOURCES;\r
616 }\r
617\r
618 ASSERT_EFI_ERROR (Status);\r
619\r
620 ZeroMem (FtwLiteDevice, sizeof (EFI_FTW_LITE_DEVICE));\r
621 FtwLiteDevice->Signature = FTW_LITE_DEVICE_SIGNATURE;\r
622\r
623 //\r
624 // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.\r
625 //\r
626 FtwLiteDevice->FtwWorkSpace = (UINT8 *) (FtwLiteDevice + 1);\r
627 FtwLiteDevice->FtwWorkSpaceSize = FTW_WORK_SPACE_SIZE;\r
628 SetMem (\r
629 FtwLiteDevice->FtwWorkSpace,\r
630 FtwLiteDevice->FtwWorkSpaceSize,\r
631 FTW_ERASED_BYTE\r
632 );\r
633 FtwLiteDevice->FtwWorkSpaceHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) FtwLiteDevice->FtwWorkSpace;\r
634\r
635 FtwLiteDevice->FtwLastRecord = NULL;\r
636\r
276be2de 637 FtwLiteDevice->WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwWorkingBase);\r
638 FtwLiteDevice->WorkSpaceLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwWorkingSize);\r
639\r
640 FtwLiteDevice->SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwSpareBase);\r
641 FtwLiteDevice->SpareAreaLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwSpareSize);\r
d7dec593 642\r
643 ASSERT ((FtwLiteDevice->WorkSpaceLength != 0) && (FtwLiteDevice->SpareAreaLength != 0));\r
644\r
645 //\r
646 // Locate FVB protocol\r
647 //\r
648 Status = gBS->LocateHandleBuffer (\r
649 ByProtocol,\r
650 &gEfiFirmwareVolumeBlockProtocolGuid,\r
651 NULL,\r
652 &HandleCount,\r
653 &HandleBuffer\r
654 );\r
655 ASSERT_EFI_ERROR (Status);\r
656\r
657 ASSERT (HandleCount > 0);\r
658\r
659 FtwLiteDevice->FtwFvBlock = NULL;\r
660 FtwLiteDevice->FtwBackupFvb = NULL;\r
661 FtwLiteDevice->FtwWorkSpaceLba = (EFI_LBA) (-1);\r
662 FtwLiteDevice->FtwSpareLba = (EFI_LBA) (-1);\r
663 for (Index = 0; Index < HandleCount; Index += 1) {\r
664 Status = gBS->HandleProtocol (\r
665 HandleBuffer[Index],\r
666 &gEfiFirmwareVolumeBlockProtocolGuid,\r
667 (VOID **) &Fvb\r
668 );\r
669 ASSERT_EFI_ERROR (Status);\r
670\r
671 Status = Fvb->GetPhysicalAddress (Fvb, &BaseAddress);\r
672 if (EFI_ERROR (Status)) {\r
673 continue;\r
674 }\r
675\r
676 FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) BaseAddress);\r
677\r
678 if ((FtwLiteDevice->WorkSpaceAddress >= BaseAddress) &&\r
679 (FtwLiteDevice->WorkSpaceAddress <= (BaseAddress + FwVolHeader->FvLength))\r
680 ) {\r
681 FtwLiteDevice->FtwFvBlock = Fvb;\r
682 //\r
683 // To get the LBA of work space\r
684 //\r
685 if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) {\r
686 //\r
687 // FV may have multiple types of BlockLength\r
688 //\r
689 FvbMapEntry = &FwVolHeader->BlockMap[0];\r
690 while (!((FvbMapEntry->NumBlocks == 0) && (FvbMapEntry->Length == 0))) {\r
691 for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) {\r
692 if (FtwLiteDevice->WorkSpaceAddress < (BaseAddress + FvbMapEntry->Length * LbaIndex)) {\r
693 FtwLiteDevice->FtwWorkSpaceLba = LbaIndex - 1;\r
694 //\r
695 // Get the Work space size and Base(Offset)\r
696 //\r
697 FtwLiteDevice->FtwWorkSpaceSize = FtwLiteDevice->WorkSpaceLength;\r
698 FtwLiteDevice->FtwWorkSpaceBase = (UINTN) (FtwLiteDevice->WorkSpaceAddress - (BaseAddress + FvbMapEntry->Length * (LbaIndex - 1)));\r
699 break;\r
700 }\r
701 }\r
702 //\r
703 // end for\r
704 //\r
705 FvbMapEntry++;\r
706 }\r
707 //\r
708 // end while\r
709 //\r
710 }\r
711 }\r
712\r
713 if ((FtwLiteDevice->SpareAreaAddress >= BaseAddress) &&\r
78d6fbd5 714 (FtwLiteDevice->SpareAreaAddress < (BaseAddress + FwVolHeader->FvLength))\r
d7dec593 715 ) {\r
716 FtwLiteDevice->FtwBackupFvb = Fvb;\r
717 //\r
718 // To get the LBA of spare\r
719 //\r
720 if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) {\r
721 //\r
722 // FV may have multiple types of BlockLength\r
723 //\r
724 FvbMapEntry = &FwVolHeader->BlockMap[0];\r
725 while (!((FvbMapEntry->NumBlocks == 0) && (FvbMapEntry->Length == 0))) {\r
726 for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) {\r
727 if (FtwLiteDevice->SpareAreaAddress < (BaseAddress + FvbMapEntry->Length * LbaIndex)) {\r
728 //\r
729 // Get the NumberOfSpareBlock and SizeOfSpareBlock\r
730 //\r
731 FtwLiteDevice->FtwSpareLba = LbaIndex - 1;\r
732 FtwLiteDevice->SizeOfSpareBlock = FvbMapEntry->Length;\r
733 FtwLiteDevice->NumberOfSpareBlock = FtwLiteDevice->SpareAreaLength / FtwLiteDevice->SizeOfSpareBlock;\r
734 //\r
735 // Check the range of spare area to make sure that it's in FV range\r
736 //\r
737 ASSERT ((FtwLiteDevice->FtwSpareLba + FtwLiteDevice->NumberOfSpareBlock) <= FvbMapEntry->NumBlocks);\r
738 break;\r
739 }\r
740 }\r
741\r
742 FvbMapEntry++;\r
743 }\r
744 //\r
745 // end while\r
746 //\r
747 }\r
748 }\r
749 }\r
750 //\r
751 // Calculate the start LBA of working block. Working block is an area which\r
752 // contains working space in its last block and has the same size as spare\r
753 // block, unless there are not enough blocks before the block that contains\r
754 // working space.\r
755 //\r
756 FtwLiteDevice->FtwWorkBlockLba = FtwLiteDevice->FtwWorkSpaceLba - FtwLiteDevice->NumberOfSpareBlock + 1;\r
757 if ((INT64) (FtwLiteDevice->FtwWorkBlockLba) < 0) {\r
758 FtwLiteDevice->FtwWorkBlockLba = 0;\r
759 }\r
760\r
761 if ((FtwLiteDevice->FtwFvBlock == NULL) ||\r
762 (FtwLiteDevice->FtwBackupFvb == NULL) ||\r
763 (FtwLiteDevice->FtwWorkSpaceLba == (EFI_LBA) (-1)) ||\r
764 (FtwLiteDevice->FtwSpareLba == (EFI_LBA) (-1))\r
765 ) {\r
766 DEBUG ((EFI_D_ERROR, "FtwLite: Working or spare FVB not ready\n"));\r
767 ASSERT_EFI_ERROR (Status);\r
768 }\r
769 //\r
770 // Refresh workspace data from working block\r
771 //\r
772 Status = WorkSpaceRefresh (FtwLiteDevice);\r
773 ASSERT_EFI_ERROR (Status);\r
774\r
775 //\r
776 // If the working block workspace is not valid, try the spare block\r
777 //\r
778 if (!IsValidWorkSpace (FtwLiteDevice->FtwWorkSpaceHeader)) {\r
779 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Workspace invalid, read from backup\n"));\r
780 //\r
781 // Read from spare block\r
782 //\r
783 Length = FtwLiteDevice->FtwWorkSpaceSize;\r
784 Status = FtwLiteDevice->FtwBackupFvb->Read (\r
785 FtwLiteDevice->FtwBackupFvb,\r
786 FtwLiteDevice->FtwSpareLba,\r
787 FtwLiteDevice->FtwWorkSpaceBase,\r
788 &Length,\r
789 FtwLiteDevice->FtwWorkSpace\r
790 );\r
791 ASSERT_EFI_ERROR (Status);\r
792\r
793 //\r
794 // If spare block is valid, then replace working block content.\r
795 //\r
796 if (IsValidWorkSpace (FtwLiteDevice->FtwWorkSpaceHeader)) {\r
797 Status = FlushSpareBlockToWorkingBlock (FtwLiteDevice);\r
798 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Restart working block in Init() - %r\n", Status));\r
799 ASSERT_EFI_ERROR (Status);\r
800\r
801 FtwAbort (FtwLiteDevice);\r
802 //\r
803 // Refresh work space.\r
804 //\r
805 Status = WorkSpaceRefresh (FtwLiteDevice);\r
806 if (EFI_ERROR (Status)) {\r
807 return EFI_ABORTED;\r
808 }\r
809 } else {\r
810 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Both are invalid, init workspace\n"));\r
811 //\r
812 // If both are invalid, then initialize work space.\r
813 //\r
814 SetMem (\r
815 FtwLiteDevice->FtwWorkSpace,\r
816 FtwLiteDevice->FtwWorkSpaceSize,\r
817 FTW_ERASED_BYTE\r
818 );\r
819 InitWorkSpaceHeader (FtwLiteDevice->FtwWorkSpaceHeader);\r
820 //\r
e11ae3a5 821 // Initialize the work space\r
d7dec593 822 //\r
e11ae3a5
LG
823 Status = FtwReclaimWorkSpace (FtwLiteDevice, FALSE);\r
824\r
d7dec593 825 if (EFI_ERROR (Status)) {\r
826 return EFI_ABORTED;\r
827 }\r
828 }\r
829 }\r
830 //\r
831 // Hook the protocol API\r
832 //\r
833 FtwLiteDevice->FtwLiteInstance.Write = FtwLiteWrite;\r
834\r
835 //\r
836 // Install protocol interface\r
837 //\r
838 Status = gBS->InstallProtocolInterface (\r
839 &FtwLiteDevice->Handle,\r
840 &gEfiFaultTolerantWriteLiteProtocolGuid,\r
841 EFI_NATIVE_INTERFACE,\r
842 &FtwLiteDevice->FtwLiteInstance\r
843 );\r
844 if (EFI_ERROR (Status)) {\r
845 return EFI_ABORTED;\r
846 }\r
847 //\r
848 // If (!SpareCompleted) THEN Abort to rollback.\r
849 //\r
850 if ((FtwLiteDevice->FtwLastRecord->WriteAllocated == FTW_VALID_STATE) &&\r
851 (FtwLiteDevice->FtwLastRecord->SpareCompleted != FTW_VALID_STATE)\r
852 ) {\r
853 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Init.. record not SpareCompleted, abort()\n"));\r
854 FtwAbort (FtwLiteDevice);\r
855 }\r
856 //\r
857 // if (SpareCompleted) THEN Restart to fault tolerant write.\r
858 //\r
859 if ((FtwLiteDevice->FtwLastRecord->SpareCompleted == FTW_VALID_STATE) &&\r
860 (FtwLiteDevice->FtwLastRecord->WriteCompleted != FTW_VALID_STATE)\r
861 ) {\r
862\r
863 Status = FtwRestart (FtwLiteDevice);\r
864 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Restart last write - %r\n", Status));\r
865 if (EFI_ERROR (Status)) {\r
866 return Status;\r
867 }\r
868 }\r
869 //\r
870 // To check the workspace buffer behind last records is EMPTY or not.\r
871 // If it's not EMPTY, FTW_LITE also need to call reclaim().\r
872 //\r
873 Record = FtwLiteDevice->FtwLastRecord;\r
874 Offset = (UINT8 *) Record - FtwLiteDevice->FtwWorkSpace;\r
875 if (FtwLiteDevice->FtwWorkSpace[Offset] != FTW_ERASED_BYTE) {\r
876 Offset += WRITE_TOTAL_SIZE;\r
877 }\r
878\r
879 if (!IsErasedFlashBuffer (\r
880 FTW_ERASE_POLARITY,\r
881 FtwLiteDevice->FtwWorkSpace + Offset,\r
882 FtwLiteDevice->FtwWorkSpaceSize - Offset\r
883 )) {\r
884 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Workspace is dirty, call reclaim...\n"));\r
e11ae3a5 885 Status = FtwReclaimWorkSpace (FtwLiteDevice, TRUE);\r
d7dec593 886 if (EFI_ERROR (Status)) {\r
887 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Workspace reclaim - %r\n", Status));\r
888 return EFI_ABORTED;\r
889 }\r
890 }\r
891\r
892 return EFI_SUCCESS;\r
893}\r