xref: /linux/drivers/s390/cio/vfio_ccw_ops.c (revision 4296151d231eeee78514bf7f495c46683785255d)
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 
18124c98674SFarhan Ali 	return ret;
18224c98674SFarhan Ali 
18324c98674SFarhan Ali out_unregister:
18424c98674SFarhan Ali 	vfio_ccw_unregister_dev_regions(private);
185d5afd5d1SCornelia Huck 	vfio_unregister_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
186d5afd5d1SCornelia Huck 				 &private->nb);
187d5afd5d1SCornelia Huck 	return ret;
18884cd8fc4SDong Jia Shi }
18984cd8fc4SDong Jia Shi 
1905bf18536SSebastian Ott static void vfio_ccw_mdev_release(struct mdev_device *mdev)
19184cd8fc4SDong Jia Shi {
19284cd8fc4SDong Jia Shi 	struct vfio_ccw_private *private =
19384cd8fc4SDong Jia Shi 		dev_get_drvdata(mdev_parent_dev(mdev));
19484cd8fc4SDong Jia Shi 
195b49bdc86SFarhan Ali 	if ((private->state != VFIO_CCW_STATE_NOT_OPER) &&
196b49bdc86SFarhan Ali 	    (private->state != VFIO_CCW_STATE_STANDBY)) {
197b49bdc86SFarhan Ali 		if (!vfio_ccw_mdev_reset(mdev))
198b49bdc86SFarhan Ali 			private->state = VFIO_CCW_STATE_STANDBY;
199b49bdc86SFarhan Ali 		/* The state will be NOT_OPER on error. */
200b49bdc86SFarhan Ali 	}
201b49bdc86SFarhan Ali 
202b49bdc86SFarhan Ali 	cp_free(&private->cp);
203600279b5SEric Farman 	vfio_ccw_unregister_dev_regions(private);
20484cd8fc4SDong Jia Shi 	vfio_unregister_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
20584cd8fc4SDong Jia Shi 				 &private->nb);
20684cd8fc4SDong Jia Shi }
20784cd8fc4SDong Jia Shi 
208db8e5d17SCornelia Huck static ssize_t vfio_ccw_mdev_read_io_region(struct vfio_ccw_private *private,
209db8e5d17SCornelia Huck 					    char __user *buf, size_t count,
210060d2b5aSDong Jia Shi 					    loff_t *ppos)
211060d2b5aSDong Jia Shi {
212db8e5d17SCornelia Huck 	loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK;
213060d2b5aSDong Jia Shi 	struct ccw_io_region *region;
2144f766173SCornelia Huck 	int ret;
215060d2b5aSDong Jia Shi 
216db8e5d17SCornelia Huck 	if (pos + count > sizeof(*region))
217060d2b5aSDong Jia Shi 		return -EINVAL;
218060d2b5aSDong Jia Shi 
2194f766173SCornelia Huck 	mutex_lock(&private->io_mutex);
220c98e16b2SEric Farman 	region = private->io_region;
221db8e5d17SCornelia Huck 	if (copy_to_user(buf, (void *)region + pos, count))
2224f766173SCornelia Huck 		ret = -EFAULT;
2234f766173SCornelia Huck 	else
2244f766173SCornelia Huck 		ret = count;
2254f766173SCornelia Huck 	mutex_unlock(&private->io_mutex);
2264f766173SCornelia Huck 	return ret;
227060d2b5aSDong Jia Shi }
228060d2b5aSDong Jia Shi 
229db8e5d17SCornelia Huck static ssize_t vfio_ccw_mdev_read(struct mdev_device *mdev,
230db8e5d17SCornelia Huck 				  char __user *buf,
231060d2b5aSDong Jia Shi 				  size_t count,
232060d2b5aSDong Jia Shi 				  loff_t *ppos)
233060d2b5aSDong Jia Shi {
234db8e5d17SCornelia Huck 	unsigned int index = VFIO_CCW_OFFSET_TO_INDEX(*ppos);
235060d2b5aSDong Jia Shi 	struct vfio_ccw_private *private;
236db8e5d17SCornelia Huck 
237db8e5d17SCornelia Huck 	private = dev_get_drvdata(mdev_parent_dev(mdev));
238db8e5d17SCornelia Huck 
239db8e5d17SCornelia Huck 	if (index >= VFIO_CCW_NUM_REGIONS + private->num_regions)
240db8e5d17SCornelia Huck 		return -EINVAL;
241db8e5d17SCornelia Huck 
242db8e5d17SCornelia Huck 	switch (index) {
243db8e5d17SCornelia Huck 	case VFIO_CCW_CONFIG_REGION_INDEX:
244db8e5d17SCornelia Huck 		return vfio_ccw_mdev_read_io_region(private, buf, count, ppos);
245db8e5d17SCornelia Huck 	default:
246db8e5d17SCornelia Huck 		index -= VFIO_CCW_NUM_REGIONS;
247db8e5d17SCornelia Huck 		return private->region[index].ops->read(private, buf, count,
248db8e5d17SCornelia Huck 							ppos);
249db8e5d17SCornelia Huck 	}
250db8e5d17SCornelia Huck 
251db8e5d17SCornelia Huck 	return -EINVAL;
252db8e5d17SCornelia Huck }
253db8e5d17SCornelia Huck 
254db8e5d17SCornelia Huck static ssize_t vfio_ccw_mdev_write_io_region(struct vfio_ccw_private *private,
255db8e5d17SCornelia Huck 					     const char __user *buf,
256db8e5d17SCornelia Huck 					     size_t count, loff_t *ppos)
257db8e5d17SCornelia Huck {
258db8e5d17SCornelia Huck 	loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK;
259060d2b5aSDong Jia Shi 	struct ccw_io_region *region;
2604f766173SCornelia Huck 	int ret;
261060d2b5aSDong Jia Shi 
262db8e5d17SCornelia Huck 	if (pos + count > sizeof(*region))
263060d2b5aSDong Jia Shi 		return -EINVAL;
264060d2b5aSDong Jia Shi 
2654f766173SCornelia Huck 	if (!mutex_trylock(&private->io_mutex))
2664f766173SCornelia Huck 		return -EAGAIN;
267060d2b5aSDong Jia Shi 
268c98e16b2SEric Farman 	region = private->io_region;
269db8e5d17SCornelia Huck 	if (copy_from_user((void *)region + pos, buf, count)) {
2704f766173SCornelia Huck 		ret = -EFAULT;
2714f766173SCornelia Huck 		goto out_unlock;
272bbe37e4cSDong Jia Shi 	}
273060d2b5aSDong Jia Shi 
2744f766173SCornelia Huck 	vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_IO_REQ);
2754f766173SCornelia Huck 	if (region->ret_code != 0)
2764f766173SCornelia Huck 		private->state = VFIO_CCW_STATE_IDLE;
2774f766173SCornelia Huck 	ret = (region->ret_code != 0) ? region->ret_code : count;
2784f766173SCornelia Huck 
2794f766173SCornelia Huck out_unlock:
2804f766173SCornelia Huck 	mutex_unlock(&private->io_mutex);
2814f766173SCornelia Huck 	return ret;
282060d2b5aSDong Jia Shi }
283060d2b5aSDong Jia Shi 
284db8e5d17SCornelia Huck static ssize_t vfio_ccw_mdev_write(struct mdev_device *mdev,
285db8e5d17SCornelia Huck 				   const char __user *buf,
286db8e5d17SCornelia Huck 				   size_t count,
287db8e5d17SCornelia Huck 				   loff_t *ppos)
288e01bcdd6SDong Jia Shi {
289db8e5d17SCornelia Huck 	unsigned int index = VFIO_CCW_OFFSET_TO_INDEX(*ppos);
290db8e5d17SCornelia Huck 	struct vfio_ccw_private *private;
291db8e5d17SCornelia Huck 
292db8e5d17SCornelia Huck 	private = dev_get_drvdata(mdev_parent_dev(mdev));
293db8e5d17SCornelia Huck 
294db8e5d17SCornelia Huck 	if (index >= VFIO_CCW_NUM_REGIONS + private->num_regions)
295db8e5d17SCornelia Huck 		return -EINVAL;
296db8e5d17SCornelia Huck 
297db8e5d17SCornelia Huck 	switch (index) {
298db8e5d17SCornelia Huck 	case VFIO_CCW_CONFIG_REGION_INDEX:
299db8e5d17SCornelia Huck 		return vfio_ccw_mdev_write_io_region(private, buf, count, ppos);
300db8e5d17SCornelia Huck 	default:
301db8e5d17SCornelia Huck 		index -= VFIO_CCW_NUM_REGIONS;
302db8e5d17SCornelia Huck 		return private->region[index].ops->write(private, buf, count,
303db8e5d17SCornelia Huck 							 ppos);
304db8e5d17SCornelia Huck 	}
305db8e5d17SCornelia Huck 
306db8e5d17SCornelia Huck 	return -EINVAL;
307db8e5d17SCornelia Huck }
308db8e5d17SCornelia Huck 
309db8e5d17SCornelia Huck static int vfio_ccw_mdev_get_device_info(struct vfio_device_info *info,
310db8e5d17SCornelia Huck 					 struct mdev_device *mdev)
311db8e5d17SCornelia Huck {
312db8e5d17SCornelia Huck 	struct vfio_ccw_private *private;
313db8e5d17SCornelia Huck 
314db8e5d17SCornelia Huck 	private = dev_get_drvdata(mdev_parent_dev(mdev));
31583d1193aSDong Jia Shi 	info->flags = VFIO_DEVICE_FLAGS_CCW | VFIO_DEVICE_FLAGS_RESET;
316db8e5d17SCornelia Huck 	info->num_regions = VFIO_CCW_NUM_REGIONS + private->num_regions;
317120e214eSDong Jia Shi 	info->num_irqs = VFIO_CCW_NUM_IRQS;
318e01bcdd6SDong Jia Shi 
319e01bcdd6SDong Jia Shi 	return 0;
320e01bcdd6SDong Jia Shi }
321e01bcdd6SDong Jia Shi 
322e01bcdd6SDong Jia Shi static int vfio_ccw_mdev_get_region_info(struct vfio_region_info *info,
323db8e5d17SCornelia Huck 					 struct mdev_device *mdev,
324db8e5d17SCornelia Huck 					 unsigned long arg)
325e01bcdd6SDong Jia Shi {
326db8e5d17SCornelia Huck 	struct vfio_ccw_private *private;
327db8e5d17SCornelia Huck 	int i;
328db8e5d17SCornelia Huck 
329db8e5d17SCornelia Huck 	private = dev_get_drvdata(mdev_parent_dev(mdev));
330e01bcdd6SDong Jia Shi 	switch (info->index) {
331e01bcdd6SDong Jia Shi 	case VFIO_CCW_CONFIG_REGION_INDEX:
332e01bcdd6SDong Jia Shi 		info->offset = 0;
333e01bcdd6SDong Jia Shi 		info->size = sizeof(struct ccw_io_region);
334e01bcdd6SDong Jia Shi 		info->flags = VFIO_REGION_INFO_FLAG_READ
335e01bcdd6SDong Jia Shi 			      | VFIO_REGION_INFO_FLAG_WRITE;
336e01bcdd6SDong Jia Shi 		return 0;
337db8e5d17SCornelia Huck 	default: /* all other regions are handled via capability chain */
338db8e5d17SCornelia Huck 	{
339db8e5d17SCornelia Huck 		struct vfio_info_cap caps = { .buf = NULL, .size = 0 };
340db8e5d17SCornelia Huck 		struct vfio_region_info_cap_type cap_type = {
341db8e5d17SCornelia Huck 			.header.id = VFIO_REGION_INFO_CAP_TYPE,
342db8e5d17SCornelia Huck 			.header.version = 1 };
343db8e5d17SCornelia Huck 		int ret;
344db8e5d17SCornelia Huck 
345db8e5d17SCornelia Huck 		if (info->index >=
346db8e5d17SCornelia Huck 		    VFIO_CCW_NUM_REGIONS + private->num_regions)
347e01bcdd6SDong Jia Shi 			return -EINVAL;
348db8e5d17SCornelia Huck 
349db8e5d17SCornelia Huck 		info->index = array_index_nospec(info->index,
350db8e5d17SCornelia Huck 						 VFIO_CCW_NUM_REGIONS +
351db8e5d17SCornelia Huck 						 private->num_regions);
352db8e5d17SCornelia Huck 
353db8e5d17SCornelia Huck 		i = info->index - VFIO_CCW_NUM_REGIONS;
354db8e5d17SCornelia Huck 
355db8e5d17SCornelia Huck 		info->offset = VFIO_CCW_INDEX_TO_OFFSET(info->index);
356db8e5d17SCornelia Huck 		info->size = private->region[i].size;
357db8e5d17SCornelia Huck 		info->flags = private->region[i].flags;
358db8e5d17SCornelia Huck 
359db8e5d17SCornelia Huck 		cap_type.type = private->region[i].type;
360db8e5d17SCornelia Huck 		cap_type.subtype = private->region[i].subtype;
361db8e5d17SCornelia Huck 
362db8e5d17SCornelia Huck 		ret = vfio_info_add_capability(&caps, &cap_type.header,
363db8e5d17SCornelia Huck 					       sizeof(cap_type));
364db8e5d17SCornelia Huck 		if (ret)
365db8e5d17SCornelia Huck 			return ret;
366db8e5d17SCornelia Huck 
367db8e5d17SCornelia Huck 		info->flags |= VFIO_REGION_INFO_FLAG_CAPS;
368db8e5d17SCornelia Huck 		if (info->argsz < sizeof(*info) + caps.size) {
369db8e5d17SCornelia Huck 			info->argsz = sizeof(*info) + caps.size;
370db8e5d17SCornelia Huck 			info->cap_offset = 0;
371db8e5d17SCornelia Huck 		} else {
372db8e5d17SCornelia Huck 			vfio_info_cap_shift(&caps, sizeof(*info));
373db8e5d17SCornelia Huck 			if (copy_to_user((void __user *)arg + sizeof(*info),
374db8e5d17SCornelia Huck 					 caps.buf, caps.size)) {
375db8e5d17SCornelia Huck 				kfree(caps.buf);
376db8e5d17SCornelia Huck 				return -EFAULT;
377e01bcdd6SDong Jia Shi 			}
378db8e5d17SCornelia Huck 			info->cap_offset = sizeof(*info);
379db8e5d17SCornelia Huck 		}
380db8e5d17SCornelia Huck 
381db8e5d17SCornelia Huck 		kfree(caps.buf);
382db8e5d17SCornelia Huck 
383db8e5d17SCornelia Huck 	}
384db8e5d17SCornelia Huck 	}
385db8e5d17SCornelia Huck 	return 0;
386e01bcdd6SDong Jia Shi }
387e01bcdd6SDong Jia Shi 
3885bf18536SSebastian Ott static int vfio_ccw_mdev_get_irq_info(struct vfio_irq_info *info)
389120e214eSDong Jia Shi {
390*4296151dSEric Farman 	switch (info->index) {
391*4296151dSEric Farman 	case VFIO_CCW_IO_IRQ_INDEX:
392120e214eSDong Jia Shi 		info->count = 1;
393120e214eSDong Jia Shi 		info->flags = VFIO_IRQ_INFO_EVENTFD;
394*4296151dSEric Farman 		break;
395*4296151dSEric Farman 	default:
396*4296151dSEric Farman 		return -EINVAL;
397*4296151dSEric Farman 	}
398120e214eSDong Jia Shi 
399120e214eSDong Jia Shi 	return 0;
400120e214eSDong Jia Shi }
401120e214eSDong Jia Shi 
402120e214eSDong Jia Shi static int vfio_ccw_mdev_set_irqs(struct mdev_device *mdev,
403120e214eSDong Jia Shi 				  uint32_t flags,
404*4296151dSEric Farman 				  uint32_t index,
405120e214eSDong Jia Shi 				  void __user *data)
406120e214eSDong Jia Shi {
407120e214eSDong Jia Shi 	struct vfio_ccw_private *private;
408120e214eSDong Jia Shi 	struct eventfd_ctx **ctx;
409120e214eSDong Jia Shi 
410120e214eSDong Jia Shi 	if (!(flags & VFIO_IRQ_SET_ACTION_TRIGGER))
411120e214eSDong Jia Shi 		return -EINVAL;
412120e214eSDong Jia Shi 
413120e214eSDong Jia Shi 	private = dev_get_drvdata(mdev_parent_dev(mdev));
414*4296151dSEric Farman 
415*4296151dSEric Farman 	switch (index) {
416*4296151dSEric Farman 	case VFIO_CCW_IO_IRQ_INDEX:
417120e214eSDong Jia Shi 		ctx = &private->io_trigger;
418*4296151dSEric Farman 		break;
419*4296151dSEric Farman 	default:
420*4296151dSEric Farman 		return -EINVAL;
421*4296151dSEric Farman 	}
422120e214eSDong Jia Shi 
423120e214eSDong Jia Shi 	switch (flags & VFIO_IRQ_SET_DATA_TYPE_MASK) {
424120e214eSDong Jia Shi 	case VFIO_IRQ_SET_DATA_NONE:
425120e214eSDong Jia Shi 	{
426120e214eSDong Jia Shi 		if (*ctx)
427120e214eSDong Jia Shi 			eventfd_signal(*ctx, 1);
428120e214eSDong Jia Shi 		return 0;
429120e214eSDong Jia Shi 	}
430120e214eSDong Jia Shi 	case VFIO_IRQ_SET_DATA_BOOL:
431120e214eSDong Jia Shi 	{
432120e214eSDong Jia Shi 		uint8_t trigger;
433120e214eSDong Jia Shi 
434120e214eSDong Jia Shi 		if (get_user(trigger, (uint8_t __user *)data))
435120e214eSDong Jia Shi 			return -EFAULT;
436120e214eSDong Jia Shi 
437120e214eSDong Jia Shi 		if (trigger && *ctx)
438120e214eSDong Jia Shi 			eventfd_signal(*ctx, 1);
439120e214eSDong Jia Shi 		return 0;
440120e214eSDong Jia Shi 	}
441120e214eSDong Jia Shi 	case VFIO_IRQ_SET_DATA_EVENTFD:
442120e214eSDong Jia Shi 	{
443120e214eSDong Jia Shi 		int32_t fd;
444120e214eSDong Jia Shi 
445120e214eSDong Jia Shi 		if (get_user(fd, (int32_t __user *)data))
446120e214eSDong Jia Shi 			return -EFAULT;
447120e214eSDong Jia Shi 
448120e214eSDong Jia Shi 		if (fd == -1) {
449120e214eSDong Jia Shi 			if (*ctx)
450120e214eSDong Jia Shi 				eventfd_ctx_put(*ctx);
451120e214eSDong Jia Shi 			*ctx = NULL;
452120e214eSDong Jia Shi 		} else if (fd >= 0) {
453120e214eSDong Jia Shi 			struct eventfd_ctx *efdctx;
454120e214eSDong Jia Shi 
455120e214eSDong Jia Shi 			efdctx = eventfd_ctx_fdget(fd);
456120e214eSDong Jia Shi 			if (IS_ERR(efdctx))
457120e214eSDong Jia Shi 				return PTR_ERR(efdctx);
458120e214eSDong Jia Shi 
459120e214eSDong Jia Shi 			if (*ctx)
460120e214eSDong Jia Shi 				eventfd_ctx_put(*ctx);
461120e214eSDong Jia Shi 
462120e214eSDong Jia Shi 			*ctx = efdctx;
463120e214eSDong Jia Shi 		} else
464120e214eSDong Jia Shi 			return -EINVAL;
465120e214eSDong Jia Shi 
466120e214eSDong Jia Shi 		return 0;
467120e214eSDong Jia Shi 	}
468120e214eSDong Jia Shi 	default:
469120e214eSDong Jia Shi 		return -EINVAL;
470120e214eSDong Jia Shi 	}
471120e214eSDong Jia Shi }
472120e214eSDong Jia Shi 
473db8e5d17SCornelia Huck int vfio_ccw_register_dev_region(struct vfio_ccw_private *private,
474db8e5d17SCornelia Huck 				 unsigned int subtype,
475db8e5d17SCornelia Huck 				 const struct vfio_ccw_regops *ops,
476db8e5d17SCornelia Huck 				 size_t size, u32 flags, void *data)
477db8e5d17SCornelia Huck {
478db8e5d17SCornelia Huck 	struct vfio_ccw_region *region;
479db8e5d17SCornelia Huck 
480db8e5d17SCornelia Huck 	region = krealloc(private->region,
481db8e5d17SCornelia Huck 			  (private->num_regions + 1) * sizeof(*region),
482db8e5d17SCornelia Huck 			  GFP_KERNEL);
483db8e5d17SCornelia Huck 	if (!region)
484db8e5d17SCornelia Huck 		return -ENOMEM;
485db8e5d17SCornelia Huck 
486db8e5d17SCornelia Huck 	private->region = region;
487db8e5d17SCornelia Huck 	private->region[private->num_regions].type = VFIO_REGION_TYPE_CCW;
488db8e5d17SCornelia Huck 	private->region[private->num_regions].subtype = subtype;
489db8e5d17SCornelia Huck 	private->region[private->num_regions].ops = ops;
490db8e5d17SCornelia Huck 	private->region[private->num_regions].size = size;
491db8e5d17SCornelia Huck 	private->region[private->num_regions].flags = flags;
492db8e5d17SCornelia Huck 	private->region[private->num_regions].data = data;
493db8e5d17SCornelia Huck 
494db8e5d17SCornelia Huck 	private->num_regions++;
495db8e5d17SCornelia Huck 
496db8e5d17SCornelia Huck 	return 0;
497db8e5d17SCornelia Huck }
498db8e5d17SCornelia Huck 
499600279b5SEric Farman void vfio_ccw_unregister_dev_regions(struct vfio_ccw_private *private)
500600279b5SEric Farman {
501600279b5SEric Farman 	int i;
502600279b5SEric Farman 
503600279b5SEric Farman 	for (i = 0; i < private->num_regions; i++)
504600279b5SEric Farman 		private->region[i].ops->release(private, &private->region[i]);
505600279b5SEric Farman 	private->num_regions = 0;
506600279b5SEric Farman 	kfree(private->region);
507600279b5SEric Farman 	private->region = NULL;
508600279b5SEric Farman }
509600279b5SEric Farman 
510e01bcdd6SDong Jia Shi static ssize_t vfio_ccw_mdev_ioctl(struct mdev_device *mdev,
511e01bcdd6SDong Jia Shi 				   unsigned int cmd,
512e01bcdd6SDong Jia Shi 				   unsigned long arg)
513e01bcdd6SDong Jia Shi {
514e01bcdd6SDong Jia Shi 	int ret = 0;
515e01bcdd6SDong Jia Shi 	unsigned long minsz;
516e01bcdd6SDong Jia Shi 
517e01bcdd6SDong Jia Shi 	switch (cmd) {
518e01bcdd6SDong Jia Shi 	case VFIO_DEVICE_GET_INFO:
519e01bcdd6SDong Jia Shi 	{
520e01bcdd6SDong Jia Shi 		struct vfio_device_info info;
521e01bcdd6SDong Jia Shi 
522e01bcdd6SDong Jia Shi 		minsz = offsetofend(struct vfio_device_info, num_irqs);
523e01bcdd6SDong Jia Shi 
524e01bcdd6SDong Jia Shi 		if (copy_from_user(&info, (void __user *)arg, minsz))
525e01bcdd6SDong Jia Shi 			return -EFAULT;
526e01bcdd6SDong Jia Shi 
527e01bcdd6SDong Jia Shi 		if (info.argsz < minsz)
528e01bcdd6SDong Jia Shi 			return -EINVAL;
529e01bcdd6SDong Jia Shi 
530db8e5d17SCornelia Huck 		ret = vfio_ccw_mdev_get_device_info(&info, mdev);
531e01bcdd6SDong Jia Shi 		if (ret)
532e01bcdd6SDong Jia Shi 			return ret;
533e01bcdd6SDong Jia Shi 
534e01bcdd6SDong Jia Shi 		return copy_to_user((void __user *)arg, &info, minsz);
535e01bcdd6SDong Jia Shi 	}
536e01bcdd6SDong Jia Shi 	case VFIO_DEVICE_GET_REGION_INFO:
537e01bcdd6SDong Jia Shi 	{
538e01bcdd6SDong Jia Shi 		struct vfio_region_info info;
539e01bcdd6SDong Jia Shi 
540e01bcdd6SDong Jia Shi 		minsz = offsetofend(struct vfio_region_info, offset);
541e01bcdd6SDong Jia Shi 
542e01bcdd6SDong Jia Shi 		if (copy_from_user(&info, (void __user *)arg, minsz))
543e01bcdd6SDong Jia Shi 			return -EFAULT;
544e01bcdd6SDong Jia Shi 
545e01bcdd6SDong Jia Shi 		if (info.argsz < minsz)
546e01bcdd6SDong Jia Shi 			return -EINVAL;
547e01bcdd6SDong Jia Shi 
548db8e5d17SCornelia Huck 		ret = vfio_ccw_mdev_get_region_info(&info, mdev, arg);
549e01bcdd6SDong Jia Shi 		if (ret)
550e01bcdd6SDong Jia Shi 			return ret;
551e01bcdd6SDong Jia Shi 
552e01bcdd6SDong Jia Shi 		return copy_to_user((void __user *)arg, &info, minsz);
553e01bcdd6SDong Jia Shi 	}
554120e214eSDong Jia Shi 	case VFIO_DEVICE_GET_IRQ_INFO:
555120e214eSDong Jia Shi 	{
556120e214eSDong Jia Shi 		struct vfio_irq_info info;
557120e214eSDong Jia Shi 
558120e214eSDong Jia Shi 		minsz = offsetofend(struct vfio_irq_info, count);
559120e214eSDong Jia Shi 
560120e214eSDong Jia Shi 		if (copy_from_user(&info, (void __user *)arg, minsz))
561120e214eSDong Jia Shi 			return -EFAULT;
562120e214eSDong Jia Shi 
563120e214eSDong Jia Shi 		if (info.argsz < minsz || info.index >= VFIO_CCW_NUM_IRQS)
564120e214eSDong Jia Shi 			return -EINVAL;
565120e214eSDong Jia Shi 
566120e214eSDong Jia Shi 		ret = vfio_ccw_mdev_get_irq_info(&info);
567120e214eSDong Jia Shi 		if (ret)
568120e214eSDong Jia Shi 			return ret;
569120e214eSDong Jia Shi 
570120e214eSDong Jia Shi 		if (info.count == -1)
571120e214eSDong Jia Shi 			return -EINVAL;
572120e214eSDong Jia Shi 
573120e214eSDong Jia Shi 		return copy_to_user((void __user *)arg, &info, minsz);
574120e214eSDong Jia Shi 	}
575120e214eSDong Jia Shi 	case VFIO_DEVICE_SET_IRQS:
576120e214eSDong Jia Shi 	{
577120e214eSDong Jia Shi 		struct vfio_irq_set hdr;
578120e214eSDong Jia Shi 		size_t data_size;
579120e214eSDong Jia Shi 		void __user *data;
580120e214eSDong Jia Shi 
581120e214eSDong Jia Shi 		minsz = offsetofend(struct vfio_irq_set, count);
582120e214eSDong Jia Shi 
583120e214eSDong Jia Shi 		if (copy_from_user(&hdr, (void __user *)arg, minsz))
584120e214eSDong Jia Shi 			return -EFAULT;
585120e214eSDong Jia Shi 
586120e214eSDong Jia Shi 		ret = vfio_set_irqs_validate_and_prepare(&hdr, 1,
587120e214eSDong Jia Shi 							 VFIO_CCW_NUM_IRQS,
588120e214eSDong Jia Shi 							 &data_size);
589120e214eSDong Jia Shi 		if (ret)
590120e214eSDong Jia Shi 			return ret;
591120e214eSDong Jia Shi 
592120e214eSDong Jia Shi 		data = (void __user *)(arg + minsz);
593*4296151dSEric Farman 		return vfio_ccw_mdev_set_irqs(mdev, hdr.flags, hdr.index, data);
594120e214eSDong Jia Shi 	}
59583d1193aSDong Jia Shi 	case VFIO_DEVICE_RESET:
59683d1193aSDong Jia Shi 		return vfio_ccw_mdev_reset(mdev);
597e01bcdd6SDong Jia Shi 	default:
598e01bcdd6SDong Jia Shi 		return -ENOTTY;
599e01bcdd6SDong Jia Shi 	}
600e01bcdd6SDong Jia Shi }
601e01bcdd6SDong Jia Shi 
60284cd8fc4SDong Jia Shi static const struct mdev_parent_ops vfio_ccw_mdev_ops = {
60384cd8fc4SDong Jia Shi 	.owner			= THIS_MODULE,
60484cd8fc4SDong Jia Shi 	.supported_type_groups  = mdev_type_groups,
60584cd8fc4SDong Jia Shi 	.create			= vfio_ccw_mdev_create,
60684cd8fc4SDong Jia Shi 	.remove			= vfio_ccw_mdev_remove,
60784cd8fc4SDong Jia Shi 	.open			= vfio_ccw_mdev_open,
60884cd8fc4SDong Jia Shi 	.release		= vfio_ccw_mdev_release,
609060d2b5aSDong Jia Shi 	.read			= vfio_ccw_mdev_read,
610060d2b5aSDong Jia Shi 	.write			= vfio_ccw_mdev_write,
611e01bcdd6SDong Jia Shi 	.ioctl			= vfio_ccw_mdev_ioctl,
61284cd8fc4SDong Jia Shi };
61384cd8fc4SDong Jia Shi 
61484cd8fc4SDong Jia Shi int vfio_ccw_mdev_reg(struct subchannel *sch)
61584cd8fc4SDong Jia Shi {
61684cd8fc4SDong Jia Shi 	return mdev_register_device(&sch->dev, &vfio_ccw_mdev_ops);
61784cd8fc4SDong Jia Shi }
61884cd8fc4SDong Jia Shi 
61984cd8fc4SDong Jia Shi void vfio_ccw_mdev_unreg(struct subchannel *sch)
62084cd8fc4SDong Jia Shi {
62184cd8fc4SDong Jia Shi 	mdev_unregister_device(&sch->dev);
62284cd8fc4SDong Jia Shi }
623