mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-01 12:53:27 +01:00
1058 lines
31 KiB
C
1058 lines
31 KiB
C
/** @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",
|
|
__func__,
|
|
(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",
|
|
__func__,
|
|
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",
|
|
__func__,
|
|
(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",
|
|
__func__,
|
|
(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
|
|
);
|
|
}
|