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