This defines and implements VFIO IOMMU API required to support
Dynamic DMA windows defined in the SPAPR specification. The ioctl handlers
implement host-size part of corresponding RTAS calls:
- VFIO_IOMMU_SPAPR_TCE_QUERY - ibm,query-pe-dma-window;
- VFIO_IOMMU_SPAPR_TCE_CREATE - ibm,create-pe-dma-window;
- VFIO_IOMMU_SPAPR_TCE_REMOVE - ibm,remove-pe-dma-window;
- VFIO_IOMMU_SPAPR_TCE_RESET - ibm,reset-pe-dma-window.

The VFIO IOMMU driver does basic sanity checks and calls corresponding
SPAPR TCE functions. At the moment only IODA2 (POWER8 PCI host bridge)
implements them.

This advertises VFIO_IOMMU_SPAPR_TCE_FLAG_DDW capability via
VFIO_IOMMU_SPAPR_TCE_GET_INFO.

This calls reset() when IOMMU is being disabled (happens when VFIO stops
using it).

Signed-off-by: Alexey Kardashevskiy <a...@ozlabs.ru>
---
 arch/powerpc/platforms/powernv/pci-ioda.c |   1 +
 drivers/vfio/vfio_iommu_spapr_tce.c       | 132 +++++++++++++++++++++++++++++-
 include/uapi/linux/vfio.h                 |  37 ++++++++-
 3 files changed, 168 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c 
