/**@file
  Dynamically Update the pages.
  
Copyright (c) 2006 - 2007 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.             

**/

#include "Bds.h"
#include "BootMaint.h"

EFI_GUID gTerminalDriverGuid = {
  0x9E863906, 0xA40F, 0x4875, {0x97, 0x7F, 0x5B, 0x93, 0xFF, 0x23, 0x7F, 0xC6}
};

VOID
RefreshUpdateData (
  IN BOOLEAN                      FormSetUpdate,
  IN EFI_PHYSICAL_ADDRESS         FormCallbackHandle,
  IN BOOLEAN                      FormUpdate,
  IN STRING_REF                   FormTitle,
  IN UINT16                       DataCount
  )
/*++
Routine Description:
  Refresh the global UpdateData structure.

Arguments:
  FormSetUpdate      - If TRUE, next variable is significant
  FormCallbackHandle - If not 0, will update FormSet with this info
  FormUpdate         - If TRUE, next variable is significant
  FormTitle          - If not 0, will update Form with this info
  DataCount          - The number of Data entries in this structure

Returns:
  None.
--*/
{
  UpdateData->FormSetUpdate = FormSetUpdate;
  if (FormSetUpdate) {
    ASSERT (0 != FormCallbackHandle);
    UpdateData->FormCallbackHandle = FormCallbackHandle;
  }

  UpdateData->FormUpdate  = FALSE;
  UpdateData->FormTitle   = FormTitle;
  UpdateData->DataCount   = DataCount;
}

VOID
UpdatePageStart (
  IN BMM_CALLBACK_DATA                *CallbackData,
  IN OUT UINT8                        **CurrentLocation
  )
{
  RefreshUpdateData (TRUE, (EFI_PHYSICAL_ADDRESS) (UINTN) CallbackData->BmmCallbackHandle, FALSE, 0, 0);

  if (!(CallbackData->BmmAskSaveOrNot)) {
    //
    // Add a "Go back to main page" tag in front of the form when there are no
    // "Apply changes" and "Discard changes" tags in the end of the form.
    //
    CreateGotoOpCode (
      FORM_MAIN_ID,
      STRING_TOKEN (STR_FORM_GOTO_MAIN),
      STRING_TOKEN (STR_FORM_GOTO_MAIN),
      EFI_IFR_FLAG_INTERACTIVE | EFI_IFR_FLAG_NV_ACCESS,
      FORM_MAIN_ID,
      *CurrentLocation
      );

    UpdateData->DataCount++;

    *CurrentLocation = *CurrentLocation + ((EFI_IFR_OP_HEADER *) (*CurrentLocation))->Length;
  }

}

VOID
UpdatePageEnd (
  IN BMM_CALLBACK_DATA                *CallbackData,
  IN UINT8                            *CurrentLocation
  )
{
  //
  // Create the "Apply changes" and "Discard changes" tags.
  //
  if (CallbackData->BmmAskSaveOrNot) {
    CreateGotoOpCode (
      FORM_MAIN_ID,
      STRING_TOKEN (STR_SAVE_AND_EXIT),
      STRING_TOKEN (STR_NULL_STRING),
      EFI_IFR_FLAG_INTERACTIVE | EFI_IFR_FLAG_NV_ACCESS,
      KEY_VALUE_SAVE_AND_EXIT,
      CurrentLocation
      );

    UpdateData->DataCount++;

    CurrentLocation = CurrentLocation + ((EFI_IFR_OP_HEADER *) CurrentLocation)->Length;

    CreateGotoOpCode (
      FORM_MAIN_ID,
      STRING_TOKEN (STR_NO_SAVE_AND_EXIT),
      STRING_TOKEN (STR_NULL_STRING),
      EFI_IFR_FLAG_INTERACTIVE | EFI_IFR_FLAG_NV_ACCESS,
      KEY_VALUE_NO_SAVE_AND_EXIT,
      CurrentLocation
      );

    UpdateData->DataCount++;
  }
  //
  // Ensure user can return to the main page.
  //
  if (0 == UpdateData->DataCount) {
    CreateGotoOpCode (
      FORM_MAIN_ID,
      STRING_TOKEN (STR_NO_SAVE_AND_EXIT),
      STRING_TOKEN (STR_NULL_STRING),
      EFI_IFR_FLAG_INTERACTIVE | EFI_IFR_FLAG_NV_ACCESS,
      KEY_VALUE_NO_SAVE_AND_EXIT,
      CurrentLocation
      );

    UpdateData->DataCount++;
  }

  CallbackData->Hii->UpdateForm (
                      CallbackData->Hii,
                      CallbackData->BmmHiiHandle,
                      CallbackData->BmmCurrentPageId,
                      TRUE,
                      UpdateData
                      );
}

VOID
CleanUpPage (
  IN EFI_FORM_LABEL                   LabelId,
  IN BMM_CALLBACK_DATA                *CallbackData
  )
{
  RefreshUpdateData (FALSE, 0, FALSE, 0, 0xff);

  //
  // Remove all op-codes from dynamic page
  //
  CallbackData->Hii->UpdateForm (
                      CallbackData->Hii,
                      CallbackData->BmmHiiHandle,
                      LabelId,
                      FALSE,
                      UpdateData
                      );
}

EFI_STATUS
BootThisFile (
  IN BM_FILE_CONTEXT                   *FileContext
  )
{
  EFI_STATUS        Status;
  UINTN             ExitDataSize;
  CHAR16            *ExitData;
  BDS_COMMON_OPTION *Option;

  Status                  = gBS->AllocatePool (EfiBootServicesData, sizeof (BDS_COMMON_OPTION), (VOID**) &Option);
  Option->Description     = FileContext->FileName;
  Option->DevicePath      = FileContext->DevicePath;
  Option->LoadOptionsSize = 0;
  Option->LoadOptions     = NULL;

  //
  // Since current no boot from removable media directly is allowed */
  //
  gST->ConOut->ClearScreen (gST->ConOut);

  ExitDataSize  = 0;

  Status        = BdsLibBootViaBootOption (Option, Option->DevicePath, &ExitDataSize, &ExitData);

  return Status;

}

