VirtioGpuDxe uses one virtio ring, for VIRTIO_GPU_CONTROL_QUEUE.
Map it for bus master common buffer operation with VirtioRingMap(), so
that it can be accessed equally by both guest and hypervisor even if an
IOMMU is used. (VirtioRingInit() already allocates the ring suitably for
this, see commit b0338c53297c, "OvmfPkg/VirtioLib: alloc VRING buffer with
AllocateSharedPages()", 2017-08-23).
Pass the resultant translation offset ("RingBaseShift"), from system
memory address to bus master device address, to VIRTIO_SET_QUEUE_ADDRESS.
Unmap the ring in all contexts where the ring becomes unused (these
contexts are mutually exclusive):
- in VirtioGpuInit(): the ring has been mapped, but we cannot complete the
virtio initialization for another reason,
- in VirtioGpuUninit(): the virtio initialization has succeeded, but
VirtioGpuDriverBindingStart() fails for another reason, or
VirtioGpuDriverBindingStop() unbinds the device after use,
- in VirtioGpuExitBoot(): ExitBootServices() is called after
VirtioGpuDriverBindingStart() has successfully bound the device.
(Unmapping the ring does not change the UEFI memory map.)
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
---
OvmfPkg/VirtioGpuDxe/VirtioGpu.h | 6 +++++
OvmfPkg/VirtioGpuDxe/Commands.c | 27 +++++++++++++++++---
2 files changed, 30 insertions(+), 3 deletions(-)
diff --git a/OvmfPkg/VirtioGpuDxe/VirtioGpu.h b/OvmfPkg/VirtioGpuDxe/VirtioGpu.h
index 078b7d44d83e..193e932e1430 100644
--- a/OvmfPkg/VirtioGpuDxe/VirtioGpu.h
+++ b/OvmfPkg/VirtioGpuDxe/VirtioGpu.h
@@ -40,46 +40,52 @@ typedef struct VGPU_GOP_STRUCT VGPU_GOP;
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')
diff --git a/OvmfPkg/VirtioGpuDxe/Commands.c b/OvmfPkg/VirtioGpuDxe/Commands.c
index 5cb003161207..4e19bac606ee 100644
--- a/OvmfPkg/VirtioGpuDxe/Commands.c
+++ b/OvmfPkg/VirtioGpuDxe/Commands.c
@@ -39,119 +39,138 @@ 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;
//
// ... 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,
- 0
+ RingBaseShift
);
if (EFI_ERROR (Status)) {
- goto ReleaseQueue;
+ 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 ReleaseQueue;
+ goto UnmapQueue;
}
return EFI_SUCCESS;
+UnmapQueue:
+ VgpuDev->VirtIo->UnmapSharedBuffer (VgpuDev->VirtIo, VgpuDev->RingMap);
+
ReleaseQueue:
VirtioRingUninit (VgpuDev->VirtIo, &VgpuDev->Ring);
@@ -182,25 +201,26 @@ 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);
}
/**
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.
**/
@@ -209,53 +229,54 @@ EFIAPI
VirtioGpuExitBoot (
IN EFI_EVENT Event,
IN VOID *Context
)
{
VGPU_DEV *VgpuDev;
VgpuDev = Context;
VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, 0);
+ VgpuDev->VirtIo->UnmapSharedBuffer (VgpuDev->VirtIo, VgpuDev->RingMap);
}
/**
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 EFI_D_ERROR level.
@return Codes for unexpected errors in VirtIo
messaging.
**/
--
2.14.1.3.gb7cf6e02401b
_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel
© 2016 - 2024 Red Hat, Inc.