xref: /openbmc/linux/drivers/s390/cio/vfio_ccw_ops.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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>
14db8e5d17SCornelia Huck #include <linux/nospec.h>
15db8e5d17SCornelia Huck #include <linux/slab.h>
1684cd8fc4SDong Jia Shi 
1784cd8fc4SDong Jia Shi #include "vfio_ccw_private.h"
1884cd8fc4SDong Jia Shi 
193bf1311fSJason Gunthorpe static const struct vfio_device_ops vfio_ccw_dev_ops;
203bf1311fSJason Gunthorpe 
vfio_ccw_mdev_reset(struct vfio_ccw_private * private)2139b6ee01SJason Gunthorpe static int vfio_ccw_mdev_reset(struct vfio_ccw_private *private)
2283d1193aSDong Jia Shi {
2383d1193aSDong Jia Shi 	/*
24bfec266cSEric Farman 	 * If the FSM state is seen as Not Operational after closing
25bfec266cSEric Farman 	 * and re-opening the mdev, return an error.
2683d1193aSDong Jia Shi 	 */
27f4b4ed44SEric Farman 	vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_CLOSE);
28bfec266cSEric Farman 	vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_OPEN);
29bfec266cSEric Farman 	if (private->state == VFIO_CCW_STATE_NOT_OPER)
30bfec266cSEric Farman 		return -EINVAL;
3183d1193aSDong Jia Shi 
32bfec266cSEric Farman 	return 0;
3383d1193aSDong Jia Shi }
3483d1193aSDong Jia Shi 
vfio_ccw_dma_unmap(struct vfio_device * vdev,u64 iova,u64 length)35ce4b4657SJason Gunthorpe static void vfio_ccw_dma_unmap(struct vfio_device *vdev, u64 iova, u64 length)
3684cd8fc4SDong Jia Shi {
3784cd8fc4SDong Jia Shi 	struct vfio_ccw_private *private =
38ce4b4657SJason Gunthorpe 		container_of(vdev, struct vfio_ccw_private, vdev);
3984cd8fc4SDong Jia Shi 
40ce4b4657SJason Gunthorpe 	/* Drivers MUST unpin pages in response to an invalidation. */
415a4fe7c4SEric Farman 	if (!cp_iova_pinned(&private->cp, iova, length))
42ce4b4657SJason Gunthorpe 		return;
434e149e43SDong Jia Shi 
44ce4b4657SJason Gunthorpe 	vfio_ccw_mdev_reset(private);
4584cd8fc4SDong Jia Shi }
4684cd8fc4SDong Jia Shi 
vfio_ccw_mdev_init_dev(struct vfio_device * vdev)47ebb72b76SKevin Tian static int vfio_ccw_mdev_init_dev(struct vfio_device *vdev)
48ebb72b76SKevin Tian {
49ebb72b76SKevin Tian 	struct vfio_ccw_private *private =
50ebb72b76SKevin Tian 		container_of(vdev, struct vfio_ccw_private, vdev);
51ebb72b76SKevin Tian 
5206caaa27SEric Farman 	mutex_init(&private->io_mutex);
5306caaa27SEric Farman 	private->state = VFIO_CCW_STATE_STANDBY;
5406caaa27SEric Farman 	INIT_LIST_HEAD(&private->crw);
5506caaa27SEric Farman 	INIT_WORK(&private->io_work, vfio_ccw_sch_io_todo);
5606caaa27SEric Farman 	INIT_WORK(&private->crw_work, vfio_ccw_crw_todo);
5706caaa27SEric Farman 
5806caaa27SEric Farman 	private->cp.guest_cp = kcalloc(CCWCHAIN_LEN_MAX, sizeof(struct ccw1),
5906caaa27SEric Farman 				       GFP_KERNEL);
6006caaa27SEric Farman 	if (!private->cp.guest_cp)
6106caaa27SEric Farman 		goto out_free_private;
6206caaa27SEric Farman 
6306caaa27SEric Farman 	private->io_region = kmem_cache_zalloc(vfio_ccw_io_region,
6406caaa27SEric Farman 					       GFP_KERNEL | GFP_DMA);
6506caaa27SEric Farman 	if (!private->io_region)
6606caaa27SEric Farman 		goto out_free_cp;
6706caaa27SEric Farman 
6806caaa27SEric Farman 	private->cmd_region = kmem_cache_zalloc(vfio_ccw_cmd_region,
6906caaa27SEric Farman 						GFP_KERNEL | GFP_DMA);
7006caaa27SEric Farman 	if (!private->cmd_region)
7106caaa27SEric Farman 		goto out_free_io;
7206caaa27SEric Farman 
7306caaa27SEric Farman 	private->schib_region = kmem_cache_zalloc(vfio_ccw_schib_region,
7406caaa27SEric Farman 						  GFP_KERNEL | GFP_DMA);
7506caaa27SEric Farman 	if (!private->schib_region)
7606caaa27SEric Farman 		goto out_free_cmd;
7706caaa27SEric Farman 
7806caaa27SEric Farman 	private->crw_region = kmem_cache_zalloc(vfio_ccw_crw_region,
7906caaa27SEric Farman 						GFP_KERNEL | GFP_DMA);
8006caaa27SEric Farman 	if (!private->crw_region)
8106caaa27SEric Farman 		goto out_free_schib;
8206caaa27SEric Farman 
83ebb72b76SKevin Tian 	return 0;
8406caaa27SEric Farman 
8506caaa27SEric Farman out_free_schib:
8606caaa27SEric Farman 	kmem_cache_free(vfio_ccw_schib_region, private->schib_region);
8706caaa27SEric Farman out_free_cmd:
8806caaa27SEric Farman 	kmem_cache_free(vfio_ccw_cmd_region, private->cmd_region);
8906caaa27SEric Farman out_free_io:
9006caaa27SEric Farman 	kmem_cache_free(vfio_ccw_io_region, private->io_region);
9106caaa27SEric Farman out_free_cp:
9206caaa27SEric Farman 	kfree(private->cp.guest_cp);
9306caaa27SEric Farman out_free_private:
9406caaa27SEric Farman 	mutex_destroy(&private->io_mutex);
9506caaa27SEric Farman 	return -ENOMEM;
96ebb72b76SKevin Tian }
97ebb72b76SKevin Tian 
vfio_ccw_mdev_probe(struct mdev_device * mdev)983bf1311fSJason Gunthorpe static int vfio_ccw_mdev_probe(struct mdev_device *mdev)
9984cd8fc4SDong Jia Shi {
1009e6f07cdSEric Farman 	struct subchannel *sch = to_subchannel(mdev->dev.parent);
1019e6f07cdSEric Farman 	struct vfio_ccw_parent *parent = dev_get_drvdata(&sch->dev);
1023d62fe18SEric Farman 	struct vfio_ccw_private *private;
1033bf1311fSJason Gunthorpe 	int ret;
10484cd8fc4SDong Jia Shi 
105d1104f93SEric Farman 	private = vfio_alloc_device(vfio_ccw_private, vdev, &mdev->dev,
106d1104f93SEric Farman 				    &vfio_ccw_dev_ops);
107d1104f93SEric Farman 	if (IS_ERR(private))
108d1104f93SEric Farman 		return PTR_ERR(private);
109bbe37e4cSDong Jia Shi 
1103d62fe18SEric Farman 	dev_set_drvdata(&parent->dev, private);
1113bf1311fSJason Gunthorpe 
1123566ee1dSMichael Kawano 	VFIO_CCW_MSG_EVENT(2, "sch %x.%x.%04x: create\n",
113008a011dSEric Farman 			   sch->schid.cssid,
114008a011dSEric Farman 			   sch->schid.ssid,
115008a011dSEric Farman 			   sch->schid.sch_no);
11660e05d1cSCornelia Huck 
1173bf1311fSJason Gunthorpe 	ret = vfio_register_emulated_iommu_dev(&private->vdev);
1183bf1311fSJason Gunthorpe 	if (ret)
119ebb72b76SKevin Tian 		goto err_put_vdev;
1203bf1311fSJason Gunthorpe 	dev_set_drvdata(&mdev->dev, private);
12184cd8fc4SDong Jia Shi 	return 0;
1223bf1311fSJason Gunthorpe 
123ebb72b76SKevin Tian err_put_vdev:
1243d62fe18SEric Farman 	dev_set_drvdata(&parent->dev, NULL);
125ebb72b76SKevin Tian 	vfio_put_device(&private->vdev);
1263bf1311fSJason Gunthorpe 	return ret;
12784cd8fc4SDong Jia Shi }
12884cd8fc4SDong Jia Shi 
vfio_ccw_mdev_release_dev(struct vfio_device * vdev)129ebb72b76SKevin Tian static void vfio_ccw_mdev_release_dev(struct vfio_device *vdev)
130ebb72b76SKevin Tian {
131ebb72b76SKevin Tian 	struct vfio_ccw_private *private =
132ebb72b76SKevin Tian 		container_of(vdev, struct vfio_ccw_private, vdev);
133d1104f93SEric Farman 	struct vfio_ccw_crw *crw, *temp;
134ebb72b76SKevin Tian 
135d1104f93SEric Farman 	list_for_each_entry_safe(crw, temp, &private->crw, next) {
136d1104f93SEric Farman 		list_del(&crw->next);
137d1104f93SEric Farman 		kfree(crw);
138d1104f93SEric Farman 	}
139d1104f93SEric Farman 
140d1104f93SEric Farman 	kmem_cache_free(vfio_ccw_crw_region, private->crw_region);
141d1104f93SEric Farman 	kmem_cache_free(vfio_ccw_schib_region, private->schib_region);
142d1104f93SEric Farman 	kmem_cache_free(vfio_ccw_cmd_region, private->cmd_region);
143d1104f93SEric Farman 	kmem_cache_free(vfio_ccw_io_region, private->io_region);
144d1104f93SEric Farman 	kfree(private->cp.guest_cp);
145d1104f93SEric Farman 	mutex_destroy(&private->io_mutex);
146ebb72b76SKevin Tian }
147ebb72b76SKevin Tian 
vfio_ccw_mdev_remove(struct mdev_device * mdev)1483bf1311fSJason Gunthorpe static void vfio_ccw_mdev_remove(struct mdev_device *mdev)
14984cd8fc4SDong Jia Shi {
1509e6f07cdSEric Farman 	struct subchannel *sch = to_subchannel(mdev->dev.parent);
1519e6f07cdSEric Farman 	struct vfio_ccw_parent *parent = dev_get_drvdata(&sch->dev);
1529e6f07cdSEric Farman 	struct vfio_ccw_private *private = dev_get_drvdata(&parent->dev);
15384cd8fc4SDong Jia Shi 
1543566ee1dSMichael Kawano 	VFIO_CCW_MSG_EVENT(2, "sch %x.%x.%04x: remove\n",
155008a011dSEric Farman 			   sch->schid.cssid,
156008a011dSEric Farman 			   sch->schid.ssid,
157008a011dSEric Farman 			   sch->schid.sch_no);
15860e05d1cSCornelia Huck 
1593bf1311fSJason Gunthorpe 	vfio_unregister_group_dev(&private->vdev);
1603bf1311fSJason Gunthorpe 
1613d62fe18SEric Farman 	dev_set_drvdata(&parent->dev, NULL);
162ebb72b76SKevin Tian 	vfio_put_device(&private->vdev);
16384cd8fc4SDong Jia Shi }
16484cd8fc4SDong Jia Shi 
vfio_ccw_mdev_open_device(struct vfio_device * vdev)1653bf1311fSJason Gunthorpe static int vfio_ccw_mdev_open_device(struct vfio_device *vdev)
16684cd8fc4SDong Jia Shi {
16784cd8fc4SDong Jia Shi 	struct vfio_ccw_private *private =
1683bf1311fSJason Gunthorpe 		container_of(vdev, struct vfio_ccw_private, vdev);
169d5afd5d1SCornelia Huck 	int ret;
17084cd8fc4SDong Jia Shi 
171204b394aSEric Farman 	/* Device cannot simply be opened again from this state */
172204b394aSEric Farman 	if (private->state == VFIO_CCW_STATE_NOT_OPER)
173204b394aSEric Farman 		return -EINVAL;
174204b394aSEric Farman 
175d5afd5d1SCornelia Huck 	ret = vfio_ccw_register_async_dev_regions(private);
176d5afd5d1SCornelia Huck 	if (ret)
177ce4b4657SJason Gunthorpe 		return ret;
17824c98674SFarhan Ali 
17924c98674SFarhan Ali 	ret = vfio_ccw_register_schib_dev_regions(private);
18024c98674SFarhan Ali 	if (ret)
18124c98674SFarhan Ali 		goto out_unregister;
18224c98674SFarhan Ali 
183d8cac29bSFarhan Ali 	ret = vfio_ccw_register_crw_dev_regions(private);
184d8cac29bSFarhan Ali 	if (ret)
185d8cac29bSFarhan Ali 		goto out_unregister;
186d8cac29bSFarhan Ali 
187204b394aSEric Farman 	vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_OPEN);
188204b394aSEric Farman 	if (private->state == VFIO_CCW_STATE_NOT_OPER) {
189204b394aSEric Farman 		ret = -EINVAL;
190204b394aSEric Farman 		goto out_unregister;
191204b394aSEric Farman 	}
192204b394aSEric Farman 
19324c98674SFarhan Ali 	return ret;
19424c98674SFarhan Ali 
19524c98674SFarhan Ali out_unregister:
19624c98674SFarhan Ali 	vfio_ccw_unregister_dev_regions(private);
197d5afd5d1SCornelia Huck 	return ret;
19884cd8fc4SDong Jia Shi }
19984cd8fc4SDong Jia Shi 
vfio_ccw_mdev_close_device(struct vfio_device * vdev)2003bf1311fSJason Gunthorpe static void vfio_ccw_mdev_close_device(struct vfio_device *vdev)
20184cd8fc4SDong Jia Shi {
20284cd8fc4SDong Jia Shi 	struct vfio_ccw_private *private =
2033bf1311fSJason Gunthorpe 		container_of(vdev, struct vfio_ccw_private, vdev);
20484cd8fc4SDong Jia Shi 
205204b394aSEric Farman 	vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_CLOSE);
206600279b5SEric Farman 	vfio_ccw_unregister_dev_regions(private);
20784cd8fc4SDong Jia Shi }
20884cd8fc4SDong Jia Shi 
vfio_ccw_mdev_read_io_region(struct vfio_ccw_private * private,char __user * buf,size_t count,loff_t * ppos)209db8e5d17SCornelia Huck static ssize_t vfio_ccw_mdev_read_io_region(struct vfio_ccw_private *private,
210db8e5d17SCornelia Huck 					    char __user *buf, size_t count,
211060d2b5aSDong Jia Shi 					    loff_t *ppos)
212060d2b5aSDong Jia Shi {
213db8e5d17SCornelia Huck 	loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK;
214060d2b5aSDong Jia Shi 	struct ccw_io_region *region;
2154f766173SCornelia Huck 	int ret;
216060d2b5aSDong Jia Shi 
217db8e5d17SCornelia Huck 	if (pos + count > sizeof(*region))
218060d2b5aSDong Jia Shi 		return -EINVAL;
219060d2b5aSDong Jia Shi 
2204f766173SCornelia Huck 	mutex_lock(&private->io_mutex);
221c98e16b2SEric Farman 	region = private->io_region;
222db8e5d17SCornelia Huck 	if (copy_to_user(buf, (void *)region + pos, count))
2234f766173SCornelia Huck 		ret = -EFAULT;
2244f766173SCornelia Huck 	else
2254f766173SCornelia Huck 		ret = count;
2264f766173SCornelia Huck 	mutex_unlock(&private->io_mutex);
2274f766173SCornelia Huck 	return ret;
228060d2b5aSDong Jia Shi }
229060d2b5aSDong Jia Shi 
vfio_ccw_mdev_read(struct vfio_device * vdev,char __user * buf,size_t count,loff_t * ppos)2303bf1311fSJason Gunthorpe static ssize_t vfio_ccw_mdev_read(struct vfio_device *vdev,
231db8e5d17SCornelia Huck 				  char __user *buf,
232060d2b5aSDong Jia Shi 				  size_t count,
233060d2b5aSDong Jia Shi 				  loff_t *ppos)
234060d2b5aSDong Jia Shi {
2353bf1311fSJason Gunthorpe 	struct vfio_ccw_private *private =
2363bf1311fSJason Gunthorpe 		container_of(vdev, struct vfio_ccw_private, vdev);
237db8e5d17SCornelia Huck 	unsigned int index = VFIO_CCW_OFFSET_TO_INDEX(*ppos);
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 
vfio_ccw_mdev_write_io_region(struct vfio_ccw_private * private,const char __user * buf,size_t count,loff_t * ppos)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 	ret = (region->ret_code != 0) ? region->ret_code : count;
2764f766173SCornelia Huck 
2774f766173SCornelia Huck out_unlock:
2784f766173SCornelia Huck 	mutex_unlock(&private->io_mutex);
2794f766173SCornelia Huck 	return ret;
280060d2b5aSDong Jia Shi }
281060d2b5aSDong Jia Shi 
vfio_ccw_mdev_write(struct vfio_device * vdev,const char __user * buf,size_t count,loff_t * ppos)2823bf1311fSJason Gunthorpe static ssize_t vfio_ccw_mdev_write(struct vfio_device *vdev,
283db8e5d17SCornelia Huck 				   const char __user *buf,
284db8e5d17SCornelia Huck 				   size_t count,
285db8e5d17SCornelia Huck 				   loff_t *ppos)
286e01bcdd6SDong Jia Shi {
2873bf1311fSJason Gunthorpe 	struct vfio_ccw_private *private =
2883bf1311fSJason Gunthorpe 		container_of(vdev, struct vfio_ccw_private, vdev);
289db8e5d17SCornelia Huck 	unsigned int index = VFIO_CCW_OFFSET_TO_INDEX(*ppos);
290db8e5d17SCornelia Huck 
291db8e5d17SCornelia Huck 	if (index >= VFIO_CCW_NUM_REGIONS + private->num_regions)
292db8e5d17SCornelia Huck 		return -EINVAL;
293db8e5d17SCornelia Huck 
294db8e5d17SCornelia Huck 	switch (index) {
295db8e5d17SCornelia Huck 	case VFIO_CCW_CONFIG_REGION_INDEX:
296db8e5d17SCornelia Huck 		return vfio_ccw_mdev_write_io_region(private, buf, count, ppos);
297db8e5d17SCornelia Huck 	default:
298db8e5d17SCornelia Huck 		index -= VFIO_CCW_NUM_REGIONS;
299db8e5d17SCornelia Huck 		return private->region[index].ops->write(private, buf, count,
300db8e5d17SCornelia Huck 							 ppos);
301db8e5d17SCornelia Huck 	}
302db8e5d17SCornelia Huck 
303db8e5d17SCornelia Huck 	return -EINVAL;
304db8e5d17SCornelia Huck }
305db8e5d17SCornelia Huck 
vfio_ccw_mdev_get_device_info(struct vfio_ccw_private * private,struct vfio_device_info * info)30639b6ee01SJason Gunthorpe static int vfio_ccw_mdev_get_device_info(struct vfio_ccw_private *private,
30739b6ee01SJason Gunthorpe 					 struct vfio_device_info *info)
308db8e5d17SCornelia Huck {
30983d1193aSDong Jia Shi 	info->flags = VFIO_DEVICE_FLAGS_CCW | VFIO_DEVICE_FLAGS_RESET;
310db8e5d17SCornelia Huck 	info->num_regions = VFIO_CCW_NUM_REGIONS + private->num_regions;
311120e214eSDong Jia Shi 	info->num_irqs = VFIO_CCW_NUM_IRQS;
312e01bcdd6SDong Jia Shi 
313e01bcdd6SDong Jia Shi 	return 0;
314e01bcdd6SDong Jia Shi }
315e01bcdd6SDong Jia Shi 
vfio_ccw_mdev_get_region_info(struct vfio_ccw_private * private,struct vfio_region_info * info,unsigned long arg)31639b6ee01SJason Gunthorpe static int vfio_ccw_mdev_get_region_info(struct vfio_ccw_private *private,
31739b6ee01SJason Gunthorpe 					 struct vfio_region_info *info,
318db8e5d17SCornelia Huck 					 unsigned long arg)
319e01bcdd6SDong Jia Shi {
320db8e5d17SCornelia Huck 	int i;
321db8e5d17SCornelia Huck 
322e01bcdd6SDong Jia Shi 	switch (info->index) {
323e01bcdd6SDong Jia Shi 	case VFIO_CCW_CONFIG_REGION_INDEX:
324e01bcdd6SDong Jia Shi 		info->offset = 0;
325e01bcdd6SDong Jia Shi 		info->size = sizeof(struct ccw_io_region);
326e01bcdd6SDong Jia Shi 		info->flags = VFIO_REGION_INFO_FLAG_READ
327e01bcdd6SDong Jia Shi 			      | VFIO_REGION_INFO_FLAG_WRITE;
328e01bcdd6SDong Jia Shi 		return 0;
329db8e5d17SCornelia Huck 	default: /* all other regions are handled via capability chain */
330db8e5d17SCornelia Huck 	{
331db8e5d17SCornelia Huck 		struct vfio_info_cap caps = { .buf = NULL, .size = 0 };
332db8e5d17SCornelia Huck 		struct vfio_region_info_cap_type cap_type = {
333db8e5d17SCornelia Huck 			.header.id = VFIO_REGION_INFO_CAP_TYPE,
334db8e5d17SCornelia Huck 			.header.version = 1 };
335db8e5d17SCornelia Huck 		int ret;
336db8e5d17SCornelia Huck 
337db8e5d17SCornelia Huck 		if (info->index >=
338db8e5d17SCornelia Huck 		    VFIO_CCW_NUM_REGIONS + private->num_regions)
339e01bcdd6SDong Jia Shi 			return -EINVAL;
340db8e5d17SCornelia Huck 
341db8e5d17SCornelia Huck 		info->index = array_index_nospec(info->index,
342db8e5d17SCornelia Huck 						 VFIO_CCW_NUM_REGIONS +
343db8e5d17SCornelia Huck 						 private->num_regions);
344db8e5d17SCornelia Huck 
345db8e5d17SCornelia Huck 		i = info->index - VFIO_CCW_NUM_REGIONS;
346db8e5d17SCornelia Huck 
347db8e5d17SCornelia Huck 		info->offset = VFIO_CCW_INDEX_TO_OFFSET(info->index);
348db8e5d17SCornelia Huck 		info->size = private->region[i].size;
349db8e5d17SCornelia Huck 		info->flags = private->region[i].flags;
350db8e5d17SCornelia Huck 
351db8e5d17SCornelia Huck 		cap_type.type = private->region[i].type;
352db8e5d17SCornelia Huck 		cap_type.subtype = private->region[i].subtype;
353db8e5d17SCornelia Huck 
354db8e5d17SCornelia Huck 		ret = vfio_info_add_capability(&caps, &cap_type.header,
355db8e5d17SCornelia Huck 					       sizeof(cap_type));
356db8e5d17SCornelia Huck 		if (ret)
357db8e5d17SCornelia Huck 			return ret;
358db8e5d17SCornelia Huck 
359db8e5d17SCornelia Huck 		info->flags |= VFIO_REGION_INFO_FLAG_CAPS;
360db8e5d17SCornelia Huck 		if (info->argsz < sizeof(*info) + caps.size) {
361db8e5d17SCornelia Huck 			info->argsz = sizeof(*info) + caps.size;
362db8e5d17SCornelia Huck 			info->cap_offset = 0;
363db8e5d17SCornelia Huck 		} else {
364db8e5d17SCornelia Huck 			vfio_info_cap_shift(&caps, sizeof(*info));
365db8e5d17SCornelia Huck 			if (copy_to_user((void __user *)arg + sizeof(*info),
366db8e5d17SCornelia Huck 					 caps.buf, caps.size)) {
367db8e5d17SCornelia Huck 				kfree(caps.buf);
368db8e5d17SCornelia Huck 				return -EFAULT;
369e01bcdd6SDong Jia Shi 			}
370db8e5d17SCornelia Huck 			info->cap_offset = sizeof(*info);
371db8e5d17SCornelia Huck 		}
372db8e5d17SCornelia Huck 
373db8e5d17SCornelia Huck 		kfree(caps.buf);
374db8e5d17SCornelia Huck 
375db8e5d17SCornelia Huck 	}
376db8e5d17SCornelia Huck 	}
377db8e5d17SCornelia Huck 	return 0;
378e01bcdd6SDong Jia Shi }
379e01bcdd6SDong Jia Shi 
vfio_ccw_mdev_get_irq_info(struct vfio_irq_info * info)3805bf18536SSebastian Ott static int vfio_ccw_mdev_get_irq_info(struct vfio_irq_info *info)
381120e214eSDong Jia Shi {
3824296151dSEric Farman 	switch (info->index) {
3834296151dSEric Farman 	case VFIO_CCW_IO_IRQ_INDEX:
384d8cac29bSFarhan Ali 	case VFIO_CCW_CRW_IRQ_INDEX:
385bccce80bSEric Farman 	case VFIO_CCW_REQ_IRQ_INDEX:
386120e214eSDong Jia Shi 		info->count = 1;
387120e214eSDong Jia Shi 		info->flags = VFIO_IRQ_INFO_EVENTFD;
3884296151dSEric Farman 		break;
3894296151dSEric Farman 	default:
3904296151dSEric Farman 		return -EINVAL;
3914296151dSEric Farman 	}
392120e214eSDong Jia Shi 
393120e214eSDong Jia Shi 	return 0;
394120e214eSDong Jia Shi }
395120e214eSDong Jia Shi 
vfio_ccw_mdev_set_irqs(struct vfio_ccw_private * private,uint32_t flags,uint32_t index,void __user * data)39639b6ee01SJason Gunthorpe static int vfio_ccw_mdev_set_irqs(struct vfio_ccw_private *private,
397120e214eSDong Jia Shi 				  uint32_t flags,
3984296151dSEric Farman 				  uint32_t index,
399120e214eSDong Jia Shi 				  void __user *data)
400120e214eSDong Jia Shi {
401120e214eSDong Jia Shi 	struct eventfd_ctx **ctx;
402120e214eSDong Jia Shi 
403120e214eSDong Jia Shi 	if (!(flags & VFIO_IRQ_SET_ACTION_TRIGGER))
404120e214eSDong Jia Shi 		return -EINVAL;
405120e214eSDong Jia Shi 
4064296151dSEric Farman 	switch (index) {
4074296151dSEric Farman 	case VFIO_CCW_IO_IRQ_INDEX:
408120e214eSDong Jia Shi 		ctx = &private->io_trigger;
4094296151dSEric Farman 		break;
410d8cac29bSFarhan Ali 	case VFIO_CCW_CRW_IRQ_INDEX:
411d8cac29bSFarhan Ali 		ctx = &private->crw_trigger;
412d8cac29bSFarhan Ali 		break;
413bccce80bSEric Farman 	case VFIO_CCW_REQ_IRQ_INDEX:
414bccce80bSEric Farman 		ctx = &private->req_trigger;
415bccce80bSEric Farman 		break;
4164296151dSEric Farman 	default:
4174296151dSEric Farman 		return -EINVAL;
4184296151dSEric Farman 	}
419120e214eSDong Jia Shi 
420120e214eSDong Jia Shi 	switch (flags & VFIO_IRQ_SET_DATA_TYPE_MASK) {
421120e214eSDong Jia Shi 	case VFIO_IRQ_SET_DATA_NONE:
422120e214eSDong Jia Shi 	{
423120e214eSDong Jia Shi 		if (*ctx)
424120e214eSDong Jia Shi 			eventfd_signal(*ctx, 1);
425120e214eSDong Jia Shi 		return 0;
426120e214eSDong Jia Shi 	}
427120e214eSDong Jia Shi 	case VFIO_IRQ_SET_DATA_BOOL:
428120e214eSDong Jia Shi 	{
429120e214eSDong Jia Shi 		uint8_t trigger;
430120e214eSDong Jia Shi 
431120e214eSDong Jia Shi 		if (get_user(trigger, (uint8_t __user *)data))
432120e214eSDong Jia Shi 			return -EFAULT;
433120e214eSDong Jia Shi 
434120e214eSDong Jia Shi 		if (trigger && *ctx)
435120e214eSDong Jia Shi 			eventfd_signal(*ctx, 1);
436120e214eSDong Jia Shi 		return 0;
437120e214eSDong Jia Shi 	}
438120e214eSDong Jia Shi 	case VFIO_IRQ_SET_DATA_EVENTFD:
439120e214eSDong Jia Shi 	{
440120e214eSDong Jia Shi 		int32_t fd;
441120e214eSDong Jia Shi 
442120e214eSDong Jia Shi 		if (get_user(fd, (int32_t __user *)data))
443120e214eSDong Jia Shi 			return -EFAULT;
444120e214eSDong Jia Shi 
445120e214eSDong Jia Shi 		if (fd == -1) {
446120e214eSDong Jia Shi 			if (*ctx)
447120e214eSDong Jia Shi 				eventfd_ctx_put(*ctx);
448120e214eSDong Jia Shi 			*ctx = NULL;
449120e214eSDong Jia Shi 		} else if (fd >= 0) {
450120e214eSDong Jia Shi 			struct eventfd_ctx *efdctx;
451120e214eSDong Jia Shi 
452120e214eSDong Jia Shi 			efdctx = eventfd_ctx_fdget(fd);
453120e214eSDong Jia Shi 			if (IS_ERR(efdctx))
454120e214eSDong Jia Shi 				return PTR_ERR(efdctx);
455120e214eSDong Jia Shi 
456120e214eSDong Jia Shi 			if (*ctx)
457120e214eSDong Jia Shi 				eventfd_ctx_put(*ctx);
458120e214eSDong Jia Shi 
459120e214eSDong Jia Shi 			*ctx = efdctx;
460120e214eSDong Jia Shi 		} else
461120e214eSDong Jia Shi 			return -EINVAL;
462120e214eSDong Jia Shi 
463120e214eSDong Jia Shi 		return 0;
464120e214eSDong Jia Shi 	}
465120e214eSDong Jia Shi 	default:
466120e214eSDong Jia Shi 		return -EINVAL;
467120e214eSDong Jia Shi 	}
468120e214eSDong Jia Shi }
469120e214eSDong Jia Shi 
vfio_ccw_register_dev_region(struct vfio_ccw_private * private,unsigned int subtype,const struct vfio_ccw_regops * ops,size_t size,u32 flags,void * data)470db8e5d17SCornelia Huck int vfio_ccw_register_dev_region(struct vfio_ccw_private *private,
471db8e5d17SCornelia Huck 				 unsigned int subtype,
472db8e5d17SCornelia Huck 				 const struct vfio_ccw_regops *ops,
473db8e5d17SCornelia Huck 				 size_t size, u32 flags, void *data)
474db8e5d17SCornelia Huck {
475db8e5d17SCornelia Huck 	struct vfio_ccw_region *region;
476db8e5d17SCornelia Huck 
477db8e5d17SCornelia Huck 	region = krealloc(private->region,
478db8e5d17SCornelia Huck 			  (private->num_regions + 1) * sizeof(*region),
479db8e5d17SCornelia Huck 			  GFP_KERNEL);
480db8e5d17SCornelia Huck 	if (!region)
481db8e5d17SCornelia Huck 		return -ENOMEM;
482db8e5d17SCornelia Huck 
483db8e5d17SCornelia Huck 	private->region = region;
484db8e5d17SCornelia Huck 	private->region[private->num_regions].type = VFIO_REGION_TYPE_CCW;
485db8e5d17SCornelia Huck 	private->region[private->num_regions].subtype = subtype;
486db8e5d17SCornelia Huck 	private->region[private->num_regions].ops = ops;
487db8e5d17SCornelia Huck 	private->region[private->num_regions].size = size;
488db8e5d17SCornelia Huck 	private->region[private->num_regions].flags = flags;
489db8e5d17SCornelia Huck 	private->region[private->num_regions].data = data;
490db8e5d17SCornelia Huck 
491db8e5d17SCornelia Huck 	private->num_regions++;
492db8e5d17SCornelia Huck 
493db8e5d17SCornelia Huck 	return 0;
494db8e5d17SCornelia Huck }
495db8e5d17SCornelia Huck 
vfio_ccw_unregister_dev_regions(struct vfio_ccw_private * private)496600279b5SEric Farman void vfio_ccw_unregister_dev_regions(struct vfio_ccw_private *private)
497600279b5SEric Farman {
498600279b5SEric Farman 	int i;
499600279b5SEric Farman 
500600279b5SEric Farman 	for (i = 0; i < private->num_regions; i++)
501600279b5SEric Farman 		private->region[i].ops->release(private, &private->region[i]);
502600279b5SEric Farman 	private->num_regions = 0;
503600279b5SEric Farman 	kfree(private->region);
504600279b5SEric Farman 	private->region = NULL;
505600279b5SEric Farman }
506600279b5SEric Farman 
vfio_ccw_mdev_ioctl(struct vfio_device * vdev,unsigned int cmd,unsigned long arg)5073bf1311fSJason Gunthorpe static ssize_t vfio_ccw_mdev_ioctl(struct vfio_device *vdev,
508e01bcdd6SDong Jia Shi 				   unsigned int cmd,
509e01bcdd6SDong Jia Shi 				   unsigned long arg)
510e01bcdd6SDong Jia Shi {
51139b6ee01SJason Gunthorpe 	struct vfio_ccw_private *private =
5123bf1311fSJason Gunthorpe 		container_of(vdev, struct vfio_ccw_private, vdev);
513e01bcdd6SDong Jia Shi 	int ret = 0;
514e01bcdd6SDong Jia Shi 	unsigned long minsz;
515e01bcdd6SDong Jia Shi 
516e01bcdd6SDong Jia Shi 	switch (cmd) {
517e01bcdd6SDong Jia Shi 	case VFIO_DEVICE_GET_INFO:
518e01bcdd6SDong Jia Shi 	{
519e01bcdd6SDong Jia Shi 		struct vfio_device_info info;
520e01bcdd6SDong Jia Shi 
521e01bcdd6SDong Jia Shi 		minsz = offsetofend(struct vfio_device_info, num_irqs);
522e01bcdd6SDong Jia Shi 
523e01bcdd6SDong Jia Shi 		if (copy_from_user(&info, (void __user *)arg, minsz))
524e01bcdd6SDong Jia Shi 			return -EFAULT;
525e01bcdd6SDong Jia Shi 
526e01bcdd6SDong Jia Shi 		if (info.argsz < minsz)
527e01bcdd6SDong Jia Shi 			return -EINVAL;
528e01bcdd6SDong Jia Shi 
52939b6ee01SJason Gunthorpe 		ret = vfio_ccw_mdev_get_device_info(private, &info);
530e01bcdd6SDong Jia Shi 		if (ret)
531e01bcdd6SDong Jia Shi 			return ret;
532e01bcdd6SDong Jia Shi 
53351c44babSWang Qing 		return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0;
534e01bcdd6SDong Jia Shi 	}
535e01bcdd6SDong Jia Shi 	case VFIO_DEVICE_GET_REGION_INFO:
536e01bcdd6SDong Jia Shi 	{
537e01bcdd6SDong Jia Shi 		struct vfio_region_info info;
538e01bcdd6SDong Jia Shi 
539e01bcdd6SDong Jia Shi 		minsz = offsetofend(struct vfio_region_info, offset);
540e01bcdd6SDong Jia Shi 
541e01bcdd6SDong Jia Shi 		if (copy_from_user(&info, (void __user *)arg, minsz))
542e01bcdd6SDong Jia Shi 			return -EFAULT;
543e01bcdd6SDong Jia Shi 
544e01bcdd6SDong Jia Shi 		if (info.argsz < minsz)
545e01bcdd6SDong Jia Shi 			return -EINVAL;
546e01bcdd6SDong Jia Shi 
54739b6ee01SJason Gunthorpe 		ret = vfio_ccw_mdev_get_region_info(private, &info, arg);
548e01bcdd6SDong Jia Shi 		if (ret)
549e01bcdd6SDong Jia Shi 			return ret;
550e01bcdd6SDong Jia Shi 
55151c44babSWang Qing 		return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0;
552e01bcdd6SDong Jia Shi 	}
553120e214eSDong Jia Shi 	case VFIO_DEVICE_GET_IRQ_INFO:
554120e214eSDong Jia Shi 	{
555120e214eSDong Jia Shi 		struct vfio_irq_info info;
556120e214eSDong Jia Shi 
557120e214eSDong Jia Shi 		minsz = offsetofend(struct vfio_irq_info, count);
558120e214eSDong Jia Shi 
559120e214eSDong Jia Shi 		if (copy_from_user(&info, (void __user *)arg, minsz))
560120e214eSDong Jia Shi 			return -EFAULT;
561120e214eSDong Jia Shi 
562120e214eSDong Jia Shi 		if (info.argsz < minsz || info.index >= VFIO_CCW_NUM_IRQS)
563120e214eSDong Jia Shi 			return -EINVAL;
564120e214eSDong Jia Shi 
565120e214eSDong Jia Shi 		ret = vfio_ccw_mdev_get_irq_info(&info);
566120e214eSDong Jia Shi 		if (ret)
567120e214eSDong Jia Shi 			return ret;
568120e214eSDong Jia Shi 
569120e214eSDong Jia Shi 		if (info.count == -1)
570120e214eSDong Jia Shi 			return -EINVAL;
571120e214eSDong Jia Shi 
572d9c48a94SEric Farman 		return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0;
573120e214eSDong Jia Shi 	}
574120e214eSDong Jia Shi 	case VFIO_DEVICE_SET_IRQS:
575120e214eSDong Jia Shi 	{
576120e214eSDong Jia Shi 		struct vfio_irq_set hdr;
577120e214eSDong Jia Shi 		size_t data_size;
578120e214eSDong Jia Shi 		void __user *data;
579120e214eSDong Jia Shi 
580120e214eSDong Jia Shi 		minsz = offsetofend(struct vfio_irq_set, count);
581120e214eSDong Jia Shi 
582120e214eSDong Jia Shi 		if (copy_from_user(&hdr, (void __user *)arg, minsz))
583120e214eSDong Jia Shi 			return -EFAULT;
584120e214eSDong Jia Shi 
585120e214eSDong Jia Shi 		ret = vfio_set_irqs_validate_and_prepare(&hdr, 1,
586120e214eSDong Jia Shi 							 VFIO_CCW_NUM_IRQS,
587120e214eSDong Jia Shi 							 &data_size);
588120e214eSDong Jia Shi 		if (ret)
589120e214eSDong Jia Shi 			return ret;
590120e214eSDong Jia Shi 
591120e214eSDong Jia Shi 		data = (void __user *)(arg + minsz);
59239b6ee01SJason Gunthorpe 		return vfio_ccw_mdev_set_irqs(private, hdr.flags, hdr.index,
59339b6ee01SJason Gunthorpe 					      data);
594120e214eSDong Jia Shi 	}
59583d1193aSDong Jia Shi 	case VFIO_DEVICE_RESET:
59639b6ee01SJason Gunthorpe 		return vfio_ccw_mdev_reset(private);
597e01bcdd6SDong Jia Shi 	default:
598e01bcdd6SDong Jia Shi 		return -ENOTTY;
599e01bcdd6SDong Jia Shi 	}
600e01bcdd6SDong Jia Shi }
601e01bcdd6SDong Jia Shi 
602bccce80bSEric Farman /* Request removal of the device*/
vfio_ccw_mdev_request(struct vfio_device * vdev,unsigned int count)6033bf1311fSJason Gunthorpe static void vfio_ccw_mdev_request(struct vfio_device *vdev, unsigned int count)
604bccce80bSEric Farman {
6053bf1311fSJason Gunthorpe 	struct vfio_ccw_private *private =
6063bf1311fSJason Gunthorpe 		container_of(vdev, struct vfio_ccw_private, vdev);
6073bf1311fSJason Gunthorpe 	struct device *dev = vdev->dev;
608bccce80bSEric Farman 
609bccce80bSEric Farman 	if (private->req_trigger) {
610bccce80bSEric Farman 		if (!(count % 10))
6113bf1311fSJason Gunthorpe 			dev_notice_ratelimited(dev,
612bccce80bSEric Farman 					       "Relaying device request to user (#%u)\n",
613bccce80bSEric Farman 					       count);
614bccce80bSEric Farman 
615bccce80bSEric Farman 		eventfd_signal(private->req_trigger, 1);
616bccce80bSEric Farman 	} else if (count == 0) {
6173bf1311fSJason Gunthorpe 		dev_notice(dev,
618bccce80bSEric Farman 			   "No device request channel registered, blocked until released by user\n");
619bccce80bSEric Farman 	}
620bccce80bSEric Farman }
621bccce80bSEric Farman 
6223bf1311fSJason Gunthorpe static const struct vfio_device_ops vfio_ccw_dev_ops = {
623ebb72b76SKevin Tian 	.init = vfio_ccw_mdev_init_dev,
624ebb72b76SKevin Tian 	.release = vfio_ccw_mdev_release_dev,
6259b0d6b7eSJason Gunthorpe 	.open_device = vfio_ccw_mdev_open_device,
6269b0d6b7eSJason Gunthorpe 	.close_device = vfio_ccw_mdev_close_device,
627060d2b5aSDong Jia Shi 	.read = vfio_ccw_mdev_read,
628060d2b5aSDong Jia Shi 	.write = vfio_ccw_mdev_write,
629e01bcdd6SDong Jia Shi 	.ioctl = vfio_ccw_mdev_ioctl,
630bccce80bSEric Farman 	.request = vfio_ccw_mdev_request,
631ce4b4657SJason Gunthorpe 	.dma_unmap = vfio_ccw_dma_unmap,
6324741f2e9SJason Gunthorpe 	.bind_iommufd = vfio_iommufd_emulated_bind,
6334741f2e9SJason Gunthorpe 	.unbind_iommufd = vfio_iommufd_emulated_unbind,
6344741f2e9SJason Gunthorpe 	.attach_ioas = vfio_iommufd_emulated_attach_ioas,
635*8cfa7186SYi Liu 	.detach_ioas = vfio_iommufd_emulated_detach_ioas,
63684cd8fc4SDong Jia Shi };
63784cd8fc4SDong Jia Shi 
6383bf1311fSJason Gunthorpe struct mdev_driver vfio_ccw_mdev_driver = {
639290aac5dSJason Gunthorpe 	.device_api = VFIO_DEVICE_API_CCW_STRING,
6409c799c22SJason Gunthorpe 	.max_instances = 1,
6413bf1311fSJason Gunthorpe 	.driver = {
6423bf1311fSJason Gunthorpe 		.name = "vfio_ccw_mdev",
6433bf1311fSJason Gunthorpe 		.owner = THIS_MODULE,
6443bf1311fSJason Gunthorpe 		.mod_name = KBUILD_MODNAME,
6453bf1311fSJason Gunthorpe 	},
6463bf1311fSJason Gunthorpe 	.probe = vfio_ccw_mdev_probe,
6473bf1311fSJason Gunthorpe 	.remove = vfio_ccw_mdev_remove,
6483bf1311fSJason Gunthorpe };
649