summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatform.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatform.c')
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatform.c1271
1 files changed, 1271 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatform.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatform.c
new file mode 100644
index 00000000..26e1db03
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatform.c
@@ -0,0 +1,1271 @@
+/** @file
+ Console Platform DXE Driver, install Console Device Guids and update Console
+ Environment Variables.
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "ConPlatform.h"
+
+
+EFI_DRIVER_BINDING_PROTOCOL gConPlatformTextInDriverBinding = {
+ ConPlatformTextInDriverBindingSupported,
+ ConPlatformTextInDriverBindingStart,
+ ConPlatformTextInDriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+EFI_DRIVER_BINDING_PROTOCOL gConPlatformTextOutDriverBinding = {
+ ConPlatformTextOutDriverBindingSupported,
+ ConPlatformTextOutDriverBindingStart,
+ ConPlatformTextOutDriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+/**
+ Entrypoint of this module.
+
+ This function is the entrypoint of this module. It installs Driver Binding
+ Protocols together with Component Name Protocols.
+
+ @param ImageHandle The firmware allocated handle for the EFI image.
+ @param SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The entry point is executed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+InitializeConPlatform(
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gConPlatformTextInDriverBinding,
+ ImageHandle,
+ &gConPlatformComponentName,
+ &gConPlatformComponentName2
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ Status = EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gConPlatformTextOutDriverBinding,
+ NULL,
+ &gConPlatformComponentName,
+ &gConPlatformComponentName2
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Test to see if EFI_SIMPLE_TEXT_INPUT_PROTOCOL is supported on ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to test.
+ @param RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCESS This driver supports this device.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+ConPlatformTextInDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ return ConPlatformDriverBindingSupported (
+ This,
+ ControllerHandle,
+ &gEfiSimpleTextInProtocolGuid
+ );
+}
+
+
+/**
+ Test to see if EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL is supported on ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to test.
+ @param RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCESS This driver supports this device.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+ConPlatformTextOutDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ return ConPlatformDriverBindingSupported (
+ This,
+ ControllerHandle,
+ &gEfiSimpleTextOutProtocolGuid
+ );
+}
+
+
+/**
+ Test to see if the specified protocol is supported on ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to test.
+ @param ProtocolGuid The specfic protocol.
+
+ @retval EFI_SUCCESS This driver supports this device.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+ConPlatformDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_GUID *ProtocolGuid
+ )
+{
+ EFI_STATUS Status;
+ VOID *Interface;
+
+ //
+ // Test to see if this is a physical device by checking if
+ // it has a Device Path Protocol.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDevicePathProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // Test to see if this device supports the specified Protocol.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ ProtocolGuid,
+ (VOID **) &Interface,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ gBS->CloseProtocol (
+ ControllerHandle,
+ ProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Start this driver on the device for console input.
+
+ Start this driver on ControllerHandle by opening Simple Text Input Protocol,
+ reading Device Path, and installing Console In Devcice GUID on ControllerHandle.
+
+ Append its device path into the console environment variables ConInDev.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to bind driver to
+ @param RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCESS This driver is added to ControllerHandle
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+ConPlatformTextInDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_SIMPLE_TEXT_INPUT_PROTOCOL *TextIn;
+ BOOLEAN IsInConInVariable;
+
+ //
+ // Get the Device Path Protocol so the environment variables can be updated
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &DevicePath,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // Open the Simple Text Input Protocol BY_DRIVER
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiSimpleTextInProtocolGuid,
+ (VOID **) &TextIn,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // Check if the device path is in ConIn Variable
+ //
+ IsInConInVariable = FALSE;
+ Status = ConPlatformUpdateDeviceVariable (
+ L"ConIn",
+ DevicePath,
+ Check
+ );
+ if (!EFI_ERROR (Status)) {
+ IsInConInVariable = TRUE;
+ }
+
+ //
+ // Append the device path to the ConInDev environment variable
+ //
+ ConPlatformUpdateDeviceVariable (
+ L"ConInDev",
+ DevicePath,
+ Append
+ );
+
+ //
+ // If the device path is an instance in the ConIn environment variable,
+ // then install EfiConsoleInDeviceGuid onto ControllerHandle
+ //
+ if (IsInConInVariable) {
+ gBS->InstallMultipleProtocolInterfaces (
+ &ControllerHandle,
+ &gEfiConsoleInDeviceGuid,
+ NULL,
+ NULL
+ );
+ } else {
+ gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiSimpleTextInProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Start this driver on the device for console output and standard error output.
+
+ Start this driver on ControllerHandle by opening Simple Text Output Protocol,
+ reading Device Path, and installing Console Out Devcic GUID, Standard Error
+ Device GUID on ControllerHandle.
+
+ Append its device path into the console environment variables ConOutDev, ErrOutDev.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to bind driver to
+ @param RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCESS This driver is added to ControllerHandle
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle
+ @retval other This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+ConPlatformTextOutDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *TextOut;
+ BOOLEAN NeedClose;
+ BOOLEAN IsInConOutVariable;
+ BOOLEAN IsInErrOutVariable;
+
+ NeedClose = TRUE;
+
+ //
+ // Get the Device Path Protocol so the environment variables can be updated
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &DevicePath,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // Open the Simple Text Output Protocol BY_DRIVER
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiSimpleTextOutProtocolGuid,
+ (VOID **) &TextOut,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // Check if the device path is in ConOut & ErrOut Variable
+ //
+ IsInConOutVariable = FALSE;
+ Status = ConPlatformUpdateDeviceVariable (
+ L"ConOut",
+ DevicePath,
+ Check
+ );
+ if (!EFI_ERROR (Status)) {
+ IsInConOutVariable = TRUE;
+ }
+
+ IsInErrOutVariable = FALSE;
+ Status = ConPlatformUpdateDeviceVariable (
+ L"ErrOut",
+ DevicePath,
+ Check
+ );
+ if (!EFI_ERROR (Status)) {
+ IsInErrOutVariable = TRUE;
+ }
+
+ //
+ // Append the device path to the ConOutDev and ErrOutDev environment variable.
+ // For GOP device path, append the sibling device path as well.
+ //
+ if (!ConPlatformUpdateGopCandidate (DevicePath)) {
+ ConPlatformUpdateDeviceVariable (
+ L"ConOutDev",
+ DevicePath,
+ Append
+ );
+ //
+ // Then append the device path to the ErrOutDev environment variable
+ //
+ ConPlatformUpdateDeviceVariable (
+ L"ErrOutDev",
+ DevicePath,
+ Append
+ );
+ }
+
+ //
+ // If the device path is an instance in the ConOut environment variable,
+ // then install EfiConsoleOutDeviceGuid onto ControllerHandle
+ //
+ if (IsInConOutVariable) {
+ NeedClose = FALSE;
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ControllerHandle,
+ &gEfiConsoleOutDeviceGuid,
+ NULL,
+ NULL
+ );
+ }
+ //
+ // If the device path is an instance in the ErrOut environment variable,
+ // then install EfiStandardErrorDeviceGuid onto ControllerHandle
+ //
+ if (IsInErrOutVariable) {
+ NeedClose = FALSE;
+ gBS->InstallMultipleProtocolInterfaces (
+ &ControllerHandle,
+ &gEfiStandardErrorDeviceGuid,
+ NULL,
+ NULL
+ );
+ }
+
+ if (NeedClose) {
+ gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiSimpleTextOutProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Stop this driver on ControllerHandle by removing Console In Devcice GUID
+ and closing the Simple Text Input protocol on ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to stop driver on
+ @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of
+ children is zero stop the entire bus driver.
+ @param ChildHandleBuffer List of Child Handles to Stop.
+
+ @retval EFI_SUCCESS This driver is removed ControllerHandle
+ @retval other This driver was not removed from this device
+
+**/
+EFI_STATUS
+EFIAPI
+ConPlatformTextInDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+
+ //
+ // Get the Device Path Protocol firstly
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &DevicePath,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ //
+ // If there is device path on ControllerHandle
+ //
+ if (!EFI_ERROR (Status)) {
+ //
+ // Remove DevicePath from ConInDev if exists.
+ //
+ ConPlatformUpdateDeviceVariable (
+ L"ConInDev",
+ DevicePath,
+ Delete
+ );
+ }
+
+ //
+ // Uninstall the Console Device GUIDs from Controller Handle
+ //
+ ConPlatformUnInstallProtocol (
+ This,
+ ControllerHandle,
+ &gEfiConsoleInDeviceGuid
+ );
+
+ //
+ // Close the Simple Text Input Protocol
+ //
+ gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiSimpleTextInProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Stop this driver on ControllerHandle by removing Console Out Devcice GUID
+ and closing the Simple Text Output protocol on ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to stop driver on
+ @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of
+ children is zero stop the entire bus driver.
+ @param ChildHandleBuffer List of Child Handles to Stop.
+
+ @retval EFI_SUCCESS This driver is removed ControllerHandle
+ @retval other This driver was not removed from this device
+
+**/
+EFI_STATUS
+EFIAPI
+ConPlatformTextOutDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+
+ //
+ // Get the Device Path Protocol firstly
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &DevicePath,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ //
+ // Remove DevicePath from ConOutDev and ErrOutDev if exists.
+ //
+ ConPlatformUpdateDeviceVariable (
+ L"ConOutDev",
+ DevicePath,
+ Delete
+ );
+ ConPlatformUpdateDeviceVariable (
+ L"ErrOutDev",
+ DevicePath,
+ Delete
+ );
+ }
+
+ //
+ // Uninstall the Console Device GUIDs from Controller Handle
+ //
+ ConPlatformUnInstallProtocol (
+ This,
+ ControllerHandle,
+ &gEfiConsoleOutDeviceGuid
+ );
+
+ ConPlatformUnInstallProtocol (
+ This,
+ ControllerHandle,
+ &gEfiStandardErrorDeviceGuid
+ );
+
+ //
+ // Close the Simple Text Output Protocol
+ //
+ gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiSimpleTextOutProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Uninstall the specified protocol.
+
+ @param This Protocol instance pointer.
+ @param Handle Handle of device to uninstall protocol on.
+ @param ProtocolGuid The specified protocol need to be uninstalled.
+
+**/
+VOID
+ConPlatformUnInstallProtocol (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Handle,
+ IN EFI_GUID *ProtocolGuid
+ )
+{
+ EFI_STATUS Status;
+
+ Status = gBS->OpenProtocol (
+ Handle,
+ ProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ Handle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ if (!EFI_ERROR (Status)) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ Handle,
+ ProtocolGuid,
+ NULL,
+ NULL
+ );
+ }
+
+ return ;
+}
+
+/**
+ Get the necessary size of buffer and read the variable.
+
+ First get the necessary size of buffer. Then read the
+ EFI variable (Name) and return a dynamically allocated
+ buffer. On failure return NULL.
+
+ @param Name String part of EFI variable name
+
+ @return Dynamically allocated memory that contains a copy of the EFI variable.
+ Caller is repsoncible freeing the buffer. Return NULL means Variable
+ was not read.
+
+**/
+VOID *
+ConPlatformGetVariable (
+ IN CHAR16 *Name
+ )
+{
+ EFI_STATUS Status;
+ VOID *Buffer;
+ UINTN BufferSize;
+
+ BufferSize = 0;
+ Buffer = NULL;
+
+ //
+ // Test to see if the variable exists. If it doesn't, return NULL.
+ //
+ Status = gRT->GetVariable (
+ Name,
+ &gEfiGlobalVariableGuid,
+ NULL,
+ &BufferSize,
+ Buffer
+ );
+
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ //
+ // Allocate the buffer to return
+ //
+ Buffer = AllocatePool (BufferSize);
+ if (Buffer == NULL) {
+ return NULL;
+ }
+ //
+ // Read variable into the allocated buffer.
+ //
+ Status = gRT->GetVariable (
+ Name,
+ &gEfiGlobalVariableGuid,
+ NULL,
+ &BufferSize,
+ Buffer
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (Buffer);
+ //
+ // To make sure Buffer is NULL if any error occurs.
+ //
+ Buffer = NULL;
+ }
+ }
+
+ return Buffer;
+}
+
+/**
+ Function returns TRUE when the two input device paths point to the two
+ GOP child handles that have the same parent.
+
+ @param Left A pointer to a device path data structure.
+ @param Right A pointer to a device path data structure.
+
+ @retval TRUE Left and Right share the same parent.
+ @retval FALSE Left and Right don't share the same parent or either of them is not
+ a GOP device path.
+**/
+BOOLEAN
+IsGopSibling (
+ IN EFI_DEVICE_PATH_PROTOCOL *Left,
+ IN EFI_DEVICE_PATH_PROTOCOL *Right
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *NodeLeft;
+ EFI_DEVICE_PATH_PROTOCOL *NodeRight;
+
+ for (NodeLeft = Left; !IsDevicePathEndType (NodeLeft); NodeLeft = NextDevicePathNode (NodeLeft)) {
+ if ((DevicePathType (NodeLeft) == ACPI_DEVICE_PATH && DevicePathSubType (NodeLeft) == ACPI_ADR_DP) ||
+ (DevicePathType (NodeLeft) == HARDWARE_DEVICE_PATH && DevicePathSubType (NodeLeft) == HW_CONTROLLER_DP &&
+ DevicePathType (NextDevicePathNode (NodeLeft)) == ACPI_DEVICE_PATH && DevicePathSubType (NextDevicePathNode (NodeLeft)) == ACPI_ADR_DP)) {
+ break;
+ }
+ }
+
+ if (IsDevicePathEndType (NodeLeft)) {
+ return FALSE;
+ }
+
+ for (NodeRight = Right; !IsDevicePathEndType (NodeRight); NodeRight = NextDevicePathNode (NodeRight)) {
+ if ((DevicePathType (NodeRight) == ACPI_DEVICE_PATH && DevicePathSubType (NodeRight) == ACPI_ADR_DP) ||
+ (DevicePathType (NodeRight) == HARDWARE_DEVICE_PATH && DevicePathSubType (NodeRight) == HW_CONTROLLER_DP &&
+ DevicePathType (NextDevicePathNode (NodeRight)) == ACPI_DEVICE_PATH && DevicePathSubType (NextDevicePathNode (NodeRight)) == ACPI_ADR_DP)) {
+ break;
+ }
+ }
+
+ if (IsDevicePathEndType (NodeRight)) {
+ return FALSE;
+ }
+
+ if (((UINTN) NodeLeft - (UINTN) Left) != ((UINTN) NodeRight - (UINTN) Right)) {
+ return FALSE;
+ }
+
+ return (BOOLEAN) (CompareMem (Left, Right, (UINTN) NodeLeft - (UINTN) Left) == 0);
+}
+
+/**
+ Check whether a USB device match the specified USB Class device path. This
+ function follows "Load Option Processing" behavior in UEFI specification.
+
+ @param UsbIo USB I/O protocol associated with the USB device.
+ @param UsbClass The USB Class device path to match.
+
+ @retval TRUE The USB device match the USB Class device path.
+ @retval FALSE The USB device does not match the USB Class device path.
+
+**/
+BOOLEAN
+MatchUsbClass (
+ IN EFI_USB_IO_PROTOCOL *UsbIo,
+ IN USB_CLASS_DEVICE_PATH *UsbClass
+ )
+{
+ EFI_STATUS Status;
+ EFI_USB_DEVICE_DESCRIPTOR DevDesc;
+ EFI_USB_INTERFACE_DESCRIPTOR IfDesc;
+ UINT8 DeviceClass;
+ UINT8 DeviceSubClass;
+ UINT8 DeviceProtocol;
+
+ if ((DevicePathType (UsbClass) != MESSAGING_DEVICE_PATH) ||
+ (DevicePathSubType (UsbClass) != MSG_USB_CLASS_DP)){
+ return FALSE;
+ }
+
+ //
+ // Check Vendor Id and Product Id.
+ //
+ Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+
+ if ((UsbClass->VendorId != 0xffff) &&
+ (UsbClass->VendorId != DevDesc.IdVendor)) {
+ return FALSE;
+ }
+
+ if ((UsbClass->ProductId != 0xffff) &&
+ (UsbClass->ProductId != DevDesc.IdProduct)) {
+ return FALSE;
+ }
+
+ DeviceClass = DevDesc.DeviceClass;
+ DeviceSubClass = DevDesc.DeviceSubClass;
+ DeviceProtocol = DevDesc.DeviceProtocol;
+ if (DeviceClass == 0) {
+ //
+ // If Class in Device Descriptor is set to 0, use the Class, SubClass and
+ // Protocol in Interface Descriptor instead.
+ //
+ Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc);
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+
+ DeviceClass = IfDesc.InterfaceClass;
+ DeviceSubClass = IfDesc.InterfaceSubClass;
+ DeviceProtocol = IfDesc.InterfaceProtocol;
+ }
+
+ //
+ // Check Class, SubClass and Protocol.
+ //
+ if ((UsbClass->DeviceClass != 0xff) &&
+ (UsbClass->DeviceClass != DeviceClass)) {
+ return FALSE;
+ }
+
+ if ((UsbClass->DeviceSubClass != 0xff) &&
+ (UsbClass->DeviceSubClass != DeviceSubClass)) {
+ return FALSE;
+ }
+
+ if ((UsbClass->DeviceProtocol != 0xff) &&
+ (UsbClass->DeviceProtocol != DeviceProtocol)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ Check whether a USB device match the specified USB WWID device path. This
+ function follows "Load Option Processing" behavior in UEFI specification.
+
+ @param UsbIo USB I/O protocol associated with the USB device.
+ @param UsbWwid The USB WWID device path to match.
+
+ @retval TRUE The USB device match the USB WWID device path.
+ @retval FALSE The USB device does not match the USB WWID device path.
+
+**/
+BOOLEAN
+MatchUsbWwid (
+ IN EFI_USB_IO_PROTOCOL *UsbIo,
+ IN USB_WWID_DEVICE_PATH *UsbWwid
+ )
+{
+ EFI_STATUS Status;
+ EFI_USB_DEVICE_DESCRIPTOR DevDesc;
+ EFI_USB_INTERFACE_DESCRIPTOR IfDesc;
+ UINT16 *LangIdTable;
+ UINT16 TableSize;
+ UINT16 Index;
+ CHAR16 *CompareStr;
+ UINTN CompareLen;
+ CHAR16 *SerialNumberStr;
+ UINTN Length;
+
+ if ((DevicePathType (UsbWwid) != MESSAGING_DEVICE_PATH) ||
+ (DevicePathSubType (UsbWwid) != MSG_USB_WWID_DP)) {
+ return FALSE;
+ }
+
+ //
+ // Check Vendor Id and Product Id.
+ //
+ Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+ if ((DevDesc.IdVendor != UsbWwid->VendorId) ||
+ (DevDesc.IdProduct != UsbWwid->ProductId)) {
+ return FALSE;
+ }
+
+ //
+ // Check Interface Number.
+ //
+ Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc);
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+ if (IfDesc.InterfaceNumber != UsbWwid->InterfaceNumber) {
+ return FALSE;
+ }
+
+ //
+ // Check Serial Number.
+ //
+ if (DevDesc.StrSerialNumber == 0) {
+ return FALSE;
+ }
+
+ //
+ // Get all supported languages.
+ //
+ TableSize = 0;
+ LangIdTable = NULL;
+ Status = UsbIo->UsbGetSupportedLanguages (UsbIo, &LangIdTable, &TableSize);
+ if (EFI_ERROR (Status) || (TableSize == 0) || (LangIdTable == NULL)) {
+ return FALSE;
+ }
+
+ //
+ // Serial number in USB WWID device path is the last 64-or-less UTF-16 characters.
+ //
+ CompareStr = (CHAR16 *) (UINTN) (UsbWwid + 1);
+ CompareLen = (DevicePathNodeLength (UsbWwid) - sizeof (USB_WWID_DEVICE_PATH)) / sizeof (CHAR16);
+ if (CompareStr[CompareLen - 1] == L'\0') {
+ CompareLen--;
+ }
+
+ //
+ // Compare serial number in each supported language.
+ //
+ for (Index = 0; Index < TableSize / sizeof (UINT16); Index++) {
+ SerialNumberStr = NULL;
+ Status = UsbIo->UsbGetStringDescriptor (
+ UsbIo,
+ LangIdTable[Index],
+ DevDesc.StrSerialNumber,
+ &SerialNumberStr
+ );
+ if (EFI_ERROR (Status) || (SerialNumberStr == NULL)) {
+ continue;
+ }
+
+ Length = StrLen (SerialNumberStr);
+ if ((Length >= CompareLen) &&
+ (CompareMem (SerialNumberStr + Length - CompareLen, CompareStr, CompareLen * sizeof (CHAR16)) == 0)) {
+ FreePool (SerialNumberStr);
+ return TRUE;
+ }
+
+ FreePool (SerialNumberStr);
+ }
+
+ return FALSE;
+}
+
+/**
+ Compare whether a full console device path matches a USB shortform device path.
+
+ @param[in] FullPath Full console device path.
+ @param[in] ShortformPath Short-form device path. Short-form device node may in the beginning or in the middle.
+
+ @retval TRUE The full console device path matches the short-form device path.
+ @retval FALSE The full console device path doesn't match the short-form device path.
+**/
+BOOLEAN
+MatchUsbShortformDevicePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *FullPath,
+ IN EFI_DEVICE_PATH_PROTOCOL *ShortformPath
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *ShortformNode;
+ UINTN ParentDevicePathSize;
+ EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath;
+ EFI_USB_IO_PROTOCOL *UsbIo;
+ EFI_HANDLE Handle;
+
+ for ( ShortformNode = ShortformPath
+ ; !IsDevicePathEnd (ShortformNode)
+ ; ShortformNode = NextDevicePathNode (ShortformNode)
+ ) {
+ if ((DevicePathType (ShortformNode) == MESSAGING_DEVICE_PATH) &&
+ ((DevicePathSubType (ShortformNode) == MSG_USB_CLASS_DP) ||
+ (DevicePathSubType (ShortformNode) == MSG_USB_WWID_DP))
+ ) {
+ break;
+ }
+ }
+
+ //
+ // Skip further compare when it's not a shortform device path.
+ //
+ if (IsDevicePathEnd (ShortformNode)) {
+ return FALSE;
+ }
+
+ //
+ // Compare the parent device path when the ShortformPath doesn't start with short-form node.
+ //
+ ParentDevicePathSize = (UINTN) ShortformNode - (UINTN) ShortformPath;
+ RemainingDevicePath = FullPath;
+ Status = gBS->LocateDevicePath (&gEfiUsbIoProtocolGuid, &RemainingDevicePath, &Handle);
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+ if (ParentDevicePathSize != 0) {
+ if ((ParentDevicePathSize > (UINTN) RemainingDevicePath - (UINTN) FullPath) ||
+ (CompareMem (FullPath, ShortformPath, ParentDevicePathSize) != 0)) {
+ return FALSE;
+ }
+ }
+
+ //
+ // Compar the USB layer.
+ //
+ Status = gBS->HandleProtocol(
+ Handle,
+ &gEfiUsbIoProtocolGuid,
+ (VOID **) &UsbIo
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ return MatchUsbClass (UsbIo, (USB_CLASS_DEVICE_PATH *)ShortformNode) ||
+ MatchUsbWwid (UsbIo, (USB_WWID_DEVICE_PATH *)ShortformNode);
+}
+
+/**
+ Function compares a device path data structure to that of all the nodes of a
+ second device path instance.
+
+ @param Multi A pointer to a multi-instance device path data structure.
+ @param Single A pointer to a single-instance device path data structure.
+ @param NewDevicePath If Delete is TRUE, this parameter must not be null, and it
+ points to the remaining device path data structure.
+ (remaining device path = Multi - Single.)
+ @param Delete If TRUE, means removing Single from Multi.
+ If FALSE, the routine just check whether Single matches
+ with any instance in Multi.
+
+ @retval EFI_SUCCESS If the Single is contained within Multi.
+ @retval EFI_NOT_FOUND If the Single is not contained within Multi.
+ @retval EFI_INVALID_PARAMETER Multi is NULL.
+ @retval EFI_INVALID_PARAMETER Single is NULL.
+ @retval EFI_INVALID_PARAMETER NewDevicePath is NULL when Delete is TRUE.
+
+**/
+EFI_STATUS
+ConPlatformMatchDevicePaths (
+ IN EFI_DEVICE_PATH_PROTOCOL *Multi,
+ IN EFI_DEVICE_PATH_PROTOCOL *Single,
+ OUT EFI_DEVICE_PATH_PROTOCOL **NewDevicePath OPTIONAL,
+ IN BOOLEAN Delete
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *TempDevicePath1;
+ EFI_DEVICE_PATH_PROTOCOL *TempDevicePath2;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePathInst;
+ UINTN Size;
+
+ //
+ // The passed in DevicePath should not be NULL
+ //
+ if ((Multi == NULL) || (Single == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // If performing Delete operation, the NewDevicePath must not be NULL.
+ //
+ if (Delete) {
+ if (NewDevicePath == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ TempDevicePath1 = NULL;
+
+ DevicePath = Multi;
+ DevicePathInst = GetNextDevicePathInstance (&DevicePath, &Size);
+
+ //
+ // Search for the match of 'Single' in 'Multi'
+ //
+ while (DevicePathInst != NULL) {
+ if ((CompareMem (Single, DevicePathInst, Size) == 0) ||
+ IsGopSibling (Single, DevicePathInst) || MatchUsbShortformDevicePath (Single, DevicePathInst)) {
+ if (!Delete) {
+ //
+ // If Delete is FALSE, return EFI_SUCCESS if Single is found in Multi.
+ //
+ FreePool (DevicePathInst);
+ return EFI_SUCCESS;
+ }
+ } else {
+ if (Delete) {
+ //
+ // If the node of Multi does not match Single, then added it back to the result.
+ // That is, the node matching Single will be dropped and deleted from result.
+ //
+ TempDevicePath2 = AppendDevicePathInstance (
+ TempDevicePath1,
+ DevicePathInst
+ );
+ if (TempDevicePath1 != NULL) {
+ FreePool (TempDevicePath1);
+ }
+ TempDevicePath1 = TempDevicePath2;
+ }
+ }
+
+ FreePool (DevicePathInst);
+ DevicePathInst = GetNextDevicePathInstance (&DevicePath, &Size);
+ }
+
+ if (Delete) {
+ //
+ // Return the new device path data structure with specified node deleted.
+ //
+ *NewDevicePath = TempDevicePath1;
+ return EFI_SUCCESS;
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ Update console environment variables.
+
+ @param VariableName Console environment variables, ConOutDev, ConInDev
+ ErrOutDev, ConIn ,ConOut or ErrOut.
+ @param DevicePath Console devcie's device path.
+ @param Operation Variable operations, including APPEND, CHECK and DELETE.
+
+ @retval EFI_SUCCESS Variable operates successfully.
+ @retval EFI_OUT_OF_RESOURCES If variable cannot be appended.
+ @retval other Variable updating failed.
+
+**/
+EFI_STATUS
+ConPlatformUpdateDeviceVariable (
+ IN CHAR16 *VariableName,
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ IN CONPLATFORM_VAR_OPERATION Operation
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *VariableDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *NewVariableDevicePath;
+
+ VariableDevicePath = NULL;
+ NewVariableDevicePath = NULL;
+
+ //
+ // Get Variable according to variable name.
+ // The memory for Variable is allocated within ConPlatformGetVarible(),
+ // it is the caller's responsibility to free the memory before return.
+ //
+ VariableDevicePath = ConPlatformGetVariable (VariableName);
+
+ if (Operation != Delete) {
+ //
+ // Match specified DevicePath in Console Variable.
+ //
+ Status = ConPlatformMatchDevicePaths (
+ VariableDevicePath,
+ DevicePath,
+ NULL,
+ FALSE
+ );
+
+ if ((Operation == Check) || (!EFI_ERROR (Status))) {
+ //
+ // Branch here includes 2 cases:
+ // 1. Operation is CHECK, simply return Status.
+ // 2. Operation is APPEND, and device path already exists in variable, also return.
+ //
+ if (VariableDevicePath != NULL) {
+ FreePool (VariableDevicePath);
+ }
+
+ return Status;
+ }
+ //
+ // We reach here to append a device path that does not exist in variable.
+ //
+ Status = EFI_SUCCESS;
+ NewVariableDevicePath = AppendDevicePathInstance (
+ VariableDevicePath,
+ DevicePath
+ );
+ if (NewVariableDevicePath == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ }
+
+ } else {
+ //
+ // We reach here to remove DevicePath from the environment variable that
+ // is a multi-instance device path.
+ //
+ Status = ConPlatformMatchDevicePaths (
+ VariableDevicePath,
+ DevicePath,
+ &NewVariableDevicePath,
+ TRUE
+ );
+ }
+
+ if (VariableDevicePath != NULL) {
+ FreePool (VariableDevicePath);
+ }
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (NewVariableDevicePath != NULL) {
+ //
+ // Update Console Environment Variable.
+ //
+ Status = gRT->SetVariable (
+ VariableName,
+ &gEfiGlobalVariableGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+ GetDevicePathSize (NewVariableDevicePath),
+ NewVariableDevicePath
+ );
+
+ FreePool (NewVariableDevicePath);
+ }
+
+ return Status;
+}
+
+/**
+ Update ConOutDev and ErrOutDev variables to add the device path of
+ GOP controller itself and the sibling controllers.
+
+ @param DevicePath Pointer to device's device path.
+
+ @retval TRUE The devcie is a GOP device.
+ @retval FALSE The devcie is not a GOP device.
+
+**/
+BOOLEAN
+ConPlatformUpdateGopCandidate (
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE PciHandle;
+ EFI_HANDLE GopHandle;
+ EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
+
+ //
+ // Check whether it's a GOP device.
+ //
+ TempDevicePath = DevicePath;
+ Status = gBS->LocateDevicePath (&gEfiGraphicsOutputProtocolGuid, &TempDevicePath, &GopHandle);
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+ //
+ // Get the parent PciIo handle in order to find all the children
+ //
+ Status = gBS->LocateDevicePath (&gEfiPciIoProtocolGuid, &DevicePath, &PciHandle);
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+ TempDevicePath = EfiBootManagerGetGopDevicePath (PciHandle);
+ if (TempDevicePath != NULL) {
+ ConPlatformUpdateDeviceVariable (L"ConOutDev", TempDevicePath, Append);
+ ConPlatformUpdateDeviceVariable (L"ErrOutDev", TempDevicePath, Append);
+ }
+ return TRUE;
+}