]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - SecurityPkg/VariableAuthenticated/RuntimeDxe/Variable.c
SecurityPkg Variable: Before EndOfDxe, just record the current boot VarErrorFlag...
[mirror_edk2.git] / SecurityPkg / VariableAuthenticated / RuntimeDxe / Variable.c
... / ...
CommitLineData
1/** @file\r
2 The common variable operation routines shared by DXE_RUNTIME variable\r
3 module and DXE_SMM variable module.\r
4\r
5 Caution: This module requires additional review when modified.\r
6 This driver will have external input - variable data. They may be input in SMM mode.\r
7 This external input must be validated carefully to avoid security issue like\r
8 buffer overflow, integer overflow.\r
9\r
10 VariableServiceGetNextVariableName () and VariableServiceQueryVariableInfo() are external API.\r
11 They need check input parameter.\r
12\r
13 VariableServiceGetVariable() and VariableServiceSetVariable() are external API\r
14 to receive datasize and data buffer. The size should be checked carefully.\r
15\r
16 VariableServiceSetVariable() should also check authenticate data to avoid buffer overflow,\r
17 integer overflow. It should also check attribute to avoid authentication bypass.\r
18\r
19Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>\r
20This program and the accompanying materials\r
21are licensed and made available under the terms and conditions of the BSD License\r
22which accompanies this distribution. The full text of the license may be found at\r
23http://opensource.org/licenses/bsd-license.php\r
24\r
25THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
26WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
27\r
28**/\r
29\r
30#include "Variable.h"\r
31#include "AuthService.h"\r
32\r
33VARIABLE_MODULE_GLOBAL *mVariableModuleGlobal;\r
34\r
35///\r
36/// Define a memory cache that improves the search performance for a variable.\r
37///\r
38VARIABLE_STORE_HEADER *mNvVariableCache = NULL;\r
39\r
40///\r
41/// The memory entry used for variable statistics data.\r
42///\r
43VARIABLE_INFO_ENTRY *gVariableInfo = NULL;\r
44\r
45///\r
46/// The list to store the variables which cannot be set after the EFI_END_OF_DXE_EVENT_GROUP_GUID\r
47/// or EVT_GROUP_READY_TO_BOOT event.\r
48///\r
49LIST_ENTRY mLockedVariableList = INITIALIZE_LIST_HEAD_VARIABLE (mLockedVariableList);\r
50\r
51///\r
52/// The flag to indicate whether the platform has left the DXE phase of execution.\r
53///\r
54BOOLEAN mEndOfDxe = FALSE;\r
55\r
56///\r
57/// The flag to indicate whether the variable storage locking is enabled.\r
58///\r
59BOOLEAN mEnableLocking = TRUE;\r
60\r
61//\r
62// It will record the current boot error flag before EndOfDxe.\r
63//\r
64VAR_ERROR_FLAG mCurrentBootVarErrFlag = VAR_ERROR_FLAG_NO_ERROR;\r
65\r
66 SecureBoot Hook for auth variable update.\r
67\r
68 @param[in] VariableName Name of Variable to be found.\r
69 @param[in] VendorGuid Variable vendor GUID.\r
70**/\r
71VOID\r
72EFIAPI\r
73SecureBootHook (\r
74 IN CHAR16 *VariableName,\r
75 IN EFI_GUID *VendorGuid\r
76 );\r
77\r
78/**\r
79 Routine used to track statistical information about variable usage.\r
80 The data is stored in the EFI system table so it can be accessed later.\r
81 VariableInfo.efi can dump out the table. Only Boot Services variable\r
82 accesses are tracked by this code. The PcdVariableCollectStatistics\r
83 build flag controls if this feature is enabled.\r
84\r
85 A read that hits in the cache will have Read and Cache true for\r
86 the transaction. Data is allocated by this routine, but never\r
87 freed.\r
88\r
89 @param[in] VariableName Name of the Variable to track.\r
90 @param[in] VendorGuid Guid of the Variable to track.\r
91 @param[in] Volatile TRUE if volatile FALSE if non-volatile.\r
92 @param[in] Read TRUE if GetVariable() was called.\r
93 @param[in] Write TRUE if SetVariable() was called.\r
94 @param[in] Delete TRUE if deleted via SetVariable().\r
95 @param[in] Cache TRUE for a cache hit.\r
96\r
97**/\r
98VOID\r
99UpdateVariableInfo (\r
100 IN CHAR16 *VariableName,\r
101 IN EFI_GUID *VendorGuid,\r
102 IN BOOLEAN Volatile,\r
103 IN BOOLEAN Read,\r
104 IN BOOLEAN Write,\r
105 IN BOOLEAN Delete,\r
106 IN BOOLEAN Cache\r
107 )\r
108{\r
109 VARIABLE_INFO_ENTRY *Entry;\r
110\r
111 if (FeaturePcdGet (PcdVariableCollectStatistics)) {\r
112\r
113 if (AtRuntime ()) {\r
114 // Don't collect statistics at runtime.\r
115 return;\r
116 }\r
117\r
118 if (gVariableInfo == NULL) {\r
119 //\r
120 // On the first call allocate a entry and place a pointer to it in\r
121 // the EFI System Table.\r
122 //\r
123 gVariableInfo = AllocateZeroPool (sizeof (VARIABLE_INFO_ENTRY));\r
124 ASSERT (gVariableInfo != NULL);\r
125\r
126 CopyGuid (&gVariableInfo->VendorGuid, VendorGuid);\r
127 gVariableInfo->Name = AllocatePool (StrSize (VariableName));\r
128 ASSERT (gVariableInfo->Name != NULL);\r
129 StrCpy (gVariableInfo->Name, VariableName);\r
130 gVariableInfo->Volatile = Volatile;\r
131 }\r
132\r
133\r
134 for (Entry = gVariableInfo; Entry != NULL; Entry = Entry->Next) {\r
135 if (CompareGuid (VendorGuid, &Entry->VendorGuid)) {\r
136 if (StrCmp (VariableName, Entry->Name) == 0) {\r
137 if (Read) {\r
138 Entry->ReadCount++;\r
139 }\r
140 if (Write) {\r
141 Entry->WriteCount++;\r
142 }\r
143 if (Delete) {\r
144 Entry->DeleteCount++;\r
145 }\r
146 if (Cache) {\r
147 Entry->CacheCount++;\r
148 }\r
149\r
150 return;\r
151 }\r
152 }\r
153\r
154 if (Entry->Next == NULL) {\r
155 //\r
156 // If the entry is not in the table add it.\r
157 // Next iteration of the loop will fill in the data.\r
158 //\r
159 Entry->Next = AllocateZeroPool (sizeof (VARIABLE_INFO_ENTRY));\r
160 ASSERT (Entry->Next != NULL);\r
161\r
162 CopyGuid (&Entry->Next->VendorGuid, VendorGuid);\r
163 Entry->Next->Name = AllocatePool (StrSize (VariableName));\r
164 ASSERT (Entry->Next->Name != NULL);\r
165 StrCpy (Entry->Next->Name, VariableName);\r
166 Entry->Next->Volatile = Volatile;\r
167 }\r
168\r
169 }\r
170 }\r
171}\r
172\r
173\r
174/**\r
175\r
176 This code checks if variable header is valid or not.\r
177\r
178 @param Variable Pointer to the Variable Header.\r
179 @param VariableStoreEnd Pointer to the Variable Store End.\r
180\r
181 @retval TRUE Variable header is valid.\r
182 @retval FALSE Variable header is not valid.\r
183\r
184**/\r
185BOOLEAN\r
186IsValidVariableHeader (\r
187 IN VARIABLE_HEADER *Variable,\r
188 IN VARIABLE_HEADER *VariableStoreEnd\r
189 )\r
190{\r
191 if ((Variable == NULL) || (Variable >= VariableStoreEnd) || (Variable->StartId != VARIABLE_DATA)) {\r
192 //\r
193 // Variable is NULL or has reached the end of variable store,\r
194 // or the StartId is not correct.\r
195 //\r
196 return FALSE;\r
197 }\r
198\r
199 return TRUE;\r
200}\r
201\r
202\r
203/**\r
204\r
205 This function writes data to the FWH at the correct LBA even if the LBAs\r
206 are fragmented.\r
207\r
208 @param Global Pointer to VARAIBLE_GLOBAL structure.\r
209 @param Volatile Point out the Variable is Volatile or Non-Volatile.\r
210 @param SetByIndex TRUE if target pointer is given as index.\r
211 FALSE if target pointer is absolute.\r
212 @param Fvb Pointer to the writable FVB protocol.\r
213 @param DataPtrIndex Pointer to the Data from the end of VARIABLE_STORE_HEADER\r
214 structure.\r
215 @param DataSize Size of data to be written.\r
216 @param Buffer Pointer to the buffer from which data is written.\r
217\r
218 @retval EFI_INVALID_PARAMETER Parameters not valid.\r
219 @retval EFI_SUCCESS Variable store successfully updated.\r
220\r
221**/\r
222EFI_STATUS\r
223UpdateVariableStore (\r
224 IN VARIABLE_GLOBAL *Global,\r
225 IN BOOLEAN Volatile,\r
226 IN BOOLEAN SetByIndex,\r
227 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb,\r
228 IN UINTN DataPtrIndex,\r
229 IN UINT32 DataSize,\r
230 IN UINT8 *Buffer\r
231 )\r
232{\r
233 EFI_FV_BLOCK_MAP_ENTRY *PtrBlockMapEntry;\r
234 UINTN BlockIndex2;\r
235 UINTN LinearOffset;\r
236 UINTN CurrWriteSize;\r
237 UINTN CurrWritePtr;\r
238 UINT8 *CurrBuffer;\r
239 EFI_LBA LbaNumber;\r
240 UINTN Size;\r
241 EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;\r
242 VARIABLE_STORE_HEADER *VolatileBase;\r
243 EFI_PHYSICAL_ADDRESS FvVolHdr;\r
244 EFI_PHYSICAL_ADDRESS DataPtr;\r
245 EFI_STATUS Status;\r
246\r
247 FwVolHeader = NULL;\r
248 DataPtr = DataPtrIndex;\r
249\r
250 //\r
251 // Check if the Data is Volatile.\r
252 //\r
253 if (!Volatile) {\r
254 if (Fvb == NULL) {\r
255 return EFI_INVALID_PARAMETER;\r
256 }\r
257 Status = Fvb->GetPhysicalAddress(Fvb, &FvVolHdr);\r
258 ASSERT_EFI_ERROR (Status);\r
259\r
260 FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) FvVolHdr);\r
261 //\r
262 // Data Pointer should point to the actual Address where data is to be\r
263 // written.\r
264 //\r
265 if (SetByIndex) {\r
266 DataPtr += mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase;\r
267 }\r
268\r
269 if ((DataPtr + DataSize) >= ((EFI_PHYSICAL_ADDRESS) (UINTN) ((UINT8 *) FwVolHeader + FwVolHeader->FvLength))) {\r
270 return EFI_INVALID_PARAMETER;\r
271 }\r
272 } else {\r
273 //\r
274 // Data Pointer should point to the actual Address where data is to be\r
275 // written.\r
276 //\r
277 VolatileBase = (VARIABLE_STORE_HEADER *) ((UINTN) mVariableModuleGlobal->VariableGlobal.VolatileVariableBase);\r
278 if (SetByIndex) {\r
279 DataPtr += mVariableModuleGlobal->VariableGlobal.VolatileVariableBase;\r
280 }\r
281\r
282 if ((DataPtr + DataSize) >= ((UINTN) ((UINT8 *) VolatileBase + VolatileBase->Size))) {\r
283 return EFI_INVALID_PARAMETER;\r
284 }\r
285\r
286 //\r
287 // If Volatile Variable just do a simple mem copy.\r
288 //\r
289 CopyMem ((UINT8 *)(UINTN)DataPtr, Buffer, DataSize);\r
290 return EFI_SUCCESS;\r
291 }\r
292\r
293 //\r
294 // If we are here we are dealing with Non-Volatile Variables.\r
295 //\r
296 LinearOffset = (UINTN) FwVolHeader;\r
297 CurrWritePtr = (UINTN) DataPtr;\r
298 CurrWriteSize = DataSize;\r
299 CurrBuffer = Buffer;\r
300 LbaNumber = 0;\r
301\r
302 if (CurrWritePtr < LinearOffset) {\r
303 return EFI_INVALID_PARAMETER;\r
304 }\r
305\r
306 for (PtrBlockMapEntry = FwVolHeader->BlockMap; PtrBlockMapEntry->NumBlocks != 0; PtrBlockMapEntry++) {\r
307 for (BlockIndex2 = 0; BlockIndex2 < PtrBlockMapEntry->NumBlocks; BlockIndex2++) {\r
308 //\r
309 // Check to see if the Variable Writes are spanning through multiple\r
310 // blocks.\r
311 //\r
312 if ((CurrWritePtr >= LinearOffset) && (CurrWritePtr < LinearOffset + PtrBlockMapEntry->Length)) {\r
313 if ((CurrWritePtr + CurrWriteSize) <= (LinearOffset + PtrBlockMapEntry->Length)) {\r
314 Status = Fvb->Write (\r
315 Fvb,\r
316 LbaNumber,\r
317 (UINTN) (CurrWritePtr - LinearOffset),\r
318 &CurrWriteSize,\r
319 CurrBuffer\r
320 );\r
321 return Status;\r
322 } else {\r
323 Size = (UINT32) (LinearOffset + PtrBlockMapEntry->Length - CurrWritePtr);\r
324 Status = Fvb->Write (\r
325 Fvb,\r
326 LbaNumber,\r
327 (UINTN) (CurrWritePtr - LinearOffset),\r
328 &Size,\r
329 CurrBuffer\r
330 );\r
331 if (EFI_ERROR (Status)) {\r
332 return Status;\r
333 }\r
334\r
335 CurrWritePtr = LinearOffset + PtrBlockMapEntry->Length;\r
336 CurrBuffer = CurrBuffer + Size;\r
337 CurrWriteSize = CurrWriteSize - Size;\r
338 }\r
339 }\r
340\r
341 LinearOffset += PtrBlockMapEntry->Length;\r
342 LbaNumber++;\r
343 }\r
344 }\r
345\r
346 return EFI_SUCCESS;\r
347}\r
348\r
349\r
350/**\r
351\r
352 This code gets the current status of Variable Store.\r
353\r
354 @param VarStoreHeader Pointer to the Variable Store Header.\r
355\r
356 @retval EfiRaw Variable store status is raw.\r
357 @retval EfiValid Variable store status is valid.\r
358 @retval EfiInvalid Variable store status is invalid.\r
359\r
360**/\r
361VARIABLE_STORE_STATUS\r
362GetVariableStoreStatus (\r
363 IN VARIABLE_STORE_HEADER *VarStoreHeader\r
364 )\r
365{\r
366 if (CompareGuid (&VarStoreHeader->Signature, &gEfiAuthenticatedVariableGuid) &&\r
367 VarStoreHeader->Format == VARIABLE_STORE_FORMATTED &&\r
368 VarStoreHeader->State == VARIABLE_STORE_HEALTHY\r
369 ) {\r
370\r
371 return EfiValid;\r
372 } else if (((UINT32 *)(&VarStoreHeader->Signature))[0] == 0xffffffff &&\r
373 ((UINT32 *)(&VarStoreHeader->Signature))[1] == 0xffffffff &&\r
374 ((UINT32 *)(&VarStoreHeader->Signature))[2] == 0xffffffff &&\r
375 ((UINT32 *)(&VarStoreHeader->Signature))[3] == 0xffffffff &&\r
376 VarStoreHeader->Size == 0xffffffff &&\r
377 VarStoreHeader->Format == 0xff &&\r
378 VarStoreHeader->State == 0xff\r
379 ) {\r
380\r
381 return EfiRaw;\r
382 } else {\r
383 return EfiInvalid;\r
384 }\r
385}\r
386\r
387\r
388/**\r
389\r
390 This code gets the size of name of variable.\r
391\r
392 @param Variable Pointer to the Variable Header.\r
393\r
394 @return UINTN Size of variable in bytes.\r
395\r
396**/\r
397UINTN\r
398NameSizeOfVariable (\r
399 IN VARIABLE_HEADER *Variable\r
400 )\r
401{\r
402 if (Variable->State == (UINT8) (-1) ||\r
403 Variable->DataSize == (UINT32) (-1) ||\r
404 Variable->NameSize == (UINT32) (-1) ||\r
405 Variable->Attributes == (UINT32) (-1)) {\r
406 return 0;\r
407 }\r
408 return (UINTN) Variable->NameSize;\r
409}\r
410\r
411/**\r
412\r
413 This code gets the size of variable data.\r
414\r
415 @param Variable Pointer to the Variable Header.\r
416\r
417 @return Size of variable in bytes.\r
418\r
419**/\r
420UINTN\r
421DataSizeOfVariable (\r
422 IN VARIABLE_HEADER *Variable\r
423 )\r
424{\r
425 if (Variable->State == (UINT8) (-1) ||\r
426 Variable->DataSize == (UINT32) (-1) ||\r
427 Variable->NameSize == (UINT32) (-1) ||\r
428 Variable->Attributes == (UINT32) (-1)) {\r
429 return 0;\r
430 }\r
431 return (UINTN) Variable->DataSize;\r
432}\r
433\r
434/**\r
435\r
436 This code gets the pointer to the variable name.\r
437\r
438 @param Variable Pointer to the Variable Header.\r
439\r
440 @return Pointer to Variable Name which is Unicode encoding.\r
441\r
442**/\r
443CHAR16 *\r
444GetVariableNamePtr (\r
445 IN VARIABLE_HEADER *Variable\r
446 )\r
447{\r
448\r
449 return (CHAR16 *) (Variable + 1);\r
450}\r
451\r
452/**\r
453\r
454 This code gets the pointer to the variable data.\r
455\r
456 @param Variable Pointer to the Variable Header.\r
457\r
458 @return Pointer to Variable Data.\r
459\r
460**/\r
461UINT8 *\r
462GetVariableDataPtr (\r
463 IN VARIABLE_HEADER *Variable\r
464 )\r
465{\r
466 UINTN Value;\r
467\r
468 //\r
469 // Be careful about pad size for alignment.\r
470 //\r
471 Value = (UINTN) GetVariableNamePtr (Variable);\r
472 Value += NameSizeOfVariable (Variable);\r
473 Value += GET_PAD_SIZE (NameSizeOfVariable (Variable));\r
474\r
475 return (UINT8 *) Value;\r
476}\r
477\r
478\r
479/**\r
480\r
481 This code gets the pointer to the next variable header.\r
482\r
483 @param Variable Pointer to the Variable Header.\r
484\r
485 @return Pointer to next variable header.\r
486\r
487**/\r
488VARIABLE_HEADER *\r
489GetNextVariablePtr (\r
490 IN VARIABLE_HEADER *Variable\r
491 )\r
492{\r
493 UINTN Value;\r
494\r
495 Value = (UINTN) GetVariableDataPtr (Variable);\r
496 Value += DataSizeOfVariable (Variable);\r
497 Value += GET_PAD_SIZE (DataSizeOfVariable (Variable));\r
498\r
499 //\r
500 // Be careful about pad size for alignment.\r
501 //\r
502 return (VARIABLE_HEADER *) HEADER_ALIGN (Value);\r
503}\r
504\r
505/**\r
506\r
507 Gets the pointer to the first variable header in given variable store area.\r
508\r
509 @param VarStoreHeader Pointer to the Variable Store Header.\r
510\r
511 @return Pointer to the first variable header.\r
512\r
513**/\r
514VARIABLE_HEADER *\r
515GetStartPointer (\r
516 IN VARIABLE_STORE_HEADER *VarStoreHeader\r
517 )\r
518{\r
519 //\r
520 // The end of variable store.\r
521 //\r
522 return (VARIABLE_HEADER *) HEADER_ALIGN (VarStoreHeader + 1);\r
523}\r
524\r
525/**\r
526\r
527 Gets the pointer to the end of the variable storage area.\r
528\r
529 This function gets pointer to the end of the variable storage\r
530 area, according to the input variable store header.\r
531\r
532 @param VarStoreHeader Pointer to the Variable Store Header.\r
533\r
534 @return Pointer to the end of the variable storage area.\r
535\r
536**/\r
537VARIABLE_HEADER *\r
538GetEndPointer (\r
539 IN VARIABLE_STORE_HEADER *VarStoreHeader\r
540 )\r
541{\r
542 //\r
543 // The end of variable store\r
544 //\r
545 return (VARIABLE_HEADER *) HEADER_ALIGN ((UINTN) VarStoreHeader + VarStoreHeader->Size);\r
546}\r
547\r
548/**\r
549 Record variable error flag.\r
550\r
551 @param[in] Flag Variable error flag to record.\r
552 @param[in] VariableName Name of variable.\r
553 @param[in] VendorGuid Guid of variable.\r
554 @param[in] Attributes Attributes of the variable.\r
555 @param[in] VariableSize Size of the variable.\r
556\r
557**/\r
558VOID\r
559RecordVarErrorFlag (\r
560 IN VAR_ERROR_FLAG Flag,\r
561 IN CHAR16 *VariableName,\r
562 IN EFI_GUID *VendorGuid,\r
563 IN UINT32 Attributes,\r
564 IN UINTN VariableSize\r
565 )\r
566{\r
567 EFI_STATUS Status;\r
568 VARIABLE_POINTER_TRACK Variable;\r
569 VAR_ERROR_FLAG *VarErrFlag;\r
570 VAR_ERROR_FLAG TempFlag;\r
571\r
572 DEBUG_CODE (\r
573 DEBUG ((EFI_D_ERROR, "RecordVarErrorFlag (0x%02x) %s:%g - 0x%08x - 0x%x\n", Flag, VariableName, VendorGuid, Attributes, VariableSize));\r
574 if (Flag == VAR_ERROR_FLAG_SYSTEM_ERROR) {\r
575 if (AtRuntime ()) {\r
576 DEBUG ((EFI_D_ERROR, "CommonRuntimeVariableSpace = 0x%x - CommonVariableTotalSize = 0x%x\n", mVariableModuleGlobal->CommonRuntimeVariableSpace, mVariableModuleGlobal->CommonVariableTotalSize));\r
577 } else {\r
578 DEBUG ((EFI_D_ERROR, "CommonVariableSpace = 0x%x - CommonVariableTotalSize = 0x%x\n", mVariableModuleGlobal->CommonVariableSpace, mVariableModuleGlobal->CommonVariableTotalSize));\r
579 }\r
580 } else {\r
581 DEBUG ((EFI_D_ERROR, "CommonMaxUserVariableSpace = 0x%x - CommonUserVariableTotalSize = 0x%x\n", mVariableModuleGlobal->CommonMaxUserVariableSpace, mVariableModuleGlobal->CommonUserVariableTotalSize));\r
582 }\r
583 );\r
584\r
585 if (!mEndOfDxe) {\r
586 //\r
587 // Before EndOfDxe, just record the current boot variable error flag to local variable,\r
588 // and leave the variable error flag in NV flash as the last boot variable error flag.\r
589 // After EndOfDxe in InitializeVarErrorFlag (), the variable error flag in NV flash\r
590 // will be initialized to this local current boot variable error flag.\r
591 //\r
592 mCurrentBootVarErrFlag &= Flag;\r
593 return;\r
594 }\r
595\r
596 //\r
597 // Record error flag (it should have be initialized).\r
598 //\r
599 Status = FindVariable (\r
600 VAR_ERROR_FLAG_NAME,\r
601 &gEdkiiVarErrorFlagGuid,\r
602 &Variable,\r
603 &mVariableModuleGlobal->VariableGlobal,\r
604 FALSE\r
605 );\r
606 if (!EFI_ERROR (Status)) {\r
607 VarErrFlag = (VAR_ERROR_FLAG *) GetVariableDataPtr (Variable.CurrPtr);\r
608 TempFlag = *VarErrFlag;\r
609 TempFlag &= Flag;\r
610 if (TempFlag == *VarErrFlag) {\r
611 return;\r
612 }\r
613 Status = UpdateVariableStore (\r
614 &mVariableModuleGlobal->VariableGlobal,\r
615 FALSE,\r
616 FALSE,\r
617 mVariableModuleGlobal->FvbInstance,\r
618 (UINTN) VarErrFlag - (UINTN) mNvVariableCache + (UINTN) mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase,\r
619 sizeof (TempFlag),\r
620 &TempFlag\r
621 );\r
622 if (!EFI_ERROR (Status)) {\r
623 //\r
624 // Update the data in NV cache.\r
625 //\r
626 *VarErrFlag = Flag;\r
627 }\r
628 }\r
629}\r
630\r
631/**\r
632 Initialize variable error flag.\r
633\r
634 Before EndOfDxe, the variable indicates the last boot variable error flag,\r
635 then it means the last boot variable error flag must be got before EndOfDxe.\r
636 After EndOfDxe, the variable indicates the current boot variable error flag,\r
637 then it means the current boot variable error flag must be got after EndOfDxe.\r
638\r
639**/\r
640VOID\r
641InitializeVarErrorFlag (\r
642 VOID\r
643 )\r
644{\r
645 EFI_STATUS Status;\r
646 VARIABLE_POINTER_TRACK Variable;\r
647 VAR_ERROR_FLAG Flag;\r
648 VAR_ERROR_FLAG VarErrFlag;\r
649\r
650 if (!mEndOfDxe) {\r
651 return;\r
652 }\r
653\r
654 Flag = mCurrentBootVarErrFlag;\r
655 DEBUG ((EFI_D_INFO, "Initialize variable error flag (%02x)\n", Flag));\r
656\r
657 Status = FindVariable (\r
658 VAR_ERROR_FLAG_NAME,\r
659 &gEdkiiVarErrorFlagGuid,\r
660 &Variable,\r
661 &mVariableModuleGlobal->VariableGlobal,\r
662 FALSE\r
663 );\r
664 if (!EFI_ERROR (Status)) {\r
665 VarErrFlag = *((VAR_ERROR_FLAG *) GetVariableDataPtr (Variable.CurrPtr));\r
666 if (VarErrFlag == Flag) {\r
667 return;\r
668 }\r
669 }\r
670\r
671 UpdateVariable (\r
672 VAR_ERROR_FLAG_NAME,\r
673 &gEdkiiVarErrorFlagGuid,\r
674 &Flag,\r
675 sizeof (Flag),\r
676 VARIABLE_ATTRIBUTE_NV_BS_RT,\r
677 0,\r
678 0,\r
679 &Variable,\r
680 NULL\r
681 );\r
682}\r
683\r
684/**\r
685 Is user variable?\r
686\r
687 @param[in] Variable Pointer to variable header.\r
688\r
689 @retval TRUE User variable.\r
690 @retval FALSE System variable.\r
691\r
692**/\r
693BOOLEAN\r
694IsUserVariable (\r
695 IN VARIABLE_HEADER *Variable\r
696 )\r
697{\r
698 VAR_CHECK_VARIABLE_PROPERTY Property;\r
699\r
700 //\r
701 // Only after End Of Dxe, the variables belong to system variable are fixed.\r
702 // If PcdMaxUserNvStorageVariableSize is 0, it means user variable share the same NV storage with system variable,\r
703 // then no need to check if the variable is user variable or not specially.\r
704 //\r
705 if (mEndOfDxe && (mVariableModuleGlobal->CommonMaxUserVariableSpace != mVariableModuleGlobal->CommonVariableSpace)) {\r
706 if (InternalVarCheckVariablePropertyGet (GetVariableNamePtr (Variable), &Variable->VendorGuid, &Property) == EFI_NOT_FOUND) {\r
707 return TRUE;\r
708 }\r
709 }\r
710 return FALSE;\r
711}\r
712\r
713/**\r
714 Calculate common user variable total size.\r
715\r
716**/\r
717VOID\r
718CalculateCommonUserVariableTotalSize (\r
719 VOID\r
720 )\r
721{\r
722 VARIABLE_HEADER *Variable;\r
723 VARIABLE_HEADER *NextVariable;\r
724 UINTN VariableSize;\r
725 VAR_CHECK_VARIABLE_PROPERTY Property;\r
726\r
727 //\r
728 // Only after End Of Dxe, the variables belong to system variable are fixed.\r
729 // If PcdMaxUserNvStorageVariableSize is 0, it means user variable share the same NV storage with system variable,\r
730 // then no need to calculate the common user variable total size specially.\r
731 //\r
732 if (mEndOfDxe && (mVariableModuleGlobal->CommonMaxUserVariableSpace != mVariableModuleGlobal->CommonVariableSpace)) {\r
733 Variable = GetStartPointer (mNvVariableCache);\r
734 while (IsValidVariableHeader (Variable, GetEndPointer (mNvVariableCache))) {\r
735 NextVariable = GetNextVariablePtr (Variable);\r
736 VariableSize = (UINTN) NextVariable - (UINTN) Variable;\r
737 if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD) {\r
738 if (InternalVarCheckVariablePropertyGet (GetVariableNamePtr (Variable), &Variable->VendorGuid, &Property) == EFI_NOT_FOUND) {\r
739 //\r
740 // No property, it is user variable.\r
741 //\r
742 mVariableModuleGlobal->CommonUserVariableTotalSize += VariableSize;\r
743 }\r
744 }\r
745\r
746 Variable = NextVariable;\r
747 }\r
748 }\r
749}\r
750\r
751/**\r
752 Initialize variable quota.\r
753\r
754**/\r
755VOID\r
756InitializeVariableQuota (\r
757 VOID\r
758 )\r
759{\r
760 STATIC BOOLEAN Initialized;\r
761\r
762 if (!mEndOfDxe || Initialized) {\r
763 return;\r
764 }\r
765 Initialized = TRUE;\r
766\r
767 InitializeVarErrorFlag ();\r
768 CalculateCommonUserVariableTotalSize ();\r
769}\r
770\r
771/**\r
772\r
773 Check the PubKeyIndex is a valid key or not.\r
774\r
775 This function will iterate the NV storage to see if this PubKeyIndex is still referenced\r
776 by any valid count-based auth variabe.\r
777\r
778 @param[in] PubKeyIndex Index of the public key in public key store.\r
779\r
780 @retval TRUE The PubKeyIndex is still in use.\r
781 @retval FALSE The PubKeyIndex is not referenced by any count-based auth variabe.\r
782\r
783**/\r
784BOOLEAN\r
785IsValidPubKeyIndex (\r
786 IN UINT32 PubKeyIndex\r
787 )\r
788{\r
789 VARIABLE_HEADER *Variable;\r
790 VARIABLE_HEADER *VariableStoreEnd;\r
791\r
792 if (PubKeyIndex > mPubKeyNumber) {\r
793 return FALSE;\r
794 }\r
795\r
796 Variable = GetStartPointer ((VARIABLE_STORE_HEADER *) (UINTN) mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase);\r
797 VariableStoreEnd = GetEndPointer ((VARIABLE_STORE_HEADER *) (UINTN) mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase);\r
798\r
799 while (IsValidVariableHeader (Variable, VariableStoreEnd)) {\r
800 if ((Variable->State == VAR_ADDED || Variable->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED)) &&\r
801 Variable->PubKeyIndex == PubKeyIndex) {\r
802 return TRUE;\r
803 }\r
804 Variable = GetNextVariablePtr (Variable);\r
805 }\r
806\r
807 return FALSE;\r
808}\r
809\r
810/**\r
811\r
812 Get the number of valid public key in PubKeyStore.\r
813\r
814 @param[in] PubKeyNumber Number of the public key in public key store.\r
815\r
816 @return Number of valid public key in PubKeyStore.\r
817\r
818**/\r
819UINT32\r
820GetValidPubKeyNumber (\r
821 IN UINT32 PubKeyNumber\r
822 )\r
823{\r
824 UINT32 PubKeyIndex;\r
825 UINT32 Counter;\r
826\r
827 Counter = 0;\r
828\r
829 for (PubKeyIndex = 1; PubKeyIndex <= PubKeyNumber; PubKeyIndex++) {\r
830 if (IsValidPubKeyIndex (PubKeyIndex)) {\r
831 Counter++;\r
832 }\r
833 }\r
834\r
835 return Counter;\r
836}\r
837\r
838/**\r
839\r
840 Filter the useless key in public key store.\r
841\r
842 This function will find out all valid public keys in public key database, save them in new allocated\r
843 buffer NewPubKeyStore, and give the new PubKeyIndex. The caller is responsible for freeing buffer\r
844 NewPubKeyIndex and NewPubKeyStore with FreePool().\r
845\r
846 @param[in] PubKeyStore Point to the public key database.\r
847 @param[in] PubKeyNumber Number of the public key in PubKeyStore.\r
848 @param[out] NewPubKeyIndex Point to an array of new PubKeyIndex corresponds to NewPubKeyStore.\r
849 @param[out] NewPubKeyStore Saved all valid public keys in PubKeyStore.\r
850 @param[out] NewPubKeySize Buffer size of the NewPubKeyStore.\r
851\r
852 @retval EFI_SUCCESS Trim operation is complete successfully.\r
853 @retval EFI_OUT_OF_RESOURCES No enough memory resources, or no useless key in PubKeyStore.\r
854\r
855**/\r
856EFI_STATUS\r
857PubKeyStoreFilter (\r
858 IN UINT8 *PubKeyStore,\r
859 IN UINT32 PubKeyNumber,\r
860 OUT UINT32 **NewPubKeyIndex,\r
861 OUT UINT8 **NewPubKeyStore,\r
862 OUT UINT32 *NewPubKeySize\r
863 )\r
864{\r
865 UINT32 PubKeyIndex;\r
866 UINT32 CopiedKey;\r
867 UINT32 NewPubKeyNumber;\r
868\r
869 NewPubKeyNumber = GetValidPubKeyNumber (PubKeyNumber);\r
870 if (NewPubKeyNumber == PubKeyNumber) {\r
871 return EFI_OUT_OF_RESOURCES;\r
872 }\r
873\r
874 if (NewPubKeyNumber != 0) {\r
875 *NewPubKeySize = NewPubKeyNumber * EFI_CERT_TYPE_RSA2048_SIZE;\r
876 } else {\r
877 *NewPubKeySize = sizeof (UINT8);\r
878 }\r
879\r
880 *NewPubKeyStore = AllocatePool (*NewPubKeySize);\r
881 if (*NewPubKeyStore == NULL) {\r
882 return EFI_OUT_OF_RESOURCES;\r
883 }\r
884\r
885 *NewPubKeyIndex = AllocateZeroPool ((PubKeyNumber + 1) * sizeof (UINT32));\r
886 if (*NewPubKeyIndex == NULL) {\r
887 FreePool (*NewPubKeyStore);\r
888 *NewPubKeyStore = NULL;\r
889 return EFI_OUT_OF_RESOURCES;\r
890 }\r
891\r
892 CopiedKey = 0;\r
893 for (PubKeyIndex = 1; PubKeyIndex <= PubKeyNumber; PubKeyIndex++) {\r
894 if (IsValidPubKeyIndex (PubKeyIndex)) {\r
895 CopyMem (\r
896 *NewPubKeyStore + CopiedKey * EFI_CERT_TYPE_RSA2048_SIZE,\r
897 PubKeyStore + (PubKeyIndex - 1) * EFI_CERT_TYPE_RSA2048_SIZE,\r
898 EFI_CERT_TYPE_RSA2048_SIZE\r
899 );\r
900 (*NewPubKeyIndex)[PubKeyIndex] = ++CopiedKey;\r
901 }\r
902 }\r
903 return EFI_SUCCESS;\r
904}\r
905\r
906/**\r
907\r
908 Variable store garbage collection and reclaim operation.\r
909\r
910 If ReclaimPubKeyStore is FALSE, reclaim variable space by deleting the obsoleted varaibles.\r
911 If ReclaimPubKeyStore is TRUE, reclaim invalid key in public key database and update the PubKeyIndex\r
912 for all the count-based authenticate variable in NV storage.\r
913\r
914 @param[in] VariableBase Base address of variable store.\r
915 @param[out] LastVariableOffset Offset of last variable.\r
916 @param[in] IsVolatile The variable store is volatile or not;\r
917 if it is non-volatile, need FTW.\r
918 @param[in, out] UpdatingPtrTrack Pointer to updating variable pointer track structure.\r
919 @param[in] NewVariable Pointer to new variable.\r
920 @param[in] NewVariableSize New variable size.\r
921 @param[in] ReclaimPubKeyStore Reclaim for public key database or not.\r
922\r
923 @return EFI_SUCCESS Reclaim operation has finished successfully.\r
924 @return EFI_OUT_OF_RESOURCES No enough memory resources or variable space.\r
925 @return EFI_DEVICE_ERROR The public key database doesn't exist.\r
926 @return Others Unexpect error happened during reclaim operation.\r
927\r
928**/\r
929EFI_STATUS\r
930Reclaim (\r
931 IN EFI_PHYSICAL_ADDRESS VariableBase,\r
932 OUT UINTN *LastVariableOffset,\r
933 IN BOOLEAN IsVolatile,\r
934 IN OUT VARIABLE_POINTER_TRACK *UpdatingPtrTrack,\r
935 IN VARIABLE_HEADER *NewVariable,\r
936 IN UINTN NewVariableSize,\r
937 IN BOOLEAN ReclaimPubKeyStore\r
938 )\r
939{\r
940 VARIABLE_HEADER *Variable;\r
941 VARIABLE_HEADER *AddedVariable;\r
942 VARIABLE_HEADER *NextVariable;\r
943 VARIABLE_HEADER *NextAddedVariable;\r
944 VARIABLE_STORE_HEADER *VariableStoreHeader;\r
945 UINT8 *ValidBuffer;\r
946 UINTN MaximumBufferSize;\r
947 UINTN VariableSize;\r
948 UINTN NameSize;\r
949 UINT8 *CurrPtr;\r
950 VOID *Point0;\r
951 VOID *Point1;\r
952 BOOLEAN FoundAdded;\r
953 EFI_STATUS Status;\r
954 UINTN CommonVariableTotalSize;\r
955 UINTN CommonUserVariableTotalSize;\r
956 UINTN HwErrVariableTotalSize;\r
957 UINT32 *NewPubKeyIndex;\r
958 UINT8 *NewPubKeyStore;\r
959 UINT32 NewPubKeySize;\r
960 VARIABLE_HEADER *PubKeyHeader;\r
961 VARIABLE_HEADER *UpdatingVariable;\r
962 VARIABLE_HEADER *UpdatingInDeletedTransition;\r
963\r
964 UpdatingVariable = NULL;\r
965 UpdatingInDeletedTransition = NULL;\r
966 if (UpdatingPtrTrack != NULL) {\r
967 UpdatingVariable = UpdatingPtrTrack->CurrPtr;\r
968 UpdatingInDeletedTransition = UpdatingPtrTrack->InDeletedTransitionPtr;\r
969 }\r
970\r
971 VariableStoreHeader = (VARIABLE_STORE_HEADER *) ((UINTN) VariableBase);\r
972\r
973 CommonVariableTotalSize = 0;\r
974 CommonUserVariableTotalSize = 0;\r
975 HwErrVariableTotalSize = 0;\r
976 NewPubKeyIndex = NULL;\r
977 NewPubKeyStore = NULL;\r
978 NewPubKeySize = 0;\r
979 PubKeyHeader = NULL;\r
980\r
981 if (IsVolatile) {\r
982 //\r
983 // Start Pointers for the variable.\r
984 //\r
985 Variable = GetStartPointer (VariableStoreHeader);\r
986 MaximumBufferSize = sizeof (VARIABLE_STORE_HEADER);\r
987\r
988 while (IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader))) {\r
989 NextVariable = GetNextVariablePtr (Variable);\r
990 if ((Variable->State == VAR_ADDED || Variable->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED)) &&\r
991 Variable != UpdatingVariable &&\r
992 Variable != UpdatingInDeletedTransition\r
993 ) {\r
994 VariableSize = (UINTN) NextVariable - (UINTN) Variable;\r
995 MaximumBufferSize += VariableSize;\r
996 }\r
997\r
998 Variable = NextVariable;\r
999 }\r
1000\r
1001 if (NewVariable != NULL) {\r
1002 //\r
1003 // Add the new variable size.\r
1004 //\r
1005 MaximumBufferSize += NewVariableSize;\r
1006 }\r
1007\r
1008 //\r
1009 // Reserve the 1 Bytes with Oxff to identify the\r
1010 // end of the variable buffer.\r
1011 //\r
1012 MaximumBufferSize += 1;\r
1013 ValidBuffer = AllocatePool (MaximumBufferSize);\r
1014 if (ValidBuffer == NULL) {\r
1015 return EFI_OUT_OF_RESOURCES;\r
1016 }\r
1017 } else {\r
1018 //\r
1019 // For NV variable reclaim, don't allocate pool here and just use mNvVariableCache\r
1020 // as the buffer to reduce SMRAM consumption for SMM variable driver.\r
1021 //\r
1022 MaximumBufferSize = mNvVariableCache->Size;\r
1023 ValidBuffer = (UINT8 *) mNvVariableCache;\r
1024 }\r
1025\r
1026 SetMem (ValidBuffer, MaximumBufferSize, 0xff);\r
1027\r
1028 //\r
1029 // Copy variable store header.\r
1030 //\r
1031 CopyMem (ValidBuffer, VariableStoreHeader, sizeof (VARIABLE_STORE_HEADER));\r
1032 CurrPtr = (UINT8 *) GetStartPointer ((VARIABLE_STORE_HEADER *) ValidBuffer);\r
1033\r
1034 if (ReclaimPubKeyStore) {\r
1035 ASSERT (IsVolatile == FALSE);\r
1036 //\r
1037 // Trim the PubKeyStore and get new PubKeyIndex.\r
1038 //\r
1039 Status = PubKeyStoreFilter (\r
1040 mPubKeyStore,\r
1041 mPubKeyNumber,\r
1042 &NewPubKeyIndex,\r
1043 &NewPubKeyStore,\r
1044 &NewPubKeySize\r
1045 );\r
1046 if (EFI_ERROR (Status)) {\r
1047 goto Done;\r
1048 }\r
1049 ASSERT ((NewPubKeyIndex != NULL) && (NewPubKeyStore != NULL));\r
1050\r
1051 //\r
1052 // Refresh the PubKeyIndex for all valid variables (ADDED and IN_DELETED_TRANSITION).\r
1053 //\r
1054 Variable = GetStartPointer (VariableStoreHeader);\r
1055 while (IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader))) {\r
1056 NextVariable = GetNextVariablePtr (Variable);\r
1057 if (Variable->State == VAR_ADDED || Variable->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED)) {\r
1058 if ((StrCmp (GetVariableNamePtr (Variable), AUTHVAR_KEYDB_NAME) == 0) &&\r
1059 (CompareGuid (&Variable->VendorGuid, &gEfiAuthenticatedVariableGuid))) {\r
1060 //\r
1061 // Skip the public key database, it will be reinstalled later.\r
1062 //\r
1063 PubKeyHeader = Variable;\r
1064 Variable = NextVariable;\r
1065 continue;\r
1066 }\r
1067\r
1068 VariableSize = (UINTN) NextVariable - (UINTN) Variable;\r
1069 CopyMem (CurrPtr, (UINT8 *) Variable, VariableSize);\r
1070 ((VARIABLE_HEADER*) CurrPtr)->PubKeyIndex = NewPubKeyIndex[Variable->PubKeyIndex];\r
1071 CurrPtr += VariableSize;\r
1072 if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) {\r
1073 HwErrVariableTotalSize += VariableSize;\r
1074 } else if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD) {\r
1075 CommonVariableTotalSize += VariableSize;\r
1076 if (IsUserVariable (Variable)) {\r
1077 CommonUserVariableTotalSize += VariableSize;\r
1078 }\r
1079 }\r
1080 }\r
1081 Variable = NextVariable;\r
1082 }\r
1083\r
1084 //\r
1085 // Reinstall the new public key database.\r
1086 //\r
1087 ASSERT (PubKeyHeader != NULL);\r
1088 if (PubKeyHeader == NULL) {\r
1089 Status = EFI_DEVICE_ERROR;\r
1090 goto Done;\r
1091 }\r
1092 CopyMem (CurrPtr, (UINT8*) PubKeyHeader, sizeof (VARIABLE_HEADER));\r
1093 Variable = (VARIABLE_HEADER*) CurrPtr;\r
1094 Variable->DataSize = NewPubKeySize;\r
1095 StrCpy (GetVariableNamePtr (Variable), GetVariableNamePtr (PubKeyHeader));\r
1096 CopyMem (GetVariableDataPtr (Variable), NewPubKeyStore, NewPubKeySize);\r
1097 CurrPtr = (UINT8*) GetNextVariablePtr (Variable);\r
1098 CommonVariableTotalSize += (UINTN) CurrPtr - (UINTN) Variable;\r
1099 if (IsUserVariable (Variable)) {\r
1100 CommonUserVariableTotalSize += (UINTN) CurrPtr - (UINTN) Variable;\r
1101 }\r
1102 } else {\r
1103 //\r
1104 // Reinstall all ADDED variables as long as they are not identical to Updating Variable.\r
1105 //\r
1106 Variable = GetStartPointer (VariableStoreHeader);\r
1107 while (IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader))) {\r
1108 NextVariable = GetNextVariablePtr (Variable);\r
1109 if (Variable != UpdatingVariable && Variable->State == VAR_ADDED) {\r
1110 VariableSize = (UINTN) NextVariable - (UINTN) Variable;\r
1111 CopyMem (CurrPtr, (UINT8 *) Variable, VariableSize);\r
1112 CurrPtr += VariableSize;\r
1113 if ((!IsVolatile) && ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD)) {\r
1114 HwErrVariableTotalSize += VariableSize;\r
1115 } else if ((!IsVolatile) && ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD)) {\r
1116 CommonVariableTotalSize += VariableSize;\r
1117 if (IsUserVariable (Variable)) {\r
1118 CommonUserVariableTotalSize += VariableSize;\r
1119 }\r
1120 }\r
1121 }\r
1122 Variable = NextVariable;\r
1123 }\r
1124\r
1125 //\r
1126 // Reinstall all in delete transition variables.\r
1127 //\r
1128 Variable = GetStartPointer (VariableStoreHeader);\r
1129 while (IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader))) {\r
1130 NextVariable = GetNextVariablePtr (Variable);\r
1131 if (Variable != UpdatingVariable && Variable != UpdatingInDeletedTransition && Variable->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED)) {\r
1132\r
1133 //\r
1134 // Buffer has cached all ADDED variable.\r
1135 // Per IN_DELETED variable, we have to guarantee that\r
1136 // no ADDED one in previous buffer.\r
1137 //\r
1138\r
1139 FoundAdded = FALSE;\r
1140 AddedVariable = GetStartPointer ((VARIABLE_STORE_HEADER *) ValidBuffer);\r
1141 while (IsValidVariableHeader (AddedVariable, GetEndPointer ((VARIABLE_STORE_HEADER *) ValidBuffer))) {\r
1142 NextAddedVariable = GetNextVariablePtr (AddedVariable);\r
1143 NameSize = NameSizeOfVariable (AddedVariable);\r
1144 if (CompareGuid (&AddedVariable->VendorGuid, &Variable->VendorGuid) &&\r
1145 NameSize == NameSizeOfVariable (Variable)\r
1146 ) {\r
1147 Point0 = (VOID *) GetVariableNamePtr (AddedVariable);\r
1148 Point1 = (VOID *) GetVariableNamePtr (Variable);\r
1149 if (CompareMem (Point0, Point1, NameSize) == 0) {\r
1150 FoundAdded = TRUE;\r
1151 break;\r
1152 }\r
1153 }\r
1154 AddedVariable = NextAddedVariable;\r
1155 }\r
1156 if (!FoundAdded) {\r
1157 //\r
1158 // Promote VAR_IN_DELETED_TRANSITION to VAR_ADDED.\r
1159 //\r
1160 VariableSize = (UINTN) NextVariable - (UINTN) Variable;\r
1161 CopyMem (CurrPtr, (UINT8 *) Variable, VariableSize);\r
1162 ((VARIABLE_HEADER *) CurrPtr)->State = VAR_ADDED;\r
1163 CurrPtr += VariableSize;\r
1164 if ((!IsVolatile) && ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD)) {\r
1165 HwErrVariableTotalSize += VariableSize;\r
1166 } else if ((!IsVolatile) && ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD)) {\r
1167 CommonVariableTotalSize += VariableSize;\r
1168 if (IsUserVariable (Variable)) {\r
1169 CommonUserVariableTotalSize += VariableSize;\r
1170 }\r
1171 }\r
1172 }\r
1173 }\r
1174\r
1175 Variable = NextVariable;\r
1176 }\r
1177\r
1178 //\r
1179 // Install the new variable if it is not NULL.\r
1180 //\r
1181 if (NewVariable != NULL) {\r
1182 if ((UINTN) (CurrPtr - ValidBuffer) + NewVariableSize > VariableStoreHeader->Size) {\r
1183 //\r
1184 // No enough space to store the new variable.\r
1185 //\r
1186 Status = EFI_OUT_OF_RESOURCES;\r
1187 goto Done;\r
1188 }\r
1189 if (!IsVolatile) {\r
1190 if ((NewVariable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) {\r
1191 HwErrVariableTotalSize += NewVariableSize;\r
1192 } else if ((NewVariable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD) {\r
1193 CommonVariableTotalSize += NewVariableSize;\r
1194 if (IsUserVariable (NewVariable)) {\r
1195 CommonUserVariableTotalSize += NewVariableSize;\r
1196 }\r
1197 }\r
1198 if ((HwErrVariableTotalSize > PcdGet32 (PcdHwErrStorageSize)) ||\r
1199 (CommonVariableTotalSize > mVariableModuleGlobal->CommonVariableSpace) ||\r
1200 (CommonUserVariableTotalSize > mVariableModuleGlobal->CommonMaxUserVariableSpace)) {\r
1201 //\r
1202 // No enough space to store the new variable by NV or NV+HR attribute.\r
1203 //\r
1204 Status = EFI_OUT_OF_RESOURCES;\r
1205 goto Done;\r
1206 }\r
1207 }\r
1208\r
1209 CopyMem (CurrPtr, (UINT8 *) NewVariable, NewVariableSize);\r
1210 ((VARIABLE_HEADER *) CurrPtr)->State = VAR_ADDED;\r
1211 if (UpdatingVariable != NULL) {\r
1212 UpdatingPtrTrack->CurrPtr = (VARIABLE_HEADER *)((UINTN)UpdatingPtrTrack->StartPtr + ((UINTN)CurrPtr - (UINTN)GetStartPointer ((VARIABLE_STORE_HEADER *) ValidBuffer)));\r
1213 UpdatingPtrTrack->InDeletedTransitionPtr = NULL;\r
1214 }\r
1215 CurrPtr += NewVariableSize;\r
1216 }\r
1217 }\r
1218\r
1219 if (IsVolatile) {\r
1220 //\r
1221 // If volatile variable store, just copy valid buffer.\r
1222 //\r
1223 SetMem ((UINT8 *) (UINTN) VariableBase, VariableStoreHeader->Size, 0xff);\r
1224 CopyMem ((UINT8 *) (UINTN) VariableBase, ValidBuffer, (UINTN) (CurrPtr - ValidBuffer));\r
1225 *LastVariableOffset = (UINTN) (CurrPtr - ValidBuffer);\r
1226 Status = EFI_SUCCESS;\r
1227 } else {\r
1228 //\r
1229 // If non-volatile variable store, perform FTW here.\r
1230 //\r
1231 Status = FtwVariableSpace (\r
1232 VariableBase,\r
1233 (VARIABLE_STORE_HEADER *) ValidBuffer\r
1234 );\r
1235 if (!EFI_ERROR (Status)) {\r
1236 *LastVariableOffset = (UINTN) (CurrPtr - ValidBuffer);\r
1237 mVariableModuleGlobal->HwErrVariableTotalSize = HwErrVariableTotalSize;\r
1238 mVariableModuleGlobal->CommonVariableTotalSize = CommonVariableTotalSize;\r
1239 mVariableModuleGlobal->CommonUserVariableTotalSize = CommonUserVariableTotalSize;\r
1240 } else {\r
1241 Variable = GetStartPointer ((VARIABLE_STORE_HEADER *)(UINTN)VariableBase);\r
1242 while (IsValidVariableHeader (Variable, GetEndPointer ((VARIABLE_STORE_HEADER *)(UINTN)VariableBase))) {\r
1243 NextVariable = GetNextVariablePtr (Variable);\r
1244 VariableSize = (UINTN) NextVariable - (UINTN) Variable;\r
1245 if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) {\r
1246 mVariableModuleGlobal->HwErrVariableTotalSize += VariableSize;\r
1247 } else if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD) {\r
1248 mVariableModuleGlobal->CommonVariableTotalSize += VariableSize;\r
1249 if (IsUserVariable (Variable)) {\r
1250 mVariableModuleGlobal->CommonUserVariableTotalSize += VariableSize;\r
1251 }\r
1252 }\r
1253\r
1254 Variable = NextVariable;\r
1255 }\r
1256 *LastVariableOffset = (UINTN) Variable - (UINTN) VariableBase;\r
1257 }\r
1258 }\r
1259\r
1260Done:\r
1261 if (IsVolatile) {\r
1262 FreePool (ValidBuffer);\r
1263 } else {\r
1264 //\r
1265 // For NV variable reclaim, we use mNvVariableCache as the buffer, so copy the data back.\r
1266 //\r
1267 CopyMem (mNvVariableCache, (UINT8 *)(UINTN)VariableBase, VariableStoreHeader->Size);\r
1268\r
1269 if (NewPubKeyStore != NULL) {\r
1270 FreePool (NewPubKeyStore);\r
1271 }\r
1272\r
1273 if (NewPubKeyIndex != NULL) {\r
1274 FreePool (NewPubKeyIndex);\r
1275 }\r
1276 }\r
1277\r
1278 return Status;\r
1279}\r
1280\r
1281/**\r
1282 Find the variable in the specified variable store.\r
1283\r
1284 @param[in] VariableName Name of the variable to be found\r
1285 @param[in] VendorGuid Vendor GUID to be found.\r
1286 @param[in] IgnoreRtCheck Ignore EFI_VARIABLE_RUNTIME_ACCESS attribute\r
1287 check at runtime when searching variable.\r
1288 @param[in, out] PtrTrack Variable Track Pointer structure that contains Variable Information.\r
1289\r
1290 @retval EFI_SUCCESS Variable found successfully\r
1291 @retval EFI_NOT_FOUND Variable not found\r
1292**/\r
1293EFI_STATUS\r
1294FindVariableEx (\r
1295 IN CHAR16 *VariableName,\r
1296 IN EFI_GUID *VendorGuid,\r
1297 IN BOOLEAN IgnoreRtCheck,\r
1298 IN OUT VARIABLE_POINTER_TRACK *PtrTrack\r
1299 )\r
1300{\r
1301 VARIABLE_HEADER *InDeletedVariable;\r
1302 VOID *Point;\r
1303\r
1304 PtrTrack->InDeletedTransitionPtr = NULL;\r
1305\r
1306 //\r
1307 // Find the variable by walk through HOB, volatile and non-volatile variable store.\r
1308 //\r
1309 InDeletedVariable = NULL;\r
1310\r
1311 for ( PtrTrack->CurrPtr = PtrTrack->StartPtr\r
1312 ; IsValidVariableHeader (PtrTrack->CurrPtr, PtrTrack->EndPtr)\r
1313 ; PtrTrack->CurrPtr = GetNextVariablePtr (PtrTrack->CurrPtr)\r
1314 ) {\r
1315 if (PtrTrack->CurrPtr->State == VAR_ADDED ||\r
1316 PtrTrack->CurrPtr->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED)\r
1317 ) {\r
1318 if (IgnoreRtCheck || !AtRuntime () || ((PtrTrack->CurrPtr->Attributes & EFI_VARIABLE_RUNTIME_ACCESS) != 0)) {\r
1319 if (VariableName[0] == 0) {\r
1320 if (PtrTrack->CurrPtr->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED)) {\r
1321 InDeletedVariable = PtrTrack->CurrPtr;\r
1322 } else {\r
1323 PtrTrack->InDeletedTransitionPtr = InDeletedVariable;\r
1324 return EFI_SUCCESS;\r
1325 }\r
1326 } else {\r
1327 if (CompareGuid (VendorGuid, &PtrTrack->CurrPtr->VendorGuid)) {\r
1328 Point = (VOID *) GetVariableNamePtr (PtrTrack->CurrPtr);\r
1329\r
1330 ASSERT (NameSizeOfVariable (PtrTrack->CurrPtr) != 0);\r
1331 if (CompareMem (VariableName, Point, NameSizeOfVariable (PtrTrack->CurrPtr)) == 0) {\r
1332 if (PtrTrack->CurrPtr->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED)) {\r
1333 InDeletedVariable = PtrTrack->CurrPtr;\r
1334 } else {\r
1335 PtrTrack->InDeletedTransitionPtr = InDeletedVariable;\r
1336 return EFI_SUCCESS;\r
1337 }\r
1338 }\r
1339 }\r
1340 }\r
1341 }\r
1342 }\r
1343 }\r
1344\r
1345 PtrTrack->CurrPtr = InDeletedVariable;\r
1346 return (PtrTrack->CurrPtr == NULL) ? EFI_NOT_FOUND : EFI_SUCCESS;\r
1347}\r
1348\r
1349\r
1350/**\r
1351 Finds variable in storage blocks of volatile and non-volatile storage areas.\r
1352\r
1353 This code finds variable in storage blocks of volatile and non-volatile storage areas.\r
1354 If VariableName is an empty string, then we just return the first\r
1355 qualified variable without comparing VariableName and VendorGuid.\r
1356 If IgnoreRtCheck is TRUE, then we ignore the EFI_VARIABLE_RUNTIME_ACCESS attribute check\r
1357 at runtime when searching existing variable, only VariableName and VendorGuid are compared.\r
1358 Otherwise, variables without EFI_VARIABLE_RUNTIME_ACCESS are not visible at runtime.\r
1359\r
1360 @param[in] VariableName Name of the variable to be found.\r
1361 @param[in] VendorGuid Vendor GUID to be found.\r
1362 @param[out] PtrTrack VARIABLE_POINTER_TRACK structure for output,\r
1363 including the range searched and the target position.\r
1364 @param[in] Global Pointer to VARIABLE_GLOBAL structure, including\r
1365 base of volatile variable storage area, base of\r
1366 NV variable storage area, and a lock.\r
1367 @param[in] IgnoreRtCheck Ignore EFI_VARIABLE_RUNTIME_ACCESS attribute\r
1368 check at runtime when searching variable.\r
1369\r
1370 @retval EFI_INVALID_PARAMETER If VariableName is not an empty string, while\r
1371 VendorGuid is NULL.\r
1372 @retval EFI_SUCCESS Variable successfully found.\r
1373 @retval EFI_NOT_FOUND Variable not found\r
1374\r
1375**/\r
1376EFI_STATUS\r
1377FindVariable (\r
1378 IN CHAR16 *VariableName,\r
1379 IN EFI_GUID *VendorGuid,\r
1380 OUT VARIABLE_POINTER_TRACK *PtrTrack,\r
1381 IN VARIABLE_GLOBAL *Global,\r
1382 IN BOOLEAN IgnoreRtCheck\r
1383 )\r
1384{\r
1385 EFI_STATUS Status;\r
1386 VARIABLE_STORE_HEADER *VariableStoreHeader[VariableStoreTypeMax];\r
1387 VARIABLE_STORE_TYPE Type;\r
1388\r
1389 if (VariableName[0] != 0 && VendorGuid == NULL) {\r
1390 return EFI_INVALID_PARAMETER;\r
1391 }\r
1392\r
1393 //\r
1394 // 0: Volatile, 1: HOB, 2: Non-Volatile.\r
1395 // The index and attributes mapping must be kept in this order as RuntimeServiceGetNextVariableName\r
1396 // make use of this mapping to implement search algorithm.\r
1397 //\r
1398 VariableStoreHeader[VariableStoreTypeVolatile] = (VARIABLE_STORE_HEADER *) (UINTN) Global->VolatileVariableBase;\r
1399 VariableStoreHeader[VariableStoreTypeHob] = (VARIABLE_STORE_HEADER *) (UINTN) Global->HobVariableBase;\r
1400 VariableStoreHeader[VariableStoreTypeNv] = mNvVariableCache;\r
1401\r
1402 //\r
1403 // Find the variable by walk through HOB, volatile and non-volatile variable store.\r
1404 //\r
1405 for (Type = (VARIABLE_STORE_TYPE) 0; Type < VariableStoreTypeMax; Type++) {\r
1406 if (VariableStoreHeader[Type] == NULL) {\r
1407 continue;\r
1408 }\r
1409\r
1410 PtrTrack->StartPtr = GetStartPointer (VariableStoreHeader[Type]);\r
1411 PtrTrack->EndPtr = GetEndPointer (VariableStoreHeader[Type]);\r
1412 PtrTrack->Volatile = (BOOLEAN) (Type == VariableStoreTypeVolatile);\r
1413\r
1414 Status = FindVariableEx (VariableName, VendorGuid, IgnoreRtCheck, PtrTrack);\r
1415 if (!EFI_ERROR (Status)) {\r
1416 return Status;\r
1417 }\r
1418 }\r
1419 return EFI_NOT_FOUND;\r
1420}\r
1421\r
1422/**\r
1423 Get index from supported language codes according to language string.\r
1424\r
1425 This code is used to get corresponding index in supported language codes. It can handle\r
1426 RFC4646 and ISO639 language tags.\r
1427 In ISO639 language tags, take 3-characters as a delimitation to find matched string and calculate the index.\r
1428 In RFC4646 language tags, take semicolon as a delimitation to find matched string and calculate the index.\r
1429\r
1430 For example:\r
1431 SupportedLang = "engfraengfra"\r
1432 Lang = "eng"\r
1433 Iso639Language = TRUE\r
1434 The return value is "0".\r
1435 Another example:\r
1436 SupportedLang = "en;fr;en-US;fr-FR"\r
1437 Lang = "fr-FR"\r
1438 Iso639Language = FALSE\r
1439 The return value is "3".\r
1440\r
1441 @param SupportedLang Platform supported language codes.\r
1442 @param Lang Configured language.\r
1443 @param Iso639Language A bool value to signify if the handler is operated on ISO639 or RFC4646.\r
1444\r
1445 @retval The index of language in the language codes.\r
1446\r
1447**/\r
1448UINTN\r
1449GetIndexFromSupportedLangCodes(\r
1450 IN CHAR8 *SupportedLang,\r
1451 IN CHAR8 *Lang,\r
1452 IN BOOLEAN Iso639Language\r
1453 )\r
1454{\r
1455 UINTN Index;\r
1456 UINTN CompareLength;\r
1457 UINTN LanguageLength;\r
1458\r
1459 if (Iso639Language) {\r
1460 CompareLength = ISO_639_2_ENTRY_SIZE;\r
1461 for (Index = 0; Index < AsciiStrLen (SupportedLang); Index += CompareLength) {\r
1462 if (AsciiStrnCmp (Lang, SupportedLang + Index, CompareLength) == 0) {\r
1463 //\r
1464 // Successfully find the index of Lang string in SupportedLang string.\r
1465 //\r
1466 Index = Index / CompareLength;\r
1467 return Index;\r
1468 }\r
1469 }\r
1470 ASSERT (FALSE);\r
1471 return 0;\r
1472 } else {\r
1473 //\r
1474 // Compare RFC4646 language code\r
1475 //\r
1476 Index = 0;\r
1477 for (LanguageLength = 0; Lang[LanguageLength] != '\0'; LanguageLength++);\r
1478\r
1479 for (Index = 0; *SupportedLang != '\0'; Index++, SupportedLang += CompareLength) {\r
1480 //\r
1481 // Skip ';' characters in SupportedLang\r
1482 //\r
1483 for (; *SupportedLang != '\0' && *SupportedLang == ';'; SupportedLang++);\r
1484 //\r
1485 // Determine the length of the next language code in SupportedLang\r
1486 //\r
1487 for (CompareLength = 0; SupportedLang[CompareLength] != '\0' && SupportedLang[CompareLength] != ';'; CompareLength++);\r
1488\r
1489 if ((CompareLength == LanguageLength) &&\r
1490 (AsciiStrnCmp (Lang, SupportedLang, CompareLength) == 0)) {\r
1491 //\r
1492 // Successfully find the index of Lang string in SupportedLang string.\r
1493 //\r
1494 return Index;\r
1495 }\r
1496 }\r
1497 ASSERT (FALSE);\r
1498 return 0;\r
1499 }\r
1500}\r
1501\r
1502/**\r
1503 Get language string from supported language codes according to index.\r
1504\r
1505 This code is used to get corresponding language strings in supported language codes. It can handle\r
1506 RFC4646 and ISO639 language tags.\r
1507 In ISO639 language tags, take 3-characters as a delimitation. Find language string according to the index.\r
1508 In RFC4646 language tags, take semicolon as a delimitation. Find language string according to the index.\r
1509\r
1510 For example:\r
1511 SupportedLang = "engfraengfra"\r
1512 Index = "1"\r
1513 Iso639Language = TRUE\r
1514 The return value is "fra".\r
1515 Another example:\r
1516 SupportedLang = "en;fr;en-US;fr-FR"\r
1517 Index = "1"\r
1518 Iso639Language = FALSE\r
1519 The return value is "fr".\r
1520\r
1521 @param SupportedLang Platform supported language codes.\r
1522 @param Index The index in supported language codes.\r
1523 @param Iso639Language A bool value to signify if the handler is operated on ISO639 or RFC4646.\r
1524\r
1525 @retval The language string in the language codes.\r
1526\r
1527**/\r
1528CHAR8 *\r
1529GetLangFromSupportedLangCodes (\r
1530 IN CHAR8 *SupportedLang,\r
1531 IN UINTN Index,\r
1532 IN BOOLEAN Iso639Language\r
1533)\r
1534{\r
1535 UINTN SubIndex;\r
1536 UINTN CompareLength;\r
1537 CHAR8 *Supported;\r
1538\r
1539 SubIndex = 0;\r
1540 Supported = SupportedLang;\r
1541 if (Iso639Language) {\r
1542 //\r
1543 // According to the index of Lang string in SupportedLang string to get the language.\r
1544 // This code will be invoked in RUNTIME, therefore there is not a memory allocate/free operation.\r
1545 // In driver entry, it pre-allocates a runtime attribute memory to accommodate this string.\r
1546 //\r
1547 CompareLength = ISO_639_2_ENTRY_SIZE;\r
1548 mVariableModuleGlobal->Lang[CompareLength] = '\0';\r
1549 return CopyMem (mVariableModuleGlobal->Lang, SupportedLang + Index * CompareLength, CompareLength);\r
1550\r
1551 } else {\r
1552 while (TRUE) {\r
1553 //\r
1554 // Take semicolon as delimitation, sequentially traverse supported language codes.\r
1555 //\r
1556 for (CompareLength = 0; *Supported != ';' && *Supported != '\0'; CompareLength++) {\r
1557 Supported++;\r
1558 }\r
1559 if ((*Supported == '\0') && (SubIndex != Index)) {\r
1560 //\r
1561 // Have completed the traverse, but not find corrsponding string.\r
1562 // This case is not allowed to happen.\r
1563 //\r
1564 ASSERT(FALSE);\r
1565 return NULL;\r
1566 }\r
1567 if (SubIndex == Index) {\r
1568 //\r
1569 // According to the index of Lang string in SupportedLang string to get the language.\r
1570 // As this code will be invoked in RUNTIME, therefore there is not memory allocate/free operation.\r
1571 // In driver entry, it pre-allocates a runtime attribute memory to accommodate this string.\r
1572 //\r
1573 mVariableModuleGlobal->PlatformLang[CompareLength] = '\0';\r
1574 return CopyMem (mVariableModuleGlobal->PlatformLang, Supported - CompareLength, CompareLength);\r
1575 }\r
1576 SubIndex++;\r
1577\r
1578 //\r
1579 // Skip ';' characters in Supported\r
1580 //\r
1581 for (; *Supported != '\0' && *Supported == ';'; Supported++);\r
1582 }\r
1583 }\r
1584}\r
1585\r
1586/**\r
1587 Returns a pointer to an allocated buffer that contains the best matching language\r
1588 from a set of supported languages.\r
1589\r
1590 This function supports both ISO 639-2 and RFC 4646 language codes, but language\r
1591 code types may not be mixed in a single call to this function. This function\r
1592 supports a variable argument list that allows the caller to pass in a prioritized\r
1593 list of language codes to test against all the language codes in SupportedLanguages.\r
1594\r
1595 If SupportedLanguages is NULL, then ASSERT().\r
1596\r
1597 @param[in] SupportedLanguages A pointer to a Null-terminated ASCII string that\r
1598 contains a set of language codes in the format\r
1599 specified by Iso639Language.\r
1600 @param[in] Iso639Language If TRUE, then all language codes are assumed to be\r
1601 in ISO 639-2 format. If FALSE, then all language\r
1602 codes are assumed to be in RFC 4646 language format\r
1603 @param[in] ... A variable argument list that contains pointers to\r
1604 Null-terminated ASCII strings that contain one or more\r
1605 language codes in the format specified by Iso639Language.\r
1606 The first language code from each of these language\r
1607 code lists is used to determine if it is an exact or\r
1608 close match to any of the language codes in\r
1609 SupportedLanguages. Close matches only apply to RFC 4646\r
1610 language codes, and the matching algorithm from RFC 4647\r
1611 is used to determine if a close match is present. If\r
1612 an exact or close match is found, then the matching\r
1613 language code from SupportedLanguages is returned. If\r
1614 no matches are found, then the next variable argument\r
1615 parameter is evaluated. The variable argument list\r
1616 is terminated by a NULL.\r
1617\r
1618 @retval NULL The best matching language could not be found in SupportedLanguages.\r
1619 @retval NULL There are not enough resources available to return the best matching\r
1620 language.\r
1621 @retval Other A pointer to a Null-terminated ASCII string that is the best matching\r
1622 language in SupportedLanguages.\r
1623\r
1624**/\r
1625CHAR8 *\r
1626EFIAPI\r
1627VariableGetBestLanguage (\r
1628 IN CONST CHAR8 *SupportedLanguages,\r
1629 IN BOOLEAN Iso639Language,\r
1630 ...\r
1631 )\r
1632{\r
1633 VA_LIST Args;\r
1634 CHAR8 *Language;\r
1635 UINTN CompareLength;\r
1636 UINTN LanguageLength;\r
1637 CONST CHAR8 *Supported;\r
1638 CHAR8 *Buffer;\r
1639\r
1640 if (SupportedLanguages == NULL) {\r
1641 return NULL;\r
1642 }\r
1643\r
1644 VA_START (Args, Iso639Language);\r
1645 while ((Language = VA_ARG (Args, CHAR8 *)) != NULL) {\r
1646 //\r
1647 // Default to ISO 639-2 mode\r
1648 //\r
1649 CompareLength = 3;\r
1650 LanguageLength = MIN (3, AsciiStrLen (Language));\r
1651\r
1652 //\r
1653 // If in RFC 4646 mode, then determine the length of the first RFC 4646 language code in Language\r
1654 //\r
1655 if (!Iso639Language) {\r
1656 for (LanguageLength = 0; Language[LanguageLength] != 0 && Language[LanguageLength] != ';'; LanguageLength++);\r
1657 }\r
1658\r
1659 //\r
1660 // Trim back the length of Language used until it is empty\r
1661 //\r
1662 while (LanguageLength > 0) {\r
1663 //\r
1664 // Loop through all language codes in SupportedLanguages\r
1665 //\r
1666 for (Supported = SupportedLanguages; *Supported != '\0'; Supported += CompareLength) {\r
1667 //\r
1668 // In RFC 4646 mode, then Loop through all language codes in SupportedLanguages\r
1669 //\r
1670 if (!Iso639Language) {\r
1671 //\r
1672 // Skip ';' characters in Supported\r
1673 //\r
1674 for (; *Supported != '\0' && *Supported == ';'; Supported++);\r
1675 //\r
1676 // Determine the length of the next language code in Supported\r
1677 //\r
1678 for (CompareLength = 0; Supported[CompareLength] != 0 && Supported[CompareLength] != ';'; CompareLength++);\r
1679 //\r
1680 // If Language is longer than the Supported, then skip to the next language\r
1681 //\r
1682 if (LanguageLength > CompareLength) {\r
1683 continue;\r
1684 }\r
1685 }\r
1686 //\r
1687 // See if the first LanguageLength characters in Supported match Language\r
1688 //\r
1689 if (AsciiStrnCmp (Supported, Language, LanguageLength) == 0) {\r
1690 VA_END (Args);\r
1691\r
1692 Buffer = Iso639Language ? mVariableModuleGlobal->Lang : mVariableModuleGlobal->PlatformLang;\r
1693 Buffer[CompareLength] = '\0';\r
1694 return CopyMem (Buffer, Supported, CompareLength);\r
1695 }\r
1696 }\r
1697\r
1698 if (Iso639Language) {\r
1699 //\r
1700 // If ISO 639 mode, then each language can only be tested once\r
1701 //\r
1702 LanguageLength = 0;\r
1703 } else {\r
1704 //\r
1705 // If RFC 4646 mode, then trim Language from the right to the next '-' character\r
1706 //\r
1707 for (LanguageLength--; LanguageLength > 0 && Language[LanguageLength] != '-'; LanguageLength--);\r
1708 }\r
1709 }\r
1710 }\r
1711 VA_END (Args);\r
1712\r
1713 //\r
1714 // No matches were found\r
1715 //\r
1716 return NULL;\r
1717}\r
1718\r
1719/**\r
1720 This function is to check if the remaining variable space is enough to set\r
1721 all Variables from argument list successfully. The purpose of the check\r
1722 is to keep the consistency of the Variables to be in variable storage.\r
1723\r
1724 Note: Variables are assumed to be in same storage.\r
1725 The set sequence of Variables will be same with the sequence of VariableEntry from argument list,\r
1726 so follow the argument sequence to check the Variables.\r
1727\r
1728 @param[in] Attributes Variable attributes for Variable entries.\r
1729 @param ... The variable argument list with type VARIABLE_ENTRY_CONSISTENCY *.\r
1730 A NULL terminates the list. The VariableSize of\r
1731 VARIABLE_ENTRY_CONSISTENCY is the variable data size as input.\r
1732 It will be changed to variable total size as output.\r
1733\r
1734 @retval TRUE Have enough variable space to set the Variables successfully.\r
1735 @retval FALSE No enough variable space to set the Variables successfully.\r
1736\r
1737**/\r
1738BOOLEAN\r
1739EFIAPI\r
1740CheckRemainingSpaceForConsistency (\r
1741 IN UINT32 Attributes,\r
1742 ...\r
1743 )\r
1744{\r
1745 EFI_STATUS Status;\r
1746 VA_LIST Args;\r
1747 VARIABLE_ENTRY_CONSISTENCY *VariableEntry;\r
1748 UINT64 MaximumVariableStorageSize;\r
1749 UINT64 RemainingVariableStorageSize;\r
1750 UINT64 MaximumVariableSize;\r
1751 UINTN TotalNeededSize;\r
1752 UINTN OriginalVarSize;\r
1753 VARIABLE_STORE_HEADER *VariableStoreHeader;\r
1754 VARIABLE_POINTER_TRACK VariablePtrTrack;\r
1755 VARIABLE_HEADER *NextVariable;\r
1756 UINTN VarNameSize;\r
1757 UINTN VarDataSize;\r
1758\r
1759 //\r
1760 // Non-Volatile related.\r
1761 //\r
1762 VariableStoreHeader = mNvVariableCache;\r
1763\r
1764 Status = VariableServiceQueryVariableInfoInternal (\r
1765 Attributes,\r
1766 &MaximumVariableStorageSize,\r
1767 &RemainingVariableStorageSize,\r
1768 &MaximumVariableSize\r
1769 );\r
1770 ASSERT_EFI_ERROR (Status);\r
1771\r
1772 TotalNeededSize = 0;\r
1773 VA_START (Args, Attributes);\r
1774 VariableEntry = VA_ARG (Args, VARIABLE_ENTRY_CONSISTENCY *);\r
1775 while (VariableEntry != NULL) {\r
1776 //\r
1777 // Calculate variable total size.\r
1778 //\r
1779 VarNameSize = StrSize (VariableEntry->Name);\r
1780 VarNameSize += GET_PAD_SIZE (VarNameSize);\r
1781 VarDataSize = VariableEntry->VariableSize;\r
1782 VarDataSize += GET_PAD_SIZE (VarDataSize);\r
1783 VariableEntry->VariableSize = HEADER_ALIGN (sizeof (VARIABLE_HEADER) + VarNameSize + VarDataSize);\r
1784\r
1785 TotalNeededSize += VariableEntry->VariableSize;\r
1786 VariableEntry = VA_ARG (Args, VARIABLE_ENTRY_CONSISTENCY *);\r
1787 }\r
1788 VA_END (Args);\r
1789\r
1790 if (RemainingVariableStorageSize >= TotalNeededSize) {\r
1791 //\r
1792 // Already have enough space.\r
1793 //\r
1794 return TRUE;\r
1795 } else if (AtRuntime ()) {\r
1796 //\r
1797 // At runtime, no reclaim.\r
1798 // The original variable space of Variables can't be reused.\r
1799 //\r
1800 return FALSE;\r
1801 }\r
1802\r
1803 VA_START (Args, Attributes);\r
1804 VariableEntry = VA_ARG (Args, VARIABLE_ENTRY_CONSISTENCY *);\r
1805 while (VariableEntry != NULL) {\r
1806 //\r
1807 // Check if Variable[Index] has been present and get its size.\r
1808 //\r
1809 OriginalVarSize = 0;\r
1810 VariablePtrTrack.StartPtr = GetStartPointer (VariableStoreHeader);\r
1811 VariablePtrTrack.EndPtr = GetEndPointer (VariableStoreHeader);\r
1812 Status = FindVariableEx (\r
1813 VariableEntry->Name,\r
1814 VariableEntry->Guid,\r
1815 FALSE,\r
1816 &VariablePtrTrack\r
1817 );\r
1818 if (!EFI_ERROR (Status)) {\r
1819 //\r
1820 // Get size of Variable[Index].\r
1821 //\r
1822 NextVariable = GetNextVariablePtr (VariablePtrTrack.CurrPtr);\r
1823 OriginalVarSize = (UINTN) NextVariable - (UINTN) VariablePtrTrack.CurrPtr;\r
1824 //\r
1825 // Add the original size of Variable[Index] to remaining variable storage size.\r
1826 //\r
1827 RemainingVariableStorageSize += OriginalVarSize;\r
1828 }\r
1829 if (VariableEntry->VariableSize > RemainingVariableStorageSize) {\r
1830 //\r
1831 // No enough space for Variable[Index].\r
1832 //\r
1833 VA_END (Args);\r
1834 return FALSE;\r
1835 }\r
1836 //\r
1837 // Sub the (new) size of Variable[Index] from remaining variable storage size.\r
1838 //\r
1839 RemainingVariableStorageSize -= VariableEntry->VariableSize;\r
1840 VariableEntry = VA_ARG (Args, VARIABLE_ENTRY_CONSISTENCY *);\r
1841 }\r
1842 VA_END (Args);\r
1843\r
1844 return TRUE;\r
1845}\r
1846\r
1847/**\r
1848 Hook the operations in PlatformLangCodes, LangCodes, PlatformLang and Lang.\r
1849\r
1850 When setting Lang/LangCodes, simultaneously update PlatformLang/PlatformLangCodes.\r
1851\r
1852 According to UEFI spec, PlatformLangCodes/LangCodes are only set once in firmware initialization,\r
1853 and are read-only. Therefore, in variable driver, only store the original value for other use.\r
1854\r
1855 @param[in] VariableName Name of variable.\r
1856\r
1857 @param[in] Data Variable data.\r
1858\r
1859 @param[in] DataSize Size of data. 0 means delete.\r
1860\r
1861 @retval EFI_SUCCESS The update operation is successful or ignored.\r
1862 @retval EFI_WRITE_PROTECTED Update PlatformLangCodes/LangCodes at runtime.\r
1863 @retval EFI_OUT_OF_RESOURCES No enough variable space to do the update operation.\r
1864 @retval Others Other errors happened during the update operation.\r
1865\r
1866**/\r
1867EFI_STATUS\r
1868AutoUpdateLangVariable (\r
1869 IN CHAR16 *VariableName,\r
1870 IN VOID *Data,\r
1871 IN UINTN DataSize\r
1872 )\r
1873{\r
1874 EFI_STATUS Status;\r
1875 CHAR8 *BestPlatformLang;\r
1876 CHAR8 *BestLang;\r
1877 UINTN Index;\r
1878 UINT32 Attributes;\r
1879 VARIABLE_POINTER_TRACK Variable;\r
1880 BOOLEAN SetLanguageCodes;\r
1881 VARIABLE_ENTRY_CONSISTENCY VariableEntry[2];\r
1882\r
1883 //\r
1884 // Don't do updates for delete operation\r
1885 //\r
1886 if (DataSize == 0) {\r
1887 return EFI_SUCCESS;\r
1888 }\r
1889\r
1890 SetLanguageCodes = FALSE;\r
1891\r
1892 if (StrCmp (VariableName, EFI_PLATFORM_LANG_CODES_VARIABLE_NAME) == 0) {\r
1893 //\r
1894 // PlatformLangCodes is a volatile variable, so it can not be updated at runtime.\r
1895 //\r
1896 if (AtRuntime ()) {\r
1897 return EFI_WRITE_PROTECTED;\r
1898 }\r
1899\r
1900 SetLanguageCodes = TRUE;\r
1901\r
1902 //\r
1903 // According to UEFI spec, PlatformLangCodes is only set once in firmware initialization, and is read-only\r
1904 // Therefore, in variable driver, only store the original value for other use.\r
1905 //\r
1906 if (mVariableModuleGlobal->PlatformLangCodes != NULL) {\r
1907 FreePool (mVariableModuleGlobal->PlatformLangCodes);\r
1908 }\r
1909 mVariableModuleGlobal->PlatformLangCodes = AllocateRuntimeCopyPool (DataSize, Data);\r
1910 ASSERT (mVariableModuleGlobal->PlatformLangCodes != NULL);\r
1911\r
1912 //\r
1913 // PlatformLang holds a single language from PlatformLangCodes,\r
1914 // so the size of PlatformLangCodes is enough for the PlatformLang.\r
1915 //\r
1916 if (mVariableModuleGlobal->PlatformLang != NULL) {\r
1917 FreePool (mVariableModuleGlobal->PlatformLang);\r
1918 }\r
1919 mVariableModuleGlobal->PlatformLang = AllocateRuntimePool (DataSize);\r
1920 ASSERT (mVariableModuleGlobal->PlatformLang != NULL);\r
1921\r
1922 } else if (StrCmp (VariableName, EFI_LANG_CODES_VARIABLE_NAME) == 0) {\r
1923 //\r
1924 // LangCodes is a volatile variable, so it can not be updated at runtime.\r
1925 //\r
1926 if (AtRuntime ()) {\r
1927 return EFI_WRITE_PROTECTED;\r
1928 }\r
1929\r
1930 SetLanguageCodes = TRUE;\r
1931\r
1932 //\r
1933 // According to UEFI spec, LangCodes is only set once in firmware initialization, and is read-only\r
1934 // Therefore, in variable driver, only store the original value for other use.\r
1935 //\r
1936 if (mVariableModuleGlobal->LangCodes != NULL) {\r
1937 FreePool (mVariableModuleGlobal->LangCodes);\r
1938 }\r
1939 mVariableModuleGlobal->LangCodes = AllocateRuntimeCopyPool (DataSize, Data);\r
1940 ASSERT (mVariableModuleGlobal->LangCodes != NULL);\r
1941 }\r
1942\r
1943 if (SetLanguageCodes\r
1944 && (mVariableModuleGlobal->PlatformLangCodes != NULL)\r
1945 && (mVariableModuleGlobal->LangCodes != NULL)) {\r
1946 //\r
1947 // Update Lang if PlatformLang is already set\r
1948 // Update PlatformLang if Lang is already set\r
1949 //\r
1950 Status = FindVariable (EFI_PLATFORM_LANG_VARIABLE_NAME, &gEfiGlobalVariableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE);\r
1951 if (!EFI_ERROR (Status)) {\r
1952 //\r
1953 // Update Lang\r
1954 //\r
1955 VariableName = EFI_PLATFORM_LANG_VARIABLE_NAME;\r
1956 Data = GetVariableDataPtr (Variable.CurrPtr);\r
1957 DataSize = Variable.CurrPtr->DataSize;\r
1958 } else {\r
1959 Status = FindVariable (EFI_LANG_VARIABLE_NAME, &gEfiGlobalVariableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE);\r
1960 if (!EFI_ERROR (Status)) {\r
1961 //\r
1962 // Update PlatformLang\r
1963 //\r
1964 VariableName = EFI_LANG_VARIABLE_NAME;\r
1965 Data = GetVariableDataPtr (Variable.CurrPtr);\r
1966 DataSize = Variable.CurrPtr->DataSize;\r
1967 } else {\r
1968 //\r
1969 // Neither PlatformLang nor Lang is set, directly return\r
1970 //\r
1971 return EFI_SUCCESS;\r
1972 }\r
1973 }\r
1974 }\r
1975\r
1976 Status = EFI_SUCCESS;\r
1977\r
1978 //\r
1979 // According to UEFI spec, "Lang" and "PlatformLang" is NV|BS|RT attributions.\r
1980 //\r
1981 Attributes = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;\r
1982\r
1983 if (StrCmp (VariableName, EFI_PLATFORM_LANG_VARIABLE_NAME) == 0) {\r
1984 //\r
1985 // Update Lang when PlatformLangCodes/LangCodes were set.\r
1986 //\r
1987 if ((mVariableModuleGlobal->PlatformLangCodes != NULL) && (mVariableModuleGlobal->LangCodes != NULL)) {\r
1988 //\r
1989 // When setting PlatformLang, firstly get most matched language string from supported language codes.\r
1990 //\r
1991 BestPlatformLang = VariableGetBestLanguage (mVariableModuleGlobal->PlatformLangCodes, FALSE, Data, NULL);\r
1992 if (BestPlatformLang != NULL) {\r
1993 //\r
1994 // Get the corresponding index in language codes.\r
1995 //\r
1996 Index = GetIndexFromSupportedLangCodes (mVariableModuleGlobal->PlatformLangCodes, BestPlatformLang, FALSE);\r
1997\r
1998 //\r
1999 // Get the corresponding ISO639 language tag according to RFC4646 language tag.\r
2000 //\r
2001 BestLang = GetLangFromSupportedLangCodes (mVariableModuleGlobal->LangCodes, Index, TRUE);\r
2002\r
2003 //\r
2004 // Check the variable space for both Lang and PlatformLang variable.\r
2005 //\r
2006 VariableEntry[0].VariableSize = ISO_639_2_ENTRY_SIZE + 1;\r
2007 VariableEntry[0].Guid = &gEfiGlobalVariableGuid;\r
2008 VariableEntry[0].Name = EFI_LANG_VARIABLE_NAME;\r
2009\r
2010 VariableEntry[1].VariableSize = AsciiStrSize (BestPlatformLang);\r
2011 VariableEntry[1].Guid = &gEfiGlobalVariableGuid;\r
2012 VariableEntry[1].Name = EFI_PLATFORM_LANG_VARIABLE_NAME;\r
2013 if (!CheckRemainingSpaceForConsistency (VARIABLE_ATTRIBUTE_NV_BS_RT, &VariableEntry[0], &VariableEntry[1], NULL)) {\r
2014 //\r
2015 // No enough variable space to set both Lang and PlatformLang successfully.\r
2016 //\r
2017 Status = EFI_OUT_OF_RESOURCES;\r
2018 } else {\r
2019 //\r
2020 // Successfully convert PlatformLang to Lang, and set the BestLang value into Lang variable simultaneously.\r
2021 //\r
2022 FindVariable (EFI_LANG_VARIABLE_NAME, &gEfiGlobalVariableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE);\r
2023\r
2024 Status = UpdateVariable (EFI_LANG_VARIABLE_NAME, &gEfiGlobalVariableGuid, BestLang,\r
2025 ISO_639_2_ENTRY_SIZE + 1, Attributes, 0, 0, &Variable, NULL);\r
2026 }\r
2027\r
2028 DEBUG ((EFI_D_INFO, "Variable Driver Auto Update PlatformLang, PlatformLang:%a, Lang:%a Status: %r\n", BestPlatformLang, BestLang, Status));\r
2029 }\r
2030 }\r
2031\r
2032 } else if (StrCmp (VariableName, EFI_LANG_VARIABLE_NAME) == 0) {\r
2033 //\r
2034 // Update PlatformLang when PlatformLangCodes/LangCodes were set.\r
2035 //\r
2036 if ((mVariableModuleGlobal->PlatformLangCodes != NULL) && (mVariableModuleGlobal->LangCodes != NULL)) {\r
2037 //\r
2038 // When setting Lang, firstly get most matched language string from supported language codes.\r
2039 //\r
2040 BestLang = VariableGetBestLanguage (mVariableModuleGlobal->LangCodes, TRUE, Data, NULL);\r
2041 if (BestLang != NULL) {\r
2042 //\r
2043 // Get the corresponding index in language codes.\r
2044 //\r
2045 Index = GetIndexFromSupportedLangCodes (mVariableModuleGlobal->LangCodes, BestLang, TRUE);\r
2046\r
2047 //\r
2048 // Get the corresponding RFC4646 language tag according to ISO639 language tag.\r
2049 //\r
2050 BestPlatformLang = GetLangFromSupportedLangCodes (mVariableModuleGlobal->PlatformLangCodes, Index, FALSE);\r
2051\r
2052 //\r
2053 // Check the variable space for both PlatformLang and Lang variable.\r
2054 //\r
2055 VariableEntry[0].VariableSize = AsciiStrSize (BestPlatformLang);\r
2056 VariableEntry[0].Guid = &gEfiGlobalVariableGuid;\r
2057 VariableEntry[0].Name = EFI_PLATFORM_LANG_VARIABLE_NAME;\r
2058\r
2059 VariableEntry[1].VariableSize = ISO_639_2_ENTRY_SIZE + 1;\r
2060 VariableEntry[1].Guid = &gEfiGlobalVariableGuid;\r
2061 VariableEntry[1].Name = EFI_LANG_VARIABLE_NAME;\r
2062 if (!CheckRemainingSpaceForConsistency (VARIABLE_ATTRIBUTE_NV_BS_RT, &VariableEntry[0], &VariableEntry[1], NULL)) {\r
2063 //\r
2064 // No enough variable space to set both PlatformLang and Lang successfully.\r
2065 //\r
2066 Status = EFI_OUT_OF_RESOURCES;\r
2067 } else {\r
2068 //\r
2069 // Successfully convert Lang to PlatformLang, and set the BestPlatformLang value into PlatformLang variable simultaneously.\r
2070 //\r
2071 FindVariable (EFI_PLATFORM_LANG_VARIABLE_NAME, &gEfiGlobalVariableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE);\r
2072\r
2073 Status = UpdateVariable (EFI_PLATFORM_LANG_VARIABLE_NAME, &gEfiGlobalVariableGuid, BestPlatformLang,\r
2074 AsciiStrSize (BestPlatformLang), Attributes, 0, 0, &Variable, NULL);\r
2075 }\r
2076\r
2077 DEBUG ((EFI_D_INFO, "Variable Driver Auto Update Lang, Lang:%a, PlatformLang:%a Status: %r\n", BestLang, BestPlatformLang, Status));\r
2078 }\r
2079 }\r
2080 }\r
2081\r
2082 if (SetLanguageCodes) {\r
2083 //\r
2084 // Continue to set PlatformLangCodes or LangCodes.\r
2085 //\r
2086 return EFI_SUCCESS;\r
2087 } else {\r
2088 return Status;\r
2089 }\r
2090}\r
2091\r
2092/**\r
2093 Update the variable region with Variable information. If EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS is set,\r
2094 index of associated public key is needed.\r
2095\r
2096 @param[in] VariableName Name of variable.\r
2097 @param[in] VendorGuid Guid of variable.\r
2098 @param[in] Data Variable data.\r
2099 @param[in] DataSize Size of data. 0 means delete.\r
2100 @param[in] Attributes Attributes of the variable.\r
2101 @param[in] KeyIndex Index of associated public key.\r
2102 @param[in] MonotonicCount Value of associated monotonic count.\r
2103 @param[in, out] CacheVariable The variable information which is used to keep track of variable usage.\r
2104 @param[in] TimeStamp Value of associated TimeStamp.\r
2105\r
2106 @retval EFI_SUCCESS The update operation is success.\r
2107 @retval EFI_OUT_OF_RESOURCES Variable region is full, can not write other data into this region.\r
2108\r
2109**/\r
2110EFI_STATUS\r
2111UpdateVariable (\r
2112 IN CHAR16 *VariableName,\r
2113 IN EFI_GUID *VendorGuid,\r
2114 IN VOID *Data,\r
2115 IN UINTN DataSize,\r
2116 IN UINT32 Attributes OPTIONAL,\r
2117 IN UINT32 KeyIndex OPTIONAL,\r
2118 IN UINT64 MonotonicCount OPTIONAL,\r
2119 IN OUT VARIABLE_POINTER_TRACK *CacheVariable,\r
2120 IN EFI_TIME *TimeStamp OPTIONAL\r
2121 )\r
2122{\r
2123 EFI_STATUS Status;\r
2124 VARIABLE_HEADER *NextVariable;\r
2125 UINTN ScratchSize;\r
2126 UINTN MaxDataSize;\r
2127 UINTN VarNameOffset;\r
2128 UINTN VarDataOffset;\r
2129 UINTN VarNameSize;\r
2130 UINTN VarSize;\r
2131 BOOLEAN Volatile;\r
2132 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;\r
2133 UINT8 State;\r
2134 VARIABLE_POINTER_TRACK *Variable;\r
2135 VARIABLE_POINTER_TRACK NvVariable;\r
2136 VARIABLE_STORE_HEADER *VariableStoreHeader;\r
2137 UINTN CacheOffset;\r
2138 UINT8 *BufferForMerge;\r
2139 UINTN MergedBufSize;\r
2140 BOOLEAN DataReady;\r
2141 UINTN DataOffset;\r
2142 BOOLEAN IsCommonVariable;\r
2143 BOOLEAN IsCommonUserVariable;\r
2144\r
2145 if (mVariableModuleGlobal->FvbInstance == NULL) {\r
2146 //\r
2147 // The FVB protocol is not installed, so the EFI_VARIABLE_WRITE_ARCH_PROTOCOL is not installed.\r
2148 //\r
2149 if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) {\r
2150 //\r
2151 // Trying to update NV variable prior to the installation of EFI_VARIABLE_WRITE_ARCH_PROTOCOL\r
2152 //\r
2153 return EFI_NOT_AVAILABLE_YET;\r
2154 } else if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) {\r
2155 //\r
2156 // Trying to update volatile authenticated variable prior to the installation of EFI_VARIABLE_WRITE_ARCH_PROTOCOL\r
2157 // The authenticated variable perhaps is not initialized, just return here.\r
2158 //\r
2159 return EFI_NOT_AVAILABLE_YET;\r
2160 }\r
2161 }\r
2162\r
2163 if ((CacheVariable->CurrPtr == NULL) || CacheVariable->Volatile) {\r
2164 Variable = CacheVariable;\r
2165 } else {\r
2166 //\r
2167 // Update/Delete existing NV variable.\r
2168 // CacheVariable points to the variable in the memory copy of Flash area\r
2169 // Now let Variable points to the same variable in Flash area.\r
2170 //\r
2171 VariableStoreHeader = (VARIABLE_STORE_HEADER *) ((UINTN) mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase);\r
2172 Variable = &NvVariable;\r
2173 Variable->StartPtr = GetStartPointer (VariableStoreHeader);\r
2174 Variable->EndPtr = GetEndPointer (VariableStoreHeader);\r
2175 Variable->CurrPtr = (VARIABLE_HEADER *)((UINTN)Variable->StartPtr + ((UINTN)CacheVariable->CurrPtr - (UINTN)CacheVariable->StartPtr));\r
2176 if (CacheVariable->InDeletedTransitionPtr != NULL) {\r
2177 Variable->InDeletedTransitionPtr = (VARIABLE_HEADER *)((UINTN)Variable->StartPtr + ((UINTN)CacheVariable->InDeletedTransitionPtr - (UINTN)CacheVariable->StartPtr));\r
2178 } else {\r
2179 Variable->InDeletedTransitionPtr = NULL;\r
2180 }\r
2181 Variable->Volatile = FALSE;\r
2182 }\r
2183\r
2184 Fvb = mVariableModuleGlobal->FvbInstance;\r
2185\r
2186 //\r
2187 // Tricky part: Use scratch data area at the end of volatile variable store\r
2188 // as a temporary storage.\r
2189 //\r
2190 NextVariable = GetEndPointer ((VARIABLE_STORE_HEADER *) ((UINTN) mVariableModuleGlobal->VariableGlobal.VolatileVariableBase));\r
2191 ScratchSize = MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32 (PcdMaxHardwareErrorVariableSize));\r
2192 SetMem (NextVariable, ScratchSize, 0xff);\r
2193 DataReady = FALSE;\r
2194\r
2195 if (Variable->CurrPtr != NULL) {\r
2196 //\r
2197 // Update/Delete existing variable.\r
2198 //\r
2199 if (AtRuntime ()) {\r
2200 //\r
2201 // If AtRuntime and the variable is Volatile and Runtime Access,\r
2202 // the volatile is ReadOnly, and SetVariable should be aborted and\r
2203 // return EFI_WRITE_PROTECTED.\r
2204 //\r
2205 if (Variable->Volatile) {\r
2206 Status = EFI_WRITE_PROTECTED;\r
2207 goto Done;\r
2208 }\r
2209 //\r
2210 // Only variable that have NV attributes can be updated/deleted in Runtime.\r
2211 //\r
2212 if ((Variable->CurrPtr->Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) {\r
2213 Status = EFI_INVALID_PARAMETER;\r
2214 goto Done;\r
2215 }\r
2216\r
2217 //\r
2218 // Only variable that have RT attributes can be updated/deleted in Runtime.\r
2219 //\r
2220 if ((Variable->CurrPtr->Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0) {\r
2221 Status = EFI_INVALID_PARAMETER;\r
2222 goto Done;\r
2223 }\r
2224 }\r
2225\r
2226 //\r
2227 // Setting a data variable with no access, or zero DataSize attributes\r
2228 // causes it to be deleted.\r
2229 // When the EFI_VARIABLE_APPEND_WRITE attribute is set, DataSize of zero will\r
2230 // not delete the variable.\r
2231 //\r
2232 if ((((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) && (DataSize == 0))|| ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0)) {\r
2233 if (Variable->InDeletedTransitionPtr != NULL) {\r
2234 //\r
2235 // Both ADDED and IN_DELETED_TRANSITION variable are present,\r
2236 // set IN_DELETED_TRANSITION one to DELETED state first.\r
2237 //\r
2238 State = Variable->InDeletedTransitionPtr->State;\r
2239 State &= VAR_DELETED;\r
2240 Status = UpdateVariableStore (\r
2241 &mVariableModuleGlobal->VariableGlobal,\r
2242 Variable->Volatile,\r
2243 FALSE,\r
2244 Fvb,\r
2245 (UINTN) &Variable->InDeletedTransitionPtr->State,\r
2246 sizeof (UINT8),\r
2247 &State\r
2248 );\r
2249 if (!EFI_ERROR (Status)) {\r
2250 if (!Variable->Volatile) {\r
2251 ASSERT (CacheVariable->InDeletedTransitionPtr != NULL);\r
2252 CacheVariable->InDeletedTransitionPtr->State = State;\r
2253 }\r
2254 } else {\r
2255 goto Done;\r
2256 }\r
2257 }\r
2258\r
2259 State = Variable->CurrPtr->State;\r
2260 State &= VAR_DELETED;\r
2261\r
2262 Status = UpdateVariableStore (\r
2263 &mVariableModuleGlobal->VariableGlobal,\r
2264 Variable->Volatile,\r
2265 FALSE,\r
2266 Fvb,\r
2267 (UINTN) &Variable->CurrPtr->State,\r
2268 sizeof (UINT8),\r
2269 &State\r
2270 );\r
2271 if (!EFI_ERROR (Status)) {\r
2272 UpdateVariableInfo (VariableName, VendorGuid, Variable->Volatile, FALSE, FALSE, TRUE, FALSE);\r
2273 if (!Variable->Volatile) {\r
2274 CacheVariable->CurrPtr->State = State;\r
2275 FlushHobVariableToFlash (VariableName, VendorGuid);\r
2276 }\r
2277 }\r
2278 goto Done;\r
2279 }\r
2280 //\r
2281 // If the variable is marked valid, and the same data has been passed in,\r
2282 // then return to the caller immediately.\r
2283 //\r
2284 if (DataSizeOfVariable (Variable->CurrPtr) == DataSize &&\r
2285 (CompareMem (Data, GetVariableDataPtr (Variable->CurrPtr), DataSize) == 0) &&\r
2286 ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) &&\r
2287 (TimeStamp == NULL)) {\r
2288 //\r
2289 // Variable content unchanged and no need to update timestamp, just return.\r
2290 //\r
2291 UpdateVariableInfo (VariableName, VendorGuid, Variable->Volatile, FALSE, TRUE, FALSE, FALSE);\r
2292 Status = EFI_SUCCESS;\r
2293 goto Done;\r
2294 } else if ((Variable->CurrPtr->State == VAR_ADDED) ||\r
2295 (Variable->CurrPtr->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION))) {\r
2296\r
2297 //\r
2298 // EFI_VARIABLE_APPEND_WRITE attribute only effects for existing variable\r
2299 //\r
2300 if ((Attributes & EFI_VARIABLE_APPEND_WRITE) != 0) {\r
2301 //\r
2302 // NOTE: From 0 to DataOffset of NextVariable is reserved for Variable Header and Name.\r
2303 // From DataOffset of NextVariable is to save the existing variable data.\r
2304 //\r
2305 DataOffset = sizeof (VARIABLE_HEADER) + Variable->CurrPtr->NameSize + GET_PAD_SIZE (Variable->CurrPtr->NameSize);\r
2306 BufferForMerge = (UINT8 *) ((UINTN) NextVariable + DataOffset);\r
2307 CopyMem (BufferForMerge, (UINT8 *) ((UINTN) Variable->CurrPtr + DataOffset), Variable->CurrPtr->DataSize);\r
2308\r
2309 //\r
2310 // Set Max Common Variable Data Size as default MaxDataSize\r
2311 //\r
2312 MaxDataSize = PcdGet32 (PcdMaxVariableSize) - DataOffset;\r
2313\r
2314 if ((CompareGuid (VendorGuid, &gEfiImageSecurityDatabaseGuid) &&\r
2315 ((StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE) == 0) || (StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE1) == 0) ||\r
2316 (StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE2) == 0))) ||\r
2317 (CompareGuid (VendorGuid, &gEfiGlobalVariableGuid) && (StrCmp (VariableName, EFI_KEY_EXCHANGE_KEY_NAME) == 0))) {\r
2318 //\r
2319 // For variables with formatted as EFI_SIGNATURE_LIST, the driver shall not perform an append of\r
2320 // EFI_SIGNATURE_DATA values that are already part of the existing variable value.\r
2321 //\r
2322 Status = AppendSignatureList (\r
2323 BufferForMerge,\r
2324 Variable->CurrPtr->DataSize,\r
2325 MaxDataSize - Variable->CurrPtr->DataSize,\r
2326 Data,\r
2327 DataSize,\r
2328 &MergedBufSize\r
2329 );\r
2330 if (Status == EFI_BUFFER_TOO_SMALL) {\r
2331 //\r
2332 // Signature List is too long, Failed to Append.\r
2333 //\r
2334 Status = EFI_INVALID_PARAMETER;\r
2335 goto Done;\r
2336 }\r
2337\r
2338 if (MergedBufSize == Variable->CurrPtr->DataSize) {\r
2339 if ((TimeStamp == NULL) || CompareTimeStamp (TimeStamp, &Variable->CurrPtr->TimeStamp)) {\r
2340 //\r
2341 // New EFI_SIGNATURE_DATA is not found and timestamp is not later\r
2342 // than current timestamp, return EFI_SUCCESS directly.\r
2343 //\r
2344 UpdateVariableInfo (VariableName, VendorGuid, Variable->Volatile, FALSE, TRUE, FALSE, FALSE);\r
2345 Status = EFI_SUCCESS;\r
2346 goto Done;\r
2347 }\r
2348 }\r
2349 } else {\r
2350 //\r
2351 // For other Variables, append the new data to the end of existing data.\r
2352 // Max Harware error record variable data size is different from common variable\r
2353 //\r
2354 if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) {\r
2355 MaxDataSize = PcdGet32 (PcdMaxHardwareErrorVariableSize) - DataOffset;\r
2356 }\r
2357\r
2358 if (Variable->CurrPtr->DataSize + DataSize > MaxDataSize) {\r
2359 //\r
2360 // Existing data size + new data size exceed maximum variable size limitation.\r
2361 //\r
2362 Status = EFI_INVALID_PARAMETER;\r
2363 goto Done;\r
2364 }\r
2365 CopyMem ((UINT8*) ((UINTN) BufferForMerge + Variable->CurrPtr->DataSize), Data, DataSize);\r
2366 MergedBufSize = Variable->CurrPtr->DataSize + DataSize;\r
2367 }\r
2368\r
2369 //\r
2370 // BufferForMerge(from DataOffset of NextVariable) has included the merged existing and new data.\r
2371 //\r
2372 Data = BufferForMerge;\r
2373 DataSize = MergedBufSize;\r
2374 DataReady = TRUE;\r
2375 }\r
2376\r
2377 //\r
2378 // Mark the old variable as in delete transition.\r
2379 //\r
2380 State = Variable->CurrPtr->State;\r
2381 State &= VAR_IN_DELETED_TRANSITION;\r
2382\r
2383 Status = UpdateVariableStore (\r
2384 &mVariableModuleGlobal->VariableGlobal,\r
2385 Variable->Volatile,\r
2386 FALSE,\r
2387 Fvb,\r
2388 (UINTN) &Variable->CurrPtr->State,\r
2389 sizeof (UINT8),\r
2390 &State\r
2391 );\r
2392 if (EFI_ERROR (Status)) {\r
2393 goto Done;\r
2394 }\r
2395 if (!Variable->Volatile) {\r
2396 CacheVariable->CurrPtr->State = State;\r
2397 }\r
2398 }\r
2399 } else {\r
2400 //\r
2401 // Not found existing variable. Create a new variable.\r
2402 //\r
2403\r
2404 if ((DataSize == 0) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) != 0)) {\r
2405 Status = EFI_SUCCESS;\r
2406 goto Done;\r
2407 }\r
2408\r
2409 //\r
2410 // Make sure we are trying to create a new variable.\r
2411 // Setting a data variable with zero DataSize or no access attributes means to delete it.\r
2412 //\r
2413 if (DataSize == 0 || (Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0) {\r
2414 Status = EFI_NOT_FOUND;\r
2415 goto Done;\r
2416 }\r
2417\r
2418 //\r
2419 // Only variable have NV|RT attribute can be created in Runtime.\r
2420 //\r
2421 if (AtRuntime () &&\r
2422 (((Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0) || ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0))) {\r
2423 Status = EFI_INVALID_PARAMETER;\r
2424 goto Done;\r
2425 }\r
2426 }\r
2427\r
2428 //\r
2429 // Function part - create a new variable and copy the data.\r
2430 // Both update a variable and create a variable will come here.\r
2431 //\r
2432 NextVariable->StartId = VARIABLE_DATA;\r
2433 //\r
2434 // NextVariable->State = VAR_ADDED;\r
2435 //\r
2436 NextVariable->Reserved = 0;\r
2437 NextVariable->PubKeyIndex = KeyIndex;\r
2438 NextVariable->MonotonicCount = MonotonicCount;\r
2439 ZeroMem (&NextVariable->TimeStamp, sizeof (EFI_TIME));\r
2440\r
2441 if (((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) &&\r
2442 (TimeStamp != NULL)) {\r
2443 if ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) {\r
2444 CopyMem (&NextVariable->TimeStamp, TimeStamp, sizeof (EFI_TIME));\r
2445 } else {\r
2446 //\r
2447 // In the case when the EFI_VARIABLE_APPEND_WRITE attribute is set, only\r
2448 // when the new TimeStamp value is later than the current timestamp associated\r
2449 // with the variable, we need associate the new timestamp with the updated value.\r
2450 //\r
2451 if (Variable->CurrPtr != NULL) {\r
2452 if (CompareTimeStamp (&Variable->CurrPtr->TimeStamp, TimeStamp)) {\r
2453 CopyMem (&NextVariable->TimeStamp, TimeStamp, sizeof (EFI_TIME));\r
2454 }\r
2455 }\r
2456 }\r
2457 }\r
2458\r
2459 //\r
2460 // The EFI_VARIABLE_APPEND_WRITE attribute will never be set in the returned\r
2461 // Attributes bitmask parameter of a GetVariable() call.\r
2462 //\r
2463 NextVariable->Attributes = Attributes & (~EFI_VARIABLE_APPEND_WRITE);\r
2464\r
2465 VarNameOffset = sizeof (VARIABLE_HEADER);\r
2466 VarNameSize = StrSize (VariableName);\r
2467 CopyMem (\r
2468 (UINT8 *) ((UINTN) NextVariable + VarNameOffset),\r
2469 VariableName,\r
2470 VarNameSize\r
2471 );\r
2472 VarDataOffset = VarNameOffset + VarNameSize + GET_PAD_SIZE (VarNameSize);\r
2473\r
2474 //\r
2475 // If DataReady is TRUE, it means the variable data has been saved into\r
2476 // NextVariable during EFI_VARIABLE_APPEND_WRITE operation preparation.\r
2477 //\r
2478 if (!DataReady) {\r
2479 CopyMem (\r
2480 (UINT8 *) ((UINTN) NextVariable + VarDataOffset),\r
2481 Data,\r
2482 DataSize\r
2483 );\r
2484 }\r
2485\r
2486 CopyMem (&NextVariable->VendorGuid, VendorGuid, sizeof (EFI_GUID));\r
2487 //\r
2488 // There will be pad bytes after Data, the NextVariable->NameSize and\r
2489 // NextVariable->DataSize should not include pad size so that variable\r
2490 // service can get actual size in GetVariable.\r
2491 //\r
2492 NextVariable->NameSize = (UINT32)VarNameSize;\r
2493 NextVariable->DataSize = (UINT32)DataSize;\r
2494\r
2495 //\r
2496 // The actual size of the variable that stores in storage should\r
2497 // include pad size.\r
2498 //\r
2499 VarSize = VarDataOffset + DataSize + GET_PAD_SIZE (DataSize);\r
2500 if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) {\r
2501 //\r
2502 // Create a nonvolatile variable.\r
2503 //\r
2504 Volatile = FALSE;\r
2505\r
2506 IsCommonVariable = FALSE;\r
2507 IsCommonUserVariable = FALSE;\r
2508 if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == 0) {\r
2509 IsCommonVariable = TRUE;\r
2510 IsCommonUserVariable = IsUserVariable (NextVariable);\r
2511 }\r
2512 if ((((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0)\r
2513 && ((VarSize + mVariableModuleGlobal->HwErrVariableTotalSize) > PcdGet32 (PcdHwErrStorageSize)))\r
2514 || (IsCommonVariable && ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > mVariableModuleGlobal->CommonVariableSpace))\r
2515 || (IsCommonVariable && AtRuntime () && ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > mVariableModuleGlobal->CommonRuntimeVariableSpace))\r
2516 || (IsCommonUserVariable && ((VarSize + mVariableModuleGlobal->CommonUserVariableTotalSize) > mVariableModuleGlobal->CommonMaxUserVariableSpace))) {\r
2517 if (AtRuntime ()) {\r
2518 if (IsCommonUserVariable && ((VarSize + mVariableModuleGlobal->CommonUserVariableTotalSize) > mVariableModuleGlobal->CommonMaxUserVariableSpace)) {\r
2519 RecordVarErrorFlag (VAR_ERROR_FLAG_USER_ERROR, VariableName, VendorGuid, Attributes, VarSize);\r
2520 }\r
2521 if (IsCommonVariable && ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > mVariableModuleGlobal->CommonRuntimeVariableSpace)) {\r
2522 RecordVarErrorFlag (VAR_ERROR_FLAG_SYSTEM_ERROR, VariableName, VendorGuid, Attributes, VarSize);\r
2523 }\r
2524 Status = EFI_OUT_OF_RESOURCES;\r
2525 goto Done;\r
2526 }\r
2527 //\r
2528 // Perform garbage collection & reclaim operation, and integrate the new variable at the same time.\r
2529 //\r
2530 Status = Reclaim (\r
2531 mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase,\r
2532 &mVariableModuleGlobal->NonVolatileLastVariableOffset,\r
2533 FALSE,\r
2534 Variable,\r
2535 NextVariable,\r
2536 HEADER_ALIGN (VarSize),\r
2537 FALSE\r
2538 );\r
2539 if (!EFI_ERROR (Status)) {\r
2540 //\r
2541 // The new variable has been integrated successfully during reclaiming.\r
2542 //\r
2543 if (Variable->CurrPtr != NULL) {\r
2544 CacheVariable->CurrPtr = (VARIABLE_HEADER *)((UINTN) CacheVariable->StartPtr + ((UINTN) Variable->CurrPtr - (UINTN) Variable->StartPtr));\r
2545 CacheVariable->InDeletedTransitionPtr = NULL;\r
2546 }\r
2547 UpdateVariableInfo (VariableName, VendorGuid, FALSE, FALSE, TRUE, FALSE, FALSE);\r
2548 FlushHobVariableToFlash (VariableName, VendorGuid);\r
2549 } else {\r
2550 if (IsCommonUserVariable && ((VarSize + mVariableModuleGlobal->CommonUserVariableTotalSize) > mVariableModuleGlobal->CommonMaxUserVariableSpace)) {\r
2551 RecordVarErrorFlag (VAR_ERROR_FLAG_USER_ERROR, VariableName, VendorGuid, Attributes, VarSize);\r
2552 }\r
2553 if (IsCommonVariable && ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > mVariableModuleGlobal->CommonVariableSpace)) {\r
2554 RecordVarErrorFlag (VAR_ERROR_FLAG_SYSTEM_ERROR, VariableName, VendorGuid, Attributes, VarSize);\r
2555 }\r
2556 }\r
2557 goto Done;\r
2558 }\r
2559 //\r
2560 // Four steps\r
2561 // 1. Write variable header\r
2562 // 2. Set variable state to header valid\r
2563 // 3. Write variable data\r
2564 // 4. Set variable state to valid\r
2565 //\r
2566 //\r
2567 // Step 1:\r
2568 //\r
2569 CacheOffset = mVariableModuleGlobal->NonVolatileLastVariableOffset;\r
2570 Status = UpdateVariableStore (\r
2571 &mVariableModuleGlobal->VariableGlobal,\r
2572 FALSE,\r
2573 TRUE,\r
2574 Fvb,\r
2575 mVariableModuleGlobal->NonVolatileLastVariableOffset,\r
2576 sizeof (VARIABLE_HEADER),\r
2577 (UINT8 *) NextVariable\r
2578 );\r
2579\r
2580 if (EFI_ERROR (Status)) {\r
2581 goto Done;\r
2582 }\r
2583\r
2584 //\r
2585 // Step 2:\r
2586 //\r
2587 NextVariable->State = VAR_HEADER_VALID_ONLY;\r
2588 Status = UpdateVariableStore (\r
2589 &mVariableModuleGlobal->VariableGlobal,\r
2590 FALSE,\r
2591 TRUE,\r
2592 Fvb,\r
2593 mVariableModuleGlobal->NonVolatileLastVariableOffset + OFFSET_OF (VARIABLE_HEADER, State),\r
2594 sizeof (UINT8),\r
2595 &NextVariable->State\r
2596 );\r
2597\r
2598 if (EFI_ERROR (Status)) {\r
2599 goto Done;\r
2600 }\r
2601 //\r
2602 // Step 3:\r
2603 //\r
2604 Status = UpdateVariableStore (\r
2605 &mVariableModuleGlobal->VariableGlobal,\r
2606 FALSE,\r
2607 TRUE,\r
2608 Fvb,\r
2609 mVariableModuleGlobal->NonVolatileLastVariableOffset + sizeof (VARIABLE_HEADER),\r
2610 (UINT32) VarSize - sizeof (VARIABLE_HEADER),\r
2611 (UINT8 *) NextVariable + sizeof (VARIABLE_HEADER)\r
2612 );\r
2613\r
2614 if (EFI_ERROR (Status)) {\r
2615 goto Done;\r
2616 }\r
2617 //\r
2618 // Step 4:\r
2619 //\r
2620 NextVariable->State = VAR_ADDED;\r
2621 Status = UpdateVariableStore (\r
2622 &mVariableModuleGlobal->VariableGlobal,\r
2623 FALSE,\r
2624 TRUE,\r
2625 Fvb,\r
2626 mVariableModuleGlobal->NonVolatileLastVariableOffset + OFFSET_OF (VARIABLE_HEADER, State),\r
2627 sizeof (UINT8),\r
2628 &NextVariable->State\r
2629 );\r
2630\r
2631 if (EFI_ERROR (Status)) {\r
2632 goto Done;\r
2633 }\r
2634\r
2635 mVariableModuleGlobal->NonVolatileLastVariableOffset += HEADER_ALIGN (VarSize);\r
2636\r
2637 if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0) {\r
2638 mVariableModuleGlobal->HwErrVariableTotalSize += HEADER_ALIGN (VarSize);\r
2639 } else {\r
2640 mVariableModuleGlobal->CommonVariableTotalSize += HEADER_ALIGN (VarSize);\r
2641 if (IsCommonUserVariable) {\r
2642 mVariableModuleGlobal->CommonUserVariableTotalSize += HEADER_ALIGN (VarSize);\r
2643 }\r
2644 }\r
2645 //\r
2646 // update the memory copy of Flash region.\r
2647 //\r
2648 CopyMem ((UINT8 *)mNvVariableCache + CacheOffset, (UINT8 *)NextVariable, VarSize);\r
2649 } else {\r
2650 //\r
2651 // Create a volatile variable.\r
2652 //\r
2653 Volatile = TRUE;\r
2654\r
2655 if ((UINT32) (VarSize + mVariableModuleGlobal->VolatileLastVariableOffset) >\r
2656 ((VARIABLE_STORE_HEADER *) ((UINTN) (mVariableModuleGlobal->VariableGlobal.VolatileVariableBase)))->Size) {\r
2657 //\r
2658 // Perform garbage collection & reclaim operation, and integrate the new variable at the same time.\r
2659 //\r
2660 Status = Reclaim (\r
2661 mVariableModuleGlobal->VariableGlobal.VolatileVariableBase,\r
2662 &mVariableModuleGlobal->VolatileLastVariableOffset,\r
2663 TRUE,\r
2664 Variable,\r
2665 NextVariable,\r
2666 HEADER_ALIGN (VarSize),\r
2667 FALSE\r
2668 );\r
2669 if (!EFI_ERROR (Status)) {\r
2670 //\r
2671 // The new variable has been integrated successfully during reclaiming.\r
2672 //\r
2673 if (Variable->CurrPtr != NULL) {\r
2674 CacheVariable->CurrPtr = (VARIABLE_HEADER *)((UINTN) CacheVariable->StartPtr + ((UINTN) Variable->CurrPtr - (UINTN) Variable->StartPtr));\r
2675 CacheVariable->InDeletedTransitionPtr = NULL;\r
2676 }\r
2677 UpdateVariableInfo (VariableName, VendorGuid, TRUE, FALSE, TRUE, FALSE, FALSE);\r
2678 }\r
2679 goto Done;\r
2680 }\r
2681\r
2682 NextVariable->State = VAR_ADDED;\r
2683 Status = UpdateVariableStore (\r
2684 &mVariableModuleGlobal->VariableGlobal,\r
2685 TRUE,\r
2686 TRUE,\r
2687 Fvb,\r
2688 mVariableModuleGlobal->VolatileLastVariableOffset,\r
2689 (UINT32) VarSize,\r
2690 (UINT8 *) NextVariable\r
2691 );\r
2692\r
2693 if (EFI_ERROR (Status)) {\r
2694 goto Done;\r
2695 }\r
2696\r
2697 mVariableModuleGlobal->VolatileLastVariableOffset += HEADER_ALIGN (VarSize);\r
2698 }\r
2699\r
2700 //\r
2701 // Mark the old variable as deleted.\r
2702 //\r
2703 if (!EFI_ERROR (Status) && Variable->CurrPtr != NULL) {\r
2704 if (Variable->InDeletedTransitionPtr != NULL) {\r
2705 //\r
2706 // Both ADDED and IN_DELETED_TRANSITION old variable are present,\r
2707 // set IN_DELETED_TRANSITION one to DELETED state first.\r
2708 //\r
2709 State = Variable->InDeletedTransitionPtr->State;\r
2710 State &= VAR_DELETED;\r
2711 Status = UpdateVariableStore (\r
2712 &mVariableModuleGlobal->VariableGlobal,\r
2713 Variable->Volatile,\r
2714 FALSE,\r
2715 Fvb,\r
2716 (UINTN) &Variable->InDeletedTransitionPtr->State,\r
2717 sizeof (UINT8),\r
2718 &State\r
2719 );\r
2720 if (!EFI_ERROR (Status)) {\r
2721 if (!Variable->Volatile) {\r
2722 ASSERT (CacheVariable->InDeletedTransitionPtr != NULL);\r
2723 CacheVariable->InDeletedTransitionPtr->State = State;\r
2724 }\r
2725 } else {\r
2726 goto Done;\r
2727 }\r
2728 }\r
2729\r
2730 State = Variable->CurrPtr->State;\r
2731 State &= VAR_DELETED;\r
2732\r
2733 Status = UpdateVariableStore (\r
2734 &mVariableModuleGlobal->VariableGlobal,\r
2735 Variable->Volatile,\r
2736 FALSE,\r
2737 Fvb,\r
2738 (UINTN) &Variable->CurrPtr->State,\r
2739 sizeof (UINT8),\r
2740 &State\r
2741 );\r
2742 if (!EFI_ERROR (Status) && !Variable->Volatile) {\r
2743 CacheVariable->CurrPtr->State = State;\r
2744 }\r
2745 }\r
2746\r
2747 if (!EFI_ERROR (Status)) {\r
2748 UpdateVariableInfo (VariableName, VendorGuid, Volatile, FALSE, TRUE, FALSE, FALSE);\r
2749 if (!Volatile) {\r
2750 FlushHobVariableToFlash (VariableName, VendorGuid);\r
2751 }\r
2752 }\r
2753\r
2754Done:\r
2755 return Status;\r
2756}\r
2757\r
2758/**\r
2759 Check if a Unicode character is a hexadecimal character.\r
2760\r
2761 This function checks if a Unicode character is a\r
2762 hexadecimal character. The valid hexadecimal character is\r
2763 L'0' to L'9', L'a' to L'f', or L'A' to L'F'.\r
2764\r
2765\r
2766 @param Char The character to check against.\r
2767\r
2768 @retval TRUE If the Char is a hexadecmial character.\r
2769 @retval FALSE If the Char is not a hexadecmial character.\r
2770\r
2771**/\r
2772BOOLEAN\r
2773EFIAPI\r
2774IsHexaDecimalDigitCharacter (\r
2775 IN CHAR16 Char\r
2776 )\r
2777{\r
2778 return (BOOLEAN) ((Char >= L'0' && Char <= L'9') || (Char >= L'A' && Char <= L'F') || (Char >= L'a' && Char <= L'f'));\r
2779}\r
2780\r
2781/**\r
2782\r
2783 This code checks if variable is hardware error record variable or not.\r
2784\r
2785 According to UEFI spec, hardware error record variable should use the EFI_HARDWARE_ERROR_VARIABLE VendorGuid\r
2786 and have the L"HwErrRec####" name convention, #### is a printed hex value and no 0x or h is included in the hex value.\r
2787\r
2788 @param VariableName Pointer to variable name.\r
2789 @param VendorGuid Variable Vendor Guid.\r
2790\r
2791 @retval TRUE Variable is hardware error record variable.\r
2792 @retval FALSE Variable is not hardware error record variable.\r
2793\r
2794**/\r
2795BOOLEAN\r
2796EFIAPI\r
2797IsHwErrRecVariable (\r
2798 IN CHAR16 *VariableName,\r
2799 IN EFI_GUID *VendorGuid\r
2800 )\r
2801{\r
2802 if (!CompareGuid (VendorGuid, &gEfiHardwareErrorVariableGuid) ||\r
2803 (StrLen (VariableName) != StrLen (L"HwErrRec####")) ||\r
2804 (StrnCmp(VariableName, L"HwErrRec", StrLen (L"HwErrRec")) != 0) ||\r
2805 !IsHexaDecimalDigitCharacter (VariableName[0x8]) ||\r
2806 !IsHexaDecimalDigitCharacter (VariableName[0x9]) ||\r
2807 !IsHexaDecimalDigitCharacter (VariableName[0xA]) ||\r
2808 !IsHexaDecimalDigitCharacter (VariableName[0xB])) {\r
2809 return FALSE;\r
2810 }\r
2811\r
2812 return TRUE;\r
2813}\r
2814\r
2815/**\r
2816 Mark a variable that will become read-only after leaving the DXE phase of execution.\r
2817\r
2818 @param[in] This The VARIABLE_LOCK_PROTOCOL instance.\r
2819 @param[in] VariableName A pointer to the variable name that will be made read-only subsequently.\r
2820 @param[in] VendorGuid A pointer to the vendor GUID that will be made read-only subsequently.\r
2821\r
2822 @retval EFI_SUCCESS The variable specified by the VariableName and the VendorGuid was marked\r
2823 as pending to be read-only.\r
2824 @retval EFI_INVALID_PARAMETER VariableName or VendorGuid is NULL.\r
2825 Or VariableName is an empty string.\r
2826 @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has\r
2827 already been signaled.\r
2828 @retval EFI_OUT_OF_RESOURCES There is not enough resource to hold the lock request.\r
2829**/\r
2830EFI_STATUS\r
2831EFIAPI\r
2832VariableLockRequestToLock (\r
2833 IN CONST EDKII_VARIABLE_LOCK_PROTOCOL *This,\r
2834 IN CHAR16 *VariableName,\r
2835 IN EFI_GUID *VendorGuid\r
2836 )\r
2837{\r
2838 VARIABLE_ENTRY *Entry;\r
2839 CHAR16 *Name;\r
2840\r
2841 if (VariableName == NULL || VariableName[0] == 0 || VendorGuid == NULL) {\r
2842 return EFI_INVALID_PARAMETER;\r
2843 }\r
2844\r
2845 if (mEndOfDxe) {\r
2846 return EFI_ACCESS_DENIED;\r
2847 }\r
2848\r
2849 Entry = AllocateRuntimeZeroPool (sizeof (*Entry) + StrSize (VariableName));\r
2850 if (Entry == NULL) {\r
2851 return EFI_OUT_OF_RESOURCES;\r
2852 }\r
2853\r
2854 DEBUG ((EFI_D_INFO, "[Variable] Lock: %g:%s\n", VendorGuid, VariableName));\r
2855\r
2856 AcquireLockOnlyAtBootTime(&mVariableModuleGlobal->VariableGlobal.VariableServicesLock);\r
2857\r
2858 Name = (CHAR16 *) ((UINTN) Entry + sizeof (*Entry));\r
2859 StrnCpy (Name, VariableName, StrLen (VariableName));\r
2860 CopyGuid (&Entry->Guid, VendorGuid);\r
2861 InsertTailList (&mLockedVariableList, &Entry->Link);\r
2862\r
2863 ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock);\r
2864\r
2865 return EFI_SUCCESS;\r
2866}\r
2867\r
2868/**\r
2869\r
2870 This code finds variable in storage blocks (Volatile or Non-Volatile).\r
2871\r
2872 Caution: This function may receive untrusted input.\r
2873 This function may be invoked in SMM mode, and datasize is external input.\r
2874 This function will do basic validation, before parse the data.\r
2875\r
2876 @param VariableName Name of Variable to be found.\r
2877 @param VendorGuid Variable vendor GUID.\r
2878 @param Attributes Attribute value of the variable found.\r
2879 @param DataSize Size of Data found. If size is less than the\r
2880 data, this value contains the required size.\r
2881 @param Data Data pointer.\r
2882\r
2883 @return EFI_INVALID_PARAMETER Invalid parameter.\r
2884 @return EFI_SUCCESS Find the specified variable.\r
2885 @return EFI_NOT_FOUND Not found.\r
2886 @return EFI_BUFFER_TO_SMALL DataSize is too small for the result.\r
2887\r
2888**/\r
2889EFI_STATUS\r
2890EFIAPI\r
2891VariableServiceGetVariable (\r
2892 IN CHAR16 *VariableName,\r
2893 IN EFI_GUID *VendorGuid,\r
2894 OUT UINT32 *Attributes OPTIONAL,\r
2895 IN OUT UINTN *DataSize,\r
2896 OUT VOID *Data\r
2897 )\r
2898{\r
2899 EFI_STATUS Status;\r
2900 VARIABLE_POINTER_TRACK Variable;\r
2901 UINTN VarDataSize;\r
2902\r
2903 if (VariableName == NULL || VendorGuid == NULL || DataSize == NULL) {\r
2904 return EFI_INVALID_PARAMETER;\r
2905 }\r
2906\r
2907 AcquireLockOnlyAtBootTime(&mVariableModuleGlobal->VariableGlobal.VariableServicesLock);\r
2908\r
2909 Status = FindVariable (VariableName, VendorGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE);\r
2910 if (Variable.CurrPtr == NULL || EFI_ERROR (Status)) {\r
2911 goto Done;\r
2912 }\r
2913\r
2914 //\r
2915 // Get data size\r
2916 //\r
2917 VarDataSize = DataSizeOfVariable (Variable.CurrPtr);\r
2918 ASSERT (VarDataSize != 0);\r
2919\r
2920 if (*DataSize >= VarDataSize) {\r
2921 if (Data == NULL) {\r
2922 Status = EFI_INVALID_PARAMETER;\r
2923 goto Done;\r
2924 }\r
2925\r
2926 CopyMem (Data, GetVariableDataPtr (Variable.CurrPtr), VarDataSize);\r
2927 if (Attributes != NULL) {\r
2928 *Attributes = Variable.CurrPtr->Attributes;\r
2929 }\r
2930\r
2931 *DataSize = VarDataSize;\r
2932 UpdateVariableInfo (VariableName, VendorGuid, Variable.Volatile, TRUE, FALSE, FALSE, FALSE);\r
2933\r
2934 Status = EFI_SUCCESS;\r
2935 goto Done;\r
2936 } else {\r
2937 *DataSize = VarDataSize;\r
2938 Status = EFI_BUFFER_TOO_SMALL;\r
2939 goto Done;\r
2940 }\r
2941\r
2942Done:\r
2943 ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock);\r
2944 return Status;\r
2945}\r
2946\r
2947\r
2948\r
2949/**\r
2950\r
2951 This code Finds the Next available variable.\r
2952\r
2953 Caution: This function may receive untrusted input.\r
2954 This function may be invoked in SMM mode. This function will do basic validation, before parse the data.\r
2955\r
2956 @param VariableNameSize Size of the variable name.\r
2957 @param VariableName Pointer to variable name.\r
2958 @param VendorGuid Variable Vendor Guid.\r
2959\r
2960 @return EFI_INVALID_PARAMETER Invalid parameter.\r
2961 @return EFI_SUCCESS Find the specified variable.\r
2962 @return EFI_NOT_FOUND Not found.\r
2963 @return EFI_BUFFER_TO_SMALL DataSize is too small for the result.\r
2964\r
2965**/\r
2966EFI_STATUS\r
2967EFIAPI\r
2968VariableServiceGetNextVariableName (\r
2969 IN OUT UINTN *VariableNameSize,\r
2970 IN OUT CHAR16 *VariableName,\r
2971 IN OUT EFI_GUID *VendorGuid\r
2972 )\r
2973{\r
2974 VARIABLE_STORE_TYPE Type;\r
2975 VARIABLE_POINTER_TRACK Variable;\r
2976 VARIABLE_POINTER_TRACK VariableInHob;\r
2977 VARIABLE_POINTER_TRACK VariablePtrTrack;\r
2978 UINTN VarNameSize;\r
2979 EFI_STATUS Status;\r
2980 VARIABLE_STORE_HEADER *VariableStoreHeader[VariableStoreTypeMax];\r
2981\r
2982 if (VariableNameSize == NULL || VariableName == NULL || VendorGuid == NULL) {\r
2983 return EFI_INVALID_PARAMETER;\r
2984 }\r
2985\r
2986 AcquireLockOnlyAtBootTime(&mVariableModuleGlobal->VariableGlobal.VariableServicesLock);\r
2987\r
2988 Status = FindVariable (VariableName, VendorGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE);\r
2989 if (Variable.CurrPtr == NULL || EFI_ERROR (Status)) {\r
2990 goto Done;\r
2991 }\r
2992\r
2993 if (VariableName[0] != 0) {\r
2994 //\r
2995 // If variable name is not NULL, get next variable.\r
2996 //\r
2997 Variable.CurrPtr = GetNextVariablePtr (Variable.CurrPtr);\r
2998 }\r
2999\r
3000 //\r
3001 // 0: Volatile, 1: HOB, 2: Non-Volatile.\r
3002 // The index and attributes mapping must be kept in this order as FindVariable\r
3003 // makes use of this mapping to implement search algorithm.\r
3004 //\r
3005 VariableStoreHeader[VariableStoreTypeVolatile] = (VARIABLE_STORE_HEADER *) (UINTN) mVariableModuleGlobal->VariableGlobal.VolatileVariableBase;\r
3006 VariableStoreHeader[VariableStoreTypeHob] = (VARIABLE_STORE_HEADER *) (UINTN) mVariableModuleGlobal->VariableGlobal.HobVariableBase;\r
3007 VariableStoreHeader[VariableStoreTypeNv] = mNvVariableCache;\r
3008\r
3009 while (TRUE) {\r
3010 //\r
3011 // Switch from Volatile to HOB, to Non-Volatile.\r
3012 //\r
3013 while (!IsValidVariableHeader (Variable.CurrPtr, Variable.EndPtr)) {\r
3014 //\r
3015 // Find current storage index\r
3016 //\r
3017 for (Type = (VARIABLE_STORE_TYPE) 0; Type < VariableStoreTypeMax; Type++) {\r
3018 if ((VariableStoreHeader[Type] != NULL) && (Variable.StartPtr == GetStartPointer (VariableStoreHeader[Type]))) {\r
3019 break;\r
3020 }\r
3021 }\r
3022 ASSERT (Type < VariableStoreTypeMax);\r
3023 //\r
3024 // Switch to next storage\r
3025 //\r
3026 for (Type++; Type < VariableStoreTypeMax; Type++) {\r
3027 if (VariableStoreHeader[Type] != NULL) {\r
3028 break;\r
3029 }\r
3030 }\r
3031 //\r
3032 // Capture the case that\r
3033 // 1. current storage is the last one, or\r
3034 // 2. no further storage\r
3035 //\r
3036 if (Type == VariableStoreTypeMax) {\r
3037 Status = EFI_NOT_FOUND;\r
3038 goto Done;\r
3039 }\r
3040 Variable.StartPtr = GetStartPointer (VariableStoreHeader[Type]);\r
3041 Variable.EndPtr = GetEndPointer (VariableStoreHeader[Type]);\r
3042 Variable.CurrPtr = Variable.StartPtr;\r
3043 }\r
3044\r
3045 //\r
3046 // Variable is found\r
3047 //\r
3048 if (Variable.CurrPtr->State == VAR_ADDED || Variable.CurrPtr->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED)) {\r
3049 if (!AtRuntime () || ((Variable.CurrPtr->Attributes & EFI_VARIABLE_RUNTIME_ACCESS) != 0)) {\r
3050 if (Variable.CurrPtr->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED)) {\r
3051 //\r
3052 // If it is a IN_DELETED_TRANSITION variable,\r
3053 // and there is also a same ADDED one at the same time,\r
3054 // don't return it.\r
3055 //\r
3056 VariablePtrTrack.StartPtr = Variable.StartPtr;\r
3057 VariablePtrTrack.EndPtr = Variable.EndPtr;\r
3058 Status = FindVariableEx (\r
3059 GetVariableNamePtr (Variable.CurrPtr),\r
3060 &Variable.CurrPtr->VendorGuid,\r
3061 FALSE,\r
3062 &VariablePtrTrack\r
3063 );\r
3064 if (!EFI_ERROR (Status) && VariablePtrTrack.CurrPtr->State == VAR_ADDED) {\r
3065 Variable.CurrPtr = GetNextVariablePtr (Variable.CurrPtr);\r
3066 continue;\r
3067 }\r
3068 }\r
3069\r
3070 //\r
3071 // Don't return NV variable when HOB overrides it\r
3072 //\r
3073 if ((VariableStoreHeader[VariableStoreTypeHob] != NULL) && (VariableStoreHeader[VariableStoreTypeNv] != NULL) &&\r
3074 (Variable.StartPtr == GetStartPointer (VariableStoreHeader[VariableStoreTypeNv]))\r
3075 ) {\r
3076 VariableInHob.StartPtr = GetStartPointer (VariableStoreHeader[VariableStoreTypeHob]);\r
3077 VariableInHob.EndPtr = GetEndPointer (VariableStoreHeader[VariableStoreTypeHob]);\r
3078 Status = FindVariableEx (\r
3079 GetVariableNamePtr (Variable.CurrPtr),\r
3080 &Variable.CurrPtr->VendorGuid,\r
3081 FALSE,\r
3082 &VariableInHob\r
3083 );\r
3084 if (!EFI_ERROR (Status)) {\r
3085 Variable.CurrPtr = GetNextVariablePtr (Variable.CurrPtr);\r
3086 continue;\r
3087 }\r
3088 }\r
3089\r
3090 VarNameSize = NameSizeOfVariable (Variable.CurrPtr);\r
3091 ASSERT (VarNameSize != 0);\r
3092\r
3093 if (VarNameSize <= *VariableNameSize) {\r
3094 CopyMem (VariableName, GetVariableNamePtr (Variable.CurrPtr), VarNameSize);\r
3095 CopyMem (VendorGuid, &Variable.CurrPtr->VendorGuid, sizeof (EFI_GUID));\r
3096 Status = EFI_SUCCESS;\r
3097 } else {\r
3098 Status = EFI_BUFFER_TOO_SMALL;\r
3099 }\r
3100\r
3101 *VariableNameSize = VarNameSize;\r
3102 goto Done;\r
3103 }\r
3104 }\r
3105\r
3106 Variable.CurrPtr = GetNextVariablePtr (Variable.CurrPtr);\r
3107 }\r
3108\r
3109Done:\r
3110 ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock);\r
3111 return Status;\r
3112}\r
3113\r
3114/**\r
3115\r
3116 This code sets variable in storage blocks (Volatile or Non-Volatile).\r
3117\r
3118 Caution: This function may receive untrusted input.\r
3119 This function may be invoked in SMM mode, and datasize and data are external input.\r
3120 This function will do basic validation, before parse the data.\r
3121 This function will parse the authentication carefully to avoid security issues, like\r
3122 buffer overflow, integer overflow.\r
3123 This function will check attribute carefully to avoid authentication bypass.\r
3124\r
3125 @param VariableName Name of Variable to be found.\r
3126 @param VendorGuid Variable vendor GUID.\r
3127 @param Attributes Attribute value of the variable found\r
3128 @param DataSize Size of Data found. If size is less than the\r
3129 data, this value contains the required size.\r
3130 @param Data Data pointer.\r
3131\r
3132 @return EFI_INVALID_PARAMETER Invalid parameter.\r
3133 @return EFI_SUCCESS Set successfully.\r
3134 @return EFI_OUT_OF_RESOURCES Resource not enough to set variable.\r
3135 @return EFI_NOT_FOUND Not found.\r
3136 @return EFI_WRITE_PROTECTED Variable is read-only.\r
3137\r
3138**/\r
3139EFI_STATUS\r
3140EFIAPI\r
3141VariableServiceSetVariable (\r
3142 IN CHAR16 *VariableName,\r
3143 IN EFI_GUID *VendorGuid,\r
3144 IN UINT32 Attributes,\r
3145 IN UINTN DataSize,\r
3146 IN VOID *Data\r
3147 )\r
3148{\r
3149 VARIABLE_POINTER_TRACK Variable;\r
3150 EFI_STATUS Status;\r
3151 VARIABLE_HEADER *NextVariable;\r
3152 EFI_PHYSICAL_ADDRESS Point;\r
3153 UINTN PayloadSize;\r
3154 LIST_ENTRY *Link;\r
3155 VARIABLE_ENTRY *Entry;\r
3156 CHAR16 *Name;\r
3157\r
3158 //\r
3159 // Check input parameters.\r
3160 //\r
3161 if (VariableName == NULL || VariableName[0] == 0 || VendorGuid == NULL) {\r
3162 return EFI_INVALID_PARAMETER;\r
3163 }\r
3164\r
3165 if (DataSize != 0 && Data == NULL) {\r
3166 return EFI_INVALID_PARAMETER;\r
3167 }\r
3168\r
3169 //\r
3170 // Check for reserverd bit in variable attribute.\r
3171 //\r
3172 if ((Attributes & (~EFI_VARIABLE_ATTRIBUTES_MASK)) != 0) {\r
3173 return EFI_INVALID_PARAMETER;\r
3174 }\r
3175\r
3176 //\r
3177 // Make sure if runtime bit is set, boot service bit is set also.\r
3178 //\r
3179 if ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == EFI_VARIABLE_RUNTIME_ACCESS) {\r
3180 return EFI_INVALID_PARAMETER;\r
3181 }\r
3182\r
3183 //\r
3184 // EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS and EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute\r
3185 // cannot be set both.\r
3186 //\r
3187 if (((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS)\r
3188 && ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) {\r
3189 return EFI_INVALID_PARAMETER;\r
3190 }\r
3191\r
3192 if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) {\r
3193 if (DataSize < AUTHINFO_SIZE) {\r
3194 //\r
3195 // Try to write Authenticated Variable without AuthInfo.\r
3196 //\r
3197 return EFI_SECURITY_VIOLATION;\r
3198 }\r
3199 PayloadSize = DataSize - AUTHINFO_SIZE;\r
3200 } else if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) {\r
3201 //\r
3202 // Sanity check for EFI_VARIABLE_AUTHENTICATION_2 descriptor.\r
3203 //\r
3204 if (DataSize < OFFSET_OF_AUTHINFO2_CERT_DATA ||\r
3205 ((EFI_VARIABLE_AUTHENTICATION_2 *) Data)->AuthInfo.Hdr.dwLength > DataSize - (OFFSET_OF (EFI_VARIABLE_AUTHENTICATION_2, AuthInfo)) ||\r
3206 ((EFI_VARIABLE_AUTHENTICATION_2 *) Data)->AuthInfo.Hdr.dwLength < OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData)) {\r
3207 return EFI_SECURITY_VIOLATION;\r
3208 }\r
3209 PayloadSize = DataSize - AUTHINFO2_SIZE (Data);\r
3210 } else {\r
3211 PayloadSize = DataSize;\r
3212 }\r
3213\r
3214 if ((UINTN)(~0) - PayloadSize < StrSize(VariableName)){\r
3215 //\r
3216 // Prevent whole variable size overflow\r
3217 //\r
3218 return EFI_INVALID_PARAMETER;\r
3219 }\r
3220\r
3221 //\r
3222 // The size of the VariableName, including the Unicode Null in bytes plus\r
3223 // the DataSize is limited to maximum size of PcdGet32 (PcdMaxHardwareErrorVariableSize)\r
3224 // bytes for HwErrRec, and PcdGet32 (PcdMaxVariableSize) bytes for the others.\r
3225 //\r
3226 if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) {\r
3227 if (StrSize (VariableName) + PayloadSize > PcdGet32 (PcdMaxHardwareErrorVariableSize) - sizeof (VARIABLE_HEADER)) {\r
3228 return EFI_INVALID_PARAMETER;\r
3229 }\r
3230 if (!IsHwErrRecVariable(VariableName, VendorGuid)) {\r
3231 return EFI_INVALID_PARAMETER;\r
3232 }\r
3233 } else {\r
3234 //\r
3235 // The size of the VariableName, including the Unicode Null in bytes plus\r
3236 // the DataSize is limited to maximum size of PcdGet32 (PcdMaxVariableSize) bytes.\r
3237 //\r
3238 if (StrSize (VariableName) + PayloadSize > PcdGet32 (PcdMaxVariableSize) - sizeof (VARIABLE_HEADER)) {\r
3239 return EFI_INVALID_PARAMETER;\r
3240 }\r
3241 }\r
3242\r
3243 AcquireLockOnlyAtBootTime(&mVariableModuleGlobal->VariableGlobal.VariableServicesLock);\r
3244\r
3245 //\r
3246 // Consider reentrant in MCA/INIT/NMI. It needs be reupdated.\r
3247 //\r
3248 if (1 < InterlockedIncrement (&mVariableModuleGlobal->VariableGlobal.ReentrantState)) {\r
3249 Point = mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase;\r
3250 //\r
3251 // Parse non-volatile variable data and get last variable offset.\r
3252 //\r
3253 NextVariable = GetStartPointer ((VARIABLE_STORE_HEADER *) (UINTN) Point);\r
3254 while (IsValidVariableHeader (NextVariable, GetEndPointer ((VARIABLE_STORE_HEADER *) (UINTN) Point))) {\r
3255 NextVariable = GetNextVariablePtr (NextVariable);\r
3256 }\r
3257 mVariableModuleGlobal->NonVolatileLastVariableOffset = (UINTN) NextVariable - (UINTN) Point;\r
3258 }\r
3259\r
3260 if (mEndOfDxe && mEnableLocking) {\r
3261 //\r
3262 // Treat the variables listed in the forbidden variable list as read-only after leaving DXE phase.\r
3263 //\r
3264 for ( Link = GetFirstNode (&mLockedVariableList)\r
3265 ; !IsNull (&mLockedVariableList, Link)\r
3266 ; Link = GetNextNode (&mLockedVariableList, Link)\r
3267 ) {\r
3268 Entry = BASE_CR (Link, VARIABLE_ENTRY, Link);\r
3269 Name = (CHAR16 *) ((UINTN) Entry + sizeof (*Entry));\r
3270 if (CompareGuid (&Entry->Guid, VendorGuid) && (StrCmp (Name, VariableName) == 0)) {\r
3271 Status = EFI_WRITE_PROTECTED;\r
3272 DEBUG ((EFI_D_INFO, "[Variable]: Changing readonly variable after leaving DXE phase - %g:%s\n", VendorGuid, VariableName));\r
3273 goto Done;\r
3274 }\r
3275 }\r
3276 }\r
3277\r
3278 Status = InternalVarCheckSetVariableCheck (VariableName, VendorGuid, Attributes, PayloadSize, (VOID *) ((UINTN) Data + DataSize - PayloadSize));\r
3279 if (EFI_ERROR (Status)) {\r
3280 goto Done;\r
3281 }\r
3282\r
3283 //\r
3284 // Check whether the input variable is already existed.\r
3285 //\r
3286 Status = FindVariable (VariableName, VendorGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, TRUE);\r
3287 if (!EFI_ERROR (Status)) {\r
3288 if (((Variable.CurrPtr->Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0) && AtRuntime ()) {\r
3289 Status = EFI_WRITE_PROTECTED;\r
3290 goto Done;\r
3291 }\r
3292 if (Attributes != 0 && (Attributes & (~EFI_VARIABLE_APPEND_WRITE)) != Variable.CurrPtr->Attributes) {\r
3293 //\r
3294 // If a preexisting variable is rewritten with different attributes, SetVariable() shall not\r
3295 // modify the variable and shall return EFI_INVALID_PARAMETER. Two exceptions to this rule:\r
3296 // 1. No access attributes specified\r
3297 // 2. The only attribute differing is EFI_VARIABLE_APPEND_WRITE\r
3298 //\r
3299 Status = EFI_INVALID_PARAMETER;\r
3300 DEBUG ((EFI_D_INFO, "[Variable]: Rewritten a preexisting variable(0x%08x) with different attributes(0x%08x) - %g:%s\n", Variable.CurrPtr->Attributes, Attributes, VendorGuid, VariableName));\r
3301 goto Done;\r
3302 }\r
3303 }\r
3304\r
3305 if (!FeaturePcdGet (PcdUefiVariableDefaultLangDeprecate)) {\r
3306 //\r
3307 // Hook the operation of setting PlatformLangCodes/PlatformLang and LangCodes/Lang.\r
3308 //\r
3309 Status = AutoUpdateLangVariable (VariableName, Data, DataSize);\r
3310 if (EFI_ERROR (Status)) {\r
3311 //\r
3312 // The auto update operation failed, directly return to avoid inconsistency between PlatformLang and Lang.\r
3313 //\r
3314 goto Done;\r
3315 }\r
3316 }\r
3317\r
3318 //\r
3319 // Process PK, KEK, Sigdb seperately.\r
3320 //\r
3321 if (CompareGuid (VendorGuid, &gEfiGlobalVariableGuid) && (StrCmp (VariableName, EFI_PLATFORM_KEY_NAME) == 0)){\r
3322 Status = ProcessVarWithPk (VariableName, VendorGuid, Data, DataSize, &Variable, Attributes, TRUE);\r
3323 } else if (CompareGuid (VendorGuid, &gEfiGlobalVariableGuid) && (StrCmp (VariableName, EFI_KEY_EXCHANGE_KEY_NAME) == 0)) {\r
3324 Status = ProcessVarWithPk (VariableName, VendorGuid, Data, DataSize, &Variable, Attributes, FALSE);\r
3325 } else if (CompareGuid (VendorGuid, &gEfiImageSecurityDatabaseGuid) &&\r
3326 ((StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE) == 0) ||\r
3327 (StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE1) == 0) ||\r
3328 (StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE2) == 0)\r
3329 )\r
3330 ) {\r
3331 Status = ProcessVarWithPk (VariableName, VendorGuid, Data, DataSize, &Variable, Attributes, FALSE);\r
3332 if (EFI_ERROR (Status)) {\r
3333 Status = ProcessVarWithKek (VariableName, VendorGuid, Data, DataSize, &Variable, Attributes);\r
3334 }\r
3335 } else {\r
3336 Status = ProcessVariable (VariableName, VendorGuid, Data, DataSize, &Variable, Attributes);\r
3337 }\r
3338\r
3339Done:\r
3340 InterlockedDecrement (&mVariableModuleGlobal->VariableGlobal.ReentrantState);\r
3341 ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock);\r
3342\r
3343 if (!AtRuntime ()) {\r
3344 if (!EFI_ERROR (Status)) {\r
3345 SecureBootHook (\r
3346 VariableName,\r
3347 VendorGuid\r
3348 );\r
3349 }\r
3350 }\r
3351\r
3352 return Status;\r
3353}\r
3354\r
3355/**\r
3356\r
3357 This code returns information about the EFI variables.\r
3358\r
3359 Caution: This function may receive untrusted input.\r
3360 This function may be invoked in SMM mode. This function will do basic validation, before parse the data.\r
3361\r
3362 @param Attributes Attributes bitmask to specify the type of variables\r
3363 on which to return information.\r
3364 @param MaximumVariableStorageSize Pointer to the maximum size of the storage space available\r
3365 for the EFI variables associated with the attributes specified.\r
3366 @param RemainingVariableStorageSize Pointer to the remaining size of the storage space available\r
3367 for EFI variables associated with the attributes specified.\r
3368 @param MaximumVariableSize Pointer to the maximum size of an individual EFI variables\r
3369 associated with the attributes specified.\r
3370\r
3371 @return EFI_SUCCESS Query successfully.\r
3372\r
3373**/\r
3374EFI_STATUS\r
3375EFIAPI\r
3376VariableServiceQueryVariableInfoInternal (\r
3377 IN UINT32 Attributes,\r
3378 OUT UINT64 *MaximumVariableStorageSize,\r
3379 OUT UINT64 *RemainingVariableStorageSize,\r
3380 OUT UINT64 *MaximumVariableSize\r
3381 )\r
3382{\r
3383 VARIABLE_HEADER *Variable;\r
3384 VARIABLE_HEADER *NextVariable;\r
3385 UINT64 VariableSize;\r
3386 VARIABLE_STORE_HEADER *VariableStoreHeader;\r
3387 UINT64 CommonVariableTotalSize;\r
3388 UINT64 HwErrVariableTotalSize;\r
3389 EFI_STATUS Status;\r
3390 VARIABLE_POINTER_TRACK VariablePtrTrack;\r
3391\r
3392 CommonVariableTotalSize = 0;\r
3393 HwErrVariableTotalSize = 0;\r
3394\r
3395 if((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) {\r
3396 //\r
3397 // Query is Volatile related.\r
3398 //\r
3399 VariableStoreHeader = (VARIABLE_STORE_HEADER *) ((UINTN) mVariableModuleGlobal->VariableGlobal.VolatileVariableBase);\r
3400 } else {\r
3401 //\r
3402 // Query is Non-Volatile related.\r
3403 //\r
3404 VariableStoreHeader = mNvVariableCache;\r
3405 }\r
3406\r
3407 //\r
3408 // Now let's fill *MaximumVariableStorageSize *RemainingVariableStorageSize\r
3409 // with the storage size (excluding the storage header size).\r
3410 //\r
3411 *MaximumVariableStorageSize = VariableStoreHeader->Size - sizeof (VARIABLE_STORE_HEADER);\r
3412\r
3413 //\r
3414 // Harware error record variable needs larger size.\r
3415 //\r
3416 if ((Attributes & (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) == (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) {\r
3417 *MaximumVariableStorageSize = PcdGet32 (PcdHwErrStorageSize);\r
3418 *MaximumVariableSize = PcdGet32 (PcdMaxHardwareErrorVariableSize) - sizeof (VARIABLE_HEADER);\r
3419 } else {\r
3420 if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) {\r
3421 if (AtRuntime ()) {\r
3422 *MaximumVariableStorageSize = mVariableModuleGlobal->CommonRuntimeVariableSpace;\r
3423 } else {\r
3424 *MaximumVariableStorageSize = mVariableModuleGlobal->CommonVariableSpace;\r
3425 }\r
3426 }\r
3427\r
3428 //\r
3429 // Let *MaximumVariableSize be PcdGet32 (PcdMaxVariableSize) with the exception of the variable header size.\r
3430 //\r
3431 *MaximumVariableSize = PcdGet32 (PcdMaxVariableSize) - sizeof (VARIABLE_HEADER);\r
3432 }\r
3433\r
3434 //\r
3435 // Point to the starting address of the variables.\r
3436 //\r
3437 Variable = GetStartPointer (VariableStoreHeader);\r
3438\r
3439 //\r
3440 // Now walk through the related variable store.\r
3441 //\r
3442 while (IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader))) {\r
3443 NextVariable = GetNextVariablePtr (Variable);\r
3444 VariableSize = (UINT64) (UINTN) NextVariable - (UINT64) (UINTN) Variable;\r
3445\r
3446 if (AtRuntime ()) {\r
3447 //\r
3448 // We don't take the state of the variables in mind\r
3449 // when calculating RemainingVariableStorageSize,\r
3450 // since the space occupied by variables not marked with\r
3451 // VAR_ADDED is not allowed to be reclaimed in Runtime.\r
3452 //\r
3453 if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) {\r
3454 HwErrVariableTotalSize += VariableSize;\r
3455 } else {\r
3456 CommonVariableTotalSize += VariableSize;\r
3457 }\r
3458 } else {\r
3459 //\r
3460 // Only care about Variables with State VAR_ADDED, because\r
3461 // the space not marked as VAR_ADDED is reclaimable now.\r
3462 //\r
3463 if (Variable->State == VAR_ADDED) {\r
3464 if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) {\r
3465 HwErrVariableTotalSize += VariableSize;\r
3466 } else {\r
3467 CommonVariableTotalSize += VariableSize;\r
3468 }\r
3469 } else if (Variable->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED)) {\r
3470 //\r
3471 // If it is a IN_DELETED_TRANSITION variable,\r
3472 // and there is not also a same ADDED one at the same time,\r
3473 // this IN_DELETED_TRANSITION variable is valid.\r
3474 //\r
3475 VariablePtrTrack.StartPtr = GetStartPointer (VariableStoreHeader);\r
3476 VariablePtrTrack.EndPtr = GetEndPointer (VariableStoreHeader);\r
3477 Status = FindVariableEx (\r
3478 GetVariableNamePtr (Variable),\r
3479 &Variable->VendorGuid,\r
3480 FALSE,\r
3481 &VariablePtrTrack\r
3482 );\r
3483 if (!EFI_ERROR (Status) && VariablePtrTrack.CurrPtr->State != VAR_ADDED) {\r
3484 if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) {\r
3485 HwErrVariableTotalSize += VariableSize;\r
3486 } else {\r
3487 CommonVariableTotalSize += VariableSize;\r
3488 }\r
3489 }\r
3490 }\r
3491 }\r
3492\r
3493 //\r
3494 // Go to the next one.\r
3495 //\r
3496 Variable = NextVariable;\r
3497 }\r
3498\r
3499 if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD){\r
3500 *RemainingVariableStorageSize = *MaximumVariableStorageSize - HwErrVariableTotalSize;\r
3501 } else {\r
3502 if (*MaximumVariableStorageSize < CommonVariableTotalSize) {\r
3503 *RemainingVariableStorageSize = 0;\r
3504 } else {\r
3505 *RemainingVariableStorageSize = *MaximumVariableStorageSize - CommonVariableTotalSize;\r
3506 }\r
3507 }\r
3508\r
3509 if (*RemainingVariableStorageSize < sizeof (VARIABLE_HEADER)) {\r
3510 *MaximumVariableSize = 0;\r
3511 } else if ((*RemainingVariableStorageSize - sizeof (VARIABLE_HEADER)) < *MaximumVariableSize) {\r
3512 *MaximumVariableSize = *RemainingVariableStorageSize - sizeof (VARIABLE_HEADER);\r
3513 }\r
3514\r
3515 return EFI_SUCCESS;\r
3516}\r
3517\r
3518/**\r
3519\r
3520 This code returns information about the EFI variables.\r
3521\r
3522 Caution: This function may receive untrusted input.\r
3523 This function may be invoked in SMM mode. This function will do basic validation, before parse the data.\r
3524\r
3525 @param Attributes Attributes bitmask to specify the type of variables\r
3526 on which to return information.\r
3527 @param MaximumVariableStorageSize Pointer to the maximum size of the storage space available\r
3528 for the EFI variables associated with the attributes specified.\r
3529 @param RemainingVariableStorageSize Pointer to the remaining size of the storage space available\r
3530 for EFI variables associated with the attributes specified.\r
3531 @param MaximumVariableSize Pointer to the maximum size of an individual EFI variables\r
3532 associated with the attributes specified.\r
3533\r
3534 @return EFI_INVALID_PARAMETER An invalid combination of attribute bits was supplied.\r
3535 @return EFI_SUCCESS Query successfully.\r
3536 @return EFI_UNSUPPORTED The attribute is not supported on this platform.\r
3537\r
3538**/\r
3539EFI_STATUS\r
3540EFIAPI\r
3541VariableServiceQueryVariableInfo (\r
3542 IN UINT32 Attributes,\r
3543 OUT UINT64 *MaximumVariableStorageSize,\r
3544 OUT UINT64 *RemainingVariableStorageSize,\r
3545 OUT UINT64 *MaximumVariableSize\r
3546 )\r
3547{\r
3548 EFI_STATUS Status;\r
3549\r
3550 if(MaximumVariableStorageSize == NULL || RemainingVariableStorageSize == NULL || MaximumVariableSize == NULL || Attributes == 0) {\r
3551 return EFI_INVALID_PARAMETER;\r
3552 }\r
3553\r
3554 if ((Attributes & VARIABLE_ATTRIBUTE_NV_BS_RT_AT_HR_AW) == 0) {\r
3555 //\r
3556 // Make sure the Attributes combination is supported by the platform.\r
3557 //\r
3558 return EFI_UNSUPPORTED;\r
3559 } else if ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == EFI_VARIABLE_RUNTIME_ACCESS) {\r
3560 //\r
3561 // Make sure if runtime bit is set, boot service bit is set also.\r
3562 //\r
3563 return EFI_INVALID_PARAMETER;\r
3564 } else if (AtRuntime () && ((Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0)) {\r
3565 //\r
3566 // Make sure RT Attribute is set if we are in Runtime phase.\r
3567 //\r
3568 return EFI_INVALID_PARAMETER;\r
3569 } else if ((Attributes & (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) {\r
3570 //\r
3571 // Make sure Hw Attribute is set with NV.\r
3572 //\r
3573 return EFI_INVALID_PARAMETER;\r
3574 }\r
3575\r
3576 AcquireLockOnlyAtBootTime(&mVariableModuleGlobal->VariableGlobal.VariableServicesLock);\r
3577\r
3578 Status = VariableServiceQueryVariableInfoInternal (\r
3579 Attributes,\r
3580 MaximumVariableStorageSize,\r
3581 RemainingVariableStorageSize,\r
3582 MaximumVariableSize\r
3583 );\r
3584\r
3585 ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock);\r
3586 return Status;\r
3587}\r
3588\r
3589/**\r
3590 This function reclaims variable storage if free size is below the threshold.\r
3591\r
3592 Caution: This function may be invoked at SMM mode.\r
3593 Care must be taken to make sure not security issue.\r
3594\r
3595**/\r
3596VOID\r
3597ReclaimForOS(\r
3598 VOID\r
3599 )\r
3600{\r
3601 EFI_STATUS Status;\r
3602 UINTN RemainingCommonRuntimeVariableSpace;\r
3603 UINTN RemainingHwErrVariableSpace;\r
3604\r
3605 Status = EFI_SUCCESS;\r
3606\r
3607 if (mVariableModuleGlobal->CommonRuntimeVariableSpace < mVariableModuleGlobal->CommonVariableTotalSize) {\r
3608 RemainingCommonRuntimeVariableSpace = 0;\r
3609 } else {\r
3610 RemainingCommonRuntimeVariableSpace = mVariableModuleGlobal->CommonRuntimeVariableSpace - mVariableModuleGlobal->CommonVariableTotalSize;\r
3611 }\r
3612\r
3613 RemainingHwErrVariableSpace = PcdGet32 (PcdHwErrStorageSize) - mVariableModuleGlobal->HwErrVariableTotalSize;\r
3614 //\r
3615 // Check if the free area is below a threshold.\r
3616 //\r
3617 if ((RemainingCommonRuntimeVariableSpace < PcdGet32 (PcdMaxVariableSize))\r
3618 || ((PcdGet32 (PcdHwErrStorageSize) != 0) &&\r
3619 (RemainingHwErrVariableSpace < PcdGet32 (PcdMaxHardwareErrorVariableSize)))){\r
3620 Status = Reclaim (\r
3621 mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase,\r
3622 &mVariableModuleGlobal->NonVolatileLastVariableOffset,\r
3623 FALSE,\r
3624 NULL,\r
3625 NULL,\r
3626 0,\r
3627 FALSE\r
3628 );\r
3629 ASSERT_EFI_ERROR (Status);\r
3630 }\r
3631}\r
3632\r
3633/**\r
3634 Init non-volatile variable store.\r
3635\r
3636 @retval EFI_SUCCESS Function successfully executed.\r
3637 @retval EFI_OUT_OF_RESOURCES Fail to allocate enough memory resource.\r
3638 @retval EFI_VOLUME_CORRUPTED Variable Store or Firmware Volume for Variable Store is corrupted.\r
3639\r
3640**/\r
3641EFI_STATUS\r
3642InitNonVolatileVariableStore (\r
3643 VOID\r
3644 )\r
3645{\r
3646 EFI_FIRMWARE_VOLUME_HEADER *FvHeader;\r
3647 VARIABLE_HEADER *Variable;\r
3648 VARIABLE_HEADER *NextVariable;\r
3649 EFI_PHYSICAL_ADDRESS VariableStoreBase;\r
3650 UINT64 VariableStoreLength;\r
3651 UINTN VariableSize;\r
3652 EFI_HOB_GUID_TYPE *GuidHob;\r
3653 EFI_PHYSICAL_ADDRESS NvStorageBase;\r
3654 UINT8 *NvStorageData;\r
3655 UINT32 NvStorageSize;\r
3656 FAULT_TOLERANT_WRITE_LAST_WRITE_DATA *FtwLastWriteData;\r
3657 UINT32 BackUpOffset;\r
3658 UINT32 BackUpSize;\r
3659 UINT32 HwErrStorageSize;\r
3660 UINT32 MaxUserNvVariableSpaceSize;\r
3661 UINT32 BoottimeReservedNvVariableSpaceSize;\r
3662\r
3663 mVariableModuleGlobal->FvbInstance = NULL;\r
3664\r
3665 //\r
3666 // Allocate runtime memory used for a memory copy of the FLASH region.\r
3667 // Keep the memory and the FLASH in sync as updates occur.\r
3668 //\r
3669 NvStorageSize = PcdGet32 (PcdFlashNvStorageVariableSize);\r
3670 NvStorageData = AllocateRuntimeZeroPool (NvStorageSize);\r
3671 if (NvStorageData == NULL) {\r
3672 return EFI_OUT_OF_RESOURCES;\r
3673 }\r
3674\r
3675 NvStorageBase = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageVariableBase64);\r
3676 if (NvStorageBase == 0) {\r
3677 NvStorageBase = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageVariableBase);\r
3678 }\r
3679 //\r
3680 // Copy NV storage data to the memory buffer.\r
3681 //\r
3682 CopyMem (NvStorageData, (UINT8 *) (UINTN) NvStorageBase, NvStorageSize);\r
3683\r
3684 //\r
3685 // Check the FTW last write data hob.\r
3686 //\r
3687 GuidHob = GetFirstGuidHob (&gEdkiiFaultTolerantWriteGuid);\r
3688 if (GuidHob != NULL) {\r
3689 FtwLastWriteData = (FAULT_TOLERANT_WRITE_LAST_WRITE_DATA *) GET_GUID_HOB_DATA (GuidHob);\r
3690 if (FtwLastWriteData->TargetAddress == NvStorageBase) {\r
3691 DEBUG ((EFI_D_INFO, "Variable: NV storage is backed up in spare block: 0x%x\n", (UINTN) FtwLastWriteData->SpareAddress));\r
3692 //\r
3693 // Copy the backed up NV storage data to the memory buffer from spare block.\r
3694 //\r
3695 CopyMem (NvStorageData, (UINT8 *) (UINTN) (FtwLastWriteData->SpareAddress), NvStorageSize);\r
3696 } else if ((FtwLastWriteData->TargetAddress > NvStorageBase) &&\r
3697 (FtwLastWriteData->TargetAddress < (NvStorageBase + NvStorageSize))) {\r
3698 //\r
3699 // Flash NV storage from the Offset is backed up in spare block.\r
3700 //\r
3701 BackUpOffset = (UINT32) (FtwLastWriteData->TargetAddress - NvStorageBase);\r
3702 BackUpSize = NvStorageSize - BackUpOffset;\r
3703 DEBUG ((EFI_D_INFO, "Variable: High partial NV storage from offset: %x is backed up in spare block: 0x%x\n", BackUpOffset, (UINTN) FtwLastWriteData->SpareAddress));\r
3704 //\r
3705 // Copy the partial backed up NV storage data to the memory buffer from spare block.\r
3706 //\r
3707 CopyMem (NvStorageData + BackUpOffset, (UINT8 *) (UINTN) FtwLastWriteData->SpareAddress, BackUpSize);\r
3708 }\r
3709 }\r
3710\r
3711 FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *) NvStorageData;\r
3712\r
3713 //\r
3714 // Check if the Firmware Volume is not corrupted\r
3715 //\r
3716 if ((FvHeader->Signature != EFI_FVH_SIGNATURE) || (!CompareGuid (&gEfiSystemNvDataFvGuid, &FvHeader->FileSystemGuid))) {\r
3717 FreePool (NvStorageData);\r
3718 DEBUG ((EFI_D_ERROR, "Firmware Volume for Variable Store is corrupted\n"));\r
3719 return EFI_VOLUME_CORRUPTED;\r
3720 }\r
3721\r
3722 VariableStoreBase = (EFI_PHYSICAL_ADDRESS) ((UINTN) FvHeader + FvHeader->HeaderLength);\r
3723 VariableStoreLength = (UINT64) (NvStorageSize - FvHeader->HeaderLength);\r
3724\r
3725 mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase = VariableStoreBase;\r
3726 mNvVariableCache = (VARIABLE_STORE_HEADER *) (UINTN) VariableStoreBase;\r
3727 if (GetVariableStoreStatus (mNvVariableCache) != EfiValid) {\r
3728 FreePool (NvStorageData);\r
3729 DEBUG((EFI_D_ERROR, "Variable Store header is corrupted\n"));\r
3730 return EFI_VOLUME_CORRUPTED;\r
3731 }\r
3732 ASSERT(mNvVariableCache->Size == VariableStoreLength);\r
3733\r
3734\r
3735 ASSERT (sizeof (VARIABLE_STORE_HEADER) <= VariableStoreLength);\r
3736\r
3737 HwErrStorageSize = PcdGet32 (PcdHwErrStorageSize);\r
3738 MaxUserNvVariableSpaceSize = PcdGet32 (PcdMaxUserNvVariableSpaceSize);\r
3739 BoottimeReservedNvVariableSpaceSize = PcdGet32 (PcdBoottimeReservedNvVariableSpaceSize);\r
3740\r
3741 //\r
3742 // Note that in EdkII variable driver implementation, Hardware Error Record type variable\r
3743 // is stored with common variable in the same NV region. So the platform integrator should\r
3744 // ensure that the value of PcdHwErrStorageSize is less than the value of\r
3745 // VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)).\r
3746 //\r
3747 ASSERT (HwErrStorageSize < (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)));\r
3748 //\r
3749 // Ensure that the value of PcdMaxUserNvVariableSpaceSize is less than the value of\r
3750 // VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)) - PcdGet32 (PcdHwErrStorageSize).\r
3751 //\r
3752 ASSERT (MaxUserNvVariableSpaceSize < (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER) - HwErrStorageSize));\r
3753 //\r
3754 // Ensure that the value of PcdBoottimeReservedNvVariableSpaceSize is less than the value of\r
3755 // VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)) - PcdGet32 (PcdHwErrStorageSize).\r
3756 //\r
3757 ASSERT (BoottimeReservedNvVariableSpaceSize < (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER) - HwErrStorageSize));\r
3758\r
3759 mVariableModuleGlobal->CommonVariableSpace = ((UINTN) VariableStoreLength - sizeof (VARIABLE_STORE_HEADER) - HwErrStorageSize);\r
3760 mVariableModuleGlobal->CommonMaxUserVariableSpace = ((MaxUserNvVariableSpaceSize != 0) ? MaxUserNvVariableSpaceSize : mVariableModuleGlobal->CommonVariableSpace);\r
3761 mVariableModuleGlobal->CommonRuntimeVariableSpace = mVariableModuleGlobal->CommonVariableSpace - BoottimeReservedNvVariableSpaceSize;\r
3762\r
3763 DEBUG ((EFI_D_INFO, "Variable driver common space: 0x%x 0x%x 0x%x\n", mVariableModuleGlobal->CommonVariableSpace, mVariableModuleGlobal->CommonMaxUserVariableSpace, mVariableModuleGlobal->CommonRuntimeVariableSpace));\r
3764\r
3765 //\r
3766 // The max variable or hardware error variable size should be < variable store size.\r
3767 //\r
3768 ASSERT(MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32 (PcdMaxHardwareErrorVariableSize)) < VariableStoreLength);\r
3769\r
3770 //\r
3771 // Parse non-volatile variable data and get last variable offset.\r
3772 //\r
3773 Variable = GetStartPointer ((VARIABLE_STORE_HEADER *)(UINTN)VariableStoreBase);\r
3774 while (IsValidVariableHeader (Variable, GetEndPointer ((VARIABLE_STORE_HEADER *)(UINTN)VariableStoreBase))) {\r
3775 NextVariable = GetNextVariablePtr (Variable);\r
3776 VariableSize = (UINTN) NextVariable - (UINTN) Variable;\r
3777 if ((Variable->Attributes & (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) == (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) {\r
3778 mVariableModuleGlobal->HwErrVariableTotalSize += VariableSize;\r
3779 } else {\r
3780 mVariableModuleGlobal->CommonVariableTotalSize += VariableSize;\r
3781 }\r
3782\r
3783 Variable = NextVariable;\r
3784 }\r
3785 mVariableModuleGlobal->NonVolatileLastVariableOffset = (UINTN) Variable - (UINTN) VariableStoreBase;\r
3786\r
3787 return EFI_SUCCESS;\r
3788}\r
3789\r
3790/**\r
3791 Flush the HOB variable to flash.\r
3792\r
3793 @param[in] VariableName Name of variable has been updated or deleted.\r
3794 @param[in] VendorGuid Guid of variable has been updated or deleted.\r
3795\r
3796**/\r
3797VOID\r
3798FlushHobVariableToFlash (\r
3799 IN CHAR16 *VariableName,\r
3800 IN EFI_GUID *VendorGuid\r
3801 )\r
3802{\r
3803 EFI_STATUS Status;\r
3804 VARIABLE_STORE_HEADER *VariableStoreHeader;\r
3805 VARIABLE_HEADER *Variable;\r
3806 VOID *VariableData;\r
3807 BOOLEAN ErrorFlag;\r
3808\r
3809 ErrorFlag = FALSE;\r
3810\r
3811 //\r
3812 // Flush the HOB variable to flash.\r
3813 //\r
3814 if (mVariableModuleGlobal->VariableGlobal.HobVariableBase != 0) {\r
3815 VariableStoreHeader = (VARIABLE_STORE_HEADER *) (UINTN) mVariableModuleGlobal->VariableGlobal.HobVariableBase;\r
3816 //\r
3817 // Set HobVariableBase to 0, it can avoid SetVariable to call back.\r
3818 //\r
3819 mVariableModuleGlobal->VariableGlobal.HobVariableBase = 0;\r
3820 for ( Variable = GetStartPointer (VariableStoreHeader)\r
3821 ; IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader))\r
3822 ; Variable = GetNextVariablePtr (Variable)\r
3823 ) {\r
3824 if (Variable->State != VAR_ADDED) {\r
3825 //\r
3826 // The HOB variable has been set to DELETED state in local.\r
3827 //\r
3828 continue;\r
3829 }\r
3830 ASSERT ((Variable->Attributes & EFI_VARIABLE_NON_VOLATILE) != 0);\r
3831 if (VendorGuid == NULL || VariableName == NULL ||\r
3832 !CompareGuid (VendorGuid, &Variable->VendorGuid) ||\r
3833 StrCmp (VariableName, GetVariableNamePtr (Variable)) != 0) {\r
3834 VariableData = GetVariableDataPtr (Variable);\r
3835 Status = VariableServiceSetVariable (\r
3836 GetVariableNamePtr (Variable),\r
3837 &Variable->VendorGuid,\r
3838 Variable->Attributes,\r
3839 Variable->DataSize,\r
3840 VariableData\r
3841 );\r
3842 DEBUG ((EFI_D_INFO, "Variable driver flush the HOB variable to flash: %g %s %r\n", &Variable->VendorGuid, GetVariableNamePtr (Variable), Status));\r
3843 } else {\r
3844 //\r
3845 // The updated or deleted variable is matched with the HOB variable.\r
3846 // Don't break here because we will try to set other HOB variables\r
3847 // since this variable could be set successfully.\r
3848 //\r
3849 Status = EFI_SUCCESS;\r
3850 }\r
3851 if (!EFI_ERROR (Status)) {\r
3852 //\r
3853 // If set variable successful, or the updated or deleted variable is matched with the HOB variable,\r
3854 // set the HOB variable to DELETED state in local.\r
3855 //\r
3856 DEBUG ((EFI_D_INFO, "Variable driver set the HOB variable to DELETED state in local: %g %s\n", &Variable->VendorGuid, GetVariableNamePtr (Variable)));\r
3857 Variable->State &= VAR_DELETED;\r
3858 } else {\r
3859 ErrorFlag = TRUE;\r
3860 }\r
3861 }\r
3862 if (ErrorFlag) {\r
3863 //\r
3864 // We still have HOB variable(s) not flushed in flash.\r
3865 //\r
3866 mVariableModuleGlobal->VariableGlobal.HobVariableBase = (EFI_PHYSICAL_ADDRESS) (UINTN) VariableStoreHeader;\r
3867 } else {\r
3868 //\r
3869 // All HOB variables have been flushed in flash.\r
3870 //\r
3871 DEBUG ((EFI_D_INFO, "Variable driver: all HOB variables have been flushed in flash.\n"));\r
3872 if (!AtRuntime ()) {\r
3873 FreePool ((VOID *) VariableStoreHeader);\r
3874 }\r
3875 }\r
3876 }\r
3877\r
3878}\r
3879\r
3880/**\r
3881 Initializes variable write service after FTW was ready.\r
3882\r
3883 @retval EFI_SUCCESS Function successfully executed.\r
3884 @retval Others Fail to initialize the variable service.\r
3885\r
3886**/\r
3887EFI_STATUS\r
3888VariableWriteServiceInitialize (\r
3889 VOID\r
3890 )\r
3891{\r
3892 EFI_STATUS Status;\r
3893 VARIABLE_STORE_HEADER *VariableStoreHeader;\r
3894 UINTN Index;\r
3895 UINT8 Data;\r
3896 EFI_PHYSICAL_ADDRESS VariableStoreBase;\r
3897 EFI_PHYSICAL_ADDRESS NvStorageBase;\r
3898\r
3899 NvStorageBase = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageVariableBase64);\r
3900 if (NvStorageBase == 0) {\r
3901 NvStorageBase = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageVariableBase);\r
3902 }\r
3903 VariableStoreBase = NvStorageBase + (((EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)(NvStorageBase))->HeaderLength);\r
3904\r
3905 //\r
3906 // Let NonVolatileVariableBase point to flash variable store base directly after FTW ready.\r
3907 //\r
3908 mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase = VariableStoreBase;\r
3909 VariableStoreHeader = (VARIABLE_STORE_HEADER *)(UINTN)VariableStoreBase;\r
3910\r
3911 //\r
3912 // Check if the free area is really free.\r
3913 //\r
3914 for (Index = mVariableModuleGlobal->NonVolatileLastVariableOffset; Index < VariableStoreHeader->Size; Index++) {\r
3915 Data = ((UINT8 *) mNvVariableCache)[Index];\r
3916 if (Data != 0xff) {\r
3917 //\r
3918 // There must be something wrong in variable store, do reclaim operation.\r
3919 //\r
3920 Status = Reclaim (\r
3921 mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase,\r
3922 &mVariableModuleGlobal->NonVolatileLastVariableOffset,\r
3923 FALSE,\r
3924 NULL,\r
3925 NULL,\r
3926 0,\r
3927 FALSE\r
3928 );\r
3929 if (EFI_ERROR (Status)) {\r
3930 return Status;\r
3931 }\r
3932 break;\r
3933 }\r
3934 }\r
3935\r
3936 FlushHobVariableToFlash (NULL, NULL);\r
3937\r
3938 //\r
3939 // Authenticated variable initialize.\r
3940 //\r
3941 Status = AutenticatedVariableServiceInitialize ();\r
3942\r
3943 return Status;\r
3944}\r
3945\r
3946\r
3947/**\r
3948 Initializes variable store area for non-volatile and volatile variable.\r
3949\r
3950 @retval EFI_SUCCESS Function successfully executed.\r
3951 @retval EFI_OUT_OF_RESOURCES Fail to allocate enough memory resource.\r
3952\r
3953**/\r
3954EFI_STATUS\r
3955VariableCommonInitialize (\r
3956 VOID\r
3957 )\r
3958{\r
3959 EFI_STATUS Status;\r
3960 VARIABLE_STORE_HEADER *VolatileVariableStore;\r
3961 VARIABLE_STORE_HEADER *VariableStoreHeader;\r
3962 UINT64 VariableStoreLength;\r
3963 UINTN ScratchSize;\r
3964 EFI_HOB_GUID_TYPE *GuidHob;\r
3965\r
3966 //\r
3967 // Allocate runtime memory for variable driver global structure.\r
3968 //\r
3969 mVariableModuleGlobal = AllocateRuntimeZeroPool (sizeof (VARIABLE_MODULE_GLOBAL));\r
3970 if (mVariableModuleGlobal == NULL) {\r
3971 return EFI_OUT_OF_RESOURCES;\r
3972 }\r
3973\r
3974 InitializeLock (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock, TPL_NOTIFY);\r
3975\r
3976 //\r
3977 // Get HOB variable store.\r
3978 //\r
3979 GuidHob = GetFirstGuidHob (&gEfiAuthenticatedVariableGuid);\r
3980 if (GuidHob != NULL) {\r
3981 VariableStoreHeader = GET_GUID_HOB_DATA (GuidHob);\r
3982 VariableStoreLength = (UINT64) (GuidHob->Header.HobLength - sizeof (EFI_HOB_GUID_TYPE));\r
3983 if (GetVariableStoreStatus (VariableStoreHeader) == EfiValid) {\r
3984 mVariableModuleGlobal->VariableGlobal.HobVariableBase = (EFI_PHYSICAL_ADDRESS) (UINTN) AllocateRuntimeCopyPool ((UINTN) VariableStoreLength, (VOID *) VariableStoreHeader);\r
3985 if (mVariableModuleGlobal->VariableGlobal.HobVariableBase == 0) {\r
3986 FreePool (mVariableModuleGlobal);\r
3987 return EFI_OUT_OF_RESOURCES;\r
3988 }\r
3989 } else {\r
3990 DEBUG ((EFI_D_ERROR, "HOB Variable Store header is corrupted!\n"));\r
3991 }\r
3992 }\r
3993\r
3994 //\r
3995 // Allocate memory for volatile variable store, note that there is a scratch space to store scratch data.\r
3996 //\r
3997 ScratchSize = MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32 (PcdMaxHardwareErrorVariableSize));\r
3998 VolatileVariableStore = AllocateRuntimePool (PcdGet32 (PcdVariableStoreSize) + ScratchSize);\r
3999 if (VolatileVariableStore == NULL) {\r
4000 if (mVariableModuleGlobal->VariableGlobal.HobVariableBase != 0) {\r
4001 FreePool ((VOID *) (UINTN) mVariableModuleGlobal->VariableGlobal.HobVariableBase);\r
4002 }\r
4003 FreePool (mVariableModuleGlobal);\r
4004 return EFI_OUT_OF_RESOURCES;\r
4005 }\r
4006\r
4007 SetMem (VolatileVariableStore, PcdGet32 (PcdVariableStoreSize) + ScratchSize, 0xff);\r
4008\r
4009 //\r
4010 // Initialize Variable Specific Data.\r
4011 //\r
4012 mVariableModuleGlobal->VariableGlobal.VolatileVariableBase = (EFI_PHYSICAL_ADDRESS) (UINTN) VolatileVariableStore;\r
4013 mVariableModuleGlobal->VolatileLastVariableOffset = (UINTN) GetStartPointer (VolatileVariableStore) - (UINTN) VolatileVariableStore;\r
4014\r
4015 CopyGuid (&VolatileVariableStore->Signature, &gEfiAuthenticatedVariableGuid);\r
4016 VolatileVariableStore->Size = PcdGet32 (PcdVariableStoreSize);\r
4017 VolatileVariableStore->Format = VARIABLE_STORE_FORMATTED;\r
4018 VolatileVariableStore->State = VARIABLE_STORE_HEALTHY;\r
4019 VolatileVariableStore->Reserved = 0;\r
4020 VolatileVariableStore->Reserved1 = 0;\r
4021\r
4022 //\r
4023 // Init non-volatile variable store.\r
4024 //\r
4025 Status = InitNonVolatileVariableStore ();\r
4026 if (EFI_ERROR (Status)) {\r
4027 if (mVariableModuleGlobal->VariableGlobal.HobVariableBase != 0) {\r
4028 FreePool ((VOID *) (UINTN) mVariableModuleGlobal->VariableGlobal.HobVariableBase);\r
4029 }\r
4030 FreePool (mVariableModuleGlobal);\r
4031 FreePool (VolatileVariableStore);\r
4032 }\r
4033\r
4034 return Status;\r
4035}\r
4036\r
4037\r
4038/**\r
4039 Get the proper fvb handle and/or fvb protocol by the given Flash address.\r
4040\r
4041 @param[in] Address The Flash address.\r
4042 @param[out] FvbHandle In output, if it is not NULL, it points to the proper FVB handle.\r
4043 @param[out] FvbProtocol In output, if it is not NULL, it points to the proper FVB protocol.\r
4044\r
4045**/\r
4046EFI_STATUS\r
4047GetFvbInfoByAddress (\r
4048 IN EFI_PHYSICAL_ADDRESS Address,\r
4049 OUT EFI_HANDLE *FvbHandle OPTIONAL,\r
4050 OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvbProtocol OPTIONAL\r
4051 )\r
4052{\r
4053 EFI_STATUS Status;\r
4054 EFI_HANDLE *HandleBuffer;\r
4055 UINTN HandleCount;\r
4056 UINTN Index;\r
4057 EFI_PHYSICAL_ADDRESS FvbBaseAddress;\r
4058 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;\r
4059 EFI_FVB_ATTRIBUTES_2 Attributes;\r
4060 UINTN BlockSize;\r
4061 UINTN NumberOfBlocks;\r
4062\r
4063 HandleBuffer = NULL;\r
4064 //\r
4065 // Get all FVB handles.\r
4066 //\r
4067 Status = GetFvbCountAndBuffer (&HandleCount, &HandleBuffer);\r
4068 if (EFI_ERROR (Status)) {\r
4069 return EFI_NOT_FOUND;\r
4070 }\r
4071\r
4072 //\r
4073 // Get the FVB to access variable store.\r
4074 //\r
4075 Fvb = NULL;\r
4076 for (Index = 0; Index < HandleCount; Index += 1, Status = EFI_NOT_FOUND, Fvb = NULL) {\r
4077 Status = GetFvbByHandle (HandleBuffer[Index], &Fvb);\r
4078 if (EFI_ERROR (Status)) {\r
4079 Status = EFI_NOT_FOUND;\r
4080 break;\r
4081 }\r
4082\r
4083 //\r
4084 // Ensure this FVB protocol supported Write operation.\r
4085 //\r
4086 Status = Fvb->GetAttributes (Fvb, &Attributes);\r
4087 if (EFI_ERROR (Status) || ((Attributes & EFI_FVB2_WRITE_STATUS) == 0)) {\r
4088 continue;\r
4089 }\r
4090\r
4091 //\r
4092 // Compare the address and select the right one.\r
4093 //\r
4094 Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress);\r
4095 if (EFI_ERROR (Status)) {\r
4096 continue;\r
4097 }\r
4098\r
4099 //\r
4100 // Assume one FVB has one type of BlockSize.\r
4101 //\r
4102 Status = Fvb->GetBlockSize (Fvb, 0, &BlockSize, &NumberOfBlocks);\r
4103 if (EFI_ERROR (Status)) {\r
4104 continue;\r
4105 }\r
4106\r
4107 if ((Address >= FvbBaseAddress) && (Address < (FvbBaseAddress + BlockSize * NumberOfBlocks))) {\r
4108 if (FvbHandle != NULL) {\r
4109 *FvbHandle = HandleBuffer[Index];\r
4110 }\r
4111 if (FvbProtocol != NULL) {\r
4112 *FvbProtocol = Fvb;\r
4113 }\r
4114 Status = EFI_SUCCESS;\r
4115 break;\r
4116 }\r
4117 }\r
4118 FreePool (HandleBuffer);\r
4119\r
4120 if (Fvb == NULL) {\r
4121 Status = EFI_NOT_FOUND;\r
4122 }\r
4123\r
4124 return Status;\r
4125}\r