/** @file Emu Bus driver Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
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; }