/** @file Implementation of interfaces function for EFI_CONFIG_KEYWORD_HANDLER_PROTOCOL. Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "HiiDatabase.h" extern HII_DATABASE_PRIVATE_DATA mPrivate; /** Convert the hex UNICODE %02x encoding of a UEFI device path to binary from of . This is a internal function. @param String MultiKeywordRequest string. @param DevicePathData Binary of a UEFI device path. @param NextString string follow the possible PathHdr string. @retval EFI_INVALID_PARAMETER The device path is not valid or the incoming parameter is invalid. @retval EFI_OUT_OF_RESOURCES Lake of resources to store necessary structures. @retval EFI_SUCCESS The device path is retrieved and translated to binary format. The Input string not include PathHdr section. **/ EFI_STATUS ExtractDevicePath ( IN EFI_STRING String, OUT UINT8 **DevicePathData, OUT EFI_STRING *NextString ) { UINTN Length; EFI_STRING PathHdr; UINT8 *DevicePathBuffer; CHAR16 TemStr[2]; UINTN Index; UINT8 DigitUint8; EFI_DEVICE_PATH_PROTOCOL *DevicePath; ASSERT (NextString != NULL && DevicePathData != NULL); // // KeywordRequest == NULL case. // if (String == NULL) { *DevicePathData = NULL; *NextString = NULL; return EFI_SUCCESS; } // // Skip '&' if exist. // if (*String == L'&') { String++; } // // Find the 'PATH=' of . // if (StrnCmp (String, L"PATH=", StrLen (L"PATH=")) != 0) { if (StrnCmp (String, L"KEYWORD=", StrLen (L"KEYWORD=")) != 0) { return EFI_INVALID_PARAMETER; } else { // // Not include PathHdr, return success and DevicePath = NULL. // *DevicePathData = NULL; *NextString = String; return EFI_SUCCESS; } } // // Check whether path data does exist. // String += StrLen (L"PATH="); if (*String == 0) { return EFI_INVALID_PARAMETER; } PathHdr = String; // // The content between 'PATH=' of and '&' of next element // or '\0' (end of configuration string) is the UNICODE %02x bytes encoding // of UEFI device path. // for (Length = 0; *String != 0 && *String != L'&'; String++, Length++) { } // // Save the return next keyword string value. // *NextString = String; // // Check DevicePath Length // if (((Length + 1) / 2) < sizeof (EFI_DEVICE_PATH_PROTOCOL)) { return EFI_INVALID_PARAMETER; } // // The data in is encoded as hex UNICODE %02x bytes in the same order // as the device path resides in RAM memory. // Translate the data into binary. // DevicePathBuffer = (UINT8 *)AllocateZeroPool ((Length + 1) / 2); if (DevicePathBuffer == NULL) { return EFI_OUT_OF_RESOURCES; } // // Convert DevicePath // ZeroMem (TemStr, sizeof (TemStr)); for (Index = 0; Index < Length; Index++) { TemStr[0] = PathHdr[Index]; DigitUint8 = (UINT8)StrHexToUint64 (TemStr); if ((Index & 1) == 0) { DevicePathBuffer[Index/2] = DigitUint8; } else { DevicePathBuffer[Index/2] = (UINT8)((DevicePathBuffer[Index/2] << 4) + DigitUint8); } } // // Validate DevicePath // DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)DevicePathBuffer; while (!IsDevicePathEnd (DevicePath)) { if ((DevicePath->Type == 0) || (DevicePath->SubType == 0) || (DevicePathNodeLength (DevicePath) < sizeof (EFI_DEVICE_PATH_PROTOCOL))) { // // Invalid device path // FreePool (DevicePathBuffer); return EFI_INVALID_PARAMETER; } DevicePath = NextDevicePathNode (DevicePath); } // // return the device path // *DevicePathData = DevicePathBuffer; return EFI_SUCCESS; } /** Get NameSpace from the input NameSpaceId string. This is a internal function. @param String format string. @param NameSpace Return the name space string. @param NextString Return the next string follow namespace. @retval EFI_SUCCESS Get the namespace string success. @retval EFI_INVALID_PARAMETER The NameSpaceId string not follow spec definition. **/ EFI_STATUS ExtractNameSpace ( IN EFI_STRING String, OUT CHAR8 **NameSpace, OUT EFI_STRING *NextString ) { CHAR16 *TmpPtr; UINTN NameSpaceSize; ASSERT (NameSpace != NULL); TmpPtr = NULL; // // Input NameSpaceId == NULL // if (String == NULL) { *NameSpace = NULL; if (NextString != NULL) { *NextString = NULL; } return EFI_SUCCESS; } // // Skip '&' if exist. // if (*String == L'&') { String++; } if (StrnCmp (String, L"NAMESPACE=", StrLen (L"NAMESPACE=")) != 0) { return EFI_INVALID_PARAMETER; } String += StrLen (L"NAMESPACE="); TmpPtr = StrStr (String, L"&"); if (TmpPtr != NULL) { *TmpPtr = 0; } if (NextString != NULL) { *NextString = String + StrLen (String); } // // Input NameSpace is unicode string. The language in String package is ascii string. // Here will convert the unicode string to ascii and save it. // NameSpaceSize = StrLen (String) + 1; *NameSpace = AllocatePool (NameSpaceSize); if (*NameSpace == NULL) { return EFI_OUT_OF_RESOURCES; } UnicodeStrToAsciiStrS (String, *NameSpace, NameSpaceSize); if (TmpPtr != NULL) { *TmpPtr = L'&'; } return EFI_SUCCESS; } /** Get Keyword from the input KeywordRequest string. This is a internal function. @param String KeywordRequestformat string. @param Keyword return the extract keyword string. @param NextString return the next string follow this keyword section. @retval EFI_SUCCESS Success to get the keyword string. @retval EFI_INVALID_PARAMETER Parse the input string return error. **/ EFI_STATUS ExtractKeyword ( IN EFI_STRING String, OUT EFI_STRING *Keyword, OUT EFI_STRING *NextString ) { EFI_STRING TmpPtr; ASSERT ((Keyword != NULL) && (NextString != NULL)); TmpPtr = NULL; // // KeywordRequest == NULL case. // if (String == NULL) { *Keyword = NULL; *NextString = NULL; return EFI_SUCCESS; } // // Skip '&' if exist. // if (*String == L'&') { String++; } if (StrnCmp (String, L"KEYWORD=", StrLen (L"KEYWORD=")) != 0) { return EFI_INVALID_PARAMETER; } String += StrLen (L"KEYWORD="); TmpPtr = StrStr (String, L"&"); if (TmpPtr != NULL) { *TmpPtr = 0; } *NextString = String + StrLen (String); *Keyword = AllocateCopyPool (StrSize (String), String); if (*Keyword == NULL) { return EFI_OUT_OF_RESOURCES; } if (TmpPtr != NULL) { *TmpPtr = L'&'; } return EFI_SUCCESS; } /** Get value from the input KeywordRequest string. This is a internal function. @param String KeywordRequestformat string. @param Value return the extract value string. @param NextString return the next string follow this keyword section. @retval EFI_SUCCESS Success to get the keyword string. @retval EFI_INVALID_PARAMETER Parse the input string return error. **/ EFI_STATUS ExtractValue ( IN EFI_STRING String, OUT EFI_STRING *Value, OUT EFI_STRING *NextString ) { EFI_STRING TmpPtr; ASSERT ((Value != NULL) && (NextString != NULL) && (String != NULL)); // // Skip '&' if exist. // if (*String == L'&') { String++; } if (StrnCmp (String, L"VALUE=", StrLen (L"VALUE=")) != 0) { return EFI_INVALID_PARAMETER; } String += StrLen (L"VALUE="); TmpPtr = StrStr (String, L"&"); if (TmpPtr != NULL) { *TmpPtr = 0; } *NextString = String + StrLen (String); *Value = AllocateCopyPool (StrSize (String), String); if (*Value == NULL) { return EFI_OUT_OF_RESOURCES; } if (TmpPtr != NULL) { *TmpPtr = L'&'; } return EFI_SUCCESS; } /** Get filter from the input KeywordRequest string. This is a internal function. @param String KeywordRequestformat string. @param FilterFlags return the filter condition. @param NextString return the next string follow this keyword section. @retval EFI_SUCCESS Success to get the keyword string. @retval EFI_INVALID_PARAMETER Parse the input string return error. **/ BOOLEAN ExtractFilter ( IN EFI_STRING String, OUT UINT8 *FilterFlags, OUT EFI_STRING *NextString ) { CHAR16 *PathPtr; CHAR16 *KeywordPtr; BOOLEAN RetVal; ASSERT ((FilterFlags != NULL) && (NextString != NULL)); // // String end, no filter section. // if (String == NULL) { *NextString = NULL; return FALSE; } *FilterFlags = 0; RetVal = TRUE; // // Skip '&' if exist. // if (*String == L'&') { String++; } if (StrnCmp (String, L"ReadOnly", StrLen (L"ReadOnly")) == 0) { // // Find ReadOnly filter. // *FilterFlags |= EFI_KEYWORD_FILTER_READONY; String += StrLen (L"ReadOnly"); } else if (StrnCmp (String, L"ReadWrite", StrLen (L"ReadWrite")) == 0) { // // Find ReadWrite filter. // *FilterFlags |= EFI_KEYWORD_FILTER_REAWRITE; String += StrLen (L"ReadWrite"); } else if (StrnCmp (String, L"Buffer", StrLen (L"Buffer")) == 0) { // // Find Buffer Filter. // *FilterFlags |= EFI_KEYWORD_FILTER_BUFFER; String += StrLen (L"Buffer"); } else if (StrnCmp (String, L"Numeric", StrLen (L"Numeric")) == 0) { // // Find Numeric Filter // String += StrLen (L"Numeric"); if (*String != L':') { *FilterFlags |= EFI_KEYWORD_FILTER_NUMERIC; } else { String++; switch (*String) { case L'1': *FilterFlags |= EFI_KEYWORD_FILTER_NUMERIC_1; break; case L'2': *FilterFlags |= EFI_KEYWORD_FILTER_NUMERIC_2; break; case L'4': *FilterFlags |= EFI_KEYWORD_FILTER_NUMERIC_4; break; case L'8': *FilterFlags |= EFI_KEYWORD_FILTER_NUMERIC_8; break; default: ASSERT (FALSE); break; } String++; } } else { // // Check whether other filter item defined by Platform. // if ((StrnCmp (String, L"&PATH", StrLen (L"&PATH")) == 0) || (StrnCmp (String, L"&KEYWORD", StrLen (L"&KEYWORD")) == 0)) { // // New KeywordRequest start, no platform defined filter. // } else { // // Platform defined filter rule. // Just skip platform defined filter rule, return success. // PathPtr = StrStr (String, L"&PATH"); KeywordPtr = StrStr (String, L"&KEYWORD"); if ((PathPtr != NULL) && (KeywordPtr != NULL)) { // // If both sections exist, return the first follow string. // String = KeywordPtr > PathPtr ? PathPtr : KeywordPtr; } else if (PathPtr != NULL) { // // Should not exist PathPtr != NULL && KeywordPtr == NULL case. // ASSERT (FALSE); } else if (KeywordPtr != NULL) { // // Just to the next keyword section. // String = KeywordPtr; } else { // // Only has platform defined filter section, just skip it. // String += StrLen (String); } } RetVal = FALSE; } *NextString = String; return RetVal; } /** Extract Readonly flag from opcode. This is a internal function. @param OpCodeData Input opcode for this question. @retval TRUE This question is readonly. @retval FALSE This question is not readonly. **/ BOOLEAN ExtractReadOnlyFromOpCode ( IN UINT8 *OpCodeData ) { EFI_IFR_QUESTION_HEADER *QuestionHdr; ASSERT (OpCodeData != NULL); QuestionHdr = (EFI_IFR_QUESTION_HEADER *)(OpCodeData + sizeof (EFI_IFR_OP_HEADER)); return (QuestionHdr->Flags & EFI_IFR_FLAG_READ_ONLY) != 0; } /** Create a circuit to check the filter section. This is a internal function. @param OpCodeData The question binary ifr data. @param KeywordRequest KeywordRequestformat string. @param NextString return the next string follow this keyword section. @param ReadOnly Return whether this question is read only. @retval KEYWORD_HANDLER_NO_ERROR Success validate. @retval KEYWORD_HANDLER_INCOMPATIBLE_VALUE_DETECTED Validate fail. **/ UINT32 ValidateFilter ( IN UINT8 *OpCodeData, IN CHAR16 *KeywordRequest, OUT CHAR16 **NextString, OUT BOOLEAN *ReadOnly ) { CHAR16 *NextFilter; CHAR16 *StringPtr; UINT8 FilterFlags; EFI_IFR_QUESTION_HEADER *QuestionHdr; EFI_IFR_OP_HEADER *OpCodeHdr; UINT8 Flags; UINT32 RetVal; RetVal = KEYWORD_HANDLER_NO_ERROR; StringPtr = KeywordRequest; OpCodeHdr = (EFI_IFR_OP_HEADER *)OpCodeData; QuestionHdr = (EFI_IFR_QUESTION_HEADER *)(OpCodeData + sizeof (EFI_IFR_OP_HEADER)); if ((OpCodeHdr->OpCode == EFI_IFR_ONE_OF_OP) || (OpCodeHdr->OpCode == EFI_IFR_NUMERIC_OP)) { Flags = *(OpCodeData + sizeof (EFI_IFR_OP_HEADER) + sizeof (EFI_IFR_QUESTION_HEADER)); } else { Flags = 0; } // // Get ReadOnly flag from Question. // *ReadOnly = ExtractReadOnlyFromOpCode (OpCodeData); while (ExtractFilter (StringPtr, &FilterFlags, &NextFilter)) { switch (FilterFlags) { case EFI_KEYWORD_FILTER_READONY: if ((QuestionHdr->Flags & EFI_IFR_FLAG_READ_ONLY) == 0) { RetVal = KEYWORD_HANDLER_INCOMPATIBLE_VALUE_DETECTED; goto Done; } break; case EFI_KEYWORD_FILTER_REAWRITE: if ((QuestionHdr->Flags & EFI_IFR_FLAG_READ_ONLY) != 0) { RetVal = KEYWORD_HANDLER_INCOMPATIBLE_VALUE_DETECTED; goto Done; } break; case EFI_KEYWORD_FILTER_BUFFER: // // Only these three opcode use numeric value type. // if ((OpCodeHdr->OpCode == EFI_IFR_ONE_OF_OP) || (OpCodeHdr->OpCode == EFI_IFR_NUMERIC_OP) || (OpCodeHdr->OpCode == EFI_IFR_CHECKBOX_OP)) { RetVal = KEYWORD_HANDLER_INCOMPATIBLE_VALUE_DETECTED; goto Done; } break; case EFI_KEYWORD_FILTER_NUMERIC: if ((OpCodeHdr->OpCode != EFI_IFR_ONE_OF_OP) && (OpCodeHdr->OpCode != EFI_IFR_NUMERIC_OP) && (OpCodeHdr->OpCode != EFI_IFR_CHECKBOX_OP)) { RetVal = KEYWORD_HANDLER_INCOMPATIBLE_VALUE_DETECTED; goto Done; } break; case EFI_KEYWORD_FILTER_NUMERIC_1: case EFI_KEYWORD_FILTER_NUMERIC_2: case EFI_KEYWORD_FILTER_NUMERIC_4: case EFI_KEYWORD_FILTER_NUMERIC_8: if ((OpCodeHdr->OpCode != EFI_IFR_ONE_OF_OP) && (OpCodeHdr->OpCode != EFI_IFR_NUMERIC_OP) && (OpCodeHdr->OpCode != EFI_IFR_CHECKBOX_OP)) { RetVal = KEYWORD_HANDLER_INCOMPATIBLE_VALUE_DETECTED; goto Done; } // // For numeric and oneof, it has flags field to specify the detail numeric type. // if ((OpCodeHdr->OpCode == EFI_IFR_ONE_OF_OP) || (OpCodeHdr->OpCode == EFI_IFR_NUMERIC_OP)) { switch (Flags & EFI_IFR_NUMERIC_SIZE) { case EFI_IFR_NUMERIC_SIZE_1: if (FilterFlags != EFI_KEYWORD_FILTER_NUMERIC_1) { RetVal = KEYWORD_HANDLER_INCOMPATIBLE_VALUE_DETECTED; goto Done; } break; case EFI_IFR_NUMERIC_SIZE_2: if (FilterFlags != EFI_KEYWORD_FILTER_NUMERIC_2) { RetVal = KEYWORD_HANDLER_INCOMPATIBLE_VALUE_DETECTED; goto Done; } break; case EFI_IFR_NUMERIC_SIZE_4: if (FilterFlags != EFI_KEYWORD_FILTER_NUMERIC_4) { RetVal = KEYWORD_HANDLER_INCOMPATIBLE_VALUE_DETECTED; goto Done; } break; case EFI_IFR_NUMERIC_SIZE_8: if (FilterFlags != EFI_KEYWORD_FILTER_NUMERIC_8) { RetVal = KEYWORD_HANDLER_INCOMPATIBLE_VALUE_DETECTED; goto Done; } break; default: ASSERT (FALSE); break; } } break; default: ASSERT (FALSE); break; } // // Jump to the next filter. // StringPtr = NextFilter; } Done: // // The current filter which is processing. // *NextString = StringPtr; return RetVal; } /** Get HII_DATABASE_RECORD from the input device path info. This is a internal function. @param DevicePath UEFI device path protocol. @retval Internal data base record. **/ HII_DATABASE_RECORD * GetRecordFromDevicePath ( IN EFI_DEVICE_PATH_PROTOCOL *DevicePath ) { LIST_ENTRY *Link; UINT8 *DevicePathPkg; UINT8 *CurrentDevicePath; UINTN DevicePathSize; HII_DATABASE_RECORD *TempDatabase; ASSERT (DevicePath != NULL); for (Link = mPrivate.DatabaseList.ForwardLink; Link != &mPrivate.DatabaseList; Link = Link->ForwardLink) { TempDatabase = CR (Link, HII_DATABASE_RECORD, DatabaseEntry, HII_DATABASE_RECORD_SIGNATURE); DevicePathPkg = TempDatabase->PackageList->DevicePathPkg; if (DevicePathPkg != NULL) { CurrentDevicePath = DevicePathPkg + sizeof (EFI_HII_PACKAGE_HEADER); DevicePathSize = GetDevicePathSize ((EFI_DEVICE_PATH_PROTOCOL *)CurrentDevicePath); if ((CompareMem (DevicePath, CurrentDevicePath, DevicePathSize) == 0)) { return TempDatabase; } } } return NULL; } /** Calculate the size of StringSrc and output it. Also copy string text from src to dest. This is a internal function. @param StringSrc Points to current null-terminated string. @param BufferSize Length of the buffer. @param StringDest Buffer to store the string text. @retval EFI_SUCCESS The string text was outputted successfully. @retval EFI_OUT_OF_RESOURCES Out of resource. **/ EFI_STATUS GetUnicodeStringTextAndSize ( IN UINT8 *StringSrc, OUT UINTN *BufferSize, OUT EFI_STRING *StringDest ) { UINTN StringSize; UINT8 *StringPtr; ASSERT (StringSrc != NULL && BufferSize != NULL && StringDest != NULL); StringSize = sizeof (CHAR16); StringPtr = StringSrc; while (ReadUnaligned16 ((UINT16 *)StringPtr) != 0) { StringSize += sizeof (CHAR16); StringPtr += sizeof (CHAR16); } *StringDest = AllocatePool (StringSize); if (*StringDest == NULL) { return EFI_OUT_OF_RESOURCES; } CopyMem (*StringDest, StringSrc, StringSize); *BufferSize = StringSize; return EFI_SUCCESS; } /** Find the string id for the input keyword. @param StringPackage Hii string package instance. @param KeywordValue Input keyword value. @param StringId The string's id, which is unique within PackageList. @retval EFI_SUCCESS The string text and font is retrieved successfully. @retval EFI_NOT_FOUND The specified text or font info can not be found out. @retval EFI_OUT_OF_RESOURCES The system is out of resources to accomplish the task. **/ EFI_STATUS GetStringIdFromString ( IN HII_STRING_PACKAGE_INSTANCE *StringPackage, IN CHAR16 *KeywordValue, OUT EFI_STRING_ID *StringId ) { UINT8 *BlockHdr; EFI_STRING_ID CurrentStringId; UINTN BlockSize; UINTN Index; UINT8 *StringTextPtr; UINTN Offset; UINT16 StringCount; UINT16 SkipCount; UINT8 Length8; EFI_HII_SIBT_EXT2_BLOCK Ext2; UINT32 Length32; UINTN StringSize; CHAR16 *String; CHAR8 *AsciiKeywordValue; UINTN KeywordValueSize; EFI_STATUS Status; ASSERT (StringPackage != NULL && KeywordValue != NULL && StringId != NULL); ASSERT (StringPackage->Signature == HII_STRING_PACKAGE_SIGNATURE); CurrentStringId = 1; Status = EFI_SUCCESS; String = NULL; BlockHdr = StringPackage->StringBlock; BlockSize = 0; Offset = 0; // // Make a ascii keyword value for later use. // KeywordValueSize = StrLen (KeywordValue) + 1; AsciiKeywordValue = AllocatePool (KeywordValueSize); if (AsciiKeywordValue == NULL) { return EFI_OUT_OF_RESOURCES; } UnicodeStrToAsciiStrS (KeywordValue, AsciiKeywordValue, KeywordValueSize); while (*BlockHdr != EFI_HII_SIBT_END) { switch (*BlockHdr) { case EFI_HII_SIBT_STRING_SCSU: Offset = sizeof (EFI_HII_STRING_BLOCK); StringTextPtr = BlockHdr + Offset; BlockSize += Offset + AsciiStrSize ((CHAR8 *)StringTextPtr); if (AsciiStrCmp (AsciiKeywordValue, (CHAR8 *)StringTextPtr) == 0) { *StringId = CurrentStringId; goto Done; } CurrentStringId++; break; case EFI_HII_SIBT_STRING_SCSU_FONT: Offset = sizeof (EFI_HII_SIBT_STRING_SCSU_FONT_BLOCK) - sizeof (UINT8); StringTextPtr = BlockHdr + Offset; if (AsciiStrCmp (AsciiKeywordValue, (CHAR8 *)StringTextPtr) == 0) { *StringId = CurrentStringId; goto Done; } BlockSize += Offset + AsciiStrSize ((CHAR8 *)StringTextPtr); CurrentStringId++; break; case EFI_HII_SIBT_STRINGS_SCSU: CopyMem (&StringCount, BlockHdr + sizeof (EFI_HII_STRING_BLOCK), sizeof (UINT16)); StringTextPtr = (UINT8 *)((UINTN)BlockHdr + sizeof (EFI_HII_SIBT_STRINGS_SCSU_BLOCK) - sizeof (UINT8)); BlockSize += StringTextPtr - BlockHdr; for (Index = 0; Index < StringCount; Index++) { BlockSize += AsciiStrSize ((CHAR8 *)StringTextPtr); if (AsciiStrCmp (AsciiKeywordValue, (CHAR8 *)StringTextPtr) == 0) { *StringId = CurrentStringId; goto Done; } StringTextPtr = StringTextPtr + AsciiStrSize ((CHAR8 *)StringTextPtr); CurrentStringId++; } break; case EFI_HII_SIBT_STRINGS_SCSU_FONT: CopyMem ( &StringCount, (UINT8 *)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK) + sizeof (UINT8)), sizeof (UINT16) ); StringTextPtr = (UINT8 *)((UINTN)BlockHdr + sizeof (EFI_HII_SIBT_STRINGS_SCSU_FONT_BLOCK) - sizeof (UINT8)); BlockSize += StringTextPtr - BlockHdr; for (Index = 0; Index < StringCount; Index++) { BlockSize += AsciiStrSize ((CHAR8 *)StringTextPtr); if (AsciiStrCmp (AsciiKeywordValue, (CHAR8 *)StringTextPtr) == 0) { *StringId = CurrentStringId; goto Done; } StringTextPtr = StringTextPtr + AsciiStrSize ((CHAR8 *)StringTextPtr); CurrentStringId++; } break; case EFI_HII_SIBT_STRING_UCS2: Offset = sizeof (EFI_HII_STRING_BLOCK); StringTextPtr = BlockHdr + Offset; // // Use StringSize to store the size of the specified string, including the NULL // terminator. // Status = GetUnicodeStringTextAndSize (StringTextPtr, &StringSize, &String); if (EFI_ERROR (Status)) { goto Done; } ASSERT (String != NULL); if (StrCmp (KeywordValue, String) == 0) { *StringId = CurrentStringId; goto Done; } BlockSize += Offset + StringSize; CurrentStringId++; break; case EFI_HII_SIBT_STRING_UCS2_FONT: Offset = sizeof (EFI_HII_SIBT_STRING_UCS2_FONT_BLOCK) - sizeof (CHAR16); StringTextPtr = BlockHdr + Offset; // // Use StringSize to store the size of the specified string, including the NULL // terminator. // Status = GetUnicodeStringTextAndSize (StringTextPtr, &StringSize, &String); if (EFI_ERROR (Status)) { goto Done; } ASSERT (String != NULL); if (StrCmp (KeywordValue, String) == 0) { *StringId = CurrentStringId; goto Done; } BlockSize += Offset + StringSize; CurrentStringId++; break; case EFI_HII_SIBT_STRINGS_UCS2: Offset = sizeof (EFI_HII_SIBT_STRINGS_UCS2_BLOCK) - sizeof (CHAR16); StringTextPtr = BlockHdr + Offset; BlockSize += Offset; CopyMem (&StringCount, BlockHdr + sizeof (EFI_HII_STRING_BLOCK), sizeof (UINT16)); for (Index = 0; Index < StringCount; Index++) { Status = GetUnicodeStringTextAndSize (StringTextPtr, &StringSize, &String); if (EFI_ERROR (Status)) { goto Done; } ASSERT (String != NULL); BlockSize += StringSize; if (StrCmp (KeywordValue, String) == 0) { *StringId = CurrentStringId; goto Done; } StringTextPtr = StringTextPtr + StringSize; CurrentStringId++; } break; case EFI_HII_SIBT_STRINGS_UCS2_FONT: Offset = sizeof (EFI_HII_SIBT_STRINGS_UCS2_FONT_BLOCK) - sizeof (CHAR16); StringTextPtr = BlockHdr + Offset; BlockSize += Offset; CopyMem ( &StringCount, (UINT8 *)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK) + sizeof (UINT8)), sizeof (UINT16) ); for (Index = 0; Index < StringCount; Index++) { Status = GetUnicodeStringTextAndSize (StringTextPtr, &StringSize, &String); if (EFI_ERROR (Status)) { goto Done; } ASSERT (String != NULL); BlockSize += StringSize; if (StrCmp (KeywordValue, String) == 0) { *StringId = CurrentStringId; goto Done; } StringTextPtr = StringTextPtr + StringSize; CurrentStringId++; } break; case EFI_HII_SIBT_DUPLICATE: BlockSize += sizeof (EFI_HII_SIBT_DUPLICATE_BLOCK); CurrentStringId++; break; case EFI_HII_SIBT_SKIP1: SkipCount = (UINT16)(*(UINT8 *)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK))); CurrentStringId = (UINT16)(CurrentStringId + SkipCount); BlockSize += sizeof (EFI_HII_SIBT_SKIP1_BLOCK); break; case EFI_HII_SIBT_SKIP2: CopyMem (&SkipCount, BlockHdr + sizeof (EFI_HII_STRING_BLOCK), sizeof (UINT16)); CurrentStringId = (UINT16)(CurrentStringId + SkipCount); BlockSize += sizeof (EFI_HII_SIBT_SKIP2_BLOCK); break; case EFI_HII_SIBT_EXT1: CopyMem ( &Length8, (UINT8 *)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK) + sizeof (UINT8)), sizeof (UINT8) ); BlockSize += Length8; break; case EFI_HII_SIBT_EXT2: CopyMem (&Ext2, BlockHdr, sizeof (EFI_HII_SIBT_EXT2_BLOCK)); BlockSize += Ext2.Length; break; case EFI_HII_SIBT_EXT4: CopyMem ( &Length32, (UINT8 *)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK) + sizeof (UINT8)), sizeof (UINT32) ); BlockSize += Length32; break; default: break; } if (String != NULL) { FreePool (String); String = NULL; } BlockHdr = StringPackage->StringBlock + BlockSize; } Status = EFI_NOT_FOUND; Done: if (AsciiKeywordValue != NULL) { FreePool (AsciiKeywordValue); } if (String != NULL) { FreePool (String); } return Status; } /** Find the next valid string id for the input string id. @param StringPackage Hii string package instance. @param StringId The current string id which is already got. 1 means just begin to get the string id. @param KeywordValue Return the string for the next string id. @retval EFI_STRING_ID Not 0 means a valid stringid found. 0 means not found a valid string id. **/ EFI_STRING_ID GetNextStringId ( IN HII_STRING_PACKAGE_INSTANCE *StringPackage, IN EFI_STRING_ID StringId, OUT EFI_STRING *KeywordValue ) { UINT8 *BlockHdr; EFI_STRING_ID CurrentStringId; UINTN BlockSize; UINTN Index; UINT8 *StringTextPtr; UINTN Offset; UINT16 StringCount; UINT16 SkipCount; UINT8 Length8; EFI_HII_SIBT_EXT2_BLOCK Ext2; UINT32 Length32; BOOLEAN FindString; UINTN StringSize; CHAR16 *String; ASSERT (StringPackage != NULL); ASSERT (StringPackage->Signature == HII_STRING_PACKAGE_SIGNATURE); CurrentStringId = 1; FindString = FALSE; String = NULL; // // Parse the string blocks to get the string text and font. // BlockHdr = StringPackage->StringBlock; BlockSize = 0; Offset = 0; while (*BlockHdr != EFI_HII_SIBT_END) { switch (*BlockHdr) { case EFI_HII_SIBT_STRING_SCSU: Offset = sizeof (EFI_HII_STRING_BLOCK); StringTextPtr = BlockHdr + Offset; if (FindString) { StringSize = AsciiStrSize ((CHAR8 *)StringTextPtr); *KeywordValue = AllocatePool (StringSize * sizeof (CHAR16)); if (*KeywordValue == NULL) { return 0; } AsciiStrToUnicodeStrS ((CHAR8 *)StringTextPtr, *KeywordValue, StringSize); return CurrentStringId; } else if (CurrentStringId == StringId) { FindString = TRUE; } BlockSize += Offset + AsciiStrSize ((CHAR8 *)StringTextPtr); CurrentStringId++; break; case EFI_HII_SIBT_STRING_SCSU_FONT: Offset = sizeof (EFI_HII_SIBT_STRING_SCSU_FONT_BLOCK) - sizeof (UINT8); StringTextPtr = BlockHdr + Offset; if (FindString) { StringSize = AsciiStrSize ((CHAR8 *)StringTextPtr); *KeywordValue = AllocatePool (StringSize * sizeof (CHAR16)); if (*KeywordValue == NULL) { return 0; } AsciiStrToUnicodeStrS ((CHAR8 *)StringTextPtr, *KeywordValue, StringSize); return CurrentStringId; } else if (CurrentStringId == StringId) { FindString = TRUE; } BlockSize += Offset + AsciiStrSize ((CHAR8 *)StringTextPtr); CurrentStringId++; break; case EFI_HII_SIBT_STRINGS_SCSU: CopyMem (&StringCount, BlockHdr + sizeof (EFI_HII_STRING_BLOCK), sizeof (UINT16)); StringTextPtr = (UINT8 *)((UINTN)BlockHdr + sizeof (EFI_HII_SIBT_STRINGS_SCSU_BLOCK) - sizeof (UINT8)); BlockSize += StringTextPtr - BlockHdr; for (Index = 0; Index < StringCount; Index++) { if (FindString) { StringSize = AsciiStrSize ((CHAR8 *)StringTextPtr); *KeywordValue = AllocatePool (StringSize * sizeof (CHAR16)); if (*KeywordValue == NULL) { return 0; } AsciiStrToUnicodeStrS ((CHAR8 *)StringTextPtr, *KeywordValue, StringSize); return CurrentStringId; } else if (CurrentStringId == StringId) { FindString = TRUE; } BlockSize += AsciiStrSize ((CHAR8 *)StringTextPtr); StringTextPtr = StringTextPtr + AsciiStrSize ((CHAR8 *)StringTextPtr); CurrentStringId++; } break; case EFI_HII_SIBT_STRINGS_SCSU_FONT: CopyMem ( &StringCount, (UINT8 *)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK) + sizeof (UINT8)), sizeof (UINT16) ); StringTextPtr = (UINT8 *)((UINTN)BlockHdr + sizeof (EFI_HII_SIBT_STRINGS_SCSU_FONT_BLOCK) - sizeof (UINT8)); BlockSize += StringTextPtr - BlockHdr; for (Index = 0; Index < StringCount; Index++) { if (FindString) { StringSize = AsciiStrSize ((CHAR8 *)StringTextPtr); *KeywordValue = AllocatePool (StringSize * sizeof (CHAR16)); if (*KeywordValue == NULL) { return 0; } AsciiStrToUnicodeStrS ((CHAR8 *)StringTextPtr, *KeywordValue, StringSize); return CurrentStringId; } else if (CurrentStringId == StringId) { FindString = TRUE; } BlockSize += AsciiStrSize ((CHAR8 *)StringTextPtr); StringTextPtr = StringTextPtr + AsciiStrSize ((CHAR8 *)StringTextPtr); CurrentStringId++; } break; case EFI_HII_SIBT_STRING_UCS2: Offset = sizeof (EFI_HII_STRING_BLOCK); StringTextPtr = BlockHdr + Offset; // // Use StringSize to store the size of the specified string, including the NULL // terminator. // GetUnicodeStringTextAndSize (StringTextPtr, &StringSize, &String); if (FindString && (String != NULL) && (*String != L'\0')) { // // String protocol use this type for the string id which has value for other package. // It will allocate an empty string block for this string id. so here we also check // *String != L'\0' to prohibit this case. // *KeywordValue = String; return CurrentStringId; } else if (CurrentStringId == StringId) { FindString = TRUE; } BlockSize += Offset + StringSize; CurrentStringId++; break; case EFI_HII_SIBT_STRING_UCS2_FONT: Offset = sizeof (EFI_HII_SIBT_STRING_UCS2_FONT_BLOCK) - sizeof (CHAR16); StringTextPtr = BlockHdr + Offset; // // Use StringSize to store the size of the specified string, including the NULL // terminator. // GetUnicodeStringTextAndSize (StringTextPtr, &StringSize, &String); if (FindString) { *KeywordValue = String; return CurrentStringId; } else if (CurrentStringId == StringId) { FindString = TRUE; } BlockSize += Offset + StringSize; CurrentStringId++; break; case EFI_HII_SIBT_STRINGS_UCS2: Offset = sizeof (EFI_HII_SIBT_STRINGS_UCS2_BLOCK) - sizeof (CHAR16); StringTextPtr = BlockHdr + Offset; BlockSize += Offset; CopyMem (&StringCount, BlockHdr + sizeof (EFI_HII_STRING_BLOCK), sizeof (UINT16)); for (Index = 0; Index < StringCount; Index++) { GetUnicodeStringTextAndSize (StringTextPtr, &StringSize, &String); if (FindString) { *KeywordValue = String; return CurrentStringId; } else if (CurrentStringId == StringId) { FindString = TRUE; } BlockSize += StringSize; StringTextPtr = StringTextPtr + StringSize; CurrentStringId++; } break; case EFI_HII_SIBT_STRINGS_UCS2_FONT: Offset = sizeof (EFI_HII_SIBT_STRINGS_UCS2_FONT_BLOCK) - sizeof (CHAR16); StringTextPtr = BlockHdr + Offset; BlockSize += Offset; CopyMem ( &StringCount, (UINT8 *)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK) + sizeof (UINT8)), sizeof (UINT16) ); for (Index = 0; Index < StringCount; Index++) { GetUnicodeStringTextAndSize (StringTextPtr, &StringSize, &String); if (FindString) { *KeywordValue = String; return CurrentStringId; } else if (CurrentStringId == StringId) { FindString = TRUE; } BlockSize += StringSize; StringTextPtr = StringTextPtr + StringSize; CurrentStringId++; } break; case EFI_HII_SIBT_DUPLICATE: BlockSize += sizeof (EFI_HII_SIBT_DUPLICATE_BLOCK); CurrentStringId++; break; case EFI_HII_SIBT_SKIP1: SkipCount = (UINT16)(*(UINT8 *)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK))); CurrentStringId = (UINT16)(CurrentStringId + SkipCount); BlockSize += sizeof (EFI_HII_SIBT_SKIP1_BLOCK); break; case EFI_HII_SIBT_SKIP2: CopyMem (&SkipCount, BlockHdr + sizeof (EFI_HII_STRING_BLOCK), sizeof (UINT16)); CurrentStringId = (UINT16)(CurrentStringId + SkipCount); BlockSize += sizeof (EFI_HII_SIBT_SKIP2_BLOCK); break; case EFI_HII_SIBT_EXT1: CopyMem ( &Length8, (UINT8 *)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK) + sizeof (UINT8)), sizeof (UINT8) ); BlockSize += Length8; break; case EFI_HII_SIBT_EXT2: CopyMem (&Ext2, BlockHdr, sizeof (EFI_HII_SIBT_EXT2_BLOCK)); BlockSize += Ext2.Length; break; case EFI_HII_SIBT_EXT4: CopyMem ( &Length32, (UINT8 *)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK) + sizeof (UINT8)), sizeof (UINT32) ); BlockSize += Length32; break; default: break; } if (String != NULL) { FreePool (String); String = NULL; } BlockHdr = StringPackage->StringBlock + BlockSize; } return 0; } /** Get string package from the input NameSpace string. This is a internal function. @param DatabaseRecord HII_DATABASE_RECORD format string. @param NameSpace NameSpace format string. @param KeywordValue Keyword value. @param StringId String Id for this keyword. @retval KEYWORD_HANDLER_NO_ERROR Get String id successfully. @retval KEYWORD_HANDLER_KEYWORD_NOT_FOUND Not found the string id in the string package. @retval KEYWORD_HANDLER_NAMESPACE_ID_NOT_FOUND Not found the string package for this namespace. @retval KEYWORD_HANDLER_UNDEFINED_PROCESSING_ERROR Out of resource error. **/ UINT32 GetStringIdFromRecord ( IN HII_DATABASE_RECORD *DatabaseRecord, IN CHAR8 **NameSpace, IN CHAR16 *KeywordValue, OUT EFI_STRING_ID *StringId ) { LIST_ENTRY *Link; HII_DATABASE_PACKAGE_LIST_INSTANCE *PackageListNode; HII_STRING_PACKAGE_INSTANCE *StringPackage; EFI_STATUS Status; CHAR8 *Name; UINT32 RetVal; ASSERT (DatabaseRecord != NULL && NameSpace != NULL && KeywordValue != NULL); PackageListNode = DatabaseRecord->PackageList; RetVal = KEYWORD_HANDLER_NAMESPACE_ID_NOT_FOUND; if (*NameSpace != NULL) { Name = *NameSpace; } else { Name = UEFI_CONFIG_LANG; } for (Link = PackageListNode->StringPkgHdr.ForwardLink; Link != &PackageListNode->StringPkgHdr; Link = Link->ForwardLink) { StringPackage = CR (Link, HII_STRING_PACKAGE_INSTANCE, StringEntry, HII_STRING_PACKAGE_SIGNATURE); if (AsciiStrnCmp (Name, StringPackage->StringPkgHdr->Language, AsciiStrLen (Name)) == 0) { Status = GetStringIdFromString (StringPackage, KeywordValue, StringId); if (EFI_ERROR (Status)) { return KEYWORD_HANDLER_KEYWORD_NOT_FOUND; } else { if (*NameSpace == NULL) { *NameSpace = AllocateCopyPool (AsciiStrSize (StringPackage->StringPkgHdr->Language), StringPackage->StringPkgHdr->Language); if (*NameSpace == NULL) { return KEYWORD_HANDLER_UNDEFINED_PROCESSING_ERROR; } } return KEYWORD_HANDLER_NO_ERROR; } } } return RetVal; } /** Tell whether this Operand is an Statement OpCode. @param Operand Operand of an IFR OpCode. @retval TRUE This is an Statement OpCode. @retval FALSE Not an Statement OpCode. **/ BOOLEAN IsStatementOpCode ( IN UINT8 Operand ) { if ((Operand == EFI_IFR_SUBTITLE_OP) || (Operand == EFI_IFR_TEXT_OP) || (Operand == EFI_IFR_RESET_BUTTON_OP) || (Operand == EFI_IFR_REF_OP) || (Operand == EFI_IFR_ACTION_OP) || (Operand == EFI_IFR_NUMERIC_OP) || (Operand == EFI_IFR_ORDERED_LIST_OP) || (Operand == EFI_IFR_CHECKBOX_OP) || (Operand == EFI_IFR_STRING_OP) || (Operand == EFI_IFR_PASSWORD_OP) || (Operand == EFI_IFR_DATE_OP) || (Operand == EFI_IFR_TIME_OP) || (Operand == EFI_IFR_GUID_OP) || (Operand == EFI_IFR_ONE_OF_OP)) { return TRUE; } return FALSE; } /** Tell whether this Operand is an Statement OpCode. @param Operand Operand of an IFR OpCode. @retval TRUE This is an Statement OpCode. @retval FALSE Not an Statement OpCode. **/ BOOLEAN IsStorageOpCode ( IN UINT8 Operand ) { if ((Operand == EFI_IFR_VARSTORE_OP) || (Operand == EFI_IFR_VARSTORE_NAME_VALUE_OP) || (Operand == EFI_IFR_VARSTORE_EFI_OP)) { return TRUE; } return FALSE; } /** Base on the prompt string id to find the question. @param FormPackage The input form package. @param KeywordStrId The input prompt string id for one question. @retval the opcode for the question. **/ UINT8 * FindQuestionFromStringId ( IN HII_IFR_PACKAGE_INSTANCE *FormPackage, IN EFI_STRING_ID KeywordStrId ) { UINT8 *OpCodeData; UINT32 Offset; EFI_IFR_STATEMENT_HEADER *StatementHeader; EFI_IFR_OP_HEADER *OpCodeHeader; UINT32 FormDataLen; ASSERT (FormPackage != NULL); FormDataLen = FormPackage->FormPkgHdr.Length - sizeof (EFI_HII_PACKAGE_HEADER); Offset = 0; while (Offset < FormDataLen) { OpCodeData = FormPackage->IfrData + Offset; OpCodeHeader = (EFI_IFR_OP_HEADER *)OpCodeData; if (IsStatementOpCode (OpCodeHeader->OpCode)) { StatementHeader = (EFI_IFR_STATEMENT_HEADER *)(OpCodeData + sizeof (EFI_IFR_OP_HEADER)); if (StatementHeader->Prompt == KeywordStrId) { return OpCodeData; } } Offset += OpCodeHeader->Length; } return NULL; } /** Base on the varstore id to find the storage info. @param FormPackage The input form package. @param VarStoreId The input storage id. @retval the opcode for the storage. **/ UINT8 * FindStorageFromVarId ( IN HII_IFR_PACKAGE_INSTANCE *FormPackage, IN EFI_VARSTORE_ID VarStoreId ) { UINT8 *OpCodeData; UINT32 Offset; EFI_IFR_OP_HEADER *OpCodeHeader; UINT32 FormDataLen; ASSERT (FormPackage != NULL); FormDataLen = FormPackage->FormPkgHdr.Length - sizeof (EFI_HII_PACKAGE_HEADER); Offset = 0; while (Offset < FormDataLen) { OpCodeData = FormPackage->IfrData + Offset; OpCodeHeader = (EFI_IFR_OP_HEADER *)OpCodeData; if (IsStorageOpCode (OpCodeHeader->OpCode)) { switch (OpCodeHeader->OpCode) { case EFI_IFR_VARSTORE_OP: if (VarStoreId == ((EFI_IFR_VARSTORE *)OpCodeData)->VarStoreId) { return OpCodeData; } break; case EFI_IFR_VARSTORE_NAME_VALUE_OP: if (VarStoreId == ((EFI_IFR_VARSTORE_NAME_VALUE *)OpCodeData)->VarStoreId) { return OpCodeData; } break; case EFI_IFR_VARSTORE_EFI_OP: if (VarStoreId == ((EFI_IFR_VARSTORE_EFI *)OpCodeData)->VarStoreId) { return OpCodeData; } break; default: break; } } Offset += OpCodeHeader->Length; } return NULL; } /** Get width info for one question. @param OpCodeData The input opcode for one question. @retval the width info for one question. **/ UINT16 GetWidth ( IN UINT8 *OpCodeData ) { UINT8 *NextOpCodeData; ASSERT (OpCodeData != NULL); switch (((EFI_IFR_OP_HEADER *)OpCodeData)->OpCode) { case EFI_IFR_REF_OP: return (UINT16)sizeof (EFI_HII_REF); case EFI_IFR_ONE_OF_OP: case EFI_IFR_NUMERIC_OP: switch (((EFI_IFR_ONE_OF *)OpCodeData)->Flags & EFI_IFR_NUMERIC_SIZE) { case EFI_IFR_NUMERIC_SIZE_1: return (UINT16)sizeof (UINT8); case EFI_IFR_NUMERIC_SIZE_2: return (UINT16)sizeof (UINT16); case EFI_IFR_NUMERIC_SIZE_4: return (UINT16)sizeof (UINT32); case EFI_IFR_NUMERIC_SIZE_8: return (UINT16)sizeof (UINT64); default: ASSERT (FALSE); return 0; } case EFI_IFR_ORDERED_LIST_OP: NextOpCodeData = OpCodeData + ((EFI_IFR_ORDERED_LIST *)OpCodeData)->Header.Length; // // OneOfOption must follow the orderedlist opcode. // ASSERT (((EFI_IFR_OP_HEADER *)NextOpCodeData)->OpCode == EFI_IFR_ONE_OF_OPTION_OP); switch (((EFI_IFR_ONE_OF_OPTION *)NextOpCodeData)->Type) { case EFI_IFR_TYPE_NUM_SIZE_8: return (UINT16)sizeof (UINT8) * ((EFI_IFR_ORDERED_LIST *)OpCodeData)->MaxContainers; case EFI_IFR_TYPE_NUM_SIZE_16: return (UINT16)sizeof (UINT16) * ((EFI_IFR_ORDERED_LIST *)OpCodeData)->MaxContainers; case EFI_IFR_TYPE_NUM_SIZE_32: return (UINT16)sizeof (UINT32) * ((EFI_IFR_ORDERED_LIST *)OpCodeData)->MaxContainers; case EFI_IFR_TYPE_NUM_SIZE_64: return (UINT16)sizeof (UINT64) * ((EFI_IFR_ORDERED_LIST *)OpCodeData)->MaxContainers; default: ASSERT (FALSE); return 0; } case EFI_IFR_CHECKBOX_OP: return (UINT16)sizeof (BOOLEAN); case EFI_IFR_PASSWORD_OP: return (UINT16)((UINTN)((EFI_IFR_PASSWORD *)OpCodeData)->MaxSize * sizeof (CHAR16)); case EFI_IFR_STRING_OP: return (UINT16)((UINTN)((EFI_IFR_STRING *)OpCodeData)->MaxSize * sizeof (CHAR16)); case EFI_IFR_DATE_OP: return (UINT16)sizeof (EFI_HII_DATE); case EFI_IFR_TIME_OP: return (UINT16)sizeof (EFI_HII_TIME); default: ASSERT (FALSE); return 0; } } /** Converts all hex string characters in range ['A'..'F'] to ['a'..'f'] for hex digits that appear between a '=' and a '&' in a config string. If ConfigString is NULL, then ASSERT(). @param[in] ConfigString Pointer to a Null-terminated Unicode string. @return Pointer to the Null-terminated Unicode result string. **/ EFI_STRING EFIAPI InternalLowerConfigString ( IN EFI_STRING ConfigString ) { EFI_STRING String; BOOLEAN Lower; ASSERT (ConfigString != NULL); // // Convert all hex digits in range [A-F] in the configuration header to [a-f] // for (String = ConfigString, Lower = FALSE; *String != L'\0'; String++) { if (*String == L'=') { Lower = TRUE; } else if (*String == L'&') { Lower = FALSE; } else if (Lower && (*String >= L'A') && (*String <= L'F')) { *String = (CHAR16)(*String - L'A' + L'a'); } } return ConfigString; } /** Allocates and returns a Null-terminated Unicode string. The format of a is as follows: GUID=32&NAME=NameLength&PATH=DevicePathSize @param[in] OpCodeData The opcode for the storage. @param[in] DriverHandle The driver handle which supports a Device Path Protocol that is the routing information PATH. Each byte of the Device Path associated with DriverHandle is converted to a 2 Unicode character hexadecimal string. @retval NULL DriverHandle does not support the Device Path Protocol. @retval Other A pointer to the Null-terminate Unicode string **/ EFI_STRING ConstructConfigHdr ( IN UINT8 *OpCodeData, IN EFI_HANDLE DriverHandle ) { UINTN NameLength; EFI_DEVICE_PATH_PROTOCOL *DevicePath; UINTN DevicePathSize; CHAR16 *String; CHAR16 *ReturnString; UINTN Index; UINT8 *Buffer; CHAR16 *Name; CHAR8 *AsciiName; UINTN NameSize; EFI_GUID *Guid; UINTN MaxLen; ASSERT (OpCodeData != NULL); switch (((EFI_IFR_OP_HEADER *)OpCodeData)->OpCode) { case EFI_IFR_VARSTORE_OP: Guid = (EFI_GUID *)(UINTN *)&((EFI_IFR_VARSTORE *)OpCodeData)->Guid; AsciiName = (CHAR8 *)((EFI_IFR_VARSTORE *)OpCodeData)->Name; break; case EFI_IFR_VARSTORE_NAME_VALUE_OP: Guid = (EFI_GUID *)(UINTN *)&((EFI_IFR_VARSTORE_NAME_VALUE *)OpCodeData)->Guid; AsciiName = NULL; break; case EFI_IFR_VARSTORE_EFI_OP: Guid = (EFI_GUID *)(UINTN *)&((EFI_IFR_VARSTORE_EFI *)OpCodeData)->Guid; AsciiName = (CHAR8 *)((EFI_IFR_VARSTORE_EFI *)OpCodeData)->Name; break; default: ASSERT (FALSE); Guid = NULL; AsciiName = NULL; break; } if (AsciiName != NULL) { NameSize = AsciiStrSize (AsciiName); Name = AllocateZeroPool (NameSize * sizeof (CHAR16)); ASSERT (Name != NULL); AsciiStrToUnicodeStrS (AsciiName, Name, NameSize); } else { Name = NULL; } // // Compute the length of Name in Unicode characters. // If Name is NULL, then the length is 0. // NameLength = 0; if (Name != NULL) { NameLength = StrLen (Name); } DevicePath = NULL; DevicePathSize = 0; // // Retrieve DevicePath Protocol associated with DriverHandle // if (DriverHandle != NULL) { DevicePath = DevicePathFromHandle (DriverHandle); if (DevicePath == NULL) { return NULL; } // // Compute the size of the device path in bytes // DevicePathSize = GetDevicePathSize (DevicePath); } // // GUID=32&NAME=NameLength&PATH=DevicePathSize // | 5 | sizeof (EFI_GUID) * 2 | 6 | NameStrLen*4 | 6 | DevicePathSize * 2 | 1 | // MaxLen = 5 + sizeof (EFI_GUID) * 2 + 6 + NameLength * 4 + 6 + DevicePathSize * 2 + 1; String = AllocateZeroPool (MaxLen * sizeof (CHAR16)); if (String == NULL) { return NULL; } // // Start with L"GUID=" // StrCpyS (String, MaxLen, L"GUID="); ReturnString = String; String += StrLen (String); if (Guid != NULL) { // // Append Guid converted to 32 // for (Index = 0, Buffer = (UINT8 *)Guid; Index < sizeof (EFI_GUID); Index++) { UnicodeValueToStringS ( String, MaxLen * sizeof (CHAR16) - ((UINTN)String - (UINTN)ReturnString), PREFIX_ZERO | RADIX_HEX, *(Buffer++), 2 ); String += StrnLenS (String, MaxLen - ((UINTN)String - (UINTN)ReturnString) / sizeof (CHAR16)); } } // // Append L"&NAME=" // StrCatS (ReturnString, MaxLen, L"&NAME="); String += StrLen (String); if (Name != NULL) { // // Append Name converted to NameLength // for ( ; *Name != L'\0'; Name++) { UnicodeValueToStringS ( String, MaxLen * sizeof (CHAR16) - ((UINTN)String - (UINTN)ReturnString), PREFIX_ZERO | RADIX_HEX, *Name, 4 ); String += StrnLenS (String, MaxLen - ((UINTN)String - (UINTN)ReturnString) / sizeof (CHAR16)); } } // // Append L"&PATH=" // StrCatS (ReturnString, MaxLen, L"&PATH="); String += StrLen (String); // // Append the device path associated with DriverHandle converted to DevicePathSize // for (Index = 0, Buffer = (UINT8 *)DevicePath; Index < DevicePathSize; Index++) { UnicodeValueToStringS ( String, MaxLen * sizeof (CHAR16) - ((UINTN)String - (UINTN)ReturnString), PREFIX_ZERO | RADIX_HEX, *(Buffer++), 2 ); String += StrnLenS (String, MaxLen - ((UINTN)String - (UINTN)ReturnString) / sizeof (CHAR16)); } // // Null terminate the Unicode string // *String = L'\0'; // // Convert all hex digits in range [A-F] in the configuration header to [a-f] // return InternalLowerConfigString (ReturnString); } /** Generate the Config request element for one question. @param Name The name info for one question. @param Offset The offset info for one question. @param Width The width info for one question. @return Pointer to the Null-terminated Unicode request element string. **/ EFI_STRING ConstructRequestElement ( IN CHAR16 *Name, IN UINT16 Offset, IN UINT16 Width ) { CHAR16 *StringPtr; UINTN Length; if (Name != NULL) { // // Add length for each Name // // ::= Name + \0 // StrLen(Name) | 1 // Length = StrLen (Name) + 1; } else { // // Add length for each Offset/Width pair // // ::= OFFSET=1234&WIDTH=1234 + \0 // | 7 | 4 | 7 | 4 | 1 // Length = (7 + 4 + 7 + 4 + 1); } // // Allocate buffer for the entire // StringPtr = AllocateZeroPool (Length * sizeof (CHAR16)); ASSERT (StringPtr != NULL); if (Name != NULL) { // // Append Name\0 // UnicodeSPrint ( StringPtr, (StrLen (Name) + 1) * sizeof (CHAR16), L"%s", Name ); } else { // // Append OFFSET=XXXX&WIDTH=YYYY\0 // UnicodeSPrint ( StringPtr, (7 + 4 + 7 + 4 + 1) * sizeof (CHAR16), L"OFFSET=%04X&WIDTH=%04X", Offset, Width ); } return StringPtr; } /** Get string value for question's name field. @param DatabaseRecord HII_DATABASE_RECORD format string. @param NameId The string id for the name field. @retval Name string. **/ CHAR16 * GetNameFromId ( IN HII_DATABASE_RECORD *DatabaseRecord, IN EFI_STRING_ID NameId ) { CHAR16 *Name; CHAR8 *PlatformLanguage; CHAR8 *SupportedLanguages; CHAR8 *BestLanguage; UINTN StringSize; CHAR16 TempString; EFI_STATUS Status; Name = NULL; BestLanguage = NULL; PlatformLanguage = NULL; SupportedLanguages = NULL; GetEfiGlobalVariable2 (L"PlatformLang", (VOID **)&PlatformLanguage, NULL); SupportedLanguages = GetSupportedLanguages (DatabaseRecord->Handle); // // Get the best matching language from SupportedLanguages // BestLanguage = GetBestLanguage ( SupportedLanguages, FALSE, // RFC 4646 mode PlatformLanguage != NULL ? PlatformLanguage : "", // Highest priority SupportedLanguages, // Lowest priority NULL ); if (BestLanguage == NULL) { BestLanguage = AllocateCopyPool (sizeof ("en-US"), "en-US"); ASSERT (BestLanguage != NULL); } StringSize = 0; Status = mPrivate.HiiString.GetString ( &mPrivate.HiiString, BestLanguage, DatabaseRecord->Handle, NameId, &TempString, &StringSize, NULL ); if (Status != EFI_BUFFER_TOO_SMALL) { goto Done; } Name = AllocateZeroPool (StringSize); if (Name == NULL) { goto Done; } Status = mPrivate.HiiString.GetString ( &mPrivate.HiiString, BestLanguage, DatabaseRecord->Handle, NameId, Name, &StringSize, NULL ); if (EFI_ERROR (Status)) { FreePool (Name); Name = NULL; goto Done; } Done: if (SupportedLanguages != NULL) { FreePool (SupportedLanguages); } if (BestLanguage != NULL) { FreePool (BestLanguage); } if (PlatformLanguage != NULL) { FreePool (PlatformLanguage); } return Name; } /** Base on the input parameter to generate the ConfigRequest string. This is a internal function. @param DatabaseRecord HII_DATABASE_RECORD format string. @param KeywordStrId Keyword string id. @param OpCodeData The IFR data for this question. @param ConfigRequest Return the generate ConfigRequest string. @retval EFI_SUCCESS Generate ConfigResp string success. @retval EFI_OUT_OF_RESOURCES System out of memory resource error. @retval EFI_NOT_FOUND Not found the question which use this string id as the prompt string id. **/ EFI_STATUS ExtractConfigRequest ( IN HII_DATABASE_RECORD *DatabaseRecord, IN EFI_STRING_ID KeywordStrId, OUT UINT8 **OpCodeData, OUT EFI_STRING *ConfigRequest ) { LIST_ENTRY *Link; HII_DATABASE_PACKAGE_LIST_INSTANCE *PackageListNode; HII_IFR_PACKAGE_INSTANCE *FormPackage; EFI_IFR_QUESTION_HEADER *Header; UINT8 *Storage; UINT8 *OpCode; CHAR16 *Name; UINT16 Offset; UINT16 Width; CHAR16 *ConfigHdr; CHAR16 *RequestElement; UINTN MaxLen; CHAR16 *StringPtr; ASSERT (DatabaseRecord != NULL && OpCodeData != NULL && ConfigRequest != NULL); OpCode = NULL; Name = NULL; Width = 0; Offset = 0; PackageListNode = DatabaseRecord->PackageList; // // Search the languages in the specified packagelist. // for (Link = PackageListNode->FormPkgHdr.ForwardLink; Link != &PackageListNode->FormPkgHdr; Link = Link->ForwardLink) { FormPackage = CR (Link, HII_IFR_PACKAGE_INSTANCE, IfrEntry, HII_IFR_PACKAGE_SIGNATURE); OpCode = FindQuestionFromStringId (FormPackage, KeywordStrId); if (OpCode != NULL) { *OpCodeData = OpCode; Header = (EFI_IFR_QUESTION_HEADER *)(OpCode + sizeof (EFI_IFR_OP_HEADER)); // // Header->VarStoreId == 0 means no storage for this question. // ASSERT (Header->VarStoreId != 0); DEBUG ((DEBUG_INFO, "Varstore Id: 0x%x\n", Header->VarStoreId)); Storage = FindStorageFromVarId (FormPackage, Header->VarStoreId); ASSERT (Storage != NULL); if (((EFI_IFR_OP_HEADER *)Storage)->OpCode == EFI_IFR_VARSTORE_NAME_VALUE_OP) { Name = GetNameFromId (DatabaseRecord, Header->VarStoreInfo.VarName); } else { Offset = Header->VarStoreInfo.VarOffset; Width = GetWidth (OpCode); } RequestElement = ConstructRequestElement (Name, Offset, Width); ConfigHdr = ConstructConfigHdr (Storage, DatabaseRecord->DriverHandle); ASSERT (ConfigHdr != NULL); MaxLen = StrLen (ConfigHdr) + 1 + StrLen (RequestElement) + 1; *ConfigRequest = AllocatePool (MaxLen * sizeof (CHAR16)); if (*ConfigRequest == NULL) { FreePool (ConfigHdr); FreePool (RequestElement); return EFI_OUT_OF_RESOURCES; } StringPtr = *ConfigRequest; StrCpyS (StringPtr, MaxLen, ConfigHdr); StrCatS (StringPtr, MaxLen, L"&"); StrCatS (StringPtr, MaxLen, RequestElement); FreePool (ConfigHdr); FreePool (RequestElement); return EFI_SUCCESS; } } return EFI_NOT_FOUND; } /** Base on the input parameter to generate the ConfigResp string. This is a internal function. @param DatabaseRecord HII_DATABASE_RECORD format string. @param KeywordStrId Keyword string id. @param ValueElement The value for the question which use keyword string id as the prompt string id. @param OpCodeData The IFR data for this question. @param ConfigResp Return the generate ConfigResp string. @retval EFI_SUCCESS Generate ConfigResp string success. @retval EFI_OUT_OF_RESOURCES System out of memory resource error. @retval EFI_NOT_FOUND Not found the question which use this string id as the prompt string id. **/ EFI_STATUS ExtractConfigResp ( IN HII_DATABASE_RECORD *DatabaseRecord, IN EFI_STRING_ID KeywordStrId, IN EFI_STRING ValueElement, OUT UINT8 **OpCodeData, OUT EFI_STRING *ConfigResp ) { LIST_ENTRY *Link; HII_DATABASE_PACKAGE_LIST_INSTANCE *PackageListNode; HII_IFR_PACKAGE_INSTANCE *FormPackage; EFI_IFR_QUESTION_HEADER *Header; UINT8 *Storage; UINT8 *OpCode; CHAR16 *Name; UINT16 Offset; UINT16 Width; CHAR16 *ConfigHdr; CHAR16 *RequestElement; UINTN MaxLen; CHAR16 *StringPtr; ASSERT ((DatabaseRecord != NULL) && (OpCodeData != NULL) && (ConfigResp != NULL) && (ValueElement != NULL)); OpCode = NULL; Name = NULL; Width = 0; Offset = 0; PackageListNode = DatabaseRecord->PackageList; // // Search the languages in the specified packagelist. // for (Link = PackageListNode->FormPkgHdr.ForwardLink; Link != &PackageListNode->FormPkgHdr; Link = Link->ForwardLink) { FormPackage = CR (Link, HII_IFR_PACKAGE_INSTANCE, IfrEntry, HII_IFR_PACKAGE_SIGNATURE); OpCode = FindQuestionFromStringId (FormPackage, KeywordStrId); if (OpCode != NULL) { *OpCodeData = OpCode; Header = (EFI_IFR_QUESTION_HEADER *)(OpCode + sizeof (EFI_IFR_OP_HEADER)); // // Header->VarStoreId == 0 means no storage for this question. // ASSERT (Header->VarStoreId != 0); DEBUG ((DEBUG_INFO, "Varstore Id: 0x%x\n", Header->VarStoreId)); Storage = FindStorageFromVarId (FormPackage, Header->VarStoreId); ASSERT (Storage != NULL); if (((EFI_IFR_OP_HEADER *)Storage)->OpCode == EFI_IFR_VARSTORE_NAME_VALUE_OP) { Name = GetNameFromId (DatabaseRecord, Header->VarStoreInfo.VarName); } else { Offset = Header->VarStoreInfo.VarOffset; Width = GetWidth (OpCode); } RequestElement = ConstructRequestElement (Name, Offset, Width); ConfigHdr = ConstructConfigHdr (Storage, DatabaseRecord->DriverHandle); ASSERT (ConfigHdr != NULL); MaxLen = StrLen (ConfigHdr) + 1 + StrLen (RequestElement) + 1 + StrLen (L"VALUE=") + StrLen (ValueElement) + 1; *ConfigResp = AllocatePool (MaxLen * sizeof (CHAR16)); if (*ConfigResp == NULL) { FreePool (ConfigHdr); FreePool (RequestElement); return EFI_OUT_OF_RESOURCES; } StringPtr = *ConfigResp; StrCpyS (StringPtr, MaxLen, ConfigHdr); StrCatS (StringPtr, MaxLen, L"&"); StrCatS (StringPtr, MaxLen, RequestElement); StrCatS (StringPtr, MaxLen, L"&"); StrCatS (StringPtr, MaxLen, L"VALUE="); StrCatS (StringPtr, MaxLen, ValueElement); FreePool (ConfigHdr); FreePool (RequestElement); return EFI_SUCCESS; } } return EFI_NOT_FOUND; } /** Get the Value section from the Hii driver. This is a internal function. @param ConfigRequest The input ConfigRequest string. @param ValueElement The respond Value section from the hii driver. @retval Misc value The error status return from ExtractConfig function. @retval EFI_OUT_OF_RESOURCES The memory can't be allocated @retval EFI_SUCCESS Get the value section success. **/ EFI_STATUS ExtractValueFromDriver ( IN CHAR16 *ConfigRequest, OUT CHAR16 **ValueElement ) { EFI_STATUS Status; EFI_STRING Result; EFI_STRING Progress; CHAR16 *StringPtr; CHAR16 *StringEnd; ASSERT ((ConfigRequest != NULL) && (ValueElement != NULL)); Status = mPrivate.ConfigRouting.ExtractConfig ( &mPrivate.ConfigRouting, (EFI_STRING)ConfigRequest, &Progress, &Result ); if (EFI_ERROR (Status)) { return Status; } // // Find Value Section and return it. // StringPtr = StrStr (Result, L"&VALUE="); ASSERT (StringPtr != NULL); StringEnd = StrStr (StringPtr + 1, L"&"); if (StringEnd != NULL) { *StringEnd = L'\0'; } *ValueElement = AllocateCopyPool (StrSize (StringPtr), StringPtr); if (*ValueElement == NULL) { return EFI_OUT_OF_RESOURCES; } if (StringEnd != NULL) { *StringEnd = L'&'; } FreePool (Result); return EFI_SUCCESS; } /** Get EFI_STRING_ID info from the input device path, namespace and keyword. This is a internal function. @param DevicePath Input device path info. @param NameSpace NameSpace format string. @param KeywordData Keyword used to get string id. @param ProgressErr Return extra error type. @param KeywordStringId Return EFI_STRING_ID. @param DataBaseRecord DataBase record data for this driver. @retval EFI_INVALID_PARAMETER Can't find the database record base on the input device path or namespace. @retval EFI_NOT_FOUND Can't find the EFI_STRING_ID for the keyword. @retval EFI_SUCCESS Find the EFI_STRING_ID. **/ EFI_STATUS GetStringIdFromDatabase ( IN EFI_DEVICE_PATH_PROTOCOL **DevicePath, IN CHAR8 **NameSpace, IN CHAR16 *KeywordData, OUT UINT32 *ProgressErr, OUT EFI_STRING_ID *KeywordStringId, OUT HII_DATABASE_RECORD **DataBaseRecord ) { HII_DATABASE_RECORD *Record; LIST_ENTRY *Link; BOOLEAN FindNameSpace; EFI_DEVICE_PATH_PROTOCOL *DestDevicePath; UINT8 *DevicePathPkg; UINTN DevicePathSize; ASSERT ((NameSpace != NULL) && (KeywordData != NULL) && (ProgressErr != NULL) && (KeywordStringId != NULL) && (DataBaseRecord != NULL)); FindNameSpace = FALSE; if (*DevicePath != NULL) { // // Get DataBaseRecord from device path protocol. // Record = GetRecordFromDevicePath (*DevicePath); if (Record == NULL) { // // Can't find the DatabaseRecord base on the input device path info. // NEED TO CONFIRM the return ProgressErr. // *ProgressErr = KEYWORD_HANDLER_MALFORMED_STRING; return EFI_INVALID_PARAMETER; } // // Get string id from the record. // *ProgressErr = GetStringIdFromRecord (Record, NameSpace, KeywordData, KeywordStringId); switch (*ProgressErr) { case KEYWORD_HANDLER_NO_ERROR: *DataBaseRecord = Record; return EFI_SUCCESS; case KEYWORD_HANDLER_NAMESPACE_ID_NOT_FOUND: return EFI_INVALID_PARAMETER; default: ASSERT (*ProgressErr == KEYWORD_HANDLER_KEYWORD_NOT_FOUND); return EFI_NOT_FOUND; } } else { // // Find driver which matches the routing data. // for (Link = mPrivate.DatabaseList.ForwardLink; Link != &mPrivate.DatabaseList; Link = Link->ForwardLink) { Record = CR (Link, HII_DATABASE_RECORD, DatabaseEntry, HII_DATABASE_RECORD_SIGNATURE); *ProgressErr = GetStringIdFromRecord (Record, NameSpace, KeywordData, KeywordStringId); if (*ProgressErr == KEYWORD_HANDLER_NO_ERROR) { *DataBaseRecord = Record; if ((DevicePathPkg = Record->PackageList->DevicePathPkg) != NULL) { DestDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)(DevicePathPkg + sizeof (EFI_HII_PACKAGE_HEADER)); DevicePathSize = GetDevicePathSize ((EFI_DEVICE_PATH_PROTOCOL *)DestDevicePath); *DevicePath = AllocateCopyPool (DevicePathSize, DestDevicePath); if (*DevicePath == NULL) { return EFI_OUT_OF_RESOURCES; } } else { // // Need to verify this ASSERT. // ASSERT (FALSE); } return EFI_SUCCESS; } else if (*ProgressErr == KEYWORD_HANDLER_UNDEFINED_PROCESSING_ERROR) { return EFI_OUT_OF_RESOURCES; } else if (*ProgressErr == KEYWORD_HANDLER_KEYWORD_NOT_FOUND) { FindNameSpace = TRUE; } } // // When PathHdr not input, if ever find the namespace, will return KEYWORD_HANDLER_KEYWORD_NOT_FOUND. // This is a bit more progress than KEYWORD_HANDLER_NAMESPACE_ID_NOT_FOUND. // if (FindNameSpace) { return EFI_NOT_FOUND; } else { return EFI_INVALID_PARAMETER; } } } /** Generate the KeywordResp String. ::= '&''&VALUE='['&READONLY'] @param NameSpace NameSpace format string. @param DevicePath Input device path info. @param KeywordData Keyword used to get string id. @param ValueStr The value section for the keyword. @param ReadOnly Whether this value is readonly. @param KeywordResp Return the point to the KeywordResp string. @retval EFI_OUT_OF_RESOURCES The memory can't be allocated. @retval EFI_SUCCESS Generate the KeywordResp string. **/ EFI_STATUS GenerateKeywordResp ( IN CHAR8 *NameSpace, IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, IN EFI_STRING KeywordData, IN EFI_STRING ValueStr, IN BOOLEAN ReadOnly, OUT EFI_STRING *KeywordResp ) { UINTN RespStrLen; CHAR16 *RespStr; CHAR16 *PathHdr; CHAR16 *UnicodeNameSpace; UINTN NameSpaceLength; ASSERT ((NameSpace != NULL) && (DevicePath != NULL) && (KeywordData != NULL) && (ValueStr != NULL) && (KeywordResp != NULL)); // // 1. Calculate the string length. // // // 1.1 NameSpaceId size. // 'NAMESPACE=' // NameSpaceLength = AsciiStrLen (NameSpace); RespStrLen = 10 + NameSpaceLength; UnicodeNameSpace = AllocatePool ((NameSpaceLength + 1) * sizeof (CHAR16)); if (UnicodeNameSpace == NULL) { return EFI_OUT_OF_RESOURCES; } AsciiStrToUnicodeStrS (NameSpace, UnicodeNameSpace, NameSpaceLength + 1); // // 1.2 PathHdr size. // PATH='&' // Attention: The output include the '&' at the end. // GenerateSubStr ( L"&PATH=", GetDevicePathSize ((EFI_DEVICE_PATH_PROTOCOL *)DevicePath), (VOID *)DevicePath, 1, &PathHdr ); RespStrLen += StrLen (PathHdr); // // 1.3 Keyword section. // 'KEYWORD='[':'(1/4)] // RespStrLen += 8 + StrLen (KeywordData); // // 1.4 Value section. // ValueStr = '&VALUE=' // RespStrLen += StrLen (ValueStr); // // 1.5 ReadOnly Section. // '&READONLY' // if (ReadOnly) { RespStrLen += 9; } // // 2. Allocate the buffer and create the KeywordResp string include '\0'. // RespStrLen += 1; *KeywordResp = AllocatePool (RespStrLen * sizeof (CHAR16)); if (*KeywordResp == NULL) { if (UnicodeNameSpace != NULL) { FreePool (UnicodeNameSpace); } return EFI_OUT_OF_RESOURCES; } RespStr = *KeywordResp; // // 2.1 Copy NameSpaceId section. // StrCpyS (RespStr, RespStrLen, L"NAMESPACE="); StrCatS (RespStr, RespStrLen, UnicodeNameSpace); // // 2.2 Copy PathHdr section. // StrCatS (RespStr, RespStrLen, PathHdr); // // 2.3 Copy Keyword section. // StrCatS (RespStr, RespStrLen, L"KEYWORD="); StrCatS (RespStr, RespStrLen, KeywordData); // // 2.4 Copy the Value section. // StrCatS (RespStr, RespStrLen, ValueStr); // // 2.5 Copy ReadOnly section if exist. // if (ReadOnly) { StrCatS (RespStr, RespStrLen, L"&READONLY"); } if (UnicodeNameSpace != NULL) { FreePool (UnicodeNameSpace); } if (PathHdr != NULL) { FreePool (PathHdr); } return EFI_SUCCESS; } /** Merge the KeywordResp String to MultiKeywordResp string. This is a internal function. @param MultiKeywordResp The existed multikeywordresp string. @param KeywordResp The input keywordResp string. @retval EFI_OUT_OF_RESOURCES The memory can't be allocated. @retval EFI_SUCCESS Generate the MultiKeywordResp string. **/ EFI_STATUS MergeToMultiKeywordResp ( IN OUT EFI_STRING *MultiKeywordResp, IN EFI_STRING *KeywordResp ) { UINTN MultiKeywordRespLen; EFI_STRING StringPtr; if (*MultiKeywordResp == NULL) { *MultiKeywordResp = *KeywordResp; *KeywordResp = NULL; return EFI_SUCCESS; } MultiKeywordRespLen = (StrLen (*MultiKeywordResp) + 1 + StrLen (*KeywordResp) + 1) * sizeof (CHAR16); StringPtr = ReallocatePool ( StrSize (*MultiKeywordResp), MultiKeywordRespLen, *MultiKeywordResp ); if (StringPtr == NULL) { return EFI_OUT_OF_RESOURCES; } *MultiKeywordResp = StringPtr; StrCatS (StringPtr, MultiKeywordRespLen / sizeof (CHAR16), L"&"); StrCatS (StringPtr, MultiKeywordRespLen / sizeof (CHAR16), *KeywordResp); return EFI_SUCCESS; } /** Enumerate all keyword in the system. If error occur when parse one keyword, just skip it and parse the next one. This is a internal function. @param NameSpace The namespace used to search the string. @param MultiResp Return the MultiKeywordResp string for the system. @param ProgressErr Return the error status. @retval EFI_OUT_OF_RESOURCES The memory can't be allocated. @retval EFI_SUCCESS Generate the MultiKeywordResp string. @retval EFI_NOT_FOUND No keyword found. **/ EFI_STATUS EnumerateAllKeywords ( IN CHAR8 *NameSpace, OUT EFI_STRING *MultiResp, OUT UINT32 *ProgressErr ) { LIST_ENTRY *Link; LIST_ENTRY *StringLink; UINT8 *DevicePathPkg; UINT8 *DevicePath; HII_DATABASE_RECORD *DataBaseRecord; HII_DATABASE_PACKAGE_LIST_INSTANCE *PackageListNode; HII_STRING_PACKAGE_INSTANCE *StringPackage; CHAR8 *LocalNameSpace; EFI_STRING_ID NextStringId; EFI_STATUS Status; UINT8 *OpCode; CHAR16 *ConfigRequest; CHAR16 *ValueElement; CHAR16 *KeywordResp; CHAR16 *MultiKeywordResp; CHAR16 *KeywordData; BOOLEAN ReadOnly; BOOLEAN FindKeywordPackages; DataBaseRecord = NULL; Status = EFI_SUCCESS; MultiKeywordResp = NULL; DevicePath = NULL; LocalNameSpace = NULL; ConfigRequest = NULL; ValueElement = NULL; KeywordResp = NULL; FindKeywordPackages = FALSE; if (NameSpace == NULL) { NameSpace = UEFI_CONFIG_LANG; } // // Find driver which matches the routing data. // for (Link = mPrivate.DatabaseList.ForwardLink; Link != &mPrivate.DatabaseList; Link = Link->ForwardLink) { DataBaseRecord = CR (Link, HII_DATABASE_RECORD, DatabaseEntry, HII_DATABASE_RECORD_SIGNATURE); if ((DevicePathPkg = DataBaseRecord->PackageList->DevicePathPkg) != NULL) { DevicePath = DevicePathPkg + sizeof (EFI_HII_PACKAGE_HEADER); } PackageListNode = DataBaseRecord->PackageList; for (StringLink = PackageListNode->StringPkgHdr.ForwardLink; StringLink != &PackageListNode->StringPkgHdr; StringLink = StringLink->ForwardLink) { StringPackage = CR (StringLink, HII_STRING_PACKAGE_INSTANCE, StringEntry, HII_STRING_PACKAGE_SIGNATURE); // // Check whether has keyword string package. // if (AsciiStrnCmp (NameSpace, StringPackage->StringPkgHdr->Language, AsciiStrLen (NameSpace)) == 0) { FindKeywordPackages = TRUE; // // Keep the NameSpace string. // LocalNameSpace = AllocateCopyPool (AsciiStrSize (StringPackage->StringPkgHdr->Language), StringPackage->StringPkgHdr->Language); if (LocalNameSpace == NULL) { return EFI_OUT_OF_RESOURCES; } // // 1 means just begin the enumerate the valid string ids. // StringId == 1 is always used to save the language for this string package. // Any valid string start from 2. so here initial it to 1. // NextStringId = 1; // // Enumerate all valid stringid in the package. // while ((NextStringId = GetNextStringId (StringPackage, NextStringId, &KeywordData)) != 0) { // // 3.3 Construct the ConfigRequest string. // Status = ExtractConfigRequest (DataBaseRecord, NextStringId, &OpCode, &ConfigRequest); if (EFI_ERROR (Status)) { // // If can't generate ConfigRequest for this question, skip it and start the next. // goto Error; } // // 3.4 Extract Value for the input keyword. // Status = ExtractValueFromDriver (ConfigRequest, &ValueElement); if (EFI_ERROR (Status)) { if (Status != EFI_OUT_OF_RESOURCES) { // // If can't generate ConfigRequest for this question, skip it and start the next. // goto Error; } // // If EFI_OUT_OF_RESOURCES error occur, no need to continue. // goto Done; } // // Extract readonly flag from opcode. // ReadOnly = ExtractReadOnlyFromOpCode (OpCode); // // 5. Generate KeywordResp string. // ASSERT (DevicePath != NULL); Status = GenerateKeywordResp (LocalNameSpace, (EFI_DEVICE_PATH_PROTOCOL *)DevicePath, KeywordData, ValueElement, ReadOnly, &KeywordResp); if (Status != EFI_SUCCESS) { // // If EFI_OUT_OF_RESOURCES error occur, no need to continue. // goto Done; } // // 6. Merge to the MultiKeywordResp string. // Status = MergeToMultiKeywordResp (&MultiKeywordResp, &KeywordResp); if (EFI_ERROR (Status)) { goto Done; } Error: // // Clean the temp buffer to later use again. // if (ConfigRequest != NULL) { FreePool (ConfigRequest); ConfigRequest = NULL; } if (ValueElement != NULL) { FreePool (ValueElement); ValueElement = NULL; } if (KeywordResp != NULL) { FreePool (KeywordResp); KeywordResp = NULL; } } if (LocalNameSpace != NULL) { FreePool (LocalNameSpace); LocalNameSpace = NULL; } } } } // // return the already get MultiKeywordString even error occurred. // if (MultiKeywordResp == NULL) { Status = EFI_NOT_FOUND; if (!FindKeywordPackages) { *ProgressErr = KEYWORD_HANDLER_NAMESPACE_ID_NOT_FOUND; } else { *ProgressErr = KEYWORD_HANDLER_KEYWORD_NOT_FOUND; } } else { Status = EFI_SUCCESS; } *MultiResp = MultiKeywordResp; Done: if (LocalNameSpace != NULL) { FreePool (LocalNameSpace); } if (ConfigRequest != NULL) { FreePool (ConfigRequest); } if (ValueElement != NULL) { FreePool (ValueElement); } return Status; } /** This function accepts a formatted string, finds the associated keyword owners, creates a string from it and forwards it to the EFI_HII_ROUTING_PROTOCOL.RouteConfig function. If there is an issue in resolving the contents of the KeywordString, then the function returns an error and also sets the Progress and ProgressErr with the appropriate information about where the issue occurred and additional data about the nature of the issue. In the case when KeywordString containing multiple keywords, when an EFI_NOT_FOUND error is generated during processing the second or later keyword element, the system storage associated with earlier keywords is not modified. All elements of the KeywordString must successfully pass all tests for format and access prior to making any modifications to storage. In the case when EFI_DEVICE_ERROR is returned from the processing of a KeywordString containing multiple keywords, the state of storage associated with earlier keywords is undefined. @param This Pointer to the EFI_KEYWORD_HANDLER _PROTOCOL instance. @param KeywordString A null-terminated string in format. @param Progress On return, points to a character in the KeywordString. Points to the string's NULL terminator if the request was successful. Points to the most recent '&' before the first failing name / value pair (or the beginning of the string if the failure is in the first name / value pair) if the request was not successful. @param ProgressErr If during the processing of the KeywordString there was a failure, this parameter gives additional information about the possible source of the problem. The various errors are defined in "Related Definitions" below. @retval EFI_SUCCESS The specified action was completed successfully. @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: 1. KeywordString is NULL. 2. Parsing of the KeywordString resulted in an error. See Progress and ProgressErr for more data. @retval EFI_NOT_FOUND An element of the KeywordString was not found. See ProgressErr for more data. @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. See ProgressErr for more data. @retval EFI_ACCESS_DENIED The action violated system policy. See ProgressErr for more data. @retval EFI_DEVICE_ERROR An unexpected system error occurred. See ProgressErr for more data. **/ EFI_STATUS EFIAPI EfiConfigKeywordHandlerSetData ( IN EFI_CONFIG_KEYWORD_HANDLER_PROTOCOL *This, IN CONST EFI_STRING KeywordString, OUT EFI_STRING *Progress, OUT UINT32 *ProgressErr ) { CHAR8 *NameSpace; EFI_STATUS Status; CHAR16 *StringPtr; EFI_DEVICE_PATH_PROTOCOL *DevicePath; CHAR16 *NextStringPtr; CHAR16 *KeywordData; EFI_STRING_ID KeywordStringId; UINT32 RetVal; HII_DATABASE_RECORD *DataBaseRecord; UINT8 *OpCode; CHAR16 *ConfigResp; CHAR16 *MultiConfigResp; CHAR16 *ValueElement; BOOLEAN ReadOnly; EFI_STRING InternalProgress; CHAR16 *TempString; CHAR16 *KeywordStartPos; if ((This == NULL) || (Progress == NULL) || (ProgressErr == NULL) || (KeywordString == NULL)) { return EFI_INVALID_PARAMETER; } *Progress = KeywordString; *ProgressErr = KEYWORD_HANDLER_UNDEFINED_PROCESSING_ERROR; Status = EFI_SUCCESS; MultiConfigResp = NULL; NameSpace = NULL; DevicePath = NULL; KeywordData = NULL; ValueElement = NULL; ConfigResp = NULL; KeywordStartPos = NULL; KeywordStringId = 0; // // Use temp string to avoid changing input string buffer. // TempString = AllocateCopyPool (StrSize (KeywordString), KeywordString); ASSERT (TempString != NULL); StringPtr = TempString; while ((StringPtr != NULL) && (*StringPtr != L'\0')) { // // 1. Get NameSpace from NameSpaceId keyword. // Status = ExtractNameSpace (StringPtr, &NameSpace, &NextStringPtr); if (EFI_ERROR (Status)) { *ProgressErr = KEYWORD_HANDLER_MALFORMED_STRING; goto Done; } ASSERT (NameSpace != NULL); // // 1.1 Check whether the input namespace is valid. // if (AsciiStrnCmp (NameSpace, UEFI_CONFIG_LANG, AsciiStrLen (UEFI_CONFIG_LANG)) != 0) { *ProgressErr = KEYWORD_HANDLER_MALFORMED_STRING; Status = EFI_INVALID_PARAMETER; goto Done; } StringPtr = NextStringPtr; // // 2. Get possible Device Path info from KeywordString. // Status = ExtractDevicePath (StringPtr, (UINT8 **)&DevicePath, &NextStringPtr); if (EFI_ERROR (Status)) { *ProgressErr = KEYWORD_HANDLER_MALFORMED_STRING; goto Done; } StringPtr = NextStringPtr; // // 3. Extract keyword from the KeywordRequest string. // KeywordStartPos = StringPtr; Status = ExtractKeyword (StringPtr, &KeywordData, &NextStringPtr); if (EFI_ERROR (Status)) { // // Can't find Keyword base on the input device path info. // *ProgressErr = KEYWORD_HANDLER_MALFORMED_STRING; Status = EFI_INVALID_PARAMETER; goto Done; } StringPtr = NextStringPtr; // // 4. Extract Value from the KeywordRequest string. // Status = ExtractValue (StringPtr, &ValueElement, &NextStringPtr); if (EFI_ERROR (Status)) { // // Can't find Value base on the input device path info. // *ProgressErr = KEYWORD_HANDLER_MALFORMED_STRING; Status = EFI_INVALID_PARAMETER; goto Done; } StringPtr = NextStringPtr; // // 5. Find READONLY tag. // if ((StringPtr != NULL) && (StrnCmp (StringPtr, L"&READONLY", StrLen (L"&READONLY")) == 0)) { ReadOnly = TRUE; StringPtr += StrLen (L"&READONLY"); } else { ReadOnly = FALSE; } // // 6. Get EFI_STRING_ID for the input keyword. // Status = GetStringIdFromDatabase (&DevicePath, &NameSpace, KeywordData, &RetVal, &KeywordStringId, &DataBaseRecord); if (EFI_ERROR (Status)) { *ProgressErr = RetVal; goto Done; } // // 7. Construct the ConfigRequest string. // Status = ExtractConfigResp (DataBaseRecord, KeywordStringId, ValueElement, &OpCode, &ConfigResp); if (EFI_ERROR (Status)) { goto Done; } // // 8. Check the readonly flag. // if (ExtractReadOnlyFromOpCode (OpCode) != ReadOnly) { // // Extracting readonly flag form opcode and extracting "READONLY" tag form KeywordString should have the same results. // If not, the input KeywordString must be incorrect, return the error status to caller. // *ProgressErr = KEYWORD_HANDLER_INCOMPATIBLE_VALUE_DETECTED; Status = EFI_INVALID_PARAMETER; goto Done; } if (ReadOnly) { *ProgressErr = KEYWORD_HANDLER_ACCESS_NOT_PERMITTED; Status = EFI_ACCESS_DENIED; goto Done; } // // 9. Merge to the MultiKeywordResp string. // Status = MergeToMultiKeywordResp (&MultiConfigResp, &ConfigResp); if (EFI_ERROR (Status)) { goto Done; } // // 10. Clean the temp buffer point. // FreePool (NameSpace); FreePool (DevicePath); FreePool (KeywordData); FreePool (ValueElement); NameSpace = NULL; DevicePath = NULL; KeywordData = NULL; ValueElement = NULL; if (ConfigResp != NULL) { FreePool (ConfigResp); ConfigResp = NULL; } KeywordStartPos = NULL; } // // 11. Set value to driver. // Status = mPrivate.ConfigRouting.RouteConfig ( &mPrivate.ConfigRouting, (EFI_STRING)MultiConfigResp, &InternalProgress ); if (EFI_ERROR (Status)) { Status = EFI_DEVICE_ERROR; goto Done; } *ProgressErr = KEYWORD_HANDLER_NO_ERROR; Done: if (KeywordStartPos != NULL) { *Progress = KeywordString + (KeywordStartPos - TempString); } else { *Progress = KeywordString + (StringPtr - TempString); } ASSERT (TempString != NULL); FreePool (TempString); if (NameSpace != NULL) { FreePool (NameSpace); } if (DevicePath != NULL) { FreePool (DevicePath); } if (KeywordData != NULL) { FreePool (KeywordData); } if (ValueElement != NULL) { FreePool (ValueElement); } if (ConfigResp != NULL) { FreePool (ConfigResp); } if ((MultiConfigResp != NULL) && (MultiConfigResp != ConfigResp)) { FreePool (MultiConfigResp); } return Status; } /** This function accepts a formatted string, finds the underlying keyword owners, creates a string from it and forwards it to the EFI_HII_ROUTING_PROTOCOL.ExtractConfig function. If there is an issue in resolving the contents of the KeywordString, then the function returns an EFI_INVALID_PARAMETER and also set the Progress and ProgressErr with the appropriate information about where the issue occurred and additional data about the nature of the issue. In the case when KeywordString is NULL, or contains multiple keywords, or when EFI_NOT_FOUND is generated while processing the keyword elements, the Results string contains values returned for all keywords processed prior to the keyword generating the error but no values for the keyword with error or any following keywords. @param This Pointer to the EFI_KEYWORD_HANDLER _PROTOCOL instance. @param NameSpaceId A null-terminated string containing the platform configuration language to search through in the system. If a NULL is passed in, then it is assumed that any platform configuration language with the prefix of "x-UEFI-" are searched. @param KeywordString A null-terminated string in format. If a NULL is passed in the KeywordString field, all of the known keywords in the system for the NameSpaceId specified are returned in the Results field. @param Progress On return, points to a character in the KeywordString. Points to the string's NULL terminator if the request was successful. Points to the most recent '&' before the first failing name / value pair (or the beginning of the string if the failure is in the first name / value pair) if the request was not successful. @param ProgressErr If during the processing of the KeywordString there was a failure, this parameter gives additional information about the possible source of the problem. See the definitions in SetData() for valid value definitions. @param Results A null-terminated string in format is returned which has all the values filled in for the keywords in the KeywordString. This is a callee-allocated field, and must be freed by the caller after being used. @retval EFI_SUCCESS The specified action was completed successfully. @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: 1.Progress, ProgressErr, or Results is NULL. 2.Parsing of the KeywordString resulted in an error. See Progress and ProgressErr for more data. @retval EFI_NOT_FOUND An element of the KeywordString was not found. See ProgressErr for more data. @retval EFI_NOT_FOUND The NamespaceId specified was not found. See ProgressErr for more data. @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. See ProgressErr for more data. @retval EFI_ACCESS_DENIED The action violated system policy. See ProgressErr for more data. @retval EFI_DEVICE_ERROR An unexpected system error occurred. See ProgressErr for more data. **/ EFI_STATUS EFIAPI EfiConfigKeywordHandlerGetData ( IN EFI_CONFIG_KEYWORD_HANDLER_PROTOCOL *This, IN CONST EFI_STRING NameSpaceId OPTIONAL, IN CONST EFI_STRING KeywordString OPTIONAL, OUT EFI_STRING *Progress, OUT UINT32 *ProgressErr, OUT EFI_STRING *Results ) { CHAR8 *NameSpace; EFI_STATUS Status; EFI_DEVICE_PATH_PROTOCOL *DevicePath; HII_DATABASE_RECORD *DataBaseRecord; CHAR16 *StringPtr; CHAR16 *NextStringPtr; CHAR16 *KeywordData; EFI_STRING_ID KeywordStringId; UINT8 *OpCode; CHAR16 *ConfigRequest; CHAR16 *ValueElement; UINT32 RetVal; BOOLEAN ReadOnly; CHAR16 *KeywordResp; CHAR16 *MultiKeywordResp; CHAR16 *TempString; if ((This == NULL) || (Progress == NULL) || (ProgressErr == NULL) || (Results == NULL)) { return EFI_INVALID_PARAMETER; } *ProgressErr = KEYWORD_HANDLER_UNDEFINED_PROCESSING_ERROR; Status = EFI_SUCCESS; DevicePath = NULL; NameSpace = NULL; KeywordData = NULL; ConfigRequest = NULL; StringPtr = KeywordString; ReadOnly = FALSE; MultiKeywordResp = NULL; KeywordStringId = 0; TempString = NULL; // // Use temp string to avoid changing input string buffer. // if (NameSpaceId != NULL) { TempString = AllocateCopyPool (StrSize (NameSpaceId), NameSpaceId); ASSERT (TempString != NULL); } // // 1. Get NameSpace from NameSpaceId keyword. // Status = ExtractNameSpace (TempString, &NameSpace, NULL); if (TempString != NULL) { FreePool (TempString); TempString = NULL; } if (EFI_ERROR (Status)) { *ProgressErr = KEYWORD_HANDLER_MALFORMED_STRING; return Status; } // // 1.1 Check whether the input namespace is valid. // if (NameSpace != NULL) { if (AsciiStrnCmp (NameSpace, UEFI_CONFIG_LANG, AsciiStrLen (UEFI_CONFIG_LANG)) != 0) { *ProgressErr = KEYWORD_HANDLER_MALFORMED_STRING; return EFI_INVALID_PARAMETER; } } if (KeywordString != NULL) { // // Use temp string to avoid changing input string buffer. // TempString = AllocateCopyPool (StrSize (KeywordString), KeywordString); ASSERT (TempString != NULL); StringPtr = TempString; while (*StringPtr != L'\0') { // // 2. Get possible Device Path info from KeywordString. // Status = ExtractDevicePath (StringPtr, (UINT8 **)&DevicePath, &NextStringPtr); if (EFI_ERROR (Status)) { *ProgressErr = KEYWORD_HANDLER_MALFORMED_STRING; goto Done; } StringPtr = NextStringPtr; // // 3. Process Keyword section from the input keywordRequest string. // // 3.1 Extract keyword from the KeywordRequest string. // Status = ExtractKeyword (StringPtr, &KeywordData, &NextStringPtr); if (EFI_ERROR (Status)) { // // Can't find Keyword base on the input device path info. // *ProgressErr = KEYWORD_HANDLER_MALFORMED_STRING; Status = EFI_INVALID_PARAMETER; goto Done; } // // 3.2 Get EFI_STRING_ID for the input keyword. // Status = GetStringIdFromDatabase (&DevicePath, &NameSpace, KeywordData, &RetVal, &KeywordStringId, &DataBaseRecord); if (EFI_ERROR (Status)) { *ProgressErr = RetVal; goto Done; } // // 3.3 Construct the ConfigRequest string. // Status = ExtractConfigRequest (DataBaseRecord, KeywordStringId, &OpCode, &ConfigRequest); if (EFI_ERROR (Status)) { goto Done; } // // 3.4 Extract Value for the input keyword. // Status = ExtractValueFromDriver (ConfigRequest, &ValueElement); if (EFI_ERROR (Status)) { if (Status != EFI_OUT_OF_RESOURCES) { Status = EFI_DEVICE_ERROR; } goto Done; } StringPtr = NextStringPtr; // // 4. Process the possible filter section. // RetVal = ValidateFilter (OpCode, StringPtr, &NextStringPtr, &ReadOnly); if (RetVal != KEYWORD_HANDLER_NO_ERROR) { *ProgressErr = RetVal; Status = EFI_INVALID_PARAMETER; goto Done; } StringPtr = NextStringPtr; // // 5. Generate KeywordResp string. // Status = GenerateKeywordResp (NameSpace, DevicePath, KeywordData, ValueElement, ReadOnly, &KeywordResp); if (Status != EFI_SUCCESS) { goto Done; } // // 6. Merge to the MultiKeywordResp string. // Status = MergeToMultiKeywordResp (&MultiKeywordResp, &KeywordResp); if (EFI_ERROR (Status)) { goto Done; } // // 7. Update return value. // *Results = MultiKeywordResp; // // 8. Clean the temp buffer. // FreePool (DevicePath); FreePool (KeywordData); FreePool (ValueElement); FreePool (ConfigRequest); DevicePath = NULL; KeywordData = NULL; ValueElement = NULL; ConfigRequest = NULL; if (KeywordResp != NULL) { FreePool (KeywordResp); KeywordResp = NULL; } } } else { // // Enumerate all keyword in the system. // Status = EnumerateAllKeywords (NameSpace, &MultiKeywordResp, ProgressErr); if (EFI_ERROR (Status)) { goto Done; } *Results = MultiKeywordResp; } *ProgressErr = KEYWORD_HANDLER_NO_ERROR; Done: *Progress = KeywordString + (StringPtr - TempString); if (TempString != NULL) { FreePool (TempString); } if (NameSpace != NULL) { FreePool (NameSpace); } if (DevicePath != NULL) { FreePool (DevicePath); } if (KeywordData != NULL) { FreePool (KeywordData); } return Status; }