]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/FirmwareVolume/FaultTolerantWriteDxe/FtwLite.c
fix the operator priority bug.
[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
8986b8cd 20 The information of write operation is stored in write record structure.\r
5944a83b
LG
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
d7dec593 426 // ELSE flush spare block to normal target block.ENDIF\r
427 //\r
428 if (IsInWorkingBlock (FtwLiteDevice, Fvb, Record->Lba)) {\r
429 //\r
430 // If target block is working block, Attention:\r
431 // it's required to set SPARE_COMPLETED to spare block.\r
432 //\r
433 WorkSpaceLbaOffset = FtwLiteDevice->FtwWorkSpaceLba - FtwLiteDevice->FtwWorkBlockLba;\r
434 Offset = (UINT8 *) Record - FtwLiteDevice->FtwWorkSpace;\r
435 Status = FtwUpdateFvState (\r
436 FtwLiteDevice->FtwBackupFvb,\r
437 FtwLiteDevice->FtwSpareLba + WorkSpaceLbaOffset,\r
438 FtwLiteDevice->FtwWorkSpaceBase + Offset,\r
439 SPARE_COMPLETED\r
440 );\r
441 ASSERT_EFI_ERROR (Status);\r
442\r
443 Status = FlushSpareBlockToWorkingBlock (FtwLiteDevice);\r
d7dec593 444 } else {\r
445 //\r
8986b8cd 446 // Update blocks other than working block\r
d7dec593 447 //\r
448 Status = FlushSpareBlockToTargetBlock (FtwLiteDevice, Fvb, Record->Lba);\r
449 }\r
450\r
451 ASSERT_EFI_ERROR (Status);\r
452\r
453 //\r
454 // Set WriteCompleted flag in record\r
455 //\r
456 Offset = (UINT8 *) Record - FtwLiteDevice->FtwWorkSpace;\r
457 Status = FtwUpdateFvState (\r
458 FtwLiteDevice->FtwFvBlock,\r
459 FtwLiteDevice->FtwWorkSpaceLba,\r
460 FtwLiteDevice->FtwWorkSpaceBase + Offset,\r
461 WRITE_COMPLETED\r
462 );\r
463 ASSERT_EFI_ERROR (Status);\r
464\r
465 Record->WriteCompleted = FTW_VALID_STATE;\r
466 return EFI_SUCCESS;\r
467}\r
468\r
469\r
6aab8214 470/**\r
471 Restarts a previously interrupted write. The caller must provide the\r
472 block protocol needed to complete the interrupted write.\r
473\r
474\r
475 @param FtwLiteDevice The private data of FTW_LITE driver\r
476 FvbHandle - The handle of FVB protocol that provides services for\r
477 reading, writing, and erasing the target block.\r
478\r
479 @retval EFI_SUCCESS The function completed successfully\r
480 @retval EFI_ACCESS_DENIED No pending writes exist\r
481 @retval EFI_NOT_FOUND FVB protocol not found by the handle\r
482 @retval EFI_ABORTED The function could not complete successfully\r
483\r
484**/\r
d7dec593 485EFI_STATUS\r
486FtwRestart (\r
487 IN EFI_FTW_LITE_DEVICE *FtwLiteDevice\r
488 )\r
d7dec593 489{\r
490 EFI_STATUS Status;\r
491 EFI_FTW_LITE_RECORD *Record;\r
492 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;\r
493 EFI_DEV_PATH_PTR DevPathPtr;\r
494\r
495 //\r
496 // Spare Completed but Destination not complete,\r
497 // Recover the targt block with the spare block.\r
498 //\r
499 Record = FtwLiteDevice->FtwLastRecord;\r
500\r
501 //\r
502 // Only support memory mapped FVB device path by now.\r
503 //\r
504 DevPathPtr.MemMap = (MEMMAP_DEVICE_PATH *) &Record->DevPath;\r
505 if (!((DevPathPtr.MemMap->Header.Type == HARDWARE_DEVICE_PATH) && (DevPathPtr.MemMap->Header.SubType == HW_MEMMAP_DP))\r
506 ) {\r
507 DEBUG ((EFI_D_FTW_LITE, "FtwLite: FVB Device Path is not memory mapped\n"));\r
508 return EFI_ABORTED;\r
509 }\r
510\r
511 Status = GetFvbByAddress (DevPathPtr.MemMap->StartingAddress, &Fvb);\r
512 if (EFI_ERROR (Status)) {\r
513 return EFI_NOT_FOUND;\r
514 }\r
515 //\r
516 // Since the content has already backuped in spare block, the write is\r
517 // guaranteed to be completed with fault tolerant manner.\r
518 //\r
519 Status = FtwWriteRecord (FtwLiteDevice, Fvb);\r
520 DEBUG ((EFI_D_FTW_INFO, "FtwLite: Restart() - %r\n", Status));\r
521\r
522 Record++;\r
523 FtwLiteDevice->FtwLastRecord = Record;\r
524\r
525 //\r
526 // Erase Spare block\r
527 // This is restart, no need to keep spareblock content.\r
528 //\r
529 FtwEraseSpareBlock (FtwLiteDevice);\r
530\r
531 return Status;\r
532}\r
533\r
534\r
6aab8214 535/**\r
536 Aborts all previous allocated writes.\r
d7dec593 537\r
d7dec593 538\r
6aab8214 539 @param FtwLiteDevice The private data of FTW_LITE driver\r
d7dec593 540\r
6aab8214 541 @retval EFI_SUCCESS The function completed successfully\r
542 @retval EFI_ABORTED The function could not complete successfully.\r
543 @retval EFI_NOT_FOUND No allocated writes exist.\r
d7dec593 544\r
6aab8214 545**/\r
546EFI_STATUS\r
547FtwAbort (\r
548 IN EFI_FTW_LITE_DEVICE *FtwLiteDevice\r
549 )\r
d7dec593 550{\r
551 EFI_STATUS Status;\r
552 UINTN Offset;\r
553\r
554 if (FtwLiteDevice->FtwLastRecord->WriteCompleted == FTW_VALID_STATE) {\r
555 return EFI_NOT_FOUND;\r
556 }\r
557 //\r
558 // Update the complete state of the header as VALID and abort.\r
559 //\r
560 Offset = (UINT8 *) FtwLiteDevice->FtwLastRecord - FtwLiteDevice->FtwWorkSpace;\r
561 Status = FtwUpdateFvState (\r
562 FtwLiteDevice->FtwFvBlock,\r
563 FtwLiteDevice->FtwWorkSpaceLba,\r
564 FtwLiteDevice->FtwWorkSpaceBase + Offset,\r
565 WRITE_COMPLETED\r
566 );\r
567 if (EFI_ERROR (Status)) {\r
568 return EFI_ABORTED;\r
569 }\r
570\r
571 FtwLiteDevice->FtwLastRecord->WriteCompleted = FTW_VALID_STATE;\r
572\r
573 Status = FtwGetLastRecord (FtwLiteDevice, &FtwLiteDevice->FtwLastRecord);\r
574\r
575 //\r
576 // Erase the spare block\r
577 //\r
578 Status = FtwEraseSpareBlock (FtwLiteDevice);\r
579\r
580 DEBUG ((EFI_D_FTW_INFO, "FtwLite: Abort() success \n"));\r
581 return EFI_SUCCESS;\r
582}\r
583\r
6aab8214 584/**\r
585 This function is the entry point of the Fault Tolerant Write driver.\r
586\r
da770255 587 @param ImageHandle A handle for the image that is initializing this driver\r
588 @param SystemTable A pointer to the EFI system table\r
6aab8214 589\r
590 @retval EFI_SUCCESS FTW has finished the initialization\r
591 @retval EFI_ABORTED FTW initialization error\r
592\r
593**/\r
d7dec593 594EFI_STATUS\r
595EFIAPI\r
596InitializeFtwLite (\r
597 IN EFI_HANDLE ImageHandle,\r
598 IN EFI_SYSTEM_TABLE *SystemTable\r
599 )\r
d7dec593 600{\r
601 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;\r
602 UINTN Index;\r
603 EFI_HANDLE *HandleBuffer;\r
604 UINTN HandleCount;\r
605 EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;\r
606 EFI_PHYSICAL_ADDRESS BaseAddress;\r
607 EFI_FTW_LITE_DEVICE *FtwLiteDevice;\r
608 EFI_FTW_LITE_RECORD *Record;\r
609 UINTN Length;\r
610 EFI_STATUS Status;\r
611 UINTN Offset;\r
612 EFI_FV_BLOCK_MAP_ENTRY *FvbMapEntry;\r
613 UINT32 LbaIndex;\r
276be2de 614\r
d7dec593 615 //\r
da770255 616 // Allocate Private data of this driver, including the FtwWorkSpace[FTW_WORK_SPACE_SIZE].\r
d7dec593 617 //\r
5944a83b
LG
618 Length = FTW_WORK_SPACE_SIZE;\r
619 if (Length < PcdGet32 (PcdFlashNvStorageFtwWorkingSize)) {\r
620 Length = PcdGet32 (PcdFlashNvStorageFtwWorkingSize);\r
621 }\r
622\r
d7dec593 623 FtwLiteDevice = NULL;\r
5944a83b 624 FtwLiteDevice = AllocatePool (sizeof (EFI_FTW_LITE_DEVICE) + Length);\r
77980854 625 ASSERT (FtwLiteDevice != NULL);\r
d7dec593 626\r
627 ZeroMem (FtwLiteDevice, sizeof (EFI_FTW_LITE_DEVICE));\r
628 FtwLiteDevice->Signature = FTW_LITE_DEVICE_SIGNATURE;\r
629\r
630 //\r
631 // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.\r
632 //\r
633 FtwLiteDevice->FtwWorkSpace = (UINT8 *) (FtwLiteDevice + 1);\r
634 FtwLiteDevice->FtwWorkSpaceSize = FTW_WORK_SPACE_SIZE;\r
5944a83b 635 FtwLiteDevice->FtwWorkSpaceBase = FTW_WORK_SPACE_BASE;\r
d7dec593 636 SetMem (\r
637 FtwLiteDevice->FtwWorkSpace,\r
638 FtwLiteDevice->FtwWorkSpaceSize,\r
639 FTW_ERASED_BYTE\r
640 );\r
641 FtwLiteDevice->FtwWorkSpaceHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) FtwLiteDevice->FtwWorkSpace;\r
642\r
643 FtwLiteDevice->FtwLastRecord = NULL;\r
644\r
276be2de 645 FtwLiteDevice->WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwWorkingBase);\r
646 FtwLiteDevice->WorkSpaceLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwWorkingSize);\r
647\r
648 FtwLiteDevice->SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwSpareBase);\r
649 FtwLiteDevice->SpareAreaLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwSpareSize);\r
d7dec593 650\r
651 ASSERT ((FtwLiteDevice->WorkSpaceLength != 0) && (FtwLiteDevice->SpareAreaLength != 0));\r
652\r
653 //\r
654 // Locate FVB protocol\r
655 //\r
656 Status = gBS->LocateHandleBuffer (\r
657 ByProtocol,\r
658 &gEfiFirmwareVolumeBlockProtocolGuid,\r
659 NULL,\r
660 &HandleCount,\r
661 &HandleBuffer\r
662 );\r
663 ASSERT_EFI_ERROR (Status);\r
664\r
665 ASSERT (HandleCount > 0);\r
666\r
667 FtwLiteDevice->FtwFvBlock = NULL;\r
668 FtwLiteDevice->FtwBackupFvb = NULL;\r
669 FtwLiteDevice->FtwWorkSpaceLba = (EFI_LBA) (-1);\r
670 FtwLiteDevice->FtwSpareLba = (EFI_LBA) (-1);\r
671 for (Index = 0; Index < HandleCount; Index += 1) {\r
672 Status = gBS->HandleProtocol (\r
673 HandleBuffer[Index],\r
674 &gEfiFirmwareVolumeBlockProtocolGuid,\r
675 (VOID **) &Fvb\r
676 );\r
677 ASSERT_EFI_ERROR (Status);\r
678\r
679 Status = Fvb->GetPhysicalAddress (Fvb, &BaseAddress);\r
680 if (EFI_ERROR (Status)) {\r
681 continue;\r
682 }\r
683\r
684 FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) BaseAddress);\r
685\r
686 if ((FtwLiteDevice->WorkSpaceAddress >= BaseAddress) &&\r
5944a83b 687 (FtwLiteDevice->WorkSpaceAddress < (BaseAddress + FwVolHeader->FvLength))\r
d7dec593 688 ) {\r
689 FtwLiteDevice->FtwFvBlock = Fvb;\r
690 //\r
691 // To get the LBA of work space\r
692 //\r
693 if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) {\r
694 //\r
695 // FV may have multiple types of BlockLength\r
696 //\r
697 FvbMapEntry = &FwVolHeader->BlockMap[0];\r
698 while (!((FvbMapEntry->NumBlocks == 0) && (FvbMapEntry->Length == 0))) {\r
699 for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) {\r
5944a83b
LG
700 if ((FtwLiteDevice->WorkSpaceAddress >= (BaseAddress + FvbMapEntry->Length * (LbaIndex - 1)))\r
701 && (FtwLiteDevice->WorkSpaceAddress < (BaseAddress + FvbMapEntry->Length * LbaIndex))) {\r
d7dec593 702 FtwLiteDevice->FtwWorkSpaceLba = LbaIndex - 1;\r
703 //\r
704 // Get the Work space size and Base(Offset)\r
705 //\r
706 FtwLiteDevice->FtwWorkSpaceSize = FtwLiteDevice->WorkSpaceLength;\r
707 FtwLiteDevice->FtwWorkSpaceBase = (UINTN) (FtwLiteDevice->WorkSpaceAddress - (BaseAddress + FvbMapEntry->Length * (LbaIndex - 1)));\r
708 break;\r
709 }\r
710 }\r
711 //\r
712 // end for\r
713 //\r
5944a83b
LG
714 if (LbaIndex <= FvbMapEntry->NumBlocks) {\r
715 //\r
716 // Work space range is found.\r
717 //\r
718 break;\r
719 }\r
d7dec593 720 FvbMapEntry++;\r
721 }\r
722 //\r
723 // end while\r
724 //\r
725 }\r
726 }\r
727\r
728 if ((FtwLiteDevice->SpareAreaAddress >= BaseAddress) &&\r
78d6fbd5 729 (FtwLiteDevice->SpareAreaAddress < (BaseAddress + FwVolHeader->FvLength))\r
d7dec593 730 ) {\r
731 FtwLiteDevice->FtwBackupFvb = Fvb;\r
732 //\r
733 // To get the LBA of spare\r
734 //\r
735 if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) {\r
736 //\r
737 // FV may have multiple types of BlockLength\r
738 //\r
739 FvbMapEntry = &FwVolHeader->BlockMap[0];\r
740 while (!((FvbMapEntry->NumBlocks == 0) && (FvbMapEntry->Length == 0))) {\r
741 for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) {\r
5944a83b
LG
742 if ((FtwLiteDevice->SpareAreaAddress >= (BaseAddress + FvbMapEntry->Length * (LbaIndex - 1)))\r
743 && (FtwLiteDevice->SpareAreaAddress < (BaseAddress + FvbMapEntry->Length * LbaIndex))) {\r
d7dec593 744 //\r
745 // Get the NumberOfSpareBlock and SizeOfSpareBlock\r
746 //\r
747 FtwLiteDevice->FtwSpareLba = LbaIndex - 1;\r
748 FtwLiteDevice->SizeOfSpareBlock = FvbMapEntry->Length;\r
749 FtwLiteDevice->NumberOfSpareBlock = FtwLiteDevice->SpareAreaLength / FtwLiteDevice->SizeOfSpareBlock;\r
750 //\r
751 // Check the range of spare area to make sure that it's in FV range\r
752 //\r
753 ASSERT ((FtwLiteDevice->FtwSpareLba + FtwLiteDevice->NumberOfSpareBlock) <= FvbMapEntry->NumBlocks);\r
754 break;\r
755 }\r
756 }\r
5944a83b
LG
757 if (LbaIndex <= FvbMapEntry->NumBlocks) {\r
758 //\r
759 // Spare FV range is found.\r
760 //\r
761 break;\r
762 }\r
d7dec593 763 FvbMapEntry++;\r
764 }\r
765 //\r
766 // end while\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 FtwLiteDevice->FtwWorkBlockLba = 0;\r
780 }\r
781\r
782 if ((FtwLiteDevice->FtwFvBlock == NULL) ||\r
783 (FtwLiteDevice->FtwBackupFvb == NULL) ||\r
784 (FtwLiteDevice->FtwWorkSpaceLba == (EFI_LBA) (-1)) ||\r
785 (FtwLiteDevice->FtwSpareLba == (EFI_LBA) (-1))\r
786 ) {\r
787 DEBUG ((EFI_D_ERROR, "FtwLite: Working or spare FVB not ready\n"));\r
77980854
LG
788 FreePool (FtwLiteDevice);\r
789 return EFI_ABORTED;\r
d7dec593 790 }\r
791 //\r
792 // Refresh workspace data from working block\r
793 //\r
794 Status = WorkSpaceRefresh (FtwLiteDevice);\r
795 ASSERT_EFI_ERROR (Status);\r
796\r
797 //\r
798 // If the working block workspace is not valid, try the spare block\r
799 //\r
800 if (!IsValidWorkSpace (FtwLiteDevice->FtwWorkSpaceHeader)) {\r
801 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Workspace invalid, read from backup\n"));\r
802 //\r
803 // Read from spare block\r
804 //\r
805 Length = FtwLiteDevice->FtwWorkSpaceSize;\r
806 Status = FtwLiteDevice->FtwBackupFvb->Read (\r
807 FtwLiteDevice->FtwBackupFvb,\r
808 FtwLiteDevice->FtwSpareLba,\r
809 FtwLiteDevice->FtwWorkSpaceBase,\r
810 &Length,\r
811 FtwLiteDevice->FtwWorkSpace\r
812 );\r
813 ASSERT_EFI_ERROR (Status);\r
814\r
815 //\r
816 // If spare block is valid, then replace working block content.\r
817 //\r
818 if (IsValidWorkSpace (FtwLiteDevice->FtwWorkSpaceHeader)) {\r
819 Status = FlushSpareBlockToWorkingBlock (FtwLiteDevice);\r
820 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Restart working block in Init() - %r\n", Status));\r
821 ASSERT_EFI_ERROR (Status);\r
822\r
823 FtwAbort (FtwLiteDevice);\r
824 //\r
825 // Refresh work space.\r
826 //\r
827 Status = WorkSpaceRefresh (FtwLiteDevice);\r
828 if (EFI_ERROR (Status)) {\r
77980854 829 FreePool (FtwLiteDevice);\r
d7dec593 830 return EFI_ABORTED;\r
831 }\r
832 } else {\r
833 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Both are invalid, init workspace\n"));\r
834 //\r
835 // If both are invalid, then initialize work space.\r
836 //\r
837 SetMem (\r
838 FtwLiteDevice->FtwWorkSpace,\r
839 FtwLiteDevice->FtwWorkSpaceSize,\r
840 FTW_ERASED_BYTE\r
841 );\r
842 InitWorkSpaceHeader (FtwLiteDevice->FtwWorkSpaceHeader);\r
843 //\r
e11ae3a5 844 // Initialize the work space\r
d7dec593 845 //\r
e11ae3a5
LG
846 Status = FtwReclaimWorkSpace (FtwLiteDevice, FALSE);\r
847\r
d7dec593 848 if (EFI_ERROR (Status)) {\r
77980854 849 FreePool (FtwLiteDevice);\r
d7dec593 850 return EFI_ABORTED;\r
851 }\r
852 }\r
853 }\r
854 //\r
855 // Hook the protocol API\r
856 //\r
857 FtwLiteDevice->FtwLiteInstance.Write = FtwLiteWrite;\r
858\r
859 //\r
860 // Install protocol interface\r
861 //\r
862 Status = gBS->InstallProtocolInterface (\r
863 &FtwLiteDevice->Handle,\r
864 &gEfiFaultTolerantWriteLiteProtocolGuid,\r
865 EFI_NATIVE_INTERFACE,\r
866 &FtwLiteDevice->FtwLiteInstance\r
867 );\r
868 if (EFI_ERROR (Status)) {\r
77980854 869 FreePool (FtwLiteDevice);\r
d7dec593 870 return EFI_ABORTED;\r
871 }\r
872 //\r
873 // If (!SpareCompleted) THEN Abort to rollback.\r
874 //\r
875 if ((FtwLiteDevice->FtwLastRecord->WriteAllocated == FTW_VALID_STATE) &&\r
876 (FtwLiteDevice->FtwLastRecord->SpareCompleted != FTW_VALID_STATE)\r
877 ) {\r
878 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Init.. record not SpareCompleted, abort()\n"));\r
879 FtwAbort (FtwLiteDevice);\r
880 }\r
881 //\r
882 // if (SpareCompleted) THEN Restart to fault tolerant write.\r
883 //\r
884 if ((FtwLiteDevice->FtwLastRecord->SpareCompleted == FTW_VALID_STATE) &&\r
885 (FtwLiteDevice->FtwLastRecord->WriteCompleted != FTW_VALID_STATE)\r
886 ) {\r
887\r
888 Status = FtwRestart (FtwLiteDevice);\r
889 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Restart last write - %r\n", Status));\r
890 if (EFI_ERROR (Status)) {\r
891 return Status;\r
892 }\r
893 }\r
894 //\r
895 // To check the workspace buffer behind last records is EMPTY or not.\r
896 // If it's not EMPTY, FTW_LITE also need to call reclaim().\r
897 //\r
898 Record = FtwLiteDevice->FtwLastRecord;\r
899 Offset = (UINT8 *) Record - FtwLiteDevice->FtwWorkSpace;\r
900 if (FtwLiteDevice->FtwWorkSpace[Offset] != FTW_ERASED_BYTE) {\r
5944a83b 901 Offset += FTW_LITE_RECORD_SIZE;\r
d7dec593 902 }\r
903\r
904 if (!IsErasedFlashBuffer (\r
905 FTW_ERASE_POLARITY,\r
906 FtwLiteDevice->FtwWorkSpace + Offset,\r
907 FtwLiteDevice->FtwWorkSpaceSize - Offset\r
908 )) {\r
909 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Workspace is dirty, call reclaim...\n"));\r
e11ae3a5 910 Status = FtwReclaimWorkSpace (FtwLiteDevice, TRUE);\r
d7dec593 911 if (EFI_ERROR (Status)) {\r
912 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Workspace reclaim - %r\n", Status));\r
77980854 913 FreePool (FtwLiteDevice);\r
d7dec593 914 return EFI_ABORTED;\r
915 }\r
916 }\r
917\r
918 return EFI_SUCCESS;\r
919}\r