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/PciIo.h>
27 #include <Protocol/PciRootBridgeIo.h>
28 #include <Protocol/PlatformBootManager.h>
29 #include <Guid/EventGroup.h>
30 #include <Guid/TtyTerm.h>
31 #include <Guid/SerialPortLibVendor.h>
33 #include "PlatformBm.h"
35 #define DP_NODE_LEN(Type) { (UINT8)sizeof (Type), (UINT8)(sizeof (Type) >> 8) }
39 VENDOR_DEVICE_PATH SerialDxe
;
40 UART_DEVICE_PATH Uart
;
41 VENDOR_DEFINED_DEVICE_PATH TermType
;
42 EFI_DEVICE_PATH_PROTOCOL End
;
43 } PLATFORM_SERIAL_CONSOLE
;
46 STATIC PLATFORM_SERIAL_CONSOLE mSerialConsole
= {
48 // VENDOR_DEVICE_PATH SerialDxe
51 { HARDWARE_DEVICE_PATH
, HW_VENDOR_DP
, DP_NODE_LEN (VENDOR_DEVICE_PATH
) },
52 EDKII_SERIAL_PORT_LIB_VENDOR_GUID
56 // UART_DEVICE_PATH Uart
59 { MESSAGING_DEVICE_PATH
, MSG_UART_DP
, DP_NODE_LEN (UART_DEVICE_PATH
) },
61 FixedPcdGet64 (PcdUartDefaultBaudRate
), // BaudRate
62 FixedPcdGet8 (PcdUartDefaultDataBits
), // DataBits
63 FixedPcdGet8 (PcdUartDefaultParity
), // Parity
64 FixedPcdGet8 (PcdUartDefaultStopBits
) // StopBits
68 // VENDOR_DEFINED_DEVICE_PATH TermType
72 MESSAGING_DEVICE_PATH
, MSG_VENDOR_DP
,
73 DP_NODE_LEN (VENDOR_DEFINED_DEVICE_PATH
)
76 // Guid to be filled in dynamically
81 // EFI_DEVICE_PATH_PROTOCOL End
84 END_DEVICE_PATH_TYPE
, END_ENTIRE_DEVICE_PATH_SUBTYPE
,
85 DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL
)
92 USB_CLASS_DEVICE_PATH Keyboard
;
93 EFI_DEVICE_PATH_PROTOCOL End
;
94 } PLATFORM_USB_KEYBOARD
;
97 STATIC PLATFORM_USB_KEYBOARD mUsbKeyboard
= {
99 // USB_CLASS_DEVICE_PATH Keyboard
103 MESSAGING_DEVICE_PATH
, MSG_USB_CLASS_DP
,
104 DP_NODE_LEN (USB_CLASS_DEVICE_PATH
)
106 0xFFFF, // VendorId: any
107 0xFFFF, // ProductId: any
108 3, // DeviceClass: HID
109 1, // DeviceSubClass: boot
110 1 // DeviceProtocol: keyboard
114 // EFI_DEVICE_PATH_PROTOCOL End
117 END_DEVICE_PATH_TYPE
, END_ENTIRE_DEVICE_PATH_SUBTYPE
,
118 DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL
)
124 Check if the handle satisfies a particular condition.
126 @param[in] Handle The handle to check.
127 @param[in] ReportText A caller-allocated string passed in for reporting
128 purposes. It must never be NULL.
130 @retval TRUE The condition is satisfied.
131 @retval FALSE Otherwise. This includes the case when the condition could not
132 be fully evaluated due to an error.
136 (EFIAPI
*FILTER_FUNCTION
) (
137 IN EFI_HANDLE Handle
,
138 IN CONST CHAR16
*ReportText
145 @param[in] Handle The handle to process.
146 @param[in] ReportText A caller-allocated string passed in for reporting
147 purposes. It must never be NULL.
151 (EFIAPI
*CALLBACK_FUNCTION
) (
152 IN EFI_HANDLE Handle
,
153 IN CONST CHAR16
*ReportText
157 Locate all handles that carry the specified protocol, filter them with a
158 callback function, and pass each handle that passes the filter to another
161 @param[in] ProtocolGuid The protocol to look for.
163 @param[in] Filter The filter function to pass each handle to. If this
164 parameter is NULL, then all handles are processed.
166 @param[in] Process The callback function to pass each handle to that
172 IN EFI_GUID
*ProtocolGuid
,
173 IN FILTER_FUNCTION Filter OPTIONAL
,
174 IN CALLBACK_FUNCTION Process
182 Status
= gBS
->LocateHandleBuffer (ByProtocol
, ProtocolGuid
,
183 NULL
/* SearchKey */, &NoHandles
, &Handles
);
184 if (EFI_ERROR (Status
)) {
186 // This is not an error, just an informative condition.
188 DEBUG ((EFI_D_VERBOSE
, "%a: %g: %r\n", __FUNCTION__
, ProtocolGuid
,
193 ASSERT (NoHandles
> 0);
194 for (Idx
= 0; Idx
< NoHandles
; ++Idx
) {
195 CHAR16
*DevicePathText
;
196 STATIC CHAR16 Fallback
[] = L
"<device path unavailable>";
199 // The ConvertDevicePathToText() function handles NULL input transparently.
201 DevicePathText
= ConvertDevicePathToText (
202 DevicePathFromHandle (Handles
[Idx
]),
203 FALSE
, // DisplayOnly
204 FALSE
// AllowShortcuts
206 if (DevicePathText
== NULL
) {
207 DevicePathText
= Fallback
;
210 if (Filter
== NULL
|| Filter (Handles
[Idx
], DevicePathText
)) {
211 Process (Handles
[Idx
], DevicePathText
);
214 if (DevicePathText
!= Fallback
) {
215 FreePool (DevicePathText
);
218 gBS
->FreePool (Handles
);
223 This FILTER_FUNCTION checks if a handle corresponds to a PCI display device.
229 IN EFI_HANDLE Handle
,
230 IN CONST CHAR16
*ReportText
234 EFI_PCI_IO_PROTOCOL
*PciIo
;
237 Status
= gBS
->HandleProtocol (Handle
, &gEfiPciIoProtocolGuid
,
239 if (EFI_ERROR (Status
)) {
241 // This is not an error worth reporting.
246 Status
= PciIo
->Pci
.Read (PciIo
, EfiPciIoWidthUint32
, 0 /* Offset */,
247 sizeof Pci
/ sizeof (UINT32
), &Pci
);
248 if (EFI_ERROR (Status
)) {
249 DEBUG ((EFI_D_ERROR
, "%a: %s: %r\n", __FUNCTION__
, ReportText
, Status
));
253 return IS_PCI_DISPLAY (&Pci
);
258 This CALLBACK_FUNCTION attempts to connect a handle non-recursively, asking
259 the matching driver to produce all first-level child handles.
265 IN EFI_HANDLE Handle
,
266 IN CONST CHAR16
*ReportText
271 Status
= gBS
->ConnectController (
272 Handle
, // ControllerHandle
273 NULL
, // DriverImageHandle
274 NULL
, // RemainingDevicePath -- produce all children
277 DEBUG ((EFI_ERROR (Status
) ? EFI_D_ERROR
: EFI_D_VERBOSE
, "%a: %s: %r\n",
278 __FUNCTION__
, ReportText
, Status
));
283 This CALLBACK_FUNCTION retrieves the EFI_DEVICE_PATH_PROTOCOL from the
284 handle, and adds it to ConOut and ErrOut.
290 IN EFI_HANDLE Handle
,
291 IN CONST CHAR16
*ReportText
295 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
297 DevicePath
= DevicePathFromHandle (Handle
);
298 if (DevicePath
== NULL
) {
299 DEBUG ((EFI_D_ERROR
, "%a: %s: handle %p: device path not found\n",
300 __FUNCTION__
, ReportText
, Handle
));
304 Status
= EfiBootManagerUpdateConsoleVariable (ConOut
, DevicePath
, NULL
);
305 if (EFI_ERROR (Status
)) {
306 DEBUG ((EFI_D_ERROR
, "%a: %s: adding to ConOut: %r\n", __FUNCTION__
,
307 ReportText
, Status
));
311 Status
= EfiBootManagerUpdateConsoleVariable (ErrOut
, DevicePath
, NULL
);
312 if (EFI_ERROR (Status
)) {
313 DEBUG ((EFI_D_ERROR
, "%a: %s: adding to ErrOut: %r\n", __FUNCTION__
,
314 ReportText
, Status
));
318 DEBUG ((EFI_D_VERBOSE
, "%a: %s: added to ConOut and ErrOut\n", __FUNCTION__
,
324 PlatformRegisterFvBootOption (
325 CONST EFI_GUID
*FileGuid
,
332 EFI_BOOT_MANAGER_LOAD_OPTION NewOption
;
333 EFI_BOOT_MANAGER_LOAD_OPTION
*BootOptions
;
334 UINTN BootOptionCount
;
335 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode
;
336 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
337 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
339 Status
= gBS
->HandleProtocol (
341 &gEfiLoadedImageProtocolGuid
,
342 (VOID
**) &LoadedImage
344 ASSERT_EFI_ERROR (Status
);
346 EfiInitializeFwVolDevicepathNode (&FileNode
, FileGuid
);
347 DevicePath
= DevicePathFromHandle (LoadedImage
->DeviceHandle
);
348 ASSERT (DevicePath
!= NULL
);
349 DevicePath
= AppendDevicePathNode (
351 (EFI_DEVICE_PATH_PROTOCOL
*) &FileNode
353 ASSERT (DevicePath
!= NULL
);
355 Status
= EfiBootManagerInitializeLoadOption (
357 LoadOptionNumberUnassigned
,
365 ASSERT_EFI_ERROR (Status
);
366 FreePool (DevicePath
);
368 BootOptions
= EfiBootManagerGetLoadOptions (
369 &BootOptionCount
, LoadOptionTypeBoot
372 OptionIndex
= EfiBootManagerFindLoadOption (
373 &NewOption
, BootOptions
, BootOptionCount
376 if (OptionIndex
== -1) {
377 Status
= EfiBootManagerAddLoadOptionVariable (&NewOption
, MAX_UINTN
);
378 ASSERT_EFI_ERROR (Status
);
380 EfiBootManagerFreeLoadOption (&NewOption
);
381 EfiBootManagerFreeLoadOptions (BootOptions
, BootOptionCount
);
392 EFI_BOOT_MANAGER_LOAD_OPTION
*CurrentBootOptions
;
393 EFI_BOOT_MANAGER_LOAD_OPTION
*BootOptions
;
394 EFI_INPUT_KEY
*BootKeys
;
395 PLATFORM_BOOT_MANAGER_PROTOCOL
*PlatformBootManager
;
396 UINTN CurrentBootOptionCount
;
400 Status
= gBS
->LocateProtocol (&gPlatformBootManagerProtocolGuid
, NULL
,
401 (VOID
**)&PlatformBootManager
);
402 if (EFI_ERROR (Status
)) {
405 Status
= PlatformBootManager
->GetPlatformBootOptionsAndKeys (
410 if (EFI_ERROR (Status
)) {
414 // Fetch the existent boot options. If there are none, CurrentBootCount
417 CurrentBootOptions
= EfiBootManagerGetLoadOptions (
418 &CurrentBootOptionCount
,
422 // Process the platform boot options.
424 for (Index
= 0; Index
< BootCount
; Index
++) {
426 UINTN BootOptionNumber
;
429 // If there are any preexistent boot options, and the subject platform boot
430 // option is already among them, then don't try to add it. Just get its
431 // assigned boot option number so we can associate a hotkey with it. Note
432 // that EfiBootManagerFindLoadOption() deals fine with (CurrentBootOptions
433 // == NULL) if (CurrentBootCount == 0).
435 Match
= EfiBootManagerFindLoadOption (
438 CurrentBootOptionCount
441 BootOptionNumber
= CurrentBootOptions
[Match
].OptionNumber
;
444 // Add the platform boot options as a new one, at the end of the boot
445 // order. Note that if the platform provided this boot option with an
446 // unassigned option number, then the below function call will assign a
449 Status
= EfiBootManagerAddLoadOptionVariable (
453 if (EFI_ERROR (Status
)) {
454 DEBUG ((DEBUG_ERROR
, "%a: failed to register \"%s\": %r\n",
455 __FUNCTION__
, BootOptions
[Index
].Description
, Status
));
458 BootOptionNumber
= BootOptions
[Index
].OptionNumber
;
462 // Register a hotkey with the boot option, if requested.
464 if (BootKeys
[Index
].UnicodeChar
== L
'\0') {
468 Status
= EfiBootManagerAddKeyOptionVariable (
475 if (EFI_ERROR (Status
)) {
476 DEBUG ((DEBUG_ERROR
, "%a: failed to register hotkey for \"%s\": %r\n",
477 __FUNCTION__
, BootOptions
[Index
].Description
, Status
));
480 EfiBootManagerFreeLoadOptions (CurrentBootOptions
, CurrentBootOptionCount
);
481 EfiBootManagerFreeLoadOptions (BootOptions
, BootCount
);
487 PlatformRegisterOptionsAndKeys (
495 EFI_BOOT_MANAGER_LOAD_OPTION BootOption
;
497 GetPlatformOptions ();
500 // Register ENTER as CONTINUE key
502 Enter
.ScanCode
= SCAN_NULL
;
503 Enter
.UnicodeChar
= CHAR_CARRIAGE_RETURN
;
504 Status
= EfiBootManagerRegisterContinueKeyOption (0, &Enter
, NULL
);
505 ASSERT_EFI_ERROR (Status
);
508 // Map F2 and ESC to Boot Manager Menu
510 F2
.ScanCode
= SCAN_F2
;
511 F2
.UnicodeChar
= CHAR_NULL
;
512 Esc
.ScanCode
= SCAN_ESC
;
513 Esc
.UnicodeChar
= CHAR_NULL
;
514 Status
= EfiBootManagerGetBootManagerMenu (&BootOption
);
515 ASSERT_EFI_ERROR (Status
);
516 Status
= EfiBootManagerAddKeyOptionVariable (
517 NULL
, (UINT16
) BootOption
.OptionNumber
, 0, &F2
, NULL
519 ASSERT (Status
== EFI_SUCCESS
|| Status
== EFI_ALREADY_STARTED
);
520 Status
= EfiBootManagerAddKeyOptionVariable (
521 NULL
, (UINT16
) BootOption
.OptionNumber
, 0, &Esc
, NULL
523 ASSERT (Status
== EFI_SUCCESS
|| Status
== EFI_ALREADY_STARTED
);
528 // BDS Platform Functions
531 Do the platform init, can be customized by OEM/IBV
532 Possible things that can be done in PlatformBootManagerBeforeConsole:
533 > Update console variable: 1. include hot-plug devices;
534 > 2. Clear ConIn and add SOL for AMT
535 > Register new Driver#### or Boot####
536 > Register new Key####: e.g.: F12
537 > Signal ReadyToLock event
538 > Authentication action: 1. connect Auth devices;
539 > 2. Identify auto logon user.
543 PlatformBootManagerBeforeConsole (
548 // Signal EndOfDxe PI Event
550 EfiEventGroupSignal (&gEfiEndOfDxeEventGroupGuid
);
553 // Dispatch deferred images after EndOfDxe event.
555 EfiBootManagerDispatchDeferredImages ();
558 // Locate the PCI root bridges and make the PCI bus driver connect each,
559 // non-recursively. This will produce a number of child handles with PciIo on
562 FilterAndProcess (&gEfiPciRootBridgeIoProtocolGuid
, NULL
, Connect
);
565 // Find all display class PCI devices (using the handles from the previous
566 // step), and connect them non-recursively. This should produce a number of
567 // child handles with GOPs on them.
569 FilterAndProcess (&gEfiPciIoProtocolGuid
, IsPciDisplay
, Connect
);
572 // Now add the device path of all handles with GOP on them to ConOut and
575 FilterAndProcess (&gEfiGraphicsOutputProtocolGuid
, NULL
, AddOutput
);
578 // Add the hardcoded short-form USB keyboard device path to ConIn.
580 EfiBootManagerUpdateConsoleVariable (ConIn
,
581 (EFI_DEVICE_PATH_PROTOCOL
*)&mUsbKeyboard
, NULL
);
584 // Add the hardcoded serial console device path to ConIn, ConOut, ErrOut.
586 STATIC_ASSERT (FixedPcdGet8 (PcdDefaultTerminalType
) == 4,
587 "PcdDefaultTerminalType must be TTYTERM");
588 STATIC_ASSERT (FixedPcdGet8 (PcdUartDefaultParity
) != 0,
589 "PcdUartDefaultParity must be set to an actual value, not 'default'");
590 STATIC_ASSERT (FixedPcdGet8 (PcdUartDefaultStopBits
) != 0,
591 "PcdUartDefaultStopBits must be set to an actual value, not 'default'");
593 CopyGuid (&mSerialConsole
.TermType
.Guid
, &gEfiTtyTermGuid
);
595 EfiBootManagerUpdateConsoleVariable (ConIn
,
596 (EFI_DEVICE_PATH_PROTOCOL
*)&mSerialConsole
, NULL
);
597 EfiBootManagerUpdateConsoleVariable (ConOut
,
598 (EFI_DEVICE_PATH_PROTOCOL
*)&mSerialConsole
, NULL
);
599 EfiBootManagerUpdateConsoleVariable (ErrOut
,
600 (EFI_DEVICE_PATH_PROTOCOL
*)&mSerialConsole
, NULL
);
603 // Register platform-specific boot options and keyboard shortcuts.
605 PlatformRegisterOptionsAndKeys ();
614 ESRT_MANAGEMENT_PROTOCOL
*EsrtManagement
;
615 EFI_PEI_HOB_POINTERS HobPointer
;
616 EFI_CAPSULE_HEADER
*CapsuleHeader
;
620 DEBUG ((DEBUG_INFO
, "%a: processing capsules ...\n", __FUNCTION__
));
622 Status
= gBS
->LocateProtocol (&gEsrtManagementProtocolGuid
, NULL
,
623 (VOID
**)&EsrtManagement
);
624 if (!EFI_ERROR (Status
)) {
625 EsrtManagement
->SyncEsrtFmp ();
629 // Find all capsule images from hob
631 HobPointer
.Raw
= GetHobList ();
633 while ((HobPointer
.Raw
= GetNextHob (EFI_HOB_TYPE_UEFI_CAPSULE
,
634 HobPointer
.Raw
)) != NULL
) {
635 CapsuleHeader
= (VOID
*)(UINTN
)HobPointer
.Capsule
->BaseAddress
;
637 Status
= ProcessCapsuleImage (CapsuleHeader
);
638 if (EFI_ERROR (Status
)) {
639 DEBUG ((DEBUG_ERROR
, "%a: failed to process capsule %p - %r\n",
640 __FUNCTION__
, CapsuleHeader
, Status
));
645 HobPointer
.Raw
= GET_NEXT_HOB (HobPointer
);
649 DEBUG ((DEBUG_WARN
, "%a: capsule update successful, resetting ...\n",
652 gRT
->ResetSystem (EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
658 #define VERSION_STRING_PREFIX L"Tianocore/EDK2 firmware version "
661 Do the platform specific action after the console is ready
662 Possible things that can be done in PlatformBootManagerAfterConsole:
663 > Console post action:
664 > Dynamically switch output mode from 100x31 to 80x25 for certain scenario
665 > Signal console ready platform customized event
666 > Run diagnostics like memory testing
667 > Connect certain devices
668 > Dispatch additional option roms
669 > Special boot: e.g.: USB boot, enter UI
673 PlatformBootManagerAfterConsole (
678 EFI_GRAPHICS_OUTPUT_PROTOCOL
*GraphicsOutput
;
679 UINTN FirmwareVerLength
;
683 FirmwareVerLength
= StrLen (PcdGetPtr (PcdFirmwareVersionString
));
686 // Show the splash screen.
688 Status
= BootLogoEnableLogo ();
689 if (EFI_ERROR (Status
)) {
690 if (FirmwareVerLength
> 0) {
691 Print (VERSION_STRING_PREFIX L
"%s\n",
692 PcdGetPtr (PcdFirmwareVersionString
));
694 Print (L
"Press ESCAPE for boot options ");
695 } else if (FirmwareVerLength
> 0) {
696 Status
= gBS
->HandleProtocol (gST
->ConsoleOutHandle
,
697 &gEfiGraphicsOutputProtocolGuid
, (VOID
**)&GraphicsOutput
);
698 if (!EFI_ERROR (Status
)) {
699 PosX
= (GraphicsOutput
->Mode
->Info
->HorizontalResolution
-
700 (StrLen (VERSION_STRING_PREFIX
) + FirmwareVerLength
) *
701 EFI_GLYPH_WIDTH
) / 2;
704 PrintXY (PosX
, PosY
, NULL
, NULL
, VERSION_STRING_PREFIX L
"%s",
705 PcdGetPtr (PcdFirmwareVersionString
));
710 // Connect the rest of the devices.
712 EfiBootManagerConnectAll ();
715 // On ARM, there is currently no reason to use the phased capsule
716 // update approach where some capsules are dispatched before EndOfDxe
717 // and some are dispatched after. So just handle all capsules here,
718 // when the console is up and we can actually give the user some
719 // feedback about what is going on.
724 // Enumerate all possible boot options.
726 EfiBootManagerRefreshAllBootOption ();
729 // Register UEFI Shell
731 PlatformRegisterFvBootOption (
732 &gUefiShellFileGuid
, L
"UEFI Shell", LOAD_OPTION_ACTIVE
737 This function is called each second during the boot manager waits the
740 @param TimeoutRemain The remaining timeout.
744 PlatformBootManagerWaitCallback (
748 EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Black
;
749 EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION White
;
753 Timeout
= PcdGet16 (PcdPlatformBootTimeOut
);
755 Black
.Raw
= 0x00000000;
756 White
.Raw
= 0x00FFFFFF;
758 Status
= BootLogoUpdateProgress (
761 L
"Press ESCAPE for boot options",
763 (Timeout
- TimeoutRemain
) * 100 / Timeout
,
766 if (EFI_ERROR (Status
)) {
772 The function is called when no boot option could be launched,
773 including platform recovery options and options pointing to applications
774 built into firmware volumes.
776 If this function returns, BDS attempts to enter an infinite loop.
780 PlatformBootManagerUnableToBoot (