xref: /linux/drivers/s390/cio/vfio_ccw_ops.c (revision ead5d1f4d877e92c051e1a1ade623d0d30e71619)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
284cd8fc4SDong Jia Shi /*
384cd8fc4SDong Jia Shi  * Physical device callbacks for vfio_ccw
484cd8fc4SDong Jia Shi  *
584cd8fc4SDong Jia Shi  * Copyright IBM Corp. 2017
6db8e5d17SCornelia Huck  * Copyright Red Hat, Inc. 2019
784cd8fc4SDong Jia Shi  *
884cd8fc4SDong Jia Shi  * Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
984cd8fc4SDong Jia Shi  *            Xiao Feng Ren <renxiaof@linux.vnet.ibm.com>
10db8e5d17SCornelia Huck  *            Cornelia Huck <cohuck@redhat.com>
1184cd8fc4SDong Jia Shi  */
1284cd8fc4SDong Jia Shi 
1384cd8fc4SDong Jia Shi #include <linux/vfio.h>
1484cd8fc4SDong Jia Shi #include <linux/mdev.h>
15db8e5d17SCornelia Huck #include <linux/nospec.h>
16db8e5d17SCornelia Huck #include <linux/slab.h>
1784cd8fc4SDong Jia Shi 
1884cd8fc4SDong Jia Shi #include "vfio_ccw_private.h"
1984cd8fc4SDong Jia Shi 
2083d1193aSDong Jia Shi static int vfio_ccw_mdev_reset(struct mdev_device *mdev)
2183d1193aSDong Jia Shi {
2283d1193aSDong Jia Shi 	struct vfio_ccw_private *private;
2383d1193aSDong Jia Shi 	struct subchannel *sch;
2483d1193aSDong Jia Shi 	int ret;
2583d1193aSDong Jia Shi 
2683d1193aSDong Jia Shi 	private = dev_get_drvdata(mdev_parent_dev(mdev));
2783d1193aSDong Jia Shi 	sch = private->sch;
2883d1193aSDong Jia Shi 	/*
2983d1193aSDong Jia Shi 	 * TODO:
3083d1193aSDong Jia Shi 	 * In the cureent stage, some things like "no I/O running" and "no
3183d1193aSDong Jia Shi 	 * interrupt pending" are clear, but we are not sure what other state
3283d1193aSDong Jia Shi 	 * we need to care about.
3383d1193aSDong Jia Shi 	 * There are still a lot more instructions need to be handled. We
3483d1193aSDong Jia Shi 	 * should come back here later.
3583d1193aSDong Jia Shi 	 */
3683d1193aSDong Jia Shi 	ret = vfio_ccw_sch_quiesce(sch);
3783d1193aSDong Jia Shi 	if (ret)
3883d1193aSDong Jia Shi 		return ret;
3983d1193aSDong Jia Shi 
40bbe37e4cSDong Jia Shi 	ret = cio_enable_subchannel(sch, (u32)(unsigned long)sch);
41bbe37e4cSDong Jia Shi 	if (!ret)
42bbe37e4cSDong Jia Shi 		private->state = VFIO_CCW_STATE_IDLE;
43bbe37e4cSDong Jia Shi 
44bbe37e4cSDong Jia Shi 	return ret;
4583d1193aSDong Jia Shi }
4683d1193aSDong Jia Shi 
4784cd8fc4SDong Jia Shi static int vfio_ccw_mdev_notifier(struct notifier_block *nb,
4884cd8fc4SDong Jia Shi 				  unsigned long action,
4984cd8fc4SDong Jia Shi 				  void *data)
5084cd8fc4SDong Jia Shi {
5184cd8fc4SDong Jia Shi 	struct vfio_ccw_private *private =
5284cd8fc4SDong Jia Shi 		container_of(nb, struct vfio_ccw_private, nb);
5384cd8fc4SDong Jia Shi 
5484cd8fc4SDong Jia Shi 	/*
5584cd8fc4SDong Jia Shi 	 * Vendor drivers MUST unpin pages in response to an
5684cd8fc4SDong Jia Shi 	 * invalidation.
5784cd8fc4SDong Jia Shi 	 */
584e149e43SDong Jia Shi 	if (action == VFIO_IOMMU_NOTIFY_DMA_UNMAP) {
594e149e43SDong Jia Shi 		struct vfio_iommu_type1_dma_unmap *unmap = data;
604e149e43SDong Jia Shi 
614e149e43SDong Jia Shi 		if (!cp_iova_pinned(&private->cp, unmap->iova))
624e149e43SDong Jia Shi 			return NOTIFY_OK;
634e149e43SDong Jia Shi 
6483d1193aSDong Jia Shi 		if (vfio_ccw_mdev_reset(private->mdev))
654e149e43SDong Jia Shi 			return NOTIFY_BAD;
664e149e43SDong Jia Shi 
674e149e43SDong Jia Shi 		cp_free(&private->cp);
684e149e43SDong Jia Shi 		return NOTIFY_OK;
694e149e43SDong Jia Shi 	}
704e149e43SDong Jia Shi 
7184cd8fc4SDong Jia Shi 	return NOTIFY_DONE;
7284cd8fc4SDong Jia Shi }
7384cd8fc4SDong Jia Shi 
7484cd8fc4SDong Jia Shi static ssize_t name_show(struct kobject *kobj, struct device *dev, char *buf)
7584cd8fc4SDong Jia Shi {
7684cd8fc4SDong Jia Shi 	return sprintf(buf, "I/O subchannel (Non-QDIO)\n");
7784cd8fc4SDong Jia Shi }
785bf18536SSebastian Ott static MDEV_TYPE_ATTR_RO(name);
7984cd8fc4SDong Jia Shi 
8084cd8fc4SDong Jia Shi static ssize_t device_api_show(struct kobject *kobj, struct device *dev,
8184cd8fc4SDong Jia Shi 			       char *buf)
8284cd8fc4SDong Jia Shi {
8384cd8fc4SDong Jia Shi 	return sprintf(buf, "%s\n", VFIO_DEVICE_API_CCW_STRING);
8484cd8fc4SDong Jia Shi }
855bf18536SSebastian Ott static MDEV_TYPE_ATTR_RO(device_api);
8684cd8fc4SDong Jia Shi 
8784cd8fc4SDong Jia Shi static ssize_t available_instances_show(struct kobject *kobj,
8884cd8fc4SDong Jia Shi 					struct device *dev, char *buf)
8984cd8fc4SDong Jia Shi {
9084cd8fc4SDong Jia Shi 	struct vfio_ccw_private *private = dev_get_drvdata(dev);
9184cd8fc4SDong Jia Shi 
9284cd8fc4SDong Jia Shi 	return sprintf(buf, "%d\n", atomic_read(&private->avail));
9384cd8fc4SDong Jia Shi }
945bf18536SSebastian Ott static MDEV_TYPE_ATTR_RO(available_instances);
9584cd8fc4SDong Jia Shi 
9684cd8fc4SDong Jia Shi static struct attribute *mdev_types_attrs[] = {
9784cd8fc4SDong Jia Shi 	&mdev_type_attr_name.attr,
9884cd8fc4SDong Jia Shi 	&mdev_type_attr_device_api.attr,
9984cd8fc4SDong Jia Shi 	&mdev_type_attr_available_instances.attr,
10084cd8fc4SDong Jia Shi 	NULL,
10184cd8fc4SDong Jia Shi };
10284cd8fc4SDong Jia Shi 
10384cd8fc4SDong Jia Shi static struct attribute_group mdev_type_group = {
10484cd8fc4SDong Jia Shi 	.name  = "io",
10584cd8fc4SDong Jia Shi 	.attrs = mdev_types_attrs,
10684cd8fc4SDong Jia Shi };
10784cd8fc4SDong Jia Shi 
1085bf18536SSebastian Ott static struct attribute_group *mdev_type_groups[] = {
10984cd8fc4SDong Jia Shi 	&mdev_type_group,
11084cd8fc4SDong Jia Shi 	NULL,
11184cd8fc4SDong Jia Shi };
11284cd8fc4SDong Jia Shi 
11384cd8fc4SDong Jia Shi static int vfio_ccw_mdev_create(struct kobject *kobj, struct mdev_device *mdev)
11484cd8fc4SDong Jia Shi {
11584cd8fc4SDong Jia Shi 	struct vfio_ccw_private *private =
11684cd8fc4SDong Jia Shi 		dev_get_drvdata(mdev_parent_dev(mdev));
11784cd8fc4SDong Jia Shi 
118bbe37e4cSDong Jia Shi 	if (private->state == VFIO_CCW_STATE_NOT_OPER)
119bbe37e4cSDong Jia Shi 		return -ENODEV;
120bbe37e4cSDong Jia Shi 
12184cd8fc4SDong Jia Shi 	if (atomic_dec_if_positive(&private->avail) < 0)
12284cd8fc4SDong Jia Shi 		return -EPERM;
12384cd8fc4SDong Jia Shi 
12484cd8fc4SDong Jia Shi 	private->mdev = mdev;
125bbe37e4cSDong Jia Shi 	private->state = VFIO_CCW_STATE_IDLE;
12684cd8fc4SDong Jia Shi 
12760e05d1cSCornelia Huck 	VFIO_CCW_MSG_EVENT(2, "mdev %pUl, sch %x.%x.%04x: create\n",
12860e05d1cSCornelia Huck 			   mdev_uuid(mdev), private->sch->schid.cssid,
12960e05d1cSCornelia Huck 			   private->sch->schid.ssid,
13060e05d1cSCornelia Huck 			   private->sch->schid.sch_no);
13160e05d1cSCornelia Huck 
13284cd8fc4SDong Jia Shi 	return 0;
13384cd8fc4SDong Jia Shi }
13484cd8fc4SDong Jia Shi 
13584cd8fc4SDong Jia Shi static int vfio_ccw_mdev_remove(struct mdev_device *mdev)
13684cd8fc4SDong Jia Shi {
13783d1193aSDong Jia Shi 	struct vfio_ccw_private *private =
13883d1193aSDong Jia Shi 		dev_get_drvdata(mdev_parent_dev(mdev));
13984cd8fc4SDong Jia Shi 
14060e05d1cSCornelia Huck 	VFIO_CCW_MSG_EVENT(2, "mdev %pUl, sch %x.%x.%04x: remove\n",
14160e05d1cSCornelia Huck 			   mdev_uuid(mdev), private->sch->schid.cssid,
14260e05d1cSCornelia Huck 			   private->sch->schid.ssid,
14360e05d1cSCornelia Huck 			   private->sch->schid.sch_no);
14460e05d1cSCornelia Huck 
145129cc19aSDong Jia Shi 	if ((private->state != VFIO_CCW_STATE_NOT_OPER) &&
146129cc19aSDong Jia Shi 	    (private->state != VFIO_CCW_STATE_STANDBY)) {
147b49bdc86SFarhan Ali 		if (!vfio_ccw_sch_quiesce(private->sch))
148bbe37e4cSDong Jia Shi 			private->state = VFIO_CCW_STATE_STANDBY;
149129cc19aSDong Jia Shi 		/* The state will be NOT_OPER on error. */
150129cc19aSDong Jia Shi 	}
151bbe37e4cSDong Jia Shi 
152b49bdc86SFarhan Ali 	cp_free(&private->cp);
15384cd8fc4SDong Jia Shi 	private->mdev = NULL;
15484cd8fc4SDong Jia Shi 	atomic_inc(&private->avail);
15584cd8fc4SDong Jia Shi 
15684cd8fc4SDong Jia Shi 	return 0;
15784cd8fc4SDong Jia Shi }
15884cd8fc4SDong Jia Shi 
15984cd8fc4SDong Jia Shi static int vfio_ccw_mdev_open(struct mdev_device *mdev)
16084cd8fc4SDong Jia Shi {
16184cd8fc4SDong Jia Shi 	struct vfio_ccw_private *private =
16284cd8fc4SDong Jia Shi 		dev_get_drvdata(mdev_parent_dev(mdev));
16384cd8fc4SDong Jia Shi 	unsigned long events = VFIO_IOMMU_NOTIFY_DMA_UNMAP;
164d5afd5d1SCornelia Huck 	int ret;
16584cd8fc4SDong Jia Shi 
16684cd8fc4SDong Jia Shi 	private->nb.notifier_call = vfio_ccw_mdev_notifier;
16784cd8fc4SDong Jia Shi 
168d5afd5d1SCornelia Huck 	ret = vfio_register_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
16984cd8fc4SDong Jia Shi 				     &events, &private->nb);
170d5afd5d1SCornelia Huck 	if (ret)
171d5afd5d1SCornelia Huck 		return ret;
172d5afd5d1SCornelia Huck 
173d5afd5d1SCornelia Huck 	ret = vfio_ccw_register_async_dev_regions(private);
174d5afd5d1SCornelia Huck 	if (ret)
17524c98674SFarhan Ali 		goto out_unregister;
17624c98674SFarhan Ali 
17724c98674SFarhan Ali 	ret = vfio_ccw_register_schib_dev_regions(private);
17824c98674SFarhan Ali 	if (ret)
17924c98674SFarhan Ali 		goto out_unregister;
18024c98674SFarhan Ali 
181*d8cac29bSFarhan Ali 	ret = vfio_ccw_register_crw_dev_regions(private);
182*d8cac29bSFarhan Ali 	if (ret)
183*d8cac29bSFarhan Ali 		goto out_unregister;
184*d8cac29bSFarhan Ali 
18524c98674SFarhan Ali 	return ret;
18624c98674SFarhan Ali 
18724c98674SFarhan Ali out_unregister:
18824c98674SFarhan Ali 	vfio_ccw_unregister_dev_regions(private);
189d5afd5d1SCornelia Huck 	vfio_unregister_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
190d5afd5d1SCornelia Huck 				 &private->nb);
191d5afd5d1SCornelia Huck 	return ret;
19284cd8fc4SDong Jia Shi }
19384cd8fc4SDong Jia Shi 
1945bf18536SSebastian Ott static void vfio_ccw_mdev_release(struct mdev_device *mdev)
19584cd8fc4SDong Jia Shi {
19684cd8fc4SDong Jia Shi 	struct vfio_ccw_private *private =
19784cd8fc4SDong Jia Shi 		dev_get_drvdata(mdev_parent_dev(mdev));
19884cd8fc4SDong Jia Shi 
199b49bdc86SFarhan Ali 	if ((private->state != VFIO_CCW_STATE_NOT_OPER) &&
200b49bdc86SFarhan Ali 	    (private->state != VFIO_CCW_STATE_STANDBY)) {
201b49bdc86SFarhan Ali 		if (!vfio_ccw_mdev_reset(mdev))
202b49bdc86SFarhan Ali 			private->state = VFIO_CCW_STATE_STANDBY;
203b49bdc86SFarhan Ali 		/* The state will be NOT_OPER on error. */
204b49bdc86SFarhan Ali 	}
205b49bdc86SFarhan Ali 
206b49bdc86SFarhan Ali 	cp_free(&private->cp);
207600279b5SEric Farman 	vfio_ccw_unregister_dev_regions(private);
20884cd8fc4SDong Jia Shi 	vfio_unregister_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
20984cd8fc4SDong Jia Shi 				 &private->nb);
21084cd8fc4SDong Jia Shi }
21184cd8fc4SDong Jia Shi 
212db8e5d17SCornelia Huck static ssize_t vfio_ccw_mdev_read_io_region(struct vfio_ccw_private *private,
213db8e5d17SCornelia Huck 					    char __user *buf, size_t count,
214060d2b5aSDong Jia Shi 					    loff_t *ppos)
215060d2b5aSDong Jia Shi {
216db8e5d17SCornelia Huck 	loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK;
217060d2b5aSDong Jia Shi 	struct ccw_io_region *region;
2184f766173SCornelia Huck 	int ret;
219060d2b5aSDong Jia Shi 
220db8e5d17SCornelia Huck 	if (pos + count > sizeof(*region))
221060d2b5aSDong Jia Shi 		return -EINVAL;
222060d2b5aSDong Jia Shi 
2234f766173SCornelia Huck 	mutex_lock(&private->io_mutex);
224c98e16b2SEric Farman 	region = private->io_region;
225db8e5d17SCornelia Huck 	if (copy_to_user(buf, (void *)region + pos, count))
2264f766173SCornelia Huck 		ret = -EFAULT;
2274f766173SCornelia Huck 	else
2284f766173SCornelia Huck 		ret = count;
2294f766173SCornelia Huck 	mutex_unlock(&private->io_mutex);
2304f766173SCornelia Huck 	return ret;
231060d2b5aSDong Jia Shi }
232060d2b5aSDong Jia Shi 
233db8e5d17SCornelia Huck static ssize_t vfio_ccw_mdev_read(struct mdev_device *mdev,
234db8e5d17SCornelia Huck 				  char __user *buf,
235060d2b5aSDong Jia Shi 				  size_t count,
236060d2b5aSDong Jia Shi 				  loff_t *ppos)
237060d2b5aSDong Jia Shi {
238db8e5d17SCornelia Huck 	unsigned int index = VFIO_CCW_OFFSET_TO_INDEX(*ppos);
239060d2b5aSDong Jia Shi 	struct vfio_ccw_private *private;
240db8e5d17SCornelia Huck 
241db8e5d17SCornelia Huck 	private = dev_get_drvdata(mdev_parent_dev(mdev));
242db8e5d17SCornelia Huck 
243db8e5d17SCornelia Huck 	if (index >= VFIO_CCW_NUM_REGIONS + private->num_regions)
244db8e5d17SCornelia Huck 		return -EINVAL;
245db8e5d17SCornelia Huck 
246db8e5d17SCornelia Huck 	switch (index) {
247db8e5d17SCornelia Huck 	case VFIO_CCW_CONFIG_REGION_INDEX:
248db8e5d17SCornelia Huck 		return vfio_ccw_mdev_read_io_region(private, buf, count, ppos);
249db8e5d17SCornelia Huck 	default:
250db8e5d17SCornelia Huck 		index -= VFIO_CCW_NUM_REGIONS;
251db8e5d17SCornelia Huck 		return private->region[index].ops->read(private, buf, count,
252db8e5d17SCornelia Huck 							ppos);
253db8e5d17SCornelia Huck 	}
254db8e5d17SCornelia Huck 
255db8e5d17SCornelia Huck 	return -EINVAL;
256db8e5d17SCornelia Huck }
257db8e5d17SCornelia Huck 
258db8e5d17SCornelia Huck static ssize_t vfio_ccw_mdev_write_io_region(struct vfio_ccw_private *private,
259db8e5d17SCornelia Huck 					     const char __user *buf,
260db8e5d17SCornelia Huck 					     size_t count, loff_t *ppos)
261db8e5d17SCornelia Huck {
262db8e5d17SCornelia Huck 	loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK;
263060d2b5aSDong Jia Shi 	struct ccw_io_region *region;
2644f766173SCornelia Huck 	int ret;
265060d2b5aSDong Jia Shi 
266db8e5d17SCornelia Huck 	if (pos + count > sizeof(*region))
267060d2b5aSDong Jia Shi 		return -EINVAL;
268060d2b5aSDong Jia Shi 
2694f766173SCornelia Huck 	if (!mutex_trylock(&private->io_mutex))
2704f766173SCornelia Huck 		return -EAGAIN;
271060d2b5aSDong Jia Shi 
272c98e16b2SEric Farman 	region = private->io_region;
273db8e5d17SCornelia Huck 	if (copy_from_user((void *)region + pos, buf, count)) {
2744f766173SCornelia Huck 		ret = -EFAULT;
2754f766173SCornelia Huck 		goto out_unlock;
276bbe37e4cSDong Jia Shi 	}
277060d2b5aSDong Jia Shi 
2784f766173SCornelia Huck 	vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_IO_REQ);
2794f766173SCornelia Huck 	if (region->ret_code != 0)
2804f766173SCornelia Huck 		private->state = VFIO_CCW_STATE_IDLE;
2814f766173SCornelia Huck 	ret = (region->ret_code != 0) ? region->ret_code : count;
2824f766173SCornelia Huck 
2834f766173SCornelia Huck out_unlock:
2844f766173SCornelia Huck 	mutex_unlock(&private->io_mutex);
2854f766173SCornelia Huck 	return ret;
286060d2b5aSDong Jia Shi }
287060d2b5aSDong Jia Shi 
288db8e5d17SCornelia Huck static ssize_t vfio_ccw_mdev_write(struct mdev_device *mdev,
289db8e5d17SCornelia Huck 				   const char __user *buf,
290db8e5d17SCornelia Huck 				   size_t count,
291db8e5d17SCornelia Huck 				   loff_t *ppos)
292e01bcdd6SDong Jia Shi {
293db8e5d17SCornelia Huck 	unsigned int index = VFIO_CCW_OFFSET_TO_INDEX(*ppos);
294db8e5d17SCornelia Huck 	struct vfio_ccw_private *private;
295db8e5d17SCornelia Huck 
296db8e5d17SCornelia Huck 	private = dev_get_drvdata(mdev_parent_dev(mdev));
297db8e5d17SCornelia Huck 
298db8e5d17SCornelia Huck 	if (index >= VFIO_CCW_NUM_REGIONS + private->num_regions)
299db8e5d17SCornelia Huck 		return -EINVAL;
300db8e5d17SCornelia Huck 
301db8e5d17SCornelia Huck 	switch (index) {
302db8e5d17SCornelia Huck 	case VFIO_CCW_CONFIG_REGION_INDEX:
303db8e5d17SCornelia Huck 		return vfio_ccw_mdev_write_io_region(private, buf, count, ppos);
304db8e5d17SCornelia Huck 	default:
305db8e5d17SCornelia Huck 		index -= VFIO_CCW_NUM_REGIONS;
306db8e5d17SCornelia Huck 		return private->region[index].ops->write(private, buf, count,
307db8e5d17SCornelia Huck 							 ppos);
308db8e5d17SCornelia Huck 	}
309db8e5d17SCornelia Huck 
310db8e5d17SCornelia Huck 	return -EINVAL;
311db8e5d17SCornelia Huck }
312db8e5d17SCornelia Huck 
313db8e5d17SCornelia Huck static int vfio_ccw_mdev_get_device_info(struct vfio_device_info *info,
314db8e5d17SCornelia Huck 					 struct mdev_device *mdev)
315db8e5d17SCornelia Huck {
316db8e5d17SCornelia Huck 	struct vfio_ccw_private *private;
317db8e5d17SCornelia Huck 
318db8e5d17SCornelia Huck 	private = dev_get_drvdata(mdev_parent_dev(mdev));
31983d1193aSDong Jia Shi 	info->flags = VFIO_DEVICE_FLAGS_CCW | VFIO_DEVICE_FLAGS_RESET;
320db8e5d17SCornelia Huck 	info->num_regions = VFIO_CCW_NUM_REGIONS + private->num_regions;
321120e214eSDong Jia Shi 	info->num_irqs = VFIO_CCW_NUM_IRQS;
322e01bcdd6SDong Jia Shi 
323e01bcdd6SDong Jia Shi 	return 0;
324e01bcdd6SDong Jia Shi }
325e01bcdd6SDong Jia Shi 
326e01bcdd6SDong Jia Shi static int vfio_ccw_mdev_get_region_info(struct vfio_region_info *info,
327db8e5d17SCornelia Huck 					 struct mdev_device *mdev,
328db8e5d17SCornelia Huck 					 unsigned long arg)
329e01bcdd6SDong Jia Shi {
330db8e5d17SCornelia Huck 	struct vfio_ccw_private *private;
331db8e5d17SCornelia Huck 	int i;
332db8e5d17SCornelia Huck 
333db8e5d17SCornelia Huck 	private = dev_get_drvdata(mdev_parent_dev(mdev));
334e01bcdd6SDong Jia Shi 	switch (info->index) {
335e01bcdd6SDong Jia Shi 	case VFIO_CCW_CONFIG_REGION_INDEX:
336e01bcdd6SDong Jia Shi 		info->offset = 0;
337e01bcdd6SDong Jia Shi 		info->size = sizeof(struct ccw_io_region);
338e01bcdd6SDong Jia Shi 		info->flags = VFIO_REGION_INFO_FLAG_READ
339e01bcdd6SDong Jia Shi 			      | VFIO_REGION_INFO_FLAG_WRITE;
340e01bcdd6SDong Jia Shi 		return 0;
341db8e5d17SCornelia Huck 	default: /* all other regions are handled via capability chain */
342db8e5d17SCornelia Huck 	{
343db8e5d17SCornelia Huck 		struct vfio_info_cap caps = { .buf = NULL, .size = 0 };
344db8e5d17SCornelia Huck 		struct vfio_region_info_cap_type cap_type = {
345db8e5d17SCornelia Huck 			.header.id = VFIO_REGION_INFO_CAP_TYPE,
346db8e5d17SCornelia Huck 			.header.version = 1 };
347db8e5d17SCornelia Huck 		int ret;
348db8e5d17SCornelia Huck 
349db8e5d17SCornelia Huck 		if (info->index >=
350db8e5d17SCornelia Huck 		    VFIO_CCW_NUM_REGIONS + private->num_regions)
351e01bcdd6SDong Jia Shi 			return -EINVAL;
352db8e5d17SCornelia Huck 
353db8e5d17SCornelia Huck 		info->index = array_index_nospec(info->index,
354db8e5d17SCornelia Huck 						 VFIO_CCW_NUM_REGIONS +
355db8e5d17SCornelia Huck 						 private->num_regions);
356db8e5d17SCornelia Huck 
357db8e5d17SCornelia Huck 		i = info->index - VFIO_CCW_NUM_REGIONS;
358db8e5d17SCornelia Huck 
359db8e5d17SCornelia Huck 		info->offset = VFIO_CCW_INDEX_TO_OFFSET(info->index);
360db8e5d17SCornelia Huck 		info->size = private->region[i].size;
361db8e5d17SCornelia Huck 		info->flags = private->region[i].flags;
362db8e5d17SCornelia Huck 
363db8e5d17SCornelia Huck 		cap_type.type = private->region[i].type;
364db8e5d17SCornelia Huck 		cap_type.subtype = private->region[i].subtype;
365db8e5d17SCornelia Huck 
366db8e5d17SCornelia Huck 		ret = vfio_info_add_capability(&caps, &cap_type.header,
367db8e5d17SCornelia Huck 					       sizeof(cap_type));
368db8e5d17SCornelia Huck 		if (ret)
369db8e5d17SCornelia Huck 			return ret;
370db8e5d17SCornelia Huck 
371db8e5d17SCornelia Huck 		info->flags |= VFIO_REGION_INFO_FLAG_CAPS;
372db8e5d17SCornelia Huck 		if (info->argsz < sizeof(*info) + caps.size) {
373db8e5d17SCornelia Huck 			info->argsz = sizeof(*info) + caps.size;
374db8e5d17SCornelia Huck 			info->cap_offset = 0;
375db8e5d17SCornelia Huck 		} else {
376db8e5d17SCornelia Huck 			vfio_info_cap_shift(&caps, sizeof(*info));
377db8e5d17SCornelia Huck 			if (copy_to_user((void __user *)arg + sizeof(*info),
378db8e5d17SCornelia Huck 					 caps.buf, caps.size)) {
379db8e5d17SCornelia Huck 				kfree(caps.buf);
380db8e5d17SCornelia Huck 				return -EFAULT;
381e01bcdd6SDong Jia Shi 			}
382db8e5d17SCornelia Huck 			info->cap_offset = sizeof(*info);
383db8e5d17SCornelia Huck 		}
384db8e5d17SCornelia Huck 
385db8e5d17SCornelia Huck 		kfree(caps.buf);
386db8e5d17SCornelia Huck 
387db8e5d17SCornelia Huck 	}
388db8e5d17SCornelia Huck 	}
389db8e5d17SCornelia Huck 	return 0;
390e01bcdd6SDong Jia Shi }
391e01bcdd6SDong Jia Shi 
3925bf18536SSebastian Ott static int vfio_ccw_mdev_get_irq_info(struct vfio_irq_info *info)
393120e214eSDong Jia Shi {
3944296151dSEric Farman 	switch (info->index) {
3954296151dSEric Farman 	case VFIO_CCW_IO_IRQ_INDEX:
396*d8cac29bSFarhan Ali 	case VFIO_CCW_CRW_IRQ_INDEX:
397120e214eSDong Jia Shi 		info->count = 1;
398120e214eSDong Jia Shi 		info->flags = VFIO_IRQ_INFO_EVENTFD;
3994296151dSEric Farman 		break;
4004296151dSEric Farman 	default:
4014296151dSEric Farman 		return -EINVAL;
4024296151dSEric Farman 	}
403120e214eSDong Jia Shi 
404120e214eSDong Jia Shi 	return 0;
405120e214eSDong Jia Shi }
406120e214eSDong Jia Shi 
407120e214eSDong Jia Shi static int vfio_ccw_mdev_set_irqs(struct mdev_device *mdev,
408120e214eSDong Jia Shi 				  uint32_t flags,
4094296151dSEric Farman 				  uint32_t index,
410120e214eSDong Jia Shi 				  void __user *data)
411120e214eSDong Jia Shi {
412120e214eSDong Jia Shi 	struct vfio_ccw_private *private;
413120e214eSDong Jia Shi 	struct eventfd_ctx **ctx;
414120e214eSDong Jia Shi 
415120e214eSDong Jia Shi 	if (!(flags & VFIO_IRQ_SET_ACTION_TRIGGER))
416120e214eSDong Jia Shi 		return -EINVAL;
417120e214eSDong Jia Shi 
418120e214eSDong Jia Shi 	private = dev_get_drvdata(mdev_parent_dev(mdev));
4194296151dSEric Farman 
4204296151dSEric Farman 	switch (index) {
4214296151dSEric Farman 	case VFIO_CCW_IO_IRQ_INDEX:
422120e214eSDong Jia Shi 		ctx = &private->io_trigger;
4234296151dSEric Farman 		break;
424*d8cac29bSFarhan Ali 	case VFIO_CCW_CRW_IRQ_INDEX:
425*d8cac29bSFarhan Ali 		ctx = &private->crw_trigger;
426*d8cac29bSFarhan Ali 		break;
4274296151dSEric Farman 	default:
4284296151dSEric Farman 		return -EINVAL;
4294296151dSEric Farman 	}
430120e214eSDong Jia Shi 
431120e214eSDong Jia Shi 	switch (flags & VFIO_IRQ_SET_DATA_TYPE_MASK) {
432120e214eSDong Jia Shi 	case VFIO_IRQ_SET_DATA_NONE:
433120e214eSDong Jia Shi 	{
434120e214eSDong Jia Shi 		if (*ctx)
435120e214eSDong Jia Shi 			eventfd_signal(*ctx, 1);
436120e214eSDong Jia Shi 		return 0;
437120e214eSDong Jia Shi 	}
438120e214eSDong Jia Shi 	case VFIO_IRQ_SET_DATA_BOOL:
439120e214eSDong Jia Shi 	{
440120e214eSDong Jia Shi 		uint8_t trigger;
441120e214eSDong Jia Shi 
442120e214eSDong Jia Shi 		if (get_user(trigger, (uint8_t __user *)data))
443120e214eSDong Jia Shi 			return -EFAULT;
444120e214eSDong Jia Shi 
445120e214eSDong Jia Shi 		if (trigger && *ctx)
446120e214eSDong Jia Shi 			eventfd_signal(*ctx, 1);
447120e214eSDong Jia Shi 		return 0;
448120e214eSDong Jia Shi 	}
449120e214eSDong Jia Shi 	case VFIO_IRQ_SET_DATA_EVENTFD:
450120e214eSDong Jia Shi 	{
451120e214eSDong Jia Shi 		int32_t fd;
452120e214eSDong Jia Shi 
453120e214eSDong Jia Shi 		if (get_user(fd, (int32_t __user *)data))
454120e214eSDong Jia Shi 			return -EFAULT;
455120e214eSDong Jia Shi 
456120e214eSDong Jia Shi 		if (fd == -1) {
457120e214eSDong Jia Shi 			if (*ctx)
458120e214eSDong Jia Shi 				eventfd_ctx_put(*ctx);
459120e214eSDong Jia Shi 			*ctx = NULL;
460120e214eSDong Jia Shi 		} else if (fd >= 0) {
461120e214eSDong Jia Shi 			struct eventfd_ctx *efdctx;
462120e214eSDong Jia Shi 
463120e214eSDong Jia Shi 			efdctx = eventfd_ctx_fdget(fd);
464120e214eSDong Jia Shi 			if (IS_ERR(efdctx))
465120e214eSDong Jia Shi 				return PTR_ERR(efdctx);
466120e214eSDong Jia Shi 
467120e214eSDong Jia Shi 			if (*ctx)
468120e214eSDong Jia Shi 				eventfd_ctx_put(*ctx);
469120e214eSDong Jia Shi 
470120e214eSDong Jia Shi 			*ctx = efdctx;
471120e214eSDong Jia Shi 		} else
472120e214eSDong Jia Shi 			return -EINVAL;
473120e214eSDong Jia Shi 
474120e214eSDong Jia Shi 		return 0;
475120e214eSDong Jia Shi 	}
476120e214eSDong Jia Shi 	default:
477120e214eSDong Jia Shi 		return -EINVAL;
478120e214eSDong Jia Shi 	}
479120e214eSDong Jia Shi }
480120e214eSDong Jia Shi 
481db8e5d17SCornelia Huck int vfio_ccw_register_dev_region(struct vfio_ccw_private *private,
482db8e5d17SCornelia Huck 				 unsigned int subtype,
483db8e5d17SCornelia Huck 				 const struct vfio_ccw_regops *ops,
484db8e5d17SCornelia Huck 				 size_t size, u32 flags, void *data)
485db8e5d17SCornelia Huck {
486db8e5d17SCornelia Huck 	struct vfio_ccw_region *region;
487db8e5d17SCornelia Huck 
488db8e5d17SCornelia Huck 	region = krealloc(private->region,
489db8e5d17SCornelia Huck 			  (private->num_regions + 1) * sizeof(*region),
490db8e5d17SCornelia Huck 			  GFP_KERNEL);
491db8e5d17SCornelia Huck 	if (!region)
492db8e5d17SCornelia Huck 		return -ENOMEM;
493db8e5d17SCornelia Huck 
494db8e5d17SCornelia Huck 	private->region = region;
495db8e5d17SCornelia Huck 	private->region[private->num_regions].type = VFIO_REGION_TYPE_CCW;
496db8e5d17SCornelia Huck 	private->region[private->num_regions].subtype = subtype;
497db8e5d17SCornelia Huck 	private->region[private->num_regions].ops = ops;
498db8e5d17SCornelia Huck 	private->region[private->num_regions].size = size;
499db8e5d17SCornelia Huck 	private->region[private->num_regions].flags = flags;
500db8e5d17SCornelia Huck 	private->region[private->num_regions].data = data;
501db8e5d17SCornelia Huck 
502db8e5d17SCornelia Huck 	private->num_regions++;
503db8e5d17SCornelia Huck 
504db8e5d17SCornelia Huck 	return 0;
505db8e5d17SCornelia Huck }
506db8e5d17SCornelia Huck 
507600279b5SEric Farman void vfio_ccw_unregister_dev_regions(struct vfio_ccw_private *private)
508600279b5SEric Farman {
509600279b5SEric Farman 	int i;
510600279b5SEric Farman 
511600279b5SEric Farman 	for (i = 0; i < private->num_regions; i++)
512600279b5SEric Farman 		private->region[i].ops->release(private, &private->region[i]);
513600279b5SEric Farman 	private->num_regions = 0;
514600279b5SEric Farman 	kfree(private->region);
515600279b5SEric Farman 	private->region = NULL;
516600279b5SEric Farman }
517600279b5SEric Farman 
518e01bcdd6SDong Jia Shi static ssize_t vfio_ccw_mdev_ioctl(struct mdev_device *mdev,
519e01bcdd6SDong Jia Shi 				   unsigned int cmd,
520e01bcdd6SDong Jia Shi 				   unsigned long arg)
521e01bcdd6SDong Jia Shi {
522e01bcdd6SDong Jia Shi 	int ret = 0;
523e01bcdd6SDong Jia Shi 	unsigned long minsz;
524e01bcdd6SDong Jia Shi 
525e01bcdd6SDong Jia Shi 	switch (cmd) {
526e01bcdd6SDong Jia Shi 	case VFIO_DEVICE_GET_INFO:
527e01bcdd6SDong Jia Shi 	{
528e01bcdd6SDong Jia Shi 		struct vfio_device_info info;
529e01bcdd6SDong Jia Shi 
530e01bcdd6SDong Jia Shi 		minsz = offsetofend(struct vfio_device_info, num_irqs);
531e01bcdd6SDong Jia Shi 
532e01bcdd6SDong Jia Shi 		if (copy_from_user(&info, (void __user *)arg, minsz))
533e01bcdd6SDong Jia Shi 			return -EFAULT;
534e01bcdd6SDong Jia Shi 
535e01bcdd6SDong Jia Shi 		if (info.argsz < minsz)
536e01bcdd6SDong Jia Shi 			return -EINVAL;
537e01bcdd6SDong Jia Shi 
538db8e5d17SCornelia Huck 		ret = vfio_ccw_mdev_get_device_info(&info, mdev);
539e01bcdd6SDong Jia Shi 		if (ret)
540e01bcdd6SDong Jia Shi 			return ret;
541e01bcdd6SDong Jia Shi 
542e01bcdd6SDong Jia Shi 		return copy_to_user((void __user *)arg, &info, minsz);
543e01bcdd6SDong Jia Shi 	}
544e01bcdd6SDong Jia Shi 	case VFIO_DEVICE_GET_REGION_INFO:
545e01bcdd6SDong Jia Shi 	{
546e01bcdd6SDong Jia Shi 		struct vfio_region_info info;
547e01bcdd6SDong Jia Shi 
548e01bcdd6SDong Jia Shi 		minsz = offsetofend(struct vfio_region_info, offset);
549e01bcdd6SDong Jia Shi 
550e01bcdd6SDong Jia Shi 		if (copy_from_user(&info, (void __user *)arg, minsz))
551e01bcdd6SDong Jia Shi 			return -EFAULT;
552e01bcdd6SDong Jia Shi 
553e01bcdd6SDong Jia Shi 		if (info.argsz < minsz)
554e01bcdd6SDong Jia Shi 			return -EINVAL;
555e01bcdd6SDong Jia Shi 
556db8e5d17SCornelia Huck 		ret = vfio_ccw_mdev_get_region_info(&info, mdev, arg);
557e01bcdd6SDong Jia Shi 		if (ret)
558e01bcdd6SDong Jia Shi 			return ret;
559e01bcdd6SDong Jia Shi 
560e01bcdd6SDong Jia Shi 		return copy_to_user((void __user *)arg, &info, minsz);
561e01bcdd6SDong Jia Shi 	}
562120e214eSDong Jia Shi 	case VFIO_DEVICE_GET_IRQ_INFO:
563120e214eSDong Jia Shi 	{
564120e214eSDong Jia Shi 		struct vfio_irq_info info;
565120e214eSDong Jia Shi 
566120e214eSDong Jia Shi 		minsz = offsetofend(struct vfio_irq_info, count);
567120e214eSDong Jia Shi 
568120e214eSDong Jia Shi 		if (copy_from_user(&info, (void __user *)arg, minsz))
569120e214eSDong Jia Shi 			return -EFAULT;
570120e214eSDong Jia Shi 
571120e214eSDong Jia Shi 		if (info.argsz < minsz || info.index >= VFIO_CCW_NUM_IRQS)
572120e214eSDong Jia Shi 			return -EINVAL;
573120e214eSDong Jia Shi 
574120e214eSDong Jia Shi 		ret = vfio_ccw_mdev_get_irq_info(&info);
575120e214eSDong Jia Shi 		if (ret)
576120e214eSDong Jia Shi 			return ret;
577120e214eSDong Jia Shi 
578120e214eSDong Jia Shi 		if (info.count == -1)
579120e214eSDong Jia Shi 			return -EINVAL;
580120e214eSDong Jia Shi 
581120e214eSDong Jia Shi 		return copy_to_user((void __user *)arg, &info, minsz);
582120e214eSDong Jia Shi 	}
583120e214eSDong Jia Shi 	case VFIO_DEVICE_SET_IRQS:
584120e214eSDong Jia Shi 	{
585120e214eSDong Jia Shi 		struct vfio_irq_set hdr;
586120e214eSDong Jia Shi 		size_t data_size;
587120e214eSDong Jia Shi 		void __user *data;
588120e214eSDong Jia Shi 
589120e214eSDong Jia Shi 		minsz = offsetofend(struct vfio_irq_set, count);
590120e214eSDong Jia Shi 
591120e214eSDong Jia Shi 		if (copy_from_user(&hdr, (void __user *)arg, minsz))
592120e214eSDong Jia Shi 			return -EFAULT;
593120e214eSDong Jia Shi 
594120e214eSDong Jia Shi 		ret = vfio_set_irqs_validate_and_prepare(&hdr, 1,
595120e214eSDong Jia Shi 							 VFIO_CCW_NUM_IRQS,
596120e214eSDong Jia Shi 							 &data_size);
597120e214eSDong Jia Shi 		if (ret)
598120e214eSDong Jia Shi 			return ret;
599120e214eSDong Jia Shi 
600120e214eSDong Jia Shi 		data = (void __user *)(arg + minsz);
6014296151dSEric Farman 		return vfio_ccw_mdev_set_irqs(mdev, hdr.flags, hdr.index, data);
602120e214eSDong Jia Shi 	}
60383d1193aSDong Jia Shi 	case VFIO_DEVICE_RESET:
60483d1193aSDong Jia Shi 		return vfio_ccw_mdev_reset(mdev);
605e01bcdd6SDong Jia Shi 	default:
606e01bcdd6SDong Jia Shi 		return -ENOTTY;
607e01bcdd6SDong Jia Shi 	}
608e01bcdd6SDong Jia Shi }
609e01bcdd6SDong Jia Shi 
61084cd8fc4SDong Jia Shi static const struct mdev_parent_ops vfio_ccw_mdev_ops = {
61184cd8fc4SDong Jia Shi 	.owner			= THIS_MODULE,
61284cd8fc4SDong Jia Shi 	.supported_type_groups  = mdev_type_groups,
61384cd8fc4SDong Jia Shi 	.create			= vfio_ccw_mdev_create,
61484cd8fc4SDong Jia Shi 	.remove			= vfio_ccw_mdev_remove,
61584cd8fc4SDong Jia Shi 	.open			= vfio_ccw_mdev_open,
61684cd8fc4SDong Jia Shi 	.release		= vfio_ccw_mdev_release,
617060d2b5aSDong Jia Shi 	.read			= vfio_ccw_mdev_read,
618060d2b5aSDong Jia Shi 	.write			= vfio_ccw_mdev_write,
619e01bcdd6SDong Jia Shi 	.ioctl			= vfio_ccw_mdev_ioctl,
62084cd8fc4SDong Jia Shi };
62184cd8fc4SDong Jia Shi 
62284cd8fc4SDong Jia Shi int vfio_ccw_mdev_reg(struct subchannel *sch)
62384cd8fc4SDong Jia Shi {
62484cd8fc4SDong Jia Shi 	return mdev_register_device(&sch->dev, &vfio_ccw_mdev_ops);
62584cd8fc4SDong Jia Shi }
62684cd8fc4SDong Jia Shi 
62784cd8fc4SDong Jia Shi void vfio_ccw_mdev_unreg(struct subchannel *sch)
62884cd8fc4SDong Jia Shi {
62984cd8fc4SDong Jia Shi 	mdev_unregister_device(&sch->dev);
63084cd8fc4SDong Jia Shi }
631