]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/FirmwareVolume/FaultTolerantWriteDxe/FtwLite.c
Correct Compiler option /FAcs to /FAsc
[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
d7dec593 625 if (FtwLiteDevice != NULL) {\r
626 Status = EFI_SUCCESS;\r
627 } else {\r
628 Status = EFI_OUT_OF_RESOURCES;\r
629 }\r
630\r
631 ASSERT_EFI_ERROR (Status);\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->FtwWorkSpaceSize = FTW_WORK_SPACE_SIZE;\r
5944a83b 641 FtwLiteDevice->FtwWorkSpaceBase = FTW_WORK_SPACE_BASE;\r
d7dec593 642 SetMem (\r
643 FtwLiteDevice->FtwWorkSpace,\r
644 FtwLiteDevice->FtwWorkSpaceSize,\r
645 FTW_ERASED_BYTE\r
646 );\r
647 FtwLiteDevice->FtwWorkSpaceHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) FtwLiteDevice->FtwWorkSpace;\r
648\r
649 FtwLiteDevice->FtwLastRecord = NULL;\r
650\r
276be2de 651 FtwLiteDevice->WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwWorkingBase);\r
652 FtwLiteDevice->WorkSpaceLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwWorkingSize);\r
653\r
654 FtwLiteDevice->SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwSpareBase);\r
655 FtwLiteDevice->SpareAreaLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwSpareSize);\r
d7dec593 656\r
657 ASSERT ((FtwLiteDevice->WorkSpaceLength != 0) && (FtwLiteDevice->SpareAreaLength != 0));\r
658\r
659 //\r
660 // Locate FVB protocol\r
661 //\r
662 Status = gBS->LocateHandleBuffer (\r
663 ByProtocol,\r
664 &gEfiFirmwareVolumeBlockProtocolGuid,\r
665 NULL,\r
666 &HandleCount,\r
667 &HandleBuffer\r
668 );\r
669 ASSERT_EFI_ERROR (Status);\r
670\r
671 ASSERT (HandleCount > 0);\r
672\r
673 FtwLiteDevice->FtwFvBlock = NULL;\r
674 FtwLiteDevice->FtwBackupFvb = NULL;\r
675 FtwLiteDevice->FtwWorkSpaceLba = (EFI_LBA) (-1);\r
676 FtwLiteDevice->FtwSpareLba = (EFI_LBA) (-1);\r
677 for (Index = 0; Index < HandleCount; Index += 1) {\r
678 Status = gBS->HandleProtocol (\r
679 HandleBuffer[Index],\r
680 &gEfiFirmwareVolumeBlockProtocolGuid,\r
681 (VOID **) &Fvb\r
682 );\r
683 ASSERT_EFI_ERROR (Status);\r
684\r
685 Status = Fvb->GetPhysicalAddress (Fvb, &BaseAddress);\r
686 if (EFI_ERROR (Status)) {\r
687 continue;\r
688 }\r
689\r
690 FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) BaseAddress);\r
691\r
692 if ((FtwLiteDevice->WorkSpaceAddress >= BaseAddress) &&\r
5944a83b 693 (FtwLiteDevice->WorkSpaceAddress < (BaseAddress + FwVolHeader->FvLength))\r
d7dec593 694 ) {\r
695 FtwLiteDevice->FtwFvBlock = Fvb;\r
696 //\r
697 // To get the LBA of work space\r
698 //\r
699 if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) {\r
700 //\r
701 // FV may have multiple types of BlockLength\r
702 //\r
703 FvbMapEntry = &FwVolHeader->BlockMap[0];\r
704 while (!((FvbMapEntry->NumBlocks == 0) && (FvbMapEntry->Length == 0))) {\r
705 for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) {\r
5944a83b
LG
706 if ((FtwLiteDevice->WorkSpaceAddress >= (BaseAddress + FvbMapEntry->Length * (LbaIndex - 1)))\r
707 && (FtwLiteDevice->WorkSpaceAddress < (BaseAddress + FvbMapEntry->Length * LbaIndex))) {\r
d7dec593 708 FtwLiteDevice->FtwWorkSpaceLba = LbaIndex - 1;\r
709 //\r
710 // Get the Work space size and Base(Offset)\r
711 //\r
712 FtwLiteDevice->FtwWorkSpaceSize = FtwLiteDevice->WorkSpaceLength;\r
713 FtwLiteDevice->FtwWorkSpaceBase = (UINTN) (FtwLiteDevice->WorkSpaceAddress - (BaseAddress + FvbMapEntry->Length * (LbaIndex - 1)));\r
714 break;\r
715 }\r
716 }\r
717 //\r
718 // end for\r
719 //\r
5944a83b
LG
720 if (LbaIndex <= FvbMapEntry->NumBlocks) {\r
721 //\r
722 // Work space range is found.\r
723 //\r
724 break;\r
725 }\r
d7dec593 726 FvbMapEntry++;\r
727 }\r
728 //\r
729 // end while\r
730 //\r
731 }\r
732 }\r
733\r
734 if ((FtwLiteDevice->SpareAreaAddress >= BaseAddress) &&\r
78d6fbd5 735 (FtwLiteDevice->SpareAreaAddress < (BaseAddress + FwVolHeader->FvLength))\r
d7dec593 736 ) {\r
737 FtwLiteDevice->FtwBackupFvb = Fvb;\r
738 //\r
739 // To get the LBA of spare\r
740 //\r
741 if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) {\r
742 //\r
743 // FV may have multiple types of BlockLength\r
744 //\r
745 FvbMapEntry = &FwVolHeader->BlockMap[0];\r
746 while (!((FvbMapEntry->NumBlocks == 0) && (FvbMapEntry->Length == 0))) {\r
747 for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) {\r
5944a83b
LG
748 if ((FtwLiteDevice->SpareAreaAddress >= (BaseAddress + FvbMapEntry->Length * (LbaIndex - 1)))\r
749 && (FtwLiteDevice->SpareAreaAddress < (BaseAddress + FvbMapEntry->Length * LbaIndex))) {\r
d7dec593 750 //\r
751 // Get the NumberOfSpareBlock and SizeOfSpareBlock\r
752 //\r
753 FtwLiteDevice->FtwSpareLba = LbaIndex - 1;\r
754 FtwLiteDevice->SizeOfSpareBlock = FvbMapEntry->Length;\r
755 FtwLiteDevice->NumberOfSpareBlock = FtwLiteDevice->SpareAreaLength / FtwLiteDevice->SizeOfSpareBlock;\r
756 //\r
757 // Check the range of spare area to make sure that it's in FV range\r
758 //\r
759 ASSERT ((FtwLiteDevice->FtwSpareLba + FtwLiteDevice->NumberOfSpareBlock) <= FvbMapEntry->NumBlocks);\r
760 break;\r
761 }\r
762 }\r
5944a83b
LG
763 if (LbaIndex <= FvbMapEntry->NumBlocks) {\r
764 //\r
765 // Spare FV range is found.\r
766 //\r
767 break;\r
768 }\r
d7dec593 769 FvbMapEntry++;\r
770 }\r
771 //\r
772 // end while\r
773 //\r
774 }\r
775 }\r
776 }\r
777 //\r
778 // Calculate the start LBA of working block. Working block is an area which\r
779 // contains working space in its last block and has the same size as spare\r
780 // block, unless there are not enough blocks before the block that contains\r
781 // working space.\r
782 //\r
783 FtwLiteDevice->FtwWorkBlockLba = FtwLiteDevice->FtwWorkSpaceLba - FtwLiteDevice->NumberOfSpareBlock + 1;\r
784 if ((INT64) (FtwLiteDevice->FtwWorkBlockLba) < 0) {\r
785 FtwLiteDevice->FtwWorkBlockLba = 0;\r
786 }\r
787\r
788 if ((FtwLiteDevice->FtwFvBlock == NULL) ||\r
789 (FtwLiteDevice->FtwBackupFvb == NULL) ||\r
790 (FtwLiteDevice->FtwWorkSpaceLba == (EFI_LBA) (-1)) ||\r
791 (FtwLiteDevice->FtwSpareLba == (EFI_LBA) (-1))\r
792 ) {\r
793 DEBUG ((EFI_D_ERROR, "FtwLite: Working or spare FVB not ready\n"));\r
794 ASSERT_EFI_ERROR (Status);\r
795 }\r
796 //\r
797 // Refresh workspace data from working block\r
798 //\r
799 Status = WorkSpaceRefresh (FtwLiteDevice);\r
800 ASSERT_EFI_ERROR (Status);\r
801\r
802 //\r
803 // If the working block workspace is not valid, try the spare block\r
804 //\r
805 if (!IsValidWorkSpace (FtwLiteDevice->FtwWorkSpaceHeader)) {\r
806 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Workspace invalid, read from backup\n"));\r
807 //\r
808 // Read from spare block\r
809 //\r
810 Length = FtwLiteDevice->FtwWorkSpaceSize;\r
811 Status = FtwLiteDevice->FtwBackupFvb->Read (\r
812 FtwLiteDevice->FtwBackupFvb,\r
813 FtwLiteDevice->FtwSpareLba,\r
814 FtwLiteDevice->FtwWorkSpaceBase,\r
815 &Length,\r
816 FtwLiteDevice->FtwWorkSpace\r
817 );\r
818 ASSERT_EFI_ERROR (Status);\r
819\r
820 //\r
821 // If spare block is valid, then replace working block content.\r
822 //\r
823 if (IsValidWorkSpace (FtwLiteDevice->FtwWorkSpaceHeader)) {\r
824 Status = FlushSpareBlockToWorkingBlock (FtwLiteDevice);\r
825 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Restart working block in Init() - %r\n", Status));\r
826 ASSERT_EFI_ERROR (Status);\r
827\r
828 FtwAbort (FtwLiteDevice);\r
829 //\r
830 // Refresh work space.\r
831 //\r
832 Status = WorkSpaceRefresh (FtwLiteDevice);\r
833 if (EFI_ERROR (Status)) {\r
834 return EFI_ABORTED;\r
835 }\r
836 } else {\r
837 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Both are invalid, init workspace\n"));\r
838 //\r
839 // If both are invalid, then initialize work space.\r
840 //\r
841 SetMem (\r
842 FtwLiteDevice->FtwWorkSpace,\r
843 FtwLiteDevice->FtwWorkSpaceSize,\r
844 FTW_ERASED_BYTE\r
845 );\r
846 InitWorkSpaceHeader (FtwLiteDevice->FtwWorkSpaceHeader);\r
847 //\r
e11ae3a5 848 // Initialize the work space\r
d7dec593 849 //\r
e11ae3a5
LG
850 Status = FtwReclaimWorkSpace (FtwLiteDevice, FALSE);\r
851\r
d7dec593 852 if (EFI_ERROR (Status)) {\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 return EFI_ABORTED;\r
873 }\r
874 //\r
875 // If (!SpareCompleted) THEN Abort to rollback.\r
876 //\r
877 if ((FtwLiteDevice->FtwLastRecord->WriteAllocated == FTW_VALID_STATE) &&\r
878 (FtwLiteDevice->FtwLastRecord->SpareCompleted != FTW_VALID_STATE)\r
879 ) {\r
880 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Init.. record not SpareCompleted, abort()\n"));\r
881 FtwAbort (FtwLiteDevice);\r
882 }\r
883 //\r
884 // if (SpareCompleted) THEN Restart to fault tolerant write.\r
885 //\r
886 if ((FtwLiteDevice->FtwLastRecord->SpareCompleted == FTW_VALID_STATE) &&\r
887 (FtwLiteDevice->FtwLastRecord->WriteCompleted != FTW_VALID_STATE)\r
888 ) {\r
889\r
890 Status = FtwRestart (FtwLiteDevice);\r
891 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Restart last write - %r\n", Status));\r
892 if (EFI_ERROR (Status)) {\r
893 return Status;\r
894 }\r
895 }\r
896 //\r
897 // To check the workspace buffer behind last records is EMPTY or not.\r
898 // If it's not EMPTY, FTW_LITE also need to call reclaim().\r
899 //\r
900 Record = FtwLiteDevice->FtwLastRecord;\r
901 Offset = (UINT8 *) Record - FtwLiteDevice->FtwWorkSpace;\r
902 if (FtwLiteDevice->FtwWorkSpace[Offset] != FTW_ERASED_BYTE) {\r
5944a83b 903 Offset += FTW_LITE_RECORD_SIZE;\r
d7dec593 904 }\r
905\r
906 if (!IsErasedFlashBuffer (\r
907 FTW_ERASE_POLARITY,\r
908 FtwLiteDevice->FtwWorkSpace + Offset,\r
909 FtwLiteDevice->FtwWorkSpaceSize - Offset\r
910 )) {\r
911 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Workspace is dirty, call reclaim...\n"));\r
e11ae3a5 912 Status = FtwReclaimWorkSpace (FtwLiteDevice, TRUE);\r
d7dec593 913 if (EFI_ERROR (Status)) {\r
914 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Workspace reclaim - %r\n", Status));\r
915 return EFI_ABORTED;\r
916 }\r
917 }\r
918\r
919 return EFI_SUCCESS;\r
920}\r