]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Library/BootMaintenanceManagerLib/BootMaintenance.c
MdeModulePkg:Create Boot Maintenance Manager Library
[mirror_edk2.git] / MdeModulePkg / Library / BootMaintenanceManagerLib / BootMaintenance.c
diff --git a/MdeModulePkg/Library/BootMaintenanceManagerLib/BootMaintenance.c b/MdeModulePkg/Library/BootMaintenanceManagerLib/BootMaintenance.c
new file mode 100644 (file)
index 0000000..9b0653c
--- /dev/null
@@ -0,0 +1,1512 @@
+/** @file\r
+The functions for Boot Maintainence Main menu.\r
+\r
+Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "BootMaintenanceManager.h"\r
+\r
+#define FRONT_PAGE_KEY_OFFSET          0x4000\r
+//\r
+// Boot video resolution and text mode.\r
+//\r
+UINT32    mBmmBootHorizontalResolution    = 0;\r
+UINT32    mBmmBootVerticalResolution      = 0;\r
+UINT32    mBmmBootTextModeColumn          = 0;\r
+UINT32    mBmmBootTextModeRow             = 0;\r
+//\r
+// BIOS setup video resolution and text mode.\r
+//\r
+UINT32    mBmmSetupTextModeColumn         = 0;\r
+UINT32    mBmmSetupTextModeRow            = 0;\r
+UINT32    mBmmSetupHorizontalResolution   = 0;\r
+UINT32    mBmmSetupVerticalResolution     = 0;\r
+\r
+EFI_DEVICE_PATH_PROTOCOL  EndDevicePath[] = {\r
+  {\r
+    END_DEVICE_PATH_TYPE,\r
+    END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
+    {\r
+      END_DEVICE_PATH_LENGTH,\r
+      0\r
+    }\r
+  }\r
+};\r
+\r
+HII_VENDOR_DEVICE_PATH  mBmmHiiVendorDevicePath = {\r
+  {\r
+    {\r
+      HARDWARE_DEVICE_PATH,\r
+      HW_VENDOR_DP,\r
+      {\r
+        (UINT8) (sizeof (VENDOR_DEVICE_PATH)),\r
+        (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)\r
+      }\r
+    },\r
+    //\r
+    // {165A028F-0BB2-4b5f-8747-77592E3F6499}\r
+    //\r
+    { 0x165a028f, 0xbb2, 0x4b5f, { 0x87, 0x47, 0x77, 0x59, 0x2e, 0x3f, 0x64, 0x99 } }\r
+  },\r
+  {\r
+    END_DEVICE_PATH_TYPE,\r
+    END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
+    { \r
+      (UINT8) (END_DEVICE_PATH_LENGTH),\r
+      (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8)\r
+    }\r
+  }\r
+};\r
+\r
+EFI_GUID mBootMaintGuid          = BOOT_MAINT_FORMSET_GUID;\r
+\r
+CHAR16  mBootMaintStorageName[]     = L"BmmData";\r
+BMM_CALLBACK_DATA  gBootMaintenancePrivate = {\r
+  BMM_CALLBACK_DATA_SIGNATURE,\r
+  NULL,\r
+  NULL,\r
+  {\r
+    BootMaintExtractConfig,\r
+    BootMaintRouteConfig,\r
+    BootMaintCallback\r
+  }\r
+};\r
+\r
+BMM_CALLBACK_DATA *mBmmCallbackInfo = &gBootMaintenancePrivate;\r
+BOOLEAN  mAllMenuInit               = FALSE;\r
+\r
+/**\r
+  Init all memu.\r
+\r
+  @param CallbackData    The BMM context data.\r
+\r
+**/\r
+VOID\r
+InitAllMenu (\r
+  IN  BMM_CALLBACK_DATA    *CallbackData\r
+  );\r
+\r
+/**\r
+  Free up all Menu Option list.\r
+\r
+**/\r
+VOID\r
+FreeAllMenu (\r
+  VOID\r
+  );\r
+\r
+/**\r
+  This function will change video resolution and text mode\r
+  according to defined setup mode or defined boot mode  \r
+\r
+  @param  IsSetupMode   Indicate mode is changed to setup mode or boot mode. \r
+\r
+  @retval  EFI_SUCCESS  Mode is changed successfully.\r
+  @retval  Others       Mode failed to be changed.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+BmmBdsSetConsoleMode (\r
+  BOOLEAN  IsSetupMode\r
+  )\r
+{\r
+  EFI_GRAPHICS_OUTPUT_PROTOCOL          *GraphicsOutput;\r
+  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL       *SimpleTextOut;\r
+  UINTN                                 SizeOfInfo;\r
+  EFI_GRAPHICS_OUTPUT_MODE_INFORMATION  *Info;\r
+  UINT32                                MaxGopMode;\r
+  UINT32                                MaxTextMode;\r
+  UINT32                                ModeNumber;\r
+  UINT32                                NewHorizontalResolution;\r
+  UINT32                                NewVerticalResolution;\r
+  UINT32                                NewColumns;\r
+  UINT32                                NewRows;\r
+  UINTN                                 HandleCount;\r
+  EFI_HANDLE                            *HandleBuffer;\r
+  EFI_STATUS                            Status;\r
+  UINTN                                 Index;\r
+  UINTN                                 CurrentColumn;\r
+  UINTN                                 CurrentRow;  \r
+\r
+  MaxGopMode  = 0;\r
+  MaxTextMode = 0;\r
+\r
+  //\r
+  // Get current video resolution and text mode \r
+  //\r
+  Status = gBS->HandleProtocol (\r
+                  gST->ConsoleOutHandle,\r
+                  &gEfiGraphicsOutputProtocolGuid,\r
+                  (VOID**)&GraphicsOutput\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    GraphicsOutput = NULL;\r
+  }\r
+\r
+  Status = gBS->HandleProtocol (\r
+                  gST->ConsoleOutHandle,\r
+                  &gEfiSimpleTextOutProtocolGuid,\r
+                  (VOID**)&SimpleTextOut\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    SimpleTextOut = NULL;\r
+  }  \r
+\r
+  if ((GraphicsOutput == NULL) || (SimpleTextOut == NULL)) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  if (IsSetupMode) {\r
+    //\r
+    // The requried resolution and text mode is setup mode.\r
+    //\r
+    NewHorizontalResolution = mBmmSetupHorizontalResolution;\r
+    NewVerticalResolution   = mBmmSetupVerticalResolution;\r
+    NewColumns              = mBmmSetupTextModeColumn;\r
+    NewRows                 = mBmmSetupTextModeRow;\r
+  } else {\r
+    //\r
+    // The required resolution and text mode is boot mode.\r
+    //\r
+    NewHorizontalResolution = mBmmBootHorizontalResolution;\r
+    NewVerticalResolution   = mBmmBootVerticalResolution;\r
+    NewColumns              = mBmmBootTextModeColumn;\r
+    NewRows                 = mBmmBootTextModeRow;   \r
+  }\r
+\r
+  if (GraphicsOutput != NULL) {\r
+    MaxGopMode  = GraphicsOutput->Mode->MaxMode;\r
+  } \r
+\r
+  if (SimpleTextOut != NULL) {\r
+    MaxTextMode = SimpleTextOut->Mode->MaxMode;\r
+  }\r
+\r
+  //\r
+  // 1. If current video resolution is same with required video resolution,\r
+  //    video resolution need not be changed.\r
+  //    1.1. If current text mode is same with required text mode, text mode need not be changed.\r
+  //    1.2. If current text mode is different from required text mode, text mode need be changed.\r
+  // 2. If current video resolution is different from required video resolution, we need restart whole console drivers.\r
+  //\r
+  for (ModeNumber = 0; ModeNumber < MaxGopMode; ModeNumber++) {\r
+    Status = GraphicsOutput->QueryMode (\r
+                       GraphicsOutput,\r
+                       ModeNumber,\r
+                       &SizeOfInfo,\r
+                       &Info\r
+                       );\r
+    if (!EFI_ERROR (Status)) {\r
+      if ((Info->HorizontalResolution == NewHorizontalResolution) &&\r
+          (Info->VerticalResolution == NewVerticalResolution)) {\r
+        if ((GraphicsOutput->Mode->Info->HorizontalResolution == NewHorizontalResolution) &&\r
+            (GraphicsOutput->Mode->Info->VerticalResolution == NewVerticalResolution)) {\r
+          //\r
+          // Current resolution is same with required resolution, check if text mode need be set\r
+          //\r
+          Status = SimpleTextOut->QueryMode (SimpleTextOut, SimpleTextOut->Mode->Mode, &CurrentColumn, &CurrentRow);\r
+          ASSERT_EFI_ERROR (Status);\r
+          if (CurrentColumn == NewColumns && CurrentRow == NewRows) {\r
+            //\r
+            // If current text mode is same with required text mode. Do nothing\r
+            //\r
+            FreePool (Info);\r
+            return EFI_SUCCESS;\r
+          } else {\r
+            //\r
+            // If current text mode is different from requried text mode.  Set new video mode\r
+            //\r
+            for (Index = 0; Index < MaxTextMode; Index++) {\r
+              Status = SimpleTextOut->QueryMode (SimpleTextOut, Index, &CurrentColumn, &CurrentRow);\r
+              if (!EFI_ERROR(Status)) {\r
+                if ((CurrentColumn == NewColumns) && (CurrentRow == NewRows)) {\r
+                  //\r
+                  // Required text mode is supported, set it.\r
+                  //\r
+                  Status = SimpleTextOut->SetMode (SimpleTextOut, Index);\r
+                  ASSERT_EFI_ERROR (Status);\r
+                  //\r
+                  // Update text mode PCD.\r
+                  //\r
+                  PcdSet32 (PcdConOutColumn, mBmmSetupTextModeColumn);\r
+                  PcdSet32 (PcdConOutRow, mBmmSetupTextModeRow);\r
+                  FreePool (Info);\r
+                  return EFI_SUCCESS;\r
+                }\r
+              }\r
+            }\r
+            if (Index == MaxTextMode) {\r
+              //\r
+              // If requried text mode is not supported, return error.\r
+              //\r
+              FreePool (Info);\r
+              return EFI_UNSUPPORTED;\r
+            }\r
+          }\r
+        } else {\r
+          //\r
+          // If current video resolution is not same with the new one, set new video resolution.\r
+          // In this case, the driver which produces simple text out need be restarted.\r
+          //\r
+          Status = GraphicsOutput->SetMode (GraphicsOutput, ModeNumber);\r
+          if (!EFI_ERROR (Status)) {\r
+            FreePool (Info);\r
+            break;\r
+          }\r
+        }\r
+      }\r
+      FreePool (Info);\r
+    }\r
+  }\r
+\r
+  if (ModeNumber == MaxGopMode) {\r
+    //\r
+    // If the resolution is not supported, return error.\r
+    //\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  //\r
+  // Set PCD to Inform GraphicsConsole to change video resolution.\r
+  // Set PCD to Inform Consplitter to change text mode.\r
+  //\r
+  PcdSet32 (PcdVideoHorizontalResolution, NewHorizontalResolution);\r
+  PcdSet32 (PcdVideoVerticalResolution, NewVerticalResolution);\r
+  PcdSet32 (PcdConOutColumn, NewColumns);\r
+  PcdSet32 (PcdConOutRow, NewRows);\r
+\r
+  //\r
+  // Video mode is changed, so restart graphics console driver and higher level driver.\r
+  // Reconnect graphics console driver and higher level driver.\r
+  // Locate all the handles with GOP protocol and reconnect it.\r
+  //\r
+  Status = gBS->LocateHandleBuffer (\r
+                   ByProtocol,\r
+                   &gEfiSimpleTextOutProtocolGuid,\r
+                   NULL,\r
+                   &HandleCount,\r
+                   &HandleBuffer\r
+                   );\r
+  if (!EFI_ERROR (Status)) {\r
+    for (Index = 0; Index < HandleCount; Index++) {\r
+      gBS->DisconnectController (HandleBuffer[Index], NULL, NULL);\r
+    }\r
+    for (Index = 0; Index < HandleCount; Index++) {\r
+      gBS->ConnectController (HandleBuffer[Index], NULL, NULL, TRUE);\r
+    }\r
+    if (HandleBuffer != NULL) {\r
+      FreePool (HandleBuffer);\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  This function converts an input device structure to a Unicode string.\r
+\r
+  @param DevPath      A pointer to the device path structure.\r
+\r
+  @return             A new allocated Unicode string that represents the device path.\r
+\r
+**/\r
+CHAR16 *\r
+UiDevicePathToStr (\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *DevPath\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+  CHAR16                           *ToText;\r
+  EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *DevPathToText;\r
+\r
+  if (DevPath == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  Status = gBS->LocateProtocol (\r
+                  &gEfiDevicePathToTextProtocolGuid,\r
+                  NULL,\r
+                  (VOID **) &DevPathToText\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+  ToText = DevPathToText->ConvertDevicePathToText (\r
+                            DevPath,\r
+                            FALSE,\r
+                            TRUE\r
+                            );\r
+  ASSERT (ToText != NULL);\r
+  return ToText;\r
+}\r
+\r
+/**\r
+  Extract filename from device path. The returned buffer is allocated using AllocateCopyPool.\r
+  The caller is responsible for freeing the allocated buffer using FreePool().\r
+\r
+  @param DevicePath       Device path.\r
+\r
+  @return                 A new allocated string that represents the file name.\r
+\r
+**/\r
+CHAR16 *\r
+ExtractFileNameFromDevicePath (\r
+  IN   EFI_DEVICE_PATH_PROTOCOL *DevicePath\r
+  )\r
+{\r
+  CHAR16          *String;\r
+  CHAR16          *MatchString;\r
+  CHAR16          *LastMatch;\r
+  CHAR16          *FileName;\r
+  UINTN           Length;\r
+\r
+  ASSERT(DevicePath != NULL);\r
+\r
+  String = UiDevicePathToStr(DevicePath);\r
+  MatchString = String;\r
+  LastMatch   = String;\r
+\r
+  while(MatchString != NULL){\r
+    LastMatch   = MatchString + 1;\r
+    MatchString = StrStr(LastMatch,L"\\");\r
+  }\r
+\r
+  Length = StrLen(LastMatch);\r
+  FileName = AllocateCopyPool ((Length + 1) * sizeof(CHAR16), LastMatch);\r
+  *(FileName + Length) = 0;\r
+\r
+  FreePool(String);\r
+\r
+  return FileName;\r
+}\r
+\r
+/**\r
+  Extract device path for given HII handle and class guid.\r
+\r
+  @param Handle          The HII handle.\r
+\r
+  @retval  NULL          Fail to get the device path string.\r
+  @return  PathString    Get the device path string.\r
+\r
+**/\r
+CHAR16 *\r
+BmmExtractDevicePathFromHiiHandle (\r
+  IN      EFI_HII_HANDLE      Handle\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+  EFI_HANDLE                       DriverHandle;\r
+\r
+  ASSERT (Handle != NULL);\r
+\r
+  if (Handle == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  Status = gHiiDatabase->GetPackageListHandle (gHiiDatabase, Handle, &DriverHandle);\r
+  if (EFI_ERROR (Status)) {\r
+    return NULL;\r
+  }\r
+\r
+  //\r
+  // Get device path string.\r
+  //\r
+  return ConvertDevicePathToText(DevicePathFromHandle (DriverHandle), FALSE, FALSE);\r
+\r
+}\r
+\r
+/**\r
+  This function allows a caller to extract the current configuration for one\r
+  or more named elements from the target driver.\r
+\r
+  @param This            Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.\r
+  @param Request         A null-terminated Unicode string in <ConfigRequest> format.\r
+  @param Progress        On return, points to a character in the Request string.\r
+                         Points to the string's null terminator if request was successful.\r
+                         Points to the most recent '&' before the first failing name/value\r
+                         pair (or the beginning of the string if the failure is in the\r
+                         first name/value pair) if the request was not successful.\r
+  @param Results         A null-terminated Unicode string in <ConfigAltResp> format which\r
+                         has all values filled in for the names in the Request string.\r
+                         String to be allocated by the called function.\r
+\r
+  @retval  EFI_SUCCESS            The Results is filled with the requested values.\r
+  @retval  EFI_OUT_OF_RESOURCES   Not enough memory to store the results.\r
+  @retval  EFI_INVALID_PARAMETER  Request is NULL, illegal syntax, or unknown name.\r
+  @retval  EFI_NOT_FOUND          Routing data doesn't match any storage in this driver.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+BootMaintExtractConfig (\r
+  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,\r
+  IN  CONST EFI_STRING                       Request,\r
+  OUT EFI_STRING                             *Progress,\r
+  OUT EFI_STRING                             *Results\r
+  )\r
+{\r
+  EFI_STATUS         Status;\r
+  UINTN              BufferSize;\r
+  BMM_CALLBACK_DATA  *Private;\r
+  EFI_STRING                       ConfigRequestHdr;\r
+  EFI_STRING                       ConfigRequest;\r
+  BOOLEAN                          AllocatedRequest;\r
+  UINTN                            Size;\r
+\r
+  if (Progress == NULL || Results == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  *Progress = Request;\r
+  if ((Request != NULL) && !HiiIsConfigHdrMatch (Request, &mBootMaintGuid, mBootMaintStorageName)) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  ConfigRequestHdr = NULL;\r
+  ConfigRequest    = NULL;\r
+  AllocatedRequest = FALSE;\r
+  Size             = 0;\r
+\r
+  Private = BMM_CALLBACK_DATA_FROM_THIS (This);\r
+  //\r
+  // Convert buffer data to <ConfigResp> by helper function BlockToConfig()\r
+  //\r
+  BufferSize = sizeof (BMM_FAKE_NV_DATA);\r
+  ConfigRequest = Request;\r
+  if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) {\r
+    //\r
+    // Request has no request element, construct full request string.\r
+    // Allocate and fill a buffer large enough to hold the <ConfigHdr> template\r
+    // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator\r
+    //\r
+    ConfigRequestHdr = HiiConstructConfigHdr (&mBootMaintGuid, mBootMaintStorageName, Private->BmmDriverHandle);\r
+    Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16);\r
+    ConfigRequest = AllocateZeroPool (Size);\r
+    ASSERT (ConfigRequest != NULL);\r
+    AllocatedRequest = TRUE;\r
+    UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)BufferSize);\r
+    FreePool (ConfigRequestHdr);\r
+  }\r
+\r
+  Status = gHiiConfigRouting->BlockToConfig (\r
+                                gHiiConfigRouting,\r
+                                ConfigRequest,\r
+                                (UINT8 *) &Private->BmmFakeNvData,\r
+                                BufferSize,\r
+                                Results,\r
+                                Progress\r
+                                );\r
+  //\r
+  // Free the allocated config request string.\r
+  //\r
+  if (AllocatedRequest) {\r
+    FreePool (ConfigRequest);\r
+    ConfigRequest = NULL;\r
+  }\r
+  //\r
+  // Set Progress string to the original request string.\r
+  //\r
+  if (Request == NULL) {\r
+    *Progress = NULL;\r
+  } else if (StrStr (Request, L"OFFSET") == NULL) {\r
+    *Progress = Request + StrLen (Request);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  This function applies changes in a driver's configuration.\r
+  Input is a Configuration, which has the routing data for this\r
+  driver followed by name / value configuration pairs. The driver\r
+  must apply those pairs to its configurable storage. If the\r
+  driver's configuration is stored in a linear block of data\r
+  and the driver's name / value pairs are in <BlockConfig>\r
+  format, it may use the ConfigToBlock helper function (above) to\r
+  simplify the job. Currently not implemented.\r
+\r
+  @param[in]  This                Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.\r
+  @param[in]  Configuration       A null-terminated Unicode string in\r
+                                  <ConfigString> format.   \r
+  @param[out] Progress            A pointer to a string filled in with the\r
+                                  offset of the most recent '&' before the\r
+                                  first failing name / value pair (or the\r
+                                  beginn ing of the string if the failure\r
+                                  is in the first name / value pair) or\r
+                                  the terminating NULL if all was\r
+                                  successful.\r
+\r
+  @retval EFI_SUCCESS             The results have been distributed or are\r
+                                  awaiting distribution.  \r
+  @retval EFI_OUT_OF_RESOURCES    Not enough memory to store the\r
+                                  parts of the results that must be\r
+                                  stored awaiting possible future\r
+                                  protocols.\r
+  @retval EFI_INVALID_PARAMETERS  Passing in a NULL for the\r
+                                  Results parameter would result\r
+                                  in this type of error.\r
+  @retval EFI_NOT_FOUND           Target for the specified routing data\r
+                                  was not found.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+BootMaintRouteConfig (\r
+  IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,\r
+  IN CONST EFI_STRING                     Configuration,\r
+  OUT EFI_STRING                          *Progress\r
+  )\r
+{\r
+  EFI_STATUS                      Status;\r
+  UINTN                           BufferSize;\r
+  EFI_HII_CONFIG_ROUTING_PROTOCOL *ConfigRouting;\r
+  BMM_FAKE_NV_DATA                *NewBmmData;\r
+  BMM_FAKE_NV_DATA                *OldBmmData;\r
+  BM_CONSOLE_CONTEXT              *NewConsoleContext;\r
+  BM_TERMINAL_CONTEXT             *NewTerminalContext;\r
+  BM_MENU_ENTRY                   *NewMenuEntry;\r
+  BM_LOAD_CONTEXT                 *NewLoadContext;\r
+  UINT16                          Index;\r
+  BOOLEAN                         TerminalAttChange;\r
+  BMM_CALLBACK_DATA               *Private; \r
+\r
+  if (Progress == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  *Progress = Configuration;\r
+\r
+  if (Configuration == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Check routing data in <ConfigHdr>.\r
+  // Note: there is no name for Name/Value storage, only GUID will be checked\r
+  //\r
+  if (!HiiIsConfigHdrMatch (Configuration, &mBootMaintGuid, mBootMaintStorageName)) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  Status = gBS->LocateProtocol (\r
+                  &gEfiHiiConfigRoutingProtocolGuid, \r
+                  NULL, \r
+                  (VOID **)&ConfigRouting\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Private = BMM_CALLBACK_DATA_FROM_THIS (This);  \r
+  //\r
+  // Get Buffer Storage data from EFI variable\r
+  //\r
+  BufferSize = sizeof (BMM_FAKE_NV_DATA);\r
+  OldBmmData = &Private->BmmOldFakeNVData;\r
+  NewBmmData = &Private->BmmFakeNvData;\r
+  //\r
+  // Convert <ConfigResp> to buffer data by helper function ConfigToBlock()\r
+  //\r
+  Status = ConfigRouting->ConfigToBlock (\r
+                            ConfigRouting,\r
+                            Configuration,\r
+                            (UINT8 *) NewBmmData,\r
+                            &BufferSize,\r
+                            Progress\r
+                            );\r
+  ASSERT_EFI_ERROR (Status);    \r
+  //\r
+  // Compare new and old BMM configuration data and only do action for modified item to \r
+  // avoid setting unnecessary non-volatile variable\r
+  //\r
+\r
+  //\r
+  // Check data which located in BMM main page and save the settings if need\r
+  //         \r
+  if (CompareMem (&NewBmmData->BootNext, &OldBmmData->BootNext, sizeof (NewBmmData->BootNext)) != 0) {\r
+    Status = Var_UpdateBootNext (Private);\r
+  }\r
+\r
+  //\r
+  // Check data which located in Boot Options Menu and save the settings if need\r
+  //      \r
+  if (CompareMem (NewBmmData->BootOptionDel, OldBmmData->BootOptionDel, sizeof (NewBmmData->BootOptionDel)) != 0) {  \r
+    for (Index = 0; \r
+         ((Index < BootOptionMenu.MenuNumber) && (Index < (sizeof (NewBmmData->BootOptionDel) / sizeof (NewBmmData->BootOptionDel[0])))); \r
+         Index ++) {\r
+      NewMenuEntry            = BOpt_GetMenuEntry (&BootOptionMenu, Index);\r
+      NewLoadContext          = (BM_LOAD_CONTEXT *) NewMenuEntry->VariableContext;\r
+      NewLoadContext->Deleted = NewBmmData->BootOptionDel[Index];\r
+      NewBmmData->BootOptionDel[Index] = FALSE;\r
+      NewBmmData->BootOptionDelMark[Index] = FALSE;\r
+    }\r
+\r
+    Var_DelBootOption ();\r
+  }\r
+\r
+  if (CompareMem (NewBmmData->BootOptionOrder, OldBmmData->BootOptionOrder, sizeof (NewBmmData->BootOptionOrder)) != 0) {\r
+    Status = Var_UpdateBootOrder (Private);\r
+  }\r
+\r
+  if (CompareMem (&NewBmmData->BootTimeOut, &OldBmmData->BootTimeOut, sizeof (NewBmmData->BootTimeOut)) != 0){\r
+    Status = gRT->SetVariable(\r
+                    L"Timeout",\r
+                    &gEfiGlobalVariableGuid,\r
+                    EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
+                    sizeof(UINT16),\r
+                    &(NewBmmData->BootTimeOut)\r
+                    );\r
+    ASSERT_EFI_ERROR(Status);\r
+\r
+    Private->BmmOldFakeNVData.BootTimeOut = NewBmmData->BootTimeOut;\r
+  }\r
+\r
+  //\r
+  // Check data which located in Driver Options Menu and save the settings if need\r
+  //              \r
+  if (CompareMem (NewBmmData->DriverOptionDel, OldBmmData->DriverOptionDel, sizeof (NewBmmData->DriverOptionDel)) != 0) {       \r
+    for (Index = 0; \r
+         ((Index < DriverOptionMenu.MenuNumber) && (Index < (sizeof (NewBmmData->DriverOptionDel) / sizeof (NewBmmData->DriverOptionDel[0])))); \r
+         Index++) {\r
+      NewMenuEntry            = BOpt_GetMenuEntry (&DriverOptionMenu, Index);\r
+      NewLoadContext          = (BM_LOAD_CONTEXT *) NewMenuEntry->VariableContext;\r
+      NewLoadContext->Deleted = NewBmmData->DriverOptionDel[Index];\r
+      NewBmmData->DriverOptionDel[Index] = FALSE;\r
+      NewBmmData->DriverOptionDelMark[Index] = FALSE;\r
+    }\r
+    Var_DelDriverOption ();  \r
+  }\r
+\r
+  if (CompareMem (NewBmmData->DriverOptionOrder, OldBmmData->DriverOptionOrder, sizeof (NewBmmData->DriverOptionOrder)) != 0) {  \r
+    Status = Var_UpdateDriverOrder (Private);\r
+  }\r
+\r
+  if (CompareMem (&NewBmmData->ConsoleOutMode, &OldBmmData->ConsoleOutMode, sizeof (NewBmmData->ConsoleOutMode)) != 0){\r
+    Var_UpdateConMode(Private);\r
+  }\r
+\r
+  TerminalAttChange = FALSE;\r
+  for (Index = 0; Index < TerminalMenu.MenuNumber; Index++) {\r
+\r
+    //\r
+    // only need update modified items\r
+    //\r
+    if (CompareMem (&NewBmmData->COMBaudRate[Index], &OldBmmData->COMBaudRate[Index], sizeof (NewBmmData->COMBaudRate[Index])) == 0 &&\r
+         CompareMem (&NewBmmData->COMDataRate[Index], &OldBmmData->COMDataRate[Index], sizeof (NewBmmData->COMDataRate[Index])) == 0 &&\r
+         CompareMem (&NewBmmData->COMStopBits[Index], &OldBmmData->COMStopBits[Index], sizeof (NewBmmData->COMStopBits[Index])) == 0 &&\r
+         CompareMem (&NewBmmData->COMParity[Index], &OldBmmData->COMParity[Index], sizeof (NewBmmData->COMParity[Index])) == 0 &&\r
+         CompareMem (&NewBmmData->COMTerminalType[Index], &OldBmmData->COMTerminalType[Index], sizeof (NewBmmData->COMTerminalType[Index])) == 0 &&\r
+         CompareMem (&NewBmmData->COMFlowControl[Index], &OldBmmData->COMFlowControl[Index], sizeof (NewBmmData->COMFlowControl[Index])) == 0) {\r
+      continue;\r
+    }\r
+\r
+    NewMenuEntry = BOpt_GetMenuEntry (&TerminalMenu, Index);\r
+    ASSERT (NewMenuEntry != NULL);\r
+    NewTerminalContext = (BM_TERMINAL_CONTEXT *) NewMenuEntry->VariableContext;\r
+    NewTerminalContext->BaudRateIndex = NewBmmData->COMBaudRate[Index];\r
+    ASSERT (NewBmmData->COMBaudRate[Index] < (sizeof (BaudRateList) / sizeof (BaudRateList[0])));\r
+    NewTerminalContext->BaudRate      = BaudRateList[NewBmmData->COMBaudRate[Index]].Value;\r
+    NewTerminalContext->DataBitsIndex = NewBmmData->COMDataRate[Index];\r
+    ASSERT (NewBmmData->COMDataRate[Index] < (sizeof (DataBitsList) / sizeof (DataBitsList[0])));\r
+    NewTerminalContext->DataBits      = (UINT8) DataBitsList[NewBmmData->COMDataRate[Index]].Value;\r
+    NewTerminalContext->StopBitsIndex = NewBmmData->COMStopBits[Index];\r
+    ASSERT (NewBmmData->COMStopBits[Index] < (sizeof (StopBitsList) / sizeof (StopBitsList[0])));\r
+    NewTerminalContext->StopBits      = (UINT8) StopBitsList[NewBmmData->COMStopBits[Index]].Value;\r
+    NewTerminalContext->ParityIndex   = NewBmmData->COMParity[Index];\r
+    ASSERT (NewBmmData->COMParity[Index] < (sizeof (ParityList) / sizeof (ParityList[0])));\r
+    NewTerminalContext->Parity        = (UINT8) ParityList[NewBmmData->COMParity[Index]].Value;\r
+    NewTerminalContext->TerminalType  = NewBmmData->COMTerminalType[Index];\r
+    NewTerminalContext->FlowControl   = NewBmmData->COMFlowControl[Index];\r
+    ChangeTerminalDevicePath (\r
+      NewTerminalContext->DevicePath,\r
+      FALSE\r
+      );\r
+    TerminalAttChange = TRUE;\r
+  }\r
+  if (TerminalAttChange) {\r
+    Var_UpdateConsoleInpOption ();\r
+    Var_UpdateConsoleOutOption ();\r
+    Var_UpdateErrorOutOption ();\r
+  }\r
+  //\r
+  // Check data which located in Console Options Menu and save the settings if need\r
+  //\r
+  if (CompareMem (NewBmmData->ConsoleInCheck, OldBmmData->ConsoleInCheck, sizeof (NewBmmData->ConsoleInCheck)) != 0){\r
+    for (Index = 0; Index < ConsoleInpMenu.MenuNumber; Index++){\r
+      NewMenuEntry                = BOpt_GetMenuEntry(&ConsoleInpMenu, Index);\r
+      NewConsoleContext           = (BM_CONSOLE_CONTEXT *)NewMenuEntry->VariableContext;\r
+      ASSERT (Index < MAX_MENU_NUMBER);\r
+      NewConsoleContext->IsActive = NewBmmData->ConsoleInCheck[Index];\r
+    }\r
+    for (Index = 0; Index < TerminalMenu.MenuNumber; Index++) {\r
+      NewMenuEntry                = BOpt_GetMenuEntry (&TerminalMenu, Index);\r
+      NewTerminalContext          = (BM_TERMINAL_CONTEXT *) NewMenuEntry->VariableContext;\r
+      ASSERT (Index + ConsoleInpMenu.MenuNumber < MAX_MENU_NUMBER);\r
+      NewTerminalContext->IsConIn = NewBmmData->ConsoleInCheck[Index + ConsoleInpMenu.MenuNumber];\r
+    }\r
+    Var_UpdateConsoleInpOption();\r
+  }\r
+\r
+  if (CompareMem (NewBmmData->ConsoleOutCheck, OldBmmData->ConsoleOutCheck, sizeof (NewBmmData->ConsoleOutCheck)) != 0){\r
+    for (Index = 0; Index < ConsoleOutMenu.MenuNumber; Index++){\r
+      NewMenuEntry                = BOpt_GetMenuEntry(&ConsoleOutMenu, Index);\r
+      NewConsoleContext           = (BM_CONSOLE_CONTEXT *)NewMenuEntry->VariableContext;\r
+      ASSERT (Index < MAX_MENU_NUMBER);\r
+      NewConsoleContext->IsActive = NewBmmData->ConsoleOutCheck[Index];\r
+    }\r
+    for (Index = 0; Index < TerminalMenu.MenuNumber; Index++) {\r
+      NewMenuEntry                = BOpt_GetMenuEntry (&TerminalMenu, Index);\r
+      NewTerminalContext          = (BM_TERMINAL_CONTEXT *) NewMenuEntry->VariableContext;\r
+      ASSERT (Index + ConsoleOutMenu.MenuNumber < MAX_MENU_NUMBER);\r
+      NewTerminalContext->IsConOut = NewBmmData->ConsoleOutCheck[Index + ConsoleOutMenu.MenuNumber];\r
+    }\r
+    Var_UpdateConsoleOutOption();\r
+  }\r
+\r
+  if (CompareMem (NewBmmData->ConsoleErrCheck, OldBmmData->ConsoleErrCheck, sizeof (NewBmmData->ConsoleErrCheck)) != 0){\r
+    for (Index = 0; Index < ConsoleErrMenu.MenuNumber; Index++){\r
+      NewMenuEntry                = BOpt_GetMenuEntry(&ConsoleErrMenu, Index);\r
+      NewConsoleContext           = (BM_CONSOLE_CONTEXT *)NewMenuEntry->VariableContext;\r
+      ASSERT (Index < MAX_MENU_NUMBER);\r
+      NewConsoleContext->IsActive = NewBmmData->ConsoleErrCheck[Index];\r
+    }\r
+    for (Index = 0; Index < TerminalMenu.MenuNumber; Index++) {\r
+      NewMenuEntry                = BOpt_GetMenuEntry (&TerminalMenu, Index);\r
+      NewTerminalContext          = (BM_TERMINAL_CONTEXT *) NewMenuEntry->VariableContext;\r
+      ASSERT (Index + ConsoleErrMenu.MenuNumber < MAX_MENU_NUMBER);\r
+      NewTerminalContext->IsStdErr = NewBmmData->ConsoleErrCheck[Index + ConsoleErrMenu.MenuNumber];\r
+    }\r
+    Var_UpdateErrorOutOption();\r
+  }\r
+\r
+  if (CompareMem (NewBmmData->BootDescriptionData, OldBmmData->BootDescriptionData, sizeof (NewBmmData->BootDescriptionData)) != 0 ||\r
+       CompareMem (NewBmmData->BootOptionalData, OldBmmData->BootOptionalData, sizeof (NewBmmData->BootOptionalData)) != 0) {\r
+    Status = Var_UpdateBootOption (Private);\r
+    NewBmmData->BootOptionChanged = FALSE;\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+    BOpt_GetBootOptions (Private);\r
+  }\r
+\r
+  if (CompareMem (NewBmmData->DriverDescriptionData, OldBmmData->DriverDescriptionData, sizeof (NewBmmData->DriverDescriptionData)) != 0 ||\r
+       CompareMem (NewBmmData->DriverOptionalData, OldBmmData->DriverOptionalData, sizeof (NewBmmData->DriverOptionalData)) != 0) {\r
+    Status = Var_UpdateDriverOption (\r
+              Private,\r
+              Private->BmmHiiHandle,\r
+              NewBmmData->DriverDescriptionData,\r
+              NewBmmData->DriverOptionalData,\r
+              NewBmmData->ForceReconnect\r
+              );\r
+    NewBmmData->DriverOptionChanged = FALSE;\r
+    NewBmmData->ForceReconnect      = TRUE;\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    BOpt_GetDriverOptions (Private);\r
+  }\r
+\r
+  //\r
+  // After user do the save action, need to update OldBmmData.\r
+  //\r
+  CopyMem (OldBmmData, NewBmmData, sizeof (BMM_FAKE_NV_DATA));\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  This function processes the results of changes in configuration.\r
+\r
+\r
+  @param This               Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.\r
+  @param Action             Specifies the type of action taken by the browser.\r
+  @param QuestionId         A unique value which is sent to the original exporting driver\r
+                            so that it can identify the type of data to expect.\r
+  @param Type               The type of value for the question.\r
+  @param Value              A pointer to the data being sent to the original exporting driver.\r
+  @param ActionRequest      On return, points to the action requested by the callback function.\r
+\r
+  @retval EFI_SUCCESS           The callback successfully handled the action.\r
+  @retval EFI_OUT_OF_RESOURCES  Not enough storage is available to hold the variable and its data.\r
+  @retval EFI_DEVICE_ERROR      The variable could not be saved.\r
+  @retval EFI_UNSUPPORTED       The specified Action is not supported by the callback.\r
+  @retval EFI_INVALID_PARAMETER The parameter of Value or ActionRequest is invalid.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+BootMaintCallback (\r
+  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL         *This,\r
+  IN        EFI_BROWSER_ACTION                     Action,\r
+  IN        EFI_QUESTION_ID                        QuestionId,\r
+  IN        UINT8                                  Type,\r
+  IN        EFI_IFR_TYPE_VALUE                     *Value,\r
+  OUT       EFI_BROWSER_ACTION_REQUEST             *ActionRequest\r
+  )\r
+{\r
+  BMM_CALLBACK_DATA *Private;\r
+  BM_MENU_ENTRY     *NewMenuEntry;\r
+  BMM_FAKE_NV_DATA  *CurrentFakeNVMap;\r
+  UINTN             OldValue;\r
+  UINTN             NewValue;\r
+  UINTN             Number;\r
+  UINTN             Index;\r
+  EFI_DEVICE_PATH_PROTOCOL * File;\r
+\r
+  if (Action != EFI_BROWSER_ACTION_CHANGING && Action != EFI_BROWSER_ACTION_CHANGED) {\r
+    //\r
+    // Do nothing for other UEFI Action. Only do call back when data is changed.\r
+    //\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+  OldValue       = 0;\r
+  NewValue       = 0;\r
+  Number         = 0;\r
+\r
+  Private        = BMM_CALLBACK_DATA_FROM_THIS (This);\r
+  //\r
+  // Retrive uncommitted data from Form Browser\r
+  //\r
+  CurrentFakeNVMap = &Private->BmmFakeNvData;\r
+  HiiGetBrowserData (&mBootMaintGuid, mBootMaintStorageName, sizeof (BMM_FAKE_NV_DATA), (UINT8 *) CurrentFakeNVMap);\r
+\r
+  if (Action == EFI_BROWSER_ACTION_CHANGING) {\r
+    if (Value == NULL) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+    \r
+    UpdatePageId (Private, QuestionId);\r
+\r
+    if (QuestionId < FILE_OPTION_OFFSET) {\r
+      if (QuestionId < CONFIG_OPTION_OFFSET) {\r
+        switch (QuestionId) {\r
+        case FORM_BOOT_ADD_ID:\r
+          // Leave BMM and enter FileExplorer. \r
+          ChooseFile( NULL, L".efi", (CHOOSE_HANDLER) CreateBootOptionFromFile, &File);\r
+          break;\r
+\r
+        case FORM_DRV_ADD_FILE_ID:\r
+          // Leave BMM and enter FileExplorer.\r
+          ChooseFile( NULL, L".efi", (CHOOSE_HANDLER) CreateDriverOptionFromFile, &File);\r
+          break;\r
+\r
+        case FORM_DRV_ADD_HANDLE_ID:\r
+          CleanUpPage (FORM_DRV_ADD_HANDLE_ID, Private);\r
+          UpdateDrvAddHandlePage (Private);\r
+          break;\r
+\r
+        case FORM_BOOT_DEL_ID:\r
+          CleanUpPage (FORM_BOOT_DEL_ID, Private);\r
+          UpdateBootDelPage (Private);\r
+          break;\r
+\r
+        case FORM_BOOT_CHG_ID:\r
+        case FORM_DRV_CHG_ID:\r
+          UpdatePageBody (QuestionId, Private);\r
+          break;\r
+\r
+        case FORM_DRV_DEL_ID:\r
+          CleanUpPage (FORM_DRV_DEL_ID, Private);\r
+          UpdateDrvDelPage (Private);\r
+          break;\r
+\r
+        case FORM_BOOT_NEXT_ID:\r
+          CleanUpPage (FORM_BOOT_NEXT_ID, Private);\r
+          UpdateBootNextPage (Private);\r
+          break;\r
+\r
+        case FORM_TIME_OUT_ID:\r
+          CleanUpPage (FORM_TIME_OUT_ID, Private);\r
+          UpdateTimeOutPage (Private);\r
+          break;\r
+\r
+        case FORM_CON_IN_ID:\r
+        case FORM_CON_OUT_ID:\r
+        case FORM_CON_ERR_ID:\r
+          UpdatePageBody (QuestionId, Private);\r
+          break;\r
+\r
+        case FORM_CON_MODE_ID:\r
+          CleanUpPage (FORM_CON_MODE_ID, Private);\r
+          UpdateConModePage (Private);\r
+          break;\r
+\r
+        case FORM_CON_COM_ID:\r
+          CleanUpPage (FORM_CON_COM_ID, Private);\r
+          UpdateConCOMPage (Private);\r
+          break;\r
+\r
+        default:\r
+          break;\r
+        }\r
+      } else if ((QuestionId >= TERMINAL_OPTION_OFFSET) && (QuestionId < CONSOLE_OPTION_OFFSET)) {\r
+        Index                  = (UINT16) (QuestionId - TERMINAL_OPTION_OFFSET);\r
+        Private->CurrentTerminal  = Index;\r
+\r
+        CleanUpPage (FORM_CON_COM_SETUP_ID, Private);\r
+        UpdateTerminalPage (Private);\r
+\r
+      } else if (QuestionId >= HANDLE_OPTION_OFFSET) {\r
+        Index                  = (UINT16) (QuestionId - HANDLE_OPTION_OFFSET);\r
+\r
+        NewMenuEntry            = BOpt_GetMenuEntry (&DriverMenu, Index);\r
+        ASSERT (NewMenuEntry != NULL);\r
+        Private->HandleContext  = (BM_HANDLE_CONTEXT *) NewMenuEntry->VariableContext;\r
+\r
+        CleanUpPage (FORM_DRV_ADD_HANDLE_DESC_ID, Private);\r
+\r
+        Private->MenuEntry                  = NewMenuEntry;\r
+        Private->LoadContext->FilePathList  = Private->HandleContext->DevicePath;\r
+\r
+        UpdateDriverAddHandleDescPage (Private);\r
+      }\r
+    }\r
+    if (QuestionId == KEY_VALUE_BOOT_FROM_FILE){\r
+      // Leave BMM and enter FileExplorer.\r
+      ChooseFile( NULL, L".efi", (CHOOSE_HANDLER) BootFromFile, &File);\r
+    }\r
+  } else if (Action == EFI_BROWSER_ACTION_CHANGED) {\r
+    if ((Value == NULL) || (ActionRequest == NULL)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+   \r
+    if (QuestionId == KEY_VALUE_SAVE_AND_EXIT_BOOT) {\r
+      CurrentFakeNVMap->BootOptionChanged = FALSE;\r
+      *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_SUBMIT_EXIT;\r
+    } else if (QuestionId == KEY_VALUE_SAVE_AND_EXIT_DRIVER) {\r
+      CurrentFakeNVMap->DriverOptionChanged = FALSE;\r
+      *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_SUBMIT_EXIT;\r
+    } else if (QuestionId == KEY_VALUE_NO_SAVE_AND_EXIT_DRIVER) {\r
+      //\r
+      // Discard changes and exit formset\r
+      //\r
+      CurrentFakeNVMap->DriverOptionalData[0]     = 0x0000;\r
+      CurrentFakeNVMap->DriverDescriptionData[0]  = 0x0000;\r
+      CurrentFakeNVMap->DriverOptionChanged = FALSE;\r
+      CurrentFakeNVMap->ForceReconnect      = TRUE;\r
+      *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT;\r
+    } else if (QuestionId == KEY_VALUE_NO_SAVE_AND_EXIT_BOOT) {\r
+      //\r
+      // Discard changes and exit formset\r
+      //\r
+      CurrentFakeNVMap->BootOptionalData[0]     = 0x0000;\r
+      CurrentFakeNVMap->BootDescriptionData[0]  = 0x0000;\r
+      CurrentFakeNVMap->BootOptionChanged = FALSE;\r
+      *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT;\r
+    } else if (QuestionId == KEY_VALUE_BOOT_DESCRIPTION || QuestionId == KEY_VALUE_BOOT_OPTION) {\r
+      CurrentFakeNVMap->BootOptionChanged = TRUE;\r
+    } else if (QuestionId == KEY_VALUE_DRIVER_DESCRIPTION || QuestionId == KEY_VALUE_DRIVER_OPTION) {\r
+      CurrentFakeNVMap->DriverOptionChanged = TRUE;\r
+    } \r
+\r
+    if ((QuestionId >= BOOT_OPTION_DEL_QUESTION_ID) && (QuestionId < BOOT_OPTION_DEL_QUESTION_ID + MAX_MENU_NUMBER)) {\r
+      if (Value->b){\r
+        //\r
+        // Means user try to delete this boot option but not press F10 or "Commit Changes and Exit" menu.\r
+        //\r
+        CurrentFakeNVMap->BootOptionDelMark[QuestionId - BOOT_OPTION_DEL_QUESTION_ID] = TRUE;\r
+      } else {\r
+        //\r
+        // Means user remove the old check status.\r
+        //\r
+        CurrentFakeNVMap->BootOptionDelMark[QuestionId - BOOT_OPTION_DEL_QUESTION_ID] = FALSE;\r
+      }\r
+    } else if ((QuestionId >= DRIVER_OPTION_DEL_QUESTION_ID) && (QuestionId < DRIVER_OPTION_DEL_QUESTION_ID + MAX_MENU_NUMBER)) {\r
+      if (Value->b){\r
+        CurrentFakeNVMap->DriverOptionDelMark[QuestionId - DRIVER_OPTION_DEL_QUESTION_ID] = TRUE;\r
+      } else {\r
+        CurrentFakeNVMap->DriverOptionDelMark[QuestionId - DRIVER_OPTION_DEL_QUESTION_ID] = FALSE;\r
+      }\r
+    } else {\r
+      switch (QuestionId) {\r
+      case KEY_VALUE_SAVE_AND_EXIT:\r
+      case KEY_VALUE_NO_SAVE_AND_EXIT:\r
+        if (QuestionId == KEY_VALUE_SAVE_AND_EXIT) {\r
+          *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_SUBMIT_EXIT;\r
+        } else if (QuestionId == KEY_VALUE_NO_SAVE_AND_EXIT) {\r
+          DiscardChangeHandler (Private, CurrentFakeNVMap);\r
+          *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT;\r
+        }\r
+\r
+        break;\r
+\r
+      case FORM_RESET:\r
+        gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL);\r
+        return EFI_UNSUPPORTED;\r
+\r
+      default:\r
+        break;\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
+  // Pass changed uncommitted data back to Form Browser\r
+  //\r
+  HiiSetBrowserData (&mBootMaintGuid, mBootMaintStorageName, sizeof (BMM_FAKE_NV_DATA), (UINT8 *) CurrentFakeNVMap, NULL);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Discard all changes done to the BMM pages such as Boot Order change,\r
+  Driver order change.\r
+\r
+  @param Private            The BMM context data.\r
+  @param CurrentFakeNVMap   The current Fack NV Map.\r
+\r
+**/\r
+VOID\r
+DiscardChangeHandler (\r
+  IN  BMM_CALLBACK_DATA               *Private,\r
+  IN  BMM_FAKE_NV_DATA                *CurrentFakeNVMap\r
+  )\r
+{\r
+  UINT16  Index;\r
+\r
+  switch (Private->BmmPreviousPageId) {\r
+  case FORM_BOOT_CHG_ID:\r
+    CopyMem (CurrentFakeNVMap->BootOptionOrder, Private->BmmOldFakeNVData.BootOptionOrder, sizeof (CurrentFakeNVMap->BootOptionOrder));\r
+    break;\r
+\r
+  case FORM_DRV_CHG_ID:\r
+    CopyMem (CurrentFakeNVMap->DriverOptionOrder, Private->BmmOldFakeNVData.DriverOptionOrder, sizeof (CurrentFakeNVMap->DriverOptionOrder));\r
+    break;\r
+\r
+  case FORM_BOOT_DEL_ID:\r
+    ASSERT (BootOptionMenu.MenuNumber <= (sizeof (CurrentFakeNVMap->BootOptionDel) / sizeof (CurrentFakeNVMap->BootOptionDel[0])));\r
+    for (Index = 0; Index < BootOptionMenu.MenuNumber; Index++) {\r
+      CurrentFakeNVMap->BootOptionDel[Index] = FALSE;\r
+    }\r
+    break;\r
+\r
+  case FORM_DRV_DEL_ID:\r
+    ASSERT (DriverOptionMenu.MenuNumber <= (sizeof (CurrentFakeNVMap->DriverOptionDel) / sizeof (CurrentFakeNVMap->DriverOptionDel[0])));\r
+    for (Index = 0; Index < DriverOptionMenu.MenuNumber; Index++) {\r
+      CurrentFakeNVMap->DriverOptionDel[Index] = FALSE;\r
+    }\r
+    break;\r
+\r
+  case FORM_BOOT_NEXT_ID:\r
+    CurrentFakeNVMap->BootNext = Private->BmmOldFakeNVData.BootNext;\r
+    break;\r
+\r
+  case FORM_TIME_OUT_ID:\r
+    CurrentFakeNVMap->BootTimeOut = Private->BmmOldFakeNVData.BootTimeOut;\r
+    break;\r
+\r
+  case FORM_DRV_ADD_HANDLE_DESC_ID:\r
+  case FORM_DRV_ADD_FILE_ID:\r
+  case FORM_DRV_ADD_HANDLE_ID:\r
+    CurrentFakeNVMap->DriverAddHandleDesc[0]          = 0x0000;\r
+    CurrentFakeNVMap->DriverAddHandleOptionalData[0]  = 0x0000;\r
+    break;\r
+\r
+  default:\r
+    break;\r
+  }\r
+}\r
+\r
+/**\r
+  Create dynamic code for BMM.\r
+\r
+  @param  BmmCallbackInfo        The BMM context data.\r
+\r
+**/\r
+VOID\r
+InitializeDrivers(\r
+  IN BMM_CALLBACK_DATA        *BmmCallbackInfo\r
+  )\r
+{\r
+  EFI_HII_HANDLE              HiiHandle;\r
+  VOID                        *StartOpCodeHandle;\r
+  VOID                        *EndOpCodeHandle;\r
+  EFI_IFR_GUID_LABEL          *StartLabel;\r
+  EFI_IFR_GUID_LABEL          *EndLabel;\r
+  UINTN                       Index;    \r
+  EFI_STRING                  String;\r
+  EFI_STRING_ID               Token;\r
+  EFI_STRING_ID               TokenHelp;  \r
+  EFI_HII_HANDLE              *HiiHandles;\r
+  EFI_GUID                    FormSetGuid;\r
+  CHAR16                      *DevicePathStr;\r
+  EFI_STRING_ID               DevicePathId;\r
+  EFI_IFR_FORM_SET            *Buffer;      \r
+  UINTN                       BufferSize;   \r
+  UINT8                       ClassGuidNum; \r
+  EFI_GUID                    *ClassGuid;   \r
+  UINTN                       TempSize;\r
+  UINT8                       *Ptr;\r
+  EFI_STATUS                  Status;\r
+\r
+  TempSize =0;\r
+  BufferSize = 0;\r
+  Buffer = NULL;\r
+\r
+  HiiHandle = BmmCallbackInfo->BmmHiiHandle;\r
+  //\r
+  // Allocate space for creation of UpdateData Buffer\r
+  //\r
+  StartOpCodeHandle = HiiAllocateOpCodeHandle ();\r
+  ASSERT (StartOpCodeHandle != NULL);\r
+\r
+  EndOpCodeHandle = HiiAllocateOpCodeHandle ();\r
+  ASSERT (EndOpCodeHandle != NULL);\r
+\r
+  //\r
+  // Create Hii Extend Label OpCode as the start opcode\r
+  //\r
+  StartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (StartOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));\r
+  StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;\r
+  StartLabel->Number       = LABEL_BMM_PLATFORM_INFORMATION;\r
+\r
+  //\r
+  // Create Hii Extend Label OpCode as the end opcode\r
+  //\r
+  EndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (EndOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));\r
+  EndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;\r
+  EndLabel->Number       = LABEL_END;\r
+\r
+  //\r
+  // Get all the Hii handles\r
+  //\r
+  HiiHandles = HiiGetHiiHandles (NULL);\r
+  ASSERT (HiiHandles != NULL);\r
+\r
+  //\r
+  // Search for formset of each class type\r
+  //\r
+  for (Index = 0; HiiHandles[Index] != NULL; Index++) {\r
+    Status = HiiGetFormSetFromHiiHandle(HiiHandles[Index], &Buffer,&BufferSize);\r
+    if (EFI_ERROR (Status)) {\r
+      continue;\r
+    }\r
+\r
+    Ptr = (UINT8 *)Buffer;\r
+    while(TempSize < BufferSize)  {\r
+      TempSize += ((EFI_IFR_OP_HEADER *) Ptr)->Length;\r
+\r
+      if (((EFI_IFR_OP_HEADER *) Ptr)->Length <= OFFSET_OF (EFI_IFR_FORM_SET, Flags)){\r
+        Ptr += ((EFI_IFR_OP_HEADER *) Ptr)->Length;\r
+        continue;\r
+      }\r
+\r
+      //\r
+      // Find FormSet OpCode\r
+      //\r
+      ClassGuidNum = (UINT8) (((EFI_IFR_FORM_SET *)Ptr)->Flags & 0x3);\r
+      ClassGuid = (EFI_GUID *) (VOID *)(Ptr + sizeof (EFI_IFR_FORM_SET));\r
+      while (ClassGuidNum-- > 0) {\r
+        if (CompareGuid (&gEfiIfrBootMaintenanceGuid, ClassGuid) == 0){\r
+          ClassGuid ++;\r
+          continue;\r
+        }\r
+\r
+        String = HiiGetString (HiiHandles[Index], ((EFI_IFR_FORM_SET *)Ptr)->FormSetTitle, NULL);\r
+        if (String == NULL) {\r
+          String = HiiGetString (HiiHandle, STRING_TOKEN (STR_MISSING_STRING), NULL);\r
+          ASSERT (String != NULL);\r
+        }\r
+        Token = HiiSetString (HiiHandle, 0, String, NULL);\r
+        FreePool (String);\r
+\r
+        String = HiiGetString (HiiHandles[Index], ((EFI_IFR_FORM_SET *)Ptr)->Help, NULL);\r
+        if (String == NULL) {\r
+          String = HiiGetString (HiiHandle, STRING_TOKEN (STR_MISSING_STRING), NULL);\r
+          ASSERT (String != NULL);\r
+        }\r
+        TokenHelp = HiiSetString (HiiHandle, 0, String, NULL);\r
+        FreePool (String);\r
+\r
+        FormSetGuid = ((EFI_IFR_FORM_SET *)Ptr)->Guid;\r
+\r
+        DevicePathStr = BmmExtractDevicePathFromHiiHandle(HiiHandles[Index]);\r
+        DevicePathId  = 0;\r
+        if (DevicePathStr != NULL){\r
+          DevicePathId = HiiSetString (HiiHandle, 0, DevicePathStr, NULL);\r
+          FreePool (DevicePathStr);\r
+        }\r
+         HiiCreateGotoExOpCode (\r
+           StartOpCodeHandle,\r
+           0,\r
+           Token,\r
+           TokenHelp,\r
+           0,\r
+           (EFI_QUESTION_ID) (Index + FRONT_PAGE_KEY_OFFSET),\r
+           0,\r
+           &FormSetGuid,\r
+           DevicePathId\r
+         );\r
+        break;\r
+      }\r
+      Ptr += ((EFI_IFR_OP_HEADER *) Ptr)->Length;\r
+    }\r
+\r
+    FreePool(Buffer);\r
+    Buffer = NULL;\r
+    TempSize = 0;\r
+    BufferSize = 0;\r
+  } \r
+  \r
+  HiiUpdateForm (\r
+    HiiHandle,\r
+    &mBootMaintGuid,\r
+    FORM_MAIN_ID,\r
+    StartOpCodeHandle,\r
+    EndOpCodeHandle\r
+    );\r
+\r
+  HiiFreeOpCodeHandle (StartOpCodeHandle);\r
+  HiiFreeOpCodeHandle (EndOpCodeHandle);  \r
+  FreePool (HiiHandles);\r
+}\r
+\r
+/**\r
+   Create dynamic code for BMM and initialize all of BMM configuration data in BmmFakeNvData and\r
+   BmmOldFakeNVData member in BMM context data.\r
+\r
+  @param CallbackData    The BMM context data.\r
+\r
+**/\r
+VOID\r
+InitializeBmmConfig (\r
+  IN  BMM_CALLBACK_DATA    *CallbackData\r
+  )\r
+{\r
+  BM_MENU_ENTRY   *NewMenuEntry;\r
+  BM_LOAD_CONTEXT *NewLoadContext;\r
+  UINT16          Index;\r
+\r
+  ASSERT (CallbackData != NULL);\r
+\r
+  InitializeDrivers (CallbackData);\r
+\r
+  //\r
+  // Initialize data which located in BMM main page\r
+  //\r
+  CallbackData->BmmFakeNvData.BootNext = (UINT16) (BootOptionMenu.MenuNumber);\r
+  for (Index = 0; Index < BootOptionMenu.MenuNumber; Index++) {\r
+    NewMenuEntry    = BOpt_GetMenuEntry (&BootOptionMenu, Index);\r
+    NewLoadContext  = (BM_LOAD_CONTEXT *) NewMenuEntry->VariableContext;\r
+\r
+    if (NewLoadContext->IsBootNext) {\r
+      CallbackData->BmmFakeNvData.BootNext = Index;\r
+      break;\r
+    }\r
+  }\r
+\r
+  CallbackData->BmmFakeNvData.BootTimeOut = PcdGet16 (PcdPlatformBootTimeOut);\r
+\r
+  //\r
+  // Initialize data which located in Boot Options Menu\r
+  //\r
+  GetBootOrder (CallbackData);\r
+\r
+  //\r
+  // Initialize data which located in Driver Options Menu\r
+  //\r
+  GetDriverOrder (CallbackData);\r
+\r
+  //\r
+  // Initialize data which located in Console Options Menu\r
+  //\r
+  GetConsoleOutMode (CallbackData);\r
+  GetConsoleInCheck (CallbackData);\r
+  GetConsoleOutCheck (CallbackData);\r
+  GetConsoleErrCheck (CallbackData);\r
+  GetTerminalAttribute (CallbackData);\r
+\r
+  CallbackData->BmmFakeNvData.ForceReconnect = TRUE;\r
+\r
+  //\r
+  // Backup Initialize BMM configuartion data to BmmOldFakeNVData\r
+  //\r
+  CopyMem (&CallbackData->BmmOldFakeNVData, &CallbackData->BmmFakeNvData, sizeof (BMM_FAKE_NV_DATA));\r
+}\r
+\r
+/**\r
+  Initialized all Menu Option List.\r
+\r
+  @param CallbackData    The BMM context data.\r
+\r
+**/\r
+VOID\r
+InitAllMenu (\r
+  IN  BMM_CALLBACK_DATA    *CallbackData\r
+  )\r
+{\r
+  InitializeListHead (&BootOptionMenu.Head);\r
+  InitializeListHead (&DriverOptionMenu.Head);\r
+  BOpt_GetBootOptions (CallbackData);\r
+  BOpt_GetDriverOptions (CallbackData);\r
+  BOpt_FindDrivers ();\r
+  InitializeListHead (&ConsoleInpMenu.Head);\r
+  InitializeListHead (&ConsoleOutMenu.Head);\r
+  InitializeListHead (&ConsoleErrMenu.Head);\r
+  InitializeListHead (&TerminalMenu.Head);\r
+  LocateSerialIo ();\r
+  GetAllConsoles ();\r
+  mAllMenuInit = TRUE;\r
+}\r
+\r
+/**\r
+  Free up all Menu Option list.\r
+\r
+**/\r
+VOID\r
+FreeAllMenu (\r
+  VOID\r
+  )\r
+{\r
+  if (!mAllMenuInit){\r
+    return;\r
+  }\r
+  BOpt_FreeMenu (&BootOptionMenu);\r
+  BOpt_FreeMenu (&DriverOptionMenu);\r
+  BOpt_FreeMenu (&DriverMenu);\r
+  FreeAllConsoles ();\r
+  mAllMenuInit = FALSE;\r
+}\r
+\r
+/**\r
+\r
+  Install Boot Maintenance Manager Menu driver.\r
+\r
+  @param ImageHandle     The image handle.\r
+  @param SystemTable     The system table.\r
+\r
+  @retval  EFI_SUCEESS  Install Boot manager menu success.\r
+  @retval  Other        Return error status.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+BootMaintenanceManagerLibConstructor (\r
+  IN EFI_HANDLE                            ImageHandle,\r
+  IN EFI_SYSTEM_TABLE                      *SystemTable\r
+  )\r
+\r
+{\r
+  EFI_STATUS               Status;\r
+  UINT8                    *Ptr;\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+  //\r
+  // Install Device Path Protocol and Config Access protocol to driver handle\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &mBmmCallbackInfo->BmmDriverHandle,\r
+                  &gEfiDevicePathProtocolGuid,\r
+                  &mBmmHiiVendorDevicePath,\r
+                  &gEfiHiiConfigAccessProtocolGuid,\r
+                  &mBmmCallbackInfo->BmmConfigAccess,\r
+                  NULL\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  //\r
+  // Post our Boot Maint VFR binary to the HII database.\r
+  //\r
+  mBmmCallbackInfo->BmmHiiHandle = HiiAddPackages (\r
+                                    &mBootMaintGuid,\r
+                                    mBmmCallbackInfo->BmmDriverHandle,\r
+                                    BootMaintenanceManagerBin,\r
+                                    BootMaintenanceManagerLibStrings,\r
+                                    NULL\r
+                                    );\r
+  ASSERT (mBmmCallbackInfo->BmmHiiHandle != NULL);\r
+\r
+  //\r
+  // Locate Formbrowser2 protocol\r
+  //\r
+  Status = gBS->LocateProtocol (&gEfiFormBrowser2ProtocolGuid, NULL, (VOID **) &mBmmCallbackInfo->FormBrowser2);\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  EfiBootManagerRefreshAllBootOption ();\r
+\r
+  //\r
+  // Create LoadOption in BmmCallbackInfo for Driver Callback\r
+  //\r
+  Ptr = AllocateZeroPool (sizeof (BM_LOAD_CONTEXT) + sizeof (BM_FILE_CONTEXT) + sizeof (BM_HANDLE_CONTEXT) + sizeof (BM_MENU_ENTRY));\r
+  ASSERT (Ptr != NULL);\r
+\r
+  //\r
+  // Initialize Bmm callback data.\r
+  //\r
+  mBmmCallbackInfo->LoadContext = (BM_LOAD_CONTEXT *) Ptr;\r
+  Ptr += sizeof (BM_LOAD_CONTEXT);\r
+\r
+  mBmmCallbackInfo->FileContext = (BM_FILE_CONTEXT *) Ptr;\r
+  Ptr += sizeof (BM_FILE_CONTEXT);\r
+\r
+  mBmmCallbackInfo->HandleContext = (BM_HANDLE_CONTEXT *) Ptr;\r
+  Ptr += sizeof (BM_HANDLE_CONTEXT);\r
+\r
+  mBmmCallbackInfo->MenuEntry     = (BM_MENU_ENTRY *) Ptr;\r
+\r
+  mBmmCallbackInfo->BmmPreviousPageId  = FORM_MAIN_ID;\r
+  mBmmCallbackInfo->BmmCurrentPageId   = FORM_MAIN_ID;\r
+\r
+  InitAllMenu (mBmmCallbackInfo);\r
+\r
+  CreateUpdateData();\r
+  //\r
+  // Update boot maintenance manager page \r
+  //\r
+  InitializeBmmConfig(mBmmCallbackInfo);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Unloads the application and its installed protocol.\r
+\r
+  @param ImageHandle       Handle that identifies the image to be unloaded.\r
+  @param  SystemTable      The system table.\r
+\r
+  @retval EFI_SUCCESS      The image has been unloaded.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+BootMaintenanceManagerLibDestructor (\r
+  IN EFI_HANDLE                            ImageHandle,\r
+  IN EFI_SYSTEM_TABLE                      *SystemTable\r
+  )\r
+\r
+{\r
+  if (mStartOpCodeHandle != NULL) {\r
+    HiiFreeOpCodeHandle (mStartOpCodeHandle);\r
+  }\r
+\r
+  if (mEndOpCodeHandle != NULL) {\r
+    HiiFreeOpCodeHandle (mEndOpCodeHandle);\r
+  }\r
+\r
+  FreeAllMenu ();\r
+\r
+  //\r
+  // Remove our IFR data from HII database\r
+  //\r
+  HiiRemovePackages (mBmmCallbackInfo->BmmHiiHandle);\r
+\r
+  gBS->UninstallMultipleProtocolInterfaces (\r
+         mBmmCallbackInfo->BmmDriverHandle,\r
+         &gEfiDevicePathProtocolGuid,\r
+         &mBmmHiiVendorDevicePath,\r
+         &gEfiHiiConfigAccessProtocolGuid,\r
+         &mBmmCallbackInfo->BmmConfigAccess,\r
+         NULL\r
+         );\r
+\r
+  FreePool (mBmmCallbackInfo->LoadContext);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r