summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/OvmfPkg/VirtioGpuDxe
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Devices/EFI/Firmware/OvmfPkg/VirtioGpuDxe
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/OvmfPkg/VirtioGpuDxe')
-rw-r--r--src/VBox/Devices/EFI/Firmware/OvmfPkg/VirtioGpuDxe/Commands.c767
-rw-r--r--src/VBox/Devices/EFI/Firmware/OvmfPkg/VirtioGpuDxe/DriverBinding.c837
-rw-r--r--src/VBox/Devices/EFI/Firmware/OvmfPkg/VirtioGpuDxe/Gop.c656
-rw-r--r--src/VBox/Devices/EFI/Firmware/OvmfPkg/VirtioGpuDxe/VirtioGpu.h402
-rw-r--r--src/VBox/Devices/EFI/Firmware/OvmfPkg/VirtioGpuDxe/VirtioGpu.inf45
5 files changed, 2707 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/OvmfPkg/VirtioGpuDxe/Commands.c b/src/VBox/Devices/EFI/Firmware/OvmfPkg/VirtioGpuDxe/Commands.c
new file mode 100644
index 00000000..fd44f585
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/OvmfPkg/VirtioGpuDxe/Commands.c
@@ -0,0 +1,767 @@
+/** @file
+
+ VirtIo GPU initialization, and commands (primitives) for the GPU device.
+
+ Copyright (C) 2016, Red Hat, Inc.
+ Copyright (c) 2017, AMD Inc, All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/VirtioLib.h>
+
+#include "VirtioGpu.h"
+
+/**
+ Configure the VirtIo GPU device that underlies VgpuDev.
+
+ @param[in,out] VgpuDev The VGPU_DEV object to set up VirtIo messaging for.
+ On input, the caller is responsible for having
+ initialized VgpuDev->VirtIo. On output, VgpuDev->Ring
+ has been initialized, and synchronous VirtIo GPU
+ commands (primitives) can be submitted to the device.
+
+ @retval EFI_SUCCESS VirtIo GPU configuration successful.
+
+ @retval EFI_UNSUPPORTED The host-side configuration of the VirtIo GPU is not
+ supported by this driver.
+
+ @retval Error codes from underlying functions.
+**/
+EFI_STATUS
+VirtioGpuInit (
+ IN OUT VGPU_DEV *VgpuDev
+ )
+{
+ UINT8 NextDevStat;
+ EFI_STATUS Status;
+ UINT64 Features;
+ UINT16 QueueSize;
+ UINT64 RingBaseShift;
+
+ //
+ // Execute virtio-v1.0-cs04, 3.1.1 Driver Requirements: Device
+ // Initialization.
+ //
+ // 1. Reset the device.
+ //
+ NextDevStat = 0;
+ Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+
+ //
+ // 2. Set the ACKNOWLEDGE status bit [...]
+ //
+ NextDevStat |= VSTAT_ACK;
+ Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+
+ //
+ // 3. Set the DRIVER status bit [...]
+ //
+ NextDevStat |= VSTAT_DRIVER;
+ Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+
+ //
+ // 4. Read device feature bits...
+ //
+ Status = VgpuDev->VirtIo->GetDeviceFeatures (VgpuDev->VirtIo, &Features);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+ if ((Features & VIRTIO_F_VERSION_1) == 0) {
+ Status = EFI_UNSUPPORTED;
+ goto Failed;
+ }
+ //
+ // We only want the most basic 2D features.
+ //
+ Features &= VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM;
+
+ //
+ // ... and write the subset of feature bits understood by the [...] driver to
+ // the device. [...]
+ // 5. Set the FEATURES_OK status bit.
+ // 6. Re-read device status to ensure the FEATURES_OK bit is still set [...]
+ //
+ Status = Virtio10WriteFeatures (VgpuDev->VirtIo, Features, &NextDevStat);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+
+ //
+ // 7. Perform device-specific setup, including discovery of virtqueues for
+ // the device [...]
+ //
+ Status = VgpuDev->VirtIo->SetQueueSel (VgpuDev->VirtIo,
+ VIRTIO_GPU_CONTROL_QUEUE);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+ Status = VgpuDev->VirtIo->GetQueueNumMax (VgpuDev->VirtIo, &QueueSize);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+
+ //
+ // We implement each VirtIo GPU command that we use with two descriptors:
+ // request, response.
+ //
+ if (QueueSize < 2) {
+ Status = EFI_UNSUPPORTED;
+ goto Failed;
+ }
+
+ //
+ // [...] population of virtqueues [...]
+ //
+ Status = VirtioRingInit (VgpuDev->VirtIo, QueueSize, &VgpuDev->Ring);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+ //
+ // If anything fails from here on, we have to release the ring.
+ //
+ Status = VirtioRingMap (
+ VgpuDev->VirtIo,
+ &VgpuDev->Ring,
+ &RingBaseShift,
+ &VgpuDev->RingMap
+ );
+ if (EFI_ERROR (Status)) {
+ goto ReleaseQueue;
+ }
+ //
+ // If anything fails from here on, we have to unmap the ring.
+ //
+ Status = VgpuDev->VirtIo->SetQueueAddress (
+ VgpuDev->VirtIo,
+ &VgpuDev->Ring,
+ RingBaseShift
+ );
+ if (EFI_ERROR (Status)) {
+ goto UnmapQueue;
+ }
+
+ //
+ // 8. Set the DRIVER_OK status bit.
+ //
+ NextDevStat |= VSTAT_DRIVER_OK;
+ Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);
+ if (EFI_ERROR (Status)) {
+ goto UnmapQueue;
+ }
+
+ return EFI_SUCCESS;
+
+UnmapQueue:
+ VgpuDev->VirtIo->UnmapSharedBuffer (VgpuDev->VirtIo, VgpuDev->RingMap);
+
+ReleaseQueue:
+ VirtioRingUninit (VgpuDev->VirtIo, &VgpuDev->Ring);
+
+Failed:
+ //
+ // If any of these steps go irrecoverably wrong, the driver SHOULD set the
+ // FAILED status bit to indicate that it has given up on the device (it can
+ // reset the device later to restart if desired). [...]
+ //
+ // VirtIo access failure here should not mask the original error.
+ //
+ NextDevStat |= VSTAT_FAILED;
+ VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);
+
+ return Status;
+}
+
+/**
+ De-configure the VirtIo GPU device that underlies VgpuDev.
+
+ @param[in,out] VgpuDev The VGPU_DEV object to tear down VirtIo messaging
+ for. On input, the caller is responsible for having
+ called VirtioGpuInit(). On output, VgpuDev->Ring has
+ been uninitialized; VirtIo GPU commands (primitives)
+ can no longer be submitted to the device.
+**/
+VOID
+VirtioGpuUninit (
+ IN OUT VGPU_DEV *VgpuDev
+ )
+{
+ //
+ // Resetting the VirtIo device makes it release its resources and forget its
+ // configuration.
+ //
+ VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, 0);
+ VgpuDev->VirtIo->UnmapSharedBuffer (VgpuDev->VirtIo, VgpuDev->RingMap);
+ VirtioRingUninit (VgpuDev->VirtIo, &VgpuDev->Ring);
+}
+
+/**
+ Allocate, zero and map memory, for bus master common buffer operation, to be
+ attached as backing store to a host-side VirtIo GPU resource.
+
+ @param[in] VgpuDev The VGPU_DEV object that represents the VirtIo GPU
+ device.
+
+ @param[in] NumberOfPages The number of whole pages to allocate and map.
+
+ @param[out] HostAddress The system memory address of the allocated area.
+
+ @param[out] DeviceAddress The bus master device address of the allocated
+ area. The VirtIo GPU device may be programmed to
+ access the allocated area through DeviceAddress;
+ DeviceAddress is to be passed to the
+ VirtioGpuResourceAttachBacking() function, as the
+ BackingStoreDeviceAddress parameter.
+
+ @param[out] Mapping A resulting token to pass to
+ VirtioGpuUnmapAndFreeBackingStore().
+
+ @retval EFI_SUCCESS The requested number of pages has been allocated, zeroed
+ and mapped.
+
+ @return Status codes propagated from
+ VgpuDev->VirtIo->AllocateSharedPages() and
+ VirtioMapAllBytesInSharedBuffer().
+**/
+EFI_STATUS
+VirtioGpuAllocateZeroAndMapBackingStore (
+ IN VGPU_DEV *VgpuDev,
+ IN UINTN NumberOfPages,
+ OUT VOID **HostAddress,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT VOID **Mapping
+ )
+{
+ EFI_STATUS Status;
+ VOID *NewHostAddress;
+
+ Status = VgpuDev->VirtIo->AllocateSharedPages (
+ VgpuDev->VirtIo,
+ NumberOfPages,
+ &NewHostAddress
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Avoid exposing stale data to the device even temporarily: zero the area
+ // before mapping it.
+ //
+ ZeroMem (NewHostAddress, EFI_PAGES_TO_SIZE (NumberOfPages));
+
+ Status = VirtioMapAllBytesInSharedBuffer (
+ VgpuDev->VirtIo, // VirtIo
+ VirtioOperationBusMasterCommonBuffer, // Operation
+ NewHostAddress, // HostAddress
+ EFI_PAGES_TO_SIZE (NumberOfPages), // NumberOfBytes
+ DeviceAddress, // DeviceAddress
+ Mapping // Mapping
+ );
+ if (EFI_ERROR (Status)) {
+ goto FreeSharedPages;
+ }
+
+ *HostAddress = NewHostAddress;
+ return EFI_SUCCESS;
+
+FreeSharedPages:
+ VgpuDev->VirtIo->FreeSharedPages (
+ VgpuDev->VirtIo,
+ NumberOfPages,
+ NewHostAddress
+ );
+ return Status;
+}
+
+/**
+ Unmap and free memory originally allocated and mapped with
+ VirtioGpuAllocateZeroAndMapBackingStore().
+
+ If the memory allocated and mapped with
+ VirtioGpuAllocateZeroAndMapBackingStore() was attached to a host-side VirtIo
+ GPU resource with VirtioGpuResourceAttachBacking(), then the caller is
+ responsible for detaching the backing store from the same resource, with
+ VirtioGpuResourceDetachBacking(), before calling this function.
+
+ @param[in] VgpuDev The VGPU_DEV object that represents the VirtIo GPU
+ device.
+
+ @param[in] NumberOfPages The NumberOfPages parameter originally passed to
+ VirtioGpuAllocateZeroAndMapBackingStore().
+
+ @param[in] HostAddress The HostAddress value originally output by
+ VirtioGpuAllocateZeroAndMapBackingStore().
+
+ @param[in] Mapping The token that was originally output by
+ VirtioGpuAllocateZeroAndMapBackingStore().
+**/
+VOID
+VirtioGpuUnmapAndFreeBackingStore (
+ IN VGPU_DEV *VgpuDev,
+ IN UINTN NumberOfPages,
+ IN VOID *HostAddress,
+ IN VOID *Mapping
+ )
+{
+ VgpuDev->VirtIo->UnmapSharedBuffer (
+ VgpuDev->VirtIo,
+ Mapping
+ );
+ VgpuDev->VirtIo->FreeSharedPages (
+ VgpuDev->VirtIo,
+ NumberOfPages,
+ HostAddress
+ );
+}
+
+/**
+ EFI_EVENT_NOTIFY function for the VGPU_DEV.ExitBoot event. It resets the
+ VirtIo device, causing it to release its resources and to forget its
+ configuration.
+
+ This function may only be called (that is, VGPU_DEV.ExitBoot may only be
+ signaled) after VirtioGpuInit() returns and before VirtioGpuUninit() is
+ called.
+
+ @param[in] Event Event whose notification function is being invoked.
+
+ @param[in] Context Pointer to the associated VGPU_DEV object.
+**/
+VOID
+EFIAPI
+VirtioGpuExitBoot (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ VGPU_DEV *VgpuDev;
+
+ DEBUG ((DEBUG_VERBOSE, "%a: Context=0x%p\n", __FUNCTION__, Context));
+ VgpuDev = Context;
+ VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, 0);
+}
+
+/**
+ Internal utility function that sends a request to the VirtIo GPU device
+ model, awaits the answer from the host, and returns a status.
+
+ @param[in,out] VgpuDev The VGPU_DEV object that represents the VirtIo GPU
+ device. The caller is responsible to have
+ successfully invoked VirtioGpuInit() on VgpuDev
+ previously, while VirtioGpuUninit() must not have
+ been called on VgpuDev.
+
+ @param[in] RequestType The type of the request. The caller is responsible
+ for providing a VirtioGpuCmd* RequestType which, on
+ success, elicits a VirtioGpuRespOkNodata response
+ from the host.
+
+ @param[in] Fence Whether to enable fencing for this request. Fencing
+ forces the host to complete the command before
+ producing a response. If Fence is TRUE, then
+ VgpuDev->FenceId is consumed, and incremented.
+
+ @param[in,out] Header Pointer to the caller-allocated request object. The
+ request must start with VIRTIO_GPU_CONTROL_HEADER.
+ This function overwrites all fields of Header before
+ submitting the request to the host:
+
+ - it sets Type from RequestType,
+
+ - it sets Flags and FenceId based on Fence,
+
+ - it zeroes CtxId and Padding.
+
+ @param[in] RequestSize Size of the entire caller-allocated request object,
+ including the leading VIRTIO_GPU_CONTROL_HEADER.
+
+ @retval EFI_SUCCESS Operation successful.
+
+ @retval EFI_DEVICE_ERROR The host rejected the request. The host error
+ code has been logged on the DEBUG_ERROR level.
+
+ @return Codes for unexpected errors in VirtIo
+ messaging, or request/response
+ mapping/unmapping.
+**/
+STATIC
+EFI_STATUS
+VirtioGpuSendCommand (
+ IN OUT VGPU_DEV *VgpuDev,
+ IN VIRTIO_GPU_CONTROL_TYPE RequestType,
+ IN BOOLEAN Fence,
+ IN OUT volatile VIRTIO_GPU_CONTROL_HEADER *Header,
+ IN UINTN RequestSize
+ )
+{
+ DESC_INDICES Indices;
+ volatile VIRTIO_GPU_CONTROL_HEADER Response;
+ EFI_STATUS Status;
+ UINT32 ResponseSize;
+ EFI_PHYSICAL_ADDRESS RequestDeviceAddress;
+ VOID *RequestMap;
+ EFI_PHYSICAL_ADDRESS ResponseDeviceAddress;
+ VOID *ResponseMap;
+
+ //
+ // Initialize Header.
+ //
+ Header->Type = RequestType;
+ if (Fence) {
+ Header->Flags = VIRTIO_GPU_FLAG_FENCE;
+ Header->FenceId = VgpuDev->FenceId++;
+ } else {
+ Header->Flags = 0;
+ Header->FenceId = 0;
+ }
+ Header->CtxId = 0;
+ Header->Padding = 0;
+
+ ASSERT (RequestSize >= sizeof *Header);
+ ASSERT (RequestSize <= MAX_UINT32);
+
+ //
+ // Map request and response to bus master device addresses.
+ //
+ Status = VirtioMapAllBytesInSharedBuffer (
+ VgpuDev->VirtIo,
+ VirtioOperationBusMasterRead,
+ (VOID *)Header,
+ RequestSize,
+ &RequestDeviceAddress,
+ &RequestMap
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Status = VirtioMapAllBytesInSharedBuffer (
+ VgpuDev->VirtIo,
+ VirtioOperationBusMasterWrite,
+ (VOID *)&Response,
+ sizeof Response,
+ &ResponseDeviceAddress,
+ &ResponseMap
+ );
+ if (EFI_ERROR (Status)) {
+ goto UnmapRequest;
+ }
+
+ //
+ // Compose the descriptor chain.
+ //
+ VirtioPrepare (&VgpuDev->Ring, &Indices);
+ VirtioAppendDesc (
+ &VgpuDev->Ring,
+ RequestDeviceAddress,
+ (UINT32)RequestSize,
+ VRING_DESC_F_NEXT,
+ &Indices
+ );
+ VirtioAppendDesc (
+ &VgpuDev->Ring,
+ ResponseDeviceAddress,
+ (UINT32)sizeof Response,
+ VRING_DESC_F_WRITE,
+ &Indices
+ );
+
+ //
+ // Send the command.
+ //
+ Status = VirtioFlush (VgpuDev->VirtIo, VIRTIO_GPU_CONTROL_QUEUE,
+ &VgpuDev->Ring, &Indices, &ResponseSize);
+ if (EFI_ERROR (Status)) {
+ goto UnmapResponse;
+ }
+
+ //
+ // Verify response size.
+ //
+ if (ResponseSize != sizeof Response) {
+ DEBUG ((DEBUG_ERROR, "%a: malformed response to Request=0x%x\n",
+ __FUNCTION__, (UINT32)RequestType));
+ Status = EFI_PROTOCOL_ERROR;
+ goto UnmapResponse;
+ }
+
+ //
+ // Unmap response and request, in reverse order of mapping. On error, the
+ // respective mapping is invalidated anyway, only the data may not have been
+ // committed to system memory (in case of VirtioOperationBusMasterWrite).
+ //
+ Status = VgpuDev->VirtIo->UnmapSharedBuffer (VgpuDev->VirtIo, ResponseMap);
+ if (EFI_ERROR (Status)) {
+ goto UnmapRequest;
+ }
+ Status = VgpuDev->VirtIo->UnmapSharedBuffer (VgpuDev->VirtIo, RequestMap);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Parse the response.
+ //
+ if (Response.Type == VirtioGpuRespOkNodata) {
+ return EFI_SUCCESS;
+ }
+
+ DEBUG ((DEBUG_ERROR, "%a: Request=0x%x Response=0x%x\n", __FUNCTION__,
+ (UINT32)RequestType, Response.Type));
+ return EFI_DEVICE_ERROR;
+
+UnmapResponse:
+ VgpuDev->VirtIo->UnmapSharedBuffer (VgpuDev->VirtIo, ResponseMap);
+
+UnmapRequest:
+ VgpuDev->VirtIo->UnmapSharedBuffer (VgpuDev->VirtIo, RequestMap);
+
+ return Status;
+}
+
+/**
+ The following functions send requests to the VirtIo GPU device model, await
+ the answer from the host, and return a status. They share the following
+ interface details:
+
+ @param[in,out] VgpuDev The VGPU_DEV object that represents the VirtIo GPU
+ device. The caller is responsible to have
+ successfully invoked VirtioGpuInit() on VgpuDev
+ previously, while VirtioGpuUninit() must not have
+ been called on VgpuDev.
+
+ @retval EFI_INVALID_PARAMETER Invalid command-specific parameters were
+ detected by this driver.
+
+ @retval EFI_SUCCESS Operation successful.
+
+ @retval EFI_DEVICE_ERROR The host rejected the request. The host error
+ code has been logged on the DEBUG_ERROR level.
+
+ @return Codes for unexpected errors in VirtIo
+ messaging.
+
+ For the command-specific parameters, please consult the GPU Device section of
+ the VirtIo 1.0 specification (see references in
+ "OvmfPkg/Include/IndustryStandard/VirtioGpu.h").
+**/
+EFI_STATUS
+VirtioGpuResourceCreate2d (
+ IN OUT VGPU_DEV *VgpuDev,
+ IN UINT32 ResourceId,
+ IN VIRTIO_GPU_FORMATS Format,
+ IN UINT32 Width,
+ IN UINT32 Height
+ )
+{
+ volatile VIRTIO_GPU_RESOURCE_CREATE_2D Request;
+
+ if (ResourceId == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Request.ResourceId = ResourceId;
+ Request.Format = (UINT32)Format;
+ Request.Width = Width;
+ Request.Height = Height;
+
+ return VirtioGpuSendCommand (
+ VgpuDev,
+ VirtioGpuCmdResourceCreate2d,
+ FALSE, // Fence
+ &Request.Header,
+ sizeof Request
+ );
+}
+
+EFI_STATUS
+VirtioGpuResourceUnref (
+ IN OUT VGPU_DEV *VgpuDev,
+ IN UINT32 ResourceId
+ )
+{
+ volatile VIRTIO_GPU_RESOURCE_UNREF Request;
+
+ if (ResourceId == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Request.ResourceId = ResourceId;
+ Request.Padding = 0;
+
+ return VirtioGpuSendCommand (
+ VgpuDev,
+ VirtioGpuCmdResourceUnref,
+ FALSE, // Fence
+ &Request.Header,
+ sizeof Request
+ );
+}
+
+EFI_STATUS
+VirtioGpuResourceAttachBacking (
+ IN OUT VGPU_DEV *VgpuDev,
+ IN UINT32 ResourceId,
+ IN EFI_PHYSICAL_ADDRESS BackingStoreDeviceAddress,
+ IN UINTN NumberOfPages
+ )
+{
+ volatile VIRTIO_GPU_RESOURCE_ATTACH_BACKING Request;
+
+ if (ResourceId == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Request.ResourceId = ResourceId;
+ Request.NrEntries = 1;
+ Request.Entry.Addr = BackingStoreDeviceAddress;
+ Request.Entry.Length = (UINT32)EFI_PAGES_TO_SIZE (NumberOfPages);
+ Request.Entry.Padding = 0;
+
+ return VirtioGpuSendCommand (
+ VgpuDev,
+ VirtioGpuCmdResourceAttachBacking,
+ FALSE, // Fence
+ &Request.Header,
+ sizeof Request
+ );
+}
+
+EFI_STATUS
+VirtioGpuResourceDetachBacking (
+ IN OUT VGPU_DEV *VgpuDev,
+ IN UINT32 ResourceId
+ )
+{
+ volatile VIRTIO_GPU_RESOURCE_DETACH_BACKING Request;
+
+ if (ResourceId == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Request.ResourceId = ResourceId;
+ Request.Padding = 0;
+
+ //
+ // In this case, we set Fence to TRUE, because after this function returns,
+ // the caller might reasonably want to repurpose the backing pages
+ // immediately. Thus we should ensure that the host releases all references
+ // to the backing pages before we return.
+ //
+ return VirtioGpuSendCommand (
+ VgpuDev,
+ VirtioGpuCmdResourceDetachBacking,
+ TRUE, // Fence
+ &Request.Header,
+ sizeof Request
+ );
+}
+
+EFI_STATUS
+VirtioGpuSetScanout (
+ IN OUT VGPU_DEV *VgpuDev,
+ IN UINT32 X,
+ IN UINT32 Y,
+ IN UINT32 Width,
+ IN UINT32 Height,
+ IN UINT32 ScanoutId,
+ IN UINT32 ResourceId
+ )
+{
+ volatile VIRTIO_GPU_SET_SCANOUT Request;
+
+ //
+ // Unlike for most other commands, ResourceId=0 is valid; it
+ // is used to disable a scanout.
+ //
+ Request.Rectangle.X = X;
+ Request.Rectangle.Y = Y;
+ Request.Rectangle.Width = Width;
+ Request.Rectangle.Height = Height;
+ Request.ScanoutId = ScanoutId;
+ Request.ResourceId = ResourceId;
+
+ return VirtioGpuSendCommand (
+ VgpuDev,
+ VirtioGpuCmdSetScanout,
+ FALSE, // Fence
+ &Request.Header,
+ sizeof Request
+ );
+}
+
+EFI_STATUS
+VirtioGpuTransferToHost2d (
+ IN OUT VGPU_DEV *VgpuDev,
+ IN UINT32 X,
+ IN UINT32 Y,
+ IN UINT32 Width,
+ IN UINT32 Height,
+ IN UINT64 Offset,
+ IN UINT32 ResourceId
+ )
+{
+ volatile VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D Request;
+
+ if (ResourceId == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Request.Rectangle.X = X;
+ Request.Rectangle.Y = Y;
+ Request.Rectangle.Width = Width;
+ Request.Rectangle.Height = Height;
+ Request.Offset = Offset;
+ Request.ResourceId = ResourceId;
+ Request.Padding = 0;
+
+ return VirtioGpuSendCommand (
+ VgpuDev,
+ VirtioGpuCmdTransferToHost2d,
+ FALSE, // Fence
+ &Request.Header,
+ sizeof Request
+ );
+}
+
+EFI_STATUS
+VirtioGpuResourceFlush (
+ IN OUT VGPU_DEV *VgpuDev,
+ IN UINT32 X,
+ IN UINT32 Y,
+ IN UINT32 Width,
+ IN UINT32 Height,
+ IN UINT32 ResourceId
+ )
+{
+ volatile VIRTIO_GPU_RESOURCE_FLUSH Request;
+
+ if (ResourceId == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Request.Rectangle.X = X;
+ Request.Rectangle.Y = Y;
+ Request.Rectangle.Width = Width;
+ Request.Rectangle.Height = Height;
+ Request.ResourceId = ResourceId;
+ Request.Padding = 0;
+
+ return VirtioGpuSendCommand (
+ VgpuDev,
+ VirtioGpuCmdResourceFlush,
+ FALSE, // Fence
+ &Request.Header,
+ sizeof Request
+ );
+}
diff --git a/src/VBox/Devices/EFI/Firmware/OvmfPkg/VirtioGpuDxe/DriverBinding.c b/src/VBox/Devices/EFI/Firmware/OvmfPkg/VirtioGpuDxe/DriverBinding.c
new file mode 100644
index 00000000..9f24ae25
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/OvmfPkg/VirtioGpuDxe/DriverBinding.c
@@ -0,0 +1,837 @@
+/** @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);
+}
diff --git a/src/VBox/Devices/EFI/Firmware/OvmfPkg/VirtioGpuDxe/Gop.c b/src/VBox/Devices/EFI/Firmware/OvmfPkg/VirtioGpuDxe/Gop.c
new file mode 100644
index 00000000..bb29f7ee
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/OvmfPkg/VirtioGpuDxe/Gop.c
@@ -0,0 +1,656 @@
+/** @file
+
+ EFI_GRAPHICS_OUTPUT_PROTOCOL member functions for the VirtIo GPU driver.
+
+ Copyright (C) 2016, Red Hat, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/MemoryAllocationLib.h>
+
+#include "VirtioGpu.h"
+
+/**
+ Release guest-side and host-side resources that are related to an initialized
+ VGPU_GOP.Gop.
+
+ param[in,out] VgpuGop The VGPU_GOP object to release resources for.
+
+ On input, the caller is responsible for having called
+ VgpuGop->Gop.SetMode() at least once successfully.
+ (This is equivalent to the requirement that
+ VgpuGop->BackingStore be non-NULL. It is also
+ equivalent to the requirement that VgpuGop->ResourceId
+ be nonzero.)
+
+ On output, resources will be released, and
+ VgpuGop->BackingStore and VgpuGop->ResourceId will be
+ nulled.
+
+ param[in] DisableHead Whether this head (scanout) currently references the
+ resource identified by VgpuGop->ResourceId. Only pass
+ FALSE when VgpuGop->Gop.SetMode() calls this function
+ while switching between modes, and set it to TRUE
+ every other time.
+**/
+VOID
+ReleaseGopResources (
+ IN OUT VGPU_GOP *VgpuGop,
+ IN BOOLEAN DisableHead
+ )
+{
+ EFI_STATUS Status;
+
+ ASSERT (VgpuGop->ResourceId != 0);
+ ASSERT (VgpuGop->BackingStore != NULL);
+
+ //
+ // If any of the following host-side destruction steps fail, we can't get out
+ // of an inconsistent state, so we'll hang. In general errors in object
+ // destruction can hardly be recovered from.
+ //
+ if (DisableHead) {
+ //
+ // Dissociate head (scanout) #0 from the currently used 2D host resource,
+ // by setting ResourceId=0 for it.
+ //
+ Status = VirtioGpuSetScanout (
+ VgpuGop->ParentBus, // VgpuDev
+ 0, 0, 0, 0, // X, Y, Width, Height
+ 0, // ScanoutId
+ 0 // ResourceId
+ );
+ //
+ // HACK BEGINS HERE
+ //
+ // According to the GPU Device section of the VirtIo specification, the
+ // above operation is valid:
+ //
+ // "The driver can use resource_id = 0 to disable a scanout."
+ //
+ // However, in practice QEMU does not allow us to disable head (scanout) #0
+ // -- it rejects the command with response code 0x1202
+ // (VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID). Looking at the QEMU source
+ // code, function virtio_gpu_set_scanout() in "hw/display/virtio-gpu.c",
+ // this appears fully intentional, despite not being documented in the
+ // spec.
+ //
+ // Surprisingly, ignoring the error here, and proceeding to release
+ // host-side resources that presumably underlie head (scanout) #0, work
+ // without any problems -- the driver survives repeated "disconnect" /
+ // "connect -r" commands in the UEFI shell.
+ //
+ // So, for now, let's just suppress the error.
+ //
+ Status = EFI_SUCCESS;
+ //
+ // HACK ENDS HERE
+ //
+
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ CpuDeadLoop ();
+ }
+ }
+
+ //
+ // Detach backing pages from the currently used 2D host resource.
+ //
+ Status = VirtioGpuResourceDetachBacking (
+ VgpuGop->ParentBus, // VgpuDev
+ VgpuGop->ResourceId // ResourceId
+ );
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ CpuDeadLoop ();
+ }
+
+ //
+ // Unmap and release backing pages.
+ //
+ VirtioGpuUnmapAndFreeBackingStore (
+ VgpuGop->ParentBus, // VgpuDev
+ VgpuGop->NumberOfPages, // NumberOfPages
+ VgpuGop->BackingStore, // HostAddress
+ VgpuGop->BackingStoreMap // Mapping
+ );
+ VgpuGop->BackingStore = NULL;
+ VgpuGop->NumberOfPages = 0;
+ VgpuGop->BackingStoreMap = NULL;
+
+ //
+ // Destroy the currently used 2D host resource.
+ //
+ Status = VirtioGpuResourceUnref (
+ VgpuGop->ParentBus, // VgpuDev
+ VgpuGop->ResourceId // ResourceId
+ );
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ CpuDeadLoop ();
+ }
+ VgpuGop->ResourceId = 0;
+}
+
+//
+// The resolutions supported by this driver.
+//
+typedef struct {
+ UINT32 Width;
+ UINT32 Height;
+} GOP_RESOLUTION;
+
+STATIC CONST GOP_RESOLUTION mGopResolutions[] = {
+ { 640, 480 },
+ { 800, 480 },
+ { 800, 600 },
+ { 832, 624 },
+ { 960, 640 },
+ { 1024, 600 },
+ { 1024, 768 },
+ { 1152, 864 },
+ { 1152, 870 },
+ { 1280, 720 },
+ { 1280, 760 },
+ { 1280, 768 },
+ { 1280, 800 },
+ { 1280, 960 },
+ { 1280, 1024 },
+ { 1360, 768 },
+ { 1366, 768 },
+ { 1400, 1050 },
+ { 1440, 900 },
+ { 1600, 900 },
+ { 1600, 1200 },
+ { 1680, 1050 },
+ { 1920, 1080 },
+ { 1920, 1200 },
+ { 1920, 1440 },
+ { 2000, 2000 },
+ { 2048, 1536 },
+ { 2048, 2048 },
+ { 2560, 1440 },
+ { 2560, 1600 },
+ { 2560, 2048 },
+ { 2800, 2100 },
+ { 3200, 2400 },
+ { 3840, 2160 },
+ { 4096, 2160 },
+ { 7680, 4320 },
+ { 8192, 4320 },
+};
+
+//
+// Macro for casting VGPU_GOP.Gop to VGPU_GOP.
+//
+#define VGPU_GOP_FROM_GOP(GopPointer) \
+ CR (GopPointer, VGPU_GOP, Gop, VGPU_GOP_SIG)
+
+//
+// EFI_GRAPHICS_OUTPUT_PROTOCOL member functions.
+//
+STATIC
+EFI_STATUS
+EFIAPI
+GopQueryMode (
+ IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
+ IN UINT32 ModeNumber,
+ OUT UINTN *SizeOfInfo,
+ OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info
+ )
+{
+ EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *GopModeInfo;
+
+ if (ModeNumber >= ARRAY_SIZE (mGopResolutions)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ GopModeInfo = AllocateZeroPool (sizeof *GopModeInfo);
+ if (GopModeInfo == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ GopModeInfo->HorizontalResolution = mGopResolutions[ModeNumber].Width;
+ GopModeInfo->VerticalResolution = mGopResolutions[ModeNumber].Height;
+ GopModeInfo->PixelFormat = PixelBltOnly;
+ GopModeInfo->PixelsPerScanLine = mGopResolutions[ModeNumber].Width;
+
+ *SizeOfInfo = sizeof *GopModeInfo;
+ *Info = GopModeInfo;
+ return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+GopSetMode (
+ IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
+ IN UINT32 ModeNumber
+ )
+{
+ VGPU_GOP *VgpuGop;
+ UINT32 NewResourceId;
+ UINTN NewNumberOfBytes;
+ UINTN NewNumberOfPages;
+ VOID *NewBackingStore;
+ EFI_PHYSICAL_ADDRESS NewBackingStoreDeviceAddress;
+ VOID *NewBackingStoreMap;
+
+ EFI_STATUS Status;
+ EFI_STATUS Status2;
+
+ if (ModeNumber >= ARRAY_SIZE (mGopResolutions)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ VgpuGop = VGPU_GOP_FROM_GOP (This);
+
+ //
+ // Distinguish the first (internal) call from the other (protocol consumer)
+ // calls.
+ //
+ if (VgpuGop->ResourceId == 0) {
+ //
+ // Set up the Gop -> GopMode -> GopModeInfo pointer chain, and the other
+ // (nonzero) constant fields.
+ //
+ // No direct framebuffer access is supported, only Blt() is.
+ //
+ VgpuGop->Gop.Mode = &VgpuGop->GopMode;
+
+ VgpuGop->GopMode.MaxMode = (UINT32)(ARRAY_SIZE (mGopResolutions));
+ VgpuGop->GopMode.Info = &VgpuGop->GopModeInfo;
+ VgpuGop->GopMode.SizeOfInfo = sizeof VgpuGop->GopModeInfo;
+
+ VgpuGop->GopModeInfo.PixelFormat = PixelBltOnly;
+
+ //
+ // This is the first time we create a host side resource.
+ //
+ NewResourceId = 1;
+ } else {
+ //
+ // We already have an active host side resource. Create the new one without
+ // interfering with the current one, so that we can cleanly bail out on
+ // error, without disturbing the current graphics mode.
+ //
+ // The formula below will alternate between IDs 1 and 2.
+ //
+ NewResourceId = 3 - VgpuGop->ResourceId;
+ }
+
+ //
+ // Create the 2D host resource.
+ //
+ Status = VirtioGpuResourceCreate2d (
+ VgpuGop->ParentBus, // VgpuDev
+ NewResourceId, // ResourceId
+ VirtioGpuFormatB8G8R8X8Unorm, // Format
+ mGopResolutions[ModeNumber].Width, // Width
+ mGopResolutions[ModeNumber].Height // Height
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Allocate, zero and map guest backing store, for bus master common buffer
+ // operation.
+ //
+ NewNumberOfBytes = mGopResolutions[ModeNumber].Width *
+ mGopResolutions[ModeNumber].Height * sizeof (UINT32);
+ NewNumberOfPages = EFI_SIZE_TO_PAGES (NewNumberOfBytes);
+ Status = VirtioGpuAllocateZeroAndMapBackingStore (
+ VgpuGop->ParentBus, // VgpuDev
+ NewNumberOfPages, // NumberOfPages
+ &NewBackingStore, // HostAddress
+ &NewBackingStoreDeviceAddress, // DeviceAddress
+ &NewBackingStoreMap // Mapping
+ );
+ if (EFI_ERROR (Status)) {
+ goto DestroyHostResource;
+ }
+
+ //
+ // Attach backing store to the host resource.
+ //
+ Status = VirtioGpuResourceAttachBacking (
+ VgpuGop->ParentBus, // VgpuDev
+ NewResourceId, // ResourceId
+ NewBackingStoreDeviceAddress, // BackingStoreDeviceAddress
+ NewNumberOfPages // NumberOfPages
+ );
+ if (EFI_ERROR (Status)) {
+ goto UnmapAndFreeBackingStore;
+ }
+
+ //
+ // Point head (scanout) #0 to the host resource.
+ //
+ Status = VirtioGpuSetScanout (
+ VgpuGop->ParentBus, // VgpuDev
+ 0, // X
+ 0, // Y
+ mGopResolutions[ModeNumber].Width, // Width
+ mGopResolutions[ModeNumber].Height, // Height
+ 0, // ScanoutId
+ NewResourceId // ResourceId
+ );
+ if (EFI_ERROR (Status)) {
+ goto DetachBackingStore;
+ }
+
+ //
+ // If this is not the first (i.e., internal) call, then we have to (a) flush
+ // the new resource to head (scanout) #0, after having flipped the latter to
+ // the former above, plus (b) release the old resources.
+ //
+ if (VgpuGop->ResourceId != 0) {
+ Status = VirtioGpuResourceFlush (
+ VgpuGop->ParentBus, // VgpuDev
+ 0, // X
+ 0, // Y
+ mGopResolutions[ModeNumber].Width, // Width
+ mGopResolutions[ModeNumber].Height, // Height
+ NewResourceId // ResourceId
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // Flip head (scanout) #0 back to the current resource. If this fails, we
+ // cannot continue, as this error occurs on the error path and is
+ // therefore non-recoverable.
+ //
+ Status2 = VirtioGpuSetScanout (
+ VgpuGop->ParentBus, // VgpuDev
+ 0, // X
+ 0, // Y
+ mGopResolutions[This->Mode->Mode].Width, // Width
+ mGopResolutions[This->Mode->Mode].Height, // Height
+ 0, // ScanoutId
+ VgpuGop->ResourceId // ResourceId
+ );
+ ASSERT_EFI_ERROR (Status2);
+ if (EFI_ERROR (Status2)) {
+ CpuDeadLoop ();
+ }
+ goto DetachBackingStore;
+ }
+
+ //
+ // Flush successful; release the old resources (without disabling head
+ // (scanout) #0).
+ //
+ ReleaseGopResources (VgpuGop, FALSE /* DisableHead */);
+ }
+
+ //
+ // This is either the first (internal) call when we have no old resources
+ // yet, or we've changed the mode successfully and released the old
+ // resources.
+ //
+ ASSERT (VgpuGop->ResourceId == 0);
+ ASSERT (VgpuGop->BackingStore == NULL);
+
+ VgpuGop->ResourceId = NewResourceId;
+ VgpuGop->BackingStore = NewBackingStore;
+ VgpuGop->NumberOfPages = NewNumberOfPages;
+ VgpuGop->BackingStoreMap = NewBackingStoreMap;
+
+ //
+ // Populate Mode and ModeInfo (mutable fields only).
+ //
+ VgpuGop->GopMode.Mode = ModeNumber;
+ VgpuGop->GopModeInfo.HorizontalResolution =
+ mGopResolutions[ModeNumber].Width;
+ VgpuGop->GopModeInfo.VerticalResolution = mGopResolutions[ModeNumber].Height;
+ VgpuGop->GopModeInfo.PixelsPerScanLine = mGopResolutions[ModeNumber].Width;
+ return EFI_SUCCESS;
+
+DetachBackingStore:
+ Status2 = VirtioGpuResourceDetachBacking (VgpuGop->ParentBus, NewResourceId);
+ ASSERT_EFI_ERROR (Status2);
+ if (EFI_ERROR (Status2)) {
+ CpuDeadLoop ();
+ }
+
+UnmapAndFreeBackingStore:
+ VirtioGpuUnmapAndFreeBackingStore (
+ VgpuGop->ParentBus, // VgpuDev
+ NewNumberOfPages, // NumberOfPages
+ NewBackingStore, // HostAddress
+ NewBackingStoreMap // Mapping
+ );
+
+DestroyHostResource:
+ Status2 = VirtioGpuResourceUnref (VgpuGop->ParentBus, NewResourceId);
+ ASSERT_EFI_ERROR (Status2);
+ if (EFI_ERROR (Status2)) {
+ CpuDeadLoop ();
+ }
+
+ return Status;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+GopBlt (
+ IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
+ IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL
+ IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,
+ IN UINTN SourceX,
+ IN UINTN SourceY,
+ IN UINTN DestinationX,
+ IN UINTN DestinationY,
+ IN UINTN Width,
+ IN UINTN Height,
+ IN UINTN Delta OPTIONAL
+ )
+{
+ VGPU_GOP *VgpuGop;
+ UINT32 CurrentHorizontal;
+ UINT32 CurrentVertical;
+ UINTN SegmentSize;
+ UINTN Y;
+ UINTN ResourceOffset;
+ EFI_STATUS Status;
+
+ VgpuGop = VGPU_GOP_FROM_GOP (This);
+ CurrentHorizontal = VgpuGop->GopModeInfo.HorizontalResolution;
+ CurrentVertical = VgpuGop->GopModeInfo.VerticalResolution;
+
+ //
+ // We can avoid pixel format conversion in the guest because the internal
+ // representation of EFI_GRAPHICS_OUTPUT_BLT_PIXEL and that of
+ // VirtioGpuFormatB8G8R8X8Unorm are identical.
+ //
+ SegmentSize = Width * sizeof (UINT32);
+
+ //
+ // Delta is relevant for operations that read a rectangle from, or write a
+ // rectangle to, BltBuffer.
+ //
+ // In these cases, Delta is the stride of BltBuffer, in bytes. If Delta is
+ // zero, then Width is the entire width of BltBuffer, and the stride is
+ // supposed to be calculated from Width.
+ //
+ if (BltOperation == EfiBltVideoToBltBuffer ||
+ BltOperation == EfiBltBufferToVideo) {
+ if (Delta == 0) {
+ Delta = SegmentSize;
+ }
+ }
+
+ //
+ // For operations that write to the display, check if the destination fits
+ // onto the display.
+ //
+ if (BltOperation == EfiBltVideoFill ||
+ BltOperation == EfiBltBufferToVideo ||
+ BltOperation == EfiBltVideoToVideo) {
+ if (DestinationX > CurrentHorizontal ||
+ Width > CurrentHorizontal - DestinationX ||
+ DestinationY > CurrentVertical ||
+ Height > CurrentVertical - DestinationY) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ //
+ // For operations that read from the display, check if the source fits onto
+ // the display.
+ //
+ if (BltOperation == EfiBltVideoToBltBuffer ||
+ BltOperation == EfiBltVideoToVideo) {
+ if (SourceX > CurrentHorizontal ||
+ Width > CurrentHorizontal - SourceX ||
+ SourceY > CurrentVertical ||
+ Height > CurrentVertical - SourceY) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ //
+ // Render the request. For requests that do not modify the display, there
+ // won't be further steps.
+ //
+ switch (BltOperation) {
+ case EfiBltVideoFill:
+ //
+ // Write data from the BltBuffer pixel (0, 0) directly to every pixel of
+ // the video display rectangle (DestinationX, DestinationY) (DestinationX +
+ // Width, DestinationY + Height). Only one pixel will be used from the
+ // BltBuffer. Delta is NOT used.
+ //
+ for (Y = 0; Y < Height; ++Y) {
+ SetMem32 (
+ VgpuGop->BackingStore +
+ (DestinationY + Y) * CurrentHorizontal + DestinationX,
+ SegmentSize,
+ *(UINT32 *)BltBuffer
+ );
+ }
+ break;
+
+ case EfiBltVideoToBltBuffer:
+ //
+ // Read data from the video display rectangle (SourceX, SourceY) (SourceX +
+ // Width, SourceY + Height) and place it in the BltBuffer rectangle
+ // (DestinationX, DestinationY ) (DestinationX + Width, DestinationY +
+ // Height). If DestinationX or DestinationY is not zero then Delta must be
+ // set to the length in bytes of a row in the BltBuffer.
+ //
+ for (Y = 0; Y < Height; ++Y) {
+ CopyMem (
+ (UINT8 *)BltBuffer +
+ (DestinationY + Y) * Delta + DestinationX * sizeof *BltBuffer,
+ VgpuGop->BackingStore +
+ (SourceY + Y) * CurrentHorizontal + SourceX,
+ SegmentSize
+ );
+ }
+ return EFI_SUCCESS;
+
+ case EfiBltBufferToVideo:
+ //
+ // Write data from the BltBuffer rectangle (SourceX, SourceY) (SourceX +
+ // Width, SourceY + Height) directly to the video display rectangle
+ // (DestinationX, DestinationY) (DestinationX + Width, DestinationY +
+ // Height). If SourceX or SourceY is not zero then Delta must be set to the
+ // length in bytes of a row in the BltBuffer.
+ //
+ for (Y = 0; Y < Height; ++Y) {
+ CopyMem (
+ VgpuGop->BackingStore +
+ (DestinationY + Y) * CurrentHorizontal + DestinationX,
+ (UINT8 *)BltBuffer +
+ (SourceY + Y) * Delta + SourceX * sizeof *BltBuffer,
+ SegmentSize
+ );
+ }
+ break;
+
+ case EfiBltVideoToVideo:
+ //
+ // Copy from the video display rectangle (SourceX, SourceY) (SourceX +
+ // Width, SourceY + Height) to the video display rectangle (DestinationX,
+ // DestinationY) (DestinationX + Width, DestinationY + Height). The
+ // BltBuffer and Delta are not used in this mode.
+ //
+ // A single invocation of CopyMem() handles overlap between source and
+ // destination (that is, within a single line), but for multiple
+ // invocations, we must handle overlaps.
+ //
+ if (SourceY < DestinationY) {
+ Y = Height;
+ while (Y > 0) {
+ --Y;
+ CopyMem (
+ VgpuGop->BackingStore +
+ (DestinationY + Y) * CurrentHorizontal + DestinationX,
+ VgpuGop->BackingStore +
+ (SourceY + Y) * CurrentHorizontal + SourceX,
+ SegmentSize
+ );
+ }
+ } else {
+ for (Y = 0; Y < Height; ++Y) {
+ CopyMem (
+ VgpuGop->BackingStore +
+ (DestinationY + Y) * CurrentHorizontal + DestinationX,
+ VgpuGop->BackingStore +
+ (SourceY + Y) * CurrentHorizontal + SourceX,
+ SegmentSize
+ );
+ }
+ }
+ break;
+
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // For operations that wrote to the display, submit the updated area to the
+ // host -- update the host resource from guest memory.
+ //
+ ResourceOffset = sizeof (UINT32) * (DestinationY * CurrentHorizontal +
+ DestinationX);
+ Status = VirtioGpuTransferToHost2d (
+ VgpuGop->ParentBus, // VgpuDev
+ (UINT32)DestinationX, // X
+ (UINT32)DestinationY, // Y
+ (UINT32)Width, // Width
+ (UINT32)Height, // Height
+ ResourceOffset, // Offset
+ VgpuGop->ResourceId // ResourceId
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Flush the updated resource to the display.
+ //
+ Status = VirtioGpuResourceFlush (
+ VgpuGop->ParentBus, // VgpuDev
+ (UINT32)DestinationX, // X
+ (UINT32)DestinationY, // Y
+ (UINT32)Width, // Width
+ (UINT32)Height, // Height
+ VgpuGop->ResourceId // ResourceId
+ );
+ return Status;
+}
+
+//
+// Template for initializing VGPU_GOP.Gop.
+//
+CONST EFI_GRAPHICS_OUTPUT_PROTOCOL mGopTemplate = {
+ GopQueryMode,
+ GopSetMode,
+ GopBlt,
+ NULL // Mode, to be overwritten in the actual protocol instance
+};
diff --git a/src/VBox/Devices/EFI/Firmware/OvmfPkg/VirtioGpuDxe/VirtioGpu.h b/src/VBox/Devices/EFI/Firmware/OvmfPkg/VirtioGpuDxe/VirtioGpu.h
new file mode 100644
index 00000000..2f10c003
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/OvmfPkg/VirtioGpuDxe/VirtioGpu.h
@@ -0,0 +1,402 @@
+/** @file
+
+ Internal type and macro definitions for the Virtio GPU hybrid driver.
+
+ Copyright (C) 2016, Red Hat, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _VIRTIO_GPU_DXE_H_
+#define _VIRTIO_GPU_DXE_H_
+
+#include <IndustryStandard/VirtioGpu.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiLib.h>
+#include <Protocol/GraphicsOutput.h>
+#include <Protocol/VirtioDevice.h>
+
+//
+// Forward declaration of VGPU_GOP.
+//
+typedef struct VGPU_GOP_STRUCT VGPU_GOP;
+
+//
+// The abstraction that directly corresponds to a Virtio GPU device.
+//
+// This structure will be installed on the handle that has the VirtIo Device
+// Protocol interface, with GUID gEfiCallerIdGuid. A similar trick is employed
+// in TerminalDxe, and it is necessary so that we can look up VGPU_DEV just
+// from the VirtIo Device Protocol handle in the Component Name 2 Protocol
+// implementation.
+//
+typedef struct {
+ //
+ // VirtIo represents access to the Virtio GPU device. Never NULL.
+ //
+ VIRTIO_DEVICE_PROTOCOL *VirtIo;
+
+ //
+ // BusName carries a customized name for
+ // EFI_COMPONENT_NAME2_PROTOCOL.GetControllerName(). It is expressed in table
+ // form because it can theoretically support several languages. Never NULL.
+ //
+ EFI_UNICODE_STRING_TABLE *BusName;
+
+ //
+ // VirtIo ring used for VirtIo communication.
+ //
+ VRING Ring;
+
+ //
+ // Token associated with Ring's mapping for bus master common buffer
+ // operation, from VirtioRingMap().
+ //
+ VOID *RingMap;
+
+ //
+ // Event to be signaled at ExitBootServices().
+ //
+ EFI_EVENT ExitBoot;
+
+ //
+ // Common running counter for all VirtIo GPU requests that ask for fencing.
+ //
+ UINT64 FenceId;
+
+ //
+ // The Child field references the GOP wrapper structure. If this pointer is
+ // NULL, then the hybrid driver has bound (i.e., started) the
+ // VIRTIO_DEVICE_PROTOCOL controller without producing the child GOP
+ // controller (that is, after Start() was called with RemainingDevicePath
+ // pointing to and End of Device Path node). Child can be created and
+ // destroyed, even repeatedly, independently of VGPU_DEV.
+ //
+ // In practice, this field represents the single head (scanout) that we
+ // support.
+ //
+ VGPU_GOP *Child;
+} VGPU_DEV;
+
+//
+// The Graphics Output Protocol wrapper structure.
+//
+#define VGPU_GOP_SIG SIGNATURE_64 ('V', 'G', 'P', 'U', '_', 'G', 'O', 'P')
+
+struct VGPU_GOP_STRUCT {
+ UINT64 Signature;
+
+ //
+ // ParentBus points to the parent VGPU_DEV object. Never NULL.
+ //
+ VGPU_DEV *ParentBus;
+
+ //
+ // GopName carries a customized name for
+ // EFI_COMPONENT_NAME2_PROTOCOL.GetControllerName(). It is expressed in table
+ // form because it can theoretically support several languages. Never NULL.
+ //
+ EFI_UNICODE_STRING_TABLE *GopName;
+
+ //
+ // GopHandle is the UEFI child handle that carries the device path ending
+ // with the ACPI ADR node, and the Graphics Output Protocol. Never NULL.
+ //
+ EFI_HANDLE GopHandle;
+
+ //
+ // The GopDevicePath field is the device path installed on GopHandle,
+ // ending with an ACPI ADR node. Never NULL.
+ //
+ EFI_DEVICE_PATH_PROTOCOL *GopDevicePath;
+
+ //
+ // The Gop field is installed on the child handle as Graphics Output Protocol
+ // interface.
+ //
+ EFI_GRAPHICS_OUTPUT_PROTOCOL Gop;
+
+ //
+ // Referenced by Gop.Mode, GopMode provides a summary about the supported
+ // graphics modes, and the current mode.
+ //
+ EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE GopMode;
+
+ //
+ // Referenced by GopMode.Info, GopModeInfo provides detailed information
+ // about the current mode.
+ //
+ EFI_GRAPHICS_OUTPUT_MODE_INFORMATION GopModeInfo;
+
+ //
+ // Identifier of the 2D host resource that is in use by this head (scanout)
+ // of the VirtIo GPU device. Zero until the first successful -- internal --
+ // Gop.SetMode() call, never zero afterwards.
+ //
+ UINT32 ResourceId;
+
+ //
+ // A number of whole pages providing the backing store for the 2D host
+ // resource identified by ResourceId above. NULL until the first successful
+ // -- internal -- Gop.SetMode() call, never NULL afterwards.
+ //
+ UINT32 *BackingStore;
+ UINTN NumberOfPages;
+
+ //
+ // Token associated with BackingStore's mapping for bus master common
+ // buffer operation. BackingStoreMap is valid if, and only if,
+ // BackingStore is non-NULL.
+ //
+ VOID *BackingStoreMap;
+};
+
+//
+// VirtIo GPU initialization, and commands (primitives) for the GPU device.
+//
+/**
+ Configure the VirtIo GPU device that underlies VgpuDev.
+
+ @param[in,out] VgpuDev The VGPU_DEV object to set up VirtIo messaging for.
+ On input, the caller is responsible for having
+ initialized VgpuDev->VirtIo. On output, VgpuDev->Ring
+ has been initialized, and synchronous VirtIo GPU
+ commands (primitives) can be submitted to the device.
+
+ @retval EFI_SUCCESS VirtIo GPU configuration successful.
+
+ @retval EFI_UNSUPPORTED The host-side configuration of the VirtIo GPU is not
+ supported by this driver.
+
+ @retval Error codes from underlying functions.
+**/
+EFI_STATUS
+VirtioGpuInit (
+ IN OUT VGPU_DEV *VgpuDev
+ );
+
+/**
+ De-configure the VirtIo GPU device that underlies VgpuDev.
+
+ @param[in,out] VgpuDev The VGPU_DEV object to tear down VirtIo messaging
+ for. On input, the caller is responsible for having
+ called VirtioGpuInit(). On output, VgpuDev->Ring has
+ been uninitialized; VirtIo GPU commands (primitives)
+ can no longer be submitted to the device.
+**/
+VOID
+VirtioGpuUninit (
+ IN OUT VGPU_DEV *VgpuDev
+ );
+
+/**
+ Allocate, zero and map memory, for bus master common buffer operation, to be
+ attached as backing store to a host-side VirtIo GPU resource.
+
+ @param[in] VgpuDev The VGPU_DEV object that represents the VirtIo GPU
+ device.
+
+ @param[in] NumberOfPages The number of whole pages to allocate and map.
+
+ @param[out] HostAddress The system memory address of the allocated area.
+
+ @param[out] DeviceAddress The bus master device address of the allocated
+ area. The VirtIo GPU device may be programmed to
+ access the allocated area through DeviceAddress;
+ DeviceAddress is to be passed to the
+ VirtioGpuResourceAttachBacking() function, as the
+ BackingStoreDeviceAddress parameter.
+
+ @param[out] Mapping A resulting token to pass to
+ VirtioGpuUnmapAndFreeBackingStore().
+
+ @retval EFI_SUCCESS The requested number of pages has been allocated, zeroed
+ and mapped.
+
+ @return Status codes propagated from
+ VgpuDev->VirtIo->AllocateSharedPages() and
+ VirtioMapAllBytesInSharedBuffer().
+**/
+EFI_STATUS
+VirtioGpuAllocateZeroAndMapBackingStore (
+ IN VGPU_DEV *VgpuDev,
+ IN UINTN NumberOfPages,
+ OUT VOID **HostAddress,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT VOID **Mapping
+ );
+
+/**
+ Unmap and free memory originally allocated and mapped with
+ VirtioGpuAllocateZeroAndMapBackingStore().
+
+ If the memory allocated and mapped with
+ VirtioGpuAllocateZeroAndMapBackingStore() was attached to a host-side VirtIo
+ GPU resource with VirtioGpuResourceAttachBacking(), then the caller is
+ responsible for detaching the backing store from the same resource, with
+ VirtioGpuResourceDetachBacking(), before calling this function.
+
+ @param[in] VgpuDev The VGPU_DEV object that represents the VirtIo GPU
+ device.
+
+ @param[in] NumberOfPages The NumberOfPages parameter originally passed to
+ VirtioGpuAllocateZeroAndMapBackingStore().
+
+ @param[in] HostAddress The HostAddress value originally output by
+ VirtioGpuAllocateZeroAndMapBackingStore().
+
+ @param[in] Mapping The token that was originally output by
+ VirtioGpuAllocateZeroAndMapBackingStore().
+**/
+VOID
+VirtioGpuUnmapAndFreeBackingStore (
+ IN VGPU_DEV *VgpuDev,
+ IN UINTN NumberOfPages,
+ IN VOID *HostAddress,
+ IN VOID *Mapping
+ );
+
+/**
+ EFI_EVENT_NOTIFY function for the VGPU_DEV.ExitBoot event. It resets the
+ VirtIo device, causing it to release its resources and to forget its
+ configuration.
+
+ This function may only be called (that is, VGPU_DEV.ExitBoot may only be
+ signaled) after VirtioGpuInit() returns and before VirtioGpuUninit() is
+ called.
+
+ @param[in] Event Event whose notification function is being invoked.
+
+ @param[in] Context Pointer to the associated VGPU_DEV object.
+**/
+VOID
+EFIAPI
+VirtioGpuExitBoot (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ The following functions send requests to the VirtIo GPU device model, await
+ the answer from the host, and return a status. They share the following
+ interface details:
+
+ @param[in,out] VgpuDev The VGPU_DEV object that represents the VirtIo GPU
+ device. The caller is responsible to have
+ successfully invoked VirtioGpuInit() on VgpuDev
+ previously, while VirtioGpuUninit() must not have
+ been called on VgpuDev.
+
+ @retval EFI_INVALID_PARAMETER Invalid command-specific parameters were
+ detected by this driver.
+
+ @retval EFI_SUCCESS Operation successful.
+
+ @retval EFI_DEVICE_ERROR The host rejected the request. The host error
+ code has been logged on the DEBUG_ERROR level.
+
+ @return Codes for unexpected errors in VirtIo
+ messaging.
+
+ For the command-specific parameters, please consult the GPU Device section of
+ the VirtIo 1.0 specification (see references in
+ "OvmfPkg/Include/IndustryStandard/VirtioGpu.h").
+**/
+EFI_STATUS
+VirtioGpuResourceCreate2d (
+ IN OUT VGPU_DEV *VgpuDev,
+ IN UINT32 ResourceId,
+ IN VIRTIO_GPU_FORMATS Format,
+ IN UINT32 Width,
+ IN UINT32 Height
+ );
+
+EFI_STATUS
+VirtioGpuResourceUnref (
+ IN OUT VGPU_DEV *VgpuDev,
+ IN UINT32 ResourceId
+ );
+
+EFI_STATUS
+VirtioGpuResourceAttachBacking (
+ IN OUT VGPU_DEV *VgpuDev,
+ IN UINT32 ResourceId,
+ IN EFI_PHYSICAL_ADDRESS BackingStoreDeviceAddress,
+ IN UINTN NumberOfPages
+ );
+
+EFI_STATUS
+VirtioGpuResourceDetachBacking (
+ IN OUT VGPU_DEV *VgpuDev,
+ IN UINT32 ResourceId
+ );
+
+EFI_STATUS
+VirtioGpuSetScanout (
+ IN OUT VGPU_DEV *VgpuDev,
+ IN UINT32 X,
+ IN UINT32 Y,
+ IN UINT32 Width,
+ IN UINT32 Height,
+ IN UINT32 ScanoutId,
+ IN UINT32 ResourceId
+ );
+
+EFI_STATUS
+VirtioGpuTransferToHost2d (
+ IN OUT VGPU_DEV *VgpuDev,
+ IN UINT32 X,
+ IN UINT32 Y,
+ IN UINT32 Width,
+ IN UINT32 Height,
+ IN UINT64 Offset,
+ IN UINT32 ResourceId
+ );
+
+EFI_STATUS
+VirtioGpuResourceFlush (
+ IN OUT VGPU_DEV *VgpuDev,
+ IN UINT32 X,
+ IN UINT32 Y,
+ IN UINT32 Width,
+ IN UINT32 Height,
+ IN UINT32 ResourceId
+ );
+
+/**
+ Release guest-side and host-side resources that are related to an initialized
+ VGPU_GOP.Gop.
+
+ param[in,out] VgpuGop The VGPU_GOP object to release resources for.
+
+ On input, the caller is responsible for having called
+ VgpuGop->Gop.SetMode() at least once successfully.
+ (This is equivalent to the requirement that
+ VgpuGop->BackingStore be non-NULL. It is also
+ equivalent to the requirement that VgpuGop->ResourceId
+ be nonzero.)
+
+ On output, resources will be released, and
+ VgpuGop->BackingStore and VgpuGop->ResourceId will be
+ nulled.
+
+ param[in] DisableHead Whether this head (scanout) currently references the
+ resource identified by VgpuGop->ResourceId. Only pass
+ FALSE when VgpuGop->Gop.SetMode() calls this function
+ while switching between modes, and set it to TRUE
+ every other time.
+**/
+VOID
+ReleaseGopResources (
+ IN OUT VGPU_GOP *VgpuGop,
+ IN BOOLEAN DisableHead
+ );
+
+//
+// Template for initializing VGPU_GOP.Gop.
+//
+extern CONST EFI_GRAPHICS_OUTPUT_PROTOCOL mGopTemplate;
+
+#endif // _VIRTIO_GPU_DXE_H_
diff --git a/src/VBox/Devices/EFI/Firmware/OvmfPkg/VirtioGpuDxe/VirtioGpu.inf b/src/VBox/Devices/EFI/Firmware/OvmfPkg/VirtioGpuDxe/VirtioGpu.inf
new file mode 100644
index 00000000..97d013ed
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/OvmfPkg/VirtioGpuDxe/VirtioGpu.inf
@@ -0,0 +1,45 @@
+## @file
+#
+# This hybrid driver produces the Graphics Output Protocol for the Virtio GPU
+# device (head #0, only and unconditionally).
+#
+# Copyright (C) 2016, Red Hat, Inc.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = VirtioGpuDxe
+ FILE_GUID = D6099B94-CD97-4CC5-8714-7F6312701A8A
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = VirtioGpuEntryPoint
+
+[Sources]
+ Commands.c
+ DriverBinding.c
+ Gop.c
+ VirtioGpu.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseMemoryLib
+ DebugLib
+ DevicePathLib
+ MemoryAllocationLib
+ PrintLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiLib
+ VirtioLib
+
+[Protocols]
+ gEfiDevicePathProtocolGuid ## TO_START ## BY_START
+ gEfiGraphicsOutputProtocolGuid ## BY_START
+ gEfiPciIoProtocolGuid ## TO_START
+ gVirtioDeviceProtocolGuid ## TO_START