]> 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 f713605c02b20546aac3d464e0b19824b165bf73..1e4020487aa73571164bc78132d7b7e4b0a7330a 100644 (file)
@@ -2,9 +2,10 @@
   Implementation for PlatformBootManagerLib library class interfaces.\r
 \r
   Copyright (C) 2015-2016, Red Hat, Inc.\r
-  Copyright (c) 2014 - 2019, ARM Ltd. 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
   SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \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
@@ -254,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
@@ -324,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
@@ -376,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
@@ -574,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
@@ -585,6 +634,10 @@ PlatformBootManagerBeforeConsole (
   //\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
@@ -653,6 +706,113 @@ HandleCapsules (
 \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
@@ -675,6 +835,7 @@ PlatformBootManagerAfterConsole (
   UINTN                         FirmwareVerLength;\r
   UINTN                         PosX;\r
   UINTN                         PosY;\r
+  EFI_INPUT_KEY                 Key;\r
 \r
   FirmwareVerLength = StrLen (PcdGetPtr (PcdFirmwareVersionString));\r
 \r
@@ -703,9 +864,10 @@ 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
+  BootDiscoveryPolicyHandler ();\r
 \r
   //\r
   // On ARM, there is currently no reason to use the phased capsule\r
@@ -716,17 +878,12 @@ PlatformBootManagerAfterConsole (
   //\r
   HandleCapsules ();\r
 \r
-  //\r
-  // Enumerate all possible boot options.\r
-  //\r
-  EfiBootManagerRefreshAllBootOption ();\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
@@ -777,5 +934,53 @@ PlatformBootManagerUnableToBoot (
   VOID\r
   )\r
 {\r
-  return;\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