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