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