From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 59C0A1A1DFB for ; Fri, 19 Aug 2016 05:49:48 -0700 (PDT) Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id E10D43F729; Fri, 19 Aug 2016 12:49:47 +0000 (UTC) Received: from lacos-laptop-7.usersys.redhat.com (ovpn-116-13.phx2.redhat.com [10.3.116.13]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u7JCnaSk011583; Fri, 19 Aug 2016 08:49:46 -0400 From: Laszlo Ersek To: edk2-devel-01 Cc: Ard Biesheuvel , Jordan Justen Date: Fri, 19 Aug 2016 14:49:26 +0200 Message-Id: <20160819124932.29711-6-lersek@redhat.com> In-Reply-To: <20160819124932.29711-1-lersek@redhat.com> References: <20160819124932.29711-1-lersek@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.68 on 10.5.11.24 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.30]); Fri, 19 Aug 2016 12:49:47 +0000 (UTC) Subject: [PATCH 05/11] OvmfPkg/VirtioGpuDxe: introduce with Component Name 2 and Driver Binding X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 19 Aug 2016 12:49:48 -0000 Content-Transfer-Encoding: quoted-printable This patch adds the skeleton of the driver: it implements the Component Name 2 Protocol and the Driver Binding Protocol, in accordance with the generic and GOP-specific requirements set forth in the UEFI spec and the Driver Writers' Guide. The basic idea is that VGPU_DEV abstracts the virtio GPU device, while the single VGPU_GOP that we intend to support at this point stands for "head" (aka "scanout") #0. For now, the Virtio Device Protocol is only used for driver binding; no actual virtio operations are done yet. Similarly, we use a "dummy" GOP GUID and protocol structure (a plain UINT8 object) for now, so that GOP-consuming drivers don't look at what we produce just yet. The driver is a bit different from the other virtio device drivers written thus far: - It implements the GetControllerName() member of the Component Name 2 Protocol. (Formatting helpful names is recommended by UEFI.) As a "best effort", we format the PCI BDF into the name (a PCI backend is not guaranteed by VIRTIO_DEVICE_PROTOCOL). It should provide a more friendly experience in the shell and elsewhere. - This driver seeks to support all RemainingDevicePath cases: - NULL: produce all (=3D one) child handles (=3D VGPU_GOP heads) at once, - End of Device Path Node: produce no child handles, - specific ACPI ADR Node: check if it's supportable, and produce it (only one specific child controller is supported). This is one of the reasons for separating VGPU_GOP from VGPU_DEV. The driver is a hybrid driver: it produces both child handles (one, to be exact), but also installs a structure (VGPU_DEV) directly on the VirtIo controller handle, using gEfiCallerIdGuid as protocol GUID. This is a trick I've seen elsewhere in edk2 (for example, TerminalDxe), and it is necessary for the following reason: In EFI_COMPONENT_NAME2_PROTOCOL.GetControllerName(), we must be able to "cast down" a VirtIo ControllerHandle to our own private data structure (VGPU_DEV). That's only possible if we install the structure directly on the VirtIo ControllerHandle (thereby rendering the driver a hybrid driver), because a child controller with our GOP implementation on it may not exist / be passed in there. Cc: Ard Biesheuvel Cc: Jordan Justen Ref: https://tianocore.acgmultimedia.com/show_bug.cgi?id=3D66 Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Laszlo Ersek --- OvmfPkg/VirtioGpuDxe/VirtioGpu.inf | 47 ++ OvmfPkg/VirtioGpuDxe/VirtioGpu.h | 106 +++ OvmfPkg/VirtioGpuDxe/DriverBinding.c | 831 ++++++++++++++++++++ 3 files changed, 984 insertions(+) diff --git a/OvmfPkg/VirtioGpuDxe/VirtioGpu.inf b/OvmfPkg/VirtioGpuDxe/Virt= ioGpu.inf new file mode 100644 index 000000000000..948350dbce21 --- /dev/null +++ b/OvmfPkg/VirtioGpuDxe/VirtioGpu.inf @@ -0,0 +1,47 @@ +## @file=0D +#=0D +# This hybrid driver produces the Graphics Output Protocol for the Virtio = GPU=0D +# device (head #0, only and unconditionally).=0D +#=0D +# Copyright (C) 2016, Red Hat, Inc.=0D +#=0D +# This program and the accompanying materials are licensed and made availa= ble=0D +# under the terms and conditions of the BSD License which accompanies this= =0D +# distribution. The full text of the license may be found at=0D +# http://opensource.org/licenses/bsd-license.php=0D +#=0D +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WI= THOUT=0D +# WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.=0D +#=0D +##=0D +=0D +[Defines]=0D + INF_VERSION =3D 0x00010005=0D + BASE_NAME =3D VirtioGpuDxe=0D + FILE_GUID =3D D6099B94-CD97-4CC5-8714-7F6312701A8A= =0D + MODULE_TYPE =3D UEFI_DRIVER=0D + VERSION_STRING =3D 1.0=0D + ENTRY_POINT =3D VirtioGpuEntryPoint=0D +=0D +[Sources]=0D + DriverBinding.c=0D + VirtioGpu.h=0D +=0D +[Packages]=0D + MdePkg/MdePkg.dec=0D + OvmfPkg/OvmfPkg.dec=0D +=0D +[LibraryClasses]=0D + BaseMemoryLib=0D + DebugLib=0D + DevicePathLib=0D + MemoryAllocationLib=0D + PrintLib=0D + UefiBootServicesTableLib=0D + UefiDriverEntryPoint=0D + UefiLib=0D +=0D +[Protocols]=0D + gEfiDevicePathProtocolGuid ## TO_START ## BY_START=0D + gEfiPciIoProtocolGuid ## TO_START=0D + gVirtioDeviceProtocolGuid ## TO_START=0D diff --git a/OvmfPkg/VirtioGpuDxe/VirtioGpu.h b/OvmfPkg/VirtioGpuDxe/Virtio= Gpu.h new file mode 100644 index 000000000000..ca5805df8442 --- /dev/null +++ b/OvmfPkg/VirtioGpuDxe/VirtioGpu.h @@ -0,0 +1,106 @@ +/** @file=0D +=0D + Internal type and macro definitions for the Virtio GPU hybrid driver.=0D +=0D + Copyright (C) 2016, Red Hat, Inc.=0D +=0D + This program and the accompanying materials are licensed and made availa= ble=0D + under the terms and conditions of the BSD License which accompanies this= =0D + distribution. The full text of the license may be found at=0D + http://opensource.org/licenses/bsd-license.php=0D +=0D + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WI= THOUT=0D + WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.=0D +=0D +**/=0D +=0D +#ifndef _VIRTIO_GPU_DXE_H_=0D +#define _VIRTIO_GPU_DXE_H_=0D +=0D +#include =0D +#include =0D +#include =0D +=0D +//=0D +// Forward declaration of VGPU_GOP.=0D +//=0D +typedef struct VGPU_GOP_STRUCT VGPU_GOP;=0D +=0D +//=0D +// The abstraction that directly corresponds to a Virtio GPU device.=0D +//=0D +// This structure will be installed on the handle that has the VirtIo Devi= ce=0D +// Protocol interface, with GUID gEfiCallerIdGuid. A similar trick is empl= oyed=0D +// in TerminalDxe, and it is necessary so that we can look up VGPU_DEV jus= t=0D +// from the VirtIo Device Protocol handle in the Component Name 2 Protocol= =0D +// implementation.=0D +//=0D +typedef struct {=0D + //=0D + // VirtIo represents access to the Virtio GPU device. Never NULL.=0D + //=0D + VIRTIO_DEVICE_PROTOCOL *VirtIo;=0D +=0D + //=0D + // BusName carries a customized name for=0D + // EFI_COMPONENT_NAME2_PROTOCOL.GetControllerName(). It is expressed in = table=0D + // form because it can theoretically support several languages. Never NU= LL.=0D + //=0D + EFI_UNICODE_STRING_TABLE *BusName;=0D +=0D + //=0D + // The Child field references the GOP wrapper structure. If this pointer= is=0D + // NULL, then the hybrid driver has bound (i.e., started) the=0D + // VIRTIO_DEVICE_PROTOCOL controller without producing the child GOP=0D + // controller (that is, after Start() was called with RemainingDevicePat= h=0D + // pointing to and End of Device Path node). Child can be created and=0D + // destroyed, even repeatedly, independently of VGPU_DEV.=0D + //=0D + // In practice, this field represents the single head (scanout) that we= =0D + // support.=0D + //=0D + VGPU_GOP *Child;=0D +} VGPU_DEV;=0D +=0D +//=0D +// The Graphics Output Protocol wrapper structure.=0D +//=0D +#define VGPU_GOP_SIG SIGNATURE_64 ('V', 'G', 'P', 'U', '_', 'G', 'O', 'P')= =0D +=0D +struct VGPU_GOP_STRUCT {=0D + UINT64 Signature;=0D +=0D + //=0D + // ParentBus points to the parent VGPU_DEV object. Never NULL.=0D + //=0D + VGPU_DEV *ParentBus;=0D +=0D + //=0D + // GopName carries a customized name for=0D + // EFI_COMPONENT_NAME2_PROTOCOL.GetControllerName(). It is expressed in = table=0D + // form because it can theoretically support several languages. Never NU= LL.=0D + //=0D + EFI_UNICODE_STRING_TABLE *GopName;=0D +=0D + //=0D + // GopHandle is the UEFI child handle that carries the device path endin= g=0D + // with the ACPI ADR node, and the Graphics Output Protocol. Never NULL.= =0D + //=0D + EFI_HANDLE GopHandle;=0D +=0D + //=0D + // The GopDevicePath field is the device path installed on GopHandle,=0D + // ending with an ACPI ADR node. Never NULL.=0D + //=0D + EFI_DEVICE_PATH_PROTOCOL *GopDevicePath;=0D +=0D + //=0D + // The Gop field is installed on the child handle as Graphics Output Pro= tocol=0D + // interface.=0D + //=0D + // For now it is just a placeholder.=0D + //=0D + UINT8 Gop;=0D +};=0D +=0D +#endif // _VIRTIO_GPU_DXE_H_=0D diff --git a/OvmfPkg/VirtioGpuDxe/DriverBinding.c b/OvmfPkg/VirtioGpuDxe/Dr= iverBinding.c new file mode 100644 index 000000000000..b902a07871e0 --- /dev/null +++ b/OvmfPkg/VirtioGpuDxe/DriverBinding.c @@ -0,0 +1,831 @@ +/** @file=0D +=0D + Implement the Driver Binding Protocol and the Component Name 2 Protocol = for=0D + the Virtio GPU hybrid driver.=0D +=0D + Copyright (C) 2016, Red Hat, Inc.=0D +=0D + This program and the accompanying materials are licensed and made availa= ble=0D + under the terms and conditions of the BSD License which accompanies this= =0D + distribution. The full text of the license may be found at=0D + http://opensource.org/licenses/bsd-license.php=0D +=0D + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WI= THOUT=0D + WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.=0D +=0D +**/=0D +=0D +#include =0D +#include =0D +#include =0D +#include =0D +#include =0D +#include =0D +#include =0D +#include =0D +#include =0D +#include =0D +=0D +#include "VirtioGpu.h"=0D +=0D +//=0D +// Dummy Graphics Output Protocol GUID: a temporary placeholder for the EF= I=0D +// counterpart. It will be replaced with the real thing as soon as we impl= ement=0D +// the EFI GOP. Refer to VGPU_GOP.Gop.=0D +//=0D +STATIC EFI_GUID mDummyGraphicsOutputProtocolGuid =3D {=0D + 0x4983f8dc, 0x2782, 0x415b,=0D + { 0x91, 0xf5, 0x2c, 0xeb, 0x48, 0x4a, 0x0f, 0xe9 }=0D +};=0D +=0D +//=0D +// The device path node that describes the Video Output Device Attributes = for=0D +// the single head (UEFI child handle) that we support.=0D +//=0D +// The ACPI_DISPLAY_ADR() macro corresponds to Table B-2, section "B.4.2 _= DOD"=0D +// in the ACPI 3.0b spec, or more recently, to Table B-379, section "B.3.2= =0D +// _DOD" in the ACPI 6.0 spec.=0D +//=0D +STATIC CONST ACPI_ADR_DEVICE_PATH mAcpiAdr =3D {=0D + { // Header=0D + ACPI_DEVICE_PATH, // Type=0D + ACPI_ADR_DP, // SubType=0D + { sizeof mAcpiAdr, 0 }, // Length=0D + },=0D + ACPI_DISPLAY_ADR ( // ADR=0D + 1, // DeviceIdScheme: use the A= CPI=0D + // bit-field definitions=0D + 0, // HeadId=0D + 0, // NonVgaOutput=0D + 1, // BiosCanDetect=0D + 0, // VendorInfo=0D + ACPI_ADR_DISPLAY_TYPE_EXTERNAL_DIGITAL, // Type=0D + 0, // Port=0D + 0 // Index=0D + )=0D +};=0D +=0D +//=0D +// Component Name 2 Protocol implementation.=0D +//=0D +STATIC CONST EFI_UNICODE_STRING_TABLE mDriverNameTable[] =3D {=0D + { "en", L"Virtio GPU Driver" },=0D + { NULL, NULL }=0D +};=0D +=0D +STATIC=0D +EFI_STATUS=0D +EFIAPI=0D +VirtioGpuGetDriverName (=0D + IN EFI_COMPONENT_NAME2_PROTOCOL *This,=0D + IN CHAR8 *Language,=0D + OUT CHAR16 **DriverName=0D + )=0D +{=0D + return LookupUnicodeString2 (Language, This->SupportedLanguages,=0D + mDriverNameTable, DriverName, FALSE /* Iso639Language */);=0D +}=0D +=0D +STATIC=0D +EFI_STATUS=0D +EFIAPI=0D +VirtioGpuGetControllerName (=0D + IN EFI_COMPONENT_NAME2_PROTOCOL *This,=0D + IN EFI_HANDLE ControllerHandle,=0D + IN EFI_HANDLE ChildHandle OPTIONAL,=0D + IN CHAR8 *Language,=0D + OUT CHAR16 **ControllerName=0D + )=0D +{=0D + EFI_STATUS Status;=0D + VGPU_DEV *VgpuDev;=0D +=0D + //=0D + // Look up the VGPU_DEV "protocol interface" on ControllerHandle.=0D + //=0D + Status =3D gBS->OpenProtocol (ControllerHandle, &gEfiCallerIdGuid,=0D + (VOID **)&VgpuDev, gImageHandle, ControllerHandle,=0D + EFI_OPEN_PROTOCOL_GET_PROTOCOL);=0D + if (EFI_ERROR (Status)) {=0D + return Status;=0D + }=0D + //=0D + // Sanity check: if we found gEfiCallerIdGuid on ControllerHandle, then = we=0D + // keep its Virtio Device Protocol interface open BY_DRIVER.=0D + //=0D + ASSERT_EFI_ERROR (EfiTestManagedDevice (ControllerHandle, gImageHandle,= =0D + &gVirtioDeviceProtocolGuid));=0D +=0D + if (ChildHandle =3D=3D NULL) {=0D + //=0D + // The caller is querying the name of the VGPU_DEV controller.=0D + //=0D + return LookupUnicodeString2 (Language, This->SupportedLanguages,=0D + VgpuDev->BusName, ControllerName, FALSE /* Iso639Language */)= ;=0D + }=0D +=0D + //=0D + // Otherwise, the caller is looking for the name of the GOP child contro= ller.=0D + // Check if it is asking about the GOP child controller that we manage. = (The=0D + // condition below covers the case when we haven't produced the GOP chil= d=0D + // controller yet, or we've destroyed it since.)=0D + //=0D + if (VgpuDev->Child =3D=3D NULL || ChildHandle !=3D VgpuDev->Child->GopHa= ndle) {=0D + return EFI_UNSUPPORTED;=0D + }=0D + //=0D + // Sanity check: our GOP child controller keeps the VGPU_DEV controller'= s=0D + // Virtio Device Protocol interface open BY_CHILD_CONTROLLER.=0D + //=0D + ASSERT_EFI_ERROR (EfiTestChildHandle (ControllerHandle, ChildHandle,=0D + &gVirtioDeviceProtocolGuid));=0D +=0D + return LookupUnicodeString2 (Language, This->SupportedLanguages,=0D + VgpuDev->Child->GopName, ControllerName,=0D + FALSE /* Iso639Language */);=0D +}=0D +=0D +STATIC CONST EFI_COMPONENT_NAME2_PROTOCOL mComponentName2 =3D {=0D + VirtioGpuGetDriverName,=0D + VirtioGpuGetControllerName,=0D + "en" // SupportedLanguages (RFC 4646)=0D +};=0D +=0D +//=0D +// Helper functions for the Driver Binding Protocol Implementation.=0D +//=0D +/**=0D + Format the VGPU_DEV controller name, to be looked up and returned by=0D + VirtioGpuGetControllerName().=0D +=0D + @param[in] ControllerHandle The handle that identifies the VGPU_DEV=0D + controller.=0D +=0D + @param[in] AgentHandle The handle of the agent that will attempt t= o=0D + temporarily open the PciIo protocol. This i= s the=0D + DriverBindingHandle member of the=0D + EFI_DRIVER_BINDING_PROTOCOL whose Start()=0D + function is calling this function.=0D +=0D + @param[in] DevicePath The device path that is installed on=0D + ControllerHandle.=0D +=0D + @param[out] ControllerName A dynamically allocated unicode string that= =0D + unconditionally says "Virtio GPU Device", w= ith a=0D + PCI Segment:Bus:Device.Function location=0D + optionally appended. The latter part is onl= y=0D + produced if DevicePath contains at least on= e=0D + PciIo node; in that case, the most specific= such=0D + node is used for retrieving the location in= fo.=0D + The caller is responsible for freeing=0D + ControllerName after use.=0D +=0D + @retval EFI_SUCCESS ControllerName has been formatted.=0D +=0D + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for ControllerNa= me.=0D +**/=0D +STATIC=0D +EFI_STATUS=0D +FormatVgpuDevName (=0D + IN EFI_HANDLE ControllerHandle,=0D + IN EFI_HANDLE AgentHandle,=0D + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,=0D + OUT CHAR16 **ControllerName=0D + )=0D +{=0D + EFI_HANDLE PciIoHandle;=0D + EFI_PCI_IO_PROTOCOL *PciIo;=0D + UINTN Segment, Bus, Device, Function;=0D + STATIC CONST CHAR16 ControllerNameStem[] =3D L"Virtio GPU Device";=0D + UINTN ControllerNameSize;=0D +=0D + if (EFI_ERROR (gBS->LocateDevicePath (&gEfiPciIoProtocolGuid, &DevicePat= h,=0D + &PciIoHandle)) ||=0D + EFI_ERROR (gBS->OpenProtocol (PciIoHandle, &gEfiPciIoProtocolGuid,=0D + (VOID **)&PciIo, AgentHandle, ControllerHandle,=0D + EFI_OPEN_PROTOCOL_GET_PROTOCOL)) ||=0D + EFI_ERROR (PciIo->GetLocation (PciIo, &Segment, &Bus, &Device,=0D + &Function))) {=0D + //=0D + // Failed to retrieve location info, return verbatim copy of static st= ring.=0D + //=0D + *ControllerName =3D AllocateCopyPool (sizeof ControllerNameStem,=0D + ControllerNameStem);=0D + return (*ControllerName =3D=3D NULL) ? EFI_OUT_OF_RESOURCES : EFI_SUCC= ESS;=0D + }=0D + //=0D + // Location info available, format ControllerName dynamically.=0D + //=0D + ControllerNameSize =3D sizeof ControllerNameStem + // includes L'\0'=0D + sizeof (CHAR16) * (1 + 4 + // Segment=0D + 1 + 2 + // Bus=0D + 1 + 2 + // Device=0D + 1 + 1 // Function=0D + );=0D + *ControllerName =3D AllocatePool (ControllerNameSize);=0D + if (*ControllerName =3D=3D NULL) {=0D + return EFI_OUT_OF_RESOURCES;=0D + }=0D +=0D + UnicodeSPrintAsciiFormat (*ControllerName, ControllerNameSize,=0D + "%s %04x:%02x:%02x.%x", ControllerNameStem, (UINT32)Segment, (UINT32)B= us,=0D + (UINT32)Device, (UINT32)Function);=0D + return EFI_SUCCESS;=0D +}=0D +=0D +/**=0D + Dynamically allocate and initialize the VGPU_GOP child object within an= =0D + otherwise configured parent VGPU_DEV object.=0D +=0D + This function adds a BY_CHILD_CONTROLLER reference to ParentBusControlle= r's=0D + VIRTIO_DEVICE_PROTOCOL interface.=0D +=0D + @param[in,out] ParentBus The pre-initialized VGPU_DEV object that= the=0D + newly created VGPU_GOP object will be th= e=0D + child of.=0D +=0D + @param[in] ParentDevicePath The device path protocol instance that i= s=0D + installed on ParentBusController.=0D +=0D + @param[in] ParentBusController The UEFI controller handle on which the= =0D + ParentBus VGPU_DEV object and the=0D + ParentDevicePath device path protocol ar= e=0D + installed.=0D +=0D + @param[in] DriverBindingHandle The DriverBindingHandle member of=0D + EFI_DRIVER_BINDING_PROTOCOL whose Start(= )=0D + function is calling this function. It is= =0D + passed as AgentHandle to gBS->OpenProtoc= ol()=0D + when creating the BY_CHILD_CONTROLLER=0D + reference.=0D +=0D + @retval EFI_SUCCESS ParentBus->Child has been created and=0D + populated, and ParentBus->Child->GopHandle= now=0D + references ParentBusController->VirtIo=0D + BY_CHILD_CONTROLLER.=0D +=0D + @retval EFI_OUT_OF_RESOURCES Memory allocation failed.=0D +=0D + @return Error codes from underlying functions.=0D +**/=0D +STATIC=0D +EFI_STATUS=0D +InitVgpuGop (=0D + IN OUT VGPU_DEV *ParentBus,=0D + IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath,=0D + IN EFI_HANDLE ParentBusController,=0D + IN EFI_HANDLE DriverBindingHandle=0D + )=0D +{=0D + VGPU_GOP *VgpuGop;=0D + EFI_STATUS Status;=0D + CHAR16 *ParentBusName;=0D + STATIC CONST CHAR16 NameSuffix[] =3D L" Head #0";=0D + UINTN NameSize;=0D + CHAR16 *Name;=0D + EFI_TPL OldTpl;=0D + VOID *ParentVirtIo;=0D +=0D + VgpuGop =3D AllocateZeroPool (sizeof *VgpuGop);=0D + if (VgpuGop =3D=3D NULL) {=0D + return EFI_OUT_OF_RESOURCES;=0D + }=0D +=0D + VgpuGop->Signature =3D VGPU_GOP_SIG;=0D + VgpuGop->ParentBus =3D ParentBus;=0D +=0D + //=0D + // Format a human-readable controller name for VGPU_GOP, and stash it fo= r=0D + // VirtioGpuGetControllerName() to look up. We simply append NameSuffix = to=0D + // ParentBus->BusName.=0D + //=0D + Status =3D LookupUnicodeString2 ("en", mComponentName2.SupportedLanguage= s,=0D + ParentBus->BusName, &ParentBusName, FALSE /* Iso639Language *= /);=0D + ASSERT_EFI_ERROR (Status);=0D + NameSize =3D StrSize (ParentBusName) - sizeof (CHAR16) + sizeof NameSuff= ix;=0D + Name =3D AllocatePool (NameSize);=0D + if (Name =3D=3D NULL) {=0D + Status =3D EFI_OUT_OF_RESOURCES;=0D + goto FreeVgpuGop;=0D + }=0D + UnicodeSPrintAsciiFormat (Name, NameSize, "%s%s", ParentBusName, NameSuf= fix);=0D + Status =3D AddUnicodeString2 ("en", mComponentName2.SupportedLanguages,= =0D + &VgpuGop->GopName, Name, FALSE /* Iso639Language */);=0D + FreePool (Name);=0D + if (EFI_ERROR (Status)) {=0D + goto FreeVgpuGop;=0D + }=0D +=0D + //=0D + // Create the child device path.=0D + //=0D + VgpuGop->GopDevicePath =3D AppendDevicePathNode (ParentDevicePath,=0D + &mAcpiAdr.Header);=0D + if (VgpuGop->GopDevicePath =3D=3D NULL) {=0D + Status =3D EFI_OUT_OF_RESOURCES;=0D + goto FreeVgpuGopName;=0D + }=0D +=0D + //=0D + // Mask protocol notify callbacks until we're done.=0D + //=0D + OldTpl =3D gBS->RaiseTPL (TPL_CALLBACK);=0D +=0D + //=0D + // Create the child handle with the child device path.=0D + //=0D + Status =3D gBS->InstallProtocolInterface (&VgpuGop->GopHandle,=0D + &gEfiDevicePathProtocolGuid, EFI_NATIVE_INTERFACE,=0D + VgpuGop->GopDevicePath);=0D + if (EFI_ERROR (Status)) {=0D + goto FreeDevicePath;=0D + }=0D +=0D + //=0D + // The child handle must present a reference to the parent handle's Virt= io=0D + // Device Protocol interface.=0D + //=0D + Status =3D gBS->OpenProtocol (ParentBusController, &gVirtioDeviceProtoco= lGuid,=0D + &ParentVirtIo, DriverBindingHandle, VgpuGop->GopHandle,= =0D + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER);=0D + if (EFI_ERROR (Status)) {=0D + goto UninstallDevicePath;=0D + }=0D + ASSERT (ParentVirtIo =3D=3D ParentBus->VirtIo);=0D +=0D + //=0D + // Initialize our Graphics Output Protocol.=0D + //=0D + // This means "nothing" for now.=0D + //=0D + Status =3D EFI_SUCCESS;=0D + if (EFI_ERROR (Status)) {=0D + goto CloseVirtIoByChild;=0D + }=0D +=0D + //=0D + // Install the Graphics Output Protocol on the child handle.=0D + //=0D + Status =3D gBS->InstallProtocolInterface (&VgpuGop->GopHandle,=0D + &mDummyGraphicsOutputProtocolGuid, EFI_NATIVE_INTERFACE,= =0D + &VgpuGop->Gop);=0D + if (EFI_ERROR (Status)) {=0D + goto UninitGop;=0D + }=0D +=0D + //=0D + // We're done.=0D + //=0D + gBS->RestoreTPL (OldTpl);=0D + ParentBus->Child =3D VgpuGop;=0D + return EFI_SUCCESS;=0D +=0D +UninitGop:=0D + //=0D + // Nothing, for now.=0D + //=0D +=0D +CloseVirtIoByChild:=0D + gBS->CloseProtocol (ParentBusController, &gVirtioDeviceProtocolGuid,=0D + DriverBindingHandle, VgpuGop->GopHandle);=0D +=0D +UninstallDevicePath:=0D + gBS->UninstallProtocolInterface (VgpuGop->GopHandle,=0D + &gEfiDevicePathProtocolGuid, VgpuGop->GopDevicePath);=0D +=0D +FreeDevicePath:=0D + gBS->RestoreTPL (OldTpl);=0D + FreePool (VgpuGop->GopDevicePath);=0D +=0D +FreeVgpuGopName:=0D + FreeUnicodeStringTable (VgpuGop->GopName);=0D +=0D +FreeVgpuGop:=0D + FreePool (VgpuGop);=0D +=0D + return Status;=0D +}=0D +=0D +/**=0D + Tear down and release the VGPU_GOP child object within the VGPU_DEV pare= nt=0D + object.=0D +=0D + This function removes the BY_CHILD_CONTROLLER reference from=0D + ParentBusController's VIRTIO_DEVICE_PROTOCOL interface.=0D +=0D + @param[in,out] ParentBus The VGPU_DEV object that the VGPU_GOP ch= ild=0D + object will be removed from.=0D +=0D + @param[in] ParentBusController The UEFI controller handle on which the= =0D + ParentBus VGPU_DEV object is installed.= =0D +=0D + @param[in] DriverBindingHandle The DriverBindingHandle member of=0D + EFI_DRIVER_BINDING_PROTOCOL whose Stop()= =0D + function is calling this function. It is= =0D + passed as AgentHandle to gBS->CloseProto= col()=0D + when removing the BY_CHILD_CONTROLLER=0D + reference.=0D +**/=0D +STATIC=0D +VOID=0D +UninitVgpuGop (=0D + IN OUT VGPU_DEV *ParentBus,=0D + IN EFI_HANDLE ParentBusController,=0D + IN EFI_HANDLE DriverBindingHandle=0D + )=0D +{=0D + VGPU_GOP *VgpuGop;=0D + EFI_STATUS Status;=0D +=0D + VgpuGop =3D ParentBus->Child;=0D + Status =3D gBS->UninstallProtocolInterface (VgpuGop->GopHandle,=0D + &mDummyGraphicsOutputProtocolGuid, &VgpuGop->Gop);=0D + ASSERT_EFI_ERROR (Status);=0D +=0D + //=0D + // Uninitialize VgpuGop->Gop.=0D + //=0D + // Nothing, for now.=0D + //=0D + Status =3D EFI_SUCCESS;=0D + ASSERT_EFI_ERROR (Status);=0D +=0D + Status =3D gBS->CloseProtocol (ParentBusController, &gVirtioDeviceProtoc= olGuid,=0D + DriverBindingHandle, VgpuGop->GopHandle);=0D + ASSERT_EFI_ERROR (Status);=0D +=0D + Status =3D gBS->UninstallProtocolInterface (VgpuGop->GopHandle,=0D + &gEfiDevicePathProtocolGuid, VgpuGop->GopDevicePath);=0D + ASSERT_EFI_ERROR (Status);=0D +=0D + FreePool (VgpuGop->GopDevicePath);=0D + FreeUnicodeStringTable (VgpuGop->GopName);=0D + FreePool (VgpuGop);=0D +=0D + ParentBus->Child =3D NULL;=0D +}=0D +=0D +//=0D +// Driver Binding Protocol Implementation.=0D +//=0D +STATIC=0D +EFI_STATUS=0D +EFIAPI=0D +VirtioGpuDriverBindingSupported (=0D + IN EFI_DRIVER_BINDING_PROTOCOL *This,=0D + IN EFI_HANDLE ControllerHandle,=0D + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL=0D + )=0D +{=0D + EFI_STATUS Status;=0D + VIRTIO_DEVICE_PROTOCOL *VirtIo;=0D +=0D + //=0D + // - If RemainingDevicePath is NULL: the caller is interested in creatin= g all=0D + // child handles.=0D + // - If RemainingDevicePath points to an end node: the caller is not=0D + // interested in creating any child handle.=0D + // - Otherwise, the caller would like to create the one child handle=0D + // specified in RemainingDevicePath. In this case we have to see if th= e=0D + // requested device path is supportable.=0D + //=0D + if (RemainingDevicePath !=3D NULL &&=0D + !IsDevicePathEnd (RemainingDevicePath) &&=0D + (DevicePathNodeLength (RemainingDevicePath) !=3D sizeof mAcpiAdr ||= =0D + CompareMem (RemainingDevicePath, &mAcpiAdr, sizeof mAcpiAdr) !=3D 0= )) {=0D + return EFI_UNSUPPORTED;=0D + }=0D +=0D + //=0D + // Open the Virtio Device Protocol interface on the controller, BY_DRIVE= R.=0D + //=0D + Status =3D gBS->OpenProtocol (ControllerHandle, &gVirtioDeviceProtocolGu= id,=0D + (VOID **)&VirtIo, This->DriverBindingHandle,=0D + ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);=0D + if (EFI_ERROR (Status)) {=0D + //=0D + // If this fails, then by default we cannot support ControllerHandle. = There=0D + // is one exception: we've already bound the device, have not produced= any=0D + // GOP child controller, and now the caller wants us to produce the ch= ild=0D + // controller (either specifically or as part of "all children"). That= 's=0D + // allowed.=0D + //=0D + if (Status =3D=3D EFI_ALREADY_STARTED) {=0D + EFI_STATUS Status2;=0D + VGPU_DEV *VgpuDev;=0D +=0D + Status2 =3D gBS->OpenProtocol (ControllerHandle, &gEfiCallerIdGuid,= =0D + (VOID **)&VgpuDev, This->DriverBindingHandle,=0D + ControllerHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL);= =0D + ASSERT_EFI_ERROR (Status2);=0D +=0D + if (VgpuDev->Child =3D=3D NULL &&=0D + (RemainingDevicePath =3D=3D NULL ||=0D + !IsDevicePathEnd (RemainingDevicePath))) {=0D + Status =3D EFI_SUCCESS;=0D + }=0D + }=0D +=0D + return Status;=0D + }=0D +=0D + //=0D + // First BY_DRIVER open; check the VirtIo revision and subsystem.=0D + //=0D + if (VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0) ||=0D + VirtIo->SubSystemDeviceId !=3D VIRTIO_SUBSYSTEM_GPU_DEVICE) {=0D + Status =3D EFI_UNSUPPORTED;=0D + goto CloseVirtIo;=0D + }=0D +=0D + //=0D + // We'll need the device path of the VirtIo device both for formatting=0D + // VGPU_DEV.BusName and for populating VGPU_GOP.GopDevicePath.=0D + //=0D + Status =3D gBS->OpenProtocol (ControllerHandle, &gEfiDevicePathProtocolG= uid,=0D + NULL, This->DriverBindingHandle, ControllerHandle,=0D + EFI_OPEN_PROTOCOL_TEST_PROTOCOL);=0D +=0D +CloseVirtIo:=0D + gBS->CloseProtocol (ControllerHandle, &gVirtioDeviceProtocolGuid,=0D + This->DriverBindingHandle, ControllerHandle);=0D +=0D + return Status;=0D +}=0D +=0D +STATIC=0D +EFI_STATUS=0D +EFIAPI=0D +VirtioGpuDriverBindingStart (=0D + IN EFI_DRIVER_BINDING_PROTOCOL *This,=0D + IN EFI_HANDLE ControllerHandle,=0D + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL=0D + )=0D +{=0D + EFI_STATUS Status;=0D + VIRTIO_DEVICE_PROTOCOL *VirtIo;=0D + BOOLEAN VirtIoBoundJustNow;=0D + VGPU_DEV *VgpuDev;=0D + EFI_DEVICE_PATH_PROTOCOL *DevicePath;=0D +=0D + //=0D + // Open the Virtio Device Protocol.=0D + //=0D + // The result of this operation, combined with the checks in=0D + // VirtioGpuDriverBindingSupported(), uniquely tells us whether we are=0D + // binding the VirtIo controller on this call (with or without creating = child=0D + // controllers), or else we're *only* creating child controllers.=0D + //=0D + Status =3D gBS->OpenProtocol (ControllerHandle, &gVirtioDeviceProtocolGu= id,=0D + (VOID **)&VirtIo, This->DriverBindingHandle,=0D + ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);=0D + if (EFI_ERROR (Status)) {=0D + //=0D + // The assertions below are based on the success of=0D + // VirtioGpuDriverBindingSupported(): we bound ControllerHandle earlie= r,=0D + // without producing child handles, and now we're producing the GOP ch= ild=0D + // handle only.=0D + //=0D + ASSERT (Status =3D=3D EFI_ALREADY_STARTED);=0D +=0D + Status =3D gBS->OpenProtocol (ControllerHandle, &gEfiCallerIdGuid,=0D + (VOID **)&VgpuDev, This->DriverBindingHandle,=0D + ControllerHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL);=0D + ASSERT_EFI_ERROR (Status);=0D +=0D + ASSERT (VgpuDev->Child =3D=3D NULL);=0D + ASSERT (=0D + RemainingDevicePath =3D=3D NULL || !IsDevicePathEnd (RemainingDevice= Path));=0D +=0D + VirtIoBoundJustNow =3D FALSE;=0D + } else {=0D + VirtIoBoundJustNow =3D TRUE;=0D +=0D + //=0D + // Allocate the private structure.=0D + //=0D + VgpuDev =3D AllocateZeroPool (sizeof *VgpuDev);=0D + if (VgpuDev =3D=3D NULL) {=0D + Status =3D EFI_OUT_OF_RESOURCES;=0D + goto CloseVirtIo;=0D + }=0D + VgpuDev->VirtIo =3D VirtIo;=0D + }=0D +=0D + //=0D + // Grab the VirtIo controller's device path. This is necessary regardles= s of=0D + // VirtIoBoundJustNow.=0D + //=0D + Status =3D gBS->OpenProtocol (ControllerHandle, &gEfiDevicePathProtocolG= uid,=0D + (VOID **)&DevicePath, This->DriverBindingHandle,=0D + ControllerHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL);=0D + if (EFI_ERROR (Status)) {=0D + goto FreeVgpuDev;=0D + }=0D +=0D + //=0D + // Create VGPU_DEV if we've bound the VirtIo controller right now (that = is,=0D + // if we aren't *only* creating child handles).=0D + //=0D + if (VirtIoBoundJustNow) {=0D + CHAR16 *VgpuDevName;=0D +=0D + //=0D + // Format a human-readable controller name for VGPU_DEV, and stash it = for=0D + // VirtioGpuGetControllerName() to look up.=0D + //=0D + Status =3D FormatVgpuDevName (ControllerHandle, This->DriverBindingHan= dle,=0D + DevicePath, &VgpuDevName);=0D + if (EFI_ERROR (Status)) {=0D + goto FreeVgpuDev;=0D + }=0D + Status =3D AddUnicodeString2 ("en", mComponentName2.SupportedLanguages= ,=0D + &VgpuDev->BusName, VgpuDevName, FALSE /* Iso639Language */)= ;=0D + FreePool (VgpuDevName);=0D + if (EFI_ERROR (Status)) {=0D + goto FreeVgpuDev;=0D + }=0D +=0D + //=0D + // Install the VGPU_DEV "protocol interface" on ControllerHandle.=0D + //=0D + Status =3D gBS->InstallProtocolInterface (&ControllerHandle,=0D + &gEfiCallerIdGuid, EFI_NATIVE_INTERFACE, VgpuDev);=0D + if (EFI_ERROR (Status)) {=0D + goto FreeVgpuDevBusName;=0D + }=0D +=0D + if (RemainingDevicePath !=3D NULL && IsDevicePathEnd (RemainingDeviceP= ath)) {=0D + //=0D + // No child handle should be produced; we're done.=0D + //=0D + DEBUG ((EFI_D_INFO, "%a: bound VirtIo=3D%p without producing GOP\n",= =0D + __FUNCTION__, (VOID *)VgpuDev->VirtIo));=0D + return EFI_SUCCESS;=0D + }=0D + }=0D +=0D + //=0D + // Below we'll produce our single child handle: the caller requested it= =0D + // either specifically, or as part of all child handles.=0D + //=0D + ASSERT (VgpuDev->Child =3D=3D NULL);=0D + ASSERT (=0D + RemainingDevicePath =3D=3D NULL || !IsDevicePathEnd (RemainingDevicePa= th));=0D +=0D + Status =3D InitVgpuGop (VgpuDev, DevicePath, ControllerHandle,=0D + This->DriverBindingHandle);=0D + if (EFI_ERROR (Status)) {=0D + goto UninstallVgpuDev;=0D + }=0D +=0D + //=0D + // We're done.=0D + //=0D + DEBUG ((EFI_D_INFO, "%a: produced GOP %a VirtIo=3D%p\n", __FUNCTION__,=0D + VirtIoBoundJustNow ? "while binding" : "for pre-bound",=0D + (VOID *)VgpuDev->VirtIo));=0D + return EFI_SUCCESS;=0D +=0D +UninstallVgpuDev:=0D + if (VirtIoBoundJustNow) {=0D + gBS->UninstallProtocolInterface (ControllerHandle, &gEfiCallerIdGuid,= =0D + VgpuDev);=0D + }=0D +=0D +FreeVgpuDevBusName:=0D + if (VirtIoBoundJustNow) {=0D + FreeUnicodeStringTable (VgpuDev->BusName);=0D + }=0D +=0D +FreeVgpuDev:=0D + if (VirtIoBoundJustNow) {=0D + FreePool (VgpuDev);=0D + }=0D +=0D +CloseVirtIo:=0D + if (VirtIoBoundJustNow) {=0D + gBS->CloseProtocol (ControllerHandle, &gVirtioDeviceProtocolGuid,=0D + This->DriverBindingHandle, ControllerHandle);=0D + }=0D +=0D + return Status;=0D +}=0D +=0D +STATIC=0D +EFI_STATUS=0D +EFIAPI=0D +VirtioGpuDriverBindingStop (=0D + IN EFI_DRIVER_BINDING_PROTOCOL *This,=0D + IN EFI_HANDLE ControllerHandle,=0D + IN UINTN NumberOfChildren,=0D + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL=0D + )=0D +{=0D + EFI_STATUS Status;=0D + VGPU_DEV *VgpuDev;=0D +=0D + //=0D + // Look up the VGPU_DEV "protocol interface" on ControllerHandle.=0D + //=0D + Status =3D gBS->OpenProtocol (ControllerHandle, &gEfiCallerIdGuid,=0D + (VOID **)&VgpuDev, This->DriverBindingHandle,=0D + ControllerHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL);=0D + if (EFI_ERROR (Status)) {=0D + return Status;=0D + }=0D + //=0D + // Sanity check: if we found gEfiCallerIdGuid on ControllerHandle, then = we=0D + // keep its Virtio Device Protocol interface open BY_DRIVER.=0D + //=0D + ASSERT_EFI_ERROR (EfiTestManagedDevice (ControllerHandle,=0D + This->DriverBindingHandle, &gVirtioDeviceProtocolGui= d));=0D +=0D + switch (NumberOfChildren) {=0D + case 0:=0D + //=0D + // The caller wants us to unbind the VirtIo controller.=0D + //=0D + if (VgpuDev->Child !=3D NULL) {=0D + //=0D + // We still have the GOP child.=0D + //=0D + Status =3D EFI_DEVICE_ERROR;=0D + break;=0D + }=0D +=0D + DEBUG ((EFI_D_INFO, "%a: unbinding GOP-less VirtIo=3D%p\n", __FUNCTION= __,=0D + (VOID *)VgpuDev->VirtIo));=0D +=0D + Status =3D gBS->UninstallProtocolInterface (ControllerHandle,=0D + &gEfiCallerIdGuid, VgpuDev);=0D + ASSERT_EFI_ERROR (Status);=0D +=0D + FreeUnicodeStringTable (VgpuDev->BusName);=0D + FreePool (VgpuDev);=0D +=0D + Status =3D gBS->CloseProtocol (ControllerHandle, &gVirtioDeviceProtoco= lGuid,=0D + This->DriverBindingHandle, ControllerHandle);=0D + ASSERT_EFI_ERROR (Status);=0D + break;=0D +=0D + case 1:=0D + //=0D + // The caller wants us to destroy our child GOP controller.=0D + //=0D + if (VgpuDev->Child =3D=3D NULL ||=0D + ChildHandleBuffer[0] !=3D VgpuDev->Child->GopHandle) {=0D + //=0D + // We have no child controller at the moment, or it differs from the= one=0D + // the caller wants us to destroy. I.e., we don't own the child=0D + // controller passed in.=0D + //=0D + Status =3D EFI_DEVICE_ERROR;=0D + break;=0D + }=0D + //=0D + // Sanity check: our GOP child controller keeps the VGPU_DEV controlle= r's=0D + // Virtio Device Protocol interface open BY_CHILD_CONTROLLER.=0D + //=0D + ASSERT_EFI_ERROR (EfiTestChildHandle (ControllerHandle,=0D + VgpuDev->Child->GopHandle,=0D + &gVirtioDeviceProtocolGuid));=0D +=0D + DEBUG ((EFI_D_INFO, "%a: destroying GOP under VirtIo=3D%p\n", __FUNCTI= ON__,=0D + (VOID *)VgpuDev->VirtIo));=0D + UninitVgpuGop (VgpuDev, ControllerHandle, This->DriverBindingHandle);= =0D + break;=0D +=0D + default:=0D + //=0D + // Impossible, we never produced more than one child.=0D + //=0D + Status =3D EFI_DEVICE_ERROR;=0D + break;=0D + }=0D + return Status;=0D +}=0D +=0D +STATIC EFI_DRIVER_BINDING_PROTOCOL mDriverBinding =3D {=0D + VirtioGpuDriverBindingSupported,=0D + VirtioGpuDriverBindingStart,=0D + VirtioGpuDriverBindingStop,=0D + 0x10, // Version=0D + NULL, // ImageHandle, overwritten in entry po= int=0D + NULL // DriverBindingHandle, ditto=0D +};=0D +=0D +//=0D +// Entry point of the driver.=0D +//=0D +EFI_STATUS=0D +EFIAPI=0D +VirtioGpuEntryPoint (=0D + IN EFI_HANDLE ImageHandle,=0D + IN EFI_SYSTEM_TABLE *SystemTable=0D + )=0D +{=0D + return EfiLibInstallDriverBindingComponentName2 (ImageHandle, SystemTabl= e,=0D + &mDriverBinding, ImageHandle, NULL /* ComponentName */,=0D + &mComponentName2);=0D +}=0D --=20 2.9.2