/** @file
  Var Check Hii bin generation.

Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution.  The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php

THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

**/

#include "VarCheckHiiGen.h"

LIST_ENTRY mVarCheckHiiList = INITIALIZE_LIST_HEAD_VARIABLE (mVarCheckHiiList);

#define VAR_CHECK_HII_VARIABLE_NODE_SIGNATURE   SIGNATURE_32 ('V', 'C', 'H', 'V')

typedef struct {
  UINTN                         Signature;
  LIST_ENTRY                    Link;
  VAR_CHECK_HII_VARIABLE_HEADER *HiiVariable;
  EFI_VARSTORE_ID               VarStoreId;

  VAR_CHECK_HII_QUESTION_HEADER **HiiQuestionArray;
} VAR_CHECK_HII_VARIABLE_NODE;

#define VAR_CHECK_HII_VARIABLE_FROM_LINK(a) CR (a, VAR_CHECK_HII_VARIABLE_NODE, Link, VAR_CHECK_HII_VARIABLE_NODE_SIGNATURE)

CHAR16 *mVarName = NULL;
UINTN  mMaxVarNameSize = 0;

#ifdef DUMP_HII_DATA
GLOBAL_REMOVE_IF_UNREFERENCED VAR_CHECK_HII_OPCODE_STRING   mIfrOpCodeStringTable[] = {
  {EFI_IFR_VARSTORE_OP,             "EFI_IFR_VARSTORE_OP"},
  {EFI_IFR_VARSTORE_EFI_OP,         "EFI_IFR_VARSTORE_EFI_OP"},
  {EFI_IFR_ONE_OF_OP,               "EFI_IFR_ONE_OF_OP"},
  {EFI_IFR_CHECKBOX_OP,             "EFI_IFR_CHECKBOX_OP"},
  {EFI_IFR_NUMERIC_OP,              "EFI_IFR_NUMERIC_OP"},
  {EFI_IFR_ORDERED_LIST_OP,         "EFI_IFR_ORDERED_LIST_OP"},
  {EFI_IFR_ONE_OF_OPTION_OP,        "EFI_IFR_ONE_OF_OPTION_OP"},
};

/**
  Ifr opcode to string.

  @param[in] IfrOpCode  Ifr OpCode.

  @return Pointer to string.

**/
CHAR8 *
IfrOpCodeToStr (
  IN UINT8  IfrOpCode
  )
{
  UINTN  Index;
  for (Index = 0; Index < ARRAY_SIZE (mIfrOpCodeStringTable); Index++) {
    if (mIfrOpCodeStringTable[Index].HiiOpCode == IfrOpCode) {
      return mIfrOpCodeStringTable[Index].HiiOpCodeStr;
    }
  }

  return "<UnknownIfrOpCode>";
}

GLOBAL_REMOVE_IF_UNREFERENCED VAR_CHECK_HII_PACKAGE_TYPE_STRING  mPackageTypeStringTable[] = {
  {EFI_HII_PACKAGE_TYPE_ALL,            "EFI_HII_PACKAGE_TYPE_ALL"},
  {EFI_HII_PACKAGE_TYPE_GUID,           "EFI_HII_PACKAGE_TYPE_GUID"},
  {EFI_HII_PACKAGE_FORMS,               "EFI_HII_PACKAGE_FORMS"},
  {EFI_HII_PACKAGE_STRINGS,             "EFI_HII_PACKAGE_STRINGS"},
  {EFI_HII_PACKAGE_FONTS,               "EFI_HII_PACKAGE_FONTS"},
  {EFI_HII_PACKAGE_IMAGES,              "EFI_HII_PACKAGE_IMAGES"},
  {EFI_HII_PACKAGE_SIMPLE_FONTS,        "EFI_HII_PACKAGE_SIMPLE_FONTS"},
  {EFI_HII_PACKAGE_DEVICE_PATH,         "EFI_HII_PACKAGE_DEVICE_PATH"},
  {EFI_HII_PACKAGE_KEYBOARD_LAYOUT,     "EFI_HII_PACKAGE_KEYBOARD_LAYOUT"},
  {EFI_HII_PACKAGE_ANIMATIONS,          "EFI_HII_PACKAGE_ANIMATIONS"},
  {EFI_HII_PACKAGE_END,                 "EFI_HII_PACKAGE_END"},
  {EFI_HII_PACKAGE_TYPE_SYSTEM_BEGIN,   "EFI_HII_PACKAGE_TYPE_SYSTEM_BEGIN"},
  {EFI_HII_PACKAGE_TYPE_SYSTEM_END,     "EFI_HII_PACKAGE_TYPE_SYSTEM_END"},
};

/**
  Hii Package type to string.

  @param[in] PackageType    Package Type

  @return Pointer to string.

**/
CHAR8 *
HiiPackageTypeToStr (
  IN UINT8  PackageType
  )
{
  UINTN     Index;
  for (Index = 0; Index < ARRAY_SIZE (mPackageTypeStringTable); Index++) {
    if (mPackageTypeStringTable[Index].PackageType == PackageType) {
      return mPackageTypeStringTable[Index].PackageTypeStr;
    }
  }

  return "<UnknownPackageType>";
}