VOID
UpdateConCOMPage (
  IN BMM_CALLBACK_DATA                *CallbackData
  )
{
  BM_MENU_ENTRY *NewMenuEntry;
  UINT16        Index;
  UINT8         *Location;
  EFI_STATUS    Status;
  VOID        	*Interface;

  Location                      = (UINT8 *) &UpdateData->Data;
  CallbackData->BmmAskSaveOrNot = FALSE;

  UpdatePageStart (CallbackData, &Location);

  Status = EfiLibLocateProtocol (&gTerminalDriverGuid, &Interface);
  if (!EFI_ERROR (Status)) {
    for (Index = 0; Index < TerminalMenu.MenuNumber; Index++) {
      NewMenuEntry = BOpt_GetMenuEntry (&TerminalMenu, Index);
 
      CreateGotoOpCode (
        FORM_CON_COM_SETUP_ID,
        NewMenuEntry->DisplayStringToken,
        STRING_TOKEN (STR_NULL_STRING),
        EFI_IFR_FLAG_INTERACTIVE | EFI_IFR_FLAG_NV_ACCESS,
        (UINT16) (TERMINAL_OPTION_OFFSET + Index),
        Location
        );
      Location = Location + ((EFI_IFR_OP_HEADER *) Location)->Length;
      UpdateData->DataCount++;
    }
  }

  UpdatePageEnd (CallbackData, Location);
}

VOID
UpdateBootDelPage (
  IN BMM_CALLBACK_DATA                *CallbackData
  )
{
  BM_MENU_ENTRY   *NewMenuEntry;
  BM_LOAD_CONTEXT *NewLoadContext;
  UINT16          Index;
  UINT8           *Location;

  Location                      = (UINT8 *) &UpdateData->Data;
  CallbackData->BmmAskSaveOrNot = TRUE;

  UpdatePageStart (CallbackData, &Location);
  CreateMenuStringToken (CallbackData, CallbackData->BmmHiiHandle, &BootOptionMenu);

  for (Index = 0; Index < BootOptionMenu.MenuNumber; Index++) {
    NewMenuEntry    = BOpt_GetMenuEntry (&BootOptionMenu, Index);
    NewLoadContext  = (BM_LOAD_CONTEXT *) NewMenuEntry->VariableContext;
    if (NewLoadContext->IsLegacy) {
      continue;
    }

    NewLoadContext->Deleted = FALSE;
    CallbackData->BmmFakeNvData->BootOptionDel[Index] = 0x00;

    CreateCheckBoxOpCode (
      (UINT16) (BOOT_OPTION_DEL_QUESTION_ID + Index),
      (UINT8) 1,
      NewMenuEntry->DisplayStringToken,
      NewMenuEntry->HelpStringToken,
      EFI_IFR_FLAG_INTERACTIVE | EFI_IFR_FLAG_NV_ACCESS,
      (UINT16) BOOT_OPTION_DEL_QUESTION_ID,
      Location
      );

    Location = Location + ((EFI_IFR_OP_HEADER *) Location)->Length;
    UpdateData->DataCount++;
  }

  UpdatePageEnd (CallbackData, Location);
}

VOID
UpdateDrvAddHandlePage (
  IN BMM_CALLBACK_DATA                *CallbackData
  )
{
  BM_MENU_ENTRY *NewMenuEntry;
  UINT16        Index;
  UINT8         *Location;

  Location                      = (UINT8 *) &UpdateData->Data;
  CallbackData->BmmAskSaveOrNot = FALSE;

  UpdatePageStart (CallbackData, &Location);

  for (Index = 0; Index < DriverMenu.MenuNumber; Index++) {
    NewMenuEntry = BOpt_GetMenuEntry (&DriverMenu, Index);

    CreateGotoOpCode (
      FORM_DRV_ADD_HANDLE_DESC_ID,
      NewMenuEntry->DisplayStringToken,
      STRING_TOKEN (STR_NULL_STRING),
      EFI_IFR_FLAG_INTERACTIVE | EFI_IFR_FLAG_NV_ACCESS,
      (UINT16) (HANDLE_OPTION_OFFSET + Index),
      Location
      );

    Location = Location + ((EFI_IFR_OP_HEADER *) Location)->Length;
    UpdateData->DataCount++;
  }

  UpdatePageEnd (CallbackData, Location);
}

VOID
UpdateDrvDelPage (
  IN BMM_CALLBACK_DATA                *CallbackData
  )
{
  BM_MENU_ENTRY   *NewMenuEntry;
  BM_LOAD_CONTEXT *NewLoadContext;
  UINT16          Index;
  UINT8           *Location;

  Location                      = (UINT8 *) &UpdateData->Data;
  CallbackData->BmmAskSaveOrNot = TRUE;

  UpdatePageStart (CallbackData, &Location);

  CreateMenuStringToken (CallbackData, CallbackData->BmmHiiHandle, &DriverOptionMenu);

  for (Index = 0; Index < DriverOptionMenu.MenuNumber; Index++) {
    NewMenuEntry            = BOpt_GetMenuEntry (&DriverOptionMenu, Index);

    NewLoadContext          = (BM_LOAD_CONTEXT *) NewMenuEntry->VariableContext;
    NewLoadContext->Deleted = FALSE;
    CallbackData->BmmFakeNvData->DriverOptionDel[Index] = 0x00;

    CreateCheckBoxOpCode (
      (UINT16) (DRIVER_OPTION_DEL_QUESTION_ID + Index),
      (UINT8) 1,
      NewMenuEntry->DisplayStringToken,
      NewMenuEntry->HelpStringToken,
      EFI_IFR_FLAG_INTERACTIVE | EFI_IFR_FLAG_NV_ACCESS,
      (UINT16) DRIVER_OPTION_DEL_QUESTION_ID,
      Location
      );

    Location = Location + ((EFI_IFR_OP_HEADER *) Location)->Length;
    UpdateData->DataCount++;
  }

  UpdatePageEnd (CallbackData, Location);
}

