@@ -11,6 +11,7 @@
#include <linux/iommu.h>
#include <linux/miscdevice.h>
#include <linux/vfio.h>
+#include <linux/pkernfs.h>
#include <uapi/linux/vfio.h>
#include "vfio.h"
@@ -21,6 +22,7 @@ struct vfio_container {
struct rw_semaphore group_lock;
struct vfio_iommu_driver *iommu_driver;
void *iommu_data;
+ struct file *persistent_pgtables;
bool noiommu;
};
@@ -306,6 +308,8 @@ static long vfio_ioctl_set_iommu(struct vfio_container *container,
continue;
}
+ driver->ops->set_persistent_pgtables(data, container->persistent_pgtables);
+
ret = __vfio_container_attach_groups(container, driver, data);
if (ret) {
driver->ops->release(data);
@@ -324,6 +328,26 @@ static long vfio_ioctl_set_iommu(struct vfio_container *container,
return ret;
}
+static int vfio_ioctl_set_persistent_pgtables(struct vfio_container *container,
+ unsigned long arg)
+{
+ struct vfio_set_persistent_pgtables set_ppts;
+ struct file *ppts;
+
+ if (copy_from_user(&set_ppts, (void __user *)arg, sizeof(set_ppts)))
+ return -EFAULT;
+
+ ppts = fget(set_ppts.persistent_pgtables_fd);
+ if (!ppts)
+ return -EBADF;
+ if (!pkernfs_is_iommu_domain_pgtables(ppts)) {
+ fput(ppts);
+ return -EBADF;
+ }
+ container->persistent_pgtables = ppts;
+ return 0;
+}
+
static long vfio_fops_unl_ioctl(struct file *filep,
unsigned int cmd, unsigned long arg)
{
@@ -345,6 +369,9 @@ static long vfio_fops_unl_ioctl(struct file *filep,
case VFIO_SET_IOMMU:
ret = vfio_ioctl_set_iommu(container, arg);
break;
+ case VFIO_CONTAINER_SET_PERSISTENT_PGTABLES:
+ ret = vfio_ioctl_set_persistent_pgtables(container, arg);
+ break;
default:
driver = container->iommu_driver;
data = container->iommu_data;
@@ -226,6 +226,8 @@ struct vfio_iommu_driver_ops {
void *data, size_t count, bool write);
struct iommu_domain *(*group_iommu_domain)(void *iommu_data,
struct iommu_group *group);
+ int (*set_persistent_pgtables)(void *iommu_data,
+ struct file *ppts);
};
struct vfio_iommu_driver {
@@ -75,6 +75,7 @@ struct vfio_iommu {
bool nesting;
bool dirty_page_tracking;
struct list_head emulated_iommu_groups;
+ struct file *persistent_pgtables;
};
struct vfio_domain {
@@ -2143,9 +2144,14 @@ static void vfio_iommu_iova_insert_copy(struct vfio_iommu *iommu,
static int vfio_iommu_domain_alloc(struct device *dev, void *data)
{
+ /* data is an in pointer to PPTs, and an out to the new domain. */
+ struct file *ppts = *(struct file **) data;
struct iommu_domain **domain = data;
- *domain = iommu_domain_alloc(dev->bus);
+ if (ppts)
+ *domain = iommu_domain_alloc_persistent(dev->bus, ppts);
+ else
+ *domain = iommu_domain_alloc(dev->bus);
return 1; /* Don't iterate */
}
@@ -2156,6 +2162,8 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
struct vfio_iommu_group *group;
struct vfio_domain *domain, *d;
bool resv_msi;
+ /* In/out ptr to iommu_domain_alloc. */
+ void *domain_alloc_data;
phys_addr_t resv_msi_base = 0;
struct iommu_domain_geometry *geo;
LIST_HEAD(iova_copy);
@@ -2203,8 +2211,12 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
* want to iterate beyond the first device (if any).
*/
ret = -EIO;
- iommu_group_for_each_dev(iommu_group, &domain->domain,
+ /* Smuggle the PPTs in the data field; it will be clobbered with the new domain */
+ domain_alloc_data = iommu->persistent_pgtables;
+ iommu_group_for_each_dev(iommu_group, &domain_alloc_data,
vfio_iommu_domain_alloc);
+ domain->domain = domain_alloc_data;
+
if (!domain->domain)
goto out_free_domain;
@@ -3165,6 +3177,16 @@ vfio_iommu_type1_group_iommu_domain(void *iommu_data,
return domain;
}
+int vfio_iommu_type1_set_persistent_pgtables(void *iommu_data,
+ struct file *ppts)
+{
+
+ struct vfio_iommu *iommu = iommu_data;
+
+ iommu->persistent_pgtables = ppts;
+ return 0;
+}
+
static const struct vfio_iommu_driver_ops vfio_iommu_driver_ops_type1 = {
.name = "vfio-iommu-type1",
.owner = THIS_MODULE,
@@ -3179,6 +3201,7 @@ static const struct vfio_iommu_driver_ops vfio_iommu_driver_ops_type1 = {
.unregister_device = vfio_iommu_type1_unregister_device,
.dma_rw = vfio_iommu_type1_dma_rw,
.group_iommu_domain = vfio_iommu_type1_group_iommu_domain,
+ .set_persistent_pgtables = vfio_iommu_type1_set_persistent_pgtables,
};
static int __init vfio_iommu_type1_init(void)
@@ -1797,6 +1797,15 @@ struct vfio_iommu_spapr_tce_remove {
};
#define VFIO_IOMMU_SPAPR_TCE_REMOVE _IO(VFIO_TYPE, VFIO_BASE + 20)
+struct vfio_set_persistent_pgtables {
+ /*
+ * File descriptor for a pkernfs IOMMU pgtables
+ * file to be used for persistence.
+ */
+ __u32 persistent_pgtables_fd;
+};
+#define VFIO_CONTAINER_SET_PERSISTENT_PGTABLES _IO(VFIO_TYPE, VFIO_BASE + 21)
+
/* ***************************************************************** */
#endif /* _UAPIVFIO_H */