]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/FirmwareVolume/FaultTolerantWriteDxe/FtwLite.c
Clean up FaultTolerantWriteDxe to remove the duplicated definition.
[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\r
5944a83b 5 This boot service protocol only provides fault tolerant write capability for \r
d7dec593 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
d7dec593 10 The implementation uses an FTW Lite (Fault Tolerant Write) Work Space. \r
da770255 11 This work space is a memory copy of the work space on the Working Block,\r
d7dec593 12 the size of the work space is the FTW_WORK_SPACE_SIZE bytes.\r
5944a83b
LG
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
8986b8cd 19 The information of write operation is stored in write record structure.\r
5944a83b
LG
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
276a49b6 23 This driver operates the data as the whole size of spare block.\r
5944a83b
LG
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
d7dec593 28\r
276a49b6
LG
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
6cc9ca32
LG
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
d7dec593 53\r
e87a334f 54#include "FtwLite.h"\r
d7dec593 55\r
6aab8214 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
5944a83b 60 the modified contents are available.\r
6aab8214 61\r
5944a83b 62 @param This The pointer to this protocol instance. \r
6aab8214 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
5944a83b
LG
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
6aab8214 77\r
78**/\r
d7dec593 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
d7dec593 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
d7dec593 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
276a49b6 135 *NumBytes = FtwLiteDevice->SpareAreaLength - Offset;\r
d7dec593 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
5944a83b 141 if ((MyOffset + FTW_LITE_RECORD_SIZE) > FtwLiteDevice->FtwWorkSpaceSize) {\r
e11ae3a5 142 Status = FtwReclaimWorkSpace (FtwLiteDevice, TRUE);\r
d7dec593 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
276a49b6 167 DEBUG ((EFI_D_ERROR, "FtwLite: Allocate record - %r\n", Status));\r
d7dec593 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
276a49b6 183 DEBUG ((EFI_D_ERROR, "FtwLite: Get FVB physical address - %r\n", Status));\r
d7dec593 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
276a49b6 238 MyLength = FtwLiteDevice->BlockSize;\r
d7dec593 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
276a49b6 257 Offset = (((UINTN) (Lba - FtwLiteDevice->FtwWorkBlockLba)) * FtwLiteDevice->BlockSize + Offset);\r
d7dec593 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
276a49b6 264 MyLength = FtwLiteDevice->BlockSize;\r
d7dec593 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
276a49b6 293 MyLength = FtwLiteDevice->BlockSize;\r
d7dec593 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
276a49b6 316 MyLength = FtwLiteDevice->BlockSize;\r
d7dec593 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
5944a83b 338 // Set the SpareComplete in the FTW record,\r
d7dec593 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
276a49b6 373 MyLength = FtwLiteDevice->BlockSize;\r
d7dec593 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
276a49b6 394 (EFI_D_ERROR,\r
d7dec593 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
6aab8214 405/**\r
da770255 406 Write a record with fault tolerant manner.\r
6aab8214 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
d7dec593 419EFI_STATUS\r
420FtwWriteRecord (\r
421 IN EFI_FTW_LITE_DEVICE *FtwLiteDevice,\r
422 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb\r
423 )\r
d7dec593 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
d7dec593 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
d7dec593 456 } else {\r
457 //\r
8986b8cd 458 // Update blocks other than working block\r
d7dec593 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
6aab8214 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
d7dec593 497EFI_STATUS\r
498FtwRestart (\r
499 IN EFI_FTW_LITE_DEVICE *FtwLiteDevice\r
500 )\r
d7dec593 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
276a49b6 519 DEBUG ((EFI_D_ERROR, "FtwLite: FVB Device Path is not memory mapped\n"));\r
d7dec593 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
276a49b6 532 DEBUG ((EFI_D_INFO, "FtwLite: Restart() - %r\n", Status));\r
d7dec593 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
6aab8214 547/**\r
548 Aborts all previous allocated writes.\r
d7dec593 549\r
d7dec593 550\r
6aab8214 551 @param FtwLiteDevice The private data of FTW_LITE driver\r
d7dec593 552\r
6aab8214 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
d7dec593 556\r
6aab8214 557**/\r
558EFI_STATUS\r
559FtwAbort (\r
560 IN EFI_FTW_LITE_DEVICE *FtwLiteDevice\r
561 )\r
d7dec593 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
276a49b6 592 DEBUG ((EFI_D_INFO, "FtwLite: Abort() success \n"));\r
d7dec593 593 return EFI_SUCCESS;\r
594}\r
595\r
6aab8214 596/**\r
597 This function is the entry point of the Fault Tolerant Write driver.\r
598\r
da770255 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
6aab8214 601\r
602 @retval EFI_SUCCESS FTW has finished the initialization\r
603 @retval EFI_ABORTED FTW initialization error\r
604\r
605**/\r
d7dec593 606EFI_STATUS\r
607EFIAPI\r
608InitializeFtwLite (\r
609 IN EFI_HANDLE ImageHandle,\r
610 IN EFI_SYSTEM_TABLE *SystemTable\r
611 )\r
d7dec593 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
d7dec593 621 EFI_STATUS Status;\r
622 UINTN Offset;\r
276a49b6 623 UINTN Length;\r
d7dec593 624 EFI_FV_BLOCK_MAP_ENTRY *FvbMapEntry;\r
625 UINT32 LbaIndex;\r
d7dec593 626 //\r
da770255 627 // Allocate Private data of this driver, including the FtwWorkSpace[FTW_WORK_SPACE_SIZE].\r
d7dec593 628 //\r
629 FtwLiteDevice = NULL;\r
276a49b6 630 FtwLiteDevice = AllocatePool (sizeof (EFI_FTW_LITE_DEVICE) + PcdGet32 (PcdFlashNvStorageFtwWorkingSize));\r
77980854 631 ASSERT (FtwLiteDevice != NULL);\r
d7dec593 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
d7dec593 640 FtwLiteDevice->FtwWorkSpaceHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) FtwLiteDevice->FtwWorkSpace;\r
d7dec593 641 FtwLiteDevice->FtwLastRecord = NULL;\r
642\r
276be2de 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
d7dec593 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
276a49b6 685 ((FtwLiteDevice->WorkSpaceAddress + FtwLiteDevice->WorkSpaceLength) <= (BaseAddress + FwVolHeader->FvLength))\r
d7dec593 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
5944a83b
LG
698 if ((FtwLiteDevice->WorkSpaceAddress >= (BaseAddress + FvbMapEntry->Length * (LbaIndex - 1)))\r
699 && (FtwLiteDevice->WorkSpaceAddress < (BaseAddress + FvbMapEntry->Length * LbaIndex))) {\r
d7dec593 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
5944a83b
LG
712 if (LbaIndex <= FvbMapEntry->NumBlocks) {\r
713 //\r
714 // Work space range is found.\r
715 //\r
716 break;\r
717 }\r
d7dec593 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
276a49b6 727 ((FtwLiteDevice->SpareAreaAddress + FtwLiteDevice->SpareAreaLength) <= (BaseAddress + FwVolHeader->FvLength))\r
d7dec593 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
5944a83b
LG
740 if ((FtwLiteDevice->SpareAreaAddress >= (BaseAddress + FvbMapEntry->Length * (LbaIndex - 1)))\r
741 && (FtwLiteDevice->SpareAreaAddress < (BaseAddress + FvbMapEntry->Length * LbaIndex))) {\r
d7dec593 742 //\r
276a49b6 743 // Get the NumberOfSpareBlock and BlockSize\r
d7dec593 744 //\r
745 FtwLiteDevice->FtwSpareLba = LbaIndex - 1;\r
276a49b6
LG
746 FtwLiteDevice->BlockSize = FvbMapEntry->Length;\r
747 FtwLiteDevice->NumberOfSpareBlock = FtwLiteDevice->SpareAreaLength / FtwLiteDevice->BlockSize;\r
d7dec593 748 //\r
749 // Check the range of spare area to make sure that it's in FV range\r
276a49b6 750 // To do delete\r
d7dec593 751 //\r
752 ASSERT ((FtwLiteDevice->FtwSpareLba + FtwLiteDevice->NumberOfSpareBlock) <= FvbMapEntry->NumBlocks);\r
753 break;\r
754 }\r
755 }\r
5944a83b
LG
756 if (LbaIndex <= FvbMapEntry->NumBlocks) {\r
757 //\r
758 // Spare FV range is found.\r
759 //\r
760 break;\r
761 }\r
d7dec593 762 FvbMapEntry++;\r
763 }\r
764 //\r
765 // end while\r
766 //\r
767 }\r
768 }\r
769 }\r
276a49b6 770\r
d7dec593 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
276a49b6
LG
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
d7dec593 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
77980854
LG
790 FreePool (FtwLiteDevice);\r
791 return EFI_ABORTED;\r
d7dec593 792 }\r
276a49b6 793\r
d7dec593 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
276a49b6 804 DEBUG ((EFI_D_ERROR, "FtwLite: Workspace invalid, read from backup\n"));\r
d7dec593 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
276a49b6 823 DEBUG ((EFI_D_ERROR, "FtwLite: Restart working block in Init() - %r\n", Status));\r
d7dec593 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
77980854 832 FreePool (FtwLiteDevice);\r
d7dec593 833 return EFI_ABORTED;\r
834 }\r
835 } else {\r
276a49b6 836 DEBUG ((EFI_D_ERROR, "FtwLite: Both are invalid, init workspace\n"));\r
d7dec593 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
e11ae3a5 847 // Initialize the work space\r
d7dec593 848 //\r
e11ae3a5
LG
849 Status = FtwReclaimWorkSpace (FtwLiteDevice, FALSE);\r
850\r
d7dec593 851 if (EFI_ERROR (Status)) {\r
77980854 852 FreePool (FtwLiteDevice);\r
d7dec593 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
77980854 872 FreePool (FtwLiteDevice);\r
d7dec593 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
276a49b6 881 DEBUG ((EFI_D_ERROR, "FtwLite: Init.. record not SpareCompleted, abort()\n"));\r
d7dec593 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
276a49b6 892 DEBUG ((EFI_D_ERROR, "FtwLite: Restart last write - %r\n", Status));\r
d7dec593 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
276a49b6 912 DEBUG ((EFI_D_ERROR, "FtwLite: Workspace is dirty, call reclaim...\n"));\r
e11ae3a5 913 Status = FtwReclaimWorkSpace (FtwLiteDevice, TRUE);\r
d7dec593 914 if (EFI_ERROR (Status)) {\r
276a49b6 915 DEBUG ((EFI_D_ERROR, "FtwLite: Workspace reclaim - %r\n", Status));\r
77980854 916 FreePool (FtwLiteDevice);\r
d7dec593 917 return EFI_ABORTED;\r
918 }\r
919 }\r
920\r
921 return EFI_SUCCESS;\r
922}\r