VOID
UpdateDriverAddHandleDescPage (
  IN BMM_CALLBACK_DATA                *CallbackData
  )
{
  BM_MENU_ENTRY *NewMenuEntry;
  UINT8         *Location;

  Location  = (UINT8 *) &UpdateData->Data;
  CallbackData->BmmFakeNvData->DriverAddActive          = 0x01;
  CallbackData->BmmFakeNvData->DriverAddForceReconnect  = 0x00;
  CallbackData->BmmAskSaveOrNot                         = TRUE;
  NewMenuEntry = CallbackData->MenuEntry;

  UpdatePageStart (CallbackData, &Location);

  UpdateData->DataCount += (UINT16) 4;

  CreateSubTitleOpCode (
    NewMenuEntry->DisplayStringToken,
    Location
    );

  Location = Location + ((EFI_IFR_OP_HEADER *) Location)->Length;

  CreateStringOpCode (
    DRV_ADD_HANDLE_DESC_QUESTION_ID,
    (UINT8) 150,
    STRING_TOKEN (STR_LOAD_OPTION_DESC),
    STRING_TOKEN (STR_NULL_STRING),
    6,
    75,
    EFI_IFR_FLAG_INTERACTIVE | EFI_IFR_FLAG_NV_ACCESS,
    KEY_VALUE_DRIVER_ADD_DESC_DATA,
    Location
    );

  Location = Location + ((EFI_IFR_OP_HEADER *) Location)->Length;

  CreateCheckBoxOpCode (
    DRV_ADD_RECON_QUESTION_ID,
    (UINT8) 1,
    STRING_TOKEN (STR_LOAD_OPTION_FORCE_RECON),
    STRING_TOKEN (STR_LOAD_OPTION_FORCE_RECON),
    EFI_IFR_FLAG_INTERACTIVE | EFI_IFR_FLAG_NV_ACCESS,
    DRV_ADD_RECON_QUESTION_ID,
    Location
    );
  Location = Location + ((EFI_IFR_OP_HEADER *) Location)->Length;

  CreateStringOpCode (
    DRIVER_ADD_OPTION_QUESTION_ID,
    (UINT8) 150,
    STRING_TOKEN (STR_OPTIONAL_DATA),
    STRING_TOKEN (STR_NULL_STRING),
    6,
    75,
    EFI_IFR_FLAG_INTERACTIVE | EFI_IFR_FLAG_NV_ACCESS,
    KEY_VALUE_DRIVER_ADD_OPT_DATA,
    Location
    );

  Location = Location + ((EFI_IFR_OP_HEADER *) Location)->Length;
  UpdatePageEnd (CallbackData, Location);
}

VOID
UpdateConsolePage (
  IN UINT16                           UpdatePageId,
  IN BM_MENU_OPTION                   *ConsoleMenu,
  IN BMM_CALLBACK_DATA                *CallbackData
  )
{
  BM_MENU_ENTRY       *NewMenuEntry;
  BM_CONSOLE_CONTEXT  *NewConsoleContext;
  BM_TERMINAL_CONTEXT *NewTerminalContext;
  UINT16              Index;
  UINT16              Index2;
  UINT8               *Location;
  UINT8               CheckFlags;
  EFI_STATUS          Status;
  VOID        	      *Interface;

  Location                      = (UINT8 *) &UpdateData->Data;
  CallbackData->BmmAskSaveOrNot = TRUE;

  UpdatePageStart (CallbackData, &Location);

  for (Index = 0; Index < ConsoleMenu->MenuNumber; Index++) {
    NewMenuEntry      = BOpt_GetMenuEntry (ConsoleMenu, Index);
    NewConsoleContext = (BM_CONSOLE_CONTEXT *) NewMenuEntry->VariableContext;
    CheckFlags        = EFI_IFR_FLAG_INTERACTIVE;
    if (NewConsoleContext->IsActive) {
      CheckFlags |= EFI_IFR_FLAG_DEFAULT;
      CallbackData->BmmFakeNvData->ConsoleCheck[Index] = TRUE;
    } else {
      CallbackData->BmmFakeNvData->ConsoleCheck[Index] = FALSE;
    }

    CreateCheckBoxOpCode (
      (UINT16) (CON_DEVICE_QUESTION_ID + Index),
      (UINT8) 1,
      NewMenuEntry->DisplayStringToken,
      NewMenuEntry->HelpStringToken,
      CheckFlags,
      (UINT16) (CONSOLE_OPTION_OFFSET + Index),
      Location
      );
    Location = Location + ((EFI_IFR_OP_HEADER *) Location)->Length;
    UpdateData->DataCount++;
  }

  Status = EfiLibLocateProtocol (&gTerminalDriverGuid, &Interface);
  if (!EFI_ERROR (Status)) {
    for (Index2 = 0; Index2 < TerminalMenu.MenuNumber; Index2++) {
      CheckFlags          = EFI_IFR_FLAG_INTERACTIVE;
      NewMenuEntry        = BOpt_GetMenuEntry (&TerminalMenu, Index2);
      NewTerminalContext  = (BM_TERMINAL_CONTEXT *) NewMenuEntry->VariableContext;

      if ((NewTerminalContext->IsConIn && (UpdatePageId == FORM_CON_IN_ID)) ||
          (NewTerminalContext->IsConOut && (UpdatePageId == FORM_CON_OUT_ID)) ||
          (NewTerminalContext->IsStdErr && (UpdatePageId == FORM_CON_ERR_ID))
          ) {
        CheckFlags |= EFI_IFR_FLAG_DEFAULT;
        CallbackData->BmmFakeNvData->ConsoleCheck[Index] = TRUE;
      } else {
        CallbackData->BmmFakeNvData->ConsoleCheck[Index] = FALSE;
      }

      CreateCheckBoxOpCode (
        (UINT16) (CON_DEVICE_QUESTION_ID + Index),
        (UINT8) 1,
        NewMenuEntry->DisplayStringToken,
        NewMenuEntry->HelpStringToken,
        CheckFlags,
        (UINT16) (CONSOLE_OPTION_OFFSET + Index),
        Location
        );
      Location = Location + ((EFI_IFR_OP_HEADER *) Location)->Length;
      UpdateData->DataCount++;
      Index++;
    }
  }

  UpdatePageEnd (CallbackData, Location);
}

