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