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