+ // if VAR_IN_DELETED_TRANSITION found, and VAR_ADDED not found,\r
+ // we return it.\r
+ //\r
+ if (InDeleteVariable != NULL) {\r
+ PtrTrack->CurrPtr = InDeleteVariable;\r
+ PtrTrack->Type = (VARIABLE_STORAGE_TYPE) InDeleteIndex;\r
+ PtrTrack->StartPtr = InDeleteStartPtr;\r
+ PtrTrack->EndPtr = InDeleteEndPtr;\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ PtrTrack->CurrPtr = NULL;\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+ Get index from supported language codes according to language string.\r
+\r
+ This code is used to get corresponding index in supported language codes. It can handle\r
+ RFC4646 and ISO639 language tags.\r
+ In ISO639 language tags, take 3-characters as a delimitation to find matched string and calculate the index.\r
+ In RFC4646 language tags, take semicolon as a delimitation to find matched string and calculate the index.\r
+\r
+ For example:\r
+ SupportedLang = "engfraengfra"\r
+ Lang = "eng"\r
+ Iso639Language = TRUE\r
+ The return value is "0".\r
+ Another example:\r
+ SupportedLang = "en;fr;en-US;fr-FR"\r
+ Lang = "fr-FR"\r
+ Iso639Language = FALSE\r
+ The return value is "3".\r
+\r
+ @param SupportedLang Platform supported language codes.\r
+ @param Lang Configured language.\r
+ @param Iso639Language A bool value to signify if the handler is operated on ISO639 or RFC4646.\r
+\r
+ @retval the index of language in the language codes.\r
+\r
+**/\r
+UINTN\r
+EFIAPI\r
+GetIndexFromSupportedLangCodes(\r
+ IN CHAR8 *SupportedLang,\r
+ IN CHAR8 *Lang,\r
+ IN BOOLEAN Iso639Language\r
+ ) \r
+{\r
+ UINTN Index;\r
+ UINT32 CompareLength;\r
+ CHAR8 *Supported;\r
+\r
+ Index = 0;\r
+ Supported = SupportedLang;\r
+ if (Iso639Language) {\r
+ CompareLength = 3;\r
+ for (Index = 0; Index < AsciiStrLen (SupportedLang); Index += CompareLength) {\r
+ if (AsciiStrnCmp (Lang, SupportedLang + Index, CompareLength) == 0) {\r
+ //\r
+ // Successfully find the index of Lang string in SupportedLang string.\r
+ //\r
+ Index = Index / CompareLength;\r
+ return Index;\r
+ }\r
+ }\r
+ ASSERT (FALSE);\r
+ return 0;\r
+ } else {\r
+ //\r
+ // Compare RFC4646 language code\r
+ //\r
+ while (*Supported != '\0') {\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 (AsciiStrnCmp (Lang, Supported - CompareLength, CompareLength) == 0) {\r
+ //\r
+ // Successfully find the index of Lang string in SupportedLang string.\r
+ //\r
+ return Index;\r
+ }\r
+ Index++;\r
+ }\r
+ ASSERT (FALSE);\r
+ return 0;\r
+ }\r
+}\r
+\r
+/**\r
+ Get language string from supported language codes according to index.\r
+\r
+ This code is used to get corresponding language string in supported language codes. It can handle\r
+ RFC4646 and ISO639 language tags.\r
+ In ISO639 language tags, take 3-characters as a delimitation. Find language string according to the index.\r
+ In RFC4646 language tags, take semicolon as a delimitation. Find language string according to the index.\r
+\r
+ For example:\r
+ SupportedLang = "engfraengfra"\r
+ Index = "1"\r
+ Iso639Language = TRUE\r
+ The return value is "fra".\r
+ Another example:\r
+ SupportedLang = "en;fr;en-US;fr-FR"\r
+ Index = "1"\r
+ Iso639Language = FALSE\r
+ The return value is "fr".\r
+\r
+ @param SupportedLang Platform supported language codes.\r
+ @param Index the index in supported language codes.\r
+ @param Iso639Language A bool value to signify if the handler is operated on ISO639 or RFC4646.\r
+\r
+ @retval the language string in the language codes.\r
+\r
+**/\r
+CHAR8 *\r
+EFIAPI\r
+GetLangFromSupportedLangCodes (\r
+ IN CHAR8 *SupportedLang,\r
+ IN UINTN Index,\r
+ IN BOOLEAN Iso639Language\r
+)\r
+{\r
+ UINTN SubIndex;\r
+ UINT32 CompareLength;\r
+ CHAR8 *Supported;\r
+\r
+ 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
+ // 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
+ CompareLength = 3;\r
+ SetMem (mGlobal->Lang, sizeof(mGlobal->Lang), 0);\r
+ return CopyMem (mGlobal->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
+ SetMem (mGlobal->PlatformLang, sizeof (mGlobal->PlatformLang), 0);\r
+ return CopyMem (mGlobal->PlatformLang, Supported - CompareLength, CompareLength);\r
+ }\r
+ SubIndex++;\r
+ }\r
+ }\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 auto update operation is successful.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\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
+\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"PlatformLangCodes") == 0) {\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
+ AsciiStrnCpy (mGlobal->PlatformLangCodes, Data, DataSize);\r
+ } else if (StrCmp (VariableName, L"LangCodes") == 0) {\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
+ AsciiStrnCpy (mGlobal->LangCodes, Data, DataSize);\r
+ } else if ((StrCmp (VariableName, L"PlatformLang") == 0) && (DataSize != 0)) {\r
+ ASSERT (AsciiStrLen (mGlobal->PlatformLangCodes) != 0);\r
+\r
+ //\r
+ // When setting PlatformLang, firstly get most matched language string from supported language codes.\r
+ //\r
+ BestPlatformLang = GetBestLanguage(mGlobal->PlatformLangCodes, FALSE, Data, NULL);\r
+\r
+ //\r
+ // Get the corresponding index in language codes.\r
+ //\r
+ Index = GetIndexFromSupportedLangCodes(mGlobal->PlatformLangCodes, BestPlatformLang, FALSE);\r
+\r
+ //\r
+ // Get the corresponding ISO639 language tag according to RFC4646 language tag.\r
+ //\r
+ BestLang = GetLangFromSupportedLangCodes(mGlobal->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);\r
+\r
+ Status = UpdateVariable(L"Lang", &gEfiGlobalVariableGuid, \r
+ BestLang, 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
+ } else if ((StrCmp (VariableName, L"Lang") == 0) && (DataSize != 0)) {\r
+ ASSERT (AsciiStrLen (mGlobal->LangCodes) != 0);\r
+\r
+ //\r
+ // When setting Lang, firstly get most matched language string from supported language codes.\r
+ //\r
+ BestLang = GetBestLanguage(mGlobal->LangCodes, TRUE, Data, NULL);\r
+\r
+ //\r
+ // Get the corresponding index in language codes.\r
+ //\r
+ Index = GetIndexFromSupportedLangCodes(mGlobal->LangCodes, BestLang, TRUE);\r
+\r
+ //\r
+ // Get the corresponding RFC4646 language tag according to ISO639 language tag.\r
+ //\r
+ BestPlatformLang = GetLangFromSupportedLangCodes(mGlobal->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);\r
+\r
+ Status = UpdateVariable(L"PlatformLang", &gEfiGlobalVariableGuid, \r
+ BestPlatformLang, 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
+ return EFI_SUCCESS;\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
+\r
+ @param[in] VendorGuid Guid of variable\r
+\r
+ @param[in] Data Variable data\r
+\r
+ @param[in] DataSize Size of data. 0 means delete\r
+\r
+ @param[in] Attributes Attribues of the variable\r
+\r
+ @param[in] Variable The variable information which is used to keep track of variable usage.\r
+\r
+ @retval EFI_SUCCESS The update operation is success.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Variable region is full, can not write other data into this region.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\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 *Variable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ VARIABLE_HEADER *NextVariable;\r
+ UINTN VarNameOffset;\r
+ UINTN VarDataOffset;\r
+ UINTN VarNameSize;\r
+ UINTN VarSize;\r
+ UINT8 State;\r
+ BOOLEAN Reclaimed;\r
+ VARIABLE_STORAGE_TYPE StorageType;\r
+\r
+ Reclaimed = FALSE;\r
+\r
+ if (Variable->CurrPtr != NULL) { \r
+ //\r
+ // Update/Delete existing variable\r
+ //\r
+ \r
+ if (EfiAtRuntime ()) { \r
+ //\r
+ // If EfiAtRuntime 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->Type == Volatile) {\r
+ return EFI_WRITE_PROTECTED;\r
+ }\r
+ //\r
+ // Only variable have NV attribute can be updated/deleted in Runtime\r
+ //\r
+ if (!(Variable->CurrPtr->Attributes & EFI_VARIABLE_NON_VOLATILE)) {\r
+ return EFI_INVALID_PARAMETER; \r
+ }\r
+ }\r
+ \r
+ //\r
+ // Setting a data variable with no access, or zero DataSize attributes\r
+ // specified causes it to be deleted.\r
+ //\r
+ if (DataSize == 0 || (Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0) {\r
+ //\r
+ // Found this variable in storage\r
+ //\r
+ State = Variable->CurrPtr->State;\r
+ State &= VAR_DELETED;\r
+\r
+ Status = mGlobal->VariableStore[Variable->Type]->Write (\r
+ mGlobal->VariableStore[Variable->Type],\r
+ VARIABLE_MEMBER_OFFSET (State, (UINTN) Variable->CurrPtr - (UINTN) Variable->StartPtr),\r
+ sizeof (Variable->CurrPtr->State),\r
+ &State\r
+ );\r
+ //\r
+ // NOTE: Write operation at least can write data to memory cache\r
+ // Discard file writing failure here.\r
+ //\r
+ return EFI_SUCCESS;\r
+ }\r
+ \r
+ //\r
+ // Found this variable in storage\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 ((Variable->CurrPtr->DataSize == DataSize) &&\r
+ (CompareMem (Data, GetVariableDataPtr (Variable->CurrPtr), DataSize) == 0)\r
+ ) {\r
+ return EFI_SUCCESS;\r
+ } else if ((Variable->CurrPtr->State == VAR_ADDED) ||\r
+ (Variable->CurrPtr->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION))) {\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 = mGlobal->VariableStore[Variable->Type]->Write (\r
+ mGlobal->VariableStore[Variable->Type],\r
+ VARIABLE_MEMBER_OFFSET (State, (UINTN) Variable->CurrPtr - (UINTN) Variable->StartPtr),\r
+ sizeof (Variable->CurrPtr->State),\r
+ &State\r
+ );\r
+ //\r
+ // NOTE: Write operation at least can write data to memory cache\r
+ // Discard file writing failure here.\r
+ //\r
+ }\r
+ } else {\r
+ //\r
+ // Create a new variable\r
+ // \r
+ \r
+ //\r
+ // Make sure we are trying to create a new variable.\r
+ // Setting a data variable with no access, or zero DataSize attributes means to delete it. \r
+ //\r
+ if (DataSize == 0 || (Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0) {\r
+ return EFI_NOT_FOUND;\r
+ } \r
+ //\r
+ // Only variable have NV|RT attribute can be created in Runtime\r
+ //\r
+ if (EfiAtRuntime () &&\r
+ (!(Attributes & EFI_VARIABLE_RUNTIME_ACCESS) || !(Attributes & EFI_VARIABLE_NON_VOLATILE))) {\r
+ return EFI_INVALID_PARAMETER;\r
+ } \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
+ // We can firstly write all the data in memory, then write them to file\r
+ // This can reduce the times of write operation\r
+ //\r
+ \r
+ NextVariable = (VARIABLE_HEADER *) mGlobal->Scratch;\r
+\r
+ NextVariable->StartId = VARIABLE_DATA;\r
+ NextVariable->Attributes = Attributes;\r
+ NextVariable->State = VAR_ADDED;\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
+ // VarDataOffset: offset from begin of current variable header\r
+ //\r
+ VarSize = VarDataOffset + DataSize + GET_PAD_SIZE (DataSize);\r
+\r
+ StorageType = (Attributes & EFI_VARIABLE_NON_VOLATILE) ? NonVolatile : Volatile;\r
+\r
+ if ((UINT32) (VarSize + mGlobal->LastVariableOffset[StorageType]) >\r
+ ((VARIABLE_STORE_HEADER *) mGlobal->VariableBase[StorageType])->Size\r
+ ) {\r
+ if ((StorageType == NonVolatile) && EfiAtRuntime ()) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ //\r
+ // Perform garbage collection & reclaim operation\r
+ //\r
+ Status = Reclaim (StorageType, Variable->CurrPtr);\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Reclaim error\r
+ // we cannot restore to original state, fetal error, report to user\r
+ //\r
+ DEBUG ((EFI_D_ERROR, "FSVariable: Recalim error (fetal error) - %r\n", Status));\r
+ return Status;\r
+ }\r
+ //\r
+ // If still no enough space, return out of resources\r
+ //\r
+ if ((UINT32) (VarSize + mGlobal->LastVariableOffset[StorageType]) >\r
+ ((VARIABLE_STORE_HEADER *) mGlobal->VariableBase[StorageType])->Size\r
+ ) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Reclaimed = TRUE;\r
+ }\r
+ Status = mGlobal->VariableStore[StorageType]->Write (\r
+ mGlobal->VariableStore[StorageType],\r
+ mGlobal->LastVariableOffset[StorageType],\r
+ VarSize,\r
+ NextVariable\r
+ );\r
+ //\r
+ // NOTE: Write operation at least can write data to memory cache\r
+ // Discard file writing failure here.\r
+ //\r
+ mGlobal->LastVariableOffset[StorageType] += VarSize;\r
+\r
+ if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0) {\r
+ mGlobal->HwErrVariableTotalSize += VarSize;\r
+ } else {\r
+ mGlobal->CommonVariableTotalSize += VarSize;\r
+ }\r
+\r
+ //\r
+ // Mark the old variable as deleted\r