From d7f791184ba3db719bc46c3f7b7e2557cc8f9e8c Mon Sep 17 00:00:00 2001 From: niruiyu Date: Wed, 24 Nov 2010 03:23:30 +0000 Subject: [PATCH] Fix AutoUpdateLangVariable() logic to handle the case PlatformLang/Lang is set before PlatformLangCodes/LangCodes. Pre-allocate pool for runtime phase. git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@11088 6f19259b-4bc3-4df7-8a09-765794883524 --- DuetPkg/FSVariable/FSVariable.c | 362 ++++++++++++++++++++++++------ DuetPkg/FSVariable/FSVariable.h | 9 +- DuetPkg/FSVariable/FSVariable.inf | 1 + 3 files changed, 297 insertions(+), 75 deletions(-) diff --git a/DuetPkg/FSVariable/FSVariable.c b/DuetPkg/FSVariable/FSVariable.c index c02bdae7d5..a8286ad9b9 100644 --- a/DuetPkg/FSVariable/FSVariable.c +++ b/DuetPkg/FSVariable/FSVariable.c @@ -576,7 +576,6 @@ Returns: **/ UINTN -EFIAPI GetIndexFromSupportedLangCodes( IN CHAR8 *SupportedLang, IN CHAR8 *Lang, @@ -584,13 +583,11 @@ GetIndexFromSupportedLangCodes( ) { UINTN Index; - UINT32 CompareLength; - CHAR8 *Supported; + UINTN CompareLength; + UINTN LanguageLength; - Index = 0; - Supported = SupportedLang; if (Iso639Language) { - CompareLength = 3; + CompareLength = ISO_639_2_ENTRY_SIZE; for (Index = 0; Index < AsciiStrLen (SupportedLang); Index += CompareLength) { if (AsciiStrnCmp (Lang, SupportedLang + Index, CompareLength) == 0) { // @@ -606,20 +603,26 @@ GetIndexFromSupportedLangCodes( // // Compare RFC4646 language code // - while (*Supported != '\0') { + Index = 0; + for (LanguageLength = 0; Lang[LanguageLength] != '\0'; LanguageLength++); + + for (Index = 0; *SupportedLang != '\0'; Index++, SupportedLang += CompareLength) { // - // take semicolon as delimitation, sequentially traverse supported language codes. + // Skip ';' characters in SupportedLang // - for (CompareLength = 0; *Supported != ';' && *Supported != '\0'; CompareLength++) { - Supported++; - } - if (AsciiStrnCmp (Lang, Supported - CompareLength, CompareLength) == 0) { + for (; *SupportedLang != '\0' && *SupportedLang == ';'; SupportedLang++); + // + // Determine the length of the next language code in SupportedLang + // + for (CompareLength = 0; SupportedLang[CompareLength] != '\0' && SupportedLang[CompareLength] != ';'; CompareLength++); + + if ((CompareLength == LanguageLength) && + (AsciiStrnCmp (Lang, SupportedLang, CompareLength) == 0)) { // // Successfully find the index of Lang string in SupportedLang string. // return Index; } - Index++; } ASSERT (FALSE); return 0; @@ -653,7 +656,6 @@ GetIndexFromSupportedLangCodes( **/ CHAR8 * -EFIAPI GetLangFromSupportedLangCodes ( IN CHAR8 *SupportedLang, IN UINTN Index, @@ -661,7 +663,7 @@ GetLangFromSupportedLangCodes ( ) { UINTN SubIndex; - UINT32 CompareLength; + UINTN CompareLength; CHAR8 *Supported; SubIndex = 0; @@ -672,10 +674,10 @@ GetLangFromSupportedLangCodes ( // As this code will be invoked in RUNTIME, therefore there is not memory allocate/free operation. // In driver entry, it pre-allocates a runtime attribute memory to accommodate this string. // - CompareLength = 3; - SetMem (mGlobal->Lang, sizeof(mGlobal->Lang), 0); + CompareLength = ISO_639_2_ENTRY_SIZE; + mGlobal->Lang[CompareLength] = '\0'; return CopyMem (mGlobal->Lang, SupportedLang + Index * CompareLength, CompareLength); - + } else { while (TRUE) { // @@ -698,7 +700,7 @@ GetLangFromSupportedLangCodes ( // As this code will be invoked in RUNTIME, therefore there is not memory allocate/free operation. // In driver entry, it pre-allocates a runtime attribute memory to accommodate this string. // - SetMem (mGlobal->PlatformLang, sizeof (mGlobal->PlatformLang), 0); + mGlobal->PlatformLang[CompareLength] = '\0'; return CopyMem (mGlobal->PlatformLang, Supported - CompareLength, CompareLength); } SubIndex++; @@ -706,6 +708,136 @@ GetLangFromSupportedLangCodes ( } } +/** + Returns a pointer to an allocated buffer that contains the best matching language + from a set of supported languages. + + This function supports both ISO 639-2 and RFC 4646 language codes, but language + code types may not be mixed in a single call to this function. This function + supports a variable argument list that allows the caller to pass in a prioritized + list of language codes to test against all the language codes in SupportedLanguages. + + If SupportedLanguages is NULL, then ASSERT(). + + @param[in] SupportedLanguages A pointer to a Null-terminated ASCII string that + contains a set of language codes in the format + specified by Iso639Language. + @param[in] Iso639Language If TRUE, then all language codes are assumed to be + in ISO 639-2 format. If FALSE, then all language + codes are assumed to be in RFC 4646 language format + @param[in] ... A variable argument list that contains pointers to + Null-terminated ASCII strings that contain one or more + language codes in the format specified by Iso639Language. + The first language code from each of these language + code lists is used to determine if it is an exact or + close match to any of the language codes in + SupportedLanguages. Close matches only apply to RFC 4646 + language codes, and the matching algorithm from RFC 4647 + is used to determine if a close match is present. If + an exact or close match is found, then the matching + language code from SupportedLanguages is returned. If + no matches are found, then the next variable argument + parameter is evaluated. The variable argument list + is terminated by a NULL. + + @retval NULL The best matching language could not be found in SupportedLanguages. + @retval NULL There are not enough resources available to return the best matching + language. + @retval Other A pointer to a Null-terminated ASCII string that is the best matching + language in SupportedLanguages. + +**/ +CHAR8 * +VariableGetBestLanguage ( + IN CONST CHAR8 *SupportedLanguages, + IN BOOLEAN Iso639Language, + ... + ) +{ + VA_LIST Args; + CHAR8 *Language; + UINTN CompareLength; + UINTN LanguageLength; + CONST CHAR8 *Supported; + CHAR8 *Buffer; + + ASSERT (SupportedLanguages != NULL); + + VA_START (Args, Iso639Language); + while ((Language = VA_ARG (Args, CHAR8 *)) != NULL) { + // + // Default to ISO 639-2 mode + // + CompareLength = 3; + LanguageLength = MIN (3, AsciiStrLen (Language)); + + // + // If in RFC 4646 mode, then determine the length of the first RFC 4646 language code in Language + // + if (!Iso639Language) { + for (LanguageLength = 0; Language[LanguageLength] != 0 && Language[LanguageLength] != ';'; LanguageLength++); + } + + // + // Trim back the length of Language used until it is empty + // + while (LanguageLength > 0) { + // + // Loop through all language codes in SupportedLanguages + // + for (Supported = SupportedLanguages; *Supported != '\0'; Supported += CompareLength) { + // + // In RFC 4646 mode, then Loop through all language codes in SupportedLanguages + // + if (!Iso639Language) { + // + // Skip ';' characters in Supported + // + for (; *Supported != '\0' && *Supported == ';'; Supported++); + // + // Determine the length of the next language code in Supported + // + for (CompareLength = 0; Supported[CompareLength] != 0 && Supported[CompareLength] != ';'; CompareLength++); + // + // If Language is longer than the Supported, then skip to the next language + // + if (LanguageLength > CompareLength) { + continue; + } + } + // + // See if the first LanguageLength characters in Supported match Language + // + if (AsciiStrnCmp (Supported, Language, LanguageLength) == 0) { + VA_END (Args); + + Buffer = Iso639Language ? mGlobal->Lang : mGlobal->PlatformLang; + Buffer[CompareLength] = '\0'; + return CopyMem (Buffer, Supported, CompareLength); + } + } + + if (Iso639Language) { + // + // If ISO 639 mode, then each language can only be tested once + // + LanguageLength = 0; + } else { + // + // If RFC 4646 mode, then trim Language from the right to the next '-' character + // + for (LanguageLength--; LanguageLength > 0 && Language[LanguageLength] != '-'; LanguageLength--); + } + } + } + VA_END (Args); + + // + // No matches were found + // + return NULL; +} + /** Hook the operations in PlatformLangCodes, LangCodes, PlatformLang and Lang. @@ -720,101 +852,186 @@ GetLangFromSupportedLangCodes ( @param[in] DataSize Size of data. 0 means delete - @retval EFI_SUCCESS auto update operation is successful. - **/ -EFI_STATUS -EFIAPI +VOID AutoUpdateLangVariable( IN CHAR16 *VariableName, IN VOID *Data, IN UINTN DataSize ) { - EFI_STATUS Status; - CHAR8 *BestPlatformLang; - CHAR8 *BestLang; - UINTN Index; - UINT32 Attributes; + EFI_STATUS Status; + CHAR8 *BestPlatformLang; + CHAR8 *BestLang; + UINTN Index; + UINT32 Attributes; VARIABLE_POINTER_TRACK Variable; + BOOLEAN SetLanguageCodes; // - // According to UEFI spec, "Lang" and "PlatformLang" is NV|BS|RT attributions. + // Don't do updates for delete operation // - Attributes = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS; + if (DataSize == 0) { + return; + } + + SetLanguageCodes = FALSE; if (StrCmp (VariableName, L"PlatformLangCodes") == 0) { + // + // PlatformLangCodes is a volatile variable, so it can not be updated at runtime. + // + if (EfiAtRuntime ()) { + return; + } + + SetLanguageCodes = TRUE; + // // According to UEFI spec, PlatformLangCodes is only set once in firmware initialization, and is read-only // Therefore, in variable driver, only store the original value for other use. // - AsciiStrnCpy (mGlobal->PlatformLangCodes, Data, DataSize); - } else if (StrCmp (VariableName, L"LangCodes") == 0) { + if (mGlobal->PlatformLangCodes != NULL) { + FreePool (mGlobal->PlatformLangCodes); + } + mGlobal->PlatformLangCodes = AllocateRuntimeCopyPool (DataSize, Data); + ASSERT (mGlobal->PlatformLangCodes != NULL); + // - // According to UEFI spec, LangCodes is only set once in firmware initialization, and is read-only - // Therefore, in variable driver, only store the original value for other use. + // PlatformLang holds a single language from PlatformLangCodes, + // so the size of PlatformLangCodes is enough for the PlatformLang. // - AsciiStrnCpy (mGlobal->LangCodes, Data, DataSize); - } else if ((StrCmp (VariableName, L"PlatformLang") == 0) && (DataSize != 0)) { - ASSERT (AsciiStrLen (mGlobal->PlatformLangCodes) != 0); + if (mGlobal->PlatformLang != NULL) { + FreePool (mGlobal->PlatformLang); + } + mGlobal->PlatformLang = AllocateRuntimePool (DataSize); + ASSERT (mGlobal->PlatformLang != NULL); + } else if (StrCmp (VariableName, L"LangCodes") == 0) { // - // When setting PlatformLang, firstly get most matched language string from supported language codes. + // LangCodes is a volatile variable, so it can not be updated at runtime. // - BestPlatformLang = GetBestLanguage(mGlobal->PlatformLangCodes, FALSE, Data, NULL); + if (EfiAtRuntime ()) { + return; + } + + SetLanguageCodes = TRUE; // - // Get the corresponding index in language codes. + // According to UEFI spec, LangCodes is only set once in firmware initialization, and is read-only + // Therefore, in variable driver, only store the original value for other use. // - Index = GetIndexFromSupportedLangCodes(mGlobal->PlatformLangCodes, BestPlatformLang, FALSE); + if (mGlobal->LangCodes != NULL) { + FreePool (mGlobal->LangCodes); + } + mGlobal->LangCodes = AllocateRuntimeCopyPool (DataSize, Data); + ASSERT (mGlobal->LangCodes != NULL); + } + if (SetLanguageCodes + && (mGlobal->PlatformLangCodes != NULL) + && (mGlobal->LangCodes != NULL)) { // - // Get the corresponding ISO639 language tag according to RFC4646 language tag. + // Update Lang if PlatformLang is already set + // Update PlatformLang if Lang is already set // - BestLang = GetLangFromSupportedLangCodes(mGlobal->LangCodes, Index, TRUE); + Status = FindVariable (L"PlatformLang", &gEfiGlobalVariableGuid, &Variable); + if (!EFI_ERROR (Status)) { + // + // Update Lang + // + VariableName = L"PlatformLang"; + Data = GetVariableDataPtr (Variable.CurrPtr); + DataSize = Variable.CurrPtr->DataSize; + } else { + Status = FindVariable (L"Lang", &gEfiGlobalVariableGuid, &Variable); + if (!EFI_ERROR (Status)) { + // + // Update PlatformLang + // + VariableName = L"Lang"; + Data = GetVariableDataPtr (Variable.CurrPtr); + DataSize = Variable.CurrPtr->DataSize; + } else { + // + // Neither PlatformLang nor Lang is set, directly return + // + return; + } + } + } + + // + // According to UEFI spec, "Lang" and "PlatformLang" is NV|BS|RT attributions. + // + Attributes = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS; + if (StrCmp (VariableName, L"PlatformLang") == 0) { // - // Successfully convert PlatformLang to Lang, and set the BestLang value into Lang variable simultaneously. + // Update Lang when PlatformLangCodes/LangCodes were set. // - FindVariable(L"Lang", &gEfiGlobalVariableGuid, &Variable); + if ((mGlobal->PlatformLangCodes != NULL) && (mGlobal->LangCodes != NULL)) { + // + // When setting PlatformLang, firstly get most matched language string from supported language codes. + // + BestPlatformLang = VariableGetBestLanguage (mGlobal->PlatformLangCodes, FALSE, Data, NULL); + if (BestPlatformLang != NULL) { + // + // Get the corresponding index in language codes. + // + Index = GetIndexFromSupportedLangCodes (mGlobal->PlatformLangCodes, BestPlatformLang, FALSE); - Status = UpdateVariable(L"Lang", &gEfiGlobalVariableGuid, - BestLang, ISO_639_2_ENTRY_SIZE + 1, Attributes, &Variable); + // + // Get the corresponding ISO639 language tag according to RFC4646 language tag. + // + BestLang = GetLangFromSupportedLangCodes (mGlobal->LangCodes, Index, TRUE); - DEBUG((EFI_D_INFO, "Variable Driver Auto Update PlatformLang, PlatformLang:%a, Lang:%a\n", BestPlatformLang, BestLang)); + // + // Successfully convert PlatformLang to Lang, and set the BestLang value into Lang variable simultaneously. + // + FindVariable(L"Lang", &gEfiGlobalVariableGuid, &Variable); - ASSERT_EFI_ERROR(Status); - - } else if ((StrCmp (VariableName, L"Lang") == 0) && (DataSize != 0)) { - ASSERT (AsciiStrLen (mGlobal->LangCodes) != 0); + Status = UpdateVariable (L"Lang", &gEfiGlobalVariableGuid, BestLang, ISO_639_2_ENTRY_SIZE + 1, Attributes, &Variable); - // - // When setting Lang, firstly get most matched language string from supported language codes. - // - BestLang = GetBestLanguage(mGlobal->LangCodes, TRUE, Data, NULL); + DEBUG ((EFI_D_INFO, "Variable Driver Auto Update PlatformLang, PlatformLang:%a, Lang:%a\n", BestPlatformLang, BestLang)); - // - // Get the corresponding index in language codes. - // - Index = GetIndexFromSupportedLangCodes(mGlobal->LangCodes, BestLang, TRUE); + ASSERT_EFI_ERROR(Status); + } + } + } else if (StrCmp (VariableName, L"Lang") == 0) { // - // Get the corresponding RFC4646 language tag according to ISO639 language tag. + // Update PlatformLang when PlatformLangCodes/LangCodes were set. // - BestPlatformLang = GetLangFromSupportedLangCodes(mGlobal->PlatformLangCodes, Index, FALSE); + if ((mGlobal->PlatformLangCodes != NULL) && (mGlobal->LangCodes != NULL)) { + // + // When setting Lang, firstly get most matched language string from supported language codes. + // + BestLang = VariableGetBestLanguage (mGlobal->LangCodes, TRUE, Data, NULL); + if (BestLang != NULL) { + // + // Get the corresponding index in language codes. + // + Index = GetIndexFromSupportedLangCodes (mGlobal->LangCodes, BestLang, TRUE); - // - // Successfully convert Lang to PlatformLang, and set the BestPlatformLang value into PlatformLang variable simultaneously. - // - FindVariable(L"PlatformLang", &gEfiGlobalVariableGuid, &Variable); + // + // Get the corresponding RFC4646 language tag according to ISO639 language tag. + // + BestPlatformLang = GetLangFromSupportedLangCodes (mGlobal->PlatformLangCodes, Index, FALSE); + + // + // Successfully convert Lang to PlatformLang, and set the BestPlatformLang value into PlatformLang variable simultaneously. + // + FindVariable(L"PlatformLang", &gEfiGlobalVariableGuid, &Variable); - Status = UpdateVariable(L"PlatformLang", &gEfiGlobalVariableGuid, - BestPlatformLang, AsciiStrSize (BestPlatformLang), Attributes, &Variable); + Status = UpdateVariable (L"PlatformLang", &gEfiGlobalVariableGuid, BestPlatformLang, + AsciiStrSize (BestPlatformLang), Attributes, &Variable); - DEBUG((EFI_D_INFO, "Variable Driver Auto Update Lang, Lang:%a, PlatformLang:%a\n", BestLang, BestPlatformLang)); - ASSERT_EFI_ERROR(Status); + DEBUG ((EFI_D_INFO, "Variable Driver Auto Update Lang, Lang:%a, PlatformLang:%a\n", BestLang, BestPlatformLang)); + ASSERT_EFI_ERROR (Status); + } + } } - return EFI_SUCCESS; } /** @@ -1723,6 +1940,9 @@ OnVirtualAddressChangeFsv ( EfiConvertPointer (0, (VOID**) &mGlobal->VariableStore[Index]); EfiConvertPointer (0, &mGlobal->VariableBase[Index]); } + EfiConvertPointer (0, (VOID **) &mGlobal->PlatformLangCodes); + EfiConvertPointer (0, (VOID **) &mGlobal->LangCodes); + EfiConvertPointer (0, (VOID **) &mGlobal->PlatformLang); EfiConvertPointer (0, &mGlobal->Scratch); EfiConvertPointer (0, (VOID**) &mGlobal); } diff --git a/DuetPkg/FSVariable/FSVariable.h b/DuetPkg/FSVariable/FSVariable.h index c29c0a5ea0..50215147ea 100644 --- a/DuetPkg/FSVariable/FSVariable.h +++ b/DuetPkg/FSVariable/FSVariable.h @@ -28,6 +28,7 @@ Abstract: #include #include #include +#include #include #include #include @@ -91,10 +92,10 @@ typedef struct { VOID *Scratch; // Buffer used during reclaim UINTN CommonVariableTotalSize; UINTN HwErrVariableTotalSize; - CHAR8 PlatformLangCodes[256]; //Pre-allocate 256 bytes space to accommodate the PlatformlangCodes. - CHAR8 LangCodes[256]; //Pre-allocate 256 bytes space to accommodate the langCodes. - CHAR8 PlatformLang[8]; //Pre-allocate 8 bytes space to accommodate the Platformlang variable. - CHAR8 Lang[4]; //Pre-allocate 4 bytes space to accommodate the lang variable. + CHAR8 *PlatformLangCodes; + CHAR8 *LangCodes; + CHAR8 *PlatformLang; + CHAR8 Lang[ISO_639_2_ENTRY_SIZE + 1]; } VARIABLE_GLOBAL; // diff --git a/DuetPkg/FSVariable/FSVariable.inf b/DuetPkg/FSVariable/FSVariable.inf index cc392d118b..83268381d3 100644 --- a/DuetPkg/FSVariable/FSVariable.inf +++ b/DuetPkg/FSVariable/FSVariable.inf @@ -51,6 +51,7 @@ DxeServicesTableLib DevicePathLib UefiDriverEntryPoint + MemoryAllocationLib [Guids] gEfiFlashMapHobGuid -- 2.39.2