/*++

Copyright (c) 2006, Intel Corporation                                                         
All rights reserved. This program and the accompanying materials                          
are licensed and made available under the terms and conditions of the BSD License         
which accompanies this distribution.  The full text of the license may be found at        
http://opensource.org/licenses/bsd-license.php                                            
                                                                                          
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             

Module Name:

  FrontPage.c

Abstract:

  FrontPage routines to handle the callbacks and browser calls
  
--*/

#include "Bds.h"
#include "FrontPage.h"
#include "String.h"

EFI_GUID                    mProcessorSubClass  = EFI_PROCESSOR_SUBCLASS_GUID;
EFI_GUID                    mMemorySubClass     = EFI_MEMORY_SUBCLASS_GUID;
EFI_GUID                    mMiscSubClass       = EFI_MISC_SUBCLASS_GUID;

UINT16                      mLastSelection;
EFI_HII_HANDLE              gFrontPageHandle;
EFI_HANDLE                  FrontPageCallbackHandle;
EFI_FORM_CALLBACK_PROTOCOL  FrontPageCallback;
EFI_FORM_BROWSER_PROTOCOL   *gBrowser;
UINTN                       gCallbackKey;
BOOLEAN                     gConnectAllHappened = FALSE;

extern EFI_HII_HANDLE       gFrontPageHandle;
extern EFI_GUID             gBdsStringPackGuid;

EFI_STATUS
EFIAPI
FrontPageCallbackRoutine (
  IN EFI_FORM_CALLBACK_PROTOCOL       *This,
  IN UINT16                           KeyValue,
  IN EFI_IFR_DATA_ARRAY               *DataArray,
  OUT EFI_HII_CALLBACK_PACKET         **Packet
  )
/*++

Routine Description:

  This is the function that is called to provide results data to the driver.  This data
  consists of a unique key which is used to identify what data is either being passed back
  or being asked for. 

Arguments:

  KeyValue -        A unique value which is sent to the original exporting driver so that it
                    can identify the type of data to expect.  The format of the data tends to
                    vary based on the op-code that geerated the callback.

  Data -            A pointer to the data being sent to the original exporting driver.

Returns: 

--*/
{
  CHAR16        *LanguageString;
  UINTN         Count;
  CHAR16        UnicodeLang[3];
  CHAR8         Lang[3];
  EFI_STATUS    Status;
  UINTN         Index;
  CHAR16        *TmpStr;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL Foreground;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL Background;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color;

  SetMem (&Foreground, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL), 0xff);
  SetMem (&Background, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL), 0x0);
  SetMem (&Color, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL), 0xff);

  Count = 0;

  //
  // The first 4 entries in the Front Page are to be GUARANTEED to remain constant so IHV's can
  // describe to their customers in documentation how to find their setup information (namely
  // under the device manager and specific buckets)
  //
  switch (KeyValue) {
  case 0x0001:
    //
    // This is the continue - clear the screen and return an error to get out of FrontPage loop
    //
    gCallbackKey = 1;
    break;

  case 0x1234:
    //
    // Collect the languages from what our current Language support is based on our VFR
    //
    Hii->GetPrimaryLanguages (Hii, gFrontPageHandle, &LanguageString);

    //
    // Based on the DataArray->Data->Data value, we can determine
    // which language was chosen by the user
    //
    for (Index = 0; Count != (UINTN) (((EFI_IFR_DATA_ENTRY *) (DataArray + 1))->Data); Index += 3) {
      Count++;
    }
    //
    // Preserve the choice the user made
    //
    mLastSelection = (UINT16) Count;

    //
    // The Language (in Unicode format) the user chose
    //
    CopyMem (UnicodeLang, &LanguageString[Index], 6);

    //
    // Convert Unicode to ASCII (Since the ISO standard assumes ASCII equivalent abbreviations
    // we can be safe in converting this Unicode stream to ASCII without any loss in meaning.
    //
    for (Index = 0; Index < 3; Index++) {
      Lang[Index] = (CHAR8) UnicodeLang[Index];
    }

    Status = gRT->SetVariable (
                    L"Lang",
                    &gEfiGlobalVariableGuid,
                    EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
                    3,
                    Lang
                    );

    gBS->FreePool (LanguageString);
    gCallbackKey = 2;
    break;

  case 0x1064:
    //
    // Boot Manager
    //
    gCallbackKey = 3;
    break;

  case 0x8567:
    //
    // Device Manager
    //
    gCallbackKey = 4;
    break;

  case 0x9876:
    //
    // Boot Maintenance Manager
    //
    gCallbackKey = 5;
    break;

  case 0xFFFE:

    break;

  case 0xFFFF:
    //
    // FrontPage TimeOut Callback
    //
    TmpStr = GetStringById (STRING_TOKEN (STR_START_BOOT_OPTION));
    if (TmpStr != NULL) {
      PlatformBdsShowProgress (
        Foreground,
        Background,
        TmpStr,
        Color,
        (UINTN) (((EFI_IFR_DATA_ENTRY *) (DataArray+1))->Data),
        0
        );
      gBS->FreePool (TmpStr);
    }
    break;

  default:
    gCallbackKey = 0;
    break;
  }

  return EFI_SUCCESS;
}

