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