/**
  Dump Hii Package.

  @param[in] HiiPackage         Pointer to Hii Package.

**/
VOID
DumpHiiPackage (
  IN VOID       *HiiPackage
  )
{
  EFI_HII_PACKAGE_HEADER        *HiiPackageHeader;
  EFI_IFR_OP_HEADER             *IfrOpCodeHeader;
  EFI_IFR_VARSTORE              *IfrVarStore;
  EFI_IFR_VARSTORE_EFI          *IfrEfiVarStore;

  HiiPackageHeader = (EFI_HII_PACKAGE_HEADER *) HiiPackage;

  DEBUG ((EFI_D_INFO, "  HiiPackageHeader->Type   - 0x%02x (%a)\n", HiiPackageHeader->Type, HiiPackageTypeToStr ((UINT8) HiiPackageHeader->Type)));
  DEBUG ((EFI_D_INFO, "  HiiPackageHeader->Length - 0x%06x\n", HiiPackageHeader->Length));

  switch (HiiPackageHeader->Type) {
    case EFI_HII_PACKAGE_FORMS:
      IfrOpCodeHeader = (EFI_IFR_OP_HEADER *) (HiiPackageHeader + 1);

      while ((UINTN) IfrOpCodeHeader < ((UINTN) HiiPackageHeader + HiiPackageHeader->Length)) {
        switch (IfrOpCodeHeader->OpCode) {
          case EFI_IFR_VARSTORE_OP:
            IfrVarStore = (EFI_IFR_VARSTORE *) IfrOpCodeHeader;
            DEBUG ((EFI_D_INFO, "    IfrOpCodeHeader->OpCode - 0x%02x (%a)\n", IfrOpCodeHeader->OpCode, IfrOpCodeToStr (IfrOpCodeHeader->OpCode)));
            DEBUG ((EFI_D_INFO, "    IfrOpCodeHeader->Length - 0x%02x\n", IfrOpCodeHeader->Length));
            DEBUG ((EFI_D_INFO, "    IfrOpCodeHeader->Scope  - 0x%02x\n", IfrOpCodeHeader->Scope));
            DEBUG ((EFI_D_INFO, "      Guid       - %g\n", &IfrVarStore->Guid));
            DEBUG ((EFI_D_INFO, "      VarStoreId - 0x%04x\n", IfrVarStore->VarStoreId));
            DEBUG ((EFI_D_INFO, "      Size       - 0x%04x\n", IfrVarStore->Size));
            DEBUG ((EFI_D_INFO, "      Name       - %a\n", IfrVarStore->Name));
            break;

          case EFI_IFR_VARSTORE_EFI_OP:
            IfrEfiVarStore = (EFI_IFR_VARSTORE_EFI *) IfrOpCodeHeader;
            if (IfrEfiVarStore->Header.Length >= sizeof (EFI_IFR_VARSTORE_EFI)) {
              DEBUG ((EFI_D_INFO, "    IfrOpCodeHeader->OpCode - 0x%02x (%a)\n", IfrOpCodeHeader->OpCode, IfrOpCodeToStr (IfrOpCodeHeader->OpCode)));
              DEBUG ((EFI_D_INFO, "    IfrOpCodeHeader->Length - 0x02%x\n", IfrOpCodeHeader->Length));
              DEBUG ((EFI_D_INFO, "    IfrOpCodeHeader->Scope  - 0x02%x\n", IfrOpCodeHeader->Scope));
              DEBUG ((EFI_D_INFO, "      Guid       - %g\n", &IfrEfiVarStore->Guid));
              DEBUG ((EFI_D_INFO, "      VarStoreId - 0x%04x\n", IfrEfiVarStore->VarStoreId));
              DEBUG ((EFI_D_INFO, "      Size       - 0x%04x\n", IfrEfiVarStore->Size));
              DEBUG ((EFI_D_INFO, "      Attributes - 0x%08x\n", IfrEfiVarStore->Attributes));
              DEBUG ((EFI_D_INFO, "      Name       - %a\n", IfrEfiVarStore->Name));
            }
            break;

          case EFI_IFR_ONE_OF_OP:
          case EFI_IFR_CHECKBOX_OP:
          case EFI_IFR_NUMERIC_OP:
          case EFI_IFR_ORDERED_LIST_OP:
            DEBUG ((EFI_D_INFO, "    IfrOpCodeHeader->OpCode - 0x%02x (%a)\n", IfrOpCodeHeader->OpCode, IfrOpCodeToStr (IfrOpCodeHeader->OpCode)));
            DEBUG ((EFI_D_INFO, "    IfrOpCodeHeader->Length - 0x02%x\n", IfrOpCodeHeader->Length));
            DEBUG ((EFI_D_INFO, "    IfrOpCodeHeader->Scope  - 0x02%x\n", IfrOpCodeHeader->Scope));
            DEBUG ((EFI_D_INFO, "      Prompt       - 0x%04x\n", ((EFI_IFR_ONE_OF *) IfrOpCodeHeader)->Question.Header.Prompt));
            DEBUG ((EFI_D_INFO, "      Help         - 0x%04x\n", ((EFI_IFR_ONE_OF *) IfrOpCodeHeader)->Question.Header.Help));
            DEBUG ((EFI_D_INFO, "      QuestionId   - 0x%04x\n", ((EFI_IFR_ONE_OF *) IfrOpCodeHeader)->Question.QuestionId));
            DEBUG ((EFI_D_INFO, "      VarStoreId   - 0x%04x\n", ((EFI_IFR_ONE_OF *) IfrOpCodeHeader)->Question.VarStoreId));
            DEBUG ((EFI_D_INFO, "      VarStoreInfo - 0x%04x\n", ((EFI_IFR_ONE_OF * )IfrOpCodeHeader)->Question.VarStoreInfo.VarOffset));
            {
              EFI_IFR_ONE_OF            *IfrOneOf;
              EFI_IFR_CHECKBOX          *IfrCheckBox;
              EFI_IFR_NUMERIC           *IfrNumeric;
              EFI_IFR_ORDERED_LIST      *IfrOrderedList;

              switch (IfrOpCodeHeader->OpCode) {
                case EFI_IFR_ONE_OF_OP:
                  IfrOneOf = (EFI_IFR_ONE_OF *) IfrOpCodeHeader;
                  DEBUG ((EFI_D_INFO, "      Flags         - 0x%02x\n", IfrOneOf->Flags));
                  switch (IfrOneOf->Flags & EFI_IFR_NUMERIC_SIZE) {
                  case EFI_IFR_NUMERIC_SIZE_1:
                    DEBUG ((EFI_D_INFO, "      MinValue      - 0x%02x\n", IfrOneOf->data.u8.MinValue));
                    DEBUG ((EFI_D_INFO, "      MaxValue      - 0x%02x\n", IfrOneOf->data.u8.MaxValue));
                    DEBUG ((EFI_D_INFO, "      Step          - 0x%02x\n", IfrOneOf->data.u8.Step));
                    break;
                  case EFI_IFR_NUMERIC_SIZE_2:
                    DEBUG ((EFI_D_INFO, "      MinValue      - 0x%04x\n", IfrOneOf->data.u16.MinValue));
                    DEBUG ((EFI_D_INFO, "      MaxValue      - 0x%04x\n", IfrOneOf->data.u16.MaxValue));
                    DEBUG ((EFI_D_INFO, "      Step          - 0x%04x\n", IfrOneOf->data.u16.Step));
                    break;
                  case EFI_IFR_NUMERIC_SIZE_4:
                    DEBUG ((EFI_D_INFO, "      MinValue      - 0x%08x\n", IfrOneOf->data.u32.MinValue));
                    DEBUG ((EFI_D_INFO, "      MaxValue      - 0x%08x\n", IfrOneOf->data.u32.MaxValue));
                    DEBUG ((EFI_D_INFO, "      Step          - 0x%08x\n", IfrOneOf->data.u32.Step));
                    break;
                  case EFI_IFR_NUMERIC_SIZE_8:
                    DEBUG ((EFI_D_INFO, "      MinValue      - 0x%016lx\n", IfrOneOf->data.u64.MinValue));
                    DEBUG ((EFI_D_INFO, "      MaxValue      - 0x%016lx\n", IfrOneOf->data.u64.MaxValue));
                    DEBUG ((EFI_D_INFO, "      Step          - 0x%016lx\n", IfrOneOf->data.u64.Step));
                    break;
                  }
                  break;
                case EFI_IFR_CHECKBOX_OP:
                  IfrCheckBox = (EFI_IFR_CHECKBOX *) IfrOpCodeHeader;
                  DEBUG ((EFI_D_INFO, "      Flags         - 0x%02x\n", IfrCheckBox->Flags));
                  break;
                case EFI_IFR_NUMERIC_OP:
                  IfrNumeric = (EFI_IFR_NUMERIC *) IfrOpCodeHeader;
                  DEBUG ((EFI_D_INFO, "      Flags         - 0x%02x\n", IfrNumeric->Flags));
                  switch (IfrNumeric->Flags & EFI_IFR_NUMERIC_SIZE) {
                  case EFI_IFR_NUMERIC_SIZE_1:
                    DEBUG ((EFI_D_INFO, "      MinValue      - 0x%02x\n", IfrNumeric->data.u8.MinValue));
                    DEBUG ((EFI_D_INFO, "      MaxValue      - 0x%02x\n", IfrNumeric->data.u8.MaxValue));
                    DEBUG ((EFI_D_INFO, "      Step          - 0x%02x\n", IfrNumeric->data.u8.Step));
                    break;
                  case EFI_IFR_NUMERIC_SIZE_2:
                    DEBUG ((EFI_D_INFO, "      MinValue      - 0x%04x\n", IfrNumeric->data.u16.MinValue));
                    DEBUG ((EFI_D_INFO, "      MaxValue      - 0x%04x\n", IfrNumeric->data.u16.MaxValue));
                    DEBUG ((EFI_D_INFO, "      Step          - 0x%04x\n", IfrNumeric->data.u16.Step));
                    break;
                  case EFI_IFR_NUMERIC_SIZE_4:
                    DEBUG ((EFI_D_INFO, "      MinValue      - 0x%08x\n", IfrNumeric->data.u32.MinValue));
                    DEBUG ((EFI_D_INFO, "      MaxValue      - 0x%08x\n", IfrNumeric->data.u32.MaxValue));
                    DEBUG ((EFI_D_INFO, "      Step          - 0x%08x\n", IfrNumeric->data.u32.Step));
                    break;
                  case EFI_IFR_NUMERIC_SIZE_8:
                    DEBUG ((EFI_D_INFO, "      MinValue      - 0x%016lx\n", IfrNumeric->data.u64.MinValue));
                    DEBUG ((EFI_D_INFO, "      MaxValue      - 0x%016lx\n", IfrNumeric->data.u64.MaxValue));
                    DEBUG ((EFI_D_INFO, "      Step          - 0x%016lx\n", IfrNumeric->data.u64.Step));
                    break;
                  }
                  break;
                case EFI_IFR_ORDERED_LIST_OP:
                  IfrOrderedList = (EFI_IFR_ORDERED_LIST *) IfrOpCodeHeader;
                  DEBUG ((EFI_D_INFO, "      MaxContainers - 0x%02x\n", IfrOrderedList->MaxContainers));
                  DEBUG ((EFI_D_INFO, "      Flags         - 0x%02x\n", IfrOrderedList->Flags));
                  break;
                default:
                  break;
              }

              if (IfrOpCodeHeader->Scope != 0) {
                UINTN                       Scope;
                EFI_IFR_ONE_OF_OPTION       *IfrOneOfOption;

                IfrOpCodeHeader = (EFI_IFR_OP_HEADER *) ((UINTN) IfrOpCodeHeader + IfrOpCodeHeader->Length);
                Scope = 1;
                while (Scope != 0) {
                  switch (IfrOpCodeHeader->OpCode) {
                    case EFI_IFR_ONE_OF_OPTION_OP:
                      IfrOneOfOption = (EFI_IFR_ONE_OF_OPTION *)IfrOpCodeHeader;
                      DEBUG ((EFI_D_INFO, "!!!!    IfrOpCodeHeader->OpCode - 0x%02x (%a)\n", (UINTN)IfrOpCodeHeader->OpCode, IfrOpCodeToStr (IfrOpCodeHeader->OpCode)));
                      DEBUG ((EFI_D_INFO, "!!!!    IfrOpCodeHeader->Scope  - 0x%02x\n", IfrOpCodeHeader->Scope));
                      DEBUG ((EFI_D_INFO, "!!!!      Option                - 0x%04x\n", IfrOneOfOption->Option));
                      DEBUG ((EFI_D_INFO, "!!!!      Flags                 - 0x%02x\n", IfrOneOfOption->Flags));
                      DEBUG ((EFI_D_INFO, "!!!!      Type                  - 0x%02x\n", IfrOneOfOption->Type));
                      switch (IfrOneOfOption->Type) {
                        case EFI_IFR_TYPE_NUM_SIZE_8:
                          DEBUG ((EFI_D_INFO, "!!!!      Value                 - 0x%02x\n", IfrOneOfOption->Value.u8));
                          break;
                        case EFI_IFR_TYPE_NUM_SIZE_16:
                          DEBUG ((EFI_D_INFO, "!!!!      Value                 - 0x%04x\n", IfrOneOfOption->Value.u16));
                          break;
                        case EFI_IFR_TYPE_NUM_SIZE_32:
                          DEBUG ((EFI_D_INFO, "!!!!      Value                 - 0x%08x\n", IfrOneOfOption->Value.u32));
                          break;
                        case EFI_IFR_TYPE_NUM_SIZE_64:
                          DEBUG ((EFI_D_INFO, "!!!!      Value                 - 0x%016lx\n", IfrOneOfOption->Value.u64));
                          break;
                        case EFI_IFR_TYPE_BOOLEAN:
                          DEBUG ((EFI_D_INFO, "!!!!      Value                 - 0x%02x\n", IfrOneOfOption->Value.b));
                          break;
                        default:
                          break;
                      }
                      break;
                  }

                  if (IfrOpCodeHeader->OpCode == EFI_IFR_END_OP) {
                    ASSERT (Scope > 0);
                    Scope--;
                    if (Scope == 0) {
                      break;
                    }
                  } else if (IfrOpCodeHeader->Scope != 0) {
                    Scope++;
                  }
                  IfrOpCodeHeader = (EFI_IFR_OP_HEADER *) ((UINTN) IfrOpCodeHeader + IfrOpCodeHeader->Length);
                }
              }
            }
          default:
            break;
        }
        IfrOpCodeHeader = (EFI_IFR_OP_HEADER *) ((UINTN) IfrOpCodeHeader + IfrOpCodeHeader->Length);
      }
      break;
    default:
      break;
  }
}