EFI_STATUS
InitializeFrontPage (
  BOOLEAN                         ReInitializeStrings
  )
/*++

Routine Description:
  
  Initialize HII information for the FrontPage

Arguments:
  None
            
Returns:
  EFI_SUCCESS       - The operation is successful.
  EFI_DEVICE_ERROR  - If the dynamic opcode creation failed.

--*/
{
  EFI_STATUS          Status;
  EFI_HII_PACKAGES    *PackageList;
  EFI_HII_UPDATE_DATA *UpdateData;
  IFR_OPTION          *OptionList;
  CHAR16              *LanguageString;
  UINTN               OptionCount;
  UINTN               Index;
  STRING_REF          Token;
  UINT16              Key;
  CHAR8               AsciiLang[4];
  CHAR16              UnicodeLang[4];
  CHAR16              Lang[4];
  CHAR16              *StringBuffer;
  UINTN               BufferSize;
  UINT8               *TempBuffer;

  UpdateData  = NULL;
  OptionList  = NULL;

  if (ReInitializeStrings) {
    //
    // BugBug: Dont' use a goto
    //
    goto ReInitStrings;
  }
  //
  // Go ahead and initialize the Device Manager
  //
  InitializeDeviceManager ();

  //
  // BugBug: if FrontPageVfrBin is generated by a tool, why are we patching it here
  //
  TempBuffer    = (UINT8 *) FrontPageVfrBin;
  TempBuffer    = TempBuffer + sizeof (EFI_HII_PACK_HEADER);
  TempBuffer    = (UINT8 *) &((EFI_IFR_FORM_SET *) TempBuffer)->NvDataSize;
  *TempBuffer   = 1;

  gCallbackKey  = 0;

  PackageList   = PreparePackages (1, &gBdsStringPackGuid, FrontPageVfrBin);

  Status        = Hii->NewPack (Hii, PackageList, &gFrontPageHandle);

  gBS->FreePool (PackageList);

  //
  // There will be only one FormConfig in the system
  // If there is another out there, someone is trying to install us
  // again.  Fail that scenario.
  //
  Status = gBS->LocateProtocol (
                  &gEfiFormBrowserProtocolGuid,
                  NULL,
                  (VOID**)&gBrowser
                  );

  //
  // This example does not implement worker functions
  // for the NV accessor functions.  Only a callback evaluator
  //
  FrontPageCallback.NvRead    = NULL;
  FrontPageCallback.NvWrite   = NULL;
  FrontPageCallback.Callback  = FrontPageCallbackRoutine;

  //
  // Install protocol interface
  //
  FrontPageCallbackHandle = NULL;
  Status = gBS->InstallProtocolInterface (
                  &FrontPageCallbackHandle,
                  &gEfiFormCallbackProtocolGuid,
                  EFI_NATIVE_INTERFACE,
                  &FrontPageCallback
                  );
  ASSERT_EFI_ERROR (Status);

ReInitStrings:
  //
  // BugBug: This logic is in BdsInitLanguage. It should not be in two places!
  //
  BufferSize = 4;
  Status = gRT->GetVariable (
                  L"Lang",
                  &gEfiGlobalVariableGuid,
                  NULL,
                  &BufferSize,
                  AsciiLang
                  );

  for (Index = 0; Index < 3; Index++) {
    UnicodeLang[Index] = (CHAR16) AsciiLang[Index];
  }

  UnicodeLang[3] = 0;

  //
  // Allocate space for creation of UpdateData Buffer
  //
  UpdateData = AllocateZeroPool (0x1000);
  ASSERT (UpdateData != NULL);

  OptionList = AllocateZeroPool (0x1000);
  ASSERT (OptionList != NULL);

  //
  // Flag update pending in FormSet
  //
  UpdateData->FormSetUpdate = TRUE;
  //
  // Register CallbackHandle data for FormSet
  //
  UpdateData->FormCallbackHandle = (EFI_PHYSICAL_ADDRESS) (UINTN) FrontPageCallbackHandle;
  UpdateData->FormUpdate  = FALSE;
  UpdateData->FormTitle   = 0;
  UpdateData->DataCount   = 1;

  //
  // Collect the languages from what our current Language support is based on our VFR
  //
  Hii->GetPrimaryLanguages (Hii, gFrontPageHandle, &LanguageString);

  OptionCount = 0;

  //
  // Try for a 512 byte Buffer
  //
  BufferSize = 0x200;

  //
  // Allocate memory for our Form binary
  //
  StringBuffer = AllocateZeroPool (BufferSize);
  ASSERT (StringBuffer != NULL);

  for (Index = 0; LanguageString[Index] != 0; Index += 3) {
    Token = 0;
    CopyMem (Lang, &LanguageString[Index], 6);
    Lang[3] = 0;

    if (!StrCmp (Lang, UnicodeLang)) {
      mLastSelection = (UINT16) OptionCount;
    }

    Status = Hii->GetString (Hii, gStringPackHandle, 1, TRUE, Lang, &BufferSize, StringBuffer);
    Hii->NewString (Hii, NULL, gStringPackHandle, &Token, StringBuffer);
    CopyMem (&OptionList[OptionCount].StringToken, &Token, sizeof (UINT16));
    CopyMem (&OptionList[OptionCount].Value, &OptionCount, sizeof (UINT16));
    Key = 0x1234;
    CopyMem (&OptionList[OptionCount].Key, &Key, sizeof (UINT16));
    OptionList[OptionCount].Flags = EFI_IFR_FLAG_INTERACTIVE | EFI_IFR_FLAG_NV_ACCESS;
    OptionCount++;
  }

  gBS->FreePool (LanguageString);

  if (ReInitializeStrings) {
    gBS->FreePool (StringBuffer);
    gBS->FreePool (OptionList);
    return EFI_SUCCESS;
  }

  Status = CreateOneOfOpCode (
            FRONT_PAGE_QUESTION_ID,                               // Question ID
            FRONT_PAGE_DATA_WIDTH,                                // Data Width
            (STRING_REF) STRING_TOKEN (STR_LANGUAGE_SELECT),      // Prompt Token
            (STRING_REF) STRING_TOKEN (STR_LANGUAGE_SELECT_HELP), // Help Token
            OptionList,       // List of Options
            OptionCount,      // Number of Options
            &UpdateData->Data // Data Buffer
            );

  //
  // Assign the number of options and the oneof and endoneof op-codes to count
  //
  UpdateData->DataCount = (UINT8) (OptionCount + 2);

  Hii->UpdateForm (Hii, gFrontPageHandle, (EFI_FORM_LABEL) 0x0002, TRUE, UpdateData);

  gBS->FreePool (UpdateData);
  //
  // gBS->FreePool (OptionList);
  //
  gBS->FreePool (StringBuffer);
  return Status;
}

