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