]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Universal/BdsDxe/BdsEntry.c
MdeModulePkg/BdsDxe: Also call PlatformBootManagerWaitCallback on 0
[mirror_edk2.git] / MdeModulePkg / Universal / BdsDxe / BdsEntry.c
index adbce15f2dac53ad46c79986ed90790122f3b7ed..7968a58f3454dd6a19c9dac049ea4cbc7e7a08e8 100644 (file)
 /** @file\r
-  The entry of the bds\r
+  This module produce main entry for BDS phase - BdsEntry.\r
+  When this module was dispatched by DxeCore, gEfiBdsArchProtocolGuid will be installed\r
+  which contains interface of BdsEntry.\r
+  After DxeCore finish DXE phase, gEfiBdsArchProtocolGuid->BdsEntry will be invoked\r
+  to enter BDS phase.\r
 \r
-Copyright (c) 2004 - 2008, Intel Corporation. <BR>\r
-All rights reserved. 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
+Copyright (c) 2004 - 2019, Intel Corporation. All rights reserved.<BR>\r
+(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>\r
+(C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
 \r
 #include "Bds.h"\r
 #include "Language.h"\r
-#include "FrontPage.h"\r
-#include "Hotkey.h"\r
 #include "HwErrRecSupport.h"\r
 \r
+#define SET_BOOT_OPTION_SUPPORT_KEY_COUNT(a, c) {  \\r
+      (a) = ((a) & ~EFI_BOOT_OPTION_SUPPORT_COUNT) | (((c) << LowBitSet32 (EFI_BOOT_OPTION_SUPPORT_COUNT)) & EFI_BOOT_OPTION_SUPPORT_COUNT); \\r
+      }\r
 \r
-EFI_BDS_ARCH_PROTOCOL_INSTANCE  gBdsInstanceTemplate = {\r
-  EFI_BDS_ARCH_PROTOCOL_INSTANCE_SIGNATURE,\r
-  NULL,\r
-  {BdsEntry},\r
-  0xFFFF,\r
-  TRUE,\r
-  EXTENSIVE\r
+///\r
+/// BDS arch protocol instance initial value.\r
+///\r
+EFI_BDS_ARCH_PROTOCOL  gBds = {\r
+  BdsEntry\r
 };\r
 \r
-UINT16                          *mBootNext = NULL;\r
+//\r
+// gConnectConInEvent - Event which is signaled when ConIn connection is required\r
+//\r
+EFI_EVENT      gConnectConInEvent = NULL;\r
+\r
+///\r
+/// The read-only variables defined in UEFI Spec.\r
+///\r
+CHAR16  *mReadOnlyVariables[] = {\r
+  EFI_PLATFORM_LANG_CODES_VARIABLE_NAME,\r
+  EFI_LANG_CODES_VARIABLE_NAME,\r
+  EFI_BOOT_OPTION_SUPPORT_VARIABLE_NAME,\r
+  EFI_HW_ERR_REC_SUPPORT_VARIABLE_NAME,\r
+  EFI_OS_INDICATIONS_SUPPORT_VARIABLE_NAME\r
+  };\r
 \r
-EFI_HANDLE                      mBdsImageHandle;\r
+CHAR16 *mBdsLoadOptionName[] = {\r
+  L"Driver",\r
+  L"SysPrep",\r
+  L"Boot",\r
+  L"PlatformRecovery"\r
+};\r
 \r
 /**\r
+  Event to Connect ConIn.\r
 \r
-  Install Boot Device Selection Protocol\r
+  @param  Event                 Event whose notification function is being invoked.\r
+  @param  Context               Pointer to the notification function's context,\r
+                                which is implementation-dependent.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+BdsDxeOnConnectConInCallBack (\r
+  IN EFI_EVENT                Event,\r
+  IN VOID                     *Context\r
+  )\r
+{\r
+  EFI_STATUS Status;\r
+\r
+  //\r
+  // When Osloader call ReadKeyStroke to signal this event\r
+  // no driver dependency is assumed existing. So use a non-dispatch version\r
+  //\r
+  Status = EfiBootManagerConnectConsoleVariable (ConIn);\r
+  if (EFI_ERROR (Status)) {\r
+    //\r
+    // Should not enter this case, if enter, the keyboard will not work.\r
+    // May need platfrom policy to connect keyboard.\r
+    //\r
+    DEBUG ((EFI_D_WARN, "[Bds] Connect ConIn failed - %r!!!\n", Status));\r
+  }\r
+}\r
+/**\r
+  Notify function for event group EFI_EVENT_GROUP_READY_TO_BOOT. This is used to\r
+  check whether there is remaining deferred load images.\r
 \r
+  @param[in]  Event   The Event that is being processed.\r
+  @param[in]  Context The Event Context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+CheckDeferredLoadImageOnReadyToBoot (\r
+  IN EFI_EVENT        Event,\r
+  IN VOID             *Context\r
+  )\r
+{\r
+  EFI_STATUS                         Status;\r
+  EFI_DEFERRED_IMAGE_LOAD_PROTOCOL   *DeferredImage;\r
+  UINTN                              HandleCount;\r
+  EFI_HANDLE                         *Handles;\r
+  UINTN                              Index;\r
+  UINTN                              ImageIndex;\r
+  EFI_DEVICE_PATH_PROTOCOL           *ImageDevicePath;\r
+  VOID                               *Image;\r
+  UINTN                              ImageSize;\r
+  BOOLEAN                            BootOption;\r
+  CHAR16                             *DevicePathStr;\r
+\r
+  //\r
+  // Find all the deferred image load protocols.\r
+  //\r
+  HandleCount = 0;\r
+  Handles = NULL;\r
+  Status = gBS->LocateHandleBuffer (\r
+    ByProtocol,\r
+    &gEfiDeferredImageLoadProtocolGuid,\r
+    NULL,\r
+    &HandleCount,\r
+    &Handles\r
+  );\r
+  if (EFI_ERROR (Status)) {\r
+    return;\r
+  }\r
+\r
+  for (Index = 0; Index < HandleCount; Index++) {\r
+    Status = gBS->HandleProtocol (Handles[Index], &gEfiDeferredImageLoadProtocolGuid, (VOID **) &DeferredImage);\r
+    if (EFI_ERROR (Status)) {\r
+      continue;\r
+    }\r
+\r
+    for (ImageIndex = 0; ; ImageIndex++) {\r
+      //\r
+      // Load all the deferred images in this protocol instance.\r
+      //\r
+      Status = DeferredImage->GetImageInfo (\r
+        DeferredImage,\r
+        ImageIndex,\r
+        &ImageDevicePath,\r
+        (VOID **) &Image,\r
+        &ImageSize,\r
+        &BootOption\r
+      );\r
+      if (EFI_ERROR (Status)) {\r
+        break;\r
+      }\r
+      DevicePathStr = ConvertDevicePathToText (ImageDevicePath, FALSE, FALSE);\r
+      DEBUG ((DEBUG_LOAD, "[Bds] Image was deferred but not loaded: %s.\n", DevicePathStr));\r
+      if (DevicePathStr != NULL) {\r
+        FreePool (DevicePathStr);\r
+      }\r
+    }\r
+  }\r
+  if (Handles != NULL) {\r
+    FreePool (Handles);\r
+  }\r
+}\r
+\r
+/**\r
+\r
+  Install Boot Device Selection Protocol\r
 \r
   @param ImageHandle     The image handle.\r
   @param SystemTable     The system table.\r
 \r
   @retval  EFI_SUCEESS  BDS has finished initializing.\r
-                        Rerun the\r
-                        dispatcher and recall BDS.Entry\r
-  @retval  Other        Return value from AllocatePool()\r
-                        or gBS->InstallProtocolInterface\r
+                        Return the dispatcher and recall BDS.Entry\r
+  @retval  Other        Return status from AllocatePool() or gBS->InstallProtocolInterface\r
 \r
 **/\r
 EFI_STATUS\r