b/arch/powerpc/platforms/powernv/pci-ioda.c
index 25a4f0e..63aa697 100644
--- a/arch/powerpc/platforms/powernv/pci-ioda.c
+++ b/arch/powerpc/platforms/powernv/pci-ioda.c
@@ -869,6 +869,7 @@ static long pnv_pci_ioda2_ddw_create(struct 
spapr_tce_iommu_group *data,
        tbl64->invalidate = pnv_pci_ioda2_tce_invalidate_64;
 
        /* Copy "invalidate" register address */
+       tbl64->it_group = pe->tce32_table.it_group;
        tbl64->it_index = pe->tce32_table.it_index;
        tbl64->it_type = TCE_PCI_SWINV_CREATE | TCE_PCI_SWINV_FREE |
                        TCE_PCI_SWINV_PAIR;
diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c 
b/drivers/vfio/vfio_iommu_spapr_tce.c
index 808c7d3..8f992de 100644
--- a/drivers/vfio/vfio_iommu_spapr_tce.c
+++ b/drivers/vfio/vfio_iommu_spapr_tce.c
@@ -124,13 +124,20 @@ static void tce_iommu_disable(struct tce_container 
*container)
 
        container->enabled = false;
 
-       if (!container->grp || !current->mm)
+       if (!container->grp)
                return;
 
        data = iommu_group_get_iommudata(container->grp);
        if (!data || !data->iommu_owner || !data->ops->get_table)
                return;
 
+       /* Try resetting, there might have been a 64bit window */
+       if (data->ops->reset)
+               data->ops->reset(data);
+
+       if (!current->mm)
+               return;
+
        tbl = data->ops->get_table(data, 0);
        if (!tbl)
                return;
@@ -213,6 +220,8 @@ static long tce_iommu_ioctl(void *iommu_data,
                info.dma32_window_start = tbl->it_offset << tbl->it_page_shift;
                info.dma32_window_size = tbl->it_size << tbl->it_page_shift;
                info.flags = 0;
+               if (data->ops->query && data->ops->create && data->ops->remove)
+                       info.flags |= VFIO_IOMMU_SPAPR_TCE_FLAG_DDW;
 
                if (copy_to_user((void __user *)arg, &info, minsz))
                        return -EFAULT;
@@ -338,6 +347,127 @@ static long tce_iommu_ioctl(void *iommu_data,
                tce_iommu_disable(container);
                mutex_unlock(&container->lock);
                return 0;
+
+       case VFIO_IOMMU_SPAPR_TCE_QUERY: {
+               struct vfio_iommu_spapr_tce_query query;
+               struct spapr_tce_iommu_group *data;
+
+               if (WARN_ON(!container->grp))
+                       return -ENXIO;
+
+               data = iommu_group_get_iommudata(container->grp);
+
+               minsz = offsetofend(struct vfio_iommu_spapr_tce_query,
+                               page_size_mask);
+
+               if (copy_from_user(&query, (void __user *)arg, minsz))
+                       return -EFAULT;
+
+               if (query.argsz < minsz)
+                       return -EINVAL;
+
+               if (!data->ops->query || !data->iommu_owner)
+                       return -ENOSYS;
+
+               ret = data->ops->query(data,
+                               &query.windows_available,
+                               &query.page_size_mask);
+
+               if (copy_to_user((void __user *)arg, &query, minsz))
+                       return -EFAULT;
+
+               return 0;
+       }
+       case VFIO_IOMMU_SPAPR_TCE_CREATE: {
+               struct vfio_iommu_spapr_tce_create create;
+               struct spapr_tce_iommu_group *data;
+               struct iommu_table *tbl;
+
+               if (WARN_ON(!container->grp))
+                       return -ENXIO;
+
+               data = iommu_group_get_iommudata(container->grp);
+
+               minsz = offsetofend(struct vfio_iommu_spapr_tce_create,
+                               start_addr);
+
+               if (copy_from_user(&create, (void __user *)arg, minsz))
+                       return -EFAULT;
+
+               if (create.argsz < minsz)
+                       return -EINVAL;
+
+               if (!data->ops->create || !data->iommu_owner)
+                       return -ENOSYS;
+
+               ret = data->ops->create(data, create.page_shift,
+                               create.window_shift, &tbl);
+               if (ret)
+                       return ret;
+
+               create.start_addr = tbl->it_offset << tbl->it_page_shift;
+
+               if (copy_to_user((void __user *)arg, &create, minsz))
+                       return -EFAULT;
+
+               return 0;
+       }
+       case VFIO_IOMMU_SPAPR_TCE_REMOVE: {
+               struct vfio_iommu_spapr_tce_remove remove;
+               struct spapr_tce_iommu_group *data;
+               struct iommu_table *tbl;
+
+               if (WARN_ON(!container->grp))
+                       return -ENXIO;
+
+               data = iommu_group_get_iommudata(container->grp);
+
+               minsz = offsetofend(struct vfio_iommu_spapr_tce_remove,
+                               start_addr);
+
+               if (copy_from_user(&remove, (void __user *)arg, minsz))
+                       return -EFAULT;
+
+               if (remove.argsz < minsz)
+                       return -EINVAL;
+
+               if (!data->ops->remove || !data->iommu_owner)
+                       return -ENOSYS;
+
+               tbl = data->ops->get_table(data, remove.start_addr);
+               if (!tbl)
+                       return -EINVAL;
+
+               ret = data->ops->remove(data, tbl);
+
+               return ret;
+       }
+       case VFIO_IOMMU_SPAPR_TCE_RESET: {
+               struct vfio_iommu_spapr_tce_reset reset;
+               struct spapr_tce_iommu_group *data;
+
+               if (WARN_ON(!container->grp))
+                       return -ENXIO;
+
+               data = iommu_group_get_iommudata(container->grp);
+
+               minsz = offsetofend(struct vfio_iommu_spapr_tce_reset, argsz);
+
+               if (copy_from_user(&reset, (void __user *)arg, minsz))
+                       return -EFAULT;
+
+               if (reset.argsz < minsz)
+                       return -EINVAL;
+
+               if (!data->ops->reset || !data->iommu_owner)
+                       return -ENOSYS;
+
+               ret = data->ops->reset(data);
+               if (ret)
+                       return ret;
+
+               return 0;
+       }
        }
 
        return -ENOTTY;
diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
index cb9023d..8b03381 100644
--- a/include/uapi/linux/vfio.h
+++ b/include/uapi/linux/vfio.h
@@ -448,13 +448,48 @@ struct vfio_iommu_type1_dma_unmap {
  */
 struct vfio_iommu_spapr_tce_info {
        __u32 argsz;
-       __u32 flags;                    /* reserved for future use */
+       __u32 flags;
+#define VFIO_IOMMU_SPAPR_TCE_FLAG_DDW  1 /* Support dynamic windows */
        __u32 dma32_window_start;       /* 32 bit window start (bytes) */
        __u32 dma32_window_size;        /* 32 bit window size (bytes) */
 };
 
 #define VFIO_IOMMU_SPAPR_TCE_GET_INFO  _IO(VFIO_TYPE, VFIO_BASE + 12)
 
+/*
+ * Dynamic DMA windows
+ */
+struct vfio_iommu_spapr_tce_query {
+       __u32 argsz;
+       /* out */
+       __u32 windows_available;
+       __u32 page_size_mask;
+};
+#define VFIO_IOMMU_SPAPR_TCE_QUERY     _IO(VFIO_TYPE, VFIO_BASE + 17)
+
+struct vfio_iommu_spapr_tce_create {
+       __u32 argsz;
+       /* in */
+       __u32 page_shift;
+       __u32 window_shift;
+       /* out */
+       __u64 start_addr;
+
+};
+#define VFIO_IOMMU_SPAPR_TCE_CREATE    _IO(VFIO_TYPE, VFIO_BASE + 18)
+
+struct vfio_iommu_spapr_tce_remove {
+       __u32 argsz;
+       /* in */
+       __u64 start_addr;
+};
+#define VFIO_IOMMU_SPAPR_TCE_REMOVE    _IO(VFIO_TYPE, VFIO_BASE + 19)
+
+struct vfio_iommu_spapr_tce_reset {
+       __u32 argsz;
+};
+#define VFIO_IOMMU_SPAPR_TCE_RESET     _IO(VFIO_TYPE, VFIO_BASE + 20)
+
 /* ***************************************************************** */
 
 #endif /* _UAPIVFIO_H */
-- 
2.0.0

_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/linuxppc-dev

Reply via email to