text: /** @file
    Legacy Boot Maintenance UI implementation.
  
  Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
  (C) Copyright 2018 Hewlett Packard Enterprise Development LP<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent
  
  **/
  
  
  #include "LegacyBootMaintUi.h"
  
  LEGACY_BOOT_OPTION_CALLBACK_DATA  *mLegacyBootOptionPrivate = NULL;
  EFI_GUID  mLegacyBootOptionGuid     = LEGACY_BOOT_OPTION_FORMSET_GUID;
  CHAR16    mLegacyBootStorageName[]  = L"LegacyBootData";
  BBS_TYPE  mBbsType[] = {BBS_FLOPPY, BBS_HARDDISK, BBS_CDROM, BBS_EMBED_NETWORK, BBS_BEV_DEVICE, BBS_UNKNOWN};
  BOOLEAN   mFirstEnterLegacyForm = FALSE;
  
  
  ///
  /// Legacy FD Info from LegacyBios.GetBbsInfo()
  ///
  LEGACY_MENU_OPTION      LegacyFDMenu = {
    LEGACY_MENU_OPTION_SIGNATURE,
    {NULL},
    0
  };
  
  ///
  /// Legacy HD Info from LegacyBios.GetBbsInfo()
  ///
  LEGACY_MENU_OPTION      LegacyHDMenu = {
    LEGACY_MENU_OPTION_SIGNATURE,
    {NULL},
    0
  };
  
  ///
  /// Legacy CD Info from LegacyBios.GetBbsInfo()
  ///
  LEGACY_MENU_OPTION      LegacyCDMenu = {
    LEGACY_MENU_OPTION_SIGNATURE,
    {NULL},
    0
  };
  
  ///
  /// Legacy NET Info from LegacyBios.GetBbsInfo()
  ///
  LEGACY_MENU_OPTION      LegacyNETMenu = {
    LEGACY_MENU_OPTION_SIGNATURE,
    {NULL},
    0
  };
  
  ///
  /// Legacy NET Info from LegacyBios.GetBbsInfo()
  ///
  LEGACY_MENU_OPTION      LegacyBEVMenu = {
    LEGACY_MENU_OPTION_SIGNATURE,
    {NULL},
    0
  };
  
  
  VOID                *mLegacyStartOpCodeHandle = NULL;
  VOID                *mLegacyEndOpCodeHandle = NULL;
  EFI_IFR_GUID_LABEL  *mLegacyStartLabel = NULL;
  EFI_IFR_GUID_LABEL  *mLegacyEndLabel = NULL;
  
  
  HII_VENDOR_DEVICE_PATH  mLegacyBootOptionHiiVendorDevicePath = {
    {
      {
        HARDWARE_DEVICE_PATH,
        HW_VENDOR_DP,
        {
          (UINT8) (sizeof (VENDOR_DEVICE_PATH)),
          (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)
        }
      },
      { 0x6bc75598, 0x89b4, 0x483d, { 0x91, 0x60, 0x7f, 0x46, 0x9a, 0x96, 0x35, 0x31 } }
    },
    {
      END_DEVICE_PATH_TYPE,
      END_ENTIRE_DEVICE_PATH_SUBTYPE,
      {
        (UINT8) (END_DEVICE_PATH_LENGTH),
        (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8)
      }
    }
  };
  
  /**
  
    Build the LegacyFDMenu LegacyHDMenu LegacyCDMenu according to LegacyBios.GetBbsInfo().
  
  **/
  VOID
  GetLegacyOptions (
    VOID
    );
  
  
  /**
  
    Base on the L"LegacyDevOrder" variable to build the current order data.
  
  **/
  VOID
  GetLegacyOptionsOrder (
    VOID
    );
  
  /**
    Re-order the Boot Option according to the DevOrder.
  
    The routine re-orders the Boot Option in BootOption array according to
    the order specified by DevOrder.
  
    @param DevOrder           Pointer to buffer containing the BBS Index,
                              high 8-bit value 0xFF indicating a disabled boot option
    @param DevOrderCount      Count of the BBS Index
    @param EnBootOption       Callee allocated buffer containing the enabled Boot Option Numbers
    @param EnBootOptionCount  Count of the enabled Boot Option Numbers
    @param DisBootOption      Callee allocated buffer containing the disabled Boot Option Numbers
    @param DisBootOptionCount Count of the disabled Boot Option Numbers
  
    @return EFI_SUCCESS       The function completed successfully.
    @retval other             Contain some error, details see  the status return by gRT->SetVariable.
  **/
  EFI_STATUS
  OrderLegacyBootOption4SameType (
    UINT16                   *DevOrder,
    UINTN                    DevOrderCount,
    UINT16                   **EnBootOption,
    UINTN                    *EnBootOptionCount,
    UINT16                   **DisBootOption,
    UINTN                    *DisBootOptionCount
    )
  {
    EFI_STATUS               Status;
    UINT16                   *NewBootOption;
    UINT16                   *BootOrder;
    UINTN                    BootOrderSize;
    UINTN                    Index;
    UINTN                    StartPosition;
  
    EFI_BOOT_MANAGER_LOAD_OPTION    BootOption;
  
    CHAR16                           OptionName[sizeof ("Boot####")];
    UINT16                   *BbsIndexArray;
    UINT16                   *DeviceTypeArray;
  
    GetEfiGlobalVariable2 (L"BootOrder", (VOID **) &BootOrder, &BootOrderSize);
    ASSERT (BootOrder != NULL);
  
    BbsIndexArray       = AllocatePool (BootOrderSize);
    DeviceTypeArray     = AllocatePool (BootOrderSize);
    *EnBootOption       = AllocatePool (BootOrderSize);
    *DisBootOption      = AllocatePool (BootOrderSize);
    *DisBootOptionCount = 0;
    *EnBootOptionCount  = 0;
    Index               = 0;
    Status              = EFI_SUCCESS;
  
    ASSERT (BbsIndexArray != NULL);
    ASSERT (DeviceTypeArray != NULL);
    ASSERT (*EnBootOption != NULL);
    ASSERT (*DisBootOption != NULL);
  
    for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) {
  
      UnicodeSPrint (OptionName, sizeof (OptionName), L"Boot%04x", BootOrder[Index]);
      Status = EfiBootManagerVariableToLoadOption (OptionName, &BootOption);
      ASSERT_EFI_ERROR (Status);
  
      if ((DevicePathType (BootOption.FilePath) == BBS_DEVICE_PATH) &&
          (DevicePathSubType (BootOption.FilePath) == BBS_BBS_DP)) {
        //
        // Legacy Boot Option
        //
        ASSERT (BootOption.OptionalDataSize == sizeof (LEGACY_BOOT_OPTION_BBS_DATA));
  
        DeviceTypeArray[Index] = ((BBS_BBS_DEVICE_PATH *) BootOption.FilePath)->DeviceType;
        BbsIndexArray  [Index] = ((LEGACY_BOOT_OPTION_BBS_DATA *) BootOption.OptionalData)->BbsIndex;
      } else {
        DeviceTypeArray[Index] = BBS_TYPE_UNKNOWN;
        BbsIndexArray  [Index] = 0xFFFF;
      }
      EfiBootManagerFreeLoadOption (&BootOption);
    }
  
    //
    // Record the corresponding Boot Option Numbers according to the DevOrder
    // Record the EnBootOption and DisBootOption according to the DevOrder
    //
    StartPosition = BootOrderSize / sizeof (UINT16);
    NewBootOption = AllocatePool (DevOrderCount * sizeof (UINT16));
    ASSERT (NewBootOption != NULL);
    while (DevOrderCount-- != 0) {
      for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) {
        if (BbsIndexArray[Index] == (DevOrder[DevOrderCount] & 0xFF)) {
          StartPosition = MIN (StartPosition, Index);
          NewBootOption[DevOrderCount] = BootOrder[Index];
  
          if ((DevOrder[DevOrderCount] & 0xFF00) == 0xFF00) {
            (*DisBootOption)[*DisBootOptionCount] = BootOrder[Index];
            (*DisBootOptionCount)++;
          } else {
            (*EnBootOption)[*EnBootOptionCount] = BootOrder[Index];
            (*EnBootOptionCount)++;
          }
          break;
        }
      }
    }
  
    //
    // Overwrite the old BootOption
    //
    CopyMem (&BootOrder[StartPosition], NewBootOption, (*DisBootOptionCount + *EnBootOptionCount) * sizeof (UINT16));
    Status = gRT->SetVariable (
                    L"BootOrder",
                    &gEfiGlobalVariableGuid,
                    VAR_FLAG,
                    BootOrderSize,
                    BootOrder
                    );
  
    FreePool (NewBootOption);
    FreePool (DeviceTypeArray);
    FreePool (BbsIndexArray);
  
    return Status;
  }
  
  /**
    Update the legacy BBS boot option. L"LegacyDevOrder" and gEfiLegacyDevOrderVariableGuid EFI Variable
    is updated with the new Legacy Boot order. The EFI Variable of "Boot####" and gEfiGlobalVariableGuid
    is also updated.
  
    @param NVMapData   The data for legacy BBS boot.
  
    @return EFI_SUCCESS           The function completed successfully.
    @retval EFI_NOT_FOUND         If L"LegacyDevOrder" and gEfiLegacyDevOrderVariableGuid EFI Variable can not be found.
    @retval EFI_OUT_OF_RESOURCES  Fail to allocate memory resource
    @retval other                 Contain some error, details see  the status return by gRT->SetVariable.
  **/
  EFI_STATUS
  UpdateBBSOption (
    IN LEGACY_BOOT_NV_DATA            *NVMapData
    )
  {
    UINTN                       Index;
    UINTN                       Index2;
    UINTN                       CurrentType;
    VOID                        *BootOptionVar;
    CHAR16                      VarName[100];
    UINTN                       OptionSize;
    EFI_STATUS                  Status;
    UINT32                      *Attribute;
    LEGACY_MENU_OPTION          *OptionMenu;
    UINT16                      *LegacyDev;
    UINT16                      *InitialLegacyDev;
    UINT8                       *VarData;
    UINTN                       VarSize;
    LEGACY_DEV_ORDER_ENTRY      *DevOrder;
    UINT8                       *OriginalPtr;
    UINT8                       *DisMap;
    UINTN                       Pos;
    UINTN                       Bit;
    UINT16                      *NewOrder;
    UINT16                      Tmp;
    UINT16                      *EnBootOption;
    UINTN                       EnBootOptionCount;
    UINT16                      *DisBootOption;
    UINTN                       DisBootOptionCount;
    UINTN                       BufferSize;
  
  
    DisMap              = NULL;
    NewOrder            = NULL;
    CurrentType         = 0;
    EnBootOption        = NULL;
    DisBootOption       = NULL;
  
  
    DisMap  = mLegacyBootOptionPrivate->MaintainMapData->DisableMap;
    Status  = EFI_SUCCESS;
  
    //
    // Update the Variable "LegacyDevOrder"
    //
    GetVariable2 (VAR_LEGACY_DEV_ORDER, &gEfiLegacyDevOrderVariableGuid, (VOID **) &VarData, &VarSize);
    if (VarData == NULL) {
      return EFI_NOT_FOUND;
    }
    OriginalPtr = VarData;
  
    while (mBbsType[CurrentType] != BBS_UNKNOWN) {
      switch (mBbsType[CurrentType]) {
      case BBS_FLOPPY:
        OptionMenu            = (LEGACY_MENU_OPTION *) &LegacyFDMenu;
        LegacyDev             = NVMapData->LegacyFD;
        InitialLegacyDev     = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyFD;
        BufferSize            = sizeof (NVMapData->LegacyFD);
        break;
  
      case BBS_HARDDISK:
        OptionMenu            = (LEGACY_MENU_OPTION *) &LegacyHDMenu;
        LegacyDev             = NVMapData->LegacyHD;
        InitialLegacyDev     = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyHD;
  
        BufferSize            = sizeof (NVMapData->LegacyHD);
        break;
  
      case BBS_CDROM:
        OptionMenu            = (LEGACY_MENU_OPTION *) &LegacyCDMenu;
        LegacyDev             = NVMapData->LegacyCD;
        InitialLegacyDev     = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyCD;
        BufferSize            = sizeof (NVMapData->LegacyCD);
        break;
  
      case BBS_EMBED_NETWORK:
        OptionMenu            = (LEGACY_MENU_OPTION *) &LegacyNETMenu;
        LegacyDev             = NVMapData->LegacyNET;
        InitialLegacyDev     = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyNET;
        BufferSize            = sizeof (NVMapData->LegacyNET);
        break;
  
      default:
        ASSERT (mBbsType[CurrentType] == BBS_BEV_DEVICE);
        OptionMenu            = (LEGACY_MENU_OPTION *) &LegacyBEVMenu;
        LegacyDev             = NVMapData->LegacyBEV;
        InitialLegacyDev     = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyBEV;
        BufferSize            = sizeof (NVMapData->LegacyBEV);
        break;
      }
  
      //
      // Check whether has value changed.
      //
      if (CompareMem (LegacyDev, InitialLegacyDev, BufferSize) == 0) {
        CurrentType++;
        continue;
      }
  
      DevOrder    = (LEGACY_DEV_ORDER_ENTRY *) OriginalPtr;
      while (VarData < OriginalPtr + VarSize) {
        if (DevOrder->BbsType == mBbsType[CurrentType]) {
          break;
        }
  
        VarData += sizeof (BBS_TYPE) + DevOrder->Length;
        DevOrder = (LEGACY_DEV_ORDER_ENTRY *) VarData;
      }
  
      if (VarData >= OriginalPtr + VarSize) {
        FreePool (OriginalPtr);
        return EFI_NOT_FOUND;
      }
  
      NewOrder = AllocateZeroPool (DevOrder->Length - sizeof (DevOrder->Length));
      if (NewOrder == NULL) {
        FreePool (OriginalPtr);
        return EFI_OUT_OF_RESOURCES;
      }
  
      for (Index = 0; Index < OptionMenu->MenuNumber; Index++) {
        if (0xFF == LegacyDev[Index]) {
          break;
        }
  
        NewOrder[Index] = LegacyDev[Index];
      }
  
      //
      // Only the enable/disable state of each boot device with same device type can be changed,
      // so we can count on the index information in DevOrder.
      // DisMap bit array is the only reliable source to check a device's en/dis state,
      // so we use DisMap to set en/dis state of each item in NewOrder array
      //
      for (Index2 = 0; Index2 < OptionMenu->MenuNumber; Index2++) {
        Tmp = (UINT16) (DevOrder->Data[Index2] & 0xFF);
        Pos = Tmp / 8;
        Bit = 7 - (Tmp % 8);
        if ((DisMap[Pos] & (1 << Bit)) != 0) {
          NewOrder[Index] = (UINT16) (0xFF00 | Tmp);
          Index++;
        }
      }
  
      CopyMem (
        DevOrder->Data,
        NewOrder,
        DevOrder->Length - sizeof (DevOrder->Length)
        );
      FreePool (NewOrder);
  
      //
      // Update BootOrder and Boot####.Attribute
      //
      // 1. Re-order the Option Number in BootOrder according to Legacy Dev Order
      //
      ASSERT (OptionMenu->MenuNumber == DevOrder->Length / sizeof (UINT16) - 1);
  
      Status = OrderLegacyBootOption4SameType (
        DevOrder->Data,
        DevOrder->Length / sizeof (UINT16) - 1,
        &EnBootOption,
        &EnBootOptionCount,
        &DisBootOption,
        &DisBootOptionCount
        );
       if (EFI_ERROR(Status)) {
         goto Fail;
       }
  
      //
      // 2. Deactivate the DisBootOption and activate the EnBootOption
      //
      for (Index = 0; Index < DisBootOptionCount; Index++) {
        UnicodeSPrint (VarName, sizeof (VarName), L"Boot%04x", DisBootOption[Index]);
        GetEfiGlobalVariable2 (VarName, (VOID **) &BootOptionVar, &OptionSize);
        if (BootOptionVar != NULL) {
          Attribute   = (UINT32 *) BootOptionVar;
          *Attribute &= ~LOAD_OPTION_ACTIVE;
  
          Status = gRT->SetVariable (
                          VarName,
                          &gEfiGlobalVariableGuid,
                          VAR_FLAG,
                          OptionSize,
                          BootOptionVar
                          );
  
          FreePool (BootOptionVar);
        }
      }
  
      for (Index = 0; Index < EnBootOptionCount; Index++) {
        UnicodeSPrint (VarName, sizeof (VarName), L"Boot%04x", EnBootOption[Index]);
        GetEfiGlobalVariable2 (VarName, (VOID **) &BootOptionVar, &OptionSize);
        if (BootOptionVar != NULL) {
          Attribute   = (UINT32 *) BootOptionVar;
          *Attribute |= LOAD_OPTION_ACTIVE;
  
          Status = gRT->SetVariable (
                          VarName,
                          &gEfiGlobalVariableGuid,
                          VAR_FLAG,
                          OptionSize,
                          BootOptionVar
                          );
  
          FreePool (BootOptionVar);
        }
      }
  
  
      FreePool (EnBootOption);
      FreePool (DisBootOption);
  
      CurrentType++;
    }
  
    Status = gRT->SetVariable (
                    VAR_LEGACY_DEV_ORDER,
                    &gEfiLegacyDevOrderVariableGuid,
                    EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                    VarSize,
                    OriginalPtr
                    );
  
  Fail:
    if (EnBootOption != NULL) {
      FreePool (EnBootOption);
    }
  
    if (DisBootOption != NULL) {
      FreePool (DisBootOption);
    }
  
    FreePool (OriginalPtr);
    return Status;
  }
  
  /**
    This function allows a caller to extract the current configuration for one
    or more named elements from the target driver.
  
  
    @param This            Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
    @param Request         A null-terminated Unicode string in <ConfigRequest> format.
    @param Progress        On return, points to a character in the Request string.
                           Points to the string's null terminator if request was successful.
                           Points to the most recent '&' before the first failing name/value
                           pair (or the beginning of the string if the failure is in the
                           first name/value pair) if the request was not successful.
    @param Results         A null-terminated Unicode string in <ConfigAltResp> format which
                           has all values filled in for the names in the Request string.
                           String to be allocated by the called function.
  
    @retval  EFI_SUCCESS            The Results is filled with the requested values.
    @retval  EFI_OUT_OF_RESOURCES   Not enough memory to store the results.
    @retval  EFI_INVALID_PARAMETER  Request is illegal syntax, or unknown name.
    @retval  EFI_NOT_FOUND          Routing data doesn't match any storage in this driver.
  
  **/
  EFI_STATUS
  EFIAPI
  LegacyBootOptionExtractConfig (
    IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
    IN  CONST EFI_STRING                       Request,
    OUT EFI_STRING                             *Progress,
    OUT EFI_STRING                             *Results
    )
  {
    if (Progress == NULL || Results == NULL) {
      return EFI_INVALID_PARAMETER;
    }
    *Progress = Request;
    return EFI_NOT_FOUND;
  }
  
  /**
    This function processes the results of changes in configuration.
  
  
    @param This            Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
    @param Configuration   A null-terminated Unicode string in <ConfigResp> format.
    @param Progress        A pointer to a string filled in with the offset of the most
                           recent '&' before the first failing name/value pair (or the
                           beginning of the string if the failure is in the first
                           name/value pair) or the terminating NULL if all was successful.
  
    @retval  EFI_SUCCESS            The Results is processed successfully.
    @retval  EFI_INVALID_PARAMETER  Configuration is NULL.
    @retval  EFI_NOT_FOUND          Routing data doesn't match any storage in this driver.
  
  **/
  EFI_STATUS
  EFIAPI
  LegacyBootOptionRouteConfig (
    IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
    IN  CONST EFI_STRING                       Configuration,
    OUT       EFI_STRING                       *Progress
    )
  {
    EFI_STATUS                      Status;
    EFI_HII_CONFIG_ROUTING_PROTOCOL *ConfigRouting;
    LEGACY_BOOT_NV_DATA             *CurrentNVMapData;
    UINTN                           BufferSize;
  
  
    if (Configuration == NULL || Progress == NULL) {
      return EFI_INVALID_PARAMETER;
    }
  
    *Progress = Configuration;
  
    //
    // Check routing data in <ConfigHdr>.
    // Note: there is no name for Name/Value storage, only GUID will be checked
    //
    if (!HiiIsConfigHdrMatch (Configuration, &mLegacyBootOptionGuid, mLegacyBootStorageName)) {
      return EFI_NOT_FOUND;
    }
  
    Status = gBS->LocateProtocol (
                    &gEfiHiiConfigRoutingProtocolGuid,
                    NULL,
                    (VOID **) &ConfigRouting
                    );
    if (EFI_ERROR (Status)) {
      return Status;
    }
  
    //
    // Convert <ConfigResp> to buffer data by helper function ConfigToBlock()
    //
    CurrentNVMapData = &mLegacyBootOptionPrivate->MaintainMapData->CurrentNvData;
    Status = ConfigRouting->ConfigToBlock (
                              ConfigRouting,
                              Configuration,
                              (UINT8 *) CurrentNVMapData,
                              &BufferSize,
                              Progress
                              );
    ASSERT_EFI_ERROR (Status);
  
    Status = UpdateBBSOption (CurrentNVMapData);
  
    return Status;
  }
  
  /**
    Refresh the global UpdateData structure.
  
  **/
  VOID
  RefreshLegacyUpdateData (
    VOID
    )
  {
    //
    // Free current updated date
    //
    if (mLegacyStartOpCodeHandle != NULL) {
      HiiFreeOpCodeHandle (mLegacyStartOpCodeHandle);
    }
    if (mLegacyEndOpCodeHandle != NULL) {
      HiiFreeOpCodeHandle (mLegacyEndOpCodeHandle);
    }
  
    //
    // Create new OpCode Handle
    //
    mLegacyStartOpCodeHandle = HiiAllocateOpCodeHandle ();
    mLegacyEndOpCodeHandle = HiiAllocateOpCodeHandle ();
  
    //
    // Create Hii Extend Label OpCode as the start opcode
    //
    mLegacyStartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (
                                           mLegacyStartOpCodeHandle,
                                           &gEfiIfrTianoGuid,
                                           NULL,
                                           sizeof (EFI_IFR_GUID_LABEL)
                                           );
    mLegacyStartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
  
    mLegacyStartLabel->Number = FORM_BOOT_LEGACY_DEVICE_ID;
  
    //
    // Create Hii Extend Label OpCode as the start opcode
    //
    mLegacyEndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (
                                           mLegacyEndOpCodeHandle,
                                           &gEfiIfrTianoGuid,
                                           NULL,
                                           sizeof (EFI_IFR_GUID_LABEL)
                                           );
    mLegacyEndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
  
    mLegacyEndLabel->Number = FORM_BOOT_LEGACY_LABEL_END;
  
  }
  
  /**
    Get the Menu Entry from the list in Menu Entry List.
  
    If MenuNumber is great or equal to the number of Menu
    Entry in the list, then ASSERT.
  
    @param MenuOption      The Menu Entry List to read the menu entry.
    @param MenuNumber      The index of Menu Entry.
  
    @return The Menu Entry.
  
  **/
  LEGACY_MENU_ENTRY *
  GetMenuEntry (
    LEGACY_MENU_OPTION      *MenuOption,
    UINTN                   MenuNumber
    )
  {
    LEGACY_MENU_ENTRY   *NewMenuEntry;
    UINTN               Index;
    LIST_ENTRY          *List;
  
    ASSERT (MenuNumber < MenuOption->MenuNumber);
  
    List = MenuOption->Head.ForwardLink;
    for (Index = 0; Index < MenuNumber; Index++) {
      List = List->ForwardLink;
    }
  
    NewMenuEntry = CR (List, LEGACY_MENU_ENTRY, Link, LEGACY_MENU_ENTRY_SIGNATURE);
  
    return NewMenuEntry;
  }
  
  /**
    Create string tokens for a menu from its help strings and display strings
  
    @param HiiHandle          Hii Handle of the package to be updated.
    @param MenuOption         The Menu whose string tokens need to be created
  
  **/
  VOID
  CreateLegacyMenuStringToken (
    IN EFI_HII_HANDLE                   HiiHandle,
    IN LEGACY_MENU_OPTION               *MenuOption
    )
  {
    LEGACY_MENU_ENTRY *NewMenuEntry;
    UINTN             Index;
  
    for (Index = 0; Index < MenuOption->MenuNumber; Index++) {
      NewMenuEntry = GetMenuEntry (MenuOption, Index);
  
      NewMenuEntry->DisplayStringToken = HiiSetString (
                                           HiiHandle,
                                           0,
                                           NewMenuEntry->DisplayString,
                                           NULL
                                           );
  
      if (NULL == NewMenuEntry->HelpString) {
        NewMenuEntry->HelpStringToken = NewMenuEntry->DisplayStringToken;
      } else {
        NewMenuEntry->HelpStringToken = HiiSetString (
                                          HiiHandle,
                                          0,
                                          NewMenuEntry->HelpString,
                                          NULL
                                          );
      }
    }
  }
  
  /**
    Create a dynamic page so that Legacy Device boot order
    can be set for specified device type.
  
    @param UpdatePageId    The form ID. It also specifies the legacy device type.
  
  
  **/
  VOID
  UpdateLegacyDeviceOrderPage (
    IN UINT16                           UpdatePageId
    )
  {
    LEGACY_MENU_OPTION          *OptionMenu;
    LEGACY_MENU_ENTRY           *NewMenuEntry;
    EFI_STRING_ID               StrRef;
    EFI_STRING_ID               StrRefHelp;
    UINT16                      *Default;
    UINT16                      Index;
    UINT16                      Key;
    CHAR16                      String[100];
    CHAR16                      *TypeStr;
    CHAR16                      *TypeStrHelp;
    CHAR16                      *FormTitle;
    VOID                        *OptionsOpCodeHandle;
    VOID                        *DefaultOpCodeHandle;
  
    Key         = 0;
    StrRef      = 0;
    StrRefHelp  = 0;
    OptionMenu  = NULL;
    TypeStr     = NULL;
    TypeStrHelp = NULL;
    Default     = NULL;
  
    RefreshLegacyUpdateData();
  
    //
    // Create oneof option list
    //
    switch (UpdatePageId) {
    case FORM_FLOPPY_BOOT_ID:
      OptionMenu  = (LEGACY_MENU_OPTION *) &LegacyFDMenu;
      Key         = (UINT16) LEGACY_FD_QUESTION_ID;
      TypeStr     = STR_FLOPPY;
      TypeStrHelp = STR_FLOPPY_HELP;
      FormTitle   = STR_FLOPPY_TITLE;
      Default     = mLegacyBootOptionPrivate->MaintainMapData->CurrentNvData.LegacyFD;
      break;
  
    case FORM_HARDDISK_BOOT_ID:
      OptionMenu  = (LEGACY_MENU_OPTION *) &LegacyHDMenu;
      Key         = (UINT16) LEGACY_HD_QUESTION_ID;
      TypeStr     = STR_HARDDISK;
      TypeStrHelp = STR_HARDDISK_HELP;
      FormTitle   = STR_HARDDISK_TITLE;
      Default     = mLegacyBootOptionPrivate->MaintainMapData->CurrentNvData.LegacyHD;
      break;
  
    case FORM_CDROM_BOOT_ID:
      OptionMenu  = (LEGACY_MENU_OPTION *) &LegacyCDMenu;
      Key         = (UINT16) LEGACY_CD_QUESTION_ID;
      TypeStr     = STR_CDROM;
      TypeStrHelp = STR_CDROM_HELP;
      FormTitle   = STR_CDROM_TITLE;
      Default     = mLegacyBootOptionPrivate->MaintainMapData->CurrentNvData.LegacyCD;
      break;
  
    case FORM_NET_BOOT_ID:
      OptionMenu  = (LEGACY_MENU_OPTION *) &LegacyNETMenu;
      Key         = (UINT16) LEGACY_NET_QUESTION_ID;
      TypeStr     = STR_NET;
      TypeStrHelp = STR_NET_HELP;
      FormTitle   = STR_NET_TITLE;
      Default     = mLegacyBootOptionPrivate->MaintainMapData->CurrentNvData.LegacyNET;
      break;
  
    case FORM_BEV_BOOT_ID:
      OptionMenu  = (LEGACY_MENU_OPTION *) &LegacyBEVMenu;
      Key         = (UINT16) LEGACY_BEV_QUESTION_ID;
      TypeStr     = STR_BEV;
      TypeStrHelp = STR_BEV_HELP;
      FormTitle   = STR_BEV_TITLE;
      Default     = mLegacyBootOptionPrivate->MaintainMapData->CurrentNvData.LegacyBEV;
      break;
  
    default:
      DEBUG ((EFI_D_ERROR, "Invalid command ID for updating page!\n"));
      return;
    }
  
    HiiSetString (mLegacyBootOptionPrivate->HiiHandle, STRING_TOKEN(STR_ORDER_CHANGE_PROMPT), FormTitle, NULL);
  
    CreateLegacyMenuStringToken (mLegacyBootOptionPrivate->HiiHandle, OptionMenu);
  
    OptionsOpCodeHandle = HiiAllocateOpCodeHandle ();
    ASSERT (OptionsOpCodeHandle != NULL);
  
  
    for (Index = 0; Index < OptionMenu->MenuNumber; Index++) {
      NewMenuEntry = GetMenuEntry (OptionMenu, Index);
      //
      // Create OneOf for each legacy device
      //
      HiiCreateOneOfOptionOpCode (
        OptionsOpCodeHandle,
        NewMenuEntry->DisplayStringToken,
        0,
        EFI_IFR_TYPE_NUM_SIZE_16,
        ((LEGACY_DEVICE_CONTEXT *) NewMenuEntry->VariableContext)->BbsIndex
        );
    }
  
    //
    // Create OneOf for item "Disabled"
    //
    HiiCreateOneOfOptionOpCode (
      OptionsOpCodeHandle,
      STRING_TOKEN (STR_DISABLE_LEGACY_DEVICE),
      0,
      EFI_IFR_TYPE_NUM_SIZE_16,
      0xFF
      );
  
    //
    // Create oneof tag here for FD/HD/CD #1 #2
    //
    for (Index = 0; Index < OptionMenu->MenuNumber; Index++) {
      DefaultOpCodeHandle = HiiAllocateOpCodeHandle ();
      ASSERT (DefaultOpCodeHandle != NULL);
  
      HiiCreateDefaultOpCode (
        DefaultOpCodeHandle,
        EFI_HII_DEFAULT_CLASS_STANDARD,
        EFI_IFR_TYPE_NUM_SIZE_16,
        *Default++
        );
  
      //
      // Create the string for oneof tag
      //
      UnicodeSPrint (String, sizeof (String), TypeStr, Index);
      StrRef = HiiSetString (mLegacyBootOptionPrivate->HiiHandle, 0, String, NULL);
  
      UnicodeSPrint (String, sizeof (String), TypeStrHelp, Index);
      StrRefHelp = HiiSetString (mLegacyBootOptionPrivate->HiiHandle, 0, String, NULL);
  
      HiiCreateOneOfOpCode (
        mLegacyStartOpCodeHandle,
        (EFI_QUESTION_ID) (Key + Index),
        VARSTORE_ID_LEGACY_BOOT,
        (UINT16) (Key + Index * 2 - CONFIG_OPTION_OFFSET),
        StrRef,
        StrRefHelp,
        EFI_IFR_FLAG_CALLBACK,
        EFI_IFR_NUMERIC_SIZE_2,
        OptionsOpCodeHandle,
        DefaultOpCodeHandle //NULL //
        );
  
      HiiFreeOpCodeHandle (DefaultOpCodeHandle);
    }
  
    HiiUpdateForm (
      mLegacyBootOptionPrivate->HiiHandle,
      &mLegacyBootOptionGuid,
      LEGACY_ORDER_CHANGE_FORM_ID,
      mLegacyStartOpCodeHandle,
      mLegacyEndOpCodeHandle
      );
  
    HiiFreeOpCodeHandle (OptionsOpCodeHandle);
  }
  
  
  /**
    Adjust question value when one question value has been changed.
  
    @param QuestionId    The question id for the value changed question.
    @param Value         The value for the changed question.
  
  **/
  VOID
  AdjustOptionValue (
    IN  UINT16                                 QuestionId,
    IN  EFI_IFR_TYPE_VALUE                     *Value
    )
  {
    UINTN                       Number;
    UINT16                      *Default;
    LEGACY_BOOT_NV_DATA         *CurrentNVMap;
    UINT16                      *CurrentVal;
    UINTN                       Index;
    UINTN                       Index2;
    UINTN                       Index3;
    UINTN                       NewValuePos;
    UINTN                       OldValue;
    UINTN                       NewValue;
    UINT8                       *DisMap;
    UINTN                       Pos;
    UINTN                       Bit;
  
    Number = 0;
    CurrentVal = 0;
    Default = NULL;
    NewValue = 0;
    NewValuePos = 0;
    OldValue = 0;
  
    //
    // Update Select FD/HD/CD/NET/BEV Order Form
    //
    ASSERT ((QuestionId >= LEGACY_FD_QUESTION_ID) && (QuestionId < LEGACY_BEV_QUESTION_ID + MAX_MENU_NUMBER));
  
    CurrentNVMap = &mLegacyBootOptionPrivate->MaintainMapData->CurrentNvData;
    HiiGetBrowserData (&mLegacyBootOptionGuid, mLegacyBootStorageName, sizeof (LEGACY_BOOT_NV_DATA), (UINT8 *) CurrentNVMap);
    DisMap  = mLegacyBootOptionPrivate->MaintainMapData->DisableMap;
  
    if (QuestionId >= LEGACY_FD_QUESTION_ID && QuestionId < LEGACY_FD_QUESTION_ID + MAX_MENU_NUMBER) {
      Number      = (UINT16) LegacyFDMenu.MenuNumber;
      CurrentVal  = CurrentNVMap->LegacyFD;
      Default     = mLegacyBootOptionPrivate->MaintainMapData->LastTimeNvData.LegacyFD;
    } else if (QuestionId >= LEGACY_HD_QUESTION_ID && QuestionId < LEGACY_HD_QUESTION_ID + MAX_MENU_NUMBER) {
      Number      = (UINT16) LegacyHDMenu.MenuNumber;
      CurrentVal  = CurrentNVMap->LegacyHD;
      Default     = mLegacyBootOptionPrivate->MaintainMapData->LastTimeNvData.LegacyHD;
    } else if (QuestionId >= LEGACY_CD_QUESTION_ID && QuestionId < LEGACY_CD_QUESTION_ID + MAX_MENU_NUMBER) {
      Number      = (UINT16) LegacyCDMenu.MenuNumber;
      CurrentVal  = CurrentNVMap->LegacyCD;
      Default     = mLegacyBootOptionPrivate->MaintainMapData->LastTimeNvData.LegacyCD;
    } else if (QuestionId >= LEGACY_NET_QUESTION_ID && QuestionId < LEGACY_NET_QUESTION_ID + MAX_MENU_NUMBER) {
      Number      = (UINT16) LegacyNETMenu.MenuNumber;
      CurrentVal  = CurrentNVMap->LegacyNET;
      Default     = mLegacyBootOptionPrivate->MaintainMapData->LastTimeNvData.LegacyNET;
    } else if (QuestionId >= LEGACY_BEV_QUESTION_ID && QuestionId < LEGACY_BEV_QUESTION_ID + MAX_MENU_NUMBER) {
      Number      = (UINT16) LegacyBEVMenu.MenuNumber;
      CurrentVal  = CurrentNVMap->LegacyBEV;
      Default     = mLegacyBootOptionPrivate->MaintainMapData->LastTimeNvData.LegacyBEV;
    }
  
    //
    //  First, find the different position
    //  if there is change, it should be only one
    //
    for (Index = 0; Index < Number; Index++) {
      if (CurrentVal[Index] != Default[Index]) {
        OldValue  = Default[Index];
        NewValue  = CurrentVal[Index];
        break;
      }
    }
  
    if (Index != Number) {
      //
      // there is change, now process
      //
      if (0xFF == NewValue) {
        //
        // This item will be disable
        // Just move the items behind this forward to overlap it
        //
        Pos = OldValue / 8;
        Bit = 7 - (OldValue % 8);
        DisMap[Pos] = (UINT8) (DisMap[Pos] | (UINT8) (1 << Bit));
        for (Index2 = Index; Index2 < Number - 1; Index2++) {
          CurrentVal[Index2] = CurrentVal[Index2 + 1];
        }
  
        CurrentVal[Index2] = 0xFF;
      } else {
        for (Index2 = 0; Index2 < Number; Index2++) {
          if (Index2 == Index) {
            continue;
          }
  
          if (Default[Index2] == NewValue) {
            //
            // If NewValue is in OldLegacyDev array
            // remember its old position
            //
            NewValuePos = Index2;
            break;
          }
        }
  
        if (Index2 != Number) {
          //
          // We will change current item to an existing item
          // (It's hard to describe here, please read code, it's like a cycle-moving)
          //
          for (Index2 = NewValuePos; Index2 != Index;) {
            if (NewValuePos < Index) {
              CurrentVal[Index2] = Default[Index2 + 1];
              Index2++;
            } else {
              CurrentVal[Index2] = Default[Index2 - 1];
              Index2--;
            }
          }
        } else {
          //
          // If NewValue is not in OldlegacyDev array, we are changing to a disabled item
          // so we should modify DisMap to reflect the change
          //
          Pos = NewValue / 8;
          Bit = 7 - (NewValue % 8);
          DisMap[Pos] = (UINT8) (DisMap[Pos] & (~ (UINT8) (1 << Bit)));
          if (0xFF != OldValue) {
            //
            // Because NewValue is a item that was disabled before
            // so after changing the OldValue should be disabled
            // actually we are doing a swap of enable-disable states of two items
            //
            Pos = OldValue / 8;
            Bit = 7 - (OldValue % 8);
            DisMap[Pos] = (UINT8) (DisMap[Pos] | (UINT8) (1 << Bit));
          }
        }
      }
      //
      // To prevent DISABLE appears in the middle of the list
      // we should perform a re-ordering
      //
      Index3 = Index;
      Index = 0;
      while (Index < Number) {
        if (0xFF != CurrentVal[Index]) {
          Index++;
          continue;
        }
  
        Index2 = Index;
        Index2++;
        while (Index2 < Number) {
          if (0xFF != CurrentVal[Index2]) {
            break;
          }
  
          Index2++;
        }
  
        if (Index2 < Number) {
          CurrentVal[Index]   = CurrentVal[Index2];
          CurrentVal[Index2]  = 0xFF;
        }
  
        Index++;
      }
  
      //
      // Return correct question value.
      //
      Value->u16 = CurrentVal[Index3];
      CopyMem (Default, CurrentVal, sizeof (UINT16) * Number);
    }
  
    //
    // Pass changed uncommitted data back to Form Browser
    //
    HiiSetBrowserData (&mLegacyBootOptionGuid, mLegacyBootStorageName, sizeof (LEGACY_BOOT_NV_DATA), (UINT8 *) CurrentNVMap, NULL);
  }
  
  /**
    This call back function is registered with Boot Manager formset.
    When user selects a boot option, this call back function will
    be triggered. The boot option is saved for later processing.
  
  
    @param This            Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
    @param Action          Specifies the type of action taken by the browser.
    @param QuestionId      A unique value which is sent to the original exporting driver
                           so that it can identify the type of data to expect.
    @param Type            The type of value for the question.
    @param Value           A pointer to the data being sent to the original exporting driver.
    @param ActionRequest   On return, points to the action requested by the callback function.
  
    @retval  EFI_SUCCESS           The callback successfully handled the action.
    @retval  EFI_INVALID_PARAMETER The setup browser call this function with invalid parameters.
  
  **/
  EFI_STATUS
  EFIAPI
  LegacyBootOptionCallback (
    IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
    IN  EFI_BROWSER_ACTION                     Action,
    IN  EFI_QUESTION_ID                        QuestionId,
    IN  UINT8                                  Type,
    IN  EFI_IFR_TYPE_VALUE                     *Value,
    OUT EFI_BROWSER_ACTION_REQUEST             *ActionRequest
    )
  {
    if (Action != EFI_BROWSER_ACTION_CHANGED && Action != EFI_BROWSER_ACTION_CHANGING && Action != EFI_BROWSER_ACTION_FORM_OPEN) {
      //
      // Do nothing for other UEFI Action. Only do call back when data is changed or the form is open.
      //
      return EFI_UNSUPPORTED;
    }
  
    if ((Value == NULL) || (ActionRequest == NULL)) {
      return EFI_INVALID_PARAMETER;
    }
  
    if (Action == EFI_BROWSER_ACTION_FORM_OPEN) {
      if (QuestionId == FORM_FLOPPY_BOOT_ID) {
        if (!mFirstEnterLegacyForm) {
          //
          // The legacyBootMaintUiLib depends on the LegacyBootManagerLib to realize its functionality.
          // We need to do the legacy boot options related actions after the LegacyBootManagerLib has been initialized.
          // Opening the legacy menus is the appropriate time that the LegacyBootManagerLib has already been initialized.
          //
          mFirstEnterLegacyForm = TRUE;
          GetLegacyOptions ();
          GetLegacyOptionsOrder ();
        }
      }
    }
  
    if (Action == EFI_BROWSER_ACTION_CHANGING) {
      switch (QuestionId) {
      case FORM_FLOPPY_BOOT_ID:
      case FORM_HARDDISK_BOOT_ID:
      case FORM_CDROM_BOOT_ID:
      case FORM_NET_BOOT_ID:
      case FORM_BEV_BOOT_ID:
        UpdateLegacyDeviceOrderPage (QuestionId);
        break;
  
      default:
        break;
      }
    } else if (Action == EFI_BROWSER_ACTION_CHANGED) {
      if ((Value == NULL) || (ActionRequest == NULL)) {
        return EFI_INVALID_PARAMETER;
      }
  
      if ((QuestionId >= LEGACY_FD_QUESTION_ID) && (QuestionId < LEGACY_BEV_QUESTION_ID + MAX_MENU_NUMBER)) {
        AdjustOptionValue(QuestionId, Value);
      }
    }
    return EFI_SUCCESS;
  }
  
  
  /**
    Create a menu entry by given menu type.
  
    @param MenuType        The Menu type to be created.
  
    @retval NULL           If failed to create the menu.
    @return the new menu entry.
  
  **/
  LEGACY_MENU_ENTRY *
  CreateMenuEntry (
    VOID
    )
  {
    LEGACY_MENU_ENTRY *MenuEntry;
  
    //
    // Create new menu entry
    //
    MenuEntry = AllocateZeroPool (sizeof (LEGACY_MENU_ENTRY));
    if (MenuEntry == NULL) {
      return NULL;
    }
  
    MenuEntry->VariableContext = AllocateZeroPool (sizeof (LEGACY_DEVICE_CONTEXT));
    if (MenuEntry->VariableContext == NULL) {
      FreePool (MenuEntry);
      return NULL;
    }
  
    MenuEntry->Signature        = LEGACY_MENU_ENTRY_SIGNATURE;
    return MenuEntry;
  }
  
  /**
  
    Base on the L"LegacyDevOrder" variable to build the current order data.
  
  **/
  VOID
  GetLegacyOptionsOrder (
    VOID
    )
  {
    UINTN                       VarSize;
    UINT8                       *VarData;
    UINT8                       *VarTmp;
    LEGACY_DEV_ORDER_ENTRY      *DevOrder;
    UINT16                      *LegacyDev;
    UINTN                       Index;
    LEGACY_MENU_OPTION          *OptionMenu;
    UINT16                      VarDevOrder;
    UINTN                       Pos;
    UINTN                       Bit;
    UINT8                       *DisMap;
    UINTN                       TotalLength;
  
    LegacyDev = NULL;
    OptionMenu = NULL;
  
    DisMap = ZeroMem (mLegacyBootOptionPrivate->MaintainMapData->DisableMap, sizeof (mLegacyBootOptionPrivate->MaintainMapData->DisableMap));
  
    //
    // Get Device Order from variable
    //
    GetVariable2 (VAR_LEGACY_DEV_ORDER, &gEfiLegacyDevOrderVariableGuid, (VOID **) &VarData, &VarSize);
    VarTmp = VarData;
    if (NULL != VarData) {
      DevOrder    = (LEGACY_DEV_ORDER_ENTRY *) VarData;
      while (VarData < VarTmp + VarSize) {
        switch (DevOrder->BbsType) {
        case BBS_FLOPPY:
          LegacyDev = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyFD;
          OptionMenu = &LegacyFDMenu;
          break;
  
        case BBS_HARDDISK:
          LegacyDev = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyHD;
          OptionMenu = &LegacyHDMenu;
          break;
  
        case BBS_CDROM:
          LegacyDev = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyCD;
          OptionMenu = &LegacyCDMenu;
          break;
  
        case BBS_EMBED_NETWORK:
          LegacyDev = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyNET;
          OptionMenu = &LegacyNETMenu;
          break;
  
        case BBS_BEV_DEVICE:
          LegacyDev = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyBEV;
          OptionMenu = &LegacyBEVMenu;
          break;
  
        case BBS_UNKNOWN:
        default:
          ASSERT (FALSE);
          DEBUG ((DEBUG_ERROR, "Unsupported device type found!\n"));
          break;
        }
  
        //
        // Create oneof tag here for FD/HD/CD #1 #2
        //
        for (Index = 0; Index < OptionMenu->MenuNumber; Index++) {
          TotalLength = sizeof (BBS_TYPE) + sizeof (UINT16) + Index * sizeof (UINT16);
          VarDevOrder = *(UINT16 *) ((UINT8 *) DevOrder + TotalLength);
  
          if (0xFF00 == (VarDevOrder & 0xFF00)) {
            LegacyDev[Index]  = 0xFF;
            Pos               = (VarDevOrder & 0xFF) / 8;
            Bit               = 7 - ((VarDevOrder & 0xFF) % 8);
            DisMap[Pos]       = (UINT8) (DisMap[Pos] | (UINT8) (1 << Bit));
          } else {
            LegacyDev[Index] = VarDevOrder & 0xFF;
          }
        }
  
        VarData ++;
        VarData += *(UINT16 *) VarData;
        DevOrder = (LEGACY_DEV_ORDER_ENTRY *) VarData;
      }
    }
  
    CopyMem (&mLegacyBootOptionPrivate->MaintainMapData->LastTimeNvData, &mLegacyBootOptionPrivate->MaintainMapData->InitialNvData, sizeof (LEGACY_BOOT_NV_DATA));
    CopyMem (&mLegacyBootOptionPrivate->MaintainMapData->CurrentNvData, &mLegacyBootOptionPrivate->MaintainMapData->InitialNvData, sizeof (LEGACY_BOOT_NV_DATA));
  }
  
  /**
  
    Build the LegacyFDMenu LegacyHDMenu LegacyCDMenu according to LegacyBios.GetBbsInfo().
  
  **/
  VOID
  GetLegacyOptions (
    VOID
    )
  {
    LEGACY_MENU_ENTRY             *NewMenuEntry;
    LEGACY_DEVICE_CONTEXT         *NewLegacyDevContext;
    EFI_BOOT_MANAGER_LOAD_OPTION  *BootOption;
    UINTN                         BootOptionCount;
    UINT16                        Index;
    UINTN                         FDNum;
    UINTN                         HDNum;
    UINTN                         CDNum;
    UINTN                         NETNum;
    UINTN                         BEVNum;
  
    //
    // Initialize Bbs Table Context from BBS info data
    //
    InitializeListHead (&LegacyFDMenu.Head);
    InitializeListHead (&LegacyHDMenu.Head);
    InitializeListHead (&LegacyCDMenu.Head);
    InitializeListHead (&LegacyNETMenu.Head);
    InitializeListHead (&LegacyBEVMenu.Head);
  
    FDNum   = 0;
    HDNum   = 0;
    CDNum   = 0;
    NETNum  = 0;
    BEVNum  = 0;
  
    EfiBootManagerConnectAll ();
  
    //
    // for better user experience
    // 1. User changes HD configuration (e.g.: unplug HDD), here we have a chance to remove the HDD boot option
    // 2. User enables/disables UEFI PXE, here we have a chance to add/remove EFI Network boot option
    //
    EfiBootManagerRefreshAllBootOption ();
  
    BootOption = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
    for (Index = 0; Index < BootOptionCount; Index++) {
      if ((DevicePathType (BootOption[Index].FilePath) != BBS_DEVICE_PATH) ||
          (DevicePathSubType (BootOption[Index].FilePath) != BBS_BBS_DP)
         ) {
        continue;
      }
      ASSERT (BootOption[Index].OptionalDataSize == sizeof (LEGACY_BOOT_OPTION_BBS_DATA));
      NewMenuEntry = CreateMenuEntry ();
      ASSERT (NewMenuEntry != NULL);
  
      NewLegacyDevContext              = (LEGACY_DEVICE_CONTEXT *) NewMenuEntry->VariableContext;
      NewLegacyDevContext->BbsIndex    = ((LEGACY_BOOT_OPTION_BBS_DATA *) BootOption[Index].OptionalData)->BbsIndex;
      NewLegacyDevContext->Description = AllocateCopyPool (StrSize (BootOption[Index].Description), BootOption[Index].Description);
      ASSERT (NewLegacyDevContext->Description != NULL);
  
      NewMenuEntry->DisplayString = NewLegacyDevContext->Description;
      NewMenuEntry->HelpString    = NULL;
  
      switch (((BBS_BBS_DEVICE_PATH *) BootOption[Index].FilePath)->DeviceType) {
      case BBS_TYPE_FLOPPY:
        InsertTailList (&LegacyFDMenu.Head, &NewMenuEntry->Link);
        FDNum++;
        break;
  
      case BBS_TYPE_HARDDRIVE:
        InsertTailList (&LegacyHDMenu.Head, &NewMenuEntry->Link);
        HDNum++;
        break;
  
      case BBS_TYPE_CDROM:
        InsertTailList (&LegacyCDMenu.Head, &NewMenuEntry->Link);
        CDNum++;
        break;
  
      case BBS_TYPE_EMBEDDED_NETWORK:
        InsertTailList (&LegacyNETMenu.Head, &NewMenuEntry->Link);
        NETNum++;
        break;
  
      case BBS_TYPE_BEV:
        InsertTailList (&LegacyBEVMenu.Head, &NewMenuEntry->Link);
        BEVNum++;
        break;
      }
    }
  
    EfiBootManagerFreeLoadOptions (BootOption, BootOptionCount);
  
    LegacyFDMenu.MenuNumber   = FDNum;
    LegacyHDMenu.MenuNumber   = HDNum;
    LegacyCDMenu.MenuNumber   = CDNum;
    LegacyNETMenu.MenuNumber  = NETNum;
    LegacyBEVMenu.MenuNumber  = BEVNum;
  }
  
  
  /**
  
    Install Boot Manager Menu driver.
  
    @param ImageHandle     The image handle.
    @param SystemTable     The system table.
  
    @retval  EFI_SUCCESS  Install Boot manager menu success.
    @retval  Other        Return error status.
  
  **/
  EFI_STATUS
  EFIAPI
  LegacyBootMaintUiLibConstructor (
    IN EFI_HANDLE                            ImageHandle,
    IN EFI_SYSTEM_TABLE                      *SystemTable
    )
  {
    EFI_STATUS                        Status;
    EFI_LEGACY_BIOS_PROTOCOL          *LegacyBios;
    LEGACY_BOOT_OPTION_CALLBACK_DATA  *LegacyBootOptionData;
  
    Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
    if (!EFI_ERROR (Status)) {
      //
      // Create LegacyBootOptionData structures for Driver Callback
      //
      LegacyBootOptionData = AllocateZeroPool (sizeof (LEGACY_BOOT_OPTION_CALLBACK_DATA));
      ASSERT (LegacyBootOptionData != NULL);
  
      LegacyBootOptionData->MaintainMapData = AllocateZeroPool (sizeof (LEGACY_BOOT_MAINTAIN_DATA));
      ASSERT (LegacyBootOptionData->MaintainMapData != NULL);
  
      LegacyBootOptionData->ConfigAccess.ExtractConfig = LegacyBootOptionExtractConfig;
      LegacyBootOptionData->ConfigAccess.RouteConfig   = LegacyBootOptionRouteConfig;
      LegacyBootOptionData->ConfigAccess.Callback      = LegacyBootOptionCallback;
  
      //
      // Install Device Path Protocol and Config Access protocol to driver handle
      //
      Status = gBS->InstallMultipleProtocolInterfaces (
                      &LegacyBootOptionData->DriverHandle,
                      &gEfiDevicePathProtocolGuid,
                      &mLegacyBootOptionHiiVendorDevicePath,
                      &gEfiHiiConfigAccessProtocolGuid,
                      &LegacyBootOptionData->ConfigAccess,
                      NULL
                      );
      ASSERT_EFI_ERROR (Status);
  
      //
      // Publish our HII data
      //
      LegacyBootOptionData->HiiHandle = HiiAddPackages (
                                        &mLegacyBootOptionGuid,
                                        LegacyBootOptionData->DriverHandle,
                                        LegacyBootMaintUiVfrBin,
                                        LegacyBootMaintUiLibStrings,
                                        NULL
                                        );
      ASSERT (LegacyBootOptionData->HiiHandle != NULL);
  
      mLegacyBootOptionPrivate = LegacyBootOptionData;
    }
  
    return EFI_SUCCESS;
  }
  
  /**
    Destructor of Customized Display Library Instance.
  
    @param  ImageHandle   The firmware allocated handle for the EFI image.
    @param  SystemTable   A pointer to the EFI System Table.
  
    @retval EFI_SUCCESS   The destructor completed successfully.
    @retval Other value   The destructor did not complete successfully.
  
  **/
  EFI_STATUS
  EFIAPI
  LegacyBootMaintUiLibDestructor (
    IN EFI_HANDLE        ImageHandle,
    IN EFI_SYSTEM_TABLE  *SystemTable
    )
  {
    EFI_STATUS    Status;
  
    if (mLegacyBootOptionPrivate != NULL && mLegacyBootOptionPrivate->DriverHandle != NULL) {
      Status = gBS->UninstallMultipleProtocolInterfaces (
                      mLegacyBootOptionPrivate->DriverHandle,
                      &gEfiDevicePathProtocolGuid,
                      &mLegacyBootOptionHiiVendorDevicePath,
                      &gEfiHiiConfigAccessProtocolGuid,
                      &mLegacyBootOptionPrivate->ConfigAccess,
                      NULL
                      );
      ASSERT_EFI_ERROR (Status);
  
      HiiRemovePackages (mLegacyBootOptionPrivate->HiiHandle);
  
      FreePool (mLegacyBootOptionPrivate->MaintainMapData);
      FreePool (mLegacyBootOptionPrivate);
    }
  
    return EFI_SUCCESS;
  }
  
  