+ while (LanguageLength > 0) {\r
+ //\r
+ // Loop through all language codes in SupportedLanguages\r
+ //\r
+ for (Supported = SupportedLanguages; *Supported != '\0'; Supported += CompareLength) {\r
+ //\r
+ // In RFC 4646 mode, then Loop through all language codes in SupportedLanguages\r
+ //\r
+ if (!Iso639Language) {\r
+ //\r
+ // Skip ';' characters in Supported\r
+ //\r
+ for (; *Supported != '\0' && *Supported == ';'; Supported++);\r
+ //\r
+ // Determine the length of the next language code in Supported\r
+ //\r
+ for (CompareLength = 0; Supported[CompareLength] != 0 && Supported[CompareLength] != ';'; CompareLength++);\r
+ //\r
+ // If Language is longer than the Supported, then skip to the next language\r
+ //\r
+ if (LanguageLength > CompareLength) {\r
+ continue;\r
+ }\r
+ }\r
+ //\r
+ // See if the first LanguageLength characters in Supported match Language\r
+ //\r
+ if (AsciiStrnCmp (Supported, Language, LanguageLength) == 0) {\r
+ VA_END (Args);\r
+\r
+ Buffer = Iso639Language ? mVariableModuleGlobal->Lang : mVariableModuleGlobal->PlatformLang;\r
+ Buffer[CompareLength] = '\0';\r
+ return CopyMem (Buffer, Supported, CompareLength);\r
+ }\r
+ }\r
+\r
+ if (Iso639Language) {\r
+ //\r
+ // If ISO 639 mode, then each language can only be tested once\r
+ //\r
+ LanguageLength = 0;\r
+ } else {\r
+ //\r
+ // If RFC 4646 mode, then trim Language from the right to the next '-' character\r
+ //\r
+ for (LanguageLength--; LanguageLength > 0 && Language[LanguageLength] != '-'; LanguageLength--);\r
+ }\r
+ }\r
+ }\r
+ VA_END (Args);\r
+\r
+ //\r
+ // No matches were found\r
+ //\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ This function is to check if the remaining variable space is enough to set\r
+ all Variables from argument list successfully. The purpose of the check\r
+ is to keep the consistency of the Variables to be in variable storage.\r
+\r
+ Note: Variables are assumed to be in same storage.\r
+ The set sequence of Variables will be same with the sequence of VariableEntry from argument list,\r
+ so follow the argument sequence to check the Variables.\r
+\r
+ @param[in] Attributes Variable attributes for Variable entries.\r
+ @param[in] Marker VA_LIST style variable argument list.\r
+ The variable argument list with type VARIABLE_ENTRY_CONSISTENCY *.\r
+ A NULL terminates the list. The VariableSize of\r
+ VARIABLE_ENTRY_CONSISTENCY is the variable data size as input.\r
+ It will be changed to variable total size as output.\r
+\r
+ @retval TRUE Have enough variable space to set the Variables successfully.\r
+ @retval FALSE No enough variable space to set the Variables successfully.\r
+\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+CheckRemainingSpaceForConsistencyInternal (\r
+ IN UINT32 Attributes,\r
+ IN VA_LIST Marker\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ VA_LIST Args;\r
+ VARIABLE_ENTRY_CONSISTENCY *VariableEntry;\r
+ UINT64 MaximumVariableStorageSize;\r
+ UINT64 RemainingVariableStorageSize;\r
+ UINT64 MaximumVariableSize;\r
+ UINTN TotalNeededSize;\r
+ UINTN OriginalVarSize;\r
+ VARIABLE_STORE_HEADER *VariableStoreHeader;\r
+ VARIABLE_POINTER_TRACK VariablePtrTrack;\r
+ VARIABLE_HEADER *NextVariable;\r
+ UINTN VarNameSize;\r
+ UINTN VarDataSize;\r
+\r
+ //\r
+ // Non-Volatile related.\r
+ //\r
+ VariableStoreHeader = mNvVariableCache;\r
+\r
+ Status = VariableServiceQueryVariableInfoInternal (\r
+ Attributes,\r
+ &MaximumVariableStorageSize,\r
+ &RemainingVariableStorageSize,\r
+ &MaximumVariableSize\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ TotalNeededSize = 0;\r
+ VA_COPY (Args, Marker);\r
+ VariableEntry = VA_ARG (Args, VARIABLE_ENTRY_CONSISTENCY *);\r
+ while (VariableEntry != NULL) {\r
+ //\r
+ // Calculate variable total size.\r
+ //\r
+ VarNameSize = StrSize (VariableEntry->Name);\r
+ VarNameSize += GET_PAD_SIZE (VarNameSize);\r
+ VarDataSize = VariableEntry->VariableSize;\r
+ VarDataSize += GET_PAD_SIZE (VarDataSize);\r
+ VariableEntry->VariableSize = HEADER_ALIGN (GetVariableHeaderSize () + VarNameSize + VarDataSize);\r
+\r
+ TotalNeededSize += VariableEntry->VariableSize;\r
+ VariableEntry = VA_ARG (Args, VARIABLE_ENTRY_CONSISTENCY *);\r
+ }\r
+ VA_END (Args);\r
+\r
+ if (RemainingVariableStorageSize >= TotalNeededSize) {\r
+ //\r
+ // Already have enough space.\r
+ //\r
+ return TRUE;\r
+ } else if (AtRuntime ()) {\r
+ //\r
+ // At runtime, no reclaim.\r
+ // The original variable space of Variables can't be reused.\r
+ //\r
+ return FALSE;\r
+ }\r
+\r
+ VA_COPY (Args, Marker);\r
+ VariableEntry = VA_ARG (Args, VARIABLE_ENTRY_CONSISTENCY *);\r
+ while (VariableEntry != NULL) {\r
+ //\r
+ // Check if Variable[Index] has been present and get its size.\r
+ //\r
+ OriginalVarSize = 0;\r
+ VariablePtrTrack.StartPtr = GetStartPointer (VariableStoreHeader);\r
+ VariablePtrTrack.EndPtr = GetEndPointer (VariableStoreHeader);\r
+ Status = FindVariableEx (\r
+ VariableEntry->Name,\r
+ VariableEntry->Guid,\r
+ FALSE,\r
+ &VariablePtrTrack\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // Get size of Variable[Index].\r
+ //\r
+ NextVariable = GetNextVariablePtr (VariablePtrTrack.CurrPtr);\r
+ OriginalVarSize = (UINTN) NextVariable - (UINTN) VariablePtrTrack.CurrPtr;\r
+ //\r
+ // Add the original size of Variable[Index] to remaining variable storage size.\r
+ //\r
+ RemainingVariableStorageSize += OriginalVarSize;\r
+ }\r
+ if (VariableEntry->VariableSize > RemainingVariableStorageSize) {\r
+ //\r
+ // No enough space for Variable[Index].\r
+ //\r
+ return FALSE;\r
+ }\r
+ //\r
+ // Sub the (new) size of Variable[Index] from remaining variable storage size.\r
+ //\r
+ RemainingVariableStorageSize -= VariableEntry->VariableSize;\r
+ VariableEntry = VA_ARG (Args, VARIABLE_ENTRY_CONSISTENCY *);\r
+ }\r
+ VA_END (Args);\r
+\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ This function is to check if the remaining variable space is enough to set\r
+ all Variables from argument list successfully. The purpose of the check\r
+ is to keep the consistency of the Variables to be in variable storage.\r
+\r
+ Note: Variables are assumed to be in same storage.\r
+ The set sequence of Variables will be same with the sequence of VariableEntry from argument list,\r
+ so follow the argument sequence to check the Variables.\r
+\r
+ @param[in] Attributes Variable attributes for Variable entries.\r
+ @param ... The variable argument list with type VARIABLE_ENTRY_CONSISTENCY *.\r
+ A NULL terminates the list. The VariableSize of\r
+ VARIABLE_ENTRY_CONSISTENCY is the variable data size as input.\r
+ It will be changed to variable total size as output.\r
+\r
+ @retval TRUE Have enough variable space to set the Variables successfully.\r
+ @retval FALSE No enough variable space to set the Variables successfully.\r
+\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+CheckRemainingSpaceForConsistency (\r
+ IN UINT32 Attributes,\r
+ ...\r
+ )\r
+{\r
+ VA_LIST Marker;\r
+ BOOLEAN Return;\r
+\r
+ VA_START (Marker, Attributes);\r
+\r
+ Return = CheckRemainingSpaceForConsistencyInternal (Attributes, Marker);\r
+\r
+ VA_END (Marker);\r
+\r
+ return Return;\r
+}\r
+\r
+/**\r
+ Hook the operations in PlatformLangCodes, LangCodes, PlatformLang and Lang.\r
+\r
+ When setting Lang/LangCodes, simultaneously update PlatformLang/PlatformLangCodes.\r
+\r
+ According to UEFI spec, PlatformLangCodes/LangCodes are only set once in firmware initialization,\r
+ and are read-only. Therefore, in variable driver, only store the original value for other use.\r
+\r
+ @param[in] VariableName Name of variable.\r
+\r
+ @param[in] Data Variable data.\r
+\r
+ @param[in] DataSize Size of data. 0 means delete.\r
+\r
+ @retval EFI_SUCCESS The update operation is successful or ignored.\r
+ @retval EFI_WRITE_PROTECTED Update PlatformLangCodes/LangCodes at runtime.\r
+ @retval EFI_OUT_OF_RESOURCES No enough variable space to do the update operation.\r
+ @retval Others Other errors happened during the update operation.\r
+\r
+**/\r
+EFI_STATUS\r
+AutoUpdateLangVariable (\r
+ IN CHAR16 *VariableName,\r
+ IN VOID *Data,\r
+ IN UINTN DataSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ CHAR8 *BestPlatformLang;\r
+ CHAR8 *BestLang;\r
+ UINTN Index;\r
+ UINT32 Attributes;\r
+ VARIABLE_POINTER_TRACK Variable;\r
+ BOOLEAN SetLanguageCodes;\r
+ VARIABLE_ENTRY_CONSISTENCY VariableEntry[2];\r
+\r
+ //\r
+ // Don't do updates for delete operation\r
+ //\r
+ if (DataSize == 0) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ SetLanguageCodes = FALSE;\r
+\r
+ if (StrCmp (VariableName, EFI_PLATFORM_LANG_CODES_VARIABLE_NAME) == 0) {\r
+ //\r
+ // PlatformLangCodes is a volatile variable, so it can not be updated at runtime.\r
+ //\r
+ if (AtRuntime ()) {\r
+ return EFI_WRITE_PROTECTED;\r
+ }\r
+\r
+ SetLanguageCodes = TRUE;\r
+\r
+ //\r
+ // According to UEFI spec, PlatformLangCodes is only set once in firmware initialization, and is read-only\r
+ // Therefore, in variable driver, only store the original value for other use.\r
+ //\r
+ if (mVariableModuleGlobal->PlatformLangCodes != NULL) {\r
+ FreePool (mVariableModuleGlobal->PlatformLangCodes);\r
+ }\r
+ mVariableModuleGlobal->PlatformLangCodes = AllocateRuntimeCopyPool (DataSize, Data);\r
+ ASSERT (mVariableModuleGlobal->PlatformLangCodes != NULL);\r
+\r
+ //\r
+ // PlatformLang holds a single language from PlatformLangCodes,\r
+ // so the size of PlatformLangCodes is enough for the PlatformLang.\r
+ //\r
+ if (mVariableModuleGlobal->PlatformLang != NULL) {\r
+ FreePool (mVariableModuleGlobal->PlatformLang);\r
+ }\r
+ mVariableModuleGlobal->PlatformLang = AllocateRuntimePool (DataSize);\r
+ ASSERT (mVariableModuleGlobal->PlatformLang != NULL);\r
+\r
+ } else if (StrCmp (VariableName, EFI_LANG_CODES_VARIABLE_NAME) == 0) {\r
+ //\r
+ // LangCodes is a volatile variable, so it can not be updated at runtime.\r
+ //\r
+ if (AtRuntime ()) {\r
+ return EFI_WRITE_PROTECTED;\r
+ }\r
+\r
+ SetLanguageCodes = TRUE;\r
+\r
+ //\r
+ // According to UEFI spec, LangCodes is only set once in firmware initialization, and is read-only\r
+ // Therefore, in variable driver, only store the original value for other use.\r
+ //\r
+ if (mVariableModuleGlobal->LangCodes != NULL) {\r
+ FreePool (mVariableModuleGlobal->LangCodes);\r
+ }\r
+ mVariableModuleGlobal->LangCodes = AllocateRuntimeCopyPool (DataSize, Data);\r
+ ASSERT (mVariableModuleGlobal->LangCodes != NULL);\r
+ }\r
+\r
+ if (SetLanguageCodes\r
+ && (mVariableModuleGlobal->PlatformLangCodes != NULL)\r
+ && (mVariableModuleGlobal->LangCodes != NULL)) {\r
+ //\r
+ // Update Lang if PlatformLang is already set\r
+ // Update PlatformLang if Lang is already set\r
+ //\r
+ Status = FindVariable (EFI_PLATFORM_LANG_VARIABLE_NAME, &gEfiGlobalVariableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE);\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // Update Lang\r
+ //\r
+ VariableName = EFI_PLATFORM_LANG_VARIABLE_NAME;\r
+ Data = GetVariableDataPtr (Variable.CurrPtr);\r
+ DataSize = DataSizeOfVariable (Variable.CurrPtr);\r
+ } else {\r
+ Status = FindVariable (EFI_LANG_VARIABLE_NAME, &gEfiGlobalVariableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE);\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // Update PlatformLang\r
+ //\r
+ VariableName = EFI_LANG_VARIABLE_NAME;\r
+ Data = GetVariableDataPtr (Variable.CurrPtr);\r
+ DataSize = DataSizeOfVariable (Variable.CurrPtr);\r
+ } else {\r
+ //\r
+ // Neither PlatformLang nor Lang is set, directly return\r
+ //\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ //\r
+ // According to UEFI spec, "Lang" and "PlatformLang" is NV|BS|RT attributions.\r
+ //\r
+ Attributes = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;\r
+\r
+ if (StrCmp (VariableName, EFI_PLATFORM_LANG_VARIABLE_NAME) == 0) {\r
+ //\r
+ // Update Lang when PlatformLangCodes/LangCodes were set.\r
+ //\r
+ if ((mVariableModuleGlobal->PlatformLangCodes != NULL) && (mVariableModuleGlobal->LangCodes != NULL)) {\r
+ //\r
+ // When setting PlatformLang, firstly get most matched language string from supported language codes.\r
+ //\r
+ BestPlatformLang = VariableGetBestLanguage (mVariableModuleGlobal->PlatformLangCodes, FALSE, Data, NULL);\r
+ if (BestPlatformLang != NULL) {\r
+ //\r
+ // Get the corresponding index in language codes.\r
+ //\r
+ Index = GetIndexFromSupportedLangCodes (mVariableModuleGlobal->PlatformLangCodes, BestPlatformLang, FALSE);\r
+\r
+ //\r
+ // Get the corresponding ISO639 language tag according to RFC4646 language tag.\r
+ //\r
+ BestLang = GetLangFromSupportedLangCodes (mVariableModuleGlobal->LangCodes, Index, TRUE);\r
+\r
+ //\r
+ // Check the variable space for both Lang and PlatformLang variable.\r
+ //\r
+ VariableEntry[0].VariableSize = ISO_639_2_ENTRY_SIZE + 1;\r
+ VariableEntry[0].Guid = &gEfiGlobalVariableGuid;\r
+ VariableEntry[0].Name = EFI_LANG_VARIABLE_NAME;\r
+\r
+ VariableEntry[1].VariableSize = AsciiStrSize (BestPlatformLang);\r
+ VariableEntry[1].Guid = &gEfiGlobalVariableGuid;\r
+ VariableEntry[1].Name = EFI_PLATFORM_LANG_VARIABLE_NAME;\r
+ if (!CheckRemainingSpaceForConsistency (VARIABLE_ATTRIBUTE_NV_BS_RT, &VariableEntry[0], &VariableEntry[1], NULL)) {\r
+ //\r
+ // No enough variable space to set both Lang and PlatformLang successfully.\r
+ //\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ } else {\r
+ //\r
+ // Successfully convert PlatformLang to Lang, and set the BestLang value into Lang variable simultaneously.\r
+ //\r
+ FindVariable (EFI_LANG_VARIABLE_NAME, &gEfiGlobalVariableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE);\r
+\r
+ Status = UpdateVariable (EFI_LANG_VARIABLE_NAME, &gEfiGlobalVariableGuid, BestLang,\r
+ ISO_639_2_ENTRY_SIZE + 1, Attributes, 0, 0, &Variable, NULL);\r
+ }\r
+\r
+ DEBUG ((EFI_D_INFO, "Variable Driver Auto Update PlatformLang, PlatformLang:%a, Lang:%a Status: %r\n", BestPlatformLang, BestLang, Status));\r
+ }\r
+ }\r
+\r
+ } else if (StrCmp (VariableName, EFI_LANG_VARIABLE_NAME) == 0) {\r
+ //\r
+ // Update PlatformLang when PlatformLangCodes/LangCodes were set.\r
+ //\r
+ if ((mVariableModuleGlobal->PlatformLangCodes != NULL) && (mVariableModuleGlobal->LangCodes != NULL)) {\r
+ //\r
+ // When setting Lang, firstly get most matched language string from supported language codes.\r
+ //\r
+ BestLang = VariableGetBestLanguage (mVariableModuleGlobal->LangCodes, TRUE, Data, NULL);\r
+ if (BestLang != NULL) {\r
+ //\r
+ // Get the corresponding index in language codes.\r
+ //\r
+ Index = GetIndexFromSupportedLangCodes (mVariableModuleGlobal->LangCodes, BestLang, TRUE);\r
+\r
+ //\r
+ // Get the corresponding RFC4646 language tag according to ISO639 language tag.\r
+ //\r
+ BestPlatformLang = GetLangFromSupportedLangCodes (mVariableModuleGlobal->PlatformLangCodes, Index, FALSE);\r
+\r
+ //\r
+ // Check the variable space for both PlatformLang and Lang variable.\r
+ //\r
+ VariableEntry[0].VariableSize = AsciiStrSize (BestPlatformLang);\r
+ VariableEntry[0].Guid = &gEfiGlobalVariableGuid;\r
+ VariableEntry[0].Name = EFI_PLATFORM_LANG_VARIABLE_NAME;\r
+\r
+ VariableEntry[1].VariableSize = ISO_639_2_ENTRY_SIZE + 1;\r
+ VariableEntry[1].Guid = &gEfiGlobalVariableGuid;\r
+ VariableEntry[1].Name = EFI_LANG_VARIABLE_NAME;\r
+ if (!CheckRemainingSpaceForConsistency (VARIABLE_ATTRIBUTE_NV_BS_RT, &VariableEntry[0], &VariableEntry[1], NULL)) {\r
+ //\r
+ // No enough variable space to set both PlatformLang and Lang successfully.\r
+ //\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ } else {\r
+ //\r
+ // Successfully convert Lang to PlatformLang, and set the BestPlatformLang value into PlatformLang variable simultaneously.\r
+ //\r
+ FindVariable (EFI_PLATFORM_LANG_VARIABLE_NAME, &gEfiGlobalVariableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE);\r
+\r
+ Status = UpdateVariable (EFI_PLATFORM_LANG_VARIABLE_NAME, &gEfiGlobalVariableGuid, BestPlatformLang,\r
+ AsciiStrSize (BestPlatformLang), Attributes, 0, 0, &Variable, NULL);\r
+ }\r
+\r
+ DEBUG ((EFI_D_INFO, "Variable Driver Auto Update Lang, Lang:%a, PlatformLang:%a Status: %r\n", BestLang, BestPlatformLang, Status));\r
+ }\r
+ }\r
+ }\r
+\r
+ if (SetLanguageCodes) {\r
+ //\r
+ // Continue to set PlatformLangCodes or LangCodes.\r
+ //\r
+ return EFI_SUCCESS;\r
+ } else {\r
+ return Status;\r
+ }\r
+}\r
+\r
+/**\r
+ Compare two EFI_TIME data.\r
+\r
+\r
+ @param FirstTime A pointer to the first EFI_TIME data.\r
+ @param SecondTime A pointer to the second EFI_TIME data.\r
+\r
+ @retval TRUE The FirstTime is not later than the SecondTime.\r
+ @retval FALSE The FirstTime is later than the SecondTime.\r
+\r
+**/\r
+BOOLEAN\r
+VariableCompareTimeStampInternal (\r
+ IN EFI_TIME *FirstTime,\r
+ IN EFI_TIME *SecondTime\r
+ )\r
+{\r
+ if (FirstTime->Year != SecondTime->Year) {\r
+ return (BOOLEAN) (FirstTime->Year < SecondTime->Year);\r
+ } else if (FirstTime->Month != SecondTime->Month) {\r
+ return (BOOLEAN) (FirstTime->Month < SecondTime->Month);\r
+ } else if (FirstTime->Day != SecondTime->Day) {\r
+ return (BOOLEAN) (FirstTime->Day < SecondTime->Day);\r
+ } else if (FirstTime->Hour != SecondTime->Hour) {\r
+ return (BOOLEAN) (FirstTime->Hour < SecondTime->Hour);\r
+ } else if (FirstTime->Minute != SecondTime->Minute) {\r
+ return (BOOLEAN) (FirstTime->Minute < SecondTime->Minute);\r
+ }\r
+\r
+ return (BOOLEAN) (FirstTime->Second <= SecondTime->Second);\r
+}\r
+\r
+/**\r
+ Update the variable region with Variable information. If EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS is set,\r
+ index of associated public key is needed.\r
+\r
+ @param[in] VariableName Name of variable.\r
+ @param[in] VendorGuid Guid of variable.\r
+ @param[in] Data Variable data.\r
+ @param[in] DataSize Size of data. 0 means delete.\r
+ @param[in] Attributes Attributes of the variable.\r
+ @param[in] KeyIndex Index of associated public key.\r
+ @param[in] MonotonicCount Value of associated monotonic count.\r
+ @param[in, out] CacheVariable The variable information which is used to keep track of variable usage.\r
+ @param[in] TimeStamp Value of associated TimeStamp.\r
+\r
+ @retval EFI_SUCCESS The update operation is success.\r
+ @retval EFI_OUT_OF_RESOURCES Variable region is full, can not write other data into this region.\r
+\r
+**/\r
+EFI_STATUS\r
+UpdateVariable (\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid,\r
+ IN VOID *Data,\r
+ IN UINTN DataSize,\r
+ IN UINT32 Attributes OPTIONAL,\r
+ IN UINT32 KeyIndex OPTIONAL,\r
+ IN UINT64 MonotonicCount OPTIONAL,\r
+ IN OUT VARIABLE_POINTER_TRACK *CacheVariable,\r
+ IN EFI_TIME *TimeStamp OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ VARIABLE_HEADER *NextVariable;\r
+ UINTN ScratchSize;\r
+ UINTN MaxDataSize;\r
+ UINTN VarNameOffset;\r
+ UINTN VarDataOffset;\r
+ UINTN VarNameSize;\r
+ UINTN VarSize;\r
+ BOOLEAN Volatile;\r
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;\r
+ UINT8 State;\r
+ VARIABLE_POINTER_TRACK *Variable;\r
+ VARIABLE_POINTER_TRACK NvVariable;\r
+ VARIABLE_STORE_HEADER *VariableStoreHeader;\r
+ UINTN CacheOffset;\r
+ UINT8 *BufferForMerge;\r
+ UINTN MergedBufSize;\r
+ BOOLEAN DataReady;\r
+ UINTN DataOffset;\r
+ BOOLEAN IsCommonVariable;\r
+ BOOLEAN IsCommonUserVariable;\r
+ AUTHENTICATED_VARIABLE_HEADER *AuthVariable;\r
+\r
+ if (mVariableModuleGlobal->FvbInstance == NULL) {\r
+ //\r
+ // The FVB protocol is not ready, so the EFI_VARIABLE_WRITE_ARCH_PROTOCOL is not installed.\r
+ //\r
+ if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) {\r
+ //\r
+ // Trying to update NV variable prior to the installation of EFI_VARIABLE_WRITE_ARCH_PROTOCOL\r
+ //\r
+ DEBUG ((EFI_D_ERROR, "Update NV variable before EFI_VARIABLE_WRITE_ARCH_PROTOCOL ready - %r\n", EFI_NOT_AVAILABLE_YET));\r
+ return EFI_NOT_AVAILABLE_YET;\r
+ } else if ((Attributes & VARIABLE_ATTRIBUTE_AT_AW) != 0) {\r
+ //\r
+ // Trying to update volatile authenticated variable prior to the installation of EFI_VARIABLE_WRITE_ARCH_PROTOCOL\r
+ // The authenticated variable perhaps is not initialized, just return here.\r
+ //\r
+ DEBUG ((EFI_D_ERROR, "Update AUTH variable before EFI_VARIABLE_WRITE_ARCH_PROTOCOL ready - %r\n", EFI_NOT_AVAILABLE_YET));\r
+ return EFI_NOT_AVAILABLE_YET;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Check if CacheVariable points to the variable in variable HOB.\r
+ // If yes, let CacheVariable points to the variable in NV variable cache.\r
+ //\r
+ if ((CacheVariable->CurrPtr != NULL) &&\r
+ (mVariableModuleGlobal->VariableGlobal.HobVariableBase != 0) &&\r
+ (CacheVariable->StartPtr == GetStartPointer ((VARIABLE_STORE_HEADER *) (UINTN) mVariableModuleGlobal->VariableGlobal.HobVariableBase))\r
+ ) {\r
+ CacheVariable->StartPtr = GetStartPointer (mNvVariableCache);\r
+ CacheVariable->EndPtr = GetEndPointer (mNvVariableCache);\r
+ CacheVariable->Volatile = FALSE;\r
+ Status = FindVariableEx (VariableName, VendorGuid, FALSE, CacheVariable);\r
+ if (CacheVariable->CurrPtr == NULL || EFI_ERROR (Status)) {\r
+ //\r
+ // There is no matched variable in NV variable cache.\r
+ //\r
+ if ((((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) && (DataSize == 0)) || (Attributes == 0)) {\r
+ //\r
+ // It is to delete variable,\r
+ // go to delete this variable in variable HOB and\r
+ // try to flush other variables from HOB to flash.\r
+ //\r
+ UpdateVariableInfo (VariableName, VendorGuid, FALSE, FALSE, FALSE, TRUE, FALSE);\r
+ FlushHobVariableToFlash (VariableName, VendorGuid);\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+ }\r
+\r
+ if ((CacheVariable->CurrPtr == NULL) || CacheVariable->Volatile) {\r
+ Variable = CacheVariable;\r
+ } else {\r
+ //\r
+ // Update/Delete existing NV variable.\r
+ // CacheVariable points to the variable in the memory copy of Flash area\r
+ // Now let Variable points to the same variable in Flash area.\r
+ //\r
+ VariableStoreHeader = (VARIABLE_STORE_HEADER *) ((UINTN) mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase);\r
+ Variable = &NvVariable;\r
+ Variable->StartPtr = GetStartPointer (VariableStoreHeader);\r
+ Variable->EndPtr = (VARIABLE_HEADER *)((UINTN)Variable->StartPtr + ((UINTN)CacheVariable->EndPtr - (UINTN)CacheVariable->StartPtr));\r
+\r
+ Variable->CurrPtr = (VARIABLE_HEADER *)((UINTN)Variable->StartPtr + ((UINTN)CacheVariable->CurrPtr - (UINTN)CacheVariable->StartPtr));\r
+ if (CacheVariable->InDeletedTransitionPtr != NULL) {\r
+ Variable->InDeletedTransitionPtr = (VARIABLE_HEADER *)((UINTN)Variable->StartPtr + ((UINTN)CacheVariable->InDeletedTransitionPtr - (UINTN)CacheVariable->StartPtr));\r
+ } else {\r
+ Variable->InDeletedTransitionPtr = NULL;\r
+ }\r
+ Variable->Volatile = FALSE;\r
+ }\r
+\r
+ Fvb = mVariableModuleGlobal->FvbInstance;\r
+\r
+ //\r
+ // Tricky part: Use scratch data area at the end of volatile variable store\r
+ // as a temporary storage.\r
+ //\r
+ NextVariable = GetEndPointer ((VARIABLE_STORE_HEADER *) ((UINTN) mVariableModuleGlobal->VariableGlobal.VolatileVariableBase));\r
+ ScratchSize = mVariableModuleGlobal->ScratchBufferSize;\r
+ SetMem (NextVariable, ScratchSize, 0xff);\r
+ DataReady = FALSE;\r
+\r
+ if (Variable->CurrPtr != NULL) {\r
+ //\r
+ // Update/Delete existing variable.\r
+ //\r
+ if (AtRuntime ()) {\r
+ //\r
+ // If AtRuntime and the variable is Volatile and Runtime Access,\r
+ // the volatile is ReadOnly, and SetVariable should be aborted and\r
+ // return EFI_WRITE_PROTECTED.\r
+ //\r
+ if (Variable->Volatile) {\r
+ Status = EFI_WRITE_PROTECTED;\r
+ goto Done;\r
+ }\r
+ //\r
+ // Only variable that have NV attributes can be updated/deleted in Runtime.\r
+ //\r
+ if ((CacheVariable->CurrPtr->Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
+ }\r
+\r
+ //\r
+ // Only variable that have RT attributes can be updated/deleted in Runtime.\r
+ //\r
+ if ((CacheVariable->CurrPtr->Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Setting a data variable with no access, or zero DataSize attributes\r
+ // causes it to be deleted.\r
+ // When the EFI_VARIABLE_APPEND_WRITE attribute is set, DataSize of zero will\r
+ // not delete the variable.\r
+ //\r
+ if ((((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) && (DataSize == 0))|| ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0)) {\r
+ if (Variable->InDeletedTransitionPtr != NULL) {\r
+ //\r
+ // Both ADDED and IN_DELETED_TRANSITION variable are present,\r
+ // set IN_DELETED_TRANSITION one to DELETED state first.\r
+ //\r
+ ASSERT (CacheVariable->InDeletedTransitionPtr != NULL);\r
+ State = CacheVariable->InDeletedTransitionPtr->State;\r
+ State &= VAR_DELETED;\r
+ Status = UpdateVariableStore (\r
+ &mVariableModuleGlobal->VariableGlobal,\r
+ Variable->Volatile,\r
+ FALSE,\r
+ Fvb,\r
+ (UINTN) &Variable->InDeletedTransitionPtr->State,\r
+ sizeof (UINT8),\r
+ &State\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ if (!Variable->Volatile) {\r
+ CacheVariable->InDeletedTransitionPtr->State = State;\r
+ }\r
+ } else {\r
+ goto Done;\r
+ }\r
+ }\r
+\r
+ State = CacheVariable->CurrPtr->State;\r
+ State &= VAR_DELETED;\r
+\r
+ Status = UpdateVariableStore (\r
+ &mVariableModuleGlobal->VariableGlobal,\r
+ Variable->Volatile,\r
+ FALSE,\r
+ Fvb,\r
+ (UINTN) &Variable->CurrPtr->State,\r
+ sizeof (UINT8),\r
+ &State\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ UpdateVariableInfo (VariableName, VendorGuid, Variable->Volatile, FALSE, FALSE, TRUE, FALSE);\r
+ if (!Variable->Volatile) {\r
+ CacheVariable->CurrPtr->State = State;\r
+ FlushHobVariableToFlash (VariableName, VendorGuid);\r
+ }\r
+ }\r
+ goto Done;\r
+ }\r
+ //\r
+ // If the variable is marked valid, and the same data has been passed in,\r
+ // then return to the caller immediately.\r
+ //\r
+ if (DataSizeOfVariable (CacheVariable->CurrPtr) == DataSize &&\r
+ (CompareMem (Data, GetVariableDataPtr (CacheVariable->CurrPtr), DataSize) == 0) &&\r
+ ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) &&\r
+ (TimeStamp == NULL)) {\r
+ //\r
+ // Variable content unchanged and no need to update timestamp, just return.\r
+ //\r
+ UpdateVariableInfo (VariableName, VendorGuid, Variable->Volatile, FALSE, TRUE, FALSE, FALSE);\r
+ Status = EFI_SUCCESS;\r
+ goto Done;\r
+ } else if ((CacheVariable->CurrPtr->State == VAR_ADDED) ||\r
+ (CacheVariable->CurrPtr->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION))) {\r
+\r
+ //\r
+ // EFI_VARIABLE_APPEND_WRITE attribute only effects for existing variable.\r
+ //\r
+ if ((Attributes & EFI_VARIABLE_APPEND_WRITE) != 0) {\r
+ //\r
+ // NOTE: From 0 to DataOffset of NextVariable is reserved for Variable Header and Name.\r
+ // From DataOffset of NextVariable is to save the existing variable data.\r
+ //\r
+ DataOffset = GetVariableDataOffset (CacheVariable->CurrPtr);\r
+ BufferForMerge = (UINT8 *) ((UINTN) NextVariable + DataOffset);\r
+ CopyMem (BufferForMerge, (UINT8 *) ((UINTN) CacheVariable->CurrPtr + DataOffset), DataSizeOfVariable (CacheVariable->CurrPtr));\r
+\r
+ //\r
+ // Set Max Common/Auth Variable Data Size as default MaxDataSize.\r
+ //\r
+ if ((Attributes & VARIABLE_ATTRIBUTE_AT_AW) != 0) {\r
+ MaxDataSize = mVariableModuleGlobal->MaxAuthVariableSize - DataOffset;\r
+ } else {\r
+ MaxDataSize = mVariableModuleGlobal->MaxVariableSize - DataOffset;\r
+ }\r
+\r
+ //\r
+ // Append the new data to the end of existing data.\r
+ // Max Harware error record variable data size is different from common/auth variable.\r
+ //\r
+ if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) {\r
+ MaxDataSize = PcdGet32 (PcdMaxHardwareErrorVariableSize) - DataOffset;\r
+ }\r
+\r
+ if (DataSizeOfVariable (CacheVariable->CurrPtr) + DataSize > MaxDataSize) {\r
+ //\r
+ // Existing data size + new data size exceed maximum variable size limitation.\r
+ //\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
+ }\r
+ CopyMem ((UINT8*) ((UINTN) BufferForMerge + DataSizeOfVariable (CacheVariable->CurrPtr)), Data, DataSize);\r
+ MergedBufSize = DataSizeOfVariable (CacheVariable->CurrPtr) + DataSize;\r
+\r
+ //\r
+ // BufferForMerge(from DataOffset of NextVariable) has included the merged existing and new data.\r
+ //\r
+ Data = BufferForMerge;\r
+ DataSize = MergedBufSize;\r
+ DataReady = TRUE;\r
+ }\r
+\r
+ //\r
+ // Mark the old variable as in delete transition.\r
+ //\r
+ State = CacheVariable->CurrPtr->State;\r
+ State &= VAR_IN_DELETED_TRANSITION;\r
+\r
+ Status = UpdateVariableStore (\r
+ &mVariableModuleGlobal->VariableGlobal,\r
+ Variable->Volatile,\r
+ FALSE,\r
+ Fvb,\r
+ (UINTN) &Variable->CurrPtr->State,\r
+ sizeof (UINT8),\r
+ &State\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+ if (!Variable->Volatile) {\r
+ CacheVariable->CurrPtr->State = State;\r
+ }\r
+ }\r
+ } else {\r
+ //\r
+ // Not found existing variable. Create a new variable.\r
+ //\r
+\r
+ if ((DataSize == 0) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) != 0)) {\r
+ Status = EFI_SUCCESS;\r
+ goto Done;\r
+ }\r
+\r
+ //\r
+ // Make sure we are trying to create a new variable.\r
+ // Setting a data variable with zero DataSize or no access attributes means to delete it.\r
+ //\r
+ if (DataSize == 0 || (Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0) {\r
+ Status = EFI_NOT_FOUND;\r
+ goto Done;\r
+ }\r
+\r
+ //\r
+ // Only variable have NV|RT attribute can be created in Runtime.\r
+ //\r
+ if (AtRuntime () &&\r
+ (((Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0) || ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0))) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Function part - create a new variable and copy the data.\r
+ // Both update a variable and create a variable will come here.\r
+ //\r
+ NextVariable->StartId = VARIABLE_DATA;\r
+ //\r
+ // NextVariable->State = VAR_ADDED;\r
+ //\r
+ NextVariable->Reserved = 0;\r
+ if (mVariableModuleGlobal->VariableGlobal.AuthFormat) {\r
+ AuthVariable = (AUTHENTICATED_VARIABLE_HEADER *) NextVariable;\r
+ AuthVariable->PubKeyIndex = KeyIndex;\r
+ AuthVariable->MonotonicCount = MonotonicCount;\r
+ ZeroMem (&AuthVariable->TimeStamp, sizeof (EFI_TIME));\r
+\r
+ if (((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) &&\r
+ (TimeStamp != NULL)) {\r
+ if ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) {\r
+ CopyMem (&AuthVariable->TimeStamp, TimeStamp, sizeof (EFI_TIME));\r
+ } else {\r
+ //\r
+ // In the case when the EFI_VARIABLE_APPEND_WRITE attribute is set, only\r
+ // when the new TimeStamp value is later than the current timestamp associated\r
+ // with the variable, we need associate the new timestamp with the updated value.\r
+ //\r
+ if (Variable->CurrPtr != NULL) {\r
+ if (VariableCompareTimeStampInternal (&(((AUTHENTICATED_VARIABLE_HEADER *) CacheVariable->CurrPtr)->TimeStamp), TimeStamp)) {\r
+ CopyMem (&AuthVariable->TimeStamp, TimeStamp, sizeof (EFI_TIME));\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // The EFI_VARIABLE_APPEND_WRITE attribute will never be set in the returned\r
+ // Attributes bitmask parameter of a GetVariable() call.\r
+ //\r
+ NextVariable->Attributes = Attributes & (~EFI_VARIABLE_APPEND_WRITE);\r
+\r
+ VarNameOffset = GetVariableHeaderSize ();\r
+ VarNameSize = StrSize (VariableName);\r
+ CopyMem (\r
+ (UINT8 *) ((UINTN) NextVariable + VarNameOffset),\r
+ VariableName,\r
+ VarNameSize\r
+ );\r
+ VarDataOffset = VarNameOffset + VarNameSize + GET_PAD_SIZE (VarNameSize);\r
+\r
+ //\r
+ // If DataReady is TRUE, it means the variable data has been saved into\r
+ // NextVariable during EFI_VARIABLE_APPEND_WRITE operation preparation.\r
+ //\r
+ if (!DataReady) {\r
+ CopyMem (\r
+ (UINT8 *) ((UINTN) NextVariable + VarDataOffset),\r
+ Data,\r
+ DataSize\r
+ );\r
+ }\r
+\r
+ CopyMem (GetVendorGuidPtr (NextVariable), VendorGuid, sizeof (EFI_GUID));\r
+ //\r
+ // There will be pad bytes after Data, the NextVariable->NameSize and\r
+ // NextVariable->DataSize should not include pad size so that variable\r
+ // service can get actual size in GetVariable.\r
+ //\r
+ SetNameSizeOfVariable (NextVariable, VarNameSize);\r
+ SetDataSizeOfVariable (NextVariable, DataSize);\r
+\r
+ //\r
+ // The actual size of the variable that stores in storage should\r
+ // include pad size.\r
+ //\r
+ VarSize = VarDataOffset + DataSize + GET_PAD_SIZE (DataSize);\r
+ if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) {\r
+ //\r
+ // Create a nonvolatile variable.\r
+ //\r
+ Volatile = FALSE;\r
+\r
+ IsCommonVariable = FALSE;\r
+ IsCommonUserVariable = FALSE;\r
+ if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == 0) {\r
+ IsCommonVariable = TRUE;\r
+ IsCommonUserVariable = IsUserVariable (NextVariable);\r
+ }\r
+ if ((((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0)\r
+ && ((VarSize + mVariableModuleGlobal->HwErrVariableTotalSize) > PcdGet32 (PcdHwErrStorageSize)))\r
+ || (IsCommonVariable && ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > mVariableModuleGlobal->CommonVariableSpace))\r
+ || (IsCommonVariable && AtRuntime () && ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > mVariableModuleGlobal->CommonRuntimeVariableSpace))\r
+ || (IsCommonUserVariable && ((VarSize + mVariableModuleGlobal->CommonUserVariableTotalSize) > mVariableModuleGlobal->CommonMaxUserVariableSpace))) {\r
+ if (AtRuntime ()) {\r
+ if (IsCommonUserVariable && ((VarSize + mVariableModuleGlobal->CommonUserVariableTotalSize) > mVariableModuleGlobal->CommonMaxUserVariableSpace)) {\r
+ RecordVarErrorFlag (VAR_ERROR_FLAG_USER_ERROR, VariableName, VendorGuid, Attributes, VarSize);\r
+ }\r
+ if (IsCommonVariable && ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > mVariableModuleGlobal->CommonRuntimeVariableSpace)) {\r
+ RecordVarErrorFlag (VAR_ERROR_FLAG_SYSTEM_ERROR, VariableName, VendorGuid, Attributes, VarSize);\r
+ }\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Done;\r
+ }\r
+ //\r
+ // Perform garbage collection & reclaim operation, and integrate the new variable at the same time.\r
+ //\r
+ Status = Reclaim (\r
+ mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase,\r
+ &mVariableModuleGlobal->NonVolatileLastVariableOffset,\r
+ FALSE,\r
+ Variable,\r
+ NextVariable,\r
+ HEADER_ALIGN (VarSize)\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // The new variable has been integrated successfully during reclaiming.\r
+ //\r
+ if (Variable->CurrPtr != NULL) {\r
+ CacheVariable->CurrPtr = (VARIABLE_HEADER *)((UINTN) CacheVariable->StartPtr + ((UINTN) Variable->CurrPtr - (UINTN) Variable->StartPtr));\r
+ CacheVariable->InDeletedTransitionPtr = NULL;\r
+ }\r
+ UpdateVariableInfo (VariableName, VendorGuid, FALSE, FALSE, TRUE, FALSE, FALSE);\r
+ FlushHobVariableToFlash (VariableName, VendorGuid);\r
+ } else {\r
+ if (IsCommonUserVariable && ((VarSize + mVariableModuleGlobal->CommonUserVariableTotalSize) > mVariableModuleGlobal->CommonMaxUserVariableSpace)) {\r
+ RecordVarErrorFlag (VAR_ERROR_FLAG_USER_ERROR, VariableName, VendorGuid, Attributes, VarSize);\r
+ }\r
+ if (IsCommonVariable && ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > mVariableModuleGlobal->CommonVariableSpace)) {\r
+ RecordVarErrorFlag (VAR_ERROR_FLAG_SYSTEM_ERROR, VariableName, VendorGuid, Attributes, VarSize);\r
+ }\r
+ }\r
+ goto Done;\r
+ }\r
+ //\r
+ // Four steps\r
+ // 1. Write variable header\r
+ // 2. Set variable state to header valid\r
+ // 3. Write variable data\r
+ // 4. Set variable state to valid\r
+ //\r
+ //\r
+ // Step 1:\r
+ //\r
+ CacheOffset = mVariableModuleGlobal->NonVolatileLastVariableOffset;\r
+ Status = UpdateVariableStore (\r
+ &mVariableModuleGlobal->VariableGlobal,\r
+ FALSE,\r
+ TRUE,\r
+ Fvb,\r
+ mVariableModuleGlobal->NonVolatileLastVariableOffset,\r
+ (UINT32) GetVariableHeaderSize (),\r
+ (UINT8 *) NextVariable\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+\r
+ //\r
+ // Step 2:\r
+ //\r
+ NextVariable->State = VAR_HEADER_VALID_ONLY;\r
+ Status = UpdateVariableStore (\r
+ &mVariableModuleGlobal->VariableGlobal,\r
+ FALSE,\r
+ TRUE,\r
+ Fvb,\r
+ mVariableModuleGlobal->NonVolatileLastVariableOffset + OFFSET_OF (VARIABLE_HEADER, State),\r
+ sizeof (UINT8),\r
+ &NextVariable->State\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+ //\r
+ // Step 3:\r
+ //\r
+ Status = UpdateVariableStore (\r
+ &mVariableModuleGlobal->VariableGlobal,\r
+ FALSE,\r
+ TRUE,\r
+ Fvb,\r
+ mVariableModuleGlobal->NonVolatileLastVariableOffset + GetVariableHeaderSize (),\r
+ (UINT32) (VarSize - GetVariableHeaderSize ()),\r
+ (UINT8 *) NextVariable + GetVariableHeaderSize ()\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+ //\r
+ // Step 4:\r
+ //\r
+ NextVariable->State = VAR_ADDED;\r
+ Status = UpdateVariableStore (\r
+ &mVariableModuleGlobal->VariableGlobal,\r
+ FALSE,\r
+ TRUE,\r
+ Fvb,\r
+ mVariableModuleGlobal->NonVolatileLastVariableOffset + OFFSET_OF (VARIABLE_HEADER, State),\r
+ sizeof (UINT8),\r
+ &NextVariable->State\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+\r
+ mVariableModuleGlobal->NonVolatileLastVariableOffset += HEADER_ALIGN (VarSize);\r
+\r
+ if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0) {\r
+ mVariableModuleGlobal->HwErrVariableTotalSize += HEADER_ALIGN (VarSize);\r
+ } else {\r
+ mVariableModuleGlobal->CommonVariableTotalSize += HEADER_ALIGN (VarSize);\r
+ if (IsCommonUserVariable) {\r
+ mVariableModuleGlobal->CommonUserVariableTotalSize += HEADER_ALIGN (VarSize);\r
+ }\r
+ }\r
+ //\r
+ // update the memory copy of Flash region.\r
+ //\r
+ CopyMem ((UINT8 *)mNvVariableCache + CacheOffset, (UINT8 *)NextVariable, VarSize);\r
+ } else {\r
+ //\r
+ // Create a volatile variable.\r
+ //\r
+ Volatile = TRUE;\r
+\r
+ if ((UINT32) (VarSize + mVariableModuleGlobal->VolatileLastVariableOffset) >\r
+ ((VARIABLE_STORE_HEADER *) ((UINTN) (mVariableModuleGlobal->VariableGlobal.VolatileVariableBase)))->Size) {\r
+ //\r
+ // Perform garbage collection & reclaim operation, and integrate the new variable at the same time.\r
+ //\r
+ Status = Reclaim (\r
+ mVariableModuleGlobal->VariableGlobal.VolatileVariableBase,\r
+ &mVariableModuleGlobal->VolatileLastVariableOffset,\r
+ TRUE,\r
+ Variable,\r
+ NextVariable,\r
+ HEADER_ALIGN (VarSize)\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // The new variable has been integrated successfully during reclaiming.\r
+ //\r
+ if (Variable->CurrPtr != NULL) {\r
+ CacheVariable->CurrPtr = (VARIABLE_HEADER *)((UINTN) CacheVariable->StartPtr + ((UINTN) Variable->CurrPtr - (UINTN) Variable->StartPtr));\r
+ CacheVariable->InDeletedTransitionPtr = NULL;\r
+ }\r
+ UpdateVariableInfo (VariableName, VendorGuid, TRUE, FALSE, TRUE, FALSE, FALSE);\r
+ }\r
+ goto Done;\r
+ }\r
+\r
+ NextVariable->State = VAR_ADDED;\r
+ Status = UpdateVariableStore (\r
+ &mVariableModuleGlobal->VariableGlobal,\r
+ TRUE,\r
+ TRUE,\r
+ Fvb,\r
+ mVariableModuleGlobal->VolatileLastVariableOffset,\r
+ (UINT32) VarSize,\r
+ (UINT8 *) NextVariable\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+\r
+ mVariableModuleGlobal->VolatileLastVariableOffset += HEADER_ALIGN (VarSize);\r
+ }\r
+\r
+ //\r
+ // Mark the old variable as deleted.\r
+ //\r
+ if (!EFI_ERROR (Status) && Variable->CurrPtr != NULL) {\r
+ if (Variable->InDeletedTransitionPtr != NULL) {\r
+ //\r
+ // Both ADDED and IN_DELETED_TRANSITION old variable are present,\r
+ // set IN_DELETED_TRANSITION one to DELETED state first.\r
+ //\r
+ ASSERT (CacheVariable->InDeletedTransitionPtr != NULL);\r
+ State = CacheVariable->InDeletedTransitionPtr->State;\r
+ State &= VAR_DELETED;\r
+ Status = UpdateVariableStore (\r
+ &mVariableModuleGlobal->VariableGlobal,\r
+ Variable->Volatile,\r
+ FALSE,\r
+ Fvb,\r
+ (UINTN) &Variable->InDeletedTransitionPtr->State,\r
+ sizeof (UINT8),\r
+ &State\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ if (!Variable->Volatile) {\r
+ CacheVariable->InDeletedTransitionPtr->State = State;\r
+ }\r