The VirtioPciDeviceDxe was extended to support a non-discoverable
MMIO Virtio case.
The Virtio spec defines both PCI and MMIO device types with the 
set of registers that are not the same between these two types
of devices. All PCI registers have corresponding MMIO registers
but the number of registers is less then MMIO. The width of MMIO
registers and PCI registers is not always the same.
Compared to PCI, MMIO Virtio devices required more registers to be
programmed in some cases. Added detection that a PCI device is 
based on a non-discoverable device and allows MMIO transactions.

Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Daniil Egranov <daniil.egra...@arm.com>
---
 OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.c      | 143 +++++++++++++++++++++-
 OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.h      |  21 +++-
 OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf |   4 +-
 OvmfPkg/VirtioPciDeviceDxe/VirtioPciFunctions.c   | 117 +++++++++++++++++-
 4 files changed, 278 insertions(+), 7 deletions(-)

diff --git a/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.c 
b/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.c
index d4b4ec21c3..df2a4ea116 100644
--- a/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.c
+++ b/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.c
@@ -4,7 +4,7 @@
 
   Copyright (C) 2012, Red Hat, Inc.
   Copyright (c) 2012 - 2016, Intel Corporation. All rights reserved.<BR>
-  Copyright (C) 2013, ARM Ltd.
+  Copyright (C) 2013 - 2018, ARM Ltd.
   Copyright (C) 2017, AMD Inc, All rights reserved.<BR>
 
   This program and the accompanying materials are licensed and made available