EFI_STATUS
CallFrontPage (
  VOID
  )
/*++

Routine Description:
  
  Call the browser and display the front page

Arguments:
  
  None
  
Returns:

--*/
{
  EFI_STATUS  Status;
  UINT8       FakeNvRamMap[1];
  BOOLEAN     FrontPageMenuResetRequired;

  //
  // Begin waiting for USER INPUT
  //
  REPORT_STATUS_CODE (
        EFI_PROGRESS_CODE,
        (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_PC_INPUT_WAIT)
        );

  FakeNvRamMap[0] = (UINT8) mLastSelection;
  FrontPageMenuResetRequired = FALSE;
  Status = gBrowser->SendForm (
                      gBrowser,
                      TRUE,                     // Use the database
                      &gFrontPageHandle,        // The HII Handle
                      1,
                      NULL,
                      FrontPageCallbackHandle,  // This is the handle that the interface to the callback was installed on
                      FakeNvRamMap,
                      NULL,
                      &FrontPageMenuResetRequired
                      );
  //
  // Check whether user change any option setting which needs a reset to be effective
  //                      
  if (FrontPageMenuResetRequired) {
    EnableResetRequired ();
  }

  Hii->ResetStrings (Hii, gFrontPageHandle);

  return Status;
}

EFI_STATUS
GetStringFromToken (
  IN      EFI_GUID                  *ProducerGuid,
  IN      STRING_REF                Token,
  OUT     CHAR16                    **String
  )