VOID
UpdateOrderPage (
  IN UINT16                           UpdatePageId,
  IN BM_MENU_OPTION                   *OptionMenu,
  IN BMM_CALLBACK_DATA                *CallbackData
  )
{
  BM_MENU_ENTRY *NewMenuEntry;
  UINT16        Index;
  UINT8         *Location;
  IFR_OPTION    *IfrOptionList;

  Location                      = (UINT8 *) &UpdateData->Data;
  CallbackData->BmmAskSaveOrNot = TRUE;

  UpdatePageStart (CallbackData, &Location);

  CreateMenuStringToken (CallbackData, CallbackData->BmmHiiHandle, OptionMenu);

  ZeroMem (CallbackData->BmmFakeNvData->OptionOrder, 100);

  IfrOptionList = AllocateZeroPool (sizeof (IFR_OPTION) * OptionMenu->MenuNumber);
  if (NULL == IfrOptionList) {
    return ;
  }

  for (Index = 0; Index < OptionMenu->MenuNumber; Index++) {
    NewMenuEntry = BOpt_GetMenuEntry (OptionMenu, Index);
    IfrOptionList[Index].StringToken = NewMenuEntry->DisplayStringToken;
    IfrOptionList[Index].Value = (UINT16) (NewMenuEntry->OptionNumber + 1);
    IfrOptionList[Index].OptionString = NULL;
    CallbackData->BmmFakeNvData->OptionOrder[Index] = (UINT8) (IfrOptionList[Index].Value);
  }

  if (OptionMenu->MenuNumber > 0) {
    CreateOrderedListOpCode (
      (UINT16) OPTION_ORDER_QUESTION_ID,
      (UINT8) 100,
      STRING_TOKEN (STR_CHANGE_ORDER),
      STRING_TOKEN (STR_CHANGE_ORDER),
      IfrOptionList,
      OptionMenu->MenuNumber,
      Location
      );

    for (Index = 0; Index < OptionMenu->MenuNumber + 2; Index++) {
      Location = Location + ((EFI_IFR_OP_HEADER *) Location)->Length;
    }

    UpdateData->DataCount = (UINT16) (UpdateData->DataCount + OptionMenu->MenuNumber + 2);
  }

  SafeFreePool (IfrOptionList);

  UpdatePageEnd (CallbackData, Location);

  CopyMem (
    CallbackData->BmmOldFakeNVData.OptionOrder,
    CallbackData->BmmFakeNvData->OptionOrder,
    100
    );
}

VOID
UpdateBootNextPage (
  IN BMM_CALLBACK_DATA                *CallbackData
  )
{
  UINT8           *Location;
  BM_MENU_ENTRY   *NewMenuEntry;
  BM_LOAD_CONTEXT *NewLoadContext;
  IFR_OPTION      *IfrOptionList;
  UINTN           NumberOfOptions;
  UINT16          Index;

  Location                      = (UINT8 *) &UpdateData->Data;
  IfrOptionList                 = NULL;
  NumberOfOptions               = BootOptionMenu.MenuNumber;
  CallbackData->BmmAskSaveOrNot = TRUE;

  UpdatePageStart (CallbackData, &Location);
  CreateMenuStringToken (CallbackData, CallbackData->BmmHiiHandle, &BootOptionMenu);

  if (NumberOfOptions > 0) {
    UpdateData->DataCount = (UINT8) (UpdateData->DataCount + NumberOfOptions);
    IfrOptionList = AllocateZeroPool ((NumberOfOptions + 1) * sizeof (IFR_OPTION));

    ASSERT (IfrOptionList);

    CallbackData->BmmFakeNvData->BootNext = (UINT16) (BootOptionMenu.MenuNumber);

    for (Index = 0; Index < BootOptionMenu.MenuNumber; Index++) {
      NewMenuEntry    = BOpt_GetMenuEntry (&BootOptionMenu, Index);
      NewLoadContext  = (BM_LOAD_CONTEXT *) NewMenuEntry->VariableContext;
      if (NewLoadContext->IsBootNext) {
        IfrOptionList[Index].Flags            = EFI_IFR_FLAG_DEFAULT | EFI_IFR_FLAG_INTERACTIVE;
        CallbackData->BmmFakeNvData->BootNext = Index;
      } else {
        IfrOptionList[Index].Flags = EFI_IFR_FLAG_INTERACTIVE;
      }

      IfrOptionList[Index].Key          = (UINT16) KEY_VALUE_MAIN_BOOT_NEXT;
      IfrOptionList[Index].Value        = Index;
      IfrOptionList[Index].StringToken  = NewMenuEntry->DisplayStringToken;
      IfrOptionList[Index].OptionString = NULL;
    }

    IfrOptionList[Index].Key          = (UINT16) KEY_VALUE_MAIN_BOOT_NEXT;
    IfrOptionList[Index].Value        = Index;
    IfrOptionList[Index].StringToken  = STRING_TOKEN (STR_NONE);
    IfrOptionList[Index].Flags        = EFI_IFR_FLAG_INTERACTIVE;
    if (CallbackData->BmmFakeNvData->BootNext == Index) {
      IfrOptionList[Index].Flags |= EFI_IFR_FLAG_DEFAULT;
    }

    IfrOptionList[Index].OptionString = NULL;

    CreateOneOfOpCode (
      (UINT16) BOOT_NEXT_QUESTION_ID,
      (UINT8) 2,
      STRING_TOKEN (STR_BOOT_NEXT),
      STRING_TOKEN (STR_BOOT_NEXT_HELP),
      IfrOptionList,
      (UINTN) (NumberOfOptions + 1),
      Location
      );
    Location  = Location + (NumberOfOptions + 2) * ((EFI_IFR_OP_HEADER *) Location)->Length;
    Location  = Location + ((EFI_IFR_OP_HEADER *) Location)->Length;

    UpdateData->DataCount += 3;
    SafeFreePool (IfrOptionList);
    IfrOptionList = NULL;
  }

  UpdatePageEnd (CallbackData, Location);
}

