/** @file Implementation of interfaces function for EFI_HII_CONFIG_ROUTING_PROTOCOL. Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at http://opensource.org/licenses/bsd-license.php THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ #include "HiiDatabase.h" extern HII_DATABASE_PRIVATE_DATA mPrivate; /** Calculate the number of Unicode characters of the incoming Configuration string, not including NULL terminator. This is a internal function. @param String String in or format. @return The number of Unicode characters. **/ UINTN CalculateConfigStringLen ( IN EFI_STRING String ) { EFI_STRING TmpPtr; // // "GUID=" should be the first element of incoming string. // ASSERT (String != NULL); ASSERT (StrnCmp (String, L"GUID=", StrLen (L"GUID=")) == 0); // // The beginning of next / should be "&GUID=". // Will meet '\0' if there is only one /. // TmpPtr = StrStr (String, L"&GUID="); if (TmpPtr == NULL) { return StrLen (String); } return (TmpPtr - String); } /** Convert the hex UNICODE %02x encoding of a UEFI device path to binary from of . This is a internal function. @param String UEFI configuration string @param DevicePathData Binary of a UEFI device path. @retval EFI_NOT_FOUND The device path is not invalid. @retval EFI_INVALID_PARAMETER Any incoming parameter is invalid. @retval EFI_OUT_OF_RESOURCES Lake of resources to store neccesary structures. @retval EFI_SUCCESS The device path is retrieved and translated to binary format. **/ EFI_STATUS GetDevicePath ( IN EFI_STRING String, OUT UINT8 **DevicePathData ) { UINTN Length; EFI_STRING PathHdr; UINT8 *DevicePathBuffer; CHAR16 TemStr[2]; UINTN Index; UINT8 DigitUint8; EFI_DEVICE_PATH_PROTOCOL *DevicePath; if (String == NULL || DevicePathData == NULL) { return EFI_INVALID_PARAMETER; } // // Find the 'PATH=' of and skip it. // for (; (*String != 0 && StrnCmp (String, L"PATH=", StrLen (L"PATH=")) != 0); String++); if (*String == 0) { return EFI_INVALID_PARAMETER; } // // 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++); // // Check DevicePath Length // if (((Length + 1) / 2) < sizeof (EFI_DEVICE_PATH_PROTOCOL)) { return EFI_NOT_FOUND; } // // 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_NOT_FOUND; } DevicePath = NextDevicePathNode (DevicePath); } // // return the device path // *DevicePathData = DevicePathBuffer; return EFI_SUCCESS; } /** Converts the unicode character of the string from uppercase to lowercase. This is a internal function. @param ConfigString String to be converted **/ VOID EFIAPI HiiToLower ( 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; } /** Generate a sub string then output it. This is a internal function. @param String A constant string which is the prefix of the to be generated string, e.g. GUID= @param BufferLen The length of the Buffer in bytes. @param Buffer Points to a buffer which will be converted to be the content of the generated string. @param Flag If 1, the buffer contains data for the value of GUID or PATH stored in UINT8 *; if 2, the buffer contains unicode string for the value of NAME; if 3, the buffer contains other data. @param SubStr Points to the output string. It's caller's responsibility to free this buffer. **/ VOID GenerateSubStr ( IN CONST EFI_STRING String, IN UINTN BufferLen, IN VOID *Buffer, IN UINT8 Flag, OUT EFI_STRING *SubStr ) { UINTN Length; EFI_STRING Str; EFI_STRING StringHeader; CHAR16 *TemString; CHAR16 *TemName; UINT8 *TemBuffer; UINTN Index; ASSERT (String != NULL && SubStr != NULL); if (Buffer == NULL) { *SubStr = AllocateCopyPool (StrSize (String), String); ASSERT (*SubStr != NULL); return; } // // Header + Data + '&' + '\0' // Length = StrLen (String) + BufferLen * 2 + 1 + 1; Str = AllocateZeroPool (Length * sizeof (CHAR16)); ASSERT (Str != NULL); StrCpyS (Str, Length, String); StringHeader = Str + StrLen (String); TemString = (CHAR16 *) StringHeader; switch (Flag) { case 1: // // Convert Buffer to Hex String in reverse order // TemBuffer = ((UINT8 *) Buffer); for (Index = 0; Index < BufferLen; Index ++, TemBuffer ++) { TemString += UnicodeValueToString (TemString, PREFIX_ZERO | RADIX_HEX, *TemBuffer, 2); } break; case 2: // // Check buffer is enough // TemName = (CHAR16 *) Buffer; ASSERT ((BufferLen * 2 + 1) >= (StrLen (TemName) * 4 + 1)); // // Convert Unicode String to Config String, e.g. "ABCD" => "0041004200430044" // for (; *TemName != L'\0'; TemName++) { TemString += UnicodeValueToString (TemString, PREFIX_ZERO | RADIX_HEX, *TemName, 4); } break; case 3: // // Convert Buffer to Hex String // TemBuffer = ((UINT8 *) Buffer) + BufferLen - 1; for (Index = 0; Index < BufferLen; Index ++, TemBuffer --) { TemString += UnicodeValueToString (TemString, PREFIX_ZERO | RADIX_HEX, *TemBuffer, 2); } break; default: break; } // // Convert the uppercase to lowercase since is defined in lowercase format. // StrCatS (Str, Length, L"&"); HiiToLower (Str); *SubStr = Str; } /** Retrieve the from String then output it. This is a internal function. @param String A sub string of a configuration string in format. @param ConfigBody Points to the output string. It's caller's responsibility to free this buffer. @retval EFI_INVALID_PARAMETER There is no form package in current hii database. @retval EFI_OUT_OF_RESOURCES Not enough memory to finish this operation. @retval EFI_SUCCESS All existing storage is exported. **/ EFI_STATUS OutputConfigBody ( IN EFI_STRING String, OUT EFI_STRING *ConfigBody ) { EFI_STRING TmpPtr; EFI_STRING Result; UINTN Length; if (String == NULL || ConfigBody == NULL) { return EFI_INVALID_PARAMETER; } // // The setting information should start OFFSET, not ALTCFG. // if (StrnCmp (String, L"&ALTCFG=", StrLen (L"&ALTCFG=")) == 0) { return EFI_INVALID_PARAMETER; } TmpPtr = StrStr (String, L"GUID="); if (TmpPtr == NULL) { // // It is the last of the incoming configuration string. // Result = AllocateCopyPool (StrSize (String), String); if (Result == NULL) { return EFI_OUT_OF_RESOURCES; } else { *ConfigBody = Result; return EFI_SUCCESS; } } Length = TmpPtr - String; if (Length == 0) { return EFI_NOT_FOUND; } Result = AllocateCopyPool (Length * sizeof (CHAR16), String); if (Result == NULL) { return EFI_OUT_OF_RESOURCES; } *(Result + Length - 1) = 0; *ConfigBody = Result; return EFI_SUCCESS; } /** Append a string to a multi-string format. This is a internal function. @param MultiString String in , , or . On input, the buffer length of this string is MAX_STRING_LENGTH. On output, the buffer length might be updated. @param AppendString NULL-terminated Unicode string. @retval EFI_INVALID_PARAMETER Any incoming parameter is invalid. @retval EFI_SUCCESS AppendString is append to the end of MultiString **/ EFI_STATUS AppendToMultiString ( IN OUT EFI_STRING *MultiString, IN EFI_STRING AppendString ) { UINTN AppendStringSize; UINTN MultiStringSize; UINTN MaxLen; if (MultiString == NULL || *MultiString == NULL || AppendString == NULL) { return EFI_INVALID_PARAMETER; } AppendStringSize = StrSize (AppendString); MultiStringSize = StrSize (*MultiString); MaxLen = MAX_STRING_LENGTH / sizeof (CHAR16); // // Enlarge the buffer each time when length exceeds MAX_STRING_LENGTH. // if (MultiStringSize + AppendStringSize > MAX_STRING_LENGTH || MultiStringSize > MAX_STRING_LENGTH) { *MultiString = (EFI_STRING) ReallocatePool ( MultiStringSize, MultiStringSize + AppendStringSize, (VOID *) (*MultiString) ); MaxLen = (MultiStringSize + AppendStringSize) / sizeof (CHAR16); ASSERT (*MultiString != NULL); } // // Append the incoming string // StrCatS (*MultiString, MaxLen, AppendString); return EFI_SUCCESS; } /** Get the value of in format, i.e. the value of OFFSET or WIDTH or VALUE. ::= 'OFFSET='&'WIDTH='&'VALUE'= This is a internal function. @param StringPtr String in format and points to the first character of . @param Number The output value. Caller takes the responsibility to free memory. @param Len Length of the , in characters. @retval EFI_OUT_OF_RESOURCES Insufficient resources to store neccessary structures. @retval EFI_SUCCESS Value of is outputted in Number successfully. **/ EFI_STATUS GetValueOfNumber ( IN EFI_STRING StringPtr, OUT UINT8 **Number, OUT UINTN *Len ) { EFI_STRING TmpPtr; UINTN Length; EFI_STRING Str; UINT8 *Buf; EFI_STATUS Status; UINT8 DigitUint8; UINTN Index; CHAR16 TemStr[2]; if (StringPtr == NULL || *StringPtr == L'\0' || Number == NULL || Len == NULL) { return EFI_INVALID_PARAMETER; } Buf = NULL; TmpPtr = StringPtr; while (*StringPtr != L'\0' && *StringPtr != L'&') { StringPtr++; } *Len = StringPtr - TmpPtr; Length = *Len + 1; Str = (EFI_STRING) AllocateZeroPool (Length * sizeof (CHAR16)); if (Str == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Exit; } CopyMem (Str, TmpPtr, *Len * sizeof (CHAR16)); *(Str + *Len) = L'\0'; Length = (Length + 1) / 2; Buf = (UINT8 *) AllocateZeroPool (Length); if (Buf == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Exit; } Length = *Len; ZeroMem (TemStr, sizeof (TemStr)); for (Index = 0; Index < Length; Index ++) { TemStr[0] = Str[Length - Index - 1]; DigitUint8 = (UINT8) StrHexToUint64 (TemStr); if ((Index & 1) == 0) { Buf [Index/2] = DigitUint8; } else { Buf [Index/2] = (UINT8) ((DigitUint8 << 4) + Buf [Index/2]); } } *Number = Buf; Status = EFI_SUCCESS; Exit: if (Str != NULL) { FreePool (Str); } return Status; } /** To find the BlockName in the string with same value. @param String Pointer to a Null-terminated Unicode string. @param BlockName Pointer to a Null-terminated Unicode string to search for. @param Buffer Pointer to the value correspond to the BlockName. @param Found The Block whether has been found. @param BufferLen The length of the buffer. @retval EFI_OUT_OF_RESOURCES Insufficient resources to store neccessary structures. @retval EFI_SUCCESS The function finishes successfully. **/ EFI_STATUS FindSameBlockElement( IN EFI_STRING String, IN EFI_STRING BlockName, IN UINT8 *Buffer, OUT BOOLEAN *Found, IN UINTN BufferLen ) { EFI_STRING BlockPtr; UINTN Length; UINT8 *TempBuffer; EFI_STATUS Status; TempBuffer = NULL; *Found = FALSE; BlockPtr = StrStr (String, BlockName); while (BlockPtr != NULL) { BlockPtr += StrLen (BlockName); Status = GetValueOfNumber (BlockPtr, &TempBuffer, &Length); if (EFI_ERROR (Status)) { return Status; } ASSERT (TempBuffer != NULL); if ((BufferLen == Length) && (0 == CompareMem (Buffer, TempBuffer, Length))) { *Found = TRUE; FreePool (TempBuffer); TempBuffer = NULL; return EFI_SUCCESS; } else { FreePool (TempBuffer); TempBuffer = NULL; BlockPtr = StrStr (BlockPtr + 1, BlockName); } } return EFI_SUCCESS; } /** Compare the in ConfigAltResp and DefaultAltCfgResp, if the in DefaultAltCfgResp but not in ConfigAltResp,add it to the ConfigAltResp. @param DefaultAltCfgResp Pointer to a null-terminated Unicode string in format. The default value string may contain more than one ConfigAltResp string for the different varstore buffer. @param ConfigAltResp Pointer to a null-terminated Unicode string in format. @param AltConfigHdr Pointer to a Unicode string in format. @param ConfigAltRespChanged Whether the ConfigAltResp has been changed. @retval EFI_OUT_OF_RESOURCES Insufficient resources to store neccessary structures. @retval EFI_SUCCESS The function finishes successfully. **/ EFI_STATUS CompareBlockElementDefault ( IN EFI_STRING DefaultAltCfgResp, IN OUT EFI_STRING *ConfigAltResp, IN EFI_STRING AltConfigHdr, IN OUT BOOLEAN *ConfigAltRespChanged ) { EFI_STATUS Status; EFI_STRING BlockPtr; EFI_STRING BlockPtrStart; EFI_STRING StringPtr; EFI_STRING AppendString; EFI_STRING AltConfigHdrPtr; UINT8 *TempBuffer; UINTN OffsetLength; UINTN AppendSize; UINTN TotalSize; BOOLEAN FoundOffset; AppendString = NULL; TempBuffer = NULL; // // Make BlockPtr point to the first with AltConfigHdr in DefaultAltCfgResp. // AltConfigHdrPtr = StrStr (DefaultAltCfgResp, AltConfigHdr); ASSERT (AltConfigHdrPtr != NULL); BlockPtr = StrStr (AltConfigHdrPtr, L"&OFFSET="); // // Make StringPtr point to the AltConfigHdr in ConfigAltResp. // StringPtr = StrStr (*ConfigAltResp, AltConfigHdr); ASSERT (StringPtr != NULL); while (BlockPtr != NULL) { // // Find the "&OFFSET=" block and get the value of the Number with AltConfigHdr in DefaultAltCfgResp. // BlockPtrStart = BlockPtr; BlockPtr += StrLen (L"&OFFSET="); Status = GetValueOfNumber (BlockPtr, &TempBuffer, &OffsetLength); if (EFI_ERROR (Status)) { Status = EFI_OUT_OF_RESOURCES; goto Exit; } // // To find the same "&OFFSET=" block in ConfigAltResp. // Status = FindSameBlockElement (StringPtr, L"&OFFSET=", TempBuffer, &FoundOffset, OffsetLength); if (TempBuffer != NULL) { FreePool (TempBuffer); TempBuffer = NULL; } if (EFI_ERROR (Status)) { Status = EFI_OUT_OF_RESOURCES; goto Exit; } if (!FoundOffset) { // // Don't find the same "&OFFSET=" block in ConfigAltResp. // Calculate the size of . // ::='OFFSET=''&WIDTH=''&VALUE='. // BlockPtr = StrStr (BlockPtr + 1, L"&OFFSET="); if (BlockPtr != NULL) { AppendSize = (BlockPtr - BlockPtrStart) * sizeof (CHAR16); } else { AppendSize = StrSize (BlockPtrStart); } // // Copy the to AppendString. // if (AppendString == NULL) { AppendString = (EFI_STRING) AllocateZeroPool (AppendSize + sizeof (CHAR16)); StrnCatS (AppendString, AppendSize / sizeof (CHAR16) + 1, BlockPtrStart, AppendSize / sizeof (CHAR16)); } else { TotalSize = StrSize (AppendString) + AppendSize + sizeof (CHAR16); AppendString = (EFI_STRING) ReallocatePool ( StrSize (AppendString), TotalSize, AppendString ); if (AppendString == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Exit; } StrnCatS (AppendString, TotalSize / sizeof (CHAR16), BlockPtrStart, AppendSize / sizeof (CHAR16)); } } else { // // To find next "&OFFSET=" block with AltConfigHdr in DefaultAltCfgResp. // BlockPtr = StrStr (BlockPtr + 1, L"&OFFSET="); } } if (AppendString != NULL) { // // Reallocate ConfigAltResp to copy the AppendString. // TotalSize = StrSize (*ConfigAltResp) + StrSize (AppendString) + sizeof (CHAR16); *ConfigAltResp = (EFI_STRING) ReallocatePool ( StrSize (*ConfigAltResp), TotalSize, *ConfigAltResp ); if (*ConfigAltResp == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Exit; } StrCatS (*ConfigAltResp, TotalSize / sizeof (CHAR16), AppendString); *ConfigAltRespChanged = TRUE; } Status = EFI_SUCCESS; Exit: if (AppendString != NULL) { FreePool (AppendString); } return Status; } /** Compare the in ConfigAltResp and DefaultAltCfgResp, if the in DefaultAltCfgResp but not in ConfigAltResp,add it to the ConfigAltResp. @param DefaultAltCfgResp Pointer to a null-terminated Unicode string in format. The default value string may contain more than one ConfigAltResp string for the different varstore buffer. @param ConfigAltResp Pointer to a null-terminated Unicode string in format. @param AltConfigHdr Pointer to a Unicode string in format. @param ConfigAltRespChanged Whether the ConfigAltResp has been changed. @retval EFI_OUT_OF_RESOURCES Insufficient resources to store neccessary structures. @retval EFI_SUCCESS The function finishes successfully. **/ EFI_STATUS CompareNameElementDefault ( IN EFI_STRING DefaultAltCfgResp, IN OUT EFI_STRING *ConfigAltResp, IN EFI_STRING AltConfigHdr, IN OUT BOOLEAN *ConfigAltRespChanged ) { EFI_STATUS Status; EFI_STRING NvConfigPtr; EFI_STRING NvConfigStart; EFI_STRING NvConfigValuePtr; EFI_STRING StringPtr; EFI_STRING NvConfigExist; EFI_STRING AppendString; CHAR16 TempChar; UINTN AppendSize; UINTN TotalSize; AppendString = NULL; NvConfigExist = NULL; // // Make NvConfigPtr point to the first with AltConfigHdr in DefaultAltCfgResp. // NvConfigPtr = StrStr (DefaultAltCfgResp, AltConfigHdr); ASSERT (NvConfigPtr != NULL); NvConfigPtr = StrStr (NvConfigPtr + StrLen(AltConfigHdr),L"&"); // // Make StringPtr point to the first with AltConfigHdr in ConfigAltResp. // StringPtr = StrStr (*ConfigAltResp, AltConfigHdr); ASSERT (StringPtr != NULL); StringPtr = StrStr (StringPtr + StrLen (AltConfigHdr), L"&"); ASSERT (StringPtr != NULL); while (NvConfigPtr != NULL) { // // ::=