/*++

Routine Description:
  
  Acquire the string associated with the ProducerGuid and return it.

Arguments:
  
  ProducerGuid - The Guid to search the HII database for
  Token - The token value of the string to extract
  String - The string that is extracted
  
Returns:

  EFI_SUCCESS - The function returns EFI_SUCCESS always.

--*/
{
  EFI_STATUS      Status;
  UINT16          HandleBufferLength;
  EFI_HII_HANDLE  *HiiHandleBuffer;
  UINTN           StringBufferLength;
  UINTN           NumberOfHiiHandles;
  UINTN           Index;
  UINT16          Length;
  EFI_GUID        HiiGuid;

  //
  // Initialize params.
  //
  HandleBufferLength  = 0;
  HiiHandleBuffer     = NULL;

  //
  // Get all the Hii handles
  //
  Status = BdsLibGetHiiHandles (Hii, &HandleBufferLength, &HiiHandleBuffer);
  ASSERT_EFI_ERROR (Status);

  //
  // Get the Hii Handle that matches the StructureNode->ProducerName
  //
  NumberOfHiiHandles = HandleBufferLength / sizeof (EFI_HII_HANDLE);
  for (Index = 0; Index < NumberOfHiiHandles; Index++) {
    Length = 0;
    Status = ExtractDataFromHiiHandle (
              HiiHandleBuffer[Index],
              &Length,
              NULL,
              &HiiGuid
              );
    if (CompareGuid (ProducerGuid, &HiiGuid)) {
      break;
    }
  }
  //
  // Find the string based on the current language
  //
  StringBufferLength  = 0x100;
  *String             = AllocateZeroPool (0x100);
  Status = Hii->GetString (
                  Hii,
                  HiiHandleBuffer[Index],
                  Token,
                  FALSE,
                  NULL,
                  &StringBufferLength,
                  *String
                  );

  if (EFI_ERROR (Status)) {
    gBS->FreePool (*String);
    *String = GetStringById (STRING_TOKEN (STR_MISSING_STRING));
  }

  gBS->FreePool (HiiHandleBuffer);
  return EFI_SUCCESS;
}

VOID
ConvertProcessorToString (
  IN  EFI_PROCESSOR_CORE_FREQUENCY_DATA *ProcessorFrequency,
  OUT CHAR16                            **String
  )
