/** @file | |
Emu Bus driver | |
Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR> | |
Portions copyright (c) 2011, Apple Inc. All rights reserved. | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "EmuBusDriverDxe.h" | |
// | |
// DriverBinding protocol global | |
// | |
EFI_DRIVER_BINDING_PROTOCOL gEmuBusDriverBinding = { | |
EmuBusDriverBindingSupported, | |
EmuBusDriverBindingStart, | |
EmuBusDriverBindingStop, | |
0xa, | |
NULL, | |
NULL | |
}; | |
EFI_STATUS | |
EFIAPI | |
EmuBusDriverBindingSupported ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE ControllerHandle, | |
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; | |
EMU_THUNK_PROTOCOL *EmuThunk; | |
// | |
// Check the contents of the first Device Path Node of RemainingDevicePath to make sure | |
// it is a legal Device Path Node for this bus driver's children. | |
// | |
if (RemainingDevicePath != NULL) { | |
// | |
// Check if RemainingDevicePath is the End of Device Path Node, | |
// if yes, go on checking other conditions | |
// | |
if (!IsDevicePathEnd (RemainingDevicePath)) { | |
// | |
// If RemainingDevicePath isn't the End of Device Path Node, | |
// check its validation | |
// | |
if ((RemainingDevicePath->Type != HARDWARE_DEVICE_PATH) || | |
(RemainingDevicePath->SubType != HW_VENDOR_DP) || | |
(DevicePathNodeLength (RemainingDevicePath) != sizeof (EMU_VENDOR_DEVICE_PATH_NODE))) | |
{ | |
return EFI_UNSUPPORTED; | |
} | |
} | |
} | |
// | |
// Open the IO Abstraction(s) needed to perform the supported test | |
// | |
Status = gBS->OpenProtocol ( | |
ControllerHandle, | |
&gEmuThunkProtocolGuid, | |
(VOID **)&EmuThunk, | |
This->DriverBindingHandle, | |
ControllerHandle, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
if (Status == EFI_ALREADY_STARTED) { | |
return EFI_SUCCESS; | |
} | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Close the I/O Abstraction(s) used to perform the supported test | |
// | |
gBS->CloseProtocol ( | |
ControllerHandle, | |
&gEmuThunkProtocolGuid, | |
This->DriverBindingHandle, | |
ControllerHandle | |
); | |
// | |
// Open the EFI Device Path protocol needed to perform the supported test | |
// | |
Status = gBS->OpenProtocol ( | |
ControllerHandle, | |
&gEfiDevicePathProtocolGuid, | |
(VOID **)&ParentDevicePath, | |
This->DriverBindingHandle, | |
ControllerHandle, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
if (Status == EFI_ALREADY_STARTED) { | |
return EFI_SUCCESS; | |
} | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Close protocol, don't use device path protocol in the Support() function | |
// | |
gBS->CloseProtocol ( | |
ControllerHandle, | |
&gEfiDevicePathProtocolGuid, | |
This->DriverBindingHandle, | |
ControllerHandle | |
); | |
return Status; | |
} | |
EFI_STATUS | |
EFIAPI | |
EmuBusDriverBindingStart ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE ControllerHandle, | |
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_STATUS InstallStatus; | |
EMU_THUNK_PROTOCOL *EmuThunk; | |
EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; | |
EMU_IO_DEVICE *EmuDevice; | |
EMU_BUS_DEVICE *EmuBusDevice; | |
EMU_IO_THUNK_PROTOCOL *EmuIoThunk; | |
UINT16 ComponentName[512]; | |
EMU_VENDOR_DEVICE_PATH_NODE *Node; | |
BOOLEAN CreateDevice; | |
InstallStatus = EFI_UNSUPPORTED; | |
Status = EFI_UNSUPPORTED; | |
// | |
// Grab the protocols we need | |
// | |
Status = gBS->OpenProtocol ( | |
ControllerHandle, | |
&gEfiDevicePathProtocolGuid, | |
(VOID **)&ParentDevicePath, | |
This->DriverBindingHandle, | |
ControllerHandle, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { | |
return Status; | |
} | |
Status = gBS->OpenProtocol ( | |
ControllerHandle, | |
&gEmuThunkProtocolGuid, | |
(VOID **)&EmuThunk, | |
This->DriverBindingHandle, | |
ControllerHandle, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { | |
return Status; | |
} | |
if (Status != EFI_ALREADY_STARTED) { | |
EmuBusDevice = AllocatePool (sizeof (EMU_BUS_DEVICE)); | |
if (EmuBusDevice == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
EmuBusDevice->Signature = EMU_BUS_DEVICE_SIGNATURE; | |
EmuBusDevice->ControllerNameTable = NULL; | |
AddUnicodeString2 ( | |
"eng", | |
gEmuBusDriverComponentName.SupportedLanguages, | |
&EmuBusDevice->ControllerNameTable, | |
L"Emulator Bus Controller", | |
TRUE | |
); | |
AddUnicodeString2 ( | |
"en", | |
gEmuBusDriverComponentName2.SupportedLanguages, | |
&EmuBusDevice->ControllerNameTable, | |
L"Emulator Bus Controller", | |
FALSE | |
); | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&ControllerHandle, | |
&gEfiCallerIdGuid, | |
EmuBusDevice, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
FreeUnicodeStringTable (EmuBusDevice->ControllerNameTable); | |
gBS->FreePool (EmuBusDevice); | |
return Status; | |
} | |
} | |
for (Status = EFI_SUCCESS, EmuIoThunk = NULL; !EFI_ERROR (Status); ) { | |
Status = EmuThunk->GetNextProtocol (TRUE, &EmuIoThunk); | |
if (EFI_ERROR (Status)) { | |
break; | |
} | |
CreateDevice = TRUE; | |
if (RemainingDevicePath != NULL) { | |
CreateDevice = FALSE; | |
// | |
// Check if RemainingDevicePath is the End of Device Path Node, | |
// if yes, don't create any child device | |
// | |
if (!IsDevicePathEnd (RemainingDevicePath)) { | |
// | |
// If RemainingDevicePath isn't the End of Device Path Node, | |
// check its validation | |
// | |
Node = (EMU_VENDOR_DEVICE_PATH_NODE *)RemainingDevicePath; | |
if ((Node->VendorDevicePath.Header.Type == HARDWARE_DEVICE_PATH) && | |
(Node->VendorDevicePath.Header.SubType == HW_VENDOR_DP) && | |
(DevicePathNodeLength (&Node->VendorDevicePath.Header) == sizeof (EMU_VENDOR_DEVICE_PATH_NODE)) | |
) | |
{ | |
if (CompareGuid (&Node->VendorDevicePath.Guid, EmuIoThunk->Protocol) && (Node->Instance == EmuIoThunk->Instance)) { | |
CreateDevice = TRUE; | |
} | |
} | |
} | |
} | |
if (CreateDevice) { | |
// | |
// Allocate instance structure, and fill in parent information. | |
// | |
EmuDevice = AllocatePool (sizeof (EMU_IO_DEVICE)); | |
if (EmuDevice == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
EmuDevice->Handle = NULL; | |
EmuDevice->ControllerHandle = ControllerHandle; | |
EmuDevice->ParentDevicePath = ParentDevicePath; | |
CopyMem (&EmuDevice->EmuIoThunk, EmuIoThunk, sizeof (EMU_IO_THUNK_PROTOCOL)); | |
EmuDevice->ControllerNameTable = NULL; | |
StrnCpyS ( | |
ComponentName, | |
sizeof (ComponentName) / sizeof (CHAR16), | |
EmuIoThunk->ConfigString, | |
sizeof (ComponentName) / sizeof (CHAR16) | |
); | |
EmuDevice->DevicePath = EmuBusCreateDevicePath ( | |
ParentDevicePath, | |
EmuIoThunk->Protocol, | |
EmuIoThunk->Instance | |
); | |
if (EmuDevice->DevicePath == NULL) { | |
gBS->FreePool (EmuDevice); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
AddUnicodeString ( | |
"eng", | |
gEmuBusDriverComponentName.SupportedLanguages, | |
&EmuDevice->ControllerNameTable, | |
ComponentName | |
); | |
EmuDevice->Signature = EMU_IO_DEVICE_SIGNATURE; | |
InstallStatus = gBS->InstallMultipleProtocolInterfaces ( | |
&EmuDevice->Handle, | |
&gEfiDevicePathProtocolGuid, | |
EmuDevice->DevicePath, | |
&gEmuIoThunkProtocolGuid, | |
&EmuDevice->EmuIoThunk, | |
NULL | |
); | |
if (EFI_ERROR (InstallStatus)) { | |
FreeUnicodeStringTable (EmuDevice->ControllerNameTable); | |
gBS->FreePool (EmuDevice); | |
} else { | |
// | |
// Open For Child Device | |
// | |
Status = gBS->OpenProtocol ( | |
ControllerHandle, | |
&gEmuThunkProtocolGuid, | |
(VOID **)&EmuThunk, | |
This->DriverBindingHandle, | |
EmuDevice->Handle, | |
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER | |
); | |
if (!EFI_ERROR (Status)) { | |
InstallStatus = EFI_SUCCESS; | |
} | |
} | |
} | |
} | |
return InstallStatus; | |
} | |
EFI_STATUS | |
EFIAPI | |
EmuBusDriverBindingStop ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE ControllerHandle, | |
IN UINTN NumberOfChildren, | |
IN EFI_HANDLE *ChildHandleBuffer | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN Index; | |
BOOLEAN AllChildrenStopped; | |
EMU_IO_THUNK_PROTOCOL *EmuIoThunk; | |
EMU_BUS_DEVICE *EmuBusDevice; | |
EMU_IO_DEVICE *EmuDevice; | |
EMU_THUNK_PROTOCOL *EmuThunk; | |
// | |
// Complete all outstanding transactions to Controller. | |
// Don't allow any new transaction to Controller to be started. | |
// | |
if (NumberOfChildren == 0) { | |
// | |
// Close the bus driver | |
// | |
Status = gBS->OpenProtocol ( | |
ControllerHandle, | |
&gEfiCallerIdGuid, | |
(VOID **)&EmuBusDevice, | |
This->DriverBindingHandle, | |
ControllerHandle, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
gBS->UninstallMultipleProtocolInterfaces ( | |
ControllerHandle, | |
&gEfiCallerIdGuid, | |
EmuBusDevice, | |
NULL | |
); | |
FreeUnicodeStringTable (EmuBusDevice->ControllerNameTable); | |
gBS->FreePool (EmuBusDevice); | |
gBS->CloseProtocol ( | |
ControllerHandle, | |
&gEmuThunkProtocolGuid, | |
This->DriverBindingHandle, | |
ControllerHandle | |
); | |
gBS->CloseProtocol ( | |
ControllerHandle, | |
&gEfiDevicePathProtocolGuid, | |
This->DriverBindingHandle, | |
ControllerHandle | |
); | |
return EFI_SUCCESS; | |
} | |
AllChildrenStopped = TRUE; | |
for (Index = 0; Index < NumberOfChildren; Index++) { | |
Status = gBS->OpenProtocol ( | |
ChildHandleBuffer[Index], | |
&gEmuIoThunkProtocolGuid, | |
(VOID **)&EmuIoThunk, | |
This->DriverBindingHandle, | |
ControllerHandle, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
); | |
if (!EFI_ERROR (Status)) { | |
EmuDevice = EMU_IO_DEVICE_FROM_THIS (EmuIoThunk); | |
Status = gBS->CloseProtocol ( | |
ControllerHandle, | |
&gEmuThunkProtocolGuid, | |
This->DriverBindingHandle, | |
EmuDevice->Handle | |
); | |
Status = gBS->UninstallMultipleProtocolInterfaces ( | |
EmuDevice->Handle, | |
&gEfiDevicePathProtocolGuid, | |
EmuDevice->DevicePath, | |
&gEmuIoThunkProtocolGuid, | |
&EmuDevice->EmuIoThunk, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
gBS->OpenProtocol ( | |
ControllerHandle, | |
&gEmuThunkProtocolGuid, | |
(VOID **)&EmuThunk, | |
This->DriverBindingHandle, | |
EmuDevice->Handle, | |
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER | |
); | |
} else { | |
// | |
// Close the child handle | |
// | |
FreeUnicodeStringTable (EmuDevice->ControllerNameTable); | |
FreePool (EmuDevice); | |
} | |
} | |
if (EFI_ERROR (Status)) { | |
AllChildrenStopped = FALSE; | |
} | |
} | |
if (!AllChildrenStopped) { | |
return EFI_DEVICE_ERROR; | |
} | |
return EFI_SUCCESS; | |
} | |
/*++ | |
Routine Description: | |
Create a device path node using Guid and InstanceNumber and append it to | |
the passed in RootDevicePath | |
Arguments: | |
RootDevicePath - Root of the device path to return. | |
Guid - GUID to use in vendor device path node. | |
InstanceNumber - Instance number to use in the vendor device path. This | |
argument is needed to make sure each device path is unique. | |
Returns: | |
EFI_DEVICE_PATH_PROTOCOL | |
**/ | |
EFI_DEVICE_PATH_PROTOCOL * | |
EmuBusCreateDevicePath ( | |
IN EFI_DEVICE_PATH_PROTOCOL *RootDevicePath, | |
IN EFI_GUID *Guid, | |
IN UINT16 InstanceNumber | |
) | |
{ | |
EMU_VENDOR_DEVICE_PATH_NODE DevicePath; | |
DevicePath.VendorDevicePath.Header.Type = HARDWARE_DEVICE_PATH; | |
DevicePath.VendorDevicePath.Header.SubType = HW_VENDOR_DP; | |
SetDevicePathNodeLength (&DevicePath.VendorDevicePath.Header, sizeof (EMU_VENDOR_DEVICE_PATH_NODE)); | |
// | |
// The GUID defines the Class | |
// | |
CopyMem (&DevicePath.VendorDevicePath.Guid, Guid, sizeof (EFI_GUID)); | |
// | |
// Add an instance number so we can make sure there are no Device Path | |
// duplication. | |
// | |
DevicePath.Instance = InstanceNumber; | |
return AppendDevicePathNode ( | |
RootDevicePath, | |
(EFI_DEVICE_PATH_PROTOCOL *)&DevicePath | |
); | |
} | |
/** | |
The user Entry Point for module EmuBusDriver. The user code starts with this function. | |
@param[in] ImageHandle The firmware allocated handle for the EFI image. | |
@param[in] SystemTable A pointer to the EFI System Table. | |
@retval EFI_SUCCESS The entry point is executed successfully. | |
@retval other Some error occurs when executing this entry point. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
InitializeEmuBusDriver ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
Status = EfiLibInstallAllDriverProtocols ( | |
ImageHandle, | |
SystemTable, | |
&gEmuBusDriverBinding, | |
ImageHandle, | |
&gEmuBusDriverComponentName, | |
NULL, | |
NULL | |
); | |
ASSERT_EFI_ERROR (Status); | |
return Status; | |
} |