]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/FaultTolerantWriteDxe/FtwMisc.c
Update the copyright notice format
[mirror_edk2.git] / MdeModulePkg / Universal / FaultTolerantWriteDxe / FtwMisc.c
CommitLineData
85e923a5
LG
1/** @file\r
2\r
3 Internal generic functions to operate flash block.\r
4\r
e5eed7d3
HT
5Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.<BR>\r
6This program and the accompanying materials \r
85e923a5
LG
7are licensed and made available under the terms and conditions of the BSD License \r
8which accompanies this distribution. The full text of the license may be found at \r
9http://opensource.org/licenses/bsd-license.php \r
10 \r
11THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
12WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
13\r
14**/\r
15\r
16#include "FaultTolerantWrite.h"\r
17\r
18/**\r
19\r
20 Check whether a flash buffer is erased.\r
21\r
22 @param Buffer Buffer to check\r
23 @param BufferSize Size of the buffer\r
24\r
25 @return A BOOLEAN value indicating erased or not.\r
26\r
27**/\r
28BOOLEAN\r
29IsErasedFlashBuffer (\r
30 IN UINT8 *Buffer,\r
31 IN UINTN BufferSize\r
32 )\r
33{\r
34 BOOLEAN IsEmpty;\r
35 UINT8 *Ptr;\r
36 UINTN Index;\r
37\r
38 Ptr = Buffer;\r
39 IsEmpty = TRUE;\r
40 for (Index = 0; Index < BufferSize; Index += 1) {\r
41 if (*Ptr++ != FTW_ERASED_BYTE) {\r
42 IsEmpty = FALSE;\r
43 break;\r
44 }\r
45 }\r
46\r
47 return IsEmpty;\r
48}\r
49\r
50/**\r
51 To erase the block with the spare block size.\r
52\r
53\r
54 @param FtwDevice The private data of FTW driver\r
55 @param FvBlock FVB Protocol interface\r
56 @param Lba Lba of the firmware block\r
57\r
58 @retval EFI_SUCCESS Block LBA is Erased successfully\r
59 @retval Others Error occurs\r
60\r
61**/\r
62EFI_STATUS\r
63FtwEraseBlock (\r
64 IN EFI_FTW_DEVICE *FtwDevice,\r
65 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,\r
66 EFI_LBA Lba\r
67 )\r
68{\r
69 return FvBlock->EraseBlocks (\r
70 FvBlock,\r
71 Lba,\r
72 FtwDevice->NumberOfSpareBlock,\r
73 EFI_LBA_LIST_TERMINATOR\r
74 );\r
75}\r
76\r
77/**\r
78 Erase spare block.\r
79\r
80 @param FtwDevice The private data of FTW driver\r
81\r
82 @retval EFI_SUCCESS The erase request was successfully completed.\r
83 @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state.\r
84 @retval EFI_DEVICE_ERROR The block device is not functioning\r
85 correctly and could not be written.\r
86 The firmware device may have been\r
87 partially erased.\r
88 @retval EFI_INVALID_PARAMETER One or more of the LBAs listed\r
89 in the variable argument list do\r
90 not exist in the firmware volume. \r
91\r
92\r
93**/\r
94EFI_STATUS\r
95FtwEraseSpareBlock (\r
96 IN EFI_FTW_DEVICE *FtwDevice\r
97 )\r
98{\r
99 return FtwDevice->FtwBackupFvb->EraseBlocks (\r
100 FtwDevice->FtwBackupFvb,\r
101 FtwDevice->FtwSpareLba,\r
102 FtwDevice->NumberOfSpareBlock,\r
103 EFI_LBA_LIST_TERMINATOR\r
104 );\r
105}\r
106\r
107/**\r
108 Retrive the proper FVB protocol interface by HANDLE.\r
109\r
110\r
111 @param FvBlockHandle The handle of FVB protocol that provides services for\r
112 reading, writing, and erasing the target block.\r
113 @param FvBlock The interface of FVB protocol\r
114\r
115 @retval EFI_SUCCESS The function completed successfully\r
116 @retval EFI_ABORTED The function could not complete successfully\r
117\r
118**/\r
119EFI_STATUS\r
120FtwGetFvbByHandle (\r
121 IN EFI_HANDLE FvBlockHandle,\r
122 OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock\r
123 )\r
124{\r
125 //\r
126 // To get the FVB protocol interface on the handle\r
127 //\r
128 return gBS->HandleProtocol (\r
129 FvBlockHandle,\r
130 &gEfiFirmwareVolumeBlockProtocolGuid,\r
131 (VOID **) FvBlock\r
132 );\r
133}\r
134\r
135/**\r
136\r
137 Is it in working block?\r
138\r
139 @param FtwDevice The private data of FTW driver\r
140 @param FvBlock Fvb protocol instance\r
141 @param Lba The block specified\r
142\r
143 @return A BOOLEAN value indicating in working block or not.\r
144\r
145**/\r
146BOOLEAN\r
147IsWorkingBlock (\r
148 EFI_FTW_DEVICE *FtwDevice,\r
149 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,\r
150 EFI_LBA Lba\r
151 )\r
152{\r
153 //\r
154 // If matching the following condition, the target block is in working block.\r
155 // 1. Target block is on the FV of working block (Using the same FVB protocol instance).\r
156 // 2. Lba falls into the range of working block.\r
157 //\r
158 return (BOOLEAN)\r
159 (\r
160 (FvBlock == FtwDevice->FtwFvBlock) &&\r
161 (Lba >= FtwDevice->FtwWorkBlockLba) &&\r
162 (Lba <= FtwDevice->FtwWorkSpaceLba)\r
163 );\r
164}\r
165\r
166/**\r
167\r
168 Get firmware block by address.\r
169\r
170\r
171 @param Address Address specified the block\r
172 @param FvBlock The block caller wanted\r
173\r
174 @retval EFI_SUCCESS The protocol instance if found.\r
175 @retval EFI_NOT_FOUND Block not found\r
176\r
177**/\r
178EFI_HANDLE\r
179GetFvbByAddress (\r
180 IN EFI_PHYSICAL_ADDRESS Address,\r
181 OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock\r
182 )\r
183{\r
184 EFI_STATUS Status;\r
185 EFI_HANDLE *HandleBuffer;\r
186 UINTN HandleCount;\r
187 UINTN Index;\r
188 EFI_PHYSICAL_ADDRESS FvbBaseAddress;\r
189 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;\r
190 EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;\r
191 EFI_HANDLE FvbHandle;\r
192\r
193 *FvBlock = NULL;\r
194 FvbHandle = NULL;\r
195 //\r
196 // Locate all handles of Fvb protocol\r
197 //\r
198 Status = gBS->LocateHandleBuffer (\r
199 ByProtocol,\r
200 &gEfiFirmwareVolumeBlockProtocolGuid,\r
201 NULL,\r
202 &HandleCount,\r
203 &HandleBuffer\r
204 );\r
205 if (EFI_ERROR (Status)) {\r
206 return NULL;\r
207 }\r
208 //\r
209 // Get the FVB to access variable store\r
210 //\r
211 for (Index = 0; Index < HandleCount; Index += 1) {\r
212 Status = gBS->HandleProtocol (\r
213 HandleBuffer[Index],\r
214 &gEfiFirmwareVolumeBlockProtocolGuid,\r
215 (VOID **) &Fvb\r
216 );\r
217 if (EFI_ERROR (Status)) {\r
218 break;\r
219 }\r
220 //\r
221 // Compare the address and select the right one\r
222 //\r
223 Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress);\r
224 if (EFI_ERROR (Status)) {\r
225 continue;\r
226 }\r
227\r
228 FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) FvbBaseAddress);\r
229 if ((Address >= FvbBaseAddress) && (Address <= (FvbBaseAddress + (FwVolHeader->FvLength - 1)))) {\r
230 *FvBlock = Fvb;\r
231 FvbHandle = HandleBuffer[Index];\r
232 break;\r
233 }\r
234 }\r
235\r
236 FreePool (HandleBuffer);\r
237 return FvbHandle;\r
238}\r
239\r
240/**\r
241\r
242 Is it in boot block?\r
243\r
244 @param FtwDevice The private data of FTW driver\r
245 @param FvBlock Fvb protocol instance\r
246 @param Lba The block specified\r
247\r
248 @return A BOOLEAN value indicating in boot block or not.\r
249\r
250**/\r
251BOOLEAN\r
252IsBootBlock (\r
253 EFI_FTW_DEVICE *FtwDevice,\r
254 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,\r
255 EFI_LBA Lba\r
256 )\r
257{\r
258 EFI_STATUS Status;\r
259 EFI_SWAP_ADDRESS_RANGE_PROTOCOL *SarProtocol;\r
260 EFI_PHYSICAL_ADDRESS BootBlockBase;\r
261 UINTN BootBlockSize;\r
262 EFI_PHYSICAL_ADDRESS BackupBlockBase;\r
263 UINTN BackupBlockSize;\r
264 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *BootFvb;\r
265 BOOLEAN IsSwapped;\r
266 EFI_HANDLE FvbHandle;\r
267\r
268 if (!FeaturePcdGet(PcdFullFtwServiceEnable)) {\r
269 return FALSE;\r
270 }\r
271\r
272 Status = gBS->LocateProtocol (&gEfiSwapAddressRangeProtocolGuid, NULL, (VOID **) &SarProtocol);\r
273 if (EFI_ERROR (Status)) {\r
274 return FALSE;\r
275 }\r
276 //\r
277 // Get the boot block range\r
278 //\r
279 Status = SarProtocol->GetRangeLocation (\r
280 SarProtocol,\r
281 &BootBlockBase,\r
282 &BootBlockSize,\r
283 &BackupBlockBase,\r
284 &BackupBlockSize\r
285 );\r
286 if (EFI_ERROR (Status)) {\r
287 return FALSE;\r
288 }\r
289\r
290 Status = SarProtocol->GetSwapState (SarProtocol, &IsSwapped);\r
291 if (EFI_ERROR (Status)) {\r
292 return FALSE;\r
293 }\r
294 //\r
295 // Get FVB by address\r
296 //\r
297 if (!IsSwapped) {\r
298 FvbHandle = GetFvbByAddress (BootBlockBase, &BootFvb);\r
299 } else {\r
300 FvbHandle = GetFvbByAddress (BackupBlockBase, &BootFvb);\r
301 }\r
302\r
303 if (FvbHandle == NULL) {\r
304 return FALSE;\r
305 }\r
306 //\r
307 // Compare the Fvb\r
308 //\r
309 return (BOOLEAN) (FvBlock == BootFvb);\r
310}\r
311\r
312/**\r
313 Copy the content of spare block to a boot block. Size is FTW_BLOCK_SIZE.\r
314 Spare block is accessed by FTW working FVB protocol interface. LBA is 1.\r
315 Target block is accessed by FvbBlock protocol interface. LBA is Lba.\r
316\r
317 FTW will do extra work on boot block update.\r
318 FTW should depend on a protocol of EFI_ADDRESS_RANGE_SWAP_PROTOCOL,\r
319 which is produced by a chipset driver.\r
320 FTW updating boot block steps may be:\r
321 1. GetRangeLocation(), if the Range is inside the boot block, FTW know\r
322 that boot block will be update. It shall add a FLAG in the working block.\r
323 2. When spare block is ready,\r
324 3. SetSwapState(EFI_SWAPPED)\r
325 4. erasing boot block,\r
326 5. programming boot block until the boot block is ok.\r
327 6. SetSwapState(UNSWAPPED)\r
328 FTW shall not allow to update boot block when battery state is error.\r
329\r
330 @param FtwDevice The private data of FTW driver\r
331\r
332 @retval EFI_SUCCESS Spare block content is copied to boot block\r
333 @retval EFI_INVALID_PARAMETER Input parameter error\r
334 @retval EFI_OUT_OF_RESOURCES Allocate memory error\r
335 @retval EFI_ABORTED The function could not complete successfully\r
336\r
337**/\r
338EFI_STATUS\r
339FlushSpareBlockToBootBlock (\r
340 EFI_FTW_DEVICE *FtwDevice\r
341 )\r
342{\r
343 EFI_STATUS Status;\r
344 UINTN Length;\r
345 UINT8 *Buffer;\r
346 UINTN Count;\r
347 UINT8 *Ptr;\r
348 UINTN Index;\r
349 BOOLEAN TopSwap;\r
350 EFI_SWAP_ADDRESS_RANGE_PROTOCOL *SarProtocol;\r
351 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *BootFvb;\r
352 EFI_LBA BootLba;\r
353\r
354 if (!FeaturePcdGet(PcdFullFtwServiceEnable)) {\r
355 return EFI_UNSUPPORTED;\r
356 }\r
357\r
358 //\r
359 // Locate swap address range protocol\r
360 //\r
361 Status = gBS->LocateProtocol (&gEfiSwapAddressRangeProtocolGuid, NULL, (VOID **) &SarProtocol);\r
362 if (EFI_ERROR (Status)) {\r
363 return Status;\r
364 }\r
365 //\r
366 // Allocate a memory buffer\r
367 //\r
368 Length = FtwDevice->SpareAreaLength;\r
369 Buffer = AllocatePool (Length);\r
370 if (Buffer == NULL) {\r
371 return EFI_OUT_OF_RESOURCES;\r
372 }\r
373 //\r
374 // Get TopSwap bit state\r
375 //\r
376 Status = SarProtocol->GetSwapState (SarProtocol, &TopSwap);\r
377 if (EFI_ERROR (Status)) {\r
378 DEBUG ((EFI_D_ERROR, "Ftw: Get Top Swapped status - %r\n", Status));\r
379 FreePool (Buffer);\r
380 return EFI_ABORTED;\r
381 }\r
382\r
383 if (TopSwap) {\r
384 //\r
385 // Get FVB of current boot block\r
386 //\r
387 if (GetFvbByAddress (FtwDevice->SpareAreaAddress + FtwDevice->SpareAreaLength, &BootFvb) == NULL) {\r
388 FreePool (Buffer);\r
389 return EFI_ABORTED;\r
390 }\r
391 //\r
392 // Read data from current boot block\r
393 //\r
394 BootLba = 0;\r
395 Ptr = Buffer;\r
396 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
397 Count = FtwDevice->BlockSize;\r
398 Status = BootFvb->Read (\r
399 BootFvb,\r
400 BootLba + Index,\r
401 0,\r
402 &Count,\r
403 Ptr\r
404 );\r
405 if (EFI_ERROR (Status)) {\r
406 FreePool (Buffer);\r
407 return Status;\r
408 }\r
409\r
410 Ptr += Count;\r
411 }\r
412 } else {\r
413 //\r
414 // Read data from spare block\r
415 //\r
416 Ptr = Buffer;\r
417 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
418 Count = FtwDevice->BlockSize;\r
419 Status = FtwDevice->FtwBackupFvb->Read (\r
420 FtwDevice->FtwBackupFvb,\r
421 FtwDevice->FtwSpareLba + Index,\r
422 0,\r
423 &Count,\r
424 Ptr\r
425 );\r
426 if (EFI_ERROR (Status)) {\r
427 FreePool (Buffer);\r
428 return Status;\r
429 }\r
430\r
431 Ptr += Count;\r
432 }\r
433 //\r
434 // Set TopSwap bit\r
435 //\r
436 Status = SarProtocol->SetSwapState (SarProtocol, TRUE);\r
437 if (EFI_ERROR (Status)) {\r
438 FreePool (Buffer);\r
439 return Status;\r
440 }\r
441 }\r
442 //\r
443 // Erase current spare block\r
444 // Because TopSwap is set, this actually erase the top block (boot block)!\r
445 //\r
446 Status = FtwEraseSpareBlock (FtwDevice);\r
447 if (EFI_ERROR (Status)) {\r
448 FreePool (Buffer);\r
449 return EFI_ABORTED;\r
450 }\r
451 //\r
452 // Write memory buffer currenet spare block. Still top block.\r
453 //\r
454 Ptr = Buffer;\r
455 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
456 Count = FtwDevice->BlockSize;\r
457 Status = FtwDevice->FtwBackupFvb->Write (\r
458 FtwDevice->FtwBackupFvb,\r
459 FtwDevice->FtwSpareLba + Index,\r
460 0,\r
461 &Count,\r
462 Ptr\r
463 );\r
464 if (EFI_ERROR (Status)) {\r
465 DEBUG ((EFI_D_ERROR, "Ftw: FVB Write boot block - %r\n", Status));\r
466 FreePool (Buffer);\r
467 return Status;\r
468 }\r
469\r
470 Ptr += Count;\r
471 }\r
472\r
473 FreePool (Buffer);\r
474\r
475 //\r
476 // Clear TopSwap bit\r
477 //\r
478 Status = SarProtocol->SetSwapState (SarProtocol, FALSE);\r
479\r
480 return Status;\r
481}\r
482\r
483/**\r
484 Copy the content of spare block to a target block. Size is FTW_BLOCK_SIZE.\r
485 Spare block is accessed by FTW backup FVB protocol interface. LBA is 1.\r
486 Target block is accessed by FvbBlock protocol interface. LBA is Lba.\r
487\r
488\r
489 @param FtwDevice The private data of FTW driver\r
490 @param FvBlock FVB Protocol interface to access target block\r
491 @param Lba Lba of the target block\r
492\r
493 @retval EFI_SUCCESS Spare block content is copied to target block\r
494 @retval EFI_INVALID_PARAMETER Input parameter error\r
495 @retval EFI_OUT_OF_RESOURCES Allocate memory error\r
496 @retval EFI_ABORTED The function could not complete successfully\r
497\r
498**/\r
499EFI_STATUS\r
500FlushSpareBlockToTargetBlock (\r
501 EFI_FTW_DEVICE *FtwDevice,\r
502 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,\r
503 EFI_LBA Lba\r
504 )\r
505{\r
506 EFI_STATUS Status;\r
507 UINTN Length;\r
508 UINT8 *Buffer;\r
509 UINTN Count;\r
510 UINT8 *Ptr;\r
511 UINTN Index;\r
512\r
513 if ((FtwDevice == NULL) || (FvBlock == NULL)) {\r
514 return EFI_INVALID_PARAMETER;\r
515 }\r
516 //\r
517 // Allocate a memory buffer\r
518 //\r
519 Length = FtwDevice->SpareAreaLength;\r
520 Buffer = AllocatePool (Length);\r
521 if (Buffer == NULL) {\r
522 return EFI_OUT_OF_RESOURCES;\r
523 }\r
524 //\r
525 // Read all content of spare block to memory buffer\r
526 //\r
527 Ptr = Buffer;\r
528 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
529 Count = FtwDevice->BlockSize;\r
530 Status = FtwDevice->FtwBackupFvb->Read (\r
531 FtwDevice->FtwBackupFvb,\r
532 FtwDevice->FtwSpareLba + Index,\r
533 0,\r
534 &Count,\r
535 Ptr\r
536 );\r
537 if (EFI_ERROR (Status)) {\r
538 FreePool (Buffer);\r
539 return Status;\r
540 }\r
541\r
542 Ptr += Count;\r
543 }\r
544 //\r
545 // Erase the target block\r
546 //\r
547 Status = FtwEraseBlock (FtwDevice, FvBlock, Lba);\r
548 if (EFI_ERROR (Status)) {\r
549 FreePool (Buffer);\r
550 return EFI_ABORTED;\r
551 }\r
552 //\r
553 // Write memory buffer to block, using the FvbBlock protocol interface\r
554 //\r
555 Ptr = Buffer;\r
556 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
557 Count = FtwDevice->BlockSize;\r
558 Status = FvBlock->Write (FvBlock, Lba + Index, 0, &Count, Ptr);\r
559 if (EFI_ERROR (Status)) {\r
560 DEBUG ((EFI_D_ERROR, "Ftw: FVB Write block - %r\n", Status));\r
561 FreePool (Buffer);\r
562 return Status;\r
563 }\r
564\r
565 Ptr += Count;\r
566 }\r
567\r
568 FreePool (Buffer);\r
569\r
570 return Status;\r
571}\r
572\r
573/**\r
574 Copy the content of spare block to working block. Size is FTW_BLOCK_SIZE.\r
575 Spare block is accessed by FTW backup FVB protocol interface. LBA is\r
576 FtwDevice->FtwSpareLba.\r
577 Working block is accessed by FTW working FVB protocol interface. LBA is\r
578 FtwDevice->FtwWorkBlockLba.\r
579\r
580 Since the working block header is important when FTW initializes, the\r
581 state of the operation should be handled carefully. The Crc value is\r
582 calculated without STATE element.\r
583\r
584 @param FtwDevice The private data of FTW driver\r
585\r
586 @retval EFI_SUCCESS Spare block content is copied to target block\r
587 @retval EFI_OUT_OF_RESOURCES Allocate memory error\r
588 @retval EFI_ABORTED The function could not complete successfully\r
589\r
590**/\r
591EFI_STATUS\r
592FlushSpareBlockToWorkingBlock (\r
593 EFI_FTW_DEVICE *FtwDevice\r
594 )\r
595{\r
596 EFI_STATUS Status;\r
597 UINTN Length;\r
598 UINT8 *Buffer;\r
599 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader;\r
600 UINTN Count;\r
601 UINT8 *Ptr;\r
602 UINTN Index;\r
603 EFI_LBA WorkSpaceLbaOffset;\r
604\r
605 //\r
606 // Allocate a memory buffer\r
607 //\r
608 Length = FtwDevice->SpareAreaLength;\r
609 Buffer = AllocatePool (Length);\r
610 if (Buffer == NULL) {\r
611 return EFI_OUT_OF_RESOURCES;\r
612 }\r
613 //\r
614 // To guarantee that the WorkingBlockValid is set on spare block\r
615 //\r
616 // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,\r
617 // WorkingBlockValid);\r
618 // To skip Signature and Crc: sizeof(EFI_GUID)+sizeof(UINT32).\r
619 //\r
620 FtwUpdateFvState (\r
621 FtwDevice->FtwBackupFvb,\r
622 FtwDevice->FtwWorkSpaceLba,\r
623 FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),\r
624 WORKING_BLOCK_VALID\r
625 );\r
626 //\r
627 // Read from spare block to memory buffer\r
628 //\r
629 Ptr = Buffer;\r
630 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
631 Count = FtwDevice->BlockSize;\r
632 Status = FtwDevice->FtwBackupFvb->Read (\r
633 FtwDevice->FtwBackupFvb,\r
634 FtwDevice->FtwSpareLba + Index,\r
635 0,\r
636 &Count,\r
637 Ptr\r
638 );\r
639 if (EFI_ERROR (Status)) {\r
640 FreePool (Buffer);\r
641 return Status;\r
642 }\r
643\r
644 Ptr += Count;\r
645 }\r
646 //\r
647 // Clear the CRC and STATE, copy data from spare to working block.\r
648 //\r
649 WorkSpaceLbaOffset = FtwDevice->FtwWorkSpaceLba - FtwDevice->FtwWorkBlockLba;\r
650 WorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (Buffer + (UINTN) WorkSpaceLbaOffset * FtwDevice->BlockSize + FtwDevice->FtwWorkSpaceBase);\r
651 InitWorkSpaceHeader (WorkingBlockHeader);\r
652 WorkingBlockHeader->WorkingBlockValid = FTW_ERASE_POLARITY;\r
653 WorkingBlockHeader->WorkingBlockInvalid = FTW_ERASE_POLARITY;\r
654\r
655 //\r
656 // target block is working block, then\r
657 // Set WorkingBlockInvalid in EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER\r
658 // before erase the working block.\r
659 //\r
660 // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,\r
661 // WorkingBlockInvalid);\r
662 // So hardcode offset as sizeof(EFI_GUID)+sizeof(UINT32) to\r
663 // skip Signature and Crc.\r
664 //\r
665 Status = FtwUpdateFvState (\r
666 FtwDevice->FtwFvBlock,\r
667 FtwDevice->FtwWorkSpaceLba,\r
668 FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),\r
669 WORKING_BLOCK_INVALID\r
670 );\r
671 if (EFI_ERROR (Status)) {\r
672 FreePool (Buffer);\r
673 return EFI_ABORTED;\r
674 }\r
675\r
676 FtwDevice->FtwWorkSpaceHeader->WorkingBlockInvalid = FTW_VALID_STATE;\r
677\r
678 //\r
679 // Erase the working block\r
680 //\r
681 Status = FtwEraseBlock (FtwDevice, FtwDevice->FtwFvBlock, FtwDevice->FtwWorkBlockLba);\r
682 if (EFI_ERROR (Status)) {\r
683 FreePool (Buffer);\r
684 return EFI_ABORTED;\r
685 }\r
686 //\r
687 // Write memory buffer to working block, using the FvbBlock protocol interface\r
688 //\r
689 Ptr = Buffer;\r
690 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
691 Count = FtwDevice->BlockSize;\r
692 Status = FtwDevice->FtwFvBlock->Write (\r
693 FtwDevice->FtwFvBlock,\r
694 FtwDevice->FtwWorkBlockLba + Index,\r
695 0,\r
696 &Count,\r
697 Ptr\r
698 );\r
699 if (EFI_ERROR (Status)) {\r
700 DEBUG ((EFI_D_ERROR, "Ftw: FVB Write block - %r\n", Status));\r
701 FreePool (Buffer);\r
702 return Status;\r
703 }\r
704\r
705 Ptr += Count;\r
706 }\r
707 //\r
708 // Since the memory buffer will not be used, free memory Buffer.\r
709 //\r
710 FreePool (Buffer);\r
711\r
712 //\r
713 // Update the VALID of the working block\r
714 //\r
715 // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER, WorkingBlockValid);\r
716 // So hardcode offset as sizeof(EFI_GUID)+sizeof(UINT32) to skip Signature and Crc.\r
717 //\r
718 Status = FtwUpdateFvState (\r
719 FtwDevice->FtwFvBlock,\r
720 FtwDevice->FtwWorkSpaceLba,\r
721 FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),\r
722 WORKING_BLOCK_VALID\r
723 );\r
724 if (EFI_ERROR (Status)) {\r
725 return EFI_ABORTED;\r
726 }\r
727\r
728 FtwDevice->FtwWorkSpaceHeader->WorkingBlockValid = FTW_VALID_STATE;\r
729\r
730 return EFI_SUCCESS;\r
731}\r
732\r
733/**\r
734 Update a bit of state on a block device. The location of the bit is\r
735 calculated by the (Lba, Offset, bit). Here bit is determined by the\r
736 the name of a certain bit.\r
737\r
738\r
739 @param FvBlock FVB Protocol interface to access SrcBlock and DestBlock\r
740 @param Lba Lba of a block\r
741 @param Offset Offset on the Lba\r
742 @param NewBit New value that will override the old value if it can be change\r
743\r
744 @retval EFI_SUCCESS A state bit has been updated successfully\r
745 @retval Others Access block device error.\r
746 Notes:\r
747 Assume all bits of State are inside the same BYTE.\r
748 @retval EFI_ABORTED Read block fail\r
749\r
750**/\r
751EFI_STATUS\r
752FtwUpdateFvState (\r
753 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,\r
754 IN EFI_LBA Lba,\r
755 IN UINTN Offset,\r
756 IN UINT8 NewBit\r
757 )\r
758{\r
759 EFI_STATUS Status;\r
760 UINT8 State;\r
761 UINTN Length;\r
762\r
763 //\r
764 // Read state from device, assume State is only one byte.\r
765 //\r
766 Length = sizeof (UINT8);\r
767 Status = FvBlock->Read (FvBlock, Lba, Offset, &Length, &State);\r
768 if (EFI_ERROR (Status)) {\r
769 return EFI_ABORTED;\r
770 }\r
771\r
772 State ^= FTW_POLARITY_REVERT;\r
773 State = (UINT8) (State | NewBit);\r
774 State ^= FTW_POLARITY_REVERT;\r
775\r
776 //\r
777 // Write state back to device\r
778 //\r
779 Length = sizeof (UINT8);\r
780 Status = FvBlock->Write (FvBlock, Lba, Offset, &Length, &State);\r
781\r
782 return Status;\r
783}\r
784\r
785/**\r
786 Get the last Write Header pointer.\r
787 The last write header is the header whose 'complete' state hasn't been set.\r
788 After all, this header may be a EMPTY header entry for next Allocate.\r
789\r
790\r
791 @param FtwWorkSpaceHeader Pointer of the working block header\r
792 @param FtwWorkSpaceSize Size of the work space\r
793 @param FtwWriteHeader Pointer to retrieve the last write header\r
794\r
795 @retval EFI_SUCCESS Get the last write record successfully\r
796 @retval EFI_ABORTED The FTW work space is damaged\r
797\r
798**/\r
799EFI_STATUS\r
800FtwGetLastWriteHeader (\r
801 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *FtwWorkSpaceHeader,\r
802 IN UINTN FtwWorkSpaceSize,\r
803 OUT EFI_FAULT_TOLERANT_WRITE_HEADER **FtwWriteHeader\r
804 )\r
805{\r
806 UINTN Offset;\r
807 EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader;\r
808\r
809 *FtwWriteHeader = NULL;\r
810 FtwHeader = (EFI_FAULT_TOLERANT_WRITE_HEADER *) (FtwWorkSpaceHeader + 1);\r
4601f374 811 Offset = sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER);\r
85e923a5
LG
812\r
813 while (FtwHeader->Complete == FTW_VALID_STATE) {\r
814 Offset += WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites, FtwHeader->PrivateDataSize);\r
815 //\r
816 // If Offset exceed the FTW work space boudary, return error.\r
817 //\r
818 if (Offset > FtwWorkSpaceSize) {\r
819 *FtwWriteHeader = FtwHeader;\r
820 return EFI_ABORTED;\r
821 }\r
822\r
823 FtwHeader = (EFI_FAULT_TOLERANT_WRITE_HEADER *) ((UINT8 *) FtwWorkSpaceHeader + Offset);\r
824 }\r
825 //\r
826 // Last write header is found\r
827 //\r
828 *FtwWriteHeader = FtwHeader;\r
829\r
830 return EFI_SUCCESS;\r
831}\r
832\r
833/**\r
834 Get the last Write Record pointer. The last write Record is the Record\r
835 whose DestinationCompleted state hasn't been set. After all, this Record\r
836 may be a EMPTY record entry for next write.\r
837\r
838\r
839 @param FtwWriteHeader Pointer to the write record header\r
840 @param FtwWriteRecord Pointer to retrieve the last write record\r
841\r
842 @retval EFI_SUCCESS Get the last write record successfully\r
843 @retval EFI_ABORTED The FTW work space is damaged\r
844\r
845**/\r
846EFI_STATUS\r
847FtwGetLastWriteRecord (\r
848 IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwWriteHeader,\r
849 OUT EFI_FAULT_TOLERANT_WRITE_RECORD **FtwWriteRecord\r
850 )\r
851{\r
852 UINTN Index;\r
853 EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord;\r
854\r
855 *FtwWriteRecord = NULL;\r
856 FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) (FtwWriteHeader + 1);\r
857\r
858 //\r
859 // Try to find the last write record "that has not completed"\r
860 //\r
861 for (Index = 0; Index < FtwWriteHeader->NumberOfWrites; Index += 1) {\r
862 if (FtwRecord->DestinationComplete != FTW_VALID_STATE) {\r
863 //\r
864 // The last write record is found\r
865 //\r
866 *FtwWriteRecord = FtwRecord;\r
867 return EFI_SUCCESS;\r
868 }\r
869\r
870 FtwRecord++;\r
871\r
872 if (FtwWriteHeader->PrivateDataSize != 0) {\r
873 FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) ((UINTN) FtwRecord + FtwWriteHeader->PrivateDataSize);\r
874 }\r
875 }\r
876 //\r
877 // if Index == NumberOfWrites, then\r
878 // the last record has been written successfully,\r
879 // but the Header->Complete Flag has not been set.\r
880 // also return the last record.\r
881 //\r
882 if (Index == FtwWriteHeader->NumberOfWrites) {\r
883 *FtwWriteRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) ((UINTN) FtwRecord - RECORD_SIZE (FtwWriteHeader->PrivateDataSize));\r
884 return EFI_SUCCESS;\r
885 }\r
886\r
887 return EFI_ABORTED;\r
888}\r
889\r
890/**\r
891 To check if FtwRecord is the first record of FtwHeader.\r
892\r
893 @param FtwHeader Pointer to the write record header\r
894 @param FtwRecord Pointer to the write record\r
895\r
896 @retval TRUE FtwRecord is the first Record of the FtwHeader\r
897 @retval FALSE FtwRecord is not the first Record of the FtwHeader\r
898\r
899**/\r
900BOOLEAN\r
901IsFirstRecordOfWrites (\r
902 IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader,\r
903 IN EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord\r
904 )\r
905{\r
906 UINT8 *Head;\r
907 UINT8 *Ptr;\r
908\r
909 Head = (UINT8 *) FtwHeader;\r
910 Ptr = (UINT8 *) FtwRecord;\r
911\r
912 Head += sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER);\r
913 return (BOOLEAN) (Head == Ptr);\r
914}\r
915\r
916/**\r
917 To check if FtwRecord is the last record of FtwHeader. Because the\r
918 FtwHeader has NumberOfWrites & PrivateDataSize, the FtwRecord can be\r
919 determined if it is the last record of FtwHeader.\r
920\r
921 @param FtwHeader Pointer to the write record header\r
922 @param FtwRecord Pointer to the write record\r
923\r
924 @retval TRUE FtwRecord is the last Record of the FtwHeader\r
925 @retval FALSE FtwRecord is not the last Record of the FtwHeader\r
926\r
927**/\r
928BOOLEAN\r
929IsLastRecordOfWrites (\r
930 IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader,\r
931 IN EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord\r
932 )\r
933{\r
934 UINT8 *Head;\r
935 UINT8 *Ptr;\r
936\r
937 Head = (UINT8 *) FtwHeader;\r
938 Ptr = (UINT8 *) FtwRecord;\r
939\r
940 Head += WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites - 1, FtwHeader->PrivateDataSize);\r
941 return (BOOLEAN) (Head == Ptr);\r
942}\r
943\r
944/**\r
945 To check if FtwRecord is the first record of FtwHeader.\r
946\r
947 @param FtwHeader Pointer to the write record header\r
948 @param FtwRecord Pointer to retrieve the previous write record\r
949\r
950 @retval EFI_ACCESS_DENIED Input record is the first record, no previous record is return.\r
951 @retval EFI_SUCCESS The previous write record is found.\r
952\r
953**/\r
954EFI_STATUS\r
955GetPreviousRecordOfWrites (\r
956 IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader,\r
957 IN OUT EFI_FAULT_TOLERANT_WRITE_RECORD **FtwRecord\r
958 )\r
959{\r
960 UINT8 *Ptr;\r
961\r
962 if (IsFirstRecordOfWrites (FtwHeader, *FtwRecord)) {\r
963 *FtwRecord = NULL;\r
964 return EFI_ACCESS_DENIED;\r
965 }\r
966\r
967 Ptr = (UINT8 *) (*FtwRecord);\r
968 Ptr -= RECORD_SIZE (FtwHeader->PrivateDataSize);\r
969 *FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) Ptr;\r
970 return EFI_SUCCESS;\r
971}\r