/**
  Dump Hii Database.

  @param[in] HiiDatabase        Pointer to Hii Database.
  @param[in] HiiDatabaseSize    Hii Database size.

**/
VOID
DumpHiiDatabase (
  IN VOID       *HiiDatabase,
  IN UINTN      HiiDatabaseSize
  )
{
  EFI_HII_PACKAGE_LIST_HEADER   *HiiPackageListHeader;
  EFI_HII_PACKAGE_HEADER        *HiiPackageHeader;

  DEBUG ((EFI_D_INFO, "HiiDatabaseSize - 0x%x\n", HiiDatabaseSize));
  HiiPackageListHeader = (EFI_HII_PACKAGE_LIST_HEADER *) HiiDatabase;

  while ((UINTN) HiiPackageListHeader < ((UINTN) HiiDatabase + HiiDatabaseSize)) {
    DEBUG ((EFI_D_INFO, "HiiPackageListHeader->PackageListGuid - %g\n", &HiiPackageListHeader->PackageListGuid));
    DEBUG ((EFI_D_INFO, "HiiPackageListHeader->PackageLength   - 0x%x\n", (UINTN)HiiPackageListHeader->PackageLength));
    HiiPackageHeader = (EFI_HII_PACKAGE_HEADER *)(HiiPackageListHeader + 1);

    while ((UINTN) HiiPackageHeader < (UINTN) HiiPackageListHeader + HiiPackageListHeader->PackageLength) {

      DumpHiiPackage (HiiPackageHeader);

      HiiPackageHeader = (EFI_HII_PACKAGE_HEADER *) ((UINTN) HiiPackageHeader + HiiPackageHeader->Length);
    }

    HiiPackageListHeader = (EFI_HII_PACKAGE_LIST_HEADER *) ((UINTN) HiiPackageListHeader + HiiPackageListHeader->PackageLength);
  }

  return ;
}
#endif

/**
  Allocates a buffer of a certain pool type.

  Allocates the number bytes specified by AllocationSize of a certain pool type 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.

  @param  MemoryType            The type of memory to allocate.
  @param  AllocationSize        The number of bytes to allocate.

  @return A pointer to the allocated buffer or NULL if allocation fails.

**/
VOID *
InternalVarCheckAllocatePool (
  IN EFI_MEMORY_TYPE  MemoryType,
  IN UINTN            AllocationSize
  )
{
  EFI_STATUS  Status;
  VOID        *Memory;

  Status = gBS->AllocatePool (MemoryType, AllocationSize, &Memory);
  if (EFI_ERROR (Status)) {
    Memory = NULL;
  }
  return Memory;
}

/**
  Allocates and zeros a buffer of type EfiBootServicesData.

  Allocates the number bytes specified by AllocationSize of type EfiBootServicesData, clears the
  buffer with zeros, 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.

  @param  AllocationSize        The number of bytes to allocate and zero.

  @return A pointer to the allocated buffer or NULL if allocation fails.

**/
VOID *
InternalVarCheckAllocateZeroPool (
  IN UINTN            AllocationSize
  )
{
  VOID  *Memory;

  Memory = InternalVarCheckAllocatePool (EfiBootServicesData, AllocationSize);
  if (Memory != NULL) {
    Memory = ZeroMem (Memory, AllocationSize);
  }
  return Memory;
}

/**
  Frees a buffer that was previously allocated with one of the pool allocation functions in the
  Memory Allocation Library.

  Frees the buffer specified by Buffer.  Buffer must have been allocated on a previous call to the
  pool allocation services of the Memory Allocation Library.  If it is not possible to free pool
  resources, then this function will perform no actions.

  If Buffer was not allocated with a pool allocation function in the Memory Allocation Library,
  then ASSERT().

  @param  Buffer                The pointer to the buffer to free.

**/
VOID
EFIAPI
InternalVarCheckFreePool (
  IN VOID   *Buffer
  )
{
  EFI_STATUS    Status;

  Status = gBS->FreePool (Buffer);
  ASSERT_EFI_ERROR (Status);
}

