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