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>
8 This program and the accompanying materials are licensed and made available
9 under the terms and conditions of the BSD License which accompanies this
10 distribution. The full text of the license may be found at
11 http://opensource.org/licenses/bsd-license.php
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
14 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 #include <IndustryStandard/Pci22.h>
19 #include <Library/BootLogoLib.h>
20 #include <Library/DevicePathLib.h>
21 #include <Library/PcdLib.h>
22 #include <Library/QemuBootOrderLib.h>
23 #include <Library/UefiBootManagerLib.h>
24 #include <Protocol/DevicePath.h>
25 #include <Protocol/FirmwareVolume2.h>
26 #include <Protocol/GraphicsOutput.h>
27 #include <Protocol/LoadedImage.h>
28 #include <Protocol/PciIo.h>
29 #include <Protocol/PciRootBridgeIo.h>
30 #include <Guid/EventGroup.h>
31 #include <Guid/RootBridgesConnectedEventGroup.h>
33 #include "PlatformBm.h"
35 #define DP_NODE_LEN(Type) { (UINT8)sizeof (Type), (UINT8)(sizeof (Type) >> 8) }
40 VENDOR_DEVICE_PATH SerialDxe
;
41 UART_DEVICE_PATH Uart
;
42 VENDOR_DEFINED_DEVICE_PATH TermType
;
43 EFI_DEVICE_PATH_PROTOCOL End
;
44 } PLATFORM_SERIAL_CONSOLE
;
47 #define SERIAL_DXE_FILE_GUID { \
48 0xD3987D4B, 0x971A, 0x435F, \
49 { 0x8C, 0xAF, 0x49, 0x67, 0xEB, 0x62, 0x72, 0x41 } \
52 STATIC PLATFORM_SERIAL_CONSOLE mSerialConsole
= {
54 // VENDOR_DEVICE_PATH SerialDxe
57 { HARDWARE_DEVICE_PATH
, HW_VENDOR_DP
, DP_NODE_LEN (VENDOR_DEVICE_PATH
) },
62 // UART_DEVICE_PATH Uart
65 { MESSAGING_DEVICE_PATH
, MSG_UART_DP
, DP_NODE_LEN (UART_DEVICE_PATH
) },
67 FixedPcdGet64 (PcdUartDefaultBaudRate
), // BaudRate
68 FixedPcdGet8 (PcdUartDefaultDataBits
), // DataBits
69 FixedPcdGet8 (PcdUartDefaultParity
), // Parity
70 FixedPcdGet8 (PcdUartDefaultStopBits
) // StopBits
74 // VENDOR_DEFINED_DEVICE_PATH TermType
78 MESSAGING_DEVICE_PATH
, MSG_VENDOR_DP
,
79 DP_NODE_LEN (VENDOR_DEFINED_DEVICE_PATH
)
82 // Guid to be filled in dynamically
87 // EFI_DEVICE_PATH_PROTOCOL End
90 END_DEVICE_PATH_TYPE
, END_ENTIRE_DEVICE_PATH_SUBTYPE
,
91 DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL
)
98 USB_CLASS_DEVICE_PATH Keyboard
;
99 EFI_DEVICE_PATH_PROTOCOL End
;
100 } PLATFORM_USB_KEYBOARD
;
103 STATIC PLATFORM_USB_KEYBOARD mUsbKeyboard
= {
105 // USB_CLASS_DEVICE_PATH Keyboard
109 MESSAGING_DEVICE_PATH
, MSG_USB_CLASS_DP
,
110 DP_NODE_LEN (USB_CLASS_DEVICE_PATH
)
112 0xFFFF, // VendorId: any
113 0xFFFF, // ProductId: any
114 3, // DeviceClass: HID
115 1, // DeviceSubClass: boot
116 1 // DeviceProtocol: keyboard
120 // EFI_DEVICE_PATH_PROTOCOL End
123 END_DEVICE_PATH_TYPE
, END_ENTIRE_DEVICE_PATH_SUBTYPE
,
124 DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL
)
130 Check if the handle satisfies a particular condition.
132 @param[in] Handle The handle to check.
133 @param[in] ReportText A caller-allocated string passed in for reporting
134 purposes. It must never be NULL.
136 @retval TRUE The condition is satisfied.
137 @retval FALSE Otherwise. This includes the case when the condition could not
138 be fully evaluated due to an error.
142 (EFIAPI
*FILTER_FUNCTION
) (
143 IN EFI_HANDLE Handle
,
144 IN CONST CHAR16
*ReportText
151 @param[in] Handle The handle to process.
152 @param[in] ReportText A caller-allocated string passed in for reporting
153 purposes. It must never be NULL.
157 (EFIAPI
*CALLBACK_FUNCTION
) (
158 IN EFI_HANDLE Handle
,
159 IN CONST CHAR16
*ReportText
163 Locate all handles that carry the specified protocol, filter them with a
164 callback function, and pass each handle that passes the filter to another
167 @param[in] ProtocolGuid The protocol to look for.
169 @param[in] Filter The filter function to pass each handle to. If this
170 parameter is NULL, then all handles are processed.
172 @param[in] Process The callback function to pass each handle to that
178 IN EFI_GUID
*ProtocolGuid
,
179 IN FILTER_FUNCTION Filter OPTIONAL
,
180 IN CALLBACK_FUNCTION Process
188 Status
= gBS
->LocateHandleBuffer (ByProtocol
, ProtocolGuid
,
189 NULL
/* SearchKey */, &NoHandles
, &Handles
);
190 if (EFI_ERROR (Status
)) {
192 // This is not an error, just an informative condition.
194 DEBUG ((EFI_D_VERBOSE
, "%a: %g: %r\n", __FUNCTION__
, ProtocolGuid
,
199 ASSERT (NoHandles
> 0);
200 for (Idx
= 0; Idx
< NoHandles
; ++Idx
) {
201 CHAR16
*DevicePathText
;
202 STATIC CHAR16 Fallback
[] = L
"<device path unavailable>";
205 // The ConvertDevicePathToText() function handles NULL input transparently.
207 DevicePathText
= ConvertDevicePathToText (
208 DevicePathFromHandle (Handles
[Idx
]),
209 FALSE
, // DisplayOnly
210 FALSE
// AllowShortcuts
212 if (DevicePathText
== NULL
) {
213 DevicePathText
= Fallback
;
216 if (Filter
== NULL
|| Filter (Handles
[Idx
], DevicePathText
)) {
217 Process (Handles
[Idx
], DevicePathText
);
220 if (DevicePathText
!= Fallback
) {
221 FreePool (DevicePathText
);
224 gBS
->FreePool (Handles
);
229 This FILTER_FUNCTION checks if a handle corresponds to a PCI display device.
235 IN EFI_HANDLE Handle
,
236 IN CONST CHAR16
*ReportText
240 EFI_PCI_IO_PROTOCOL
*PciIo
;
243 Status
= gBS
->HandleProtocol (Handle
, &gEfiPciIoProtocolGuid
,
245 if (EFI_ERROR (Status
)) {
247 // This is not an error worth reporting.
252 Status
= PciIo
->Pci
.Read (PciIo
, EfiPciIoWidthUint32
, 0 /* Offset */,
253 sizeof Pci
/ sizeof (UINT32
), &Pci
);
254 if (EFI_ERROR (Status
)) {
255 DEBUG ((EFI_D_ERROR
, "%a: %s: %r\n", __FUNCTION__
, ReportText
, Status
));
259 return IS_PCI_DISPLAY (&Pci
);
264 This CALLBACK_FUNCTION attempts to connect a handle non-recursively, asking
265 the matching driver to produce all first-level child handles.
271 IN EFI_HANDLE Handle
,
272 IN CONST CHAR16
*ReportText
277 Status
= gBS
->ConnectController (
278 Handle
, // ControllerHandle
279 NULL
, // DriverImageHandle
280 NULL
, // RemainingDevicePath -- produce all children
283 DEBUG ((EFI_ERROR (Status
) ? EFI_D_ERROR
: EFI_D_VERBOSE
, "%a: %s: %r\n",
284 __FUNCTION__
, ReportText
, Status
));
289 This CALLBACK_FUNCTION retrieves the EFI_DEVICE_PATH_PROTOCOL from the
290 handle, and adds it to ConOut and ErrOut.
296 IN EFI_HANDLE Handle
,
297 IN CONST CHAR16
*ReportText
301 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
303 DevicePath
= DevicePathFromHandle (Handle
);
304 if (DevicePath
== NULL
) {
305 DEBUG ((EFI_D_ERROR
, "%a: %s: handle %p: device path not found\n",
306 __FUNCTION__
, ReportText
, Handle
));
310 Status
= EfiBootManagerUpdateConsoleVariable (ConOut
, DevicePath
, NULL
);
311 if (EFI_ERROR (Status
)) {
312 DEBUG ((EFI_D_ERROR
, "%a: %s: adding to ConOut: %r\n", __FUNCTION__
,
313 ReportText
, Status
));
317 Status
= EfiBootManagerUpdateConsoleVariable (ErrOut
, DevicePath
, NULL
);
318 if (EFI_ERROR (Status
)) {
319 DEBUG ((EFI_D_ERROR
, "%a: %s: adding to ErrOut: %r\n", __FUNCTION__
,
320 ReportText
, Status
));
324 DEBUG ((EFI_D_VERBOSE
, "%a: %s: added to ConOut and ErrOut\n", __FUNCTION__
,
330 PlatformRegisterFvBootOption (
338 EFI_BOOT_MANAGER_LOAD_OPTION NewOption
;
339 EFI_BOOT_MANAGER_LOAD_OPTION
*BootOptions
;
340 UINTN BootOptionCount
;
341 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode
;
342 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
343 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
345 Status
= gBS
->HandleProtocol (
347 &gEfiLoadedImageProtocolGuid
,
348 (VOID
**) &LoadedImage
350 ASSERT_EFI_ERROR (Status
);
352 EfiInitializeFwVolDevicepathNode (&FileNode
, FileGuid
);
353 DevicePath
= DevicePathFromHandle (LoadedImage
->DeviceHandle
);
354 ASSERT (DevicePath
!= NULL
);
355 DevicePath
= AppendDevicePathNode (
357 (EFI_DEVICE_PATH_PROTOCOL
*) &FileNode
359 ASSERT (DevicePath
!= NULL
);
361 Status
= EfiBootManagerInitializeLoadOption (
363 LoadOptionNumberUnassigned
,
371 ASSERT_EFI_ERROR (Status
);
372 FreePool (DevicePath
);
374 BootOptions
= EfiBootManagerGetLoadOptions (
375 &BootOptionCount
, LoadOptionTypeBoot
378 OptionIndex
= EfiBootManagerFindLoadOption (
379 &NewOption
, BootOptions
, BootOptionCount
382 if (OptionIndex
== -1) {
383 Status
= EfiBootManagerAddLoadOptionVariable (&NewOption
, MAX_UINTN
);
384 ASSERT_EFI_ERROR (Status
);
386 EfiBootManagerFreeLoadOption (&NewOption
);
387 EfiBootManagerFreeLoadOptions (BootOptions
, BootOptionCount
);
392 Remove all MemoryMapped(...)/FvFile(...) and Fv(...)/FvFile(...) boot options
393 whose device paths do not resolve exactly to an FvFile in the system.
395 This removes any boot options that point to binaries built into the firmware
396 and have become stale due to any of the following:
397 - FvMain's base address or size changed (historical),
398 - FvMain's FvNameGuid changed,
399 - the FILE_GUID of the pointed-to binary changed,
400 - the referenced binary is no longer built into the firmware.
402 EfiBootManagerFindLoadOption() used in PlatformRegisterFvBootOption() only
403 avoids exact duplicates.
407 RemoveStaleFvFileOptions (
411 EFI_BOOT_MANAGER_LOAD_OPTION
*BootOptions
;
412 UINTN BootOptionCount
;
415 BootOptions
= EfiBootManagerGetLoadOptions (&BootOptionCount
,
418 for (Index
= 0; Index
< BootOptionCount
; ++Index
) {
419 EFI_DEVICE_PATH_PROTOCOL
*Node1
, *Node2
, *SearchNode
;
424 // If the device path starts with neither MemoryMapped(...) nor Fv(...),
425 // then keep the boot option.
427 Node1
= BootOptions
[Index
].FilePath
;
428 if (!(DevicePathType (Node1
) == HARDWARE_DEVICE_PATH
&&
429 DevicePathSubType (Node1
) == HW_MEMMAP_DP
) &&
430 !(DevicePathType (Node1
) == MEDIA_DEVICE_PATH
&&
431 DevicePathSubType (Node1
) == MEDIA_PIWG_FW_VOL_DP
)) {
436 // If the second device path node is not FvFile(...), then keep the boot
439 Node2
= NextDevicePathNode (Node1
);
440 if (DevicePathType (Node2
) != MEDIA_DEVICE_PATH
||
441 DevicePathSubType (Node2
) != MEDIA_PIWG_FW_FILE_DP
) {
446 // Locate the Firmware Volume2 protocol instance that is denoted by the
447 // boot option. If this lookup fails (i.e., the boot option references a
448 // firmware volume that doesn't exist), then we'll proceed to delete the
452 Status
= gBS
->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid
,
453 &SearchNode
, &FvHandle
);
455 if (!EFI_ERROR (Status
)) {
457 // The firmware volume was found; now let's see if it contains the FvFile
458 // identified by GUID.
460 EFI_FIRMWARE_VOLUME2_PROTOCOL
*FvProtocol
;
461 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH
*FvFileNode
;
463 EFI_FV_FILETYPE FoundType
;
464 EFI_FV_FILE_ATTRIBUTES FileAttributes
;
465 UINT32 AuthenticationStatus
;
467 Status
= gBS
->HandleProtocol (FvHandle
, &gEfiFirmwareVolume2ProtocolGuid
,
468 (VOID
**)&FvProtocol
);
469 ASSERT_EFI_ERROR (Status
);
471 FvFileNode
= (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH
*)Node2
;
473 // Buffer==NULL means we request metadata only: BufferSize, FoundType,
476 Status
= FvProtocol
->ReadFile (
478 &FvFileNode
->FvFileName
, // NameGuid
483 &AuthenticationStatus
485 if (!EFI_ERROR (Status
)) {
487 // The FvFile was found. Keep the boot option.
494 // Delete the boot option.
496 Status
= EfiBootManagerDeleteLoadOptionVariable (
497 BootOptions
[Index
].OptionNumber
, LoadOptionTypeBoot
);
499 CHAR16
*DevicePathString
;
501 DevicePathString
= ConvertDevicePathToText(BootOptions
[Index
].FilePath
,
504 EFI_ERROR (Status
) ? EFI_D_WARN
: EFI_D_VERBOSE
,
505 "%a: removing stale Boot#%04x %s: %r\n",
507 (UINT32
)BootOptions
[Index
].OptionNumber
,
508 DevicePathString
== NULL
? L
"<unavailable>" : DevicePathString
,
511 if (DevicePathString
!= NULL
) {
512 FreePool (DevicePathString
);
517 EfiBootManagerFreeLoadOptions (BootOptions
, BootOptionCount
);
523 PlatformRegisterOptionsAndKeys (
531 EFI_BOOT_MANAGER_LOAD_OPTION BootOption
;
534 // Register ENTER as CONTINUE key
536 Enter
.ScanCode
= SCAN_NULL
;
537 Enter
.UnicodeChar
= CHAR_CARRIAGE_RETURN
;
538 Status
= EfiBootManagerRegisterContinueKeyOption (0, &Enter
, NULL
);
539 ASSERT_EFI_ERROR (Status
);
542 // Map F2 and ESC to Boot Manager Menu
544 F2
.ScanCode
= SCAN_F2
;
545 F2
.UnicodeChar
= CHAR_NULL
;
546 Esc
.ScanCode
= SCAN_ESC
;
547 Esc
.UnicodeChar
= CHAR_NULL
;
548 Status
= EfiBootManagerGetBootManagerMenu (&BootOption
);
549 ASSERT_EFI_ERROR (Status
);
550 Status
= EfiBootManagerAddKeyOptionVariable (
551 NULL
, (UINT16
) BootOption
.OptionNumber
, 0, &F2
, NULL
553 ASSERT (Status
== EFI_SUCCESS
|| Status
== EFI_ALREADY_STARTED
);
554 Status
= EfiBootManagerAddKeyOptionVariable (
555 NULL
, (UINT16
) BootOption
.OptionNumber
, 0, &Esc
, NULL
557 ASSERT (Status
== EFI_SUCCESS
|| Status
== EFI_ALREADY_STARTED
);
562 // BDS Platform Functions
565 Do the platform init, can be customized by OEM/IBV
566 Possible things that can be done in PlatformBootManagerBeforeConsole:
567 > Update console variable: 1. include hot-plug devices;
568 > 2. Clear ConIn and add SOL for AMT
569 > Register new Driver#### or Boot####
570 > Register new Key####: e.g.: F12
571 > Signal ReadyToLock event
572 > Authentication action: 1. connect Auth devices;
573 > 2. Identify auto logon user.
577 PlatformBootManagerBeforeConsole (
581 RETURN_STATUS PcdStatus
;
584 // Signal EndOfDxe PI Event
586 EfiEventGroupSignal (&gEfiEndOfDxeEventGroupGuid
);
589 // Dispatch deferred images after EndOfDxe event.
591 EfiBootManagerDispatchDeferredImages ();
594 // Locate the PCI root bridges and make the PCI bus driver connect each,
595 // non-recursively. This will produce a number of child handles with PciIo on
598 FilterAndProcess (&gEfiPciRootBridgeIoProtocolGuid
, NULL
, Connect
);
601 // Signal the ACPI platform driver that it can download QEMU ACPI tables.
603 EfiEventGroupSignal (&gRootBridgesConnectedEventGroupGuid
);
606 // Find all display class PCI devices (using the handles from the previous
607 // step), and connect them non-recursively. This should produce a number of
608 // child handles with GOPs on them.
610 FilterAndProcess (&gEfiPciIoProtocolGuid
, IsPciDisplay
, Connect
);
613 // Now add the device path of all handles with GOP on them to ConOut and
616 FilterAndProcess (&gEfiGraphicsOutputProtocolGuid
, NULL
, AddOutput
);
619 // Add the hardcoded short-form USB keyboard device path to ConIn.
621 EfiBootManagerUpdateConsoleVariable (ConIn
,
622 (EFI_DEVICE_PATH_PROTOCOL
*)&mUsbKeyboard
, NULL
);
625 // Add the hardcoded serial console device path to ConIn, ConOut, ErrOut.
627 CopyGuid (&mSerialConsole
.TermType
.Guid
,
628 PcdGetPtr (PcdTerminalTypeGuidBuffer
));
629 EfiBootManagerUpdateConsoleVariable (ConIn
,
630 (EFI_DEVICE_PATH_PROTOCOL
*)&mSerialConsole
, NULL
);
631 EfiBootManagerUpdateConsoleVariable (ConOut
,
632 (EFI_DEVICE_PATH_PROTOCOL
*)&mSerialConsole
, NULL
);
633 EfiBootManagerUpdateConsoleVariable (ErrOut
,
634 (EFI_DEVICE_PATH_PROTOCOL
*)&mSerialConsole
, NULL
);
637 // Set the front page timeout from the QEMU configuration.
639 PcdStatus
= PcdSet16S (PcdPlatformBootTimeOut
,
640 GetFrontPageTimeoutFromQemu ());
641 ASSERT_RETURN_ERROR (PcdStatus
);
644 // Register platform-specific boot options and keyboard shortcuts.
646 PlatformRegisterOptionsAndKeys ();
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 senarino
654 > Signal console ready platform customized event
655 > Run diagnostics like memory testing
656 > Connect certain devices
657 > Dispatch aditional option roms
658 > Special boot: e.g.: USB boot, enter UI
662 PlatformBootManagerAfterConsole (
666 RETURN_STATUS Status
;
669 // Show the splash screen.
671 BootLogoEnableLogo ();
674 // Process QEMU's -kernel command line option. The kernel booted this way
675 // will receive ACPI tables: in PlatformBootManagerBeforeConsole(), we
676 // connected any and all PCI root bridges, and then signaled the ACPI
679 TryRunningQemuKernel ();
682 // Connect the purported boot devices.
684 Status
= ConnectDevicesFromQemu ();
685 if (RETURN_ERROR (Status
)) {
687 // Connect the rest of the devices.
689 EfiBootManagerConnectAll ();
693 // Enumerate all possible boot options, then filter and reorder them based on
694 // the QEMU configuration.
696 EfiBootManagerRefreshAllBootOption ();
699 // Register UEFI Shell
701 PlatformRegisterFvBootOption (
702 &gUefiShellFileGuid
, L
"EFI Internal Shell", LOAD_OPTION_ACTIVE
705 RemoveStaleFvFileOptions ();
706 SetBootOrderFromQemu ();
710 This function is called each second during the boot manager waits the
713 @param TimeoutRemain The remaining timeout.
717 PlatformBootManagerWaitCallback (
721 EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Black
;
722 EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION White
;
725 Timeout
= PcdGet16 (PcdPlatformBootTimeOut
);
727 Black
.Raw
= 0x00000000;
728 White
.Raw
= 0x00FFFFFF;
730 BootLogoUpdateProgress (
733 L
"Start boot option",
735 (Timeout
- TimeoutRemain
) * 100 / Timeout
,