/*++

Routine Description:
  
  Convert Processor Frequency Data to a string

Arguments:
  
  ProcessorFrequency - The frequency data to process
  String - The string that is created
  
Returns:

--*/
{
  CHAR16  *StringBuffer;
  UINTN   Index;
  UINT32  FreqMhz;

  if (ProcessorFrequency->Exponent >= 6) {
    FreqMhz = ProcessorFrequency->Value;
    for (Index = 0; Index < (UINTN) (ProcessorFrequency->Exponent - 6); Index++) {
      FreqMhz *= 10;
    }
  } else {
    FreqMhz = 0;
  }

  StringBuffer = AllocateZeroPool (0x20);
  ASSERT (StringBuffer != NULL);
  Index = UnicodeValueToString (StringBuffer, LEFT_JUSTIFY, FreqMhz / 1000, 3);
  StrCat (StringBuffer, L".");
  UnicodeValueToString (StringBuffer + Index + 1, PREFIX_ZERO, (FreqMhz % 1000) / 10, 2);
  StrCat (StringBuffer, L" GHz");

  *String = (CHAR16 *) StringBuffer;

  return ;
}

VOID
ConvertMemorySizeToString (
  IN  UINT32          MemorySize,
  OUT CHAR16          **String
  )
/*++

Routine Description:
  
  Convert Memory Size to a string

Arguments:
  
  MemorySize - The size of the memory to process
  String - The string that is created
  
Returns:

--*/
{
  CHAR16  *StringBuffer;

  StringBuffer = AllocateZeroPool (0x20);
  ASSERT (StringBuffer != NULL);
  UnicodeValueToString (StringBuffer, LEFT_JUSTIFY, MemorySize, 6);
  StrCat (StringBuffer, L" MB RAM");

  *String = (CHAR16 *) StringBuffer;

  return ;
}

VOID
UpdateFrontPageStrings (
  VOID
  )