/**
  Reallocates a buffer of type EfiBootServicesData.

  Allocates and zeros the number bytes specified by NewSize from memory of type
  EfiBootServicesData.  If OldBuffer is not NULL, then the smaller of OldSize and
  NewSize bytes are copied from OldBuffer to the newly allocated buffer, and
  OldBuffer is freed.  A pointer to the newly allocated buffer is returned.
  If NewSize 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 the allocation of the new buffer is successful and the smaller of NewSize and OldSize
  is greater than (MAX_ADDRESS - OldBuffer + 1), then ASSERT().

  @param  OldSize        The size, in bytes, of OldBuffer.
  @param  NewSize        The size, in bytes, of the buffer to reallocate.
  @param  OldBuffer      The buffer to copy to the allocated buffer.  This is an optional
                         parameter that may be NULL.

  @return A pointer to the allocated buffer or NULL if allocation fails.

**/
VOID *
InternalVarCheckReallocatePool (
  IN UINTN            OldSize,
  IN UINTN            NewSize,
  IN VOID             *OldBuffer  OPTIONAL
  )
{
  VOID  *NewBuffer;

  NewBuffer = InternalVarCheckAllocateZeroPool (NewSize);
  if (NewBuffer != NULL && OldBuffer != NULL) {
    CopyMem (NewBuffer, OldBuffer, MIN (OldSize, NewSize));
    InternalVarCheckFreePool (OldBuffer);
  }
  return NewBuffer;
}

/**
  Merge Hii Question.

  @param[in, out] HiiVariableNode   Pointer to Hii Variable node.
  @param[in]      HiiQuestion       Pointer to Hii Question.
  @param[in]      FromFv            Hii Question from FV.

**/
VOID
MergeHiiQuestion (
  IN OUT VAR_CHECK_HII_VARIABLE_NODE    *HiiVariableNode,
  IN VAR_CHECK_HII_QUESTION_HEADER      *HiiQuestion,
  IN BOOLEAN                            FromFv
  )
{
  VAR_CHECK_HII_QUESTION_HEADER     *HiiQuestion1;
  VAR_CHECK_HII_QUESTION_HEADER     *HiiQuestion2;
  VAR_CHECK_HII_QUESTION_HEADER     *NewHiiQuestion;
  UINT8                             NewLength;
  UINT64                            Minimum1;
  UINT64                            Maximum1;
  UINT64                            OneValue1;
  UINT64                            Minimum2;
  UINT64                            Maximum2;
  UINT64                            OneValue2;
  UINT8                             *Ptr;
  UINT8                             *Ptr1;
  UINT8                             *Ptr2;

  //
  // Hii Question from Hii Database has high priority.
  // Do not to merge Hii Question from Fv to Hii Question from Hii Database.
  //
  if (FromFv) {
    InternalVarCheckFreePool (HiiQuestion);
    return;
  }

  HiiQuestion1 = HiiVariableNode->HiiQuestionArray[HiiQuestion->VarOffset];
  HiiQuestion2 = HiiQuestion;

  ASSERT ((HiiQuestion1->OpCode == HiiQuestion2->OpCode) && (HiiQuestion1->StorageWidth == HiiQuestion2->StorageWidth));

  switch (HiiQuestion1->OpCode) {
    case EFI_IFR_ONE_OF_OP:
      DEBUG ((EFI_D_INFO, "MergeHiiQuestion - EFI_IFR_ONE_OF_OP VarOffset = 0x%04x\n", HiiQuestion1->VarOffset));
      //
      // Get the length of Hii Question 1.
      //
      NewLength = HiiQuestion1->Length;

      //
      // Check if the one of options in Hii Question 2 have been in Hii Question 1.
      //
      Ptr2 = (UINT8 *) ((VAR_CHECK_HII_QUESTION_ONEOF *) HiiQuestion2 + 1);
      while ((UINTN) Ptr2 < (UINTN) HiiQuestion2 + HiiQuestion2->Length) {
        OneValue2 = 0;
        CopyMem (&OneValue2, Ptr2, HiiQuestion2->StorageWidth);

        Ptr1 = (UINT8 *) ((VAR_CHECK_HII_QUESTION_ONEOF *) HiiQuestion1 + 1);
        while ((UINTN) Ptr1 < (UINTN) HiiQuestion1 + HiiQuestion1->Length) {
          OneValue1 = 0;
          CopyMem (&OneValue1, Ptr1, HiiQuestion1->StorageWidth);
          if (OneValue2 == OneValue1) {
            //
            // Match
            //
            break;
          }
          Ptr1 += HiiQuestion1->StorageWidth;
        }
        if ((UINTN) Ptr1 >= ((UINTN) HiiQuestion1 + HiiQuestion1->Length)) {
          //
          // No match
          //
          NewLength = (UINT8) (NewLength + HiiQuestion1->StorageWidth);
        }
        Ptr2 += HiiQuestion2->StorageWidth;
      }

      if (NewLength > HiiQuestion1->Length) {
        //
        // Merge the one of options of Hii Question 2 and Hii Question 1.
        //
        NewHiiQuestion = InternalVarCheckAllocateZeroPool (NewLength);
        ASSERT (NewHiiQuestion != NULL);
        CopyMem (NewHiiQuestion, HiiQuestion1, HiiQuestion1->Length);
        //
        // Use the new length.
        //
        NewHiiQuestion->Length = NewLength;
        Ptr = (UINT8 *) NewHiiQuestion + HiiQuestion1->Length;

        Ptr2 = (UINT8 *) ((VAR_CHECK_HII_QUESTION_ONEOF *) HiiQuestion2 + 1);
        while ((UINTN) Ptr2 < (UINTN) HiiQuestion2 + HiiQuestion2->Length) {
          OneValue2 = 0;
          CopyMem (&OneValue2, Ptr2, HiiQuestion2->StorageWidth);

          Ptr1 = (UINT8 *) ((VAR_CHECK_HII_QUESTION_ONEOF *) HiiQuestion1 + 1);
          while ((UINTN) Ptr1 < (UINTN) HiiQuestion1 + HiiQuestion1->Length) {
            OneValue1 = 0;
            CopyMem (&OneValue1, Ptr1, HiiQuestion1->StorageWidth);
            if (OneValue2 == OneValue1) {
              //
              // Match
              //
              break;
            }
            Ptr1 += HiiQuestion1->StorageWidth;
          }
          if ((UINTN) Ptr1 >= ((UINTN) HiiQuestion1 + HiiQuestion1->Length)) {
            //
            // No match
            //
            CopyMem (Ptr, &OneValue2, HiiQuestion1->StorageWidth);
            Ptr += HiiQuestion1->StorageWidth;
          }
          Ptr2 += HiiQuestion2->StorageWidth;
        }

        HiiVariableNode->HiiQuestionArray[HiiQuestion1->VarOffset] = NewHiiQuestion;
        InternalVarCheckFreePool (HiiQuestion1);
      }
      break;

    case EFI_IFR_CHECKBOX_OP:
      DEBUG ((EFI_D_INFO, "MergeHiiQuestion - EFI_IFR_CHECKBOX_OP VarOffset = 0x%04x\n", HiiQuestion1->VarOffset));
      break;

    case EFI_IFR_NUMERIC_OP:
      DEBUG ((EFI_D_INFO, "MergeHiiQuestion - EFI_IFR_NUMERIC_OP VarOffset = 0x%04x\n", HiiQuestion1->VarOffset));
      //
      // Get minimum and maximum of Hii Question 1.
      //
      Minimum1 = 0;
      Maximum1 = 0;
      Ptr = (UINT8 *) ((VAR_CHECK_HII_QUESTION_NUMERIC *) HiiQuestion1 + 1);
      CopyMem (&Minimum1, Ptr, HiiQuestion1->StorageWidth);
      Ptr += HiiQuestion1->StorageWidth;
      CopyMem (&Maximum1, Ptr, HiiQuestion1->StorageWidth);

      //
      // Get minimum and maximum of Hii Question 2.
      //
      Minimum2 = 0;
      Maximum2 = 0;
      Ptr = (UINT8 *) ((VAR_CHECK_HII_QUESTION_NUMERIC *) HiiQuestion2 + 1);
      CopyMem (&Minimum2, Ptr, HiiQuestion2->StorageWidth);
      Ptr += HiiQuestion2->StorageWidth;
      CopyMem (&Maximum2, Ptr, HiiQuestion2->StorageWidth);

      //
      // Update minimum.
      //
      Ptr = (UINT8 *) ((VAR_CHECK_HII_QUESTION_NUMERIC *) HiiQuestion1 + 1);
      if (Minimum2 < Minimum1) {
        Minimum1 = Minimum2;
        CopyMem (Ptr, &Minimum1, HiiQuestion1->StorageWidth);
      }
      //
      // Update maximum.
      //
      Ptr += HiiQuestion1->StorageWidth;
      if (Maximum2 > Maximum1) {
        Maximum1 = Maximum2;
        CopyMem (Ptr, &Maximum1, HiiQuestion1->StorageWidth);
      }
      break;

    case EFI_IFR_ORDERED_LIST_OP:
      DEBUG ((EFI_D_INFO, "MergeHiiQuestion - EFI_IFR_ORDERED_LIST_OP VarOffset = 0x%04x\n", HiiQuestion1->VarOffset));
      //
      // Get the length of Hii Question 1.
      //
      NewLength = HiiQuestion1->Length;

      //
      // Check if the one of options in Hii Question 2 have been in Hii Question 1.
      //
      Ptr2 = (UINT8 *) ((VAR_CHECK_HII_QUESTION_ORDEREDLIST *) HiiQuestion2 + 1);
      while ((UINTN) Ptr2 < (UINTN) HiiQuestion2 + HiiQuestion2->Length) {
        OneValue2 = 0;
        CopyMem (&OneValue2, Ptr2, HiiQuestion2->StorageWidth);

        Ptr1 = (UINT8 *) ((VAR_CHECK_HII_QUESTION_ORDEREDLIST *) HiiQuestion1 + 1);
        while ((UINTN) Ptr1 < (UINTN) HiiQuestion1 + HiiQuestion1->Length) {
          OneValue1 = 0;
          CopyMem (&OneValue1, Ptr1, HiiQuestion1->StorageWidth);
          if (OneValue2 == OneValue1) {
            //
            // Match
            //
            break;
          }
          Ptr1 += HiiQuestion1->StorageWidth;
        }
        if ((UINTN) Ptr1 >= ((UINTN) HiiQuestion1 + HiiQuestion1->Length)) {
          //
          // No match
          //
          NewLength = (UINT8) (NewLength + HiiQuestion1->StorageWidth);
        }
        Ptr2 += HiiQuestion2->StorageWidth;
      }

      if (NewLength > HiiQuestion1->Length) {
        //
        // Merge the one of options of Hii Question 2 and Hii Question 1.
        //
        NewHiiQuestion = InternalVarCheckAllocateZeroPool (NewLength);
        ASSERT (NewHiiQuestion != NULL);
        CopyMem (NewHiiQuestion, HiiQuestion1, HiiQuestion1->Length);
        //
        // Use the new length.
        //
        NewHiiQuestion->Length = NewLength;
        Ptr = (UINT8 *) NewHiiQuestion + HiiQuestion1->Length;

        Ptr2 = (UINT8 *) ((VAR_CHECK_HII_QUESTION_ORDEREDLIST *) HiiQuestion2 + 1);
        while ((UINTN) Ptr2 < (UINTN) HiiQuestion2 + HiiQuestion2->Length) {
          OneValue2 = 0;
          CopyMem (&OneValue2, Ptr2, HiiQuestion2->StorageWidth);

          Ptr1 = (UINT8 *) ((VAR_CHECK_HII_QUESTION_ORDEREDLIST *) HiiQuestion1 + 1);
          while ((UINTN) Ptr1 < (UINTN) HiiQuestion1 + HiiQuestion1->Length) {
            OneValue1 = 0;
            CopyMem (&OneValue1, Ptr1, HiiQuestion1->StorageWidth);
            if (OneValue2 == OneValue1) {
              //
              // Match
              //
              break;
            }
            Ptr1 += HiiQuestion1->StorageWidth;
          }
          if ((UINTN) Ptr1 >= ((UINTN) HiiQuestion1 + HiiQuestion1->Length)) {
            //
            // No match
            //
            CopyMem (Ptr, &OneValue2, HiiQuestion1->StorageWidth);
            Ptr += HiiQuestion1->StorageWidth;
          }
          Ptr2 += HiiQuestion2->StorageWidth;
        }

        HiiVariableNode->HiiQuestionArray[HiiQuestion1->VarOffset] = NewHiiQuestion;
        InternalVarCheckFreePool (HiiQuestion1);
      }
      break;

    default:
      ASSERT (FALSE);
      return;
      break;
  }

  //
  //
  // Hii Question 2 has been merged with Hii Question 1.
  //
  InternalVarCheckFreePool (HiiQuestion2);
}

