Generally we don't know about vfio bus drivers until a device is added to the vfio-core with vfio_add_group_dev(), this optional registration with vfio_register_bus_driver() allows vfio-core to track known drivers. Our current use for this information is to know whether a driver is vfio compatible during a bind operation. For devices on buses with driver_override support, we can use this linkage to block non-vfio drivers from binding to devices where the iommu group state would trigger a BUG to avoid host/user integrity issues.
Signed-off-by: Alex Williamson <alex.william...@redhat.com> Reviewed-by: Eric Auger <eric.au...@redhat.com> --- drivers/vfio/vfio.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/vfio.h | 3 +++ 2 files changed, 58 insertions(+) diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index 7597a377eb4e..f40d1508d368 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c @@ -43,6 +43,8 @@ struct class *class; struct list_head iommu_drivers_list; struct mutex iommu_drivers_lock; + struct list_head bus_drivers_list; + struct mutex bus_drivers_lock; struct list_head group_list; struct idr group_idr; struct mutex group_lock; @@ -51,6 +53,11 @@ wait_queue_head_t release_q; } vfio; +struct vfio_bus_driver { + struct device_driver *drv; + struct list_head vfio_next; +}; + struct vfio_iommu_driver { const struct vfio_iommu_driver_ops *ops; struct list_head vfio_next; @@ -2243,6 +2250,52 @@ int vfio_unregister_notifier(struct device *dev, enum vfio_notify_type type, } EXPORT_SYMBOL(vfio_unregister_notifier); +int vfio_register_bus_driver(struct device_driver *drv) +{ + struct vfio_bus_driver *driver, *tmp; + + driver = kzalloc(sizeof(*driver), GFP_KERNEL); + if (!driver) + return -ENOMEM; + + driver->drv = drv; + + mutex_lock(&vfio.bus_drivers_lock); + + /* Check for duplicates */ + list_for_each_entry(tmp, &vfio.bus_drivers_list, vfio_next) { + if (tmp->drv == drv) { + mutex_unlock(&vfio.bus_drivers_lock); + kfree(driver); + return -EINVAL; + } + } + + list_add(&driver->vfio_next, &vfio.bus_drivers_list); + + mutex_unlock(&vfio.bus_drivers_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(vfio_register_bus_driver); + +void vfio_unregister_bus_driver(struct device_driver *drv) +{ + struct vfio_bus_driver *driver; + + mutex_lock(&vfio.bus_drivers_lock); + list_for_each_entry(driver, &vfio.bus_drivers_list, vfio_next) { + if (driver->drv == drv) { + list_del(&driver->vfio_next); + mutex_unlock(&vfio.bus_drivers_lock); + kfree(driver); + return; + } + } + mutex_unlock(&vfio.bus_drivers_lock); +} +EXPORT_SYMBOL_GPL(vfio_unregister_bus_driver); + /** * Module/class support */ @@ -2266,8 +2319,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.bus_drivers_lock); INIT_LIST_HEAD(&vfio.group_list); INIT_LIST_HEAD(&vfio.iommu_drivers_list); + INIT_LIST_HEAD(&vfio.bus_drivers_list); init_waitqueue_head(&vfio.release_q); ret = misc_register(&vfio_dev); diff --git a/include/linux/vfio.h b/include/linux/vfio.h index 9b34d0af5d27..dab0f8105e4a 100644 --- a/include/linux/vfio.h +++ b/include/linux/vfio.h @@ -92,6 +92,9 @@ struct vfio_iommu_driver_ops { extern void vfio_unregister_iommu_driver( const struct vfio_iommu_driver_ops *ops); +extern int vfio_register_bus_driver(struct device_driver *drv); +extern void vfio_unregister_bus_driver(struct device_driver *drv); + /* * External user API */