1 /** @file
2
3 Internal generic functions to operate flash block.
4
5 Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 **/
15
16 #include "FaultTolerantWrite.h"
17
18 /**
19
20 Check whether a flash buffer is erased.
21
22 @param Buffer Buffer to check
23 @param BufferSize Size of the buffer
24
25 @return A BOOLEAN value indicating erased or not.
26
27 **/
28 BOOLEAN
IsErasedFlashBuffer(IN UINT8 * Buffer,IN UINTN BufferSize)29 IsErasedFlashBuffer (
30 IN UINT8 *Buffer,
31 IN UINTN BufferSize
32 )
33 {
34 BOOLEAN IsEmpty;
35 UINT8 *Ptr;
36 UINTN Index;
37
38 Ptr = Buffer;
39 IsEmpty = TRUE;
40 for (Index = 0; Index < BufferSize; Index += 1) {
41 if (*Ptr++ != FTW_ERASED_BYTE) {
42 IsEmpty = FALSE;
43 break;
44 }
45 }
46
47 return IsEmpty;
48 }
49
50 /**
51 To erase the block with specified blocks.
52
53
54 @param FtwDevice The private data of FTW driver
55 @param FvBlock FVB Protocol interface
56 @param Lba Lba of the firmware block
57 @param NumberOfBlocks The number of consecutive blocks starting with Lba
58
59 @retval EFI_SUCCESS Block LBA is Erased successfully
60 @retval Others Error occurs
61
62 **/
63 EFI_STATUS
FtwEraseBlock(IN EFI_FTW_DEVICE * FtwDevice,EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL * FvBlock,EFI_LBA Lba,UINTN NumberOfBlocks)64 FtwEraseBlock (
65 IN EFI_FTW_DEVICE *FtwDevice,
66 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
67 EFI_LBA Lba,
68 UINTN NumberOfBlocks
69 )
70 {
71 return FvBlock->EraseBlocks (
72 FvBlock,
73 Lba,
74 NumberOfBlocks,
75 EFI_LBA_LIST_TERMINATOR
76 );
77 }
78
79 /**
80 Erase spare block.
81
82 @param FtwDevice The private data of FTW driver
83
84 @retval EFI_SUCCESS The erase request was successfully completed.
85 @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state.
86 @retval EFI_DEVICE_ERROR The block device is not functioning
87 correctly and could not be written.
88 The firmware device may have been
89 partially erased.
90 @retval EFI_INVALID_PARAMETER One or more of the LBAs listed
91 in the variable argument list do
92 not exist in the firmware volume.
93
94
95 **/
96 EFI_STATUS
FtwEraseSpareBlock(IN EFI_FTW_DEVICE * FtwDevice)97 FtwEraseSpareBlock (
98 IN EFI_FTW_DEVICE *FtwDevice
99 )
100 {
101 return FtwDevice->FtwBackupFvb->EraseBlocks (
102 FtwDevice->FtwBackupFvb,
103 FtwDevice->FtwSpareLba,
104 FtwDevice->NumberOfSpareBlock,
105 EFI_LBA_LIST_TERMINATOR
106 );
107 }
108
109 /**
110
111 Is it in working block?
112
113 @param FtwDevice The private data of FTW driver
114 @param FvBlock Fvb protocol instance
115 @param Lba The block specified
116
117 @return A BOOLEAN value indicating in working block or not.
118
119 **/
120 BOOLEAN
IsWorkingBlock(EFI_FTW_DEVICE * FtwDevice,EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL * FvBlock,EFI_LBA Lba)121 IsWorkingBlock (
122 EFI_FTW_DEVICE *FtwDevice,
123 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
124 EFI_LBA Lba
125 )
126 {
127 //
128 // If matching the following condition, the target block is in working block.
129 // 1. Target block is on the FV of working block (Using the same FVB protocol instance).
130 // 2. Lba falls into the range of working block.
131 //
132 return (BOOLEAN)
133 (
134 (FvBlock == FtwDevice->FtwFvBlock) &&
135 (Lba >= FtwDevice->FtwWorkBlockLba) &&
136 (Lba <= FtwDevice->FtwWorkSpaceLba)
137 );
138 }
139
140 /**
141
142 Get firmware volume block by address.
143
144
145 @param Address Address specified the block
146 @param FvBlock The block caller wanted
147
148 @retval EFI_SUCCESS The protocol instance if found.
149 @retval EFI_NOT_FOUND Block not found
150
151 **/
152 EFI_HANDLE
GetFvbByAddress(IN EFI_PHYSICAL_ADDRESS Address,OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL ** FvBlock)153 GetFvbByAddress (
154 IN EFI_PHYSICAL_ADDRESS Address,
155 OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock
156 )
157 {
158 EFI_STATUS Status;
159 EFI_HANDLE *HandleBuffer;
160 UINTN HandleCount;
161 UINTN Index;
162 EFI_PHYSICAL_ADDRESS FvbBaseAddress;
163 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
164 EFI_HANDLE FvbHandle;
165 UINTN BlockSize;
166 UINTN NumberOfBlocks;
167
168 *FvBlock = NULL;
169 FvbHandle = NULL;
170 HandleBuffer = NULL;
171 //
172 // Locate all handles of Fvb protocol
173 //
174 Status = GetFvbCountAndBuffer (&HandleCount, &HandleBuffer);
175 if (EFI_ERROR (Status)) {
176 return NULL;
177 }
178 //
179 // Get the FVB to access variable store
180 //
181 for (Index = 0; Index < HandleCount; Index += 1) {
182 Status = FtwGetFvbByHandle (HandleBuffer[Index], &Fvb);
183 if (EFI_ERROR (Status)) {
184 break;
185 }
186 //
187 // Compare the address and select the right one
188 //
189 Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress);
190 if (EFI_ERROR (Status)) {
191 continue;
192 }
193
194 //
195 // Now, one FVB has one type of BlockSize
196 //
197 Status = Fvb->GetBlockSize (Fvb, 0, &BlockSize, &NumberOfBlocks);
198 if (EFI_ERROR (Status)) {
199 continue;
200 }
201
202 if ((Address >= FvbBaseAddress) && (Address < (FvbBaseAddress + BlockSize * NumberOfBlocks))) {
203 *FvBlock = Fvb;
204 FvbHandle = HandleBuffer[Index];
205 break;
206 }
207 }
208
209 FreePool (HandleBuffer);
210 return FvbHandle;
211 }
212
213 /**
214
215 Is it in boot block?
216
217 @param FtwDevice The private data of FTW driver
218 @param FvBlock Fvb protocol instance
219
220 @return A BOOLEAN value indicating in boot block or not.
221
222 **/
223 BOOLEAN
IsBootBlock(EFI_FTW_DEVICE * FtwDevice,EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL * FvBlock)224 IsBootBlock (
225 EFI_FTW_DEVICE *FtwDevice,
226 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock
227 )
228 {
229 EFI_STATUS Status;
230 EFI_SWAP_ADDRESS_RANGE_PROTOCOL *SarProtocol;
231 EFI_PHYSICAL_ADDRESS BootBlockBase;
232 UINTN BootBlockSize;
233 EFI_PHYSICAL_ADDRESS BackupBlockBase;
234 UINTN BackupBlockSize;
235 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *BootFvb;
236 BOOLEAN IsSwapped;
237 EFI_HANDLE FvbHandle;
238
239 if (!FeaturePcdGet(PcdFullFtwServiceEnable)) {
240 return FALSE;
241 }
242
243 Status = FtwGetSarProtocol ((VOID **) &SarProtocol);
244 if (EFI_ERROR (Status)) {
245 return FALSE;
246 }
247 //
248 // Get the boot block range
249 //
250 Status = SarProtocol->GetRangeLocation (
251 SarProtocol,
252 &BootBlockBase,
253 &BootBlockSize,
254 &BackupBlockBase,
255 &BackupBlockSize
256 );
257 if (EFI_ERROR (Status)) {
258 return FALSE;
259 }
260
261 Status = SarProtocol->GetSwapState (SarProtocol, &IsSwapped);
262 if (EFI_ERROR (Status)) {
263 return FALSE;
264 }
265 //
266 // Get FVB by address
267 //
268 if (!IsSwapped) {
269 FvbHandle = GetFvbByAddress (BootBlockBase, &BootFvb);
270 } else {
271 FvbHandle = GetFvbByAddress (BackupBlockBase, &BootFvb);
272 }
273
274 if (FvbHandle == NULL) {
275 return FALSE;
276 }
277 //
278 // Compare the Fvb
279 //
280 return (BOOLEAN) (FvBlock == BootFvb);
281 }
282
283 /**
284 Copy the content of spare block to a boot block. Size is FTW_BLOCK_SIZE.
285 Spare block is accessed by FTW working FVB protocol interface.
286 Target block is accessed by FvBlock protocol interface.
287
288 FTW will do extra work on boot block update.
289 FTW should depend on a protocol of EFI_ADDRESS_RANGE_SWAP_PROTOCOL,
290 which is produced by a chipset driver.
291 FTW updating boot block steps may be:
292 1. GetRangeLocation(), if the Range is inside the boot block, FTW know
293 that boot block will be update. It shall add a FLAG in the working block.
294 2. When spare block is ready,
295 3. SetSwapState(SWAPPED)
296 4. erasing boot block,
297 5. programming boot block until the boot block is ok.
298 6. SetSwapState(UNSWAPPED)
299 FTW shall not allow to update boot block when battery state is error.
300
301 @param FtwDevice The private data of FTW driver
302
303 @retval EFI_SUCCESS Spare block content is copied to boot block
304 @retval EFI_INVALID_PARAMETER Input parameter error
305 @retval EFI_OUT_OF_RESOURCES Allocate memory error
306 @retval EFI_ABORTED The function could not complete successfully
307
308 **/
309 EFI_STATUS
FlushSpareBlockToBootBlock(EFI_FTW_DEVICE * FtwDevice)310 FlushSpareBlockToBootBlock (
311 EFI_FTW_DEVICE *FtwDevice
312 )
313 {
314 EFI_STATUS Status;
315 UINTN Length;
316 UINT8 *Buffer;
317 UINTN Count;
318 UINT8 *Ptr;
319 UINTN Index;
320 BOOLEAN TopSwap;
321 EFI_SWAP_ADDRESS_RANGE_PROTOCOL *SarProtocol;
322 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *BootFvb;
323 EFI_LBA BootLba;
324
325 if (!FeaturePcdGet(PcdFullFtwServiceEnable)) {
326 return EFI_UNSUPPORTED;
327 }
328
329 //
330 // Locate swap address range protocol
331 //
332 Status = FtwGetSarProtocol ((VOID **) &SarProtocol);
333 if (EFI_ERROR (Status)) {
334 return Status;
335 }
336 //
337 // Allocate a memory buffer
338 //
339 Length = FtwDevice->SpareAreaLength;
340 Buffer = AllocatePool (Length);
341 if (Buffer == NULL) {
342 return EFI_OUT_OF_RESOURCES;
343 }
344 //
345 // Get TopSwap bit state
346 //
347 Status = SarProtocol->GetSwapState (SarProtocol, &TopSwap);
348 if (EFI_ERROR (Status)) {
349 DEBUG ((EFI_D_ERROR, "Ftw: Get Top Swapped status - %r\n", Status));
350 FreePool (Buffer);
351 return EFI_ABORTED;
352 }
353
354 if (TopSwap) {
355 //
356 // Get FVB of current boot block
357 //
358 if (GetFvbByAddress (FtwDevice->SpareAreaAddress + FtwDevice->SpareAreaLength, &BootFvb) == NULL) {
359 FreePool (Buffer);
360 return EFI_ABORTED;
361 }
362 //
363 // Read data from current boot block
364 //
365 BootLba = 0;
366 Ptr = Buffer;
367 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
368 Count = FtwDevice->SpareBlockSize;
369 Status = BootFvb->Read (
370 BootFvb,
371 BootLba + Index,
372 0,
373 &Count,
374 Ptr
375 );
376 if (EFI_ERROR (Status)) {
377 FreePool (Buffer);
378 return Status;
379 }
380
381 Ptr += Count;
382 }
383 } else {
384 //
385 // Read data from spare block
386 //
387 Ptr = Buffer;
388 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
389 Count = FtwDevice->SpareBlockSize;
390 Status = FtwDevice->FtwBackupFvb->Read (
391 FtwDevice->FtwBackupFvb,
392 FtwDevice->FtwSpareLba + Index,
393 0,
394 &Count,
395 Ptr
396 );
397 if (EFI_ERROR (Status)) {
398 FreePool (Buffer);
399 return Status;
400 }
401
402 Ptr += Count;
403 }
404 //
405 // Set TopSwap bit
406 //
407 Status = SarProtocol->SetSwapState (SarProtocol, TRUE);
408 if (EFI_ERROR (Status)) {
409 FreePool (Buffer);
410 return Status;
411 }
412 }
413 //
414 // Erase current spare block
415 // Because TopSwap is set, this actually erase the top block (boot block)!
416 //
417 Status = FtwEraseSpareBlock (FtwDevice);
418 if (EFI_ERROR (Status)) {
419 FreePool (Buffer);
420 return EFI_ABORTED;
421 }
422 //
423 // Write memory buffer to current spare block. Still top block.
424 //
425 Ptr = Buffer;
426 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
427 Count = FtwDevice->SpareBlockSize;
428 Status = FtwDevice->FtwBackupFvb->Write (
429 FtwDevice->FtwBackupFvb,
430 FtwDevice->FtwSpareLba + Index,
431 0,
432 &Count,
433 Ptr
434 );
435 if (EFI_ERROR (Status)) {
436 DEBUG ((EFI_D_ERROR, "Ftw: FVB Write boot block - %r\n", Status));
437 FreePool (Buffer);
438 return Status;
439 }
440
441 Ptr += Count;
442 }
443
444 FreePool (Buffer);
445
446 //
447 // Clear TopSwap bit
448 //
449 Status = SarProtocol->SetSwapState (SarProtocol, FALSE);
450
451 return Status;
452 }
453
454 /**
455 Copy the content of spare block to a target block.
456 Spare block is accessed by FTW backup FVB protocol interface.
457 Target block is accessed by FvBlock protocol interface.
458
459
460 @param FtwDevice The private data of FTW driver
461 @param FvBlock FVB Protocol interface to access target block
462 @param Lba Lba of the target block
463 @param BlockSize The size of the block
464 @param NumberOfBlocks The number of consecutive blocks starting with Lba
465
466 @retval EFI_SUCCESS Spare block content is copied to target block
467 @retval EFI_INVALID_PARAMETER Input parameter error
468 @retval EFI_OUT_OF_RESOURCES Allocate memory error
469 @retval EFI_ABORTED The function could not complete successfully
470
471 **/
472 EFI_STATUS
FlushSpareBlockToTargetBlock(EFI_FTW_DEVICE * FtwDevice,EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL * FvBlock,EFI_LBA Lba,UINTN BlockSize,UINTN NumberOfBlocks)473 FlushSpareBlockToTargetBlock (
474 EFI_FTW_DEVICE *FtwDevice,
475 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
476 EFI_LBA Lba,
477 UINTN BlockSize,
478 UINTN NumberOfBlocks
479 )
480 {
481 EFI_STATUS Status;
482 UINTN Length;
483 UINT8 *Buffer;
484 UINTN Count;
485 UINT8 *Ptr;
486 UINTN Index;
487
488 if ((FtwDevice == NULL) || (FvBlock == NULL)) {
489 return EFI_INVALID_PARAMETER;
490 }
491 //
492 // Allocate a memory buffer
493 //
494 Length = FtwDevice->SpareAreaLength;
495 Buffer = AllocatePool (Length);
496 if (Buffer == NULL) {
497 return EFI_OUT_OF_RESOURCES;
498 }
499 //
500 // Read all content of spare block to memory buffer
501 //
502 Ptr = Buffer;
503 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
504 Count = FtwDevice->SpareBlockSize;
505 Status = FtwDevice->FtwBackupFvb->Read (
506 FtwDevice->FtwBackupFvb,
507 FtwDevice->FtwSpareLba + Index,
508 0,
509 &Count,
510 Ptr
511 );
512 if (EFI_ERROR (Status)) {
513 FreePool (Buffer);
514 return Status;
515 }
516
517 Ptr += Count;
518 }
519 //
520 // Erase the target block
521 //
522 Status = FtwEraseBlock (FtwDevice, FvBlock, Lba, NumberOfBlocks);
523 if (EFI_ERROR (Status)) {
524 FreePool (Buffer);
525 return EFI_ABORTED;
526 }
527 //
528 // Write memory buffer to block, using the FvBlock protocol interface
529 //
530 Ptr = Buffer;
531 for (Index = 0; Index < NumberOfBlocks; Index += 1) {
532 Count = BlockSize;
533 Status = FvBlock->Write (FvBlock, Lba + Index, 0, &Count, Ptr);
534 if (EFI_ERROR (Status)) {
535 DEBUG ((EFI_D_ERROR, "Ftw: FVB Write block - %r\n", Status));
536 FreePool (Buffer);
537 return Status;
538 }
539
540 Ptr += Count;
541 }
542
543 FreePool (Buffer);
544
545 return Status;
546 }
547
548 /**
549 Copy the content of spare block to working block. Size is FTW_BLOCK_SIZE.
550 Spare block is accessed by FTW backup FVB protocol interface. LBA is
551 FtwDevice->FtwSpareLba.
552 Working block is accessed by FTW working FVB protocol interface. LBA is
553 FtwDevice->FtwWorkBlockLba.
554
555 Since the working block header is important when FTW initializes, the
556 state of the operation should be handled carefully. The Crc value is
557 calculated without STATE element.
558
559 @param FtwDevice The private data of FTW driver
560
561 @retval EFI_SUCCESS Spare block content is copied to target block
562 @retval EFI_OUT_OF_RESOURCES Allocate memory error
563 @retval EFI_ABORTED The function could not complete successfully
564
565 **/
566 EFI_STATUS
FlushSpareBlockToWorkingBlock(EFI_FTW_DEVICE * FtwDevice)567 FlushSpareBlockToWorkingBlock (
568 EFI_FTW_DEVICE *FtwDevice
569 )
570 {
571 EFI_STATUS Status;
572 UINTN Length;
573 UINT8 *Buffer;
574 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader;
575 UINTN Count;
576 UINT8 *Ptr;
577 UINTN Index;
578
579 //
580 // Allocate a memory buffer
581 //
582 Length = FtwDevice->SpareAreaLength;
583 Buffer = AllocatePool (Length);
584 if (Buffer == NULL) {
585 return EFI_OUT_OF_RESOURCES;
586 }
587
588 //
589 // To guarantee that the WorkingBlockValid is set on spare block
590 //
591 // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
592 // WorkingBlockValid);
593 // To skip Signature and Crc: sizeof(EFI_GUID)+sizeof(UINT32).
594 //
595 FtwUpdateFvState (
596 FtwDevice->FtwBackupFvb,
597 FtwDevice->SpareBlockSize,
598 FtwDevice->FtwSpareLba + FtwDevice->FtwWorkSpaceLbaInSpare,
599 FtwDevice->FtwWorkSpaceBaseInSpare + sizeof (EFI_GUID) + sizeof (UINT32),
600 WORKING_BLOCK_VALID
601 );
602 //
603 // Read from spare block to memory buffer
604 //
605 Ptr = Buffer;
606 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
607 Count = FtwDevice->SpareBlockSize;
608 Status = FtwDevice->FtwBackupFvb->Read (
609 FtwDevice->FtwBackupFvb,
610 FtwDevice->FtwSpareLba + Index,
611 0,
612 &Count,
613 Ptr
614 );
615 if (EFI_ERROR (Status)) {
616 FreePool (Buffer);
617 return Status;
618 }
619
620 Ptr += Count;
621 }
622 //
623 // Clear the CRC and STATE, copy data from spare to working block.
624 //
625 WorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (Buffer + (UINTN) FtwDevice->FtwWorkSpaceLbaInSpare * FtwDevice->SpareBlockSize + FtwDevice->FtwWorkSpaceBaseInSpare);
626 InitWorkSpaceHeader (WorkingBlockHeader);
627 WorkingBlockHeader->WorkingBlockValid = FTW_ERASE_POLARITY;
628 WorkingBlockHeader->WorkingBlockInvalid = FTW_ERASE_POLARITY;
629
630 //
631 // target block is working block, then
632 // Set WorkingBlockInvalid in EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
633 // before erase the working block.
634 //
635 // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
636 // WorkingBlockInvalid);
637 // So hardcode offset as sizeof(EFI_GUID)+sizeof(UINT32) to
638 // skip Signature and Crc.
639 //
640 Status = FtwUpdateFvState (
641 FtwDevice->FtwFvBlock,
642 FtwDevice->WorkBlockSize,
643 FtwDevice->FtwWorkSpaceLba,
644 FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),
645 WORKING_BLOCK_INVALID
646 );
647 if (EFI_ERROR (Status)) {
648 FreePool (Buffer);
649 return EFI_ABORTED;
650 }
651
652 FtwDevice->FtwWorkSpaceHeader->WorkingBlockInvalid = FTW_VALID_STATE;
653
654 //
655 // Erase the working block
656 //
657 Status = FtwEraseBlock (FtwDevice, FtwDevice->FtwFvBlock, FtwDevice->FtwWorkBlockLba, FtwDevice->NumberOfWorkBlock);
658 if (EFI_ERROR (Status)) {
659 FreePool (Buffer);
660 return EFI_ABORTED;
661 }
662 //
663 // Write memory buffer to working block, using the FvBlock protocol interface
664 //
665 Ptr = Buffer;
666 for (Index = 0; Index < FtwDevice->NumberOfWorkBlock; Index += 1) {
667 Count = FtwDevice->WorkBlockSize;
668 Status = FtwDevice->FtwFvBlock->Write (
669 FtwDevice->FtwFvBlock,
670 FtwDevice->FtwWorkBlockLba + Index,
671 0,
672 &Count,
673 Ptr
674 );
675 if (EFI_ERROR (Status)) {
676 DEBUG ((EFI_D_ERROR, "Ftw: FVB Write block - %r\n", Status));
677 FreePool (Buffer);
678 return Status;
679 }
680
681 Ptr += Count;
682 }
683 //
684 // Since the memory buffer will not be used, free memory Buffer.
685 //
686 FreePool (Buffer);
687
688 //
689 // Update the VALID of the working block
690 //
691 // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER, WorkingBlockValid);
692 // So hardcode offset as sizeof(EFI_GUID)+sizeof(UINT32) to skip Signature and Crc.
693 //
694 Status = FtwUpdateFvState (
695 FtwDevice->FtwFvBlock,
696 FtwDevice->WorkBlockSize,
697 FtwDevice->FtwWorkSpaceLba,
698 FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),
699 WORKING_BLOCK_VALID
700 );
701 if (EFI_ERROR (Status)) {
702 return EFI_ABORTED;
703 }
704
705 FtwDevice->FtwWorkSpaceHeader->WorkingBlockInvalid = FTW_INVALID_STATE;
706 FtwDevice->FtwWorkSpaceHeader->WorkingBlockValid = FTW_VALID_STATE;
707
708 return EFI_SUCCESS;
709 }
710
711 /**
712 Update a bit of state on a block device. The location of the bit is
713 calculated by the (Lba, Offset, bit). Here bit is determined by the
714 the name of a certain bit.
715
716
717 @param FvBlock FVB Protocol interface to access SrcBlock and DestBlock
718 @param BlockSize The size of the block
719 @param Lba Lba of a block
720 @param Offset Offset on the Lba
721 @param NewBit New value that will override the old value if it can be change
722
723 @retval EFI_SUCCESS A state bit has been updated successfully
724 @retval Others Access block device error.
725 Notes:
726 Assume all bits of State are inside the same BYTE.
727 @retval EFI_ABORTED Read block fail
728
729 **/
730 EFI_STATUS
FtwUpdateFvState(IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL * FvBlock,IN UINTN BlockSize,IN EFI_LBA Lba,IN UINTN Offset,IN UINT8 NewBit)731 FtwUpdateFvState (
732 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
733 IN UINTN BlockSize,
734 IN EFI_LBA Lba,
735 IN UINTN Offset,
736 IN UINT8 NewBit
737 )
738 {
739 EFI_STATUS Status;
740 UINT8 State;
741 UINTN Length;
742
743 //
744 // Calculate the real Offset and Lba to write.
745 //
746 while (Offset >= BlockSize) {
747 Offset -= BlockSize;
748 Lba++;
749 }
750
751 //
752 // Read state from device, assume State is only one byte.
753 //
754 Length = sizeof (UINT8);
755 Status = FvBlock->Read (FvBlock, Lba, Offset, &Length, &State);
756 if (EFI_ERROR (Status)) {
757 return EFI_ABORTED;
758 }
759
760 State ^= FTW_POLARITY_REVERT;
761 State = (UINT8) (State | NewBit);
762 State ^= FTW_POLARITY_REVERT;
763
764 //
765 // Write state back to device
766 //
767 Length = sizeof (UINT8);
768 Status = FvBlock->Write (FvBlock, Lba, Offset, &Length, &State);
769
770 return Status;
771 }
772
773 /**
774 Get the last Write Header pointer.
775 The last write header is the header whose 'complete' state hasn't been set.
776 After all, this header may be a EMPTY header entry for next Allocate.
777
778
779 @param FtwWorkSpaceHeader Pointer of the working block header
780 @param FtwWorkSpaceSize Size of the work space
781 @param FtwWriteHeader Pointer to retrieve the last write header
782
783 @retval EFI_SUCCESS Get the last write record successfully
784 @retval EFI_ABORTED The FTW work space is damaged
785
786 **/
787 EFI_STATUS
FtwGetLastWriteHeader(IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER * FtwWorkSpaceHeader,IN UINTN FtwWorkSpaceSize,OUT EFI_FAULT_TOLERANT_WRITE_HEADER ** FtwWriteHeader)788 FtwGetLastWriteHeader (
789 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *FtwWorkSpaceHeader,
790 IN UINTN FtwWorkSpaceSize,
791 OUT EFI_FAULT_TOLERANT_WRITE_HEADER **FtwWriteHeader
792 )
793 {
794 UINTN Offset;
795 EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader;
796
797 *FtwWriteHeader = NULL;
798 FtwHeader = (EFI_FAULT_TOLERANT_WRITE_HEADER *) (FtwWorkSpaceHeader + 1);
799 Offset = sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER);
800
801 while (FtwHeader->Complete == FTW_VALID_STATE) {
802 Offset += FTW_WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites, FtwHeader->PrivateDataSize);
803 //
804 // If Offset exceed the FTW work space boudary, return error.
805 //
806 if (Offset >= FtwWorkSpaceSize) {
807 *FtwWriteHeader = FtwHeader;
808 return EFI_ABORTED;
809 }
810
811 FtwHeader = (EFI_FAULT_TOLERANT_WRITE_HEADER *) ((UINT8 *) FtwWorkSpaceHeader + Offset);
812 }
813 //
814 // Last write header is found
815 //
816 *FtwWriteHeader = FtwHeader;
817
818 return EFI_SUCCESS;
819 }
820
821 /**
822 Get the last Write Record pointer. The last write Record is the Record
823 whose DestinationCompleted state hasn't been set. After all, this Record
824 may be a EMPTY record entry for next write.
825
826
827 @param FtwWriteHeader Pointer to the write record header
828 @param FtwWriteRecord Pointer to retrieve the last write record
829
830 @retval EFI_SUCCESS Get the last write record successfully
831 @retval EFI_ABORTED The FTW work space is damaged
832
833 **/
834 EFI_STATUS
FtwGetLastWriteRecord(IN EFI_FAULT_TOLERANT_WRITE_HEADER * FtwWriteHeader,OUT EFI_FAULT_TOLERANT_WRITE_RECORD ** FtwWriteRecord)835 FtwGetLastWriteRecord (
836 IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwWriteHeader,
837 OUT EFI_FAULT_TOLERANT_WRITE_RECORD **FtwWriteRecord
838 )
839 {
840 UINTN Index;
841 EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord;
842
843 *FtwWriteRecord = NULL;
844 FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) (FtwWriteHeader + 1);
845
846 //
847 // Try to find the last write record "that has not completed"
848 //
849 for (Index = 0; Index < FtwWriteHeader->NumberOfWrites; Index += 1) {
850 if (FtwRecord->DestinationComplete != FTW_VALID_STATE) {
851 //
852 // The last write record is found
853 //
854 *FtwWriteRecord = FtwRecord;
855 return EFI_SUCCESS;
856 }
857
858 FtwRecord++;
859
860 if (FtwWriteHeader->PrivateDataSize != 0) {
861 FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) ((UINTN) FtwRecord + (UINTN) FtwWriteHeader->PrivateDataSize);
862 }
863 }
864 //
865 // if Index == NumberOfWrites, then
866 // the last record has been written successfully,
867 // but the Header->Complete Flag has not been set.
868 // also return the last record.
869 //
870 if (Index == FtwWriteHeader->NumberOfWrites) {
871 *FtwWriteRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) ((UINTN) FtwRecord - FTW_RECORD_SIZE (FtwWriteHeader->PrivateDataSize));
872 return EFI_SUCCESS;
873 }
874
875 return EFI_ABORTED;
876 }
877
878 /**
879 To check if FtwRecord is the first record of FtwHeader.
880
881 @param FtwHeader Pointer to the write record header
882 @param FtwRecord Pointer to the write record
883
884 @retval TRUE FtwRecord is the first Record of the FtwHeader
885 @retval FALSE FtwRecord is not the first Record of the FtwHeader
886
887 **/
888 BOOLEAN
IsFirstRecordOfWrites(IN EFI_FAULT_TOLERANT_WRITE_HEADER * FtwHeader,IN EFI_FAULT_TOLERANT_WRITE_RECORD * FtwRecord)889 IsFirstRecordOfWrites (
890 IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader,
891 IN EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord
892 )
893 {
894 UINT8 *Head;
895 UINT8 *Ptr;
896
897 Head = (UINT8 *) FtwHeader;
898 Ptr = (UINT8 *) FtwRecord;
899
900 Head += sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER);
901 return (BOOLEAN) (Head == Ptr);
902 }
903
904 /**
905 To check if FtwRecord is the last record of FtwHeader. Because the
906 FtwHeader has NumberOfWrites & PrivateDataSize, the FtwRecord can be
907 determined if it is the last record of FtwHeader.
908
909 @param FtwHeader Pointer to the write record header
910 @param FtwRecord Pointer to the write record
911
912 @retval TRUE FtwRecord is the last Record of the FtwHeader
913 @retval FALSE FtwRecord is not the last Record of the FtwHeader
914
915 **/
916 BOOLEAN
IsLastRecordOfWrites(IN EFI_FAULT_TOLERANT_WRITE_HEADER * FtwHeader,IN EFI_FAULT_TOLERANT_WRITE_RECORD * FtwRecord)917 IsLastRecordOfWrites (
918 IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader,
919 IN EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord
920 )
921 {
922 UINT8 *Head;
923 UINT8 *Ptr;
924
925 Head = (UINT8 *) FtwHeader;
926 Ptr = (UINT8 *) FtwRecord;
927
928 Head += FTW_WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites - 1, FtwHeader->PrivateDataSize);
929 return (BOOLEAN) (Head == Ptr);
930 }
931
932 /**
933 To check if FtwRecord is the first record of FtwHeader.
934
935 @param FtwHeader Pointer to the write record header
936 @param FtwRecord Pointer to retrieve the previous write record
937
938 @retval EFI_ACCESS_DENIED Input record is the first record, no previous record is return.
939 @retval EFI_SUCCESS The previous write record is found.
940
941 **/
942 EFI_STATUS
GetPreviousRecordOfWrites(IN EFI_FAULT_TOLERANT_WRITE_HEADER * FtwHeader,IN OUT EFI_FAULT_TOLERANT_WRITE_RECORD ** FtwRecord)943 GetPreviousRecordOfWrites (
944 IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader,
945 IN OUT EFI_FAULT_TOLERANT_WRITE_RECORD **FtwRecord
946 )
947 {
948 UINT8 *Ptr;
949
950 if (IsFirstRecordOfWrites (FtwHeader, *FtwRecord)) {
951 *FtwRecord = NULL;
952 return EFI_ACCESS_DENIED;
953 }
954
955 Ptr = (UINT8 *) (*FtwRecord);
956 Ptr -= FTW_RECORD_SIZE (FtwHeader->PrivateDataSize);
957 *FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) Ptr;
958 return EFI_SUCCESS;
959 }
960
961 /**
962 Allocate private data for FTW driver and initialize it.
963
964 @param[out] FtwData Pointer to the FTW device structure
965
966 @retval EFI_SUCCESS Initialize the FTW device successfully.
967 @retval EFI_OUT_OF_RESOURCES Allocate memory error
968 @retval EFI_INVALID_PARAMETER Workspace or Spare block does not exist
969
970 **/
971 EFI_STATUS
InitFtwDevice(OUT EFI_FTW_DEVICE ** FtwData)972 InitFtwDevice (
973 OUT EFI_FTW_DEVICE **FtwData
974 )
975 {
976 EFI_FTW_DEVICE *FtwDevice;
977
978 //
979 // Allocate private data of this driver,
980 // Including the FtwWorkSpace[FTW_WORK_SPACE_SIZE].
981 //
982 FtwDevice = AllocateZeroPool (sizeof (EFI_FTW_DEVICE) + PcdGet32 (PcdFlashNvStorageFtwWorkingSize));
983 if (FtwDevice == NULL) {
984 return EFI_OUT_OF_RESOURCES;
985 }
986
987 //
988 // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.
989 //
990 FtwDevice->WorkSpaceLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwWorkingSize);
991 FtwDevice->SpareAreaLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwSpareSize);
992 if ((FtwDevice->WorkSpaceLength == 0) || (FtwDevice->SpareAreaLength == 0)) {
993 DEBUG ((EFI_D_ERROR, "Ftw: Workspace or Spare block does not exist!\n"));
994 FreePool (FtwDevice);
995 return EFI_INVALID_PARAMETER;
996 }
997
998 FtwDevice->Signature = FTW_DEVICE_SIGNATURE;
999 FtwDevice->FtwFvBlock = NULL;
1000 FtwDevice->FtwBackupFvb = NULL;
1001 FtwDevice->FtwWorkSpaceLba = (EFI_LBA) (-1);
1002 FtwDevice->FtwSpareLba = (EFI_LBA) (-1);
1003
1004 FtwDevice->WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageFtwWorkingBase64);
1005 if (FtwDevice->WorkSpaceAddress == 0) {
1006 FtwDevice->WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwWorkingBase);
1007 }
1008
1009 FtwDevice->SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageFtwSpareBase64);
1010 if (FtwDevice->SpareAreaAddress == 0) {
1011 FtwDevice->SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwSpareBase);
1012 }
1013
1014 *FtwData = FtwDevice;
1015 return EFI_SUCCESS;
1016 }
1017
1018
1019 /**
1020 Find the proper Firmware Volume Block protocol for FTW operation.
1021
1022 @param[in, out] FtwDevice Pointer to the FTW device structure
1023
1024 @retval EFI_SUCCESS Find the FVB protocol successfully.
1025 @retval EFI_NOT_FOUND No proper FVB protocol was found.
1026 @retval EFI_ABORTED Some data can not be got or be invalid.
1027
1028 **/
1029 EFI_STATUS
FindFvbForFtw(IN OUT EFI_FTW_DEVICE * FtwDevice)1030 FindFvbForFtw (
1031 IN OUT EFI_FTW_DEVICE *FtwDevice
1032 )
1033 {
1034 EFI_STATUS Status;
1035 EFI_HANDLE *HandleBuffer;
1036 UINTN HandleCount;
1037 UINTN Index;
1038 EFI_PHYSICAL_ADDRESS FvbBaseAddress;
1039 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
1040 EFI_FVB_ATTRIBUTES_2 Attributes;
1041 UINT32 LbaIndex;
1042 UINTN BlockSize;
1043 UINTN NumberOfBlocks;
1044
1045 HandleBuffer = NULL;
1046
1047 //
1048 // Get all FVB handle.
1049 //
1050 Status = GetFvbCountAndBuffer (&HandleCount, &HandleBuffer);
1051 if (EFI_ERROR (Status)) {
1052 return EFI_NOT_FOUND;
1053 }
1054
1055 //
1056 // Get the FVB to access variable store
1057 //
1058 Fvb = NULL;
1059 for (Index = 0; Index < HandleCount; Index += 1) {
1060 Status = FtwGetFvbByHandle (HandleBuffer[Index], &Fvb);
1061 if (EFI_ERROR (Status)) {
1062 Status = EFI_NOT_FOUND;
1063 break;
1064 }
1065
1066 //
1067 // Ensure this FVB protocol support Write operation.
1068 //
1069 Status = Fvb->GetAttributes (Fvb, &Attributes);
1070 if (EFI_ERROR (Status) || ((Attributes & EFI_FVB2_WRITE_STATUS) == 0)) {
1071 continue;
1072 }
1073 //
1074 // Compare the address and select the right one
1075 //
1076 Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress);
1077 if (EFI_ERROR (Status)) {
1078 continue;
1079 }
1080
1081 //
1082 // Now, one FVB has one type of BlockSize.
1083 //
1084 Status = Fvb->GetBlockSize (Fvb, 0, &BlockSize, &NumberOfBlocks);
1085 if (EFI_ERROR (Status)) {
1086 continue;
1087 }
1088
1089 if ((FtwDevice->FtwFvBlock == NULL) && (FtwDevice->WorkSpaceAddress >= FvbBaseAddress) &&
1090 ((FtwDevice->WorkSpaceAddress + FtwDevice->WorkSpaceLength) <= (FvbBaseAddress + BlockSize * NumberOfBlocks))) {
1091 FtwDevice->FtwFvBlock = Fvb;
1092 //
1093 // To get the LBA of work space
1094 //
1095 for (LbaIndex = 1; LbaIndex <= NumberOfBlocks; LbaIndex += 1) {
1096 if ((FtwDevice->WorkSpaceAddress >= (FvbBaseAddress + BlockSize * (LbaIndex - 1)))
1097 && (FtwDevice->WorkSpaceAddress < (FvbBaseAddress + BlockSize * LbaIndex))) {
1098 FtwDevice->FtwWorkSpaceLba = LbaIndex - 1;
1099 //
1100 // Get the Work space size and Base(Offset)
1101 //
1102 FtwDevice->FtwWorkSpaceSize = FtwDevice->WorkSpaceLength;
1103 FtwDevice->WorkBlockSize = BlockSize;
1104 FtwDevice->FtwWorkSpaceBase = (UINTN) (FtwDevice->WorkSpaceAddress - (FvbBaseAddress + FtwDevice->WorkBlockSize * (LbaIndex - 1)));
1105 FtwDevice->NumberOfWorkSpaceBlock = FTW_BLOCKS (FtwDevice->FtwWorkSpaceBase + FtwDevice->FtwWorkSpaceSize, FtwDevice->WorkBlockSize);
1106 if (FtwDevice->FtwWorkSpaceSize >= FtwDevice->WorkBlockSize) {
1107 //
1108 // Check the alignment of work space address and length, they should be block size aligned when work space size is larger than one block size.
1109 //
1110 if (((FtwDevice->WorkSpaceAddress & (FtwDevice->WorkBlockSize - 1)) != 0) ||
1111 ((FtwDevice->WorkSpaceLength & (FtwDevice->WorkBlockSize - 1)) != 0)) {
1112 DEBUG ((EFI_D_ERROR, "Ftw: Work space address or length is not block size aligned when work space size is larger than one block size\n"));
1113 FreePool (HandleBuffer);
1114 ASSERT (FALSE);
1115 return EFI_ABORTED;
1116 }
1117 } else if ((FtwDevice->FtwWorkSpaceBase + FtwDevice->FtwWorkSpaceSize) > FtwDevice->WorkBlockSize) {
1118 DEBUG ((EFI_D_ERROR, "Ftw: The work space range should not span blocks when work space size is less than one block size\n"));
1119 FreePool (HandleBuffer);
1120 ASSERT (FALSE);
1121 return EFI_ABORTED;
1122 }
1123 break;
1124 }
1125 }
1126 }
1127
1128 if ((FtwDevice->FtwBackupFvb == NULL) && (FtwDevice->SpareAreaAddress >= FvbBaseAddress) &&
1129 ((FtwDevice->SpareAreaAddress + FtwDevice->SpareAreaLength) <= (FvbBaseAddress + BlockSize * NumberOfBlocks))) {
1130 FtwDevice->FtwBackupFvb = Fvb;
1131 //
1132 // To get the LBA of spare
1133 //
1134 for (LbaIndex = 1; LbaIndex <= NumberOfBlocks; LbaIndex += 1) {
1135 if ((FtwDevice->SpareAreaAddress >= (FvbBaseAddress + BlockSize * (LbaIndex - 1)))
1136 && (FtwDevice->SpareAreaAddress < (FvbBaseAddress + BlockSize * LbaIndex))) {
1137 //
1138 // Get the NumberOfSpareBlock and BlockSize
1139 //
1140 FtwDevice->FtwSpareLba = LbaIndex - 1;
1141 FtwDevice->SpareBlockSize = BlockSize;
1142 FtwDevice->NumberOfSpareBlock = FtwDevice->SpareAreaLength / FtwDevice->SpareBlockSize;
1143 //
1144 // Check the range of spare area to make sure that it's in FV range
1145 //
1146 if ((FtwDevice->FtwSpareLba + FtwDevice->NumberOfSpareBlock) > NumberOfBlocks) {
1147 DEBUG ((EFI_D_ERROR, "Ftw: Spare area is out of FV range\n"));
1148 FreePool (HandleBuffer);
1149 ASSERT (FALSE);
1150 return EFI_ABORTED;
1151 }
1152 //
1153 // Check the alignment of spare area address and length, they should be block size aligned
1154 //
1155 if (((FtwDevice->SpareAreaAddress & (FtwDevice->SpareBlockSize - 1)) != 0) ||
1156 ((FtwDevice->SpareAreaLength & (FtwDevice->SpareBlockSize - 1)) != 0)) {
1157 DEBUG ((EFI_D_ERROR, "Ftw: Spare area address or length is not block size aligned\n"));
1158 FreePool (HandleBuffer);
1159 //
1160 // Report Status Code EFI_SW_EC_ABORTED.
1161 //
1162 REPORT_STATUS_CODE ((EFI_ERROR_CODE | EFI_ERROR_UNRECOVERED), (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_EC_ABORTED));
1163 ASSERT (FALSE);
1164 CpuDeadLoop ();
1165 }
1166 break;
1167 }
1168 }
1169 }
1170 }
1171 FreePool (HandleBuffer);
1172
1173 if ((FtwDevice->FtwBackupFvb == NULL) || (FtwDevice->FtwFvBlock == NULL) ||
1174 (FtwDevice->FtwWorkSpaceLba == (EFI_LBA) (-1)) || (FtwDevice->FtwSpareLba == (EFI_LBA) (-1))) {
1175 return EFI_ABORTED;
1176 }
1177 DEBUG ((EFI_D_INFO, "Ftw: FtwWorkSpaceLba - 0x%lx, WorkBlockSize - 0x%x, FtwWorkSpaceBase - 0x%x\n", FtwDevice->FtwWorkSpaceLba, FtwDevice->WorkBlockSize, FtwDevice->FtwWorkSpaceBase));
1178 DEBUG ((EFI_D_INFO, "Ftw: FtwSpareLba - 0x%lx, SpareBlockSize - 0x%x\n", FtwDevice->FtwSpareLba, FtwDevice->SpareBlockSize));
1179
1180 return EFI_SUCCESS;
1181 }
1182
1183
1184 /**
1185 Initialization for Fault Tolerant Write protocol.
1186
1187 @param[in, out] FtwDevice Pointer to the FTW device structure
1188
1189 @retval EFI_SUCCESS Initialize the FTW protocol successfully.
1190 @retval EFI_NOT_FOUND No proper FVB protocol was found.
1191
1192 **/
1193 EFI_STATUS
InitFtwProtocol(IN OUT EFI_FTW_DEVICE * FtwDevice)1194 InitFtwProtocol (
1195 IN OUT EFI_FTW_DEVICE *FtwDevice
1196 )
1197 {
1198 EFI_STATUS Status;
1199 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
1200 EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader;
1201 UINTN Offset;
1202 EFI_HANDLE FvbHandle;
1203 EFI_LBA WorkSpaceLbaOffset;
1204
1205 //
1206 // Find the right SMM Fvb protocol instance for FTW.
1207 //
1208 Status = FindFvbForFtw (FtwDevice);
1209 if (EFI_ERROR (Status)) {
1210 return EFI_NOT_FOUND;
1211 }
1212
1213 //
1214 // Calculate the start LBA of working block.
1215 //
1216 if (FtwDevice->FtwWorkSpaceSize >= FtwDevice->WorkBlockSize) {
1217 //
1218 // Working block is a standalone area which only contains working space.
1219 //
1220 FtwDevice->NumberOfWorkBlock = FtwDevice->NumberOfWorkSpaceBlock;
1221 } else {
1222 //
1223 // Working block is an area which
1224 // contains working space in its last block and has the same size as spare
1225 // block, unless there are not enough blocks before the block that contains
1226 // working space.
1227 //
1228 FtwDevice->NumberOfWorkBlock = (UINTN) (FtwDevice->FtwWorkSpaceLba + FtwDevice->NumberOfWorkSpaceBlock);
1229 while (FtwDevice->NumberOfWorkBlock * FtwDevice->WorkBlockSize > FtwDevice->SpareAreaLength) {
1230 FtwDevice->NumberOfWorkBlock--;
1231 }
1232 }
1233 FtwDevice->FtwWorkBlockLba = FtwDevice->FtwWorkSpaceLba + FtwDevice->NumberOfWorkSpaceBlock - FtwDevice->NumberOfWorkBlock;
1234 DEBUG ((EFI_D_INFO, "Ftw: NumberOfWorkBlock - 0x%x, FtwWorkBlockLba - 0x%lx\n", FtwDevice->NumberOfWorkBlock, FtwDevice->FtwWorkBlockLba));
1235
1236 //
1237 // Calcualte the LBA and base of work space in spare block.
1238 // Note: Do not assume Spare Block and Work Block have same block size.
1239 //
1240 WorkSpaceLbaOffset = FtwDevice->FtwWorkSpaceLba - FtwDevice->FtwWorkBlockLba;
1241 FtwDevice->FtwWorkSpaceLbaInSpare = (EFI_LBA) (((UINTN) WorkSpaceLbaOffset * FtwDevice->WorkBlockSize + FtwDevice->FtwWorkSpaceBase) / FtwDevice->SpareBlockSize);
1242 FtwDevice->FtwWorkSpaceBaseInSpare = ((UINTN) WorkSpaceLbaOffset * FtwDevice->WorkBlockSize + FtwDevice->FtwWorkSpaceBase) % FtwDevice->SpareBlockSize;
1243 DEBUG ((EFI_D_INFO, "Ftw: WorkSpaceLbaInSpare - 0x%lx, WorkSpaceBaseInSpare - 0x%x\n", FtwDevice->FtwWorkSpaceLbaInSpare, FtwDevice->FtwWorkSpaceBaseInSpare));
1244
1245 //
1246 // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE.
1247 //
1248 FtwDevice->FtwWorkSpace = (UINT8 *) (FtwDevice + 1);
1249 FtwDevice->FtwWorkSpaceHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) FtwDevice->FtwWorkSpace;
1250
1251 FtwDevice->FtwLastWriteHeader = NULL;
1252 FtwDevice->FtwLastWriteRecord = NULL;
1253
1254 InitializeLocalWorkSpaceHeader ();
1255
1256 //
1257 // Refresh the working space data from working block
1258 //
1259 Status = WorkSpaceRefresh (FtwDevice);
1260 ASSERT_EFI_ERROR (Status);
1261 //
1262 // If the working block workspace is not valid, try the spare block
1263 //
1264 if (!IsValidWorkSpace (FtwDevice->FtwWorkSpaceHeader)) {
1265 //
1266 // Read from spare block
1267 //
1268 Status = ReadWorkSpaceData (
1269 FtwDevice->FtwBackupFvb,
1270 FtwDevice->SpareBlockSize,
1271 FtwDevice->FtwSpareLba + FtwDevice->FtwWorkSpaceLbaInSpare,
1272 FtwDevice->FtwWorkSpaceBaseInSpare,
1273 FtwDevice->FtwWorkSpaceSize,
1274 FtwDevice->FtwWorkSpace
1275 );
1276 ASSERT_EFI_ERROR (Status);
1277
1278 //
1279 // If spare block is valid, then replace working block content.
1280 //
1281 if (IsValidWorkSpace (FtwDevice->FtwWorkSpaceHeader)) {
1282 Status = FlushSpareBlockToWorkingBlock (FtwDevice);
1283 DEBUG ((EFI_D_INFO, "Ftw: Restart working block update in %a() - %r\n",
1284 __FUNCTION__, Status));
1285 FtwAbort (&FtwDevice->FtwInstance);
1286 //
1287 // Refresh work space.
1288 //
1289 Status = WorkSpaceRefresh (FtwDevice);
1290 ASSERT_EFI_ERROR (Status);
1291 } else {
1292 DEBUG ((EFI_D_INFO,
1293 "Ftw: Both working and spare blocks are invalid, init workspace\n"));
1294 //
1295 // If both are invalid, then initialize work space.
1296 //
1297 SetMem (
1298 FtwDevice->FtwWorkSpace,
1299 FtwDevice->FtwWorkSpaceSize,
1300 FTW_ERASED_BYTE
1301 );
1302 InitWorkSpaceHeader (FtwDevice->FtwWorkSpaceHeader);
1303 //
1304 // Initialize the work space
1305 //
1306 Status = FtwReclaimWorkSpace (FtwDevice, FALSE);
1307 ASSERT_EFI_ERROR (Status);
1308 }
1309 }
1310 //
1311 // If the FtwDevice->FtwLastWriteRecord is 1st record of write header &&
1312 // (! SpareComplete) THEN call Abort().
1313 //
1314 if ((FtwDevice->FtwLastWriteHeader->HeaderAllocated == FTW_VALID_STATE) &&
1315 (FtwDevice->FtwLastWriteRecord->SpareComplete != FTW_VALID_STATE) &&
1316 IsFirstRecordOfWrites (FtwDevice->FtwLastWriteHeader, FtwDevice->FtwLastWriteRecord)
1317 ) {
1318 DEBUG ((EFI_D_ERROR, "Ftw: Init.. find first record not SpareCompleted, abort()\n"));
1319 FtwAbort (&FtwDevice->FtwInstance);
1320 }
1321 //
1322 // If Header is incompleted and the last record has completed, then
1323 // call Abort() to set the Header->Complete FLAG.
1324 //
1325 if ((FtwDevice->FtwLastWriteHeader->Complete != FTW_VALID_STATE) &&
1326 (FtwDevice->FtwLastWriteRecord->DestinationComplete == FTW_VALID_STATE) &&
1327 IsLastRecordOfWrites (FtwDevice->FtwLastWriteHeader, FtwDevice->FtwLastWriteRecord)
1328 ) {
1329 DEBUG ((EFI_D_ERROR, "Ftw: Init.. find last record completed but header not, abort()\n"));
1330 FtwAbort (&FtwDevice->FtwInstance);
1331 }
1332 //
1333 // To check the workspace buffer following last Write header/records is EMPTY or not.
1334 // If it's not EMPTY, FTW also need to call reclaim().
1335 //
1336 FtwHeader = FtwDevice->FtwLastWriteHeader;
1337 Offset = (UINT8 *) FtwHeader - FtwDevice->FtwWorkSpace;
1338 if (FtwDevice->FtwWorkSpace[Offset] != FTW_ERASED_BYTE) {
1339 Offset += FTW_WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites, FtwHeader->PrivateDataSize);
1340 }
1341
1342 if (!IsErasedFlashBuffer (FtwDevice->FtwWorkSpace + Offset, FtwDevice->FtwWorkSpaceSize - Offset)) {
1343 Status = FtwReclaimWorkSpace (FtwDevice, TRUE);
1344 ASSERT_EFI_ERROR (Status);
1345 }
1346
1347 //
1348 // Restart if it's boot block
1349 //
1350 if ((FtwDevice->FtwLastWriteHeader->Complete != FTW_VALID_STATE) &&
1351 (FtwDevice->FtwLastWriteRecord->SpareComplete == FTW_VALID_STATE)
1352 ) {
1353 if (FtwDevice->FtwLastWriteRecord->BootBlockUpdate == FTW_VALID_STATE) {
1354 Status = FlushSpareBlockToBootBlock (FtwDevice);
1355 DEBUG ((EFI_D_ERROR, "Ftw: Restart boot block update - %r\n", Status));
1356 ASSERT_EFI_ERROR (Status);
1357 FtwAbort (&FtwDevice->FtwInstance);
1358 } else {
1359 //
1360 // if (SpareCompleted) THEN Restart to fault tolerant write.
1361 //
1362 FvbHandle = NULL;
1363 FvbHandle = GetFvbByAddress ((EFI_PHYSICAL_ADDRESS) (UINTN) ((INT64) FtwDevice->SpareAreaAddress + FtwDevice->FtwLastWriteRecord->RelativeOffset), &Fvb);
1364 if (FvbHandle != NULL) {
1365 Status = FtwRestart (&FtwDevice->FtwInstance, FvbHandle);
1366 DEBUG ((EFI_D_ERROR, "Ftw: Restart last write - %r\n", Status));
1367 ASSERT_EFI_ERROR (Status);
1368 }
1369 FtwAbort (&FtwDevice->FtwInstance);
1370 }
1371 }
1372 //
1373 // Hook the protocol API
1374 //
1375 FtwDevice->FtwInstance.GetMaxBlockSize = FtwGetMaxBlockSize;
1376 FtwDevice->FtwInstance.Allocate = FtwAllocate;
1377 FtwDevice->FtwInstance.Write = FtwWrite;
1378 FtwDevice->FtwInstance.Restart = FtwRestart;
1379 FtwDevice->FtwInstance.Abort = FtwAbort;
1380 FtwDevice->FtwInstance.GetLastWrite = FtwGetLastWrite;
1381
1382 return EFI_SUCCESS;
1383 }
1384
1385