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