From: Jiang Liu <jiang....@huawei.com>

VFIO driver should reject binding unsafe drivers to devices belonging to
active VFIO groups, otherwise it will break the DMA isolation property
of VFIO groups. So hook IOMMU_GROUP_NOTIFY_QUERY_BINDING event to reject
unsafe device driver binding for active VFIO groups.

Signed-off-by: Jiang Liu <jiang....@huawei.com>
---
 drivers/vfio/pci/vfio_pci.c |   11 ++++-
 drivers/vfio/vfio.c         |  106 ++++++++++++++++++++++++++++++++++++-------
 include/linux/vfio.h        |    3 ++
 3 files changed, 102 insertions(+), 18 deletions(-)

diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index 6968b72..e380fc1 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -538,6 +538,7 @@ static struct pci_driver vfio_pci_driver = {
 static void __exit vfio_pci_cleanup(void)
 {
        pci_unregister_driver(&vfio_pci_driver);
+       vfio_unregister_device_driver(&vfio_pci_driver.driver);
        vfio_pci_virqfd_exit();
        vfio_pci_uninit_perm_bits();
 }
@@ -556,6 +557,10 @@ static int __init vfio_pci_init(void)
        if (ret)
                goto out_virqfd;
 
+       ret = vfio_register_device_driver(&vfio_pci_driver.driver);
+       if (ret)
+               goto out_vfio_drv;
+
        /* Register and scan for devices */
        ret = pci_register_driver(&vfio_pci_driver);
        if (ret)
@@ -563,9 +568,11 @@ static int __init vfio_pci_init(void)
 
        return 0;
 
-out_virqfd:
-       vfio_pci_virqfd_exit();
 out_driver:
+       vfio_unregister_device_driver(&vfio_pci_driver.driver);
+out_vfio_drv:
+       vfio_pci_virqfd_exit();
+out_virqfd:
        vfio_pci_uninit_perm_bits();
        return ret;
 }
diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
index 3359ec2..02da980 100644
--- a/drivers/vfio/vfio.c
+++ b/drivers/vfio/vfio.c
@@ -39,6 +39,8 @@ static struct vfio {
        struct class                    *class;
        struct list_head                iommu_drivers_list;
        struct mutex                    iommu_drivers_lock;
+       struct list_head                device_drivers_list;
+       struct mutex                    device_drivers_lock;
        struct list_head                group_list;
        struct idr                      group_idr;
        struct mutex                    group_lock;
@@ -54,6 +56,11 @@ struct vfio_iommu_driver {
        struct list_head                        vfio_next;
 };
 
+struct vfio_device_driver {
+       const struct device_driver              *drv;
+       struct list_head                        vfio_next;
+};
+
 struct vfio_container {
        struct kref                     kref;
        struct list_head                group_list;
@@ -135,6 +142,55 @@ void vfio_unregister_iommu_driver(const struct 
vfio_iommu_driver_ops *ops)
 EXPORT_SYMBOL_GPL(vfio_unregister_iommu_driver);
 
 /**
+ * VFIO device driver registration
+ */
+int vfio_register_device_driver(const struct device_driver *drv)
+{
+       struct vfio_device_driver *driver, *tmp;
+
+       driver = kzalloc(sizeof(*driver), GFP_KERNEL);
+       if (!driver)
+               return -ENOMEM;
+
+       driver->drv = drv;
+
+       mutex_lock(&vfio.device_drivers_lock);
+
+       /* Check for duplicates */
+       list_for_each_entry(tmp, &vfio.device_drivers_list, vfio_next) {
+               if (tmp->drv == drv) {
+                       mutex_unlock(&vfio.device_drivers_lock);
+                       kfree(driver);
+                       return -EINVAL;
+               }
+       }
+
+       list_add(&driver->vfio_next, &vfio.device_drivers_list);
+
+       mutex_unlock(&vfio.device_drivers_lock);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(vfio_register_device_driver);
+
+void vfio_unregister_device_driver(const struct device_driver *drv)
+{
+       struct vfio_device_driver *driver;
+
+       mutex_lock(&vfio.device_drivers_lock);
+       list_for_each_entry(driver, &vfio.device_drivers_list, vfio_next) {
+               if (driver->drv == drv) {
+                       list_del(&driver->vfio_next);
+                       mutex_unlock(&vfio.device_drivers_lock);
+                       kfree(driver);
+                       return;
+               }
+       }
+       mutex_unlock(&vfio.device_drivers_lock);
+}
+EXPORT_SYMBOL_GPL(vfio_unregister_device_driver);
+
+/**
  * Group minor allocation/free - both called with vfio.group_lock held
  */
 static int vfio_alloc_group_minor(struct vfio_group *group)
@@ -463,17 +519,18 @@ static bool vfio_whitelisted_driver(struct device_driver 
*drv)
  */
 static int vfio_dev_viable(struct device *dev, void *data)
 {
-       struct vfio_group *group = data;
-       struct vfio_device *device;
+       struct vfio_device_driver *driver;
 
        if (!dev->driver || vfio_whitelisted_driver(dev->driver))
                return 0;
 
-       device = vfio_group_get_device(group, dev);
-       if (device) {
-               vfio_device_put(device);
-               return 0;
-       }
+       mutex_lock(&vfio.device_drivers_lock);
+       list_for_each_entry(driver, &vfio.device_drivers_list, vfio_next)
+               if (driver->drv == dev->driver) {
+                       mutex_unlock(&vfio.device_drivers_lock);
+                       return 0;
+               }
+       mutex_unlock(&vfio.device_drivers_lock);
 
        return -EINVAL;
 }
@@ -496,7 +553,6 @@ static int vfio_group_nb_add_dev(struct vfio_group *group, 
struct device *dev)
        if (!atomic_read(&group->container_users))
                return 0;
 
-       /* TODO Prevent device auto probing */
        WARN("Device %s added to live group %d!\n", dev_name(dev),
             iommu_group_id(group->iommu_group));
 
@@ -533,9 +589,28 @@ static int vfio_group_nb_verify(struct vfio_group *group, 
struct device *dev)
        return vfio_dev_viable(dev, group);
 }
 
+static int vfio_group_nb_solicit_binding(struct vfio_group *group,
+                                        struct device *dev)
+{
+       int ret;
+
+       /* Allow driver binding for idle groups */
+       if (!atomic_read(&group->container_users))
+               return 0;
+
+       ret = vfio_dev_viable(dev, group);
+       if (ret)
+               dev_info(dev,
+                        "VFIO rejects binding device in active group to unsafe 
driver %s!\n",
+                        dev_driver_string(dev));
+
+       return ret;
+}
+
 static int vfio_iommu_group_notifier(struct notifier_block *nb,
                                     unsigned long action, void *data)
 {
+       int ret = NOTIFY_DONE;
        struct vfio_group *group = container_of(nb, struct vfio_group, nb);
        struct device *dev = data;
 
@@ -556,6 +631,10 @@ static int vfio_iommu_group_notifier(struct notifier_block 
*nb,
        case IOMMU_GROUP_NOTIFY_DEL_DEVICE:
                vfio_group_nb_del_dev(group, dev);
                break;
+       case IOMMU_GROUP_NOTIFY_SOLICIT_BINDING:
+               if (vfio_group_nb_solicit_binding(group, dev))
+                       ret = notifier_from_errno(-EBUSY);
+               break;
        case IOMMU_GROUP_NOTIFY_BIND_DRIVER:
                pr_debug("%s: Device %s, group %d binding to driver\n",
                         __func__, dev_name(dev),
@@ -576,18 +655,11 @@ static int vfio_iommu_group_notifier(struct 
notifier_block *nb,
                pr_debug("%s: Device %s, group %d unbound from driver\n",
                         __func__, dev_name(dev),
                         iommu_group_id(group->iommu_group));
-               /*
-                * XXX An unbound device in a live group is ok, but we'd
-                * really like to avoid the above BUG_ON by preventing other
-                * drivers from binding to it.  Once that occurs, we have to
-                * stop the system to maintain isolation.  At a minimum, we'd
-                * want a toggle to disable driver auto probe for this device.
-                */
                break;
        }
 
        vfio_group_put(group);
-       return NOTIFY_OK;
+       return ret;
 }
 
 /**
@@ -1334,8 +1406,10 @@ static int __init vfio_init(void)
        idr_init(&vfio.group_idr);
        mutex_init(&vfio.group_lock);
        mutex_init(&vfio.iommu_drivers_lock);
+       mutex_init(&vfio.device_drivers_lock);
        INIT_LIST_HEAD(&vfio.group_list);
        INIT_LIST_HEAD(&vfio.iommu_drivers_list);
+       INIT_LIST_HEAD(&vfio.device_drivers_list);
        init_waitqueue_head(&vfio.release_q);
 
        vfio.class = class_create(THIS_MODULE, "vfio");
diff --git a/include/linux/vfio.h b/include/linux/vfio.h
index 0a4f180..4faa9b9 100644
--- a/include/linux/vfio.h
+++ b/include/linux/vfio.h
@@ -78,6 +78,9 @@ extern int vfio_register_iommu_driver(const struct 
vfio_iommu_driver_ops *ops);
 extern void vfio_unregister_iommu_driver(
                                const struct vfio_iommu_driver_ops *ops);
 
+extern int vfio_register_device_driver(const struct device_driver *drv);
+extern void vfio_unregister_device_driver(const struct device_driver *drv);
+
 /**
  * offsetofend(TYPE, MEMBER)
  *
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to