]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ArmPkg/Library/PlatformBootManagerLib/PlatformBm.c
ArmPkg: Enable boot discovery policy for ARM package.
[mirror_edk2.git] / ArmPkg / Library / PlatformBootManagerLib / PlatformBm.c
index 3456a71fbb9cd12912a443abc1907d2015cb0944..1e4020487aa73571164bc78132d7b7e4b0a7330a 100644 (file)
@@ -2,17 +2,12 @@
   Implementation for PlatformBootManagerLib library class interfaces.\r
 \r
   Copyright (C) 2015-2016, Red Hat, Inc.\r
-  Copyright (c) 2014, ARM Ltd. All rights reserved.<BR>\r
-  Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2014 - 2021, ARM Ltd. All rights reserved.<BR>\r
+  Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>\r
   Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>\r
+  Copyright (c) 2021, Semihalf All rights reserved.<BR>\r
 \r
-  This program and the accompanying materials are licensed and made available\r
-  under the terms and conditions of the BSD License which accompanies this\r
-  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, WITHOUT\r
-  WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
 \r
 #include <Library/PcdLib.h>\r
 #include <Library/UefiBootManagerLib.h>\r
 #include <Library/UefiLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+#include <Protocol/BootManagerPolicy.h>\r
 #include <Protocol/DevicePath.h>\r
 #include <Protocol/EsrtManagement.h>\r
 #include <Protocol/GraphicsOutput.h>\r
 #include <Protocol/LoadedImage.h>\r
+#include <Protocol/NonDiscoverableDevice.h>\r
 #include <Protocol/PciIo.h>\r
 #include <Protocol/PciRootBridgeIo.h>\r
 #include <Protocol/PlatformBootManager.h>\r
+#include <Guid/BootDiscoveryPolicy.h>\r
 #include <Guid/EventGroup.h>\r
+#include <Guid/NonDiscoverableDevice.h>\r
 #include <Guid/TtyTerm.h>\r
+#include <Guid/SerialPortLibVendor.h>\r
 \r
 #include "PlatformBm.h"\r
 \r
@@ -47,18 +48,13 @@ typedef struct {
 } PLATFORM_SERIAL_CONSOLE;\r
 #pragma pack ()\r
 \r