/*++

Routine Description:
  
  Update the banner information for the Front Page based on DataHub information

Arguments:
  
  None
  
Returns:

--*/
{
  EFI_STATUS                        Status;
  STRING_REF                        TokenToUpdate;
  CHAR16                            *NewString;
  UINT64                            MonotonicCount;
  EFI_DATA_HUB_PROTOCOL             *DataHub;
  EFI_DATA_RECORD_HEADER            *Record;
  EFI_SUBCLASS_TYPE1_HEADER         *DataHeader;
  EFI_MISC_BIOS_VENDOR_DATA         *BiosVendor;
  EFI_MISC_SYSTEM_MANUFACTURER_DATA *SystemManufacturer;
  EFI_PROCESSOR_VERSION_DATA        *ProcessorVersion;
  EFI_PROCESSOR_CORE_FREQUENCY_DATA *ProcessorFrequency;
  EFI_MEMORY_ARRAY_START_ADDRESS_DATA *MemoryArray;
  CHAR8                             LangCode[3];
  CHAR16                            Lang[3];
  UINTN                             Size;
  UINTN                             Index;
  BOOLEAN                           Find[5];

  ZeroMem (Find, sizeof (Find));

  //
  // Update Front Page strings
  //
  Status = gBS->LocateProtocol (
                  &gEfiDataHubProtocolGuid,
                  NULL,
                  (VOID**)&DataHub
                  );
  ASSERT_EFI_ERROR (Status);

  Size = 3;

  Status = gRT->GetVariable (
                  L"Lang",
                  &gEfiGlobalVariableGuid,
                  NULL,
                  &Size,
                  LangCode
                  );

  for (Index = 0; Index < 3; Index++) {
    Lang[Index] = (CHAR16) LangCode[Index];
  }

  MonotonicCount  = 0;
  Record          = NULL;
  do {
    Status = DataHub->GetNextRecord (DataHub, &MonotonicCount, NULL, &Record);
    if (Record->DataRecordClass == EFI_DATA_RECORD_CLASS_DATA) {
      DataHeader = (EFI_SUBCLASS_TYPE1_HEADER *) (Record + 1);
      if (CompareGuid (&Record->DataRecordGuid, &mMiscSubClass) &&
          (DataHeader->RecordType == EFI_MISC_BIOS_VENDOR_RECORD_NUMBER)
          ) {
        BiosVendor = (EFI_MISC_BIOS_VENDOR_DATA *) (DataHeader + 1);
        GetStringFromToken (&Record->ProducerName, BiosVendor->BiosVersion, &NewString);
        TokenToUpdate = (STRING_REF) STR_FRONT_PAGE_BIOS_VERSION;
        Hii->NewString (Hii, Lang, gFrontPageHandle, &TokenToUpdate, NewString);
        gBS->FreePool (NewString);
        Find[0] = TRUE;
      }

      if (CompareGuid (&Record->DataRecordGuid, &mMiscSubClass) &&
          (DataHeader->RecordType == EFI_MISC_SYSTEM_MANUFACTURER_RECORD_NUMBER)
          ) {
        SystemManufacturer = (EFI_MISC_SYSTEM_MANUFACTURER_DATA *) (DataHeader + 1);
        GetStringFromToken (&Record->ProducerName, SystemManufacturer->SystemProductName, &NewString);
        TokenToUpdate = (STRING_REF) STR_FRONT_PAGE_COMPUTER_MODEL;
        Hii->NewString (Hii, Lang, gFrontPageHandle, &TokenToUpdate, NewString);
        gBS->FreePool (NewString);
        Find[1] = TRUE;
      }

      if (CompareGuid (&Record->DataRecordGuid, &mProcessorSubClass) &&
          (DataHeader->RecordType == ProcessorVersionRecordType)
          ) {
        ProcessorVersion = (EFI_PROCESSOR_VERSION_DATA *) (DataHeader + 1);
        GetStringFromToken (&Record->ProducerName, *ProcessorVersion, &NewString);
        TokenToUpdate = (STRING_REF) STR_FRONT_PAGE_CPU_MODEL;
        Hii->NewString (Hii, Lang, gFrontPageHandle, &TokenToUpdate, NewString);
        gBS->FreePool (NewString);
        Find[2] = TRUE;
      }

      if (CompareGuid (&Record->DataRecordGuid, &mProcessorSubClass) &&
          (DataHeader->RecordType == ProcessorCoreFrequencyRecordType)
          ) {
        ProcessorFrequency = (EFI_PROCESSOR_CORE_FREQUENCY_DATA *) (DataHeader + 1);
        ConvertProcessorToString (ProcessorFrequency, &NewString);
        TokenToUpdate = (STRING_REF) STR_FRONT_PAGE_CPU_SPEED;
        Hii->NewString (Hii, Lang, gFrontPageHandle, &TokenToUpdate, NewString);
        gBS->FreePool (NewString);
        Find[3] = TRUE;
      }

      if (CompareGuid (&Record->DataRecordGuid, &mMemorySubClass) &&
          (DataHeader->RecordType == EFI_MEMORY_ARRAY_START_ADDRESS_RECORD_NUMBER)
          ) {
        MemoryArray = (EFI_MEMORY_ARRAY_START_ADDRESS_DATA *) (DataHeader + 1);
        ConvertMemorySizeToString((UINT32)(RShiftU64((MemoryArray->MemoryArrayEndAddress - 
                                  MemoryArray->MemoryArrayStartAddress + 1), 20)),
                                  &NewString);
        TokenToUpdate = (STRING_REF) STR_FRONT_PAGE_MEMORY_SIZE;
        Hii->NewString (Hii, Lang, gFrontPageHandle, &TokenToUpdate, NewString);
        gBS->FreePool (NewString);
        Find[4] = TRUE;
      }
    }
  } while (!EFI_ERROR (Status) && (MonotonicCount != 0) && !(Find[0] && Find[1] && Find[2] && Find[3] && Find[4]));

  return ;
}

VOID
PlatformBdsEnterFrontPage (
  IN UINT16                       TimeoutDefault,
  IN BOOLEAN                      ConnectAllHappened
  )
