From 3c59d94637adbfdd497b5a2c16073c7dc62b669c Mon Sep 17 00:00:00 2001 From: Shenglei Zhang Date: Tue, 28 May 2019 16:07:43 +0800 Subject: [PATCH] BaseTools/FCE: Add a tool FCE FCE is a tool to retrieve and change HII configuration data in Firmware Device(*.fd) files. https://bugzilla.tianocore.org/show_bug.cgi?id=1848 Cc: Bob Feng Cc: Liming Gao Signed-off-by: Shenglei Zhang Reviewed-by: Bob Feng --- BaseTools/BinWrappers/PosixLike/FCE | 29 + BaseTools/Source/C/FCE/BinaryCreate.c | 216 + BaseTools/Source/C/FCE/BinaryCreate.h | 157 + BaseTools/Source/C/FCE/BinaryParse.c | 1326 ++++ BaseTools/Source/C/FCE/BinaryParse.h | 187 + BaseTools/Source/C/FCE/Common.c | 2183 ++++++ BaseTools/Source/C/FCE/Common.h | 999 +++ BaseTools/Source/C/FCE/Expression.c | 2367 ++++++ BaseTools/Source/C/FCE/Fce.c | 6449 +++++++++++++++++ BaseTools/Source/C/FCE/Fce.h | 447 ++ BaseTools/Source/C/FCE/GNUmakefile | 55 + BaseTools/Source/C/FCE/IfrParse.c | 4836 ++++++++++++ BaseTools/Source/C/FCE/IfrParse.h | 789 ++ BaseTools/Source/C/FCE/Makefile | 19 + .../Source/C/FCE/MonotonicBasedVariable.c | 874 +++ .../Source/C/FCE/MonotonicBasedVariable.h | 162 + BaseTools/Source/C/FCE/TimeBasedVariable.c | 878 +++ BaseTools/Source/C/FCE/TimeBasedVariable.h | 166 + BaseTools/Source/C/FCE/Variable.c | 1091 +++ BaseTools/Source/C/FCE/Variable.h | 154 + BaseTools/Source/C/FCE/VariableCommon.h | 55 + BaseTools/Source/C/GNUmakefile | 3 +- BaseTools/Source/C/Makefile | 3 +- 23 files changed, 23443 insertions(+), 2 deletions(-) create mode 100755 BaseTools/BinWrappers/PosixLike/FCE create mode 100644 BaseTools/Source/C/FCE/BinaryCreate.c create mode 100644 BaseTools/Source/C/FCE/BinaryCreate.h create mode 100644 BaseTools/Source/C/FCE/BinaryParse.c create mode 100644 BaseTools/Source/C/FCE/BinaryParse.h create mode 100644 BaseTools/Source/C/FCE/Common.c create mode 100644 BaseTools/Source/C/FCE/Common.h create mode 100644 BaseTools/Source/C/FCE/Expression.c create mode 100644 BaseTools/Source/C/FCE/Fce.c create mode 100644 BaseTools/Source/C/FCE/Fce.h create mode 100644 BaseTools/Source/C/FCE/GNUmakefile create mode 100644 BaseTools/Source/C/FCE/IfrParse.c create mode 100644 BaseTools/Source/C/FCE/IfrParse.h create mode 100644 BaseTools/Source/C/FCE/Makefile create mode 100644 BaseTools/Source/C/FCE/MonotonicBasedVariable.c create mode 100644 BaseTools/Source/C/FCE/MonotonicBasedVariable.h create mode 100644 BaseTools/Source/C/FCE/TimeBasedVariable.c create mode 100644 BaseTools/Source/C/FCE/TimeBasedVariable.h create mode 100644 BaseTools/Source/C/FCE/Variable.c create mode 100644 BaseTools/Source/C/FCE/Variable.h create mode 100644 BaseTools/Source/C/FCE/VariableCommon.h diff --git a/BaseTools/BinWrappers/PosixLike/FCE b/BaseTools/BinWrappers/PosixLike/FCE new file mode 100755 index 0000000000..a244ecc095 --- /dev/null +++ b/BaseTools/BinWrappers/PosixLike/FCE @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +full_cmd=${BASH_SOURCE:-$0} # see http://mywiki.wooledge.org/BashFAQ/028 for a discussion of why $0 is not a good choice here +dir=$(dirname "$full_cmd") +cmd=${full_cmd##*/} + +if [ -n "$WORKSPACE" ] && [ -e "$WORKSPACE/Conf/BaseToolsCBinaries" ] +then + exec "$WORKSPACE/Conf/BaseToolsCBinaries/$cmd" +elif [ -n "$WORKSPACE" ] && [ -e "$EDK_TOOLS_PATH/Source/C" ] +then + if [ ! -e "$EDK_TOOLS_PATH/Source/C/bin/$cmd" ] + then + echo "BaseTools C Tool binary was not found ($cmd)" + echo "You may need to run:" + echo " make -C $EDK_TOOLS_PATH/Source/C" + else + exec "$EDK_TOOLS_PATH/Source/C/bin/$cmd" "$@" + fi +elif [ -e "$dir/../../Source/C/bin/$cmd" ] +then + exec "$dir/../../Source/C/bin/$cmd" "$@" +else + echo "Unable to find the real '$cmd' to run" + echo "This message was printed by" + echo " $0" + exit 127 +fi + diff --git a/BaseTools/Source/C/FCE/BinaryCreate.c b/BaseTools/Source/C/FCE/BinaryCreate.c new file mode 100644 index 0000000000..2f36bc2ef3 --- /dev/null +++ b/BaseTools/Source/C/FCE/BinaryCreate.c @@ -0,0 +1,216 @@ +/** @file + + The API to create the binary. + + Copyright (c) 2011-2019, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "BinaryCreate.h" +#ifndef __GNUC__ +#define GENSEC_RAW "GenSec -s %s \"%s\" -o \"%s\" > NUL" +#else +#define GENSEC_RAW "GenSec -s %s \"%s\" -o \"%s\" > /dev/null" +#endif + +// +// The guid is to for FFS of BFV. +// +EFI_GUID gEfiFfsBfvForMultiPlatformGuid = EFI_FFS_BFV_FOR_MULTIPLATFORM_GUID; +EFI_GUID gEfiFfsBfvForMultiPlatformGuid2 = EFI_FFS_BFV_FOR_MULTIPLATFORM_GUID2; + +/** + Convert a GUID to a string. + + @param[in] Guid Pointer to GUID to print. + + @return The string after convert. +**/ +static +CHAR8 * +LibBfmGuidToStr ( + IN EFI_GUID *Guid +) +{ + CHAR8 * Buffer; + + Buffer = NULL; + + if (Guid == NULL) { + return NULL; + } + + Buffer = (CHAR8 *) malloc (36 + 1); + + if (Buffer == NULL) { + return NULL; + } + memset (Buffer, '\0', 36 + 1); + + sprintf ( + Buffer, + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + Guid->Data1, + Guid->Data2, + Guid->Data3, + Guid->Data4[0], + Guid->Data4[1], + Guid->Data4[2], + Guid->Data4[3], + Guid->Data4[4], + Guid->Data4[5], + Guid->Data4[6], + Guid->Data4[7] + ); + + return Buffer; +} + +/** + Create the Ras section in FFS + + @param[in] InputFilePath .efi file, it's optional unless process PE/TE section. + @param[in] OutputFilePath .te or .pe file + + @retval EFI_SUCCESS + +**/ +EFI_STATUS +CreateRawSection ( + IN CHAR8* InputFilePath, + IN CHAR8* OutputFilePath + ) +{ + INT32 ReturnValue; + CHAR8* SystemCommand; + + SystemCommand = NULL; + SystemCommand = malloc ( + strlen (GENSEC_RAW) + + strlen ("EFI_SECTION_RAW") + + strlen (InputFilePath) + + strlen (OutputFilePath) + + 1 + ); + if (NULL == SystemCommand) { + return EFI_OUT_OF_RESOURCES; + } + sprintf ( + SystemCommand, + GENSEC_RAW, + "EFI_SECTION_RAW", + InputFilePath, + OutputFilePath + ); + ReturnValue = system (SystemCommand); + free(SystemCommand); + + if (ReturnValue != 0) { + printf ("Error. Call GenSec failed.\n"); + return EFI_ABORTED; + } + return EFI_SUCCESS; +} + +/** + Create the Ras type of FFS + + @param[in] InputFilePath .efi file, it's optional unless process PE/TE section. + @param[in] OutputFilePath .te or .pe file + + @retval EFI_SUCCESS + +**/ +EFI_STATUS +CreateRawFfs ( + IN CHAR8** InputFilePaths, + IN CHAR8* OutputFilePath, + IN BOOLEAN SizeOptimized + ) +{ + INT32 ReturnValue; + CHAR8* SystemCommandFormatString; + CHAR8* SystemCommand; + CHAR8* GuidStr; + CHAR8* FilePathFormatStr; + CHAR8* FilePathStr; + UINT32 Index; + UINT32 StrLen; + UINT32 Size; + + SystemCommand = NULL; + GuidStr = NULL; + FilePathStr = NULL; + StrLen = 0; + + FilePathFormatStr = " -i \""; + + for (Index = 0; InputFilePaths[Index] != NULL; Index++) { + Size = strlen (FilePathFormatStr) + strlen (InputFilePaths[Index]) + 2; // 2 menas "" " + if (FilePathStr == NULL) { + FilePathStr = malloc (Size); + if (NULL == FilePathStr) { + return EFI_OUT_OF_RESOURCES; + } + } else { + FilePathStr = realloc (FilePathStr, StrLen + Size); + if (NULL == FilePathStr) { + return EFI_OUT_OF_RESOURCES; + } + } + memset (FilePathStr + StrLen, ' ', Size); + memcpy (FilePathStr + StrLen, FilePathFormatStr, strlen(FilePathFormatStr)); + memcpy(FilePathStr + StrLen + strlen(FilePathFormatStr), InputFilePaths[Index], strlen(InputFilePaths[Index])); + StrLen += Size; + *(FilePathStr + StrLen - 2) = '\"'; + } + if (FilePathStr == NULL) { + return EFI_ABORTED; + } + *(FilePathStr + StrLen - 1)= '\0'; + + + if (SizeOptimized) { + GuidStr = LibBfmGuidToStr(&gEfiFfsBfvForMultiPlatformGuid2); + } else { + GuidStr = LibBfmGuidToStr(&gEfiFfsBfvForMultiPlatformGuid); + } + if (NULL == GuidStr) { + free (FilePathStr); + return EFI_OUT_OF_RESOURCES; + } + SystemCommandFormatString = "GenFfs -t %s %s -g %s -o \"%s\""; + SystemCommand = malloc ( + strlen (SystemCommandFormatString) + + strlen ("EFI_FV_FILETYPE_FREEFORM") + + strlen (FilePathStr) + + strlen (GuidStr) + + strlen (OutputFilePath) + + 1 + ); + if (NULL == SystemCommand) { + free (GuidStr); + free (FilePathStr); + return EFI_OUT_OF_RESOURCES; + } + sprintf ( + SystemCommand, + "GenFfs -t %s %s -g %s -o \"%s\"", + "EFI_FV_FILETYPE_FREEFORM",// -t + FilePathStr, // -i + GuidStr, // -g + OutputFilePath // -o + ); + ReturnValue = system (SystemCommand); + free(SystemCommand); + free (FilePathStr); + free (GuidStr); + + if (ReturnValue != 0) { + printf ("Error. Call GenFfs failed.\n"); + return EFI_ABORTED; + } + return EFI_SUCCESS; +} + diff --git a/BaseTools/Source/C/FCE/BinaryCreate.h b/BaseTools/Source/C/FCE/BinaryCreate.h new file mode 100644 index 0000000000..0e2f22599e --- /dev/null +++ b/BaseTools/Source/C/FCE/BinaryCreate.h @@ -0,0 +1,157 @@ +/** @file + + The API to create the binary. + + Copyright (c) 2011-2019, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _BINARY_CREATE_H_ +#define _BINARY_CREATE_H_ 1 + +#include +#include "Compress.h" +#include "Decompress.h" +#include "CommonLib.h" +#include "EfiUtilityMsgs.h" +#include "FirmwareVolumeBufferLib.h" +#include "OsPath.h" +#include "ParseGuidedSectionTools.h" +#include "StringFuncs.h" +#include "ParseInf.h" +#include +#include +#include +#include +#include +#include +#include +#include + +//1AE42876-008F-4161-B2B7-1C0D15C5EF43 +#define EFI_FFS_BFV_FOR_MULTIPLATFORM_GUID \ + { 0x1ae42876, 0x008f, 0x4161, { 0xb2, 0xb7, 0x1c, 0xd, 0x15, 0xc5, 0xef, 0x43 }} + +extern EFI_GUID gEfiFfsBfvForMultiPlatformGuid; + +// {003E7B41-98A2-4BE2-B27A-6C30C7655225} +#define EFI_FFS_BFV_FOR_MULTIPLATFORM_GUID2 \ + { 0x3e7b41, 0x98a2, 0x4be2, { 0xb2, 0x7a, 0x6c, 0x30, 0xc7, 0x65, 0x52, 0x25 }} + +extern EFI_GUID gEfiFfsBfvForMultiPlatformGuid2; + +typedef UINT64 SKU_ID; + +typedef struct { + UINT32 Offset:24; + UINT32 Value:8; +} PCD_DATA_DELTA; + +typedef struct { + SKU_ID SkuId; + UINT16 DefaultId; + UINT8 Reserved[6]; +} PCD_DEFAULT_INFO; + +typedef struct { + // + // Full size, it must be at 8 byte alignment. + // + UINT32 DataSize; + // + // HeaderSize includes HeaderSize fields and DefaultInfo arrays + // + UINT32 HeaderSize; + // + // DefaultInfo arrays those have the same default setting. + // + PCD_DEFAULT_INFO DefaultInfo[1]; + // + // Default data is stored as variable storage or the array of DATA_DELTA. + // +} PCD_DEFAULT_DATA; + +#define PCD_NV_STORE_DEFAULT_BUFFER_SIGNATURE SIGNATURE_32('N', 'S', 'D', 'B') + +typedef struct { + // + // PCD_NV_STORE_DEFAULT_BUFFER_SIGNATURE + // + UINT32 Signature; + // + // Length of the taken default buffer + // + UINT32 Length; + // + // Length of the total reserved buffer + // + UINT32 MaxLength; + // + // Reserved for 8 byte alignment + // + UINT32 Reserved; + // one or more PCD_DEFAULT_DATA +} PCD_NV_STORE_DEFAULT_BUFFER_HEADER; + +// +// NvStoreDefaultValueBuffer layout: +// +-------------------------------------+ +// | PCD_NV_STORE_DEFAULT_BUFFER_HEADER | +// +-------------------------------------+ +// | PCD_DEFAULT_DATA (DEFAULT, Standard)| +// +-------------------------------------+ +// | PCD_DATA_DELTA (DEFAULT, Standard)| +// +-------------------------------------+ +// | ...... | +// +-------------------------------------+ +// | PCD_DEFAULT_DATA (SKU A, Standard) | +// +-------------------------------------+ +// | PCD_DATA_DELTA (SKU A, Standard) | +// +-------------------------------------+ +// | ...... | +// +-------------------------------------+ +// + +#pragma pack(1) + +typedef struct { + UINT16 Offset; + UINT8 Value; +} DATA_DELTA; + +#pragma pack() + +/** + Create the Ras section in FFS + + @param[in] InputFilePath The input file path and name. + @param[in] OutputFilePath The output file path and name. + + @retval EFI_SUCCESS + +**/ +EFI_STATUS +CreateRawSection ( + IN CHAR8* InputFilePath, + IN CHAR8* OutputFilePath + ); + +/** + Create the Ras type of FFS + + @param[in] InputFilePath .efi file, it's optional unless process PE/TE section. + @param[in] OutputFilePath .te or .pe file + + @retval EFI_SUCCESS + +**/ +EFI_STATUS +CreateRawFfs ( + IN CHAR8** InputFilePaths, + IN CHAR8* OutputFilePath, + IN BOOLEAN SizeOptimized + ); + +#endif + diff --git a/BaseTools/Source/C/FCE/BinaryParse.c b/BaseTools/Source/C/FCE/BinaryParse.c new file mode 100644 index 0000000000..e9f8ee6826 --- /dev/null +++ b/BaseTools/Source/C/FCE/BinaryParse.c @@ -0,0 +1,1326 @@ +/** @file + + The API to parse the binary. + + Copyright (c) 2011-2019, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __GNUC__ +#include "windows.h" +#else +#include +#include +#include +#endif +#include "BinaryParse.h" +#include "BinaryCreate.h" +#include "VariableCommon.h" +#include "FirmwareVolumeBufferLib.h" + +extern G_EFI_FD_INFO gEfiFdInfo; +extern EFI_HANDLE mParsedGuidedSectionTools; +extern CHAR8 mInputFdName[MAX_FILENAME_LEN]; + +// +// The Guid to sign the position of Vfr and Uni array in FV +// +EFI_GUID gVfrArrayAttractGuid = EFI_VFR_ATTRACT_GUID; +EFI_GUID gUniStrArrayAttractGuid = EFI_UNI_STR_ATTRACT_GUID; +EFI_GUID gEfiSystemNvDataFvGuid = EFI_SYSTEM_NVDATA_FV_GUID; +EFI_GUID gEfiCrc32GuidedSectionExtractionProtocolGuid = EFI_CRC32_GUIDED_SECTION_EXTRACTION_PROTOCOL_GUID; + +/** + Converts a three byte length value into a UINT32. + + @param ThreeByteLength Pointer to the first of the 3 byte length. + + @retval Length Size of the section + +**/ +static +UINT32 +Get3ByteLength ( + IN UINT8 *ThreeByteLength + ) +{ + UINT32 Length; + + Length = 0; + + if (ThreeByteLength == NULL) { + return 0; + } + + Length = *((UINT32 *) ThreeByteLength); + Length = Length & 0x00FFFFFF; + + return Length; +} + +/** + Generate the unique template filename. +**/ +CHAR8 * +GenTempFile ( + VOID + ) +{ + CHAR8 *TemString; + TemString = NULL; +#ifndef __GNUC__ + TemString = CloneString (tmpnam (NULL)); +#else + CHAR8 tmp[] = "/tmp/fileXXXXXX"; + UINTN Fdtmp; + Fdtmp = mkstemp(tmp); + TemString = CloneString(tmp); + close(Fdtmp); +#endif + return TemString; +} + +/** + Check whether exist the same Ifr FFS. If not existed, return TRUE. + + @param[in] FfsImage The pointer to the binary. + @param[in] FileSize The size of binary. + + @return The string after convert. +**/ +static +BOOLEAN +NotExistSameFfsIfr ( + IN VOID *FfsImage +) +{ + UINT32 Index; + + Index = 0; + + while (gEfiFdInfo.FfsArray[Index] != NULL) { + if (memcmp (gEfiFdInfo.FfsArray[Index], FfsImage, sizeof (EFI_GUID)) == 0) { + return FALSE; + } + Index++; + } + return TRUE; +} + +/** + This function returns the next larger size that meets the alignment + requirement specified. + + @param ActualSize The size. + @param Alignment The desired alignment. + + @retval The Occupied length + +**/ +static +UINT32 +GetOccupiedSize ( + IN UINT32 ActualSize, + IN UINT32 Alignment + ) +{ + UINT32 OccupiedSize; + + OccupiedSize = ActualSize; + while ((OccupiedSize & (Alignment - 1)) != 0) { + OccupiedSize++; + } + + return OccupiedSize; +} + + +/** + Parses FFS Sections, and remove the FFS headers. Tis function olny handle one efi in this FFS. + + @param SectionBuffer The section base address + @param BufferLength The length of FFS. + @param EfiBufferHeader The structure dual pointer to the efi informations + + @retval EFI_SUCCESS The application exited normally. + @retval EFI_ABORTED An error occurred. + +**/ +EFI_STATUS +ParseSection ( + IN BOOLEAN IsFfsOrEfi, + IN OUT UINT8 *SectionBuffer, + IN UINT32 BufferLength, + IN OUT EFI_SECTION_STRUCT **EfiBufferHeader + ) +{ + UINT32 ParsedLength; + EFI_SECTION_TYPE Type; + UINT8 *Ptr; + UINT32 SectionLength; + UINT8 *CompressedBuffer; + UINT32 CompressedLength; + UINT8 *UncompressedBuffer; + UINT32 UncompressedLength; + UINT8 CompressionType; + DECOMPRESS_FUNCTION DecompressFunction; + GETINFO_FUNCTION GetInfoFunction; + UINT32 ScratchSize; + UINT8 *ScratchBuffer; + EFI_STATUS Status; + UINT32 DstSize; + CHAR8 *ExtractionTool; + CHAR8 *ToolInputFile; + CHAR8 *ToolOutputFile; + CHAR8 *SystemCommandFormatString; + CHAR8 *SystemCommand; + UINT8 *ToolOutputBuffer; + UINT32 ToolOutputLength; + BOOLEAN HasDepexSection; + + Ptr = NULL; + SectionLength = 0; + CompressedBuffer = NULL; + CompressedLength = 0; + UncompressedBuffer = NULL; + UncompressedLength = 0; + CompressionType = 0; + ScratchSize = 0; + ScratchBuffer = NULL; + Status = EFI_SUCCESS; + DstSize = 0; + ExtractionTool = NULL; + ToolInputFile = NULL; + ToolOutputFile = NULL; + SystemCommandFormatString = NULL; + SystemCommand = NULL; + + // + // Jump the FFS header + // + if (IsFfsOrEfi) { + SectionBuffer = SectionBuffer + sizeof (EFI_FFS_FILE_HEADER); + BufferLength = BufferLength - sizeof (EFI_FFS_FILE_HEADER); + } + ParsedLength = 0; + HasDepexSection = FALSE; + ExtractionTool = NULL; + ToolOutputLength = 0; + ToolOutputBuffer = NULL; + + (*EfiBufferHeader)->Length = BufferLength; + + while (ParsedLength < BufferLength) { + Ptr = SectionBuffer + ParsedLength; + + SectionLength = Get3ByteLength (((EFI_COMMON_SECTION_HEADER *) Ptr)->Size); + Type = ((EFI_COMMON_SECTION_HEADER *) Ptr)->Type; + + // + // This is sort of an odd check, but is necessary because FFS files are + // padded to a QWORD boundary, meaning there is potentially a whole section + // header worth of 0xFF bytes. + // + if ((SectionLength == 0xffffff) && (Type == 0xff)) { + ParsedLength += 4; + continue; + } + + switch (Type) { + + case EFI_SECTION_PE32: + case EFI_SECTION_TE: + // + //Got the correct address + // + (*EfiBufferHeader)->BufferBase = (UINTN)(Ptr + sizeof (EFI_COMMON_SECTION_HEADER)); + return EFI_SUCCESS; + + case EFI_SECTION_RAW: + case EFI_SECTION_PIC: + break; + + case EFI_SECTION_USER_INTERFACE: + HasDepexSection = FALSE; + break; + + case EFI_SECTION_FIRMWARE_VOLUME_IMAGE: + case EFI_SECTION_COMPATIBILITY16: + case EFI_SECTION_FREEFORM_SUBTYPE_GUID: + break; + + case EFI_SECTION_PEI_DEPEX: + case EFI_SECTION_DXE_DEPEX: + case EFI_SECTION_SMM_DEPEX: + HasDepexSection = TRUE; + break; + + case EFI_SECTION_VERSION: + break; + case EFI_SECTION_COMPRESSION: + UncompressedBuffer = NULL; + CompressedLength = SectionLength - sizeof (EFI_COMPRESSION_SECTION); + UncompressedLength = ((EFI_COMPRESSION_SECTION *) Ptr)->UncompressedLength; + CompressionType = ((EFI_COMPRESSION_SECTION *) Ptr)->CompressionType; + + if (CompressionType == EFI_NOT_COMPRESSED) { + if (CompressedLength != UncompressedLength) { + Error ( + NULL, + 0, + 0, + "file is not compressed, but the compressed length does not match the uncompressed length", + NULL + ); + return EFI_ABORTED; + } + + UncompressedBuffer = Ptr + sizeof (EFI_COMPRESSION_SECTION); + } else if (CompressionType == EFI_STANDARD_COMPRESSION) { + GetInfoFunction = EfiGetInfo; + DecompressFunction = EfiDecompress; + CompressedBuffer = Ptr + sizeof (EFI_COMPRESSION_SECTION); + + Status = GetInfoFunction ( + CompressedBuffer, + CompressedLength, + &DstSize, + &ScratchSize + ); + if (EFI_ERROR (Status)) { + Error (NULL, 0, 0003, "error getting compression info from compression section", NULL); + return EFI_ABORTED; + } + + if (DstSize != UncompressedLength) { + Error (NULL, 0, 0003, "compression error in the compression section", NULL); + return EFI_ABORTED; + } + + ScratchBuffer = malloc (ScratchSize); + if (ScratchBuffer == NULL) { + return EFI_ABORTED; + } + UncompressedBuffer = malloc (UncompressedLength); + if (UncompressedBuffer == NULL) { + free (ScratchBuffer); + return EFI_ABORTED; + } + memset (UncompressedBuffer, 0, UncompressedLength); + + Status = DecompressFunction ( + CompressedBuffer, + CompressedLength, + UncompressedBuffer, + UncompressedLength, + ScratchBuffer, + ScratchSize + ); + free (ScratchBuffer); + if (Status != EFI_SUCCESS) { + Error (NULL, 0, 0003, "decompress failed", NULL); + free (UncompressedBuffer); + return EFI_ABORTED; + } + } else { + Error (NULL, 0, 0003, "unrecognized compression type", "type 0x%X", CompressionType); + return EFI_ABORTED; + } + + Status = ParseSection (FALSE, UncompressedBuffer, UncompressedLength, EfiBufferHeader); + if (Status != EFI_SUCCESS) { + Error (NULL, 0, 0003, "failed to parse section", NULL); + free (UncompressedBuffer); + UncompressedBuffer = NULL; + } else { + return EFI_SUCCESS; + } + // + // Store the allocate memory address for UncompressedBuffer + // + if (UncompressedBuffer != NULL) { + (*EfiBufferHeader)->UncompressedBuffer[(*EfiBufferHeader)->UnCompressIndex] = (UINTN) UncompressedBuffer; + (*EfiBufferHeader)->UnCompressIndex = (*EfiBufferHeader)->UnCompressIndex + 1; + } + break; + + case EFI_SECTION_GUID_DEFINED: + // + // Decompress failed, and then check for CRC32 sections which we can handle internally if needed. + // Maybe this section is no-compressed. + // + if (!CompareGuid ( + &((EFI_GUID_DEFINED_SECTION *) Ptr)->SectionDefinitionGuid, + &gEfiCrc32GuidedSectionExtractionProtocolGuid + )) { + // + // CRC32 guided section + // + Status = ParseSection ( + FALSE, + SectionBuffer + ((EFI_GUID_DEFINED_SECTION *) Ptr)->DataOffset, + BufferLength - ((EFI_GUID_DEFINED_SECTION *) Ptr)->DataOffset, + EfiBufferHeader + ); + if (EFI_ERROR (Status)) { + Error (NULL, 0, 0003, "parse of CRC32 GUIDED section failed", NULL); + return EFI_ABORTED; + } else { + return EFI_SUCCESS; + } + } else { + ExtractionTool = LookupGuidedSectionToolPath ( + mParsedGuidedSectionTools, + &((EFI_GUID_DEFINED_SECTION *) Ptr)->SectionDefinitionGuid + ); + + if (ExtractionTool != NULL) { + ToolInputFile = GenTempFile (); + ToolOutputFile = GenTempFile (); + // + // Construction 'system' command string + // + SystemCommandFormatString = "%s -d -o \"%s\" \"%s\""; + SystemCommand = malloc ( + strlen (SystemCommandFormatString) \ + + strlen (ExtractionTool) \ + + strlen (ToolInputFile) \ + + strlen (ToolOutputFile) \ + + 1 + ); + if (SystemCommand == NULL) { + free (ExtractionTool); + free (ToolInputFile); + free (ToolOutputFile); + return EFI_ABORTED; + } + sprintf ( + SystemCommand, + "%s -d -o \"%s\" \"%s\"", + ExtractionTool, + ToolOutputFile, + ToolInputFile + ); + free (ExtractionTool); + + Status = PutFileImage ( + ToolInputFile, + (CHAR8*) Ptr + ((EFI_GUID_DEFINED_SECTION *) Ptr)->DataOffset, + SectionLength - ((EFI_GUID_DEFINED_SECTION *) Ptr)->DataOffset + ); + + if (HasDepexSection) { + HasDepexSection = FALSE; + } + + system (SystemCommand); + remove (ToolInputFile); + free (ToolInputFile); + ToolInputFile = NULL; + free (SystemCommand); + SystemCommand = NULL; + + if (EFI_ERROR (Status)) { + Error ("FCE", 0, 0004, "unable to decoded GUIDED section", NULL); + free (ToolOutputFile); + return EFI_ABORTED; + } + + Status = GetFileImage ( + ToolOutputFile, + (CHAR8 **)&ToolOutputBuffer, + &ToolOutputLength + ); + remove (ToolOutputFile); + free (ToolOutputFile); + ToolOutputFile = NULL; + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + } + Status = ParseSection ( + FALSE, + ToolOutputBuffer, + ToolOutputLength, + EfiBufferHeader + ); + if (EFI_ERROR (Status)) { + Error (NULL, 0, 0003, "parse of decoded GUIDED section failed", NULL); + return EFI_ABORTED; + } + } + break; + + default: + ; + } + ParsedLength += SectionLength; + // + // We make then next section begin on a 4-byte boundary + // + ParsedLength = GetOccupiedSize (ParsedLength, 4); + } + + return EFI_ABORTED; +} + +static +BOOLEAN +GetNextOffset ( + IN UINT8 *Data, + IN EFI_GUID *Guid, + IN UINTN Len, + IN OUT UINTN *Offset + ) +{ + UINTN NextOffset; + if (*Offset >= Len || Len - *Offset <= sizeof (EFI_GUID)) { + return FALSE; + } + + for (NextOffset = *Offset; NextOffset < Len - sizeof (EFI_GUID); NextOffset++) { + if (CompareGuid(Guid, (EFI_GUID*)(Data + NextOffset)) == 0) { + *Offset = NextOffset + sizeof(EFI_GUID); + return TRUE; + } + } + return FALSE; +} + +/** + Get the address by Guid. + + Parse the FFS image, and find the GUID address.There may be some Guids matching the + searched Guid. + + @param Fv the Pointer to the image. + @param Guid The Guid need to find. + @param Offset The dual Pointer to the offset. + @param NumOfMatchGuid The number of matching Guid offset. + + @retval EFI_SUCCESS The Search was complete successfully + @return EFI_ABORTED An error occurred +**/ +EFI_STATUS +GetAddressByGuid ( + IN VOID *Fv, + IN EFI_GUID *Guid, + IN UINTN Len, + OUT UINTN **Offset, + OUT UINT8 *NumOfMatchGuid + ) +{ + VOID *LocalFv; + UINT8 Flag; + + EFI_RAW_SECTION* Section; + UINT8 *RawData; + VOID* SectionStart; + UINTN NextOffset; + UINTN Key; + UINTN TotalSectionsSize; + UINTN SecLen; + UINTN SecHdr; + EFI_STATUS Status; + + if( (Fv == NULL) || (Fv == NULL) || (Guid == NULL) || Len == 0 ){ + return EFI_ABORTED; + } + + LocalFv = Fv; + Flag = 0; + Section = NULL; + Key = 0; + + if (NumOfMatchGuid != NULL) { + *NumOfMatchGuid = 0; + } + + SectionStart = (VOID*)((UINTN)LocalFv + FvBufGetFfsHeaderSize(LocalFv)); + TotalSectionsSize = Len - FvBufGetFfsHeaderSize(LocalFv); + while (TRUE) { + Status = FvBufFindNextSection ( + SectionStart, + TotalSectionsSize, + &Key, + (VOID **)&Section + ); + if (Section == NULL || EFI_ERROR (Status)) { + break; + } + + if (EFI_SECTION_RAW == Section->Type) { + if ((*(UINT32 *)Section->Size & 0xffffff) == 0xffffff) { + SecLen = ((EFI_RAW_SECTION2 *)Section)->ExtendedSize; + SecHdr = sizeof(EFI_RAW_SECTION2); + } else { + SecLen = *(UINT32 *)Section->Size & 0xffffff; + SecHdr = sizeof(EFI_RAW_SECTION); + } + if (SecLen <= SecHdr || SecLen - SecHdr < sizeof(EFI_GUID)) { + continue; + } + RawData = (UINT8 *)Section + SecHdr; + NextOffset = 0; + while (GetNextOffset(RawData, Guid, SecLen - SecHdr, &NextOffset)) { + Flag = 1; + if ((NumOfMatchGuid != NULL) && (Offset != NULL)) { + if (*NumOfMatchGuid == 0) { + *Offset = malloc (sizeof (UINTN) * MAX_MATCH_GUID_NUM); + if (*Offset == NULL) { + return EFI_ABORTED; + } + memset (*Offset, 0, sizeof (UINTN) * MAX_MATCH_GUID_NUM); + } + *(*Offset + *NumOfMatchGuid) = NextOffset + (RawData - (UINT8 *)Fv); + (*NumOfMatchGuid)++; + } else { + return EFI_SUCCESS; + } + } + } + } + + if( Flag == 0 ) { + return EFI_ABORTED; + } + return EFI_SUCCESS; +} + +/** + Search the VfrBin Base address. + + According the known GUID gVfrArrayAttractGuid to get the base address from FFS. + + @param Fv the Pointer to the FFS + @param EfiAddr the Pointer to the EFI in FFS + @param Length the length of Fv + @param Offset the Pointer to the Addr (Offset) + @param NumOfMachingOffset the number of Addr (Offset) + + @retval EFI_SUCCESS Get the address successfully. +**/ +EFI_STATUS +SearchVfrBinInFFS ( + IN VOID *Fv, + IN VOID *EfiAddr, + IN UINTN Length, + OUT UINTN **Offset, + OUT UINT8 *NumOfMachingOffset + ) +{ + UINTN Index; + EFI_STATUS Status; + UINTN VirOffValue; + + Index = 0; + Status = EFI_SUCCESS; + VirOffValue = 0; + + if ((Fv == NULL) || (Offset == NULL)) { + return EFI_ABORTED; + } + Status = GetAddressByGuid ( + Fv, + &gVfrArrayAttractGuid, + Length, + Offset, + NumOfMachingOffset + ); + if (Status != EFI_SUCCESS) { + return EFI_ABORTED; + } + + while (Index < *NumOfMachingOffset) { + // + // Got the virOffset after the GUID + // + VirOffValue = *(UINTN *)((UINTN)Fv + *(*Offset + Index)); + // + //Transfer the offset to the VA address. One modules may own more VfrBin address. + // + *(*Offset + Index) = (UINTN) EfiAddr + VirOffValue; + Index++; + } + return EFI_SUCCESS; +} + +/** + Search the UniBin Base address. + + According the known GUID gUniStrArrayAttractGuid to get the base address from FFS. + + @param Fv the Pointer to the FFS + @param EfiAddr the Pointer to the EFI in FFS + @param Length the length of Fv + @param Offset the Pointer to the Addr (Offset) + + @retval Base address Get the address successfully. +**/ +EFI_STATUS +SearchUniBinInFFS ( + IN VOID *Fv, + IN VOID *EfiAddr, + IN UINTN Length, + OUT UINTN **Offset + ) +{ + UINT8 NumOfMachingOffset; + EFI_STATUS Status; + UINTN VirOffValue; + + NumOfMachingOffset = 0; + Status = EFI_SUCCESS; + VirOffValue = 0; + + if ((Fv == NULL) || (Offset == NULL)) { + return EFI_ABORTED; + } + Status = GetAddressByGuid ( + Fv, + &gUniStrArrayAttractGuid, + Length, + Offset, + &NumOfMachingOffset + ); + if (Status != EFI_SUCCESS) { + return EFI_ABORTED; + } + // + //Transfer the offset to the VA address. There is only one UniArray in one modules. + // + if (NumOfMachingOffset == 1) { + VirOffValue = *(UINTN *)((UINTN)Fv + **Offset); + **Offset = (UINTN) EfiAddr + VirOffValue; + } else { + printf ("Error. Find more than 1 UniBin in FFS.\n"); + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +SearchNvStoreDatabaseInFd( + IN VOID *Fv, + IN UINTN length + ) +{ + EFI_STATUS Status; + UINTN Offset; + PCD_NV_STORE_DEFAULT_BUFFER_HEADER *NvStoreHeader; + Status = EFI_SUCCESS; + Offset = 0; + if (Fv == NULL) { + printf ("The FV is NULL."); + return EFI_ABORTED; + } + while (Offset < (length - sizeof(PCD_NV_STORE_DEFAULT_BUFFER_HEADER))){ + NvStoreHeader = (PCD_NV_STORE_DEFAULT_BUFFER_HEADER *)((UINT8*)Fv + Offset); + if (NvStoreHeader->Signature == PCD_NV_STORE_DEFAULT_BUFFER_SIGNATURE) { + gEfiFdInfo.ExistNvStoreDatabase = TRUE; + gEfiFdInfo.NvStoreDatabase = (UINT8 *) NvStoreHeader; + break; + } + Offset++; + } + if (Offset == (length - sizeof(PCD_NV_STORE_DEFAULT_BUFFER_HEADER)) || gEfiFdInfo.ExistNvStoreDatabase != TRUE) { + //printf ("Not found the PcdNvStoreDefaultValueBuffer\n"); + return Status; + } + return Status; +} + +/** + Get the address by Guid. + + Parse the FD image, and find the GUID address.There may be some Guids matching the + searched Guid. + + @param Fv the Pointer to the image. + @param Guid The Guid need to find. + @param Offset The dual Pointer to the offset. + @param NumOfMatchGuid The number of matching Guid offset. + + @retval EFI_SUCCESS The Search was complete successfully + @return EFI_ABORTED An error occurred +**/ +EFI_STATUS +GetVariableAddressByGuid ( + IN VOID *Fv, + IN EFI_GUID *Guid, + IN UINTN Len, + OUT UINTN **Offset, + OUT UINT8 *NumOfMatchGuid + ) +{ + UINTN NextOffset; + UINT8 Flag; + + if( (Fv == NULL) || (Fv == NULL) || (Guid == NULL) ){ + return EFI_ABORTED; + } + + Flag = 0; + NextOffset = 0; + + if (NumOfMatchGuid != NULL) { + *NumOfMatchGuid = 0; + } + while (GetNextOffset(Fv, Guid, Len, &NextOffset)) { + Flag = 1; + if (NumOfMatchGuid != NULL && Offset != NULL) { + if (*NumOfMatchGuid == 0) { + *Offset = malloc (sizeof (UINTN) * MAX_MATCH_GUID_NUM); + if (*Offset == NULL) { + return EFI_ABORTED; + } + memset (*Offset, 0, sizeof (UINTN) * MAX_MATCH_GUID_NUM); + } + *(*Offset + *NumOfMatchGuid) = NextOffset; + (*NumOfMatchGuid)++; + } else { + return EFI_SUCCESS; + } + } + + if( Flag == 0 ) { + return EFI_ABORTED; + } + return EFI_SUCCESS; +} + +/** + Search the EFI Variable Base address. + + According the known GUID gEfiSystemNvDataFvGuid to get the base address from FFS. + + @param Fv the Pointer to the FFS + @param Length the length of Fv + @param Offset the Pointer to the Addr (Offset) + @param NumOfMachingOffset the number of IFR array in one FFS + + @retval EFI_SUCCESS Get the address successfully. + @retval EFI_ABORTED An error occured. +**/ +EFI_STATUS +SearchEfiVarInFFS ( + IN VOID *Fv, + IN UINTN Length, + OUT UINTN **Offset, + OUT UINT8 *NumOfMachingOffset + ) +{ + EFI_STATUS Status; + UINT8 Index; + + Status = EFI_SUCCESS; + Index = 0; + + if ((Fv == NULL) || (Offset == NULL)) { + printf ("The FV or offset is NULL."); + return EFI_ABORTED; + } + Status = GetVariableAddressByGuid ( + Fv, + &gEfiSystemNvDataFvGuid, + Length, + Offset, + NumOfMachingOffset + ); + if (Status != EFI_SUCCESS) { + return EFI_ABORTED; + } + // + //Transfer the offset to the VA address. + // + while (Index < *NumOfMachingOffset) { + *(*Offset + Index) = (UINTN) Fv + *(*Offset + Index); + Index++; + } + return EFI_SUCCESS; +} + +/** + Parse the Ffs header to get the size. + + @param InputFile The pointer to the input file + @param FvSize The pointer to the file size + + @return EFI_SUCCESS Get the file size successfully +**/ +EFI_STATUS +ReadFfsHeader ( + IN FILE *InputFile, + OUT UINT32 *FvSize + ) +{ + EFI_FFS_FILE_HEADER FfsHeader; + EFI_FV_FILETYPE Type; + + // + // Check input parameters + // + if ((InputFile == NULL) || (FvSize == NULL)) { + return EFI_ABORTED; + } + // + // Read the header + // + fread ( + &FfsHeader, + sizeof (EFI_FFS_FILE_HEADER), + 1, + InputFile + ); + Type = FfsHeader.Type; + + if (Type == EFI_FV_FILETYPE_DRIVER) { + *FvSize = *(UINT32 *)FfsHeader.Size & 0xffffff; + } else if (Type == EFI_FV_FILETYPE_APPLICATION) { + *FvSize = *(UINT32 *)FfsHeader.Size & 0xffffff; + } else if (Type == EFI_FV_FILETYPE_FREEFORM) { + *FvSize = *(UINT32 *)FfsHeader.Size & 0xffffff; + } else { + return EFI_ABORTED; + } + return EFI_SUCCESS; +} + +/* + Read the length of the whole FD + + This function determines the size of the FV. + + @param InputFile The file that contains the FV image. + @param FvSize The size of the FV. + + @retval EFI_SUCCESS The application exited normally. + @retval EFI_ABORTED An error occurred. + +**/ +static +EFI_STATUS +ReadFdHeader ( + IN FILE *InputFile, + OUT UINT32 *FvSize + ) +{ + // + // Check input parameters + // + if ((InputFile == NULL) || (FvSize == NULL)) { + return EFI_ABORTED; + } + *FvSize = 0; + // + // Get the total size of FD file (Fixed the length) + // + fseek(InputFile,0,SEEK_END); + *FvSize = ftell(InputFile); + fseek(InputFile,0,SEEK_SET); + + if (*FvSize == 0) { + return EFI_ABORTED; + } + return EFI_SUCCESS; +} + +/** + Read the file to memory. + + @param InputFile The file that contains the FV image. + @param Size The size of the file. + + @retval The pointer to the begining position of memory. +**/ +VOID * +ReadFileToMemory ( + IN CHAR8 *FileName, + OUT UINT32 *Size + ) +{ + FILE *InFile; + VOID *Address; + UINT32 BytesRead; + EFI_STATUS Status; + + InFile = NULL; + Address = NULL; + BytesRead = 0; + Status = EFI_SUCCESS; + + InFile = fopen (FileName,"rb"); + if (InFile == NULL) { + return NULL; + } + // + // Determine the size of FV + // + Status = ReadFdHeader (InFile, Size); + if (Status != EFI_SUCCESS) { + fclose (InFile); + return NULL; + } + // + // Allocate a buffer for the FV image + // + Address = malloc (*Size); + if (Address == NULL) { + fclose (InFile); + return NULL; + } + memset (Address, 0, *Size); + // + // Seek to the start of the image, then read the entire FV to the buffer + // + fseek (InFile, 0, SEEK_SET); + BytesRead = fread (Address, 1, *Size, InFile); + fclose (InFile); + if ((UINTN) BytesRead != *Size) { + free (Address); + return NULL; + } + return Address; +} + +/** + Search the EFI variables address in Fd. + + Open and read the *.fd to the memory, initialize the global structure. + Update the EFI variables addr and the begining position of memory. + + @retval EFI_SUCCESS Get the address successfully. +**/ +EFI_STATUS +GetEfiVariablesAddr ( + BOOLEAN UqiIsSet + ) +{ + VOID *FdImage; + UINT32 FdSize; + EFI_STATUS Status; + UINTN *EfiVarAddr; + UINT8 NumOfMachingVar; + UINT32 Index; + BOOLEAN GotFlag; + EFI_FIRMWARE_VOLUME_HEADER *Variable; + BOOLEAN AuthencitatedMonotonicOrNot; + BOOLEAN AuthencitatedBasedTimeOrNot; + BOOLEAN NormalOrNot; + + FdImage = NULL; + FdSize = 0; + Status = EFI_SUCCESS; + EfiVarAddr = NULL; + NumOfMachingVar = 0; + Index = 0; + GotFlag = TRUE; + Variable = NULL; + + FdImage = ReadFileToMemory (mInputFdName, &FdSize); + if (FdImage == NULL) { + return EFI_ABORTED; + } + if (!UqiIsSet) { + Status = SearchNvStoreDatabaseInFd(FdImage, FdSize); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + } + Status = SearchEfiVarInFFS ( + FdImage, + FdSize, + &EfiVarAddr, + &NumOfMachingVar + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + // + // Check the signature "_FVH" + // + Index = 0; + GotFlag = FALSE; + + while (Index < NumOfMachingVar) { + Variable = (EFI_FIRMWARE_VOLUME_HEADER *)(*(EfiVarAddr + Index) - 0x20); + if (Variable->Signature == 0x4856465F) { + AuthencitatedMonotonicOrNot = CheckMonotonicBasedVarStore ((UINT8 *)Variable + Variable->HeaderLength); + AuthencitatedBasedTimeOrNot = CheckTimeBasedVarStoreOrNot ((UINT8 *)Variable + Variable->HeaderLength); + NormalOrNot = CheckNormalVarStoreOrNot ((UINT8 *)Variable + Variable->HeaderLength); + if (AuthencitatedMonotonicOrNot || AuthencitatedBasedTimeOrNot || NormalOrNot) { + GotFlag = TRUE; + gEfiFdInfo.EfiVariableAddr = (UINTN)Variable; + break; + } + } + Index++; + } + free (EfiVarAddr); + if (!GotFlag) { + return EFI_ABORTED; + } + gEfiFdInfo.Fd = FdImage; + gEfiFdInfo.FdSize = FdSize; + + return EFI_SUCCESS; +} + +/** + Pick up the FFS which includes IFR section. + + Parse all FFS extracted by BfmLib, and save all which includes IFR + Binary to gEfiFdInfo structure. + + @retval EFI_SUCCESS Get the address successfully. + @retval EFI_BUFFER_TOO_SMALL Memory can't be allocated. + @retval EFI_ABORTED Read FFS Failed. +**/ +EFI_STATUS +FindFileInFolder ( + IN CHAR8 *FolderName, + OUT BOOLEAN *ExistStorageInBfv, + OUT BOOLEAN *SizeOptimized +) +{ + CHAR8 *FileName; + CHAR8 *CurFolderName; + EFI_STATUS Status; + UINTN MaxFileNameLen; + UINTN Index; + CHAR8 FileNameArry[MAX_FILENAME_LEN]; + FILE *FfsFile; + UINTN FileSize; + VOID *FfsImage; + UINTN BytesRead; +#ifndef __GNUC__ + HANDLE FindHandle; + WIN32_FIND_DATA FindFileData; +#else + struct dirent *pDirent; + DIR *pDir; +#endif + + FileName = NULL; + CurFolderName = NULL; + Status = EFI_SUCCESS; + MaxFileNameLen = 0; + Index = 0; + FileSize = 0; + BytesRead = 0; + FfsImage = NULL; + FfsFile = NULL; + + MaxFileNameLen = strlen (FolderName) + MAX_FILENAME_LEN; + CurFolderName = (CHAR8 *)calloc( + strlen (FolderName) + strlen (OS_SEP_STR) + strlen ("*.*")+ 1, + sizeof(CHAR8) + ); + if (CurFolderName == NULL) { + return EFI_BUFFER_TOO_SMALL; + } + strcpy (CurFolderName, FolderName); + strcat (CurFolderName, OS_SEP_STR); + strcat (CurFolderName, "*.*"); + FileName = (CHAR8 *)calloc( + MaxFileNameLen, + sizeof(CHAR8) + ); + if (FileName == NULL) { + free (CurFolderName); + return EFI_BUFFER_TOO_SMALL; + } + +#ifndef __GNUC__ + if((FindHandle = FindFirstFile(CurFolderName, &FindFileData)) != INVALID_HANDLE_VALUE){ + do { + memset (FileName, 0, MaxFileNameLen); + if ((strcmp (FindFileData.cFileName, ".") == 0) + || (strcmp (FindFileData.cFileName, "..") == 0) + ) { + continue; + } + if (strlen(FolderName) + strlen ("\\") + strlen (FindFileData.cFileName) > MAX_FILENAME_LEN - 1) { + Status = EFI_ABORTED; + goto Done; + } + snprintf (FileNameArry, MAX_FILENAME_LEN, "%s%c%s", FolderName, OS_SEP, FindFileData.cFileName); + FfsFile = fopen (FileNameArry, "rb"); + if (FfsFile == NULL) { + Status = EFI_ABORTED; + goto Done; + } + Status = ReadFfsHeader (FfsFile, (UINT32 *)&FileSize); + if (EFI_ERROR (Status)) { + fclose (FfsFile); + Status = EFI_SUCCESS; + continue; + } + // + // Allocate a buffer for the FFS file + // + FfsImage = malloc (FileSize); + if (FfsImage == NULL) { + fclose (FfsFile); + Status = EFI_BUFFER_TOO_SMALL; + goto Done; + } + // + // Seek to the start of the image, then read the entire FV to the buffer + // + fseek (FfsFile, 0, SEEK_SET); + BytesRead = fread (FfsImage, 1, FileSize, FfsFile); + fclose (FfsFile); + + if ((UINTN) BytesRead != FileSize) { + free (FfsImage); + Status = EFI_ABORTED; + goto Done; + } + // + // Check whether exists the storage ffs in BFV for multi-platform mode + // + if (CompareGuid(&gEfiFfsBfvForMultiPlatformGuid,(EFI_GUID *) FfsImage) == 0) { + *ExistStorageInBfv = TRUE; + *SizeOptimized = FALSE; + gEfiFdInfo.StorageFfsInBfv = FfsImage; + continue; + } + // + // Check whether exists the optimized storage ffs in BFV for multi-platform mode + // + if (CompareGuid(&gEfiFfsBfvForMultiPlatformGuid2,(EFI_GUID *) FfsImage) == 0) { + *ExistStorageInBfv = TRUE; + *SizeOptimized = TRUE; + gEfiFdInfo.StorageFfsInBfv = FfsImage; + continue; + } + // + // Check whether current FFS includes IFR + // + Status = GetAddressByGuid ( + FfsImage, + &gVfrArrayAttractGuid, + FileSize, + NULL, + NULL + ); + if (EFI_ERROR (Status)) { + free (FfsImage); + Status = EFI_SUCCESS; + } else { + // + // Check whether existed same IFR binary. If existed, not insert the new one. + // + if (NotExistSameFfsIfr (FfsImage)) { + gEfiFdInfo.FfsArray[Index] = FfsImage; + gEfiFdInfo.Length[Index++] = FileSize; + } + } + + } while (FindNextFile (FindHandle, &FindFileData)); + FindClose(FindHandle); + } else { + Status = EFI_ABORTED; + goto Done; + } + +Done: + free (CurFolderName); + free (FileName); + +#else + if((pDir = opendir(FolderName)) != NULL){ + while ((pDirent = readdir(pDir)) != NULL){ + memset (FileName, 0, MaxFileNameLen); + if ((strcmp (pDirent->d_name, ".") == 0) + || (strcmp (pDirent->d_name, "..") == 0) + ) { + continue; + } + sprintf (FileNameArry, "%s%c%s", FolderName, OS_SEP, pDirent->d_name); + FfsFile = fopen (FileNameArry, "rb"); + Status = ReadFfsHeader (FfsFile, (UINT32 *)&FileSize); + if (EFI_ERROR (Status)) { + fclose (FfsFile); + Status = EFI_SUCCESS; + continue; + } + // + // Allocate a buffer for the FFS file + // + FfsImage = malloc (FileSize); + if (FfsImage == NULL) { + fclose (FfsFile); + Status = EFI_BUFFER_TOO_SMALL; + goto Done; + } + // + // Seek to the start of the image, then read the entire FV to the buffer + // + fseek (FfsFile, 0, SEEK_SET); + BytesRead = fread (FfsImage, 1, FileSize, FfsFile); + fclose (FfsFile); + + if ((UINTN) BytesRead != FileSize) { + free (FfsImage); + Status = EFI_ABORTED; + goto Done; + } + // + // Check whether exists the storage ffs in BFV for multi-platform mode + // + if (CompareGuid(&gEfiFfsBfvForMultiPlatformGuid,(EFI_GUID *) FfsImage) == 0) { + *ExistStorageInBfv = TRUE; + *SizeOptimized = FALSE; + gEfiFdInfo.StorageFfsInBfv = FfsImage; + continue; + } + // + // Check whether exists the optimized storage ffs in BFV for multi-platform mode + // + if (CompareGuid(&gEfiFfsBfvForMultiPlatformGuid2,(EFI_GUID *) FfsImage) == 0) { + *ExistStorageInBfv = TRUE; + *SizeOptimized = TRUE; + gEfiFdInfo.StorageFfsInBfv = FfsImage; + continue; + } + // + // Check whether current FFS includes IFR + // + Status = GetAddressByGuid ( + FfsImage, + &gVfrArrayAttractGuid, + FileSize, + NULL, + NULL + ); + if (EFI_ERROR (Status)) { + free (FfsImage); + Status = EFI_SUCCESS; + } else { + // + // Check whether existed same IFR binary. If existed, not insert the new one. + // + if (NotExistSameFfsIfr (FfsImage)) { + gEfiFdInfo.FfsArray[Index] = FfsImage; + gEfiFdInfo.Length[Index++] = FileSize; + } + } + + } + closedir(pDir); + } else { + Status = EFI_ABORTED; + goto Done; + } + +Done: + free (CurFolderName); + free (FileName); +#endif + return Status; +} + diff --git a/BaseTools/Source/C/FCE/BinaryParse.h b/BaseTools/Source/C/FCE/BinaryParse.h new file mode 100644 index 0000000000..a3995b8b79 --- /dev/null +++ b/BaseTools/Source/C/FCE/BinaryParse.h @@ -0,0 +1,187 @@ +/** @file + + The API to parse the binary. + + Copyright (c) 2011-2019, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#ifndef _BINARY_PARSE_H_ +#define _BINARY_PARSE_H_ 1 + +#include +#include "Compress.h" +#include "Decompress.h" +#include "CommonLib.h" +#include "EfiUtilityMsgs.h" +#include "FirmwareVolumeBufferLib.h" +#include "OsPath.h" +#include "ParseGuidedSectionTools.h" +#include "StringFuncs.h" +#include "ParseInf.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __GNUC__ +#define OS_SEP '/' +#define OS_SEP_STR "/" +#else +#define OS_SEP '\\' +#define OS_SEP_STR "\\" +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define snprintf _snprintf +#endif + +#define TEMP_DIR_NAME "Temp" +#define MAX_FILENAME_LEN 200 +#define MAX_MATCH_GUID_NUM 100 +#define MAX_EFI_IN_FFS_NUM 100 + +typedef struct { + VOID *Fd; + UINT32 FdSize; + UINTN EfiVariableAddr; + UINTN Length[MAX_EFI_IN_FFS_NUM]; + VOID *FfsArray[MAX_EFI_IN_FFS_NUM]; + VOID *StorageFfsInBfv; + VOID *NvStoreDatabase; + BOOLEAN ExistNvStoreDatabase; +} G_EFI_FD_INFO; + +/// +///Define the structure for th sections +/// +typedef struct { + UINTN BufferBase; + UINTN UncompressedBuffer[MAX_EFI_IN_FFS_NUM]; + UINTN Length; + UINT8 UnCompressIndex; +} EFI_SECTION_STRUCT; + +// {d0bc7cb4-6a47-495f-aa11-710746da06a2} +#define EFI_VFR_ATTRACT_GUID \ +{ 0xd0bc7cb4, 0x6a47, 0x495f, { 0xaa, 0x11, 0x71, 0x7, 0x46, 0xda, 0x6, 0xa2 } } + +// {8913C5E0-33F6-4d86-9BF1-43EF89FC0666} +#define EFI_UNI_STR_ATTRACT_GUID \ +{ 0x8913c5e0, 0x33f6, 0x4d86, { 0x9b, 0xf1, 0x43, 0xef, 0x89, 0xfc, 0x6, 0x66 } } + +// {FFF12B8D-7696-4C8B-A985-2747075B4F50} +#define EFI_SYSTEM_NVDATA_FV_GUID \ +{ 0xFFF12B8D, 0x7696, 0x4C8B, { 0xA9, 0x85, 0x27, 0x47, 0x07, 0x5B, 0x4F, 0x50 }} + +/** + Parses FFS Sections, and remove the FFS headers. Tis function olny handle one efi in this FFS. + + @param SectionBuffer The section base address + @param BufferLength The length of FFS. + @param EfiBufferHeader The structure dual pointer to the efi informations + + @retval EFI_SUCCESS The application exited normally. + @retval EFI_ABORTED An error occurred. + +**/ +EFI_STATUS +ParseSection ( + IN BOOLEAN IsFfsOrEfi, + IN OUT UINT8 *SectionBuffer, + IN UINT32 BufferLength, + IN OUT EFI_SECTION_STRUCT **EfiBufferHeader + ); + +/** + Search the VfrBin Base address. + + According the known GUID gVfrArrayAttractGuid to get the base address from FFS. + + @param Fv the Pointer to the FFS + @param EfiAddr the Pointer to the EFI in FFS + @param Length the length of Fv + @param Offset the Pointer to the Addr (Offset) + @param NumOfMachingOffset the number of Addr (Offset) + + @retval EFI_SUCCESS Get the address successfully. +**/ +EFI_STATUS +SearchVfrBinInFFS ( + IN VOID *Fv, + IN VOID *EfiAddr, + IN UINTN Length, + OUT UINTN **Offset, + OUT UINT8 *NumOfMachingOffset + ); + +/** + Search the UniBin Base address. + + According the known GUID gUniStrArrayAttractGuid to get the base address from FFS. + + @param Fv the Pointer to the FFS + @param EfiAddr the Pointer to the EFI in FFS + @param Length the length of Fv + @param Offset the Pointer to the Addr (Offset) + + @retval Base address Get the address successfully. +**/ +EFI_STATUS +SearchUniBinInFFS ( + IN VOID *Fv, + IN VOID *EfiAddr, + IN UINTN Length, + OUT UINTN **Offset + ); + +/** + Read the file to memory. + + @param InputFile The file that contains the FV image. + @param Size The size of the file. + + @retval The pointer to the begining position of memory. +**/ +VOID * +ReadFileToMemory ( + IN CHAR8 *FileName, + OUT UINT32 *Size + ); + +/** + Search the EFI variables address in Fd. + + Open and read the *.fd to the memory, initialize the global structure. + Update the EFI variables addr and the begining position of memory. + + @retval EFI_SUCCESS Get the address successfully. +**/ +EFI_STATUS +GetEfiVariablesAddr ( + BOOLEAN UqiIsSet + ); + +/** + Pick up the FFS which includes IFR section. + + Parse all FFS extracted by BfmLib, and save all which includes IFR + Binary to gEfiFdInfo structure. + + @retval EFI_SUCCESS Get the address successfully. + @retval EFI_BUFFER_TOO_SMALL Memory can't be allocated. + @retval EFI_ABORTED Read FFS Failed. +**/ +EFI_STATUS +FindFileInFolder ( + IN CHAR8 *FolderName, + OUT BOOLEAN *ExistStorageInBfv, + OUT BOOLEAN *SizeOptimized +); + +#endif + diff --git a/BaseTools/Source/C/FCE/Common.c b/BaseTools/Source/C/FCE/Common.c new file mode 100644 index 0000000000..9b14a24a9b --- /dev/null +++ b/BaseTools/Source/C/FCE/Common.c @@ -0,0 +1,2183 @@ +/** @file + + Common library. + + Copyright (c) 2011-2019, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#include "Common.h" + +#define WARNING_STATUS_NUMBER 4 +#define ERROR_STATUS_NUMBER 24 + +CONST CHAR8 mHexStr[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + +CONST CHAR8 *mStatusString[] = { + "Success", // RETURN_SUCCESS = 0 + "Warning Unknown Glyph", // RETURN_WARN_UNKNOWN_GLYPH = 1 + "Warning Delete Failure", // RETURN_WARN_DELETE_FAILURE = 2 + "Warning Write Failure", // RETURN_WARN_WRITE_FAILURE = 3 + "Warning Buffer Too Small", // RETURN_WARN_BUFFER_TOO_SMALL = 4 + "Load Error", // RETURN_LOAD_ERROR = 1 | MAX_BIT + "Invalid Parameter", // RETURN_INVALID_PARAMETER = 2 | MAX_BIT + "Unsupported", // RETURN_UNSUPPORTED = 3 | MAX_BIT + "Bad Buffer Size", // RETURN_BAD_BUFFER_SIZE = 4 | MAX_BIT + "Buffer Too Small", // RETURN_BUFFER_TOO_SMALL, = 5 | MAX_BIT + "Not Ready", // RETURN_NOT_READY = 6 | MAX_BIT + "Device Error", // RETURN_DEVICE_ERROR = 7 | MAX_BIT + "Write Protected", // RETURN_WRITE_PROTECTED = 8 | MAX_BIT + "Out of Resources", // RETURN_OUT_OF_RESOURCES = 9 | MAX_BIT + "Volume Corrupt", // RETURN_VOLUME_CORRUPTED = 10 | MAX_BIT + "Volume Full", // RETURN_VOLUME_FULL = 11 | MAX_BIT + "No Media", // RETURN_NO_MEDIA = 12 | MAX_BIT + "Media changed", // RETURN_MEDIA_CHANGED = 13 | MAX_BIT + "Not Found", // RETURN_NOT_FOUND = 14 | MAX_BIT + "Access Denied", // RETURN_ACCESS_DENIED = 15 | MAX_BIT + "No Response", // RETURN_NO_RESPONSE = 16 | MAX_BIT + "No mapping", // RETURN_NO_MAPPING = 17 | MAX_BIT + "Time out", // RETURN_TIMEOUT = 18 | MAX_BIT + "Not started", // RETURN_NOT_STARTED = 19 | MAX_BIT + "Already started", // RETURN_ALREADY_STARTED = 20 | MAX_BIT + "Aborted", // RETURN_ABORTED = 21 | MAX_BIT + "ICMP Error", // RETURN_ICMP_ERROR = 22 | MAX_BIT + "TFTP Error", // RETURN_TFTP_ERROR = 23 | MAX_BIT + "Protocol Error" // RETURN_PROTOCOL_ERROR = 24 | MAX_BIT +}; + +/** + Copies one Null-terminated Unicode string to another Null-terminated Unicode + string and returns the new Unicode string. + + This function copies the contents of the Unicode string Source to the Unicode + string Destination, and returns Destination. If Source and Destination + overlap, then the results are undefined. + + If Destination is NULL, then return NULL. + If Destination is not aligned on a 16-bit boundary, then return NULL. + + @param Destination A pointer to a Null-terminated Unicode string. + @param Source A pointer to a Null-terminated Unicode string. + + @return Destination. + +**/ +CHAR16 * +StrCpy ( + OUT CHAR16 *Destination, + IN CONST CHAR16 *Source + ) +{ + CHAR16 *ReturnValue; + + ReturnValue = NULL; + + if ((Destination == NULL) || ((UINTN) Destination % 2 != 0)) { + return NULL; + } + + ReturnValue = Destination; + while (*Source != 0) { + *(Destination++) = *(Source++); + } + *Destination = 0; + return ReturnValue; +} + +/** + Returns the length of a Null-terminated Unicode string. + + This function returns the number of Unicode characters in the Null-terminated + Unicode string specified by String. + + If String is NULL, then return 0. + + @param String A pointer to a Null-terminated Unicode string. + + @return The length of String. + +**/ +UINTN +FceStrLen ( + IN CONST CHAR16 *String + ) +{ + UINTN Length; + + if (String == NULL) { + return 0; + } + for (Length = 0; *String != L'\0'; String++, Length++) { + ; + } + return Length; +} + +/** + Returns the size of a Null-terminated Unicode string in bytes, including the + Null terminator. + + This function returns the size, in bytes, of the Null-terminated Unicode string + specified by String. + + If String is NULL, then ASSERT(). + If String is not aligned on a 16-bit boundary, then ASSERT(). + If PcdMaximumUnicodeStringLength is not zero, and String contains more than + PcdMaximumUnicodeStringLength Unicode characters, not including the + Null-terminator, then ASSERT(). + + @param String A pointer to a Null-terminated Unicode string. + + @return The size of String. + +**/ +UINTN +FceStrSize ( + IN CONST CHAR16 *String + ) +{ + return (FceStrLen (String) + 1) * sizeof (*String); +} + +/** + Compares two Null-terminated Unicode strings, and returns the difference + between the first mismatched Unicode characters. + + This function compares the Null-terminated Unicode string FirstString to the + Null-terminated Unicode string SecondString. If FirstString is identical to + SecondString, then 0 is returned. Otherwise, the value returned is the first + mismatched Unicode character in SecondString subtracted from the first + mismatched Unicode character in FirstString. + + @param FirstString A pointer to a Null-terminated Unicode string. + @param SecondString A pointer to a Null-terminated Unicode string. + + @retval 0 FirstString is identical to SecondString. + @return others FirstString is not identical to SecondString. + +**/ +INTN +FceStrCmp ( + IN CONST CHAR16 *FirstString, + IN CONST CHAR16 *SecondString + ) +{ + while ((*FirstString != L'\0') && (*FirstString == *SecondString)) { + FirstString++; + SecondString++; + } + return *FirstString - *SecondString; +} + +/** + Concatenates one Null-terminated Unicode string to another Null-terminated + Unicode string, and returns the concatenated Unicode string. + + This function concatenates two Null-terminated Unicode strings. The contents + of Null-terminated Unicode string Source are concatenated to the end of + Null-terminated Unicode string Destination. The Null-terminated concatenated + Unicode String is returned. If Source and Destination overlap, then the + results are undefined. + + If Destination is NULL, then ASSERT(). + If Destination is not aligned on a 16-bit boundary, then ASSERT(). + If Source is NULL, then ASSERT(). + If Source is not aligned on a 16-bit boundary, then ASSERT(). + If Source and Destination overlap, then ASSERT(). + If PcdMaximumUnicodeStringLength is not zero, and Destination contains more + than PcdMaximumUnicodeStringLength Unicode characters, not including the + Null-terminator, then ASSERT(). + If PcdMaximumUnicodeStringLength is not zero, and Source contains more than + PcdMaximumUnicodeStringLength Unicode characters, not including the + Null-terminator, then ASSERT(). + If PcdMaximumUnicodeStringLength is not zero, and concatenating Destination + and Source results in a Unicode string with more than + PcdMaximumUnicodeStringLength Unicode characters, not including the + Null-terminator, then ASSERT(). + + @param Destination A pointer to a Null-terminated Unicode string. + @param Source A pointer to a Null-terminated Unicode string. + + @return Destination. + +**/ +CHAR16 * +StrCat ( + IN OUT CHAR16 *Destination, + IN CONST CHAR16 *Source + ) +{ + StrCpy (Destination + FceStrLen (Destination), Source); + + // + // Size of the resulting string should never be zero. + // PcdMaximumUnicodeStringLength is tested inside FceStrLen(). + // + ASSERT (FceStrSize (Destination) != 0); + return Destination; +} + +/** + Returns the first occurrence of a Null-terminated Unicode sub-string + in a Null-terminated Unicode string. + + This function scans the contents of the Null-terminated Unicode string + specified by String and returns the first occurrence of SearchString. + If SearchString is not found in String, then NULL is returned. If + the length of SearchString is zero, then String is + returned. + + If String is NULL, then ASSERT(). + If String is not aligned on a 16-bit boundary, then ASSERT(). + If SearchString is NULL, then ASSERT(). + If SearchString is not aligned on a 16-bit boundary, then ASSERT(). + + If PcdMaximumUnicodeStringLength is not zero, and SearchString + or String contains more than PcdMaximumUnicodeStringLength Unicode + characters, not including the Null-terminator, then ASSERT(). + + @param String A pointer to a Null-terminated Unicode string. + @param SearchString A pointer to a Null-terminated Unicode string to search for. + + @retval NULL If the SearchString does not appear in String. + @return others If there is a match. + +**/ +CHAR16 * +StrStr ( + IN CONST CHAR16 *String, + IN CONST CHAR16 *SearchString + ) +{ + CONST CHAR16 *FirstMatch; + CONST CHAR16 *SearchStringTmp; + + // + // ASSERT both strings are less long than PcdMaximumUnicodeStringLength. + // Length tests are performed inside FceStrLen(). + // + ASSERT (FceStrSize (String) != 0); + ASSERT (FceStrSize (SearchString) != 0); + + if (*SearchString == L'\0') { + return (CHAR16 *) String; + } + + while (*String != L'\0') { + SearchStringTmp = SearchString; + FirstMatch = String; + + while ((*String == *SearchStringTmp) + && (*String != L'\0')) { + String++; + SearchStringTmp++; + } + + if (*SearchStringTmp == L'\0') { + return (CHAR16 *) FirstMatch; + } + + if (*String == L'\0') { + return NULL; + } + + String = FirstMatch + 1; + } + + return NULL; +} + +/** + Convert one Null-terminated ASCII string to a Null-terminated + Unicode string and returns the Unicode string. + + This function converts the contents of the ASCII string Source to the Unicode + string Destination, and returns Destination. The function terminates the + Unicode string Destination by appending a Null-terminator character at the end. + The caller is responsible to make sure Destination points to a buffer with size + equal or greater than ((AsciiStrLen (Source) + 1) * sizeof (CHAR16)) in bytes. + + @param Source A pointer to a Null-terminated ASCII string. + @param Destination A pointer to a Null-terminated Unicode string. + + @return Destination. + @return NULL If Destination or Source is NULL, return NULL. + +**/ +CHAR16 * +AsciiStrToUnicodeStr ( + IN CONST CHAR8 *Source, + OUT CHAR16 *Destination + ) +{ + CHAR16 *ReturnValue; + + ReturnValue = NULL; + + if ((Destination == NULL) || (Source == NULL) || (strlen (Source) == 0)) { + return NULL; + } + ReturnValue = Destination; + while (*Source != '\0') { + *(Destination++) = (CHAR16) *(Source++); + } + // + // End the Destination with a NULL. + // + *Destination = '\0'; + + return ReturnValue; +} + +/** + Internal function that convert a number to a string in Buffer. + + Print worker function that converts a decimal or hexadecimal number to an ASCII string in Buffer. + + @param Buffer Location to place the ASCII string of Value. + @param Value The value to convert to a Decimal or Hexadecimal string in Buffer. + @param Radix Radix of the value + + @return A pointer to the end of buffer filled with ASCII string. + +**/ +CHAR8 * +BasePrintLibValueToString ( + IN OUT CHAR8 *Buffer, + IN INT64 Value, + IN UINTN Radix + ) +{ + UINT32 Remainder; + + // + // Loop to convert one digit at a time in reverse order + // + *Buffer = 0; + do { + Value = (INT64)DivU64x32Remainder ((UINT64)Value, (UINT32)Radix, &Remainder); + *(++Buffer) = mHexStr[Remainder]; + } while (Value != 0); + + // + // Return pointer of the end of filled buffer. + // + return Buffer; +} + +/** + Reads a 16-bit value from memory that may be unaligned. + + This function returns the 16-bit value pointed to by Buffer. The function + guarantees that the read operation does not produce an alignment fault. + + If the Buffer is NULL, then ASSERT(). + + @param Buffer A pointer to a 16-bit value that may be unaligned. + + @return The 16-bit value read from Buffer. + +**/ +UINT16 +FceReadUnaligned16 ( + IN CONST UINT16 *Buffer + ) +{ + ASSERT (Buffer != NULL); + + return *Buffer; +} + +/** + Reads a 32-bit value from memory that may be unaligned. + + This function returns the 32-bit value pointed to by Buffer. The function + guarantees that the read operation does not produce an alignment fault. + + If the Buffer is NULL, then ASSERT(). + + @param Buffer A pointer to a 32-bit value that may be unaligned. + + @return The 32-bit value read from Buffer. + +**/ +UINT32 +ReadUnaligned32 ( + IN CONST UINT32 *Buffer + ) +{ + ASSERT (Buffer != NULL); + + return *Buffer; +} + +/** + Internal function that places the character into the Buffer. + + Internal function that places ASCII or Unicode character into the Buffer. + + @param Buffer The buffer to place the Unicode or ASCII string. + @param EndBuffer The end of the input Buffer. No characters will be + placed after that. + @param Length The count of character to be placed into Buffer. + (Negative value indicates no buffer fill.) + @param Character The character to be placed into Buffer. + @param Increment The character increment in Buffer. + + @return Buffer. + +**/ +CHAR8 * +BasePrintLibFillBuffer ( + OUT CHAR8 *Buffer, + IN CHAR8 *EndBuffer, + IN INTN Length, + IN UINTN Character, + IN INTN Increment + ) +{ + INTN Index; + + for (Index = 0; Index < Length && Buffer < EndBuffer; Index++) { + *Buffer = (CHAR8) Character; + if (Increment != 1) { + *(Buffer + 1) = (CHAR8)(Character >> 8); + } + Buffer += Increment; + } + + return Buffer; +} + +/** + Worker function that produces a Null-terminated string in an output buffer + based on a Null-terminated format string and a VA_LIST argument list. + + VSPrint function to process format and place the results in Buffer. Since a + VA_LIST is used this routine allows the nesting of Vararg routines. Thus + this is the main print working routine. + + If COUNT_ONLY_NO_PRINT is set in Flags, Buffer will not be modified at all. + + @param[out] Buffer The character buffer to print the results of the + parsing of Format into. + @param[in] BufferSize The maximum number of characters to put into + buffer. + @param[in] Flags Initial flags value. + Can only have FORMAT_UNICODE, OUTPUT_UNICODE, + and COUNT_ONLY_NO_PRINT set. + @param[in] Format A Null-terminated format string. + @param[in] VaListMarker VA_LIST style variable argument list consumed by + processing Format. + @param[in] BaseListMarker BASE_LIST style variable argument list consumed + by processing Format. + + @return The number of characters printed not including the Null-terminator. + If COUNT_ONLY_NO_PRINT was set returns the same, but without any + modification to Buffer. + +**/ +UINTN +BasePrintLibSPrintMarker ( + OUT CHAR8 *Buffer, + IN UINTN BufferSize, + IN UINTN Flags, + IN CONST CHAR8 *Format, + IN VA_LIST VaListMarker, OPTIONAL + IN BASE_LIST BaseListMarker OPTIONAL + ) +{ + CHAR8 *OriginalBuffer; + CHAR8 *EndBuffer; + CHAR8 ValueBuffer[MAXIMUM_VALUE_CHARACTERS]; + UINT32 BytesPerOutputCharacter; + UINTN BytesPerFormatCharacter; + UINTN FormatMask; + UINTN FormatCharacter; + UINTN Width; + UINTN Precision; + INT64 Value; + CONST CHAR8 *ArgumentString; + UINTN Character; + EFI_GUID *TmpGuid; + TIME *TmpTime; + UINTN Count; + UINTN ArgumentMask; + INTN BytesPerArgumentCharacter; + UINTN ArgumentCharacter; + BOOLEAN Done; + UINTN Index; + CHAR8 Prefix; + BOOLEAN ZeroPad; + BOOLEAN Comma; + UINTN Digits; + UINTN Radix; + RETURN_STATUS Status; + UINT32 GuidData1; + UINT16 GuidData2; + UINT16 GuidData3; + UINTN LengthToReturn; + + // + // If you change this code be sure to match the 2 versions of this function. + // Nearly identical logic is found in the BasePrintLib and + // DxePrintLibPrint2Protocol (both PrintLib instances). + // + + if ((Flags & COUNT_ONLY_NO_PRINT) != 0) { + if (BufferSize == 0) { + Buffer = NULL; + } + } else { + // + // We can run without a Buffer for counting only. + // + if (BufferSize == 0) { + return 0; + } + ASSERT (Buffer != NULL); + } + + if ((Flags & OUTPUT_UNICODE) != 0) { + BytesPerOutputCharacter = 2; + } else { + BytesPerOutputCharacter = 1; + } + + LengthToReturn = 0; + + // + // Reserve space for the Null terminator. + // + BufferSize--; + OriginalBuffer = Buffer; + + // + // Set the tag for the end of the input Buffer. + // + EndBuffer = Buffer + BufferSize * BytesPerOutputCharacter; + + if ((Flags & FORMAT_UNICODE) != 0) { + // + // Make sure format string cannot contain more than PcdMaximumUnicodeStringLength + // Unicode characters if PcdMaximumUnicodeStringLength is not zero. + // + ASSERT (FceStrSize ((CHAR16 *) Format) != 0); + BytesPerFormatCharacter = 2; + FormatMask = 0xffff; + } else { + // + // Make sure format string cannot contain more than PcdMaximumAsciiStringLength + // Ascii characters if PcdMaximumAsciiStringLength is not zero. + // + ASSERT (strlen (Format) + 1 != 0); + BytesPerFormatCharacter = 1; + FormatMask = 0xff; + } + + // + // Get the first character from the format string + // + FormatCharacter = ((*Format & 0xff) | (*(Format + 1) << 8)) & FormatMask; + + // + // Loop until the end of the format string is reached or the output buffer is full + // + while (FormatCharacter != 0 && Buffer < EndBuffer) { + // + // Clear all the flag bits except those that may have been passed in + // + Flags &= (OUTPUT_UNICODE | FORMAT_UNICODE | COUNT_ONLY_NO_PRINT); + + // + // Set the default width to zero, and the default precision to 1 + // + Width = 0; + Precision = 1; + Prefix = 0; + Comma = FALSE; + ZeroPad = FALSE; + Count = 0; + Digits = 0; + + switch (FormatCharacter) { + case '%': + // + // Parse Flags and Width + // + for (Done = FALSE; !Done; ) { + Format += BytesPerFormatCharacter; + FormatCharacter = ((*Format & 0xff) | (*(Format + 1) << 8)) & FormatMask; + switch (FormatCharacter) { + case '.': + Flags |= PRECISION; + break; + case '-': + Flags |= LEFT_JUSTIFY; + break; + case '+': + Flags |= PREFIX_SIGN; + break; + case ' ': + Flags |= PREFIX_BLANK; + break; + case ',': + Flags |= COMMA_TYPE; + break; + case 'L': + case 'l': + Flags |= LONG_TYPE; + break; + case '*': + if ((Flags & PRECISION) == 0) { + Flags |= PAD_TO_WIDTH; + if (BaseListMarker == NULL) { + Width = VA_ARG (VaListMarker, UINTN); + } else { + Width = BASE_ARG (BaseListMarker, UINTN); + } + } else { + if (BaseListMarker == NULL) { + Precision = VA_ARG (VaListMarker, UINTN); + } else { + Precision = BASE_ARG (BaseListMarker, UINTN); + } + } + break; + case '0': + if ((Flags & PRECISION) == 0) { + Flags |= PREFIX_ZERO; + } + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + for (Count = 0; ((FormatCharacter >= '0') && (FormatCharacter <= '9')); ){ + Count = (Count * 10) + FormatCharacter - '0'; + Format += BytesPerFormatCharacter; + FormatCharacter = ((*Format & 0xff) | (*(Format + 1) << 8)) & FormatMask; + } + Format -= BytesPerFormatCharacter; + if ((Flags & PRECISION) == 0) { + Flags |= PAD_TO_WIDTH; + Width = Count; + } else { + Precision = Count; + } + break; + + case '\0': + // + // Make no output if Format string terminates unexpectedly when + // looking up for flag, width, precision and type. + // + Format -= BytesPerFormatCharacter; + Precision = 0; + // + // break skipped on purpose. + // + default: + Done = TRUE; + break; + } + } + + // + // Handle each argument type + // + switch (FormatCharacter) { + case 'p': + // + // Flag space, +, 0, L & l are invalid for type p. + // + Flags &= ~(PREFIX_BLANK | PREFIX_SIGN | PREFIX_ZERO | LONG_TYPE); + if (sizeof (VOID *) > 4) { + Flags |= LONG_TYPE; + } + case 'X': + Flags |= PREFIX_ZERO; + // + // break skipped on purpose + // + case 'x': + Flags |= RADIX_HEX; + // + // break skipped on purpose + // + case 'd': + if ((Flags & LONG_TYPE) == 0) { + // + // 'd','x', and 'X' that are not preceded by 'l' or 'L' are assumed to be type "int". + // This assumption is made so the format string definition is compatible with the ANSI C + // Specification for formatted strings. It is recommended that the Base Types be used + // everywhere, but in this one case, compliance with ANSI C is more important, and + // provides an implementation that is compatible with that largest possible set of CPU + // architectures. This is why the type "int" is used in this one case. + // + if (BaseListMarker == NULL) { + Value = VA_ARG (VaListMarker, int); + } else { + Value = BASE_ARG (BaseListMarker, int); + } + } else { + if (BaseListMarker == NULL) { + Value = VA_ARG (VaListMarker, INT64); + } else { + Value = BASE_ARG (BaseListMarker, INT64); + } + } + if ((Flags & PREFIX_BLANK) != 0) { + Prefix = ' '; + } + if ((Flags & PREFIX_SIGN) != 0) { + Prefix = '+'; + } + if ((Flags & COMMA_TYPE) != 0) { + Comma = TRUE; + } + if ((Flags & RADIX_HEX) == 0) { + Radix = 10; + if (Comma) { + Flags &= (~PREFIX_ZERO); + Precision = 1; + } + if (Value < 0) { + Flags |= PREFIX_SIGN; + Prefix = '-'; + Value = -Value; + } + } else { + Radix = 16; + Comma = FALSE; + if ((Flags & LONG_TYPE) == 0 && Value < 0) { + // + // 'd','x', and 'X' that are not preceded by 'l' or 'L' are assumed to be type "int". + // This assumption is made so the format string definition is compatible with the ANSI C + // Specification for formatted strings. It is recommended that the Base Types be used + // everywhere, but in this one case, compliance with ANSI C is more important, and + // provides an implementation that is compatible with that largest possible set of CPU + // architectures. This is why the type "unsigned int" is used in this one case. + // + Value = (unsigned int)Value; + } + } + // + // Convert Value to a reversed string + // + Count = BasePrintLibValueToString (ValueBuffer, Value, Radix) - ValueBuffer; + if (Value == 0 && Precision == 0) { + Count = 0; + } + ArgumentString = (CHAR8 *)ValueBuffer + Count; + + Digits = Count % 3; + if (Digits != 0) { + Digits = 3 - Digits; + } + if (Comma && Count != 0) { + Count += ((Count - 1) / 3); + } + if (Prefix != 0) { + Count++; + Precision++; + } + Flags |= ARGUMENT_REVERSED; + ZeroPad = TRUE; + if ((Flags & PREFIX_ZERO) != 0) { + if ((Flags & LEFT_JUSTIFY) == 0) { + if ((Flags & PAD_TO_WIDTH) != 0) { + if ((Flags & PRECISION) == 0) { + Precision = Width; + } + } + } + } + break; + + case 's': + case 'S': + Flags |= ARGUMENT_UNICODE; + // + // break skipped on purpose + // + case 'a': + if (BaseListMarker == NULL) { + ArgumentString = VA_ARG (VaListMarker, CHAR8 *); + } else { + ArgumentString = BASE_ARG (BaseListMarker, CHAR8 *); + } + if (ArgumentString == NULL) { + Flags &= (~ARGUMENT_UNICODE); + ArgumentString = ""; + } + // + // Set the default precision for string to be zero if not specified. + // + if ((Flags & PRECISION) == 0) { + Precision = 0; + } + break; + + case 'c': + if (BaseListMarker == NULL) { + Character = VA_ARG (VaListMarker, UINTN) & 0xffff; + } else { + Character = BASE_ARG (BaseListMarker, UINTN) & 0xffff; + } + ArgumentString = (CHAR8 *)&Character; + Flags |= ARGUMENT_UNICODE; + break; + + case 'g': + if (BaseListMarker == NULL) { + TmpGuid = VA_ARG (VaListMarker, EFI_GUID *); + } else { + TmpGuid = BASE_ARG (BaseListMarker, EFI_GUID *); + } + if (TmpGuid == NULL) { + ArgumentString = ""; + } else { + GuidData1 = ReadUnaligned32 (&(TmpGuid->Data1)); + GuidData2 = FceReadUnaligned16 (&(TmpGuid->Data2)); + GuidData3 = FceReadUnaligned16 (&(TmpGuid->Data3)); + BasePrintLibSPrint ( + ValueBuffer, + MAXIMUM_VALUE_CHARACTERS, + 0, + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + GuidData1, + GuidData2, + GuidData3, + TmpGuid->Data4[0], + TmpGuid->Data4[1], + TmpGuid->Data4[2], + TmpGuid->Data4[3], + TmpGuid->Data4[4], + TmpGuid->Data4[5], + TmpGuid->Data4[6], + TmpGuid->Data4[7] + ); + ArgumentString = ValueBuffer; + } + break; + + case 't': + if (BaseListMarker == NULL) { + TmpTime = VA_ARG (VaListMarker, TIME *); + } else { + TmpTime = BASE_ARG (BaseListMarker, TIME *); + } + if (TmpTime == NULL) { + ArgumentString = ""; + } else { + BasePrintLibSPrint ( + ValueBuffer, + MAXIMUM_VALUE_CHARACTERS, + 0, + "%02d/%02d/%04d %02d:%02d", + TmpTime->Month, + TmpTime->Day, + TmpTime->Year, + TmpTime->Hour, + TmpTime->Minute + ); + ArgumentString = ValueBuffer; + } + break; + + case 'r': + if (BaseListMarker == NULL) { + Status = VA_ARG (VaListMarker, RETURN_STATUS); + } else { + Status = BASE_ARG (BaseListMarker, RETURN_STATUS); + } + ArgumentString = ValueBuffer; + if (RETURN_ERROR (Status)) { + // + // Clear error bit + // + Index = Status & ~MAX_BIT; + if (Index > 0 && Index <= ERROR_STATUS_NUMBER) { + ArgumentString = mStatusString [Index + WARNING_STATUS_NUMBER]; + } + } else { + Index = Status; + if (Index <= WARNING_STATUS_NUMBER) { + ArgumentString = mStatusString [Index]; + } + } + if (ArgumentString == ValueBuffer) { + BasePrintLibSPrint ((CHAR8 *) ValueBuffer, MAXIMUM_VALUE_CHARACTERS, 0, "%08X", Status); + } + break; + + case '\r': + Format += BytesPerFormatCharacter; + FormatCharacter = ((*Format & 0xff) | (*(Format + 1) << 8)) & FormatMask; + if (FormatCharacter == '\n') { + // + // Translate '\r\n' to '\r\n' + // + ArgumentString = "\r\n"; + } else { + // + // Translate '\r' to '\r' + // + ArgumentString = "\r"; + Format -= BytesPerFormatCharacter; + } + break; + + case '\n': + // + // Translate '\n' to '\r\n' and '\n\r' to '\r\n' + // + ArgumentString = "\r\n"; + Format += BytesPerFormatCharacter; + FormatCharacter = ((*Format & 0xff) | (*(Format + 1) << 8)) & FormatMask; + if (FormatCharacter != '\r') { + Format -= BytesPerFormatCharacter; + } + break; + + case '%': + default: + // + // if the type is '%' or unknown, then print it to the screen + // + ArgumentString = (CHAR8 *)&FormatCharacter; + Flags |= ARGUMENT_UNICODE; + break; + } + break; + + case '\r': + Format += BytesPerFormatCharacter; + FormatCharacter = ((*Format & 0xff) | (*(Format + 1) << 8)) & FormatMask; + if (FormatCharacter == '\n') { + // + // Translate '\r\n' to '\r\n' + // + ArgumentString = "\r\n"; + } else { + // + // Translate '\r' to '\r' + // + ArgumentString = "\r"; + Format -= BytesPerFormatCharacter; + } + break; + + case '\n': + // + // Translate '\n' to '\r\n' and '\n\r' to '\r\n' + // + ArgumentString = "\r\n"; + Format += BytesPerFormatCharacter; + FormatCharacter = ((*Format & 0xff) | (*(Format + 1) << 8)) & FormatMask; + if (FormatCharacter != '\r') { + Format -= BytesPerFormatCharacter; + } + break; + + default: + ArgumentString = (CHAR8 *)&FormatCharacter; + Flags |= ARGUMENT_UNICODE; + break; + } + + // + // Retrieve the ArgumentString attriubutes + // + if ((Flags & ARGUMENT_UNICODE) != 0) { + ArgumentMask = 0xffff; + BytesPerArgumentCharacter = 2; + } else { + ArgumentMask = 0xff; + BytesPerArgumentCharacter = 1; + } + if ((Flags & ARGUMENT_REVERSED) != 0) { + BytesPerArgumentCharacter = -BytesPerArgumentCharacter; + } else { + // + // Compute the number of characters in ArgumentString and store it in Count + // ArgumentString is either null-terminated, or it contains Precision characters + // + for (Count = 0; Count < Precision || ((Flags & PRECISION) == 0); Count++) { + ArgumentCharacter = ((ArgumentString[Count * BytesPerArgumentCharacter] & 0xff) | ((ArgumentString[Count * BytesPerArgumentCharacter + 1]) << 8)) & ArgumentMask; + if (ArgumentCharacter == 0) { + break; + } + } + } + + if (Precision < Count) { + Precision = Count; + } + + // + // Pad before the string + // + if ((Flags & (PAD_TO_WIDTH | LEFT_JUSTIFY)) == (PAD_TO_WIDTH)) { + LengthToReturn += ((Width - Precision) * BytesPerOutputCharacter); + if ((Flags & COUNT_ONLY_NO_PRINT) == 0 && Buffer != NULL) { + Buffer = BasePrintLibFillBuffer (Buffer, EndBuffer, Width - Precision, ' ', BytesPerOutputCharacter); + } + } + + if (ZeroPad) { + if (Prefix != 0) { + LengthToReturn += (1 * BytesPerOutputCharacter); + if ((Flags & COUNT_ONLY_NO_PRINT) == 0 && Buffer != NULL) { + Buffer = BasePrintLibFillBuffer (Buffer, EndBuffer, 1, Prefix, BytesPerOutputCharacter); + } + } + LengthToReturn += ((Precision - Count) * BytesPerOutputCharacter); + if ((Flags & COUNT_ONLY_NO_PRINT) == 0 && Buffer != NULL) { + Buffer = BasePrintLibFillBuffer (Buffer, EndBuffer, Precision - Count, '0', BytesPerOutputCharacter); + } + } else { + LengthToReturn += ((Precision - Count) * BytesPerOutputCharacter); + if ((Flags & COUNT_ONLY_NO_PRINT) == 0 && Buffer != NULL) { + Buffer = BasePrintLibFillBuffer (Buffer, EndBuffer, Precision - Count, ' ', BytesPerOutputCharacter); + } + if (Prefix != 0) { + LengthToReturn += (1 * BytesPerOutputCharacter); + if ((Flags & COUNT_ONLY_NO_PRINT) == 0 && Buffer != NULL) { + Buffer = BasePrintLibFillBuffer (Buffer, EndBuffer, 1, Prefix, BytesPerOutputCharacter); + } + } + } + + // + // Output the Prefix character if it is present + // + Index = 0; + if (Prefix != 0) { + Index++; + } + + // + // Copy the string into the output buffer performing the required type conversions + // + while (Index < Count) { + ArgumentCharacter = ((*ArgumentString & 0xff) | (*(ArgumentString + 1) << 8)) & ArgumentMask; + + LengthToReturn += (1 * BytesPerOutputCharacter); + if ((Flags & COUNT_ONLY_NO_PRINT) == 0 && Buffer != NULL) { + Buffer = BasePrintLibFillBuffer (Buffer, EndBuffer, 1, ArgumentCharacter, BytesPerOutputCharacter); + } + ArgumentString += BytesPerArgumentCharacter; + Index++; + if (Comma) { + Digits++; + if (Digits == 3) { + Digits = 0; + Index++; + if (Index < Count) { + LengthToReturn += (1 * BytesPerOutputCharacter); + if ((Flags & COUNT_ONLY_NO_PRINT) == 0 && Buffer != NULL) { + Buffer = BasePrintLibFillBuffer (Buffer, EndBuffer, 1, ',', BytesPerOutputCharacter); + } + } + } + } + } + + // + // Pad after the string + // + if ((Flags & (PAD_TO_WIDTH | LEFT_JUSTIFY)) == (PAD_TO_WIDTH | LEFT_JUSTIFY)) { + LengthToReturn += ((Width - Precision) * BytesPerOutputCharacter); + if ((Flags & COUNT_ONLY_NO_PRINT) == 0 && Buffer != NULL) { + Buffer = BasePrintLibFillBuffer (Buffer, EndBuffer, Width - Precision, ' ', BytesPerOutputCharacter); + } + } + + // + // Get the next character from the format string + // + Format += BytesPerFormatCharacter; + + // + // Get the next character from the format string + // + FormatCharacter = ((*Format & 0xff) | (*(Format + 1) << 8)) & FormatMask; + } + + if ((Flags & COUNT_ONLY_NO_PRINT) != 0) { + return (LengthToReturn / BytesPerOutputCharacter); + } + + ASSERT (Buffer != NULL); + // + // Null terminate the Unicode or ASCII string + // + BasePrintLibFillBuffer (Buffer, EndBuffer + BytesPerOutputCharacter, 1, 0, BytesPerOutputCharacter); + // + // Make sure output buffer cannot contain more than PcdMaximumUnicodeStringLength + // Unicode characters if PcdMaximumUnicodeStringLength is not zero. + // + ASSERT ((((Flags & OUTPUT_UNICODE) == 0)) || (FceStrSize ((CHAR16 *) OriginalBuffer) != 0)); + // + // Make sure output buffer cannot contain more than PcdMaximumAsciiStringLength + // ASCII characters if PcdMaximumAsciiStringLength is not zero. + // + ASSERT ((((Flags & OUTPUT_UNICODE) != 0)) || ((strlen (OriginalBuffer) + 1) != 0)); + + return ((Buffer - OriginalBuffer) / BytesPerOutputCharacter); +} + +/** + Worker function that produces a Null-terminated string in an output buffer + based on a Null-terminated format string and variable argument list. + + VSPrint function to process format and place the results in Buffer. Since a + VA_LIST is used this routine allows the nesting of Vararg routines. Thus + this is the main print working routine + + @param StartOfBuffer The character buffer to print the results of the parsing + of Format into. + @param BufferSize The maximum number of characters to put into buffer. + Zero means no limit. + @param Flags Initial flags value. + Can only have FORMAT_UNICODE and OUTPUT_UNICODE set + @param FormatString A Null-terminated format string. + @param ... The variable argument list. + + @return The number of characters printed. + +**/ +UINTN +BasePrintLibSPrint ( + OUT CHAR8 *StartOfBuffer, + IN UINTN BufferSize, + IN UINTN Flags, + IN CONST CHAR8 *FormatString, + ... + ) +{ + VA_LIST Marker; + + VA_START (Marker, FormatString); + return BasePrintLibSPrintMarker (StartOfBuffer, BufferSize, Flags, FormatString, Marker, NULL); +} + +/** + Produces a Null-terminated Unicode string in an output buffer based on + a Null-terminated Unicode format string and a VA_LIST argument list + + Produces a Null-terminated Unicode string in the output buffer specified by StartOfBuffer + and BufferSize. + The Unicode string is produced by parsing the format string specified by FormatString. + Arguments are pulled from the variable argument list specified by Marker based on the + contents of the format string. + The number of Unicode characters in the produced output buffer is returned not including + the Null-terminator. + If BufferSize is 0 or 1, then no output buffer is produced and 0 is returned. + + If BufferSize > 1 and StartOfBuffer is NULL, then ASSERT(). + If BufferSize > 1 and StartOfBuffer is not aligned on a 16-bit boundary, then ASSERT(). + If BufferSize > 1 and FormatString is NULL, then ASSERT(). + If BufferSize > 1 and FormatString is not aligned on a 16-bit boundary, then ASSERT(). + If PcdMaximumUnicodeStringLength is not zero, and FormatString contains more than + PcdMaximumUnicodeStringLength Unicode characters not including the Null-terminator, then + ASSERT(). + If PcdMaximumUnicodeStringLength is not zero, and produced Null-terminated Unicode string + contains more than PcdMaximumUnicodeStringLength Unicode characters not including the + Null-terminator, then ASSERT(). + + @param StartOfBuffer A pointer to the output buffer for the produced Null-terminated + Unicode string. + @param BufferSize The size, in bytes, of the output buffer specified by StartOfBuffer. + @param FormatString A Null-terminated Unicode format string. + @param Marker VA_LIST marker for the variable argument list. + + @return The number of Unicode characters in the produced output buffer not including the + Null-terminator. + +**/ +UINTN +UnicodeVSPrint ( + OUT CHAR16 *StartOfBuffer, + IN UINTN BufferSize, + IN CONST CHAR16 *FormatString, + IN VA_LIST Marker + ) +{ + ASSERT_UNICODE_BUFFER (StartOfBuffer); + ASSERT_UNICODE_BUFFER (FormatString); + return BasePrintLibSPrintMarker ((CHAR8 *)StartOfBuffer, BufferSize >> 1, FORMAT_UNICODE | OUTPUT_UNICODE, (CHAR8 *)FormatString, Marker, NULL); +} + +/** + Produces a Null-terminated Unicode string in an output buffer based on a Null-terminated + Unicode format string and variable argument list. + + Produces a Null-terminated Unicode string in the output buffer specified by StartOfBuffer + and BufferSize. + The Unicode string is produced by parsing the format string specified by FormatString. + Arguments are pulled from the variable argument list based on the contents of the format string. + The number of Unicode characters in the produced output buffer is returned not including + the Null-terminator. + If BufferSize is 0 or 1, then no output buffer is produced and 0 is returned. + + If BufferSize > 1 and StartOfBuffer is NULL, then ASSERT(). + If BufferSize > 1 and StartOfBuffer is not aligned on a 16-bit boundary, then ASSERT(). + If BufferSize > 1 and FormatString is NULL, then ASSERT(). + If BufferSize > 1 and FormatString is not aligned on a 16-bit boundary, then ASSERT(). + If PcdMaximumUnicodeStringLength is not zero, and FormatString contains more than + PcdMaximumUnicodeStringLength Unicode characters not including the Null-terminator, then + ASSERT(). + If PcdMaximumUnicodeStringLength is not zero, and produced Null-terminated Unicode string + contains more than PcdMaximumUnicodeStringLength Unicode characters not including the + Null-terminator, then ASSERT(). + + @param StartOfBuffer A pointer to the output buffer for the produced Null-terminated + Unicode string. + @param BufferSize The size, in bytes, of the output buffer specified by StartOfBuffer. + @param FormatString A Null-terminated Unicode format string. + @param ... Variable argument list whose contents are accessed based on the + format string specified by FormatString. + + @return The number of Unicode characters in the produced output buffer not including the + Null-terminator. + +**/ +UINTN +UnicodeSPrint ( + OUT CHAR16 *StartOfBuffer, + IN UINTN BufferSize, + IN CONST CHAR16 *FormatString, + ... + ) +{ + VA_LIST Marker; + + VA_START (Marker, FormatString); + return UnicodeVSPrint (StartOfBuffer, BufferSize, FormatString, Marker); +} + +/** + Convert a Null-terminated Unicode string to a Null-terminated + ASCII string and returns the ASCII string. + + This function converts the content of the Unicode string Source + to the ASCII string Destination by copying the lower 8 bits of + each Unicode character. It returns Destination. The function terminates + the ASCII string Destination by appending a Null-terminator character + at the end. The caller is responsible to make sure Destination points + to a buffer with size equal or greater than (FceStrLen (Source) + 1) in bytes. + + If Destination is NULL, then ASSERT(). + If Source is NULL, then ASSERT(). + If Source is not aligned on a 16-bit boundary, then ASSERT(). + If Source and Destination overlap, then ASSERT(). + + If any Unicode characters in Source contain non-zero value in + the upper 8 bits, then ASSERT(). + + @param Source Pointer to a Null-terminated Unicode string. + @param Destination Pointer to a Null-terminated ASCII string. + + @reture Destination + +**/ +CHAR8 * +UnicodeStrToAsciiStr ( + IN CONST CHAR16 *Source, + OUT CHAR8 *Destination + ) +{ + CHAR8 *ReturnValue; + + ReturnValue = Destination; + assert (Destination != NULL); + assert (Source != NULL); + assert (((UINTN) Source & 0x01) == 0); + + while (*Source != L'\0') { + // + // If any Unicode characters in Source contain + // non-zero value in the upper 8 bits, then ASSERT(). + // + assert (*Source < 0x100); + *(ReturnValue++) = (CHAR8) *(Source++); + } + + *ReturnValue = '\0'; + + return Destination; +} + +/** + Allocate new memory and then copy the Unicode string Source to Destination. + + @param Dest Location to copy string + @param Src String to copy + +**/ +VOID +NewStringCpy ( + IN OUT CHAR16 **Dest, + IN CHAR16 *Src + ) +{ + if (*Dest != NULL) { + FreePool (*Dest); + } + *Dest = FceAllocateCopyPool (FceStrSize (Src), Src); + ASSERT (*Dest != NULL); +} + +/** + Check if a Unicode character is a decimal character. + + This internal function checks if a Unicode character is a + decimal character. The valid decimal character is from + L'0' to L'9'. + + @param Char The character to check against. + + @retval TRUE If the Char is a decmial character. + @retval FALSE If the Char is not a decmial character. + +**/ +BOOLEAN +FceInternalIsDecimalDigitCharacter ( + IN CHAR16 Char + ) +{ + return (BOOLEAN) ((Char >= L'0') && (Char <= L'9')); +} + +/** + Convert a Unicode character to upper case only if + it maps to a valid small-case ASCII character. + + This internal function only deal with Unicode character + which maps to a valid small-case ASCII character, i.e. + L'a' to L'z'. For other Unicode character, the input character + is returned directly. + + @param Char The character to convert. + + @retval LowerCharacter If the Char is with range L'a' to L'z'. + @retval Unchanged Otherwise. + +**/ +CHAR16 +FceInternalCharToUpper ( + IN CHAR16 Char + ) +{ + if ((Char >= L'a') && (Char <= L'z')) { + return (CHAR16) (Char - (L'a' - L'A')); + } + + return Char; +} + +/** + Convert a Unicode character to numerical value. + + This internal function only deal with Unicode character + which maps to a valid hexadecimal ASII character, i.e. + L'0' to L'9', L'a' to L'f' or L'A' to L'F'. For other + Unicode character, the value returned does not make sense. + + @param Char The character to convert. + + @return The numerical value converted. + +**/ +UINTN +FceInternalHexCharToUintn ( + IN CHAR16 Char + ) +{ + if (FceInternalIsDecimalDigitCharacter (Char)) { + return Char - L'0'; + } + + return (UINTN) (10 + FceInternalCharToUpper (Char) - L'A'); +} + +/** + Check if a Unicode character is a hexadecimal character. + + This internal function checks if a Unicode character is a + decimal character. The valid hexadecimal character is + L'0' to L'9', L'a' to L'f', or L'A' to L'F'. + + + @param Char The character to check against. + + @retval TRUE If the Char is a hexadecmial character. + @retval FALSE If the Char is not a hexadecmial character. + +**/ +BOOLEAN +FceInternalIsHexaDecimalDigitCharacter ( + IN CHAR16 Char + ) +{ + + return (BOOLEAN) (FceInternalIsDecimalDigitCharacter (Char) || + ((Char >= L'A') && (Char <= L'F')) || + ((Char >= L'a') && (Char <= L'f'))); +} + + +/** + Convert a Null-terminated Unicode decimal string to a value of + type UINT64. + + This function returns a value of type UINT64 by interpreting the contents + of the Unicode string specified by String as a decimal number. The format + of the input Unicode string String is: + + [spaces] [decimal digits]. + + The valid decimal digit character is in the range [0-9]. The + function will ignore the pad space, which includes spaces or + tab characters, before [decimal digits]. The running zero in the + beginning of [decimal digits] will be ignored. Then, the function + stops at the first character that is a not a valid decimal character + or a Null-terminator, whichever one comes first. + + If String is NULL, then ASSERT(). + If String is not aligned in a 16-bit boundary, then ASSERT(). + If String has only pad spaces, then 0 is returned. + If String has no pad spaces or valid decimal digits, + then 0 is returned. + If the number represented by String overflows according + to the range defined by UINT64, then ASSERT(). + + If PcdMaximumUnicodeStringLength is not zero, and String contains + more than PcdMaximumUnicodeStringLength Unicode characters, not including + the Null-terminator, then ASSERT(). + + @param String A pointer to a Null-terminated Unicode string. + + @retval Value translated from String. + +**/ +UINT64 +FceStrDecimalToUint64 ( + IN CONST CHAR16 *String + ) +{ + UINT64 Result; + + // + // ASSERT String is less long than PcdMaximumUnicodeStringLength. + // Length tests are performed inside FceStrLen(). + // + ASSERT (FceStrSize (String) != 0); + + // + // Ignore the pad spaces (space or tab) + // + while ((*String == L' ') || (*String == L'\t')) { + String++; + } + + // + // Ignore leading Zeros after the spaces + // + while (*String == L'0') { + String++; + } + + Result = 0; + + while (FceInternalIsDecimalDigitCharacter (*String)) { + // + // If the number represented by String overflows according + // to the range defined by UINTN, then ASSERT(). + // + ASSERT (Result <= DivU64x32 (((UINT64) ~0) - (*String - L'0') , 10)); + + Result = MultU64x32 (Result, 10) + (*String - L'0'); + String++; + } + + return Result; +} + + +/** + Convert a Null-terminated Unicode hexadecimal string to a value of type UINT64. + + This function returns a value of type UINT64 by interpreting the contents + of the Unicode string specified by String as a hexadecimal number. + The format of the input Unicode string String is + + [spaces][zeros][x][hexadecimal digits]. + + The valid hexadecimal digit character is in the range [0-9], [a-f] and [A-F]. + The prefix "0x" is optional. Both "x" and "X" is allowed in "0x" prefix. + If "x" appears in the input string, it must be prefixed with at least one 0. + The function will ignore the pad space, which includes spaces or tab characters, + before [zeros], [x] or [hexadecimal digit]. The running zero before [x] or + [hexadecimal digit] will be ignored. Then, the decoding starts after [x] or the + first valid hexadecimal digit. Then, the function stops at the first character that is + a not a valid hexadecimal character or NULL, whichever one comes first. + + If String is NULL, then ASSERT(). + If String is not aligned in a 16-bit boundary, then ASSERT(). + If String has only pad spaces, then zero is returned. + If String has no leading pad spaces, leading zeros or valid hexadecimal digits, + then zero is returned. + If the number represented by String overflows according to the range defined by + UINT64, then ASSERT(). + + If PcdMaximumUnicodeStringLength is not zero, and String contains more than + PcdMaximumUnicodeStringLength Unicode characters, not including the Null-terminator, + then ASSERT(). + + @param String A pointer to a Null-terminated Unicode string. + + @retval Value translated from String. + +**/ +UINT64 +FceStrHexToUint64 ( + IN CONST CHAR16 *String + ) +{ + UINT64 Result; + + // + // ASSERT String is less long than PcdMaximumUnicodeStringLength. + // Length tests are performed inside FceStrLen(). + // + ASSERT (FceStrSize (String) != 0); + + // + // Ignore the pad spaces (space or tab) + // + while ((*String == L' ') || (*String == L'\t')) { + String++; + } + + // + // Ignore leading Zeros after the spaces + // + while (*String == L'0') { + String++; + } + + if (FceInternalCharToUpper (*String) == L'X') { + ASSERT (*(String - 1) == L'0'); + if (*(String - 1) != L'0') { + return 0; + } + // + // Skip the 'X' + // + String++; + } + + Result = 0; + + while (FceInternalIsHexaDecimalDigitCharacter (*String)) { + // + // If the Hex Number represented by String overflows according + // to the range defined by UINTN, then ASSERT(). + // + ASSERT (Result <= RShiftU64 (((UINT64) ~0) - FceInternalHexCharToUintn (*String) , 4)); + + Result = LShiftU64 (Result, 4); + Result = Result + FceInternalHexCharToUintn (*String); + String++; + } + + return Result; +} + + +CHAR16 +ToUpper ( + CHAR16 a + ) +{ + if (('a' <= a) && (a <= 'z')) { + return (CHAR16) (a - 0x20); + } else { + return a; + } +} + +CHAR16 +ToLower ( + CHAR16 a + ) +{ + if (('A' <= a) && (a <= 'Z')) { + return (CHAR16) (a + 0x20); + } else { + return a; + } +} + +/** + Performs a case-insensitive comparison between a Null-terminated + Unicode pattern string and a Null-terminated Unicode string. + + @param String - A pointer to a Null-terminated Unicode string. + @param Pattern - A pointer to a Null-terminated Unicode pattern string. + + + @retval TRUE - Pattern was found in String. + @retval FALSE - Pattern was not found in String. + +**/ +BOOLEAN +MetaiMatch ( + IN CHAR16 *String, + IN CHAR16 *Pattern + ) +{ + CHAR16 c; + CHAR16 p; + + assert (String != NULL); + assert (Pattern != NULL); + + for (;;) { + p = *Pattern; + Pattern += 1; + + if (p == 0) { + // + // End of pattern. If end of string, TRUE match + // + if (*String) { + return FALSE; + } else { + return TRUE; + } + + } else { + + c = *String; + if (ToUpper (c) != ToUpper (p)) { + return FALSE; + } + + String += 1; + + } + + } + +} +/** + Multiplies a 64-bit unsigned integer by a 32-bit unsigned integer and + generates a 64-bit unsigned result. + + This function multiplies the 64-bit unsigned value Multiplicand by the 32-bit + unsigned value Multiplier and generates a 64-bit unsigned result. This 64- + bit unsigned result is returned. + + @param Multiplicand A 64-bit unsigned value. + @param Multiplier A 32-bit unsigned value. + + @return Multiplicand * Multiplier. + +**/ +UINT64 +MultU64x32 ( + IN UINT64 Multiplicand, + IN UINT32 Multiplier + ) +{ + return Multiplicand * Multiplier; +} + +/** + Divides a 64-bit unsigned integer by a 32-bit unsigned integer and generates + a 64-bit unsigned result. + + This function divides the 64-bit unsigned value Dividend by the 32-bit + unsigned value Divisor and generates a 64-bit unsigned quotient. This + function returns the 64-bit unsigned quotient. + + If Divisor is 0, then ASSERT(). + + @param Dividend A 64-bit unsigned value. + @param Divisor A 32-bit unsigned value. + + @return Dividend / Divisor + +**/ +UINT64 +DivU64x32 ( + IN UINT64 Dividend, + IN UINT32 Divisor + ) +{ + ASSERT (Divisor != 0); + return Dividend / Divisor; +} + +/** + Shifts a 64-bit integer left between 0 and 63 bits. The low bits are filled + with zeros. The shifted value is returned. + + This function shifts the 64-bit value Operand to the left by Count bits. The + low Count bits are set to zero. The shifted value is returned. + + If Count is greater than 63, then ASSERT(). + + @param Operand The 64-bit operand to shift left. + @param Count The number of bits to shift left. + + @return Operand << Count. + +**/ +UINT64 +LShiftU64 ( + IN UINT64 Operand, + IN UINTN Count + ) +{ + ASSERT (Count < 64); + return Operand << Count; +} + +/** + Shifts a 64-bit integer right between 0 and 63 bits. This high bits are + filled with zeros. The shifted value is returned. + + This function shifts the 64-bit value Operand to the right by Count bits. The + high Count bits are set to zero. The shifted value is returned. + + If Count is greater than 63, then ASSERT(). + + @param Operand The 64-bit operand to shift right. + @param Count The number of bits to shift right. + + @return Operand >> Count. + +**/ +UINT64 +RShiftU64 ( + IN UINT64 Operand, + IN UINTN Count + ) + +{ + ASSERT (Count < 64); + return Operand >> Count; +} + + +/** + Divides a 64-bit unsigned integer by a 32-bit unsigned integer and generates + a 64-bit unsigned result and an optional 32-bit unsigned remainder. + + This function divides the 64-bit unsigned value Dividend by the 32-bit + unsigned value Divisor and generates a 64-bit unsigned quotient. If Remainder + is not NULL, then the 32-bit unsigned remainder is returned in Remainder. + This function returns the 64-bit unsigned quotient. + + If Divisor is 0, then ASSERT(). + + @param Dividend A 64-bit unsigned value. + @param Divisor A 32-bit unsigned value. + @param Remainder A pointer to a 32-bit unsigned value. This parameter is + optional and may be NULL. + + @return Dividend / Divisor + +**/ +UINT64 +DivU64x32Remainder ( + IN UINT64 Dividend, + IN UINT32 Divisor, + OUT UINT32 *Remainder + ) +{ + ASSERT (Divisor != 0); + + if (Remainder != NULL) { + *Remainder = (UINT32)(Dividend % Divisor); + } + return Dividend / Divisor; +} + +/** + Copies a buffer to an allocated buffer. + + Allocates the number bytes specified by AllocationSize, copies allocationSize bytes + from Buffer to the newly allocated buffer, and returns a pointer to the allocated + buffer. If AllocationSize is 0, then a valid buffer of 0 size is returned. If there + is not enough memory remaining to satisfy the request, then NULL is returned. + + If Buffer is NULL, then ASSERT(). + + @param AllocationSize The number of bytes to allocate and zero. + @param Buffer The buffer to copy to the allocated buffer. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +FceAllocateCopyPool ( + IN UINTN AllocationSize, + IN CONST VOID *Buffer + ) +{ + VOID *Memory; + + Memory = NULL; + + if ((Buffer == NULL) || (AllocationSize == 0)) { + return Memory; + } + + Memory = calloc (AllocationSize, sizeof (CHAR8)); + if (Memory != NULL) { + Memory = memcpy (Memory, Buffer, AllocationSize); + } + return Memory; +} + +/** + Initializes the head node of a doubly-linked list, and returns the pointer to + the head node of the doubly-linked list. + + Initializes the forward and backward links of a new linked list. After + initializing a linked list with this function, the other linked list + functions may be used to add and remove nodes from the linked list. It is up + to the caller of this function to allocate the memory for ListHead. + + If ListHead is NULL, then ASSERT(). + + @param ListHead A pointer to the head node of a new doubly-linked list. + + @return ListHead + +**/ +LIST_ENTRY * +InitializeListHead ( + IN OUT LIST_ENTRY *ListHead + ) + +{ + assert (ListHead != NULL); + + ListHead->ForwardLink = ListHead; + ListHead->BackLink = ListHead; + return ListHead; +} + +/** + Adds a node to the beginning of a doubly-linked list, and returns the pointer + to the head node of the doubly-linked list. + + Adds the node Entry at the beginning of the doubly-linked list denoted by + ListHead, and returns ListHead. + + If ListHead is NULL, then ASSERT(). + If Entry is NULL, then ASSERT(). + If ListHead was not initialized with INTIALIZE_LIST_HEAD_VARIABLE() or + InitializeListHead(), then ASSERT(). + If PcdMaximumLinkedListLenth is not zero, and prior to insertion the number + of nodes in ListHead, including the ListHead node, is greater than or + equal to PcdMaximumLinkedListLength, then ASSERT(). + + @param ListHead A pointer to the head node of a doubly-linked list. + @param Entry A pointer to a node that is to be inserted at the beginning + of a doubly-linked list. + + @return ListHead + +**/ +LIST_ENTRY * +InsertHeadList ( + IN OUT LIST_ENTRY *ListHead, + IN OUT LIST_ENTRY *Entry + ) +{ + assert ((ListHead != NULL) && (Entry != NULL)); + + Entry->ForwardLink = ListHead->ForwardLink; + Entry->BackLink = ListHead; + Entry->ForwardLink->BackLink = Entry; + ListHead->ForwardLink = Entry; + return ListHead; +} + +/** + Adds a node to the end of a doubly-linked list, and returns the pointer to + the head node of the doubly-linked list. + + Adds the node Entry to the end of the doubly-linked list denoted by ListHead, + and returns ListHead. + + If ListHead is NULL, then ASSERT(). + If Entry is NULL, then ASSERT(). + If ListHead was not initialized with INTIALIZE_LIST_HEAD_VARIABLE() or + InitializeListHead(), then ASSERT(). + If PcdMaximumLinkedListLenth is not zero, and prior to insertion the number + of nodes in ListHead, including the ListHead node, is greater than or + equal to PcdMaximumLinkedListLength, then ASSERT(). + + @param ListHead A pointer to the head node of a doubly-linked list. + @param Entry A pointer to a node that is to be added at the end of the + doubly-linked list. + + @return ListHead + +**/ +LIST_ENTRY * +InsertTailList ( + IN OUT LIST_ENTRY *ListHead, + IN OUT LIST_ENTRY *Entry + ) +{ + assert ((ListHead != NULL) && (Entry != NULL)); + + Entry->ForwardLink = ListHead; + Entry->BackLink = ListHead->BackLink; + Entry->BackLink->ForwardLink = Entry; + ListHead->BackLink = Entry; + return ListHead; +} + +/** + Retrieves the first node of a doubly-linked list. + + Returns the first node of a doubly-linked list. List must have been + initialized with INTIALIZE_LIST_HEAD_VARIABLE() or InitializeListHead(). + If List is empty, then List is returned. + + If List is NULL, then ASSERT(). + If List was not initialized with INTIALIZE_LIST_HEAD_VARIABLE() or + InitializeListHead(), then ASSERT(). + If PcdMaximumLinkedListLenth is not zero, and the number of nodes + in List, including the List node, is greater than or equal to + PcdMaximumLinkedListLength, then ASSERT(). + + @param List A pointer to the head node of a doubly-linked list. + + @return The first node of a doubly-linked list. + @retval NULL The list is empty. + +**/ +LIST_ENTRY * +GetFirstNode ( + IN CONST LIST_ENTRY *List + ) +{ + assert (List != NULL); + + return List->ForwardLink; +} + +/** + Retrieves the next node of a doubly-linked list. + + Returns the node of a doubly-linked list that follows Node. + List must have been initialized with INTIALIZE_LIST_HEAD_VARIABLE() + or InitializeListHead(). If List is empty, then List is returned. + + If List is NULL, then ASSERT(). + If Node is NULL, then ASSERT(). + If List was not initialized with INTIALIZE_LIST_HEAD_VARIABLE() or + InitializeListHead(), then ASSERT(). + If PcdMaximumLinkedListLenth is not zero, and List contains more than + PcdMaximumLinkedListLenth nodes, then ASSERT(). + If PcdVerifyNodeInList is TRUE and Node is not a node in List, then ASSERT(). + + @param List A pointer to the head node of a doubly-linked list. + @param Node A pointer to a node in the doubly-linked list. + + @return A pointer to the next node if one exists. Otherwise List is returned. + +**/ +LIST_ENTRY * +GetNextNode ( + IN CONST LIST_ENTRY *List, + IN CONST LIST_ENTRY *Node + ) +{ + assert ((List != NULL) && (Node != NULL)); + + return Node->ForwardLink; +} + +/** + Retrieves the previous node of a doubly-linked list. + + Returns the node of a doubly-linked list that precedes Node. + List must have been initialized with INTIALIZE_LIST_HEAD_VARIABLE() + or InitializeListHead(). If List is empty, then List is returned. + + If List is NULL, then ASSERT(). + If Node is NULL, then ASSERT(). + If List was not initialized with INTIALIZE_LIST_HEAD_VARIABLE() or + InitializeListHead(), then ASSERT(). + If PcdMaximumLinkedListLenth is not zero, and List contains more than + PcdMaximumLinkedListLenth nodes, then ASSERT(). + If PcdVerifyNodeInList is TRUE and Node is not a node in List, then ASSERT(). + + @param List A pointer to the head node of a doubly-linked list. + @param Node A pointer to a node in the doubly-linked list. + + @return A pointer to the previous node if one exists. Otherwise List is returned. + +**/ +LIST_ENTRY * +GetPreviousNode ( + IN CONST LIST_ENTRY *List, + IN CONST LIST_ENTRY *Node + ) +{ + assert ((List != NULL) && (Node != NULL)); + + return Node->BackLink; +} + +/** + Checks to see if a doubly-linked list is empty or not. + + Checks to see if the doubly-linked list is empty. If the linked list contains + zero nodes, this function returns TRUE. Otherwise, it returns FALSE. + + If ListHead is NULL, then ASSERT(). + If ListHead was not initialized with INTIALIZE_LIST_HEAD_VARIABLE() or + InitializeListHead(), then ASSERT(). + If PcdMaximumLinkedListLenth is not zero, and the number of nodes + in List, including the List node, is greater than or equal to + PcdMaximumLinkedListLength, then ASSERT(). + + @param ListHead A pointer to the head node of a doubly-linked list. + + @retval TRUE The linked list is empty. + @retval FALSE The linked list is not empty. + +**/ +BOOLEAN +IsListEmpty ( + IN CONST LIST_ENTRY *ListHead + ) +{ + assert (ListHead != NULL); + + return (BOOLEAN)(ListHead->ForwardLink == ListHead); +} + +/** + Determines if a node in a doubly-linked list is the head node of a the same + doubly-linked list. This function is typically used to terminate a loop that + traverses all the nodes in a doubly-linked list starting with the head node. + + Returns TRUE if Node is equal to List. Returns FALSE if Node is one of the + nodes in the doubly-linked list specified by List. List must have been + initialized with INTIALIZE_LIST_HEAD_VARIABLE() or InitializeListHead(). + + If List is NULL, then ASSERT(). + If Node is NULL, then ASSERT(). + If List was not initialized with INTIALIZE_LIST_HEAD_VARIABLE() or InitializeListHead(), + then ASSERT(). + If PcdMaximumLinkedListLenth is not zero, and the number of nodes + in List, including the List node, is greater than or equal to + PcdMaximumLinkedListLength, then ASSERT(). + If PcdVerifyNodeInList is TRUE and Node is not a node in List and Node is not + equal to List, then ASSERT(). + + @param List A pointer to the head node of a doubly-linked list. + @param Node A pointer to a node in the doubly-linked list. + + @retval TRUE Node is the head of the doubly-linked list pointed by List. + @retval FALSE Node is not the head of the doubly-linked list pointed by List. + +**/ +BOOLEAN +IsNull ( + IN CONST LIST_ENTRY *List, + IN CONST LIST_ENTRY *Node + ) +{ + assert ((List != NULL) && (Node != NULL)); + + return (BOOLEAN)(Node == List); +} + +/** + Determines if a node the last node in a doubly-linked list. + + Returns TRUE if Node is the last node in the doubly-linked list specified by + List. Otherwise, FALSE is returned. List must have been initialized with + INTIALIZE_LIST_HEAD_VARIABLE() or InitializeListHead(). + + If List is NULL, then ASSERT(). + If Node is NULL, then ASSERT(). + If List was not initialized with INTIALIZE_LIST_HEAD_VARIABLE() or + InitializeListHead(), then ASSERT(). + If PcdMaximumLinkedListLenth is not zero, and the number of nodes + in List, including the List node, is greater than or equal to + PcdMaximumLinkedListLength, then ASSERT(). + If PcdVerifyNodeInList is TRUE and Node is not a node in List, then ASSERT(). + + @param List A pointer to the head node of a doubly-linked list. + @param Node A pointer to a node in the doubly-linked list. + + @retval TRUE Node is the last node in the linked list. + @retval FALSE Node is not the last node in the linked list. + +**/ +BOOLEAN +IsNodeAtEnd ( + IN CONST LIST_ENTRY *List, + IN CONST LIST_ENTRY *Node + ) +{ + assert ((List != NULL) && (Node != NULL)); + + return (BOOLEAN)(!IsNull (List, Node) && (List->BackLink == Node)); +} + +/** + Removes a node from a doubly-linked list, and returns the node that follows + the removed node. + + Removes the node Entry from a doubly-linked list. It is up to the caller of + this function to release the memory used by this node if that is required. On + exit, the node following Entry in the doubly-linked list is returned. If + Entry is the only node in the linked list, then the head node of the linked + list is returned. + + If Entry is NULL, then ASSERT(). + If Entry is the head node of an empty list, then ASSERT(). + If PcdMaximumLinkedListLength is not zero, and the number of nodes in the + linked list containing Entry, including the Entry node, is greater than + or equal to PcdMaximumLinkedListLength, then ASSERT(). + + @param Entry A pointer to a node in a linked list. + + @return Entry. + +**/ +LIST_ENTRY * +RemoveEntryList ( + IN CONST LIST_ENTRY *Entry + ) +{ + assert (!IsListEmpty (Entry)); + + Entry->ForwardLink->BackLink = Entry->BackLink; + Entry->BackLink->ForwardLink = Entry->ForwardLink; + return Entry->ForwardLink; +} + diff --git a/BaseTools/Source/C/FCE/Common.h b/BaseTools/Source/C/FCE/Common.h new file mode 100644 index 0000000000..6b21974878 --- /dev/null +++ b/BaseTools/Source/C/FCE/Common.h @@ -0,0 +1,999 @@ +/** @file + + Common library. + + Copyright (c) 2011-2019, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#ifndef _COMMON_LIB_H_ +#define _COMMON_LIB_H_ + +#include +#include +#include +#ifdef __GNUC__ +#include +#else +#include +#include +#endif +#include +#include +#include +#include "CommonLib.h" +#include + +#define MAX_QUI_PARAM_LEN 2000 +#define ERROR_INFO_LENGTH 400 +#define MAX_STR_LEN_FOR_PICK_UQI 200 +#define MAX_PLATFORM_DEFAULT_ID_NUM 1000 +#define _MAX_BUILD_VERSION 100 +#define _MAXIMUM_SECTION_FILE_NUM 1000 + +#ifndef _MAX_PATH +#define _MAX_PATH 500 +#endif + +/// +/// Variable attributes. +/// +#define EFI_VARIABLE_NON_VOLATILE 0x00000001 +#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x00000002 +#define EFI_VARIABLE_RUNTIME_ACCESS 0x00000004 + +/// +/// This attribute is identified by the mnemonic 'HR' +/// elsewhere in this specification. +/// +#define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x00000010 + +#define VARSTORE_LIST_TYPE 0x0000000000000001ULL +#define EFI_VARSTORE_LIST_TYPE 0x0000000000000002ULL +#define PLATFORM_DEFAULT_ID_TYPE 0x0000000000000004ULL +#define UQI_LIST_TYPE 0x0000000000000008ULL +#define HII_OBJ_LIST_TYPE 0x0000000000000010ULL + +/// +/// LIST_ENTRY structure definition. +/// +typedef struct _LIST_ENTRY { + struct _LIST_ENTRY *ForwardLink; + struct _LIST_ENTRY *BackLink; +} LIST_ENTRY; + +#define CR(Record, TYPE, Field, TestSignature) ((TYPE *) ((CHAR8 *) (Record) - (CHAR8 *) &(((TYPE *) 0)->Field))) +#define AllocateZeroPool(a) calloc(a, sizeof (CHAR8)) +#define FreePool(a) free(a) +#define CopyMem(a, b, c) memcpy(a, b, c) +#define ZeroMem(a, b) memset(a, 0, b) +#define CompareMem(a, b, c) memcmp(a, b, c) +#define AllocatePool(a) malloc(a) + +/** + Returns a 16-bit signature built from 2 ASCII characters. + + This macro returns a 16-bit value built from the two ASCII characters specified + by A and B. + + @param A The first ASCII character. + @param B The second ASCII character. + + @return A 16-bit value built from the two ASCII characters specified by A and B. + +**/ +#define SIGNATURE_16(A, B) ((A) | (B << 8)) + +/** + Returns a 32-bit signature built from 4 ASCII characters. + + This macro returns a 32-bit value built from the four ASCII characters specified + by A, B, C, and D. + + @param A The first ASCII character. + @param B The second ASCII character. + @param C The third ASCII character. + @param D The fourth ASCII character. + + @return A 32-bit value built from the two ASCII characters specified by A, B, + C and D. + +**/ +#define SIGNATURE_32(A, B, C, D) (SIGNATURE_16 (A, B) | (SIGNATURE_16 (C, D) << 16)) + +#define ASSERT_UNICODE_BUFFER(Buffer) ASSERT ((((UINTN) (Buffer)) & 0x01) == 0) + +/** + Returns an argument of a specified type from a variable argument list and updates + the pointer to the variable argument list to point to the next argument. + + This function returns an argument of the type specified by TYPE from the beginning + of the variable argument list specified by Marker. Marker is then updated to point + to the next argument in the variable argument list. The method for computing the + pointer to the next argument in the argument list is CPU specific following the EFIAPI ABI. + + @param Marker The pointer to the beginning of a variable argument list. + @param TYPE The type of argument to retrieve from the beginning + of the variable argument list. + + @return An argument of the type specified by TYPE. + +**/ +#define BASE_ARG(Marker, TYPE) (*(TYPE *) ((Marker += _BASE_INT_SIZE_OF (TYPE)) - _BASE_INT_SIZE_OF (TYPE))) + +/// +/// Define the maximum number of characters that are required to +/// encode with a NULL terminator a decimal, hexadecimal, GUID, +/// or TIME value. +/// +/// Maximum Length Decimal String = 28 +/// "-9,223,372,036,854,775,808" +/// Maximum Length Hexadecimal String = 17 +/// "FFFFFFFFFFFFFFFF" +/// Maximum Length GUID = 37 +/// "00000000-0000-0000-0000-000000000000" +/// Maximum Length TIME = 18 +/// "12/12/2006 12:12" +/// +#define MAXIMUM_VALUE_CHARACTERS 38 + +/// +/// Pointer to the start of a variable argument list stored in a memory buffer. Same as UINT8 *. +/// +typedef UINTN *BASE_LIST; + +/** + Returns the size of a data type in sizeof(UINTN) units rounded up to the nearest UINTN boundary. + + @param TYPE The date type to determine the size of. + + @return The size of TYPE in sizeof (UINTN) units rounded up to the nearest UINTN boundary. +**/ +#define _BASE_INT_SIZE_OF(TYPE) ((sizeof (TYPE) + sizeof (UINTN) - 1) / sizeof (UINTN)) + +// +// Print primitives +// +#define PREFIX_SIGN BIT1 +#define PREFIX_BLANK BIT2 +#define LONG_TYPE BIT4 +#define OUTPUT_UNICODE BIT6 +#define FORMAT_UNICODE BIT8 +#define PAD_TO_WIDTH BIT9 +#define ARGUMENT_UNICODE BIT10 +#define PRECISION BIT11 +#define ARGUMENT_REVERSED BIT12 +#define COUNT_ONLY_NO_PRINT BIT13 + +/// +/// Flags bitmask values use in UnicodeValueToString() and +/// AsciiValueToString() +/// +#define LEFT_JUSTIFY 0x01 +#define COMMA_TYPE 0x08 +#define PREFIX_ZERO 0x20 +#define RADIX_HEX 0x80 + +// +// Record date and time information +// +typedef struct { + UINT16 Year; + UINT8 Month; + UINT8 Day; + UINT8 Hour; + UINT8 Minute; + UINT8 Second; + UINT8 Pad1; + UINT32 Nanosecond; + INT16 TimeZone; + UINT8 Daylight; + UINT8 Pad2; +} TIME; + + +/** + Copies one Null-terminated Unicode string to another Null-terminated Unicode + string and returns the new Unicode string. + + This function copies the contents of the Unicode string Source to the Unicode + string Destination, and returns Destination. If Source and Destination + overlap, then the results are undefined. + + If Destination is NULL, then return NULL. + If Destination is not aligned on a 16-bit boundary, then return NULL. + + @param Destination A pointer to a Null-terminated Unicode string. + @param Source A pointer to a Null-terminated Unicode string. + + @return Destination. + +**/ +CHAR16 * +StrCpy ( + OUT CHAR16 *Destination, + IN CONST CHAR16 *Source + ); + +/** + Returns the length of a Null-terminated Unicode string. + + This function returns the number of Unicode characters in the Null-terminated + Unicode string specified by String. + + If String is NULL, then return 0. + + @param String A pointer to a Null-terminated Unicode string. + + @return The length of String. + +**/ +UINTN +FceStrLen ( + IN CONST CHAR16 *String + ); + +/** + Returns the size of a Null-terminated Unicode string in bytes, including the + Null terminator. + + This function returns the size, in bytes, of the Null-terminated Unicode string + specified by String. + + If String is NULL, then ASSERT(). + If String is not aligned on a 16-bit boundary, then ASSERT(). + If PcdMaximumUnicodeStringLength is not zero, and String contains more than + PcdMaximumUnicodeStringLength Unicode characters, not including the + Null-terminator, then ASSERT(). + + @param String A pointer to a Null-terminated Unicode string. + + @return The size of String. + +**/ +UINTN +FceStrSize ( + IN CONST CHAR16 *String + ); + +/** + Compares two Null-terminated Unicode strings, and returns the difference + between the first mismatched Unicode characters. + + This function compares the Null-terminated Unicode string FirstString to the + Null-terminated Unicode string SecondString. If FirstString is identical to + SecondString, then 0 is returned. Otherwise, the value returned is the first + mismatched Unicode character in SecondString subtracted from the first + mismatched Unicode character in FirstString. + + @param FirstString A pointer to a Null-terminated Unicode string. + @param SecondString A pointer to a Null-terminated Unicode string. + + @retval 0 FirstString is identical to SecondString. + @return others FirstString is not identical to SecondString. + +**/ +INTN +FceStrCmp ( + IN CONST CHAR16 *FirstString, + IN CONST CHAR16 *SecondString + ); + +/** + Concatenates one Null-terminated Unicode string to another Null-terminated + Unicode string, and returns the concatenated Unicode string. + + This function concatenates two Null-terminated Unicode strings. The contents + of Null-terminated Unicode string Source are concatenated to the end of + Null-terminated Unicode string Destination. The Null-terminated concatenated + Unicode String is returned. If Source and Destination overlap, then the + results are undefined. + + If Destination is NULL, then ASSERT(). + If Destination is not aligned on a 16-bit boundary, then ASSERT(). + If Source is NULL, then ASSERT(). + If Source is not aligned on a 16-bit boundary, then ASSERT(). + If Source and Destination overlap, then ASSERT(). + If PcdMaximumUnicodeStringLength is not zero, and Destination contains more + than PcdMaximumUnicodeStringLength Unicode characters, not including the + Null-terminator, then ASSERT(). + If PcdMaximumUnicodeStringLength is not zero, and Source contains more than + PcdMaximumUnicodeStringLength Unicode characters, not including the + Null-terminator, then ASSERT(). + If PcdMaximumUnicodeStringLength is not zero, and concatenating Destination + and Source results in a Unicode string with more than + PcdMaximumUnicodeStringLength Unicode characters, not including the + Null-terminator, then ASSERT(). + + @param Destination A pointer to a Null-terminated Unicode string. + @param Source A pointer to a Null-terminated Unicode string. + + @return Destination. + +**/ +CHAR16 * +StrCat ( + IN OUT CHAR16 *Destination, + IN CONST CHAR16 *Source + ); + +/** + Returns the first occurrence of a Null-terminated Unicode sub-string + in a Null-terminated Unicode string. + + This function scans the contents of the Null-terminated Unicode string + specified by String and returns the first occurrence of SearchString. + If SearchString is not found in String, then NULL is returned. If + the length of SearchString is zero, then String is + returned. + + If String is NULL, then ASSERT(). + If String is not aligned on a 16-bit boundary, then ASSERT(). + If SearchString is NULL, then ASSERT(). + If SearchString is not aligned on a 16-bit boundary, then ASSERT(). + + If PcdMaximumUnicodeStringLength is not zero, and SearchString + or String contains more than PcdMaximumUnicodeStringLength Unicode + characters, not including the Null-terminator, then ASSERT(). + + @param String A pointer to a Null-terminated Unicode string. + @param SearchString A pointer to a Null-terminated Unicode string to search for. + + @retval NULL If the SearchString does not appear in String. + @return others If there is a match. + +**/ +CHAR16 * +StrStr ( + IN CONST CHAR16 *String, + IN CONST CHAR16 *SearchString + ); + +/** + Convert a Null-terminated Unicode decimal string to a value of + type UINT64. + + This function returns a value of type UINT64 by interpreting the contents + of the Unicode string specified by String as a decimal number. The format + of the input Unicode string String is: + + [spaces] [decimal digits]. + + The valid decimal digit character is in the range [0-9]. The + function will ignore the pad space, which includes spaces or + tab characters, before [decimal digits]. The running zero in the + beginning of [decimal digits] will be ignored. Then, the function + stops at the first character that is a not a valid decimal character + or a Null-terminator, whichever one comes first. + + If String is NULL, then ASSERT(). + If String is not aligned in a 16-bit boundary, then ASSERT(). + If String has only pad spaces, then 0 is returned. + If String has no pad spaces or valid decimal digits, + then 0 is returned. + If the number represented by String overflows according + to the range defined by UINT64, then ASSERT(). + + If PcdMaximumUnicodeStringLength is not zero, and String contains + more than PcdMaximumUnicodeStringLength Unicode characters, not including + the Null-terminator, then ASSERT(). + + @param String A pointer to a Null-terminated Unicode string. + + @retval Value translated from String. + +**/ +UINT64 +FceStrDecimalToUint64 ( + IN CONST CHAR16 *String + ); + + +/** + Convert one Null-terminated ASCII string to a Null-terminated + Unicode string and returns the Unicode string. + + This function converts the contents of the ASCII string Source to the Unicode + string Destination, and returns Destination. The function terminates the + Unicode string Destination by appending a Null-terminator character at the end. + The caller is responsible to make sure Destination points to a buffer with size + equal or greater than ((AsciiStrLen (Source) + 1) * sizeof (CHAR16)) in bytes. + + @param Source A pointer to a Null-terminated ASCII string. + @param Destination A pointer to a Null-terminated Unicode string. + + @return Destination. + @return NULL If Destination or Source is NULL, return NULL. + +**/ +CHAR16 * +AsciiStrToUnicodeStr ( + IN CONST CHAR8 *Source, + OUT CHAR16 *Destination + ); + +/** + Worker function that produces a Null-terminated string in an output buffer + based on a Null-terminated format string and variable argument list. + + VSPrint function to process format and place the results in Buffer. Since a + VA_LIST is used this routine allows the nesting of Vararg routines. Thus + this is the main print working routine + + @param StartOfBuffer The character buffer to print the results of the parsing + of Format into. + @param BufferSize The maximum number of characters to put into buffer. + Zero means no limit. + @param Flags Initial flags value. + Can only have FORMAT_UNICODE and OUTPUT_UNICODE set + @param FormatString A Null-terminated format string. + @param ... The variable argument list. + + @return The number of characters printed. + +**/ +UINTN +BasePrintLibSPrint ( + OUT CHAR8 *StartOfBuffer, + IN UINTN BufferSize, + IN UINTN Flags, + IN CONST CHAR8 *FormatString, + ... + ); + +/** + Produces a Null-terminated Unicode string in an output buffer based on a Null-terminated + Unicode format string and variable argument list. + + Produces a Null-terminated Unicode string in the output buffer specified by StartOfBuffer + and BufferSize. + The Unicode string is produced by parsing the format string specified by FormatString. + Arguments are pulled from the variable argument list based on the contents of the format string. + The number of Unicode characters in the produced output buffer is returned not including + the Null-terminator. + If BufferSize is 0 or 1, then no output buffer is produced and 0 is returned. + + If BufferSize > 1 and StartOfBuffer is NULL, then ASSERT(). + If BufferSize > 1 and StartOfBuffer is not aligned on a 16-bit boundary, then ASSERT(). + If BufferSize > 1 and FormatString is NULL, then ASSERT(). + If BufferSize > 1 and FormatString is not aligned on a 16-bit boundary, then ASSERT(). + If PcdMaximumUnicodeStringLength is not zero, and FormatString contains more than + PcdMaximumUnicodeStringLength Unicode characters not including the Null-terminator, then + ASSERT(). + If PcdMaximumUnicodeStringLength is not zero, and produced Null-terminated Unicode string + contains more than PcdMaximumUnicodeStringLength Unicode characters not including the + Null-terminator, then ASSERT(). + + @param StartOfBuffer A pointer to the output buffer for the produced Null-terminated + Unicode string. + @param BufferSize The size, in bytes, of the output buffer specified by StartOfBuffer. + @param FormatString A Null-terminated Unicode format string. + @param ... Variable argument list whose contents are accessed based on the + format string specified by FormatString. + + @return The number of Unicode characters in the produced output buffer not including the + Null-terminator. + +**/ +UINTN +UnicodeSPrint ( + OUT CHAR16 *StartOfBuffer, + IN UINTN BufferSize, + IN CONST CHAR16 *FormatString, + ... + ); + +/** + Convert a Null-terminated Unicode string to a Null-terminated + ASCII string and returns the ASCII string. + + This function converts the content of the Unicode string Source + to the ASCII string Destination by copying the lower 8 bits of + each Unicode character. It returns Destination. The function terminates + the ASCII string Destination by appending a Null-terminator character + at the end. The caller is responsible to make sure Destination points + to a buffer with size equal or greater than (FceStrLen (Source) + 1) in bytes. + + If Destination is NULL, then ASSERT(). + If Source is NULL, then ASSERT(). + If Source is not aligned on a 16-bit boundary, then ASSERT(). + If Source and Destination overlap, then ASSERT(). + + If any Unicode characters in Source contain non-zero value in + the upper 8 bits, then ASSERT(). + + @param Source Pointer to a Null-terminated Unicode string. + @param Destination Pointer to a Null-terminated ASCII string. + + @reture Destination + +**/ +CHAR8 * +UnicodeStrToAsciiStr ( + IN CONST CHAR16 *Source, + OUT CHAR8 *Destination + ); + +/** + Allocate new memory and then copy the Unicode string Source to Destination. + + @param Dest Location to copy string + @param Src String to copy + +**/ +VOID +NewStringCpy ( + IN OUT CHAR16 **Dest, + IN CHAR16 *Src + ); + +/** + Convert a Null-terminated Unicode hexadecimal string to a value of type UINT64. + + This function returns a value of type UINT64 by interpreting the contents + of the Unicode string specified by String as a hexadecimal number. + The format of the input Unicode string String is + + [spaces][zeros][x][hexadecimal digits]. + + The valid hexadecimal digit character is in the range [0-9], [a-f] and [A-F]. + The prefix "0x" is optional. Both "x" and "X" is allowed in "0x" prefix. + If "x" appears in the input string, it must be prefixed with at least one 0. + The function will ignore the pad space, which includes spaces or tab characters, + before [zeros], [x] or [hexadecimal digit]. The running zero before [x] or + [hexadecimal digit] will be ignored. Then, the decoding starts after [x] or the + first valid hexadecimal digit. Then, the function stops at the first character that is + a not a valid hexadecimal character or NULL, whichever one comes first. + + If String is NULL, then ASSERT(). + If String is not aligned in a 16-bit boundary, then ASSERT(). + If String has only pad spaces, then zero is returned. + If String has no leading pad spaces, leading zeros or valid hexadecimal digits, + then zero is returned. + If the number represented by String overflows according to the range defined by + UINT64, then ASSERT(). + + If PcdMaximumUnicodeStringLength is not zero, and String contains more than + PcdMaximumUnicodeStringLength Unicode characters, not including the Null-terminator, + then ASSERT(). + + @param String A pointer to a Null-terminated Unicode string. + + @retval Value translated from String. + +**/ +UINT64 +FceStrHexToUint64 ( + IN CONST CHAR16 *String + ); + + +CHAR16 +ToUpper ( + CHAR16 a + ); + +CHAR16 +ToLower ( + CHAR16 a + ); + +/** + Performs a case-insensitive comparison between a Null-terminated + Unicode pattern string and a Null-terminated Unicode string. + + @param String - A pointer to a Null-terminated Unicode string. + @param Pattern - A pointer to a Null-terminated Unicode pattern string. + + + @retval TRUE - Pattern was found in String. + @retval FALSE - Pattern was not found in String. + +**/ +BOOLEAN +MetaiMatch ( + IN CHAR16 *String, + IN CHAR16 *Pattern + ); + +/** + Multiplies a 64-bit unsigned integer by a 32-bit unsigned integer and + generates a 64-bit unsigned result. + + This function multiplies the 64-bit unsigned value Multiplicand by the 32-bit + unsigned value Multiplier and generates a 64-bit unsigned result. This 64- + bit unsigned result is returned. + + @param Multiplicand A 64-bit unsigned value. + @param Multiplier A 32-bit unsigned value. + + @return Multiplicand * Multiplier. + +**/ +UINT64 +MultU64x32 ( + IN UINT64 Multiplicand, + IN UINT32 Multiplier + ); + +/** + Divides a 64-bit unsigned integer by a 32-bit unsigned integer and generates + a 64-bit unsigned result. + + This function divides the 64-bit unsigned value Dividend by the 32-bit + unsigned value Divisor and generates a 64-bit unsigned quotient. This + function returns the 64-bit unsigned quotient. + + If Divisor is 0, then ASSERT(). + + @param Dividend A 64-bit unsigned value. + @param Divisor A 32-bit unsigned value. + + @return Dividend / Divisor + +**/ +UINT64 +DivU64x32 ( + IN UINT64 Dividend, + IN UINT32 Divisor + ); + +/** + Shifts a 64-bit integer left between 0 and 63 bits. The low bits are filled + with zeros. The shifted value is returned. + + This function shifts the 64-bit value Operand to the left by Count bits. The + low Count bits are set to zero. The shifted value is returned. + + If Count is greater than 63, then ASSERT(). + + @param Operand The 64-bit operand to shift left. + @param Count The number of bits to shift left. + + @return Operand << Count. + +**/ +UINT64 +LShiftU64 ( + IN UINT64 Operand, + IN UINTN Count + ); + +/** + Shifts a 64-bit integer right between 0 and 63 bits. This high bits are + filled with zeros. The shifted value is returned. + + This function shifts the 64-bit value Operand to the right by Count bits. The + high Count bits are set to zero. The shifted value is returned. + + If Count is greater than 63, then ASSERT(). + + @param Operand The 64-bit operand to shift right. + @param Count The number of bits to shift right. + + @return Operand >> Count. + +**/ +UINT64 +RShiftU64 ( + IN UINT64 Operand, + IN UINTN Count + ); + + +/** + Divides a 64-bit unsigned integer by a 32-bit unsigned integer and generates + a 64-bit unsigned result and an optional 32-bit unsigned remainder. + + This function divides the 64-bit unsigned value Dividend by the 32-bit + unsigned value Divisor and generates a 64-bit unsigned quotient. If Remainder + is not NULL, then the 32-bit unsigned remainder is returned in Remainder. + This function returns the 64-bit unsigned quotient. + + If Divisor is 0, then ASSERT(). + + @param Dividend A 64-bit unsigned value. + @param Divisor A 32-bit unsigned value. + @param Remainder A pointer to a 32-bit unsigned value. This parameter is + optional and may be NULL. + + @return Dividend / Divisor + +**/ +UINT64 +DivU64x32Remainder ( + IN UINT64 Dividend, + IN UINT32 Divisor, + OUT UINT32 *Remainder + ); + +/** + Copies a buffer to an allocated buffer. + + Allocates the number bytes specified by AllocationSize, copies allocationSize bytes + from Buffer to the newly allocated buffer, and returns a pointer to the allocated + buffer. If AllocationSize is 0, then a valid buffer of 0 size is returned. If there + is not enough memory remaining to satisfy the request, then NULL is returned. + + If Buffer is NULL, then ASSERT(). + + @param AllocationSize The number of bytes to allocate and zero. + @param Buffer The buffer to copy to the allocated buffer. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +FceAllocateCopyPool ( + IN UINTN AllocationSize, + IN CONST VOID *Buffer + ); + +/** + Initializes the head node of a doubly-linked list, and returns the pointer to + the head node of the doubly-linked list. + + Initializes the forward and backward links of a new linked list. After + initializing a linked list with this function, the other linked list + functions may be used to add and remove nodes from the linked list. It is up + to the caller of this function to allocate the memory for ListHead. + + If ListHead is NULL, then ASSERT(). + + @param ListHead A pointer to the head node of a new doubly-linked list. + + @return ListHead + +**/ +LIST_ENTRY * +InitializeListHead ( + IN OUT LIST_ENTRY *ListHead + ); + +/** + Adds a node to the beginning of a doubly-linked list, and returns the pointer + to the head node of the doubly-linked list. + + Adds the node Entry at the beginning of the doubly-linked list denoted by + ListHead, and returns ListHead. + + If ListHead is NULL, then ASSERT(). + If Entry is NULL, then ASSERT(). + If ListHead was not initialized with INTIALIZE_LIST_HEAD_VARIABLE() or + InitializeListHead(), then ASSERT(). + If PcdMaximumLinkedListLenth is not zero, and prior to insertion the number + of nodes in ListHead, including the ListHead node, is greater than or + equal to PcdMaximumLinkedListLength, then ASSERT(). + + @param ListHead A pointer to the head node of a doubly-linked list. + @param Entry A pointer to a node that is to be inserted at the beginning + of a doubly-linked list. + + @return ListHead + +**/ +LIST_ENTRY * +InsertHeadList ( + IN OUT LIST_ENTRY *ListHead, + IN OUT LIST_ENTRY *Entry + ); + +/** + Adds a node to the end of a doubly-linked list, and returns the pointer to + the head node of the doubly-linked list. + + Adds the node Entry to the end of the doubly-linked list denoted by ListHead, + and returns ListHead. + + If ListHead is NULL, then ASSERT(). + If Entry is NULL, then ASSERT(). + If ListHead was not initialized with INTIALIZE_LIST_HEAD_VARIABLE() or + InitializeListHead(), then ASSERT(). + If PcdMaximumLinkedListLenth is not zero, and prior to insertion the number + of nodes in ListHead, including the ListHead node, is greater than or + equal to PcdMaximumLinkedListLength, then ASSERT(). + + @param ListHead A pointer to the head node of a doubly-linked list. + @param Entry A pointer to a node that is to be added at the end of the + doubly-linked list. + + @return ListHead + +**/ +LIST_ENTRY * +InsertTailList ( + IN OUT LIST_ENTRY *ListHead, + IN OUT LIST_ENTRY *Entry + ); + +/** + Retrieves the first node of a doubly-linked list. + + Returns the first node of a doubly-linked list. List must have been + initialized with INTIALIZE_LIST_HEAD_VARIABLE() or InitializeListHead(). + If List is empty, then List is returned. + + If List is NULL, then ASSERT(). + If List was not initialized with INTIALIZE_LIST_HEAD_VARIABLE() or + InitializeListHead(), then ASSERT(). + If PcdMaximumLinkedListLenth is not zero, and the number of nodes + in List, including the List node, is greater than or equal to + PcdMaximumLinkedListLength, then ASSERT(). + + @param List A pointer to the head node of a doubly-linked list. + + @return The first node of a doubly-linked list. + @retval NULL The list is empty. + +**/ +LIST_ENTRY * +GetFirstNode ( + IN CONST LIST_ENTRY *List + ); + +/** + Retrieves the next node of a doubly-linked list. + + Returns the node of a doubly-linked list that follows Node. + List must have been initialized with INTIALIZE_LIST_HEAD_VARIABLE() + or InitializeListHead(). If List is empty, then List is returned. + + If List is NULL, then ASSERT(). + If Node is NULL, then ASSERT(). + If List was not initialized with INTIALIZE_LIST_HEAD_VARIABLE() or + InitializeListHead(), then ASSERT(). + If PcdMaximumLinkedListLenth is not zero, and List contains more than + PcdMaximumLinkedListLenth nodes, then ASSERT(). + If PcdVerifyNodeInList is TRUE and Node is not a node in List, then ASSERT(). + + @param List A pointer to the head node of a doubly-linked list. + @param Node A pointer to a node in the doubly-linked list. + + @return A pointer to the next node if one exists. Otherwise List is returned. + +**/ +LIST_ENTRY * +GetNextNode ( + IN CONST LIST_ENTRY *List, + IN CONST LIST_ENTRY *Node + ); + +/** + Retrieves the previous node of a doubly-linked list. + + Returns the node of a doubly-linked list that precedes Node. + List must have been initialized with INTIALIZE_LIST_HEAD_VARIABLE() + or InitializeListHead(). If List is empty, then List is returned. + + If List is NULL, then ASSERT(). + If Node is NULL, then ASSERT(). + If List was not initialized with INTIALIZE_LIST_HEAD_VARIABLE() or + InitializeListHead(), then ASSERT(). + If PcdMaximumLinkedListLenth is not zero, and List contains more than + PcdMaximumLinkedListLenth nodes, then ASSERT(). + If PcdVerifyNodeInList is TRUE and Node is not a node in List, then ASSERT(). + + @param List A pointer to the head node of a doubly-linked list. + @param Node A pointer to a node in the doubly-linked list. + + @return A pointer to the previous node if one exists. Otherwise List is returned. + +**/ +LIST_ENTRY * +GetPreviousNode ( + IN CONST LIST_ENTRY *List, + IN CONST LIST_ENTRY *Node + ); + +/** + Checks to see if a doubly-linked list is empty or not. + + Checks to see if the doubly-linked list is empty. If the linked list contains + zero nodes, this function returns TRUE. Otherwise, it returns FALSE. + + If ListHead is NULL, then ASSERT(). + If ListHead was not initialized with INTIALIZE_LIST_HEAD_VARIABLE() or + InitializeListHead(), then ASSERT(). + If PcdMaximumLinkedListLenth is not zero, and the number of nodes + in List, including the List node, is greater than or equal to + PcdMaximumLinkedListLength, then ASSERT(). + + @param ListHead A pointer to the head node of a doubly-linked list. + + @retval TRUE The linked list is empty. + @retval FALSE The linked list is not empty. + +**/ +BOOLEAN +IsListEmpty ( + IN CONST LIST_ENTRY *ListHead + ); + +/** + Determines if a node in a doubly-linked list is the head node of a the same + doubly-linked list. This function is typically used to terminate a loop that + traverses all the nodes in a doubly-linked list starting with the head node. + + Returns TRUE if Node is equal to List. Returns FALSE if Node is one of the + nodes in the doubly-linked list specified by List. List must have been + initialized with INTIALIZE_LIST_HEAD_VARIABLE() or InitializeListHead(). + + If List is NULL, then ASSERT(). + If Node is NULL, then ASSERT(). + If List was not initialized with INTIALIZE_LIST_HEAD_VARIABLE() or InitializeListHead(), + then ASSERT(). + If PcdMaximumLinkedListLenth is not zero, and the number of nodes + in List, including the List node, is greater than or equal to + PcdMaximumLinkedListLength, then ASSERT(). + If PcdVerifyNodeInList is TRUE and Node is not a node in List and Node is not + equal to List, then ASSERT(). + + @param List A pointer to the head node of a doubly-linked list. + @param Node A pointer to a node in the doubly-linked list. + + @retval TRUE Node is the head of the doubly-linked list pointed by List. + @retval FALSE Node is not the head of the doubly-linked list pointed by List. + +**/ +BOOLEAN +IsNull ( + IN CONST LIST_ENTRY *List, + IN CONST LIST_ENTRY *Node + ); + +/** + Determines if a node the last node in a doubly-linked list. + + Returns TRUE if Node is the last node in the doubly-linked list specified by + List. Otherwise, FALSE is returned. List must have been initialized with + INTIALIZE_LIST_HEAD_VARIABLE() or InitializeListHead(). + + If List is NULL, then ASSERT(). + If Node is NULL, then ASSERT(). + If List was not initialized with INTIALIZE_LIST_HEAD_VARIABLE() or + InitializeListHead(), then ASSERT(). + If PcdMaximumLinkedListLenth is not zero, and the number of nodes + in List, including the List node, is greater than or equal to + PcdMaximumLinkedListLength, then ASSERT(). + If PcdVerifyNodeInList is TRUE and Node is not a node in List, then ASSERT(). + + @param List A pointer to the head node of a doubly-linked list. + @param Node A pointer to a node in the doubly-linked list. + + @retval TRUE Node is the last node in the linked list. + @retval FALSE Node is not the last node in the linked list. + +**/ +BOOLEAN +IsNodeAtEnd ( + IN CONST LIST_ENTRY *List, + IN CONST LIST_ENTRY *Node + ); + +/** + Removes a node from a doubly-linked list, and returns the node that follows + the removed node. + + Removes the node Entry from a doubly-linked list. It is up to the caller of + this function to release the memory used by this node if that is required. On + exit, the node following Entry in the doubly-linked list is returned. If + Entry is the only node in the linked list, then the head node of the linked + list is returned. + + If Entry is NULL, then ASSERT(). + If Entry is the head node of an empty list, then ASSERT(). + If PcdMaximumLinkedListLength is not zero, and the number of nodes in the + linked list containing Entry, including the Entry node, is greater than + or equal to PcdMaximumLinkedListLength, then ASSERT(). + + @param Entry A pointer to a node in a linked list. + + @return Entry. + +**/ +LIST_ENTRY * +RemoveEntryList ( + IN CONST LIST_ENTRY *Entry + ); + +#endif diff --git a/BaseTools/Source/C/FCE/Expression.c b/BaseTools/Source/C/FCE/Expression.c new file mode 100644 index 0000000000..34b310d97f --- /dev/null +++ b/BaseTools/Source/C/FCE/Expression.c @@ -0,0 +1,2367 @@ +/** @file + + Utility functions for expression evaluation. + + Copyright (c) 2011-2019, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "IfrParse.h" + +#define gEmptyString L"" +// +// Global stack used to evaluate boolean expresions +// +EFI_HII_VALUE *mOpCodeScopeStack = NULL; +EFI_HII_VALUE *mOpCodeScopeStackEnd = NULL; +EFI_HII_VALUE *mOpCodeScopeStackPointer = NULL; + +EFI_HII_VALUE *mExpressionEvaluationStack = NULL; +EFI_HII_VALUE *mExpressionEvaluationStackEnd = NULL; +EFI_HII_VALUE *mExpressionEvaluationStackPointer = NULL; +UINTN mExpressionEvaluationStackOffset = 0; + +EFI_HII_VALUE *mCurrentExpressionStack = NULL; +EFI_HII_VALUE *mCurrentExpressionEnd = NULL; +EFI_HII_VALUE *mCurrentExpressionPointer = NULL; + +EFI_HII_VALUE *mMapExpressionListStack = NULL; +EFI_HII_VALUE *mMapExpressionListEnd = NULL; +EFI_HII_VALUE *mMapExpressionListPointer = NULL; + +/** + Get Value for given Name from a NameValue Storage. + + @param Storage The NameValue Storage. + @param Name The Name. + @param Value The retured Value. + + @retval EFI_SUCCESS Value found for given Name. + @retval EFI_NOT_FOUND No such Name found in NameValue storage. + +**/ +EFI_STATUS +GetValueByName ( + IN FORMSET_STORAGE *Storage, + IN CHAR16 *Name, + IN OUT CHAR16 **Value + ) +{ + LIST_ENTRY *Link; + NAME_VALUE_NODE *Node; + + *Value = NULL; + + Link = GetFirstNode (&Storage->NameValueListHead); + while (!IsNull (&Storage->NameValueListHead, Link)) { + Node = NAME_VALUE_NODE_FROM_LINK (Link); + + if (FceStrCmp (Name, Node->Name) == 0) { + NewStringCpy (Value, Node->EditValue); + return EFI_SUCCESS; + } + + Link = GetNextNode (&Storage->NameValueListHead, Link); + } + + return EFI_NOT_FOUND; +} + +/** + Grow size of the stack. + + This is an internal function. + + @param Stack On input: old stack; On output: new stack + @param StackPtr On input: old stack pointer; On output: new stack + pointer + @param StackEnd On input: old stack end; On output: new stack end + + @retval EFI_SUCCESS Grow stack success. + @retval EFI_OUT_OF_RESOURCES No enough memory for stack space. + +**/ +EFI_STATUS +GrowStack ( + IN OUT EFI_HII_VALUE **Stack, + IN OUT EFI_HII_VALUE **StackPtr, + IN OUT EFI_HII_VALUE **StackEnd + ) +{ + UINTN Size; + EFI_HII_VALUE *NewStack; + + Size = EXPRESSION_STACK_SIZE_INCREMENT; + if (*StackPtr != NULL) { + Size = Size + (*StackEnd - *Stack); + } + + NewStack = AllocatePool (Size * sizeof (EFI_HII_VALUE)); + if (NewStack == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (*StackPtr != NULL) { + // + // Copy from Old Stack to the New Stack + // + CopyMem ( + NewStack, + *Stack, + (*StackEnd - *Stack) * sizeof (EFI_HII_VALUE) + ); + + // + // Free The Old Stack + // + FreePool (*Stack); + } + + // + // Make the Stack pointer point to the old data in the new stack + // + *StackPtr = NewStack + (*StackPtr - *Stack); + *Stack = NewStack; + *StackEnd = NewStack + Size; + + return EFI_SUCCESS; +} + + +/** + Push an element onto the Boolean Stack. + + @param Stack On input: old stack; On output: new stack + @param StackPtr On input: old stack pointer; On output: new stack + pointer + @param StackEnd On input: old stack end; On output: new stack end + @param Data Data to push. + + @retval EFI_SUCCESS Push stack success. + +**/ +EFI_STATUS +PushStack ( + IN OUT EFI_HII_VALUE **Stack, + IN OUT EFI_HII_VALUE **StackPtr, + IN OUT EFI_HII_VALUE **StackEnd, + IN EFI_HII_VALUE *Data + ) +{ + EFI_STATUS Status; + + // + // Check for a stack overflow condition + // + if (*StackPtr >= *StackEnd) { + // + // Grow the stack + // + Status = GrowStack (Stack, StackPtr, StackEnd); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Push the item onto the stack + // + CopyMem (*StackPtr, Data, sizeof (EFI_HII_VALUE)); + *StackPtr = *StackPtr + 1; + + return EFI_SUCCESS; +} + + +/** + Pop an element from the stack. + + @param Stack On input: old stack + @param StackPtr On input: old stack pointer; On output: new stack pointer + @param Data Data to pop. + + @retval EFI_SUCCESS The value was popped onto the stack. + @retval EFI_ACCESS_DENIED The pop operation underflowed the stack + +**/ +EFI_STATUS +PopStack ( + IN EFI_HII_VALUE *Stack, + IN OUT EFI_HII_VALUE **StackPtr, + OUT EFI_HII_VALUE *Data + ) +{ + // + // Check for a stack underflow condition + // + if (*StackPtr == Stack) { + return EFI_ACCESS_DENIED; + } + + // + // Pop the item off the stack + // + *StackPtr = *StackPtr - 1; + CopyMem (Data, *StackPtr, sizeof (EFI_HII_VALUE)); + return EFI_SUCCESS; +} + + +/** + Reset stack pointer to begin of the stack. + +**/ +VOID +ResetCurrentExpressionStack ( + VOID + ) +{ + mCurrentExpressionPointer = mCurrentExpressionStack; +} + + +/** + Push current expression onto the Stack + + @param Pointer Pointer to current expression. + + @retval EFI_SUCCESS The value was pushed onto the stack. + @retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the stack. + +**/ +EFI_STATUS +PushCurrentExpression ( + IN VOID *Pointer + ) +{ + EFI_HII_VALUE Data; + + Data.Type = EFI_IFR_TYPE_NUM_SIZE_64; + Data.Value.u64 = (UINT64) (UINTN) Pointer; + + return PushStack ( + &mCurrentExpressionStack, + &mCurrentExpressionPointer, + &mCurrentExpressionEnd, + &Data + ); +} + + +/** + Pop current expression from the Stack + + @param Pointer Pointer to current expression to be pop. + + @retval EFI_SUCCESS The value was pushed onto the stack. + @retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the stack. + +**/ +EFI_STATUS +PopCurrentExpression ( + OUT VOID **Pointer + ) +{ + EFI_STATUS Status; + EFI_HII_VALUE Data; + + Status = PopStack ( + mCurrentExpressionStack, + &mCurrentExpressionPointer, + &Data + ); + + *Pointer = (VOID *) (UINTN) Data.Value.u64; + + return Status; +} + +/** + Reset stack pointer to begin of the stack. + +**/ +VOID +ResetMapExpressionListStack ( + VOID + ) +{ + mMapExpressionListPointer = mMapExpressionListStack; +} + + +/** + Push the list of map expression onto the Stack + + @param Pointer Pointer to the list of map expression to be pushed. + + @retval EFI_SUCCESS The value was pushed onto the stack. + @retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the stack. + +**/ +EFI_STATUS +PushMapExpressionList ( + IN VOID *Pointer + ) +{ + EFI_HII_VALUE Data; + + Data.Type = EFI_IFR_TYPE_NUM_SIZE_64; + Data.Value.u64 = (UINT64) (UINTN) Pointer; + + return PushStack ( + &mMapExpressionListStack, + &mMapExpressionListPointer, + &mMapExpressionListEnd, + &Data + ); +} + + +/** + Pop the list of map expression from the Stack + + @param Pointer Pointer to the list of map expression to be pop. + + @retval EFI_SUCCESS The value was pushed onto the stack. + @retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the stack. + +**/ +EFI_STATUS +PopMapExpressionList ( + OUT VOID **Pointer + ) +{ + EFI_STATUS Status; + EFI_HII_VALUE Data; + + Status = PopStack ( + mMapExpressionListStack, + &mMapExpressionListPointer, + &Data + ); + + *Pointer = (VOID *) (UINTN) Data.Value.u64; + + return Status; +} + +/** + Reset stack pointer to begin of the stack. + +**/ +VOID +ResetScopeStack ( + VOID + ) +{ + mOpCodeScopeStackPointer = mOpCodeScopeStack; +} + + +/** + Push an Operand onto the Stack + + @param Operand Operand to push. + + @retval EFI_SUCCESS The value was pushed onto the stack. + @retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the + stack. + +**/ +EFI_STATUS +PushScope ( + IN UINT8 Operand + ) +{ + EFI_HII_VALUE Data; + + Data.Type = EFI_IFR_TYPE_NUM_SIZE_8; + Data.Value.u8 = Operand; + + return PushStack ( + &mOpCodeScopeStack, + &mOpCodeScopeStackPointer, + &mOpCodeScopeStackEnd, + &Data + ); +} + + +/** + Pop an Operand from the Stack + + @param Operand Operand to pop. + + @retval EFI_SUCCESS The value was pushed onto the stack. + @retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the + stack. + +**/ +EFI_STATUS +PopScope ( + OUT UINT8 *Operand + ) +{ + EFI_STATUS Status; + EFI_HII_VALUE Data; + + Status = PopStack ( + mOpCodeScopeStack, + &mOpCodeScopeStackPointer, + &Data + ); + + *Operand = Data.Value.u8; + + return Status; +} + + +/** + Push an Expression value onto the Stack + + @param Value Expression value to push. + + @retval EFI_SUCCESS The value was pushed onto the stack. + @retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the + stack. + +**/ +EFI_STATUS +PushExpression ( + IN EFI_HII_VALUE *Value + ) +{ + return PushStack ( + &mExpressionEvaluationStack, + &mExpressionEvaluationStackPointer, + &mExpressionEvaluationStackEnd, + Value + ); +} + + +/** + Pop an Expression value from the stack. + + @param Value Expression value to pop. + + @retval EFI_SUCCESS The value was popped onto the stack. + @retval EFI_ACCESS_DENIED The pop operation underflowed the stack + +**/ +EFI_STATUS +PopExpression ( + OUT EFI_HII_VALUE *Value + ) +{ + return PopStack ( + mExpressionEvaluationStack + mExpressionEvaluationStackOffset, + &mExpressionEvaluationStackPointer, + Value + ); +} + +/** + Get current stack offset from stack start. + + @return Stack offset to stack start. +**/ +UINTN +SaveExpressionEvaluationStackOffset ( + ) +{ + UINTN TempStackOffset; + TempStackOffset = mExpressionEvaluationStackOffset; + mExpressionEvaluationStackOffset = mExpressionEvaluationStackPointer - mExpressionEvaluationStack; + return TempStackOffset; +} + +/** + Restore stack offset based on input stack offset + + @param StackOffset Offset to stack start. + +**/ +VOID +RestoreExpressionEvaluationStackOffset ( + UINTN StackOffset + ) +{ + mExpressionEvaluationStackOffset = StackOffset; +} + + +/** + Search a Question in Form scope using its QuestionId. + + @param Form The form which contains this Question. + @param QuestionId Id of this Question. + + @retval Pointer The Question. + @retval NULL Specified Question not found in the form. + +**/ +FORM_BROWSER_STATEMENT * +IdToQuestion2 ( + IN FORM_BROWSER_FORM *Form, + IN UINT16 QuestionId + ) +{ + LIST_ENTRY *Link; + FORM_BROWSER_STATEMENT *Question; + + if (QuestionId == 0) { + // + // The value of zero is reserved + // + return NULL; + } + + Link = GetFirstNode (&Form->StatementListHead); + while (!IsNull (&Form->StatementListHead, Link)) { + Question = FORM_BROWSER_STATEMENT_FROM_LINK (Link); + + if (Question->QuestionId == QuestionId) { + return Question; + } + + Link = GetNextNode (&Form->StatementListHead, Link); + } + + return NULL; +} + + +/** + Search a Question in Formset scope using its QuestionId. + + @param FormSet The formset which contains this form. + @param Form The form which contains this Question. + @param QuestionId Id of this Question. + + @retval Pointer The Question. + @retval NULL Specified Question not found in the form. + +**/ +FORM_BROWSER_STATEMENT * +IdToQuestion ( + IN FORM_BROWSER_FORMSET *FormSet, + IN FORM_BROWSER_FORM *Form, + IN UINT16 QuestionId + ) +{ + LIST_ENTRY *Link; + FORM_BROWSER_STATEMENT *Question; + + // + // Search in the form scope first + // + Question = IdToQuestion2 (Form, QuestionId); + if (Question != NULL) { + return Question; + } + + // + // Search in the formset scope + // + Link = GetFirstNode (&FormSet->FormListHead); + while (!IsNull (&FormSet->FormListHead, Link)) { + Form = FORM_BROWSER_FORM_FROM_LINK (Link); + + Question = IdToQuestion2 (Form, QuestionId); + if (Question != NULL) { + return Question; + } + + Link = GetNextNode (&FormSet->FormListHead, Link); + } + + return NULL; +} + + +/** + Get Expression given its RuleId. + + @param Form The form which contains this Expression. + @param RuleId Id of this Expression. + + @retval Pointer The Expression. + @retval NULL Specified Expression not found in the form. + +**/ +FORM_EXPRESSION * +RuleIdToExpression ( + IN FORM_BROWSER_FORM *Form, + IN UINT8 RuleId + ) +{ + LIST_ENTRY *Link; + FORM_EXPRESSION *Expression; + + Link = GetFirstNode (&Form->ExpressionListHead); + while (!IsNull (&Form->ExpressionListHead, Link)) { + Expression = FORM_EXPRESSION_FROM_LINK (Link); + + if ((Expression->Type == EFI_HII_EXPRESSION_RULE) && (Expression->RuleId == RuleId)) { + return Expression; + } + + Link = GetNextNode (&Form->ExpressionListHead, Link); + } + + return NULL; +} + +/** + Convert the input Unicode character to upper. + + @param String Th Unicode character to be converted. + +**/ +VOID +IfrStrToUpper ( + IN CHAR16 *String + ) +{ + while (*String != 0) { + if ((*String >= 'a') && (*String <= 'z')) { + *String = (UINT16) ((*String) & ((UINT16) ~0x20)); + } + String++; + } +} + +/** + Evaluate opcode EFI_IFR_TO_STRING. + + @param FormSet Formset which contains this opcode. + @param Format String format in EFI_IFR_TO_STRING. + @param Result Evaluation result for this opcode. + + @retval EFI_SUCCESS Opcode evaluation success. + @retval Other Opcode evaluation failed. + +**/ +EFI_STATUS +IfrToString ( + IN FORM_BROWSER_FORMSET *FormSet, + IN UINT8 Format, + OUT EFI_HII_VALUE *Result + ) +{ + EFI_STATUS Status; + EFI_HII_VALUE Value; + CHAR16 *PrintFormat; + CHAR16 Buffer[MAXIMUM_VALUE_CHARACTERS]; + UINTN BufferSize; + + Status = PopExpression (&Value); + if (EFI_ERROR (Status)) { + return Status; + } + + switch (Value.Type) { + case EFI_IFR_TYPE_NUM_SIZE_8: + case EFI_IFR_TYPE_NUM_SIZE_16: + case EFI_IFR_TYPE_NUM_SIZE_32: + case EFI_IFR_TYPE_NUM_SIZE_64: + BufferSize = MAXIMUM_VALUE_CHARACTERS * sizeof (CHAR16); + switch (Format) { + case EFI_IFR_STRING_UNSIGNED_DEC: + case EFI_IFR_STRING_SIGNED_DEC: + PrintFormat = L"%ld"; + break; + + case EFI_IFR_STRING_LOWERCASE_HEX: + PrintFormat = L"%lx"; + break; + + case EFI_IFR_STRING_UPPERCASE_HEX: + PrintFormat = L"%lX"; + break; + + default: + return EFI_UNSUPPORTED; + } + UnicodeSPrint (Buffer, BufferSize, PrintFormat, Value.Value.u64); + break; + + case EFI_IFR_TYPE_STRING: + CopyMem (Result, &Value, sizeof (EFI_HII_VALUE)); + return EFI_SUCCESS; + + case EFI_IFR_TYPE_BOOLEAN: + break; + + default: + return EFI_UNSUPPORTED; + } + + Result->Type = EFI_IFR_TYPE_STRING; + //Result->Value.string = NewString (String, FormSet->HiiHandle); + return EFI_SUCCESS; +} + +/** + Evaluate opcode EFI_IFR_TO_UINT. + + @param FormSet Formset which contains this opcode. + @param Result Evaluation result for this opcode. + + @retval EFI_SUCCESS Opcode evaluation success. + @retval Other Opcode evaluation failed. + +**/ +EFI_STATUS +IfrToUint ( + IN FORM_BROWSER_FORMSET *FormSet, + OUT EFI_HII_VALUE *Result + ) +{ + EFI_STATUS Status; + EFI_HII_VALUE Value; + CHAR16 *String; + CHAR16 *StringPtr; + + Status = PopExpression (&Value); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Value.Type >= EFI_IFR_TYPE_OTHER) { + return EFI_UNSUPPORTED; + } + + Status = EFI_SUCCESS; + if (Value.Type == EFI_IFR_TYPE_STRING) { + String = GetToken (Value.Value.string, FormSet->UnicodeBinary); + if (String == NULL) { + return EFI_NOT_FOUND; + } + + IfrStrToUpper (String); + StringPtr = StrStr (String, L"0X"); + if (StringPtr != NULL) { + // + // Hex string + // + Result->Value.u64 = FceStrHexToUint64 (String); + } else { + // + // decimal string + // + Result->Value.u64 = FceStrDecimalToUint64 (String); + } + FreePool (String); + } else { + CopyMem (Result, &Value, sizeof (EFI_HII_VALUE)); + } + + Result->Type = EFI_IFR_TYPE_NUM_SIZE_64; + return Status; +} + +/** + Evaluate opcode EFI_IFR_CATENATE. + + @param FormSet Formset which contains this opcode. + @param Result Evaluation result for this opcode. + + @retval EFI_SUCCESS Opcode evaluation success. + @retval Other Opcode evaluation failed. + +**/ +EFI_STATUS +IfrCatenate ( + IN FORM_BROWSER_FORMSET *FormSet, + OUT EFI_HII_VALUE *Result + ) +{ + EFI_STATUS Status; + EFI_HII_VALUE Value; + CHAR16 *String[2]; + UINTN Index; + CHAR16 *StringPtr; + UINTN Size; + + // + // String[0] - The second string + // String[1] - The first string + // + String[0] = NULL; + String[1] = NULL; + StringPtr = NULL; + Status = EFI_SUCCESS; + + for (Index = 0; Index < 2; Index++) { + Status = PopExpression (&Value); + if (EFI_ERROR (Status)) { + goto Done; + } + + if (Value.Type != EFI_IFR_TYPE_STRING) { + Status = EFI_UNSUPPORTED; + goto Done; + } + + String[Index] = GetToken (Value.Value.string, FormSet->UnicodeBinary); + if (String[Index] == NULL) { + Status = EFI_NOT_FOUND; + goto Done; + } + } + + Size = FceStrSize (String[0]); + StringPtr= AllocatePool (FceStrSize (String[1]) + Size); + ASSERT (StringPtr != NULL); + StrCpy (StringPtr, String[1]); + StrCat (StringPtr, String[0]); + + Result->Type = EFI_IFR_TYPE_STRING; + //Result->Value.string = NewString (StringPtr, FormSet->HiiHandle); + +Done: + if (String[0] != NULL) { + FreePool (String[0]); + } + if (String[1] != NULL) { + FreePool (String[1]); + } + if (StringPtr != NULL) { + FreePool (StringPtr); + } + + return Status; +} + +/** + Evaluate opcode EFI_IFR_MATCH. + + @param FormSet Formset which contains this opcode. + @param Result Evaluation result for this opcode. + + @retval EFI_SUCCESS Opcode evaluation success. + @retval Other Opcode evaluation failed. + +**/ +EFI_STATUS +IfrMatch ( + IN FORM_BROWSER_FORMSET *FormSet, + OUT EFI_HII_VALUE *Result + ) +{ + EFI_STATUS Status; + EFI_HII_VALUE Value; + CHAR16 *String[2]; + UINTN Index; + + // + // String[0] - The string to search + // String[1] - pattern + // + String[0] = NULL; + String[1] = NULL; + Status = EFI_SUCCESS; + for (Index = 0; Index < 2; Index++) { + Status = PopExpression (&Value); + if (EFI_ERROR (Status)) { + goto Done; + } + + if (Value.Type != EFI_IFR_TYPE_STRING) { + Status = EFI_UNSUPPORTED; + goto Done; + } + + String[Index] = GetToken (Value.Value.string, FormSet->UnicodeBinary); + if (String [Index] == NULL) { + Status = EFI_NOT_FOUND; + goto Done; + } + } + + Result->Type = EFI_IFR_TYPE_BOOLEAN; + Result->Value.b = MetaiMatch (String[0], String[1]); + +Done: + if (String[0] != NULL) { + FreePool (String[0]); + } + if (String[1] != NULL) { + FreePool (String[1]); + } + + return Status; +} + + +/** + Evaluate opcode EFI_IFR_FIND. + + @param FormSet Formset which contains this opcode. + @param Format Case sensitive or insensitive. + @param Result Evaluation result for this opcode. + + @retval EFI_SUCCESS Opcode evaluation success. + @retval Other Opcode evaluation failed. + +**/ +EFI_STATUS +IfrFind ( + IN FORM_BROWSER_FORMSET *FormSet, + IN UINT8 Format, + OUT EFI_HII_VALUE *Result + ) +{ + EFI_STATUS Status; + EFI_HII_VALUE Value; + CHAR16 *String[2]; + UINTN Base; + CHAR16 *StringPtr; + UINTN Index; + + if (Format > EFI_IFR_FF_CASE_INSENSITIVE) { + return EFI_UNSUPPORTED; + } + + Status = PopExpression (&Value); + if (EFI_ERROR (Status)) { + return Status; + } + if (Value.Type > EFI_IFR_TYPE_NUM_SIZE_64) { + return EFI_UNSUPPORTED; + } + Base = (UINTN) Value.Value.u64; + + // + // String[0] - sub-string + // String[1] - The string to search + // + String[0] = NULL; + String[1] = NULL; + for (Index = 0; Index < 2; Index++) { + Status = PopExpression (&Value); + if (EFI_ERROR (Status)) { + goto Done; + } + + if (Value.Type != EFI_IFR_TYPE_STRING) { + Status = EFI_UNSUPPORTED; + goto Done; + } + + String[Index] = GetToken (Value.Value.string, FormSet->UnicodeBinary); + if (String[Index] == NULL) { + Status = EFI_NOT_FOUND; + goto Done; + } + + if (Format == EFI_IFR_FF_CASE_INSENSITIVE) { + // + // Case insensitive, convert both string to upper case + // + IfrStrToUpper (String[Index]); + } + } + + Result->Type = EFI_IFR_TYPE_NUM_SIZE_64; + if (Base >= FceStrLen (String[1])) { + Result->Value.u64 = 0xFFFFFFFFFFFFFFFFULL; + } else { + StringPtr = StrStr (String[1] + Base, String[0]); + Result->Value.u64 = (StringPtr == NULL) ? 0xFFFFFFFFFFFFFFFFULL : (StringPtr - String[1]); + } + +Done: + if (String[0] != NULL) { + FreePool (String[0]); + } + if (String[1] != NULL) { + FreePool (String[1]); + } + + return Status; +} + + +/** + Evaluate opcode EFI_IFR_MID. + + @param FormSet Formset which contains this opcode. + @param Result Evaluation result for this opcode. + + @retval EFI_SUCCESS Opcode evaluation success. + @retval Other Opcode evaluation failed. + +**/ +EFI_STATUS +IfrMid ( + IN FORM_BROWSER_FORMSET *FormSet, + OUT EFI_HII_VALUE *Result + ) +{ + EFI_STATUS Status; + EFI_HII_VALUE Value; + CHAR16 *String; + UINTN Base; + UINTN Length; + CHAR16 *SubString; + + Status = PopExpression (&Value); + if (EFI_ERROR (Status)) { + return Status; + } + if (Value.Type > EFI_IFR_TYPE_NUM_SIZE_64) { + return EFI_UNSUPPORTED; + } + Length = (UINTN) Value.Value.u64; + + Status = PopExpression (&Value); + if (EFI_ERROR (Status)) { + return Status; + } + if (Value.Type > EFI_IFR_TYPE_NUM_SIZE_64) { + return EFI_UNSUPPORTED; + } + Base = (UINTN) Value.Value.u64; + + Status = PopExpression (&Value); + if (EFI_ERROR (Status)) { + return Status; + } + if (Value.Type != EFI_IFR_TYPE_STRING) { + return EFI_UNSUPPORTED; + } + String = GetToken (Value.Value.string, FormSet->UnicodeBinary); + if (String == NULL) { + return EFI_NOT_FOUND; + } + + if ((Length == 0) || (Base >= FceStrLen (String))) { + SubString = gEmptyString; + } else { + SubString = String + Base; + if ((Base + Length) < FceStrLen (String)) { + SubString[Length] = L'\0'; + } + } + + Result->Type = EFI_IFR_TYPE_STRING; + //Result->Value.string = NewString (SubString, FormSet->HiiHandle); + + FreePool (String); + + return Status; +} + +/** + Evaluate opcode EFI_IFR_TOKEN. + + @param FormSet Formset which contains this opcode. + @param Result Evaluation result for this opcode. + + @retval EFI_SUCCESS Opcode evaluation success. + @retval Other Opcode evaluation failed. + +**/ +EFI_STATUS +IfrToken ( + IN FORM_BROWSER_FORMSET *FormSet, + OUT EFI_HII_VALUE *Result + ) +{ + EFI_STATUS Status; + EFI_HII_VALUE Value; + CHAR16 *String[2]; + UINTN Count; + CHAR16 *Delimiter; + CHAR16 *SubString; + CHAR16 *StringPtr; + UINTN Index; + + Status = PopExpression (&Value); + if (EFI_ERROR (Status)) { + return Status; + } + if (Value.Type > EFI_IFR_TYPE_NUM_SIZE_64) { + return EFI_UNSUPPORTED; + } + Count = (UINTN) Value.Value.u64; + + // + // String[0] - Delimiter + // String[1] - The string to search + // + String[0] = NULL; + String[1] = NULL; + for (Index = 0; Index < 2; Index++) { + Status = PopExpression (&Value); + if (EFI_ERROR (Status)) { + goto Done; + } + + if (Value.Type != EFI_IFR_TYPE_STRING) { + Status = EFI_UNSUPPORTED; + goto Done; + } + + String[Index] = GetToken (Value.Value.string, FormSet->UnicodeBinary); + if (String[Index] == NULL) { + Status = EFI_NOT_FOUND; + goto Done; + } + } + + Delimiter = String[0]; + SubString = String[1]; + while (Count > 0) { + SubString = StrStr (SubString, Delimiter); + if (SubString != NULL) { + // + // Skip over the delimiter + // + SubString = SubString + FceStrLen (Delimiter); + } else { + break; + } + Count--; + } + + if (SubString == NULL) { + // + // nth delimited sub-string not found, push an empty string + // + SubString = gEmptyString; + } else { + // + // Put a NULL terminator for nth delimited sub-string + // + StringPtr = StrStr (SubString, Delimiter); + if (StringPtr != NULL) { + *StringPtr = L'\0'; + } + } + + Result->Type = EFI_IFR_TYPE_STRING; + //Result->Value.string = NewString (SubString, FormSet->HiiHandle); + +Done: + if (String[0] != NULL) { + FreePool (String[0]); + } + if (String[1] != NULL) { + FreePool (String[1]); + } + + return Status; +} + + +/** + Evaluate opcode EFI_IFR_SPAN. + + @param FormSet Formset which contains this opcode. + @param Flags FIRST_MATCHING or FIRST_NON_MATCHING. + @param Result Evaluation result for this opcode. + + @retval EFI_SUCCESS Opcode evaluation success. + @retval Other Opcode evaluation failed. + +**/ +EFI_STATUS +IfrSpan ( + IN FORM_BROWSER_FORMSET *FormSet, + IN UINT8 Flags, + OUT EFI_HII_VALUE *Result + ) +{ + EFI_STATUS Status; + EFI_HII_VALUE Value; + CHAR16 *String[2]; + CHAR16 *Charset; + UINTN Base; + UINTN Index; + CHAR16 *StringPtr; + BOOLEAN Found; + + Status = PopExpression (&Value); + if (EFI_ERROR (Status)) { + return Status; + } + if (Value.Type > EFI_IFR_TYPE_NUM_SIZE_64) { + return EFI_UNSUPPORTED; + } + Base = (UINTN) Value.Value.u64; + + // + // String[0] - Charset + // String[1] - The string to search + // + String[0] = NULL; + String[1] = NULL; + for (Index = 0; Index < 2; Index++) { + Status = PopExpression (&Value); + if (EFI_ERROR (Status)) { + goto Done; + } + + if (Value.Type != EFI_IFR_TYPE_STRING) { + Status = EFI_UNSUPPORTED; + goto Done; + } + + String[Index] = GetToken (Value.Value.string, FormSet->UnicodeBinary); + if (String [Index] == NULL) { + Status = EFI_NOT_FOUND; + goto Done; + } + } + + if (Base >= FceStrLen (String[1])) { + Status = EFI_UNSUPPORTED; + goto Done; + } + + Found = FALSE; + StringPtr = String[1] + Base; + Charset = String[0]; + while (*StringPtr != 0 && !Found) { + Index = 0; + while (Charset[Index] != 0) { + if ((*StringPtr >= Charset[Index]) && (*StringPtr <= Charset[Index + 1])) { + if (Flags == EFI_IFR_FLAGS_FIRST_MATCHING) { + Found = TRUE; + break; + } + } else { + if (Flags == EFI_IFR_FLAGS_FIRST_NON_MATCHING) { + Found = TRUE; + break; + } + } + // + // Skip characters pair representing low-end of a range and high-end of a range + // + Index += 2; + } + + if (!Found) { + StringPtr++; + } + } + + Result->Type = EFI_IFR_TYPE_NUM_SIZE_64; + Result->Value.u64 = StringPtr - String[1]; + +Done: + if (String[0] != NULL) { + FreePool (String[0]); + } + if (String[1] != NULL) { + FreePool (String[1]); + } + + return Status; +} + + +/** + Zero extend integer/boolean/date/time to UINT64 for comparing. + + @param Value HII Value to be converted. + +**/ +VOID +ExtendValueToU64 ( + IN EFI_HII_VALUE *Value + ) +{ + UINT64 Temp; + + Temp = 0; + switch (Value->Type) { + case EFI_IFR_TYPE_NUM_SIZE_8: + Temp = Value->Value.u8; + break; + + case EFI_IFR_TYPE_NUM_SIZE_16: + Temp = Value->Value.u16; + break; + + case EFI_IFR_TYPE_NUM_SIZE_32: + Temp = Value->Value.u32; + break; + + case EFI_IFR_TYPE_BOOLEAN: + Temp = Value->Value.b; + break; + + case EFI_IFR_TYPE_TIME: + Temp = Value->Value.u32 & 0xffffff; + break; + + case EFI_IFR_TYPE_DATE: + Temp = Value->Value.u32; + break; + + default: + return; + } + + Value->Value.u64 = Temp; +} + + +/** + Compare two Hii value. + + @param Value1 Expression value to compare on left-hand. + @param Value2 Expression value to compare on right-hand. + @param FormSet The pointer to the Formset. + + @retval EFI_INVALID_PARAMETER Could not perform compare on two values. + @retval 0 Two operators equal. + @return Positive value if Value1 is greater than Value2. + @retval Negative value if Value1 is less than Value2. + +**/ +INTN +CompareHiiValue ( + IN EFI_HII_VALUE *Value1, + IN EFI_HII_VALUE *Value2, + IN FORM_BROWSER_FORMSET *FormSet + ) +{ + INTN Result; + INT64 Temp64; + CHAR16 *Str1; + CHAR16 *Str2; + + if ((Value1->Type >= EFI_IFR_TYPE_OTHER) || (Value2->Type >= EFI_IFR_TYPE_OTHER) ) { + return EFI_INVALID_PARAMETER; + } + + if ((Value1->Type == EFI_IFR_TYPE_STRING) || (Value2->Type == EFI_IFR_TYPE_STRING) ) { + if (Value1->Type != Value2->Type) { + // + // Both Operator should be type of String + // + return EFI_INVALID_PARAMETER; + } + + if ((Value1->Value.string == 0) || (Value2->Value.string == 0)) { + // + // StringId 0 is reserved + // + return EFI_INVALID_PARAMETER; + } + + if (Value1->Value.string == Value2->Value.string) { + return 0; + } + + Str1 = GetToken (Value1->Value.string, FormSet->UnicodeBinary); + if (Str1 == NULL) { + // + // String not found + // + return EFI_INVALID_PARAMETER; + } + + Str2 = GetToken (Value2->Value.string, FormSet->UnicodeBinary); + if (Str2 == NULL) { + FreePool (Str1); + return EFI_INVALID_PARAMETER; + } + + Result = FceStrCmp (Str1, Str2); + + FreePool (Str1); + FreePool (Str2); + + return Result; + } + + // + // Take remain types(integer, boolean, date/time) as integer + // + Temp64 = (INT64) (Value1->Value.u64 - Value2->Value.u64); + if (Temp64 > 0) { + Result = 1; + } else if (Temp64 < 0) { + Result = -1; + } else { + Result = 0; + } + + return Result; +} + +/** + Tell whether this Operand is an constant Expression or not + + @param Operand Operand of an IFR OpCode. + + @retval TRUE This is an Expression OpCode. + @retval FALSE Not an Expression OpCode. + +**/ +BOOLEAN +IsConstantExpressionOpCode ( + IN UINT8 Operand + ) +{ + if ((Operand == EFI_IFR_EQ_ID_VAL_OP) || + (Operand == EFI_IFR_EQ_ID_ID_OP) || + (Operand == EFI_IFR_EQ_ID_VAL_LIST_OP )|| + (Operand == EFI_IFR_QUESTION_REF1_OP) || + (Operand == EFI_IFR_QUESTION_REF2_OP) || + (Operand == EFI_IFR_QUESTION_REF3_OP) || + (Operand == EFI_IFR_THIS_OP ) || + (Operand == EFI_IFR_SECURITY_OP) || + (Operand == EFI_IFR_GET_OP) || + (Operand == EFI_IFR_SET_OP) + ) { + return FALSE; + } else { + return TRUE; + } +} + +/** + Update the HiiValue of question from its variable. + + @param FormSet FormSet associated with this expression. + @param Question The pointer to the Question + + @return EFI_SUCCESS + @return EFI_NOT_FOUND +**/ +EFI_STATUS +UpdateHiiValue ( + IN FORM_BROWSER_FORMSET *FormSet, + IN FORM_BROWSER_STATEMENT *Question + ) +{ + EFI_STATUS Status; + FORMSET_STORAGE *VarList; + UINT8 *VarBuffer; + EFI_HII_VALUE *HiiValue; + + Status = EFI_SUCCESS; + HiiValue = &Question->HiiValue; + + Status = SearchVarStorage ( + Question, + NULL, + Question->VarStoreInfo.VarOffset, + FormSet->StorageListHead, + (CHAR8 **)&VarBuffer, + &VarList + ); + if (EFI_ERROR(Status)) { + return Status; + } + if (Question->QuestionReferToBitField) { + GetBitsQuestionValue (Question, VarBuffer, &HiiValue->Value.u32); + } else { + CopyMem (&HiiValue->Value.u64, VarBuffer, Question->StorageWidth); + } + return Status; +} +/** + Evaluate the result of a HII expression. + + If Expression is NULL, then ASSERT. + + @param FormSet FormSet associated with this expression. + @param Form Form associated with this expression. + @param Expression Expression to be evaluated. + @param ConstantExpression The pointer to the flag of constant expression. If constant, will return TRUE. + + @retval EFI_SUCCESS The expression evaluated successfuly + @retval EFI_NOT_FOUND The Question which referenced by a QuestionId + could not be found. + @retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the + stack. + @retval EFI_ACCESS_DENIED The pop operation underflowed the stack + @retval EFI_INVALID_PARAMETER Syntax error with the Expression + +**/ +EFI_STATUS +EvaluateExpression ( + IN FORM_BROWSER_FORMSET *FormSet, + IN FORM_BROWSER_FORM *Form, + IN OUT FORM_EXPRESSION *Expression, + IN OUT BOOLEAN *ConstantExpression + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Link; + EXPRESSION_OPCODE *OpCode; + FORM_BROWSER_STATEMENT *Question; + FORM_BROWSER_STATEMENT *Question2; + UINT16 Index; + EFI_HII_VALUE Data1; + EFI_HII_VALUE Data2; + EFI_HII_VALUE Data3; + FORM_EXPRESSION *RuleExpression; + EFI_HII_VALUE *Value; + INTN Result; + CHAR16 *StrPtr; + UINT32 TempValue; + LIST_ENTRY *SubExpressionLink; + FORM_EXPRESSION *SubExpression; + UINTN StackOffset; + UINTN TempLength; + CHAR16 TempStr[5]; + UINT8 DigitUint8; + UINT8 *TempBuffer; + + // + // Save current stack offset. + // + StackOffset = SaveExpressionEvaluationStackOffset (); + + ASSERT (Expression != NULL); + Expression->Result.Type = EFI_IFR_TYPE_OTHER; + + Link = GetFirstNode (&Expression->OpCodeListHead); + while (!IsNull (&Expression->OpCodeListHead, Link)) { + OpCode = EXPRESSION_OPCODE_FROM_LINK (Link); + + Link = GetNextNode (&Expression->OpCodeListHead, Link); + + ZeroMem (&Data1, sizeof (EFI_HII_VALUE)); + ZeroMem (&Data2, sizeof (EFI_HII_VALUE)); + ZeroMem (&Data3, sizeof (EFI_HII_VALUE)); + + Value = &Data3; + Value->Type = EFI_IFR_TYPE_BOOLEAN; + Status = EFI_SUCCESS; + + // + // Check whether it is a constant expression or not + // + if (*ConstantExpression) { + *ConstantExpression = IsConstantExpressionOpCode (OpCode->Operand); + } + + switch (OpCode->Operand) { + // + // Built-in functions + // + case EFI_IFR_EQ_ID_VAL_OP: + Question = IdToQuestion (FormSet, Form, OpCode->QuestionId); + if (Question == NULL) { + Status = EFI_NOT_FOUND; + goto Done; + } + UpdateHiiValue (FormSet, Question); + Result = CompareHiiValue (&Question->HiiValue, &OpCode->Value, FormSet); + if ((EFI_STATUS)Result == EFI_INVALID_PARAMETER) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + Value->Value.b = (BOOLEAN) ((Result == 0) ? TRUE : FALSE); + break; + + case EFI_IFR_EQ_ID_ID_OP: + Question = IdToQuestion (FormSet, Form, OpCode->QuestionId); + if (Question == NULL) { + Status = EFI_NOT_FOUND; + goto Done; + } + + Question2 = IdToQuestion (FormSet, Form, OpCode->QuestionId2); + if (Question2 == NULL) { + Status = EFI_NOT_FOUND; + goto Done; + } + UpdateHiiValue (FormSet, Question); + UpdateHiiValue (FormSet, Question2); + Result = CompareHiiValue (&Question->HiiValue, &Question2->HiiValue, FormSet); + if ((EFI_STATUS)Result == EFI_INVALID_PARAMETER) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + Value->Value.b = (BOOLEAN) ((Result == 0) ? TRUE : FALSE); + break; + + case EFI_IFR_EQ_ID_VAL_LIST_OP: + + Question = IdToQuestion (FormSet, Form, OpCode->QuestionId); + if (Question == NULL) { + Status = EFI_NOT_FOUND; + goto Done; + } + UpdateHiiValue (FormSet, Question); + Value->Value.b = FALSE; + for (Index =0; Index < OpCode->ListLength; Index++) { + if (Question->HiiValue.Value.u16 == OpCode->ValueList[Index]) { + Value->Value.b = TRUE; + break; + } + } + break; + + case EFI_IFR_DUP_OP: + Status = PopExpression (Value); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = PushExpression (Value); + break; + + case EFI_IFR_QUESTION_REF1_OP: + case EFI_IFR_THIS_OP: + Question = IdToQuestion (FormSet, Form, OpCode->QuestionId); + if (Question == NULL) { + Status = EFI_NOT_FOUND; + goto Done; + } + UpdateHiiValue (FormSet, Question); + Value = &Question->HiiValue; + break; + + case EFI_IFR_SECURITY_OP: + // + // Do nothing, as no need for static scaning + // + break; + + case EFI_IFR_GET_OP: + // + // Get Value from VarStore buffer, EFI VarStore, Name/Value VarStore. + // + Value->Type = EFI_IFR_TYPE_UNDEFINED; + Value->Value.u8 = 0; + if (OpCode->VarStorage != NULL) { + switch (OpCode->VarStorage->Type) { + case EFI_IFR_VARSTORE_OP: + // + // Get value from Buffer + // + Value->Type = OpCode->ValueType; + CopyMem (&Value->Value, OpCode->VarStorage->Buffer + OpCode->VarStoreInfo.VarOffset, OpCode->ValueWidth); + break; + + case EFI_IFR_VARSTORE_EFI_OP: + // + // Get value from Buffer + // + if (OpCode->VarStorage->NewEfiVarstore) { + Value->Type = OpCode->ValueType; + CopyMem (&Value->Value, OpCode->VarStorage->Buffer + OpCode->VarStoreInfo.VarOffset, OpCode->ValueWidth); + } else { + CopyMem (&Value->Value, OpCode->VarStorage->Buffer, OpCode->ValueWidth); + } + + + break; + case EFI_HII_VARSTORE_NAME_VALUE: + if (OpCode->ValueType != EFI_IFR_TYPE_STRING) { + // + // Get value from string except for STRING value. + // + Status = GetValueByName (OpCode->VarStorage, OpCode->ValueName, &StrPtr); + if (!EFI_ERROR (Status)) { + ASSERT (StrPtr != NULL); + TempLength = FceStrLen (StrPtr); + if (OpCode->ValueWidth >= ((TempLength + 1) / 2)) { + Value->Type = OpCode->ValueType; + TempBuffer = (UINT8 *) &Value->Value; + ZeroMem (TempStr, sizeof (TempStr)); + for (Index = 0; Index < TempLength; Index ++) { + TempStr[0] = StrPtr[TempLength - Index - 1]; + DigitUint8 = (UINT8) FceStrHexToUint64 (TempStr); + if ((Index & 1) == 0) { + TempBuffer [Index/2] = DigitUint8; + } else { + TempBuffer [Index/2] = (UINT8) ((DigitUint8 << 4) + TempBuffer [Index/2]); + } + } + } + free (StrPtr); + StrPtr = NULL; + } + } + break; + default: + // + // Not recognize storage. + // + Status = EFI_UNSUPPORTED; + goto Done; + } + } + + break; + + case EFI_IFR_QUESTION_REF3_OP: + if (OpCode->DevicePath == 0) { + // + // EFI_IFR_QUESTION_REF3 + // Pop an expression from the expression stack + // + Status = PopExpression (Value); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Validate the expression value + // + if ((Value->Type > EFI_IFR_TYPE_NUM_SIZE_64) || (Value->Value.u64 > 0xffff)) { + Status = EFI_NOT_FOUND; + goto Done; + } + + Question = IdToQuestion (FormSet, Form, Value->Value.u16); + if (Question == NULL) { + Status = EFI_NOT_FOUND; + goto Done; + } + + // + // push the questions' value on to the expression stack + // + Value = &Question->HiiValue; + } else { + // + // BUGBUG: push 0 for EFI_IFR_QUESTION_REF3_2 and EFI_IFR_QUESTION_REF3_3, + // since it is impractical to evaluate the value of a Question in another + // Hii Package list. + // + ZeroMem (Value, sizeof (EFI_HII_VALUE)); + } + break; + + case EFI_IFR_RULE_REF_OP: + // + // Find expression for this rule + // + RuleExpression = RuleIdToExpression (Form, OpCode->RuleId); + if (RuleExpression == NULL) { + Status = EFI_NOT_FOUND; + goto Done; + } + + // + // Evaluate this rule expression + // + Status = EvaluateExpression (FormSet, Form, RuleExpression, ConstantExpression); + if (EFI_ERROR (Status)) { + goto Done; + } + + Value = &RuleExpression->Result; + break; + + case EFI_IFR_STRING_REF1_OP: + Value->Type = EFI_IFR_TYPE_STRING; + Value->Value.string = OpCode->Value.Value.string; + break; + + // + // Constant + // + case EFI_IFR_TRUE_OP: + case EFI_IFR_FALSE_OP: + case EFI_IFR_ONE_OP: + case EFI_IFR_ONES_OP: + case EFI_IFR_UINT8_OP: + case EFI_IFR_UINT16_OP: + case EFI_IFR_UINT32_OP: + case EFI_IFR_UINT64_OP: + case EFI_IFR_UNDEFINED_OP: + case EFI_IFR_VERSION_OP: + case EFI_IFR_ZERO_OP: + Value = &OpCode->Value; + break; + + // + // unary-op + // + case EFI_IFR_LENGTH_OP: + Status = PopExpression (Value); + if (EFI_ERROR (Status)) { + goto Done; + } + if (Value->Type != EFI_IFR_TYPE_STRING) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + StrPtr = GetToken (Value->Value.string, FormSet->UnicodeBinary); + if (StrPtr == NULL) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + Value->Type = EFI_IFR_TYPE_NUM_SIZE_64; + Value->Value.u64 = FceStrLen (StrPtr); + FreePool (StrPtr); + break; + + case EFI_IFR_NOT_OP: + Status = PopExpression (Value); + if (EFI_ERROR (Status)) { + goto Done; + } + if (Value->Type != EFI_IFR_TYPE_BOOLEAN) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + Value->Value.b = (BOOLEAN) (!Value->Value.b); + break; + + case EFI_IFR_QUESTION_REF2_OP: + // + // Pop an expression from the expression stack + // + Status = PopExpression (Value); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Validate the expression value + // + if ((Value->Type > EFI_IFR_TYPE_NUM_SIZE_64) || (Value->Value.u64 > 0xffff)) { + Status = EFI_NOT_FOUND; + goto Done; + } + + Question = IdToQuestion (FormSet, Form, Value->Value.u16); + if (Question == NULL) { + Status = EFI_NOT_FOUND; + goto Done; + } + + Value = &Question->HiiValue; + break; + + case EFI_IFR_STRING_REF2_OP: + // + // Pop an expression from the expression stack + // + Status = PopExpression (Value); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Validate the expression value + // + if ((Value->Type > EFI_IFR_TYPE_NUM_SIZE_64) || (Value->Value.u64 > 0xffff)) { + Status = EFI_NOT_FOUND; + goto Done; + } + + Value->Type = EFI_IFR_TYPE_STRING; + StrPtr = GetToken (Value->Value.u16, FormSet->UnicodeBinary); + if (StrPtr == NULL) { + // + // If String not exit, push an empty string + // + //Value->Value.string = NewString (gEmptyString, FormSet->HiiHandle); + } else { + Index = (UINT16) Value->Value.u64; + Value->Value.string = Index; + FreePool (StrPtr); + } + break; + + case EFI_IFR_TO_BOOLEAN_OP: + // + // Pop an expression from the expression stack + // + Status = PopExpression (Value); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Convert an expression to a Boolean + // + if (Value->Type <= EFI_IFR_TYPE_DATE) { + // + // When converting from an unsigned integer, zero will be converted to + // FALSE and any other value will be converted to TRUE. + // + Value->Value.b = (BOOLEAN) (Value->Value.u64 != 0); + + Value->Type = EFI_IFR_TYPE_BOOLEAN; + } else if (Value->Type == EFI_IFR_TYPE_STRING) { + // + // When converting from a string, if case-insensitive compare + // with "true" is True, then push True. If a case-insensitive compare + // with "false" is True, then push False. Otherwise, push Undefined. + // + StrPtr = GetToken (Value->Value.string, FormSet->UnicodeBinary); + if (StrPtr == NULL) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + IfrStrToUpper (StrPtr); + if (FceStrCmp (StrPtr, L"TRUE") == 0){ + Value->Value.b = TRUE; + } else if (FceStrCmp (StrPtr, L"FALSE") == 0) { + Value->Value.b = FALSE; + } else { + Status = EFI_INVALID_PARAMETER; + FreePool (StrPtr); + goto Done; + } + FreePool (StrPtr); + Value->Type = EFI_IFR_TYPE_BOOLEAN; + } + break; + + case EFI_IFR_TO_STRING_OP: + //Status = IfrToString (FormSet, OpCode->Format, Value); + break; + + case EFI_IFR_TO_UINT_OP: + Status = IfrToUint (FormSet, Value); + break; + + case EFI_IFR_TO_LOWER_OP: + case EFI_IFR_TO_UPPER_OP: + + Status = PopExpression (Value); + if (EFI_ERROR (Status)) { + goto Done; + } + + if (Value->Type != EFI_IFR_TYPE_STRING) { + Status = EFI_UNSUPPORTED; + goto Done; + } + + StrPtr = GetToken (Value->Value.string, FormSet->UnicodeBinary); + if (StrPtr == NULL) { + Status = EFI_NOT_FOUND; + goto Done; + } + // + // Do nothing here, as these two Opcode are to change or update the String Package + // + FreePool (StrPtr); + break; + + case EFI_IFR_BITWISE_NOT_OP: + // + // Pop an expression from the expression stack + // + Status = PopExpression (Value); + if (EFI_ERROR (Status)) { + goto Done; + } + if (Value->Type > EFI_IFR_TYPE_DATE) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + Value->Type = EFI_IFR_TYPE_NUM_SIZE_64; + Value->Value.u64 = ~Value->Value.u64; + break; + + case EFI_IFR_SET_OP: + // + // Pop an expression from the expression stack + // + Status = PopExpression (Value); + if (EFI_ERROR (Status)) { + goto Done; + } + Data1.Type = EFI_IFR_TYPE_BOOLEAN; + Data1.Value.b = FALSE; + // + // Not support SetOpcode for static scaning + // + if (OpCode->VarStorage != NULL) { + switch (OpCode->VarStorage->Type) { + + case EFI_IFR_VARSTORE_OP: + CopyMem (OpCode->VarStorage->Buffer + OpCode->VarStoreInfo.VarOffset, &Value->Value, OpCode->ValueWidth); + Data1.Value.b = TRUE; + break; + case EFI_IFR_VARSTORE_EFI_OP: + if (OpCode->VarStorage->NewEfiVarstore) { + CopyMem (OpCode->VarStorage->Buffer + OpCode->VarStoreInfo.VarOffset, &Value->Value, OpCode->ValueWidth); + Data1.Value.b = TRUE; + } else { + CopyMem (OpCode->VarStorage->Buffer, &Value->Value, OpCode->ValueWidth); + Data1.Value.b = TRUE; + } + break; + case EFI_HII_VARSTORE_NAME_VALUE: + + break; + break; + default: + // + // Not recognize storage. + // + Status = EFI_UNSUPPORTED; + goto Done; + break; + } + Value = &Data1; + } + break; + + // + // binary-op + // + case EFI_IFR_ADD_OP: + case EFI_IFR_SUBTRACT_OP: + case EFI_IFR_MULTIPLY_OP: + case EFI_IFR_DIVIDE_OP: + case EFI_IFR_MODULO_OP: + case EFI_IFR_BITWISE_AND_OP: + case EFI_IFR_BITWISE_OR_OP: + case EFI_IFR_SHIFT_LEFT_OP: + case EFI_IFR_SHIFT_RIGHT_OP: + // + // Pop an expression from the expression stack + // + Status = PopExpression (&Data2); + if (EFI_ERROR (Status)) { + goto Done; + } + if (Data2.Type > EFI_IFR_TYPE_DATE) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + // + // Pop another expression from the expression stack + // + Status = PopExpression (&Data1); + if (EFI_ERROR (Status)) { + goto Done; + } + if (Data1.Type > EFI_IFR_TYPE_DATE) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + Value->Type = EFI_IFR_TYPE_NUM_SIZE_64; + + switch (OpCode->Operand) { + case EFI_IFR_ADD_OP: + Value->Value.u64 = Data1.Value.u64 + Data2.Value.u64; + break; + + case EFI_IFR_SUBTRACT_OP: + Value->Value.u64 = Data1.Value.u64 - Data2.Value.u64; + break; + + case EFI_IFR_MULTIPLY_OP: + Value->Value.u64 = MultU64x32 (Data1.Value.u64, (UINT32) Data2.Value.u64); + break; + + case EFI_IFR_DIVIDE_OP: + Value->Value.u64 = DivU64x32 (Data1.Value.u64, (UINT32) Data2.Value.u64); + break; + + case EFI_IFR_MODULO_OP: + DivU64x32Remainder (Data1.Value.u64, (UINT32) Data2.Value.u64, &TempValue); + Value->Value.u64 = TempValue; + break; + + case EFI_IFR_BITWISE_AND_OP: + Value->Value.u64 = Data1.Value.u64 & Data2.Value.u64; + break; + + case EFI_IFR_BITWISE_OR_OP: + Value->Value.u64 = Data1.Value.u64 | Data2.Value.u64; + break; + + case EFI_IFR_SHIFT_LEFT_OP: + Value->Value.u64 = LShiftU64 (Data1.Value.u64, (UINTN) Data2.Value.u64); + break; + + case EFI_IFR_SHIFT_RIGHT_OP: + Value->Value.u64 = RShiftU64 (Data1.Value.u64, (UINTN) Data2.Value.u64); + break; + + default: + break; + } + break; + + case EFI_IFR_AND_OP: + case EFI_IFR_OR_OP: + // + // Two Boolean operator + // + Status = PopExpression (&Data2); + if (EFI_ERROR (Status)) { + goto Done; + } + if (Data2.Type != EFI_IFR_TYPE_BOOLEAN) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + // + // Pop another expression from the expression stack + // + Status = PopExpression (&Data1); + if (EFI_ERROR (Status)) { + goto Done; + } + if (Data1.Type != EFI_IFR_TYPE_BOOLEAN) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + if (OpCode->Operand == EFI_IFR_AND_OP) { + Value->Value.b = (BOOLEAN) (Data1.Value.b && Data2.Value.b); + } else { + Value->Value.b = (BOOLEAN) (Data1.Value.b || Data2.Value.b); + } + break; + + case EFI_IFR_EQUAL_OP: + case EFI_IFR_NOT_EQUAL_OP: + case EFI_IFR_GREATER_EQUAL_OP: + case EFI_IFR_GREATER_THAN_OP: + case EFI_IFR_LESS_EQUAL_OP: + case EFI_IFR_LESS_THAN_OP: + // + // Compare two integer, string, boolean or date/time + // + Status = PopExpression (&Data2); + if (EFI_ERROR (Status)) { + goto Done; + } + if ((Data2.Type > EFI_IFR_TYPE_BOOLEAN) && (Data2.Type != EFI_IFR_TYPE_STRING)) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + // + // Pop another expression from the expression stack + // + Status = PopExpression (&Data1); + if (EFI_ERROR (Status)) { + goto Done; + } + + Result = CompareHiiValue (&Data1, &Data2, FormSet); + if ((EFI_STATUS)Result == EFI_INVALID_PARAMETER) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + switch (OpCode->Operand) { + case EFI_IFR_EQUAL_OP: + Value->Value.b = (BOOLEAN) ((Result == 0) ? TRUE : FALSE); + break; + + case EFI_IFR_NOT_EQUAL_OP: + Value->Value.b = (BOOLEAN) ((Result != 0) ? TRUE : FALSE); + break; + + case EFI_IFR_GREATER_EQUAL_OP: + Value->Value.b = (BOOLEAN) ((Result >= 0) ? TRUE : FALSE); + break; + + case EFI_IFR_GREATER_THAN_OP: + Value->Value.b = (BOOLEAN) ((Result > 0) ? TRUE : FALSE); + break; + + case EFI_IFR_LESS_EQUAL_OP: + Value->Value.b = (BOOLEAN) ((Result <= 0) ? TRUE : FALSE); + break; + + case EFI_IFR_LESS_THAN_OP: + Value->Value.b = (BOOLEAN) ((Result < 0) ? TRUE : FALSE); + break; + + default: + break; + } + break; + + case EFI_IFR_MATCH_OP: + Status = IfrMatch (FormSet, Value); + break; + + case EFI_IFR_CATENATE_OP: + Status = IfrCatenate (FormSet, Value); + break; + + // + // ternary-op + // + case EFI_IFR_CONDITIONAL_OP: + // + // Pop third expression from the expression stack + // + Status = PopExpression (&Data3); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Pop second expression from the expression stack + // + Status = PopExpression (&Data2); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Pop first expression from the expression stack + // + Status = PopExpression (&Data1); + if (EFI_ERROR (Status)) { + goto Done; + } + if (Data1.Type != EFI_IFR_TYPE_BOOLEAN) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + if (Data1.Value.b) { + Value = &Data3; + } else { + Value = &Data2; + } + break; + + case EFI_IFR_FIND_OP: + Status = IfrFind (FormSet, OpCode->Format, Value); + break; + + case EFI_IFR_MID_OP: + Status = IfrMid (FormSet, Value); + break; + + case EFI_IFR_TOKEN_OP: + Status = IfrToken (FormSet, Value); + break; + + case EFI_IFR_SPAN_OP: + Status = IfrSpan (FormSet, OpCode->Flags, Value); + break; + + case EFI_IFR_MAP_OP: + // + // Pop the check value + // + Status = PopExpression (&Data1); + if (EFI_ERROR (Status)) { + goto Done; + } + // + // Check MapExpression list is valid. + // + if (OpCode->MapExpressionList.ForwardLink == NULL) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + // + // Go through map expression list. + // + SubExpressionLink = GetFirstNode(&OpCode->MapExpressionList); + while (!IsNull (&OpCode->MapExpressionList, SubExpressionLink)) { + SubExpression = FORM_EXPRESSION_FROM_LINK (SubExpressionLink); + // + // Evaluate the first expression in this pair. + // + Status = EvaluateExpression (FormSet, Form, SubExpression, ConstantExpression); + if (EFI_ERROR (Status)) { + goto Done; + } + // + // Compare the expression value with current value + // + if (CompareHiiValue (&Data1, &SubExpression->Result, FormSet) == 0) { + // + // Try get the map value. + // + SubExpressionLink = GetNextNode (&OpCode->MapExpressionList, SubExpressionLink); + if (IsNull (&OpCode->MapExpressionList, SubExpressionLink)) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + SubExpression = FORM_EXPRESSION_FROM_LINK (SubExpressionLink); + Status = EvaluateExpression (FormSet, Form, SubExpression, ConstantExpression); + if (EFI_ERROR (Status)) { + goto Done; + } + Value = &SubExpression->Result; + break; + } + // + // Skip the second expression on this pair. + // + SubExpressionLink = GetNextNode (&OpCode->MapExpressionList, SubExpressionLink); + if (IsNull (&OpCode->MapExpressionList, SubExpressionLink)) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + // + // Goto the first expression on next pair. + // + SubExpressionLink = GetNextNode (&OpCode->MapExpressionList, SubExpressionLink); + } + + // + // No map value is found. + // + if (IsNull (&OpCode->MapExpressionList, SubExpressionLink)) { + Value->Type = EFI_IFR_TYPE_UNDEFINED; + Value->Value.u8 = 0; + } + break; + + default: + break; + } + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = PushExpression (Value); + if (EFI_ERROR (Status)) { + goto Done; + } + } + + // + // Pop the final result from expression stack + // + Value = &Data1; + Status = PopExpression (Value); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // After evaluating an expression, there should be only one value left on the expression stack + // + if (PopExpression (Value) != EFI_ACCESS_DENIED) { + Status = EFI_INVALID_PARAMETER; + } + +Done: + RestoreExpressionEvaluationStackOffset (StackOffset); + if (!EFI_ERROR (Status)) { + CopyMem (&Expression->Result, Value, sizeof (EFI_HII_VALUE)); + } + + return Status; +} diff --git a/BaseTools/Source/C/FCE/Fce.c b/BaseTools/Source/C/FCE/Fce.c new file mode 100644 index 0000000000..1845c508b5 --- /dev/null +++ b/BaseTools/Source/C/FCE/Fce.c @@ -0,0 +1,6449 @@ +/** @file + + FCE is a tool which enables developers to retrieve and change HII configuration ("Setup") + data in Firmware Device files (".fd" files). + + Copyright (c) 2011-2019, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Fce.h" + +#ifndef __GNUC__ +#define COPY_STR "copy \"%s\" \"%s\" > NUL" +#define RMDIR_STR "rmdir /S /Q \"%s\" > NUL" +#define DEL_STR "del \"%s\" > NUL" +#else +#define COPY_STR "cp \"%s\" \"%s\" > /dev/null" +#define RMDIR_STR "rm -r \"%s\" > /dev/null" +#define DEL_STR "rm \"%s\" > /dev/null" +#endif + +// +// Utility global variables +// +OPERATION_TYPE Operations; + +CHAR8 mInputFdName[MAX_FILENAME_LEN]; +CHAR8 mOutputFdName[MAX_FILENAME_LEN]; +CHAR8 mOutTxtName[MAX_FILENAME_LEN]; +CHAR8 mSetupTxtName[MAX_FILENAME_LEN]; + +CHAR8* mUtilityFilename = NULL; +UINT32 mEfiVariableAddr = 0; + +UQI_PARAM_LIST *mUqiList = NULL; +UQI_PARAM_LIST *mLastUqiList = NULL; +LIST_ENTRY mVarListEntry; +LIST_ENTRY mBfvVarListEntry; +LIST_ENTRY mAllVarListEntry; +LIST_ENTRY mFormSetListEntry; + +// +// Store GUIDed Section guid->tool mapping +// +EFI_HANDLE mParsedGuidedSectionTools = NULL; + +CHAR8* mGuidToolDefinition = "GuidToolDefinitionConf.ini"; + +// +//gFfsArray is used to store all the FFS informations of Fd +// +G_EFI_FD_INFO gEfiFdInfo; +// +//mMultiPlatformParam is used to store the structures about multi-platform support +// +MULTI_PLATFORM_PARAMETERS mMultiPlatformParam; + +UINT32 mFormSetOrderRead; +UINT32 mFormSetOrderParse; + +CHAR8 mFullGuidToolDefinitionDir[_MAX_PATH]; + +CHAR8 *mFvNameGuidString = NULL; + +/** + Check the revision of BfmLib. If not matched, return an error. + + @retval EFI_SUCCESS If revision matched, return EFI_SUCCESS. + @retval EFI_UNSUPPORTED Other cases. +**/ +static +EFI_STATUS +CheckBfmLibRevision ( + VOID + ) +{ + CHAR8 *SystemCommandFormatString; + CHAR8 *SystemCommand; + CHAR8 *TempSystemCommand; + CHAR8 *Revision; + FILE *FileHandle; + CHAR8 RevisionStr[_MAX_BUILD_VERSION]; + UINT32 Len; + + SystemCommandFormatString = NULL; + SystemCommand = NULL; + TempSystemCommand = NULL; + Revision = "Revision.txt"; + + memset (RevisionStr, 0, _MAX_BUILD_VERSION); + + // + // Construction 'system' command string + // + SystemCommandFormatString = "BfmLib -v > %s"; + SystemCommand = malloc ( + strlen (SystemCommandFormatString) + strlen (Revision) + 1 + ); + if (SystemCommand == NULL) { + return EFI_UNSUPPORTED; + } + sprintf ( + SystemCommand, + "BfmLib -v > %s", + Revision + ); + + if (mFullGuidToolDefinitionDir[0] != 0) { + TempSystemCommand = SystemCommand; + SystemCommand = malloc ( + strlen (mFullGuidToolDefinitionDir) + strlen (OS_SEP_STR) + strlen (SystemCommandFormatString) + strlen (Revision) + 1 + ); + + if (SystemCommand == NULL) { + free (TempSystemCommand); + return EFI_UNSUPPORTED; + } + strcpy (SystemCommand, mFullGuidToolDefinitionDir); + strcat (SystemCommand, OS_SEP_STR); + strcat (SystemCommand, TempSystemCommand); + free (TempSystemCommand); + } + + system (SystemCommand); + free (SystemCommand); + FileHandle = fopen (Revision, "r"); + if (FileHandle == NULL) { + printf ("Error. Read the revision file of BfmLib failed.\n"); + return EFI_ABORTED; + } + fgets(RevisionStr, _MAX_BUILD_VERSION, FileHandle); + Len = strlen(RevisionStr); + if (RevisionStr[Len - 1] == '\n') { + RevisionStr[Len - 1] = 0; + } + fclose (FileHandle); + remove (Revision); + + if (strlen (RevisionStr) > _MAX_BUILD_VERSION - 1) { + printf ("The revision string is too long"); + return EFI_UNSUPPORTED; + } + if (strcmp (RevisionStr, __BUILD_VERSION) == 0) { + return EFI_SUCCESS; + } + return EFI_UNSUPPORTED; +} + +/** + Transfer the Ascii string to the Dec Number + + @param InStr The Ascii string. + + @retval DecNum Return the Dec number. +**/ +static +UINT64 +StrToDec ( + IN CHAR8 *InStr + ) +{ + UINT8 Index; + UINTN DecNum; + UINTN Temp; + + Index = 0; + DecNum = 0; + Temp = 0; + + if (InStr == NULL) { + return (UINT64)-1; + } + while (Index < strlen (InStr)) { + + if ((*(InStr + Index) >= '0') && (*(InStr + Index) <= '9')) { + Temp = *(InStr + Index) - '0'; + } else if ((*(InStr + Index) >= 'a') && (*(InStr + Index) <= 'f')) { + Temp = *(InStr + Index) - 'a' + 10; + } else if ((*(InStr + Index) >= 'A') && (*(InStr + Index) <= 'F')) { + Temp = *(InStr + Index) - 'A' + 10; + } + DecNum = DecNum + Temp * (UINTN) pow (16, strlen (InStr) - Index - 1); + Index++; + } + return DecNum; +} +/** + Check whether there are some errors in uqi parameters of One_of + + @param Question The pointer to the question Node. + @param UqiValue The value of One_of. + + @retval TRUE If existed error, return TRUE; + @return FALSE Otherwise, return FALSE; +**/ +static +BOOLEAN +CheckOneOfParamError ( + IN CONST FORM_BROWSER_STATEMENT *Question, + IN UINT64 UqiValue + ) +{ + LIST_ENTRY *Link; + QUESTION_OPTION *Option; + UINT64 Value; + + Link = NULL; + Option = NULL; + Value = 0; + + Link = GetFirstNode (&Question->OptionListHead); + while (!IsNull (&Question->OptionListHead, Link)) { + Option = QUESTION_OPTION_FROM_LINK (Link); + Value = 0; + CopyMem (&Value, &Option->Value.Value.u64, Question->StorageWidth); + if (Value == UqiValue) { + return FALSE; + } + Link = GetNextNode (&Question->OptionListHead, Link); + } + + return TRUE; +} + +/** + Check whether there are some errors in uqi parameters of Orderlist + + @param HiiObjList The pointer to the Hii Object Node. + @param UqiValue The pointer to the Uqi parameter. + + @retval TRUE If existed error, return TRUE; + @return FALSE Otherwise, return FALSE; +**/ +static +BOOLEAN +CheckOrderParamError ( + IN CONST FORM_BROWSER_STATEMENT *Question, + IN CHAR8 *UqiValue + ) +{ + UINT8 MaxNum; + MaxNum = UqiValue[0]; + + if (MaxNum != (UINT8)(Question->MaxContainers)) { + return TRUE; + } + + return FALSE; +} + +/** + Writes a Unicode string + + @param Package A pointer to the Unicode string. + + @return NULL +**/ +static +VOID +WriteUnicodeStr ( + IN CHAR16 *pcString + ) +{ + UINTN Index; + + if (pcString == NULL) { + return; + } + + for (Index = 0; pcString[Index] != 0; Index++) { + printf("%c", pcString[Index] & 0x00FF); + } +} + +/** + Parse and set the quick configure information by the command line. + + Read the UQI Config information from command line directly, and then compare it with the value in VarList. + Update the Update flag in Varlist. + + @param CurUqiList The pointer to the current uqi + @param DefaultId The default Id. + @param PlatformId The platform Id. + @param CurQuestion The pointer to the matched question + + @retval EFI_SUCCESS It was complete successfully + @retval EFI_UNSUPPORTED Update a read-only value + @return EFI_ABORTED An error occurred +**/ +static +EFI_STATUS +SetUqiParametersWithQuestion ( + IN UQI_PARAM_LIST *CurUqiList, + IN UINT16 DefaultId, + IN UINT64 PlatformId, + IN FORM_BROWSER_STATEMENT *CurQuestion + ) +{ + FORMSET_STORAGE *VarList; + BOOLEAN IsFound; + UINT8 *ValueAddrOfVar; + UINT16 Index; + UINT64 QuestionValue; + UINT64 UqiValue; + EFI_STATUS Status; + CHAR8 *ErrorStr; + FORM_BROWSER_FORMSET *FormSet; + LIST_ENTRY *FormSetLink; + UINT64 CurValue; + + VarList = NULL; + Index = 0; + ValueAddrOfVar = NULL; + QuestionValue = 0; + UqiValue = 0; + ErrorStr = NULL; + Status = EFI_SUCCESS; + FormSet = NULL; + FormSetLink = NULL; + CurValue = 0; + + // + // Search the Variable List by VarStoreId and Offset + // + IsFound = FALSE; + FormSetLink = GetFirstNode (&mFormSetListEntry); + while (!IsNull (&mFormSetListEntry, FormSetLink)) { + FormSet = FORM_BROWSER_FORMSET_FROM_LINK (FormSetLink); + Status = SearchVarStorage ( + CurQuestion, + NULL, + CurQuestion->VarStoreInfo.VarOffset, + FormSet->StorageListHead, + (CHAR8 **) &ValueAddrOfVar, + &VarList + ); + + if (!EFI_ERROR (Status)) { + IsFound = TRUE; + break; + } + FormSetLink = GetNextNode (&mFormSetListEntry, FormSetLink); + } + + if (!IsFound) { + if (CurUqiList->Header.ScriptsLine == 0) { + printf ("Error. The question in the command line doesn't store value by EFI_VARSTORE_IFR or EFI_VARSTORE_IFR_EFI.\n"); + } else { + printf ("Error. The question in the line %d of script doesn't store value by EFI_VARSTORE_IFR or EFI_VARSTORE_IFR_EFI.\n", CurUqiList->Header.ScriptsLine); + } + return EFI_ABORTED; + } + + // + // Check the length of variable value + // + if (CurQuestion->QuestionReferToBitField) { + GetBitsQuestionValue (CurQuestion, ValueAddrOfVar, (UINT32*)&QuestionValue); + } else { + switch (CurQuestion->StorageWidth) { + + case sizeof (UINT8): + QuestionValue = *(UINT8 *)ValueAddrOfVar; + break; + + case sizeof (UINT16): + QuestionValue = *(UINT16 *)ValueAddrOfVar; + break; + + case sizeof (UINT32): + QuestionValue = *(UINT32 *)ValueAddrOfVar; + break; + + case sizeof (UINT64): + QuestionValue = *(UINT64 *)ValueAddrOfVar; + break; + + default: + // + // The storage width of ORDERED_LIST may not be any type above. + // + ; + break; + } + } + UqiValue = *(UINT64 *)CurUqiList->Header.Value; + CurUqiList->SameOrNot = TRUE; + + // + // Check and set the checkbox value + // + if (CurQuestion->Operand == EFI_IFR_CHECKBOX_OP) { + if ((UqiValue != 0) && (UqiValue != 1)) { + CurUqiList->ErrorOrNot = TRUE; + CurUqiList->SameOrNot = FALSE; + CurUqiList->Error = malloc (ERROR_INFO_LENGTH * sizeof (CHAR8)); + if (CurUqiList->Error == NULL) { + printf ("Fail to allocate memory!\n"); + return EFI_ABORTED; + } + memset (CurUqiList->Error, 0, ERROR_INFO_LENGTH); + sprintf (CurUqiList->Error, "Error. The updated value of CHECKBOX 0x%llx is invalid.\n", (unsigned long long) UqiValue); + if (CurQuestion->QuestionReferToBitField) { + GetBitsQuestionValue (CurQuestion, ValueAddrOfVar, (UINT32*)CurUqiList->Header.DiffValue); + } else { + memcpy ( + CurUqiList->Header.DiffValue, + ValueAddrOfVar, + CurQuestion->StorageWidth + ); + } + } else { + if (QuestionValue != UqiValue) { + if (CurQuestion->QuestionReferToBitField) { + GetBitsQuestionValue (CurQuestion, ValueAddrOfVar, (UINT32*)CurUqiList->Header.DiffValue); + SetBitsQuestionValue (CurQuestion, ValueAddrOfVar, *(UINT32*)CurUqiList->Header.Value); + } else { + memcpy ( + CurUqiList->Header.DiffValue, + ValueAddrOfVar, + CurQuestion->StorageWidth + ); + memcpy ( + ValueAddrOfVar, + CurUqiList->Header.Value, + CurQuestion->StorageWidth + ); + } + CurUqiList->SameOrNot = FALSE; + } + } + } + if (CurQuestion->Operand == EFI_IFR_STRING_OP) { + if ((FceStrLen((wchar_t *)CurUqiList->Header.Value) + 1) * 2 > CurQuestion->StorageWidth) { + CurUqiList->ErrorOrNot = TRUE; + CurUqiList->Error = malloc (ERROR_INFO_LENGTH * sizeof (CHAR8)); + if (CurUqiList->Error == NULL) { + printf ("Fail to allocate memory!\n"); + return EFI_ABORTED; + } + memset (CurUqiList->Error, 0, ERROR_INFO_LENGTH); + sprintf (CurUqiList->Error, "Error. The updated value of STRING exceed its Size.\n"); + memcpy ( + CurUqiList->Header.DiffValue, + ValueAddrOfVar, + CurQuestion->StorageWidth + ); + } else { + if (memcmp((CHAR8 *)CurUqiList->Header.Value, ValueAddrOfVar, CurQuestion->StorageWidth) != 0) { + memcpy( + CurUqiList->Header.DiffValue, + ValueAddrOfVar, + CurQuestion->StorageWidth + ); + memcpy( + ValueAddrOfVar, + CurUqiList->Header.Value, + CurQuestion->StorageWidth + ); + CurUqiList->SameOrNot = FALSE; + } + } + } + // + // Check and set the NUMERIC value + // + if (CurQuestion->Operand == EFI_IFR_NUMERIC_OP) { + if ((UqiValue < CurQuestion->Minimum) || (UqiValue > CurQuestion->Maximum)) { + CurUqiList->ErrorOrNot = TRUE; + CurUqiList->SameOrNot = FALSE; + CurUqiList->Error = malloc (ERROR_INFO_LENGTH * sizeof (CHAR8)); + if (CurUqiList->Error == NULL) { + printf ("Fail to allocate memory!\n"); + return EFI_ABORTED; + } + memset (CurUqiList->Error, 0, ERROR_INFO_LENGTH); + sprintf (CurUqiList->Error, "Error. The updated value of NUMERIC 0x%llx is invalid.\n", (unsigned long long) UqiValue); + if (CurQuestion->QuestionReferToBitField) { + GetBitsQuestionValue (CurQuestion, ValueAddrOfVar, (UINT32*)CurUqiList->Header.DiffValue); + } else { + memcpy ( + CurUqiList->Header.DiffValue, + ValueAddrOfVar, + CurQuestion->StorageWidth + ); + } + } else { + if (QuestionValue != UqiValue) { + if (CurQuestion->QuestionReferToBitField) { + GetBitsQuestionValue (CurQuestion, ValueAddrOfVar, (UINT32*)CurUqiList->Header.DiffValue); + SetBitsQuestionValue (CurQuestion, ValueAddrOfVar, *(UINT32*)CurUqiList->Header.Value); + } else { + memcpy ( + CurUqiList->Header.DiffValue, + ValueAddrOfVar, + CurQuestion->StorageWidth + ); + memcpy ( + ValueAddrOfVar, + CurUqiList->Header.Value, + CurQuestion->StorageWidth + ); + } + CurUqiList->SameOrNot = FALSE; + } + } + } + // + // Check and set the ONE_OF value + // + if (CurQuestion->Operand == EFI_IFR_ONE_OF_OP) { + if (CheckOneOfParamError (CurQuestion, UqiValue)) { + CurUqiList->ErrorOrNot = TRUE; + CurUqiList->SameOrNot = FALSE; + CurUqiList->Error = malloc (ERROR_INFO_LENGTH * sizeof (CHAR8)); + if (CurUqiList->Error == NULL) { + printf ("Fail to allocate memory!\n"); + return EFI_ABORTED; + } + memset (CurUqiList->Error, 0, ERROR_INFO_LENGTH); + sprintf (CurUqiList->Error, "Error. The updated ONE_OF value 0x%llx is invalid.\n", (unsigned long long) UqiValue); + if (CurQuestion->QuestionReferToBitField) { + GetBitsQuestionValue (CurQuestion, ValueAddrOfVar, (UINT32*)CurUqiList->Header.DiffValue); + } else { + memcpy ( + CurUqiList->Header.DiffValue, + ValueAddrOfVar, + CurQuestion->StorageWidth + ); + } + } else { + if (QuestionValue != UqiValue) { + if (CurQuestion->QuestionReferToBitField) { + GetBitsQuestionValue (CurQuestion, ValueAddrOfVar, (UINT32*)CurUqiList->Header.DiffValue); + SetBitsQuestionValue (CurQuestion, ValueAddrOfVar, *(UINT32*)CurUqiList->Header.Value); + } else { + memcpy ( + CurUqiList->Header.DiffValue, + ValueAddrOfVar, + CurQuestion->StorageWidth + ); + memcpy ( + ValueAddrOfVar, + CurUqiList->Header.Value, + CurQuestion->StorageWidth + ); + } + CurUqiList->SameOrNot = FALSE; + } + } + } + // + // Check and set the ORDER_LIST value + // + if (CurQuestion->Operand == EFI_IFR_ORDERED_LIST_OP) { + // + // Synchronize the MaxContainers value + // + *CurUqiList->Header.DiffValue = (UINT8)CurQuestion->MaxContainers; + + if (CheckOrderParamError (CurQuestion, (CHAR8 *)CurUqiList->Header.Value)) { + + CurUqiList->ErrorOrNot = TRUE; + CurUqiList->SameOrNot = FALSE; + CurUqiList->Error = malloc (ERROR_INFO_LENGTH * sizeof (CHAR8)); + if (CurUqiList->Error == NULL) { + printf ("Fail to allocate memory!\n"); + return EFI_ABORTED; + } + memset (CurUqiList->Error, 0, ERROR_INFO_LENGTH); + + ErrorStr = CurUqiList->Error; + sprintf (ErrorStr, "Error. The updated NORDERED_LIST value "); + ErrorStr = ErrorStr + strlen ("Error. The updated NORDERED_LIST value "); + + for (Index = 0; Index < CurQuestion->StorageWidth; Index++) { + sprintf (ErrorStr, "%02x ", *(CurUqiList->Header.Value + Index + 1)); + ErrorStr +=3; + } + sprintf (ErrorStr, "is invalid.\n"); + memcpy ( + CurUqiList->Header.DiffValue + 1, + ValueAddrOfVar, + CurQuestion->StorageWidth + ); + } else { + for (Index = 0; Index < CurQuestion->StorageWidth/CurQuestion->MaxContainers; Index++) { + CurValue = 0; + memcpy ( + &CurValue, + (ValueAddrOfVar + Index * CurQuestion->StorageWidth/CurQuestion->MaxContainers), + CurQuestion->StorageWidth/CurQuestion->MaxContainers + ); + if (CurValue != *((UINT64 *)CurUqiList->Header.Value + Index + 1)) { + CurUqiList->SameOrNot = FALSE; + break; + } + } + if (!CurUqiList->SameOrNot) { + memcpy ( + CurUqiList->Header.DiffValue + 1, + ValueAddrOfVar, + CurQuestion->StorageWidth + ); + for (Index = 0; Index < CurQuestion->MaxContainers; Index++) { + switch (CurQuestion->StorageWidth/CurQuestion->MaxContainers) { + + case sizeof (UINT8): + *((UINT8 *)ValueAddrOfVar + Index) = *(UINT8 *)((UINT64 *)CurUqiList->Header.Value + 1 + Index); + break; + + case sizeof (UINT16): + *((UINT16 *)ValueAddrOfVar + Index) = *(UINT16 *)((UINT64 *)CurUqiList->Header.Value + 1 + Index); + break; + + case sizeof (UINT32): + *((UINT32 *)ValueAddrOfVar + Index) = *(UINT32 *)((UINT64 *)CurUqiList->Header.Value + 1 + Index); + break; + + case sizeof (UINT64): + *((UINT64 *)ValueAddrOfVar + Index) = *((UINT64 *)CurUqiList->Header.Value + 1 + Index); + break; + + default: + *((UINT8 *)ValueAddrOfVar + Index) = *(UINT8 *)((UINT64 *)CurUqiList->Header.Value + 1 + Index); + break; + } + } + // + // Update the vaule of ORDERED_LIST according to its size + // + CurUqiList->Header.Value = (UINT8 *)((UINT64 *)CurUqiList->Header.Value); + memcpy ( + CurUqiList->Header.Value + 1, + ValueAddrOfVar, + CurQuestion->StorageWidth + ); + } + } + } + + return EFI_SUCCESS; +} + +/** + Parse and set the quick configure information by the command line. + + Read the UQI Config information from command line directly, and then compare it with the value in VarList. + Update the Update flag in Varlist. + + @param UqiList The pointer to the uqi list + @param DefaultId The default Id. + @param PlatformId The platform Id. + + @retval EFI_SUCCESS It was complete successfully + @retval EFI_UNSUPPORTED Update a read-only value + @return EFI_ABORTED An error occurred +**/ +static +EFI_STATUS +SetUqiParameters ( + IN UQI_PARAM_LIST *UqiList, + IN UINT16 DefaultId, + IN UINT64 PlatformId + ) +{ + FORM_BROWSER_FORMSET *FormSet; + LIST_ENTRY *FormSetLink; + FORM_BROWSER_FORM *Form; + LIST_ENTRY *FormLink; + FORM_BROWSER_STATEMENT *Question; + LIST_ENTRY *QuestionLink; + LIST_ENTRY *FormSetEntryListHead; + UQI_PARAM_LIST *CurUqiList; + + FormSet = NULL; + FormSetLink = NULL; + Form = NULL; + FormLink = NULL; + Question = NULL; + QuestionLink = NULL; + FormSetEntryListHead = &mFormSetListEntry; + + FormSetLink = GetFirstNode (FormSetEntryListHead); + while (!IsNull (FormSetEntryListHead, FormSetLink)) { + FormSet = FORM_BROWSER_FORMSET_FROM_LINK (FormSetLink); + // + // Parse all forms in formset + // + FormLink = GetFirstNode (&FormSet->FormListHead); + + while (!IsNull (&FormSet->FormListHead, FormLink)) { + Form = FORM_BROWSER_FORM_FROM_LINK (FormLink); + // + // Parse five kinds of Questions in Form + // + QuestionLink = GetFirstNode (&Form->StatementListHead); + + while (!IsNull (&Form->StatementListHead, QuestionLink)) { + Question = FORM_BROWSER_STATEMENT_FROM_LINK (QuestionLink); + QuestionLink = GetNextNode (&Form->StatementListHead, QuestionLink); + // + // Parse five kinds of Questions in Form + // + if ((Question->Operand == EFI_IFR_ONE_OF_OP) + || (Question->Operand == EFI_IFR_NUMERIC_OP) + || (Question->Operand == EFI_IFR_CHECKBOX_OP) + || (Question->Operand == EFI_IFR_ORDERED_LIST_OP) + || (Question->Operand == EFI_IFR_STRING_OP) + ) { + if (mMultiPlatformParam.MultiPlatformOrNot) { + // + // Only compare the valid EFI_IFR_VARSTORE_EFI_OP in multi-platform mode + // + if (Question->Type != EFI_IFR_VARSTORE_EFI_OP) { + continue; + } + if ((Question->Type == EFI_IFR_VARSTORE_EFI_OP) + && Question->NewEfiVarstore + && ((Question->Attributes & EFI_VARIABLE_NON_VOLATILE) == 0)) { + continue; + } + } + + CurUqiList = UqiList; + while (CurUqiList != NULL) { + if ((PlatformId == CurUqiList->Header.PlatformId[0]) + && (DefaultId == CurUqiList->Header.DefaultId[0]) + && CompareUqiHeader (&(Question->Uqi), &(CurUqiList->Header)) + ) { + // + // Add further check to avoid a case that there are many options with a + // same UQI (en-Us), but always returns the first one. + // + if (!CurUqiList->ParseOrNot) { + CurUqiList->ParseOrNot = TRUE; + break; + } + } + CurUqiList = CurUqiList->Next; + } + if (CurUqiList != NULL) { + SetUqiParametersWithQuestion (CurUqiList, DefaultId, PlatformId, Question); + } + } + } + FormLink = GetNextNode (&FormSet->FormListHead, FormLink); + } + FormSetLink = GetNextNode (FormSetEntryListHead, FormSetLink); + } + + return EFI_SUCCESS; +} + +/** + Set question value per UqiList. + + @param UqiList The pointer to the uqi list + @param DefaultId The default Id. + @param PlatformId The platform Id. +**/ +VOID +SetUqiParametersMultiMode ( + IN UQI_PARAM_LIST *UqiList, + IN UINT16 DefaultId, + IN UINT64 PlatformId + ) +{ + UQI_PARAM_LIST *CurUqiList; + + CurUqiList = UqiList; + while (CurUqiList != NULL) { + if ((CurUqiList->ParseOrNot == FALSE) + && (PlatformId == CurUqiList->Header.PlatformId[0]) + && (DefaultId == CurUqiList->Header.DefaultId[0]) + ) { + CurUqiList->ParseOrNot = TRUE; + if (CurUqiList->Header.Question != NULL) { + SetUqiParametersWithQuestion (CurUqiList, DefaultId, PlatformId, CurUqiList->Header.Question); + } + } + CurUqiList = CurUqiList->Next; + } +} + +/** + Find the matched question for each UQI string in UqiList + +**/ +EFI_STATUS +ScanUqiFullList ( + IN UQI_PARAM_LIST *UqiList + ) +{ + FORM_BROWSER_FORMSET *FormSet; + LIST_ENTRY *FormSetLink; + FORM_BROWSER_FORM *Form; + LIST_ENTRY *FormLink; + FORM_BROWSER_STATEMENT *Question; + LIST_ENTRY *QuestionLink; + LIST_ENTRY *FormSetEntryListHead; + UQI_PARAM_LIST *CurUqiList; + UINT64 PlatformId; + UINT16 DefaultId; + + if (UqiList == NULL) { + return EFI_SUCCESS; + } + FormSet = NULL; + FormSetLink = NULL; + Form = NULL; + FormLink = NULL; + Question = NULL; + QuestionLink = NULL; + FormSetEntryListHead = &mFormSetListEntry; + + FormSetLink = FormSetEntryListHead->ForwardLink; + while (FormSetEntryListHead != FormSetLink) { + FormSet = FORM_BROWSER_FORMSET_FROM_LINK (FormSetLink); + // + // Parse all forms in formset + // + FormLink = FormSet->FormListHead.ForwardLink; + + while (&FormSet->FormListHead != FormLink) { + Form = FORM_BROWSER_FORM_FROM_LINK (FormLink); + // + // Parse five kinds of Questions in Form + // + QuestionLink = Form->StatementListHead.ForwardLink; + + while (&Form->StatementListHead != QuestionLink) { + Question = FORM_BROWSER_STATEMENT_FROM_LINK (QuestionLink); + QuestionLink = QuestionLink->ForwardLink; + // + // Parse five kinds of Questions in Form + // + if ((Question->Operand == EFI_IFR_ONE_OF_OP) + || (Question->Operand == EFI_IFR_NUMERIC_OP) + || (Question->Operand == EFI_IFR_CHECKBOX_OP) + || (Question->Operand == EFI_IFR_ORDERED_LIST_OP) + || (Question->Operand == EFI_IFR_STRING_OP) + ) { + // + // Only compare the valid EFI_IFR_VARSTORE_EFI_OP in multi-platform mode + // + if (Question->Type != EFI_IFR_VARSTORE_EFI_OP) { + continue; + } else if (Question->NewEfiVarstore + && ((Question->Attributes & EFI_VARIABLE_NON_VOLATILE) == 0)) { + continue; + } + + CurUqiList = UqiList; + PlatformId = 0xFFFFFFFF; + DefaultId = 0xFFFF; + while (CurUqiList != NULL) { + if ((CurUqiList->Header.Question == NULL) + && CompareUqiHeader (&(Question->Uqi), &(CurUqiList->Header)) + && ((PlatformId != CurUqiList->Header.PlatformId[0]) || (DefaultId != CurUqiList->Header.DefaultId[0])) + ) { + CurUqiList->Header.Question = Question; + PlatformId = CurUqiList->Header.PlatformId[0]; + DefaultId = CurUqiList->Header.DefaultId[0]; + } + CurUqiList = CurUqiList->Next; + } + } + } + FormLink = FormLink->ForwardLink; + } + FormSetLink = FormSetLink->ForwardLink; + } + + return EFI_SUCCESS; +} + +/** + Create and insert the UQI Node to the UQI parameter list. + + @retval Node address If successed, return the node address. + @retval NULL An error occurred. + +**/ +static +UQI_PARAM_LIST * +CreateInsertUqiNode ( + ) +{ + UQI_PARAM_LIST *Node; + + Node = (UQI_PARAM_LIST *) malloc (sizeof (UQI_PARAM_LIST)); + if (Node == NULL) { + return NULL; + } + memset (Node, 0, sizeof (UQI_PARAM_LIST)); + + if (mUqiList == NULL) { + mUqiList = Node; + } else { + mLastUqiList->Next = Node; + } + + mLastUqiList = Node; + return Node; +} +/** + Parse the first part of QUI string + + @param **argv[] The dual array pointer to the parameters. + @param Number The pointer to the number of the first character of UQI. + @param Buffer The buffer to store the first part of UQI. + + @retval EFI_SUCCESS Parse the QUI parameters successfully. + @retval EFI_INVALID_PARAMETER An error occurred. + +**/ +static +EFI_STATUS +ParseFirstpartOfUqi ( + IN CHAR8 **argv[], + OUT UINT32 *Number, + OUT UINT16 **Buffer + ) +{ + UINT32 Index; + + Index = 0; + + *Number = (UINT32)StrToDec ((*argv)[0]); + + if ((*Number <= 0) || (*Number > MAX_QUI_PARAM_LEN)) { + printf ("Error. Invalid UQI.\n"); + return EFI_INVALID_PARAMETER; + } + + *Buffer = malloc ((*Number + 1) * sizeof (CHAR16)); + assert (*Buffer != NULL); + memset (*Buffer, 0, (*Number + 1) * sizeof (CHAR16)); + + for (Index = 0; Index < *Number; Index++) { + if (StrToDec ((*argv)[Index]) > 0xff) { + printf ("Error. Invalid UQI.\n"); + return EFI_INVALID_PARAMETER; + } + *(*Buffer + Index) = (UINT16)StrToDec ((*argv)[Index + 1]); + } + return EFI_SUCCESS; +} + +/** + Parse the QUI input parameters from the command line + + @param argc The pointer to the number of input parameters. + @param **argv[] The dual array pointer to the parameters. + + @retval EFI_SUCCESS Parse the QUI parameters successfully. + @retval EFI_INVALID_PARAMETER An error occurred. + +**/ +static +EFI_STATUS +ParseUqiParam ( + IN UINT32 *argc, + IN CHAR8 **argv[] + ) +{ + UINT32 Index; + UQI_PARAM_LIST *UqiNode; + EFI_STATUS Status; + + Index = 0; + UqiNode = NULL; + + if (*argc < 4) { + printf ("Error. The correct command is 'FCE updateq -i -o '.\n"); + return EFI_INVALID_PARAMETER; + } + + UqiNode = CreateInsertUqiNode (); + assert (UqiNode != NULL); + + UqiNode->Header.DefaultId = (UINT16 *) calloc (1, sizeof (UINT16)); + UqiNode->Header.PlatformId = (UINT64 *) calloc (1, sizeof (UINT64)); + + Status = ParseFirstpartOfUqi (argv, &(UqiNode->Header.HexNum), &(UqiNode->Header.Data)); + if (EFI_ERROR (Status)) { + return Status; + } + *argc -= (UqiNode->Header.HexNum + 1); + *argv += UqiNode->Header.HexNum + 1; + // + // Parse the TYPE and value + // + if (strcmp ("ONE_OF", (*argv)[0]) == 0) { + UqiNode->Header.Type = ONE_OF; + } else if (strcmp ("NUMERIC", (*argv)[0]) == 0) { + UqiNode->Header.Type = NUMERIC; + } else if (strcmp ("CHECKBOX", (*argv)[0]) == 0) { + UqiNode->Header.Type = CHECKBOX; + } else if (strcmp ("STRING", (*argv)[0]) == 0) { + UqiNode->Header.Type = STRING; + } else if (strcmp ("ORDERED_LIST", (*argv)[0]) == 0) { + UqiNode->Header.Type = ORDERED_LIST; + } else { + printf ("Error. The correct command is 'FCE updateq -i -o '.\n"); + return EFI_INVALID_PARAMETER; + } + *argc -= 1; + *argv += 1; + // + // Get the value according to the type of questions. + // + switch (UqiNode->Header.Type) { + + case ONE_OF: + case NUMERIC: + case CHECKBOX: + UqiNode->Header.Value = malloc (sizeof (UINT64)); + if (UqiNode->Header.Value == NULL) { + printf ("Fali to allocate memory!\n"); + return EFI_OUT_OF_RESOURCES; + } + memset (UqiNode->Header.Value, 0, sizeof (UINT64)); + + UqiNode->Header.DiffValue = malloc (sizeof (UINT64)); + if (UqiNode->Header.DiffValue == NULL) { + printf ("Fali to allocate memory!\n"); + return EFI_OUT_OF_RESOURCES; + } + memset ( + UqiNode->Header.DiffValue, + 0, + sizeof (UINT64) + ); + *(UINT64 *)(UqiNode->Header.Value) = (UINT64)StrToDec ((*argv)[0]); + break; + + case ORDERED_LIST: + UqiNode->Header.Value = malloc (((UINTN)StrToDec ((*argv)[0]) + 1) * sizeof (UINT64)); + if (UqiNode->Header.Value == NULL) { + printf ("Fali to allocate memory!\n"); + return EFI_OUT_OF_RESOURCES; + } + memset ( + UqiNode->Header.Value, + 0, + (UINTN)StrToDec(((*argv)[0]) + 1) * sizeof (UINT64) + ); + + UqiNode->Header.DiffValue = malloc (((UINTN)StrToDec ((*argv)[0]) + 1) * sizeof (UINT64)); + if (UqiNode->Header.DiffValue == NULL) { + printf ("Fali to allocate memory!\n"); + return EFI_OUT_OF_RESOURCES; + } + memset ( + UqiNode->Header.DiffValue, + 0, + (UINTN)(StrToDec ((*argv)[0]) + 1) * sizeof (UINT64) + ); + + *UqiNode->Header.Value = (UINT8)StrToDec ((*argv)[0]); + for (Index = 1; Index <= StrToDec ((*argv)[0]); Index++) { + *((UINT64 *)UqiNode->Header.Value + Index) = StrToDec ((*argv)[Index]); + } + *argc -= (UINTN)StrToDec ((*argv)[0]); + *argv += (UINTN)StrToDec ((*argv)[0]); + break; + + default: + break; + } + + *argc -= 1; + *argv += 1; + + if (*argc > 0) { + printf ("Error. The correct command is 'FCE updateq -i -o '.\n"); + return EFI_INVALID_PARAMETER; + } + return EFI_SUCCESS; +} +/** + Parse the input Fd file, and get the file name according to the FILETYPE. + + @param FdName The Name of Fd file + @param FILETYPE The type of Fd file + + @return EFI_SUCCESS Get the file name successfully + @return EFI_ABORTED An error occurred. +**/ +static +EFI_STATUS +ParseInFile ( + IN CHAR8 *FdName, + IN FILETYPE Type +) +{ + FILE *Fptr; + + Fptr = NULL; + + if ((Type == INFD) && ((FdName == NULL) || (Fptr = fopen (FdName, "r")) == NULL)) { + if (FdName != NULL) { + printf ("Error: The file doesn't exist '%s'\n", FdName); + } + return EFI_ABORTED; + } + if ((Type == OUTFD) && (FdName == NULL) ) { + printf ("Error: The name is NULL.\n"); + return EFI_ABORTED; + } + if ((Type == SETUPTXT) && (FdName == NULL) ) { + printf ("Error: The