-#define SERIAL_DXE_FILE_GUID { \\r
-          0xD3987D4B, 0x971A, 0x435F, \\r
-          { 0x8C, 0xAF, 0x49, 0x67, 0xEB, 0x62, 0x72, 0x41 } \\r
-          }\r
-\r
 STATIC PLATFORM_SERIAL_CONSOLE mSerialConsole = {\r
   //\r
   // VENDOR_DEVICE_PATH SerialDxe\r
   //\r
   {\r
     { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, DP_NODE_LEN (VENDOR_DEVICE_PATH) },\r
-    SERIAL_DXE_FILE_GUID\r
+    EDKII_SERIAL_PORT_LIB_VENDOR_GUID\r
   },\r
 \r
   //\r
@@ -263,6 +259,37 @@ IsPciDisplay (
 }\r
 \r
 \r
+/**\r
+  This FILTER_FUNCTION checks if a handle corresponds to a non-discoverable\r
+  USB host controller.\r
+**/\r
+STATIC\r
+BOOLEAN\r
+EFIAPI\r
+IsUsbHost (\r
+  IN EFI_HANDLE   Handle,\r
+  IN CONST CHAR16 *ReportText\r
+  )\r
+{\r
+  NON_DISCOVERABLE_DEVICE   *Device;\r
+  EFI_STATUS                Status;\r
+\r
+  Status = gBS->HandleProtocol (Handle,\r
+                  &gEdkiiNonDiscoverableDeviceProtocolGuid,\r
+                  (VOID **)&Device);\r
+  if (EFI_ERROR (Status)) {\r
+    return FALSE;\r
+  }\r
+\r
+  if (CompareGuid (Device->Type, &gEdkiiNonDiscoverableUhciDeviceGuid) ||\r
+      CompareGuid (Device->Type, &gEdkiiNonDiscoverableEhciDeviceGuid) ||\r
+      CompareGuid (Device->Type, &gEdkiiNonDiscoverableXhciDeviceGuid)) {\r
+    return TRUE;\r
+  }\r
+  return FALSE;\r
+}\r
+\r
+\r
 /**\r
   This CALLBACK_FUNCTION attempts to connect a handle non-recursively, asking\r
   the matching driver to produce all first-level child handles.\r
@@ -333,7 +360,8 @@ VOID
 PlatformRegisterFvBootOption (\r
   CONST EFI_GUID                   *FileGuid,\r
   CHAR16                           *Description,\r
-  UINT32                           Attributes\r
+  UINT32                           Attributes,\r
+  EFI_INPUT_KEY                    *Key\r
   )\r
 {\r
   EFI_STATUS                        Status;\r
@@ -385,6 +413,9 @@ PlatformRegisterFvBootOption (
   if (OptionIndex == -1) {\r
     Status = EfiBootManagerAddLoadOptionVariable (&NewOption, MAX_UINTN);\r
     ASSERT_EFI_ERROR (Status);\r
+    Status = EfiBootManagerAddKeyOptionVariable (NULL,\r
+               (UINT16)NewOption.OptionNumber, 0, Key, NULL);\r
+    ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);\r
   }\r
   EfiBootManagerFreeLoadOption (&NewOption);\r
   EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
@@ -478,7 +509,7 @@ GetPlatformOptions (
                NULL,\r
                BootOptionNumber,\r
                0,\r
-               BootKeys[Index],\r
+               &BootKeys[Index],\r
                NULL\r
                );\r
     if (EFI_ERROR (Status)) {\r
@@ -553,26 +584,16 @@ PlatformBootManagerBeforeConsole (
   VOID\r
   )\r
 {\r
-  EFI_STATUS                    Status;\r
-  ESRT_MANAGEMENT_PROTOCOL      *EsrtManagement;\r
-\r
-  if (GetBootModeHob() == BOOT_ON_FLASH_UPDATE) {\r
-    DEBUG ((DEBUG_INFO, "ProcessCapsules Before EndOfDxe ......\n"));\r
-    Status = ProcessCapsules ();\r
-    DEBUG ((DEBUG_INFO, "ProcessCapsules returned %r\n", Status));\r
-  } else {\r
-    Status = gBS->LocateProtocol (&gEsrtManagementProtocolGuid, NULL,\r
-                    (VOID **)&EsrtManagement);\r
-    if (!EFI_ERROR (Status)) {\r
-      EsrtManagement->SyncEsrtFmp ();\r
-    }\r
-  }\r
-\r
   //\r
   // Signal EndOfDxe PI Event\r
   //\r
   EfiEventGroupSignal (&gEfiEndOfDxeEventGroupGuid);\r
 \r
+  //\r
+  // Dispatch deferred images after EndOfDxe event.\r
+  //\r
+  EfiBootManagerDispatchDeferredImages ();\r
+\r
   //\r
   // Locate the PCI root bridges and make the PCI bus driver connect each,\r
   // non-recursively. This will produce a number of child handles with PciIo on\r
@@ -593,6 +614,15 @@ PlatformBootManagerBeforeConsole (
   //\r
   FilterAndProcess (&gEfiGraphicsOutputProtocolGuid, NULL, AddOutput);\r
 \r
+  //\r
+  // The core BDS code connects short-form USB device paths by explicitly\r
+  // looking for handles with PCI I/O installed, and checking the PCI class\r
+  // code whether it matches the one for a USB host controller. This means\r
+  // non-discoverable USB host controllers need to have the non-discoverable\r
+  // PCI driver attached first.\r
+  //\r
+  FilterAndProcess (&gEdkiiNonDiscoverableDeviceProtocolGuid, IsUsbHost, Connect);\r
+\r
   //\r
   // Add the hardcoded short-form USB keyboard device path to ConIn.\r
   //\r
@@ -602,7 +632,13 @@ PlatformBootManagerBeforeConsole (
   //\r
   // Add the hardcoded serial console device path to ConIn, ConOut, ErrOut.\r
   //\r
-  ASSERT (FixedPcdGet8 (PcdDefaultTerminalType) == 4);\r
+  STATIC_ASSERT (FixedPcdGet8 (PcdDefaultTerminalType) == 4,\r
+    "PcdDefaultTerminalType must be TTYTERM");\r
+  STATIC_ASSERT (FixedPcdGet8 (PcdUartDefaultParity) != 0,\r
+    "PcdUartDefaultParity must be set to an actual value, not 'default'");\r
+  STATIC_ASSERT (FixedPcdGet8 (PcdUartDefaultStopBits) != 0,\r
+    "PcdUartDefaultStopBits must be set to an actual value, not 'default'");\r
+\r
   CopyGuid (&mSerialConsole.TermType.Guid, &gEfiTtyTermGuid);\r
 \r
   EfiBootManagerUpdateConsoleVariable (ConIn,\r
@@ -618,17 +654,174 @@ PlatformBootManagerBeforeConsole (
   PlatformRegisterOptionsAndKeys ();\r
 }\r
 \r
+STATIC\r
+VOID\r
+HandleCapsules (\r
+  VOID\r
+  )\r
+{\r
+  ESRT_MANAGEMENT_PROTOCOL    *EsrtManagement;\r
+  EFI_PEI_HOB_POINTERS        HobPointer;\r
+  EFI_CAPSULE_HEADER          *CapsuleHeader;\r
+  BOOLEAN                     NeedReset;\r
+  EFI_STATUS                  Status;\r
+\r
+  DEBUG ((DEBUG_INFO, "%a: processing capsules ...\n", __FUNCTION__));\r
+\r
+  Status = gBS->LocateProtocol (&gEsrtManagementProtocolGuid, NULL,\r
+                  (VOID **)&EsrtManagement);\r
+  if (!EFI_ERROR (Status)) {\r
+    EsrtManagement->SyncEsrtFmp ();\r
+  }\r
+\r
+  //\r
+  // Find all capsule images from hob\r
+  //\r
+  HobPointer.Raw = GetHobList ();\r
+  NeedReset = FALSE;\r
+  while ((HobPointer.Raw = GetNextHob (EFI_HOB_TYPE_UEFI_CAPSULE,\r
+                             HobPointer.Raw)) != NULL) {\r
+    CapsuleHeader = (VOID *)(UINTN)HobPointer.Capsule->BaseAddress;\r
+\r
+    Status = ProcessCapsuleImage (CapsuleHeader);\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((DEBUG_ERROR, "%a: failed to process capsule %p - %r\n",\r
+        __FUNCTION__, CapsuleHeader, Status));\r
+      return;\r
+    }\r
+\r
+    NeedReset = TRUE;\r
+    HobPointer.Raw = GET_NEXT_HOB (HobPointer);\r
+  }\r
+\r
+  if (NeedReset) {\r
+      DEBUG ((DEBUG_WARN, "%a: capsule update successful, resetting ...\n",\r
+        __FUNCTION__));\r
+\r
+      gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL);\r
+      CpuDeadLoop();\r
+  }\r
+}\r
+\r
+\r
 #define VERSION_STRING_PREFIX    L"Tianocore/EDK2 firmware version "\r
 \r
+/**\r
+  This functions checks the value of BootDiscoverPolicy variable and\r
+  connect devices of class specified by that variable. Then it refreshes\r
+  Boot order for newly discovered boot device.\r
+\r
+  @retval  EFI_SUCCESS  Devices connected successfully or connection\r
+                        not required.\r
+  @retval  others       Return values from GetVariable(), LocateProtocol()\r
+                        and ConnectDeviceClass().\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+BootDiscoveryPolicyHandler (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+  UINT32                           DiscoveryPolicy;\r
+  UINT32                           DiscoveryPolicyOld;\r
+  UINTN                            Size;\r
+  EFI_BOOT_MANAGER_POLICY_PROTOCOL *BMPolicy;\r
+  EFI_GUID                         *Class;\r
+\r
+  Size = sizeof (DiscoveryPolicy);\r
+  Status = gRT->GetVariable (\r
+                  BOOT_DISCOVERY_POLICY_VAR,\r
+                  &gBootDiscoveryPolicyMgrFormsetGuid,\r
+                  NULL,\r
+                  &Size,\r
+                  &DiscoveryPolicy\r
+                  );\r
+  if (Status == EFI_NOT_FOUND) {\r
+    DiscoveryPolicy = PcdGet32 (PcdBootDiscoveryPolicy);\r
+    Status = PcdSet32S (PcdBootDiscoveryPolicy, DiscoveryPolicy);\r
+    if (Status == EFI_NOT_FOUND) {\r
+      return EFI_SUCCESS;\r
+    } else if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  } else if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (DiscoveryPolicy == BDP_CONNECT_MINIMAL) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  switch (DiscoveryPolicy) {\r
+    case BDP_CONNECT_NET:\r
+      Class = &gEfiBootManagerPolicyNetworkGuid;\r
+      break;\r
+    case BDP_CONNECT_ALL:\r
+      Class = &gEfiBootManagerPolicyConnectAllGuid;\r
+      break;\r
+    default:\r
+      DEBUG ((\r
+        DEBUG_INFO,\r
+        "%a - Unexpected DiscoveryPolicy (0x%x). Run Minimal Discovery Policy\n",\r
+        __FUNCTION__,\r
+        DiscoveryPolicy\r
+        ));\r
+      return EFI_SUCCESS;\r
+  }\r
+\r
+  Status = gBS->LocateProtocol (\r
+                  &gEfiBootManagerPolicyProtocolGuid,\r
+                  NULL,\r
+                  (VOID **)&BMPolicy\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_INFO, "%a - Failed to locate gEfiBootManagerPolicyProtocolGuid."\r
+      "Driver connect will be skipped.\n", __FUNCTION__));\r
+    return Status;\r
+  }\r
+\r
+  Status = BMPolicy->ConnectDeviceClass (BMPolicy, Class);\r
+  if (EFI_ERROR (Status)){\r
+    DEBUG ((DEBUG_ERROR, "%a - ConnectDeviceClass returns - %r\n", __FUNCTION__, Status));\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Refresh Boot Options if Boot Discovery Policy has been changed\r
+  //\r
+  Size = sizeof (DiscoveryPolicyOld);\r
+  Status = gRT->GetVariable (\r
+                  BOOT_DISCOVERY_POLICY_OLD_VAR,\r
+                  &gBootDiscoveryPolicyMgrFormsetGuid,\r
+                  NULL,\r
+                  &Size,\r
+                  &DiscoveryPolicyOld\r
+                  );\r
+  if ((Status == EFI_NOT_FOUND) || (DiscoveryPolicyOld != DiscoveryPolicy)) {\r
+    EfiBootManagerRefreshAllBootOption ();\r
+\r
+    Status = gRT->SetVariable (\r
+                    BOOT_DISCOVERY_POLICY_OLD_VAR,\r
+                    &gBootDiscoveryPolicyMgrFormsetGuid,\r
+                    EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+                    sizeof (DiscoveryPolicyOld),\r
+                    &DiscoveryPolicy\r
+                    );\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\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
+    > Dynamically switch output mode from 100x31 to 80x25 for certain scenario\r
     > Signal console ready platform customized event\r
   > Run diagnostics like memory testing\r
   > Connect certain devices\r
-  > Dispatch aditional option roms\r
+  > Dispatch additional option roms\r
   > Special boot: e.g.: USB boot, enter UI\r
 **/\r
 VOID\r
@@ -637,12 +830,12 @@ PlatformBootManagerAfterConsole (
   VOID\r
   )\r
 {\r
-  ESRT_MANAGEMENT_PROTOCOL      *EsrtManagement;\r
   EFI_STATUS                    Status;\r
   EFI_GRAPHICS_OUTPUT_PROTOCOL  *GraphicsOutput;\r
   UINTN                         FirmwareVerLength;\r
   UINTN                         PosX;\r
   UINTN                         PosY;\r
+  EFI_INPUT_KEY                 Key;\r
 \r
   FirmwareVerLength = StrLen (PcdGetPtr (PcdFirmwareVersionString));\r
 \r
@@ -671,33 +864,26 @@ PlatformBootManagerAfterConsole (
   }\r
 \r
   //\r
-  // Connect the rest of the devices.\r
+  // Connect device specified by BootDiscoverPolicy variable and\r
+  // refresh Boot order for newly discovered boot devices\r
   //\r
-  EfiBootManagerConnectAll ();\r
-\r
-  Status = gBS->LocateProtocol (&gEsrtManagementProtocolGuid, NULL,\r
-                  (VOID **)&EsrtManagement);\r
-  if (!EFI_ERROR (Status)) {\r
-    EsrtManagement->SyncEsrtFmp ();\r
-  }\r
-\r
-  if (GetBootModeHob() == BOOT_ON_FLASH_UPDATE) {\r
-    DEBUG((DEBUG_INFO, "ProcessCapsules After EndOfDxe ......\n"));\r
-    Status = ProcessCapsules ();\r
-    DEBUG((DEBUG_INFO, "ProcessCapsules returned %r\n", Status));\r
-  }\r
+  BootDiscoveryPolicyHandler ();\r
 \r
   //\r
-  // Enumerate all possible boot options.\r
+  // On ARM, there is currently no reason to use the phased capsule\r
+  // update approach where some capsules are dispatched before EndOfDxe\r
+  // and some are dispatched after. So just handle all capsules here,\r
+  // when the console is up and we can actually give the user some\r
+  // feedback about what is going on.\r
   //\r
-  EfiBootManagerRefreshAllBootOption ();\r
+  HandleCapsules ();\r
 \r
   //\r
   // Register UEFI Shell\r
   //\r
-  PlatformRegisterFvBootOption (\r
-    &gUefiShellFileGuid, L"UEFI Shell", LOAD_OPTION_ACTIVE\r
-    );\r
+  Key.ScanCode     = SCAN_NULL;\r
+  Key.UnicodeChar  = L's';\r
+  PlatformRegisterFvBootOption (&gUefiShellFileGuid, L"UEFI Shell", 0, &Key);\r
 }\r
 \r
 /**\r
@@ -734,3 +920,67 @@ PlatformBootManagerWaitCallback (
     Print (L".");\r
   }\r
 }\r
+\r
+/**\r
+  The function is called when no boot option could be launched,\r
+  including platform recovery options and options pointing to applications\r
+  built into firmware volumes.\r
+\r
+  If this function returns, BDS attempts to enter an infinite loop.\r
+**/\r
+VOID\r
+EFIAPI\r
+PlatformBootManagerUnableToBoot (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  EFI_BOOT_MANAGER_LOAD_OPTION BootManagerMenu;\r
+  EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
+  UINTN                        OldBootOptionCount;\r
+  UINTN                        NewBootOptionCount;\r
+\r
+  //\r
+  // Record the total number of boot configured boot options\r
+  //\r
+  BootOptions = EfiBootManagerGetLoadOptions (&OldBootOptionCount,\r
+                  LoadOptionTypeBoot);\r
+  EfiBootManagerFreeLoadOptions (BootOptions, OldBootOptionCount);\r
+\r
+  //\r
+  // Connect all devices, and regenerate all boot options\r
+  //\r
+  EfiBootManagerConnectAll ();\r
+  EfiBootManagerRefreshAllBootOption ();\r
+\r
+  //\r
+  // Record the updated number of boot configured boot options\r
+  //\r
+  BootOptions = EfiBootManagerGetLoadOptions (&NewBootOptionCount,\r
+                  LoadOptionTypeBoot);\r
+  EfiBootManagerFreeLoadOptions (BootOptions, NewBootOptionCount);\r
+\r
+  //\r
+  // If the number of configured boot options has changed, reboot\r
+  // the system so the new boot options will be taken into account\r
+  // while executing the ordinary BDS bootflow sequence.\r
+  // *Unless* persistent varstore is being emulated, since we would\r
+  // then end up in an endless reboot loop.\r
+  //\r
+  if (!PcdGetBool (PcdEmuVariableNvModeEnable)) {\r
+    if (NewBootOptionCount != OldBootOptionCount) {\r
+      DEBUG ((DEBUG_WARN, "%a: rebooting after refreshing all boot options\n",\r
+        __FUNCTION__));\r
+      gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL);\r
+    }\r
+  }\r
+\r
+  Status = EfiBootManagerGetBootManagerMenu (&BootManagerMenu);\r
+  if (EFI_ERROR (Status)) {\r
+    return;\r
+  }\r
+\r
+  for (;;) {\r
+    EfiBootManagerBoot (&BootManagerMenu);\r
+  }\r
+}\r