@@ -55,213 +176,473 @@ BdsInitialize (
   )\r
 {\r
   EFI_STATUS  Status;\r
-\r
-  mBdsImageHandle = ImageHandle;\r
-\r
+  EFI_HANDLE  Handle;\r
   //\r
   // Install protocol interface\r
   //\r
-  Status = gBS->InstallProtocolInterface (\r
-                  &gBdsInstanceTemplate.Handle,\r
-                  &gEfiBdsArchProtocolGuid,\r
-                  EFI_NATIVE_INTERFACE,\r
-                  &gBdsInstanceTemplate.Bds\r
+  Handle = NULL;\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &Handle,\r
+                  &gEfiBdsArchProtocolGuid, &gBds,\r
+                  NULL\r
                   );\r
   ASSERT_EFI_ERROR (Status);\r
 \r
+  DEBUG_CODE (\r
+    EFI_EVENT   Event;\r
+    //\r
+    // Register notify function to check deferred images on ReadyToBoot Event.\r
+    //\r
+    Status = gBS->CreateEventEx (\r
+                    EVT_NOTIFY_SIGNAL,\r
+                    TPL_CALLBACK,\r
+                    CheckDeferredLoadImageOnReadyToBoot,\r
+                    NULL,\r
+                    &gEfiEventReadyToBootGuid,\r
+                    &Event\r
+                    );\r
+    ASSERT_EFI_ERROR (Status);\r
+  );\r
   return Status;\r
 }\r
 \r
 /**\r
+  Function waits for a given event to fire, or for an optional timeout to expire.\r
 \r
-  In the loop of attempt to boot for the boot order\r
+  @param   Event              The event to wait for\r
+  @param   Timeout            An optional timeout value in 100 ns units.\r
+\r
+  @retval  EFI_SUCCESS      Event fired before Timeout expired.\r
+  @retval  EFI_TIME_OUT     Timout expired before Event fired..\r
+\r
+**/\r
+EFI_STATUS\r
+BdsWaitForSingleEvent (\r
+  IN  EFI_EVENT                  Event,\r
+  IN  UINT64                     Timeout       OPTIONAL\r
+  )\r
+{\r
+  UINTN       Index;\r
+  EFI_STATUS  Status;\r
+  EFI_EVENT   TimerEvent;\r
+  EFI_EVENT   WaitList[2];\r
 \r
+  if (Timeout != 0) {\r
+    //\r
+    // Create a timer event\r
+    //\r
+    Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent);\r
+    if (!EFI_ERROR (Status)) {\r
+      //\r
+      // Set the timer event\r
+      //\r
+      gBS->SetTimer (\r
+             TimerEvent,\r
+             TimerRelative,\r
+             Timeout\r
+             );\r
+\r
+      //\r
+      // Wait for the original event or the timer\r
+      //\r
+      WaitList[0] = Event;\r
+      WaitList[1] = TimerEvent;\r
+      Status      = gBS->WaitForEvent (2, WaitList, &Index);\r
+      ASSERT_EFI_ERROR (Status);\r
+      gBS->CloseEvent (TimerEvent);\r
+\r
+      //\r
+      // If the timer expired, change the return to timed out\r
+      //\r
+      if (Index == 1) {\r
+        Status = EFI_TIMEOUT;\r
+      }\r
+    }\r
+  } else {\r
+    //\r
+    // No timeout... just wait on the event\r
+    //\r
+    Status = gBS->WaitForEvent (1, &Event, &Index);\r
+    ASSERT (!EFI_ERROR (Status));\r
+    ASSERT (Index == 0);\r
+  }\r
 \r
-  @param VOID           No input parameter.\r
+  return Status;\r
+}\r
 \r
-  @retval VOID          No returns.\r
+/**\r
+  The function reads user inputs.\r
 \r
 **/\r
 VOID\r
