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