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 - 2016, 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 <Protocol/DevicePath.h>
28 #include <Protocol/EsrtManagement.h>
29 #include <Protocol/GraphicsOutput.h>
30 #include <Protocol/LoadedImage.h>
31 #include <Protocol/PciIo.h>
32 #include <Protocol/PciRootBridgeIo.h>
33 #include <Protocol/PlatformBootManager.h>
34 #include <Guid/EventGroup.h>
35 #include <Guid/TtyTerm.h>
37 #include "PlatformBm.h"
39 #define DP_NODE_LEN(Type) { (UINT8)sizeof (Type), (UINT8)(sizeof (Type) >> 8) }
43 VENDOR_DEVICE_PATH SerialDxe
;
44 UART_DEVICE_PATH Uart
;
45 VENDOR_DEFINED_DEVICE_PATH TermType
;
46 EFI_DEVICE_PATH_PROTOCOL End
;
47 } PLATFORM_SERIAL_CONSOLE
;
50 #define SERIAL_DXE_FILE_GUID { \
51 0xD3987D4B, 0x971A, 0x435F, \
52 { 0x8C, 0xAF, 0x49, 0x67, 0xEB, 0x62, 0x72, 0x41 } \
55 STATIC PLATFORM_SERIAL_CONSOLE mSerialConsole
= {
57 // VENDOR_DEVICE_PATH SerialDxe
60 { HARDWARE_DEVICE_PATH
, HW_VENDOR_DP
, DP_NODE_LEN (VENDOR_DEVICE_PATH
) },
65 // UART_DEVICE_PATH Uart
68 { MESSAGING_DEVICE_PATH
, MSG_UART_DP
, DP_NODE_LEN (UART_DEVICE_PATH
) },
70 FixedPcdGet64 (PcdUartDefaultBaudRate
), // BaudRate
71 FixedPcdGet8 (PcdUartDefaultDataBits
), // DataBits
72 FixedPcdGet8 (PcdUartDefaultParity
), // Parity
73 FixedPcdGet8 (PcdUartDefaultStopBits
) // StopBits
77 // VENDOR_DEFINED_DEVICE_PATH TermType
81 MESSAGING_DEVICE_PATH
, MSG_VENDOR_DP
,
82 DP_NODE_LEN (VENDOR_DEFINED_DEVICE_PATH
)
85 // Guid to be filled in dynamically
90 // EFI_DEVICE_PATH_PROTOCOL End
93 END_DEVICE_PATH_TYPE
, END_ENTIRE_DEVICE_PATH_SUBTYPE
,
94 DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL
)
101 USB_CLASS_DEVICE_PATH Keyboard
;
102 EFI_DEVICE_PATH_PROTOCOL End
;
103 } PLATFORM_USB_KEYBOARD
;
106 STATIC PLATFORM_USB_KEYBOARD mUsbKeyboard
= {
108 // USB_CLASS_DEVICE_PATH Keyboard
112 MESSAGING_DEVICE_PATH
, MSG_USB_CLASS_DP
,
113 DP_NODE_LEN (USB_CLASS_DEVICE_PATH
)
115 0xFFFF, // VendorId: any
116 0xFFFF, // ProductId: any
117 3, // DeviceClass: HID
118 1, // DeviceSubClass: boot
119 1 // DeviceProtocol: keyboard
123 // EFI_DEVICE_PATH_PROTOCOL End
126 END_DEVICE_PATH_TYPE
, END_ENTIRE_DEVICE_PATH_SUBTYPE
,
127 DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL
)
133 Check if the handle satisfies a particular condition.
135 @param[in] Handle The handle to check.
136 @param[in] ReportText A caller-allocated string passed in for reporting
137 purposes. It must never be NULL.
139 @retval TRUE The condition is satisfied.
140 @retval FALSE Otherwise. This includes the case when the condition could not
141 be fully evaluated due to an error.
145 (EFIAPI
*FILTER_FUNCTION
) (
146 IN EFI_HANDLE Handle
,
147 IN CONST CHAR16
*ReportText
154 @param[in] Handle The handle to process.
155 @param[in] ReportText A caller-allocated string passed in for reporting
156 purposes. It must never be NULL.
160 (EFIAPI
*CALLBACK_FUNCTION
) (
161 IN EFI_HANDLE Handle
,
162 IN CONST CHAR16
*ReportText
166 Locate all handles that carry the specified protocol, filter them with a
167 callback function, and pass each handle that passes the filter to another
170 @param[in] ProtocolGuid The protocol to look for.
172 @param[in] Filter The filter function to pass each handle to. If this
173 parameter is NULL, then all handles are processed.
175 @param[in] Process The callback function to pass each handle to that
181 IN EFI_GUID
*ProtocolGuid
,
182 IN FILTER_FUNCTION Filter OPTIONAL
,
183 IN CALLBACK_FUNCTION Process
191 Status
= gBS
->LocateHandleBuffer (ByProtocol
, ProtocolGuid
,
192 NULL
/* SearchKey */, &NoHandles
, &Handles
);
193 if (EFI_ERROR (Status
)) {
195 // This is not an error, just an informative condition.
197 DEBUG ((EFI_D_VERBOSE
, "%a: %g: %r\n", __FUNCTION__
, ProtocolGuid
,
202 ASSERT (NoHandles
> 0);
203 for (Idx
= 0; Idx
< NoHandles
; ++Idx
) {
204 CHAR16
*DevicePathText
;
205 STATIC CHAR16 Fallback
[] = L
"<device path unavailable>";
208 // The ConvertDevicePathToText() function handles NULL input transparently.
210 DevicePathText
= ConvertDevicePathToText (
211 DevicePathFromHandle (Handles
[Idx
]),
212 FALSE
, // DisplayOnly
213 FALSE
// AllowShortcuts
215 if (DevicePathText
== NULL
) {
216 DevicePathText
= Fallback
;
219 if (Filter
== NULL
|| Filter (Handles
[Idx
], DevicePathText
)) {
220 Process (Handles
[Idx
], DevicePathText
);
223 if (DevicePathText
!= Fallback
) {
224 FreePool (DevicePathText
);
227 gBS
->FreePool (Handles
);
232 This FILTER_FUNCTION checks if a handle corresponds to a PCI display device.
238 IN EFI_HANDLE Handle
,
239 IN CONST CHAR16
*ReportText
243 EFI_PCI_IO_PROTOCOL
*PciIo
;
246 Status
= gBS
->HandleProtocol (Handle
, &gEfiPciIoProtocolGuid
,
248 if (EFI_ERROR (Status
)) {
250 // This is not an error worth reporting.
255 Status
= PciIo
->Pci
.Read (PciIo
, EfiPciIoWidthUint32
, 0 /* Offset */,
256 sizeof Pci
/ sizeof (UINT32
), &Pci
);
257 if (EFI_ERROR (Status
)) {
258 DEBUG ((EFI_D_ERROR
, "%a: %s: %r\n", __FUNCTION__
, ReportText
, Status
));
262 return IS_PCI_DISPLAY (&Pci
);
267 This CALLBACK_FUNCTION attempts to connect a handle non-recursively, asking
268 the matching driver to produce all first-level child handles.
274 IN EFI_HANDLE Handle
,
275 IN CONST CHAR16
*ReportText
280 Status
= gBS
->ConnectController (
281 Handle
, // ControllerHandle
282 NULL
, // DriverImageHandle
283 NULL
, // RemainingDevicePath -- produce all children
286 DEBUG ((EFI_ERROR (Status
) ? EFI_D_ERROR
: EFI_D_VERBOSE
, "%a: %s: %r\n",
287 __FUNCTION__
, ReportText
, Status
));
292 This CALLBACK_FUNCTION retrieves the EFI_DEVICE_PATH_PROTOCOL from the
293 handle, and adds it to ConOut and ErrOut.
299 IN EFI_HANDLE Handle
,
300 IN CONST CHAR16
*ReportText
304 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
306 DevicePath
= DevicePathFromHandle (Handle
);
307 if (DevicePath
== NULL
) {
308 DEBUG ((EFI_D_ERROR
, "%a: %s: handle %p: device path not found\n",
309 __FUNCTION__
, ReportText
, Handle
));
313 Status
= EfiBootManagerUpdateConsoleVariable (ConOut
, DevicePath
, NULL
);
314 if (EFI_ERROR (Status
)) {
315 DEBUG ((EFI_D_ERROR
, "%a: %s: adding to ConOut: %r\n", __FUNCTION__
,
316 ReportText
, Status
));
320 Status
= EfiBootManagerUpdateConsoleVariable (ErrOut
, DevicePath
, NULL
);
321 if (EFI_ERROR (Status
)) {
322 DEBUG ((EFI_D_ERROR
, "%a: %s: adding to ErrOut: %r\n", __FUNCTION__
,
323 ReportText
, Status
));
327 DEBUG ((EFI_D_VERBOSE
, "%a: %s: added to ConOut and ErrOut\n", __FUNCTION__
,
333 PlatformRegisterFvBootOption (
334 CONST EFI_GUID
*FileGuid
,
341 EFI_BOOT_MANAGER_LOAD_OPTION NewOption
;
342 EFI_BOOT_MANAGER_LOAD_OPTION
*BootOptions
;
343 UINTN BootOptionCount
;
344 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode
;
345 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
346 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
348 Status
= gBS
->HandleProtocol (
350 &gEfiLoadedImageProtocolGuid
,
351 (VOID
**) &LoadedImage
353 ASSERT_EFI_ERROR (Status
);
355 EfiInitializeFwVolDevicepathNode (&FileNode
, FileGuid
);
356 DevicePath
= DevicePathFromHandle (LoadedImage
->DeviceHandle
);
357 ASSERT (DevicePath
!= NULL
);
358 DevicePath
= AppendDevicePathNode (
360 (EFI_DEVICE_PATH_PROTOCOL
*) &FileNode
362 ASSERT (DevicePath
!= NULL
);
364 Status
= EfiBootManagerInitializeLoadOption (
366 LoadOptionNumberUnassigned
,
374 ASSERT_EFI_ERROR (Status
);
375 FreePool (DevicePath
);
377 BootOptions
= EfiBootManagerGetLoadOptions (
378 &BootOptionCount
, LoadOptionTypeBoot
381 OptionIndex
= EfiBootManagerFindLoadOption (
382 &NewOption
, BootOptions
, BootOptionCount
385 if (OptionIndex
== -1) {
386 Status
= EfiBootManagerAddLoadOptionVariable (&NewOption
, MAX_UINTN
);
387 ASSERT_EFI_ERROR (Status
);
389 EfiBootManagerFreeLoadOption (&NewOption
);
390 EfiBootManagerFreeLoadOptions (BootOptions
, BootOptionCount
);
401 EFI_BOOT_MANAGER_LOAD_OPTION
*CurrentBootOptions
;
402 EFI_BOOT_MANAGER_LOAD_OPTION
*BootOptions
;
403 EFI_INPUT_KEY
*BootKeys
;
404 PLATFORM_BOOT_MANAGER_PROTOCOL
*PlatformBootManager
;
405 UINTN CurrentBootOptionCount
;
409 Status
= gBS
->LocateProtocol (&gPlatformBootManagerProtocolGuid
, NULL
,
410 (VOID
**)&PlatformBootManager
);
411 if (EFI_ERROR (Status
)) {
414 Status
= PlatformBootManager
->GetPlatformBootOptionsAndKeys (
419 if (EFI_ERROR (Status
)) {
423 // Fetch the existent boot options. If there are none, CurrentBootCount
426 CurrentBootOptions
= EfiBootManagerGetLoadOptions (
427 &CurrentBootOptionCount
,
431 // Process the platform boot options.
433 for (Index
= 0; Index
< BootCount
; Index
++) {
435 UINTN BootOptionNumber
;
438 // If there are any preexistent boot options, and the subject platform boot
439 // option is already among them, then don't try to add it. Just get its
440 // assigned boot option number so we can associate a hotkey with it. Note
441 // that EfiBootManagerFindLoadOption() deals fine with (CurrentBootOptions
442 // == NULL) if (CurrentBootCount == 0).
444 Match
= EfiBootManagerFindLoadOption (
447 CurrentBootOptionCount
450 BootOptionNumber
= CurrentBootOptions
[Match
].OptionNumber
;
453 // Add the platform boot options as a new one, at the end of the boot
454 // order. Note that if the platform provided this boot option with an
455 // unassigned option number, then the below function call will assign a
458 Status
= EfiBootManagerAddLoadOptionVariable (
462 if (EFI_ERROR (Status
)) {
463 DEBUG ((DEBUG_ERROR
, "%a: failed to register \"%s\": %r\n",
464 __FUNCTION__
, BootOptions
[Index
].Description
, Status
));
467 BootOptionNumber
= BootOptions
[Index
].OptionNumber
;
471 // Register a hotkey with the boot option, if requested.
473 if (BootKeys
[Index
].UnicodeChar
== L
'\0') {
477 Status
= EfiBootManagerAddKeyOptionVariable (
484 if (EFI_ERROR (Status
)) {
485 DEBUG ((DEBUG_ERROR
, "%a: failed to register hotkey for \"%s\": %r\n",
486 __FUNCTION__
, BootOptions
[Index
].Description
, Status
));
489 EfiBootManagerFreeLoadOptions (CurrentBootOptions
, CurrentBootOptionCount
);
490 EfiBootManagerFreeLoadOptions (BootOptions
, BootCount
);
496 PlatformRegisterOptionsAndKeys (
504 EFI_BOOT_MANAGER_LOAD_OPTION BootOption
;
506 GetPlatformOptions ();
509 // Register ENTER as CONTINUE key
511 Enter
.ScanCode
= SCAN_NULL
;
512 Enter
.UnicodeChar
= CHAR_CARRIAGE_RETURN
;
513 Status
= EfiBootManagerRegisterContinueKeyOption (0, &Enter
, NULL
);
514 ASSERT_EFI_ERROR (Status
);
517 // Map F2 and ESC to Boot Manager Menu
519 F2
.ScanCode
= SCAN_F2
;
520 F2
.UnicodeChar
= CHAR_NULL
;
521 Esc
.ScanCode
= SCAN_ESC
;
522 Esc
.UnicodeChar
= CHAR_NULL
;
523 Status
= EfiBootManagerGetBootManagerMenu (&BootOption
);
524 ASSERT_EFI_ERROR (Status
);
525 Status
= EfiBootManagerAddKeyOptionVariable (
526 NULL
, (UINT16
) BootOption
.OptionNumber
, 0, &F2
, NULL
528 ASSERT (Status
== EFI_SUCCESS
|| Status
== EFI_ALREADY_STARTED
);
529 Status
= EfiBootManagerAddKeyOptionVariable (
530 NULL
, (UINT16
) BootOption
.OptionNumber
, 0, &Esc
, NULL
532 ASSERT (Status
== EFI_SUCCESS
|| Status
== EFI_ALREADY_STARTED
);
537 // BDS Platform Functions
540 Do the platform init, can be customized by OEM/IBV
541 Possible things that can be done in PlatformBootManagerBeforeConsole:
542 > Update console variable: 1. include hot-plug devices;
543 > 2. Clear ConIn and add SOL for AMT
544 > Register new Driver#### or Boot####
545 > Register new Key####: e.g.: F12
546 > Signal ReadyToLock event
547 > Authentication action: 1. connect Auth devices;
548 > 2. Identify auto logon user.
552 PlatformBootManagerBeforeConsole (
557 ESRT_MANAGEMENT_PROTOCOL
*EsrtManagement
;
559 if (GetBootModeHob() == BOOT_ON_FLASH_UPDATE
) {
560 DEBUG ((DEBUG_INFO
, "ProcessCapsules Before EndOfDxe ......\n"));
561 Status
= ProcessCapsules ();
562 DEBUG ((DEBUG_INFO
, "ProcessCapsules returned %r\n", Status
));
564 Status
= gBS
->LocateProtocol (&gEsrtManagementProtocolGuid
, NULL
,
565 (VOID
**)&EsrtManagement
);
566 if (!EFI_ERROR (Status
)) {
567 EsrtManagement
->SyncEsrtFmp ();
572 // Signal EndOfDxe PI Event
574 EfiEventGroupSignal (&gEfiEndOfDxeEventGroupGuid
);
577 // Locate the PCI root bridges and make the PCI bus driver connect each,
578 // non-recursively. This will produce a number of child handles with PciIo on
581 FilterAndProcess (&gEfiPciRootBridgeIoProtocolGuid
, NULL
, Connect
);
584 // Find all display class PCI devices (using the handles from the previous
585 // step), and connect them non-recursively. This should produce a number of
586 // child handles with GOPs on them.
588 FilterAndProcess (&gEfiPciIoProtocolGuid
, IsPciDisplay
, Connect
);
591 // Now add the device path of all handles with GOP on them to ConOut and
594 FilterAndProcess (&gEfiGraphicsOutputProtocolGuid
, NULL
, AddOutput
);
597 // Add the hardcoded short-form USB keyboard device path to ConIn.
599 EfiBootManagerUpdateConsoleVariable (ConIn
,
600 (EFI_DEVICE_PATH_PROTOCOL
*)&mUsbKeyboard
, NULL
);
603 // Add the hardcoded serial console device path to ConIn, ConOut, ErrOut.
605 ASSERT (FixedPcdGet8 (PcdDefaultTerminalType
) == 4);
606 CopyGuid (&mSerialConsole
.TermType
.Guid
, &gEfiTtyTermGuid
);
608 EfiBootManagerUpdateConsoleVariable (ConIn
,
609 (EFI_DEVICE_PATH_PROTOCOL
*)&mSerialConsole
, NULL
);
610 EfiBootManagerUpdateConsoleVariable (ConOut
,
611 (EFI_DEVICE_PATH_PROTOCOL
*)&mSerialConsole
, NULL
);
612 EfiBootManagerUpdateConsoleVariable (ErrOut
,
613 (EFI_DEVICE_PATH_PROTOCOL
*)&mSerialConsole
, NULL
);
616 // Register platform-specific boot options and keyboard shortcuts.
618 PlatformRegisterOptionsAndKeys ();
621 #define VERSION_STRING_PREFIX L"Tianocore/EDK2 firmware version "
624 Do the platform specific action after the console is ready
625 Possible things that can be done in PlatformBootManagerAfterConsole:
626 > Console post action:
627 > Dynamically switch output mode from 100x31 to 80x25 for certain senarino
628 > Signal console ready platform customized event
629 > Run diagnostics like memory testing
630 > Connect certain devices
631 > Dispatch aditional option roms
632 > Special boot: e.g.: USB boot, enter UI
636 PlatformBootManagerAfterConsole (
640 ESRT_MANAGEMENT_PROTOCOL
*EsrtManagement
;
642 EFI_GRAPHICS_OUTPUT_PROTOCOL
*GraphicsOutput
;
643 UINTN FirmwareVerLength
;
647 FirmwareVerLength
= StrLen (PcdGetPtr (PcdFirmwareVersionString
));
650 // Show the splash screen.
652 Status
= BootLogoEnableLogo ();
653 if (EFI_ERROR (Status
)) {
654 if (FirmwareVerLength
> 0) {
655 Print (VERSION_STRING_PREFIX L
"%s\n",
656 PcdGetPtr (PcdFirmwareVersionString
));
658 Print (L
"Press ESCAPE for boot options ");
659 } else if (FirmwareVerLength
> 0) {
660 Status
= gBS
->HandleProtocol (gST
->ConsoleOutHandle
,
661 &gEfiGraphicsOutputProtocolGuid
, (VOID
**)&GraphicsOutput
);
662 if (!EFI_ERROR (Status
)) {
663 PosX
= (GraphicsOutput
->Mode
->Info
->HorizontalResolution
-
664 (StrLen (VERSION_STRING_PREFIX
) + FirmwareVerLength
) *
665 EFI_GLYPH_WIDTH
) / 2;
668 PrintXY (PosX
, PosY
, NULL
, NULL
, VERSION_STRING_PREFIX L
"%s",
669 PcdGetPtr (PcdFirmwareVersionString
));
674 // Connect the rest of the devices.
676 EfiBootManagerConnectAll ();
678 Status
= gBS
->LocateProtocol (&gEsrtManagementProtocolGuid
, NULL
,
679 (VOID
**)&EsrtManagement
);
680 if (!EFI_ERROR (Status
)) {
681 EsrtManagement
->SyncEsrtFmp ();
684 if (GetBootModeHob() == BOOT_ON_FLASH_UPDATE
) {
685 DEBUG((DEBUG_INFO
, "ProcessCapsules After EndOfDxe ......\n"));
686 Status
= ProcessCapsules ();
687 DEBUG((DEBUG_INFO
, "ProcessCapsules returned %r\n", Status
));
691 // Enumerate all possible boot options.
693 EfiBootManagerRefreshAllBootOption ();
696 // Register UEFI Shell
698 PlatformRegisterFvBootOption (
699 &gUefiShellFileGuid
, L
"UEFI Shell", LOAD_OPTION_ACTIVE
704 This function is called each second during the boot manager waits the
707 @param TimeoutRemain The remaining timeout.
711 PlatformBootManagerWaitCallback (
715 EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Black
;
716 EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION White
;
720 Timeout
= PcdGet16 (PcdPlatformBootTimeOut
);
722 Black
.Raw
= 0x00000000;
723 White
.Raw
= 0x00FFFFFF;
725 Status
= BootLogoUpdateProgress (
728 L
"Press ESCAPE for boot options",
730 (Timeout
- TimeoutRemain
) * 100 / Timeout
,
733 if (EFI_ERROR (Status
)) {