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
} 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
}\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
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
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
NULL,\r
BootOptionNumber,\r
0,\r
- BootKeys[Index],\r
+ &BootKeys[Index],\r
NULL\r
);\r
if (EFI_ERROR (Status)) {\r
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
//\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
//\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
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
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
}\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
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