blob: 666d577547322ebd825ab119d0c95c1171c17729 [file] [log] [blame]
/** @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 <Library/DevicePathLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/PrintLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Protocol/ComponentName2.h>
#include <Protocol/DevicePath.h>
#include <Protocol/DriverBinding.h>
#include <Protocol/PciIo.h>
#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);
}