/**
  Get OneOf option data.

  @param[in]  IfrOpCodeHeader   Pointer to Ifr OpCode header.
  @param[out] Count             Pointer to option count.
  @param[out] Width             Pointer to option width.
  @param[out] OptionBuffer      Pointer to option buffer.

**/
VOID
GetOneOfOption (
  IN  EFI_IFR_OP_HEADER     *IfrOpCodeHeader,
  OUT UINTN                 *Count,
  OUT UINT8                 *Width,
  OUT VOID                  *OptionBuffer OPTIONAL
  )
{
  UINTN                     Scope;
  EFI_IFR_ONE_OF_OPTION     *IfrOneOfOption;

  //
  // Assume all OPTION has same Width.
  //
  *Count = 0;

  if (IfrOpCodeHeader->Scope != 0) {
    //
    // Nested OpCode.
    //
    Scope = 1;
    IfrOpCodeHeader = (EFI_IFR_OP_HEADER *) ((UINTN) IfrOpCodeHeader + IfrOpCodeHeader->Length);
    while (Scope != 0) {
      switch (IfrOpCodeHeader->OpCode) {
        case EFI_IFR_ONE_OF_OPTION_OP:
          IfrOneOfOption = (EFI_IFR_ONE_OF_OPTION *) IfrOpCodeHeader;
          switch (IfrOneOfOption->Type) {
            case EFI_IFR_TYPE_NUM_SIZE_8:
              *Count = *Count + 1;
              *Width = sizeof (UINT8);
              if (OptionBuffer != NULL) {
                CopyMem (OptionBuffer, &IfrOneOfOption->Value.u8, sizeof (UINT8));
                OptionBuffer = (UINT8 *) OptionBuffer + 1;
              }
              break;
            case EFI_IFR_TYPE_NUM_SIZE_16:
              *Count = *Count + 1;
              *Width = sizeof (UINT16);
              if (OptionBuffer != NULL) {
                CopyMem (OptionBuffer, &IfrOneOfOption->Value.u16, sizeof (UINT16));
                OptionBuffer = (UINT16 *) OptionBuffer + 1;
              }
              break;
            case EFI_IFR_TYPE_NUM_SIZE_32:
              *Count = *Count + 1;
              *Width = sizeof (UINT32);
              if (OptionBuffer != NULL) {
                CopyMem (OptionBuffer, &IfrOneOfOption->Value.u32, sizeof (UINT32));
                OptionBuffer = (UINT32 *) OptionBuffer + 1;
              }
              break;
            case EFI_IFR_TYPE_NUM_SIZE_64:
              *Count = *Count + 1;
              *Width = sizeof (UINT64);
              if (OptionBuffer != NULL) {
                CopyMem (OptionBuffer, &IfrOneOfOption->Value.u64, sizeof (UINT64));
                OptionBuffer = (UINT64 *) OptionBuffer + 1;
              }
              break;
            case EFI_IFR_TYPE_BOOLEAN:
              *Count = *Count + 1;
              *Width = sizeof (BOOLEAN);
              if (OptionBuffer != NULL) {
                CopyMem (OptionBuffer, &IfrOneOfOption->Value.b, sizeof (BOOLEAN));
                OptionBuffer = (BOOLEAN *) OptionBuffer + 1;
              }
              break;
            default:
              break;
          }
          break;
      }

      //
      // Until End OpCode.
      //
      if (IfrOpCodeHeader->OpCode == EFI_IFR_END_OP) {
        ASSERT (Scope > 0);
        Scope--;
        if (Scope == 0) {
          break;
        }
      } else if (IfrOpCodeHeader->Scope != 0) {
        //
        // Nested OpCode.
        //
        Scope++;
      }
      IfrOpCodeHeader = (EFI_IFR_OP_HEADER *) ((UINTN) IfrOpCodeHeader + IfrOpCodeHeader->Length);
    }
  }

  return ;
}

