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.biesheu...@linaro.org>
Cc: Brijesh Singh <brijesh.si...@amd.com>
Cc: Jordan Justen <jordan.l.jus...@intel.com>
Cc: Tom Lendacky <thomas.lenda...@amd.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Laszlo Ersek <ler...@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

Reply via email to