@@ -215,6 +215,147 @@ VirtioPciIoWrite (
 
 /**
 
+  Read a word from Region 0 of the device specified by PciMemIo.
+
+  Region 0 must be an iomem region. This is an internal function for the PCI
+  implementation of the protocol.
+
+  @param[in] Dev          Virtio PCI device.
+
+  @param[in] FieldOffset  Source offset.
+
+  @param[in] FieldSize    Source field size, must be in { 1, 2, 4, 8 }.
+
+  @param[in] BufferSize   Number of bytes available in the target buffer. Must
+                          equal FieldSize.
+
+  @param[out] Buffer      Target buffer.
+
+
+  @return  Status code returned by PciIo->Io.Read().
+
+**/
+EFI_STATUS
+EFIAPI
+VirtioPciMemIoRead (
+  IN  VIRTIO_PCI_DEVICE         *Dev,
+  IN  UINTN                     FieldOffset,
+  IN  UINTN                     FieldSize,
+  IN  UINTN                     BufferSize,
+  OUT VOID                      *Buffer
+  )
+{
+  UINTN                     Count;
+  EFI_PCI_IO_PROTOCOL_WIDTH Width;
+  EFI_PCI_IO_PROTOCOL       *PciIo;
+
+  ASSERT (FieldSize == BufferSize);
+
+  PciIo = Dev->PciIo;
+  Count = 1;
+
+  switch (FieldSize) {
+  case 1:
+    Width = EfiPciIoWidthUint8;
+    break;
+
+  case 2:
+    Width = EfiPciIoWidthUint16;
+    break;
+
+  case 4:
+    Width = EfiPciIoWidthUint32;
+    break;
+
+  case 8:
+    Width = EfiPciIoWidthUint64;
+    break;
+
+  default:
+    ASSERT (FALSE);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  return PciIo->Mem.Read (
+                     PciIo,
+                     Width,
+                     PCI_BAR_IDX0,
+                     FieldOffset,
+                     Count,
+                     Buffer
+                     );
+}
+
+/**
+
+  Write a word into Region 0 of the device specified by PciMemIo.
+
+  Region 0 must be an iomem region. This is an internal function for the PCI
+  implementation of the protocol.
+
+  @param[in] Dev          Virtio PCI device.
+
+  @param[in] FieldOffset  Destination offset.
+
+  @param[in] FieldSize    Destination field size, must be in { 1, 2, 4, 8 }.
+
+  @param[in] Value        Little endian value to write, converted to UINT64.
+                          The least significant FieldSize bytes will be used.
+
+
+  @return  Status code returned by PciIo->Io.Write().
+
+**/
+EFI_STATUS
+EFIAPI
+VirtioPciMemIoWrite (
+  IN  VIRTIO_PCI_DEVICE         *Dev,
+  IN UINTN                      FieldOffset,
+  IN UINTN                      FieldSize,
+  IN UINT64                     Value
+  )
+{
+  UINTN                     Count;
+  EFI_PCI_IO_PROTOCOL_WIDTH Width;
+  EFI_PCI_IO_PROTOCOL       *PciIo;
+
+  PciIo = Dev->PciIo;
+  Count = 1;
+
+  switch (FieldSize) {
+  case 1:
+    Width = EfiPciIoWidthUint8;
+    break;
+
+  case 2:
+    Width = EfiPciIoWidthUint16;
+    break;
+
+  case 4:
+    Width = EfiPciIoWidthUint32;
+    break;
+
+  case 8:
+    Width = EfiPciIoWidthUint64;
+    break;
+
+  default:
+    ASSERT (FALSE);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  return PciIo->Mem.Write (
+                     PciIo,
+                     Width,
+                     PCI_BAR_IDX0,
+                     FieldOffset,
+                     Count,
+                     &Value
+                     );
+}
+
+/**
+
   Device probe function for this driver.
 
   The DXE core calls this function for any given device in order to see if the
diff --git a/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.h 
b/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.h
index 1f0dc45d50..e91191689c 100644
--- a/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.h
+++ b/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.h
@@ -2,7 +2,7 @@
 
   Internal definitions for the VirtIo PCI Device driver
 
-  Copyright (C) 2013, ARM Ltd
+  Copyright (C) 2013-2018, ARM Ltd
   Copyright (c) 2017, AMD Inc, All rights reserved.<BR>
 
   This program and the accompanying materials are licensed and made available
@@ -58,6 +58,25 @@ VirtioPciIoWrite (
   IN UINT64                      Value
   );
 
+EFI_STATUS
+EFIAPI
+VirtioPciMemIoRead (
+  IN  VIRTIO_PCI_DEVICE         *Dev,
+  IN  UINTN                      FieldOffset,
+  IN  UINTN                      FieldSize,
+  IN  UINTN                      BufferSize,
+  OUT VOID                       *Buffer
+  );
+
+EFI_STATUS
+EFIAPI
+VirtioPciMemIoWrite (
+  IN  VIRTIO_PCI_DEVICE         *Dev,
+  IN UINTN                       FieldOffset,
+  IN UINTN                       FieldSize,
+  IN UINT64                      Value
+  );
+
 /********************************************
  * PCI Functions for VIRTIO_DEVICE_PROTOCOL
  *******************************************/
diff --git a/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf 
b/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf
index 4b5d1a493e..675b156c82 100644
--- a/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf
+++ b/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf
@@ -2,7 +2,7 @@
 # This driver produces the VirtIo Device Protocol instances for VirtIo PCI
 # Device
 #
-# Copyright (C) 2013, ARM Ltd
+# Copyright (C) 2013-2018, ARM Ltd
 #
 # This program and the accompanying materials are licensed and made available
 # under the terms and conditions of the BSD License which accompanies this
@@ -28,6 +28,7 @@
 
 [Packages]
   MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
   OvmfPkg/OvmfPkg.dec
 
 [LibraryClasses]
@@ -41,3 +42,4 @@
 [Protocols]
   gEfiPciIoProtocolGuid     ## TO_START
   gVirtioDeviceProtocolGuid ## BY_START
+  gEdkiiNonDiscoverableDeviceProtocolGuid
diff --git a/OvmfPkg/VirtioPciDeviceDxe/VirtioPciFunctions.c 
b/OvmfPkg/VirtioPciDeviceDxe/VirtioPciFunctions.c
index b52060d13d..45b49f0915 100644
--- a/OvmfPkg/VirtioPciDeviceDxe/VirtioPciFunctions.c
+++ b/OvmfPkg/VirtioPciDeviceDxe/VirtioPciFunctions.c
@@ -4,7 +4,7 @@
 
   Copyright (C) 2012, Red Hat, Inc.
   Copyright (c) 2012, Intel Corporation. All rights reserved.<BR>
-  Copyright (C) 2013, ARM Ltd.
+  Copyright (C) 2013-2018, ARM Ltd.
   Copyright (C) 2017, AMD Inc, All rights reserved.<BR>
 
   This program and the accompanying materials are licensed and made available
@@ -21,10 +21,76 @@
 #include <Library/MemoryAllocationLib.h>
 #include <Library/UefiBootServicesTableLib.h>
 #include <Library/UefiLib.h>
+
+#include <Protocol/NonDiscoverableDevice.h>
+
 #include "VirtioPciDevice.h"
 
 /**
 
+  Check if virtvo PCI device is a non discoverable virtio MMIO device.
+
+  @param[in] Dev       Pointer to the virtio PCI device structure.
+
+  @return              TRUE if PCI device is a non discoverable device.
+
+**/
+STATIC
+BOOLEAN
+VirtioIsNonDiscoverableMmioDevice (
+  IN VIRTIO_PCI_DEVICE  *Dev
+  )
+{
+  EFI_HANDLE                          *HandleBuffer;
+  EFI_PCI_IO_PROTOCOL                 *PciIo;
+  NON_DISCOVERABLE_DEVICE             *Device;
+  EFI_STATUS                          Status;
+  UINTN                               HIndex;
+  UINTN                               HandleCount;
+  BOOLEAN                             RetStatus;
+
+  RetStatus = FALSE;
+  Status = gBS->LocateHandleBuffer (ByProtocol,
+                                    &gEfiPciIoProtocolGuid,
+                                    NULL,
+                                    &HandleCount,
+                                    &HandleBuffer);
+
+  if (!EFI_ERROR (Status)) {
+    for (HIndex = 0; HIndex < HandleCount; ++HIndex) {
+      Status = gBS->OpenProtocol (HandleBuffer[HIndex],
+                                  &gEfiPciIoProtocolGuid,
+                                  (VOID **) &PciIo,
+                                  NULL,
+                                  NULL,
+                                  EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+
+      if (EFI_ERROR (Status)) {
+        continue;
+      }
+
+      if (PciIo == Dev->PciIo) {
+        Status = gBS->OpenProtocol (HandleBuffer[HIndex],
+                                    &gEdkiiNonDiscoverableDeviceProtocolGuid,
+                                    (VOID **) &Device,
+                                    NULL,
+                                    NULL,
+                                    EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+
+        if (!EFI_ERROR (Status)) {
+          RetStatus = TRUE;
+        }
+        break;
+      }
+    }
+  }
+
+  gBS->FreePool (HandleBuffer);
+  return RetStatus;
+}
+
+/**
+
   Read a word from Region 0 of the device specified by VirtIo Device protocol.
 
   The function implements the ReadDevice protocol member of
@@ -219,7 +285,21 @@ VirtioPciSetQueueAlignment (
   IN  UINT32                  Alignment
   )
 {
-  return EFI_SUCCESS;
+  VIRTIO_PCI_DEVICE *Dev;
+  EFI_STATUS Status;
+
+  Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This);
+  Status = EFI_SUCCESS;
+
+  // This register is required to be programmed for MMIO type of devices.
+  // Virtio MMIO device can be registered as a non-discoverable device on
+  // PCI bus, check if it's the case.
+  if (VirtioIsNonDiscoverableMmioDevice (Dev)) {
+    Status = VirtioPciMemIoWrite (Dev, VIRTIO_MMIO_OFFSET_QUEUE_ALIGN,
+                                  sizeof (UINT32), Alignment);
+  }
+
+  return Status;
 }
 
 EFI_STATUS
@@ -229,7 +309,22 @@ VirtioPciSetPageSize (
   IN  UINT32                  PageSize
   )
 {
-  return (PageSize == EFI_PAGE_SIZE) ? EFI_SUCCESS : EFI_UNSUPPORTED;
+  VIRTIO_PCI_DEVICE *Dev;
+  EFI_STATUS Status;
+
+  Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This);
+
+  // This register is required to be programmed for MMIO type of devices.
+  // Virtio MMIO device can be registered as a non-discoverable device on
+  // PCI bus, check if it's the case.
+  if (VirtioIsNonDiscoverableMmioDevice (Dev)) {
+    Status = VirtioPciMemIoWrite (Dev, VIRTIO_MMIO_OFFSET_GUEST_PAGE_SIZE,
+                                  sizeof (UINT32), PageSize);
+  } else {
+    Status = (PageSize == EFI_PAGE_SIZE) ? EFI_SUCCESS : EFI_UNSUPPORTED;
+  }
+
+  return Status;
 }
 
 EFI_STATUS
@@ -254,11 +349,25 @@ VirtioPciSetQueueSize (
   IN  UINT16                 Size
   )
 {
+  VIRTIO_PCI_DEVICE *Dev;
+  EFI_STATUS Status;
+
   //
   // This function is only applicable in Virtio-MMIO.
   // (The QueueSize field is read-only in Virtio proper (PCI))
   //
-  return EFI_SUCCESS;
+  Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This);
+  Status = EFI_SUCCESS;
+
+  // This register is required to be programmed for MMIO type of devices.
+  // Virtio MMIO device can be registered as a non-discoverable device on
+  // PCI bus, check if it's the case.
+  if (VirtioIsNonDiscoverableMmioDevice (Dev)) {
+    Status = VirtioPciMemIoWrite (Dev, VIRTIO_MMIO_OFFSET_QUEUE_NUM,
+                                  sizeof (UINT32), (UINT32) Size);
+  }
+
+  return Status;
 }
 
 EFI_STATUS
-- 
2.11.0

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel

Reply via email to