/**
  Parse Hii Question Oneof.

  @param[in] IfrOpCodeHeader    Pointer to Ifr OpCode header.

  return Pointer to Hii Question.

**/
VAR_CHECK_HII_QUESTION_HEADER *
ParseHiiQuestionOneOf (
  IN EFI_IFR_OP_HEADER  *IfrOpCodeHeader
  )
{
  EFI_IFR_ONE_OF                *IfrOneOf;
  VAR_CHECK_HII_QUESTION_ONEOF  *OneOf;
  UINTN                         Length;
  UINT8                         Width;
  UINTN                         OptionCount;
  UINT8                         OptionWidth;

  IfrOneOf = (EFI_IFR_ONE_OF *) IfrOpCodeHeader;

  Width = (UINT8) (1 << (IfrOneOf->Flags & EFI_IFR_NUMERIC_SIZE));

  GetOneOfOption (IfrOpCodeHeader, &OptionCount, &OptionWidth, NULL);
  ASSERT (Width == OptionWidth);

  Length = sizeof (*OneOf) + OptionCount * Width;

  OneOf = InternalVarCheckAllocateZeroPool (Length);
  ASSERT (OneOf != NULL);
  OneOf->OpCode       = EFI_IFR_ONE_OF_OP;
  OneOf->Length       = (UINT8) Length;
  OneOf->VarOffset    = IfrOneOf->Question.VarStoreInfo.VarOffset;
  OneOf->StorageWidth = Width;

  GetOneOfOption (IfrOpCodeHeader, &OptionCount, &OptionWidth, OneOf + 1);

  return (VAR_CHECK_HII_QUESTION_HEADER *) OneOf;
}

/**
  Parse Hii Question CheckBox.

  @param[in] IfrOpCodeHeader    Pointer to Ifr OpCode header.

  return Pointer to Hii Question.

**/
VAR_CHECK_HII_QUESTION_HEADER *
ParseHiiQuestionCheckBox (
  IN EFI_IFR_OP_HEADER  *IfrOpCodeHeader
  )
{
  EFI_IFR_CHECKBOX                  *IfrCheckBox;
  VAR_CHECK_HII_QUESTION_CHECKBOX   *CheckBox;

  IfrCheckBox = (EFI_IFR_CHECKBOX *) IfrOpCodeHeader;

  CheckBox = InternalVarCheckAllocateZeroPool (sizeof (*CheckBox));
  ASSERT (CheckBox != NULL);
  CheckBox->OpCode       = EFI_IFR_CHECKBOX_OP;
  CheckBox->Length       = (UINT8) sizeof (*CheckBox);;
  CheckBox->VarOffset    = IfrCheckBox->Question.VarStoreInfo.VarOffset;
  CheckBox->StorageWidth = (UINT8) sizeof (BOOLEAN);

  return (VAR_CHECK_HII_QUESTION_HEADER *) CheckBox;
}

/**
  Parse Hii Question Numeric.

  @param[in] IfrOpCodeHeader    Pointer to Ifr OpCode header.

  return Pointer to Hii Question.

**/
VAR_CHECK_HII_QUESTION_HEADER *
ParseHiiQuestionNumeric (
  IN EFI_IFR_OP_HEADER  *IfrOpCodeHeader
  )
{
  EFI_IFR_NUMERIC                   *IfrNumeric;
  VAR_CHECK_HII_QUESTION_NUMERIC    *Numeric;
  UINT8                             Width;

  IfrNumeric = (EFI_IFR_NUMERIC *) IfrOpCodeHeader;

  Numeric = InternalVarCheckAllocateZeroPool (sizeof (VAR_CHECK_HII_QUESTION_NUMERIC) + 2 * sizeof (UINT64));
  ASSERT (Numeric != NULL);

  Width = (UINT8) (1 << (IfrNumeric->Flags & EFI_IFR_NUMERIC_SIZE));

  Numeric->OpCode       = EFI_IFR_NUMERIC_OP;
  Numeric->Length       = (UINT8) (sizeof (VAR_CHECK_HII_QUESTION_NUMERIC) + 2 * Width);
  Numeric->VarOffset    = IfrNumeric->Question.VarStoreInfo.VarOffset;
  Numeric->StorageWidth = Width;

  CopyMem (Numeric + 1, &IfrNumeric->data, Width * 2);

  return (VAR_CHECK_HII_QUESTION_HEADER *) Numeric;
}

/**
  Parse Hii Question OrderedList.

  @param[in] IfrOpCodeHeader    Pointer to Ifr OpCode header.

  return Pointer to Hii Question.

**/
VAR_CHECK_HII_QUESTION_HEADER *
ParseHiiQuestionOrderedList (
  IN EFI_IFR_OP_HEADER  *IfrOpCodeHeader
  )
{
  EFI_IFR_ORDERED_LIST                  *IfrOrderedList;
  VAR_CHECK_HII_QUESTION_ORDEREDLIST    *OrderedList;
  UINTN                                 Length;
  UINTN                                 OptionCount;
  UINT8                                 OptionWidth;

  IfrOrderedList = (EFI_IFR_ORDERED_LIST *) IfrOpCodeHeader;

  GetOneOfOption (IfrOpCodeHeader, &OptionCount, &OptionWidth, NULL);

  Length = sizeof (*OrderedList) + OptionCount * OptionWidth;

  OrderedList = InternalVarCheckAllocateZeroPool (Length);
  ASSERT (OrderedList != NULL);
  OrderedList->OpCode        = EFI_IFR_ORDERED_LIST_OP;
  OrderedList->Length        = (UINT8) Length;
  OrderedList->VarOffset     = IfrOrderedList->Question.VarStoreInfo.VarOffset;
  OrderedList->StorageWidth  = OptionWidth;
  OrderedList->MaxContainers = IfrOrderedList->MaxContainers;

  GetOneOfOption (IfrOpCodeHeader, &OptionCount, &OptionWidth, OrderedList + 1);

  return (VAR_CHECK_HII_QUESTION_HEADER *) OrderedList;
}

/**
  Parse and create Hii Question node.

  @param[in] HiiVariableNode    Pointer to Hii Variable node.
  @param[in] IfrOpCodeHeader    Pointer to Ifr OpCode header.
  @param[in] FromFv             Hii Question from FV.

**/
VOID
ParseHiiQuestion (
  IN VAR_CHECK_HII_VARIABLE_NODE    *HiiVariableNode,
  IN  EFI_IFR_OP_HEADER             *IfrOpCodeHeader,
  IN BOOLEAN                        FromFv
  )
{
  VAR_CHECK_HII_QUESTION_HEADER *HiiQuestion;

  switch (IfrOpCodeHeader->OpCode) {
    case EFI_IFR_ONE_OF_OP:
      HiiQuestion = ParseHiiQuestionOneOf (IfrOpCodeHeader);
      break;

    case EFI_IFR_CHECKBOX_OP:
      HiiQuestion = ParseHiiQuestionCheckBox (IfrOpCodeHeader);
      break;

    case EFI_IFR_NUMERIC_OP:
      HiiQuestion = ParseHiiQuestionNumeric (IfrOpCodeHeader);
      break;

    case EFI_IFR_ORDERED_LIST_OP:
      HiiQuestion = ParseHiiQuestionOrderedList (IfrOpCodeHeader);
      break;

    default:
      ASSERT (FALSE);
      return;
      break;
  }

  if (HiiVariableNode->HiiQuestionArray[HiiQuestion->VarOffset] != NULL) {
    MergeHiiQuestion (HiiVariableNode, HiiQuestion, FromFv);
  } else {
    HiiVariableNode->HiiQuestionArray[HiiQuestion->VarOffset] = HiiQuestion;
  }
}

/**
  Find Hii variable node by name and GUID.

  @param[in] Name   Pointer to variable name.
  @param[in] Guid   Pointer to vendor GUID.

  @return Pointer to Hii Variable node.

**/
VAR_CHECK_HII_VARIABLE_NODE *
FindHiiVariableNode (
  IN CHAR16     *Name,
  IN EFI_GUID   *Guid
  )
{
  VAR_CHECK_HII_VARIABLE_NODE   *HiiVariableNode;
  LIST_ENTRY                    *Link;

  for (Link = mVarCheckHiiList.ForwardLink
      ;Link != &mVarCheckHiiList
      ;Link = Link->ForwardLink) {
    HiiVariableNode = VAR_CHECK_HII_VARIABLE_FROM_LINK (Link);

    if ((StrCmp (Name, (CHAR16 *) (HiiVariableNode->HiiVariable + 1)) == 0) &&
        CompareGuid (Guid, &HiiVariableNode->HiiVariable->Guid)) {
      return HiiVariableNode;
    }
  }

  return NULL;
}

