2 Implementation for PlatformBootManagerLib library class interfaces.
4 Copyright (C) 2015-2016, Red Hat, Inc.
5 Copyright (c) 2014, 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 // Locate the PCI root bridges and make the PCI bus driver connect each,
554 // non-recursively. This will produce a number of child handles with PciIo on
557 FilterAndProcess (&gEfiPciRootBridgeIoProtocolGuid
, NULL
, Connect
);
560 // Find all display class PCI devices (using the handles from the previous
561 // step), and connect them non-recursively. This should produce a number of
562 // child handles with GOPs on them.
564 FilterAndProcess (&gEfiPciIoProtocolGuid
, IsPciDisplay
, Connect
);
567 // Now add the device path of all handles with GOP on them to ConOut and
570 FilterAndProcess (&gEfiGraphicsOutputProtocolGuid
, NULL
, AddOutput
);
573 // Add the hardcoded short-form USB keyboard device path to ConIn.
575 EfiBootManagerUpdateConsoleVariable (ConIn
,
576 (EFI_DEVICE_PATH_PROTOCOL
*)&mUsbKeyboard
, NULL
);
579 // Add the hardcoded serial console device path to ConIn, ConOut, ErrOut.
581 ASSERT (FixedPcdGet8 (PcdDefaultTerminalType
) == 4);
582 CopyGuid (&mSerialConsole
.TermType
.Guid
, &gEfiTtyTermGuid
);
584 EfiBootManagerUpdateConsoleVariable (ConIn
,
585 (EFI_DEVICE_PATH_PROTOCOL
*)&mSerialConsole
, NULL
);
586 EfiBootManagerUpdateConsoleVariable (ConOut
,
587 (EFI_DEVICE_PATH_PROTOCOL
*)&mSerialConsole
, NULL
);
588 EfiBootManagerUpdateConsoleVariable (ErrOut
,
589 (EFI_DEVICE_PATH_PROTOCOL
*)&mSerialConsole
, NULL
);
592 // Register platform-specific boot options and keyboard shortcuts.
594 PlatformRegisterOptionsAndKeys ();
603 ESRT_MANAGEMENT_PROTOCOL
*EsrtManagement
;
604 EFI_PEI_HOB_POINTERS HobPointer
;
605 EFI_CAPSULE_HEADER
*CapsuleHeader
;
609 DEBUG ((DEBUG_INFO
, "%a: processing capsules ...\n", __FUNCTION__
));
611 Status
= gBS
->LocateProtocol (&gEsrtManagementProtocolGuid
, NULL
,
612 (VOID
**)&EsrtManagement
);
613 if (!EFI_ERROR (Status
)) {
614 EsrtManagement
->SyncEsrtFmp ();
618 // Find all capsule images from hob
620 HobPointer
.Raw
= GetHobList ();
622 while ((HobPointer
.Raw
= GetNextHob (EFI_HOB_TYPE_UEFI_CAPSULE
,
623 HobPointer
.Raw
)) != NULL
) {
624 CapsuleHeader
= (VOID
*)(UINTN
)HobPointer
.Capsule
->BaseAddress
;
626 Status
= ProcessCapsuleImage (CapsuleHeader
);
627 if (EFI_ERROR (Status
)) {
628 DEBUG ((DEBUG_ERROR
, "%a: failed to process capsule %p - %r\n",
629 __FUNCTION__
, CapsuleHeader
, Status
));
634 HobPointer
.Raw
= GET_NEXT_HOB (HobPointer
);
638 DEBUG ((DEBUG_WARN
, "%a: capsule update successful, resetting ...\n",
641 gRT
->ResetSystem (EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
647 #define VERSION_STRING_PREFIX L"Tianocore/EDK2 firmware version "
650 Do the platform specific action after the console is ready
651 Possible things that can be done in PlatformBootManagerAfterConsole:
652 > Console post action:
653 > Dynamically switch output mode from 100x31 to 80x25 for certain scenario
654 > Signal console ready platform customized event
655 > Run diagnostics like memory testing
656 > Connect certain devices
657 > Dispatch additional option roms
658 > Special boot: e.g.: USB boot, enter UI
662 PlatformBootManagerAfterConsole (
667 EFI_GRAPHICS_OUTPUT_PROTOCOL
*GraphicsOutput
;
668 UINTN FirmwareVerLength
;
672 FirmwareVerLength
= StrLen (PcdGetPtr (PcdFirmwareVersionString
));
675 // Show the splash screen.
677 Status
= BootLogoEnableLogo ();
678 if (EFI_ERROR (Status
)) {
679 if (FirmwareVerLength
> 0) {
680 Print (VERSION_STRING_PREFIX L
"%s\n",
681 PcdGetPtr (PcdFirmwareVersionString
));
683 Print (L
"Press ESCAPE for boot options ");
684 } else if (FirmwareVerLength
> 0) {
685 Status
= gBS
->HandleProtocol (gST
->ConsoleOutHandle
,
686 &gEfiGraphicsOutputProtocolGuid
, (VOID
**)&GraphicsOutput
);
687 if (!EFI_ERROR (Status
)) {
688 PosX
= (GraphicsOutput
->Mode
->Info
->HorizontalResolution
-
689 (StrLen (VERSION_STRING_PREFIX
) + FirmwareVerLength
) *
690 EFI_GLYPH_WIDTH
) / 2;
693 PrintXY (PosX
, PosY
, NULL
, NULL
, VERSION_STRING_PREFIX L
"%s",
694 PcdGetPtr (PcdFirmwareVersionString
));
699 // Connect the rest of the devices.
701 EfiBootManagerConnectAll ();
704 // On ARM, there is currently no reason to use the phased capsule
705 // update approach where some capsules are dispatched before EndOfDxe
706 // and some are dispatched after. So just handle all capsules here,
707 // when the console is up and we can actually give the user some
708 // feedback about what is going on.
713 // Enumerate all possible boot options.
715 EfiBootManagerRefreshAllBootOption ();
718 // Register UEFI Shell
720 PlatformRegisterFvBootOption (
721 &gUefiShellFileGuid
, L
"UEFI Shell", LOAD_OPTION_ACTIVE
726 This function is called each second during the boot manager waits the
729 @param TimeoutRemain The remaining timeout.
733 PlatformBootManagerWaitCallback (
737 EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Black
;
738 EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION White
;
742 Timeout
= PcdGet16 (PcdPlatformBootTimeOut
);
744 Black
.Raw
= 0x00000000;
745 White
.Raw
= 0x00FFFFFF;
747 Status
= BootLogoUpdateProgress (
750 L
"Press ESCAPE for boot options",
752 (Timeout
- TimeoutRemain
) * 100 / Timeout
,
755 if (EFI_ERROR (Status
)) {
761 The function is called when no boot option could be launched,
762 including platform recovery options and options pointing to applications
763 built into firmware volumes.
765 If this function returns, BDS attempts to enter an infinite loop.
769 PlatformBootManagerUnableToBoot (