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