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