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 This program and the accompanying materials are licensed and made available
10 under the terms and conditions of the BSD License which accompanies this
11 distribution. The full text of the license may be found at
12 http://opensource.org/licenses/bsd-license.php
14 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
15 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
19 #include <IndustryStandard/Pci22.h>
20 #include <Library/BootLogoLib.h>
21 #include <Library/CapsuleLib.h>
22 #include <Library/DevicePathLib.h>
23 #include <Library/HobLib.h>
24 #include <Library/PcdLib.h>
25 #include <Library/UefiBootManagerLib.h>
26 #include <Library/UefiLib.h>
27 #include <Library/UefiRuntimeServicesTableLib.h>
28 #include <Protocol/DevicePath.h>
29 #include <Protocol/EsrtManagement.h>
30 #include <Protocol/GraphicsOutput.h>
31 #include <Protocol/LoadedImage.h>
32 #include <Protocol/PciIo.h>
33 #include <Protocol/PciRootBridgeIo.h>
34 #include <Protocol/PlatformBootManager.h>
35 #include <Guid/EventGroup.h>
36 #include <Guid/TtyTerm.h>
38 #include "PlatformBm.h"
40 #define DP_NODE_LEN(Type) { (UINT8)sizeof (Type), (UINT8)(sizeof (Type) >> 8) }
44 VENDOR_DEVICE_PATH SerialDxe
;
45 UART_DEVICE_PATH Uart
;
46 VENDOR_DEFINED_DEVICE_PATH TermType
;
47 EFI_DEVICE_PATH_PROTOCOL End
;
48 } PLATFORM_SERIAL_CONSOLE
;
51 #define SERIAL_DXE_FILE_GUID { \
52 0xD3987D4B, 0x971A, 0x435F, \
53 { 0x8C, 0xAF, 0x49, 0x67, 0xEB, 0x62, 0x72, 0x41 } \
56 STATIC PLATFORM_SERIAL_CONSOLE mSerialConsole
= {
58 // VENDOR_DEVICE_PATH SerialDxe
61 { HARDWARE_DEVICE_PATH
, HW_VENDOR_DP
, DP_NODE_LEN (VENDOR_DEVICE_PATH
) },
66 // UART_DEVICE_PATH Uart
69 { MESSAGING_DEVICE_PATH
, MSG_UART_DP
, DP_NODE_LEN (UART_DEVICE_PATH
) },
71 FixedPcdGet64 (PcdUartDefaultBaudRate
), // BaudRate
72 FixedPcdGet8 (PcdUartDefaultDataBits
), // DataBits
73 FixedPcdGet8 (PcdUartDefaultParity
), // Parity
74 FixedPcdGet8 (PcdUartDefaultStopBits
) // StopBits
78 // VENDOR_DEFINED_DEVICE_PATH TermType
82 MESSAGING_DEVICE_PATH
, MSG_VENDOR_DP
,
83 DP_NODE_LEN (VENDOR_DEFINED_DEVICE_PATH
)
86 // Guid to be filled in dynamically
91 // EFI_DEVICE_PATH_PROTOCOL End
94 END_DEVICE_PATH_TYPE
, END_ENTIRE_DEVICE_PATH_SUBTYPE
,
95 DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL
)
102 USB_CLASS_DEVICE_PATH Keyboard
;
103 EFI_DEVICE_PATH_PROTOCOL End
;
104 } PLATFORM_USB_KEYBOARD
;
107 STATIC PLATFORM_USB_KEYBOARD mUsbKeyboard
= {
109 // USB_CLASS_DEVICE_PATH Keyboard
113 MESSAGING_DEVICE_PATH
, MSG_USB_CLASS_DP
,
114 DP_NODE_LEN (USB_CLASS_DEVICE_PATH
)
116 0xFFFF, // VendorId: any
117 0xFFFF, // ProductId: any
118 3, // DeviceClass: HID
119 1, // DeviceSubClass: boot
120 1 // DeviceProtocol: keyboard
124 // EFI_DEVICE_PATH_PROTOCOL End
127 END_DEVICE_PATH_TYPE
, END_ENTIRE_DEVICE_PATH_SUBTYPE
,
128 DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL
)
134 Check if the handle satisfies a particular condition.
136 @param[in] Handle The handle to check.
137 @param[in] ReportText A caller-allocated string passed in for reporting
138 purposes. It must never be NULL.
140 @retval TRUE The condition is satisfied.
141 @retval FALSE Otherwise. This includes the case when the condition could not
142 be fully evaluated due to an error.
146 (EFIAPI
*FILTER_FUNCTION
) (
147 IN EFI_HANDLE Handle
,
148 IN CONST CHAR16
*ReportText
155 @param[in] Handle The handle to process.
156 @param[in] ReportText A caller-allocated string passed in for reporting
157 purposes. It must never be NULL.
161 (EFIAPI
*CALLBACK_FUNCTION
) (
162 IN EFI_HANDLE Handle
,
163 IN CONST CHAR16
*ReportText
167 Locate all handles that carry the specified protocol, filter them with a
168 callback function, and pass each handle that passes the filter to another
171 @param[in] ProtocolGuid The protocol to look for.
173 @param[in] Filter The filter function to pass each handle to. If this
174 parameter is NULL, then all handles are processed.
176 @param[in] Process The callback function to pass each handle to that
182 IN EFI_GUID
*ProtocolGuid
,
183 IN FILTER_FUNCTION Filter OPTIONAL
,
184 IN CALLBACK_FUNCTION Process
192 Status
= gBS
->LocateHandleBuffer (ByProtocol
, ProtocolGuid
,
193 NULL
/* SearchKey */, &NoHandles
, &Handles
);
194 if (EFI_ERROR (Status
)) {
196 // This is not an error, just an informative condition.
198 DEBUG ((EFI_D_VERBOSE
, "%a: %g: %r\n", __FUNCTION__
, ProtocolGuid
,
203 ASSERT (NoHandles
> 0);
204 for (Idx
= 0; Idx
< NoHandles
; ++Idx
) {
205 CHAR16
*DevicePathText
;
206 STATIC CHAR16 Fallback
[] = L
"<device path unavailable>";
209 // The ConvertDevicePathToText() function handles NULL input transparently.
211 DevicePathText
= ConvertDevicePathToText (
212 DevicePathFromHandle (Handles
[Idx
]),
213 FALSE
, // DisplayOnly
214 FALSE
// AllowShortcuts
216 if (DevicePathText
== NULL
) {
217 DevicePathText
= Fallback
;
220 if (Filter
== NULL
|| Filter (Handles
[Idx
], DevicePathText
)) {
221 Process (Handles
[Idx
], DevicePathText
);
224 if (DevicePathText
!= Fallback
) {
225 FreePool (DevicePathText
);
228 gBS
->FreePool (Handles
);
233 This FILTER_FUNCTION checks if a handle corresponds to a PCI display device.
239 IN EFI_HANDLE Handle
,
240 IN CONST CHAR16
*ReportText
244 EFI_PCI_IO_PROTOCOL
*PciIo
;
247 Status
= gBS
->HandleProtocol (Handle
, &gEfiPciIoProtocolGuid
,
249 if (EFI_ERROR (Status
)) {
251 // This is not an error worth reporting.
256 Status
= PciIo
->Pci
.Read (PciIo
, EfiPciIoWidthUint32
, 0 /* Offset */,
257 sizeof Pci
/ sizeof (UINT32
), &Pci
);
258 if (EFI_ERROR (Status
)) {
259 DEBUG ((EFI_D_ERROR
, "%a: %s: %r\n", __FUNCTION__
, ReportText
, Status
));
263 return IS_PCI_DISPLAY (&Pci
);
268 This CALLBACK_FUNCTION attempts to connect a handle non-recursively, asking
269 the matching driver to produce all first-level child handles.
275 IN EFI_HANDLE Handle
,
276 IN CONST CHAR16
*ReportText
281 Status
= gBS
->ConnectController (
282 Handle
, // ControllerHandle
283 NULL
, // DriverImageHandle
284 NULL
, // RemainingDevicePath -- produce all children
287 DEBUG ((EFI_ERROR (Status
) ? EFI_D_ERROR
: EFI_D_VERBOSE
, "%a: %s: %r\n",
288 __FUNCTION__
, ReportText
, Status
));
293 This CALLBACK_FUNCTION retrieves the EFI_DEVICE_PATH_PROTOCOL from the
294 handle, and adds it to ConOut and ErrOut.
300 IN EFI_HANDLE Handle
,
301 IN CONST CHAR16
*ReportText
305 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
307 DevicePath
= DevicePathFromHandle (Handle
);
308 if (DevicePath
== NULL
) {
309 DEBUG ((EFI_D_ERROR
, "%a: %s: handle %p: device path not found\n",
310 __FUNCTION__
, ReportText
, Handle
));
314 Status
= EfiBootManagerUpdateConsoleVariable (ConOut
, DevicePath
, NULL
);
315 if (EFI_ERROR (Status
)) {
316 DEBUG ((EFI_D_ERROR
, "%a: %s: adding to ConOut: %r\n", __FUNCTION__
,
317 ReportText
, Status
));
321 Status
= EfiBootManagerUpdateConsoleVariable (ErrOut
, DevicePath
, NULL
);
322 if (EFI_ERROR (Status
)) {
323 DEBUG ((EFI_D_ERROR
, "%a: %s: adding to ErrOut: %r\n", __FUNCTION__
,
324 ReportText
, Status
));
328 DEBUG ((EFI_D_VERBOSE
, "%a: %s: added to ConOut and ErrOut\n", __FUNCTION__
,
334 PlatformRegisterFvBootOption (
335 CONST EFI_GUID
*FileGuid
,
342 EFI_BOOT_MANAGER_LOAD_OPTION NewOption
;
343 EFI_BOOT_MANAGER_LOAD_OPTION
*BootOptions
;
344 UINTN BootOptionCount
;
345 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode
;
346 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
347 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
349 Status
= gBS
->HandleProtocol (
351 &gEfiLoadedImageProtocolGuid
,
352 (VOID
**) &LoadedImage
354 ASSERT_EFI_ERROR (Status
);
356 EfiInitializeFwVolDevicepathNode (&FileNode
, FileGuid
);
357 DevicePath
= DevicePathFromHandle (LoadedImage
->DeviceHandle
);
358 ASSERT (DevicePath
!= NULL
);
359 DevicePath
= AppendDevicePathNode (
361 (EFI_DEVICE_PATH_PROTOCOL
*) &FileNode
363 ASSERT (DevicePath
!= NULL
);
365 Status
= EfiBootManagerInitializeLoadOption (
367 LoadOptionNumberUnassigned
,
375 ASSERT_EFI_ERROR (Status
);
376 FreePool (DevicePath
);
378 BootOptions
= EfiBootManagerGetLoadOptions (
379 &BootOptionCount
, LoadOptionTypeBoot
382 OptionIndex
= EfiBootManagerFindLoadOption (
383 &NewOption
, BootOptions
, BootOptionCount
386 if (OptionIndex
== -1) {
387 Status
= EfiBootManagerAddLoadOptionVariable (&NewOption
, MAX_UINTN
);
388 ASSERT_EFI_ERROR (Status
);
390 EfiBootManagerFreeLoadOption (&NewOption
);
391 EfiBootManagerFreeLoadOptions (BootOptions
, BootOptionCount
);
402 EFI_BOOT_MANAGER_LOAD_OPTION
*CurrentBootOptions
;
403 EFI_BOOT_MANAGER_LOAD_OPTION
*BootOptions
;
404 EFI_INPUT_KEY
*BootKeys
;
405 PLATFORM_BOOT_MANAGER_PROTOCOL
*PlatformBootManager
;
406 UINTN CurrentBootOptionCount
;
410 Status
= gBS
->LocateProtocol (&gPlatformBootManagerProtocolGuid
, NULL
,
411 (VOID
**)&PlatformBootManager
);
412 if (EFI_ERROR (Status
)) {
415 Status
= PlatformBootManager
->GetPlatformBootOptionsAndKeys (
420 if (EFI_ERROR (Status
)) {
424 // Fetch the existent boot options. If there are none, CurrentBootCount
427 CurrentBootOptions
= EfiBootManagerGetLoadOptions (
428 &CurrentBootOptionCount
,
432 // Process the platform boot options.
434 for (Index
= 0; Index
< BootCount
; Index
++) {
436 UINTN BootOptionNumber
;
439 // If there are any preexistent boot options, and the subject platform boot
440 // option is already among them, then don't try to add it. Just get its
441 // assigned boot option number so we can associate a hotkey with it. Note
442 // that EfiBootManagerFindLoadOption() deals fine with (CurrentBootOptions
443 // == NULL) if (CurrentBootCount == 0).
445 Match
= EfiBootManagerFindLoadOption (
448 CurrentBootOptionCount
451 BootOptionNumber
= CurrentBootOptions
[Match
].OptionNumber
;
454 // Add the platform boot options as a new one, at the end of the boot
455 // order. Note that if the platform provided this boot option with an
456 // unassigned option number, then the below function call will assign a
459 Status
= EfiBootManagerAddLoadOptionVariable (
463 if (EFI_ERROR (Status
)) {
464 DEBUG ((DEBUG_ERROR
, "%a: failed to register \"%s\": %r\n",
465 __FUNCTION__
, BootOptions
[Index
].Description
, Status
));
468 BootOptionNumber
= BootOptions
[Index
].OptionNumber
;
472 // Register a hotkey with the boot option, if requested.
474 if (BootKeys
[Index
].UnicodeChar
== L
'\0') {
478 Status
= EfiBootManagerAddKeyOptionVariable (
485 if (EFI_ERROR (Status
)) {
486 DEBUG ((DEBUG_ERROR
, "%a: failed to register hotkey for \"%s\": %r\n",
487 __FUNCTION__
, BootOptions
[Index
].Description
, Status
));
490 EfiBootManagerFreeLoadOptions (CurrentBootOptions
, CurrentBootOptionCount
);
491 EfiBootManagerFreeLoadOptions (BootOptions
, BootCount
);
497 PlatformRegisterOptionsAndKeys (
505 EFI_BOOT_MANAGER_LOAD_OPTION BootOption
;
507 GetPlatformOptions ();
510 // Register ENTER as CONTINUE key
512 Enter
.ScanCode
= SCAN_NULL
;
513 Enter
.UnicodeChar
= CHAR_CARRIAGE_RETURN
;
514 Status
= EfiBootManagerRegisterContinueKeyOption (0, &Enter
, NULL
);
515 ASSERT_EFI_ERROR (Status
);
518 // Map F2 and ESC to Boot Manager Menu
520 F2
.ScanCode
= SCAN_F2
;
521 F2
.UnicodeChar
= CHAR_NULL
;
522 Esc
.ScanCode
= SCAN_ESC
;
523 Esc
.UnicodeChar
= CHAR_NULL
;
524 Status
= EfiBootManagerGetBootManagerMenu (&BootOption
);
525 ASSERT_EFI_ERROR (Status
);
526 Status
= EfiBootManagerAddKeyOptionVariable (
527 NULL
, (UINT16
) BootOption
.OptionNumber
, 0, &F2
, NULL
529 ASSERT (Status
== EFI_SUCCESS
|| Status
== EFI_ALREADY_STARTED
);
530 Status
= EfiBootManagerAddKeyOptionVariable (
531 NULL
, (UINT16
) BootOption
.OptionNumber
, 0, &Esc
, NULL
533 ASSERT (Status
== EFI_SUCCESS
|| Status
== EFI_ALREADY_STARTED
);
538 // BDS Platform Functions
541 Do the platform init, can be customized by OEM/IBV
542 Possible things that can be done in PlatformBootManagerBeforeConsole:
543 > Update console variable: 1. include hot-plug devices;
544 > 2. Clear ConIn and add SOL for AMT
545 > Register new Driver#### or Boot####
546 > Register new Key####: e.g.: F12
547 > Signal ReadyToLock event
548 > Authentication action: 1. connect Auth devices;
549 > 2. Identify auto logon user.
553 PlatformBootManagerBeforeConsole (
558 // Signal EndOfDxe PI Event
560 EfiEventGroupSignal (&gEfiEndOfDxeEventGroupGuid
);
563 // Locate the PCI root bridges and make the PCI bus driver connect each,
564 // non-recursively. This will produce a number of child handles with PciIo on
567 FilterAndProcess (&gEfiPciRootBridgeIoProtocolGuid
, NULL
, Connect
);
570 // Find all display class PCI devices (using the handles from the previous
571 // step), and connect them non-recursively. This should produce a number of
572 // child handles with GOPs on them.
574 FilterAndProcess (&gEfiPciIoProtocolGuid
, IsPciDisplay
, Connect
);
577 // Now add the device path of all handles with GOP on them to ConOut and
580 FilterAndProcess (&gEfiGraphicsOutputProtocolGuid
, NULL
, AddOutput
);
583 // Add the hardcoded short-form USB keyboard device path to ConIn.
585 EfiBootManagerUpdateConsoleVariable (ConIn
,
586 (EFI_DEVICE_PATH_PROTOCOL
*)&mUsbKeyboard
, NULL
);
589 // Add the hardcoded serial console device path to ConIn, ConOut, ErrOut.
591 ASSERT (FixedPcdGet8 (PcdDefaultTerminalType
) == 4);
592 CopyGuid (&mSerialConsole
.TermType
.Guid
, &gEfiTtyTermGuid
);
594 EfiBootManagerUpdateConsoleVariable (ConIn
,
595 (EFI_DEVICE_PATH_PROTOCOL
*)&mSerialConsole
, NULL
);
596 EfiBootManagerUpdateConsoleVariable (ConOut
,
597 (EFI_DEVICE_PATH_PROTOCOL
*)&mSerialConsole
, NULL
);
598 EfiBootManagerUpdateConsoleVariable (ErrOut
,
599 (EFI_DEVICE_PATH_PROTOCOL
*)&mSerialConsole
, NULL
);
602 // Register platform-specific boot options and keyboard shortcuts.
604 PlatformRegisterOptionsAndKeys ();
613 ESRT_MANAGEMENT_PROTOCOL
*EsrtManagement
;
614 EFI_PEI_HOB_POINTERS HobPointer
;
615 EFI_CAPSULE_HEADER
*CapsuleHeader
;
619 DEBUG ((DEBUG_INFO
, "%a: processing capsules ...\n", __FUNCTION__
));
621 Status
= gBS
->LocateProtocol (&gEsrtManagementProtocolGuid
, NULL
,
622 (VOID
**)&EsrtManagement
);
623 if (!EFI_ERROR (Status
)) {
624 EsrtManagement
->SyncEsrtFmp ();
628 // Find all capsule images from hob
630 HobPointer
.Raw
= GetHobList ();
632 while ((HobPointer
.Raw
= GetNextHob (EFI_HOB_TYPE_UEFI_CAPSULE
,
633 HobPointer
.Raw
)) != NULL
) {
634 CapsuleHeader
= (VOID
*)(UINTN
)HobPointer
.Capsule
->BaseAddress
;
636 Status
= ProcessCapsuleImage (CapsuleHeader
);
637 if (EFI_ERROR (Status
)) {
638 DEBUG ((DEBUG_ERROR
, "%a: failed to process capsule %p - %r\n",
639 __FUNCTION__
, CapsuleHeader
, Status
));
644 HobPointer
.Raw
= GET_NEXT_HOB (HobPointer
);
648 DEBUG ((DEBUG_WARN
, "%a: capsule update successful, resetting ...\n",
651 gRT
->ResetSystem (EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
657 #define VERSION_STRING_PREFIX L"Tianocore/EDK2 firmware version "
660 Do the platform specific action after the console is ready
661 Possible things that can be done in PlatformBootManagerAfterConsole:
662 > Console post action:
663 > Dynamically switch output mode from 100x31 to 80x25 for certain senarino
664 > Signal console ready platform customized event
665 > Run diagnostics like memory testing
666 > Connect certain devices
667 > Dispatch aditional option roms
668 > Special boot: e.g.: USB boot, enter UI
672 PlatformBootManagerAfterConsole (
677 EFI_GRAPHICS_OUTPUT_PROTOCOL
*GraphicsOutput
;
678 UINTN FirmwareVerLength
;
682 FirmwareVerLength
= StrLen (PcdGetPtr (PcdFirmwareVersionString
));
685 // Show the splash screen.
687 Status
= BootLogoEnableLogo ();
688 if (EFI_ERROR (Status
)) {
689 if (FirmwareVerLength
> 0) {
690 Print (VERSION_STRING_PREFIX L
"%s\n",
691 PcdGetPtr (PcdFirmwareVersionString
));
693 Print (L
"Press ESCAPE for boot options ");
694 } else if (FirmwareVerLength
> 0) {
695 Status
= gBS
->HandleProtocol (gST
->ConsoleOutHandle
,
696 &gEfiGraphicsOutputProtocolGuid
, (VOID
**)&GraphicsOutput
);
697 if (!EFI_ERROR (Status
)) {
698 PosX
= (GraphicsOutput
->Mode
->Info
->HorizontalResolution
-
699 (StrLen (VERSION_STRING_PREFIX
) + FirmwareVerLength
) *
700 EFI_GLYPH_WIDTH
) / 2;
703 PrintXY (PosX
, PosY
, NULL
, NULL
, VERSION_STRING_PREFIX L
"%s",
704 PcdGetPtr (PcdFirmwareVersionString
));
709 // Connect the rest of the devices.
711 EfiBootManagerConnectAll ();
714 // On ARM, there is currently no reason to use the phased capsule
715 // update approach where some capsules are dispatched before EndOfDxe
716 // and some are dispatched after. So just handle all capsules here,
717 // when the console is up and we can actually give the user some
718 // feedback about what is going on.
723 // Enumerate all possible boot options.
725 EfiBootManagerRefreshAllBootOption ();
728 // Register UEFI Shell
730 PlatformRegisterFvBootOption (
731 &gUefiShellFileGuid
, L
"UEFI Shell", LOAD_OPTION_ACTIVE
736 This function is called each second during the boot manager waits the
739 @param TimeoutRemain The remaining timeout.
743 PlatformBootManagerWaitCallback (
747 EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Black
;
748 EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION White
;
752 Timeout
= PcdGet16 (PcdPlatformBootTimeOut
);
754 Black
.Raw
= 0x00000000;
755 White
.Raw
= 0x00FFFFFF;
757 Status
= BootLogoUpdateProgress (
760 L
"Press ESCAPE for boot options",
762 (Timeout
- TimeoutRemain
) * 100 / Timeout
,
765 if (EFI_ERROR (Status
)) {
771 The function is called when no boot option could be launched,
772 including platform recovery options and options pointing to applications
773 built into firmware volumes.
775 If this function returns, BDS attempts to enter an infinite loop.
779 PlatformBootManagerUnableToBoot (