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