/**
  Find Hii variable node by var store id.

  @param[in] VarStoreId         Var store id.

  @return Pointer to Hii Variable node.

**/
VAR_CHECK_HII_VARIABLE_NODE *
FindHiiVariableNodeByVarStoreId (
  IN EFI_VARSTORE_ID            VarStoreId
  )
{
  VAR_CHECK_HII_VARIABLE_NODE   *HiiVariableNode;
  LIST_ENTRY                    *Link;

  if (VarStoreId == 0) {
    //
    // The variable store identifier, which is unique within the current form set.
    // A value of zero is invalid.
    //
    return NULL;
  }

  for (Link = mVarCheckHiiList.ForwardLink
      ;Link != &mVarCheckHiiList
      ;Link = Link->ForwardLink) {
    HiiVariableNode = VAR_CHECK_HII_VARIABLE_FROM_LINK (Link);
    //
    // The variable store identifier, which is unique within the current form set.
    //
    if (VarStoreId == HiiVariableNode->VarStoreId) {
      return HiiVariableNode;
    }
  }

  return NULL;
}

/**
  Destroy var store id in the Hii Variable node after parsing one Hii Package.

**/
VOID
DestroyVarStoreId (
  VOID
  )
{
  VAR_CHECK_HII_VARIABLE_NODE   *HiiVariableNode;
  LIST_ENTRY                    *Link;

  for (Link = mVarCheckHiiList.ForwardLink
      ;Link != &mVarCheckHiiList
      ;Link = Link->ForwardLink) {
    HiiVariableNode = VAR_CHECK_HII_VARIABLE_FROM_LINK (Link);
    //
    // The variable store identifier, which is unique within the current form set.
    // A value of zero is invalid.
    //
    HiiVariableNode->VarStoreId = 0;
  }
}

/**
  Create Hii Variable node.

  @param[in] IfrEfiVarStore  Pointer to EFI VARSTORE.

**/
VOID
CreateHiiVariableNode (
  IN EFI_IFR_VARSTORE_EFI   *IfrEfiVarStore
  )
{
  VAR_CHECK_HII_VARIABLE_NODE   *HiiVariableNode;
  VAR_CHECK_HII_VARIABLE_HEADER *HiiVariable;
  UINTN                         HeaderLength;
  CHAR16                        *VarName;
  UINTN                         VarNameSize;

  //
  // Get variable name.
  //
  VarNameSize = AsciiStrSize ((CHAR8 *) IfrEfiVarStore->Name) * sizeof (CHAR16);
  if (VarNameSize > mMaxVarNameSize) {
    mVarName = InternalVarCheckReallocatePool (mMaxVarNameSize, VarNameSize, mVarName);
    ASSERT (mVarName != NULL);
    mMaxVarNameSize = VarNameSize;
  }
  AsciiStrToUnicodeStrS ((CHAR8 *) IfrEfiVarStore->Name, mVarName, mMaxVarNameSize / sizeof (CHAR16));
  VarName = mVarName;

  HiiVariableNode = FindHiiVariableNode (
                      VarName,
                      &IfrEfiVarStore->Guid
                      );
  if (HiiVariableNode == NULL) {
    //
    // Not found, then create new.
    //
    HeaderLength = sizeof (*HiiVariable) + VarNameSize;
    HiiVariable = InternalVarCheckAllocateZeroPool (HeaderLength);
    ASSERT (HiiVariable != NULL);
    HiiVariable->Revision = VAR_CHECK_HII_REVISION;
    HiiVariable->OpCode = EFI_IFR_VARSTORE_EFI_OP;
    HiiVariable->HeaderLength = (UINT16) HeaderLength;
    HiiVariable->Size = IfrEfiVarStore->Size;
    HiiVariable->Attributes = IfrEfiVarStore->Attributes;
    CopyGuid (&HiiVariable->Guid, &IfrEfiVarStore->Guid);
    StrCpyS ((CHAR16 *) (HiiVariable + 1), VarNameSize / sizeof (CHAR16), VarName);

    HiiVariableNode = InternalVarCheckAllocateZeroPool (sizeof (*HiiVariableNode));
    ASSERT (HiiVariableNode != NULL);
    HiiVariableNode->Signature = VAR_CHECK_HII_VARIABLE_NODE_SIGNATURE;
    HiiVariableNode->HiiVariable = HiiVariable;
    //
    // The variable store identifier, which is unique within the current form set.
    //
    HiiVariableNode->VarStoreId = IfrEfiVarStore->VarStoreId;
    HiiVariableNode->HiiQuestionArray = InternalVarCheckAllocateZeroPool (IfrEfiVarStore->Size * sizeof (VAR_CHECK_HII_QUESTION_HEADER *));

    InsertTailList (&mVarCheckHiiList, &HiiVariableNode->Link);
  } else {
    HiiVariableNode->VarStoreId = IfrEfiVarStore->VarStoreId;
  }
}

/**
  Parse and create Hii Variable node list.

  @param[in] HiiPackage         Pointer to Hii Package.

**/
VOID
ParseHiiVariable (
  IN VOID       *HiiPackage
  )
{
  EFI_HII_PACKAGE_HEADER    *HiiPackageHeader;
  EFI_IFR_OP_HEADER         *IfrOpCodeHeader;
  EFI_IFR_VARSTORE_EFI      *IfrEfiVarStore;

  HiiPackageHeader = (EFI_HII_PACKAGE_HEADER *) HiiPackage;

  switch (HiiPackageHeader->Type) {
    case EFI_HII_PACKAGE_FORMS:
      IfrOpCodeHeader = (EFI_IFR_OP_HEADER *) (HiiPackageHeader + 1);

      while ((UINTN) IfrOpCodeHeader < (UINTN) HiiPackageHeader + HiiPackageHeader->Length) {
        switch (IfrOpCodeHeader->OpCode) {
          case EFI_IFR_VARSTORE_EFI_OP:
            //
            // Come to EFI VARSTORE in Form Package.
            //
            IfrEfiVarStore = (EFI_IFR_VARSTORE_EFI *) IfrOpCodeHeader;
            if ((IfrEfiVarStore->Header.Length >= sizeof (EFI_IFR_VARSTORE_EFI)) &&
                ((IfrEfiVarStore->Attributes & EFI_VARIABLE_NON_VOLATILE) != 0)) {
              //
              // Only create node list for Hii Variable with NV attribute.
              //
              CreateHiiVariableNode (IfrEfiVarStore);
            }
            break;

          default:
            break;
        }
      IfrOpCodeHeader = (EFI_IFR_OP_HEADER *) ((UINTN) IfrOpCodeHeader + IfrOpCodeHeader->Length);
      }
      break;

    default:
      break;
  }
}

/**
  Var Check Parse Hii Package.

  @param[in] HiiPackage         Pointer to Hii Package.
  @param[in] FromFv             Hii Package from FV.

**/
VOID
VarCheckParseHiiPackage (
  IN VOID       *HiiPackage,
  IN BOOLEAN    FromFv
  )
{
  EFI_HII_PACKAGE_HEADER        *HiiPackageHeader;
  EFI_IFR_OP_HEADER             *IfrOpCodeHeader;
  VAR_CHECK_HII_VARIABLE_NODE   *HiiVariableNode;

  //
  // Parse and create Hii Variable node list for this Hii Package.
  //
  ParseHiiVariable (HiiPackage);

  HiiPackageHeader = (EFI_HII_PACKAGE_HEADER *) HiiPackage;

  switch (HiiPackageHeader->Type) {
    case EFI_HII_PACKAGE_FORMS:
      IfrOpCodeHeader = (EFI_IFR_OP_HEADER *) (HiiPackageHeader + 1);

      while ((UINTN) IfrOpCodeHeader < (UINTN) HiiPackageHeader + HiiPackageHeader->Length) {
        switch (IfrOpCodeHeader->OpCode) {
          case EFI_IFR_ONE_OF_OP:
          case EFI_IFR_CHECKBOX_OP:
          case EFI_IFR_NUMERIC_OP:
          case EFI_IFR_ORDERED_LIST_OP:
            HiiVariableNode = FindHiiVariableNodeByVarStoreId (((EFI_IFR_ONE_OF *) IfrOpCodeHeader)->Question.VarStoreId);
            if ((HiiVariableNode == NULL) ||
                //
                // No related Hii Variable node found.
                //
                ((((EFI_IFR_ONE_OF *) IfrOpCodeHeader)->Question.Header.Prompt == 0) && (((EFI_IFR_ONE_OF *) IfrOpCodeHeader)->Question.Header.Help == 0))) {
                //
                // meanless IFR item introduced by ECP.
                //
            } else {
              //
              // Normal IFR
              //
              ParseHiiQuestion (HiiVariableNode, IfrOpCodeHeader, FromFv);
            }
          default:
            break;
        }
        IfrOpCodeHeader = (EFI_IFR_OP_HEADER *) ((UINTN) IfrOpCodeHeader + IfrOpCodeHeader->Length);
      }
      break;

    default:
      break;
  }
  DestroyVarStoreId ();
}

