]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Universal/BdsDxe/BdsEntry.c
MdeModulePkg/BdsDxe: Don't delete "BootNext" until booting it
[mirror_edk2.git] / MdeModulePkg / Universal / BdsDxe / BdsEntry.c
index aacc4a64794de9212469dd290e23de5b8bf4d5c1..a6fe617b56939ea169a86e113fb6d52e9b5d3a13 100644 (file)
@@ -5,7 +5,7 @@
   After DxeCore finish DXE phase, gEfiBdsArchProtocolGuid->BdsEntry will be invoked\r
   to enter BDS phase.\r
 \r
-Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2004 - 2017, 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
 This program and the accompanying materials\r
@@ -86,6 +86,81 @@ BdsDxeOnConnectConInCallBack (
     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
@@ -119,6 +194,21 @@ BdsInitialize (
                   );\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
@@ -279,6 +369,11 @@ BootBootOptions (
 {\r
   UINTN                              Index;\r
 \r
+  //\r
+  // Report Status Code to indicate BDS starts attempting booting from the UEFI BootOrder list.\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
   // Attempt boot each boot option\r
   //\r
@@ -353,17 +448,28 @@ ProcessLoadOptions (
       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
-      if (LoadOptionType == LoadOptionTypePlatformRecovery) {\r
-        //\r
-        // Stop processing if any entry is successful\r
-        //\r
+      //\r
+      // Stop processing if any PlatformRecovery#### returns success.\r
+      //\r
+      if ((LoadOptions[Index].Status == EFI_SUCCESS) &&\r
+          (LoadOptionType == LoadOptionTypePlatformRecovery)) {\r
         break;\r
       }\r
-      if ((LoadOptions[Index].Attributes & LOAD_OPTION_FORCE_RECONNECT) != 0) {\r
+\r
+      //\r
+      // Only set ReconnectAll flag when the load option executes successfully.\r
+      //\r
+      if (!EFI_ERROR (LoadOptions[Index].Status) &&\r
+          (LoadOptions[Index].Attributes & LOAD_OPTION_FORCE_RECONNECT) != 0) {\r
         ReconnectAll = TRUE;\r
       }\r
     }\r
@@ -702,7 +808,8 @@ BdsEntry (
   ASSERT_EFI_ERROR (Status);\r
 \r
   //\r
-  // Cache and remove the "BootNext" NV variable.\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
@@ -711,17 +818,6 @@ BdsEntry (
     }\r
     BootNext = NULL;\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
   // Initialize the platform language variables\r
@@ -736,7 +832,7 @@ BdsEntry (
   FilePath = FileDevicePath (NULL, EFI_REMOVABLE_MEDIA_FILE_NAME);\r
   Status = EfiBootManagerInitializeLoadOption (\r
              &LoadOption,\r
-             0,\r
+             LoadOptionNumberUnassigned,\r
              LoadOptionTypePlatformRecovery,\r
              LOAD_OPTION_ACTIVE,\r
              L"Default PlatformRecovery",\r
@@ -745,9 +841,24 @@ BdsEntry (
              0\r
              );\r
   ASSERT_EFI_ERROR (Status);\r
-  EfiBootManagerLoadOptionToVariable (&LoadOption);\r
+  LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypePlatformRecovery);\r
+  if (EfiBootManagerFindLoadOption (&LoadOption, 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
+    LoadOption.OptionNumber = Index;\r
+    Status = EfiBootManagerLoadOptionToVariable (&LoadOption);\r
+    ASSERT_EFI_ERROR (Status);\r
+  }\r
   EfiBootManagerFreeLoadOption (&LoadOption);\r
   FreePool (FilePath);\r
+  EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount);\r
 \r
   //\r
   // Report Status Code to indicate connecting drivers will happen\r
@@ -757,6 +868,23 @@ BdsEntry (
     (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
@@ -789,21 +917,9 @@ BdsEntry (
   if (PcdGetBool (PcdConInConnectOnDemand)) {\r
     EfiBootManagerConnectConsoleVariable (ConOut);\r
     EfiBootManagerConnectConsoleVariable (ErrOut);\r
-\r
     //\r
-    // Initialize ConnectConIn event\r
+    // Do not connect ConIn devices when lazy ConIn feature is ON.\r
     //\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
   } else {\r
     EfiBootManagerConnectAllDefaultConsoles ();\r
   }\r
@@ -926,10 +1042,25 @@ BdsEntry (
 \r
     EfiBootManagerHotkeyBoot ();\r
 \r
-    //\r
-    // Boot to "BootNext"\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