/** @file
  Implementation of HII utility library.

  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
  (C) Copyright 2021 Hewlett Packard Enterprise Development LP<BR>
  Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "HiiInternal.h"

/**
  Initialize the internal data structure of a FormSet.

  @param  Handle                 PackageList Handle
  @param  FormSetGuid            On input, GUID or class GUID of a formset. If not
                                 specified (NULL or zero GUID), take the first
                                 FormSet with class GUID EFI_HII_PLATFORM_SETUP_FORMSET_GUID
                                 found in package list.
                                 On output, GUID of the formset found(if not NULL).
  @param  FormSet                FormSet data structure.

  @retval EFI_SUCCESS            The function completed successfully.
  @retval EFI_NOT_FOUND          The specified FormSet could not be found.

**/
EFI_STATUS
CreateFormSetFromHiiHandle (
  IN     EFI_HII_HANDLE  Handle,
  IN OUT EFI_GUID        *FormSetGuid,
  OUT HII_FORMSET        *FormSet
  )
{
  EFI_STATUS                 Status;
  EFI_HANDLE                 DriverHandle;
  EFI_HII_DATABASE_PROTOCOL  *HiiDatabase;

  if ((FormSetGuid == NULL) || (FormSet == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Locate required Hii Database protocol
  //
  Status = gBS->LocateProtocol (
                  &gEfiHiiDatabaseProtocolGuid,
                  NULL,
                  (VOID **)&HiiDatabase
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  Status = GetIfrBinaryData (Handle, FormSetGuid, &FormSet->IfrBinaryLength, &FormSet->IfrBinaryData);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  FormSet->Signature = HII_FORMSET_SIGNATURE;
  FormSet->HiiHandle = Handle;
  CopyMem (&FormSet->Guid, FormSetGuid, sizeof (EFI_GUID));
  //
  // Retrieve ConfigAccess Protocol associated with this HiiPackageList
  //
  Status = HiiDatabase->GetPackageListHandle (HiiDatabase, Handle, &DriverHandle);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  FormSet->DriverHandle = DriverHandle;
  Status                = gBS->HandleProtocol (
                                 DriverHandle,
                                 &gEfiHiiConfigAccessProtocolGuid,
                                 (VOID **)&FormSet->ConfigAccess
                                 );
  if (EFI_ERROR (Status)) {
    //
    // Configuration Driver don't attach ConfigAccess protocol to its HII package
    // list, then there will be no configuration action required
    //
    FormSet->ConfigAccess = NULL;
  }

  Status = gBS->HandleProtocol (
                  DriverHandle,
                  &gEfiDevicePathProtocolGuid,
                  (VOID **)&FormSet->DevicePath
                  );
  if (EFI_ERROR (Status)) {
    //
    // Configuration Driver don't attach ConfigAccess protocol to its HII package
    // list, then there will be no configuration action required
    //
    FormSet->DevicePath = NULL;
  }

  //
  // Parse the IFR binary OpCodes
  //
  Status = ParseOpCodes (FormSet);

  return Status;
}

/**
  Initialize a Formset and get current setting for Questions.

  @param  FormSet                FormSet data structure.

**/
VOID
InitializeFormSet (
  IN OUT HII_FORMSET  *FormSet
  )
{
  LIST_ENTRY           *Link;
  HII_FORMSET_STORAGE  *Storage;
  LIST_ENTRY           *FormLink;
  HII_STATEMENT        *Question;
  HII_FORM             *Form;

  if (FormSet == NULL) {
    return;
  }

  //
  // Load Storage for all questions with storage
  //
  Link = GetFirstNode (&FormSet->StorageListHead);
  while (!IsNull (&FormSet->StorageListHead, Link)) {
    Storage = HII_STORAGE_FROM_LINK (Link);
    LoadFormSetStorage (FormSet, Storage);
    Link = GetNextNode (&FormSet->StorageListHead, Link);
  }

  //
  // Get Current Value for all no storage questions
  //
  FormLink = GetFirstNode (&FormSet->FormListHead);
  while (!IsNull (&FormSet->FormListHead, FormLink)) {
    Form = HII_FORM_FROM_LINK (FormLink);
    Link = GetFirstNode (&Form->StatementListHead);
    while (!IsNull (&Form->StatementListHead, Link)) {
      Question = HII_STATEMENT_FROM_LINK (Link);
      if (Question->Storage == NULL) {
        RetrieveQuestion (FormSet, Form, Question);
      }

      Link = GetNextNode (&Form->StatementListHead, Link);
    }

    FormLink = GetNextNode (&FormSet->FormListHead, FormLink);
  }
}

/**
  Free resources allocated for a FormSet.

  @param[in,out]  FormSet                Pointer of the FormSet

**/
VOID
DestroyFormSet (
  IN OUT HII_FORMSET  *FormSet
  )
{
  LIST_ENTRY                *Link;
  HII_FORMSET_STORAGE       *Storage;
  HII_FORMSET_DEFAULTSTORE  *DefaultStore;
  HII_FORM                  *Form;

  if (FormSet->IfrBinaryData == NULL) {
    //
    // Uninitialized FormSet
    //
    FreePool (FormSet);
    return;
  }

  //
  // Free IFR binary buffer
  //
  FreePool (FormSet->IfrBinaryData);

  //
  // Free FormSet Storage
  //
  if (FormSet->StorageListHead.ForwardLink != NULL) {
    while (!IsListEmpty (&FormSet->StorageListHead)) {
      Link    = GetFirstNode (&FormSet->StorageListHead);
      Storage = HII_STORAGE_FROM_LINK (Link);
      RemoveEntryList (&Storage->Link);

      if (Storage != NULL) {
        FreePool (Storage);
      }
    }
  }

  //
  // Free FormSet Default Store
  //
  if (FormSet->DefaultStoreListHead.ForwardLink != NULL) {
    while (!IsListEmpty (&FormSet->DefaultStoreListHead)) {
      Link         = GetFirstNode (&FormSet->DefaultStoreListHead);
      DefaultStore = HII_FORMSET_DEFAULTSTORE_FROM_LINK (Link);
      RemoveEntryList (&DefaultStore->Link);

      FreePool (DefaultStore);
    }
  }

  //
  // Free Forms
  //
  if (FormSet->FormListHead.ForwardLink != NULL) {
    while (!IsListEmpty (&FormSet->FormListHead)) {
      Link = GetFirstNode (&FormSet->FormListHead);
      Form = HII_FORM_FROM_LINK (Link);
      RemoveEntryList (&Form->Link);

      DestroyForm (FormSet, Form);
    }
  }

  FreePool (FormSet);
}

/**
  Submit data for a form.

  @param[in]  FormSet                FormSet which contains the Form.
  @param[in]  Form                   Form to submit.

  @retval EFI_SUCCESS            The function completed successfully.
  @retval Others                 Other errors occur.

**/
EFI_STATUS
SubmitForm (
  IN HII_FORMSET  *FormSet,
  IN HII_FORM     *Form
  )
{
  EFI_STATUS                       Status;
  EFI_HII_CONFIG_ROUTING_PROTOCOL  *HiiConfigRouting;
  LIST_ENTRY                       *Link;
  EFI_STRING                       ConfigResp;
  EFI_STRING                       Progress;
  HII_FORMSET_STORAGE              *Storage;
  HII_FORM_CONFIG_REQUEST          *ConfigInfo;

  if ((FormSet == NULL) || (Form == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  Status = NoSubmitCheck (FormSet, &Form, NULL);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  Link = GetFirstNode (&Form->ConfigRequestHead);
  while (!IsNull (&Form->ConfigRequestHead, Link)) {
    ConfigInfo = HII_FORM_CONFIG_REQUEST_FROM_LINK (Link);
    Link       = GetNextNode (&Form->ConfigRequestHead, Link);

    Storage = ConfigInfo->Storage;
    if (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE) {
      continue;
    }

    //
    // Skip if there is no RequestElement
    //
    if (ConfigInfo->ElementCount == 0) {
      continue;
    }

    Status = StorageToConfigResp (ConfigInfo->Storage, &ConfigResp, ConfigInfo->ConfigRequest);
    if (EFI_ERROR (Status)) {
      return Status;
    }

    Status = gBS->LocateProtocol (
                    &gEfiHiiConfigRoutingProtocolGuid,
                    NULL,
                    (VOID **)&HiiConfigRouting
                    );
    if (EFI_ERROR (Status)) {
      return Status;
    }

    Status = HiiConfigRouting->RouteConfig (
                                 HiiConfigRouting,
                                 ConfigResp,
                                 &Progress
                                 );

    if (EFI_ERROR (Status)) {
      FreePool (ConfigResp);
      continue;
    }

    FreePool (ConfigResp);
  }

  return Status;
}

/**
  Save Question Value to the memory, but not to storage.

  @param[in]     FormSet                FormSet data structure.
  @param[in]     Form                   Form data structure.
  @param[in,out] Question               Pointer to the Question.
  @param[in]     QuestionValue          New Question Value to be set.

  @retval EFI_SUCCESS            The question value has been set successfully.
  @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.

**/
EFI_STATUS
SetQuestionValue (
  IN     HII_FORMSET          *FormSet,
  IN     HII_FORM             *Form,
  IN OUT HII_STATEMENT        *Question,
  IN     HII_STATEMENT_VALUE  *QuestionValue
  )
{
  UINT8                *Src;
  UINTN                BufferLen;
  UINTN                StorageWidth;
  HII_FORMSET_STORAGE  *Storage;
  CHAR16               *ValueStr;
  BOOLEAN              IsBufferStorage;
  UINT8                *TemBuffer;
  CHAR16               *TemName;
  CHAR16               *TemString;
  UINTN                Index;
  HII_NAME_VALUE_NODE  *Node;
  EFI_STATUS           Status;

  if ((FormSet == NULL) || (Form == NULL) || (Question == NULL) || (QuestionValue == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  Status = EFI_SUCCESS;
  Node   = NULL;

  //
  // If Question value is provided by an Expression, then it is read only
  //
  if ((Question->ValueExpression != NULL) || (Question->Value.Type != QuestionValue->Type)) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Before set question value, evaluate its write expression.
  //
  if ((Question->WriteExpression != NULL) && (Form->FormType == STANDARD_MAP_FORM_TYPE)) {
    Status = EvaluateHiiExpression (FormSet, Form, Question->WriteExpression);
    if (EFI_ERROR (Status)) {
      return Status;
    }
  }

  Storage = Question->Storage;
  if (Storage != NULL) {
    StorageWidth = Question->StorageWidth;
    if (Question->Value.Type == EFI_IFR_TYPE_BUFFER) {
      Question->Value.BufferLen = QuestionValue->BufferLen;
      Question->Value.Buffer    = AllocateCopyPool (QuestionValue->BufferLen, QuestionValue->Buffer);
      if (Question->Value.Buffer == NULL) {
        return EFI_OUT_OF_RESOURCES;
      }

      Question->Value.BufferValueType = QuestionValue->BufferValueType;
      Src                             = Question->Value.Buffer;
    } else if (Question->Value.Type == EFI_IFR_TYPE_STRING) {
      Question->Value.Value.string = QuestionValue->Value.string;
      TemString                    = HiiGetString (FormSet->HiiHandle, QuestionValue->Value.string, NULL);
      if (TemString == NULL) {
        return EFI_ABORTED;
      }

      Question->Value.BufferLen = Question->StorageWidth;
      Question->Value.Buffer    = AllocateZeroPool (Question->StorageWidth);
      if (Question->Value.Buffer == NULL) {
        FreePool (TemString);
        return EFI_OUT_OF_RESOURCES;
      }

      CopyMem (Question->Value.Buffer, TemString, StrSize (TemString));
      Src = Question->Value.Buffer;
      FreePool (TemString);
    } else {
      CopyMem (&Question->Value.Value, &QuestionValue->Value, sizeof (EFI_IFR_TYPE_VALUE));
      Src = (UINT8 *)&Question->Value.Value;
    }

    if ((Storage->Type == EFI_HII_VARSTORE_BUFFER) || (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER)) {
      IsBufferStorage = TRUE;
    } else {
      IsBufferStorage = FALSE;
    }

    if (IsBufferStorage) {
      //
      // If the Question refer to bit filed, copy the value in related bit filed to storage edit buffer.
      //
      if (Question->QuestionReferToBitField) {
        SetBitsQuestionValue (Question, Storage->Buffer + Question->VarStoreInfo.VarOffset, (UINT32)(*Src));
      } else {
        CopyMem (Storage->Buffer + Question->VarStoreInfo.VarOffset, Src, StorageWidth);
      }
    } else {
      if (Question->Value.Type == EFI_IFR_TYPE_STRING) {
        //
        // Allocate enough string buffer.
        //
        ValueStr  = NULL;
        BufferLen = ((StrLen ((CHAR16 *)Src) * 4) + 1) * sizeof (CHAR16);
        ValueStr  = AllocatePool (BufferLen);
        if (ValueStr == NULL) {
          if (Question->Value.Buffer != NULL) {
            FreePool (Question->Value.Buffer);
          }

          return EFI_OUT_OF_RESOURCES;
        }

        //
        // Convert Unicode String to Config String, e.g. "ABCD" => "0041004200430044"
        //
        TemName   = (CHAR16 *)Src;
        TemString = ValueStr;
        for ( ; *TemName != L'\0'; TemName++) {
          UnicodeValueToStringS (
            TemString,
            BufferLen - ((UINTN)TemString - (UINTN)ValueStr),
            PREFIX_ZERO | RADIX_HEX,
            *TemName,
            4
            );
          TemString += StrnLenS (TemString, (BufferLen - ((UINTN)TemString - (UINTN)ValueStr)) / sizeof (CHAR16));
        }
      } else {
        BufferLen = StorageWidth * 2 + 1;
        ValueStr  = AllocateZeroPool (BufferLen * sizeof (CHAR16));
        if (ValueStr == NULL) {
          if (Question->Value.Buffer != NULL) {
            FreePool (Question->Value.Buffer);
          }

          return EFI_OUT_OF_RESOURCES;
        }

        //
        // Convert Buffer to Hex String
        //
        TemBuffer = Src + StorageWidth - 1;
        TemString = ValueStr;
        for (Index = 0; Index < StorageWidth; Index++, TemBuffer--) {
          UnicodeValueToStringS (
            TemString,
            BufferLen * sizeof (CHAR16) - ((UINTN)TemString - (UINTN)ValueStr),
            PREFIX_ZERO | RADIX_HEX,
            *TemBuffer,
            2
            );
          TemString += StrnLenS (TemString, BufferLen - ((UINTN)TemString - (UINTN)ValueStr) / sizeof (CHAR16));
        }
      }

      Status = SetValueByName (Storage, Question->VariableName, ValueStr, &Node);
      FreePool (ValueStr);
      if (EFI_ERROR (Status)) {
        if (Question->Value.Buffer != NULL) {
          FreePool (Question->Value.Buffer);
        }

        return Status;
      }
    }
  } else {
    if (Question->Value.Type == EFI_IFR_TYPE_BUFFER) {
      Question->Value.BufferLen = QuestionValue->BufferLen;
      Question->Value.Buffer    = AllocateCopyPool (QuestionValue->BufferLen, QuestionValue->Buffer);
      if (Question->Value.Buffer == NULL) {
        return EFI_OUT_OF_RESOURCES;
      }

      Question->Value.BufferValueType = QuestionValue->BufferValueType;
    } else if (Question->Value.Type == EFI_IFR_TYPE_STRING) {
      Question->Value.Value.string = QuestionValue->Value.string;
      TemString                    = HiiGetString (FormSet->HiiHandle, QuestionValue->Value.string, NULL);
      if (TemString == NULL) {
        return EFI_ABORTED;
      }

      Question->Value.BufferLen = (UINT16)StrSize (TemString);
      Question->Value.Buffer    = AllocateZeroPool (QuestionValue->BufferLen);
      if (Question->Value.Buffer == NULL) {
        FreePool (TemString);
        return EFI_OUT_OF_RESOURCES;
      }

      CopyMem (Question->Value.Buffer, TemString, StrSize (TemString));
      FreePool (TemString);
    } else {
      CopyMem (&Question->Value.Value, &QuestionValue->Value, sizeof (EFI_IFR_TYPE_VALUE));
    }
  }

  return Status;
}

/**
  Get Question's current Value from storage.

  @param[in]     FormSet                FormSet data structure.
  @param[in]     Form                   Form data structure.
  @param[in,out] Question               Question to be initialized.

  @return the current Question Value in storage if success.
  @return NULL if Question is not found or any error occurs.

**/
HII_STATEMENT_VALUE *
RetrieveQuestion (
  IN     HII_FORMSET    *FormSet,
  IN     HII_FORM       *Form,
  IN OUT HII_STATEMENT  *Question
  )
{
  EFI_STATUS                       Status;
  EFI_HII_CONFIG_ROUTING_PROTOCOL  *HiiConfigRouting;
  EFI_BROWSER_ACTION_REQUEST       ActionRequest;
  HII_FORMSET_STORAGE              *Storage;
  HII_STATEMENT_VALUE              *QuestionValue;
  EFI_IFR_TYPE_VALUE               *TypeValue;
  EFI_TIME                         EfiTime;
  BOOLEAN                          Enabled;
  BOOLEAN                          Pending;
  UINT8                            *Dst;
  UINTN                            StorageWidth;
  CHAR16                           *ConfigRequest;
  CHAR16                           *Progress;
  CHAR16                           *Result;
  CHAR16                           *ValueStr;
  UINTN                            Length;
  BOOLEAN                          IsBufferStorage;
  CHAR16                           *NewHiiString;

  if ((FormSet == NULL) || (Form == NULL) || (Question == NULL)) {
    return NULL;
  }

  Status   = EFI_SUCCESS;
  ValueStr = NULL;
  Result   = NULL;

  QuestionValue = AllocateZeroPool (sizeof (HII_STATEMENT_VALUE));
  if (QuestionValue == NULL) {
    return NULL;
  }

  QuestionValue->Type      = Question->Value.Type;
  QuestionValue->BufferLen = Question->Value.BufferLen;
  if (QuestionValue->BufferLen != 0) {
    QuestionValue->Buffer = AllocateZeroPool (QuestionValue->BufferLen);
    if (QuestionValue->Buffer == NULL) {
      FreePool (QuestionValue);
      return NULL;
    }
  }

  //
  // Question value is provided by RTC
  //
  Storage      = Question->Storage;
  StorageWidth = Question->StorageWidth;

  if (Storage == NULL) {
    //
    // It's a Question without storage, or RTC date/time
    //
    if ((Question->Operand == EFI_IFR_DATE_OP) || (Question->Operand == EFI_IFR_TIME_OP)) {
      //
      // Date and time define the same Flags bit
      //
      switch (Question->ExtraData.Flags & EFI_QF_DATE_STORAGE) {
        case QF_DATE_STORAGE_TIME:

          Status = gRT->GetTime (&EfiTime, NULL);
          break;

        case QF_DATE_STORAGE_WAKEUP:

          Status = gRT->GetWakeupTime (&Enabled, &Pending, &EfiTime);
          break;

        case QF_DATE_STORAGE_NORMAL:
        default:

          goto ON_ERROR;
      }

      if (EFI_ERROR (Status)) {
        if (Question->Operand == EFI_IFR_DATE_OP) {
          QuestionValue->Value.date.Year  = 0xff;
          QuestionValue->Value.date.Month = 0xff;
          QuestionValue->Value.date.Day   = 0xff;
        } else {
          QuestionValue->Value.time.Hour   = 0xff;
          QuestionValue->Value.time.Minute = 0xff;
          QuestionValue->Value.time.Second = 0xff;
        }

        return QuestionValue;
      }

      if (Question->Operand == EFI_IFR_DATE_OP) {
        QuestionValue->Value.date.Year  = EfiTime.Year;
        QuestionValue->Value.date.Month = EfiTime.Month;
        QuestionValue->Value.date.Day   = EfiTime.Day;
      } else {
        QuestionValue->Value.time.Hour   = EfiTime.Hour;
        QuestionValue->Value.time.Minute = EfiTime.Minute;
        QuestionValue->Value.time.Second = EfiTime.Second;
      }
    } else {
      if (((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != EFI_IFR_FLAG_CALLBACK) ||
          (FormSet->ConfigAccess == NULL))
      {
        goto ON_ERROR;
      }

      if (QuestionValue->Type == EFI_IFR_TYPE_BUFFER) {
        //
        // For OrderedList, passing in the value buffer to Callback()
        //
        TypeValue = (EFI_IFR_TYPE_VALUE *)QuestionValue->Buffer;
      } else {
        TypeValue = &QuestionValue->Value;
      }

      ActionRequest = EFI_BROWSER_ACTION_REQUEST_NONE;
      Status        = FormSet->ConfigAccess->Callback (
                                               FormSet->ConfigAccess,
                                               EFI_BROWSER_ACTION_RETRIEVE,
                                               Question->QuestionId,
                                               QuestionValue->Type,
                                               TypeValue,
                                               &ActionRequest
                                               );

      if (!EFI_ERROR (Status) && (QuestionValue->Type == EFI_IFR_TYPE_STRING)) {
        if (TypeValue->string == 0) {
          goto ON_ERROR;
        }

        NewHiiString = GetTokenString (TypeValue->string, FormSet->HiiHandle);
        if (NewHiiString == NULL) {
          goto ON_ERROR;
        }

        QuestionValue->Buffer = AllocatePool (StrSize (NewHiiString));
        if (QuestionValue->Buffer == NULL) {
          FreePool (NewHiiString);
          goto ON_ERROR;
        }

        CopyMem (QuestionValue->Buffer, NewHiiString, StrSize (NewHiiString));
        QuestionValue->BufferLen = (UINT16)StrSize (NewHiiString);

        FreePool (NewHiiString);
      }
    }

    return QuestionValue;
  }

  //
  // Question value is provided by EFI variable
  //
  if (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE) {
    if ((QuestionValue->Type != EFI_IFR_TYPE_BUFFER) && (QuestionValue->Type != EFI_IFR_TYPE_STRING)) {
      Dst          = QuestionValue->Buffer;
      StorageWidth = QuestionValue->BufferLen;
    } else {
      Dst          = (UINT8 *)&QuestionValue->Value;
      StorageWidth = sizeof (EFI_IFR_TYPE_VALUE);
    }

    Status = gRT->GetVariable (
                    Question->VariableName,
                    &Storage->Guid,
                    NULL,
                    &StorageWidth,
                    Dst
                    );

    return QuestionValue;
  }

  Status = gBS->LocateProtocol (
                  &gEfiHiiConfigRoutingProtocolGuid,
                  NULL,
                  (VOID **)&HiiConfigRouting
                  );
  if (EFI_ERROR (Status)) {
    goto ON_ERROR;
  }

  if (QuestionValue->BufferLen != 0) {
    Dst = QuestionValue->Buffer;
  } else {
    Dst = (UINT8 *)&QuestionValue->Value;
  }

  if ((Storage->Type == EFI_HII_VARSTORE_BUFFER) || (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER)) {
    IsBufferStorage = TRUE;
  } else {
    IsBufferStorage = FALSE;
  }

  Storage = GetFstStgFromVarId (FormSet, Question->VarStoreId);
  if (Storage == NULL) {
    goto ON_ERROR;
  }

  //
  // <ConfigRequest> ::= <ConfigHdr> + <BlockName> || <ConfigHdr> + "&" + <VariableName>
  //
  if (IsBufferStorage) {
    Length  = StrLen (Storage->ConfigHdr);
    Length += StrLen (Question->BlockName);
  } else {
    Length  = StrLen (Storage->ConfigHdr);
    Length += StrLen (Question->VariableName) + 1;
  }

  ConfigRequest = AllocatePool ((Length + 1) * sizeof (CHAR16));
  if (ConfigRequest == NULL) {
    goto ON_ERROR;
  }

  StrCpyS (ConfigRequest, Length + 1, Storage->ConfigHdr);
  if (IsBufferStorage) {
    StrCatS (ConfigRequest, Length + 1, Question->BlockName);
  } else {
    StrCatS (ConfigRequest, Length + 1, L"&");
    StrCatS (ConfigRequest, Length + 1, Question->VariableName);
  }

  //
  // Request current settings from Configuration Driver
  //
  Status = HiiConfigRouting->ExtractConfig (
                               HiiConfigRouting,
                               ConfigRequest,
                               &Progress,
                               &Result
                               );
  FreePool (ConfigRequest);
  if (EFI_ERROR (Status)) {
    goto ON_ERROR;
  }

  if (IsBufferStorage) {
    ValueStr = StrStr (Result, L"&VALUE");
    if (ValueStr == NULL) {
      FreePool (Result);
      goto ON_ERROR;
    }

    ValueStr = ValueStr + 6;
  } else {
    ValueStr = Result + Length;
  }

  if (*ValueStr != '=') {
    FreePool (Result);
    goto ON_ERROR;
  }

  ValueStr++;
  Status = BufferToQuestionValue (Question, ValueStr, QuestionValue);
  if (EFI_ERROR (Status)) {
    FreePool (Result);
    goto ON_ERROR;
  }

  if (Result != NULL) {
    FreePool (Result);
  }

  return QuestionValue;

ON_ERROR:

  if (QuestionValue->Buffer != NULL) {
    FreePool (QuestionValue->Buffer);
  }

  FreePool (QuestionValue);

  return NULL;
}
