--- /dev/null
+/** @file\r
+\r
+ Implement the Driver Binding Protocol and the Component Name 2 Protocol for\r
+ the Virtio GPU hybrid driver.\r
+\r
+ Copyright (C) 2016, Red Hat, Inc.\r
+\r
+ This program and the accompanying materials are licensed and made available\r
+ under the terms and conditions of the BSD License which accompanies this\r
+ distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
+ WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/DevicePathLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/PrintLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiLib.h>\r
+#include <Protocol/ComponentName2.h>\r
+#include <Protocol/DevicePath.h>\r
+#include <Protocol/DriverBinding.h>\r
+#include <Protocol/PciIo.h>\r
+\r
+#include "VirtioGpu.h"\r
+\r
+//\r
+// Dummy Graphics Output Protocol GUID: a temporary placeholder for the EFI\r
+// counterpart. It will be replaced with the real thing as soon as we implement\r
+// the EFI GOP. Refer to VGPU_GOP.Gop.\r
+//\r
+STATIC EFI_GUID mDummyGraphicsOutputProtocolGuid = {\r
+ 0x4983f8dc, 0x2782, 0x415b,\r
+ { 0x91, 0xf5, 0x2c, 0xeb, 0x48, 0x4a, 0x0f, 0xe9 }\r
+};\r
+\r
+//\r
+// The device path node that describes the Video Output Device Attributes for\r
+// the single head (UEFI child handle) that we support.\r
+//\r
+// The ACPI_DISPLAY_ADR() macro corresponds to Table B-2, section "B.4.2 _DOD"\r
+// in the ACPI 3.0b spec, or more recently, to Table B-379, section "B.3.2\r
+// _DOD" in the ACPI 6.0 spec.\r
+//\r
+STATIC CONST ACPI_ADR_DEVICE_PATH mAcpiAdr = {\r
+ { // Header\r
+ ACPI_DEVICE_PATH, // Type\r
+ ACPI_ADR_DP, // SubType\r
+ { sizeof mAcpiAdr, 0 }, // Length\r
+ },\r
+ ACPI_DISPLAY_ADR ( // ADR\r
+ 1, // DeviceIdScheme: use the ACPI\r
+ // bit-field definitions\r
+ 0, // HeadId\r
+ 0, // NonVgaOutput\r
+ 1, // BiosCanDetect\r
+ 0, // VendorInfo\r
+ ACPI_ADR_DISPLAY_TYPE_EXTERNAL_DIGITAL, // Type\r
+ 0, // Port\r
+ 0 // Index\r
+ )\r
+};\r
+\r
+//\r
+// Component Name 2 Protocol implementation.\r
+//\r
+STATIC CONST EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {\r
+ { "en", L"Virtio GPU Driver" },\r
+ { NULL, NULL }\r
+};\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioGpuGetDriverName (\r
+ IN EFI_COMPONENT_NAME2_PROTOCOL *This,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **DriverName\r
+ )\r
+{\r
+ return LookupUnicodeString2 (Language, This->SupportedLanguages,\r
+ mDriverNameTable, DriverName, FALSE /* Iso639Language */);\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioGpuGetControllerName (\r
+ IN EFI_COMPONENT_NAME2_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_HANDLE ChildHandle OPTIONAL,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **ControllerName\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ VGPU_DEV *VgpuDev;\r
+\r
+ //\r
+ // Look up the VGPU_DEV "protocol interface" on ControllerHandle.\r
+ //\r
+ Status = gBS->OpenProtocol (ControllerHandle, &gEfiCallerIdGuid,\r
+ (VOID **)&VgpuDev, gImageHandle, ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ //\r
+ // Sanity check: if we found gEfiCallerIdGuid on ControllerHandle, then we\r
+ // keep its Virtio Device Protocol interface open BY_DRIVER.\r
+ //\r
+ ASSERT_EFI_ERROR (EfiTestManagedDevice (ControllerHandle, gImageHandle,\r
+ &gVirtioDeviceProtocolGuid));\r
+\r
+ if (ChildHandle == NULL) {\r
+ //\r
+ // The caller is querying the name of the VGPU_DEV controller.\r
+ //\r
+ return LookupUnicodeString2 (Language, This->SupportedLanguages,\r
+ VgpuDev->BusName, ControllerName, FALSE /* Iso639Language */);\r
+ }\r
+\r
+ //\r
+ // Otherwise, the caller is looking for the name of the GOP child controller.\r
+ // Check if it is asking about the GOP child controller that we manage. (The\r
+ // condition below covers the case when we haven't produced the GOP child\r
+ // controller yet, or we've destroyed it since.)\r
+ //\r
+ if (VgpuDev->Child == NULL || ChildHandle != VgpuDev->Child->GopHandle) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+ //\r
+ // Sanity check: our GOP child controller keeps the VGPU_DEV controller's\r
+ // Virtio Device Protocol interface open BY_CHILD_CONTROLLER.\r
+ //\r
+ ASSERT_EFI_ERROR (EfiTestChildHandle (ControllerHandle, ChildHandle,\r
+ &gVirtioDeviceProtocolGuid));\r
+\r
+ return LookupUnicodeString2 (Language, This->SupportedLanguages,\r
+ VgpuDev->Child->GopName, ControllerName,\r
+ FALSE /* Iso639Language */);\r
+}\r
+\r
+STATIC CONST EFI_COMPONENT_NAME2_PROTOCOL mComponentName2 = {\r
+ VirtioGpuGetDriverName,\r
+ VirtioGpuGetControllerName,\r
+ "en" // SupportedLanguages (RFC 4646)\r
+};\r
+\r
+//\r
+// Helper functions for the Driver Binding Protocol Implementation.\r
+//\r
+/**\r
+ Format the VGPU_DEV controller name, to be looked up and returned by\r
+ VirtioGpuGetControllerName().\r
+\r
+ @param[in] ControllerHandle The handle that identifies the VGPU_DEV\r
+ controller.\r
+\r
+ @param[in] AgentHandle The handle of the agent that will attempt to\r
+ temporarily open the PciIo protocol. This is the\r
+ DriverBindingHandle member of the\r
+ EFI_DRIVER_BINDING_PROTOCOL whose Start()\r
+ function is calling this function.\r
+\r
+ @param[in] DevicePath The device path that is installed on\r
+ ControllerHandle.\r
+\r
+ @param[out] ControllerName A dynamically allocated unicode string that\r
+ unconditionally says "Virtio GPU Device", with a\r
+ PCI Segment:Bus:Device.Function location\r
+ optionally appended. The latter part is only\r
+ produced if DevicePath contains at least one\r
+ PciIo node; in that case, the most specific such\r
+ node is used for retrieving the location info.\r
+ The caller is responsible for freeing\r
+ ControllerName after use.\r
+\r
+ @retval EFI_SUCCESS ControllerName has been formatted.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for ControllerName.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+FormatVgpuDevName (\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_HANDLE AgentHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
+ OUT CHAR16 **ControllerName\r
+ )\r
+{\r
+ EFI_HANDLE PciIoHandle;\r
+ EFI_PCI_IO_PROTOCOL *PciIo;\r
+ UINTN Segment, Bus, Device, Function;\r
+ STATIC CONST CHAR16 ControllerNameStem[] = L"Virtio GPU Device";\r
+ UINTN ControllerNameSize;\r
+\r
+ if (EFI_ERROR (gBS->LocateDevicePath (&gEfiPciIoProtocolGuid, &DevicePath,\r
+ &PciIoHandle)) ||\r
+ EFI_ERROR (gBS->OpenProtocol (PciIoHandle, &gEfiPciIoProtocolGuid,\r
+ (VOID **)&PciIo, AgentHandle, ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL)) ||\r
+ EFI_ERROR (PciIo->GetLocation (PciIo, &Segment, &Bus, &Device,\r
+ &Function))) {\r
+ //\r
+ // Failed to retrieve location info, return verbatim copy of static string.\r
+ //\r
+ *ControllerName = AllocateCopyPool (sizeof ControllerNameStem,\r
+ ControllerNameStem);\r
+ return (*ControllerName == NULL) ? EFI_OUT_OF_RESOURCES : EFI_SUCCESS;\r
+ }\r
+ //\r
+ // Location info available, format ControllerName dynamically.\r
+ //\r
+ ControllerNameSize = sizeof ControllerNameStem + // includes L'\0'\r
+ sizeof (CHAR16) * (1 + 4 + // Segment\r
+ 1 + 2 + // Bus\r
+ 1 + 2 + // Device\r
+ 1 + 1 // Function\r
+ );\r
+ *ControllerName = AllocatePool (ControllerNameSize);\r
+ if (*ControllerName == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ UnicodeSPrintAsciiFormat (*ControllerName, ControllerNameSize,\r
+ "%s %04x:%02x:%02x.%x", ControllerNameStem, (UINT32)Segment, (UINT32)Bus,\r
+ (UINT32)Device, (UINT32)Function);\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Dynamically allocate and initialize the VGPU_GOP child object within an\r
+ otherwise configured parent VGPU_DEV object.\r
+\r
+ This function adds a BY_CHILD_CONTROLLER reference to ParentBusController's\r
+ VIRTIO_DEVICE_PROTOCOL interface.\r
+\r
+ @param[in,out] ParentBus The pre-initialized VGPU_DEV object that the\r
+ newly created VGPU_GOP object will be the\r
+ child of.\r
+\r
+ @param[in] ParentDevicePath The device path protocol instance that is\r
+ installed on ParentBusController.\r
+\r
+ @param[in] ParentBusController The UEFI controller handle on which the\r
+ ParentBus VGPU_DEV object and the\r
+ ParentDevicePath device path protocol are\r
+ installed.\r
+\r
+ @param[in] DriverBindingHandle The DriverBindingHandle member of\r
+ EFI_DRIVER_BINDING_PROTOCOL whose Start()\r
+ function is calling this function. It is\r
+ passed as AgentHandle to gBS->OpenProtocol()\r
+ when creating the BY_CHILD_CONTROLLER\r
+ reference.\r
+\r
+ @retval EFI_SUCCESS ParentBus->Child has been created and\r
+ populated, and ParentBus->Child->GopHandle now\r
+ references ParentBusController->VirtIo\r
+ BY_CHILD_CONTROLLER.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.\r
+\r
+ @return Error codes from underlying functions.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+InitVgpuGop (\r
+ IN OUT VGPU_DEV *ParentBus,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath,\r
+ IN EFI_HANDLE ParentBusController,\r
+ IN EFI_HANDLE DriverBindingHandle\r
+ )\r
+{\r
+ VGPU_GOP *VgpuGop;\r
+ EFI_STATUS Status;\r
+ CHAR16 *ParentBusName;\r
+ STATIC CONST CHAR16 NameSuffix[] = L" Head #0";\r
+ UINTN NameSize;\r
+ CHAR16 *Name;\r
+ EFI_TPL OldTpl;\r
+ VOID *ParentVirtIo;\r
+\r
+ VgpuGop = AllocateZeroPool (sizeof *VgpuGop);\r
+ if (VgpuGop == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ VgpuGop->Signature = VGPU_GOP_SIG;\r
+ VgpuGop->ParentBus = ParentBus;\r
+\r
+ //\r
+ // Format a human-readable controller name for VGPU_GOP, and stash it for\r
+ // VirtioGpuGetControllerName() to look up. We simply append NameSuffix to\r
+ // ParentBus->BusName.\r
+ //\r
+ Status = LookupUnicodeString2 ("en", mComponentName2.SupportedLanguages,\r
+ ParentBus->BusName, &ParentBusName, FALSE /* Iso639Language */);\r
+ ASSERT_EFI_ERROR (Status);\r
+ NameSize = StrSize (ParentBusName) - sizeof (CHAR16) + sizeof NameSuffix;\r
+ Name = AllocatePool (NameSize);\r
+ if (Name == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto FreeVgpuGop;\r
+ }\r
+ UnicodeSPrintAsciiFormat (Name, NameSize, "%s%s", ParentBusName, NameSuffix);\r
+ Status = AddUnicodeString2 ("en", mComponentName2.SupportedLanguages,\r
+ &VgpuGop->GopName, Name, FALSE /* Iso639Language */);\r
+ FreePool (Name);\r
+ if (EFI_ERROR (Status)) {\r
+ goto FreeVgpuGop;\r
+ }\r
+\r
+ //\r
+ // Create the child device path.\r
+ //\r
+ VgpuGop->GopDevicePath = AppendDevicePathNode (ParentDevicePath,\r
+ &mAcpiAdr.Header);\r
+ if (VgpuGop->GopDevicePath == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto FreeVgpuGopName;\r
+ }\r
+\r
+ //\r
+ // Mask protocol notify callbacks until we're done.\r
+ //\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ //\r
+ // Create the child handle with the child device path.\r
+ //\r
+ Status = gBS->InstallProtocolInterface (&VgpuGop->GopHandle,\r
+ &gEfiDevicePathProtocolGuid, EFI_NATIVE_INTERFACE,\r
+ VgpuGop->GopDevicePath);\r
+ if (EFI_ERROR (Status)) {\r
+ goto FreeDevicePath;\r
+ }\r
+\r
+ //\r
+ // The child handle must present a reference to the parent handle's Virtio\r
+ // Device Protocol interface.\r
+ //\r
+ Status = gBS->OpenProtocol (ParentBusController, &gVirtioDeviceProtocolGuid,\r
+ &ParentVirtIo, DriverBindingHandle, VgpuGop->GopHandle,\r
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER);\r
+ if (EFI_ERROR (Status)) {\r
+ goto UninstallDevicePath;\r
+ }\r
+ ASSERT (ParentVirtIo == ParentBus->VirtIo);\r
+\r
+ //\r
+ // Initialize our Graphics Output Protocol.\r
+ //\r
+ // This means "nothing" for now.\r
+ //\r
+ Status = EFI_SUCCESS;\r
+ if (EFI_ERROR (Status)) {\r
+ goto CloseVirtIoByChild;\r
+ }\r
+\r
+ //\r
+ // Install the Graphics Output Protocol on the child handle.\r
+ //\r
+ Status = gBS->InstallProtocolInterface (&VgpuGop->GopHandle,\r
+ &mDummyGraphicsOutputProtocolGuid, EFI_NATIVE_INTERFACE,\r
+ &VgpuGop->Gop);\r
+ if (EFI_ERROR (Status)) {\r
+ goto UninitGop;\r
+ }\r
+\r
+ //\r
+ // We're done.\r
+ //\r
+ gBS->RestoreTPL (OldTpl);\r
+ ParentBus->Child = VgpuGop;\r
+ return EFI_SUCCESS;\r
+\r
+UninitGop:\r
+ //\r
+ // Nothing, for now.\r
+ //\r
+\r
+CloseVirtIoByChild:\r
+ gBS->CloseProtocol (ParentBusController, &gVirtioDeviceProtocolGuid,\r
+ DriverBindingHandle, VgpuGop->GopHandle);\r
+\r
+UninstallDevicePath:\r
+ gBS->UninstallProtocolInterface (VgpuGop->GopHandle,\r
+ &gEfiDevicePathProtocolGuid, VgpuGop->GopDevicePath);\r
+\r
+FreeDevicePath:\r
+ gBS->RestoreTPL (OldTpl);\r
+ FreePool (VgpuGop->GopDevicePath);\r
+\r
+FreeVgpuGopName:\r
+ FreeUnicodeStringTable (VgpuGop->GopName);\r
+\r
+FreeVgpuGop:\r
+ FreePool (VgpuGop);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Tear down and release the VGPU_GOP child object within the VGPU_DEV parent\r
+ object.\r
+\r
+ This function removes the BY_CHILD_CONTROLLER reference from\r
+ ParentBusController's VIRTIO_DEVICE_PROTOCOL interface.\r
+\r
+ @param[in,out] ParentBus The VGPU_DEV object that the VGPU_GOP child\r
+ object will be removed from.\r
+\r
+ @param[in] ParentBusController The UEFI controller handle on which the\r
+ ParentBus VGPU_DEV object is installed.\r
+\r
+ @param[in] DriverBindingHandle The DriverBindingHandle member of\r
+ EFI_DRIVER_BINDING_PROTOCOL whose Stop()\r
+ function is calling this function. It is\r
+ passed as AgentHandle to gBS->CloseProtocol()\r
+ when removing the BY_CHILD_CONTROLLER\r
+ reference.\r
+**/\r
+STATIC\r
+VOID\r
+UninitVgpuGop (\r
+ IN OUT VGPU_DEV *ParentBus,\r
+ IN EFI_HANDLE ParentBusController,\r
+ IN EFI_HANDLE DriverBindingHandle\r
+ )\r
+{\r
+ VGPU_GOP *VgpuGop;\r
+ EFI_STATUS Status;\r
+\r
+ VgpuGop = ParentBus->Child;\r
+ Status = gBS->UninstallProtocolInterface (VgpuGop->GopHandle,\r
+ &mDummyGraphicsOutputProtocolGuid, &VgpuGop->Gop);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ //\r
+ // Uninitialize VgpuGop->Gop.\r
+ //\r
+ // Nothing, for now.\r
+ //\r
+ Status = EFI_SUCCESS;\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ Status = gBS->CloseProtocol (ParentBusController, &gVirtioDeviceProtocolGuid,\r
+ DriverBindingHandle, VgpuGop->GopHandle);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ Status = gBS->UninstallProtocolInterface (VgpuGop->GopHandle,\r
+ &gEfiDevicePathProtocolGuid, VgpuGop->GopDevicePath);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ FreePool (VgpuGop->GopDevicePath);\r
+ FreeUnicodeStringTable (VgpuGop->GopName);\r
+ FreePool (VgpuGop);\r
+\r
+ ParentBus->Child = NULL;\r
+}\r
+\r
+//\r
+// Driver Binding Protocol Implementation.\r
+//\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioGpuDriverBindingSupported (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ VIRTIO_DEVICE_PROTOCOL *VirtIo;\r
+\r
+ //\r
+ // - If RemainingDevicePath is NULL: the caller is interested in creating all\r
+ // child handles.\r
+ // - If RemainingDevicePath points to an end node: the caller is not\r
+ // interested in creating any child handle.\r
+ // - Otherwise, the caller would like to create the one child handle\r
+ // specified in RemainingDevicePath. In this case we have to see if the\r
+ // requested device path is supportable.\r
+ //\r
+ if (RemainingDevicePath != NULL &&\r
+ !IsDevicePathEnd (RemainingDevicePath) &&\r
+ (DevicePathNodeLength (RemainingDevicePath) != sizeof mAcpiAdr ||\r
+ CompareMem (RemainingDevicePath, &mAcpiAdr, sizeof mAcpiAdr) != 0)) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // Open the Virtio Device Protocol interface on the controller, BY_DRIVER.\r
+ //\r
+ Status = gBS->OpenProtocol (ControllerHandle, &gVirtioDeviceProtocolGuid,\r
+ (VOID **)&VirtIo, This->DriverBindingHandle,\r
+ ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // If this fails, then by default we cannot support ControllerHandle. There\r
+ // is one exception: we've already bound the device, have not produced any\r
+ // GOP child controller, and now the caller wants us to produce the child\r
+ // controller (either specifically or as part of "all children"). That's\r
+ // allowed.\r
+ //\r
+ if (Status == EFI_ALREADY_STARTED) {\r
+ EFI_STATUS Status2;\r
+ VGPU_DEV *VgpuDev;\r
+\r
+ Status2 = gBS->OpenProtocol (ControllerHandle, &gEfiCallerIdGuid,\r
+ (VOID **)&VgpuDev, This->DriverBindingHandle,\r
+ ControllerHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
+ ASSERT_EFI_ERROR (Status2);\r
+\r
+ if (VgpuDev->Child == NULL &&\r
+ (RemainingDevicePath == NULL ||\r
+ !IsDevicePathEnd (RemainingDevicePath))) {\r
+ Status = EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // First BY_DRIVER open; check the VirtIo revision and subsystem.\r
+ //\r
+ if (VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0) ||\r
+ VirtIo->SubSystemDeviceId != VIRTIO_SUBSYSTEM_GPU_DEVICE) {\r
+ Status = EFI_UNSUPPORTED;\r
+ goto CloseVirtIo;\r
+ }\r
+\r
+ //\r
+ // We'll need the device path of the VirtIo device both for formatting\r
+ // VGPU_DEV.BusName and for populating VGPU_GOP.GopDevicePath.\r
+ //\r
+ Status = gBS->OpenProtocol (ControllerHandle, &gEfiDevicePathProtocolGuid,\r
+ NULL, This->DriverBindingHandle, ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL);\r
+\r
+CloseVirtIo:\r
+ gBS->CloseProtocol (ControllerHandle, &gVirtioDeviceProtocolGuid,\r
+ This->DriverBindingHandle, ControllerHandle);\r
+\r
+ return Status;\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioGpuDriverBindingStart (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ VIRTIO_DEVICE_PROTOCOL *VirtIo;\r
+ BOOLEAN VirtIoBoundJustNow;\r
+ VGPU_DEV *VgpuDev;\r
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
+\r
+ //\r
+ // Open the Virtio Device Protocol.\r
+ //\r
+ // The result of this operation, combined with the checks in\r
+ // VirtioGpuDriverBindingSupported(), uniquely tells us whether we are\r
+ // binding the VirtIo controller on this call (with or without creating child\r
+ // controllers), or else we're *only* creating child controllers.\r
+ //\r
+ Status = gBS->OpenProtocol (ControllerHandle, &gVirtioDeviceProtocolGuid,\r
+ (VOID **)&VirtIo, This->DriverBindingHandle,\r
+ ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // The assertions below are based on the success of\r
+ // VirtioGpuDriverBindingSupported(): we bound ControllerHandle earlier,\r
+ // without producing child handles, and now we're producing the GOP child\r
+ // handle only.\r
+ //\r
+ ASSERT (Status == EFI_ALREADY_STARTED);\r
+\r
+ Status = gBS->OpenProtocol (ControllerHandle, &gEfiCallerIdGuid,\r
+ (VOID **)&VgpuDev, This->DriverBindingHandle,\r
+ ControllerHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ ASSERT (VgpuDev->Child == NULL);\r
+ ASSERT (\r
+ RemainingDevicePath == NULL || !IsDevicePathEnd (RemainingDevicePath));\r
+\r
+ VirtIoBoundJustNow = FALSE;\r
+ } else {\r
+ VirtIoBoundJustNow = TRUE;\r
+\r
+ //\r
+ // Allocate the private structure.\r
+ //\r
+ VgpuDev = AllocateZeroPool (sizeof *VgpuDev);\r
+ if (VgpuDev == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto CloseVirtIo;\r
+ }\r
+ VgpuDev->VirtIo = VirtIo;\r
+ }\r
+\r
+ //\r
+ // Grab the VirtIo controller's device path. This is necessary regardless of\r
+ // VirtIoBoundJustNow.\r
+ //\r
+ Status = gBS->OpenProtocol (ControllerHandle, &gEfiDevicePathProtocolGuid,\r
+ (VOID **)&DevicePath, This->DriverBindingHandle,\r
+ ControllerHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
+ if (EFI_ERROR (Status)) {\r
+ goto FreeVgpuDev;\r
+ }\r
+\r
+ //\r
+ // Create VGPU_DEV if we've bound the VirtIo controller right now (that is,\r
+ // if we aren't *only* creating child handles).\r
+ //\r
+ if (VirtIoBoundJustNow) {\r
+ CHAR16 *VgpuDevName;\r
+\r
+ //\r
+ // Format a human-readable controller name for VGPU_DEV, and stash it for\r
+ // VirtioGpuGetControllerName() to look up.\r
+ //\r
+ Status = FormatVgpuDevName (ControllerHandle, This->DriverBindingHandle,\r
+ DevicePath, &VgpuDevName);\r
+ if (EFI_ERROR (Status)) {\r
+ goto FreeVgpuDev;\r
+ }\r
+ Status = AddUnicodeString2 ("en", mComponentName2.SupportedLanguages,\r
+ &VgpuDev->BusName, VgpuDevName, FALSE /* Iso639Language */);\r
+ FreePool (VgpuDevName);\r
+ if (EFI_ERROR (Status)) {\r
+ goto FreeVgpuDev;\r
+ }\r
+\r
+ //\r
+ // Install the VGPU_DEV "protocol interface" on ControllerHandle.\r
+ //\r
+ Status = gBS->InstallProtocolInterface (&ControllerHandle,\r
+ &gEfiCallerIdGuid, EFI_NATIVE_INTERFACE, VgpuDev);\r
+ if (EFI_ERROR (Status)) {\r
+ goto FreeVgpuDevBusName;\r
+ }\r
+\r
+ if (RemainingDevicePath != NULL && IsDevicePathEnd (RemainingDevicePath)) {\r
+ //\r
+ // No child handle should be produced; we're done.\r
+ //\r
+ DEBUG ((EFI_D_INFO, "%a: bound VirtIo=%p without producing GOP\n",\r
+ __FUNCTION__, (VOID *)VgpuDev->VirtIo));\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Below we'll produce our single child handle: the caller requested it\r
+ // either specifically, or as part of all child handles.\r
+ //\r
+ ASSERT (VgpuDev->Child == NULL);\r
+ ASSERT (\r
+ RemainingDevicePath == NULL || !IsDevicePathEnd (RemainingDevicePath));\r
+\r
+ Status = InitVgpuGop (VgpuDev, DevicePath, ControllerHandle,\r
+ This->DriverBindingHandle);\r
+ if (EFI_ERROR (Status)) {\r
+ goto UninstallVgpuDev;\r
+ }\r
+\r
+ //\r
+ // We're done.\r
+ //\r
+ DEBUG ((EFI_D_INFO, "%a: produced GOP %a VirtIo=%p\n", __FUNCTION__,\r
+ VirtIoBoundJustNow ? "while binding" : "for pre-bound",\r
+ (VOID *)VgpuDev->VirtIo));\r
+ return EFI_SUCCESS;\r
+\r
+UninstallVgpuDev:\r
+ if (VirtIoBoundJustNow) {\r
+ gBS->UninstallProtocolInterface (ControllerHandle, &gEfiCallerIdGuid,\r
+ VgpuDev);\r
+ }\r
+\r
+FreeVgpuDevBusName:\r
+ if (VirtIoBoundJustNow) {\r
+ FreeUnicodeStringTable (VgpuDev->BusName);\r
+ }\r
+\r
+FreeVgpuDev:\r
+ if (VirtIoBoundJustNow) {\r
+ FreePool (VgpuDev);\r
+ }\r
+\r
+CloseVirtIo:\r
+ if (VirtIoBoundJustNow) {\r
+ gBS->CloseProtocol (ControllerHandle, &gVirtioDeviceProtocolGuid,\r
+ This->DriverBindingHandle, ControllerHandle);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioGpuDriverBindingStop (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN UINTN NumberOfChildren,\r
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ VGPU_DEV *VgpuDev;\r
+\r
+ //\r
+ // Look up the VGPU_DEV "protocol interface" on ControllerHandle.\r
+ //\r
+ Status = gBS->OpenProtocol (ControllerHandle, &gEfiCallerIdGuid,\r
+ (VOID **)&VgpuDev, This->DriverBindingHandle,\r
+ ControllerHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ //\r
+ // Sanity check: if we found gEfiCallerIdGuid on ControllerHandle, then we\r
+ // keep its Virtio Device Protocol interface open BY_DRIVER.\r
+ //\r
+ ASSERT_EFI_ERROR (EfiTestManagedDevice (ControllerHandle,\r
+ This->DriverBindingHandle, &gVirtioDeviceProtocolGuid));\r
+\r
+ switch (NumberOfChildren) {\r
+ case 0:\r
+ //\r
+ // The caller wants us to unbind the VirtIo controller.\r
+ //\r
+ if (VgpuDev->Child != NULL) {\r
+ //\r
+ // We still have the GOP child.\r
+ //\r
+ Status = EFI_DEVICE_ERROR;\r
+ break;\r
+ }\r
+\r
+ DEBUG ((EFI_D_INFO, "%a: unbinding GOP-less VirtIo=%p\n", __FUNCTION__,\r
+ (VOID *)VgpuDev->VirtIo));\r
+\r
+ Status = gBS->UninstallProtocolInterface (ControllerHandle,\r
+ &gEfiCallerIdGuid, VgpuDev);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ FreeUnicodeStringTable (VgpuDev->BusName);\r
+ FreePool (VgpuDev);\r
+\r
+ Status = gBS->CloseProtocol (ControllerHandle, &gVirtioDeviceProtocolGuid,\r
+ This->DriverBindingHandle, ControllerHandle);\r
+ ASSERT_EFI_ERROR (Status);\r
+ break;\r
+\r
+ case 1:\r
+ //\r
+ // The caller wants us to destroy our child GOP controller.\r
+ //\r
+ if (VgpuDev->Child == NULL ||\r
+ ChildHandleBuffer[0] != VgpuDev->Child->GopHandle) {\r
+ //\r
+ // We have no child controller at the moment, or it differs from the one\r
+ // the caller wants us to destroy. I.e., we don't own the child\r
+ // controller passed in.\r
+ //\r
+ Status = EFI_DEVICE_ERROR;\r
+ break;\r
+ }\r
+ //\r
+ // Sanity check: our GOP child controller keeps the VGPU_DEV controller's\r
+ // Virtio Device Protocol interface open BY_CHILD_CONTROLLER.\r
+ //\r
+ ASSERT_EFI_ERROR (EfiTestChildHandle (ControllerHandle,\r
+ VgpuDev->Child->GopHandle,\r
+ &gVirtioDeviceProtocolGuid));\r
+\r
+ DEBUG ((EFI_D_INFO, "%a: destroying GOP under VirtIo=%p\n", __FUNCTION__,\r
+ (VOID *)VgpuDev->VirtIo));\r
+ UninitVgpuGop (VgpuDev, ControllerHandle, This->DriverBindingHandle);\r
+ break;\r
+\r
+ default:\r
+ //\r
+ // Impossible, we never produced more than one child.\r
+ //\r
+ Status = EFI_DEVICE_ERROR;\r
+ break;\r
+ }\r
+ return Status;\r
+}\r
+\r
+STATIC EFI_DRIVER_BINDING_PROTOCOL mDriverBinding = {\r
+ VirtioGpuDriverBindingSupported,\r
+ VirtioGpuDriverBindingStart,\r
+ VirtioGpuDriverBindingStop,\r
+ 0x10, // Version\r
+ NULL, // ImageHandle, overwritten in entry point\r
+ NULL // DriverBindingHandle, ditto\r
+};\r
+\r
+//\r
+// Entry point of the driver.\r
+//\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioGpuEntryPoint (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ return EfiLibInstallDriverBindingComponentName2 (ImageHandle, SystemTable,\r
+ &mDriverBinding, ImageHandle, NULL /* ComponentName */,\r
+ &mComponentName2);\r
+}\r