2 Implementation for PlatformBootManagerLib library class interfaces.
4 Copyright (C) 2015-2016, Red Hat, Inc.
5 Copyright (c) 2014 - 2019, ARM Ltd. All rights reserved.<BR>
6 Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
7 Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>
9 SPDX-License-Identifier: BSD-2-Clause-Patent
13 #include <IndustryStandard/Pci22.h>
14 #include <Library/BootLogoLib.h>
15 #include <Library/CapsuleLib.h>
16 #include <Library/DevicePathLib.h>
17 #include <Library/HobLib.h>
18 #include <Library/PcdLib.h>
19 #include <Library/UefiBootManagerLib.h>
20 #include <Library/UefiLib.h>
21 #include <Library/UefiRuntimeServicesTableLib.h>
22 #include <Protocol/DevicePath.h>
23 #include <Protocol/EsrtManagement.h>
24 #include <Protocol/GraphicsOutput.h>
25 #include <Protocol/LoadedImage.h>
26 #include <Protocol/NonDiscoverableDevice.h>
27 #include <Protocol/PciIo.h>
28 #include <Protocol/PciRootBridgeIo.h>
29 #include <Protocol/PlatformBootManager.h>
30 #include <Guid/EventGroup.h>
31 #include <Guid/NonDiscoverableDevice.h>
32 #include <Guid/TtyTerm.h>
33 #include <Guid/SerialPortLibVendor.h>
35 #include "PlatformBm.h"
37 #define DP_NODE_LEN(Type) { (UINT8)sizeof (Type), (UINT8)(sizeof (Type) >> 8) }
41 VENDOR_DEVICE_PATH SerialDxe
;
42 UART_DEVICE_PATH Uart
;
43 VENDOR_DEFINED_DEVICE_PATH TermType
;
44 EFI_DEVICE_PATH_PROTOCOL End
;
45 } PLATFORM_SERIAL_CONSOLE
;
48 STATIC PLATFORM_SERIAL_CONSOLE mSerialConsole
= {
50 // VENDOR_DEVICE_PATH SerialDxe
53 { HARDWARE_DEVICE_PATH
, HW_VENDOR_DP
, DP_NODE_LEN (VENDOR_DEVICE_PATH
) },
54 EDKII_SERIAL_PORT_LIB_VENDOR_GUID
58 // UART_DEVICE_PATH Uart
61 { MESSAGING_DEVICE_PATH
, MSG_UART_DP
, DP_NODE_LEN (UART_DEVICE_PATH
) },
63 FixedPcdGet64 (PcdUartDefaultBaudRate
), // BaudRate
64 FixedPcdGet8 (PcdUartDefaultDataBits
), // DataBits
65 FixedPcdGet8 (PcdUartDefaultParity
), // Parity
66 FixedPcdGet8 (PcdUartDefaultStopBits
) // StopBits
70 // VENDOR_DEFINED_DEVICE_PATH TermType
74 MESSAGING_DEVICE_PATH
, MSG_VENDOR_DP
,
75 DP_NODE_LEN (VENDOR_DEFINED_DEVICE_PATH
)
78 // Guid to be filled in dynamically
83 // EFI_DEVICE_PATH_PROTOCOL End
86 END_DEVICE_PATH_TYPE
, END_ENTIRE_DEVICE_PATH_SUBTYPE
,
87 DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL
)
94 USB_CLASS_DEVICE_PATH Keyboard
;
95 EFI_DEVICE_PATH_PROTOCOL End
;
96 } PLATFORM_USB_KEYBOARD
;
99 STATIC PLATFORM_USB_KEYBOARD mUsbKeyboard
= {
101 // USB_CLASS_DEVICE_PATH Keyboard
105 MESSAGING_DEVICE_PATH
, MSG_USB_CLASS_DP
,
106 DP_NODE_LEN (USB_CLASS_DEVICE_PATH
)
108 0xFFFF, // VendorId: any
109 0xFFFF, // ProductId: any
110 3, // DeviceClass: HID
111 1, // DeviceSubClass: boot
112 1 // DeviceProtocol: keyboard
116 // EFI_DEVICE_PATH_PROTOCOL End
119 END_DEVICE_PATH_TYPE
, END_ENTIRE_DEVICE_PATH_SUBTYPE
,
120 DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL
)
126 Check if the handle satisfies a particular condition.
128 @param[in] Handle The handle to check.
129 @param[in] ReportText A caller-allocated string passed in for reporting
130 purposes. It must never be NULL.
132 @retval TRUE The condition is satisfied.
133 @retval FALSE Otherwise. This includes the case when the condition could not
134 be fully evaluated due to an error.
138 (EFIAPI
*FILTER_FUNCTION
) (
139 IN EFI_HANDLE Handle
,
140 IN CONST CHAR16
*ReportText
147 @param[in] Handle The handle to process.
148 @param[in] ReportText A caller-allocated string passed in for reporting
149 purposes. It must never be NULL.
153 (EFIAPI
*CALLBACK_FUNCTION
) (
154 IN EFI_HANDLE Handle
,
155 IN CONST CHAR16
*ReportText
159 Locate all handles that carry the specified protocol, filter them with a
160 callback function, and pass each handle that passes the filter to another
163 @param[in] ProtocolGuid The protocol to look for.
165 @param[in] Filter The filter function to pass each handle to. If this
166 parameter is NULL, then all handles are processed.
168 @param[in] Process The callback function to pass each handle to that
174 IN EFI_GUID
*ProtocolGuid
,
175 IN FILTER_FUNCTION Filter OPTIONAL
,
176 IN CALLBACK_FUNCTION Process
184 Status
= gBS
->LocateHandleBuffer (ByProtocol
, ProtocolGuid
,
185 NULL
/* SearchKey */, &NoHandles
, &Handles
);
186 if (EFI_ERROR (Status
)) {
188 // This is not an error, just an informative condition.
190 DEBUG ((EFI_D_VERBOSE
, "%a: %g: %r\n", __FUNCTION__
, ProtocolGuid
,
195 ASSERT (NoHandles
> 0);
196 for (Idx
= 0; Idx
< NoHandles
; ++Idx
) {
197 CHAR16
*DevicePathText
;
198 STATIC CHAR16 Fallback
[] = L
"<device path unavailable>";
201 // The ConvertDevicePathToText() function handles NULL input transparently.
203 DevicePathText
= ConvertDevicePathToText (
204 DevicePathFromHandle (Handles
[Idx
]),
205 FALSE
, // DisplayOnly
206 FALSE
// AllowShortcuts
208 if (DevicePathText
== NULL
) {
209 DevicePathText
= Fallback
;
212 if (Filter
== NULL
|| Filter (Handles
[Idx
], DevicePathText
)) {
213 Process (Handles
[Idx
], DevicePathText
);
216 if (DevicePathText
!= Fallback
) {
217 FreePool (DevicePathText
);
220 gBS
->FreePool (Handles
);
225 This FILTER_FUNCTION checks if a handle corresponds to a PCI display device.
231 IN EFI_HANDLE Handle
,
232 IN CONST CHAR16
*ReportText
236 EFI_PCI_IO_PROTOCOL
*PciIo
;
239 Status
= gBS
->HandleProtocol (Handle
, &gEfiPciIoProtocolGuid
,
241 if (EFI_ERROR (Status
)) {
243 // This is not an error worth reporting.
248 Status
= PciIo
->Pci
.Read (PciIo
, EfiPciIoWidthUint32
, 0 /* Offset */,
249 sizeof Pci
/ sizeof (UINT32
), &Pci
);
250 if (EFI_ERROR (Status
)) {
251 DEBUG ((EFI_D_ERROR
, "%a: %s: %r\n", __FUNCTION__
, ReportText
, Status
));
255 return IS_PCI_DISPLAY (&Pci
);
260 This FILTER_FUNCTION checks if a handle corresponds to a non-discoverable
267 IN EFI_HANDLE Handle
,
268 IN CONST CHAR16
*ReportText
271 NON_DISCOVERABLE_DEVICE
*Device
;
274 Status
= gBS
->HandleProtocol (Handle
,
275 &gEdkiiNonDiscoverableDeviceProtocolGuid
,
277 if (EFI_ERROR (Status
)) {
281 if (CompareGuid (Device
->Type
, &gEdkiiNonDiscoverableUhciDeviceGuid
) ||
282 CompareGuid (Device
->Type
, &gEdkiiNonDiscoverableEhciDeviceGuid
) ||
283 CompareGuid (Device
->Type
, &gEdkiiNonDiscoverableXhciDeviceGuid
)) {
291 This CALLBACK_FUNCTION attempts to connect a handle non-recursively, asking
292 the matching driver to produce all first-level child handles.
298 IN EFI_HANDLE Handle
,
299 IN CONST CHAR16
*ReportText
304 Status
= gBS
->ConnectController (
305 Handle
, // ControllerHandle
306 NULL
, // DriverImageHandle
307 NULL
, // RemainingDevicePath -- produce all children
310 DEBUG ((EFI_ERROR (Status
) ? EFI_D_ERROR
: EFI_D_VERBOSE
, "%a: %s: %r\n",
311 __FUNCTION__
, ReportText
, Status
));
316 This CALLBACK_FUNCTION retrieves the EFI_DEVICE_PATH_PROTOCOL from the
317 handle, and adds it to ConOut and ErrOut.
323 IN EFI_HANDLE Handle
,
324 IN CONST CHAR16
*ReportText
328 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
330 DevicePath
= DevicePathFromHandle (Handle
);
331 if (DevicePath
== NULL
) {
332 DEBUG ((EFI_D_ERROR
, "%a: %s: handle %p: device path not found\n",
333 __FUNCTION__
, ReportText
, Handle
));
337 Status
= EfiBootManagerUpdateConsoleVariable (ConOut
, DevicePath
, NULL
);
338 if (EFI_ERROR (Status
)) {
339 DEBUG ((EFI_D_ERROR
, "%a: %s: adding to ConOut: %r\n", __FUNCTION__
,
340 ReportText
, Status
));
344 Status
= EfiBootManagerUpdateConsoleVariable (ErrOut
, DevicePath
, NULL
);
345 if (EFI_ERROR (Status
)) {
346 DEBUG ((EFI_D_ERROR
, "%a: %s: adding to ErrOut: %r\n", __FUNCTION__
,
347 ReportText
, Status
));
351 DEBUG ((EFI_D_VERBOSE
, "%a: %s: added to ConOut and ErrOut\n", __FUNCTION__
,
357 PlatformRegisterFvBootOption (
358 CONST EFI_GUID
*FileGuid
,
365 EFI_BOOT_MANAGER_LOAD_OPTION NewOption
;
366 EFI_BOOT_MANAGER_LOAD_OPTION
*BootOptions
;
367 UINTN BootOptionCount
;
368 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode
;
369 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
370 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
372 Status
= gBS
->HandleProtocol (
374 &gEfiLoadedImageProtocolGuid
,
375 (VOID
**) &LoadedImage
377 ASSERT_EFI_ERROR (Status
);
379 EfiInitializeFwVolDevicepathNode (&FileNode
, FileGuid
);
380 DevicePath
= DevicePathFromHandle (LoadedImage
->DeviceHandle
);
381 ASSERT (DevicePath
!= NULL
);
382 DevicePath
= AppendDevicePathNode (
384 (EFI_DEVICE_PATH_PROTOCOL
*) &FileNode
386 ASSERT (DevicePath
!= NULL
);
388 Status
= EfiBootManagerInitializeLoadOption (
390 LoadOptionNumberUnassigned
,
398 ASSERT_EFI_ERROR (Status
);
399 FreePool (DevicePath
);
401 BootOptions
= EfiBootManagerGetLoadOptions (
402 &BootOptionCount
, LoadOptionTypeBoot
405 OptionIndex
= EfiBootManagerFindLoadOption (
406 &NewOption
, BootOptions
, BootOptionCount
409 if (OptionIndex
== -1) {
410 Status
= EfiBootManagerAddLoadOptionVariable (&NewOption
, MAX_UINTN
);
411 ASSERT_EFI_ERROR (Status
);
413 EfiBootManagerFreeLoadOption (&NewOption
);
414 EfiBootManagerFreeLoadOptions (BootOptions
, BootOptionCount
);
425 EFI_BOOT_MANAGER_LOAD_OPTION
*CurrentBootOptions
;
426 EFI_BOOT_MANAGER_LOAD_OPTION
*BootOptions
;
427 EFI_INPUT_KEY
*BootKeys
;
428 PLATFORM_BOOT_MANAGER_PROTOCOL
*PlatformBootManager
;
429 UINTN CurrentBootOptionCount
;
433 Status
= gBS
->LocateProtocol (&gPlatformBootManagerProtocolGuid
, NULL
,
434 (VOID
**)&PlatformBootManager
);
435 if (EFI_ERROR (Status
)) {
438 Status
= PlatformBootManager
->GetPlatformBootOptionsAndKeys (
443 if (EFI_ERROR (Status
)) {
447 // Fetch the existent boot options. If there are none, CurrentBootCount
450 CurrentBootOptions
= EfiBootManagerGetLoadOptions (
451 &CurrentBootOptionCount
,
455 // Process the platform boot options.
457 for (Index
= 0; Index
< BootCount
; Index
++) {
459 UINTN BootOptionNumber
;
462 // If there are any preexistent boot options, and the subject platform boot
463 // option is already among them, then don't try to add it. Just get its
464 // assigned boot option number so we can associate a hotkey with it. Note
465 // that EfiBootManagerFindLoadOption() deals fine with (CurrentBootOptions
466 // == NULL) if (CurrentBootCount == 0).
468 Match
= EfiBootManagerFindLoadOption (
471 CurrentBootOptionCount
474 BootOptionNumber
= CurrentBootOptions
[Match
].OptionNumber
;
477 // Add the platform boot options as a new one, at the end of the boot
478 // order. Note that if the platform provided this boot option with an
479 // unassigned option number, then the below function call will assign a
482 Status
= EfiBootManagerAddLoadOptionVariable (
486 if (EFI_ERROR (Status
)) {
487 DEBUG ((DEBUG_ERROR
, "%a: failed to register \"%s\": %r\n",
488 __FUNCTION__
, BootOptions
[Index
].Description
, Status
));
491 BootOptionNumber
= BootOptions
[Index
].OptionNumber
;
495 // Register a hotkey with the boot option, if requested.
497 if (BootKeys
[Index
].UnicodeChar
== L
'\0') {
501 Status
= EfiBootManagerAddKeyOptionVariable (
508 if (EFI_ERROR (Status
)) {
509 DEBUG ((DEBUG_ERROR
, "%a: failed to register hotkey for \"%s\": %r\n",
510 __FUNCTION__
, BootOptions
[Index
].Description
, Status
));
513 EfiBootManagerFreeLoadOptions (CurrentBootOptions
, CurrentBootOptionCount
);
514 EfiBootManagerFreeLoadOptions (BootOptions
, BootCount
);
520 PlatformRegisterOptionsAndKeys (
528 EFI_BOOT_MANAGER_LOAD_OPTION BootOption
;
530 GetPlatformOptions ();
533 // Register ENTER as CONTINUE key
535 Enter
.ScanCode
= SCAN_NULL
;
536 Enter
.UnicodeChar
= CHAR_CARRIAGE_RETURN
;
537 Status
= EfiBootManagerRegisterContinueKeyOption (0, &Enter
, NULL
);
538 ASSERT_EFI_ERROR (Status
);
541 // Map F2 and ESC to Boot Manager Menu
543 F2
.ScanCode
= SCAN_F2
;
544 F2
.UnicodeChar
= CHAR_NULL
;
545 Esc
.ScanCode
= SCAN_ESC
;
546 Esc
.UnicodeChar
= CHAR_NULL
;
547 Status
= EfiBootManagerGetBootManagerMenu (&BootOption
);
548 ASSERT_EFI_ERROR (Status
);
549 Status
= EfiBootManagerAddKeyOptionVariable (
550 NULL
, (UINT16
) BootOption
.OptionNumber
, 0, &F2
, NULL
552 ASSERT (Status
== EFI_SUCCESS
|| Status
== EFI_ALREADY_STARTED
);
553 Status
= EfiBootManagerAddKeyOptionVariable (
554 NULL
, (UINT16
) BootOption
.OptionNumber
, 0, &Esc
, NULL
556 ASSERT (Status
== EFI_SUCCESS
|| Status
== EFI_ALREADY_STARTED
);
561 // BDS Platform Functions
564 Do the platform init, can be customized by OEM/IBV
565 Possible things that can be done in PlatformBootManagerBeforeConsole:
566 > Update console variable: 1. include hot-plug devices;
567 > 2. Clear ConIn and add SOL for AMT
568 > Register new Driver#### or Boot####
569 > Register new Key####: e.g.: F12
570 > Signal ReadyToLock event
571 > Authentication action: 1. connect Auth devices;
572 > 2. Identify auto logon user.
576 PlatformBootManagerBeforeConsole (
581 // Signal EndOfDxe PI Event
583 EfiEventGroupSignal (&gEfiEndOfDxeEventGroupGuid
);
586 // Dispatch deferred images after EndOfDxe event.
588 EfiBootManagerDispatchDeferredImages ();
591 // Locate the PCI root bridges and make the PCI bus driver connect each,
592 // non-recursively. This will produce a number of child handles with PciIo on
595 FilterAndProcess (&gEfiPciRootBridgeIoProtocolGuid
, NULL
, Connect
);
598 // Find all display class PCI devices (using the handles from the previous
599 // step), and connect them non-recursively. This should produce a number of
600 // child handles with GOPs on them.
602 FilterAndProcess (&gEfiPciIoProtocolGuid
, IsPciDisplay
, Connect
);
605 // Now add the device path of all handles with GOP on them to ConOut and
608 FilterAndProcess (&gEfiGraphicsOutputProtocolGuid
, NULL
, AddOutput
);
611 // The core BDS code connects short-form USB device paths by explicitly
612 // looking for handles with PCI I/O installed, and checking the PCI class
613 // code whether it matches the one for a USB host controller. This means
614 // non-discoverable USB host controllers need to have the non-discoverable
615 // PCI driver attached first.
617 FilterAndProcess (&gEdkiiNonDiscoverableDeviceProtocolGuid
, IsUsbHost
, Connect
);
620 // Add the hardcoded short-form USB keyboard device path to ConIn.
622 EfiBootManagerUpdateConsoleVariable (ConIn
,
623 (EFI_DEVICE_PATH_PROTOCOL
*)&mUsbKeyboard
, NULL
);
626 // Add the hardcoded serial console device path to ConIn, ConOut, ErrOut.
628 STATIC_ASSERT (FixedPcdGet8 (PcdDefaultTerminalType
) == 4,
629 "PcdDefaultTerminalType must be TTYTERM");
630 STATIC_ASSERT (FixedPcdGet8 (PcdUartDefaultParity
) != 0,
631 "PcdUartDefaultParity must be set to an actual value, not 'default'");
632 STATIC_ASSERT (FixedPcdGet8 (PcdUartDefaultStopBits
) != 0,
633 "PcdUartDefaultStopBits must be set to an actual value, not 'default'");
635 CopyGuid (&mSerialConsole
.TermType
.Guid
, &gEfiTtyTermGuid
);
637 EfiBootManagerUpdateConsoleVariable (ConIn
,
638 (EFI_DEVICE_PATH_PROTOCOL
*)&mSerialConsole
, NULL
);
639 EfiBootManagerUpdateConsoleVariable (ConOut
,
640 (EFI_DEVICE_PATH_PROTOCOL
*)&mSerialConsole
, NULL
);
641 EfiBootManagerUpdateConsoleVariable (ErrOut
,
642 (EFI_DEVICE_PATH_PROTOCOL
*)&mSerialConsole
, NULL
);
645 // Register platform-specific boot options and keyboard shortcuts.
647 PlatformRegisterOptionsAndKeys ();
656 ESRT_MANAGEMENT_PROTOCOL
*EsrtManagement
;
657 EFI_PEI_HOB_POINTERS HobPointer
;
658 EFI_CAPSULE_HEADER
*CapsuleHeader
;
662 DEBUG ((DEBUG_INFO
, "%a: processing capsules ...\n", __FUNCTION__
));
664 Status
= gBS
->LocateProtocol (&gEsrtManagementProtocolGuid
, NULL
,
665 (VOID
**)&EsrtManagement
);
666 if (!EFI_ERROR (Status
)) {
667 EsrtManagement
->SyncEsrtFmp ();
671 // Find all capsule images from hob
673 HobPointer
.Raw
= GetHobList ();
675 while ((HobPointer
.Raw
= GetNextHob (EFI_HOB_TYPE_UEFI_CAPSULE
,
676 HobPointer
.Raw
)) != NULL
) {
677 CapsuleHeader
= (VOID
*)(UINTN
)HobPointer
.Capsule
->BaseAddress
;
679 Status
= ProcessCapsuleImage (CapsuleHeader
);
680 if (EFI_ERROR (Status
)) {
681 DEBUG ((DEBUG_ERROR
, "%a: failed to process capsule %p - %r\n",
682 __FUNCTION__
, CapsuleHeader
, Status
));
687 HobPointer
.Raw
= GET_NEXT_HOB (HobPointer
);
691 DEBUG ((DEBUG_WARN
, "%a: capsule update successful, resetting ...\n",
694 gRT
->ResetSystem (EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
700 #define VERSION_STRING_PREFIX L"Tianocore/EDK2 firmware version "
703 Do the platform specific action after the console is ready
704 Possible things that can be done in PlatformBootManagerAfterConsole:
705 > Console post action:
706 > Dynamically switch output mode from 100x31 to 80x25 for certain scenario
707 > Signal console ready platform customized event
708 > Run diagnostics like memory testing
709 > Connect certain devices
710 > Dispatch additional option roms
711 > Special boot: e.g.: USB boot, enter UI
715 PlatformBootManagerAfterConsole (
720 EFI_GRAPHICS_OUTPUT_PROTOCOL
*GraphicsOutput
;
721 UINTN FirmwareVerLength
;
725 FirmwareVerLength
= StrLen (PcdGetPtr (PcdFirmwareVersionString
));
728 // Show the splash screen.
730 Status
= BootLogoEnableLogo ();
731 if (EFI_ERROR (Status
)) {
732 if (FirmwareVerLength
> 0) {
733 Print (VERSION_STRING_PREFIX L
"%s\n",
734 PcdGetPtr (PcdFirmwareVersionString
));
736 Print (L
"Press ESCAPE for boot options ");
737 } else if (FirmwareVerLength
> 0) {
738 Status
= gBS
->HandleProtocol (gST
->ConsoleOutHandle
,
739 &gEfiGraphicsOutputProtocolGuid
, (VOID
**)&GraphicsOutput
);
740 if (!EFI_ERROR (Status
)) {
741 PosX
= (GraphicsOutput
->Mode
->Info
->HorizontalResolution
-
742 (StrLen (VERSION_STRING_PREFIX
) + FirmwareVerLength
) *
743 EFI_GLYPH_WIDTH
) / 2;
746 PrintXY (PosX
, PosY
, NULL
, NULL
, VERSION_STRING_PREFIX L
"%s",
747 PcdGetPtr (PcdFirmwareVersionString
));
752 // Connect the rest of the devices.
754 EfiBootManagerConnectAll ();
757 // On ARM, there is currently no reason to use the phased capsule
758 // update approach where some capsules are dispatched before EndOfDxe
759 // and some are dispatched after. So just handle all capsules here,
760 // when the console is up and we can actually give the user some
761 // feedback about what is going on.
766 // Enumerate all possible boot options.
768 EfiBootManagerRefreshAllBootOption ();
771 // Register UEFI Shell
773 PlatformRegisterFvBootOption (
774 &gUefiShellFileGuid
, L
"UEFI Shell", LOAD_OPTION_ACTIVE
779 This function is called each second during the boot manager waits the
782 @param TimeoutRemain The remaining timeout.
786 PlatformBootManagerWaitCallback (
790 EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Black
;
791 EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION White
;
795 Timeout
= PcdGet16 (PcdPlatformBootTimeOut
);
797 Black
.Raw
= 0x00000000;
798 White
.Raw
= 0x00FFFFFF;
800 Status
= BootLogoUpdateProgress (
803 L
"Press ESCAPE for boot options",
805 (Timeout
- TimeoutRemain
) * 100 / Timeout
,
808 if (EFI_ERROR (Status
)) {
814 The function is called when no boot option could be launched,
815 including platform recovery options and options pointing to applications
816 built into firmware volumes.
818 If this function returns, BDS attempts to enter an infinite loop.
822 PlatformBootManagerUnableToBoot (