Add a new VFIO_IRQ_SET_ACTION_PREPARE to set VFIO_IRQ_SET_DATA_MSI_IOVA,
giving user space an interface to forward to kernel the stage-1 IOVA (of
a 2-stage translation: IOVA->IPA->PA) for an MSI doorbell address, since
the ITS hardware needs to be programmed with the top level IOVA address,
in order to work with the IOMMU on ARM64.

Signed-off-by: Nicolin Chen <nicol...@nvidia.com>
---
 include/uapi/linux/vfio.h         |  8 ++++--
 drivers/vfio/pci/vfio_pci_intrs.c | 46 +++++++++++++++++++++++++++++++
 drivers/vfio/vfio_main.c          |  3 ++
 3 files changed, 55 insertions(+), 2 deletions(-)

diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
index c8dbf8219c4f..85095e59a3c6 100644
--- a/include/uapi/linux/vfio.h
+++ b/include/uapi/linux/vfio.h
@@ -590,6 +590,8 @@ struct vfio_irq_set {
 #define VFIO_IRQ_SET_ACTION_MASK       (1 << 3) /* Mask interrupt */
 #define VFIO_IRQ_SET_ACTION_UNMASK     (1 << 4) /* Unmask interrupt */
 #define VFIO_IRQ_SET_ACTION_TRIGGER    (1 << 5) /* Trigger interrupt */
+#define VFIO_IRQ_SET_DATA_MSI_IOVA     (1 << 6) /* Data is MSI IOVA (u64) */
+#define VFIO_IRQ_SET_ACTION_PREPARE    (1 << 7) /* Prepare interrupt */
        __u32   index;
        __u32   start;
        __u32   count;
@@ -599,10 +601,12 @@ struct vfio_irq_set {
 
 #define VFIO_IRQ_SET_DATA_TYPE_MASK    (VFIO_IRQ_SET_DATA_NONE | \
                                         VFIO_IRQ_SET_DATA_BOOL | \
-                                        VFIO_IRQ_SET_DATA_EVENTFD)
+                                        VFIO_IRQ_SET_DATA_EVENTFD | \
+                                        VFIO_IRQ_SET_DATA_MSI_IOVA)
 #define VFIO_IRQ_SET_ACTION_TYPE_MASK  (VFIO_IRQ_SET_ACTION_MASK | \
                                         VFIO_IRQ_SET_ACTION_UNMASK | \
-                                        VFIO_IRQ_SET_ACTION_TRIGGER)
+                                        VFIO_IRQ_SET_ACTION_TRIGGER | \
+                                        VFIO_IRQ_SET_ACTION_PREPARE)
 /**
  * VFIO_DEVICE_RESET - _IO(VFIO_TYPE, VFIO_BASE + 11)
  *
diff --git a/drivers/vfio/pci/vfio_pci_intrs.c 
b/drivers/vfio/pci/vfio_pci_intrs.c
index 8382c5834335..80ea6bc1941f 100644
--- a/drivers/vfio/pci/vfio_pci_intrs.c
+++ b/drivers/vfio/pci/vfio_pci_intrs.c
@@ -685,6 +685,8 @@ static int vfio_pci_set_msi_trigger(struct 
vfio_pci_core_device *vdev,
 
        if (irq_is(vdev, index) && !count && (flags & VFIO_IRQ_SET_DATA_NONE)) {
                vfio_msi_disable(vdev, msix);
+               for (i = start; i < start + count; i++)
+                       vfio_iommufd_device_unset_msi_iova(&vdev->vdev, i);
                return 0;
        }
 
@@ -728,6 +730,47 @@ static int vfio_pci_set_msi_trigger(struct 
vfio_pci_core_device *vdev,
        return 0;
 }
 
+static int vfio_pci_set_msi_prepare(struct vfio_pci_core_device *vdev,
+                                   unsigned int index, unsigned int start,
+                                   unsigned int count, uint32_t flags,
+                                   void *data)
+{
+       struct vfio_device *core = &vdev->vdev;
+       uint64_t *iovas = data;
+       unsigned int i;
+       int ret;
+
+       if (!vfio_iommufd_device_ictx(core))
+               return -EOPNOTSUPP;
+       if (!(irq_is(vdev, index) || is_irq_none(vdev)))
+               return -EINVAL;
+
+       if (flags & VFIO_IRQ_SET_DATA_NONE) {
+               if (!count)
+                       return -EINVAL;
+               for (i = start; i < start + count; i++)
+                       vfio_iommufd_device_unset_msi_iova(core, i);
+               return 0;
+       }
+
+       if (!(flags & VFIO_IRQ_SET_DATA_MSI_IOVA))
+               return -EOPNOTSUPP;
+       if (!IS_ENABLED(CONFIG_IRQ_MSI_IOMMU))
+               return -EOPNOTSUPP;
+
+       ret = vfio_iommufd_device_set_num_msi_iovas(core, start + count);
+       if (ret)
+               return ret;
+
+       for (i = start; i < start + count; i++) {
+               ret = vfio_iommufd_device_set_msi_iova(core, i, iovas[i]);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
 static int vfio_pci_set_ctx_trigger_single(struct eventfd_ctx **ctx,
                                           unsigned int count, uint32_t flags,
                                           void *data)
@@ -837,6 +880,9 @@ int vfio_pci_set_irqs_ioctl(struct vfio_pci_core_device 
*vdev, uint32_t flags,
                case VFIO_IRQ_SET_ACTION_TRIGGER:
                        func = vfio_pci_set_msi_trigger;
                        break;
+               case VFIO_IRQ_SET_ACTION_PREPARE:
+                       func = vfio_pci_set_msi_prepare;
+                       break;
                }
                break;
        case VFIO_PCI_ERR_IRQ_INDEX:
diff --git a/drivers/vfio/vfio_main.c b/drivers/vfio/vfio_main.c
index 1fd261efc582..ad11c8e7da7b 100644
--- a/drivers/vfio/vfio_main.c
+++ b/drivers/vfio/vfio_main.c
@@ -1554,6 +1554,9 @@ int vfio_set_irqs_validate_and_prepare(struct 
vfio_irq_set *hdr, int num_irqs,
        case VFIO_IRQ_SET_DATA_EVENTFD:
                size = sizeof(int32_t);
                break;
+       case VFIO_IRQ_SET_DATA_MSI_IOVA:
+               size = sizeof(uint64_t);
+               break;
        default:
                return -EINVAL;
        }
-- 
2.43.0


Reply via email to