]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/FirmwareVolume/FaultTolerantWriteLite/Dxe/FtwLite.c
Adjust directory structures.
[mirror_edk2.git] / MdeModulePkg / Universal / FirmwareVolume / FaultTolerantWriteLite / Dxe / FtwLite.c
CommitLineData
d7dec593 1/*++\r
2\r
3Copyright (c) 2006 - 2007, 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 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 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 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 FreePool (MyBuffer);\r
295 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 FreePool (MyBuffer);\r
318 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 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 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 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 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 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_FV_BLOCK_MAP_ENTRY *FvbMapEntry;\r
636 UINT32 LbaIndex;\r
276be2de 637\r
d7dec593 638 //\r
639 // Allocate Private data of this driver,\r
640 // INCLUDING THE FtwWorkSpace[FTW_WORK_SPACE_SIZE].\r
641 //\r
642 FtwLiteDevice = NULL;\r
643 FtwLiteDevice = AllocatePool (sizeof (EFI_FTW_LITE_DEVICE) + FTW_WORK_SPACE_SIZE);\r
644 if (FtwLiteDevice != NULL) {\r
645 Status = EFI_SUCCESS;\r
646 } else {\r
647 Status = EFI_OUT_OF_RESOURCES;\r
648 }\r
649\r
650 ASSERT_EFI_ERROR (Status);\r
651\r
652 ZeroMem (FtwLiteDevice, sizeof (EFI_FTW_LITE_DEVICE));\r
653 FtwLiteDevice->Signature = FTW_LITE_DEVICE_SIGNATURE;\r
654\r
655 //\r
656 // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.\r
657 //\r
658 FtwLiteDevice->FtwWorkSpace = (UINT8 *) (FtwLiteDevice + 1);\r
659 FtwLiteDevice->FtwWorkSpaceSize = FTW_WORK_SPACE_SIZE;\r
660 SetMem (\r
661 FtwLiteDevice->FtwWorkSpace,\r
662 FtwLiteDevice->FtwWorkSpaceSize,\r
663 FTW_ERASED_BYTE\r
664 );\r
665 FtwLiteDevice->FtwWorkSpaceHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) FtwLiteDevice->FtwWorkSpace;\r
666\r
667 FtwLiteDevice->FtwLastRecord = NULL;\r
668\r
276be2de 669 FtwLiteDevice->WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwWorkingBase);\r
670 FtwLiteDevice->WorkSpaceLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwWorkingSize);\r
671\r
672 FtwLiteDevice->SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwSpareBase);\r
673 FtwLiteDevice->SpareAreaLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwSpareSize);\r
d7dec593 674\r
675 ASSERT ((FtwLiteDevice->WorkSpaceLength != 0) && (FtwLiteDevice->SpareAreaLength != 0));\r
676\r
677 //\r
678 // Locate FVB protocol\r
679 //\r
680 Status = gBS->LocateHandleBuffer (\r
681 ByProtocol,\r
682 &gEfiFirmwareVolumeBlockProtocolGuid,\r
683 NULL,\r
684 &HandleCount,\r
685 &HandleBuffer\r
686 );\r
687 ASSERT_EFI_ERROR (Status);\r
688\r
689 ASSERT (HandleCount > 0);\r
690\r
691 FtwLiteDevice->FtwFvBlock = NULL;\r
692 FtwLiteDevice->FtwBackupFvb = NULL;\r
693 FtwLiteDevice->FtwWorkSpaceLba = (EFI_LBA) (-1);\r
694 FtwLiteDevice->FtwSpareLba = (EFI_LBA) (-1);\r
695 for (Index = 0; Index < HandleCount; Index += 1) {\r
696 Status = gBS->HandleProtocol (\r
697 HandleBuffer[Index],\r
698 &gEfiFirmwareVolumeBlockProtocolGuid,\r
699 (VOID **) &Fvb\r
700 );\r
701 ASSERT_EFI_ERROR (Status);\r
702\r
703 Status = Fvb->GetPhysicalAddress (Fvb, &BaseAddress);\r
704 if (EFI_ERROR (Status)) {\r
705 continue;\r
706 }\r
707\r
708 FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) BaseAddress);\r
709\r
710 if ((FtwLiteDevice->WorkSpaceAddress >= BaseAddress) &&\r
711 (FtwLiteDevice->WorkSpaceAddress <= (BaseAddress + FwVolHeader->FvLength))\r
712 ) {\r
713 FtwLiteDevice->FtwFvBlock = Fvb;\r
714 //\r
715 // To get the LBA of work space\r
716 //\r
717 if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) {\r
718 //\r
719 // FV may have multiple types of BlockLength\r
720 //\r
721 FvbMapEntry = &FwVolHeader->BlockMap[0];\r
722 while (!((FvbMapEntry->NumBlocks == 0) && (FvbMapEntry->Length == 0))) {\r
723 for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) {\r
724 if (FtwLiteDevice->WorkSpaceAddress < (BaseAddress + FvbMapEntry->Length * LbaIndex)) {\r
725 FtwLiteDevice->FtwWorkSpaceLba = LbaIndex - 1;\r
726 //\r
727 // Get the Work space size and Base(Offset)\r
728 //\r
729 FtwLiteDevice->FtwWorkSpaceSize = FtwLiteDevice->WorkSpaceLength;\r
730 FtwLiteDevice->FtwWorkSpaceBase = (UINTN) (FtwLiteDevice->WorkSpaceAddress - (BaseAddress + FvbMapEntry->Length * (LbaIndex - 1)));\r
731 break;\r
732 }\r
733 }\r
734 //\r
735 // end for\r
736 //\r
737 FvbMapEntry++;\r
738 }\r
739 //\r
740 // end while\r
741 //\r
742 }\r
743 }\r
744\r
745 if ((FtwLiteDevice->SpareAreaAddress >= BaseAddress) &&\r
746 (FtwLiteDevice->SpareAreaAddress <= (BaseAddress + FwVolHeader->FvLength))\r
747 ) {\r
748 FtwLiteDevice->FtwBackupFvb = Fvb;\r
749 //\r
750 // To get the LBA of spare\r
751 //\r
752 if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) {\r
753 //\r
754 // FV may have multiple types of BlockLength\r
755 //\r
756 FvbMapEntry = &FwVolHeader->BlockMap[0];\r
757 while (!((FvbMapEntry->NumBlocks == 0) && (FvbMapEntry->Length == 0))) {\r
758 for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) {\r
759 if (FtwLiteDevice->SpareAreaAddress < (BaseAddress + FvbMapEntry->Length * LbaIndex)) {\r
760 //\r
761 // Get the NumberOfSpareBlock and SizeOfSpareBlock\r
762 //\r
763 FtwLiteDevice->FtwSpareLba = LbaIndex - 1;\r
764 FtwLiteDevice->SizeOfSpareBlock = FvbMapEntry->Length;\r
765 FtwLiteDevice->NumberOfSpareBlock = FtwLiteDevice->SpareAreaLength / FtwLiteDevice->SizeOfSpareBlock;\r
766 //\r
767 // Check the range of spare area to make sure that it's in FV range\r
768 //\r
769 ASSERT ((FtwLiteDevice->FtwSpareLba + FtwLiteDevice->NumberOfSpareBlock) <= FvbMapEntry->NumBlocks);\r
770 break;\r
771 }\r
772 }\r
773\r
774 FvbMapEntry++;\r
775 }\r
776 //\r
777 // end while\r
778 //\r
779 }\r
780 }\r
781 }\r
782 //\r
783 // Calculate the start LBA of working block. Working block is an area which\r
784 // contains working space in its last block and has the same size as spare\r
785 // block, unless there are not enough blocks before the block that contains\r
786 // working space.\r
787 //\r
788 FtwLiteDevice->FtwWorkBlockLba = FtwLiteDevice->FtwWorkSpaceLba - FtwLiteDevice->NumberOfSpareBlock + 1;\r
789 if ((INT64) (FtwLiteDevice->FtwWorkBlockLba) < 0) {\r
790 FtwLiteDevice->FtwWorkBlockLba = 0;\r
791 }\r
792\r
793 if ((FtwLiteDevice->FtwFvBlock == NULL) ||\r
794 (FtwLiteDevice->FtwBackupFvb == NULL) ||\r
795 (FtwLiteDevice->FtwWorkSpaceLba == (EFI_LBA) (-1)) ||\r
796 (FtwLiteDevice->FtwSpareLba == (EFI_LBA) (-1))\r
797 ) {\r
798 DEBUG ((EFI_D_ERROR, "FtwLite: Working or spare FVB not ready\n"));\r
799 ASSERT_EFI_ERROR (Status);\r
800 }\r
801 //\r
802 // Refresh workspace data from working block\r
803 //\r
804 Status = WorkSpaceRefresh (FtwLiteDevice);\r
805 ASSERT_EFI_ERROR (Status);\r
806\r
807 //\r
808 // If the working block workspace is not valid, try the spare block\r
809 //\r
810 if (!IsValidWorkSpace (FtwLiteDevice->FtwWorkSpaceHeader)) {\r
811 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Workspace invalid, read from backup\n"));\r
812 //\r
813 // Read from spare block\r
814 //\r
815 Length = FtwLiteDevice->FtwWorkSpaceSize;\r
816 Status = FtwLiteDevice->FtwBackupFvb->Read (\r
817 FtwLiteDevice->FtwBackupFvb,\r
818 FtwLiteDevice->FtwSpareLba,\r
819 FtwLiteDevice->FtwWorkSpaceBase,\r
820 &Length,\r
821 FtwLiteDevice->FtwWorkSpace\r
822 );\r
823 ASSERT_EFI_ERROR (Status);\r
824\r
825 //\r
826 // If spare block is valid, then replace working block content.\r
827 //\r
828 if (IsValidWorkSpace (FtwLiteDevice->FtwWorkSpaceHeader)) {\r
829 Status = FlushSpareBlockToWorkingBlock (FtwLiteDevice);\r
830 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Restart working block in Init() - %r\n", Status));\r
831 ASSERT_EFI_ERROR (Status);\r
832\r
833 FtwAbort (FtwLiteDevice);\r
834 //\r
835 // Refresh work space.\r
836 //\r
837 Status = WorkSpaceRefresh (FtwLiteDevice);\r
838 if (EFI_ERROR (Status)) {\r
839 return EFI_ABORTED;\r
840 }\r
841 } else {\r
842 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Both are invalid, init workspace\n"));\r
843 //\r
844 // If both are invalid, then initialize work space.\r
845 //\r
846 SetMem (\r
847 FtwLiteDevice->FtwWorkSpace,\r
848 FtwLiteDevice->FtwWorkSpaceSize,\r
849 FTW_ERASED_BYTE\r
850 );\r
851 InitWorkSpaceHeader (FtwLiteDevice->FtwWorkSpaceHeader);\r
852 //\r
853 // Write to work space on the working block\r
854 //\r
855 Length = FtwLiteDevice->FtwWorkSpaceSize;\r
856 Status = FtwLiteDevice->FtwFvBlock->Write (\r
857 FtwLiteDevice->FtwFvBlock,\r
858 FtwLiteDevice->FtwWorkSpaceLba,\r
859 FtwLiteDevice->FtwWorkSpaceBase,\r
860 &Length,\r
861 FtwLiteDevice->FtwWorkSpace\r
862 );\r
863 if (EFI_ERROR (Status)) {\r
864 return EFI_ABORTED;\r
865 }\r
866 }\r
867 }\r
868 //\r
869 // Hook the protocol API\r
870 //\r
871 FtwLiteDevice->FtwLiteInstance.Write = FtwLiteWrite;\r
872\r
873 //\r
874 // Install protocol interface\r
875 //\r
876 Status = gBS->InstallProtocolInterface (\r
877 &FtwLiteDevice->Handle,\r
878 &gEfiFaultTolerantWriteLiteProtocolGuid,\r
879 EFI_NATIVE_INTERFACE,\r
880 &FtwLiteDevice->FtwLiteInstance\r
881 );\r
882 if (EFI_ERROR (Status)) {\r
883 return EFI_ABORTED;\r
884 }\r
885 //\r
886 // If (!SpareCompleted) THEN Abort to rollback.\r
887 //\r
888 if ((FtwLiteDevice->FtwLastRecord->WriteAllocated == FTW_VALID_STATE) &&\r
889 (FtwLiteDevice->FtwLastRecord->SpareCompleted != FTW_VALID_STATE)\r
890 ) {\r
891 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Init.. record not SpareCompleted, abort()\n"));\r
892 FtwAbort (FtwLiteDevice);\r
893 }\r
894 //\r
895 // if (SpareCompleted) THEN Restart to fault tolerant write.\r
896 //\r
897 if ((FtwLiteDevice->FtwLastRecord->SpareCompleted == FTW_VALID_STATE) &&\r
898 (FtwLiteDevice->FtwLastRecord->WriteCompleted != FTW_VALID_STATE)\r
899 ) {\r
900\r
901 Status = FtwRestart (FtwLiteDevice);\r
902 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Restart last write - %r\n", Status));\r
903 if (EFI_ERROR (Status)) {\r
904 return Status;\r
905 }\r
906 }\r
907 //\r
908 // To check the workspace buffer behind last records is EMPTY or not.\r
909 // If it's not EMPTY, FTW_LITE also need to call reclaim().\r
910 //\r
911 Record = FtwLiteDevice->FtwLastRecord;\r
912 Offset = (UINT8 *) Record - FtwLiteDevice->FtwWorkSpace;\r
913 if (FtwLiteDevice->FtwWorkSpace[Offset] != FTW_ERASED_BYTE) {\r
914 Offset += WRITE_TOTAL_SIZE;\r
915 }\r
916\r
917 if (!IsErasedFlashBuffer (\r
918 FTW_ERASE_POLARITY,\r
919 FtwLiteDevice->FtwWorkSpace + Offset,\r
920 FtwLiteDevice->FtwWorkSpaceSize - Offset\r
921 )) {\r
922 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Workspace is dirty, call reclaim...\n"));\r
923 Status = FtwReclaimWorkSpace (FtwLiteDevice);\r
924 if (EFI_ERROR (Status)) {\r
925 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Workspace reclaim - %r\n", Status));\r
926 return EFI_ABORTED;\r
927 }\r
928 }\r
929\r
930 return EFI_SUCCESS;\r
931}\r