VOID
UpdateTimeOutPage (
  IN BMM_CALLBACK_DATA                *CallbackData
  )
{
  UINT8   *Location;
  UINT16  BootTimeOut;

  Location                      = (UINT8 *) &UpdateData->Data;
  CallbackData->BmmAskSaveOrNot = TRUE;

  UpdatePageStart (CallbackData, &Location);

  BootTimeOut = BdsLibGetTimeout ();

  CreateNumericOpCode (
    (UINT16) BOOT_TIME_OUT_QUESTION_ID,
    (UINT8) 2,
    STRING_TOKEN (STR_NUM_AUTO_BOOT),
    STRING_TOKEN (STR_HLP_AUTO_BOOT),
    0,
    65535,
    0,
    10,
    0,
    0,
    Location
    );

  CallbackData->BmmFakeNvData->BootTimeOut = (UINT16) BootTimeOut;
  UpdateData->DataCount++;
  Location = Location + ((EFI_IFR_OP_HEADER *) Location)->Length;

  UpdatePageEnd (CallbackData, Location);
}

VOID
UpdateTerminalPage (
  IN BMM_CALLBACK_DATA                *CallbackData
  )
{
  UINT16              Index;
  UINT8               *Location;
  UINT8               CheckFlags;
  IFR_OPTION          *IfrOptionList;
  BM_MENU_ENTRY       *NewMenuEntry;
  BM_TERMINAL_CONTEXT *NewTerminalContext;

  ZeroMem (UpdateData, UPDATE_DATA_SIZE);
  Location = (UINT8 *) &UpdateData->Data;
  UpdatePageStart (CallbackData, &Location);

  NewMenuEntry = BOpt_GetMenuEntry (
                  &TerminalMenu,
                  CallbackData->CurrentTerminal
                  );

  if (!NewMenuEntry) {
    return ;
  }

  NewTerminalContext  = (BM_TERMINAL_CONTEXT *) NewMenuEntry->VariableContext;

  IfrOptionList       = AllocateZeroPool (sizeof (IFR_OPTION) * 19);
  if (!IfrOptionList) {
    return ;
  }

  for (Index = 0; Index < 19; Index++) {
    CheckFlags = EFI_IFR_FLAG_INTERACTIVE;
    if (NewTerminalContext->BaudRate == (UINT64) (BaudRateList[Index].Value)) {
      CheckFlags |= EFI_IFR_FLAG_DEFAULT;
      NewTerminalContext->BaudRateIndex         = (UINT8) Index;
      CallbackData->BmmFakeNvData->COMBaudRate  = NewTerminalContext->BaudRateIndex;
    }

    IfrOptionList[Index].Flags        = CheckFlags;
    IfrOptionList[Index].Key          = KEY_VALUE_COM_SET_BAUD_RATE;
    IfrOptionList[Index].StringToken  = BaudRateList[Index].StringToken;
    IfrOptionList[Index].Value        = Index;
  }

  CreateOneOfOpCode (
    (UINT16) COM_BAUD_RATE_QUESTION_ID,
    (UINT8) 1,
    STRING_TOKEN (STR_COM_BAUD_RATE),
    STRING_TOKEN (STR_COM_BAUD_RATE),
    IfrOptionList,
    19,
    Location
    );

  Location              = Location + (Index + 1) * ((EFI_IFR_OP_HEADER *) Location)->Length;
  Location              = Location + ((EFI_IFR_OP_HEADER *) Location)->Length;
  UpdateData->DataCount = (UINT8) (UpdateData->DataCount + Index);
  UpdateData->DataCount += 2;

  SafeFreePool (IfrOptionList);

  IfrOptionList = AllocateZeroPool (sizeof (IFR_OPTION) * 4);
  if (!IfrOptionList) {
    return ;
  }

  for (Index = 0; Index < 4; Index++) {
    CheckFlags = EFI_IFR_FLAG_INTERACTIVE;

    if (NewTerminalContext->DataBits == DataBitsList[Index].Value) {
      NewTerminalContext->DataBitsIndex         = (UINT8) Index;
      CallbackData->BmmFakeNvData->COMDataRate  = NewTerminalContext->DataBitsIndex;
      CheckFlags |= EFI_IFR_FLAG_DEFAULT;
    }

    IfrOptionList[Index].Flags        = CheckFlags;
    IfrOptionList[Index].Key          = KEY_VALUE_COM_SET_DATA_BITS;
    IfrOptionList[Index].StringToken  = DataBitsList[Index].StringToken;
    IfrOptionList[Index].Value        = Index;
  }

  CreateOneOfOpCode (
    (UINT16) COM_DATA_RATE_QUESTION_ID,
    (UINT8) 1,
    STRING_TOKEN (STR_COM_DATA_BITS),
    STRING_TOKEN (STR_COM_DATA_BITS),
    IfrOptionList,
    4,
    Location
    );

  Location              = Location + (Index + 1) * ((EFI_IFR_OP_HEADER *) Location)->Length;
  Location              = Location + ((EFI_IFR_OP_HEADER *) Location)->Length;
  UpdateData->DataCount = (UINT8) (UpdateData->DataCount + Index);
  UpdateData->DataCount += 2;

  SafeFreePool (IfrOptionList);

  IfrOptionList = AllocateZeroPool (sizeof (IFR_OPTION) * 5);
  if (!IfrOptionList) {
    return ;
  }

  for (Index = 0; Index < 5; Index++) {
    CheckFlags = EFI_IFR_FLAG_INTERACTIVE;
    if (NewTerminalContext->Parity == ParityList[Index].Value) {
      CheckFlags |= EFI_IFR_FLAG_DEFAULT;
      NewTerminalContext->ParityIndex         = (UINT8) Index;
      CallbackData->BmmFakeNvData->COMParity  = NewTerminalContext->ParityIndex;
    }

    IfrOptionList[Index].Flags        = CheckFlags;
    IfrOptionList[Index].Key          = KEY_VALUE_COM_SET_PARITY;
    IfrOptionList[Index].StringToken  = ParityList[Index].StringToken;
    IfrOptionList[Index].Value        = Index;
  }

  CreateOneOfOpCode (
    (UINT16) COM_PARITY_QUESTION_ID,
    (UINT8) 1,
    STRING_TOKEN (STR_COM_PARITY),
    STRING_TOKEN (STR_COM_PARITY),
    IfrOptionList,
    5,
    Location
    );

  Location              = Location + (Index + 1) * ((EFI_IFR_OP_HEADER *) Location)->Length;
  Location              = Location + ((EFI_IFR_OP_HEADER *) Location)->Length;
  UpdateData->DataCount = (UINT8) (UpdateData->DataCount + Index);
  UpdateData->DataCount += 2;

  SafeFreePool (IfrOptionList);

  IfrOptionList = AllocateZeroPool (sizeof (IFR_OPTION) * 3);
  if (!IfrOptionList) {
    return ;
  }

  for (Index = 0; Index < 3; Index++) {
    CheckFlags = EFI_IFR_FLAG_INTERACTIVE;
    if (NewTerminalContext->StopBits == StopBitsList[Index].Value) {
      CheckFlags |= EFI_IFR_FLAG_DEFAULT;
      NewTerminalContext->StopBitsIndex         = (UINT8) Index;
      CallbackData->BmmFakeNvData->COMStopBits  = NewTerminalContext->StopBitsIndex;
    }

    IfrOptionList[Index].Flags        = CheckFlags;
    IfrOptionList[Index].Key          = KEY_VALUE_COM_SET_STOP_BITS;
    IfrOptionList[Index].StringToken  = StopBitsList[Index].StringToken;
    IfrOptionList[Index].Value        = Index;
  }

  CreateOneOfOpCode (
    (UINT16) COM_STOP_BITS_QUESTION_ID,
    (UINT8) 1,
    STRING_TOKEN (STR_COM_STOP_BITS),
    STRING_TOKEN (STR_COM_STOP_BITS),
    IfrOptionList,
    3,
    Location
    );

  Location              = Location + (Index + 1) * ((EFI_IFR_OP_HEADER *) Location)->Length;
  Location              = Location + ((EFI_IFR_OP_HEADER *) Location)->Length;
  UpdateData->DataCount = (UINT8) (UpdateData->DataCount + Index);
  UpdateData->DataCount += 2;

  SafeFreePool (IfrOptionList);

  IfrOptionList = AllocateZeroPool (sizeof (IFR_OPTION) * 4);
  if (!IfrOptionList) {
    return ;
  }

  for (Index = 0; Index < 4; Index++) {
    CheckFlags = EFI_IFR_FLAG_INTERACTIVE;
    if (NewTerminalContext->TerminalType == Index) {
      CheckFlags |= EFI_IFR_FLAG_DEFAULT;
      CallbackData->BmmFakeNvData->COMTerminalType = NewTerminalContext->TerminalType;
    }

    IfrOptionList[Index].Flags        = CheckFlags;
    IfrOptionList[Index].Key          = KEY_VALUE_COM_SET_TERMI_TYPE;
    IfrOptionList[Index].StringToken  = (STRING_REF) TerminalType[Index];
    IfrOptionList[Index].Value        = Index;
  }

  CreateOneOfOpCode (
    (UINT16) COM_TERMINAL_QUESTION_ID,
    (UINT8) 1,
    STRING_TOKEN (STR_COM_TERMI_TYPE),
    STRING_TOKEN (STR_COM_TERMI_TYPE),
    IfrOptionList,
    4,
    Location
    );

  Location              = Location + (Index + 1) * ((EFI_IFR_OP_HEADER *) Location)->Length;
  Location              = Location + ((EFI_IFR_OP_HEADER *) Location)->Length;
  UpdateData->DataCount = (UINT8) (UpdateData->DataCount + Index);
  UpdateData->DataCount += 2;

  SafeFreePool (IfrOptionList);

  CreateGotoOpCode (
    FORM_MAIN_ID,
    STRING_TOKEN (STR_SAVE_AND_EXIT),
    STRING_TOKEN (STR_NULL_STRING),
    EFI_IFR_FLAG_INTERACTIVE | EFI_IFR_FLAG_NV_ACCESS,
    KEY_VALUE_SAVE_AND_EXIT,
    Location
    );

  Location = Location + ((EFI_IFR_OP_HEADER *) Location)->Length;
  UpdateData->DataCount++;

  CreateGotoOpCode (
    FORM_MAIN_ID,
    STRING_TOKEN (STR_NO_SAVE_AND_EXIT),
    STRING_TOKEN (STR_NULL_STRING),
    EFI_IFR_FLAG_INTERACTIVE | EFI_IFR_FLAG_NV_ACCESS,
    KEY_VALUE_NO_SAVE_AND_EXIT,
    Location
    );

  UpdateData->DataCount++;

  CallbackData->Hii->UpdateForm (
                      CallbackData->Hii,
                      CallbackData->BmmHiiHandle,
                      (EFI_FORM_LABEL) FORM_CON_COM_SETUP_ID,
                      TRUE,
                      UpdateData
                      );

}

