]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Universal/Variable/EmuRuntimeDxe/EmuVariable.c
Fix AutoUpdateLangVariable() logic to handle the case PlatformLang/Lang is set before...
[mirror_edk2.git] / MdeModulePkg / Universal / Variable / EmuRuntimeDxe / EmuVariable.c
index d7fba462cd161625c4502a092d65a54a92192d54..b0644d6040a9c386f813aa58653f21d3870be2fc 100644 (file)
@@ -395,7 +395,6 @@ UpdateVariableInfo (
 \r
 **/\r
 UINTN\r
-EFIAPI\r
 GetIndexFromSupportedLangCodes(\r
   IN  CHAR8            *SupportedLang,\r
   IN  CHAR8            *Lang,\r
@@ -403,13 +402,11 @@ GetIndexFromSupportedLangCodes(
   ) \r
 {\r
   UINTN    Index;\r
-  UINT32   CompareLength;\r
-  CHAR8    *Supported;\r
+  UINT   CompareLength;\r
+  UINTN    LanguageLength;\r
 \r
-  Index = 0;\r
-  Supported = SupportedLang;\r
   if (Iso639Language) {\r
-    CompareLength = 3;\r
+    CompareLength = ISO_639_2_ENTRY_SIZE;\r
     for (Index = 0; Index < AsciiStrLen (SupportedLang); Index += CompareLength) {\r
       if (AsciiStrnCmp (Lang, SupportedLang + Index, CompareLength) == 0) {\r
         //\r
@@ -425,20 +422,26 @@ GetIndexFromSupportedLangCodes(
     //\r
     // Compare RFC4646 language code\r
     //\r
-    while (*Supported != '\0') {\r
+    Index = 0;\r
+    for (LanguageLength = 0; Lang[LanguageLength] != '\0'; LanguageLength++);\r
+\r
+    for (Index = 0; *SupportedLang != '\0'; Index++, SupportedLang += CompareLength) {\r
       //\r
-      // take semicolon as delimitation, sequentially traverse supported language codes.\r
+      // Skip ';' characters in SupportedLang\r
       //\r
-      for (CompareLength = 0; *Supported != ';' && *Supported != '\0'; CompareLength++) {\r
-        Supported++;\r
-      }\r
-      if (AsciiStrnCmp (Lang, Supported - CompareLength, CompareLength) == 0) {\r
+      for (; *SupportedLang != '\0' && *SupportedLang == ';'; SupportedLang++);\r
+      //\r
+      // Determine the length of the next language code in SupportedLang\r
+      //\r
+      for (CompareLength = 0; SupportedLang[CompareLength] != '\0' && SupportedLang[CompareLength] != ';'; CompareLength++);\r
+      \r
+      if ((CompareLength == LanguageLength) && \r
+          (AsciiStrnCmp (Lang, SupportedLang, 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
@@ -472,7 +475,6 @@ GetIndexFromSupportedLangCodes(
 \r
 **/\r
 CHAR8 *\r
-EFIAPI\r
 GetLangFromSupportedLangCodes (\r
   IN  CHAR8            *SupportedLang,\r
   IN  UINTN            Index,\r
@@ -480,7 +482,7 @@ GetLangFromSupportedLangCodes (
 )\r
 {\r
   UINTN    SubIndex;\r
-  UINT32   CompareLength;\r
+  UINT   CompareLength;\r
   CHAR8    *Supported;\r
 \r
   SubIndex  = 0;\r
@@ -491,10 +493,10 @@ GetLangFromSupportedLangCodes (
     // 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 (mVariableModuleGlobal->Lang, sizeof(mVariableModuleGlobal->Lang), 0);\r
+    CompareLength = ISO_639_2_ENTRY_SIZE;\r
+    mVariableModuleGlobal->Lang[CompareLength] = '\0';\r
     return CopyMem (mVariableModuleGlobal->Lang, SupportedLang + Index * CompareLength, CompareLength);\r
-      \r
+\r
   } else {\r
     while (TRUE) {\r
       //\r
@@ -517,14 +519,144 @@ GetLangFromSupportedLangCodes (
         // 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 (mVariableModuleGlobal->PlatformLang, sizeof (mVariableModuleGlobal->PlatformLang), 0);\r
+        mVariableModuleGlobal->PlatformLang[CompareLength] = '\0';\r
         return CopyMem (mVariableModuleGlobal->PlatformLang, Supported - CompareLength, CompareLength);\r
       }\r
-      SubIndex++;                       \r
+      SubIndex++;\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
+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
@@ -539,101 +671,186 @@ GetLangFromSupportedLangCodes (
 \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
+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
+  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
-  // According to UEFI spec, "Lang" and "PlatformLang" is NV|BS|RT attributions.\r
+  // Don't do updates for delete operation\r
   //\r
-  Attributes = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;\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 (EfiAtRuntime ()) {\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
-    AsciiStrnCpy (mVariableModuleGlobal->PlatformLangCodes, Data, DataSize);\r
-  } else if (StrCmp (VariableName, L"LangCodes") == 0) {\r
+    if (mVariableModuleGlobal->PlatformLangCodes != NULL) {\r
+      FreePool (mVariableModuleGlobal->PlatformLangCodes);\r
+    }\r
+    mVariableModuleGlobal->PlatformLangCodes = AllocateRuntimeCopyPool (DataSize, Data);\r
+    ASSERT (mVariableModuleGlobal->PlatformLangCodes != NULL);\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
+    // PlatformLang holds a single language from PlatformLangCodes, \r
+    // so the size of PlatformLangCodes is enough for the PlatformLang.\r
     //\r
-    AsciiStrnCpy (mVariableModuleGlobal->LangCodes, Data, DataSize);\r
-  } else if ((StrCmp (VariableName, L"PlatformLang") == 0) && (DataSize != 0)) {\r
-    ASSERT (AsciiStrLen (mVariableModuleGlobal->PlatformLangCodes) != 0);\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
-    // When setting PlatformLang, firstly get most matched language string from supported language codes.\r
+    // LangCodes is a volatile variable, so it can not be updated at runtime.\r
     //\r
-    BestPlatformLang = GetBestLanguage(mVariableModuleGlobal->PlatformLangCodes, FALSE, Data, NULL);\r
+    if (EfiAtRuntime ()) {\r
+      return;\r
+    }\r
+\r
+    SetLanguageCodes = TRUE;\r
 \r
     //\r
-    // Get the corresponding index in language codes.\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
-    Index = GetIndexFromSupportedLangCodes(mVariableModuleGlobal->PlatformLangCodes, BestPlatformLang, FALSE);\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
-    // Get the corresponding ISO639 language tag according to RFC4646 language tag.\r
+    // Update Lang if PlatformLang is already set\r
+    // Update PlatformLang if Lang is already set\r
     //\r
-    BestLang = GetLangFromSupportedLangCodes(mVariableModuleGlobal->LangCodes, Index, TRUE);\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
-    // Successfully convert PlatformLang to Lang, and set the BestLang value into Lang variable simultaneously.\r
+    // Update Lang when PlatformLangCodes/LangCodes were set.\r
     //\r
-    FindVariable(L"Lang", &gEfiGlobalVariableGuid, &Variable, (VARIABLE_GLOBAL *)mVariableModuleGlobal);\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
-    Status = UpdateVariable(L"Lang", &gEfiGlobalVariableGuid, \r
-                    BestLang, ISO_639_2_ENTRY_SIZE + 1, Attributes, &Variable);\r
+        //\r
+        // Get the corresponding ISO639 language tag according to RFC4646 language tag.\r
+        //\r
+        BestLang = GetLangFromSupportedLangCodes (mVariableModuleGlobal->LangCodes, Index, TRUE);\r
 \r
-    DEBUG((EFI_D_INFO, "Variable Driver Auto Update PlatformLang, PlatformLang:%a, Lang:%a\n", BestPlatformLang, BestLang));\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
-    ASSERT_EFI_ERROR(Status);\r
+        Status = UpdateVariable (L"Lang", &gEfiGlobalVariableGuid, BestLang, ISO_639_2_ENTRY_SIZE + 1, Attributes, &Variable);\r
 \r
-  } else if ((StrCmp (VariableName, L"Lang") == 0) && (DataSize != 0)) {\r
-    ASSERT (AsciiStrLen (mVariableModuleGlobal->LangCodes) != 0);\r
+        DEBUG ((EFI_D_INFO, "Variable Driver Auto Update PlatformLang, PlatformLang:%a, Lang:%a\n", BestPlatformLang, BestLang));\r
 \r
-    //\r
-    // When setting Lang, firstly get most matched language string from supported language codes.\r
-    //\r
-    BestLang = GetBestLanguage(mVariableModuleGlobal->LangCodes, TRUE, Data, NULL);\r
+        ASSERT_EFI_ERROR(Status);\r
+      }\r
+    }\r
 \r
+  } else if (StrCmp (VariableName, L"Lang") == 0) {\r
     //\r
-    // Get the corresponding index in language codes.\r
+    // Update PlatformLang when PlatformLangCodes/LangCodes were set.\r
     //\r
-    Index = GetIndexFromSupportedLangCodes(mVariableModuleGlobal->LangCodes, BestLang, TRUE);\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
+        // 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
+        // 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\r
-                    BestPlatformLang, AsciiStrSize (BestPlatformLang), Attributes, &Variable);\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
+        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
-  return EFI_SUCCESS;\r
 }\r
 \r
 /**\r