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