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