+ SubIndex = 0;\r
+ Supported = SupportedLang;\r
+ if (Iso639Language) {\r
+ //\r
+ // According to the index of Lang string in SupportedLang string to get the language.\r
+ // This code will be invoked in RUNTIME, therefore there is not a memory allocate/free operation.\r
+ // In driver entry, it pre-allocates a runtime attribute memory to accommodate this string.\r
+ //\r
+ CompareLength = ISO_639_2_ENTRY_SIZE;\r
+ mVariableModuleGlobal->Lang[CompareLength] = '\0';\r
+ return CopyMem (mVariableModuleGlobal->Lang, SupportedLang + Index * CompareLength, CompareLength);\r
+ \r
+ } else {\r
+ while (TRUE) {\r
+ //\r
+ // Take semicolon as delimitation, sequentially traverse supported language codes.\r
+ //\r
+ for (CompareLength = 0; *Supported != ';' && *Supported != '\0'; CompareLength++) {\r
+ Supported++;\r
+ }\r
+ if ((*Supported == '\0') && (SubIndex != Index)) {\r
+ //\r
+ // Have completed the traverse, but not find corrsponding string.\r
+ // This case is not allowed to happen.\r
+ //\r
+ ASSERT(FALSE);\r
+ return NULL;\r
+ }\r
+ if (SubIndex == Index) {\r
+ //\r
+ // According to the index of Lang string in SupportedLang string to get the language.\r
+ // As this code will be invoked in RUNTIME, therefore there is not memory allocate/free operation.\r
+ // In driver entry, it pre-allocates a runtime attribute memory to accommodate this string.\r
+ //\r
+ mVariableModuleGlobal->PlatformLang[CompareLength] = '\0';\r
+ return CopyMem (mVariableModuleGlobal->PlatformLang, Supported - CompareLength, CompareLength);\r
+ }\r
+ SubIndex++;\r
+\r
+ //\r
+ // Skip ';' characters in Supported\r
+ //\r
+ for (; *Supported != '\0' && *Supported == ';'; Supported++);\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ Returns a pointer to an allocated buffer that contains the best matching language \r
+ from a set of supported languages. \r
+ \r
+ This function supports both ISO 639-2 and RFC 4646 language codes, but language \r
+ code types may not be mixed in a single call to this function. This function\r
+ supports a variable argument list that allows the caller to pass in a prioritized\r
+ list of language codes to test against all the language codes in SupportedLanguages.\r
+\r
+ If SupportedLanguages is NULL, then ASSERT().\r
+\r
+ @param[in] SupportedLanguages A pointer to a Null-terminated ASCII string that\r
+ contains a set of language codes in the format \r
+ specified by Iso639Language.\r
+ @param[in] Iso639Language If TRUE, then all language codes are assumed to be\r
+ in ISO 639-2 format. If FALSE, then all language\r
+ codes are assumed to be in RFC 4646 language format\r
+ @param[in] ... A variable argument list that contains pointers to \r
+ Null-terminated ASCII strings that contain one or more\r
+ language codes in the format specified by Iso639Language.\r
+ The first language code from each of these language\r
+ code lists is used to determine if it is an exact or\r
+ close match to any of the language codes in \r
+ SupportedLanguages. Close matches only apply to RFC 4646\r
+ language codes, and the matching algorithm from RFC 4647\r
+ is used to determine if a close match is present. If \r
+ an exact or close match is found, then the matching\r
+ language code from SupportedLanguages is returned. If\r
+ no matches are found, then the next variable argument\r
+ parameter is evaluated. The variable argument list \r
+ is terminated by a NULL.\r
+\r
+ @retval NULL The best matching language could not be found in SupportedLanguages.\r
+ @retval NULL There are not enough resources available to return the best matching \r
+ language.\r
+ @retval Other A pointer to a Null-terminated ASCII string that is the best matching \r
+ language in SupportedLanguages.\r
+\r
+**/\r
+CHAR8 *\r
+EFIAPI\r
+VariableGetBestLanguage (\r
+ IN CONST CHAR8 *SupportedLanguages, \r
+ IN BOOLEAN Iso639Language,\r
+ ...\r
+ )\r
+{\r
+ VA_LIST Args;\r
+ CHAR8 *Language;\r
+ UINTN CompareLength;\r
+ UINTN LanguageLength;\r
+ CONST CHAR8 *Supported;\r
+ CHAR8 *Buffer;\r
+\r
+ ASSERT (SupportedLanguages != NULL);\r
+\r
+ VA_START (Args, Iso639Language);\r
+ while ((Language = VA_ARG (Args, CHAR8 *)) != NULL) {\r
+ //\r
+ // Default to ISO 639-2 mode\r
+ //\r
+ CompareLength = 3;\r
+ LanguageLength = MIN (3, AsciiStrLen (Language));\r
+\r
+ //\r
+ // If in RFC 4646 mode, then determine the length of the first RFC 4646 language code in Language\r
+ //\r
+ if (!Iso639Language) {\r
+ for (LanguageLength = 0; Language[LanguageLength] != 0 && Language[LanguageLength] != ';'; LanguageLength++);\r
+ }\r
+\r
+ //\r
+ // Trim back the length of Language used until it is empty\r
+ //\r
+ 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
+ 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
+**/\r
+VOID\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
+\r
+ //\r
+ // Don't do updates for delete operation\r
+ //\r
+ if (DataSize == 0) {\r
+ return;\r
+ }\r
+\r
+ SetLanguageCodes = FALSE;\r
+\r
+ if (StrCmp (VariableName, L"PlatformLangCodes") == 0) {\r
+ //\r
+ // PlatformLangCodes is a volatile variable, so it can not be updated at runtime.\r
+ //\r
+ if (AtRuntime ()) {\r
+ return;\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, L"LangCodes") == 0) {\r
+ //\r
+ // LangCodes is a volatile variable, so it can not be updated at runtime.\r
+ //\r
+ if (AtRuntime ()) {\r
+ return;\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 (L"PlatformLang", &gEfiGlobalVariableGuid, &Variable, (VARIABLE_GLOBAL *) mVariableModuleGlobal);\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // Update Lang\r
+ //\r
+ VariableName = L"PlatformLang";\r
+ Data = GetVariableDataPtr (Variable.CurrPtr);\r
+ DataSize = Variable.CurrPtr->DataSize;\r
+ } else {\r
+ Status = FindVariable (L"Lang", &gEfiGlobalVariableGuid, &Variable, (VARIABLE_GLOBAL *) mVariableModuleGlobal);\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // Update PlatformLang\r
+ //\r
+ VariableName = L"Lang";\r
+ Data = GetVariableDataPtr (Variable.CurrPtr);\r
+ DataSize = Variable.CurrPtr->DataSize;\r
+ } else {\r
+ //\r
+ // Neither PlatformLang nor Lang is set, directly return\r
+ //\r
+ return;\r
+ }\r
+ }\r
+ }\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, L"PlatformLang") == 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
+ // Successfully convert PlatformLang to Lang, and set the BestLang value into Lang variable simultaneously.\r
+ //\r
+ FindVariable (L"Lang", &gEfiGlobalVariableGuid, &Variable, (VARIABLE_GLOBAL *)mVariableModuleGlobal);\r
+\r
+ Status = UpdateVariable (L"Lang", &gEfiGlobalVariableGuid, BestLang,\r
+ ISO_639_2_ENTRY_SIZE + 1, Attributes, &Variable);\r
+\r
+ DEBUG ((EFI_D_INFO, "Variable Driver Auto Update PlatformLang, PlatformLang:%a, Lang:%a\n", BestPlatformLang, BestLang));\r
+\r
+ ASSERT_EFI_ERROR(Status);\r
+ }\r
+ }\r
+\r
+ } else if (StrCmp (VariableName, L"Lang") == 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
+ // Successfully convert Lang to PlatformLang, and set the BestPlatformLang value into PlatformLang variable simultaneously.\r
+ //\r
+ FindVariable (L"PlatformLang", &gEfiGlobalVariableGuid, &Variable, (VARIABLE_GLOBAL *)mVariableModuleGlobal);\r
+\r
+ Status = UpdateVariable (L"PlatformLang", &gEfiGlobalVariableGuid, BestPlatformLang, \r
+ AsciiStrSize (BestPlatformLang), Attributes, &Variable);\r
+\r
+ DEBUG ((EFI_D_INFO, "Variable Driver Auto Update Lang, Lang:%a, PlatformLang:%a\n", BestLang, BestPlatformLang));\r
+ ASSERT_EFI_ERROR (Status);\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ Update the variable region with Variable information. These are the same \r
+ arguments as the EFI Variable services.\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 Attribues of the variable.\r
+ @param[in] CacheVariable The variable information which is used to keep track of variable usage.\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 VARIABLE_POINTER_TRACK *CacheVariable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ VARIABLE_HEADER *NextVariable;\r
+ UINTN ScratchSize;\r
+ UINTN NonVolatileVarableStoreSize;\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
+ BOOLEAN Reclaimed;\r
+ VARIABLE_POINTER_TRACK *Variable;\r
+ VARIABLE_POINTER_TRACK NvVariable;\r
+ VARIABLE_STORE_HEADER *VariableStoreHeader;\r
+ UINTN CacheOffset;\r
+\r
+ if ((mVariableModuleGlobal->FvbInstance == NULL) && ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0)) {\r
+ //\r
+ // The FVB protocol is not ready. Trying to update NV variable prior to the installation\r
+ // of EFI_VARIABLE_WRITE_ARCH_PROTOCOL.\r
+ //\r
+ return EFI_NOT_AVAILABLE_YET; \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 = GetEndPointer (VariableStoreHeader);\r
+ Variable->CurrPtr = (VARIABLE_HEADER *)((UINTN)Variable->StartPtr + ((UINTN)CacheVariable->CurrPtr - (UINTN)CacheVariable->StartPtr));\r
+ Variable->Volatile = FALSE;\r
+ } \r
+\r
+ Fvb = mVariableModuleGlobal->FvbInstance;\r
+ Reclaimed = 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 ((Variable->CurrPtr->Attributes & EFI_VARIABLE_NON_VOLATILE) == 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
+ //\r
+ if (DataSize == 0 || (Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0) { \r
+ State = Variable->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
+ }\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 (Variable->CurrPtr) == DataSize &&\r
+ (CompareMem (Data, GetVariableDataPtr (Variable->CurrPtr), DataSize) == 0)) {\r
+ \r
+ UpdateVariableInfo (VariableName, VendorGuid, Variable->Volatile, FALSE, TRUE, FALSE, FALSE);\r
+ Status = EFI_SUCCESS;\r
+ goto Done;\r
+ } else if ((Variable->CurrPtr->State == VAR_ADDED) ||\r
+ (Variable->CurrPtr->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION))) {\r
+\r
+ //\r
+ // Mark the old variable as in delete transition.\r
+ //\r
+ State = Variable->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
+ //\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
+ //\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 = MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32 (PcdMaxHardwareErrorVariableSize));\r
+\r
+ SetMem (NextVariable, ScratchSize, 0xff);\r
+\r
+ NextVariable->StartId = VARIABLE_DATA;\r
+ NextVariable->Attributes = Attributes;\r
+ //\r
+ // NextVariable->State = VAR_ADDED;\r
+ //\r
+ NextVariable->Reserved = 0;\r
+ VarNameOffset = sizeof (VARIABLE_HEADER);\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
+ CopyMem (\r
+ (UINT8 *) ((UINTN) NextVariable + VarDataOffset),\r
+ Data,\r
+ DataSize\r
+ );\r
+ CopyMem (&NextVariable->VendorGuid, 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
+ NextVariable->NameSize = (UINT32)VarNameSize;\r
+ NextVariable->DataSize = (UINT32)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
+ NonVolatileVarableStoreSize = ((VARIABLE_STORE_HEADER *)(UINTN)(mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase))->Size;\r
+ if ((((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0) \r
+ && ((VarSize + mVariableModuleGlobal->HwErrVariableTotalSize) > PcdGet32 (PcdHwErrStorageSize)))\r
+ || (((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == 0) \r
+ && ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > NonVolatileVarableStoreSize - sizeof (VARIABLE_STORE_HEADER) - PcdGet32 (PcdHwErrStorageSize)))) {\r
+ if (AtRuntime ()) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Done;\r
+ }\r
+ //\r
+ // Perform garbage collection & reclaim operation.\r
+ //\r
+ Status = Reclaim (mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase, \r
+ &mVariableModuleGlobal->NonVolatileLastVariableOffset, FALSE, Variable->CurrPtr);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+ //\r
+ // If still no enough space, return out of resources.\r
+ //\r
+ if ((((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0) \r
+ && ((VarSize + mVariableModuleGlobal->HwErrVariableTotalSize) > PcdGet32 (PcdHwErrStorageSize)))\r
+ || (((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == 0) \r
+ && ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > NonVolatileVarableStoreSize - sizeof (VARIABLE_STORE_HEADER) - PcdGet32 (PcdHwErrStorageSize)))) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Done;\r
+ }\r
+ Reclaimed = TRUE;\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
+ sizeof (VARIABLE_HEADER),\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 + sizeof (VARIABLE_HEADER),\r
+ (UINT32) VarSize - sizeof (VARIABLE_HEADER),\r
+ (UINT8 *) NextVariable + sizeof (VARIABLE_HEADER)\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
+ }\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.\r
+ //\r
+ Status = Reclaim (mVariableModuleGlobal->VariableGlobal.VolatileVariableBase, \r
+ &mVariableModuleGlobal->VolatileLastVariableOffset, TRUE, Variable->CurrPtr);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+ //\r
+ // If still no enough space, return out of resources.\r
+ //\r
+ if ((UINT32) (VarSize + mVariableModuleGlobal->VolatileLastVariableOffset) >\r
+ ((VARIABLE_STORE_HEADER *) ((UINTN) (mVariableModuleGlobal->VariableGlobal.VolatileVariableBase)))->Size\r
+ ) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Done;\r
+ }\r
+ Reclaimed = TRUE;\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 (!Reclaimed && !EFI_ERROR (Status) && Variable->CurrPtr != NULL) {\r
+ State = Variable->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) && !Variable->Volatile) { \r
+ CacheVariable->CurrPtr->State = State;\r
+ }\r
+ }\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ UpdateVariableInfo (VariableName, VendorGuid, Volatile, FALSE, TRUE, FALSE, FALSE);\r
+ }\r
+\r
+Done:\r
+ return Status;\r
+}\r
+\r
+/**\r
+\r
+ This code finds variable in storage blocks (Volatile or Non-Volatile).\r