/*++

Routine Description:
  This function is the main entry of the platform setup entry.
  The function will present the main menu of the system setup, 
  this is the platform reference part and can be customize.
  
Arguments:
  TimeoutDefault     - The fault time out value before the system
                       continue to boot.
  ConnectAllHappened - The indicater to check if the connect all have
                       already happended.
  
Returns:
  None

--*/
{
  EFI_STATUS                    Status;
  EFI_HII_UPDATE_DATA           *UpdateData;
  EFI_CONSOLE_CONTROL_PROTOCOL  *ConsoleControl;

  //
  // Indicate if we need connect all in the platform setup
  //
  if (ConnectAllHappened) {
    gConnectAllHappened = TRUE;
  }
  //
  // Allocate space for creation of Buffer
  //
  UpdateData = AllocateZeroPool (0x1000);
  ASSERT (UpdateData != NULL);

  UpdateData->FormSetUpdate       = FALSE;
  UpdateData->FormCallbackHandle  = 0;
  UpdateData->FormUpdate          = FALSE;
  UpdateData->FormTitle           = 0;
  UpdateData->DataCount           = 1;

  //
  // Remove Banner Op-code if any at this label
  //
  Hii->UpdateForm (Hii, gFrontPageHandle, (EFI_FORM_LABEL) 0xFFFF, FALSE, UpdateData);

  //
  // Create Banner Op-code which reflects correct timeout value
  //
  CreateBannerOpCode (
    STRING_TOKEN (STR_TIME_OUT_PROMPT),
    TimeoutDefault,
    (UINT8) EFI_IFR_BANNER_TIMEOUT,
    &UpdateData->Data
    );

  //
  // Add Banner Op-code at this label
  //
  Hii->UpdateForm (Hii, gFrontPageHandle, (EFI_FORM_LABEL) 0xFFFF, TRUE, UpdateData);

  do {

    InitializeFrontPage (TRUE);

    //
    // Update Front Page strings
    //
    UpdateFrontPageStrings ();

    gCallbackKey = 0;
    PERF_START (0, "BdsTimeOut", "BDS", 0);
    Status = CallFrontPage ();
    PERF_END (0, "BdsTimeOut", "BDS", 0);

    //
    // If gCallbackKey is greater than 1 and less or equal to 5,
    // it will lauch configuration utilities.
    // 2 = set language
    // 3 = boot manager
    // 4 = device manager
    // 5 = boot maintainenance manager
    //
    if ((gCallbackKey > 0x0001) && (gCallbackKey <= 0x0005)) {
      REPORT_STATUS_CODE (
            EFI_PROGRESS_CODE,
            (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_PC_USER_SETUP)
            );
    }
    //
    // Based on the key that was set, we can determine what to do
    //
    switch (gCallbackKey) {
    //
    // The first 4 entries in the Front Page are to be GUARANTEED to remain constant so IHV's can
    // describe to their customers in documentation how to find their setup information (namely
    // under the device manager and specific buckets)
    //
    // These entries consist of the Continue, Select language, Boot Manager, and Device Manager
    //
    case 0x0001:
      //
      // User hit continue
      //
      break;

    case 0x0002:
      //
      // User made a language setting change - display front page again
      //
      break;

    case 0x0003:
      //
      // User chose to run the Boot Manager
      //
      CallBootManager ();
      break;

    case 0x0004:
      //
      // Display the Device Manager
      //
      do {
        CallDeviceManager();
      } while (gCallbackKey == 4);
      break;

    case 0x0005:
      //
      // Display the Boot Maintenance Manager
      //
      BdsStartBootMaint ();
      break;
    }

  } while ((Status == EFI_SUCCESS) && (gCallbackKey != 1));

  //
  //Will leave browser, check any reset required change is applied? if yes, reset system
  //
  SetupResetReminder ();
  
  //
  // Automatically load current entry
  // Note: The following lines of code only execute when Auto boot
  // takes affect
  //
  Status = gBS->LocateProtocol (&gEfiConsoleControlProtocolGuid, NULL, (VOID**)&ConsoleControl);
  ConsoleControl->SetMode (ConsoleControl, EfiConsoleControlScreenText);

}
