1fb1ff4c1SBharat Bhushan // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) 2fb1ff4c1SBharat Bhushan /* 3fb1ff4c1SBharat Bhushan * Copyright 2013-2016 Freescale Semiconductor Inc. 4fb1ff4c1SBharat Bhushan * Copyright 2016-2017,2019-2020 NXP 5fb1ff4c1SBharat Bhushan */ 6fb1ff4c1SBharat Bhushan 7fb1ff4c1SBharat Bhushan #include <linux/device.h> 8fb1ff4c1SBharat Bhushan #include <linux/iommu.h> 9fb1ff4c1SBharat Bhushan #include <linux/module.h> 10fb1ff4c1SBharat Bhushan #include <linux/mutex.h> 11fb1ff4c1SBharat Bhushan #include <linux/slab.h> 12fb1ff4c1SBharat Bhushan #include <linux/types.h> 13fb1ff4c1SBharat Bhushan #include <linux/vfio.h> 14fb1ff4c1SBharat Bhushan #include <linux/fsl/mc.h> 15fb1ff4c1SBharat Bhushan 16fb1ff4c1SBharat Bhushan #include "vfio_fsl_mc_private.h" 17fb1ff4c1SBharat Bhushan 18704f5082SDiana Craciun static struct fsl_mc_driver vfio_fsl_mc_driver; 19704f5082SDiana Craciun 20fb1ff4c1SBharat Bhushan static int vfio_fsl_mc_open(void *device_data) 21fb1ff4c1SBharat Bhushan { 22fb1ff4c1SBharat Bhushan if (!try_module_get(THIS_MODULE)) 23fb1ff4c1SBharat Bhushan return -ENODEV; 24fb1ff4c1SBharat Bhushan 25fb1ff4c1SBharat Bhushan return 0; 26fb1ff4c1SBharat Bhushan } 27fb1ff4c1SBharat Bhushan 28fb1ff4c1SBharat Bhushan static void vfio_fsl_mc_release(void *device_data) 29fb1ff4c1SBharat Bhushan { 30fb1ff4c1SBharat Bhushan module_put(THIS_MODULE); 31fb1ff4c1SBharat Bhushan } 32fb1ff4c1SBharat Bhushan 33fb1ff4c1SBharat Bhushan static long vfio_fsl_mc_ioctl(void *device_data, unsigned int cmd, 34fb1ff4c1SBharat Bhushan unsigned long arg) 35fb1ff4c1SBharat Bhushan { 36*f97f4c04SDiana Craciun unsigned long minsz; 37*f97f4c04SDiana Craciun struct vfio_fsl_mc_device *vdev = device_data; 38*f97f4c04SDiana Craciun struct fsl_mc_device *mc_dev = vdev->mc_dev; 39*f97f4c04SDiana Craciun 40fb1ff4c1SBharat Bhushan switch (cmd) { 41fb1ff4c1SBharat Bhushan case VFIO_DEVICE_GET_INFO: 42fb1ff4c1SBharat Bhushan { 43*f97f4c04SDiana Craciun struct vfio_device_info info; 44*f97f4c04SDiana Craciun 45*f97f4c04SDiana Craciun minsz = offsetofend(struct vfio_device_info, num_irqs); 46*f97f4c04SDiana Craciun 47*f97f4c04SDiana Craciun if (copy_from_user(&info, (void __user *)arg, minsz)) 48*f97f4c04SDiana Craciun return -EFAULT; 49*f97f4c04SDiana Craciun 50*f97f4c04SDiana Craciun if (info.argsz < minsz) 51*f97f4c04SDiana Craciun return -EINVAL; 52*f97f4c04SDiana Craciun 53*f97f4c04SDiana Craciun info.flags = VFIO_DEVICE_FLAGS_FSL_MC; 54*f97f4c04SDiana Craciun info.num_regions = mc_dev->obj_desc.region_count; 55*f97f4c04SDiana Craciun info.num_irqs = mc_dev->obj_desc.irq_count; 56*f97f4c04SDiana Craciun 57*f97f4c04SDiana Craciun return copy_to_user((void __user *)arg, &info, minsz) ? 58*f97f4c04SDiana Craciun -EFAULT : 0; 59fb1ff4c1SBharat Bhushan } 60fb1ff4c1SBharat Bhushan case VFIO_DEVICE_GET_REGION_INFO: 61fb1ff4c1SBharat Bhushan { 62fb1ff4c1SBharat Bhushan return -ENOTTY; 63fb1ff4c1SBharat Bhushan } 64fb1ff4c1SBharat Bhushan case VFIO_DEVICE_GET_IRQ_INFO: 65fb1ff4c1SBharat Bhushan { 66fb1ff4c1SBharat Bhushan return -ENOTTY; 67fb1ff4c1SBharat Bhushan } 68fb1ff4c1SBharat Bhushan case VFIO_DEVICE_SET_IRQS: 69fb1ff4c1SBharat Bhushan { 70fb1ff4c1SBharat Bhushan return -ENOTTY; 71fb1ff4c1SBharat Bhushan } 72fb1ff4c1SBharat Bhushan case VFIO_DEVICE_RESET: 73fb1ff4c1SBharat Bhushan { 74fb1ff4c1SBharat Bhushan return -ENOTTY; 75fb1ff4c1SBharat Bhushan } 76fb1ff4c1SBharat Bhushan default: 77fb1ff4c1SBharat Bhushan return -ENOTTY; 78fb1ff4c1SBharat Bhushan } 79fb1ff4c1SBharat Bhushan } 80fb1ff4c1SBharat Bhushan 81fb1ff4c1SBharat Bhushan static ssize_t vfio_fsl_mc_read(void *device_data, char __user *buf, 82fb1ff4c1SBharat Bhushan size_t count, loff_t *ppos) 83fb1ff4c1SBharat Bhushan { 84fb1ff4c1SBharat Bhushan return -EINVAL; 85fb1ff4c1SBharat Bhushan } 86fb1ff4c1SBharat Bhushan 87fb1ff4c1SBharat Bhushan static ssize_t vfio_fsl_mc_write(void *device_data, const char __user *buf, 88fb1ff4c1SBharat Bhushan size_t count, loff_t *ppos) 89fb1ff4c1SBharat Bhushan { 90fb1ff4c1SBharat Bhushan return -EINVAL; 91fb1ff4c1SBharat Bhushan } 92fb1ff4c1SBharat Bhushan 93fb1ff4c1SBharat Bhushan static int vfio_fsl_mc_mmap(void *device_data, struct vm_area_struct *vma) 94fb1ff4c1SBharat Bhushan { 95fb1ff4c1SBharat Bhushan return -EINVAL; 96fb1ff4c1SBharat Bhushan } 97fb1ff4c1SBharat Bhushan 98fb1ff4c1SBharat Bhushan static const struct vfio_device_ops vfio_fsl_mc_ops = { 99fb1ff4c1SBharat Bhushan .name = "vfio-fsl-mc", 100fb1ff4c1SBharat Bhushan .open = vfio_fsl_mc_open, 101fb1ff4c1SBharat Bhushan .release = vfio_fsl_mc_release, 102fb1ff4c1SBharat Bhushan .ioctl = vfio_fsl_mc_ioctl, 103fb1ff4c1SBharat Bhushan .read = vfio_fsl_mc_read, 104fb1ff4c1SBharat Bhushan .write = vfio_fsl_mc_write, 105fb1ff4c1SBharat Bhushan .mmap = vfio_fsl_mc_mmap, 106fb1ff4c1SBharat Bhushan }; 107fb1ff4c1SBharat Bhushan 108704f5082SDiana Craciun static int vfio_fsl_mc_bus_notifier(struct notifier_block *nb, 109704f5082SDiana Craciun unsigned long action, void *data) 110704f5082SDiana Craciun { 111704f5082SDiana Craciun struct vfio_fsl_mc_device *vdev = container_of(nb, 112704f5082SDiana Craciun struct vfio_fsl_mc_device, nb); 113704f5082SDiana Craciun struct device *dev = data; 114704f5082SDiana Craciun struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); 115704f5082SDiana Craciun struct fsl_mc_device *mc_cont = to_fsl_mc_device(mc_dev->dev.parent); 116704f5082SDiana Craciun 117704f5082SDiana Craciun if (action == BUS_NOTIFY_ADD_DEVICE && 118704f5082SDiana Craciun vdev->mc_dev == mc_cont) { 119704f5082SDiana Craciun mc_dev->driver_override = kasprintf(GFP_KERNEL, "%s", 120704f5082SDiana Craciun vfio_fsl_mc_ops.name); 121704f5082SDiana Craciun if (!mc_dev->driver_override) 122704f5082SDiana Craciun dev_warn(dev, "VFIO_FSL_MC: Setting driver override for device in dprc %s failed\n", 123704f5082SDiana Craciun dev_name(&mc_cont->dev)); 124704f5082SDiana Craciun else 125704f5082SDiana Craciun dev_info(dev, "VFIO_FSL_MC: Setting driver override for device in dprc %s\n", 126704f5082SDiana Craciun dev_name(&mc_cont->dev)); 127704f5082SDiana Craciun } else if (action == BUS_NOTIFY_BOUND_DRIVER && 128704f5082SDiana Craciun vdev->mc_dev == mc_cont) { 129704f5082SDiana Craciun struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver); 130704f5082SDiana Craciun 131704f5082SDiana Craciun if (mc_drv && mc_drv != &vfio_fsl_mc_driver) 132704f5082SDiana Craciun dev_warn(dev, "VFIO_FSL_MC: Object %s bound to driver %s while DPRC bound to vfio-fsl-mc\n", 133704f5082SDiana Craciun dev_name(dev), mc_drv->driver.name); 134704f5082SDiana Craciun } 135704f5082SDiana Craciun 136704f5082SDiana Craciun return 0; 137704f5082SDiana Craciun } 138704f5082SDiana Craciun 139704f5082SDiana Craciun static int vfio_fsl_mc_init_device(struct vfio_fsl_mc_device *vdev) 140704f5082SDiana Craciun { 141704f5082SDiana Craciun struct fsl_mc_device *mc_dev = vdev->mc_dev; 142704f5082SDiana Craciun int ret; 143704f5082SDiana Craciun 144704f5082SDiana Craciun /* Non-dprc devices share mc_io from parent */ 145704f5082SDiana Craciun if (!is_fsl_mc_bus_dprc(mc_dev)) { 146704f5082SDiana Craciun struct fsl_mc_device *mc_cont = to_fsl_mc_device(mc_dev->dev.parent); 147704f5082SDiana Craciun 148704f5082SDiana Craciun mc_dev->mc_io = mc_cont->mc_io; 149704f5082SDiana Craciun return 0; 150704f5082SDiana Craciun } 151704f5082SDiana Craciun 152704f5082SDiana Craciun vdev->nb.notifier_call = vfio_fsl_mc_bus_notifier; 153704f5082SDiana Craciun ret = bus_register_notifier(&fsl_mc_bus_type, &vdev->nb); 154704f5082SDiana Craciun if (ret) 155704f5082SDiana Craciun return ret; 156704f5082SDiana Craciun 157704f5082SDiana Craciun /* open DPRC, allocate a MC portal */ 158704f5082SDiana Craciun ret = dprc_setup(mc_dev); 159704f5082SDiana Craciun if (ret) { 160704f5082SDiana Craciun dev_err(&mc_dev->dev, "VFIO_FSL_MC: Failed to setup DPRC (%d)\n", ret); 161704f5082SDiana Craciun goto out_nc_unreg; 162704f5082SDiana Craciun } 163704f5082SDiana Craciun 164704f5082SDiana Craciun ret = dprc_scan_container(mc_dev, false); 165704f5082SDiana Craciun if (ret) { 166704f5082SDiana Craciun dev_err(&mc_dev->dev, "VFIO_FSL_MC: Container scanning failed (%d)\n", ret); 167704f5082SDiana Craciun goto out_dprc_cleanup; 168704f5082SDiana Craciun } 169704f5082SDiana Craciun 170704f5082SDiana Craciun return 0; 171704f5082SDiana Craciun 172704f5082SDiana Craciun out_dprc_cleanup: 173704f5082SDiana Craciun dprc_remove_devices(mc_dev, NULL, 0); 174704f5082SDiana Craciun dprc_cleanup(mc_dev); 175704f5082SDiana Craciun out_nc_unreg: 176704f5082SDiana Craciun bus_unregister_notifier(&fsl_mc_bus_type, &vdev->nb); 177704f5082SDiana Craciun vdev->nb.notifier_call = NULL; 178704f5082SDiana Craciun 179704f5082SDiana Craciun return ret; 180704f5082SDiana Craciun } 181704f5082SDiana Craciun 182fb1ff4c1SBharat Bhushan static int vfio_fsl_mc_probe(struct fsl_mc_device *mc_dev) 183fb1ff4c1SBharat Bhushan { 184fb1ff4c1SBharat Bhushan struct iommu_group *group; 185fb1ff4c1SBharat Bhushan struct vfio_fsl_mc_device *vdev; 186fb1ff4c1SBharat Bhushan struct device *dev = &mc_dev->dev; 187fb1ff4c1SBharat Bhushan int ret; 188fb1ff4c1SBharat Bhushan 189fb1ff4c1SBharat Bhushan group = vfio_iommu_group_get(dev); 190fb1ff4c1SBharat Bhushan if (!group) { 191fb1ff4c1SBharat Bhushan dev_err(dev, "VFIO_FSL_MC: No IOMMU group\n"); 192fb1ff4c1SBharat Bhushan return -EINVAL; 193fb1ff4c1SBharat Bhushan } 194fb1ff4c1SBharat Bhushan 195fb1ff4c1SBharat Bhushan vdev = devm_kzalloc(dev, sizeof(*vdev), GFP_KERNEL); 196fb1ff4c1SBharat Bhushan if (!vdev) { 197fb1ff4c1SBharat Bhushan ret = -ENOMEM; 198fb1ff4c1SBharat Bhushan goto out_group_put; 199fb1ff4c1SBharat Bhushan } 200fb1ff4c1SBharat Bhushan 201fb1ff4c1SBharat Bhushan vdev->mc_dev = mc_dev; 202fb1ff4c1SBharat Bhushan 203fb1ff4c1SBharat Bhushan ret = vfio_add_group_dev(dev, &vfio_fsl_mc_ops, vdev); 204fb1ff4c1SBharat Bhushan if (ret) { 205fb1ff4c1SBharat Bhushan dev_err(dev, "VFIO_FSL_MC: Failed to add to vfio group\n"); 206fb1ff4c1SBharat Bhushan goto out_group_put; 207fb1ff4c1SBharat Bhushan } 208704f5082SDiana Craciun 209704f5082SDiana Craciun ret = vfio_fsl_mc_init_device(vdev); 210704f5082SDiana Craciun if (ret) 211704f5082SDiana Craciun goto out_group_dev; 212704f5082SDiana Craciun 213fb1ff4c1SBharat Bhushan return 0; 214fb1ff4c1SBharat Bhushan 215704f5082SDiana Craciun out_group_dev: 216704f5082SDiana Craciun vfio_del_group_dev(dev); 217fb1ff4c1SBharat Bhushan out_group_put: 218fb1ff4c1SBharat Bhushan vfio_iommu_group_put(group, dev); 219fb1ff4c1SBharat Bhushan return ret; 220fb1ff4c1SBharat Bhushan } 221fb1ff4c1SBharat Bhushan 222fb1ff4c1SBharat Bhushan static int vfio_fsl_mc_remove(struct fsl_mc_device *mc_dev) 223fb1ff4c1SBharat Bhushan { 224fb1ff4c1SBharat Bhushan struct vfio_fsl_mc_device *vdev; 225fb1ff4c1SBharat Bhushan struct device *dev = &mc_dev->dev; 226fb1ff4c1SBharat Bhushan 227fb1ff4c1SBharat Bhushan vdev = vfio_del_group_dev(dev); 228fb1ff4c1SBharat Bhushan if (!vdev) 229fb1ff4c1SBharat Bhushan return -EINVAL; 230fb1ff4c1SBharat Bhushan 231704f5082SDiana Craciun if (is_fsl_mc_bus_dprc(mc_dev)) { 232704f5082SDiana Craciun dprc_remove_devices(mc_dev, NULL, 0); 233704f5082SDiana Craciun dprc_cleanup(mc_dev); 234704f5082SDiana Craciun } 235704f5082SDiana Craciun 236704f5082SDiana Craciun if (vdev->nb.notifier_call) 237704f5082SDiana Craciun bus_unregister_notifier(&fsl_mc_bus_type, &vdev->nb); 238704f5082SDiana Craciun 239fb1ff4c1SBharat Bhushan vfio_iommu_group_put(mc_dev->dev.iommu_group, dev); 240fb1ff4c1SBharat Bhushan 241fb1ff4c1SBharat Bhushan return 0; 242fb1ff4c1SBharat Bhushan } 243fb1ff4c1SBharat Bhushan 244fb1ff4c1SBharat Bhushan static struct fsl_mc_driver vfio_fsl_mc_driver = { 245fb1ff4c1SBharat Bhushan .probe = vfio_fsl_mc_probe, 246fb1ff4c1SBharat Bhushan .remove = vfio_fsl_mc_remove, 247fb1ff4c1SBharat Bhushan .driver = { 248fb1ff4c1SBharat Bhushan .name = "vfio-fsl-mc", 249fb1ff4c1SBharat Bhushan .owner = THIS_MODULE, 250fb1ff4c1SBharat Bhushan }, 251fb1ff4c1SBharat Bhushan }; 252fb1ff4c1SBharat Bhushan 253fb1ff4c1SBharat Bhushan static int __init vfio_fsl_mc_driver_init(void) 254fb1ff4c1SBharat Bhushan { 255fb1ff4c1SBharat Bhushan return fsl_mc_driver_register(&vfio_fsl_mc_driver); 256fb1ff4c1SBharat Bhushan } 257fb1ff4c1SBharat Bhushan 258fb1ff4c1SBharat Bhushan static void __exit vfio_fsl_mc_driver_exit(void) 259fb1ff4c1SBharat Bhushan { 260fb1ff4c1SBharat Bhushan fsl_mc_driver_unregister(&vfio_fsl_mc_driver); 261fb1ff4c1SBharat Bhushan } 262fb1ff4c1SBharat Bhushan 263fb1ff4c1SBharat Bhushan module_init(vfio_fsl_mc_driver_init); 264fb1ff4c1SBharat Bhushan module_exit(vfio_fsl_mc_driver_exit); 265fb1ff4c1SBharat Bhushan 266fb1ff4c1SBharat Bhushan MODULE_LICENSE("Dual BSD/GPL"); 267fb1ff4c1SBharat Bhushan MODULE_DESCRIPTION("VFIO for FSL-MC devices - User Level meta-driver"); 268