/**
  Var Check Parse Hii Database.

  @param[in] HiiDatabase        Pointer to Hii Database.
  @param[in] HiiDatabaseSize    Hii Database size.

**/
VOID
VarCheckParseHiiDatabase (
  IN VOID       *HiiDatabase,
  IN UINTN      HiiDatabaseSize
  )
{
  EFI_HII_PACKAGE_LIST_HEADER   *HiiPackageListHeader;
  EFI_HII_PACKAGE_HEADER        *HiiPackageHeader;

  HiiPackageListHeader = (EFI_HII_PACKAGE_LIST_HEADER *) HiiDatabase;

  while ((UINTN) HiiPackageListHeader < ((UINTN) HiiDatabase + HiiDatabaseSize)) {
    HiiPackageHeader = (EFI_HII_PACKAGE_HEADER *) (HiiPackageListHeader + 1);

    while ((UINTN) HiiPackageHeader < ((UINTN) HiiPackageListHeader + HiiPackageListHeader->PackageLength)) {
      //
      // Parse Hii Package.
      //
      VarCheckParseHiiPackage (HiiPackageHeader, FALSE);

      HiiPackageHeader = (EFI_HII_PACKAGE_HEADER *) ((UINTN) HiiPackageHeader + HiiPackageHeader->Length);
    }

    HiiPackageListHeader = (EFI_HII_PACKAGE_LIST_HEADER *) ((UINTN) HiiPackageListHeader + HiiPackageListHeader->PackageLength);
  }
}

/**
  Destroy Hii Variable node.

**/
VOID
DestroyHiiVariableNode (
  VOID
  )
{
  VAR_CHECK_HII_VARIABLE_NODE   *HiiVariableNode;
  LIST_ENTRY                    *HiiVariableLink;
  UINTN                         Index;

  while (mVarCheckHiiList.ForwardLink != &mVarCheckHiiList) {
    HiiVariableLink = mVarCheckHiiList.ForwardLink;
    HiiVariableNode = VAR_CHECK_HII_VARIABLE_FROM_LINK (HiiVariableLink);

    RemoveEntryList (&HiiVariableNode->Link);

    //
    // Free the allocated buffer.
    //
    for (Index = 0; Index < HiiVariableNode->HiiVariable->Size; Index++) {
      if (HiiVariableNode->HiiQuestionArray[Index] != NULL) {
        InternalVarCheckFreePool (HiiVariableNode->HiiQuestionArray[Index]);
      }
    }
    InternalVarCheckFreePool (HiiVariableNode->HiiQuestionArray);
    InternalVarCheckFreePool (HiiVariableNode->HiiVariable);
    InternalVarCheckFreePool (HiiVariableNode);
  }
}

/**
  Build VarCheckHiiBin.

  @param[out] Size      Pointer to VarCheckHii size.

  @return Pointer to VarCheckHiiBin.

**/
VOID *
BuildVarCheckHiiBin (
  OUT UINTN  *Size
  )
{
  VAR_CHECK_HII_VARIABLE_NODE   *HiiVariableNode;
  LIST_ENTRY                    *HiiVariableLink;
  UINTN                         Index;
  VOID                          *Data;
  UINT8                         *Ptr;
  UINT32                        BinSize;
  UINT32                        HiiVariableLength;

  //
  // Get Size
  //
  BinSize = 0;

  for (HiiVariableLink = mVarCheckHiiList.ForwardLink
      ;HiiVariableLink != &mVarCheckHiiList
      ;HiiVariableLink = HiiVariableLink->ForwardLink) {
    //
    // For Hii Variable header align.
    //
    BinSize = (UINT32) HEADER_ALIGN (BinSize);

    HiiVariableNode = VAR_CHECK_HII_VARIABLE_FROM_LINK (HiiVariableLink);
    HiiVariableLength = HiiVariableNode->HiiVariable->HeaderLength;

    for (Index = 0; Index < HiiVariableNode->HiiVariable->Size; Index++) {
      if (HiiVariableNode->HiiQuestionArray[Index] != NULL) {
        //
        // For Hii Question header align.
        //
        HiiVariableLength = (UINT32) HEADER_ALIGN (HiiVariableLength);
        HiiVariableLength += HiiVariableNode->HiiQuestionArray[Index]->Length;
      }
    }

    HiiVariableNode->HiiVariable->Length = HiiVariableLength;
    BinSize += HiiVariableLength;
  }

  DEBUG ((EFI_D_INFO, "VarCheckHiiBin - size = 0x%x\n", BinSize));
  if (BinSize == 0) {
    *Size = BinSize;
    return NULL;
  }

  //
  // AllocatePages () and AllocatePool () from gBS are used for the process of VarCheckHiiBin generation.
  // Only here AllocateRuntimeZeroPool () from MemoryAllocateLib is used for runtime access
  // in SetVariable check handler.
  //
  Data = AllocateRuntimeZeroPool (BinSize);
  ASSERT (Data != NULL);
  DEBUG ((EFI_D_INFO, "VarCheckHiiBin - built at 0x%x\n", Data));

  //
  // Gen Data
  //
  Ptr = Data;
  for (HiiVariableLink = mVarCheckHiiList.ForwardLink
      ;HiiVariableLink != &mVarCheckHiiList
      ;HiiVariableLink = HiiVariableLink->ForwardLink) {
    //
    // For Hii Variable header align.
    //
    Ptr = (UINT8 *) HEADER_ALIGN (Ptr);

    HiiVariableNode = VAR_CHECK_HII_VARIABLE_FROM_LINK (HiiVariableLink);
    CopyMem (Ptr, HiiVariableNode->HiiVariable, HiiVariableNode->HiiVariable->HeaderLength);
    Ptr += HiiVariableNode->HiiVariable->HeaderLength;

    for (Index = 0; Index < HiiVariableNode->HiiVariable->Size; Index++) {
      if (HiiVariableNode->HiiQuestionArray[Index] != NULL) {
        //
        // For Hii Question header align.
        //
        Ptr = (UINT8 *) HEADER_ALIGN (Ptr);
        CopyMem (Ptr, HiiVariableNode->HiiQuestionArray[Index], HiiVariableNode->HiiQuestionArray[Index]->Length);
        Ptr += HiiVariableNode->HiiQuestionArray[Index]->Length;
      }
    }
  }

  *Size = BinSize;
  return Data;
}

/**
  Generate VarCheckHiiBin from Hii Database and FV.

**/
VOID
EFIAPI
VarCheckHiiGen (
  VOID
  )
{
  VarCheckHiiGenFromHiiDatabase ();
  VarCheckHiiGenFromFv ();

  mVarCheckHiiBin = BuildVarCheckHiiBin (&mVarCheckHiiBinSize);
  if (mVarCheckHiiBin == NULL) {
    DEBUG ((EFI_D_INFO, "[VarCheckHii] This driver could be removed from *.dsc and *.fdf\n"));
    return;
  }

  DestroyHiiVariableNode ();
  if (mVarName != NULL) {
    InternalVarCheckFreePool (mVarName);
  }

#ifdef DUMP_VAR_CHECK_HII
  DEBUG_CODE (
    DumpVarCheckHii (mVarCheckHiiBin, mVarCheckHiiBinSize);
  );
#endif
}

