/** @file | |
Implementation for PlatformBdsLib library class interfaces. | |
Copyright (C) 2015, Red Hat, Inc. | |
Copyright (c) 2014, ARM Ltd. All rights reserved.<BR> | |
Copyright (c) 2004 - 2008, Intel Corporation. All rights reserved.<BR> | |
This program and the accompanying materials are licensed and made available | |
under the terms and conditions of the BSD License which accompanies this | |
distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT | |
WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
**/ | |
#include <IndustryStandard/Pci22.h> | |
#include <Library/DevicePathLib.h> | |
#include <Library/PcdLib.h> | |
#include <Library/PlatformBdsLib.h> | |
#include <Library/QemuBootOrderLib.h> | |
#include <Protocol/DevicePath.h> | |
#include <Protocol/GraphicsOutput.h> | |
#include <Protocol/PciIo.h> | |
#include <Protocol/PciRootBridgeIo.h> | |
#include <Guid/EventGroup.h> | |
#include "IntelBdsPlatform.h" | |
#define DP_NODE_LEN(Type) { (UINT8)sizeof (Type), (UINT8)(sizeof (Type) >> 8) } | |
#pragma pack (1) | |
typedef struct { | |
VENDOR_DEVICE_PATH SerialDxe; | |
UART_DEVICE_PATH Uart; | |
VENDOR_DEFINED_DEVICE_PATH TermType; | |
EFI_DEVICE_PATH_PROTOCOL End; | |
} PLATFORM_SERIAL_CONSOLE; | |
#pragma pack () | |
#define SERIAL_DXE_FILE_GUID { \ | |
0xD3987D4B, 0x971A, 0x435F, \ | |
{ 0x8C, 0xAF, 0x49, 0x67, 0xEB, 0x62, 0x72, 0x41 } \ | |
} | |
STATIC PLATFORM_SERIAL_CONSOLE mSerialConsole = { | |
// | |
// VENDOR_DEVICE_PATH SerialDxe | |
// | |
{ | |
{ HARDWARE_DEVICE_PATH, HW_VENDOR_DP, DP_NODE_LEN (VENDOR_DEVICE_PATH) }, | |
SERIAL_DXE_FILE_GUID | |
}, | |
// | |
// UART_DEVICE_PATH Uart | |
// | |
{ | |
{ MESSAGING_DEVICE_PATH, MSG_UART_DP, DP_NODE_LEN (UART_DEVICE_PATH) }, | |
0, // Reserved | |
FixedPcdGet64 (PcdUartDefaultBaudRate), // BaudRate | |
FixedPcdGet8 (PcdUartDefaultDataBits), // DataBits | |
FixedPcdGet8 (PcdUartDefaultParity), // Parity | |
FixedPcdGet8 (PcdUartDefaultStopBits) // StopBits | |
}, | |
// | |
// VENDOR_DEFINED_DEVICE_PATH TermType | |
// | |
{ | |
{ | |
MESSAGING_DEVICE_PATH, MSG_VENDOR_DP, | |
DP_NODE_LEN (VENDOR_DEFINED_DEVICE_PATH) | |
} | |
// | |
// Guid to be filled in dynamically | |
// | |
}, | |
// | |
// EFI_DEVICE_PATH_PROTOCOL End | |
// | |
{ | |
END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, | |
DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL) | |
} | |
}; | |
#pragma pack (1) | |
typedef struct { | |
USB_CLASS_DEVICE_PATH Keyboard; | |
EFI_DEVICE_PATH_PROTOCOL End; | |
} PLATFORM_USB_KEYBOARD; | |
#pragma pack () | |
STATIC PLATFORM_USB_KEYBOARD mUsbKeyboard = { | |
// | |
// USB_CLASS_DEVICE_PATH Keyboard | |
// | |
{ | |
{ | |
MESSAGING_DEVICE_PATH, MSG_USB_CLASS_DP, | |
DP_NODE_LEN (USB_CLASS_DEVICE_PATH) | |
}, | |
0xFFFF, // VendorId: any | |
0xFFFF, // ProductId: any | |
3, // DeviceClass: HID | |
1, // DeviceSubClass: boot | |
1 // DeviceProtocol: keyboard | |
}, | |
// | |
// EFI_DEVICE_PATH_PROTOCOL End | |
// | |
{ | |
END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, | |
DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL) | |
} | |
}; | |
/** | |
An empty function to pass error checking of CreateEventEx (). | |
@param Event Event whose notification function is being invoked. | |
@param Context Pointer to the notification function's context, | |
which is implementation-dependent. | |
**/ | |
STATIC | |
VOID | |
EFIAPI | |
EmptyCallbackFunction ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
} | |
// | |
// BDS Platform Functions | |
// | |
/** | |
Platform Bds init. Include the platform firmware vendor, revision | |
and so crc check. | |
**/ | |
VOID | |
EFIAPI | |
PlatformBdsInit ( | |
VOID | |
) | |
{ | |
EFI_EVENT EndOfDxeEvent; | |
EFI_STATUS Status; | |
// | |
// Signal EndOfDxe PI Event | |
// | |
Status = gBS->CreateEventEx ( | |
EVT_NOTIFY_SIGNAL, | |
TPL_CALLBACK, | |
EmptyCallbackFunction, | |
NULL, | |
&gEfiEndOfDxeEventGroupGuid, | |
&EndOfDxeEvent | |
); | |
if (!EFI_ERROR (Status)) { | |
gBS->SignalEvent (EndOfDxeEvent); | |
gBS->CloseEvent (EndOfDxeEvent); | |
} | |
} | |
/** | |
Check if the handle satisfies a particular condition. | |
@param[in] Handle The handle to check. | |
@param[in] ReportText A caller-allocated string passed in for reporting | |
purposes. It must never be NULL. | |
@retval TRUE The condition is satisfied. | |
@retval FALSE Otherwise. This includes the case when the condition could not | |
be fully evaluated due to an error. | |
**/ | |
typedef | |
BOOLEAN | |
(EFIAPI *FILTER_FUNCTION) ( | |
IN EFI_HANDLE Handle, | |
IN CONST CHAR16 *ReportText | |
); | |
/** | |
Process a handle. | |
@param[in] Handle The handle to process. | |
@param[in] ReportText A caller-allocated string passed in for reporting | |
purposes. It must never be NULL. | |
**/ | |
typedef | |
VOID | |
(EFIAPI *CALLBACK_FUNCTION) ( | |
IN EFI_HANDLE Handle, | |
IN CONST CHAR16 *ReportText | |
); | |
/** | |
Locate all handles that carry the specified protocol, filter them with a | |
callback function, and pass each handle that passes the filter to another | |
callback. | |
@param[in] ProtocolGuid The protocol to look for. | |
@param[in] Filter The filter function to pass each handle to. If this | |
parameter is NULL, then all handles are processed. | |
@param[in] Process The callback function to pass each handle to that | |
clears the filter. | |
**/ | |
STATIC | |
VOID | |
FilterAndProcess ( | |
IN EFI_GUID *ProtocolGuid, | |
IN FILTER_FUNCTION Filter OPTIONAL, | |
IN CALLBACK_FUNCTION Process | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_HANDLE *Handles; | |
UINTN NoHandles; | |
UINTN Idx; | |
Status = gBS->LocateHandleBuffer (ByProtocol, ProtocolGuid, | |
NULL /* SearchKey */, &NoHandles, &Handles); | |
if (EFI_ERROR (Status)) { | |
// | |
// This is not an error, just an informative condition. | |
// | |
DEBUG ((EFI_D_VERBOSE, "%a: %g: %r\n", __FUNCTION__, ProtocolGuid, | |
Status)); | |
return; | |
} | |
ASSERT (NoHandles > 0); | |
for (Idx = 0; Idx < NoHandles; ++Idx) { | |
CHAR16 *DevicePathText; | |
STATIC CHAR16 Fallback[] = L"<device path unavailable>"; | |
// | |
// The ConvertDevicePathToText() function handles NULL input transparently. | |
// | |
DevicePathText = ConvertDevicePathToText ( | |
DevicePathFromHandle (Handles[Idx]), | |
FALSE, // DisplayOnly | |
FALSE // AllowShortcuts | |
); | |
if (DevicePathText == NULL) { | |
DevicePathText = Fallback; | |
} | |
if (Filter == NULL || Filter (Handles[Idx], DevicePathText)) { | |
Process (Handles[Idx], DevicePathText); | |
} | |
if (DevicePathText != Fallback) { | |
FreePool (DevicePathText); | |
} | |
} | |
gBS->FreePool (Handles); | |
} | |
/** | |
This FILTER_FUNCTION checks if a handle corresponds to a PCI display device. | |
**/ | |
STATIC | |
BOOLEAN | |
EFIAPI | |
IsPciDisplay ( | |
IN EFI_HANDLE Handle, | |
IN CONST CHAR16 *ReportText | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
PCI_TYPE00 Pci; | |
Status = gBS->HandleProtocol (Handle, &gEfiPciIoProtocolGuid, | |
(VOID**)&PciIo); | |
if (EFI_ERROR (Status)) { | |
// | |
// This is not an error worth reporting. | |
// | |
return FALSE; | |
} | |
Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, 0 /* Offset */, | |
sizeof Pci / sizeof (UINT32), &Pci); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((EFI_D_ERROR, "%a: %s: %r\n", __FUNCTION__, ReportText, Status)); | |
return FALSE; | |
} | |
return IS_PCI_DISPLAY (&Pci); | |
} | |
/** | |
This CALLBACK_FUNCTION attempts to connect a handle non-recursively, asking | |
the matching driver to produce all first-level child handles. | |
**/ | |
STATIC | |
VOID | |
EFIAPI | |
Connect ( | |
IN EFI_HANDLE Handle, | |
IN CONST CHAR16 *ReportText | |
) | |
{ | |
EFI_STATUS Status; | |
Status = gBS->ConnectController ( | |
Handle, // ControllerHandle | |
NULL, // DriverImageHandle | |
NULL, // RemainingDevicePath -- produce all children | |
FALSE // Recursive | |
); | |
DEBUG ((EFI_ERROR (Status) ? EFI_D_ERROR : EFI_D_VERBOSE, "%a: %s: %r\n", | |
__FUNCTION__, ReportText, Status)); | |
} | |
/** | |
This CALLBACK_FUNCTION retrieves the EFI_DEVICE_PATH_PROTOCOL from the | |
handle, and adds it to ConOut and ErrOut. | |
**/ | |
STATIC | |
VOID | |
EFIAPI | |
AddOutput ( | |
IN EFI_HANDLE Handle, | |
IN CONST CHAR16 *ReportText | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
DevicePath = DevicePathFromHandle (Handle); | |
if (DevicePath == NULL) { | |
DEBUG ((EFI_D_ERROR, "%a: %s: handle %p: device path not found\n", | |
__FUNCTION__, ReportText, Handle)); | |
return; | |
} | |
Status = BdsLibUpdateConsoleVariable (L"ConOut", DevicePath, NULL); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((EFI_D_ERROR, "%a: %s: adding to ConOut: %r\n", __FUNCTION__, | |
ReportText, Status)); | |
return; | |
} | |
Status = BdsLibUpdateConsoleVariable (L"ErrOut", DevicePath, NULL); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((EFI_D_ERROR, "%a: %s: adding to ErrOut: %r\n", __FUNCTION__, | |
ReportText, Status)); | |
return; | |
} | |
DEBUG ((EFI_D_VERBOSE, "%a: %s: added to ConOut and ErrOut\n", __FUNCTION__, | |
ReportText)); | |
} | |
/** | |
The function will execute with as the platform policy, current policy | |
is driven by boot mode. IBV/OEM can customize this code for their specific | |
policy action. | |
@param DriverOptionList The header of the driver option link list | |
@param BootOptionList The header of the boot option link list | |
@param ProcessCapsules A pointer to ProcessCapsules() | |
@param BaseMemoryTest A pointer to BaseMemoryTest() | |
**/ | |
VOID | |
EFIAPI | |
PlatformBdsPolicyBehavior ( | |
IN LIST_ENTRY *DriverOptionList, | |
IN LIST_ENTRY *BootOptionList, | |
IN PROCESS_CAPSULES ProcessCapsules, | |
IN BASEM_MEMORY_TEST BaseMemoryTest | |
) | |
{ | |
// | |
// Locate the PCI root bridges and make the PCI bus driver connect each, | |
// non-recursively. This will produce a number of child handles with PciIo on | |
// them. | |
// | |
FilterAndProcess (&gEfiPciRootBridgeIoProtocolGuid, NULL, Connect); | |
// | |
// Find all display class PCI devices (using the handles from the previous | |
// step), and connect them non-recursively. This should produce a number of | |
// child handles with GOPs on them. | |
// | |
FilterAndProcess (&gEfiPciIoProtocolGuid, IsPciDisplay, Connect); | |
// | |
// Now add the device path of all handles with GOP on them to ConOut and | |
// ErrOut. | |
// | |
FilterAndProcess (&gEfiGraphicsOutputProtocolGuid, NULL, AddOutput); | |
// | |
// Add the hardcoded short-form USB keyboard device path to ConIn. | |
// | |
BdsLibUpdateConsoleVariable (L"ConIn", | |
(EFI_DEVICE_PATH_PROTOCOL *)&mUsbKeyboard, NULL); | |
// | |
// Add the hardcoded serial console device path to ConIn, ConOut, ErrOut. | |
// | |
CopyGuid (&mSerialConsole.TermType.Guid, | |
PcdGetPtr (PcdTerminalTypeGuidBuffer)); | |
BdsLibUpdateConsoleVariable (L"ConIn", | |
(EFI_DEVICE_PATH_PROTOCOL *)&mSerialConsole, NULL); | |
BdsLibUpdateConsoleVariable (L"ConOut", | |
(EFI_DEVICE_PATH_PROTOCOL *)&mSerialConsole, NULL); | |
BdsLibUpdateConsoleVariable (L"ErrOut", | |
(EFI_DEVICE_PATH_PROTOCOL *)&mSerialConsole, NULL); | |
// | |
// Connect the consoles based on the above variables. | |
// | |
BdsLibConnectAllDefaultConsoles (); | |
// | |
// Show the splash screen. | |
// | |
EnableQuietBoot (PcdGetPtr (PcdLogoFile)); | |
// | |
// Connect the rest of the devices. | |
// | |
BdsLibConnectAll (); | |
// | |
// Process QEMU's -kernel command line option. Note that the kernel booted | |
// this way should receive ACPI tables, which is why we connect all devices | |
// first (see above) -- PCI enumeration blocks ACPI table installation, if | |
// there is a PCI host. | |
// | |
TryRunningQemuKernel (); | |
BdsLibEnumerateAllBootOption (BootOptionList); | |
SetBootOrderFromQemu (BootOptionList); | |
// | |
// The BootOrder variable may have changed, reload the in-memory list with | |
// it. | |
// | |
BdsLibBuildOptionFromVar (BootOptionList, L"BootOrder"); | |
PlatformBdsEnterFrontPage (GetFrontPageTimeoutFromQemu(), TRUE); | |
} | |
/** | |
Hook point after a boot attempt succeeds. We don't expect a boot option to | |
return, so the UEFI 2.0 specification defines that you will default to an | |
interactive mode and stop processing the BootOrder list in this case. This | |
is also a platform implementation and can be customized by IBV/OEM. | |
@param Option Pointer to Boot Option that succeeded to boot. | |
**/ | |
VOID | |
EFIAPI | |
PlatformBdsBootSuccess ( | |
IN BDS_COMMON_OPTION *Option | |
) | |
{ | |
} | |
/** | |
Hook point after a boot attempt fails. | |
@param Option Pointer to Boot Option that failed to boot. | |
@param Status Status returned from failed boot. | |
@param ExitData Exit data returned from failed boot. | |
@param ExitDataSize Exit data size returned from failed boot. | |
**/ | |
VOID | |
EFIAPI | |
PlatformBdsBootFail ( | |
IN BDS_COMMON_OPTION *Option, | |
IN EFI_STATUS Status, | |
IN CHAR16 *ExitData, | |
IN UINTN ExitDataSize | |
) | |
{ | |
} | |
/** | |
This function locks platform flash that is not allowed to be updated during normal boot path. | |
The flash layout is platform specific. | |
**/ | |
VOID | |
EFIAPI | |
PlatformBdsLockNonUpdatableFlash ( | |
VOID | |
) | |
{ | |
return; | |
} |