VOID
UpdatePageBody (
  IN UINT16                           UpdatePageId,
  IN BMM_CALLBACK_DATA                *CallbackData
  )
{
  CleanUpPage (UpdatePageId, CallbackData);
  switch (UpdatePageId) {
  case FORM_CON_IN_ID:
    UpdateConsolePage (UpdatePageId, &ConsoleInpMenu, CallbackData);
    break;

  case FORM_CON_OUT_ID:
    UpdateConsolePage (UpdatePageId, &ConsoleOutMenu, CallbackData);
    break;

  case FORM_CON_ERR_ID:
    UpdateConsolePage (UpdatePageId, &ConsoleErrMenu, CallbackData);
    break;

  case FORM_BOOT_CHG_ID:
    UpdateOrderPage (UpdatePageId, &BootOptionMenu, CallbackData);
    break;

  case FORM_DRV_CHG_ID:
    UpdateOrderPage (UpdatePageId, &DriverOptionMenu, CallbackData);
    break;

  default:
    break;
  }
}

VOID *
GetLegacyBootOptionVar (
  IN  UINTN                            DeviceType,
  OUT UINTN                            *OptionIndex,
  OUT UINTN                            *OptionSize
  )
{
  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
  VOID                      *OptionBuffer;
  UINTN                     OrderSize;
  UINTN                     Index;
  UINT32                    Attribute;
  UINT16                    *OrderBuffer;
  CHAR16                    StrTemp[100];
  UINT16                    FilePathSize;
  CHAR16                    *Description;
  UINT8                     *Ptr;
  UINT8                     *OptionalData;

  //
  // Get Boot Option number from the size of BootOrder
  //
  OrderBuffer = BdsLibGetVariableAndSize (
                  L"BootOrder",
                  &gEfiGlobalVariableGuid,
                  &OrderSize
                  );

  for (Index = 0; Index < OrderSize / sizeof (UINT16); Index++) {
    UnicodeSPrint (StrTemp, 100, L"Boot%04x", OrderBuffer[Index]);
    OptionBuffer = BdsLibGetVariableAndSize (
                    StrTemp,
                    &gEfiGlobalVariableGuid,
                    OptionSize
                    );
    if (NULL == OptionBuffer) {
      continue;
    }

    Ptr       = (UINT8 *) OptionBuffer;
    Attribute = *(UINT32 *) Ptr;
    Ptr += sizeof (UINT32);

    FilePathSize = *(UINT16 *) Ptr;
    Ptr += sizeof (UINT16);

    Description = (CHAR16 *) Ptr;
    Ptr += StrSize ((CHAR16 *) Ptr);

    //
    // Now Ptr point to Device Path
    //
    DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) Ptr;
    Ptr += FilePathSize;

    //
    // Now Ptr point to Optional Data
    //
    OptionalData = Ptr;

    if ((DeviceType == ((BBS_TABLE *) OptionalData)->DeviceType) &&
        (BBS_DEVICE_PATH == DevicePath->Type) &&
        (BBS_BBS_DP == DevicePath->SubType)
        ) {
      *OptionIndex = OrderBuffer[Index];
      SafeFreePool (OrderBuffer);
      return OptionBuffer;
    } else {
      SafeFreePool (OptionBuffer);
    }
  }

  SafeFreePool (OrderBuffer);
  return NULL;
}

