xref: /openbmc/linux/drivers/vfio/fsl-mc/vfio_fsl_mc.c (revision f97f4c04e5d6ca8b90b5b412bd7e72de79426263)
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