/** @file Implementation of interfaces function for EFI_HII_CONFIG_ROUTING_PROTOCOL. Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #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 necessary 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 ++) { UnicodeValueToStringS ( TemString, sizeof (CHAR16) * (Length - StrnLenS (Str, Length)), PREFIX_ZERO | RADIX_HEX, *TemBuffer, 2 ); TemString += StrnLenS (TemString, Length - StrnLenS (Str, Length)); } 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++) { UnicodeValueToStringS ( TemString, sizeof (CHAR16) * (Length - StrnLenS (Str, Length)), PREFIX_ZERO | RADIX_HEX, *TemName, 4 ); TemString += StrnLenS (TemString, Length - StrnLenS (Str, Length)); } break; case 3: // // Convert Buffer to Hex String // TemBuffer = ((UINT8 *) Buffer) + BufferLen - 1; for (Index = 0; Index < BufferLen; Index ++, TemBuffer --) { UnicodeValueToStringS ( TemString, sizeof (CHAR16) * (Length - StrnLenS (Str, Length)), PREFIX_ZERO | RADIX_HEX, *TemBuffer, 2 ); TemString += StrnLenS (TemString, Length - StrnLenS (Str, Length)); } 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 necessary 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 necessary 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 necessary 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 necessary 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) { // // ::=