-BdsBootDeviceSelect (\r
+BdsReadKeys (\r
   VOID\r
   )\r
 {\r
-  EFI_STATUS        Status;\r
-  LIST_ENTRY        *Link;\r
-  BDS_COMMON_OPTION *BootOption;\r
-  UINTN             ExitDataSize;\r
-  CHAR16            *ExitData;\r
-  UINT16            Timeout;\r
-  LIST_ENTRY        BootLists;\r
-  CHAR16            Buffer[20];\r
-  BOOLEAN           BootNextExist;\r
-  LIST_ENTRY        *LinkBootNext;\r
+  EFI_STATUS         Status;\r
+  EFI_INPUT_KEY      Key;\r
 \r
-  //\r
-  // Got the latest boot option\r
-  //\r
-  BootNextExist = FALSE;\r
-  LinkBootNext  = NULL;\r
-  InitializeListHead (&BootLists);\r
+  if (PcdGetBool (PcdConInConnectOnDemand)) {\r
+    return;\r
+  }\r
 \r
-  //\r
-  // First check the boot next option\r
-  //\r
-  ZeroMem (Buffer, sizeof (Buffer));\r
+  while (gST->ConIn != NULL) {\r
 \r
-  if (mBootNext != NULL) {\r
-    //\r
-    // Indicate we have the boot next variable, so this time\r
-    // boot will always have this boot option\r
-    //\r
-    BootNextExist = TRUE;\r
+    Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);\r
 \r
-    //\r
-    // Clear the this variable so it's only exist in this time boot\r
-    //\r
-    gRT->SetVariable (\r
-          L"BootNext",\r
-          &gEfiGlobalVariableGuid,\r
-          EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
-          0,\r
-          mBootNext\r
-          );\r
+    if (EFI_ERROR (Status)) {\r
+      //\r
+      // No more keys.\r
+      //\r
+      break;\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+  The function waits for the boot manager timeout expires or hotkey is pressed.\r
+\r
+  It calls PlatformBootManagerWaitCallback each second.\r
+\r
+  @param     HotkeyTriggered   Input hotkey event.\r
+**/\r
+VOID\r
+BdsWait (\r
+  IN EFI_EVENT      HotkeyTriggered\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  UINT16                TimeoutRemain;\r
+\r
+  DEBUG ((EFI_D_INFO, "[Bds]BdsWait ...Zzzzzzzzzzzz...\n"));\r
+\r
+  TimeoutRemain = PcdGet16 (PcdPlatformBootTimeOut);\r
+  while (TimeoutRemain != 0) {\r
+    DEBUG ((EFI_D_INFO, "[Bds]BdsWait(%d)..Zzzz...\n", (UINTN) TimeoutRemain));\r
+    PlatformBootManagerWaitCallback (TimeoutRemain);\r
+\r
+    BdsReadKeys (); // BUGBUG: Only reading can signal HotkeyTriggered\r
+                    //         Can be removed after all keyboard drivers invoke callback in timer callback.\r
+\r
+    if (HotkeyTriggered != NULL) {\r
+      Status = BdsWaitForSingleEvent (HotkeyTriggered, EFI_TIMER_PERIOD_SECONDS (1));\r
+      if (!EFI_ERROR (Status)) {\r
+        break;\r
+      }\r
+    } else {\r
+      gBS->Stall (1000000);\r
+    }\r
 \r
     //\r
-    // Add the boot next boot option\r
+    // 0xffff means waiting forever\r
+    // BDS with no hotkey provided and 0xffff as timeout will "hang" in the loop\r
     //\r
-    UnicodeSPrint (Buffer, sizeof (Buffer), L"Boot%04x", *mBootNext);\r
-    BootOption = BdsLibVariableToOption (&BootLists, Buffer);\r
-    BootOption->BootCurrent = *mBootNext;\r
+    if (TimeoutRemain != 0xffff) {\r
+      TimeoutRemain--;\r
+    }\r
   }\r
-  //\r
-  // Parse the boot order to get boot option\r
-  //\r
-  BdsLibBuildOptionFromVar (&BootLists, L"BootOrder");\r
-  Link = BootLists.ForwardLink;\r
+  PlatformBootManagerWaitCallback (0);\r
+  DEBUG ((EFI_D_INFO, "[Bds]Exit the waiting!\n"));\r
+}\r
+\r
+/**\r
+  Attempt to boot each boot option in the BootOptions array.\r
+\r
+  @param BootOptions       Input boot option array.\r
+  @param BootOptionCount   Input boot option count.\r
+  @param BootManagerMenu   Input boot manager menu.\r
+\r
+  @retval TRUE  Successfully boot one of the boot options.\r
+  @retval FALSE Failed boot any of the boot options.\r
+**/\r
+BOOLEAN\r
+BootBootOptions (\r
+  IN EFI_BOOT_MANAGER_LOAD_OPTION    *BootOptions,\r
+  IN UINTN                           BootOptionCount,\r
+  IN EFI_BOOT_MANAGER_LOAD_OPTION    *BootManagerMenu OPTIONAL\r
+  )\r
+{\r
+  UINTN                              Index;\r
 \r
   //\r
-  // Parameter check, make sure the loop will be valid\r
+  // Report Status Code to indicate BDS starts attempting booting from the UEFI BootOrder list.\r
   //\r
-  if (Link == NULL) {\r
-    return ;\r
-  }\r
+  REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_ATTEMPT_BOOT_ORDER_EVENT));\r
+\r
   //\r
-  // Here we make the boot in a loop, every boot success will\r
-  // return to the front page\r
+  // Attempt boot each boot option\r
   //\r
-  for (;;) {\r
-    //\r
-    // Check the boot option list first\r
-    //\r
-    if (Link == &BootLists) {\r
-      //\r
-      // There are two ways to enter here:\r
-      // 1. There is no active boot option, give user chance to\r
-      //    add new boot option\r
-      // 2. All the active boot option processed, and there is no\r
-      //    one is success to boot, then we back here to allow user\r
-      //    add new active boot option\r
-      //\r
-      Timeout = 0xffff;\r
-      PlatformBdsEnterFrontPage (Timeout, FALSE);\r
-      InitializeListHead (&BootLists);\r
-      BdsLibBuildOptionFromVar (&BootLists, L"BootOrder");\r
-      Link = BootLists.ForwardLink;\r
-      continue;\r
-    }\r
-    //\r
-    // Get the boot option from the link list\r
-    //\r
-    BootOption = CR (Link, BDS_COMMON_OPTION, Link, BDS_LOAD_OPTION_SIGNATURE);\r
-\r
+  for (Index = 0; Index < BootOptionCount; Index++) {\r
     //\r
     // According to EFI Specification, if a load option is not marked\r
     // as LOAD_OPTION_ACTIVE, the boot manager will not automatically\r
     // load the option.\r
     //\r
-    if (!IS_LOAD_OPTION_TYPE (BootOption->Attribute, LOAD_OPTION_ACTIVE)) {\r
-      //\r
-      // skip the header of the link list, becuase it has no boot option\r
-      //\r
-      Link = Link->ForwardLink;\r
+    if ((BootOptions[Index].Attributes & LOAD_OPTION_ACTIVE) == 0) {\r
       continue;\r
     }\r
+\r
     //\r
-    // Make sure the boot option device path connected,\r
-    // but ignore the BBS device path\r
+    // Boot#### load options with LOAD_OPTION_CATEGORY_APP are executables which are not\r
+    // part of the normal boot processing. Boot options with reserved category values will be\r
+    // ignored by the boot manager.\r
     //\r
-    if (DevicePathType (BootOption->DevicePath) != BBS_DEVICE_PATH) {\r
-      //\r
-      // Notes: the internal shell can not been connected with device path\r
-      // so we do not check the status here\r
-      //\r
-      BdsLibConnectDevicePath (BootOption->DevicePath);\r
+    if ((BootOptions[Index].Attributes & LOAD_OPTION_CATEGORY) != LOAD_OPTION_CATEGORY_BOOT) {\r
+      continue;\r
     }\r
+\r
     //\r
     // All the driver options should have been processed since\r
     // now boot will be performed.\r
     //\r
-    Status = BdsLibBootViaBootOption (BootOption, BootOption->DevicePath, &ExitDataSize, &ExitData);\r
-    if (EFI_ERROR (Status)) {\r
-      //\r
-      // Call platform action to indicate the boot fail\r
-      //\r
-      BootOption->StatusString = GetStringById (STRING_TOKEN (STR_BOOT_FAILED));\r
-      PlatformBdsBootFail (BootOption, Status, ExitData, ExitDataSize);\r
+    EfiBootManagerBoot (&BootOptions[Index]);\r
 \r
-      //\r
-      // Check the next boot option\r
-      //\r
-      Link = Link->ForwardLink;\r
+    //\r
+    // If the boot via Boot#### returns with a status of EFI_SUCCESS, platform firmware\r
+    // supports boot manager menu, and if firmware is configured to boot in an\r
+    // interactive mode, the boot manager will stop processing the BootOrder variable and\r
+    // present a boot manager menu to the user.\r
+    //\r
+    if ((BootManagerMenu != NULL) && (BootOptions[Index].Status == EFI_SUCCESS)) {\r
+      EfiBootManagerBoot (BootManagerMenu);\r
+      break;\r
+    }\r
+  }\r
 \r
-    } else {\r
-      //\r
-      // Call platform action to indicate the boot success\r
-      //\r
-      BootOption->StatusString = GetStringById (STRING_TOKEN (STR_BOOT_SUCCEEDED)); \r
-      PlatformBdsBootSuccess (BootOption);\r
+  return (BOOLEAN) (Index < BootOptionCount);\r
+}\r
+\r
+/**\r
+  The function will load and start every Driver####, SysPrep#### or PlatformRecovery####.\r
+\r
+  @param  LoadOptions        Load option array.\r
+  @param  LoadOptionCount    Load option count.\r
+**/\r
+VOID\r
+ProcessLoadOptions (\r
+  IN EFI_BOOT_MANAGER_LOAD_OPTION       *LoadOptions,\r
+  IN UINTN                              LoadOptionCount\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  UINTN                             Index;\r
+  BOOLEAN                           ReconnectAll;\r
+  EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LoadOptionType;\r
+\r
+  ReconnectAll   = FALSE;\r
+  LoadOptionType = LoadOptionTypeMax;\r
+\r
+  //\r
+  // Process the driver option\r
+  //\r
+  for (Index = 0; Index < LoadOptionCount; Index++) {\r
+    //\r
+    // All the load options in the array should be of the same type.\r
+    //\r
+    if (Index == 0) {\r
+      LoadOptionType = LoadOptions[Index].OptionType;\r
+    }\r
+    ASSERT (LoadOptionType == LoadOptions[Index].OptionType);\r
+    ASSERT (LoadOptionType != LoadOptionTypeBoot);\r
+\r
+    Status = EfiBootManagerProcessLoadOption (&LoadOptions[Index]);\r
 \r
+    //\r
+    // Status indicates whether the load option is loaded and executed\r
+    // LoadOptions[Index].Status is what the load option returns\r
+    //\r
+    if (!EFI_ERROR (Status)) {\r
       //\r
-      // Boot success, then stop process the boot order, and\r
-      // present the boot manager menu, front page\r
+      // Stop processing if any PlatformRecovery#### returns success.\r
       //\r
-      Timeout = 0xffff;\r
-      PlatformBdsEnterFrontPage (Timeout, FALSE);\r
+      if ((LoadOptions[Index].Status == EFI_SUCCESS) &&\r
+          (LoadOptionType == LoadOptionTypePlatformRecovery)) {\r
+        break;\r
+      }\r
 \r
       //\r
-      // Rescan the boot option list, avoid pertential risk of the boot\r
-      // option change in front page\r
+      // Only set ReconnectAll flag when the load option executes successfully.\r
       //\r
-      if (BootNextExist) {\r
-        LinkBootNext = BootLists.ForwardLink;\r
+      if (!EFI_ERROR (LoadOptions[Index].Status) &&\r
+          (LoadOptions[Index].Attributes & LOAD_OPTION_FORCE_RECONNECT) != 0) {\r
+        ReconnectAll = TRUE;\r
       }\r
+    }\r
+  }\r
 \r
-      InitializeListHead (&BootLists);\r
-      if (LinkBootNext != NULL) {\r
-        //\r
-        // Reserve the boot next option\r
-        //\r
-        InsertTailList (&BootLists, LinkBootNext);\r
-      }\r
+  //\r
+  // If a driver load option is marked as LOAD_OPTION_FORCE_RECONNECT,\r
+  // then all of the EFI drivers in the system will be disconnected and\r
+  // reconnected after the last driver load option is processed.\r
+  //\r
+  if (ReconnectAll && LoadOptionType == LoadOptionTypeDriver) {\r
+    EfiBootManagerDisconnectAll ();\r
+    EfiBootManagerConnectAll ();\r
+  }\r
+}\r
 \r
-      BdsLibBuildOptionFromVar (&BootLists, L"BootOrder");\r
-      Link = BootLists.ForwardLink;\r
-    }\r
+/**\r
+\r
+  Validate input console variable data.\r
+\r
+  If found the device path is not a valid device path, remove the variable.\r
+\r
+  @param VariableName             Input console variable name.\r
+\r
+**/\r
+VOID\r
+BdsFormalizeConsoleVariable (\r
+  IN  CHAR16          *VariableName\r
+  )\r
+{\r
+  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;\r
+  UINTN                     VariableSize;\r
+  EFI_STATUS                Status;\r
+\r
+  GetEfiGlobalVariable2 (VariableName, (VOID **) &DevicePath, &VariableSize);\r
+  if ((DevicePath != NULL) && !IsDevicePathValid (DevicePath, VariableSize)) {\r
+    Status = gRT->SetVariable (\r
+                    VariableName,\r
+                    &gEfiGlobalVariableGuid,\r
+                    EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
+                    0,\r
+                    NULL\r
+                    );\r
+    //\r
+    // Deleting variable with current variable implementation shouldn't fail.\r
+    //\r
+    ASSERT_EFI_ERROR (Status);\r
   }\r
 \r
+  if (DevicePath != NULL) {\r
+    FreePool (DevicePath);\r
+  }\r
 }\r
 \r
 /**\r
+  Formalize OsIndication related variables.\r
 \r
-  Service routine for BdsInstance->Entry(). Devices are connected, the\r
-  consoles are initialized, and the boot options are tried.\r
+  For OsIndicationsSupported, Create a BS/RT/UINT64 variable to report caps\r
+  Delete OsIndications variable if it is not NV/BS/RT UINT64.\r
+\r
+  Item 3 is used to solve case when OS corrupts OsIndications. Here simply delete this NV variable.\r
+\r
+  Create a boot option for BootManagerMenu if it hasn't been created yet\r
+\r
+**/\r
+VOID\r
+BdsFormalizeOSIndicationVariable (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS                      Status;\r
+  UINT64                          OsIndicationSupport;\r
+  UINT64                          OsIndication;\r
+  UINTN                           DataSize;\r
+  UINT32                          Attributes;\r
+  EFI_BOOT_MANAGER_LOAD_OPTION    BootManagerMenu;\r
+\r
+  //\r
+  // OS indicater support variable\r
+  //\r
+  Status = EfiBootManagerGetBootManagerMenu (&BootManagerMenu);\r
+  if (Status != EFI_NOT_FOUND) {\r
+    OsIndicationSupport = EFI_OS_INDICATIONS_BOOT_TO_FW_UI;\r
+    EfiBootManagerFreeLoadOption (&BootManagerMenu);\r
+  } else {\r
+    OsIndicationSupport = 0;\r
+  }\r
+\r
+  if (PcdGetBool (PcdPlatformRecoverySupport)) {\r
+    OsIndicationSupport |= EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY;\r
+  }\r
+\r
+  if (PcdGetBool(PcdCapsuleOnDiskSupport)) {\r
+    OsIndicationSupport |= EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED;\r
+  }\r
+\r
+  Status = gRT->SetVariable (\r
+                  EFI_OS_INDICATIONS_SUPPORT_VARIABLE_NAME,\r
+                  &gEfiGlobalVariableGuid,\r
+                  EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
+                  sizeof(UINT64),\r
+                  &OsIndicationSupport\r
+                  );\r
+  //\r
+  // Platform needs to make sure setting volatile variable before calling 3rd party code shouldn't fail.\r
+  //\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  //\r
+  // If OsIndications is invalid, remove it.\r
+  // Invalid case\r
+  //   1. Data size != UINT64\r
+  //   2. OsIndication value inconsistence\r
+  //   3. OsIndication attribute inconsistence\r
+  //\r
+  OsIndication = 0;\r
+  Attributes = 0;\r
+  DataSize = sizeof(UINT64);\r
+  Status = gRT->GetVariable (\r
+                  EFI_OS_INDICATIONS_VARIABLE_NAME,\r
+                  &gEfiGlobalVariableGuid,\r
+                  &Attributes,\r
+                  &DataSize,\r
+                  &OsIndication\r
+                  );\r
+  if (Status == EFI_NOT_FOUND) {\r
+    return;\r
+  }\r
+\r
+  if ((DataSize != sizeof (OsIndication)) ||\r
+      ((OsIndication & ~OsIndicationSupport) != 0) ||\r
+      (Attributes != (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE))\r
+     ){\r
+\r
+    DEBUG ((EFI_D_ERROR, "[Bds] Unformalized OsIndications variable exists. Delete it\n"));\r
+    Status = gRT->SetVariable (\r
+                    EFI_OS_INDICATIONS_VARIABLE_NAME,\r
+                    &gEfiGlobalVariableGuid,\r
+                    0,\r
+                    0,\r
+                    NULL\r
+                    );\r
+    //\r
+    // Deleting variable with current variable implementation shouldn't fail.\r
+    //\r
+    ASSERT_EFI_ERROR(Status);\r
+  }\r
+}\r
+\r
+/**\r
+\r
+  Validate variables.\r
+\r
+**/\r
+VOID\r
+BdsFormalizeEfiGlobalVariable (\r
+  VOID\r
+  )\r
+{\r
+  //\r
+  // Validate Console variable.\r
+  //\r
+  BdsFormalizeConsoleVariable (EFI_CON_IN_VARIABLE_NAME);\r
+  BdsFormalizeConsoleVariable (EFI_CON_OUT_VARIABLE_NAME);\r
+  BdsFormalizeConsoleVariable (EFI_ERR_OUT_VARIABLE_NAME);\r
+\r
+  //\r
+  // Validate OSIndication related variable.\r
+  //\r
+  BdsFormalizeOSIndicationVariable ();\r
+}\r
 \r
+/**\r
 \r
-  @param This            - Protocol Instance structure.\r
+  Service routine for BdsInstance->Entry(). Devices are connected, the\r
+  consoles are initialized, and the boot options are tried.\r
 \r
-  @retval  EFI_SUCEESS  BDS->Entry has finished executing.\r
+  @param This             Protocol Instance structure.\r
 \r
 **/\r
 VOID\r
@@ -270,84 +651,510 @@ BdsEntry (
   IN EFI_BDS_ARCH_PROTOCOL  *This\r
   )\r
 {\r
-  EFI_BDS_ARCH_PROTOCOL_INSTANCE  *PrivateData;\r
-  LIST_ENTRY                      DriverOptionList;\r
-  LIST_ENTRY                      BootOptionList;\r
-  UINTN                           BootNextSize;\r
+  EFI_BOOT_MANAGER_LOAD_OPTION    *LoadOptions;\r
+  UINTN                           LoadOptionCount;\r
+  CHAR16                          *FirmwareVendor;\r
+  EFI_EVENT                       HotkeyTriggered;\r
+  UINT64                          OsIndication;\r
+  UINTN                           DataSize;\r
+  EFI_STATUS                      Status;\r
+  UINT32                          BootOptionSupport;\r
+  UINT16                          BootTimeOut;\r
+  EDKII_VARIABLE_LOCK_PROTOCOL    *VariableLock;\r
+  UINTN                           Index;\r
+  EFI_BOOT_MANAGER_LOAD_OPTION    LoadOption;\r
+  UINT16                          *BootNext;\r
+  CHAR16                          BootNextVariableName[sizeof ("Boot####")];\r
+  EFI_BOOT_MANAGER_LOAD_OPTION    BootManagerMenu;\r
+  BOOLEAN                         BootFwUi;\r
+  BOOLEAN                         PlatformRecovery;\r
+  BOOLEAN                         BootSuccess;\r
+  EFI_DEVICE_PATH_PROTOCOL        *FilePath;\r
+  EFI_STATUS                      BootManagerMenuStatus;\r
+  EFI_BOOT_MANAGER_LOAD_OPTION    PlatformDefaultBootOption;\r
+\r
+  HotkeyTriggered = NULL;\r
+  Status          = EFI_SUCCESS;\r
+  BootSuccess     = FALSE;\r
 \r
   //\r
   // Insert the performance probe\r
   //\r
-  PERF_END (0, DXE_TOK, NULL, 0);\r
-  PERF_START (0, BDS_TOK, NULL, 0);\r
+  PERF_CROSSMODULE_END("DXE");\r
+  PERF_CROSSMODULE_BEGIN("BDS");\r
+  DEBUG ((EFI_D_INFO, "[Bds] Entry...\n"));\r
 \r
   //\r
-  // Initialize the global system boot option and driver option\r
+  // Fill in FirmwareVendor and FirmwareRevision from PCDs\r
   //\r
-  InitializeListHead (&DriverOptionList);\r
-  InitializeListHead (&BootOptionList);\r
+  FirmwareVendor = (CHAR16 *) PcdGetPtr (PcdFirmwareVendor);\r
+  gST->FirmwareVendor = AllocateRuntimeCopyPool (StrSize (FirmwareVendor), FirmwareVendor);\r
+  ASSERT (gST->FirmwareVendor != NULL);\r
+  gST->FirmwareRevision = PcdGet32 (PcdFirmwareRevision);\r
 \r
   //\r
-  // Initialize hotkey service\r
+  // Fixup Tasble CRC after we updated Firmware Vendor and Revision\r
   //\r
-  InitializeHotkeyService ();\r
+  gST->Hdr.CRC32 = 0;\r
+  gBS->CalculateCrc32 ((VOID *) gST, sizeof (EFI_SYSTEM_TABLE), &gST->Hdr.CRC32);\r
 \r
   //\r
-  // Get the BDS private data\r
+  // Validate Variable.\r
   //\r
-  PrivateData = EFI_BDS_ARCH_PROTOCOL_INSTANCE_FROM_THIS (This);\r
+  BdsFormalizeEfiGlobalVariable ();\r
 \r
   //\r
-  // Do the platform init, can be customized by OEM/IBV\r
+  // Mark the read-only variables if the Variable Lock protocol exists\r
+  //\r
+  Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock);\r
+  DEBUG ((EFI_D_INFO, "[BdsDxe] Locate Variable Lock protocol - %r\n", Status));\r
+  if (!EFI_ERROR (Status)) {\r
+    for (Index = 0; Index < ARRAY_SIZE (mReadOnlyVariables); Index++) {\r
+      Status = VariableLock->RequestToLock (VariableLock, mReadOnlyVariables[Index], &gEfiGlobalVariableGuid);\r
+      ASSERT_EFI_ERROR (Status);\r
+    }\r
+  }\r
+\r
+  InitializeHwErrRecSupport ();\r
+\r
+  //\r
+  // Initialize L"Timeout" EFI global variable.\r
+  //\r
+  BootTimeOut = PcdGet16 (PcdPlatformBootTimeOut);\r
+  if (BootTimeOut != 0xFFFF) {\r
+    //\r
+    // If time out value equal 0xFFFF, no need set to 0xFFFF to variable area because UEFI specification\r
+    // define same behavior between no value or 0xFFFF value for L"Timeout".\r
+    //\r
+    BdsDxeSetVariableAndReportStatusCodeOnError (\r
+      EFI_TIME_OUT_VARIABLE_NAME,\r
+      &gEfiGlobalVariableGuid,\r
+      EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
+      sizeof (UINT16),\r
+      &BootTimeOut\r
+      );\r
+  }\r
+\r
   //\r
-  PERF_START (0, "PlatformBds", "BDS", 0);\r
-  PlatformBdsInit (PrivateData);\r
+  // Initialize L"BootOptionSupport" EFI global variable.\r
+  // Lazy-ConIn implictly disables BDS hotkey.\r
+  //\r
+  BootOptionSupport = EFI_BOOT_OPTION_SUPPORT_APP | EFI_BOOT_OPTION_SUPPORT_SYSPREP;\r
+  if (!PcdGetBool (PcdConInConnectOnDemand)) {\r
+    BootOptionSupport |= EFI_BOOT_OPTION_SUPPORT_KEY;\r
+    SET_BOOT_OPTION_SUPPORT_KEY_COUNT (BootOptionSupport, 3);\r
+  }\r
+  Status = gRT->SetVariable (\r
+                  EFI_BOOT_OPTION_SUPPORT_VARIABLE_NAME,\r
+                  &gEfiGlobalVariableGuid,\r
+                  EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
+                  sizeof (BootOptionSupport),\r
+                  &BootOptionSupport\r
+                  );\r
+  //\r
+  // Platform needs to make sure setting volatile variable before calling 3rd party code shouldn't fail.\r
+  //\r
+  ASSERT_EFI_ERROR (Status);\r
 \r
-  if (FeaturePcdGet (PcdSupportHardwareErrorRecord)) {\r
-    InitializeHwErrRecSupport (PcdGet16 (PcdHardwareErrorRecordLevel));\r
+  //\r
+  // Cache the "BootNext" NV variable before calling any PlatformBootManagerLib APIs\r
+  // This could avoid the "BootNext" set by PlatformBootManagerLib be consumed in this boot.\r
+  //\r
+  GetEfiGlobalVariable2 (EFI_BOOT_NEXT_VARIABLE_NAME, (VOID **) &BootNext, &DataSize);\r
+  if (DataSize != sizeof (UINT16)) {\r
+    if (BootNext != NULL) {\r
+      FreePool (BootNext);\r
+    }\r
+    BootNext = NULL;\r
   }\r
+\r
   //\r
-  // bugbug: platform specific code\r
-  // Initialize the platform specific string and language\r
+  // Initialize the platform language variables\r
   //\r
-  InitializeStringSupport ();\r
   InitializeLanguage (TRUE);\r
-  InitializeFrontPage (TRUE);\r
+\r
+  FilePath = FileDevicePath (NULL, EFI_REMOVABLE_MEDIA_FILE_NAME);\r
+  if (FilePath == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "Fail to allocate memory for defualt boot file path. Unable to boot.\n"));\r
+    CpuDeadLoop ();\r
+  }\r
+  Status = EfiBootManagerInitializeLoadOption (\r
+             &PlatformDefaultBootOption,\r
+             LoadOptionNumberUnassigned,\r
+             LoadOptionTypePlatformRecovery,\r
+             LOAD_OPTION_ACTIVE,\r
+             L"Default PlatformRecovery",\r
+             FilePath,\r
+             NULL,\r
+             0\r
+             );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  //\r
+  // System firmware must include a PlatformRecovery#### variable specifying\r
+  // a short-form File Path Media Device Path containing the platform default\r
+  // file path for removable media if the platform supports Platform Recovery.\r
+  //\r
+  if (PcdGetBool (PcdPlatformRecoverySupport)) {\r
+    LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypePlatformRecovery);\r
+    if (EfiBootManagerFindLoadOption (&PlatformDefaultBootOption, LoadOptions, LoadOptionCount) == -1) {\r
+      for (Index = 0; Index < LoadOptionCount; Index++) {\r
+        //\r
+        // The PlatformRecovery#### options are sorted by OptionNumber.\r
+        // Find the the smallest unused number as the new OptionNumber.\r
+        //\r
+        if (LoadOptions[Index].OptionNumber != Index) {\r
+          break;\r
+        }\r
+      }\r
+      PlatformDefaultBootOption.OptionNumber = Index;\r
+      Status = EfiBootManagerLoadOptionToVariable (&PlatformDefaultBootOption);\r
+      ASSERT_EFI_ERROR (Status);\r
+    }\r
+    EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount);\r
+  }\r
+  FreePool (FilePath);\r
+\r
+  //\r
+  // Report Status Code to indicate connecting drivers will happen\r
+  //\r
+  REPORT_STATUS_CODE (\r
+    EFI_PROGRESS_CODE,\r
+    (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_BEGIN_CONNECTING_DRIVERS)\r
+    );\r
+\r
+  //\r
+  // Initialize ConnectConIn event before calling platform code.\r
+  //\r
+  if (PcdGetBool (PcdConInConnectOnDemand)) {\r
+    Status = gBS->CreateEventEx (\r
+                    EVT_NOTIFY_SIGNAL,\r
+                    TPL_CALLBACK,\r
+                    BdsDxeOnConnectConInCallBack,\r
+                    NULL,\r
+                    &gConnectConInEventGuid,\r
+                    &gConnectConInEvent\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      gConnectConInEvent = NULL;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Do the platform init, can be customized by OEM/IBV\r
+  // Possible things that can be done in PlatformBootManagerBeforeConsole:\r
+  // > Update console variable: 1. include hot-plug devices; 2. Clear ConIn and add SOL for AMT\r
+  // > Register new Driver#### or Boot####\r
+  // > Register new Key####: e.g.: F12\r
+  // > Signal ReadyToLock event\r
+  // > Authentication action: 1. connect Auth devices; 2. Identify auto logon user.\r
+  //\r
+  PERF_INMODULE_BEGIN("PlatformBootManagerBeforeConsole");\r
+  PlatformBootManagerBeforeConsole ();\r
+  PERF_INMODULE_END("PlatformBootManagerBeforeConsole");\r
+\r
+  //\r
+  // Initialize hotkey service\r
+  //\r
+  EfiBootManagerStartHotkeyService (&HotkeyTriggered);\r
+\r
+  //\r
+  // Execute Driver Options\r
+  //\r
+  LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypeDriver);\r
+  ProcessLoadOptions (LoadOptions, LoadOptionCount);\r
+  EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount);\r
+\r
+  //\r
+  // Connect consoles\r
+  //\r
+  PERF_INMODULE_BEGIN("EfiBootManagerConnectAllDefaultConsoles");\r
+  if (PcdGetBool (PcdConInConnectOnDemand)) {\r
+    EfiBootManagerConnectConsoleVariable (ConOut);\r
+    EfiBootManagerConnectConsoleVariable (ErrOut);\r
+    //\r
+    // Do not connect ConIn devices when lazy ConIn feature is ON.\r
+    //\r
+  } else {\r
+    EfiBootManagerConnectAllDefaultConsoles ();\r
+  }\r
+  PERF_INMODULE_END("EfiBootManagerConnectAllDefaultConsoles");\r
+\r
+  //\r
+  // Do the platform specific action after the console is ready\r
+  // Possible things that can be done in PlatformBootManagerAfterConsole:\r
+  // > Console post action:\r
+  //   > Dynamically switch output mode from 100x31 to 80x25 for certain senarino\r
+  //   > Signal console ready platform customized event\r
+  // > Run diagnostics like memory testing\r
+  // > Connect certain devices\r
+  // > Dispatch aditional option roms\r
+  // > Special boot: e.g.: USB boot, enter UI\r
+  //\r
+  PERF_INMODULE_BEGIN("PlatformBootManagerAfterConsole");\r
+  PlatformBootManagerAfterConsole ();\r
+  PERF_INMODULE_END("PlatformBootManagerAfterConsole");\r
 \r
   //\r
-  // Set up the device list based on EFI 1.1 variables\r
-  // process Driver#### and Load the driver's in the\r
-  // driver option list\r
+  // If any component set PcdTestKeyUsed to TRUE because use of a test key\r
+  // was detected, then display a warning message on the debug log and the console\r
   //\r
-  BdsLibBuildOptionFromVar (&DriverOptionList, L"DriverOrder");\r
-  if (!IsListEmpty (&DriverOptionList)) {\r
-    BdsLibLoadDrivers (&DriverOptionList);\r
+  if (PcdGetBool (PcdTestKeyUsed)) {\r
+    DEBUG ((DEBUG_ERROR, "**********************************\n"));\r
+    DEBUG ((DEBUG_ERROR, "**  WARNING: Test Key is used.  **\n"));\r
+    DEBUG ((DEBUG_ERROR, "**********************************\n"));\r
+    Print (L"**  WARNING: Test Key is used.  **\n");\r
   }\r
+\r
   //\r
-  // Check if we have the boot next option\r
+  // Boot to Boot Manager Menu when EFI_OS_INDICATIONS_BOOT_TO_FW_UI is set. Skip HotkeyBoot\r
   //\r
-  mBootNext = BdsLibGetVariableAndSize (\r
-                L"BootNext",\r
-                &gEfiGlobalVariableGuid,\r
-                &BootNextSize\r
-                );\r
+  DataSize = sizeof (UINT64);\r
+  Status = gRT->GetVariable (\r
+                  EFI_OS_INDICATIONS_VARIABLE_NAME,\r
+                  &gEfiGlobalVariableGuid,\r
+                  NULL,\r
+                  &DataSize,\r
+                  &OsIndication\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    OsIndication = 0;\r
+  }\r
+\r
+  DEBUG_CODE (\r
+    EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LoadOptionType;\r
+    DEBUG ((EFI_D_INFO, "[Bds]OsIndication: %016x\n", OsIndication));\r
+    DEBUG ((EFI_D_INFO, "[Bds]=============Begin Load Options Dumping ...=============\n"));\r
+    for (LoadOptionType = 0; LoadOptionType < LoadOptionTypeMax; LoadOptionType++) {\r
+      DEBUG ((\r
+        EFI_D_INFO, "  %s Options:\n",\r
+        mBdsLoadOptionName[LoadOptionType]\r
+        ));\r
+      LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionType);\r
+      for (Index = 0; Index < LoadOptionCount; Index++) {\r
+        DEBUG ((\r
+          EFI_D_INFO, "    %s%04x: %s \t\t 0x%04x\n",\r
+          mBdsLoadOptionName[LoadOptionType],\r
+          LoadOptions[Index].OptionNumber,\r
+          LoadOptions[Index].Description,\r
+          LoadOptions[Index].Attributes\r
+          ));\r
+      }\r
+      EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount);\r
+    }\r
+    DEBUG ((EFI_D_INFO, "[Bds]=============End Load Options Dumping=============\n"));\r
+  );\r
 \r
   //\r
-  // Setup some platform policy here\r
+  // BootManagerMenu doesn't contain the correct information when return status is EFI_NOT_FOUND.\r
   //\r
-  PlatformBdsPolicyBehavior (PrivateData, &DriverOptionList, &BootOptionList);\r
-  PERF_END (0, "PlatformBds", "BDS", 0);\r
+  BootManagerMenuStatus = EfiBootManagerGetBootManagerMenu (&BootManagerMenu);\r
 \r
+  BootFwUi         = (BOOLEAN) ((OsIndication & EFI_OS_INDICATIONS_BOOT_TO_FW_UI) != 0);\r
+  PlatformRecovery = (BOOLEAN) ((OsIndication & EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY) != 0);\r
   //\r
-  // BDS select the boot device to load OS\r
+  // Clear EFI_OS_INDICATIONS_BOOT_TO_FW_UI to acknowledge OS\r
   //\r
-  BdsBootDeviceSelect ();\r
+  if (BootFwUi || PlatformRecovery) {\r
+    OsIndication &= ~((UINT64) (EFI_OS_INDICATIONS_BOOT_TO_FW_UI | EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY));\r
+    Status = gRT->SetVariable (\r
+               EFI_OS_INDICATIONS_VARIABLE_NAME,\r
+               &gEfiGlobalVariableGuid,\r
+               EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
+               sizeof(UINT64),\r
+               &OsIndication\r
+               );\r
+    //\r
+    // Changing the content without increasing its size with current variable implementation shouldn't fail.\r
+    //\r
+    ASSERT_EFI_ERROR (Status);\r
+  }\r
 \r
   //\r
-  // Only assert here since this is the right behavior, we should never\r
-  // return back to DxeCore.\r
+  // Launch Boot Manager Menu directly when EFI_OS_INDICATIONS_BOOT_TO_FW_UI is set. Skip HotkeyBoot\r
   //\r
-  ASSERT (FALSE);\r
+  if (BootFwUi && (BootManagerMenuStatus != EFI_NOT_FOUND)) {\r
+    //\r
+    // Follow generic rule, Call BdsDxeOnConnectConInCallBack to connect ConIn before enter UI\r
+    //\r
+    if (PcdGetBool (PcdConInConnectOnDemand)) {\r
+      BdsDxeOnConnectConInCallBack (NULL, NULL);\r
+    }\r
+\r
+    //\r
+    // Directly enter the setup page.\r
+    //\r
+    EfiBootManagerBoot (&BootManagerMenu);\r
+  }\r
+\r
+  if (!PlatformRecovery) {\r
+    //\r
+    // Execute SysPrep####\r
+    //\r
+    LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypeSysPrep);\r
+    ProcessLoadOptions (LoadOptions, LoadOptionCount);\r
+    EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount);\r
 \r
-  return ;\r
+    //\r
+    // Execute Key####\r
+    //\r
+    PERF_INMODULE_BEGIN ("BdsWait");\r
+    BdsWait (HotkeyTriggered);\r
+    PERF_INMODULE_END ("BdsWait");\r
+    //\r
+    // BdsReadKeys() can be removed after all keyboard drivers invoke callback in timer callback.\r
+    //\r
+    BdsReadKeys ();\r
+\r
+    EfiBootManagerHotkeyBoot ();\r
+\r
+    if (BootNext != NULL) {\r
+      //\r
+      // Delete "BootNext" NV variable before transferring control to it to prevent loops.\r
+      //\r
+      Status = gRT->SetVariable (\r
+                      EFI_BOOT_NEXT_VARIABLE_NAME,\r
+                      &gEfiGlobalVariableGuid,\r
+                      0,\r
+                      0,\r
+                      NULL\r
+                      );\r
+      //\r
+      // Deleting NV variable shouldn't fail unless it doesn't exist.\r
+      //\r
+      ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND);\r
+\r
+      //\r
+      // Boot to "BootNext"\r
+      //\r
+      UnicodeSPrint (BootNextVariableName, sizeof (BootNextVariableName), L"Boot%04x", *BootNext);\r
+      Status = EfiBootManagerVariableToLoadOption (BootNextVariableName, &LoadOption);\r
+      if (!EFI_ERROR (Status)) {\r
+        EfiBootManagerBoot (&LoadOption);\r
+        EfiBootManagerFreeLoadOption (&LoadOption);\r
+        if ((LoadOption.Status == EFI_SUCCESS) &&\r
+            (BootManagerMenuStatus != EFI_NOT_FOUND) &&\r
+            (LoadOption.OptionNumber != BootManagerMenu.OptionNumber)) {\r
+          //\r
+          // Boot to Boot Manager Menu upon EFI_SUCCESS\r
+          // Exception: Do not boot again when the BootNext points to Boot Manager Menu.\r
+          //\r
+          EfiBootManagerBoot (&BootManagerMenu);\r
+        }\r
+      }\r
+    }\r
+\r
+    do {\r
+      //\r
+      // Retry to boot if any of the boot succeeds\r
+      //\r
+      LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypeBoot);\r
+      BootSuccess = BootBootOptions (LoadOptions, LoadOptionCount, (BootManagerMenuStatus != EFI_NOT_FOUND) ? &BootManagerMenu : NULL);\r
+      EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount);\r
+    } while (BootSuccess);\r
+  }\r
+\r
+  if (BootManagerMenuStatus != EFI_NOT_FOUND) {\r
+    EfiBootManagerFreeLoadOption (&BootManagerMenu);\r
+  }\r
+\r
+  if (!BootSuccess) {\r
+    if (PlatformRecovery) {\r
+      LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypePlatformRecovery);\r
+      ProcessLoadOptions (LoadOptions, LoadOptionCount);\r
+      EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount);\r
+    } else {\r
+      //\r
+      // When platform recovery is not enabled, still boot to platform default file path.\r
+      //\r
+      EfiBootManagerProcessLoadOption (&PlatformDefaultBootOption);\r
+    }\r
+  }\r
+  EfiBootManagerFreeLoadOption (&PlatformDefaultBootOption);\r
+\r
+  DEBUG ((EFI_D_ERROR, "[Bds] Unable to boot!\n"));\r
+  PlatformBootManagerUnableToBoot ();\r
+  CpuDeadLoop ();\r
+}\r
+\r
+/**\r
+  Set the variable and report the error through status code upon failure.\r
+\r
+  @param  VariableName           A Null-terminated string that is the name of the vendor's variable.\r
+                                 Each VariableName is unique for each VendorGuid. VariableName must\r
+                                 contain 1 or more characters. If VariableName is an empty string,\r
+                                 then EFI_INVALID_PARAMETER is returned.\r
+  @param  VendorGuid             A unique identifier for the vendor.\r
+  @param  Attributes             Attributes bitmask to set for the variable.\r
+  @param  DataSize               The size in bytes of the Data buffer. Unless the EFI_VARIABLE_APPEND_WRITE,\r
+                                 or EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute is set, a size of zero\r
+                                 causes the variable to be deleted. When the EFI_VARIABLE_APPEND_WRITE attribute is\r
+                                 set, then a SetVariable() call with a DataSize of zero will not cause any change to\r
+                                 the variable value (the timestamp associated with the variable may be updated however\r
+                                 even if no new data value is provided,see the description of the\r
+                                 EFI_VARIABLE_AUTHENTICATION_2 descriptor below. In this case the DataSize will not\r
+                                 be zero since the EFI_VARIABLE_AUTHENTICATION_2 descriptor will be populated).\r
+  @param  Data                   The contents for the variable.\r
+\r
+  @retval EFI_SUCCESS            The firmware has successfully stored the variable and its data as\r
+                                 defined by the Attributes.\r
+  @retval EFI_INVALID_PARAMETER  An invalid combination of attribute bits, name, and GUID was supplied, or the\r
+                                 DataSize exceeds the maximum allowed.\r
+  @retval EFI_INVALID_PARAMETER  VariableName is an empty string.\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 retrieved due to a hardware error.\r
+  @retval EFI_WRITE_PROTECTED    The variable in question is read-only.\r
+  @retval EFI_WRITE_PROTECTED    The variable in question cannot be deleted.\r
+  @retval EFI_SECURITY_VIOLATION The variable could not be written due to EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACESS\r
+                                 being set, but the AuthInfo does NOT pass the validation check carried out by the firmware.\r
+\r
+  @retval EFI_NOT_FOUND          The variable trying to be updated or deleted was not found.\r
+**/\r
+EFI_STATUS\r
+BdsDxeSetVariableAndReportStatusCodeOnError (\r
+  IN CHAR16     *VariableName,\r
+  IN EFI_GUID   *VendorGuid,\r
+  IN UINT32     Attributes,\r
+  IN UINTN      DataSize,\r
+  IN VOID       *Data\r
+  )\r
+{\r
+  EFI_STATUS                 Status;\r
+  EDKII_SET_VARIABLE_STATUS  *SetVariableStatus;\r
+  UINTN                      NameSize;\r
+\r
+  Status = gRT->SetVariable (\r
+                  VariableName,\r
+                  VendorGuid,\r
+                  Attributes,\r
+                  DataSize,\r
+                  Data\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    NameSize = StrSize (VariableName);\r
+    SetVariableStatus = AllocatePool (sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + DataSize);\r
+    if (SetVariableStatus != NULL) {\r
+      CopyGuid (&SetVariableStatus->Guid, VendorGuid);\r
+      SetVariableStatus->NameSize   = NameSize;\r
+      SetVariableStatus->DataSize   = DataSize;\r
+      SetVariableStatus->SetStatus  = Status;\r
+      SetVariableStatus->Attributes = Attributes;\r
+      CopyMem (SetVariableStatus + 1,                          VariableName, NameSize);\r
+      CopyMem (((UINT8 *) (SetVariableStatus + 1)) + NameSize, Data,         DataSize);\r
+\r
+      REPORT_STATUS_CODE_EX (\r
+        EFI_ERROR_CODE,\r
+        PcdGet32 (PcdErrorCodeSetVariable),\r
+        0,\r
+        NULL,\r
+        &gEdkiiStatusCodeDataTypeVariableGuid,\r
+        SetVariableStatus,\r
+        sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + DataSize\r
+        );\r
+\r
+      FreePool (SetVariableStatus);\r
+    }\r
+  }\r
+\r
+  return Status;\r
 }\r