VOID
UpdateSetLegacyDeviceOrderPage (
  IN UINT16                           UpdatePageId,
  IN BMM_CALLBACK_DATA                *CallbackData
  )
{
  BM_LEGACY_DEV_ORDER_CONTEXT *DevOrder;
  BM_MENU_OPTION              *OptionMenu;
  BM_MENU_ENTRY               *NewMenuEntry;
  IFR_OPTION                  *IfrOptionList;
  STRING_REF                  StrRef;
  STRING_REF                  StrRefHelp;
  BBS_TYPE                    BbsType;
  UINTN                       VarSize;
  UINTN                       Pos;
  UINTN                       Bit;
  UINT16                      Index;
  UINT16                      Index2;
  UINT16                      Key;
  CHAR16                      String[100];
  CHAR16                      *TypeStr;
  CHAR16                      *TypeStrHelp;
  UINT16                      VarDevOrder;
  UINT8                       *Location;
  UINT8                       *VarData;
  UINT8                       *OriginalPtr;
  UINT8                       *LegacyOrder;
  UINT8                       *OldData;
  UINT8                       *DisMap;

  OptionMenu                    = NULL;
  Key = 0;
  StrRef = 0;
  StrRefHelp = 0;
  TypeStr = NULL;
  TypeStrHelp = NULL;
  BbsType = BBS_FLOPPY;
  LegacyOrder = NULL;
  OldData = NULL;
  DisMap = NULL;

  Location = (UINT8 *) &UpdateData->Data;
  CallbackData->BmmAskSaveOrNot = TRUE;

  UpdatePageStart (CallbackData, &Location);

  DisMap = CallbackData->BmmOldFakeNVData.DisableMap;

  SetMem (DisMap, 32, 0);
  //
  // Create oneof option list
  //
  switch (UpdatePageId) {
  case FORM_SET_FD_ORDER_ID:
    OptionMenu  = (BM_MENU_OPTION *) &LegacyFDMenu;
    Key         = LEGACY_FD_QUESTION_ID;
    TypeStr     = StrFloppy;
    TypeStrHelp = StrFloppyHelp;
    BbsType     = BBS_FLOPPY;
    LegacyOrder = CallbackData->BmmFakeNvData->LegacyFD;
    OldData     = CallbackData->BmmOldFakeNVData.LegacyFD;
    break;

  case FORM_SET_HD_ORDER_ID:
    OptionMenu  = (BM_MENU_OPTION *) &LegacyHDMenu;
    Key         = LEGACY_HD_QUESTION_ID;
    TypeStr     = StrHardDisk;
    TypeStrHelp = StrHardDiskHelp;
    BbsType     = BBS_HARDDISK;
    LegacyOrder = CallbackData->BmmFakeNvData->LegacyHD;
    OldData     = CallbackData->BmmOldFakeNVData.LegacyHD;
    break;

  case FORM_SET_CD_ORDER_ID:
    OptionMenu  = (BM_MENU_OPTION *) &LegacyCDMenu;
    Key         = LEGACY_CD_QUESTION_ID;
    TypeStr     = StrCDROM;
    TypeStrHelp = StrCDROMHelp;
    BbsType     = BBS_CDROM;
    LegacyOrder = CallbackData->BmmFakeNvData->LegacyCD;
    OldData     = CallbackData->BmmOldFakeNVData.LegacyCD;
    break;

  case FORM_SET_NET_ORDER_ID:
    OptionMenu  = (BM_MENU_OPTION *) &LegacyNETMenu;
    Key         = LEGACY_NET_QUESTION_ID;
    TypeStr     = StrNET;
    TypeStrHelp = StrNETHelp;
    BbsType     = BBS_EMBED_NETWORK;
    LegacyOrder = CallbackData->BmmFakeNvData->LegacyNET;
    OldData     = CallbackData->BmmOldFakeNVData.LegacyNET;
    break;

  case FORM_SET_BEV_ORDER_ID:
    OptionMenu  = (BM_MENU_OPTION *) &LegacyBEVMenu;
    Key         = LEGACY_BEV_QUESTION_ID;
    TypeStr     = StrBEV;
    TypeStrHelp = StrBEVHelp;
    BbsType     = BBS_BEV_DEVICE;
    LegacyOrder = CallbackData->BmmFakeNvData->LegacyBEV;
    OldData     = CallbackData->BmmOldFakeNVData.LegacyBEV;
    break;

  }

  CreateMenuStringToken (CallbackData, CallbackData->BmmHiiHandle, OptionMenu);

  IfrOptionList = AllocateZeroPool (sizeof (IFR_OPTION) * (OptionMenu->MenuNumber + 1));
  if (NULL == IfrOptionList) {
    return ;
  }

  for (Index = 0; Index < OptionMenu->MenuNumber; Index++) {
    NewMenuEntry                = BOpt_GetMenuEntry (OptionMenu, Index);
    IfrOptionList[Index].Flags  = EFI_IFR_FLAG_INTERACTIVE;
    if (0 == Index) {
      IfrOptionList[Index].Flags |= EFI_IFR_FLAG_DEFAULT;
    }

    IfrOptionList[Index].Key          = Key;
    IfrOptionList[Index].StringToken  = NewMenuEntry->DisplayStringToken;
    IfrOptionList[Index].Value        = (UINT16) ((BM_LEGACY_DEVICE_CONTEXT *) NewMenuEntry->VariableContext)->Index;
    IfrOptionList[Index].OptionString = NULL;
  }
  //
  // for item "Disabled"
  //
  IfrOptionList[Index].Flags        = EFI_IFR_FLAG_INTERACTIVE;
  IfrOptionList[Index].Key          = Key;
  IfrOptionList[Index].StringToken  = STRING_TOKEN (STR_DISABLE_LEGACY_DEVICE);
  IfrOptionList[Index].Value        = 0xFF;
  IfrOptionList[Index].OptionString = NULL;

  //
  // Get Device Order from variable
  //
  VarData = BdsLibGetVariableAndSize (
              VarLegacyDevOrder,
              &EfiLegacyDevOrderGuid,
              &VarSize
              );

  if (NULL != VarData) {
    OriginalPtr = VarData;
    DevOrder    = (BM_LEGACY_DEV_ORDER_CONTEXT *) VarData;
    while (VarData < VarData + VarSize) {
      if (DevOrder->BbsType == BbsType) {
        break;
      }

      VarData += sizeof (BBS_TYPE);
      VarData += *(UINT16 *) VarData;
      DevOrder = (BM_LEGACY_DEV_ORDER_CONTEXT *) VarData;
    }
    //
    // Create oneof tag here for FD/HD/CD #1 #2
    //
    for (Index = 0; Index < OptionMenu->MenuNumber; Index++) {
      for (Index2 = 0; Index2 <= OptionMenu->MenuNumber; Index2++) {
        IfrOptionList[Index2].Key = (UINT16) (Key + Index);
      }
      //
      // Create the string for oneof tag
      //
      UnicodeSPrint (String, sizeof (String), TypeStr, Index);
      StrRef = 0;
      CallbackData->Hii->NewString (
                          CallbackData->Hii,
                          NULL,
                          CallbackData->BmmHiiHandle,
                          &StrRef,
                          String
                          );

      UnicodeSPrint (String, sizeof (String), TypeStrHelp, Index);
      StrRefHelp = 0;
      CallbackData->Hii->NewString (
                          CallbackData->Hii,
                          NULL,
                          CallbackData->BmmHiiHandle,
                          &StrRefHelp,
                          String
                          );

      CreateOneOfOpCode (
        (UINT16) (Key + Index),
        (UINT8) 1,
        StrRef,
        StrRefHelp,
        IfrOptionList,
        OptionMenu->MenuNumber + 1,
        Location
        );

      VarDevOrder = *(UINT16 *) ((UINT8 *) DevOrder + sizeof (BBS_TYPE) + sizeof (UINT16) + Index * sizeof (UINT16));

      if (0xFF00 == (VarDevOrder & 0xFF00)) {
        LegacyOrder[Index]  = 0xFF;
        Pos                 = (VarDevOrder & 0xFF) / 8;
        Bit                 = 7 - ((VarDevOrder & 0xFF) % 8);
        DisMap[Pos] |= (UINT8) (1 << Bit);
      } else {
        LegacyOrder[Index] = (UINT8) (VarDevOrder & 0xFF);
      }

      Location              = Location + (OptionMenu->MenuNumber + 2) * ((EFI_IFR_OP_HEADER *) Location)->Length;
      Location              = Location + ((EFI_IFR_OP_HEADER *) Location)->Length;
      UpdateData->DataCount = (UINT16) (UpdateData->DataCount + (OptionMenu->MenuNumber + 3));
    }
  }

  CopyMem (
    OldData,
    LegacyOrder,
    100
    );

  if (IfrOptionList != NULL) {
    SafeFreePool (IfrOptionList);
    IfrOptionList = NULL;
  }

  UpdatePageEnd (CallbackData, Location);
}

VOID
UpdatePageId (
  BMM_CALLBACK_DATA              *Private,
  UINT16                         NewPageId
  )
{
  UINT16  FileOptionMask;

  FileOptionMask = (UINT16) (FILE_OPTION_MASK & NewPageId);

  if ((NewPageId < FILE_OPTION_OFFSET) && (NewPageId >= HANDLE_OPTION_OFFSET)) {
    //
    // If we select a handle to add driver option, advance to the add handle description page.
    //
    NewPageId = FORM_DRV_ADD_HANDLE_DESC_ID;
  } else if ((NewPageId == KEY_VALUE_SAVE_AND_EXIT) || (NewPageId == KEY_VALUE_NO_SAVE_AND_EXIT)) {
    //
    // Return to main page after "Save Changes" or "Discard Changes".
    //
    NewPageId = FORM_MAIN_ID;
  }

  if ((NewPageId > 0) && (NewPageId < MAXIMUM_FORM_ID)) {
    Private->BmmPreviousPageId  = Private->BmmCurrentPageId;
    Private->BmmCurrentPageId   = NewPageId;
  }
}
